@monster-orange/den 0.4.12 → 0.4.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/README.md +7 -7
  3. package/docs/distributions.md +146 -10
  4. package/package.json +1 -1
  5. package/src/shared/agents/default-pack/be-dev.md +1 -0
  6. package/src/shared/agents/default-pack/devops.md +1 -0
  7. package/src/shared/agents/default-pack/fe-dev.md +1 -0
  8. package/src/shared/agents/default-pack/orchestrator.md +3 -1
  9. package/src/shared/agents/default-pack/planner.md +1 -0
  10. package/src/shared/agents/default-pack/reviewer.md +1 -0
  11. package/src/shared/agents/default-pack/tester.md +16 -0
  12. package/src/shared/agents/default-pack/ux-designer.md +1 -0
  13. package/src/shared/agents/den-claude.ts +119 -13
  14. package/src/shared/agents/permissions.ts +108 -0
  15. package/src/shared/agents/persona-store.ts +286 -0
  16. package/src/shared/agents/personas.ts +116 -12
  17. package/src/shared/agents/records.ts +28 -0
  18. package/src/shared/agents/roles.ts +288 -0
  19. package/src/shared/agents/runtime.ts +41 -0
  20. package/src/shared/den-config.ts +20 -1
  21. package/src/ui/cli/agent-ops.ts +535 -17
  22. package/src/ui/cli/agents-command.ts +12 -2
  23. package/src/ui/cli/env-probe.ts +4 -1
  24. package/src/ui/mcp/tools.ts +1 -1
  25. package/src/ui/server/agent-routes.ts +260 -21
  26. package/src/ui/server/public/agent-start.js +3 -2
  27. package/src/ui/server/public/agents-page.js +499 -48
  28. package/src/ui/server/public/agents.html +58 -15
  29. package/src/ui/server/public/i18n/es.js +120 -0
  30. package/src/ui/server/public/i18n/ja.js +120 -0
  31. package/src/ui/server/public/persona-ui.css +539 -0
  32. package/src/ui/server/public/persona-ui.js +796 -0
  33. package/src/ui/server/public/settings-page.js +4 -9
  34. package/src/ui/server/public/settings-registry.js +22 -0
  35. package/src/ui/server/public/settings.html +1 -0
  36. package/src/ui/server/public/shell.js +117 -0
  37. package/src/ui/server/public/topbar.js +2 -2
  38. package/src/ui/server/route-classes.ts +18 -0
  39. package/src/ui/server/session-routes.ts +21 -5
  40. package/src/ui/server/ui-assets.ts +3 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,108 @@ All notable changes to Den are documented here. Each release lists the
4
4
  config and manifest changes a distribution must make to move to it under
5
5
  **Migrations**.
6
6
 
