diff --git a/CHANGELOG.md b/CHANGELOG.md index fffc8e5..0571673 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,14 +12,15 @@ CalVer image tags (`stable-YYYY.MM.N`, `dev-YYYY.MM.N`) are produced for every C ### Added +- **Agent Workspace Prompt** — admin-editable Jinja2 markdown template for the analyst's `CLAUDE.md`, surfaced in their workspace by `da analyst setup`. Default = rich briefing with RBAC-filtered tables/metrics/marketplaces context. Edit at `/admin/workspace-prompt`. Endpoints: `GET /api/welcome` (analyst-facing, auth required), `GET/PUT/DELETE /api/admin/workspace-prompt-template`, `POST /api/admin/workspace-prompt-template/preview`. CLI: `da analyst setup` writes `CLAUDE.md` by default; new `--no-claude-md` flag opts out. See `docs/agent-workspace-prompt.md`. - **Agent Setup Prompt** — customizable bash setup script shown on `/setup` and copied by the dashboard clipboard CTA. Default = the live `setup_instructions.resolve_lines()` output (TLS trust bootstrap, CLI install, login, marketplace, skills). Admin override at `/admin/agent-prompt` — full replacement of the default, not a banner added on top. Override flows to both the `/setup` page display and the dashboard clipboard payload. Jinja2 is available for `{{ instance.name }}` etc.; `{server_url}` and `{token}` are JS-substituted at clipboard-copy time and survive Jinja2 rendering unchanged. REST API: `GET /api/admin/welcome-template` returns `{content, default, updated_at, updated_by}` (`content` is `null` when no override is set; `default` is always the live computed script); `PUT` to set an override; `DELETE` to clear; `POST /api/admin/welcome-template/preview` for live preview without persisting. Available Jinja2 placeholders: `instance.{name,subtitle}`, `server.{url,hostname}`, `user` (may be `null` for anonymous visitors), `now`, `today`. Override content is HTML-sanitized post-render (script/iframe/event-handler strip). See `docs/agent-setup-prompt.md`. -- DuckDB schema v21: `welcome_template` singleton table backing the banner override. Auto-migration v20→v21 on first start. +- DuckDB schema v21: `welcome_template` singleton table backing the Agent Setup Prompt override. Auto-migration v20→v21 on first start. - DuckDB schema v22: `setup_banner` table reserved (no consumers; retained for forward compatibility with already-migrated instances). +- DuckDB schema v23: `claude_md_template` singleton table backing the Agent Workspace Prompt override. Auto-migration v22→v23. ### Changed -- **BREAKING (CLI):** `da analyst setup` no longer generates a `CLAUDE.md` file in the analyst workspace. Workspace-context customisation is handled via the `/setup` page banner instead. Existing analysts with a server-generated `CLAUDE.md` may delete it manually if desired. -- **BREAKING (API):** `GET /api/welcome` removed. The endpoint was internal-only (consumed only by the CLI's now-removed `CLAUDE.md` generation step). +- `da analyst setup` writes `CLAUDE.md` to the analyst workspace from the server-rendered template (fetched via `GET /api/welcome`). Use `--no-claude-md` to opt out. Analysts who ran setup while CLAUDE.md generation was temporarily absent will have their file written on the next `da analyst setup` run. - `/install` page renamed to `/setup` ("Setup local agent" nav label) with 302 redirect from `/install`. - Dashboard "What Claude Code will receive" inline preview replaced with a link to `/setup` for the canonical view. diff --git a/docs/agent-workspace-prompt.md b/docs/agent-workspace-prompt.md new file mode 100644 index 0000000..2488295 --- /dev/null +++ b/docs/agent-workspace-prompt.md @@ -0,0 +1,114 @@ +# Agent Workspace Prompt + +The agent workspace prompt is the `CLAUDE.md` file written to each analyst's +workspace by `da analyst setup`. It gives Claude Code context about the +connected instance: available tables (RBAC-filtered), business metrics, installed +plugins, and operational rules for the analyst. + +## When is CLAUDE.md written? + +`da analyst setup` fetches `GET /api/welcome` and writes the rendered markdown +to `/CLAUDE.md` on every run (including `--force` re-initialisation). + +To skip writing CLAUDE.md: + +```bash +da analyst setup --server-url https://agnes.example.com --no-claude-md +``` + +**Analysts who ran setup while CLAUDE.md generation was temporarily absent** will +have their file written on the next `da analyst setup` run. Any existing +`CLAUDE.md` is overwritten with the current server template. + +The companion `CLAUDE.local.md` (at `.claude/CLAUDE.local.md`) is **never** +overwritten — it is the analyst's personal customisation space. + +## Editing the template + +Admins configure the template via: + +- **Admin UI:** `/admin/workspace-prompt` — Jinja2 markdown editor with a + placeholder cheatsheet, live preview (rendered against the calling admin's + RBAC context), and save/reset actions. +- **REST API:** + - `GET /api/admin/workspace-prompt-template` — returns + `{content, default, updated_at, updated_by}`. `content` is `null` when no + override is set; `default` is always the live rendered default. + - `PUT /api/admin/workspace-prompt-template` with body `{"content": "..."}` — + validates Jinja2 syntax against two stubs (authenticated user, minimal user) + before persisting. Returns `400` on syntax errors or unknown placeholders. + - `DELETE /api/admin/workspace-prompt-template` — clears the override; reverts + to the rich default template from `config/claude_md_template.txt`. + - `POST /api/admin/workspace-prompt-template/preview` with + body `{"content": "..."}` — renders arbitrary content against the calling + admin's live RBAC context without persisting. Used by the editor's Preview + button. + +The override lives in `system.duckdb` (table `claude_md_template`, singleton +row id=1). `DELETE` NULLs `content`; audit trail (`updated_at`, `updated_by`) +is preserved. + +## Default template + +The default template is `config/claude_md_template.txt` (Jinja2 markdown). +When no admin override is set, this file is rendered for every `GET /api/welcome` +request. Operators can customise it per-instance via the UI — or ship a modified +default by editing the file before deployment. + +## Template language + +[Jinja2](https://jinja.palletsprojects.com/) with `autoescape=False` and +`StrictUndefined`. Autoescape is off because the rendered output is markdown, not +HTML. `StrictUndefined` means any typo in a placeholder name raises an error at +PUT validation time, so the admin is notified immediately. + +## Available placeholders + +| Placeholder | Type | Notes | +|---|---|---| +| `instance.name` | string | `instance.name` from `instance.yaml` | +| `instance.subtitle` | string | `instance.subtitle` from `instance.yaml` | +| `server.url` | string | Full server URL at render time | +| `server.hostname` | string | Host part only | +| `sync_interval` | string | e.g. `"1h"` from `instance.yaml` | +| `data_source.type` | string | `keboola`, `bigquery`, or `local` | +| `tables` | list[dict] | RBAC-filtered list of `{name, description, query_mode}` | +| `metrics.count` | int | Total metric definitions in DB | +| `metrics.categories` | list[str] | Sorted unique category names | +| `marketplaces` | list[dict] | RBAC-filtered `{slug, name, plugins:[{name}]}` | +| `user.id` | string | Analyst user ID | +| `user.email` | string | Analyst email | +| `user.name` | string | Analyst display name | +| `user.is_admin` | bool | Whether the user is in the Admin group | +| `user.groups` | list[str] | User's group names | +| `now` | datetime (UTC, tz-aware) | Server time at render | +| `today` | string (`YYYY-MM-DD`) | Server date | + +## Example: iterating tables + +```jinja2 +## Available Datasets +{% for t in tables -%} +- `{{ t.name }}`{% if t.description %} — {{ t.description }}{% endif %} +{% else -%} +- _No tables registered yet._ +{% endfor %} +``` + +## Example: conditional marketplace section + +```jinja2 +{% if marketplaces %} +## Plugins +{% for mp in marketplaces %} +- **{{ mp.name }}**: {{ mp.plugins | map(attribute="name") | join(", ") }} +{% endfor %} +{% endif %} +``` + +## Resetting to the built-in default + +Click **Reset to default** in the admin UI, or call +`DELETE /api/admin/workspace-prompt-template`. The next analyst who runs +`da analyst setup` will receive the rich default template from +`config/claude_md_template.txt`.