@caliber-ai/cli 0.6.0 → 0.8.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/bin.js +130 -88
- package/dist/bin.js.map +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -75,8 +75,8 @@ if (dsn) {
|
|
|
75
75
|
|
|
76
76
|
// src/cli.ts
|
|
77
77
|
import { Command } from "commander";
|
|
78
|
-
import
|
|
79
|
-
import
|
|
78
|
+
import fs20 from "fs";
|
|
79
|
+
import path17 from "path";
|
|
80
80
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
81
81
|
|
|
82
82
|
// src/commands/init.ts
|
|
@@ -935,9 +935,9 @@ async function getValidToken() {
|
|
|
935
935
|
}
|
|
936
936
|
return refreshed;
|
|
937
937
|
}
|
|
938
|
-
async function apiRequest(
|
|
938
|
+
async function apiRequest(path19, options = {}) {
|
|
939
939
|
let token = await getValidToken();
|
|
940
|
-
let resp = await fetch(`${API_URL}${
|
|
940
|
+
let resp = await fetch(`${API_URL}${path19}`, {
|
|
941
941
|
method: options.method || "GET",
|
|
942
942
|
headers: {
|
|
943
943
|
"Content-Type": "application/json",
|
|
@@ -951,7 +951,7 @@ async function apiRequest(path18, options = {}) {
|
|
|
951
951
|
throw new Error("Session expired. Run `caliber login` to re-authenticate.");
|
|
952
952
|
}
|
|
953
953
|
token = refreshed;
|
|
954
|
-
resp = await fetch(`${API_URL}${
|
|
954
|
+
resp = await fetch(`${API_URL}${path19}`, {
|
|
955
955
|
method: options.method || "GET",
|
|
956
956
|
headers: {
|
|
957
957
|
"Content-Type": "application/json",
|
|
@@ -967,9 +967,9 @@ async function apiRequest(path18, options = {}) {
|
|
|
967
967
|
const json = await resp.json();
|
|
968
968
|
return json.data;
|
|
969
969
|
}
|
|
970
|
-
async function apiStream(
|
|
970
|
+
async function apiStream(path19, body, onChunk, onComplete, onError, onStatus) {
|
|
971
971
|
let token = await getValidToken();
|
|
972
|
-
let resp = await fetch(`${API_URL}${
|
|
972
|
+
let resp = await fetch(`${API_URL}${path19}`, {
|
|
973
973
|
method: "POST",
|
|
974
974
|
headers: {
|
|
975
975
|
"Content-Type": "application/json",
|
|
@@ -983,7 +983,7 @@ async function apiStream(path18, body, onChunk, onComplete, onError, onStatus) {
|
|
|
983
983
|
throw new Error("Session expired. Run `caliber login` to re-authenticate.");
|
|
984
984
|
}
|
|
985
985
|
token = refreshed;
|
|
986
|
-
resp = await fetch(`${API_URL}${
|
|
986
|
+
resp = await fetch(`${API_URL}${path19}`, {
|
|
987
987
|
method: "POST",
|
|
988
988
|
headers: {
|
|
989
989
|
"Content-Type": "application/json",
|
|
@@ -1227,6 +1227,7 @@ import fs13 from "fs";
|
|
|
1227
1227
|
import path12 from "path";
|
|
1228
1228
|
var SETTINGS_PATH = path12.join(".claude", "settings.json");
|
|
1229
1229
|
var HOOK_COMMAND = "caliber refresh --quiet";
|
|
1230
|
+
var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
|
|
1230
1231
|
function readSettings() {
|
|
1231
1232
|
if (!fs13.existsSync(SETTINGS_PATH)) return {};
|
|
1232
1233
|
try {
|
|
@@ -1260,7 +1261,7 @@ function installHook() {
|
|
|
1260
1261
|
}
|
|
1261
1262
|
settings.hooks.SessionEnd.push({
|
|
1262
1263
|
matcher: "",
|
|
1263
|
-
hooks: [{ type: "command", command: HOOK_COMMAND }]
|
|
1264
|
+
hooks: [{ type: "command", command: HOOK_COMMAND, description: HOOK_DESCRIPTION }]
|
|
1264
1265
|
});
|
|
1265
1266
|
writeSettings(settings);
|
|
1266
1267
|
return { installed: true, alreadyInstalled: false };
|
|
@@ -1569,11 +1570,11 @@ async function initCommand(options) {
|
|
|
1569
1570
|
console.error(chalk2.red(err instanceof Error ? err.message : "Unknown error"));
|
|
1570
1571
|
throw new Error("__exit__");
|
|
1571
1572
|
}
|
|
1572
|
-
|
|
1573
|
-
if (!hookAnswer || hookAnswer.toLowerCase() !== "n") {
|
|
1573
|
+
if (targetAgent === "claude" || targetAgent === "both") {
|
|
1574
1574
|
const hookResult = installHook();
|
|
1575
1575
|
if (hookResult.installed) {
|
|
1576
|
-
console.log(` ${chalk2.green("\u2713")} Auto-refresh hook installed`);
|
|
1576
|
+
console.log(` ${chalk2.green("\u2713")} Auto-refresh hook installed \u2014 docs update on Claude Code session end`);
|
|
1577
|
+
console.log(chalk2.dim(" Run `caliber hooks remove` to disable"));
|
|
1577
1578
|
const sha = getCurrentHeadSha();
|
|
1578
1579
|
if (sha) {
|
|
1579
1580
|
writeState({ lastRefreshSha: sha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
@@ -1581,8 +1582,6 @@ async function initCommand(options) {
|
|
|
1581
1582
|
} else if (hookResult.alreadyInstalled) {
|
|
1582
1583
|
console.log(chalk2.dim(" Auto-refresh hook already installed"));
|
|
1583
1584
|
}
|
|
1584
|
-
} else {
|
|
1585
|
-
console.log(chalk2.dim(" Skipped. Run `caliber hooks install` later to enable."));
|
|
1586
1585
|
}
|
|
1587
1586
|
try {
|
|
1588
1587
|
let projectId = existingProjectId;
|
|
@@ -2545,6 +2544,8 @@ async function diffCommand(options) {
|
|
|
2545
2544
|
}
|
|
2546
2545
|
|
|
2547
2546
|
// src/commands/refresh.ts
|
|
2547
|
+
import fs19 from "fs";
|
|
2548
|
+
import path16 from "path";
|
|
2548
2549
|
import chalk11 from "chalk";
|
|
2549
2550
|
import ora9 from "ora";
|
|
2550
2551
|
|
|
@@ -2662,6 +2663,92 @@ function writeRefreshDocs(docs) {
|
|
|
2662
2663
|
function log(quiet, ...args) {
|
|
2663
2664
|
if (!quiet) console.log(...args);
|
|
2664
2665
|
}
|
|
2666
|
+
function discoverGitRepos(parentDir) {
|
|
2667
|
+
const repos = [];
|
|
2668
|
+
try {
|
|
2669
|
+
const entries = fs19.readdirSync(parentDir, { withFileTypes: true });
|
|
2670
|
+
for (const entry of entries) {
|
|
2671
|
+
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
2672
|
+
const childPath = path16.join(parentDir, entry.name);
|
|
2673
|
+
if (fs19.existsSync(path16.join(childPath, ".git"))) {
|
|
2674
|
+
repos.push(childPath);
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
} catch {
|
|
2678
|
+
}
|
|
2679
|
+
return repos.sort();
|
|
2680
|
+
}
|
|
2681
|
+
async function refreshSingleRepo(repoDir, options) {
|
|
2682
|
+
const quiet = !!options.quiet;
|
|
2683
|
+
const prefix = options.label ? `${chalk11.bold(options.label)} ` : "";
|
|
2684
|
+
const state = readState();
|
|
2685
|
+
const lastSha = state?.lastRefreshSha ?? null;
|
|
2686
|
+
const diff = collectDiff(lastSha);
|
|
2687
|
+
const currentSha = getCurrentHeadSha();
|
|
2688
|
+
if (!diff.hasChanges) {
|
|
2689
|
+
if (currentSha) {
|
|
2690
|
+
writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
2691
|
+
}
|
|
2692
|
+
log(quiet, chalk11.dim(`${prefix}No changes since last refresh.`));
|
|
2693
|
+
return;
|
|
2694
|
+
}
|
|
2695
|
+
const spinner = quiet ? null : ora9(`${prefix}Analyzing changes...`).start();
|
|
2696
|
+
const existingDocs = readExistingConfigs(repoDir);
|
|
2697
|
+
const fingerprint = collectFingerprint(repoDir);
|
|
2698
|
+
const projectContext = {
|
|
2699
|
+
languages: fingerprint.languages,
|
|
2700
|
+
frameworks: fingerprint.frameworks,
|
|
2701
|
+
packageName: fingerprint.packageName
|
|
2702
|
+
};
|
|
2703
|
+
const response = await apiRequest("/api/setups/refresh", {
|
|
2704
|
+
method: "POST",
|
|
2705
|
+
body: {
|
|
2706
|
+
diff: {
|
|
2707
|
+
committed: diff.committedDiff,
|
|
2708
|
+
staged: diff.stagedDiff,
|
|
2709
|
+
unstaged: diff.unstagedDiff,
|
|
2710
|
+
changedFiles: diff.changedFiles,
|
|
2711
|
+
summary: diff.summary
|
|
2712
|
+
},
|
|
2713
|
+
existingDocs,
|
|
2714
|
+
projectContext
|
|
2715
|
+
}
|
|
2716
|
+
});
|
|
2717
|
+
if (!response.docsUpdated || response.docsUpdated.length === 0) {
|
|
2718
|
+
spinner?.succeed(`${prefix}No doc updates needed`);
|
|
2719
|
+
if (currentSha) {
|
|
2720
|
+
writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
2721
|
+
}
|
|
2722
|
+
return;
|
|
2723
|
+
}
|
|
2724
|
+
if (options.dryRun) {
|
|
2725
|
+
spinner?.info(`${prefix}Dry run \u2014 would update:`);
|
|
2726
|
+
for (const doc of response.docsUpdated) {
|
|
2727
|
+
console.log(` ${chalk11.yellow("~")} ${doc}`);
|
|
2728
|
+
}
|
|
2729
|
+
if (response.changesSummary) {
|
|
2730
|
+
console.log(chalk11.dim(`
|
|
2731
|
+
${response.changesSummary}`));
|
|
2732
|
+
}
|
|
2733
|
+
return;
|
|
2734
|
+
}
|
|
2735
|
+
const written = writeRefreshDocs(response.updatedDocs);
|
|
2736
|
+
spinner?.succeed(`${prefix}Updated ${written.length} doc${written.length === 1 ? "" : "s"}`);
|
|
2737
|
+
for (const file of written) {
|
|
2738
|
+
log(quiet, ` ${chalk11.green("\u2713")} ${file}`);
|
|
2739
|
+
}
|
|
2740
|
+
if (response.changesSummary) {
|
|
2741
|
+
log(quiet, chalk11.dim(`
|
|
2742
|
+
${response.changesSummary}`));
|
|
2743
|
+
}
|
|
2744
|
+
if (currentSha) {
|
|
2745
|
+
writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
2746
|
+
}
|
|
2747
|
+
trackEvent("refresh_completed", {
|
|
2748
|
+
docs_updated: written.length,
|
|
2749
|
+
changed_files: diff.changedFiles.length
|
|
2750
|
+
});
|
|
2751
|
+
}
|
|
2665
2752
|
async function refreshCommand(options) {
|
|
2666
2753
|
const quiet = !!options.quiet;
|
|
2667
2754
|
try {
|
|
@@ -2671,78 +2758,30 @@ async function refreshCommand(options) {
|
|
|
2671
2758
|
console.log(chalk11.red("Not authenticated. Run `caliber login` first."));
|
|
2672
2759
|
throw new Error("__exit__");
|
|
2673
2760
|
}
|
|
2674
|
-
if (
|
|
2675
|
-
|
|
2676
|
-
console.log(chalk11.red("Not inside a git repository."));
|
|
2677
|
-
throw new Error("__exit__");
|
|
2678
|
-
}
|
|
2679
|
-
const state = readState();
|
|
2680
|
-
const lastSha = state?.lastRefreshSha ?? null;
|
|
2681
|
-
const diff = collectDiff(lastSha);
|
|
2682
|
-
const currentSha = getCurrentHeadSha();
|
|
2683
|
-
if (!diff.hasChanges) {
|
|
2684
|
-
if (currentSha) {
|
|
2685
|
-
writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
2686
|
-
}
|
|
2687
|
-
log(quiet, chalk11.dim("No changes since last refresh."));
|
|
2761
|
+
if (isGitRepo()) {
|
|
2762
|
+
await refreshSingleRepo(process.cwd(), options);
|
|
2688
2763
|
return;
|
|
2689
2764
|
}
|
|
2690
|
-
const
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
frameworks: fingerprint.frameworks,
|
|
2696
|
-
packageName: fingerprint.packageName
|
|
2697
|
-
};
|
|
2698
|
-
const response = await apiRequest("/api/setups/refresh", {
|
|
2699
|
-
method: "POST",
|
|
2700
|
-
body: {
|
|
2701
|
-
diff: {
|
|
2702
|
-
committed: diff.committedDiff,
|
|
2703
|
-
staged: diff.stagedDiff,
|
|
2704
|
-
unstaged: diff.unstagedDiff,
|
|
2705
|
-
changedFiles: diff.changedFiles,
|
|
2706
|
-
summary: diff.summary
|
|
2707
|
-
},
|
|
2708
|
-
existingDocs,
|
|
2709
|
-
projectContext
|
|
2710
|
-
}
|
|
2711
|
-
});
|
|
2712
|
-
if (!response.docsUpdated || response.docsUpdated.length === 0) {
|
|
2713
|
-
spinner?.succeed("No doc updates needed");
|
|
2714
|
-
if (currentSha) {
|
|
2715
|
-
writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
2716
|
-
}
|
|
2717
|
-
return;
|
|
2765
|
+
const repos = discoverGitRepos(process.cwd());
|
|
2766
|
+
if (repos.length === 0) {
|
|
2767
|
+
if (quiet) return;
|
|
2768
|
+
console.log(chalk11.red("Not inside a git repository and no git repos found in child directories."));
|
|
2769
|
+
throw new Error("__exit__");
|
|
2718
2770
|
}
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2771
|
+
log(quiet, chalk11.dim(`Found ${repos.length} git repo${repos.length === 1 ? "" : "s"}
|
|
2772
|
+
`));
|
|
2773
|
+
const originalDir = process.cwd();
|
|
2774
|
+
for (const repo of repos) {
|
|
2775
|
+
const repoName = path16.basename(repo);
|
|
2776
|
+
try {
|
|
2777
|
+
process.chdir(repo);
|
|
2778
|
+
await refreshSingleRepo(repo, { ...options, label: repoName });
|
|
2779
|
+
} catch (err) {
|
|
2780
|
+
if (err instanceof Error && err.message === "__exit__") continue;
|
|
2781
|
+
log(quiet, chalk11.yellow(`${repoName}: refresh failed \u2014 ${err instanceof Error ? err.message : "unknown error"}`));
|
|
2727
2782
|
}
|
|
2728
|
-
return;
|
|
2729
|
-
}
|
|
2730
|
-
const written = writeRefreshDocs(response.updatedDocs);
|
|
2731
|
-
spinner?.succeed(`Updated ${written.length} doc${written.length === 1 ? "" : "s"}`);
|
|
2732
|
-
for (const file of written) {
|
|
2733
|
-
log(quiet, ` ${chalk11.green("\u2713")} ${file}`);
|
|
2734
|
-
}
|
|
2735
|
-
if (response.changesSummary) {
|
|
2736
|
-
log(quiet, chalk11.dim(`
|
|
2737
|
-
${response.changesSummary}`));
|
|
2738
|
-
}
|
|
2739
|
-
if (currentSha) {
|
|
2740
|
-
writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
2741
2783
|
}
|
|
2742
|
-
|
|
2743
|
-
docs_updated: written.length,
|
|
2744
|
-
changed_files: diff.changedFiles.length
|
|
2745
|
-
});
|
|
2784
|
+
process.chdir(originalDir);
|
|
2746
2785
|
} catch (err) {
|
|
2747
2786
|
if (err instanceof Error && err.message === "__exit__") throw err;
|
|
2748
2787
|
if (quiet) return;
|
|
@@ -2782,9 +2821,9 @@ async function hooksStatusCommand() {
|
|
|
2782
2821
|
}
|
|
2783
2822
|
|
|
2784
2823
|
// src/cli.ts
|
|
2785
|
-
var __dirname2 =
|
|
2824
|
+
var __dirname2 = path17.dirname(fileURLToPath3(import.meta.url));
|
|
2786
2825
|
var pkg3 = JSON.parse(
|
|
2787
|
-
|
|
2826
|
+
fs20.readFileSync(path17.resolve(__dirname2, "..", "package.json"), "utf-8")
|
|
2788
2827
|
);
|
|
2789
2828
|
var program = new Command();
|
|
2790
2829
|
program.name("caliber").description("Configure your coding agent environment").version(pkg3.version);
|
|
@@ -2805,16 +2844,16 @@ hooks.command("remove").description("Remove auto-refresh SessionEnd hook").actio
|
|
|
2805
2844
|
hooks.command("status").description("Check if auto-refresh hook is installed").action(hooksStatusCommand);
|
|
2806
2845
|
|
|
2807
2846
|
// src/utils/version-check.ts
|
|
2808
|
-
import
|
|
2809
|
-
import
|
|
2847
|
+
import fs21 from "fs";
|
|
2848
|
+
import path18 from "path";
|
|
2810
2849
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
2811
2850
|
import readline3 from "readline";
|
|
2812
2851
|
import { execSync as execSync4 } from "child_process";
|
|
2813
2852
|
import chalk13 from "chalk";
|
|
2814
2853
|
import ora10 from "ora";
|
|
2815
|
-
var __dirname_vc =
|
|
2854
|
+
var __dirname_vc = path18.dirname(fileURLToPath4(import.meta.url));
|
|
2816
2855
|
var pkg4 = JSON.parse(
|
|
2817
|
-
|
|
2856
|
+
fs21.readFileSync(path18.resolve(__dirname_vc, "..", "package.json"), "utf-8")
|
|
2818
2857
|
);
|
|
2819
2858
|
function promptYesNo(question) {
|
|
2820
2859
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -2911,7 +2950,10 @@ program.parseAsync().catch((err) => {
|
|
|
2911
2950
|
const msg = err instanceof Error ? err.message : "Unexpected error";
|
|
2912
2951
|
if (msg !== "__exit__") {
|
|
2913
2952
|
console.error(msg);
|
|
2914
|
-
Sentry.captureException(err, {
|
|
2953
|
+
Sentry.captureException(err, {
|
|
2954
|
+
tags: { command: process.argv[2] || "unknown" },
|
|
2955
|
+
contexts: { cli: { args: process.argv.slice(2).join(" "), cwd: process.cwd() } }
|
|
2956
|
+
});
|
|
2915
2957
|
}
|
|
2916
2958
|
trackEvent("error_occurred", { error_type: "cli_crash", error_message: msg, command: process.argv[2] || "unknown" });
|
|
2917
2959
|
process.exitCode = 1;
|