modelroom 0.0.0-stage → 0.0.2

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 (66) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/LICENSE +21 -0
  3. package/README.md +149 -2
  4. package/dist/bin/modelroom.d.ts +3 -0
  5. package/dist/bin/modelroom.d.ts.map +1 -0
  6. package/dist/bin/modelroom.js +242 -0
  7. package/dist/bin/modelroom.js.map +1 -0
  8. package/dist/lib/agents/index.d.ts +22 -0
  9. package/dist/lib/agents/index.d.ts.map +1 -0
  10. package/dist/lib/agents/index.js +68 -0
  11. package/dist/lib/agents/index.js.map +1 -0
  12. package/dist/lib/artifact.d.ts +22 -0
  13. package/dist/lib/artifact.d.ts.map +1 -0
  14. package/dist/lib/artifact.js +62 -0
  15. package/dist/lib/artifact.js.map +1 -0
  16. package/dist/lib/config.d.ts +53 -0
  17. package/dist/lib/config.d.ts.map +1 -0
  18. package/dist/lib/config.js +92 -0
  19. package/dist/lib/config.js.map +1 -0
  20. package/dist/lib/facets.d.ts +62 -0
  21. package/dist/lib/facets.d.ts.map +1 -0
  22. package/dist/lib/facets.js +181 -0
  23. package/dist/lib/facets.js.map +1 -0
  24. package/dist/lib/frontmatter.d.ts +41 -0
  25. package/dist/lib/frontmatter.d.ts.map +1 -0
  26. package/dist/lib/frontmatter.js +219 -0
  27. package/dist/lib/frontmatter.js.map +1 -0
  28. package/dist/lib/lint.d.ts +26 -0
  29. package/dist/lib/lint.d.ts.map +1 -0
  30. package/dist/lib/lint.js +139 -0
  31. package/dist/lib/lint.js.map +1 -0
  32. package/dist/lib/opencode.d.ts +42 -0
  33. package/dist/lib/opencode.d.ts.map +1 -0
  34. package/dist/lib/opencode.js +229 -0
  35. package/dist/lib/opencode.js.map +1 -0
  36. package/dist/lib/pkg.d.ts +2 -0
  37. package/dist/lib/pkg.d.ts.map +1 -0
  38. package/dist/lib/pkg.js +19 -0
  39. package/dist/lib/pkg.js.map +1 -0
  40. package/dist/lib/rooms.d.ts +63 -0
  41. package/dist/lib/rooms.d.ts.map +1 -0
  42. package/dist/lib/rooms.js +178 -0
  43. package/dist/lib/rooms.js.map +1 -0
  44. package/dist/lib/server.d.ts +53 -0
  45. package/dist/lib/server.d.ts.map +1 -0
  46. package/dist/lib/server.js +538 -0
  47. package/dist/lib/server.js.map +1 -0
  48. package/docs/01_concept.md +69 -0
  49. package/docs/02-convention.md +109 -0
  50. package/docs/03-cli.md +103 -0
  51. package/docs/04-agents.md +88 -0
  52. package/examples/rooms/sign-in/BEHAVIOUR.md +23 -0
  53. package/examples/rooms/sign-in/BRIEF.md +15 -0
  54. package/examples/rooms/sign-in/SPEC.md +14 -0
  55. package/examples/rooms/sign-in/index.html +56 -0
  56. package/package.json +83 -4
  57. package/templates/AGENTS.md +31 -0
  58. package/templates/modelroom.json +14 -0
  59. package/templates/room/BEHAVIOUR.md +19 -0
  60. package/templates/room/BRIEF.md +16 -0
  61. package/templates/room/SPEC.md +9 -0
  62. package/templates/room/index.html +53 -0
  63. package/ui/dashboard.html +332 -0
  64. package/ui/modelroom.css +2 -0
  65. package/ui/room.html +466 -0
  66. package/ui/settings.html +125 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,32 @@
