mulmoterminal 6.6.0 → 6.7.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 (122) hide show
  1. package/README.md +2 -2
  2. package/blueprints/ask/hearing.json +5 -2
  3. package/blueprints/polish/hearing.json +3 -1
  4. package/blueprints/review/hearing.json +3 -1
  5. package/blueprints/style/hearing.json +3 -1
  6. package/blueprints/style/manifest.json +5 -1
  7. package/blueprints/verify/hearing.json +3 -1
  8. package/blueprints/write/hearing.json +9 -1
  9. package/blueprints/write/manifest.json +2 -1
  10. package/common/blueprint/hearing.ts +6 -0
  11. package/common/blueprint/manifest.ts +6 -0
  12. package/common/blueprint/refusal.ts +59 -1
  13. package/common/keymap.ts +8 -0
  14. package/dist/assets/{abnfDiagram-O67JEVCF-BypT3U3a.js → abnfDiagram-O67JEVCF-COztuCu2.js} +1 -1
  15. package/dist/assets/architecture-7GRP2DOG-DSPxsQOu.js +1 -0
  16. package/dist/assets/{architectureDiagram-NJMV4G6O-CyObEvXl.js → architectureDiagram-NJMV4G6O-CocxKb8D.js} +1 -1
  17. package/dist/assets/{blockDiagram-BEXU5L5S-D-HVBeq1.js → blockDiagram-BEXU5L5S-CoIOnOr8.js} +1 -1
  18. package/dist/assets/{c4Diagram-YGBWAQC7-Qgd--us_.js → c4Diagram-YGBWAQC7-DCkPDN1m.js} +1 -1
  19. package/dist/assets/channel-j_IknEot.js +1 -0
  20. package/dist/assets/{chunk-3FUC2YCW-BR-5aCbb.js → chunk-3FUC2YCW-BpkN5ntL.js} +1 -1
  21. package/dist/assets/{chunk-5DYCD2WN-Bo8hecoJ.js → chunk-5DYCD2WN-CkSUlK9I.js} +1 -1
  22. package/dist/assets/{chunk-742MDFTN-CD8LfZUq.js → chunk-742MDFTN-IT8yQzYr.js} +1 -1
  23. package/dist/assets/{chunk-7INBJB4K-CX2KLcpU.js → chunk-7INBJB4K-JGgoF_Kc.js} +1 -1
  24. package/dist/assets/{chunk-7M6MHVWA-EAATiixY.js → chunk-7M6MHVWA-DeMh7ryC.js} +1 -1
  25. package/dist/assets/{chunk-7PRAP22T-kBBJ0KJk.js → chunk-7PRAP22T-Mu7uMyeb.js} +1 -1
  26. package/dist/assets/{chunk-GTNCS2PH-BMtqOq18.js → chunk-GTNCS2PH-CZnXYcEC.js} +1 -1
  27. package/dist/assets/{chunk-GWA4HPMP-B0Mi72WR.js → chunk-GWA4HPMP-hNVTjL0n.js} +1 -1
  28. package/dist/assets/{chunk-MBY4JIJT-DmHkAdrQ.js → chunk-MBY4JIJT-BNBCCYaO.js} +1 -1
  29. package/dist/assets/{chunk-NETBCI7D-CXGvJJei.js → chunk-NETBCI7D-BFEY_LEK.js} +1 -1
  30. package/dist/assets/{chunk-O7XYJQB3-WBMmFQya.js → chunk-O7XYJQB3-igihGd57.js} +1 -1
  31. package/dist/assets/{chunk-UA2S7LBM-DaQ23UEn.js → chunk-UA2S7LBM-AvoPHYX2.js} +1 -1
  32. package/dist/assets/{chunk-WEXAMYUT-DviQkb8I.js → chunk-WEXAMYUT-CnzHksV_.js} +1 -1
  33. package/dist/assets/{chunk-Z7XXMR3K-DOtBujlQ.js → chunk-Z7XXMR3K-BZUbrMkZ.js} +1 -1
  34. package/dist/assets/{chunk-ZIGJFQKS-VtmgG3xL.js → chunk-ZIGJFQKS-DFj3hrIY.js} +1 -1
  35. package/dist/assets/{classDiagram-v2-NBCMYWYE-ndrP6enZ.js → classDiagram-v2-NBCMYWYE-C2GePjwg.js} +1 -1
  36. package/dist/assets/{cynefin-OW5HDTMX-5iOYoFuE.js → cynefin-OW5HDTMX-E4NRCqvt.js} +1 -1
  37. package/dist/assets/{cynefinDiagram-VND7K2PF-DWaNHdmF.js → cynefinDiagram-VND7K2PF-BnHou4hN.js} +1 -1
  38. package/dist/assets/{dagre-6A5THRUB-CRI-Ki8R.js → dagre-6A5THRUB-kSCwZxth.js} +1 -1
  39. package/dist/assets/{diagram-22UHCM2B-Cb4_8V3f.js → diagram-22UHCM2B-DT04U1z3.js} +1 -1
  40. package/dist/assets/{diagram-3UASUU5V--mTYdfxz.js → diagram-3UASUU5V-QaR0jD4H.js} +1 -1
  41. package/dist/assets/{diagram-ATOU4E4O-BwY4-yUt.js → diagram-ATOU4E4O-lmm7fQMw.js} +1 -1
  42. package/dist/assets/{diagram-CDSNMT55-CW8ENZKa.js → diagram-CDSNMT55-CdsN9JC6.js} +1 -1
  43. package/dist/assets/{diagram-MLGK6HIB-Ew0oj4NU.js → diagram-MLGK6HIB-CaVs00rU.js} +1 -1
  44. package/dist/assets/{diagram-MPIPVDR6-BK0uRGQB.js → diagram-MPIPVDR6-BudW9uT0.js} +1 -1
  45. package/dist/assets/{dist-Cp7rDLig.js → dist-4u5CgGvM.js} +1 -1
  46. package/dist/assets/{dist-CNs34037.js → dist-BkNFiPKG.js} +1 -1
  47. package/dist/assets/{dist-DRZ3y7AE.js → dist-CBfB3Kbo.js} +1 -1
  48. package/dist/assets/{dist-jO1cPpPw.js → dist-DJ_QvH_t.js} +1 -1
  49. package/dist/assets/{ebnfDiagram-ZINNZB2B-DGu-I1_-.js → ebnfDiagram-ZINNZB2B-DT9rFfx-.js} +1 -1
  50. package/dist/assets/{elk-276RUBZZ-IKdDujmV.js → elk-276RUBZZ-DDeDnR5i.js} +1 -1
  51. package/dist/assets/{erDiagram-OPXOYQCR-CLcXgJdC.js → erDiagram-OPXOYQCR-B9Fv1DKw.js} +1 -1
  52. package/dist/assets/eventmodeling-NTZA5JFV-BnIWMpmq.js +1 -0
  53. package/dist/assets/flowDiagram-KWPJA3E3-CgKOYFOK.js +1 -0
  54. package/dist/assets/{ganttDiagram-FUAMR5RP-lfct59GT.js → ganttDiagram-FUAMR5RP-CzlCs_Rv.js} +1 -1
  55. package/dist/assets/{gitGraph-4MIJSDKK-Hr_vAl58.js → gitGraph-4MIJSDKK-DMlpm2X5.js} +1 -1
  56. package/dist/assets/{gitGraphDiagram-X574FWY7-DyTAObOG.js → gitGraphDiagram-X574FWY7-BodBMwuj.js} +1 -1
  57. package/dist/assets/{index-BfCtnfM3.js → index-DtoMXmGR.js} +334 -332
  58. package/dist/assets/index-XRjuwB8c.css +1 -0
  59. package/dist/assets/{info-A6RAGUB7-C2RIrvn7.js → info-A6RAGUB7-DcD8IyFk.js} +1 -1
  60. package/dist/assets/{infoDiagram-VRGFBTTK-Cehr7nT0.js → infoDiagram-VRGFBTTK-BaZCI3bY.js} +1 -1
  61. package/dist/assets/{ishikawaDiagram-OU5B5YK6-DNo8VfZy.js → ishikawaDiagram-OU5B5YK6-C2YA2vvC.js} +1 -1
  62. package/dist/assets/{journeyDiagram-ZHPQQLJL-BjQHzZxG.js → journeyDiagram-ZHPQQLJL-CqjWX8sS.js} +1 -1
  63. package/dist/assets/{kanban-definition-PNTS6WVX-CSr7kyll.js → kanban-definition-PNTS6WVX-CoM8jlm5.js} +1 -1
  64. package/dist/assets/{line-CmNgViQc.js → line-Bt2FCpGX.js} +1 -1
  65. package/dist/assets/{mermaid-parser.core-C_qcrA5s.js → mermaid-parser.core-D9qR3oRl.js} +3 -3
  66. package/dist/assets/{mermaid.core-BG82qqEB.js → mermaid.core-BIXntNJ_.js} +4 -4
  67. package/dist/assets/{mindmap-definition-NLK3R4M7-psSKkeLb.js → mindmap-definition-NLK3R4M7-4FYsrukx.js} +1 -1
  68. package/dist/assets/{packet-AYTQ26CC-BS5zpaST.js → packet-AYTQ26CC-eQcU7oGW.js} +1 -1
  69. package/dist/assets/{pegDiagram-GJSIUBJH-DhpL7ygV.js → pegDiagram-GJSIUBJH-AwYWp-ta.js} +1 -1
  70. package/dist/assets/{pie-WAS4IAKB-T5-dog1P.js → pie-WAS4IAKB-CJF4n-uR.js} +1 -1
  71. package/dist/assets/{pieDiagram-5QR66LMP-CmXcDzDT.js → pieDiagram-5QR66LMP-CKnVXfdJ.js} +1 -1
  72. package/dist/assets/{quadrantDiagram-O4NWA36T-BIDYLhBi.js → quadrantDiagram-O4NWA36T-BefBP8Qf.js} +1 -1
  73. package/dist/assets/{radar-RG4KPBEZ-_895RNt8.js → radar-RG4KPBEZ-BjQl0TqH.js} +1 -1
  74. package/dist/assets/{railroad-74A4TZTK-Cz2ZyH7U.js → railroad-74A4TZTK-Z4kUresc.js} +1 -1
  75. package/dist/assets/railroad-abnf-HS5TGJTU-DUcNrXOB.js +1 -0
  76. package/dist/assets/railroad-ebnf-LZEXJU2U-CiLdyg9w.js +1 -0
  77. package/dist/assets/railroad-peg-WCYAUIDC-k26xztEI.js +1 -0
  78. package/dist/assets/{railroadDiagram-XR7U4H2S-BQ1Mc95v.js → railroadDiagram-XR7U4H2S-BUskllkB.js} +1 -1
  79. package/dist/assets/{requirementDiagram-PLB6GJNP-ChVue_nk.js → requirementDiagram-PLB6GJNP-BCbsSEYH.js} +1 -1
  80. package/dist/assets/{sankeyDiagram-IPEJSGJF-Cd9K0txb.js → sankeyDiagram-IPEJSGJF-DuFOhh5F.js} +1 -1
  81. package/dist/assets/{sequenceDiagram-PO4LG4MO-kiPuazKP.js → sequenceDiagram-PO4LG4MO-CtacIk91.js} +1 -1
  82. package/dist/assets/{stateDiagram-v2-GCMORJYK-BrYqOUDC.js → stateDiagram-v2-GCMORJYK-Bh0PfQR6.js} +1 -1
  83. package/dist/assets/{swimlanes-2SLR337P-BGv93v9F.js → swimlanes-2SLR337P-3VWib0yw.js} +1 -1
  84. package/dist/assets/swimlanesDiagram-TC7HE7FX-DZwaV5aw.js +8 -0
  85. package/dist/assets/{timeline-definition-EJHVYXUP-B2m2999q.js → timeline-definition-EJHVYXUP-DLUjmMJp.js} +1 -1
  86. package/dist/assets/{treeView-Q6P3EWNA-Bp2HlEG5.js → treeView-Q6P3EWNA-urctZJZ8.js} +1 -1
  87. package/dist/assets/{treemap-WGGIJYW6-BBhRxIiH.js → treemap-WGGIJYW6-7VaIVFkb.js} +1 -1
  88. package/dist/assets/{usecaseDiagram-POWQR4AR-uFIx7ToF.js → usecaseDiagram-POWQR4AR-OlI3xHtd.js} +1 -1
  89. package/dist/assets/{vennDiagram-UO4OBE2U-BQGzVBTB.js → vennDiagram-UO4OBE2U-ABZr8NaN.js} +1 -1
  90. package/dist/assets/{wardley-WFR3VGLG-BQk6DDg6.js → wardley-WFR3VGLG-CX3iRocH.js} +1 -1
  91. package/dist/assets/{wardleyDiagram-VNRHLVJA-q-_GY-qU.js → wardleyDiagram-VNRHLVJA-CmHM0PTe.js} +1 -1
  92. package/dist/assets/{xychartDiagram-PMCCYNJV-B8D1QFx-.js → xychartDiagram-PMCCYNJV-CYUP0J_R.js} +1 -1
  93. package/dist/index.html +2 -2
  94. package/package.json +1 -1
  95. package/server/agents/claude-help-probe.ts +73 -0
  96. package/server/agents/claude-permission-modes.ts +45 -0
  97. package/server/agents/rate-limit-probe.spec.ts +19 -0
  98. package/server/agents/rate-limit-probe.ts +6 -6
  99. package/server/blueprint/executor.ts +14 -13
  100. package/server/blueprint/folderSuggestion.ts +31 -0
  101. package/server/blueprint/installer.ts +12 -10
  102. package/server/blueprint/marketRoutes.ts +12 -7
  103. package/server/blueprint/newFolder.ts +37 -0
  104. package/server/blueprint/refused.ts +16 -0
  105. package/server/blueprint/routes.ts +80 -26
  106. package/server/blueprint/wiring.ts +4 -1
  107. package/server/routes/ws-routes.ts +5 -7
  108. package/server/session/lifecycle.ts +15 -8
  109. package/server/session/pty-kill-plan.ts +10 -0
  110. package/server/session/pty-kill.ts +106 -0
  111. package/server/session/pty-spawn.ts +24 -2
  112. package/server/session/spawn-claude.ts +7 -2
  113. package/server/skills/mulmoterminal-keys/SKILL.md +1 -0
  114. package/dist/assets/architecture-7GRP2DOG-Bbv-2KeL.js +0 -1
  115. package/dist/assets/channel-BHcWpvnw.js +0 -1
  116. package/dist/assets/eventmodeling-NTZA5JFV-9UzRuTrz.js +0 -1
  117. package/dist/assets/flowDiagram-KWPJA3E3-Bq9iQFMs.js +0 -1
  118. package/dist/assets/index-BHeY7dDr.css +0 -1
  119. package/dist/assets/railroad-abnf-HS5TGJTU-CTBfwbDI.js +0 -1
  120. package/dist/assets/railroad-ebnf-LZEXJU2U-BxMnP09p.js +0 -1
  121. package/dist/assets/railroad-peg-WCYAUIDC-VQLBtQ7q.js +0 -1
  122. package/dist/assets/swimlanesDiagram-TC7HE7FX-XHEHPbk9.js +0 -8
