@macintacos/caret 0.1.3 → 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/.claude-plugin/plugin.json +1 -1
- package/README.md +2 -2
- package/commands/demo.md +5 -4
- package/dist/cli.js +339 -49
- package/hooks/hooks.json +10 -0
- package/opencode/caret.plugin.ts +138 -20
- package/package.json +9 -1
- package/ui/dist/assets/{angular-html-Cg_qO4OQ.js → angular-html-B6NJ8Crx.js} +1 -1
- package/ui/dist/assets/{angular-ts-BmV-Wd4K.js → angular-ts-FdEJue4l.js} +1 -1
- package/ui/dist/assets/{apl-DDDJQLEy.js → apl-8jCzQ5pt.js} +1 -1
- package/ui/dist/assets/{astro-Cq94xJ2F.js → astro-D6Uv3Zd6.js} +1 -1
- package/ui/dist/assets/{blade-DZCNRrZB.js → blade-DdLBtQgs.js} +1 -1
- package/ui/dist/assets/{c-CwNkiPnO.js → c-BtgVJzRT.js} +1 -1
- package/ui/dist/assets/{cobol-CgSNuNV-.js → cobol-BOF3ihbS.js} +1 -1
- package/ui/dist/assets/{coffee-DWlIdtuG.js → coffee-Br1deUOy.js} +1 -1
- package/ui/dist/assets/{cpp-CmiYyU8y.js → cpp-BuRF7xA8.js} +1 -1
- package/ui/dist/assets/{crystal-BiyOLiea.js → crystal-C2zE7OWx.js} +1 -1
- package/ui/dist/assets/{css-DGiwxBdC.js → css-iBwOFG5O.js} +1 -1
- package/ui/dist/assets/{dist-CCQGXHXf.js → dist--moZHrIw.js} +1 -1
- package/ui/dist/assets/{dist-CbS4AEdX.js → dist-C4FPvSXr.js} +1 -1
- package/ui/dist/assets/{dist-CNR9H9hI.js → dist-CDXacocg.js} +1 -1
- package/ui/dist/assets/{dist-CfztD2mm.js → dist-CsIQFmL2.js} +1 -1
- package/ui/dist/assets/{dist-D_I8tzqw.js → dist-CtqCYfGw.js} +1 -1
- package/ui/dist/assets/{dist-BViEvwEG.js → dist-D0u8lgi2.js} +1 -1
- package/ui/dist/assets/{dist-BkSlyoLl.js → dist-D2IYj7rL.js} +1 -1
- package/ui/dist/assets/{dist-24WuufPm.js → dist-DIdA1vYB.js} +1 -1
- package/ui/dist/assets/{dist-C_IYb7Qh.js → dist-DSFoRH4B.js} +1 -1
- package/ui/dist/assets/{dist-B3mK9QMK.js → dist-DXeIzOJW.js} +1 -1
- package/ui/dist/assets/{dist-B-SHQUSo.js → dist-DhFSo_KF.js} +1 -1
- package/ui/dist/assets/{dist-Cr9g-IkY.js → dist-DheG7L5T.js} +1 -1
- package/ui/dist/assets/{dist-CKVYvLDM.js → dist-Diqv5yoL.js} +1 -1
- package/ui/dist/assets/{dist-BXtMxuI7.js → dist-Dm7ikdu5.js} +1 -1
- package/ui/dist/assets/{dist-C1Qk3ApG.js → dist-DvWv2GjQ.js} +1 -1
- package/ui/dist/assets/{dist-C9DszYV1.js → dist-jmyqwp8P.js} +1 -1
- package/ui/dist/assets/{dist-C-hQPg8K.js → dist-zkfAcw6C.js} +1 -1
- package/ui/dist/assets/{edge-CZXzS9pm.js → edge-C8C1WH_b.js} +1 -1
- package/ui/dist/assets/{elixir-BTnUmaBf.js → elixir-ChioPNrw.js} +1 -1
- package/ui/dist/assets/{elm-Bq5po3Zd.js → elm-DrMVy3GL.js} +1 -1
- package/ui/dist/assets/{erb-BOVmqWJf.js → erb-B3LvCl6V.js} +1 -1
- package/ui/dist/assets/{git-rebase-DLQM-CxK.js → git-rebase-Bv1Z90mV.js} +1 -1
- package/ui/dist/assets/{glimmer-js-DzMlcKY7.js → glimmer-js-_Cj2ZxqT.js} +1 -1
- package/ui/dist/assets/{glimmer-ts-Dz691XKF.js → glimmer-ts-eeir-ofz.js} +1 -1
- package/ui/dist/assets/{glsl-CMQZ1AkF.js → glsl-Zcj4mBmc.js} +1 -1
- package/ui/dist/assets/{graphql-DBKIWtF7.js → graphql-DcI0CImY.js} +1 -1
- package/ui/dist/assets/{hack-DZbb_fkF.js → hack-CRMO6OEY.js} +1 -1
- package/ui/dist/assets/{haml-CSjdx5hj.js → haml-vpaJ2U9N.js} +1 -1
- package/ui/dist/assets/{handlebars-Mu9SxQrl.js → handlebars-BmRnoKX5.js} +1 -1
- package/ui/dist/assets/{html-D4wtNI0H.js → html-CWopqLKh.js} +1 -1
- package/ui/dist/assets/{html-derivative-DHrkr74L.js → html-derivative-kjil5-1g.js} +1 -1
- package/ui/dist/assets/{http-DtBk1Y64.js → http-BsiD1_wo.js} +1 -1
- package/ui/dist/assets/{hurl-1qu6SWMo.js → hurl-BSOSzca8.js} +1 -1
- package/ui/dist/assets/index-C6mdkhzF.css +1 -0
- package/ui/dist/assets/index-anQLM8ZA.js +896 -0
- package/ui/dist/assets/{java-CLLSyNOn.js → java-B3dX7M_O.js} +1 -1
- package/ui/dist/assets/{javascript-B-_kYMSx.js → javascript-FM1qUZzw.js} +1 -1
- package/ui/dist/assets/{jinja-BtHF51nI.js → jinja-DVVPq-Qq.js} +1 -1
- package/ui/dist/assets/{jison-C93_a--j.js → jison-DZjXlKnT.js} +1 -1
- package/ui/dist/assets/{json-C4FWYbGR.js → json-psWWRfU7.js} +1 -1
- package/ui/dist/assets/{jsx-BdkuTVvH.js → jsx-Bv0NBlC9.js} +1 -1
- package/ui/dist/assets/{julia-BmboAp4o.js → julia-DRPZCNs5.js} +1 -1
- package/ui/dist/assets/{just-E1Wftakf.js → just-CznON7Md.js} +1 -1
- package/ui/dist/assets/{latex-DZFvvGDu.js → latex-99tFYwaR.js} +1 -1
- package/ui/dist/assets/{liquid-BUTRgRVi.js → liquid-8z3tzRYn.js} +1 -1
- package/ui/dist/assets/{lua-Dl1ciOfH.js → lua-BZoKCS17.js} +1 -1
- package/ui/dist/assets/{marko-Bh-A3HQZ.js → marko-BU9z-RZr.js} +1 -1
- package/ui/dist/assets/{mdc-C2XCGsSV.js → mdc-C38yLzkY.js} +1 -1
- package/ui/dist/assets/{nginx-igqyMv_F.js → nginx-UMVInepI.js} +1 -1
- package/ui/dist/assets/{nim-DelsCcX1.js → nim-Du4jXclQ.js} +1 -1
- package/ui/dist/assets/{perl-Cgh7mkol.js → perl-CoEbwzPf.js} +1 -1
- package/ui/dist/assets/{php-DXn-4Rf4.js → php-ubTDGUam.js} +1 -1
- package/ui/dist/assets/{pug-BNWZhj7f.js → pug-Bmsmvvm7.js} +1 -1
- package/ui/dist/assets/{qml-CHLXijrJ.js → qml-YXj-z032.js} +1 -1
- package/ui/dist/assets/{r-g949Oasl.js → r-BNkIpWO8.js} +1 -1
- package/ui/dist/assets/{razor-BUv8GTQg.js → razor-ZM--hAhl.js} +1 -1
- package/ui/dist/assets/{regexp-Dxi3LqhO.js → regexp-CHr2m78T.js} +1 -1
- package/ui/dist/assets/{rst-CMQNxthU.js → rst-Xx0XAo8H.js} +1 -1
- package/ui/dist/assets/{ruby-CDBQomnV.js → ruby-BWgyGqGH.js} +1 -1
- package/ui/dist/assets/{sas-uuqXrFx7.js → sas-CwTmTlh0.js} +1 -1
- package/ui/dist/assets/{scss-CniJSQ3f.js → scss-DNgS20lR.js} +1 -1
- package/ui/dist/assets/{shellscript-CuBMk1iS.js → shellscript-Dkezxj03.js} +1 -1
- package/ui/dist/assets/{shellsession-BzDlSDO2.js → shellsession-D6yCCjOL.js} +1 -1
- package/ui/dist/assets/{soy-e0u5_VID.js → soy-lzhm7a9Y.js} +1 -1
- package/ui/dist/assets/{sql-Cbg060Y9.js → sql-Yo8AWGPM.js} +1 -1
- package/ui/dist/assets/{stata-n_N_Lnpt.js → stata-D62k9gHq.js} +1 -1
- package/ui/dist/assets/{surrealql-Sa2566xv.js → surrealql-qp-qEg-3.js} +1 -1
- package/ui/dist/assets/{svelte-B0zgpD2X.js → svelte-DLztoC2n.js} +1 -1
- package/ui/dist/assets/{templ-BMfZts2f.js → templ-Cu6WsZcl.js} +1 -1
- package/ui/dist/assets/{tex-7PyiPoxe.js → tex-CqZU9zkk.js} +1 -1
- package/ui/dist/assets/{ts-tags-nHnZDDzD.js → ts-tags-ClqaCWh2.js} +1 -1
- package/ui/dist/assets/{tsx-BgvqLYni.js → tsx-Cj58ow-S.js} +1 -1
- package/ui/dist/assets/{twig-BqJ1gUqf.js → twig-CgaKnAdg.js} +1 -1
- package/ui/dist/assets/{typescript-C2nbCIiA.js → typescript-CKElJ_dH.js} +1 -1
- package/ui/dist/assets/{vue-B_l4_yyQ.js → vue-DtckUkhv.js} +1 -1
- package/ui/dist/assets/{vue-html-qalJD3cY.js → vue-html-Cqq7SmCi.js} +1 -1
- package/ui/dist/assets/{vue-vine-Cle_s8sp.js → vue-vine-Dp4BXiQa.js} +1 -1
- package/ui/dist/assets/{xml-CnKUcNAY.js → xml-C3UNSSfc.js} +1 -1
- package/ui/dist/assets/{xsl-D5cPGaiC.js → xsl-BL_LCJsy.js} +1 -1
- package/ui/dist/assets/{yaml-BcZKwY9y.js → yaml-CD_CHV62.js} +1 -1
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/index-DT45eblI.css +0 -1
- package/ui/dist/assets/index-Dqm3ZFVQ.js +0 -554
package/dist/cli.js
CHANGED
|
@@ -5790,7 +5790,7 @@ import { createHash } from "crypto";
|
|
|
5790
5790
|
// package.json
|
|
5791
5791
|
var package_default = {
|
|
5792
5792
|
name: "@macintacos/caret",
|
|
5793
|
-
version: "0.
|
|
5793
|
+
version: "0.3.0",
|
|
5794
5794
|
license: "MIT",
|
|
5795
5795
|
type: "module",
|
|
5796
5796
|
description: "A Claude Code plugin that replaces the terminal plan-approval prompt with a local web UI for inline review, annotation, and approval.",
|
|
@@ -5822,6 +5822,7 @@ var package_default = {
|
|
|
5822
5822
|
"@opencode-ai/plugin": "^1.16.2",
|
|
5823
5823
|
"@playwright/test": "^1.60.0",
|
|
5824
5824
|
"@sveltejs/vite-plugin-svelte": "^7.1.2",
|
|
5825
|
+
"@tailwindcss/vite": "^4.3.2",
|
|
5825
5826
|
"@types/bun": "^1.3.14",
|
|
5826
5827
|
"@types/semver": "^7.7.1",
|
|
5827
5828
|
"happy-dom": "^20.9.0",
|
|
@@ -5829,6 +5830,8 @@ var package_default = {
|
|
|
5829
5830
|
"pino-pretty": "^13.1.3",
|
|
5830
5831
|
svelte: "^5.55.9",
|
|
5831
5832
|
"svelte-check": "^4.4.8",
|
|
5833
|
+
tailwindcss: "^4.3.2",
|
|
5834
|
+
"tw-animate-css": "^1.4.0",
|
|
5832
5835
|
typescript: "^6.0.3",
|
|
5833
5836
|
vite: "^8.0.14"
|
|
5834
5837
|
},
|
|
@@ -5840,8 +5843,11 @@ var package_default = {
|
|
|
5840
5843
|
"@codemirror/state": "^6.6.0",
|
|
5841
5844
|
"@codemirror/view": "^6.43.0",
|
|
5842
5845
|
"@commander-js/extra-typings": "^15.0.0",
|
|
5846
|
+
"@internationalized/date": "^3.12.2",
|
|
5843
5847
|
"@lezer/highlight": "^1.2.3",
|
|
5844
5848
|
"@pierre/diffs": "1.2.7",
|
|
5849
|
+
"bits-ui": "^2.18.1",
|
|
5850
|
+
clsx: "^2.1.1",
|
|
5845
5851
|
commander: "^15.0.0",
|
|
5846
5852
|
marked: "^18.0.4",
|
|
5847
5853
|
pino: "^10.3.1",
|
|
@@ -5849,6 +5855,8 @@ var package_default = {
|
|
|
5849
5855
|
semver: "^7.8.1",
|
|
5850
5856
|
shiki: "^4.1.0",
|
|
5851
5857
|
"smol-toml": "1.6.1",
|
|
5858
|
+
"tailwind-merge": "^3.6.0",
|
|
5859
|
+
"tailwind-variants": "^3.2.2",
|
|
5852
5860
|
xss: "^1.0.15",
|
|
5853
5861
|
zod: "4.4.3"
|
|
5854
5862
|
}
|
|
@@ -5980,7 +5988,7 @@ function currentCommit() {
|
|
|
5980
5988
|
}
|
|
5981
5989
|
|
|
5982
5990
|
// src/commands/daemon.ts
|
|
5983
|
-
import { randomUUID
|
|
5991
|
+
import { randomUUID } from "crypto";
|
|
5984
5992
|
import { existsSync as existsSync3, unlinkSync as unlinkSync3 } from "fs";
|
|
5985
5993
|
|
|
5986
5994
|
// src/daemon-lifecycle.ts
|
|
@@ -6026,6 +6034,22 @@ async function longPoll(baseUrl, id) {
|
|
|
6026
6034
|
throw new Error(`decision long-poll failed: ${res.status}`);
|
|
6027
6035
|
return await res.json();
|
|
6028
6036
|
}
|
|
6037
|
+
async function listReviews(baseUrl) {
|
|
6038
|
+
const res = await fetch(`${baseUrl}/api/reviews`, { signal: AbortSignal.timeout(1000) });
|
|
6039
|
+
if (!res.ok)
|
|
6040
|
+
throw new Error(`GET /api/reviews failed: ${res.status}`);
|
|
6041
|
+
return await res.json();
|
|
6042
|
+
}
|
|
6043
|
+
async function resolveReview(baseUrl, id, body) {
|
|
6044
|
+
const res = await fetch(`${baseUrl}/api/reviews/${id}/resolve`, {
|
|
6045
|
+
method: "POST",
|
|
6046
|
+
headers: { "Content-Type": "application/json" },
|
|
6047
|
+
body: JSON.stringify(body),
|
|
6048
|
+
signal: AbortSignal.timeout(1000)
|
|
6049
|
+
});
|
|
6050
|
+
if (!res.ok)
|
|
6051
|
+
throw new Error(`POST /resolve failed: ${res.status}`);
|
|
6052
|
+
}
|
|
6029
6053
|
|
|
6030
6054
|
// src/log.ts
|
|
6031
6055
|
var import_pino = __toESM(require_pino(), 1);
|
|
@@ -6202,7 +6226,7 @@ function toClientReview(review) {
|
|
|
6202
6226
|
annotations: cur.annotations,
|
|
6203
6227
|
versions: review.versions,
|
|
6204
6228
|
generalCommentDraft: review.generalCommentDraft ?? "",
|
|
6205
|
-
composerScratches:
|
|
6229
|
+
composerScratches: cur.composerScratches ?? [],
|
|
6206
6230
|
createdAt: review.createdAt,
|
|
6207
6231
|
updatedAt: review.updatedAt,
|
|
6208
6232
|
decision: review.decision
|
|
@@ -20954,6 +20978,172 @@ function createDecisions(log = noopLogger) {
|
|
|
20954
20978
|
};
|
|
20955
20979
|
}
|
|
20956
20980
|
|
|
20981
|
+
// src/plan-files.ts
|
|
20982
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync6, realpathSync, statSync as statSync2 } from "fs";
|
|
20983
|
+
import { extname, isAbsolute, join as join6, relative as relative2, resolve, sep } from "path";
|
|
20984
|
+
var EXCERPT_RADIUS = 6;
|
|
20985
|
+
var EXCERPT_HEAD_LINES = 13;
|
|
20986
|
+
var MAX_EXCERPT_BYTES = 2 * 1024 * 1024;
|
|
20987
|
+
var MAX_SCAN_ENTRIES = 5000;
|
|
20988
|
+
var SKIP_DIRS = new Set(["node_modules", "dist", "build", "coverage", "out"]);
|
|
20989
|
+
var EXT_LANGUAGE = {
|
|
20990
|
+
".ts": "typescript",
|
|
20991
|
+
".mts": "typescript",
|
|
20992
|
+
".cts": "typescript",
|
|
20993
|
+
".tsx": "tsx",
|
|
20994
|
+
".js": "javascript",
|
|
20995
|
+
".mjs": "javascript",
|
|
20996
|
+
".cjs": "javascript",
|
|
20997
|
+
".jsx": "jsx",
|
|
20998
|
+
".svelte": "svelte",
|
|
20999
|
+
".vue": "vue",
|
|
21000
|
+
".json": "json",
|
|
21001
|
+
".jsonc": "jsonc",
|
|
21002
|
+
".css": "css",
|
|
21003
|
+
".scss": "scss",
|
|
21004
|
+
".less": "less",
|
|
21005
|
+
".html": "html",
|
|
21006
|
+
".htm": "html",
|
|
21007
|
+
".xml": "xml",
|
|
21008
|
+
".svg": "xml",
|
|
21009
|
+
".md": "markdown",
|
|
21010
|
+
".mdx": "mdx",
|
|
21011
|
+
".py": "python",
|
|
21012
|
+
".rb": "ruby",
|
|
21013
|
+
".rs": "rust",
|
|
21014
|
+
".go": "go",
|
|
21015
|
+
".java": "java",
|
|
21016
|
+
".kt": "kotlin",
|
|
21017
|
+
".c": "c",
|
|
21018
|
+
".h": "c",
|
|
21019
|
+
".cc": "cpp",
|
|
21020
|
+
".cpp": "cpp",
|
|
21021
|
+
".hpp": "cpp",
|
|
21022
|
+
".sh": "shellscript",
|
|
21023
|
+
".bash": "shellscript",
|
|
21024
|
+
".zsh": "shellscript",
|
|
21025
|
+
".toml": "toml",
|
|
21026
|
+
".yaml": "yaml",
|
|
21027
|
+
".yml": "yaml",
|
|
21028
|
+
".ini": "ini",
|
|
21029
|
+
".sql": "sql",
|
|
21030
|
+
".graphql": "graphql",
|
|
21031
|
+
".gql": "graphql",
|
|
21032
|
+
".php": "php",
|
|
21033
|
+
".swift": "swift",
|
|
21034
|
+
".dart": "dart"
|
|
21035
|
+
};
|
|
21036
|
+
function languageForPath(path) {
|
|
21037
|
+
return EXT_LANGUAGE[extname(path).toLowerCase()] ?? "text";
|
|
21038
|
+
}
|
|
21039
|
+
function safeRealpath(path) {
|
|
21040
|
+
try {
|
|
21041
|
+
return realpathSync(path);
|
|
21042
|
+
} catch {
|
|
21043
|
+
return null;
|
|
21044
|
+
}
|
|
21045
|
+
}
|
|
21046
|
+
function containedFile(cwdReal, abs) {
|
|
21047
|
+
const real = safeRealpath(abs);
|
|
21048
|
+
if (real === null)
|
|
21049
|
+
return null;
|
|
21050
|
+
if (real !== cwdReal && !real.startsWith(cwdReal + sep))
|
|
21051
|
+
return null;
|
|
21052
|
+
try {
|
|
21053
|
+
if (!statSync2(real).isFile())
|
|
21054
|
+
return null;
|
|
21055
|
+
} catch {
|
|
21056
|
+
return null;
|
|
21057
|
+
}
|
|
21058
|
+
return real;
|
|
21059
|
+
}
|
|
21060
|
+
function basenameSearch(cwdReal, name) {
|
|
21061
|
+
if (name === "")
|
|
21062
|
+
return null;
|
|
21063
|
+
let scanned = 0;
|
|
21064
|
+
const queue = [cwdReal];
|
|
21065
|
+
while (queue.length > 0) {
|
|
21066
|
+
const currentDir = queue.shift();
|
|
21067
|
+
let entries;
|
|
21068
|
+
try {
|
|
21069
|
+
entries = readdirSync2(currentDir, { withFileTypes: true });
|
|
21070
|
+
} catch {
|
|
21071
|
+
continue;
|
|
21072
|
+
}
|
|
21073
|
+
for (const entry of entries) {
|
|
21074
|
+
if (++scanned > MAX_SCAN_ENTRIES)
|
|
21075
|
+
return null;
|
|
21076
|
+
if (entry.isFile()) {
|
|
21077
|
+
if (entry.name === name)
|
|
21078
|
+
return join6(currentDir, entry.name);
|
|
21079
|
+
} else if (entry.isDirectory() && !SKIP_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
|
|
21080
|
+
queue.push(join6(currentDir, entry.name));
|
|
21081
|
+
}
|
|
21082
|
+
}
|
|
21083
|
+
}
|
|
21084
|
+
return null;
|
|
21085
|
+
}
|
|
21086
|
+
function resolveFileInCwd(cwd, candidate) {
|
|
21087
|
+
if (cwd === "" || !isAbsolute(cwd))
|
|
21088
|
+
return null;
|
|
21089
|
+
const cwdReal = safeRealpath(cwd);
|
|
21090
|
+
if (cwdReal === null)
|
|
21091
|
+
return null;
|
|
21092
|
+
const cleaned = candidate.trim();
|
|
21093
|
+
if (cleaned === "")
|
|
21094
|
+
return null;
|
|
21095
|
+
const direct = containedFile(cwdReal, resolve(cwdReal, cleaned));
|
|
21096
|
+
if (direct !== null)
|
|
21097
|
+
return direct;
|
|
21098
|
+
const name = cleaned.split("/").pop() ?? "";
|
|
21099
|
+
return basenameSearch(cwdReal, name);
|
|
21100
|
+
}
|
|
21101
|
+
function readFileExcerpt(cwd, candidate, line) {
|
|
21102
|
+
const abs = resolveFileInCwd(cwd, candidate);
|
|
21103
|
+
if (abs === null)
|
|
21104
|
+
return null;
|
|
21105
|
+
let size;
|
|
21106
|
+
try {
|
|
21107
|
+
size = statSync2(abs).size;
|
|
21108
|
+
} catch {
|
|
21109
|
+
return null;
|
|
21110
|
+
}
|
|
21111
|
+
if (size > MAX_EXCERPT_BYTES)
|
|
21112
|
+
return null;
|
|
21113
|
+
let content;
|
|
21114
|
+
try {
|
|
21115
|
+
content = readFileSync6(abs, "utf-8");
|
|
21116
|
+
} catch {
|
|
21117
|
+
return null;
|
|
21118
|
+
}
|
|
21119
|
+
if (content.includes("\x00"))
|
|
21120
|
+
return null;
|
|
21121
|
+
const allLines = content.split(`
|
|
21122
|
+
`);
|
|
21123
|
+
if (allLines.length > 1 && allLines[allLines.length - 1] === "")
|
|
21124
|
+
allLines.pop();
|
|
21125
|
+
const totalLines = allLines.length;
|
|
21126
|
+
let startLine;
|
|
21127
|
+
let endLine;
|
|
21128
|
+
if (line !== undefined && line >= 1) {
|
|
21129
|
+
const target = Math.min(line, totalLines);
|
|
21130
|
+
startLine = Math.max(1, target - EXCERPT_RADIUS);
|
|
21131
|
+
endLine = Math.min(totalLines, target + EXCERPT_RADIUS);
|
|
21132
|
+
} else {
|
|
21133
|
+
startLine = 1;
|
|
21134
|
+
endLine = Math.min(totalLines, EXCERPT_HEAD_LINES);
|
|
21135
|
+
}
|
|
21136
|
+
const cwdReal = safeRealpath(cwd) ?? cwd;
|
|
21137
|
+
return {
|
|
21138
|
+
path: relative2(cwdReal, abs),
|
|
21139
|
+
language: languageForPath(abs),
|
|
21140
|
+
startLine,
|
|
21141
|
+
endLine,
|
|
21142
|
+
lines: allLines.slice(startLine - 1, endLine),
|
|
21143
|
+
totalLines
|
|
21144
|
+
};
|
|
21145
|
+
}
|
|
21146
|
+
|
|
20957
21147
|
// src/prefs.ts
|
|
20958
21148
|
import { readFile as readFile2, writeFile } from "fs/promises";
|
|
20959
21149
|
import { dirname as dirname3 } from "path";
|
|
@@ -20983,17 +21173,17 @@ async function writeApproveMode(mode, file2 = prefsFile(), log = noopLogger, set
|
|
|
20983
21173
|
}
|
|
20984
21174
|
|
|
20985
21175
|
// src/reviews.ts
|
|
20986
|
-
import {
|
|
21176
|
+
import { randomBytes } from "crypto";
|
|
20987
21177
|
|
|
20988
21178
|
// src/plan-file.ts
|
|
20989
|
-
import { existsSync as existsSync2, statSync as
|
|
21179
|
+
import { existsSync as existsSync2, statSync as statSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
20990
21180
|
function writeCanonicalPlanFile(planFilePath, canonical, log) {
|
|
20991
21181
|
if (!planFilePath)
|
|
20992
21182
|
return;
|
|
20993
21183
|
try {
|
|
20994
21184
|
if (!planFilePath.endsWith(".md"))
|
|
20995
21185
|
return;
|
|
20996
|
-
if (!existsSync2(planFilePath) || !
|
|
21186
|
+
if (!existsSync2(planFilePath) || !statSync3(planFilePath).isFile())
|
|
20997
21187
|
return;
|
|
20998
21188
|
writeFileSync2(planFilePath, canonical);
|
|
20999
21189
|
} catch (err) {
|
|
@@ -27764,6 +27954,9 @@ async function formatPlanMarkdown(plan, log = noopLogger, doFormat = prettierFor
|
|
|
27764
27954
|
}
|
|
27765
27955
|
|
|
27766
27956
|
// src/reviews.ts
|
|
27957
|
+
function newReviewId() {
|
|
27958
|
+
return randomBytes(8).toString("base64url");
|
|
27959
|
+
}
|
|
27767
27960
|
function deriveTitle(plan) {
|
|
27768
27961
|
for (const line of plan.split(`
|
|
27769
27962
|
`)) {
|
|
@@ -27812,7 +28005,7 @@ async function routeIncomingPlan(input, store, log = noopLogger) {
|
|
|
27812
28005
|
expired
|
|
27813
28006
|
};
|
|
27814
28007
|
}
|
|
27815
|
-
const id =
|
|
28008
|
+
const id = newReviewId();
|
|
27816
28009
|
const planEpoch = store.epochOf(sessionId);
|
|
27817
28010
|
const review = {
|
|
27818
28011
|
id,
|
|
@@ -27921,6 +28114,8 @@ var PlanInputSchema = exports_external.object({
|
|
|
27921
28114
|
plan: exports_external.string().optional(),
|
|
27922
28115
|
planFilePath: exports_external.string().optional()
|
|
27923
28116
|
}).catch({});
|
|
28117
|
+
var MAX_FILE_REFS = 200;
|
|
28118
|
+
var FileRefsBodySchema = exports_external.object({ paths: exports_external.array(exports_external.string()).catch([]) }).catch({ paths: [] });
|
|
27924
28119
|
var BehaviorSchema = exports_external.enum(["allow", "deny"]);
|
|
27925
28120
|
var ResolveBodySchema = exports_external.object({
|
|
27926
28121
|
behavior: BehaviorSchema.catch("allow"),
|
|
@@ -27971,11 +28166,13 @@ var nullToUndefined = (v2) => v2 ?? undefined;
|
|
|
27971
28166
|
var DraftBodySchema = exports_external.object({
|
|
27972
28167
|
annotations: exports_external.array(AnnotationSchema).nullish().transform(nullToUndefined),
|
|
27973
28168
|
generalCommentDraft: exports_external.string().nullish().transform(nullToUndefined),
|
|
27974
|
-
composerScratches: exports_external.array(PersistedScratchSchema).nullish().transform(nullToUndefined).catch(undefined)
|
|
28169
|
+
composerScratches: exports_external.array(PersistedScratchSchema).nullish().transform(nullToUndefined).catch(undefined),
|
|
28170
|
+
version: exports_external.number().int().min(1).nullish().transform(nullToUndefined).catch(undefined)
|
|
27975
28171
|
}).catch({
|
|
27976
28172
|
annotations: undefined,
|
|
27977
28173
|
generalCommentDraft: undefined,
|
|
27978
|
-
composerScratches: undefined
|
|
28174
|
+
composerScratches: undefined,
|
|
28175
|
+
version: undefined
|
|
27979
28176
|
});
|
|
27980
28177
|
async function parseBody(req, schema) {
|
|
27981
28178
|
return schema.parse(await req.json().catch(() => ({})));
|
|
@@ -27998,7 +28195,7 @@ function resolveOptions(opts) {
|
|
|
27998
28195
|
log: opts.log ?? noopLogger
|
|
27999
28196
|
};
|
|
28000
28197
|
}
|
|
28001
|
-
var ID_ROUTE_RE = /^\/api\/reviews\/([^/]+)(\/decision|\/resolve|\/draft|\/expire)?$/;
|
|
28198
|
+
var ID_ROUTE_RE = /^\/api\/reviews\/([^/]+)(\/decision|\/resolve|\/draft|\/expire|\/file-refs|\/file)?$/;
|
|
28002
28199
|
function matchIdRoute(path) {
|
|
28003
28200
|
const m = path.match(ID_ROUTE_RE);
|
|
28004
28201
|
if (!m)
|
|
@@ -28012,11 +28209,11 @@ function createServer(opts) {
|
|
|
28012
28209
|
const { awaitDecision, resolveDecision, clearDecision, openDecisionCount } = createDecisions(log);
|
|
28013
28210
|
const approveModeSet = approveVariants && approveVariants.length > 0 ? { valid: approveVariants.map((v2) => v2.id), fallback: approveVariants[0]?.id ?? "default" } : { valid: ["default"], fallback: "default" };
|
|
28014
28211
|
function raceDecision(id, ms2) {
|
|
28015
|
-
return new Promise((
|
|
28016
|
-
const t = setTimeout(() =>
|
|
28212
|
+
return new Promise((resolve2) => {
|
|
28213
|
+
const t = setTimeout(() => resolve2(null), ms2);
|
|
28017
28214
|
awaitDecision(id).then((d) => {
|
|
28018
28215
|
clearTimeout(t);
|
|
28019
|
-
|
|
28216
|
+
resolve2(d);
|
|
28020
28217
|
});
|
|
28021
28218
|
});
|
|
28022
28219
|
}
|
|
@@ -28148,6 +28345,34 @@ function createServer(opts) {
|
|
|
28148
28345
|
const r = store.get(id);
|
|
28149
28346
|
return r ? Response.json(toClientReview(r)) : notFound();
|
|
28150
28347
|
}
|
|
28348
|
+
async function handleFileRefs(req, id) {
|
|
28349
|
+
const r = store.get(id);
|
|
28350
|
+
if (!r)
|
|
28351
|
+
return notFound();
|
|
28352
|
+
const { paths } = await parseBody(req, FileRefsBodySchema);
|
|
28353
|
+
const seen = new Set;
|
|
28354
|
+
const resolved = [];
|
|
28355
|
+
for (const p of paths.slice(0, MAX_FILE_REFS)) {
|
|
28356
|
+
if (seen.has(p))
|
|
28357
|
+
continue;
|
|
28358
|
+
seen.add(p);
|
|
28359
|
+
if (resolveFileInCwd(r.cwd, p) !== null)
|
|
28360
|
+
resolved.push(p);
|
|
28361
|
+
}
|
|
28362
|
+
log.debug("request", `file-refs resolved: ${resolved.length}/${seen.size}`, { reviewId: id });
|
|
28363
|
+
return Response.json({ resolved });
|
|
28364
|
+
}
|
|
28365
|
+
function handleFileExcerpt(req, id) {
|
|
28366
|
+
const r = store.get(id);
|
|
28367
|
+
if (!r)
|
|
28368
|
+
return notFound();
|
|
28369
|
+
const params = new URL(req.url).searchParams;
|
|
28370
|
+
const path = params.get("path") ?? "";
|
|
28371
|
+
const lineRaw = params.get("line");
|
|
28372
|
+
const line = lineRaw !== null && /^\d+$/.test(lineRaw) ? Number(lineRaw) : undefined;
|
|
28373
|
+
const excerpt = readFileExcerpt(r.cwd, path, line);
|
|
28374
|
+
return excerpt ? Response.json(excerpt) : notFound();
|
|
28375
|
+
}
|
|
28151
28376
|
async function handleDecision(id) {
|
|
28152
28377
|
const inMem = store.get(id);
|
|
28153
28378
|
if (inMem?.decision) {
|
|
@@ -28181,8 +28406,9 @@ function createServer(opts) {
|
|
|
28181
28406
|
if (body.generalCommentDraft != null) {
|
|
28182
28407
|
r.generalCommentDraft = body.generalCommentDraft;
|
|
28183
28408
|
}
|
|
28184
|
-
|
|
28185
|
-
|
|
28409
|
+
const cur = currentVersion(r);
|
|
28410
|
+
if (body.composerScratches != null && (body.version == null || body.version === cur.version)) {
|
|
28411
|
+
cur.composerScratches = body.composerScratches;
|
|
28186
28412
|
}
|
|
28187
28413
|
});
|
|
28188
28414
|
if (updated)
|
|
@@ -28204,7 +28430,7 @@ function createServer(opts) {
|
|
|
28204
28430
|
r.decision = decision;
|
|
28205
28431
|
r.status = decision.behavior === "allow" ? "approved" : "rejected";
|
|
28206
28432
|
r.generalCommentDraft = "";
|
|
28207
|
-
r.composerScratches = [];
|
|
28433
|
+
currentVersion(r).composerScratches = [];
|
|
28208
28434
|
});
|
|
28209
28435
|
if (decision.behavior === "allow") {
|
|
28210
28436
|
store.bumpEpoch(existing.sessionId);
|
|
@@ -28257,6 +28483,10 @@ function createServer(opts) {
|
|
|
28257
28483
|
const { id, sub } = route;
|
|
28258
28484
|
if (method === "GET" && !sub)
|
|
28259
28485
|
return handleGetReview(id);
|
|
28486
|
+
if (method === "GET" && sub === "/file")
|
|
28487
|
+
return handleFileExcerpt(req, id);
|
|
28488
|
+
if (method === "POST" && sub === "/file-refs")
|
|
28489
|
+
return handleFileRefs(req, id);
|
|
28260
28490
|
if (method === "GET" && sub === "/decision")
|
|
28261
28491
|
return handleDecision(id);
|
|
28262
28492
|
if (method === "PUT" && sub === "/draft")
|
|
@@ -28347,7 +28577,7 @@ function createServer(opts) {
|
|
|
28347
28577
|
|
|
28348
28578
|
// src/store.ts
|
|
28349
28579
|
import { readdir, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
28350
|
-
import { join as
|
|
28580
|
+
import { join as join7 } from "path";
|
|
28351
28581
|
function createStore(dir, log = noopLogger) {
|
|
28352
28582
|
const reviews = new Map;
|
|
28353
28583
|
const epochs = new Map;
|
|
@@ -28356,7 +28586,7 @@ function createStore(dir, log = noopLogger) {
|
|
|
28356
28586
|
const prev = writeChains.get(review.id) ?? Promise.resolve();
|
|
28357
28587
|
const next = prev.catch(() => {}).then(async () => {
|
|
28358
28588
|
ensureStateDir(dir);
|
|
28359
|
-
await writeFile2(
|
|
28589
|
+
await writeFile2(join7(dir, `${review.id}.json`), JSON.stringify(review, null, 2), {
|
|
28360
28590
|
mode: 384
|
|
28361
28591
|
});
|
|
28362
28592
|
log.debug("store", `review persisted: ${shortId(review.id)}`, { reviewId: review.id });
|
|
@@ -28399,14 +28629,14 @@ function createStore(dir, log = noopLogger) {
|
|
|
28399
28629
|
return;
|
|
28400
28630
|
review.status = "expired";
|
|
28401
28631
|
review.generalCommentDraft = "";
|
|
28402
|
-
review.composerScratches = [];
|
|
28632
|
+
currentVersion(review).composerScratches = [];
|
|
28403
28633
|
review.updatedAt = Math.max(Date.now(), review.updatedAt + 1);
|
|
28404
28634
|
reviews.delete(id);
|
|
28405
28635
|
await persist(review);
|
|
28406
28636
|
return review;
|
|
28407
28637
|
},
|
|
28408
28638
|
async persisted(id) {
|
|
28409
|
-
return await readJsonFile(
|
|
28639
|
+
return await readJsonFile(join7(dir, `${id}.json`)) ?? undefined;
|
|
28410
28640
|
},
|
|
28411
28641
|
size() {
|
|
28412
28642
|
return reviews.size;
|
|
@@ -28433,7 +28663,7 @@ function createStore(dir, log = noopLogger) {
|
|
|
28433
28663
|
if (!file2.endsWith(".json"))
|
|
28434
28664
|
continue;
|
|
28435
28665
|
try {
|
|
28436
|
-
const review = JSON.parse(await readFile3(
|
|
28666
|
+
const review = JSON.parse(await readFile3(join7(dir, file2), "utf-8"));
|
|
28437
28667
|
if (isUnresolved(review.status)) {
|
|
28438
28668
|
reviews.set(review.id, review);
|
|
28439
28669
|
loaded++;
|
|
@@ -28481,7 +28711,7 @@ async function runDaemon(opts) {
|
|
|
28481
28711
|
buildId: await currentBuildId(),
|
|
28482
28712
|
commit: currentCommit(),
|
|
28483
28713
|
stateDir: stateDir(),
|
|
28484
|
-
instanceId:
|
|
28714
|
+
instanceId: randomUUID().slice(0, 8),
|
|
28485
28715
|
approveVariants: adapter.approveVariants,
|
|
28486
28716
|
log
|
|
28487
28717
|
});
|
|
@@ -28523,8 +28753,8 @@ import { existsSync as existsSync4 } from "fs";
|
|
|
28523
28753
|
import { release } from "os";
|
|
28524
28754
|
|
|
28525
28755
|
// src/discovery.ts
|
|
28526
|
-
import { readdirSync as
|
|
28527
|
-
import { basename, join as
|
|
28756
|
+
import { readdirSync as readdirSync3, statSync as statSync4 } from "fs";
|
|
28757
|
+
import { basename, join as join8 } from "path";
|
|
28528
28758
|
var CARET_BIN = "caret-native";
|
|
28529
28759
|
async function safe(build) {
|
|
28530
28760
|
try {
|
|
@@ -28724,7 +28954,7 @@ function listReviewFiles() {
|
|
|
28724
28954
|
const dir = reviewsDir();
|
|
28725
28955
|
let files;
|
|
28726
28956
|
try {
|
|
28727
|
-
files =
|
|
28957
|
+
files = readdirSync3(dir);
|
|
28728
28958
|
} catch {
|
|
28729
28959
|
return [];
|
|
28730
28960
|
}
|
|
@@ -28734,7 +28964,7 @@ function listReviewFiles() {
|
|
|
28734
28964
|
continue;
|
|
28735
28965
|
if (out.length >= 5000)
|
|
28736
28966
|
break;
|
|
28737
|
-
const raw = readJsonFileSync(
|
|
28967
|
+
const raw = readJsonFileSync(join8(dir, file2));
|
|
28738
28968
|
if (raw && typeof raw.id === "string" && typeof raw.status === "string") {
|
|
28739
28969
|
out.push({ id: raw.id, status: raw.status });
|
|
28740
28970
|
}
|
|
@@ -28745,7 +28975,7 @@ var TAIL_BYTES = 256 * 1024;
|
|
|
28745
28975
|
async function logStats(path) {
|
|
28746
28976
|
let size;
|
|
28747
28977
|
try {
|
|
28748
|
-
size =
|
|
28978
|
+
size = statSync4(path).size;
|
|
28749
28979
|
} catch {
|
|
28750
28980
|
return { path, exists: false, size: 0, errors: 0, warns: 0 };
|
|
28751
28981
|
}
|
|
@@ -28832,8 +29062,8 @@ async function runDiscoverySubcommand(opts) {
|
|
|
28832
29062
|
}
|
|
28833
29063
|
|
|
28834
29064
|
// src/commands/install-opencode.ts
|
|
28835
|
-
import { existsSync as existsSync7, readFileSync as
|
|
28836
|
-
import { join as
|
|
29065
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
29066
|
+
import { join as join10 } from "path";
|
|
28837
29067
|
|
|
28838
29068
|
// src/adapters/opencode/deploy.ts
|
|
28839
29069
|
import { existsSync as existsSync5, mkdirSync as mkdirSync2, rmSync, writeFileSync as writeFileSync4 } from "fs";
|
|
@@ -28892,27 +29122,27 @@ function removeFiles(targets, opts) {
|
|
|
28892
29122
|
}
|
|
28893
29123
|
|
|
28894
29124
|
// src/adapters/opencode/packaging.ts
|
|
28895
|
-
import { existsSync as existsSync6, readFileSync as
|
|
28896
|
-
import { dirname as dirname6, join as
|
|
29125
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7, readdirSync as readdirSync4 } from "fs";
|
|
29126
|
+
import { dirname as dirname6, join as join9 } from "path";
|
|
28897
29127
|
function resolveCaretRoot() {
|
|
28898
29128
|
const candidates = [];
|
|
28899
29129
|
const script = process.argv[1];
|
|
28900
29130
|
if (script)
|
|
28901
|
-
candidates.push(
|
|
28902
|
-
candidates.push(
|
|
29131
|
+
candidates.push(join9(dirname6(script), ".."));
|
|
29132
|
+
candidates.push(join9(dirname6(process.execPath), ".."));
|
|
28903
29133
|
for (const root of candidates) {
|
|
28904
|
-
if (existsSync6(
|
|
29134
|
+
if (existsSync6(join9(root, "opencode", "caret.plugin.ts")))
|
|
28905
29135
|
return root;
|
|
28906
29136
|
}
|
|
28907
29137
|
throw new Error(`caret OpenCode packaging not found (no opencode/caret.plugin.ts under: ${candidates.join(", ") || "(none)"}). The OpenCode plugin source must ship with caret.`);
|
|
28908
29138
|
}
|
|
28909
29139
|
function loadOpencodePackaging(root = resolveCaretRoot()) {
|
|
28910
|
-
const pluginSource =
|
|
28911
|
-
const binPath =
|
|
28912
|
-
const commandsDir =
|
|
29140
|
+
const pluginSource = readFileSync7(join9(root, "opencode", "caret.plugin.ts"), "utf-8");
|
|
29141
|
+
const binPath = join9(root, "bin", "caret");
|
|
29142
|
+
const commandsDir = join9(root, "opencode", "commands");
|
|
28913
29143
|
let commands = [];
|
|
28914
29144
|
try {
|
|
28915
|
-
commands =
|
|
29145
|
+
commands = readdirSync4(commandsDir).filter((f) => f.endsWith(".md")).sort().map((f) => ({ name: f, contents: readFileSync7(join9(commandsDir, f), "utf-8") }));
|
|
28916
29146
|
} catch {
|
|
28917
29147
|
commands = [];
|
|
28918
29148
|
}
|
|
@@ -28924,7 +29154,7 @@ function runInstallOpencodeSubcommand(opts, deps = {}) {
|
|
|
28924
29154
|
const dir = deps.configDir ?? opencodeConfigDir();
|
|
28925
29155
|
const pkg = deps.packaging ?? loadOpencodePackaging();
|
|
28926
29156
|
const pluginPath = pluginFilePath(dir);
|
|
28927
|
-
const commandPaths = pkg.commands.map((c) =>
|
|
29157
|
+
const commandPaths = pkg.commands.map((c) => join10(commandDir(dir), namespacedCommandFilename(c.name)));
|
|
28928
29158
|
if (opts.uninstall) {
|
|
28929
29159
|
const result2 = removeFiles([pluginPath, ...commandPaths], { dryRun: opts.dryRun });
|
|
28930
29160
|
const manifest2 = uninstallManifest(dir, opts.dryRun);
|
|
@@ -28937,7 +29167,7 @@ function runInstallOpencodeSubcommand(opts, deps = {}) {
|
|
|
28937
29167
|
contents: stripNonDefaultExports(renderPlugin(pkg.pluginSource, { version: VERSION, binPath: pkg.binPath }))
|
|
28938
29168
|
},
|
|
28939
29169
|
...pkg.commands.map((c) => ({
|
|
28940
|
-
path:
|
|
29170
|
+
path: join10(commandDir(dir), namespacedCommandFilename(c.name)),
|
|
28941
29171
|
contents: renderPlugin(c.contents, { version: VERSION, binPath: pkg.binPath })
|
|
28942
29172
|
}))
|
|
28943
29173
|
];
|
|
@@ -28974,7 +29204,7 @@ var bunInstall = (configDir2) => {
|
|
|
28974
29204
|
};
|
|
28975
29205
|
function installManifest(dir, dryRun) {
|
|
28976
29206
|
const path = packageJsonPath(dir);
|
|
28977
|
-
const existing = existsSync7(path) ?
|
|
29207
|
+
const existing = existsSync7(path) ? readFileSync8(path, "utf-8") : null;
|
|
28978
29208
|
let next;
|
|
28979
29209
|
try {
|
|
28980
29210
|
next = addPluginDependency(existing, OPENCODE_PLUGIN_DEP, OPENCODE_PLUGIN_DEP_VERSION);
|
|
@@ -28991,7 +29221,7 @@ function uninstallManifest(dir, dryRun) {
|
|
|
28991
29221
|
const path = packageJsonPath(dir);
|
|
28992
29222
|
if (!existsSync7(path))
|
|
28993
29223
|
return [];
|
|
28994
|
-
const existing =
|
|
29224
|
+
const existing = readFileSync8(path, "utf-8");
|
|
28995
29225
|
let next;
|
|
28996
29226
|
try {
|
|
28997
29227
|
next = removePluginDependency(existing, OPENCODE_PLUGIN_DEP);
|
|
@@ -29029,6 +29259,51 @@ async function runPrewarm() {
|
|
|
29029
29259
|
process.exit(0);
|
|
29030
29260
|
}
|
|
29031
29261
|
|
|
29262
|
+
// src/reconcile.ts
|
|
29263
|
+
async function runReconcile(stdin, deps) {
|
|
29264
|
+
const ctx = {};
|
|
29265
|
+
try {
|
|
29266
|
+
const input = deps.parseHookInput(stdin);
|
|
29267
|
+
ctx.sessionId = input.sessionId;
|
|
29268
|
+
if (!input.sessionId)
|
|
29269
|
+
return;
|
|
29270
|
+
const match = (await deps.listReviews()).find((r) => r.sessionId === input.sessionId);
|
|
29271
|
+
if (!match) {
|
|
29272
|
+
logDebug("reconcile", "no pending review for session; nothing to reconcile", { ...ctx });
|
|
29273
|
+
return;
|
|
29274
|
+
}
|
|
29275
|
+
ctx.reviewId = match.id;
|
|
29276
|
+
await deps.resolveReview(match.id);
|
|
29277
|
+
logInfo("reconcile", "terminal approval reconciled", { ...ctx });
|
|
29278
|
+
} catch (err) {
|
|
29279
|
+
logDebug("reconcile", "reconcile skipped", { ...ctx, err: String(err) });
|
|
29280
|
+
}
|
|
29281
|
+
}
|
|
29282
|
+
|
|
29283
|
+
// src/commands/reconcile.ts
|
|
29284
|
+
function prodReconcileDeps(baseUrl, adapter) {
|
|
29285
|
+
return {
|
|
29286
|
+
parseHookInput: (stdin) => adapter.parseHookInput(stdin),
|
|
29287
|
+
listReviews: () => listReviews(baseUrl),
|
|
29288
|
+
resolveReview: (id) => resolveReview(baseUrl, id, { behavior: "allow" })
|
|
29289
|
+
};
|
|
29290
|
+
}
|
|
29291
|
+
async function runReconcileSubcommand() {
|
|
29292
|
+
try {
|
|
29293
|
+
const loaded = loadSettings();
|
|
29294
|
+
setLogLevel(loaded.logging.level);
|
|
29295
|
+
setRedact(loaded.logging.redact);
|
|
29296
|
+
warnInvalidEnvVars((msg) => logWarn("env", msg));
|
|
29297
|
+
const adapter = selectAdapter();
|
|
29298
|
+
const baseUrl = `http://localhost:${getPort(loaded)}`;
|
|
29299
|
+
const stdin = await Bun.stdin.text();
|
|
29300
|
+
await runReconcile(stdin, prodReconcileDeps(baseUrl, adapter));
|
|
29301
|
+
} catch (err) {
|
|
29302
|
+
logDebug("reconcile", "reconcile subcommand skipped", { err: String(err) });
|
|
29303
|
+
}
|
|
29304
|
+
process.exit(0);
|
|
29305
|
+
}
|
|
29306
|
+
|
|
29032
29307
|
// src/commands/redact.ts
|
|
29033
29308
|
function runRedactSubcommand() {
|
|
29034
29309
|
try {
|
|
@@ -30404,17 +30679,24 @@ function denyDecision(reason) {
|
|
|
30404
30679
|
class TimeoutError extends Error {
|
|
30405
30680
|
}
|
|
30406
30681
|
function withTimeout(p, ms2, message) {
|
|
30407
|
-
return new Promise((
|
|
30682
|
+
return new Promise((resolve2, reject) => {
|
|
30408
30683
|
const t = setTimeout(() => reject(new TimeoutError(message)), ms2);
|
|
30409
30684
|
p.then((v3) => {
|
|
30410
30685
|
clearTimeout(t);
|
|
30411
|
-
|
|
30686
|
+
resolve2(v3);
|
|
30412
30687
|
}, (e) => {
|
|
30413
30688
|
clearTimeout(t);
|
|
30414
30689
|
reject(e);
|
|
30415
30690
|
});
|
|
30416
30691
|
});
|
|
30417
30692
|
}
|
|
30693
|
+
async function expireAbandoned(expire, handle) {
|
|
30694
|
+
if (!handle)
|
|
30695
|
+
return;
|
|
30696
|
+
try {
|
|
30697
|
+
await expire(handle.baseUrl, handle.id);
|
|
30698
|
+
} catch {}
|
|
30699
|
+
}
|
|
30418
30700
|
async function runReview(stdin, deps) {
|
|
30419
30701
|
let step = "parse";
|
|
30420
30702
|
const ctx = {};
|
|
@@ -30434,6 +30716,7 @@ async function runReview(stdin, deps) {
|
|
|
30434
30716
|
step = "postReview";
|
|
30435
30717
|
const { id, hasLiveClient } = await deps.postReview(baseUrl, input);
|
|
30436
30718
|
ctx.reviewId = id;
|
|
30719
|
+
deps.onPosted?.(baseUrl, id);
|
|
30437
30720
|
logDebug("review", `review created: ${shortId(id)}`, { ...ctx });
|
|
30438
30721
|
const open = new URL(baseUrl);
|
|
30439
30722
|
open.hostname = VANITY_HOST;
|
|
@@ -30509,6 +30792,7 @@ async function runReviewSubcommand() {
|
|
|
30509
30792
|
warnInvalidEnvVars((msg) => logWarn("env", msg));
|
|
30510
30793
|
const adapter = selectAdapter();
|
|
30511
30794
|
let hookInput;
|
|
30795
|
+
let posted;
|
|
30512
30796
|
let responded = false;
|
|
30513
30797
|
const respond = (decision) => {
|
|
30514
30798
|
if (responded)
|
|
@@ -30517,21 +30801,26 @@ async function runReviewSubcommand() {
|
|
|
30517
30801
|
process.stdout.write(`${adapter.emitDecision(decision, hookInput)}
|
|
30518
30802
|
`);
|
|
30519
30803
|
};
|
|
30520
|
-
const denyAndExit = (reason) => {
|
|
30804
|
+
const denyAndExit = async (reason) => {
|
|
30521
30805
|
if (!responded)
|
|
30522
30806
|
logError("signal", new Error(reason));
|
|
30523
30807
|
respond({ behavior: "deny", feedback: `${reason} See ${logFile()}.`, decidedAt: Date.now() });
|
|
30808
|
+
await expireAbandoned(expireReview, posted);
|
|
30524
30809
|
process.exit(0);
|
|
30525
30810
|
};
|
|
30526
|
-
process.once("SIGINT", () => denyAndExit("caret: interrupted (SIGINT) \u2014 denying to fail safe."));
|
|
30527
|
-
process.once("SIGTERM", () => denyAndExit("caret: terminated (SIGTERM) \u2014 denying to fail safe."));
|
|
30811
|
+
process.once("SIGINT", () => void denyAndExit("caret: interrupted (SIGINT) \u2014 denying to fail safe."));
|
|
30812
|
+
process.once("SIGTERM", () => void denyAndExit("caret: terminated (SIGTERM) \u2014 denying to fail safe."));
|
|
30528
30813
|
const stdin = await Bun.stdin.text();
|
|
30529
30814
|
try {
|
|
30530
30815
|
hookInput = adapter.parseHookInput(stdin);
|
|
30531
30816
|
} catch {
|
|
30532
30817
|
hookInput = undefined;
|
|
30533
30818
|
}
|
|
30534
|
-
const
|
|
30819
|
+
const deps = prodReviewDeps(loaded, adapter);
|
|
30820
|
+
deps.onPosted = (baseUrl, id) => {
|
|
30821
|
+
posted = { baseUrl, id };
|
|
30822
|
+
};
|
|
30823
|
+
const out = await runReview(stdin, deps);
|
|
30535
30824
|
respond(out);
|
|
30536
30825
|
process.exit(0);
|
|
30537
30826
|
}
|
|
@@ -32589,10 +32878,11 @@ function runProgram(program2, onError) {
|
|
|
32589
32878
|
|
|
32590
32879
|
// src/cli.ts
|
|
32591
32880
|
function buildProgram() {
|
|
32592
|
-
const program2 = createProgram("caret", "caret hook CLI: daemon | prewarm | review | redact | discovery | install-opencode").version(VERSION);
|
|
32881
|
+
const program2 = createProgram("caret", "caret hook CLI: daemon | prewarm | review | reconcile | redact | discovery | install-opencode").version(VERSION);
|
|
32593
32882
|
program2.command("daemon").description("run the review daemon").option("--ephemeral", "bind an OS-assigned port instead of the configured one").action((opts) => runDaemon({ ephemeral: opts.ephemeral ?? false }));
|
|
32594
32883
|
program2.command("prewarm").description("warm-start the daemon").action(() => runPrewarm());
|
|
32595
32884
|
program2.command("review").description("review a plan from stdin (ExitPlanMode hook)").action(() => runReviewSubcommand());
|
|
32885
|
+
program2.command("reconcile").description("reconcile a terminal plan approval into the daemon (ExitPlanMode PostToolUse hook)").action(() => runReconcileSubcommand());
|
|
32596
32886
|
program2.command("redact").description("write redacted copies of the logs").action(() => runRedactSubcommand());
|
|
32597
32887
|
program2.command("discovery").description("print a diagnostics report").option("--json", "emit the machine-readable JSON document").action((opts) => runDiscoverySubcommand({ json: opts.json ?? false }));
|
|
32598
32888
|
program2.command("install-opencode").description("install (or with --uninstall, remove) caret's OpenCode plugin + commands").option("--uninstall", "remove caret's OpenCode files instead of installing them").option("--dry-run", "print what would change without writing").action((opts) => runInstallOpencodeSubcommand({
|