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
43 changes: 43 additions & 0 deletions .github/scripts/close-issues-on-dev.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env bash
# close-issues-on-dev.sh — close the issues named by pull requests that merged into a branch other than
# dev, once their merge commit is part of dev (see .github/workflows/close-issues-on-dev.yml).
#
# DRY_RUN=1 .github/scripts/close-issues-on-dev.sh # list, close nothing
#
# Needs: gh (authenticated; GH_TOKEN in CI), a checkout with full history, and origin/dev or HEAD on dev.
# LOOKBACK: how many recently merged pull requests to consider (default 300).
set -euo pipefail

DRY_RUN="${DRY_RUN:-0}"
LOOKBACK="${LOOKBACK:-300}"
REPO="${GITHUB_REPOSITORY:-$(gh repo view --json nameWithOwner -q .nameWithOwner)}"
DEV_REF="${DEV_REF:-HEAD}"
[ "$DEV_REF" = HEAD ] && [ "$(git rev-parse --abbrev-ref HEAD)" != dev ] && DEV_REF=origin/dev

closed=0; pending=0
while IFS=$'\t' read -r pr base merge body64; do
[ -n "$merge" ] || continue
[ "$base" = dev ] && continue # GitHub closes these itself
if ! git merge-base --is-ancestor "$merge" "$DEV_REF" 2>/dev/null; then
pending=$((pending + 1)); continue # merged, but not on dev yet
fi
body=$(printf '%s' "$body64" | base64 --decode)
for n in $(printf '%s\n' "$body" | grep -oiE '\b(close[sd]?|fix(e[sd])?|resolve[sd]?)[[:space:]]*:?[[:space:]]+#[0-9]+' \
| grep -oE '[0-9]+$' | sort -un); do
state=$(gh issue view "$n" --repo "$REPO" --json state -q .state 2>/dev/null || echo MISSING)
[ "$state" = OPEN ] || continue
if [ "$DRY_RUN" = 1 ]; then
echo "would close #$n (pull request #$pr into $base, ${merge:0:9} is on dev)"
else
gh issue close "$n" --repo "$REPO" \
--comment "Closed by #$pr, which merged into \`$base\` and reached \`dev\` in ${merge:0:9}."
echo "closed #$n (pull request #$pr into $base)"
fi
closed=$((closed + 1))
done
done < <(gh pr list --repo "$REPO" --state merged --limit "$LOOKBACK" \
--json number,baseRefName,mergeCommit,body \
-q '.[] | [.number, .baseRefName, (.mergeCommit.oid // ""), (.body // "" | @base64)] | @tsv')

verb=closed; [ "$DRY_RUN" = 1 ] && verb="to close"
echo "issues $verb: $closed; merged pull requests not on dev yet: $pending"
44 changes: 44 additions & 0 deletions .github/workflows/close-issues-on-dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# ─────────────────────────────────────────────────────────────────────────────
# A pull request that merges into a release or integration branch (0.2.1, a
# perf branch) never closes the issues it names: GitHub acts on "Closes #N" only
# when the merge lands on the default branch. This closes them when that work
# reaches `dev`.
#
# On every push to dev it looks at pull requests merged into any other branch,
# keeps those whose merge commit is now part of dev, and closes each open issue
# their description closes ("Closes", "Fixes", "Resolves" #N), with a comment
# naming the pull request. "Part of #N" and "Refs #N" are not closing words, so
# umbrella issues stay open.
# ─────────────────────────────────────────────────────────────────────────────
name: close-issues-on-dev

on:
push:
branches: [dev]
workflow_dispatch:
inputs:
dry_run:
description: list what would be closed, close nothing
type: boolean
default: true

permissions:
contents: read
issues: write
pull-requests: read

jobs:
close:
name: close issues whose pull request reached dev
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: close the issues
env:
GH_TOKEN: ${{ github.token }}
DRY_RUN: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run && '1' || '0' }}
run: .github/scripts/close-issues-on-dev.sh
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<h1 align="center">AxiomCode Graph</h1>

<p align="center">
<strong>A god's-eye view of your codebase for AI agents. Stop grepping. Ensure correctness and completeness for any task your AI agent performs.</strong>
<strong>Know what each change breaks in your codebase. Stop grepping. Ensure correctness and completeness for any task your AI agent performs. Ship less buggy code.</strong>
</p>

<p align="center">
Expand Down Expand Up @@ -127,7 +127,7 @@ established relationships that could lead to false positives.

AxiomCode Graph is two parts. The **engine** (`@axiomcode/code-graph` on npm) parses a repository and builds its
graph; it also provides the `axiomcode` command and an MCP server. The **plugin** (`plugins/axiomcode/`) is the
agent-facing frontend: a skill, four MCP tools, and hooks. Install the engine first.
agent-facing frontend: a skill, five MCP tools, and hooks. Install the engine first.

Requirements: **Node ≥ 22.13** and **Python 3** (`python3`, or `python` / `py` on Windows). On Windows, also
[Git for Windows](https://git-scm.com/download/win): the CLI runs under its bash. The engine ships as a prebuilt
Expand All @@ -139,7 +139,7 @@ your platform. No Soufflé and no compiler are needed, with one exception:
> `sudo apt install build-essential python3`. The full `node:*` Docker images already include them.

Before every release, the exact packages that ship are installed without Soufflé and run end to end (every CLI verb,
five languages) on macOS arm64 and x64, Linux x64 and arm64, and Windows x64. Check an install with
six language engines, including HTML/CSS) on macOS arm64 and x64, Linux x64 and arm64, and Windows x64. Check an install with
`axiomcode --version`.

### Installation
Expand Down Expand Up @@ -259,7 +259,7 @@ looked up again in the graph before you see it; the `verified:` line is that che

### Support for agents

Every agent below gets the four MCP tools and the skill; the hooks, which keep the graph current and report what an
Every agent below gets the five MCP tools and the skill; the hooks, which keep the graph current and report what an
edit breaks, run where the last column says so. No hook annotates the agent's own reads and searches.

| Agent | Hooks |
Expand Down
4 changes: 2 additions & 2 deletions tests/mcp_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
[os.path.join(ROOT, 'plugins', 'axiomcode', 'AGENTS.md')] + \
sorted(glob.glob(os.path.join(ROOT, 'plugins', 'axiomcode', 'rules', '*.mdc')))

VERB = r'(find|impact|path|tests)'
VERB = r'(context|impact|link|path|tests)'
# a tool named: a call `impact(`, the Claude Code name mcp__plugin_axiomcode_axiomcode__impact, MCP `impact`, or a command
# in backticks (`axiomcode impact x`); `path:line: …` is an answer's shape, not the tool
MENTION = re.compile(r'`(?:axiomcode )?' + VERB + r'(?:[ \t][^`\n]*)?`|\bmcp__plugin_axiomcode_axiomcode__' + VERB + r'\b'
Expand Down Expand Up @@ -130,7 +130,7 @@ def controls(tools):
got = len(mismatches(documented(text), fake, 'control'))
if got != want:
bad.append(f"control {text!r}: {got} mismatch(es), want {want}")
if set(tools) != {'impact', 'path', 'tests'}:
if set(tools) != {'context', 'impact', 'link', 'path', 'tests'}:
bad.append(f"the server listed {sorted(tools)}; the MCP tools were not read")
return bad

Expand Down
Loading