package/README.md CHANGED
@@ -704,7 +704,7 @@ the `claude` / `codex` sessions themselves.
704
704
  | `CLIENT_PORT` | `6856` | Vite dev-server port (dev only: the URL you open with `yarn dev`). |
705
705
  | `CLAUDE_BIN` | `claude` | The Claude Code binary to spawn. On Windows a bare name is resolved on `PATH` before it reaches the PTY layer (which matches file names exactly): to the `.exe` when there is one, otherwise to the `.cmd` shim an npm-global install leaves, run through `cmd.exe`. |
706
706
  | `CLAUDE_CWD` | current dir | Working directory each `claude` PTY runs in; determines which project's sessions are listed. Via `npx mulmoterminal@latest` it defaults to the directory you ran the command from (override with `--cwd <dir>`, relative allowed); when the server is run directly it falls back to `~/mulmoclaude`. A value read from `.env` must be an absolute path (`~` is not expanded). Running in one of your own project directories does **not** litter it — see the note under the table. |
707
- | `CLAUDE_PERMISSION_MODE` | `auto` | Permission mode passed to each `claude` spawn. |
707
+ | `CLAUDE_PERMISSION_MODE` | `auto` | Permission mode passed to each `claude` spawn. A Claude Code whose `--help` does not list the mode is not started: the cell says so, and for `auto` says to update Claude Code. |
708
708
  | `MT_TITLE_SOURCE` | `transcript` | Where the cell header's AI title comes from. `transcript` reads the title Claude Code writes into its own transcript — no extra process. `headless` restores the old behaviour of summarizing the recent turns with `claude -p`, which costs a model call but follows a session whose topic drifts (Claude's own title is written once and never revised). |
