Skip to main content
Glama

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault

No arguments

Instructions

Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.

This server publishes no instructions, or was last inspected before Glama recorded them.

Capabilities

Features and capabilities supported by this server

Protocol revision2025-11-25

CapabilityDetails
tools
{
  "listChanged": false
}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}
experimental
{}

Tools

Functions exposed to the LLM to take actions

NameDescription
codebench_importA

Import benchmark results from CSV or JSON.

CSV convention: first column is the row label, remaining columns are metric names with finite numeric values.

JSON convention: array of objects, first key is the row label, rest are metric keys with finite numeric values.

Each (row label, metric) pair may appear only once per import, and NaN/Infinity are refused: a non-finite measurement is not one.

Args:

  • benchmark: Benchmark name (e.g. "search-perf")

  • csv_data: CSV string (header + data rows). Provide csv_data OR json_data.

  • json_data: JSON array string. Provide csv_data OR json_data.

  • date: Run date (default: today, ISO format YYYY-MM-DD)

  • tags: Optional tags (e.g. ["nightly", "v2.1"])

  • meta: Optional metadata (e.g. {"git_sha": "abc123", "ci_url": "..."})

codebench_queryA

Query and pivot benchmark results.

group_by="row": original table shape (row_labels as rows, metrics as columns). Returns one table per run.

group_by="run": trend view (runs as rows, metrics as columns). Returns one table per row_label.

Args:

  • benchmark: Benchmark name to query

  • runs: Specific run IDs (default: all matching)

  • date_from: Start date filter (inclusive, YYYY-MM-DD)

  • date_to: End date filter (inclusive, YYYY-MM-DD)

  • metrics: Which metrics to include (default: all)

  • rows: Which row_labels to include (default: all)

  • group_by: Pivot axis — "row" or "run"

  • last_n: Limit to last N runs by date. 0 means NO runs; omit it for no limit. A negative value is an error (it used to mean "no limit").

  • format: Output — "json" or "csv"

codebench_listA

List benchmarks or runs.

Without benchmark: lists all benchmark names with run counts. With benchmark: lists runs for that benchmark.

Args:

  • benchmark: If provided, list runs for this benchmark

  • last_n: Limit to last N runs. Requires benchmark — supplying last_n without one is an ERROR, because benchmark names have no runs to limit and the argument would otherwise be silently discarded. 0 means NO runs; omit it for no limit. A negative value is an error.

codebench_deleteA

Delete a single run or all runs for a benchmark.

Args:

  • run_id: Delete a specific run (e.g. "BE-1")

  • benchmark: Delete all runs for a benchmark name

blockers_addA

Defer an item by adding a blocker.

Args:

  • item_id: The blocked entity (e.g. "CB-5", "FR-012")

  • reason: Why it's blocked

  • blocked_by: Dependency entity (e.g. "CB-3"). Required for entity_resolved triggers.

  • trigger_type: entity_resolved, date, or manual. Defaults to entity_resolved if blocked_by provided, manual otherwise.

  • trigger_at: Date/datetime for date triggers (e.g. "2026-04-10"). Normalized to UTC.

blockers_queryA

List blockers with filters. Each result includes computed satisfaction state.

Args:

  • item_id: Filter by blocked item (e.g. "CB-5")

  • blocked_by: Filter by dependency ("what does CB-3 unblock?")

  • trigger_type: Filter by trigger type (entity_resolved, date, manual)

  • active_only: Only unsatisfied, uncancelled blockers (default: true)

blockers_checkA

Scan for currently actionable items — items whose blockers are all satisfied.

Returns actionable items (all blockers met), partially unblocked items (some blockers met), and overdue date triggers.

blockers_resolveA

Cancel or manually resolve a blocker.

Args:

  • blocker_id: The blocker row ID

  • action: 'cancel' (any trigger type) or 'resolve' (manual triggers only)

claims_claimA

Claim a finding or requirement so parallel agents do not collide.

Args:

  • entity_id: CB-N, FR-N or NFR-N

  • holder: who is claiming — a branch name, agent id, or person

  • holder_kind: branch | agent | human

  • holder_repo: absolute path of the repo owning the branch, if any

  • note: free-text reason, kept on renewal unless replaced

  • project: also move a finding to in_progress (requirements never project)

  • allow_terminal: claim even if the entity is already resolved

Returns:

  • outcome: claimed | already_mine | held_by_other | entity_terminal | undetermined. On held_by_other the holder fields name the INCUMBENT. On undetermined, re-issue the identical call — it converges on already_mine.

claims_releaseA

Release a claim. Authorized on the full (holder, holder_kind, holder_repo) triple — pass exactly what you claimed with.

Returns:

  • outcome: released | not_yours | not_claimed | undetermined. A projected status is restored only if it still holds the projected value, so finished work is never resurrected.

claims_who_holdsB

Who currently holds this entity, if anyone.

claims_held_byB

Everything a given holder currently holds.

claims_listA

List live claims, optionally filtered by kind, holder or holder kind.

Args:

  • kind: Entity kind filter (e.g. "finding"), or omit for every kind.

  • holder: Holder name filter, or omit for every holder.

  • holder_kind: Holder kind filter (e.g. "branch"), or omit for every kind.

  • limit: Max rows (default 200). 0 means NO rows; a negative value is an error (it used to mean "no limit").

addA

Record a code finding observation (deduplicated by fingerprint).

If the fingerprint matches a live finding, that finding's occurrence count is bumped and IT is returned (was_new: false, dedup_action: "bumped"); a match on a fixed finding reopens it as a regression ("reopened"); a match on a wont_fix/not_a_bug finding creates a new row linked via meta.recurrence_of. Without a fingerprint a conservative server-side one is derived from category, file and the normalized description.

dedup_action has exactly four values — "created", "bumped", "reopened" and "recurrence_of_closed" — and the fourth is the one to read carefully: a recurrence of a DISMISSED twin files a NEW row and therefore reports was_new: true, so a client that tells create from match by gating on was_new == false misses the event entirely. The twin's id is always in meta.recurrence_of, and meta.similar_to usually carries its status alongside; on the paths where it does not — a caller-supplied fingerprint whose text does not resemble the twin, or a normalized description under the similarity minimum — the twin's status is NOT in this response at all, and costs one get.

attention is a top-level list, ALWAYS present and often empty: an empty list means "evaluated, nothing serious fired", which is a different fact from an absent channel. Two record forms exist, and a list may carry both (severity first, category second; each form at most once).

{signal: severity_escalated, from, to} says THIS observation raised the finding's stored severity. It appears only where a stored severity was raised — the bumped and reopened branches — and severity is monotonic under observation, so there is no de-escalation record to expect.