1
+ # Changelog
2
+
3
+ ## [0.0.2](https://github.com/modelroom/modelroom/compare/modelroom-v0.0.1...modelroom-v0.0.2) (2026-10-18)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * bin の先頭の ./ を外し、publish 時のマニフェスト補正をなくす ([f1ba67e](https://github.com/modelroom/modelroom/commit/f1ba67e8d6d91affb5f542c13b46fe620e92aab4))
9
+
10
+
11
+ ### Miscellaneous Chores
12
+
13
+ * npm の説明文とキーワードを直す ([5fa6ade](https://github.com/modelroom/modelroom/commit/5fa6ade94fdbcdf236a4ab0157dce76552d02305))
14
+
15
+ ## 0.0.1 (2026-10-18)
16
+
17
+
18
+ ### Features
19
+
20
+ * frontmatter as the record — derived facets, search, and check
21
+ * room session panel — transcript, context and usage (opencode)
22
+ * room のセッションパネル(履歴・コンテキスト・使用量, opencode)
23
+ * the modelroom workspace — server, CLI, agent adapters and UI
24
+ * `modelroom` with no command now serves; `npm run modelroom` is the documented start
25
+
26
+
27
+ ### Bug Fixes
28
+
29
+ * セッションIDを推測不能にし、残るTOCTOUとエラー露出を解消
30
+ * ファイル生成の競合とエラー文言の露出をなくす
31
+ * ファイル読み書きのTOCTOUとスタック情報の露出を解消
32
+ * 設定を永続化し、拒否したリクエストの副作用と spawn の二重通知をなくす
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Chrogo Inc
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,150 @@
1
- # Temporary Holding Version
1
+ # modelroom
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![CI](https://github.com/modelroom/modelroom/actions/workflows/ci.yml/badge.svg)](https://github.com/modelroom/modelroom/actions/workflows/ci.yml)
4
+ [![codecov](https://codecov.io/gh/modelroom/modelroom/branch/main/graph/badge.svg)](https://codecov.io/gh/modelroom/modelroom)
5
+ [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/modelroom/modelroom/badge)](https://scorecard.dev/viewer/?uri=github.com/modelroom/modelroom)
6
+ [![npm](https://img.shields.io/npm/v/modelroom.svg)](https://www.npmjs.com/package/modelroom)
7
+ [![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
8
+
9
+ **A full-scale model room for UI — convincing, not yet real.**
10
+
11
+ modelroom is a workspace for designing screens with coding agents. Each screen is a **room**:
12
+ a reason (`BRIEF.md`), an invisible spec (`SPEC.md`), a set of promises (`BEHAVIOUR.md`) and the
13
+ artifact (`index.html`) — kept apart, side by side, on one page.
14
+
15
+ It is agent-agnostic. Bring OpenCode, Claude Code, Codex, or any CLI. modelroom bundles no model
16
+ and no chat product: it gives your agent a place to work, and gives you a place to look.
17
+
18
+ ```bash
19
+ npx modelroom init checkout # a room, with its docs and a starter artifact
20
+ npx modelroom # → http://127.0.0.1:4321
21
+ ```
22
+
23
+ ## Why "model room"
24
+
25
+ A model room in real estate is furnished, full scale, and convincing enough to decide in — but it
26
+ is not the apartment you will live in. That is the point: decide here, build the real thing after.
27
+ The design layer is the model room; implementation is the building.
28
+
29
+ Because a room is **not** the real thing, the promises that only the real system can keep —
30
+ records, timestamps, rounding, permissions — do not live here. The design layer never reads your
31
+ application code.
32
+
33
+ ## What you get
34
+
35
+ | Where | What |
36
+ |---|---|
37
+ | `/` | every room, side by side, with thumbnails — full-text search and filters built from the records |
38
+ | `/room/<name>` | chat, the room's docs (editable), and the artifact — updating as the agent works |
39
+ | `/settings` | which agent to run, and with which model |
40
+ | `/files/<path>` | your repository, served read-only and **unmodified** (no injected scripts) |
41
+
42
+ The room's docs are the only files the UI can write. Changing `index.html` (from the agent or by
43
+ hand) reloads the preview with no manual refresh; changes elsewhere in the room stream in the same way.
44
+
45
+ ## The convention in one table
46
+
47
+ | File | Answers | Shown to |
48
+ |---|---|---|
49
+ | `BRIEF.md` | why does this room exist? | director, everyone |
50
+ | `SPEC.md` | what the artifact cannot show (devices, states, constraints, what it may use) | designer, agent, implementer |
51
+ | `BEHAVIOUR.md` | what it promises (operations and results) | tests, implementer |
52
+ | `index.html` | what it looks like — the artifact itself | director, owner, implementer |
53
+
54
+ Two rules do the work: **the artifact owns what is visible** (specs never restate it), and **a
55
+ statement lives in one file** (others reference it by id). See [`docs/02-convention.md`](docs/02-convention.md).
56
+
57
+ Each room's frontmatter is its record. A few keys are reserved (`title`, `summary`,
58
+ `key_points`, `tags`, `status`, `version`, `deprecated`, `superseded_by`); **everything else
59
+ becomes a facet on the dashboard automatically** — a string turns into a select, a list into
60
+ checkboxes, built from the values that actually exist. Search covers the docs, not just the
61
+ titles.
62
+
63
+ ```yaml
64
+ ---
65
+ title: Sign in
66
+ status: draft
67
+ version: 1
68
+ summary: A boring front door — signed in, or told plainly why not.
69
+ tags: [auth, form]
70
+ ---
71
+ ```
72
+
73
+ An agent adds; it does not delete. Retiring a room is `deprecated: true` plus
74
+ `superseded_by: <room>`, so the record stays and git can bring it back. `modelroom check`
75
+ verifies the convention without writing anything. `adopted` is a human decision — an agent
76
+ must never set it.
77
+
78
+ ## Agents
79
+
80
+ ```jsonc
81
+ // modelroom.json
82
+ {
83
+ "rooms": "rooms",
84
+ "agent": {
85
+ "adapter": "opencode", // or "command" for any CLI
86
+ "command": "opencode",
87
+ "agent": "build",
88
+ "model": "openrouter/deepseek/deepseek-v4.1-flash",
89
+ "standalone": true
90
+ }
91
+ }
92
+ ```
93
+
94
+ `adapter: "command"` runs anything: `{ "command": "claude", "args": ["-p", "{prompt}"] }`.
95
+ Full detail, including how to scope an agent so it can only write room files, is in
96
+ [`docs/04-agents.md`](docs/04-agents.md).
97
+
98
+ ## From source
99
+
100
+ ```bash
101
+ git clone https://github.com/modelroom/modelroom && cd modelroom
102
+ npm install # dev only: tailwind/daisyUI (CSS is committed) + playwright (optional)
103
+ npm test # smoke checks
104
+ npm run modelroom # serves examples/rooms
105
+ npm run build:css # after changing ui/*.html
106
+ ```
107
+
108
+ The stylesheet in `ui/` is built and committed, so users never run a build step. Artifacts (in
109
+ `templates/` and `examples/`) are deliberately framework-free: plain HTML and CSS, no CDN, no web
110
+ fonts — they are handed to implementation and must render with no network.
111
+
112
+ PNG thumbnails are optional and need Playwright:
113
+
114
+ ```bash
115
+ npm i -D playwright && npx playwright install chromium
116
+ ```
117
+
118
+ ## Development
119
+
120
+ ```bash
121
+ npm ci
122
+ npm run dev # serve, watching the source
123
+ npm run verify # lint, types, format, tests, build — the same gates as CI
124
+ ```
125
+
126
+ Source is TypeScript (erasable syntax only) and runs directly on Node 22.18+. `npm run build`
127
+ emits `dist/` with declarations; that is what npm publishes. See
128
+ [CONTRIBUTING.md](CONTRIBUTING.md) for the rules.
129
+
130
+ ## Docs
131
+
132
+ - [`docs/01_concept.md`](docs/01_concept.md) — the layers, their audiences, the one-way dependency
133
+ - [`docs/02-convention.md`](docs/02-convention.md) — the files, the rules, the naming
134
+ - [`docs/03-cli.md`](docs/03-cli.md) — commands and configuration
135
+ - [`docs/04-agents.md`](docs/04-agents.md) — adapters and how to write one
136
+ - [`AGENTS.md`](AGENTS.md) — for agents working **on** modelroom
137
+
138
+ ## What it is not
139
+
140
+ - Not an app builder (v0, Lovable, bolt.new). Those put chat first; this puts the room first.
141
+ - Not a fork of anything. It is a convention plus a small local server.
142
+ - Not a replacement for your agent. It drives the one you already use.
143
+
144
+ ## Status
145
+
146
+ Early (`0.0.1`) — the first release. The convention comes from a real project; expect churn.
147
+
148
+ ## License
149
+
150
+ MIT
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=modelroom.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modelroom.d.ts","sourceRoot":"","sources":["../../bin/modelroom.ts"],"names":[],"mappings":""}
@@ -0,0 +1,242 @@
1
+ #!/usr/bin/env node
2
+ // modelroom — CLI
3
+ //
4
+ // modelroom [serve] [--port 4321] [--host 127.0.0.1] [--open]
5
+ // modelroom init [<room>] [--dir rooms] [--force]
6
+ // modelroom check [--json] [--strict]
7
+ // modelroom doctor
8
+ //
9
+ // `modelroom` with no command serves. See docs/03-cli.md.
10
+ import { readFile, writeFile, mkdir, access } from 'node:fs/promises';
11
+ import { constants } from 'node:fs';
12
+ import { spawn } from 'node:child_process';
13
+ import { dirname, join, resolve } from 'node:path';
14
+ import { fileURLToPath } from 'node:url';
15
+ import { loadConfig } from '../lib/config.js';
16
+ import { createModelroom } from '../lib/server.js';
17
+ import { listRoomNames } from '../lib/rooms.js';
18
+ import { lintRooms } from '../lib/lint.js';
19
+ import { findPackageRoot } from '../lib/pkg.js';
20
+ const pkgDir = findPackageRoot(dirname(fileURLToPath(import.meta.url)));
21
+ const templatesDir = join(pkgDir, 'templates');
22
+ const args = process.argv.slice(2);
23
+ const opts = {};
24
+ const flagValues = new Set();
25
+ for (let i = 0; i < args.length; i++) {
26
+ const arg = args[i];
27
+ if (!arg.startsWith('--'))
28
+ continue;
29
+ const key = arg.slice(2);
30
+ const next = args[i + 1];
31
+ if (next && !next.startsWith('--')) {
32
+ opts[key] = next;
33
+ flagValues.add(i + 1);
34
+ i++;
35
+ }
36
+ else {
37
+ opts[key] = true;
38
+ }
39
+ }
40
+ // The command is the first bare word that is not a flag's value — so bare
41
+ // `modelroom` serves, and `modelroom --port 5000` still serves on that port.
42
+ const positional = args.filter((a, i) => !a.startsWith('-') && !flagValues.has(i));
43
+ const cmd = positional[0] ?? 'serve';
44
+ const pkg = JSON.parse(await readFile(join(pkgDir, 'package.json'), 'utf8'));
45
+ if (opts.version || cmd === 'version') {
46
+ console.log(pkg.version);
47
+ process.exit(0);
48
+ }
49
+ if (cmd === 'help' || opts.help)
50
+ usage();
51
+ function usage() {
52
+ console.log(`modelroom ${pkg.version} — a full-scale model room for UI
53
+
54
+ usage
55
+ modelroom [serve] [--port 4321] [--host 127.0.0.1] [--open]
56
+ modelroom init [<room>] [--dir rooms] [--force]
57
+ modelroom check [--json] [--strict]
58
+ modelroom doctor
59
+ modelroom --version | --help
60
+
61
+ A room is one screen: rooms/<name>/ with BRIEF.md, SPEC.md, BEHAVIOUR.md and an artifact.
62
+ Config lives in modelroom.json; local state in .modelroom/state.json (git-ignore it).
63
+ `);
64
+ process.exit(0);
65
+ }
66
+ const cwd = resolve(typeof opts.cwd === 'string' ? opts.cwd : process.cwd());
67
+ const str = (v) => typeof v === 'string' ? v : undefined;
68
+ // ── serve ───────────────────────────────────────────────────────────────────
69
+ if (cmd === 'serve') {
70
+ const app = await createModelroom({ cwd, port: str(opts.port), host: str(opts.host) });
71
+ app.startWatching();
72
+ try {
73
+ const { url } = await app.listen();
74
+ const rooms = await listRoomNames(app.cfg);
75
+ console.log([
76
+ `modelroom ${pkg.version}`,
77
+ ` ${url}`,
78
+ ` root ${app.cfg.rootDir}`,
79
+ ` rooms ${rooms.length ? rooms.join(', ') : '(none yet — try: npx modelroom init hello)'}`,
80
+ ` agent ${app.cfg.agent.adapter} · ${app.cfg.agent.command} · agent=${app.cfg.agent.agent || '-'} · model=${app.cfg.agent.model || 'default'}`,
81
+ '',
82
+ ' Artifacts are served unmodified. Writable files: ' + app.cfg.docs.join(', '),
83
+ ' Ctrl+C to stop',
84
+ ].join('\n'));
85
+ if (opts.open)
86
+ openBrowser(url);
87
+ }
88
+ catch (err) {
89
+ const code = err.code;
90
+ console.error(code === 'EADDRINUSE'
91
+ ? `port ${app.cfg.port} is in use — pass --port <n>`
92
+ : err instanceof Error
93
+ ? err.message
94
+ : String(err));
95
+ process.exit(1);
96
+ }
97
+ }
98
+ // ── init ────────────────────────────────────────────────────────────────────
99
+ else if (cmd === 'init') {
100
+ const cfg = await loadConfig(cwd);
101
+ if (typeof opts.dir === 'string' && !opts.dir.startsWith('-')) {
102
+ cfg.rooms = opts.dir;
103
+ cfg.roomsDir = resolve(cfg.rootDir, cfg.rooms);
104
+ }
105
+ const room = typeof opts.room === 'string' && !opts.room.startsWith('-')
106
+ ? opts.room
107
+ : positional[1] || 'hello';
108
+ const force = Boolean(opts.force);
109
+ await mkdir(cfg.roomsDir, { recursive: true });
110
+ const roomDir = join(cfg.roomsDir, room);
111
+ await mkdir(roomDir, { recursive: true });
112
+ const written = [];
113
+ for (const f of ['BRIEF.md', 'SPEC.md', 'BEHAVIOUR.md', 'index.html']) {
114
+ const dest = join(roomDir, f);
115
+ if (!force && (await exists(dest))) {
116
+ console.log(`skip ${rel(dest, cwd)} (exists)`);
117
+ continue;
118
+ }
119
+ const src = await readFile(join(templatesDir, 'room', f), 'utf8');
120
+ await writeFile(dest, src.replaceAll('{{room}}', room));
121
+ written.push(rel(dest, cwd));
122
+ }
123
+ for (const [tpl, dest, label] of [
124
+ ['modelroom.json', join(cwd, 'modelroom.json'), 'modelroom.json'],
125
+ ['AGENTS.md', join(cwd, 'AGENTS.md'), 'AGENTS.md'],
126
+ ]) {
127
+ if (!force && (await exists(dest))) {
128
+ console.log(`skip ${label} (exists)`);
129
+ continue;
130
+ }
131
+ await writeFile(dest, await readFile(join(templatesDir, tpl), 'utf8'));
132
+ written.push(label);
133
+ }
134
+ const gi = join(cwd, '.gitignore');
135
+ const lines = await readFile(gi, 'utf8').catch(() => '');
136
+ if (!lines.includes('.modelroom')) {
137
+ await writeFile(gi, (lines ? lines.replace(/\n?$/, '\n') : '') + '.modelroom/\n');
138
+ written.push('.gitignore (+ .modelroom/)');
139
+ }
140
+ console.log(`created in ${cwd}:`);
141
+ for (const w of written)
142
+ console.log(` ${w}`);
143
+ console.log(`\nnext\n npx modelroom\n open http://127.0.0.1:${cfg.port}/room/${room}`);
144
+ }
145
+ // ── check ───────────────────────────────────────────────────────────────────
146
+ // The record is derived from the docs; the check looks for what would stop the
147
+ // derivation — malformed frontmatter, reserved keys with the wrong shape, and
148
+ // the same value spelled two ways. It never deletes or rewrites.
149
+ else if (cmd === 'check') {
150
+ const cfg = await loadConfig(cwd);
151
+ const { issues, stats } = await lintRooms(cfg);
152
+ if (opts.json) {
153
+ console.log(JSON.stringify({ stats, issues }, null, 2));
154
+ }
155
+ else {
156
+ for (const i of issues) {
157
+ const where = `${i.room}${i.file ? '/' + i.file : ''}`;
158
+ console.log(`${i.level === 'error' ? 'FAIL' : 'warn'} ${where} ${i.code} ${i.message}`);
159
+ }
160
+ console.log(`\n${stats.rooms} room(s), ${stats.errors} error(s), ${stats.warnings} warning(s)`);
161
+ }
162
+ process.exit(stats.errors || (opts.strict && stats.warnings) ? 1 : 0);
163
+ }
164
+ // ── doctor ──────────────────────────────────────────────────────────────────
165
+ else if (cmd === 'doctor') {
166
+ const cfg = await loadConfig(cwd);
167
+ const checks = [];
168
+ const nodeMajor = Number(process.versions.node.split('.')[0]);
169
+ checks.push([nodeMajor >= 22, `node ${process.versions.node} (needs >= 22.18)`]);
170
+ checks.push([await exists(cfg.rootDir), `root exists: ${cfg.rootDir}`]);
171
+ checks.push([await exists(cfg.roomsDir), `rooms dir exists: ${rel(cfg.roomsDir, cwd)}`]);
172
+ const rooms = await listRoomNames(cfg);
173
+ checks.push([rooms.length > 0, `rooms found: ${rooms.length ? rooms.join(', ') : 'none'}`]);
174
+ checks.push([
175
+ await exists(cfg.configPath),
176
+ `config: ${(await exists(cfg.configPath)) ? 'modelroom.json' : 'modelroom.json (defaults in use)'}`,
177
+ ]);
178
+ const onPath = await which(cfg.agent.command);
179
+ checks.push([
180
+ Boolean(onPath),
181
+ `agent command on PATH: ${cfg.agent.command}${onPath ? '' : ' (not found — the chat will fail)'}`,
182
+ ]);
183
+ const { playwrightAvailable } = await import('../lib/artifact.js');
184
+ const pw = await playwrightAvailable();
185
+ checks.push([pw, `png rendering (playwright): ${pw ? 'available' : 'not installed (optional)'}`]);
186
+ for (const room of rooms) {
187
+ const missing = [];
188
+ for (const d of cfg.docs)
189
+ if (!(await exists(join(cfg.roomsDir, room, d))))
190
+ missing.push(d);
191
+ const hasArtifact = await exists(join(cfg.roomsDir, room, cfg.artifact));
192
+ checks.push([
193
+ missing.length === 0 && hasArtifact,
194
+ `room ${room}: ${missing.length ? 'missing ' + missing.join(', ') : 'docs ok'}${hasArtifact ? '' : `, no ${cfg.artifact}`}`,
195
+ ]);
196
+ }
197
+ let bad = 0;
198
+ for (const [ok, label] of checks) {
199
+ if (!ok)
200
+ bad++;
201
+ console.log(`${ok ? 'ok ' : 'FAIL'} ${label}`);
202
+ }
203
+ const onlyOptional = checks.every(([ok, label]) => ok || label.includes('optional'));
204
+ console.log(bad ? `\n${bad} problem(s)${onlyOptional ? ' (optional)' : ''}` : '\nall good');
205
+ process.exit(bad && !onlyOptional ? 1 : 0);
206
+ }
207
+ else {
208
+ console.error(`unknown command: ${cmd}\n`);
209
+ usage();
210
+ }
211
+ // ── helpers ─────────────────────────────────────────────────────────────────
212
+ async function exists(p) {
213
+ try {
214
+ await access(p, constants.F_OK);
215
+ return true;
216
+ }
217
+ catch {
218
+ return false;
219
+ }
220
+ }
221
+ function rel(p, base) {
222
+ return p.startsWith(base) ? p.slice(base.length + 1) : p;
223
+ }
224
+ async function which(bin) {
225
+ if (!bin)
226
+ return null;
227
+ const dirs = (process.env.PATH || '').split(':');
228
+ for (const d of dirs)
229
+ if (await exists(join(d, bin)))
230
+ return join(d, bin);
231
+ return null;
232
+ }
233
+ function openBrowser(url) {
234
+ const opener = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
235
+ try {
236
+ spawn(opener, [url], { stdio: 'ignore', detached: true }).unref();
237
+ }
238
+ catch {
239
+ /* headless: ignore */
240
+ }
241
+ }
242
+ //# sourceMappingURL=modelroom.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modelroom.js","sourceRoot":"","sources":["../../bin/modelroom.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { type ChildProcess } from 'node:child_process';
2
+ import type { Config } from '../config.ts';
3
+ export type Stream = 'stdout' | 'stderr';
4
+ export interface RunOptions {
5
+ cfg: Config;
6
+ cwd: string;
7
+ prompt: string;
8
+ sessionId: string;
9
+ onChunk: (stream: Stream, chunk: string) => void;
10
+ onExit: (code: number, error?: string) => void;
11
+ }
12
+ export interface Adapter {
13
+ name: string;
14
+ newSessionId: () => string;
15
+ run: (options: RunOptions) => ChildProcess;
16
+ }
17
+ export declare const stripAnsi: (s: string) => string;
18
+ export declare const opencode: Adapter;
19
+ export declare const command: Adapter;
20
+ export declare const adapters: Record<string, Adapter>;
21
+ export declare const getAdapter: (name: string) => Adapter;
22
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../lib/agents/index.ts"],"names":[],"mappings":"AAWA,OAAO,EAAS,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAE9D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C,MAAM,MAAM,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEzC,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAChD;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,MAAM,CAAC;IAC3B,GAAG,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,YAAY,CAAC;CAC5C;AAID,eAAO,MAAM,SAAS,MAAO,MAAM,KAAG,MAAoC,CAAC;AAyC3E,eAAO,MAAM,QAAQ,EAAE,OAYtB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,OAWrB,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAyB,CAAC;AACvE,eAAO,MAAM,UAAU,SAAU,MAAM,KAAG,OAAqC,CAAC"}
@@ -0,0 +1,68 @@
1
+ // modelroom — agent adapters
2
+ //
3
+ // An adapter runs a coding agent for one prompt in one room. Two ship today:
4
+ // - `opencode` OpenCode CLI (sessions, model, agent selection)
5
+ // - `command` any CLI, configured with args that contain {prompt} and {session}
6
+ //
7
+ // Adapter interface (see docs/04-agents.md):
8
+ // name string
9
+ // newSessionId() string one id per room, reused across prompts
10
+ // run(options) child handle streams stdout/stderr
11
+ import { spawn } from 'node:child_process';
12
+ import { randomUUID } from 'node:crypto';
13
+ // eslint-disable-next-line no-control-regex -- the escape byte is the point
14
+ const stripAnsiRe = /\u001b\[[0-9;?]*[ -/]*[@-~]/g;
15
+ export const stripAnsi = (s) => s.replace(stripAnsiRe, '');
16
+ // Session ids are not secrets, but they are identifiers: keep them unpredictable.
17
+ const random = () => randomUUID().replaceAll('-', '').slice(0, 8);
18
+ function spawnStreaming({ command, args, cwd, env, onChunk, onExit, }) {
19
+ const child = spawn(command, args, {
20
+ cwd,
21
+ // Close stdin: agents that read stdin will hang forever if it stays open.
22
+ stdio: ['ignore', 'pipe', 'pipe'],
23
+ env: { ...process.env, NO_COLOR: '1', FORCE_COLOR: '0', ...env },
24
+ });
25
+ // `error` (spawn failed) and `close` can both fire for one process: report the
26
+ // exit once, so the caller never sees a double `chat-done`.
27
+ let reported = false;
28
+ const finish = (code, error) => {
29
+ if (reported)
30
+ return;
31
+ reported = true;
32
+ onExit(code, error);
33
+ };
34
+ child.stdout?.on('data', (b) => onChunk('stdout', String(b)));
35
+ child.stderr?.on('data', (b) => onChunk('stderr', String(b)));
36
+ child.on('error', (err) => finish(-1, `cannot start ${command}: ${err.message}`));
37
+ child.on('close', (code) => finish(code ?? -1));
38
+ return child;
39
+ }
40
+ export const opencode = {
41
+ name: 'opencode',
42
+ newSessionId: () => `ses_${Date.now().toString(36)}${random()}`,
43
+ run({ cfg, cwd, prompt, sessionId, onChunk, onExit }) {
44
+ const a = cfg.agent;
45
+ const args = ['run'];
46
+ if (a.standalone)
47
+ args.push('--standalone');
48
+ if (a.agent)
49
+ args.push('--agent', a.agent);
50
+ if (a.model)
51
+ args.push('--model', a.model);
52
+ args.push('--session', sessionId, '--auto', prompt);
53
+ return spawnStreaming({ command: a.command || 'opencode', args, cwd, onChunk, onExit });
54
+ },
55
+ };
56
+ export const command = {
57
+ name: 'command',
58
+ newSessionId: () => `ses_${Date.now().toString(36)}${random()}`,
59
+ run({ cfg, cwd, prompt, sessionId, onChunk, onExit }) {
60
+ const a = cfg.agent;
61
+ const template = Array.isArray(a.args) && a.args.length ? a.args : ['{prompt}'];
62
+ const args = template.map((s) => String(s).replaceAll('{prompt}', prompt).replaceAll('{session}', sessionId));
63
+ return spawnStreaming({ command: a.command, args, cwd, onChunk, onExit });
64
+ },
65
+ };
66
+ export const adapters = { opencode, command };
67
+ export const getAdapter = (name) => adapters[name] ?? opencode;
68
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/agents/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { Config } from './config.ts';
2
+ export declare function playwrightAvailable(): Promise<boolean>;
3
+ export declare const INSTALL_HINT: string;
4
+ export type RenderResult = {
5
+ ok: true;
6
+ png: string;
7
+ width: number;
8
+ height: number;
9
+ } | {
10
+ ok: false;
11
+ error: string;
12
+ };
13
+ /**
14
+ * Render `rooms/<room>/<artifact>` to `<artifact>.png` next to it.
15
+ */
16
+ export declare function renderArtifact({ cfg, room, device, scale, }: {
17
+ cfg: Pick<Config, 'rooms' | 'roomsDir' | 'artifact'>;
18
+ room: string;
19
+ device?: string;
20
+ scale?: number;
21
+ }): Promise<RenderResult>;
22
+ //# sourceMappingURL=artifact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifact.d.ts","sourceRoot":"","sources":["../../lib/artifact.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,OAAO,CAAC,CAO5D;AAED,eAAO,MAAM,YAAY,QAGyC,CAAC;AAEnE,MAAM,MAAM,YAAY,GACpB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACxD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAIjC;;GAEG;AACH,wBAAsB,cAAc,CAAC,EACnC,GAAG,EACH,IAAI,EACJ,MAAiB,EACjB,KAAS,GACV,EAAE;IACD,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,GAAG,UAAU,GAAG,UAAU,CAAC,CAAC;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,YAAY,CAAC,CAoCxB"}