modelroom 0.0.5 → 0.1.0

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 (69) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +40 -8
  3. package/dist/bin/modelroom.js +241 -75
  4. package/dist/bin/modelroom.js.map +1 -1
  5. package/dist/lib/agents/index.d.ts +6 -0
  6. package/dist/lib/agents/index.d.ts.map +1 -1
  7. package/dist/lib/agents/index.js +22 -4
  8. package/dist/lib/agents/index.js.map +1 -1
  9. package/dist/lib/artifact.d.ts +1 -1
  10. package/dist/lib/artifact.d.ts.map +1 -1
  11. package/dist/lib/artifact.js +9 -5
  12. package/dist/lib/artifact.js.map +1 -1
  13. package/dist/lib/config.d.ts +17 -1
  14. package/dist/lib/config.d.ts.map +1 -1
  15. package/dist/lib/config.js +40 -4
  16. package/dist/lib/config.js.map +1 -1
  17. package/dist/lib/frontmatter.d.ts +2 -0
  18. package/dist/lib/frontmatter.d.ts.map +1 -1
  19. package/dist/lib/frontmatter.js +11 -2
  20. package/dist/lib/frontmatter.js.map +1 -1
  21. package/dist/lib/i18n.d.ts +315 -0
  22. package/dist/lib/i18n.d.ts.map +1 -0
  23. package/dist/lib/i18n.js +651 -0
  24. package/dist/lib/i18n.js.map +1 -0
  25. package/dist/lib/lang.d.ts +4 -0
  26. package/dist/lib/lang.d.ts.map +1 -0
  27. package/dist/lib/lang.js +5 -0
  28. package/dist/lib/lang.js.map +1 -0
  29. package/dist/lib/lint.d.ts +3 -0
  30. package/dist/lib/lint.d.ts.map +1 -1
  31. package/dist/lib/lint.js +22 -12
  32. package/dist/lib/lint.js.map +1 -1
  33. package/dist/lib/locale.d.ts +9 -0
  34. package/dist/lib/locale.d.ts.map +1 -0
  35. package/dist/lib/locale.js +32 -0
  36. package/dist/lib/locale.js.map +1 -0
  37. package/dist/lib/opencode.d.ts +31 -1
  38. package/dist/lib/opencode.d.ts.map +1 -1
  39. package/dist/lib/opencode.js +83 -1
  40. package/dist/lib/opencode.js.map +1 -1
  41. package/dist/lib/server.d.ts +3 -0
  42. package/dist/lib/server.d.ts.map +1 -1
  43. package/dist/lib/server.js +79 -36
  44. package/dist/lib/server.js.map +1 -1
  45. package/dist/lib/templates.d.ts +7 -0
  46. package/dist/lib/templates.d.ts.map +1 -0
  47. package/dist/lib/templates.js +30 -0
  48. package/dist/lib/templates.js.map +1 -0
  49. package/docs/02-convention.md +10 -5
  50. package/docs/03-cli.md +59 -14
  51. package/docs/04-agents.md +27 -3
  52. package/package.json +9 -19
  53. package/templates/{AGENTS.md → en/AGENTS.md} +8 -3
  54. package/templates/ja/AGENTS.md +38 -0
  55. package/templates/ja/room/BEHAVIOUR.md +18 -0
  56. package/templates/ja/room/BRIEF.md +16 -0
  57. package/templates/ja/room/SPEC.md +9 -0
  58. package/templates/ja/room/index.html +53 -0
  59. package/templates/modelroom.json +3 -1
  60. package/ui/assets/app.js +120 -47
  61. package/ui/dashboard.html +11 -7
  62. package/ui/kit.html +13 -9
  63. package/ui/modelroom.css +1 -2
  64. package/ui/room.html +11 -7
  65. package/ui/settings.html +11 -7
  66. /package/templates/{room → en/room}/BEHAVIOUR.md +0 -0
  67. /package/templates/{room → en/room}/BRIEF.md +0 -0
  68. /package/templates/{room → en/room}/SPEC.md +0 -0
  69. /package/templates/{room → en/room}/index.html +0 -0
