feat: git notes based AI attribution (git ai log/blame/inspect)#22
feat: git notes based AI attribution (git ai log/blame/inspect)#22
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (8)
✅ Files skipped from review due to trivial changes (4)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughAdds a full AI-attribution subsystem: schemas, hashing, attribution builder, git-notes persistence, CLI commands (record/log/blame/inspect/export/import/explain/scan/validate/interactive/notes), an Ink explorer UI, tests, GitService helpers, and dual-mode CLI wiring for Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant CLI as CLI
participant GitSvc as GitService
participant AttrSvc as AttributionService
participant NotesStore as AiNotesStore
participant Git as git
User->>CLI: git ai record --commit SHA --intent "..." --prompt "..." --path file.ts
CLI->>GitSvc: raw(['rev-parse', commit])
GitSvc->>Git: git rev-parse
Git-->>GitSvc: resolved SHA
CLI->>GitSvc: raw(['show', 'SHA:path']) %% optional lines-from-file
GitSvc->>Git: git show
Git-->>GitSvc: file contents
CLI->>AttrSvc: buildRecord(commit, input)
AttrSvc->>GitSvc: raw(['show','-s','--format=%T',commit])
GitSvc->>Git: git show (tree)
Git-->>GitSvc: tree hash
AttrSvc-->>CLI: AiAttribution {id, commit, tree, anchors, ...}
CLI->>NotesStore: upsertAttribution(record)
NotesStore->>GitSvc: raw(['notes','--ref','refs/notes/git-ai','show',commit])
GitSvc->>Git: git notes show
Git-->>GitSvc: note JSON or error
NotesStore->>NotesStore: merge index & records
NotesStore->>GitSvc: raw(['notes','--ref','refs/notes/git-ai','add','-f','-m',payload])
GitSvc->>Git: git notes add
Git-->>GitSvc: success
NotesStore-->>CLI: success
CLI-->>User: ✓ Record saved (id)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (17)
git-ai/src/commands/ai/index.ts (1)
30-39: Surface rewrite preservation in the top-level help too.This block already documents how to share
refs/notes/git-ai; addinggit config --add notes.rewriteRef refs/notes/git-aihere would also cover the setup users need to keep notes across amend/rebase flows.✏️ Suggested help text
ai.addHelpText( 'after', [ '', 'Notes storage:', ' This tool stores metadata in `git notes --ref refs/notes/git-ai`.', + ' Preserve notes across rebases/amends with:', + ' git config --add notes.rewriteRef refs/notes/git-ai', ' Share notes with teammates by pushing/fetching that ref:', ' git push origin refs/notes/git-ai', ' git fetch origin refs/notes/git-ai:refs/notes/git-ai',🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@git-ai/src/commands/ai/index.ts` around lines 30 - 39, The top-level help added via ai.addHelpText should mention preserving notes across rewrites; update the message array passed to ai.addHelpText to include a line advising users to run "git config --add notes.rewriteRef refs/notes/git-ai" (or equivalent) so `refs/notes/git-ai` is automatically rewritten during amend/rebase flows; edit the string block in the ai.addHelpText call to add that single-line instruction near the notes/sharing lines so users see setup and preservation together.git-ai/tsconfig.json (1)
33-33: Consider a separate test tsconfig.Dropping the test-file exclusion makes the main
tscbuild emit compiled tests intodistso the newpackage.jsontest flow works, but it also couplesnpm run build/prepareto test-only code and ships test artifacts with the release build. Atsconfig.build.json+tsconfig.test.jsonsplit would keep the workflow without bloatingdist.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@git-ai/tsconfig.json` at line 33, Replace the current single tsconfig setup that removes "exclude": ["node_modules", "dist"] with a split config: create tsconfig.build.json (extends the base tsconfig and keeps tests excluded so tsc --build/--project for production builds doesn't emit test artifacts) and tsconfig.test.json (extends base but includes test files for the test flow). Update the build/test npm scripts to point to the appropriate config (e.g., tsc -p tsconfig.build.json for build/prepare and tsc -p tsconfig.test.json or your test runner compile step for tests) and ensure the base tsconfig or the original "exclude" remains as the canonical source for editor tooling.git-ai/src/commands/ai/AiLogCommand.ts (1)
67-73: Sequential note fetching may be slow for large limits.Each commit triggers a separate
git notes showcall. For large--limitvalues, this could be slow. Consider batching or parallelizing if performance becomes an issue, though it's acceptable for typical usage.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@git-ai/src/commands/ai/AiLogCommand.ts` around lines 67 - 73, The loop fetches notes sequentially (for const commit of commits { await store.listIndexForCommit(commit) }) which can be slow for large limits; change to parallelize or batch the calls by mapping commits to promises and using Promise.all (or processing in configurable batches) to call store.listIndexForCommit concurrently, then flatten results and apply matchesFilter to populate rows (keep references: commits, store.listIndexForCommit, matchesFilter, rows).git-ai/src/commands/ai/AiImportCommand.ts (1)
42-51: Consider logging line numbers for invalid entries.When records fail to parse, users have no way to identify which lines are problematic for debugging or correction.
♻️ Suggested improvement
+ let lineNum = 0; for (const line of lines) { + lineNum++; try { const parsed = JSON.parse(line); const rec = AiAttributionSchema.parse(parsed); await store.upsertAttribution(rec); ok++; } catch { + console.warn(`Line ${lineNum}: invalid or unparseable`); bad++; } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@git-ai/src/commands/ai/AiImportCommand.ts` around lines 42 - 51, When iterating lines in AiImportCommand (the for loop that calls JSON.parse and AiAttributionSchema.parse and upserts via store.upsertAttribution), update the catch to log which line index/text failed and include the parsing error; specifically capture the current index (or line number) and the caught error, and write a clear processLogger or console.error message referencing the failing line, the index, and the error before incrementing bad so users can locate and fix invalid entries.git-ai/src/index.ts (1)
31-44: Version string is duplicated.The version
1.0.0appears in both the banner (line 31) and.version()(line 44). Consider extracting to a constant to avoid drift.♻️ Suggested improvement
+const VERSION = '1.0.0'; + if (process.stdout.isTTY) { const banner = ` - ${chalk.bold.magenta('●')} ${chalk.bold('GIT-AI')} ${chalk.dim('v1.0.0')} + ${chalk.bold.magenta('●')} ${chalk.bold('GIT-AI')} ${chalk.dim(`v${VERSION}`)} ${chalk.dim('————————————————————————————————')} `; console.log(banner); } program .name(nameFromBin) .description(...) - .version('1.0.0'); + .version(VERSION);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@git-ai/src/index.ts` around lines 31 - 44, Extract the duplicated version string into a single constant (e.g., VERSION) and use that constant both when building the banner (the variable banner) and when calling program.version() so the value is defined in one place; update any references (banner construction and program.version) to reference the new VERSION constant to prevent drift.git-ai/src/commands/ai/AiScanCommand.ts (1)
48-54: Consider reporting all matches per rule, not just the first.Currently
diff.match(rule.pattern)returns only the first match. For a thorough scan, users may want to see all occurrences. UsingmatchAllwith the global flag would capture repeated violations.♻️ Suggested enhancement for multiple matches
+// Add 'g' flag to patterns in RISK_RULES, e.g.: +// { id: 'eval', pattern: /\beval\s*\(/g, message: '...' }, const hits: { rule: string; message: string; sample: string }[] = []; for (const rule of RISK_RULES) { - const m = diff.match(rule.pattern); - if (m) { - hits.push({ rule: rule.id, message: rule.message, sample: m[0] }); - } + const matches = [...diff.matchAll(rule.pattern)]; + for (const m of matches) { + hits.push({ rule: rule.id, message: rule.message, sample: m[0] }); + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@git-ai/src/commands/ai/AiScanCommand.ts` around lines 48 - 54, The scan only captures the first match because it uses diff.match(rule.pattern); update the loop over RISK_RULES in AiScanCommand to iterate over all matches: for each rule, obtain a global RegExp (if rule.pattern isn’t already global, construct one preserving flags plus 'g'), call diff.matchAll(globalPattern), and for each match push a hit object (use rule.id, rule.message and the match text like m[0]) into the hits array so every occurrence is reported rather than only the first.git-ai/src/commands/ai/AiNotesCommand.ts (1)
35-41: Consider using proper typing instead ofany.The
any[]type and(r: any)reduce type safety.simple-git(orGitService) likely returns a typed remote object.♻️ Suggested improvement
- let remotes: any[] = []; + let remotes: { name: string; refs?: { fetch?: string; push?: string } }[] = []; try { remotes = await git.getRemotes(true); } catch { // ignore } - const found = remotes.find((r: any) => r.name === remote); + const found = remotes.find((r) => r.name === remote);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@git-ai/src/commands/ai/AiNotesCommand.ts` around lines 35 - 41, The code uses unsafe any for remotes and the find callback; change remotes: any[] to a concrete remote type (e.g. RemoteWithRefs[] from simple-git or your project's GitService remote type) and update the find callback to (r: RemoteWithRefs) => r.name === remote; if the project doesn't expose that type, add a small local interface (e.g. interface GitRemote { name: string; refs?: Record<string,string> }) and use GitRemote[] and (r: GitRemote) in the find. Also add the necessary import for RemoteWithRefs (or declare the local interface) so the compiler can enforce proper typing for git.getRemotes and the found variable.git-ai/src/commands/ai/AiBlameCommand.ts (1)
89-96: First-match semantics may produce non-deterministic results.Line 94 selects
matches[0].idwhen multiple records claim the same line hash. Since the order depends on commit iteration and map insertion order, results could vary. Consider sorting candidates bycreatedAtor commit date for deterministic output.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@git-ai/src/commands/ai/AiBlameCommand.ts` around lines 89 - 96, The current annotation loop in AiBlameCommand uses the first element of matches (matches[0].id) which can be nondeterministic when multiple records share a line hash; update the logic in the loop that builds annotated (referencing lineHashes, hashToRecord, matches) to deterministically pick a record by sorting or selecting by a stable timestamp field (e.g., record.createdAt or commit date) before taking the id, so recordId is derived from the earliest/desired createdAt-sorted candidate rather than insertion order.git-ai/src/commands/ai/AiValidateCommand.ts (2)
50-61: Redundant validation:getRecordalready validates internally.Per
notes-store.ts:105-112,getRecordreturnsnullwhenAiAttributionSchema.safeParsefails. The secondsafeParseon line 56 will always succeed ifrecis non-null.If the intent is to catch schema drift between index and full record, the current approach is fine as defensive coding. Otherwise, you could simplify by trusting
getRecord's internal validation.♻️ Optional simplification
for (const entry of idx) { const rec = await store.getRecord(c, entry.id); if (!rec) { bad++; console.error(`Invalid: missing record payload for ${entry.id} on ${c}`); continue; } - const parsed = AiAttributionSchema.safeParse(rec); - if (!parsed.success) { - bad++; - console.error(`Invalid: schema mismatch for ${entry.id} on ${c}`); - continue; - } ok++; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@git-ai/src/commands/ai/AiValidateCommand.ts` around lines 50 - 61, The second schema check is redundant because store.getRecord already returns null on schema failure; remove the AiAttributionSchema.safeParse block (the parsed variable and its check) from AiValidateCommand.ts and rely on getRecord's null check (rec) to mark bad and log the error; keep the existing error message for missing/invalid payloads and continue the loop, ensuring no other logic depends on parsed being present.
10-14: Consider extractingtoNumto a shared utility.This helper is duplicated across
AiValidateCommand,AiBlameCommand,AiExportCommand, andAiLogCommand. Extracting it to a shared module (e.g.,utils/parse.ts) would reduce duplication.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@git-ai/src/commands/ai/AiValidateCommand.ts` around lines 10 - 14, The helper function toNum is duplicated across AiValidateCommand, AiBlameCommand, AiExportCommand and AiLogCommand; extract it into a single shared parse utility module (exporting function toNum(value: string | undefined, fallback: number): number) and update each command to import and use that exported toNum instead of the inline copy, removing the duplicated implementations and adjusting imports/exports so TypeScript compiles cleanly.git-ai/src/commands/ai/AiExportCommand.ts (1)
47-47: Consider using a static import forfs/promises.The dynamic
await import('fs/promises')is unusual for a Node.js built-in. A static import at the top of the file would be cleaner and has negligible overhead.♻️ Use static import
import { Command } from 'commander'; import { GitService } from '../../core/GitService.js'; import { AiNotesStore } from '../../ai/notes-store.js'; +import fs from 'fs/promises';Then remove line 47 and use
fsdirectly on line 59.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@git-ai/src/commands/ai/AiExportCommand.ts` at line 47, Replace the dynamic import of fs/promises in the AiExportCommand module with a static top-level import and use that exported binding where fs is referenced: remove the await import('fs/promises') statement and add a static import for fs/promises at the top of the file so functions like the code that writes the exported file in AiExportCommand (the place currently using the dynamically imported fs) call the imported fs methods directly.git-ai/src/commands/InitCommand.ts (1)
134-136: Consider including the log file location in the error message.The generic "Check logs for details" message is helpful, but users may not know where logs are stored. Consider including the log path or the actual error summary.
💡 Suggested improvement
} catch (error) { logger.error('Init failed', error as any); - console.error('\n❌ Setup failed. Check logs for details.'); + const msg = error instanceof Error ? error.message : 'Unknown error'; + console.error(`\n❌ Setup failed: ${msg}`); } finally {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@git-ai/src/commands/InitCommand.ts` around lines 134 - 136, The catch block in InitCommand.ts currently logs the error via logger.error and prints a generic console.error message; update the catch handler (the catch in the init flow where logger.error(...) and console.error(...) are called) to include either the actual error summary (e.g., error.message or a trimmed stack) and the log file path in the console output so users know where to look; modify the logger.error call to still record the full error and change the console.error line to print "❌ Setup failed: <short error message>. See logs at <logPath>" (use the existing log path/config variable used by your app).git-ai/src/ai/schema.ts (1)
24-26: Consider stricter validation forcreatedAttimestamp.The
createdAtfield only validates that it's a non-empty string. For better data integrity, consider validating ISO 8601 format withz.string().datetime()or a custom refinement.💡 Suggested improvement
// Actors and time. author: z.string().min(1).optional(), - createdAt: z.string().min(1), + createdAt: z.string().datetime({ message: 'createdAt must be ISO 8601 format' }),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@git-ai/src/ai/schema.ts` around lines 24 - 26, The createdAt field currently only ensures a non-empty string; update its Zod validation to enforce an ISO 8601 timestamp by replacing z.string().min(1) for the createdAt property with z.string().datetime() (or, if you need timezone control, use z.string().refine(s => !Number.isNaN(Date.parse(s)), { message: 'createdAt must be a valid ISO 8601 datetime' })) so the schema's createdAt property is strictly validated; adjust any tests or consumers expecting the looser format accordingly.git-ai/src/commands/ai/AiExplainCommand.ts (2)
60-69: Consider using a typed array instead ofany[].Using
any[]loses type safety for the records being collected. Sincestore.getRecord()returnsAiAttribution | null, consider typing this more precisely.♻️ Suggested improvement
- const records: any[] = []; + const records: Array<AiAttribution & { noteCommit: string }> = [];You'll need to import
AiAttributionfrom the schema:import { AiNotesStore } from '../../ai/notes-store.js'; +import { AiAttribution } from '../../ai/schema.js';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@git-ai/src/commands/ai/AiExplainCommand.ts` around lines 60 - 69, The records array is typed as any[], losing type safety; change it to a properly typed array such as (AiAttribution & { noteCommit: typeof c })[] (or replace typeof c with the actual commit id type) and import AiAttribution from your schema; update the declaration of records and any related usages so that records.push({ noteCommit: c, ...rec }) only accepts non-null rec (guard rec's nullability or narrow its type after the await from store.getRecord), and ensure store.getRecord is treated as returning AiAttribution | null in the loop that iterates commits and calls store.listIndexForCommit and store.getRecord.
89-104: Consider token limits for large files or many records.The prompt includes the full file content and all attribution records (up to 200 commits worth). For large files or extensive attribution history, this could exceed model token limits and lead to truncation or API errors.
For now this is acceptable, but consider adding content truncation or summarization for future robustness.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@git-ai/src/commands/ai/AiExplainCommand.ts` around lines 89 - 104, The prompt assembly in AiExplainCommand.ts (the prompt variable built from records and fileContent) can exceed model token limits for large files or many AI attribution records; update the logic that prepares records and fileContent before JSON.stringify and concatenation to truncate or summarize them (e.g., keep only the most recent N records from records, or collapse records to a brief summary, and trim fileContent to a token/character limit or generate a short diff/summary) and/or add a lightweight token-estimation step to ensure the final prompt fits model limits; apply these changes where prompt is constructed so prompt uses the trimmed/summarized records and fileContent.git-ai/src/ai/notes-store.ts (2)
100-112: Commit field override is intentional but may mask inconsistencies.Both
listIndexForCommitandgetRecordoverride the storedcommitfield with the parameter. This ensures consistency but silently ignores any mismatch between the stored commit and the requested one. Consider logging a warning if they differ, which could indicate data corruption.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@git-ai/src/ai/notes-store.ts` around lines 100 - 112, Both listIndexForCommit and getRecord currently overwrite the stored commit with the provided commit parameter, which can hide mismatches; after calling readCommitNote(check readCommitNote) validate whether each entry's commit (for listIndexForCommit: payload.index entries' commit; for getRecord: the stored rec.commit) differs from the commit parameter and emit a warning via the class logger (e.g., this.logger.warn) naming the function (listIndexForCommit/getRecord), the id or index entry info, the stored commit value and the requested commit; keep the existing behavior of returning the result with commit set to the parameter but add the warning when they differ to surface potential data corruption involving AiIndexEntry and AiAttribution.
61-65: Large JSON payloads may exceed command-line argument limits.Passing the serialized JSON via
-mcan fail if the payload grows large (many records). Most systems have anARG_MAXlimit (128KB–2MB). Consider using stdin viagit notes add -F -for robustness.♻️ Suggested improvement using stdin
public async writeCommitNote(commit: string, payload: NotesPayload): Promise<void> { const serialized = JSON.stringify(payload); - // Replace existing note. - await this.git.raw(['notes', '--ref', NOTES_REF, 'add', '-f', '-m', serialized, commit]); + // Replace existing note using stdin to avoid ARG_MAX limits. + await this.git.rawWithStdin(['notes', '--ref', NOTES_REF, 'add', '-f', '-F', '-', commit], serialized); }This requires adding a
rawWithStdinmethod to GitService that pipes input to the command.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@git-ai/src/ai/notes-store.ts` around lines 61 - 65, The current writeCommitNote in NotesStore serializes payload and passes it with -m which can hit ARG_MAX for large JSON; change writeCommitNote to call a new GitService.rawWithStdin method that pipes serialized JSON to git via stdin and uses git notes --ref NOTES_REF add -f -F - (or ['notes','--ref',NOTES_REF,'add','-f','-F','-']) instead of -m, and implement rawWithStdin on GitService to spawn the git process, write the provided string to stdin, handle stream/exit errors and return/throw on failures so writeCommitNote can await it.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@git-ai/src/ai/line-hash.ts`:
- Around line 12-15: The current hashLines implementation maps all input lines
through hashLine then filters hashes, which is ineffective because hashLine
always returns a digest; instead filter out blank/whitespace-only lines before
hashing so you don't produce SHA-256 of the empty string. Update hashLines to
first filter lines (e.g., line.trim().length > 0) and then map the remaining
lines with hashLine (referencing function hashLines and helper hashLine) so only
non-blank lines produce anchors.
In `@git-ai/src/commands/ai/AiExportCommand.ts`:
- Around line 59-60: The file write operation using fs.writeFile (writing
outFile with lines.join and lines.length) lacks error handling; wrap the await
fs.writeFile(...) call in a try/catch inside the same function (the
AiExportCommand export handler) and on error call console.error with a
user-friendly message that includes the outFile path and the error.message (or
error object), then exit or rethrow to surface failure (e.g., process.exit(1) or
throw) so write failures (permission, disk full) are handled gracefully.
In `@git-ai/src/commands/ai/AiImportCommand.ts`:
- Around line 54-56: The import command currently only warns when invalid lines
are skipped (in AiImportCommand where the code checks "if (bad > 0) {
console.warn(...); }") but does not set a non-zero exit status; update that
block to set a failure exit code (e.g., set process.exitCode = 1 or call
process.exit(1)) immediately after logging so CI/consumers detect the partial
failure. Ensure you modify the "if (bad > 0)" branch in AiImportCommand.ts (the
run/execute method handling import results) to both warn and set the non-zero
exit code.
In `@git-ai/src/commands/ai/AiInspectCommand.ts`:
- Around line 31-33: The recovery message in AiInspectCommand.ts currently
hardcodes "ai-git ai log"; update the two console.error lines that print the
recovery suggestion (the lines referencing id and resolved) to avoid the
hardcoded prefix—either compute the CLI prefix dynamically (e.g., derive from
process.argv[1] or a shared config/utility) and interpolate it into the
suggestion, or replace the hardcoded string with a generic, context-neutral
suggestion such as "git ai log" so the message matches how the user invoked the
command; ensure you modify the console.error that prints the recovery hint and
keep id/resolved interpolation intact.
In `@git-ai/src/core/GitService.ts`:
- Around line 12-18: The raw method is leaking value-bearing git arguments into
logs; update GitService.raw (around the this.git.raw call and logger.error) to
sanitize args before joining: detect flags that carry values (e.g., "-m", "-F")
and either remove their following argument or replace the flag's value with a
redaction token (e.g., "<REDACTED>"), and/or log only the subcommand (args[0])
plus sanitized args; then use the sanitizedArgs.join(' ') in the logger.error
call while keeping the original error thrown.
In `@git-ai/src/index.ts`:
- Around line 29-35: The banner always hardcodes "GIT-AI"; update the display
logic in the TTY banner block (the conditional using process.stdout.isTTY and
the banner constant) to compute the app name dynamically from the invocation
(e.g., derive a friendly name via path.basename(process.argv[1] or
process.argv[0]) and map/normalize it to "GIT-AI" or "ai-git" as appropriate),
then interpolate that computed name into the banner string instead of the
literal 'GIT-AI', and keep the rest of the banner formatting and console.log
call unchanged.
In `@git-ai/src/ui/AiExplorer.tsx`:
- Around line 53-56: The finally block currently uses "if (!alive) return;"
which returns from inside finally and can mask exceptions; change it to avoid
returning in finally by moving the alive check to guard the setLoading call
instead — e.g., replace the return pattern with "if (alive) setLoading(false);"
so the finally block simply conditionally calls setLoading based on the alive
flag (refer to the finally block that calls setLoading and the alive and
setLoading symbols in AiExplorer.tsx).
- Around line 117-125: The current slice rows.slice(0, 30) only shows the first
30 items while selected can be beyond that, so compute a sliding window around
selected (e.g., start = clamp(selected - 15, 0, Math.max(0, rows.length - 30))
and end = start + 30) and render rows.slice(start, end) instead; update the
isSelected test to compare (i + start) === selected and keep the same key and
Text rendering (references: rows.slice(0, 30), selected, Text, map callback) so
the highlighted item is always visible.
---
Nitpick comments:
In `@git-ai/src/ai/notes-store.ts`:
- Around line 100-112: Both listIndexForCommit and getRecord currently overwrite
the stored commit with the provided commit parameter, which can hide mismatches;
after calling readCommitNote(check readCommitNote) validate whether each entry's
commit (for listIndexForCommit: payload.index entries' commit; for getRecord:
the stored rec.commit) differs from the commit parameter and emit a warning via
the class logger (e.g., this.logger.warn) naming the function
(listIndexForCommit/getRecord), the id or index entry info, the stored commit
value and the requested commit; keep the existing behavior of returning the
result with commit set to the parameter but add the warning when they differ to
surface potential data corruption involving AiIndexEntry and AiAttribution.
- Around line 61-65: The current writeCommitNote in NotesStore serializes
payload and passes it with -m which can hit ARG_MAX for large JSON; change
writeCommitNote to call a new GitService.rawWithStdin method that pipes
serialized JSON to git via stdin and uses git notes --ref NOTES_REF add -f -F -
(or ['notes','--ref',NOTES_REF,'add','-f','-F','-']) instead of -m, and
implement rawWithStdin on GitService to spawn the git process, write the
provided string to stdin, handle stream/exit errors and return/throw on failures
so writeCommitNote can await it.
In `@git-ai/src/ai/schema.ts`:
- Around line 24-26: The createdAt field currently only ensures a non-empty
string; update its Zod validation to enforce an ISO 8601 timestamp by replacing
z.string().min(1) for the createdAt property with z.string().datetime() (or, if
you need timezone control, use z.string().refine(s =>
!Number.isNaN(Date.parse(s)), { message: 'createdAt must be a valid ISO 8601
datetime' })) so the schema's createdAt property is strictly validated; adjust
any tests or consumers expecting the looser format accordingly.
In `@git-ai/src/commands/ai/AiBlameCommand.ts`:
- Around line 89-96: The current annotation loop in AiBlameCommand uses the
first element of matches (matches[0].id) which can be nondeterministic when
multiple records share a line hash; update the logic in the loop that builds
annotated (referencing lineHashes, hashToRecord, matches) to deterministically
pick a record by sorting or selecting by a stable timestamp field (e.g.,
record.createdAt or commit date) before taking the id, so recordId is derived
from the earliest/desired createdAt-sorted candidate rather than insertion
order.
In `@git-ai/src/commands/ai/AiExplainCommand.ts`:
- Around line 60-69: The records array is typed as any[], losing type safety;
change it to a properly typed array such as (AiAttribution & { noteCommit:
typeof c })[] (or replace typeof c with the actual commit id type) and import
AiAttribution from your schema; update the declaration of records and any
related usages so that records.push({ noteCommit: c, ...rec }) only accepts
non-null rec (guard rec's nullability or narrow its type after the await from
store.getRecord), and ensure store.getRecord is treated as returning
AiAttribution | null in the loop that iterates commits and calls
store.listIndexForCommit and store.getRecord.
- Around line 89-104: The prompt assembly in AiExplainCommand.ts (the prompt
variable built from records and fileContent) can exceed model token limits for
large files or many AI attribution records; update the logic that prepares
records and fileContent before JSON.stringify and concatenation to truncate or
summarize them (e.g., keep only the most recent N records from records, or
collapse records to a brief summary, and trim fileContent to a token/character
limit or generate a short diff/summary) and/or add a lightweight
token-estimation step to ensure the final prompt fits model limits; apply these
changes where prompt is constructed so prompt uses the trimmed/summarized
records and fileContent.
In `@git-ai/src/commands/ai/AiExportCommand.ts`:
- Line 47: Replace the dynamic import of fs/promises in the AiExportCommand
module with a static top-level import and use that exported binding where fs is
referenced: remove the await import('fs/promises') statement and add a static
import for fs/promises at the top of the file so functions like the code that
writes the exported file in AiExportCommand (the place currently using the
dynamically imported fs) call the imported fs methods directly.
In `@git-ai/src/commands/ai/AiImportCommand.ts`:
- Around line 42-51: When iterating lines in AiImportCommand (the for loop that
calls JSON.parse and AiAttributionSchema.parse and upserts via
store.upsertAttribution), update the catch to log which line index/text failed
and include the parsing error; specifically capture the current index (or line
number) and the caught error, and write a clear processLogger or console.error
message referencing the failing line, the index, and the error before
incrementing bad so users can locate and fix invalid entries.
In `@git-ai/src/commands/ai/AiLogCommand.ts`:
- Around line 67-73: The loop fetches notes sequentially (for const commit of
commits { await store.listIndexForCommit(commit) }) which can be slow for large
limits; change to parallelize or batch the calls by mapping commits to promises
and using Promise.all (or processing in configurable batches) to call
store.listIndexForCommit concurrently, then flatten results and apply
matchesFilter to populate rows (keep references: commits,
store.listIndexForCommit, matchesFilter, rows).
In `@git-ai/src/commands/ai/AiNotesCommand.ts`:
- Around line 35-41: The code uses unsafe any for remotes and the find callback;
change remotes: any[] to a concrete remote type (e.g. RemoteWithRefs[] from
simple-git or your project's GitService remote type) and update the find
callback to (r: RemoteWithRefs) => r.name === remote; if the project doesn't
expose that type, add a small local interface (e.g. interface GitRemote { name:
string; refs?: Record<string,string> }) and use GitRemote[] and (r: GitRemote)
in the find. Also add the necessary import for RemoteWithRefs (or declare the
local interface) so the compiler can enforce proper typing for git.getRemotes
and the found variable.
In `@git-ai/src/commands/ai/AiScanCommand.ts`:
- Around line 48-54: The scan only captures the first match because it uses
diff.match(rule.pattern); update the loop over RISK_RULES in AiScanCommand to
iterate over all matches: for each rule, obtain a global RegExp (if rule.pattern
isn’t already global, construct one preserving flags plus 'g'), call
diff.matchAll(globalPattern), and for each match push a hit object (use rule.id,
rule.message and the match text like m[0]) into the hits array so every
occurrence is reported rather than only the first.
In `@git-ai/src/commands/ai/AiValidateCommand.ts`:
- Around line 50-61: The second schema check is redundant because
store.getRecord already returns null on schema failure; remove the
AiAttributionSchema.safeParse block (the parsed variable and its check) from
AiValidateCommand.ts and rely on getRecord's null check (rec) to mark bad and
log the error; keep the existing error message for missing/invalid payloads and
continue the loop, ensuring no other logic depends on parsed being present.
- Around line 10-14: The helper function toNum is duplicated across
AiValidateCommand, AiBlameCommand, AiExportCommand and AiLogCommand; extract it
into a single shared parse utility module (exporting function toNum(value:
string | undefined, fallback: number): number) and update each command to import
and use that exported toNum instead of the inline copy, removing the duplicated
implementations and adjusting imports/exports so TypeScript compiles cleanly.
In `@git-ai/src/commands/ai/index.ts`:
- Around line 30-39: The top-level help added via ai.addHelpText should mention
preserving notes across rewrites; update the message array passed to
ai.addHelpText to include a line advising users to run "git config --add
notes.rewriteRef refs/notes/git-ai" (or equivalent) so `refs/notes/git-ai` is
automatically rewritten during amend/rebase flows; edit the string block in the
ai.addHelpText call to add that single-line instruction near the notes/sharing
lines so users see setup and preservation together.
In `@git-ai/src/commands/InitCommand.ts`:
- Around line 134-136: The catch block in InitCommand.ts currently logs the
error via logger.error and prints a generic console.error message; update the
catch handler (the catch in the init flow where logger.error(...) and
console.error(...) are called) to include either the actual error summary (e.g.,
error.message or a trimmed stack) and the log file path in the console output so
users know where to look; modify the logger.error call to still record the full
error and change the console.error line to print "❌ Setup failed: <short error
message>. See logs at <logPath>" (use the existing log path/config variable used
by your app).
In `@git-ai/src/index.ts`:
- Around line 31-44: Extract the duplicated version string into a single
constant (e.g., VERSION) and use that constant both when building the banner
(the variable banner) and when calling program.version() so the value is defined
in one place; update any references (banner construction and program.version) to
reference the new VERSION constant to prevent drift.
In `@git-ai/tsconfig.json`:
- Line 33: Replace the current single tsconfig setup that removes "exclude":
["node_modules", "dist"] with a split config: create tsconfig.build.json
(extends the base tsconfig and keeps tests excluded so tsc --build/--project for
production builds doesn't emit test artifacts) and tsconfig.test.json (extends
base but includes test files for the test flow). Update the build/test npm
scripts to point to the appropriate config (e.g., tsc -p tsconfig.build.json for
build/prepare and tsc -p tsconfig.test.json or your test runner compile step for
tests) and ensure the base tsconfig or the original "exclude" remains as the
canonical source for editor tooling.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 4746b49d-1ec6-4f27-8a43-4724420e8630
📒 Files selected for processing (30)
git-ai/README.mdgit-ai/package.jsongit-ai/src/ai/__tests__/notes-rewrite.test.tsgit-ai/src/ai/__tests__/notes-store.test.tsgit-ai/src/ai/attribution.tsgit-ai/src/ai/line-hash.tsgit-ai/src/ai/notes-store.tsgit-ai/src/ai/query.tsgit-ai/src/ai/schema.tsgit-ai/src/cli/pr-command.tsgit-ai/src/commands/InitCommand.tsgit-ai/src/commands/ResolveCommand.tsgit-ai/src/commands/ai/AiBlameCommand.tsgit-ai/src/commands/ai/AiExplainCommand.tsgit-ai/src/commands/ai/AiExportCommand.tsgit-ai/src/commands/ai/AiImportCommand.tsgit-ai/src/commands/ai/AiInspectCommand.tsgit-ai/src/commands/ai/AiInteractiveCommand.tsgit-ai/src/commands/ai/AiLogCommand.tsgit-ai/src/commands/ai/AiNotesCommand.tsgit-ai/src/commands/ai/AiRecordCommand.tsgit-ai/src/commands/ai/AiScanCommand.tsgit-ai/src/commands/ai/AiValidateCommand.tsgit-ai/src/commands/ai/index.tsgit-ai/src/core/GitService.tsgit-ai/src/index.tsgit-ai/src/ui/AiExplorer.tsxgit-ai/src/ui/PRList.tsxgit-ai/src/ui/TreeUI.tsxgit-ai/tsconfig.json
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (2)
git-ai/src/ai/__tests__/notes-rewrite.test.ts (1)
58-63: Strengthen the amend-rewrite assertion by checking record identity.Line 60 confirms cardinality, but adding an explicit
idequality check makes this test validate true note carry-over, not just a single surviving record.Proposed patch
const idx2 = await store.listIndexForCommit(sha2); // If git notes rewrite is working, the note should have been copied. assert.equal(idx2.length, 1); + assert.equal(idx2[0].id, rec.id); const loaded2 = await store.getRecord(sha2, idx2[0].id); assert.ok(loaded2); assert.equal(loaded2!.prompt, 'amend rewrite');🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@git-ai/src/ai/__tests__/notes-rewrite.test.ts` around lines 58 - 63, The test currently asserts only that one note exists after amend-rewrite but not that it is the same record; update the assertions in the test around store.listIndexForCommit(sha2) and store.getRecord(sha2, idx2[0].id) to also verify that the record id matches the original note's id (compare idx2[0].id or loaded2.id against the original id captured earlier, e.g., the id from the first commit's index/loaded record) so the test validates true note carry-over rather than just cardinality.git-ai/scripts/run-tests.mjs (1)
26-33: Sort discovered test files for deterministic execution order.Line 26 currently preserves filesystem traversal order, which can vary across environments. Sorting before spawn keeps runs more reproducible.
Proposed patch
-const testFiles = await collectTestFiles(distDir); +const testFiles = (await collectTestFiles(distDir)).sort((a, b) => + a.localeCompare(b), +);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@git-ai/scripts/run-tests.mjs` around lines 26 - 33, Discovered test file order is non-deterministic; sort the array returned by collectTestFiles before spawning the test runner. Locate where testFiles is assigned/used (variable testFiles from collectTestFiles) and sort it (e.g., testFiles.sort()) before passing to spawn/process.execPath, so the spawn call always receives a deterministically ordered list of files.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@git-ai/src/commands/ai/AiBlameCommand.ts`:
- Around line 159-166: The sort comparator in the deterministic selection uses
localeCompare on recordMeta.get(...).createdAt which is not guaranteed to be
chronological; update the comparator in the sort for sorted (operating on
matches, recordMeta) to parse createdAt into a numeric timestamp (e.g.,
Date.parse(createdAt) or new Date(createdAt).getTime()) for both a and b, treat
invalid/missing dates as 0, compare the numeric timestamps (bTime - aTime for
newest-first), and keep the existing tie-breaker of returning
a.id.localeCompare(b.id) when timestamps are equal.
- Around line 66-99: The loop currently collapses metadata by entry.id so the
same record id on multiple commits overwrites earlier data; preserve (commit,id)
as a single identity: change recordMeta to key by a composite key (e.g.
`${commit}:${entry.id}`) and keep hashToRecord entries as {commit, id} (already
present), update pickDeterministicRecordId to accept/return that composite key
or the {commit,id} pair, and change annotated entries (and any downstream use
like printBlock/store.getRecord) to use the combined commit+id when fetching
record metadata via store.getRecord(commit, id) so you never lose which commit a
record came from.
- Around line 50-51: The split on fileContent is producing a trailing empty line
for files that end with a newline; before computing lines and mapping with
hashLine, trim a single terminal newline from fileContent (not full trim) so the
resulting lines array doesn't include a synthetic "" last element; update the
code that builds lines (the fileContent → lines split and the subsequent
lineHashes = lines.map(hashLine)) to first remove one trailing "\n" or "\r\n" if
present so displayed ranges and the hashLine correlations (see line-hash.ts
behavior) remain correct.
- Around line 147-150: The logged note fields (meta.prompt, meta.provider,
meta.model, meta.intent, etc.) are untrusted and can include ANSI/control
sequences that spoof/alter terminal output; sanitize them before logging by
adding a helper (e.g., sanitizeNote or stripAnsiAndControls) and use it wherever
you log note-derived strings: apply it to meta.prompt (after normalizing
whitespace), meta.provider, meta.model, meta.intent, and recordId, stripping
ANSI escape sequences and other control characters and truncating to a safe max
length; update the console.log lines in AiBlameCommand (the lines that reference
meta.prompt, meta.provider/meta.model, meta.intent, and recordId) to call that
sanitizer so only cleaned text is emitted.
In `@git-ai/src/commands/ai/AiScanCommand.ts`:
- Line 24: The command currently prefers staged (--cached) diffs even when a
user passed --commit; update the AiScanCommand flow that inspects staged changes
(the logic around the '--commit' option and the staged-diff check in the
run/execute path) so that if a commit value is provided (i.e., commit !== 'HEAD'
or user explicitly passed --commit) you generate the diff for that commit
instead of using --cached; only fall back to scanning staged changes when no
explicit commit was supplied. Locate the option definition ('-c, --commit
<sha>') and the staged-diff conditional (the block currently preferring
'--cached' around lines 35-44) and change the branching to prefer the requested
commit patch when commit is set, otherwise keep the existing staged-or-HEAD
behavior.
- Around line 53-57: The current loop in AiScanCommand scans the entire patch
text via diff.matchAll(toGlobal(rule.pattern)), which can flag removed lines;
change the scanning to only examine added content: extract the added lines from
the patch (lines starting with '+' but not file headers like '+++') or parse the
diff hunks to collect added hunks, join them into an addedText string, and then
run addedText.matchAll(toGlobal(rule.pattern)) to populate hits (keeping
rule.id, rule.message and sample from the match). Update the code around the
RISK_RULES loop (where diff.matchAll/toGlobal(rule.pattern)/hits.push are used)
to use the added-only text instead of the full diff.
---
Nitpick comments:
In `@git-ai/scripts/run-tests.mjs`:
- Around line 26-33: Discovered test file order is non-deterministic; sort the
array returned by collectTestFiles before spawning the test runner. Locate where
testFiles is assigned/used (variable testFiles from collectTestFiles) and sort
it (e.g., testFiles.sort()) before passing to spawn/process.execPath, so the
spawn call always receives a deterministically ordered list of files.
In `@git-ai/src/ai/__tests__/notes-rewrite.test.ts`:
- Around line 58-63: The test currently asserts only that one note exists after
amend-rewrite but not that it is the same record; update the assertions in the
test around store.listIndexForCommit(sha2) and store.getRecord(sha2, idx2[0].id)
to also verify that the record id matches the original note's id (compare
idx2[0].id or loaded2.id against the original id captured earlier, e.g., the id
from the first commit's index/loaded record) so the test validates true note
carry-over rather than just cardinality.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 28dd0e78-f50e-430c-a70c-91d3c6969694
📒 Files selected for processing (16)
.github/workflows/ci.ymlgit-ai/package.jsongit-ai/scripts/run-tests.mjsgit-ai/src/ai/__tests__/notes-rewrite.test.tsgit-ai/src/ai/line-hash.tsgit-ai/src/commands/ai/AiBlameCommand.tsgit-ai/src/commands/ai/AiExportCommand.tsgit-ai/src/commands/ai/AiImportCommand.tsgit-ai/src/commands/ai/AiInspectCommand.tsgit-ai/src/commands/ai/AiNotesCommand.tsgit-ai/src/commands/ai/AiScanCommand.tsgit-ai/src/commands/ai/AiValidateCommand.tsgit-ai/src/commands/ai/index.tsgit-ai/src/core/GitService.tsgit-ai/src/index.tsgit-ai/src/ui/AiExplorer.tsx
✅ Files skipped from review due to trivial changes (2)
- .github/workflows/ci.yml
- git-ai/src/ai/line-hash.ts
🚧 Files skipped from review as they are similar to previous changes (10)
- git-ai/package.json
- git-ai/src/commands/ai/index.ts
- git-ai/src/commands/ai/AiNotesCommand.ts
- git-ai/src/commands/ai/AiInspectCommand.ts
- git-ai/src/commands/ai/AiValidateCommand.ts
- git-ai/src/index.ts
- git-ai/src/core/GitService.ts
- git-ai/src/commands/ai/AiImportCommand.ts
- git-ai/src/commands/ai/AiExportCommand.ts
- git-ai/src/ui/AiExplorer.tsx
| export function buildAiScanCommand(): Command { | ||
| const cmd = new Command('scan') | ||
| .description('Heuristic scan for risky patterns in current staged diff (best-effort)') | ||
| .option('-c, --commit <sha>', 'Commit to scan diff for (default: HEAD)', 'HEAD') |
There was a problem hiding this comment.
Honor --commit instead of always preferring staged diff.
When staged changes exist, the current flow scans --cached and ignores the requested commit patch, which can produce results for the wrong target.
Suggested fix
- .option('-c, --commit <sha>', 'Commit to scan diff for (default: HEAD)', 'HEAD')
+ .option('-c, --commit <sha>', 'Commit to scan patch for (if omitted, scan staged diff and fallback to HEAD)')
@@
- const commit = (opts.commit ?? 'HEAD').trim();
+ const commit = opts.commit?.trim();
@@
- // Default: scan staged diff. If in git subcommand mode, users can still stage before scan.
- diff = await git.raw(['diff', '--cached']);
- if (!diff.trim()) {
- // Fallback: scan last commit patch.
- diff = await git.raw(['show', '--format=', commit]);
- }
+ if (commit) {
+ diff = await git.raw(['show', '--format=', commit]);
+ } else {
+ // Default: scan staged diff. If empty, fallback to HEAD patch.
+ diff = await git.raw(['diff', '--cached']);
+ if (!diff.trim()) {
+ diff = await git.raw(['show', '--format=', 'HEAD']);
+ }
+ }Also applies to: 35-44
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@git-ai/src/commands/ai/AiScanCommand.ts` at line 24, The command currently
prefers staged (--cached) diffs even when a user passed --commit; update the
AiScanCommand flow that inspects staged changes (the logic around the '--commit'
option and the staged-diff check in the run/execute path) so that if a commit
value is provided (i.e., commit !== 'HEAD' or user explicitly passed --commit)
you generate the diff for that commit instead of using --cached; only fall back
to scanning staged changes when no explicit commit was supplied. Locate the
option definition ('-c, --commit <sha>') and the staged-diff conditional (the
block currently preferring '--cached' around lines 35-44) and change the
branching to prefer the requested commit patch when commit is set, otherwise
keep the existing staged-or-HEAD behavior.
| for (const rule of RISK_RULES) { | ||
| for (const m of diff.matchAll(toGlobal(rule.pattern))) { | ||
| hits.push({ rule: rule.id, message: rule.message, sample: m[0] }); | ||
| } | ||
| } |
There was a problem hiding this comment.
Avoid scanning removed/context lines; limit scan to added content.
Scanning the full patch text can flag code that was deleted, so risk checks may fail even when a risky pattern is being removed.
Suggested fix
- const hits: { rule: string; message: string; sample: string }[] = [];
+ const scanText = diff
+ .split('\n')
+ .filter((line) => line.startsWith('+') && !line.startsWith('+++'))
+ .map((line) => line.slice(1))
+ .join('\n');
+
+ const hits: { rule: string; message: string; sample: string }[] = [];
for (const rule of RISK_RULES) {
- for (const m of diff.matchAll(toGlobal(rule.pattern))) {
+ for (const m of scanText.matchAll(toGlobal(rule.pattern))) {
hits.push({ rule: rule.id, message: rule.message, sample: m[0] });
}
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@git-ai/src/commands/ai/AiScanCommand.ts` around lines 53 - 57, The current
loop in AiScanCommand scans the entire patch text via
diff.matchAll(toGlobal(rule.pattern)), which can flag removed lines; change the
scanning to only examine added content: extract the added lines from the patch
(lines starting with '+' but not file headers like '+++') or parse the diff
hunks to collect added hunks, join them into an addedText string, and then run
addedText.matchAll(toGlobal(rule.pattern)) to populate hits (keeping rule.id,
rule.message and sample from the match). Update the code around the RISK_RULES
loop (where diff.matchAll/toGlobal(rule.pattern)/hits.push are used) to use the
added-only text instead of the full diff.
|
@copilot I’ll fix the issues mentioned by CodeRabbit. |
|
@copilot Verify each finding against the current code and only fix it if needed. In Verify each finding against the current code and only fix it if needed. In In Verify each finding against the current code and only fix it if needed. In Verify each finding against the current code and only fix it if needed. In Verify each finding against the current code and only fix it if needed. In |
- Strip trailing newline before splitting file content to avoid synthetic empty line - Use composite key (commit:id) for recordMeta to prevent metadata overwrite - Sanitize untrusted note fields (ANSI/control chars) before terminal output - Use numeric timestamps in sort comparator for deterministic record selection - Respect explicit --commit flag in AiScanCommand instead of always checking staged Agent-Logs-Url: https://github.com/BeyteFlow/git-ai/sessions/b0bd246a-0edd-485f-9eac-a96ba4764447 Co-authored-by: jaseel0 <225665919+jaseel0@users.noreply.github.com>
Agent-Logs-Url: https://github.com/BeyteFlow/git-ai/sessions/b0bd246a-0edd-485f-9eac-a96ba4764447 Co-authored-by: jaseel0 <225665919+jaseel0@users.noreply.github.com>
All 5 findings verified and fixed in
|
Summary
git aisubcommands: log, blame, inspect, record, export/import, explore, explain, validate, scan, notes.Why
notes.rewriteRefpreserves notes across rebases/cherry-picks/amends.Usage
git ai record --intent "refactor" --prompt "simplify parser" --path src/parser.ts --lines-from-filegit ai log -n 200 --model gemini-1.5-flashgit ai blame src/parser.tsgit ai inspect <id> --commit HEADgit ai notesNotes
git push origin refs/notes/git-aiandgit fetch origin refs/notes/git-ai:refs/notes/git-aigit config --add notes.rewriteRef refs/notes/git-aiSummary by CodeRabbit
New Features
Documentation
Chores