claude-review-tool 0.1.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/LICENSE +21 -0
- package/README.md +17 -0
- package/dist/args.js +28 -0
- package/dist/args.js.map +1 -0
- package/dist/capture-schema.js +265 -0
- package/dist/capture-schema.js.map +1 -0
- package/dist/captures.js +86 -0
- package/dist/captures.js.map +1 -0
- package/dist/cli.js +137 -0
- package/dist/cli.js.map +1 -0
- package/dist/early.js +1 -0
- package/dist/errors.js +13 -0
- package/dist/errors.js.map +1 -0
- package/dist/http.js +82 -0
- package/dist/http.js.map +1 -0
- package/dist/init.js +52 -0
- package/dist/init.js.map +1 -0
- package/dist/inject.js +104 -0
- package/dist/inject.js.map +1 -0
- package/dist/intake-message.js +130 -0
- package/dist/intake-message.js.map +1 -0
- package/dist/intake.md +35 -0
- package/dist/overlay.js +215 -0
- package/dist/permissions.js +96 -0
- package/dist/permissions.js.map +1 -0
- package/dist/project.js +19 -0
- package/dist/project.js.map +1 -0
- package/dist/proxy.js +242 -0
- package/dist/proxy.js.map +1 -0
- package/dist/serve.js +114 -0
- package/dist/serve.js.map +1 -0
- package/dist/session-events.js +8 -0
- package/dist/session-events.js.map +1 -0
- package/dist/session-stub.js +232 -0
- package/dist/session-stub.js.map +1 -0
- package/dist/session.js +404 -0
- package/dist/session.js.map +1 -0
- package/dist/sessions.js +293 -0
- package/dist/sessions.js.map +1 -0
- package/dist/target.js +72 -0
- package/dist/target.js.map +1 -0
- package/dist/tasks.js +573 -0
- package/dist/tasks.js.map +1 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Simon (simv)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# claude-review-tool
|
|
2
|
+
|
|
3
|
+
The `crt` CLI for the **Claude Review Tool**: a local proxy that injects an annotation overlay into your running dev site, lets you talk to Claude in the page, and writes self-contained task files to `.crt/tasks/` that any later Claude Code session can complete with `/crt:next`.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
cd my-app && npm run dev # your dev server
|
|
7
|
+
npx claude-review-tool serve --open # proxies it at http://localhost:4400 and opens the browser
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Most people install it through the Claude Code plugin instead, which adds `/crt:serve`, `/crt:next`, `/crt:tasks`, `/crt:task` and `/crt:done`:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
claude plugin marketplace add simv/crt
|
|
14
|
+
claude plugin install crt@crt
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Full documentation — the loop, the task format, script-tag mode, troubleshooting — is in the repository README: https://github.com/simv/crt#readme. Node ≥ 20; Windows, macOS and Linux. MIT.
|
package/dist/args.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function parseArgs(argv) {
|
|
2
|
+
const positionals = [];
|
|
3
|
+
const flags = {};
|
|
4
|
+
for (let i = 0; i < argv.length; i++) {
|
|
5
|
+
const a = argv[i];
|
|
6
|
+
if (a.startsWith("--")) {
|
|
7
|
+
const eq = a.indexOf("=");
|
|
8
|
+
if (eq !== -1) {
|
|
9
|
+
flags[a.slice(2, eq)] = a.slice(eq + 1);
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
const next = argv[i + 1];
|
|
13
|
+
if (next !== undefined && !next.startsWith("--")) {
|
|
14
|
+
flags[a.slice(2)] = next;
|
|
15
|
+
i++;
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
flags[a.slice(2)] = true;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
positionals.push(a);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return { command: positionals[0], positionals: positionals.slice(1), flags };
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=args.js.map
|
package/dist/args.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAOA,MAAM,UAAU,SAAS,CAAC,IAAc;IACtC,MAAM,WAAW,GAAa,EAAE,CAAC;IACjC,MAAM,KAAK,GAAqC,EAAE,CAAC;IACnD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAE,CAAC;QACnB,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACvB,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC1B,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;gBACd,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;YAC1C,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;gBACzB,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;oBACjD,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;oBACzB,CAAC,EAAE,CAAC;gBACN,CAAC;qBAAM,CAAC;oBACN,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;gBAC3B,CAAC;YACH,CAAC;QACH,CAAC;aAAM,CAAC;YACN,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;AAC/E,CAAC"}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capture bundle schema (PRD §6.3, task CRT-0002 Ask 6).
|
|
3
|
+
*
|
|
4
|
+
* One source of truth for the shape of `.crt/captures/<id>/capture.json`: a small JSON-schema-like
|
|
5
|
+
* DSL declares every field with the requirement that demands it, the TypeScript types are inferred
|
|
6
|
+
* from the declaration, and `validateCaptureBundle` checks a bundle at runtime (the server rejects
|
|
7
|
+
* anything else with a 400). The overlay imports only the types.
|
|
8
|
+
*
|
|
9
|
+
* Every `doc` string starts with the F-id that requires the field.
|
|
10
|
+
*/
|
|
11
|
+
function fail(errors, path, expected, value) {
|
|
12
|
+
const got = value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
|
|
13
|
+
errors.push(`${path || "$"}: expected ${expected}, got ${got}`);
|
|
14
|
+
}
|
|
15
|
+
export function str(doc, opts = {}) {
|
|
16
|
+
return {
|
|
17
|
+
doc,
|
|
18
|
+
check(v, path, errors) {
|
|
19
|
+
if (typeof v !== "string")
|
|
20
|
+
return fail(errors, path, "string", v);
|
|
21
|
+
if (opts.max !== undefined && v.length > opts.max) {
|
|
22
|
+
errors.push(`${path}: string longer than ${opts.max} chars`);
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function num(doc) {
|
|
28
|
+
return {
|
|
29
|
+
doc,
|
|
30
|
+
check(v, path, errors) {
|
|
31
|
+
if (typeof v !== "number" || !Number.isFinite(v))
|
|
32
|
+
fail(errors, path, "finite number", v);
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export function bool(doc) {
|
|
37
|
+
return {
|
|
38
|
+
doc,
|
|
39
|
+
check(v, path, errors) {
|
|
40
|
+
if (typeof v !== "boolean")
|
|
41
|
+
fail(errors, path, "boolean", v);
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export function lit(value, doc) {
|
|
46
|
+
return {
|
|
47
|
+
doc,
|
|
48
|
+
check(v, path, errors) {
|
|
49
|
+
if (v !== value)
|
|
50
|
+
fail(errors, path, JSON.stringify(value), v);
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export function oneOf(values, doc) {
|
|
55
|
+
return {
|
|
56
|
+
doc,
|
|
57
|
+
check(v, path, errors) {
|
|
58
|
+
if (typeof v !== "string" || !values.includes(v))
|
|
59
|
+
fail(errors, path, `one of ${values.join("|")}`, v);
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export function nullable(inner) {
|
|
64
|
+
return {
|
|
65
|
+
doc: inner.doc,
|
|
66
|
+
check(v, path, errors) {
|
|
67
|
+
if (v !== null)
|
|
68
|
+
inner.check(v, path, errors);
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
export function optional(inner) {
|
|
73
|
+
return { doc: inner.doc, optional: true, check: (v, p, e) => inner.check(v, p, e) };
|
|
74
|
+
}
|
|
75
|
+
export function arr(item, doc, opts = {}) {
|
|
76
|
+
return {
|
|
77
|
+
doc,
|
|
78
|
+
check(v, path, errors) {
|
|
79
|
+
if (!Array.isArray(v))
|
|
80
|
+
return fail(errors, path, "array", v);
|
|
81
|
+
if (opts.max !== undefined && v.length > opts.max)
|
|
82
|
+
errors.push(`${path}: more than ${opts.max} items`);
|
|
83
|
+
v.forEach((x, i) => item.check(x, `${path}[${i}]`, errors));
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
export function rec(item, doc) {
|
|
88
|
+
return {
|
|
89
|
+
doc,
|
|
90
|
+
check(v, path, errors) {
|
|
91
|
+
if (typeof v !== "object" || v === null || Array.isArray(v))
|
|
92
|
+
return fail(errors, path, "object", v);
|
|
93
|
+
for (const [k, x] of Object.entries(v))
|
|
94
|
+
item.check(x, `${path}.${k}`, errors);
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
export function obj(fields, doc) {
|
|
99
|
+
return {
|
|
100
|
+
doc,
|
|
101
|
+
fields,
|
|
102
|
+
check(v, path, errors) {
|
|
103
|
+
if (typeof v !== "object" || v === null || Array.isArray(v))
|
|
104
|
+
return fail(errors, path, "object", v);
|
|
105
|
+
const o = v;
|
|
106
|
+
for (const [k, s] of Object.entries(fields)) {
|
|
107
|
+
const p = path ? `${path}.${k}` : k;
|
|
108
|
+
if (!(k in o) || o[k] === undefined) {
|
|
109
|
+
if (!s.optional)
|
|
110
|
+
errors.push(`${p}: missing`);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
s.check(o[k], p, errors);
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
// ---------------------------------------------------------------------------------------------
|
|
119
|
+
// Capture bundle
|
|
120
|
+
// ---------------------------------------------------------------------------------------------
|
|
121
|
+
export const CAPTURE_VERSION = 1;
|
|
122
|
+
export const MAX_TEXT_CHARS = 500; // F-17
|
|
123
|
+
export const MAX_HTML_CHARS = 4096; // F-19
|
|
124
|
+
export const MAX_CONSOLE_ENTRIES = 50; // F-20
|
|
125
|
+
export const MAX_NETWORK_ENTRIES = 50; // F-21
|
|
126
|
+
export const MAX_BOX_ELEMENTS = 10; // F-9 (task Ask 3)
|
|
127
|
+
export const MAX_COMPONENTS = 5; // F-18
|
|
128
|
+
export const RectSchema = obj({
|
|
129
|
+
x: num("F-17 left edge, CSS px"),
|
|
130
|
+
y: num("F-17 top edge, CSS px"),
|
|
131
|
+
width: num("F-17 width, CSS px"),
|
|
132
|
+
height: num("F-17 height, CSS px"),
|
|
133
|
+
}, "F-17 a rectangle in viewport coordinates at capture time");
|
|
134
|
+
export const PointSchema = obj({ x: num("F-10 x, CSS px"), y: num("F-10 y, CSS px") }, "F-10 a viewport point");
|
|
135
|
+
export const PageSchema = obj({
|
|
136
|
+
url: str("F-15 full URL as the browser shows it (the CRT origin)"),
|
|
137
|
+
pathname: str("F-15 location.pathname"),
|
|
138
|
+
query: str("F-15 location.search including the leading ? (empty when none)"),
|
|
139
|
+
hash: str("F-15 location.hash including the leading # (empty when none)"),
|
|
140
|
+
title: str("F-15 document.title"),
|
|
141
|
+
viewport: obj({ width: num("F-15 window.innerWidth"), height: num("F-15 window.innerHeight") }, "F-15 viewport size in CSS px"),
|
|
142
|
+
devicePixelRatio: num("F-15 window.devicePixelRatio"),
|
|
143
|
+
userAgent: str("F-15 navigator.userAgent"),
|
|
144
|
+
timestamp: str("F-15 ISO-8601 time the capture started"),
|
|
145
|
+
scroll: obj({ x: num("F-15 window.scrollX"), y: num("F-15 window.scrollY") }, "F-15 scroll offset in CSS px"),
|
|
146
|
+
}, "F-15 page metadata");
|
|
147
|
+
export const FrameworkSchema = obj({
|
|
148
|
+
name: oneOf(["next", "react", "vue", "svelte", "angular", "unknown"], "F-22 detected UI framework; 'next' implies React"),
|
|
149
|
+
version: nullable(str("F-22 framework version when exposed (next.version, Vue app.version, ng-version)")),
|
|
150
|
+
bundler: oneOf(["vite", "webpack", "unknown"], "F-22 detected bundler/dev server"),
|
|
151
|
+
route: nullable(str("F-22 matched route pattern when derivable (Next.js pages router: __NEXT_DATA__.page)")),
|
|
152
|
+
hints: arr(str("F-22 evidence string"), "F-22 what the detection was based on"),
|
|
153
|
+
}, "F-22 framework hints");
|
|
154
|
+
export const ComponentSchema = obj({
|
|
155
|
+
name: str("F-18 component display name"),
|
|
156
|
+
kind: oneOf(["function", "class", "forward_ref", "memo", "server", "unknown"], "F-18 component kind; 'server' is a React Server Component known only by name on the client"),
|
|
157
|
+
}, "F-18 one component in the owner chain, nearest first (the components whose JSX rendered the element)");
|
|
158
|
+
export const SourceSchema = obj({
|
|
159
|
+
file: str("F-18 source file as recorded by the dev build (React _debugSource.fileName, React 19 owner-stack frame, Vue __file)"),
|
|
160
|
+
line: nullable(num("F-18 1-based line number when known (for 'owner_stack' it is a line in the dev-bundled module, so approximate)")),
|
|
161
|
+
column: nullable(num("F-18 1-based column when known")),
|
|
162
|
+
via: oneOf(["debug_source", "owner_stack", "vue_file"], "F-18 how the location was derived: React ≤18 _debugSource (exact), React ≥19 _debugStack owner stack (file exact, line approximate), Vue component __file"),
|
|
163
|
+
}, "F-18 source location of the nearest component that has one");
|
|
164
|
+
export const ElementSchema = obj({
|
|
165
|
+
selector: str("F-17 unique CSS selector (document.querySelectorAll(selector).length === 1 at capture time)"),
|
|
166
|
+
xpath: str("F-17 absolute XPath"),
|
|
167
|
+
tag: str("F-17 lower-case tag name"),
|
|
168
|
+
id: str("F-17 id attribute, empty when none"),
|
|
169
|
+
classes: arr(str("F-17 one class"), "F-17 classList"),
|
|
170
|
+
dataset: rec(str("F-17 data-* value"), "F-17 data-* attributes keyed by dataset name (camelCase)"),
|
|
171
|
+
role: nullable(str("F-17 ARIA role: explicit role attribute, else null")),
|
|
172
|
+
ariaLabel: nullable(str("F-17 aria-label, else null")),
|
|
173
|
+
text: str("F-17 trimmed textContent, whitespace collapsed", { max: MAX_TEXT_CHARS }),
|
|
174
|
+
rect: RectSchema,
|
|
175
|
+
styles: rec(str("F-17 computed value"), "F-17 curated computed-style subset (display, position, size, box, font, color, background)"),
|
|
176
|
+
components: arr(ComponentSchema, "F-18 nearest named components, innermost first", { max: MAX_COMPONENTS }),
|
|
177
|
+
source: nullable(SourceSchema),
|
|
178
|
+
componentFramework: oneOf(["react", "vue", "unknown"], "F-18 which walker produced components/source"),
|
|
179
|
+
outerHtml: str("F-19 element outerHTML, truncated", { max: MAX_HTML_CHARS }),
|
|
180
|
+
parentOuterHtml: str("F-19 parent outerHTML, truncated; empty for the root", { max: MAX_HTML_CHARS }),
|
|
181
|
+
detached: bool("F-12 true when the annotated element was no longer in the document at send time (SPA navigation) and details come from the annotation-time snapshot"),
|
|
182
|
+
}, "F-17/F-18/F-19 everything recorded about one annotated element");
|
|
183
|
+
export const AnnotationSchema = obj({
|
|
184
|
+
n: num("F-11 1-based number shown on the badge"),
|
|
185
|
+
kind: oneOf(["select", "box", "pin"], "F-8/F-9/F-10 tool that created it"),
|
|
186
|
+
note: str("F-11 developer's free-text note (may be empty)"),
|
|
187
|
+
rect: RectSchema,
|
|
188
|
+
point: nullable(PointSchema),
|
|
189
|
+
element: nullable(ElementSchema),
|
|
190
|
+
elements: arr(ElementSchema, "F-9 elements inside a Box, largest first; empty for select/pin", {
|
|
191
|
+
max: MAX_BOX_ELEMENTS,
|
|
192
|
+
}),
|
|
193
|
+
image: nullable(str("F-16 file name of this annotation's crop inside the capture dir (ann-<n>.png); null when rasterisation failed")),
|
|
194
|
+
}, "F-11 one annotation; `rect` is the element box (select), the drawn box (box) or a 1x1 at the point (pin)");
|
|
195
|
+
export const ConsoleEntrySchema = obj({
|
|
196
|
+
level: oneOf(["error", "warn", "uncaught", "unhandledrejection"], "F-20 origin of the entry"),
|
|
197
|
+
message: str("F-20 formatted message, truncated to 2000 chars", { max: 2000 }),
|
|
198
|
+
stack: nullable(str("F-20 stack when an Error was involved, truncated to 4000 chars", { max: 4000 })),
|
|
199
|
+
timestamp: str("F-20 ISO-8601 time"),
|
|
200
|
+
url: nullable(str("F-20 location.href when the entry was recorded")),
|
|
201
|
+
}, "F-20 one console/error entry");
|
|
202
|
+
export const NetworkEntrySchema = obj({
|
|
203
|
+
method: str("F-21 HTTP method, upper-case (GET for resources seen only through PerformanceObserver)"),
|
|
204
|
+
url: str("F-21 request URL as issued, truncated to 2000 chars", { max: 2000 }),
|
|
205
|
+
status: nullable(num("F-21 HTTP status (>= 400); null when no response arrived")),
|
|
206
|
+
error: nullable(str("F-21 error message when the request threw (network failure, CORS, abort); null for a status failure", { max: 500 })),
|
|
207
|
+
via: oneOf(["fetch", "xhr", "resource"], "F-21 how the failure was observed: the fetch hook, the XMLHttpRequest hook, or a PerformanceObserver resource entry with responseStatus (img, script, css, …)"),
|
|
208
|
+
durationMs: nullable(num("F-21 wall time from request start to failure, when known")),
|
|
209
|
+
timestamp: str("F-21 ISO-8601 time the failure was recorded"),
|
|
210
|
+
}, "F-21 one failed network request (status >= 400 or errored) since page load");
|
|
211
|
+
export const ScreenshotsSchema = obj({
|
|
212
|
+
viewport: nullable(str("F-16 clean viewport screenshot file name (viewport.png), null when rasterisation failed")),
|
|
213
|
+
annotated: nullable(str("F-16 viewport screenshot with annotation markers drawn on (viewport-annotated.png)")),
|
|
214
|
+
error: nullable(str("F-16 why screenshots are missing, when they are")),
|
|
215
|
+
}, "F-16 screenshot file names relative to the capture dir");
|
|
216
|
+
export const CaptureBundleSchema = obj({
|
|
217
|
+
version: lit(CAPTURE_VERSION, "schema version"),
|
|
218
|
+
id: str("F-23 capture id, assigned by the server (empty when sent by the overlay)"),
|
|
219
|
+
page: PageSchema,
|
|
220
|
+
framework: FrameworkSchema,
|
|
221
|
+
annotations: arr(AnnotationSchema, "F-11 the frozen annotation set, in badge order"),
|
|
222
|
+
console: arr(ConsoleEntrySchema, "F-20 console.error/warn + uncaught errors since overlay load, oldest first", {
|
|
223
|
+
max: MAX_CONSOLE_ENTRIES,
|
|
224
|
+
}),
|
|
225
|
+
network: arr(NetworkEntrySchema, "F-21 failed network requests since overlay load, oldest first", {
|
|
226
|
+
max: MAX_NETWORK_ENTRIES,
|
|
227
|
+
}),
|
|
228
|
+
screenshots: ScreenshotsSchema,
|
|
229
|
+
}, "F-13 the frozen capture bundle written to .crt/captures/<id>/capture.json");
|
|
230
|
+
/** Returns validation errors (empty when the value is a well-formed CaptureBundle). */
|
|
231
|
+
export function validateCaptureBundle(value) {
|
|
232
|
+
const errors = [];
|
|
233
|
+
CaptureBundleSchema.check(value, "", errors);
|
|
234
|
+
return errors;
|
|
235
|
+
}
|
|
236
|
+
export function isCaptureBundle(value) {
|
|
237
|
+
return validateCaptureBundle(value).length === 0;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* What the overlay POSTs to /__crt/captures (F-13): the bundle plus PNGs as base64.
|
|
241
|
+
* Keys are the file names referenced from the bundle (`viewport.png`, `ann-1.png`, …).
|
|
242
|
+
*/
|
|
243
|
+
export const CapturePostSchema = obj({
|
|
244
|
+
bundle: CaptureBundleSchema,
|
|
245
|
+
images: rec(str("F-16 base64 PNG bytes (no data: prefix)"), "F-16 PNG files keyed by file name"),
|
|
246
|
+
}, "F-13 POST /__crt/captures body");
|
|
247
|
+
export function validateCapturePost(value) {
|
|
248
|
+
const errors = [];
|
|
249
|
+
CapturePostSchema.check(value, "", errors);
|
|
250
|
+
if (errors.length)
|
|
251
|
+
return errors;
|
|
252
|
+
const { bundle, images } = value;
|
|
253
|
+
const names = Object.keys(images);
|
|
254
|
+
for (const name of names) {
|
|
255
|
+
if (!/^[a-z0-9-]+\.png$/.test(name))
|
|
256
|
+
errors.push(`images.${name}: file name must match [a-z0-9-]+.png`);
|
|
257
|
+
}
|
|
258
|
+
const referenced = [bundle.screenshots.viewport, bundle.screenshots.annotated, ...bundle.annotations.map((a) => a.image)];
|
|
259
|
+
for (const ref of referenced) {
|
|
260
|
+
if (ref && !(ref in images))
|
|
261
|
+
errors.push(`images: bundle references ${ref} which was not sent`);
|
|
262
|
+
}
|
|
263
|
+
return errors;
|
|
264
|
+
}
|
|
265
|
+
//# sourceMappingURL=capture-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture-schema.js","sourceRoot":"","sources":["../src/capture-schema.ts"],"names":[],"mappings":"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"}
|
package/dist/captures.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capture store (PRD F-13, F-23): `.crt/captures/<id>/` holds `capture.json` plus the PNGs the
|
|
3
|
+
* overlay sent. Ids are time-ordered so the folder lists chronologically. Captures older than
|
|
4
|
+
* `PRUNE_AFTER_MS` are removed on server start; a capture that became a task has already been
|
|
5
|
+
* moved to `.crt/tasks/assets/<TASK-ID>/` (M3), so pruning only ever hits unsent/abandoned ones.
|
|
6
|
+
*/
|
|
7
|
+
import { randomBytes } from "node:crypto";
|
|
8
|
+
import { mkdirSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { validateCapturePost } from "./capture-schema.js";
|
|
11
|
+
export const CAPTURES_DIRNAME = "captures";
|
|
12
|
+
export const PRUNE_AFTER_MS = 7 * 24 * 60 * 60 * 1000;
|
|
13
|
+
const ID_RE = /^\d{8}-\d{6}-[a-f0-9]{4}$/;
|
|
14
|
+
export function capturesDir(projectRoot) {
|
|
15
|
+
return join(projectRoot, ".crt", CAPTURES_DIRNAME);
|
|
16
|
+
}
|
|
17
|
+
/** `YYYYMMDD-HHMMSS-xxxx` in local time: sortable, readable, and safe on every filesystem. */
|
|
18
|
+
export function newCaptureId(now = new Date()) {
|
|
19
|
+
const p = (n, w = 2) => String(n).padStart(w, "0");
|
|
20
|
+
const stamp = `${now.getFullYear()}${p(now.getMonth() + 1)}${p(now.getDate())}` +
|
|
21
|
+
`-${p(now.getHours())}${p(now.getMinutes())}${p(now.getSeconds())}`;
|
|
22
|
+
return `${stamp}-${randomBytes(2).toString("hex")}`;
|
|
23
|
+
}
|
|
24
|
+
export class CaptureValidationError extends Error {
|
|
25
|
+
errors;
|
|
26
|
+
constructor(errors) {
|
|
27
|
+
super(`invalid capture: ${errors.slice(0, 5).join("; ")}${errors.length > 5 ? ` (+${errors.length - 5} more)` : ""}`);
|
|
28
|
+
this.name = "CaptureValidationError";
|
|
29
|
+
this.errors = errors;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Validate a POST body and write it as `.crt/captures/<id>/`. The bundle's `id` is assigned here;
|
|
34
|
+
* whatever the overlay sent is overwritten. Files are written with `\n` line endings.
|
|
35
|
+
*/
|
|
36
|
+
export function writeCapture(projectRoot, body, now = new Date()) {
|
|
37
|
+
const errors = validateCapturePost(body);
|
|
38
|
+
if (errors.length)
|
|
39
|
+
throw new CaptureValidationError(errors);
|
|
40
|
+
const post = body;
|
|
41
|
+
const id = newCaptureId(now);
|
|
42
|
+
const dir = join(capturesDir(projectRoot), id);
|
|
43
|
+
mkdirSync(dir, { recursive: true });
|
|
44
|
+
const files = [];
|
|
45
|
+
for (const [name, b64] of Object.entries(post.images)) {
|
|
46
|
+
writeFileSync(join(dir, name), Buffer.from(b64, "base64"));
|
|
47
|
+
files.push(name);
|
|
48
|
+
}
|
|
49
|
+
const bundle = { ...post.bundle, id };
|
|
50
|
+
const jsonPath = join(dir, "capture.json");
|
|
51
|
+
writeFileSync(jsonPath, JSON.stringify(bundle, null, 2) + "\n", "utf8");
|
|
52
|
+
files.push("capture.json");
|
|
53
|
+
return { id, dir, jsonPath, files };
|
|
54
|
+
}
|
|
55
|
+
/** Remove capture directories whose mtime is older than `maxAgeMs`. Returns the ids removed. */
|
|
56
|
+
export function pruneCaptures(projectRoot, now = new Date(), maxAgeMs = PRUNE_AFTER_MS) {
|
|
57
|
+
const dir = capturesDir(projectRoot);
|
|
58
|
+
let entries;
|
|
59
|
+
try {
|
|
60
|
+
entries = readdirSync(dir);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
const removed = [];
|
|
66
|
+
for (const name of entries) {
|
|
67
|
+
if (!ID_RE.test(name))
|
|
68
|
+
continue;
|
|
69
|
+
const full = join(dir, name);
|
|
70
|
+
try {
|
|
71
|
+
const st = statSync(full);
|
|
72
|
+
if (!st.isDirectory())
|
|
73
|
+
continue;
|
|
74
|
+
const age = now.getTime() - st.mtimeMs;
|
|
75
|
+
if (age > maxAgeMs) {
|
|
76
|
+
rmSync(full, { recursive: true, force: true });
|
|
77
|
+
removed.push(name);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// A capture vanishing mid-scan is not an error.
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return removed;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=captures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"captures.js","sourceRoot":"","sources":["../src/captures.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CRT command-line entry point.
|
|
4
|
+
*
|
|
5
|
+
* Commands (see docs/PRD.md §6):
|
|
6
|
+
* crt serve [--target <url>] [--port <n>] [--open] F-1, F-5
|
|
7
|
+
* crt init F-35
|
|
8
|
+
* crt tasks [--json] F-33 (also refreshes the README index, F-34)
|
|
9
|
+
* crt task <ID> [--validate] F-33 (F-32 format check)
|
|
10
|
+
*
|
|
11
|
+
* Every failure is one `crt: <message>` line on stderr and a non-zero exit (N-6).
|
|
12
|
+
*/
|
|
13
|
+
import { readFileSync } from "node:fs";
|
|
14
|
+
import { basename, resolve } from "node:path";
|
|
15
|
+
import { fileURLToPath } from "node:url";
|
|
16
|
+
import { parseArgs } from "./args.js";
|
|
17
|
+
import { CrtError } from "./errors.js";
|
|
18
|
+
import { initProject, readConfig } from "./init.js";
|
|
19
|
+
import { findProjectRoot } from "./project.js";
|
|
20
|
+
import { serve } from "./serve.js";
|
|
21
|
+
import { findTaskFile, listTasks, validateTaskText, writeIndex } from "./tasks.js";
|
|
22
|
+
const USAGE = [
|
|
23
|
+
"crt — Claude Review Tool",
|
|
24
|
+
"",
|
|
25
|
+
"Usage:",
|
|
26
|
+
" crt serve [--target <url>] [--port <n>] [--open]",
|
|
27
|
+
" crt init",
|
|
28
|
+
" crt tasks [--json]",
|
|
29
|
+
" crt task <ID> [--validate]",
|
|
30
|
+
].join("\n");
|
|
31
|
+
async function main(argv) {
|
|
32
|
+
const { command, positionals, flags } = parseArgs(argv);
|
|
33
|
+
switch (command) {
|
|
34
|
+
case "serve": {
|
|
35
|
+
const handle = await serve({
|
|
36
|
+
target: stringFlag(flags.target, "--target <url>"),
|
|
37
|
+
port: portFlag(flags.port),
|
|
38
|
+
open: flags.open === true,
|
|
39
|
+
overlayPath: fileURLToPath(new URL("./overlay.js", import.meta.url)),
|
|
40
|
+
intakePromptPath: fileURLToPath(new URL("./intake.md", import.meta.url)),
|
|
41
|
+
});
|
|
42
|
+
const stop = () => {
|
|
43
|
+
void handle.close().then(() => process.exit(0));
|
|
44
|
+
};
|
|
45
|
+
process.once("SIGINT", stop);
|
|
46
|
+
process.once("SIGTERM", stop);
|
|
47
|
+
return -1; // keep running
|
|
48
|
+
}
|
|
49
|
+
case "init": {
|
|
50
|
+
const root = findProjectRoot();
|
|
51
|
+
const r = initProject(root);
|
|
52
|
+
console.log(r.created.length
|
|
53
|
+
? `crt init: created ${r.created.join(", ")}`
|
|
54
|
+
: `crt init: ${root} already initialised`);
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
case "tasks": {
|
|
58
|
+
const tasksDir = tasksDirOf(findProjectRoot());
|
|
59
|
+
const tasks = listTasks(tasksDir);
|
|
60
|
+
writeIndex(tasksDir);
|
|
61
|
+
if (flags.json === true) {
|
|
62
|
+
console.log(JSON.stringify({ tasksDir, tasks }, null, 2));
|
|
63
|
+
return 0;
|
|
64
|
+
}
|
|
65
|
+
if (!tasks.length) {
|
|
66
|
+
console.log(`no tasks in ${tasksDir}`);
|
|
67
|
+
return 0;
|
|
68
|
+
}
|
|
69
|
+
const statusW = Math.max(...tasks.map((t) => t.status.length));
|
|
70
|
+
const priorityW = Math.max(...tasks.map((t) => t.priority.length));
|
|
71
|
+
for (const t of tasks) {
|
|
72
|
+
console.log(`${t.id} ${t.status.padEnd(statusW)} ${t.priority.padEnd(priorityW)} ${t.title} (${t.updated.slice(0, 10)})`);
|
|
73
|
+
}
|
|
74
|
+
const backlog = tasks.filter((t) => t.status === "backlog").length;
|
|
75
|
+
console.log(`${tasks.length} task${tasks.length === 1 ? "" : "s"}, ${backlog} in backlog`);
|
|
76
|
+
return 0;
|
|
77
|
+
}
|
|
78
|
+
case "task": {
|
|
79
|
+
const id = positionals[0];
|
|
80
|
+
if (!id || !/^CRT-\d{4}$/i.test(id))
|
|
81
|
+
throw new CrtError("usage: crt task <CRT-NNNN> [--validate]", 2);
|
|
82
|
+
const file = findTaskFile(tasksDirOf(findProjectRoot()), id);
|
|
83
|
+
if (!file)
|
|
84
|
+
throw new CrtError(`no task ${id.toUpperCase()} in .crt/tasks`);
|
|
85
|
+
const text = readFileSync(file, "utf8");
|
|
86
|
+
if (flags.validate === true) {
|
|
87
|
+
const errors = validateTaskText(text, basename(file));
|
|
88
|
+
if (errors.length) {
|
|
89
|
+
console.error(`crt task: ${basename(file)} does not match the F-32 format:`);
|
|
90
|
+
for (const e of errors)
|
|
91
|
+
console.error(` - ${e}`);
|
|
92
|
+
return 1;
|
|
93
|
+
}
|
|
94
|
+
console.log(`crt task: ${basename(file)} is valid`);
|
|
95
|
+
return 0;
|
|
96
|
+
}
|
|
97
|
+
process.stdout.write(text);
|
|
98
|
+
return 0;
|
|
99
|
+
}
|
|
100
|
+
case undefined:
|
|
101
|
+
console.log(USAGE);
|
|
102
|
+
return 0;
|
|
103
|
+
default:
|
|
104
|
+
console.error(`crt: unknown command "${command}"\n\n${USAGE}`);
|
|
105
|
+
return 1;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function tasksDirOf(root) {
|
|
109
|
+
return resolve(root, readConfig(root).tasksDir);
|
|
110
|
+
}
|
|
111
|
+
function stringFlag(value, usage) {
|
|
112
|
+
if (value === undefined)
|
|
113
|
+
return undefined;
|
|
114
|
+
if (typeof value !== "string" || value === "")
|
|
115
|
+
throw new CrtError(`${usage} needs a value`);
|
|
116
|
+
return value;
|
|
117
|
+
}
|
|
118
|
+
function portFlag(value) {
|
|
119
|
+
if (value === undefined)
|
|
120
|
+
return undefined;
|
|
121
|
+
const n = typeof value === "string" ? Number(value) : NaN;
|
|
122
|
+
if (!Number.isInteger(n) || n < 1 || n > 65535)
|
|
123
|
+
throw new CrtError(`--port must be a number between 1 and 65535`);
|
|
124
|
+
return n;
|
|
125
|
+
}
|
|
126
|
+
main(process.argv.slice(2)).then((code) => {
|
|
127
|
+
if (code >= 0)
|
|
128
|
+
process.exit(code);
|
|
129
|
+
}, (err) => {
|
|
130
|
+
if (err instanceof CrtError) {
|
|
131
|
+
console.error(`crt: ${err.message}`);
|
|
132
|
+
process.exit(err.exitCode);
|
|
133
|
+
}
|
|
134
|
+
console.error(err);
|
|
135
|
+
process.exit(1);
|
|
136
|
+
});
|
|
137
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/early.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";(()=>{function f(){let e=window,t=e.__crt??(e.__crt={});return t.__console??(t.__console={entries:[],installed:!1})}function w(e){let{entries:t}=f();t.push(e),t.length>50&&t.splice(0,t.length-50)}function u(e){if(typeof e=="string")return e;if(e instanceof Error)return`${e.name}: ${e.message}`;try{let t=JSON.stringify(e);return t===void 0?String(e):t}catch{return String(e)}}function g(e){if(e.length===0)return"";let[t,...o]=e;if(typeof t=="string"&&/%[sdifoOc]/.test(t)){let n=0;return[t.replace(/%[sdifoOc]/g,s=>s==="%c"?(n++,""):n<o.length?u(o[n++]):s),...o.slice(n).map(u)].join(" ")}return e.map(u).join(" ")}function a(e,t,o){let n=o instanceof Error?o:t.find(r=>r instanceof Error);w({level:e,message:g(t).slice(0,2e3),stack:n?.stack?n.stack.slice(0,4e3):null,timestamp:new Date().toISOString(),url:location.href})}function d(){let e=f();if(!e.installed){e.installed=!0;for(let t of["error","warn"]){let o=console[t];console[t]=function(...n){try{a(t,n)}catch{}return o.apply(this===void 0?console:this,n)}}window.addEventListener("error",t=>{let o=t.error instanceof Error?t.error.message:t.message||"Uncaught error",n=t.filename?` (${t.filename}:${t.lineno}:${t.colno})`:"";a("uncaught",[o+n],t.error)}),window.addEventListener("unhandledrejection",t=>{let o=t.reason,n=o instanceof Error?o.message:u(o);a("unhandledrejection",[`Unhandled promise rejection: ${n}`],o)})}}function h(){let e=window,t=e.__crt??(e.__crt={});return t.__network??(t.__network={entries:[],installed:!1})}function m(e){if(/\/__crt\//.test(e.url)||/^(data|blob):/i.test(e.url))return;let{entries:t}=h();t.push(e),t.length>50&&t.splice(0,t.length-50)}function l(e,t,o,n,r,s){m({method:t.toUpperCase(),url:String(o).slice(0,2e3),status:n,error:r==null?null:E(r),via:e,durationMs:s===null?null:Math.round(performance.now()-s),timestamp:new Date().toISOString()})}function E(e){return(e instanceof Error?`${e.name}: ${e.message}`:String(e)).slice(0,500)}function k(e,t){return typeof e=="string"?{method:t?.method??"GET",url:e}:e instanceof URL?{method:t?.method??"GET",url:e.href}:{method:t?.method??e.method??"GET",url:e.url}}function p(){let e=h();if(!e.installed){if(e.installed=!0,typeof window.fetch=="function"){let t=window.fetch;window.fetch=function(o,n){let r=performance.now(),s={method:"GET",url:""};try{s=k(o,n)}catch{}let i=t.call(this===void 0?window:this,o,n);return i.then(c=>{c.status>=400&&l("fetch",s.method,s.url,c.status,null,r)},c=>l("fetch",s.method,s.url,null,c,r)),i}}if(typeof XMLHttpRequest=="function"){let t=XMLHttpRequest.prototype,o=t.open,n=t.send,r=new WeakMap;t.open=function(s,i,...c){return r.set(this,{method:String(s),url:String(i),started:0}),o.apply(this,[s,i,...c])},t.send=function(s){let i=r.get(this);return i&&(i.started=performance.now(),this.addEventListener("loadend",()=>{try{this.status>=400?l("xhr",i.method,i.url,this.status,null,i.started):this.status===0&&l("xhr",i.method,i.url,null,"request failed (no response: network error, CORS or abort)",i.started)}catch{}},{once:!0})),n.call(this,s)}}if(typeof PerformanceObserver=="function")try{new PerformanceObserver(o=>{for(let n of o.getEntries()){let r=n.responseStatus;typeof r!="number"||r<400||n.initiatorType==="fetch"||n.initiatorType==="xmlhttprequest"||m({method:"GET",url:n.name.slice(0,2e3),status:r,error:null,via:"resource",durationMs:Math.round(n.duration),timestamp:new Date().toISOString()})}}).observe({type:"resource",buffered:!0})}catch{}}}d();p();})();
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CrtError: a user-facing failure with a single actionable line (PRD N-6).
|
|
3
|
+
* The CLI prints `crt: <message>` to stderr and exits with `exitCode`.
|
|
4
|
+
*/
|
|
5
|
+
export class CrtError extends Error {
|
|
6
|
+
exitCode;
|
|
7
|
+
constructor(message, exitCode = 1) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = "CrtError";
|
|
10
|
+
this.exitCode = exitCode;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,OAAO,QAAS,SAAQ,KAAK;IACxB,QAAQ,CAAS;IAC1B,YAAY,OAAe,EAAE,QAAQ,GAAG,CAAC;QACvC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;CACF"}
|