package/docs/04-agents.md CHANGED
@@ -38,11 +38,26 @@ Two things are worth knowing, both learned the hard way:
38
38
  environment, which is what makes provider keys visible to it. If your shared service already
39
39
  has keys, either setting works.
40
40
 
41
+ ### What the chat shows
42
+
43
+ modelroom runs the agent, but it does not read the *turn* out of the CLI's prose. While the
44
+ process is alive, opencode's session already describes that turn — the text, the reasoning, one
45
+ entry per tool call, and the patch an edit wrote — and modelroom follows that instead. So a tool
46
+ call is a row you can open, an edit shows its diff, reasoning folds away, and the CLI's own
47
+ output stays in the run log. It is the same session the panel reads, asked about once a second.
48
+
49
+ If the session cannot be reached, or the adapter has no such place to look, the chat is exactly
50
+ what the process printed. That is the `command` adapter, and it is why `structured` exists.
51
+
52
+ The agent runs with its working directory set to `cfg.rootDir` — for a new
53
+ install, `modelroom/`. That is where it reads `AGENTS.md`, and it is the whole
54
+ tree modelroom will serve under `/files/...`.
55
+
41
56
  Agents that need to *write* only the room's files should be scoped on their side, for example
42
57
  with OpenCode `permissions`:
43
58
 
44
59
  ```yaml
45
- # .opencode/agents/design.md
60
+ # modelroom/.opencode/agents/design.md
46
61
  permissions:
47
62
  - { action: "*", resource: "*", effect: deny }
48
63
  - { action: read, resource: "rooms/*", effect: allow }
@@ -63,7 +78,11 @@ For anything else — Claude Code, Codex, a local model, a shell script:
63
78
  ```
64
79
 
65
80
  `{session}` is substituted with the room's session id. Sessions are stored per room in
66
- `.modelroom/state.json`; **new session** in the room UI starts a fresh one.
81
+ `.modelroom/state.json` at the project root; **new session** in the room UI starts a fresh one.
82
+
83
+ The generated `AGENTS.md` tells the agent which language to write the room's prose in, and
84
+ that frontmatter keys and promise ids stay in English. Change `lang` in `modelroom.json`
85
+ to change that language for new rooms and for the UI; existing documents are not rewritten.
67
86
 
68
87
  ## Writing an adapter
69
88
 
@@ -72,6 +91,7 @@ An adapter is one object:
72
91
  ```js
73
92
  export const mine = {
74
93
  name: 'mine',
94
+ structured: false, // true only if the turn can be read while it still runs
75
95
  newSessionId: () => `ses_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`,
76
96
  run({ cfg, cwd, prompt, sessionId, onChunk, onExit }) {
77
97
  // spawn your CLI; stream output with onChunk('stdout'|'stderr', text)
@@ -80,9 +100,13 @@ export const mine = {
80
100
  };
81
101
  ```
82
102
 
83
- Add it to `lib/agents/index.mjs`. Two rules keep adapters honest:
103
+ Add it to `lib/agents/index.ts`. Three rules keep adapters honest:
84
104
 
85
105
  1. **Close stdin** (`stdio: ['ignore', 'pipe', 'pipe']`). A CLI that reads stdin will hang
86
106
  forever if the pipe stays open.
87
107
  2. **Do not parse the output into meaning.** modelroom shows the agent's words as they are.
88
108
  Interpretation belongs to the person reading the room.
109
+ 3. **`structured: true` is a claim that the turn can be read while the run is in progress** —
110
+ that the agent keeps it somewhere modelroom can ask for it. Only one adapter makes that
111
+ claim today, and the reading lives in `lib/opencode.ts` (`sessionParts`). If the claim is
112
+ wrong, the agent's *prose* lands in the chat and nothing else breaks.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "modelroom",
3
- "version": "0.0.5",
3
+ "version": "0.1.0",
4
4
  "description": "A local workspace to design UI screens with any coding agent — one room per screen: BRIEF, SPEC, BEHAVIOUR and the artifact.",
