mulmoterminal 6.4.0 → 6.6.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 +17 -12
- package/bin/cli-args.d.ts +2 -0
- package/bin/cli-args.js +41 -3
- package/bin/mulmoterminal.js +10 -0
- package/bin/node-install.d.ts +6 -0
- package/bin/node-install.js +81 -0
- package/blueprints/ask/checks/base.mjs +12 -0
- package/blueprints/ask/checks/replies.mjs +99 -0
- package/blueprints/ask/hearing.json +23 -0
- package/blueprints/ask/manifest.json +9 -0
- package/blueprints/ask/presets/keihi/keihi.md +19 -0
- package/blueprints/ask/presets.json +15 -0
- package/blueprints/ask/skills/answer/SKILL.md +60 -0
- package/blueprints/ask/skills/keep/SKILL.md +25 -0
- package/blueprints/ask/steps.json +19 -0
- package/blueprints/docs/checks/chaff.mjs +91 -0
- package/blueprints/docs/checks/chaff.sh +7 -0
- package/blueprints/docs/checks/dismissals.d.mts +10 -0
- package/blueprints/docs/checks/dismissals.mjs +75 -0
- package/blueprints/docs/checks/documents.mjs +33 -0
- package/blueprints/docs/checks/drafts.mjs +63 -0
- package/blueprints/docs/checks/markdown.mjs +84 -0
- package/blueprints/docs/checks/workspace.sh +26 -0
- package/blueprints/docs/manifest.json +12 -0
- package/blueprints/docs/plan.json +11 -0
- package/blueprints/docs/skills/workspace/SKILL.md +22 -0
- package/blueprints/firebase/checks/deploy-check.sh +18 -0
- package/blueprints/firebase/checks/emulator-test.sh +11 -0
- package/blueprints/firebase/checks/google-signin.sh +14 -0
- package/blueprints/firebase/checks/java21.sh +28 -0
- package/blueprints/firebase/checks/page-renders.sh +28 -0
- package/blueprints/firebase/checks/prerequisites.sh +18 -0
- package/blueprints/firebase/checks/project-id.sh +9 -0
- package/blueprints/firebase/checks/projects.sh +12 -0
- package/blueprints/firebase/checks/protect.sh +30 -0
- package/blueprints/firebase/checks/scaffold.sh +12 -0
- package/blueprints/firebase/checks/spec.sh +8 -0
- package/blueprints/firebase/guides/app-check.md +13 -0
- package/blueprints/firebase/guides/billing.md +13 -0
- package/blueprints/firebase/guides/google-signin.md +14 -0
- package/blueprints/firebase/guides/sign-in.md +14 -0
- package/blueprints/firebase/guides/try-it.md +6 -0
- package/blueprints/firebase/infra/firebase.json +59 -0
- package/blueprints/firebase/infra/firestore.indexes.json +4 -0
- package/blueprints/firebase/infra/firestore.rules +9 -0
- package/blueprints/firebase/manifest.json +39 -0
- package/blueprints/firebase/plan.json +71 -0
- package/blueprints/firebase/security/checklist.md +11 -0
- package/blueprints/firebase/skills/auth/SKILL.md +22 -0
- package/blueprints/firebase/skills/data-rules/SKILL.md +28 -0
- package/blueprints/firebase/skills/deploy-dev/SKILL.md +25 -0
- package/blueprints/firebase/skills/deploy-production/SKILL.md +24 -0
- package/blueprints/firebase/skills/prerequisites/SKILL.md +29 -0
- package/blueprints/firebase/skills/projects/SKILL.md +35 -0
- package/blueprints/firebase/skills/protect/SKILL.md +23 -0
- package/blueprints/firebase/skills/scaffold/SKILL.md +33 -0
- package/blueprints/firebase/skills/spec/SKILL.md +30 -0
- package/blueprints/firebase/spec/overview.md +34 -0
- package/blueprints/internal/checks/auth-config.sh +7 -0
- package/blueprints/internal/checks/blocking-registered.sh +12 -0
- package/blueprints/internal/checks/identity-platform.sh +11 -0
- package/blueprints/internal/guides/blocking-functions.md +13 -0
- package/blueprints/internal/guides/identity-platform.md +11 -0
- package/blueprints/internal/guides/try-refusal.md +15 -0
- package/blueprints/internal/hearing.json +112 -0
- package/blueprints/internal/infra/firestore.rules +38 -0
- package/blueprints/internal/manifest.json +8 -0
- package/blueprints/internal/presets.json +26 -0
- package/blueprints/internal/security/threats.md +9 -0
- package/blueprints/internal/skills/audit-log/SKILL.md +23 -0
- package/blueprints/internal/skills/domain-restriction/SKILL.md +44 -0
- package/blueprints/internal/skills/offboarding/SKILL.md +27 -0
- package/blueprints/internal/skills/production-auth/SKILL.md +26 -0
- package/blueprints/internal/skills/roles/SKILL.md +26 -0
- package/blueprints/internal/skills/verify-dev/SKILL.md +22 -0
- package/blueprints/internal/spec/requirements.md +32 -0
- package/blueprints/internal/steps.json +53 -0
- package/blueprints/local/checks/handover.sh +6 -0
- package/blueprints/local/checks/node-sqlite.sh +6 -0
- package/blueprints/local/checks/scaffold.sh +12 -0
- package/blueprints/local/checks/smoke.sh +34 -0
- package/blueprints/local/checks/spec.sh +8 -0
- package/blueprints/local/checks/tests-pass.sh +7 -0
- package/blueprints/local/manifest.json +21 -0
- package/blueprints/local/plan.json +68 -0
- package/blueprints/local/security/checklist.md +9 -0
- package/blueprints/local/skills/api/SKILL.md +22 -0
- package/blueprints/local/skills/auth/SKILL.md +27 -0
- package/blueprints/local/skills/data/SKILL.md +22 -0
- package/blueprints/local/skills/handover/SKILL.md +23 -0
- package/blueprints/local/skills/prerequisites/SKILL.md +22 -0
- package/blueprints/local/skills/run-check/SKILL.md +19 -0
- package/blueprints/local/skills/scaffold/SKILL.md +35 -0
- package/blueprints/local/skills/spec/SKILL.md +29 -0
- package/blueprints/local/skills/ui/SKILL.md +22 -0
- package/blueprints/local/spec/overview.md +33 -0
- package/blueprints/polish/checks/base.mjs +12 -0
- package/blueprints/polish/checks/feedback.mjs +9 -0
- package/blueprints/polish/checks/report.mjs +34 -0
- package/blueprints/polish/checks/targets.mjs +115 -0
- package/blueprints/polish/hearing.json +37 -0
- package/blueprints/polish/manifest.json +9 -0
- package/blueprints/polish/presets/oshirase/oshirase.md +7 -0
- package/blueprints/polish/presets.json +17 -0
- package/blueprints/polish/skills/polish/SKILL.md +60 -0
- package/blueprints/polish/skills/report/SKILL.md +34 -0
- package/blueprints/polish/skills/survey/SKILL.md +43 -0
- package/blueprints/polish/steps.json +27 -0
- package/blueprints/product/hearing.json +92 -0
- package/blueprints/product/manifest.json +8 -0
- package/blueprints/product/presets.json +101 -0
- package/blueprints/product/security/threats.md +15 -0
- package/blueprints/product/skills/acceptance/SKILL.md +26 -0
- package/blueprints/product/skills/acceptance-firebase/SKILL.md +27 -0
- package/blueprints/product/skills/features-firebase/SKILL.md +28 -0
- package/blueprints/product/spec/requirements.md +42 -0
- package/blueprints/product/steps.json +30 -0
- package/blueprints/refactor/checks/prs.sh +22 -0
- package/blueprints/refactor/checks/report.sh +6 -0
- package/blueprints/refactor/checks/targets.mjs +114 -0
- package/blueprints/refactor/checks/tranche.sh +10 -0
- package/blueprints/refactor/hearing.json +44 -0
- package/blueprints/refactor/manifest.json +9 -0
- package/blueprints/refactor/skills/report/SKILL.md +26 -0
- package/blueprints/refactor/skills/survey/SKILL.md +87 -0
- package/blueprints/refactor/skills/tranche/SKILL.md +172 -0
- package/blueprints/refactor/skills/tranche/cross-review.md +72 -0
- package/blueprints/refactor/steps.json +27 -0
- package/blueprints/repo/checks/ci-check.sh +36 -0
- package/blueprints/repo/checks/ci-recorded.sh +10 -0
- package/blueprints/repo/checks/default-branch.sh +4 -0
- package/blueprints/repo/checks/gates.sh +24 -0
- package/blueprints/repo/checks/repo-check.sh +14 -0
- package/blueprints/repo/guides/gh-login.md +12 -0
- package/blueprints/repo/guides/trust-repo.md +10 -0
- package/blueprints/repo/manifest.json +13 -0
- package/blueprints/repo/plan.json +25 -0
- package/blueprints/repo/skills/baseline/SKILL.md +42 -0
- package/blueprints/repo/skills/ci/SKILL.md +35 -0
- package/blueprints/repo/skills/state/SKILL.md +32 -0
- package/blueprints/review/checks/base.mjs +12 -0
- package/blueprints/review/checks/candidates.d.mts +11 -0
- package/blueprints/review/checks/candidates.mjs +43 -0
- package/blueprints/review/checks/feedback.mjs +14 -0
- package/blueprints/review/checks/findings.mjs +114 -0
- package/blueprints/review/checks/report.mjs +29 -0
- package/blueprints/review/hearing.json +31 -0
- package/blueprints/review/manifest.json +9 -0
- package/blueprints/review/presets/itaku-keiyaku/contract.txt +31 -0
- package/blueprints/review/presets.json +16 -0
- package/blueprints/review/skills/propose/SKILL.md +36 -0
- package/blueprints/review/skills/read/SKILL.md +77 -0
- package/blueprints/review/skills/report/SKILL.md +34 -0
- package/blueprints/review/steps.json +26 -0
- package/blueprints/style/checks/base.mjs +12 -0
- package/blueprints/style/checks/counter.mjs +29 -0
- package/blueprints/style/checks/report.mjs +18 -0
- package/blueprints/style/checks/rules.mjs +56 -0
- package/blueprints/style/checks/sources.mjs +37 -0
- package/blueprints/style/hearing.json +42 -0
- package/blueprints/style/manifest.json +9 -0
- package/blueprints/style/presets/shanai/tehon-1.md +19 -0
- package/blueprints/style/presets/shanai/tehon-2.md +15 -0
- package/blueprints/style/presets/shanai/tehon-3.md +24 -0
- package/blueprints/style/presets/shanai/tehon-4.md +23 -0
- package/blueprints/style/presets/shanai/tehon-5.md +20 -0
- package/blueprints/style/presets/shanai/tehon-6.md +20 -0
- package/blueprints/style/presets/shanai/tehon-7.md +25 -0
- package/blueprints/style/presets/shanai/tehon-8.md +24 -0
- package/blueprints/style/presets.json +17 -0
- package/blueprints/style/skills/counter/SKILL.md +29 -0
- package/blueprints/style/skills/report/SKILL.md +17 -0
- package/blueprints/style/skills/rules/SKILL.md +80 -0
- package/blueprints/style/skills/sources/SKILL.md +35 -0
- package/blueprints/style/steps.json +34 -0
- package/blueprints/verify/checks/base.mjs +12 -0
- package/blueprints/verify/checks/extract.mjs +35 -0
- package/blueprints/verify/checks/facts.d.mts +13 -0
- package/blueprints/verify/checks/facts.mjs +187 -0
- package/blueprints/verify/checks/report.mjs +51 -0
- package/blueprints/verify/checks/rules.d.mts +21 -0
- package/blueprints/verify/checks/rules.mjs +142 -0
- package/blueprints/verify/hearing.json +31 -0
- package/blueprints/verify/manifest.json +9 -0
- package/blueprints/verify/presets/osaka-kyoto/itinerary.md +29 -0
- package/blueprints/verify/presets.json +16 -0
- package/blueprints/verify/skills/extract/SKILL.md +64 -0
- package/blueprints/verify/skills/report/SKILL.md +39 -0
- package/blueprints/verify/steps.json +19 -0
- package/blueprints/write/checks/base.mjs +12 -0
- package/blueprints/write/checks/brief.mjs +42 -0
- package/blueprints/write/checks/feedback.mjs +10 -0
- package/blueprints/write/checks/parts.mjs +102 -0
- package/blueprints/write/checks/report.mjs +36 -0
- package/blueprints/write/hearing.json +35 -0
- package/blueprints/write/manifest.json +9 -0
- package/blueprints/write/presets.json +19 -0
- package/blueprints/write/skills/brief/SKILL.md +43 -0
- package/blueprints/write/skills/draft/SKILL.md +63 -0
- package/blueprints/write/skills/outline/SKILL.md +30 -0
- package/blueprints/write/skills/report/SKILL.md +35 -0
- package/blueprints/write/steps.json +35 -0
- package/common/blueprint/changedFiles.ts +38 -0
- package/common/blueprint/executorPolicy.ts +56 -0
- package/common/blueprint/hearing.ts +98 -0
- package/common/blueprint/manifest.ts +53 -0
- package/common/blueprint/plan.ts +88 -0
- package/common/blueprint/presets.ts +24 -0
- package/common/blueprint/refusal.ts +44 -0
- package/common/blueprint/registry.ts +82 -0
- package/common/blueprint/relativePath.ts +7 -0
- package/common/blueprint/run.ts +75 -0
- package/common/blueprint/samples.ts +24 -0
- package/common/blueprint/specRevisionPrompt.ts +36 -0
- package/common/blueprint/state.ts +193 -0
- package/common/blueprint/stepNotice.ts +29 -0
- package/common/blueprint/stepPrompt.ts +62 -0
- package/common/githubIcons.ts +36 -0
- package/common/keymap.ts +1 -0
- package/common/markedUnread.ts +6 -0
- package/dist/assets/{abnfDiagram-O67JEVCF-0EIO6Rib.js → abnfDiagram-O67JEVCF-BypT3U3a.js} +1 -1
- package/dist/assets/architecture-7GRP2DOG-Bbv-2KeL.js +1 -0
- package/dist/assets/{architectureDiagram-NJMV4G6O-Dv4vmoGJ.js → architectureDiagram-NJMV4G6O-CyObEvXl.js} +1 -1
- package/dist/assets/{blockDiagram-BEXU5L5S-DtAUYedX.js → blockDiagram-BEXU5L5S-D-HVBeq1.js} +1 -1
- package/dist/assets/{c4Diagram-YGBWAQC7-BUjpgg36.js → c4Diagram-YGBWAQC7-Qgd--us_.js} +1 -1
- package/dist/assets/channel-BHcWpvnw.js +1 -0
- package/dist/assets/{chunk-3FUC2YCW-BRaUCmDn.js → chunk-3FUC2YCW-BR-5aCbb.js} +2 -2
- package/dist/assets/{chunk-5DYCD2WN-BcCVJxNU.js → chunk-5DYCD2WN-Bo8hecoJ.js} +1 -1
- package/dist/assets/{chunk-742MDFTN-r7J0AnEC.js → chunk-742MDFTN-CD8LfZUq.js} +1 -1
- package/dist/assets/{chunk-7INBJB4K-BgjDPpSK.js → chunk-7INBJB4K-CX2KLcpU.js} +1 -1
- package/dist/assets/{chunk-7M6MHVWA-BkohiMAa.js → chunk-7M6MHVWA-EAATiixY.js} +1 -1
- package/dist/assets/{chunk-7PRAP22T-BgtGs2QT.js → chunk-7PRAP22T-kBBJ0KJk.js} +1 -1
- package/dist/assets/{chunk-GTNCS2PH-CabGHxDI.js → chunk-GTNCS2PH-BMtqOq18.js} +1 -1
- package/dist/assets/{chunk-GWA4HPMP-ByBWBjP8.js → chunk-GWA4HPMP-B0Mi72WR.js} +1 -1
- package/dist/assets/{chunk-MBY4JIJT-CoqxPGVJ.js → chunk-MBY4JIJT-DmHkAdrQ.js} +1 -1
- package/dist/assets/{chunk-NETBCI7D-C3TzGKdM.js → chunk-NETBCI7D-CXGvJJei.js} +1 -1
- package/dist/assets/{chunk-O7XYJQB3-C_ibJVHM.js → chunk-O7XYJQB3-WBMmFQya.js} +1 -1
- package/dist/assets/{chunk-UA2S7LBM-cdcwqQWa.js → chunk-UA2S7LBM-DaQ23UEn.js} +1 -1
- package/dist/assets/{chunk-WEXAMYUT-DpgQkB4Y.js → chunk-WEXAMYUT-DviQkb8I.js} +1 -1
- package/dist/assets/{chunk-Z7XXMR3K-43mpKAmk.js → chunk-Z7XXMR3K-DOtBujlQ.js} +1 -1
- package/dist/assets/{chunk-ZIGJFQKS-BqjohiQA.js → chunk-ZIGJFQKS-VtmgG3xL.js} +1 -1
- package/dist/assets/{classDiagram-v2-NBCMYWYE--RyRI2eN.js → classDiagram-v2-NBCMYWYE-ndrP6enZ.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-Dr5PAqCO.js → cynefin-OW5HDTMX-5iOYoFuE.js} +1 -1
- package/dist/assets/{cynefinDiagram-VND7K2PF-CsJyNlep.js → cynefinDiagram-VND7K2PF-DWaNHdmF.js} +1 -1
- package/dist/assets/{dagre-6A5THRUB-C5ja50BF.js → dagre-6A5THRUB-CRI-Ki8R.js} +1 -1
- package/dist/assets/{diagram-22UHCM2B-C4cl0V63.js → diagram-22UHCM2B-Cb4_8V3f.js} +1 -1
- package/dist/assets/{diagram-3UASUU5V-DV_eMiwc.js → diagram-3UASUU5V--mTYdfxz.js} +1 -1
- package/dist/assets/{diagram-ATOU4E4O-jzKf3iFX.js → diagram-ATOU4E4O-BwY4-yUt.js} +1 -1
- package/dist/assets/{diagram-CDSNMT55-BwFqusNo.js → diagram-CDSNMT55-CW8ENZKa.js} +1 -1
- package/dist/assets/{diagram-MLGK6HIB-CoMwibZN.js → diagram-MLGK6HIB-Ew0oj4NU.js} +1 -1
- package/dist/assets/{diagram-MPIPVDR6-CR1GehW4.js → diagram-MPIPVDR6-BK0uRGQB.js} +1 -1
- package/dist/assets/{dist-DzuN4wOr.js → dist-CNs34037.js} +1 -1
- package/dist/assets/{dist-Cl-PfToj.js → dist-Cp7rDLig.js} +1 -1
- package/dist/assets/{dist-BtevH0MI.js → dist-DRZ3y7AE.js} +1 -1
- package/dist/assets/{dist-4y2pKI__.js → dist-jO1cPpPw.js} +1 -1
- package/dist/assets/{ebnfDiagram-ZINNZB2B-DofcoTlD.js → ebnfDiagram-ZINNZB2B-DGu-I1_-.js} +1 -1
- package/dist/assets/{elk-276RUBZZ-BPEoPGYi.js → elk-276RUBZZ-IKdDujmV.js} +1 -1
- package/dist/assets/{erDiagram-OPXOYQCR-BW9UZdWF.js → erDiagram-OPXOYQCR-CLcXgJdC.js} +1 -1
- package/dist/assets/eventmodeling-NTZA5JFV-9UzRuTrz.js +1 -0
- package/dist/assets/flowDiagram-KWPJA3E3-Bq9iQFMs.js +1 -0
- package/dist/assets/{ganttDiagram-FUAMR5RP-QI2xjALT.js → ganttDiagram-FUAMR5RP-lfct59GT.js} +1 -1
- package/dist/assets/{gitGraph-4MIJSDKK-DwWl22rs.js → gitGraph-4MIJSDKK-Hr_vAl58.js} +1 -1
- package/dist/assets/{gitGraphDiagram-X574FWY7-OdBtq_J3.js → gitGraphDiagram-X574FWY7-DyTAObOG.js} +1 -1
- package/dist/assets/index-BHeY7dDr.css +1 -0
- package/dist/assets/{index-DZ3P7G29.js → index-BfCtnfM3.js} +410 -400
- package/dist/assets/{info-A6RAGUB7-D4dgNXV5.js → info-A6RAGUB7-C2RIrvn7.js} +1 -1
- package/dist/assets/{infoDiagram-VRGFBTTK-BLafPiGw.js → infoDiagram-VRGFBTTK-Cehr7nT0.js} +1 -1
- package/dist/assets/{ishikawaDiagram-OU5B5YK6-hNUEv0D2.js → ishikawaDiagram-OU5B5YK6-DNo8VfZy.js} +1 -1
- package/dist/assets/{journeyDiagram-ZHPQQLJL-COYfA96w.js → journeyDiagram-ZHPQQLJL-BjQHzZxG.js} +1 -1
- package/dist/assets/{kanban-definition-PNTS6WVX-C5jDklyw.js → kanban-definition-PNTS6WVX-CSr7kyll.js} +1 -1
- package/dist/assets/{line-D8KBsMtt.js → line-CmNgViQc.js} +1 -1
- package/dist/assets/{mermaid-parser.core-Dlf5XX-m.js → mermaid-parser.core-C_qcrA5s.js} +3 -3
- package/dist/assets/{mermaid.core-77q0N4Kj.js → mermaid.core-BG82qqEB.js} +4 -4
- package/dist/assets/{mindmap-definition-NLK3R4M7-Bh3ST-uc.js → mindmap-definition-NLK3R4M7-psSKkeLb.js} +1 -1
- package/dist/assets/{packet-AYTQ26CC-dzdfTl_4.js → packet-AYTQ26CC-BS5zpaST.js} +1 -1
- package/dist/assets/{pegDiagram-GJSIUBJH-HUFfoKUw.js → pegDiagram-GJSIUBJH-DhpL7ygV.js} +1 -1
- package/dist/assets/{pie-WAS4IAKB-0S5KvqDy.js → pie-WAS4IAKB-T5-dog1P.js} +1 -1
- package/dist/assets/{pieDiagram-5QR66LMP-BAUKrsuB.js → pieDiagram-5QR66LMP-CmXcDzDT.js} +1 -1
- package/dist/assets/{quadrantDiagram-O4NWA36T-WKXQPbHy.js → quadrantDiagram-O4NWA36T-BIDYLhBi.js} +1 -1
- package/dist/assets/{radar-RG4KPBEZ-CmvfbUxs.js → radar-RG4KPBEZ-_895RNt8.js} +1 -1
- package/dist/assets/{railroad-74A4TZTK-B1eU1nZI.js → railroad-74A4TZTK-Cz2ZyH7U.js} +1 -1
- package/dist/assets/railroad-abnf-HS5TGJTU-CTBfwbDI.js +1 -0
- package/dist/assets/railroad-ebnf-LZEXJU2U-BxMnP09p.js +1 -0
- package/dist/assets/railroad-peg-WCYAUIDC-VQLBtQ7q.js +1 -0
- package/dist/assets/{railroadDiagram-XR7U4H2S-DI3y2Box.js → railroadDiagram-XR7U4H2S-BQ1Mc95v.js} +1 -1
- package/dist/assets/{requirementDiagram-PLB6GJNP-BHVnyaGP.js → requirementDiagram-PLB6GJNP-ChVue_nk.js} +1 -1
- package/dist/assets/{sankeyDiagram-IPEJSGJF-DPhG8F4e.js → sankeyDiagram-IPEJSGJF-Cd9K0txb.js} +1 -1
- package/dist/assets/{sequenceDiagram-PO4LG4MO-BNnXeMyc.js → sequenceDiagram-PO4LG4MO-kiPuazKP.js} +1 -1
- package/dist/assets/sizeCapture-INFHLROL-3uHXrFqA.js +1 -0
- package/dist/assets/{stateDiagram-v2-GCMORJYK-Dg80rzkM.js → stateDiagram-v2-GCMORJYK-BrYqOUDC.js} +1 -1
- package/dist/assets/{swimlanes-2SLR337P-BjIVtOvB.js → swimlanes-2SLR337P-BGv93v9F.js} +1 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-XHEHPbk9.js +8 -0
- package/dist/assets/{timeline-definition-EJHVYXUP-CWVsm4DO.js → timeline-definition-EJHVYXUP-B2m2999q.js} +1 -1
- package/dist/assets/{treeView-Q6P3EWNA-VIzH5Ryp.js → treeView-Q6P3EWNA-Bp2HlEG5.js} +1 -1
- package/dist/assets/{treemap-WGGIJYW6-DIurGTrO.js → treemap-WGGIJYW6-BBhRxIiH.js} +1 -1
- package/dist/assets/{usecaseDiagram-POWQR4AR-CGjjyVXQ.js → usecaseDiagram-POWQR4AR-uFIx7ToF.js} +1 -1
- package/dist/assets/{vennDiagram-UO4OBE2U-D25_R90R.js → vennDiagram-UO4OBE2U-BQGzVBTB.js} +1 -1
- package/dist/assets/{wardley-WFR3VGLG-AZXL1jj9.js → wardley-WFR3VGLG-BQk6DDg6.js} +1 -1
- package/dist/assets/{wardleyDiagram-VNRHLVJA-D3g7xxxA.js → wardleyDiagram-VNRHLVJA-q-_GY-qU.js} +1 -1
- package/dist/assets/{xychartDiagram-PMCCYNJV-C5FxR_Fd.js → xychartDiagram-PMCCYNJV-B8D1QFx-.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +22 -21
- package/server/blueprint/answersFile.ts +11 -0
- package/server/blueprint/checkRunner.ts +78 -0
- package/server/blueprint/executor.ts +490 -0
- package/server/blueprint/executorLock.ts +96 -0
- package/server/blueprint/installer.ts +142 -0
- package/server/blueprint/marketRoutes.ts +104 -0
- package/server/blueprint/packs.ts +141 -0
- package/server/blueprint/projectFiles.ts +112 -0
- package/server/blueprint/registry.ts +68 -0
- package/server/blueprint/routes.ts +222 -0
- package/server/blueprint/runStore.ts +49 -0
- package/server/blueprint/samples.ts +53 -0
- package/server/blueprint/trust.ts +99 -0
- package/server/blueprint/wiring.ts +167 -0
- package/server/config/app-config.ts +2 -2
- package/server/config/config-schema.ts +25 -1
- package/server/config/dir-config.ts +2 -2
- package/server/config/header-config.ts +87 -28
- package/server/config/header-resolve.ts +27 -5
- package/server/git/spawn-collect.ts +11 -2
- package/server/index.ts +1 -1
- package/server/routes/app-routes.ts +3 -0
- package/server/session/pty-connection.ts +31 -12
- package/server/skills/mulmoterminal-header/SKILL.md +37 -13
- package/server/skills/mulmoterminal-keys/SKILL.md +4 -2
- package/common/githubPaneOrder.ts +0 -83
- package/dist/assets/architecture-7GRP2DOG-BrJq_YrR.js +0 -1
- package/dist/assets/channel-1p-80xtZ.js +0 -1
- package/dist/assets/eventmodeling-NTZA5JFV-B13TyR96.js +0 -1
- package/dist/assets/flowDiagram-KWPJA3E3-CU0tsDCp.js +0 -1
- package/dist/assets/index-BMqji566.css +0 -1
- package/dist/assets/railroad-abnf-HS5TGJTU-Buwlc453.js +0 -1
- package/dist/assets/railroad-ebnf-LZEXJU2U-CTzd-w0L.js +0 -1
- package/dist/assets/railroad-peg-WCYAUIDC-BzSzMdhx.js +0 -1
- package/dist/assets/sizeCapture-INFHLROL-B0uUizjq.js +0 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-_i8YJGRx.js +0 -8
package/README.md
CHANGED
|
@@ -63,7 +63,9 @@ npx mulmoterminal@latest # starts on http://localhost:34567 and opens you
|
|
|
63
63
|
Requires **Node ≥ 22.12**, and by default the [`claude`](https://claude.com/claude-code) CLI —
|
|
64
64
|
on your `PATH` or named by `CLAUDE_BIN`, already logged in. Declare a different default agent
|
|
65
65
|
(`--agent codex`, or `"defaultAgent"` in `~/.mulmoterminal/config.json`) and start-up checks that
|
|
66
|
-
one instead. `npx mulmoterminal@latest init` reports what it can't find.
|
|
66
|
+
one instead. `npx mulmoterminal@latest init` reports what it can't find. On an older Node it stops
|
|
67
|
+
before starting the server, with a banner and the upgrade commands for however that Node was
|
|
68
|
+
installed.
|
|
67
69
|
|
|
68
70
|
### Why not tmux + iTerm panes?
|
|
69
71
|
|
|
@@ -277,7 +279,7 @@ Needs **Node ≥ 22.12**, plus these CLIs — on your `PATH`, or named by the ma
|
|
|
277
279
|
| Optional | any other agent CLI | a cell can run **Codex**, **Antigravity** (`agy`), **Grok**, **Muse**, **GitHub Copilot CLI** or **Cursor CLI** instead of Claude — install only the ones you use, and a missing one simply fails to start that cell. What each can do is [the capability matrix](docs/agent-capability-matrix.md); how to install and pick one is the [agents guide](https://receptron.github.io/mulmoterminal/guide/en/agents.html) | e.g. `npm i -g @openai/codex` |
|
|
278
280
|
| Optional | `ffmpeg` | video rendering from the [mulmo-script panel](#wiki-collections--the-gui-panel) (its plugin ships enabled) | `brew install ffmpeg` · `sudo apt install ffmpeg` · `sudo dnf install ffmpeg` |
|
|
279
281
|
| Optional | `ollama` | [`claude-ollama`](https://receptron.github.io/mulmoterminal/guide/en/claude-ollama.html) — Claude Code against a fully local model | [ollama.com/download](https://ollama.com/download) |
|
|
280
|
-
| Linux only | a file dialog | the **Choose a folder
|
|
282
|
+
| Linux only | a file dialog | the launcher's **Choose a folder** button and the path menu's **Insert a file path**, which open an OS dialog on the machine the server runs on. macOS and Windows have one built in; **WSL** uses the Windows one over interop and needs nothing installed. A Linux desktop needs one of these — without any, they say so and you type the path instead (#1447) | `sudo apt install zenity` · `sudo dnf install zenity` · `kdialog`, `qarma` and `yad` also work |
|
|
281
283
|
|
|
282
284
|
The server starts without any of the non-required rows; you just lose that row's feature,
|
|
283
285
|
and the header/panel for it says so. `git` and `gh` are marked required because losing them
|
|
@@ -810,9 +812,9 @@ easier to debug than one that silently vanished. See the
|
|
|
810
812
|
#### Header buttons
|
|
811
813
|
|
|
812
814
|
Each terminal header shows configurable **action buttons**. Omitting `buttons` (globally or per-dir)
|
|
813
|
-
keeps the built-in **starter set**:
|
|
814
|
-
file
|
|
815
|
-
|
|
815
|
+
keeps the built-in **starter set**: this branch's PR (git repos, only when a PR exists). Inserting a
|
|
816
|
+
file path, revealing the folder, the in-app file explorer, a new terminal here and the GitHub links
|
|
817
|
+
are items in the **path menu** (click the directory path on the terminal header). Setting `buttons` (at either level) **replaces the whole default
|
|
816
818
|
set** with your list (it is not merged on top), so listing your own — even a **shorter** one — is how
|
|
817
819
|
you drop, reorder, or swap them.
|
|
818
820
|
A button has an `id`, `label`, and a `run` of `"shell"` (run a command), `"input"` (send text to the
|
|
@@ -822,6 +824,8 @@ change takes effect; it costs a resume and asks nothing first). An `open` button
|
|
|
822
824
|
(in-app explorer) / `view` (a built-in overlay) / `terminal` (a dir → a new cell running `$SHELL`,
|
|
823
825
|
opened next to the current one) / `pr: true` (open the current branch's PR — the button is hidden when
|
|
824
826
|
there's no open PR) / `pickFile: true` (OS file dialog → insert the path).
|
|
827
|
+
An entry with `items` instead of `run` is a **folder**: one icon that opens a menu of the buttons
|
|
828
|
+
inside it (one level only — a folder cannot hold a folder).
|
|
825
829
|
`${dir}`, `${branch}`, `${repo}`, … substitute live context, and `when` (e.g. `"isGitRepo"`) gates
|
|
826
830
|
visibility. The `/mulmoterminal-header` skill writes a valid config interactively; per-dir buttons
|
|
827
831
|
merge over the global ones by `id`, while `chips` replace the global list wholesale.
|
|
@@ -988,7 +992,7 @@ Settings → Directory settings names both files and lists which keys the local
|
|
|
988
992
|
| Field | Meaning |
|
|
989
993
|
| ------------ | ------- |
|
|
990
994
|
| `name` | Label shown as a badge in the terminal/cell header. |
|
|
991
|
-
| `icon` | An **image** marking this directory — shown in the cell header, the cockpit roster, the filmstrip thumbnails, the launcher's directory chips, and the phone's terminal list and terminal screen. Either a path **relative to this directory** (an absolute path, or a `../` that escapes it, is rejected), an `http(s)://` URL, or a `data:image/…` URI. PNG / JPEG / **GIF (animated plays)** / WebP / AVIF / SVG / ICO / BMP. Not to be confused with a header **button's** `icon`, which is a Material Symbols name, or with the small glyph a chat started from a **collection** wears beside its status dot — that one is the collection's own `icon`, and it says which collection the cell was opened for while this one says which directory it runs in. **Omit it and the repository's own favicon is used** (`public/favicon.svg`, `apple-touch-icon.png`, a web manifest — see `autoDirIcon`); `false` means no icon here and stops that search. |
|
|
995
|
+
| `icon` | An **image** marking this directory — shown in the cell header, the cockpit roster, the filmstrip thumbnails, the launcher's directory chips, and the phone's terminal list and terminal screen. Either a path **relative to this directory** (an absolute path, or a `../` that escapes it, is rejected), an `http(s)://` URL, or a `data:image/…` URI. PNG / JPEG / **GIF (animated plays)** / WebP / AVIF / SVG / ICO / BMP. Not to be confused with a header **button's** `icon`, which is a Material Symbols name (or `github:<name>` for one of GitHub's icons), or with the small glyph a chat started from a **collection** wears beside its status dot — that one is the collection's own `icon`, and it says which collection the cell was opened for while this one says which directory it runs in. **Omit it and the repository's own favicon is used** (`public/favicon.svg`, `apple-touch-icon.png`, a web manifest — see `autoDirIcon`); `false` means no icon here and stops that search. |
|
|
992
996
|
| `backgroundImage` | A **picture shown faintly behind this directory's terminals**, blended with the text so it stays readable on either theme. A string is the image (15% opaque, fills the terminal); `{ "image", "opacity", "fit" }` sets how faint (above 0, at most 1) and whether it crops to fill (`cover`) or shows whole (`contain`). The image follows `icon`'s rules. |
|
|
993
997
|
| `badgeColor` | Badge background color (`#rrggbb`); text auto-contrasts. |
|
|
994
998
|
| `headerColor` | Header **background** color (`#rrggbb`) — the grid cell's header row and the terminal's own header row (grid row 2). While a terminal is working/blocked the status tint still shows; the custom color applies when idle. |
|
|
@@ -1422,8 +1426,8 @@ A worktree cell's header carries a **diff badge** (`+<commits> ●<dirty>`); cli
|
|
|
1422
1426
|
Closing a worktree cell asks whether to **keep** the worktree or **discard & remove** it
|
|
1423
1427
|
(a dirty worktree is never removed unless you confirm).
|
|
1424
1428
|
|
|
1425
|
-
**PRs & Issues (cross-repo).** The toolbar's **Pull requests** button
|
|
1426
|
-
view that aggregates open PRs **and** issues across the repos listed in Settings →
|
|
1429
|
+
**PRs & Issues (cross-repo).** The toolbar's **Pull requests** button (shown once at least one
|
|
1430
|
+
repository is listed) opens a full-screen view that aggregates open PRs **and** issues across the repos listed in Settings →
|
|
1427
1431
|
**Pull request repos** (`prRepos`, `owner/repo` entries, or `gitlab.com/group/project` — plus any host declared in `gitlabHosts`) via your server-side `gh` / `glab` login.
|
|
1428
1432
|
PRs show a CI-rollup / review-decision / draft badge; each repo lists its latest open
|
|
1429
1433
|
issues. Rows are real links, per-repo errors don't sink the view, and the two lists load
|
|
@@ -1504,8 +1508,8 @@ The **Settings** modal (the gear button) shows an **estimated $ cost** — Sessi
|
|
|
1504
1508
|
0.1×, cache writes at 1.25× input). It's an estimate: real billing differs, **flat-plan
|
|
1505
1509
|
(Max) usage isn't reflected**, and turns on unpriced models are flagged and excluded.
|
|
1506
1510
|
|
|
1507
|
-
A separate, full **double-entry accounting** book (the `account_balance`
|
|
1508
|
-
`/accounting`) is provided by the bundled `@mulmoclaude/accounting-plugin` and stores its
|
|
1511
|
+
A separate, full **double-entry accounting** book (the `account_balance` button first on the
|
|
1512
|
+
Collections screen's top row → `/accounting`) is provided by the bundled `@mulmoclaude/accounting-plugin` and stores its
|
|
1509
1513
|
books under `<workspace>/data/accounting`. It's a bookkeeping app — unrelated to the LLM
|
|
1510
1514
|
cost estimate above — and is also exposed to Claude as the `manageAccounting` GUI tool.
|
|
1511
1515
|
|
|
@@ -1694,7 +1698,8 @@ narrow and tall for one record you are discussing — and each position keeps it
|
|
|
1694
1698
|
receives those as input. A cell that **stops for a permission prompt comes back to full strength
|
|
1695
1699
|
on its own**, so setting one aside can never hide a session that is waiting on you; a merely *finished* turn
|
|
1696
1700
|
does not, since that is the expected outcome of setting a running agent aside.
|
|
1697
|
-
- **
|
|
1701
|
+
- **History menu** (on the cell header) — the Prompts and Conversation panes below, and the
|
|
1702
|
+
**Timeline**: a read-only per-session activity timeline (tools run, newest first),
|
|
1698
1703
|
from `GET /api/transcript/timeline`.
|
|
1699
1704
|
- **Bring another cell's turn here** (💬) — pick another terminal in the grid and its
|
|
1700
1705
|
**last completed turn** is pasted into *this* cell's input box, so you can have Claude
|
|
@@ -1703,7 +1708,7 @@ narrow and tall for one record you are discussing — and each position keeps it
|
|
|
1703
1708
|
ANSI debris and nothing lost to scrollback. It is **pasted, never sent** — you read
|
|
1704
1709
|
what arrived and press Enter, in the cell you were already in. A turn still running
|
|
1705
1710
|
isn't available yet (Codex writes its rollout only once the turn ends).
|
|
1706
|
-
- **Tools pane** — the available GUI tools plus a live tool-call history for the active
|
|
1711
|
+
- **Tools pane** (the cell header's **Tools** menu, beside the Canvas, Collections and **Restart the agent**) — the available GUI tools plus a live tool-call history for the active
|
|
1707
1712
|
session.
|
|
1708
1713
|
- **Prompts pane** — the prompts *you* sent the enlarged cell's session, newest first, from
|
|
1709
1714
|
`GET /api/transcript/prompts`. The mirror of the Timeline above: that one is what the agent
|
package/bin/cli-args.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { NodeUpgradeGuide } from "./node-install.js";
|
|
1
2
|
export type PortChoice = { port: number; explicit: boolean } | { error: string };
|
|
2
3
|
export type CwdChoice = { path: string; mustExist: boolean } | { error: string };
|
|
3
4
|
export declare function parsePortArg(args: string[], env: Record<string, string | undefined>, defaultPort: number): PortChoice;
|
|
@@ -16,6 +17,7 @@ export declare function saysYes(answer: unknown): boolean;
|
|
|
16
17
|
export declare const SECOND_INSTANCE_NOTE: string;
|
|
17
18
|
export declare const MIN_NODE_LABEL: string;
|
|
18
19
|
export declare function nodeMeetsMinimum(version: string): boolean;
|
|
20
|
+
export declare function unsupportedNodeMessage(version: string, execPath: string, upgrade: NodeUpgradeGuide): string;
|
|
19
21
|
export declare function serverNodeArgs(serverEntry: string, launchDir: string, port: number, declaredAgent?: string | null): string[];
|
|
20
22
|
export declare function serverSpawnEnv(env: Record<string, string | undefined>, cwd: string): Record<string, string | undefined>;
|
|
21
23
|
export interface RunningInstance {
|
package/bin/cli-args.js
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
// environment or the filesystem.
|
|
12
12
|
import { isIP } from "node:net";
|
|
13
13
|
import { join } from "node:path";
|
|
14
|
+
import { NODE_DOWNLOAD_URL } from "./node-install.js";
|
|
14
15
|
|
|
15
16
|
// The v4 loopback every local client of this server dials by literal — `guiMcpUrlTemplate` in
|
|
16
17
|
// server/infra/gui-mcp-registration.ts builds `http://127.0.0.1:<port>/api/mcp/...`. Duplicated
|
|
@@ -390,24 +391,61 @@ const MIN_NODE_MAJOR = 22;
|
|
|
390
391
|
const MIN_NODE_MINOR = 12;
|
|
391
392
|
|
|
392
393
|
/**
|
|
393
|
-
* The lowest Node
|
|
394
|
+
* The lowest Node MulmoTerminal runs on, for the "needs ≥ x.y" line.
|
|
394
395
|
*/
|
|
395
396
|
export const MIN_NODE_LABEL = `${MIN_NODE_MAJOR}.${MIN_NODE_MINOR}`;
|
|
396
397
|
|
|
397
398
|
/**
|
|
398
|
-
* Whether the running Node is new enough
|
|
399
|
+
* Whether the running Node is new enough — the `init` tick, and the gate on a normal launch.
|
|
399
400
|
*
|
|
400
401
|
* `process.versions.node` is "major.minor.patch", with a "-prerelease" tag on the patch for
|
|
401
402
|
* nightlies ("22.12.0-nightly…"). Only major.minor gate, and Number.parseInt stops at the
|
|
402
403
|
* first non-digit, so that tag never reaches the comparison. A string that is not a version
|
|
403
404
|
* parses to NaN, and every comparison against NaN is false, so an unreadable version reads as
|
|
404
|
-
* "below minimum"
|
|
405
|
+
* "below minimum". Node's own `process.versions.node` is always readable, so that only ever
|
|
406
|
+
* decides a malformed string passed in a test.
|
|
405
407
|
*/
|
|
406
408
|
export function nodeMeetsMinimum(version) {
|
|
407
409
|
const [major, minor] = version.split(".").map((part) => Number.parseInt(part, 10));
|
|
408
410
|
return major > MIN_NODE_MAJOR || (major === MIN_NODE_MAJOR && minor >= MIN_NODE_MINOR);
|
|
409
411
|
}
|
|
410
412
|
|
|
413
|
+
const NODE_TOO_OLD_ART = [
|
|
414
|
+
" ## ## ###### ######## ##########",
|
|
415
|
+
" #### ## ## ## ## ## ##",
|
|
416
|
+
" ## ## ## ## ## ## ## ########",
|
|
417
|
+
" ## #### ## ## ## ## ##",
|
|
418
|
+
" ## ## ###### ######## ##########",
|
|
419
|
+
"",
|
|
420
|
+
" ########## ###### ###### ###### ## ########",
|
|
421
|
+
" ## ## ## ## ## ## ## ## ## ##",
|
|
422
|
+
" ## ## ## ## ## ## ## ## ## ##",
|
|
423
|
+
" ## ## ## ## ## ## ## ## ## ##",
|
|
424
|
+
" ## ###### ###### ###### ########## ########",
|
|
425
|
+
];
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* What a normal launch prints before stopping on a Node below the minimum.
|
|
429
|
+
*
|
|
430
|
+
* Without it the first thing to fail is the server spawn itself — Node 20 rejects
|
|
431
|
+
* `--env-file-if-exists` with "bad option", which reads as a `.env` problem rather than an old
|
|
432
|
+
* Node (#2351). The banner is large so it is not mistaken for one more line of startup output.
|
|
433
|
+
* `upgrade` is `nodeUpgradeGuide`'s answer for this Node.
|
|
434
|
+
*/
|
|
435
|
+
export function unsupportedNodeMessage(version, execPath, upgrade) {
|
|
436
|
+
const lines = ["", ...NODE_TOO_OLD_ART, "", ` Node ${version} is not supported. MulmoTerminal needs Node ≥ ${MIN_NODE_LABEL}.`, ` Running: ${execPath}`];
|
|
437
|
+
if (upgrade.via) lines.push(` Installed with: ${upgrade.via}`);
|
|
438
|
+
lines.push("");
|
|
439
|
+
if (upgrade.commands.length > 0) {
|
|
440
|
+
lines.push(" To upgrade, run:", "", ...upgrade.commands.map((command) => ` ${command}`), "");
|
|
441
|
+
lines.push(` Node LTS downloads: ${NODE_DOWNLOAD_URL}`);
|
|
442
|
+
} else {
|
|
443
|
+
lines.push(` To upgrade, install the LTS from ${NODE_DOWNLOAD_URL}`);
|
|
444
|
+
}
|
|
445
|
+
lines.push(" Then open a new terminal and run the same command again.", "");
|
|
446
|
+
return lines.join("\n");
|
|
447
|
+
}
|
|
448
|
+
|
|
411
449
|
/**
|
|
412
450
|
* The node arguments the launcher spawns the server with.
|
|
413
451
|
*
|
package/bin/mulmoterminal.js
CHANGED
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
stopCommandFor,
|
|
32
32
|
SECOND_INSTANCE_NOTE,
|
|
33
33
|
nodeMeetsMinimum,
|
|
34
|
+
unsupportedNodeMessage,
|
|
34
35
|
MIN_NODE_LABEL,
|
|
35
36
|
serverNodeArgs,
|
|
36
37
|
serverSpawnEnv,
|
|
@@ -41,6 +42,7 @@ import { liveInstances, servingInstances } from "./instances.js";
|
|
|
41
42
|
import { agentBin, AGENT_BIN_SPEC } from "./agent-bins.js";
|
|
42
43
|
import { configuredDefaultAgent, gateFor, isKnownAgent, missingAgentMessage, parseAgentArg, resolveDeclaredAgent } from "./default-agent.js";
|
|
43
44
|
import { setProcessTitle } from "./process-title.js";
|
|
45
|
+
import { nodeUpgradeGuide } from "./node-install.js";
|
|
44
46
|
|
|
45
47
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
46
48
|
const PKG_DIR = join(__dirname, "..");
|
|
@@ -740,6 +742,14 @@ async function main() {
|
|
|
740
742
|
return;
|
|
741
743
|
}
|
|
742
744
|
|
|
745
|
+
// Before anything that spawns: on an old Node the server dies on its own node flags, and what
|
|
746
|
+
// it prints says nothing about the version.
|
|
747
|
+
if (!nodeMeetsMinimum(process.versions.node)) {
|
|
748
|
+
const upgrade = nodeUpgradeGuide(process.execPath, process.platform, process.env);
|
|
749
|
+
console.error(unsupportedNodeMessage(process.versions.node, process.execPath, upgrade));
|
|
750
|
+
process.exit(1);
|
|
751
|
+
}
|
|
752
|
+
|
|
743
753
|
checkForUpdate();
|
|
744
754
|
|
|
745
755
|
const declaredAgent = passStartupGate(args);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export interface NodeUpgradeGuide {
|
|
2
|
+
via: string | null;
|
|
3
|
+
commands: string[];
|
|
4
|
+
}
|
|
5
|
+
export declare const NODE_DOWNLOAD_URL: string;
|
|
6
|
+
export declare function nodeUpgradeGuide(execPath: string, platform: string, env: Record<string, string | undefined>): NodeUpgradeGuide;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// How to upgrade THIS Node, worked out from where it is installed (#2351).
|
|
2
|
+
//
|
|
3
|
+
// "Install the LTS from nodejs.org" is wrong advice for most people who hit the version gate: a
|
|
4
|
+
// Node that came from a version manager is shadowed by it, so a fresh installer leaves `node` on
|
|
5
|
+
// PATH exactly as old as before. The binary's own path says which tool put it there.
|
|
6
|
+
//
|
|
7
|
+
// Every command below was checked against that tool's own help or README. A tool whose upgrade
|
|
8
|
+
// command could not be confirmed gets no command — its readers still get the download link.
|
|
9
|
+
|
|
10
|
+
export const NODE_DOWNLOAD_URL = "https://nodejs.org/en/download";
|
|
11
|
+
|
|
12
|
+
// fnm's `default` takes a version, and it has no documented name for "the latest LTS".
|
|
13
|
+
// Any major at or above the minimum works; this one was the Active LTS when written.
|
|
14
|
+
const SUGGESTED_NODE_MAJOR = 24;
|
|
15
|
+
|
|
16
|
+
const toMatchable = (execPath) => execPath.replaceAll("\\", "/").toLowerCase();
|
|
17
|
+
|
|
18
|
+
const withoutTrailingSlashes = (path) => (path.endsWith("/") ? withoutTrailingSlashes(path.slice(0, -1)) : path);
|
|
19
|
+
|
|
20
|
+
// Boundary-aware, so `C:\Program Files\nodejs-old` is not inside `C:\Program Files\nodejs`.
|
|
21
|
+
function isInsideDir(path, dir) {
|
|
22
|
+
if (typeof dir !== "string") return false;
|
|
23
|
+
const prefix = withoutTrailingSlashes(toMatchable(dir));
|
|
24
|
+
return prefix !== "" && path.startsWith(`${prefix}/`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// The app id is read off a path and printed as a command to paste, so anything beyond a plain
|
|
28
|
+
// manifest name gets no command rather than a second one smuggled in by `&` or a space.
|
|
29
|
+
const isPlainScoopAppId = (id) => /^[a-z0-9][a-z0-9._-]*$/.test(id);
|
|
30
|
+
|
|
31
|
+
const guide = (via, commands) => ({ via, commands });
|
|
32
|
+
|
|
33
|
+
function versionManagerGuide(path) {
|
|
34
|
+
if (path.includes("/.nodebrew/")) return guide("nodebrew", ["nodebrew install stable", "nodebrew use stable"]);
|
|
35
|
+
if (path.includes("/.nvm/versions/node/")) return guide("nvm", ["nvm install --lts", "nvm alias default 'lts/*'"]);
|
|
36
|
+
if (path.includes("volta/tools/image/node/")) return guide("Volta", ["volta install node"]);
|
|
37
|
+
if (path.includes("/fnm/") || path.includes("/fnm_multishells/")) {
|
|
38
|
+
return guide("fnm", [`fnm install ${SUGGESTED_NODE_MAJOR}`, `fnm default ${SUGGESTED_NODE_MAJOR}`]);
|
|
39
|
+
}
|
|
40
|
+
if (path.includes("/mise/installs/node/")) return guide("mise", ["mise use -g node@lts"]);
|
|
41
|
+
if (path.includes("/.asdf/installs/nodejs/")) return guide("asdf", ["asdf install nodejs latest", "asdf set -u nodejs $(asdf latest nodejs)"]);
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function packageManagerGuide(path) {
|
|
46
|
+
const scoopApp = /\/scoop\/apps\/([^/]+)\//.exec(path)?.[1];
|
|
47
|
+
if (scoopApp) return guide("Scoop", isPlainScoopAppId(scoopApp) ? [`scoop update ${scoopApp}`] : []);
|
|
48
|
+
// Homebrew resolves `node` to its keg, so the formula is in the path: `node`, or a pinned `node@20`.
|
|
49
|
+
const formula = /\/cellar\/(node|node@\d+)\//.exec(path)?.[1];
|
|
50
|
+
if (formula === "node") return guide("Homebrew", ["brew upgrade node"]);
|
|
51
|
+
if (formula) return guide(`Homebrew (${formula})`, [`brew unlink ${formula}`, "brew install node"]);
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// nvm-windows points one fixed symlink (NVM_SYMLINK, by default C:\Program Files\nodejs — the
|
|
56
|
+
// installer's own directory) at the active version, so only its environment tells the two apart.
|
|
57
|
+
function nvmWindowsGuide(path, env) {
|
|
58
|
+
if (!isInsideDir(path, env.NVM_SYMLINK) && !isInsideDir(path, env.NVM_HOME)) return null;
|
|
59
|
+
return guide("nvm-windows", ["nvm install lts", "nvm use lts"]);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// The .msi installs to `Program Files\nodejs` (or its x86 twin) on whichever drive Windows is on.
|
|
63
|
+
const isWindowsInstallerPath = (path) => /\/program files( \(x86\))?\/nodejs\/node\.exe$/.test(path);
|
|
64
|
+
|
|
65
|
+
function installerGuide(path, platform) {
|
|
66
|
+
if (platform === "win32" && isWindowsInstallerPath(path)) return guide("the Windows installer", []);
|
|
67
|
+
if (platform === "darwin" && path === "/usr/local/bin/node") return guide("the macOS installer from nodejs.org", []);
|
|
68
|
+
if (platform === "linux" && path === "/usr/bin/node") return guide("your system's package manager", []);
|
|
69
|
+
return guide(null, []);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* How the running Node was installed, and the commands that upgrade it there.
|
|
74
|
+
*
|
|
75
|
+
* `via` is null when the path matches nothing known; `commands` is empty when the only honest
|
|
76
|
+
* advice is the download page.
|
|
77
|
+
*/
|
|
78
|
+
export function nodeUpgradeGuide(execPath, platform, env) {
|
|
79
|
+
const path = toMatchable(execPath);
|
|
80
|
+
return (platform === "win32" ? nvmWindowsGuide(path, env) : null) ?? versionManagerGuide(path) ?? packageManagerGuide(path) ?? installerGuide(path, platform);
|
|
81
|
+
}
|
|
@@ -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,99 @@
|
|
|
1
|
+
// Reads .blueprint/replies.json — an answer to each question the person asked — and answers one question
|
|
2
|
+
// per mode. Exit 0 is yes.
|
|
3
|
+
// answer every question has exactly one reply; a reply found in the documents quotes them (chaff cite)
|
|
4
|
+
// and names every address it quotes; a reply not found says what was searched. Records the
|
|
5
|
+
// documents' and FAQ.md's fingerprints.
|
|
6
|
+
// keep the documents are unchanged; FAQ.md holds every question after what it held before when the
|
|
7
|
+
// person asked to keep the answers, and is untouched otherwise
|
|
8
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { fromBase } from "./base.mjs";
|
|
10
|
+
const { fail, quotationProblems, readJson } = await import(fromBase("chaff.mjs"));
|
|
11
|
+
const { digest, documentSource, documentsNamed, fingerprint } = await import(fromBase("documents.mjs"));
|
|
12
|
+
|
|
13
|
+
const REPLIES = ".blueprint/replies.json";
|
|
14
|
+
const REPLIES_PAGE = ".blueprint/replies.md";
|
|
15
|
+
const RECORDED = ".blueprint/.replies-before.json";
|
|
16
|
+
const FAQ = "FAQ.md";
|
|
17
|
+
const KEEP = "このフォルダの FAQ.md に書き足す";
|
|
18
|
+
|
|
19
|
+
const answers = readJson(".blueprint/answers.json", "the interview answers");
|
|
20
|
+
const documents = documentsNamed(answers?.documents);
|
|
21
|
+
const questions = String(answers?.questions ?? "")
|
|
22
|
+
.split("\n")
|
|
23
|
+
.map((line) => line.trim())
|
|
24
|
+
.filter((line) => line !== "");
|
|
25
|
+
if (questions.length === 0) fail("the interview asks no question");
|
|
26
|
+
if (new Set(questions).size !== questions.length) fail("the interview asks the same question twice");
|
|
27
|
+
if (answers?.keep === KEEP && documents.includes(FAQ)) fail(`${FAQ} is one of the documents, so the answers cannot be added to it`);
|
|
28
|
+
|
|
29
|
+
const indented = (line) => " " + line;
|
|
30
|
+
const nonEmpty = (value) => typeof value === "string" && value.trim() !== "";
|
|
31
|
+
|
|
32
|
+
const replyProblem = (reply) => {
|
|
33
|
+
if (typeof reply !== "object" || reply === null) return "a reply is not an object";
|
|
34
|
+
if (!nonEmpty(reply.question)) return "a reply names no question";
|
|
35
|
+
const which = JSON.stringify(reply.question);
|
|
36
|
+
if (typeof reply.found !== "boolean") return `${which}: "found" must be true or false`;
|
|
37
|
+
if (!nonEmpty(reply.answer)) return `${which}: no answer`;
|
|
38
|
+
const citations = reply.citations ?? [];
|
|
39
|
+
if (!Array.isArray(citations)) return `${which}: citations must be an array`;
|
|
40
|
+
if (reply.found && citations.length === 0) return `${which}: an answer found in the documents quotes them`;
|
|
41
|
+
if (!citations.every((citation) => nonEmpty(citation?.address))) return `${which}: every quotation needs an "address"`;
|
|
42
|
+
const unnamed = citations.filter((citation) => !reply.answer.includes(citation.address.trim()));
|
|
43
|
+
if (unnamed.length > 0) return `${which}: the answer does not name ${unnamed.map((citation) => citation.address).join(", ")}, which it quotes`;
|
|
44
|
+
if (!reply.found && (!Array.isArray(reply.searched) || !reply.searched.some(nonEmpty))) return `${which}: say what was searched ("searched")`;
|
|
45
|
+
return null;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const readReplies = () => {
|
|
49
|
+
const record = readJson(REPLIES, '{ "replies": [{ "question", "found", "answer", "citations", "searched" }] }');
|
|
50
|
+
const replies = Array.isArray(record?.replies) ? record.replies : fail(`${REPLIES} needs a "replies" array`);
|
|
51
|
+
const problem = replies.map(replyProblem).find((found) => found !== null);
|
|
52
|
+
if (problem) fail(`${REPLIES}: ${problem}`);
|
|
53
|
+
const asked = replies.map((reply) => reply.question.trim());
|
|
54
|
+
const unanswered = questions.filter((question) => !asked.includes(question));
|
|
55
|
+
if (unanswered.length > 0) fail(`no reply to:\n${unanswered.map(indented).join("\n")}`);
|
|
56
|
+
const extra = asked.filter((question) => !questions.includes(question));
|
|
57
|
+
if (extra.length > 0) fail(`replies to questions nobody asked:\n${extra.map(indented).join("\n")}`);
|
|
58
|
+
if (new Set(asked).size !== asked.length) fail(`${REPLIES}: a question is answered twice`);
|
|
59
|
+
return replies;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const faqState = () => (existsSync(FAQ) ? { bytes: readFileSync(FAQ).length, hash: fingerprint(FAQ) } : null);
|
|
63
|
+
|
|
64
|
+
const mode = process.argv[2];
|
|
65
|
+
|
|
66
|
+
if (mode === "answer") {
|
|
67
|
+
const replies = readReplies();
|
|
68
|
+
const quoted = replies.flatMap((reply) => quotationProblems(JSON.stringify(reply.question), reply.citations ?? [], documentSource(documents)));
|
|
69
|
+
if (quoted.length > 0) fail(quoted.join("\n"));
|
|
70
|
+
if (!existsSync(REPLIES_PAGE)) fail(`${REPLIES_PAGE} is missing: the replies for the person to read`);
|
|
71
|
+
const page = readFileSync(REPLIES_PAGE, "utf8");
|
|
72
|
+
const absent = questions.filter((question) => !page.includes(question));
|
|
73
|
+
if (absent.length > 0) fail(`${REPLIES_PAGE} does not show: ${absent.join(" / ")}`);
|
|
74
|
+
writeFileSync(RECORDED, JSON.stringify({ documents: Object.fromEntries(documents.map((file) => [file, fingerprint(file)])), faq: faqState() }));
|
|
75
|
+
console.log(`${replies.length} repl(ies): ${replies.filter((reply) => reply.found).length} found in the documents`);
|
|
76
|
+
} else if (mode === "keep") {
|
|
77
|
+
const recorded = readJson(RECORDED, "what the answer step recorded");
|
|
78
|
+
const readThen = recorded?.documents;
|
|
79
|
+
if (typeof readThen !== "object" || readThen === null || Array.isArray(readThen))
|
|
80
|
+
fail(`${RECORDED} is not what the answer step records: run the answer check again`);
|
|
81
|
+
if (JSON.stringify(Object.keys(readThen).sort()) !== JSON.stringify([...documents].sort()))
|
|
82
|
+
fail(`the documents named now are not the ones that were read (${Object.keys(readThen).join(", ")}): run the answer check again`);
|
|
83
|
+
const changed = documents.filter((file) => readThen[file] !== fingerprint(file));
|
|
84
|
+
if (changed.length > 0) fail(`changed since they were read — the documents must stay as they are: ${changed.join(", ")}`);
|
|
85
|
+
const before = recorded.faq ?? null;
|
|
86
|
+
if (answers?.keep === KEEP) {
|
|
87
|
+
if (!existsSync(FAQ)) fail(`${FAQ} is missing`);
|
|
88
|
+
const now = readFileSync(FAQ);
|
|
89
|
+
if (before !== null && digest(now.subarray(0, before.bytes)) !== before.hash) fail(`${FAQ}: what it held before must stay as it was; add after it`);
|
|
90
|
+
const text = now.toString("utf8");
|
|
91
|
+
const absent = questions.filter((question) => !text.includes(question));
|
|
92
|
+
if (absent.length > 0) fail(`${FAQ} does not hold: ${absent.join(" / ")}`);
|
|
93
|
+
} else if (JSON.stringify(faqState()) !== JSON.stringify(before)) {
|
|
94
|
+
fail(`${FAQ} changed, but the person chose not to keep the answers`);
|
|
95
|
+
}
|
|
96
|
+
console.log("the documents are untouched");
|
|
97
|
+
} else {
|
|
98
|
+
fail(`usage: replies.mjs answer | keep (got ${JSON.stringify(mode)})`);
|
|
99
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"questions": [
|
|
3
|
+
{
|
|
4
|
+
"id": "documents",
|
|
5
|
+
"label": "質問する相手の文書(このフォルダの中のファイルを 1 行に 1 つ)",
|
|
6
|
+
"why": "答えはこの文書だけから出し、どこに書いてあるかを示す",
|
|
7
|
+
"kind": "text"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"id": "questions",
|
|
11
|
+
"label": "質問(1 行に 1 つ)",
|
|
12
|
+
"why": "質問ごとに一つ答える",
|
|
13
|
+
"kind": "text"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": "keep",
|
|
17
|
+
"label": "答えを残しますか",
|
|
18
|
+
"why": "読んで確かめた答えを、このフォルダの FAQ.md に書き足すかどうか",
|
|
19
|
+
"kind": "select",
|
|
20
|
+
"options": ["このフォルダの FAQ.md に書き足す", "残さない(.blueprint の中だけ)"]
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"slug": "ask",
|
|
3
|
+
"kind": "usecase",
|
|
4
|
+
"title": "文書に尋ねる(どこに書いてあるかを添えて)",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"description": "込み入ったマニュアル・規程・契約書に質問すると、文書のどこに書いてあるかを原文の引用で示して答える。書いていないことは「書いていない」と答える。引用は chaff cite で原文にあることを確かめる。答えは、人が読んでから FAQ.md に残せる。",
|
|
7
|
+
"bases": ["docs"],
|
|
8
|
+
"report": ".blueprint/replies.md"
|
|
9
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# 経費精算の手引き
|
|
2
|
+
|
|
3
|
+
## 対象になるもの
|
|
4
|
+
|
|
5
|
+
仕事のために使った交通費、宿泊費、会議の飲食代が対象です。私用の買い物や、会社のカードで払ったものは対象になりません。
|
|
6
|
+
|
|
7
|
+
## 申請のしかた
|
|
8
|
+
|
|
9
|
+
1. 領収書を写真に撮り、経費精算システムに登録します。
|
|
10
|
+
2. 使った日から30日以内に申請します。30日を過ぎたものは、上長の承認があるときだけ受け付けます。
|
|
11
|
+
3. 上長が承認すると、経理部が確かめます。
|
|
12
|
+
|
|
13
|
+
## 支払い
|
|
14
|
+
|
|
15
|
+
経理部の確認が済んだ申請は、毎月25日にまとめて給与口座へ振り込みます。25日が土日祝日のときは、その前の営業日に振り込みます。
|
|
16
|
+
|
|
17
|
+
## 領収書が無いとき
|
|
18
|
+
|
|
19
|
+
交通系ICカードの利用履歴を印刷したものは、領収書の代わりになります。それ以外で領収書が無いときは、「支払証明書」に理由を書いて上長の承認を受けてください。
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"presets": [
|
|
3
|
+
{
|
|
4
|
+
"id": "keihi",
|
|
5
|
+
"title": "経費精算の手引きに尋ねる",
|
|
6
|
+
"description": "見本の手引き(keihi.md)に 3 つの質問をすると、どこに書いてあるかを原文の引用で示して答える。書いていないこと(海外出張の日当)には「書いていない」と答える。",
|
|
7
|
+
"base": "docs",
|
|
8
|
+
"answers": {
|
|
9
|
+
"documents": "keihi.md",
|
|
10
|
+
"questions": "申請の期限はいつまで?\n領収書をなくしたときはどうする?\n海外出張の日当はいくら?",
|
|
11
|
+
"keep": "このフォルダの FAQ.md に書き足す"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: blueprint-ask-answer
|
|
3
|
+
description: "Answer each question from the named documents only, quoting where the answer is written, or saying plainly that the documents do not say — changing nothing yet."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Answer from the documents
|
|
7
|
+
|
|
8
|
+
`.blueprint/answers.json` names the documents (`documents`, one path per line), the questions
|
|
9
|
+
(`questions`, one per line) and whether to keep the answers (`keep`). The person may not know the
|
|
10
|
+
documents well: answer in their language and in plain words.
|
|
11
|
+
|
|
12
|
+
Change nothing in the repository: this step only reads. Write only under `.blueprint/`; the person reads
|
|
13
|
+
the answers before anything is kept.
|
|
14
|
+
|
|
15
|
+
## How to answer
|
|
16
|
+
|
|
17
|
+
- **Only from the documents.** Not from general knowledge, not from what such documents usually say. If the
|
|
18
|
+
documents do not answer a question, the answer is that they do not — and what they do say nearby, if it
|
|
19
|
+
helps.
|
|
20
|
+
- **Find where it is written.** `chaff tree <document>` (through the base pack's wrapper,
|
|
21
|
+
`sh <base pack>/checks/chaff.sh tree <document>`) shows the document's addresses — articles, sections,
|
|
22
|
+
items — so an answer can say "第4条第2項" or "Section 3.1". Read the whole relevant part, including
|
|
23
|
+
exceptions and definitions elsewhere that change its meaning.
|
|
24
|
+
- **When provisions disagree**, say so and quote both rather than choosing one.
|
|
25
|
+
|
|
26
|
+
## Write `.blueprint/replies.json`
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"replies": [
|
|
31
|
+
{
|
|
32
|
+
"question": "委託料はいつまでに払う?",
|
|
33
|
+
"found": true,
|
|
34
|
+
"answer": "検収後30日以内です(第4条第2項)。",
|
|
35
|
+
"citations": [{ "source": "contract.txt", "address": "第4条第2項", "quote": "成果物の検収後30日以内に" }]
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"question": "消費税は含まれる?",
|
|
39
|
+
"found": false,
|
|
40
|
+
"answer": "文書には書かれていません。第4条第1項は「金50万円」とだけ定めています。",
|
|
41
|
+
"citations": [{ "source": "contract.txt", "address": "第4条第1項", "quote": "委託料として金50万円を支払う" }],
|
|
42
|
+
"searched": ["消費税", "税", "税込", "税別"]
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
- `question`: the question exactly as it is in `questions`. One reply per question, none extra.
|
|
49
|
+
- `found`: `true` when the documents answer it. Then at least one citation.
|
|
50
|
+
- `citations`: `source` is the document's path as `documents` names it; `address` is where; `quote` is
|
|
51
|
+
copied character for character — the check runs `chaff cite` on every one. **Write every cited address in
|
|
52
|
+
the `answer` text** so the person can open the document there.
|
|
53
|
+
- `searched`: when `found` is `false`, the words and places you looked for.
|
|
54
|
+
|
|
55
|
+
Also write `.blueprint/replies.md` for the person: each question as a heading, the answer, and the quotations.
|
|
56
|
+
|
|
57
|
+
## Done when
|
|
58
|
+
|
|
59
|
+
`node <usecase pack>/checks/replies.mjs answer` (with `BLUEPRINT_BASE` and `BLUEPRINT_USECASE` set to the
|
|
60
|
+
pack folders from your prompt) passes. It also records a fingerprint of the documents and of `FAQ.md`.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: blueprint-ask-keep
|
|
3
|
+
description: "Add the approved answers to FAQ.md when the person chose to keep them, after what it already holds; change nothing otherwise."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Keep the answers
|
|
7
|
+
|
|
8
|
+
The person has read the answers and approved them. Read `.blueprint/answers.json` and
|
|
9
|
+
`.blueprint/replies.json`.
|
|
10
|
+
|
|
11
|
+
**Never change a document.** The check compares each one with the fingerprint recorded when the answers were
|
|
12
|
+
written.
|
|
13
|
+
|
|
14
|
+
If `keep` is `残さない(.blueprint の中だけ)`, there is nothing to write: leave `FAQ.md` exactly as it is,
|
|
15
|
+
run the check and stop.
|
|
16
|
+
|
|
17
|
+
Otherwise add to `FAQ.md` in this folder (create it if there is none). **Add after what it already holds;
|
|
18
|
+
never edit or reorder what is there** — the check compares the beginning of the file with what it held
|
|
19
|
+
before. For each question: the question as a `##` heading, word for word; the answer; and where it is
|
|
20
|
+
written (document and address). For a question the documents do not answer, say so — that is worth keeping
|
|
21
|
+
too.
|
|
22
|
+
|
|
23
|
+
## Done when
|
|
24
|
+
|
|
25
|
+
`node <usecase pack>/checks/replies.mjs keep` passes.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"steps": [
|
|
3
|
+
{
|
|
4
|
+
"id": "answer",
|
|
5
|
+
"title": "文書から答える",
|
|
6
|
+
"description": "質問ごとに、文書のどこに書いてあるかを引用で示して答える。書いていなければ、そう答えて何を探したかを残す。",
|
|
7
|
+
"skill": "skills/answer",
|
|
8
|
+
"check": "node \"$BLUEPRINT_USECASE/checks/replies.mjs\" answer"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"id": "keep",
|
|
12
|
+
"title": "答えを残す",
|
|
13
|
+
"description": "残すと決めたなら、確かめた答えを FAQ.md に書き足す。",
|
|
14
|
+
"skill": "skills/keep",
|
|
15
|
+
"check": "node \"$BLUEPRINT_USECASE/checks/replies.mjs\" keep",
|
|
16
|
+
"gates": ["review"]
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|