@macintacos/caret 0.8.2 → 0.10.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 +59 -47
- package/commands/discovery.md +4 -4
- package/dist/cli.js +341 -114
- package/opencode/caret.plugin.ts +91 -30
- package/opencode/commands/discovery.md +4 -4
- package/package.json +3 -2
- package/ui/dist/assets/{angular-html-BpJgIORc.js → angular-html-166Zp_qr.js} +1 -1
- package/ui/dist/assets/{angular-ts-Dw7jt6H3.js → angular-ts-DJf9y5RJ.js} +1 -1
- package/ui/dist/assets/{apl-DscWKpqG.js → apl-BRtDRKgK.js} +1 -1
- package/ui/dist/assets/{astro-M2TfAY-4.js → astro-B9_p1ZlX.js} +1 -1
- package/ui/dist/assets/{blade-BKZbiCTN.js → blade-DUQgH5UI.js} +1 -1
- package/ui/dist/assets/{c-vSGY_oaW.js → c-DkmnqVR5.js} +1 -1
- package/ui/dist/assets/{cobol-D_b85OID.js → cobol-D_DeO3_Y.js} +1 -1
- package/ui/dist/assets/{coffee-yR0vOcDv.js → coffee-BlbmSk0s.js} +1 -1
- package/ui/dist/assets/{cpp-B9n-PBog.js → cpp-C-2HRkcc.js} +1 -1
- package/ui/dist/assets/{crystal-DlMW4K68.js → crystal-CpLflAjJ.js} +1 -1
- package/ui/dist/assets/{css-eR_OGyvz.js → css-BVuUbqzr.js} +1 -1
- package/ui/dist/assets/{dist-Dw6gzrLi.js → dist-B9dAYtn2.js} +1 -1
- package/ui/dist/assets/{dist-BOlD4h0w.js → dist-BtZ4f0sy.js} +1 -1
- package/ui/dist/assets/{dist-BDTKIP5U.js → dist-BxKUx0iz.js} +1 -1
- package/ui/dist/assets/{dist-C-9GQ1Zp.js → dist-CmhY3KNJ.js} +1 -1
- package/ui/dist/assets/{dist-BVU_DRtk.js → dist-D-vK_12F.js} +1 -1
- package/ui/dist/assets/{dist-z5ayJUeC.js → dist-D1hccSXB.js} +1 -1
- package/ui/dist/assets/{dist-fegIt7sh.js → dist-DWjpoB5Q.js} +1 -1
- package/ui/dist/assets/{dist-Cu14OUmD.js → dist-Dd0x4Lgi.js} +1 -1
- package/ui/dist/assets/{dist-Bq4ybaGS.js → dist-Deoj7mVd.js} +1 -1
- package/ui/dist/assets/{dist-CA0N05NE.js → dist-DtJjv6C-.js} +1 -1
- package/ui/dist/assets/{dist-9nsJ_jfT.js → dist-DyJyAkU7.js} +1 -1
- package/ui/dist/assets/{dist-YE2Uj3eD.js → dist-DzbZtok6.js} +1 -1
- package/ui/dist/assets/{dist-CLxm4mmq.js → dist-Jn2c79bF.js} +1 -1
- package/ui/dist/assets/{dist--nCimyFJ.js → dist-WBctQMJH.js} +1 -1
- package/ui/dist/assets/{dist-DpjGqY_j.js → dist-nPPt0-3f.js} +1 -1
- package/ui/dist/assets/{dist-ZIUMZKKQ.js → dist-qtw8Ijdq.js} +1 -1
- package/ui/dist/assets/{dist-EFvLx_Vg.js → dist-vj9fsPbi.js} +1 -1
- package/ui/dist/assets/{edge-CIm6BqT_.js → edge-Bvaq3vgu.js} +1 -1
- package/ui/dist/assets/{elixir-CGFSlFw_.js → elixir-AtYS8b3T.js} +1 -1
- package/ui/dist/assets/{elm-BSew7n5O.js → elm-DiPfkLEI.js} +1 -1
- package/ui/dist/assets/{erb-BkgqIZze.js → erb-DFPsOHl7.js} +1 -1
- package/ui/dist/assets/{git-rebase-BUYBBmxS.js → git-rebase-DOQhjkkB.js} +1 -1
- package/ui/dist/assets/{glimmer-js-BGag6-lb.js → glimmer-js-DnsoKoXj.js} +1 -1
- package/ui/dist/assets/{glimmer-ts-DDMkKLl0.js → glimmer-ts-efCSUcGf.js} +1 -1
- package/ui/dist/assets/{glsl-DORy1f5o.js → glsl-8mp3VUjq.js} +1 -1
- package/ui/dist/assets/{graphql-CQdH4wHz.js → graphql-NvuaWD7i.js} +1 -1
- package/ui/dist/assets/{hack-DEMoudQX.js → hack-BJ5H84Wq.js} +1 -1
- package/ui/dist/assets/{haml-5F6bZL9O.js → haml-Dzosfq8Y.js} +1 -1
- package/ui/dist/assets/{handlebars-CJrxVb1S.js → handlebars-tKuTe0db.js} +1 -1
- package/ui/dist/assets/{html-DnBxHgUN.js → html-JqbgrVrs.js} +1 -1
- package/ui/dist/assets/{html-derivative-oO1_x7AL.js → html-derivative-0a3abtuv.js} +1 -1
- package/ui/dist/assets/{http-x7FjaFyJ.js → http-DMJqqRWL.js} +1 -1
- package/ui/dist/assets/{hurl-C3DS1OMk.js → hurl-N2MzWbSO.js} +1 -1
- package/ui/dist/assets/index-BMSMV2hN.css +1 -0
- package/ui/dist/assets/index-C_IKB4Ku.js +2501 -0
- package/ui/dist/assets/{java-C2C6Komk.js → java-B3EVVFFi.js} +1 -1
- package/ui/dist/assets/{javascript-DEpYVqmG.js → javascript-Y0fZ_N_R.js} +1 -1
- package/ui/dist/assets/{jinja-BNfhmgIR.js → jinja-BD-QmqJ0.js} +1 -1
- package/ui/dist/assets/{jison-ooxyiW2g.js → jison-BK-X5L2R.js} +1 -1
- package/ui/dist/assets/{json-Dx2gR8Lp.js → json-DlHOOSIb.js} +1 -1
- package/ui/dist/assets/{jsx-BZCBHgZQ.js → jsx-XohAHRMn.js} +1 -1
- package/ui/dist/assets/{julia-kkZGM_a9.js → julia-BBLrPT8A.js} +1 -1
- package/ui/dist/assets/{just-Cjde5dAJ.js → just-DEry_8sa.js} +1 -1
- package/ui/dist/assets/{latex-D3gXuVSc.js → latex-BuCB4qwR.js} +1 -1
- package/ui/dist/assets/{liquid-DmlvH9NM.js → liquid-ZtUmbkDf.js} +1 -1
- package/ui/dist/assets/{lua-pCB9FV-x.js → lua-BmOQjHgA.js} +1 -1
- package/ui/dist/assets/{marko-DwItdpjh.js → marko-BXMGyPGo.js} +1 -1
- package/ui/dist/assets/{mdc-SSlXAY7R.js → mdc-Byn32K04.js} +1 -1
- package/ui/dist/assets/{nginx-DwgR7vzY.js → nginx-BrQiZd4C.js} +1 -1
- package/ui/dist/assets/{nim-BB87Orsk.js → nim-Ceo1IkbE.js} +1 -1
- package/ui/dist/assets/{perl-BEwuOnSP.js → perl-Bum057xI.js} +1 -1
- package/ui/dist/assets/{php-B7dC7_E_.js → php-DcJ8ynWT.js} +1 -1
- package/ui/dist/assets/{pug-cCzod0Ou.js → pug-CA9Zezth.js} +1 -1
- package/ui/dist/assets/{qml-DlvhYl5u.js → qml-BHS1pXn9.js} +1 -1
- package/ui/dist/assets/{r-gkifu9rO.js → r-B4aRkCWL.js} +1 -1
- package/ui/dist/assets/{razor-CQ3nK7LT.js → razor-BXH-K44T.js} +1 -1
- package/ui/dist/assets/{regexp-BfKjRa3b.js → regexp-DUs7aoKy.js} +1 -1
- package/ui/dist/assets/{rst-CaTCPdHP.js → rst-DCrs7vP-.js} +1 -1
- package/ui/dist/assets/{ruby-Qi28elVh.js → ruby-0-lN-lO6.js} +1 -1
- package/ui/dist/assets/{sas-C0-zyShz.js → sas-B-GmRmDQ.js} +1 -1
- package/ui/dist/assets/{scss-B3v_ewrQ.js → scss-oB6C5dt1.js} +1 -1
- package/ui/dist/assets/{shellscript-CAX3EBQC.js → shellscript-BDueQ3Pr.js} +1 -1
- package/ui/dist/assets/{shellsession-BbCaojs-.js → shellsession-snwU-yjZ.js} +1 -1
- package/ui/dist/assets/{soy-C_L1i27o.js → soy-1UzSzCS_.js} +1 -1
- package/ui/dist/assets/{sql-DfQzhZMD.js → sql-_8rHrLe0.js} +1 -1
- package/ui/dist/assets/{stata-wKRkbYfj.js → stata-BFBQv4-R.js} +1 -1
- package/ui/dist/assets/{surrealql-r1cCvWUu.js → surrealql-C-hPyRoi.js} +1 -1
- package/ui/dist/assets/{svelte-QE15YUDB.js → svelte-COzcYWbs.js} +1 -1
- package/ui/dist/assets/{templ-DPDxNWwC.js → templ-CIF5RvVU.js} +1 -1
- package/ui/dist/assets/{tex-BFuhjY1V.js → tex-DiIjyX_E.js} +1 -1
- package/ui/dist/assets/{ts-tags-BUMMUMV3.js → ts-tags-Bb-cll5a.js} +1 -1
- package/ui/dist/assets/{tsx-BL1rdCtn.js → tsx-B_TJQczU.js} +1 -1
- package/ui/dist/assets/{twig-CFLDJ71e.js → twig-B2bev-dI.js} +1 -1
- package/ui/dist/assets/{typescript-Duv2diAv.js → typescript-Ii543U_f.js} +1 -1
- package/ui/dist/assets/{vue-DkO776Yn.js → vue-CGYOL_ei.js} +1 -1
- package/ui/dist/assets/{vue-html-9RYUFMtL.js → vue-html-DJiTs0pH.js} +1 -1
- package/ui/dist/assets/{vue-vine-C3meWGQa.js → vue-vine-BWgBzdhj.js} +1 -1
- package/ui/dist/assets/{xml-ClHTQj3q.js → xml-wwBhpto-.js} +1 -1
- package/ui/dist/assets/{xsl-D1Lt0UF-.js → xsl-ia_QpSvH.js} +1 -1
- package/ui/dist/assets/{yaml-BhUwnbwF.js → yaml-DPR0vpXc.js} +1 -1
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/index-CDxgCCFg.js +0 -1164
- package/ui/dist/assets/index-FuUBXrz0.css +0 -1
package/dist/cli.js
CHANGED
|
@@ -1990,7 +1990,7 @@ var require_indexes = __commonJS((exports, module) => {
|
|
|
1990
1990
|
|
|
1991
1991
|
// node_modules/.bun/thread-stream@4.2.0/node_modules/thread-stream/index.js
|
|
1992
1992
|
var require_thread_stream = __commonJS((exports, module) => {
|
|
1993
|
-
var __dirname = "/Users/juliantorres/GitLocal/Play/caret/node_modules/.bun/thread-stream@4.2.0/node_modules/thread-stream";
|
|
1993
|
+
var __dirname = "/Users/juliantorres/GitLocal/Play/caret/trunk/node_modules/.bun/thread-stream@4.2.0/node_modules/thread-stream";
|
|
1994
1994
|
var { version: version2 } = require_package();
|
|
1995
1995
|
var { EventEmitter } = __require("events");
|
|
1996
1996
|
var { Worker } = __require("worker_threads");
|
|
@@ -2506,7 +2506,7 @@ var require_thread_stream = __commonJS((exports, module) => {
|
|
|
2506
2506
|
|
|
2507
2507
|
// node_modules/.bun/pino@10.3.1/node_modules/pino/lib/transport.js
|
|
2508
2508
|
var require_transport = __commonJS((exports, module) => {
|
|
2509
|
-
var __dirname = "/Users/juliantorres/GitLocal/Play/caret/node_modules/.bun/pino@10.3.1/node_modules/pino/lib";
|
|
2509
|
+
var __dirname = "/Users/juliantorres/GitLocal/Play/caret/trunk/node_modules/.bun/pino@10.3.1/node_modules/pino/lib";
|
|
2510
2510
|
var { createRequire } = __require("module");
|
|
2511
2511
|
var { existsSync: existsSync4 } = __require("fs");
|
|
2512
2512
|
var getCallers = require_caller();
|
|
@@ -5803,8 +5803,8 @@ __export(exports_dist, {
|
|
|
5803
5803
|
});
|
|
5804
5804
|
import { styleText as styleText2, stripVTControlCharacters } from "util";
|
|
5805
5805
|
import process$1 from "process";
|
|
5806
|
-
import { existsSync as existsSync10, lstatSync, readdirSync as
|
|
5807
|
-
import { dirname as dirname8, join as
|
|
5806
|
+
import { existsSync as existsSync10, lstatSync, readdirSync as readdirSync5 } from "fs";
|
|
5807
|
+
import { dirname as dirname8, join as join14 } from "path";
|
|
5808
5808
|
function isUnicodeSupported() {
|
|
5809
5809
|
if (process$1.platform !== "win32") {
|
|
5810
5810
|
return process$1.env.TERM !== "linux";
|
|
@@ -6517,8 +6517,8 @@ ${n3}
|
|
|
6517
6517
|
let i;
|
|
6518
6518
|
existsSync10(t2) ? lstatSync(t2).isDirectory() && (!e.directory || t2.endsWith("/")) ? i = t2 : i = dirname8(t2) : i = dirname8(t2);
|
|
6519
6519
|
const c2 = t2.length > 1 && t2.endsWith("/") ? t2.slice(0, -1) : t2;
|
|
6520
|
-
return
|
|
6521
|
-
const n3 =
|
|
6520
|
+
return readdirSync5(i).map((r2) => {
|
|
6521
|
+
const n3 = join14(i, r2), m3 = lstatSync(n3);
|
|
6522
6522
|
return {
|
|
6523
6523
|
name: r2,
|
|
6524
6524
|
path: n3,
|
|
@@ -24178,6 +24178,63 @@ config(en_default());
|
|
|
24178
24178
|
var DEFAULT_PORT = 42718;
|
|
24179
24179
|
var VANITY_HOST = "caret.localhost";
|
|
24180
24180
|
var HOOK_TIMEOUT_S = 3900;
|
|
24181
|
+
var EXCERPT_RADIUS = 30;
|
|
24182
|
+
var KNOWN_FILE_EXTENSIONS = new Set([
|
|
24183
|
+
"ts",
|
|
24184
|
+
"tsx",
|
|
24185
|
+
"mts",
|
|
24186
|
+
"cts",
|
|
24187
|
+
"js",
|
|
24188
|
+
"jsx",
|
|
24189
|
+
"mjs",
|
|
24190
|
+
"cjs",
|
|
24191
|
+
"svelte",
|
|
24192
|
+
"vue",
|
|
24193
|
+
"json",
|
|
24194
|
+
"jsonc",
|
|
24195
|
+
"css",
|
|
24196
|
+
"scss",
|
|
24197
|
+
"less",
|
|
24198
|
+
"html",
|
|
24199
|
+
"htm",
|
|
24200
|
+
"xml",
|
|
24201
|
+
"svg",
|
|
24202
|
+
"md",
|
|
24203
|
+
"mdx",
|
|
24204
|
+
"py",
|
|
24205
|
+
"rb",
|
|
24206
|
+
"rs",
|
|
24207
|
+
"go",
|
|
24208
|
+
"java",
|
|
24209
|
+
"kt",
|
|
24210
|
+
"c",
|
|
24211
|
+
"h",
|
|
24212
|
+
"cc",
|
|
24213
|
+
"cpp",
|
|
24214
|
+
"hpp",
|
|
24215
|
+
"sh",
|
|
24216
|
+
"bash",
|
|
24217
|
+
"zsh",
|
|
24218
|
+
"toml",
|
|
24219
|
+
"yaml",
|
|
24220
|
+
"yml",
|
|
24221
|
+
"ini",
|
|
24222
|
+
"sql",
|
|
24223
|
+
"graphql",
|
|
24224
|
+
"gql",
|
|
24225
|
+
"php",
|
|
24226
|
+
"swift",
|
|
24227
|
+
"dart",
|
|
24228
|
+
"txt",
|
|
24229
|
+
"lock",
|
|
24230
|
+
"cfg",
|
|
24231
|
+
"conf"
|
|
24232
|
+
]);
|
|
24233
|
+
function hasKnownFileExtension(path) {
|
|
24234
|
+
const base = path.split("/").pop() ?? "";
|
|
24235
|
+
const dot = base.lastIndexOf(".");
|
|
24236
|
+
return dot > 0 && KNOWN_FILE_EXTENSIONS.has(base.slice(dot + 1).toLowerCase());
|
|
24237
|
+
}
|
|
24181
24238
|
var BUN_SOCKET_IDLE_CAP_S = 255;
|
|
24182
24239
|
var IDLE_TIMEOUT_HEADROOM_S = 5;
|
|
24183
24240
|
var MAX_HEARTBEAT_MS = (BUN_SOCKET_IDLE_CAP_S - IDLE_TIMEOUT_HEADROOM_S) * 1000;
|
|
@@ -24294,7 +24351,7 @@ async function loadUiAssets() {
|
|
|
24294
24351
|
// package.json
|
|
24295
24352
|
var package_default = {
|
|
24296
24353
|
name: "@macintacos/caret",
|
|
24297
|
-
version: "0.
|
|
24354
|
+
version: "0.10.0",
|
|
24298
24355
|
license: "MIT",
|
|
24299
24356
|
type: "module",
|
|
24300
24357
|
description: "A Claude Code plugin that replaces the terminal plan-approval prompt with a local web UI for inline review, annotation, and approval.",
|
|
@@ -24323,7 +24380,7 @@ var package_default = {
|
|
|
24323
24380
|
".claude-plugin/plugin.json"
|
|
24324
24381
|
],
|
|
24325
24382
|
scripts: {
|
|
24326
|
-
test: "bun test --conditions browser",
|
|
24383
|
+
test: "bun test --conditions browser --timeout 30000",
|
|
24327
24384
|
lint: "mise run lint",
|
|
24328
24385
|
format: "mise run format",
|
|
24329
24386
|
build: "mise run build"
|
|
@@ -24359,6 +24416,7 @@ var package_default = {
|
|
|
24359
24416
|
"@lezer/highlight": "^1.2.3",
|
|
24360
24417
|
"@opencode-ai/plugin": "^1.16.2",
|
|
24361
24418
|
"@pierre/diffs": "1.2.7",
|
|
24419
|
+
"@pierre/trees": "1.0.0-beta.6",
|
|
24362
24420
|
"bits-ui": "^2.18.1",
|
|
24363
24421
|
clsx: "^2.1.1",
|
|
24364
24422
|
commander: "^15.0.0",
|
|
@@ -25160,9 +25218,10 @@ var PlanInputSchema = exports_external.object({
|
|
|
25160
25218
|
cwd: exports_external.string().optional(),
|
|
25161
25219
|
title: exports_external.string().optional(),
|
|
25162
25220
|
plan: exports_external.string().optional(),
|
|
25163
|
-
planFilePath: exports_external.string().optional()
|
|
25221
|
+
planFilePath: exports_external.string().optional(),
|
|
25222
|
+
cmux: exports_external.object({ workspaceId: exports_external.string().min(1), surfaceId: exports_external.string().min(1) }).optional().catch(undefined)
|
|
25164
25223
|
}).catch({});
|
|
25165
|
-
var MAX_FILE_REFS =
|
|
25224
|
+
var MAX_FILE_REFS = 1000;
|
|
25166
25225
|
var FileRefsBodySchema = exports_external.object({ paths: exports_external.array(exports_external.string()).catch([]) }).catch({ paths: [] });
|
|
25167
25226
|
var BehaviorSchema = exports_external.enum(["allow", "deny"]);
|
|
25168
25227
|
var ResolveBodySchema = exports_external.object({
|
|
@@ -25226,12 +25285,41 @@ async function parseBody(req, schema) {
|
|
|
25226
25285
|
return schema.parse(await req.json().catch(() => ({})));
|
|
25227
25286
|
}
|
|
25228
25287
|
|
|
25288
|
+
// src/lib/cmux.ts
|
|
25289
|
+
function readCmuxPane(env = process.env) {
|
|
25290
|
+
const workspaceId = env.CMUX_WORKSPACE_ID;
|
|
25291
|
+
const surfaceId = env.CMUX_SURFACE_ID;
|
|
25292
|
+
if (!workspaceId || !surfaceId)
|
|
25293
|
+
return;
|
|
25294
|
+
return { workspaceId, surfaceId };
|
|
25295
|
+
}
|
|
25296
|
+
function markPaneRead(pane, deps = {}) {
|
|
25297
|
+
const spawn = deps.spawn ?? Bun.spawn;
|
|
25298
|
+
const log = deps.log ?? noopLogger;
|
|
25299
|
+
try {
|
|
25300
|
+
spawn([
|
|
25301
|
+
"cmux",
|
|
25302
|
+
"mark-notification-read",
|
|
25303
|
+
"--workspace",
|
|
25304
|
+
pane.workspaceId,
|
|
25305
|
+
"--surface",
|
|
25306
|
+
pane.surfaceId
|
|
25307
|
+
], { stdio: ["ignore", "ignore", "ignore"] }).unref();
|
|
25308
|
+
} catch (err) {
|
|
25309
|
+
log.warn("spawn", "unread mark clear failed", { reason: String(err) });
|
|
25310
|
+
}
|
|
25311
|
+
}
|
|
25312
|
+
|
|
25313
|
+
// src/plan/directory.ts
|
|
25314
|
+
import { readdir as readdir2 } from "fs/promises";
|
|
25315
|
+
import { join as join8, relative as relative3, sep as sep2 } from "path";
|
|
25316
|
+
|
|
25229
25317
|
// src/plan/excerpt.ts
|
|
25230
|
-
import {
|
|
25318
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
25319
|
+
import { readdir, realpath, stat } from "fs/promises";
|
|
25231
25320
|
import { extname, isAbsolute, join as join7, relative as relative2, resolve, sep } from "path";
|
|
25232
|
-
var
|
|
25233
|
-
var
|
|
25234
|
-
var MAX_EXCERPT_BYTES = 2 * 1024 * 1024;
|
|
25321
|
+
var EXCERPT_HEAD_LINES = 60;
|
|
25322
|
+
var MAX_EXCERPT_BYTES = 10 * 1024 * 1024;
|
|
25235
25323
|
var MAX_SCAN_ENTRIES = 5000;
|
|
25236
25324
|
var SKIP_DIRS = new Set(["node_modules", "dist", "build", "coverage", "out"]);
|
|
25237
25325
|
var EXT_LANGUAGE = {
|
|
@@ -25285,36 +25373,32 @@ function languageForPath(path) {
|
|
|
25285
25373
|
return EXT_LANGUAGE[extname(path).toLowerCase()] ?? "text";
|
|
25286
25374
|
}
|
|
25287
25375
|
function safeRealpath(path) {
|
|
25288
|
-
|
|
25289
|
-
return realpathSync(path);
|
|
25290
|
-
} catch {
|
|
25291
|
-
return null;
|
|
25292
|
-
}
|
|
25376
|
+
return realpath(path).catch(() => null);
|
|
25293
25377
|
}
|
|
25294
|
-
function
|
|
25295
|
-
|
|
25378
|
+
function safeStat(path) {
|
|
25379
|
+
return stat(path).catch(() => null);
|
|
25380
|
+
}
|
|
25381
|
+
async function contained(cwdReal, abs) {
|
|
25382
|
+
const real = await safeRealpath(abs);
|
|
25296
25383
|
if (real === null)
|
|
25297
25384
|
return null;
|
|
25298
25385
|
if (real !== cwdReal && !real.startsWith(cwdReal + sep))
|
|
25299
25386
|
return null;
|
|
25300
|
-
|
|
25301
|
-
|
|
25302
|
-
return null;
|
|
25303
|
-
} catch {
|
|
25387
|
+
const stats = await safeStat(real);
|
|
25388
|
+
if (stats === null)
|
|
25304
25389
|
return null;
|
|
25305
|
-
|
|
25306
|
-
|
|
25390
|
+
if (stats.isFile())
|
|
25391
|
+
return { path: real, kind: "file" };
|
|
25392
|
+
return stats.isDirectory() ? { path: real, kind: "directory" } : null;
|
|
25307
25393
|
}
|
|
25308
|
-
function basenameSearch(cwdReal, name) {
|
|
25309
|
-
if (name === "")
|
|
25310
|
-
return null;
|
|
25394
|
+
async function basenameSearch(cwdReal, name) {
|
|
25311
25395
|
let scanned = 0;
|
|
25312
25396
|
const queue = [cwdReal];
|
|
25313
25397
|
while (queue.length > 0) {
|
|
25314
25398
|
const currentDir = queue.shift();
|
|
25315
25399
|
let entries;
|
|
25316
25400
|
try {
|
|
25317
|
-
entries =
|
|
25401
|
+
entries = await readdir(currentDir, { withFileTypes: true });
|
|
25318
25402
|
} catch {
|
|
25319
25403
|
continue;
|
|
25320
25404
|
}
|
|
@@ -25331,49 +25415,73 @@ function basenameSearch(cwdReal, name) {
|
|
|
25331
25415
|
}
|
|
25332
25416
|
return null;
|
|
25333
25417
|
}
|
|
25334
|
-
function
|
|
25418
|
+
async function resolveInCwd(cwd, candidate) {
|
|
25335
25419
|
if (cwd === "" || !isAbsolute(cwd))
|
|
25336
25420
|
return null;
|
|
25337
|
-
const cwdReal = safeRealpath(cwd);
|
|
25421
|
+
const cwdReal = await safeRealpath(cwd);
|
|
25338
25422
|
if (cwdReal === null)
|
|
25339
25423
|
return null;
|
|
25340
25424
|
const cleaned = candidate.trim();
|
|
25341
25425
|
if (cleaned === "")
|
|
25342
25426
|
return null;
|
|
25343
|
-
const direct =
|
|
25427
|
+
const direct = await contained(cwdReal, resolve(cwdReal, cleaned));
|
|
25344
25428
|
if (direct !== null)
|
|
25345
25429
|
return direct;
|
|
25346
|
-
|
|
25347
|
-
return basenameSearch(cwdReal, name);
|
|
25348
|
-
}
|
|
25349
|
-
function readFileExcerpt(cwd, candidate, line) {
|
|
25350
|
-
const abs = resolveFileInCwd(cwd, candidate);
|
|
25351
|
-
if (abs === null)
|
|
25352
|
-
return null;
|
|
25353
|
-
let size;
|
|
25354
|
-
try {
|
|
25355
|
-
size = statSync2(abs).size;
|
|
25356
|
-
} catch {
|
|
25357
|
-
return null;
|
|
25358
|
-
}
|
|
25359
|
-
if (size > MAX_EXCERPT_BYTES)
|
|
25430
|
+
if (cleaned.includes("/") || !hasKnownFileExtension(cleaned))
|
|
25360
25431
|
return null;
|
|
25432
|
+
const found = await basenameSearch(cwdReal, cleaned);
|
|
25433
|
+
return found === null ? null : { path: found, kind: "file" };
|
|
25434
|
+
}
|
|
25435
|
+
async function isFileTooLargeToPreview(cwd, candidate) {
|
|
25436
|
+
const hit = await resolveInCwd(cwd, candidate);
|
|
25437
|
+
if (hit === null || hit.kind !== "file")
|
|
25438
|
+
return false;
|
|
25439
|
+
const stats = await safeStat(hit.path);
|
|
25440
|
+
return stats !== null && stats.size > MAX_EXCERPT_BYTES;
|
|
25441
|
+
}
|
|
25442
|
+
var memo = null;
|
|
25443
|
+
function statIdentity(stats) {
|
|
25444
|
+
return `${stats.size}:${stats.mtimeMs}:${stats.ino}`;
|
|
25445
|
+
}
|
|
25446
|
+
function splitLines(content) {
|
|
25447
|
+
const lines = content.split(`
|
|
25448
|
+
`);
|
|
25449
|
+
if (lines.length > 1 && lines[lines.length - 1] === "")
|
|
25450
|
+
lines.pop();
|
|
25451
|
+
return lines;
|
|
25452
|
+
}
|
|
25453
|
+
function readLines(abs, stats) {
|
|
25454
|
+
const identity = statIdentity(stats);
|
|
25455
|
+
if (memo !== null && memo.path === abs && memo.identity === identity)
|
|
25456
|
+
return memo.lines;
|
|
25361
25457
|
let content;
|
|
25362
25458
|
try {
|
|
25363
25459
|
content = readFileSync7(abs, "utf-8");
|
|
25364
25460
|
} catch {
|
|
25365
25461
|
return null;
|
|
25366
25462
|
}
|
|
25367
|
-
|
|
25463
|
+
const lines = content.includes("\x00") ? null : splitLines(content);
|
|
25464
|
+
memo = { path: abs, identity, lines };
|
|
25465
|
+
return lines;
|
|
25466
|
+
}
|
|
25467
|
+
async function readFileExcerpt(cwd, candidate, line, range) {
|
|
25468
|
+
const hit = await resolveInCwd(cwd, candidate);
|
|
25469
|
+
if (hit === null || hit.kind !== "file")
|
|
25470
|
+
return null;
|
|
25471
|
+
const abs = hit.path;
|
|
25472
|
+
const stats = await safeStat(abs);
|
|
25473
|
+
if (stats === null || stats.size > MAX_EXCERPT_BYTES)
|
|
25474
|
+
return null;
|
|
25475
|
+
const allLines = readLines(abs, stats);
|
|
25476
|
+
if (allLines === null)
|
|
25368
25477
|
return null;
|
|
25369
|
-
const allLines = content.split(`
|
|
25370
|
-
`);
|
|
25371
|
-
if (allLines.length > 1 && allLines[allLines.length - 1] === "")
|
|
25372
|
-
allLines.pop();
|
|
25373
25478
|
const totalLines = allLines.length;
|
|
25374
25479
|
let startLine;
|
|
25375
25480
|
let endLine;
|
|
25376
|
-
if (
|
|
25481
|
+
if (range !== undefined) {
|
|
25482
|
+
startLine = Math.min(Math.max(1, range.start), totalLines);
|
|
25483
|
+
endLine = Math.min(totalLines, Math.max(range.end, startLine));
|
|
25484
|
+
} else if (line !== undefined && line >= 1) {
|
|
25377
25485
|
const target = Math.min(line, totalLines);
|
|
25378
25486
|
startLine = Math.max(1, target - EXCERPT_RADIUS);
|
|
25379
25487
|
endLine = Math.min(totalLines, target + EXCERPT_RADIUS);
|
|
@@ -25381,7 +25489,7 @@ function readFileExcerpt(cwd, candidate, line) {
|
|
|
25381
25489
|
startLine = 1;
|
|
25382
25490
|
endLine = Math.min(totalLines, EXCERPT_HEAD_LINES);
|
|
25383
25491
|
}
|
|
25384
|
-
const cwdReal = safeRealpath(cwd) ?? cwd;
|
|
25492
|
+
const cwdReal = await safeRealpath(cwd) ?? cwd;
|
|
25385
25493
|
return {
|
|
25386
25494
|
path: relative2(cwdReal, abs),
|
|
25387
25495
|
language: languageForPath(abs),
|
|
@@ -25392,6 +25500,62 @@ function readFileExcerpt(cwd, candidate, line) {
|
|
|
25392
25500
|
};
|
|
25393
25501
|
}
|
|
25394
25502
|
|
|
25503
|
+
// src/plan/directory.ts
|
|
25504
|
+
var MAX_DIR_ENTRIES = 500;
|
|
25505
|
+
var MAX_DIR_DEPTH = 10;
|
|
25506
|
+
function byKindThenName(a, b) {
|
|
25507
|
+
if (a.kind !== b.kind)
|
|
25508
|
+
return a.kind === "directory" ? -1 : 1;
|
|
25509
|
+
return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
|
|
25510
|
+
}
|
|
25511
|
+
function entryFor(name, kind) {
|
|
25512
|
+
if (kind === "file")
|
|
25513
|
+
return { name, kind };
|
|
25514
|
+
return SKIP_DIRS.has(name) || name.startsWith(".") ? { name, kind, skipped: true } : { name, kind };
|
|
25515
|
+
}
|
|
25516
|
+
async function listDirectory(cwd, root, path) {
|
|
25517
|
+
const rootHit = await resolveInCwd(cwd, root);
|
|
25518
|
+
if (rootHit === null || rootHit.kind !== "directory")
|
|
25519
|
+
return null;
|
|
25520
|
+
const target = path === "" ? rootHit : await resolveInCwd(cwd, path);
|
|
25521
|
+
if (target === null || target.kind !== "directory")
|
|
25522
|
+
return null;
|
|
25523
|
+
const rel = relative3(rootHit.path, target.path);
|
|
25524
|
+
if (rel === ".." || rel.startsWith(`..${sep2}`))
|
|
25525
|
+
return null;
|
|
25526
|
+
if (rel !== "" && rel.split(sep2).length > MAX_DIR_DEPTH)
|
|
25527
|
+
return null;
|
|
25528
|
+
let dirents;
|
|
25529
|
+
try {
|
|
25530
|
+
dirents = await readdir2(target.path, { withFileTypes: true });
|
|
25531
|
+
} catch {
|
|
25532
|
+
return null;
|
|
25533
|
+
}
|
|
25534
|
+
const entries = [];
|
|
25535
|
+
const links = [];
|
|
25536
|
+
for (const d of dirents) {
|
|
25537
|
+
if (d.isFile())
|
|
25538
|
+
entries.push(entryFor(d.name, "file"));
|
|
25539
|
+
else if (d.isDirectory())
|
|
25540
|
+
entries.push(entryFor(d.name, "directory"));
|
|
25541
|
+
else if (d.isSymbolicLink())
|
|
25542
|
+
links.push(d);
|
|
25543
|
+
}
|
|
25544
|
+
const targets = await Promise.all(links.map((d) => resolveInCwd(cwd, join8(target.path, d.name))));
|
|
25545
|
+
targets.forEach((hit, i) => {
|
|
25546
|
+
const link = links[i];
|
|
25547
|
+
if (hit !== null && link !== undefined)
|
|
25548
|
+
entries.push(entryFor(link.name, hit.kind));
|
|
25549
|
+
});
|
|
25550
|
+
entries.sort(byKindThenName);
|
|
25551
|
+
const cwdReal = await safeRealpath(cwd) ?? cwd;
|
|
25552
|
+
return {
|
|
25553
|
+
path: relative3(cwdReal, target.path),
|
|
25554
|
+
entries: entries.slice(0, MAX_DIR_ENTRIES),
|
|
25555
|
+
total: entries.length
|
|
25556
|
+
};
|
|
25557
|
+
}
|
|
25558
|
+
|
|
25395
25559
|
// src/review/decisions.ts
|
|
25396
25560
|
function createDecisions(log = noopLogger) {
|
|
25397
25561
|
const pending = new Map;
|
|
@@ -25438,12 +25602,12 @@ function createDecisions(log = noopLogger) {
|
|
|
25438
25602
|
import { randomBytes } from "crypto";
|
|
25439
25603
|
|
|
25440
25604
|
// src/plan/canonical-file.ts
|
|
25441
|
-
import { appendFileSync, existsSync as existsSync4, statSync as
|
|
25605
|
+
import { appendFileSync, existsSync as existsSync4, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
25442
25606
|
function guardedPlanFileWrite(planFilePath, log, failMsg, write) {
|
|
25443
25607
|
try {
|
|
25444
25608
|
if (!planFilePath.endsWith(".md"))
|
|
25445
25609
|
return;
|
|
25446
|
-
if (!existsSync4(planFilePath) || !
|
|
25610
|
+
if (!existsSync4(planFilePath) || !statSync2(planFilePath).isFile())
|
|
25447
25611
|
return;
|
|
25448
25612
|
write(planFilePath);
|
|
25449
25613
|
} catch (err) {
|
|
@@ -25468,24 +25632,24 @@ import { createHash as createHash2 } from "crypto";
|
|
|
25468
25632
|
import { chmodSync as chmodSync2, existsSync as existsSync5, mkdirSync as mkdirSync2, renameSync, writeFileSync as writeFileSync3 } from "fs";
|
|
25469
25633
|
import { mkdtemp, rm } from "fs/promises";
|
|
25470
25634
|
import { tmpdir } from "os";
|
|
25471
|
-
import { dirname as dirname5, join as
|
|
25472
|
-
var RUMDL_VERSION = "0.2.
|
|
25635
|
+
import { dirname as dirname5, join as join9 } from "path";
|
|
25636
|
+
var RUMDL_VERSION = "0.2.47";
|
|
25473
25637
|
var ASSETS = {
|
|
25474
25638
|
"darwin-arm64": {
|
|
25475
25639
|
url: `https://github.com/rvben/rumdl/releases/download/v${RUMDL_VERSION}/rumdl-v${RUMDL_VERSION}-aarch64-apple-darwin.tar.gz`,
|
|
25476
|
-
sha256: "
|
|
25640
|
+
sha256: "10dc23e994327ecc6e98d285802fcad1174608f203062cb18bac55b51976e0cb"
|
|
25477
25641
|
},
|
|
25478
25642
|
"darwin-x64": {
|
|
25479
25643
|
url: `https://github.com/rvben/rumdl/releases/download/v${RUMDL_VERSION}/rumdl-v${RUMDL_VERSION}-x86_64-apple-darwin.tar.gz`,
|
|
25480
|
-
sha256: "
|
|
25644
|
+
sha256: "9a2bf0c692310e0469fdc66fa83f74579cf0331cc1f9b306b25849cffd146c0f"
|
|
25481
25645
|
},
|
|
25482
25646
|
"linux-arm64": {
|
|
25483
25647
|
url: `https://github.com/rvben/rumdl/releases/download/v${RUMDL_VERSION}/rumdl-v${RUMDL_VERSION}-aarch64-unknown-linux-musl.tar.gz`,
|
|
25484
|
-
sha256: "
|
|
25648
|
+
sha256: "b5ab4207b92c0bf0ea6d8946f15545c787e8cf6ecf2176d9e7f26c43aead86a8"
|
|
25485
25649
|
},
|
|
25486
25650
|
"linux-x64": {
|
|
25487
25651
|
url: `https://github.com/rvben/rumdl/releases/download/v${RUMDL_VERSION}/rumdl-v${RUMDL_VERSION}-x86_64-unknown-linux-musl.tar.gz`,
|
|
25488
|
-
sha256: "
|
|
25652
|
+
sha256: "bd4049763e93d6c25420ec086dee6690af31efc85af463c3ecf5d686a5fcf762"
|
|
25489
25653
|
}
|
|
25490
25654
|
};
|
|
25491
25655
|
function rumdlAsset(platform = process.platform, arch = process.arch) {
|
|
@@ -25501,6 +25665,8 @@ code-blocks = false
|
|
|
25501
25665
|
tables = false
|
|
25502
25666
|
reflow = true
|
|
25503
25667
|
reflow-mode = "normalize"
|
|
25668
|
+
reflow-length-exemptions = true
|
|
25669
|
+
ignore-link-urls = true
|
|
25504
25670
|
`;
|
|
25505
25671
|
async function downloadRumdl(binPath, asset, fetchImpl = fetch) {
|
|
25506
25672
|
const res = await fetchImpl(asset.url);
|
|
@@ -25509,14 +25675,14 @@ async function downloadRumdl(binPath, asset, fetchImpl = fetch) {
|
|
|
25509
25675
|
if (actual !== asset.sha256) {
|
|
25510
25676
|
throw new Error(`rumdl: checksum mismatch for ${asset.url} (expected ${asset.sha256}, got ${actual})`);
|
|
25511
25677
|
}
|
|
25512
|
-
const work = await mkdtemp(
|
|
25678
|
+
const work = await mkdtemp(join9(tmpdir(), "caret-rumdl-"));
|
|
25513
25679
|
try {
|
|
25514
|
-
const archive =
|
|
25680
|
+
const archive = join9(work, "rumdl.tar.gz");
|
|
25515
25681
|
await Bun.write(archive, bytes);
|
|
25516
25682
|
const tar = Bun.spawn(["tar", "-xzf", archive, "-C", work], { stderr: "pipe" });
|
|
25517
25683
|
if (await tar.exited !== 0)
|
|
25518
25684
|
throw new Error(`rumdl: failed to extract ${asset.url}`);
|
|
25519
|
-
const extracted =
|
|
25685
|
+
const extracted = join9(work, "rumdl");
|
|
25520
25686
|
if (!existsSync5(extracted))
|
|
25521
25687
|
throw new Error(`rumdl: binary not found in ${asset.url}`);
|
|
25522
25688
|
mkdirSync2(dirname5(binPath), { recursive: true });
|
|
@@ -25549,8 +25715,9 @@ async function ensureRumdl(install = installPinnedRumdl) {
|
|
|
25549
25715
|
const config2 = rumdlConfig();
|
|
25550
25716
|
writeFileSync3(config2, RUMDL_CONFIG);
|
|
25551
25717
|
const override = process.env.CARET_RUMDL_BIN?.trim();
|
|
25552
|
-
if (override)
|
|
25718
|
+
if (override && await installedVersion(override) === RUMDL_VERSION) {
|
|
25553
25719
|
return { bin: override, config: config2, installed: false };
|
|
25720
|
+
}
|
|
25554
25721
|
const bin = rumdlBin();
|
|
25555
25722
|
if (await installedVersion(bin) === RUMDL_VERSION)
|
|
25556
25723
|
return { bin, config: config2, installed: false };
|
|
@@ -25633,6 +25800,7 @@ async function routeIncomingPlan(input, store, log = noopLogger) {
|
|
|
25633
25800
|
await store.update(latest.id, (r) => {
|
|
25634
25801
|
r.versions.push({ version: version2, plan, annotations: [], createdAt: now });
|
|
25635
25802
|
r.status = "pending";
|
|
25803
|
+
r.cmux = input.cmux ?? r.cmux;
|
|
25636
25804
|
r.decision = undefined;
|
|
25637
25805
|
});
|
|
25638
25806
|
log.info("review", `review appended: ${shortId(latest.id)} v${version2}`, {
|
|
@@ -25659,6 +25827,7 @@ async function routeIncomingPlan(input, store, log = noopLogger) {
|
|
|
25659
25827
|
title: input.title?.trim() || deriveTitle(plan),
|
|
25660
25828
|
status: "pending",
|
|
25661
25829
|
planEpoch,
|
|
25830
|
+
cmux: input.cmux,
|
|
25662
25831
|
versions: [{ version: 1, plan, annotations: [], createdAt: now }],
|
|
25663
25832
|
createdAt: now,
|
|
25664
25833
|
updatedAt: now
|
|
@@ -25745,10 +25914,11 @@ function resolveOptions(opts) {
|
|
|
25745
25914
|
approveVariants: opts.approveVariants,
|
|
25746
25915
|
source: opts.source,
|
|
25747
25916
|
diagnostics: opts.diagnostics,
|
|
25917
|
+
markPaneRead: opts.markPaneRead ?? ((pane) => markPaneRead(pane, { log: opts.log })),
|
|
25748
25918
|
log: opts.log ?? noopLogger
|
|
25749
25919
|
};
|
|
25750
25920
|
}
|
|
25751
|
-
var ID_ROUTE_RE = /^\/api\/reviews\/([^/]+)(\/decision|\/resolve|\/draft|\/expire|\/file-refs|\/file)?$/;
|
|
25921
|
+
var ID_ROUTE_RE = /^\/api\/reviews\/([^/]+)(\/decision|\/resolve|\/draft|\/expire|\/seen|\/file-refs|\/file|\/dir)?$/;
|
|
25752
25922
|
function matchIdRoute(path) {
|
|
25753
25923
|
const m = path.match(ID_ROUTE_RE);
|
|
25754
25924
|
if (!m)
|
|
@@ -25807,6 +25977,9 @@ function createServer(opts) {
|
|
|
25807
25977
|
function notFound() {
|
|
25808
25978
|
return new Response("not found", { status: 404 });
|
|
25809
25979
|
}
|
|
25980
|
+
function tooLarge() {
|
|
25981
|
+
return new Response("too large", { status: 413 });
|
|
25982
|
+
}
|
|
25810
25983
|
function handleHealth() {
|
|
25811
25984
|
const body = {
|
|
25812
25985
|
...IDENTITY,
|
|
@@ -25911,27 +26084,59 @@ function createServer(opts) {
|
|
|
25911
26084
|
return notFound();
|
|
25912
26085
|
const { paths } = await parseBody(req, FileRefsBodySchema);
|
|
25913
26086
|
const seen = new Set;
|
|
25914
|
-
const
|
|
25915
|
-
for (const p of paths
|
|
26087
|
+
const unique = [];
|
|
26088
|
+
for (const p of paths) {
|
|
25916
26089
|
if (seen.has(p))
|
|
25917
26090
|
continue;
|
|
25918
26091
|
seen.add(p);
|
|
25919
|
-
if (
|
|
25920
|
-
|
|
26092
|
+
if (unique.push(p) === MAX_FILE_REFS)
|
|
26093
|
+
break;
|
|
25921
26094
|
}
|
|
25922
|
-
|
|
26095
|
+
const entries = await Promise.all(unique.map(async (p) => {
|
|
26096
|
+
const hit = await resolveInCwd(r.cwd, p);
|
|
26097
|
+
return hit === null ? null : [p, hit.kind];
|
|
26098
|
+
}));
|
|
26099
|
+
const resolved = Object.fromEntries(entries.filter((e) => e !== null));
|
|
26100
|
+
log.debug("request", `file-refs resolved: ${Object.keys(resolved).length}/${unique.length}`, {
|
|
26101
|
+
reviewId: id,
|
|
26102
|
+
requested: paths.length,
|
|
26103
|
+
directories: Object.values(resolved).filter((k) => k === "directory").length
|
|
26104
|
+
});
|
|
25923
26105
|
return Response.json({ resolved });
|
|
25924
26106
|
}
|
|
25925
|
-
function handleFileExcerpt(req, id) {
|
|
26107
|
+
async function handleFileExcerpt(req, id) {
|
|
25926
26108
|
const r = store.get(id);
|
|
25927
26109
|
if (!r)
|
|
25928
26110
|
return notFound();
|
|
25929
26111
|
const params = new URL(req.url).searchParams;
|
|
25930
26112
|
const path = params.get("path") ?? "";
|
|
25931
|
-
const
|
|
25932
|
-
|
|
25933
|
-
|
|
25934
|
-
|
|
26113
|
+
const num = (key) => {
|
|
26114
|
+
const raw = params.get(key);
|
|
26115
|
+
return raw !== null && /^\d+$/.test(raw) ? Number(raw) : undefined;
|
|
26116
|
+
};
|
|
26117
|
+
const line = num("line");
|
|
26118
|
+
const start = num("start");
|
|
26119
|
+
const end = num("end");
|
|
26120
|
+
const range = start !== undefined && end !== undefined ? { start, end } : undefined;
|
|
26121
|
+
const excerpt = await readFileExcerpt(r.cwd, path, line, range);
|
|
26122
|
+
if (excerpt)
|
|
26123
|
+
return Response.json(excerpt);
|
|
26124
|
+
return await isFileTooLargeToPreview(r.cwd, path) ? tooLarge() : notFound();
|
|
26125
|
+
}
|
|
26126
|
+
async function handleDirListing(req, id) {
|
|
26127
|
+
const r = store.get(id);
|
|
26128
|
+
if (!r)
|
|
26129
|
+
return notFound();
|
|
26130
|
+
const params = new URL(req.url).searchParams;
|
|
26131
|
+
const listing = await listDirectory(r.cwd, params.get("root") ?? "", params.get("path") ?? "");
|
|
26132
|
+
if (listing === null)
|
|
26133
|
+
return notFound();
|
|
26134
|
+
log.debug("request", "dir listed", {
|
|
26135
|
+
reviewId: id,
|
|
26136
|
+
total: listing.total,
|
|
26137
|
+
returned: listing.entries.length
|
|
26138
|
+
});
|
|
26139
|
+
return Response.json(listing);
|
|
25935
26140
|
}
|
|
25936
26141
|
async function handleDecision(id) {
|
|
25937
26142
|
const inMem = store.get(id);
|
|
@@ -25989,6 +26194,10 @@ function createServer(opts) {
|
|
|
25989
26194
|
await store.update(id, (r) => {
|
|
25990
26195
|
r.decision = decision;
|
|
25991
26196
|
r.status = decision.behavior === "allow" ? "approved" : "rejected";
|
|
26197
|
+
const general = r.generalCommentDraft?.trim();
|
|
26198
|
+
if (general && decision.behavior === "deny" && decision.feedback?.includes(general)) {
|
|
26199
|
+
currentVersion(r).generalComment = general;
|
|
26200
|
+
}
|
|
25992
26201
|
r.generalCommentDraft = "";
|
|
25993
26202
|
currentVersion(r).composerScratches = [];
|
|
25994
26203
|
});
|
|
@@ -26001,6 +26210,8 @@ function createServer(opts) {
|
|
|
26001
26210
|
});
|
|
26002
26211
|
}
|
|
26003
26212
|
}
|
|
26213
|
+
if (existing.cmux)
|
|
26214
|
+
cfg.markPaneRead(existing.cmux);
|
|
26004
26215
|
setTimeout(() => resolveDecision(id, decision), 0);
|
|
26005
26216
|
log.info("resolve", `review ${shortId(id)} resolved: ${decision.behavior}`, {
|
|
26006
26217
|
reviewId: id,
|
|
@@ -26021,6 +26232,14 @@ function createServer(opts) {
|
|
|
26021
26232
|
});
|
|
26022
26233
|
return Response.json({ ok: true });
|
|
26023
26234
|
}
|
|
26235
|
+
function handleSeen(id) {
|
|
26236
|
+
const existing = store.get(id);
|
|
26237
|
+
if (!existing)
|
|
26238
|
+
return notFound();
|
|
26239
|
+
if (existing.cmux)
|
|
26240
|
+
cfg.markPaneRead(existing.cmux);
|
|
26241
|
+
return new Response(null, { status: 204 });
|
|
26242
|
+
}
|
|
26024
26243
|
async function dispatch(req, method, path) {
|
|
26025
26244
|
if (method === "GET" && path === "/api/health")
|
|
26026
26245
|
return handleHealth();
|
|
@@ -26047,6 +26266,8 @@ function createServer(opts) {
|
|
|
26047
26266
|
return handleGetReview(id);
|
|
26048
26267
|
if (method === "GET" && sub === "/file")
|
|
26049
26268
|
return handleFileExcerpt(req, id);
|
|
26269
|
+
if (method === "GET" && sub === "/dir")
|
|
26270
|
+
return handleDirListing(req, id);
|
|
26050
26271
|
if (method === "POST" && sub === "/file-refs")
|
|
26051
26272
|
return handleFileRefs(req, id);
|
|
26052
26273
|
if (method === "GET" && sub === "/decision")
|
|
@@ -26057,6 +26278,8 @@ function createServer(opts) {
|
|
|
26057
26278
|
return handleResolve(req, id);
|
|
26058
26279
|
if (method === "POST" && sub === "/expire")
|
|
26059
26280
|
return handleExpire(id);
|
|
26281
|
+
if (method === "POST" && sub === "/seen")
|
|
26282
|
+
return handleSeen(id);
|
|
26060
26283
|
}
|
|
26061
26284
|
if (method === "GET") {
|
|
26062
26285
|
const asset = handleAsset(path);
|
|
@@ -26138,8 +26361,8 @@ function createServer(opts) {
|
|
|
26138
26361
|
}
|
|
26139
26362
|
|
|
26140
26363
|
// src/review/store.ts
|
|
26141
|
-
import { readdir, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
26142
|
-
import { join as
|
|
26364
|
+
import { readdir as readdir3, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
26365
|
+
import { join as join10 } from "path";
|
|
26143
26366
|
function createStore(dir, log = noopLogger) {
|
|
26144
26367
|
const reviews = new Map;
|
|
26145
26368
|
const epochs = new Map;
|
|
@@ -26148,7 +26371,7 @@ function createStore(dir, log = noopLogger) {
|
|
|
26148
26371
|
const prev = writeChains.get(review.id) ?? Promise.resolve();
|
|
26149
26372
|
const next = prev.catch(() => {}).then(async () => {
|
|
26150
26373
|
ensureStateDir(dir);
|
|
26151
|
-
await writeFile2(
|
|
26374
|
+
await writeFile2(join10(dir, `${review.id}.json`), JSON.stringify(review, null, 2), {
|
|
26152
26375
|
mode: 384
|
|
26153
26376
|
});
|
|
26154
26377
|
log.debug("store", `review persisted: ${shortId(review.id)}`, { reviewId: review.id });
|
|
@@ -26198,7 +26421,7 @@ function createStore(dir, log = noopLogger) {
|
|
|
26198
26421
|
return review;
|
|
26199
26422
|
},
|
|
26200
26423
|
async persisted(id) {
|
|
26201
|
-
return await readJsonFile(
|
|
26424
|
+
return await readJsonFile(join10(dir, `${id}.json`)) ?? undefined;
|
|
26202
26425
|
},
|
|
26203
26426
|
size() {
|
|
26204
26427
|
return reviews.size;
|
|
@@ -26215,7 +26438,7 @@ function createStore(dir, log = noopLogger) {
|
|
|
26215
26438
|
async rehydrate() {
|
|
26216
26439
|
let files;
|
|
26217
26440
|
try {
|
|
26218
|
-
files = await
|
|
26441
|
+
files = await readdir3(dir);
|
|
26219
26442
|
} catch {
|
|
26220
26443
|
log.debug("store", "no reviews dir; nothing to rehydrate");
|
|
26221
26444
|
return;
|
|
@@ -26225,7 +26448,7 @@ function createStore(dir, log = noopLogger) {
|
|
|
26225
26448
|
if (!file2.endsWith(".json"))
|
|
26226
26449
|
continue;
|
|
26227
26450
|
try {
|
|
26228
|
-
const review = JSON.parse(await readFile3(
|
|
26451
|
+
const review = JSON.parse(await readFile3(join10(dir, file2), "utf-8"));
|
|
26229
26452
|
if (isUnresolved(review.status)) {
|
|
26230
26453
|
reviews.set(review.id, review);
|
|
26231
26454
|
loaded++;
|
|
@@ -26324,8 +26547,8 @@ import { existsSync as existsSync7 } from "fs";
|
|
|
26324
26547
|
import { release } from "os";
|
|
26325
26548
|
|
|
26326
26549
|
// src/discovery.ts
|
|
26327
|
-
import { readdirSync as
|
|
26328
|
-
import { basename as basename2, join as
|
|
26550
|
+
import { readdirSync as readdirSync3, statSync as statSync3 } from "fs";
|
|
26551
|
+
import { basename as basename2, join as join11 } from "path";
|
|
26329
26552
|
var CARET_BIN = "caret-native";
|
|
26330
26553
|
async function safe(build) {
|
|
26331
26554
|
try {
|
|
@@ -26525,7 +26748,7 @@ function listReviewFiles() {
|
|
|
26525
26748
|
const dir = reviewsDir();
|
|
26526
26749
|
let files;
|
|
26527
26750
|
try {
|
|
26528
|
-
files =
|
|
26751
|
+
files = readdirSync3(dir);
|
|
26529
26752
|
} catch {
|
|
26530
26753
|
return [];
|
|
26531
26754
|
}
|
|
@@ -26535,7 +26758,7 @@ function listReviewFiles() {
|
|
|
26535
26758
|
continue;
|
|
26536
26759
|
if (out.length >= 5000)
|
|
26537
26760
|
break;
|
|
26538
|
-
const raw = readJsonFileSync(
|
|
26761
|
+
const raw = readJsonFileSync(join11(dir, file2));
|
|
26539
26762
|
if (raw && typeof raw.id === "string" && typeof raw.status === "string") {
|
|
26540
26763
|
out.push({ id: raw.id, status: raw.status });
|
|
26541
26764
|
}
|
|
@@ -26546,7 +26769,7 @@ var TAIL_BYTES = 256 * 1024;
|
|
|
26546
26769
|
async function logStats(path) {
|
|
26547
26770
|
let size;
|
|
26548
26771
|
try {
|
|
26549
|
-
size =
|
|
26772
|
+
size = statSync3(path).size;
|
|
26550
26773
|
} catch {
|
|
26551
26774
|
return { path, exists: false, size: 0, errors: 0, warns: 0 };
|
|
26552
26775
|
}
|
|
@@ -26639,33 +26862,33 @@ import {
|
|
|
26639
26862
|
existsSync as existsSync9,
|
|
26640
26863
|
mkdirSync as mkdirSync3,
|
|
26641
26864
|
rmSync as rmSync2,
|
|
26642
|
-
statSync as
|
|
26865
|
+
statSync as statSync4,
|
|
26643
26866
|
symlinkSync,
|
|
26644
26867
|
writeFileSync as writeFileSync5
|
|
26645
26868
|
} from "fs";
|
|
26646
|
-
import { join as
|
|
26869
|
+
import { join as join13, resolve as resolve2 } from "path";
|
|
26647
26870
|
|
|
26648
26871
|
// src/adapters/opencode/packaging.ts
|
|
26649
|
-
import { existsSync as existsSync8, readdirSync as
|
|
26650
|
-
import { dirname as dirname7, join as
|
|
26872
|
+
import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync8 } from "fs";
|
|
26873
|
+
import { dirname as dirname7, join as join12 } from "path";
|
|
26651
26874
|
function resolveCaretRoot() {
|
|
26652
26875
|
const candidates = [];
|
|
26653
26876
|
const script = process.argv[1];
|
|
26654
26877
|
if (script)
|
|
26655
|
-
candidates.push(
|
|
26656
|
-
candidates.push(
|
|
26878
|
+
candidates.push(join12(dirname7(script), ".."));
|
|
26879
|
+
candidates.push(join12(dirname7(process.execPath), ".."));
|
|
26657
26880
|
for (const root of candidates) {
|
|
26658
|
-
if (existsSync8(
|
|
26881
|
+
if (existsSync8(join12(root, "opencode", "caret.plugin.ts")))
|
|
26659
26882
|
return root;
|
|
26660
26883
|
}
|
|
26661
26884
|
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.`);
|
|
26662
26885
|
}
|
|
26663
26886
|
function loadOpencodePackaging(root = resolveCaretRoot()) {
|
|
26664
|
-
const binPath =
|
|
26665
|
-
const commandsDir =
|
|
26887
|
+
const binPath = join12(root, "bin", "caret");
|
|
26888
|
+
const commandsDir = join12(root, "opencode", "commands");
|
|
26666
26889
|
let commands = [];
|
|
26667
26890
|
try {
|
|
26668
|
-
commands =
|
|
26891
|
+
commands = readdirSync4(commandsDir).filter((f) => f.endsWith(".md")).sort().map((f) => ({ name: f, contents: readFileSync8(join12(commandsDir, f), "utf-8") }));
|
|
26669
26892
|
} catch {
|
|
26670
26893
|
commands = [];
|
|
26671
26894
|
}
|
|
@@ -26674,17 +26897,17 @@ function loadOpencodePackaging(root = resolveCaretRoot()) {
|
|
|
26674
26897
|
|
|
26675
26898
|
// src/commands/install/local.ts
|
|
26676
26899
|
function devMarketplaceDir() {
|
|
26677
|
-
return
|
|
26900
|
+
return join13(stateDir(), "dev-marketplace");
|
|
26678
26901
|
}
|
|
26679
26902
|
function resolveLocalCheckout(opts = {}, deps = {}) {
|
|
26680
26903
|
const repoDir = resolve2(caretRoot(deps));
|
|
26681
|
-
if (!existsSync9(
|
|
26904
|
+
if (!existsSync9(join13(repoDir, ".claude-plugin", "marketplace.json"))) {
|
|
26682
26905
|
throw new Error(`--from-local must run from a caret checkout (no .claude-plugin/marketplace.json at ${repoDir}). A published caret installs with a plain \`caret install\`.`);
|
|
26683
26906
|
}
|
|
26684
26907
|
if (opts.requireArtifacts !== false) {
|
|
26685
26908
|
const missing = [
|
|
26686
|
-
isExecutable(
|
|
26687
|
-
isDirectory(
|
|
26909
|
+
isExecutable(join13(repoDir, "bin", "caret-native")) ? null : "bin/caret-native",
|
|
26910
|
+
isDirectory(join13(repoDir, "bin", "ui")) ? null : "bin/ui"
|
|
26688
26911
|
].filter((m) => m !== null);
|
|
26689
26912
|
if (missing.length > 0) {
|
|
26690
26913
|
throw new Error(`--from-local reuses the build artifacts bin/caret-native + bin/ui, and ${repoDir} is missing ${missing.join(" + ")} \u2014 run \`mise run build\` first.`);
|
|
@@ -26703,9 +26926,9 @@ function caretRoot(deps) {
|
|
|
26703
26926
|
}
|
|
26704
26927
|
function writeDevMarketplace(repoDir, outDir) {
|
|
26705
26928
|
rmSync2(outDir, { recursive: true, force: true });
|
|
26706
|
-
mkdirSync3(
|
|
26707
|
-
symlinkSync(resolve2(repoDir),
|
|
26708
|
-
writeFileSync5(
|
|
26929
|
+
mkdirSync3(join13(outDir, ".claude-plugin"), { recursive: true });
|
|
26930
|
+
symlinkSync(resolve2(repoDir), join13(outDir, "caret"));
|
|
26931
|
+
writeFileSync5(join13(outDir, ".claude-plugin", "marketplace.json"), `${JSON.stringify({
|
|
26709
26932
|
name: "caret",
|
|
26710
26933
|
description: "Local caret dev build (generated by caret install --from-local).",
|
|
26711
26934
|
owner: { name: "macintacos" },
|
|
@@ -26716,20 +26939,20 @@ function writeDevMarketplace(repoDir, outDir) {
|
|
|
26716
26939
|
async function prewarmLocalBuild(repoDir, deps = {}) {
|
|
26717
26940
|
const spawn = deps.spawn ?? runCaptured;
|
|
26718
26941
|
try {
|
|
26719
|
-
await spawn([
|
|
26942
|
+
await spawn([join13(repoDir, "bin", "caret-native"), "prewarm"]);
|
|
26720
26943
|
} catch {}
|
|
26721
26944
|
}
|
|
26722
26945
|
function isExecutable(path) {
|
|
26723
26946
|
try {
|
|
26724
26947
|
accessSync(path, constants.X_OK);
|
|
26725
|
-
return
|
|
26948
|
+
return statSync4(path).isFile();
|
|
26726
26949
|
} catch {
|
|
26727
26950
|
return false;
|
|
26728
26951
|
}
|
|
26729
26952
|
}
|
|
26730
26953
|
function isDirectory(path) {
|
|
26731
26954
|
try {
|
|
26732
|
-
return
|
|
26955
|
+
return statSync4(path).isDirectory();
|
|
26733
26956
|
} catch {
|
|
26734
26957
|
return false;
|
|
26735
26958
|
}
|
|
@@ -26991,7 +27214,7 @@ async function runInstallClaudeTarget(opts, deps = {}) {
|
|
|
26991
27214
|
|
|
26992
27215
|
// src/commands/install/opencode.ts
|
|
26993
27216
|
import { existsSync as existsSync13, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
|
|
26994
|
-
import { basename as basename3, join as
|
|
27217
|
+
import { basename as basename3, join as join15 } from "path";
|
|
26995
27218
|
|
|
26996
27219
|
// src/adapters/opencode/deploy.ts
|
|
26997
27220
|
import { existsSync as existsSync11, mkdirSync as mkdirSync4, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
@@ -27094,7 +27317,7 @@ async function promptUpgrade(verdict, clack) {
|
|
|
27094
27317
|
|
|
27095
27318
|
// src/commands/install/opencode.ts
|
|
27096
27319
|
function isCaretCheckout(dir) {
|
|
27097
|
-
return existsSync13(
|
|
27320
|
+
return existsSync13(join15(dir, "opencode", "caret.plugin.ts"));
|
|
27098
27321
|
}
|
|
27099
27322
|
function caretEntries(text2, isCheckout) {
|
|
27100
27323
|
return pluginEntries(text2).filter((entry) => {
|
|
@@ -27120,7 +27343,7 @@ async function runInstallOpencodeTarget(opts, deps = {}) {
|
|
|
27120
27343
|
const ui = deps.ui ?? silentUI;
|
|
27121
27344
|
const isCheckout = deps.isCheckout ?? isCaretCheckout;
|
|
27122
27345
|
const configFile2 = resolveConfigFile(dir);
|
|
27123
|
-
const commandPaths = pkg.commands.map((c3) =>
|
|
27346
|
+
const commandPaths = pkg.commands.map((c3) => join15(commandDir(dir), namespacedCommandFilename(c3.name)));
|
|
27124
27347
|
const legacy = existingLegacyInstallFiles(dir);
|
|
27125
27348
|
const specifier = opts.local ? localPluginSpecifier(opts.local.repoDir) : CARET_PACKAGE;
|
|
27126
27349
|
if (opts.dryRun) {
|
|
@@ -27143,7 +27366,7 @@ async function runInstallOpencodeTarget(opts, deps = {}) {
|
|
|
27143
27366
|
await upgradeStep(configFile2, opts, deps, ui);
|
|
27144
27367
|
await sweepLegacy(legacy, dir, ui);
|
|
27145
27368
|
const files = pkg.commands.map((c3) => ({
|
|
27146
|
-
path:
|
|
27369
|
+
path: join15(commandDir(dir), namespacedCommandFilename(c3.name)),
|
|
27147
27370
|
contents: renderPlugin(c3.contents, { version: VERSION, binPath: pkg.binPath })
|
|
27148
27371
|
}));
|
|
27149
27372
|
await ui.step("Deploying the /caret:* command files", async () => deployFiles(files, { dryRun: false }), (deployed) => `Deployed ${deployed.paths.length} command file(s) to ${dir}`);
|
|
@@ -28813,7 +29036,10 @@ async function runReview(stdin2, deps) {
|
|
|
28813
29036
|
step = "ensureDaemon";
|
|
28814
29037
|
baseUrl = await deps.ensureDaemon();
|
|
28815
29038
|
step = "postReview";
|
|
28816
|
-
const { id, hasLiveClient } = await deps.postReview(baseUrl,
|
|
29039
|
+
const { id, hasLiveClient } = await deps.postReview(baseUrl, {
|
|
29040
|
+
...input,
|
|
29041
|
+
cmux: deps.readPane?.()
|
|
29042
|
+
});
|
|
28817
29043
|
ctx.reviewId = id;
|
|
28818
29044
|
deps.onPosted?.(baseUrl, id);
|
|
28819
29045
|
logDebug("review", `review created: ${shortId(id)}`, { ...ctx });
|
|
@@ -28880,6 +29106,7 @@ function prodReviewDeps(s, adapter) {
|
|
|
28880
29106
|
postReview,
|
|
28881
29107
|
longPoll,
|
|
28882
29108
|
openBrowser,
|
|
29109
|
+
readPane: readCmuxPane,
|
|
28883
29110
|
timeoutMs: reviewTimeoutMs(s),
|
|
28884
29111
|
expire: expireReview
|
|
28885
29112
|
};
|