Planhub
Planhub is a CLI app that syncs local planning artifacts (plans, issues,
milestones written as .md files) to GitHub Issues via the API.
Goals
- Keep planning artifacts in git and reviewable.
- Generate/update GitHub issues and milestones from local files.
- Make planning conversational: we can edit
.mdfiles and sync later.
How It Works
Planhub keeps planning data next to the code it describes. Each repository gets
a .plan/ folder containing milestones and issues as Markdown files with YAML
front matter. You edit these files like normal docs, then planhub sync maps
them to GitHub issues and milestones.
Installation
Global install from PyPI (recommended for CLI usage). pipx gives you a clean
CLI install without polluting your system Python, and puts planhub on your
PATH:
pipx install planhub
Project-local install with uv (inside your repo). This keeps the tool in a virtual environment for that repo:
uv venv
uv pip install planhub
First Run (Required)
Run planhub setup immediately after installation to create the global config
at ~/.planhub/config.yaml (created only if missing):
planhub setup
Without this setup step, sync defaults and other CLI behavior may be missing.
Commands
planhub init- Creates the standard
.plan/structure in the current repo. - Use
--dry-runto preview the folders that would be created. - Offers to install a
planhub-plan-artifactsskill for Claude Code and Cursor (.claude/skills/and.cursor/skills/) that teaches the agent.plan/layout, front matter, and to never runplanhub syncunasked. - Use
--skillsto install/update without prompting, or--no-skillsto skip without prompting. Omit both to be asked (interactive shells only — non-interactive runs skip by default). - Re-running
planhub init --skillsrefreshes any installed skill file whose content is out of date with the bundled template.
- Creates the standard
planhub setup- Creates the global config file at
~/.planhub/config.yaml(if missing). - Use
--dry-runto preview what would be created.
- Creates the global config file at
planhub issue <title>- Creates a new GitHub issue with the given title.
- Requires credentials and a GitHub
remote.origin.url.
planhub pull- Imports GitHub issues and milestones into
.plan/. Never writes to GitHub. - Imports all GitHub milestones into
.plan/(including open milestones with only closed issues, empty milestones, and closed milestones under.plan/archive/milestones). Closed milestone issues are imported into the milestone folder; closed backlog/root issues are not imported as files. - GitHub is also the source of truth for milestone
state; local milestone files are reconciled from GitHub before archive moves run. - Use
--forceto overwrite an already-imported local issue file's title/body/labels/assignees/milestone/state from its current GitHub content. Local-only front matter keys (e.g.id) are preserved. Without--force, an already-imported issue's content is left untouched. - Use
--dry-runto preview imports/overwrites without writing changes. - Requires credentials and a GitHub
remote.origin.urlto import; without them, pull still runs local reconcile and exits 0.
- Imports GitHub issues and milestones into
planhub push- Reads
.plan/files and creates or updates GitHub issues and milestones. Never imports from GitHub. - Prints explicit operation counts for create/update/archive/delete.
- Writes the GitHub
numberback into each file after creation. - GitHub is the source of truth for issue state during push; push does not
send local
stateorstate_reasonto GitHub, it reconciles those fields from the GitHub response. - Push runs in three phases: parse files, build a sync plan, then apply it.
- Use
--dry-runto validate files without writing changes. - Use
--verbosefor path-level planned changes, or--compactfor concise output. CLI flags override config. - Requires credentials and a GitHub
remote.origin.urlonly when there are pending creates/updates; exits non-zero in that case if missing. - Closed synced root issues are archived under
.plan/archive/issuesby default (or deleted whensync.closed_issues.policy: deleteis configured). - Milestone issues remain inside their milestone folder. When
milestone.mdhasstate: "closed", push moves the whole milestone directory to.plan/archive/milestones. If set back tostate: "open", push moves that directory back under.plan/milestones.
- Reads
planhub sync- Runs
planhub pullthenplanhub push— the same combined behavior as beforepull/pushexisted as separate commands. - Use
--dry-run,--verbose, or--compactas withpush.
- Runs
Credentials
Planhub can reuse your GitHub CLI session or a token stored in the environment.
The simplest path is to authenticate once with gh, and the CLI will fetch a
token automatically.
gh auth login
Alternatively, create a personal access token and export it:
- GitHub → Settings → Developer settings → Personal access tokens.
- Create a fine-grained token for the target repo(s).
- Grant Issues: Read (and Write if you will push updates later).
export GITHUB_TOKEN=ghp_your_token_here
Suggested Data Repo Layout
.plan/
issues/
20260127-backlog-issue.md
milestones/
stage-1/
milestone.md
issues/
issue-001.md
assets/
diagram.png
Front Matter Tips
milestonecan be a title or a number; usemilestone: nullto clear it.- Use
labels: []orassignees: []to remove them on GitHub. state_reasonrequiresstate: "closed".
Config
~/.planhub/config.yamland.plan/config.yamlare layered (repo overrides global).- Set
sync.behavior.verbositytocompact(default) orverbose.
Development
Setup
Install development dependencies:
uv sync --dev
Pre-commit Hooks
This project uses pre-commit to run code quality checks before commits. Install the hooks:
uv run pre-commit install
The hooks will automatically run on git commit. They check for:
- Code formatting and linting (ruff)
- Trailing whitespace and end-of-file fixes
- YAML, JSON, and TOML syntax
- Merge conflicts and other common issues
To run hooks manually:
uv run pre-commit run --all-files
Version Bumping
This project includes a grow.py script for bumping versions and creating releases:
./grow.py
The script will:
- Read the current version from
pyproject.toml - Show commits since the last tag
- Prompt for the new version
- Update the version in
pyproject.toml - Create a commit with a changelog of commits since the last release
- Create an annotated tag with the new version
- Optionally push commits and tags to the remote repository
The changelog commit includes all commit messages since the last tag, making it easy to see what changed in each release on GitHub.
License
Planhub is licensed under the GNU Affero General Public License v3.0
(AGPL-3.0). See LICENSE for the full text.
Next Steps
- Implement the parsing + GitHub sync logic.
- Add CI to validate file formats.
Metadata
Release files for planhub 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| planhub-0.4.0.tar.gz | 143.1 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| planhub-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 190.3 kB
Release files / planhub-0.4.0.tar.gz
| Download URL | planhub-0.4.0.tar.gz |
|---|---|
| Size | 143.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9cf0c577ac36a4ec66a4280534e444785b0dce461d8c9faa067437e9f202ee74
|
|
BLAKE2b-256 checksum How to use checksums |
8eac8aff244f7cab76a56077d4dd5c8d8338b425f27e5a6438efea287a898333
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 27, 2026.
Transparency logRelease files / planhub-0.4.0-py3-none-any.whl
| Download URL | planhub-0.4.0-py3-none-any.whl |
|---|---|
| Size | 47.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7ce88ddeb2a62effc33c2a9f5d3f04345ee1d86e2593063e59d33ff398713ebb
|
|
BLAKE2b-256 checksum How to use checksums |
33bde08d4b5c41c556817ee7a99a7a34d045d0e78ab2078eb4e73c2dca27ae96
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 27, 2026.
Transparency logSecurity for planhub 0.4.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for planhub-0.4.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 27, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.