caveat-cli 0.2.1 → 0.3.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 +366 -37
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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) {
|
|
@@ -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")) : {};
|
|
@@ -21251,7 +21253,7 @@ var stdoutLogger = {
|
|
|
21251
21253
|
};
|
|
21252
21254
|
|
|
21253
21255
|
// src/commands/init.ts
|
|
21254
|
-
import { existsSync as existsSync8, mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "node:fs";
|
|
21256
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync4, readdirSync as readdirSync4, writeFileSync as writeFileSync5 } from "node:fs";
|
|
21255
21257
|
import { join as join9 } from "node:path";
|
|
21256
21258
|
|
|
21257
21259
|
// src/claudeInstall.ts
|
|
@@ -21417,7 +21419,7 @@ var KNOWLEDGE_GITIGNORE = [
|
|
|
21417
21419
|
"community/",
|
|
21418
21420
|
""
|
|
21419
21421
|
].join("\n");
|
|
21420
|
-
function runInit(ctx, opts = { skipClaude: false, dryRun: false }) {
|
|
21422
|
+
async function runInit(ctx, opts = { skipClaude: false, skipShared: false, dryRun: false }) {
|
|
21421
21423
|
ensureUserConfig(ctx.userConfigPath);
|
|
21422
21424
|
ctx.logger.info(`user config: ${ctx.userConfigPath}`);
|
|
21423
21425
|
if (!existsSync8(ctx.paths.knowledgeRepo)) {
|
|
@@ -21433,7 +21435,17 @@ function runInit(ctx, opts = { skipClaude: false, dryRun: false }) {
|
|
|
21433
21435
|
ctx.logger.info(`.gitignore created: ${gitignorePath}`);
|
|
21434
21436
|
}
|
|
21435
21437
|
const db = openDb({ path: ctx.paths.dbPath, logger: ctx.logger });
|
|
21436
|
-
|
|
21438
|
+
try {
|
|
21439
|
+
if (!opts.skipShared && !opts.dryRun) {
|
|
21440
|
+
await subscribeSharedRepo(ctx, db);
|
|
21441
|
+
} else if (opts.skipShared) {
|
|
21442
|
+
ctx.logger.info("shared community DB subscription skipped (--skip-shared)");
|
|
21443
|
+
} else if (opts.dryRun) {
|
|
21444
|
+
ctx.logger.info(`[dry-run] would subscribe to shared community DB: ${ctx.config.sharedRepo}`);
|
|
21445
|
+
}
|
|
21446
|
+
} finally {
|
|
21447
|
+
db.close();
|
|
21448
|
+
}
|
|
21437
21449
|
ctx.logger.info(`db initialized: ${ctx.paths.dbPath}`);
|
|
21438
21450
|
if (opts.skipClaude) {
|
|
21439
21451
|
ctx.logger.info("Claude Code integration skipped (--skip-claude)");
|
|
@@ -21453,6 +21465,52 @@ function runInit(ctx, opts = { skipClaude: false, dryRun: false }) {
|
|
|
21453
21465
|
});
|
|
21454
21466
|
reportInstallResult(ctx, result, opts.dryRun);
|
|
21455
21467
|
}
|
|
21468
|
+
async function subscribeSharedRepo(ctx, db) {
|
|
21469
|
+
const url = ctx.config.sharedRepo;
|
|
21470
|
+
const validation = validateCommunityUrl(url);
|
|
21471
|
+
if (!validation.valid) {
|
|
21472
|
+
ctx.logger.warn(
|
|
21473
|
+
`sharedRepo is not a valid GitHub URL \u2014 skipping: ${validation.reason}`
|
|
21474
|
+
);
|
|
21475
|
+
return;
|
|
21476
|
+
}
|
|
21477
|
+
const handle = validation.handle;
|
|
21478
|
+
const target = join9(ctx.paths.communityDir, handle);
|
|
21479
|
+
if (!existsSync8(target)) {
|
|
21480
|
+
if (!existsSync8(ctx.paths.communityDir)) {
|
|
21481
|
+
mkdirSync4(ctx.paths.communityDir, { recursive: true });
|
|
21482
|
+
}
|
|
21483
|
+
try {
|
|
21484
|
+
await communityAdd({
|
|
21485
|
+
url,
|
|
21486
|
+
communityDir: ctx.paths.communityDir,
|
|
21487
|
+
logger: ctx.logger
|
|
21488
|
+
});
|
|
21489
|
+
ctx.logger.info(`shared community DB subscribed: ${url} \u2192 community/${handle}/`);
|
|
21490
|
+
} catch (err) {
|
|
21491
|
+
ctx.logger.warn(
|
|
21492
|
+
`shared community DB subscription failed: ${err instanceof Error ? err.message : String(err)}`
|
|
21493
|
+
);
|
|
21494
|
+
return;
|
|
21495
|
+
}
|
|
21496
|
+
} else {
|
|
21497
|
+
ctx.logger.info(`shared community DB already subscribed: community/${handle}/`);
|
|
21498
|
+
}
|
|
21499
|
+
if (existsSync8(ctx.paths.communityDir)) {
|
|
21500
|
+
rebuildAll(db);
|
|
21501
|
+
if (existsSync8(ctx.paths.entriesDir)) {
|
|
21502
|
+
scanSource({ db, source: "own", entriesRoot: ctx.paths.entriesDir });
|
|
21503
|
+
}
|
|
21504
|
+
for (const entry of readdirSync4(ctx.paths.communityDir, { withFileTypes: true })) {
|
|
21505
|
+
if (!entry.isDirectory()) continue;
|
|
21506
|
+
const source = `community/${entry.name}`;
|
|
21507
|
+
const root = join9(ctx.paths.communityDir, entry.name, "entries");
|
|
21508
|
+
if (!existsSync8(root)) continue;
|
|
21509
|
+
const result = scanSource({ db, source, entriesRoot: root });
|
|
21510
|
+
ctx.logger.info(`indexed ${source}: +${result.added}`);
|
|
21511
|
+
}
|
|
21512
|
+
}
|
|
21513
|
+
}
|
|
21456
21514
|
function runUninstall(ctx, opts) {
|
|
21457
21515
|
const cliScriptPath = process.argv[1];
|
|
21458
21516
|
if (!cliScriptPath) {
|
|
@@ -21497,7 +21555,7 @@ function reportInstallResult(ctx, result, dryRun) {
|
|
|
21497
21555
|
}
|
|
21498
21556
|
|
|
21499
21557
|
// src/commands/indexCmd.ts
|
|
21500
|
-
import { existsSync as existsSync9, readdirSync as
|
|
21558
|
+
import { existsSync as existsSync9, readdirSync as readdirSync5, mkdirSync as mkdirSync5 } from "node:fs";
|
|
21501
21559
|
import { dirname as dirname5, join as join10 } from "node:path";
|
|
21502
21560
|
function runIndex(ctx, opts) {
|
|
21503
21561
|
const dbDir = dirname5(ctx.paths.dbPath);
|
|
@@ -21515,7 +21573,7 @@ function runIndex(ctx, opts) {
|
|
|
21515
21573
|
ctx.logger.warn(`entries dir not found: ${ctx.paths.entriesDir}`);
|
|
21516
21574
|
}
|
|
21517
21575
|
if (existsSync9(ctx.paths.communityDir)) {
|
|
21518
|
-
for (const entry of
|
|
21576
|
+
for (const entry of readdirSync5(ctx.paths.communityDir, { withFileTypes: true })) {
|
|
21519
21577
|
if (!entry.isDirectory()) continue;
|
|
21520
21578
|
const source = `community/${entry.name}`;
|
|
21521
21579
|
const root = join10(ctx.paths.communityDir, entry.name, "entries");
|
|
@@ -30024,12 +30082,12 @@ function createDetailRoute(ctx) {
|
|
|
30024
30082
|
}
|
|
30025
30083
|
|
|
30026
30084
|
// ../web/dist/routes/community.js
|
|
30027
|
-
import { existsSync as existsSync10, readdirSync as
|
|
30085
|
+
import { existsSync as existsSync10, readdirSync as readdirSync6, statSync as statSync4 } from "node:fs";
|
|
30028
30086
|
import { join as join12 } from "node:path";
|
|
30029
30087
|
function listCommunity(communityDir, db) {
|
|
30030
30088
|
if (!existsSync10(communityDir)) return [];
|
|
30031
30089
|
const handles = [];
|
|
30032
|
-
for (const entry of
|
|
30090
|
+
for (const entry of readdirSync6(communityDir, { withFileTypes: true })) {
|
|
30033
30091
|
if (!entry.isDirectory()) continue;
|
|
30034
30092
|
const handlePath = join12(communityDir, entry.name);
|
|
30035
30093
|
const countRow = db.prepare("SELECT COUNT(*) AS n FROM entries WHERE source = ?").get(`community/${entry.name}`);
|
|
@@ -44648,6 +44706,263 @@ async function runHook(name) {
|
|
|
44648
44706
|
process.exit(0);
|
|
44649
44707
|
}
|
|
44650
44708
|
|
|
44709
|
+
// src/commands/pull.ts
|
|
44710
|
+
import { existsSync as existsSync11, readdirSync as readdirSync7 } from "node:fs";
|
|
44711
|
+
import { join as join14 } from "node:path";
|
|
44712
|
+
async function runPull(ctx) {
|
|
44713
|
+
if (!existsSync11(ctx.paths.communityDir)) {
|
|
44714
|
+
ctx.logger.info(
|
|
44715
|
+
"no community repos yet \u2014 did you run `caveat init`? You can also `caveat community add <url>` manually."
|
|
44716
|
+
);
|
|
44717
|
+
return;
|
|
44718
|
+
}
|
|
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
|
+
const db = openDb({ path: ctx.paths.dbPath, logger: ctx.logger });
|
|
44731
|
+
try {
|
|
44732
|
+
rebuildAll(db);
|
|
44733
|
+
if (existsSync11(ctx.paths.entriesDir)) {
|
|
44734
|
+
const ownResult = scanSource({ db, source: "own", entriesRoot: ctx.paths.entriesDir });
|
|
44735
|
+
ctx.logger.info(`own: +${ownResult.added}`);
|
|
44736
|
+
}
|
|
44737
|
+
for (const entry of readdirSync7(ctx.paths.communityDir, { withFileTypes: true })) {
|
|
44738
|
+
if (!entry.isDirectory()) continue;
|
|
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}`);
|
|
44744
|
+
}
|
|
44745
|
+
} finally {
|
|
44746
|
+
db.close();
|
|
44747
|
+
}
|
|
44748
|
+
}
|
|
44749
|
+
|
|
44750
|
+
// 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
|
+
async function runPush(ctx, opts) {
|
|
44762
|
+
const ghOk = checkGhAvailable(ctx);
|
|
44763
|
+
if (!ghOk) return;
|
|
44764
|
+
const sharedValidation = validateCommunityUrl(ctx.config.sharedRepo);
|
|
44765
|
+
if (!sharedValidation.valid) {
|
|
44766
|
+
ctx.logger.error(
|
|
44767
|
+
`sharedRepo is not a valid GitHub URL: ${ctx.config.sharedRepo} (${sharedValidation.reason})`
|
|
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
|
|
44871
|
+
});
|
|
44872
|
+
if (r2.status !== 0) {
|
|
44873
|
+
ctx.logger.error(`gh api user failed: ${(r2.stderr || r2.stdout).trim()}`);
|
|
44874
|
+
process.exitCode = 1;
|
|
44875
|
+
return void 0;
|
|
44876
|
+
}
|
|
44877
|
+
return r2.stdout.trim();
|
|
44878
|
+
}
|
|
44879
|
+
function parseOwnerRepo(url) {
|
|
44880
|
+
const m = /^https:\/\/github\.com\/([^/]+)\/([^/]+?)(\.git)?\/?$/.exec(url);
|
|
44881
|
+
if (!m) return [void 0, void 0];
|
|
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 {
|
|
44905
|
+
}
|
|
44906
|
+
}
|
|
44907
|
+
}
|
|
44908
|
+
return void 0;
|
|
44909
|
+
}
|
|
44910
|
+
function forkStagingDir(ctx) {
|
|
44911
|
+
return join15(ctx.caveatHome, "push-fork");
|
|
44912
|
+
}
|
|
44913
|
+
function ensureFork(ctx, owner, repo, ghUser) {
|
|
44914
|
+
const r2 = spawnSync2(
|
|
44915
|
+
"gh",
|
|
44916
|
+
["repo", "fork", `${owner}/${repo}`, "--clone=false", "--remote=false"],
|
|
44917
|
+
{ encoding: "utf-8", shell: true }
|
|
44918
|
+
);
|
|
44919
|
+
if (r2.status !== 0 && !(r2.stderr || "").includes("already exists")) {
|
|
44920
|
+
ctx.logger.warn(`gh repo fork stderr: ${r2.stderr.trim()}`);
|
|
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}`);
|
|
44951
|
+
}
|
|
44952
|
+
}
|
|
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
|
+
|
|
44651
44966
|
// src/commands/community.ts
|
|
44652
44967
|
async function runCommunityAdd(ctx, url) {
|
|
44653
44968
|
try {
|
|
@@ -44700,12 +45015,16 @@ function runCommunityList(ctx) {
|
|
|
44700
45015
|
|
|
44701
45016
|
// src/index.ts
|
|
44702
45017
|
var program = new Command();
|
|
44703
|
-
program.name("caveat").description("External spec gotcha knowledge base CLI").version("0.
|
|
45018
|
+
program.name("caveat").description("External spec gotcha knowledge base CLI").version("0.3.0");
|
|
44704
45019
|
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) => {
|
|
45020
|
+
"Initialize ~/.caveatrc.json, ~/.caveat/, subscribe to the shared community DB, and register Claude Code integration"
|
|
45021
|
+
).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
45022
|
const ctx = buildContext(stdoutLogger);
|
|
44708
|
-
runInit(ctx, {
|
|
45023
|
+
await runInit(ctx, {
|
|
45024
|
+
skipClaude: opts.skipClaude,
|
|
45025
|
+
skipShared: opts.skipShared,
|
|
45026
|
+
dryRun: opts.dryRun
|
|
45027
|
+
});
|
|
44709
45028
|
});
|
|
44710
45029
|
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
45030
|
const ctx = buildContext(stdoutLogger);
|
|
@@ -44738,6 +45057,16 @@ program.command("stats").description("Show aggregate stats").action(() => {
|
|
|
44738
45057
|
const ctx = buildContext(stdoutLogger);
|
|
44739
45058
|
runStats(ctx);
|
|
44740
45059
|
});
|
|
45060
|
+
program.command("pull").description(
|
|
45061
|
+
"Refresh all subscribed community repos (incl. shared DB) and re-index. Use this to receive contributions others have merged."
|
|
45062
|
+
).action(async () => {
|
|
45063
|
+
const ctx = buildContext(stdoutLogger);
|
|
45064
|
+
await runPull(ctx);
|
|
45065
|
+
});
|
|
45066
|
+
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) => {
|
|
45067
|
+
const ctx = buildContext(stdoutLogger);
|
|
45068
|
+
await runPush(ctx, { id, dryRun: opts.dryRun });
|
|
45069
|
+
});
|
|
44741
45070
|
program.command("serve").description("Start the read-only web share portal").option("--port <n>", "port number", (v) => Number(v), 4242).action((opts) => {
|
|
44742
45071
|
runServe({ port: opts.port });
|
|
44743
45072
|
});
|