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
4 changes: 2 additions & 2 deletions .github/instructions/index.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ setup.sh # Shell-based setup script (uv venv + uv sync)
properties.yml # Project configuration (repo path/remote, template path/remote)
template.ignore.yml # Paths /template pull/push must never touch (.gitignore handles the rest via git ls-files)
modules/
common/ # cli.py, properties.py, utils.py — shared helpers
common/ # cli.py, properties.py, route_utils.py, utils.py — shared helpers
repo/ # pull.py, push.py, log.py, squash.py, rebase.py, pr_*.py — git/PR workflow modules
setup/ # properties.py — creates/stamps properties.yml, called by setup.sh/setup.ps1
template/ # ignore.py, naming.py, pull.py, push.py, resolve.py, route.py, scope.py — sync shared tooling with the parent template repo for /template
Expand All @@ -35,7 +35,7 @@ tasks/
.github/
instructions/ # Copilot instruction files
prompts/ # Copilot prompt files (/push, /pull, /squash, /rebase, /fix, /test, /docs, /pr-notes, /pr, /pr-cleanup, /punch-it-chewy, /template, /update, /upgrade, /repo, /setup) — source of truth for slash commands
workflows/ # tests.yml (reusable), feature_branches.yml, protected_branches.yml
workflows/ # tests.yml — ruff + pylint + yamllint + actionlint + check_agents, on PRs to development/main
.claude/
commands/ # Claude Code slash commands, hand-maintained mirror of .github/prompts/ (see prompts.instructions.md)
skills/ # Claude Code skills (auto-discovered), hand-maintained mirror of .github/prompts/ (see prompts.instructions.md)
Expand Down
1 change: 1 addition & 0 deletions .github/instructions/modules.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def main() -> None:
|--------|----------|
| `cli.py` | Click-like `echo`, `prompt`, `confirm`, `is_tty`, `command`/`option` decorators |
| `properties.py` | Read `properties.yml` — `get_repo_local()`, `get_repo_remote()`, `get_template_local()`, `get_template_remote()` |
| `route_utils.py` | AI-tool command routers (`modules/*/route.py`) — `find_repo_root()` (`properties.yml`-anchored, falls back to `cwd()`, no exception), `build_env()` |
| `utils.py` | `success()`, `error()`, `warning()`, `info()`, `create_slug()` |

## Guidelines
Expand Down
4 changes: 2 additions & 2 deletions .github/instructions/tasks.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ namespace.add_collection(my_new_module, name="my_new_module")
| libs | `uv run --no-sync invoke upgrade.libs` | Upgrade libraries only |
| python | `uv run --no-sync invoke upgrade.python` | Upgrade Python only |
| sync | `uv run --no-sync invoke upgrade.sync` | Sync dependencies (no version check) |
| upgrade | `uv run --no-sync invoke upgrade.upgrade` | Upgrade Python + all dependencies (default) |
| upgrade | `uv run --no-sync invoke upgrade` | Upgrade Python + all dependencies (default; explicit form: `upgrade.all`) |

### Versioning Tasks
Read-only version-lock *checks* — compare `pyproject.toml` deps and `.github/workflows/` action
Expand All @@ -115,7 +115,7 @@ module behavior behind these.
| Fix code style | `uv run --no-sync invoke fix` |
| Run all tests | `uv run --no-sync invoke test` |
| Run one linter | `uv run --no-sync invoke tests.pylint` |
| Upgrade everything | `uv run --no-sync invoke upgrade.upgrade` |
| Upgrade everything | `uv run --no-sync invoke upgrade` |
| Run a module | `uv run --no-sync python -m modules.chat.start --title="..."` |
| Test a route | `uv run --no-sync python -m modules.chat.route "start my chat"` |

Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ name: Tests
run-name: "Tests (${{ github.head_ref || github.ref_name }})"

on:
push:
pull_request:
branches:
- main # Trigger on pushes to the main branch
- development # Trigger on PRs targeting development
- main # Trigger on PRs targeting main