709
709
  | `MT_TITLE_MODEL` | `haiku` | Model used for the cell header's AI title. Only read when `MT_TITLE_SOURCE=headless`. Accepts a `--model` alias or a full model id. |
710
710
  | `CODEX_BIN` | `codex` | The Codex CLI binary to spawn. |
@@ -1687,7 +1687,7 @@ narrow and tall for one record you are discussing — and each position keeps it
1687
1687
 
1688
1688
  ![Zoom — one agent enlarged, the others as a filmstrip along the bottom](https://raw.githubusercontent.com/receptron/mulmoterminal/main/docs/guide/images/grid-zoom.png)
1689
1689
 
1690
- - **Set a terminal aside** — the moon button in a cell's header **sinks** it: the tile, its
1690
+ - **Set a terminal aside** — the bed button (a person asleep) in a cell's header **sinks** it: the tile, its
1691
1691
  filmstrip thumbnail and its cockpit-roster row all fade, and the working dot stops pulsing.
1692
1692
  The session stays **connected and keeps its whole history** — this is what to reach for
1693
1693
  instead of `/clear`-ing a cell you are done with for now, which resets the conversation just
@@ -4,13 +4,16 @@
4
4
  "id": "documents",
5
5
  "label": "質問する相手の文書(このフォルダの中のファイルを 1 行に 1 つ)",
6
6
  "why": "答えはこの文書だけから出し、どこに書いてあるかを示す",
7
- "kind": "text"
7
+ "kind": "text",
8
+ "lines": true,
9
+ "pick": "files"
8
10
  },
