Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
1ca428f
chore(dev): bump to 0.11.3-dev
huhamhire Jul 28, 2026
03a1fc1
ci(pages): rebuild the website when the changelog changes
huhamhire Jul 28, 2026
4dbe993
fix(agent): surface the real LLM failure cause and hint an unavailabl…
huhamhire Sep 7, 2026
a30dc5c
Merge pull request #220 from huhamhire/fix/llm-error-cause
huhamhire Sep 7, 2026
f39375a
feat(pr): reconcile remote state after merge and review verdicts
huhamhire Sep 7, 2026
45ca431
fix(gui): never leave the window blank on a renderer failure
huhamhire Sep 7, 2026
b7401fb
Merge pull request #221 from huhamhire/feat/post-action-refresh
huhamhire Sep 8, 2026
1facdc8
feat(proxy): let hosts bypass the proxy via a configurable no_proxy list
huhamhire Sep 8, 2026
c4f5c00
Merge pull request #222 from huhamhire/feat/no-proxy
huhamhire Sep 8, 2026
defb4a0
feat(comments): render @mentions as pills instead of running text
huhamhire Sep 8, 2026
b06eb97
Merge pull request #223 from huhamhire/feat/mention-pill
huhamhire Sep 8, 2026
69b62a8
fix(gui): contain a failed lazy chunk instead of taking down the app
huhamhire Sep 8, 2026
195aa96
Merge pull request #224 from huhamhire/fix/lazy-chunk-boundary
huhamhire Sep 8, 2026
3ead85a
chore(pragent): upgrade the embedded runtime to pr-agent 0.45.0
huhamhire Sep 8, 2026
91702d0
fix(pragent): correct the describe file walkthrough and stop quoted h…
huhamhire Sep 8, 2026
c79adc4
Merge pull request #225 from huhamhire/chore/pragent-0.45
huhamhire Sep 8, 2026
49dd713
chore(deps): upgrade monaco-editor to 0.56.0
huhamhire Sep 8, 2026
3ecd1f6
Merge pull request #226 from huhamhire/chore/monaco-0.56
huhamhire Sep 8, 2026
e8732e2
fix(chat): anchor the commit divider to the boundary, not to the end …
huhamhire Sep 8, 2026
e34c8a8
Merge pull request #227 from huhamhire/fix/chat-commit-divider
huhamhire Sep 8, 2026
154bd44
feat(comments): reference a comment into the agent conversation
huhamhire Sep 8, 2026
006f5e1
feat(comments): turn the agent answer into a draft reply to the comment
huhamhire Sep 8, 2026
3a9e1ef
fix(comments): align the reference button height with its sibling act…
huhamhire Sep 9, 2026
1151211
Merge pull request #228 from huhamhire/feat/ask-agent-about-comment
huhamhire Sep 9, 2026
c045063
fix(describe): drop the blank card and keep diagrams left-to-right
huhamhire Sep 9, 2026
d67de0f
Merge pull request #229 from huhamhire/fix/describe-0.45-regressions
huhamhire Sep 9, 2026
674b585
chore(release): 0.12.0
huhamhire Sep 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,23 @@ name: Deploy Website (GitHub Pages)

# Brand website (website/, VitePress). Decoupled from the v* release pipeline:
# build on every PR touching the site/docs (as a check), deploy on push to master.
# CHANGELOG.md / CHANGELOG.zh-CN.md are included because the site changelog is synced from them (see
# website/scripts/sync-docs.mjs) — a release merges the changelog to master and must rebuild the site.
on:
push:
branches: [master]
paths:
- 'website/**'
- 'docs/**'
- 'CHANGELOG.md'
- 'CHANGELOG.zh-CN.md'
- '.github/workflows/pages.yml'
pull_request:
paths:
- 'website/**'
- 'docs/**'
- 'CHANGELOG.md'
- 'CHANGELOG.zh-CN.md'
- '.github/workflows/pages.yml'
workflow_dispatch:

Expand Down
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,33 @@
All notable changes to this project are recorded here. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and the versioning follows [Semantic Versioning](https://semver.org/).

## [0.12.0] - 2026-09-09

### ✨ Added

- A comment can now be handed to the agent to investigate: click the reference button on any comment and ask about it, and the agent gets the comment, its author and the code it points at, then checks the claim against the actual code. Its answer can be turned into a draft reply to that comment in one click, editable before it goes out.
- The code editor is now Monaco 0.56.0 (from 0.55.1), which also drops the outdated sanitizer copy behind most of the open dependency advisories.
- The bundled review engine is now pr-agent 0.45.0 (from 0.39.0). Two long-standing local fixes are no longer needed — a single-line file change is rendered correctly upstream now, and a binary file no longer has to be worked around — and its YAML handling is more tolerant of imperfect model output.
- Mentions now render as a pill instead of blending into the surrounding text, so it is obvious at a glance when someone is named — on the activity page, in the inline diff comments, in drafts, and in the PR description alike.
- Proxy settings now take a list of **direct connections**: hosts that bypass the proxy and connect straight out, so an internal code platform, its git remote, or a self-hosted model stays reachable while everything else still goes through the proxy. Uses the familiar `NO_PROXY` syntax (a domain covers its subdomains), and applies to every outbound path at once — REST, git and the LLM call.
- A review that fails because the model is unavailable now says so and tells you what to do — with a local CLI provider (claude / codex) the model comes from that CLI's own configuration, so it has to be changed there.

### 🔧 Fixed

- A generated PR description no longer ends with a blank card — an invisible marker the review engine now stamps into its output was being treated as a section of its own.
- Diagrams in a generated PR description are laid out left-to-right again, instead of turning tall and top-down once they get past a few steps and pushing the rest of the result off screen.
- The commit divider in the review timeline now stays where the commit boundary actually is, instead of sliding further down every time a new message appears below it.
- The file list in a generated PR description now shows the real number of added and removed lines per file, instead of `+-1/--1`.
- Links in that file list now open the file instead of pointing at a non-existent line, so clicking through works.
- A PR description ending in a git merge tail (`# Conflicts:` and the file lines under it) no longer breaks up the generated description — those lines are part of the quoted description, not headings of their own.
- A part of the interface that loads on demand — the diff editor, a comment's inline code context — no longer takes the whole app down with it when it fails to load; the failure now stays inside that pane. If it failed because the app was updated or rebuilt while the window was open, it says so and offers to reload, which is the only thing that actually helps in that case.
- A failed review now shows the provider's actual error instead of only "all fallback models failed" — the real cause (an unavailable model, an expired login, an exhausted quota) was previously swallowed and never reached the run card.
- A local CLI provider that exits successfully but returns an empty reply is now reported as a failure naming that cause, rather than as an unexplained LLM failure.
- A merged PR now leaves the list on its own shortly after you merge it, instead of lingering until the next periodic sync — the remote takes a few seconds to actually mark it merged, and the app now waits for that rather than refreshing too early and finding nothing changed.
- Approving a PR now updates whether it can be merged, so the merge button appears as soon as your approval satisfies the last requirement — previously it stayed hidden until the next periodic sync, because the remote recomputes mergeability only after the approval returns.
- The window no longer goes permanently black when the interface fails: a crash now shows what went wrong with a retry / reload, a failure during startup shows a recovery screen, and a renderer that dies outright is reloaded automatically. Crash details are also written to the application log, which they previously never were.
- Switching to another PR while a merge is still in flight no longer clears the PR you just opened.

## [0.11.2] - 2026-07-28

> Highlights of this release:
Expand Down Expand Up @@ -514,6 +541,7 @@ and the versioning follows [Semantic Versioning](https://semver.org/).
License: [Apache-2.0](LICENSE). The package bundles third-party components (pr-agent, Electron, etc.), each distributed under its own license, see [NOTICE](NOTICE).

[Unreleased]: https://github.com/huhamhire/code-meeseeks/compare/v0.11.1...HEAD
[0.12.0]: https://github.com/huhamhire/code-meeseeks/compare/v0.11.2...v0.12.0
[0.11.2]: https://github.com/huhamhire/code-meeseeks/compare/v0.11.1...v0.11.2
[0.11.1]: https://github.com/huhamhire/code-meeseeks/compare/v0.11.0...v0.11.1
[0.11.0]: https://github.com/huhamhire/code-meeseeks/compare/v0.10.0...v0.11.0
Expand Down
28 changes: 28 additions & 0 deletions CHANGELOG.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,33 @@
本项目所有重要变更记录于此。格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),
版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。

## [0.12.0] - 2026-09-09

### ✨ 新增

- 现在可以把评论交给 Agent 调查:在任意评论上点击引用按钮再提问,Agent 会拿到评论原文、作者及其指向的代码,对照真实代码核查该说法是否成立。得到的结论可一键转成针对该评论的回复草稿,发出前仍可编辑。
- 代码编辑器升级至 Monaco 0.56.0(原 0.55.1),同时替换掉了此前多数依赖安全告警所指向的过时清理库副本。
- 内置评审引擎升级至 pr-agent 0.45.0(原 0.39.0)。两处长期存在的本地修补不再需要——单行文件变更在上游已渲染正确,二进制文件也无需再绕开——其 YAML 解析对不规范的模型输出也更宽容。
- @提及 改为胶囊标签展示,不再淹没在正文里,一眼即可看出点到了谁——活动页、内联 diff 评论、草稿与 PR 描述一致生效。
- 代理设置新增**直连地址**列表:列出的地址跳过代理直接连接,内网代码平台、它的 git 远端或自建模型服务因此保持可达,其余流量照常走代理。沿用通行的 `NO_PROXY` 写法(填域名同时覆盖子域),并对所有出站路径一并生效——REST、git 与 LLM 调用。
- 因模型不可用而失败的评审现在会明确说明,并给出处理方式——使用本地 CLI 供应商(claude / codex)时,模型来自该 CLI 自身的配置,需要在那里更换。

### 🔧 修复

- 生成的 PR 描述末尾不再出现空白卡片——评审引擎新加入输出的一个不可见标记此前被当成了独立分块。
- 生成的 PR 描述中的图表恢复为从左至右布局,不再在步骤稍多时变成纵向长图、把其余内容挤出屏幕。
- 评审时间线中的提交分割线现在固定在提交边界的真实位置,不再因为下方出现新消息而一路下移。
- 生成的 PR 描述中,文件清单现在显示每个文件真实的增删行数,不再是 `+-1/--1`。
- 该清单中的链接现在指向文件本身,而非一个不存在的行号,点击可正常跳转。
- PR 描述末尾带有 git 合并残留(`# Conflicts:` 及其下的文件行)时,生成的描述不再被切碎——那些行属于被引用的描述正文,而不是标题。
- 按需加载的界面部分——diff 编辑器、评论中的内联代码上下文——加载失败时不再拖垮整个应用,失败被限制在该区域内。若失败原因是窗口开着时应用被更新或重新构建,会明确说明并提供重新加载,那也是这种情况下唯一有效的操作。
- 评审失败时现在会展示供应商返回的真实错误,而不再只有一句「所有备选模型均调用失败」——真正的原因(模型不可用、登录过期、额度耗尽)此前被吞掉,从未出现在运行卡片上。
- 本地 CLI 供应商正常退出却返回空回复时,现在会作为失败上报并指明该原因,而不再表现为一次无从解释的 LLM 调用失败。
- 合并 PR 后,该 PR 会在稍后自动从列表中消失,而不再滞留到下一次周期同步——远端需要几秒才真正标记为已合并,应用现在会等待这一刻,而不是过早刷新、结果什么都没变。
- 批准 PR 后会重新判断其是否可合并,当你的批准满足最后一项要求时合并按钮即刻出现——此前它会一直隐藏到下一次周期同步,因为远端要在批准返回之后才重新计算可合并性。
- 界面出错时窗口不再永久黑屏:渲染崩溃会显示出错内容并提供重试/重新加载,启动阶段失败会显示恢复界面,渲染进程整个崩溃则自动重新加载。崩溃详情同时写入应用日志——此前从不记录。
- 合并请求仍在进行时切换到其他 PR,不再清空你刚打开的那个 PR。

## [0.11.2] - 2026-07-28

> 本次发布要点:
Expand Down Expand Up @@ -514,6 +541,7 @@
许可证:[Apache-2.0](LICENSE)。打包内含第三方组件(pr-agent、Electron 等),各按其许可证分发,见 [NOTICE](NOTICE)。

[Unreleased]: https://github.com/huhamhire/code-meeseeks/compare/v0.11.1...HEAD
[0.12.0]: https://github.com/huhamhire/code-meeseeks/compare/v0.11.2...v0.12.0
[0.11.2]: https://github.com/huhamhire/code-meeseeks/compare/v0.11.1...v0.11.2
[0.11.1]: https://github.com/huhamhire/code-meeseeks/compare/v0.11.0...v0.11.1
[0.11.0]: https://github.com/huhamhire/code-meeseeks/compare/v0.10.0...v0.11.0
Expand Down
4 changes: 2 additions & 2 deletions apps/desktop/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@meebox/desktop",
"version": "0.11.2",
"version": "0.12.0",
"private": true,
"description": "meebox Electron desktop app",
"author": {
Expand Down Expand Up @@ -81,7 +81,7 @@
"i18next": "^26.3.1",
"i18next-resources-to-backend": "^1.2.1",
"mermaid": "^11.4.0",
"monaco-editor": "^0.55.0",
"monaco-editor": "^0.56.0",
"pino": "^9.5.0",
"pino-roll": "^3.0.0",
"react": "^19.2.0",
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/scripts/pragent-runtime.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
},
"prAgent": {
"_comment": "嵌入式运行时安装的 pr-agent 版本(pinned);须与 sitecustomize.py 的 _EXPECTED_PRAGENT_VERSION 对齐,升级时两处同步",
"version": "0.39.0"
"version": "0.45.0"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,15 @@
applied it silently degrades, never letting a shim exception block the flow.
"""
from .patches.describe_assessment import patch as _patch_describe_assessment
from .patches.git_patch_processing import patch as _patch_git_patch_processing
from .patches.litellm_handler import patch as _patch_litellm_handler
from .patches.load_yaml import patch as _patch_load_yaml
from .patches.local_git_provider import patch as _patch_local_git_provider
from .runtime import _debug, _register_post_import


def apply() -> None:
# local_git_provider two patches merged into one patch_fn (registering multiple finders for the
# same module shadows each other; only the meta_path[0] one takes effect): binary-safe
# get_diff_files + get_line_link anchor.
# local_git_provider: inject get_line_link (the structured /review anchor) + get_repo_file_content. Registered as a
# single patch_fn because multiple finders on one module shadow each other (only meta_path[0] takes effect).
_register_post_import(
"pr_agent.git_providers.local_git_provider",
_patch_local_git_provider,
Expand All @@ -40,11 +38,4 @@ def apply() -> None:
"pr_agent.tools.pr_description",
_patch_describe_assessment,
)
# git patch processing: fix the phantom "unchanged" line on single-line hunks (omitted hunk size defaulted to 0
# instead of 1), which made single-line file changes look like they contained both the old and new value. Self-
# disabling once upstream fixes it — see patches/git_patch_processing.py.
_register_post_import(
"pr_agent.algo.git_patch_processing",
_patch_git_patch_processing,
)
_debug("meebox shim loaded")
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import sys

from ..runtime import _debug, strip_cache_break
from ..usage import _emit_usage_tokens
from ..usage import _emit_llm_error, _emit_usage_tokens
from .specs import _CLI_SPECS


Expand Down Expand Up @@ -88,12 +88,18 @@ async def run_cli_chat(bin_name, system, user) -> str:
except Exception as exc: # noqa: BLE001
raise RuntimeError(f"failed to start CLI '{bin_name}': {exc}") from exc
out, err = await proc.communicate(prompt.encode("utf-8"))
out_text = (out or b"").decode("utf-8", "replace")
if proc.returncode != 0:
raise RuntimeError(
f"CLI '{bin_name}' exit code {proc.returncode}: "
f"{(err or b'').decode('utf-8', 'replace')[:500]}"
)
text, usage = spec["parser"]((out or b"").decode("utf-8", "replace"))
# Error source: some CLIs report the failure only on **stdout** (codex writes turn.failed / error events into its
# JSONL stream and leaves stderr empty), which would otherwise surface as an empty reason and lose the real cause
# (auth / model unavailable / quota). When the spec provides an extractor, take stdout's verdict first and fall
# back to stderr; either way keep the raise contract (pr-agent turns it into "all fallback models failed").
extractor = spec.get("error_extractor")
detail = (extractor(out_text) if extractor else None) or (err or b"").decode("utf-8", "replace")
detail = (detail or "<no output>")[:500]
_emit_llm_error(bin_name, detail)
raise RuntimeError(f"CLI '{bin_name}' exit code {proc.returncode}: {detail}")
text, usage = spec["parser"](out_text)
if usage:
# prompt_tokens ≈ total input-side size, output_tokens ≈ completion (input/output_tokens share the same names across both).
# The cache fields differ in convention between the two:
Expand All @@ -115,6 +121,18 @@ async def run_cli_chat(bin_name, system, user) -> str:
cache_read_tokens=cache_read if isinstance(cache_read, int) else None,
turns=turns if isinstance(turns, int) else None,
)
# Empty reply on a zero exit code: the CLI ran to completion but produced no assistant message (codex does this when a turn
# dies on a tool/feature failure, e.g. Code Mode failing closed because its host binary is missing). Downstream this became
# an empty prompt into pr-agent's load_yaml → the opaque "all fallback models failed"; raise here instead, carrying any error
# event the stream did report, so the run card names the actual cause. Usage is emitted first, keeping the token count truthful.
if not (text or "").strip():
extractor = spec.get("error_extractor")
detail = (extractor(out_text) if extractor else None) or ""
msg = f"CLI '{bin_name}' returned an empty reply" + (
f": {detail[:500]}" if detail else " (no error reported in its output)"
)
_emit_llm_error(bin_name, msg)
raise RuntimeError(msg)
return text


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,45 @@ def _parse_codex_output(stdout):
if isinstance(usage, dict) and turns:
usage["num_turns"] = turns
return (text if isinstance(text, str) else ""), usage


def _extract_codex_error(stdout):
"""Pull the failure cause out of the `codex exec --json` event stream, returning a message string or None.

codex reports failures on **stdout** (the JSONL stream) and leaves stderr empty, so without this the caller would
raise with an empty detail and the real cause (auth, model 404, quota ...) would never reach the logs. Precedence:
- `turn.failed` → error.message is the terminal verdict, most accurate;
- otherwise the last `type=="error"` event (the intermediate retry notices carry the same upstream message);
- otherwise the last `item.completed` whose item.type=="error" (startup-stage errors, e.g. an unavailable feature host).
"""
import json

turn_failed = None
last_error = None
last_item_error = None
for line in (stdout or "").splitlines():
line = line.strip()
if not line:
continue
try:
ev = json.loads(line)
except Exception: # noqa: BLE001 - skip non-JSON lines (logs, etc.)
continue
if not isinstance(ev, dict):
continue
etype = ev.get("type")
if etype == "turn.failed":
err = ev.get("error")
if isinstance(err, dict) and isinstance(err.get("message"), str):
turn_failed = err["message"]
elif etype == "error":
msg = ev.get("message")
if isinstance(msg, str):
last_error = msg
elif etype == "item.completed":
item = ev.get("item")
if isinstance(item, dict) and item.get("type") == "error":
msg = item.get("message")
if isinstance(msg, str):
last_item_error = msg
return turn_failed or last_error or last_item_error
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Spec table for adapted local CLI commands: argv flags (the prompt always goes via stdin) + output parser + billing env to strip.
Registering one entry here is enough for a new command; the renderer-side whitelist validation must stay in sync (see LlmProfileForm.validateProfile)."""
from .parsers import _parse_claude_output, _parse_codex_output
from .parsers import _extract_codex_error, _parse_claude_output, _parse_codex_output

# `low_effort_flags`: argv to append for the low-effort tier (only enabled by the Agent orchestration channel via MEEBOX_CLI_REASONING,
# see install.py). Commands with a trailing `-` (stdin) insert these flags before the `-`, keeping `-` last.
Expand Down Expand Up @@ -30,6 +30,9 @@
],
"low_effort_flags": ["-c", "model_reasoning_effort=low"],
"parser": _parse_codex_output,
# codex writes its failures into the stdout event stream and leaves stderr empty, so a non-zero exit needs
# the cause extracted from stdout (see install.py); commands without this key just fall back to stderr.
"error_extractor": _extract_codex_error,
"strip_env": ("OPENAI_API_KEY", "CODEX_API_KEY"),
},
}
Loading
Loading