5
5
  "keywords": [
6
6
  "agent-agnostic",
@@ -53,9 +53,9 @@
53
53
  "build": "npm run build:server && npm run build:web && npm run build:css",
54
54
  "build:server": "tsc -p tsconfig.build.json",
55
55
  "build:web": "node scripts/build-web.mjs",
56
- "build:css": "tailwindcss -i src/modelroom.css -o ui/modelroom.css --minify",
56
+ "build:css": "esbuild src/modelroom.css --bundle --minify --outfile=ui/modelroom.css --target=es2022",
57
57
  "dev:web": "node scripts/build-web.mjs --dev --watch",
58
- "dev:css": "tailwindcss -i src/modelroom.css -o ui/modelroom.css --watch",
58
+ "dev:css": "esbuild src/modelroom.css --bundle --outfile=ui/modelroom.css --watch --target=es2022",
59
59
  "lint": "oxlint",
60
60
  "lint:types": "oxlint --type-aware",
61
61
  "format": "oxfmt",
@@ -64,6 +64,7 @@
64
64
  "test": "vitest run --coverage",
65
65
  "test:watch": "vitest",
66
66
  "verify": "npm run lint && npm run lint:types && npm run format:check && npm run typecheck && npm test && npm run build",
67
+ "verify:ui": "node scripts/verify-ui.mjs",
67
68
  "prepublishOnly": "npm run build"
68
69
  },
69
70
  "devDependencies": {
@@ -75,19 +76,14 @@
75
76
  "@codemirror/state": "^6.7.6",
76
77
  "@codemirror/view": "^6.43.14",
77
78
  "@lezer/highlight": "^1.2.5",
78
- "@radix-ui/react-checkbox": "^1.4.1",
79
- "@radix-ui/react-separator": "^1.2.0",
80
- "@radix-ui/react-slot": "^1.5.0",
81
- "@radix-ui/react-tabs": "^1.2.1",
82
- "@radix-ui/react-toggle-group": "^1.2.1",
83
- "@tailwindcss/cli": "^4.3.3",
84
- "@tailwindcss/typography": "^0.5.20",
79
+ "@mantine/core": "^9.7.3",
80
+ "@mantine/hooks": "^9.7.3",
81
+ "@tanstack/react-virtual": "^3.14.21",
85
82
  "@types/node": "^26.6.5",
86
83
  "@types/react": "^19.3.0",
87
84
  "@types/react-dom": "^19.3.0",
88
85
  "@vitest/coverage-v8": "^5.0.5",
89
- "class-variance-authority": "^0.7.1",
90
- "clsx": "^2.1.1",
86
+ "axe-core": "^4.14.0",
91
87
  "esbuild": "^0.28.2",
92
88
  "highlight.js": "^11.12.0",
93
89
  "lefthook": "^2.2.2",
@@ -101,16 +97,10 @@
101
97
  "react": "^19.3.1",
102
98
  "react-dom": "^19.3.1",
103
99
  "react-resizable-panels": "^3.0.6",
104
- "tailwind-merge": "^3.7.0",
105
- "tailwindcss": "^4.3.3",
106
100
  "typescript": "^7.0.2",
107
101
  "vitest": "^5.0.5"
108
102
  },
109
- "overrides": {
110
- "postcss-selector-parser": "^7.1.6"
111
- },
112
103
  "engines": {
113
104
  "node": ">=22.18"
114
- },
115
- "//": "postcss-selector-parser is overridden to 7.x: @tailwindcss/typography pins ^6, where GHSA-rj75-hqrm-r3gf lives with no 6.x fix. It is a build-time parser, and `npm run build:css` checks the result."
105
+ }
116
106
  }
