@polderlabs/bizar 10.2.0 → 10.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 +31 -8
- package/bizar-dash/CONTROL_SURFACES.md +0 -1
- package/bizar-dash/dist/assets/{ActivityFeed-JjmOY2AQ.js → ActivityFeed-xXiUCZLB.js} +1 -1
- package/bizar-dash/dist/assets/{ActivityFeed-JjmOY2AQ.js.map → ActivityFeed-xXiUCZLB.js.map} +1 -1
- package/bizar-dash/dist/assets/{ActivityView-BIIMvGWo.js → ActivityView-nOYhCp3h.js} +1 -1
- package/bizar-dash/dist/assets/{ActivityView-BIIMvGWo.js.map → ActivityView-nOYhCp3h.js.map} +1 -1
- package/bizar-dash/dist/assets/{AdminView-DRidJtkq.js → AdminView-Coi4V3sq.js} +1 -1
- package/bizar-dash/dist/assets/{AdminView-DRidJtkq.js.map → AdminView-Coi4V3sq.js.map} +1 -1
- package/bizar-dash/dist/assets/{AgentActivity-79l4SBMC.js → AgentActivity-nEri4lbx.js} +1 -1
- package/bizar-dash/dist/assets/{AgentActivity-79l4SBMC.js.map → AgentActivity-nEri4lbx.js.map} +1 -1
- package/bizar-dash/dist/assets/{AgentsView-0vT4uPSc.js → AgentsView-ByiIJ0ej.js} +3 -3
- package/bizar-dash/dist/assets/{AgentsView-0vT4uPSc.js.map → AgentsView-ByiIJ0ej.js.map} +1 -1
- package/bizar-dash/dist/assets/{Alert-CG5q_s75.js → Alert-Dg_Vwxh5.js} +1 -1
- package/bizar-dash/dist/assets/{Alert-CG5q_s75.js.map → Alert-Dg_Vwxh5.js.map} +1 -1
- package/bizar-dash/dist/assets/{ArtifactsView-DM8ZmpR9.js → ArtifactsView-CGclRNnz.js} +1 -1
- package/bizar-dash/dist/assets/{ArtifactsView-DM8ZmpR9.js.map → ArtifactsView-CGclRNnz.js.map} +1 -1
- package/bizar-dash/dist/assets/{AuthView-BveN94MC.js → AuthView-utVm6M3D.js} +1 -1
- package/bizar-dash/dist/assets/{AuthView-BveN94MC.js.map → AuthView-utVm6M3D.js.map} +1 -1
- package/bizar-dash/dist/assets/{Avatar-ulUPIYTd.js → Avatar-CXDZdKy4.js} +1 -1
- package/bizar-dash/dist/assets/{Avatar-ulUPIYTd.js.map → Avatar-CXDZdKy4.js.map} +1 -1
- package/bizar-dash/dist/assets/{BackgroundJobsView-BWErhqFo.js → BackgroundJobsView-j603pz6o.js} +1 -1
- package/bizar-dash/dist/assets/{BackgroundJobsView-BWErhqFo.js.map → BackgroundJobsView-j603pz6o.js.map} +1 -1
- package/bizar-dash/dist/assets/{BackupView-CnBjzG9N.js → BackupView-DgvnntTE.js} +1 -1
- package/bizar-dash/dist/assets/{BackupView-CnBjzG9N.js.map → BackupView-DgvnntTE.js.map} +1 -1
- package/bizar-dash/dist/assets/{Card-B0imhxMk.js → Card-D9019JOD.js} +1 -1
- package/bizar-dash/dist/assets/{Card-B0imhxMk.js.map → Card-D9019JOD.js.map} +1 -1
- package/bizar-dash/dist/assets/{ChatView-D6zGytzx.js → ChatView-C0cTgj5d.js} +1 -1
- package/bizar-dash/dist/assets/{ChatView-D6zGytzx.js.map → ChatView-C0cTgj5d.js.map} +1 -1
- package/bizar-dash/dist/assets/{Chip-wwZSGwLE.js → Chip-DQtalZ4y.js} +1 -1
- package/bizar-dash/dist/assets/{Chip-wwZSGwLE.js.map → Chip-DQtalZ4y.js.map} +1 -1
- package/bizar-dash/dist/assets/{ClaudeSessionsView-DK1sOLhi.js → ClaudeSessionsView-H3Bqjo6b.js} +1 -1
- package/bizar-dash/dist/assets/{ClaudeSessionsView-DK1sOLhi.js.map → ClaudeSessionsView-H3Bqjo6b.js.map} +1 -1
- package/bizar-dash/dist/assets/{ClipboardView-CZ2lYPr0.js → ClipboardView-BAguaKbx.js} +1 -1
- package/bizar-dash/dist/assets/{ClipboardView-CZ2lYPr0.js.map → ClipboardView-BAguaKbx.js.map} +1 -1
- package/bizar-dash/dist/assets/{ConfigView-D-zT9eNA.js → ConfigView-DWHJgoe8.js} +1 -1
- package/bizar-dash/dist/assets/{ConfigView-D-zT9eNA.js.map → ConfigView-DWHJgoe8.js.map} +1 -1
- package/bizar-dash/dist/assets/{DiagnosticsView-BocV68Lb.js → DiagnosticsView-oCT624hk.js} +1 -1
- package/bizar-dash/dist/assets/{DiagnosticsView-BocV68Lb.js.map → DiagnosticsView-oCT624hk.js.map} +1 -1
- package/bizar-dash/dist/assets/{DialogsView-BVjPI5x7.js → DialogsView-CqHNals_.js} +1 -1
- package/bizar-dash/dist/assets/{DialogsView-BVjPI5x7.js.map → DialogsView-CqHNals_.js.map} +1 -1
- package/bizar-dash/dist/assets/{DoctorView-84gJcZwM.js → DoctorView-6kfQq_Mx.js} +1 -1
- package/bizar-dash/dist/assets/{DoctorView-84gJcZwM.js.map → DoctorView-6kfQq_Mx.js.map} +1 -1
- package/bizar-dash/dist/assets/{EmptyState-BjgVOrcO.js → EmptyState-DLmtBX_6.js} +1 -1
- package/bizar-dash/dist/assets/{EmptyState-BjgVOrcO.js.map → EmptyState-DLmtBX_6.js.map} +1 -1
- package/bizar-dash/dist/assets/{EnvVarsView-BJ2TEzF7.js → EnvVarsView-DySOXlP1.js} +2 -2
- package/bizar-dash/dist/assets/{EnvVarsView-BJ2TEzF7.js.map → EnvVarsView-DySOXlP1.js.map} +1 -1
- package/bizar-dash/dist/assets/{ErrorState-BE3WBFqF.js → ErrorState-Bump8diE.js} +1 -1
- package/bizar-dash/dist/assets/{ErrorState-BE3WBFqF.js.map → ErrorState-Bump8diE.js.map} +1 -1
- package/bizar-dash/dist/assets/{EvalView-BJJ2P-kK.js → EvalView-FnZ5d93z.js} +1 -1
- package/bizar-dash/dist/assets/{EvalView-BJJ2P-kK.js.map → EvalView-FnZ5d93z.js.map} +1 -1
- package/bizar-dash/dist/assets/{GoalsView-pwROHApi.js → GoalsView-C4m1WnVv.js} +1 -1
- package/bizar-dash/dist/assets/{GoalsView-pwROHApi.js.map → GoalsView-C4m1WnVv.js.map} +1 -1
- package/bizar-dash/dist/assets/{Grid-qcvbhx6e.js → Grid-dEnoGGws.js} +1 -1
- package/bizar-dash/dist/assets/{Grid-qcvbhx6e.js.map → Grid-dEnoGGws.js.map} +1 -1
- package/bizar-dash/dist/assets/{HistoryView-C7SSOLJA.js → HistoryView-xdRybjZ2.js} +1 -1
- package/bizar-dash/dist/assets/{HistoryView-C7SSOLJA.js.map → HistoryView-xdRybjZ2.js.map} +1 -1
- package/bizar-dash/dist/assets/{Input-Bgzxf_Hu.js → Input-BSa3lh_-.js} +1 -1
- package/bizar-dash/dist/assets/{Input-Bgzxf_Hu.js.map → Input-BSa3lh_-.js.map} +1 -1
- package/bizar-dash/dist/assets/{LibrariesView-CbFFksox.js → LibrariesView-1zR04Eci.js} +1 -1
- package/bizar-dash/dist/assets/{LibrariesView-CbFFksox.js.map → LibrariesView-1zR04Eci.js.map} +1 -1
- package/bizar-dash/dist/assets/{LightRAGView-BqDrJbRq.js → LightRAGView-PubmA9oL.js} +1 -1
- package/bizar-dash/dist/assets/{LightRAGView-BqDrJbRq.js.map → LightRAGView-PubmA9oL.js.map} +1 -1
- package/bizar-dash/dist/assets/{ListHeader-TjPJrUfY.js → ListHeader-Ak9WJd39.js} +1 -1
- package/bizar-dash/dist/assets/{ListHeader-TjPJrUfY.js.map → ListHeader-Ak9WJd39.js.map} +1 -1
- package/bizar-dash/dist/assets/{MemoryView-BlTeHLhX.js → MemoryView-DBGpuETw.js} +1 -1
- package/bizar-dash/dist/assets/{MemoryView-BlTeHLhX.js.map → MemoryView-DBGpuETw.js.map} +1 -1
- package/bizar-dash/dist/assets/MiscView-DufqBHkX.js +1 -0
- package/bizar-dash/dist/assets/{MiscView-B8QY82Pm.js.map → MiscView-DufqBHkX.js.map} +1 -1
- package/bizar-dash/dist/assets/{ModsView-CvW1OWts.js → ModsView-DcX6eVU2.js} +2 -2
- package/bizar-dash/dist/assets/{ModsView-CvW1OWts.js.map → ModsView-DcX6eVU2.js.map} +1 -1
- package/bizar-dash/dist/assets/{NotificationsView-CsBt5U7x.js → NotificationsView-B8IhHRQC.js} +1 -1
- package/bizar-dash/dist/assets/{NotificationsView-CsBt5U7x.js.map → NotificationsView-B8IhHRQC.js.map} +1 -1
- package/bizar-dash/dist/assets/ObsidianView-DEuk65zS.js +1 -0
- package/bizar-dash/dist/assets/{ObsidianView-BCTejEj2.js.map → ObsidianView-DEuk65zS.js.map} +1 -1
- package/bizar-dash/dist/assets/{OverviewView-BsuPxwnB.js → OverviewView-CTI2spTd.js} +1 -1
- package/bizar-dash/dist/assets/{OverviewView-BsuPxwnB.js.map → OverviewView-CTI2spTd.js.map} +1 -1
- package/bizar-dash/dist/assets/{ProgressBar-DlZTETEn.js → ProgressBar-D3lkz42E.js} +1 -1
- package/bizar-dash/dist/assets/{ProgressBar-DlZTETEn.js.map → ProgressBar-D3lkz42E.js.map} +1 -1
- package/bizar-dash/dist/assets/{ProjectsView-Soxtmpet.js → ProjectsView-Du0LzE7A.js} +1 -1
- package/bizar-dash/dist/assets/{ProjectsView-Soxtmpet.js.map → ProjectsView-Du0LzE7A.js.map} +1 -1
- package/bizar-dash/dist/assets/{ProvidersView-DaCEV9pM.js → ProvidersView-5bCgXwBo.js} +1 -1
- package/bizar-dash/dist/assets/{ProvidersView-DaCEV9pM.js.map → ProvidersView-5bCgXwBo.js.map} +1 -1
- package/bizar-dash/dist/assets/{SchedulesView-DY2ByBj_.js → SchedulesView-DFHdIz5P.js} +1 -1
- package/bizar-dash/dist/assets/{SchedulesView-DY2ByBj_.js.map → SchedulesView-DFHdIz5P.js.map} +1 -1
- package/bizar-dash/dist/assets/{Select-Dbd88_n8.js → Select-C8EJj4NM.js} +1 -1
- package/bizar-dash/dist/assets/{Select-Dbd88_n8.js.map → Select-C8EJj4NM.js.map} +1 -1
- package/bizar-dash/dist/assets/{SettingsRow-CLMNd434.js → SettingsRow-BdOOmYMD.js} +1 -1
- package/bizar-dash/dist/assets/{SettingsRow-CLMNd434.js.map → SettingsRow-BdOOmYMD.js.map} +1 -1
- package/bizar-dash/dist/assets/{SettingsView-b24oFq9x.js → SettingsView-CqwQ6Knv.js} +1 -1
- package/bizar-dash/dist/assets/{SettingsView-b24oFq9x.js.map → SettingsView-CqwQ6Knv.js.map} +1 -1
- package/bizar-dash/dist/assets/{Sheet-DnDid58j.js → Sheet-Cztsvgse.js} +1 -1
- package/bizar-dash/dist/assets/{Sheet-DnDid58j.js.map → Sheet-Cztsvgse.js.map} +1 -1
- package/bizar-dash/dist/assets/{Slider-BJCvKI1I.js → Slider-BexNfF6K.js} +1 -1
- package/bizar-dash/dist/assets/{Slider-BJCvKI1I.js.map → Slider-BexNfF6K.js.map} +1 -1
- package/bizar-dash/dist/assets/{Sparkline-ktqQLdyb.js → Sparkline-CLZBtvsN.js} +1 -1
- package/bizar-dash/dist/assets/{Sparkline-ktqQLdyb.js.map → Sparkline-CLZBtvsN.js.map} +1 -1
- package/bizar-dash/dist/assets/{Switch-CwXfsDii.js → Switch-B1tiRwNK.js} +1 -1
- package/bizar-dash/dist/assets/{Switch-CwXfsDii.js.map → Switch-B1tiRwNK.js.map} +1 -1
- package/bizar-dash/dist/assets/{TasksView-BHmpzFCf.js → TasksView-CT0fPlos.js} +1 -1
- package/bizar-dash/dist/assets/{TasksView-BHmpzFCf.js.map → TasksView-CT0fPlos.js.map} +1 -1
- package/bizar-dash/dist/assets/{Textarea-BzIAwSlp.js → Textarea-D_TPpVMy.js} +1 -1
- package/bizar-dash/dist/assets/{Textarea-BzIAwSlp.js.map → Textarea-D_TPpVMy.js.map} +1 -1
- package/bizar-dash/dist/assets/{UpdateView-CSmX9NN0.js → UpdateView-tD2q2OhW.js} +1 -1
- package/bizar-dash/dist/assets/{UpdateView-CSmX9NN0.js.map → UpdateView-tD2q2OhW.js.map} +1 -1
- package/bizar-dash/dist/assets/{UsageView-COyr_Gfi.js → UsageView-B4BuAFKA.js} +1 -1
- package/bizar-dash/dist/assets/{UsageView-COyr_Gfi.js.map → UsageView-B4BuAFKA.js.map} +1 -1
- package/bizar-dash/dist/assets/{ViewHeader-BzYZ3m-e.js → ViewHeader-V_SzbUpw.js} +1 -1
- package/bizar-dash/dist/assets/{ViewHeader-BzYZ3m-e.js.map → ViewHeader-V_SzbUpw.js.map} +1 -1
- package/bizar-dash/dist/assets/{VoiceView-D33_xG5t.js → VoiceView-D3vb5OM2.js} +1 -1
- package/bizar-dash/dist/assets/{VoiceView-D33_xG5t.js.map → VoiceView-D3vb5OM2.js.map} +1 -1
- package/bizar-dash/dist/assets/{brain-CM-Vls8r.js → brain-BmfWcitQ.js} +1 -1
- package/bizar-dash/dist/assets/{brain-CM-Vls8r.js.map → brain-BmfWcitQ.js.map} +1 -1
- package/bizar-dash/dist/assets/{calendar-C0lgZk69.js → calendar-VntJUzFB.js} +1 -1
- package/bizar-dash/dist/assets/{calendar-C0lgZk69.js.map → calendar-VntJUzFB.js.map} +1 -1
- package/bizar-dash/dist/assets/{circle-Bx_hQEBC.js → circle-KrQQySeK.js} +1 -1
- package/bizar-dash/dist/assets/{circle-Bx_hQEBC.js.map → circle-KrQQySeK.js.map} +1 -1
- package/bizar-dash/dist/assets/{circle-alert-DHvsqpw5.js → circle-alert-CQ9MLygI.js} +1 -1
- package/bizar-dash/dist/assets/{circle-alert-DHvsqpw5.js.map → circle-alert-CQ9MLygI.js.map} +1 -1
- package/bizar-dash/dist/assets/{circle-check-Bx8wmXR9.js → circle-check-COtVgsWw.js} +1 -1
- package/bizar-dash/dist/assets/{circle-check-Bx8wmXR9.js.map → circle-check-COtVgsWw.js.map} +1 -1
- package/bizar-dash/dist/assets/{clock-CejGoecX.js → clock-C0GlM4qp.js} +1 -1
- package/bizar-dash/dist/assets/{clock-CejGoecX.js.map → clock-C0GlM4qp.js.map} +1 -1
- package/bizar-dash/dist/assets/{download-4cEUWue1.js → download-CW4DmtRe.js} +1 -1
- package/bizar-dash/dist/assets/{download-4cEUWue1.js.map → download-CW4DmtRe.js.map} +1 -1
- package/bizar-dash/dist/assets/{external-link-0NqUhI4i.js → external-link-CAOnh_DH.js} +1 -1
- package/bizar-dash/dist/assets/{external-link-0NqUhI4i.js.map → external-link-CAOnh_DH.js.map} +1 -1
- package/bizar-dash/dist/assets/{file-text-yEMkrSoZ.js → file-text-CS1jB_oV.js} +1 -1
- package/bizar-dash/dist/assets/{file-text-yEMkrSoZ.js.map → file-text-CS1jB_oV.js.map} +1 -1
- package/bizar-dash/dist/assets/{filter-BXt60zAS.js → filter-DyQbml3L.js} +1 -1
- package/bizar-dash/dist/assets/{filter-BXt60zAS.js.map → filter-DyQbml3L.js.map} +1 -1
- package/bizar-dash/dist/assets/{git-pull-request-BjG5lNtR.js → git-pull-request-Co6gYfba.js} +1 -1
- package/bizar-dash/dist/assets/{git-pull-request-BjG5lNtR.js.map → git-pull-request-Co6gYfba.js.map} +1 -1
- package/bizar-dash/dist/assets/{main-CokHAXWM.js → main-CyAIirBB.js} +28 -33
- package/bizar-dash/dist/assets/main-CyAIirBB.js.map +1 -0
- package/bizar-dash/dist/assets/network-CDn43Gsj.js +6 -0
- package/bizar-dash/dist/assets/network-CDn43Gsj.js.map +1 -0
- package/bizar-dash/dist/assets/{pause-CAmulU0F.js → pause-CKYjSTGX.js} +1 -1
- package/bizar-dash/dist/assets/{pause-CAmulU0F.js.map → pause-CKYjSTGX.js.map} +1 -1
- package/bizar-dash/dist/assets/{pencil-eq5IqBmU.js → pencil-BUJ2DpWs.js} +1 -1
- package/bizar-dash/dist/assets/{pencil-eq5IqBmU.js.map → pencil-BUJ2DpWs.js.map} +1 -1
- package/bizar-dash/dist/assets/{play-BoLrrcBA.js → play-CGxZWqtW.js} +1 -1
- package/bizar-dash/dist/assets/{play-BoLrrcBA.js.map → play-CGxZWqtW.js.map} +1 -1
- package/bizar-dash/dist/assets/{plug-DBrVM9lK.js → plug-CQYGgGSy.js} +1 -1
- package/bizar-dash/dist/assets/{plug-DBrVM9lK.js.map → plug-CQYGgGSy.js.map} +1 -1
- package/bizar-dash/dist/assets/{plus-DENm0hqV.js → plus-fFxt-8z5.js} +1 -1
- package/bizar-dash/dist/assets/{plus-DENm0hqV.js.map → plus-fFxt-8z5.js.map} +1 -1
- package/bizar-dash/dist/assets/{power-DLjXF7zE.js → power-Cjgy2KsI.js} +1 -1
- package/bizar-dash/dist/assets/{power-DLjXF7zE.js.map → power-Cjgy2KsI.js.map} +1 -1
- package/bizar-dash/dist/assets/{power-off-D47cOnG_.js → power-off-B81hLRah.js} +1 -1
- package/bizar-dash/dist/assets/{power-off-D47cOnG_.js.map → power-off-B81hLRah.js.map} +1 -1
- package/bizar-dash/dist/assets/{refresh-ccw-DNrXX9gB.js → refresh-ccw-BCXx86xp.js} +1 -1
- package/bizar-dash/dist/assets/{refresh-ccw-DNrXX9gB.js.map → refresh-ccw-BCXx86xp.js.map} +1 -1
- package/bizar-dash/dist/assets/{refresh-cw-CoAHDpeS.js → refresh-cw-COTg1nZo.js} +1 -1
- package/bizar-dash/dist/assets/{refresh-cw-CoAHDpeS.js.map → refresh-cw-COTg1nZo.js.map} +1 -1
- package/bizar-dash/dist/assets/{rotate-ccw-ObX5awaG.js → rotate-ccw-BZrEVc0Z.js} +1 -1
- package/bizar-dash/dist/assets/{rotate-ccw-ObX5awaG.js.map → rotate-ccw-BZrEVc0Z.js.map} +1 -1
- package/bizar-dash/dist/assets/{rotate-cw-xpPJw9LP.js → rotate-cw-CsTtaLV2.js} +1 -1
- package/bizar-dash/dist/assets/{rotate-cw-xpPJw9LP.js.map → rotate-cw-CsTtaLV2.js.map} +1 -1
- package/bizar-dash/dist/assets/{save-D1iKiJJl.js → save-DtMTge1F.js} +1 -1
- package/bizar-dash/dist/assets/{save-D1iKiJJl.js.map → save-DtMTge1F.js.map} +1 -1
- package/bizar-dash/dist/assets/{send-BRcAhZUm.js → send-DyBhyk8t.js} +1 -1
- package/bizar-dash/dist/assets/{send-BRcAhZUm.js.map → send-DyBhyk8t.js.map} +1 -1
- package/bizar-dash/dist/assets/{shield-alert-C6vEcK-g.js → shield-alert-BPGJlRF8.js} +1 -1
- package/bizar-dash/dist/assets/{shield-alert-C6vEcK-g.js.map → shield-alert-BPGJlRF8.js.map} +1 -1
- package/bizar-dash/dist/assets/{trash-2-DZDjEnNh.js → trash-2-2NsyTU8c.js} +1 -1
- package/bizar-dash/dist/assets/{trash-2-DZDjEnNh.js.map → trash-2-2NsyTU8c.js.map} +1 -1
- package/bizar-dash/dist/assets/{trending-up-DMj-AJVf.js → trending-up-BXwP23VK.js} +1 -1
- package/bizar-dash/dist/assets/{trending-up-DMj-AJVf.js.map → trending-up-BXwP23VK.js.map} +1 -1
- package/bizar-dash/dist/assets/{triangle-alert-_2xe_tl4.js → triangle-alert-DFmQ_RXN.js} +1 -1
- package/bizar-dash/dist/assets/{triangle-alert-_2xe_tl4.js.map → triangle-alert-DFmQ_RXN.js.map} +1 -1
- package/bizar-dash/dist/assets/{upload-DdDljAeV.js → upload-Cj-HYRYV.js} +1 -1
- package/bizar-dash/dist/assets/{upload-DdDljAeV.js.map → upload-Cj-HYRYV.js.map} +1 -1
- package/bizar-dash/dist/assets/{zap-mHsgh0FP.js → zap-BaOiybcl.js} +1 -1
- package/bizar-dash/dist/assets/{zap-mHsgh0FP.js.map → zap-BaOiybcl.js.map} +1 -1
- package/bizar-dash/dist/index.html +1 -1
- package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/bizar-dash/src/server/routes/update.mjs +48 -21
- package/bizar-dash/src/web/v8/App.tsx +0 -4
- package/bizar-dash/src/web/v8/__tests__/new-views.test.tsx +0 -17
- package/bizar-dash/src/web/v8/shell/Sidebar.tsx +0 -1
- package/bizar-dash/src/web/v8/views/Router.tsx +0 -5
- package/bizar-dash/tests/_update-test-helpers.mjs +13 -0
- package/bizar-dash/tests/update-check.test.mjs +18 -13
- package/bizar-dash/tests/update-packages.test.mjs +154 -0
- package/bizar-dash/tests/update-run.test.mjs +17 -80
- package/cli/banner.mjs +1 -1
- package/cli/bin.mjs +8 -1
- package/cli/bootstrap.mjs +1 -1
- package/cli/commands/claude-cmd.mjs +1 -1
- package/cli/commands/deploy/docker.mjs +0 -3
- package/cli/install/banner.mjs +67 -0
- package/cli/install/banner.test.mjs +39 -0
- package/cli/install/index.mjs +32 -0
- package/cli/install/index.test.mjs +61 -0
- package/cli/install/paths.mjs +80 -0
- package/cli/install/paths.test.mjs +77 -0
- package/cli/install/plugin.mjs +161 -0
- package/cli/install/postinstall.mjs +111 -0
- package/cli/install.mjs +13 -600
- package/cli/migrate.mjs +157 -0
- package/cli/migrate.test.mjs +85 -0
- package/cli/provision.mjs +615 -1918
- package/cli/provision.test.mjs +1 -1
- package/cli/service-controller.test.mjs +0 -1
- package/cli/service-env.mjs +0 -1
- package/cli/utils.mjs +0 -1
- package/install.sh +54 -466
- package/package.json +1 -1
- package/packages/sdk/package.json +1 -1
- package/scripts/install-curl.sh +17 -0
- package/bizar-dash/dist/assets/HeadroomView-D1UiTYbt.js +0 -11
- package/bizar-dash/dist/assets/HeadroomView-D1UiTYbt.js.map +0 -1
- package/bizar-dash/dist/assets/MiscView-B8QY82Pm.js +0 -1
- package/bizar-dash/dist/assets/ObsidianView-BCTejEj2.js +0 -1
- package/bizar-dash/dist/assets/main-CokHAXWM.js.map +0 -1
- package/bizar-dash/skills/headroom/SKILL.md +0 -94
- package/bizar-dash/src/server/headroom.mjs +0 -649
- package/bizar-dash/src/server/routes/headroom.mjs +0 -126
- package/bizar-dash/src/web/v8/views/Headroom/HeadroomView.tsx +0 -201
- package/bizar-dash/tests/graphify-mod-spawn.node.test.mjs +0 -90
- package/bizar-dash/tests/headroom-install.test.mjs +0 -173
- package/bizar-dash/tests/headroom-settings.test.mjs +0 -126
- package/bizar-dash/tests/headroom-status.test.mjs +0 -117
- package/bizar-dash/tests/v7-archive/mobile-overview.test.tsx +0 -214
- package/bizar-dash/tests/v7-archive/mobile-settings.test.tsx +0 -181
- package/bizar-dash/tests/v7-archive/navigation/CommandPalette.test.tsx +0 -105
- package/bizar-dash/tests/v7-archive/settings-nav.test.tsx +0 -128
- package/bizar-dash/tests/v7-archive/views/Overview.test.tsx +0 -413
- package/cli/commands/headroom.mjs +0 -204
- package/cli/provision-claude.mjs +0 -1176
package/cli/provision-claude.mjs
DELETED
|
@@ -1,1176 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* cli/provision-claude.mjs
|
|
4
|
-
*
|
|
5
|
-
* v6.3.0 — Claude Code-native installer + updater.
|
|
6
|
-
*
|
|
7
|
-
* Replaces `cli/provision.mjs` (the Cline-era provisioner). The install
|
|
8
|
-
* / update flow for Claude Code is different from Cline:
|
|
9
|
-
*
|
|
10
|
-
* - Config root is `~/.claude/` (NOT `~/.cline/`).
|
|
11
|
-
* - The configuration file is `~/.claude/settings.json` (NOT
|
|
12
|
-
* `~/.cline/cline.json`).
|
|
13
|
-
* - MCP servers are registered via `claude mcp add` (NOT by
|
|
14
|
-
* patching a `plugin[]` array).
|
|
15
|
-
* - Plugins are managed via `claude plugin install ...` (NOT
|
|
16
|
-
* copied into `~/.cline/plugins/bizar/`).
|
|
17
|
-
* - Skills live under `~/.claude/skills/<name>/SKILL.md`.
|
|
18
|
-
* - Commands live under `~/.claude/commands/<name>.md`.
|
|
19
|
-
* - Rules live under `~/.claude/rules/<name>.md`.
|
|
20
|
-
* - Hooks are wired into `~/.claude/settings.json` (NOT installed
|
|
21
|
-
* as executable files in `~/.cline/hooks/`).
|
|
22
|
-
* - `~/.claude/CLAUDE.md` mirrors the repo's `AGENTS.md`.
|
|
23
|
-
*
|
|
24
|
-
* Public API:
|
|
25
|
-
* runProvision({ mode, dryRun, force, yes, restart, ... })
|
|
26
|
-
* Runs the full provision flow for `mode` ('install' | 'update').
|
|
27
|
-
* detectState()
|
|
28
|
-
* Probe what's installed without modifying anything.
|
|
29
|
-
* Returns a structured state object.
|
|
30
|
-
*
|
|
31
|
-
* Both modes are idempotent and safe to re-run.
|
|
32
|
-
*
|
|
33
|
-
* Flags (parsed by the CLI shim at the bottom):
|
|
34
|
-
* --mode=install|update|install-only-system
|
|
35
|
-
* --dry-run Print actions, make no changes.
|
|
36
|
-
* --force Overwrite existing files.
|
|
37
|
-
* --yes / -y Same as --force.
|
|
38
|
-
* --non-interactive Same as --yes.
|
|
39
|
-
*/
|
|
40
|
-
|
|
41
|
-
import chalk from 'chalk';
|
|
42
|
-
import { execFileSync, execSync, spawn, spawnSync } from 'node:child_process';
|
|
43
|
-
import {
|
|
44
|
-
chmodSync,
|
|
45
|
-
copyFileSync,
|
|
46
|
-
existsSync,
|
|
47
|
-
mkdirSync,
|
|
48
|
-
readdirSync,
|
|
49
|
-
readFileSync,
|
|
50
|
-
rmSync,
|
|
51
|
-
statSync,
|
|
52
|
-
writeFileSync,
|
|
53
|
-
} from 'node:fs';
|
|
54
|
-
import { homedir } from 'node:os';
|
|
55
|
-
import { dirname, join, resolve, sep } from 'node:path';
|
|
56
|
-
import { fileURLToPath } from 'node:url';
|
|
57
|
-
|
|
58
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
59
|
-
const __dirname = dirname(__filename);
|
|
60
|
-
const HOME = homedir();
|
|
61
|
-
|
|
62
|
-
// Repo root = `<pkg>/cli/provision-claude.mjs` → one level up.
|
|
63
|
-
// This works for source checkouts AND global npm installs.
|
|
64
|
-
export const REPO_ROOT = resolve(__dirname, '..');
|
|
65
|
-
|
|
66
|
-
// ─── Paths ────────────────────────────────────────────────────────────────────
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Resolve the Claude Code config directory.
|
|
70
|
-
*
|
|
71
|
-
* Resolution order (matches Claude Code's own resolver, see
|
|
72
|
-
* https://code.claude.com/docs/en/cli-reference):
|
|
73
|
-
* 1. `process.env.CLAUDE_CONFIG_DIR` (explicit override)
|
|
74
|
-
* 2. `$HOME/.claude` (default)
|
|
75
|
-
* 3. (Windows only) `%APPDATA%\Claude`
|
|
76
|
-
*/
|
|
77
|
-
export function resolveClaudeDir() {
|
|
78
|
-
if (process.env.CLAUDE_CONFIG_DIR && process.env.CLAUDE_CONFIG_DIR.trim()) {
|
|
79
|
-
return process.env.CLAUDE_CONFIG_DIR.trim();
|
|
80
|
-
}
|
|
81
|
-
if (process.platform === 'win32') {
|
|
82
|
-
return process.env.APPDATA
|
|
83
|
-
? join(process.env.APPDATA, 'Claude')
|
|
84
|
-
: join(HOME, '.claude');
|
|
85
|
-
}
|
|
86
|
-
return join(HOME, '.claude');
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export const CLAUDE_DIR = resolveClaudeDir();
|
|
90
|
-
|
|
91
|
-
/** `~/.bizar_home/` — Bizar-specific runtime state (NOT under ~/.claude/). */
|
|
92
|
-
export const BIZAR_HOME = process.env.BIZAR_HOME
|
|
93
|
-
|| join(process.env.XDG_CONFIG_HOME || join(HOME, '.config'), 'bizar');
|
|
94
|
-
|
|
95
|
-
// Standard Claude Code subdirectories (v6.3.0 — Claude Code layout)
|
|
96
|
-
export const CLAUDE_AGENTS_DIR = join(CLAUDE_DIR, 'agents');
|
|
97
|
-
export const CLAUDE_SKILLS_DIR = join(CLAUDE_DIR, 'skills');
|
|
98
|
-
export const CLAUDE_COMMANDS_DIR = join(CLAUDE_DIR, 'commands');
|
|
99
|
-
export const CLAUDE_HOOKS_DIR = join(CLAUDE_DIR, 'hooks');
|
|
100
|
-
export const CLAUDE_RULES_DIR = join(CLAUDE_DIR, 'rules');
|
|
101
|
-
export const CLAUDE_PLUGINS_DIR = join(CLAUDE_DIR, 'plugins');
|
|
102
|
-
|
|
103
|
-
// Service / dashboard pid files
|
|
104
|
-
const SERVICE_PID_FILE = join(BIZAR_HOME, 'service.pid');
|
|
105
|
-
const DASHBOARD_PID_FILE = join(BIZAR_HOME, 'dashboard.pid');
|
|
106
|
-
const DASHBOARD_PORT_FILE = join(BIZAR_HOME, 'dashboard.port');
|
|
107
|
-
|
|
108
|
-
// ─── Tiny utilities ──────────────────────────────────────────────────────────
|
|
109
|
-
|
|
110
|
-
function haveCmd(cmd) {
|
|
111
|
-
try {
|
|
112
|
-
execSync(`command -v ${cmd}`, { stdio: ['ignore', 'pipe', 'ignore'] });
|
|
113
|
-
return true;
|
|
114
|
-
} catch {
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Build the SDK workspace so its compiled JS output lives at
|
|
121
|
-
* `<pkg>/packages/sdk/dist/`. The dashboard imports the SDK memory
|
|
122
|
-
* module from `dist/memory/index.js`, not from `src/memory/index.js`,
|
|
123
|
-
* so without this build step the npm-installed package ships a TS
|
|
124
|
-
* source tree that the dashboard cannot run.
|
|
125
|
-
*
|
|
126
|
-
* v7.0.1 — `install.sh` + `bizar update` previously skipped this step
|
|
127
|
-
* entirely. Symptom: `bizar dash start` crashes with
|
|
128
|
-
* `Cannot find module .../packages/sdk/dist/memory/index.js`
|
|
129
|
-
* (or .../src/memory/index.js for older import paths). Root cause: the
|
|
130
|
-
* SDK's `package.json` advertises `./dist/*` exports, but the SDK's
|
|
131
|
-
* `tsconfig.json` is never compiled by the installer.
|
|
132
|
-
*
|
|
133
|
-
* Bun resolution: when bun is invoked from a globally npm-installed
|
|
134
|
-
* package, its lifecycle script runs with a minimal PATH that does
|
|
135
|
-
* NOT include `~/.bun/bin`. So `bun run build:sdk` (which shells out to
|
|
136
|
-
* `tsc` via bun's bin shim) fails with `tsc: command not found`. To
|
|
137
|
-
* dodge that, we resolve bun to its absolute path
|
|
138
|
-
* (`$HOME/.bun/bin/bun`) and prepend that dir to the child process
|
|
139
|
-
* PATH so bun's tsc shim is visible.
|
|
140
|
-
*
|
|
141
|
-
* Idempotent: skips when `dist/memory/index.js` already exists.
|
|
142
|
-
* Non-fatal: returns `{ ok: false, message }` rather than throwing —
|
|
143
|
-
* the rest of the provision flow should still complete (some operators
|
|
144
|
-
* run a separate build pipeline, e.g. a CI step).
|
|
145
|
-
*/
|
|
146
|
-
export async function buildSdk({ dryRun = false } = {}) {
|
|
147
|
-
const sdkRoot = join(REPO_ROOT, 'packages', 'sdk');
|
|
148
|
-
const srcEntry = join(sdkRoot, 'src', 'memory', 'index.ts');
|
|
149
|
-
const distEntry = join(sdkRoot, 'dist', 'memory', 'index.js');
|
|
150
|
-
|
|
151
|
-
if (!existsSync(srcEntry)) {
|
|
152
|
-
// No SDK TS source — this is a stripped install or an unusual layout.
|
|
153
|
-
// Don't fail the whole provision over it; just report.
|
|
154
|
-
return { ok: true, skipped: true, message: `SDK src not found at ${srcEntry} — skipping build (likely a non-bundled install)` };
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (existsSync(distEntry)) {
|
|
158
|
-
return { ok: true, skipped: true, message: `SDK dist already present — skipping build (delete ${join(sdkRoot, 'dist')} to force)` };
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
if (dryRun) {
|
|
162
|
-
return { ok: true, skipped: false, message: `would build SDK at ${sdkRoot}` };
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Resolve bun to an absolute path. `command -v bun` finds whatever is
|
|
166
|
-
// on PATH, but bun's bin dir (typically `~/.bun/bin`) is NOT always on
|
|
167
|
-
// PATH — especially when invoked from a globally npm-installed package
|
|
168
|
-
// whose lifecycle scripts run with a minimal env. Without the absolute
|
|
169
|
-
// path, `bun run build:sdk` shells out to `tsc`, finds no shim on PATH,
|
|
170
|
-
// and fails with `tsc: command not found`.
|
|
171
|
-
//
|
|
172
|
-
// Bun's bin dir contains only `bun` and `bunx` — NOT `tsc`. So even
|
|
173
|
-
// when bun runs `build:sdk` (which executes the `tsc` line from
|
|
174
|
-
// package.json), the child shell still needs `tsc` on PATH. The npm
|
|
175
|
-
// global bin dir (where `npm install -g typescript` lands the shim) is
|
|
176
|
-
// the typical source — resolve it via `npm root -g`'s parent and
|
|
177
|
-
// prepend both bun's bin and the npm global bin to the child PATH.
|
|
178
|
-
const bunFromHome = join(process.env.HOME || '', '.bun', 'bin', 'bun');
|
|
179
|
-
let cmd, args, childEnv;
|
|
180
|
-
if (existsSync(bunFromHome)) {
|
|
181
|
-
cmd = bunFromHome;
|
|
182
|
-
args = ['run', 'build:sdk'];
|
|
183
|
-
const pathDelim = process.platform === 'win32' ? ';' : ':';
|
|
184
|
-
const bunBin = dirname(bunFromHome);
|
|
185
|
-
let npmGlobalBin = '';
|
|
186
|
-
try {
|
|
187
|
-
const npmRootG = execSync('npm root -g', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
|
|
188
|
-
// npm root -g returns .../lib/node_modules — parent is .../lib, and
|
|
189
|
-
// the bin dir lives at .../bin. So walk up one and over.
|
|
190
|
-
npmGlobalBin = join(dirname(npmRootG), '..', 'bin');
|
|
191
|
-
} catch {
|
|
192
|
-
// npm not on PATH — fall back to the standard location.
|
|
193
|
-
npmGlobalBin = process.platform === 'win32'
|
|
194
|
-
? join(process.env.APPDATA || '', 'npm')
|
|
195
|
-
: join(process.env.HOME || '', '.local', 'bin');
|
|
196
|
-
}
|
|
197
|
-
const pathParts = [bunBin];
|
|
198
|
-
if (npmGlobalBin && existsSync(npmGlobalBin)) pathParts.push(npmGlobalBin);
|
|
199
|
-
pathParts.push(process.env.PATH || '');
|
|
200
|
-
childEnv = { ...process.env, PATH: pathParts.join(pathDelim) };
|
|
201
|
-
} else if (haveCmd('bun')) {
|
|
202
|
-
cmd = 'bun';
|
|
203
|
-
args = ['run', 'build:sdk'];
|
|
204
|
-
childEnv = process.env;
|
|
205
|
-
} else {
|
|
206
|
-
// Fallback: npx tsc. Requires a network round-trip on first use but
|
|
207
|
-
// works on systems without bun installed (CI, minimal containers).
|
|
208
|
-
cmd = 'npx';
|
|
209
|
-
args = ['--yes', 'tsc', '-p', 'packages/sdk/tsconfig.json'];
|
|
210
|
-
childEnv = process.env;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
try {
|
|
214
|
-
logInfo(`building SDK with \`${cmd} ${args.join(' ')}\` (REPO_ROOT=${REPO_ROOT})`);
|
|
215
|
-
execFileSync(cmd, args, { cwd: REPO_ROOT, stdio: 'pipe', timeout: 180_000, env: childEnv });
|
|
216
|
-
if (existsSync(distEntry)) {
|
|
217
|
-
return { ok: true, skipped: false, message: 'SDK built (dist/memory/index.js present)' };
|
|
218
|
-
}
|
|
219
|
-
return { ok: false, message: `build completed but ${distEntry} still missing — check tsconfig outDir` };
|
|
220
|
-
} catch (err) {
|
|
221
|
-
const stderr = err.stderr ? err.stderr.toString().split('\n').slice(0, 5).join(' | ') : '(no stderr)';
|
|
222
|
-
return { ok: false, message: `SDK build failed: ${stderr}` };
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Build the Bizar plugin shim (`plugins/bizar/`) so the loader can find
|
|
228
|
-
* `plugins/bizar/dist/index.js` at runtime. Same root cause as `buildSdk()`:
|
|
229
|
-
* the npm-installed package ships `plugins/bizar/index.ts` (TS source) but
|
|
230
|
-
* nothing at `dist/`. Without this build step, `bizar dash start` reports
|
|
231
|
-
* `Plugin source not found at .../plugins/bizar` from `cli/install.mjs:installPluginBizar`
|
|
232
|
-
* (which is misleading — the dir exists, just the compiled entry doesn't).
|
|
233
|
-
*
|
|
234
|
-
* v8.0.2 — same bun/npx/tsc strategy as `buildSdk()`. We compile the
|
|
235
|
-
* single TS entry `plugins/bizar/index.ts` to `plugins/bizar/dist/index.js`
|
|
236
|
-
* via the existing `plugins/bizar/tsconfig.json`.
|
|
237
|
-
*
|
|
238
|
-
* Idempotent: skips when `plugins/bizar/dist/index.js` already exists.
|
|
239
|
-
* Non-fatal: returns `{ ok: false, message }` rather than throwing.
|
|
240
|
-
*/
|
|
241
|
-
export async function buildPlugin({ dryRun = false } = {}) {
|
|
242
|
-
const pluginRoot = join(REPO_ROOT, 'plugins', 'bizar');
|
|
243
|
-
const srcEntry = join(pluginRoot, 'index.ts');
|
|
244
|
-
const distEntry = join(pluginRoot, 'dist', 'index.js');
|
|
245
|
-
|
|
246
|
-
if (!existsSync(srcEntry)) {
|
|
247
|
-
return { ok: true, skipped: true, message: `Plugin src not found at ${srcEntry} — skipping build` };
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
if (existsSync(distEntry)) {
|
|
251
|
-
return { ok: true, skipped: true, message: `Plugin dist already present — skipping build (delete ${join(pluginRoot, 'dist')} to force)` };
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
if (dryRun) {
|
|
255
|
-
return { ok: true, skipped: false, message: `would build plugin at ${pluginRoot}` };
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// Same PATH-resolution dance as buildSdk(): npm-global scripts may have
|
|
259
|
-
// a stripped PATH that doesn't include ~/.bun/bin or the npm bin dir,
|
|
260
|
-
// so bun's tsc shim isn't visible to the child shell.
|
|
261
|
-
const bunFromHome = join(process.env.HOME || '', '.bun', 'bin', 'bun');
|
|
262
|
-
let cmd, args, childEnv;
|
|
263
|
-
if (existsSync(bunFromHome)) {
|
|
264
|
-
cmd = bunFromHome;
|
|
265
|
-
args = ['x', 'tsc', '-p', 'plugins/bizar/tsconfig.json'];
|
|
266
|
-
const pathDelim = process.platform === 'win32' ? ';' : ':';
|
|
267
|
-
const bunBin = dirname(bunFromHome);
|
|
268
|
-
let npmGlobalBin = '';
|
|
269
|
-
try {
|
|
270
|
-
const npmRootG = execSync('npm root -g', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
|
|
271
|
-
npmGlobalBin = join(dirname(npmRootG), '..', 'bin');
|
|
272
|
-
} catch {
|
|
273
|
-
npmGlobalBin = process.platform === 'win32'
|
|
274
|
-
? join(process.env.APPDATA || '', 'npm')
|
|
275
|
-
: join(process.env.HOME || '', '.local', 'bin');
|
|
276
|
-
}
|
|
277
|
-
const pathParts = [bunBin];
|
|
278
|
-
if (npmGlobalBin && existsSync(npmGlobalBin)) pathParts.push(npmGlobalBin);
|
|
279
|
-
pathParts.push(process.env.PATH || '');
|
|
280
|
-
childEnv = { ...process.env, PATH: pathParts.join(pathDelim) };
|
|
281
|
-
} else if (haveCmd('bun')) {
|
|
282
|
-
cmd = 'bun';
|
|
283
|
-
args = ['x', 'tsc', '-p', 'plugins/bizar/tsconfig.json'];
|
|
284
|
-
childEnv = process.env;
|
|
285
|
-
} else {
|
|
286
|
-
cmd = 'npx';
|
|
287
|
-
args = ['--yes', 'tsc', '-p', 'plugins/bizar/tsconfig.json'];
|
|
288
|
-
childEnv = process.env;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
try {
|
|
292
|
-
logInfo(`building plugin with \`${cmd} ${args.join(' ')}\` (REPO_ROOT=${REPO_ROOT})`);
|
|
293
|
-
execFileSync(cmd, args, { cwd: REPO_ROOT, stdio: 'pipe', timeout: 180_000, env: childEnv });
|
|
294
|
-
if (existsSync(distEntry)) {
|
|
295
|
-
return { ok: true, skipped: false, message: 'Plugin built (dist/index.js present)' };
|
|
296
|
-
}
|
|
297
|
-
return { ok: false, message: `build completed but ${distEntry} still missing — check tsconfig outDir` };
|
|
298
|
-
} catch (err) {
|
|
299
|
-
const stderr = err.stderr ? err.stderr.toString().split('\n').slice(0, 5).join(' | ') : '(no stderr)';
|
|
300
|
-
return { ok: false, message: `Plugin build failed: ${stderr}` };
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* Build the dashboard SPA (Vite production bundle) so the Node server
|
|
306
|
-
* in `bizar-dash/src/server/server.mjs` can serve the React UI from
|
|
307
|
-
* `bizar-dash/dist/index.html` + `dist/assets/*`. Without this step,
|
|
308
|
-
* npm installs that don't ship a pre-built `dist/` (the published
|
|
309
|
-
* tarball excludes it because it is `.gitignore`'d) leave the
|
|
310
|
-
* dashboard serving 404s for every asset, and the page renders blank
|
|
311
|
-
* with errors like `can't access property 'useState', et is undefined`.
|
|
312
|
-
*
|
|
313
|
-
* v7.0.3 — `install.sh` + `bizar update` previously skipped the
|
|
314
|
-
* dashboard build entirely. Symptom: a fresh `bizar install` /
|
|
315
|
-
* `bizar update` overwrote the package's pre-existing `dist/` (if
|
|
316
|
-
* any) and the dashboard's first request returned a 404 for the
|
|
317
|
-
* bundle, followed by the React error above in the browser console.
|
|
318
|
-
*
|
|
319
|
-
* Same bun / npx resolution strategy as `buildSdk()`. The vite config
|
|
320
|
-
* reads the same `tsconfig.json` for type info, but the heavy lifting
|
|
321
|
-
* is `vite build` itself which uses esbuild internally, so no tsc
|
|
322
|
-
* dependency here.
|
|
323
|
-
*
|
|
324
|
-
* Idempotent: skips when `bizar-dash/dist/index.html` already exists.
|
|
325
|
-
* Non-fatal: returns `{ ok: false, message }` rather than throwing —
|
|
326
|
-
* the rest of the provision flow should still complete.
|
|
327
|
-
*/
|
|
328
|
-
export async function buildDash({ dryRun = false } = {}) {
|
|
329
|
-
const dashDir = join(REPO_ROOT, 'bizar-dash');
|
|
330
|
-
const distIndex = join(dashDir, 'dist', 'index.html');
|
|
331
|
-
|
|
332
|
-
if (!existsSync(join(dashDir, 'src', 'web', 'index.html'))) {
|
|
333
|
-
return { ok: true, skipped: true, message: `Dashboard src not found at ${join(dashDir, 'src', 'web', 'index.html')} — skipping build` };
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
if (existsSync(distIndex)) {
|
|
337
|
-
return { ok: true, skipped: true, message: `Dashboard dist already present — skipping build (delete ${join(dashDir, 'dist')} to force)` };
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
if (dryRun) {
|
|
341
|
-
return { ok: true, skipped: false, message: `would build dashboard at ${dashDir}` };
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
const bunFromHome = join(process.env.HOME || '', '.bun', 'bin', 'bun');
|
|
345
|
-
let cmd, args, childEnv;
|
|
346
|
-
if (existsSync(bunFromHome)) {
|
|
347
|
-
cmd = bunFromHome;
|
|
348
|
-
args = ['run', 'build:dash'];
|
|
349
|
-
const pathDelim = process.platform === 'win32' ? ';' : ':';
|
|
350
|
-
const bunBin = dirname(bunFromHome);
|
|
351
|
-
const pathParts = [bunBin, process.env.PATH || ''];
|
|
352
|
-
childEnv = { ...process.env, PATH: pathParts.join(pathDelim) };
|
|
353
|
-
} else if (haveCmd('bun')) {
|
|
354
|
-
cmd = 'bun';
|
|
355
|
-
args = ['run', 'build:dash'];
|
|
356
|
-
childEnv = process.env;
|
|
357
|
-
} else {
|
|
358
|
-
cmd = 'npx';
|
|
359
|
-
args = ['--yes', 'vite', 'build'];
|
|
360
|
-
childEnv = process.env;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
try {
|
|
364
|
-
logInfo(`building dashboard with \`${cmd} ${args.join(' ')}\` (REPO_ROOT=${REPO_ROOT})`);
|
|
365
|
-
// `vite build` writes to the configured outDir (../../dist from
|
|
366
|
-
// src/web/). On a non-bun shell the cwd must be REPO_ROOT because
|
|
367
|
-
// the configured `root` is relative. Bun handles this either way.
|
|
368
|
-
execFileSync(cmd, args, { cwd: REPO_ROOT, stdio: 'pipe', timeout: 240_000, env: childEnv });
|
|
369
|
-
if (existsSync(distIndex)) {
|
|
370
|
-
return { ok: true, skipped: false, message: 'Dashboard built (dist/index.html present)' };
|
|
371
|
-
}
|
|
372
|
-
return { ok: false, message: `build completed but ${distIndex} still missing — check vite.config.ts outDir` };
|
|
373
|
-
} catch (err) {
|
|
374
|
-
const stderr = err.stderr ? err.stderr.toString().split('\n').slice(0, 5).join(' | ') : '(no stderr)';
|
|
375
|
-
return { ok: false, message: `Dashboard build failed: ${stderr}` };
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
function readTextSafe(file, fallback = '') {
|
|
380
|
-
try {
|
|
381
|
-
if (!existsSync(file)) return fallback;
|
|
382
|
-
return readFileSync(file, 'utf8');
|
|
383
|
-
} catch {
|
|
384
|
-
return fallback;
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
function readJsonSafe(file, fallback = null) {
|
|
389
|
-
try {
|
|
390
|
-
if (!existsSync(file)) return fallback;
|
|
391
|
-
return JSON.parse(readFileSync(file, 'utf8'));
|
|
392
|
-
} catch {
|
|
393
|
-
return fallback;
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
function ensureDir(dir) {
|
|
398
|
-
mkdirSync(dir, { recursive: true });
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
function copyDirContents(src, dest) {
|
|
402
|
-
ensureDir(dest);
|
|
403
|
-
for (const name of readdirSync(src)) {
|
|
404
|
-
const sp = join(src, name);
|
|
405
|
-
const dp = join(dest, name);
|
|
406
|
-
const st = statSync(sp);
|
|
407
|
-
if (st.isDirectory()) copyDirContents(sp, dp);
|
|
408
|
-
else copyFileSync(sp, dp);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
function rimrafSafe(p) {
|
|
413
|
-
try { rmSync(p, { recursive: true, force: true }); } catch { /* ignore */ }
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
function logOk(msg) { console.log(chalk.green(' ✓') + ' ' + msg); }
|
|
417
|
-
function logInfo(msg) { console.log(chalk.cyan(' →') + ' ' + msg); }
|
|
418
|
-
function logWarn(msg) { console.log(chalk.yellow(' ⚠') + ' ' + msg); }
|
|
419
|
-
function logErr(msg) { console.log(chalk.red(' ✗') + ' ' + msg); }
|
|
420
|
-
function section(title) { console.log('\n' + chalk.bold.cyan(`── ${title} ──`)); }
|
|
421
|
-
|
|
422
|
-
// ─── Bizar HOME bootstrap ────────────────────────────────────────────────────
|
|
423
|
-
|
|
424
|
-
/**
|
|
425
|
-
* Ensure `~/.bizar_home/` (or `$BIZAR_HOME`) exists with the runtime
|
|
426
|
-
* subdirectories Bizar needs (memory vault, loops dir, dashboard port
|
|
427
|
-
* file). Idempotent.
|
|
428
|
-
*/
|
|
429
|
-
export function ensureBizarHome({ dryRun = false } = {}) {
|
|
430
|
-
if (dryRun) {
|
|
431
|
-
return { ok: true, message: `[dry-run] would ensure ${BIZAR_HOME}` };
|
|
432
|
-
}
|
|
433
|
-
mkdirSync(BIZAR_HOME, { recursive: true });
|
|
434
|
-
mkdirSync(join(BIZAR_HOME, 'memory-vault'), { recursive: true });
|
|
435
|
-
mkdirSync(join(BIZAR_HOME, 'loops'), { recursive: true });
|
|
436
|
-
// Touch the dashboard port file so downstream readers can probe it.
|
|
437
|
-
if (!existsSync(DASHBOARD_PORT_FILE)) {
|
|
438
|
-
writeFileSync(DASHBOARD_PORT_FILE, '4321\n');
|
|
439
|
-
}
|
|
440
|
-
return { ok: true, message: `${BIZAR_HOME} ready`, path: BIZAR_HOME };
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
// ─── Toolchain ───────────────────────────────────────────────────────────────
|
|
444
|
-
|
|
445
|
-
function checkToolchain() {
|
|
446
|
-
section('Toolchain');
|
|
447
|
-
if (haveCmd('node')) logOk(`node ${execSync('node --version').toString().trim()}`);
|
|
448
|
-
else { logErr('node not on PATH — install Node.js 18+ from https://nodejs.org'); process.exit(1); }
|
|
449
|
-
|
|
450
|
-
if (haveCmd('npm')) logOk(`npm ${execSync('npm --version').toString().trim()}`);
|
|
451
|
-
else logWarn('npm not on PATH — Claude Code install will need npm');
|
|
452
|
-
|
|
453
|
-
if (haveCmd('git')) logOk(`git ${execSync('git --version').toString().trim().split(' ')[2]}`);
|
|
454
|
-
else logWarn('git not on PATH — optional for many tasks');
|
|
455
|
-
|
|
456
|
-
if (haveCmd('claude')) logOk(`claude ${execSync('claude --version').toString().trim().split('\n')[0]}`);
|
|
457
|
-
else logWarn('claude not on PATH — will install via npm');
|
|
458
|
-
|
|
459
|
-
if (haveCmd('agent-browser')) logOk(`agent-browser ${execSync('agent-browser --version').toString().trim()}`);
|
|
460
|
-
else logInfo('agent-browser not installed (optional; npm install -g agent-browser)');
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
/** Install Claude Code CLI + SDK globally via npm. Idempotent. */
|
|
464
|
-
function installClaudeCli({ force = false, dryRun = false } = {}) {
|
|
465
|
-
if (haveCmd('claude') && !force) return;
|
|
466
|
-
section('Installing Claude Code CLI');
|
|
467
|
-
logInfo('npm install -g @anthropic-ai/claude-agent-sdk @anthropic-ai/claude-code');
|
|
468
|
-
if (dryRun) return;
|
|
469
|
-
try {
|
|
470
|
-
execSync(
|
|
471
|
-
'npm install -g @anthropic-ai/claude-agent-sdk @anthropic-ai/claude-code',
|
|
472
|
-
{ stdio: 'inherit' },
|
|
473
|
-
);
|
|
474
|
-
logOk('Claude Code CLI installed');
|
|
475
|
-
} catch (err) {
|
|
476
|
-
logErr(`npm install failed: ${err.message}`);
|
|
477
|
-
logWarn('retry manually: npm install -g @anthropic-ai/claude-code');
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
// ─── File sync helpers ──────────────────────────────────────────────────────
|
|
482
|
-
|
|
483
|
-
/**
|
|
484
|
-
* Mirror a source directory into a destination directory, copying
|
|
485
|
-
* files and preserving subdirectory structure. Skips dotfiles by default.
|
|
486
|
-
*/
|
|
487
|
-
function syncDir(srcDir, destDir, opts = {}) {
|
|
488
|
-
if (!existsSync(srcDir)) return { copied: 0, skipped: 0 };
|
|
489
|
-
ensureDir(destDir);
|
|
490
|
-
let copied = 0;
|
|
491
|
-
let skipped = 0;
|
|
492
|
-
for (const name of readdirSync(srcDir)) {
|
|
493
|
-
if (name.startsWith('.')) continue;
|
|
494
|
-
const sp = join(srcDir, name);
|
|
495
|
-
const dp = join(destDir, name);
|
|
496
|
-
const st = statSync(sp);
|
|
497
|
-
if (opts.filter && !opts.filter(name, sp)) { skipped++; continue; }
|
|
498
|
-
if (st.isDirectory()) {
|
|
499
|
-
const r = syncDir(sp, dp, opts);
|
|
500
|
-
copied += r.copied;
|
|
501
|
-
skipped += r.skipped;
|
|
502
|
-
} else {
|
|
503
|
-
copyFileSync(sp, dp);
|
|
504
|
-
copied++;
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
return { copied, skipped };
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
/**
|
|
511
|
-
* Copy `config/agents/*.md` → `~/.claude/agents/*.md`.
|
|
512
|
-
*
|
|
513
|
-
* v6.3.0 — Agent files use Claude Code frontmatter (name / description
|
|
514
|
-
* / tools / model at the top). The source files in `config/agents/`
|
|
515
|
-
* have already been migrated by a parallel task; this helper is a
|
|
516
|
-
* straight copy with no YAML regeneration (Claude Code reads `.md`
|
|
517
|
-
* directly — no `.yaml` shadow needed).
|
|
518
|
-
*/
|
|
519
|
-
export async function syncAgentFiles({ dryRun = false, force = false } = {}) {
|
|
520
|
-
const src = join(REPO_ROOT, 'config', 'agents');
|
|
521
|
-
const dest = CLAUDE_AGENTS_DIR;
|
|
522
|
-
if (!existsSync(src)) {
|
|
523
|
-
return { ok: true, message: `no agents source at ${src}`, copied: 0, skipped: 0 };
|
|
524
|
-
}
|
|
525
|
-
if (dryRun) {
|
|
526
|
-
return { ok: true, message: `[dry-run] would sync ${src} → ${dest}` };
|
|
527
|
-
}
|
|
528
|
-
ensureDir(dest);
|
|
529
|
-
ensureDir(join(dest, '_shared'));
|
|
530
|
-
// Top-level .md files
|
|
531
|
-
let { copied, skipped } = syncDir(src, dest, { filter: (n) => n.endsWith('.md') });
|
|
532
|
-
// _shared/ — always overwrite (it's tiny + ships agent defaults).
|
|
533
|
-
const sharedSrc = join(src, '_shared');
|
|
534
|
-
if (existsSync(sharedSrc)) {
|
|
535
|
-
for (const entry of readdirSync(sharedSrc, { withFileTypes: true })) {
|
|
536
|
-
if (!entry.isFile()) continue;
|
|
537
|
-
copyFileSync(join(sharedSrc, entry.name), join(dest, '_shared', entry.name));
|
|
538
|
-
copied++;
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
return { ok: true, message: `${copied} agent(s) synced (${skipped} kept)`, copied, skipped };
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
/**
|
|
545
|
-
* Copy `config/skills/<name>/` → `~/.claude/skills/<name>/`.
|
|
546
|
-
*
|
|
547
|
-
* Each skill is mirrored as its own subdirectory under
|
|
548
|
-
* `~/.claude/skills/<name>/`. Honors `force` to overwrite.
|
|
549
|
-
*/
|
|
550
|
-
export async function syncSkillFiles({ dryRun = false, force = false } = {}) {
|
|
551
|
-
const src = join(REPO_ROOT, 'config', 'skills');
|
|
552
|
-
const dest = CLAUDE_SKILLS_DIR;
|
|
553
|
-
if (!existsSync(src)) {
|
|
554
|
-
return { ok: true, message: `no skills source at ${src}`, copied: 0, skipped: 0 };
|
|
555
|
-
}
|
|
556
|
-
if (dryRun) {
|
|
557
|
-
return { ok: true, message: `[dry-run] would sync ${src} → ${dest}` };
|
|
558
|
-
}
|
|
559
|
-
ensureDir(dest);
|
|
560
|
-
copyDirContents(src, dest);
|
|
561
|
-
|
|
562
|
-
// Mirror AGENT_BASELINE.md → ~/.claude/skills/agent-baseline/SKILL.md
|
|
563
|
-
// (the Claude Code skill loader resolves the `name: agent-baseline`
|
|
564
|
-
// reference via this path).
|
|
565
|
-
const sharedBaseline = join(REPO_ROOT, 'config', 'agents', '_shared', 'AGENT_BASELINE.md');
|
|
566
|
-
if (existsSync(sharedBaseline)) {
|
|
567
|
-
const baselineDir = join(dest, 'agent-baseline');
|
|
568
|
-
ensureDir(baselineDir);
|
|
569
|
-
copyFileSync(sharedBaseline, join(baselineDir, 'SKILL.md'));
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
const count = readdirSync(dest).filter((n) => {
|
|
573
|
-
try { return statSync(join(dest, n)).isDirectory(); } catch { return false; }
|
|
574
|
-
}).length;
|
|
575
|
-
return { ok: true, message: `${count} skill(s) synced`, copied: count, skipped: 0 };
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
/**
|
|
579
|
-
* Copy `.claude/commands/*.md` → `~/.claude/commands/*.md`.
|
|
580
|
-
*
|
|
581
|
-
* Slash commands live in the project's `.claude/commands/` directory
|
|
582
|
-
* (project-scoped). The global mirror in `~/.claude/commands/` makes
|
|
583
|
-
* them available across every Claude Code session for the user.
|
|
584
|
-
*/
|
|
585
|
-
export async function syncCommandFiles({ dryRun = false, force = false } = {}) {
|
|
586
|
-
// Project-scoped commands first, then legacy `config/commands/` as
|
|
587
|
-
// a fallback for older Bizar installs.
|
|
588
|
-
const candidates = [
|
|
589
|
-
join(REPO_ROOT, '.claude', 'commands'),
|
|
590
|
-
join(REPO_ROOT, 'config', 'commands'),
|
|
591
|
-
];
|
|
592
|
-
let src = null;
|
|
593
|
-
for (const c of candidates) if (existsSync(c)) { src = c; break; }
|
|
594
|
-
if (!src) {
|
|
595
|
-
return { ok: true, message: 'no commands source found', copied: 0, skipped: 0 };
|
|
596
|
-
}
|
|
597
|
-
const dest = CLAUDE_COMMANDS_DIR;
|
|
598
|
-
if (dryRun) {
|
|
599
|
-
return { ok: true, message: `[dry-run] would sync ${src} → ${dest}` };
|
|
600
|
-
}
|
|
601
|
-
ensureDir(dest);
|
|
602
|
-
const { copied, skipped } = syncDir(src, dest, { filter: (n) => n.endsWith('.md') });
|
|
603
|
-
return { ok: true, message: `${copied} command(s) synced (${skipped} kept)`, copied, skipped };
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
/**
|
|
607
|
-
* Copy `config/rules/*.md` → `~/.claude/rules/*.md`.
|
|
608
|
-
*
|
|
609
|
-
* Claude Code auto-loads every `.md` file under `~/.claude/rules/` as
|
|
610
|
-
* an always-on rule (https://code.claude.com/docs/en/rules).
|
|
611
|
-
*/
|
|
612
|
-
export async function syncRulesFiles({ dryRun = false, force = false } = {}) {
|
|
613
|
-
const src = join(REPO_ROOT, 'config', 'rules');
|
|
614
|
-
const dest = CLAUDE_RULES_DIR;
|
|
615
|
-
if (!existsSync(src)) {
|
|
616
|
-
return { ok: true, message: `no rules source at ${src}`, copied: 0, skipped: 0 };
|
|
617
|
-
}
|
|
618
|
-
if (dryRun) {
|
|
619
|
-
return { ok: true, message: `[dry-run] would sync ${src} → ${dest}` };
|
|
620
|
-
}
|
|
621
|
-
ensureDir(dest);
|
|
622
|
-
const { copied, skipped } = syncDir(src, dest, { filter: (n) => n.endsWith('.md') || n.endsWith('.txt') });
|
|
623
|
-
return { ok: true, message: `${copied} rule(s) synced (${skipped} kept)`, copied, skipped };
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
/**
|
|
627
|
-
* Copy `.claude/hooks/*.mjs` → `~/.claude/hooks/*.mjs`.
|
|
628
|
-
*
|
|
629
|
-
* Hook adapter scripts are real Node modules — Claude Code spawns them
|
|
630
|
-
* and pipes JSON on stdin / stdout. They must be `chmod +x` so Claude
|
|
631
|
-
* Code can `execve` them directly.
|
|
632
|
-
*/
|
|
633
|
-
export async function syncHookFiles({ dryRun = false, force = false } = {}) {
|
|
634
|
-
const src = join(REPO_ROOT, '.claude', 'hooks');
|
|
635
|
-
const dest = CLAUDE_HOOKS_DIR;
|
|
636
|
-
ensureDir(dest);
|
|
637
|
-
if (!existsSync(src)) {
|
|
638
|
-
return { ok: true, message: `no hooks source at ${src}`, copied: 0, skipped: 0 };
|
|
639
|
-
}
|
|
640
|
-
if (dryRun) {
|
|
641
|
-
return { ok: true, message: `[dry-run] would sync ${src} → ${dest}` };
|
|
642
|
-
}
|
|
643
|
-
copyDirContents(src, dest);
|
|
644
|
-
// Make all scripts executable.
|
|
645
|
-
for (const name of readdirSync(dest)) {
|
|
646
|
-
const fp = join(dest, name);
|
|
647
|
-
try {
|
|
648
|
-
const st = statSync(fp);
|
|
649
|
-
if (st.isFile() && (name.endsWith('.sh') || name.endsWith('.mjs'))) {
|
|
650
|
-
chmodSync(fp, 0o755);
|
|
651
|
-
}
|
|
652
|
-
} catch { /* ignore */ }
|
|
653
|
-
}
|
|
654
|
-
return { ok: true, message: 'hook scripts installed', copied: readdirSync(dest).length, skipped: 0 };
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
// ─── settings.json ───────────────────────────────────────────────────────────
|
|
658
|
-
|
|
659
|
-
/**
|
|
660
|
-
* Generate `~/.claude/settings.json` with the Bizar harness surface:
|
|
661
|
-
* - $schema pointing to https://json.schemastore.org/claude-code-settings.json
|
|
662
|
-
* - mcpServers block registering the `bizar` MCP server (stdio)
|
|
663
|
-
* - permissions.allow for mcp__bizar__*
|
|
664
|
-
* - permissions.deny for dangerous patterns
|
|
665
|
-
* - hooks.PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, SessionEnd
|
|
666
|
-
* - env block with BIZAR_HOME, BIZAR_MEMORY_VAULT
|
|
667
|
-
*
|
|
668
|
-
* Idempotent — we merge with any existing settings.json (preserve
|
|
669
|
-
* user-added keys, never overwrite).
|
|
670
|
-
*/
|
|
671
|
-
export function writeClaudeSettings({ dryRun = false, force = false } = {}) {
|
|
672
|
-
const fp = join(CLAUDE_DIR, 'settings.json');
|
|
673
|
-
const existing = readJsonSafe(fp, {}) || {};
|
|
674
|
-
|
|
675
|
-
const bizarSettings = {
|
|
676
|
-
$schema: 'https://json.schemastore.org/claude-code-settings.json',
|
|
677
|
-
mcpServers: {
|
|
678
|
-
bizar: {
|
|
679
|
-
type: 'stdio',
|
|
680
|
-
command: 'npx',
|
|
681
|
-
args: ['-y', '@polderlabs/bizar-sdk', 'mcp'],
|
|
682
|
-
env: {
|
|
683
|
-
BIZAR_HOME,
|
|
684
|
-
BIZAR_MEMORY_VAULT: process.env.BIZAR_MEMORY_VAULT || join(BIZAR_HOME, 'memory-vault'),
|
|
685
|
-
},
|
|
686
|
-
},
|
|
687
|
-
},
|
|
688
|
-
permissions: {
|
|
689
|
-
allow: ['mcp__bizar__*'],
|
|
690
|
-
deny: [
|
|
691
|
-
'Read(./.env)',
|
|
692
|
-
'Read(./.env.*)',
|
|
693
|
-
'Bash(rm -rf /)',
|
|
694
|
-
'Bash(sudo *)',
|
|
695
|
-
'Write(./node_modules/**)',
|
|
696
|
-
'Write(./package-lock.json)',
|
|
697
|
-
],
|
|
698
|
-
},
|
|
699
|
-
env: {
|
|
700
|
-
BIZAR_HOME,
|
|
701
|
-
BIZAR_MEMORY_VAULT: process.env.BIZAR_MEMORY_VAULT || join(BIZAR_HOME, 'memory-vault'),
|
|
702
|
-
},
|
|
703
|
-
hooks: {
|
|
704
|
-
PreToolUse: [
|
|
705
|
-
{
|
|
706
|
-
matcher: 'Write|Edit|MultiEdit|Bash',
|
|
707
|
-
hooks: [
|
|
708
|
-
{
|
|
709
|
-
type: 'command',
|
|
710
|
-
command: join(CLAUDE_HOOKS_DIR, 'pretooluse-editwrite.mjs'),
|
|
711
|
-
timeout: 10,
|
|
712
|
-
},
|
|
713
|
-
],
|
|
714
|
-
},
|
|
715
|
-
],
|
|
716
|
-
PostToolUse: [
|
|
717
|
-
{
|
|
718
|
-
matcher: 'Edit|Write|MultiEdit',
|
|
719
|
-
hooks: [
|
|
720
|
-
{
|
|
721
|
-
type: 'command',
|
|
722
|
-
command: join(CLAUDE_HOOKS_DIR, 'posttooluse-editwrite.mjs'),
|
|
723
|
-
timeout: 10,
|
|
724
|
-
},
|
|
725
|
-
],
|
|
726
|
-
},
|
|
727
|
-
],
|
|
728
|
-
UserPromptSubmit: [
|
|
729
|
-
{
|
|
730
|
-
hooks: [
|
|
731
|
-
{
|
|
732
|
-
type: 'command',
|
|
733
|
-
command: join(CLAUDE_HOOKS_DIR, 'userpromptsubmit-tag.mjs'),
|
|
734
|
-
timeout: 10,
|
|
735
|
-
},
|
|
736
|
-
],
|
|
737
|
-
},
|
|
738
|
-
],
|
|
739
|
-
SessionStart: [
|
|
740
|
-
{
|
|
741
|
-
hooks: [
|
|
742
|
-
{
|
|
743
|
-
type: 'command',
|
|
744
|
-
command: join(CLAUDE_HOOKS_DIR, 'sessionstart-prime.mjs'),
|
|
745
|
-
timeout: 10,
|
|
746
|
-
},
|
|
747
|
-
],
|
|
748
|
-
},
|
|
749
|
-
],
|
|
750
|
-
SessionEnd: [
|
|
751
|
-
{
|
|
752
|
-
hooks: [
|
|
753
|
-
{
|
|
754
|
-
type: 'command',
|
|
755
|
-
command: join(CLAUDE_HOOKS_DIR, 'sessionend-record.mjs'),
|
|
756
|
-
timeout: 10,
|
|
757
|
-
},
|
|
758
|
-
],
|
|
759
|
-
},
|
|
760
|
-
],
|
|
761
|
-
},
|
|
762
|
-
};
|
|
763
|
-
|
|
764
|
-
// Merge bizarre-specific keys into existing settings, preserve user's
|
|
765
|
-
// unrelated keys (model, attribution, theme, etc.).
|
|
766
|
-
const merged = { ...existing };
|
|
767
|
-
if (force) {
|
|
768
|
-
Object.assign(merged, bizarSettings);
|
|
769
|
-
} else {
|
|
770
|
-
merged.$schema = merged.$schema || bizarSettings.$schema;
|
|
771
|
-
merged.mcpServers = { ...(existing.mcpServers || {}), ...bizarSettings.mcpServers };
|
|
772
|
-
merged.permissions = {
|
|
773
|
-
allow: [...new Set([...(existing.permissions?.allow || []), ...bizarSettings.permissions.allow])],
|
|
774
|
-
deny: [...new Set([...(existing.permissions?.deny || []), ...bizarSettings.permissions.deny])],
|
|
775
|
-
ask: [...new Set([...(existing.permissions?.ask || [])])],
|
|
776
|
-
};
|
|
777
|
-
merged.env = { ...(existing.env || {}), ...bizarSettings.env };
|
|
778
|
-
merged.hooks = { ...(existing.hooks || {}), ...bizarSettings.hooks };
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
if (dryRun) {
|
|
782
|
-
return { ok: true, message: `[dry-run] would write ${fp}` };
|
|
783
|
-
}
|
|
784
|
-
ensureDir(CLAUDE_DIR);
|
|
785
|
-
writeFileSync(fp, JSON.stringify(merged, null, 2) + '\n');
|
|
786
|
-
return { ok: true, message: `wrote ${fp}`, path: fp };
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
// ─── CLAUDE.md mirror ──────────────────────────────────────────────────────
|
|
790
|
-
|
|
791
|
-
/**
|
|
792
|
-
* Mirror the repo's `AGENTS.md` into `~/.claude/CLAUDE.md`.
|
|
793
|
-
*
|
|
794
|
-
* Claude Code reads `~/.claude/CLAUDE.md` at session start as a global
|
|
795
|
-
* memory file. Mirroring `AGENTS.md` (the canonical harness entry
|
|
796
|
-
* point) makes the project's always-on rules available to every
|
|
797
|
-
* Claude Code session for the user, not just project-scoped sessions.
|
|
798
|
-
*/
|
|
799
|
-
export function writeClaudeMdMirror({ dryRun = false, force = false } = {}) {
|
|
800
|
-
const srcCandidates = [
|
|
801
|
-
join(REPO_ROOT, 'AGENTS.md'),
|
|
802
|
-
join(REPO_ROOT, 'CLAUDE.md'),
|
|
803
|
-
];
|
|
804
|
-
const src = srcCandidates.find((p) => existsSync(p));
|
|
805
|
-
const dest = join(CLAUDE_DIR, 'CLAUDE.md');
|
|
806
|
-
if (!src) {
|
|
807
|
-
return { ok: false, message: 'no CLAUDE.md / AGENTS.md found in repo root' };
|
|
808
|
-
}
|
|
809
|
-
if (existsSync(dest) && !force) {
|
|
810
|
-
return { ok: true, message: `${dest} already exists — pass --force to overwrite` };
|
|
811
|
-
}
|
|
812
|
-
if (dryRun) {
|
|
813
|
-
return { ok: true, message: `[dry-run] would mirror ${src} → ${dest}` };
|
|
814
|
-
}
|
|
815
|
-
ensureDir(CLAUDE_DIR);
|
|
816
|
-
// Add a banner so users know this is auto-generated.
|
|
817
|
-
const body = readFileSync(src, 'utf8');
|
|
818
|
-
const banner =
|
|
819
|
-
`# CLAUDE.md — Mirror of AGENTS.md for Claude Code compatibility\n\n` +
|
|
820
|
-
`> Auto-generated by \`cli/provision-claude.mjs:writeClaudeMdMirror\`.\n` +
|
|
821
|
-
`> DO NOT EDIT THIS FILE DIRECTLY — edit the project root's \`AGENTS.md\`\n` +
|
|
822
|
-
`> and re-run \`bizar update\` (or \`make mirror-claude-md\`) to refresh.\n\n` +
|
|
823
|
-
`---\n\n`;
|
|
824
|
-
writeFileSync(dest, banner + body);
|
|
825
|
-
return { ok: true, message: `mirrored ${src} → ${dest}`, path: dest };
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
// ─── MCP server registration ─────────────────────────────────────────────────
|
|
829
|
-
|
|
830
|
-
/**
|
|
831
|
-
* Register the `bizar` MCP server via `claude mcp add`. Idempotent —
|
|
832
|
-
* if the server is already registered in settings.json, we skip the
|
|
833
|
-
* `claude mcp add` call.
|
|
834
|
-
*
|
|
835
|
-
* Per https://code.claude.com/docs/en/mcp, the canonical way to add a
|
|
836
|
-
* stdio MCP server is:
|
|
837
|
-
*
|
|
838
|
-
* claude mcp add <name> -- <command> [args...]
|
|
839
|
-
*/
|
|
840
|
-
export function setupMcpServer({ dryRun = false } = {}) {
|
|
841
|
-
// Idempotency probe: read existing settings.json and look for a
|
|
842
|
-
// bizar MCP server entry. If present, skip.
|
|
843
|
-
const settingsPath = join(CLAUDE_DIR, 'settings.json');
|
|
844
|
-
const existing = readJsonSafe(settingsPath, null);
|
|
845
|
-
if (existing && existing.mcpServers && existing.mcpServers.bizar) {
|
|
846
|
-
return { ok: true, message: `bizar MCP server already registered in ${settingsPath}` };
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
if (!haveCmd('claude')) {
|
|
850
|
-
return { ok: false, message: 'claude CLI not on PATH — install @anthropic-ai/claude-code first' };
|
|
851
|
-
}
|
|
852
|
-
if (dryRun) {
|
|
853
|
-
return { ok: true, message: '[dry-run] would run: claude mcp add bizar -- npx -y @polderlabs/bizar-sdk mcp' };
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
const r = spawnSync('claude', [
|
|
857
|
-
'mcp', 'add', '-f', '-s', 'user', 'bizar', '--',
|
|
858
|
-
'npx', '-y', '@polderlabs/bizar-sdk', 'mcp',
|
|
859
|
-
], { stdio: 'inherit', timeout: 60_000 });
|
|
860
|
-
|
|
861
|
-
if (r.status !== 0) {
|
|
862
|
-
return { ok: false, message: `claude mcp add exited with code ${r.status}` };
|
|
863
|
-
}
|
|
864
|
-
return { ok: true, message: 'bizar MCP server registered via `claude mcp add`' };
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
/**
|
|
868
|
-
* If `./.claude-plugin/plugin.json` exists, install the Bizar plugin
|
|
869
|
-
* via `claude plugin install .`. Per
|
|
870
|
-
* https://code.claude.com/docs/en/plugins, a Claude Code plugin is a
|
|
871
|
-
* directory containing a `.claude-plugin/plugin.json` manifest.
|
|
872
|
-
*/
|
|
873
|
-
export function setupClaudePlugin({ dryRun = false } = {}) {
|
|
874
|
-
const manifestPath = join(REPO_ROOT, '.claude-plugin', 'plugin.json');
|
|
875
|
-
if (!existsSync(manifestPath)) {
|
|
876
|
-
return { ok: true, message: `no plugin manifest at ${manifestPath} — skipping plugin install` };
|
|
877
|
-
}
|
|
878
|
-
if (!haveCmd('claude')) {
|
|
879
|
-
return { ok: false, message: 'claude CLI not on PATH — install @anthropic-ai/claude-code first' };
|
|
880
|
-
}
|
|
881
|
-
if (dryRun) {
|
|
882
|
-
return { ok: true, message: `[dry-run] would run: claude plugin install ${REPO_ROOT}` };
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
const r = spawnSync('claude', ['plugin', 'install', '.'], {
|
|
886
|
-
stdio: 'inherit',
|
|
887
|
-
timeout: 120_000,
|
|
888
|
-
cwd: REPO_ROOT,
|
|
889
|
-
});
|
|
890
|
-
if (r.status !== 0) {
|
|
891
|
-
return { ok: false, message: `claude plugin install exited with code ${r.status}` };
|
|
892
|
-
}
|
|
893
|
-
return { ok: true, message: `claude plugin installed from ${REPO_ROOT}` };
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
// ─── Dashboard service ───────────────────────────────────────────────────────
|
|
897
|
-
|
|
898
|
-
function installDashboardService({ dryRun = false, start = true } = {}) {
|
|
899
|
-
const bin = join(REPO_ROOT, 'cli', 'bin.mjs');
|
|
900
|
-
if (!existsSync(bin)) {
|
|
901
|
-
return { ok: false, message: `cli/bin.mjs not found at ${bin} — service registration deferred` };
|
|
902
|
-
}
|
|
903
|
-
if (dryRun) {
|
|
904
|
-
return { ok: true, message: `[dry-run] would run: node ${bin} service install` };
|
|
905
|
-
}
|
|
906
|
-
const r = spawnSync('node', [bin, 'service', 'install'], { stdio: 'inherit' });
|
|
907
|
-
if (r.status !== 0) {
|
|
908
|
-
return { ok: false, message: `service install returned ${r.status}` };
|
|
909
|
-
}
|
|
910
|
-
return { ok: true, message: 'dashboard service registered' };
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
// ─── Detection ───────────────────────────────────────────────────────────────
|
|
914
|
-
|
|
915
|
-
/**
|
|
916
|
-
* Probe the current state without modifying anything. Both install and
|
|
917
|
-
* update flows start here so they can decide what to skip.
|
|
918
|
-
*
|
|
919
|
-
* Returned shape:
|
|
920
|
-
* {
|
|
921
|
-
* claudeDir: string,
|
|
922
|
-
* claudeCli: { available, version },
|
|
923
|
-
* agentsDir: { exists, count },
|
|
924
|
-
* skillsDir: { exists, count },
|
|
925
|
-
* commandsDir: { exists, count },
|
|
926
|
-
* hooksDir: { exists, count },
|
|
927
|
-
* rulesDir: { exists, count },
|
|
928
|
-
* settingsFile: { exists, parses },
|
|
929
|
-
* mcpServer: { registered, type, command },
|
|
930
|
-
* pluginManifest: { exists },
|
|
931
|
-
* bizarHome: { exists },
|
|
932
|
-
* gitRepo: boolean,
|
|
933
|
-
* }
|
|
934
|
-
*/
|
|
935
|
-
export function detectState() {
|
|
936
|
-
const settingsPath = join(CLAUDE_DIR, 'settings.json');
|
|
937
|
-
const settings = readJsonSafe(settingsPath, null);
|
|
938
|
-
|
|
939
|
-
let claudeVersion = null;
|
|
940
|
-
try {
|
|
941
|
-
claudeVersion = execFileSync('claude', ['--version'], { encoding: 'utf8', timeout: 5000 })
|
|
942
|
-
.toString().trim().split('\n')[0] || null;
|
|
943
|
-
} catch { /* not installed */ }
|
|
944
|
-
|
|
945
|
-
const countMd = (dir) => {
|
|
946
|
-
if (!existsSync(dir)) return 0;
|
|
947
|
-
try {
|
|
948
|
-
return readdirSync(dir, { withFileTypes: true })
|
|
949
|
-
.filter((e) => e.isFile() && (e.name.endsWith('.md') || e.name.endsWith('.mjs'))).length;
|
|
950
|
-
} catch { return 0; }
|
|
951
|
-
};
|
|
952
|
-
const countSkillDirs = (dir) => {
|
|
953
|
-
if (!existsSync(dir)) return 0;
|
|
954
|
-
try {
|
|
955
|
-
return readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).length;
|
|
956
|
-
} catch { return 0; }
|
|
957
|
-
};
|
|
958
|
-
|
|
959
|
-
const mcpServer = settings && settings.mcpServers && settings.mcpServers.bizar
|
|
960
|
-
? {
|
|
961
|
-
registered: true,
|
|
962
|
-
type: settings.mcpServers.bizar.type || 'stdio',
|
|
963
|
-
command: settings.mcpServers.bizar.command || 'npx',
|
|
964
|
-
}
|
|
965
|
-
: { registered: false };
|
|
966
|
-
|
|
967
|
-
return {
|
|
968
|
-
claudeDir: CLAUDE_DIR,
|
|
969
|
-
claudeCli: { available: !!claudeVersion, version: claudeVersion },
|
|
970
|
-
agentsDir: { exists: existsSync(CLAUDE_AGENTS_DIR), count: countMd(CLAUDE_AGENTS_DIR) },
|
|
971
|
-
skillsDir: { exists: existsSync(CLAUDE_SKILLS_DIR), count: countSkillDirs(CLAUDE_SKILLS_DIR) },
|
|
972
|
-
commandsDir: { exists: existsSync(CLAUDE_COMMANDS_DIR), count: countMd(CLAUDE_COMMANDS_DIR) },
|
|
973
|
-
hooksDir: { exists: existsSync(CLAUDE_HOOKS_DIR), count: countMd(CLAUDE_HOOKS_DIR) },
|
|
974
|
-
rulesDir: { exists: existsSync(CLAUDE_RULES_DIR), count: countMd(CLAUDE_RULES_DIR) },
|
|
975
|
-
settingsFile: {
|
|
976
|
-
exists: existsSync(settingsPath),
|
|
977
|
-
parses: settings !== null && typeof settings === 'object',
|
|
978
|
-
path: settingsPath,
|
|
979
|
-
},
|
|
980
|
-
mcpServer,
|
|
981
|
-
pluginManifest: { exists: existsSync(join(REPO_ROOT, '.claude-plugin', 'plugin.json')) },
|
|
982
|
-
bizarHome: { exists: existsSync(BIZAR_HOME), path: BIZAR_HOME },
|
|
983
|
-
gitRepo: existsSync(join(REPO_ROOT, '.git')),
|
|
984
|
-
};
|
|
985
|
-
}
|
|
986
|
-
|
|
987
|
-
/** Read-only JSON snapshot of detectState() — for diagnostics + tests. */
|
|
988
|
-
export function detectStateJson() {
|
|
989
|
-
return JSON.stringify(detectState(), null, 2);
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
// ─── Top-level orchestration ─────────────────────────────────────────────────
|
|
993
|
-
|
|
994
|
-
/**
|
|
995
|
-
* The unified provision flow. `mode` is 'install' or 'update'.
|
|
996
|
-
*
|
|
997
|
-
* Steps performed:
|
|
998
|
-
* 1. Detect state (no side effects).
|
|
999
|
-
* 2. Ensure ~/.bizar_home/ exists (memory-vault, loops, port file).
|
|
1000
|
-
* 3. Check toolchain + install Claude Code CLI if missing.
|
|
1001
|
-
* 4. Sync agent files to ~/.claude/agents/.
|
|
1002
|
-
* 5. Sync skills to ~/.claude/skills/.
|
|
1003
|
-
* 6. Sync commands to ~/.claude/commands/.
|
|
1004
|
-
* 7. Sync rules to ~/.claude/rules/.
|
|
1005
|
-
* 8. Sync hook adapter scripts to ~/.claude/hooks/.
|
|
1006
|
-
* 9. Write ~/.claude/settings.json with MCP + hooks + permissions + env.
|
|
1007
|
-
* 10. Register the Bizar MCP server via `claude mcp add`.
|
|
1008
|
-
* 11. Install the Bizar plugin via `claude plugin install .` (optional).
|
|
1009
|
-
* 12. Mirror AGENTS.md → ~/.claude/CLAUDE.md.
|
|
1010
|
-
* 13. (install) Register dashboard service.
|
|
1011
|
-
* 14. Print summary.
|
|
1012
|
-
*
|
|
1013
|
-
* Every step is idempotent — running this twice is safe.
|
|
1014
|
-
*/
|
|
1015
|
-
export async function runProvision(opts = {}) {
|
|
1016
|
-
const {
|
|
1017
|
-
mode = 'install',
|
|
1018
|
-
dryRun = false,
|
|
1019
|
-
force = false,
|
|
1020
|
-
yes = false,
|
|
1021
|
-
start = true,
|
|
1022
|
-
} = opts;
|
|
1023
|
-
|
|
1024
|
-
const effectiveMode = mode === 'update' ? 'update' : 'install';
|
|
1025
|
-
|
|
1026
|
-
console.log('');
|
|
1027
|
-
console.log(chalk.bold.cyan(' ⚡ BizarHarness Provisioner v6.3.0 (Claude Code)'));
|
|
1028
|
-
console.log(chalk.dim(` Mode: ${effectiveMode}${force ? ' (force)' : ''}${dryRun ? ' (dry-run)' : ''}`));
|
|
1029
|
-
console.log('');
|
|
1030
|
-
|
|
1031
|
-
// ── 1. Detect state ──────────────────────────────────────────────────
|
|
1032
|
-
const state = detectState();
|
|
1033
|
-
|
|
1034
|
-
// ── 2. Bizar HOME bootstrap ──────────────────────────────────────────
|
|
1035
|
-
section('Bizar HOME');
|
|
1036
|
-
const bizarHomeStep = ensureBizarHome({ dryRun });
|
|
1037
|
-
if (bizarHomeStep.ok) logOk(bizarHomeStep.message);
|
|
1038
|
-
else logErr(bizarHomeStep.message);
|
|
1039
|
-
|
|
1040
|
-
// ── 3. Toolchain ────────────────────────────────────────────────────
|
|
1041
|
-
checkToolchain();
|
|
1042
|
-
installClaudeCli({ force, dryRun });
|
|
1043
|
-
|
|
1044
|
-
// ── 4-8. File sync ──────────────────────────────────────────────────
|
|
1045
|
-
const stepResults = [];
|
|
1046
|
-
const runStep = async (label, fn) => {
|
|
1047
|
-
section(label);
|
|
1048
|
-
const r = await fn();
|
|
1049
|
-
const marker = r.ok ? '✓' : '✗';
|
|
1050
|
-
console.log(` ${marker} ${r.message}`);
|
|
1051
|
-
stepResults.push({ label, ...r });
|
|
1052
|
-
return r;
|
|
1053
|
-
};
|
|
1054
|
-
|
|
1055
|
-
await runStep('Syncing agents', () => syncAgentFiles({ dryRun, force }));
|
|
1056
|
-
await runStep('Syncing skills', () => syncSkillFiles({ dryRun, force }));
|
|
1057
|
-
await runStep('Syncing commands', () => syncCommandFiles({ dryRun, force }));
|
|
1058
|
-
await runStep('Syncing rules', () => syncRulesFiles({ dryRun, force }));
|
|
1059
|
-
await runStep('Syncing hooks', () => syncHookFiles({ dryRun, force }));
|
|
1060
|
-
|
|
1061
|
-
// ── 4b. Build the SDK workspace so dist/ exists. ────────────────────
|
|
1062
|
-
// The dashboard imports from packages/sdk/dist/* at runtime; without
|
|
1063
|
-
// this step v7.0.0+ npm installs crash on dashboard startup because
|
|
1064
|
-
// the SDK ships as TS source only. See `buildSdk()` JSDoc for the
|
|
1065
|
-
// full story.
|
|
1066
|
-
await runStep('Building SDK', () => buildSdk({ dryRun }));
|
|
1067
|
-
|
|
1068
|
-
// ── 4b.2. Build the Bizar plugin shim so dist/index.js exists. ────
|
|
1069
|
-
// The loader expects `plugins/bizar/dist/index.js`. Without this
|
|
1070
|
-
// step v8.0.x npm installs crash on `bizar dash start` with
|
|
1071
|
-
// `Plugin source not found at .../plugins/bizar` from
|
|
1072
|
-
// `cli/install.mjs:installPluginBizar` (the dir exists, but the
|
|
1073
|
-
// compiled entry does not). See `buildPlugin()` JSDoc.
|
|
1074
|
-
await runStep('Building plugin', () => buildPlugin({ dryRun }));
|
|
1075
|
-
|
|
1076
|
-
// ── 4c. Build the dashboard SPA so dist/index.html exists. ───────────
|
|
1077
|
-
// The Node server in `bizar-dash/src/server/server.mjs` serves the
|
|
1078
|
-
// React UI from `bizar-dash/dist/`. The published npm tarball excludes
|
|
1079
|
-
// `dist/` because it is `.gitignore`'d, so a fresh install must
|
|
1080
|
-
// rebuild it. Skips when already present (idempotent). See
|
|
1081
|
-
// `buildDash()` JSDoc for the v7.0.3 reason this step exists.
|
|
1082
|
-
await runStep('Building dashboard', () => buildDash({ dryRun }));
|
|
1083
|
-
|
|
1084
|
-
// ── 9. settings.json (MCP + hooks + permissions + env) ──────────────
|
|
1085
|
-
section('Writing settings.json');
|
|
1086
|
-
const settingsStep = writeClaudeSettings({ dryRun, force });
|
|
1087
|
-
if (settingsStep.ok) logOk(settingsStep.message); else logErr(settingsStep.message);
|
|
1088
|
-
stepResults.push({ label: 'settings.json', ...settingsStep });
|
|
1089
|
-
|
|
1090
|
-
// ── 10. MCP server registration ──────────────────────────────────────
|
|
1091
|
-
section('MCP server (bizar)');
|
|
1092
|
-
const mcpStep = setupMcpServer({ dryRun });
|
|
1093
|
-
if (mcpStep.ok) logOk(mcpStep.message); else logWarn(mcpStep.message);
|
|
1094
|
-
stepResults.push({ label: 'mcp-server', ...mcpStep });
|
|
1095
|
-
|
|
1096
|
-
// ── 11. Plugin install (optional) ───────────────────────────────────
|
|
1097
|
-
section('Claude plugin');
|
|
1098
|
-
const pluginStep = setupClaudePlugin({ dryRun });
|
|
1099
|
-
if (pluginStep.ok) logOk(pluginStep.message); else logWarn(pluginStep.message);
|
|
1100
|
-
stepResults.push({ label: 'claude-plugin', ...pluginStep });
|
|
1101
|
-
|
|
1102
|
-
// ── 12. CLAUDE.md mirror ────────────────────────────────────────────
|
|
1103
|
-
section('CLAUDE.md mirror');
|
|
1104
|
-
const claudeMdStep = writeClaudeMdMirror({ dryRun, force });
|
|
1105
|
-
if (claudeMdStep.ok) logOk(claudeMdStep.message); else logWarn(claudeMdStep.message);
|
|
1106
|
-
stepResults.push({ label: 'claude-md', ...claudeMdStep });
|
|
1107
|
-
|
|
1108
|
-
// ── 13. Dashboard service ───────────────────────────────────────────
|
|
1109
|
-
if (start && effectiveMode === 'install') {
|
|
1110
|
-
section('Dashboard service');
|
|
1111
|
-
const svcStep = installDashboardService({ dryRun });
|
|
1112
|
-
if (svcStep.ok) logOk(svcStep.message); else logWarn(svcStep.message);
|
|
1113
|
-
stepResults.push({ label: 'service', ...svcStep });
|
|
1114
|
-
}
|
|
1115
|
-
|
|
1116
|
-
// ── 14. Summary ─────────────────────────────────────────────────────
|
|
1117
|
-
section('Provision complete');
|
|
1118
|
-
const finalState = detectState();
|
|
1119
|
-
console.log(chalk.dim(JSON.stringify(finalState, null, 2)));
|
|
1120
|
-
console.log('');
|
|
1121
|
-
const anyFail = stepResults.some((r) => !r.ok);
|
|
1122
|
-
if (anyFail) {
|
|
1123
|
-
console.log(chalk.yellow(' ⚠ Some steps had issues. See messages above.'));
|
|
1124
|
-
} else {
|
|
1125
|
-
console.log(chalk.bold.green(' ✓ Bizar is ready.'));
|
|
1126
|
-
console.log(chalk.dim(' Next: restart your Claude Code session to pick up the new config.'));
|
|
1127
|
-
}
|
|
1128
|
-
console.log('');
|
|
1129
|
-
return { ok: !anyFail, mode: effectiveMode, state: finalState, stepResults };
|
|
1130
|
-
}
|
|
1131
|
-
|
|
1132
|
-
// ─── CLI entry ───────────────────────────────────────────────────────────────
|
|
1133
|
-
|
|
1134
|
-
function parseFlags(argv) {
|
|
1135
|
-
const opts = {
|
|
1136
|
-
mode: 'install',
|
|
1137
|
-
dryRun: false,
|
|
1138
|
-
force: false,
|
|
1139
|
-
yes: false,
|
|
1140
|
-
start: true,
|
|
1141
|
-
update: false,
|
|
1142
|
-
};
|
|
1143
|
-
for (let i = 0; i < argv.length; i++) {
|
|
1144
|
-
const a = argv[i];
|
|
1145
|
-
if (a === '--mode=install') opts.mode = 'install';
|
|
1146
|
-
else if (a === '--mode=update') opts.mode = 'update';
|
|
1147
|
-
else if (a === '--mode=install-only-system') opts.mode = 'install-only-system';
|
|
1148
|
-
else if (a === '--mode') {
|
|
1149
|
-
const v = argv[++i];
|
|
1150
|
-
if (v === 'install' || v === 'update' || v === 'install-only-system') opts.mode = v;
|
|
1151
|
-
}
|
|
1152
|
-
else if (a === '--dry-run') opts.dryRun = true;
|
|
1153
|
-
else if (a === '--force') opts.force = true;
|
|
1154
|
-
else if (a === '--yes' || a === '-y') opts.yes = true;
|
|
1155
|
-
else if (a === '--non-interactive') opts.yes = true;
|
|
1156
|
-
else if (a === '--no-service') opts.start = false;
|
|
1157
|
-
else if (a === '--update') opts.mode = 'update';
|
|
1158
|
-
else if (a === '--help' || a === '-h') {
|
|
1159
|
-
console.log(
|
|
1160
|
-
'Usage: node cli/provision-claude.mjs [--mode=install|update|install-only-system]\n' +
|
|
1161
|
-
' [--dry-run] [--force] [--yes] [--non-interactive] [--no-service]',
|
|
1162
|
-
);
|
|
1163
|
-
process.exit(0);
|
|
1164
|
-
}
|
|
1165
|
-
}
|
|
1166
|
-
if (opts.mode === 'update') opts.update = true;
|
|
1167
|
-
return opts;
|
|
1168
|
-
}
|
|
1169
|
-
|
|
1170
|
-
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
1171
|
-
const opts = parseFlags(process.argv.slice(2));
|
|
1172
|
-
runProvision(opts).catch((err) => {
|
|
1173
|
-
logErr(`provision failed: ${err?.message || err}`);
|
|
1174
|
-
process.exit(1);
|
|
1175
|
-
});
|
|
1176
|
-
}
|