@macintacos/caret 1.4.1 → 1.5.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 +2 -1
- package/README.md +4 -3
- package/commands/doctor.md +9 -9
- package/dist/cli.js +5926 -4851
- package/hooks/hooks.json +1 -0
- package/mod/band.d.ts +5 -0
- package/mod/hooks/band.tsx +84 -0
- package/opencode/caret.plugin.v2.ts +36 -6
- package/opencode/commands/doctor.md +10 -10
- package/package.json +6 -4
- package/ui/dist/assets/{ada-C5qYipkI.js → ada-X3Y2mlq2.js} +1 -1
- package/ui/dist/assets/{ahk-CA9gdOdF.js → ahk-DwADkxyd.js} +1 -1
- package/ui/dist/assets/{angular-html-yY3ygMZZ.js → angular-html-qVQ4C4o_.js} +1 -1
- package/ui/dist/assets/{angular-ts-CfoJFBTQ.js → angular-ts-EK5Kpiok.js} +1 -1
- package/ui/dist/assets/{apl-DT7k7bw4.js → apl-DkOo45EZ.js} +1 -1
- package/ui/dist/assets/{astro-CJZXIXFd.js → astro-DiCafZrp.js} +1 -1
- package/ui/dist/assets/beancount-C2IN4Vk8.js +1 -0
- package/ui/dist/assets/{blade-D9JiJjrS.js → blade-BTJ7AhWu.js} +1 -1
- package/ui/dist/assets/{c-D0Y9hqRQ.js → c-CsbUy4Dz.js} +1 -1
- package/ui/dist/assets/{chapel-D7yn7jxF.js → chapel-DpoSYw7F.js} +1 -1
- package/ui/dist/assets/{clike-D_6L3tnO.js → clike-xvONpD0Z.js} +1 -1
- package/ui/dist/assets/{cobol-ON3ke5xj.js → cobol-CUJ4b23B.js} +1 -1
- package/ui/dist/assets/{coffee-BYtoZsQ6.js → coffee-BXmFtcUl.js} +1 -1
- package/ui/dist/assets/coq-CGaSfhwR.js +1 -0
- package/ui/dist/assets/{cpp-ksKJapDi.js → cpp-8j7sTms0.js} +1 -1
- package/ui/dist/assets/crystal-Bl6YKCG_.js +1 -0
- package/ui/dist/assets/{crystal-2UizyKGm.js → crystal-DNZCLxLj.js} +1 -1
- package/ui/dist/assets/csharp-dmMxLVLW.js +1 -0
- package/ui/dist/assets/css-qlALwXNw.js +1 -0
- package/ui/dist/assets/dart-CHZY9Xhz.js +1 -0
- package/ui/dist/assets/{dist-Lc2Es7Pl.js → dist--iE_Uraw.js} +1 -1
- package/ui/dist/assets/{dist-CUrPqCJM.js → dist-BUdhGqN6.js} +1 -1
- package/ui/dist/assets/dist-BW8E7IFp.js +12 -0
- package/ui/dist/assets/{dist-DOXluTcG.js → dist-BsN4uwD3.js} +1 -1
- package/ui/dist/assets/{dist-BubrR0xG.js → dist-BtudxTi6.js} +1 -1
- package/ui/dist/assets/dist-BzruqOsf.js +1 -0
- package/ui/dist/assets/dist-C0kJT6Lc.js +1 -0
- package/ui/dist/assets/{dist-CjIRqXSt.js → dist-C5H8xzaz.js} +1 -1
- package/ui/dist/assets/dist-CB20dMA4.js +1 -0
- package/ui/dist/assets/dist-CMFZirm7.js +1 -0
- package/ui/dist/assets/{dist-R5oGZF-x.js → dist-CYb_vx5W.js} +1 -1
- package/ui/dist/assets/{dist-BdX-zXTt.js → dist-Cd-j6irP.js} +1 -1
- package/ui/dist/assets/{dist-TxQM6vOw.js → dist-CrA0chnO.js} +1 -1
- package/ui/dist/assets/{dist-BWCMQAHO.js → dist-D8cv9-6G.js} +1 -1
- package/ui/dist/assets/dist-DjmYaBKK.js +1 -0
- package/ui/dist/assets/{dist-CJI9XKY6.js → dist-DsnAu5ap.js} +1 -1
- package/ui/dist/assets/{dist-DyhOeNTK.js → dist-JmWLG_PM.js} +1 -1
- package/ui/dist/assets/dist-ldnZ9m94.js +1 -0
- package/ui/dist/assets/dotenv-CuH5my32.js +1 -0
- package/ui/dist/assets/{dylan-BCDoL_-p.js → dylan-S7ucR4pR.js} +1 -1
- package/ui/dist/assets/{edge-CQXgfN6f.js → edge-lQhDsIq4.js} +1 -1
- package/ui/dist/assets/{elixir-Bq5Vdfdz.js → elixir-KszFncrP.js} +1 -1
- package/ui/dist/assets/{elm-Drj2uRJz.js → elm-Dqe2R2wa.js} +1 -1
- package/ui/dist/assets/emacs-lisp-DR2vtWaj.js +1 -0
- package/ui/dist/assets/{erb-DR5atT5u.js → erb-BnrfByrh.js} +1 -1
- package/ui/dist/assets/forth-D6RzAl64.js +1 -0
- package/ui/dist/assets/{git-rebase-qqDOUY4y.js → git-rebase-BDscUCID.js} +1 -1
- package/ui/dist/assets/{glimmer-js-swrKPqN7.js → glimmer-js-bhLtRJb5.js} +1 -1
- package/ui/dist/assets/{glimmer-ts-B2b_vXqT.js → glimmer-ts-BYViMEFk.js} +1 -1
- package/ui/dist/assets/{glsl-CuJlzsa0.js → glsl-Cpk2pGFt.js} +1 -1
- package/ui/dist/assets/gn-CAXYAgV-.js +1 -0
- package/ui/dist/assets/{graphql-N5XL4Igl.js → graphql-CkOJ7M-b.js} +1 -1
- package/ui/dist/assets/groovy-CUrWKPia.js +1 -0
- package/ui/dist/assets/{hack-DAjYMpAQ.js → hack-BW0ZnP7T.js} +1 -1
- package/ui/dist/assets/{haml-bCL8XrBO.js → haml-CyPw2pP2.js} +1 -1
- package/ui/dist/assets/handlebars-BLJrIa2a.js +1 -0
- package/ui/dist/assets/{html-C_jnzgd_.js → html-CovmzHZW.js} +1 -1
- package/ui/dist/assets/{html-derivative-BpHLgdTe.js → html-derivative-uUnrNlDA.js} +1 -1
- package/ui/dist/assets/{http-C9gOztuo.js → http-V8-emqXn.js} +1 -1
- package/ui/dist/assets/{hurl-BXoVnMal.js → hurl-BVPscIjK.js} +1 -1
- package/ui/dist/assets/index-Bh5kFacs.css +1 -0
- package/ui/dist/assets/index-CmRCy8ln.js +4402 -0
- package/ui/dist/assets/{java-ChvIR-vI.js → java-B7EAkw4g.js} +1 -1
- package/ui/dist/assets/{javascript-BtM5a1-k.js → javascript-DgfWn5VS.js} +1 -1
- package/ui/dist/assets/javascript-lS2r2ifg.js +1 -0
- package/ui/dist/assets/{jinja-pvNd4qzh.js → jinja-CclKQ1j3.js} +1 -1
- package/ui/dist/assets/{jison-sfvdVpT7.js → jison-3TCso6ry.js} +1 -1
- package/ui/dist/assets/{json-CNZpiPKJ.js → json-Dt0uvard.js} +1 -1
- package/ui/dist/assets/{jsx-DZDMTAk_.js → jsx-BS8w9yi4.js} +1 -1
- package/ui/dist/assets/{julia-BL7qk1iT.js → julia-BUWxYWs0.js} +1 -1
- package/ui/dist/assets/{just-uod8WSY3.js → just-DG7KPqff.js} +1 -1
- package/ui/dist/assets/latex-DsfZsCcM.js +1 -0
- package/ui/dist/assets/{liquid-BawoDryy.js → liquid-CxDSipCW.js} +1 -1
- package/ui/dist/assets/{lua-DWiK1E2i.js → lua-BSmyZdUb.js} +1 -1
- package/ui/dist/assets/{lua-CiA1ziua.js → lua-DPCEkfD_.js} +1 -1
- package/ui/dist/assets/luau-VGWVLoqX.js +1 -0
- package/ui/dist/assets/marko-BWlnpqQw.js +1 -0
- package/ui/dist/assets/{mdc-Bmg-n7wg.js → mdc-eaLNe5kQ.js} +1 -1
- package/ui/dist/assets/moonbit-D9W784E_.js +1 -0
- package/ui/dist/assets/nextflow-CSgcYZcx.js +1 -0
- package/ui/dist/assets/{nextflow-groovy-DJMQeKeT.js → nextflow-groovy-D7IRt6Pp.js} +1 -1
- package/ui/dist/assets/{nginx-BMaDbqW3.js → nginx-C-ORUuei.js} +1 -1
- package/ui/dist/assets/{nginx-6CyLwTv5.js → nginx-WuEiJASv.js} +1 -1
- package/ui/dist/assets/{nim-qQvA9F3H.js → nim-DURQt8pC.js} +1 -1
- package/ui/dist/assets/nsis-viPvXFiq.js +1 -0
- package/ui/dist/assets/{ntriples-B9h1VB_g.js → ntriples-CzMBulkp.js} +1 -1
- package/ui/dist/assets/nushell--0fT3PI1.js +1 -0
- package/ui/dist/assets/{org-CZPJg31I.js → org-DF-_PQyb.js} +1 -1
- package/ui/dist/assets/{perl-BHwT64Dn.js → perl-EsnDEwQm.js} +1 -1
- package/ui/dist/assets/php-qqUCY8u7.js +1 -0
- package/ui/dist/assets/{powershell-BcIto6Sf.js → powershell-D8WpQPaX.js} +1 -1
- package/ui/dist/assets/{powershell-CCSX75yM.js → powershell-QtSnmXcu.js} +1 -1
- package/ui/dist/assets/pug-BqCHZ5xT.js +1 -0
- package/ui/dist/assets/{pug-BVUwFWbq.js → pug-DBzX-Yjm.js} +1 -1
- package/ui/dist/assets/{qml-CLSmNp_i.js → qml-FU0hHOzz.js} +1 -1
- package/ui/dist/assets/{r-EIHOgDPx.js → r-B1R3kKI3.js} +1 -1
- package/ui/dist/assets/{razor-BPii5bUu.js → razor-DFHHY-nP.js} +1 -1
- package/ui/dist/assets/{regexp-CWI8pQZM.js → regexp-bNGHCeT-.js} +1 -1
- package/ui/dist/assets/{rst-IDb7GnpL.js → rst-t_M_4h4V.js} +1 -1
- package/ui/dist/assets/{ruby-JHA-t8UR.js → ruby-CMCln8Kh.js} +1 -1
- package/ui/dist/assets/ruby-ChfNwlCY.js +1 -0
- package/ui/dist/assets/rust-DpiIf5JO.js +1 -0
- package/ui/dist/assets/{sas-Dlys6wEq.js → sas-aj8KmYmR.js} +1 -1
- package/ui/dist/assets/{scss-DQ3TetHN.js → scss-CK4Kg7zq.js} +1 -1
- package/ui/dist/assets/{shellscript-DZ2dKShG.js → shellscript-vERKF4sn.js} +1 -1
- package/ui/dist/assets/{shellsession-CCP03ZF7.js → shellsession-v8u6MLz4.js} +1 -1
- package/ui/dist/assets/{sieve-BAxa3IjF.js → sieve-tLWDfEkZ.js} +1 -1
- package/ui/dist/assets/solr-B78wSQJQ.js +1 -0
- package/ui/dist/assets/{soy-e3nnrv7u.js → soy-DpaBWgD_.js} +1 -1
- package/ui/dist/assets/sql-CHWh70q1.js +1 -0
- package/ui/dist/assets/{stata-COrW6kNx.js → stata-uTaTVul9.js} +1 -1
- package/ui/dist/assets/{surrealql-CYe8f-Bp.js → surrealql-CaHw_Gex.js} +1 -1
- package/ui/dist/assets/{svelte-CuoMH8V_.js → svelte-C9w3Y1KH.js} +1 -1
- package/ui/dist/assets/{templ-BaMURml8.js → templ-BMIzyzvj.js} +1 -1
- package/ui/dist/assets/{tex-Clsc8t8H.js → tex-HiBB66nZ.js} +1 -1
- package/ui/dist/assets/{tiki-AK6FwT8q.js → tiki-CvUEFusT.js} +1 -1
- package/ui/dist/assets/{ts-tags-BAg5C1ub.js → ts-tags-Bq2_8owA.js} +1 -1
- package/ui/dist/assets/{tsx-rMIupNhm.js → tsx-DPhZ_mW8.js} +1 -1
- package/ui/dist/assets/twig-BINOenEN.js +1 -0
- package/ui/dist/assets/{typescript-3GSK8yFX.js → typescript-DpX28Q13.js} +1 -1
- package/ui/dist/assets/typespec-BGhR2SqG.js +1 -0
- package/ui/dist/assets/{typst-CGKkM3pT.js → typst-DKxhFqTr.js} +1 -1
- package/ui/dist/assets/{verilog-7LoJAqYT.js → verilog-CjI9Y6RI.js} +1 -1
- package/ui/dist/assets/{vue-DcXRtF34.js → vue-Rgy4EXPq.js} +1 -1
- package/ui/dist/assets/{vue-html-BIR8K8bh.js → vue-html-DkO7PDTd.js} +1 -1
- package/ui/dist/assets/{vue-vine-IEgILria.js → vue-vine-DytETe0m.js} +1 -1
- package/ui/dist/assets/{xml-CIHnIn95.js → xml-1TgE5Q65.js} +1 -1
- package/ui/dist/assets/{xsl-CaZZ2eEQ.js → xsl-87-g_ngb.js} +1 -1
- package/ui/dist/assets/{yaml-Dj6iHQsO.js → yaml-BrsuA1y0.js} +1 -1
- package/ui/dist/index.html +3 -3
- package/ui/dist/assets/beancount-D-usSTwE.js +0 -1
- package/ui/dist/assets/coq-DuBRPxYg.js +0 -1
- package/ui/dist/assets/crystal-Bsdphhtm.js +0 -1
- package/ui/dist/assets/csharp-oqKa8noW.js +0 -1
- package/ui/dist/assets/css-BiY6DdoY.js +0 -1
- package/ui/dist/assets/dart-CnvKMtbv.js +0 -1
- package/ui/dist/assets/dist-C3j3DCXP.js +0 -1
- package/ui/dist/assets/dist-CfuU_6ln.js +0 -1
- package/ui/dist/assets/dist-D0Ats_4w.js +0 -1
- package/ui/dist/assets/dist-DOswrDst.js +0 -12
- package/ui/dist/assets/dist-DXn_f-MA.js +0 -1
- package/ui/dist/assets/dist-ryZq9scv.js +0 -1
- package/ui/dist/assets/dist-szzEpuzi.js +0 -1
- package/ui/dist/assets/dotenv-_5a1GRtc.js +0 -1
- package/ui/dist/assets/emacs-lisp-c_oH4hRZ.js +0 -1
- package/ui/dist/assets/forth-Cezjo90N.js +0 -1
- package/ui/dist/assets/gn-ilITqXS6.js +0 -1
- package/ui/dist/assets/groovy-CUpZ7dEl.js +0 -1
- package/ui/dist/assets/handlebars-DHKgZM6S.js +0 -1
- package/ui/dist/assets/index-FmL6-rOV.css +0 -1
- package/ui/dist/assets/index-jwD-lIWV.js +0 -6899
- package/ui/dist/assets/javascript-DoL9Wb-Q.js +0 -1
- package/ui/dist/assets/latex-dpi8HpbG.js +0 -1
- package/ui/dist/assets/luau-C2pBCSgt.js +0 -1
- package/ui/dist/assets/marko-BXkudM8D.js +0 -1
- package/ui/dist/assets/moonbit-DgjMoLWr.js +0 -1
- package/ui/dist/assets/nextflow-BJtWHP5T.js +0 -1
- package/ui/dist/assets/nsis-CfSuhXs5.js +0 -1
- package/ui/dist/assets/nushell-DHYfO66s.js +0 -1
- package/ui/dist/assets/php-D8NTgrbg.js +0 -1
- package/ui/dist/assets/pug-Gsm0M45u.js +0 -1
- package/ui/dist/assets/ruby-BjXV7-Qa.js +0 -1
- package/ui/dist/assets/rust-Cfkwpbl8.js +0 -1
- package/ui/dist/assets/solr-BBopId9w.js +0 -1
- package/ui/dist/assets/sql-CkwaWr-O.js +0 -1
- package/ui/dist/assets/twig-DqwvPhpg.js +0 -1
- package/ui/dist/assets/typespec-BRdr0IET.js +0 -1
package/hooks/hooks.json
CHANGED
package/mod/band.d.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// Claude Code's plan-waiting band. caret's CLI owns the link — host, port, login token —
|
|
2
|
+
// and the band's on/off setting, so nothing here repeats caret's rules.
|
|
3
|
+
import type { Hook, Register } from "claude-code";
|
|
4
|
+
import { atom, read, update } from "claude-code";
|
|
5
|
+
|
|
6
|
+
type Shown = { session: string; url: string; owner: string };
|
|
7
|
+
type HookDollar = Parameters<Hook<"tool.call">>[0];
|
|
8
|
+
|
|
9
|
+
const band = atom({ plugin: "caret", key: "band" } as const, null as Shown | null);
|
|
10
|
+
|
|
11
|
+
// $.process.run takes no shell, and Windows cannot exec bin/caret's shebang. The timeout
|
|
12
|
+
// must outlast band-link's review poll (src/commands/band-link.ts) plus its boot.
|
|
13
|
+
const caret = ($: HookDollar, ...args: string[]) =>
|
|
14
|
+
$.process.run(["bash", `${$.plugin.root}/bin/caret`, ...args], { timeoutMs: 15_000 });
|
|
15
|
+
|
|
16
|
+
// A band that breaks must never hold up the review: pass the event on.
|
|
17
|
+
const passThrough = <E, R>(_: unknown, e: E, next: (e: E) => R) => next(e);
|
|
18
|
+
|
|
19
|
+
const bandWhile = async <T,>(
|
|
20
|
+
$: HookDollar,
|
|
21
|
+
session: string,
|
|
22
|
+
signal: AbortSignal,
|
|
23
|
+
review: () => Promise<T>,
|
|
24
|
+
): Promise<T> => {
|
|
25
|
+
// Waits overlap (a backgrounded review_plan, then another call): each clears only its own band.
|
|
26
|
+
const owner = crypto.randomUUID();
|
|
27
|
+
let settled = false;
|
|
28
|
+
const clear = async () => {
|
|
29
|
+
settled = true;
|
|
30
|
+
await update($, band, (cur) => (cur?.owner === owner ? null : cur));
|
|
31
|
+
};
|
|
32
|
+
// An interrupt aborts the signal; nothing promises it settles `review`.
|
|
33
|
+
const onAbort = () => void clear();
|
|
34
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
35
|
+
// Not awaited: the review never waits on the band.
|
|
36
|
+
void caret($, "band-link", "--session", session).then(
|
|
37
|
+
async ({ stdout }) => {
|
|
38
|
+
const url = stdout.trim();
|
|
39
|
+
if (url && !settled) await update($, band, () => ({ session, url, owner }));
|
|
40
|
+
},
|
|
41
|
+
() => {},
|
|
42
|
+
);
|
|
43
|
+
try {
|
|
44
|
+
return await review();
|
|
45
|
+
} finally {
|
|
46
|
+
signal.removeEventListener("abort", onAbort);
|
|
47
|
+
await clear();
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const register: Register = (on) => {
|
|
52
|
+
on("tool.call", { tool: "mcp__plugin_caret_caret__review_plan" }, async ($, e, next) =>
|
|
53
|
+
// The `mcp-` prefix is src/commands/mcp.ts's session id; keep the two in sync.
|
|
54
|
+
bandWhile($, `mcp-${await $.session.id()}`, next.signal, () => next(e)),
|
|
55
|
+
).catch(passThrough);
|
|
56
|
+
// caret's own PermissionRequest hook sits beneath this one and holds the plan until a decision.
|
|
57
|
+
on("classic.PermissionRequest", { tool_name: "ExitPlanMode" }, ($, e, next) =>
|
|
58
|
+
bandWhile($, e.session_id, next.signal, () => next(e)),
|
|
59
|
+
).catch(passThrough);
|
|
60
|
+
on("ui.render", { component: "AbovePrompt" }, async ($, e, next) => {
|
|
61
|
+
const shown = await read($, band);
|
|
62
|
+
if (e.props.hasSurvey || shown === null) return next(e);
|
|
63
|
+
const { Box, Button, Link, Text } = $.ui.resolve(e);
|
|
64
|
+
return (
|
|
65
|
+
<Box gap={1}>
|
|
66
|
+
<Link href={shown.url}>
|
|
67
|
+
<Text bold color="inverseText" backgroundColor="warning">
|
|
68
|
+
{" 🥕 CARET PLAN WAITING FOR YOUR REVIEW "}
|
|
69
|
+
</Text>
|
|
70
|
+
</Link>
|
|
71
|
+
{/* A bare digit in an empty prompt presses a band Button. */}
|
|
72
|
+
<Button
|
|
73
|
+
hotkey="1"
|
|
74
|
+
plain
|
|
75
|
+
onPress={() =>
|
|
76
|
+
void caret($, "band-link", "--session", shown.session, "--open").catch(() => {})
|
|
77
|
+
}
|
|
78
|
+
>
|
|
79
|
+
open in browser
|
|
80
|
+
</Button>
|
|
81
|
+
</Box>
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
};
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
runPlanReview,
|
|
24
24
|
type WarmRunner,
|
|
25
25
|
} from "./caret.core.ts";
|
|
26
|
-
import { editPermitted, type Rule } from "./permission.ts";
|
|
26
|
+
import { editChecks, editPermitted, type Rule } from "./permission.ts";
|
|
27
27
|
import { decisionText, nodeSpawnRunner, type SpawnRunner } from "./review-bridge.ts";
|
|
28
28
|
|
|
29
29
|
/** Appended last, it outranks a config deny-all: v2 disables a tool only when the last
|
|
@@ -91,6 +91,15 @@ export type SetupContext = {
|
|
|
91
91
|
tool: {
|
|
92
92
|
transform: (callback: (tools: { add: (tool: ReviewTool) => void }) => void) => Promise<unknown>;
|
|
93
93
|
};
|
|
94
|
+
/** Hosts ≥ 2.0.29 only. */
|
|
95
|
+
permission?: {
|
|
96
|
+
assert: (input: {
|
|
97
|
+
sessionID: string;
|
|
98
|
+
agent?: string;
|
|
99
|
+
action: string;
|
|
100
|
+
resources: string[];
|
|
101
|
+
}) => Promise<void>;
|
|
102
|
+
};
|
|
94
103
|
};
|
|
95
104
|
|
|
96
105
|
/** The v2 tool-call context fields caret's `execute` reads. */
|
|
@@ -118,6 +127,7 @@ export function createCaretSetup(opts: {
|
|
|
118
127
|
input: unknown,
|
|
119
128
|
context: ToolContext,
|
|
120
129
|
): Promise<{ content: string; metadata?: Record<string, string> }> {
|
|
130
|
+
let declined: unknown;
|
|
121
131
|
try {
|
|
122
132
|
const session = await readSession(context.sessionID).catch(() => undefined);
|
|
123
133
|
const directory = session?.location.directory ?? ctx.location.directory;
|
|
@@ -130,15 +140,34 @@ export function createCaretSetup(opts: {
|
|
|
130
140
|
// Fail-open: an unreadable session is treated as primary, as on v1.
|
|
131
141
|
isSubagent: async () => Boolean(session?.parentID),
|
|
132
142
|
canEdit: async (planFilePath) => {
|
|
143
|
+
const location = { directory, projectDirectory: ctx.location.project.directory };
|
|
144
|
+
const permission = ctx.permission;
|
|
145
|
+
if (typeof permission?.assert === "function") {
|
|
146
|
+
try {
|
|
147
|
+
for (const check of editChecks(planFilePath, location)) {
|
|
148
|
+
await permission.assert({
|
|
149
|
+
sessionID: context.sessionID,
|
|
150
|
+
agent: context.agent,
|
|
151
|
+
action: check.action,
|
|
152
|
+
resources: [check.resource],
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
return true;
|
|
156
|
+
} catch (error) {
|
|
157
|
+
if ((error as { _tag?: unknown } | null)?._tag === "Tool.Error") return false;
|
|
158
|
+
// A plain rejection must end the step, so it escapes the fail-safe catch below.
|
|
159
|
+
declined = error;
|
|
160
|
+
throw error;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
133
163
|
// Session rules can deny, so an unreadable session cannot prove allow.
|
|
134
164
|
if (!session) return false;
|
|
135
165
|
try {
|
|
136
166
|
const agent = await ctx.agent.get({ agentID: context.agent });
|
|
137
|
-
return editPermitted(
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
);
|
|
167
|
+
return editPermitted(planFilePath, location, {
|
|
168
|
+
agent: agent.data.permissions,
|
|
169
|
+
session: session.permissions ?? [],
|
|
170
|
+
});
|
|
142
171
|
} catch {
|
|
143
172
|
return false;
|
|
144
173
|
}
|
|
@@ -155,6 +184,7 @@ export function createCaretSetup(opts: {
|
|
|
155
184
|
if (!decision || context.signal.aborted) return { content: text };
|
|
156
185
|
return { content: text, metadata: { [CARET_DECISION_KEY]: decision.behavior } };
|
|
157
186
|
} catch (error) {
|
|
187
|
+
if (declined !== undefined && error === declined) throw error;
|
|
158
188
|
const message = error instanceof Error ? error.message : String(error);
|
|
159
189
|
return {
|
|
160
190
|
content: decisionText(
|
|
@@ -3,13 +3,13 @@ description: Diagnose this caret install — run caret doctor, relay each failin
|
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
caret's `doctor` subcommand renders a verdict over a one-shot, read-only snapshot of the
|
|
6
|
-
local install:
|
|
7
|
-
read
|
|
8
|
-
settings, review counts, the OpenCode adapter's install-state
|
|
9
|
-
enabled state), log sizes and error counts, recent failures grouped
|
|
10
|
-
codes, and system basics. The report is **always redacted** (home
|
|
11
|
-
foreign usernames censored) and never contains plan, prompt, or feedback
|
|
12
|
-
exists to be shared.
|
|
6
|
+
local install: the failing checks first, each with the command that fixes it, then the
|
|
7
|
+
passing ones, then the state it read them from — running caret processes, daemon identity,
|
|
8
|
+
lock/port state, effective settings, review counts, the OpenCode adapter's install-state
|
|
9
|
+
probe (plugin version, enabled state), log sizes and error counts, recent failures grouped
|
|
10
|
+
by review with their codes, and system basics. The report is **always redacted** (home
|
|
11
|
+
paths become `~`, foreign usernames censored) and never contains plan, prompt, or feedback
|
|
12
|
+
bodies — it exists to be shared.
|
|
13
13
|
|
|
14
14
|
## 1. Run it
|
|
15
15
|
|
|
@@ -47,9 +47,9 @@ one. What each id means:
|
|
|
47
47
|
| `opencode-caret-version` | OpenCode would load a caret behind the published one | the check's remedy verbatim |
|
|
48
48
|
|
|
49
49
|
An `unknown` check is not a failure: it names its own `reason` — no network, an unreadable
|
|
50
|
-
config, or a report section that never collected, which also shows as
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
config, or a report section that never collected, which also shows as an `error` line and
|
|
51
|
+
leaves the check with no detail. Present it as-is. A daemon that is simply not running is
|
|
52
|
+
not one of these — it reports `reachable false` and *passes*.
|
|
53
53
|
|
|
54
54
|
## 3. Present the report
|
|
55
55
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@macintacos/caret",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "A Claude Code plugin that replaces the terminal plan-approval prompt with a local web UI for inline review, annotation, and approval.",
|
|
@@ -28,6 +28,8 @@
|
|
|
28
28
|
"commands/",
|
|
29
29
|
"opencode/",
|
|
30
30
|
"templates/",
|
|
31
|
+
"mod/hooks/band.tsx",
|
|
32
|
+
"mod/band.d.ts",
|
|
31
33
|
".claude-plugin/plugin.json"
|
|
32
34
|
],
|
|
33
35
|
"scripts": {
|
|
@@ -46,13 +48,13 @@
|
|
|
46
48
|
"@codemirror/state": "^6.6.0",
|
|
47
49
|
"@codemirror/view": "^6.43.0",
|
|
48
50
|
"@commander-js/extra-typings": "^15.0.0",
|
|
49
|
-
"@happy-dom/global-registrator": "^
|
|
51
|
+
"@happy-dom/global-registrator": "^21.0.4",
|
|
50
52
|
"@internationalized/date": "^3.12.2",
|
|
51
53
|
"@laststance/tailwind-suggest-canonical-classes": "^0.1.1",
|
|
52
54
|
"@lezer/highlight": "^1.2.3",
|
|
53
55
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
54
56
|
"@opencode/plugin": "^2.0.18",
|
|
55
|
-
"@pierre/diffs": "1.
|
|
57
|
+
"@pierre/diffs": "1.5.2",
|
|
56
58
|
"@pierre/trees": "1.0.0-beta.6",
|
|
57
59
|
"@playwright/test": "^1.62.1",
|
|
58
60
|
"@sveltejs/vite-plugin-svelte": "^7.3.0",
|
|
@@ -64,7 +66,7 @@
|
|
|
64
66
|
"clsx": "^2.1.1",
|
|
65
67
|
"commander": "^15.0.0",
|
|
66
68
|
"cuelume": "^0.2.2",
|
|
67
|
-
"happy-dom": "^
|
|
69
|
+
"happy-dom": "^21.0.4",
|
|
68
70
|
"jsonc-parser": "^3.3.1",
|
|
69
71
|
"listr2": "^11.0.0",
|
|
70
72
|
"marked": "^18.0.4",
|