@macintacos/caret 0.1.3 → 0.2.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/commands/demo.md +5 -4
- package/dist/cli.js +331 -49
- package/hooks/hooks.json +10 -0
- package/opencode/caret.plugin.ts +138 -20
- package/package.json +1 -1
- package/ui/dist/assets/{angular-html-Cg_qO4OQ.js → angular-html-Qbpn1MTX.js} +1 -1
- package/ui/dist/assets/{angular-ts-BmV-Wd4K.js → angular-ts-DOJ1C-2P.js} +1 -1
- package/ui/dist/assets/{apl-DDDJQLEy.js → apl-Y5-ddbi_.js} +1 -1
- package/ui/dist/assets/{astro-Cq94xJ2F.js → astro-CT31GiG6.js} +1 -1
- package/ui/dist/assets/{blade-DZCNRrZB.js → blade-DstC0Y7u.js} +1 -1
- package/ui/dist/assets/{c-CwNkiPnO.js → c-1h9wmDaJ.js} +1 -1
- package/ui/dist/assets/{cobol-CgSNuNV-.js → cobol-BJsqFKRW.js} +1 -1
- package/ui/dist/assets/{coffee-DWlIdtuG.js → coffee-lAZsrYrG.js} +1 -1
- package/ui/dist/assets/{cpp-CmiYyU8y.js → cpp-BwPvHcji.js} +1 -1
- package/ui/dist/assets/{crystal-BiyOLiea.js → crystal-C9nhMRLM.js} +1 -1
- package/ui/dist/assets/{css-DGiwxBdC.js → css-zV5baxCq.js} +1 -1
- package/ui/dist/assets/{dist-B-SHQUSo.js → dist-B2afH7tP.js} +1 -1
- package/ui/dist/assets/{dist-CCQGXHXf.js → dist-BL3PTdx3.js} +1 -1
- package/ui/dist/assets/{dist-B3mK9QMK.js → dist-Ba7qEsX8.js} +1 -1
- package/ui/dist/assets/{dist-C9DszYV1.js → dist-BcfPYvB6.js} +1 -1
- package/ui/dist/assets/{dist-24WuufPm.js → dist-C94yZ6__.js} +1 -1
- package/ui/dist/assets/{dist-C1Qk3ApG.js → dist-CBdstVS8.js} +1 -1
- package/ui/dist/assets/{dist-Cr9g-IkY.js → dist-CWdEcyiK.js} +1 -1
- package/ui/dist/assets/{dist-CbS4AEdX.js → dist-CnTQMJTD.js} +1 -1
- package/ui/dist/assets/{dist-CfztD2mm.js → dist-Cuw-4MzZ.js} +1 -1
- package/ui/dist/assets/{dist-CNR9H9hI.js → dist-D1Xb0cdg.js} +1 -1
- package/ui/dist/assets/{dist-C-hQPg8K.js → dist-D5aJA_uS.js} +1 -1
- package/ui/dist/assets/{dist-D_I8tzqw.js → dist-DGObAN16.js} +1 -1
- package/ui/dist/assets/{dist-BXtMxuI7.js → dist-DTwEdaPW.js} +1 -1
- package/ui/dist/assets/{dist-BViEvwEG.js → dist-EeYINWCK.js} +1 -1
- package/ui/dist/assets/{dist-CKVYvLDM.js → dist-ZPJnos5t.js} +1 -1
- package/ui/dist/assets/{dist-BkSlyoLl.js → dist-ap3lqXWD.js} +1 -1
- package/ui/dist/assets/{dist-C_IYb7Qh.js → dist-eJbkdDpi.js} +1 -1
- package/ui/dist/assets/{edge-CZXzS9pm.js → edge-DtPr1ved.js} +1 -1
- package/ui/dist/assets/{elixir-BTnUmaBf.js → elixir-DxdQpZmO.js} +1 -1
- package/ui/dist/assets/{elm-Bq5po3Zd.js → elm-PyFaNcFd.js} +1 -1
- package/ui/dist/assets/{erb-BOVmqWJf.js → erb-4d0ztAHA.js} +1 -1
- package/ui/dist/assets/{git-rebase-DLQM-CxK.js → git-rebase-CF959Xij.js} +1 -1
- package/ui/dist/assets/{glimmer-js-DzMlcKY7.js → glimmer-js-DOoPHQhz.js} +1 -1
- package/ui/dist/assets/{glimmer-ts-Dz691XKF.js → glimmer-ts-B-wO1BbM.js} +1 -1
- package/ui/dist/assets/{glsl-CMQZ1AkF.js → glsl-DhUIrRGp.js} +1 -1
- package/ui/dist/assets/{graphql-DBKIWtF7.js → graphql-DHm0YGdI.js} +1 -1
- package/ui/dist/assets/{hack-DZbb_fkF.js → hack-KmeknXZp.js} +1 -1
- package/ui/dist/assets/{haml-CSjdx5hj.js → haml-CqH8UVt6.js} +1 -1
- package/ui/dist/assets/{handlebars-Mu9SxQrl.js → handlebars-xVW6rqZc.js} +1 -1
- package/ui/dist/assets/{html-D4wtNI0H.js → html-Dm0ku22r.js} +1 -1
- package/ui/dist/assets/{html-derivative-DHrkr74L.js → html-derivative-ByxJ1rMF.js} +1 -1
- package/ui/dist/assets/{http-DtBk1Y64.js → http--f7CCugj.js} +1 -1
- package/ui/dist/assets/{hurl-1qu6SWMo.js → hurl-hbsn4uwO.js} +1 -1
- package/ui/dist/assets/index-2kPU99Wr.css +1 -0
- package/ui/dist/assets/{index-Dqm3ZFVQ.js → index-CIXjaWlx.js} +285 -95
- package/ui/dist/assets/{java-CLLSyNOn.js → java-DIuHCmu6.js} +1 -1
- package/ui/dist/assets/{javascript-B-_kYMSx.js → javascript-gLvwRdLL.js} +1 -1
- package/ui/dist/assets/{jinja-BtHF51nI.js → jinja-CllNBGTW.js} +1 -1
- package/ui/dist/assets/{jison-C93_a--j.js → jison--nGCTsNX.js} +1 -1
- package/ui/dist/assets/{json-C4FWYbGR.js → json-C56ryB74.js} +1 -1
- package/ui/dist/assets/{jsx-BdkuTVvH.js → jsx-4Pk1mTn6.js} +1 -1
- package/ui/dist/assets/{julia-BmboAp4o.js → julia-C0bepdRl.js} +1 -1
- package/ui/dist/assets/{just-E1Wftakf.js → just-DZULmPRj.js} +1 -1
- package/ui/dist/assets/{latex-DZFvvGDu.js → latex-D16Jgu4V.js} +1 -1
- package/ui/dist/assets/{liquid-BUTRgRVi.js → liquid-C8coUMRS.js} +1 -1
- package/ui/dist/assets/{lua-Dl1ciOfH.js → lua-Cf5MxbTu.js} +1 -1
- package/ui/dist/assets/{marko-Bh-A3HQZ.js → marko-Cb70KBLA.js} +1 -1
- package/ui/dist/assets/{mdc-C2XCGsSV.js → mdc-C7MhKLIm.js} +1 -1
- package/ui/dist/assets/{nginx-igqyMv_F.js → nginx-BXtCcpzS.js} +1 -1
- package/ui/dist/assets/{nim-DelsCcX1.js → nim-DGCttsC9.js} +1 -1
- package/ui/dist/assets/{perl-Cgh7mkol.js → perl-QjSbxj8-.js} +1 -1
- package/ui/dist/assets/{php-DXn-4Rf4.js → php-C3F5od73.js} +1 -1
- package/ui/dist/assets/{pug-BNWZhj7f.js → pug-C5_Ubyev.js} +1 -1
- package/ui/dist/assets/{qml-CHLXijrJ.js → qml-ClOBZo_4.js} +1 -1
- package/ui/dist/assets/{r-g949Oasl.js → r-CRs34ME2.js} +1 -1
- package/ui/dist/assets/{razor-BUv8GTQg.js → razor-Da5e4iwK.js} +1 -1
- package/ui/dist/assets/{regexp-Dxi3LqhO.js → regexp-CIgnY5KL.js} +1 -1
- package/ui/dist/assets/{rst-CMQNxthU.js → rst-B8IBFGFC.js} +1 -1
- package/ui/dist/assets/{ruby-CDBQomnV.js → ruby-DCWmsWCN.js} +1 -1
- package/ui/dist/assets/{sas-uuqXrFx7.js → sas-C6G9MYN0.js} +1 -1
- package/ui/dist/assets/{scss-CniJSQ3f.js → scss-CNd_k2F5.js} +1 -1
- package/ui/dist/assets/{shellscript-CuBMk1iS.js → shellscript-CwXf9FTq.js} +1 -1
- package/ui/dist/assets/{shellsession-BzDlSDO2.js → shellsession-BJzcxmip.js} +1 -1
- package/ui/dist/assets/{soy-e0u5_VID.js → soy-KzIeqqPW.js} +1 -1
- package/ui/dist/assets/{sql-Cbg060Y9.js → sql-DBDZ7dWg.js} +1 -1
- package/ui/dist/assets/{stata-n_N_Lnpt.js → stata-SsEZ25vz.js} +1 -1
- package/ui/dist/assets/{surrealql-Sa2566xv.js → surrealql-C7tSrOv6.js} +1 -1
- package/ui/dist/assets/{svelte-B0zgpD2X.js → svelte-BHETRygr.js} +1 -1
- package/ui/dist/assets/{templ-BMfZts2f.js → templ-mCQ9V1Fb.js} +1 -1
- package/ui/dist/assets/{tex-7PyiPoxe.js → tex-DgKGHlQ1.js} +1 -1
- package/ui/dist/assets/{ts-tags-nHnZDDzD.js → ts-tags-D-31aQRg.js} +1 -1
- package/ui/dist/assets/{tsx-BgvqLYni.js → tsx-CKRZAYYe.js} +1 -1
- package/ui/dist/assets/{twig-BqJ1gUqf.js → twig-COT1S6WR.js} +1 -1
- package/ui/dist/assets/{typescript-C2nbCIiA.js → typescript-IBoewa4A.js} +1 -1
- package/ui/dist/assets/{vue-B_l4_yyQ.js → vue-44M0YEZN.js} +1 -1
- package/ui/dist/assets/{vue-html-qalJD3cY.js → vue-html-C1gtv8Ck.js} +1 -1
- package/ui/dist/assets/{vue-vine-Cle_s8sp.js → vue-vine-DEW0Tzyn.js} +1 -1
- package/ui/dist/assets/{xml-CnKUcNAY.js → xml-CSnYabNH.js} +1 -1
- package/ui/dist/assets/{xsl-D5cPGaiC.js → xsl-DrkZvWNe.js} +1 -1
- package/ui/dist/assets/{yaml-BcZKwY9y.js → yaml-D6QWa51n.js} +1 -1
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/index-DT45eblI.css +0 -1
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.2.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.",
|
|
@@ -5980,7 +5980,7 @@ function currentCommit() {
|
|
|
5980
5980
|
}
|
|
5981
5981
|
|
|
5982
5982
|
// src/commands/daemon.ts
|
|
5983
|
-
import { randomUUID
|
|
5983
|
+
import { randomUUID } from "crypto";
|
|
5984
5984
|
import { existsSync as existsSync3, unlinkSync as unlinkSync3 } from "fs";
|
|
5985
5985
|
|
|
5986
5986
|
// src/daemon-lifecycle.ts
|
|
@@ -6026,6 +6026,22 @@ async function longPoll(baseUrl, id) {
|
|
|
6026
6026
|
throw new Error(`decision long-poll failed: ${res.status}`);
|
|
6027
6027
|
return await res.json();
|
|
6028
6028
|
}
|
|
6029
|
+
async function listReviews(baseUrl) {
|
|
6030
|
+
const res = await fetch(`${baseUrl}/api/reviews`, { signal: AbortSignal.timeout(1000) });
|
|
6031
|
+
if (!res.ok)
|
|
6032
|
+
throw new Error(`GET /api/reviews failed: ${res.status}`);
|
|
6033
|
+
return await res.json();
|
|
6034
|
+
}
|
|
6035
|
+
async function resolveReview(baseUrl, id, body) {
|
|
6036
|
+
const res = await fetch(`${baseUrl}/api/reviews/${id}/resolve`, {
|
|
6037
|
+
method: "POST",
|
|
6038
|
+
headers: { "Content-Type": "application/json" },
|
|
6039
|
+
body: JSON.stringify(body),
|
|
6040
|
+
signal: AbortSignal.timeout(1000)
|
|
6041
|
+
});
|
|
6042
|
+
if (!res.ok)
|
|
6043
|
+
throw new Error(`POST /resolve failed: ${res.status}`);
|
|
6044
|
+
}
|
|
6029
6045
|
|
|
6030
6046
|
// src/log.ts
|
|
6031
6047
|
var import_pino = __toESM(require_pino(), 1);
|
|
@@ -6202,7 +6218,7 @@ function toClientReview(review) {
|
|
|
6202
6218
|
annotations: cur.annotations,
|
|
6203
6219
|
versions: review.versions,
|
|
6204
6220
|
generalCommentDraft: review.generalCommentDraft ?? "",
|
|
6205
|
-
composerScratches:
|
|
6221
|
+
composerScratches: cur.composerScratches ?? [],
|
|
6206
6222
|
createdAt: review.createdAt,
|
|
6207
6223
|
updatedAt: review.updatedAt,
|
|
6208
6224
|
decision: review.decision
|
|
@@ -20954,6 +20970,172 @@ function createDecisions(log = noopLogger) {
|
|
|
20954
20970
|
};
|
|
20955
20971
|
}
|
|
20956
20972
|
|
|
20973
|
+
// src/plan-files.ts
|
|
20974
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync6, realpathSync, statSync as statSync2 } from "fs";
|
|
20975
|
+
import { extname, isAbsolute, join as join6, relative as relative2, resolve, sep } from "path";
|
|
20976
|
+
var EXCERPT_RADIUS = 6;
|
|
20977
|
+
var EXCERPT_HEAD_LINES = 13;
|
|
20978
|
+
var MAX_EXCERPT_BYTES = 2 * 1024 * 1024;
|
|
20979
|
+
var MAX_SCAN_ENTRIES = 5000;
|
|
20980
|
+
var SKIP_DIRS = new Set(["node_modules", "dist", "build", "coverage", "out"]);
|
|
20981
|
+
var EXT_LANGUAGE = {
|
|
20982
|
+
".ts": "typescript",
|
|
20983
|
+
".mts": "typescript",
|
|
20984
|
+
".cts": "typescript",
|
|
20985
|
+
".tsx": "tsx",
|
|
20986
|
+
".js": "javascript",
|
|
20987
|
+
".mjs": "javascript",
|
|
20988
|
+
".cjs": "javascript",
|
|
20989
|
+
".jsx": "jsx",
|
|
20990
|
+
".svelte": "svelte",
|
|
20991
|
+
".vue": "vue",
|
|
20992
|
+
".json": "json",
|
|
20993
|
+
".jsonc": "jsonc",
|
|
20994
|
+
".css": "css",
|
|
20995
|
+
".scss": "scss",
|
|
20996
|
+
".less": "less",
|
|
20997
|
+
".html": "html",
|
|
20998
|
+
".htm": "html",
|
|
20999
|
+
".xml": "xml",
|
|
21000
|
+
".svg": "xml",
|
|
21001
|
+
".md": "markdown",
|
|
21002
|
+
".mdx": "mdx",
|
|
21003
|
+
".py": "python",
|
|
21004
|
+
".rb": "ruby",
|
|
21005
|
+
".rs": "rust",
|
|
21006
|
+
".go": "go",
|
|
21007
|
+
".java": "java",
|
|
21008
|
+
".kt": "kotlin",
|
|
21009
|
+
".c": "c",
|
|
21010
|
+
".h": "c",
|
|
21011
|
+
".cc": "cpp",
|
|
21012
|
+
".cpp": "cpp",
|
|
21013
|
+
".hpp": "cpp",
|
|
21014
|
+
".sh": "shellscript",
|
|
21015
|
+
".bash": "shellscript",
|
|
21016
|
+
".zsh": "shellscript",
|
|
21017
|
+
".toml": "toml",
|
|
21018
|
+
".yaml": "yaml",
|
|
21019
|
+
".yml": "yaml",
|
|
21020
|
+
".ini": "ini",
|
|
21021
|
+
".sql": "sql",
|
|
21022
|
+
".graphql": "graphql",
|
|
21023
|
+
".gql": "graphql",
|
|
21024
|
+
".php": "php",
|
|
21025
|
+
".swift": "swift",
|
|
21026
|
+
".dart": "dart"
|
|
21027
|
+
};
|
|
21028
|
+
function languageForPath(path) {
|
|
21029
|
+
return EXT_LANGUAGE[extname(path).toLowerCase()] ?? "text";
|
|
21030
|
+
}
|
|
21031
|
+
function safeRealpath(path) {
|
|
21032
|
+
try {
|
|
21033
|
+
return realpathSync(path);
|
|
21034
|
+
} catch {
|
|
21035
|
+
return null;
|
|
21036
|
+
}
|
|
21037
|
+
}
|
|
21038
|
+
function containedFile(cwdReal, abs) {
|
|
21039
|
+
const real = safeRealpath(abs);
|
|
21040
|
+
if (real === null)
|
|
21041
|
+
return null;
|
|
21042
|
+
if (real !== cwdReal && !real.startsWith(cwdReal + sep))
|
|
21043
|
+
return null;
|
|
21044
|
+
try {
|
|
21045
|
+
if (!statSync2(real).isFile())
|
|
21046
|
+
return null;
|
|
21047
|
+
} catch {
|
|
21048
|
+
return null;
|
|
21049
|
+
}
|
|
21050
|
+
return real;
|
|
21051
|
+
}
|
|
21052
|
+
function basenameSearch(cwdReal, name) {
|
|
21053
|
+
if (name === "")
|
|
21054
|
+
return null;
|
|
21055
|
+
let scanned = 0;
|
|
21056
|
+
const queue = [cwdReal];
|
|
21057
|
+
while (queue.length > 0) {
|
|
21058
|
+
const currentDir = queue.shift();
|
|
21059
|
+
let entries;
|
|
21060
|
+
try {
|
|
21061
|
+
entries = readdirSync2(currentDir, { withFileTypes: true });
|
|
21062
|
+
} catch {
|
|
21063
|
+
continue;
|
|
21064
|
+
}
|
|
21065
|
+
for (const entry of entries) {
|
|
21066
|
+
if (++scanned > MAX_SCAN_ENTRIES)
|
|
21067
|
+
return null;
|
|
21068
|
+
if (entry.isFile()) {
|
|
21069
|
+
if (entry.name === name)
|
|
21070
|
+
return join6(currentDir, entry.name);
|
|
21071
|
+
} else if (entry.isDirectory() && !SKIP_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
|
|
21072
|
+
queue.push(join6(currentDir, entry.name));
|
|
21073
|
+
}
|
|
21074
|
+
}
|
|
21075
|
+
}
|
|
21076
|
+
return null;
|
|
21077
|
+
}
|
|
21078
|
+
function resolveFileInCwd(cwd, candidate) {
|
|
21079
|
+
if (cwd === "" || !isAbsolute(cwd))
|
|
21080
|
+
return null;
|
|
21081
|
+
const cwdReal = safeRealpath(cwd);
|
|
21082
|
+
if (cwdReal === null)
|
|
21083
|
+
return null;
|
|
21084
|
+
const cleaned = candidate.trim();
|
|
21085
|
+
if (cleaned === "")
|
|
21086
|
+
return null;
|
|
21087
|
+
const direct = containedFile(cwdReal, resolve(cwdReal, cleaned));
|
|
21088
|
+
if (direct !== null)
|
|
21089
|
+
return direct;
|
|
21090
|
+
const name = cleaned.split("/").pop() ?? "";
|
|
21091
|
+
return basenameSearch(cwdReal, name);
|
|
21092
|
+
}
|
|
21093
|
+
function readFileExcerpt(cwd, candidate, line) {
|
|
21094
|
+
const abs = resolveFileInCwd(cwd, candidate);
|
|
21095
|
+
if (abs === null)
|
|
21096
|
+
return null;
|
|
21097
|
+
let size;
|
|
21098
|
+
try {
|
|
21099
|
+
size = statSync2(abs).size;
|
|
21100
|
+
} catch {
|
|
21101
|
+
return null;
|
|
21102
|
+
}
|
|
21103
|
+
if (size > MAX_EXCERPT_BYTES)
|
|
21104
|
+
return null;
|
|
21105
|
+
let content;
|
|
21106
|
+
try {
|
|
21107
|
+
content = readFileSync6(abs, "utf-8");
|
|
21108
|
+
} catch {
|
|
21109
|
+
return null;
|
|
21110
|
+
}
|
|
21111
|
+
if (content.includes("\x00"))
|
|
21112
|
+
return null;
|
|
21113
|
+
const allLines = content.split(`
|
|
21114
|
+
`);
|
|
21115
|
+
if (allLines.length > 1 && allLines[allLines.length - 1] === "")
|
|
21116
|
+
allLines.pop();
|
|
21117
|
+
const totalLines = allLines.length;
|
|
21118
|
+
let startLine;
|
|
21119
|
+
let endLine;
|
|
21120
|
+
if (line !== undefined && line >= 1) {
|
|
21121
|
+
const target = Math.min(line, totalLines);
|
|
21122
|
+
startLine = Math.max(1, target - EXCERPT_RADIUS);
|
|
21123
|
+
endLine = Math.min(totalLines, target + EXCERPT_RADIUS);
|
|
21124
|
+
} else {
|
|
21125
|
+
startLine = 1;
|
|
21126
|
+
endLine = Math.min(totalLines, EXCERPT_HEAD_LINES);
|
|
21127
|
+
}
|
|
21128
|
+
const cwdReal = safeRealpath(cwd) ?? cwd;
|
|
21129
|
+
return {
|
|
21130
|
+
path: relative2(cwdReal, abs),
|
|
21131
|
+
language: languageForPath(abs),
|
|
21132
|
+
startLine,
|
|
21133
|
+
endLine,
|
|
21134
|
+
lines: allLines.slice(startLine - 1, endLine),
|
|
21135
|
+
totalLines
|
|
21136
|
+
};
|
|
21137
|
+
}
|
|
21138
|
+
|
|
20957
21139
|
// src/prefs.ts
|
|
20958
21140
|
import { readFile as readFile2, writeFile } from "fs/promises";
|
|
20959
21141
|
import { dirname as dirname3 } from "path";
|
|
@@ -20983,17 +21165,17 @@ async function writeApproveMode(mode, file2 = prefsFile(), log = noopLogger, set
|
|
|
20983
21165
|
}
|
|
20984
21166
|
|
|
20985
21167
|
// src/reviews.ts
|
|
20986
|
-
import {
|
|
21168
|
+
import { randomBytes } from "crypto";
|
|
20987
21169
|
|
|
20988
21170
|
// src/plan-file.ts
|
|
20989
|
-
import { existsSync as existsSync2, statSync as
|
|
21171
|
+
import { existsSync as existsSync2, statSync as statSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
20990
21172
|
function writeCanonicalPlanFile(planFilePath, canonical, log) {
|
|
20991
21173
|
if (!planFilePath)
|
|
20992
21174
|
return;
|
|
20993
21175
|
try {
|
|
20994
21176
|
if (!planFilePath.endsWith(".md"))
|
|
20995
21177
|
return;
|
|
20996
|
-
if (!existsSync2(planFilePath) || !
|
|
21178
|
+
if (!existsSync2(planFilePath) || !statSync3(planFilePath).isFile())
|
|
20997
21179
|
return;
|
|
20998
21180
|
writeFileSync2(planFilePath, canonical);
|
|
20999
21181
|
} catch (err) {
|
|
@@ -27764,6 +27946,9 @@ async function formatPlanMarkdown(plan, log = noopLogger, doFormat = prettierFor
|
|
|
27764
27946
|
}
|
|
27765
27947
|
|
|
27766
27948
|
// src/reviews.ts
|
|
27949
|
+
function newReviewId() {
|
|
27950
|
+
return randomBytes(8).toString("base64url");
|
|
27951
|
+
}
|
|
27767
27952
|
function deriveTitle(plan) {
|
|
27768
27953
|
for (const line of plan.split(`
|
|
27769
27954
|
`)) {
|
|
@@ -27812,7 +27997,7 @@ async function routeIncomingPlan(input, store, log = noopLogger) {
|
|
|
27812
27997
|
expired
|
|
27813
27998
|
};
|
|
27814
27999
|
}
|
|
27815
|
-
const id =
|
|
28000
|
+
const id = newReviewId();
|
|
27816
28001
|
const planEpoch = store.epochOf(sessionId);
|
|
27817
28002
|
const review = {
|
|
27818
28003
|
id,
|
|
@@ -27921,6 +28106,8 @@ var PlanInputSchema = exports_external.object({
|
|
|
27921
28106
|
plan: exports_external.string().optional(),
|
|
27922
28107
|
planFilePath: exports_external.string().optional()
|
|
27923
28108
|
}).catch({});
|
|
28109
|
+
var MAX_FILE_REFS = 200;
|
|
28110
|
+
var FileRefsBodySchema = exports_external.object({ paths: exports_external.array(exports_external.string()).catch([]) }).catch({ paths: [] });
|
|
27924
28111
|
var BehaviorSchema = exports_external.enum(["allow", "deny"]);
|
|
27925
28112
|
var ResolveBodySchema = exports_external.object({
|
|
27926
28113
|
behavior: BehaviorSchema.catch("allow"),
|
|
@@ -27971,11 +28158,13 @@ var nullToUndefined = (v2) => v2 ?? undefined;
|
|
|
27971
28158
|
var DraftBodySchema = exports_external.object({
|
|
27972
28159
|
annotations: exports_external.array(AnnotationSchema).nullish().transform(nullToUndefined),
|
|
27973
28160
|
generalCommentDraft: exports_external.string().nullish().transform(nullToUndefined),
|
|
27974
|
-
composerScratches: exports_external.array(PersistedScratchSchema).nullish().transform(nullToUndefined).catch(undefined)
|
|
28161
|
+
composerScratches: exports_external.array(PersistedScratchSchema).nullish().transform(nullToUndefined).catch(undefined),
|
|
28162
|
+
version: exports_external.number().int().min(1).nullish().transform(nullToUndefined).catch(undefined)
|
|
27975
28163
|
}).catch({
|
|
27976
28164
|
annotations: undefined,
|
|
27977
28165
|
generalCommentDraft: undefined,
|
|
27978
|
-
composerScratches: undefined
|
|
28166
|
+
composerScratches: undefined,
|
|
28167
|
+
version: undefined
|
|
27979
28168
|
});
|
|
27980
28169
|
async function parseBody(req, schema) {
|
|
27981
28170
|
return schema.parse(await req.json().catch(() => ({})));
|
|
@@ -27998,7 +28187,7 @@ function resolveOptions(opts) {
|
|
|
27998
28187
|
log: opts.log ?? noopLogger
|
|
27999
28188
|
};
|
|
28000
28189
|
}
|
|
28001
|
-
var ID_ROUTE_RE = /^\/api\/reviews\/([^/]+)(\/decision|\/resolve|\/draft|\/expire)?$/;
|
|
28190
|
+
var ID_ROUTE_RE = /^\/api\/reviews\/([^/]+)(\/decision|\/resolve|\/draft|\/expire|\/file-refs|\/file)?$/;
|
|
28002
28191
|
function matchIdRoute(path) {
|
|
28003
28192
|
const m = path.match(ID_ROUTE_RE);
|
|
28004
28193
|
if (!m)
|
|
@@ -28012,11 +28201,11 @@ function createServer(opts) {
|
|
|
28012
28201
|
const { awaitDecision, resolveDecision, clearDecision, openDecisionCount } = createDecisions(log);
|
|
28013
28202
|
const approveModeSet = approveVariants && approveVariants.length > 0 ? { valid: approveVariants.map((v2) => v2.id), fallback: approveVariants[0]?.id ?? "default" } : { valid: ["default"], fallback: "default" };
|
|
28014
28203
|
function raceDecision(id, ms2) {
|
|
28015
|
-
return new Promise((
|
|
28016
|
-
const t = setTimeout(() =>
|
|
28204
|
+
return new Promise((resolve2) => {
|
|
28205
|
+
const t = setTimeout(() => resolve2(null), ms2);
|
|
28017
28206
|
awaitDecision(id).then((d) => {
|
|
28018
28207
|
clearTimeout(t);
|
|
28019
|
-
|
|
28208
|
+
resolve2(d);
|
|
28020
28209
|
});
|
|
28021
28210
|
});
|
|
28022
28211
|
}
|
|
@@ -28148,6 +28337,34 @@ function createServer(opts) {
|
|
|
28148
28337
|
const r = store.get(id);
|
|
28149
28338
|
return r ? Response.json(toClientReview(r)) : notFound();
|
|
28150
28339
|
}
|
|
28340
|
+
async function handleFileRefs(req, id) {
|
|
28341
|
+
const r = store.get(id);
|
|
28342
|
+
if (!r)
|
|
28343
|
+
return notFound();
|
|
28344
|
+
const { paths } = await parseBody(req, FileRefsBodySchema);
|
|
28345
|
+
const seen = new Set;
|
|
28346
|
+
const resolved = [];
|
|
28347
|
+
for (const p of paths.slice(0, MAX_FILE_REFS)) {
|
|
28348
|
+
if (seen.has(p))
|
|
28349
|
+
continue;
|
|
28350
|
+
seen.add(p);
|
|
28351
|
+
if (resolveFileInCwd(r.cwd, p) !== null)
|
|
28352
|
+
resolved.push(p);
|
|
28353
|
+
}
|
|
28354
|
+
log.debug("request", `file-refs resolved: ${resolved.length}/${seen.size}`, { reviewId: id });
|
|
28355
|
+
return Response.json({ resolved });
|
|
28356
|
+
}
|
|
28357
|
+
function handleFileExcerpt(req, id) {
|
|
28358
|
+
const r = store.get(id);
|
|
28359
|
+
if (!r)
|
|
28360
|
+
return notFound();
|
|
28361
|
+
const params = new URL(req.url).searchParams;
|
|
28362
|
+
const path = params.get("path") ?? "";
|
|
28363
|
+
const lineRaw = params.get("line");
|
|
28364
|
+
const line = lineRaw !== null && /^\d+$/.test(lineRaw) ? Number(lineRaw) : undefined;
|
|
28365
|
+
const excerpt = readFileExcerpt(r.cwd, path, line);
|
|
28366
|
+
return excerpt ? Response.json(excerpt) : notFound();
|
|
28367
|
+
}
|
|
28151
28368
|
async function handleDecision(id) {
|
|
28152
28369
|
const inMem = store.get(id);
|
|
28153
28370
|
if (inMem?.decision) {
|
|
@@ -28181,8 +28398,9 @@ function createServer(opts) {
|
|
|
28181
28398
|
if (body.generalCommentDraft != null) {
|
|
28182
28399
|
r.generalCommentDraft = body.generalCommentDraft;
|
|
28183
28400
|
}
|
|
28184
|
-
|
|
28185
|
-
|
|
28401
|
+
const cur = currentVersion(r);
|
|
28402
|
+
if (body.composerScratches != null && (body.version == null || body.version === cur.version)) {
|
|
28403
|
+
cur.composerScratches = body.composerScratches;
|
|
28186
28404
|
}
|
|
28187
28405
|
});
|
|
28188
28406
|
if (updated)
|
|
@@ -28204,7 +28422,7 @@ function createServer(opts) {
|
|
|
28204
28422
|
r.decision = decision;
|
|
28205
28423
|
r.status = decision.behavior === "allow" ? "approved" : "rejected";
|
|
28206
28424
|
r.generalCommentDraft = "";
|
|
28207
|
-
r.composerScratches = [];
|
|
28425
|
+
currentVersion(r).composerScratches = [];
|
|
28208
28426
|
});
|
|
28209
28427
|
if (decision.behavior === "allow") {
|
|
28210
28428
|
store.bumpEpoch(existing.sessionId);
|
|
@@ -28257,6 +28475,10 @@ function createServer(opts) {
|
|
|
28257
28475
|
const { id, sub } = route;
|
|
28258
28476
|
if (method === "GET" && !sub)
|
|
28259
28477
|
return handleGetReview(id);
|
|
28478
|
+
if (method === "GET" && sub === "/file")
|
|
28479
|
+
return handleFileExcerpt(req, id);
|
|
28480
|
+
if (method === "POST" && sub === "/file-refs")
|
|
28481
|
+
return handleFileRefs(req, id);
|
|
28260
28482
|
if (method === "GET" && sub === "/decision")
|
|
28261
28483
|
return handleDecision(id);
|
|
28262
28484
|
if (method === "PUT" && sub === "/draft")
|
|
@@ -28347,7 +28569,7 @@ function createServer(opts) {
|
|
|
28347
28569
|
|
|
28348
28570
|
// src/store.ts
|
|
28349
28571
|
import { readdir, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
28350
|
-
import { join as
|
|
28572
|
+
import { join as join7 } from "path";
|
|
28351
28573
|
function createStore(dir, log = noopLogger) {
|
|
28352
28574
|
const reviews = new Map;
|
|
28353
28575
|
const epochs = new Map;
|
|
@@ -28356,7 +28578,7 @@ function createStore(dir, log = noopLogger) {
|
|
|
28356
28578
|
const prev = writeChains.get(review.id) ?? Promise.resolve();
|
|
28357
28579
|
const next = prev.catch(() => {}).then(async () => {
|
|
28358
28580
|
ensureStateDir(dir);
|
|
28359
|
-
await writeFile2(
|
|
28581
|
+
await writeFile2(join7(dir, `${review.id}.json`), JSON.stringify(review, null, 2), {
|
|
28360
28582
|
mode: 384
|
|
28361
28583
|
});
|
|
28362
28584
|
log.debug("store", `review persisted: ${shortId(review.id)}`, { reviewId: review.id });
|
|
@@ -28399,14 +28621,14 @@ function createStore(dir, log = noopLogger) {
|
|
|
28399
28621
|
return;
|
|
28400
28622
|
review.status = "expired";
|
|
28401
28623
|
review.generalCommentDraft = "";
|
|
28402
|
-
review.composerScratches = [];
|
|
28624
|
+
currentVersion(review).composerScratches = [];
|
|
28403
28625
|
review.updatedAt = Math.max(Date.now(), review.updatedAt + 1);
|
|
28404
28626
|
reviews.delete(id);
|
|
28405
28627
|
await persist(review);
|
|
28406
28628
|
return review;
|
|
28407
28629
|
},
|
|
28408
28630
|
async persisted(id) {
|
|
28409
|
-
return await readJsonFile(
|
|
28631
|
+
return await readJsonFile(join7(dir, `${id}.json`)) ?? undefined;
|
|
28410
28632
|
},
|
|
28411
28633
|
size() {
|
|
28412
28634
|
return reviews.size;
|
|
@@ -28433,7 +28655,7 @@ function createStore(dir, log = noopLogger) {
|
|
|
28433
28655
|
if (!file2.endsWith(".json"))
|
|
28434
28656
|
continue;
|
|
28435
28657
|
try {
|
|
28436
|
-
const review = JSON.parse(await readFile3(
|
|
28658
|
+
const review = JSON.parse(await readFile3(join7(dir, file2), "utf-8"));
|
|
28437
28659
|
if (isUnresolved(review.status)) {
|
|
28438
28660
|
reviews.set(review.id, review);
|
|
28439
28661
|
loaded++;
|
|
@@ -28481,7 +28703,7 @@ async function runDaemon(opts) {
|
|
|
28481
28703
|
buildId: await currentBuildId(),
|
|
28482
28704
|
commit: currentCommit(),
|
|
28483
28705
|
stateDir: stateDir(),
|
|
28484
|
-
instanceId:
|
|
28706
|
+
instanceId: randomUUID().slice(0, 8),
|
|
28485
28707
|
approveVariants: adapter.approveVariants,
|
|
28486
28708
|
log
|
|
28487
28709
|
});
|
|
@@ -28523,8 +28745,8 @@ import { existsSync as existsSync4 } from "fs";
|
|
|
28523
28745
|
import { release } from "os";
|
|
28524
28746
|
|
|
28525
28747
|
// src/discovery.ts
|
|
28526
|
-
import { readdirSync as
|
|
28527
|
-
import { basename, join as
|
|
28748
|
+
import { readdirSync as readdirSync3, statSync as statSync4 } from "fs";
|
|
28749
|
+
import { basename, join as join8 } from "path";
|
|
28528
28750
|
var CARET_BIN = "caret-native";
|
|
28529
28751
|
async function safe(build) {
|
|
28530
28752
|
try {
|
|
@@ -28724,7 +28946,7 @@ function listReviewFiles() {
|
|
|
28724
28946
|
const dir = reviewsDir();
|
|
28725
28947
|
let files;
|
|
28726
28948
|
try {
|
|
28727
|
-
files =
|
|
28949
|
+
files = readdirSync3(dir);
|
|
28728
28950
|
} catch {
|
|
28729
28951
|
return [];
|
|
28730
28952
|
}
|
|
@@ -28734,7 +28956,7 @@ function listReviewFiles() {
|
|
|
28734
28956
|
continue;
|
|
28735
28957
|
if (out.length >= 5000)
|
|
28736
28958
|
break;
|
|
28737
|
-
const raw = readJsonFileSync(
|
|
28959
|
+
const raw = readJsonFileSync(join8(dir, file2));
|
|
28738
28960
|
if (raw && typeof raw.id === "string" && typeof raw.status === "string") {
|
|
28739
28961
|
out.push({ id: raw.id, status: raw.status });
|
|
28740
28962
|
}
|
|
@@ -28745,7 +28967,7 @@ var TAIL_BYTES = 256 * 1024;
|
|
|
28745
28967
|
async function logStats(path) {
|
|
28746
28968
|
let size;
|
|
28747
28969
|
try {
|
|
28748
|
-
size =
|
|
28970
|
+
size = statSync4(path).size;
|
|
28749
28971
|
} catch {
|
|
28750
28972
|
return { path, exists: false, size: 0, errors: 0, warns: 0 };
|
|
28751
28973
|
}
|
|
@@ -28832,8 +29054,8 @@ async function runDiscoverySubcommand(opts) {
|
|
|
28832
29054
|
}
|
|
28833
29055
|
|
|
28834
29056
|
// src/commands/install-opencode.ts
|
|
28835
|
-
import { existsSync as existsSync7, readFileSync as
|
|
28836
|
-
import { join as
|
|
29057
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
29058
|
+
import { join as join10 } from "path";
|
|
28837
29059
|
|
|
28838
29060
|
// src/adapters/opencode/deploy.ts
|
|
28839
29061
|
import { existsSync as existsSync5, mkdirSync as mkdirSync2, rmSync, writeFileSync as writeFileSync4 } from "fs";
|
|
@@ -28892,27 +29114,27 @@ function removeFiles(targets, opts) {
|
|
|
28892
29114
|
}
|
|
28893
29115
|
|
|
28894
29116
|
// src/adapters/opencode/packaging.ts
|
|
28895
|
-
import { existsSync as existsSync6, readFileSync as
|
|
28896
|
-
import { dirname as dirname6, join as
|
|
29117
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7, readdirSync as readdirSync4 } from "fs";
|
|
29118
|
+
import { dirname as dirname6, join as join9 } from "path";
|
|
28897
29119
|
function resolveCaretRoot() {
|
|
28898
29120
|
const candidates = [];
|
|
28899
29121
|
const script = process.argv[1];
|
|
28900
29122
|
if (script)
|
|
28901
|
-
candidates.push(
|
|
28902
|
-
candidates.push(
|
|
29123
|
+
candidates.push(join9(dirname6(script), ".."));
|
|
29124
|
+
candidates.push(join9(dirname6(process.execPath), ".."));
|
|
28903
29125
|
for (const root of candidates) {
|
|
28904
|
-
if (existsSync6(
|
|
29126
|
+
if (existsSync6(join9(root, "opencode", "caret.plugin.ts")))
|
|
28905
29127
|
return root;
|
|
28906
29128
|
}
|
|
28907
29129
|
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
29130
|
}
|
|
28909
29131
|
function loadOpencodePackaging(root = resolveCaretRoot()) {
|
|
28910
|
-
const pluginSource =
|
|
28911
|
-
const binPath =
|
|
28912
|
-
const commandsDir =
|
|
29132
|
+
const pluginSource = readFileSync7(join9(root, "opencode", "caret.plugin.ts"), "utf-8");
|
|
29133
|
+
const binPath = join9(root, "bin", "caret");
|
|
29134
|
+
const commandsDir = join9(root, "opencode", "commands");
|
|
28913
29135
|
let commands = [];
|
|
28914
29136
|
try {
|
|
28915
|
-
commands =
|
|
29137
|
+
commands = readdirSync4(commandsDir).filter((f) => f.endsWith(".md")).sort().map((f) => ({ name: f, contents: readFileSync7(join9(commandsDir, f), "utf-8") }));
|
|
28916
29138
|
} catch {
|
|
28917
29139
|
commands = [];
|
|
28918
29140
|
}
|
|
@@ -28924,7 +29146,7 @@ function runInstallOpencodeSubcommand(opts, deps = {}) {
|
|
|
28924
29146
|
const dir = deps.configDir ?? opencodeConfigDir();
|
|
28925
29147
|
const pkg = deps.packaging ?? loadOpencodePackaging();
|
|
28926
29148
|
const pluginPath = pluginFilePath(dir);
|
|
28927
|
-
const commandPaths = pkg.commands.map((c) =>
|
|
29149
|
+
const commandPaths = pkg.commands.map((c) => join10(commandDir(dir), namespacedCommandFilename(c.name)));
|
|
28928
29150
|
if (opts.uninstall) {
|
|
28929
29151
|
const result2 = removeFiles([pluginPath, ...commandPaths], { dryRun: opts.dryRun });
|
|
28930
29152
|
const manifest2 = uninstallManifest(dir, opts.dryRun);
|
|
@@ -28937,7 +29159,7 @@ function runInstallOpencodeSubcommand(opts, deps = {}) {
|
|
|
28937
29159
|
contents: stripNonDefaultExports(renderPlugin(pkg.pluginSource, { version: VERSION, binPath: pkg.binPath }))
|
|
28938
29160
|
},
|
|
28939
29161
|
...pkg.commands.map((c) => ({
|
|
28940
|
-
path:
|
|
29162
|
+
path: join10(commandDir(dir), namespacedCommandFilename(c.name)),
|
|
28941
29163
|
contents: renderPlugin(c.contents, { version: VERSION, binPath: pkg.binPath })
|
|
28942
29164
|
}))
|
|
28943
29165
|
];
|
|
@@ -28974,7 +29196,7 @@ var bunInstall = (configDir2) => {
|
|
|
28974
29196
|
};
|
|
28975
29197
|
function installManifest(dir, dryRun) {
|
|
28976
29198
|
const path = packageJsonPath(dir);
|
|
28977
|
-
const existing = existsSync7(path) ?
|
|
29199
|
+
const existing = existsSync7(path) ? readFileSync8(path, "utf-8") : null;
|
|
28978
29200
|
let next;
|
|
28979
29201
|
try {
|
|
28980
29202
|
next = addPluginDependency(existing, OPENCODE_PLUGIN_DEP, OPENCODE_PLUGIN_DEP_VERSION);
|
|
@@ -28991,7 +29213,7 @@ function uninstallManifest(dir, dryRun) {
|
|
|
28991
29213
|
const path = packageJsonPath(dir);
|
|
28992
29214
|
if (!existsSync7(path))
|
|
28993
29215
|
return [];
|
|
28994
|
-
const existing =
|
|
29216
|
+
const existing = readFileSync8(path, "utf-8");
|
|
28995
29217
|
let next;
|
|
28996
29218
|
try {
|
|
28997
29219
|
next = removePluginDependency(existing, OPENCODE_PLUGIN_DEP);
|
|
@@ -29029,6 +29251,51 @@ async function runPrewarm() {
|
|
|
29029
29251
|
process.exit(0);
|
|
29030
29252
|
}
|
|
29031
29253
|
|
|
29254
|
+
// src/reconcile.ts
|
|
29255
|
+
async function runReconcile(stdin, deps) {
|
|
29256
|
+
const ctx = {};
|
|
29257
|
+
try {
|
|
29258
|
+
const input = deps.parseHookInput(stdin);
|
|
29259
|
+
ctx.sessionId = input.sessionId;
|
|
29260
|
+
if (!input.sessionId)
|
|
29261
|
+
return;
|
|
29262
|
+
const match = (await deps.listReviews()).find((r) => r.sessionId === input.sessionId);
|
|
29263
|
+
if (!match) {
|
|
29264
|
+
logDebug("reconcile", "no pending review for session; nothing to reconcile", { ...ctx });
|
|
29265
|
+
return;
|
|
29266
|
+
}
|
|
29267
|
+
ctx.reviewId = match.id;
|
|
29268
|
+
await deps.resolveReview(match.id);
|
|
29269
|
+
logInfo("reconcile", "terminal approval reconciled", { ...ctx });
|
|
29270
|
+
} catch (err) {
|
|
29271
|
+
logDebug("reconcile", "reconcile skipped", { ...ctx, err: String(err) });
|
|
29272
|
+
}
|
|
29273
|
+
}
|
|
29274
|
+
|
|
29275
|
+
// src/commands/reconcile.ts
|
|
29276
|
+
function prodReconcileDeps(baseUrl, adapter) {
|
|
29277
|
+
return {
|
|
29278
|
+
parseHookInput: (stdin) => adapter.parseHookInput(stdin),
|
|
29279
|
+
listReviews: () => listReviews(baseUrl),
|
|
29280
|
+
resolveReview: (id) => resolveReview(baseUrl, id, { behavior: "allow" })
|
|
29281
|
+
};
|
|
29282
|
+
}
|
|
29283
|
+
async function runReconcileSubcommand() {
|
|
29284
|
+
try {
|
|
29285
|
+
const loaded = loadSettings();
|
|
29286
|
+
setLogLevel(loaded.logging.level);
|
|
29287
|
+
setRedact(loaded.logging.redact);
|
|
29288
|
+
warnInvalidEnvVars((msg) => logWarn("env", msg));
|
|
29289
|
+
const adapter = selectAdapter();
|
|
29290
|
+
const baseUrl = `http://localhost:${getPort(loaded)}`;
|
|
29291
|
+
const stdin = await Bun.stdin.text();
|
|
29292
|
+
await runReconcile(stdin, prodReconcileDeps(baseUrl, adapter));
|
|
29293
|
+
} catch (err) {
|
|
29294
|
+
logDebug("reconcile", "reconcile subcommand skipped", { err: String(err) });
|
|
29295
|
+
}
|
|
29296
|
+
process.exit(0);
|
|
29297
|
+
}
|
|
29298
|
+
|
|
29032
29299
|
// src/commands/redact.ts
|
|
29033
29300
|
function runRedactSubcommand() {
|
|
29034
29301
|
try {
|
|
@@ -30404,17 +30671,24 @@ function denyDecision(reason) {
|
|
|
30404
30671
|
class TimeoutError extends Error {
|
|
30405
30672
|
}
|
|
30406
30673
|
function withTimeout(p, ms2, message) {
|
|
30407
|
-
return new Promise((
|
|
30674
|
+
return new Promise((resolve2, reject) => {
|
|
30408
30675
|
const t = setTimeout(() => reject(new TimeoutError(message)), ms2);
|
|
30409
30676
|
p.then((v3) => {
|
|
30410
30677
|
clearTimeout(t);
|
|
30411
|
-
|
|
30678
|
+
resolve2(v3);
|
|
30412
30679
|
}, (e) => {
|
|
30413
30680
|
clearTimeout(t);
|
|
30414
30681
|
reject(e);
|
|
30415
30682
|
});
|
|
30416
30683
|
});
|
|
30417
30684
|
}
|
|
30685
|
+
async function expireAbandoned(expire, handle) {
|
|
30686
|
+
if (!handle)
|
|
30687
|
+
return;
|
|
30688
|
+
try {
|
|
30689
|
+
await expire(handle.baseUrl, handle.id);
|
|
30690
|
+
} catch {}
|
|
30691
|
+
}
|
|
30418
30692
|
async function runReview(stdin, deps) {
|
|
30419
30693
|
let step = "parse";
|
|
30420
30694
|
const ctx = {};
|
|
@@ -30434,6 +30708,7 @@ async function runReview(stdin, deps) {
|
|
|
30434
30708
|
step = "postReview";
|
|
30435
30709
|
const { id, hasLiveClient } = await deps.postReview(baseUrl, input);
|
|
30436
30710
|
ctx.reviewId = id;
|
|
30711
|
+
deps.onPosted?.(baseUrl, id);
|
|
30437
30712
|
logDebug("review", `review created: ${shortId(id)}`, { ...ctx });
|
|
30438
30713
|
const open = new URL(baseUrl);
|
|
30439
30714
|
open.hostname = VANITY_HOST;
|
|
@@ -30509,6 +30784,7 @@ async function runReviewSubcommand() {
|
|
|
30509
30784
|
warnInvalidEnvVars((msg) => logWarn("env", msg));
|
|
30510
30785
|
const adapter = selectAdapter();
|
|
30511
30786
|
let hookInput;
|
|
30787
|
+
let posted;
|
|
30512
30788
|
let responded = false;
|
|
30513
30789
|
const respond = (decision) => {
|
|
30514
30790
|
if (responded)
|
|
@@ -30517,21 +30793,26 @@ async function runReviewSubcommand() {
|
|
|
30517
30793
|
process.stdout.write(`${adapter.emitDecision(decision, hookInput)}
|
|
30518
30794
|
`);
|
|
30519
30795
|
};
|
|
30520
|
-
const denyAndExit = (reason) => {
|
|
30796
|
+
const denyAndExit = async (reason) => {
|
|
30521
30797
|
if (!responded)
|
|
30522
30798
|
logError("signal", new Error(reason));
|
|
30523
30799
|
respond({ behavior: "deny", feedback: `${reason} See ${logFile()}.`, decidedAt: Date.now() });
|
|
30800
|
+
await expireAbandoned(expireReview, posted);
|
|
30524
30801
|
process.exit(0);
|
|
30525
30802
|
};
|
|
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."));
|
|
30803
|
+
process.once("SIGINT", () => void denyAndExit("caret: interrupted (SIGINT) \u2014 denying to fail safe."));
|
|
30804
|
+
process.once("SIGTERM", () => void denyAndExit("caret: terminated (SIGTERM) \u2014 denying to fail safe."));
|
|
30528
30805
|
const stdin = await Bun.stdin.text();
|
|
30529
30806
|
try {
|
|
30530
30807
|
hookInput = adapter.parseHookInput(stdin);
|
|
30531
30808
|
} catch {
|
|
30532
30809
|
hookInput = undefined;
|
|
30533
30810
|
}
|
|
30534
|
-
const
|
|
30811
|
+
const deps = prodReviewDeps(loaded, adapter);
|
|
30812
|
+
deps.onPosted = (baseUrl, id) => {
|
|
30813
|
+
posted = { baseUrl, id };
|
|
30814
|
+
};
|
|
30815
|
+
const out = await runReview(stdin, deps);
|
|
30535
30816
|
respond(out);
|
|
30536
30817
|
process.exit(0);
|
|
30537
30818
|
}
|
|
@@ -32589,10 +32870,11 @@ function runProgram(program2, onError) {
|
|
|
32589
32870
|
|
|
32590
32871
|
// src/cli.ts
|
|
32591
32872
|
function buildProgram() {
|
|
32592
|
-
const program2 = createProgram("caret", "caret hook CLI: daemon | prewarm | review | redact | discovery | install-opencode").version(VERSION);
|
|
32873
|
+
const program2 = createProgram("caret", "caret hook CLI: daemon | prewarm | review | reconcile | redact | discovery | install-opencode").version(VERSION);
|
|
32593
32874
|
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
32875
|
program2.command("prewarm").description("warm-start the daemon").action(() => runPrewarm());
|
|
32595
32876
|
program2.command("review").description("review a plan from stdin (ExitPlanMode hook)").action(() => runReviewSubcommand());
|
|
32877
|
+
program2.command("reconcile").description("reconcile a terminal plan approval into the daemon (ExitPlanMode PostToolUse hook)").action(() => runReconcileSubcommand());
|
|
32596
32878
|
program2.command("redact").description("write redacted copies of the logs").action(() => runRedactSubcommand());
|
|
32597
32879
|
program2.command("discovery").description("print a diagnostics report").option("--json", "emit the machine-readable JSON document").action((opts) => runDiscoverySubcommand({ json: opts.json ?? false }));
|
|
32598
32880
|
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({
|