caveat-cli 0.3.0 → 0.5.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 +393 -287
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -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,7 +16139,7 @@ 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/
|
|
16142
|
+
var SHARED_REPO_URL = "https://github.com/kitepon-rgb/Caveat";
|
|
16143
16143
|
var DEFAULT_CONFIG = {
|
|
16144
16144
|
knowledgeRepo: "own",
|
|
16145
16145
|
semverKeys: ["driver", "cuda", "node"],
|
|
@@ -21228,15 +21228,246 @@ function stopReminderText() {
|
|
|
21228
21228
|
return [
|
|
21229
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",
|
|
21230
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",
|
|
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"
|
|
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"
|
|
21232
21233
|
].join("\n");
|
|
21233
21234
|
}
|
|
21234
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
|
+
|
|
21235
21466
|
// src/context.ts
|
|
21236
21467
|
function buildContext(logger, overrides = {}) {
|
|
21237
21468
|
const userHome = overrides.userHome ?? homedir();
|
|
21238
21469
|
const caveatHome = overrides.caveatHome ?? findCaveatHome(userHome);
|
|
21239
|
-
const userConfigPath =
|
|
21470
|
+
const userConfigPath = join9(userHome, ".caveatrc.json");
|
|
21240
21471
|
const config3 = loadConfig(userConfigPath);
|
|
21241
21472
|
const paths = resolvePaths(caveatHome, config3.knowledgeRepo, userHome);
|
|
21242
21473
|
return { caveatHome, userHome, userConfigPath, config: config3, paths, logger };
|
|
@@ -21253,13 +21484,13 @@ var stdoutLogger = {
|
|
|
21253
21484
|
};
|
|
21254
21485
|
|
|
21255
21486
|
// src/commands/init.ts
|
|
21256
|
-
import { existsSync as
|
|
21257
|
-
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";
|
|
21258
21489
|
|
|
21259
21490
|
// src/claudeInstall.ts
|
|
21260
|
-
import { spawnSync } from "node:child_process";
|
|
21261
|
-
import { copyFileSync, existsSync as
|
|
21262
|
-
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";
|
|
21263
21494
|
var EVENT_USER_PROMPT_SUBMIT = "UserPromptSubmit";
|
|
21264
21495
|
var EVENT_STOP = "Stop";
|
|
21265
21496
|
function quote(p2) {
|
|
@@ -21293,16 +21524,16 @@ function removeHook(settings, event, command) {
|
|
|
21293
21524
|
return true;
|
|
21294
21525
|
}
|
|
21295
21526
|
function readSettings(path) {
|
|
21296
|
-
if (!
|
|
21297
|
-
return JSON.parse(
|
|
21527
|
+
if (!existsSync9(path)) return {};
|
|
21528
|
+
return JSON.parse(readFileSync6(path, "utf-8"));
|
|
21298
21529
|
}
|
|
21299
21530
|
function writeSettings(path, settings) {
|
|
21300
|
-
const dir =
|
|
21301
|
-
if (!
|
|
21531
|
+
const dir = dirname5(path);
|
|
21532
|
+
if (!existsSync9(dir)) mkdirSync4(dir, { recursive: true });
|
|
21302
21533
|
let backupPath = "";
|
|
21303
|
-
if (
|
|
21534
|
+
if (existsSync9(path)) {
|
|
21304
21535
|
backupPath = `${path}.caveat-backup-${Date.now()}`;
|
|
21305
|
-
|
|
21536
|
+
copyFileSync2(path, backupPath);
|
|
21306
21537
|
}
|
|
21307
21538
|
writeFileSync4(path, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
21308
21539
|
return backupPath;
|
|
@@ -21313,7 +21544,7 @@ function shellQuote(s) {
|
|
|
21313
21544
|
}
|
|
21314
21545
|
function runClaude(args) {
|
|
21315
21546
|
const line = [CLAUDE_BIN, ...args].map(shellQuote).join(" ");
|
|
21316
|
-
return
|
|
21547
|
+
return spawnSync2(line, { shell: true, encoding: "utf-8" });
|
|
21317
21548
|
}
|
|
21318
21549
|
function registerMcp(nodePath, cliScriptPath, dryRun, logger) {
|
|
21319
21550
|
const args = mcpArgs(cliScriptPath);
|
|
@@ -21363,7 +21594,7 @@ function unregisterMcp(dryRun, logger) {
|
|
|
21363
21594
|
return { action: "skipped", detail: "not registered or removal failed" };
|
|
21364
21595
|
}
|
|
21365
21596
|
function installClaudeIntegration(opts) {
|
|
21366
|
-
const settingsPath =
|
|
21597
|
+
const settingsPath = join10(opts.claudeDir, "settings.json");
|
|
21367
21598
|
const settings = readSettings(settingsPath);
|
|
21368
21599
|
const usCmd = hookCommand(opts.nodePath, opts.cliScriptPath, "user-prompt-submit");
|
|
21369
21600
|
const stopCmd = hookCommand(opts.nodePath, opts.cliScriptPath, "stop");
|
|
@@ -21382,7 +21613,7 @@ function installClaudeIntegration(opts) {
|
|
|
21382
21613
|
return { mcp, hooks: { userPromptSubmit, stop }, backupPath };
|
|
21383
21614
|
}
|
|
21384
21615
|
function uninstallClaudeIntegration(opts) {
|
|
21385
|
-
const settingsPath =
|
|
21616
|
+
const settingsPath = join10(opts.claudeDir, "settings.json");
|
|
21386
21617
|
const settings = readSettings(settingsPath);
|
|
21387
21618
|
const usCmd = hookCommand(opts.nodePath, opts.cliScriptPath, "user-prompt-submit");
|
|
21388
21619
|
const stopCmd = hookCommand(opts.nodePath, opts.cliScriptPath, "stop");
|
|
@@ -21422,15 +21653,15 @@ var KNOWLEDGE_GITIGNORE = [
|
|
|
21422
21653
|
async function runInit(ctx, opts = { skipClaude: false, skipShared: false, dryRun: false }) {
|
|
21423
21654
|
ensureUserConfig(ctx.userConfigPath);
|
|
21424
21655
|
ctx.logger.info(`user config: ${ctx.userConfigPath}`);
|
|
21425
|
-
if (!
|
|
21426
|
-
|
|
21427
|
-
|
|
21656
|
+
if (!existsSync10(ctx.paths.knowledgeRepo)) {
|
|
21657
|
+
mkdirSync5(ctx.paths.knowledgeRepo, { recursive: true });
|
|
21658
|
+
mkdirSync5(ctx.paths.entriesDir, { recursive: true });
|
|
21428
21659
|
ctx.logger.info(`knowledge repo scaffolded: ${ctx.paths.knowledgeRepo}`);
|
|
21429
21660
|
} else {
|
|
21430
21661
|
ctx.logger.info(`knowledge repo: ${ctx.paths.knowledgeRepo}`);
|
|
21431
21662
|
}
|
|
21432
|
-
const gitignorePath =
|
|
21433
|
-
if (!
|
|
21663
|
+
const gitignorePath = join11(ctx.paths.knowledgeRepo, ".gitignore");
|
|
21664
|
+
if (!existsSync10(gitignorePath)) {
|
|
21434
21665
|
writeFileSync5(gitignorePath, KNOWLEDGE_GITIGNORE, "utf-8");
|
|
21435
21666
|
ctx.logger.info(`.gitignore created: ${gitignorePath}`);
|
|
21436
21667
|
}
|
|
@@ -21457,7 +21688,7 @@ async function runInit(ctx, opts = { skipClaude: false, skipShared: false, dryRu
|
|
|
21457
21688
|
return;
|
|
21458
21689
|
}
|
|
21459
21690
|
const result = installClaudeIntegration({
|
|
21460
|
-
claudeDir:
|
|
21691
|
+
claudeDir: join11(ctx.userHome, ".claude"),
|
|
21461
21692
|
cliScriptPath,
|
|
21462
21693
|
nodePath: process.execPath,
|
|
21463
21694
|
dryRun: opts.dryRun,
|
|
@@ -21475,10 +21706,10 @@ async function subscribeSharedRepo(ctx, db) {
|
|
|
21475
21706
|
return;
|
|
21476
21707
|
}
|
|
21477
21708
|
const handle = validation.handle;
|
|
21478
|
-
const target =
|
|
21479
|
-
if (!
|
|
21480
|
-
if (!
|
|
21481
|
-
|
|
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 });
|
|
21482
21713
|
}
|
|
21483
21714
|
try {
|
|
21484
21715
|
await communityAdd({
|
|
@@ -21496,16 +21727,16 @@ async function subscribeSharedRepo(ctx, db) {
|
|
|
21496
21727
|
} else {
|
|
21497
21728
|
ctx.logger.info(`shared community DB already subscribed: community/${handle}/`);
|
|
21498
21729
|
}
|
|
21499
|
-
if (
|
|
21730
|
+
if (existsSync10(ctx.paths.communityDir)) {
|
|
21500
21731
|
rebuildAll(db);
|
|
21501
|
-
if (
|
|
21732
|
+
if (existsSync10(ctx.paths.entriesDir)) {
|
|
21502
21733
|
scanSource({ db, source: "own", entriesRoot: ctx.paths.entriesDir });
|
|
21503
21734
|
}
|
|
21504
|
-
for (const entry of
|
|
21735
|
+
for (const entry of readdirSync6(ctx.paths.communityDir, { withFileTypes: true })) {
|
|
21505
21736
|
if (!entry.isDirectory()) continue;
|
|
21506
21737
|
const source = `community/${entry.name}`;
|
|
21507
|
-
const root =
|
|
21508
|
-
if (!
|
|
21738
|
+
const root = join11(ctx.paths.communityDir, entry.name, "entries");
|
|
21739
|
+
if (!existsSync10(root)) continue;
|
|
21509
21740
|
const result = scanSource({ db, source, entriesRoot: root });
|
|
21510
21741
|
ctx.logger.info(`indexed ${source}: +${result.added}`);
|
|
21511
21742
|
}
|
|
@@ -21518,7 +21749,7 @@ function runUninstall(ctx, opts) {
|
|
|
21518
21749
|
process.exit(1);
|
|
21519
21750
|
}
|
|
21520
21751
|
const result = uninstallClaudeIntegration({
|
|
21521
|
-
claudeDir:
|
|
21752
|
+
claudeDir: join11(ctx.userHome, ".claude"),
|
|
21522
21753
|
cliScriptPath,
|
|
21523
21754
|
nodePath: process.execPath,
|
|
21524
21755
|
dryRun: opts.dryRun,
|
|
@@ -21555,29 +21786,29 @@ function reportInstallResult(ctx, result, dryRun) {
|
|
|
21555
21786
|
}
|
|
21556
21787
|
|
|
21557
21788
|
// src/commands/indexCmd.ts
|
|
21558
|
-
import { existsSync as
|
|
21559
|
-
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";
|
|
21560
21791
|
function runIndex(ctx, opts) {
|
|
21561
|
-
const dbDir =
|
|
21562
|
-
if (!
|
|
21792
|
+
const dbDir = dirname6(ctx.paths.dbPath);
|
|
21793
|
+
if (!existsSync11(dbDir)) mkdirSync6(dbDir, { recursive: true });
|
|
21563
21794
|
const db = openDb({ path: ctx.paths.dbPath, logger: ctx.logger });
|
|
21564
21795
|
try {
|
|
21565
21796
|
if (opts.full) {
|
|
21566
21797
|
ctx.logger.info("full rebuild: DELETE FROM entries");
|
|
21567
21798
|
rebuildAll(db);
|
|
21568
21799
|
}
|
|
21569
|
-
if (
|
|
21800
|
+
if (existsSync11(ctx.paths.entriesDir)) {
|
|
21570
21801
|
const result = scanSource({ db, source: "own", entriesRoot: ctx.paths.entriesDir });
|
|
21571
21802
|
ctx.logger.info(`own: +${result.added} ~${result.updated} -${result.deleted}`);
|
|
21572
21803
|
} else {
|
|
21573
21804
|
ctx.logger.warn(`entries dir not found: ${ctx.paths.entriesDir}`);
|
|
21574
21805
|
}
|
|
21575
|
-
if (
|
|
21576
|
-
for (const entry of
|
|
21806
|
+
if (existsSync11(ctx.paths.communityDir)) {
|
|
21807
|
+
for (const entry of readdirSync7(ctx.paths.communityDir, { withFileTypes: true })) {
|
|
21577
21808
|
if (!entry.isDirectory()) continue;
|
|
21578
21809
|
const source = `community/${entry.name}`;
|
|
21579
|
-
const root =
|
|
21580
|
-
if (!
|
|
21810
|
+
const root = join12(ctx.paths.communityDir, entry.name, "entries");
|
|
21811
|
+
if (!existsSync11(root)) continue;
|
|
21581
21812
|
const result = scanSource({ db, source, entriesRoot: root });
|
|
21582
21813
|
ctx.logger.info(`${source}: +${result.added} ~${result.updated} -${result.deleted}`);
|
|
21583
21814
|
}
|
|
@@ -22351,11 +22582,11 @@ var serve = (options2, listeningListener) => {
|
|
|
22351
22582
|
|
|
22352
22583
|
// ../web/dist/context.js
|
|
22353
22584
|
import { homedir as homedir2 } from "node:os";
|
|
22354
|
-
import { join as
|
|
22585
|
+
import { join as join13 } from "node:path";
|
|
22355
22586
|
function buildWebContext(overrides = {}) {
|
|
22356
22587
|
const userHome = overrides.userHome ?? homedir2();
|
|
22357
22588
|
const caveatHome = overrides.caveatHome ?? findCaveatHome(userHome);
|
|
22358
|
-
const userConfigPath =
|
|
22589
|
+
const userConfigPath = join13(userHome, ".caveatrc.json");
|
|
22359
22590
|
const logger = overrides.logger ?? stderrLogger;
|
|
22360
22591
|
const config3 = loadConfig(userConfigPath);
|
|
22361
22592
|
const paths = resolvePaths(caveatHome, config3.knowledgeRepo, userHome);
|
|
@@ -30082,14 +30313,14 @@ function createDetailRoute(ctx) {
|
|
|
30082
30313
|
}
|
|
30083
30314
|
|
|
30084
30315
|
// ../web/dist/routes/community.js
|
|
30085
|
-
import { existsSync as
|
|
30086
|
-
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";
|
|
30087
30318
|
function listCommunity(communityDir, db) {
|
|
30088
|
-
if (!
|
|
30319
|
+
if (!existsSync12(communityDir)) return [];
|
|
30089
30320
|
const handles = [];
|
|
30090
|
-
for (const entry of
|
|
30321
|
+
for (const entry of readdirSync8(communityDir, { withFileTypes: true })) {
|
|
30091
30322
|
if (!entry.isDirectory()) continue;
|
|
30092
|
-
const handlePath =
|
|
30323
|
+
const handlePath = join14(communityDir, entry.name);
|
|
30093
30324
|
const countRow = db.prepare("SELECT COUNT(*) AS n FROM entries WHERE source = ?").get(`community/${entry.name}`);
|
|
30094
30325
|
handles.push({
|
|
30095
30326
|
handle: entry.name,
|
|
@@ -44387,11 +44618,11 @@ var StdioServerTransport = class {
|
|
|
44387
44618
|
|
|
44388
44619
|
// ../mcp/dist/context.js
|
|
44389
44620
|
import { homedir as homedir3 } from "node:os";
|
|
44390
|
-
import { join as
|
|
44621
|
+
import { join as join15 } from "node:path";
|
|
44391
44622
|
function buildMcpContext(overrides = {}) {
|
|
44392
44623
|
const userHome = overrides.userHome ?? homedir3();
|
|
44393
44624
|
const caveatHome = overrides.caveatHome ?? findCaveatHome(userHome);
|
|
44394
|
-
const userConfigPath =
|
|
44625
|
+
const userConfigPath = join15(userHome, ".caveatrc.json");
|
|
44395
44626
|
const logger = overrides.logger ?? stderrLogger;
|
|
44396
44627
|
const config3 = loadConfig(userConfigPath);
|
|
44397
44628
|
const paths = resolvePaths(caveatHome, config3.knowledgeRepo, userHome);
|
|
@@ -44540,6 +44771,48 @@ function handleIngestResearch(ctx, args) {
|
|
|
44540
44771
|
);
|
|
44541
44772
|
}
|
|
44542
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
|
+
|
|
44543
44816
|
// ../mcp/dist/registerTools.js
|
|
44544
44817
|
function jsonResult(data) {
|
|
44545
44818
|
return {
|
|
@@ -44615,6 +44888,24 @@ function registerAllTools(server, ctx) {
|
|
|
44615
44888
|
},
|
|
44616
44889
|
async (args) => jsonResult(handleIngestResearch(ctx, args))
|
|
44617
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
|
+
);
|
|
44618
44909
|
}
|
|
44619
44910
|
|
|
44620
44911
|
// ../mcp/dist/server.js
|
|
@@ -44707,40 +44998,31 @@ async function runHook(name) {
|
|
|
44707
44998
|
}
|
|
44708
44999
|
|
|
44709
45000
|
// src/commands/pull.ts
|
|
44710
|
-
import { existsSync as
|
|
44711
|
-
import { join as join14 } from "node:path";
|
|
45001
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
44712
45002
|
async function runPull(ctx) {
|
|
44713
|
-
if (!
|
|
45003
|
+
if (!existsSync13(ctx.paths.communityDir)) {
|
|
44714
45004
|
ctx.logger.info(
|
|
44715
45005
|
"no community repos yet \u2014 did you run `caveat init`? You can also `caveat community add <url>` manually."
|
|
44716
45006
|
);
|
|
44717
45007
|
return;
|
|
44718
45008
|
}
|
|
44719
|
-
const results = await communityPull({
|
|
44720
|
-
communityDir: ctx.paths.communityDir,
|
|
44721
|
-
logger: ctx.logger
|
|
44722
|
-
});
|
|
44723
|
-
for (const r2 of results) {
|
|
44724
|
-
if (r2.status === "ok") {
|
|
44725
|
-
ctx.logger.info(`${r2.handle}: pulled`);
|
|
44726
|
-
} else {
|
|
44727
|
-
ctx.logger.warn(`${r2.handle}: FAILED \u2014 ${r2.message ?? "unknown"}`);
|
|
44728
|
-
}
|
|
44729
|
-
}
|
|
44730
45009
|
const db = openDb({ path: ctx.paths.dbPath, logger: ctx.logger });
|
|
44731
45010
|
try {
|
|
44732
|
-
|
|
44733
|
-
|
|
44734
|
-
|
|
44735
|
-
|
|
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
|
+
}
|
|
44736
45023
|
}
|
|
44737
|
-
for (const
|
|
44738
|
-
|
|
44739
|
-
const source = `community/${entry.name}`;
|
|
44740
|
-
const root = join14(ctx.paths.communityDir, entry.name, "entries");
|
|
44741
|
-
if (!existsSync11(root)) continue;
|
|
44742
|
-
const scan = scanSource({ db, source, entriesRoot: root });
|
|
44743
|
-
ctx.logger.info(`${source}: +${scan.added}`);
|
|
45024
|
+
for (const i2 of result.indexed) {
|
|
45025
|
+
ctx.logger.info(`${i2.source}: +${i2.added}`);
|
|
44744
45026
|
}
|
|
44745
45027
|
} finally {
|
|
44746
45028
|
db.close();
|
|
@@ -44748,220 +45030,44 @@ async function runPull(ctx) {
|
|
|
44748
45030
|
}
|
|
44749
45031
|
|
|
44750
45032
|
// src/commands/push.ts
|
|
44751
|
-
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
44752
|
-
import {
|
|
44753
|
-
copyFileSync as copyFileSync2,
|
|
44754
|
-
existsSync as existsSync12,
|
|
44755
|
-
mkdirSync as mkdirSync6,
|
|
44756
|
-
readdirSync as readdirSync8,
|
|
44757
|
-
readFileSync as readFileSync6,
|
|
44758
|
-
statSync as statSync5
|
|
44759
|
-
} from "node:fs";
|
|
44760
|
-
import { dirname as dirname6, join as join15, relative as relative2 } from "node:path";
|
|
44761
45033
|
async function runPush(ctx, opts) {
|
|
44762
|
-
const
|
|
44763
|
-
|
|
44764
|
-
|
|
44765
|
-
|
|
44766
|
-
|
|
44767
|
-
|
|
44768
|
-
|
|
44769
|
-
process.exitCode = 1;
|
|
44770
|
-
return;
|
|
44771
|
-
}
|
|
44772
|
-
const [sharedOwner, sharedName] = parseOwnerRepo(ctx.config.sharedRepo);
|
|
44773
|
-
if (!sharedOwner || !sharedName) {
|
|
44774
|
-
ctx.logger.error(`cannot parse sharedRepo owner/repo: ${ctx.config.sharedRepo}`);
|
|
44775
|
-
process.exitCode = 1;
|
|
44776
|
-
return;
|
|
44777
|
-
}
|
|
44778
|
-
const owned = findOwnedEntry(ctx, opts.id);
|
|
44779
|
-
if (!owned) {
|
|
44780
|
-
ctx.logger.error(
|
|
44781
|
-
`entry not found in ${ctx.paths.entriesDir}: id=${opts.id}. List with \`caveat list\`, or write the md file and index first.`
|
|
44782
|
-
);
|
|
44783
|
-
process.exitCode = 1;
|
|
44784
|
-
return;
|
|
44785
|
-
}
|
|
44786
|
-
const ghUser = resolveGhUser(ctx);
|
|
44787
|
-
if (!ghUser) return;
|
|
44788
|
-
if (opts.dryRun) {
|
|
44789
|
-
ctx.logger.info(`[dry-run] would push entry ${owned.id} (${owned.title})`);
|
|
44790
|
-
ctx.logger.info(`[dry-run] 1. fork ${sharedOwner}/${sharedName} as ${ghUser}/${sharedName}`);
|
|
44791
|
-
ctx.logger.info(`[dry-run] 2. clone fork \u2192 ${forkStagingDir(ctx)}`);
|
|
44792
|
-
ctx.logger.info(`[dry-run] 3. copy entry \u2192 entries/${owned.relPath}`);
|
|
44793
|
-
ctx.logger.info("[dry-run] 4. commit, push branch, open PR against main");
|
|
44794
|
-
return;
|
|
44795
|
-
}
|
|
44796
|
-
ensureFork(ctx, sharedOwner, sharedName, ghUser);
|
|
44797
|
-
const stagingDir = forkStagingDir(ctx);
|
|
44798
|
-
ensureStagingClone(ctx, stagingDir, ghUser, sharedName, sharedOwner);
|
|
44799
|
-
const branch = `caveat-push-${owned.id}-${Date.now().toString(36)}`;
|
|
44800
|
-
run(ctx, "git", ["checkout", "-b", branch], { cwd: stagingDir });
|
|
44801
|
-
const destPath = join15(stagingDir, "entries", owned.relPath);
|
|
44802
|
-
mkdirSync6(dirname6(destPath), { recursive: true });
|
|
44803
|
-
copyFileSync2(owned.absPath, destPath);
|
|
44804
|
-
run(ctx, "git", ["add", join15("entries", owned.relPath)], { cwd: stagingDir });
|
|
44805
|
-
const verb = existsInUpstream(stagingDir, "entries/" + owned.relPath) ? "update" : "add";
|
|
44806
|
-
const commitMsg = `${verb}: ${owned.title}`;
|
|
44807
|
-
run(ctx, "git", ["commit", "-m", commitMsg], { cwd: stagingDir });
|
|
44808
|
-
run(ctx, "git", ["push", "--set-upstream", "origin", branch], { cwd: stagingDir });
|
|
44809
|
-
const prTitle = `${verb}: ${owned.title}`;
|
|
44810
|
-
const prBody = [
|
|
44811
|
-
`Contribution of caveat \`${owned.id}\` via \`caveat push\`.`,
|
|
44812
|
-
"",
|
|
44813
|
-
`- entry path: \`entries/${owned.relPath}\``,
|
|
44814
|
-
`- action: ${verb}`,
|
|
44815
|
-
"",
|
|
44816
|
-
"Merged PRs will appear in subscribers' repos on their next `caveat pull`."
|
|
44817
|
-
].join("\n");
|
|
44818
|
-
const prResult = runClaim(
|
|
44819
|
-
ctx,
|
|
44820
|
-
"gh",
|
|
44821
|
-
[
|
|
44822
|
-
"pr",
|
|
44823
|
-
"create",
|
|
44824
|
-
"--repo",
|
|
44825
|
-
`${sharedOwner}/${sharedName}`,
|
|
44826
|
-
"--base",
|
|
44827
|
-
"main",
|
|
44828
|
-
"--head",
|
|
44829
|
-
`${ghUser}:${branch}`,
|
|
44830
|
-
"--title",
|
|
44831
|
-
prTitle,
|
|
44832
|
-
"--body",
|
|
44833
|
-
prBody
|
|
44834
|
-
],
|
|
44835
|
-
{ cwd: stagingDir }
|
|
44836
|
-
);
|
|
44837
|
-
if (prResult.status === 0) {
|
|
44838
|
-
const url = prResult.stdout.trim();
|
|
44839
|
-
ctx.logger.info(`PR opened: ${url}`);
|
|
44840
|
-
ctx.logger.info(
|
|
44841
|
-
"Once merged, other subscribers will see your entry after their next `caveat pull`."
|
|
44842
|
-
);
|
|
44843
|
-
} else {
|
|
44844
|
-
ctx.logger.error(
|
|
44845
|
-
`gh pr create failed: ${(prResult.stderr || prResult.stdout || "").trim()}`
|
|
44846
|
-
);
|
|
44847
|
-
process.exitCode = 1;
|
|
44848
|
-
}
|
|
44849
|
-
}
|
|
44850
|
-
function checkGhAvailable(ctx) {
|
|
44851
|
-
const r2 = spawnSync2("gh", ["--version"], { encoding: "utf-8", shell: true });
|
|
44852
|
-
if (r2.status !== 0) {
|
|
44853
|
-
ctx.logger.error(
|
|
44854
|
-
"gh (GitHub CLI) is required for `caveat push`. Install: https://cli.github.com/"
|
|
44855
|
-
);
|
|
44856
|
-
process.exitCode = 1;
|
|
44857
|
-
return false;
|
|
44858
|
-
}
|
|
44859
|
-
const auth = spawnSync2("gh", ["auth", "status"], { encoding: "utf-8", shell: true });
|
|
44860
|
-
if (auth.status !== 0) {
|
|
44861
|
-
ctx.logger.error("gh is not authenticated. Run `gh auth login` first.");
|
|
44862
|
-
process.exitCode = 1;
|
|
44863
|
-
return false;
|
|
44864
|
-
}
|
|
44865
|
-
return true;
|
|
44866
|
-
}
|
|
44867
|
-
function resolveGhUser(ctx) {
|
|
44868
|
-
const r2 = spawnSync2("gh", ["api", "user", "--jq", ".login"], {
|
|
44869
|
-
encoding: "utf-8",
|
|
44870
|
-
shell: true
|
|
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
|
|
44871
45041
|
});
|
|
44872
|
-
|
|
44873
|
-
|
|
44874
|
-
|
|
44875
|
-
|
|
44876
|
-
|
|
44877
|
-
|
|
44878
|
-
|
|
44879
|
-
|
|
44880
|
-
|
|
44881
|
-
|
|
44882
|
-
return [m[1], m[2]];
|
|
44883
|
-
}
|
|
44884
|
-
function findOwnedEntry(ctx, id) {
|
|
44885
|
-
if (!existsSync12(ctx.paths.entriesDir)) return void 0;
|
|
44886
|
-
for (const categoryDirent of readdirSync8(ctx.paths.entriesDir, { withFileTypes: true })) {
|
|
44887
|
-
if (!categoryDirent.isDirectory()) continue;
|
|
44888
|
-
const categoryDir = join15(ctx.paths.entriesDir, categoryDirent.name);
|
|
44889
|
-
for (const fileDirent of readdirSync8(categoryDir, { withFileTypes: true })) {
|
|
44890
|
-
if (!fileDirent.isFile() || !fileDirent.name.endsWith(".md")) continue;
|
|
44891
|
-
const absPath = join15(categoryDir, fileDirent.name);
|
|
44892
|
-
const raw2 = readFileSync6(absPath, "utf-8");
|
|
44893
|
-
try {
|
|
44894
|
-
const parsed = parseMarkdown(raw2);
|
|
44895
|
-
if (parsed.frontmatter.id === id) {
|
|
44896
|
-
const relPath = relative2(ctx.paths.entriesDir, absPath).replace(/\\/g, "/");
|
|
44897
|
-
return {
|
|
44898
|
-
id,
|
|
44899
|
-
title: parsed.frontmatter.title,
|
|
44900
|
-
absPath,
|
|
44901
|
-
relPath
|
|
44902
|
-
};
|
|
44903
|
-
}
|
|
44904
|
-
} catch {
|
|
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}`);
|
|
44905
45052
|
}
|
|
44906
|
-
|
|
44907
|
-
|
|
44908
|
-
|
|
44909
|
-
}
|
|
44910
|
-
|
|
44911
|
-
|
|
44912
|
-
|
|
44913
|
-
|
|
44914
|
-
|
|
44915
|
-
|
|
44916
|
-
|
|
44917
|
-
|
|
44918
|
-
|
|
44919
|
-
|
|
44920
|
-
|
|
44921
|
-
|
|
44922
|
-
void ghUser;
|
|
44923
|
-
}
|
|
44924
|
-
function ensureStagingClone(ctx, stagingDir, ghUser, sharedName, upstreamOwner) {
|
|
44925
|
-
if (!existsSync12(stagingDir)) {
|
|
44926
|
-
mkdirSync6(dirname6(stagingDir), { recursive: true });
|
|
44927
|
-
const forkUrl = `https://github.com/${ghUser}/${sharedName}.git`;
|
|
44928
|
-
run(ctx, "git", ["clone", "--depth", "30", forkUrl, stagingDir]);
|
|
44929
|
-
run(ctx, "git", ["remote", "add", "upstream", `https://github.com/${upstreamOwner}/${sharedName}.git`], {
|
|
44930
|
-
cwd: stagingDir
|
|
44931
|
-
});
|
|
44932
|
-
} else {
|
|
44933
|
-
run(ctx, "git", ["checkout", "main"], { cwd: stagingDir });
|
|
44934
|
-
run(ctx, "git", ["fetch", "upstream", "main"], { cwd: stagingDir });
|
|
44935
|
-
run(ctx, "git", ["reset", "--hard", "upstream/main"], { cwd: stagingDir });
|
|
44936
|
-
run(ctx, "git", ["push", "origin", "main", "--force-with-lease"], { cwd: stagingDir });
|
|
44937
|
-
}
|
|
44938
|
-
}
|
|
44939
|
-
function existsInUpstream(stagingDir, relFromRoot) {
|
|
44940
|
-
return existsSync12(join15(stagingDir, relFromRoot));
|
|
44941
|
-
}
|
|
44942
|
-
function run(ctx, command, args, opts = {}) {
|
|
44943
|
-
const r2 = spawnSync2(command, args, {
|
|
44944
|
-
encoding: "utf-8",
|
|
44945
|
-
cwd: opts.cwd,
|
|
44946
|
-
shell: true
|
|
44947
|
-
});
|
|
44948
|
-
if (r2.status !== 0) {
|
|
44949
|
-
const msg = (r2.stderr || r2.stdout || "unknown").trim();
|
|
44950
|
-
throw new Error(`${command} ${args.join(" ")} failed: ${msg}`);
|
|
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;
|
|
44951
45069
|
}
|
|
44952
45070
|
}
|
|
44953
|
-
function runClaim(ctx, command, args, opts = {}) {
|
|
44954
|
-
const r2 = spawnSync2(command, args, {
|
|
44955
|
-
encoding: "utf-8",
|
|
44956
|
-
cwd: opts.cwd,
|
|
44957
|
-
shell: true
|
|
44958
|
-
});
|
|
44959
|
-
return {
|
|
44960
|
-
status: r2.status,
|
|
44961
|
-
stdout: typeof r2.stdout === "string" ? r2.stdout : "",
|
|
44962
|
-
stderr: typeof r2.stderr === "string" ? r2.stderr : ""
|
|
44963
|
-
};
|
|
44964
|
-
}
|
|
44965
45071
|
|
|
44966
45072
|
// src/commands/community.ts
|
|
44967
45073
|
async function runCommunityAdd(ctx, url) {
|
|
@@ -45015,7 +45121,7 @@ function runCommunityList(ctx) {
|
|
|
45015
45121
|
|
|
45016
45122
|
// src/index.ts
|
|
45017
45123
|
var program = new Command();
|
|
45018
|
-
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.5.0");
|
|
45019
45125
|
program.command("init").description(
|
|
45020
45126
|
"Initialize ~/.caveatrc.json, ~/.caveat/, subscribe to the shared community DB, and register Claude Code integration"
|
|
45021
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) => {
|