Metadata-Version: 2.5
Name: code-massager
Version: 0.0.1
Summary: A web-based code explorer for human review sweeps of AI-generated codebases.
Project-URL: Homepage, https://sf92.github.io/code-massager/
Project-URL: Repository, https://github.com/SF92/code-massager
Author: Scott Fraser
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Python: >=3.12
Requires-Dist: dependency-injector>=4.42.0
Requires-Dist: django-ninja>=1.3.0
Requires-Dist: django>=5.1
Requires-Dist: httpx>=0.28.1
Requires-Dist: psycopg[binary]>=3.2.0
Requires-Dist: pydantic>=2.13.4
Requires-Dist: python-dotenv>=1.0.0
Requires-Dist: redis>=5.2.0
Requires-Dist: uvicorn[standard]>=0.49.0
Requires-Dist: whitenoise>=6.8.0
Description-Content-Type: text/markdown

# Code Massager

**[code-massager site →](https://sf92.github.io/code-massager/)**

A web-based code explorer for **human review sweeps** of AI-generated codebases.

Day-to-day, coding agents write almost all the code. This is great for productivity but over time it can lead to varying degrees of code rot and out-of-the-loop syndrome.

Periodically a human sweeps some or all of it, spots the patterns that are drifting — architecture, tests, naming, duplication — and leaves notes. Code Massager is where that sweep happens, and it ends by exporting a single structured artifact a coding agent can act on to (1) refactor accordingly and (2) fold the durable preferences into its own best-practice context files so the same note doesn't have to be written twice.

<img src="assets/note-to-artifact.png" width="880" alt="An illustration of the exported artifact: on the left, a line note left on a silent early return; on the right, the markdown sweep file that note becomes — a title, the repository and revision, the preamble instructing the agent to fold general preferences into its context files, and the notes grouped by scope.">

## Running locally

### Dependencies

This project relies on only `uv` and `git`. If both are available in your path, the below command will run code-massager successfully.

From a clone, in the checkout you want to sweep:

```bash
uv run serve
```

That is the whole of it. It creates its own sqlite database, serves the app on <http://127.0.0.1:8765/>, and opens a browser there. There are no bells and whistles: no Postgres, no Docker, no Redis, and nothing to configure — the repository it sweeps is the directory you ran it in, read straight off the filesystem with `git`.

Note: if you wish to deploy this remotely there is a postgres + redis + k8s stack available but it is not yet documented in this readme.

### Options

| flag | what it does | default |
| --- | --- | --- |
| `--repo PATH` | the git repo to sweep | the current directory |
| `--db PATH` | the SQLite database file | `<state>/massager.sqlite3` |
| `--port N` | the port to serve on | `8765` |
| `--config PATH` | a TOML file setting any of the above | none |
| `--no-open` | do not open a browser | the browser opens |

Example:

```bash
uv run serve --repo ~/code/some-project --port 9000 --no-open
```

The bind address is always `127.0.0.1` and is not configurable. A local run
installs no authentication at all.

### The config file

Anything a flag sets, a `[serve]` table can set instead. Pass it with
`--config`; a named file that does not exist, does not parse, or carries a key
that is not one of the four below is an error rather than a silent fall back to
the defaults.

```toml
[serve]
repo = "/home/me/code/some-project"
db = "/home/me/sweeps/some-project.sqlite3"
port = 8900
open_browser = false
```

A flag beats the file, and the file beats the default.

### Where things are kept

Under `$XDG_STATE_HOME/code-massager`, or `~/.local/state/code-massager` when
`XDG_STATE_HOME` is unset or relative:

- `massager.sqlite3` — the sweeps, notes and exports. Move it with `--db`.
- `cache` — the git tree and blob cache. Discardable; deleting it costs one
  slower browse.

**Exported artifacts land in the directory you ran the command from**, not in
the repository being swept. With the default `--repo` those are the same place;
with an explicit `--repo` elsewhere they are not.

## Stack

- **Backend** — Django + django-ninja, Postgres deployed (SQLite locally), Redis
  (a file-backed cache locally), the GitLab API, and a local git checkout
- **Frontend** — React + Vite, served by the same origin in production
- **Deploy** — GitLab CI → Helm → Kubernetes (dev then prod)

## Developing

Everything below is for changing Code Massager itself. None of it is needed to
run it.

### The checks

One command runs everything the CI pipeline checks, in cost order, stopping at
the first failure:

```bash
uv run checks
```

A green run means the `check` stage of `.gitlab-ci.yml` will pass, with one
exception it names on the way out: `test-engines`, which runs the suite against
real Postgres and Redis containers. It wraps these, and nothing stops you
running one on its own:

### The Frontend

#### Dev mode

To run the frontend locally in development mode which hot reloads to immediately reflect changes in the browser:

```bash
uv run frontend-dev     # Vite on 5173, proxying /api and /healthz to 8765
```

`frontend-dev` starts no backend and supervises nothing — one process per command, so Ctrl-C is unambiguous. It expects something serving the API on 8765. `uv run serve` in another terminal will do this. 

Note that `serve` alone is **not** a frontend dev loop: it serves the committed `api/src/web_dist` snapshot, so an edit under `web/src` is invisible to it until the bundle is rebuilt.

#### Unit tests

```bash
cd web && npm test      # vitest
```

#### The committed build

`api/src/web_dist/` is **committed**, so a fresh clone *serves* the SPA with no Node toolchain present — which is what makes `uv run serve` a single command. *Changing* the frontend is a different matter: the checks and the rebuild below both need `node_modules` installed (see "The checks"). It is a deliberate exception to "never track a build artifact", and the `stale-frontend-build` CI job rebuilds it and fails on any diff, so a bundle that has drifted from `web/src` is a red pipeline rather than a silent mismatch. **After changing anything under `web/src`, rebuild and commit the result:**

```bash
cd web && npm run build && cd .. && git status --porcelain api/src/web_dist
```

### The backend

#### Checks and tests

One command runs the backend's three, in cost order, stopping at the first
failure — and unlike `uv run checks` it never touches `web/node_modules`:

```bash
uv run backend-checks
```

`uv run checks` covers the whole set; individually, the backend's three are:

```bash
cd api && uv run isort --check-only src tests
cd api && uv run mypy
cd api && uv run pytest -q                     # needs nothing running
```

The Python project is a **uv workspace**: the lockfile and the virtualenv live at the repository root, and `api/` is the member. `uv sync --frozen` from either directory resolves to the same root `.venv`.

#### Running the backend on its own

```bash
cd api && uv run uvicorn src.django_setup.asgi:application --port 8765
```

This serves the API and nothing else — no SPA, no browser, no repository
registered. It is what `uv run frontend-dev` wants on the other end when you
are working on the frontend against a backend you are also changing; for
anything else `uv run serve` is the shorter route. Override `DATABASE_URL`
inline to point it at a database that persists: the `.env` default is
in-memory.

#### Migrations

A model change needs a migration generated and committed; the container
entrypoint runs `migrate --check` and refuses to serve an unmigrated schema.

```bash
cd api && uv run python -m src.manage makemigrations <app>
```

### The Docker stack

tbc