9
11
  {
10
12
  "id": "questions",
11
13
  "label": "質問(1 行に 1 つ)",
12
14
  "why": "質問ごとに一つ答える",
13
- "kind": "text"
15
+ "kind": "text",
16
+ "lines": true
14
17
  },
15
18
  {
16
19
  "id": "keep",
@@ -4,7 +4,9 @@
4
4
  "id": "targets",
5
5
  "label": "整えたい文書(ファイル・フォルダを 1 行に 1 つ。このフォルダの中の場所で)",
6
6
  "why": "候補はこの中から選ぶ",
7
- "kind": "text"
7
+ "kind": "text",
8
+ "lines": true,
9
+ "pick": "files"
8
10
  },
9
11
  {
10
12
  "id": "style",
@@ -4,7 +4,9 @@
4
4
  "id": "documents",
5
5
  "label": "読み解く文書(このフォルダの中のファイルを 1 行に 1 つ)",
6
6
  "why": "指摘と引用はこの文書に対して行う",
7
- "kind": "text"
7
+ "kind": "text",
8
+ "lines": true,
9
+ "pick": "files"
8
10
  },
9
11
  {
10
12
  "id": "kind",
@@ -4,7 +4,9 @@
4
4
  "id": "sources",
5
5
  "label": "手本にしたい文章(ファイル・フォルダ・URL を、1 行に 1 つ)",
6
6
  "why": "規約はここから測る。手本に無いことは規約にしない",
7
- "kind": "text"
7
+ "kind": "text",
8
+ "lines": true,
9
+ "pick": "files"
8
10
  },