jobs:
# https://github.com/rhysd/actionlint
Expand Down
15 changes: 7 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Repo Setup (Python)
# Template Python
[![Tests](https://github.com/LevonBecker/template_python/actions/workflows/tests.yml/badge.svg)](https://github.com/LevonBecker/template_python/actions/workflows/tests.yml)

Skeleton setup for a repository that uses Python invoke tasks, reusable modules, uv for
Expand All @@ -7,7 +7,8 @@ project to get a consistent git workflow, linting, and session logging out of th

## Setup
```bash
./setup.sh
./setup.sh # macOS/Linux
.\setup.ps1 # Windows (PowerShell)
```

Creates a `.venv` with `uv` and installs dependencies. Update `properties.yml` with the local repo
Expand All @@ -21,7 +22,7 @@ setup.sh # Shell-based setup script (uv venv + uv sync)
properties.yml # Project configuration (repo path/remote, template path/remote)
template.ignore.yml # Paths /template pull/push must never touch (.gitignore handles the rest via git ls-files)
modules/
common/ # cli.py, properties.py, utils.py — shared helpers
common/ # cli.py, properties.py, route_utils.py, utils.py — shared helpers
repo/ # pull.py, push.py, log.py, squash.py, rebase.py, pr_*.py — git/PR workflow modules
setup/ # properties.py — creates/stamps properties.yml, called by setup.sh/setup.ps1
template/ # ignore.py, naming.py, pull.py, push.py, resolve.py, route.py, scope.py — sync shared tooling with the parent template repo for /template
Expand All @@ -43,9 +44,7 @@ tasks/
instructions/ # Copilot instruction files
prompts/ # /push, /pull, /squash, /rebase, /fix, /test, /docs, /pr-notes, /pr, /pr-cleanup, /punch-it-chewy, /template, /update, /upgrade, /repo, /setup — source of truth
workflows/
tests.yml # Reusable CI: ruff + pylint + yamllint + actionlint + check_agents
feature_branches.yml # Runs tests.yml on pull_request
protected_branches.yml # Runs tests.yml on push to main
tests.yml # CI: ruff + pylint + yamllint + actionlint + check_agents, on PRs to development/main
.claude/
commands/ # Claude Code slash commands, hand-maintained mirror of .github/prompts/
skills/ # Claude Code skills (auto-discovered), hand-maintained mirror of .github/prompts/
Expand Down Expand Up @@ -144,6 +143,6 @@ they aren't meant to lint clean from this repo's root.
| [`addons/shopfiy_dawn_theme/`](addons/shopfiy_dawn_theme/) | `dawn.list` / `dawn.upgrade` invoke tasks for tracking and staging upstream Shopify/dawn theme upgrades | `modules/dawn/` (`__init__.py`, `list.py`, `upgrade.py`, `README.md`), `tasks/dawn.py`, `.github/workflows/{deploy,promote,release,tests,upgrade}.yml`, `.github/instructions/dawn.instructions.md`, `.github/prompts/dawn.prompt.md`, `.claude/commands/dawn.md` |

## CI
GitHub Actions runs Ruff, Pylint, yamllint, actionlint, and check_agents on every push and pull request via
`.github/workflows/tests.yml` (invoked by `feature_branches.yml` and `protected_branches.yml`).
GitHub Actions runs Ruff, Pylint, yamllint, actionlint, and check_agents on every pull request
targeting `development` or `main` via `.github/workflows/tests.yml`.

27 changes: 13 additions & 14 deletions modules/common/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,23 @@ Shared utilities and helper functions used across all modules in the template_py

This module provides common functionality that is used by other modules throughout the repository, including configuration parsing and utility functions.

## Modules

### `utils.py`

Common utility functions for console output, error handling, and shared operations.

**Functions:**
- `success(message)` - Print success messages with ✅ emoji
- `error(message)` - Print error messages with ❌ emoji
- `warning(message)` - Print warning messages with ⚠️ emoji
- `info(message)` - Print info messages with ℹ️ emoji
## Files

- `utils.py` — console output helpers: `success()`, `error()`, `warning()`, `info()` (✅/❌/⚠️/ℹ️
prefixed), plus `create_slug()`
- `properties.py` — reads `properties.yml`: `get_repo_root()`, `get_repo_local()`,
`get_repo_remote()`, `get_template_local()`, `get_template_remote()`
- `cli.py` — Click-like CLI compatibility helpers backed by `argparse` (`echo`, `secho`, `prompt`,
`confirm`, `is_tty`, `Choice`, `command`/`option` decorators) — TUI-safe, no external dependency
- `route_utils.py` — shared helpers for the `/repo`- and `/template`-style AI-tool command
routers: `find_repo_root()` (walks upward for `properties.yml`), `build_env()`
- `README.md` — this file

## Dependencies

This module depends on:
- `common.properties` - For reading configuration from `properties.yml`
- Standard library: `pathlib`, `shutil`
- Internal CLI helper: `modules/common/cli.py` (TUI-safe prompt/confirm/option handling)
- Standard library: `pathlib`, `argparse`, `subprocess`
- `pyyaml` — for reading `properties.yml`

## Architecture

Expand Down
205 changes: 40 additions & 165 deletions modules/repo/README.md
Original file line number Diff line number Diff line change
@@ -1,169 +1,44 @@
# Repo Manager Agent
# Repo Module

The repo module handles repository-level operations including git synchronization.

## Features

- **Repository Push/Pull**: Full git operations
- **Automated Commits**: Timestamp-based commit messages
- **Error Handling**: Graceful handling of push/pull failures

## Commands

### `/repo push` (alias: `/push`)
Push changes to git remote from any location.

**Workflow:**
1. **Navigate to repository root** - Automatically changes to repo directory
2. **Git pull** - Fetch and merge latest changes from remote
3. **Check for local changes** - Identify uncommitted work with `git status`
4. **Stage all changes** - Run `git add .` if changes detected
5. **Commit with timestamp** - Create commit: `Push repository: Automated commit YYYY-MM-DD HH:MM:SS`
6. **Push to remote** - Upload changes to GitHub
7. **Confirm completion** - Show status summary

**Features:**
- ✅ Works from any directory in the repository
- ✅ Automatic git operations (pull, commit, push)
- ✅ Timestamp-based commit messages
- ✅ Error handling for each push step

**When to Use:**
- **End of session** - Push all work before closing
- **After making changes** - Save work to remote
- **Regular backups** - Keep work synchronized and safe
- **After major changes** - Backup important work immediately

**Error Handling:**
- Git pull failure: Stops execution
- Git push failure: Stops execution with error

**Module:** `modules.repo.push` (via `/repo push`)

### `/repo pull` (alias: `/pull`)
Pull updates from git remote from any location.

**Workflow:**
1. **Check working directory** - Stash local changes if the working tree is dirty
2. **Git pull** - Fetch and merge latest changes from remote (rebase, to handle diverged history)
3. **Restore stash** - Restore stashed changes, if any were made

**Features:**
- ✅ Works from any directory in the repository
- ✅ Stashes and restores local changes automatically around the pull
- ✅ Rebases onto latest remote history instead of merge-committing

**When to Use:**
- **Start of session** - Get latest from git remote
- **After making changes on another device** - Pull updates to local
- **Before starting work** - Ensure you have latest version

**Error Handling:**
- Stash failure: Stops execution
- Git pull failure: Stops execution; restores the stash first if one was made

**Module:** `modules.repo.pull` (via `/repo pull`)

## Branch Maintenance

### `/rebase`
Rebase the current branch onto the remote default branch (`origin/main` or `origin/master`).
Optionally offers to run `/squash` first. Handles stashing local changes and interactive conflict
resolution (ours/theirs/manual) if restoring the stash conflicts.

**Module:** `modules.repo.rebase` (via `/rebase`)

### `/squash`
Anchored squash of every commit down to the repository's root commit into one commit, with an
auto-generated bulleted message. Prompts to review the message, confirm the (irreversible) local
squash, and optionally force-push (`--force-with-lease`).

**Module:** `modules.repo.squash` (via `/squash`)

### `/pr-cleanup`
Cleans up after a PR has been merged on GitHub: confirms the current branch's PR is merged (`gh pr
view`), switches to the detected default branch, pulls latest, then force-deletes the local
feature branch (`git branch -D` — safe here since the merge was already confirmed via GitHub,
which handles squash merges that `git branch -d` would otherwise reject as "not fully merged").

**Module:** `modules.repo.pr_cleanup` (via `/pr-cleanup`)

## Git Integration

### Push/Pull Operations

**Pull Sequence:**
```bash
git status --porcelain # Check for uncommitted changes, stash if dirty
git pull --rebase # Fetch and rebase onto remote
git stash pop # Restore stashed changes, if any
```

**Push Sequence:**
```bash
git pull # Fetch and merge from remote
git status --porcelain # Check for changes
git add . # Stage all changes
git commit -m "Push repository: Automated commit 2025-12-11 01:30:45"
git push # Upload to remote
```

**Commit Message Format:**
```
Push repository: Automated commit YYYY-MM-DD HH:MM:SS
```

## Configuration

Default settings in `config.yml`:
- `repo_path`: "${repo_local}"

## Permissions Required

- `git_pull` - Pull from remote repository
- `git_push` - Push to remote repository
- `git_commit` - Create commits
- `git_status` - Check repository status
- `directory_access` - Navigate repository directories
- `bash_execute` - Run shell scripts

## Dependencies

- `git` - Version control operations

## Best Practices

1. **Pull/Push Regularly**: Run `/repo pull` at start, `/repo push` at end of sessions
2. **Check Status**: Review git status before push if concerned about changes
3. **Network Aware**: Git operations require network connectivity
4. **Backup Important**: Always push before major changes or deletions

## Workflow Examples

**Cross-Device Workflow:**
```
Device A:
1. /repo pull # Pull latest before starting
[work on content]
2. /repo push # Push changes to remote

Device B (later):
1. /repo pull # Pull changes from Device A
```

## Related Commands

- `/repo push` - Push all commits to remote (alias: `/push`)
- `/repo pull` - Pull latest changes from remote (alias: `/pull`)
Git workflow and session-logging logic behind the `repo.*` invoke tasks and their `/push`,
`/pull`, `/squash`, `/rebase`, `/pr-notes`, `/pr`, `/pr-cleanup`, and `/repo` slash-command
counterparts. Every file exposes a `main()` entry point, runnable standalone via
`python -m modules.repo.<name>`.

## Files

- `push.py` - Push to git (used by `/repo push`, alias `/push`)
- `pull.py` - Pull from git (used by `/repo pull`, alias `/pull`)
- `config.yml` - Agent configuration
- `README.md` - This file

## Command Aliases

- `/push` → Push to git
- `/pull` → Pull from git
- `pull.py` — stash local changes if dirty, `git pull --rebase`, restore the stash
(`repo.pull` / `/pull`)
- `push.py` — pull, stage, commit with a timestamped message, and push the primary tracked branch
(`repo.push` / `/push`)
- `pr_push.py` — push the current **feature** branch (fix → test → commit → push), for branches
that may not have upstream tracking yet; used by `/punch-it-chewy` ahead of opening a PR
- `log.py` — save a session log markdown file to `logs/` (`repo.log`)
- `squash.py` — anchored squash of every commit down to the root commit, with an auto-generated
message and optional force-push (`repo.squash` / `/squash`)
- `rebase.py` — rebase onto the remote default branch, optionally squashing first, with stash and
conflict handling (`repo.rebase` / `/rebase`)
- `pr_diff.py` — detect a PR's base branch and print commit log/diff context vs. that base; also
exposes `PROTECTED_BRANCHES`, `current_branch()`, and `detect_base_branch()` shared by the other
`pr_*`/`push.py`/`pr_cleanup.py` modules (`repo.pr_diff`)
- `pr_notes.py` — save PR notes markdown to `tmp/pull_requests/` (`repo.pr_notes_save`)
- `pr_create.py` — open a GitHub Pull Request for the current branch via `gh` (`repo.pr_create`
/ `/pr`)
- `pr_cleanup.py` — confirm the current branch's PR is merged (`gh pr view`), switch to the
detected default branch, pull latest, then force-delete the local feature branch
(`git branch -D` — safe here since the merge was already confirmed via GitHub, which handles
squash merges that `git branch -d` would otherwise reject as "not fully merged")
(`repo.pr_cleanup` / `/pr-cleanup`)
- `route.py` — routes `/repo <subcommand> ...` arguments to the module above matching that
subcommand (`push`, `pull`, `pr_diff`, `pr_notes`, `pr_create`, `pr_push`, `pr_cleanup`,
`rebase`, `squash`)
- `README.md` — this file

## Conventions

- Shell out via `subprocess.run(..., cwd=repo_path)` — never `shell=True`
- Resolve the repo path via `modules.common.properties.get_repo_local()`
- Report outcomes via `modules.common.utils` (`success`/`error`/`warning`)
- `PROTECTED_BRANCHES` (`development`, `develop`, `main`, `master`) is defined once in
`pr_diff.py` and imported wherever a module needs to distinguish a feature branch from the
primary tracked branch
20 changes: 18 additions & 2 deletions modules/tests/check_agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,28 @@

from __future__ import annotations

import os
from pathlib import Path

from ..common import cli as click
from ..common.properties import get_repo_local
from ..common.route_utils import find_repo_root
from ..common.utils import error, success

# This file's fixed depth below the repo root (modules/tests/check_agents.py -> repo root).
_REPO_ROOT_DEPTH = 2


def _resolve_repo_root() -> Path:
"""Return the repo root.

In CI, properties.yml (the anchor find_repo_root() searches for) is gitignored and never
created, so resolve via this file's fixed position relative to the repo root instead.
Locally, prefer find_repo_root() so the check still works when invoked from a subdirectory.
"""
if os.environ.get("CI"):
return Path(__file__).resolve().parents[_REPO_ROOT_DEPTH]
return find_repo_root()


def _prompt_names(repo_path: Path) -> set[str]:
"""Return the command names sourced from .github/prompts/*.prompt.md."""
Expand Down Expand Up @@ -49,7 +65,7 @@ def _report_diff(label: str, expected: set[str], actual: set[str]) -> bool:
@click.command()
def main() -> None:
"""Verify every .github/prompts/*.prompt.md has a matching mirror in the other three command dirs."""
repo_path = get_repo_local()
repo_path = _resolve_repo_root()
prompts = _prompt_names(repo_path)

click.echo(f"Checking {len(prompts)} prompt(s) are mirrored into commands, skills, and clinerules...")
Expand Down