glab-discussion
CLI wrapper around GitLab Discussions REST API for listing, creating, and managing merge request discussions. Built on top of glab for authentication.
Why
The glab CLI has no native support for merge request discussions. Reviewing comments, replying to threads, adding inline code review notes, and resolving discussions all require manual API calls with complex nested JSON payloads. This tool wraps those APIs into simple commands, with an incremental dump mode designed for AI agent workflows — each discussion thread gets its own file, only changed threads are rewritten, and bot authors are automatically tagged.
Installation
uv tool install glab-discussion
Claude Code plugin
The repo includes a Claude Code plugin with:
- a skill that teaches AI agents how to use
glab-discussion - a PreToolUse hook that blocks three command shapes and redirects the agent to
glab-discussioninstead. This keeps thread IDs, resolve state, and inline diff positions in scope rather than letting the agent wrangle raw API JSON.
claude plugin marketplace add fprochazka/glab-discussion
claude plugin install glab-discussion@fprochazka-glab-discussion
To upgrade after a new release:
uv tool install --force glab-discussion
uv tool install --force bash-classify
claude plugin marketplace update fprochazka-glab-discussion
claude plugin update glab-discussion@fprochazka-glab-discussion
The hook blocks:
| Rule | Shape |
|---|---|
mr-discussions-api |
glab api against a merge request's discussions or notes sub-resource |
mr-view-comments |
glab mr view --comments, its -c short form, and --resolved / --unresolved, which imply it |
mr-note |
glab mr note, except the read-only glab mr note list |
The verdict comes from bash-classify, which parses the command and reports which of those shapes it actually invokes. Text that merely names one — a heredoc body, an echo argument, a commit message, a grep pattern — is not an invocation and is allowed, while a wrapper (sudo, timeout, bash -c, xargs) does not hide one.
Without bash-classify the hook still works, but in a degraded mode: it falls back to matching the raw command text. That is wrong in both directions — it denies anything that so much as mentions a blocked command, and it lets glab mr view 42 -c through, because the old pattern only ever knew the long --comments spelling. Every deny issued that way says so in its reason, and a SessionStart hook says the same thing once at the start of a session, so the agent can tell you to install or upgrade the tool (plugin SessionStart hooks need Claude Code 2.1.257 or newer; versions 2.1.216 to 2.1.252 silently skipped them).
Usage
By default, the MR is auto-detected from the current git branch (via glab mr view). Override with --mr-url or --hostname/--project/--mr-iid.
read
Read MR discussions. Prints to stdout by default, or writes per-thread files with --dump. In non-interactive environments (AI agents, piped output), --dump is the default.
glab-discussion read # auto-detect MR from git branch
glab-discussion read --dump # one file per thread, incrementally updated
glab-discussion read --dump --full # clear and rewrite all files
glab-discussion read --no-dump # force stdout even in non-interactive mode
write
Create a new discussion, reply to a thread, or add an inline diff note.
glab-discussion write --body "Comment text"
glab-discussion write --reply-to DISCUSSION_ID --body "Reply"
glab-discussion write --file path/to/file.py --new-line 42 --body "Issue here"
glab-discussion write --file path/to/file.py --old-line 10 --body "Was wrong"
echo "From stdin" | glab-discussion write --body -
--new-line corresponds to the file on the MR source branch — if the branch is checked out locally, local file line numbers match directly. --old-line refers to the target branch version.
diff
Show the MR diff annotated with old/new line numbers, so you know which line numbers to use with write --new-line or --old-line.
glab-discussion diff
glab-discussion diff --file path/to/file.py
glab-discussion diff --version 3
resolve
Resolve or unresolve a discussion.
glab-discussion resolve DISCUSSION_ID
glab-discussion resolve DISCUSSION_ID --unresolve
edit
Edit an existing note's body.
glab-discussion edit NOTE_ID --body "Updated text"
echo "From stdin" | glab-discussion edit NOTE_ID --body -
delete
Delete a note.
glab-discussion delete NOTE_ID
Requirements
glabCLI installed and authenticated- Python 3.12+
The Claude Code plugin's hook additionally needs:
jqbash-classify0.10.0 or newer. Without it the hook still runs, but falls back to text patterns that can misfire: they deny commands that only mention a blocked command in a heredoc, a commit message or agreppattern.
uv tool install bash-classify
Development
git clone https://github.com/fprochazka/glab-discussion.git
cd glab-discussion
uv sync --dev
Run tests and linting:
uv run ruff format .
uv run ruff check .
uv run pytest
Releasing
Version is derived automatically from git tags via hatch-vcs — no manual version bumping needed.
Before tagging, bump the version in both plugin manifest files:
coding-agent-plugins/claude-code/.claude-plugin/plugin.json.claude-plugin/marketplace.json
Wait for CI to pass on master, then tag, push, and create a GitHub release:
# Review changes since last release
git log $(git describe --tags --abbrev=0)..HEAD --oneline
git tag v<version>
git push origin v<version>
gh release create v<version> --title "v<version>" --notes "..."
The publish.yml GitHub Action builds and publishes to PyPI automatically via trusted publishing.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file glab_discussion-0.4.0.tar.gz.
File metadata
- Download URL: glab_discussion-0.4.0.tar.gz
- Upload date:
- Size: 44.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f62780fbc85105217b605c4f8a262468b2fc00daef505b81a21e8899f4841819
|
|
| MD5 |
f297bb7de74891e41ef2cb6313ae0785
|
|
| BLAKE2b-256 |
efa82eda067a50a9cc5a811300b52acdfd6213ccbf1c64135e0ea7d43a3d756f
|
Provenance
The following attestation bundles were made for glab_discussion-0.4.0.tar.gz:
Publisher:
publish.yml on fprochazka/glab-discussion
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
glab_discussion-0.4.0.tar.gz -
Subject digest:
f62780fbc85105217b605c4f8a262468b2fc00daef505b81a21e8899f4841819 - Sigstore transparency entry: 2724491394
- Sigstore integration time:
-
Permalink:
fprochazka/glab-discussion@40785320a63bbb12a3140c9f30c901a68de567e7 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/fprochazka
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@40785320a63bbb12a3140c9f30c901a68de567e7 -
Trigger Event:
push
-
Statement type:
File details
Details for the file glab_discussion-0.4.0-py3-none-any.whl.
File metadata
- Download URL: glab_discussion-0.4.0-py3-none-any.whl
- Upload date:
- Size: 18.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
183aa06bf91ed2dfce6fbea236efd7312147372a65754220c8674afbc7ac0bdf
|
|
| MD5 |
0f5c86f0241de95e8419942ad0b6c512
|
|
| BLAKE2b-256 |
72cdc3edec020c285cf2146d8a972e9b89797be0e68874994486d9483657086e
|
Provenance
The following attestation bundles were made for glab_discussion-0.4.0-py3-none-any.whl:
Publisher:
publish.yml on fprochazka/glab-discussion
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
glab_discussion-0.4.0-py3-none-any.whl -
Subject digest:
183aa06bf91ed2dfce6fbea236efd7312147372a65754220c8674afbc7ac0bdf - Sigstore transparency entry: 2724492118
- Sigstore integration time:
-
Permalink:
fprochazka/glab-discussion@40785320a63bbb12a3140c9f30c901a68de567e7 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/fprochazka
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@40785320a63bbb12a3140c9f30c901a68de567e7 -
Trigger Event:
push
-
Statement type: