@monster-orange/den 0.4.5 → 0.4.7
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 +140 -0
- package/README.md +21 -7
- package/docs/distributions.md +221 -0
- package/package.json +4 -3
- package/skills/install-den/SKILL.md +114 -0
- package/src/shared/agent-setup.ts +9 -0
- package/src/shared/agents/den-claude.ts +19 -5
- package/src/shared/agents/guardrails/hooks.json +27 -0
- package/src/shared/agents/guardrails/instructions.md +39 -0
- package/src/shared/agents/guardrails/pretooluse.mjs +430 -0
- package/src/shared/agents/guardrails/stop-hook.ts +66 -0
- package/src/shared/agents/guardrails.ts +114 -0
- package/src/shared/agents/handback.ts +241 -0
- package/src/shared/agents/personas.ts +55 -6
- package/src/shared/constants.ts +7 -0
- package/src/shared/den-config.ts +67 -6
- package/src/shared/dev-metrics/emit.ts +59 -5
- package/src/shared/dev-metrics/polling.ts +135 -0
- package/src/shared/engine.ts +14 -2
- package/src/shared/port-pick.ts +60 -0
- package/src/ui/cli/agent-ops.ts +7 -3
- package/src/ui/cli/agents-command.ts +51 -1
- package/src/ui/cli/create-main.ts +1 -1
- package/src/ui/cli/create.ts +263 -30
- package/src/ui/cli/doctor-command.ts +87 -4
- package/src/ui/cli/doctor-onboard.ts +291 -0
- package/src/ui/cli/flag-parser.ts +5 -0
- package/src/ui/cli/index.ts +352 -5
- package/src/ui/cli/json-out.ts +49 -0
- package/src/ui/cli/mcp-connect.ts +179 -15
- package/src/ui/cli/repo-command.ts +490 -0
- package/src/ui/cli/repo-detect.ts +192 -0
- package/src/ui/cli/web-command.ts +3 -0
- package/src/ui/mcp/connect.ts +134 -0
- package/src/ui/server/public/settings-registry.js +3 -3
- package/src/ui/server/request-guard.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,146 @@ 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.7] - 2026-11-07
|
|
8
|
+
|
|
9
|
+
Agents read slices, wait in the background and hand back plainly (den#5,
|
|
10
|
+
the default-pack guardrails issue). Every persona in Den's default pack
|
|
11
|
+
gets Den's shared instructions and, on Claude Code, Den's hooks, with no
|
|
12
|
+
setup. `doctor` warns about polling loops it sees in the local dev metrics.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **The Stop hook reads every last message.** With a `## Hand-back`
|
|
17
|
+
heading it checks the whole hand-back, as before. Any other last message
|
|
18
|
+
gets the plain-writing check: every ID explained. It still never blocks
|
|
19
|
+
twice in a row.
|
|
20
|
+
- **`URL: none` in a hand-back.** Work with no page (a CLI change, a
|
|
21
|
+
refused request) says `URL: none`, with a reason if it likes. An empty
|
|
22
|
+
or missing URL still fails.
|
|
23
|
+
|
|
24
|
+
- **`down --json` and `web down --json`.** The clean-up steps answer in
|
|
25
|
+
JSON like every other onboarding step: `{ ok, workspace, services }` and
|
|
26
|
+
`{ ok, url, port, running, stopped }`. Both are safe to re-run.
|
|
27
|
+
|
|
28
|
+
- **Hooks for default-pack agents.** A Claude Code agent from Den's default
|
|
29
|
+
pack runs with Den's own hooks, in its own `--settings` file (never your
|
|
30
|
+
`~/.claude`). A `PreToolUse` hook on Bash and Read blocks reading all of
|
|
31
|
+
a log or a large file into the conversation (`cat`, `less`, `tail` from
|
|
32
|
+
a line such as `tail +1`, a Read with no limit; a plain `tail` passes) and says to read a slice or use a
|
|
33
|
+
subagent. It blocks a foreground `sleep`, alone or in an `until`/`while`/
|
|
34
|
+
`for` loop, and says to run in the background or use a wait primitive.
|
|
35
|
+
It blocks nothing else.
|
|
36
|
+
- **Shared persona instructions.** Every default-pack persona's role prompt
|
|
37
|
+
ends with Den's `guardrails/instructions.md`: write plainly, in short
|
|
38
|
+
sentences; say what every issue or ID is; ask each decision as a numbered
|
|
39
|
+
yes/no question; no caveat sections; and the hand-back template (URL,
|
|
40
|
+
machine, branch, what changed, what to click, decisions).
|
|
41
|
+
- **The hand-back check.** `<cli> agents handback-check <file|->` rejects a
|
|
42
|
+
hand-back with a missing part, an unexplained ID, or a decision that is
|
|
43
|
+
not a numbered yes/no question, and says what to fix. A `Stop` hook runs
|
|
44
|
+
the same check on a turn that ends with a `Hand-back` heading, and sends
|
|
45
|
+
Claude back to fix it once.
|
|
46
|
+
- **doctor warns about polling loops.** It reads the end of the local
|
|
47
|
+
`.dev-metrics/events.jsonl` (at most 8 MB) and warns when one command,
|
|
48
|
+
run from one place, ran more than 600 times in an hour:
|
|
49
|
+
`looks like a polling loop: status in services/api, 1200 calls in an hour (…)`.
|
|
50
|
+
It is local and sends nothing.
|
|
51
|
+
- **Each dev-metrics line records `cwd`**, relative to the distribution root
|
|
52
|
+
(`.` at the root, `outside` elsewhere), never an absolute or home path.
|
|
53
|
+
Like every field, it is sent only when you opt in.
|
|
54
|
+
|
|
55
|
+
### Metrics (before → after)
|
|
56
|
+
|
|
57
|
+
- Polling loops a week: 18,600 → under 100 expected, since the hook blocks
|
|
58
|
+
them. The "after" is counted on the candidate's first week.
|
|
59
|
+
- Incomplete hand-backs: not measured → 0 for default-pack agents (the Stop
|
|
60
|
+
hook sends them back). The candidate's first run sets the baseline.
|
|
61
|
+
- Hand-backs with an unexplained ID: no baseline → 0 for default-pack
|
|
62
|
+
agents. The candidate's first run sets the baseline.
|
|
63
|
+
- doctor on a replay of a three-day polling burst: no warning → it warns.
|
|
64
|
+
|
|
65
|
+
### Migrations
|
|
66
|
+
|
|
67
|
+
- None needed. To turn the guardrails off for Den's pack, set
|
|
68
|
+
`agents.default_pack: { hooks: false, instructions: false }`. To turn
|
|
69
|
+
them on for your own pack, write its `persona_packs` entry as
|
|
70
|
+
`{ path: <dir>, hooks: true, instructions: true }`. A persona's
|
|
71
|
+
`den_hooks` / `den_instructions` front matter wins over its pack's.
|
|
72
|
+
|
|
73
|
+
## [0.4.6] - 2026-11-07
|
|
74
|
+
|
|
75
|
+
Den in five minutes, set up by your own agent (ON-2, with ON-4's dry run).
|
|
76
|
+
A developer pastes one line into their agent, and every command the agent
|
|
77
|
+
runs answers in JSON, never prompts, and is safe to re-run.
|
|
78
|
+
|
|
79
|
+
### Added
|
|
80
|
+
|
|
81
|
+
- **Set up by your agent.** `den --help` and the README print the line to
|
|
82
|
+
paste: "Set up Den for me by following
|
|
83
|
+
https://raw.githubusercontent.com/Monster-Orange/den/main/skills/install-den/SKILL.md". The install-den skill it points
|
|
84
|
+
at ships in the package as `skills/install-den/SKILL.md`: plain Markdown
|
|
85
|
+
with Claude skill front matter, at most three questions, no secrets in
|
|
86
|
+
chat, no edits to the person's repos, nothing sent off the machine.
|
|
87
|
+
- **`<cli> repo add <path|git-url>`** adds a repo with no prompts and never
|
|
88
|
+
writes into it. A git URL is cloned where Den keeps managed clones. A repo
|
|
89
|
+
with no manifest (or `--sidecar`) gets a side-car manifest in the
|
|
90
|
+
distribution's `manifests/`, detected from its Makefile `up` target, its
|
|
91
|
+
compose file or its `package.json` `dev`/`start` scripts (and `test`), and
|
|
92
|
+
validated before it is written. When detection can't decide, nothing is
|
|
93
|
+
written and the answer asks, with the choices; a re-run passes
|
|
94
|
+
`--run "<cmd>"`. `--health <url>` adds a health check. The workspace is
|
|
95
|
+
`-w`, else the focused one, else a new `default`. The same repo again
|
|
96
|
+
answers `existed: true`.
|
|
97
|
+
- **`--json` for setup.** `create --yes --json` (a directory that already
|
|
98
|
+
holds the distribution is `existed: true`), `up --json` (each service's
|
|
99
|
+
result and health), `doctor --json` and `mcp connect --json`. Stdout is
|
|
100
|
+
one JSON object, the log goes to stderr, and a failure carries a `fix`
|
|
101
|
+
the agent can run or an `ask` for the person.
|
|
102
|
+
- **`doctor --json`'s onboarding checks:** Node 22.13 or later, git, Docker
|
|
103
|
+
(only when a registered repo needs it), the web server and its URL, and
|
|
104
|
+
Den's MCP through it, each `ok`, `warn` or `fail`. The human `doctor` is
|
|
105
|
+
unchanged.
|
|
106
|
+
- **`mcp connect --runtime claude|codex|gemini|grok`** names the agent by
|
|
107
|
+
its runtime. Grok CLI is a new client: Den writes its own
|
|
108
|
+
`[mcp_servers.<cli>]` table in `~/.grok/config.toml` (or the project's
|
|
109
|
+
`.grok/config.toml`), as xAI documents. A config file that already has the
|
|
110
|
+
entry is no longer rewritten.
|
|
111
|
+
- **`create` picks a free web port.** When 7777 is in use on this machine,
|
|
112
|
+
`create` picks the next free port above it (never 7778, 7791 or 7799),
|
|
113
|
+
writes it to the new distribution's `config/local/user.yml` and says so,
|
|
114
|
+
in JSON as `port` and `portPicked`. `--port <n>` sets it outright. The
|
|
115
|
+
check connects and binds only; it sends nothing to the port's holder.
|
|
116
|
+
`DEN_WEB_PORT_START` moves the starting port.
|
|
117
|
+
- **`web up --json`** answers `{ ok, url, port }` once the server answers,
|
|
118
|
+
and `doctor --json`'s web check carries the `port`.
|
|
119
|
+
|
|
120
|
+
### Changed
|
|
121
|
+
|
|
122
|
+
- `create` into a directory that already holds the same distribution says
|
|
123
|
+
so and exits 0, instead of refusing a non-empty directory.
|
|
124
|
+
|
|
125
|
+
### Fixed
|
|
126
|
+
|
|
127
|
+
- **An opted-in CLI sends its metrics.** The CLI records each run as it
|
|
128
|
+
exits, and a send started then never ran. With `metrics.share: true`, the
|
|
129
|
+
CLI now hands the event to a small detached sender that gives up after 2
|
|
130
|
+
seconds. The web server sends as before. Not opted in, nothing is sent.
|
|
131
|
+
- **Linked devices read plainly.** A device approved with a code says
|
|
132
|
+
"approved with a code", not "signed in with approved with a code".
|
|
133
|
+
- **An agent's refusal names the admin password.** It said "the lock",
|
|
134
|
+
which 0.4.4 removed.
|
|
135
|
+
|
|
136
|
+
### Metric
|
|
137
|
+
|
|
138
|
+
- Time from the paste to "service running, MCP connected" (R11): not
|
|
139
|
+
measured before; ON-4's dry run (create, repo add, up, web up, doctor,
|
|
140
|
+
mcp connect, each run twice) now records it, about 16 s locally. Target:
|
|
141
|
+
5 minutes or less.
|
|
142
|
+
|
|
143
|
+
### Migrations
|
|
144
|
+
|
|
145
|
+
- None.
|
|
146
|
+
|
|
7
147
|
## [0.4.5] - 2026-11-07
|
|
8
148
|
|
|
9
149
|
Sign in with GitHub as the owner, from your other devices; approve a
|
package/README.md
CHANGED
|
@@ -10,10 +10,24 @@
|
|
|
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.7/hero-dark.png">
|
|
14
|
+
<img src="docs/images/0.4.7/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
|
+
## Set up by your agent
|
|
18
|
+
|
|
19
|
+
Paste this line into your coding agent (Claude Code, Codex, Gemini, Grok, …):
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
Set up Den for me by following https://raw.githubusercontent.com/Monster-Orange/den/main/skills/install-den/SKILL.md
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The agent follows the [install-den skill](skills/install-den/SKILL.md). It asks you at most three questions,
|
|
26
|
+
creates your Den, adds one of your repos without changing it, starts it, checks it, and connects itself to Den's
|
|
27
|
+
MCP server. Then it tells you what's running and the URL to open. It never asks for a password in chat, never
|
|
28
|
+
edits your repos and sends nothing off your machine. Every step it runs answers in JSON and is safe to re-run:
|
|
29
|
+
see [Set up by your agent](docs/distributions.md#set-up-by-your-agent).
|
|
30
|
+
|
|
17
31
|
## What Den is
|
|
18
32
|
|
|
19
33
|
Den is an opinionated local development framework for solo developers and small teams. You point it at the
|
|
@@ -24,7 +38,7 @@ It doesn't replace your tools. Your repos, Docker, your test runners, your secre
|
|
|
24
38
|
yours. Den connects them and turns on good practice from the start.
|
|
25
39
|
|
|
26
40
|
<p align="center">
|
|
27
|
-
<img src="docs/images/0.4.
|
|
41
|
+
<img src="docs/images/0.4.7/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">
|
|
28
42
|
</p>
|
|
29
43
|
|
|
30
44
|
## How it helps
|
|
@@ -54,10 +68,10 @@ yours. Den connects them and turns on good practice from the start.
|
|
|
54
68
|
lives in your own repo.
|
|
55
69
|
|
|
56
70
|
<p align="center">
|
|
57
|
-
<img src="docs/images/0.4.
|
|
58
|
-
<img src="docs/images/0.4.
|
|
59
|
-
<img src="docs/images/0.4.
|
|
60
|
-
<img src="docs/images/0.4.
|
|
71
|
+
<img src="docs/images/0.4.7/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.7/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.7/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.7/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">
|
|
61
75
|
</p>
|
|
62
76
|
|
|
63
77
|
## Get started
|
package/docs/distributions.md
CHANGED
|
@@ -32,8 +32,22 @@ From a Den checkout, `./bin/den create acme ../acme` does the same.
|
|
|
32
32
|
| `--title <title>` | `<Name>` Title Case | Brand name shown to people |
|
|
33
33
|
| `--cli <cli>` | `<name>` | The command people type |
|
|
34
34
|
| `--env-prefix <PREFIX>` | `<NAME>` | Env-var prefix (`ACME` → `ACME_HOME`) |
|
|
35
|
+
| `--yes`, `-y` | | Never prompt (create never does) |
|
|
36
|
+
| `--json` | | One JSON object on stdout |
|
|
37
|
+
| `--port <n>` | `7777`, else free | The web UI's port |
|
|
35
38
|
|
|
36
39
|
`create` refuses a directory that is not empty and never overwrites a file.
|
|
40
|
+
A directory that already holds this distribution (its `den.yml` names the
|
|
41
|
+
same product and command) is not an error: `create` says so and writes
|
|
42
|
+
nothing, so it is safe to re-run.
|
|
43
|
+
|
|
44
|
+
The web UI's port is 7777 unless that port is in use on this machine (another
|
|
45
|
+
Den already running, say). Then `create` picks the next free port above it,
|
|
46
|
+
never a port Den itself reserves (7778, 7791, 7799), writes it to the new
|
|
47
|
+
distribution's `config/local/user.yml` as `web: { port: N }`, and says so.
|
|
48
|
+
`--port <n>` sets it outright. The check only connects and binds; it sends
|
|
49
|
+
nothing to whatever holds the port. `DEN_WEB_PORT_START` moves the starting
|
|
50
|
+
port (tests use it so none touches 7777).
|
|
37
51
|
[`examples/distribution/`](../examples/distribution) is exactly the output of
|
|
38
52
|
`den create acme`; a unit test regenerates it, so the two never drift
|
|
39
53
|
(`npm run example:regen` after changing the scaffolder).
|
|
@@ -48,6 +62,91 @@ npm install
|
|
|
48
62
|
make help
|
|
49
63
|
```
|
|
50
64
|
|
|
65
|
+
## Set up by your agent
|
|
66
|
+
|
|
67
|
+
A developer pastes one line into their agent:
|
|
68
|
+
|
|
69
|
+
```text
|
|
70
|
+
Set up Den for me by following https://raw.githubusercontent.com/Monster-Orange/den/main/skills/install-den/SKILL.md
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
That file is the install-den skill, also in the package at
|
|
74
|
+
`skills/install-den/SKILL.md`: plain Markdown any agent can follow, with
|
|
75
|
+
Claude skill front matter. `den --help` prints the line. The skill runs the
|
|
76
|
+
commands below. Each one takes `--json`, never prompts, and is safe to
|
|
77
|
+
re-run. With `--json`, stdout carries exactly one JSON object and the
|
|
78
|
+
command's log goes to stderr. When `ok` is false the object says why
|
|
79
|
+
(`error`) and carries either a command the agent can run (`fix`) or a
|
|
80
|
+
question for the person (`ask`, sometimes with `choices`).
|
|
81
|
+
|
|
82
|
+
| Step | Answers |
|
|
83
|
+
| ---------------------------------------------------------- | ---------------------------------------------------------------------------- |
|
|
84
|
+
| `npx @monster-orange/den create <name> [dir] --yes --json` | `{ ok, dir, cli, existed, files, port, portPicked?, next }` |
|
|
85
|
+
| `<cli> repo add <path\|git-url> --json` | `{ ok, existed, repo, workspace, mode, path, manifest, services, next }` |
|
|
86
|
+
| `<cli> up <repo> --json` | `{ ok, workspace, services: [{ address, result, health, detail? }], next }` |
|
|
87
|
+
| `<cli> web up --no-open --json` | `{ ok, url, port, version? }` |
|
|
88
|
+
| `<cli> doctor --json` | `{ ok, checks: [{ id, status, detail, url?, port?, fix?, ask? }] }` |
|
|
89
|
+
| `<cli> mcp connect --runtime <runtime> --json` | `{ ok, server, clients: [{ id, ok, said, file?, changed? }], restart, web }` |
|
|
90
|
+
| `<cli> down <repo> --json` | `{ ok, workspace, services, all? }` |
|
|
91
|
+
| `<cli> web down --json` | `{ ok, url, port, running, stopped }` |
|
|
92
|
+
|
|
93
|
+
### repo add
|
|
94
|
+
|
|
95
|
+
`<cli> repo add <path|git-url> [--sidecar] [-w <workspace>] [--name <repo>]
|
|
96
|
+
[--run "<cmd>"] [--down "<cmd>"] [--health <url>] [--branch <branch>]
|
|
97
|
+
[--json]` adds a repo to a workspace without writing into the repo.
|
|
98
|
+
|
|
99
|
+
- A path is the person's own checkout, added dev-managed at that path. A git
|
|
100
|
+
URL is cloned where Den keeps its managed clones
|
|
101
|
+
(`.<name>-services/<repo>/<branch>`, the remote's default branch unless
|
|
102
|
+
`--branch` says otherwise) and added managed. A clone that is already
|
|
103
|
+
there is used as it is.
|
|
104
|
+
- A repo with its own manifest is used as it is. One without, or with
|
|
105
|
+
`--sidecar`, gets a side-car: a held manifest at
|
|
106
|
+
`manifests/<repo>.<name>.yml` ([below](#overriding-a-manifest)), keyed by
|
|
107
|
+
the repo's `origin` name or folder name. It is checked by the manifest
|
|
108
|
+
validator before it is written, and an existing one is never overwritten.
|
|
109
|
+
`--sidecar` on a repo with its own manifest is an error: Den always uses
|
|
110
|
+
the repo's own file (change it with an override instead).
|
|
111
|
+
- The side-car's commands are detected from the repo's files, in this order:
|
|
112
|
+
a Makefile with an `up` target (`make up`, plus `down`, `setup` and
|
|
113
|
+
`restart` when present); otherwise a compose file
|
|
114
|
+
(`docker compose -f <file> up -d` and `down`) or `package.json`'s `dev`,
|
|
115
|
+
else `start`, script. One candidate decides. Two, or none, is a question:
|
|
116
|
+
the answer has `ask` and `choices`, nothing is written, and a re-run names
|
|
117
|
+
the command with `--run "<cmd>"`. A Makefile `test` target or a
|
|
118
|
+
`package.json` `test` script becomes the repo's `tests.unit`.
|
|
119
|
+
`--health <url>` adds an HTTP health check.
|
|
120
|
+
- The workspace is `-w <workspace>` (made and activated if it doesn't exist),
|
|
121
|
+
else the focused one, else a new `default` workspace.
|
|
122
|
+
- The same repo again answers `ok` with `existed: true`. A different repo
|
|
123
|
+
under the same name is an error that offers `relocate` or `--name`.
|
|
124
|
+
|
|
125
|
+
### up, doctor and mcp connect
|
|
126
|
+
|
|
127
|
+
- `down --json` and `web down --json` are the clean-up steps. Each is safe
|
|
128
|
+
to re-run: `web down` answers `stopped: false` when the server wasn't
|
|
129
|
+
running, and `ok` is true once nothing answers on its port.
|
|
130
|
+
|
|
131
|
+
- `up --json` runs `up` as usual, then reports each service it touched:
|
|
132
|
+
`result` is `started`, `already-running`, `failed` or `skipped`, and
|
|
133
|
+
`health` is the service's health after it started (a service with a
|
|
134
|
+
health check is waited on, up to 30 seconds). A healthy service is
|
|
135
|
+
`already-running` on a re-run.
|
|
136
|
+
- `doctor --json` lists the onboarding checks first: `node` (22.13 or
|
|
137
|
+
later), `git`, `docker` (only when a registered repo has a compose file or
|
|
138
|
+
block, or a docker command), `web` (the web server answers at `url`) and
|
|
139
|
+
`mcp` (`<cli> mcp --call workspaces` gets an answer through it). Then one
|
|
140
|
+
`strategy:<kind>` check per row of the [human doctor](#doctor). `ok` is
|
|
141
|
+
false, and the exit code 1, when any check is `fail`.
|
|
142
|
+
- `mcp connect --runtime <runtime>` names the agent by its runtime: `claude`
|
|
143
|
+
(the `claude-code` client), `codex`, `gemini` or `grok`. Grok CLI keeps its
|
|
144
|
+
MCP servers in `~/.grok/config.toml` (or `.grok/config.toml` with
|
|
145
|
+
`--project`), one `[mcp_servers.<name>]` table each
|
|
146
|
+
([xAI's docs](https://docs.x.ai/build/features/mcp-servers)); Den writes
|
|
147
|
+
only its own table. A config file that already has the entry is left as
|
|
148
|
+
it is (`changed: false`).
|
|
149
|
+
|
|
51
150
|
## Layout
|
|
52
151
|
|
|
53
152
|
| Path | What |
|
|
@@ -587,6 +686,22 @@ Acme doctor: strategies
|
|
|
587
686
|
`session-backend` is never a doctor failure: nothing needs a session until
|
|
588
687
|
one is started (see Sessions).
|
|
589
688
|
|
|
689
|
+
`<cli> doctor --json` adds the onboarding checks (Node, git, Docker, the web
|
|
690
|
+
server, Den's MCP) and answers with one JSON object: see
|
|
691
|
+
[Set up by your agent](#up-doctor-and-mcp-connect).
|
|
692
|
+
|
|
693
|
+
doctor also reads the end of the local `.dev-metrics/events.jsonl` (at
|
|
694
|
+
most 8 MB of it, however big the file) and warns when one command, run
|
|
695
|
+
from one place, ran more than 600 times in an hour. That is almost always
|
|
696
|
+
an agent polling. It is a warning, never a failure, and nothing is sent
|
|
697
|
+
(see [Dev metrics](#dev-metrics-local-unless-you-opt-in)):
|
|
698
|
+
|
|
699
|
+
```
|
|
700
|
+
Acme doctor: dev-metrics (local, never sent)
|
|
701
|
+
|
|
702
|
+
⚠ looks like a polling loop: status in services/api, 1200 calls in an hour (2026-11-04T09:00:00Z–2026-11-04T09:59:57Z)
|
|
703
|
+
```
|
|
704
|
+
|
|
590
705
|
## den.yml: the product profile
|
|
591
706
|
|
|
592
707
|
Every runtime name derives from `product.name`. Set only what you want to
|
|
@@ -1915,6 +2030,7 @@ server scoped to its workspace.
|
|
|
1915
2030
|
<cli> agents resume <id> [-w <ws>]
|
|
1916
2031
|
<cli> agents personas [--json]
|
|
1917
2032
|
<cli> agents doctor [-w <ws>] [--json] # can each runtime run here, and why not
|
|
2033
|
+
<cli> agents handback-check <file|-> # is a hand-back complete and plain (see Guardrails)
|
|
1918
2034
|
<cli> sessions attach <ws>:<id> # its terminal; ctrl-] detaches
|
|
1919
2035
|
```
|
|
1920
2036
|
|
|
@@ -2118,6 +2234,8 @@ standing: true # starts with its workspace (later slices)
|
|
|
2118
2234
|
context: [docs/specs, the design system] # what to read first
|
|
2119
2235
|
runtime: generic # default den-claude
|
|
2120
2236
|
permission_mode: plan # den-claude only; default `default`
|
|
2237
|
+
den_hooks: false # Den's guardrail hooks for this persona (see Guardrails)
|
|
2238
|
+
den_instructions: false # Den's shared instructions for this persona
|
|
2121
2239
|
argv: ["some-cli", "--model", "{model}", "--mcp-config", "{mcp_config}"]
|
|
2122
2240
|
---
|
|
2123
2241
|
|
|
@@ -2139,6 +2257,104 @@ used without converting them. Files without front matter (a README) are
|
|
|
2139
2257
|
skipped; two files with one id in a pack, or a pack that isn't a directory,
|
|
2140
2258
|
are errors naming the file or the key.
|
|
2141
2259
|
|
|
2260
|
+
### Guardrails: hooks, shared instructions, hand-backs
|
|
2261
|
+
|
|
2262
|
+
Every persona in Den's default pack gets two things with no setup: Den's
|
|
2263
|
+
**shared persona instructions** and, on den-claude, Den's **hooks**. Both
|
|
2264
|
+
are Den's own files, in `src/shared/agents/guardrails/` of the package.
|
|
2265
|
+
|
|
2266
|
+
**Shared instructions** (`instructions.md`) are appended to the role prompt
|
|
2267
|
+
(den-claude's `--append-system-prompt`; any runtime that takes the role
|
|
2268
|
+
prompt gets them). They say:
|
|
2269
|
+
|
|
2270
|
+
- write to people plainly, in short sentences;
|
|
2271
|
+
- never name an issue or ID without saying what it is;
|
|
2272
|
+
- ask each decision as a numbered yes/no question;
|
|
2273
|
+
- no caveat sections, and nothing the person doesn't need in order to act;
|
|
2274
|
+
- read a slice of a log, or hand it to a subagent; don't poll with `sleep`;
|
|
2275
|
+
- end a hand-back with the template below.
|
|
2276
|
+
|
|
2277
|
+
**Hooks** (`hooks.json`, a Claude Code settings fragment) go in the agent's
|
|
2278
|
+
own settings file, `--settings <stateDir>/agent-files/<id>/settings.json`.
|
|
2279
|
+
Den never writes your `~/.claude`; your own hooks still run.
|
|
2280
|
+
|
|
2281
|
+
- `PreToolUse` on `Bash|Read` (`pretooluse.mjs`, plain Node, no imports)
|
|
2282
|
+
blocks, with a reason Claude sees (exit 2):
|
|
2283
|
+
- reading all of a log or a large file into the conversation: a `Read`
|
|
2284
|
+
with no `limit`, or `cat`, `less`, or `tail` from a line (`tail +1`,
|
|
2285
|
+
`tail -n +1`), whose output reaches the conversation. A log is `*.log` or `*.log.<N>`, a `.jsonl` under
|
|
2286
|
+
`.dev-metrics/`, or a `.jsonl` under `.claude/projects/` (a transcript).
|
|
2287
|
+
Large is over 256 KB. `cat x.log | grep …`, `cat x.log > y`, a plain
|
|
2288
|
+
`tail x.log` (10 lines) and `tail -n 100 x.log` pass. The reason says to read a slice or hand the
|
|
2289
|
+
file to a subagent.
|
|
2290
|
+
- `sleep` in the foreground, alone or in an `until …; do sleep …; done`,
|
|
2291
|
+
`while` or `for` loop. A Bash call with `run_in_background`, and
|
|
2292
|
+
`sleep … &`, pass. The reason says to run in the background or use a
|
|
2293
|
+
wait primitive. Recent Claude Code versions have their own sleep guard,
|
|
2294
|
+
which answers first (and also stops `sleep … &`). The sleep is blocked
|
|
2295
|
+
either way; Den's hook covers runtimes and versions without that guard.
|
|
2296
|
+
- Nothing else. Quoted text, heredoc bodies and other tools always pass,
|
|
2297
|
+
and a hook that can't read its input lets the call through.
|
|
2298
|
+
- `Stop` (`stop-hook.ts`) checks a turn's last message, which is what the
|
|
2299
|
+
person reads. With a `Hand-back` heading it gets the whole hand-back
|
|
2300
|
+
check; any other message gets the plain-writing part only (every ID
|
|
2301
|
+
explained). If the check fails, Claude is told what to fix and keeps
|
|
2302
|
+
going (Claude Code's `decision: "block"`). It never blocks twice in a
|
|
2303
|
+
row (`stop_hook_active`), so it can't loop. Den adds no `SubagentStop` hook: a subagent reports to its parent
|
|
2304
|
+
agent, not to a person.
|
|
2305
|
+
|
|
2306
|
+
**The hand-back template**, which the check enforces:
|
|
2307
|
+
|
|
2308
|
+
```markdown
|
|
2309
|
+
## Hand-back
|
|
2310
|
+
|
|
2311
|
+
- URL: <the full link the person opens, with https://>
|
|
2312
|
+
- Machine: <the machine it runs on>
|
|
2313
|
+
- Branch: <the branch>
|
|
2314
|
+
- What changed: <what is different now>
|
|
2315
|
+
- What to click: <the steps to see it working>
|
|
2316
|
+
- Decisions:
|
|
2317
|
+
1. <a question the person answers yes or no>?
|
|
2318
|
+
```
|
|
2319
|
+
|
|
2320
|
+
`<cli> agents handback-check <file|-> [--json]` runs the same check on a
|
|
2321
|
+
file or stdin: exit 0 when the hand-back passes, 1 with each problem. It
|
|
2322
|
+
rejects a hand-back when:
|
|
2323
|
+
|
|
2324
|
+
- a part is missing or empty. A part is a line starting with its label and
|
|
2325
|
+
a colon (bold is fine), or a heading that is its label. URL must hold a
|
|
2326
|
+
link (`://`), or say `none` for work with no page (`URL: none — a CLI
|
|
2327
|
+
change` is fine). `Decisions: none` is allowed.
|
|
2328
|
+
- an ID is bare. An ID is `#<digits>`, or 1 to 4 capitals, an optional `-`,
|
|
2329
|
+
digits and an optional `.<digits>` (`R12`, `AG-14`, `KB-4.1`). Links,
|
|
2330
|
+
`inline code`, code blocks, and the URL and Branch parts are not checked.
|
|
2331
|
+
An ID is explained (glossed) when
|
|
2332
|
+
1. a parenthetical of two or more words follows it: `#5 (the hooks issue)`;
|
|
2333
|
+
2. a colon or a spaced dash and two or more words follow it:
|
|
2334
|
+
`R12: plain writing`;
|
|
2335
|
+
3. it sits in a parenthetical of IDs only, right after a word:
|
|
2336
|
+
`plain writing (R12)`, `the issues (#5, #6)`;
|
|
2337
|
+
4. or the same ID was explained earlier in the text.
|
|
2338
|
+
- a decision isn't a numbered yes/no question: each item must be numbered
|
|
2339
|
+
(`1.` or `1)`), end in `?`, not start with what, which, how, why, when,
|
|
2340
|
+
where or who, and not offer a choice (`or`).
|
|
2341
|
+
|
|
2342
|
+
**Turning them off or on.** They are on for Den's default pack and off for
|
|
2343
|
+
your own packs. A pack's setting applies to each of its personas; a
|
|
2344
|
+
persona's front matter (`den_hooks`, `den_instructions`) wins over it:
|
|
2345
|
+
|
|
2346
|
+
```yaml
|
|
2347
|
+
agents:
|
|
2348
|
+
default_pack: { hooks: false } # Den's pack: no hooks, instructions kept
|
|
2349
|
+
persona_packs:
|
|
2350
|
+
- personas # your pack: neither
|
|
2351
|
+
- { path: more-personas, hooks: true, instructions: true } # both
|
|
2352
|
+
```
|
|
2353
|
+
|
|
2354
|
+
A persona of yours that replaces a default one (same id) takes your
|
|
2355
|
+
pack's setting, not Den's pack's. Anything but `true` or `false`, or an
|
|
2356
|
+
unknown key, is an error naming the key.
|
|
2357
|
+
|
|
2142
2358
|
### Runtimes
|
|
2143
2359
|
|
|
2144
2360
|
`<cli> doctor` lists the runtime kind. Den knows the runtimes in its
|
|
@@ -2321,6 +2537,11 @@ metrics:
|
|
|
2321
2537
|
share: true
|
|
2322
2538
|
```
|
|
2323
2539
|
|
|
2540
|
+
Each line also records `cwd`: where the command ran, relative to the
|
|
2541
|
+
distribution root (`.` for the root), or `outside` for anywhere else. It is
|
|
2542
|
+
never an absolute or home path. [`<cli> doctor`](#doctor) uses it to warn
|
|
2543
|
+
about polling loops.
|
|
2544
|
+
|
|
2324
2545
|
Only the boolean `true` counts. With it, and a Sentry DSN in
|
|
2325
2546
|
`DEV_METRICS_SENTRY_DSN` (or a synced `dev-metrics.config`), each line is
|
|
2326
2547
|
also sent, best effort. A DSN on its own never sends. `CI=true` and
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monster-orange/den",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.7",
|
|
4
4
|
"description": "Den: a local multi-repo dev orchestrator, HTTPS edge and E2E harness",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
"README.md",
|
|
26
26
|
"CHANGELOG.md",
|
|
27
27
|
"docs/",
|
|
28
|
-
"!docs/images/"
|
|
28
|
+
"!docs/images/",
|
|
29
|
+
"skills/"
|
|
29
30
|
],
|
|
30
31
|
"scripts": {
|
|
31
32
|
"cli": "tsx src/ui/cli/index.ts menu",
|
|
@@ -44,7 +45,7 @@
|
|
|
44
45
|
"gate:org-names": "node scripts/org-name-gate.mjs",
|
|
45
46
|
"docs:shots": "node scripts/doc-shots.mjs",
|
|
46
47
|
"docs:links": "node scripts/docs-links.js",
|
|
47
|
-
"example:regen": "rm -rf examples/distribution && ./bin/den create acme examples/distribution",
|
|
48
|
+
"example:regen": "rm -rf examples/distribution && ./bin/den create acme examples/distribution --port 7777",
|
|
48
49
|
"dist-smoke": "bash scripts/dist-smoke.sh"
|
|
49
50
|
},
|
|
50
51
|
"keywords": [
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: install-den
|
|
3
|
+
description: Install and set up Den, Monster Orange's local dev orchestrator, for a developer in five minutes. Use when the person says "set up Den", "install Den", "get me started with Den", or pastes a link to this file.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Install Den
|
|
7
|
+
|
|
8
|
+
You are setting up Den on this person's machine. Den runs their repos locally, shows them in a browser, and gives you
|
|
9
|
+
(the agent) an MCP server to work with. Aim to be done in five minutes. Keep your messages short and plain.
|
|
10
|
+
|
|
11
|
+
## Hard rules
|
|
12
|
+
|
|
13
|
+
- **Never ask for a token, password or secret in chat.** If a step needs one, tell the person which command to run
|
|
14
|
+
themselves.
|
|
15
|
+
- **Never edit the person's repos.** Den's files go in the Den folder only. Read the repo; don't write to it.
|
|
16
|
+
- **Send nothing off this machine.** No uploads, no telemetry, no pasting their code anywhere. The only downloads are
|
|
17
|
+
Den from npm and a repo the person gave you as a git URL.
|
|
18
|
+
- **Sudo is the person's job.** Never run `sudo` yourself. When a Den step needs it, Den asks for the password in its
|
|
19
|
+
own prompt (in the terminal or the browser), and the person types it there.
|
|
20
|
+
- Every step below is safe to re-run. If you are unsure whether a step ran, run it again.
|
|
21
|
+
|
|
22
|
+
## 1. Check the prerequisites
|
|
23
|
+
|
|
24
|
+
Run these and read the results. Don't install anything without saying so first.
|
|
25
|
+
|
|
26
|
+
1. `node --version`: needs **22.13 or later**. If it's older or missing, say so and suggest their usual installer
|
|
27
|
+
(for example `brew install node`, or `nvm install 22`).
|
|
28
|
+
2. `npm --version`: needs 9 or later. It comes with Node.
|
|
29
|
+
3. `git --version`: needs to be there.
|
|
30
|
+
4. `docker info`: only matters if the repo runs in containers (it has a `docker-compose.yml` or `compose.yaml`).
|
|
31
|
+
If Docker is needed and not running, ask them to start it.
|
|
32
|
+
|
|
33
|
+
If anything is missing, stop here, tell them what and how to fix it, and wait.
|
|
34
|
+
|
|
35
|
+
## 2. Ask at most three questions
|
|
36
|
+
|
|
37
|
+
Ask them together, each with a default, so the person can just say "yes":
|
|
38
|
+
|
|
39
|
+
1. **Where should your Den live?** Default: `~/den`. It must be a new or empty folder.
|
|
40
|
+
2. **Which repo should we start with?** Default: the folder you're in now. A local path or a git URL.
|
|
41
|
+
3. **What should we call your Den?** Default: `den`. Lowercase letters, numbers and dashes. This also becomes the
|
|
42
|
+
command name, so `acme` gives you `./bin/acme`.
|
|
43
|
+
|
|
44
|
+
Below, `<dir>` is the Den folder, `<repo>` the repo path, and `<name>` the name.
|
|
45
|
+
|
|
46
|
+
## 3. Create the Den
|
|
47
|
+
|
|
48
|
+
Every Den command below takes `--json`. It then prints one JSON object on stdout and its log on stderr. Read `ok`
|
|
49
|
+
first. When `ok` is false, the object says why (`error`) and either a command to run (`fix`) or a question for the
|
|
50
|
+
person (`ask`, sometimes with `choices`).
|
|
51
|
+
|
|
52
|
+
1. `npx @monster-orange/den create <name> <dir> --yes --json`. It answers `{ok, dir, cli, existed, port, next}`. If
|
|
53
|
+
the folder already holds this Den, it answers `ok` with `existed: true` and changes nothing. `port` is the browser
|
|
54
|
+
UI's port. When another Den already uses the usual one on this machine, Den picks the next free port and says so
|
|
55
|
+
(`portPicked`). Remember the port for the hand-back.
|
|
56
|
+
2. `cd <dir> && npm install`
|
|
57
|
+
3. From here on, the Den command is `./bin/<name>`, run from `<dir>`.
|
|
58
|
+
|
|
59
|
+
## 4. Add the repo without changing it
|
|
60
|
+
|
|
61
|
+
1. `./bin/<name> repo add <repo> --json`. `<repo>` can be a path or a git URL. Den clones a URL into its own folder.
|
|
62
|
+
- If the repo has its own `.<name>.yml`, Den uses it.
|
|
63
|
+
- If it doesn't, Den works out how the repo starts and writes a side-car manifest into `<dir>/manifests/`. It never
|
|
64
|
+
writes into the repo. It looks for a Makefile `up` target, then a compose file, then the `dev` or `start` script
|
|
65
|
+
in `package.json`. `--sidecar` asks for this explicitly, and fails if the repo has its own manifest.
|
|
66
|
+
- If you can see which port the app listens on, add `--health http://localhost:<port>/`. Den then checks that it
|
|
67
|
+
answers.
|
|
68
|
+
2. If the answer has `ask`, Den couldn't tell how the repo starts, and it wrote nothing. Ask the person **one**
|
|
69
|
+
question: the `ask` text, with any `choices` numbered (there may be none). Then re-run with their answer:
|
|
70
|
+
`./bin/<name> repo add <repo> --run "<their command>" --json`.
|
|
71
|
+
3. The answer names the repo (`repo`), the workspace it joined (`workspace`) and where its manifest is (`manifest`).
|
|
72
|
+
A workspace is Den's name for a set of repos that run together. The first one is called `default`.
|
|
73
|
+
|
|
74
|
+
## 5. Start it and check it
|
|
75
|
+
|
|
76
|
+
1. `./bin/<name> up <repo> --json`. `<repo>` is the `repo` value from step 4. Each service comes back with a
|
|
77
|
+
`result` (`started` or `already-running`) and a `health` (`ok`, or `unknown` when it has no health check).
|
|
78
|
+
2. `./bin/<name> web up --no-open --json`. This starts the browser UI, and answers `{ok, url, port}`. The MCP server
|
|
79
|
+
reads through it.
|
|
80
|
+
3. `./bin/<name> doctor --json`. Each check comes back as `ok`, `warn` or `fail`. The checks are Node, git, Docker
|
|
81
|
+
(only if a repo needs it), the web server (with its `url` and `port`) and Den's MCP. Fix every `fail` before you hand back.
|
|
82
|
+
|
|
83
|
+
## 6. Connect yourself to Den's MCP
|
|
84
|
+
|
|
85
|
+
1. `./bin/<name> mcp connect --runtime <you> --json`. `<you>` is `claude`, `codex`, `gemini` or `grok`. For any other
|
|
86
|
+
agent, `./bin/<name> mcp config` prints the JSON to add by hand.
|
|
87
|
+
2. `./bin/<name> mcp clients` shows whether you are connected.
|
|
88
|
+
3. If the answer has `restart: true`, you load the new MCP server only when you start again. Tell the person to
|
|
89
|
+
restart you once.
|
|
90
|
+
|
|
91
|
+
## 7. Hand back
|
|
92
|
+
|
|
93
|
+
Tell the person, in a few short sentences. Explain every name you use the first time (for example, "a workspace is a
|
|
94
|
+
set of repos that run together").
|
|
95
|
+
|
|
96
|
+
- **Open:** the `url` from `web up` or from the doctor's `web` check, for example `http://localhost:<port>`. Never
|
|
97
|
+
assume a port: use the one Den gave you.
|
|
98
|
+
- **What's running:** each service from `up`, and whether it's healthy.
|
|
99
|
+
- **Your Den:** where it lives (`<dir>`) and its command (`./bin/<name>`).
|
|
100
|
+
- **Decisions, if any:** as numbered yes/no questions, for example "1. Restart me now so I can use Den's tools?"
|
|
101
|
+
- **Three next things we can do together:**
|
|
102
|
+
1. Add another repo.
|
|
103
|
+
2. Look at a service's logs, or run its tests.
|
|
104
|
+
3. Start an agent session on this repo from the browser.
|
|
105
|
+
|
|
106
|
+
## When something fails
|
|
107
|
+
|
|
108
|
+
1. Read the JSON. If it has `fix`, run that command, then re-run the step that failed.
|
|
109
|
+
2. If it has `ask`, the person has to decide or act (sudo, a password, starting Docker, signing in). Ask them the
|
|
110
|
+
question, show any `choices` numbered, and wait.
|
|
111
|
+
3. If there is neither, ask the person **one** clear question, with your best guess as the default.
|
|
112
|
+
4. Never work around a failure by editing their repo, turning off a check, or skipping a step without saying so.
|
|
113
|
+
5. If you're stuck after two tries, stop. Tell them what you ran, what it said, and where Den writes its logs
|
|
114
|
+
(`~/.<name>/`), so they can get help.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one line a developer pastes into their agent to have it set Den up
|
|
3
|
+
* (ON-5): the agent follows the install-den skill published at this URL
|
|
4
|
+
* (skills/install-den/SKILL.md in the package).
|
|
5
|
+
*/
|
|
6
|
+
export const AGENT_SETUP_URL =
|
|
7
|
+
"https://raw.githubusercontent.com/Monster-Orange/den/main/skills/install-den/SKILL.md";
|
|
8
|
+
|
|
9
|
+
export const AGENT_SETUP_LINE = `Set up Den for me by following ${AGENT_SETUP_URL}`;
|