@@ -3,6 +3,8 @@
3
3
  This repository uses [modelroom](https://github.com/modelroom/modelroom): one room per screen,
4
4
  with the reason, the invisible spec, the promises and the artifact kept apart.
5
5
 
6
+ You are working inside the design layer (`modelroom/`). Write the room's prose in English.
7
+
6
8
  ## Where you may write
7
9
 
8
10
  - `rooms/<room>/BRIEF.md` — why this room exists
@@ -10,7 +12,8 @@ with the reason, the invisible spec, the promises and the artifact kept apart.
10
12
  - `rooms/<room>/BEHAVIOUR.md` — what it promises (operations and results)
11
13
  - `rooms/<room>/index.html` — the artifact itself
12
14
 
13
- Nothing else. Do not touch the application code from here; the design layer never reads it.
15
+ Nothing else. Do not reach outside this directory; the design layer never reads the
16
+ application code. The application is handed this layer, not the other way round.
14
17
 
15
18
  ## How to work
16
19
 
@@ -25,7 +28,9 @@ Nothing else. Do not touch the application code from here; the design layer neve
25
28
 
26
29
  ## Conventions
27
30
 
28
- - Add `Status: draft|review|adopted` to `BRIEF.md` when you want the dashboard to show a state.
29
- Changing it to `adopted` is a human decision, not yours.
31
+ - Frontmatter keys (`title`, `status`, `summary`, `tags`) and promise ids (`@verb-1`) stay
32
+ in English, whatever language the prose uses.
33
+ - Add `status: draft|review|adopted` to `BRIEF.md` when you want the dashboard to show a
34
+ state. Changing it to `adopted` is a human decision, not yours.
30
35
 
31
36
  See `docs/02-convention.md` in the modelroom package for the reasoning.
@@ -0,0 +1,38 @@
1
+ # AGENTS.md — デザインレイヤー
2
+
3
+ このリポジトリは [modelroom](https://github.com/modelroom/modelroom) を使っています。
4
+ 1画面につき1つの room を作り、理由・見えない仕様・約束・artifact を分けて保ちます。
5
+
6
+ あなたはデザインレイヤー(`modelroom/`)の中で作業しています。room の文章は日本語で書いてください。
7
+
8
+ ## 書き込んでよい場所
9
+
10
+ - `rooms/<room>/BRIEF.md` — この room が存在する理由
11
+ - `rooms/<room>/SPEC.md` — artifact では見えないこと(デバイス、状態、制約、使ってよいもの)
12
+ - `rooms/<room>/BEHAVIOUR.md` — 何を約束するか(操作と結果)
13
+ - `rooms/<room>/index.html` — artifact そのもの
14
+
15
+ それ以外は書き込まないでください。このディレクトリの外には出ないでください。
16
+ デザインレイヤーはアプリケーションのコードを読みません。アプリケーションに渡すのは
17
+ このレイヤーであり、その逆ではありません。
18
+
19
+ ## 進め方
20
+
21
+ 1. まず room の `BRIEF.md` を読み、次に `SPEC.md` と `BEHAVIOUR.md` を読みます。
22
+ 2. それらに合うように artifact を変えます。仕様が間違っているなら、黙って書き換えず、
23
+ 間違っていると伝えてください。
24
+ 3. artifact は自己完結にします。CDN、Webフォント、フレームワーク、ビルド手順は使いません。
25
+ 実装に渡すものなので、単体で成立する必要があります。
26
+ 4. `SPEC.md` に、artifact を見れば分かることを書き直さないでください。同じ約束を2つの
27
+ ファイルにコピーせず、id(`@verb-1`)で参照します。
28
+ 5. 実際のシステムが必要とする約束(レコード、タイムスタンプ、権限)は、このレイヤーには
29
+ 置きません。
30
+
31
+ ## 決まりごと
32
+
33
+ - frontmatter のキー(`title`、`status`、`summary`、`tags`)と約束の id(`@verb-1`)は、
34
+ 文章がどの言語でも英語のままにします。
35
+ - ダッシュボードに状態を出したいときは、`BRIEF.md` に `status: draft|review|adopted` を
36
+ 足します。`adopted` に変えるのは人間の判断で、あなたの判断ではありません。
37
+
38
+ 理由は modelroom パッケージの `docs/02-convention.md` にあります。
@@ -0,0 +1,18 @@
1
+ # {{room}} — 振る舞い
2
+
3
+ この room が約束すること: 操作と結果。コードではなく、仕事の言葉で書きます。
4
+
5
+ ```gherkin
6
+ 機能: {{room}}
7
+
8
+ @first-1
9
+ シナリオ: 最初の約束
10
+ 前提 何かの開始点
11
+ もし 人が何かをする
12
+ ならば 観測できることが起きる
13
+ ```
14
+
15
+ メモ
16
+ - 約束ごとに1つの id(`@<verb>-<n>`)を付け、1つのシナリオに1つの約束を書きます。
17
+ - 実際のシステムが必要とする約束(レコード、タイムスタンプ、権限)は、このファイルではなく
18
+ アプリケーションに属します。
@@ -0,0 +1,16 @@
1
+ ---
2
+ title: {{room}}
3
+ status: draft
4
+ version: 1
5
+ summary: 次に読む人が判断できる一言。
6
+ tags: []
7
+ ---
8
+ # {{room}}
9
+
10
+ この room はなぜ存在するのか。誰のためで、何が成り立っていれば作る価値があるのか。
11
+ 優先順位と、あえてやらないことを書いてください。
12
+
13
+ - **誰のため:**
14
+ - **なぜ:**
15
+ - **いちばん大事なこと:**
16
+ - **やらないこと:**
@@ -0,0 +1,9 @@
1
+ # {{room}} — 仕様
2
+
3
+ artifact では見えないことだけを書きます。スクリーンショットで見えることは artifact に
4
+ 属し、ここには書きません。
5
+
6
+ - **使われる場所:**(デバイス、入力、環境)
7
+ - **状態:**(例: 既定、空、エラー — それぞれどのファイルか)
8
+ - **制約:**(レイアウトに関係なく守るべきこと)
9
+ - **使ってよいもの:**(この room が使える共有コンポーネントとトークン)
@@ -0,0 +1,53 @@
1
+ <!doctype html>
2
+ <!-- {{room}} — artifact(実装に渡すもの)
3
+ 意図的に自己完結: CDN なし、フレームワークなし、ビルドなし。 -->
4
+ <html lang="ja">
5
+ <head>
6
+ <meta charset="utf-8">
7
+ <meta name="viewport" content="width=device-width, initial-scale=1">
8
+ <title>{{room}}</title>
9
+ <style>
10
+ :root {
11
+ --bg: #faf9f7;
12
+ --surface: #ffffff;
13
+ --text: #1c1917;
14
+ --muted: #78716c;
15
+ --line: #e7e5e4;
16
+ --primary: #1c1917;
17
+ --primary-text: #fafaf9;
18
+ --radius: 16px;
19
+ --space: 24px;
20
+ }
21
+ * { box-sizing: border-box; }
22
+ html, body { height: 100%; }
23
+ body {
24
+ margin: 0; display: grid; place-items: center; padding: 40px 20px; background: var(--bg); color: var(--text);
25
+ font-family: "Hiragino Kaku Gothic ProN", "Hiragino Sans", "Noto Sans JP", system-ui, sans-serif;
26
+ line-height: 1.7;
27
+ }
28
+ .card {
29
+ width: min(520px, 100%); background: var(--surface); border: 1px solid var(--line);
30
+ border-radius: var(--radius); padding: var(--space); display: flex; flex-direction: column; gap: var(--space);
31
+ }
32
+ h1 { font-size: 22px; margin: 0; letter-spacing: .01em; }
33
+ p { margin: 0; color: var(--muted); font-size: 15px; }
34
+ .actions { display: flex; gap: 12px; flex-wrap: wrap; }
35
+ button {
36
+ font: inherit; padding: 14px 22px; border-radius: var(--radius); border: 1px solid var(--line);
37
+ background: var(--surface); color: var(--text); cursor: pointer;
38
+ }
39
+ button.primary { background: var(--primary); color: var(--primary-text); border-color: var(--primary); }
40
+ </style>
41
+ </head>
42
+ <body>
43
+ <main class="card">
44
+ <h1>{{room}}</h1>
45
+ <p>これは room の artifact です。チャットから置き換えるか、手で書いてください。
46
+ 仕様と約束は隣の SPEC.md と BEHAVIOUR.md にあります。</p>
47
+ <div class="actions">
48
+ <button class="primary">主なアクション</button>
49
+ <button>副次的なアクション</button>
50
+ </div>
51
+ </main>
52
+ </body>
53
+ </html>
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "root": ".",
3
- "rooms": "rooms",
3
+ "rooms": "{{rooms}}",
4
+ "state": "{{state}}",
5
+ "lang": "{{lang}}",
4
6
  "artifact": "index.html",
5
7
  "docs": ["BRIEF.md", "SPEC.md", "BEHAVIOUR.md"],
6
8
  "port": 4321,