{signal: category_divergence, observed, stored} says this observation does not NAME the matched finding's category. It appears on every branch that HAS a matched row: bumped, reopened, and the recurrence branch, where the comparison is against the DISMISSED TWIN rather than the new row. Both sides are normalized, so a difference of spelling (Process Improvement vs process-improvement) is deliberately not a signal while a difference of name is; a stored category that is not text is skipped rather than raising. A newly created finding matched nothing, so it emits neither record.

stripped_meta_keys is a top-level list, ALWAYS present and often empty, following the same discipline as attention: [] means "checked, nothing to strip", never "no such channel". A meta key that is identity machinery OUTPUT (e.g. occurrences, recurrence_of, category_minted) is stripped from what gets stored rather than refused, so a caller that copies a fetched card's meta forward (get -> modify -> add) can tell, from this response alone, which of its own keys silently did not land. resolver_errors is the one exception: it reports a FAILURE state, not machinery input, so it is REFUSED outright rather than stripped, on this path exactly as on update's meta_update. This is the ADD-side contract only — CSV import strips the same dynamic reserved union but silently, with no equivalent response key (a decided, separate contract, CB-51), and update's meta_update still refuses every reserved key rather than stripping any of them.

stripped_description_tail is a top-level boolean, ALWAYS present and usually False, following that same discipline: False means "checked, nothing to cut", never "no such channel". Some filing agents leak a slice of their own tool call into the end of description; when the text after a </description> marker is nothing but envelope lines, that tail is CUT rather than refused — the finding is real and only its tail is junk — and cut BEFORE the fingerprint is derived, so a tailed and a clean report of one defect collapse onto one card instead of two. Prose that merely quotes the marker is not cut. True means the text stored is not byte-for-byte the text you passed.

Args:

  • severity: critical, high, medium, or low (case-insensitive, no aliases)

  • category: Finding category (e.g. tz_naive_datetime, n_plus_one, missing_validation). Call categories first to reuse existing category names. Spelling is normalized (casefold, hyphen/whitespace -> "_"); a category this tracker does not already hold is REFUSED with a hint unless new_category=true — but only when the observation would CREATE a row: a fingerprint match on a known live or fixed finding is recorded regardless, with the observed category kept in the occurrence ring.

  • file: File path relative to project root

  • description: What's wrong

  • lines: WHERE IN THE CODE this finding is, and the only input that gives the card a durable ANCHOR. An anchor stores the surrounding source text and the commit it was read at, so the card still points at the right code after the file is edited and the line numbers move; a path written in description does not, and is never read as a location. WITHOUT THIS THE CARD HAS NO ANCHOR — nothing else in this call supplies one. Four spellings, all accepted: a bare line number ("1850") or range ("1850-1870"), which are read against the file argument above; a full "path/file.py:1850" token, whose path must name the same file as file or the anchor is refused rather than pointed at another file's line numbers; and a list ("[1850, 1870]"), which is N SEPARATE lines and never a range. Pass it whenever the finding is about a place in the code. Omit it — do not invent one — when the finding is about a process, a decision or a whole file: a made-up anchor is worse than none. Same field as meta.lines; supplying both with different values is refused.

  • source: First reporter of this defect (default: claude). Frozen at first report by design (BT-4): a re-observation keeps the original; newest sources live in the occurrence ring (meta.occurrences[*].source) — and an imported observation's ring source can be a peer tracker's.

  • tags: Optional tags for grouping

  • meta: Optional JSON metadata for anything this call has no argument for (module, rule_code, and so on). The code location is NOT one of those: it has its own lines argument above, and that is the spelling to use. meta.lines remains the same field and still works, so the two must not disagree — passing both with different values is refused rather than one silently winning. Top-level meta is the row's AUTHORED state, observation-frozen (BT-4): a re-observation's meta lands only as per-occurrence evidence in meta.occurrences[*].meta. Promoting specific keys into the row is a future allowlist by measured demand, not a general merge.

  • reported_at_commit: Git SHA when finding was created (auto-detected from HEAD if omitted)

  • reported_at_ref: Version/tag label (e.g. "v2.1.0"), always caller-supplied. Observation-frozen: a bump never updates it (per-occurrence refs stay in the ring as evidence) — but manually mutable BY DESIGN via update(reported_at_ref=), since a release is tagged after filing.

  • fingerprint: Stable identity token for this defect, computed from the INVARIANT part of the observation (normalized error signature + failing test + anchor file — no timestamps, SHAs, run ids). Same defect → same fingerprint. The auto: prefix is reserved for server-derived values.

  • new_category: Explicit permission to MINT a category the tracker does not hold yet (CB-60). Minting is stamped as meta.category_minted for later counting. Existing categories never need this.

batch_addA

Record multiple finding observations at once (deduplicated by fingerprint).

Members are deduplicated exactly like add — including against each other, so two members sharing a fingerprint yield one insert plus one bump. One result per input, in input order. Unknown member keys are refused.

Each result carries the same discriminators add returns, and the same four dedup_action values: "created", "bumped", "reopened" and "recurrence_of_closed", the last of which reports was_new: true because it files a NEW row linked to a dismissed twin via meta.recurrence_of.

Each result also carries its OWN attention list — always present, often empty, never shared between members. Two record forms exist, in this order and at most once each: {signal: severity_escalated, from, to} on the bumped and reopened branches, meaning that member's observation raised the stored finding's severity; and {signal: category_divergence, observed, stored} on every branch with a matched row (bumped, reopened, and the recurrence branch, where the comparison is against the dismissed twin), meaning that member does not NAME the matched finding's category. Both category sides are normalized, so a difference of spelling is not a signal; a stored category that is not text is skipped rather than raising.

Each result also carries its OWN stripped_meta_keys list — always present, often empty, never shared between members — following the same discipline: a meta key that is identity machinery OUTPUT (e.g. occurrences, recurrence_of, category_minted) is stripped from what gets stored rather than refused, and reported here so a caller forwarding a fetched card's meta can tell which of its own keys silently did not land. resolver_errors is refused outright instead (a FAILURE state, not machinery input), on this path exactly as on add.

Each result likewise carries its OWN stripped_description_tail boolean — always present, usually False, meaning "checked, nothing to cut" rather than "no such channel". A leaked tool-call tail on that member's description (envelope lines and nothing else after a </description> marker) is CUT rather than refused, before the fingerprint is derived so a tailed and a clean report of one defect collapse onto one card; prose merely quoting the marker is left alone. True means that member's stored text is not the text you passed.

