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/dist/http.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* F-6 script-tag mode: an app on another local origin (`http://localhost:3000`) may call the
|
|
3
|
+
* `/__crt/` API cross-origin. Only loopback origins qualify — `localhost`, `*.localhost`,
|
|
4
|
+
* `127.0.0.1` and `[::1]`, any port, http or https — so a page from the internet cannot.
|
|
5
|
+
*/
|
|
6
|
+
export function isLocalOrigin(origin) {
|
|
7
|
+
if (!origin)
|
|
8
|
+
return false;
|
|
9
|
+
let url;
|
|
10
|
+
try {
|
|
11
|
+
url = new URL(origin);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
if (url.protocol !== "http:" && url.protocol !== "https:")
|
|
17
|
+
return false;
|
|
18
|
+
if (url.origin !== origin)
|
|
19
|
+
return false; // an Origin header is exactly scheme://host[:port]
|
|
20
|
+
const host = url.hostname.toLowerCase();
|
|
21
|
+
return host === "localhost" || host.endsWith(".localhost") || host === "127.0.0.1" || host === "[::1]";
|
|
22
|
+
}
|
|
23
|
+
export const CORS_METHODS = "GET, POST, DELETE, OPTIONS";
|
|
24
|
+
export const CORS_HEADERS = "content-type, last-event-id";
|
|
25
|
+
/**
|
|
26
|
+
* Add the CORS response headers for an allowed local origin (a no-op for same-origin requests,
|
|
27
|
+
* which carry no Origin, and for anything not local). Returns whether the origin was allowed.
|
|
28
|
+
*/
|
|
29
|
+
export function applyCors(req, res) {
|
|
30
|
+
const origin = req.headers.origin;
|
|
31
|
+
if (!isLocalOrigin(origin))
|
|
32
|
+
return false;
|
|
33
|
+
res.setHeader("access-control-allow-origin", origin);
|
|
34
|
+
res.setHeader("access-control-allow-methods", CORS_METHODS);
|
|
35
|
+
res.setHeader("access-control-allow-headers", CORS_HEADERS);
|
|
36
|
+
res.setHeader("access-control-max-age", "600");
|
|
37
|
+
res.setHeader("vary", "origin");
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
export class BodyTooLarge extends Error {
|
|
41
|
+
constructor(limit) {
|
|
42
|
+
super(`body exceeds ${limit} bytes`);
|
|
43
|
+
this.name = "BodyTooLarge";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export function readBody(req, limit) {
|
|
47
|
+
return new Promise((resolve, reject) => {
|
|
48
|
+
const chunks = [];
|
|
49
|
+
let size = 0;
|
|
50
|
+
req.on("data", (c) => {
|
|
51
|
+
size += c.length;
|
|
52
|
+
if (size > limit) {
|
|
53
|
+
reject(new BodyTooLarge(limit));
|
|
54
|
+
req.destroy();
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
chunks.push(c);
|
|
58
|
+
});
|
|
59
|
+
req.on("end", () => resolve(Buffer.concat(chunks)));
|
|
60
|
+
req.on("error", reject);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/** Parse a JSON body; the returned status is what to answer with when it fails. */
|
|
64
|
+
export async function readJson(req, limit) {
|
|
65
|
+
try {
|
|
66
|
+
const raw = await readBody(req, limit);
|
|
67
|
+
return { ok: true, value: raw.length ? JSON.parse(raw.toString("utf8")) : {} };
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
return { ok: false, status: err instanceof BodyTooLarge ? 413 : 400, error: `body: ${err.message}` };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export function json(res, status, body) {
|
|
74
|
+
const text = JSON.stringify(body);
|
|
75
|
+
res.writeHead(status, {
|
|
76
|
+
"content-type": "application/json; charset=utf-8",
|
|
77
|
+
"content-length": String(Buffer.byteLength(text)),
|
|
78
|
+
"cache-control": "no-store",
|
|
79
|
+
});
|
|
80
|
+
res.end(text);
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=http.js.map
|
package/dist/http.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
|
package/dist/init.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `crt init` (PRD F-35): make a project CRT-ready, idempotently.
|
|
3
|
+
* Creates `.crt/tasks/`, writes `.crt/config.json` if absent, and adds `.crt/captures/`
|
|
4
|
+
* to `.gitignore` (creating it if needed). `crt serve` runs this first.
|
|
5
|
+
* The `.gitignore` line is the only write outside `.crt/` the server ever makes (N-5).
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
export const DEFAULT_PORT = 4400;
|
|
10
|
+
export const CAPTURES_IGNORE = ".crt/captures/";
|
|
11
|
+
export const DEFAULT_CONFIG = { tasksDir: ".crt/tasks", target: null, port: DEFAULT_PORT };
|
|
12
|
+
export function initProject(root) {
|
|
13
|
+
const crtDir = join(root, ".crt");
|
|
14
|
+
const tasksDir = join(crtDir, "tasks");
|
|
15
|
+
const configPath = join(crtDir, "config.json");
|
|
16
|
+
const gitignorePath = join(root, ".gitignore");
|
|
17
|
+
const created = [];
|
|
18
|
+
if (!existsSync(tasksDir)) {
|
|
19
|
+
mkdirSync(tasksDir, { recursive: true });
|
|
20
|
+
created.push(tasksDir);
|
|
21
|
+
}
|
|
22
|
+
if (!existsSync(configPath)) {
|
|
23
|
+
writeFileSync(configPath, JSON.stringify(DEFAULT_CONFIG, null, 2) + "\n", "utf8");
|
|
24
|
+
created.push(configPath);
|
|
25
|
+
}
|
|
26
|
+
const ignore = existsSync(gitignorePath) ? readFileSync(gitignorePath, "utf8") : "";
|
|
27
|
+
if (!ignore.split(/\r?\n/).some(isCapturesIgnore)) {
|
|
28
|
+
const sep = ignore === "" || ignore.endsWith("\n") ? "" : "\n";
|
|
29
|
+
writeFileSync(gitignorePath, `${ignore}${sep}${CAPTURES_IGNORE}\n`, "utf8");
|
|
30
|
+
created.push(gitignorePath);
|
|
31
|
+
}
|
|
32
|
+
return { crtDir, tasksDir, configPath, created };
|
|
33
|
+
}
|
|
34
|
+
function isCapturesIgnore(line) {
|
|
35
|
+
const t = line.trim().replace(/^\//, "");
|
|
36
|
+
return t === ".crt/captures/" || t === ".crt/captures" || t === ".crt/" || t === ".crt";
|
|
37
|
+
}
|
|
38
|
+
/** Read `.crt/config.json`, tolerating absence or malformed content (defaults win). */
|
|
39
|
+
export function readConfig(root) {
|
|
40
|
+
try {
|
|
41
|
+
const raw = JSON.parse(readFileSync(join(root, ".crt", "config.json"), "utf8"));
|
|
42
|
+
return {
|
|
43
|
+
tasksDir: typeof raw.tasksDir === "string" ? raw.tasksDir : DEFAULT_CONFIG.tasksDir,
|
|
44
|
+
target: typeof raw.target === "string" && raw.target ? raw.target : null,
|
|
45
|
+
port: typeof raw.port === "number" && Number.isInteger(raw.port) && raw.port > 0 ? raw.port : DEFAULT_PORT,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return { ...DEFAULT_CONFIG };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=init.js.map
|
package/dist/init.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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"}
|
package/dist/inject.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML overlay injection (PRD F-2) and the response-body helpers it needs.
|
|
3
|
+
* Pure functions over strings/buffers so they are unit-testable without a server;
|
|
4
|
+
* proxy.ts decides *when* to call them.
|
|
5
|
+
*/
|
|
6
|
+
import { brotliDecompressSync, gunzipSync, inflateRawSync, inflateSync } from "node:zlib";
|
|
7
|
+
export const OVERLAY_PATH = "/__crt/overlay.js";
|
|
8
|
+
export const OVERLAY_TAG = `<script src="${OVERLAY_PATH}" defer></script>`;
|
|
9
|
+
/** F-20: a ~1 KB blocking script that hooks console/errors before the page's own scripts run. */
|
|
10
|
+
export const EARLY_PATH = "/__crt/early.js";
|
|
11
|
+
export const EARLY_TAG = `<script src="${EARLY_PATH}"></script>`;
|
|
12
|
+
export const INJECT_TAGS = EARLY_TAG + OVERLAY_TAG;
|
|
13
|
+
/** Content-Encodings we can decode in-process; anything else is passed through uninjected. */
|
|
14
|
+
export const SUPPORTED_ENCODINGS = ["gzip", "x-gzip", "deflate", "br", "identity"];
|
|
15
|
+
export function isHtml(contentType) {
|
|
16
|
+
if (!contentType)
|
|
17
|
+
return false;
|
|
18
|
+
const v = Array.isArray(contentType) ? (contentType[0] ?? "") : contentType;
|
|
19
|
+
return /^\s*text\/html\b/i.test(v);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Insert the early-hook + overlay tags before `</head>`, else before `</body>`, else append.
|
|
23
|
+
* Case-insensitive; the first match wins. Idempotent: an existing overlay tag is left alone.
|
|
24
|
+
*/
|
|
25
|
+
export function injectOverlayTag(html, tag = INJECT_TAGS) {
|
|
26
|
+
if (html.includes(OVERLAY_TAG))
|
|
27
|
+
return html;
|
|
28
|
+
if (html.includes(tag))
|
|
29
|
+
return html;
|
|
30
|
+
const head = /<\/head\s*>/i.exec(html);
|
|
31
|
+
if (head)
|
|
32
|
+
return html.slice(0, head.index) + tag + html.slice(head.index);
|
|
33
|
+
const body = /<\/body\s*>/i.exec(html);
|
|
34
|
+
if (body)
|
|
35
|
+
return html.slice(0, body.index) + tag + html.slice(body.index);
|
|
36
|
+
return html + tag;
|
|
37
|
+
}
|
|
38
|
+
/** Decode a response body per its Content-Encoding. Returns null for encodings we can't handle. */
|
|
39
|
+
export function decodeBody(body, contentEncoding) {
|
|
40
|
+
const enc = (contentEncoding ?? "identity").trim().toLowerCase();
|
|
41
|
+
switch (enc) {
|
|
42
|
+
case "":
|
|
43
|
+
case "identity":
|
|
44
|
+
return body;
|
|
45
|
+
case "gzip":
|
|
46
|
+
case "x-gzip":
|
|
47
|
+
return gunzipSync(body);
|
|
48
|
+
case "br":
|
|
49
|
+
return brotliDecompressSync(body);
|
|
50
|
+
case "deflate":
|
|
51
|
+
// Some servers send raw deflate despite the name; try zlib-wrapped first.
|
|
52
|
+
try {
|
|
53
|
+
return inflateSync(body);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return inflateRawSync(body);
|
|
57
|
+
}
|
|
58
|
+
default:
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Narrow a client's Accept-Encoding to what decodeBody can undo, so an upstream
|
|
64
|
+
* never hands us HTML in an encoding we can't inject into (e.g. zstd).
|
|
65
|
+
*/
|
|
66
|
+
export function filterAcceptEncoding(value) {
|
|
67
|
+
if (value === undefined)
|
|
68
|
+
return undefined;
|
|
69
|
+
const wanted = value
|
|
70
|
+
.split(",")
|
|
71
|
+
.map((s) => s.trim().split(";")[0].toLowerCase())
|
|
72
|
+
.filter((s) => SUPPORTED_ENCODINGS.includes(s) && s !== "identity");
|
|
73
|
+
return wanted.length ? wanted.join(", ") : "identity";
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Relax a Content-Security-Policy just enough for the same-origin overlay script:
|
|
77
|
+
* make sure the directive that governs scripts allows 'self'. Dev servers rarely send
|
|
78
|
+
* CSP at all, so this is a cheap safety net rather than a full CSP rewriter.
|
|
79
|
+
*/
|
|
80
|
+
export function relaxCsp(csp) {
|
|
81
|
+
const directives = csp
|
|
82
|
+
.split(";")
|
|
83
|
+
.map((d) => d.trim())
|
|
84
|
+
.filter(Boolean);
|
|
85
|
+
const find = (name) => directives.findIndex((d) => {
|
|
86
|
+
const l = d.toLowerCase();
|
|
87
|
+
return l === name || l.startsWith(name + " ");
|
|
88
|
+
});
|
|
89
|
+
const allowsSelf = (d) => /(^|\s)'self'(\s|$)/i.test(d) || /(^|\s)\*(\s|$)/.test(d);
|
|
90
|
+
const elem = find("script-src-elem");
|
|
91
|
+
const scriptIdx = elem !== -1 ? elem : find("script-src");
|
|
92
|
+
if (scriptIdx !== -1) {
|
|
93
|
+
if (!allowsSelf(directives[scriptIdx]))
|
|
94
|
+
directives[scriptIdx] = `${directives[scriptIdx]} 'self'`;
|
|
95
|
+
return directives.join("; ");
|
|
96
|
+
}
|
|
97
|
+
const defaultIdx = find("default-src");
|
|
98
|
+
if (defaultIdx !== -1 && !allowsSelf(directives[defaultIdx])) {
|
|
99
|
+
const sources = directives[defaultIdx].replace(/^default-src\s*/i, "");
|
|
100
|
+
directives.push(`script-src ${sources} 'self'`.replace(/\s+/g, " "));
|
|
101
|
+
}
|
|
102
|
+
return directives.join("; ");
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=inject.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inject.js","sourceRoot":"","sources":["../src/inject.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The first user message of an intake session (PRD F-24): a readable summary of the capture, the
|
|
3
|
+
* developer's notes, the absolute path of the capture bundle, and the screenshots as image
|
|
4
|
+
* content blocks. Everything else (outer HTML, computed styles, XPath, console entries) stays in
|
|
5
|
+
* capture.json, which the intake instructions tell Claude to read first.
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync, statSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { validateCaptureBundle } from "./capture-schema.js";
|
|
10
|
+
/** The Messages API caps images at 5 MB; stay under it with headroom for base64. */
|
|
11
|
+
export const MAX_IMAGE_BYTES = 3.5 * 1024 * 1024;
|
|
12
|
+
/** Annotated viewport + up to this many per-annotation crops. */
|
|
13
|
+
export const MAX_ANNOTATION_IMAGES = 6;
|
|
14
|
+
export class CaptureNotFoundError extends Error {
|
|
15
|
+
constructor(id, cause) {
|
|
16
|
+
super(`capture ${id} not found: ${cause}`);
|
|
17
|
+
this.name = "CaptureNotFoundError";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function readCaptureBundle(captureDir) {
|
|
21
|
+
let raw;
|
|
22
|
+
try {
|
|
23
|
+
raw = JSON.parse(readFileSync(join(captureDir, "capture.json"), "utf8"));
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
throw new CaptureNotFoundError(captureDir, err.message);
|
|
27
|
+
}
|
|
28
|
+
const errors = validateCaptureBundle(raw);
|
|
29
|
+
if (errors.length)
|
|
30
|
+
throw new CaptureNotFoundError(captureDir, `invalid capture.json (${errors[0]})`);
|
|
31
|
+
return raw;
|
|
32
|
+
}
|
|
33
|
+
/** F-14: appended to the first message of a quick-note session. */
|
|
34
|
+
export const QUICK_NOTE_INSTRUCTIONS = [
|
|
35
|
+
"Quick note (F-14): the developer sent this without opening a chat and is not watching the panel.",
|
|
36
|
+
"Do not propose the definition of done for confirmation — locate the code, decide the DoD yourself, and call write_task directly.",
|
|
37
|
+
"Ask a question only if the note plus the code genuinely cannot tell you what is wanted; asking opens the chat panel for the developer.",
|
|
38
|
+
].join(" ");
|
|
39
|
+
export function buildIntakeMessage(captureDir, bundle = readCaptureBundle(captureDir), opts = {}) {
|
|
40
|
+
const images = [];
|
|
41
|
+
const attach = (name, label) => {
|
|
42
|
+
if (!name || images.length > MAX_ANNOTATION_IMAGES)
|
|
43
|
+
return;
|
|
44
|
+
try {
|
|
45
|
+
const file = join(captureDir, name);
|
|
46
|
+
if (statSync(file).size > MAX_IMAGE_BYTES)
|
|
47
|
+
return;
|
|
48
|
+
images.push({ mediaType: "image/png", data: readFileSync(file).toString("base64"), label });
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// A missing image is not fatal; the text still says which files exist.
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
attach(bundle.screenshots.annotated ?? bundle.screenshots.viewport, bundle.screenshots.annotated ? "viewport (annotated)" : "viewport");
|
|
55
|
+
for (const a of bundle.annotations)
|
|
56
|
+
attach(a.image, `annotation ${a.n}`);
|
|
57
|
+
const text = renderIntakeText(captureDir, bundle, images.map((i) => i.label));
|
|
58
|
+
return { text: opts.quick ? `${text}\n\n${QUICK_NOTE_INSTRUCTIONS}` : text, images };
|
|
59
|
+
}
|
|
60
|
+
/** F-30: the one-line description of a capture shown in the session list. */
|
|
61
|
+
export function summarizeCapture(bundle) {
|
|
62
|
+
const note = bundle.annotations.map((a) => a.note.trim()).find(Boolean);
|
|
63
|
+
const where = bundle.page.pathname + bundle.page.query;
|
|
64
|
+
return note ? truncate(note, 80) : `${bundle.annotations.length} annotation${bundle.annotations.length === 1 ? "" : "s"} on ${where}`;
|
|
65
|
+
}
|
|
66
|
+
export function renderIntakeText(captureDir, b, attached) {
|
|
67
|
+
const p = b.page;
|
|
68
|
+
const lines = [];
|
|
69
|
+
lines.push(`CRT intake for capture ${b.id}.`, `Capture bundle: ${join(captureDir, "capture.json")} (read it first; the PNGs are next to it).`, "");
|
|
70
|
+
lines.push(`Page: ${p.url}`);
|
|
71
|
+
lines.push(` route ${p.pathname}${p.query}${p.hash} · title "${p.title}" · viewport ${p.viewport.width}×${p.viewport.height} @ ${p.devicePixelRatio}x · captured ${p.timestamp}`);
|
|
72
|
+
const fw = b.framework;
|
|
73
|
+
lines.push(`Framework: ${fw.name}${fw.version ? ` ${fw.version}` : ""}${fw.bundler !== "unknown" ? ` (${fw.bundler})` : ""}${fw.route ? ` · route pattern ${fw.route}` : ""}`);
|
|
74
|
+
if (b.console.length) {
|
|
75
|
+
const errors = b.console.filter((c) => c.level !== "warn");
|
|
76
|
+
lines.push(`Console since load: ${b.console.length} entries, ${errors.length} errors${errors[0] ? ` — first: ${truncate(errors[0].message, 160)}` : ""}`);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
lines.push("Console since load: clean");
|
|
80
|
+
}
|
|
81
|
+
if (b.network.length) {
|
|
82
|
+
const first = b.network[0];
|
|
83
|
+
const what = first.status !== null ? `${first.status}` : (first.error ?? "failed");
|
|
84
|
+
lines.push(`Failed network requests since load: ${b.network.length} — first: ${first.method} ${truncate(first.url, 120)} → ${what}`);
|
|
85
|
+
}
|
|
86
|
+
lines.push("", `Annotations (${b.annotations.length}):`);
|
|
87
|
+
for (const a of b.annotations)
|
|
88
|
+
lines.push(...describeAnnotation(a));
|
|
89
|
+
lines.push("");
|
|
90
|
+
lines.push(attached.length ? `Attached images: ${attached.join(", ")}.` : "No screenshots could be rasterised for this capture.");
|
|
91
|
+
if (b.screenshots.error)
|
|
92
|
+
lines.push(`Screenshot note: ${b.screenshots.error}`);
|
|
93
|
+
return lines.join("\n");
|
|
94
|
+
}
|
|
95
|
+
function describeAnnotation(a) {
|
|
96
|
+
const note = a.note.trim() ? `"${a.note.trim()}"` : "(no note)";
|
|
97
|
+
const out = [];
|
|
98
|
+
if (a.kind === "select" && a.element) {
|
|
99
|
+
const el = a.element;
|
|
100
|
+
out.push(`${a.n}. [select] ${note}`);
|
|
101
|
+
out.push(` element: <${el.tag}${el.id ? ` id="${el.id}"` : ""}${el.classes.length ? ` class="${el.classes.join(" ")}"` : ""}>${el.role ? ` role=${el.role}` : ""}${el.ariaLabel ? ` aria-label="${el.ariaLabel}"` : ""}${el.detached ? " (detached at send time)" : ""}`);
|
|
102
|
+
out.push(` selector: ${el.selector}`);
|
|
103
|
+
if (el.text)
|
|
104
|
+
out.push(` text: ${JSON.stringify(truncate(el.text, 120))}`);
|
|
105
|
+
if (el.components.length)
|
|
106
|
+
out.push(` components: ${el.components.map((c) => `${c.name}${c.kind === "server" ? " (server)" : ""}`).join(" ← ")}`);
|
|
107
|
+
if (el.source)
|
|
108
|
+
out.push(` source: ${el.source.file}${el.source.line ? `:${el.source.line}` : ""} (${el.source.via}${el.source.via === "owner_stack" ? ", line approximate" : ""})`);
|
|
109
|
+
const size = `${Math.round(el.rect.width)}×${Math.round(el.rect.height)} at (${Math.round(el.rect.x)}, ${Math.round(el.rect.y)})`;
|
|
110
|
+
out.push(` box: ${size}; display ${el.styles.display ?? "?"}, position ${el.styles.position ?? "?"}`);
|
|
111
|
+
}
|
|
112
|
+
else if (a.kind === "box") {
|
|
113
|
+
out.push(`${a.n}. [box] ${note}`);
|
|
114
|
+
out.push(` region: ${Math.round(a.rect.width)}×${Math.round(a.rect.height)} at (${Math.round(a.rect.x)}, ${Math.round(a.rect.y)}), ${a.elements.length} element${a.elements.length === 1 ? "" : "s"} inside`);
|
|
115
|
+
for (const el of a.elements.slice(0, 5)) {
|
|
116
|
+
const comp = el.components[0] ? ` — ${el.components[0].name}` : "";
|
|
117
|
+
out.push(` - ${el.selector}${comp}${el.text ? ` ${JSON.stringify(truncate(el.text, 60))}` : ""}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
out.push(`${a.n}. [pin] ${note}`);
|
|
122
|
+
out.push(` point: (${Math.round(a.rect.x)}, ${Math.round(a.rect.y)}) in the viewport — no element bound; look at the crop`);
|
|
123
|
+
}
|
|
124
|
+
return out;
|
|
125
|
+
}
|
|
126
|
+
function truncate(s, max) {
|
|
127
|
+
const t = s.replace(/\s+/g, " ").trim();
|
|
128
|
+
return t.length > max ? `${t.slice(0, max - 1)}…` : t;
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=intake-message.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intake-message.js","sourceRoot":"","sources":["../src/intake-message.ts"],"names":[],"mappings":"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"}
|
package/dist/intake.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: intake
|
|
3
|
+
description: Turn a CRT capture (annotated page + notes) into a self-contained task file with a definition of done. Used automatically by the CRT in-page chat; invoke manually with a capture path to run intake from the terminal.
|
|
4
|
+
argument-hint: "<path-to-capture-dir>"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are running CRT intake (PRD §6.4). Your job is to understand exactly what the developer is pointing at, gather the context a future session will need, agree a definition of done, and write one task file. You do **not** fix the problem now.
|
|
8
|
+
|
|
9
|
+
The capture lives at `$ARGUMENTS` (a directory containing `capture.json` and PNG screenshots). If invoked from the in-page chat, the first message already contains the capture summary and images.
|
|
10
|
+
|
|
11
|
+
## Steps
|
|
12
|
+
|
|
13
|
+
1. **Read the capture.** Open `capture.json`. Look at every screenshot. Note the URL, route, viewport, each annotation's note, selector, component chain, source file hints, outer HTML, and any console errors.
|
|
14
|
+
|
|
15
|
+
2. **Locate the code.** Using the component names, source hints, selectors, class names and visible text, find the file(s) that render each annotated element and the code that produces the observed state. Read enough surrounding code to explain *why* the page shows what it shows. Prefer `Grep`/`Glob`/`Read`; do not modify source.
|
|
16
|
+
|
|
17
|
+
3. **Clarify only if necessary.** If the developer's ask or the definition of done cannot be pinned down from the notes plus the code, ask at most three short questions in one message and wait. Otherwise do not ask.
|
|
18
|
+
|
|
19
|
+
4. **Propose the definition of done.** Reply with a one-paragraph restatement of the ask and a checklist of concrete, checkable DoD items (behavioural outcome, tests, no regressions). Wait for the developer to accept or edit.
|
|
20
|
+
|
|
21
|
+
**Quick-note mode (PRD F-14).** When the first message ends with a paragraph starting `Quick note (F-14)`, the developer sent their notes without opening the chat and is not watching. Skip the wait in step 4 and go straight to step 5 with the DoD you decided. Only fall back to step 3 if the notes plus the code genuinely do not say what is wanted; a question opens the panel for the developer, and the task is written once they answer.
|
|
22
|
+
|
|
23
|
+
5. **Write the task.**
|
|
24
|
+
- **In-page intake** (a `write_task` tool from the `crt` MCP server is available): call it exactly once with `title`, `summary`, `context` (reproduction, component, file:line), `ask`, `definitionOfDone` (one checkable item per entry), `notes`, `priority`, `tags`, and `files` (every project-relative source file you identified). The CRT server allocates the `CRT-NNNN` id, renders the F-32 file, moves the capture's screenshots to `.crt/tasks/assets/CRT-NNNN/`, fills **Evidence** from the capture, sets `session:` to this session, and regenerates the index. Do not write the file yourself.
|
|
25
|
+
- **Terminal intake** (no `write_task` tool): allocate the next ID by scanning `.crt/tasks/` for the highest `CRT-NNNN` and adding one. Create `.crt/tasks/CRT-NNNN-<slug>.md` in exactly the PRD F-32 format (frontmatter, then Summary, Context, Evidence, Ask, Definition of Done, Notes, Log). Move the capture's files to `.crt/tasks/assets/CRT-NNNN/` and reference the images from **Evidence**. List every source file you identified in `files:`. Set `session:` to your session ID (`${CLAUDE_SESSION_ID}`). Add the first Log entry. Then run `crt task CRT-NNNN --validate` and fix anything it reports, and `crt tasks` to regenerate the index — where `crt` is `npx --no crt` if the project has it installed, else `npx -y claude-review-tool@latest`.
|
|
26
|
+
|
|
27
|
+
6. **Confirm.** Reply with the task ID, the file path, and the DoD. Nothing else.
|
|
28
|
+
|
|
29
|
+
## Permissions during in-page intake
|
|
30
|
+
|
|
31
|
+
Reads, searches and read-only `git` commands run without prompting; every other command prompts the developer in the page with Allow / Deny and defaults to Deny after five minutes. Prefer reading code over running it: do not run builds, tests or the dev server unless the definition of done cannot be written without the result.
|
|
32
|
+
|
|
33
|
+
## Quality bar
|
|
34
|
+
|
|
35
|
+
The task must be workable by a session that has never seen this page, this conversation, or you. If you would need to ask the developer something to start work, that question must be answered in the file. Concrete beats complete: exact selectors, exact file paths with line numbers, exact expected values.
|