ndrstnd 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 +201 -0
- package/README.md +56 -0
- package/dist/server/agent.d.ts +51 -0
- package/dist/server/agent.js +77 -0
- package/dist/server/agent.js.map +1 -0
- package/dist/server/analysis-core.d.ts +93 -0
- package/dist/server/analysis-core.js +442 -0
- package/dist/server/analysis-core.js.map +1 -0
- package/dist/server/analyze.d.ts +55 -0
- package/dist/server/analyze.js +72 -0
- package/dist/server/analyze.js.map +1 -0
- package/dist/server/artifact.d.ts +8 -0
- package/dist/server/artifact.js +43 -0
- package/dist/server/artifact.js.map +1 -0
- package/dist/server/claude.d.ts +29 -0
- package/dist/server/claude.js +243 -0
- package/dist/server/claude.js.map +1 -0
- package/dist/server/cli-support.d.ts +7 -0
- package/dist/server/cli-support.js +13 -0
- package/dist/server/cli-support.js.map +1 -0
- package/dist/server/cli.d.ts +2 -0
- package/dist/server/cli.js +276 -0
- package/dist/server/cli.js.map +1 -0
- package/dist/server/codex.d.ts +34 -0
- package/dist/server/codex.js +256 -0
- package/dist/server/codex.js.map +1 -0
- package/dist/server/conversation.d.ts +9 -0
- package/dist/server/conversation.js +57 -0
- package/dist/server/conversation.js.map +1 -0
- package/dist/server/evidence-ordering.d.ts +13 -0
- package/dist/server/evidence-ordering.js +143 -0
- package/dist/server/evidence-ordering.js.map +1 -0
- package/dist/server/git-model.d.ts +15 -0
- package/dist/server/git-model.js +156 -0
- package/dist/server/git-model.js.map +1 -0
- package/dist/server/git.d.ts +29 -0
- package/dist/server/git.js +124 -0
- package/dist/server/git.js.map +1 -0
- package/dist/server/http.d.ts +14 -0
- package/dist/server/http.js +139 -0
- package/dist/server/http.js.map +1 -0
- package/dist/server/preview-fixture.d.ts +1 -0
- package/dist/server/preview-fixture.js +23 -0
- package/dist/server/preview-fixture.js.map +1 -0
- package/dist/server/review-presentation.d.ts +3 -0
- package/dist/server/review-presentation.js +14 -0
- package/dist/server/review-presentation.js.map +1 -0
- package/dist/server/skill.d.ts +14 -0
- package/dist/server/skill.js +94 -0
- package/dist/server/skill.js.map +1 -0
- package/dist/server/store.d.ts +36 -0
- package/dist/server/store.js +134 -0
- package/dist/server/store.js.map +1 -0
- package/dist/server/version.d.ts +2 -0
- package/dist/server/version.js +13 -0
- package/dist/server/version.js.map +1 -0
- package/dist/shared/analysis-schema.d.ts +325 -0
- package/dist/shared/analysis-schema.js +50 -0
- package/dist/shared/analysis-schema.js.map +1 -0
- package/dist/shared/domain.d.ts +33 -0
- package/dist/shared/domain.js +2 -0
- package/dist/shared/domain.js.map +1 -0
- package/dist/web/evidence-model.d.ts +36 -0
- package/dist/web/evidence-model.js +134 -0
- package/dist/web/evidence-model.js.map +1 -0
- package/dist/web/frozen-review-data.d.ts +3 -0
- package/dist/web/frozen-review-data.js +202 -0
- package/dist/web/frozen-review-data.js.map +1 -0
- package/dist/web/language.d.ts +4 -0
- package/dist/web/language.js +14 -0
- package/dist/web/language.js.map +1 -0
- package/dist/web/page.d.ts +10 -0
- package/dist/web/page.js +1038 -0
- package/dist/web/page.js.map +1 -0
- package/dist/web/review-data.d.ts +20 -0
- package/dist/web/review-data.js +2 -0
- package/dist/web/review-data.js.map +1 -0
- package/dist/web/test-plan-model.d.ts +27 -0
- package/dist/web/test-plan-model.js +89 -0
- package/dist/web/test-plan-model.js.map +1 -0
- package/package.json +47 -0
- package/src/skill-assets/ndrstnd/SKILL.md +22 -0
- package/src/skill-assets/ndrstnd/agents/openai.yaml +4 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
export function parseNameStatus(output) {
|
|
3
|
+
const fields = output.split("\0");
|
|
4
|
+
const files = [];
|
|
5
|
+
for (let index = 0; index < fields.length - 1;) {
|
|
6
|
+
const statusField = fields[index++];
|
|
7
|
+
if (statusField === undefined || statusField === "")
|
|
8
|
+
continue;
|
|
9
|
+
const code = statusField[0] ?? "?";
|
|
10
|
+
const path = fields[index++] ?? "";
|
|
11
|
+
const previousPath = code === "R" || code === "C" ? path : undefined;
|
|
12
|
+
const finalPath = previousPath === undefined ? path : fields[index++] ?? path;
|
|
13
|
+
files.push({ id: stableId(finalPath), path: finalPath, previousPath, status: statusFor(code), binary: false, signal: "meaningful" });
|
|
14
|
+
}
|
|
15
|
+
return files;
|
|
16
|
+
}
|
|
17
|
+
export function parsePatch(patch, files) {
|
|
18
|
+
const filesByHeader = mapFilesByHeaderLine(files);
|
|
19
|
+
const hunks = [];
|
|
20
|
+
const binaryFileIds = new Set();
|
|
21
|
+
let currentFile;
|
|
22
|
+
let currentHunk;
|
|
23
|
+
const finishHunk = () => {
|
|
24
|
+
if (currentHunk === undefined)
|
|
25
|
+
return;
|
|
26
|
+
const { oldCursor: _oldCursor, newCursor: _newCursor, ...hunk } = currentHunk;
|
|
27
|
+
hunks.push({ ...hunk, id: stableId(`${hunk.fileId}:${hunk.oldStart}:${hunk.newStart}:${hunks.length}`) });
|
|
28
|
+
currentHunk = undefined;
|
|
29
|
+
};
|
|
30
|
+
for (const line of patch.split("\n")) {
|
|
31
|
+
if (line.startsWith("diff --git ")) {
|
|
32
|
+
finishHunk();
|
|
33
|
+
currentFile = filesByHeader.get(line);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (currentFile === undefined)
|
|
37
|
+
continue;
|
|
38
|
+
if (line.startsWith("Binary files ") || line.startsWith("GIT binary patch")) {
|
|
39
|
+
binaryFileIds.add(currentFile.id);
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
const header = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/.exec(line);
|
|
43
|
+
if (header !== null) {
|
|
44
|
+
finishHunk();
|
|
45
|
+
const oldStart = Number(header[1]);
|
|
46
|
+
const newStart = Number(header[2]);
|
|
47
|
+
currentHunk = { fileId: currentFile.id, oldStart, newStart, oldCursor: oldStart, newCursor: newStart, lines: [] };
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (currentHunk === undefined || line.startsWith("\\ No newline"))
|
|
51
|
+
continue;
|
|
52
|
+
if (line.startsWith("+")) {
|
|
53
|
+
currentHunk.lines.push({ kind: "addition", content: line.slice(1), newLine: currentHunk.newCursor++ });
|
|
54
|
+
}
|
|
55
|
+
else if (line.startsWith("-")) {
|
|
56
|
+
currentHunk.lines.push({ kind: "deletion", content: line.slice(1), oldLine: currentHunk.oldCursor++ });
|
|
57
|
+
}
|
|
58
|
+
else if (line.startsWith(" ")) {
|
|
59
|
+
currentHunk.lines.push({ kind: "context", content: line.slice(1), oldLine: currentHunk.oldCursor++, newLine: currentHunk.newCursor++ });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
finishHunk();
|
|
63
|
+
return { hunks, binaryFileIds };
|
|
64
|
+
}
|
|
65
|
+
export function untrackedFiles(paths, knownFiles) {
|
|
66
|
+
const knownPaths = new Set(knownFiles.map((file) => file.path));
|
|
67
|
+
return paths.flatMap((path) => knownPaths.has(path) ? [] : [{ id: stableId(path), path, status: "added", binary: false, signal: "meaningful" }]);
|
|
68
|
+
}
|
|
69
|
+
export function finalizeFiles(files, hunks, binaryFileIds) {
|
|
70
|
+
const hunkFileIds = new Set(hunks.map((hunk) => hunk.fileId));
|
|
71
|
+
return files.map((file) => {
|
|
72
|
+
const binary = binaryFileIds.has(file.id);
|
|
73
|
+
const classification = classifyFile(file.path, binary);
|
|
74
|
+
if (!binary && !hunkFileIds.has(file.id) && classification.signal === "meaningful") {
|
|
75
|
+
return { ...file, binary, signal: "low-signal", signalReason: hunklessReason(file.status) };
|
|
76
|
+
}
|
|
77
|
+
return { ...file, binary, signal: classification.signal, signalReason: classification.reason };
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
function hunklessReason(status) {
|
|
81
|
+
if (status === "renamed")
|
|
82
|
+
return "Rename without content changes";
|
|
83
|
+
if (status === "copied")
|
|
84
|
+
return "Copy without content changes";
|
|
85
|
+
return "No content changes";
|
|
86
|
+
}
|
|
87
|
+
export function classifyFile(path, binary) {
|
|
88
|
+
const normalized = path.toLowerCase();
|
|
89
|
+
if (binary)
|
|
90
|
+
return { signal: "low-signal", reason: "Binary change" };
|
|
91
|
+
if (/(^|\/)(node_modules|vendor|vendors|third_party|third-party)\//.test(normalized))
|
|
92
|
+
return { signal: "low-signal", reason: "Vendored dependency" };
|
|
93
|
+
if (/(^|\/)(package-lock\.json|npm-shrinkwrap\.json|yarn\.lock|pnpm-lock\.yaml|podfile\.lock|cargo\.lock|package\.resolved)$/.test(normalized))
|
|
94
|
+
return { signal: "low-signal", reason: "Lockfile" };
|
|
95
|
+
if (/(^|\/)(dist|build|coverage)\//.test(normalized) || /\.min\.(js|css)$/.test(normalized))
|
|
96
|
+
return { signal: "low-signal", reason: "Generated output" };
|
|
97
|
+
return { signal: "meaningful" };
|
|
98
|
+
}
|
|
99
|
+
export function statusFor(code) {
|
|
100
|
+
const statuses = { A: "added", M: "modified", D: "deleted", R: "renamed", C: "copied" };
|
|
101
|
+
return statuses[code] ?? "unknown";
|
|
102
|
+
}
|
|
103
|
+
export function stableId(value) {
|
|
104
|
+
return createHash("sha256").update(value).digest("hex").slice(0, 16);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Git prints each `diff --git` header side raw or C-quoted depending on the path and core.quotePath,
|
|
108
|
+
* and a raw path may itself contain ` b/`, so header lines cannot be split reliably. Matching whole
|
|
109
|
+
* header lines precomputed from the known files sidesteps both ambiguities.
|
|
110
|
+
*/
|
|
111
|
+
function mapFilesByHeaderLine(files) {
|
|
112
|
+
const byHeader = new Map();
|
|
113
|
+
for (const file of files) {
|
|
114
|
+
for (const oldSide of headerSideForms(`a/${file.previousPath ?? file.path}`)) {
|
|
115
|
+
for (const newSide of headerSideForms(`b/${file.path}`)) {
|
|
116
|
+
byHeader.set(`diff --git ${oldSide} ${newSide}`, file);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return byHeader;
|
|
121
|
+
}
|
|
122
|
+
function headerSideForms(side) {
|
|
123
|
+
return new Set([side, cQuote(side, true), cQuote(side, false)]);
|
|
124
|
+
}
|
|
125
|
+
const C_QUOTE_ESCAPES = new Map([
|
|
126
|
+
[0x07, "\\a"], [0x08, "\\b"], [0x09, "\\t"], [0x0a, "\\n"], [0x0b, "\\v"], [0x0c, "\\f"], [0x0d, "\\r"], [0x22, "\\\""], [0x5c, "\\\\"],
|
|
127
|
+
]);
|
|
128
|
+
function cQuote(side, quoteNonAscii) {
|
|
129
|
+
let quoted = "";
|
|
130
|
+
let needsQuoting = false;
|
|
131
|
+
for (const character of side) {
|
|
132
|
+
const code = character.codePointAt(0) ?? 0;
|
|
133
|
+
const escape = C_QUOTE_ESCAPES.get(code);
|
|
134
|
+
if (escape !== undefined) {
|
|
135
|
+
quoted += escape;
|
|
136
|
+
needsQuoting = true;
|
|
137
|
+
}
|
|
138
|
+
else if (code < 0x20 || code === 0x7f) {
|
|
139
|
+
quoted += octalEscape(code);
|
|
140
|
+
needsQuoting = true;
|
|
141
|
+
}
|
|
142
|
+
else if (code > 0x7f && quoteNonAscii) {
|
|
143
|
+
for (const byte of Buffer.from(character, "utf8"))
|
|
144
|
+
quoted += octalEscape(byte);
|
|
145
|
+
needsQuoting = true;
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
quoted += character;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return needsQuoting ? `"${quoted}"` : side;
|
|
152
|
+
}
|
|
153
|
+
function octalEscape(byte) {
|
|
154
|
+
return `\\${byte.toString(8).padStart(3, "0")}`;
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=git-model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-model.js","sourceRoot":"","sources":["../../src/server/git-model.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ChangedFile, DiffHunk } from "../shared/domain.js";
|
|
2
|
+
export interface CollectedReviewInput {
|
|
3
|
+
repoPath: string;
|
|
4
|
+
targetRef: string;
|
|
5
|
+
baseRef: string;
|
|
6
|
+
mergeBase: string;
|
|
7
|
+
includesWorkingTree?: boolean;
|
|
8
|
+
files: ChangedFile[];
|
|
9
|
+
hunks: DiffHunk[];
|
|
10
|
+
}
|
|
11
|
+
export interface GitRepositoryReader {
|
|
12
|
+
collectReviewInput(repoPath: string, targetRef?: string, explicitBaseRef?: string): Promise<CollectedReviewInput>;
|
|
13
|
+
}
|
|
14
|
+
export declare class GitReader implements GitRepositoryReader {
|
|
15
|
+
/** Without a targetRef the working tree is reviewed, so any branch name, including one literally called WORKTREE, stays reviewable. */
|
|
16
|
+
collectReviewInput(repoPath: string, targetRef?: string, explicitBaseRef?: string): Promise<CollectedReviewInput>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Makes the artifact directory Git-ignored in the reviewed repository via .git/info/exclude,
|
|
20
|
+
* so artifacts never enter a working-tree review or risk being committed. Excluding through
|
|
21
|
+
* the repository's own .gitignore would modify a tracked file, which a review must not do.
|
|
22
|
+
*/
|
|
23
|
+
export declare function ensureArtifactDirectoryIgnored(repoPath: string): Promise<void>;
|
|
24
|
+
export interface ReviewScope {
|
|
25
|
+
targetLabel: string;
|
|
26
|
+
localCommitsIncluded: number;
|
|
27
|
+
}
|
|
28
|
+
/** Describes what a collected review covers so the CLI can confirm the scope before the expensive analysis. */
|
|
29
|
+
export declare function describeReviewScope(repoPath: string, input: CollectedReviewInput): Promise<ReviewScope>;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { execFile as execFileCallback } from "node:child_process";
|
|
2
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname, isAbsolute, join } from "node:path";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
import { finalizeFiles, parseNameStatus, parsePatch, untrackedFiles } from "./git-model.js";
|
|
6
|
+
const execFile = promisify(execFileCallback);
|
|
7
|
+
const EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
8
|
+
export class GitReader {
|
|
9
|
+
/** Without a targetRef the working tree is reviewed, so any branch name, including one literally called WORKTREE, stays reviewable. */
|
|
10
|
+
async collectReviewInput(repoPath, targetRef, explicitBaseRef) {
|
|
11
|
+
await git(repoPath, ["rev-parse", "--show-toplevel"]);
|
|
12
|
+
const workingTreeTarget = targetRef === undefined;
|
|
13
|
+
if (targetRef !== undefined)
|
|
14
|
+
await git(repoPath, ["rev-parse", "--verify", targetRef]);
|
|
15
|
+
const targetLabel = targetRef ?? await resolveWorktreeTargetRef(repoPath);
|
|
16
|
+
const baseRef = explicitBaseRef === "empty" ? EMPTY_TREE : explicitBaseRef ?? await resolveBaseRef(repoPath, targetLabel);
|
|
17
|
+
if (baseRef !== EMPTY_TREE)
|
|
18
|
+
await git(repoPath, ["rev-parse", "--verify", baseRef]);
|
|
19
|
+
const head = await gitOptional(repoPath, ["rev-parse", "HEAD"]);
|
|
20
|
+
const target = targetRef === undefined ? head?.trim() : (await git(repoPath, ["rev-parse", targetRef])).trim();
|
|
21
|
+
const includeWorkingTree = workingTreeTarget || (head !== undefined && head.trim() === target);
|
|
22
|
+
if (target === undefined && baseRef !== EMPTY_TREE)
|
|
23
|
+
throw new Error("A repository without commits needs `--base empty` for a working-tree review.");
|
|
24
|
+
const mergeBase = baseRef === EMPTY_TREE ? EMPTY_TREE : (await git(repoPath, ["merge-base", baseRef, target ?? baseRef])).trim();
|
|
25
|
+
// Diffing the merge-base rather than the base tip keeps upstream commits landed after the fork point out of the review.
|
|
26
|
+
const comparison = targetRef === undefined || includeWorkingTree ? [mergeBase] : [mergeBase, targetRef];
|
|
27
|
+
const nameStatus = await git(repoPath, ["diff", "--name-status", "-z", "--find-renames", "--find-copies", ...comparison]);
|
|
28
|
+
const trackedFiles = parseNameStatus(nameStatus);
|
|
29
|
+
// Without --binary git prints a one-line "Binary files ... differ" marker, which is all parsePatch needs; the full binary payload would only risk overflowing maxBuffer.
|
|
30
|
+
const patch = await git(repoPath, ["diff", "--no-ext-diff", "--unified=3", "--find-renames", "--find-copies", ...comparison]);
|
|
31
|
+
const untracked = includeWorkingTree ? await collectUntrackedPatch(repoPath, trackedFiles) : { files: [], patch: "" };
|
|
32
|
+
const files = [...trackedFiles, ...untracked.files];
|
|
33
|
+
const parsedPatch = parsePatch(`${patch}\n${untracked.patch}`, files);
|
|
34
|
+
const hunks = parsedPatch.hunks;
|
|
35
|
+
const finalizedFiles = finalizeFiles(files, hunks, parsedPatch.binaryFileIds);
|
|
36
|
+
return { repoPath, targetRef: targetLabel, baseRef: explicitBaseRef === "empty" ? "empty" : baseRef, mergeBase, includesWorkingTree: includeWorkingTree, files: finalizedFiles, hunks };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Makes the artifact directory Git-ignored in the reviewed repository via .git/info/exclude,
|
|
41
|
+
* so artifacts never enter a working-tree review or risk being committed. Excluding through
|
|
42
|
+
* the repository's own .gitignore would modify a tracked file, which a review must not do.
|
|
43
|
+
*/
|
|
44
|
+
export async function ensureArtifactDirectoryIgnored(repoPath) {
|
|
45
|
+
if (await gitOptional(repoPath, ["check-ignore", "-q", ".ndrstnd"]) !== undefined)
|
|
46
|
+
return;
|
|
47
|
+
const commonDir = (await git(repoPath, ["rev-parse", "--git-common-dir"])).trim();
|
|
48
|
+
const excludePath = join(isAbsolute(commonDir) ? commonDir : join(repoPath, commonDir), "info", "exclude");
|
|
49
|
+
await mkdir(dirname(excludePath), { recursive: true });
|
|
50
|
+
const existing = await readFile(excludePath, "utf8").catch(() => "");
|
|
51
|
+
const separator = existing === "" || existing.endsWith("\n") ? "" : "\n";
|
|
52
|
+
await writeFile(excludePath, `${existing}${separator}.ndrstnd/\n`, "utf8");
|
|
53
|
+
}
|
|
54
|
+
/** Describes what a collected review covers so the CLI can confirm the scope before the expensive analysis. */
|
|
55
|
+
export async function describeReviewScope(repoPath, input) {
|
|
56
|
+
const targetLabel = input.targetRef;
|
|
57
|
+
if (input.includesWorkingTree !== true || input.baseRef === "empty")
|
|
58
|
+
return { targetLabel, localCommitsIncluded: 0 };
|
|
59
|
+
const count = await gitOptional(repoPath, ["rev-list", "--count", `${input.baseRef}..HEAD`]);
|
|
60
|
+
return { targetLabel, localCommitsIncluded: Number(count?.trim() ?? "0") };
|
|
61
|
+
}
|
|
62
|
+
async function collectUntrackedPatch(repoPath, files) {
|
|
63
|
+
const untracked = (await git(repoPath, ["ls-files", "--others", "--exclude-standard", "-z"])).split("\0").filter(Boolean);
|
|
64
|
+
const patches = [];
|
|
65
|
+
for (const path of untracked) {
|
|
66
|
+
patches.push(await gitAllowDifference(repoPath, ["diff", "--no-index", "--unified=3", "--", "/dev/null", path]));
|
|
67
|
+
}
|
|
68
|
+
return { files: untrackedFiles(untracked, files), patch: patches.join("\n") };
|
|
69
|
+
}
|
|
70
|
+
async function resolveBaseRef(repoPath, targetRef) {
|
|
71
|
+
const head = (await git(repoPath, ["rev-parse", "HEAD"])).trim();
|
|
72
|
+
const target = (await git(repoPath, ["rev-parse", targetRef])).trim();
|
|
73
|
+
if (head !== target) {
|
|
74
|
+
return "HEAD";
|
|
75
|
+
}
|
|
76
|
+
const upstream = await gitOptional(repoPath, ["rev-parse", "--abbrev-ref", `${targetRef}@{upstream}`]);
|
|
77
|
+
if (upstream !== undefined) {
|
|
78
|
+
return upstream.trim();
|
|
79
|
+
}
|
|
80
|
+
for (const candidate of ["main", "master", "origin/main", "origin/master"]) {
|
|
81
|
+
if (candidate === targetRef) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (await gitOptional(repoPath, ["rev-parse", "--verify", candidate]) !== undefined) {
|
|
85
|
+
return candidate;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
throw new Error(`Cannot infer a base for ${targetRef}. Check out its base branch or configure an upstream branch.`);
|
|
89
|
+
}
|
|
90
|
+
async function resolveWorktreeTargetRef(repoPath) {
|
|
91
|
+
const branch = await gitOptional(repoPath, ["symbolic-ref", "--quiet", "--short", "HEAD"]);
|
|
92
|
+
return branch?.trim() || "HEAD";
|
|
93
|
+
}
|
|
94
|
+
async function git(repoPath, args) {
|
|
95
|
+
try {
|
|
96
|
+
const { stdout } = await execFile("git", ["-C", repoPath, ...args], { encoding: "utf8", maxBuffer: 16 * 1024 * 1024 });
|
|
97
|
+
return stdout;
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
101
|
+
throw new Error(`Git could not ${args.join(" ")}: ${detail}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async function gitOptional(repoPath, args) {
|
|
105
|
+
try {
|
|
106
|
+
return await git(repoPath, args);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async function gitAllowDifference(repoPath, args) {
|
|
113
|
+
try {
|
|
114
|
+
const { stdout } = await execFile("git", ["-C", repoPath, ...args], { encoding: "utf8", maxBuffer: 16 * 1024 * 1024 });
|
|
115
|
+
return stdout;
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
const result = error;
|
|
119
|
+
if (result.code === 1 && typeof result.stdout === "string")
|
|
120
|
+
return result.stdout;
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/server/git.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AnalysisRevision, ReviewStore, StoredReviewSession } from "./store.js";
|
|
2
|
+
import { type ReviewAgent } from "./agent.js";
|
|
3
|
+
export interface ReviewServer {
|
|
4
|
+
url: string;
|
|
5
|
+
close(): Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
export interface ReviewServerOptions {
|
|
8
|
+
port?: number;
|
|
9
|
+
session?: StoredReviewSession;
|
|
10
|
+
revision?: AnalysisRevision;
|
|
11
|
+
store?: ReviewStore;
|
|
12
|
+
agent?: ReviewAgent;
|
|
13
|
+
}
|
|
14
|
+
export declare function startReviewServer({ port, session, revision, store, agent }?: ReviewServerOptions): Promise<ReviewServer>;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { createServer } from "node:http";
|
|
3
|
+
import { isAgentRevision } from "./store.js";
|
|
4
|
+
import { codexAgent } from "./agent.js";
|
|
5
|
+
import { analyzeWithAgent, answerQuestionWithAgent } from "./analyze.js";
|
|
6
|
+
import { renderWorkspace } from "../web/page.js";
|
|
7
|
+
import { createReviewPresentationData } from "./review-presentation.js";
|
|
8
|
+
export async function startReviewServer({ port = 0, session, revision, store, agent = codexAgent } = {}) {
|
|
9
|
+
const launchToken = randomBytes(24).toString("base64url");
|
|
10
|
+
const server = createServer(async (request, response) => {
|
|
11
|
+
const requestUrl = new URL(request.url ?? "/", "http://127.0.0.1");
|
|
12
|
+
if (requestUrl.pathname === "/api/health") {
|
|
13
|
+
response.writeHead(200, { "content-type": "application/json; charset=utf-8" });
|
|
14
|
+
response.end(JSON.stringify({ status: "ok" }));
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const authorized = requestUrl.searchParams.get("token") === launchToken;
|
|
18
|
+
if (!authorized) {
|
|
19
|
+
response.writeHead(404, { "content-type": "text/plain; charset=utf-8" });
|
|
20
|
+
response.end("Not found");
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const activeRevision = session === undefined ? revision : store?.listRevisions(session.id).find(isAgentRevision) ?? revision;
|
|
24
|
+
if (session !== undefined && requestUrl.pathname === `/api/session/${session.id}` && requestUrl.searchParams.get("token") === launchToken) {
|
|
25
|
+
response.writeHead(200, { "content-type": "application/json; charset=utf-8", "cache-control": "no-store" });
|
|
26
|
+
response.end(JSON.stringify(session));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (activeRevision !== undefined && requestUrl.pathname === `/api/revision/${activeRevision.id}` && requestUrl.searchParams.get("token") === launchToken) {
|
|
30
|
+
response.writeHead(200, { "content-type": "application/json; charset=utf-8", "cache-control": "no-store" });
|
|
31
|
+
response.end(JSON.stringify(activeRevision));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (store !== undefined && session !== undefined && requestUrl.pathname === `/api/session/${session.id}/revisions`) {
|
|
35
|
+
return sendJson(response, store.listRevisions(session.id));
|
|
36
|
+
}
|
|
37
|
+
if (store !== undefined && requestUrl.pathname === "/api/lenses") {
|
|
38
|
+
return sendJson(response, store.listLenses());
|
|
39
|
+
}
|
|
40
|
+
if (store !== undefined && requestUrl.pathname === "/api/preferences") {
|
|
41
|
+
if (request.method === "GET")
|
|
42
|
+
return sendJson(response, { zoom: store.getPreference("zoom") });
|
|
43
|
+
if (request.method === "POST") {
|
|
44
|
+
try {
|
|
45
|
+
const body = await readJson(request);
|
|
46
|
+
if (typeof body.zoom !== "number" || !Number.isInteger(body.zoom) || body.zoom < 0 || body.zoom > 4)
|
|
47
|
+
return sendJson(response, { error: "zoom must be an integer from 0 through 4." }, 400);
|
|
48
|
+
store.setPreference("zoom", String(body.zoom));
|
|
49
|
+
return sendJson(response, { zoom: body.zoom });
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
return sendJson(response, { error: error instanceof Error ? error.message : String(error) }, 400);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (store !== undefined && session !== undefined && activeRevision !== undefined && requestUrl.pathname === `/api/revision/${activeRevision.id}/questions`) {
|
|
57
|
+
if (request.method === "GET")
|
|
58
|
+
return sendJson(response, store.listQuestions(activeRevision.id));
|
|
59
|
+
if (request.method === "POST") {
|
|
60
|
+
try {
|
|
61
|
+
const body = await readJson(request);
|
|
62
|
+
const selection = typeof body.selection === "string" ? body.selection.slice(0, 20_000) : "";
|
|
63
|
+
const question = typeof body.question === "string" ? body.question.slice(0, 2_000) : "";
|
|
64
|
+
if (selection === "" || question === "")
|
|
65
|
+
return sendJson(response, { error: "Selection and question are required." }, 400);
|
|
66
|
+
const card = store.createQuestion(activeRevision.id, selection, question);
|
|
67
|
+
const answer = await answerQuestionWithAgent(agent, session.input, session.conversation, selection, question);
|
|
68
|
+
store.answerQuestion(card.id, answer.answer, answer.provenance);
|
|
69
|
+
return sendJson(response, { ...card, answer: answer.answer, provenance: answer.provenance });
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
return sendJson(response, { error: error instanceof Error ? error.message : String(error) }, 500);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (store !== undefined && session !== undefined && requestUrl.pathname === `/api/session/${session.id}/reanalyze` && request.method === "POST") {
|
|
77
|
+
try {
|
|
78
|
+
const body = await readJson(request);
|
|
79
|
+
const lens = store.getLens(typeof body.lensId === "string" ? body.lensId : "default") ?? store.getLens("default");
|
|
80
|
+
if (lens === undefined)
|
|
81
|
+
return sendJson(response, { error: "No default review lens is configured." }, 500);
|
|
82
|
+
const document = await analyzeWithAgent(agent, session.input, session.conversation, undefined, lens.instructions);
|
|
83
|
+
const next = store.createRevision(session.id, agent.id, "complete", document);
|
|
84
|
+
return sendJson(response, next);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
return sendJson(response, { error: error instanceof Error ? error.message : String(error) }, 500);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (requestUrl.pathname !== "/") {
|
|
91
|
+
response.writeHead(404, { "content-type": "text/plain; charset=utf-8" });
|
|
92
|
+
response.end("Not found");
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
response.writeHead(200, {
|
|
96
|
+
"content-type": "text/html; charset=utf-8",
|
|
97
|
+
"cache-control": "no-store",
|
|
98
|
+
});
|
|
99
|
+
response.end(session === undefined || activeRevision === undefined ? "ndrstnd is waiting for a review session." : await renderWorkspace(createReviewPresentationData(session, activeRevision), launchToken));
|
|
100
|
+
});
|
|
101
|
+
await new Promise((resolve, reject) => {
|
|
102
|
+
server.once("error", reject);
|
|
103
|
+
server.listen({ host: "127.0.0.1", port }, () => {
|
|
104
|
+
server.off("error", reject);
|
|
105
|
+
resolve();
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
const address = server.address();
|
|
109
|
+
if (address === null || typeof address === "string") {
|
|
110
|
+
await closeServer(server);
|
|
111
|
+
throw new Error("ndrstnd could not determine its local server address.");
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
url: `http://127.0.0.1:${address.port}/?token=${launchToken}`,
|
|
115
|
+
close: () => closeServer(server),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function sendJson(response, value, status = 200) {
|
|
119
|
+
response.writeHead(status, { "content-type": "application/json; charset=utf-8", "cache-control": "no-store" });
|
|
120
|
+
response.end(JSON.stringify(value));
|
|
121
|
+
}
|
|
122
|
+
async function readJson(request) {
|
|
123
|
+
let content = "";
|
|
124
|
+
for await (const chunk of request) {
|
|
125
|
+
content += chunk.toString();
|
|
126
|
+
if (content.length > 50_000)
|
|
127
|
+
throw new Error("Request is too large.");
|
|
128
|
+
}
|
|
129
|
+
const value = JSON.parse(content);
|
|
130
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
131
|
+
throw new Error("Request must be a JSON object.");
|
|
132
|
+
return value;
|
|
133
|
+
}
|
|
134
|
+
function closeServer(server) {
|
|
135
|
+
return new Promise((resolve, reject) => {
|
|
136
|
+
server.close((error) => (error ? reject(error) : resolve()));
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/server/http.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { chmod, mkdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
import { frozenReviewData } from "../web/frozen-review-data.js";
|
|
6
|
+
import { renderArtifact } from "../web/page.js";
|
|
7
|
+
const artifactPath = resolve(process.cwd(), ".ndrstnd", "frozen-review.html");
|
|
8
|
+
await mkdir(join(process.cwd(), ".ndrstnd"), { recursive: true, mode: 0o700 });
|
|
9
|
+
await writeFile(artifactPath, await renderArtifact(frozenReviewData), { encoding: "utf8", mode: 0o600 });
|
|
10
|
+
await chmod(artifactPath, 0o600);
|
|
11
|
+
process.stdout.write(`ndrstnd frozen fixture: ${artifactPath}\n`);
|
|
12
|
+
if (!process.argv.includes("--no-open"))
|
|
13
|
+
await openBrowser(artifactPath);
|
|
14
|
+
async function openBrowser(path) {
|
|
15
|
+
const command = process.platform === "darwin" ? ["open", [path]] : process.platform === "win32" ? ["cmd", ["/c", "start", "", path]] : ["xdg-open", [path]];
|
|
16
|
+
try {
|
|
17
|
+
await promisify(execFile)(command[0], command[1]);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
process.stdout.write("Could not open a browser automatically. Open the file path above.\n");
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=preview-fixture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview-fixture.js","sourceRoot":"","sources":["../../src/server/preview-fixture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,EAAE,oBAAoB,CAAC,CAAC;AAC9E,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAC/E,MAAM,SAAS,CAAC,YAAY,EAAE,MAAM,cAAc,CAAC,gBAAgB,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACzG,MAAM,KAAK,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;AACjC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,2BAA2B,YAAY,IAAI,CAAC,CAAC;AAElE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC;IAAE,MAAM,WAAW,CAAC,YAAY,CAAC,CAAC;AAEzE,KAAK,UAAU,WAAW,CAAC,IAAY;IACrC,MAAM,OAAO,GAAuB,OAAO,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAChL,IAAI,CAAC;QACH,MAAM,SAAS,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qEAAqE,CAAC,CAAC;IAC9F,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { AnalysisRevision, StoredReviewSession } from "./store.js";
|
|
2
|
+
import type { ReviewPresentationData } from "../web/review-data.js";
|
|
3
|
+
export declare function createReviewPresentationData(session: StoredReviewSession, revision: AnalysisRevision): ReviewPresentationData;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function createReviewPresentationData(session, revision) {
|
|
2
|
+
return {
|
|
3
|
+
sessionId: session.id,
|
|
4
|
+
revisionId: revision.id,
|
|
5
|
+
targetRef: session.targetRef,
|
|
6
|
+
baseRef: session.baseRef,
|
|
7
|
+
mergeBase: session.mergeBase,
|
|
8
|
+
files: session.input.files,
|
|
9
|
+
hunks: session.input.hunks,
|
|
10
|
+
document: revision.document,
|
|
11
|
+
agentName: revision.source === "claude" ? "Claude Code" : "Codex",
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=review-presentation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-presentation.js","sourceRoot":"","sources":["../../src/server/review-presentation.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,4BAA4B,CAAC,OAA4B,EAAE,QAA0B;IACnG,OAAO;QACL,SAAS,EAAE,OAAO,CAAC,EAAE;QACrB,UAAU,EAAE,QAAQ,CAAC,EAAE;QACvB,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK;QAC1B,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK;QAC1B,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,SAAS,EAAE,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO;KAClE,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ReviewAgent, type ReviewAgentId } from "./agent.js";
|
|
2
|
+
/** True when any installed skill differs from the bundled one in any file, meaning that agent follows outdated instructions or assets. */
|
|
3
|
+
export declare function installedSkillIsStale(): Promise<boolean>;
|
|
4
|
+
export interface SkillInstallation {
|
|
5
|
+
agent: ReviewAgent;
|
|
6
|
+
destination: string;
|
|
7
|
+
status: "installed" | "skipped";
|
|
8
|
+
reason?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Installs the bundled skill for the requested agent, or for every agent whose
|
|
12
|
+
* configuration directory exists when none is requested explicitly.
|
|
13
|
+
*/
|
|
14
|
+
export declare function installSkill(force?: boolean, agentId?: ReviewAgentId): Promise<SkillInstallation[]>;
|