Skip to content
rlsbl.utils
On this page

Shared utilities: subprocess runner, tool detection, project root discovery, git helpers, version bumping, changelog extraction, and commit tooling.

#rlsbl.utils

#rlsbl.utils

Shared utilities: subprocess runner, git helpers, version bumping, changelog extraction, commit tooling, and GitHub API queries.

#run

python
def run(cmd, args=None, timeout=120, env=None, cwd=None)

Run a command with args, return trimmed stdout. Raise on failure.

Under --dry-run a non-observe command is recorded rather than executed, and there is no stdout to trim: the carrier standing in for the run is returned instead. A caller that ignores the return value (every mutation that only needed to happen) previews cleanly; a caller that reads the string truncates the preview, which is the honest answer for output that was never produced.

#warn_exception

python
def warn_exception(context: str, exc: Exception) -> None

Print a warning with optional traceback for non-fatal errors.

#require_tool

python
def require_tool(name, purpose=None, fatal=True)

Check that a CLI tool is available on PATH.

Args:

  • name: command name (e.g., "uv", "npm", "go").
  • purpose: optional human-readable reason ("for editable install"),

included in the error message when fatal.

  • fatal: if True, raise FileNotFoundError on missing tool; if False,

return None silently.

Returns the resolved path to the tool, or None if missing and not fatal.

#find_project_root

python
def find_project_root(start=None)

Walk up from start (default: cwd) to find .rlsbl/ or .rlsbl-monorepo/.

Returns the directory path containing the marker, or None if not found. Prefers the nearest ancestor with either marker.

#find_sub_project_root

python
def find_sub_project_root(start=None)

Resolve which PROJECT start (default: cwd) is in, monorepo-aware.

Returns (root, project, workspace_root):

  • root is the sub-project directory in a monorepo and the project root

otherwise, or None when start is in no rlsbl project at all.

  • project is the :class:~rlsbl.workspace.WorkspaceProject start

resolves to, or None outside a workspace (and, in the one case the workspace loader can still produce, when no member claims start).

  • workspace_root is the enclosing workspace, or None.

The monorepo half is why this is not :func:find_project_root: a member whose per-package .rlsbl/ was cleaned up has no marker of its own, so walking up finds the workspace root and answers with the wrong project. Membership is decided by the workspace, which owns the mapping.

This is the resolution the CLI applies before handing a command its project root; it is exposed here so a command that must degrade rather than exit (rlsbl watch runs in any git repository) can ask the same question without the CLI's error paths.

#is_virtual_uv_root

python
def is_virtual_uv_root(project_dir: str) -> bool

True iff project_dir is a virtual uv workspace root.

A virtual root has a pyproject.toml declaring a [tool.uv.workspace] but no [project] table. It aggregates member packages for editable installs but is not itself a distributable package -- it has no name and no version, so pypi.detect() must not claim it and version/name/ publish checks do not apply to it.

Returns False when there is no pyproject.toml, when it cannot be parsed, when there is no [tool.uv.workspace], or when a [project] table is present (a real package that also happens to define a workspace).

#is_clean_tree

python
def is_clean_tree(cwd=None)

Returns True if the git working tree is clean (no uncommitted changes).

cwd selects the repository the probe runs in; None means the process cwd. Every clean-tree question in rlsbl goes through here, so the --no-optional-locks form is spelled once: without it git status refreshes the index and takes index.lock, which in a worktree shared by several sessions can make a concurrent commit fail, and it is also what puts the probe on the observe allowlist so a preview really runs it.

#status_argv

python
def status_argv(*, paths=None, untracked=None)

The argv for a git status --porcelain -z read.