7
+ ## [0.4.13] - 2026-11-07
8
+
9
+ ### Added
10
+
11
+ - **The Start dialog.** + Start agent opens the dialog from the Den Shell
12
+ design. Pick a persona from a list you can search, with category and
13
+ source chips (built-in, distribution, mine); with no filter it is grouped
14
+ by category, and Plain session (your shell in the workspace, no agent) is
15
+ pinned last. Each persona shows its one-line description. "What's a
16
+ persona?" explains the idea, and Details shows the instructions, runtime,
17
+ model and version, permission mode and allowed commands. Then Runtime,
18
+ Workspace, an optional first instruction, and a collapsed Advanced
19
+ section: Model and Version, Permission mode, and Extra allowed commands,
20
+ with a preview of exactly what the runtime will be given.
21
+ - **First instruction.** Den types it into the agent's session once the
22
+ session is ready, as a bracketed paste when the runtime asks for one,
23
+ then Enter. It never goes on the command line and is never stored; the
24
+ audit log records its length only.
25
+ - **Your own personas.** + New persona, Duplicate and Edit write personas
26
+ of your own to `<state_dir>/personas/<id>.md` (0600). Settings ›
27
+ Personas lists every persona with the same search and chips: View or
28
+ Edit, Duplicate, Delete (your own only), + New persona. Built-in and
29
+ distribution personas are read-only; Duplicate makes your own copy.
30
+ Routes: `GET /api/personas/<id>`, `POST /api/personas`, `POST` and
31
+ `DELETE /api/personas/<id>`.
32
+ - **Models and versions.** A runtime lists its models (den-claude: opus,
33
+ sonnet and haiku, each "latest" or a pinned version, from a catalog
34
+ shipped with Den). A pinned version runs as `--model <full id>`. A
35
+ persona or session pinned to an older version says "newer available";
36
+ your own persona has Use latest.
37
+ - **Permissions as intent.** Ask for everything, Auto-approve edits in the
38
+ worktree, or Auto-approve all, plus extra allowed commands as plain
39
+ prefixes. den-claude turns them into `--permission-mode` and
40
+ `Bash(<prefix>)` / `Bash(<prefix> *)` allow rules beside Den's deny
41
+ rules. Other runtimes take none from Den yet, and the dialog says "not
42
+ supported on <runtime>" rather than dropping one.
43
+ - **Override tags.** A session started with choices that differ from its
44
+ persona is tagged in the list: "model: haiku", "pinned <version>",
45
+ "auto-approve edits", "custom tools", the runtime. "auto-approve all" is
46
+ always tagged. Start fresh and Relaunch keep the choices.
47
+ - **Roles and categories.** Den's roles are planner, orchestrator, sde,
48
+ reviewer, tester and designer. Each is a template (instructions,
49
+ permission mode, a read-only command preset) that fills in New persona
50
+ without overwriting what you typed, and it adds nothing hidden to a
51
+ prompt. A distribution adds roles and categories (`agents.roles`,
52
+ `agents.categories`) and may extend a Den role's preset, never redefine
53
+ it.
54
+ - **Plain session from the page.** `POST /api/workspaces/<ws>/sessions`
55
+ starts your login shell in the workspace's folder (capped by
56
+ `sessions.max_concurrent`).
57
+ - **A tester persona** in Den's default pack.
58
+
59
+ ### Changed
60
+
61
+ - `POST /api/workspaces/<ws>/agents` takes `version`, `permission`,
62
+ `allow` and `instruction`, and refuses an unknown field. Each is checked
63
+ against the runtime's catalog: a model or version it doesn't list, a
64
+ mode other than `ask`, `edits` or `all`, or a command that isn't a plain
65
+ prefix (at most 32, 120 characters each) is a 400. An agent's token gets
66
+ 403 on every persona route and on a plain session.
67
+ - The agent record keeps `version`, `permission`, `allow` and `overrides`.
68
+ - A persona's `role` is a role key; a sentence there still reads as its
69
+ description. Personas gain `display_name`, `category`, `version` and
70
+ `allowed_commands`. `permission_mode` takes `ask`, `edits` or `all`
71
+ (Claude Code's own modes still read). `agents personas` shows SOURCE and
72
+ ROLE columns.
73
+ - fe-dev, be-dev and devops are role `sde`; ux-designer is role
74
+ `designer` in category Design.
75
+ - A persona whose runtime isn't installed starts on the first one that is,
76
+ and the Start dialog says so.
77
+ - **`sessionTitle` is the name a session shows.** `agents list --json`,
78
+ `GET /api/sessions` and the start and resume answers (MCP
79
+ `agent_resume`) now carry the resolved name (Den's rename or the
80
+ runtime's, the later) as `sessionTitle`. 0.4.12 called it `sessionName`,
81
+ which is the agent record's session handle (`<dist>-<ws>:<id>`) and has
82
+ been since 0.4.0. Records are unchanged.
83
+
84
+ ### Fixed
85
+
86
+ - **Settings shows a saved choice at once.** Agents tab layout and Open
87
+ file links in saved, but the pressed button stayed on the old value
88
+ until a reload. The page now takes every setting the save returns.
89
+ - **Claude Code's `/rename "x"` shows as x.** Its title keeps the quotes;
90
+ Den drops one pair around the whole name, for display only.
91
+ - **The terminal fit check is exact again.** The browser test compares
92
+ what is left below and beside a terminal with the cell size xterm drew
93
+ with (now on each pane as `data-cell-w` and `data-cell-h`), less than
94
+ one cell, so a missed row fails on any font.
95
+
96
+ ### Migrations
97
+
98
+ - None required. A persona whose `role:` is a sentence keeps working (it
99
+ becomes the description); set `role:` to a role key to give it one.
100
+ - A persona pack may now set `display_name`, `category`, `version` and
101
+ `allowed_commands`. Use `permission_mode: ask | edits | all` for a mode
102
+ every runtime can translate.
103
+ - `agents.roles` and `agents.categories` are new keys of `agents:` in the
104
+ distribution's config (see docs/distributions.md, Personas).
105
+ - Read `sessionTitle`, not `sessionName`, for the name a session shows in
106
+ `agents list --json`, `GET /api/sessions` and the start and resume
107
+ answers. No record needs changing.
108
+
7
109
  ## [0.4.12] - 2026-11-07
8
110
 
9
111
  ### Added
package/README.md CHANGED
@@ -10,8 +10,8 @@
10
10
  </p>
11
11
 
12
12
  <picture>
13
- <source media="(prefers-color-scheme: dark)" srcset="docs/images/0.4.12/hero-dark.png">
14
- <img src="docs/images/0.4.12/hero-light.png" width="100%" alt="Den's Graph page: the demo workspace's services grouped by application, all healthy, with the status chip and the left rail">
13
+ <source media="(prefers-color-scheme: dark)" srcset="docs/images/0.4.13/hero-dark.png">
14
+ <img src="docs/images/0.4.13/hero-light.png" width="100%" alt="Den's Graph page: the demo workspace's services grouped by application, all healthy, with the status chip and the left rail">
15
15
  </picture>
16
16
 
17
17
  ## Set up by your agent
@@ -38,7 +38,7 @@ It doesn't replace your tools. Your repos, Docker, your test runners, your secre
38
38
  yours. Den connects them and turns on good practice from the start.
39
39
 
40
40
  <p align="center">
41
- <img src="docs/images/0.4.12/up.gif" width="100%" alt="make up in the Graph page: every stack down, then demo-api, demo-web and demo-notes come up in dependency order until the status chip reads All 3 healthy">
41
+ <img src="docs/images/0.4.13/up.gif" width="100%" alt="make up in the Graph page: every stack down, then demo-api, demo-web and demo-notes come up in dependency order until the status chip reads All 3 healthy">
42
42
  </p>
43
43
 
44
44
  ## How it helps
@@ -68,10 +68,10 @@ yours. Den connects them and turns on good practice from the start.
68
68
  lives in your own repo.
69
69
 
70
70
  <p align="center">
71
- <img src="docs/images/0.4.12/agents.png" width="49%" alt="The Agents tab: the demo workspace's sessions on the left, and one open in a terminal pane on the right, running demo-api's test and then the service">
72
- <img src="docs/images/0.4.12/workspaces.png" width="49%" alt="The Workspaces page: the demo workspace's repos with their health, mode and Start, Stop, Restart and Logs actions">
73
- <img src="docs/images/0.4.12/status-drawer.png" width="49%" alt="The status drawer: nothing needs attention, and every service in the demo workspace, by application, with its latency">
74
- <img src="docs/images/0.4.12/edge.png" width="49%" alt="The Edge page: the edge container up, its ports and TLS, and every route it serves, Den's own and each stack's, with its upstream">
71
+ <img src="docs/images/0.4.13/agents.png" width="49%" alt="The Agents tab: the demo workspace's sessions on the left, and one open in a terminal pane on the right, running demo-api's test and then the service">
72
+ <img src="docs/images/0.4.13/workspaces.png" width="49%" alt="The Workspaces page: the demo workspace's repos with their health, mode and Start, Stop, Restart and Logs actions">
73
+ <img src="docs/images/0.4.13/status-drawer.png" width="49%" alt="The status drawer: nothing needs attention, and every service in the demo workspace, by application, with its latency">
74
+ <img src="docs/images/0.4.13/edge.png" width="49%" alt="The Edge page: the edge container up, its ports and TLS, and every route it serves, Den's own and each stack's, with its upstream">
75
75
  </p>
76
76
 
77
77
  ## Get started
@@ -2054,10 +2054,25 @@ server scoped to its workspace.
2054
2054
  unique in the distribution, so a second workspace's planner gets
2055
2055
  `planner-<ws>` unless you give `--id`.
2056
2056
  - The **agent record**, `<stateDir>/agents/<id>.json` (0600), holds its id,
2057
- persona, runtime, model, workspace, cwd, session and times. It never holds
2058
- the environment or the command line. `list` reads it for persona, runtime
2057
+ persona, runtime, model, workspace, cwd, session and times, and what it was
2058
+ started with from the Start dialog: `version` (the model's, "latest" or
2059
+ pinned), `permission` (`ask`, `edits` or `all`), `allow` (extra allowed
2060
+ commands) and `overrides` (which of these differ from its persona). Start
2061
+ fresh and Relaunch keep them. It never holds the environment, the command
2062
+ line or a first instruction. `list` reads it for persona, runtime
2059
2063
  and model; a record whose session has gone is listed as `stopped` (after
2060
2064
  `stop`) or `ended`.
2065
+ - **Two name fields.** In the record, `sessionName` is the session's
2066
+ handle, `<dist>-<ws>:<id>` (what `sessions attach` takes); it has meant
2067
+ that since 0.4.0, and `displayName` is the name a person gave the agent.
2068
+ In `agents list --json`, `GET /api/sessions` and the start and resume
2069
+ answers (MCP `agent_resume`), `sessionTitle` is the name its session
2070
+ shows: the latest of Den's rename and the runtime's own (Claude Code's
2071
+ `/rename`, one pair of surrounding quotes dropped), absent when there is
2072
+ none. The handle there is `name` (`/api/sessions`) or `session` (the
2073
+ start and resume answers). 0.4.12 sent the name shown as `sessionName`;
2074
+ read `sessionTitle` instead. Records are unchanged, so none written
2075
+ before needs migrating.
2061
2076
  - `list` shows agents only: sessions with an agent record. A session from
2062
2077
  `sessions start` is not an agent; `sessions list` shows it.
2063
2078
  - `stop` sends its session TERM and marks it stopped. `resume` works only
@@ -2104,13 +2119,78 @@ started. The workspace header counts them ("2 agents").
2104
2119
  workspace, and **Open** on a row with a session opens its terminal there.
2105
2120
 
2106
2121
  The routes: `GET /api/workspaces/<ws>/agents`, `POST
2107
- /api/workspaces/<ws>/agents` (`{ persona, runtime?, model?, id? }`), `POST
2108
- /api/workspaces/<ws>/agents/<id>/stop` and `…/resume`, and `GET
2109
- /api/personas` (personas and runtimes). Errors are the usual
2122
+ /api/workspaces/<ws>/agents` (`{ persona, runtime?, model?, version?,
2123
+ permission?, allow?, instruction?, id? }`, see
2124
+ [The Start dialog](#the-start-dialog)), `POST
2125
+ /api/workspaces/<ws>/agents/<id>/stop` and `…/resume`, `POST
2126
+ /api/workspaces/<ws>/sessions` (a plain session), `GET /api/personas`
2127
+ (personas, runtimes with their models and permission translation, roles
2128
+ and categories), and the persona routes under
2129
+ [Personas](#personas). Errors are the usual
2110
2130
  `{ ok: false, error, code, requestId }`: `cap` (429),
2111
2131
  `conflict` (409, e.g. already running, or no resume), `not_found` (404),
2112
2132
  `exited_at_once` (502, with the session's last lines).
2113
2133
 
2134
+ ### The Start dialog
2135
+
2136
+ **+ Start agent** on the Agents tab (or `/agents?start=1`) opens the Start
2137
+ dialog:
2138
+
2139
+ - **Persona**: a list you can search (name, role, description), with
2140
+ category chips (only the categories that have personas) and source chips
2141
+ (built-in, distribution, mine). With no filter set it is grouped by
2142
+ category. Each row is the persona's name ("Rita (reviewer)", else its
2143
+ id), its source badge and its one-line description, and "not installed"
2144
+ when its runtime isn't. **Plain session** is pinned last: your login
2145
+ shell in the workspace's folder, no agent. **What's a persona?** explains
2146
+ the idea; **Details** shows the selected persona's instructions, runtime,
2147
+ model and version, permission mode, role preset and extra commands.
2148
+ **+ New persona**, **Duplicate** and (for your own) **Edit** open the
2149
+ persona editor, and the dialog picks what you saved.
2150
+ - **Runtime** and **Workspace**. Installed runtimes come first; the rest
2151
+ are listed, disabled, and named under the field ("Not found on this
2152
+ machine: …"). A persona whose runtime isn't installed starts on the first
2153
+ one that is, and the dialog says so. The line under the runtime says what
2154
+ will run (see below).
2155
+ - **First instruction** (optional, up to 8000 characters): the agent's
2156
+ first message. Den types it into the agent's session once the session is
2157
+ ready (it has printed and gone quiet, or after 30 s), through the session
2158
+ host, as a bracketed paste when the runtime asked for one, then Enter. It
2159
+ is never a command-line argument and never stored; the audit log records
2160
+ its length only.
2161
+ - **Advanced** (collapsed): **Model** and **Version** (the runtime's own
2162
+ list; "Persona default" first), the **Permission mode** (Ask for
2163
+ everything, Auto-approve edits in the worktree, Auto-approve all), the
2164
+ role's command preset as chips, **Extra allowed commands** (plain command
2165
+ prefixes, one per line, no patterns), and a preview of exactly what the
2166
+ runtime will be given. A command a runtime can't take says "not supported
2167
+ on <runtime>", and a pattern says "use a plain prefix"; neither is ever
2168
+ dropped silently. "Edit raw runtime config…" is not there yet.
2169
+
2170
+ What you change in Advanced applies to that launch only, and the session's
2171
+ row is tagged: "model: haiku", "pinned claude-opus-4-8", "auto-approve
2172
+ edits", "custom tools", the runtime, and always "auto-approve all" for an
2173
+ agent started so. An agent pinned to an older version says "newer
2174
+ available".
2175
+
2176
+ Every field is checked on the server, not only in the page. The model and
2177
+ version must be ones the runtime lists, the permission one of `ask`,
2178
+ `edits` or `all`, and `allow` a list of at most 32 plain prefixes (letters,
2179
+ digits, single spaces and `. _ / : = @ % + , -`, at most 120 characters
2180
+ each). An unknown field is refused. A person's start only: an agent's
2181
+ token gets 403.
2182
+
2183
+ **How each runtime takes the choices.** den-claude (Claude Code): a model is
2184
+ `--model <alias>` (`opus`, `sonnet`, `haiku`), a pinned version is `--model
2185
+ <full id>`. The model list is a catalog shipped with each Den release,
2186
+ because `claude` can't list its models. The permission mode is
2187
+ `--permission-mode` (`default`, `acceptEdits`, `bypassPermissions`), and
2188
+ each command prefix becomes two allow rules, `Bash(<prefix>)` and
2189
+ `Bash(<prefix> *)`, in the agent's settings file beside Den's deny rules
2190
+ (deny wins). Codex CLI, Gemini CLI, Grok Build and generic take no model,
2191
+ mode or commands from Den yet: the dialog says "not supported on <runtime>"
2192
+ and the start is refused if you set one.
2193
+
2114
2194
  ### The Agents tab
2115
2195
 
2116
2196
  "Agents" in the rail (Develop, after Graph) is `/agents`: every session and
@@ -2181,7 +2261,10 @@ agent of the workspaces in view, and their terminals in the browser.
2181
2261
  pane header adds ` · <name>` after the runtime, and the tab, the list and
2182
2262
  ⌘K add it too, when it is not empty and says more than the agent's own
2183
2263
  name or persona. `GET /api/sessions`, `agents list --json` and the start
2184
- and resume answers (MCP `agent_resume`) carry it as `sessionName`.
2264
+ and resume answers (MCP `agent_resume`) carry it as `sessionTitle`
2265
+ (0.4.12: `sessionName`, which is the record's session handle). A
2266
+ `/rename "api spike"` shows as api spike: one pair of quotes around the
2267
+ whole name is dropped, for display only.
2185
2268
  Codex CLI, Gemini CLI and Grok Build have no name of their own yet. A
2186
2269
  catalog entry's `titleFromOsc: true` marks a runtime whose terminal
2187
2270
  title (OSC 0/2) names the session; no built-in runtime sets it, and the
@@ -2386,13 +2469,18 @@ a Claude Code agent file, read as it is, plus a few keys of Den's own:
2386
2469
  ```markdown
2387
2470
  ---
2388
2471
  name: planner # the id (or `id:`; default: the file name)
2389
- description: Writes stories and their acceptance criteria. # or `role:`
2472
+ display_name: Rita # optional; shown as "Rita (reviewer)"
2473
+ role: planner # a role (see Roles); a sentence here is the description
2474
+ category: engineering # see Categories; default: the role's
2475
+ description: Writes stories and their acceptance criteria. # one line
2390
2476
  model: some-model # optional; `inherit` means none
2477
+ version: latest # or a pinned version of the model
2478
+ allowed_commands: [npm test] # extra plain prefixes allowed without asking
2391
2479
  tools: Read, Grep # hints for the runtime; Den restricts nothing with them
2392
2480
  standing: true # starts with its workspace (later slices)
2393
2481
  context: [docs/specs, the design system] # what to read first
2394
2482
  runtime: generic # default den-claude
2395
- permission_mode: plan # den-claude only; default `default`
2483
+ permission_mode: ask # ask, edits or all (Claude Code's own modes still read)
2396
2484
  den_hooks: false # Den's guardrail hooks for this persona (see Guardrails)
2397
2485
  den_instructions: false # Den's shared instructions for this persona
2398
2486
  argv: ["some-cli", "--model", "{model}", "--mcp-config", "{mcp_config}"]
@@ -2402,8 +2490,9 @@ You are the planner. …
2402
2490
  ```
2403
2491
 
2404
2492
  Den ships a default pack: `planner` and `orchestrator` (standing), `fe-dev`,
2405
- `be-dev`, `devops`, `reviewer` and `ux-designer`. Add your own packs, read in
2406
- order after Den's; a persona with the same id replaces the earlier one:
2493
+ `be-dev` and `devops` (role `sde`), `reviewer`, `tester` and `ux-designer`
2494
+ (role `designer`, category Design). Add your own packs, read in order after
2495
+ Den's; a persona with the same id replaces the earlier one:
2407
2496
 
2408
2497
  ```yaml
2409
2498
  agents:
@@ -2416,6 +2505,53 @@ used without converting them. Files without front matter (a README) are
2416
2505
  skipped; two files with one id in a pack, or a pack that isn't a directory,
2417
2506
  are errors naming the file or the key.
2418
2507
 
2508
+ **Your own personas** live in `<stateDir>/personas/<id>.md` (0600), in the
2509
+ same format, read last: one wins over a pack's persona of the same id.
2510
+ Settings › Personas lists every persona with the Start dialog's search and
2511
+ chips: **View** (or **Edit** for your own), **Duplicate**, **Delete** (your
2512
+ own only) and **+ New persona**. A built-in or distribution persona is
2513
+ read-only; Duplicate makes your own copy to edit. "newer available" marks a
2514
+ persona pinned to an older version, with **Use latest** for your own. The
2515
+ routes: `GET /api/personas/<id>` (with its instructions), `POST
2516
+ /api/personas` (new), `POST /api/personas/<id>` (edit; 409 `read_only` for
2517
+ one that isn't yours) and `DELETE /api/personas/<id>`. Each field is
2518
+ checked: the role, category, runtime, model and version must be registered,
2519
+ the description one line of at most 200 characters, the name at most 40,
2520
+ the instructions at most 20000 characters with no control characters but
2521
+ newlines and tabs. An agent's token can't create, change or delete a
2522
+ persona (403); a person sets what agents may do.
2523
+
2524
+ **Roles.** A role is a template and a key: default instructions, a
2525
+ permission mode and a command preset (read-only commands, allowed without
2526
+ asking). Den's roles are `planner`, `orchestrator`, `sde`, `reviewer`,
2527
+ `tester` and `designer`. Picking a role in New persona fills the form in,
2528
+ never over text you typed; a role adds nothing hidden to an agent's prompt.
2529
+ A distribution adds roles, and may extend a Den role's preset but not
2530
+ redefine it:
2531
+
2532
+ ```yaml
2533
+ agents:
2534
+ roles:
2535
+ copywriter:
2536
+ description: Drafts release notes and site copy.
2537
+ instructions: Draft copy in the house voice. Leave drafts for review.
2538
+ mode: ask # ask, edits or all
2539
+ preset: [vale]
2540
+ category: marketing
2541
+ reviewer:
2542
+ preset: [semgrep] # added to Den's preset
2543
+ ```
2544
+
2545
+ **Categories** group personas in the Start list and Settings › Personas.
2546
+ Den's are Engineering, Design, Product, Marketing and Operations; a
2547
+ distribution adds more:
2548
+
2549
+ ```yaml
2550
+ agents:
2551
+ categories:
2552
+ - { id: legal, label: Legal }
2553
+ ```
2554
+
2419
2555
  ### Guardrails: hooks, shared instructions, hand-backs
2420
2556
 
2421
2557
  Every persona in Den's default pack gets two things with no setup: Den's
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monster-orange/den",
3
- "version": "0.4.12",
3
+ "version": "0.4.13",
4
4
  "description": "Den: a local multi-repo dev orchestrator, HTTPS edge and E2E harness",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: be-dev
3
+ role: sde
3
4
  description: Implements back-end stories, test first, keeping contracts stable.
4
5
  context:
5
6
  [
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: devops
3
+ role: sde
3
4
  description: Builds and maintains pipelines, environments and service configuration.
4
5
  context: [the story, the workspace's service manifests, CI configuration]
5
6
  ---
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: fe-dev
3
+ role: sde
3
4
  description: Implements front-end stories, test first, to the design system.
4
5
  context:
5
6
  [
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: orchestrator
3
+ role: orchestrator
3
4
  description: Takes the next ready story, picks the persona for it and starts that agent.
4
5
  standing: true
5
6
  context: [ready work items, running agents in the workspace]
@@ -9,7 +10,8 @@ You are the orchestrator. Keep work flowing without overloading the machine
9
10
  or the people reviewing it.
10
11
 
11
12
  - Take the next ready story, in priority order. Pick the persona that fits it:
12
- fe-dev, be-dev or devops for implementation, ux-designer for design work.
13
+ fe-dev, be-dev or devops for implementation, ux-designer for design work,
14
+ tester to run the suites.
13
15
  - Start one agent per story, in its own worktree, and link it to the story.
14
16
  - Respect the workspace's concurrency cap; wait rather than queueing more.
15
17
  - When a pull request is opened, ask the reviewer for a review. Never approve
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: planner
3
+ role: planner
3
4
  description: Writes stories and their acceptance criteria for the workspace.
4
5
  standing: true
5
6
  context: [the workspace's specs and docs, open work items, the design system]
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: reviewer
3
+ role: reviewer
3
4
  description: Reviews a pull request adversarially. Never approves or merges.
4
5
  context: [the pull request and its story, the repo's conventions and tests]
5
6
  ---
@@ -0,0 +1,16 @@
1
+ ---
2
+ name: tester
3
+ role: tester
4
+ description: Runs the test suites and reports failures with repro steps.
5
+ context: [the story and its acceptance criteria, the repo's test commands]
6
+ ---
7
+
8
+ You are the tester. Find out what is broken before anyone ships it.
9
+
10
+ - Run the workspace's unit, integration and end-to-end suites, as the repo
11
+ documents them.
12
+ - For each failure, write the exact repro: the smallest failing command, what
13
+ it printed, and what you expected.
14
+ - Separate real failures from flaky ones; run a suspected flake again before
15
+ reporting it.
16
+ - Don't fix code. Report what you found to the person or agent who asked.
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: ux-designer
3
+ role: designer
3
4
  description: Designs flows and screens with the design system, on request.
4
5
  context: [the story, the design system, existing screens of the product]
5
6
  ---
@@ -61,12 +61,26 @@ import {
61
61
  type LaunchOutput,
62
62
  type Runtime,
63
63
  type RuntimeDetect,
64
+ type RuntimeModel,
65
+ type RuntimePermissions,
64
66
  } from "./runtime.js";
67
+ import { PERMISSION_INTENTS, type PermissionIntent } from "./permissions.js";
65
68
  import { builtinEntry } from "./catalog.js";
66
69
  import { catalogFind } from "./catalog-runtime.js";
67
70
  import { locateRuntime, type RuntimeFind } from "./runtime-search.js";
68
71
  import { claudeProjectsDir } from "../usage/paths.js";
69
- import { transcriptTitle } from "./transcript-follow.js";
72
+ import { cleanName, transcriptTitle } from "./transcript-follow.js";
73
+
74
+ /**
75
+ * `/rename "x"` keeps the quotes in customTitle: one matching pair around
76
+ * the whole name goes, for display only (0.4.13). Nothing inside: null.
77
+ */
78
+ export function unquoteTitle(name: string | null): string | null {
79
+ if (!name || name.length < 2) return name;
80
+ const q = name[0];
81
+ if ((q !== '"' && q !== "'") || name[name.length - 1] !== q) return name;
82
+ return cleanName(name.slice(1, -1));
83
+ }
70
84
 
71
85
  export const DEN_CLAUDE_ID = "den-claude";
72
86
  const BIN = "claude";
@@ -90,12 +104,91 @@ export const PERMISSION_MODES = [
90
104
  ] as const;
91
105
  export const DEFAULT_PERMISSION_MODE = "default";
92
106
 
93
- /** The persona's permission mode, checked; a bad one names the key. */
107
+ /**
108
+ * Den's permission intents (AG-13) as Claude Code's modes:
109
+ * https://code.claude.com/docs/en/iam (permission modes): `default` asks
110
+ * on first use of each tool, `acceptEdits` accepts file edits for the
111
+ * session, `bypassPermissions` skips every prompt.
112
+ */
113
+ export const INTENT_MODE: Record<PermissionIntent, string> = {
114
+ ask: "default",
115
+ edits: "acceptEdits",
116
+ all: "bypassPermissions",
117
+ };
118
+
119
+ /**
120
+ * One plain command prefix as Claude Code allow rules (the same docs,
121
+ * Bash rules): the command alone, and the command with arguments (`*`
122
+ * matches any run of characters). Deny rules (Den's state dir) still win:
123
+ * Claude Code checks deny before allow.
124
+ */
125
+ export const COMMAND_RULES = ["Bash({c})", "Bash({c} *)"];
126
+
127
+ /** How den-claude expresses permission intents (AG-13.2). */
128
+ export const DEN_CLAUDE_PERMISSIONS: RuntimePermissions = {
129
+ modes: {
130
+ ask: "--permission-mode default: asks before edits and commands",
131
+ edits:
132
+ "--permission-mode acceptEdits: file edits run without asking; commands ask",
133
+ all: "--permission-mode bypassPermissions: nothing asks",
134
+ },
135
+ commandRules: COMMAND_RULES,
136
+ };
137
+
138
+ /** A command prefix's allow rules (permissions.ts checked it). */
139
+ export function allowRules(commands: readonly string[]): string[] {
140
+ return commands.flatMap((c) =>
141
+ COMMAND_RULES.map((r) => r.split("{c}").join(c)),
142
+ );
143
+ }
144
+
145
+ /**
146
+ * Claude Code's models (AG-12.3): the aliases `claude --model` takes,
147
+ * each "latest" (the alias tracks its newest model) or pinned to one full
148
+ * model id. Claude Code has no command that lists models, so the list
149
+ * ships with each Den release (https://code.claude.com/docs/en/model-config:
150
+ * aliases opus, sonnet, haiku, or a full model name). Newest first.
151
+ */
152
+ export const DEN_CLAUDE_MODELS: readonly RuntimeModel[] = [
153
+ {
154
+ id: "opus",
155
+ label: "Opus",
156
+ versions: [
157
+ "claude-opus-5-5",
158
+ "claude-opus-5",
159
+ "claude-opus-4-8",
160
+ "claude-opus-4-7",
161
+ "claude-opus-4-6",
162
+ ],
163
+ latest: "claude-opus-5-5",
164
+ },
165
+ {
166
+ id: "sonnet",
167
+ label: "Sonnet",
168
+ versions: ["claude-sonnet-5-5", "claude-sonnet-5", "claude-sonnet-4-6"],
169
+ latest: "claude-sonnet-5-5",
170
+ },
171
+ {
172
+ id: "haiku",
173
+ label: "Haiku",
174
+ versions: ["claude-haiku-5-5", "claude-haiku-4-5"],
175
+ latest: "claude-haiku-5-5",
176
+ },
177
+ ];
178
+
179
+ /**
180
+ * The mode to launch with: the agent's intent (Start › Advanced), else the
181
+ * persona's `permission_mode`: one of Den's intents, or one of Claude
182
+ * Code's own modes (as before AG-13). A bad one names the key.
183
+ */
94
184
  function permissionMode(input: LaunchInput): string {
185
+ if (input.permission) return INTENT_MODE[input.permission];
95
186
  const m = input.persona.permissionMode ?? DEFAULT_PERMISSION_MODE;
187
+ if ((PERMISSION_INTENTS as readonly string[]).includes(m))
188
+ return INTENT_MODE[m as PermissionIntent];
96
189
  if (!(PERMISSION_MODES as readonly string[]).includes(m))
97
190
  throw new LaunchError(
98
- `${input.persona.source}: permission_mode "${m.replace(/[^\x20-\x7e]/g, "?").slice(0, 40)}" must be one of ${PERMISSION_MODES.join(", ")}`,
191
+ `${input.persona.source}: permission_mode "${m.replace(/[^\x20-\x7e]/g, "?").slice(0, 40)}" must be one of ${[...PERMISSION_INTENTS, ...PERMISSION_MODES].join(", ")}`,
99
192
  );
100
193
  return m;
101
194
  }
@@ -244,7 +337,9 @@ function common(input: LaunchInput): {
244
337
  }),
245
338
  )
246
339
  : null;
247
- const settings = input.stateDir || hooks ? settingsPath(input) : null;
340
+ const allow = allowRules(input.allow ?? []);
341
+ const settings =
342
+ input.stateDir || hooks || allow.length ? settingsPath(input) : null;
248
343
  return {
249
344
  argv: [
250
345
  "--mcp-config",
@@ -268,13 +363,18 @@ function common(input: LaunchInput): {
268
363
  ? {
269
364
  [settings]: `${JSON.stringify(
270
365
  {
271
- ...(input.stateDir
366
+ ...(input.stateDir || allow.length
272
367
  ? {
273
368
  permissions: {
274
- deny: stateDirDenyRules(
275
- input.stateDir,
276
- input.env?.HOME,
277
- ),
369
+ ...(allow.length ? { allow } : {}),
370
+ ...(input.stateDir
371
+ ? {
372
+ deny: stateDirDenyRules(
373
+ input.stateDir,
374
+ input.env?.HOME,
375
+ ),
376
+ }
377
+ : {}),
278
378
  },
279
379
  }
280
380
  : {}),
@@ -289,7 +389,9 @@ function common(input: LaunchInput): {
289
389
  warnings:
290
390
  mode === "bypassPermissions"
291
391
  ? [
292
- `⚠ persona ${input.persona.id} sets permission_mode: bypassPermissions: Claude Code will not ask before running any tool`,
392
+ input.permission === "all"
393
+ ? `⚠ agent ${input.agentId} starts with auto-approve all (bypassPermissions): Claude Code will not ask before running any tool`
394
+ : `⚠ persona ${input.persona.id} sets permission_mode: bypassPermissions: Claude Code will not ask before running any tool`,
293
395
  ]
294
396
  : [],
295
397
  };
@@ -347,6 +449,9 @@ export const denClaude: Runtime = {
347
449
  denTools: true,
348
450
  firstPrompt: true,
349
451
  find: DEN_CLAUDE_FIND,
452
+ models: () =>
453
+ DEN_CLAUDE_MODELS.map((m) => ({ ...m, versions: [...m.versions] })),
454
+ permissions: DEN_CLAUDE_PERMISSIONS,
350
455
  detect,
351
456
  launch(input): LaunchOutput {
352
457
  const bin = binFor(input);
@@ -385,12 +490,13 @@ export const denClaude: Runtime = {
385
490
  },
386
491
  // AG-4b, unofficial (catalog sessionName): `/rename` appends a
387
492
  // {"type":"custom-title","customTitle":…} record to the transcript; the
388
- // latest one, read on from the last ask (never the whole file). Null on
389
- // any failure: it must never break a pane.
493
+ // latest one, read on from the last ask (never the whole file), less one
494
+ // pair of surrounding quotes. Null on any failure: it must never break a
495
+ // pane.
390
496
  sessionName(rec, env = process.env) {
391
497
  try {
392
498
  const file = transcriptFile(rec.resumeToken, env);
393
- return file ? transcriptTitle(file) : null;
499
+ return file ? unquoteTitle(transcriptTitle(file)) : null;
394
500
  } catch {
395
501
  return null;
396
502
  }