| name | setup-py-project |
| description | Use when the user wants to create a new Python project/package, name a project, set up a GitHub repo, or bootstrap a project from scratch. Triggers on requests like "help me set up a project", "create a package", "I need a name for...", "new repo for...". |
| argument-hint | [project-name-or-description] |
AI-Assisted Python Project Setup
You are helping the user create a new Python project from scratch (or partially). Use the wads package tools to do the heavy lifting. Always confirm before taking irreversible actions (repo creation, git push).
Quick Reference: Key Functions
All functions are in wads.project_setup and wads.user_dirs:
from wads.project_setup import check_name_availability, check_names, is_available_on_pypi, is_available_on_github
from wads.project_setup import list_name_candidate_files, load_name_candidates
from wads.project_setup import detect_github_username, create_github_repo, repo_exists
from wads.project_setup import setup_project, create_misc_docs, setup_opsward_for_project
from wads.user_dirs import read_user_preferences, write_user_preferences, name_candidates_dir, config_dir
Conversation Flow
1. Understand what the user wants
Determine the scope. The user may want:
- Full setup: Name → repo → files → commit/push (guide them through everything)
- Just naming help: Check availability, suggest names
- Just populate: An existing repo needs project files
- Just create a repo: Name and description are ready
2. Gather project information
Always needed: name (or help choosing one), short description.
Detect automatically: GitHub username/org, author name, license.
Ask only if relevant: org (if different from personal), license (if not MIT), extra dependencies.
Run this early to detect defaults:
python -c "
from wads.user_dirs import read_user_preferences
from wads.project_setup import detect_github_username
prefs = read_user_preferences()
username = detect_github_username()
print(f'Preferences: {prefs}')
print(f'GitHub username: {username}')
"
3. Name assistance protocol
If the user needs help with a name:
Step A — Check name candidate pools:
python -c "
from wads.project_setup import list_name_candidate_files, load_name_candidates
files = list_name_candidate_files()
if files:
print('Available name pools:')
for f in files:
names = load_name_candidates(f)
print(f' {f.name}: {len(names)} names')
else:
print('No name candidate files found.')
"
If no candidate files exist, tell the user:
You don't have any name candidate pools set up. You can create text files (one name per line) in:
{name_candidates_dir path}
For example, create short_words.txt or latin_names.txt with names you like.
I'll check them for availability next time.
Step B — Generate suggestions:
Use your own creativity to suggest 5-8 names based on the description. Consider:
- Short, memorable names (2-8 chars are ideal for Python packages)
- Relevant to the project's purpose
- Acronyms if the user has mentioned them
- Names from candidate pools that fit
If candidate pools exist, also filter pool names for relevance to the description and include good matches.
Step C — Check all candidates:
python -c "
from wads.project_setup import check_names
import json
names = ['name1', 'name2', 'name3'] # all candidates
results = check_names(names, org='USERNAME')
for r in results:
status = []
if not r['valid_pep508']: status.append('INVALID')
if r['pypi_available'] == False: status.append(f'PyPI taken: {r[\"pypi_url\"]}')
elif r['pypi_available']: status.append('PyPI ✓')
if r['github_available'] == False: status.append(f'GitHub taken: {r[\"github_url\"]}')
elif r['github_available']: status.append('GitHub ✓')
print(f'{r[\"name\"]}: {\" | \".join(status)}')
"
Step D — Present results as a clear table and let the user pick or suggest alternatives.
4. Confirm before acting
Before creating a repo or populating files, present a summary:
Here's what I'll do:
- Name:
mypackage
- Description: "A tool for doing X"
- GitHub: Create
username/mypackage (public)
- Author: Thor Whalen
- License: MIT
- CI template: uv-based (modern default)
This will create the repo and populate it with pyproject.toml, CI workflow, README, etc.
Shall I proceed?
5. Execute
For full setup, use setup_project:
python -c "
from wads.project_setup import setup_project
result = setup_project(
'mypackage',
description='A tool for doing X',
org='username',
author='Thor Whalen',
license='mit',
proj_rootdir='/abs/path/to/parent/mypackage', # see pitfall note below
create_repo=True,
populate=True,
create_devdocs=False,
setup_opsward=False,
)
print(result)
"
Or call individual functions for partial flows (populate only, repo only, etc.).
⚠️ Pitfall: proj_rootdir is the full package path, NOT its parent.
The name is misleading. Source (wads/project_setup.py):
pkg_dir = proj_rootdir or os.path.join(os.getcwd(), name)
So proj_rootdir is passed straight to populate_pkg_dir as the package dir.
- ✅ Correct:
proj_rootdir='/abs/path/to/parent/mypackage'
- ❌ Wrong:
proj_rootdir='/abs/path/to/parent' → populates files directly into that parent and creates a stray parent/__init__.py (a nested package named after the folder), polluting a directory that likely holds many other projects.
If you omit proj_rootdir, it defaults to os.path.join(os.getcwd(), name) — so either cd to the intended parent dir first, or always pass the full target path explicitly (recommended).
⚠️ Pitfall: tomli_w is a hard dep for populate_pkg_dir.
Install it in the active venv before calling any populate flow, otherwise write_pyproject_configs raises ImportError mid-run (leaving a half-populated directory):
pip install tomli_w
⚠️ Pitfall: populate needs a .git/ to generate CI config.
populate_pkg_dir calls git remote get-url origin on the package dir when generating the CI workflow; if .git/ is missing it raises AssertionError: Didn't find the git_dir: .../.git and — because the exception propagates out of populate — later setup_project steps like create_devdocs never run. The directory is left half-populated (pyproject/README/LICENSE present, CI + misc/docs missing).
Fix: either let create_repo=True do a gh repo clone first (which sets up .git/ with an origin), or when you've set create_repo=False, pre-initialize git yourself before calling setup_project:
mkdir -p /abs/path/to/parent/mypackage
cd /abs/path/to/parent/mypackage && git init -q && git remote add origin https://github.com/ORG/mypackage.git
If it already half-ran, you can finish off the missing steps by calling the individual functions directly (e.g. create_misc_docs(pkg_dir)).
6. Post-creation options
After the main setup, offer (don't force) these extras:
-
Dev docs: "Would you like me to create misc/docs/ with research, design, and roadmap templates?"
from wads.project_setup import create_misc_docs
create_misc_docs('/path/to/project')
-
AI agent setup: Check if opsward is available first:
python -c "import opsward; print('available')" 2>/dev/null && echo "opsward is installed"
If available: "Would you like me to set up AI agent configuration with opsward?"
-
Initial commit: "Would you like me to make the initial commit and push?"
cd /path/to/project && git add -A && git commit -m "Initial project setup via wads" && git push -u origin main
-
Frontend component(s): If the project ships a JS/TS part (widget, browser UI, TS library), offer to add one or more frontend profiles. Each lands in its own subdir with a path-filtered NPM CI workflow (validate-always, publish-opt-in via [publish-npm]); multiple components never collide.
populate my-project --root-url https://github.com/ORG/my-project --frontend ts
populate my-project --root-url https://github.com/ORG/my-project --frontend js,ts
populate my-project --root-url https://github.com/ORG/my-project --frontend ts-monorepo
Profiles: js (npm single-package), ts (TypeScript single-package), ts-monorepo (pnpm workspaces + turbo). --with-npm is a back-compat alias for --frontend js.
-
Publish to PyPI: "Would you like to publish to PyPI? (This runs pack go .)"
-
Enable GitHub Pages (source: gh-pages branch, / (root) folder): The
CI workflow's github-pages job (via i2mint/epythet/actions/publish-github-pages)
pushes built docs to the gh-pages branch, but GitHub Pages must be
explicitly enabled in repo settings to serve from that branch — otherwise the
docs build succeeds but nothing is published. Do this automatically when
the situation is unambiguous (see decision tree); only ask in the ambiguous
case. See the GitHub Pages + Discussions (gh) procedure below.
-
Enable GitHub Discussions: Do this automatically unless the user
explicitly asked not to. See the procedure below.
GitHub Pages + Discussions (gh)
Run this after the repo exists on GitHub (after the initial push, ideally after
the first CI run that creates gh-pages). Requires the gh CLI — if gh is
not installed, skip Pages/Discussions here, tell the user to install it
(https://cli.github.com/), and note they can run epythet configure-pages ORG/REPO
(or set Pages manually) later. Throughout, ORG/REPO is the repo's owner/name.
GitHub Pages — target is source branch gh-pages, folder / (root).
Read the current config first, then act on a decision tree:
gh api repos/ORG/REPO/pages --jq '{branch:.source.branch, path:.source.path, build_type:.build_type}' 2>/dev/null
- Empty / 404 → Pages not enabled (GitHub's default). Enable it
automatically (no need to ask):
gh api repos/ORG/REPO/pages -X POST -f 'source[branch]=gh-pages' -f 'source[path]=/'
If that fails because the gh-pages branch doesn't exist yet (CI hasn't run),
create the branch from the default branch first, then retry the POST:
DEF=$(gh api repos/ORG/REPO --jq '.default_branch')
gh api repos/ORG/REPO/git/refs -X POST \
-f ref="refs/heads/gh-pages" \
-f sha="$(gh api repos/ORG/REPO/git/ref/heads/$DEF --jq '.object.sha')"
- Already
branch=gh-pages, path=/ → nothing to do.
- Anything else (a different branch, a non-root path, or
build_type=workflow
— i.e. the "GitHub Actions" source) → this is a particular situation; do NOT
silently change it. Check whether the gh-pages branch even exists:
gh api repos/ORG/REPO/branches/gh-pages --jq '.name' 2>/dev/null
Then show the user the current setting (and whether gh-pages exists) and
ask for confirmation before switching. If they confirm, update with PUT
(Pages already exists, so POST would 409):
gh api repos/ORG/REPO/pages -X PUT -f 'source[branch]=gh-pages' -f 'source[path]=/'
GitHub Discussions — enable by default unless the user explicitly opted out.
Check, then enable if off:
gh api repos/ORG/REPO --jq '.has_discussions'
gh repo edit ORG/REPO --enable-discussions
7. Save preferences
If this is the user's first time, offer to save detected defaults:
from wads.user_dirs import write_user_preferences
write_user_preferences({
"github_username": "detected_username",
"default_author": "Author Name",
"default_license": "mit",
"default_org": "org_name",
})
Important Notes
- Never create a repo without confirmation. Always present the plan first.
- Always check availability before proposing a name as final. Don't present a taken name as a good option.
- If
gh CLI is not installed, tell the user and provide install instructions. You can still help with naming and populate (just skip repo creation).
- populate_pkg_dir is idempotent — it skips files that already exist. Safe to run on an existing project.
- The uv CI template (
github_ci_uv.yml) is the modern default. It's automatically used by populate.