Internal helpers and authentication
These source-derived modules are not root-level public exports. See identity models for author configuration and tool API for registered operations.
runner
Subprocess runners for git, gh, and uv commands.
detect_package_name(project_path)
Read the package name from pyproject.toml.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
project_path
|
Path
|
Project root containing |
required |
Returns:
| Type | Description |
|---|---|
str | None
|
Package name or |
Source code in packages/axm-git/src/axm_git/core/runner.py
find_git_root(path)
Find the git repository root containing path.
Uses git rev-parse --show-toplevel which walks up the directory
tree, supporting mono-repo and workspace layouts where .git
lives above the package directory.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
Any directory that may be inside a git repository. |
required |
Returns:
| Type | Description |
|---|---|
Path | None
|
Repository root as a |
Path | None
|
inside a git repository. |
Source code in packages/axm-git/src/axm_git/core/runner.py
gh_available()
Check whether the GitHub CLI is installed and authenticated.
Source code in packages/axm-git/src/axm_git/core/runner.py
not_a_repo_error(stderr, path)
Build a ToolResult for a failed git command.
If stderr contains "not a git repository" and path has
child directories that are git repos, the error message is enriched
with suggestions. Otherwise a standard error is returned.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stderr
|
str
|
Stderr output from the failed git command. |
required |
path
|
Path
|
Directory that was used as |
required |
Returns:
| Type | Description |
|---|---|
ToolResult
|
|
Source code in packages/axm-git/src/axm_git/core/runner.py
parse_porcelain_z(status_stdout)
Parse git status --porcelain -z output into {path, status} rows.
Records are NUL-terminated rather than newline-terminated, so paths with
spaces are emitted verbatim (unquoted, unescaped). Rename/copy entries
(R/C) span two NUL-separated fields — XY <space>dest followed
by the original source path — so the destination is kept as path and
the trailing source field is consumed and discarded.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
status_stdout
|
str
|
Raw stdout from |
required |
Returns:
| Type | Description |
|---|---|
list[dict[str, str]]
|
List of |
Source code in packages/axm-git/src/axm_git/core/runner.py
reset_paths(paths, git_root)
Unstage exactly paths via a scoped git reset -- <paths>.
Restoration is strictly scoped to paths: it never runs a bare
git reset (which would unstage the whole index, including third-party
staged files) and never touches the worktree (no checkout/clean). A
no-op when paths is empty.
Source code in packages/axm-git/src/axm_git/core/runner.py
resolve_default_branch(working_dir)
Resolve the repository's default branch.
Reads git symbolic-ref refs/remotes/origin/HEAD (e.g.
refs/remotes/origin/master) and strips the
refs/remotes/origin/ prefix. Falls back to "main" when the
command fails or returns an empty/unexpected value (for instance a
repo with no origin/HEAD ref).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
working_dir
|
Path
|
A directory inside the git repository. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The default branch name, or |
Source code in packages/axm-git/src/axm_git/core/runner.py
run_gh(args, cwd, *, timeout=DEFAULT_GH_TIMEOUT)
Run a GitHub CLI command.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
args
|
list[str]
|
gh subcommand and arguments. |
required |
cwd
|
Path
|
Working directory (project root). |
required |
timeout
|
float | None
|
Subprocess timeout in seconds (default 120.0). Use
|
DEFAULT_GH_TIMEOUT
|
Returns:
| Type | Description |
|---|---|
CompletedProcess[str]
|
Completed process result with |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If |
TimeoutExpired
|
If the command exceeds timeout.
Callers should catch and convert via :func: |
Source code in packages/axm-git/src/axm_git/core/runner.py
run_git(args, cwd, *, timeout=DEFAULT_GIT_TIMEOUT)
Run a git command in the given directory.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
args
|
list[str]
|
Git subcommand and arguments (e.g. |
required |
cwd
|
Path
|
Working directory (project root). |
required |
timeout
|
float | None
|
Subprocess timeout in seconds (default 30.0). Use
|
DEFAULT_GIT_TIMEOUT
|
Returns:
| Type | Description |
|---|---|
CompletedProcess[str]
|
Completed process result with |
Raises:
| Type | Description |
|---|---|
TimeoutExpired
|
If the command exceeds timeout.
Callers should catch and convert via :func: |
Source code in packages/axm-git/src/axm_git/core/runner.py
stage_spec_files(files, git_root, *, working_dir=None, warnings=None)
Stage each file in files, returning an error message on failure.
Paths in files are resolved against git_root first, then against working_dir (if provided and distinct), so both git-root-relative and package-relative inputs work transparently. Absolute inputs are accepted when they point inside git_root.
Tracked-but-deleted files (git status D) are staged as deletions.
Gitignored files are skipped with a warning appended to warnings.
Truly missing files (never tracked) produce a clear diagnostic error
listing every absolute path that was attempted.
Source code in packages/axm-git/src/axm_git/core/runner.py
staged_delta(before, after)
Return the sorted paths staged between two index snapshots.
The delta is after - before — exactly the paths a staging operation
introduced, excluding anything a third party had already staged before
the call. Sorted for deterministic output.
Source code in packages/axm-git/src/axm_git/core/runner.py
suggest_git_repos(path)
Find immediate child directories that are git repositories.
Scans one level deep for subdirectories containing a .git dir.
Returns a sorted list of directory names. If path is itself a
git repository (has .git/ at root), returns an empty list.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
Directory to scan. |
required |
Returns:
| Type | Description |
|---|---|
list[str]
|
Sorted list of child directory names that are git repos. |
Source code in packages/axm-git/src/axm_git/core/runner.py
timeout_error_result(exc)
Build a ToolResult for a subprocess.TimeoutExpired.
Source code in packages/axm-git/src/axm_git/core/runner.py
semver
Semantic versioning — parse commits and compute next version.
VersionBump
dataclass
Result of a semver computation.
Attributes:
| Name | Type | Description |
|---|---|---|
current |
str
|
Current tag (e.g. |
next |
str
|
Next tag (e.g. |
bump |
str
|
Bump type ( |
commits |
list[str]
|
One-line commit summaries since last tag. |
breaking |
bool
|
Whether a breaking change was detected. |
Source code in packages/axm-git/src/axm_git/core/semver.py
classify_commit(subject)
Classify a single conventional-commit subject.
Reuses the module regexes so per-commit labelling stays consistent
with :func:compute_bump. Internal-public: importable by sibling
tools (e.g. release_diff) but intentionally absent from __all__.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
subject
|
str
|
A commit subject line, optionally prefixed by a short
hash ( |
required |
Returns:
| Type | Description |
|---|---|
str
|
|
bool
|
( |
tuple[str, bool]
|
|
tuple[str, bool]
|
when the subject carries a conventional prefix, falling back to |
tuple[str, bool]
|
|
tuple[str, bool]
|
commits or |
tuple[str, bool]
|
logic (:func: |
Source code in packages/axm-git/src/axm_git/core/semver.py
compute_bump(commits, current_tag)
Compute the next semver version from commit messages.
Rules (pre-1.0, i.e. major == 0):
- feat!: or BREAKING CHANGE: → minor bump
- feat: → minor bump
- everything else → patch bump
Rules (post-1.0):
- feat!: or BREAKING CHANGE: → major bump
- feat: → minor bump
- everything else → patch bump
Accepts both git log --oneline lines (<short-hash> <message>)
and raw conventional-commit messages (feat: x). The leading token
is stripped only when it matches a short-hash shape
(hex, 3-40 chars); otherwise the whole line is treated as the message.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
commits
|
list[str]
|
Commit lines, either oneline ( |
required |
current_tag
|
str
|
Current version tag (e.g. |
required |
Returns:
| Type | Description |
|---|---|
VersionBump
|
VersionBump with computed next version. |
Source code in packages/axm-git/src/axm_git/core/semver.py
parse_tag(tag)
Parse a semver tag string into (major, minor, patch).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tag
|
str
|
Version string, with or without |
required |
Returns:
| Type | Description |
|---|---|
tuple[int, int, int]
|
Tuple of (major, minor, patch). |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the tag doesn't match semver format. |
Source code in packages/axm-git/src/axm_git/core/semver.py
branch_naming
Branch naming convention for ticket-driven workflows.
CONVENTIONAL_COMMIT_FORMAT = '<type>[(scope)][!]: <description>'
module-attribute
Human-readable expected Conventional Commit summary format.
branch_name_from_ticket(ticket_id, title, labels)
Build a deterministic branch name from ticket metadata.
Produces names in the format <type>/<TICKET_ID>-<slug> where
type is derived from the ticket labels.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ticket_id
|
str
|
Ticket identifier (e.g. |
required |
title
|
str
|
Ticket title used to generate the slug. |
required |
labels
|
list[str]
|
Ticket labels used to determine the branch type. |
required |
Returns:
| Type | Description |
|---|---|
str
|
A URL-safe branch name. |
Source code in packages/axm-git/src/axm_git/core/branch_naming.py
is_conventional_commit(message)
Return whether message matches the Conventional Commit format.
A message is conventional when it starts with type: or
type(scope): (an optional breaking ! marker is allowed before
the colon, e.g. feat!: or fix(scope)!:) followed by a space.
This is the single source of truth shared by branch-type inference
and the git_commit validation path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
message
|
str
|
Commit summary line to validate. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
Source code in packages/axm-git/src/axm_git/core/branch_naming.py
slugify(title, *, max_len=40)
Convert a title string into a URL-safe slug.
Lowercases the input, replaces non-alphanumeric characters with hyphens, collapses consecutive hyphens, and strips leading/trailing hyphens.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
title
|
str
|
The title to slugify. |
required |
max_len
|
int
|
Maximum length of the slug (default 40). Truncation prefers word boundaries when possible. |
40
|
Returns:
| Type | Description |
|---|---|
str
|
A sanitized slug, or |
str
|
contains only special characters. |
Source code in packages/axm-git/src/axm_git/core/branch_naming.py
commit_cmd
build_commit_cmd(message, body, *, skip_hooks=True, author=None)
Build the git commit argument list.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
message
|
str
|
Commit summary line. |
required |
body
|
str | None
|
Optional extended commit body. |
required |
skip_hooks
|
bool
|
Append |
True
|
author
|
str | None
|
Git |
None
|
Source code in packages/axm-git/src/axm_git/core/commit_cmd.py
commit_spec
Shared commit spec validation and Git hook autofix-retry plumbing.
AutofixRetry
dataclass
Outcome of an autofix-aware commit retry.
Attributes:
| Name | Type | Description |
|---|---|---|
result |
_GitResultLike
|
The final GitResult-like object (returncode/stdout/stderr). |
retried |
bool
|
Whether a re-stage + retry was actually performed. |
auto_fixed |
list[str]
|
Files the commit hook modified, captured before
re-staging (the subsequent |
Source code in packages/axm-git/src/axm_git/core/commit_spec.py
attempt_commit_with_autofix_retry(cmd, files, git_root, first_result, *, working_dir=None)
Re-stage + retry cmd once when a commit hook auto-fixed files.
Detection is on the combined stdout+stderr of first_result: when the
canonical "files were modified" marker is present, the modified
files are captured (git diff --name-only before re-staging), the
spec files are re-staged via the subdir-aware resolver, and cmd is
retried once. Otherwise first_result is returned unchanged.
Source code in packages/axm-git/src/axm_git/core/commit_spec.py
validate_commit_spec(spec)
Validate a commit_spec dict (pure; stricter merged contract).
Requires a non-empty message AND a non-empty files list — the
stricter of the two prior per-surface validators. Returns
(spec, error_message) where spec is None when an error is set;
each surface wraps the error string in its own result type.
Source code in packages/axm-git/src/axm_git/core/commit_spec.py
phase_commit
Retrieve commit hashes for AXM protocol phases.
get_phase_commit(working_dir, phase_name, *, message_format='[axm] {phase}')
Retrieve the commit hash associated with an AXM phase.
Searches git log for commits whose message matches the format used by legacy phase integrations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
working_dir
|
Path
|
Repository root path. |
required |
phase_name
|
str
|
Phase name to search for. |
required |
message_format
|
str
|
Message pattern used by the phase integration.
(default |
'[axm] {phase}'
|
Returns:
| Type | Description |
|---|---|
str | None
|
Short commit hash if found, |
Source code in packages/axm-git/src/axm_git/core/phase_commit.py
pr_recovery
Shared recovery for an already-existing GitHub pull request.
When gh pr create fails because a PR already exists for the branch,
:class:~axm_git.tools.pr.GitPRTool recovers it via gh pr view. This
module factors that recovery into a result-agnostic helper.
PRRecovery
dataclass
Normalized result of recovering an existing pull request.
On success error is None and url/number are populated.
On failure error carries the reason and url/number are empty.
Source code in packages/axm-git/src/axm_git/core/pr_recovery.py
ok
property
Whether recovery succeeded.
is_already_exists(stderr)
Return True when stderr signals an existing PR (case-insensitive).
recover_existing_pr(working_dir)
Resolve the existing PR via gh pr view after an 'already exists' error.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
working_dir
|
Path
|
Repository working directory. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
A |
PRRecovery
|
class: |
PRRecovery
|
success, or with |
Source code in packages/axm-git/src/axm_git/core/pr_recovery.py
gh_auth
classify_gh_auth(available, returncode)
Classify the GitHub CLI authentication state from observable process state.
Source code in packages/axm-git/src/axm_git/core/gh_auth.py
gh_auth_state()
Return the local GitHub CLI authentication state.
Source code in packages/axm-git/src/axm_git/core/gh_auth.py
credentials
GhAuthDependency
Bases: AuthDependencySpec
GitHub CLI authentication dependency with catalog metadata.
Source code in packages/axm-git/src/axm_git/credentials.py
gh_credentials()
Declare the GitHub CLI session consumed by axm-git.