Commit graph

351 commits

Author SHA1 Message Date
chethanuk
72bfe7a872
docs(ci): pass untrusted PR fields via env to prevent script injection (#430)
The CI integration docs interpolated github.event.pull_request.title,
github.base_ref and github.head_ref directly inside shell run: blocks.
GitHub substitutes ${{ }} textually before the shell parses the line, so a
PR title or branch name containing shell metacharacters executes on the
runner of anyone who copies the snippet.

Hoist all three into env: mappings and reference them as shell variables,
matching action.yml:228-244 and the repo's own review rule at
internal/config/rules/rule_docs/github_workflows.md:6.

Applied identically to en, ja and zh; the three code blocks were
byte-identical before and remain so.
2026-07-22 10:49:13 +08:00
chethanuk
83dacc2725
docs(examples): OCR_LLM_MODEL is required, not optional (#431) 2026-07-22 09:14:39 +08:00
Shaurya Srivastava
c60e88650a
docs(pages): add CC-Switch proxy setup to Configuration (#429)
Some checks are pending
CI / test (push) Waiting to run
Deploy Pages / build (push) Waiting to run
Deploy Pages / deploy (push) Blocked by required conditions
Migrate the CC-Switch note removed from the README in #426 into the
docs Configuration page (en, zh, ja).

Signed-off-by: shaurya2k06 <shaurya2k06@gmail.com>
2026-07-21 19:11:48 +08:00
chethanuk
b00244926a
ci: cancel superseded CI runs on the same PR or branch (#425)
Every push to a PR started a new CI run while the previous one kept
running to completion on the self-hosted pool. Only the newest commit
matters, so the earlier runs were holding runners for results nobody
would read.

Adds a top-level concurrency group keyed on the PR number, falling back
to the ref for push-to-main. This follows the pattern OpenSandbox uses
across its CI workflows, and is byte-identical to the block already
running in ocr-review.yml, so it reuses the repo's existing idiom
rather than introducing a second one.

Left the other workflows alone deliberately. deploy-pages.yml already
sets cancel-in-progress: false, and release.yml must never cancel:
aborting mid npm-publish would leave the platform packages published
while the meta package's optionalDependencies reference versions that
were never pushed, and npm publish is not reversible.

Verified with actionlint v1.7.12 (clean across all four workflows) and
by exercising both paths live on a fork: a superseded pull_request run
cancelled in 38s, and a superseded push-to-main run cancelled while a
run from a commit without the block, on the same branch, stayed queued.

Closes #422
2026-07-21 19:01:51 +08:00
kite
90306cafb4
docs(readme): slim README by removing sections duplicated on docs site (#426) 2026-07-21 18:09:02 +08:00
kite
817ddf9403
docs(readme): link to docs site and collapse duplicated sections (#424)
Reduce the large content overlap between the README files and the docs
site (pages/src/content/docs). Add a Documentation section linking to
open-codereview.ai/docs, and collapse the Commands, Review Rules, and
Configuration Reference sections into one-line summaries plus links.
This makes the docs site the single source of truth for reference
content and cuts the multi-language maintenance burden.

Applied consistently across all five localized READMEs (en/zh/ja/ko/ru).
2026-07-21 17:12:31 +08:00
chethanuk
05bedc6f6a
fix(viewer): use [ocr] log prefix for consistency (#420)
renderTemplate logged its template-execution error with a [viewer] prefix
while the rest of the project logs under the [ocr prefix family, making
the line invisible to a grep '\[ocr' triage pass.

A census of bracketed prefixes across all Go sources shows [viewer] was
the only stdout prefix sharing no prefix with [ocr:

  "[ocr]          100 sites
  "[ocr session]    2 sites (internal/session) - deliberate sub-namespace,
                            left unchanged
  "[viewer]         1 site  - the outlier fixed here

Remaining bracketed literals are not log prefixes and are untouched:
[bug]/[low] are buildBadge() category/severity expectations and
[A][M][D][R][B][S] are statusBadge() file-status badges, both in
cmd/opencodereview tests.

Log text only: no test, parser, doc, or CI check consumes the string.
TestRenderTemplate_ExecutionError exercises this path but asserts only
the Content-Type header, so it passes unchanged.

Closes #415
2026-07-21 16:58:18 +08:00
chethanuk
9ab06166d4
refactor(agent,scan): share one 80% token threshold (#421)
tokenWarningThreshold = 0.80 is defined in internal/llmloop, but it is
unexported, so the four call sites outside that package could not reach
it and each hardcoded '* 4 / 5' instead. The 80% policy therefore had two
encodings that could silently drift apart.

Add llmloop.PromptTokenLimit and route all six sites through it:

  internal/agent/agent.go:594, 790
  internal/scan/agent.go:321, 555
  internal/llmloop/compression.go:99
  internal/llmloop/loop.go:440

loop.go:439's softLimit stays inline; it uses the separate 60%
tokenSoftThreshold, which has a single consumer and stays unexported.

Behaviour is unchanged. 0.80 in float64 is strictly above exact 4/5, so
for positive x the product never rounds below the exact value and
truncation lands on the same integer; verified over
x in [-5_000_000, 5_000_000] with zero divergences. The float form is
also the more robust of the two: x*4 overflows int64 above MaxInt64/4,
where the old expression silently returned 0 (i.e. no limit).

The <= 0 guards stay at the call sites. filterLargeDiffs/filterLargeScans
treat a non-positive limit as keep-everything, while the pre-flight gates
treat it as a ceiling that rejects every prompt, so the helper does not
special-case it.

Tests: TestPromptTokenLimit pins the arithmetic with hand-computed
expectations, and new boundary tests pin the threshold itself - with
MaxTokens=100 an exactly-80-token input is kept and an 81-token one
dropped. The pre-existing filter tests survive mutating the constant to
both 0.75 and 0.85; the new ones fail on both.

Closes #417
2026-07-21 16:57:28 +08:00
Max
2b08e0ecbd
docs(telemetry): explain why checkMetricErr intentionally ignores errors (#418)
* docs(telemetry): explain why checkMetricErr intentionally ignores errors

* style: remove trailing space and apply compact formatting
2026-07-21 16:08:44 +08:00
chethanuk
7c7032534e
fix(llmloop): scope async memory compression to each RunPerFile conversation (#395)
* fix(llmloop): scope async memory compression to each RunPerFile conversation

The Runner is shared by all concurrent per-file review goroutines, but it
held a single compressionMu/pendingJob slot for async memory compression.
With concurrency > 1 that shared slot caused four defects (#384):

1. Cross-file apply: tryApplyPendingCompression had no owner check, so
   file B could splice file A's rebuilt history into its own messages.
2. Cross-file cancel/replace: the warning-threshold paths canceled
   whichever file's job happened to be pending, surfacing spurious
   "context canceled" errors at the gateway; triggerAsyncCompression
   overwrote the slot unconditionally, wasting the superseded request.
3. Same-call start-then-cancel: the soft-threshold trigger fired before
   the new messages were appended, so an append that crossed the warning
   threshold canceled the job started microseconds earlier.
4. The pendingJob == nil fast-path read in addNextMessage was unlocked.

Fix: move the bookkeeping into a compressionState owned by each
RunPerFile call and thread it through trigger/apply/cancel. The
nil-pending gate is now an atomic check-and-set inside
triggerAsyncCompression under st.mu, and the async trigger moved to the
end of addNextMessage, gated on the post-append count sitting strictly
between the soft and warning thresholds. RunPerFile defers a cancel so
no job outlives its conversation. Aggregate token counters and warnings
stay Runner-level.

Intentional behavior deltas, all strictly safer: the async snapshot now
includes the just-appended round; an in-flight job is canceled when
RunPerFile returns instead of running up to 5 minutes orphaned; no async
job starts when the call is about to return false.

Verified: new regression tests (cross-file isolation via a channel-gated
fake client, owner-only summary apply with post-snapshot suffix
preserved, no start-then-cancel in one update, 4 concurrent RunPerFile
calls under -race); all existing compression tests updated to the
per-conversation API; full suite green with -race; coverage 81.1%.
E2E: 2-file concurrent review against a local OpenAI-compatible stub
with compression exercised — zero "context canceled", both files done.

Fixes #384

* docs(llmloop): fix stale cancel-order comment, note sync-compression retry path

Review feedback on #395: cancelPendingCompression cancels and then
clears pendingJob, both under st.mu — the old comment described the
reverse order. Also note in addNextMessage that a pre-append
compression failure is retried by the post-append check.
2026-07-21 15:42:35 +08:00
kite
d0caa8af66
feat(pages): add social preview meta tags and og-image (#414)
Some checks are pending
CI / test (push) Waiting to run
Deploy Pages / build (push) Waiting to run
Deploy Pages / deploy (push) Blocked by required conditions
Add description, Open Graph, and Twitter Card meta tags to the site's index.html, plus an og-image.png asset. Fixes link previews in messaging apps (iMessage, WeChat, Slack, etc.) showing no description or thumbnail when the site URL is unfurled into a card.
2026-07-21 14:33:27 +08:00
Frank Barrett
389a6e3fa3
fix(pages): scroll docs deep links on load (#413)
Read the routed fragment after markdown renders so direct links and later hash
changes reach their headings. Retry briefly for rendered content and cancel
stale attempts during navigation.

The routed-fragment effect and the in-content link handler share a single
scrollToFragmentWhenReady helper, so the rAF retry loop is defined once and the
click path gains the cancellation it previously lacked.

Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 13:34:24 +08:00
chethanuk
870fc6a4a9
feat(examples): add Gerrit CI integration example (Jenkins + Gerrit Trigger) (#401)
* feat(examples): add Gerrit CI integration for publishing review results

Adds examples/gerrit_ci following the gitflic_ci CI-glue pattern (#316):
a stdlib-only post_review.py that reads 'ocr review --format json' and
publishes summary, inline comments, suggestion blocks, and file-level
findings in ONE batched POST /a/changes/{change}/revisions/{rev}/review,
tagged autogenerated:opencodereview with notify=OWNER and
omit_duplicate_comments.

Decisions (validated against a live Gerrit 3.14 in Docker):
- Plain comments, bare line = end_line, no CommentRange: the range form
  {start,0,end,0} renders lines start..end-1 in the UI (end_character 0
  excludes the final line), so correct ranges would need file contents
  in CI. Verified via UI screenshots during E2E.
- Preemptive HTTP Basic auth (urllib's handler does not preempt),
  XSSI )]}' stripping, HTML-200 detected as config error, 400 batch
  fold-retry, 409 change-closed tolerated (defensive on modern Gerrit:
  label-free reviews post fine on closed changes), password scrubbed
  from all error output.
- Jenkins Gerrit Trigger as the reference integration; the script is
  trigger-agnostic via flags/env (Zuul/hook recipes in the README).
  Jenkinsfile always passes the injected patchset SHA (revision race).

45 table-driven stdlib-unittest tests, red-to-green TDD; E2E against
gerritcodereview/gerrit covering live post, unicode round-trip, dedup
re-run, and failure paths (401 exit 2 with scrubbed password).

* fix(examples/gerrit_ci): harden post_review.py per review findings

- Send Authorization via add_unredirected_header so Basic credentials
  cannot follow a redirect to another host (urllib forwards ordinary
  headers cross-host).
- Reject non-object JSON input cleanly instead of an AttributeError
  traceback; validate --timeout > 0 at argparse time.
- Warn on stderr when the 400-fallback folded summary is truncated, and
  report the fold accurately instead of claiming N inline comments.
- Pin drafts=KEEP in ReviewInput (depot_tools convention; old servers
  defaulted to deleting the caller's drafts).
- Jenkinsfile: fetch with an explicit dest refspec so
  origin/$GERRIT_BRANCH materializes under narrow-refspec clones;
  comment out extra_body thinking (OpenAI rejects unknown fields).
- Gitflic parity: optional positional input arg; single-sourced
  'current' revision default; scrub() skips sub-4-char passwords.
- README: document exit 1 and the defensive 409 branch; add gerrit_ci
  row to all five root READMEs (parity with the GitFlic example).
- Tests: 44 -> 55, covering stdin input, flag-over-env precedence,
  fold-retry failure, fold truncation, GERRIT_CHANGE_URL wiring,
  non-dict JSON, timeout validation, positional input.

* fix(examples/gerrit_ci): address OCR bot review on #401

- Jenkinsfile: resolve the LLM endpoint from the OCR_LLM_URL/TOKEN/MODEL
  env triple instead of `ocr config set`, so the auth token stays
  env-only and is never written to ~/.opencodereview/config.json on a
  shared agent (OCR_CONFIG_PATH is deliberately ignored by write paths,
  so it can't redirect the leak). Pin the npm install to a validated
  version. Document the config-file fallback (and its cleanup) for
  extra_body, which has no env equivalent.
- post_review.py: scrub the base64(user:password) Authorization value
  from error output too, not just the raw password — a proxy echoing the
  request header would otherwise leak decodable credentials. +1 test.

* feat(examples/gerrit_ci): scoped retry + robustness polish

Post-review hardening from an OSS-precedent study (depot_tools, kudu,
Gerrit REST docs):

- Bounded retry (3 attempts, exp backoff) in make_poster, scoped to the
  provably-safe failures only: HTTP 5xx and pre-response connection
  errors (refused/reset/DNS). Read-timeouts are deliberately NOT retried
  — a timeout is ambiguous (the server may have applied the review) and
  omit_duplicate_comments dedupes only inline comments, not the summary
  message, so a blind retry could post a duplicate change message. 4xx
  (400/401/404/409) propagate unchanged so main() classifies them as
  before.
- Document why plain comments are used, not robot_comments: the latter
  is deprecated since Gerrit 3.6, disabled-by-default in 3.12, and
  slated for removal; the tag already marks bot origin.
- Fold fallback: strip the '; N posted as inline comment(s).' clause
  from the reused summary so the folded message doesn't claim inline
  comments were posted and then explain they couldn't be placed.
- README: document the retry scoping and note fix_suggestions / label
  voting as intentional future options.

Tests: 56 -> 61 (5 retry cases: 5xx-then-ok, conn-err-then-ok,
5xx-exhaust, read-timeout-not-retried, 4xx/409-not-retried).
2026-07-21 13:25:30 +08:00
chethanuk
efac9ecc06
fix(llmloop): guard nil tool-call arguments map to prevent panic (#393)
* fix(llmloop): guard nil tool-call arguments map to prevent panic

Some OpenAI-compatible gateways emit "arguments": null for tool calls.
json.Unmarshal("null", &args) succeeds and sets the map to nil (JSON
null nils maps regardless of prior value), so the code_comment path
override (args["path"] = newPath) panicked with "assignment to entry
in nil map", killing the per-file subtask.

- internal/llmloop: parse arguments through a shared parseToolArgs
  helper that always returns a non-nil map, covering both the known-tool
  and dynamic-tool paths.
- internal/llm: the Anthropic history-replay path had the same hazard --
  null arguments reset the pre-initialized argsMap to nil, serializing
  tool_use input as JSON null, which the API rejects.

Fixes #382

* docs(llm): trim nil-args comment and cross-reference parseToolArgs

Review feedback on #393: the two null-arguments guards now reference
each other instead of sharing a helper; a 2-line guard does not justify
a cross-package export.
2026-07-21 13:24:50 +08:00
chethanuk
151cc7582e
docs(pages): document local-model setup, tool-calling requirement, and LLM timeouts (#400)
* docs(pages): add FAQ entry for local models without native tool calling

Two changes per locale (en/zh/ja), addressing #234:

- New "No tool calls parsed" entry under Configuration & startup: the
  symptom loop, the durable rule that the model must support native tool
  calling (deepseek-r1 narrates calls in content and can never work;
  qwen3 works), the Ollama tools-tag search link, and the maintainer's
  curl snippet to verify a model emits structured tool_calls without OCR
  in the loop.
- The existing "Max tool requests reached" entry (where users actually
  land) gains a 4th cause bullet cross-linking the new entry.

Anchors follow generateHeadingId (pages/src/utils/headingId.ts), the
site's actual slugger, and were verified against the rendered DOM in
all three locales. Code blocks are byte-identical across locales per
i18n convention; heading counts stay in parity.

* docs(pages): document Ollama custom-provider setup and LLM timeouts

Two additions per locale (en/zh/ja), addressing #234:

- Custom providers: a copy-paste Ollama example (127.0.0.1:11434/v1,
  protocol openai) with the note that custom providers require a
  non-empty api_key placeholder (resolver has no env fallback for them)
  and a pointer to the FAQ tool-calling rule.
- New Timeouts subsection: providers.<name>.timeout_sec /
  llm.timeout_sec / OCR_LLM_TIMEOUT, the 300s default, and the caveat
  that timeout_sec is not supported by 'ocr config set' (config_cmd has
  no timeout handling) so config.json must be edited directly.

The ja Timeouts heading is タイムアウト(Timeouts) so the site slugger
(which strips katakana) still yields a linkable #timeouts anchor.
Code blocks byte-identical across locales; heading parity kept.

* fix(pages): decode percent-encoded anchor fragments before id lookup

marked percent-encodes non-ASCII hrefs (#超时 renders as #%E8%B6%85%E6%97%B6),
but heading ids are raw text from generateHeadingId, so handleContentClick's
getElementById never matched for CJK anchors: same-page clicks silently
no-oped and cross-page anchor scrolls exhausted their retries at the top of
the page. This affected every pre-existing zh in-page anchor (e.g.
faq 复用已有的环境变量) as well as the zh links added for #234.

Decode the fragment (with a malformed-input guard) at both lookup sites.
2026-07-21 11:57:35 +08:00
JIA
d75f945b46
fix(codex): add native marketplace manifest (#402)
Some checks are pending
CI / test (push) Waiting to run
Deploy Pages / build (push) Waiting to run
Deploy Pages / deploy (push) Blocked by required conditions
* fix(codex): add native marketplace manifest

* fix(codex): unify marketplace entries
2026-07-20 22:13:50 +08:00
kite
3493658a87
docs(readme): update official website URL to open-codereview.ai (#410)
Point the logo link and official website link to the new custom
domain https://open-codereview.ai across all localized READMEs.
2026-07-20 19:47:14 +08:00
kite
750304d63a
feat(pages): serve landing site at custom-domain root with clean URLs (#407)
Migrate the landing site from the GitHub Pages subpath
/open-code-review/ to the root of the open-codereview.ai custom domain
and drop the /#/ from URLs:

- webpack publicPath -> '/' so assets load at the domain root
- switch HashRouter -> BrowserRouter for clean paths (e.g. /docs)
- fix HeroSection '#/docs' anchor to a router Link
- add public/CNAME (open-codereview.ai) for the GitHub Pages custom domain
- emit 404.html (copy of index.html) as SPA deep-link fallback
2026-07-20 16:12:26 +08:00
ckappgit
52081281a7
feat: add pot code review rules (#406) 2026-07-20 16:09:08 +08:00
kite
8e628f9e1c docs(pages): simplify page title to "Open Code Review" 2026-07-20 14:50:14 +08:00
ckappgit
a4a281c1f5
feat: add po code review rules (#404)
Some checks are pending
CI / test (push) Waiting to run
2026-07-20 14:20:34 +08:00
Shaurya Srivastava
41620dc723
docs(pages): document PowerShell install.ps1 on installation pages (#399)
Some checks failed
CI / test (push) Has been cancelled
Deploy Pages / build (push) Has been cancelled
Deploy Pages / deploy (push) Has been cancelled
Replace the Windows Release/NPM-only note with the install.ps1 one-liner
across en, zh, and ja docs.

Signed-off-by: shaurya2k06 <shaurya2k06@gmail.com>
2026-07-17 22:08:33 +08:00
chethanuk
5c6280c8c9
fix(diff): add --end-of-options guard and no-commit regression test for workspace diff (#376)
Some checks are pending
CI / test (push) Waiting to run
Deploy Pages / build (push) Waiting to run
Deploy Pages / deploy (push) Blocked by required conditions
Follow-up to #381, which normalized the argument ordering in
workspaceTrackedDiff (options -> positional ref -> --) but stopped short of
the --end-of-options guard. This adds it before HEAD in the first runGit
call, bringing workspace mode fully in line with the canonical range/commit/
merge-base calls in this file (git.go:119/126/256), which already require
git >= 2.24.

The --staged fallback call is untouched: it has no positional ref (only the
-- pathspec separator), so --end-of-options would guard nothing there.

Also documents why the --staged fallback is load-bearing (repos with no
commits have no HEAD, so `git diff HEAD` fails while `git diff --staged`
still surfaces staged changes against the empty tree) and pins it with
TestWorkspaceDiffNoCommitsUsesStagedFallback.

Closes #374
2026-07-17 20:29:51 +08:00
Shaurya Srivastava
3eda80ee5e
feat: add install.ps1 for Windows one-line install (#397)
* feat: add install.ps1 for Windows one-line install

Give Windows users the same checksum-verified one-liner experience as
install.sh, and document it next to the curl instructions.

Signed-off-by: shaurya2k06 <shaurya2k06@gmail.com>

* fix(install): harden arch detection and document pipe-to-shell risk

Clarify unsupported/empty architecture errors in install.ps1, and recommend
download-and-inspect as a safer alternative to curl|sh and irm|iex.

Signed-off-by: shaurya2k06 <shaurya2k06@gmail.com>

* fix(install.ps1): fail closed when PROCESSOR_ARCHITECTURE is empty

Do not assume AMD64 from Is64BitOperatingSystem — that is also true on
ARM64 Windows and would silently install the wrong binary.

Signed-off-by: shaurya2k06 <shaurya2k06@gmail.com>

---------

Signed-off-by: shaurya2k06 <shaurya2k06@gmail.com>
2026-07-17 20:12:40 +08:00
chethanuk
3b00867287
feat(pages): expand Integrations sidebar group by default (#394)
The Integrations group in the docs sidebar defaulted to collapsed,
hiding its child pages (Agent Skill, Command (Claude Code),
Delegation Mode, CI/CD) unless a visitor noticed the chevron or
deep-linked into a child page. Flip the initial expandedItems state
for 'sb-integrations' to true so the group renders expanded on load.

Manual toggling still works (toggleExpand flips per-id state) and the
auto-expand-on-active-child effect only ever sets true, so neither
conflicts with the new default.

Verified in a browser against the issue's acceptance criteria:
expanded on load, header click collapses/re-expands, deep link to a
child slug auto-expands, no sidebar layout regressions. npm run
typecheck && npm run build both pass (pages/ has no test harness).

Fixes #388
2026-07-17 20:05:40 +08:00
chethanuk
0b226fa693
fix(scan): check user include patterns before the extension allowlist (#378)
The scan path evaluated the built-in extension allowlist before user
include globs, so an explicit include (e.g. **/*.ftl) could never force
a non-allowlisted extension into a scan — while the preview/diff path
already checks user includes first. The two paths disagreed about which
files the same include selects.

Reorders whyExcluded in scan/agent.go to match preview.go exactly:
binary, user-exclude, user-include, extension allowlist, default
excluded paths. User excludes still take precedence over includes, and
binary/size guards still run regardless. Adds a regression test: a .ftl
file with a matching include glob now yields ExcludeNone (fails on the
previous ordering).

Related to #371
2026-07-17 16:38:21 +08:00
kite
c4de7ac32d
docs(pages): note that LLM API key is optional in delegation mode (#392)
Add a parenthetical to the quickstart prerequisites and a tip before
Step 2 so delegation-mode users know they can skip LLM configuration.
2026-07-17 16:36:19 +08:00
kite
86724c3d1a
docs(pages): simplify claude-code prerequisites, recommend delegation mode (#391)
Remove redundant auto-install hints (the command handles this
transparently) and replace the LLM prerequisite note with a tip
pointing users to Delegation Mode as a zero-config alternative.
2026-07-17 16:29:52 +08:00
Polly Labs
087807a314
docs: remove direct subprocess page (#390) 2026-07-17 14:08:25 +08:00
kite
17049fb1e3
docs(pages): remove integrations parent page, keep as sidebar group (#387)
Some checks are pending
CI / test (push) Waiting to run
Deploy Pages / build (push) Waiting to run
Deploy Pages / deploy (push) Blocked by required conditions
The integrations overview page added little value beyond navigation.
Remove its content and make the sidebar entry a non-navigable group
node that only expands/collapses its children.
2026-07-17 11:37:32 +08:00
Aarish Alam
d75e215a04
feat: add LiteLLM AI gateway provider preset (#385) 2026-07-17 11:10:40 +08:00
kite
2967f2a9cb
docs(pages): add Delegation Mode documentation to website (#386)
Add a new Delegation Mode page under Integrations in the docs site,
covering the ocr delegate subcommand workflow for subscription-based
AI coding agents (Claude Code, Codex, Cursor, Open Code, Qoder).

- New docs in en/zh/ja under integrations/delegate.md
- Register 'delegate' slug in docs index.ts
- Add sidebar entry in DocsPage.tsx
- Add i18n labels for all three languages
- Fix list-style-type reset caused by Tailwind Preflight in docs
2026-07-17 11:03:58 +08:00
kite
4ee453fd79
feat(delegate): add delegation mode for host-agent driven code review (#383)
Some checks are pending
CI / test (push) Waiting to run
* feat(delegate): add delegation mode for host-agent driven code review

Add `ocr delegate` subcommand that provides deterministic file selection
and rule resolution without calling any LLM. This enables AI coding agents
to perform reviews themselves using OCR only for engineering scaffolding
(preview which files to review, resolve grouped rules by path).

Includes:
- `ocr delegate preview` — outputs reviewable file list with mode/ref metadata
- `ocr delegate rule <path...>` — outputs review rules grouped by content
- Claude Code plugin command (delegate-review.md)
- Skill definitions for Claude Code, Codex, and Cursor
- Unit tests for internal/delegate package
- README documentation synced across all 5 locales

* fix(delegate): group rules by source, pattern and text

GroupRules keyed groups on rule text alone, so files sharing identical
rule text but resolved from different sources or matched by different
patterns were merged into one group that kept only the first file's
Source/Pattern metadata. Use a composite (source, pattern, text) key so
each group's provenance is accurate for every file it contains.
2026-07-16 13:10:54 +08:00
chethanuk
b3e321cb1e
feat(rules): add FreeMarker (.ftl/.ftlh/.ftlx) review support (#377)
Some checks are pending
CI / test (push) Waiting to run
Deploy Pages / build (push) Waiting to run
Deploy Pages / deploy (push) Blocked by required conditions
Adds the FreeMarker template extensions to the supported-file-type
allowlist and introduces a freemarker.md system rule layer (glob
**/*.{ftl,ftlh,ftlx}) covering SSTI (?new()/Execute/ObjectConstructor,
?eval/?api), output escaping vs the ftlh/ftlx auto-escape formats,
null/missing-value handling, logic-in-template smells, include/import
hygiene, and locale-sensitive formatting. Extends the allowlist and
system-rules tests and documents the new mapping in the en/ja/zh pages.

Closes #371
2026-07-16 11:36:04 +08:00
fuhui
43524cb157
refactor(diff): normalize workspace git argument ordering (#381) 2026-07-16 11:20:32 +08:00
Stefano Maffeis
2bf81bc1c1
docs(pages): add OpenAI Responses API to website i18n strings (#373)
Some checks are pending
CI / test (push) Waiting to run
Deploy Pages / build (push) Waiting to run
Deploy Pages / deploy (push) Blocked by required conditions
Update features.feat3Desc and docs.configManualCustomNote across all
three language files (en, zh, ja) to include the openai-responses
protocol added in PR #363.

Closes #372
2026-07-15 16:27:23 +08:00
Jinho Ayden Jeong
22b07fa4ce
feat(llm): add built-in Ollama Cloud provider preset (#375)
* feat(llm): add built-in Ollama Cloud provider preset

Add ollama-cloud to the built-in provider registry so users can select
Ollama Cloud directly instead of hand-configuring a custom provider.

The preset uses the OpenAI-compatible protocol against https://ollama.com/v1
with Bearer auth from OLLAMA_API_KEY, and ships gpt-oss:120b and gpt-oss:20b.

Runtime-verified against the live Ollama Cloud endpoint:
- ocr llm test: connectivity confirmed for both models
- ocr review: tool-calls flow works end-to-end on both models

Closes #305

* feat(llm): expand ollama-cloud model list to all 18 verified models

Query /v1/models on Ollama Cloud to populate the preset with every
available model instead of only gpt-oss. All 18 models were runtime-verified
to support the tools/tool_calls flow via /v1/chat/completions, and a
representative subset was confirmed end-to-end through ocr review.

Co-Authored-By: chethanuk <chethanuk@outlook.com>
2026-07-15 16:24:59 +08:00
fuhui
0ec3769d58
fix(diff): preserve non-ASCII paths (#365)
* fix(diff): preserve non-ASCII paths

Disable Git path quoting when generating diffs so parsed paths can be read back correctly. Add regression coverage for workspace, commit, and range modes.

* fix(diff): preserve untracked non-ASCII paths

Disable Git path quoting when listing untracked files and add regression coverage for non-ASCII workspace paths.
2026-07-15 14:26:25 +08:00
Lei Zhang
f58b0f2924
feat: Add OpenAI Responses API support and refactor protocol handling (#363)
* feat(llm): add OpenAI Responses API support

Add `openai-responses` as a third LLM protocol alongside `anthropic` and
`openai-chat-completions`, enabling code review via the OpenAI Responses
API (/v1/responses) for GPT-5.x / o-series models.

Protocol naming refactor (backward-compatible):
- Canonicalize "openai" -> "openai-chat-completions" (alias still accepted)
- Add NormalizeProtocol / ValidateProtocol / IsAnthropicProtocol helpers
- Registry uses canonical constants; resolver normalizes everywhere

New OpenAIResponsesClient (stateless replay, per DESIGN_STATE_CACHE_PHASE.md):
- system messages -> Instructions; tool calls -> function_call items keyed
  by CallID so the agent loop pairs results correctly
- store=false (privacy); prompt_cache_key = sha256(instructions)[:32]
- Phase fields (commentary/final_answer) dropped with TODO for gpt-5.3-codex+

Config plumbing:
- llm.protocol field + OCR_LLM_PROTOCOL env (priority over use_anthropic /
  OCR_USE_ANTHROPIC); TUI exposes all three protocols in Custom & Manual
- anthropic-vertex rejected with friendly "not yet implemented" message

Docs: protocol reference, config examples, env var table, and Responses API
notes (store=false caching caveat, cache key derivation, Phase TODO) updated
across en/zh-CN/ko-KR/ja-JP/ru-RU READMEs.

* refactor(llm): switch PromptCacheKey to precomputed scheme via ChatRequest.CacheKey

Replace per-turn sha256 computation inside buildResponsesParams with a
precomputed cache key that callers compute once per session and pass
through ChatRequest.CacheKey (json:"-"). The key now incorporates the
first user message alongside instructions, so different files under
review land in distinct cache buckets — the previous instructions-only
key was identical across all files.

Changes:
- ChatRequest gains CacheKey string field (json:"-", zero impact on
  Chat Completions / Anthropic clients which never read it)
- New llm.ComputeCacheKey helper: sha256(instructions + "\x00" +
  firstUser)[:32]
- responses_client.go: reads req.CacheKey directly, removes promptCacheKey
  function and first-user-message scanning
- loop.go: RunPerFile computes cacheKey once before the loop, reuses
  every turn
- All 8 remaining call sites (agent, scan, relocation, compression,
  llm_cmd) compute once at request construction
- Update PLAN_RESPONSES_SUPPORT.md and DESIGN_STATE_CACHE_PHASE.md to
  reflect the precomputed scheme
- Update tests: passthrough tests for client, dedicated TestComputeCacheKey

* refactor(llm): use canonical protocol name "openai" and UUID-based session ID for cache key

Two changes to maximize backward compatibility and simplify the design:

1. Protocol naming: revert ProtocolOpenAIChatCompletions value from
   "openai-chat-completions" back to "openai". Old config files with
   protocol: "openai" are now identical to what new configs write —
   zero behavioral difference. The alias direction in NormalizeProtocol
   is reversed: "openai-chat-completions" -> "openai" (for configs
   written during this branch's testing phase only).

2. Cache key: replace content-based sha256 hash (ComputeCacheKey) with a
   random UUID session ID. The agent loop generates one UUID per file in
   RunPerFile and passes it via ChatRequest.SessionID; the Responses
   client uses it as prompt_cache_key. Single-turn call sites no longer
   set a cache key (no multi-turn caching benefit). This removes the
   need to scan messages or compute hashes, and eliminates collision
   risk between files with similar content.

   ChatRequest.CacheKey is renamed to SessionID to reflect its actual
   semantic — a per-session identifier that the Responses client
   repurposes as prompt_cache_key.

Also updates PLAN_RESPONSES_SUPPORT.md, all 5 README translations,
test expectations, and promotes google/uuid to a direct dependency.

* refactor(llm): remove IsAnthropicProtocol helper and anthropic-vertex special case

* refactor(llm): remove openai-chat-completions branch-internal alias

* docs: remove DESIGN_STATE_CACHE_PHASE and PLAN_RESPONSES_SUPPORT design notes

* fix(llm): address code review findings on Responses API support

- provider_cmd: clear stale use_anthropic when switching to openai-responses
- resolver: validate preset protocol with ValidateProtocol for consistency
- responses_client: swap usage mapping to resolveUsage-first (matches OpenAIClient)
- responses_client: map failed/cancelled statuses to 'error' finish reason
- usage_resolver: add Responses API field paths (input_tokens, output_tokens,
  input_tokens_details.cached_tokens)
- add tests for all four fixes

* fix(llm): mirror use_anthropic when setting llm.protocol

- config_cmd: 'ocr config set llm.protocol' now mirrors use_anthropic
  (anthropic -> true, OpenAI family -> false) for backward compat with
  older binaries that predate llm.protocol
- provider_cmd: openai-responses now sets use_anthropic=false instead of
  nil, so older binaries fall back to the OpenAI family rather than
  wrongly defaulting to anthropic
- update tests for both write paths

* fix(llm): mirror protocol when setting llm.use_anthropic

- config_cmd: 'ocr config set llm.use_anthropic' now mirrors protocol
  (true -> anthropic, false -> openai) so the two fields never disagree,
  matching the reverse llm.protocol mirroring added previously
- without this, setting use_anthropic=true while protocol=openai-responses
  left a contradictory config that misled older binaries into using the
  anthropic protocol against an OpenAI endpoint
- extend tests to cover both values and stale-protocol overwrite

* docs(llm): fix NormalizeProtocol comment to match lowercasing behavior

The comment claimed unknown values are 'returned unchanged', but the
default branch lowercases and trims them (corroborated by the
'gRPC -> grpc' test). Update the wording to describe the actual
behavior so callers aren't misled about round-trip fidelity.

* docs(llm): drop OpenAI Responses API implementation notes from READMEs

* fix(llm): address code review findings on Responses API support

- config: preserve openai-responses when setting llm.use_anthropic=false
  (only mirror to openai when protocol is unset or a legacy anthropic/openai)
- config: add Protocol values guidance to unknown-key error message
- protocol: extract normalized local var in NormalizeProtocol
- responses_client: align SDK base URL trimming with NewOpenAIClient
- responses_client: drop unused test-only sdkBaseURL method

* fix(llm): use protocol constants consistently

- providers: edenai now uses ProtocolOpenAIChatCompletions like the rest
  of the registry instead of the "openai" string literal
- provider_cmd: print the normalized protocol variable (what is actually
  saved) instead of the raw TUI value

* fix(llm): drop stream key and surface non-completed status in Responses client

Address two PR review comments on OpenAI Responses API support:

1. extra_body.stream=true was forwarded to Responses.New, making the API
   return SSE while the SDK expects JSON and breaking every call. Skip the
   'stream' key (like OpenAIClient treats it as a non-forwarded key) while
   still forwarding other extra_body entries.

2. The Responses API returns HTTP 200 even for failed/cancelled (terminal)
   and queued/in_progress (background) states, so the SDK reports nil error.
   Surface these as real errors so callers branching on err != nil (ocr llm
   test, review loop) fail instead of treating a dead response as success.

Add table-driven tests covering both fixes.
2026-07-15 12:54:52 +08:00
xujiejie
a32f85272b
feat(telemetry): propagate W3C traceparent from parent process (#352)
Some checks failed
CI / test (push) Has been cancelled
- Add ContextWithTraceParentFromEnv to extract TRACEPARENT env var and
  inject upstream span context via OTel TextMapPropagator.
- Register TraceContext+Baggage composite propagator in Init().
- Wire trace parent propagation into review and scan entry points.
- Add tests for valid, absent, disabled, and malformed TRACEPARENT.
2026-07-14 11:28:09 +08:00
Abhiram V
b12649c0a4
fix: remove redundant font-family from .response-text .inline-code (#339)
* fix: remove redundant font-family from .response-text .inline-code

* style: remove trailing whitespace and blank line from .inline-code
2026-07-14 10:20:19 +08:00
zhouzhihao
50e5a17293
fix(llm): support opt-in OpenAI streaming (#359)
Some checks are pending
CI / test (push) Waiting to run
2026-07-13 19:38:19 +08:00
V. D'AGOSTINO
242616544c
fix(background-file) Manage path at git repository root directory Refs:#324 (#327)
Some checks are pending
CI / test (push) Waiting to run
2026-07-13 11:27:54 +08:00
makoMakoGo
22782aa66e
fix(plugin): isolate Claude Code package (#350)
Some checks failed
CI / test (push) Waiting to run
Deploy Pages / build (push) Has been cancelled
Deploy Pages / deploy (push) Has been cancelled
* fix(plugin): isolate Claude Code package

* docs(plugin): point guides at Claude root
2026-07-12 14:02:23 +08:00
kite
c3da878850
fix(pages): widen blog detail content area with percentage-based max-width (#356)
Some checks are pending
CI / test (push) Waiting to run
Deploy Pages / build (push) Waiting to run
Deploy Pages / deploy (push) Blocked by required conditions
2026-07-11 10:46:47 +08:00
kite
4cc2fe8568
feat(pages): add blog section with i18n support (#355)
Some checks are pending
CI / test (push) Waiting to run
Deploy Pages / build (push) Waiting to run
Deploy Pages / deploy (push) Blocked by required conditions
Add a blog feature to the pages site including:
- BlogPage component with list/detail views, tag filtering, search, and TOC
- Blog content system with markdown posts and i18n (en/ja/zh)
- Navbar integration with blog tab and improved active state detection
- MarkdownRenderer image path handling for relative/absolute paths
- Webpack CopyPlugin for serving static blog assets
2026-07-10 22:55:53 +08:00
Victor M. SMITH
802af6b461
feat(llm): add built-in Eden AI provider preset (#346)
Some checks are pending
CI / test (push) Waiting to run
Eden AI (https://www.edenai.co) is an OpenAI-compatible aggregator that exposes 100+ models from multiple providers through a single EU-hosted endpoint and API key.

Registers it as a built-in provider preset (protocol openai, base URL https://api.edenai.run/v3, key via EDENAI_API_KEY), mirroring the existing OpenAI-compatible presets. Models use Eden AI's provider/model naming. Updates the provider registry tests accordingly.

Signed-off-by: Victor M. SMITH <72023257+MVS-source@users.noreply.github.com>
2026-07-10 16:57:49 +08:00
kite
d191862b95
fix(llm): make OCR_LLM_EXTRA_HEADERS apply to all resolver strategies (#353)
Previously, OCR_LLM_EXTRA_HEADERS was only parsed inside the tryOCREnv
strategy, so extra headers set via the environment variable were ignored
when the endpoint was resolved through config-file providers or other
strategies. Move the parsing into the global resolution loop so the env
var acts as a universal override, merging into whatever headers the
winning strategy already provides (env values take precedence on
conflict).
2026-07-10 16:52:59 +08:00
kite
88faa5775e fix(vscode): add PNG icon for marketplace display and bump to v0.1.1
VS Code Marketplace requires PNG format icons — SVG icons are ignored,
causing the extension to show a default placeholder avatar.
2026-07-10 15:20:22 +08:00
kite
37ac8658dc
chore(vscode): prepare extension for marketplace publishing (#349)
- Update repository URL to alibaba/open-code-review
- Add LICENSE file for marketplace compliance
- Exclude __mocks__ from VSIX package
- Add *.vsix to .gitignore and remove tracked VSIX binary
2026-07-10 11:36:39 +08:00