Completing a conversation is now a skill the session runs on itself before it ends, instead of a button that resumes it afterwards. A resume is a new process, so it re-creates the whole transcript as fresh input tokens — the same tidy-up costs several times more once the process has exited. The spawn guidelines point the session at `.claude/skills/complete/SKILL.md` after its final notification; that pass waits 30s (a window to redirect after reading the result), publishes the summary via conv-scaffold + one subagent, tidies up, and signs off with COMPLETED. With the prompts living in a skill, the whole CTA layer goes: - backend: ctas.py, /api/ctas* (+ /run, /prompt, /reorder), the seeded prompt writer, the Cta* schemas, meta.ctas and its merge path. /api/claude-hooks stays — it just no longer lives in a CTA-shaped section. - frontend: Settings -> CTAs page, the CTA buttons under a finished thread, the CTA badge, ctaIcons, the cta:<id> composer tags, and the ctas SSE event. - the native Claude Code hooks list moves onto the main Settings page (/settings#hooks), where it is the only hooks surface left. The COMPLETED seal now reads `meta.completedAt`, derived from the transcript's COMPLETED marker the parser already flags, rather than the meta.ctas["complete"] ledger — nothing has to stamp it. What survives of the old pass is unchanged: conv-scaffold, the published summary on meta.summary, and the Summary card (ConversationCtas -> ConversationSummary). Existing meta.ctas data is left alone in the store; it is simply no longer read. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
108 lines
5.1 KiB
Markdown
108 lines
5.1 KiB
Markdown
# Project working conventions
|
|
|
|
Generic, host-agnostic conventions for working in a repo that the ai-agent tool
|
|
manages. These are the portable rules — worktree workflow, artefacts, plans,
|
|
committing, and how to signal a task is finished — with none of the homelab's
|
|
Traefik/Compose/registry specifics baked in. A concrete repo (e.g. the homelab)
|
|
keeps its own `CLAUDE.md` for its architecture and layers this on top; the
|
|
ai-agent ships this file as the starting point for a new project's root
|
|
`CLAUDE.md`.
|
|
|
|
Anything wrapped in `{{…}}` is a placeholder to fill in per repo.
|
|
|
|
## Before you edit: isolate your work
|
|
|
|
**Spin up an isolated git worktree and work there**, rather than editing files on
|
|
the checked-out branch. Before creating the worktree, pull the latest `main` so
|
|
the worktree starts from current state.
|
|
|
|
- `{{worktree_command}}` — create the worktree (e.g. `scripts/new-worktree.sh`).
|
|
- `{{pull_command}}` — pull `main` first (e.g. `git pull`, or an
|
|
auth-wrapped variant if a proxy fronts your git host).
|
|
|
|
Skip the worktree only for trivial, read-only inspection; any change to tracked
|
|
files goes through one.
|
|
|
|
## Directory conventions
|
|
|
|
- `data/` — file-backed working data: notes, logs, plans, boards. Not tracked in
|
|
git. Write a plan to `data/log/<YYYY-MM-DD>-[title].md`.
|
|
- **Generated artefacts** (images, screenshots, renders, charts, exports — any
|
|
binary/media file) tied to a project go into that project's artefact folder:
|
|
|
|
```
|
|
<projectFolder>/.ai/artefacts/<YYYY-MM-DD>/<sessionId>/<file>
|
|
```
|
|
|
|
where `<projectFolder>` is the **canonical** directory (never a worktree copy —
|
|
worktrees get deleted and aren't mounted into the viewer) and `<sessionId>` is
|
|
the current conversation id (`$CLAUDE_SESSION_ID`, else resolve it via the
|
|
session-id helper). The ai-agent viewer serves these folders: images you `Read`
|
|
from there render inline in the tool call, and the conversation details page
|
|
lists the whole set in its **Artefacts** card. Add `.ai/` to the repo's
|
|
`.gitignore` (or `.git/info/exclude`) if it isn't already ignored.
|
|
- Artefacts tied to no particular project still go under `data/` — e.g.
|
|
`data/artifacts/<YYYY-MM-DD>-[title].png` — which is likewise mounted into the
|
|
viewer and previewed inline. Files written anywhere else can't be served or
|
|
previewed.
|
|
|
|
## Committing changes
|
|
|
|
**Commit after each change.** When you finish a logical unit of work — a source
|
|
edit, a config change, a script, docs — commit it right away in its own focused
|
|
commit rather than batching unrelated changes, and push as you go.
|
|
|
|
Use `{{commit_command}}` to commit and push (e.g. the `commit-project` skill for
|
|
per-project Gitea/GitHub repos, or a repo-specific commit skill).
|
|
|
|
## Finishing a feature: open a documented PR
|
|
|
|
When the task's guidelines ask for a PR instead of a deploy, finish feature
|
|
work by pushing the branch and opening a pull request with `{{pr_command}}`
|
|
(e.g. the `open-pr` skill), assigned to the repo owner. The PR body is the
|
|
feature's documentation — write it with these sections:
|
|
|
|
- **Summary** — what changed and why, for someone reading history later.
|
|
- **Key changes** — one bullet per meaningful change, not per commit.
|
|
- **Key decisions** — non-obvious choices and their trade-offs.
|
|
- **Changelog** — user-facing Keep-a-Changelog bullets (`Added/Changed/Fixed/
|
|
Removed`); these entries are harvested to generate the project's changelog.
|
|
- **Test notes** — how it was verified.
|
|
|
|
Attach a screenshot of any visible result. Deployment happens after the PR is
|
|
reviewed and merged, not as part of the task.
|
|
|
|
## When work is done
|
|
|
|
**Send a push notification after finishing any task the user kicked off** — not
|
|
just long-running ones but every completed task — so they know it's done:
|
|
|
|
```bash
|
|
{{notify_command}} "✅ Task finished" "short outcome" --type {{type}}
|
|
```
|
|
|
|
Keep it short: say what finished and the outcome. Pass `--type <type>` to pick a
|
|
fitting icon and `--url <url>` when the task has a more relevant page than the
|
|
default (the conversation in the ai-agent viewer).
|
|
|
|
**Then end the turn with `DONE`.** After sending that notification, your final
|
|
message must end with the literal marker `DONE` on its own line. The ai-agent
|
|
conversation viewer keys "finished" off this: a conversation that has both
|
|
recorded a notification and ended its last message with `DONE` is shown as
|
|
finished, instead of being stuck on the stale `running` badge the
|
|
worktree/select-project step set at the start.
|
|
|
|
## Completing the conversation
|
|
|
|
When the task guidelines ask you to **complete** the conversation, read
|
|
`.claude/skills/complete/SKILL.md` after that final notification and follow it.
|
|
It waits 30 s (a window for the user to redirect after reading the result), then
|
|
publishes the conversation's summary (`conv-scaffold` + one subagent) and tidies
|
|
up — memories, docs, artefacts, worktrees, changelog. That pass signs off with
|
|
`COMPLETED` instead of `DONE`, which is what puts the COMPLETED seal on the
|
|
conversation card.
|
|
|
|
It runs **in this session**, before it ends: resuming a finished conversation
|
|
re-creates its whole transcript as fresh input tokens, so the same work costs
|
|
several times more once the process has exited.
|