9
11
  {
10
12
  "id": "kind",
@@ -5,5 +5,9 @@
5
5
  "version": "0.1.0",
6
6
  "description": "手本にしたい文章やページから「うちの文章はこう書く」を取り出し、機械が毎回確かめる決まり(chaff.yaml)と、書くときに読む手引き(STYLE.md)にする。手本が自分の規約で指摘されないこと、わざと崩した文章には規約が効くことまで確かめる。",
7
7
  "bases": ["docs"],
8
- "report": ".blueprint/style-report.md"
8
+ "report": ".blueprint/style-report.md",
9
+ "next": [
10
+ { "usecase": "write", "answers": { "style": "このフォルダの規約(STYLE.md と chaff.yaml)" } },
11
+ { "usecase": "polish", "answers": { "style": "このフォルダの規約(STYLE.md と chaff.yaml)" } }
12
+ ]
9
13
  }
@@ -4,7 +4,9 @@
4
4
  "id": "documents",
5
5
  "label": "確かめる文書(このフォルダの中のファイルを 1 行に 1 つ)",
6
6
  "why": "日付や金額はこの文書から抜き出し、原文の引用で裏づける",
7
- "kind": "text"
7
+ "kind": "text",
8
+ "lines": true,
9
+ "pick": "files"
8
10
  },
9
11
  {
10
12
  "id": "kind",
@@ -9,12 +9,20 @@
9
9
  },
10
10
  { "id": "topic", "label": "何について書きますか", "why": "要旨と構成の出発点になる", "kind": "text" },
11
11
  { "id": "audience", "label": "誰が読み、読んだあと何ができるようになればよいですか", "why": "何を書き、何を省くかの基準になる", "kind": "text" },
12
- { "id": "points", "label": "必ず入れたいこと(1 行に 1 つ)", "why": "要旨の要点になり、書き終えたときに入っているかを確かめる", "kind": "text" },
12
+ {
13
+ "id": "points",
14
+ "label": "必ず入れたいこと(1 行に 1 つ)",
15
+ "why": "要旨の要点になり、書き終えたときに入っているかを確かめる",
16
+ "kind": "text",
17
+ "lines": true
18
+ },
13
19
  {
14
20
  "id": "sources",
15
21
  "label": "参照する資料(ファイル・フォルダ・URL を 1 行に 1 つ。無ければ空で)",
16
22
  "why": "事実はここから取り、取った文は引用として原文にあることを確かめる",
17
23
  "kind": "text",
24
+ "lines": true,
25
+ "pick": "files",
18
26
  "required": false
19
27
  },
20
28
  {
@@ -5,5 +5,6 @@
5
5
  "version": "0.1.0",
6
6
  "description": "記事・論文・規程・マニュアルから本一冊まで、頼まれた文書をフォルダの規約(STYLE.md と chaff.yaml)に沿って書く。要旨と構成を人が確かめてから、部分(章)を一つずつ書き、部分ごとに chaff で確かめる。資料から取った文は引用として残し、原文にあることを確かめる。",
7
7
  "bases": ["docs"],
8
- "report": ".blueprint/write-report.md"
8
+ "report": ".blueprint/write-report.md",
9
+ "next": [{ "usecase": "polish" }]
9
10
  }
@@ -12,6 +12,10 @@ const questionSchema = z.object({
12
12
  kind: z.enum(HEARING_KINDS),
13
13
  options: z.array(z.string().min(1)).optional(),
14
14
  required: z.boolean().default(true),
15
+ // A text answer that is a list, one item per line: the form gives it a multi-line field, which keeps the newlines.
16
+ lines: z.boolean().default(false),
17
+ // A one-per-line answer whose lines are files in the build's folder: the form offers them to pick from.
18
+ pick: z.literal("files").optional(),
15
19
  // Asked only when an earlier answer equals this value.
16
20
  showIf: z.object({ id: z.string(), equals: z.union([z.string(), z.boolean(), z.number()]) }).optional(),
17
21
  });
