@jpkme/diff-review 0.1.15 → 0.1.17
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/README.md +22 -14
- package/bin/diff-review.mjs +192 -21
- package/lib/acp-policy.mjs +10 -2
- package/lib/acp-policy.test.mjs +27 -0
- package/lib/acp.mjs +86 -10
- package/lib/agent-login.mjs +848 -36
- package/lib/agent-login.test.mjs +538 -0
- package/lib/agents.mjs +0 -39
- package/lib/agents.test.mjs +17 -24
- package/lib/extract-cache.test.mjs +162 -162
- package/lib/git.mjs +25 -9
- package/lib/model-label.mjs +20 -4
- package/lib/model-label.test.mjs +15 -2
- package/lib/output-name.mjs +7 -0
- package/lib/prompt.mjs +14 -11
- package/lib/prompt.test.mjs +49 -0
- package/lib/review-dir.mjs +14 -1
- package/lib/review-meta.mjs +11 -3
- package/lib/review-meta.test.mjs +19 -2
- package/lib/review-prep.mjs +63 -63
- package/lib/review.mjs +36 -4
- package/lib/review.test.mjs +35 -0
- package/lib/validate.mjs +11 -1
- package/lib/validate.test.mjs +47 -0
- package/lib/wait-output.test.mjs +1 -1
- package/package.json +2 -2
- package/schema.json +16 -1
- package/web/build/client/_app/immutable/assets/0.B04dzd8P.css +1 -0
- package/web/build/client/_app/immutable/assets/0.B04dzd8P.css.br +0 -0
- package/web/build/client/_app/immutable/assets/0.B04dzd8P.css.gz +0 -0
- package/web/build/client/_app/immutable/assets/2.CQmxVXnC.css +1 -0
- package/web/build/client/_app/immutable/assets/2.CQmxVXnC.css.br +0 -0
- package/web/build/client/_app/immutable/assets/2.CQmxVXnC.css.gz +0 -0
- package/web/build/client/_app/immutable/assets/3.BVJgADrM.css +1 -0
- package/web/build/client/_app/immutable/assets/3.BVJgADrM.css.br +0 -0
- package/web/build/client/_app/immutable/assets/3.BVJgADrM.css.gz +0 -0
- package/web/build/client/_app/immutable/assets/storage.Ztmr3s84.css +1 -0
- package/web/build/client/_app/immutable/assets/storage.Ztmr3s84.css.br +0 -0
- package/web/build/client/_app/immutable/assets/storage.Ztmr3s84.css.gz +0 -0
- package/web/build/client/_app/immutable/chunks/B-2kfLeA.js +1 -0
- package/web/build/client/_app/immutable/chunks/B-2kfLeA.js.br +0 -0
- package/web/build/client/_app/immutable/chunks/B-2kfLeA.js.gz +0 -0
- package/web/build/client/_app/immutable/chunks/B4SiKXUO.js +2 -0
- package/web/build/client/_app/immutable/chunks/B4SiKXUO.js.br +0 -0
- package/web/build/client/_app/immutable/chunks/B4SiKXUO.js.gz +0 -0
- package/web/build/client/_app/immutable/chunks/{D8WOD_eS.js → DA0YnpGR.js} +3 -3
- package/web/build/client/_app/immutable/chunks/DA0YnpGR.js.br +0 -0
- package/web/build/client/_app/immutable/chunks/DA0YnpGR.js.gz +0 -0
- package/web/build/client/_app/immutable/chunks/{BpVtXA-T.js → Dv85Hz5p.js} +1 -1
- package/web/build/client/_app/immutable/chunks/Dv85Hz5p.js.br +2 -0
- package/web/build/client/_app/immutable/chunks/Dv85Hz5p.js.gz +0 -0
- package/web/build/client/_app/immutable/chunks/b-BaDYIc.js +215 -0
- package/web/build/client/_app/immutable/chunks/b-BaDYIc.js.br +0 -0
- package/web/build/client/_app/immutable/chunks/b-BaDYIc.js.gz +0 -0
- package/web/build/client/_app/immutable/entry/app.DPUME9Z_.js +2 -0
- package/web/build/client/_app/immutable/entry/app.DPUME9Z_.js.br +0 -0
- package/web/build/client/_app/immutable/entry/app.DPUME9Z_.js.gz +0 -0
- package/web/build/client/_app/immutable/entry/start.hH87UZsD.js +1 -0
- package/web/build/client/_app/immutable/entry/start.hH87UZsD.js.br +0 -0
- package/web/build/client/_app/immutable/entry/start.hH87UZsD.js.gz +0 -0
- package/web/build/client/_app/immutable/nodes/0.BhHA-Ohy.js +1 -0
- package/web/build/client/_app/immutable/nodes/0.BhHA-Ohy.js.br +0 -0
- package/web/build/client/_app/immutable/nodes/0.BhHA-Ohy.js.gz +0 -0
- package/web/build/client/_app/immutable/nodes/1.jjvGFUxu.js +1 -0
- package/web/build/client/_app/immutable/nodes/1.jjvGFUxu.js.br +2 -0
- package/web/build/client/_app/immutable/nodes/1.jjvGFUxu.js.gz +0 -0
- package/web/build/client/_app/immutable/nodes/2.CJTh6CGA.js +12 -0
- package/web/build/client/_app/immutable/nodes/2.CJTh6CGA.js.br +0 -0
- package/web/build/client/_app/immutable/nodes/2.CJTh6CGA.js.gz +0 -0
- package/web/build/client/_app/immutable/nodes/3.DYAPRc_2.js +340 -0
- package/web/build/client/_app/immutable/nodes/3.DYAPRc_2.js.br +0 -0
- package/web/build/client/_app/immutable/nodes/3.DYAPRc_2.js.gz +0 -0
- package/web/build/client/_app/version.json +1 -1
- package/web/build/client/_app/version.json.br +0 -0
- package/web/build/client/_app/version.json.gz +0 -0
- package/web/build/handler.js +3 -3
- package/web/build/index.js +3 -3
- package/web/build/server/chunks/0-8NUeB9cB.js +11 -0
- package/web/build/server/chunks/{0-DUStDOsr.js.map → 0-8NUeB9cB.js.map} +1 -1
- package/web/build/server/chunks/1-BApN-ILJ.js +7 -0
- package/web/build/server/chunks/{1-DUj63QTT.js.map → 1-BApN-ILJ.js.map} +1 -1
- package/web/build/server/chunks/2-Bq7iMeQx.js +7 -0
- package/web/build/server/chunks/2-Bq7iMeQx.js.map +1 -0
- package/web/build/server/chunks/3-W0cqofyE.js +7 -0
- package/web/build/server/chunks/{3-C7UD6rei.js.map → 3-W0cqofyE.js.map} +1 -1
- package/web/build/server/chunks/_server.ts-Bu1fdGSB.js +123 -0
- package/web/build/server/chunks/_server.ts-Bu1fdGSB.js.map +1 -0
- package/web/build/server/chunks/{_server.ts-4RcoUd0l.js → _server.ts-BuUNechu.js} +5 -4
- package/web/build/server/chunks/_server.ts-BuUNechu.js.map +1 -0
- package/web/build/server/chunks/{agent-sessions-C_hlZJKN.js → agent-sessions-BArYLTW6.js} +1103 -146
- package/web/build/server/chunks/agent-sessions-BArYLTW6.js.map +1 -0
- package/web/build/server/chunks/{git-review-Bh1w0gak.js → git-review-BJ_JrFNP.js} +87 -26
- package/web/build/server/chunks/git-review-BJ_JrFNP.js.map +1 -0
- package/web/build/server/chunks/{handler-e2lEvjVT.js → handler-Df_bJiCq.js} +3 -3
- package/web/build/server/chunks/{handler-e2lEvjVT.js.map → handler-Df_bJiCq.js.map} +1 -1
- package/web/build/server/chunks/{hooks.server-Um1Sknmz.js → hooks.server-BEl7rVad.js} +4 -3
- package/web/build/server/chunks/{hooks.server-Um1Sknmz.js.map → hooks.server-BEl7rVad.js.map} +1 -1
- package/web/build/server/chunks/{index.js-C3-YX8SA.js → index.js-Bb0CZLZ_.js} +884 -881
- package/web/build/server/chunks/index.js-Bb0CZLZ_.js.map +1 -0
- package/web/build/server/chunks/manifest.js-j442duM7.js +65 -0
- package/web/build/server/chunks/manifest.js-j442duM7.js.map +1 -0
- package/web/build/server/chunks/{remote-1g4ayag-DMiPy1mT.js → remote-1g4ayag-BxKnZQPn.js} +6 -6
- package/web/build/server/chunks/remote-1g4ayag-BxKnZQPn.js.map +1 -0
- package/web/build/server/chunks/{remote-wh6t6j-RlIVOUe-.js → remote-wh6t6j-B-AHrGaN.js} +58 -12
- package/web/build/server/chunks/remote-wh6t6j-B-AHrGaN.js.map +1 -0
- package/web/build/server/chunks/{validate-CAQ31C7k.js → validate-CXjTRGnR.js} +67 -21
- package/web/build/server/chunks/validate-CXjTRGnR.js.map +1 -0
- package/web/build/client/_app/immutable/assets/0.BKRG1f36.css +0 -1
- package/web/build/client/_app/immutable/assets/0.BKRG1f36.css.br +0 -0
- package/web/build/client/_app/immutable/assets/0.BKRG1f36.css.gz +0 -0
- package/web/build/client/_app/immutable/assets/2.C4whE4xz.css +0 -1
- package/web/build/client/_app/immutable/assets/2.C4whE4xz.css.br +0 -0
- package/web/build/client/_app/immutable/assets/2.C4whE4xz.css.gz +0 -0
- package/web/build/client/_app/immutable/assets/3.Cw1J6YTM.css +0 -1
- package/web/build/client/_app/immutable/assets/3.Cw1J6YTM.css.br +0 -0
- package/web/build/client/_app/immutable/assets/3.Cw1J6YTM.css.gz +0 -0
- package/web/build/client/_app/immutable/assets/storage.TlsiS-AP.css +0 -1
- package/web/build/client/_app/immutable/assets/storage.TlsiS-AP.css.br +0 -0
- package/web/build/client/_app/immutable/assets/storage.TlsiS-AP.css.gz +0 -0
- package/web/build/client/_app/immutable/chunks/BpVtXA-T.js.br +0 -2
- package/web/build/client/_app/immutable/chunks/BpVtXA-T.js.gz +0 -0
- package/web/build/client/_app/immutable/chunks/CUhrLa7d.js +0 -210
- package/web/build/client/_app/immutable/chunks/CUhrLa7d.js.br +0 -0
- package/web/build/client/_app/immutable/chunks/CUhrLa7d.js.gz +0 -0
- package/web/build/client/_app/immutable/chunks/D8WOD_eS.js.br +0 -0
- package/web/build/client/_app/immutable/chunks/D8WOD_eS.js.gz +0 -0
- package/web/build/client/_app/immutable/chunks/QvO6K-HK.js +0 -2
- package/web/build/client/_app/immutable/chunks/QvO6K-HK.js.br +0 -0
- package/web/build/client/_app/immutable/chunks/QvO6K-HK.js.gz +0 -0
- package/web/build/client/_app/immutable/chunks/lUYXRDtZ.js +0 -1
- package/web/build/client/_app/immutable/chunks/lUYXRDtZ.js.br +0 -0
- package/web/build/client/_app/immutable/chunks/lUYXRDtZ.js.gz +0 -0
- package/web/build/client/_app/immutable/entry/app.BjJsHui4.js +0 -2
- package/web/build/client/_app/immutable/entry/app.BjJsHui4.js.br +0 -0
- package/web/build/client/_app/immutable/entry/app.BjJsHui4.js.gz +0 -0
- package/web/build/client/_app/immutable/entry/start.D4unRHC4.js +0 -1
- package/web/build/client/_app/immutable/entry/start.D4unRHC4.js.br +0 -0
- package/web/build/client/_app/immutable/entry/start.D4unRHC4.js.gz +0 -0
- package/web/build/client/_app/immutable/nodes/0.Dj97JW46.js +0 -1
- package/web/build/client/_app/immutable/nodes/0.Dj97JW46.js.br +0 -0
- package/web/build/client/_app/immutable/nodes/0.Dj97JW46.js.gz +0 -0
- package/web/build/client/_app/immutable/nodes/1.C8JJYlWG.js +0 -1
- package/web/build/client/_app/immutable/nodes/1.C8JJYlWG.js.br +0 -1
- package/web/build/client/_app/immutable/nodes/1.C8JJYlWG.js.gz +0 -0
- package/web/build/client/_app/immutable/nodes/2.CDI0Ipi4.js +0 -10
- package/web/build/client/_app/immutable/nodes/2.CDI0Ipi4.js.br +0 -0
- package/web/build/client/_app/immutable/nodes/2.CDI0Ipi4.js.gz +0 -0
- package/web/build/client/_app/immutable/nodes/3.DVC22CDg.js +0 -171
- package/web/build/client/_app/immutable/nodes/3.DVC22CDg.js.br +0 -0
- package/web/build/client/_app/immutable/nodes/3.DVC22CDg.js.gz +0 -0
- package/web/build/server/chunks/0-DUStDOsr.js +0 -11
- package/web/build/server/chunks/1-DUj63QTT.js +0 -7
- package/web/build/server/chunks/2-B4PSKe5P.js +0 -7
- package/web/build/server/chunks/2-B4PSKe5P.js.map +0 -1
- package/web/build/server/chunks/3-C7UD6rei.js +0 -7
- package/web/build/server/chunks/_server.ts-4RcoUd0l.js.map +0 -1
- package/web/build/server/chunks/agent-sessions-C_hlZJKN.js.map +0 -1
- package/web/build/server/chunks/git-review-Bh1w0gak.js.map +0 -1
- package/web/build/server/chunks/index.js-C3-YX8SA.js.map +0 -1
- package/web/build/server/chunks/manifest.js-CmbRPv0j.js +0 -58
- package/web/build/server/chunks/manifest.js-CmbRPv0j.js.map +0 -1
- package/web/build/server/chunks/remote-1g4ayag-DMiPy1mT.js.map +0 -1
- package/web/build/server/chunks/remote-wh6t6j-RlIVOUe-.js.map +0 -1
- package/web/build/server/chunks/validate-CAQ31C7k.js.map +0 -1
package/lib/review-meta.mjs
CHANGED
|
@@ -7,12 +7,15 @@ import { decodePayload } from "./json-payload.mjs";
|
|
|
7
7
|
/**
|
|
8
8
|
* `agentSession` = la sesión ACP que generó la review, para seguir conversando con el agente desde la web.
|
|
9
9
|
* @param {string} filePath
|
|
10
|
-
*
|
|
10
|
+
* `modelLabel` = nombre legible que el agente da al modelo ("Haiku 5.5"): `model` puede ser un alias
|
|
11
|
+
* (`haiku`) que más adelante apunte a otra versión.
|
|
12
|
+
* @param {{ agent?: string, model?: string, modelLabel?: string, agentSession?: { agentId: string, sessionId: string, outputFilename: string } }} provenance
|
|
11
13
|
* @returns {boolean}
|
|
12
14
|
*/
|
|
13
|
-
export function stampReviewProvenance(filePath, { agent, model, agentSession } = {}) {
|
|
15
|
+
export function stampReviewProvenance(filePath, { agent, model, modelLabel, agentSession } = {}) {
|
|
14
16
|
const agentName = String(agent || "").trim();
|
|
15
17
|
const modelId = String(model || "").trim();
|
|
18
|
+
const label = String(modelLabel || "").trim();
|
|
16
19
|
if (!agentName && !modelId && !agentSession) return false;
|
|
17
20
|
|
|
18
21
|
let payload;
|
|
@@ -27,7 +30,12 @@ export function stampReviewProvenance(filePath, { agent, model, agentSession } =
|
|
|
27
30
|
const meta =
|
|
28
31
|
current && typeof current === "object" && !Array.isArray(current) ? { ...current } : {};
|
|
29
32
|
if (agentName) meta.agent = agentName;
|
|
30
|
-
if (modelId)
|
|
33
|
+
if (modelId) {
|
|
34
|
+
meta.model = modelId;
|
|
35
|
+
// Un label viejo describiría otro modelo: se reemplaza o se borra junto con `model`.
|
|
36
|
+
if (label && label !== modelId) meta.modelLabel = label;
|
|
37
|
+
else delete meta.modelLabel;
|
|
38
|
+
}
|
|
31
39
|
if (agentSession) meta.agentSession = { ...agentSession };
|
|
32
40
|
payload.meta = meta;
|
|
33
41
|
writeFileSync(filePath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
package/lib/review-meta.test.mjs
CHANGED
|
@@ -22,10 +22,10 @@ test("stampReviewProvenance no pisa el modelo si no hay uno", () => {
|
|
|
22
22
|
const file = join(dir, "diff-rev_a_b.json");
|
|
23
23
|
writeFileSync(file, JSON.stringify({ meta: { model: "sonnet" }, intent: "x" }));
|
|
24
24
|
|
|
25
|
-
stampReviewProvenance(file, { agent: "
|
|
25
|
+
stampReviewProvenance(file, { agent: "Cursor" });
|
|
26
26
|
|
|
27
27
|
const saved = JSON.parse(readFileSync(file, "utf8"));
|
|
28
|
-
assert.equal(saved.meta.agent, "
|
|
28
|
+
assert.equal(saved.meta.agent, "Cursor");
|
|
29
29
|
assert.equal(saved.meta.model, "sonnet");
|
|
30
30
|
});
|
|
31
31
|
|
|
@@ -39,3 +39,20 @@ test("stampReviewProvenance guarda la sesión del agente", () => {
|
|
|
39
39
|
|
|
40
40
|
assert.deepEqual(JSON.parse(readFileSync(file, "utf8")).meta.agentSession, agentSession);
|
|
41
41
|
});
|
|
42
|
+
|
|
43
|
+
test("stampReviewProvenance guarda el nombre con versión junto al alias", () => {
|
|
44
|
+
const dir = mkdtempSync(join(tmpdir(), "diff-review-meta-"));
|
|
45
|
+
const file = join(dir, "diff-rev_a_b.json");
|
|
46
|
+
writeFileSync(file, JSON.stringify({ meta: { model: "claude-haiku-5-5" }, intent: "x" }));
|
|
47
|
+
|
|
48
|
+
stampReviewProvenance(file, { agent: "Claude Code", model: "haiku", modelLabel: "Haiku 5.5" });
|
|
49
|
+
let meta = JSON.parse(readFileSync(file, "utf8")).meta;
|
|
50
|
+
assert.equal(meta.model, "haiku");
|
|
51
|
+
assert.equal(meta.modelLabel, "Haiku 5.5");
|
|
52
|
+
|
|
53
|
+
// Otro modelo sin nombre conocido: el label anterior ya no corresponde.
|
|
54
|
+
stampReviewProvenance(file, { model: "claude-opus-5" });
|
|
55
|
+
meta = JSON.parse(readFileSync(file, "utf8")).meta;
|
|
56
|
+
assert.equal(meta.model, "claude-opus-5");
|
|
57
|
+
assert.equal(meta.modelLabel, undefined);
|
|
58
|
+
});
|
package/lib/review-prep.mjs
CHANGED
|
@@ -1,63 +1,63 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Precomputa contexto git para el prompt de review (diff cache + lista de archivos).
|
|
3
|
-
*/
|
|
4
|
-
import { loadReviewConfig } from "./config.mjs";
|
|
5
|
-
import { extractBranchDiff } from "./extract.mjs";
|
|
6
|
-
import { outputRelPath } from "./output-name.mjs";
|
|
7
|
-
import { ensureReviewDir, readDiffCache, writeDiffCache } from "./review-dir.mjs";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Cachea el three-dot diff y arma las opciones de `buildPrompt` (tips, files, ignores).
|
|
11
|
-
* @param {{
|
|
12
|
-
* repo: string,
|
|
13
|
-
* branch: string,
|
|
14
|
-
* base?: string,
|
|
15
|
-
* outputFilename: string,
|
|
16
|
-
* acp?: boolean,
|
|
17
|
-
* }} input
|
|
18
|
-
*/
|
|
19
|
-
export function prepareReviewPromptOpts(input) {
|
|
20
|
-
const repo = String(input.repo || "").trim();
|
|
21
|
-
const branch = String(input.branch || "").trim();
|
|
22
|
-
const base = String(input.base || "develop").trim() || "develop";
|
|
23
|
-
const outputFilename = String(input.outputFilename || "").trim();
|
|
24
|
-
if (!repo || !branch || !outputFilename) {
|
|
25
|
-
throw new Error("prepareReviewPromptOpts requiere repo, branch y outputFilename.");
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
ensureReviewDir(repo);
|
|
29
|
-
writeDiffCache(repo, outputFilename, { branch, base });
|
|
30
|
-
const cached = readDiffCache(repo, outputFilename);
|
|
31
|
-
|
|
32
|
-
let extracted;
|
|
33
|
-
try {
|
|
34
|
-
extracted = extractBranchDiff({
|
|
35
|
-
repo,
|
|
36
|
-
branch,
|
|
37
|
-
base,
|
|
38
|
-
...(cached ? { cachedDiff: cached } : {}),
|
|
39
|
-
});
|
|
40
|
-
} catch {
|
|
41
|
-
extracted = extractBranchDiff({ repo, branch, base });
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const config = loadReviewConfig(repo);
|
|
45
|
-
const diffRelPath = cached
|
|
46
|
-
? outputRelPath(outputFilename).replace(/\.json$/i, ".diff")
|
|
47
|
-
: null;
|
|
48
|
-
|
|
49
|
-
return {
|
|
50
|
-
outputFilename,
|
|
51
|
-
acp: Boolean(input.acp),
|
|
52
|
-
diffRelPath,
|
|
53
|
-
mergeBase: extracted.mergeBase || undefined,
|
|
54
|
-
branchSha: extracted.branchSha || undefined,
|
|
55
|
-
baseSha: extracted.baseSha || undefined,
|
|
56
|
-
files: (extracted.files || []).map((f) => ({
|
|
57
|
-
path: f.path,
|
|
58
|
-
changeType: f.changeType,
|
|
59
|
-
})),
|
|
60
|
-
ignored: extracted.context?.ignored || [],
|
|
61
|
-
ignoreGlobs: config.ignore,
|
|
62
|
-
};
|
|
63
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Precomputa contexto git para el prompt de review (diff cache + lista de archivos).
|
|
3
|
+
*/
|
|
4
|
+
import { loadReviewConfig } from "./config.mjs";
|
|
5
|
+
import { extractBranchDiff } from "./extract.mjs";
|
|
6
|
+
import { outputRelPath } from "./output-name.mjs";
|
|
7
|
+
import { ensureReviewDir, readDiffCache, writeDiffCache } from "./review-dir.mjs";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Cachea el three-dot diff y arma las opciones de `buildPrompt` (tips, files, ignores).
|
|
11
|
+
* @param {{
|
|
12
|
+
* repo: string,
|
|
13
|
+
* branch: string,
|
|
14
|
+
* base?: string,
|
|
15
|
+
* outputFilename: string,
|
|
16
|
+
* acp?: boolean,
|
|
17
|
+
* }} input
|
|
18
|
+
*/
|
|
19
|
+
export function prepareReviewPromptOpts(input) {
|
|
20
|
+
const repo = String(input.repo || "").trim();
|
|
21
|
+
const branch = String(input.branch || "").trim();
|
|
22
|
+
const base = String(input.base || "develop").trim() || "develop";
|
|
23
|
+
const outputFilename = String(input.outputFilename || "").trim();
|
|
24
|
+
if (!repo || !branch || !outputFilename) {
|
|
25
|
+
throw new Error("prepareReviewPromptOpts requiere repo, branch y outputFilename.");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
ensureReviewDir(repo);
|
|
29
|
+
writeDiffCache(repo, outputFilename, { branch, base });
|
|
30
|
+
const cached = readDiffCache(repo, outputFilename);
|
|
31
|
+
|
|
32
|
+
let extracted;
|
|
33
|
+
try {
|
|
34
|
+
extracted = extractBranchDiff({
|
|
35
|
+
repo,
|
|
36
|
+
branch,
|
|
37
|
+
base,
|
|
38
|
+
...(cached ? { cachedDiff: cached } : {}),
|
|
39
|
+
});
|
|
40
|
+
} catch {
|
|
41
|
+
extracted = extractBranchDiff({ repo, branch, base });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const config = loadReviewConfig(repo);
|
|
45
|
+
const diffRelPath = cached
|
|
46
|
+
? outputRelPath(outputFilename).replace(/\.json$/i, ".diff")
|
|
47
|
+
: null;
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
outputFilename,
|
|
51
|
+
acp: Boolean(input.acp),
|
|
52
|
+
diffRelPath,
|
|
53
|
+
mergeBase: extracted.mergeBase || undefined,
|
|
54
|
+
branchSha: extracted.branchSha || undefined,
|
|
55
|
+
baseSha: extracted.baseSha || undefined,
|
|
56
|
+
files: (extracted.files || []).map((f) => ({
|
|
57
|
+
path: f.path,
|
|
58
|
+
changeType: f.changeType,
|
|
59
|
+
})),
|
|
60
|
+
ignored: extracted.context?.ignored || [],
|
|
61
|
+
ignoreGlobs: config.ignore,
|
|
62
|
+
};
|
|
63
|
+
}
|
package/lib/review.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
BLOCK_OPS,
|
|
3
3
|
FINDING_CLASSES,
|
|
4
4
|
FINDING_KINDS,
|
|
5
|
+
FINDING_RESOLUTION_KINDS,
|
|
5
6
|
FINDING_SEVERITIES,
|
|
6
7
|
GROUP_KINDS,
|
|
7
8
|
SKIP_REASONS,
|
|
@@ -111,6 +112,37 @@ function findingKey(finding) {
|
|
|
111
112
|
return [finding.file, finding.line ?? "", finding.kind, finding.what].join("|");
|
|
112
113
|
}
|
|
113
114
|
|
|
115
|
+
/** Triage UI (`ignored` | `fix` | `corrected`); se preserva al rehidratar desde disco. */
|
|
116
|
+
function normalizeResolution(raw) {
|
|
117
|
+
const kind = String(raw?.kind ?? "")
|
|
118
|
+
.trim()
|
|
119
|
+
.toLowerCase();
|
|
120
|
+
if (!FINDING_RESOLUTION_KINDS.includes(kind)) return undefined;
|
|
121
|
+
const detail = String(raw?.detail ?? "").trim();
|
|
122
|
+
return detail ? { kind, detail } : { kind };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function assignFindingIds(findings) {
|
|
126
|
+
const used = new Set();
|
|
127
|
+
for (const finding of findings) {
|
|
128
|
+
const id = String(finding.id || "").trim();
|
|
129
|
+
if (id && !used.has(id)) {
|
|
130
|
+
finding.id = id;
|
|
131
|
+
used.add(id);
|
|
132
|
+
} else {
|
|
133
|
+
finding.id = "";
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
let n = 1;
|
|
137
|
+
for (const finding of findings) {
|
|
138
|
+
if (finding.id) continue;
|
|
139
|
+
while (used.has(`f${n}`)) n += 1;
|
|
140
|
+
finding.id = `f${n}`;
|
|
141
|
+
used.add(finding.id);
|
|
142
|
+
n += 1;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
114
146
|
function normalizeFindings(raw, { blockIds, knownPaths }) {
|
|
115
147
|
const seen = new Set();
|
|
116
148
|
const findings = [];
|
|
@@ -123,8 +155,9 @@ function normalizeFindings(raw, { blockIds, knownPaths }) {
|
|
|
123
155
|
const severity = oneOf(item.severity, FINDING_SEVERITIES, "med");
|
|
124
156
|
const block = String(item?.block || "").trim();
|
|
125
157
|
const line = Number.isFinite(Number(item?.line)) ? Number(item.line) : null;
|
|
158
|
+
const resolution = normalizeResolution(item?.resolution);
|
|
126
159
|
const finding = {
|
|
127
|
-
id: "",
|
|
160
|
+
id: String(item?.id || "").trim(),
|
|
128
161
|
class: cls,
|
|
129
162
|
severity,
|
|
130
163
|
// Una mejora de calidad nunca frena un merge, diga lo que diga el modelo;
|
|
@@ -136,6 +169,7 @@ function normalizeFindings(raw, { blockIds, knownPaths }) {
|
|
|
136
169
|
block: blockIds.has(block) ? block : "",
|
|
137
170
|
what,
|
|
138
171
|
fix: text(item.fix || item.detail, MAX.finding),
|
|
172
|
+
...(resolution ? { resolution } : {}),
|
|
139
173
|
};
|
|
140
174
|
const key = findingKey(finding);
|
|
141
175
|
if (seen.has(key)) continue;
|
|
@@ -150,9 +184,7 @@ function normalizeFindings(raw, { blockIds, knownPaths }) {
|
|
|
150
184
|
rank[a.severity] - rank[b.severity] ||
|
|
151
185
|
a.file.localeCompare(b.file),
|
|
152
186
|
);
|
|
153
|
-
findings
|
|
154
|
-
finding.id = `f${i + 1}`;
|
|
155
|
-
});
|
|
187
|
+
assignFindingIds(findings);
|
|
156
188
|
return findings;
|
|
157
189
|
}
|
|
158
190
|
|
package/lib/review.test.mjs
CHANGED
|
@@ -130,6 +130,41 @@ test("buildReview deduplica hallazgos repetidos y limpia referencias colgadas",
|
|
|
130
130
|
assert.equal(doc.findings[0].block, "");
|
|
131
131
|
});
|
|
132
132
|
|
|
133
|
+
test("buildReview preserva resolution e ids al rehidratar triage desde disco", () => {
|
|
134
|
+
const withFindings = payload();
|
|
135
|
+
withFindings.findings = [
|
|
136
|
+
{
|
|
137
|
+
id: "f2",
|
|
138
|
+
class: "quality",
|
|
139
|
+
severity: "low",
|
|
140
|
+
kind: "docs",
|
|
141
|
+
file: "src/a.ts",
|
|
142
|
+
line: 3,
|
|
143
|
+
what: "comentario viejo",
|
|
144
|
+
fix: "actualizar",
|
|
145
|
+
resolution: { kind: "ignored" },
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
id: "f1",
|
|
149
|
+
class: "risk",
|
|
150
|
+
severity: "med",
|
|
151
|
+
kind: "bug",
|
|
152
|
+
file: "src/a.ts",
|
|
153
|
+
line: 22,
|
|
154
|
+
block: "b1",
|
|
155
|
+
what: "null sin chequear",
|
|
156
|
+
fix: "guardar",
|
|
157
|
+
resolution: { kind: "corrected", detail: "null-check en parse" },
|
|
158
|
+
},
|
|
159
|
+
];
|
|
160
|
+
const doc = buildReview({ skeleton: skeleton(), payload: withFindings });
|
|
161
|
+
const byId = Object.fromEntries(doc.findings.map((f) => [f.id, f]));
|
|
162
|
+
assert.equal(doc.findings.length, 2);
|
|
163
|
+
assert.deepEqual(byId.f1.resolution, { kind: "corrected", detail: "null-check en parse" });
|
|
164
|
+
assert.deepEqual(byId.f2.resolution, { kind: "ignored" });
|
|
165
|
+
assert.equal(byId.f1.what, "null sin chequear");
|
|
166
|
+
});
|
|
167
|
+
|
|
133
168
|
test("una respuesta JSON del modelo atraviesa decode, validación y normalización", () => {
|
|
134
169
|
const answer = JSON.stringify({
|
|
135
170
|
meta: { agent: "Claude Code", model: "claude-opus" },
|
package/lib/validate.mjs
CHANGED
|
@@ -8,6 +8,7 @@ export const FINDING_KINDS = [
|
|
|
8
8
|
"missing-test", "perf", "maintainability", "docs", "other",
|
|
9
9
|
];
|
|
10
10
|
export const SKIP_REASONS = ["generated", "lockfile", "format", "vendored", "binary", "trivial", "ignored"];
|
|
11
|
+
export const FINDING_RESOLUTION_KINDS = ["ignored", "fix", "corrected"];
|
|
11
12
|
|
|
12
13
|
/** Lo que tiene que devolver el modelo en un turno. El resto lo agrega el pipeline. */
|
|
13
14
|
export const REVIEW_SCHEMA = {
|
|
@@ -77,6 +78,15 @@ export const REVIEW_SCHEMA = {
|
|
|
77
78
|
block: { type: "string" },
|
|
78
79
|
what: { type: "string", minLength: 1 },
|
|
79
80
|
fix: { type: "string" },
|
|
81
|
+
resolution: {
|
|
82
|
+
type: "object",
|
|
83
|
+
required: ["kind"],
|
|
84
|
+
additionalProperties: true,
|
|
85
|
+
properties: {
|
|
86
|
+
kind: { type: "string", enum: FINDING_RESOLUTION_KINDS },
|
|
87
|
+
detail: { type: "string" },
|
|
88
|
+
},
|
|
89
|
+
},
|
|
80
90
|
},
|
|
81
91
|
},
|
|
82
92
|
},
|
|
@@ -155,7 +165,7 @@ export function reviewIssues(payload, knownPaths = []) {
|
|
|
155
165
|
const agentName = typeof meta.agent === "string" ? meta.agent.trim() : "";
|
|
156
166
|
const modelId = typeof meta.model === "string" ? meta.model.trim() : "";
|
|
157
167
|
if (!agentName || agentName.startsWith("<")) {
|
|
158
|
-
errors.push("meta.agent es obligatorio: nombre del producto (Claude Code, Cursor,
|
|
168
|
+
errors.push("meta.agent es obligatorio: nombre del producto (Claude Code, Cursor, Copilot, …), sin placeholder");
|
|
159
169
|
}
|
|
160
170
|
if (!modelId || modelId.startsWith("<")) {
|
|
161
171
|
errors.push("meta.model es obligatorio: id exacto del modelo, sin placeholder");
|
package/lib/validate.test.mjs
CHANGED
|
@@ -68,6 +68,53 @@ test("validateAgainst acepta los dos ejes de hallazgo", () => {
|
|
|
68
68
|
assert.deepEqual(validateAgainst(REVIEW_SCHEMA, doc), []);
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
+
test("validateAgainst acepta resolution de triage en hallazgos", () => {
|
|
72
|
+
const doc = valid();
|
|
73
|
+
doc.findings = [
|
|
74
|
+
{
|
|
75
|
+
id: "f1",
|
|
76
|
+
class: "risk",
|
|
77
|
+
severity: "high",
|
|
78
|
+
blocking: true,
|
|
79
|
+
kind: "bug",
|
|
80
|
+
file: "src/a.ts",
|
|
81
|
+
line: 4,
|
|
82
|
+
what: "rompe",
|
|
83
|
+
fix: "arreglar",
|
|
84
|
+
resolution: { kind: "corrected", detail: "null-check en parse" },
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: "f2",
|
|
88
|
+
class: "quality",
|
|
89
|
+
severity: "low",
|
|
90
|
+
blocking: false,
|
|
91
|
+
kind: "maintainability",
|
|
92
|
+
file: "src/a.ts",
|
|
93
|
+
line: null,
|
|
94
|
+
what: "duplicado",
|
|
95
|
+
resolution: { kind: "ignored" },
|
|
96
|
+
},
|
|
97
|
+
];
|
|
98
|
+
assert.deepEqual(validateAgainst(REVIEW_SCHEMA, doc), []);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("validateAgainst rechaza resolution.kind inválido", () => {
|
|
102
|
+
const doc = valid();
|
|
103
|
+
doc.findings = [
|
|
104
|
+
{
|
|
105
|
+
id: "f1",
|
|
106
|
+
class: "risk",
|
|
107
|
+
severity: "high",
|
|
108
|
+
blocking: true,
|
|
109
|
+
kind: "bug",
|
|
110
|
+
file: "src/a.ts",
|
|
111
|
+
what: "x",
|
|
112
|
+
resolution: { kind: "done" },
|
|
113
|
+
},
|
|
114
|
+
];
|
|
115
|
+
assert.ok(validateAgainst(REVIEW_SCHEMA, doc).some((e) => e.includes("kind")));
|
|
116
|
+
});
|
|
117
|
+
|
|
71
118
|
test("reviewIssues detecta un bloque que apunta a un grupo inexistente", () => {
|
|
72
119
|
const doc = valid();
|
|
73
120
|
doc.blocks[0].group = "g9";
|
package/lib/wait-output.test.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { describe, it } from "node:test";
|
|
|
6
6
|
import { existingReviewOutput, waitForReviewOutput } from "./wait-output.mjs";
|
|
7
7
|
|
|
8
8
|
const valid = () => ({
|
|
9
|
-
meta: { agent: "
|
|
9
|
+
meta: { agent: "Claude Code", model: "opus" },
|
|
10
10
|
intent: "Agrega el ABM de depósitos locales end-to-end para dejar de administrarlos por SQL.",
|
|
11
11
|
groups: [{ id: "g1", kind: "feat", title: "feat(product): CRUD de depósitos", intent: "base de todo" }],
|
|
12
12
|
blocks: [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jpkme/diff-review",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
4
4
|
"description": "Local UI to prompt a coding agent for a branch review and browse the result.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"LICENSE"
|
|
16
16
|
],
|
|
17
17
|
"scripts": {
|
|
18
|
-
"test": "node --test lib/*.test.mjs",
|
|
18
|
+
"test": "node --test lib/*.test.mjs && node --experimental-strip-types --test web/src/lib/report/triage.test.ts",
|
|
19
19
|
"web:install": "npm --prefix web install",
|
|
20
20
|
"web:dev": "npm --prefix web run dev",
|
|
21
21
|
"web:build": "npm --prefix web run build",
|
package/schema.json
CHANGED
|
@@ -123,7 +123,22 @@
|
|
|
123
123
|
"line": { "type": ["integer", "null"], "description": "Línea de la post-imagen (columna derecha / @@ +N)." },
|
|
124
124
|
"block": { "type": ["string", "null"], "description": "Id del bloque, o vacío/null si es transversal." },
|
|
125
125
|
"what": { "type": "string" },
|
|
126
|
-
"fix": { "type": "string", "description": "Acción concreta." }
|
|
126
|
+
"fix": { "type": "string", "description": "Acción concreta." },
|
|
127
|
+
"resolution": {
|
|
128
|
+
"type": "object",
|
|
129
|
+
"description": "Triage de la UI (Ignorar / Corregir / Corregido). Lo escribe el viewer; el agente debe preservarlo al editar el JSON.",
|
|
130
|
+
"required": ["kind"],
|
|
131
|
+
"properties": {
|
|
132
|
+
"kind": {
|
|
133
|
+
"type": "string",
|
|
134
|
+
"enum": ["ignored", "fix", "corrected"]
|
|
135
|
+
},
|
|
136
|
+
"detail": {
|
|
137
|
+
"type": "string",
|
|
138
|
+
"description": "Resumen de la corrección aplicada (solo con kind=corrected)."
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
127
142
|
}
|
|
128
143
|
}
|
|
129
144
|
},
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--bg:#0b0e14;--bg-elev:#11151d;--bg-card:#151a24;--bg-card-hover:#1a2130;--border:#242b3a;--border-soft:#1c2230;--text:#e6e9ef;--text-dim:#9aa4b8;--text-faint:#7a8499;--accent:#6ea8fe;--accent-soft:#1c2b45;--ok:#7ee2a8;--ok-bg:#0d2a1c;--warn:#e2c67e;--danger:#ff9a9a;--danger-bg:#2a1414;--danger-border:#4a1e1e;--radius:3px;--mono:ui-monospace, "JetBrains Mono", "Fira Code", Consolas, "SFMono-Regular", Menlo, monospace;--sans:-apple-system, "Segoe UI", Inter, Roboto, Helvetica, Arial, sans-serif;--btn-primary-bg:#1c2b45;--btn-primary-border:#3a5a8f;--btn-primary-text:#cfe0ff;--kind-feat:#7ee2a8;--kind-fix:#ff9a9a;--kind-refactor:#c9a5ff;--kind-perf:#e2c67e;--kind-test:#6ea8fe;--kind-chore:#9aa4b8;--kind-docs:#7fd6e0;--kind-infra:#f0a868;--sev-high:#ff9a9a;--sev-med:#f08a5a;--sev-low:#c4b86a;--sev-nit:#7f8aa3;--diff-add-bg:#0f2419;--diff-add-fg:#7ee2a8;--diff-del-bg:#2a1418;--diff-del-fg:#ff9a9a;--diff-hunk-bg:#141924;--diff-gutter:#46516a;--diff-mark:#2b3f63;--diff-mark-edge:#6ea8fe;--diff-add-strong:#1f6b3c;--diff-del-strong:#6d2530;--diff-blank-bg:repeating-linear-gradient(135deg, #11151d 0 6px, #151a24 6px 12px);--fix-bg:#0e1a13;--fix-border:#2f6b46;--fix-text:#7ee2a8;--publish-bg:#0e1622;--publish-border:#3a5a8f;--publish-text:#7db8ff;--scrollbar-thumb:#2a3348;--scrollbar-thumb-hover:#3a4660;--selection-bg:#33507a;--header-bg:#0b0e14eb}:root[data-theme=light]{--bg:#fff;--bg-elev:#f6f7f9;--bg-card:#eef0f3;--bg-card-hover:#e4e7ec;--border:#d7dbe2;--border-soft:#e4e7ec;--text:#16181d;--text-dim:#4b5563;--text-faint:#5b6472;--accent:#2f6fe0;--accent-soft:#dce8ff;--ok:#1f9d55;--ok-bg:#e3f8ea;--warn:#a5720a;--danger:#c92a2a;--danger-bg:#fbe4e4;--danger-border:#f3b4b4;--btn-primary-bg:#2f6fe0;--btn-primary-border:#2358b8;--btn-primary-text:#fff;--kind-feat:#1f9d55;--kind-fix:#c92a2a;--kind-refactor:#7c4dd6;--kind-perf:#a5720a;--kind-test:#2f6fe0;--kind-chore:#5b6472;--kind-docs:#0f8fa3;--kind-infra:#c1690f;--sev-high:#c92a2a;--sev-med:#c44d1a;--sev-low:#8b7d2e;--sev-nit:#6b7280;--diff-add-bg:#e6f7ec;--diff-add-fg:#1f9d55;--diff-del-bg:#fbe9e9;--diff-del-fg:#c92a2a;--diff-hunk-bg:#eef1f5;--diff-gutter:#9aa3b2;--diff-mark:#cfe0ff;--diff-mark-edge:#2f6fe0;--diff-add-strong:#a7ebbd;--diff-del-strong:#f9b8b8;--diff-blank-bg:repeating-linear-gradient(135deg, #f6f7f9 0 6px, #eef0f3 6px 12px);--fix-bg:#e6f7ec;--fix-border:#1f9d55;--fix-text:#157a41;--publish-bg:#e8f0fe;--publish-border:#2f6fe0;--publish-text:#1d4fa8;--scrollbar-thumb:#c7cbd4;--scrollbar-thumb-hover:#aab0bb;--selection-bg:#cfe0ff;--header-bg:#ffffffeb}.code .chg{border-radius:2px}.code.del .chg{background:var(--diff-del-strong)}.code.add .chg{background:var(--diff-add-strong)}.hljs-comment,.hljs-quote{color:#6b7689;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-literal,.hljs-doctag,.hljs-formula{color:#c9a5ff}.hljs-string,.hljs-regexp,.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string{color:#a5d6a7}.hljs-number,.hljs-variable,.hljs-template-variable,.hljs-type,.hljs-selector-class,.hljs-selector-attr,.hljs-selector-pseudo{color:#f0a868}.hljs-title,.hljs-section,.hljs-title.class_,.hljs-title.function_{color:#7fd6e0}.hljs-attr,.hljs-property,.hljs-symbol,.hljs-bullet,.hljs-link{color:#6ea8fe}.hljs-built_in,.hljs-class,.hljs-params{color:#e6e9ef}.hljs-meta,.hljs-tag{color:#9aa4b8}:root[data-theme=light] .hljs-comment,:root[data-theme=light] .hljs-quote{color:#6b7280}:root[data-theme=light] .hljs-keyword,:root[data-theme=light] .hljs-selector-tag,:root[data-theme=light] .hljs-literal,:root[data-theme=light] .hljs-doctag,:root[data-theme=light] .hljs-formula{color:#7c4dd6}:root[data-theme=light] .hljs-string,:root[data-theme=light] .hljs-regexp,:root[data-theme=light] .hljs-addition,:root[data-theme=light] .hljs-attribute,:root[data-theme=light] .hljs-meta .hljs-string{color:#1f9d55}:root[data-theme=light] .hljs-number,:root[data-theme=light] .hljs-variable,:root[data-theme=light] .hljs-template-variable,:root[data-theme=light] .hljs-type,:root[data-theme=light] .hljs-selector-class,:root[data-theme=light] .hljs-selector-attr,:root[data-theme=light] .hljs-selector-pseudo{color:#b5650f}:root[data-theme=light] .hljs-title,:root[data-theme=light] .hljs-section,:root[data-theme=light] .hljs-title.class_,:root[data-theme=light] .hljs-title.function_{color:#0f8fa3}:root[data-theme=light] .hljs-attr,:root[data-theme=light] .hljs-property,:root[data-theme=light] .hljs-symbol,:root[data-theme=light] .hljs-bullet,:root[data-theme=light] .hljs-link{color:#2f6fe0}:root[data-theme=light] .hljs-built_in,:root[data-theme=light] .hljs-class,:root[data-theme=light] .hljs-params{color:#16181d}:root[data-theme=light] .hljs-meta,:root[data-theme=light] .hljs-tag{color:#5b6472}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.anchor-flash{animation:1.4s ease-out anchor-flash}@keyframes anchor-flash{0%{box-shadow:0 0 0 2px var(--accent)}to{box-shadow:0 0 0 2px #0000}}*{box-sizing:border-box}html,body{background:var(--bg);color:var(--text);font-family:var(--sans);min-height:100%;margin:0;padding:0}body{-webkit-font-smoothing:antialiased}button,input,select{font:inherit;color:inherit}button{cursor:pointer}::selection{background:var(--selection-bg)}html,body,*{scrollbar-width:thin;scrollbar-color:var(--scrollbar-thumb) transparent}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:var(--scrollbar-thumb);background-clip:padding-box;border:2px solid #0000;border-radius:999px}::-webkit-scrollbar-thumb:hover{background:var(--scrollbar-thumb-hover);background-clip:padding-box;border:2px solid #0000}::-webkit-scrollbar-corner{background:0 0}.app-header{--app-header-h:52px;min-height:var(--app-header-h);box-sizing:border-box;border-bottom:1px solid var(--border-soft);background:var(--header-bg);flex-shrink:0;justify-content:space-between;align-items:center;gap:12px 16px;min-width:0;padding:0 16px 0 12px;display:flex}.app-brand{border-radius:var(--radius);letter-spacing:-.01em;color:var(--text);white-space:nowrap;flex-shrink:0;align-items:center;gap:7px;padding:4px 6px;font-size:14px;font-weight:600;line-height:1;text-decoration:none;display:flex}a.app-brand:hover{background:color-mix(in srgb, var(--text) 7%, transparent)}a.app-brand:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.boot.svelte-12qhfyh{color:var(--text-dim);font-family:var(--mono);padding:48px 28px;font-size:13px}.boot.svelte-12qhfyh button:where(.svelte-12qhfyh){background:var(--btn-primary-bg);border:1px solid var(--btn-primary-border);color:var(--btn-primary-text);border-radius:var(--radius);margin-top:12px;padding:8px 14px}
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.live.svelte-18urbyr{background:radial-gradient(900px 420px at 8% -8%, color-mix(in srgb, var(--accent) 10%, transparent), transparent 70%), var(--bg);flex-direction:column;flex:1;min-height:0;display:flex}.brand.svelte-18urbyr{align-items:center;gap:8px;min-width:0;display:flex}.pill.svelte-18urbyr{letter-spacing:.02em;border-radius:var(--radius);border:1px solid var(--border);color:var(--text-dim);background:var(--bg-card);text-transform:lowercase;padding:3px 9px;font-size:11px;font-weight:500}.pill.busy.svelte-18urbyr{border-color:color-mix(in srgb, var(--accent) 45%, var(--border));color:var(--accent);background:var(--accent-soft)}.pill.error.svelte-18urbyr{border-color:var(--danger-border,var(--danger));color:var(--danger);background:var(--danger-bg,transparent)}.pill.gone.svelte-18urbyr{color:var(--text-faint)}.actions.svelte-18urbyr{flex:none;align-items:center;gap:8px;display:flex}.ghost.svelte-18urbyr{border:1px solid var(--border);border-radius:var(--radius);color:var(--text);font:inherit;cursor:pointer;background:0 0;padding:6px 11px;font-size:13px}.ghost.svelte-18urbyr:hover{border-color:var(--accent)}.body.svelte-18urbyr{flex:1;grid-template-columns:minmax(280px,320px) 1fr;gap:20px;min-height:0;padding:20px 24px 24px;display:grid}.card.svelte-18urbyr{border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-elev);align-self:start;padding:16px 16px 14px}.fields.svelte-18urbyr{flex-direction:column;gap:14px;margin:0;display:flex}.fields.svelte-18urbyr dt:where(.svelte-18urbyr){text-transform:uppercase;letter-spacing:.04em;color:var(--text-faint);margin:0 0 3px;font-size:11px}.fields.svelte-18urbyr dd:where(.svelte-18urbyr){font-size:13px;font-family:var(--mono);text-overflow:ellipsis;white-space:nowrap;color:var(--text);margin:0;overflow:hidden}.fields.svelte-18urbyr dd.account-dd:where(.svelte-18urbyr){white-space:normal;text-overflow:unset;font-family:inherit;overflow:visible}.fields.svelte-18urbyr dd.branch-base:where(.svelte-18urbyr){white-space:normal;text-overflow:unset;overflow-wrap:anywhere;line-height:1.35;overflow:visible}.branch.svelte-18urbyr{color:var(--accent);font-weight:600}.arrow.svelte-18urbyr{color:var(--text-faint);margin:0 2px}.base.svelte-18urbyr{color:var(--text)}.card-note.svelte-18urbyr{border-top:1px solid var(--border-soft,var(--border));color:var(--text-faint);margin:16px 0 0;padding-top:12px;font-size:12.5px;line-height:1.4}.stage.svelte-18urbyr{border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-elev);flex-direction:column;min-width:0;min-height:0;display:flex;overflow:hidden}.center.svelte-18urbyr{text-align:center;flex-direction:column;flex:1;justify-content:center;align-items:center;gap:8px;padding:40px 24px;display:flex}.center.fail.svelte-18urbyr .title:where(.svelte-18urbyr){color:var(--danger)}.auth-actions.svelte-18urbyr{flex-wrap:wrap;justify-content:center;gap:8px;margin-top:12px;display:flex}.primary.svelte-18urbyr{border:1px solid color-mix(in srgb, var(--accent) 55%, var(--border));border-radius:var(--radius);background:var(--accent-soft);color:var(--accent);font:inherit;cursor:pointer;padding:7px 14px;font-size:13px;font-weight:600}.primary.svelte-18urbyr:hover:not(:disabled){border-color:var(--accent)}.primary.svelte-18urbyr:disabled{opacity:.6;cursor:default}.auth-note.svelte-18urbyr{max-width:28rem;color:var(--text-dim);margin:10px 0 0;font-size:13px}.copilot-host-pick.svelte-18urbyr{flex-direction:column;align-items:center;gap:4px;width:min(100%,28rem);display:flex}.copilot-host-field.svelte-18urbyr{width:100%;color:var(--text-dim);text-align:left;flex-direction:column;gap:4px;margin-top:8px;font-size:12px;display:flex}.copilot-host-field.svelte-18urbyr input:where(.svelte-18urbyr){border:1px solid var(--border);border-radius:var(--radius);background:var(--bg);color:var(--text);font:inherit;padding:7px 10px;font-size:13px}.auth-err.svelte-18urbyr{max-width:28rem;color:var(--danger);margin:8px 0 0;font-size:13px}.spinner.svelte-18urbyr{border:2px solid var(--border);border-top-color:var(--accent);border-radius:50%;width:34px;height:34px;margin-bottom:6px;animation:.8s linear infinite svelte-18urbyr-spin}@keyframes svelte-18urbyr-spin{to{transform:rotate(360deg)}}.title.svelte-18urbyr{margin:0;font-size:17px;font-weight:600}.detail.svelte-18urbyr{color:var(--text-dim);max-width:28rem;margin:0;font-size:14px}.hint.svelte-18urbyr{max-width:32rem;color:var(--text-faint);margin:6px 0 0;font-size:13px}.stage-header.svelte-18urbyr{border-bottom:1px solid var(--border);border-radius:0;flex:none;align-items:center;gap:8px;min-width:0;padding:10px 14px;display:flex}.stage-header.svelte-18urbyr strong:where(.svelte-18urbyr){font-size:13.5px;font-weight:650}.stage-header.svelte-18urbyr small:where(.svelte-18urbyr){font-family:var(--mono);color:var(--text-faint);text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:11.5px;overflow:hidden}.log-wrap.svelte-18urbyr{border-radius:0;flex-direction:column;flex:1;gap:10px;min-height:0;padding:12px 14px;display:flex}.log-wrap.svelte-18urbyr .log{flex:1;min-height:0}.stage-footer.svelte-18urbyr{border-top:1px solid var(--border);border-radius:0;flex-wrap:wrap;flex:none;align-items:center;gap:10px;min-width:0;margin-top:auto;padding-top:10px;display:flex}.stage-footer.svelte-18urbyr .trust{flex:0 auto;min-width:0;max-width:100%}.footer-spacer.svelte-18urbyr{flex:8px;min-width:8px}.stage-footer.svelte-18urbyr .perm-actions{flex:none}.launch-banner.svelte-18urbyr{border:1px solid var(--border);border-radius:var(--radius);background:color-mix(in srgb, var(--accent-soft) 70%, var(--bg-card));align-items:flex-start;gap:12px;padding:10px 12px;display:flex}.launch-banner.svelte-18urbyr .title:where(.svelte-18urbyr){font-size:14px}.launch-banner.svelte-18urbyr .detail:where(.svelte-18urbyr){margin-top:2px;font-size:12.5px}.spinner.sm.svelte-18urbyr{flex:none;width:18px;height:18px;margin:2px 0 0}@media (width<=800px){.body.svelte-18urbyr{grid-template-rows:auto 1fr;grid-template-columns:1fr;gap:14px;padding:16px}.card.svelte-18urbyr{align-self:stretch}.stage.svelte-18urbyr{min-height:280px}}@media (width<=480px){.bar.svelte-18urbyr{flex-wrap:wrap;padding-block:8px}.pill.svelte-18urbyr{font-size:10px}}.page.svelte-1uha8ag{flex-direction:column;min-height:100vh;display:flex}.page.live.svelte-1uha8ag{height:100vh;min-height:100vh;overflow:hidden}.diff-backdrop.svelte-1uha8ag{z-index:80;background:color-mix(in srgb, var(--bg) 55%, transparent);-webkit-backdrop-filter:blur(10px);place-items:center;padding:24px;animation:.12s ease-out svelte-1uha8ag-diff-fade;display:grid;position:fixed;inset:0}@keyframes svelte-1uha8ag-diff-fade{0%{opacity:0}}.diff-modal.svelte-1uha8ag{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);text-align:center;width:min(380px,100%);padding:28px 24px;box-shadow:0 16px 48px #00000047}.confirm-dialog.svelte-1uha8ag{z-index:81;background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);text-align:left;outline:none;width:min(420px,100vw - 32px);padding:18px 20px;position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);box-shadow:0 16px 48px #00000047}.confirm-dialog.svelte-1uha8ag h2:where(.svelte-1uha8ag){color:var(--text);margin:0 0 10px;font-size:15px;font-weight:650}.confirm-dialog.svelte-1uha8ag p:where(.svelte-1uha8ag){color:var(--text-dim);margin:0;font-size:13px;line-height:1.45}.confirm-dialog.svelte-1uha8ag code:where(.svelte-1uha8ag){color:var(--text);font-size:12px}.confirm-actions.svelte-1uha8ag{justify-content:flex-end;gap:8px;margin-top:16px;display:flex}.confirm-actions.svelte-1uha8ag button:where(.svelte-1uha8ag){background:var(--bg-elev);border:1px solid var(--border);border-radius:var(--radius);color:var(--text);cursor:pointer;padding:7px 12px;font-size:13px}.confirm-actions.svelte-1uha8ag button.primary:where(.svelte-1uha8ag){background:var(--btn-primary-bg);border-color:var(--btn-primary-border);color:var(--btn-primary-text)}.diff-modal.svelte-1uha8ag p:where(.svelte-1uha8ag){color:var(--text);margin:14px 0 0;font-size:15px}.diff-modal-file.svelte-1uha8ag{text-overflow:ellipsis;white-space:nowrap;color:var(--text-faint);margin-top:6px;font-size:12px;display:block;overflow:hidden}.diff-spinner.svelte-1uha8ag{border:2px solid var(--border);border-top-color:var(--accent);border-radius:50%;width:28px;height:28px;margin:0 auto;animation:.7s linear infinite svelte-1uha8ag-diff-spin}.cli-wait.svelte-1uha8ag{text-align:center;background:var(--bg);flex-direction:column;flex:1;justify-content:center;align-items:center;gap:12px;min-height:0;padding:32px 24px;display:flex}.cli-wait-title.svelte-1uha8ag{color:var(--text);margin:0;font-size:15px}.cli-wait-detail.svelte-1uha8ag{color:var(--text-faint);margin:0;font-size:13px}.cli-wait.svelte-1uha8ag .ghost:where(.svelte-1uha8ag){border:1px solid var(--border);border-radius:var(--radius);color:var(--text-dim);cursor:pointer;background:0 0;margin-top:8px;padding:6px 12px;font-size:13px}@keyframes svelte-1uha8ag-diff-spin{to{transform:rotate(360deg)}}.toast.svelte-1uha8ag{z-index:50;border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-card);color:var(--text);padding:10px 16px;font-size:13px;position:fixed;top:16px;left:50%;transform:translate(-50%);box-shadow:0 8px 24px #00000029}.topbar.svelte-1uha8ag{z-index:5;position:sticky;top:0}.workspace.svelte-1uha8ag{flex:1;grid-template-columns:minmax(0,1fr) minmax(300px,380px);align-items:start;gap:28px;width:100%;max-width:1280px;margin:0 auto;padding:22px 28px 40px;display:grid;overflow:auto}.setup.svelte-1uha8ag{min-width:0}h2.svelte-1uha8ag{letter-spacing:.06em;text-transform:uppercase;color:var(--text-faint);margin:0 0 4px;font-size:13px;font-weight:600}.result-head.svelte-1uha8ag{margin-top:24px}.section-head.svelte-1uha8ag{justify-content:space-between;align-items:baseline;margin-top:22px;display:flex}.hint-text.svelte-1uha8ag{color:var(--text-dim);margin:0 0 12px;font-size:12.5px;line-height:1.5}.hint-text.svelte-1uha8ag code:where(.svelte-1uha8ag){font-family:var(--mono);color:var(--text);font-size:12px}.detect-wait.svelte-1uha8ag{color:var(--text-dim);margin:0 0 10px;font-size:13px}button.detect-open.svelte-1uha8ag{border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-card);width:100%;color:var(--text);font:inherit;text-align:left;cursor:pointer;align-items:center;gap:12px;margin:0 0 12px;padding:12px 14px;transition:border-color .12s,background .12s;display:flex}button.detect-open.ready.svelte-1uha8ag{border-color:color-mix(in srgb, var(--accent) 55%, var(--border));background:var(--accent-soft)}button.detect-open.svelte-1uha8ag:hover{border-color:var(--accent)}button.detect-open.svelte-1uha8ag:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.detect-open-text.svelte-1uha8ag{flex-direction:column;flex:1;gap:3px;min-width:0;display:flex}.detect-open-label.svelte-1uha8ag{font-size:13px;font-weight:600}.detect-open-label.svelte-1uha8ag small:where(.svelte-1uha8ag){color:var(--text-dim);font-weight:400}.detect-open-text.svelte-1uha8ag code:where(.svelte-1uha8ag),.detect-open-meta.svelte-1uha8ag{text-overflow:ellipsis;white-space:nowrap;color:var(--text-dim);font-size:12px;overflow:hidden}.detect-open-cta.svelte-1uha8ag{color:var(--accent);font-size:13px;font-weight:600}.detect-open-chevron.svelte-1uha8ag{color:var(--accent);flex-shrink:0;transition:transform .12s}button.detect-open.svelte-1uha8ag:hover .detect-open-chevron:where(.svelte-1uha8ag){transform:translate(2px)}.error-line.svelte-1uha8ag{color:var(--danger);margin:0 0 10px;font-size:12px}.ok-line.svelte-1uha8ag{color:var(--ok);margin:0 0 10px;font-size:12px}.row.svelte-1uha8ag{flex-wrap:wrap;gap:8px;display:flex}.repo-setup.svelte-1uha8ag{flex-direction:column;gap:10px;display:flex}.field.svelte-1uha8ag{flex-direction:column;flex:1;gap:4px;min-width:180px;display:flex}.repo-field.svelte-1uha8ag{flex:none;width:100%;min-width:0}.branches-row.svelte-1uha8ag .field:where(.svelte-1uha8ag){flex:1;min-width:160px}.field.svelte-1uha8ag span:where(.svelte-1uha8ag){color:var(--text-faint);font-size:11px}.repo-row.svelte-1uha8ag{gap:6px;min-width:0;display:flex}.repo-row.svelte-1uha8ag input[type=text]:where(.svelte-1uha8ag){flex:1;min-width:0}.repo-row.svelte-1uha8ag button:where(.svelte-1uha8ag){flex-shrink:0}.source-toggle.svelte-1uha8ag{margin-bottom:10px}input[type=text].svelte-1uha8ag,select.svelte-1uha8ag{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);flex:1;min-width:0;padding:9px 12px}button.svelte-1uha8ag{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);color:var(--text);padding:9px 12px;font-size:12.5px}button.svelte-1uha8ag:hover:not(:disabled){border-color:var(--accent)}button.svelte-1uha8ag:disabled{opacity:.5;cursor:not-allowed}.mode-toggle.svelte-1uha8ag{border:1px solid var(--border);border-radius:var(--radius);width:fit-content;margin-bottom:10px;display:flex;overflow:hidden}.mode-toggle.svelte-1uha8ag button:where(.svelte-1uha8ag){background:0 0;border:0;border-radius:0;padding:6px 12px;font-size:12px}.mode-toggle.svelte-1uha8ag button:where(.svelte-1uha8ag)+button:where(.svelte-1uha8ag){border-left:1px solid var(--border)}.mode-toggle.svelte-1uha8ag button.on:where(.svelte-1uha8ag){background:var(--btn-primary-bg);color:var(--btn-primary-text);font-weight:600}textarea.svelte-1uha8ag{resize:vertical;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);width:100%;height:220px;font-family:var(--mono);color:var(--text);padding:12px;font-size:12px;line-height:1.5}.loading-models.svelte-1uha8ag{color:var(--text-faint);font-weight:400}.models-error.svelte-1uha8ag{flex-wrap:wrap;align-items:baseline;gap:8px;display:flex}button.linkish.svelte-1uha8ag{color:var(--accent,#3b82f6);cursor:pointer;font:inherit;background:0 0;border:none;padding:0;text-decoration:underline}.actions.svelte-1uha8ag{gap:8px;margin-top:10px;display:flex}button.primary.svelte-1uha8ag{background:var(--btn-primary-bg);border-color:var(--btn-primary-border);color:var(--btn-primary-text);justify-content:center;align-items:center;gap:8px;font-weight:600;display:inline-flex}.btn-spinner.svelte-1uha8ag{border:2px solid color-mix(in srgb, var(--btn-primary-text) 35%, transparent);border-top-color:var(--btn-primary-text);border-radius:50%;flex-shrink:0;width:14px;height:14px;animation:.75s linear infinite svelte-1uha8ag-btn-spin}@keyframes svelte-1uha8ag-btn-spin{to{transform:rotate(360deg)}}.coverage-box.svelte-1uha8ag{border:1px solid var(--border);border-radius:var(--radius);border-left:3px solid var(--sev-med);background:var(--bg-card);flex-direction:column;gap:10px;margin-top:12px;padding:12px 14px;display:flex}.coverage-head.svelte-1uha8ag{flex-wrap:wrap;align-items:baseline;gap:8px;display:flex}.coverage-head.svelte-1uha8ag strong:where(.svelte-1uha8ag){color:var(--sev-med);font-size:13px}.coverage-head.svelte-1uha8ag span:where(.svelte-1uha8ag){color:var(--text-faint);font-size:11.5px}.coverage-box.svelte-1uha8ag summary:where(.svelte-1uha8ag){color:var(--text-dim);cursor:pointer;font-size:12px}.coverage-list.svelte-1uha8ag{max-height:180px;font-family:var(--mono);color:var(--text-faint);margin:8px 0 0;padding-left:18px;font-size:11px;overflow:auto}.coverage-box.svelte-1uha8ag textarea:where(.svelte-1uha8ag){resize:vertical;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);width:100%;font-family:var(--mono);color:var(--text);padding:10px;font-size:11.5px;line-height:1.5}.coverage-actions.svelte-1uha8ag{flex-wrap:wrap;gap:8px;display:flex}.dropzone.svelte-1uha8ag{border:1.5px dashed var(--border);text-align:center;background:var(--bg-elev);padding:26px;transition:background .15s,border-color .15s}.dropzone.drag.svelte-1uha8ag{border-color:var(--accent);background:var(--accent-soft)}.dropzone.busy.svelte-1uha8ag{opacity:.7}.dropzone.svelte-1uha8ag p:where(.svelte-1uha8ag){color:var(--text-dim);margin:0 0 6px;font-size:14px}.visually-hidden.svelte-1uha8ag{clip:rect(0 0 0 0);width:1px;height:1px;position:absolute;overflow:hidden}.drop-errors.svelte-1uha8ag{background:var(--danger-bg);color:var(--danger);margin-top:10px;padding:10px 12px;font-size:12.5px}.drop-errors.svelte-1uha8ag strong:where(.svelte-1uha8ag){margin-bottom:4px;display:block}.drop-errors.svelte-1uha8ag ul:where(.svelte-1uha8ag){margin:0;padding-left:18px;line-height:1.6}.reports-col.svelte-1uha8ag{min-width:0;max-height:calc(100vh - 110px);position:sticky;top:88px;overflow:auto}.report-list.svelte-1uha8ag{flex-direction:column;gap:8px;margin:0;padding:0;list-style:none;display:flex}.report-item.svelte-1uha8ag{border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-card);align-items:stretch;display:flex;position:relative;overflow:visible}.report-item.svelte-1uha8ag:hover{background:var(--bg-card-hover);border-color:#33507a}.report-card.svelte-1uha8ag{min-width:0;color:var(--text);flex:1;padding:12px 8px 12px 14px;text-decoration:none;display:block}.report-actions.svelte-1uha8ag{flex-shrink:0;padding:8px 8px 0 0;position:relative}.kebab.svelte-1uha8ag{border-radius:var(--radius);width:40px;min-width:40px;height:40px;min-height:40px;color:var(--text-dim);cursor:pointer;background:0 0;border:1px solid #0000;justify-content:center;align-items:center;padding:0;display:inline-flex}.kebab.svelte-1uha8ag:hover{border-color:var(--border);color:var(--text);background:var(--bg)}.report-menu.svelte-1uha8ag{z-index:40;border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-card);width:148px;padding:4px;position:fixed;box-shadow:0 8px 24px #0000002e}.report-menu.svelte-1uha8ag .menu-item:where(.svelte-1uha8ag){width:100%;color:var(--text);text-align:left;cursor:pointer;background:0 0;border:0;padding:8px 10px;font-size:12.5px;display:block}.report-menu.svelte-1uha8ag .menu-item:where(.svelte-1uha8ag):hover{background:var(--accent-soft)}.report-menu.svelte-1uha8ag .menu-item.danger:where(.svelte-1uha8ag){color:var(--danger)}.report-top.svelte-1uha8ag{justify-content:space-between;align-items:baseline;gap:12px;display:flex}.report-top.svelte-1uha8ag strong:where(.svelte-1uha8ag){font-size:14px}.report-prov.svelte-1uha8ag{min-width:0;color:var(--text-dim);align-items:center;gap:6px;margin-top:4px;font-size:12px;display:flex}.report-prov.svelte-1uha8ag .prov-agent:where(.svelte-1uha8ag){text-overflow:ellipsis;white-space:nowrap;flex:0 auto;min-width:0;overflow:hidden}.report-prov.svelte-1uha8ag .prov-sep:where(.svelte-1uha8ag){color:var(--text-faint);flex-shrink:0}.report-prov.svelte-1uha8ag .prov-model:where(.svelte-1uha8ag){min-width:0;font-family:var(--mono);text-overflow:ellipsis;white-space:nowrap;flex:auto;font-size:11.5px;overflow:hidden}.when.svelte-1uha8ag{color:var(--text-dim);font-size:12px}.report-meta.svelte-1uha8ag{color:var(--text-dim);justify-content:space-between;align-items:baseline;gap:12px;margin-top:6px;font-size:12px;display:flex}.report-base.svelte-1uha8ag{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.base-arrow.svelte-1uha8ag{color:var(--text-faint)}.report-counts.svelte-1uha8ag{flex-shrink:0;align-items:baseline;gap:8px;display:flex}.pill.svelte-1uha8ag{font-family:var(--mono);font-size:11px}.pill.bad.svelte-1uha8ag{color:var(--danger)}.report-intent.svelte-1uha8ag{color:var(--text-dim);-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;margin:8px 0 0;font-size:12.5px;line-height:1.45;display:-webkit-box;overflow:hidden}@media (width<=960px){.workspace.svelte-1uha8ag{grid-template-columns:1fr}.reports-col.svelte-1uha8ag{max-height:none;position:static}}@media (width<=768px){.repo-row.svelte-1uha8ag{flex-direction:column;align-items:stretch}.repo-row.svelte-1uha8ag button:where(.svelte-1uha8ag){width:100%}.branches-row.svelte-1uha8ag{flex-direction:column}.branches-row.svelte-1uha8ag .field:where(.svelte-1uha8ag){width:100%;min-width:0}.row.svelte-1uha8ag{flex-direction:column}}
|
|
Binary file
|
|
Binary file
|