@coldtea/pr-lens-cli 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 +117 -0
- package/dist/args.d.ts +18 -0
- package/dist/args.d.ts.map +1 -0
- package/dist/args.js +49 -0
- package/dist/args.js.map +1 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +8 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +97 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/analyze.d.ts +4 -0
- package/dist/commands/analyze.d.ts.map +1 -0
- package/dist/commands/analyze.js +158 -0
- package/dist/commands/analyze.js.map +1 -0
- package/dist/commands/comment.d.ts +4 -0
- package/dist/commands/comment.d.ts.map +1 -0
- package/dist/commands/comment.js +63 -0
- package/dist/commands/comment.js.map +1 -0
- package/dist/commands/export.d.ts +4 -0
- package/dist/commands/export.d.ts.map +1 -0
- package/dist/commands/export.js +44 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/render.d.ts +4 -0
- package/dist/commands/render.d.ts.map +1 -0
- package/dist/commands/render.js +108 -0
- package/dist/commands/render.js.map +1 -0
- package/dist/commands/validate.d.ts +4 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +49 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/comment.d.ts +16 -0
- package/dist/comment.d.ts.map +1 -0
- package/dist/comment.js +148 -0
- package/dist/comment.js.map +1 -0
- package/dist/config-file.d.ts +10 -0
- package/dist/config-file.d.ts.map +1 -0
- package/dist/config-file.js +19 -0
- package/dist/config-file.js.map +1 -0
- package/dist/corrections.d.ts +16 -0
- package/dist/corrections.d.ts.map +1 -0
- package/dist/corrections.js +80 -0
- package/dist/corrections.js.map +1 -0
- package/dist/document.d.ts +23 -0
- package/dist/document.d.ts.map +1 -0
- package/dist/document.js +75 -0
- package/dist/document.js.map +1 -0
- package/dist/errors.d.ts +15 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +14 -0
- package/dist/errors.js.map +1 -0
- package/dist/extract.d.ts +54 -0
- package/dist/extract.d.ts.map +1 -0
- package/dist/extract.js +84 -0
- package/dist/extract.js.map +1 -0
- package/dist/git.d.ts +40 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +104 -0
- package/dist/git.js.map +1 -0
- package/dist/io.d.ts +6 -0
- package/dist/io.d.ts.map +1 -0
- package/dist/io.js +35 -0
- package/dist/io.js.map +1 -0
- package/dist/prompt.d.ts +23 -0
- package/dist/prompt.d.ts.map +1 -0
- package/dist/prompt.js +69 -0
- package/dist/prompt.js.map +1 -0
- package/dist/providers/gemini.d.ts +3 -0
- package/dist/providers/gemini.d.ts.map +1 -0
- package/dist/providers/gemini.js +46 -0
- package/dist/providers/gemini.js.map +1 -0
- package/dist/providers/http.d.ts +5 -0
- package/dist/providers/http.d.ts.map +1 -0
- package/dist/providers/http.js +26 -0
- package/dist/providers/http.js.map +1 -0
- package/dist/providers/index.d.ts +48 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +71 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/openai.d.ts +17 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +42 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/snapshot.d.ts +15 -0
- package/dist/snapshot.d.ts.map +1 -0
- package/dist/snapshot.js +77 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/terminal.d.ts +6 -0
- package/dist/terminal.d.ts.map +1 -0
- package/dist/terminal.js +5 -0
- package/dist/terminal.js.map +1 -0
- package/dist/version.d.ts +4 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +4 -0
- package/dist/version.js.map +1 -0
- package/package.json +57 -0
- package/src/args.ts +52 -0
- package/src/bin.ts +10 -0
- package/src/cli.ts +120 -0
- package/src/commands/analyze.ts +206 -0
- package/src/commands/comment.ts +77 -0
- package/src/commands/export.ts +61 -0
- package/src/commands/render.ts +125 -0
- package/src/commands/validate.ts +57 -0
- package/src/comment.ts +190 -0
- package/src/config-file.ts +26 -0
- package/src/corrections.ts +86 -0
- package/src/document.ts +119 -0
- package/src/errors.ts +36 -0
- package/src/extract.ts +136 -0
- package/src/git.ts +151 -0
- package/src/io.ts +38 -0
- package/src/prompt.ts +87 -0
- package/src/providers/gemini.ts +61 -0
- package/src/providers/http.ts +54 -0
- package/src/providers/index.ts +124 -0
- package/src/providers/openai.ts +68 -0
- package/src/snapshot.ts +102 -0
- package/src/terminal.ts +9 -0
- package/src/version.ts +4 -0
package/dist/document.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { assertNever, formatIssues, safeParseConfig, safeParseGraphDoc, safeParsePatchDoc, safeParseRenderManifest, } from "@coldtea/pr-lens-schema";
|
|
2
|
+
import { extname } from "node:path";
|
|
3
|
+
import { parse as parseYaml } from "yaml";
|
|
4
|
+
import { PrLensCliError } from "./errors.js";
|
|
5
|
+
import { readTextFile } from "./io.js";
|
|
6
|
+
export const describeDocumentKind = (kind) => {
|
|
7
|
+
switch (kind) {
|
|
8
|
+
case "graph":
|
|
9
|
+
return "graph document";
|
|
10
|
+
case "patch":
|
|
11
|
+
return "patch document";
|
|
12
|
+
case "render-manifest":
|
|
13
|
+
return "render manifest";
|
|
14
|
+
case "config":
|
|
15
|
+
return "config";
|
|
16
|
+
default:
|
|
17
|
+
return assertNever(kind, "Unhandled document kind");
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
export const invalidDocument = (path, kind, error) => new PrLensCliError("INVALID_DOCUMENT", `${path} is not a valid ${describeDocumentKind(kind)} [${error.code}]`, formatIssues(error.issues));
|
|
21
|
+
const take = (path, kind, parsed) => {
|
|
22
|
+
if (!parsed.ok)
|
|
23
|
+
throw invalidDocument(path, kind, parsed.error);
|
|
24
|
+
return parsed.value;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* YAML is a superset of JSON, so one parser reads both and a hand-written
|
|
28
|
+
* config gets the same treatment whichever spelling a repository chose.
|
|
29
|
+
*/
|
|
30
|
+
const readDocumentSource = async (path) => {
|
|
31
|
+
const text = await readTextFile(path);
|
|
32
|
+
try {
|
|
33
|
+
return parseYaml(text);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
37
|
+
const syntax = extname(path) === ".json" ? "JSON" : "YAML";
|
|
38
|
+
throw new PrLensCliError("UNREADABLE_FILE", `${path} is not valid ${syntax}`, detail);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
export const readGraphDoc = async (path) => take(path, "graph", safeParseGraphDoc(await readDocumentSource(path)));
|
|
42
|
+
export const readRenderManifest = async (path) => take(path, "render-manifest", safeParseRenderManifest(await readDocumentSource(path)));
|
|
43
|
+
export const readConfig = async (path) => take(path, "config", safeParseConfig(await readDocumentSource(path)));
|
|
44
|
+
/**
|
|
45
|
+
* `kind` names the document. Only a config lacks one, because it is the only
|
|
46
|
+
* one of the four a person writes by hand, and a discriminant in a file a
|
|
47
|
+
* person maintains buys nothing.
|
|
48
|
+
*/
|
|
49
|
+
const documentKindOf = (path, value) => {
|
|
50
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
51
|
+
throw new PrLensCliError("UNKNOWN_DOCUMENT", `${path} does not hold a PR Lens document`);
|
|
52
|
+
const kind = "kind" in value ? value.kind : undefined;
|
|
53
|
+
if (kind === undefined)
|
|
54
|
+
return "config";
|
|
55
|
+
if (kind === "graph" || kind === "patch" || kind === "render-manifest")
|
|
56
|
+
return kind;
|
|
57
|
+
throw new PrLensCliError("UNKNOWN_DOCUMENT", `${path} declares an unknown document kind ${JSON.stringify(kind)}`, "expected 'graph', 'patch' or 'render-manifest' — or no 'kind' at all, for a config");
|
|
58
|
+
};
|
|
59
|
+
export const validateDocumentFile = async (path) => {
|
|
60
|
+
const source = await readDocumentSource(path);
|
|
61
|
+
const kind = documentKindOf(path, source);
|
|
62
|
+
switch (kind) {
|
|
63
|
+
case "graph":
|
|
64
|
+
return { kind, document: take(path, kind, safeParseGraphDoc(source)) };
|
|
65
|
+
case "patch":
|
|
66
|
+
return { kind, document: take(path, kind, safeParsePatchDoc(source)) };
|
|
67
|
+
case "render-manifest":
|
|
68
|
+
return { kind, document: take(path, kind, safeParseRenderManifest(source)) };
|
|
69
|
+
case "config":
|
|
70
|
+
return { kind, document: take(path, kind, safeParseConfig(source)) };
|
|
71
|
+
default:
|
|
72
|
+
return assertNever(kind, "Unhandled document kind");
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
//# sourceMappingURL=document.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document.js","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every failure a run can end on, as a code a caller — a script, a workflow
|
|
3
|
+
* step, or a future command — can branch on. Messages are for humans and may
|
|
4
|
+
* be reworded; codes are the contract.
|
|
5
|
+
*/
|
|
6
|
+
export type CliErrorCode = "USAGE" | "UNREADABLE_FILE" | "UNKNOWN_DOCUMENT" | "INVALID_DOCUMENT" | "GIT_FAILED" | "EMPTY_DIFF" | "REPOSITORY_UNKNOWN" | "MISSING_API_KEY" | "PROVIDER_FAILED" | "MODEL_OUTPUT_INVALID" | "RENDER_FAILED";
|
|
7
|
+
export declare class PrLensCliError extends Error {
|
|
8
|
+
readonly code: CliErrorCode;
|
|
9
|
+
/** Extra lines printed under the message: validation issues, a git stderr, a hint. */
|
|
10
|
+
readonly details: string | undefined;
|
|
11
|
+
constructor(code: CliErrorCode, message: string, details?: string);
|
|
12
|
+
}
|
|
13
|
+
export declare const usageError: (message: string, details?: string) => PrLensCliError;
|
|
14
|
+
export declare const formatError: (error: PrLensCliError) => string;
|
|
15
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,YAAY,GACpB,OAAO,GACP,iBAAiB,GACjB,kBAAkB,GAClB,kBAAkB,GAClB,YAAY,GACZ,YAAY,GACZ,oBAAoB,GACpB,iBAAiB,GACjB,iBAAiB,GACjB,sBAAsB,GACtB,eAAe,CAAC;AAEpB,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,sFAAsF;IACtF,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAErC,YAAY,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAKhE;CACF;AAED,eAAO,MAAM,UAAU,YAAa,MAAM,YAAY,MAAM,KAAG,cAChB,CAAC;AAEhD,eAAO,MAAM,WAAW,UAAW,cAAc,KAAG,MACkD,CAAC"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export class PrLensCliError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
/** Extra lines printed under the message: validation issues, a git stderr, a hint. */
|
|
4
|
+
details;
|
|
5
|
+
constructor(code, message, details) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "PrLensCliError";
|
|
8
|
+
this.code = code;
|
|
9
|
+
this.details = details;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export const usageError = (message, details) => new PrLensCliError("USAGE", message, details);
|
|
13
|
+
export const formatError = (error) => [`✗ ${error.message} [${error.code}]`, error.details ?? ""].filter((line) => line !== "").join("\n");
|
|
14
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAkBA,MAAM,OAAO,cAAe,SAAQ,KAAK;IAC9B,IAAI,CAAe;IAC5B,sFAAsF;IAC7E,OAAO,CAAqB;IAErC,YAAY,IAAkB,EAAE,OAAe,EAAE,OAAgB;QAC/D,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC7B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,OAAe,EAAE,OAAgB,EAAkB,EAAE,CAC9E,IAAI,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAEhD,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,KAAqB,EAAU,EAAE,CAC3D,CAAC,KAAK,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC,IAAI,GAAG,EAAE,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { type GraphDoc, type GraphDocInput, type Lens } from "@coldtea/pr-lens-schema";
|
|
2
|
+
import type { JsonCompletion } from "./providers/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* The fields the repository already knows the answer to. They are written
|
|
5
|
+
* over whatever the model emitted rather than merged with it: a commit sha, a
|
|
6
|
+
* line count or the lens set that was asked for is a fact, and a fact the
|
|
7
|
+
* model is free to restate is a fact that eventually disagrees with itself.
|
|
8
|
+
*/
|
|
9
|
+
export type KnownFields = {
|
|
10
|
+
provenance: GraphDocInput["provenance"];
|
|
11
|
+
stats: {
|
|
12
|
+
filesChanged: number;
|
|
13
|
+
additions: number;
|
|
14
|
+
deletions: number;
|
|
15
|
+
};
|
|
16
|
+
lenses: readonly Lens[];
|
|
17
|
+
generatedAt: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Stamping the lens set is what makes `--lens` a request rather than a
|
|
21
|
+
* suggestion: a document whose flows or views need a lens that was not asked
|
|
22
|
+
* for now fails validation, and the correction round removes them.
|
|
23
|
+
*/
|
|
24
|
+
export declare const applyKnownFields: (body: Record<string, unknown>, known: KnownFields) => unknown;
|
|
25
|
+
export type JsonObjectRead = {
|
|
26
|
+
ok: true;
|
|
27
|
+
value: Record<string, unknown>;
|
|
28
|
+
} | {
|
|
29
|
+
ok: false;
|
|
30
|
+
reason: string;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Providers asked for JSON still fence it now and then, and some prepend a
|
|
34
|
+
* line of prose. Both are recoverable, and a run that already spent the tokens
|
|
35
|
+
* should not fail on packaging.
|
|
36
|
+
*
|
|
37
|
+
* A failure is returned rather than thrown, because truncated or malformed
|
|
38
|
+
* JSON is a weak model's most common answer and deserves the same correction
|
|
39
|
+
* round a structurally wrong document gets.
|
|
40
|
+
*/
|
|
41
|
+
export declare const readJsonObject: (text: string) => JsonObjectRead;
|
|
42
|
+
export type Complete = (request: JsonCompletion) => Promise<string>;
|
|
43
|
+
export type Extraction = {
|
|
44
|
+
document: GraphDoc;
|
|
45
|
+
attempts: number;
|
|
46
|
+
};
|
|
47
|
+
export declare const extractGraph: (options: {
|
|
48
|
+
system: string;
|
|
49
|
+
user: string;
|
|
50
|
+
maxOutputTokens: number;
|
|
51
|
+
known: KnownFields;
|
|
52
|
+
onAttempt?: (attempt: number) => void;
|
|
53
|
+
}, complete: Complete) => Promise<Extraction>;
|
|
54
|
+
//# sourceMappingURL=extract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../src/extract.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,IAAI,EACV,MAAM,yBAAyB,CAAC;AAGjC,OAAO,KAAK,EAAE,cAAc,EAAQ,MAAM,sBAAsB,CAAC;AAQjE;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,UAAU,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IACxC,KAAK,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE,MAAM,EAAE,SAAS,IAAI,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,SAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,SAAS,WAAW,KAAG,OAWpF,CAAC;AAIF,MAAM,MAAM,cAAc,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAC5C;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAElC;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,SAAU,MAAM,KAAG,cAqB7C,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAEpE,MAAM,MAAM,UAAU,GAAG;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAElE,eAAO,MAAM,YAAY,YACd;IACP,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,WAAW,CAAC;IACnB,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC,YACS,QAAQ,KACjB,OAAO,CAAC,UAAU,CAoCpB,CAAC"}
|
package/dist/extract.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { SCHEMA_VERSION, safeParseGraphDoc, } from "@coldtea/pr-lens-schema";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { PrLensCliError } from "./errors.js";
|
|
4
|
+
import { buildJsonRepairPrompt, buildRepairPrompt } from "./prompt.js";
|
|
5
|
+
/** One repair round. A model that cannot fix a named path in one go does not fix it in three. */
|
|
6
|
+
const MAX_ATTEMPTS = 2;
|
|
7
|
+
const JsonObject = z.record(z.string(), z.unknown());
|
|
8
|
+
/**
|
|
9
|
+
* Stamping the lens set is what makes `--lens` a request rather than a
|
|
10
|
+
* suggestion: a document whose flows or views need a lens that was not asked
|
|
11
|
+
* for now fails validation, and the correction round removes them.
|
|
12
|
+
*/
|
|
13
|
+
export const applyKnownFields = (body, known) => {
|
|
14
|
+
const authored = JsonObject.safeParse(body.stats);
|
|
15
|
+
return {
|
|
16
|
+
...body,
|
|
17
|
+
schemaVersion: SCHEMA_VERSION,
|
|
18
|
+
kind: "graph",
|
|
19
|
+
generatedAt: known.generatedAt,
|
|
20
|
+
provenance: known.provenance,
|
|
21
|
+
lenses: [...known.lenses],
|
|
22
|
+
stats: { ...(authored.success ? authored.data : {}), ...known.stats },
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
const FENCE = /^```(?:json)?\s*\n([\s\S]*?)\n?```\s*$/;
|
|
26
|
+
/**
|
|
27
|
+
* Providers asked for JSON still fence it now and then, and some prepend a
|
|
28
|
+
* line of prose. Both are recoverable, and a run that already spent the tokens
|
|
29
|
+
* should not fail on packaging.
|
|
30
|
+
*
|
|
31
|
+
* A failure is returned rather than thrown, because truncated or malformed
|
|
32
|
+
* JSON is a weak model's most common answer and deserves the same correction
|
|
33
|
+
* round a structurally wrong document gets.
|
|
34
|
+
*/
|
|
35
|
+
export const readJsonObject = (text) => {
|
|
36
|
+
const fenced = FENCE.exec(text.trim());
|
|
37
|
+
const candidate = fenced?.[1] ?? text;
|
|
38
|
+
const start = candidate.indexOf("{");
|
|
39
|
+
const end = candidate.lastIndexOf("}");
|
|
40
|
+
let reason = "the answer holds no JSON object";
|
|
41
|
+
const attempts = [candidate, start >= 0 && end > start ? candidate.slice(start, end + 1) : ""];
|
|
42
|
+
for (const attempt of attempts) {
|
|
43
|
+
if (attempt === "")
|
|
44
|
+
continue;
|
|
45
|
+
try {
|
|
46
|
+
const parsed = JsonObject.safeParse(JSON.parse(attempt));
|
|
47
|
+
if (parsed.success)
|
|
48
|
+
return { ok: true, value: parsed.data };
|
|
49
|
+
reason = "the answer is JSON, but not an object";
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
reason = error instanceof Error ? error.message : String(error);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return { ok: false, reason };
|
|
56
|
+
};
|
|
57
|
+
export const extractGraph = async (options, complete) => {
|
|
58
|
+
const turns = [{ role: "user", text: options.user }];
|
|
59
|
+
const giveUp = (message, details) => new PrLensCliError("MODEL_OUTPUT_INVALID", message, details);
|
|
60
|
+
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt += 1) {
|
|
61
|
+
options.onAttempt?.(attempt);
|
|
62
|
+
const last = attempt === MAX_ATTEMPTS;
|
|
63
|
+
const text = await complete({
|
|
64
|
+
system: options.system,
|
|
65
|
+
turns,
|
|
66
|
+
maxOutputTokens: options.maxOutputTokens,
|
|
67
|
+
});
|
|
68
|
+
const read = readJsonObject(text);
|
|
69
|
+
if (!read.ok) {
|
|
70
|
+
if (last)
|
|
71
|
+
throw giveUp("the model did not answer with a JSON object, twice", read.reason);
|
|
72
|
+
turns.push({ role: "model", text }, { role: "user", text: buildJsonRepairPrompt(read.reason) });
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const parsed = safeParseGraphDoc(applyKnownFields(read.value, options.known));
|
|
76
|
+
if (parsed.ok)
|
|
77
|
+
return { document: parsed.value, attempts: attempt };
|
|
78
|
+
if (last)
|
|
79
|
+
throw giveUp(`the model produced a document that does not validate, twice [${parsed.error.code}]`, parsed.error.message);
|
|
80
|
+
turns.push({ role: "model", text }, { role: "user", text: buildRepairPrompt(parsed.error) });
|
|
81
|
+
}
|
|
82
|
+
throw giveUp("extraction produced no document", "no attempt returned an answer");
|
|
83
|
+
};
|
|
84
|
+
//# sourceMappingURL=extract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract.js","sourceRoot":"","sources":["../src/extract.ts"],"names":[],"mappings":"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"}
|
package/dist/git.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export declare const git: (repo: string, args: readonly string[]) => Promise<string>;
|
|
2
|
+
export type Commit = {
|
|
3
|
+
sha: string;
|
|
4
|
+
ref: string | undefined;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* A ref becomes the commit it named at the moment of the run, and a branch
|
|
8
|
+
* keeps its name for the permalinks. `HEAD` is resolved to a branch name when
|
|
9
|
+
* it is on one, since "HEAD" tells a reader of the comment nothing.
|
|
10
|
+
*/
|
|
11
|
+
export declare const resolveCommit: (repo: string, ref: string) => Promise<Commit>;
|
|
12
|
+
/**
|
|
13
|
+
* The commit the pull request would be compared against, not the tip of the
|
|
14
|
+
* base branch: a diff against the tip would attribute every change made on
|
|
15
|
+
* the base branch since the fork point to this pull request.
|
|
16
|
+
*/
|
|
17
|
+
export declare const mergeBase: (repo: string, base: string, head: string) => Promise<string>;
|
|
18
|
+
export type ChangedFile = {
|
|
19
|
+
path: string;
|
|
20
|
+
additions: number | undefined;
|
|
21
|
+
deletions: number | undefined;
|
|
22
|
+
};
|
|
23
|
+
export type Diff = {
|
|
24
|
+
files: ChangedFile[];
|
|
25
|
+
additions: number;
|
|
26
|
+
deletions: number;
|
|
27
|
+
patch: string;
|
|
28
|
+
truncatedAt: number | undefined;
|
|
29
|
+
};
|
|
30
|
+
export declare const collectDiff: (repo: string, base: string, head: string, maxPatchBytes: number) => Promise<Diff>;
|
|
31
|
+
export type RepoSlug = {
|
|
32
|
+
owner: string;
|
|
33
|
+
name: string;
|
|
34
|
+
host: string;
|
|
35
|
+
};
|
|
36
|
+
export declare const parseRemoteUrl: (url: string) => RepoSlug | undefined;
|
|
37
|
+
export declare const parseRepoSlug: (slug: string) => RepoSlug | undefined;
|
|
38
|
+
export declare const remoteSlug: (repo: string, remote: string) => Promise<RepoSlug | undefined>;
|
|
39
|
+
export declare const repositoryRoot: (repo: string) => Promise<string>;
|
|
40
|
+
//# sourceMappingURL=git.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,GAAG,SAAgB,MAAM,QAAQ,SAAS,MAAM,EAAE,KAAG,OAAO,CAAC,MAAM,CAiB/E,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAE9D;;;;GAIG;AACH,eAAO,MAAM,aAAa,SAAgB,MAAM,OAAO,MAAM,KAAG,OAAO,CAAC,MAAM,CAM7E,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,SAAS,SAAgB,MAAM,QAAQ,MAAM,QAAQ,MAAM,KAAG,OAAO,CAAC,MAAM,CASxF,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,IAAI,GAAG;IACjB,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC,CAAC;AAmBF,eAAO,MAAM,WAAW,SAChB,MAAM,QACN,MAAM,QACN,MAAM,iBACG,MAAM,KACpB,OAAO,CAAC,IAAI,CAoBd,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAIrE,eAAO,MAAM,cAAc,QAAS,MAAM,KAAG,QAAQ,GAAG,SAavD,CAAC;AAEF,eAAO,MAAM,aAAa,SAAU,MAAM,KAAG,QAAQ,GAAG,SAMvD,CAAC;AAEF,eAAO,MAAM,UAAU,SAAgB,MAAM,UAAU,MAAM,KAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAG3F,CAAC;AAEF,eAAO,MAAM,cAAc,SAAgB,MAAM,KAAG,OAAO,CAAC,MAAM,CACN,CAAC"}
|
package/dist/git.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import { PrLensCliError } from "./errors.js";
|
|
4
|
+
const run = promisify(execFile);
|
|
5
|
+
/** Large enough for any diff worth sending to a model; a bigger one is truncated anyway. */
|
|
6
|
+
const MAX_GIT_OUTPUT_BYTES = 64 * 1024 * 1024;
|
|
7
|
+
export const git = async (repo, args) => {
|
|
8
|
+
try {
|
|
9
|
+
const { stdout } = await run("git", [...args], {
|
|
10
|
+
cwd: repo,
|
|
11
|
+
maxBuffer: MAX_GIT_OUTPUT_BYTES,
|
|
12
|
+
encoding: "utf8",
|
|
13
|
+
});
|
|
14
|
+
return stdout;
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
const stderr = typeof error === "object" && error !== null && "stderr" in error ? String(error.stderr) : "";
|
|
18
|
+
throw new PrLensCliError("GIT_FAILED", `git ${args.join(" ")} failed in ${repo}`, stderr.trim() || (error instanceof Error ? error.message : String(error)));
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* A ref becomes the commit it named at the moment of the run, and a branch
|
|
23
|
+
* keeps its name for the permalinks. `HEAD` is resolved to a branch name when
|
|
24
|
+
* it is on one, since "HEAD" tells a reader of the comment nothing.
|
|
25
|
+
*/
|
|
26
|
+
export const resolveCommit = async (repo, ref) => {
|
|
27
|
+
const sha = (await git(repo, ["rev-parse", "--verify", `${ref}^{commit}`])).trim();
|
|
28
|
+
if (ref !== "HEAD")
|
|
29
|
+
return { sha, ref };
|
|
30
|
+
const branch = (await git(repo, ["rev-parse", "--abbrev-ref", "HEAD"])).trim();
|
|
31
|
+
return { sha, ref: branch === "HEAD" ? undefined : branch };
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* The commit the pull request would be compared against, not the tip of the
|
|
35
|
+
* base branch: a diff against the tip would attribute every change made on
|
|
36
|
+
* the base branch since the fork point to this pull request.
|
|
37
|
+
*/
|
|
38
|
+
export const mergeBase = async (repo, base, head) => {
|
|
39
|
+
const merged = await git(repo, ["merge-base", base, head]).catch(() => undefined);
|
|
40
|
+
if (merged === undefined)
|
|
41
|
+
throw new PrLensCliError("GIT_FAILED", `${base} and ${head} have no common ancestor`, "fetch the base branch, or pass --base with a ref that shares history with the head");
|
|
42
|
+
return merged.trim();
|
|
43
|
+
};
|
|
44
|
+
/** `--numstat` reports a binary file's line counts as `-`, which is not zero. */
|
|
45
|
+
const parseNumstat = (stdout) => stdout
|
|
46
|
+
.split("\n")
|
|
47
|
+
.filter((line) => line.trim() !== "")
|
|
48
|
+
.flatMap((line) => {
|
|
49
|
+
const [additions, deletions, ...rest] = line.split("\t");
|
|
50
|
+
const path = rest.join("\t");
|
|
51
|
+
if (additions === undefined || deletions === undefined || path === "")
|
|
52
|
+
return [];
|
|
53
|
+
const count = (value) => value === "-" ? undefined : Number(value);
|
|
54
|
+
return [{ path, additions: count(additions), deletions: count(deletions) }];
|
|
55
|
+
});
|
|
56
|
+
const sum = (values) => values.reduce((total, value) => total + (value ?? 0), 0);
|
|
57
|
+
export const collectDiff = async (repo, base, head, maxPatchBytes) => {
|
|
58
|
+
const files = parseNumstat(await git(repo, ["diff", "--numstat", "--find-renames", base, head]));
|
|
59
|
+
const patch = await git(repo, [
|
|
60
|
+
"diff",
|
|
61
|
+
"--no-color",
|
|
62
|
+
"--find-renames",
|
|
63
|
+
"--unified=3",
|
|
64
|
+
base,
|
|
65
|
+
head,
|
|
66
|
+
]);
|
|
67
|
+
const withinBudget = Buffer.byteLength(patch, "utf8") <= maxPatchBytes;
|
|
68
|
+
return {
|
|
69
|
+
files,
|
|
70
|
+
additions: sum(files.map((file) => file.additions)),
|
|
71
|
+
deletions: sum(files.map((file) => file.deletions)),
|
|
72
|
+
patch: withinBudget ? patch : Buffer.from(patch, "utf8").subarray(0, maxPatchBytes).toString("utf8"),
|
|
73
|
+
truncatedAt: withinBudget ? undefined : maxPatchBytes,
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
const REMOTE_URL = /^(?:(?:ssh|git|https?):\/\/)?(?:[^@/]+@)?([^/:]+)[:/](.+?)(?:\.git)?\/?$/;
|
|
77
|
+
export const parseRemoteUrl = (url) => {
|
|
78
|
+
const match = REMOTE_URL.exec(url.trim());
|
|
79
|
+
if (!match)
|
|
80
|
+
return undefined;
|
|
81
|
+
const [, host, path] = match;
|
|
82
|
+
if (host === undefined || path === undefined)
|
|
83
|
+
return undefined;
|
|
84
|
+
const segments = path.split("/").filter((segment) => segment !== "");
|
|
85
|
+
const name = segments.at(-1);
|
|
86
|
+
const owner = segments.slice(0, -1).join("/");
|
|
87
|
+
if (name === undefined || owner === "")
|
|
88
|
+
return undefined;
|
|
89
|
+
return { owner, name, host };
|
|
90
|
+
};
|
|
91
|
+
export const parseRepoSlug = (slug) => {
|
|
92
|
+
const segments = slug.split("/").filter((segment) => segment !== "");
|
|
93
|
+
const name = segments.at(-1);
|
|
94
|
+
const owner = segments.slice(0, -1).join("/");
|
|
95
|
+
if (name === undefined || owner === "" || segments.length !== 2)
|
|
96
|
+
return undefined;
|
|
97
|
+
return { owner, name, host: "github.com" };
|
|
98
|
+
};
|
|
99
|
+
export const remoteSlug = async (repo, remote) => {
|
|
100
|
+
const url = await git(repo, ["remote", "get-url", remote]).catch(() => undefined);
|
|
101
|
+
return url === undefined ? undefined : parseRemoteUrl(url);
|
|
102
|
+
};
|
|
103
|
+
export const repositoryRoot = async (repo) => (await git(repo, ["rev-parse", "--show-toplevel"])).trim();
|
|
104
|
+
//# sourceMappingURL=git.js.map
|
package/dist/git.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"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"}
|
package/dist/io.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const readTextFile: (path: string) => Promise<string>;
|
|
2
|
+
export declare const readJsonFile: (path: string) => Promise<unknown>;
|
|
3
|
+
export declare const writeTextFile: (path: string, contents: string) => Promise<string>;
|
|
4
|
+
/** One trailing newline, so written documents behave in a diff and in a shell. */
|
|
5
|
+
export declare const writeJsonFile: (path: string, value: unknown) => Promise<string>;
|
|
6
|
+
//# sourceMappingURL=io.d.ts.map
|
package/dist/io.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"io.d.ts","sourceRoot":"","sources":["../src/io.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,YAAY,SAAgB,MAAM,KAAG,OAAO,CAAC,MAAM,CAM/D,CAAC;AAEF,eAAO,MAAM,YAAY,SAAgB,MAAM,KAAG,OAAO,CAAC,OAAO,CAOhE,CAAC;AAEF,eAAO,MAAM,aAAa,SAAgB,MAAM,YAAY,MAAM,KAAG,OAAO,CAAC,MAAM,CASlF,CAAC;AAEF,kFAAkF;AAClF,eAAO,MAAM,aAAa,SAAU,MAAM,SAAS,OAAO,KAAG,OAAO,CAAC,MAAM,CACf,CAAC"}
|
package/dist/io.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { PrLensCliError } from "./errors.js";
|
|
4
|
+
const describe = (error) => error instanceof Error ? error.message : String(error);
|
|
5
|
+
export const readTextFile = async (path) => {
|
|
6
|
+
try {
|
|
7
|
+
return await readFile(resolve(path), "utf8");
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
throw new PrLensCliError("UNREADABLE_FILE", `cannot read ${path}`, describe(error));
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
export const readJsonFile = async (path) => {
|
|
14
|
+
const text = await readTextFile(path);
|
|
15
|
+
try {
|
|
16
|
+
return JSON.parse(text);
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
throw new PrLensCliError("UNREADABLE_FILE", `${path} is not valid JSON`, describe(error));
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
export const writeTextFile = async (path, contents) => {
|
|
23
|
+
const absolute = resolve(path);
|
|
24
|
+
try {
|
|
25
|
+
await mkdir(dirname(absolute), { recursive: true });
|
|
26
|
+
await writeFile(absolute, contents, "utf8");
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
throw new PrLensCliError("UNREADABLE_FILE", `cannot write ${path}`, describe(error));
|
|
30
|
+
}
|
|
31
|
+
return absolute;
|
|
32
|
+
};
|
|
33
|
+
/** One trailing newline, so written documents behave in a diff and in a shell. */
|
|
34
|
+
export const writeJsonFile = (path, value) => writeTextFile(path, `${JSON.stringify(value, null, 2)}\n`);
|
|
35
|
+
//# sourceMappingURL=io.js.map
|
package/dist/io.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"io.js","sourceRoot":"","sources":["../src/io.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAU,EAAE,CAC1C,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAEzD,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAAE,IAAY,EAAmB,EAAE;IAClE,IAAI,CAAC;QACH,OAAO,MAAM,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,cAAc,CAAC,iBAAiB,EAAE,eAAe,IAAI,EAAE,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IACtF,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAAE,IAAY,EAAoB,EAAE;IACnE,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,cAAc,CAAC,iBAAiB,EAAE,GAAG,IAAI,oBAAoB,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IAC5F,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,EAAE,IAAY,EAAE,QAAgB,EAAmB,EAAE;IACrF,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACpD,MAAM,SAAS,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC9C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,cAAc,CAAC,iBAAiB,EAAE,gBAAgB,IAAI,EAAE,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IACvF,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC;AAEF,kFAAkF;AAClF,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,IAAY,EAAE,KAAc,EAAmB,EAAE,CAC7E,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC"}
|
package/dist/prompt.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Lens, PrLensSchemaError } from "@coldtea/pr-lens-schema";
|
|
2
|
+
import type { Diff } from "./git.js";
|
|
3
|
+
export type PromptContext = {
|
|
4
|
+
repo: {
|
|
5
|
+
owner: string;
|
|
6
|
+
name: string;
|
|
7
|
+
};
|
|
8
|
+
base: {
|
|
9
|
+
sha: string;
|
|
10
|
+
ref: string | undefined;
|
|
11
|
+
};
|
|
12
|
+
head: {
|
|
13
|
+
sha: string;
|
|
14
|
+
ref: string | undefined;
|
|
15
|
+
};
|
|
16
|
+
diff: Diff;
|
|
17
|
+
lenses: readonly Lens[];
|
|
18
|
+
};
|
|
19
|
+
export declare const SYSTEM_PROMPT: string;
|
|
20
|
+
export declare const buildExtractionPrompt: (context: PromptContext, jsonSchema: string) => string;
|
|
21
|
+
export declare const buildJsonRepairPrompt: (reason: string) => string;
|
|
22
|
+
export declare const buildRepairPrompt: (error: PrLensSchemaError) => string;
|
|
23
|
+
//# sourceMappingURL=prompt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACvE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAErC,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IAC/C,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IAC/C,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,SAAS,IAAI,EAAE,CAAC;CACzB,CAAC;AAEF,eAAO,MAAM,aAAa,QA0Bd,CAAC;AAWb,eAAO,MAAM,qBAAqB,YAAa,aAAa,cAAc,MAAM,KAAG,MAsBrE,CAAC;AAEf,eAAO,MAAM,qBAAqB,WAAY,MAAM,KAAG,MAKzC,CAAC;AAEf,eAAO,MAAM,iBAAiB,UAAW,iBAAiB,KAAG,MAO/C,CAAC"}
|
package/dist/prompt.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export const SYSTEM_PROMPT = [
|
|
2
|
+
"You read a pull request diff and produce one PR Lens graph document: the map a reviewer wishes they had before reading the code.",
|
|
3
|
+
"",
|
|
4
|
+
"You are not a review bot. You never report bugs, risks, style or security findings — there is no field for them and a document that carries them is rejected. Your only job is to describe what the system looks like and how data moves through it, and to mark what this change did to it.",
|
|
5
|
+
"",
|
|
6
|
+
"Rules the validator enforces, in the order they are usually broken:",
|
|
7
|
+
"- Unknown fields are rejected outright. Emit only what the schema names.",
|
|
8
|
+
"- Every edge endpoint and every flow participant must be a node id you declared, and every node must sit in a lane id you declared. An edge to an undeclared node is the single most common failure.",
|
|
9
|
+
"- Ids match ^[A-Za-z0-9][A-Za-z0-9._:/-]*$ and are unique within their collection. Prefer readable kebab-case, e.g. 'broadcast-sender'.",
|
|
10
|
+
"- File paths are repository-relative and POSIX: no leading slash, no drive letter, no backslash, no '..' segment.",
|
|
11
|
+
"- A flow's step order is the order of the messages array. There is no step number field.",
|
|
12
|
+
"- A message with kind 'self' has from equal to to, and no other kind may.",
|
|
13
|
+
"- A document that carries flows must declare the 'data-flow' lens.",
|
|
14
|
+
"",
|
|
15
|
+
"What makes the document worth reading:",
|
|
16
|
+
"- Include the unchanged neighbours the changed code touches, marked delta 'unchanged'. They are the context that makes blast radius legible; a diagram of only changed nodes says nothing about impact.",
|
|
17
|
+
"- Lanes are the reader's mental model of the system — a runtime, a tier, a boundary they already hold in their head — not the folder tree.",
|
|
18
|
+
"- Use as few lanes as carry that meaning: three or four. Every lane makes the diagram wider, and a diagram wider than a pull request comment is shown scaled down — past about four lanes the labels on the cards stop being readable without opening the image. If you reach six, two of them are usually the same boundary at different granularity: merge them, and express the finer split as a `group` on the nodes instead, which costs no width.",
|
|
19
|
+
"- Mark at most one or two edges 'hero': the connection the change is really about.",
|
|
20
|
+
"- Give the document a title a reviewer would recognise, and a summary that answers 'what does this change do?' in a short paragraph.",
|
|
21
|
+
"- Attach file refs to nodes, edges and steps wherever the diff shows where they live: they become the permalinks a reader clicks.",
|
|
22
|
+
"- Add a data flow only when the change actually has an ordered sequence worth animating. One good flow beats three thin ones.",
|
|
23
|
+
"- Build the drill-down tree: a view over the whole document, and a focused view for each part of the change that deserves its own section. Views are what a reader expands in the comment, so a document without them renders as one picture and nothing else.",
|
|
24
|
+
"- Stat chips carry the numbers that make the change legible, e.g. label 'Postmark calls' value '500x fewer'. Leave out files changed and line counts — those are filled in for you.",
|
|
25
|
+
"",
|
|
26
|
+
"Answer with the JSON document alone. No prose, no markdown fence.",
|
|
27
|
+
].join("\n");
|
|
28
|
+
const fileList = (diff) => diff.files
|
|
29
|
+
.map((file) => {
|
|
30
|
+
const added = file.additions === undefined ? "bin" : `+${file.additions}`;
|
|
31
|
+
const removed = file.deletions === undefined ? "" : ` -${file.deletions}`;
|
|
32
|
+
return ` ${file.path} (${added}${removed})`;
|
|
33
|
+
})
|
|
34
|
+
.join("\n");
|
|
35
|
+
export const buildExtractionPrompt = (context, jsonSchema) => [
|
|
36
|
+
`Repository: ${context.repo.owner}/${context.repo.name}`,
|
|
37
|
+
`Base commit: ${context.base.sha}${context.base.ref ? ` (${context.base.ref})` : ""}`,
|
|
38
|
+
`Head commit: ${context.head.sha}${context.head.ref ? ` (${context.head.ref})` : ""}`,
|
|
39
|
+
`Lenses to fill: ${context.lenses.join(", ")} — exactly these, and no others. Anything needing a lens that is not listed is out of scope for this document.`,
|
|
40
|
+
"",
|
|
41
|
+
`Changed files (${context.diff.files.length}, +${context.diff.additions} -${context.diff.deletions}):`,
|
|
42
|
+
fileList(context.diff),
|
|
43
|
+
"",
|
|
44
|
+
"Leave out schemaVersion, kind, generatedAt, provenance, and the numeric fields of stats. Those are filled in from the repository itself, and anything you write there is discarded.",
|
|
45
|
+
"",
|
|
46
|
+
"JSON Schema of the document (draft 2020-12). A field with a default may be omitted:",
|
|
47
|
+
"",
|
|
48
|
+
jsonSchema,
|
|
49
|
+
"",
|
|
50
|
+
"The diff follows.",
|
|
51
|
+
context.diff.truncatedAt === undefined
|
|
52
|
+
? ""
|
|
53
|
+
: `It was truncated at ${context.diff.truncatedAt} bytes, so the tail of the change is missing; describe what you can see and do not invent the rest.`,
|
|
54
|
+
"",
|
|
55
|
+
context.diff.patch,
|
|
56
|
+
].join("\n");
|
|
57
|
+
export const buildJsonRepairPrompt = (reason) => [
|
|
58
|
+
`That answer could not be read as JSON: ${reason}`,
|
|
59
|
+
"",
|
|
60
|
+
"Return the whole document again as a single JSON object and nothing else — no prose around it, no markdown fence, and nothing after the closing brace. If the previous answer was cut off, shorten the document so it fits: fewer nodes and one flow beat a document that never ends.",
|
|
61
|
+
].join("\n");
|
|
62
|
+
export const buildRepairPrompt = (error) => [
|
|
63
|
+
"That document was rejected. Each line is one problem, as a path into the document, a reason, and the machine code:",
|
|
64
|
+
"",
|
|
65
|
+
error.message,
|
|
66
|
+
"",
|
|
67
|
+
"Return the whole corrected document again, as JSON alone. Fix only what was named — do not restructure the parts that validated, and do not add fields the schema does not name.",
|
|
68
|
+
].join("\n");
|
|
69
|
+
//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,kIAAkI;IAClI,EAAE;IACF,8RAA8R;IAC9R,EAAE;IACF,qEAAqE;IACrE,0EAA0E;IAC1E,sMAAsM;IACtM,yIAAyI;IACzI,mHAAmH;IACnH,0FAA0F;IAC1F,2EAA2E;IAC3E,oEAAoE;IACpE,EAAE;IACF,wCAAwC;IACxC,yMAAyM;IACzM,4IAA4I;IAC5I,ybAAyb;IACzb,oFAAoF;IACpF,sIAAsI;IACtI,mIAAmI;IACnI,+HAA+H;IAC/H,gQAAgQ;IAChQ,qLAAqL;IACrL,EAAE;IACF,mEAAmE;CACpE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,MAAM,QAAQ,GAAG,CAAC,IAAU,EAAU,EAAE,CACtC,IAAI,CAAC,KAAK;KACP,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;IACZ,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;IAC1E,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC;IAC1E,OAAO,KAAK,IAAI,CAAC,IAAI,KAAK,KAAK,GAAG,OAAO,GAAG,CAAC;AAC/C,CAAC,CAAC;KACD,IAAI,CAAC,IAAI,CAAC,CAAC;AAEhB,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,OAAsB,EAAE,UAAkB,EAAU,EAAE,CAC1F;IACE,eAAe,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE;IACxD,gBAAgB,OAAO,CAAC,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;IACrF,gBAAgB,OAAO,CAAC,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;IACrF,mBAAmB,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,gHAAgH;IAC5J,EAAE;IACF,kBAAkB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,MAAM,OAAO,CAAC,IAAI,CAAC,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,SAAS,IAAI;IACtG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC;IACtB,EAAE;IACF,qLAAqL;IACrL,EAAE;IACF,qFAAqF;IACrF,EAAE;IACF,UAAU;IACV,EAAE;IACF,mBAAmB;IACnB,OAAO,CAAC,IAAI,CAAC,WAAW,KAAK,SAAS;QACpC,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,uBAAuB,OAAO,CAAC,IAAI,CAAC,WAAW,qGAAqG;IACxJ,EAAE;IACF,OAAO,CAAC,IAAI,CAAC,KAAK;CACnB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEf,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,MAAc,EAAU,EAAE,CAC9D;IACE,0CAA0C,MAAM,EAAE;IAClD,EAAE;IACF,uRAAuR;CACxR,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEf,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,KAAwB,EAAU,EAAE,CACpE;IACE,oHAAoH;IACpH,EAAE;IACF,KAAK,CAAC,OAAO;IACb,EAAE;IACF,kLAAkL;CACnL,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../src/providers/gemini.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAmB3D,eAAO,MAAM,kBAAkB,aACnB,QAAQ,WACT,cAAc,KACtB,OAAO,CAAC,MAAM,CAoChB,CAAC"}
|