untracked is one of no/normal/all (None leaves git's default); paths become a pathspec, which narrows the answer to those paths without changing the record format. An unknown untracked mode is a hard error rather than a silently-ignored word.

#parse_status_records

python
def parse_status_records(stdout)

The status records in raw git status --porcelain -z stdout.

Split out from :func:working_tree_status so a caller that already holds the output -- the release executor's declared result capture, which runs the read itself -- parses it the same way as everyone else instead of hand-rolling a second parser.

stdout must be the UNSTRIPPED output: an unstaged record starts with a space ( D path), and stripping it shifts every column.

#parse_status_paths

python
def parse_status_paths(stdout)

The changed paths in raw git status --porcelain -z stdout.

The records of :func:parse_status_records, minus their XY status columns: exactly the path list a commit can name.

#working_tree_status

python
def working_tree_status(cwd=None, *, paths=None, untracked=None)

The porcelain status records of the working tree at cwd.

Each record is XY <path>, NOT stripped: the leading status columns carry meaning. :func:working_tree_paths is the parsed form. paths and untracked are the read's two knobs, described on :func:status_argv.

Read in -z mode, which is the whole point. Git's default porcelain output C-QUOTES any path it cannot print literally -- anything non-ASCII, or holding a space, tab, quote or backslash -- so a file really named pkgé/x.txt arrives as "pkg\303\251/x.txt". Every consumer here feeds those paths straight back to git in a commit, where the escaped spelling names nothing, and an operation that already deleted something is then left half-done. -z emits NUL-terminated records with the paths verbatim, so there is no quoting to decode and no decoder to get wrong.

A rename or copy emits its ORIGIN path as a second NUL field; that field is consumed with the record it belongs to rather than returned as a record of its own (it has no status columns and is not a path that changed). This deliberately does NOT go through :func:run, which strips the whole output -- that would eat the leading space of an unstaged record ( D path) and the parsed path would lose its first character.

#working_tree_paths

python
def working_tree_paths(cwd=None, *, paths=None, untracked=None)

Every path the working tree at cwd reports a change for.

The records :func:working_tree_status returns, minus their XY status columns: exactly the path list a commit can name. A rename yields the NEW path (its origin was already consumed), and nothing is unquoted because -z output was never quoted.

paths narrows the read to a pathspec (the answer still names paths relative to the repo root, not to the pathspec), and untracked selects how untracked content is reported -- "all" lists every untracked file instead of collapsing a wholly-untracked directory into one record.

#get_current_branch

python
def get_current_branch(*, cwd)

Returns the current git branch name for the repo at cwd.

cwd is REQUIRED (keyword-only) and must be the project/repo directory: there is intentionally no default that falls back to the process cwd. A default-to-process-cwd let a test run this (and the subsequent push_if_needed) against whatever repo the test process happened to be in -- the real dev repo -- causing a stray git push. Every caller now declares which repo it means.

Raises GitError when HEAD is detached (git returns the literal string "HEAD"), since callers like push_if_needed would silently misbehave by operating on origin/HEAD.

#_resolve_timeout

python
def _resolve_timeout(config, key, default, *, override=None)

Resolve a timeout: explicit override > config key > shipped default.

There is deliberately NO environment-variable layer. A timeout is either declared in the project's config or passed explicitly on the command line; an ambient env var configuring release behavior is exactly the kind of invisible state this tool refuses to have.

A present-but-invalid value (config or override) is a hard error naming the key and the value -- never silently ignored, never silently defaulted.

#get_push_timeout

python
def get_push_timeout(config=None, *, override=None)

Return the push timeout in seconds.

Precedence: override (the --push-timeout CLI flag) > config dict push_timeout > :data:DEFAULT_PUSH_TIMEOUT.

config may be None (override > default only).

#get_check_timeout

python
def get_check_timeout(config=None, *, override=None)

Return the check timeout in seconds.

Precedence: override > config dict check_timeout > :data:DEFAULT_CHECK_TIMEOUT.

config may be None (override > default only).

#get_ci_timeout

python
def get_ci_timeout(config=None, *, override=None)

Return the release CI-wait timeout in seconds.

Precedence: override (the --ci-timeout CLI flag) > config dict ci_timeout > :data:DEFAULT_CI_TIMEOUT.

config may be None (override > default only).

#get_hook_timeout

python
def get_hook_timeout(config=None, *, override=None)

Return the release-hook timeout in seconds, or None for no timeout.

Precedence: override > config dict hook_timeout > None. The default is deliberately "no timeout": release hooks legitimately run whole test suites and deploys, and killing one mid-flight is worse than waiting.

A present-but-invalid value is a hard error (:class:ConfigError).

#validate_timeout_override

python
def validate_timeout_override(config_key, value)

Validate a CLI timeout override, naming the FLAG in the error.

--check-timeout and --hook-timeout reach their consumers by being written into the in-memory config (see apply_timeout_overrides), which means an invalid flag value used to surface much later as "Invalid check_timeout in .rlsbl/config.json" -- pointing the operator at a file they never touched. Validating at the argv boundary keeps the blame where it belongs.

#remote_branch_exists

python
def remote_branch_exists(branch, cwd=None)

Check whether origin/{branch} exists as a valid ref.

#LocalTagState

Tri-state outcome of a local tag lookup.

UNKNOWN is what :func:tag_exists_locally collapses onto False: a preview past its first recorded mutation, where the framework answers every observe with a carrier standing in for a run that did not happen. Callers whose question is "does this tag need creating?" may read that as "no" -- a preview creates no tags either way. Callers whose question is "was this version ever released?" may NOT: absence of an answer is not evidence of absence, and reading it as one is how a healthy release turned into a destroyed-tag diagnosis under --dry-run.

#local_tag_state

python
def local_tag_state(tag, cwd=None)

Is tag in the local repository -- or is the question unanswerable?

The honest form of :func:tag_exists_locally; see :class:LocalTagState for which callers may collapse UNKNOWN.

#tag_exists_locally

python
def tag_exists_locally(tag, cwd=None)

Check whether a git tag exists in the local repository.

Answers False when the question is unanswerable -- a preview past its first recorded mutation, where the framework replies to every observe with a stale carrier. "The tag is not there yet" is the state a preview is describing anyway: a preview creates no tags, so one it cannot see is one the run it previews would still have to create.

Callers that cannot make that reading use :func:local_tag_state.

#tag_exists_on_remote

python
def tag_exists_on_remote(tag, cwd=None)

Check whether a git tag exists on the origin remote.

False when unanswerable, for the same reason as :func:tag_exists_locally.

#RemoteTagState

Tri-state outcome of a commit-aware remote tag lookup.

#RemoteTagResult

Result of resolving a tag to its peeled commit on a remote.

Attributes:

  • state: PRESENT (tag found; commit is set), ABSENT (ls-remote

succeeded but no matching ref), or INCONCLUSIVE (ls-remote failed due to network/auth/timeout; error carries the underlying text).

  • commit: the peeled commit SHA the tag points to. Only set when state

is PRESENT; None otherwise. For annotated tags this is the ^{} peeled line (the commit), not the tag-object SHA.

  • error: underlying error text when state is INCONCLUSIVE; None otherwise.

#remote_tag_commit

python
def remote_tag_commit(tag, cwd=None, remote='origin', timeout=30)

Resolve a tag to the commit it points to on remote (default origin).

Runs git ls-remote --tags <remote> <tag> and returns a tri-state RemoteTagResult:

  • PRESENT with the peeled commit SHA. For annotated tags, ls-remote prints

two lines -- <tag-object-sha> refs/tags/<tag> and <commit-sha> refs/tags/<tag>^{}. The ^{} peeled line is the commit and is preferred. For lightweight tags there is a single line whose SHA is already the commit.

  • ABSENT when ls-remote succeeds but returns no matching ref.
  • INCONCLUSIVE when ls-remote fails (network, auth, timeout); the failure

text is carried in error.

Both refs/tags/<tag> and refs/tags/<tag>^{} are passed as explicit match patterns: a bare <tag> pattern matches only the direct ref and suppresses the peeled ^{} line, which would make annotated tags false-negative to the tag-object SHA instead of the commit.

#TagCommitMap

Every tag in one namespace mapped to the commit it points at.

commits is empty exactly when the namespace holds no tags OR when the probe failed; error distinguishes the two. A reader that cannot tell "no tags" from "could not ask" would report every release as missing the moment a remote became unreachable, which is why the two are separate fields rather than an empty dict standing for both.

#conclusive

python
def conclusive(self) -> bool

#_peel_ref_lines

python
def _peel_ref_lines(lines)

Fold refs/tags lines into {tag: commit}, preferring the peel.

Both ls-remote and for-each-ref emit an annotated tag twice: once as the tag object and once as <tag>^{}, the commit it wraps. The peeled form is the commit and always wins; a lightweight tag has only the direct form, whose object already IS the commit.

#local_tag_commits

python
def local_tag_commits(cwd=None, timeout=30)

Every LOCAL tag mapped to its commit, in ONE git call.

The whole namespace at once rather than one probe per tag: a project with hundreds of archived releases would otherwise spawn hundreds of processes to answer a question for-each-ref answers in a single pass.

#_peel_local_annotated

python
def _peel_local_annotated(mapping, cwd, timeout)

Replace any tag-object SHA in mapping with the commit it wraps.

One rev-parse carrying every tag's ^{} form, so annotated tags cost one extra process for the whole namespace rather than one each.

#remote_tag_commits

python
def remote_tag_commits(cwd=None, remote='origin', timeout=60)

Every tag on remote mapped to its commit, in ONE ls-remote call.

This is why the unpublished-refs check needs no version window: the network cost of checking one release and of checking every release ever made is the same single round trip.

#remote_is_configured

python
def remote_is_configured(remote='origin', cwd=None, timeout=10)

Is remote configured in this repository at all?

A repository with no remote has nothing to be missing FROM, which is a different state from a remote that could not be reached -- the first is a skip, the second is fail-closed.

#resolve_tag_push_plan

python
def resolve_tag_push_plan(tags, cwd=None, remote='origin')

Commit-aware decision for pushing one or more release tags.

Each tag in tags must exist locally; its peeled commit is resolved via git rev-parse refs/tags/<tag>^{} (which yields the commit for both lightweight and annotated tags). For each tag the remote state is resolved with :func:remote_tag_commit:

  • INCONCLUSIVE (ls-remote failed): raise :class:GitError carrying the

underlying error. A release must never push blind after an inconclusive remote probe -- the old bare except Exception: pass skip that pushed anyway is exactly the bug this replaces.

  • PRESENT at a different commit than the local tag: raise :class:GitError

naming the tag, the local SHA, and the divergent remote SHA. A tag must never be force-moved by a release.

  • PRESENT at the same commit: idempotent -- already pushed.
  • ABSENT: needs pushing.

Returns True when at least one tag needs pushing (the caller runs the push; git no-ops the already-present-identical refs pushed alongside the missing ones -- verified: git push origin <present-identical> <absent> exits 0). Returns False when every tag is already present at the matching commit (the caller skips the push entirely -- the idempotent resume/re-entry case).

#push_if_needed

python
def push_if_needed(branch, *, config, cwd, sha=None)

Push the branch to origin if local is ahead of remote.

The push runs with --no-verify: this is a release-internal push, and the pre-push hook exists to catch MANUAL pushes to release branches. There is no environment-variable handshake -- bypassing the hook is expressed by not running it.

Args:

  • branch: branch name to push.
  • config: project config dict forwarded to get_push_timeout.
  • cwd: REQUIRED (keyword-only) repo directory the git commands run from.

There is deliberately no process-cwd default: an unrooted push once executed a real git push from the test-runner's own repo. Callers must pass the project root explicitly.

  • sha: optional explicit commit to publish as <sha>:refs/heads/<branch>

instead of pushing the branch ref by name. The release flow always passes it: the commit that CI verified is the commit that is published, and a ride-in that landed on the local branch after the candidate was pinned can never be swept along by the push.

#extract_changelog_entry_from_text

python
def extract_changelog_entry_from_text(content, version)

Extract a changelog entry for a specific version from a markdown string.

Looks for a heading like '## 1.2.3' and captures everything until the next heading or EOF.

#extract_changelog_entry

python
def extract_changelog_entry(changelog_path, version)

Extract a changelog entry for a specific version.

Looks for a heading like '## 1.2.3' and captures everything until the next heading or EOF.

#check_gh_installed

python
def check_gh_installed()

Check that the gh CLI is installed.

#check_gh_auth

python
def check_gh_auth()

Check that the gh CLI is authenticated for github.com.

The host is named explicitly: github.com is the only forge rlsbl talks to, and the observe allowlist pins this exact argv so the bare gh auth status prefix cannot also admit --show-token.

#find_commit_tool

python
def find_commit_tool()

Detect safegit or fall back to git for committing.

Returns "safegit" if available on PATH, otherwise "git". Prints a one-time warning to stderr when falling back to git.

#has_staged_or_modified

python
def has_staged_or_modified(paths: list[str], cwd: str | None=None) -> bool

Check if any of the given paths have staged or unstaged changes.

When cwd is set, git commands run from that directory and os.path.exists checks are resolved relative to it. Paths must be relative to cwd (or absolute).

#partition_stageable

python
def partition_stageable(paths: list[str], cwd: str | None=None) -> tuple[list[str], list[str]]

Split paths into (stageable, ignored), preserving the given order.

Stageable means git reports some status for the path -- modified, staged, deleted or untracked -- so naming it in a commit contributes something to the tree. A path git reports nothing for contributes nothing: git add stages a no-op and safegit 0.29+ refuses the whole commit with "nothing to commit for : staging it leaves the tree of unchanged".

Ignored means the path is untracked AND matched by a gitignore rule. That is not a no-op, it is a mistake -- a commit was told to carry a file the repository has been configured to exclude -- so it is reported back rather than quietly dropped, and :func:commit_files refuses on it.

Paths must be relative to cwd (or absolute), matching the convention every commit helper here uses.

#assert_git_toplevel

python
def assert_git_toplevel(cwd: str | None, expected_root: str) -> None

Hard-error if the git repo discovered from cwd is not expected_root.

Closes the junk-commit class: a commit whose cwd is a non-git fixture directory (e.g. a TMPDIR created inside the repo) makes git walk UP to the nearest enclosing repo -- the real dev repo -- and commit there. Comparing the resolved git rev-parse --show-toplevel against the caller's declared project root and refusing on mismatch makes that impossible.

Silent no-op when cwd is not inside any git repo (callers gate commits on :func:is_git_repo separately); a mismatch is a hard :class:GitError.

#commit_files

python
def commit_files(message: str, files: list[str], allow_failure: bool=False, autogenerated: bool=True, cwd: str | None=None, expected_root: str | None=None, return_result: bool=False, require_change: bool=False) -> bool

Commit specific files using safegit (preferred) or git.

When autogenerated is True, passes --trailer "Autogenerated: true" to the commit command (supported by git 2.32+ and safegit 0.10.0+).

When cwd is set, the commit tool runs from that directory. File paths must be relative to cwd (or absolute). This is needed in monorepo mode where paths are relative to the repo root but the process CWD is a sub-project directory.

When expected_root is set, the git repo discovered from cwd must be that project root (:func:assert_git_toplevel) or the commit is refused with a hard error -- the guard against committing into the wrong repository.

Returns True on success. When allow_failure is True, catches errors and returns False with a warning to stderr. When False, exceptions propagate. allow_failure covers the refusals below as well: a caller that declared a failed commit survivable does not get a hard error because the file list turned out to be uncommittable. The ONE thing it never silences is expected_root -- committing into the wrong repository is a safety refusal, not a commit failure.

return_result returns the commit run's own result instead of True. Under a preview that result is the framework's carrier standing in for the commit that was recorded rather than made -- the Phase-A executor forwards it into the candidate push, which is how the preview renders the push of a commit that does not exist. Callers that only need success must NOT set it: branching on a carrier truncates the preview. A skipped commit (see below) returns True even under return_result, since no run happened; in live mode nothing reads that value, and a preview never skips.

Unchanged files are never named. Callers list a fixed file set -- the release's finalize step names CHANGELOG.md whether or not the regeneration altered a byte -- and safegit 0.29+ refuses a commit that names a path whose staging leaves the tree unchanged. The list is filtered through :func:partition_stageable first, so every caller inherits the behavior; a gitignored path is a refusal rather than a drop, and an empty result is a stated no-op. require_change turns that no-op into a refusal, for the caller whose expected change MUST have materialized.

The filter is skipped under a preview: the writes a preview's commit would carry were recorded rather than performed, so asking the working tree about them would drop every file from a commit that is itself only recorded.

#is_git_repo

python
def is_git_repo(path: str | None=None) -> bool

Return True if path (default: process cwd) is inside a git work tree.

#commit_scaffold_file

python
def commit_scaffold_file(message: str, files: list[str], *, cwd: str | None=None, expected_root: str | None=None) -> None

Commit a freshly-scaffolded file, failing loudly on commit error.

The scaffold write has already succeeded by the time this is called. Outside a git repository there is nothing to commit, so this is a no-op (matching the project convention: never commit or git-init in a non-git directory). Inside a git repository, a commit failure is a hard error with an actionable message -- it is never silently swallowed, because a scaffolded release file that is on disk but not committed can silently block or corrupt a later release.

Args:

  • message: commit message.
  • files: file paths to commit (relative to cwd or absolute).
  • cwd: directory the commit tool runs from; also the directory whose

git-repo membership is checked.

  • expected_root: when set, the git repo discovered from cwd must be

this project root or the commit is refused (hard error). Guards against a mis-rooted cwd walking up into the wrong repo.

#commit_files_if_changed

python
def commit_files_if_changed(message: str, files: list[str], skip_message: str='No changes to commit.', autogenerated: bool=True, cwd: str | None=None) -> bool

Commit files only if they have actual changes, otherwise print skip_message.

Returns True if a commit was made, False if nothing changed. Raises on commit failure (never uses allow_failure=True).

#_parse_prerelease_suffix

python
def _parse_prerelease_suffix(version)

Parse the pre-release suffix from a semver version string.

Returns (preid, counter) if the version has a suffix like "-alpha.0", or (None, None) if no suffix is present.

Raises VersionError if the suffix format is invalid.

#bump_version

python
def bump_version(version, bump_type, preid='')

Bump a semver version string by the given type.

Supported bump types: patch, minor, major, infra, prerelease.

When preid is set with a standard bump (patch/minor/major), the bumped base version gets a pre-release suffix appended: e.g. minor + alpha on "0.42.0" produces "0.43.0-alpha.0".

When bump_type is "prerelease":

  • The current version must have a pre-release suffix.
  • If preid is empty or matches the current preid: increment the counter.
  • If preid is "stable": strip the suffix, return the base version.
  • If preid is higher in the ordering (alpha < beta < rc < stable): promote.
  • If preid is lower: error (cannot demote).

Infra with preid is a hard error.

Without preid, the existing behavior is preserved: strip any pre-release suffix and bump the base version normally.

#is_private_repo

python
def is_private_repo()

Detect if the current repo is private via GitHub API.

Returns True if private, False if public, None if detection fails.

The query goes through gh api, which resolves and applies the credential inside its own process. rlsbl deliberately never asks for the token itself: a raw credential on a captured stdout pipe is exactly what the observe standard forbids (see :mod:rlsbl.observe_allowlist).

#extract_github_repo_from_remote

python
def extract_github_repo_from_remote(remote_url: str) -> str | None

Extract owner/repo from a git remote URL.

Supports:

  • SCP-style: git@github.com:owner/repo.git, git@gw:owner/repo.git, gp:owner/repo.git
  • HTTPS: https://github.com/owner/repo.git

Returns "owner/repo" or None if the URL doesn't match.

#get_origin_repo

python
def get_origin_repo() -> str | None

Get owner/repo for the origin remote of the current git repo.

Returns None on any error (no remote, not a git repo, unparseable URL).

#get_github_repo

python
def get_github_repo(config: dict | None=None) -> str | None

Resolve the GitHub owner/repo slug from config or the git remote.

Precedence:

  1. config["github_repo"] if config is provided and the key is set.
  2. get_origin_repo() to parse the origin remote URL.

Returns "owner/repo" or None if neither source provides a slug.

#run_gh

python
def run_gh(args: list, config: dict | None=None, **kwargs) -> str

Run a gh CLI command with automatic GH_REPO resolution.

Resolves the repo slug via get_github_repo(config) and, if found, sets GH_REPO in a per-call env dict so gh targets the correct repository. Does NOT mutate os.environ (critical for thread-safety in watch.py's ThreadPoolExecutor).

Accepts timeout, env and cwd; anything else is a hard error. The call goes through effects.gh, the chokepoint's named entry point for the gh CLI, so every GitHub verb in the codebase passes one identifiable seam.

#run_gh_unscoped

python
def run_gh_unscoped(args: list, *, timeout: int=120, cwd: str | None=None) -> str

Invoke gh WITHOUT injecting GH_REPO; return trimmed stdout.

For the gh calls that must not be scoped to the current project: the repo-independent ones (--version, auth status) and the ones that name their own --repo explicitly. They deliberately skip :func:run_gh, but they still route through effects.gh, so the gh family remains one enumerable surface.

Contract matches :func:run: capture, text, check=True, 120s default.

#read_go_module_path

python
def read_go_module_path(project_dir: str) -> str | None

Read the module path from go.mod.

Returns None if go.mod does not exist or cannot be parsed.

More tools from this site

  • claudestream Drive Claude Code from Python: run it as a subprocess and read its output as typed events, with async and sync sessions, sandbox policies, and tools you define in Python
  • claudewheel A TUI Claude Code Launcher that lets you have more than one profile, manage sessions lifecycle, pick the exact CC version, model to use (even older unlisted ones), pick which GitHub account to use, etc.
  • dirstat Fast, single-binary directory statistics CLI: every file under a tree grouped by format, with counts, sizes, and lines of code, as a colored terminal table or as JSON
  • fastware A batteries-included ASGI framework: msgspec JSON, a managed Granian server, dependency injection, SSE, WebSockets, auth, and a test client
  • go-toml-edit Zero-dep TOML editing library for Go with comment preservation
  • howmuchleft The fastest Claude Code statusline: context window, 5-hour, and weekly limit usage as three customizable gradient bars, rendering in about 6 ms
  • orxtra
  • pgdesign
  • predraw Declarative rendering pipeline: describe a scene in JSON and get SVG, PNG and WebP out, with light and dark style tokens, reusable components and text converted to path outlines
  • reposummary Turn a git repository's history into a Markdown journal: pick a time window or revision range and get a readable digest of what changed, optionally narrated by an LLM
  • safegit git wrapper CLI that gives each commit its own temporary index and retries ref updates on conflict, so concurrent agents share one repository
  • saferm Command-line replacement for rm that archives every deletion with a mandatory reason and the context it ran in, so deleted files can be listed, inspected and restored
  • selfdoc Static Site Generator that builds a project's documentation site directly from its source code, so the docs can never drift from the code they describe, with SEO/AEO, first-class blog, search, and cross-project linking built in
  • strictcli
  • stricttest An always-on test-isolation floor: a pytest plugin and a Go env-hygiene module that make a test suite structurally unable to reach real credentials, the real HOME, the network, or the development repository.
  • wesktop A Python framework that turns an ASGI web app into a desktop application, serving it from a local Granian server and displaying it in a native OS window via pywebview
Search