Args:

  • findings: List of finding objects, each with keys: severity, category, file, description, and optionally: lines, source, tags, meta, reported_at_commit, reported_at_ref, fingerprint. lines is per-member and works exactly as it does on add: it is WHERE IN THE CODE that member is, and the only input that gives its card a durable ANCHOR surviving later edits to the file. A bare number ("1850"), a range ("1850-1870"), a full "path/file.py:1850" token (whose path must name that member's own file) or a list of lines. Omit it on a member that describes no place in the code rather than inventing one. It is the same field as that member's meta.lines, and the two are refused when they disagree.

  • reported_at_commit: Default commit SHA for all findings (auto-detected if omitted). Per-finding values override this.

  • reported_at_ref: Default version label for all findings. Per-finding values override this.

  • new_category: Batch-wide permission to MINT categories the tracker does not hold yet (CB-60); the first member introducing a category is stamped meta.category_minted.

updateA

Update a finding's status, severity, notes, tags, or metadata.

Args:

  • finding_id: The finding ID (e.g. CB-1)

  • status: New status: open, in_progress, fixed, not_a_bug, wont_fix, stale. Aliases accepted: done/resolved/implemented/closed → fixed, wontfix → wont_fix, invalid → not_a_bug, active/working/in-progress → in_progress

  • severity: Re-triage the finding: critical, high, medium, or low. Case-insensitive, but no aliases — unlike status, "crit" and "P0" are refused.

  • notes: REPLACES the notes wholesale, discarding whatever was there. To add to an existing record without destroying it, use append_note instead. If meta_update also carries a "notes" key, the meta_update value is the one that lands — see meta_update for why that is deliberate.

  • append_note: Appends a newline-joined line, preserving the prior notes. This is the safe way to add evidence to a long-lived card.

  • tags: Replace tags list

  • meta_update: Merge additional metadata keys. The three meta-writing arguments compose over ONE dict, in this order: notes replaces, append_note then extends that replacement, and meta_update merges LAST. So passing both notes= and meta_update={"notes": ...} in a single call is neither an error nor a refusal — meta_update wins the collision, on every key it names. That precedence is deliberate rather than incidental: meta_update names the storage key directly, which makes it the repair path for keys no other argument can reach (similar_to, category_minted, fingerprint_refusals), and a stamp no argument could overwrite would be an unrepairable one.

  • reported_at_ref: Update version/tag label (e.g. "v2.1.0"). This is the SANCTIONED manual mutation of an observation-frozen column (BT-4): observations never move it, this call does — a release is tagged after filing.

queryA

Search and filter findings. Returns structured results.

Supports lookup by ID via id= (single) or ids= (batch). Missing IDs are silently absent from the result so the caller can diff. For a strict single-ID fetch that errors on miss, use get instead.

Args:

  • id: Fetch a single finding by exact ID (e.g. CB-1383)

  • ids: Fetch multiple findings by ID list; missing IDs are skipped

  • status: Filter by status (open, in_progress, fixed, not_a_bug, wont_fix, stale, deferred). Aliases accepted. Use 'deferred' to find items with active blockers.

  • severity: Filter by severity (critical, high, medium, low)

  • category: Filter by exact category

  • file: Filter by file path (substring match)

  • source: Filter by source (claude, ruff, human, etc.). Compares the FIRST reporter — the column is frozen at first report (BT-4); later observations' sources live only in the occurrence ring (meta.occurrences[*].source), and an imported observation's ring source can be a peer tracker's.

  • tag: Filter by tag (finds findings containing this tag)

  • meta_key: Filter by metadata key existence. Reads the row's AUTHORED top-level meta (the column), never the occurrence ring.

  • meta_value: Filter by metadata value (requires meta_key; same authored top-level meta as meta_key — ring meta is not consulted)

  • commit: Matches the first-report column OR any occurrence in the ring (prefix match, hex validated) — "what was observed on this commit" (CB-128). staleness_check uses the NEWEST ring entry instead: a different question.

  • ref: Filter by reported_at_ref (exact match, never prefix) — matches the first-observed or manually assigned release ref (BT-4); per-occurrence refs in the ring are not consulted.

  • fingerprint: Filter by identity fingerprint (exact match)

  • group_by: Group results by: severity, category, status, file, source, tag, meta: (source groups count FIRST reporters — the column is frozen at first report). tag and meta:<key> do NOT partition the population: a card with two tags is counted under both, and a card carrying no value on the axis is in no group at all. The response therefore always carries population, ungrouped_rows, multi_group_rows and nonscalar_value_rows beside groups; the counts sum to the population only while the last three are 0. meta:<key> reads the AUTHORED top-level meta, like meta_key does, and a key holding ., [, ] or " is REFUSED — SQLite cannot tell such a name from a path (CB-167). A key that is absent, JSON null, or holds an object/array is ungrouped rather than invented. OVERLAPS grouping_tags DELIBERATELY and differently: that tool is a tag census with pair co-occurrence over status/category only; this is a distribution that composes with every filter on this tool.

  • limit: Max results. A limit you PASS is always honoured: 0 means NO results, and it means that with id/ids too (CB-158 — an id list used to raise any smaller limit to fit itself, so limit=0 came back full). A negative value is an error (it used to mean "no limit"). Omit it and the page size is 100, widened to fit an ids list so a batch lookup returns every id it asked for.

  • offset: Pagination offset

  • resolve_anchors: Resolve each result's location anchor against the repository HEAD, so a card whose code moved reports its new path. OFF by default because it costs 2-4 git calls per ANCHORED row and this is the primary read path; the cheap half — whether a card carries an anchor at all, and the refusal token when capture found nothing to grab — is in every result either way. get resolves one card by default.

recentA

Findings TOUCHED at or after a date — the one call for "what closed since".

WHAT THIS MEASURES: updated_at, the time of the LAST WRITE to the row, and not the moment the finding was closed. There is no close timestamp anywhere in the schema. A status change moves updated_at, and so do a re-tag, an AUTHORED meta patch, a severity re-triage, an append_note, and a DEDUPLICATED OBSERVATION — a repeat report bumps the occurrence count and stamps updated_at while the status stays exactly where it was.

AUTHORED is doing work in that sentence since CB-230: the tracker's own housekeeping (refreshing a card's code anchor) writes meta WITHOUT stamping, so a maintenance pass no longer floods this reader with every card it touched.

So recent(since=..., status="fixed") means "cards that are fixed NOW and were touched since that date", NOT "cards closed since that date". The error is ONE-SIDED: false positives are possible, misses are not, because closing a card always writes updated_at — guaranteed rather than merely true, since housekeeping is refused outright when it carries a status.

Rows come back newest touch first, with rowid breaking the whole-second ties updated_at produces, so a paged walk is stable.

Args:

  • since: Lower bound on updated_at, INCLUSIVE. 'YYYY-MM-DD' or 'YYYY-MM-DDTHH:MM:SSZ'. REQUIRED — an unparseable value is refused rather than defaulted, because a silently widened window answers a question nobody asked.

  • status: Filter by status (open, in_progress, fixed, not_a_bug, wont_fix, stale). Aliases accepted. Omit for every status. The deferred pseudo-status of query is NOT accepted here and is refused rather than ignored — use query for it.

  • limit: Max results (default 100). 0 means NO results. A negative value is an error (it used to mean "no limit"). The neighbouring query tool answers the same argument the same way.

  • offset: Pagination offset

