project-init · diff
v1.0 to v1.0
40 added, 27 removed. Audit A to A.
---
name: project-init
description: 'Scaffold an unconfigured directory into a configured pi project. Interactive, profile-driven: previews the planned writes, then writes AGENTS.md, .pi/settings.json and prompt files — optionally also a knowledge base, an openspec/ scaffold, and user-global ~/.pi/agent/settings.json. Use on a bare directory, or when the dashboard''s "Initialize" button spawns this session.'
license: MIT
metadata:
author: pi-dashboard
version: "1.0"
---
# Project Init
Turn the current directory into a configured pi project through a guided,
conversational flow. You are running as a first-class interactive session —
converse, confirm, and only write files after the user agrees.
**Target directory** = your current working directory (`cwd`). All writes land
there. All profile/doctrine paths below are relative to THIS skill's directory
(the folder containing this `SKILL.md`); call it `<skill>`.
## Step 1 — Resolve the available profiles
A profile is a directory bundle: `profile.json` (optional, carries
`description` + `dox`), `AGENTS.md.tmpl`, `settings.json.tmpl`, and
`prompts/*.md`.
Enumerate profiles from two sources, **user wins by name**:
1. Shipped: `<skill>/profiles/*`
2. User: `~/.pi/project-profiles/*`
```bash
ls -1 "<skill>/profiles" 2>/dev/null
ls -1 "$HOME/.pi/project-profiles" 2>/dev/null
```
Merge: a user profile of the same name fully shadows the shipped one. A
directory is only a usable profile when it contains BOTH `AGENTS.md.tmpl` and
`settings.json.tmpl`. Read each `profile.json` for its `description` and `dox`
flag (`dox` defaults to `false` when absent or unreadable).
## Step 2 — Ask which profile
Use `ask_user` (select) to present the resolved profiles by name +
description. Shipped defaults are `coding` (TDD/simplicity/surgical, OpenSpec
on, `npm ci` worktree-init) and `docs` (writing structure, OpenSpec off, no
build step).
## Step 2b — Resolve the technology stack (stack-aware profiles only)
When the chosen profile's `profile.json` has `"stackAware": true` (the shipped
`coding` profile does), its templates carry technology placeholders
(`{{INSTALL_CMD}}`, `{{TEST_CMD}}`, `{{BUILD_CMD}}`, `{{INIT_GATE}}`,
`{{INIT_COMMAND}}`) that must be filled from the project's stack — a coding repo
may be Node, Rust, Go, Python, Java, etc.
**Auto-detect + confirm:**
1. Detect a best guess from marker files in the target directory:
| Marker file(s) | Stack | install / test / build | init gate + run |
|---|---|---|---|
| `pnpm-lock.yaml` | pnpm | `pnpm install --frozen-lockfile` / `pnpm test` / `pnpm build` | `test ! -d node_modules` / `pnpm install --frozen-lockfile` |
| `yarn.lock` | yarn | `yarn install --frozen-lockfile` / `yarn test` / `yarn build` | `test ! -d node_modules` / `yarn install --frozen-lockfile` |
| `bun.lockb` / `bun.lock` | bun | `bun install` / `bun test` / `bun run build` | `test ! -d node_modules` / `bun install` |
| `package-lock.json` / `package.json` | npm | `npm ci` / `npm test` / `npm run build` | `test ! -d node_modules` / `npm ci` |
| `Cargo.toml` | cargo | `cargo fetch` / `cargo test` / `cargo build` | `test ! -d target` / `cargo fetch` |
| `go.mod` | go | `go mod download` / `go test ./...` / `go build ./...` | `test ! -f go.sum` / `go mod download` |
| `poetry.lock` or `pyproject.toml`+`[tool.poetry]` | poetry | `poetry install` / `poetry run pytest` / `poetry build` | `test ! -d .venv` / `poetry install` |
| `requirements.txt` / `pyproject.toml` | pip | venv+pip install / `.venv/bin/pytest` / `python -m build` | `test ! -d .venv` / venv+pip install |
| `pom.xml` | maven | `mvn -q dependency:go-offline` / `mvn test` / `mvn -q package -DskipTests` | `test ! -d target` / `mvn -q dependency:go-offline` |
| `build.gradle`(`.kts`) | gradle | `./gradlew dependencies` / `./gradlew test` / `./gradlew build -x test` | `test ! -d .gradle` / `./gradlew dependencies` |
Prefer a JS lockfile over a plain `package.json`. A bare directory yields no
guess.
2. **Confirm with the user** (`ask_user`): show the detected stack (or "none
detected") and let them confirm or pick a different stack from the table.
Never scaffold a stack-aware profile without a resolved stack.
3. Substitute the chosen stack's values for the `{{…}}` placeholders when
writing the templates in Step 4. After writing, verify NO `{{…}}` placeholder
remains in `AGENTS.md` or `.pi/settings.json` — an unfilled placeholder means
the stack was not resolved; go back and ask.
## Step 3 — Ask about optional capabilities, preview, then confirm
### 3a — Ask about DOX (all profiles)
DOX seeds a directory-level documentation doctrine and wires the kb toolset (see
Step 5). It is now an **interactive choice on every profile**, not a fixed
per-profile switch. Ask with `ask_user` (confirm), pre-selecting the profile's
`dox` value as the DEFAULT answer:
> Enable DOX documentation doctrine? Seeds a directory-level AGENTS.md discipline
> into `./AGENTS.md` and enables the kb toolset. (default: `<profile.dox>`)
Record the answer as `DOX_ENABLED`. The `dox` flag in `profile.json` only sets
the default; the user's answer decides. Step 5 gates on `DOX_ENABLED`, not the
raw flag.
### 3b — Ask about OpenSpec init (coding profile only)
**Gate: only when the selected profile is `coding`** (skip for `docs` / any
OpenSpec-off profile), mirroring Steps 4b and 5. Skip the prompt entirely when an
`openspec/` directory already exists in the target (idempotent — treat as
already-initialized). Otherwise ask with `ask_user` (confirm):
> Initialize OpenSpec for this project? Runs `openspec init --tools pi`, which
> creates the `openspec/` scaffold and wires the OpenSpec commands into pi.
Record the answer as `OPENSPEC_INIT`. The actual run happens in Step 6.
### 3c — Preview + confirm
List exactly what you will write, then ask the user to confirm (`ask_user`
confirm). The writes for profile `<p>` are:
- `./AGENTS.md` ← `<skill>/profiles/<p>/AGENTS.md.tmpl` (substitute `{{PROJECT_NAME}}` with the directory basename)
- `./.pi/settings.json` ← `<skill>/profiles/<p>/settings.json.tmpl`
- `./.pi/prompts/*.md` ← each file in `<skill>/profiles/<p>/prompts/`
When `DOX_ENABLED` is true, ALSO name in the preview:
- - the DOX doctrine seed appended to `./AGENTS.md` (see Step 5)
- - the kb toolset flip written to `./.pi/dashboard/knowledge_base.json`
+ - the DOX pointer block appended to `./AGENTS.md` (see Step 5)
+ - the `doctrine` choice + kb toolset flip written to `./.pi/dashboard/knowledge_base.json`
When `OPENSPEC_INIT` is true, ALSO disclose the side effect (see Step 6) — NOT a
plain file write:
- runs `openspec init --tools pi`, scaffolding `./openspec/` and wiring pi
When the chosen profile is `coding`, ALSO disclose the possible side effect (see
Step 4b) — NOT a file write:
- may offer to `pi install` the `eng-disciplines` skills **user-globally**
(writes `~/.pi/agent/settings.json`). This is machine-wide: the skills become
available in **all** projects on this machine, not just this one. Always
opt-in via a separate prompt; never forced. Needs Node/npm on the machine
(the install runs `pi install npm:…`); the scaffolded repo itself gains no
dependency.
**Idempotency:** before writing, check whether `./AGENTS.md` or
`./.pi/settings.json` already exist. If they do, ask before overwriting — never
clobber silently.
## Step 4 — Write the scaffold
On confirmation, write the files listed above. Substitute `{{PROJECT_NAME}}`
in `AGENTS.md.tmpl` with the target directory's basename, and — for a
stack-aware profile — the stack placeholders resolved in Step 2b.
**Validate the hook.** After writing `./.pi/settings.json`, confirm its
`worktreeInit` is a valid change-A hook: a non-empty `gate` string plus a `run`
that is either `{ type: "script", command: "<non-empty>" }` or
`{ type: "agent", prompt: "<non-empty>" }`. If it is not valid, warn the user —
an invalid hook fails open (change-A ignores it) and the Initialize button will
loop back to this skill instead of running the hook.
## Step 4b — Ensure discipline skills (only when the profile is `coding`)
The `coding` template's `## Discipline Skills` checkpoint table references the
`eng-disciplines` skills. This step makes those references live. **Gate: run
only when the selected profile is `coding`** (skip for `docs` / any
OpenSpec-off profile), mirroring how Step 5 gates on `dox: true`.
**Detect** (read-only; both forms tolerate a missing `pi` binary):
```bash
pi list 2>/dev/null | grep -q pi-dashboard-eng-disciplines \
|| stat ~/.pi/agent/npm/node_modules/@blackbelt-technology/pi-dashboard-eng-disciplines >/dev/null 2>&1
```
If `pi` is not on PATH and the stat misses, treat as ABSENT but do NOT error
the init — fall through to the footnote. (Known limit: the stat form only sees
the `npm:` global path; a `git:`/renamed install is invisible and would
re-prompt.)
**PRESENT** → skills already global. Skip the prompt (idempotent re-run) and
write NO activation footnote into `./AGENTS.md`. If a prior run left the
"not detected" footnote (e.g. the skills were installed after a decline),
remove that line before exiting so the file never claims the skills are missing
when they are present. Done.
**ABSENT** → `ask_user` (confirm):
> Install the discipline skills globally? They power the checkpoint table this
> project's AGENTS.md references, and become available in ALL projects on this
> machine. Writes `~/.pi/agent/settings.json` (user-global, not project-local).
- **Yes** → run `pi install npm:@blackbelt-technology/pi-dashboard-eng-disciplines`
and verify exit 0. On success, write NO footnote. On non-zero exit (or a
missing `pi`), fall through to the footnote path.
- **No / install failed** → append ONE line under the `## Discipline Skills`
table in `./AGENTS.md`:
> Discipline skills not detected — run `pi install npm:@blackbelt-technology/pi-dashboard-eng-disciplines` to activate the checkpoints above.
The footnote is **detection-conditional**: written only on the absent/declined
branch, never on the present/installed path (so a successfully-wired project
never carries a false "not detected" line). The install is always user-global
and never forced.
- ## Step 5 — DOX doctrine seed (only when `DOX_ENABLED` is true)
+ ## Step 5 — DOX pointer seed (only when `DOX_ENABLED` is true)
> Gated on the user's Step 3a answer (`DOX_ENABLED`), NOT the raw `profile.json`
> `dox` flag — the flag only supplied the default. Skip this step entirely when
> the user declined DOX.
+ Do NOT copy any doctrine text. The per-turn DOX doctrine (READ + WRITE) is
+ injected by the `pi-dashboard-kb-extension`; the project's `AGENTS.md` carries
+ only a marker + pointer block. Tune it via `.pi/dashboard/knowledge_base.json`.
- The canonical doctrine ships once at `<skill>/dox-doctrine.md`. Do NOT copy the
- whole file. Seed a single block into `./AGENTS.md` **only when it does not
- already carry the marker** `<!-- dox-doctrine -->` (idempotent — if the marker
- is present, skip this step entirely).
+ **1. Seed the pointer block.** Append it to `./AGENTS.md` **only when it does
+ not already carry the marker** `<!-- dox-doctrine -->` (idempotent — if the
+ marker is present, skip this step entirely):
- Compose the seeded block as:
+ ```markdown
+ <!-- dox-doctrine -->
- 1. The marker line `<!-- dox-doctrine -->`
- 2. The WRITE discipline — the text between `<!-- dox:write:start -->` and
- `<!-- dox:write:end -->` in `dox-doctrine.md`.
- 3. ONE READ discipline variant — **skip this item entirely when the chosen
- profile's `AGENTS.md.tmpl` already embeds a `## Finding docs (READ
- discipline)` section** (the shipped `coding` profile does; seeding it again
- would duplicate the gate). Seed only the WRITE discipline in that case.
- Otherwise:
- - If the kb toolset is wired (this profile writes
- `knowledge_base.json` with `indexAgentsFiles`/`directoryLevelAgents`),
- use the text between `<!-- dox:read:kb:start -->` and
- `<!-- dox:read:kb:end -->` (references `kb agents` / `kb_search`).
- - Otherwise use the text between `<!-- dox:read:manual:start -->` and
- `<!-- dox:read:manual:end -->` (manual chain-walk; no kb references).
+ ## DOX doctrine
- Strip the delimiter comments from the seeded text. Append the block to
- `./AGENTS.md`.
+ Per-turn DOX doctrine — the kb-first READ discipline and the directory `AGENTS.md`
+ WRITE discipline — is injected by the `pi-dashboard-kb-extension`. Tune it in
+ `.pi/dashboard/knowledge_base.json` under the `doctrine` key (`inject`, `write`).
+ ```
- Then write `./.pi/dashboard/knowledge_base.json` (when absent) enabling the
- directory-level AGENTS.md toolset:
+ **2. Record the doctrine choice.** Read-merge-write
+ `./.pi/dashboard/knowledge_base.json` — read the file if present, set ONLY the
+ `doctrine` key, preserve every other key, write valid JSON. Since `DOX_ENABLED`
+ is true here, write:
```json
+ "doctrine": { "inject": "kb", "write": true }
+ ```
+
+ When the file is absent, also enable the directory-level AGENTS.md toolset:
+
+ ```json
{
"sources": [{ "kind": "filesystem", "ref": "." }],
"indexAgentsFiles": true,
- "directoryLevelAgents": { "enabled": true }
+ "directoryLevelAgents": { "enabled": true },
+ "doctrine": { "inject": "kb", "write": true }
}
```
+
+ **3. Scaffold the directory `AGENTS.md` tree** (path-only rows):
+
+ ```bash
+ kb dox init
+ ```
+
+ **4. Offer `dox-describe`.** After the tree is scaffolded, ask the user
+ (`ask_user` confirm): "Fill the empty Purpose cells now with `dox-describe`?
+ (one subagent per `AGENTS.md`, plan-then-confirm, ≤50 rows per run)". On yes,
+ invoke the `dox-describe` skill (ships with `packages/kb-extension`); on no,
+ tell the user they can run it later.
## Step 6 — Run OpenSpec init (only when `OPENSPEC_INIT` is true)
Gated on the user's Step 3b answer (`OPENSPEC_INIT`); this only becomes true for
the `coding` profile when no `openspec/` dir pre-existed. Mirrors Step 4b's
side-effect pattern — opt-in, verified, never forced.
**Run** (always non-interactive so it never hijacks the conversation):
```bash
openspec init --tools pi
```
The `--tools pi` flag pins the wiring to pi and skips OpenSpec's interactive tool
picker. Verify exit 0 and that `./openspec/` now exists. On success, tell the
user OpenSpec is wired (the `coding` AGENTS.md already documents
`openspec change new <name>`). On non-zero exit (or a missing `openspec` binary),
warn the user and point them at `openspec init --tools pi` to run manually — do
NOT error the init; the scaffold is already written.
**Idempotent re-run:** if `./openspec/` already existed, Step 3b never asked, so
`OPENSPEC_INIT` is false and this step is skipped — no clobber.
## Step 7 — Done
Confirm what was written. Writing `worktreeInit` flips the directory to
"configured": the next dashboard **Initialize** click runs the hook (change-A)
instead of re-launching this skill. Tell the user they can click Initialize
again to run `npm ci` (or the profile's hook).