mulmoterminal 7.1.0 → 7.3.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.
- package/README.md +37 -16
- package/bin/cli-args.d.ts +1 -0
- package/bin/cli-args.js +5 -0
- package/bin/mulmoterminal.js +23 -3
- package/bin/ssh-hint.d.ts +3 -0
- package/bin/ssh-hint.js +32 -0
- package/bin/stop.d.ts +3 -1
- package/bin/stop.js +33 -6
- package/blueprints/ask/skills/keep/SKILL.md +1 -1
- package/blueprints/cloudflare/checks/page-renders.sh +12 -1
- package/blueprints/cloudflare/skills/spec/SKILL.md +1 -1
- package/blueprints/compare/checks/articles.d.mts +8 -0
- package/blueprints/compare/checks/articles.mjs +109 -0
- package/blueprints/compare/checks/base.mjs +12 -0
- package/blueprints/compare/checks/comparison.mjs +22 -0
- package/blueprints/compare/checks/comparisonView.d.mts +4 -0
- package/blueprints/compare/checks/comparisonView.mjs +14 -0
- package/blueprints/compare/checks/report.mjs +47 -0
- package/blueprints/compare/checks/versions.mjs +33 -0
- package/blueprints/compare/hearing.json +27 -0
- package/blueprints/compare/manifest.json +9 -0
- package/blueprints/compare/presets/itaku-kaitei/kyu.txt +31 -0
- package/blueprints/compare/presets/itaku-kaitei/shin.txt +31 -0
- package/blueprints/compare/presets.json +11 -0
- package/blueprints/compare/skills/pair/SKILL.md +51 -0
- package/blueprints/compare/skills/report/SKILL.md +33 -0
- package/blueprints/compare/steps.json +20 -0
- package/blueprints/docs/checks/chaff.sh +1 -1
- package/blueprints/docs/checks/drafts.mjs +9 -6
- package/blueprints/docs/checks/markdown.d.mts +9 -0
- package/blueprints/docs/checks/markdown.mjs +16 -0
- package/blueprints/docs/checks/places.mjs +6 -2
- package/blueprints/docs/skills/workspace/SKILL.md +1 -1
- package/blueprints/firebase/checks/page-renders.sh +12 -1
- package/blueprints/firebase/skills/spec/SKILL.md +1 -1
- package/blueprints/from-collection/checks/actions.sh +9 -7
- package/blueprints/from-collection/checks/compare.mjs +20 -10
- package/blueprints/from-collection/checks/import-supabase.sh +35 -0
- package/blueprints/from-collection/checks/supabase-verify.mjs +82 -0
- package/blueprints/from-collection/manifest.json +2 -2
- package/blueprints/from-collection/skills/acceptance/SKILL.md +3 -0
- package/blueprints/from-collection/skills/acceptance-cloudflare/SKILL.md +3 -0
- package/blueprints/from-collection/skills/acceptance-firebase/SKILL.md +3 -0
- package/blueprints/from-collection/skills/acceptance-supabase/SKILL.md +33 -0
- package/blueprints/from-collection/skills/actions/SKILL.md +2 -1
- package/blueprints/from-collection/skills/actions-cloudflare/SKILL.md +2 -1
- package/blueprints/from-collection/skills/actions-firebase/SKILL.md +2 -1
- package/blueprints/from-collection/skills/actions-supabase/SKILL.md +51 -0
- package/blueprints/from-collection/skills/import-production-supabase/SKILL.md +28 -0
- package/blueprints/from-collection/skills/import-supabase/SKILL.md +64 -0
- package/blueprints/from-collection/spec/conversion.md +21 -2
- package/blueprints/from-collection/spec/requirements.md +2 -2
- package/blueprints/from-collection/steps.json +37 -0
- package/blueprints/local/skills/spec/SKILL.md +1 -1
- package/blueprints/polish/checks/feedback.mjs +3 -1
- package/blueprints/polish/checks/kind.d.mts +9 -0
- package/blueprints/polish/checks/kind.mjs +27 -0
- package/blueprints/polish/checks/named.d.mts +6 -0
- package/blueprints/polish/checks/named.mjs +49 -0
- package/blueprints/polish/checks/report.mjs +12 -0
- package/blueprints/polish/checks/targets.mjs +93 -9
- package/blueprints/polish/checks/targetsView.mjs +4 -1
- package/blueprints/polish/checks/viewpoints.d.mts +22 -0
- package/blueprints/polish/checks/viewpoints.mjs +113 -0
- package/blueprints/polish/hearing.json +28 -1
- package/blueprints/polish/kinds.json +15 -0
- package/blueprints/polish/presets/blog/blog.md +19 -0
- package/blueprints/polish/presets/houkoku/houkoku.md +19 -0
- package/blueprints/polish/presets.json +27 -0
- package/blueprints/polish/skills/polish/SKILL.md +32 -2
- package/blueprints/polish/skills/report/SKILL.md +11 -2
- package/blueprints/polish/skills/survey/SKILL.md +18 -0
- package/blueprints/polish/viewpoints.json +89 -0
- package/blueprints/product/manifest.json +2 -2
- package/blueprints/product/skills/acceptance-supabase/SKILL.md +30 -0
- package/blueprints/product/steps.json +9 -0
- package/blueprints/refactor/skills/report/SKILL.md +2 -1
- package/blueprints/refactor/skills/survey/SKILL.md +1 -1
- package/blueprints/review/skills/report/SKILL.md +1 -1
- package/blueprints/style/skills/report/SKILL.md +1 -1
- package/blueprints/summarize/checks/base.mjs +12 -0
- package/blueprints/summarize/checks/parts.d.mts +14 -0
- package/blueprints/summarize/checks/parts.mjs +115 -0
- package/blueprints/summarize/checks/report.mjs +51 -0
- package/blueprints/summarize/checks/summary.mjs +48 -0
- package/blueprints/summarize/hearing.json +34 -0
- package/blueprints/summarize/manifest.json +9 -0
- package/blueprints/summarize/presets/keihi/keihi.md +19 -0
- package/blueprints/summarize/presets.json +11 -0
- package/blueprints/summarize/skills/report/SKILL.md +26 -0
- package/blueprints/summarize/skills/summarize/SKILL.md +50 -0
- package/blueprints/summarize/steps.json +20 -0
- package/blueprints/supabase/checks/advisors.mjs +47 -0
- package/blueprints/supabase/checks/client-secrets.mjs +145 -0
- package/blueprints/supabase/checks/declared-open.d.mts +3 -0
- package/blueprints/supabase/checks/declared-open.mjs +48 -0
- package/blueprints/supabase/checks/deploy-check.sh +33 -0
- package/blueprints/supabase/checks/handover.sh +14 -0
- package/blueprints/supabase/checks/linked-url.mjs +37 -0
- package/blueprints/supabase/checks/local-stack.sh +28 -0
- package/blueprints/supabase/checks/migrations-applied.mjs +49 -0
- package/blueprints/supabase/checks/page-renders.sh +39 -0
- package/blueprints/supabase/checks/prerequisites.sh +8 -0
- package/blueprints/supabase/checks/public-tables.mjs +20 -0
- package/blueprints/supabase/checks/scaffold.sh +14 -0
- package/blueprints/supabase/checks/security-probe.mjs +228 -0
- package/blueprints/supabase/checks/security-report.sh +11 -0
- package/blueprints/supabase/checks/security.sh +49 -0
- package/blueprints/supabase/checks/serve.sh +25 -0
- package/blueprints/supabase/checks/smoke.sh +11 -0
- package/blueprints/supabase/checks/spec.sh +12 -0
- package/blueprints/supabase/checks/supabase-cli.mjs +31 -0
- package/blueprints/supabase/checks/tests-pass.sh +8 -0
- package/blueprints/supabase/manifest.json +27 -0
- package/blueprints/supabase/plan.json +76 -0
- package/blueprints/supabase/security/checklist.md +10 -0
- package/blueprints/supabase/skills/auth/SKILL.md +36 -0
- package/blueprints/supabase/skills/data/SKILL.md +50 -0
- package/blueprints/supabase/skills/deploy/SKILL.md +44 -0
- package/blueprints/supabase/skills/handover/SKILL.md +39 -0
- package/blueprints/supabase/skills/prerequisites/SKILL.md +24 -0
- package/blueprints/supabase/skills/run-check/SKILL.md +24 -0
- package/blueprints/supabase/skills/scaffold/SKILL.md +62 -0
- package/blueprints/supabase/skills/security/SKILL.md +87 -0
- package/blueprints/supabase/skills/spec/SKILL.md +32 -0
- package/blueprints/supabase/skills/ui/SKILL.md +25 -0
- package/blueprints/supabase/spec/overview.md +35 -0
- package/blueprints/verify/skills/report/SKILL.md +1 -1
- package/common/agentAccounts.ts +5 -0
- package/common/agentEntries.ts +63 -0
- package/common/appActions.ts +62 -0
- package/common/blueprint/collectionSource.ts +17 -1
- package/common/blueprint/personLanguage.ts +26 -0
- package/common/blueprint/personalData.ts +80 -0
- package/common/blueprint/refusal.ts +16 -0
- package/common/blueprint/run.ts +8 -0
- package/common/blueprint/sourceStatus.ts +14 -0
- package/common/blueprint/specRevisionPrompt.ts +3 -0
- package/common/blueprint/stepPrompt.ts +5 -1
- package/common/customAgents.ts +6 -0
- package/common/dirConfigSaveReport.ts +34 -0
- package/common/headerActions.ts +74 -0
- package/common/headerButtonEntries.ts +203 -0
- package/common/headerButtonFolders.ts +149 -0
- package/common/headerChips.ts +103 -0
- package/common/hiddenCharacters.ts +108 -0
- package/common/keyRecording.ts +27 -0
- package/common/keymap.ts +40 -0
- package/common/keymapPresets.ts +134 -0
- package/common/mdPreviewMessage.ts +19 -2
- package/common/previewCodeBlocks.ts +47 -0
- package/common/providerEntries.ts +99 -0
- package/common/remoteServer.ts +11 -0
- package/common/themeEntries.ts +79 -0
- package/common/viewTargets.ts +5 -0
- package/common/whatsNew.ts +71 -0
- package/dist/assets/{abnfDiagram-O67JEVCF-B5xaEVpw.js → abnfDiagram-O67JEVCF-BnQ6PasO.js} +1 -1
- package/dist/assets/architecture-7GRP2DOG-WG-E0vWg.js +1 -0
- package/dist/assets/{architectureDiagram-NJMV4G6O-qBYbXFGp.js → architectureDiagram-NJMV4G6O-BKtC0YZv.js} +1 -1
- package/dist/assets/{blockDiagram-BEXU5L5S-DQioBmlX.js → blockDiagram-BEXU5L5S-az-TvPjp.js} +1 -1
- package/dist/assets/{c4Diagram-YGBWAQC7-cyT63OdJ.js → c4Diagram-YGBWAQC7-xDds9K_k.js} +1 -1
- package/dist/assets/channel-Dyz5xJeN.js +1 -0
- package/dist/assets/{chunk-3FUC2YCW-GAIwUw3Q.js → chunk-3FUC2YCW-DLr3D6jO.js} +1 -1
- package/dist/assets/{chunk-5DYCD2WN-DMiZMIIB.js → chunk-5DYCD2WN-5H5gAWR8.js} +1 -1
- package/dist/assets/{chunk-742MDFTN-6zizfpHe.js → chunk-742MDFTN-CcavS_nS.js} +1 -1
- package/dist/assets/{chunk-7INBJB4K-C2dZhhKc.js → chunk-7INBJB4K-DplkzUdE.js} +1 -1
- package/dist/assets/{chunk-7M6MHVWA-CqPNVLmK.js → chunk-7M6MHVWA-D4sIxByI.js} +1 -1
- package/dist/assets/{chunk-7PRAP22T-BoBqX9Yo.js → chunk-7PRAP22T-sPfItgkT.js} +1 -1
- package/dist/assets/{chunk-GTNCS2PH-0N3yAwxf.js → chunk-GTNCS2PH-o55BwD7R.js} +1 -1
- package/dist/assets/{chunk-GWA4HPMP-g7RRSruF.js → chunk-GWA4HPMP-DsJRcpHG.js} +1 -1
- package/dist/assets/{chunk-MBY4JIJT-C3i2TuVa.js → chunk-MBY4JIJT-BCFvhNqi.js} +1 -1
- package/dist/assets/{chunk-NETBCI7D-CH7vEM98.js → chunk-NETBCI7D-GQGQ0f8K.js} +1 -1
- package/dist/assets/{chunk-O7XYJQB3-D1JLIcoV.js → chunk-O7XYJQB3-CNBOCJb8.js} +2 -2
- package/dist/assets/{chunk-UA2S7LBM-CpyAfTb8.js → chunk-UA2S7LBM-DVKHgPob.js} +1 -1
- package/dist/assets/{chunk-WEXAMYUT-BGgzQY1u.js → chunk-WEXAMYUT-CuAljQbF.js} +1 -1
- package/dist/assets/{chunk-Z7XXMR3K-CEMmXuqC.js → chunk-Z7XXMR3K-ChACA7mW.js} +1 -1
- package/dist/assets/{chunk-ZIGJFQKS-g1Kaf-uj.js → chunk-ZIGJFQKS-BWf0ZX3U.js} +1 -1
- package/dist/assets/{classDiagram-v2-NBCMYWYE-B54sxnuH.js → classDiagram-v2-NBCMYWYE-2EURLE58.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-D796zEZZ.js → cynefin-OW5HDTMX-C5kb1EwK.js} +1 -1
- package/dist/assets/{cynefinDiagram-VND7K2PF-WHBkoilE.js → cynefinDiagram-VND7K2PF-DMytcOCT.js} +1 -1
- package/dist/assets/{dagre-6A5THRUB-2gQ61gKB.js → dagre-6A5THRUB-CUUE8Q1U.js} +1 -1
- package/dist/assets/{diagram-22UHCM2B-yDp0rHwO.js → diagram-22UHCM2B-CC1W7G0a.js} +1 -1
- package/dist/assets/{diagram-3UASUU5V-pnng1LdB.js → diagram-3UASUU5V-DMERag7H.js} +1 -1
- package/dist/assets/{diagram-ATOU4E4O-D5Jkesja.js → diagram-ATOU4E4O-DAMncBlv.js} +1 -1
- package/dist/assets/{diagram-CDSNMT55-DkH9QxZ2.js → diagram-CDSNMT55-C69-LfJU.js} +1 -1
- package/dist/assets/{diagram-MLGK6HIB-CDjGhKHq.js → diagram-MLGK6HIB-Qc95cLSN.js} +1 -1
- package/dist/assets/{diagram-MPIPVDR6-DAAr_c_l.js → diagram-MPIPVDR6-DrmRWZxC.js} +1 -1
- package/dist/assets/{dist-C1cqjsKD.js → dist-B-lTjmBE.js} +1 -1
- package/dist/assets/{dist-CeWYOYWG.js → dist-B-mUmtv8.js} +1 -1
- package/dist/assets/dist-B84r2Zu4.js +199 -0
- package/dist/assets/{dist-CgC34u2d.js → dist-BFtxixYt.js} +1 -1
- package/dist/assets/{dist-C3s_Ij_z.js → dist-C9QzqYij.js} +5 -5
- package/dist/assets/{dist-BSQTHmad.js → dist-CPuq5mCf.js} +1 -1
- package/dist/assets/{dist-CWSTMA5n.js → dist-CQ6YfKVL.js} +1 -1
- package/dist/assets/{dist-C1s7S4KF.js → dist-CYrIpcYc.js} +1 -1
- package/dist/assets/dist-CZ9-VOqZ.js +9 -0
- package/dist/assets/{dist-DbLWBcKv.js → dist-DF-Vjhnj.js} +1 -1
- package/dist/assets/{dist-B0sdAm9J.js → dist-Z8kzHarM.js} +1 -1
- package/dist/assets/{ebnfDiagram-ZINNZB2B-DKJbg6sg.js → ebnfDiagram-ZINNZB2B-DbzDNXYn.js} +1 -1
- package/dist/assets/{elk-276RUBZZ-CgwsWSX7.js → elk-276RUBZZ-CLrfQluT.js} +1 -1
- package/dist/assets/{erDiagram-OPXOYQCR-tCyjW7gV.js → erDiagram-OPXOYQCR-B1BCJu2j.js} +1 -1
- package/dist/assets/eventmodeling-NTZA5JFV-DO2IaAYE.js +1 -0
- package/dist/assets/flowDiagram-KWPJA3E3-Os30OifC.js +1 -0
- package/dist/assets/{ganttDiagram-FUAMR5RP-C3CZ2yCC.js → ganttDiagram-FUAMR5RP-D0VKOYxQ.js} +1 -1
- package/dist/assets/{gitGraph-4MIJSDKK-n2QHyQg8.js → gitGraph-4MIJSDKK-CzOJUjdw.js} +1 -1
- package/dist/assets/{gitGraphDiagram-X574FWY7-Dtk0GaqE.js → gitGraphDiagram-X574FWY7-vDZYUYtS.js} +1 -1
- package/dist/assets/index-DyqFaGZi.css +1 -0
- package/dist/assets/{index-CyHGyZiL.js → index-K_24iRwq.js} +363 -359
- package/dist/assets/{info-A6RAGUB7-Dt0N-ywn.js → info-A6RAGUB7-B9K2IdSh.js} +1 -1
- package/dist/assets/{infoDiagram-VRGFBTTK-DtjU_EzA.js → infoDiagram-VRGFBTTK-CXpbV_l5.js} +1 -1
- package/dist/assets/{ishikawaDiagram-OU5B5YK6-xOGev5sa.js → ishikawaDiagram-OU5B5YK6-CerjLKSu.js} +1 -1
- package/dist/assets/javascript-CppQSorr.js +1 -0
- package/dist/assets/{journeyDiagram-ZHPQQLJL-Disti4Wm.js → journeyDiagram-ZHPQQLJL-CYE7e5SQ.js} +1 -1
- package/dist/assets/{kanban-definition-PNTS6WVX-CB5dROof.js → kanban-definition-PNTS6WVX-hTQifntC.js} +1 -1
- package/dist/assets/{line-D6MSXT7u.js → line-DHCgFjyl.js} +1 -1
- package/dist/assets/{marp-BASsSHAw.js → marp-BZuPB_AE.js} +61 -61
- package/dist/assets/{mermaid-parser.core-C29lKtTF.js → mermaid-parser.core-Ci8-dBWZ.js} +3 -3
- package/dist/assets/{mermaid.core-CWf9rghw.js → mermaid.core-D1g_MhHe.js} +5 -5
- package/dist/assets/{mindmap-definition-NLK3R4M7-BfBZCMx_.js → mindmap-definition-NLK3R4M7-3-mAUeFh.js} +1 -1
- package/dist/assets/{packet-AYTQ26CC-BRpJC2Bl.js → packet-AYTQ26CC-Bi4OEG7f.js} +1 -1
- package/dist/assets/{pegDiagram-GJSIUBJH-xck9NPib.js → pegDiagram-GJSIUBJH-BvlMXL59.js} +1 -1
- package/dist/assets/{pie-WAS4IAKB-De38-oiC.js → pie-WAS4IAKB-D0Bzfos1.js} +1 -1
- package/dist/assets/{pieDiagram-5QR66LMP-DI7hJbHF.js → pieDiagram-5QR66LMP-ByI7bRXL.js} +1 -1
- package/dist/assets/punycode.es6-BGceFjA3.js +1 -0
- package/dist/assets/{quadrantDiagram-O4NWA36T-Bxqm6tTX.js → quadrantDiagram-O4NWA36T-DHt1QBiU.js} +1 -1
- package/dist/assets/{radar-RG4KPBEZ-CS8aCQ7S.js → radar-RG4KPBEZ-DNfkRPMH.js} +1 -1
- package/dist/assets/{railroad-74A4TZTK-DmloOzOC.js → railroad-74A4TZTK-1yUJUd_D.js} +1 -1
- package/dist/assets/railroad-abnf-HS5TGJTU-D3EYjt_F.js +1 -0
- package/dist/assets/railroad-ebnf-LZEXJU2U-8aE6RTc7.js +1 -0
- package/dist/assets/railroad-peg-WCYAUIDC-CFCfxkTd.js +1 -0
- package/dist/assets/{railroadDiagram-XR7U4H2S-D8ruOuhL.js → railroadDiagram-XR7U4H2S-y8FT2bjZ.js} +1 -1
- package/dist/assets/{requirementDiagram-PLB6GJNP-zWCW1gnc.js → requirementDiagram-PLB6GJNP-DCE3eASQ.js} +1 -1
- package/dist/assets/{sankeyDiagram-IPEJSGJF-D1ndcb5q.js → sankeyDiagram-IPEJSGJF-DkfyNgDg.js} +1 -1
- package/dist/assets/{sequenceDiagram-PO4LG4MO-zO_f3a5l.js → sequenceDiagram-PO4LG4MO-DFeaLdZP.js} +1 -1
- package/dist/assets/{stateDiagram-v2-GCMORJYK-DjHTP1Un.js → stateDiagram-v2-GCMORJYK-DXKYQ2gY.js} +1 -1
- package/dist/assets/{swimlanes-2SLR337P-3TYxXfqC.js → swimlanes-2SLR337P-BpbeyLoP.js} +1 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-BL6ovFkZ.js +8 -0
- package/dist/assets/{timeline-definition-EJHVYXUP-D2rwOmgp.js → timeline-definition-EJHVYXUP-CHZxYKup.js} +1 -1
- package/dist/assets/{treeView-Q6P3EWNA-pJAjZYyX.js → treeView-Q6P3EWNA-DnkPtA3-.js} +1 -1
- package/dist/assets/{treemap-WGGIJYW6-BkBiLnEF.js → treemap-WGGIJYW6-DLBjcQqV.js} +1 -1
- package/dist/assets/{usecaseDiagram-POWQR4AR-DZQYTp5w.js → usecaseDiagram-POWQR4AR-ChaNA2Rg.js} +1 -1
- package/dist/assets/{vennDiagram-UO4OBE2U-CLFk8_TU.js → vennDiagram-UO4OBE2U-pLa46WbE.js} +1 -1
- package/dist/assets/vitesse-dark-BZCL-v6S.js +1 -0
- package/dist/assets/vitesse-light-VbXTXTou.js +1 -0
- package/dist/assets/{wardley-WFR3VGLG-UZLdcCMt.js → wardley-WFR3VGLG-RCPsNr08.js} +1 -1
- package/dist/assets/{wardleyDiagram-VNRHLVJA-DOCJDdFu.js → wardleyDiagram-VNRHLVJA-BIvz-wXl.js} +1 -1
- package/dist/assets/{xychartDiagram-PMCCYNJV-DaRQvK2Z.js → xychartDiagram-PMCCYNJV-GkaA0v7z.js} +1 -1
- package/dist/index.html +3 -3
- package/docs/guide/en/v1.10.0.md +51 -0
- package/docs/guide/en/v1.11.0.md +44 -0
- package/docs/guide/en/v1.11.1.md +35 -0
- package/docs/guide/en/v1.12.0.md +46 -0
- package/docs/guide/en/v1.7.1.md +24 -0
- package/docs/guide/en/v1.7.2.md +27 -0
- package/docs/guide/en/v1.8.0.md +57 -0
- package/docs/guide/en/v1.9.0.md +31 -0
- package/docs/guide/en/v1.9.1.md +40 -0
- package/docs/guide/en/v1.9.2.md +27 -0
- package/docs/guide/en/v2.0.0.md +198 -0
- package/docs/guide/en/v2.1.0.md +159 -0
- package/docs/guide/en/v2.1.1.md +77 -0
- package/docs/guide/en/v2.2.0.md +150 -0
- package/docs/guide/en/v2.3.0.md +117 -0
- package/docs/guide/en/v2.4.0.md +135 -0
- package/docs/guide/en/v2.5.0.md +120 -0
- package/docs/guide/en/v2.5.1.md +108 -0
- package/docs/guide/en/v2.5.2.md +113 -0
- package/docs/guide/en/v2.5.3.md +158 -0
- package/docs/guide/en/v2.6.0.md +194 -0
- package/docs/guide/en/v2.7.0.md +228 -0
- package/docs/guide/en/v2.8.0.md +293 -0
- package/docs/guide/en/v2.9.0.md +207 -0
- package/docs/guide/en/v2.9.1.md +84 -0
- package/docs/guide/en/v3.0.0.md +121 -0
- package/docs/guide/en/v4.0.0.md +142 -0
- package/docs/guide/en/v4.1.0.md +114 -0
- package/docs/guide/en/v4.1.1.md +109 -0
- package/docs/guide/en/v4.10.0.md +99 -0
- package/docs/guide/en/v4.10.1.md +98 -0
- package/docs/guide/en/v4.11.0.md +177 -0
- package/docs/guide/en/v4.12.0.md +134 -0
- package/docs/guide/en/v4.13.0.md +106 -0
- package/docs/guide/en/v4.14.0.md +143 -0
- package/docs/guide/en/v4.15.0.md +131 -0
- package/docs/guide/en/v4.16.0.md +90 -0
- package/docs/guide/en/v4.16.1.md +110 -0
- package/docs/guide/en/v4.17.0.md +127 -0
- package/docs/guide/en/v4.18.0.md +107 -0
- package/docs/guide/en/v4.19.0.md +150 -0
- package/docs/guide/en/v4.2.0.md +201 -0
- package/docs/guide/en/v4.20.0.md +142 -0
- package/docs/guide/en/v4.21.0.md +159 -0
- package/docs/guide/en/v4.22.0.md +122 -0
- package/docs/guide/en/v4.23.0.md +113 -0
- package/docs/guide/en/v4.24.0.md +71 -0
- package/docs/guide/en/v4.25.0.md +113 -0
- package/docs/guide/en/v4.26.0.md +113 -0
- package/docs/guide/en/v4.27.0.md +125 -0
- package/docs/guide/en/v4.3.0.md +157 -0
- package/docs/guide/en/v4.3.1.md +73 -0
- package/docs/guide/en/v4.4.0.md +261 -0
- package/docs/guide/en/v4.5.0.md +241 -0
- package/docs/guide/en/v4.5.1.md +137 -0
- package/docs/guide/en/v4.6.0.md +175 -0
- package/docs/guide/en/v4.6.1.md +177 -0
- package/docs/guide/en/v4.7.0.md +179 -0
- package/docs/guide/en/v4.7.1.md +120 -0
- package/docs/guide/en/v4.7.2.md +195 -0
- package/docs/guide/en/v4.7.3.md +148 -0
- package/docs/guide/en/v4.7.4.md +123 -0
- package/docs/guide/en/v4.7.5.md +126 -0
- package/docs/guide/en/v4.7.6.md +81 -0
- package/docs/guide/en/v4.8.0.md +187 -0
- package/docs/guide/en/v4.8.1.md +149 -0
- package/docs/guide/en/v4.8.2.md +202 -0
- package/docs/guide/en/v4.8.3.md +97 -0
- package/docs/guide/en/v4.8.4.md +115 -0
- package/docs/guide/en/v4.8.5.md +163 -0
- package/docs/guide/en/v4.9.0.md +100 -0
- package/docs/guide/en/v5.0.0.md +106 -0
- package/docs/guide/en/v5.0.1.md +69 -0
- package/docs/guide/en/v5.1.0.md +110 -0
- package/docs/guide/en/v5.2.0.md +108 -0
- package/docs/guide/en/v5.3.0.md +107 -0
- package/docs/guide/en/v5.4.0.md +131 -0
- package/docs/guide/en/v5.5.0.md +71 -0
- package/docs/guide/en/v5.6.0.md +58 -0
- package/docs/guide/en/v5.7.0.md +60 -0
- package/docs/guide/en/v5.8.0.md +94 -0
- package/docs/guide/en/v6.0.0.md +44 -0
- package/docs/guide/en/v6.1.0.md +45 -0
- package/docs/guide/en/v6.2.0.md +65 -0
- package/docs/guide/en/v6.3.0.md +65 -0
- package/docs/guide/en/v6.4.0.md +85 -0
- package/docs/guide/en/v6.5.0.md +62 -0
- package/docs/guide/en/v6.6.0.md +133 -0
- package/docs/guide/en/v6.7.0.md +91 -0
- package/docs/guide/en/v6.8.0.md +235 -0
- package/docs/guide/en/v7.0.0.md +117 -0
- package/docs/guide/en/v7.1.0.md +106 -0
- package/docs/guide/en/v7.2.0.md +182 -0
- package/docs/guide/en/v7.3.0.md +92 -0
- package/docs/guide/ja/v1.10.0.md +51 -0
- package/docs/guide/ja/v1.11.0.md +44 -0
- package/docs/guide/ja/v1.11.1.md +37 -0
- package/docs/guide/ja/v1.12.0.md +48 -0
- package/docs/guide/ja/v1.7.1.md +26 -0
- package/docs/guide/ja/v1.7.2.md +29 -0
- package/docs/guide/ja/v1.8.0.md +57 -0
- package/docs/guide/ja/v1.9.0.md +33 -0
- package/docs/guide/ja/v1.9.1.md +40 -0
- package/docs/guide/ja/v1.9.2.md +31 -0
- package/docs/guide/ja/v2.0.0.md +173 -0
- package/docs/guide/ja/v2.1.0.md +132 -0
- package/docs/guide/ja/v2.1.1.md +62 -0
- package/docs/guide/ja/v2.2.0.md +150 -0
- package/docs/guide/ja/v2.3.0.md +108 -0
- package/docs/guide/ja/v2.4.0.md +130 -0
- package/docs/guide/ja/v2.5.0.md +115 -0
- package/docs/guide/ja/v2.5.1.md +103 -0
- package/docs/guide/ja/v2.5.2.md +112 -0
- package/docs/guide/ja/v2.5.3.md +155 -0
- package/docs/guide/ja/v2.6.0.md +192 -0
- package/docs/guide/ja/v2.7.0.md +228 -0
- package/docs/guide/ja/v2.8.0.md +284 -0
- package/docs/guide/ja/v2.9.0.md +198 -0
- package/docs/guide/ja/v2.9.1.md +80 -0
- package/docs/guide/ja/v3.0.0.md +116 -0
- package/docs/guide/ja/v4.0.0.md +139 -0
- package/docs/guide/ja/v4.1.0.md +111 -0
- package/docs/guide/ja/v4.1.1.md +107 -0
- package/docs/guide/ja/v4.10.0.md +98 -0
- package/docs/guide/ja/v4.10.1.md +95 -0
- package/docs/guide/ja/v4.11.0.md +174 -0
- package/docs/guide/ja/v4.12.0.md +135 -0
- package/docs/guide/ja/v4.13.0.md +101 -0
- package/docs/guide/ja/v4.14.0.md +140 -0
- package/docs/guide/ja/v4.15.0.md +126 -0
- package/docs/guide/ja/v4.16.0.md +86 -0
- package/docs/guide/ja/v4.16.1.md +106 -0
- package/docs/guide/ja/v4.17.0.md +125 -0
- package/docs/guide/ja/v4.18.0.md +102 -0
- package/docs/guide/ja/v4.19.0.md +148 -0
- package/docs/guide/ja/v4.2.0.md +196 -0
- package/docs/guide/ja/v4.20.0.md +135 -0
- package/docs/guide/ja/v4.21.0.md +156 -0
- package/docs/guide/ja/v4.22.0.md +120 -0
- package/docs/guide/ja/v4.23.0.md +109 -0
- package/docs/guide/ja/v4.24.0.md +69 -0
- package/docs/guide/ja/v4.25.0.md +111 -0
- package/docs/guide/ja/v4.26.0.md +110 -0
- package/docs/guide/ja/v4.27.0.md +121 -0
- package/docs/guide/ja/v4.3.0.md +157 -0
- package/docs/guide/ja/v4.3.1.md +72 -0
- package/docs/guide/ja/v4.4.0.md +256 -0
- package/docs/guide/ja/v4.5.0.md +235 -0
- package/docs/guide/ja/v4.5.1.md +140 -0
- package/docs/guide/ja/v4.6.0.md +172 -0
- package/docs/guide/ja/v4.6.1.md +174 -0
- package/docs/guide/ja/v4.7.0.md +174 -0
- package/docs/guide/ja/v4.7.1.md +120 -0
- package/docs/guide/ja/v4.7.2.md +196 -0
- package/docs/guide/ja/v4.7.3.md +143 -0
- package/docs/guide/ja/v4.7.4.md +119 -0
- package/docs/guide/ja/v4.7.5.md +124 -0
- package/docs/guide/ja/v4.7.6.md +78 -0
- package/docs/guide/ja/v4.8.0.md +190 -0
- package/docs/guide/ja/v4.8.1.md +147 -0
- package/docs/guide/ja/v4.8.2.md +198 -0
- package/docs/guide/ja/v4.8.3.md +95 -0
- package/docs/guide/ja/v4.8.4.md +116 -0
- package/docs/guide/ja/v4.8.5.md +162 -0
- package/docs/guide/ja/v4.9.0.md +99 -0
- package/docs/guide/ja/v5.0.0.md +104 -0
- package/docs/guide/ja/v5.0.1.md +69 -0
- package/docs/guide/ja/v5.1.0.md +106 -0
- package/docs/guide/ja/v5.2.0.md +106 -0
- package/docs/guide/ja/v5.3.0.md +103 -0
- package/docs/guide/ja/v5.4.0.md +125 -0
- package/docs/guide/ja/v5.5.0.md +69 -0
- package/docs/guide/ja/v5.6.0.md +57 -0
- package/docs/guide/ja/v5.7.0.md +60 -0
- package/docs/guide/ja/v5.8.0.md +75 -0
- package/docs/guide/ja/v6.0.0.md +44 -0
- package/docs/guide/ja/v6.1.0.md +45 -0
- package/docs/guide/ja/v6.2.0.md +65 -0
- package/docs/guide/ja/v6.3.0.md +42 -0
- package/docs/guide/ja/v6.4.0.md +72 -0
- package/docs/guide/ja/v6.5.0.md +49 -0
- package/docs/guide/ja/v6.6.0.md +101 -0
- package/docs/guide/ja/v6.7.0.md +88 -0
- package/docs/guide/ja/v6.8.0.md +234 -0
- package/docs/guide/ja/v7.0.0.md +114 -0
- package/docs/guide/ja/v7.1.0.md +100 -0
- package/docs/guide/ja/v7.2.0.md +174 -0
- package/docs/guide/ja/v7.3.0.md +89 -0
- package/package.json +6 -2
- package/server/backends/rawServingPlan.ts +9 -8
- package/server/backends/scheduler-boot.ts +27 -24
- package/server/backends/scheduler.ts +81 -3
- package/server/blueprint/collectionSnapshot.ts +37 -8
- package/server/blueprint/executor.ts +25 -1
- package/server/blueprint/packs.ts +4 -1
- package/server/blueprint/routes.ts +69 -7
- package/server/blueprint/sourceFingerprint.ts +56 -0
- package/server/blueprint/sourceStatus.ts +38 -0
- package/server/blueprint/wiring.ts +1 -0
- package/server/config/agent-entry-routes.ts +124 -0
- package/server/config/app-config.ts +30 -15
- package/server/config/config-reload.ts +60 -0
- package/server/config/config-routes.ts +124 -26
- package/server/config/config-schema.ts +24 -9
- package/server/config/dir-config.ts +7 -3
- package/server/config/header-button-routes.ts +104 -0
- package/server/config/header-chip-routes.ts +56 -0
- package/server/config/header-config.ts +6 -7
- package/server/config/keymap-binding-route.ts +52 -0
- package/server/config/system-task-settings.ts +21 -0
- package/server/config/theme-entry-routes.ts +60 -0
- package/server/files/codeHighlight.ts +151 -0
- package/server/files/files-browse.ts +89 -14
- package/server/files/files-tree-routes.ts +102 -0
- package/server/files/mdPreviewReporter.ts +72 -19
- package/server/files/pathContainment.ts +22 -3
- package/server/files/previewCodeFence.ts +20 -0
- package/server/files/renderedDoc.ts +54 -0
- package/server/files/tree-ops.ts +154 -0
- package/server/index.ts +1 -1
- package/server/infra/on-listening.ts +6 -3
- package/server/routes/app-routes.ts +10 -1
- package/server/routes/dir-routes.ts +7 -0
- package/server/session/reap-schedule.ts +42 -14
- package/server/skills/mulmoterminal-config/SKILL.md +43 -18
- package/server/skills/mulmoterminal-dirs/SKILL.md +3 -1
- package/server/skills/mulmoterminal-header/SKILL.md +35 -8
- package/server/skills/mulmoterminal-keys/SKILL.md +29 -13
- package/server/skills/mulmoterminal-model/SKILL.md +12 -7
- package/server/skills/mulmoterminal-theme/SKILL.md +4 -2
- package/server/whatsNew/guidePage.ts +69 -0
- package/server/whatsNew/routes.ts +63 -0
- package/server/whatsNew/state.ts +43 -0
- package/dist/assets/architecture-7GRP2DOG-BxUYapfa.js +0 -1
- package/dist/assets/channel-DLqe-NE3.js +0 -1
- package/dist/assets/dist-DHSqnwKI.js +0 -9
- package/dist/assets/eventmodeling-NTZA5JFV-Bj6FbHv7.js +0 -1
- package/dist/assets/flowDiagram-KWPJA3E3-C3CneunC.js +0 -1
- package/dist/assets/index-BkbFlsCM.css +0 -1
- package/dist/assets/railroad-abnf-HS5TGJTU-lxjdNIS_.js +0 -1
- package/dist/assets/railroad-ebnf-LZEXJU2U-BvnO_c-5.js +0 -1
- package/dist/assets/railroad-peg-WCYAUIDC-Bco3H4cO.js +0 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-D0Ef5eY1.js +0 -8
package/README.md
CHANGED
|
@@ -183,18 +183,19 @@ than as bytes (files within the session's working directory only):
|
|
|
183
183
|
**A line after the path is followed.** `src/a.ts:42`, `src/a.ts:42:7` (gcc, clang, `eslint -f unix`,
|
|
184
184
|
stack traces) and `src/a.ts(12,5)` (tsc) are one link. In the pane, and in the Files view, the file
|
|
185
185
|
opens as text with the caret on that line and column — an HTML page, an SVG or a table included, and a
|
|
186
|
-
tab reading in Preview switches to Edit.
|
|
186
|
+
tab reading in Preview switches to Edit. An image, a PDF, a video or an audio file has no line to go
|
|
187
|
+
to and simply opens as itself. A route that renders the file in a new tab (`.md`, `.json`,
|
|
187
188
|
`.csv`, `.html` with no cell enlarged) ignores the line. When the pane beside an enlarged cell takes
|
|
188
189
|
the click, the keyboard stays in the terminal; the full-screen Files view, with no terminal beside it,
|
|
189
190
|
puts it in the editor. (ESLint's default output puts the path on a line of its own, with no line number to follow.)
|
|
190
191
|
|
|
191
192
|
**While a grid cell is enlarged, the [Files pane](#files-view-browse--edit) takes the click first** — every
|
|
192
|
-
row above
|
|
193
|
-
|
|
193
|
+
row above, since the pane is the same editor with a Preview for Markdown, HTML and SVG, shows an
|
|
194
|
+
image as a picture and a PDF in a frame, and plays a video or an audio file with the browser's own
|
|
195
|
+
controls (seeking included, whatever the file's size). The file opens *beside* the terminal
|
|
194
196
|
that printed it, and the pane opens itself if it was closed. It declines, leaving the routing
|
|
195
|
-
above untouched, when nothing is enlarged, when the path is not under that cell's own directory
|
|
196
|
-
(the pane cannot walk above its root)
|
|
197
|
-
browser tab can play.
|
|
197
|
+
above untouched, when nothing is enlarged, or when the path is not under that cell's own directory
|
|
198
|
+
(the pane cannot walk above its root). A file too large to edit as text still offers **Open in OS**.
|
|
198
199
|
|
|
199
200
|
Highlighting in the Files view covers the JS/TS family, JSON and Markdown (the modes
|
|
200
201
|
`cmEditor.ts` bundles); other languages open as plain text.
|
|
@@ -308,7 +309,8 @@ mulmoterminal
|
|
|
308
309
|
|
|
309
310
|
**Stopping it.** `Ctrl+C` in the terminal that started it — or, if you can no longer find that
|
|
310
311
|
terminal, **Settings → Quit MulmoTerminal** in the browser, or **`npx mulmoterminal@latest stop`**
|
|
311
|
-
from any terminal (installed globally, just `mulmoterminal stop
|
|
312
|
+
from any terminal (installed globally, just `mulmoterminal stop`; it stops every server running on
|
|
313
|
+
this machine, and `--port <port>` stops only that one). All three run the same shutdown:
|
|
312
314
|
with `tmux` installed the agent sessions survive and come back under **Settings → Sessions that
|
|
313
315
|
survived a restart**; without it they end with the server.
|
|
314
316
|
|
|
@@ -634,6 +636,17 @@ up for Codex, loaded by description.
|
|
|
634
636
|
|
|
635
637
|
---
|
|
636
638
|
|
|
639
|
+
## Running the server on another machine
|
|
640
|
+
|
|
641
|
+
The server can live on a Linux box, a VPS or a container while your browser stays on your laptop:
|
|
642
|
+
start it there over SSH (`npx mulmoterminal`), and reach it through an SSH tunnel
|
|
643
|
+
(`ssh -N -L 34567:127.0.0.1:34567 you@server`, then open `http://localhost:34567`). Started over
|
|
644
|
+
SSH, the launcher opens no browser on the server and prints that command. It still listens on
|
|
645
|
+
loopback only — nothing is opened to the network. Logging the agents in on the server, Docker, and
|
|
646
|
+
the few actions that act on the server's machine: [Run the server on another machine](https://receptron.github.io/mulmoterminal/guide/en/remote.html).
|
|
647
|
+
|
|
648
|
+
---
|
|
649
|
+
|
|
637
650
|
## Session persistence (tmux)
|
|
638
651
|
|
|
639
652
|
If **`tmux` is installed**, MulmoTerminal runs each Claude session and launcher inside
|
|
@@ -777,7 +790,7 @@ The Settings modal (the gear button) persists per-user UI choices to `~/.mulmote
|
|
|
777
790
|
|
|
778
791
|

|
|
779
792
|
|
|
780
|
-
*Open it from the gear button in the toolbar. The **sidebar** groups the sections — Appearance, Projects, Header & launch, Input, Models & servers, Notifications, Integrations, Sessions, Help — and one is on screen at a time; on a phone the sidebar becomes a picker above the section. The interface is available in **English, 日本語, 简体中文, 繁體中文 and 한국어**: it follows your browser's language unless you pick one under **Language** (per browser, like the theme). Translated so far: this modal, the grid's status words, and every button's hover tip and screen-reader label; the rest of the app's words are still English. Under the title, a **Version** row shows what is running: the version from the shipped `package.json`, plus a `commit <sha>` chip on a git checkout — there the version is only whatever was last released, so the commit is what identifies the build. When something newer exists, the row is followed by the header badge's update notice, command included. Pick a **theme**, set the **terminal font size**, **font** and **scroll speed** (and whether sending **returns to the latest output**), set a custom **attention sound**, list the repos the cross-repo **PRs & Issues** view should aggregate, add **launch commands** for grid cells, register your own **MCP servers**, and turn on the switches for what this app writes on your behalf — **issue work comments**, the **PR clone footer**, the **closing summary**, the **decision digest**, the **dev worklog** — no need to hand-edit the config file.
|
|
793
|
+
*Open it from the gear button in the toolbar. The **sidebar** groups the sections — Appearance, Projects, Header & launch, Input, Models & servers, Notifications, Integrations, Sessions, Help — and one is on screen at a time; on a phone the sidebar becomes a picker above the section. The interface is available in **English, 日本語, 简体中文, 繁體中文 and 한국어**: it follows your browser's language unless you pick one under **Language** (per browser, like the theme). Translated so far: this modal, the grid's status words, and every button's hover tip and screen-reader label; the rest of the app's words are still English. Under the title, a **Version** row shows what is running: the version from the shipped `package.json`, plus a `commit <sha>` chip on a git checkout — there the version is only whatever was last released, so the commit is what identifies the build. When something newer exists, the row is followed by the header badge's update notice, command included. Pick a **theme**, set the **terminal font size**, **font** and **scroll speed** (and whether sending **returns to the latest output**), set a custom **attention sound**, list the repos the cross-repo **PRs & Issues** view should aggregate, add **launch commands** for grid cells, register your own **MCP servers**, and turn on the switches for what this app writes on your behalf — **issue work comments**, the **PR clone footer**, the **closing summary**, the **decision digest**, the **dev worklog** — no need to hand-edit the config file. Three settings stay with their skill because a form would be the wrong tool for them (`keymap`, `themes`, `buttons`/`chips`); Settings shows what each is doing now and launches that skill. Backends (`providers`), custom agents and accounts are added and removed in **Models and backends**. Note that **theme, font size, scroll speed and the return-to-latest switch are stored per browser** (they're display preferences, so a phone and a desktop keep their own); the rest live in `~/.mulmoterminal/config.json` and are shared by every client.*
|
|
781
794
|
|
|
782
795
|
| Field | Meaning |
|
|
783
796
|
| ------------ | ------- |
|
|
@@ -799,7 +812,7 @@ The Settings modal (the gear button) persists per-user UI choices to `~/.mulmote
|
|
|
799
812
|
| `pushKinds` | Which moments push: `"finished"` (a turn ended, ✅) and/or `"waiting"` (the agent stopped to ask — a permission prompt or a question, ❓, **once per prompt**). Omit to keep both; `[]` for none. A kind added in a later version stays off until you tick it. |
|
|
800
813
|
| `worklogEnabled` | `true` to run the built-in **dev worklog** batch (see below). Off by default (each run spawns an LLM session, so it costs tokens). Editable in Settings → **Sessions and background tasks**. |
|
|
801
814
|
| `worklogIntervalHours` | Worklog cadence in hours (default `6`, clamped to `1`–`168`). A stepper in the same Settings section covers the range. |
|
|
802
|
-
| `feedRefreshEnabled` | `false` stops the hourly **collection/feed refresh** (one scheduled task per root — the workspace and every saved project directory). **On by default**; only an explicit `false` turns it off, so an existing config keeps the behaviour it has.
|
|
815
|
+
| `feedRefreshEnabled` | `false` stops the hourly **collection/feed refresh** (one scheduled task per root — the workspace and every saved project directory). **On by default**; only an explicit `false` turns it off, so an existing config keeps the behaviour it has. Saved from Settings (or any `POST /api/config`), it applies at once — the running scheduler rebuilds its built-in tasks; a hand-edit applies after **Settings → Reload config file** (or a restart). Feeds still refresh on demand. Checkbox in Settings → **Sessions**. |
|
|
803
816
|
| `calendarSyncEnabled` | `false` stops the hourly **Google Calendar sync** (pulls changed events into collections that declare a calendar). Same default and the same restart rule as `feedRefreshEnabled`; calendar collections still sync on demand. Checkbox in the same Settings section. With both off and `worklogEnabled` off, no **built-in** task is registered. Your own tasks in `config/scheduler/tasks.json` are unaffected — these switches do not touch them, and an enabled one still registers and still runs the tick loop. Only when there is no enabled user task either does the scheduler stop writing its state file and run logs. |
|
|
804
817
|
| `terminalSubmit` | Which bytes Claude reads as **submit** vs **newline**: `"cr"` (default — Enter submits, Shift+Enter makes a newline) or `"esc-cr"` (for a Claude Code rebound the other way). Applies to the keyboard **and** the phone remote-view submit, for **Claude sessions only** (shell/codex keep plain Enter). See the [Configuration guide](https://receptron.github.io/mulmoterminal/guide/en/config.html#terminal-submit). Settings → **Terminal keys** offers both, worded as behaviour. |
|
|
805
818
|
| `copyOnSelect` | `true` puts a **mouse selection on the clipboard the moment it settles**, with no key pressed (the PuTTY / iTerm2 behaviour). **Off by default** — it changes the clipboard when you may only have meant to highlight something. There is a checkbox in Settings → **Terminal keys**, applied at once; a hand edit of the file needs a **server restart, then a tab reload** (the server reads this file once at startup, and the browser reads the value from it on load). Composes with the `copy` keymap action rather than replacing it. Over plain `http://` the browser gives a page no clipboard access, so a fallback asks xterm to copy instead; see the [Configuration guide](https://receptron.github.io/mulmoterminal/guide/en/config.html#copy-on-select). |
|
|
@@ -811,6 +824,7 @@ The Settings modal (the gear button) persists per-user UI choices to `~/.mulmote
|
|
|
811
824
|
| `autoDirIcon` | Whether a project that sets no `icon` shows the favicon its repository already ships (`public/favicon.svg`, `apple-touch-icon.png`, a web manifest — first hit wins, ordered by how the image survives at 14px). **On by default**; Settings → **Directory appearance** has the switch. A single project opts out with `"icon": false` in its own `.mulmoterminal.json`, which this does not override. A key that was written and got it wrong shows nothing rather than falling back — a broken setting has to look broken. `true` / `false` only. |
|
|
812
825
|
| `cockpitLines` | `{ summary, prompt, response }` — how many lines each **cockpit-roster** row shows before it clamps (default `2` / `2` / `3`, each clamped to `1`–`20`). Raising them trades how many sessions fit on screen for reading a long one in place. Three steppers in Settings → **Waiting rows**. |
|
|
813
826
|
| `showLoadAverage` | Whether the grid header draws this machine's **load average** beside the 5h / 7d usage windows, as a percentage of its cores (`load 334%` = a 20-core machine with 66.8 runnable processes). **On by default**; amber at 100%, red at 200%, hover for the raw 1 / 5 / 15-minute figures. Settings → **Grid header read-outs** has the switch. A host that keeps no load average (Windows) shows nothing rather than `0%`, whatever this says. `true` / `false` only. |
|
|
827
|
+
| `remoteServer` | **Experimental.** The server runs on another machine than your browser (an SSH tunnel — [guide](https://receptron.github.io/mulmoterminal/guide/en/remote.html)): the actions that would act on the server's screen (the file dialog, the file manager, the OS app) are hidden or say why, and a dropped file is always uploaded. **Off unless `true`**; Settings → **Sessions and background tasks** has the box (or set it in the server's config). |
|
|
814
828
|
| `fontFamily` | The **terminal font** every session renders in — a CSS font-family stack, e.g. `"'Cica', 'MS Gothic', monospace"`. Set it in Settings → **Terminal font**, applied at once; editing the file instead needs a **restart** (this config is read once at startup). Unset uses the built-in stack (JetBrains Mono / Fira Code / Menlo / Consolas, then CJK faces for Japanese, Korean and Chinese). Unlike the per-browser font **size**, this is one value for the whole host — it names fonts, and which fonts exist is a property of the machine. A directory can override it. See the [Configuration guide](https://receptron.github.io/mulmoterminal/guide/en/config.html#font-family). |
|
|
815
829
|
|
|
816
830
|
Every MulmoTerminal on the machine shares this one file, so an older build could save over a key a
|
|
@@ -829,9 +843,14 @@ are items in the **path menu** (click the directory path on the terminal header)
|
|
|
829
843
|
set** with your list (it is not merged on top), so listing your own — even a **shorter** one — is how
|
|
830
844
|
you drop, reorder, or swap them.
|
|
831
845
|
A button has an `id`, `label`, and a `run` of `"shell"` (run a command), `"input"` (send text to the
|
|
832
|
-
agent), `"open"`, or `"action"` (act on the cell itself
|
|
833
|
-
|
|
834
|
-
|
|
846
|
+
agent), `"open"`, or `"action"` (act on the cell itself, named like the `keymap` actions so one operation is
|
|
847
|
+
a button, a key and a command-palette entry — `action: "terminal-restart"` ends the agent and starts
|
|
848
|
+
it again in the same cell on the same conversation, which is how an MCP / config / plugin change
|
|
849
|
+
takes effect; it costs a resume and asks nothing first. `"terminal-new-here"` opens the launch panel
|
|
850
|
+
on the cell's directory, and `"pane-files"` / `"pane-prompts"` / … / `"terminal-timeline"` /
|
|
851
|
+
`"terminal-talk"` / `"terminal-park"` / `"terminal-copy-code"` / `"terminal-insert-path"` / `"terminal-reveal"` /
|
|
852
|
+
`"terminal-voice"` / `"terminal-diff"` / `"terminal-note"` do what the cell's menus and buttons do, and `"screen-*"` / `"settings-open"` /
|
|
853
|
+
`"sound-toggle"` / `"view-toggle"` / `"order-*"` / `"page-next"` / `"page-prev"` do what the toolbar and the page tabs do; the old name `"restart"` still works). An `open` button targets one of `url` / `reveal` (OS file manager) / `files`
|
|
835
854
|
(in-app explorer) / `view` (a built-in overlay) / `terminal` (a dir → a new cell running `$SHELL`,
|
|
836
855
|
opened next to the current one) / `pr: true` (open the current branch's PR — the button is hidden when
|
|
837
856
|
there's no open PR) / `pickFile: true` (OS file dialog → insert the path).
|
|
@@ -891,7 +910,7 @@ channel is connected (its Google sign-in supplies the notification auth). With R
|
|
|
891
910
|
disconnected, or with no device registered, the toggle is a no-op.
|
|
892
911
|
|
|
893
912
|
**Dev worklog (cross-clone).** Set `worklogEnabled: true` in
|
|
894
|
-
`~/.mulmoterminal/config.json` (
|
|
913
|
+
`~/.mulmoterminal/config.json` (or switch it on in Settings, which applies at once; after a hand-edit use Settings → **Reload config file**)
|
|
895
914
|
to register a built-in scheduled task. Every `worklogIntervalHours` (default 6) it spawns
|
|
896
915
|
a Claude session that reviews the work you did across **all your saved working dirs**
|
|
897
916
|
(`cwdPresets`) since it last ran, and writes it up as a short manager-style report.
|
|
@@ -1324,11 +1343,13 @@ or changed (amber), and a notch where lines were removed, as VS Code's gutter sh
|
|
|
1324
1343
|
to date as you type. **Changes** in the header also shows the removed lines in place, as a unified
|
|
1325
1344
|
diff. Nothing is marked for a file outside git or one not yet committed.
|
|
1326
1345
|
|
|
1327
|
-
**HTML pages and
|
|
1346
|
+
**HTML pages, images, PDFs, video and audio show in the pane too.** An `.html` file has a **Preview** like a Markdown
|
|
1328
1347
|
file: the page itself, sandboxed as presentHtml pages are — its scripts run on an opaque origin with
|
|
1329
1348
|
no fetch/XHR; images and the curated CDN list still load, and so does an image it links beside it,
|
|
1330
|
-
while a relative stylesheet or script does not. An `.svg` previews as the picture it draws,
|
|
1331
|
-
rather than as "not text"
|
|
1349
|
+
while a relative stylesheet or script does not. An `.svg` previews as the picture it draws, a PNG, JPEG, GIF or WebP opens as the image
|
|
1350
|
+
rather than as "not text", a PDF opens in a frame, and a video or audio file plays with the
|
|
1351
|
+
browser's controls, seeking by range rather than fetching the whole file. A text file too large to edit
|
|
1352
|
+
says so and still offers **Open in OS**. A path to any of these clicked in terminal output opens in the pane when
|
|
1332
1353
|
it is up, so a chart an agent just wrote stays in the grid. They are served only under the workspace
|
|
1333
1354
|
or a terminal's directory, like every other file the app hands to the browser.
|
|
1334
1355
|
|
package/bin/cli-args.d.ts
CHANGED
|
@@ -25,4 +25,5 @@ export interface RunningInstance {
|
|
|
25
25
|
port: number | null;
|
|
26
26
|
}
|
|
27
27
|
export declare function stopCommandFor(pkgDir: string): string;
|
|
28
|
+
export declare function stopCommandForThis(stopCommand: string, port: number, instances: readonly { port: number | null }[]): string;
|
|
28
29
|
export declare function runningInstancesPrompt(instances: readonly RunningInstance[], stopCommand?: string): string;
|
package/bin/cli-args.js
CHANGED
|
@@ -366,6 +366,11 @@ export function saysYes(answer) {
|
|
|
366
366
|
// they are: npx unpacks into `<cache>/_npx/<hash>/node_modules/mulmoterminal`.
|
|
367
367
|
const NPX_INSTALL = /[/\\]_npx[/\\]/;
|
|
368
368
|
|
|
369
|
+
/** The stop command the ready banner shows for the server on `port`: the plain one while it is the
|
|
370
|
+
* only server, and one narrowed to its port once another is running, which the plain one would stop
|
|
371
|
+
* too (#2683). `instances` is the registry, this server included. */
|
|
372
|
+
export const stopCommandForThis = (stopCommand, port, instances) => (instances.some((i) => i.port !== port) ? `${stopCommand} --port ${port}` : stopCommand);
|
|
373
|
+
|
|
369
374
|
export const stopCommandFor = (pkgDir) => (NPX_INSTALL.test(String(pkgDir)) ? "npx mulmoterminal@latest stop" : "mulmoterminal stop");
|
|
370
375
|
|
|
371
376
|
export function runningInstancesPrompt(instances, stopCommand = "mulmoterminal stop") {
|
package/bin/mulmoterminal.js
CHANGED
|
@@ -14,6 +14,7 @@ import { dirname, join, resolve } from "node:path";
|
|
|
14
14
|
import { fileURLToPath } from "node:url";
|
|
15
15
|
import { computeUpdateNotice, isUpdateCheckDisabled } from "./update-check.js";
|
|
16
16
|
import { detectNpxCacheDir, npxCacheHintLines } from "./npx-cache-hint.js";
|
|
17
|
+
import { configuredRemoteServer, sshTunnelHintLines } from "./ssh-hint.js";
|
|
17
18
|
import { planAfterServerExit } from "./server-supervision.js";
|
|
18
19
|
import { waitUntilReady } from "./wait-ready.js";
|
|
19
20
|
import {
|
|
@@ -29,6 +30,7 @@ import {
|
|
|
29
30
|
secondInstancePrompt,
|
|
30
31
|
runningInstancesPrompt,
|
|
31
32
|
stopCommandFor,
|
|
33
|
+
stopCommandForThis,
|
|
32
34
|
SECOND_INSTANCE_NOTE,
|
|
33
35
|
nodeMeetsMinimum,
|
|
34
36
|
unsupportedNodeMessage,
|
|
@@ -109,6 +111,17 @@ function readConfiguredDefaultAgent() {
|
|
|
109
111
|
}
|
|
110
112
|
}
|
|
111
113
|
|
|
114
|
+
// The experimental `remoteServer` (#2669): the browser is on another machine, so the launcher opens
|
|
115
|
+
// none here even when started without SSH (a service, a remote desktop). A missing or unreadable
|
|
116
|
+
// file is simply "not set".
|
|
117
|
+
function readRemoteServer() {
|
|
118
|
+
try {
|
|
119
|
+
return configuredRemoteServer(JSON.parse(readFileSync(CONFIG_FILE, "utf8")));
|
|
120
|
+
} catch {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
112
125
|
// The command for `agent`, honouring its `<AGENT>_BIN` override.
|
|
113
126
|
//
|
|
114
127
|
// The override is the point rather than a detail: the gate used to look for the literal word
|
|
@@ -478,11 +491,16 @@ async function choosePort(requested, explicit) {
|
|
|
478
491
|
//
|
|
479
492
|
// `url` is where the BROWSER goes and `note` is what launchTarget wants said about that choice —
|
|
480
493
|
// two different facts since #1889, and the note is null whenever the URL already covers it.
|
|
481
|
-
function announceReady(url, note, noOpen) {
|
|
482
|
-
printReadyBanner(url, STOP_COMMAND);
|
|
494
|
+
function announceReady(url, note, noOpen, sshHint, port) {
|
|
495
|
+
printReadyBanner(url, stopCommandForThis(STOP_COMMAND, port, liveInstances()));
|
|
483
496
|
// Either the address a widened bind serves other machines on, or why the browser was NOT sent
|
|
484
497
|
// to `localhost`. Null whenever the URL above already said everything.
|
|
485
498
|
if (note) log(note);
|
|
499
|
+
// Over SSH the browser is on the other machine (see ssh-hint.js): say how to reach it, open nothing.
|
|
500
|
+
if (sshHint) {
|
|
501
|
+
sshHint.forEach((line) => log(line));
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
486
504
|
if (noOpen) return;
|
|
487
505
|
try {
|
|
488
506
|
// The command is a hardcoded literal; url is built by browserUrl from a numeric port, so it
|
|
@@ -549,7 +567,9 @@ function runServer({ port, probedAddress, localhostIsUnambiguous, noOpen, launch
|
|
|
549
567
|
readyStarted = true;
|
|
550
568
|
const localhostIsOurs = localhostIsUnambiguous && serverSaysLocalhostIsOurs !== false;
|
|
551
569
|
const { url, note } = launchTarget(reachHost, port, localhostIsOurs);
|
|
552
|
-
cancelReady = waitUntilReady(port, () => announceReady(url, note, noOpen
|
|
570
|
+
cancelReady = waitUntilReady(port, () => announceReady(url, note, noOpen, sshTunnelHintLines(process.env, port, readRemoteServer()), port), {
|
|
571
|
+
host: reachHost,
|
|
572
|
+
});
|
|
553
573
|
};
|
|
554
574
|
// The same message answers a second question now: whether this lifetime ever bound at all,
|
|
555
575
|
// which is what a restart is allowed to depend on. Recorded BEFORE the address is looked at —
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare function inSshSession(env: Record<string, string | undefined>): boolean;
|
|
2
|
+
export declare const configuredRemoteServer: (config: unknown) => boolean;
|
|
3
|
+
export declare function sshTunnelHintLines(env: Record<string, string | undefined>, port: number, remoteServer?: boolean): string[] | null;
|
package/bin/ssh-hint.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// A launcher started over SSH is on a machine whose screen nobody is looking at: the browser that
|
|
2
|
+
// should open this is on the other end of the connection. So it does not try to open one here — on
|
|
3
|
+
// a remote Mac that would put a window on a display no one sees — and it says how to reach the
|
|
4
|
+
// server from there instead: an SSH port forward, the route the FAQ recommends. Nothing here reads
|
|
5
|
+
// the process; the caller passes its environment.
|
|
6
|
+
|
|
7
|
+
/** Whether this process runs inside an SSH login. sshd sets both variables; either is enough. */
|
|
8
|
+
export function inSshSession(env) {
|
|
9
|
+
return Boolean(env.SSH_CONNECTION || env.SSH_CLIENT);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** The experimental `remoteServer` out of a parsed `~/.mulmoterminal/config.json`: true only for a
|
|
13
|
+
* literal `true`, like the server's own reading of it (common/remoteServer.ts). */
|
|
14
|
+
export const configuredRemoteServer = (config) => config !== null && typeof config === "object" && config.remoteServer === true;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The lines that tell someone on another machine how to open this server, or null when the browser
|
|
18
|
+
* is expected here: not an SSH login, and `remoteServer` not set. The host is left for them to fill
|
|
19
|
+
* in: SSH_CONNECTION names the address THIS side answered on, which behind NAT or a container is not
|
|
20
|
+
* one their machine can reach.
|
|
21
|
+
*/
|
|
22
|
+
export function sshTunnelHintLines(env, port, remoteServer = false) {
|
|
23
|
+
const ssh = inSshSession(env);
|
|
24
|
+
if (!ssh && !remoteServer) return null;
|
|
25
|
+
const user = env.USER || env.LOGNAME || "<user>";
|
|
26
|
+
const why = ssh ? "Started over SSH" : "remoteServer is set in config.json";
|
|
27
|
+
return [
|
|
28
|
+
`${why}, so no browser is opened here. On your own machine, run:`,
|
|
29
|
+
` ssh -N -L ${port}:127.0.0.1:${port} ${user}@<this-host>`,
|
|
30
|
+
`then open http://localhost:${port} there.`,
|
|
31
|
+
];
|
|
32
|
+
}
|
package/bin/stop.d.ts
CHANGED
|
@@ -16,10 +16,12 @@ export interface StopResult {
|
|
|
16
16
|
stubborn: StubbornInstance[];
|
|
17
17
|
unconfirmed: InstanceEntry[];
|
|
18
18
|
}
|
|
19
|
-
export type StopArgs = { help: true } | { error: string } | { force: boolean };
|
|
19
|
+
export type StopArgs = { help: true } | { error: string } | { force: boolean; port: number | null };
|
|
20
20
|
|
|
21
21
|
export declare const STOP_USAGE: string;
|
|
22
22
|
export declare function parseStopArgs(args: readonly string[]): StopArgs;
|
|
23
|
+
export declare function selectByPort(instances: readonly InstanceEntry[], port: number | null): InstanceEntry[];
|
|
24
|
+
export declare function noServerOnPortReport(port: number, instances: readonly InstanceEntry[]): string[];
|
|
23
25
|
export interface ConfirmDeps {
|
|
24
26
|
owners?: (port: number) => Promise<number[] | null>;
|
|
25
27
|
}
|
package/bin/stop.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// Windows user find or kill anything.
|
|
15
15
|
import { dirname } from "node:path";
|
|
16
16
|
import { fileURLToPath } from "node:url";
|
|
17
|
-
import { stopCommandFor } from "./cli-args.js";
|
|
17
|
+
import { parsePortArg, stopCommandFor } from "./cli-args.js";
|
|
18
18
|
import { isProcessAlive, liveInstances } from "./instances.js";
|
|
19
19
|
import { portOwners } from "./port-owner.js";
|
|
20
20
|
|
|
@@ -140,10 +140,11 @@ export function stopReport({ stopped, stubborn, unconfirmed }, platform = proces
|
|
|
140
140
|
export const stopExitCode = ({ stubborn, unconfirmed }) => (stubborn.length + unconfirmed.length ? 1 : 0);
|
|
141
141
|
|
|
142
142
|
export const STOP_USAGE = [
|
|
143
|
-
"Usage: mulmoterminal stop [--force]",
|
|
143
|
+
"Usage: mulmoterminal stop [--port <port>] [--force]",
|
|
144
144
|
"",
|
|
145
145
|
"Stops every running MulmoTerminal server on this machine, from any terminal.",
|
|
146
146
|
"",
|
|
147
|
+
" --port Stop only the server on that port (the one at http://localhost:<port>).",
|
|
147
148
|
" --force Also stop a registered server that is no longer answering. Off by default:",
|
|
148
149
|
" a server that crashed leaves its entry behind, and that pid may since have",
|
|
149
150
|
" been given to an unrelated program.",
|
|
@@ -153,9 +154,26 @@ export const STOP_USAGE = [
|
|
|
153
154
|
* argument handling is testable — and so `stop --help` can never be read as "stop everything". */
|
|
154
155
|
export function parseStopArgs(args) {
|
|
155
156
|
if (args.includes("--help") || args.includes("-h")) return { help: true };
|
|
156
|
-
const
|
|
157
|
+
const portAt = args.indexOf("--port");
|
|
158
|
+
const portArgs = portAt === -1 ? [] : [portAt, portAt + 1];
|
|
159
|
+
const unknown = args.filter((a, i) => a !== "--force" && !portArgs.includes(i));
|
|
157
160
|
if (unknown.length) return { error: `Unknown argument for stop: ${unknown.join(" ")}` };
|
|
158
|
-
|
|
161
|
+
const force = args.includes("--force");
|
|
162
|
+
if (portAt === -1) return { force, port: null };
|
|
163
|
+
// The launcher's own reading of --port, so the two accept the same values. The environment's
|
|
164
|
+
// PORT is not consulted: a `stop` narrowed by a variable nobody typed here would surprise.
|
|
165
|
+
const parsed = parsePortArg(args, {}, null);
|
|
166
|
+
return "error" in parsed ? { error: parsed.error } : { force, port: parsed.port };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** The servers a `stop` is about: all of them, or only the one on `port` (#2683). */
|
|
170
|
+
export const selectByPort = (instances, port) => (port === null ? instances : instances.filter((i) => i.port === port));
|
|
171
|
+
|
|
172
|
+
/** What `stop --port` says when nothing is registered there — and what IS running, since a wrong
|
|
173
|
+
* port is the likeliest reason. */
|
|
174
|
+
export function noServerOnPortReport(port, instances) {
|
|
175
|
+
const running = instances.map(describeInstance);
|
|
176
|
+
return [`No MulmoTerminal server is running on port ${port}.`, running.length ? `Running: ${running.join(", ")}` : "MulmoTerminal is not running."];
|
|
159
177
|
}
|
|
160
178
|
|
|
161
179
|
export async function runStop(args = []) {
|
|
@@ -169,7 +187,16 @@ export async function runStop(args = []) {
|
|
|
169
187
|
console.error(STOP_USAGE);
|
|
170
188
|
process.exit(2);
|
|
171
189
|
}
|
|
172
|
-
const
|
|
173
|
-
|
|
190
|
+
const all = liveInstances();
|
|
191
|
+
const targets = selectByPort(all, parsed.port);
|
|
192
|
+
// Nothing on that port is the state asked for, like "nothing was running": not a failure.
|
|
193
|
+
if (parsed.port !== null && !targets.length) {
|
|
194
|
+
noServerOnPortReport(parsed.port, all).forEach((line) => console.log(line));
|
|
195
|
+
process.exit(0);
|
|
196
|
+
}
|
|
197
|
+
const result = await stopInstances(targets, { force: parsed.force });
|
|
198
|
+
// A retry must name the same server, or `--force` would stop every one.
|
|
199
|
+
const stopCommand = parsed.port === null ? selfStopCommand() : `${selfStopCommand()} --port ${parsed.port}`;
|
|
200
|
+
stopReport(result, process.platform, stopCommand).forEach((line) => console.log(line));
|
|
174
201
|
process.exit(stopExitCode(result));
|
|
175
202
|
}
|
|
@@ -18,7 +18,7 @@ Otherwise add to `FAQ.md` in this folder (create it if there is none). **Add aft
|
|
|
18
18
|
never edit or reorder what is there** — the check compares the beginning of the file with what it held
|
|
19
19
|
before. For each question: the question as a `##` heading, word for word; the answer; and where it is
|
|
20
20
|
written (the document, and the place named as in the answer: 第4条第2項, or a section's heading in 「」 —
|
|
21
|
-
never chaff's `h1.3`). For a question the documents do not answer, say so — that is worth keeping
|
|
21
|
+
in an English FAQ, in "…" — never chaff's `h1.3`). For a question the documents do not answer, say so — that is worth keeping
|
|
22
22
|
too.
|
|
23
23
|
|
|
24
24
|
## Done when
|
|
@@ -15,12 +15,23 @@ RENDER_LIMIT_SECONDS=60
|
|
|
15
15
|
work=$(mktemp -d)
|
|
16
16
|
"$chrome" --headless=new --disable-gpu --no-first-run --user-data-dir="$work/profile" --virtual-time-budget=15000 --dump-dom "$url" >"$work/dom.html" 2>/dev/null &
|
|
17
17
|
pid=$!
|
|
18
|
-
|
|
18
|
+
# Chrome can still be writing its profile when it is stopped, and under set -e a failed rm in this trap would turn a page
|
|
19
|
+
# that rendered into a failed check; the scratch folder is left behind instead.
|
|
20
|
+
trap 'kill "$pid" 2>/dev/null || true; rm -rf "$work" 2>/dev/null || true' EXIT
|
|
19
21
|
waited=0
|
|
20
22
|
until grep -q "</html>" "$work/dom.html" 2>/dev/null || [ "$waited" -ge "$RENDER_LIMIT_SECONDS" ] || ! kill -0 "$pid" 2>/dev/null; do
|
|
21
23
|
sleep 1
|
|
22
24
|
waited=$((waited + 1))
|
|
23
25
|
done
|
|
26
|
+
# A Chrome that crashed before printing the page says nothing about the app: like no Chrome, it cannot judge.
|
|
27
|
+
if ! grep -q "</html>" "$work/dom.html" 2>/dev/null && ! kill -0 "$pid" 2>/dev/null; then
|
|
28
|
+
status=0
|
|
29
|
+
wait "$pid" || status=$?
|
|
30
|
+
if [ "$status" -ne 0 ]; then
|
|
31
|
+
echo "Chrome ($chrome) exited with status $status before printing the page; could not confirm that $url renders" >&2
|
|
32
|
+
exit 0
|
|
33
|
+
fi
|
|
34
|
+
fi
|
|
24
35
|
node -e 'let s="";process.stdin.on("data",(c)=>s+=c).on("end",()=>{
|
|
25
36
|
const body = (s.match(/<body[^>]*>([\s\S]*)<\/body>/i) || [, ""])[1];
|
|
26
37
|
const text = body.replace(/<(script|style|noscript)[\s\S]*?<\/\1>/gi, "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
@@ -17,7 +17,7 @@ The answers are in `.blueprint/answers.json`; the prompt names both pack directo
|
|
|
17
17
|
4. Something the answers do not settle is not guessed: write "未定" and add a line to
|
|
18
18
|
`.blueprint/open-questions.md` saying what is needed and why it matters.
|
|
19
19
|
5. Append both packs' `security/` checklists under a heading "必ず詰める点", unchanged.
|
|
20
|
-
6. Write in the language the user answered in. Nothing is built in this step; the next step stops for the user
|
|
20
|
+
6. Write in the language the build's prompt says the user reads (without one, the language the user answered in). Nothing is built in this step; the next step stops for the user
|
|
21
21
|
to read this spec.
|
|
22
22
|
|
|
23
23
|
Done when the check passes: `.blueprint/spec.md` exists and no `{{` placeholder is left in it.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Types for articles.mjs, which the pack runs with plain node and the specs import.
|
|
2
|
+
export type Article = { readonly address: string; readonly label: string; readonly body: string };
|
|
3
|
+
export type Row = { readonly old?: string | null; readonly new?: string | null; readonly change: string; readonly what?: string };
|
|
4
|
+
|
|
5
|
+
export declare const CHANGES: readonly string[];
|
|
6
|
+
export declare const articlesIn: (tree: unknown, text: string) => Article[];
|
|
7
|
+
export declare const mentions: (text: string, name: string) => boolean;
|
|
8
|
+
export declare const comparisonProblems: (rows: unknown, olds: readonly Article[], news: readonly Article[]) => string[];
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// Two versions of a document, article by article: each article's body as chaff's tree spans it, and whether the
|
|
2
|
+
// pairing an agent wrote says the truth about them. What changed is read by the agent; whether anything changed
|
|
3
|
+
// at all is decided here, by comparing text, so "unchanged" cannot be written over a changed article or the
|
|
4
|
+
// other way round.
|
|
5
|
+
|
|
6
|
+
export const CHANGES = ["same", "changed", "added", "removed"];
|
|
7
|
+
|
|
8
|
+
// Spacing aside: a reflowed line is not a change to what an article says.
|
|
9
|
+
const squash = (text) => String(text).replace(/\s+/gu, "");
|
|
10
|
+
|
|
11
|
+
const articleNodes = (node) => [
|
|
12
|
+
...(node?.kind === "article" && typeof node.address === "string" && node.address !== "" ? [node] : []),
|
|
13
|
+
...(Array.isArray(node?.children) ? node.children.flatMap(articleNodes) : []),
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
// The article's own number (第4条, Article 4) is where it sits, not what it says: a renumbered article is the same.
|
|
17
|
+
const withoutLabel = (text, label) => {
|
|
18
|
+
const trimmed = text.trimStart();
|
|
19
|
+
return typeof label === "string" && label !== "" && trimmed.startsWith(label) ? trimmed.slice(label.length) : trimmed;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/** Each article of `text` as `tree` (chaff tree --format json) reads it: its address, label and body. */
|
|
23
|
+
export const articlesIn = (tree, text) =>
|
|
24
|
+
articleNodes(tree).map((node) => {
|
|
25
|
+
const whole = String(text).slice(node.span?.start ?? 0, node.span?.end ?? 0);
|
|
26
|
+
return { address: node.address, label: node.attrs?.label ?? node.address, body: squash(withoutLabel(whole, node.attrs?.label)) };
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const rowShape = (row, index) => {
|
|
30
|
+
if (row === null || typeof row !== "object") return `row ${index + 1} is not an object`;
|
|
31
|
+
if (!CHANGES.includes(row.change)) return `row ${index + 1}: "change" must be one of ${CHANGES.join(", ")}`;
|
|
32
|
+
const has = (side) => typeof row[side] === "string" && row[side] !== "";
|
|
33
|
+
const wanted = { same: [true, true], changed: [true, true], added: [false, true], removed: [true, false] }[row.change];
|
|
34
|
+
if (has("old") !== wanted[0] || has("new") !== wanted[1])
|
|
35
|
+
return `row ${index + 1}: a "${row.change}" row has ${wanted[0] ? "an" : "no"} "old" and ${wanted[1] ? "a" : "no"} "new"`;
|
|
36
|
+
if (row.change === "changed" && !(typeof row.what === "string" && row.what.trim() !== "")) return `row ${index + 1}: say in "what" what changed`;
|
|
37
|
+
return null;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Every article of a version in exactly one row, and no row naming one that is not there.
|
|
41
|
+
const coverage = (rows, articles, side, version) => {
|
|
42
|
+
const named = rows.map((row) => row[side]).filter((address) => typeof address === "string" && address !== "");
|
|
43
|
+
const known = articles.map((article) => article.address);
|
|
44
|
+
return [
|
|
45
|
+
...known.filter((address) => !named.includes(address)).map((address) => `${version} ${labelOf(articles, address)}: in no row`),
|
|
46
|
+
...[...new Set(named.filter((address, index) => named.indexOf(address) !== index))].map(
|
|
47
|
+
(address) => `${version} ${labelOf(articles, address)}: in more than one row`,
|
|
48
|
+
),
|
|
49
|
+
...named.filter((address) => !known.includes(address)).map((address) => `${version} has no article at ${address}`),
|
|
50
|
+
];
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const labelOf = (articles, address) => articles.find((article) => article.address === address)?.label ?? address;
|
|
54
|
+
|
|
55
|
+
// A pair said to be the same must read the same, and one said to have changed must not.
|
|
56
|
+
const truth = (row, olds, news) => {
|
|
57
|
+
if (row.change !== "same" && row.change !== "changed") return null;
|
|
58
|
+
const [was, now] = [olds.find((article) => article.address === row.old), news.find((article) => article.address === row.new)];
|
|
59
|
+
if (!was || !now) return null;
|
|
60
|
+
const pair = `${labelOf(olds, row.old)} → ${labelOf(news, row.new)}`;
|
|
61
|
+
if (row.change === "same" && was.body !== now.body) return `${pair}: written "same", but the text differs`;
|
|
62
|
+
if (row.change === "changed" && was.body === now.body) return `${pair}: written "changed", but the text is the same`;
|
|
63
|
+
return null;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// A provision removed from the old version and added to the new one word for word is one that moved: a pair missed.
|
|
67
|
+
const missedPairs = (rows, olds, news) => {
|
|
68
|
+
const bodyOf = (articles, address) => articles.find((article) => article.address === address)?.body;
|
|
69
|
+
const added = rows.filter((row) => row.change === "added").map((row) => row.new);
|
|
70
|
+
return rows
|
|
71
|
+
.filter((row) => row.change === "removed")
|
|
72
|
+
.flatMap((row) => {
|
|
73
|
+
const body = bodyOf(olds, row.old);
|
|
74
|
+
const twin = added.find((address) => body !== undefined && bodyOf(news, address) === body);
|
|
75
|
+
return twin === undefined ? [] : [`${labelOf(olds, row.old)} (removed) and ${labelOf(news, twin)} (added) read the same: pair them as "same"`];
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/** What is wrong with `rows` (the pairing) for the articles `olds` and `news`. */
|
|
80
|
+
export const comparisonProblems = (rows, olds, news) => {
|
|
81
|
+
if (!Array.isArray(rows)) return ['the pairing needs a "rows" list'];
|
|
82
|
+
const shapes = rows.map(rowShape).filter((problem) => problem !== null);
|
|
83
|
+
if (shapes.length > 0) return shapes;
|
|
84
|
+
return [
|
|
85
|
+
...coverage(rows, olds, "old", "the old version"),
|
|
86
|
+
...coverage(rows, news, "new", "the new version"),
|
|
87
|
+
...rows.map((row) => truth(row, olds, news)).filter((problem) => problem !== null),
|
|
88
|
+
...missedPairs(rows, olds, news),
|
|
89
|
+
];
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// Where a name stops and another article's number would go on. A name ending in a letter or digit ("Article 1")
|
|
93
|
+
// stops at a word boundary, so "Article 12", "Article 1bis" and "Article 1-2" are other words. A name ending in any
|
|
94
|
+
// other character (第1条) goes on only into a branch number: a digit, or の/之/ノ with a numeral (第1条の2,
|
|
95
|
+
// 第二十一条の二) — 「第1条の委託料」 still names 第1条, and so does 「第1条第2項」.
|
|
96
|
+
// Letters of the Latin script (full-width ones too) and decimal digits of any width: kana and kanji end the word.
|
|
97
|
+
const WORD_END = /[\p{Script=Latin}\p{Nd}]$/u;
|
|
98
|
+
// A dash of any kind, a point or a slash between two word characters joins them into one number (1-2, 1–2, 1.2, 1/2).
|
|
99
|
+
const WORD_GOES_ON = /^(?:[\p{Script=Latin}\p{Nd}]|[\p{Pd}..//][\p{Script=Latin}\p{Nd}])/u;
|
|
100
|
+
const BRANCH_GOES_ON = /^(?:[0-90-9]|[の之ノ][0-90-9一二三四五六七八九十百千])/u;
|
|
101
|
+
|
|
102
|
+
/** Whether `text` names the article `name`: a match that goes on into a longer number names another article. */
|
|
103
|
+
export const mentions = (text, name) => {
|
|
104
|
+
const goesOn = WORD_END.test(name) ? WORD_GOES_ON : BRANCH_GOES_ON;
|
|
105
|
+
const source = String(text);
|
|
106
|
+
const escaped = name.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
107
|
+
const hits = [...source.matchAll(new RegExp(escaped, "gu"))].map((match) => match.index ?? 0);
|
|
108
|
+
return hits.some((index) => !goesOn.test(source.slice(index + name.length)));
|
|
109
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// The docs base holds the helpers every usecase on it shares (running chaff, reading Markdown sections).
|
|
2
|
+
// A usecase loads them from BLUEPRINT_BASE, which the executor sets for every check.
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
|
|
6
|
+
if (!process.env.BLUEPRINT_BASE) {
|
|
7
|
+
console.error("BLUEPRINT_BASE is not set: run this check through the blueprint executor");
|
|
8
|
+
process.exit(1);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** The URL of a module in the docs base's checks/, for `await import(...)`. */
|
|
12
|
+
export const fromBase = (file) => pathToFileURL(join(process.env.BLUEPRINT_BASE, "checks", file)).href;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// The pair step's check: .blueprint/comparison.json pairs every article of the old version with its article in the
|
|
2
|
+
// new one, and says truly whether each changed. Writes the pairing as a person reads it at the gate, and records
|
|
3
|
+
// what was checked so the report can tell the documents and the pairing were not touched since.
|
|
4
|
+
import { writeFileSync } from "node:fs";
|
|
5
|
+
import { fromBase } from "./base.mjs";
|
|
6
|
+
import { comparisonProblems } from "./articles.mjs";
|
|
7
|
+
import { comparisonText } from "./comparisonView.mjs";
|
|
8
|
+
import { readVersions } from "./versions.mjs";
|
|
9
|
+
const { fail, readJson } = await import(fromBase("chaff.mjs"));
|
|
10
|
+
const { fingerprint } = await import(fromBase("documents.mjs"));
|
|
11
|
+
|
|
12
|
+
const PAIRING = ".blueprint/comparison.json";
|
|
13
|
+
const READABLE = ".blueprint/comparison.txt";
|
|
14
|
+
const CHECKED = ".blueprint/.comparison-checked";
|
|
15
|
+
|
|
16
|
+
const versions = readVersions();
|
|
17
|
+
const rows = readJson(PAIRING, '{ "rows": [{ "old", "new", "change", "what" }] }')?.rows;
|
|
18
|
+
const problems = comparisonProblems(rows, versions.old.articles, versions.new.articles);
|
|
19
|
+
if (problems.length > 0) fail(`${PAIRING}:\n ${problems.join("\n ")}`);
|
|
20
|
+
writeFileSync(READABLE, comparisonText(rows, versions.old.articles, versions.new.articles));
|
|
21
|
+
writeFileSync(CHECKED, JSON.stringify({ old: versions.old.print, new: versions.new.print, pairing: fingerprint(PAIRING) }) + "\n");
|
|
22
|
+
console.log(`${rows.length} pair(s): every article accounted for`);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// The pairing as plain text a person reads at the gate: one line a pair, the old article first. The marks are the
|
|
2
|
+
// ones a diff uses, so they read the same in any language: "=" unchanged, "~" changed, "+" added, "-" removed.
|
|
3
|
+
|
|
4
|
+
const MARKS = { same: "=", changed: "~", added: "+", removed: "-" };
|
|
5
|
+
|
|
6
|
+
const labelOf = (articles, address) => (typeof address === "string" ? (articles.find((article) => article.address === address)?.label ?? address) : "");
|
|
7
|
+
|
|
8
|
+
const lineOf = (row, olds, news) => {
|
|
9
|
+
const pair = `${labelOf(olds, row.old)} → ${labelOf(news, row.new)}`.trim();
|
|
10
|
+
const what = typeof row.what === "string" && row.what.trim() !== "" ? ` ${row.what.split("\n").join(" ").trim()}` : "";
|
|
11
|
+
return `${MARKS[row.change] ?? "?"} ${pair}${what}`;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const comparisonText = (rows, olds, news) => [...rows.map((row) => lineOf(row, olds, news)), ""].join("\n");
|