Metadata-Version: 2.4
Name: qtmp
Version: 1.0.0
Summary: Qtmp -- Developer Environment Automation CLI. Detects project environments, resolves dependencies, generates execution plans, and runs package-manager commands, with idempotent operations and dry-run planning built in.
Author: Sai Sabarish S
Author-email: internoffl@gmail.com
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Dynamic: author
Dynamic: author-email
Dynamic: description
Dynamic: description-content-type
Dynamic: provides-extra
Dynamic: requires-python
Dynamic: summary

# qtmp — Q-Templates: Developer Environment Automation CLI

A Python CLI that detects your project environment, resolves
dependencies between tech stacks, generates an execution plan, and
runs the package-manager commands — instead of you hunting through
docs and typing them one by one.

```bash
pip3 install qtmp
qtmp create react
qtmp add tailwind
qtmp plan add sqlalchemy   # dry run first
qtmp add sqlalchemy        # then actually run it
qtmp doctor
```

## Why this isn't just "a script that runs npm install"

- **Recipe architecture** — every piece of the stack (React, Tailwind,
  FastAPI, SQLAlchemy, ...) is a self-contained `Recipe` with three
  methods: `requires()`, `detect()`, `plan()`. Adding a new one is one
  file + one line in a registry — nothing in the CLI, planner, or
  executor changes.
- **Real dependency resolution** — `qtmp add sqlalchemy` in an empty
  folder resolves to: scaffold FastAPI → install the Postgres driver
  → install SQLAlchemy, in that order, with shared dependencies
  (a "diamond") only ever installed once.
- **Project detection** — reads `package.json`, `vite.config.*`,
  `pyproject.toml`, `requirements.txt`, `docker-compose.yml`,
  `tailwind.config.*` once per run into a `ProjectContext`, so every
  recipe and `qtmp doctor` see the same picture of "what's already here."
- **Dry-run planning** — `qtmp plan add X` shows the exact commands that
  would run, against the real dependency graph, without touching the
  filesystem. `qtmp add X` is the same resolution path, one step later.
- **Idempotent by construction** — every recipe's `detect()` is checked
  *before* its `plan()` is even called. Running `qtmp add tailwind`
  twice reports "already installed, nothing to do" instead of
  reinstalling or corrupting config.
- **Fail-fast, resumable execution** — if a step fails (bad network,
  missing tool), everything after it stops. Already-applied steps are
  skipped on the next attempt; nothing after the failure was touched.

## Commands

| Command | What it does |
|---|---|
| `qtmp create react` / `qtmp create fastapi` / `qtmp create frontend-basic` | Scaffold a brand-new project |
| `qtmp <capability>` | **Shorthand** — `qtmp fastapi` == `qtmp create fastapi`; `qtmp tailwind` == `qtmp add tailwind` (creatable capabilities go to `create`, everything else to `add`) |
| `qtmp add <capability>` | Resolve dependencies and install/configure a capability |
| `qtmp plan add <capability>` | Same resolution, dry-run only — prints the commands, changes nothing |
| `qtmp doctor` | Diagnose which tools are on PATH and what this directory looks like |
| `qtmp list` | List every registered capability |
| `qtmp cheatsheet [name]` | View built-in or your own personal cheatsheets |
| `qtmp --version` | Print the installed version |

`--cwd` works in **either position** — `qtmp --cwd ./my-app create react` and `qtmp create react --cwd ./my-app` both work identically.

Beginner React (`b`) gets Vite + Tailwind automatically. Advanced (`a`)
gets TypeScript, Tailwind, shadcn/ui, React Router, Axios, and a
`src/components|pages|hooks|lib` layout — all resolved and applied
through the same planner/executor path as a manual `qtmp add`.

`frontend-basic` (aliases: `frontend`, `html-css-js`, `vanilla`) scaffolds
a plain `index.html` + `style.css` + `script.js` — no framework, no npm,
no network required. Good for confirming files land where you expect
without waiting on any package manager.

### Re-running `create` is safe

Every creatable recipe checks the target folder before scaffolding:
- **Doesn't exist / empty** → scaffolds normally.
- **Already a valid project of that type** → skipped, reports "already exists," doesn't touch it.
- **Exists, non-empty, and isn't that project type** → clean error, doesn't blindly run a scaffolder into someone else's files.

## Architecture

```
qtmp/
├── context.py      ProjectContext.detect(cwd) -- one detection pass, shared everywhere
├── planner.py      Step, Plan, resolve_plan() -- pure dependency-graph resolution
├── executor.py      apply(plan) -- runs pending steps, skips satisfied ones, fails fast
├── doctor.py        read-only environment + project diagnostics
├── recipes/
│   ├── base.py       Recipe ABC: requires() / detect() / plan() / (optional) create()
│   ├── react.py       provides "react", creatable
│   ├── tailwind.py    provides "tailwind", requires "react" only if no node project exists yet
│   ├── router_axios.py provides "router" / "axios" / "shadcn" (shadcn requires "tailwind")
│   ├── fastapi.py      provides "fastapi", creatable
│   ├── database.py     provides "postgres" / "db-driver-postgres" / "sqlalchemy"
│   ├── frontend_basic.py provides "frontend-basic", creatable -- plain HTML/CSS/JS
│   └── __init__.py     REGISTRY = {capability_name: recipe_instance} -- the only file
│                       you edit to add a new recipe
├── cheatsheet_manager.py   bundled vs. personal (~/.qtmp/cheatsheets/) cheatsheets
└── cli.py            argparse subcommands, all going through resolve_plan()/apply();
                       also handles --cwd extraction (works before/after the
                       subcommand) and shorthand expansion ('qtmp fastapi' -> 'qtmp create fastapi')
```

### The dependency-resolution example, end to end

`qtmp add sqlalchemy` on an empty folder resolves like this:

```
sqlalchemy
 ├── requires: fastapi          -> not detected -> scaffold venv, install fastapi+uvicorn, write main.py
 └── requires: db-driver-postgres
      └── requires: fastapi     -> already resolved above, not repeated
                                -> not detected -> pip install psycopg2-binary
 -> not detected -> pip install sqlalchemy
```

Run it for real and check the output of `qtmp plan add sqlalchemy` first
— that's the exact plan the executor will follow, nothing hidden.

### Adding a new recipe

```python
# qtmp/recipes/docker.py
from qtmp.recipes.base import Recipe
from qtmp.planner import Step

class DockerRecipe(Recipe):
    name = "Docker"
    provides = "docker"

    def detect(self, ctx):
        return ctx.has_docker_compose

    def plan(self, ctx):
        return [Step("Write docker-compose.yml", apply_fn=lambda: ...)]
```
```python
# qtmp/recipes/__init__.py
from qtmp.recipes.docker import DockerRecipe
REGISTRY["docker"] = DockerRecipe()
```
That's the entire integration surface. `cli.py`, `planner.py`, and
`executor.py` never need to know Docker exists.

## Testing

```bash
pip install -e ".[dev]"
pytest -v
```

60 tests covering:
- **CLI helpers** — shorthand expansion (`qtmp fastapi` → `create fastapi`), and `--cwd` extraction working in either position, including the `--cwd=value` form and repeated occurrences.
- **Existing-target safety** — `create` skips cleanly on an already-valid project of the same type, and raises a clear error (not a crash) on a genuine name collision with unrelated content.
- **Project detection** — Node/Vite/Python/venv/Docker/Tailwind sniffing, and that a malformed `package.json` doesn't crash detection.
- **Dependency resolution** — ordering, diamond dependencies installed once, unknown capabilities raising a clear error, and the exact FastAPI→driver→SQLAlchemy shape from the design doc.
- **Idempotency & safe execution** — a fully-satisfied chain is a no-op; a failure mid-plan halts everything after it and reports which step failed; already-applied steps are never re-run.
- **Individual recipes** — `detect()`/`plan()`/`requires()` logic for React, Tailwind, Router, Axios, shadcn, FastAPI, and the database chain, with **no real npm/pip/network calls** — commands are asserted on, never executed, in unit tests.
- **Cheatsheets** — personal overrides taking precedence over bundled ones, and falling back correctly on removal.

Recipe tests never shell out for real — `plan()` is asserted on
directly, so these pass identically whether or not Node/Python/Postgres
happen to be installed on the machine running CI.

## Honest limitations (not yet done)

- **Cross-platform**: path handling is OS-aware (`os.name == "nt"`
  branches for venv paths), but this has only actually been *run* on
  macOS/Linux. Windows support is written for, not verified.
- **Package manager choice**: assumes `npm` and `pip`. No `pnpm`/`yarn`/
  `poetry`/`uv` alternative paths yet — a natural next recipe-level
  feature (a `--pm` flag resolved per-ecosystem).
- **`postgres` recipe** deliberately does *not* auto-install a local
  Postgres server (that usually needs `sudo` / a package-manager
  choice we shouldn't make for you) — it only guides you and checks
  for `psql` on PATH. Automating a *sudo-requiring* step didn't feel
  like something a CLI should do silently, even in service of a demo.
- **AI-assisted setup** from an earlier iteration was removed in this
  rewrite to keep the core deterministic and testable; if it comes
  back, it'll be a strictly optional layer on top of `resolve_plan()`,
  never a replacement for it.
