@g-abhishek/gitx 0.1.4 → 0.1.5
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 +24 -12
- package/dist/ai/reviewHelpers.d.ts.map +1 -1
- package/dist/ai/reviewHelpers.js +53 -38
- package/dist/ai/reviewHelpers.js.map +1 -1
- package/dist/cli/commands/pr/fixComments.d.ts +5 -2
- package/dist/cli/commands/pr/fixComments.d.ts.map +1 -1
- package/dist/cli/commands/pr/fixComments.js +5 -82
- package/dist/cli/commands/pr/fixComments.js.map +1 -1
- package/dist/cli/commands/pr/index.js +2 -2
- package/dist/cli/commands/pr/index.js.map +1 -1
- package/dist/cli/commands/pr/resolve.d.ts +3 -0
- package/dist/cli/commands/pr/resolve.d.ts.map +1 -0
- package/dist/cli/commands/pr/resolve.js +92 -0
- package/dist/cli/commands/pr/resolve.js.map +1 -0
- package/dist/cli/commands/pr/review.js +1 -1
- package/dist/cli/commands/pr/review.js.map +1 -1
- package/dist/utils/gitOps.d.ts +11 -4
- package/dist/utils/gitOps.d.ts.map +1 -1
- package/dist/utils/gitOps.js +53 -37
- package/dist/utils/gitOps.js.map +1 -1
- package/dist/workflows/pr.d.ts +1 -1
- package/dist/workflows/pr.d.ts.map +1 -1
- package/dist/workflows/pr.js +11 -5
- package/dist/workflows/pr.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ gitx wraps your everyday git operations with AI to generate commit messages, wri
|
|
|
19
19
|
- [gitx implement](#gitx-implement)
|
|
20
20
|
- [gitx pr create](#gitx-pr-create)
|
|
21
21
|
- [gitx pr review](#gitx-pr-review)
|
|
22
|
-
- [gitx pr
|
|
22
|
+
- [gitx pr resolve](#gitx-pr-resolve)
|
|
23
23
|
- [gitx pr merge](#gitx-pr-merge)
|
|
24
24
|
- [gitx pr list](#gitx-pr-list)
|
|
25
25
|
- [gitx pr close](#gitx-pr-close)
|
|
@@ -35,10 +35,10 @@ gitx wraps your everyday git operations with AI to generate commit messages, wri
|
|
|
35
35
|
|---------|--------------|
|
|
36
36
|
| **AI commit messages** | Generates conventional-commit messages from your staged diff |
|
|
37
37
|
| **AI PR descriptions** | Writes PR title + body from your branch commits and diff |
|
|
38
|
-
| **AI code review** | Senior-dev quality review with inline comments posted to GitHub/GitLab/Azure |
|
|
38
|
+
| **AI code review** | Senior-dev quality review with inline comments posted to GitHub/GitLab/Azure (`gitx pr review`) |
|
|
39
|
+
| **AI comment resolve** | Reads unresolved review comments, fixes them in code, commits and pushes (`gitx pr resolve`) |
|
|
39
40
|
| **AI conflict resolution** | Tries to auto-resolve merge/rebase conflicts; prompts when unsure |
|
|
40
41
|
| **AI task implementation** | Takes a plain-English task, plans and applies diffs, commits, pushes, opens PR |
|
|
41
|
-
| **AI comment addressing** | Reads unresolved review comments and generates targeted fixes |
|
|
42
42
|
| **gitx ask** | Ask anything about your repo — get answers grounded in live git context |
|
|
43
43
|
|
|
44
44
|
---
|
|
@@ -178,9 +178,12 @@ Stage → AI-commit → push in one command.
|
|
|
178
178
|
```bash
|
|
179
179
|
gitx push
|
|
180
180
|
gitx push -b feature/my-branch # push to a specific branch name
|
|
181
|
+
gitx push --staged # commit only already-staged files (skip git add -A)
|
|
181
182
|
gitx push --dry-run # preview without pushing
|
|
182
183
|
```
|
|
183
184
|
|
|
185
|
+
Use `--staged` when you've manually staged a subset of changes with `git add` and want the AI commit message to reflect only those changes.
|
|
186
|
+
|
|
184
187
|
---
|
|
185
188
|
|
|
186
189
|
### gitx sync
|
|
@@ -273,25 +276,34 @@ Run a senior-developer quality AI review on an open PR and post the results as f
|
|
|
273
276
|
|
|
274
277
|
```bash
|
|
275
278
|
gitx pr review <number>
|
|
276
|
-
gitx pr review 42 --no-comment # show review locally, don't post to
|
|
277
|
-
gitx pr review 42 --
|
|
278
|
-
gitx pr review 42 --no-push # apply fixes locally without pushing
|
|
279
|
+
gitx pr review 42 --no-comment # show review locally, don't post to PR
|
|
280
|
+
gitx pr review 42 --inline # force inline comments (skip plain-comment fallback)
|
|
279
281
|
```
|
|
280
282
|
|
|
281
|
-
The review covers: correctness, security, robustness, performance, breaking changes, best practices, test coverage, and documentation.
|
|
283
|
+
The review covers: correctness, security, robustness, performance, breaking changes, best practices, test coverage, and documentation. After reviewing, run `gitx pr resolve <number>` to AI-fix the comments in your code.
|
|
282
284
|
|
|
283
285
|
---
|
|
284
286
|
|
|
285
|
-
### gitx pr
|
|
287
|
+
### gitx pr resolve
|
|
286
288
|
|
|
287
|
-
Read all unresolved review comments on a PR, AI-generate targeted fixes for each, and apply them.
|
|
289
|
+
Read all unresolved review comments on a PR, AI-generate targeted code fixes for each, and apply them.
|
|
288
290
|
|
|
289
291
|
```bash
|
|
290
|
-
gitx pr
|
|
291
|
-
gitx pr
|
|
292
|
-
gitx pr
|
|
292
|
+
gitx pr resolve <number> # apply fixes, commit, and push
|
|
293
|
+
gitx pr resolve 42 --no-commit # apply fixes to working tree only — review before committing
|
|
294
|
+
gitx pr resolve 42 --no-push # apply and commit locally, skip push
|
|
295
|
+
gitx pr resolve 42 --dry-run # preview what would be fixed, nothing applied
|
|
293
296
|
```
|
|
294
297
|
|
|
298
|
+
**Typical workflow:**
|
|
299
|
+
```bash
|
|
300
|
+
gitx pr review 42 # AI reviews and posts inline comments to the PR
|
|
301
|
+
# ... read the comments, understand the feedback ...
|
|
302
|
+
gitx pr resolve 42 # AI fixes the comments in your code and pushes
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Use `--no-commit` when you want to inspect the AI-applied diffs with `git diff` before deciding to commit.
|
|
306
|
+
|
|
295
307
|
---
|
|
296
308
|
|
|
297
309
|
### gitx pr merge
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reviewHelpers.d.ts","sourceRoot":"","sources":["../../src/ai/reviewHelpers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,wBAAwB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAIvG,wBAAgB,uBAAuB,IAAI,MAAM,CAwChD;AAwJD,wBAAgB,uBAAuB,CACrC,OAAO,EAAE;IACP,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,gBAAgB,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACzF,GACA,MAAM,
|
|
1
|
+
{"version":3,"file":"reviewHelpers.d.ts","sourceRoot":"","sources":["../../src/ai/reviewHelpers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,wBAAwB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAIvG,wBAAgB,uBAAuB,IAAI,MAAM,CAwChD;AAwJD,wBAAgB,uBAAuB,CACrC,OAAO,EAAE;IACP,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,gBAAgB,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACzF,GACA,MAAM,CA6GR;AAID,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,wBAAwB,CAkCxE;AAID;;;GAGG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAsBvC;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,MAAM,CAgCT;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,aAAa,CAgD5F;AAqCD;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAgCvC;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,MAAM,CAoE1E;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAa3D"}
|
package/dist/ai/reviewHelpers.js
CHANGED
|
@@ -181,63 +181,77 @@ export function buildSeniorReviewPrompt(context) {
|
|
|
181
181
|
}
|
|
182
182
|
// ── Parse which lines actually changed per file ───────────────────────────
|
|
183
183
|
const hunkRanges = parseHunkRanges(context.diff);
|
|
184
|
-
// ──
|
|
185
|
-
//
|
|
186
|
-
//
|
|
187
|
-
//
|
|
188
|
-
//
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
184
|
+
// ── Budget constants ──────────────────────────────────────────────────────
|
|
185
|
+
// Claude has a 200k-token context window. We stay well within that while
|
|
186
|
+
// sending enough code for a thorough review.
|
|
187
|
+
//
|
|
188
|
+
// Strategy:
|
|
189
|
+
// • Files ≤ FULL_FILE_THRESHOLD lines → send the ENTIRE file (best context)
|
|
190
|
+
// • Larger files → smart section extraction around hunks
|
|
191
|
+
// with generous context above (function boundary) and below (CONTEXT_LINES_BELOW)
|
|
192
|
+
// • Hard per-file cap for huge files → PER_FILE_LINE_CAP lines of excerpts
|
|
193
|
+
const FULL_FILE_THRESHOLD = 400; // files ≤ this many lines are sent whole
|
|
194
|
+
const CONTEXT_LINES_BELOW = 60; // lines below each hunk in extraction mode
|
|
195
|
+
const PER_FILE_LINE_CAP = 800; // hard cap on excerpt lines for very large files
|
|
196
|
+
const DIFF_BUDGET = 30_000; // unified diff character budget (was 5k — way too small)
|
|
197
|
+
const CTX_FILE_MAX = 4_000; // max chars per supporting context file (was 1.5k)
|
|
192
198
|
const changedEntries = Object.entries(context.changedFiles);
|
|
193
199
|
if (changedEntries.length > 0) {
|
|
194
|
-
parts.push(`\n### Changed
|
|
200
|
+
parts.push(`\n### Changed files (line numbers are exact positions in the new file)`);
|
|
195
201
|
for (const [path, content] of changedEntries) {
|
|
202
|
+
const fileLines = content.split("\n");
|
|
196
203
|
const hunks = hunkRanges.get(path) ?? [];
|
|
197
204
|
let excerpt;
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
excerpt =
|
|
202
|
-
.slice(0, 60)
|
|
205
|
+
let deliveryNote;
|
|
206
|
+
if (fileLines.length <= FULL_FILE_THRESHOLD) {
|
|
207
|
+
// Small file — send the whole thing. The AI gets complete context with no gaps.
|
|
208
|
+
excerpt = fileLines
|
|
203
209
|
.map((l, i) => `${String(i + 1).padStart(5, " ")} | ${l}`)
|
|
204
210
|
.join("\n");
|
|
205
|
-
|
|
206
|
-
excerpt += "\n … (file continues — only first 60 lines shown as fallback)";
|
|
211
|
+
deliveryNote = `full file, ${fileLines.length} lines`;
|
|
207
212
|
}
|
|
208
|
-
else {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
`\n … (${excerptLines.length - PER_FILE_LINE_CAP} more lines not shown` +
|
|
218
|
-
` — this function is unusually large; review the diff for remaining changes)`;
|
|
213
|
+
else if (hunks.length === 0) {
|
|
214
|
+
// No hunk data (binary / rename-only) — show first 100 lines as fallback
|
|
215
|
+
excerpt = fileLines
|
|
216
|
+
.slice(0, 100)
|
|
217
|
+
.map((l, i) => `${String(i + 1).padStart(5, " ")} | ${l}`)
|
|
218
|
+
.join("\n");
|
|
219
|
+
if (fileLines.length > 100)
|
|
220
|
+
excerpt += `\n … (file continues — only first 100 lines shown; no hunk data available)`;
|
|
221
|
+
deliveryNote = `first 100 of ${fileLines.length} lines (no hunk data)`;
|
|
219
222
|
}
|
|
220
223
|
else {
|
|
221
|
-
|
|
224
|
+
// Large file — extract sections around changed hunks with generous context
|
|
225
|
+
excerpt = extractChangedSections(content, hunks, CONTEXT_LINES_BELOW);
|
|
226
|
+
// Cap at PER_FILE_LINE_CAP complete lines — never mid-line
|
|
227
|
+
const excerptLines = excerpt.split("\n");
|
|
228
|
+
if (excerptLines.length > PER_FILE_LINE_CAP) {
|
|
229
|
+
excerpt =
|
|
230
|
+
excerptLines.slice(0, PER_FILE_LINE_CAP).join("\n") +
|
|
231
|
+
`\n … (${excerptLines.length - PER_FILE_LINE_CAP} more excerpt lines omitted` +
|
|
232
|
+
` — file has ${fileLines.length} total lines; see the diff below for full change)`;
|
|
233
|
+
}
|
|
234
|
+
deliveryNote = `${hunks.length} hunk${hunks.length > 1 ? "s" : ""}, ${fileLines.length} total lines`;
|
|
222
235
|
}
|
|
223
|
-
|
|
224
|
-
? ` (${hunks.length} change hunk${hunks.length > 1 ? "s" : ""})`
|
|
225
|
-
: "";
|
|
226
|
-
parts.push(`\n#### ${path}${hunkDesc}\n\`\`\`\n${finalExcerpt}\n\`\`\``);
|
|
236
|
+
parts.push(`\n#### ${path} (${deliveryNote})\n\`\`\`\n${excerpt}\n\`\`\``);
|
|
227
237
|
}
|
|
228
238
|
}
|
|
229
|
-
// ──
|
|
239
|
+
// ── Unified diff (the raw patch — gives the AI the exact before/after for every line)
|
|
230
240
|
if (context.diff.trim()) {
|
|
231
241
|
const diffSlice = context.diff.slice(0, DIFF_BUDGET);
|
|
232
242
|
const diffTrunc = diffSlice.length < context.diff.length;
|
|
233
|
-
parts.push(`\n### Unified diff${diffTrunc ?
|
|
243
|
+
parts.push(`\n### Unified diff${diffTrunc ? ` (truncated to ${DIFF_BUDGET} chars — see file sections above for full content)` : ""}\n\`\`\`diff\n${diffSlice}\n\`\`\``);
|
|
234
244
|
}
|
|
235
245
|
// ── Supporting context files (unchanged files the changes depend on) ──────
|
|
236
246
|
const ctxEntries = Object.entries(context.contextFiles);
|
|
237
247
|
if (ctxEntries.length > 0) {
|
|
238
248
|
parts.push(`\n### Supporting context files (unchanged — imported by changed files)`);
|
|
239
249
|
for (const [path, content] of ctxEntries) {
|
|
240
|
-
|
|
250
|
+
const ctxLines = content.split("\n");
|
|
251
|
+
const ctxExcerpt = content.length <= CTX_FILE_MAX
|
|
252
|
+
? content
|
|
253
|
+
: content.slice(0, CTX_FILE_MAX) + `\n… (truncated — ${ctxLines.length} total lines)`;
|
|
254
|
+
parts.push(`\n#### ${path}\n\`\`\`\n${ctxExcerpt}\n\`\`\``);
|
|
241
255
|
}
|
|
242
256
|
}
|
|
243
257
|
// ── Existing PR comments ──────────────────────────────────────────────────
|
|
@@ -415,13 +429,14 @@ const GITX_COMMAND_REFERENCE = `
|
|
|
415
429
|
| gitx config show | Display current configuration (secrets redacted) |
|
|
416
430
|
| gitx config set <key> [value] | Set a single config value (provider, token, model, etc.) |
|
|
417
431
|
| gitx commit [-m msg] [--push] [--dry-run] | AI-generate commit message → commit (optionally push) |
|
|
418
|
-
| gitx push [-b branch] [--dry-run] | Stage → AI-commit → push in one step |
|
|
432
|
+
| gitx push [-b branch] [--staged] [--dry-run] | Stage → AI-commit → push in one step; --staged uses already-staged files only |
|
|
419
433
|
| gitx sync [--base branch] [--strategy merge|rebase] [--continue] [--abort] | Sync current branch with base; AI resolves conflicts |
|
|
434
|
+
| gitx port <target…> [--base branch] [--no-pr] [--draft] [--continue] [--abort] | Cherry-pick commits onto other branches with incremental detection |
|
|
420
435
|
| gitx implement "<task>" [--mode plan|guided|semi-auto|auto] [--dry-run] | AI-plan and implement a task end-to-end |
|
|
421
436
|
| gitx pr list [--state open|closed|all] | List pull requests |
|
|
422
437
|
| gitx pr create [--title T] [--body B] [--draft] [--dry-run] | AI-generate PR title/body → open PR |
|
|
423
|
-
| gitx pr review <number> [--no-comment] [--
|
|
424
|
-
| gitx pr
|
|
438
|
+
| gitx pr review <number> [--no-comment] [--inline] | Senior-dev AI review — posts inline comments to the PR |
|
|
439
|
+
| gitx pr resolve <number> [--no-commit] [--no-push] [--dry-run] | AI-fix review comments in code; --no-commit applies fixes without committing |
|
|
425
440
|
| gitx pr merge <number> [--strategy squash|merge|rebase] [--delete-branch] | Merge a PR |
|
|
426
441
|
| gitx pr close <number> [-f] | Close a PR |
|
|
427
442
|
| gitx ask "<question>" [--pr] | Ask a question about the repo using AI + live git context |
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reviewHelpers.js","sourceRoot":"","sources":["../../src/ai/reviewHelpers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shared helpers for the senior-developer PR review.\n *\n * buildSeniorReviewSystem() — the AI system prompt\n * buildSeniorReviewPrompt() — formats the user-facing context block\n * parseSeniorReview() — safely parses the AI JSON response\n */\n\nimport type { AiAskContext, AiAskResponse, AiDetailedReviewResponse, AiFixResponse } from \"./types.js\";\n\n// ─── System prompt ────────────────────────────────────────────────────────────\n\nexport function buildSeniorReviewSystem(): string {\n return `You are a principal software engineer and tech lead doing a thorough pull request review.\nYou have access to the CHANGED SECTIONS of each file (extracted around the exact lines that changed),\nplus supporting context files and the full unified diff.\n\nYour review MUST cover every one of these dimensions:\n 1. Correctness — logic errors, off-by-one, wrong conditions, silent failures\n 2. Security — injection, auth bypass, secret leakage, unvalidated input\n 3. Robustness — missing error handling, null/undefined guard, edge cases\n 4. Performance — unnecessary loops, N+1 queries, missing caching\n 5. Breaking changes — does this break existing API contracts, interfaces, or callers?\n 6. Best practices — naming, DRY, SOLID, idiomatic language usage\n 7. Test coverage — are critical paths tested? are tests meaningful?\n 8. Documentation — are public APIs documented? are complex sections explained?\n\nFor EVERY issue that maps to a specific line, add an inline comment.\nLine numbers shown in the excerpts are the REAL line numbers in the new file — use them exactly.\nOnly reference lines that appear in the excerpts you were given.\n\nVerdict rules:\n - \"approve\" → no critical or warning issues\n - \"request_changes\" → one or more critical/warning issues found\n - \"comment\" → only suggestions / minor observations\n\nRespond with ONLY valid JSON (no markdown fences, no prose outside JSON):\n{\n \"summary\": \"<3-5 sentence executive summary>\",\n \"verdict\": \"approve|request_changes|comment\",\n \"issues\": [\n { \"severity\": \"critical|warning|suggestion\", \"description\": \"<issue>\", \"file\": \"<path or null>\", \"line\": <number or null> }\n ],\n \"inlineComments\": [\n { \"path\": \"<relative file path>\", \"line\": <line number>, \"body\": \"<markdown comment>\", \"severity\": \"critical|warning|suggestion\", \"suggestion\": \"<replacement code or null>\" }\n ],\n \"positives\": [\"<good thing>\"],\n \"testingNotes\": \"<how to manually test>\",\n \"checklist\": [\n { \"area\": \"<Correctness|Security|Robustness|Performance|Breaking changes|Best practices|Tests|Documentation>\", \"status\": \"pass|warn|fail\", \"note\": \"<one sentence>\" }\n ]\n}`;\n}\n\n// ─── Diff parsing ─────────────────────────────────────────────────────────────\n\n/**\n * Parse a unified diff and return the NEW-file line ranges that were touched,\n * grouped by file path.\n *\n * A diff hunk header looks like: @@ -10,7 +12,8 @@\n * +12,8 → new file starts at line 12, hunk spans 8 lines\n */\nfunction parseHunkRanges(diff: string): Map<string, Array<{ start: number; end: number }>> {\n const result = new Map<string, Array<{ start: number; end: number }>>();\n let currentFile = \"\";\n\n for (const line of diff.split(\"\\n\")) {\n const fileMatch = line.match(/^\\+\\+\\+ b\\/(.+)/);\n if (fileMatch?.[1] && fileMatch[1] !== \"/dev/null\") {\n currentFile = fileMatch[1].trim();\n if (!result.has(currentFile)) result.set(currentFile, []);\n continue;\n }\n\n // @@ -old,count +new,count @@\n const hunkMatch = line.match(/^@@ -\\d+(?:,\\d+)? \\+(\\d+)(?:,(\\d+))? @@/);\n if (hunkMatch && currentFile) {\n const start = parseInt(hunkMatch[1]!, 10);\n const count = parseInt(hunkMatch[2] ?? \"1\", 10);\n result.get(currentFile)!.push({ start, end: start + Math.max(count - 1, 0) });\n }\n }\n\n return result;\n}\n\n/**\n * Scan BACKWARD from `fromLine` (1-based) to find where the containing\n * function / class / method starts.\n *\n * Recognises common declaration patterns for TypeScript, JavaScript, Python,\n * Go, Rust, Java, and C#. Falls back to `fromLine - fallback` if nothing\n * is found within `maxScan` lines.\n */\nfunction findContainerStart(\n lines: string[],\n fromLine: number,\n maxScan = 80,\n fallback = 30\n): number {\n // fromLine is 1-based; lines[] is 0-based\n const startIdx = Math.min(lines.length - 1, Math.max(0, fromLine - 2));\n\n for (let i = startIdx; i >= Math.max(0, startIdx - maxScan); i--) {\n const line = lines[i] ?? \"\";\n if (\n // TS/JS: export [default] [abstract] [async] function foo(\n /^\\s*(export\\s+)?(default\\s+)?(abstract\\s+)?(async\\s+)?function[\\s*]/.test(line) ||\n // TS/JS: export [abstract] class Foo\n /^\\s*(export\\s+)?(abstract\\s+)?class\\s+\\w/.test(line) ||\n // TS/JS class methods: [public|private|protected|static|override|async] methodName(\n /^\\s*(public|private|protected|static|override|async)(\\s+(public|private|protected|static|override|async))*\\s+\\w+\\s*[(<]/.test(line) ||\n // TS/JS arrow function assigned to const/let/var\n /^\\s*(export\\s+)?(const|let|var)\\s+\\w+\\s*=\\s*(async\\s+)?\\(/.test(line) ||\n // TS/JS shorthand method (no keyword): methodName(args) {\n /^\\s*\\w+\\s*\\([^)]*\\)\\s*(?::\\s*\\S+\\s*)?\\{/.test(line) ||\n // Python: def foo(\n /^\\s*def\\s+\\w+\\s*\\(/.test(line) ||\n // Go: func (recv) Foo(\n /^\\s*func\\s+/.test(line) ||\n // Rust: fn foo(\n /^\\s*(pub\\s+)?(async\\s+)?fn\\s+\\w+/.test(line) ||\n // Java/C#: returnType methodName(\n /^\\s*(public|private|protected|internal|static|virtual|override)\\s+\\S+\\s+\\w+\\s*\\(/.test(line)\n ) {\n return i + 1; // convert back to 1-based\n }\n }\n\n // Nothing found — fall back to a fixed number of lines above\n return Math.max(1, fromLine - fallback);\n}\n\n/**\n * Extract the sections of a file that were changed, always starting each\n * window at the nearest enclosing function/class boundary (or 30 lines up,\n * whichever is closer).\n *\n * Overlapping windows are merged so the same lines aren't shown twice.\n * The first HEADER_LINES of the file are always prepended (imports, class\n * declarations) so the AI understands module structure.\n *\n * Returns a line-numbered string ready to paste into the prompt.\n */\nfunction extractChangedSections(\n fileContent: string,\n hunks: Array<{ start: number; end: number }>,\n contextLinesBelow = 20 // lines below the hunk (above uses function-boundary scan)\n): string {\n const lines = fileContent.split(\"\\n\");\n const total = lines.length;\n\n // Always include file header (imports / module preamble) for structural context\n const HEADER_LINES = 20;\n\n // Build windows: above = scan to function start, below = fixed context\n const windows: Array<{ start: number; end: number }> = [];\n for (const hunk of hunks) {\n const windowStart = findContainerStart(lines, hunk.start);\n windows.push({\n start: windowStart,\n end: Math.min(total, hunk.end + contextLinesBelow),\n });\n }\n // Sort then merge\n windows.sort((a, b) => a.start - b.start);\n const merged: Array<{ start: number; end: number }> = [];\n for (const w of windows) {\n if (merged.length > 0 && w.start <= merged[merged.length - 1]!.end + 1) {\n merged[merged.length - 1]!.end = Math.max(merged[merged.length - 1]!.end, w.end);\n } else {\n merged.push({ ...w });\n }\n }\n\n const formatRange = (start: number, end: number): string =>\n lines\n .slice(start - 1, end)\n .map((l, i) => `${String(start + i).padStart(5, \" \")} | ${l}`)\n .join(\"\\n\");\n\n const sections: string[] = [];\n\n // File header (always included, unless the first window already covers it)\n const firstWindowStart = merged[0]?.start ?? 1;\n if (firstWindowStart > HEADER_LINES + 1) {\n sections.push(formatRange(1, Math.min(HEADER_LINES, total)));\n sections.push(\" … (lines omitted) …\");\n }\n\n for (let i = 0; i < merged.length; i++) {\n const { start, end } = merged[i]!;\n if (i > 0 && start > (merged[i - 1]!.end + 1)) {\n sections.push(\" … (lines omitted) …\");\n }\n sections.push(formatRange(start, end));\n }\n\n return sections.join(\"\\n\");\n}\n\n// ─── User prompt builder ──────────────────────────────────────────────────────\n\nexport function buildSeniorReviewPrompt(\n context: {\n prTitle: string;\n prBody: string;\n author: string;\n headBranch: string;\n baseBranch: string;\n diff: string;\n changedFiles: Record<string, string>;\n contextFiles: Record<string, string>;\n repoFileList: string[];\n existingComments: Array<{ author: string; body: string; path?: string; line?: number }>;\n }\n): string {\n const parts: string[] = [];\n\n parts.push(`## PR: ${context.prTitle}`);\n parts.push(`Author: ${context.author} | ${context.headBranch} → ${context.baseBranch}`);\n if (context.prBody.trim()) {\n parts.push(`\\n### Description\\n${context.prBody.slice(0, 1000)}`);\n }\n\n // ── Parse which lines actually changed per file ───────────────────────────\n const hunkRanges = parseHunkRanges(context.diff);\n\n // ── Changed file sections — only the relevant areas + function context ──────\n // Hard per-file cap: if a single function is genuinely 500+ lines we still\n // need to stop somewhere, but we ALWAYS stop on a complete line boundary so\n // the AI never sees half a statement.\n // The per-file cap is generous (300 lines) to handle large but realistic functions.\n const PER_FILE_LINE_CAP = 300;\n const DIFF_BUDGET = 5_000;\n const CTX_FILE_MAX = 1_500;\n\n const changedEntries = Object.entries(context.changedFiles);\n if (changedEntries.length > 0) {\n parts.push(`\\n### Changed sections (line numbers are exact positions in the new file)`);\n\n for (const [path, content] of changedEntries) {\n const hunks = hunkRanges.get(path) ?? [];\n\n let excerpt: string;\n if (hunks.length === 0) {\n // No hunk data (binary / rename-only) — show first 60 lines as fallback\n const lines = content.split(\"\\n\");\n excerpt = lines\n .slice(0, 60)\n .map((l, i) => `${String(i + 1).padStart(5, \" \")} | ${l}`)\n .join(\"\\n\");\n if (lines.length > 60) excerpt += \"\\n … (file continues — only first 60 lines shown as fallback)\";\n } else {\n excerpt = extractChangedSections(content, hunks);\n }\n\n // Cap at PER_FILE_LINE_CAP complete lines — never mid-line\n const excerptLines = excerpt.split(\"\\n\");\n let finalExcerpt: string;\n if (excerptLines.length > PER_FILE_LINE_CAP) {\n finalExcerpt =\n excerptLines.slice(0, PER_FILE_LINE_CAP).join(\"\\n\") +\n `\\n … (${excerptLines.length - PER_FILE_LINE_CAP} more lines not shown` +\n ` — this function is unusually large; review the diff for remaining changes)`;\n } else {\n finalExcerpt = excerpt;\n }\n\n const hunkDesc = hunks.length > 0\n ? ` (${hunks.length} change hunk${hunks.length > 1 ? \"s\" : \"\"})`\n : \"\";\n parts.push(`\\n#### ${path}${hunkDesc}\\n\\`\\`\\`\\n${finalExcerpt}\\n\\`\\`\\``);\n }\n }\n\n // ── Diff (compact view of what changed, used for overall change understanding)\n if (context.diff.trim()) {\n const diffSlice = context.diff.slice(0, DIFF_BUDGET);\n const diffTrunc = diffSlice.length < context.diff.length;\n parts.push(\n `\\n### Unified diff${diffTrunc ? \" (truncated)\" : \"\"}\\n\\`\\`\\`diff\\n${diffSlice}\\n\\`\\`\\``\n );\n }\n\n // ── Supporting context files (unchanged files the changes depend on) ──────\n const ctxEntries = Object.entries(context.contextFiles);\n if (ctxEntries.length > 0) {\n parts.push(`\\n### Supporting context files (unchanged — imported by changed files)`);\n for (const [path, content] of ctxEntries) {\n parts.push(`\\n#### ${path}\\n\\`\\`\\`\\n${content.slice(0, CTX_FILE_MAX)}\\n\\`\\`\\``);\n }\n }\n\n // ── Existing PR comments ──────────────────────────────────────────────────\n if (context.existingComments.length > 0) {\n parts.push(`\\n### Existing review comments`);\n for (const c of context.existingComments.slice(0, 6)) {\n const loc = c.path ? ` (${c.path}${c.line ? `:${c.line}` : \"\"})` : \"\";\n parts.push(`- **${c.author}**${loc}: ${c.body.slice(0, 150)}`);\n }\n }\n\n // ── Repo file tree (structural awareness) ────────────────────────────────\n if (context.repoFileList.length > 0) {\n parts.push(`\\n### Repository file tree (top 60 files)\\n${context.repoFileList.slice(0, 60).join(\"\\n\")}`);\n }\n\n return parts.join(\"\\n\");\n}\n\n// ─── Response parser ──────────────────────────────────────────────────────────\n\nexport function parseSeniorReview(text: string): AiDetailedReviewResponse {\n let parsed: Partial<AiDetailedReviewResponse> = {};\n try {\n const fenced = text.match(/```(?:json)?\\s*([\\s\\S]*?)```/);\n const raw = fenced?.[1]?.trim() ?? text.trim();\n const start = raw.search(/\\{/);\n const end = raw.lastIndexOf(\"}\");\n const jsonStr = start !== -1 && end > start ? raw.slice(start, end + 1) : raw;\n parsed = JSON.parse(jsonStr) as Partial<AiDetailedReviewResponse>;\n } catch {\n return {\n summary: \"AI review could not be parsed. Please inspect the diff manually.\",\n verdict: \"comment\",\n issues: [],\n inlineComments: [],\n positives: [],\n testingNotes: \"Test the changed functionality manually.\",\n checklist: [],\n };\n }\n\n return {\n summary: parsed.summary ?? \"Review generated.\",\n verdict: ([\"approve\", \"request_changes\", \"comment\"].includes(parsed.verdict ?? \"\"))\n ? (parsed.verdict as AiDetailedReviewResponse[\"verdict\"])\n : \"comment\",\n issues: Array.isArray(parsed.issues) ? parsed.issues : [],\n inlineComments: Array.isArray(parsed.inlineComments)\n ? parsed.inlineComments.filter((c) => c.path && c.line > 0)\n : [],\n positives: Array.isArray(parsed.positives) ? parsed.positives : [],\n testingNotes: parsed.testingNotes ?? \"\",\n checklist: Array.isArray(parsed.checklist) ? parsed.checklist : [],\n };\n}\n\n// ─── Fix generation helpers ───────────────────────────────────────────────────\n\n/**\n * System prompt for the AI fix generator.\n * Instructs the model to produce a minimal, targeted line-range replacement.\n */\nexport function buildFixSystem(): string {\n return `You are a senior developer addressing a pull request review comment.\nYour job is to generate the MINIMAL code change that addresses the reviewer's concern.\n\nRules:\n- Change as few lines as possible — do not refactor surrounding code\n- Preserve the existing indentation style exactly\n- If the comment is a question or discussion (no code change needed), set isDiscussion: true\n- If you are unsure of the correct fix, set confidence: \"low\"\n- startLine and endLine are 1-based, inclusive line numbers in the CURRENT file\n\nRespond with ONLY valid JSON (no markdown fences, no prose outside JSON):\n{\n \"file\": \"<relative file path>\",\n \"startLine\": <1-based line where replacement starts>,\n \"endLine\": <1-based line where replacement ends, inclusive>,\n \"replacement\": \"<new code lines, newline-separated, preserving indentation>\",\n \"explanation\": \"<one sentence: what you changed and why>\",\n \"confidence\": \"high|low\",\n \"resolves\": true|false,\n \"isDiscussion\": true|false\n}`;\n}\n\n/**\n * Build the user prompt for a single fix request.\n * Includes the comment, file content with line numbers, and the relevant diff.\n */\nexport function buildFixPrompt(ctx: {\n comment: string;\n commentAuthor: string;\n filePath: string;\n line: number;\n fileContent: string;\n fileDiff: string;\n}): string {\n const lines = ctx.fileContent.split(\"\\n\");\n\n // Show a window of ±30 lines around the commented line (with real line numbers)\n const windowStart = Math.max(1, ctx.line - 30);\n const windowEnd = Math.min(lines.length, ctx.line + 30);\n const excerpt = lines\n .slice(windowStart - 1, windowEnd)\n .map((l, i) => `${String(windowStart + i).padStart(4, \" \")} | ${l}`)\n .join(\"\\n\");\n\n const diffSection = ctx.fileDiff.length > 3000\n ? ctx.fileDiff.slice(0, 3000) + \"\\n... (diff truncated)\"\n : ctx.fileDiff;\n\n return `## Review Comment\nAuthor: ${ctx.commentAuthor}\nFile: ${ctx.filePath} · Line: ${ctx.line}\n\n> ${ctx.comment.replace(/\\n/g, \"\\n> \")}\n\n## File Context (lines ${windowStart}–${windowEnd} of ${lines.length})\n\\`\\`\\`\n${excerpt}\n\\`\\`\\`\n\n## Diff for this file\n\\`\\`\\`diff\n${diffSection}\n\\`\\`\\`\n\nGenerate the fix JSON now.`;\n}\n\n/**\n * Safely parse the AI response for a fix request.\n * Returns a safe fallback (isDiscussion + low confidence) on parse failure.\n */\nexport function parseFixResponse(text: string, filePath: string, line: number): AiFixResponse {\n try {\n const jsonMatch = text.match(/\\{[\\s\\S]*\\}/);\n if (!jsonMatch) throw new Error(\"no JSON found\");\n const parsed = JSON.parse(jsonMatch[0]) as Partial<AiFixResponse>;\n\n if (parsed.isDiscussion) {\n return {\n file: parsed.file ?? filePath,\n startLine: line,\n endLine: line,\n replacement: \"\",\n explanation: parsed.explanation ?? \"This comment is a discussion — no code change needed.\",\n confidence: \"low\",\n resolves: parsed.resolves ?? false,\n isDiscussion: true,\n };\n }\n\n // Validate required fields; fall back to low confidence if anything is off\n const hasReplacement = typeof parsed.replacement === \"string\";\n const hasLines = typeof parsed.startLine === \"number\" && typeof parsed.endLine === \"number\";\n if (!hasReplacement || !hasLines) {\n throw new Error(\"missing required fields\");\n }\n\n return {\n file: parsed.file ?? filePath,\n startLine: parsed.startLine!,\n endLine: parsed.endLine!,\n replacement: parsed.replacement!,\n explanation: parsed.explanation ?? \"AI-generated fix.\",\n confidence: parsed.confidence === \"high\" ? \"high\" : \"low\",\n resolves: parsed.resolves ?? false,\n isDiscussion: false,\n };\n } catch {\n return {\n file: filePath,\n startLine: line,\n endLine: line,\n replacement: \"\",\n explanation: \"AI could not generate a fix for this comment.\",\n confidence: \"low\",\n resolves: false,\n isDiscussion: true, // treat parse failure as discussion → no code change\n };\n }\n}\n\n// ─── Ask command helpers ───────────────────────────────────────────────────────\n\n/** Command reference embedded into the ask system prompt. */\nconst GITX_COMMAND_REFERENCE = `\n## gitx Command Reference\n\n| Command | Description |\n|---------|-------------|\n| gitx init / gitx config setup | Interactive setup wizard — configure git & AI providers |\n| gitx config show | Display current configuration (secrets redacted) |\n| gitx config set <key> [value] | Set a single config value (provider, token, model, etc.) |\n| gitx commit [-m msg] [--push] [--dry-run] | AI-generate commit message → commit (optionally push) |\n| gitx push [-b branch] [--dry-run] | Stage → AI-commit → push in one step |\n| gitx sync [--base branch] [--strategy merge|rebase] [--continue] [--abort] | Sync current branch with base; AI resolves conflicts |\n| gitx implement \"<task>\" [--mode plan|guided|semi-auto|auto] [--dry-run] | AI-plan and implement a task end-to-end |\n| gitx pr list [--state open|closed|all] | List pull requests |\n| gitx pr create [--title T] [--body B] [--draft] [--dry-run] | AI-generate PR title/body → open PR |\n| gitx pr review <number> [--no-comment] [--address] [--no-push] | Senior-dev AI review with inline comments |\n| gitx pr fix-comments <number> [--dry-run] [--no-push] | AI-fix review comments and push |\n| gitx pr merge <number> [--strategy squash|merge|rebase] [--delete-branch] | Merge a PR |\n| gitx pr close <number> [-f] | Close a PR |\n| gitx ask \"<question>\" [--pr] | Ask a question about the repo using AI + live git context |\n\n## Supported Providers\n- Git hosts: GitHub, GitLab, Azure DevOps\n- AI backends: Anthropic Claude (API), OpenAI, Local Claude CLI\n\n## Environment Variables\n- ANTHROPIC_API_KEY — Anthropic API key (auto-selects Claude as AI provider)\n- OPENAI_API_KEY — OpenAI API key\n- GITX_AI_MODEL — Override the AI model name\n- GITX_DEBUG=1 — Print full stack traces on errors\n`.trim();\n\n/**\n * Builds the system prompt for `gitx ask`.\n * Includes the full command reference and setup guidance so the AI can answer\n * both \"how do I…\" questions and \"is X configured?\" diagnostics.\n */\nexport function buildAskSystem(): string {\n return `You are gitx-assistant, a smart support assistant embedded in the gitx CLI.\nYou help users with three types of questions:\n\n1. SETUP / DIAGNOSTIC — \"is my AI provider set up?\", \"why isn't gitx working?\", \"what provider am I using?\"\n → Use the GITX SETUP STATUS section in the context. Give a clear yes/no diagnostic and actionable fix steps.\n\n2. REPO STATE — \"what did I last commit?\", \"do I have unstaged changes?\", \"show me open PRs\"\n → Use the LIVE REPO CONTEXT section in the context.\n\n3. HOW-TO — \"how do I sync with main?\", \"how do I undo a commit?\", \"how do I create a PR?\"\n → Use the GITX COMMAND REFERENCE below. Show the exact command.\n\n${GITX_COMMAND_REFERENCE}\n\n## Setup Fix Guide (use when AI or provider is not configured)\n- No AI provider → Run: gitx config setup (or set ANTHROPIC_API_KEY / OPENAI_API_KEY env var)\n- AI provider configured in config but not working → Run: gitx config show to inspect; re-run gitx config set <provider>\n- No git provider token → Run: gitx config set github (or gitlab / azure)\n- Not inside a git repo → cd into your project folder first\n\nRules:\n- Answer concisely and accurately. Get to the point immediately.\n- For setup/diagnostic questions: state clearly whether it IS or IS NOT configured, then explain WHY and how to fix it.\n- Never fabricate details — only use what is in the provided context.\n- Format your answer in plain text. Use a code block only for commands or file paths.\n- When suggesting commands, put them in suggestedCommands so they render highlighted.\n\nRespond with ONLY valid JSON (no markdown fences):\n{\"answer\":\"<answer text>\",\"suggestedCommands\":[\"<cmd1>\",\"<cmd2>\"]}\n\nThe suggestedCommands array may be empty [] if no command applies.`;\n}\n\n/**\n * Builds the user-turn prompt for `gitx ask`, injecting live repo context\n * and the full gitx setup status so the AI can answer diagnostic questions accurately.\n */\nexport function buildAskPrompt(question: string, ctx: AiAskContext): string {\n const lines: string[] = [];\n\n // ── Section 1: gitx setup status ──────────────────────────────────────────\n lines.push(`## gitx Setup Status`);\n\n // AI provider\n const ai = ctx.aiSetup;\n lines.push(`- AI provider: ${ai.provider}`);\n lines.push(`- AI configured: ${ai.isConfigured ? \"YES\" : \"NO — not configured\"}`);\n if (ai.model) lines.push(`- AI model: ${ai.model}`);\n lines.push(`- AI key source: ${ai.keySource}`);\n\n // Git providers\n if (ctx.gitProviders.length > 0) {\n lines.push(`- Git providers configured:`);\n ctx.gitProviders.forEach((p) => {\n const tokenStatus = p.hasToken ? \"token ✓\" : \"token MISSING\";\n lines.push(` ${p.name}: ${tokenStatus}`);\n });\n } else {\n lines.push(`- Git providers configured: none`);\n }\n\n if (ctx.defaultBranch) {\n lines.push(`- Default base branch: ${ctx.defaultBranch}`);\n }\n\n // ── Section 2: live repo context ──────────────────────────────────────────\n lines.push(``);\n lines.push(`## Live Repo Context`);\n lines.push(`- Inside git repo: ${ctx.isInsideGitRepo ? \"YES\" : \"NO\"}`);\n\n if (ctx.isInsideGitRepo) {\n lines.push(`- Current branch: ${ctx.currentBranch}`);\n\n if (ctx.recentCommits.length > 0) {\n lines.push(`- Recent commits (newest first):`);\n ctx.recentCommits.forEach((c) => lines.push(` ${c}`));\n } else {\n lines.push(`- Recent commits: (none yet)`);\n }\n\n if (ctx.gitStatus.trim()) {\n lines.push(`- Working tree status:\\n${ctx.gitStatus}`);\n } else {\n lines.push(`- Working tree status: clean`);\n }\n\n if (ctx.stashes && ctx.stashes.length > 0) {\n lines.push(`- Stashes:`);\n ctx.stashes.forEach((s) => lines.push(` ${s}`));\n }\n\n if (ctx.openPRs && ctx.openPRs.length > 0) {\n lines.push(`- Open PRs:`);\n ctx.openPRs.forEach((pr) =>\n lines.push(` #${pr.number} [${pr.state}] \"${pr.title}\" (branch: ${pr.branch})`)\n );\n }\n }\n\n // ── Section 3: question ───────────────────────────────────────────────────\n lines.push(``);\n lines.push(`## Question`);\n lines.push(question);\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Safely parses the AI JSON response for `gitx ask`.\n * Falls back to using the raw text as the answer if JSON parsing fails.\n */\nexport function parseAskResponse(raw: string): AiAskResponse {\n try {\n const parsed = JSON.parse(raw) as Partial<AiAskResponse>;\n return {\n answer: parsed.answer?.trim() ?? raw.trim(),\n suggestedCommands: Array.isArray(parsed.suggestedCommands)\n ? parsed.suggestedCommands.filter((c): c is string => typeof c === \"string\")\n : [],\n };\n } catch {\n // If the AI returned plain text instead of JSON, use it directly\n return { answer: raw.trim(), suggestedCommands: [] };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"reviewHelpers.js","sourceRoot":"","sources":["../../src/ai/reviewHelpers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shared helpers for the senior-developer PR review.\n *\n * buildSeniorReviewSystem() — the AI system prompt\n * buildSeniorReviewPrompt() — formats the user-facing context block\n * parseSeniorReview() — safely parses the AI JSON response\n */\n\nimport type { AiAskContext, AiAskResponse, AiDetailedReviewResponse, AiFixResponse } from \"./types.js\";\n\n// ─── System prompt ────────────────────────────────────────────────────────────\n\nexport function buildSeniorReviewSystem(): string {\n return `You are a principal software engineer and tech lead doing a thorough pull request review.\nYou have access to the CHANGED SECTIONS of each file (extracted around the exact lines that changed),\nplus supporting context files and the full unified diff.\n\nYour review MUST cover every one of these dimensions:\n 1. Correctness — logic errors, off-by-one, wrong conditions, silent failures\n 2. Security — injection, auth bypass, secret leakage, unvalidated input\n 3. Robustness — missing error handling, null/undefined guard, edge cases\n 4. Performance — unnecessary loops, N+1 queries, missing caching\n 5. Breaking changes — does this break existing API contracts, interfaces, or callers?\n 6. Best practices — naming, DRY, SOLID, idiomatic language usage\n 7. Test coverage — are critical paths tested? are tests meaningful?\n 8. Documentation — are public APIs documented? are complex sections explained?\n\nFor EVERY issue that maps to a specific line, add an inline comment.\nLine numbers shown in the excerpts are the REAL line numbers in the new file — use them exactly.\nOnly reference lines that appear in the excerpts you were given.\n\nVerdict rules:\n - \"approve\" → no critical or warning issues\n - \"request_changes\" → one or more critical/warning issues found\n - \"comment\" → only suggestions / minor observations\n\nRespond with ONLY valid JSON (no markdown fences, no prose outside JSON):\n{\n \"summary\": \"<3-5 sentence executive summary>\",\n \"verdict\": \"approve|request_changes|comment\",\n \"issues\": [\n { \"severity\": \"critical|warning|suggestion\", \"description\": \"<issue>\", \"file\": \"<path or null>\", \"line\": <number or null> }\n ],\n \"inlineComments\": [\n { \"path\": \"<relative file path>\", \"line\": <line number>, \"body\": \"<markdown comment>\", \"severity\": \"critical|warning|suggestion\", \"suggestion\": \"<replacement code or null>\" }\n ],\n \"positives\": [\"<good thing>\"],\n \"testingNotes\": \"<how to manually test>\",\n \"checklist\": [\n { \"area\": \"<Correctness|Security|Robustness|Performance|Breaking changes|Best practices|Tests|Documentation>\", \"status\": \"pass|warn|fail\", \"note\": \"<one sentence>\" }\n ]\n}`;\n}\n\n// ─── Diff parsing ─────────────────────────────────────────────────────────────\n\n/**\n * Parse a unified diff and return the NEW-file line ranges that were touched,\n * grouped by file path.\n *\n * A diff hunk header looks like: @@ -10,7 +12,8 @@\n * +12,8 → new file starts at line 12, hunk spans 8 lines\n */\nfunction parseHunkRanges(diff: string): Map<string, Array<{ start: number; end: number }>> {\n const result = new Map<string, Array<{ start: number; end: number }>>();\n let currentFile = \"\";\n\n for (const line of diff.split(\"\\n\")) {\n const fileMatch = line.match(/^\\+\\+\\+ b\\/(.+)/);\n if (fileMatch?.[1] && fileMatch[1] !== \"/dev/null\") {\n currentFile = fileMatch[1].trim();\n if (!result.has(currentFile)) result.set(currentFile, []);\n continue;\n }\n\n // @@ -old,count +new,count @@\n const hunkMatch = line.match(/^@@ -\\d+(?:,\\d+)? \\+(\\d+)(?:,(\\d+))? @@/);\n if (hunkMatch && currentFile) {\n const start = parseInt(hunkMatch[1]!, 10);\n const count = parseInt(hunkMatch[2] ?? \"1\", 10);\n result.get(currentFile)!.push({ start, end: start + Math.max(count - 1, 0) });\n }\n }\n\n return result;\n}\n\n/**\n * Scan BACKWARD from `fromLine` (1-based) to find where the containing\n * function / class / method starts.\n *\n * Recognises common declaration patterns for TypeScript, JavaScript, Python,\n * Go, Rust, Java, and C#. Falls back to `fromLine - fallback` if nothing\n * is found within `maxScan` lines.\n */\nfunction findContainerStart(\n lines: string[],\n fromLine: number,\n maxScan = 80,\n fallback = 30\n): number {\n // fromLine is 1-based; lines[] is 0-based\n const startIdx = Math.min(lines.length - 1, Math.max(0, fromLine - 2));\n\n for (let i = startIdx; i >= Math.max(0, startIdx - maxScan); i--) {\n const line = lines[i] ?? \"\";\n if (\n // TS/JS: export [default] [abstract] [async] function foo(\n /^\\s*(export\\s+)?(default\\s+)?(abstract\\s+)?(async\\s+)?function[\\s*]/.test(line) ||\n // TS/JS: export [abstract] class Foo\n /^\\s*(export\\s+)?(abstract\\s+)?class\\s+\\w/.test(line) ||\n // TS/JS class methods: [public|private|protected|static|override|async] methodName(\n /^\\s*(public|private|protected|static|override|async)(\\s+(public|private|protected|static|override|async))*\\s+\\w+\\s*[(<]/.test(line) ||\n // TS/JS arrow function assigned to const/let/var\n /^\\s*(export\\s+)?(const|let|var)\\s+\\w+\\s*=\\s*(async\\s+)?\\(/.test(line) ||\n // TS/JS shorthand method (no keyword): methodName(args) {\n /^\\s*\\w+\\s*\\([^)]*\\)\\s*(?::\\s*\\S+\\s*)?\\{/.test(line) ||\n // Python: def foo(\n /^\\s*def\\s+\\w+\\s*\\(/.test(line) ||\n // Go: func (recv) Foo(\n /^\\s*func\\s+/.test(line) ||\n // Rust: fn foo(\n /^\\s*(pub\\s+)?(async\\s+)?fn\\s+\\w+/.test(line) ||\n // Java/C#: returnType methodName(\n /^\\s*(public|private|protected|internal|static|virtual|override)\\s+\\S+\\s+\\w+\\s*\\(/.test(line)\n ) {\n return i + 1; // convert back to 1-based\n }\n }\n\n // Nothing found — fall back to a fixed number of lines above\n return Math.max(1, fromLine - fallback);\n}\n\n/**\n * Extract the sections of a file that were changed, always starting each\n * window at the nearest enclosing function/class boundary (or 30 lines up,\n * whichever is closer).\n *\n * Overlapping windows are merged so the same lines aren't shown twice.\n * The first HEADER_LINES of the file are always prepended (imports, class\n * declarations) so the AI understands module structure.\n *\n * Returns a line-numbered string ready to paste into the prompt.\n */\nfunction extractChangedSections(\n fileContent: string,\n hunks: Array<{ start: number; end: number }>,\n contextLinesBelow = 20 // lines below the hunk (above uses function-boundary scan)\n): string {\n const lines = fileContent.split(\"\\n\");\n const total = lines.length;\n\n // Always include file header (imports / module preamble) for structural context\n const HEADER_LINES = 20;\n\n // Build windows: above = scan to function start, below = fixed context\n const windows: Array<{ start: number; end: number }> = [];\n for (const hunk of hunks) {\n const windowStart = findContainerStart(lines, hunk.start);\n windows.push({\n start: windowStart,\n end: Math.min(total, hunk.end + contextLinesBelow),\n });\n }\n // Sort then merge\n windows.sort((a, b) => a.start - b.start);\n const merged: Array<{ start: number; end: number }> = [];\n for (const w of windows) {\n if (merged.length > 0 && w.start <= merged[merged.length - 1]!.end + 1) {\n merged[merged.length - 1]!.end = Math.max(merged[merged.length - 1]!.end, w.end);\n } else {\n merged.push({ ...w });\n }\n }\n\n const formatRange = (start: number, end: number): string =>\n lines\n .slice(start - 1, end)\n .map((l, i) => `${String(start + i).padStart(5, \" \")} | ${l}`)\n .join(\"\\n\");\n\n const sections: string[] = [];\n\n // File header (always included, unless the first window already covers it)\n const firstWindowStart = merged[0]?.start ?? 1;\n if (firstWindowStart > HEADER_LINES + 1) {\n sections.push(formatRange(1, Math.min(HEADER_LINES, total)));\n sections.push(\" … (lines omitted) …\");\n }\n\n for (let i = 0; i < merged.length; i++) {\n const { start, end } = merged[i]!;\n if (i > 0 && start > (merged[i - 1]!.end + 1)) {\n sections.push(\" … (lines omitted) …\");\n }\n sections.push(formatRange(start, end));\n }\n\n return sections.join(\"\\n\");\n}\n\n// ─── User prompt builder ──────────────────────────────────────────────────────\n\nexport function buildSeniorReviewPrompt(\n context: {\n prTitle: string;\n prBody: string;\n author: string;\n headBranch: string;\n baseBranch: string;\n diff: string;\n changedFiles: Record<string, string>;\n contextFiles: Record<string, string>;\n repoFileList: string[];\n existingComments: Array<{ author: string; body: string; path?: string; line?: number }>;\n }\n): string {\n const parts: string[] = [];\n\n parts.push(`## PR: ${context.prTitle}`);\n parts.push(`Author: ${context.author} | ${context.headBranch} → ${context.baseBranch}`);\n if (context.prBody.trim()) {\n parts.push(`\\n### Description\\n${context.prBody.slice(0, 1000)}`);\n }\n\n // ── Parse which lines actually changed per file ───────────────────────────\n const hunkRanges = parseHunkRanges(context.diff);\n\n // ── Budget constants ──────────────────────────────────────────────────────\n // Claude has a 200k-token context window. We stay well within that while\n // sending enough code for a thorough review.\n //\n // Strategy:\n // • Files ≤ FULL_FILE_THRESHOLD lines → send the ENTIRE file (best context)\n // • Larger files → smart section extraction around hunks\n // with generous context above (function boundary) and below (CONTEXT_LINES_BELOW)\n // • Hard per-file cap for huge files → PER_FILE_LINE_CAP lines of excerpts\n const FULL_FILE_THRESHOLD = 400; // files ≤ this many lines are sent whole\n const CONTEXT_LINES_BELOW = 60; // lines below each hunk in extraction mode\n const PER_FILE_LINE_CAP = 800; // hard cap on excerpt lines for very large files\n const DIFF_BUDGET = 30_000; // unified diff character budget (was 5k — way too small)\n const CTX_FILE_MAX = 4_000; // max chars per supporting context file (was 1.5k)\n\n const changedEntries = Object.entries(context.changedFiles);\n if (changedEntries.length > 0) {\n parts.push(`\\n### Changed files (line numbers are exact positions in the new file)`);\n\n for (const [path, content] of changedEntries) {\n const fileLines = content.split(\"\\n\");\n const hunks = hunkRanges.get(path) ?? [];\n\n let excerpt: string;\n let deliveryNote: string;\n\n if (fileLines.length <= FULL_FILE_THRESHOLD) {\n // Small file — send the whole thing. The AI gets complete context with no gaps.\n excerpt = fileLines\n .map((l, i) => `${String(i + 1).padStart(5, \" \")} | ${l}`)\n .join(\"\\n\");\n deliveryNote = `full file, ${fileLines.length} lines`;\n } else if (hunks.length === 0) {\n // No hunk data (binary / rename-only) — show first 100 lines as fallback\n excerpt = fileLines\n .slice(0, 100)\n .map((l, i) => `${String(i + 1).padStart(5, \" \")} | ${l}`)\n .join(\"\\n\");\n if (fileLines.length > 100)\n excerpt += `\\n … (file continues — only first 100 lines shown; no hunk data available)`;\n deliveryNote = `first 100 of ${fileLines.length} lines (no hunk data)`;\n } else {\n // Large file — extract sections around changed hunks with generous context\n excerpt = extractChangedSections(content, hunks, CONTEXT_LINES_BELOW);\n\n // Cap at PER_FILE_LINE_CAP complete lines — never mid-line\n const excerptLines = excerpt.split(\"\\n\");\n if (excerptLines.length > PER_FILE_LINE_CAP) {\n excerpt =\n excerptLines.slice(0, PER_FILE_LINE_CAP).join(\"\\n\") +\n `\\n … (${excerptLines.length - PER_FILE_LINE_CAP} more excerpt lines omitted` +\n ` — file has ${fileLines.length} total lines; see the diff below for full change)`;\n }\n deliveryNote = `${hunks.length} hunk${hunks.length > 1 ? \"s\" : \"\"}, ${fileLines.length} total lines`;\n }\n\n parts.push(`\\n#### ${path} (${deliveryNote})\\n\\`\\`\\`\\n${excerpt}\\n\\`\\`\\``);\n }\n }\n\n // ── Unified diff (the raw patch — gives the AI the exact before/after for every line)\n if (context.diff.trim()) {\n const diffSlice = context.diff.slice(0, DIFF_BUDGET);\n const diffTrunc = diffSlice.length < context.diff.length;\n parts.push(\n `\\n### Unified diff${diffTrunc ? ` (truncated to ${DIFF_BUDGET} chars — see file sections above for full content)` : \"\"}\\n\\`\\`\\`diff\\n${diffSlice}\\n\\`\\`\\``\n );\n }\n\n // ── Supporting context files (unchanged files the changes depend on) ──────\n const ctxEntries = Object.entries(context.contextFiles);\n if (ctxEntries.length > 0) {\n parts.push(`\\n### Supporting context files (unchanged — imported by changed files)`);\n for (const [path, content] of ctxEntries) {\n const ctxLines = content.split(\"\\n\");\n const ctxExcerpt = content.length <= CTX_FILE_MAX\n ? content\n : content.slice(0, CTX_FILE_MAX) + `\\n… (truncated — ${ctxLines.length} total lines)`;\n parts.push(`\\n#### ${path}\\n\\`\\`\\`\\n${ctxExcerpt}\\n\\`\\`\\``);\n }\n }\n\n // ── Existing PR comments ──────────────────────────────────────────────────\n if (context.existingComments.length > 0) {\n parts.push(`\\n### Existing review comments`);\n for (const c of context.existingComments.slice(0, 6)) {\n const loc = c.path ? ` (${c.path}${c.line ? `:${c.line}` : \"\"})` : \"\";\n parts.push(`- **${c.author}**${loc}: ${c.body.slice(0, 150)}`);\n }\n }\n\n // ── Repo file tree (structural awareness) ────────────────────────────────\n if (context.repoFileList.length > 0) {\n parts.push(`\\n### Repository file tree (top 60 files)\\n${context.repoFileList.slice(0, 60).join(\"\\n\")}`);\n }\n\n return parts.join(\"\\n\");\n}\n\n// ─── Response parser ──────────────────────────────────────────────────────────\n\nexport function parseSeniorReview(text: string): AiDetailedReviewResponse {\n let parsed: Partial<AiDetailedReviewResponse> = {};\n try {\n const fenced = text.match(/```(?:json)?\\s*([\\s\\S]*?)```/);\n const raw = fenced?.[1]?.trim() ?? text.trim();\n const start = raw.search(/\\{/);\n const end = raw.lastIndexOf(\"}\");\n const jsonStr = start !== -1 && end > start ? raw.slice(start, end + 1) : raw;\n parsed = JSON.parse(jsonStr) as Partial<AiDetailedReviewResponse>;\n } catch {\n return {\n summary: \"AI review could not be parsed. Please inspect the diff manually.\",\n verdict: \"comment\",\n issues: [],\n inlineComments: [],\n positives: [],\n testingNotes: \"Test the changed functionality manually.\",\n checklist: [],\n };\n }\n\n return {\n summary: parsed.summary ?? \"Review generated.\",\n verdict: ([\"approve\", \"request_changes\", \"comment\"].includes(parsed.verdict ?? \"\"))\n ? (parsed.verdict as AiDetailedReviewResponse[\"verdict\"])\n : \"comment\",\n issues: Array.isArray(parsed.issues) ? parsed.issues : [],\n inlineComments: Array.isArray(parsed.inlineComments)\n ? parsed.inlineComments.filter((c) => c.path && c.line > 0)\n : [],\n positives: Array.isArray(parsed.positives) ? parsed.positives : [],\n testingNotes: parsed.testingNotes ?? \"\",\n checklist: Array.isArray(parsed.checklist) ? parsed.checklist : [],\n };\n}\n\n// ─── Fix generation helpers ───────────────────────────────────────────────────\n\n/**\n * System prompt for the AI fix generator.\n * Instructs the model to produce a minimal, targeted line-range replacement.\n */\nexport function buildFixSystem(): string {\n return `You are a senior developer addressing a pull request review comment.\nYour job is to generate the MINIMAL code change that addresses the reviewer's concern.\n\nRules:\n- Change as few lines as possible — do not refactor surrounding code\n- Preserve the existing indentation style exactly\n- If the comment is a question or discussion (no code change needed), set isDiscussion: true\n- If you are unsure of the correct fix, set confidence: \"low\"\n- startLine and endLine are 1-based, inclusive line numbers in the CURRENT file\n\nRespond with ONLY valid JSON (no markdown fences, no prose outside JSON):\n{\n \"file\": \"<relative file path>\",\n \"startLine\": <1-based line where replacement starts>,\n \"endLine\": <1-based line where replacement ends, inclusive>,\n \"replacement\": \"<new code lines, newline-separated, preserving indentation>\",\n \"explanation\": \"<one sentence: what you changed and why>\",\n \"confidence\": \"high|low\",\n \"resolves\": true|false,\n \"isDiscussion\": true|false\n}`;\n}\n\n/**\n * Build the user prompt for a single fix request.\n * Includes the comment, file content with line numbers, and the relevant diff.\n */\nexport function buildFixPrompt(ctx: {\n comment: string;\n commentAuthor: string;\n filePath: string;\n line: number;\n fileContent: string;\n fileDiff: string;\n}): string {\n const lines = ctx.fileContent.split(\"\\n\");\n\n // Show a window of ±30 lines around the commented line (with real line numbers)\n const windowStart = Math.max(1, ctx.line - 30);\n const windowEnd = Math.min(lines.length, ctx.line + 30);\n const excerpt = lines\n .slice(windowStart - 1, windowEnd)\n .map((l, i) => `${String(windowStart + i).padStart(4, \" \")} | ${l}`)\n .join(\"\\n\");\n\n const diffSection = ctx.fileDiff.length > 3000\n ? ctx.fileDiff.slice(0, 3000) + \"\\n... (diff truncated)\"\n : ctx.fileDiff;\n\n return `## Review Comment\nAuthor: ${ctx.commentAuthor}\nFile: ${ctx.filePath} · Line: ${ctx.line}\n\n> ${ctx.comment.replace(/\\n/g, \"\\n> \")}\n\n## File Context (lines ${windowStart}–${windowEnd} of ${lines.length})\n\\`\\`\\`\n${excerpt}\n\\`\\`\\`\n\n## Diff for this file\n\\`\\`\\`diff\n${diffSection}\n\\`\\`\\`\n\nGenerate the fix JSON now.`;\n}\n\n/**\n * Safely parse the AI response for a fix request.\n * Returns a safe fallback (isDiscussion + low confidence) on parse failure.\n */\nexport function parseFixResponse(text: string, filePath: string, line: number): AiFixResponse {\n try {\n const jsonMatch = text.match(/\\{[\\s\\S]*\\}/);\n if (!jsonMatch) throw new Error(\"no JSON found\");\n const parsed = JSON.parse(jsonMatch[0]) as Partial<AiFixResponse>;\n\n if (parsed.isDiscussion) {\n return {\n file: parsed.file ?? filePath,\n startLine: line,\n endLine: line,\n replacement: \"\",\n explanation: parsed.explanation ?? \"This comment is a discussion — no code change needed.\",\n confidence: \"low\",\n resolves: parsed.resolves ?? false,\n isDiscussion: true,\n };\n }\n\n // Validate required fields; fall back to low confidence if anything is off\n const hasReplacement = typeof parsed.replacement === \"string\";\n const hasLines = typeof parsed.startLine === \"number\" && typeof parsed.endLine === \"number\";\n if (!hasReplacement || !hasLines) {\n throw new Error(\"missing required fields\");\n }\n\n return {\n file: parsed.file ?? filePath,\n startLine: parsed.startLine!,\n endLine: parsed.endLine!,\n replacement: parsed.replacement!,\n explanation: parsed.explanation ?? \"AI-generated fix.\",\n confidence: parsed.confidence === \"high\" ? \"high\" : \"low\",\n resolves: parsed.resolves ?? false,\n isDiscussion: false,\n };\n } catch {\n return {\n file: filePath,\n startLine: line,\n endLine: line,\n replacement: \"\",\n explanation: \"AI could not generate a fix for this comment.\",\n confidence: \"low\",\n resolves: false,\n isDiscussion: true, // treat parse failure as discussion → no code change\n };\n }\n}\n\n// ─── Ask command helpers ───────────────────────────────────────────────────────\n\n/** Command reference embedded into the ask system prompt. */\nconst GITX_COMMAND_REFERENCE = `\n## gitx Command Reference\n\n| Command | Description |\n|---------|-------------|\n| gitx init / gitx config setup | Interactive setup wizard — configure git & AI providers |\n| gitx config show | Display current configuration (secrets redacted) |\n| gitx config set <key> [value] | Set a single config value (provider, token, model, etc.) |\n| gitx commit [-m msg] [--push] [--dry-run] | AI-generate commit message → commit (optionally push) |\n| gitx push [-b branch] [--staged] [--dry-run] | Stage → AI-commit → push in one step; --staged uses already-staged files only |\n| gitx sync [--base branch] [--strategy merge|rebase] [--continue] [--abort] | Sync current branch with base; AI resolves conflicts |\n| gitx port <target…> [--base branch] [--no-pr] [--draft] [--continue] [--abort] | Cherry-pick commits onto other branches with incremental detection |\n| gitx implement \"<task>\" [--mode plan|guided|semi-auto|auto] [--dry-run] | AI-plan and implement a task end-to-end |\n| gitx pr list [--state open|closed|all] | List pull requests |\n| gitx pr create [--title T] [--body B] [--draft] [--dry-run] | AI-generate PR title/body → open PR |\n| gitx pr review <number> [--no-comment] [--inline] | Senior-dev AI review — posts inline comments to the PR |\n| gitx pr resolve <number> [--no-commit] [--no-push] [--dry-run] | AI-fix review comments in code; --no-commit applies fixes without committing |\n| gitx pr merge <number> [--strategy squash|merge|rebase] [--delete-branch] | Merge a PR |\n| gitx pr close <number> [-f] | Close a PR |\n| gitx ask \"<question>\" [--pr] | Ask a question about the repo using AI + live git context |\n\n## Supported Providers\n- Git hosts: GitHub, GitLab, Azure DevOps\n- AI backends: Anthropic Claude (API), OpenAI, Local Claude CLI\n\n## Environment Variables\n- ANTHROPIC_API_KEY — Anthropic API key (auto-selects Claude as AI provider)\n- OPENAI_API_KEY — OpenAI API key\n- GITX_AI_MODEL — Override the AI model name\n- GITX_DEBUG=1 — Print full stack traces on errors\n`.trim();\n\n/**\n * Builds the system prompt for `gitx ask`.\n * Includes the full command reference and setup guidance so the AI can answer\n * both \"how do I…\" questions and \"is X configured?\" diagnostics.\n */\nexport function buildAskSystem(): string {\n return `You are gitx-assistant, a smart support assistant embedded in the gitx CLI.\nYou help users with three types of questions:\n\n1. SETUP / DIAGNOSTIC — \"is my AI provider set up?\", \"why isn't gitx working?\", \"what provider am I using?\"\n → Use the GITX SETUP STATUS section in the context. Give a clear yes/no diagnostic and actionable fix steps.\n\n2. REPO STATE — \"what did I last commit?\", \"do I have unstaged changes?\", \"show me open PRs\"\n → Use the LIVE REPO CONTEXT section in the context.\n\n3. HOW-TO — \"how do I sync with main?\", \"how do I undo a commit?\", \"how do I create a PR?\"\n → Use the GITX COMMAND REFERENCE below. Show the exact command.\n\n${GITX_COMMAND_REFERENCE}\n\n## Setup Fix Guide (use when AI or provider is not configured)\n- No AI provider → Run: gitx config setup (or set ANTHROPIC_API_KEY / OPENAI_API_KEY env var)\n- AI provider configured in config but not working → Run: gitx config show to inspect; re-run gitx config set <provider>\n- No git provider token → Run: gitx config set github (or gitlab / azure)\n- Not inside a git repo → cd into your project folder first\n\nRules:\n- Answer concisely and accurately. Get to the point immediately.\n- For setup/diagnostic questions: state clearly whether it IS or IS NOT configured, then explain WHY and how to fix it.\n- Never fabricate details — only use what is in the provided context.\n- Format your answer in plain text. Use a code block only for commands or file paths.\n- When suggesting commands, put them in suggestedCommands so they render highlighted.\n\nRespond with ONLY valid JSON (no markdown fences):\n{\"answer\":\"<answer text>\",\"suggestedCommands\":[\"<cmd1>\",\"<cmd2>\"]}\n\nThe suggestedCommands array may be empty [] if no command applies.`;\n}\n\n/**\n * Builds the user-turn prompt for `gitx ask`, injecting live repo context\n * and the full gitx setup status so the AI can answer diagnostic questions accurately.\n */\nexport function buildAskPrompt(question: string, ctx: AiAskContext): string {\n const lines: string[] = [];\n\n // ── Section 1: gitx setup status ──────────────────────────────────────────\n lines.push(`## gitx Setup Status`);\n\n // AI provider\n const ai = ctx.aiSetup;\n lines.push(`- AI provider: ${ai.provider}`);\n lines.push(`- AI configured: ${ai.isConfigured ? \"YES\" : \"NO — not configured\"}`);\n if (ai.model) lines.push(`- AI model: ${ai.model}`);\n lines.push(`- AI key source: ${ai.keySource}`);\n\n // Git providers\n if (ctx.gitProviders.length > 0) {\n lines.push(`- Git providers configured:`);\n ctx.gitProviders.forEach((p) => {\n const tokenStatus = p.hasToken ? \"token ✓\" : \"token MISSING\";\n lines.push(` ${p.name}: ${tokenStatus}`);\n });\n } else {\n lines.push(`- Git providers configured: none`);\n }\n\n if (ctx.defaultBranch) {\n lines.push(`- Default base branch: ${ctx.defaultBranch}`);\n }\n\n // ── Section 2: live repo context ──────────────────────────────────────────\n lines.push(``);\n lines.push(`## Live Repo Context`);\n lines.push(`- Inside git repo: ${ctx.isInsideGitRepo ? \"YES\" : \"NO\"}`);\n\n if (ctx.isInsideGitRepo) {\n lines.push(`- Current branch: ${ctx.currentBranch}`);\n\n if (ctx.recentCommits.length > 0) {\n lines.push(`- Recent commits (newest first):`);\n ctx.recentCommits.forEach((c) => lines.push(` ${c}`));\n } else {\n lines.push(`- Recent commits: (none yet)`);\n }\n\n if (ctx.gitStatus.trim()) {\n lines.push(`- Working tree status:\\n${ctx.gitStatus}`);\n } else {\n lines.push(`- Working tree status: clean`);\n }\n\n if (ctx.stashes && ctx.stashes.length > 0) {\n lines.push(`- Stashes:`);\n ctx.stashes.forEach((s) => lines.push(` ${s}`));\n }\n\n if (ctx.openPRs && ctx.openPRs.length > 0) {\n lines.push(`- Open PRs:`);\n ctx.openPRs.forEach((pr) =>\n lines.push(` #${pr.number} [${pr.state}] \"${pr.title}\" (branch: ${pr.branch})`)\n );\n }\n }\n\n // ── Section 3: question ───────────────────────────────────────────────────\n lines.push(``);\n lines.push(`## Question`);\n lines.push(question);\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Safely parses the AI JSON response for `gitx ask`.\n * Falls back to using the raw text as the answer if JSON parsing fails.\n */\nexport function parseAskResponse(raw: string): AiAskResponse {\n try {\n const parsed = JSON.parse(raw) as Partial<AiAskResponse>;\n return {\n answer: parsed.answer?.trim() ?? raw.trim(),\n suggestedCommands: Array.isArray(parsed.suggestedCommands)\n ? parsed.suggestedCommands.filter((c): c is string => typeof c === \"string\")\n : [],\n };\n } catch {\n // If the AI returned plain text instead of JSON, use it directly\n return { answer: raw.trim(), suggestedCommands: [] };\n }\n}\n"]}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @deprecated Renamed to `resolve.ts`.
|
|
3
|
+
* This command is now `gitx pr resolve`. Use registerPrResolveCommand from ./resolve.js instead.
|
|
4
|
+
*/
|
|
5
|
+
export { registerPrResolveCommand as registerPrFixCommentsCommand } from "./resolve.js";
|
|
3
6
|
//# sourceMappingURL=fixComments.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fixComments.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/pr/fixComments.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"fixComments.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/pr/fixComments.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,wBAAwB,IAAI,4BAA4B,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -1,83 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export function registerPrFixCommentsCommand(pr) {
|
|
7
|
-
pr.command("fix-comments")
|
|
8
|
-
.description("🩹 AI-fix review comments and push changes")
|
|
9
|
-
.argument("<id>", "Pull request number")
|
|
10
|
-
.option("--dry-run", "Preview fixes without applying or committing", false)
|
|
11
|
-
.option("--no-push", "Apply & commit locally but skip push", false)
|
|
12
|
-
.action(async (id, options) => {
|
|
13
|
-
const prNumber = parseInt(id, 10);
|
|
14
|
-
if (isNaN(prNumber) || prNumber <= 0) {
|
|
15
|
-
logger.error(`Invalid PR number: ${id}`);
|
|
16
|
-
process.exit(1);
|
|
17
|
-
}
|
|
18
|
-
const gitx = await Gitx.fromCwd();
|
|
19
|
-
const ctx = await gitx.getRepoContext();
|
|
20
|
-
logger.info(`🩹 Fixing review comments on PR #${prNumber} (${ctx.repoSlug})…\n`);
|
|
21
|
-
// ── AI availability warning ────────────────────────────────────────────
|
|
22
|
-
if (!await Gitx.isAiAvailable(gitx.config)) {
|
|
23
|
-
logger.warn("⚠️ No AI provider configured — AI fix suggestions will be empty.\n" +
|
|
24
|
-
" Run `gitx config` to set up an AI provider (Claude, OpenAI, or claude-cli).");
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
if (!options.dryRun) {
|
|
28
|
-
const { proceed } = await inquirer.prompt([
|
|
29
|
-
{
|
|
30
|
-
type: "confirm",
|
|
31
|
-
name: "proceed",
|
|
32
|
-
message: "This will apply AI-suggested fixes, commit, and push. Continue?",
|
|
33
|
-
default: false,
|
|
34
|
-
},
|
|
35
|
-
]);
|
|
36
|
-
if (!proceed) {
|
|
37
|
-
logger.warn("Cancelled.");
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
// ── Guard: warn about uncommitted changes ────────────────────────────
|
|
42
|
-
if (!options.dryRun) {
|
|
43
|
-
const dirty = await isWorkingTreeDirty(gitx.cwd);
|
|
44
|
-
if (dirty) {
|
|
45
|
-
logger.warn("⚠️ You have uncommitted changes. They may conflict with applied fixes.");
|
|
46
|
-
const { cont } = await inquirer.prompt([
|
|
47
|
-
{ type: "confirm", name: "cont", message: "Continue anyway?", default: false },
|
|
48
|
-
]);
|
|
49
|
-
if (!cont) {
|
|
50
|
-
logger.warn("Cancelled.");
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
const result = await runFixCommentsWorkflow(gitx, prNumber, options.dryRun);
|
|
56
|
-
logger.info(`\n📋 PR: ${result.pr.title}`);
|
|
57
|
-
if (result.appliedFixes.length === 0 && result.skippedFixes.length === 0) {
|
|
58
|
-
logger.info("No actionable review comments found.");
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
if (result.appliedFixes.length > 0) {
|
|
62
|
-
logger.success(`\n✅ Applied ${result.appliedFixes.length} fix(es):`);
|
|
63
|
-
result.appliedFixes.forEach((f) => logger.info(` • ${f.path} — ${f.rationale}`));
|
|
64
|
-
}
|
|
65
|
-
if (result.skippedFixes.length > 0) {
|
|
66
|
-
logger.warn(`\n⚠️ Skipped ${result.skippedFixes.length} fix(es):`);
|
|
67
|
-
result.skippedFixes.forEach((f) => logger.warn(` • ${f.path}: ${f.reason}`));
|
|
68
|
-
}
|
|
69
|
-
// Push if not dry-run and push not disabled
|
|
70
|
-
if (!options.dryRun && options.push !== false && result.appliedFixes.length > 0) {
|
|
71
|
-
const branch = await getCurrentBranch(gitx.cwd);
|
|
72
|
-
logger.info(`\n🚀 Pushing ${branch}…`);
|
|
73
|
-
try {
|
|
74
|
-
await pushBranch(branch, gitx.cwd);
|
|
75
|
-
logger.success("Branch pushed.");
|
|
76
|
-
}
|
|
77
|
-
catch (err) {
|
|
78
|
-
logger.warn(`Push failed: ${String(err.message ?? err)}`);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* @deprecated Renamed to `resolve.ts`.
|
|
3
|
+
* This command is now `gitx pr resolve`. Use registerPrResolveCommand from ./resolve.js instead.
|
|
4
|
+
*/
|
|
5
|
+
export { registerPrResolveCommand as registerPrFixCommentsCommand } from "./resolve.js";
|
|
83
6
|
//# sourceMappingURL=fixComments.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fixComments.js","sourceRoot":"","sources":["../../../../src/cli/commands/pr/fixComments.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fixComments.js","sourceRoot":"","sources":["../../../../src/cli/commands/pr/fixComments.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,wBAAwB,IAAI,4BAA4B,EAAE,MAAM,cAAc,CAAC","sourcesContent":["/**\n * @deprecated Renamed to `resolve.ts`.\n * This command is now `gitx pr resolve`. Use registerPrResolveCommand from ./resolve.js instead.\n */\nexport { registerPrResolveCommand as registerPrFixCommentsCommand } from \"./resolve.js\";\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { registerPrListCommand } from "./list.js";
|
|
2
2
|
import { registerPrCreateCommand } from "./create.js";
|
|
3
3
|
import { registerPrReviewCommand } from "./review.js";
|
|
4
|
-
import {
|
|
4
|
+
import { registerPrResolveCommand } from "./resolve.js";
|
|
5
5
|
import { registerPrCloseCommand } from "./close.js";
|
|
6
6
|
import { registerPrMergeCommand } from "./merge.js";
|
|
7
7
|
export function registerPrCommands(program) {
|
|
@@ -10,7 +10,7 @@ export function registerPrCommands(program) {
|
|
|
10
10
|
registerPrCreateCommand(pr);
|
|
11
11
|
registerPrMergeCommand(pr);
|
|
12
12
|
registerPrReviewCommand(pr);
|
|
13
|
-
|
|
13
|
+
registerPrResolveCommand(pr);
|
|
14
14
|
registerPrCloseCommand(pr);
|
|
15
15
|
}
|
|
16
16
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/cli/commands/pr/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAClD,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/cli/commands/pr/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAClD,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEpD,MAAM,UAAU,kBAAkB,CAAC,OAAgB;IACjD,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,0BAA0B,CAAC,CAAC;IAEzE,qBAAqB,CAAC,EAAE,CAAC,CAAC;IAC1B,uBAAuB,CAAC,EAAE,CAAC,CAAC;IAC5B,sBAAsB,CAAC,EAAE,CAAC,CAAC;IAC3B,uBAAuB,CAAC,EAAE,CAAC,CAAC;IAC5B,wBAAwB,CAAC,EAAE,CAAC,CAAC;IAC7B,sBAAsB,CAAC,EAAE,CAAC,CAAC;AAC7B,CAAC","sourcesContent":["import type { Command } from \"commander\";\nimport { registerPrListCommand } from \"./list.js\";\nimport { registerPrCreateCommand } from \"./create.js\";\nimport { registerPrReviewCommand } from \"./review.js\";\nimport { registerPrResolveCommand } from \"./resolve.js\";\nimport { registerPrCloseCommand } from \"./close.js\";\nimport { registerPrMergeCommand } from \"./merge.js\";\n\nexport function registerPrCommands(program: Command): void {\n const pr = program.command(\"pr\").description(\"🔀 Pull request commands\");\n\n registerPrListCommand(pr);\n registerPrCreateCommand(pr);\n registerPrMergeCommand(pr);\n registerPrReviewCommand(pr);\n registerPrResolveCommand(pr);\n registerPrCloseCommand(pr);\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/pr/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOzC,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CA+F1D"}
|