caveat-cli 0.6.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/README.md +32 -21
- package/dist/index.js +258 -501
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9761,49 +9761,49 @@ var require_fast_uri = __commonJS({
|
|
|
9761
9761
|
schemelessOptions.skipEscape = true;
|
|
9762
9762
|
return serialize(resolved, schemelessOptions);
|
|
9763
9763
|
}
|
|
9764
|
-
function resolveComponent(base2,
|
|
9764
|
+
function resolveComponent(base2, relative2, options2, skipNormalization) {
|
|
9765
9765
|
const target = {};
|
|
9766
9766
|
if (!skipNormalization) {
|
|
9767
9767
|
base2 = parse4(serialize(base2, options2), options2);
|
|
9768
|
-
|
|
9768
|
+
relative2 = parse4(serialize(relative2, options2), options2);
|
|
9769
9769
|
}
|
|
9770
9770
|
options2 = options2 || {};
|
|
9771
|
-
if (!options2.tolerant &&
|
|
9772
|
-
target.scheme =
|
|
9773
|
-
target.userinfo =
|
|
9774
|
-
target.host =
|
|
9775
|
-
target.port =
|
|
9776
|
-
target.path = removeDotSegments(
|
|
9777
|
-
target.query =
|
|
9771
|
+
if (!options2.tolerant && relative2.scheme) {
|
|
9772
|
+
target.scheme = relative2.scheme;
|
|
9773
|
+
target.userinfo = relative2.userinfo;
|
|
9774
|
+
target.host = relative2.host;
|
|
9775
|
+
target.port = relative2.port;
|
|
9776
|
+
target.path = removeDotSegments(relative2.path || "");
|
|
9777
|
+
target.query = relative2.query;
|
|
9778
9778
|
} else {
|
|
9779
|
-
if (
|
|
9780
|
-
target.userinfo =
|
|
9781
|
-
target.host =
|
|
9782
|
-
target.port =
|
|
9783
|
-
target.path = removeDotSegments(
|
|
9784
|
-
target.query =
|
|
9779
|
+
if (relative2.userinfo !== void 0 || relative2.host !== void 0 || relative2.port !== void 0) {
|
|
9780
|
+
target.userinfo = relative2.userinfo;
|
|
9781
|
+
target.host = relative2.host;
|
|
9782
|
+
target.port = relative2.port;
|
|
9783
|
+
target.path = removeDotSegments(relative2.path || "");
|
|
9784
|
+
target.query = relative2.query;
|
|
9785
9785
|
} else {
|
|
9786
|
-
if (!
|
|
9786
|
+
if (!relative2.path) {
|
|
9787
9787
|
target.path = base2.path;
|
|
9788
|
-
if (
|
|
9789
|
-
target.query =
|
|
9788
|
+
if (relative2.query !== void 0) {
|
|
9789
|
+
target.query = relative2.query;
|
|
9790
9790
|
} else {
|
|
9791
9791
|
target.query = base2.query;
|
|
9792
9792
|
}
|
|
9793
9793
|
} else {
|
|
9794
|
-
if (
|
|
9795
|
-
target.path = removeDotSegments(
|
|
9794
|
+
if (relative2.path[0] === "/") {
|
|
9795
|
+
target.path = removeDotSegments(relative2.path);
|
|
9796
9796
|
} else {
|
|
9797
9797
|
if ((base2.userinfo !== void 0 || base2.host !== void 0 || base2.port !== void 0) && !base2.path) {
|
|
9798
|
-
target.path = "/" +
|
|
9798
|
+
target.path = "/" + relative2.path;
|
|
9799
9799
|
} else if (!base2.path) {
|
|
9800
|
-
target.path =
|
|
9800
|
+
target.path = relative2.path;
|
|
9801
9801
|
} else {
|
|
9802
|
-
target.path = base2.path.slice(0, base2.path.lastIndexOf("/") + 1) +
|
|
9802
|
+
target.path = base2.path.slice(0, base2.path.lastIndexOf("/") + 1) + relative2.path;
|
|
9803
9803
|
}
|
|
9804
9804
|
target.path = removeDotSegments(target.path);
|
|
9805
9805
|
}
|
|
9806
|
-
target.query =
|
|
9806
|
+
target.query = relative2.query;
|
|
9807
9807
|
}
|
|
9808
9808
|
target.userinfo = base2.userinfo;
|
|
9809
9809
|
target.host = base2.host;
|
|
@@ -9811,7 +9811,7 @@ var require_fast_uri = __commonJS({
|
|
|
9811
9811
|
}
|
|
9812
9812
|
target.scheme = base2.scheme;
|
|
9813
9813
|
}
|
|
9814
|
-
target.fragment =
|
|
9814
|
+
target.fragment = relative2.fragment;
|
|
9815
9815
|
return target;
|
|
9816
9816
|
}
|
|
9817
9817
|
function equal(uriA, uriB, options2) {
|
|
@@ -12976,7 +12976,7 @@ import { Command } from "commander";
|
|
|
12976
12976
|
|
|
12977
12977
|
// src/context.ts
|
|
12978
12978
|
import { homedir } from "node:os";
|
|
12979
|
-
import { join as
|
|
12979
|
+
import { join as join7 } from "node:path";
|
|
12980
12980
|
|
|
12981
12981
|
// ../../packages/core/dist/db.js
|
|
12982
12982
|
import { DatabaseSync } from "node:sqlite";
|
|
@@ -15819,7 +15819,14 @@ function search(db, opts = {}) {
|
|
|
15819
15819
|
conditions.push(`e.confidence IN (${placeholders})`);
|
|
15820
15820
|
params.push(...filters.confidence);
|
|
15821
15821
|
}
|
|
15822
|
+
if (filters.visibility === "public" || filters.visibility === "private") {
|
|
15823
|
+
conditions.push(`e.visibility = ?`);
|
|
15824
|
+
params.push(filters.visibility);
|
|
15825
|
+
}
|
|
15822
15826
|
if (conditions.length) sql += " AND " + conditions.join(" AND ");
|
|
15827
|
+
if (!ftsQuery) {
|
|
15828
|
+
sql += ` ORDER BY json_extract(e.frontmatter_json, '$.updated_at') DESC`;
|
|
15829
|
+
}
|
|
15823
15830
|
sql += " LIMIT ?";
|
|
15824
15831
|
params.push(limit);
|
|
15825
15832
|
const rows = db.prepare(sql).all(...params);
|
|
@@ -15864,6 +15871,7 @@ function toSearchResult(row) {
|
|
|
15864
15871
|
title: row.title,
|
|
15865
15872
|
symptomExcerpt: symptom.slice(0, SYMPTOM_EXCERPT_LENGTH),
|
|
15866
15873
|
confidence: row.confidence,
|
|
15874
|
+
visibility: row.visibility ?? "public",
|
|
15867
15875
|
environment: fm.environment ?? {}
|
|
15868
15876
|
};
|
|
15869
15877
|
}
|
|
@@ -16105,12 +16113,10 @@ function resolvePaths(caveatHome, knowledgeRepo, userHome) {
|
|
|
16105
16113
|
|
|
16106
16114
|
// ../../packages/core/dist/config.js
|
|
16107
16115
|
import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
16108
|
-
var SHARED_REPO_URL = "https://github.com/kitepon-rgb/Caveat";
|
|
16109
16116
|
var DEFAULT_CONFIG = {
|
|
16110
16117
|
knowledgeRepo: "own",
|
|
16111
16118
|
semverKeys: ["driver", "cuda", "node"],
|
|
16112
|
-
communitySources: []
|
|
16113
|
-
sharedRepo: SHARED_REPO_URL
|
|
16119
|
+
communitySources: []
|
|
16114
16120
|
};
|
|
16115
16121
|
function loadConfig(userConfigPath) {
|
|
16116
16122
|
const userCfg = existsSync5(userConfigPath) ? JSON.parse(readFileSync4(userConfigPath, "utf-8")) : {};
|
|
@@ -16134,7 +16140,7 @@ function deepMerge(base2, overlay) {
|
|
|
16134
16140
|
}
|
|
16135
16141
|
|
|
16136
16142
|
// ../../packages/core/dist/community.js
|
|
16137
|
-
import { existsSync as existsSync6, readdirSync as readdirSync3 } from "node:fs";
|
|
16143
|
+
import { existsSync as existsSync6, readdirSync as readdirSync3, rmSync, statSync as statSync4 } from "node:fs";
|
|
16138
16144
|
import { join as join6 } from "node:path";
|
|
16139
16145
|
|
|
16140
16146
|
// ../../node_modules/.pnpm/simple-git@3.36.0/node_modules/simple-git/dist/esm/index.js
|
|
@@ -21164,6 +21170,37 @@ function communityList(opts) {
|
|
|
21164
21170
|
}
|
|
21165
21171
|
return out;
|
|
21166
21172
|
}
|
|
21173
|
+
function communityRemove(opts) {
|
|
21174
|
+
const handle = (opts.handle ?? "").trim();
|
|
21175
|
+
if (!handle) {
|
|
21176
|
+
throw new Error("handle is required");
|
|
21177
|
+
}
|
|
21178
|
+
if (handle === "." || handle === ".." || /[\\/]/.test(handle)) {
|
|
21179
|
+
throw new Error(`invalid handle (no path separators or relative segments): ${opts.handle}`);
|
|
21180
|
+
}
|
|
21181
|
+
const target = join6(opts.communityDir, handle);
|
|
21182
|
+
const dirExisted = existsSync6(target) && statSync4(target).isDirectory();
|
|
21183
|
+
const source = `community/${handle}`;
|
|
21184
|
+
const row = opts.db.prepare("SELECT COUNT(*) AS n FROM entries WHERE source = ?").get(source);
|
|
21185
|
+
const rowCount = row?.n ?? 0;
|
|
21186
|
+
if (opts.dryRun) {
|
|
21187
|
+
return { handle, path: target, dirExisted, rowCount, removed: false, dryRun: true };
|
|
21188
|
+
}
|
|
21189
|
+
if (dirExisted) {
|
|
21190
|
+
rmSync(target, { recursive: true, force: true });
|
|
21191
|
+
}
|
|
21192
|
+
if (rowCount > 0) {
|
|
21193
|
+
opts.db.prepare("DELETE FROM entries WHERE source = ?").run(source);
|
|
21194
|
+
}
|
|
21195
|
+
return {
|
|
21196
|
+
handle,
|
|
21197
|
+
path: target,
|
|
21198
|
+
dirExisted,
|
|
21199
|
+
rowCount,
|
|
21200
|
+
removed: dirExisted || rowCount > 0,
|
|
21201
|
+
dryRun: false
|
|
21202
|
+
};
|
|
21203
|
+
}
|
|
21167
21204
|
|
|
21168
21205
|
// ../../packages/core/dist/claudeHooks.js
|
|
21169
21206
|
var CAVEAT_TRIGGERS = [
|
|
@@ -21194,262 +21231,28 @@ function stopReminderText() {
|
|
|
21194
21231
|
return [
|
|
21195
21232
|
"[caveat] \u3053\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u3067\u5916\u90E8\u4ED5\u69D8\u306E\u7F60\uFF08GPU/\u30C9\u30E9\u30A4\u30D0/\u30C4\u30FC\u30EB\u30D0\u30FC\u30B8\u30E7\u30F3/IDE/\u518D\u73FE\u6027\uFF09\u3092\u8E0F\u307F\u3001\u6B21\u56DE\u306E\u81EA\u5206\u306E\u8ABF\u67FB\u304C\u77ED\u7E2E\u3067\u304D\u305D\u3046\u306A\u3089 mcp__caveat__caveat_record \u3067\u767B\u9332\u3092\u691C\u8A0E\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
21196
21233
|
"\u89E3\u6C7A\u3057\u305F\u30B1\u30FC\u30B9\u3060\u3051\u3067\u306A\u304F\u3001\u300C\u73FE\u72B6\u306E\u5236\u7D04\u3067\u306F\u4E0D\u53EF\u80FD\u3068\u5224\u5B9A\u3057\u305F\u300D\u7D50\u8AD6\u3082 outcome: impossible \u3068\u3057\u3066\u8A18\u9332\u5BFE\u8C61\u3002\u6B21\u56DE\u540C\u3058 3 \u6642\u9593\u3092\u6EB6\u304B\u3055\u306A\u3044\u305F\u3081\u306B\u3002",
|
|
21197
|
-
"\u65E2\u306B\u540C\u7B49\u306E caveat \u304C\u3042\u308B\u5834\u5408\u306F caveat_update \u3067\u4E0A\u66F8\u304D\u3001\u307E\u305F\u306F last_verified \u3060\u3051\u66F4\u65B0\u3002"
|
|
21198
|
-
"\u8A18\u9332\u3057\u305F\u7F60\u304C\u4ED6\u306E\u30E6\u30FC\u30B6\u306B\u3082\u518D\u5229\u7528\u4FA1\u5024\u304C\u3042\u308B\u306A\u3089 mcp__caveat__caveat_push \u3067\u5171\u6709 DB \u306B PR \u3092\u6295\u3052\u308B\u3053\u3068\u3082\u691C\u8A0E\uFF08\u500B\u4EBA\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u56FA\u6709\u306E\u7269\u306F push \u3057\u306A\u3044\uFF09\u3002"
|
|
21234
|
+
"\u65E2\u306B\u540C\u7B49\u306E caveat \u304C\u3042\u308B\u5834\u5408\u306F caveat_update \u3067\u4E0A\u66F8\u304D\u3001\u307E\u305F\u306F last_verified \u3060\u3051\u66F4\u65B0\u3002"
|
|
21199
21235
|
].join("\n");
|
|
21200
21236
|
}
|
|
21201
21237
|
|
|
21202
|
-
// ../../packages/core/dist/push.js
|
|
21203
|
-
import { spawnSync } from "node:child_process";
|
|
21204
|
-
import {
|
|
21205
|
-
copyFileSync,
|
|
21206
|
-
existsSync as existsSync7,
|
|
21207
|
-
mkdirSync as mkdirSync3,
|
|
21208
|
-
readdirSync as readdirSync4,
|
|
21209
|
-
readFileSync as readFileSync5
|
|
21210
|
-
} from "node:fs";
|
|
21211
|
-
import { dirname as dirname4, join as join7, relative as relative2 } from "node:path";
|
|
21212
|
-
async function pushEntry(opts) {
|
|
21213
|
-
if (!checkGhAvailable()) {
|
|
21214
|
-
return {
|
|
21215
|
-
status: "gh-missing",
|
|
21216
|
-
detail: "gh (GitHub CLI) is required. Install: https://cli.github.com/"
|
|
21217
|
-
};
|
|
21218
|
-
}
|
|
21219
|
-
if (!checkGhAuthed()) {
|
|
21220
|
-
return {
|
|
21221
|
-
status: "gh-unauthed",
|
|
21222
|
-
detail: "gh is not authenticated. Run `gh auth login`."
|
|
21223
|
-
};
|
|
21224
|
-
}
|
|
21225
|
-
const validation = validateCommunityUrl(opts.sharedRepoUrl);
|
|
21226
|
-
if (!validation.valid) {
|
|
21227
|
-
return { status: "failed", detail: `sharedRepo URL invalid: ${validation.reason}` };
|
|
21228
|
-
}
|
|
21229
|
-
const [sharedOwner, sharedName] = parseOwnerRepo(opts.sharedRepoUrl);
|
|
21230
|
-
if (!sharedOwner || !sharedName) {
|
|
21231
|
-
return { status: "failed", detail: `cannot parse owner/repo from ${opts.sharedRepoUrl}` };
|
|
21232
|
-
}
|
|
21233
|
-
const owned = findOwnedEntry(opts.entriesDir, opts.id);
|
|
21234
|
-
if (!owned) {
|
|
21235
|
-
return {
|
|
21236
|
-
status: "not-found",
|
|
21237
|
-
detail: `entry id=${opts.id} not found under ${opts.entriesDir}`
|
|
21238
|
-
};
|
|
21239
|
-
}
|
|
21240
|
-
const ghUser = resolveGhUser();
|
|
21241
|
-
if (!ghUser) {
|
|
21242
|
-
return { status: "failed", detail: "gh api user failed" };
|
|
21243
|
-
}
|
|
21244
|
-
if (opts.dryRun) {
|
|
21245
|
-
return {
|
|
21246
|
-
status: "dry-run",
|
|
21247
|
-
plannedSteps: [
|
|
21248
|
-
`fork ${sharedOwner}/${sharedName} as ${ghUser}/${sharedName}`,
|
|
21249
|
-
`clone fork \u2192 ${forkStagingDir(opts.caveatHome)}`,
|
|
21250
|
-
`copy entry \u2192 entries/${owned.relPath}`,
|
|
21251
|
-
"commit, push branch, open PR against main"
|
|
21252
|
-
]
|
|
21253
|
-
};
|
|
21254
|
-
}
|
|
21255
|
-
try {
|
|
21256
|
-
ensureFork(sharedOwner, sharedName);
|
|
21257
|
-
const stagingDir = forkStagingDir(opts.caveatHome);
|
|
21258
|
-
ensureStagingClone(stagingDir, ghUser, sharedName, sharedOwner);
|
|
21259
|
-
const branch = `caveat-push-${owned.id}-${Date.now().toString(36)}`;
|
|
21260
|
-
runOrThrow("git", ["checkout", "-b", branch], { cwd: stagingDir });
|
|
21261
|
-
const destPath = join7(stagingDir, "entries", owned.relPath);
|
|
21262
|
-
mkdirSync3(dirname4(destPath), { recursive: true });
|
|
21263
|
-
copyFileSync(owned.absPath, destPath);
|
|
21264
|
-
runOrThrow("git", ["add", join7("entries", owned.relPath)], { cwd: stagingDir });
|
|
21265
|
-
const isUpdate = upstreamAlreadyHas(stagingDir, "entries/" + owned.relPath);
|
|
21266
|
-
const verb = isUpdate ? "update" : "add";
|
|
21267
|
-
const commitMsg = `${verb}: ${owned.title}`;
|
|
21268
|
-
runOrThrow("git", ["commit", "-m", commitMsg], { cwd: stagingDir });
|
|
21269
|
-
runOrThrow("git", ["push", "--set-upstream", "origin", branch], { cwd: stagingDir });
|
|
21270
|
-
const prTitle = `${verb}: ${owned.title}`;
|
|
21271
|
-
const prBody = [
|
|
21272
|
-
`Contribution of caveat \`${owned.id}\` via \`caveat push\`.`,
|
|
21273
|
-
"",
|
|
21274
|
-
`- entry path: \`entries/${owned.relPath}\``,
|
|
21275
|
-
`- action: ${verb}`,
|
|
21276
|
-
"",
|
|
21277
|
-
"Merged PRs will appear in subscribers' repos on their next `caveat pull`."
|
|
21278
|
-
].join("\n");
|
|
21279
|
-
const pr = runCapture(
|
|
21280
|
-
"gh",
|
|
21281
|
-
[
|
|
21282
|
-
"pr",
|
|
21283
|
-
"create",
|
|
21284
|
-
"--repo",
|
|
21285
|
-
`${sharedOwner}/${sharedName}`,
|
|
21286
|
-
"--base",
|
|
21287
|
-
"main",
|
|
21288
|
-
"--head",
|
|
21289
|
-
`${ghUser}:${branch}`,
|
|
21290
|
-
"--title",
|
|
21291
|
-
prTitle,
|
|
21292
|
-
"--body",
|
|
21293
|
-
prBody
|
|
21294
|
-
],
|
|
21295
|
-
{ cwd: stagingDir }
|
|
21296
|
-
);
|
|
21297
|
-
if (pr.status !== 0) {
|
|
21298
|
-
return {
|
|
21299
|
-
status: "failed",
|
|
21300
|
-
detail: (pr.stderr || "gh pr create failed").trim()
|
|
21301
|
-
};
|
|
21302
|
-
}
|
|
21303
|
-
return { status: "ok", prUrl: pr.stdout.trim() };
|
|
21304
|
-
} catch (err) {
|
|
21305
|
-
return {
|
|
21306
|
-
status: "failed",
|
|
21307
|
-
detail: err instanceof Error ? err.message : String(err)
|
|
21308
|
-
};
|
|
21309
|
-
}
|
|
21310
|
-
}
|
|
21311
|
-
function shellQuote(s) {
|
|
21312
|
-
if (!/[\s&|<>^()"`$!*?\[\]{}\\]/.test(s)) return s;
|
|
21313
|
-
return `"${s.replace(/"/g, '""')}"`;
|
|
21314
|
-
}
|
|
21315
|
-
function runCapture(command, args, opts = {}) {
|
|
21316
|
-
const line = [command, ...args].map(shellQuote).join(" ");
|
|
21317
|
-
const r2 = spawnSync(line, {
|
|
21318
|
-
encoding: "utf-8",
|
|
21319
|
-
cwd: opts.cwd,
|
|
21320
|
-
shell: true
|
|
21321
|
-
});
|
|
21322
|
-
return {
|
|
21323
|
-
status: r2.status,
|
|
21324
|
-
stdout: typeof r2.stdout === "string" ? r2.stdout : "",
|
|
21325
|
-
stderr: typeof r2.stderr === "string" ? r2.stderr : ""
|
|
21326
|
-
};
|
|
21327
|
-
}
|
|
21328
|
-
function runOrThrow(command, args, opts = {}) {
|
|
21329
|
-
const r2 = runCapture(command, args, opts);
|
|
21330
|
-
if (r2.status !== 0) {
|
|
21331
|
-
throw new Error(
|
|
21332
|
-
`${command} ${args.join(" ")} failed: ${(r2.stderr || r2.stdout || "unknown").trim()}`
|
|
21333
|
-
);
|
|
21334
|
-
}
|
|
21335
|
-
}
|
|
21336
|
-
function checkGhAvailable() {
|
|
21337
|
-
return runCapture("gh", ["--version"]).status === 0;
|
|
21338
|
-
}
|
|
21339
|
-
function checkGhAuthed() {
|
|
21340
|
-
return runCapture("gh", ["auth", "status"]).status === 0;
|
|
21341
|
-
}
|
|
21342
|
-
function resolveGhUser() {
|
|
21343
|
-
const r2 = runCapture("gh", ["api", "user", "--jq", ".login"]);
|
|
21344
|
-
if (r2.status !== 0) return void 0;
|
|
21345
|
-
return r2.stdout.trim() || void 0;
|
|
21346
|
-
}
|
|
21347
|
-
function parseOwnerRepo(url) {
|
|
21348
|
-
const m = /^https:\/\/github\.com\/([^/]+)\/([^/]+?)(\.git)?\/?$/.exec(url);
|
|
21349
|
-
if (!m) return [void 0, void 0];
|
|
21350
|
-
return [m[1], m[2]];
|
|
21351
|
-
}
|
|
21352
|
-
function findOwnedEntry(entriesDir, id) {
|
|
21353
|
-
if (!existsSync7(entriesDir)) return void 0;
|
|
21354
|
-
for (const categoryDirent of readdirSync4(entriesDir, { withFileTypes: true })) {
|
|
21355
|
-
if (!categoryDirent.isDirectory()) continue;
|
|
21356
|
-
const categoryDir = join7(entriesDir, categoryDirent.name);
|
|
21357
|
-
for (const fileDirent of readdirSync4(categoryDir, { withFileTypes: true })) {
|
|
21358
|
-
if (!fileDirent.isFile() || !fileDirent.name.endsWith(".md")) continue;
|
|
21359
|
-
const absPath = join7(categoryDir, fileDirent.name);
|
|
21360
|
-
const raw2 = readFileSync5(absPath, "utf-8");
|
|
21361
|
-
try {
|
|
21362
|
-
const parsed = parseMarkdown(raw2);
|
|
21363
|
-
if (parsed.frontmatter.id === id) {
|
|
21364
|
-
const relPath = relative2(entriesDir, absPath).replace(/\\/g, "/");
|
|
21365
|
-
return { id, title: parsed.frontmatter.title, absPath, relPath };
|
|
21366
|
-
}
|
|
21367
|
-
} catch {
|
|
21368
|
-
}
|
|
21369
|
-
}
|
|
21370
|
-
}
|
|
21371
|
-
return void 0;
|
|
21372
|
-
}
|
|
21373
|
-
function forkStagingDir(caveatHome) {
|
|
21374
|
-
return join7(caveatHome, "push-fork");
|
|
21375
|
-
}
|
|
21376
|
-
function ensureFork(owner, repo) {
|
|
21377
|
-
runCapture("gh", ["repo", "fork", `${owner}/${repo}`, "--clone=false", "--remote=false"]);
|
|
21378
|
-
}
|
|
21379
|
-
function ensureStagingClone(stagingDir, ghUser, sharedName, upstreamOwner) {
|
|
21380
|
-
if (!existsSync7(stagingDir)) {
|
|
21381
|
-
mkdirSync3(dirname4(stagingDir), { recursive: true });
|
|
21382
|
-
const forkUrl = `https://github.com/${ghUser}/${sharedName}.git`;
|
|
21383
|
-
runOrThrow("git", ["clone", "--depth", "30", forkUrl, stagingDir]);
|
|
21384
|
-
runOrThrow(
|
|
21385
|
-
"git",
|
|
21386
|
-
["remote", "add", "upstream", `https://github.com/${upstreamOwner}/${sharedName}.git`],
|
|
21387
|
-
{ cwd: stagingDir }
|
|
21388
|
-
);
|
|
21389
|
-
} else {
|
|
21390
|
-
runOrThrow("git", ["checkout", "main"], { cwd: stagingDir });
|
|
21391
|
-
runOrThrow("git", ["fetch", "upstream", "main"], { cwd: stagingDir });
|
|
21392
|
-
runOrThrow("git", ["reset", "--hard", "upstream/main"], { cwd: stagingDir });
|
|
21393
|
-
runOrThrow("git", ["push", "origin", "main", "--force-with-lease"], { cwd: stagingDir });
|
|
21394
|
-
}
|
|
21395
|
-
}
|
|
21396
|
-
function upstreamAlreadyHas(stagingDir, relFromRoot) {
|
|
21397
|
-
return existsSync7(join7(stagingDir, relFromRoot));
|
|
21398
|
-
}
|
|
21399
|
-
|
|
21400
|
-
// ../../packages/core/dist/pullShared.js
|
|
21401
|
-
import { existsSync as existsSync8, readdirSync as readdirSync5 } from "node:fs";
|
|
21402
|
-
import { join as join8 } from "node:path";
|
|
21403
|
-
async function pullShared(opts) {
|
|
21404
|
-
const result = { pulled: [], indexed: [] };
|
|
21405
|
-
if (!existsSync8(opts.communityDir)) {
|
|
21406
|
-
return result;
|
|
21407
|
-
}
|
|
21408
|
-
const pulls = await communityPull({
|
|
21409
|
-
communityDir: opts.communityDir,
|
|
21410
|
-
logger: opts.logger
|
|
21411
|
-
});
|
|
21412
|
-
for (const p2 of pulls) {
|
|
21413
|
-
result.pulled.push({
|
|
21414
|
-
handle: p2.handle,
|
|
21415
|
-
status: p2.status,
|
|
21416
|
-
message: p2.message
|
|
21417
|
-
});
|
|
21418
|
-
}
|
|
21419
|
-
rebuildAll(opts.db);
|
|
21420
|
-
if (existsSync8(opts.entriesDir)) {
|
|
21421
|
-
const own = scanSource({ db: opts.db, source: "own", entriesRoot: opts.entriesDir });
|
|
21422
|
-
result.indexed.push({ source: "own", ...own });
|
|
21423
|
-
}
|
|
21424
|
-
for (const entry of readdirSync5(opts.communityDir, { withFileTypes: true })) {
|
|
21425
|
-
if (!entry.isDirectory()) continue;
|
|
21426
|
-
const source = `community/${entry.name}`;
|
|
21427
|
-
const root = join8(opts.communityDir, entry.name, "entries");
|
|
21428
|
-
if (!existsSync8(root)) continue;
|
|
21429
|
-
const scan = scanSource({ db: opts.db, source, entriesRoot: root });
|
|
21430
|
-
result.indexed.push({ source, ...scan });
|
|
21431
|
-
}
|
|
21432
|
-
return result;
|
|
21433
|
-
}
|
|
21434
|
-
|
|
21435
21238
|
// src/context.ts
|
|
21436
21239
|
function buildContext(logger, overrides = {}) {
|
|
21437
21240
|
const userHome = overrides.userHome ?? homedir();
|
|
21438
21241
|
const caveatHome = overrides.caveatHome ?? findCaveatHome(userHome);
|
|
21439
|
-
const userConfigPath =
|
|
21242
|
+
const userConfigPath = join7(userHome, ".caveatrc.json");
|
|
21440
21243
|
const config3 = loadConfig(userConfigPath);
|
|
21441
21244
|
const paths = resolvePaths(caveatHome, config3.knowledgeRepo, userHome);
|
|
21442
21245
|
return { caveatHome, userHome, userConfigPath, config: config3, paths, logger };
|
|
21443
21246
|
}
|
|
21444
21247
|
|
|
21445
21248
|
// src/version.ts
|
|
21446
|
-
import { readFileSync as
|
|
21447
|
-
import { dirname as
|
|
21249
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
21250
|
+
import { dirname as dirname4, join as join8 } from "node:path";
|
|
21448
21251
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
21449
21252
|
function resolveVersion() {
|
|
21450
21253
|
try {
|
|
21451
|
-
const here2 =
|
|
21452
|
-
const pkg = JSON.parse(
|
|
21254
|
+
const here2 = dirname4(fileURLToPath3(import.meta.url));
|
|
21255
|
+
const pkg = JSON.parse(readFileSync5(join8(here2, "..", "package.json"), "utf-8"));
|
|
21453
21256
|
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
21454
21257
|
} catch {
|
|
21455
21258
|
return "0.0.0";
|
|
@@ -21468,13 +21271,13 @@ var stdoutLogger = {
|
|
|
21468
21271
|
};
|
|
21469
21272
|
|
|
21470
21273
|
// src/commands/init.ts
|
|
21471
|
-
import { existsSync as
|
|
21472
|
-
import { join as
|
|
21274
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync4, readdirSync as readdirSync4, renameSync, rmdirSync, writeFileSync as writeFileSync5 } from "node:fs";
|
|
21275
|
+
import { dirname as dirname6, join as join10 } from "node:path";
|
|
21473
21276
|
|
|
21474
21277
|
// src/claudeInstall.ts
|
|
21475
|
-
import { spawnSync
|
|
21476
|
-
import { copyFileSync
|
|
21477
|
-
import { dirname as
|
|
21278
|
+
import { spawnSync } from "node:child_process";
|
|
21279
|
+
import { copyFileSync, existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
21280
|
+
import { dirname as dirname5, join as join9 } from "node:path";
|
|
21478
21281
|
var EVENT_USER_PROMPT_SUBMIT = "UserPromptSubmit";
|
|
21479
21282
|
var EVENT_STOP = "Stop";
|
|
21480
21283
|
function quote(p2) {
|
|
@@ -21508,27 +21311,27 @@ function removeHook(settings, event, command) {
|
|
|
21508
21311
|
return true;
|
|
21509
21312
|
}
|
|
21510
21313
|
function readSettings(path) {
|
|
21511
|
-
if (!
|
|
21512
|
-
return JSON.parse(
|
|
21314
|
+
if (!existsSync7(path)) return {};
|
|
21315
|
+
return JSON.parse(readFileSync6(path, "utf-8"));
|
|
21513
21316
|
}
|
|
21514
21317
|
function writeSettings(path, settings) {
|
|
21515
|
-
const dir =
|
|
21516
|
-
if (!
|
|
21318
|
+
const dir = dirname5(path);
|
|
21319
|
+
if (!existsSync7(dir)) mkdirSync3(dir, { recursive: true });
|
|
21517
21320
|
let backupPath = "";
|
|
21518
|
-
if (
|
|
21321
|
+
if (existsSync7(path)) {
|
|
21519
21322
|
backupPath = `${path}.caveat-backup-${Date.now()}`;
|
|
21520
|
-
|
|
21323
|
+
copyFileSync(path, backupPath);
|
|
21521
21324
|
}
|
|
21522
21325
|
writeFileSync4(path, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
21523
21326
|
return backupPath;
|
|
21524
21327
|
}
|
|
21525
21328
|
var CLAUDE_BIN = "claude";
|
|
21526
|
-
function
|
|
21329
|
+
function shellQuote(s) {
|
|
21527
21330
|
return /[\s&|<>^()]/.test(s) ? `"${s}"` : s;
|
|
21528
21331
|
}
|
|
21529
21332
|
function runClaude(args) {
|
|
21530
|
-
const line = [CLAUDE_BIN, ...args].map(
|
|
21531
|
-
return
|
|
21333
|
+
const line = [CLAUDE_BIN, ...args].map(shellQuote).join(" ");
|
|
21334
|
+
return spawnSync(line, { shell: true, encoding: "utf-8" });
|
|
21532
21335
|
}
|
|
21533
21336
|
function registerMcp(nodePath, cliScriptPath, dryRun, logger) {
|
|
21534
21337
|
const args = mcpArgs(cliScriptPath);
|
|
@@ -21578,7 +21381,7 @@ function unregisterMcp(dryRun, logger) {
|
|
|
21578
21381
|
return { action: "skipped", detail: "not registered or removal failed" };
|
|
21579
21382
|
}
|
|
21580
21383
|
function installClaudeIntegration(opts) {
|
|
21581
|
-
const settingsPath =
|
|
21384
|
+
const settingsPath = join9(opts.claudeDir, "settings.json");
|
|
21582
21385
|
const settings = readSettings(settingsPath);
|
|
21583
21386
|
const usCmd = hookCommand(opts.nodePath, opts.cliScriptPath, "user-prompt-submit");
|
|
21584
21387
|
const stopCmd = hookCommand(opts.nodePath, opts.cliScriptPath, "stop");
|
|
@@ -21597,7 +21400,7 @@ function installClaudeIntegration(opts) {
|
|
|
21597
21400
|
return { mcp, hooks: { userPromptSubmit, stop }, backupPath };
|
|
21598
21401
|
}
|
|
21599
21402
|
function uninstallClaudeIntegration(opts) {
|
|
21600
|
-
const settingsPath =
|
|
21403
|
+
const settingsPath = join9(opts.claudeDir, "settings.json");
|
|
21601
21404
|
const settings = readSettings(settingsPath);
|
|
21602
21405
|
const usCmd = hookCommand(opts.nodePath, opts.cliScriptPath, "user-prompt-submit");
|
|
21603
21406
|
const stopCmd = hookCommand(opts.nodePath, opts.cliScriptPath, "stop");
|
|
@@ -21629,35 +21432,34 @@ var KNOWLEDGE_GITIGNORE = [
|
|
|
21629
21432
|
".obsidian/",
|
|
21630
21433
|
""
|
|
21631
21434
|
].join("\n");
|
|
21632
|
-
async function runInit(ctx, opts = { skipClaude: false,
|
|
21435
|
+
async function runInit(ctx, opts = { skipClaude: false, dryRun: false }) {
|
|
21633
21436
|
ensureUserConfig(ctx.userConfigPath);
|
|
21634
21437
|
ctx.logger.info(`user config: ${ctx.userConfigPath}`);
|
|
21635
|
-
if (!
|
|
21636
|
-
|
|
21637
|
-
|
|
21438
|
+
if (!existsSync8(ctx.paths.knowledgeRepo)) {
|
|
21439
|
+
mkdirSync4(ctx.paths.knowledgeRepo, { recursive: true });
|
|
21440
|
+
mkdirSync4(ctx.paths.entriesDir, { recursive: true });
|
|
21638
21441
|
ctx.logger.info(`knowledge repo scaffolded: ${ctx.paths.knowledgeRepo}`);
|
|
21639
21442
|
} else {
|
|
21640
21443
|
ctx.logger.info(`knowledge repo: ${ctx.paths.knowledgeRepo}`);
|
|
21641
21444
|
}
|
|
21642
21445
|
migrateLegacyCommunityDir(ctx);
|
|
21643
|
-
const gitignorePath =
|
|
21644
|
-
if (!
|
|
21446
|
+
const gitignorePath = join10(ctx.paths.knowledgeRepo, ".gitignore");
|
|
21447
|
+
if (!existsSync8(gitignorePath)) {
|
|
21645
21448
|
writeFileSync5(gitignorePath, KNOWLEDGE_GITIGNORE, "utf-8");
|
|
21646
21449
|
ctx.logger.info(`.gitignore created: ${gitignorePath}`);
|
|
21647
21450
|
}
|
|
21648
|
-
|
|
21649
|
-
|
|
21650
|
-
if (!
|
|
21651
|
-
|
|
21652
|
-
} else if (opts.skipShared) {
|
|
21653
|
-
ctx.logger.info("shared community DB subscription skipped (--skip-shared)");
|
|
21654
|
-
} else if (opts.dryRun) {
|
|
21655
|
-
ctx.logger.info(`[dry-run] would subscribe to shared community DB: ${ctx.config.sharedRepo}`);
|
|
21656
|
-
}
|
|
21657
|
-
} finally {
|
|
21451
|
+
if (!opts.dryRun) {
|
|
21452
|
+
const dbDir = dirname6(ctx.paths.dbPath);
|
|
21453
|
+
if (!existsSync8(dbDir)) mkdirSync4(dbDir, { recursive: true });
|
|
21454
|
+
const db = openDb({ path: ctx.paths.dbPath, logger: ctx.logger });
|
|
21658
21455
|
db.close();
|
|
21456
|
+
ctx.logger.info(`db initialized: ${ctx.paths.dbPath}`);
|
|
21457
|
+
} else {
|
|
21458
|
+
ctx.logger.info(`[dry-run] db path: ${ctx.paths.dbPath}`);
|
|
21659
21459
|
}
|
|
21660
|
-
ctx.logger.info(
|
|
21460
|
+
ctx.logger.info(
|
|
21461
|
+
"tip: subscribe to a group repo with `caveat community add <github-url>`, then `caveat pull`."
|
|
21462
|
+
);
|
|
21661
21463
|
if (opts.skipClaude) {
|
|
21662
21464
|
ctx.logger.info("Claude Code integration skipped (--skip-claude)");
|
|
21663
21465
|
return;
|
|
@@ -21668,7 +21470,7 @@ async function runInit(ctx, opts = { skipClaude: false, skipShared: false, dryRu
|
|
|
21668
21470
|
return;
|
|
21669
21471
|
}
|
|
21670
21472
|
const result = installClaudeIntegration({
|
|
21671
|
-
claudeDir:
|
|
21473
|
+
claudeDir: join10(ctx.userHome, ".claude"),
|
|
21672
21474
|
cliScriptPath,
|
|
21673
21475
|
nodePath: process.execPath,
|
|
21674
21476
|
dryRun: opts.dryRun,
|
|
@@ -21677,20 +21479,20 @@ async function runInit(ctx, opts = { skipClaude: false, skipShared: false, dryRu
|
|
|
21677
21479
|
reportInstallResult(ctx, result, opts.dryRun);
|
|
21678
21480
|
}
|
|
21679
21481
|
function migrateLegacyCommunityDir(ctx) {
|
|
21680
|
-
const legacy =
|
|
21482
|
+
const legacy = join10(ctx.paths.knowledgeRepo, "community");
|
|
21681
21483
|
const current = ctx.paths.communityDir;
|
|
21682
21484
|
if (legacy === current) return;
|
|
21683
|
-
if (!
|
|
21684
|
-
if (
|
|
21485
|
+
if (!existsSync8(legacy)) return;
|
|
21486
|
+
if (existsSync8(current)) {
|
|
21685
21487
|
ctx.logger.warn(
|
|
21686
21488
|
`legacy community dir still exists at ${legacy} \u2014 remove manually (new location in use)`
|
|
21687
21489
|
);
|
|
21688
21490
|
return;
|
|
21689
21491
|
}
|
|
21690
|
-
|
|
21691
|
-
for (const entry of
|
|
21492
|
+
mkdirSync4(current, { recursive: true });
|
|
21493
|
+
for (const entry of readdirSync4(legacy, { withFileTypes: true })) {
|
|
21692
21494
|
if (!entry.isDirectory()) continue;
|
|
21693
|
-
renameSync(
|
|
21495
|
+
renameSync(join10(legacy, entry.name), join10(current, entry.name));
|
|
21694
21496
|
}
|
|
21695
21497
|
try {
|
|
21696
21498
|
rmdirSync(legacy);
|
|
@@ -21698,52 +21500,6 @@ function migrateLegacyCommunityDir(ctx) {
|
|
|
21698
21500
|
}
|
|
21699
21501
|
ctx.logger.info(`migrated legacy community/ \u2192 ${current}`);
|
|
21700
21502
|
}
|
|
21701
|
-
async function subscribeSharedRepo(ctx, db) {
|
|
21702
|
-
const url = ctx.config.sharedRepo;
|
|
21703
|
-
const validation = validateCommunityUrl(url);
|
|
21704
|
-
if (!validation.valid) {
|
|
21705
|
-
ctx.logger.warn(
|
|
21706
|
-
`sharedRepo is not a valid GitHub URL \u2014 skipping: ${validation.reason}`
|
|
21707
|
-
);
|
|
21708
|
-
return;
|
|
21709
|
-
}
|
|
21710
|
-
const handle = validation.handle;
|
|
21711
|
-
const target = join12(ctx.paths.communityDir, handle);
|
|
21712
|
-
if (!existsSync10(target)) {
|
|
21713
|
-
if (!existsSync10(ctx.paths.communityDir)) {
|
|
21714
|
-
mkdirSync5(ctx.paths.communityDir, { recursive: true });
|
|
21715
|
-
}
|
|
21716
|
-
try {
|
|
21717
|
-
await communityAdd({
|
|
21718
|
-
url,
|
|
21719
|
-
communityDir: ctx.paths.communityDir,
|
|
21720
|
-
logger: ctx.logger
|
|
21721
|
-
});
|
|
21722
|
-
ctx.logger.info(`shared community DB subscribed: ${url} \u2192 community/${handle}/`);
|
|
21723
|
-
} catch (err) {
|
|
21724
|
-
ctx.logger.warn(
|
|
21725
|
-
`shared community DB subscription failed: ${err instanceof Error ? err.message : String(err)}`
|
|
21726
|
-
);
|
|
21727
|
-
return;
|
|
21728
|
-
}
|
|
21729
|
-
} else {
|
|
21730
|
-
ctx.logger.info(`shared community DB already subscribed: community/${handle}/`);
|
|
21731
|
-
}
|
|
21732
|
-
if (existsSync10(ctx.paths.communityDir)) {
|
|
21733
|
-
rebuildAll(db);
|
|
21734
|
-
if (existsSync10(ctx.paths.entriesDir)) {
|
|
21735
|
-
scanSource({ db, source: "own", entriesRoot: ctx.paths.entriesDir });
|
|
21736
|
-
}
|
|
21737
|
-
for (const entry of readdirSync6(ctx.paths.communityDir, { withFileTypes: true })) {
|
|
21738
|
-
if (!entry.isDirectory()) continue;
|
|
21739
|
-
const source = `community/${entry.name}`;
|
|
21740
|
-
const root = join12(ctx.paths.communityDir, entry.name, "entries");
|
|
21741
|
-
if (!existsSync10(root)) continue;
|
|
21742
|
-
const result = scanSource({ db, source, entriesRoot: root });
|
|
21743
|
-
ctx.logger.info(`indexed ${source}: +${result.added}`);
|
|
21744
|
-
}
|
|
21745
|
-
}
|
|
21746
|
-
}
|
|
21747
21503
|
function runUninstall(ctx, opts) {
|
|
21748
21504
|
const cliScriptPath = process.argv[1];
|
|
21749
21505
|
if (!cliScriptPath) {
|
|
@@ -21751,7 +21507,7 @@ function runUninstall(ctx, opts) {
|
|
|
21751
21507
|
process.exit(1);
|
|
21752
21508
|
}
|
|
21753
21509
|
const result = uninstallClaudeIntegration({
|
|
21754
|
-
claudeDir:
|
|
21510
|
+
claudeDir: join10(ctx.userHome, ".claude"),
|
|
21755
21511
|
cliScriptPath,
|
|
21756
21512
|
nodePath: process.execPath,
|
|
21757
21513
|
dryRun: opts.dryRun,
|
|
@@ -21788,29 +21544,29 @@ function reportInstallResult(ctx, result, dryRun) {
|
|
|
21788
21544
|
}
|
|
21789
21545
|
|
|
21790
21546
|
// src/commands/indexCmd.ts
|
|
21791
|
-
import { existsSync as
|
|
21792
|
-
import { dirname as dirname7, join as
|
|
21547
|
+
import { existsSync as existsSync9, readdirSync as readdirSync5, mkdirSync as mkdirSync5 } from "node:fs";
|
|
21548
|
+
import { dirname as dirname7, join as join11 } from "node:path";
|
|
21793
21549
|
function runIndex(ctx, opts) {
|
|
21794
21550
|
const dbDir = dirname7(ctx.paths.dbPath);
|
|
21795
|
-
if (!
|
|
21551
|
+
if (!existsSync9(dbDir)) mkdirSync5(dbDir, { recursive: true });
|
|
21796
21552
|
const db = openDb({ path: ctx.paths.dbPath, logger: ctx.logger });
|
|
21797
21553
|
try {
|
|
21798
21554
|
if (opts.full) {
|
|
21799
21555
|
ctx.logger.info("full rebuild: DELETE FROM entries");
|
|
21800
21556
|
rebuildAll(db);
|
|
21801
21557
|
}
|
|
21802
|
-
if (
|
|
21558
|
+
if (existsSync9(ctx.paths.entriesDir)) {
|
|
21803
21559
|
const result = scanSource({ db, source: "own", entriesRoot: ctx.paths.entriesDir });
|
|
21804
21560
|
ctx.logger.info(`own: +${result.added} ~${result.updated} -${result.deleted}`);
|
|
21805
21561
|
} else {
|
|
21806
21562
|
ctx.logger.warn(`entries dir not found: ${ctx.paths.entriesDir}`);
|
|
21807
21563
|
}
|
|
21808
|
-
if (
|
|
21809
|
-
for (const entry of
|
|
21564
|
+
if (existsSync9(ctx.paths.communityDir)) {
|
|
21565
|
+
for (const entry of readdirSync5(ctx.paths.communityDir, { withFileTypes: true })) {
|
|
21810
21566
|
if (!entry.isDirectory()) continue;
|
|
21811
21567
|
const source = `community/${entry.name}`;
|
|
21812
|
-
const root =
|
|
21813
|
-
if (!
|
|
21568
|
+
const root = join11(ctx.paths.communityDir, entry.name, "entries");
|
|
21569
|
+
if (!existsSync9(root)) continue;
|
|
21814
21570
|
const result = scanSource({ db, source, entriesRoot: root });
|
|
21815
21571
|
ctx.logger.info(`${source}: +${result.added} ~${result.updated} -${result.deleted}`);
|
|
21816
21572
|
}
|
|
@@ -22584,11 +22340,11 @@ var serve = (options2, listeningListener) => {
|
|
|
22584
22340
|
|
|
22585
22341
|
// ../web/dist/context.js
|
|
22586
22342
|
import { homedir as homedir2 } from "node:os";
|
|
22587
|
-
import { join as
|
|
22343
|
+
import { join as join12 } from "node:path";
|
|
22588
22344
|
function buildWebContext(overrides = {}) {
|
|
22589
22345
|
const userHome = overrides.userHome ?? homedir2();
|
|
22590
22346
|
const caveatHome = overrides.caveatHome ?? findCaveatHome(userHome);
|
|
22591
|
-
const userConfigPath =
|
|
22347
|
+
const userConfigPath = join12(userHome, ".caveatrc.json");
|
|
22592
22348
|
const logger = overrides.logger ?? stderrLogger;
|
|
22593
22349
|
const config3 = loadConfig(userConfigPath);
|
|
22594
22350
|
const paths = resolvePaths(caveatHome, config3.knowledgeRepo, userHome);
|
|
@@ -24693,7 +24449,7 @@ ul.entries li { padding: 0.5rem 0; border-bottom: 1px solid #eee; }
|
|
|
24693
24449
|
ul.entries a.title { font-weight: 600; color: #0a5fff; text-decoration: none; }
|
|
24694
24450
|
ul.entries a.title:hover { text-decoration: underline; }
|
|
24695
24451
|
ul.entries .meta { font-size: 0.85rem; color: #666; margin-top: 0.2rem; }
|
|
24696
|
-
|
|
24452
|
+
.badge {
|
|
24697
24453
|
display: inline-block;
|
|
24698
24454
|
padding: 0 0.4rem;
|
|
24699
24455
|
margin-right: 0.3rem;
|
|
@@ -24701,10 +24457,12 @@ ul.entries .meta .badge {
|
|
|
24701
24457
|
background: #f0f0f0;
|
|
24702
24458
|
font-size: 0.8rem;
|
|
24703
24459
|
}
|
|
24704
|
-
|
|
24705
|
-
|
|
24706
|
-
|
|
24707
|
-
|
|
24460
|
+
.badge.confirmed { background: #d7f4dc; color: #16703a; }
|
|
24461
|
+
.badge.reproduced { background: #e6eeff; color: #234ea3; }
|
|
24462
|
+
.badge.tentative { background: #fff4d1; color: #8a5a00; }
|
|
24463
|
+
.badge.impossible { background: #ffd9d9; color: #8a1a1a; }
|
|
24464
|
+
.badge.public { background: #eef2f5; color: #5a6470; }
|
|
24465
|
+
.badge.private { background: #fde7e7; color: #a02525; font-weight: 600; }
|
|
24708
24466
|
ul.entries .excerpt { margin-top: 0.3rem; color: #444; font-size: 0.92rem; }
|
|
24709
24467
|
article h2 { border-bottom: 1px solid #eee; padding-bottom: 0.2rem; margin-top: 1.6rem; }
|
|
24710
24468
|
article pre {
|
|
@@ -24779,6 +24537,7 @@ function createIndexRoute(ctx) {
|
|
|
24779
24537
|
const q2 = c3.req.query("q")?.trim() ?? "";
|
|
24780
24538
|
const sourceParam = c3.req.query("source");
|
|
24781
24539
|
const tagParam = c3.req.query("tag");
|
|
24540
|
+
const visibilityParam = c3.req.query("visibility");
|
|
24782
24541
|
const filters = {};
|
|
24783
24542
|
if (sourceParam === "own" || sourceParam === "community" || sourceParam === "all") {
|
|
24784
24543
|
filters.source = sourceParam;
|
|
@@ -24786,17 +24545,22 @@ function createIndexRoute(ctx) {
|
|
|
24786
24545
|
if (tagParam) {
|
|
24787
24546
|
filters.tags = [tagParam];
|
|
24788
24547
|
}
|
|
24789
|
-
|
|
24548
|
+
if (visibilityParam === "public" || visibilityParam === "private") {
|
|
24549
|
+
filters.visibility = visibilityParam;
|
|
24550
|
+
}
|
|
24551
|
+
const results = search(ctx.db, { query: q2, filters, limit: 50 });
|
|
24790
24552
|
const rows = results.map((r2) => {
|
|
24791
24553
|
const envEntries = Object.entries(r2.environment ?? {});
|
|
24792
24554
|
const envSummary = envEntries.slice(0, 3).map(([k2, v]) => `${escapeHtml(k2)}:${escapeHtml(String(v))}`).join(" ");
|
|
24793
24555
|
const hrefSource = r2.source === "own" ? "" : `?source=${encodeURIComponent(r2.source)}`;
|
|
24556
|
+
const visibilityBadge = r2.visibility === "private" ? `<span class="badge private" title="local-only \u2014 never pushed to community DB">\u{1F512} private</span>` : `<span class="badge public">public</span>`;
|
|
24794
24557
|
return `
|
|
24795
24558
|
<li>
|
|
24796
24559
|
<a class="title" href="/g/${encodeURIComponent(r2.id)}${hrefSource}">${escapeHtml(r2.title)}</a>
|
|
24797
24560
|
<div class="meta">
|
|
24798
24561
|
<span class="badge ${escapeHtml(r2.confidence ?? "")}">${escapeHtml(r2.confidence ?? "")}</span>
|
|
24799
24562
|
<span class="badge">${escapeHtml(r2.source)}</span>
|
|
24563
|
+
${visibilityBadge}
|
|
24800
24564
|
${envSummary ? `<span>${envSummary}</span>` : ""}
|
|
24801
24565
|
</div>
|
|
24802
24566
|
${r2.symptomExcerpt ? `<div class="excerpt">${escapeHtml(r2.symptomExcerpt)}</div>` : ""}
|
|
@@ -24810,9 +24574,18 @@ function createIndexRoute(ctx) {
|
|
|
24810
24574
|
<option value="own" ${sourceParam === "own" ? "selected" : ""}>own</option>
|
|
24811
24575
|
<option value="community" ${sourceParam === "community" ? "selected" : ""}>community</option>
|
|
24812
24576
|
</select>
|
|
24577
|
+
<select name="visibility" title="visibility filter \u2014 useful when sharing screen">
|
|
24578
|
+
<option value="" ${!visibilityParam ? "selected" : ""}>any visibility</option>
|
|
24579
|
+
<option value="public" ${visibilityParam === "public" ? "selected" : ""}>public only</option>
|
|
24580
|
+
<option value="private" ${visibilityParam === "private" ? "selected" : ""}>private only</option>
|
|
24581
|
+
</select>
|
|
24813
24582
|
<button type="submit">search</button>
|
|
24814
24583
|
</form>
|
|
24815
|
-
${results.length > 0 ? `<ul class="entries">${rows}</ul>` :
|
|
24584
|
+
${results.length > 0 ? `<ul class="entries">${rows}</ul>` : (() => {
|
|
24585
|
+
if (q2) return `<div class="empty">no results for "${escapeHtml(q2)}"</div>`;
|
|
24586
|
+
const hasFilter = !!(sourceParam || tagParam || visibilityParam);
|
|
24587
|
+
return hasFilter ? `<div class="empty">no entries match the active filters</div>` : `<div class="empty">no entries yet \u2014 run <code>caveat index</code> after adding md files</div>`;
|
|
24588
|
+
})()}`;
|
|
24816
24589
|
const title = q2 ? `search: ${q2} \xB7 Caveat` : "Caveat";
|
|
24817
24590
|
return c3.html(layout(title, body));
|
|
24818
24591
|
});
|
|
@@ -30278,10 +30051,11 @@ function createDetailRoute(ctx) {
|
|
|
30278
30051
|
}
|
|
30279
30052
|
const fm = entry.frontmatter;
|
|
30280
30053
|
const envRows = Object.entries(fm.environment ?? {}).map(([k2, v]) => `<dt>${escapeHtml(k2)}</dt><dd>${escapeHtml(String(v))}</dd>`).join("");
|
|
30054
|
+
const visibilityBadge = fm.visibility === "private" ? `<span class="badge private" title="local-only \u2014 never pushed to community DB">\u{1F512} private</span>` : `<span class="badge public">public</span>`;
|
|
30281
30055
|
const metaRows = [
|
|
30282
30056
|
`<dt>id</dt><dd><code>${escapeHtml(fm.id)}</code></dd>`,
|
|
30283
30057
|
`<dt>source</dt><dd>${escapeHtml(entry.source)}</dd>`,
|
|
30284
|
-
`<dt>visibility</dt><dd>${
|
|
30058
|
+
`<dt>visibility</dt><dd>${visibilityBadge}</dd>`,
|
|
30285
30059
|
`<dt>confidence</dt><dd><span class="badge ${escapeHtml(fm.confidence)}">${escapeHtml(fm.confidence)}</span></dd>`
|
|
30286
30060
|
];
|
|
30287
30061
|
if (fm.outcome) {
|
|
@@ -30315,14 +30089,14 @@ function createDetailRoute(ctx) {
|
|
|
30315
30089
|
}
|
|
30316
30090
|
|
|
30317
30091
|
// ../web/dist/routes/community.js
|
|
30318
|
-
import { existsSync as
|
|
30319
|
-
import { join as
|
|
30092
|
+
import { existsSync as existsSync10, readdirSync as readdirSync6, statSync as statSync5 } from "node:fs";
|
|
30093
|
+
import { join as join13 } from "node:path";
|
|
30320
30094
|
function listCommunity(communityDir, db) {
|
|
30321
|
-
if (!
|
|
30095
|
+
if (!existsSync10(communityDir)) return [];
|
|
30322
30096
|
const handles = [];
|
|
30323
|
-
for (const entry of
|
|
30097
|
+
for (const entry of readdirSync6(communityDir, { withFileTypes: true })) {
|
|
30324
30098
|
if (!entry.isDirectory()) continue;
|
|
30325
|
-
const handlePath =
|
|
30099
|
+
const handlePath = join13(communityDir, entry.name);
|
|
30326
30100
|
const countRow = db.prepare("SELECT COUNT(*) AS n FROM entries WHERE source = ?").get(`community/${entry.name}`);
|
|
30327
30101
|
handles.push({
|
|
30328
30102
|
handle: entry.name,
|
|
@@ -30339,7 +30113,7 @@ function createCommunityRoute(ctx) {
|
|
|
30339
30113
|
const list2 = handles.map((h2) => {
|
|
30340
30114
|
let mtime = "";
|
|
30341
30115
|
try {
|
|
30342
|
-
mtime =
|
|
30116
|
+
mtime = statSync5(h2.path).mtime.toISOString().slice(0, 10);
|
|
30343
30117
|
} catch {
|
|
30344
30118
|
mtime = "";
|
|
30345
30119
|
}
|
|
@@ -44620,11 +44394,11 @@ var StdioServerTransport = class {
|
|
|
44620
44394
|
|
|
44621
44395
|
// ../mcp/dist/context.js
|
|
44622
44396
|
import { homedir as homedir3 } from "node:os";
|
|
44623
|
-
import { join as
|
|
44397
|
+
import { join as join14 } from "node:path";
|
|
44624
44398
|
function buildMcpContext(overrides = {}) {
|
|
44625
44399
|
const userHome = overrides.userHome ?? homedir3();
|
|
44626
44400
|
const caveatHome = overrides.caveatHome ?? findCaveatHome(userHome);
|
|
44627
|
-
const userConfigPath =
|
|
44401
|
+
const userConfigPath = join14(userHome, ".caveatrc.json");
|
|
44628
44402
|
const logger = overrides.logger ?? stderrLogger;
|
|
44629
44403
|
const config3 = loadConfig(userConfigPath);
|
|
44630
44404
|
const paths = resolvePaths(caveatHome, config3.knowledgeRepo, userHome);
|
|
@@ -44680,7 +44454,9 @@ var recordInputShape = {
|
|
|
44680
44454
|
context: external_exports.string().optional(),
|
|
44681
44455
|
confidence: confidenceSchema2.optional(),
|
|
44682
44456
|
outcome: outcomeSchema.optional(),
|
|
44683
|
-
visibility: visibilitySchema.
|
|
44457
|
+
visibility: visibilitySchema.describe(
|
|
44458
|
+
'REQUIRED. Ask the user "public (share to community) or private (keep local only)?" before calling \u2014 never auto-classify. The user owns the knowledge; they decide its reach.'
|
|
44459
|
+
),
|
|
44684
44460
|
tags: external_exports.array(external_exports.string()).optional(),
|
|
44685
44461
|
environment: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
44686
44462
|
category: external_exports.string().optional().describe("Directory under entries/ (e.g., gpu, claude-code). Default: misc")
|
|
@@ -44731,45 +44507,37 @@ function handleListRecent(ctx, args) {
|
|
|
44731
44507
|
}
|
|
44732
44508
|
|
|
44733
44509
|
// ../mcp/dist/tools/pull.js
|
|
44510
|
+
import { existsSync as existsSync11, readdirSync as readdirSync7 } from "node:fs";
|
|
44511
|
+
import { join as join15 } from "node:path";
|
|
44734
44512
|
var pullInputShape = {};
|
|
44735
44513
|
async function handlePull(ctx, _args = {}) {
|
|
44736
|
-
const
|
|
44737
|
-
|
|
44738
|
-
|
|
44739
|
-
|
|
44740
|
-
|
|
44741
|
-
|
|
44742
|
-
|
|
44743
|
-
|
|
44744
|
-
|
|
44745
|
-
|
|
44746
|
-
|
|
44747
|
-
|
|
44748
|
-
|
|
44749
|
-
})
|
|
44750
|
-
|
|
44751
|
-
}
|
|
44752
|
-
|
|
44753
|
-
|
|
44754
|
-
|
|
44755
|
-
|
|
44756
|
-
|
|
44757
|
-
|
|
44758
|
-
|
|
44759
|
-
|
|
44760
|
-
|
|
44761
|
-
|
|
44762
|
-
|
|
44763
|
-
id: args.id,
|
|
44764
|
-
dryRun: args.dry_run ?? false,
|
|
44765
|
-
logger: ctx.logger
|
|
44766
|
-
});
|
|
44767
|
-
return {
|
|
44768
|
-
status: result.status,
|
|
44769
|
-
detail: result.detail,
|
|
44770
|
-
pr_url: result.prUrl,
|
|
44771
|
-
planned_steps: result.plannedSteps
|
|
44772
|
-
};
|
|
44514
|
+
const pulled = [];
|
|
44515
|
+
const indexed = [];
|
|
44516
|
+
if (existsSync11(ctx.paths.communityDir)) {
|
|
44517
|
+
const results = await communityPull({
|
|
44518
|
+
communityDir: ctx.paths.communityDir,
|
|
44519
|
+
logger: ctx.logger
|
|
44520
|
+
});
|
|
44521
|
+
for (const r2 of results) {
|
|
44522
|
+
pulled.push({ handle: r2.handle, status: r2.status, message: r2.message });
|
|
44523
|
+
}
|
|
44524
|
+
}
|
|
44525
|
+
rebuildAll(ctx.db);
|
|
44526
|
+
if (existsSync11(ctx.paths.entriesDir)) {
|
|
44527
|
+
const own = scanSource({ db: ctx.db, source: "own", entriesRoot: ctx.paths.entriesDir });
|
|
44528
|
+
indexed.push({ source: "own", ...own });
|
|
44529
|
+
}
|
|
44530
|
+
if (existsSync11(ctx.paths.communityDir)) {
|
|
44531
|
+
for (const entry of readdirSync7(ctx.paths.communityDir, { withFileTypes: true })) {
|
|
44532
|
+
if (!entry.isDirectory()) continue;
|
|
44533
|
+
const source = `community/${entry.name}`;
|
|
44534
|
+
const root = join15(ctx.paths.communityDir, entry.name, "entries");
|
|
44535
|
+
if (!existsSync11(root)) continue;
|
|
44536
|
+
const scan = scanSource({ db: ctx.db, source, entriesRoot: root });
|
|
44537
|
+
indexed.push({ source, ...scan });
|
|
44538
|
+
}
|
|
44539
|
+
}
|
|
44540
|
+
return { pulled, indexed };
|
|
44773
44541
|
}
|
|
44774
44542
|
|
|
44775
44543
|
// ../mcp/dist/registerTools.js
|
|
@@ -44788,7 +44556,7 @@ function registerAllTools(server, ctx) {
|
|
|
44788
44556
|
"caveat_search",
|
|
44789
44557
|
{
|
|
44790
44558
|
title: "caveat_search",
|
|
44791
|
-
description: "
|
|
44559
|
+
description: 'Search the "caveats" knowledge base \u2014 records of time-wasting traps in EXTERNAL specs (GPU/driver/CUDA versions, native-module builds, IDE/shell quirks, platform-specific behavior, library version incompatibilities) that someone already diagnosed. Call this FIRST when a problem smells environmental rather than a logic bug \u2014 before reading stack traces top-to-bottom or trying fixes. Query: 3+ chars, plain tokens only (no OR/NEAR/other FTS5 operators). Returns summary rows including `{id, source}` \u2014 pass BOTH to caveat_get for the full body.',
|
|
44792
44560
|
inputSchema: searchInputShape
|
|
44793
44561
|
},
|
|
44794
44562
|
async (args) => jsonResult(handleSearch(ctx, args))
|
|
@@ -44797,7 +44565,7 @@ function registerAllTools(server, ctx) {
|
|
|
44797
44565
|
"caveat_get",
|
|
44798
44566
|
{
|
|
44799
44567
|
title: "caveat_get",
|
|
44800
|
-
description:
|
|
44568
|
+
description: 'Fetch the full body (frontmatter + H2 sections + text) of a caveat by id. IMPORTANT: when the id came from caveat_search, you MUST pass the `source` field from that same result (e.g., "community/Caveat"). The default source is "own" only; omitting source for a community entry returns not-found.',
|
|
44801
44569
|
inputSchema: getInputShape
|
|
44802
44570
|
},
|
|
44803
44571
|
async (args) => jsonResult(handleGet(ctx, args))
|
|
@@ -44806,7 +44574,7 @@ function registerAllTools(server, ctx) {
|
|
|
44806
44574
|
"caveat_record",
|
|
44807
44575
|
{
|
|
44808
44576
|
title: "caveat_record",
|
|
44809
|
-
description: "Create a new caveat
|
|
44577
|
+
description: "Create a new caveat: a record of an external-spec trap that wasted real time (wrong driver, version mismatch, platform bug, IDE quirk, native-module issue, etc.) so future sessions can find it via caveat_search. REQUIRED BEFORE CALLING: (1) run caveat_search first to avoid duplicates, (2) ASK THE USER whether this should be `public` (shareable to the community DB) or `private` (kept local only) \u2014 never auto-classify visibility; the user owns the knowledge and decides its reach. Qualifies: specific symptom + diagnosed cause (or `outcome: impossible` verdict) + environment fingerprint. Does NOT qualify: project-internal bugs, user preferences, session summaries, ephemeral task notes. Auto-fills source_session and environment defaults; source_project is left null by design (shared knowledge must not leak per-user project names).",
|
|
44810
44578
|
inputSchema: recordInputShape
|
|
44811
44579
|
},
|
|
44812
44580
|
async (args) => jsonResult(handleRecord(ctx, args))
|
|
@@ -44815,7 +44583,7 @@ function registerAllTools(server, ctx) {
|
|
|
44815
44583
|
"caveat_update",
|
|
44816
44584
|
{
|
|
44817
44585
|
title: "caveat_update",
|
|
44818
|
-
description: "Patch an existing caveat. Frontmatter shallow-merges (
|
|
44586
|
+
description: "Patch an existing caveat \u2014 use when newer evidence extends or corrects one that already exists. Frontmatter shallow-merges, but array fields (tags etc.) REPLACE rather than append \u2014 to add one tag, read the current list first, then patch with the full new array. Sections match by case-insensitive H2 heading. Immutable keys: id, created_at, source_session, source_project. Common uses: bump `last_verified` after re-confirming, add a resolution when it was `tentative`, flip `outcome` to `impossible`.",
|
|
44819
44587
|
inputSchema: updateInputShape
|
|
44820
44588
|
},
|
|
44821
44589
|
async (args) => jsonResult(handleUpdate(ctx, args))
|
|
@@ -44824,7 +44592,7 @@ function registerAllTools(server, ctx) {
|
|
|
44824
44592
|
"caveat_list_recent",
|
|
44825
44593
|
{
|
|
44826
44594
|
title: "caveat_list_recent",
|
|
44827
|
-
description: "List caveats by updated_at DESC.",
|
|
44595
|
+
description: "List caveats ordered by updated_at DESC. Use for browsing recent additions \u2014 e.g., showing the user what is new after caveat_pull. Not for search; use caveat_search when you have a query.",
|
|
44828
44596
|
inputSchema: listRecentInputShape
|
|
44829
44597
|
},
|
|
44830
44598
|
async (args) => jsonResult(handleListRecent(ctx, args))
|
|
@@ -44833,20 +44601,11 @@ function registerAllTools(server, ctx) {
|
|
|
44833
44601
|
"caveat_pull",
|
|
44834
44602
|
{
|
|
44835
44603
|
title: "caveat_pull",
|
|
44836
|
-
description: "git-pull
|
|
44604
|
+
description: "git-pull every subscribed community caveat repo (added via `caveat community add`) and re-index. Call when: (a) the user explicitly asks about others' knowledge on a topic, or (b) caveat_search returned empty for a query that feels like it should have hits and a subscribed repo might be stale. Do NOT call reflexively at session start \u2014 it is cheap but not free, and stale-by-minutes is acceptable. Safe and idempotent.",
|
|
44837
44605
|
inputSchema: pullInputShape
|
|
44838
44606
|
},
|
|
44839
44607
|
async (args) => jsonResult(await handlePull(ctx, args))
|
|
44840
44608
|
);
|
|
44841
|
-
server.registerTool(
|
|
44842
|
-
"caveat_push",
|
|
44843
|
-
{
|
|
44844
|
-
title: "caveat_push",
|
|
44845
|
-
description: "Contribute a user-owned caveat to the shared community DB via fork + PR. Call this after caveat_record when the entry looks genuinely reusable by others (not a one-off project tie-in, not duplicated by existing community entries). Requires the `gh` CLI on the user's machine; returns status=gh-missing or gh-unauthed when unavailable. Use dry_run=true to preview without touching GitHub.",
|
|
44846
|
-
inputSchema: pushInputShape
|
|
44847
|
-
},
|
|
44848
|
-
async (args) => jsonResult(await handlePush(ctx, args))
|
|
44849
|
-
);
|
|
44850
44609
|
}
|
|
44851
44610
|
|
|
44852
44611
|
// ../mcp/dist/server.js
|
|
@@ -44939,77 +44698,46 @@ async function runHook(name) {
|
|
|
44939
44698
|
}
|
|
44940
44699
|
|
|
44941
44700
|
// src/commands/pull.ts
|
|
44942
|
-
import { existsSync as
|
|
44701
|
+
import { existsSync as existsSync12, readdirSync as readdirSync8 } from "node:fs";
|
|
44702
|
+
import { join as join16 } from "node:path";
|
|
44943
44703
|
async function runPull(ctx) {
|
|
44944
|
-
if (!
|
|
44704
|
+
if (!existsSync12(ctx.paths.communityDir)) {
|
|
44945
44705
|
ctx.logger.info(
|
|
44946
|
-
"no community repos yet \u2014
|
|
44706
|
+
"no community repos yet \u2014 add one with `caveat community add <github-url>`."
|
|
44947
44707
|
);
|
|
44948
44708
|
return;
|
|
44949
44709
|
}
|
|
44710
|
+
const pulls = await communityPull({
|
|
44711
|
+
communityDir: ctx.paths.communityDir,
|
|
44712
|
+
logger: ctx.logger
|
|
44713
|
+
});
|
|
44714
|
+
for (const p2 of pulls) {
|
|
44715
|
+
if (p2.status === "ok") {
|
|
44716
|
+
ctx.logger.info(`${p2.handle}: pulled`);
|
|
44717
|
+
} else {
|
|
44718
|
+
ctx.logger.warn(`${p2.handle}: FAILED \u2014 ${p2.message ?? "unknown"}`);
|
|
44719
|
+
}
|
|
44720
|
+
}
|
|
44950
44721
|
const db = openDb({ path: ctx.paths.dbPath, logger: ctx.logger });
|
|
44951
44722
|
try {
|
|
44952
|
-
|
|
44953
|
-
|
|
44954
|
-
|
|
44955
|
-
|
|
44956
|
-
logger: ctx.logger
|
|
44957
|
-
});
|
|
44958
|
-
for (const p2 of result.pulled) {
|
|
44959
|
-
if (p2.status === "ok") {
|
|
44960
|
-
ctx.logger.info(`${p2.handle}: pulled`);
|
|
44961
|
-
} else {
|
|
44962
|
-
ctx.logger.warn(`${p2.handle}: FAILED \u2014 ${p2.message ?? "unknown"}`);
|
|
44963
|
-
}
|
|
44723
|
+
rebuildAll(db);
|
|
44724
|
+
if (existsSync12(ctx.paths.entriesDir)) {
|
|
44725
|
+
const own = scanSource({ db, source: "own", entriesRoot: ctx.paths.entriesDir });
|
|
44726
|
+
ctx.logger.info(`own: +${own.added}`);
|
|
44964
44727
|
}
|
|
44965
|
-
for (const
|
|
44966
|
-
|
|
44728
|
+
for (const entry of readdirSync8(ctx.paths.communityDir, { withFileTypes: true })) {
|
|
44729
|
+
if (!entry.isDirectory()) continue;
|
|
44730
|
+
const source = `community/${entry.name}`;
|
|
44731
|
+
const root = join16(ctx.paths.communityDir, entry.name, "entries");
|
|
44732
|
+
if (!existsSync12(root)) continue;
|
|
44733
|
+
const scan = scanSource({ db, source, entriesRoot: root });
|
|
44734
|
+
ctx.logger.info(`${source}: +${scan.added}`);
|
|
44967
44735
|
}
|
|
44968
44736
|
} finally {
|
|
44969
44737
|
db.close();
|
|
44970
44738
|
}
|
|
44971
44739
|
}
|
|
44972
44740
|
|
|
44973
|
-
// src/commands/push.ts
|
|
44974
|
-
async function runPush(ctx, opts) {
|
|
44975
|
-
const result = await pushEntry({
|
|
44976
|
-
entriesDir: ctx.paths.entriesDir,
|
|
44977
|
-
caveatHome: ctx.caveatHome,
|
|
44978
|
-
sharedRepoUrl: ctx.config.sharedRepo,
|
|
44979
|
-
id: opts.id,
|
|
44980
|
-
dryRun: opts.dryRun,
|
|
44981
|
-
logger: ctx.logger
|
|
44982
|
-
});
|
|
44983
|
-
switch (result.status) {
|
|
44984
|
-
case "ok":
|
|
44985
|
-
ctx.logger.info(`PR opened: ${result.prUrl ?? ""}`);
|
|
44986
|
-
ctx.logger.info(
|
|
44987
|
-
"Once merged, other subscribers will see your entry after their next `caveat pull`."
|
|
44988
|
-
);
|
|
44989
|
-
break;
|
|
44990
|
-
case "dry-run":
|
|
44991
|
-
for (const [i2, step] of (result.plannedSteps ?? []).entries()) {
|
|
44992
|
-
ctx.logger.info(`[dry-run] ${i2 + 1}. ${step}`);
|
|
44993
|
-
}
|
|
44994
|
-
break;
|
|
44995
|
-
case "not-found":
|
|
44996
|
-
ctx.logger.error(
|
|
44997
|
-
`${result.detail}. List entries with \`caveat list\` or write + index first.`
|
|
44998
|
-
);
|
|
44999
|
-
process.exitCode = 1;
|
|
45000
|
-
break;
|
|
45001
|
-
case "gh-missing":
|
|
45002
|
-
case "gh-unauthed":
|
|
45003
|
-
ctx.logger.error(result.detail ?? "gh CLI issue");
|
|
45004
|
-
process.exitCode = 1;
|
|
45005
|
-
break;
|
|
45006
|
-
case "failed":
|
|
45007
|
-
ctx.logger.error(result.detail ?? "unknown failure");
|
|
45008
|
-
process.exitCode = 1;
|
|
45009
|
-
break;
|
|
45010
|
-
}
|
|
45011
|
-
}
|
|
45012
|
-
|
|
45013
44741
|
// src/commands/community.ts
|
|
45014
44742
|
async function runCommunityAdd(ctx, url) {
|
|
45015
44743
|
try {
|
|
@@ -45059,17 +44787,46 @@ function runCommunityList(ctx) {
|
|
|
45059
44787
|
db.close();
|
|
45060
44788
|
}
|
|
45061
44789
|
}
|
|
44790
|
+
function runCommunityRemove(ctx, handle, opts) {
|
|
44791
|
+
const db = openDb({ path: ctx.paths.dbPath, logger: ctx.logger });
|
|
44792
|
+
try {
|
|
44793
|
+
const result = communityRemove({
|
|
44794
|
+
communityDir: ctx.paths.communityDir,
|
|
44795
|
+
handle,
|
|
44796
|
+
db,
|
|
44797
|
+
dryRun: opts.dryRun,
|
|
44798
|
+
logger: ctx.logger
|
|
44799
|
+
});
|
|
44800
|
+
if (!result.dirExisted && result.rowCount === 0) {
|
|
44801
|
+
ctx.logger.warn(`no community subscription named "${handle}" \u2014 nothing to remove`);
|
|
44802
|
+
return;
|
|
44803
|
+
}
|
|
44804
|
+
const prefix = result.dryRun ? "[dry-run] would " : "";
|
|
44805
|
+
if (result.dirExisted) {
|
|
44806
|
+
ctx.logger.info(`${prefix}remove dir: ${result.path}`);
|
|
44807
|
+
}
|
|
44808
|
+
if (result.rowCount > 0) {
|
|
44809
|
+
ctx.logger.info(
|
|
44810
|
+
`${prefix}delete ${result.rowCount} db row(s) for source=community/${handle}`
|
|
44811
|
+
);
|
|
44812
|
+
}
|
|
44813
|
+
} catch (err) {
|
|
44814
|
+
ctx.logger.error(err instanceof Error ? err.message : String(err));
|
|
44815
|
+
process.exitCode = 1;
|
|
44816
|
+
} finally {
|
|
44817
|
+
db.close();
|
|
44818
|
+
}
|
|
44819
|
+
}
|
|
45062
44820
|
|
|
45063
44821
|
// src/index.ts
|
|
45064
44822
|
var program = new Command();
|
|
45065
44823
|
program.name("caveat").description("External spec gotcha knowledge base CLI").version(CAVEAT_VERSION);
|
|
45066
44824
|
program.command("init").description(
|
|
45067
|
-
"Initialize ~/.caveatrc.json, ~/.caveat/,
|
|
45068
|
-
).option("--skip-claude", "skip Claude Code MCP + hook registration", false).option("--
|
|
44825
|
+
"Initialize ~/.caveatrc.json, ~/.caveat/, and register Claude Code integration. Add knowledge sources later with `caveat community add <github-url>`."
|
|
44826
|
+
).option("--skip-claude", "skip Claude Code MCP + hook registration", false).option("--dry-run", "show planned changes without writing", false).action(async (opts) => {
|
|
45069
44827
|
const ctx = buildContext(stdoutLogger);
|
|
45070
44828
|
await runInit(ctx, {
|
|
45071
44829
|
skipClaude: opts.skipClaude,
|
|
45072
|
-
skipShared: opts.skipShared,
|
|
45073
44830
|
dryRun: opts.dryRun
|
|
45074
44831
|
});
|
|
45075
44832
|
});
|
|
@@ -45105,15 +44862,11 @@ program.command("stats").description("Show aggregate stats").action(() => {
|
|
|
45105
44862
|
runStats(ctx);
|
|
45106
44863
|
});
|
|
45107
44864
|
program.command("pull").description(
|
|
45108
|
-
"
|
|
44865
|
+
"git-pull every subscribed community repo and re-index. Use this to receive updates from group/teammate repos."
|
|
45109
44866
|
).action(async () => {
|
|
45110
44867
|
const ctx = buildContext(stdoutLogger);
|
|
45111
44868
|
await runPull(ctx);
|
|
45112
44869
|
});
|
|
45113
|
-
program.command("push").description("Contribute a caveat to the shared community DB via fork + PR (requires gh CLI).").argument("<id>", "entry id (from `caveat list`)").option("--dry-run", "show planned steps without making GitHub changes", false).action(async (id, opts) => {
|
|
45114
|
-
const ctx = buildContext(stdoutLogger);
|
|
45115
|
-
await runPush(ctx, { id, dryRun: opts.dryRun });
|
|
45116
|
-
});
|
|
45117
44870
|
program.command("serve").description("Start the read-only web share portal").option("--port <n>", "port number", (v) => Number(v), 4242).action((opts) => {
|
|
45118
44871
|
runServe({ port: opts.port });
|
|
45119
44872
|
});
|
|
@@ -45136,6 +44889,10 @@ community.command("list").description("List imported community repos with entry
|
|
|
45136
44889
|
const ctx = buildContext(stdoutLogger);
|
|
45137
44890
|
runCommunityList(ctx);
|
|
45138
44891
|
});
|
|
44892
|
+
community.command("remove <handle>").description("Unsubscribe from a community repo: delete community/<handle>/ and purge its DB rows").option("--dry-run", "show what would be removed without touching disk or db", false).action((handle, opts) => {
|
|
44893
|
+
const ctx = buildContext(stdoutLogger);
|
|
44894
|
+
runCommunityRemove(ctx, handle, { dryRun: opts.dryRun });
|
|
44895
|
+
});
|
|
45139
44896
|
program.parseAsync(process.argv).catch((err) => {
|
|
45140
44897
|
const msg = err instanceof Error ? err.message : String(err);
|
|
45141
44898
|
process.stderr.write(`[caveat:error] ${msg}
|