caveat-cli 0.6.2 → 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/caveat.js +0 -0
- package/dist/index.js +250 -507
- package/dist/index.js.map +1 -1
- package/package.json +12 -12
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,274 +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
|
-
if (owned.visibility === "private") {
|
|
21241
|
-
return {
|
|
21242
|
-
status: "visibility-private",
|
|
21243
|
-
detail: `entry id=${opts.id} has visibility: private and cannot be pushed to the public community DB. Update the entry to visibility: public first (the user declared this entry local-only).`
|
|
21244
|
-
};
|
|
21245
|
-
}
|
|
21246
|
-
const ghUser = resolveGhUser();
|
|
21247
|
-
if (!ghUser) {
|
|
21248
|
-
return { status: "failed", detail: "gh api user failed" };
|
|
21249
|
-
}
|
|
21250
|
-
if (opts.dryRun) {
|
|
21251
|
-
return {
|
|
21252
|
-
status: "dry-run",
|
|
21253
|
-
plannedSteps: [
|
|
21254
|
-
`fork ${sharedOwner}/${sharedName} as ${ghUser}/${sharedName}`,
|
|
21255
|
-
`clone fork \u2192 ${forkStagingDir(opts.caveatHome)}`,
|
|
21256
|
-
`copy entry \u2192 entries/${owned.relPath}`,
|
|
21257
|
-
"commit, push branch, open PR against main"
|
|
21258
|
-
]
|
|
21259
|
-
};
|
|
21260
|
-
}
|
|
21261
|
-
try {
|
|
21262
|
-
ensureFork(sharedOwner, sharedName);
|
|
21263
|
-
const stagingDir = forkStagingDir(opts.caveatHome);
|
|
21264
|
-
ensureStagingClone(stagingDir, ghUser, sharedName, sharedOwner);
|
|
21265
|
-
const branch = `caveat-push-${owned.id}-${Date.now().toString(36)}`;
|
|
21266
|
-
runOrThrow("git", ["checkout", "-b", branch], { cwd: stagingDir });
|
|
21267
|
-
const destPath = join7(stagingDir, "entries", owned.relPath);
|
|
21268
|
-
mkdirSync3(dirname4(destPath), { recursive: true });
|
|
21269
|
-
copyFileSync(owned.absPath, destPath);
|
|
21270
|
-
runOrThrow("git", ["add", join7("entries", owned.relPath)], { cwd: stagingDir });
|
|
21271
|
-
const isUpdate = upstreamAlreadyHas(stagingDir, "entries/" + owned.relPath);
|
|
21272
|
-
const verb = isUpdate ? "update" : "add";
|
|
21273
|
-
const commitMsg = `${verb}: ${owned.title}`;
|
|
21274
|
-
runOrThrow("git", ["commit", "-m", commitMsg], { cwd: stagingDir });
|
|
21275
|
-
runOrThrow("git", ["push", "--set-upstream", "origin", branch], { cwd: stagingDir });
|
|
21276
|
-
const prTitle = `${verb}: ${owned.title}`;
|
|
21277
|
-
const prBody = [
|
|
21278
|
-
`Contribution of caveat \`${owned.id}\` via \`caveat push\`.`,
|
|
21279
|
-
"",
|
|
21280
|
-
`- entry path: \`entries/${owned.relPath}\``,
|
|
21281
|
-
`- action: ${verb}`,
|
|
21282
|
-
"",
|
|
21283
|
-
"Merged PRs will appear in subscribers' repos on their next `caveat pull`."
|
|
21284
|
-
].join("\n");
|
|
21285
|
-
const pr = runCapture(
|
|
21286
|
-
"gh",
|
|
21287
|
-
[
|
|
21288
|
-
"pr",
|
|
21289
|
-
"create",
|
|
21290
|
-
"--repo",
|
|
21291
|
-
`${sharedOwner}/${sharedName}`,
|
|
21292
|
-
"--base",
|
|
21293
|
-
"main",
|
|
21294
|
-
"--head",
|
|
21295
|
-
`${ghUser}:${branch}`,
|
|
21296
|
-
"--title",
|
|
21297
|
-
prTitle,
|
|
21298
|
-
"--body",
|
|
21299
|
-
prBody
|
|
21300
|
-
],
|
|
21301
|
-
{ cwd: stagingDir }
|
|
21302
|
-
);
|
|
21303
|
-
if (pr.status !== 0) {
|
|
21304
|
-
return {
|
|
21305
|
-
status: "failed",
|
|
21306
|
-
detail: (pr.stderr || "gh pr create failed").trim()
|
|
21307
|
-
};
|
|
21308
|
-
}
|
|
21309
|
-
return { status: "ok", prUrl: pr.stdout.trim() };
|
|
21310
|
-
} catch (err) {
|
|
21311
|
-
return {
|
|
21312
|
-
status: "failed",
|
|
21313
|
-
detail: err instanceof Error ? err.message : String(err)
|
|
21314
|
-
};
|
|
21315
|
-
}
|
|
21316
|
-
}
|
|
21317
|
-
function shellQuote(s) {
|
|
21318
|
-
if (!/[\s&|<>^()"`$!*?\[\]{}\\]/.test(s)) return s;
|
|
21319
|
-
return `"${s.replace(/"/g, '""')}"`;
|
|
21320
|
-
}
|
|
21321
|
-
function runCapture(command, args, opts = {}) {
|
|
21322
|
-
const line = [command, ...args].map(shellQuote).join(" ");
|
|
21323
|
-
const r2 = spawnSync(line, {
|
|
21324
|
-
encoding: "utf-8",
|
|
21325
|
-
cwd: opts.cwd,
|
|
21326
|
-
shell: true
|
|
21327
|
-
});
|
|
21328
|
-
return {
|
|
21329
|
-
status: r2.status,
|
|
21330
|
-
stdout: typeof r2.stdout === "string" ? r2.stdout : "",
|
|
21331
|
-
stderr: typeof r2.stderr === "string" ? r2.stderr : ""
|
|
21332
|
-
};
|
|
21333
|
-
}
|
|
21334
|
-
function runOrThrow(command, args, opts = {}) {
|
|
21335
|
-
const r2 = runCapture(command, args, opts);
|
|
21336
|
-
if (r2.status !== 0) {
|
|
21337
|
-
throw new Error(
|
|
21338
|
-
`${command} ${args.join(" ")} failed: ${(r2.stderr || r2.stdout || "unknown").trim()}`
|
|
21339
|
-
);
|
|
21340
|
-
}
|
|
21341
|
-
}
|
|
21342
|
-
function checkGhAvailable() {
|
|
21343
|
-
return runCapture("gh", ["--version"]).status === 0;
|
|
21344
|
-
}
|
|
21345
|
-
function checkGhAuthed() {
|
|
21346
|
-
return runCapture("gh", ["auth", "status"]).status === 0;
|
|
21347
|
-
}
|
|
21348
|
-
function resolveGhUser() {
|
|
21349
|
-
const r2 = runCapture("gh", ["api", "user", "--jq", ".login"]);
|
|
21350
|
-
if (r2.status !== 0) return void 0;
|
|
21351
|
-
return r2.stdout.trim() || void 0;
|
|
21352
|
-
}
|
|
21353
|
-
function parseOwnerRepo(url) {
|
|
21354
|
-
const m = /^https:\/\/github\.com\/([^/]+)\/([^/]+?)(\.git)?\/?$/.exec(url);
|
|
21355
|
-
if (!m) return [void 0, void 0];
|
|
21356
|
-
return [m[1], m[2]];
|
|
21357
|
-
}
|
|
21358
|
-
function findOwnedEntry(entriesDir, id) {
|
|
21359
|
-
if (!existsSync7(entriesDir)) return void 0;
|
|
21360
|
-
for (const categoryDirent of readdirSync4(entriesDir, { withFileTypes: true })) {
|
|
21361
|
-
if (!categoryDirent.isDirectory()) continue;
|
|
21362
|
-
const categoryDir = join7(entriesDir, categoryDirent.name);
|
|
21363
|
-
for (const fileDirent of readdirSync4(categoryDir, { withFileTypes: true })) {
|
|
21364
|
-
if (!fileDirent.isFile() || !fileDirent.name.endsWith(".md")) continue;
|
|
21365
|
-
const absPath = join7(categoryDir, fileDirent.name);
|
|
21366
|
-
const raw2 = readFileSync5(absPath, "utf-8");
|
|
21367
|
-
try {
|
|
21368
|
-
const parsed = parseMarkdown(raw2);
|
|
21369
|
-
if (parsed.frontmatter.id === id) {
|
|
21370
|
-
const relPath = relative2(entriesDir, absPath).replace(/\\/g, "/");
|
|
21371
|
-
return {
|
|
21372
|
-
id,
|
|
21373
|
-
title: parsed.frontmatter.title,
|
|
21374
|
-
absPath,
|
|
21375
|
-
relPath,
|
|
21376
|
-
visibility: parsed.frontmatter.visibility
|
|
21377
|
-
};
|
|
21378
|
-
}
|
|
21379
|
-
} catch {
|
|
21380
|
-
}
|
|
21381
|
-
}
|
|
21382
|
-
}
|
|
21383
|
-
return void 0;
|
|
21384
|
-
}
|
|
21385
|
-
function forkStagingDir(caveatHome) {
|
|
21386
|
-
return join7(caveatHome, "push-fork");
|
|
21387
|
-
}
|
|
21388
|
-
function ensureFork(owner, repo) {
|
|
21389
|
-
runCapture("gh", ["repo", "fork", `${owner}/${repo}`, "--clone=false", "--remote=false"]);
|
|
21390
|
-
}
|
|
21391
|
-
function ensureStagingClone(stagingDir, ghUser, sharedName, upstreamOwner) {
|
|
21392
|
-
if (!existsSync7(stagingDir)) {
|
|
21393
|
-
mkdirSync3(dirname4(stagingDir), { recursive: true });
|
|
21394
|
-
const forkUrl = `https://github.com/${ghUser}/${sharedName}.git`;
|
|
21395
|
-
runOrThrow("git", ["clone", "--depth", "30", forkUrl, stagingDir]);
|
|
21396
|
-
runOrThrow(
|
|
21397
|
-
"git",
|
|
21398
|
-
["remote", "add", "upstream", `https://github.com/${upstreamOwner}/${sharedName}.git`],
|
|
21399
|
-
{ cwd: stagingDir }
|
|
21400
|
-
);
|
|
21401
|
-
} else {
|
|
21402
|
-
runOrThrow("git", ["checkout", "main"], { cwd: stagingDir });
|
|
21403
|
-
runOrThrow("git", ["fetch", "upstream", "main"], { cwd: stagingDir });
|
|
21404
|
-
runOrThrow("git", ["reset", "--hard", "upstream/main"], { cwd: stagingDir });
|
|
21405
|
-
runOrThrow("git", ["push", "origin", "main", "--force-with-lease"], { cwd: stagingDir });
|
|
21406
|
-
}
|
|
21407
|
-
}
|
|
21408
|
-
function upstreamAlreadyHas(stagingDir, relFromRoot) {
|
|
21409
|
-
return existsSync7(join7(stagingDir, relFromRoot));
|
|
21410
|
-
}
|
|
21411
|
-
|
|
21412
|
-
// ../../packages/core/dist/pullShared.js
|
|
21413
|
-
import { existsSync as existsSync8, readdirSync as readdirSync5 } from "node:fs";
|
|
21414
|
-
import { join as join8 } from "node:path";
|
|
21415
|
-
async function pullShared(opts) {
|
|
21416
|
-
const result = { pulled: [], indexed: [] };
|
|
21417
|
-
if (!existsSync8(opts.communityDir)) {
|
|
21418
|
-
return result;
|
|
21419
|
-
}
|
|
21420
|
-
const pulls = await communityPull({
|
|
21421
|
-
communityDir: opts.communityDir,
|
|
21422
|
-
logger: opts.logger
|
|
21423
|
-
});
|
|
21424
|
-
for (const p2 of pulls) {
|
|
21425
|
-
result.pulled.push({
|
|
21426
|
-
handle: p2.handle,
|
|
21427
|
-
status: p2.status,
|
|
21428
|
-
message: p2.message
|
|
21429
|
-
});
|
|
21430
|
-
}
|
|
21431
|
-
rebuildAll(opts.db);
|
|
21432
|
-
if (existsSync8(opts.entriesDir)) {
|
|
21433
|
-
const own = scanSource({ db: opts.db, source: "own", entriesRoot: opts.entriesDir });
|
|
21434
|
-
result.indexed.push({ source: "own", ...own });
|
|
21435
|
-
}
|
|
21436
|
-
for (const entry of readdirSync5(opts.communityDir, { withFileTypes: true })) {
|
|
21437
|
-
if (!entry.isDirectory()) continue;
|
|
21438
|
-
const source = `community/${entry.name}`;
|
|
21439
|
-
const root = join8(opts.communityDir, entry.name, "entries");
|
|
21440
|
-
if (!existsSync8(root)) continue;
|
|
21441
|
-
const scan = scanSource({ db: opts.db, source, entriesRoot: root });
|
|
21442
|
-
result.indexed.push({ source, ...scan });
|
|
21443
|
-
}
|
|
21444
|
-
return result;
|
|
21445
|
-
}
|
|
21446
|
-
|
|
21447
21238
|
// src/context.ts
|
|
21448
21239
|
function buildContext(logger, overrides = {}) {
|
|
21449
21240
|
const userHome = overrides.userHome ?? homedir();
|
|
21450
21241
|
const caveatHome = overrides.caveatHome ?? findCaveatHome(userHome);
|
|
21451
|
-
const userConfigPath =
|
|
21242
|
+
const userConfigPath = join7(userHome, ".caveatrc.json");
|
|
21452
21243
|
const config3 = loadConfig(userConfigPath);
|
|
21453
21244
|
const paths = resolvePaths(caveatHome, config3.knowledgeRepo, userHome);
|
|
21454
21245
|
return { caveatHome, userHome, userConfigPath, config: config3, paths, logger };
|
|
21455
21246
|
}
|
|
21456
21247
|
|
|
21457
21248
|
// src/version.ts
|
|
21458
|
-
import { readFileSync as
|
|
21459
|
-
import { dirname as
|
|
21249
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
21250
|
+
import { dirname as dirname4, join as join8 } from "node:path";
|
|
21460
21251
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
21461
21252
|
function resolveVersion() {
|
|
21462
21253
|
try {
|
|
21463
|
-
const here2 =
|
|
21464
|
-
const pkg = JSON.parse(
|
|
21254
|
+
const here2 = dirname4(fileURLToPath3(import.meta.url));
|
|
21255
|
+
const pkg = JSON.parse(readFileSync5(join8(here2, "..", "package.json"), "utf-8"));
|
|
21465
21256
|
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
21466
21257
|
} catch {
|
|
21467
21258
|
return "0.0.0";
|
|
@@ -21480,13 +21271,13 @@ var stdoutLogger = {
|
|
|
21480
21271
|
};
|
|
21481
21272
|
|
|
21482
21273
|
// src/commands/init.ts
|
|
21483
|
-
import { existsSync as
|
|
21484
|
-
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";
|
|
21485
21276
|
|
|
21486
21277
|
// src/claudeInstall.ts
|
|
21487
|
-
import { spawnSync
|
|
21488
|
-
import { copyFileSync
|
|
21489
|
-
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";
|
|
21490
21281
|
var EVENT_USER_PROMPT_SUBMIT = "UserPromptSubmit";
|
|
21491
21282
|
var EVENT_STOP = "Stop";
|
|
21492
21283
|
function quote(p2) {
|
|
@@ -21520,27 +21311,27 @@ function removeHook(settings, event, command) {
|
|
|
21520
21311
|
return true;
|
|
21521
21312
|
}
|
|
21522
21313
|
function readSettings(path) {
|
|
21523
|
-
if (!
|
|
21524
|
-
return JSON.parse(
|
|
21314
|
+
if (!existsSync7(path)) return {};
|
|
21315
|
+
return JSON.parse(readFileSync6(path, "utf-8"));
|
|
21525
21316
|
}
|
|
21526
21317
|
function writeSettings(path, settings) {
|
|
21527
|
-
const dir =
|
|
21528
|
-
if (!
|
|
21318
|
+
const dir = dirname5(path);
|
|
21319
|
+
if (!existsSync7(dir)) mkdirSync3(dir, { recursive: true });
|
|
21529
21320
|
let backupPath = "";
|
|
21530
|
-
if (
|
|
21321
|
+
if (existsSync7(path)) {
|
|
21531
21322
|
backupPath = `${path}.caveat-backup-${Date.now()}`;
|
|
21532
|
-
|
|
21323
|
+
copyFileSync(path, backupPath);
|
|
21533
21324
|
}
|
|
21534
21325
|
writeFileSync4(path, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
21535
21326
|
return backupPath;
|
|
21536
21327
|
}
|
|
21537
21328
|
var CLAUDE_BIN = "claude";
|
|
21538
|
-
function
|
|
21329
|
+
function shellQuote(s) {
|
|
21539
21330
|
return /[\s&|<>^()]/.test(s) ? `"${s}"` : s;
|
|
21540
21331
|
}
|
|
21541
21332
|
function runClaude(args) {
|
|
21542
|
-
const line = [CLAUDE_BIN, ...args].map(
|
|
21543
|
-
return
|
|
21333
|
+
const line = [CLAUDE_BIN, ...args].map(shellQuote).join(" ");
|
|
21334
|
+
return spawnSync(line, { shell: true, encoding: "utf-8" });
|
|
21544
21335
|
}
|
|
21545
21336
|
function registerMcp(nodePath, cliScriptPath, dryRun, logger) {
|
|
21546
21337
|
const args = mcpArgs(cliScriptPath);
|
|
@@ -21590,7 +21381,7 @@ function unregisterMcp(dryRun, logger) {
|
|
|
21590
21381
|
return { action: "skipped", detail: "not registered or removal failed" };
|
|
21591
21382
|
}
|
|
21592
21383
|
function installClaudeIntegration(opts) {
|
|
21593
|
-
const settingsPath =
|
|
21384
|
+
const settingsPath = join9(opts.claudeDir, "settings.json");
|
|
21594
21385
|
const settings = readSettings(settingsPath);
|
|
21595
21386
|
const usCmd = hookCommand(opts.nodePath, opts.cliScriptPath, "user-prompt-submit");
|
|
21596
21387
|
const stopCmd = hookCommand(opts.nodePath, opts.cliScriptPath, "stop");
|
|
@@ -21609,7 +21400,7 @@ function installClaudeIntegration(opts) {
|
|
|
21609
21400
|
return { mcp, hooks: { userPromptSubmit, stop }, backupPath };
|
|
21610
21401
|
}
|
|
21611
21402
|
function uninstallClaudeIntegration(opts) {
|
|
21612
|
-
const settingsPath =
|
|
21403
|
+
const settingsPath = join9(opts.claudeDir, "settings.json");
|
|
21613
21404
|
const settings = readSettings(settingsPath);
|
|
21614
21405
|
const usCmd = hookCommand(opts.nodePath, opts.cliScriptPath, "user-prompt-submit");
|
|
21615
21406
|
const stopCmd = hookCommand(opts.nodePath, opts.cliScriptPath, "stop");
|
|
@@ -21641,35 +21432,34 @@ var KNOWLEDGE_GITIGNORE = [
|
|
|
21641
21432
|
".obsidian/",
|
|
21642
21433
|
""
|
|
21643
21434
|
].join("\n");
|
|
21644
|
-
async function runInit(ctx, opts = { skipClaude: false,
|
|
21435
|
+
async function runInit(ctx, opts = { skipClaude: false, dryRun: false }) {
|
|
21645
21436
|
ensureUserConfig(ctx.userConfigPath);
|
|
21646
21437
|
ctx.logger.info(`user config: ${ctx.userConfigPath}`);
|
|
21647
|
-
if (!
|
|
21648
|
-
|
|
21649
|
-
|
|
21438
|
+
if (!existsSync8(ctx.paths.knowledgeRepo)) {
|
|
21439
|
+
mkdirSync4(ctx.paths.knowledgeRepo, { recursive: true });
|
|
21440
|
+
mkdirSync4(ctx.paths.entriesDir, { recursive: true });
|
|
21650
21441
|
ctx.logger.info(`knowledge repo scaffolded: ${ctx.paths.knowledgeRepo}`);
|
|
21651
21442
|
} else {
|
|
21652
21443
|
ctx.logger.info(`knowledge repo: ${ctx.paths.knowledgeRepo}`);
|
|
21653
21444
|
}
|
|
21654
21445
|
migrateLegacyCommunityDir(ctx);
|
|
21655
|
-
const gitignorePath =
|
|
21656
|
-
if (!
|
|
21446
|
+
const gitignorePath = join10(ctx.paths.knowledgeRepo, ".gitignore");
|
|
21447
|
+
if (!existsSync8(gitignorePath)) {
|
|
21657
21448
|
writeFileSync5(gitignorePath, KNOWLEDGE_GITIGNORE, "utf-8");
|
|
21658
21449
|
ctx.logger.info(`.gitignore created: ${gitignorePath}`);
|
|
21659
21450
|
}
|
|
21660
|
-
|
|
21661
|
-
|
|
21662
|
-
if (!
|
|
21663
|
-
|
|
21664
|
-
} else if (opts.skipShared) {
|
|
21665
|
-
ctx.logger.info("shared community DB subscription skipped (--skip-shared)");
|
|
21666
|
-
} else if (opts.dryRun) {
|
|
21667
|
-
ctx.logger.info(`[dry-run] would subscribe to shared community DB: ${ctx.config.sharedRepo}`);
|
|
21668
|
-
}
|
|
21669
|
-
} 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 });
|
|
21670
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}`);
|
|
21671
21459
|
}
|
|
21672
|
-
ctx.logger.info(
|
|
21460
|
+
ctx.logger.info(
|
|
21461
|
+
"tip: subscribe to a group repo with `caveat community add <github-url>`, then `caveat pull`."
|
|
21462
|
+
);
|
|
21673
21463
|
if (opts.skipClaude) {
|
|
21674
21464
|
ctx.logger.info("Claude Code integration skipped (--skip-claude)");
|
|
21675
21465
|
return;
|
|
@@ -21680,7 +21470,7 @@ async function runInit(ctx, opts = { skipClaude: false, skipShared: false, dryRu
|
|
|
21680
21470
|
return;
|
|
21681
21471
|
}
|
|
21682
21472
|
const result = installClaudeIntegration({
|
|
21683
|
-
claudeDir:
|
|
21473
|
+
claudeDir: join10(ctx.userHome, ".claude"),
|
|
21684
21474
|
cliScriptPath,
|
|
21685
21475
|
nodePath: process.execPath,
|
|
21686
21476
|
dryRun: opts.dryRun,
|
|
@@ -21689,20 +21479,20 @@ async function runInit(ctx, opts = { skipClaude: false, skipShared: false, dryRu
|
|
|
21689
21479
|
reportInstallResult(ctx, result, opts.dryRun);
|
|
21690
21480
|
}
|
|
21691
21481
|
function migrateLegacyCommunityDir(ctx) {
|
|
21692
|
-
const legacy =
|
|
21482
|
+
const legacy = join10(ctx.paths.knowledgeRepo, "community");
|
|
21693
21483
|
const current = ctx.paths.communityDir;
|
|
21694
21484
|
if (legacy === current) return;
|
|
21695
|
-
if (!
|
|
21696
|
-
if (
|
|
21485
|
+
if (!existsSync8(legacy)) return;
|
|
21486
|
+
if (existsSync8(current)) {
|
|
21697
21487
|
ctx.logger.warn(
|
|
21698
21488
|
`legacy community dir still exists at ${legacy} \u2014 remove manually (new location in use)`
|
|
21699
21489
|
);
|
|
21700
21490
|
return;
|
|
21701
21491
|
}
|
|
21702
|
-
|
|
21703
|
-
for (const entry of
|
|
21492
|
+
mkdirSync4(current, { recursive: true });
|
|
21493
|
+
for (const entry of readdirSync4(legacy, { withFileTypes: true })) {
|
|
21704
21494
|
if (!entry.isDirectory()) continue;
|
|
21705
|
-
renameSync(
|
|
21495
|
+
renameSync(join10(legacy, entry.name), join10(current, entry.name));
|
|
21706
21496
|
}
|
|
21707
21497
|
try {
|
|
21708
21498
|
rmdirSync(legacy);
|
|
@@ -21710,52 +21500,6 @@ function migrateLegacyCommunityDir(ctx) {
|
|
|
21710
21500
|
}
|
|
21711
21501
|
ctx.logger.info(`migrated legacy community/ \u2192 ${current}`);
|
|
21712
21502
|
}
|
|
21713
|
-
async function subscribeSharedRepo(ctx, db) {
|
|
21714
|
-
const url = ctx.config.sharedRepo;
|
|
21715
|
-
const validation = validateCommunityUrl(url);
|
|
21716
|
-
if (!validation.valid) {
|
|
21717
|
-
ctx.logger.warn(
|
|
21718
|
-
`sharedRepo is not a valid GitHub URL \u2014 skipping: ${validation.reason}`
|
|
21719
|
-
);
|
|
21720
|
-
return;
|
|
21721
|
-
}
|
|
21722
|
-
const handle = validation.handle;
|
|
21723
|
-
const target = join12(ctx.paths.communityDir, handle);
|
|
21724
|
-
if (!existsSync10(target)) {
|
|
21725
|
-
if (!existsSync10(ctx.paths.communityDir)) {
|
|
21726
|
-
mkdirSync5(ctx.paths.communityDir, { recursive: true });
|
|
21727
|
-
}
|
|
21728
|
-
try {
|
|
21729
|
-
await communityAdd({
|
|
21730
|
-
url,
|
|
21731
|
-
communityDir: ctx.paths.communityDir,
|
|
21732
|
-
logger: ctx.logger
|
|
21733
|
-
});
|
|
21734
|
-
ctx.logger.info(`shared community DB subscribed: ${url} \u2192 community/${handle}/`);
|
|
21735
|
-
} catch (err) {
|
|
21736
|
-
ctx.logger.warn(
|
|
21737
|
-
`shared community DB subscription failed: ${err instanceof Error ? err.message : String(err)}`
|
|
21738
|
-
);
|
|
21739
|
-
return;
|
|
21740
|
-
}
|
|
21741
|
-
} else {
|
|
21742
|
-
ctx.logger.info(`shared community DB already subscribed: community/${handle}/`);
|
|
21743
|
-
}
|
|
21744
|
-
if (existsSync10(ctx.paths.communityDir)) {
|
|
21745
|
-
rebuildAll(db);
|
|
21746
|
-
if (existsSync10(ctx.paths.entriesDir)) {
|
|
21747
|
-
scanSource({ db, source: "own", entriesRoot: ctx.paths.entriesDir });
|
|
21748
|
-
}
|
|
21749
|
-
for (const entry of readdirSync6(ctx.paths.communityDir, { withFileTypes: true })) {
|
|
21750
|
-
if (!entry.isDirectory()) continue;
|
|
21751
|
-
const source = `community/${entry.name}`;
|
|
21752
|
-
const root = join12(ctx.paths.communityDir, entry.name, "entries");
|
|
21753
|
-
if (!existsSync10(root)) continue;
|
|
21754
|
-
const result = scanSource({ db, source, entriesRoot: root });
|
|
21755
|
-
ctx.logger.info(`indexed ${source}: +${result.added}`);
|
|
21756
|
-
}
|
|
21757
|
-
}
|
|
21758
|
-
}
|
|
21759
21503
|
function runUninstall(ctx, opts) {
|
|
21760
21504
|
const cliScriptPath = process.argv[1];
|
|
21761
21505
|
if (!cliScriptPath) {
|
|
@@ -21763,7 +21507,7 @@ function runUninstall(ctx, opts) {
|
|
|
21763
21507
|
process.exit(1);
|
|
21764
21508
|
}
|
|
21765
21509
|
const result = uninstallClaudeIntegration({
|
|
21766
|
-
claudeDir:
|
|
21510
|
+
claudeDir: join10(ctx.userHome, ".claude"),
|
|
21767
21511
|
cliScriptPath,
|
|
21768
21512
|
nodePath: process.execPath,
|
|
21769
21513
|
dryRun: opts.dryRun,
|
|
@@ -21800,29 +21544,29 @@ function reportInstallResult(ctx, result, dryRun) {
|
|
|
21800
21544
|
}
|
|
21801
21545
|
|
|
21802
21546
|
// src/commands/indexCmd.ts
|
|
21803
|
-
import { existsSync as
|
|
21804
|
-
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";
|
|
21805
21549
|
function runIndex(ctx, opts) {
|
|
21806
21550
|
const dbDir = dirname7(ctx.paths.dbPath);
|
|
21807
|
-
if (!
|
|
21551
|
+
if (!existsSync9(dbDir)) mkdirSync5(dbDir, { recursive: true });
|
|
21808
21552
|
const db = openDb({ path: ctx.paths.dbPath, logger: ctx.logger });
|
|
21809
21553
|
try {
|
|
21810
21554
|
if (opts.full) {
|
|
21811
21555
|
ctx.logger.info("full rebuild: DELETE FROM entries");
|
|
21812
21556
|
rebuildAll(db);
|
|
21813
21557
|
}
|
|
21814
|
-
if (
|
|
21558
|
+
if (existsSync9(ctx.paths.entriesDir)) {
|
|
21815
21559
|
const result = scanSource({ db, source: "own", entriesRoot: ctx.paths.entriesDir });
|
|
21816
21560
|
ctx.logger.info(`own: +${result.added} ~${result.updated} -${result.deleted}`);
|
|
21817
21561
|
} else {
|
|
21818
21562
|
ctx.logger.warn(`entries dir not found: ${ctx.paths.entriesDir}`);
|
|
21819
21563
|
}
|
|
21820
|
-
if (
|
|
21821
|
-
for (const entry of
|
|
21564
|
+
if (existsSync9(ctx.paths.communityDir)) {
|
|
21565
|
+
for (const entry of readdirSync5(ctx.paths.communityDir, { withFileTypes: true })) {
|
|
21822
21566
|
if (!entry.isDirectory()) continue;
|
|
21823
21567
|
const source = `community/${entry.name}`;
|
|
21824
|
-
const root =
|
|
21825
|
-
if (!
|
|
21568
|
+
const root = join11(ctx.paths.communityDir, entry.name, "entries");
|
|
21569
|
+
if (!existsSync9(root)) continue;
|
|
21826
21570
|
const result = scanSource({ db, source, entriesRoot: root });
|
|
21827
21571
|
ctx.logger.info(`${source}: +${result.added} ~${result.updated} -${result.deleted}`);
|
|
21828
21572
|
}
|
|
@@ -22596,11 +22340,11 @@ var serve = (options2, listeningListener) => {
|
|
|
22596
22340
|
|
|
22597
22341
|
// ../web/dist/context.js
|
|
22598
22342
|
import { homedir as homedir2 } from "node:os";
|
|
22599
|
-
import { join as
|
|
22343
|
+
import { join as join12 } from "node:path";
|
|
22600
22344
|
function buildWebContext(overrides = {}) {
|
|
22601
22345
|
const userHome = overrides.userHome ?? homedir2();
|
|
22602
22346
|
const caveatHome = overrides.caveatHome ?? findCaveatHome(userHome);
|
|
22603
|
-
const userConfigPath =
|
|
22347
|
+
const userConfigPath = join12(userHome, ".caveatrc.json");
|
|
22604
22348
|
const logger = overrides.logger ?? stderrLogger;
|
|
22605
22349
|
const config3 = loadConfig(userConfigPath);
|
|
22606
22350
|
const paths = resolvePaths(caveatHome, config3.knowledgeRepo, userHome);
|
|
@@ -24705,7 +24449,7 @@ ul.entries li { padding: 0.5rem 0; border-bottom: 1px solid #eee; }
|
|
|
24705
24449
|
ul.entries a.title { font-weight: 600; color: #0a5fff; text-decoration: none; }
|
|
24706
24450
|
ul.entries a.title:hover { text-decoration: underline; }
|
|
24707
24451
|
ul.entries .meta { font-size: 0.85rem; color: #666; margin-top: 0.2rem; }
|
|
24708
|
-
|
|
24452
|
+
.badge {
|
|
24709
24453
|
display: inline-block;
|
|
24710
24454
|
padding: 0 0.4rem;
|
|
24711
24455
|
margin-right: 0.3rem;
|
|
@@ -24713,10 +24457,12 @@ ul.entries .meta .badge {
|
|
|
24713
24457
|
background: #f0f0f0;
|
|
24714
24458
|
font-size: 0.8rem;
|
|
24715
24459
|
}
|
|
24716
|
-
|
|
24717
|
-
|
|
24718
|
-
|
|
24719
|
-
|
|
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; }
|
|
24720
24466
|
ul.entries .excerpt { margin-top: 0.3rem; color: #444; font-size: 0.92rem; }
|
|
24721
24467
|
article h2 { border-bottom: 1px solid #eee; padding-bottom: 0.2rem; margin-top: 1.6rem; }
|
|
24722
24468
|
article pre {
|
|
@@ -24791,6 +24537,7 @@ function createIndexRoute(ctx) {
|
|
|
24791
24537
|
const q2 = c3.req.query("q")?.trim() ?? "";
|
|
24792
24538
|
const sourceParam = c3.req.query("source");
|
|
24793
24539
|
const tagParam = c3.req.query("tag");
|
|
24540
|
+
const visibilityParam = c3.req.query("visibility");
|
|
24794
24541
|
const filters = {};
|
|
24795
24542
|
if (sourceParam === "own" || sourceParam === "community" || sourceParam === "all") {
|
|
24796
24543
|
filters.source = sourceParam;
|
|
@@ -24798,17 +24545,22 @@ function createIndexRoute(ctx) {
|
|
|
24798
24545
|
if (tagParam) {
|
|
24799
24546
|
filters.tags = [tagParam];
|
|
24800
24547
|
}
|
|
24801
|
-
|
|
24548
|
+
if (visibilityParam === "public" || visibilityParam === "private") {
|
|
24549
|
+
filters.visibility = visibilityParam;
|
|
24550
|
+
}
|
|
24551
|
+
const results = search(ctx.db, { query: q2, filters, limit: 50 });
|
|
24802
24552
|
const rows = results.map((r2) => {
|
|
24803
24553
|
const envEntries = Object.entries(r2.environment ?? {});
|
|
24804
24554
|
const envSummary = envEntries.slice(0, 3).map(([k2, v]) => `${escapeHtml(k2)}:${escapeHtml(String(v))}`).join(" ");
|
|
24805
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>`;
|
|
24806
24557
|
return `
|
|
24807
24558
|
<li>
|
|
24808
24559
|
<a class="title" href="/g/${encodeURIComponent(r2.id)}${hrefSource}">${escapeHtml(r2.title)}</a>
|
|
24809
24560
|
<div class="meta">
|
|
24810
24561
|
<span class="badge ${escapeHtml(r2.confidence ?? "")}">${escapeHtml(r2.confidence ?? "")}</span>
|
|
24811
24562
|
<span class="badge">${escapeHtml(r2.source)}</span>
|
|
24563
|
+
${visibilityBadge}
|
|
24812
24564
|
${envSummary ? `<span>${envSummary}</span>` : ""}
|
|
24813
24565
|
</div>
|
|
24814
24566
|
${r2.symptomExcerpt ? `<div class="excerpt">${escapeHtml(r2.symptomExcerpt)}</div>` : ""}
|
|
@@ -24822,9 +24574,18 @@ function createIndexRoute(ctx) {
|
|
|
24822
24574
|
<option value="own" ${sourceParam === "own" ? "selected" : ""}>own</option>
|
|
24823
24575
|
<option value="community" ${sourceParam === "community" ? "selected" : ""}>community</option>
|
|
24824
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>
|
|
24825
24582
|
<button type="submit">search</button>
|
|
24826
24583
|
</form>
|
|
24827
|
-
${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
|
+
})()}`;
|
|
24828
24589
|
const title = q2 ? `search: ${q2} \xB7 Caveat` : "Caveat";
|
|
24829
24590
|
return c3.html(layout(title, body));
|
|
24830
24591
|
});
|
|
@@ -30290,10 +30051,11 @@ function createDetailRoute(ctx) {
|
|
|
30290
30051
|
}
|
|
30291
30052
|
const fm = entry.frontmatter;
|
|
30292
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>`;
|
|
30293
30055
|
const metaRows = [
|
|
30294
30056
|
`<dt>id</dt><dd><code>${escapeHtml(fm.id)}</code></dd>`,
|
|
30295
30057
|
`<dt>source</dt><dd>${escapeHtml(entry.source)}</dd>`,
|
|
30296
|
-
`<dt>visibility</dt><dd>${
|
|
30058
|
+
`<dt>visibility</dt><dd>${visibilityBadge}</dd>`,
|
|
30297
30059
|
`<dt>confidence</dt><dd><span class="badge ${escapeHtml(fm.confidence)}">${escapeHtml(fm.confidence)}</span></dd>`
|
|
30298
30060
|
];
|
|
30299
30061
|
if (fm.outcome) {
|
|
@@ -30327,14 +30089,14 @@ function createDetailRoute(ctx) {
|
|
|
30327
30089
|
}
|
|
30328
30090
|
|
|
30329
30091
|
// ../web/dist/routes/community.js
|
|
30330
|
-
import { existsSync as
|
|
30331
|
-
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";
|
|
30332
30094
|
function listCommunity(communityDir, db) {
|
|
30333
|
-
if (!
|
|
30095
|
+
if (!existsSync10(communityDir)) return [];
|
|
30334
30096
|
const handles = [];
|
|
30335
|
-
for (const entry of
|
|
30097
|
+
for (const entry of readdirSync6(communityDir, { withFileTypes: true })) {
|
|
30336
30098
|
if (!entry.isDirectory()) continue;
|
|
30337
|
-
const handlePath =
|
|
30099
|
+
const handlePath = join13(communityDir, entry.name);
|
|
30338
30100
|
const countRow = db.prepare("SELECT COUNT(*) AS n FROM entries WHERE source = ?").get(`community/${entry.name}`);
|
|
30339
30101
|
handles.push({
|
|
30340
30102
|
handle: entry.name,
|
|
@@ -30351,7 +30113,7 @@ function createCommunityRoute(ctx) {
|
|
|
30351
30113
|
const list2 = handles.map((h2) => {
|
|
30352
30114
|
let mtime = "";
|
|
30353
30115
|
try {
|
|
30354
|
-
mtime =
|
|
30116
|
+
mtime = statSync5(h2.path).mtime.toISOString().slice(0, 10);
|
|
30355
30117
|
} catch {
|
|
30356
30118
|
mtime = "";
|
|
30357
30119
|
}
|
|
@@ -44632,11 +44394,11 @@ var StdioServerTransport = class {
|
|
|
44632
44394
|
|
|
44633
44395
|
// ../mcp/dist/context.js
|
|
44634
44396
|
import { homedir as homedir3 } from "node:os";
|
|
44635
|
-
import { join as
|
|
44397
|
+
import { join as join14 } from "node:path";
|
|
44636
44398
|
function buildMcpContext(overrides = {}) {
|
|
44637
44399
|
const userHome = overrides.userHome ?? homedir3();
|
|
44638
44400
|
const caveatHome = overrides.caveatHome ?? findCaveatHome(userHome);
|
|
44639
|
-
const userConfigPath =
|
|
44401
|
+
const userConfigPath = join14(userHome, ".caveatrc.json");
|
|
44640
44402
|
const logger = overrides.logger ?? stderrLogger;
|
|
44641
44403
|
const config3 = loadConfig(userConfigPath);
|
|
44642
44404
|
const paths = resolvePaths(caveatHome, config3.knowledgeRepo, userHome);
|
|
@@ -44745,45 +44507,37 @@ function handleListRecent(ctx, args) {
|
|
|
44745
44507
|
}
|
|
44746
44508
|
|
|
44747
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";
|
|
44748
44512
|
var pullInputShape = {};
|
|
44749
44513
|
async function handlePull(ctx, _args = {}) {
|
|
44750
|
-
const
|
|
44751
|
-
|
|
44752
|
-
|
|
44753
|
-
|
|
44754
|
-
|
|
44755
|
-
|
|
44756
|
-
|
|
44757
|
-
|
|
44758
|
-
|
|
44759
|
-
|
|
44760
|
-
|
|
44761
|
-
|
|
44762
|
-
|
|
44763
|
-
})
|
|
44764
|
-
|
|
44765
|
-
}
|
|
44766
|
-
|
|
44767
|
-
|
|
44768
|
-
|
|
44769
|
-
|
|
44770
|
-
|
|
44771
|
-
|
|
44772
|
-
|
|
44773
|
-
|
|
44774
|
-
|
|
44775
|
-
|
|
44776
|
-
|
|
44777
|
-
id: args.id,
|
|
44778
|
-
dryRun: args.dry_run ?? false,
|
|
44779
|
-
logger: ctx.logger
|
|
44780
|
-
});
|
|
44781
|
-
return {
|
|
44782
|
-
status: result.status,
|
|
44783
|
-
detail: result.detail,
|
|
44784
|
-
pr_url: result.prUrl,
|
|
44785
|
-
planned_steps: result.plannedSteps
|
|
44786
|
-
};
|
|
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 };
|
|
44787
44541
|
}
|
|
44788
44542
|
|
|
44789
44543
|
// ../mcp/dist/registerTools.js
|
|
@@ -44847,20 +44601,11 @@ function registerAllTools(server, ctx) {
|
|
|
44847
44601
|
"caveat_pull",
|
|
44848
44602
|
{
|
|
44849
44603
|
title: "caveat_pull",
|
|
44850
|
-
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.",
|
|
44851
44605
|
inputSchema: pullInputShape
|
|
44852
44606
|
},
|
|
44853
44607
|
async (args) => jsonResult(await handlePull(ctx, args))
|
|
44854
44608
|
);
|
|
44855
|
-
server.registerTool(
|
|
44856
|
-
"caveat_push",
|
|
44857
|
-
{
|
|
44858
|
-
title: "caveat_push",
|
|
44859
|
-
description: "Contribute a user-owned caveat to the public shared DB via fork + PR on GitHub. This is a PUBLIC, externally-visible action (the PR appears on GitHub and is hard to fully retract). Confirm with the user before calling without dry_run \u2014 or call with dry_run=true first to show the plan. Only push entries that document a trap genuinely reusable by others (not project-specific ties, not duplicated by existing community entries). Entries with `visibility: private` are rejected (`status=visibility-private`) \u2014 the user has declared them local-only. Requires `gh` CLI authenticated; returns `status=gh-missing` / `gh-unauthed` on failure.",
|
|
44860
|
-
inputSchema: pushInputShape
|
|
44861
|
-
},
|
|
44862
|
-
async (args) => jsonResult(await handlePush(ctx, args))
|
|
44863
|
-
);
|
|
44864
44609
|
}
|
|
44865
44610
|
|
|
44866
44611
|
// ../mcp/dist/server.js
|
|
@@ -44953,77 +44698,46 @@ async function runHook(name) {
|
|
|
44953
44698
|
}
|
|
44954
44699
|
|
|
44955
44700
|
// src/commands/pull.ts
|
|
44956
|
-
import { existsSync as
|
|
44701
|
+
import { existsSync as existsSync12, readdirSync as readdirSync8 } from "node:fs";
|
|
44702
|
+
import { join as join16 } from "node:path";
|
|
44957
44703
|
async function runPull(ctx) {
|
|
44958
|
-
if (!
|
|
44704
|
+
if (!existsSync12(ctx.paths.communityDir)) {
|
|
44959
44705
|
ctx.logger.info(
|
|
44960
|
-
"no community repos yet \u2014
|
|
44706
|
+
"no community repos yet \u2014 add one with `caveat community add <github-url>`."
|
|
44961
44707
|
);
|
|
44962
44708
|
return;
|
|
44963
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
|
+
}
|
|
44964
44721
|
const db = openDb({ path: ctx.paths.dbPath, logger: ctx.logger });
|
|
44965
44722
|
try {
|
|
44966
|
-
|
|
44967
|
-
|
|
44968
|
-
|
|
44969
|
-
|
|
44970
|
-
logger: ctx.logger
|
|
44971
|
-
});
|
|
44972
|
-
for (const p2 of result.pulled) {
|
|
44973
|
-
if (p2.status === "ok") {
|
|
44974
|
-
ctx.logger.info(`${p2.handle}: pulled`);
|
|
44975
|
-
} else {
|
|
44976
|
-
ctx.logger.warn(`${p2.handle}: FAILED \u2014 ${p2.message ?? "unknown"}`);
|
|
44977
|
-
}
|
|
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}`);
|
|
44978
44727
|
}
|
|
44979
|
-
for (const
|
|
44980
|
-
|
|
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}`);
|
|
44981
44735
|
}
|
|
44982
44736
|
} finally {
|
|
44983
44737
|
db.close();
|
|
44984
44738
|
}
|
|
44985
44739
|
}
|
|
44986
44740
|
|
|
44987
|
-
// src/commands/push.ts
|
|
44988
|
-
async function runPush(ctx, opts) {
|
|
44989
|
-
const result = await pushEntry({
|
|
44990
|
-
entriesDir: ctx.paths.entriesDir,
|
|
44991
|
-
caveatHome: ctx.caveatHome,
|
|
44992
|
-
sharedRepoUrl: ctx.config.sharedRepo,
|
|
44993
|
-
id: opts.id,
|
|
44994
|
-
dryRun: opts.dryRun,
|
|
44995
|
-
logger: ctx.logger
|
|
44996
|
-
});
|
|
44997
|
-
switch (result.status) {
|
|
44998
|
-
case "ok":
|
|
44999
|
-
ctx.logger.info(`PR opened: ${result.prUrl ?? ""}`);
|
|
45000
|
-
ctx.logger.info(
|
|
45001
|
-
"Once merged, other subscribers will see your entry after their next `caveat pull`."
|
|
45002
|
-
);
|
|
45003
|
-
break;
|
|
45004
|
-
case "dry-run":
|
|
45005
|
-
for (const [i2, step] of (result.plannedSteps ?? []).entries()) {
|
|
45006
|
-
ctx.logger.info(`[dry-run] ${i2 + 1}. ${step}`);
|
|
45007
|
-
}
|
|
45008
|
-
break;
|
|
45009
|
-
case "not-found":
|
|
45010
|
-
ctx.logger.error(
|
|
45011
|
-
`${result.detail}. List entries with \`caveat list\` or write + index first.`
|
|
45012
|
-
);
|
|
45013
|
-
process.exitCode = 1;
|
|
45014
|
-
break;
|
|
45015
|
-
case "gh-missing":
|
|
45016
|
-
case "gh-unauthed":
|
|
45017
|
-
ctx.logger.error(result.detail ?? "gh CLI issue");
|
|
45018
|
-
process.exitCode = 1;
|
|
45019
|
-
break;
|
|
45020
|
-
case "failed":
|
|
45021
|
-
ctx.logger.error(result.detail ?? "unknown failure");
|
|
45022
|
-
process.exitCode = 1;
|
|
45023
|
-
break;
|
|
45024
|
-
}
|
|
45025
|
-
}
|
|
45026
|
-
|
|
45027
44741
|
// src/commands/community.ts
|
|
45028
44742
|
async function runCommunityAdd(ctx, url) {
|
|
45029
44743
|
try {
|
|
@@ -45073,17 +44787,46 @@ function runCommunityList(ctx) {
|
|
|
45073
44787
|
db.close();
|
|
45074
44788
|
}
|
|
45075
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
|
+
}
|
|
45076
44820
|
|
|
45077
44821
|
// src/index.ts
|
|
45078
44822
|
var program = new Command();
|
|
45079
44823
|
program.name("caveat").description("External spec gotcha knowledge base CLI").version(CAVEAT_VERSION);
|
|
45080
44824
|
program.command("init").description(
|
|
45081
|
-
"Initialize ~/.caveatrc.json, ~/.caveat/,
|
|
45082
|
-
).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) => {
|
|
45083
44827
|
const ctx = buildContext(stdoutLogger);
|
|
45084
44828
|
await runInit(ctx, {
|
|
45085
44829
|
skipClaude: opts.skipClaude,
|
|
45086
|
-
skipShared: opts.skipShared,
|
|
45087
44830
|
dryRun: opts.dryRun
|
|
45088
44831
|
});
|
|
45089
44832
|
});
|
|
@@ -45119,15 +44862,11 @@ program.command("stats").description("Show aggregate stats").action(() => {
|
|
|
45119
44862
|
runStats(ctx);
|
|
45120
44863
|
});
|
|
45121
44864
|
program.command("pull").description(
|
|
45122
|
-
"
|
|
44865
|
+
"git-pull every subscribed community repo and re-index. Use this to receive updates from group/teammate repos."
|
|
45123
44866
|
).action(async () => {
|
|
45124
44867
|
const ctx = buildContext(stdoutLogger);
|
|
45125
44868
|
await runPull(ctx);
|
|
45126
44869
|
});
|
|
45127
|
-
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) => {
|
|
45128
|
-
const ctx = buildContext(stdoutLogger);
|
|
45129
|
-
await runPush(ctx, { id, dryRun: opts.dryRun });
|
|
45130
|
-
});
|
|
45131
44870
|
program.command("serve").description("Start the read-only web share portal").option("--port <n>", "port number", (v) => Number(v), 4242).action((opts) => {
|
|
45132
44871
|
runServe({ port: opts.port });
|
|
45133
44872
|
});
|
|
@@ -45150,6 +44889,10 @@ community.command("list").description("List imported community repos with entry
|
|
|
45150
44889
|
const ctx = buildContext(stdoutLogger);
|
|
45151
44890
|
runCommunityList(ctx);
|
|
45152
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
|
+
});
|
|
45153
44896
|
program.parseAsync(process.argv).catch((err) => {
|
|
45154
44897
|
const msg = err instanceof Error ? err.message : String(err);
|
|
45155
44898
|
process.stderr.write(`[caveat:error] ${msg}
|