getA

Fetch a single finding by ID with full body (description, severity, status, tags, meta, timestamps, commit refs).

The result carries an anchor summary saying where this card's code is NOW: state tells a card with no anchor apart from one whose anchor was retracted by hand and from one where capture looked and had nothing to grab, and loc_status/moved_file/path report the resolution against HEAD when it ran.

Raises a not-found error if the ID does not exist. For lenient batch lookup that silently drops missing IDs, use query(ids=[...]).

Args:

  • finding_id: The finding ID (e.g. CB-1383)

  • resolve_anchors: Resolve the anchor against the repository (default ON — the cost is bounded by one card). Pass False for a read that must not spawn a process: no git available, no repository, or a caller that only wants to know whether an anchor exists at all.

statsA

Aggregated cross-tabulated counts.

Args:

  • group_by: Group by: severity, category, status, file, source, tag, meta: (source buckets count FIRST reporters — the column is frozen at first report, BT-4). With tag or meta:<key> the rows do NOT partition: a card with two tags is cross-tabulated under both of them, so the totals exceed the number of findings. population, ungrouped_rows, multi_group_rows and nonscalar_value_rows ride beside groups on every axis and are what make that readable. A meta key holding ., [, ] or " is refused (CB-167).

summaryA

Dashboard overview — open/resolved counts, severity breakdown, top categories, hottest files, deferred counts. Start here for orientation.

categoriesA

List all existing categories with counts. Call this before adding findings to reuse consistent category names.

categories_normalizeA

Rename stored categories and re-key their fingerprints (CB-61).

TWO MODES, and the second is a working mode rather than a side effect. Without fold_map this folds every stored SPELLING to its canonical form, for rows filed before write-time canonicalization existed, whose stored auto:v1 fingerprint still carries the old spelling and therefore forks identity when the same defect is reported again. With a fold_map it MERGES CATEGORY NAMES: any stored name may be renamed to any canonical target, and the two need not be spellings of each other. That second mode is how a tracker's rare category names are collapsed into its common ones.

DRY RUN BY DEFAULT — without apply=true nothing is written and the report tells you exactly what would change. A key that matches no stored category is accepted and renames nothing, and unmatched_fold_keys names every one of them, so a typo on the left-hand side is stated rather than left to be spotted as a pair missing from the from -> to table. A typo in a TARGET is refused instead — see new_category. Matching is exact against the stored spelling, so a canonical key does not reach a stored Process Improvement and is reported unmatched. Take an export-csv backup before applying; restore-csv puts findings back verbatim into an EMPTY tracker, but milestone items and audit history are not in a CSV export and are not restored.

Each renamed row's fingerprint is handled by kind: a NULL or a caller-SUPPLIED fingerprint is left byte-identical, an auto:v1 one is re-derived with the new category after its stored inputs are verified to reproduce the stored hash. A row that fails that round trip is skipped WHOLE and reported under unverifiable. The occurrence ring (meta.occurrences) is never rewritten.

If the fold would put two LIVE findings on one fingerprint, the run writes NOTHING and reports the colliding pair by id — merging two cards is a decision, not a migration step. Any OTHER identity merge — two closed cards, or a closed card and a live one — is legal, so it is reported under merged_identities rather than refused: the run proceeds, and you are told which cards this fold fused. Both unmatched_fold_keys and merged_identities are always present; [] means "checked, none".

Args:

  • fold_map: Optional {stored category name: canonical target name} map, as an object or a JSON string. The key is matched exactly against the stored value and may be any name the table holds. Every target must already be canonical (casefold, hyphen/whitespace -> "_"). Omit it to fold every stored spelling to its own normalized form; {} is an explicit no-op.

  • apply: Write the changes. Default false (report only).

  • new_category: Permission to fold INTO a category this tracker does not hold yet, for the whole map at once. Without it such a target is refused, naming the nearest existing categories — an operation meant to REDUCE the number of category names must not invent one by typo. The refusal stops at the first bad target.

relations_relateA

Assert a typed relation between two findings.

Args:

  • src_id: Source finding (e.g. "CB-5"). For duplicate_of this is the LOSER — the card that dies.

  • rel: duplicate_of, split_from, follow_up_of, found_during, distinct_from, or related_to.

  • dst_id: Target finding. For duplicate_of this is the SURVIVOR.

  • source: Who is asserting this (e.g. "owner", "goldset-2026-08-17").

  • note: Optional reasoning.

relations_unrelateA

Retract a relation. Tombstones it — the row and its history remain.

Args:

  • src_id: Source finding

  • rel: The relation to retract

  • dst_id: Target finding

  • retracted_by: Who is retracting it

  • reason: Why

relations_queryA

List relations touching a finding, in both directions.

Args:

  • entity_id: Finding to look up (e.g. "CB-5"). Omit to list all.

  • rel: Filter by relation. rel="distinct_from" alone lists every live suppression, which is worth reviewing periodically.

  • include_retracted: Include tombstoned edges (default: false)

grouping_citationsA

Connected components of the hand-written CB-id reference graph.

READ-ONLY, and an ANNOTATION of what people already wrote — no link here is inferred. Every edge carries the field it came from and the quoted context of its first mention. A node whose degree exceeds hub_degree is a landmark many work units point AT, not a member of one: it does not transmit connectivity and is reported as an ANCHOR with its citers, so the components either side of it stay separate. References to ids outside the population are COUNTED as dangling, never dropped.

A pair you have declared DIFFERENT — relations_relate(a, "distinct_from", b) — stops being joined BY THAT REFERENCE, in either order; retracting the declaration brings the grouping back. The citation itself still appears, with its quoted context, in suppressed_edges: your declaration corrects the conclusion this tool draws from the reference, not the fact that somebody wrote it. Read still_grouped on each entry before trusting the separation — dropping one reference does not cut a graph, so if a third card cites both they are in one component regardless, and that flag (counted by still_grouped_total) is how the report tells you your declaration lost rather than leaving you to notice. This is the ONLY place distinct_from suppresses: grouping_filing's declared lineage (split_from / split_children) is untouched, because overriding one declaration with another is a different question from overriding a guess.

Args:

  • status: Narrow/widen the population (default: live statuses; "all")

  • category: Restrict to one category

  • hub_degree: Degree above which a node becomes an anchor (default 3, chosen on the outcome — see DEFAULT_HUB_DEGREE); None disables hub splitting and returns the raw components

  • component_limit: Max components returned (totals stay visible)

  • member_limit: Max members per component (edges follow the page)

  • anchor_limit: Max anchors returned (default 25)

  • orphan_limit: Max orphan ids returned (default 50)

