21 Commits

Author SHA1 Message Date
kite 2f3ed41009 refactor(agent): split agent.go into util.go and compression.go, fix compression bugs
Extract utility functions (stripEmptyPlanBlock, stripMarkdownFences,
buildMessageXML, copyMessages, countMessagesTokens, reviewModeString,
detectGitBranch) into util.go and all compression logic into compression.go
to improve readability (agent.go: 1489 → 1114 lines).

Fix pre-existing compression bugs from commit 14adf6f:
- Return unmodified messages on compression failure instead of truncating
  to frozen zone (which discarded all conversation context)
- Check error in async compression goroutine to avoid applying failed results
- Track snapshot length to append post-snapshot messages when applying async
  compression, preventing silent message loss
- Strip old <previous_review_summary> before appending new one to prevent
  cumulative frozen zone bloat across multiple compressions
- Guard pendingJob cleanup to only clear when it matches the completed job,
  preventing accidental clearing of a newly scheduled job
- Add context.Context and 5s timeout to detectGitBranch
- Deep copy ToolCalls in copyMessages to match session/history.go
- Reuse finalCount in addNextMessage to avoid redundant token counting
2026-06-21 21:20:40 +08:00
ZheNing Hu 283ec8558c fix(diff): detect renamed and deleted files correctly in diff parsing (#105)
When a file was renamed on the target branch, ocr review emitted
'[ocr] WARNING: cannot read file <old path> at ref <to>: exit status 128'.

Two compounding bugs:

1. The parser required 'a/'/'b/' prefixes when matching '--- /dev/null' /
   '+++ /dev/null', but git emits these lines without prefixes, so
   IsNew/IsDeleted were never set and deleted files fell through to a
   doomed 'git show ref:<old path>'.

2. 'rename from' / 'rename to' extended headers were never parsed, and
   git diff/show call sites did not force rename detection, so renames
   degraded to delete+add whenever the user had diff.renames=false.

Fixes:
- Parse 'rename from'/'rename to', 'new file mode', 'deleted file mode'
  and unprefixed /dev/null markers in ParseDiffText.
- Pass --find-renames to all git diff/show invocations so rename
  detection no longer depends on user config.
- Add IsRenamed to model.Diff (json: is_renamed) and prefer it in
  diffStatus.
- Add parser unit tests and a range-mode rename regression test.

Fixes #99
2026-06-14 22:29:02 +08:00
Daniel Grießhaber c2c7c37e0f fix(agent): user includes should have higher priority than system supported extensions (#51) 2026-06-12 14:29:47 +08:00
kite 01c9c85456 feat: add review filter to remove provably incorrect comments after subtask
- Add REVIEW_FILTER_TASK template and LLM conversation config
- Implement executeReviewFilter to post-process comments per file
- Add CommentsForPath and RemoveByPathAndIndices to CommentCollector
- Apply timeout from template config to review filter LLM call
- Await CommentWorkerPool before running filter to prevent race condition
- Include raw response preview in filter JSON parse error logs
2026-06-09 20:41:42 +08:00
kite ef46dfdac9 feat(tool): add global git subprocess concurrency limiter and propagate context.Context to diff layer
Introduce gitcmd.Runner with channel-based semaphore to cap concurrent
git subprocesses (default 16, configurable via --max-git-procs). Route
all tool-layer and diff-layer git calls through the shared runner.
Also fix diff.Provider.runGit lacking context.Context — now the full
chain (GetDiff → MergeBase → ParseDiffText → finalizeDiff) propagates
the caller's context for proper cancellation and timeout support.
2026-06-06 20:20:34 +08:00
kite 363d971916 fix(tool): make Registry and DiffMap concurrency-safe via freeze semantics
Registry was a plain map alias and DiffMap a shared map[string]string,
both relying on implicit write-before-read ordering with no compile-time
safety. Refactor Registry into an encapsulated struct with Freeze() that
panics on post-freeze writes, and wrap DiffMap as an immutable value type
with defensive copy on construction. Move DiffMap injection to before
filterDiffs to preserve the original behavior of exposing all file diffs.
2026-06-06 19:41:11 +08:00
kite 08bb28ced5 feat(llm): enable Anthropic prompt caching and fix token accounting
Add cache_control ephemeral breakpoints on system prompt and tool
definitions to activate Anthropic prompt caching for multi-turn
agent loops. Fix token double-counting by removing the redundant
totalTokensUsed accumulator and computing it from sub-items. Surface
cache read/write statistics in both text and JSON summary output.
2026-06-06 19:26:34 +08:00
kite 828fc6cae7 fix(tool): propagate context.Context to all tool providers to enable subprocess cancellation
Add ctx parameter to Provider.Execute interface and propagate it through
FileReader.Read, code_search.gitGrep/runGitGrep, and file_find.listGitFiles.
This ensures parent task cancellation/timeout properly terminates running
git subprocesses (grep, show, ls-files, ls-tree). Also adds 10s timeout
to file_find and fixes context.Canceled handling in code_search.
2026-06-06 19:26:34 +08:00
kite 8e9772baa8 fix(llm): add context support to StreamCompletion and memory compression to prevent infinite timeouts
StreamCompletion used http.NewRequest without context and retry with
context.Background(), making streaming calls uncancellable. Memory
compression called Completions() without context, leaving async
goroutines unable to be cancelled or timed out.

- Add StreamCompletionWithCtx to LLMClient interface and both
  OpenAI/Anthropic implementations
- Propagate context through runCompression, addNextMessage, and
  triggerAsyncCompression call chain
- Add 5-minute timeout cap on async compression goroutines
- Cancel in-flight compression HTTP requests when job is superseded
2026-06-06 13:22:32 +08:00
kite 0ac1193040 feat(session,viewer): record review mode and diff refs in session
Store ReviewMode, DiffFrom, DiffTo, and DiffCommit in SessionHistory
and persist them to JSONL (conditionally, only when non-empty). Display
mode in viewer list page and show version details (from/to or commit)
on the session detail page based on review mode.
2026-06-05 09:44:33 +08:00
kite e48b132ebf fix(tool,agent): parse git grep output correctly in range mode and retry on empty results
code_search used SplitN(":", 3) to parse git grep output, but in
range/commit mode git grep prefixes each line with the ref
(ref:file:line:content). All matches silently failed Atoi on the
filepath field, returning an empty string that caused the review
session to terminate immediately.

Fix code_search to detect the ref prefix and adjust split/index
accordingly. Also replace the hard stop on empty tool results with
a retry loop (up to 3 consecutive empty rounds) so the LLM can
recover or call task_done gracefully.
2026-06-04 23:43:26 +08:00
Coldaine a53b16cff5 fix(agent): stop leaking {{plan_guidance}} into MAIN_TASK prompts (#33)
Replace the hard-coded Chinese-only empty-plan branch with a compiled
regex + helper that strips the "### Review Plan …" wrapper (any language
variant) when the plan phase produces no output.

TWO BUGS fixed:
- The wrapper was only stripped for the Chinese literal `### 审查计划`,
  not the actual English `### Review Plan (Optional)` shipped in the
  template, so `{{plan_guidance}}` leaked into the rendered prompt on
  every review where the plan phase was skipped or failed.
- The strip ran AFTER `ReplaceAll("{{plan_guidance}}", "")`, so the
  token was already gone and the regex couldn't match the wrapper.
  Fixed ordering: strip BEFORE replace.

Also simplifies the regex alternation per Copilot review feedback:
`Review Plan` and `审查计划` already subsume the `(Optional)`/`(可选)`
suffixes because the trailing `[^\n]*` consumes them.

Adds unit tests for English, Chinese, and integration (production flow).

Co-authored-by: Copilot <copilot-pull-request-reviewer@github.com>
2026-06-04 14:11:31 +08:00
Lei Zhang 482de7ac52 fix: skip binary review (#28)
* fix(agent): skip binary files during code review

Previously, binary files were passed through the review pipeline because
shouldReview only checked path/extension rules. This caused the agent to
incorrectly attempt reviewing binary content.

- Add ExcludeBinary reason to the exclusion filter
- Change shouldReview/whyExcluded to accept model.Diff instead of path
  string, enabling access to the IsBinary flag
- Check IsBinary as the first gate in whyExcluded
- Differentiate skip log messages for binary vs path-filtered files

* test: add unit tests for preview.go

* fix: normalize dash to em-dash in binary file skip log message

* test(agent): strengthen UserIncludePattern tests to cover additive semantics

- Document that include patterns are additive, not exclusive
- Add cases verifying include bypasses default-path exclusion
- Add cases for extension filter precedence over include logic
- Add TestWhyExcluded_IncludeBypassesDefaultPath for rescue semantics
- Add TestWhyExcluded_IncludeAndExcludeInteraction for priority order
2026-06-03 18:01:30 +08:00
kite f363702a15 feat(agent): integrate session recording and token tracking for re-location task
Re-location LLM calls were invisible in session history and their token
  consumption was unaccounted for. Refactor the code_comment handling path
  to record re-location requests/responses into session JSONL, accumulate
  token usage into global counters, and display them in the viewer.

  Also fixes: async context cancellation risk (WithoutCancel), duplicate
  telemetry recording, missing timeout enforcement, missing {existing_code}
  placeholder in the re-location prompt, and renames Parse to ParseComments
  for clarity.
2026-05-30 23:25:12 +08:00
kite e71fa597d8 feat(review): add --preview flag to show files before running LLM review 2026-05-26 23:06:47 +08:00
kite db5bc4b8a9 feat(rules): add include/exclude file filter support in rule.json
Allow users to configure include/exclude glob patterns in
  .open-code-review/rule.json (global or project-level) to control
  which files are reviewed. Exclude has highest priority, include
  penetrates default path exclusions but not the extension allowlist.
  Only one layer takes effect following --rule > project > global priority.
2026-05-25 20:19:02 +08:00
kite d44fd31989 feat(rules): unify rule resolution behind Resolver interface with four-layer priority
Consolidate review rules into a composedResolver that supports four layers
  (--rule flag > project .open-code-review/rule.json > global ~/rule.json >
  embedded system default), each with first-match-wins fall-through semantics.
2026-05-25 17:45:53 +08:00
kite 35424d18bf feat(config): add path exclusion patterns for file filtering
This change introduces a new configuration mechanism for excluding specific file paths during code reviews. It adds support for glob patterns including recursive directory matching (**), single-segment wildcards (*), and brace expansion ({a,b}), allowing more flexible control over which files should be reviewed or skipped based on their location within the repository.
2026-05-23 00:11:35 +08:00
kite 63d6108db0 fix(agent): Update file counting logic for accurate review metrics
This change refines how file counts are tracked during the agent's review process. It now correctly excludes deleted files when calculating the number of files being reviewed, ensuring telemetry and error reporting reflect actual processing rather than total diffs. This improves accuracy in monitoring and debugging scenarios.
2026-05-22 22:28:52 +08:00
kite f054bb799e fix: propagate LLM API errors instead of showing misleading "Looks good to me"
When all subtask LLM calls fail (e.g. invalid API key), dispatchSubtasks
now returns an error instead of empty comments with nil error. Partial
failures are recorded as subtask_error warnings so output functions can
distinguish "no issues found" from "review failed".
2026-05-22 15:28:26 +08:00
kite 7c8b8562aa feat: init 2026-05-20 22:03:52 +08:00