Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@ All notable changes to Code Context Control (C3) are documented here.
The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.158.3] - 2026-10-23

### Fixed — `c3_bitbucket list_prs` takes `author` and `reviewer`

The handler and the Bitbucket client could already filter `list_prs` by
author and by reviewer (Bitbucket's `role.N`/`username.N` query), but the
MCP tool's signature had neither argument, so a client that passed one was
refused with pydantic's "Unexpected keyword argument" before the handler ran.
Work Agents' Bitbucket watchers and its Work page asked for "my pull
requests" that way and failed on every poll. `c3_bitbucket` now takes
`author` and `reviewer` (Bitbucket user names) and passes them through, in
text and in `format="json"`.

## [2.158.2] - 2026-10-21

### Fixed — approval requests that never reached the desk or the phone
Expand Down
2 changes: 1 addition & 1 deletion cli/c3.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
# Config
CONFIG_DIR = ".c3"
CONFIG_FILE = ".c3/config.json"
__version__ = "2.158.2"
__version__ = "2.158.3"

# The PreToolUse matcher for native subagent calls (installer and hub migration).
AGENT_MATCHER = "Agent|Task"
Expand Down
6 changes: 5 additions & 1 deletion cli/mcp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -1277,6 +1277,8 @@ async def c3_bitbucket(
pr_id: int = 0,
branch: str = "",
state: str = "OPEN",
author: str = "",
reviewer: str = "",
title: str = "",
body: str = "",
from_branch: str = "",
Expand Down Expand Up @@ -1304,7 +1306,8 @@ async def c3_bitbucket(
default branch, browse and clone URLs),
readme ([at=branch/commit] — the root README, markdown preferred),
get_file (path [at] — one text file, 256 KB cap, binary refused),
list_prs, get_pr, get_pr_diff, get_pr_activities, get_pr_commits,
list_prs ([author] [reviewer] — Bitbucket user names), get_pr,
get_pr_diff, get_pr_activities, get_pr_commits,
pr_issues (pr_id — Jira issues Bitbucket's Jira integration links to the PR),
create_pr, update_pr (pr_id + any of title/description/reviewers/to_branch —
unchanged fields are preserved; reviewers is the FULL comma-separated
Expand Down Expand Up @@ -1335,6 +1338,7 @@ def finalize(fname, fargs, fresp, fsumm, **kw):
return await asyncio.to_thread(
handle_bitbucket, action, svc, finalize,
project=project, repo=repo, pr_id=pr_id, branch=branch, state=state,
author=author, reviewer=reviewer,
title=title, body=body, from_branch=from_branch, to_branch=to_branch,
description=description, reviewers=reviewers, name=name, url=url,
events=events, start_point=start_point, commit=commit,
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "code-context-control"
version = "2.158.2"
version = "2.158.3"
description = "Local MCP code-intelligence for AI coding tools: surgical search/read/edit, agent-config version history, path-level access + masking guards, and a multi-project hub."
readme = "README.md"
requires-python = ">=3.10"
Expand Down
20 changes: 20 additions & 0 deletions tests/test_bitbucket_tool.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
"""Tests for cli/tools/bitbucket.py — handle_bitbucket dispatch."""
from __future__ import annotations

import asyncio
import json
import sys
import tempfile
import unittest
from pathlib import Path
from types import SimpleNamespace
from unittest import mock

REPO_ROOT = Path(__file__).resolve().parent.parent
Expand Down Expand Up @@ -456,6 +458,24 @@ def test_list_branches_passes_filter_and_order(self):
self.assertEqual(kw["filter_text"], "feat")
self.assertEqual(kw["order"], "modification")

def test_list_prs_passes_author_and_reviewer(self):
finalize, _ = _captured_finalize()
tool.handle_bitbucket("list_prs", self.svc, finalize, author="alice", reviewer="bob")
kw = next(c[2] for c in self.client.calls if c[0] == "list_prs")
self.assertEqual((kw["author"], kw["reviewer"]), ("alice", "bob"))


class TestBitbucketMcpTool(unittest.TestCase):
def test_list_prs_author_and_reviewer_reach_the_handler(self):
from cli import mcp_server
schema = asyncio.run(mcp_server.mcp.get_tool("c3_bitbucket")).parameters["properties"]
self.assertLessEqual({"author", "reviewer"}, set(schema))
ctx = SimpleNamespace(request_context=SimpleNamespace(lifespan_context=object()))
with mock.patch.object(tool, "handle_bitbucket", return_value="ok") as handle:
asyncio.run(mcp_server.c3_bitbucket("list_prs", author="alice", reviewer="bob", ctx=ctx))
self.assertEqual((handle.call_args.kwargs["author"], handle.call_args.kwargs["reviewer"]),
("alice", "bob"))

def test_cap_clamps_single_overlong_line(self):
# Issue 7: one line with no newlines must still be clamped by chars.
long_line = "x" * 200_000
Expand Down
Loading