@macintacos/caret 0.9.0 → 0.11.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 -52
- package/commands/discovery.md +4 -4
- package/dist/cli.js +312 -111
- package/opencode/commands/discovery.md +4 -4
- package/package.json +3 -2
- package/ui/dist/assets/{angular-html-BOjikgNB.js → angular-html-5RvLUq7Z.js} +1 -1
- package/ui/dist/assets/{angular-ts-BjxNvVmD.js → angular-ts-H7A3iXyF.js} +1 -1
- package/ui/dist/assets/{apl-BkiW4daM.js → apl-TTxSuUB_.js} +1 -1
- package/ui/dist/assets/{astro-3adpTlIH.js → astro-BKz-vRRo.js} +1 -1
- package/ui/dist/assets/{blade-DPTGWOFi.js → blade-DWrd7tz4.js} +1 -1
- package/ui/dist/assets/{c-Gr6AqmnN.js → c-IGApzdRH.js} +1 -1
- package/ui/dist/assets/{cobol-C8hrPk4X.js → cobol-Cazc7O_T.js} +1 -1
- package/ui/dist/assets/{coffee-COAUS6hR.js → coffee-Dd22AwJE.js} +1 -1
- package/ui/dist/assets/{cpp-DRNHed7s.js → cpp-BDjV8GMs.js} +1 -1
- package/ui/dist/assets/{crystal-xGCPBQ2Z.js → crystal-4S07s0cd.js} +1 -1
- package/ui/dist/assets/{css-g3rqYwXR.js → css-1gk7lXYc.js} +1 -1
- package/ui/dist/assets/{dist-B0fJR4WS.js → dist--bc2PnyK.js} +1 -1
- package/ui/dist/assets/{dist-DAyUKqoN.js → dist-BGBwiuT6.js} +1 -1
- package/ui/dist/assets/{dist-DPkspc9Q.js → dist-BGuWRPi1.js} +1 -1
- package/ui/dist/assets/{dist-D55VuegY.js → dist-BIDT62JM.js} +1 -1
- package/ui/dist/assets/{dist-ALQWrgOU.js → dist-BcmAlNPx.js} +1 -1
- package/ui/dist/assets/{dist-B8chzuGI.js → dist-Be8oTfP4.js} +1 -1
- package/ui/dist/assets/{dist-BeBmdH90.js → dist-Bw95B0_z.js} +1 -1
- package/ui/dist/assets/{dist-CqeZRBk3.js → dist-BydijwDA.js} +1 -1
- package/ui/dist/assets/{dist-B3rxpa4n.js → dist-CQluSlUy.js} +1 -1
- package/ui/dist/assets/{dist-B57N0_ad.js → dist-CU4ehwGW.js} +1 -1
- package/ui/dist/assets/{dist-CbLm1gmp.js → dist-Cc8VJ54F.js} +1 -1
- package/ui/dist/assets/{dist-D8G49gMQ.js → dist-DR5uGjyJ.js} +1 -1
- package/ui/dist/assets/{dist-BtEZoKjT.js → dist-DZeAClcR.js} +1 -1
- package/ui/dist/assets/{dist-DGuk1LV9.js → dist-DbsGRkRt.js} +1 -1
- package/ui/dist/assets/{dist-Baejo1Q0.js → dist-cqKXvOJB.js} +1 -1
- package/ui/dist/assets/{dist-ekL3xCM_.js → dist-j6YQuVcV.js} +1 -1
- package/ui/dist/assets/{dist--Ppdbw_u.js → dist-wWa669u6.js} +1 -1
- package/ui/dist/assets/{edge-1tf_B5nG.js → edge-C2c1CO-6.js} +1 -1
- package/ui/dist/assets/{elixir-Ca3rBU7D.js → elixir-DXn37Fta.js} +1 -1
- package/ui/dist/assets/{elm-CbkPZkSz.js → elm-B-bvoD7s.js} +1 -1
- package/ui/dist/assets/{erb-DV7pKwEX.js → erb-DCbJLk1z.js} +1 -1
- package/ui/dist/assets/{git-rebase-DItBLEqi.js → git-rebase-gAHQgEDP.js} +1 -1
- package/ui/dist/assets/{glimmer-js-CAykgXKb.js → glimmer-js-BfsyNEr7.js} +1 -1
- package/ui/dist/assets/{glimmer-ts-BPT-EGHX.js → glimmer-ts-NHB1jiZs.js} +1 -1
- package/ui/dist/assets/{glsl-CMfWGTLW.js → glsl-CKTIh5q1.js} +1 -1
- package/ui/dist/assets/{graphql-CR_NOWo2.js → graphql-BAVT-Qfu.js} +1 -1
- package/ui/dist/assets/{hack-BWP6dwsC.js → hack-BOJXdhzo.js} +1 -1
- package/ui/dist/assets/{haml-CDa-RN50.js → haml-CUHOH-NL.js} +1 -1
- package/ui/dist/assets/{handlebars-sJHLZ5A7.js → handlebars-CNe_S8PQ.js} +1 -1
- package/ui/dist/assets/{html-63fn47vQ.js → html-COaRmwrO.js} +1 -1
- package/ui/dist/assets/{html-derivative-BXnhYyFs.js → html-derivative-CinwqZLf.js} +1 -1
- package/ui/dist/assets/{http-IOW0RfUa.js → http-DbQ_t1_2.js} +1 -1
- package/ui/dist/assets/{hurl-CTSYkbKC.js → hurl-BHWvSlwU.js} +1 -1
- package/ui/dist/assets/index-C6liS7CI.js +3420 -0
- package/ui/dist/assets/index-c2ZxcgJn.css +1 -0
- package/ui/dist/assets/{java-DOX-o9K8.js → java-C8tTvVpe.js} +1 -1
- package/ui/dist/assets/{javascript-NWcTx9fC.js → javascript-BeAm0j_g.js} +1 -1
- package/ui/dist/assets/{jinja-TT5Clkg-.js → jinja-D_xgx2y9.js} +1 -1
- package/ui/dist/assets/{jison-DoZAP4a3.js → jison-NuLIt77u.js} +1 -1
- package/ui/dist/assets/{json-Dd1DbJZw.js → json-CV5jRNNK.js} +1 -1
- package/ui/dist/assets/{jsx-D95PvYev.js → jsx-HTlBmr1T.js} +1 -1
- package/ui/dist/assets/{julia-DzNcHVqx.js → julia-_z5NZrIG.js} +1 -1
- package/ui/dist/assets/{just-DapN_z3p.js → just-BCA5G6NT.js} +1 -1
- package/ui/dist/assets/{latex-Bf3O2fQb.js → latex-CqX8jMgP.js} +1 -1
- package/ui/dist/assets/{liquid-NXRAWoEl.js → liquid-zb-EmYR_.js} +1 -1
- package/ui/dist/assets/{lua-DgnpxH9p.js → lua-Dji9Xdi2.js} +1 -1
- package/ui/dist/assets/{marko-wkWPjoQ9.js → marko-C0oVISAz.js} +1 -1
- package/ui/dist/assets/{mdc-DtOun-X2.js → mdc-CcpeRnqw.js} +1 -1
- package/ui/dist/assets/{nginx-XCdDX9Gp.js → nginx-DFCvTKJt.js} +1 -1
- package/ui/dist/assets/{nim-CnDu2vnO.js → nim-BZjuPzz9.js} +1 -1
- package/ui/dist/assets/{perl-mCSP7zn3.js → perl-B_Clm_ch.js} +1 -1
- package/ui/dist/assets/{php-CnDYV7m9.js → php-3ilvRbct.js} +1 -1
- package/ui/dist/assets/{pug-DUv1_GFU.js → pug-DztvP6cK.js} +1 -1
- package/ui/dist/assets/{qml-Cw49qsJy.js → qml-1bJ9wjdn.js} +1 -1
- package/ui/dist/assets/{r-DUSb08Z8.js → r-BvZmLpfT.js} +1 -1
- package/ui/dist/assets/{razor-CKco3Dbd.js → razor-BjY7GlMn.js} +1 -1
- package/ui/dist/assets/{regexp-DbjqCAsi.js → regexp-BkIlfW8b.js} +1 -1
- package/ui/dist/assets/{rst-iLrpHokU.js → rst-DyPD805e.js} +1 -1
- package/ui/dist/assets/{ruby-BBYneGfk.js → ruby-C9tcEn71.js} +1 -1
- package/ui/dist/assets/{sas-DoS0TCZm.js → sas-oyddpUVQ.js} +1 -1
- package/ui/dist/assets/{scss-DwjazCIb.js → scss-HITFHPDF.js} +1 -1
- package/ui/dist/assets/{shellscript-bR-cjL3L.js → shellscript-x1x9neoG.js} +1 -1
- package/ui/dist/assets/{shellsession-D54KuySr.js → shellsession-DGuaRj5a.js} +1 -1
- package/ui/dist/assets/{soy-QeEusSoH.js → soy-BDcTtkxw.js} +1 -1
- package/ui/dist/assets/{sql-BjJgWH0F.js → sql-BNkgB3gD.js} +1 -1
- package/ui/dist/assets/{stata---E5T02y.js → stata-BqJ-ReAl.js} +1 -1
- package/ui/dist/assets/{surrealql-B3nSLmva.js → surrealql-Cq1d2uiF.js} +1 -1
- package/ui/dist/assets/{svelte-BrJnUGsO.js → svelte-DFzyyr9O.js} +1 -1
- package/ui/dist/assets/{templ-Cp6BywaR.js → templ-C6322Zc6.js} +1 -1
- package/ui/dist/assets/{tex-yFYe_F-5.js → tex-ChOOnEH8.js} +1 -1
- package/ui/dist/assets/{ts-tags-Dn1XZNS9.js → ts-tags-pjZ_FkRd.js} +1 -1
- package/ui/dist/assets/{tsx-BnhO4Hkv.js → tsx-D7E88Xp5.js} +1 -1
- package/ui/dist/assets/{twig-B-zB3u-Z.js → twig-CJAbKsCA.js} +1 -1
- package/ui/dist/assets/{typescript-D49Rl_Ln.js → typescript-BWgdaVLK.js} +1 -1
- package/ui/dist/assets/{vue-DmTXiGeI.js → vue-VFPf5VDL.js} +1 -1
- package/ui/dist/assets/{vue-html-CeXpHptR.js → vue-html-D1bRfEwp.js} +1 -1
- package/ui/dist/assets/{vue-vine-C2uWvGRx.js → vue-vine-CEAW1Fs_.js} +1 -1
- package/ui/dist/assets/{xml-SZiG-U1T.js → xml-T1VYFptl.js} +1 -1
- package/ui/dist/assets/{xsl-DksAOCj6.js → xsl-DpQuzNtk.js} +1 -1
- package/ui/dist/assets/{yaml-DX7HDqJF.js → yaml-Ds8gJ67a.js} +1 -1
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/index-B-GFE-1x.css +0 -1
- package/ui/dist/assets/index-CtmSI3Ho.js +0 -1203
package/dist/cli.js
CHANGED
|
@@ -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.11.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 EXCERPT_RADIUS = 30;
|
|
25233
25321
|
var EXCERPT_HEAD_LINES = 60;
|
|
25234
|
-
var MAX_EXCERPT_BYTES =
|
|
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,55 +25415,66 @@ 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
|
-
|
|
25430
|
+
if (cleaned.includes("/") || !hasKnownFileExtension(cleaned))
|
|
25431
|
+
return null;
|
|
25432
|
+
const found = await basenameSearch(cwdReal, cleaned);
|
|
25433
|
+
return found === null ? null : { path: found, kind: "file" };
|
|
25348
25434
|
}
|
|
25349
|
-
function isFileTooLargeToPreview(cwd, candidate) {
|
|
25350
|
-
const
|
|
25351
|
-
if (
|
|
25435
|
+
async function isFileTooLargeToPreview(cwd, candidate) {
|
|
25436
|
+
const hit = await resolveInCwd(cwd, candidate);
|
|
25437
|
+
if (hit === null || hit.kind !== "file")
|
|
25352
25438
|
return false;
|
|
25353
|
-
|
|
25354
|
-
|
|
25355
|
-
} catch {
|
|
25356
|
-
return false;
|
|
25357
|
-
}
|
|
25439
|
+
const stats = await safeStat(hit.path);
|
|
25440
|
+
return stats !== null && stats.size > MAX_EXCERPT_BYTES;
|
|
25358
25441
|
}
|
|
25359
|
-
|
|
25360
|
-
|
|
25361
|
-
|
|
25362
|
-
|
|
25363
|
-
|
|
25364
|
-
|
|
25365
|
-
|
|
25366
|
-
|
|
25367
|
-
|
|
25368
|
-
|
|
25369
|
-
|
|
25370
|
-
|
|
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;
|
|
25371
25457
|
let content;
|
|
25372
25458
|
try {
|
|
25373
25459
|
content = readFileSync7(abs, "utf-8");
|
|
25374
25460
|
} catch {
|
|
25375
25461
|
return null;
|
|
25376
25462
|
}
|
|
25377
|
-
|
|
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)
|
|
25378
25477
|
return null;
|
|
25379
|
-
const allLines = content.split(`
|
|
25380
|
-
`);
|
|
25381
|
-
if (allLines.length > 1 && allLines[allLines.length - 1] === "")
|
|
25382
|
-
allLines.pop();
|
|
25383
25478
|
const totalLines = allLines.length;
|
|
25384
25479
|
let startLine;
|
|
25385
25480
|
let endLine;
|
|
@@ -25394,7 +25489,7 @@ function readFileExcerpt(cwd, candidate, line, range) {
|
|
|
25394
25489
|
startLine = 1;
|
|
25395
25490
|
endLine = Math.min(totalLines, EXCERPT_HEAD_LINES);
|
|
25396
25491
|
}
|
|
25397
|
-
const cwdReal = safeRealpath(cwd) ?? cwd;
|
|
25492
|
+
const cwdReal = await safeRealpath(cwd) ?? cwd;
|
|
25398
25493
|
return {
|
|
25399
25494
|
path: relative2(cwdReal, abs),
|
|
25400
25495
|
language: languageForPath(abs),
|
|
@@ -25405,6 +25500,62 @@ function readFileExcerpt(cwd, candidate, line, range) {
|
|
|
25405
25500
|
};
|
|
25406
25501
|
}
|
|
25407
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
|
+
|
|
25408
25559
|
// src/review/decisions.ts
|
|
25409
25560
|
function createDecisions(log = noopLogger) {
|
|
25410
25561
|
const pending = new Map;
|
|
@@ -25451,12 +25602,12 @@ function createDecisions(log = noopLogger) {
|
|
|
25451
25602
|
import { randomBytes } from "crypto";
|
|
25452
25603
|
|
|
25453
25604
|
// src/plan/canonical-file.ts
|
|
25454
|
-
import { appendFileSync, existsSync as existsSync4, statSync as
|
|
25605
|
+
import { appendFileSync, existsSync as existsSync4, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
25455
25606
|
function guardedPlanFileWrite(planFilePath, log, failMsg, write) {
|
|
25456
25607
|
try {
|
|
25457
25608
|
if (!planFilePath.endsWith(".md"))
|
|
25458
25609
|
return;
|
|
25459
|
-
if (!existsSync4(planFilePath) || !
|
|
25610
|
+
if (!existsSync4(planFilePath) || !statSync2(planFilePath).isFile())
|
|
25460
25611
|
return;
|
|
25461
25612
|
write(planFilePath);
|
|
25462
25613
|
} catch (err) {
|
|
@@ -25481,7 +25632,7 @@ import { createHash as createHash2 } from "crypto";
|
|
|
25481
25632
|
import { chmodSync as chmodSync2, existsSync as existsSync5, mkdirSync as mkdirSync2, renameSync, writeFileSync as writeFileSync3 } from "fs";
|
|
25482
25633
|
import { mkdtemp, rm } from "fs/promises";
|
|
25483
25634
|
import { tmpdir } from "os";
|
|
25484
|
-
import { dirname as dirname5, join as
|
|
25635
|
+
import { dirname as dirname5, join as join9 } from "path";
|
|
25485
25636
|
var RUMDL_VERSION = "0.2.47";
|
|
25486
25637
|
var ASSETS = {
|
|
25487
25638
|
"darwin-arm64": {
|
|
@@ -25524,14 +25675,14 @@ async function downloadRumdl(binPath, asset, fetchImpl = fetch) {
|
|
|
25524
25675
|
if (actual !== asset.sha256) {
|
|
25525
25676
|
throw new Error(`rumdl: checksum mismatch for ${asset.url} (expected ${asset.sha256}, got ${actual})`);
|
|
25526
25677
|
}
|
|
25527
|
-
const work = await mkdtemp(
|
|
25678
|
+
const work = await mkdtemp(join9(tmpdir(), "caret-rumdl-"));
|
|
25528
25679
|
try {
|
|
25529
|
-
const archive =
|
|
25680
|
+
const archive = join9(work, "rumdl.tar.gz");
|
|
25530
25681
|
await Bun.write(archive, bytes);
|
|
25531
25682
|
const tar = Bun.spawn(["tar", "-xzf", archive, "-C", work], { stderr: "pipe" });
|
|
25532
25683
|
if (await tar.exited !== 0)
|
|
25533
25684
|
throw new Error(`rumdl: failed to extract ${asset.url}`);
|
|
25534
|
-
const extracted =
|
|
25685
|
+
const extracted = join9(work, "rumdl");
|
|
25535
25686
|
if (!existsSync5(extracted))
|
|
25536
25687
|
throw new Error(`rumdl: binary not found in ${asset.url}`);
|
|
25537
25688
|
mkdirSync2(dirname5(binPath), { recursive: true });
|
|
@@ -25564,8 +25715,9 @@ async function ensureRumdl(install = installPinnedRumdl) {
|
|
|
25564
25715
|
const config2 = rumdlConfig();
|
|
25565
25716
|
writeFileSync3(config2, RUMDL_CONFIG);
|
|
25566
25717
|
const override = process.env.CARET_RUMDL_BIN?.trim();
|
|
25567
|
-
if (override)
|
|
25718
|
+
if (override && await installedVersion(override) === RUMDL_VERSION) {
|
|
25568
25719
|
return { bin: override, config: config2, installed: false };
|
|
25720
|
+
}
|
|
25569
25721
|
const bin = rumdlBin();
|
|
25570
25722
|
if (await installedVersion(bin) === RUMDL_VERSION)
|
|
25571
25723
|
return { bin, config: config2, installed: false };
|
|
@@ -25648,6 +25800,7 @@ async function routeIncomingPlan(input, store, log = noopLogger) {
|
|
|
25648
25800
|
await store.update(latest.id, (r) => {
|
|
25649
25801
|
r.versions.push({ version: version2, plan, annotations: [], createdAt: now });
|
|
25650
25802
|
r.status = "pending";
|
|
25803
|
+
r.cmux = input.cmux ?? r.cmux;
|
|
25651
25804
|
r.decision = undefined;
|
|
25652
25805
|
});
|
|
25653
25806
|
log.info("review", `review appended: ${shortId(latest.id)} v${version2}`, {
|
|
@@ -25674,6 +25827,7 @@ async function routeIncomingPlan(input, store, log = noopLogger) {
|
|
|
25674
25827
|
title: input.title?.trim() || deriveTitle(plan),
|
|
25675
25828
|
status: "pending",
|
|
25676
25829
|
planEpoch,
|
|
25830
|
+
cmux: input.cmux,
|
|
25677
25831
|
versions: [{ version: 1, plan, annotations: [], createdAt: now }],
|
|
25678
25832
|
createdAt: now,
|
|
25679
25833
|
updatedAt: now
|
|
@@ -25760,10 +25914,11 @@ function resolveOptions(opts) {
|
|
|
25760
25914
|
approveVariants: opts.approveVariants,
|
|
25761
25915
|
source: opts.source,
|
|
25762
25916
|
diagnostics: opts.diagnostics,
|
|
25917
|
+
markPaneRead: opts.markPaneRead ?? ((pane) => markPaneRead(pane, { log: opts.log })),
|
|
25763
25918
|
log: opts.log ?? noopLogger
|
|
25764
25919
|
};
|
|
25765
25920
|
}
|
|
25766
|
-
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)?$/;
|
|
25767
25922
|
function matchIdRoute(path) {
|
|
25768
25923
|
const m = path.match(ID_ROUTE_RE);
|
|
25769
25924
|
if (!m)
|
|
@@ -25929,18 +26084,27 @@ function createServer(opts) {
|
|
|
25929
26084
|
return notFound();
|
|
25930
26085
|
const { paths } = await parseBody(req, FileRefsBodySchema);
|
|
25931
26086
|
const seen = new Set;
|
|
25932
|
-
const
|
|
25933
|
-
for (const p of paths
|
|
26087
|
+
const unique = [];
|
|
26088
|
+
for (const p of paths) {
|
|
25934
26089
|
if (seen.has(p))
|
|
25935
26090
|
continue;
|
|
25936
26091
|
seen.add(p);
|
|
25937
|
-
if (
|
|
25938
|
-
|
|
26092
|
+
if (unique.push(p) === MAX_FILE_REFS)
|
|
26093
|
+
break;
|
|
25939
26094
|
}
|
|
25940
|
-
|
|
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
|
+
});
|
|
25941
26105
|
return Response.json({ resolved });
|
|
25942
26106
|
}
|
|
25943
|
-
function handleFileExcerpt(req, id) {
|
|
26107
|
+
async function handleFileExcerpt(req, id) {
|
|
25944
26108
|
const r = store.get(id);
|
|
25945
26109
|
if (!r)
|
|
25946
26110
|
return notFound();
|
|
@@ -25954,10 +26118,25 @@ function createServer(opts) {
|
|
|
25954
26118
|
const start = num("start");
|
|
25955
26119
|
const end = num("end");
|
|
25956
26120
|
const range = start !== undefined && end !== undefined ? { start, end } : undefined;
|
|
25957
|
-
const excerpt = readFileExcerpt(r.cwd, path, line, range);
|
|
26121
|
+
const excerpt = await readFileExcerpt(r.cwd, path, line, range);
|
|
25958
26122
|
if (excerpt)
|
|
25959
26123
|
return Response.json(excerpt);
|
|
25960
|
-
return isFileTooLargeToPreview(r.cwd, path) ? tooLarge() : notFound();
|
|
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);
|
|
25961
26140
|
}
|
|
25962
26141
|
async function handleDecision(id) {
|
|
25963
26142
|
const inMem = store.get(id);
|
|
@@ -26015,6 +26194,10 @@ function createServer(opts) {
|
|
|
26015
26194
|
await store.update(id, (r) => {
|
|
26016
26195
|
r.decision = decision;
|
|
26017
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
|
+
}
|
|
26018
26201
|
r.generalCommentDraft = "";
|
|
26019
26202
|
currentVersion(r).composerScratches = [];
|
|
26020
26203
|
});
|
|
@@ -26027,6 +26210,8 @@ function createServer(opts) {
|
|
|
26027
26210
|
});
|
|
26028
26211
|
}
|
|
26029
26212
|
}
|
|
26213
|
+
if (existing.cmux)
|
|
26214
|
+
cfg.markPaneRead(existing.cmux);
|
|
26030
26215
|
setTimeout(() => resolveDecision(id, decision), 0);
|
|
26031
26216
|
log.info("resolve", `review ${shortId(id)} resolved: ${decision.behavior}`, {
|
|
26032
26217
|
reviewId: id,
|
|
@@ -26047,6 +26232,14 @@ function createServer(opts) {
|
|
|
26047
26232
|
});
|
|
26048
26233
|
return Response.json({ ok: true });
|
|
26049
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
|
+
}
|
|
26050
26243
|
async function dispatch(req, method, path) {
|
|
26051
26244
|
if (method === "GET" && path === "/api/health")
|
|
26052
26245
|
return handleHealth();
|
|
@@ -26073,6 +26266,8 @@ function createServer(opts) {
|
|
|
26073
26266
|
return handleGetReview(id);
|
|
26074
26267
|
if (method === "GET" && sub === "/file")
|
|
26075
26268
|
return handleFileExcerpt(req, id);
|
|
26269
|
+
if (method === "GET" && sub === "/dir")
|
|
26270
|
+
return handleDirListing(req, id);
|
|
26076
26271
|
if (method === "POST" && sub === "/file-refs")
|
|
26077
26272
|
return handleFileRefs(req, id);
|
|
26078
26273
|
if (method === "GET" && sub === "/decision")
|
|
@@ -26083,6 +26278,8 @@ function createServer(opts) {
|
|
|
26083
26278
|
return handleResolve(req, id);
|
|
26084
26279
|
if (method === "POST" && sub === "/expire")
|
|
26085
26280
|
return handleExpire(id);
|
|
26281
|
+
if (method === "POST" && sub === "/seen")
|
|
26282
|
+
return handleSeen(id);
|
|
26086
26283
|
}
|
|
26087
26284
|
if (method === "GET") {
|
|
26088
26285
|
const asset = handleAsset(path);
|
|
@@ -26164,8 +26361,8 @@ function createServer(opts) {
|
|
|
26164
26361
|
}
|
|
26165
26362
|
|
|
26166
26363
|
// src/review/store.ts
|
|
26167
|
-
import { readdir, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
26168
|
-
import { join as
|
|
26364
|
+
import { readdir as readdir3, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
26365
|
+
import { join as join10 } from "path";
|
|
26169
26366
|
function createStore(dir, log = noopLogger) {
|
|
26170
26367
|
const reviews = new Map;
|
|
26171
26368
|
const epochs = new Map;
|
|
@@ -26174,7 +26371,7 @@ function createStore(dir, log = noopLogger) {
|
|
|
26174
26371
|
const prev = writeChains.get(review.id) ?? Promise.resolve();
|
|
26175
26372
|
const next = prev.catch(() => {}).then(async () => {
|
|
26176
26373
|
ensureStateDir(dir);
|
|
26177
|
-
await writeFile2(
|
|
26374
|
+
await writeFile2(join10(dir, `${review.id}.json`), JSON.stringify(review, null, 2), {
|
|
26178
26375
|
mode: 384
|
|
26179
26376
|
});
|
|
26180
26377
|
log.debug("store", `review persisted: ${shortId(review.id)}`, { reviewId: review.id });
|
|
@@ -26224,7 +26421,7 @@ function createStore(dir, log = noopLogger) {
|
|
|
26224
26421
|
return review;
|
|
26225
26422
|
},
|
|
26226
26423
|
async persisted(id) {
|
|
26227
|
-
return await readJsonFile(
|
|
26424
|
+
return await readJsonFile(join10(dir, `${id}.json`)) ?? undefined;
|
|
26228
26425
|
},
|
|
26229
26426
|
size() {
|
|
26230
26427
|
return reviews.size;
|
|
@@ -26241,7 +26438,7 @@ function createStore(dir, log = noopLogger) {
|
|
|
26241
26438
|
async rehydrate() {
|
|
26242
26439
|
let files;
|
|
26243
26440
|
try {
|
|
26244
|
-
files = await
|
|
26441
|
+
files = await readdir3(dir);
|
|
26245
26442
|
} catch {
|
|
26246
26443
|
log.debug("store", "no reviews dir; nothing to rehydrate");
|
|
26247
26444
|
return;
|
|
@@ -26251,7 +26448,7 @@ function createStore(dir, log = noopLogger) {
|
|
|
26251
26448
|
if (!file2.endsWith(".json"))
|
|
26252
26449
|
continue;
|
|
26253
26450
|
try {
|
|
26254
|
-
const review = JSON.parse(await readFile3(
|
|
26451
|
+
const review = JSON.parse(await readFile3(join10(dir, file2), "utf-8"));
|
|
26255
26452
|
if (isUnresolved(review.status)) {
|
|
26256
26453
|
reviews.set(review.id, review);
|
|
26257
26454
|
loaded++;
|
|
@@ -26350,8 +26547,8 @@ import { existsSync as existsSync7 } from "fs";
|
|
|
26350
26547
|
import { release } from "os";
|
|
26351
26548
|
|
|
26352
26549
|
// src/discovery.ts
|
|
26353
|
-
import { readdirSync as
|
|
26354
|
-
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";
|
|
26355
26552
|
var CARET_BIN = "caret-native";
|
|
26356
26553
|
async function safe(build) {
|
|
26357
26554
|
try {
|
|
@@ -26551,7 +26748,7 @@ function listReviewFiles() {
|
|
|
26551
26748
|
const dir = reviewsDir();
|
|
26552
26749
|
let files;
|
|
26553
26750
|
try {
|
|
26554
|
-
files =
|
|
26751
|
+
files = readdirSync3(dir);
|
|
26555
26752
|
} catch {
|
|
26556
26753
|
return [];
|
|
26557
26754
|
}
|
|
@@ -26561,7 +26758,7 @@ function listReviewFiles() {
|
|
|
26561
26758
|
continue;
|
|
26562
26759
|
if (out.length >= 5000)
|
|
26563
26760
|
break;
|
|
26564
|
-
const raw = readJsonFileSync(
|
|
26761
|
+
const raw = readJsonFileSync(join11(dir, file2));
|
|
26565
26762
|
if (raw && typeof raw.id === "string" && typeof raw.status === "string") {
|
|
26566
26763
|
out.push({ id: raw.id, status: raw.status });
|
|
26567
26764
|
}
|
|
@@ -26572,7 +26769,7 @@ var TAIL_BYTES = 256 * 1024;
|
|
|
26572
26769
|
async function logStats(path) {
|
|
26573
26770
|
let size;
|
|
26574
26771
|
try {
|
|
26575
|
-
size =
|
|
26772
|
+
size = statSync3(path).size;
|
|
26576
26773
|
} catch {
|
|
26577
26774
|
return { path, exists: false, size: 0, errors: 0, warns: 0 };
|
|
26578
26775
|
}
|
|
@@ -26665,33 +26862,33 @@ import {
|
|
|
26665
26862
|
existsSync as existsSync9,
|
|
26666
26863
|
mkdirSync as mkdirSync3,
|
|
26667
26864
|
rmSync as rmSync2,
|
|
26668
|
-
statSync as
|
|
26865
|
+
statSync as statSync4,
|
|
26669
26866
|
symlinkSync,
|
|
26670
26867
|
writeFileSync as writeFileSync5
|
|
26671
26868
|
} from "fs";
|
|
26672
|
-
import { join as
|
|
26869
|
+
import { join as join13, resolve as resolve2 } from "path";
|
|
26673
26870
|
|
|
26674
26871
|
// src/adapters/opencode/packaging.ts
|
|
26675
|
-
import { existsSync as existsSync8, readdirSync as
|
|
26676
|
-
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";
|
|
26677
26874
|
function resolveCaretRoot() {
|
|
26678
26875
|
const candidates = [];
|
|
26679
26876
|
const script = process.argv[1];
|
|
26680
26877
|
if (script)
|
|
26681
|
-
candidates.push(
|
|
26682
|
-
candidates.push(
|
|
26878
|
+
candidates.push(join12(dirname7(script), ".."));
|
|
26879
|
+
candidates.push(join12(dirname7(process.execPath), ".."));
|
|
26683
26880
|
for (const root of candidates) {
|
|
26684
|
-
if (existsSync8(
|
|
26881
|
+
if (existsSync8(join12(root, "opencode", "caret.plugin.ts")))
|
|
26685
26882
|
return root;
|
|
26686
26883
|
}
|
|
26687
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.`);
|
|
26688
26885
|
}
|
|
26689
26886
|
function loadOpencodePackaging(root = resolveCaretRoot()) {
|
|
26690
|
-
const binPath =
|
|
26691
|
-
const commandsDir =
|
|
26887
|
+
const binPath = join12(root, "bin", "caret");
|
|
26888
|
+
const commandsDir = join12(root, "opencode", "commands");
|
|
26692
26889
|
let commands = [];
|
|
26693
26890
|
try {
|
|
26694
|
-
commands =
|
|
26891
|
+
commands = readdirSync4(commandsDir).filter((f) => f.endsWith(".md")).sort().map((f) => ({ name: f, contents: readFileSync8(join12(commandsDir, f), "utf-8") }));
|
|
26695
26892
|
} catch {
|
|
26696
26893
|
commands = [];
|
|
26697
26894
|
}
|
|
@@ -26700,17 +26897,17 @@ function loadOpencodePackaging(root = resolveCaretRoot()) {
|
|
|
26700
26897
|
|
|
26701
26898
|
// src/commands/install/local.ts
|
|
26702
26899
|
function devMarketplaceDir() {
|
|
26703
|
-
return
|
|
26900
|
+
return join13(stateDir(), "dev-marketplace");
|
|
26704
26901
|
}
|
|
26705
26902
|
function resolveLocalCheckout(opts = {}, deps = {}) {
|
|
26706
26903
|
const repoDir = resolve2(caretRoot(deps));
|
|
26707
|
-
if (!existsSync9(
|
|
26904
|
+
if (!existsSync9(join13(repoDir, ".claude-plugin", "marketplace.json"))) {
|
|
26708
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\`.`);
|
|
26709
26906
|
}
|
|
26710
26907
|
if (opts.requireArtifacts !== false) {
|
|
26711
26908
|
const missing = [
|
|
26712
|
-
isExecutable(
|
|
26713
|
-
isDirectory(
|
|
26909
|
+
isExecutable(join13(repoDir, "bin", "caret-native")) ? null : "bin/caret-native",
|
|
26910
|
+
isDirectory(join13(repoDir, "bin", "ui")) ? null : "bin/ui"
|
|
26714
26911
|
].filter((m) => m !== null);
|
|
26715
26912
|
if (missing.length > 0) {
|
|
26716
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.`);
|
|
@@ -26729,9 +26926,9 @@ function caretRoot(deps) {
|
|
|
26729
26926
|
}
|
|
26730
26927
|
function writeDevMarketplace(repoDir, outDir) {
|
|
26731
26928
|
rmSync2(outDir, { recursive: true, force: true });
|
|
26732
|
-
mkdirSync3(
|
|
26733
|
-
symlinkSync(resolve2(repoDir),
|
|
26734
|
-
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({
|
|
26735
26932
|
name: "caret",
|
|
26736
26933
|
description: "Local caret dev build (generated by caret install --from-local).",
|
|
26737
26934
|
owner: { name: "macintacos" },
|
|
@@ -26742,20 +26939,20 @@ function writeDevMarketplace(repoDir, outDir) {
|
|
|
26742
26939
|
async function prewarmLocalBuild(repoDir, deps = {}) {
|
|
26743
26940
|
const spawn = deps.spawn ?? runCaptured;
|
|
26744
26941
|
try {
|
|
26745
|
-
await spawn([
|
|
26942
|
+
await spawn([join13(repoDir, "bin", "caret-native"), "prewarm"]);
|
|
26746
26943
|
} catch {}
|
|
26747
26944
|
}
|
|
26748
26945
|
function isExecutable(path) {
|
|
26749
26946
|
try {
|
|
26750
26947
|
accessSync(path, constants.X_OK);
|
|
26751
|
-
return
|
|
26948
|
+
return statSync4(path).isFile();
|
|
26752
26949
|
} catch {
|
|
26753
26950
|
return false;
|
|
26754
26951
|
}
|
|
26755
26952
|
}
|
|
26756
26953
|
function isDirectory(path) {
|
|
26757
26954
|
try {
|
|
26758
|
-
return
|
|
26955
|
+
return statSync4(path).isDirectory();
|
|
26759
26956
|
} catch {
|
|
26760
26957
|
return false;
|
|
26761
26958
|
}
|
|
@@ -27017,7 +27214,7 @@ async function runInstallClaudeTarget(opts, deps = {}) {
|
|
|
27017
27214
|
|
|
27018
27215
|
// src/commands/install/opencode.ts
|
|
27019
27216
|
import { existsSync as existsSync13, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
|
|
27020
|
-
import { basename as basename3, join as
|
|
27217
|
+
import { basename as basename3, join as join15 } from "path";
|
|
27021
27218
|
|
|
27022
27219
|
// src/adapters/opencode/deploy.ts
|
|
27023
27220
|
import { existsSync as existsSync11, mkdirSync as mkdirSync4, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
@@ -27120,7 +27317,7 @@ async function promptUpgrade(verdict, clack) {
|
|
|
27120
27317
|
|
|
27121
27318
|
// src/commands/install/opencode.ts
|
|
27122
27319
|
function isCaretCheckout(dir) {
|
|
27123
|
-
return existsSync13(
|
|
27320
|
+
return existsSync13(join15(dir, "opencode", "caret.plugin.ts"));
|
|
27124
27321
|
}
|
|
27125
27322
|
function caretEntries(text2, isCheckout) {
|
|
27126
27323
|
return pluginEntries(text2).filter((entry) => {
|
|
@@ -27146,7 +27343,7 @@ async function runInstallOpencodeTarget(opts, deps = {}) {
|
|
|
27146
27343
|
const ui = deps.ui ?? silentUI;
|
|
27147
27344
|
const isCheckout = deps.isCheckout ?? isCaretCheckout;
|
|
27148
27345
|
const configFile2 = resolveConfigFile(dir);
|
|
27149
|
-
const commandPaths = pkg.commands.map((c3) =>
|
|
27346
|
+
const commandPaths = pkg.commands.map((c3) => join15(commandDir(dir), namespacedCommandFilename(c3.name)));
|
|
27150
27347
|
const legacy = existingLegacyInstallFiles(dir);
|
|
27151
27348
|
const specifier = opts.local ? localPluginSpecifier(opts.local.repoDir) : CARET_PACKAGE;
|
|
27152
27349
|
if (opts.dryRun) {
|
|
@@ -27169,7 +27366,7 @@ async function runInstallOpencodeTarget(opts, deps = {}) {
|
|
|
27169
27366
|
await upgradeStep(configFile2, opts, deps, ui);
|
|
27170
27367
|
await sweepLegacy(legacy, dir, ui);
|
|
27171
27368
|
const files = pkg.commands.map((c3) => ({
|
|
27172
|
-
path:
|
|
27369
|
+
path: join15(commandDir(dir), namespacedCommandFilename(c3.name)),
|
|
27173
27370
|
contents: renderPlugin(c3.contents, { version: VERSION, binPath: pkg.binPath })
|
|
27174
27371
|
}));
|
|
27175
27372
|
await ui.step("Deploying the /caret:* command files", async () => deployFiles(files, { dryRun: false }), (deployed) => `Deployed ${deployed.paths.length} command file(s) to ${dir}`);
|
|
@@ -28839,7 +29036,10 @@ async function runReview(stdin2, deps) {
|
|
|
28839
29036
|
step = "ensureDaemon";
|
|
28840
29037
|
baseUrl = await deps.ensureDaemon();
|
|
28841
29038
|
step = "postReview";
|
|
28842
|
-
const { id, hasLiveClient } = await deps.postReview(baseUrl,
|
|
29039
|
+
const { id, hasLiveClient } = await deps.postReview(baseUrl, {
|
|
29040
|
+
...input,
|
|
29041
|
+
cmux: deps.readPane?.()
|
|
29042
|
+
});
|
|
28843
29043
|
ctx.reviewId = id;
|
|
28844
29044
|
deps.onPosted?.(baseUrl, id);
|
|
28845
29045
|
logDebug("review", `review created: ${shortId(id)}`, { ...ctx });
|
|
@@ -28906,6 +29106,7 @@ function prodReviewDeps(s, adapter) {
|
|
|
28906
29106
|
postReview,
|
|
28907
29107
|
longPoll,
|
|
28908
29108
|
openBrowser,
|
|
29109
|
+
readPane: readCmuxPane,
|
|
28909
29110
|
timeoutMs: reviewTimeoutMs(s),
|
|
28910
29111
|
expire: expireReview
|
|
28911
29112
|
};
|