@@ -33,6 +37,8 @@ function questionProblems(question: HearingQuestion, earlier: ReadonlySet<string
33
37
  const problems: string[] = [];
34
38
  if (earlier.has(question.id)) problems.push(`duplicate question id "${question.id}"`);
35
39
  if (needsOptions(question) && !question.options?.length) problems.push(`"${question.id}" is a ${question.kind} with no options`);
40
+ if (question.lines && question.kind !== "text") problems.push(`"${question.id}" is a ${question.kind}, and only a text answer can be one per line`);
41
+ if (question.pick && !question.lines) problems.push(`"${question.id}" picks files but is not one per line`);
36
42
  if (question.showIf && !earlier.has(question.showIf.id)) problems.push(`"${question.id}" depends on "${question.showIf.id}", which is not asked before it`);
37
43
  return problems;
38
44
  }
@@ -2,6 +2,7 @@
2
2
  // a USECASE pack says what kind of system is built (internal tool, social …) and which bases it can
3
3
  // sit on. The two are composed at run time, so neither repeats the other.
4
4
  import { z } from "zod";
5
+ import { hearingAnswersSchema } from "./hearing.js";
5
6
 
6
7
  export const BLUEPRINT_SLUG_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;
7
8
 
@@ -32,12 +33,17 @@ export const baseManifestSchema = z.object({
32
33
  /** Where a usecase's report is written: a Markdown file directly under the build's own `.blueprint/` folder. */
33
34
  export const REPORT_PATH_RE = /^\.blueprint\/[A-Za-z0-9][A-Za-z0-9._-]*\.md$/;
34
35
 
36
+ /** A usecase a person may go on to in the same folder once this one finishes, with the answers it fills in. */
37
+ export const nextStepSchema = z.object({ usecase: slug, answers: hearingAnswersSchema.default({}) });
38
+ export type NextStep = z.infer<typeof nextStepSchema>;
39
+
35
40
  export const usecaseManifestSchema = z.object({
36
41
  ...manifestCommon,
37
42
  kind: z.literal("usecase"),
38
43
  bases: z.array(slug).min(1),
39
44
  /** The report the person reads when the build finishes; shown in the run view. */
40
45
  report: z.string().regex(REPORT_PATH_RE).optional(),
46
+ next: z.array(nextStepSchema).default([]),
41
47
  });
42
48
 
43
49
  export const blueprintManifestSchema = z.discriminatedUnion("kind", [baseManifestSchema, usecaseManifestSchema]);
@@ -1,10 +1,12 @@
1
- // Why the server would not start or move a build, as data: the UI words it in the person's language,
1
+ // Why the server would not start or move a build, or install a pack, as data: the UI words it in the person's language,
2
2
  // and the English sentence the API also returns is derived from the same object so the two cannot drift.
3
3
  import { z } from "zod";
4
4
 
5
5
  export const refusalSchema = z.discriminatedUnion("code", [
6
6
  z.object({ code: z.literal("not-absolute") }),
7
7
  z.object({ code: z.literal("not-a-directory"), dir: z.string() }),
8
+ z.object({ code: z.literal("no-parent"), dir: z.string() }),
9
+ z.object({ code: z.literal("folder-taken"), dir: z.string() }),
8
10
  z.object({ code: z.literal("untrusted"), dir: z.string() }),
9
11
  z.object({ code: z.literal("folder-busy"), dir: z.string(), runId: z.string() }),
10
12
  z.object({ code: z.literal("samples-clash"), files: z.array(z.string()).readonly() }),
@@ -13,17 +15,51 @@ export const refusalSchema = z.discriminatedUnion("code", [
13
15
  z.object({ code: z.literal("spec-not-at-review") }),
14
16
  z.object({ code: z.literal("message-pending") }),
15
17
  z.object({ code: z.literal("agent-working") }),
18
+ z.object({ code: z.literal("registry-url-not-allowed"), urls: z.array(z.string()).readonly() }),
19
+ z.object({ code: z.literal("registry-unknown"), url: z.string() }),
20
+ z.object({ code: z.literal("pack-not-listed"), url: z.string(), slug: z.string() }),
21
+ z.object({ code: z.literal("pack-busy"), slug: z.string() }),
22
+ z.object({ code: z.literal("pack-not-installed"), slug: z.string() }),
23
+ z.object({ code: z.literal("pack-builtin"), slug: z.string() }),
24
+ z.object({ code: z.literal("pack-local-repo"), repo: z.string() }),
25
+ // What is wrong with the pack, or with fetching it, stays technical English inside the worded sentence.
26
+ z.object({ code: z.literal("pack-broken"), detail: z.string() }),
27
+ z.object({ code: z.literal("install-failed"), detail: z.string() }),
16
28
  ]);
17
29
 
18
30
  export type Refusal = z.infer<typeof refusalSchema>;
19
31
  export type RefusalCode = Refusal["code"];
20
32
 
33
+ const MARKET_CODES = [
34
+ "registry-url-not-allowed",
35
+ "registry-unknown",
36
+ "pack-not-listed",
37
+ "pack-busy",
38
+ "pack-not-installed",
39
+ "pack-builtin",
40
+ "pack-local-repo",
41
+ "pack-broken",
42
+ "install-failed",
43
+ ] as const satisfies readonly RefusalCode[];
44
+
45
+ type MarketRefusal = Extract<Refusal, { code: (typeof MARKET_CODES)[number] }>;
46
+
47
+ const isMarketRefusal = (refusal: Refusal): refusal is MarketRefusal => MARKET_CODES.some((code) => code === refusal.code);
48
+
21
49
  export function englishRefusal(refusal: Refusal): string {
50
+ return isMarketRefusal(refusal) ? englishMarketRefusal(refusal) : englishBuildRefusal(refusal);
51
+ }
52
+
53
+ function englishBuildRefusal(refusal: Exclude<Refusal, MarketRefusal>): string {
22
54
  switch (refusal.code) {
23
55
  case "not-absolute":
24
56
  return "projectDir must be an absolute path below the root";
25
57
  case "not-a-directory":
26
58
  return `projectDir is not a directory: ${refusal.dir}`;
59
+ case "no-parent":
60
+ return `${refusal.dir} does not exist: a new folder is made only inside one that does`;
61
+ case "folder-taken":
62
+ return `${refusal.dir} was made by something else just now: start again, or choose another folder`;
27
63
  case "untrusted":
28
64
  return `Claude Code does not trust ${refusal.dir} yet. Open a terminal there once and accept the trust prompt, then try again.`;
29
65
  case "folder-busy":
@@ -42,3 +78,25 @@ export function englishRefusal(refusal: Refusal): string {
42
78
  return "an agent is working on the build";
43
79
  }
44
80
  }
81
+
82
+ function englishMarketRefusal(refusal: MarketRefusal): string {
83
+ switch (refusal.code) {
84
+ case "registry-url-not-allowed":
85
+ return `registry URLs must be https (or http on localhost): ${refusal.urls.join(", ")}`;
86
+ case "registry-unknown":
87
+ return `not a registry this machine reads: ${refusal.url}`;
88
+ case "pack-not-listed":
89
+ return `${refusal.url} lists no pack "${refusal.slug}"`;
90
+ case "pack-busy":
91
+ return `"${refusal.slug}" is already being installed or removed`;
92
+ case "pack-not-installed":
93
+ return `"${refusal.slug}" is not an installed pack`;
94
+ case "pack-builtin":
95
+ return `"${refusal.slug}" is a pack shipped with MulmoTerminal and cannot be replaced`;
96
+ case "pack-local-repo":
97
+ return `a registry on the web cannot install from this machine's disk (${refusal.repo})`;
98
+ case "pack-broken":
99
+ case "install-failed":
100
+ return refusal.detail;
101
+ }
102
+ }
package/common/keymap.ts CHANGED
@@ -25,6 +25,8 @@ export const KEYMAP_ACTIONS = [
25
25
  "terminal-new-adjacent",
26
26
  "terminal-close",
27
27
  "terminal-restart",
28
+ "terminal-move-prev",
29
+ "terminal-move-next",
28
30
  "files-find",
29
31
  "files-search",
30
32
  "command-palette",
@@ -80,6 +82,10 @@ export const NEEDS_A_CURRENT_TERMINAL: readonly KeymapAction[] = [
80
82
  // plans/feat-2106-focus-prev-next.md for why that is a change of its own.
81
83
  export const NEEDS_NOTHING_ENLARGED: readonly KeymapAction[] = ["focus-next", "focus-prev"];
82
84
 
85
+ // Actions that reorder the grid, which only manual order lets anyone do: in auto and priority order
86
+ // the grid decides the position, and a move would be undone on the next sort.
87
+ export const NEEDS_MANUAL_ORDER: readonly KeymapAction[] = ["terminal-move-prev", "terminal-move-next"];
88
+
83
89
  // A key that puts BYTES into the focused terminal instead of running an app action (#1005) —
84
90
  // Cmd+Right as Ctrl+E for end-of-line, say, or Alt+B for word-back.
85
91
  //
@@ -405,6 +411,7 @@ function duplicateWarnings(bound: Map<string, Claim[]>): KeymapProblem[] {
405
411
  // - `copy` with no selection (clipboardActionFor), deliberately, so Ctrl+C stays interrupt.
406
412
  // - NEEDS_A_CURRENT_TERMINAL with nothing enlarged (gridShortcutFor).
407
413
  // - NEEDS_NOTHING_ENLARGED with something enlarged (gridShortcutFor), the mirror of it.
414
+ // - NEEDS_MANUAL_ORDER in auto or priority order (gridShortcutFor).
408
415
  //
409
416
  // `acts` and `otherwise` are the two halves of what the user is told.
410
417
  interface StandsAside {
@@ -415,6 +422,7 @@ const WHILE_ENLARGED: StandsAside = { acts: "only while a terminal is enlarged",
415
422
  const WHILE_NOT_ENLARGED: StandsAside = { acts: "only while no terminal is enlarged", otherwise: "when one is" };
416
423
  const standsAside = (label: string): StandsAside | null => {
417
424
  if (label === "copy") return { acts: "only while text is selected", otherwise: "when nothing is" };
425
+ if (NEEDS_MANUAL_ORDER.some((action) => action === label)) return { acts: "only in manual order", otherwise: "in auto or priority order" };
418
426
  if (NEEDS_NOTHING_ENLARGED.some((action) => action === label)) return WHILE_NOT_ENLARGED;
419
427
  return NEEDS_A_CURRENT_TERMINAL.some((action) => action === label) ? WHILE_ENLARGED : null;
420
428
  };
@@ -1 +1 @@
1
- import{m as e,t}from"./mermaid-parser.core-C_qcrA5s.js";import{n}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import{m as r}from"./src-CllyRBe0.js";import"./chunk-O7XYJQB3-WBMmFQya.js";import{n as i,r as a,t as o}from"./chunk-GTNCS2PH-BMtqOq18.js";import{t as s}from"./chunk-JWPE2WC7-DVXcaiue.js";import"./mermaid.core-BG82qqEB.js";var c=e().RailroadAbnf.parser.LangiumParser,l=n(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=n(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=n(e=>{if(e.includes(`*`)){let[t,n]=e.split(`*`);return{min:t?parseInt(t,10):0,max:n?parseInt(n,10):1/0}}let t=parseInt(e,10);return{min:t,max:t}},`parseRepeat`),f=n(e=>{let t=p(e.primary);if(!e.repeat)return t;let{min:n,max:r}=d(e.repeat);return n===0&&r===1?{type:`optional`,element:t}:{type:`repetition`,element:t,min:n,max:r}},`transformElement`),p=n(e=>{switch(e.$type){case`AbnfStringLiteral`:return{type:`terminal`,value:e.value};case`AbnfNumVal`:return{type:`terminal`,value:e.value};case`AbnfRuleName`:return{type:`nonterminal`,name:e.name};case`AbnfGroup`:return l(e.element);case`AbnfOptionalGroup`:return{type:`optional`,element:l(e.element)};default:throw Error(`Unsupported ABNF primary node: ${e.$type}`)}},`transformPrimary`),m=n(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=n(e=>{s(e,o),e.title&&o.setTitle(e.title),e.rules.map(e=>o.addRule(m(e)))},`populateDb`),g={parser:{parse:n(e=>{o.clear(),r.debug(`[ABNF Parser] Starting Langium parse`);let n=c.parse(e);if(n.lexerErrors.length>0||n.parserErrors.length>0)throw new t(n);let i=n.value;r.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),r.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:o}},db:o,renderer:a,styles:i};export{g as diagram};
1
+ import{m as e,t}from"./mermaid-parser.core-D9qR3oRl.js";import{n}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import{m as r}from"./src-CllyRBe0.js";import"./chunk-O7XYJQB3-igihGd57.js";import{n as i,r as a,t as o}from"./chunk-GTNCS2PH-CZnXYcEC.js";import{t as s}from"./chunk-JWPE2WC7-DVXcaiue.js";import"./mermaid.core-BIXntNJ_.js";var c=e().RailroadAbnf.parser.LangiumParser,l=n(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=n(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=n(e=>{if(e.includes(`*`)){let[t,n]=e.split(`*`);return{min:t?parseInt(t,10):0,max:n?parseInt(n,10):1/0}}let t=parseInt(e,10);return{min:t,max:t}},`parseRepeat`),f=n(e=>{let t=p(e.primary);if(!e.repeat)return t;let{min:n,max:r}=d(e.repeat);return n===0&&r===1?{type:`optional`,element:t}:{type:`repetition`,element:t,min:n,max:r}},`transformElement`),p=n(e=>{switch(e.$type){case`AbnfStringLiteral`:return{type:`terminal`,value:e.value};case`AbnfNumVal`:return{type:`terminal`,value:e.value};case`AbnfRuleName`:return{type:`nonterminal`,name:e.name};case`AbnfGroup`:return l(e.element);case`AbnfOptionalGroup`:return{type:`optional`,element:l(e.element)};default:throw Error(`Unsupported ABNF primary node: ${e.$type}`)}},`transformPrimary`),m=n(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=n(e=>{s(e,o),e.title&&o.setTitle(e.title),e.rules.map(e=>o.addRule(m(e)))},`populateDb`),g={parser:{parse:n(e=>{o.clear(),r.debug(`[ABNF Parser] Starting Langium parse`);let n=c.parse(e);if(n.lexerErrors.length>0||n.parserErrors.length>0)throw new t(n);let i=n.value;r.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),r.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:o}},db:o,renderer:a,styles:i};export{g as diagram};
@@ -0,0 +1 @@
1
+ import"./chunk-FOHPRMQF-DzxwRta7.js";import{M as e}from"./mermaid-parser.core-D9qR3oRl.js";export{e as createArchitectureServices};