grouping_tagsA

Tag pivots: counts, co-occurrence, and near-duplicate taxonomy strings.

READ-ONLY. Co-occurrence carries Jaccard beside the raw count, because on a corpus with one 390-card tag the raw count ranks that tag's pairs first no matter how weak the association is. variants spans tags AND categories in one namespace: the taxonomy drift is not confined to one column (process_improvement / process-improvement).

Args:

  • status: Narrow/widen the population (default: live statuses; "all")

  • category: Restrict to one category

  • min_pair_count: Drop tag pairs co-occurring fewer times (default 2)

  • tag_limit: Max tags returned (totals stay visible)

  • pair_limit: Max pairs returned (default 50; None for all)

grouping_filingA

Split lineages and shared filing events (sprint / plan).

READ-ONLY. LINEAGE IS TRAVERSED, NOT GROUPED: A → B → C is one lineage with depths, and its links resolve against EVERY card in the tracker, not just the population, so a fixed middle card does not sever the chain. A lineage surfaces when at least one member is in the population; a lineage value naming no card is reported unresolved, not dropped. Filing events are grouped by exact value within the population.

Args:

  • status: Narrow/widen the population (default: live statuses; "all")

  • category: Restrict to one category

  • lineage_limit: Max lineages returned (totals stay visible)

  • event_limit: Max filing events returned (totals stay visible)

staleness_checkA

Check if findings are stale by comparing against git history.

Staleness is checked against each finding's NEWEST observation: a deduplicated re-observation records its commit in the occurrence ring, and that commit — not the frozen first-report reported_at_commit — is what the file is compared from; findings with no ring fall back to the first report. Each result carries checked_commit, the commit the verdict was computed against.

Returns file_status for each finding:

  • current: file unchanged since finding was reported

  • modified: file changed but still exists

  • renamed: file was renamed/moved

  • deleted: file no longer exists

  • unknown: can't determine, with a reason naming which question could not be answered — no provenance data, an unreachable commit, a path outside this repository's worktree, a path the reported commit never contained (a glob, free text, or a file added later), an empty or malformed value, a path that is neither a file nor a directory, or a git/stat call that failed

Args:

  • finding_id: Check a single finding (e.g. CB-1)

  • status: Filter by finding status (default: open)

  • category: Filter by category

  • file: Filter by file path (substring match)

  • resolve_anchors: Also resolve each finding's location anchor against HEAD, so a record says where the reported LINES are now and not only what became of the file. OFF by default: this query permits ten thousand rows and already spends git per file. Every record carries the cheap half of the summary either way — whether the card has an anchor at all.

anchor_resolveA

Resolve stored location anchors to their current lines on HEAD.

Each anchored finding gets a record: status (current / moved / moved_file / lost / ambiguous / unknown), the coordinate, the channel that produced it ("git" for reverse blame, "content" for the secondary text channel), a reason token when there is no answer, and survived as "/" when part of a span outlived the rest.

moved_file is a status of its own, not moved with a different path: the code left the file the finding names, and a consumer must see that rather than receive a line number in a file it never asked about.

THE SUMMARY'S DENOMINATOR IS anchored, NOT total. anchored counts the rows that CARRY an anchor (a persisted refusal and the tombstone included); rows filed before anchors existed carry none and are counted in without_anchor instead. So a moved_file share is summary["moved_file"] / anchored; computing it against total is a share of a population the number does not describe.

