@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.
- package/CHANGELOG.md +102 -0
- package/README.md +7 -7
- package/docs/distributions.md +146 -10
- package/package.json +1 -1
- package/src/shared/agents/default-pack/be-dev.md +1 -0
- package/src/shared/agents/default-pack/devops.md +1 -0
- package/src/shared/agents/default-pack/fe-dev.md +1 -0
- package/src/shared/agents/default-pack/orchestrator.md +3 -1
- package/src/shared/agents/default-pack/planner.md +1 -0
- package/src/shared/agents/default-pack/reviewer.md +1 -0
- package/src/shared/agents/default-pack/tester.md +16 -0
- package/src/shared/agents/default-pack/ux-designer.md +1 -0
- package/src/shared/agents/den-claude.ts +119 -13
- package/src/shared/agents/permissions.ts +108 -0
- package/src/shared/agents/persona-store.ts +286 -0
- package/src/shared/agents/personas.ts +116 -12
- package/src/shared/agents/records.ts +28 -0
- package/src/shared/agents/roles.ts +288 -0
- package/src/shared/agents/runtime.ts +41 -0
- package/src/shared/den-config.ts +20 -1
- package/src/ui/cli/agent-ops.ts +535 -17
- package/src/ui/cli/agents-command.ts +12 -2
- package/src/ui/cli/env-probe.ts +4 -1
- package/src/ui/mcp/tools.ts +1 -1
- package/src/ui/server/agent-routes.ts +260 -21
- package/src/ui/server/public/agent-start.js +3 -2
- package/src/ui/server/public/agents-page.js +499 -48
- package/src/ui/server/public/agents.html +58 -15
- package/src/ui/server/public/i18n/es.js +120 -0
- package/src/ui/server/public/i18n/ja.js +120 -0
- package/src/ui/server/public/persona-ui.css +539 -0
- package/src/ui/server/public/persona-ui.js +796 -0
- package/src/ui/server/public/settings-page.js +4 -9
- package/src/ui/server/public/settings-registry.js +22 -0
- package/src/ui/server/public/settings.html +1 -0
- package/src/ui/server/public/shell.js +117 -0
- package/src/ui/server/public/topbar.js +2 -2
- package/src/ui/server/route-classes.ts +18 -0
- package/src/ui/server/session-routes.ts +21 -5
- 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.
|
|
14
|
-
<img src="docs/images/0.4.
|
|
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.
|
|
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.
|
|
72
|
-
<img src="docs/images/0.4.
|
|
73
|
-
<img src="docs/images/0.4.
|
|
74
|
-
<img src="docs/images/0.4.
|
|
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
|
package/docs/distributions.md
CHANGED
|
@@ -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
|
|
2058
|
-
|
|
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?,
|
|
2108
|
-
|
|
2109
|
-
|
|
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 `
|
|
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
|
-
|
|
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:
|
|
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
|
|
2406
|
-
|
|
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,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
|
|
@@ -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.
|
|
@@ -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
|
-
/**
|
|
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
|
|
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
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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
|
-
|
|
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)
|
|
389
|
-
// any failure: it must never break a
|
|
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
|
}
|