caveat-cli 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +509 -74
- 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, relative3, options2, skipNormalization) {
|
|
9765
9765
|
const target = {};
|
|
9766
9766
|
if (!skipNormalization) {
|
|
9767
9767
|
base2 = parse4(serialize(base2, options2), options2);
|
|
9768
|
-
|
|
9768
|
+
relative3 = parse4(serialize(relative3, 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 && relative3.scheme) {
|
|
9772
|
+
target.scheme = relative3.scheme;
|
|
9773
|
+
target.userinfo = relative3.userinfo;
|
|
9774
|
+
target.host = relative3.host;
|
|
9775
|
+
target.port = relative3.port;
|
|
9776
|
+
target.path = removeDotSegments(relative3.path || "");
|
|
9777
|
+
target.query = relative3.query;
|
|
9778
9778
|
} else {
|
|
9779
|
-
if (
|
|
9780
|
-
target.userinfo =
|
|
9781
|
-
target.host =
|
|
9782
|
-
target.port =
|
|
9783
|
-
target.path = removeDotSegments(
|
|
9784
|
-
target.query =
|
|
9779
|
+
if (relative3.userinfo !== void 0 || relative3.host !== void 0 || relative3.port !== void 0) {
|
|
9780
|
+
target.userinfo = relative3.userinfo;
|
|
9781
|
+
target.host = relative3.host;
|
|
9782
|
+
target.port = relative3.port;
|
|
9783
|
+
target.path = removeDotSegments(relative3.path || "");
|
|
9784
|
+
target.query = relative3.query;
|
|
9785
9785
|
} else {
|
|
9786
|
-
if (!
|
|
9786
|
+
if (!relative3.path) {
|
|
9787
9787
|
target.path = base2.path;
|
|
9788
|
-
if (
|
|
9789
|
-
target.query =
|
|
9788
|
+
if (relative3.query !== void 0) {
|
|
9789
|
+
target.query = relative3.query;
|
|
9790
9790
|
} else {
|
|
9791
9791
|
target.query = base2.query;
|
|
9792
9792
|
}
|
|
9793
9793
|
} else {
|
|
9794
|
-
if (
|
|
9795
|
-
target.path = removeDotSegments(
|
|
9794
|
+
if (relative3.path[0] === "/") {
|
|
9795
|
+
target.path = removeDotSegments(relative3.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 = "/" + relative3.path;
|
|
9799
9799
|
} else if (!base2.path) {
|
|
9800
|
-
target.path =
|
|
9800
|
+
target.path = relative3.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) + relative3.path;
|
|
9803
9803
|
}
|
|
9804
9804
|
target.path = removeDotSegments(target.path);
|
|
9805
9805
|
}
|
|
9806
|
-
target.query =
|
|
9806
|
+
target.query = relative3.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 = relative3.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 join9 } from "node:path";
|
|
12980
12980
|
|
|
12981
12981
|
// ../../packages/core/dist/db.js
|
|
12982
12982
|
import { DatabaseSync } from "node:sqlite";
|
|
@@ -16139,10 +16139,12 @@ function resolvePaths(caveatHome, knowledgeRepo, userHome) {
|
|
|
16139
16139
|
|
|
16140
16140
|
// ../../packages/core/dist/config.js
|
|
16141
16141
|
import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
16142
|
+
var SHARED_REPO_URL = "https://github.com/kitepon-rgb/caveats-quo";
|
|
16142
16143
|
var DEFAULT_CONFIG = {
|
|
16143
16144
|
knowledgeRepo: "own",
|
|
16144
16145
|
semverKeys: ["driver", "cuda", "node"],
|
|
16145
|
-
communitySources: []
|
|
16146
|
+
communitySources: [],
|
|
16147
|
+
sharedRepo: SHARED_REPO_URL
|
|
16146
16148
|
};
|
|
16147
16149
|
function loadConfig(userConfigPath) {
|
|
16148
16150
|
const userCfg = existsSync5(userConfigPath) ? JSON.parse(readFileSync4(userConfigPath, "utf-8")) : {};
|
|
@@ -21226,15 +21228,246 @@ function stopReminderText() {
|
|
|
21226
21228
|
return [
|
|
21227
21229
|
"[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",
|
|
21228
21230
|
"\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",
|
|
21229
|
-
"\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"
|
|
21231
|
+
"\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",
|
|
21232
|
+
"\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"
|
|
21230
21233
|
].join("\n");
|
|
21231
21234
|
}
|
|
21232
21235
|
|
|
21236
|
+
// ../../packages/core/dist/push.js
|
|
21237
|
+
import { spawnSync } from "node:child_process";
|
|
21238
|
+
import {
|
|
21239
|
+
copyFileSync,
|
|
21240
|
+
existsSync as existsSync7,
|
|
21241
|
+
mkdirSync as mkdirSync3,
|
|
21242
|
+
readdirSync as readdirSync4,
|
|
21243
|
+
readFileSync as readFileSync5
|
|
21244
|
+
} from "node:fs";
|
|
21245
|
+
import { dirname as dirname4, join as join7, relative as relative2 } from "node:path";
|
|
21246
|
+
async function pushEntry(opts) {
|
|
21247
|
+
if (!checkGhAvailable()) {
|
|
21248
|
+
return {
|
|
21249
|
+
status: "gh-missing",
|
|
21250
|
+
detail: "gh (GitHub CLI) is required. Install: https://cli.github.com/"
|
|
21251
|
+
};
|
|
21252
|
+
}
|
|
21253
|
+
if (!checkGhAuthed()) {
|
|
21254
|
+
return {
|
|
21255
|
+
status: "gh-unauthed",
|
|
21256
|
+
detail: "gh is not authenticated. Run `gh auth login`."
|
|
21257
|
+
};
|
|
21258
|
+
}
|
|
21259
|
+
const validation = validateCommunityUrl(opts.sharedRepoUrl);
|
|
21260
|
+
if (!validation.valid) {
|
|
21261
|
+
return { status: "failed", detail: `sharedRepo URL invalid: ${validation.reason}` };
|
|
21262
|
+
}
|
|
21263
|
+
const [sharedOwner, sharedName] = parseOwnerRepo(opts.sharedRepoUrl);
|
|
21264
|
+
if (!sharedOwner || !sharedName) {
|
|
21265
|
+
return { status: "failed", detail: `cannot parse owner/repo from ${opts.sharedRepoUrl}` };
|
|
21266
|
+
}
|
|
21267
|
+
const owned = findOwnedEntry(opts.entriesDir, opts.id);
|
|
21268
|
+
if (!owned) {
|
|
21269
|
+
return {
|
|
21270
|
+
status: "not-found",
|
|
21271
|
+
detail: `entry id=${opts.id} not found under ${opts.entriesDir}`
|
|
21272
|
+
};
|
|
21273
|
+
}
|
|
21274
|
+
const ghUser = resolveGhUser();
|
|
21275
|
+
if (!ghUser) {
|
|
21276
|
+
return { status: "failed", detail: "gh api user failed" };
|
|
21277
|
+
}
|
|
21278
|
+
if (opts.dryRun) {
|
|
21279
|
+
return {
|
|
21280
|
+
status: "dry-run",
|
|
21281
|
+
plannedSteps: [
|
|
21282
|
+
`fork ${sharedOwner}/${sharedName} as ${ghUser}/${sharedName}`,
|
|
21283
|
+
`clone fork \u2192 ${forkStagingDir(opts.caveatHome)}`,
|
|
21284
|
+
`copy entry \u2192 entries/${owned.relPath}`,
|
|
21285
|
+
"commit, push branch, open PR against main"
|
|
21286
|
+
]
|
|
21287
|
+
};
|
|
21288
|
+
}
|
|
21289
|
+
try {
|
|
21290
|
+
ensureFork(sharedOwner, sharedName);
|
|
21291
|
+
const stagingDir = forkStagingDir(opts.caveatHome);
|
|
21292
|
+
ensureStagingClone(stagingDir, ghUser, sharedName, sharedOwner);
|
|
21293
|
+
const branch = `caveat-push-${owned.id}-${Date.now().toString(36)}`;
|
|
21294
|
+
run("git", ["checkout", "-b", branch], { cwd: stagingDir });
|
|
21295
|
+
const destPath = join7(stagingDir, "entries", owned.relPath);
|
|
21296
|
+
mkdirSync3(dirname4(destPath), { recursive: true });
|
|
21297
|
+
copyFileSync(owned.absPath, destPath);
|
|
21298
|
+
run("git", ["add", join7("entries", owned.relPath)], { cwd: stagingDir });
|
|
21299
|
+
const isUpdate = upstreamAlreadyHas(stagingDir, "entries/" + owned.relPath);
|
|
21300
|
+
const verb = isUpdate ? "update" : "add";
|
|
21301
|
+
const commitMsg = `${verb}: ${owned.title}`;
|
|
21302
|
+
run("git", ["commit", "-m", commitMsg], { cwd: stagingDir });
|
|
21303
|
+
run("git", ["push", "--set-upstream", "origin", branch], { cwd: stagingDir });
|
|
21304
|
+
const prTitle = `${verb}: ${owned.title}`;
|
|
21305
|
+
const prBody = [
|
|
21306
|
+
`Contribution of caveat \`${owned.id}\` via \`caveat push\`.`,
|
|
21307
|
+
"",
|
|
21308
|
+
`- entry path: \`entries/${owned.relPath}\``,
|
|
21309
|
+
`- action: ${verb}`,
|
|
21310
|
+
"",
|
|
21311
|
+
"Merged PRs will appear in subscribers' repos on their next `caveat pull`."
|
|
21312
|
+
].join("\n");
|
|
21313
|
+
const pr = spawnSync(
|
|
21314
|
+
"gh",
|
|
21315
|
+
[
|
|
21316
|
+
"pr",
|
|
21317
|
+
"create",
|
|
21318
|
+
"--repo",
|
|
21319
|
+
`${sharedOwner}/${sharedName}`,
|
|
21320
|
+
"--base",
|
|
21321
|
+
"main",
|
|
21322
|
+
"--head",
|
|
21323
|
+
`${ghUser}:${branch}`,
|
|
21324
|
+
"--title",
|
|
21325
|
+
prTitle,
|
|
21326
|
+
"--body",
|
|
21327
|
+
prBody
|
|
21328
|
+
],
|
|
21329
|
+
{ cwd: stagingDir, encoding: "utf-8", shell: true }
|
|
21330
|
+
);
|
|
21331
|
+
if (pr.status !== 0) {
|
|
21332
|
+
return {
|
|
21333
|
+
status: "failed",
|
|
21334
|
+
detail: (typeof pr.stderr === "string" ? pr.stderr : String(pr.stderr ?? "")).trim() || "gh pr create failed"
|
|
21335
|
+
};
|
|
21336
|
+
}
|
|
21337
|
+
const prUrl = (typeof pr.stdout === "string" ? pr.stdout : String(pr.stdout ?? "")).trim();
|
|
21338
|
+
return { status: "ok", prUrl };
|
|
21339
|
+
} catch (err) {
|
|
21340
|
+
return {
|
|
21341
|
+
status: "failed",
|
|
21342
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
21343
|
+
};
|
|
21344
|
+
}
|
|
21345
|
+
}
|
|
21346
|
+
function checkGhAvailable() {
|
|
21347
|
+
const r2 = spawnSync("gh", ["--version"], { encoding: "utf-8", shell: true });
|
|
21348
|
+
return r2.status === 0;
|
|
21349
|
+
}
|
|
21350
|
+
function checkGhAuthed() {
|
|
21351
|
+
const r2 = spawnSync("gh", ["auth", "status"], { encoding: "utf-8", shell: true });
|
|
21352
|
+
return r2.status === 0;
|
|
21353
|
+
}
|
|
21354
|
+
function resolveGhUser() {
|
|
21355
|
+
const r2 = spawnSync("gh", ["api", "user", "--jq", ".login"], {
|
|
21356
|
+
encoding: "utf-8",
|
|
21357
|
+
shell: true
|
|
21358
|
+
});
|
|
21359
|
+
if (r2.status !== 0) return void 0;
|
|
21360
|
+
return typeof r2.stdout === "string" ? r2.stdout.trim() : "";
|
|
21361
|
+
}
|
|
21362
|
+
function parseOwnerRepo(url) {
|
|
21363
|
+
const m = /^https:\/\/github\.com\/([^/]+)\/([^/]+?)(\.git)?\/?$/.exec(url);
|
|
21364
|
+
if (!m) return [void 0, void 0];
|
|
21365
|
+
return [m[1], m[2]];
|
|
21366
|
+
}
|
|
21367
|
+
function findOwnedEntry(entriesDir, id) {
|
|
21368
|
+
if (!existsSync7(entriesDir)) return void 0;
|
|
21369
|
+
for (const categoryDirent of readdirSync4(entriesDir, { withFileTypes: true })) {
|
|
21370
|
+
if (!categoryDirent.isDirectory()) continue;
|
|
21371
|
+
const categoryDir = join7(entriesDir, categoryDirent.name);
|
|
21372
|
+
for (const fileDirent of readdirSync4(categoryDir, { withFileTypes: true })) {
|
|
21373
|
+
if (!fileDirent.isFile() || !fileDirent.name.endsWith(".md")) continue;
|
|
21374
|
+
const absPath = join7(categoryDir, fileDirent.name);
|
|
21375
|
+
const raw2 = readFileSync5(absPath, "utf-8");
|
|
21376
|
+
try {
|
|
21377
|
+
const parsed = parseMarkdown(raw2);
|
|
21378
|
+
if (parsed.frontmatter.id === id) {
|
|
21379
|
+
const relPath = relative2(entriesDir, absPath).replace(/\\/g, "/");
|
|
21380
|
+
return { id, title: parsed.frontmatter.title, absPath, relPath };
|
|
21381
|
+
}
|
|
21382
|
+
} catch {
|
|
21383
|
+
}
|
|
21384
|
+
}
|
|
21385
|
+
}
|
|
21386
|
+
return void 0;
|
|
21387
|
+
}
|
|
21388
|
+
function forkStagingDir(caveatHome) {
|
|
21389
|
+
return join7(caveatHome, "push-fork");
|
|
21390
|
+
}
|
|
21391
|
+
function ensureFork(owner, repo) {
|
|
21392
|
+
spawnSync(
|
|
21393
|
+
"gh",
|
|
21394
|
+
["repo", "fork", `${owner}/${repo}`, "--clone=false", "--remote=false"],
|
|
21395
|
+
{ encoding: "utf-8", shell: true }
|
|
21396
|
+
);
|
|
21397
|
+
}
|
|
21398
|
+
function ensureStagingClone(stagingDir, ghUser, sharedName, upstreamOwner) {
|
|
21399
|
+
if (!existsSync7(stagingDir)) {
|
|
21400
|
+
mkdirSync3(dirname4(stagingDir), { recursive: true });
|
|
21401
|
+
const forkUrl = `https://github.com/${ghUser}/${sharedName}.git`;
|
|
21402
|
+
run("git", ["clone", "--depth", "30", forkUrl, stagingDir]);
|
|
21403
|
+
run(
|
|
21404
|
+
"git",
|
|
21405
|
+
["remote", "add", "upstream", `https://github.com/${upstreamOwner}/${sharedName}.git`],
|
|
21406
|
+
{ cwd: stagingDir }
|
|
21407
|
+
);
|
|
21408
|
+
} else {
|
|
21409
|
+
run("git", ["checkout", "main"], { cwd: stagingDir });
|
|
21410
|
+
run("git", ["fetch", "upstream", "main"], { cwd: stagingDir });
|
|
21411
|
+
run("git", ["reset", "--hard", "upstream/main"], { cwd: stagingDir });
|
|
21412
|
+
run("git", ["push", "origin", "main", "--force-with-lease"], { cwd: stagingDir });
|
|
21413
|
+
}
|
|
21414
|
+
}
|
|
21415
|
+
function upstreamAlreadyHas(stagingDir, relFromRoot) {
|
|
21416
|
+
return existsSync7(join7(stagingDir, relFromRoot));
|
|
21417
|
+
}
|
|
21418
|
+
function run(command, args, opts = {}) {
|
|
21419
|
+
const r2 = spawnSync(command, args, {
|
|
21420
|
+
encoding: "utf-8",
|
|
21421
|
+
cwd: opts.cwd,
|
|
21422
|
+
shell: true
|
|
21423
|
+
});
|
|
21424
|
+
if (r2.status !== 0) {
|
|
21425
|
+
const stderr = typeof r2.stderr === "string" ? r2.stderr : String(r2.stderr ?? "");
|
|
21426
|
+
const stdout = typeof r2.stdout === "string" ? r2.stdout : String(r2.stdout ?? "");
|
|
21427
|
+
throw new Error(`${command} ${args.join(" ")} failed: ${(stderr || stdout || "unknown").trim()}`);
|
|
21428
|
+
}
|
|
21429
|
+
}
|
|
21430
|
+
|
|
21431
|
+
// ../../packages/core/dist/pullShared.js
|
|
21432
|
+
import { existsSync as existsSync8, readdirSync as readdirSync5 } from "node:fs";
|
|
21433
|
+
import { join as join8 } from "node:path";
|
|
21434
|
+
async function pullShared(opts) {
|
|
21435
|
+
const result = { pulled: [], indexed: [] };
|
|
21436
|
+
if (!existsSync8(opts.communityDir)) {
|
|
21437
|
+
return result;
|
|
21438
|
+
}
|
|
21439
|
+
const pulls = await communityPull({
|
|
21440
|
+
communityDir: opts.communityDir,
|
|
21441
|
+
logger: opts.logger
|
|
21442
|
+
});
|
|
21443
|
+
for (const p2 of pulls) {
|
|
21444
|
+
result.pulled.push({
|
|
21445
|
+
handle: p2.handle,
|
|
21446
|
+
status: p2.status,
|
|
21447
|
+
message: p2.message
|
|
21448
|
+
});
|
|
21449
|
+
}
|
|
21450
|
+
rebuildAll(opts.db);
|
|
21451
|
+
if (existsSync8(opts.entriesDir)) {
|
|
21452
|
+
const own = scanSource({ db: opts.db, source: "own", entriesRoot: opts.entriesDir });
|
|
21453
|
+
result.indexed.push({ source: "own", ...own });
|
|
21454
|
+
}
|
|
21455
|
+
for (const entry of readdirSync5(opts.communityDir, { withFileTypes: true })) {
|
|
21456
|
+
if (!entry.isDirectory()) continue;
|
|
21457
|
+
const source = `community/${entry.name}`;
|
|
21458
|
+
const root = join8(opts.communityDir, entry.name, "entries");
|
|
21459
|
+
if (!existsSync8(root)) continue;
|
|
21460
|
+
const scan = scanSource({ db: opts.db, source, entriesRoot: root });
|
|
21461
|
+
result.indexed.push({ source, ...scan });
|
|
21462
|
+
}
|
|
21463
|
+
return result;
|
|
21464
|
+
}
|
|
21465
|
+
|
|
21233
21466
|
// src/context.ts
|
|
21234
21467
|
function buildContext(logger, overrides = {}) {
|
|
21235
21468
|
const userHome = overrides.userHome ?? homedir();
|
|
21236
21469
|
const caveatHome = overrides.caveatHome ?? findCaveatHome(userHome);
|
|
21237
|
-
const userConfigPath =
|
|
21470
|
+
const userConfigPath = join9(userHome, ".caveatrc.json");
|
|
21238
21471
|
const config3 = loadConfig(userConfigPath);
|
|
21239
21472
|
const paths = resolvePaths(caveatHome, config3.knowledgeRepo, userHome);
|
|
21240
21473
|
return { caveatHome, userHome, userConfigPath, config: config3, paths, logger };
|
|
@@ -21251,13 +21484,13 @@ var stdoutLogger = {
|
|
|
21251
21484
|
};
|
|
21252
21485
|
|
|
21253
21486
|
// src/commands/init.ts
|
|
21254
|
-
import { existsSync as
|
|
21255
|
-
import { join as
|
|
21487
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync5, readdirSync as readdirSync6, writeFileSync as writeFileSync5 } from "node:fs";
|
|
21488
|
+
import { join as join11 } from "node:path";
|
|
21256
21489
|
|
|
21257
21490
|
// src/claudeInstall.ts
|
|
21258
|
-
import { spawnSync } from "node:child_process";
|
|
21259
|
-
import { copyFileSync, existsSync as
|
|
21260
|
-
import { dirname as
|
|
21491
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
21492
|
+
import { copyFileSync as copyFileSync2, existsSync as existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
21493
|
+
import { dirname as dirname5, join as join10 } from "node:path";
|
|
21261
21494
|
var EVENT_USER_PROMPT_SUBMIT = "UserPromptSubmit";
|
|
21262
21495
|
var EVENT_STOP = "Stop";
|
|
21263
21496
|
function quote(p2) {
|
|
@@ -21291,16 +21524,16 @@ function removeHook(settings, event, command) {
|
|
|
21291
21524
|
return true;
|
|
21292
21525
|
}
|
|
21293
21526
|
function readSettings(path) {
|
|
21294
|
-
if (!
|
|
21295
|
-
return JSON.parse(
|
|
21527
|
+
if (!existsSync9(path)) return {};
|
|
21528
|
+
return JSON.parse(readFileSync6(path, "utf-8"));
|
|
21296
21529
|
}
|
|
21297
21530
|
function writeSettings(path, settings) {
|
|
21298
|
-
const dir =
|
|
21299
|
-
if (!
|
|
21531
|
+
const dir = dirname5(path);
|
|
21532
|
+
if (!existsSync9(dir)) mkdirSync4(dir, { recursive: true });
|
|
21300
21533
|
let backupPath = "";
|
|
21301
|
-
if (
|
|
21534
|
+
if (existsSync9(path)) {
|
|
21302
21535
|
backupPath = `${path}.caveat-backup-${Date.now()}`;
|
|
21303
|
-
|
|
21536
|
+
copyFileSync2(path, backupPath);
|
|
21304
21537
|
}
|
|
21305
21538
|
writeFileSync4(path, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
21306
21539
|
return backupPath;
|
|
@@ -21311,7 +21544,7 @@ function shellQuote(s) {
|
|
|
21311
21544
|
}
|
|
21312
21545
|
function runClaude(args) {
|
|
21313
21546
|
const line = [CLAUDE_BIN, ...args].map(shellQuote).join(" ");
|
|
21314
|
-
return
|
|
21547
|
+
return spawnSync2(line, { shell: true, encoding: "utf-8" });
|
|
21315
21548
|
}
|
|
21316
21549
|
function registerMcp(nodePath, cliScriptPath, dryRun, logger) {
|
|
21317
21550
|
const args = mcpArgs(cliScriptPath);
|
|
@@ -21361,7 +21594,7 @@ function unregisterMcp(dryRun, logger) {
|
|
|
21361
21594
|
return { action: "skipped", detail: "not registered or removal failed" };
|
|
21362
21595
|
}
|
|
21363
21596
|
function installClaudeIntegration(opts) {
|
|
21364
|
-
const settingsPath =
|
|
21597
|
+
const settingsPath = join10(opts.claudeDir, "settings.json");
|
|
21365
21598
|
const settings = readSettings(settingsPath);
|
|
21366
21599
|
const usCmd = hookCommand(opts.nodePath, opts.cliScriptPath, "user-prompt-submit");
|
|
21367
21600
|
const stopCmd = hookCommand(opts.nodePath, opts.cliScriptPath, "stop");
|
|
@@ -21380,7 +21613,7 @@ function installClaudeIntegration(opts) {
|
|
|
21380
21613
|
return { mcp, hooks: { userPromptSubmit, stop }, backupPath };
|
|
21381
21614
|
}
|
|
21382
21615
|
function uninstallClaudeIntegration(opts) {
|
|
21383
|
-
const settingsPath =
|
|
21616
|
+
const settingsPath = join10(opts.claudeDir, "settings.json");
|
|
21384
21617
|
const settings = readSettings(settingsPath);
|
|
21385
21618
|
const usCmd = hookCommand(opts.nodePath, opts.cliScriptPath, "user-prompt-submit");
|
|
21386
21619
|
const stopCmd = hookCommand(opts.nodePath, opts.cliScriptPath, "stop");
|
|
@@ -21417,23 +21650,33 @@ var KNOWLEDGE_GITIGNORE = [
|
|
|
21417
21650
|
"community/",
|
|
21418
21651
|
""
|
|
21419
21652
|
].join("\n");
|
|
21420
|
-
function runInit(ctx, opts = { skipClaude: false, dryRun: false }) {
|
|
21653
|
+
async function runInit(ctx, opts = { skipClaude: false, skipShared: false, dryRun: false }) {
|
|
21421
21654
|
ensureUserConfig(ctx.userConfigPath);
|
|
21422
21655
|
ctx.logger.info(`user config: ${ctx.userConfigPath}`);
|
|
21423
|
-
if (!
|
|
21424
|
-
|
|
21425
|
-
|
|
21656
|
+
if (!existsSync10(ctx.paths.knowledgeRepo)) {
|
|
21657
|
+
mkdirSync5(ctx.paths.knowledgeRepo, { recursive: true });
|
|
21658
|
+
mkdirSync5(ctx.paths.entriesDir, { recursive: true });
|
|
21426
21659
|
ctx.logger.info(`knowledge repo scaffolded: ${ctx.paths.knowledgeRepo}`);
|
|
21427
21660
|
} else {
|
|
21428
21661
|
ctx.logger.info(`knowledge repo: ${ctx.paths.knowledgeRepo}`);
|
|
21429
21662
|
}
|
|
21430
|
-
const gitignorePath =
|
|
21431
|
-
if (!
|
|
21663
|
+
const gitignorePath = join11(ctx.paths.knowledgeRepo, ".gitignore");
|
|
21664
|
+
if (!existsSync10(gitignorePath)) {
|
|
21432
21665
|
writeFileSync5(gitignorePath, KNOWLEDGE_GITIGNORE, "utf-8");
|
|
21433
21666
|
ctx.logger.info(`.gitignore created: ${gitignorePath}`);
|
|
21434
21667
|
}
|
|
21435
21668
|
const db = openDb({ path: ctx.paths.dbPath, logger: ctx.logger });
|
|
21436
|
-
|
|
21669
|
+
try {
|
|
21670
|
+
if (!opts.skipShared && !opts.dryRun) {
|
|
21671
|
+
await subscribeSharedRepo(ctx, db);
|
|
21672
|
+
} else if (opts.skipShared) {
|
|
21673
|
+
ctx.logger.info("shared community DB subscription skipped (--skip-shared)");
|
|
21674
|
+
} else if (opts.dryRun) {
|
|
21675
|
+
ctx.logger.info(`[dry-run] would subscribe to shared community DB: ${ctx.config.sharedRepo}`);
|
|
21676
|
+
}
|
|
21677
|
+
} finally {
|
|
21678
|
+
db.close();
|
|
21679
|
+
}
|
|
21437
21680
|
ctx.logger.info(`db initialized: ${ctx.paths.dbPath}`);
|
|
21438
21681
|
if (opts.skipClaude) {
|
|
21439
21682
|
ctx.logger.info("Claude Code integration skipped (--skip-claude)");
|
|
@@ -21445,7 +21688,7 @@ function runInit(ctx, opts = { skipClaude: false, dryRun: false }) {
|
|
|
21445
21688
|
return;
|
|
21446
21689
|
}
|
|
21447
21690
|
const result = installClaudeIntegration({
|
|
21448
|
-
claudeDir:
|
|
21691
|
+
claudeDir: join11(ctx.userHome, ".claude"),
|
|
21449
21692
|
cliScriptPath,
|
|
21450
21693
|
nodePath: process.execPath,
|
|
21451
21694
|
dryRun: opts.dryRun,
|
|
@@ -21453,6 +21696,52 @@ function runInit(ctx, opts = { skipClaude: false, dryRun: false }) {
|
|
|
21453
21696
|
});
|
|
21454
21697
|
reportInstallResult(ctx, result, opts.dryRun);
|
|
21455
21698
|
}
|
|
21699
|
+
async function subscribeSharedRepo(ctx, db) {
|
|
21700
|
+
const url = ctx.config.sharedRepo;
|
|
21701
|
+
const validation = validateCommunityUrl(url);
|
|
21702
|
+
if (!validation.valid) {
|
|
21703
|
+
ctx.logger.warn(
|
|
21704
|
+
`sharedRepo is not a valid GitHub URL \u2014 skipping: ${validation.reason}`
|
|
21705
|
+
);
|
|
21706
|
+
return;
|
|
21707
|
+
}
|
|
21708
|
+
const handle = validation.handle;
|
|
21709
|
+
const target = join11(ctx.paths.communityDir, handle);
|
|
21710
|
+
if (!existsSync10(target)) {
|
|
21711
|
+
if (!existsSync10(ctx.paths.communityDir)) {
|
|
21712
|
+
mkdirSync5(ctx.paths.communityDir, { recursive: true });
|
|
21713
|
+
}
|
|
21714
|
+
try {
|
|
21715
|
+
await communityAdd({
|
|
21716
|
+
url,
|
|
21717
|
+
communityDir: ctx.paths.communityDir,
|
|
21718
|
+
logger: ctx.logger
|
|
21719
|
+
});
|
|
21720
|
+
ctx.logger.info(`shared community DB subscribed: ${url} \u2192 community/${handle}/`);
|
|
21721
|
+
} catch (err) {
|
|
21722
|
+
ctx.logger.warn(
|
|
21723
|
+
`shared community DB subscription failed: ${err instanceof Error ? err.message : String(err)}`
|
|
21724
|
+
);
|
|
21725
|
+
return;
|
|
21726
|
+
}
|
|
21727
|
+
} else {
|
|
21728
|
+
ctx.logger.info(`shared community DB already subscribed: community/${handle}/`);
|
|
21729
|
+
}
|
|
21730
|
+
if (existsSync10(ctx.paths.communityDir)) {
|
|
21731
|
+
rebuildAll(db);
|
|
21732
|
+
if (existsSync10(ctx.paths.entriesDir)) {
|
|
21733
|
+
scanSource({ db, source: "own", entriesRoot: ctx.paths.entriesDir });
|
|
21734
|
+
}
|
|
21735
|
+
for (const entry of readdirSync6(ctx.paths.communityDir, { withFileTypes: true })) {
|
|
21736
|
+
if (!entry.isDirectory()) continue;
|
|
21737
|
+
const source = `community/${entry.name}`;
|
|
21738
|
+
const root = join11(ctx.paths.communityDir, entry.name, "entries");
|
|
21739
|
+
if (!existsSync10(root)) continue;
|
|
21740
|
+
const result = scanSource({ db, source, entriesRoot: root });
|
|
21741
|
+
ctx.logger.info(`indexed ${source}: +${result.added}`);
|
|
21742
|
+
}
|
|
21743
|
+
}
|
|
21744
|
+
}
|
|
21456
21745
|
function runUninstall(ctx, opts) {
|
|
21457
21746
|
const cliScriptPath = process.argv[1];
|
|
21458
21747
|
if (!cliScriptPath) {
|
|
@@ -21460,7 +21749,7 @@ function runUninstall(ctx, opts) {
|
|
|
21460
21749
|
process.exit(1);
|
|
21461
21750
|
}
|
|
21462
21751
|
const result = uninstallClaudeIntegration({
|
|
21463
|
-
claudeDir:
|
|
21752
|
+
claudeDir: join11(ctx.userHome, ".claude"),
|
|
21464
21753
|
cliScriptPath,
|
|
21465
21754
|
nodePath: process.execPath,
|
|
21466
21755
|
dryRun: opts.dryRun,
|
|
@@ -21497,29 +21786,29 @@ function reportInstallResult(ctx, result, dryRun) {
|
|
|
21497
21786
|
}
|
|
21498
21787
|
|
|
21499
21788
|
// src/commands/indexCmd.ts
|
|
21500
|
-
import { existsSync as
|
|
21501
|
-
import { dirname as
|
|
21789
|
+
import { existsSync as existsSync11, readdirSync as readdirSync7, mkdirSync as mkdirSync6 } from "node:fs";
|
|
21790
|
+
import { dirname as dirname6, join as join12 } from "node:path";
|
|
21502
21791
|
function runIndex(ctx, opts) {
|
|
21503
|
-
const dbDir =
|
|
21504
|
-
if (!
|
|
21792
|
+
const dbDir = dirname6(ctx.paths.dbPath);
|
|
21793
|
+
if (!existsSync11(dbDir)) mkdirSync6(dbDir, { recursive: true });
|
|
21505
21794
|
const db = openDb({ path: ctx.paths.dbPath, logger: ctx.logger });
|
|
21506
21795
|
try {
|
|
21507
21796
|
if (opts.full) {
|
|
21508
21797
|
ctx.logger.info("full rebuild: DELETE FROM entries");
|
|
21509
21798
|
rebuildAll(db);
|
|
21510
21799
|
}
|
|
21511
|
-
if (
|
|
21800
|
+
if (existsSync11(ctx.paths.entriesDir)) {
|
|
21512
21801
|
const result = scanSource({ db, source: "own", entriesRoot: ctx.paths.entriesDir });
|
|
21513
21802
|
ctx.logger.info(`own: +${result.added} ~${result.updated} -${result.deleted}`);
|
|
21514
21803
|
} else {
|
|
21515
21804
|
ctx.logger.warn(`entries dir not found: ${ctx.paths.entriesDir}`);
|
|
21516
21805
|
}
|
|
21517
|
-
if (
|
|
21518
|
-
for (const entry of
|
|
21806
|
+
if (existsSync11(ctx.paths.communityDir)) {
|
|
21807
|
+
for (const entry of readdirSync7(ctx.paths.communityDir, { withFileTypes: true })) {
|
|
21519
21808
|
if (!entry.isDirectory()) continue;
|
|
21520
21809
|
const source = `community/${entry.name}`;
|
|
21521
|
-
const root =
|
|
21522
|
-
if (!
|
|
21810
|
+
const root = join12(ctx.paths.communityDir, entry.name, "entries");
|
|
21811
|
+
if (!existsSync11(root)) continue;
|
|
21523
21812
|
const result = scanSource({ db, source, entriesRoot: root });
|
|
21524
21813
|
ctx.logger.info(`${source}: +${result.added} ~${result.updated} -${result.deleted}`);
|
|
21525
21814
|
}
|
|
@@ -22293,11 +22582,11 @@ var serve = (options2, listeningListener) => {
|
|
|
22293
22582
|
|
|
22294
22583
|
// ../web/dist/context.js
|
|
22295
22584
|
import { homedir as homedir2 } from "node:os";
|
|
22296
|
-
import { join as
|
|
22585
|
+
import { join as join13 } from "node:path";
|
|
22297
22586
|
function buildWebContext(overrides = {}) {
|
|
22298
22587
|
const userHome = overrides.userHome ?? homedir2();
|
|
22299
22588
|
const caveatHome = overrides.caveatHome ?? findCaveatHome(userHome);
|
|
22300
|
-
const userConfigPath =
|
|
22589
|
+
const userConfigPath = join13(userHome, ".caveatrc.json");
|
|
22301
22590
|
const logger = overrides.logger ?? stderrLogger;
|
|
22302
22591
|
const config3 = loadConfig(userConfigPath);
|
|
22303
22592
|
const paths = resolvePaths(caveatHome, config3.knowledgeRepo, userHome);
|
|
@@ -30024,14 +30313,14 @@ function createDetailRoute(ctx) {
|
|
|
30024
30313
|
}
|
|
30025
30314
|
|
|
30026
30315
|
// ../web/dist/routes/community.js
|
|
30027
|
-
import { existsSync as
|
|
30028
|
-
import { join as
|
|
30316
|
+
import { existsSync as existsSync12, readdirSync as readdirSync8, statSync as statSync4 } from "node:fs";
|
|
30317
|
+
import { join as join14 } from "node:path";
|
|
30029
30318
|
function listCommunity(communityDir, db) {
|
|
30030
|
-
if (!
|
|
30319
|
+
if (!existsSync12(communityDir)) return [];
|
|
30031
30320
|
const handles = [];
|
|
30032
|
-
for (const entry of
|
|
30321
|
+
for (const entry of readdirSync8(communityDir, { withFileTypes: true })) {
|
|
30033
30322
|
if (!entry.isDirectory()) continue;
|
|
30034
|
-
const handlePath =
|
|
30323
|
+
const handlePath = join14(communityDir, entry.name);
|
|
30035
30324
|
const countRow = db.prepare("SELECT COUNT(*) AS n FROM entries WHERE source = ?").get(`community/${entry.name}`);
|
|
30036
30325
|
handles.push({
|
|
30037
30326
|
handle: entry.name,
|
|
@@ -44329,11 +44618,11 @@ var StdioServerTransport = class {
|
|
|
44329
44618
|
|
|
44330
44619
|
// ../mcp/dist/context.js
|
|
44331
44620
|
import { homedir as homedir3 } from "node:os";
|
|
44332
|
-
import { join as
|
|
44621
|
+
import { join as join15 } from "node:path";
|
|
44333
44622
|
function buildMcpContext(overrides = {}) {
|
|
44334
44623
|
const userHome = overrides.userHome ?? homedir3();
|
|
44335
44624
|
const caveatHome = overrides.caveatHome ?? findCaveatHome(userHome);
|
|
44336
|
-
const userConfigPath =
|
|
44625
|
+
const userConfigPath = join15(userHome, ".caveatrc.json");
|
|
44337
44626
|
const logger = overrides.logger ?? stderrLogger;
|
|
44338
44627
|
const config3 = loadConfig(userConfigPath);
|
|
44339
44628
|
const paths = resolvePaths(caveatHome, config3.knowledgeRepo, userHome);
|
|
@@ -44482,6 +44771,48 @@ function handleIngestResearch(ctx, args) {
|
|
|
44482
44771
|
);
|
|
44483
44772
|
}
|
|
44484
44773
|
|
|
44774
|
+
// ../mcp/dist/tools/pull.js
|
|
44775
|
+
var pullInputShape = {};
|
|
44776
|
+
async function handlePull(ctx, _args = {}) {
|
|
44777
|
+
const result = await pullShared({
|
|
44778
|
+
communityDir: ctx.paths.communityDir,
|
|
44779
|
+
entriesDir: ctx.paths.entriesDir,
|
|
44780
|
+
db: ctx.db,
|
|
44781
|
+
logger: ctx.logger
|
|
44782
|
+
});
|
|
44783
|
+
return {
|
|
44784
|
+
pulled: result.pulled,
|
|
44785
|
+
indexed: result.indexed.map((i2) => ({
|
|
44786
|
+
source: i2.source,
|
|
44787
|
+
added: i2.added,
|
|
44788
|
+
updated: i2.updated,
|
|
44789
|
+
deleted: i2.deleted
|
|
44790
|
+
}))
|
|
44791
|
+
};
|
|
44792
|
+
}
|
|
44793
|
+
|
|
44794
|
+
// ../mcp/dist/tools/push.js
|
|
44795
|
+
var pushInputShape = {
|
|
44796
|
+
id: external_exports.string().min(1).describe("Entry id (frontmatter.id) to push to the shared community DB"),
|
|
44797
|
+
dry_run: external_exports.boolean().optional().describe("If true, compute the plan but do not modify GitHub or local git state")
|
|
44798
|
+
};
|
|
44799
|
+
async function handlePush(ctx, args) {
|
|
44800
|
+
const result = await pushEntry({
|
|
44801
|
+
entriesDir: ctx.paths.entriesDir,
|
|
44802
|
+
caveatHome: ctx.caveatHome,
|
|
44803
|
+
sharedRepoUrl: ctx.config.sharedRepo,
|
|
44804
|
+
id: args.id,
|
|
44805
|
+
dryRun: args.dry_run ?? false,
|
|
44806
|
+
logger: ctx.logger
|
|
44807
|
+
});
|
|
44808
|
+
return {
|
|
44809
|
+
status: result.status,
|
|
44810
|
+
detail: result.detail,
|
|
44811
|
+
pr_url: result.prUrl,
|
|
44812
|
+
planned_steps: result.plannedSteps
|
|
44813
|
+
};
|
|
44814
|
+
}
|
|
44815
|
+
|
|
44485
44816
|
// ../mcp/dist/registerTools.js
|
|
44486
44817
|
function jsonResult(data) {
|
|
44487
44818
|
return {
|
|
@@ -44557,6 +44888,24 @@ function registerAllTools(server, ctx) {
|
|
|
44557
44888
|
},
|
|
44558
44889
|
async (args) => jsonResult(handleIngestResearch(ctx, args))
|
|
44559
44890
|
);
|
|
44891
|
+
server.registerTool(
|
|
44892
|
+
"caveat_pull",
|
|
44893
|
+
{
|
|
44894
|
+
title: "caveat_pull",
|
|
44895
|
+
description: "git-pull all subscribed community caveat repos (incl. the shared DB) and re-index. Call this when the user asks if others have documented a similar trap, or at the start of a session that might benefit from fresh external knowledge. Safe and idempotent \u2014 re-running is cheap.",
|
|
44896
|
+
inputSchema: pullInputShape
|
|
44897
|
+
},
|
|
44898
|
+
async (args) => jsonResult(await handlePull(ctx, args))
|
|
44899
|
+
);
|
|
44900
|
+
server.registerTool(
|
|
44901
|
+
"caveat_push",
|
|
44902
|
+
{
|
|
44903
|
+
title: "caveat_push",
|
|
44904
|
+
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.",
|
|
44905
|
+
inputSchema: pushInputShape
|
|
44906
|
+
},
|
|
44907
|
+
async (args) => jsonResult(await handlePush(ctx, args))
|
|
44908
|
+
);
|
|
44560
44909
|
}
|
|
44561
44910
|
|
|
44562
44911
|
// ../mcp/dist/server.js
|
|
@@ -44648,6 +44997,78 @@ async function runHook(name) {
|
|
|
44648
44997
|
process.exit(0);
|
|
44649
44998
|
}
|
|
44650
44999
|
|
|
45000
|
+
// src/commands/pull.ts
|
|
45001
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
45002
|
+
async function runPull(ctx) {
|
|
45003
|
+
if (!existsSync13(ctx.paths.communityDir)) {
|
|
45004
|
+
ctx.logger.info(
|
|
45005
|
+
"no community repos yet \u2014 did you run `caveat init`? You can also `caveat community add <url>` manually."
|
|
45006
|
+
);
|
|
45007
|
+
return;
|
|
45008
|
+
}
|
|
45009
|
+
const db = openDb({ path: ctx.paths.dbPath, logger: ctx.logger });
|
|
45010
|
+
try {
|
|
45011
|
+
const result = await pullShared({
|
|
45012
|
+
communityDir: ctx.paths.communityDir,
|
|
45013
|
+
entriesDir: ctx.paths.entriesDir,
|
|
45014
|
+
db,
|
|
45015
|
+
logger: ctx.logger
|
|
45016
|
+
});
|
|
45017
|
+
for (const p2 of result.pulled) {
|
|
45018
|
+
if (p2.status === "ok") {
|
|
45019
|
+
ctx.logger.info(`${p2.handle}: pulled`);
|
|
45020
|
+
} else {
|
|
45021
|
+
ctx.logger.warn(`${p2.handle}: FAILED \u2014 ${p2.message ?? "unknown"}`);
|
|
45022
|
+
}
|
|
45023
|
+
}
|
|
45024
|
+
for (const i2 of result.indexed) {
|
|
45025
|
+
ctx.logger.info(`${i2.source}: +${i2.added}`);
|
|
45026
|
+
}
|
|
45027
|
+
} finally {
|
|
45028
|
+
db.close();
|
|
45029
|
+
}
|
|
45030
|
+
}
|
|
45031
|
+
|
|
45032
|
+
// src/commands/push.ts
|
|
45033
|
+
async function runPush(ctx, opts) {
|
|
45034
|
+
const result = await pushEntry({
|
|
45035
|
+
entriesDir: ctx.paths.entriesDir,
|
|
45036
|
+
caveatHome: ctx.caveatHome,
|
|
45037
|
+
sharedRepoUrl: ctx.config.sharedRepo,
|
|
45038
|
+
id: opts.id,
|
|
45039
|
+
dryRun: opts.dryRun,
|
|
45040
|
+
logger: ctx.logger
|
|
45041
|
+
});
|
|
45042
|
+
switch (result.status) {
|
|
45043
|
+
case "ok":
|
|
45044
|
+
ctx.logger.info(`PR opened: ${result.prUrl ?? ""}`);
|
|
45045
|
+
ctx.logger.info(
|
|
45046
|
+
"Once merged, other subscribers will see your entry after their next `caveat pull`."
|
|
45047
|
+
);
|
|
45048
|
+
break;
|
|
45049
|
+
case "dry-run":
|
|
45050
|
+
for (const [i2, step] of (result.plannedSteps ?? []).entries()) {
|
|
45051
|
+
ctx.logger.info(`[dry-run] ${i2 + 1}. ${step}`);
|
|
45052
|
+
}
|
|
45053
|
+
break;
|
|
45054
|
+
case "not-found":
|
|
45055
|
+
ctx.logger.error(
|
|
45056
|
+
`${result.detail}. List entries with \`caveat list\` or write + index first.`
|
|
45057
|
+
);
|
|
45058
|
+
process.exitCode = 1;
|
|
45059
|
+
break;
|
|
45060
|
+
case "gh-missing":
|
|
45061
|
+
case "gh-unauthed":
|
|
45062
|
+
ctx.logger.error(result.detail ?? "gh CLI issue");
|
|
45063
|
+
process.exitCode = 1;
|
|
45064
|
+
break;
|
|
45065
|
+
case "failed":
|
|
45066
|
+
ctx.logger.error(result.detail ?? "unknown failure");
|
|
45067
|
+
process.exitCode = 1;
|
|
45068
|
+
break;
|
|
45069
|
+
}
|
|
45070
|
+
}
|
|
45071
|
+
|
|
44651
45072
|
// src/commands/community.ts
|
|
44652
45073
|
async function runCommunityAdd(ctx, url) {
|
|
44653
45074
|
try {
|
|
@@ -44700,12 +45121,16 @@ function runCommunityList(ctx) {
|
|
|
44700
45121
|
|
|
44701
45122
|
// src/index.ts
|
|
44702
45123
|
var program = new Command();
|
|
44703
|
-
program.name("caveat").description("External spec gotcha knowledge base CLI").version("0.
|
|
45124
|
+
program.name("caveat").description("External spec gotcha knowledge base CLI").version("0.4.0");
|
|
44704
45125
|
program.command("init").description(
|
|
44705
|
-
"Initialize ~/.caveatrc.json, ~/.caveat/, and register Claude Code integration"
|
|
44706
|
-
).option("--skip-claude", "skip Claude Code MCP + hook registration", false).option("--dry-run", "show planned changes without writing", false).action((opts) => {
|
|
45126
|
+
"Initialize ~/.caveatrc.json, ~/.caveat/, subscribe to the shared community DB, and register Claude Code integration"
|
|
45127
|
+
).option("--skip-claude", "skip Claude Code MCP + hook registration", false).option("--skip-shared", "skip subscribing to the shared community DB", false).option("--dry-run", "show planned changes without writing", false).action(async (opts) => {
|
|
44707
45128
|
const ctx = buildContext(stdoutLogger);
|
|
44708
|
-
runInit(ctx, {
|
|
45129
|
+
await runInit(ctx, {
|
|
45130
|
+
skipClaude: opts.skipClaude,
|
|
45131
|
+
skipShared: opts.skipShared,
|
|
45132
|
+
dryRun: opts.dryRun
|
|
45133
|
+
});
|
|
44709
45134
|
});
|
|
44710
45135
|
program.command("uninstall").description("Remove Claude Code MCP server and hooks registered by `caveat init`").option("--dry-run", "show planned changes without writing", false).action((opts) => {
|
|
44711
45136
|
const ctx = buildContext(stdoutLogger);
|
|
@@ -44738,6 +45163,16 @@ program.command("stats").description("Show aggregate stats").action(() => {
|
|
|
44738
45163
|
const ctx = buildContext(stdoutLogger);
|
|
44739
45164
|
runStats(ctx);
|
|
44740
45165
|
});
|
|
45166
|
+
program.command("pull").description(
|
|
45167
|
+
"Refresh all subscribed community repos (incl. shared DB) and re-index. Use this to receive contributions others have merged."
|
|
45168
|
+
).action(async () => {
|
|
45169
|
+
const ctx = buildContext(stdoutLogger);
|
|
45170
|
+
await runPull(ctx);
|
|
45171
|
+
});
|
|
45172
|
+
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) => {
|
|
45173
|
+
const ctx = buildContext(stdoutLogger);
|
|
45174
|
+
await runPush(ctx, { id, dryRun: opts.dryRun });
|
|
45175
|
+
});
|
|
44741
45176
|
program.command("serve").description("Start the read-only web share portal").option("--port <n>", "port number", (v) => Number(v), 4242).action((opts) => {
|
|
44742
45177
|
runServe({ port: opts.port });
|
|
44743
45178
|
});
|