Args:

  • finding_id: Resolve one finding instead of a population

  • status: Status filter; "all" widens to every status (default: open)

  • category: Restrict to one category

  • file: Restrict to one file (the finding's file column)

  • project_dir: The repository the anchors resolve against. Omitting it reports no_root rather than reading whatever tree the server process happens to stand in — a long-lived server's cwd has nothing to do with the tracker a call is about

  • limit: Maximum findings examined (default 10000). 0 means NO findings are examined; a negative value is an error (it used to mean "no limit").

anchor_recaptureA

Rebuild stored location anchors from the git object store. DRY RUN by default.

The sanctioned repair path: meta.loc is writable through update_finding(meta_update=) and NOTHING validates it there, so a hand-assembled object is accepted at the write and read back as unknown(invalid_anchor). This verb builds the object itself, from the same capture the file-time resolver uses.

Four behaviours are specified rather than incidental. A FAILED capture never replaces a valid stored anchor (outcome kept) — the refusal is usually about the environment, and the anchor it would destroy is still good in a clone that has the history. The loc: null tombstone ("do not recapture") is left alone unless force_tombstone says otherwise. The git work runs outside any transaction, and only the version check and the write share one — so a row whose anchor changed while the capture ran is reported stale and left to the other writer.

include_unanchored widens the POPULATION to rows that never carried an anchor at all — every finding filed before the capture seam landed, since capture runs only when a genuine new finding is filed. They report would_backfill/backfilled, never folded into would_update/updated: "acquired an anchor for the first time" is the number this exists to produce. It is NOT force_tombstone (a loc: null tombstone is a key that is present and null, and this flag never touches it) and it is NOT a fingerprint backfill — nothing here reads or writes that column.

Args:

  • finding_id: Repair one finding instead of a population

  • status: Status filter; "all" widens to every status (default: open)

  • category: Restrict to one category

  • file: Restrict to one file (the finding's file column)

  • project_dir: The repository to capture from. Omitting it makes every capture refuse with no_root, which by the rule above leaves every valid anchor untouched

  • apply: Write the rebuilt anchors (default: report only)

  • force_tombstone: Overwrite a loc: null tombstone

  • include_unanchored: Also take rows carrying no loc key at all (the backfill population). Leaves tombstones alone; still a dry run unless apply is set

  • limit: Maximum findings examined (default 10000). 0 means NO findings are examined; a negative value is an error (it used to mean "no limit").

codemerge_startA

Start a new merge session for a branch.

Args:

  • session_id: Unique identifier for this merge session

  • branch: Git branch name being merged

  • description: Human-readable description of the work

  • base_commit: Git commit SHA this branch diverged from

  • repo_root: Repo root path (default: cwd)

  • allow_restart: If True, reuse this session_id when its previous session is finished — 'abandoned' or 'done'. Restarting DELETES that session's file claims, so re-claim anything you still need. It does NOT restart a live one: starting over an 'active' or 'merging' session is an error whether or not this is set.

codemerge_claimB

Claim a file as being modified by this session.

Args:

  • session_id: The merge session ID

  • file_path: File path being modified (relative to repo root)

codemerge_checkA

Check for overlapping file claims with other sessions.

Returns whether the session is clean to proceed, lists any conflicts, and records the current main HEAD for CAS comparison at merge time.

Args:

  • session_id: The merge session ID

  • main_changed_files: Files changed on main since base (optional, for overlap check)

codemerge_mergeA

Acquire the merge lock and proceed with merging.

Uses compare-and-swap on main HEAD to prevent races. If main has moved since check, returns proceed=False with reason='main_moved'. If another session holds the lock, returns proceed=False with reason='lock_held'.

Args:

  • session_id: The merge session ID

  • expected_main_head: The main HEAD SHA recorded during codemerge_check

codemerge_finishA

Finish a merge session and release the lock.

Call this after codemerge_merge() returned proceed=true; the session must be in 'merging' state or this refuses in BOTH directions of success.

Args:

  • session_id: The merge session ID

  • success: True if the merge succeeded (status→done). False if the git merge/cherry-pick failed (status→active): the lock is released and the session stays alive so it can try again. False does NOT close the session — use codemerge_abandon for that.

codemerge_abandonA

Close a session for good, so its files stop blocking everyone else.

This is the way OUT of a session that will not be merged under its own lock — including the case an agent hits routinely: the branch was integrated by some other route (a merge harness holding its own lock), so codemerge_merge refuses with reason='main_moved' and the session is stranded in 'active', which codemerge_finish will not accept. Until it is abandoned, its claimed files are reported as conflicts to every later session, with no expiry — so closing it is what keeps codemerge_check worth consulting.

What it does, stated exactly: the session's claim rows are NOT deleted, they stop being reported, because the conflict query selects on session status. The merge lock is released only if this session holds it.

Re-issuing it is safe: a second call on an already-abandoned session changes nothing but the timestamps. A 'done' session is REFUSED — that would erase the record of a merge that succeeded — and an unknown session_id is an error.

Args:

  • session_id: The merge session ID. Whatever session is NAMED is the one closed; nothing ties it to the caller, so passing a stale or mistyped id closes someone else's work.

codemerge_sessionsA

List merge sessions with claim counts.

Args:

  • status: Filter by status ('active', 'merging', 'done', 'abandoned'). Omit for all sessions.

codemerge_statusA

Dashboard summary: session counts by status, total active claims, and who (if anyone) holds the merge lock.

codemerge_claimsA

List all files a session has claimed, in claim order.

Args:

  • session_id: The merge session ID.

milestone_createA

Create a new milestone.

Args:

  • id: Slug identifier, e.g. 'release/1.2' or 'stream/security'.

  • kind: 'release' or 'stream'. Streams never close.

  • description: Short charter for the milestone.

  • target_date: ISO date (e.g. '2026-06-30'). Optional, releases only.

milestone_updateA

Update mutable fields of a milestone. id and kind are immutable.

Args:

  • id: Milestone slug.

  • description: New description (or None to skip).

  • target_date: New ISO target date (or None to skip).

  • state: New state (open / closing / shipped / archived).

milestone_listA

List milestones with optional filters.

Args:

  • kind: 'release' or 'stream'.

  • state: 'open' / 'closing' / 'shipped' / 'archived'.

milestone_statusA

Detailed rollup for one milestone: item counts by status / size, blockers, branch-only items, days to target.

Args:

  • id: Milestone slug.

milestone_add_itemA

Attach an item (bug / requirement / external) to a milestone.

Args:

  • milestone_id: Target milestone slug.

  • item_kind: 'bug' (CB-N), 'requirement' (FR-N), or 'external'.

  • item_ref: The id of the underlying entity (must exist for bug/req).

  • size: 'large' (worktree+sprint), 'small' (1-2h), 'triage' (minutes).

  • priority: Lower = higher priority. Default 100.

  • acceptance: Markdown acceptance criteria. Required for size='large'.

  • linked_frs: Optional list of FR ids to link (used by pull_next eligibility).

milestone_move_itemB

Move an item to a different milestone.

Args:

  • item_ref: The item to move (e.g. CB-5).

  • to_milestone: Destination milestone slug.

  • reason: One-line audit reason.

milestone_set_statusB

Set an item's status. Records done_commit if status is terminal.

Args:

  • item_ref: The item id (e.g. CB-5).

  • status: open / in_progress / done / deferred / dismissed.

  • commit: SHA where the work landed on main (recorded for terminal status).

  • reason: Optional audit reason.

milestone_audit_queryA

Audit log query with filters. Returns most-recent rows first.

Args:

  • milestone_id: Filter by milestone slug.

  • item_ref: Filter by item id.

  • actor: Filter by actor.

  • since: ISO datetime — only rows at or after this time.

  • limit: Max rows (default 200). 0 means NO rows; a negative value is an error (it used to mean "no limit").

triage_inboxA

List open items in stream/triage, oldest first.

Args:

  • limit: Max rows (default 50).

triage_dismissA

Mark a triage item as dismissed. Propagates to the underlying entity: bug → finding 'not_a_bug'; requirement → requirement 'obsolete'; external → no propagation.

Args:

  • bug_id: The item id (e.g. CB-5).

  • reason: Required one-line audit reason.

triage_promoteA

Move a triage item to a target milestone.

Args:

  • bug_id: The item id (e.g. CB-5).

  • to_milestone: Destination milestone slug.

  • size: 'large' / 'small' / 'triage'. Default 'small'.

  • acceptance: Required for size='large'.

  • priority: Lower = higher priority. Default 100.

  • linked_frs: FR ids linked to this item (required for size='large' bugs in release milestones to be pull-eligible).

pull_nextA

Claim the next eligible item for the calling agent. Returns the item dict or None if nothing eligible.

Priority: stream/security > release/* (earliest target_date) > stream/triage > stream/maintenance.

Args:

  • agent_id: Stable id for the calling agent. Used as actor in audit.

  • capacity: Dict like {'large':1,'small':2,'triage':5}. Defaults to those values if not provided.

release_itemA

Free agent capacity for an item.

Args:

  • item_ref: The item id (e.g. CB-5).

  • status: 'done' (terminal) or 'abandoned' (returns item to 'open').

  • commit: SHA where the work landed (recorded if status='done').

wip_statusA

Snapshot of agent_capacity. agent_id=None returns all agents.

Args:

  • agent_id: Filter to one agent (None = all).

milestone_reconcileA

One-time repair (CB-107) for stream items whose source finding or requirement resolved before the status-change hook existed (CB-26). The hook keeps new resolutions in sync; this is the retroactive fix for rows it never saw.

DRY RUN BY DEFAULT — without apply=true nothing is written, and the response still lists every candidate transition it WOULD make. This is a bulk mutation, and CLAUDE.md is explicit that a repair tool which writes by default is how it becomes an accident, so the dry-run default is load-bearing and this wrapper refuses to weaken it.

Args:

  • apply: Must be a literal JSON boolean. Defaults to false (dry run). Rejected outright for any other JSON type (a string like "false", a number, null) rather than coerced by truthiness — an MCP client sends JSON over the wire, and Python's bool("0") and bool("false") are both True, which would silently turn a client's intended dry run into a write (CB-82's class of bug). CB-151: strict typing refuses this at the pydantic boundary before the tool body runs, closing the one hole the previous union annotation + isinstance check left open (1.0/0.0 coerced to a real bool before isinstance could see it).

mark_branch_onlyA

Flag an item as living on a feature branch (not yet integrated). Called by worktree-setup.sh when a branch is created.

Args:

  • item_ref: The item id (e.g. CB-5).

  • branch_name: Git branch holding the work.

mark_integratedA

Mark an item as merged to main. Sets done_commit, status='done', clears branch_only. Called by worktree-finish.sh.

Args:

  • item_ref: The item id (e.g. CB-5).

  • commit: Commit SHA where the work landed on main.

milestone_closeA

Close a release milestone. Refuses if items are unfinished, on a branch, or have unresolved blockers. Streams cannot be closed.

Args:

  • id: Milestone slug (must be kind='release').

  • force: Override the close-gate (still won't close streams). Audit-logged.

  • reason: Audit reason for the close.

milestone_deferC

Move an item to stream/maintenance (or another milestone) and mark it deferred.

Args:

  • item_ref: The item to defer.

  • to_milestone: Destination (default 'stream/maintenance').

  • reason: Optional audit reason.

reqs_addB

Add a requirement.

Args:

  • req_id: Requirement ID (e.g. FR-001)

  • description: What the system shall do

  • section: Section name (e.g. "1.10 Document Sorting")

  • priority: must, should, or could

  • status: planned, partial, implemented, verified, superseded, obsolete

  • source: Where this requirement came from (e.g. Take 26, NEW)

  • test_coverage: Test file name(s)

  • tags: Optional tags

  • meta: Optional metadata

reqs_updateA

Update a requirement's status, description, or metadata.

Args:

  • req_id: Requirement ID (e.g. FR-001)

  • status: New status: planned, partial, implemented, verified, superseded, obsolete

  • description: Updated description

  • priority: Updated priority: must, should, could

  • section: Updated section name

  • test_coverage: Updated test file reference

  • notes: Notes (stored in meta.notes). REPLACES the stored notes wholesale. If meta_update also carries a "notes" key, the meta_update value is the one that lands — see meta_update.

  • tags: Replace tags

  • meta_update: Merge metadata keys. notes and meta_update compose over ONE dict: notes replaces first, meta_update merges LAST. So passing both notes= and meta_update={"notes": ...} in a single call is neither an error nor a refusal — meta_update wins the collision, on every key it names. That precedence is deliberate: meta_update names the storage key directly, so it is the repair path for a key no other argument can write. Unlike the findings update tool there is no append_note here, so there is no third writer.

reqs_queryA

Search and filter requirements.

Supports lookup by ID via id= (single) or ids= (batch). Missing IDs are silently absent from the result. For a strict single-ID fetch that errors on miss, use reqs_get.

Args:

  • id: Fetch a single requirement by exact ID (e.g. FR-001)

  • ids: Fetch multiple requirements by ID list; missing IDs are skipped

  • status: Filter by status (planned, partial, implemented, verified, superseded, obsolete, deferred). Use 'deferred' to find requirements with active blockers.

  • priority: Filter by priority (must, should, could)

  • section: Filter by section (substring match)

  • search: Search in description and ID

  • source: Filter by source (substring match)

  • tag: Filter by tag

  • group_by: Group by: section, status, priority, source

  • limit: Max results. A limit you PASS is always honoured: 0 means NO results, and it means that with id/ids too (CB-158 — an id list used to raise any smaller limit to fit itself, so limit=0 came back full). A negative value is an error (it used to mean "no limit"). Omit it and the page size is 100, widened to fit an ids list so a batch lookup returns every id it asked for.

  • offset: Pagination offset

reqs_getA

Fetch a single requirement by ID with full body.

Raises a not-found error if the ID does not exist. For lenient batch lookup that silently drops missing IDs, use reqs_query(ids=[...]).

Args:

  • req_id: The requirement ID (e.g. FR-001)

reqs_statsA

Aggregated requirement counts by status x priority.

Args:

  • group_by: Group by: status, priority, section, source

reqs_summaryA

Dashboard overview --- status breakdown, priority split, section progress, requirements without tests, deferred counts. Start here.

reqs_verifyA

Verify requirements for issues.

Runs automated checks to find problems:

  • tests: do referenced test files actually exist?

  • ids: duplicate IDs, numbering gaps

  • status: contradictions (description says superseded but status says planned)

Args:

  • checks: List of checks to run (default: all). Options: tests, ids, status

  • project_dir: Project root for test file verification (default: cwd)

reqs_importA

Import requirements from a REQUIREMENTS.md file.

Parses markdown tables with columns: | ID | Requirement | Priority | Status | Source | Test Coverage |

Uses INSERT OR REPLACE, so re-importing updates existing entries.

Args:

  • markdown_path: Path to the REQUIREMENTS.md file

reqs_embedA

Store an embedding vector for a requirement.

YOU compute the embedding, in your own process, and pass the finished numbers here. This tool never receives the requirement's text. codebugs stores the vector in its own local SQLite file and sends it nowhere. (Scope, stated precisely rather than loudly, because a promise wider than its check is worse than no promise. The route above is the claim. A test enforces two narrower things beside it: this package's own source imports none of the socket-opening modules that test lists, and it imports nothing at all from outside the package and the standard library without a declared, reasoned entry — so a network client nobody anticipated is still refused. Neither says "codebugs cannot reach the network": the MCP transport your client is talking over is a separate layer, and it is not covered.)

Because there is no embedding provider inside codebugs, nothing here knows the "right" dimensionality — it is whatever the first stored vector had. So the vector is refused if it is empty, contains a non-number, contains NaN or infinity, or has a different number of components than the vectors already stored in this tracker. Each of those would otherwise break reqs_search_similar: a mismatched width makes it unable to score the other rows, and a NaN makes a row drop out of every result with no error at all.

Once a tracker holds vectors of one width you cannot switch embedding model: there is no clear-and-re-embed operation in this package. reqs_embedding_stats reports which widths are actually present.

Args:

  • req_id: Requirement ID

  • embedding: Float vector. Any dimensionality, but the SAME one for every requirement in a given tracker.

reqs_batch_embedA

Store embeddings for multiple requirements at once.

Same preconditions as reqs_embed: you compute the vectors yourself and pass finished numbers, the requirement text never reaches this tool, and codebugs stores them locally and sends them nowhere.

Every vector in one call must have the same number of components as every other vector in the call AND as the vectors already stored in this tracker; empty vectors, non-numbers, NaN and infinity are refused. The self-consistency rule is a separate one: in an empty tracker there is nothing to compare against, so without it a single call could create the mixed state the rules exist to prevent.

Args:

  • embeddings: Dict mapping requirement ID to float vector

reqs_search_similarA

Find requirements semantically similar to a query.

Pass a query embedding (from the same model used to embed requirements). You compute it yourself; no text is sent anywhere by this tool, and the query vector is not stored.

Requirements whose stored vector has a different number of components than your query are EXCLUDED from the search rather than compared, so one foreign vector can no longer make the whole search fail. That also means they are invisible here: if you get fewer results than you expect, call reqs_embedding_stats, which reports which widths this tracker holds. A query vector that is empty or contains NaN or infinity is refused, because it would match nothing and return an empty list indistinguishable from an empty tracker.

Returns requirements ranked by cosine similarity.

Args:

  • query_embedding: Query vector

  • limit: Max results (default 10)

  • min_similarity: Minimum cosine similarity (default 0.3)

  • status: Optional status filter

reqs_embedding_statsA

Report on embedding coverage --- how many requirements have embeddings.

This tool takes no input at all, so it is not a privacy surface and carries no precondition block of its own; that is said explicitly rather than left as an omission a reader has to interpret.

Beyond coverage it reports dimensions --- which vector widths this tracker actually holds, and how many rows each --- plus mixed, true when there is more than one. That is the channel for noticing a tracker that received vectors from two different embedding models: reqs_search_similar silently excludes rows of a width other than your query's, and being able to see the split here is what keeps that from looking like "nothing is similar". Both keys are always present; an empty dimensions list means no vectors are stored, never that the check did not run.

similarity_checkA

Preview what the file-time annotator would stamp for an observation.

Applies EXACTLY the resolver's policy: same candidate pool (live + dismissed, same category, newest 500), same minimum-text-length gate, same scoring. Advisory only — nothing is written.

Args:

  • description: The observation's description text

  • category: Category to search within (annotation never crosses it)

  • meta: Optional observation meta (volatile values are stripped from the text before scoring, same as fingerprint normalization)

  • threshold: Minimum similarity in [0, 1] (default 0.7, calibrated)

  • limit: Max matches returned (default 5)

similarity_reportA

Offline grouping scrub: similarity families as auditable evidence.

Families are connected components with min_pair_score and edge lists — the dry run for any future backfill/merge (the blocked backfill card); no merge is performed or implied. Default population is LIVE rows; pass status= to widen (the sentinel "all" means every status). Wall-clock is quadratic per category block (~115k pair comparisons on a 3k-row tracker); prefer the CLI for very large trackers.

Args:

  • threshold: Minimum similarity in [0, 1] (default 0.7, calibrated)

  • category: Restrict to one category

  • status: Widen/narrow the population (default: live statuses; "all")

  • family_limit: Max families returned (totals stay visible)

  • member_limit: Max members per family (totals stay visible)

codesweep_createA

Create a new sweep for batch iteration over items.

Args:

  • name: Optional human-readable name (must be unique)

  • description: What this sweep is for

  • default_batch_size: Default items per batch (default: 10)

  • lifecycle: Ordered list of allowed states (default ["pending","done"]). For retro-style workflows: ["DETECTED","CONFIRMED","ESCALATED", "POSTPONED","RESOLVED","DROPPED"].

  • terminal_states: States that count as "processed" (default ["done"]).

  • transitions: Optional dict[state, list[allowed_next_state]] for DAG-constrained lifecycles. None = unconstrained transitions.

codesweep_addA

Add items to a sweep. Atomic upsert: existing items have their recurrence_count bumped instead of being silently skipped, their last_seen updated, and their archive flag cleared (R5: re-detected archived items un-archive automatically).

Args:

  • sweep_ref: Sweep ID (SW-N) or name

  • items: Item identifiers to add

  • tags: Optional tags applied to this batch (overwrite on bump)

Returns: {sweep_id, added, recurrence_bumped, duplicates_skipped (alias)}

codesweep_nextA

Get next batch of unprocessed (non-terminal, non-archived) items in insertion order.

Args:

  • sweep_ref: Sweep ID (SW-N) or name

  • limit: Batch size (overrides sweep default). 0 means NO items; omit it to use the sweep's own default batch size. A negative value is an error (it used to mean "no limit").

  • tags: Filter to items matching any of these tags

codesweep_markA

Mark items by state transition.

Args:

  • sweep_ref: Sweep ID (SW-N) or name

  • items: Item identifiers to mark

  • processed: Legacy mode — True maps to first terminal state, False to first non-terminal state. Omit it entirely (the default) to get the same effect as True. MUTUALLY EXCLUSIVE with state: sending both is an error, including when the two happen to agree, because state names one state and processed names a class of them.

  • state: Explicit target state. Validated against the sweep's lifecycle and transitions DAG (if declared). Mutually exclusive with processed — send one or the other, never both.

codesweep_statusA

Sweep overview — total/processed/remaining/archived counts, per-tag and per-state breakdowns. Archived entries are excluded from total/processed/ remaining and reported separately as archived.

Args:

  • sweep_ref: Sweep ID (SW-N) or name

codesweep_archiveA

Archive a sweep. Archived sweeps are excluded from codesweep_list by default.

For entry-level archive, use codesweep_archive_items.

Args:

  • sweep_ref: Sweep ID (SW-N) or name

codesweep_archive_itemsA

Selectively archive entries within a sweep (soft-delete).

Archived entries are excluded from codesweep_next, codesweep_status totals, and default codesweep_list_items. They remain matchable by codesweep_add for recurrence detection — re-adding un-archives them with recurrence_count carried forward (R5 invariant).

At least one filter is required.

Args:

  • sweep_ref: Sweep ID (SW-N) or name

  • items: Specific item identifiers to archive. An EXPLICITLY EMPTY list selects nothing; passing it together with where_status or older_than is an ERROR, because those filters would then be silently ignored. Omit items entirely to archive by filter.

  • where_status: Archive entries currently in this state

  • older_than: Duration spec — '30d', '2w', '6m', '1y'. Compares against the entry's last activity timestamp.

  • reason: Free-form reason recorded on each archived entry

codesweep_list_itemsA

List items in a sweep with optional filters.

Args:

  • sweep_ref: Sweep ID (SW-N) or name

  • state: Filter to a specific state

  • tag: Filter to items having this tag

  • include_archived: Include archived entries alongside live ones

  • archived_only: Show only archived entries. Mutually exclusive with include_archived: passing both as true is an error (it used to mean archived-only, returning FEWER entries than include_archived alone).

  • limit: Max number of entries to return. 0 means NO entries; omit it for no limit. A negative value is an error (it used to mean "no limit").

codesweep_listB

List all sweeps with summary counts.

Args:

  • include_archived: Include archived sweeps (default: false)

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/faxik/codebugs'

If you have feedback or need assistance with the MCP directory API, please join our Discord server