agents-inc 0.152.1 → 0.153.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/CHANGELOG.md +33 -0
- package/README.md +31 -35
- package/dist/{chunk-LDP5ASDM.js → chunk-2HC2SNRP.js} +2 -15
- package/dist/chunk-2HC2SNRP.js.map +1 -0
- package/dist/{chunk-WKTHWQKM.js → chunk-35VOK7XE.js} +1338 -2573
- package/dist/chunk-35VOK7XE.js.map +1 -0
- package/dist/{chunk-SX5H6TSC.js → chunk-4VYALFQZ.js} +11 -8
- package/dist/chunk-4VYALFQZ.js.map +1 -0
- package/dist/{chunk-KXYVJGJP.js → chunk-6CNRMCFM.js} +3 -2
- package/dist/{chunk-KXYVJGJP.js.map → chunk-6CNRMCFM.js.map} +1 -1
- package/dist/{chunk-LZ52XEPR.js → chunk-ACKC577F.js} +7 -6
- package/dist/chunk-ACKC577F.js.map +1 -0
- package/dist/{chunk-XHUODHZ4.js → chunk-BMGBA6HJ.js} +22 -49
- package/dist/chunk-BMGBA6HJ.js.map +1 -0
- package/dist/{chunk-FVDKAVOL.js → chunk-EFU6P3B7.js} +18 -51
- package/dist/chunk-EFU6P3B7.js.map +1 -0
- package/dist/{chunk-STMHDOZD.js → chunk-F7Z5I6GU.js} +12 -9
- package/dist/chunk-F7Z5I6GU.js.map +1 -0
- package/dist/{chunk-E5X6RJ4E.js → chunk-FIHTAFST.js} +29 -26
- package/dist/chunk-FIHTAFST.js.map +1 -0
- package/dist/{chunk-QZZJFYUQ.js → chunk-FRWKCE6R.js} +3 -3
- package/dist/chunk-FRWKCE6R.js.map +1 -0
- package/dist/{chunk-5UWKOOIS.js → chunk-HEBPKK5L.js} +129 -195
- package/dist/chunk-HEBPKK5L.js.map +1 -0
- package/dist/{chunk-MJ2KHG24.js → chunk-IO4VEW5U.js} +6 -6
- package/dist/{chunk-MJ2KHG24.js.map → chunk-IO4VEW5U.js.map} +1 -1
- package/dist/{chunk-TUPYW2IQ.js → chunk-JC7DBPRD.js} +9 -12
- package/dist/chunk-JC7DBPRD.js.map +1 -0
- package/dist/{chunk-5GUOIJSG.js → chunk-JFAUQDZM.js} +3 -15
- package/dist/chunk-JFAUQDZM.js.map +1 -0
- package/dist/{chunk-ZL7PFNHN.js → chunk-K3O3YQYG.js} +2 -2
- package/dist/{chunk-SKQWRUPX.js → chunk-K7PJDWER.js} +59 -33
- package/dist/chunk-K7PJDWER.js.map +1 -0
- package/dist/{chunk-MVR3SYMN.js → chunk-KET3EVGX.js} +4 -3
- package/dist/{chunk-MVR3SYMN.js.map → chunk-KET3EVGX.js.map} +1 -1
- package/dist/{chunk-MWG4VU64.js → chunk-M5HSTNYU.js} +50 -56
- package/dist/chunk-M5HSTNYU.js.map +1 -0
- package/dist/{chunk-TJJPWUMA.js → chunk-NSJ2S7R5.js} +3 -2
- package/dist/{chunk-TJJPWUMA.js.map → chunk-NSJ2S7R5.js.map} +1 -1
- package/dist/{chunk-IHUY4M22.js → chunk-NXGMM5YN.js} +4 -4
- package/dist/chunk-NXQAY5U7.js +18441 -0
- package/dist/chunk-NXQAY5U7.js.map +1 -0
- package/dist/{chunk-Q2KG7UV3.js → chunk-OJJLYHM3.js} +2 -1
- package/dist/{chunk-Q2KG7UV3.js.map → chunk-OJJLYHM3.js.map} +1 -1
- package/dist/{chunk-PZIZNMHK.js → chunk-ORJ5Y222.js} +159 -138
- package/dist/chunk-ORJ5Y222.js.map +1 -0
- package/dist/{chunk-NR3OZGL4.js → chunk-OZXAJNLA.js} +2 -1
- package/dist/{chunk-NR3OZGL4.js.map → chunk-OZXAJNLA.js.map} +1 -1
- package/dist/{chunk-MM7CTTAV.js → chunk-Q5FLW4JG.js} +2 -2
- package/dist/{chunk-MM7CTTAV.js.map → chunk-Q5FLW4JG.js.map} +1 -1
- package/dist/{chunk-GRVKW3CC.js → chunk-R4SEU3DF.js} +3 -2
- package/dist/{chunk-GRVKW3CC.js.map → chunk-R4SEU3DF.js.map} +1 -1
- package/dist/{chunk-76ADG7LQ.js → chunk-R54KIGN2.js} +8 -22
- package/dist/chunk-R54KIGN2.js.map +1 -0
- package/dist/{chunk-SBHBBZKF.js → chunk-REKOWTIB.js} +25 -120
- package/dist/chunk-REKOWTIB.js.map +1 -0
- package/dist/chunk-TY6CAOL7.js +40 -0
- package/dist/chunk-TY6CAOL7.js.map +1 -0
- package/dist/{chunk-BNSRA6HL.js → chunk-U2DNAHFE.js} +39 -176
- package/dist/chunk-U2DNAHFE.js.map +1 -0
- package/dist/{chunk-PMM2MYMA.js → chunk-U2YSGBHA.js} +624 -349
- package/dist/chunk-U2YSGBHA.js.map +1 -0
- package/dist/{chunk-DTSG5XZB.js → chunk-V3ENMW7G.js} +3 -3
- package/dist/{chunk-L7SZXUBJ.js → chunk-VECFAKM5.js} +7 -6
- package/dist/{chunk-L7SZXUBJ.js.map → chunk-VECFAKM5.js.map} +1 -1
- package/dist/chunk-VGEBYCQZ.js +59 -0
- package/dist/chunk-VGEBYCQZ.js.map +1 -0
- package/dist/chunk-VZRO7RYT.js +3694 -0
- package/dist/chunk-VZRO7RYT.js.map +1 -0
- package/dist/{chunk-LASI35KT.js → chunk-WDAO3RUY.js} +14 -28
- package/dist/chunk-WDAO3RUY.js.map +1 -0
- package/dist/{chunk-MQ7FLIYS.js → chunk-WG6V6VJV.js} +3 -2
- package/dist/{chunk-MQ7FLIYS.js.map → chunk-WG6V6VJV.js.map} +1 -1
- package/dist/chunk-X5XJ6WKO.js +284 -0
- package/dist/chunk-X5XJ6WKO.js.map +1 -0
- package/dist/{chunk-RNJWAKMO.js → chunk-XGMOXMS4.js} +9 -8
- package/dist/chunk-XGMOXMS4.js.map +1 -0
- package/dist/{chunk-IUQBOCSU.js → chunk-ZBPEFQ4G.js} +2 -1
- package/dist/{chunk-IUQBOCSU.js.map → chunk-ZBPEFQ4G.js.map} +1 -1
- package/dist/{chunk-QRDTLVXG.js → chunk-ZNDLUWWD.js} +9 -22
- package/dist/chunk-ZNDLUWWD.js.map +1 -0
- package/dist/commands/build/marketplace.js +114 -35
- package/dist/commands/build/marketplace.js.map +1 -1
- package/dist/commands/build/plugins.js +9 -10
- package/dist/commands/build/plugins.js.map +1 -1
- package/dist/commands/compile.js +97 -70
- package/dist/commands/compile.js.map +1 -1
- package/dist/commands/doctor.js +793 -64
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/edit.js +169 -208
- package/dist/commands/edit.js.map +1 -1
- package/dist/commands/eject.js +24 -39
- package/dist/commands/eject.js.map +1 -1
- package/dist/commands/init.js +41 -46
- package/dist/commands/list.js +10 -12
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/search.js +20 -81
- package/dist/commands/search.js.map +1 -1
- package/dist/commands/uninstall.js +25 -31
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/commands/update.js +82 -332
- package/dist/commands/update.js.map +1 -1
- package/dist/components/common/confirm.js +1 -1
- package/dist/components/common/prompt-confirm.js +1 -1
- package/dist/components/common/select-list.js +2 -2
- package/dist/components/common/spinner.js +1 -1
- package/dist/components/wizard/category-grid.js +5 -6
- package/dist/components/wizard/checkbox-grid.js +5 -6
- package/dist/components/wizard/domain-selection.js +12 -13
- package/dist/components/wizard/run-wizard-session.js +30 -36
- package/dist/components/wizard/scroll-affordance.js +2 -2
- package/dist/components/wizard/skill-agent-summary.js +7 -7
- package/dist/components/wizard/source-grid.js +6 -11
- package/dist/components/wizard/stack-selection.js +9 -9
- package/dist/components/wizard/step-agents.js +12 -12
- package/dist/components/wizard/step-build.js +10 -11
- package/dist/components/wizard/step-confirm.js +11 -11
- package/dist/components/wizard/step-sources.js +11 -17
- package/dist/components/wizard/step-stack.js +10 -10
- package/dist/components/wizard/summary-panel.js +10 -10
- package/dist/components/wizard/toast.js +2 -2
- package/dist/components/wizard/wizard-layout.js +15 -16
- package/dist/components/wizard/wizard-tabs.js +6 -12
- package/dist/components/wizard/wizard.js +29 -35
- package/dist/config-exports.js +1 -1
- package/dist/copy-local-skills-KF2RAVD3.js +14 -0
- package/dist/hooks/init.js +49 -48
- package/dist/hooks/init.js.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/load-agent-defs-DGXGBU3Q.js +15 -0
- package/dist/{loader-5JP4S7CJ.js → loader-4U7UCE2Y.js} +7 -5
- package/dist/recompile-project-agents-CJVBAWDJ.js +20 -0
- package/dist/{source-loader-MZYCPL2S.js → source-loader-ZNSYJJCI.js} +6 -6
- package/dist/src/agents/developer/ai-developer/identity.md +4 -4
- package/dist/src/agents/developer/ai-developer/playbook.md +1 -1
- package/dist/src/agents/developer/api-developer/identity.md +4 -4
- package/dist/src/agents/developer/api-developer/metadata.yaml +1 -1
- package/dist/src/agents/developer/cli-developer/identity.md +4 -4
- package/dist/src/agents/developer/web-developer/identity.md +2 -2
- package/dist/src/agents/developer/web-developer/metadata.yaml +1 -1
- package/dist/src/agents/meta/agent-summoner/identity.md +4 -5
- package/dist/src/agents/meta/agent-summoner/output.md +2 -2
- package/dist/src/agents/meta/agent-summoner/playbook.md +15 -16
- package/dist/src/agents/meta/codex-keeper/identity.md +4 -4
- package/dist/src/agents/meta/codex-keeper/playbook.md +1 -1
- package/dist/src/agents/meta/convention-keeper/identity.md +1 -1
- package/dist/src/agents/meta/skill-summoner/critical-reminders.md +0 -34
- package/dist/src/agents/meta/skill-summoner/critical-requirements.md +27 -3
- package/dist/src/agents/meta/skill-summoner/identity.md +2 -2
- package/{src/agents/planning/web-pm → dist/src/agents/planning/pm}/critical-reminders.md +10 -7
- package/dist/src/agents/planning/{web-pm → pm}/critical-requirements.md +8 -4
- package/dist/src/agents/planning/pm/identity.md +47 -0
- package/dist/src/agents/planning/pm/metadata.yaml +12 -0
- package/dist/src/agents/planning/{web-pm → pm}/output.md +7 -90
- package/dist/src/agents/planning/pm/playbook.md +185 -0
- package/dist/src/agents/researcher/ai-researcher/critical-reminders.md +58 -0
- package/dist/src/agents/researcher/ai-researcher/critical-requirements.md +40 -0
- package/dist/src/agents/researcher/ai-researcher/identity.md +86 -0
- package/dist/src/agents/researcher/ai-researcher/metadata.yaml +10 -0
- package/dist/src/agents/researcher/ai-researcher/output.md +363 -0
- package/dist/src/agents/researcher/ai-researcher/playbook.md +545 -0
- package/dist/src/agents/researcher/api-researcher/critical-reminders.md +16 -0
- package/dist/src/agents/researcher/api-researcher/identity.md +7 -7
- package/dist/src/agents/researcher/cli-researcher/critical-reminders.md +50 -0
- package/dist/src/agents/researcher/cli-researcher/critical-requirements.md +36 -0
- package/dist/src/agents/researcher/cli-researcher/identity.md +87 -0
- package/dist/src/agents/researcher/cli-researcher/metadata.yaml +10 -0
- package/dist/src/agents/researcher/cli-researcher/output.md +343 -0
- package/dist/src/agents/researcher/cli-researcher/playbook.md +370 -0
- package/dist/src/agents/researcher/web-researcher/identity.md +5 -6
- package/dist/src/agents/reviewer/reviewer/critical-reminders.md +29 -0
- package/dist/src/agents/reviewer/reviewer/critical-requirements.md +31 -0
- package/dist/src/agents/reviewer/reviewer/identity.md +41 -0
- package/dist/src/agents/reviewer/reviewer/metadata.yaml +12 -0
- package/dist/src/agents/reviewer/reviewer/output.md +202 -0
- package/dist/src/agents/reviewer/reviewer/playbook.md +141 -0
- package/dist/src/agents/tester/ai-tester/critical-reminders.md +48 -0
- package/dist/src/agents/tester/ai-tester/critical-requirements.md +49 -0
- package/dist/src/agents/tester/ai-tester/identity.md +62 -0
- package/dist/src/agents/tester/ai-tester/metadata.yaml +12 -0
- package/dist/src/agents/tester/ai-tester/output.md +375 -0
- package/dist/src/agents/tester/ai-tester/playbook.md +789 -0
- package/dist/src/agents/tester/api-tester/identity.md +3 -3
- package/dist/src/agents/tester/cli-tester/critical-reminders.md +3 -3
- package/dist/src/agents/tester/cli-tester/critical-requirements.md +2 -2
- package/dist/src/agents/tester/cli-tester/identity.md +3 -3
- package/dist/src/agents/tester/cli-tester/output.md +4 -4
- package/dist/src/agents/tester/cli-tester/playbook.md +3 -3
- package/dist/src/agents/tester/web-tester/identity.md +3 -3
- package/dist/stores/wizard-store.js +8 -6
- package/package.json +13 -2
- package/src/agents/developer/ai-developer/identity.md +4 -4
- package/src/agents/developer/ai-developer/playbook.md +1 -1
- package/src/agents/developer/api-developer/identity.md +4 -4
- package/src/agents/developer/api-developer/metadata.yaml +1 -1
- package/src/agents/developer/cli-developer/identity.md +4 -4
- package/src/agents/developer/web-developer/identity.md +2 -2
- package/src/agents/developer/web-developer/metadata.yaml +1 -1
- package/src/agents/meta/agent-summoner/identity.md +4 -5
- package/src/agents/meta/agent-summoner/output.md +2 -2
- package/src/agents/meta/agent-summoner/playbook.md +15 -16
- package/src/agents/meta/codex-keeper/identity.md +4 -4
- package/src/agents/meta/codex-keeper/playbook.md +1 -1
- package/src/agents/meta/convention-keeper/identity.md +1 -1
- package/src/agents/meta/skill-summoner/critical-reminders.md +0 -34
- package/src/agents/meta/skill-summoner/critical-requirements.md +27 -3
- package/src/agents/meta/skill-summoner/identity.md +2 -2
- package/{dist/src/agents/planning/web-pm → src/agents/planning/pm}/critical-reminders.md +10 -7
- package/src/agents/planning/{web-pm → pm}/critical-requirements.md +8 -4
- package/src/agents/planning/pm/identity.md +47 -0
- package/src/agents/planning/pm/metadata.yaml +12 -0
- package/src/agents/planning/{web-pm → pm}/output.md +7 -90
- package/src/agents/planning/pm/playbook.md +185 -0
- package/src/agents/researcher/ai-researcher/critical-reminders.md +58 -0
- package/src/agents/researcher/ai-researcher/critical-requirements.md +40 -0
- package/src/agents/researcher/ai-researcher/identity.md +86 -0
- package/src/agents/researcher/ai-researcher/metadata.yaml +10 -0
- package/src/agents/researcher/ai-researcher/output.md +363 -0
- package/src/agents/researcher/ai-researcher/playbook.md +545 -0
- package/src/agents/researcher/api-researcher/critical-reminders.md +16 -0
- package/src/agents/researcher/api-researcher/identity.md +7 -7
- package/src/agents/researcher/cli-researcher/critical-reminders.md +50 -0
- package/src/agents/researcher/cli-researcher/critical-requirements.md +36 -0
- package/src/agents/researcher/cli-researcher/identity.md +87 -0
- package/src/agents/researcher/cli-researcher/metadata.yaml +10 -0
- package/src/agents/researcher/cli-researcher/output.md +343 -0
- package/src/agents/researcher/cli-researcher/playbook.md +370 -0
- package/src/agents/researcher/web-researcher/identity.md +5 -6
- package/src/agents/reviewer/reviewer/critical-reminders.md +29 -0
- package/src/agents/reviewer/reviewer/critical-requirements.md +31 -0
- package/src/agents/reviewer/reviewer/identity.md +41 -0
- package/src/agents/reviewer/reviewer/metadata.yaml +12 -0
- package/src/agents/reviewer/reviewer/output.md +202 -0
- package/src/agents/reviewer/reviewer/playbook.md +141 -0
- package/src/agents/tester/ai-tester/critical-reminders.md +48 -0
- package/src/agents/tester/ai-tester/critical-requirements.md +49 -0
- package/src/agents/tester/ai-tester/identity.md +62 -0
- package/src/agents/tester/ai-tester/metadata.yaml +12 -0
- package/src/agents/tester/ai-tester/output.md +375 -0
- package/src/agents/tester/ai-tester/playbook.md +789 -0
- package/src/agents/tester/api-tester/identity.md +3 -3
- package/src/agents/tester/cli-tester/critical-reminders.md +3 -3
- package/src/agents/tester/cli-tester/critical-requirements.md +2 -2
- package/src/agents/tester/cli-tester/identity.md +3 -3
- package/src/agents/tester/cli-tester/output.md +4 -4
- package/src/agents/tester/cli-tester/playbook.md +3 -3
- package/src/agents/tester/web-tester/identity.md +3 -3
- package/src/schemas/metadata.schema.json +31 -2
- package/src/schemas/project-config.schema.json +61 -15
- package/src/schemas/project-source-config.schema.json +0 -47
- package/dist/chunk-5GUOIJSG.js.map +0 -1
- package/dist/chunk-5UWKOOIS.js.map +0 -1
- package/dist/chunk-76ADG7LQ.js.map +0 -1
- package/dist/chunk-BNSRA6HL.js.map +0 -1
- package/dist/chunk-E5X6RJ4E.js.map +0 -1
- package/dist/chunk-EEPELKOM.js +0 -5938
- package/dist/chunk-EEPELKOM.js.map +0 -1
- package/dist/chunk-F7T5AFFN.js +0 -20
- package/dist/chunk-F7T5AFFN.js.map +0 -1
- package/dist/chunk-FVDKAVOL.js.map +0 -1
- package/dist/chunk-KFB5CRXZ.js +0 -377
- package/dist/chunk-KFB5CRXZ.js.map +0 -1
- package/dist/chunk-KR65TPKC.js +0 -664
- package/dist/chunk-KR65TPKC.js.map +0 -1
- package/dist/chunk-LASI35KT.js.map +0 -1
- package/dist/chunk-LDP5ASDM.js.map +0 -1
- package/dist/chunk-LZ52XEPR.js.map +0 -1
- package/dist/chunk-M7HO7NBK.js +0 -88
- package/dist/chunk-M7HO7NBK.js.map +0 -1
- package/dist/chunk-MWG4VU64.js.map +0 -1
- package/dist/chunk-ODNDXQH2.js +0 -77
- package/dist/chunk-ODNDXQH2.js.map +0 -1
- package/dist/chunk-P44SXVH3.js +0 -80
- package/dist/chunk-P44SXVH3.js.map +0 -1
- package/dist/chunk-PMM2MYMA.js.map +0 -1
- package/dist/chunk-PZIZNMHK.js.map +0 -1
- package/dist/chunk-Q45ZU6HD.js +0 -37
- package/dist/chunk-Q45ZU6HD.js.map +0 -1
- package/dist/chunk-QRDTLVXG.js.map +0 -1
- package/dist/chunk-QZZJFYUQ.js.map +0 -1
- package/dist/chunk-R5NOI2LP.js +0 -170
- package/dist/chunk-R5NOI2LP.js.map +0 -1
- package/dist/chunk-RNJWAKMO.js.map +0 -1
- package/dist/chunk-SBHBBZKF.js.map +0 -1
- package/dist/chunk-SKQWRUPX.js.map +0 -1
- package/dist/chunk-STMHDOZD.js.map +0 -1
- package/dist/chunk-SX5H6TSC.js.map +0 -1
- package/dist/chunk-TUPYW2IQ.js.map +0 -1
- package/dist/chunk-UBEDZ4FD.js +0 -4198
- package/dist/chunk-UBEDZ4FD.js.map +0 -1
- package/dist/chunk-VY6MNOXL.js +0 -20
- package/dist/chunk-VY6MNOXL.js.map +0 -1
- package/dist/chunk-WBCPLH66.js +0 -42
- package/dist/chunk-WBCPLH66.js.map +0 -1
- package/dist/chunk-WKTHWQKM.js.map +0 -1
- package/dist/chunk-XHUODHZ4.js.map +0 -1
- package/dist/chunk-ZPPHXXAP.js +0 -252
- package/dist/chunk-ZPPHXXAP.js.map +0 -1
- package/dist/commands/import/skill.js +0 -344
- package/dist/commands/import/skill.js.map +0 -1
- package/dist/commands/new/agent.js +0 -300
- package/dist/commands/new/agent.js.map +0 -1
- package/dist/commands/new/marketplace.js +0 -310
- package/dist/commands/new/marketplace.js.map +0 -1
- package/dist/commands/new/skill.js +0 -26
- package/dist/commands/validate.js +0 -575
- package/dist/commands/validate.js.map +0 -1
- package/dist/components/wizard/search-modal.js +0 -13
- package/dist/components/wizard/selection-card.js +0 -9
- package/dist/components/wizard/step-settings.js +0 -18
- package/dist/copy-local-skills-6EMVN7RB.js +0 -14
- package/dist/load-agent-defs-BWOUVEAR.js +0 -15
- package/dist/load-agent-defs-BWOUVEAR.js.map +0 -1
- package/dist/loader-5JP4S7CJ.js.map +0 -1
- package/dist/recompile-project-agents-6DL45TDI.js +0 -19
- package/dist/recompile-project-agents-6DL45TDI.js.map +0 -1
- package/dist/source-loader-MZYCPL2S.js.map +0 -1
- package/dist/src/agents/developer/web-architecture/critical-reminders.md +0 -48
- package/dist/src/agents/developer/web-architecture/critical-requirements.md +0 -65
- package/dist/src/agents/developer/web-architecture/identity.md +0 -64
- package/dist/src/agents/developer/web-architecture/metadata.yaml +0 -12
- package/dist/src/agents/developer/web-architecture/output.md +0 -449
- package/dist/src/agents/developer/web-architecture/playbook.md +0 -481
- package/dist/src/agents/pattern/pattern-scout/critical-reminders.md +0 -71
- package/dist/src/agents/pattern/pattern-scout/critical-requirements.md +0 -31
- package/dist/src/agents/pattern/pattern-scout/identity.md +0 -25
- package/dist/src/agents/pattern/pattern-scout/metadata.yaml +0 -10
- package/dist/src/agents/pattern/pattern-scout/output.md +0 -290
- package/dist/src/agents/pattern/pattern-scout/playbook.md +0 -1806
- package/dist/src/agents/pattern/web-pattern-critique/critical-reminders.md +0 -30
- package/dist/src/agents/pattern/web-pattern-critique/critical-requirements.md +0 -28
- package/dist/src/agents/pattern/web-pattern-critique/identity.md +0 -41
- package/dist/src/agents/pattern/web-pattern-critique/metadata.yaml +0 -12
- package/dist/src/agents/pattern/web-pattern-critique/output.md +0 -314
- package/dist/src/agents/pattern/web-pattern-critique/playbook.md +0 -572
- package/dist/src/agents/planning/api-pm/critical-reminders.md +0 -48
- package/dist/src/agents/planning/api-pm/critical-requirements.md +0 -41
- package/dist/src/agents/planning/api-pm/identity.md +0 -39
- package/dist/src/agents/planning/api-pm/metadata.yaml +0 -12
- package/dist/src/agents/planning/api-pm/output.md +0 -475
- package/dist/src/agents/planning/api-pm/playbook.md +0 -129
- package/dist/src/agents/planning/web-pm/identity.md +0 -26
- package/dist/src/agents/planning/web-pm/metadata.yaml +0 -12
- package/dist/src/agents/planning/web-pm/playbook.md +0 -318
- package/dist/src/agents/reviewer/ai-reviewer/critical-reminders.md +0 -40
- package/dist/src/agents/reviewer/ai-reviewer/critical-requirements.md +0 -37
- package/dist/src/agents/reviewer/ai-reviewer/identity.md +0 -51
- package/dist/src/agents/reviewer/ai-reviewer/metadata.yaml +0 -10
- package/dist/src/agents/reviewer/ai-reviewer/output.md +0 -395
- package/dist/src/agents/reviewer/ai-reviewer/playbook.md +0 -90
- package/dist/src/agents/reviewer/api-reviewer/critical-reminders.md +0 -31
- package/dist/src/agents/reviewer/api-reviewer/critical-requirements.md +0 -27
- package/dist/src/agents/reviewer/api-reviewer/identity.md +0 -51
- package/dist/src/agents/reviewer/api-reviewer/metadata.yaml +0 -12
- package/dist/src/agents/reviewer/api-reviewer/output.md +0 -343
- package/dist/src/agents/reviewer/api-reviewer/playbook.md +0 -288
- package/dist/src/agents/reviewer/cli-reviewer/critical-reminders.md +0 -33
- package/dist/src/agents/reviewer/cli-reviewer/critical-requirements.md +0 -30
- package/dist/src/agents/reviewer/cli-reviewer/identity.md +0 -44
- package/dist/src/agents/reviewer/cli-reviewer/metadata.yaml +0 -12
- package/dist/src/agents/reviewer/cli-reviewer/output.md +0 -414
- package/dist/src/agents/reviewer/cli-reviewer/playbook.md +0 -218
- package/dist/src/agents/reviewer/infra-reviewer/critical-reminders.md +0 -38
- package/dist/src/agents/reviewer/infra-reviewer/critical-requirements.md +0 -38
- package/dist/src/agents/reviewer/infra-reviewer/identity.md +0 -53
- package/dist/src/agents/reviewer/infra-reviewer/metadata.yaml +0 -10
- package/dist/src/agents/reviewer/infra-reviewer/output.md +0 -364
- package/dist/src/agents/reviewer/infra-reviewer/playbook.md +0 -161
- package/dist/src/agents/reviewer/web-reviewer/critical-reminders.md +0 -31
- package/dist/src/agents/reviewer/web-reviewer/critical-requirements.md +0 -26
- package/dist/src/agents/reviewer/web-reviewer/identity.md +0 -40
- package/dist/src/agents/reviewer/web-reviewer/metadata.yaml +0 -12
- package/dist/src/agents/reviewer/web-reviewer/output.md +0 -333
- package/dist/src/agents/reviewer/web-reviewer/playbook.md +0 -156
- package/src/agents/developer/web-architecture/critical-reminders.md +0 -48
- package/src/agents/developer/web-architecture/critical-requirements.md +0 -65
- package/src/agents/developer/web-architecture/identity.md +0 -64
- package/src/agents/developer/web-architecture/metadata.yaml +0 -12
- package/src/agents/developer/web-architecture/output.md +0 -449
- package/src/agents/developer/web-architecture/playbook.md +0 -481
- package/src/agents/pattern/pattern-scout/critical-reminders.md +0 -71
- package/src/agents/pattern/pattern-scout/critical-requirements.md +0 -31
- package/src/agents/pattern/pattern-scout/identity.md +0 -25
- package/src/agents/pattern/pattern-scout/metadata.yaml +0 -10
- package/src/agents/pattern/pattern-scout/output.md +0 -290
- package/src/agents/pattern/pattern-scout/playbook.md +0 -1806
- package/src/agents/pattern/web-pattern-critique/critical-reminders.md +0 -30
- package/src/agents/pattern/web-pattern-critique/critical-requirements.md +0 -28
- package/src/agents/pattern/web-pattern-critique/identity.md +0 -41
- package/src/agents/pattern/web-pattern-critique/metadata.yaml +0 -12
- package/src/agents/pattern/web-pattern-critique/output.md +0 -314
- package/src/agents/pattern/web-pattern-critique/playbook.md +0 -572
- package/src/agents/planning/api-pm/critical-reminders.md +0 -48
- package/src/agents/planning/api-pm/critical-requirements.md +0 -41
- package/src/agents/planning/api-pm/identity.md +0 -39
- package/src/agents/planning/api-pm/metadata.yaml +0 -12
- package/src/agents/planning/api-pm/output.md +0 -475
- package/src/agents/planning/api-pm/playbook.md +0 -129
- package/src/agents/planning/web-pm/identity.md +0 -26
- package/src/agents/planning/web-pm/metadata.yaml +0 -12
- package/src/agents/planning/web-pm/playbook.md +0 -318
- package/src/agents/reviewer/ai-reviewer/critical-reminders.md +0 -40
- package/src/agents/reviewer/ai-reviewer/critical-requirements.md +0 -37
- package/src/agents/reviewer/ai-reviewer/identity.md +0 -51
- package/src/agents/reviewer/ai-reviewer/metadata.yaml +0 -10
- package/src/agents/reviewer/ai-reviewer/output.md +0 -395
- package/src/agents/reviewer/ai-reviewer/playbook.md +0 -90
- package/src/agents/reviewer/api-reviewer/critical-reminders.md +0 -31
- package/src/agents/reviewer/api-reviewer/critical-requirements.md +0 -27
- package/src/agents/reviewer/api-reviewer/identity.md +0 -51
- package/src/agents/reviewer/api-reviewer/metadata.yaml +0 -12
- package/src/agents/reviewer/api-reviewer/output.md +0 -343
- package/src/agents/reviewer/api-reviewer/playbook.md +0 -288
- package/src/agents/reviewer/cli-reviewer/critical-reminders.md +0 -33
- package/src/agents/reviewer/cli-reviewer/critical-requirements.md +0 -30
- package/src/agents/reviewer/cli-reviewer/identity.md +0 -44
- package/src/agents/reviewer/cli-reviewer/metadata.yaml +0 -12
- package/src/agents/reviewer/cli-reviewer/output.md +0 -414
- package/src/agents/reviewer/cli-reviewer/playbook.md +0 -218
- package/src/agents/reviewer/infra-reviewer/critical-reminders.md +0 -38
- package/src/agents/reviewer/infra-reviewer/critical-requirements.md +0 -38
- package/src/agents/reviewer/infra-reviewer/identity.md +0 -53
- package/src/agents/reviewer/infra-reviewer/metadata.yaml +0 -10
- package/src/agents/reviewer/infra-reviewer/output.md +0 -364
- package/src/agents/reviewer/infra-reviewer/playbook.md +0 -161
- package/src/agents/reviewer/web-reviewer/critical-reminders.md +0 -31
- package/src/agents/reviewer/web-reviewer/critical-requirements.md +0 -26
- package/src/agents/reviewer/web-reviewer/identity.md +0 -40
- package/src/agents/reviewer/web-reviewer/metadata.yaml +0 -12
- package/src/agents/reviewer/web-reviewer/output.md +0 -333
- package/src/agents/reviewer/web-reviewer/playbook.md +0 -156
- /package/dist/{chunk-ZL7PFNHN.js.map → chunk-K3O3YQYG.js.map} +0 -0
- /package/dist/{chunk-IHUY4M22.js.map → chunk-NXGMM5YN.js.map} +0 -0
- /package/dist/{chunk-DTSG5XZB.js.map → chunk-V3ENMW7G.js.map} +0 -0
- /package/dist/{commands/new/skill.js.map → copy-local-skills-KF2RAVD3.js.map} +0 -0
- /package/dist/{components/wizard/search-modal.js.map → load-agent-defs-DGXGBU3Q.js.map} +0 -0
- /package/dist/{components/wizard/selection-card.js.map → loader-4U7UCE2Y.js.map} +0 -0
- /package/dist/{components/wizard/step-settings.js.map → recompile-project-agents-CJVBAWDJ.js.map} +0 -0
- /package/dist/{copy-local-skills-6EMVN7RB.js.map → source-loader-ZNSYJJCI.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/stores/wizard-store.ts","../src/cli/lib/wizard/build-step-logic.ts","../src/cli/lib/wizard/scope-diff.ts","../src/cli/lib/wizard/domain-order.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport {\n DEFAULT_PUBLIC_SOURCE_NAME,\n DEFAULT_SCRATCH_DOMAINS,\n EJECT_SOURCE,\n FALLBACK_DOMAIN,\n INSTALL_MODES,\n} from \"../consts.js\";\nimport type { InstallMode } from \"../lib/installation/index.js\";\nimport { deriveInstallMode as sharedDeriveInstallMode } from \"../lib/installation/installation.js\";\nimport type { AgentScopeConfig, SkillConfig, SkillScope } from \"../types/config.js\";\nimport {\n isActiveAt,\n isGlobalTombstone,\n isProjectOwned,\n} from \"../lib/configuration/scope-predicates.js\";\nimport { matrix, getSkillById, getCategoryDomain } from \"../lib/matrix/matrix-provider.js\";\nimport { buildCategoriesForDomain, orderDomains, skillSlotKey } from \"../lib/wizard/index.js\";\nimport type {\n AgentName,\n Domain,\n DomainSelections,\n SkillId,\n Category,\n CategorySelections,\n ResolvedSkill,\n} from \"../types/index.js\";\nimport type { SourceOption, SourceRow } from \"../components/wizard/source-grid.js\";\nimport { warn } from \"../utils/logger.js\";\nimport { typedEntries, typedFromEntries, typedKeys, typedValues } from \"../utils/typed-object.js\";\n\n/** Toast strings surfaced by scope/selection guards (E2E asserts these verbatim). */\nconst TOAST_MESSAGES = {\n GLOBAL_SKILLS_LOCKED: \"Global skills cannot be changed from project scope\",\n ONLY_SKILL_IN_CATEGORY: \"Cannot deselect the only skill in this category\",\n ALREADY_EJECTED_AT_GLOBAL: \"Already exists as ejected skill at global scope\",\n GLOBAL_AGENTS_LOCKED: \"Global agents cannot be changed from project scope\",\n} as const;\n\n/** First defined source among candidates, else the default public source. */\nfunction resolveEffectiveSource(...candidates: Array<string | undefined>): string {\n return candidates.find((source) => source !== undefined) ?? DEFAULT_PUBLIC_SOURCE_NAME;\n}\n\n/** Name of a skill's primary available source, if any. */\nfunction primarySourceName(skill: ResolvedSkill | undefined): string | undefined {\n return skill?.availableSources?.find((s) => s.primary)?.name;\n}\n\nfunction createDefaultSkillConfig(id: SkillId): SkillConfig {\n const skill = matrix.skills[id];\n const primarySource = primarySourceName(skill);\n return { id, scope: \"global\", source: primarySource ?? DEFAULT_PUBLIC_SOURCE_NAME };\n}\n\n/** True when configs hold an active (non-excluded) project-scope entry for the id. */\nfunction hasProjectActive(configs: SkillConfig[], id: SkillId): boolean {\n return configs.some((sc) => sc.id === id && isActiveAt(sc, \"project\"));\n}\n\n/** The active (non-excluded) global-scope entry for the id, when configs hold one. */\nfunction findGlobalActive(configs: SkillConfig[], id: SkillId): SkillConfig | undefined {\n return configs.find((sc) => sc.id === id && isActiveAt(sc, \"global\"));\n}\n\n/** True when configs hold an active (non-excluded) global-scope entry for the id. */\nfunction hasGlobalActive(configs: SkillConfig[], id: SkillId): boolean {\n return findGlobalActive(configs, id) !== undefined;\n}\n\n/** True when configs hold an excluded global-scope tombstone for the id. */\nfunction hasGlobalTombstone(configs: SkillConfig[], id: SkillId): boolean {\n return configs.some((sc) => sc.id === id && isGlobalTombstone(sc));\n}\n\n/**\n * D-233 Scenario B: a dual-scope pair — an active project entry plus a global tombstone for\n * the same skill. The shape the `[P][G]` row renders from; removal collapses it to a single\n * inherited-global entry, re-selection rebuilds it.\n */\nfunction isDualScopePair(configs: SkillConfig[], id: SkillId): boolean {\n return hasProjectActive(configs, id) && hasGlobalTombstone(configs, id);\n}\n\n/**\n * True when a SELECTED skill must not be deselected at project scope.\n *\n * The lock protects GLOBAL-OWNED halves and nothing else. Two arms: an active global entry in\n * the hydration snapshot (genuinely global-only — the long-standing read-only behaviour); and a\n * snapshot tombstone paired with a LIVE plain active global entry (the stale state a persisted\n * `[P][G]` reaches after an in-session collapse, whose deselect would silently tombstone the\n * still-real global install).\n *\n * A persisted `[P][G]` pair matches neither: its live global half is a tombstone, and its project\n * half is the project's own install to drop — `applySkillRemoval` collapses the pair to the\n * inherited global entry, so removing it never touches the global install it masks. A skill\n * freshly added this session (absent from the snapshot) is likewise nobody's install yet and\n * stays freely deselectable.\n */\nfunction isGloballyLockedSkill(\n installed: SkillConfig[],\n liveConfigs: SkillConfig[],\n id: SkillId,\n): boolean {\n return (\n hasGlobalActive(installed, id) ||\n (hasGlobalTombstone(installed, id) && hasGlobalActive(liveConfigs, id))\n );\n}\n\n/**\n * True when an exclusive-category radio swap must not drop this skill at project scope.\n *\n * The deselect lock, plus one case it deliberately allows on its own row: a live `[P][G]` pair.\n * Dropping a pair's project half leaves the global install it masks ACTIVE, so a swap that did it\n * implicitly would seat the new pick beside a still-active sibling in a category that permits one.\n * Removing the pair stays the project's to do — with the selection key on its own row, where\n * nothing takes the freed slot.\n */\nfunction blocksExclusiveSwap(\n installed: SkillConfig[],\n liveConfigs: SkillConfig[],\n id: SkillId,\n): boolean {\n return isGloballyLockedSkill(installed, liveConfigs, id) || isDualScopePair(liveConfigs, id);\n}\n\n/**\n * True when re-scoping this project-eject entry to global would overwrite an ejected global\n * install that the live config does not already override with a tombstone.\n */\nfunction wouldOverwriteGlobalEject(\n config: SkillConfig,\n liveConfigs: SkillConfig[],\n installedSkillConfigs: SkillConfig[] | null,\n): boolean {\n if (config.scope !== \"project\" || config.source !== EJECT_SOURCE) return false;\n const globalEjectInstalled = installedSkillConfigs?.some(\n (sc) =>\n sc.id === config.id && sc.scope === \"global\" && sc.source === EJECT_SOURCE && !sc.excluded,\n );\n if (!globalEjectInstalled) return false;\n return !liveConfigs.some((sc) => sc.id === config.id && sc.excluded);\n}\n\n/**\n * Rewrites the source of the ACTIVE entry at (id, scope), leaving every other entry untouched —\n * in particular a dual-scope skill's excluded global tombstone, which keeps describing the\n * masked global install (D-262).\n */\nfunction withActiveEntrySource(\n configs: SkillConfig[],\n id: SkillId,\n scope: SkillScope | undefined,\n source: string,\n): SkillConfig[] {\n return configs.map((sc) =>\n sc.id === id && !sc.excluded && sc.scope === scope ? { ...sc, source } : sc,\n );\n}\n\n/**\n * Removes deselected skills, honouring what the project is allowed to remove (D-277).\n *\n * An entry the project OWNS — project-scoped, or the project's own global tombstone — is\n * dropped. An entry the project merely INHERITS (an active global-scope entry the hydration\n * snapshot already carried) is immutable from project scope and survives BYTE-IDENTICAL: a\n * project edit neither uninstalls a global install nor mints a tombstone for it. A skill this\n * session added itself is absent from the snapshot, is nobody's install yet, and is dropped\n * like any other so an accidental add stays undoable.\n *\n * `installedSkillConfigs` is `null` when editing FROM global scope, where the config being\n * edited IS the global one: nothing is inherited, so every removal is a genuine uninstall\n * (D-233 Scenario C).\n *\n * Dual-scope entries (an active project entry paired with a global tombstone) collapse to a\n * single inherited-global entry so the `[G]` badge keeps rendering after the project half is\n * dropped (D-233 Scenario B).\n */\nfunction applySkillRemoval(\n configs: SkillConfig[],\n removedIds: Iterable<SkillId>,\n installedSkillConfigs: SkillConfig[] | null,\n): SkillConfig[] {\n // Annotated because `instanceof Set` narrows an `Iterable<SkillId>` to `Set<any>`,\n // which then spreads out as `any[]` below.\n const removed: ReadonlySet<SkillId> =\n removedIds instanceof Set ? removedIds : new Set(removedIds);\n const installedIds = new Set((installedSkillConfigs ?? []).map((s) => s.id));\n\n /**\n * A removed entry the project may not drop: a global install that existed before this session\n * and that the project does not own. Dual-scope pairs are excluded — both their halves go, and\n * the surviving global install is re-surfaced as a single inherited entry below.\n */\n const survivesRemoval = (entry: SkillConfig): boolean =>\n !isDualScopePair(configs, entry.id) && !isProjectOwned(entry) && installedIds.has(entry.id);\n\n const retained = configs.filter((sc) => !removed.has(sc.id) || survivesRemoval(sc));\n const resurfacedInheritedGlobals = [...removed]\n .filter((id) => isDualScopePair(configs, id))\n .map((id) => ({\n id,\n scope: \"global\" as const,\n source:\n configs.find((sc) => sc.id === id && sc.scope === \"global\")?.source ??\n DEFAULT_PUBLIC_SOURCE_NAME,\n }));\n\n return [...retained, ...resurfacedInheritedGlobals];\n}\n\n/** All skill IDs across a domain's category selections, in category order. */\nfunction flattenCategorySelections(selections: CategorySelections): SkillId[] {\n return typedValues(selections).flat();\n}\n\n/** Collects all skill IDs from a domain's category selections. */\nfunction collectSkillIdsFromSelections(selections: CategorySelections): Set<SkillId> {\n return new Set(flattenCategorySelections(selections));\n}\n\n/** Reconciles skill configs after selection changes: drops project-owned removals, leaves inherited global installs untouched, restores excluded on re-select, adds new defaults. */\nfunction reconcileSkillConfigs(\n configs: SkillConfig[],\n added: SkillId[],\n removed: SkillId[],\n installedSkillConfigs: SkillConfig[] | null,\n isEditingFromGlobalScope: boolean,\n): SkillConfig[] {\n // Editing from global scope has no project overlay, so a removal is a genuine uninstall.\n // Pass null so applySkillRemoval DROPS the skill rather than retaining it as an inherited\n // global install the project may not touch (D-233 Scenario C).\n const effectiveInstalled = isEditingFromGlobalScope ? null : installedSkillConfigs;\n let result = applySkillRemoval(configs, removed, effectiveInstalled);\n\n for (const id of added) {\n // Dual-scope restore: re-selecting a skill that is globally installed (recorded as a\n // tombstone in the project snapshot) with no current project entry re-creates BOTH a\n // fresh project entry and a global tombstone — mirroring toggleSkillScope's G->P path\n // so the row renders `[P][G]` again (D-233 Scenario B second spacebar).\n if (\n !isEditingFromGlobalScope &&\n hasGlobalTombstone(installedSkillConfigs ?? [], id) &&\n !hasProjectActive(result, id)\n ) {\n const globalEntry =\n (installedSkillConfigs ?? []).find((sc) => sc.id === id && sc.scope === \"global\") ??\n result.find((sc) => sc.id === id && sc.scope === \"global\");\n const source = globalEntry?.source ?? DEFAULT_PUBLIC_SOURCE_NAME;\n result = [\n ...result.filter((sc) => sc.id !== id),\n { id, scope: \"project\", source },\n { id, scope: \"global\", excluded: true, source },\n ];\n continue;\n }\n\n const existingExcluded = result.find((sc) => sc.id === id && sc.excluded);\n if (existingExcluded) {\n // Un-excluding drops the tombstone flag rather than setting it to `undefined`:\n // an absent `excluded` is what \"not excluded\" means everywhere else in the config.\n result = result.map((sc) => {\n if (sc.id !== id || !sc.excluded) return sc;\n const { excluded: _excluded, ...active } = sc;\n return active;\n });\n } else if (!result.some((sc) => sc.id === id)) {\n // Re-selecting a skill that the hydration snapshot still holds is a RESTORE, not a fresh\n // add: it must come back with its persisted scope and source rather than the wizard\n // defaults (a project-scoped eject skill would otherwise round-trip to global+marketplace\n // and trigger a bogus scope/source migration). buildSkillConfigForId degrades to exactly\n // createDefaultSkillConfig's output when the snapshot has no entry, so genuinely-new\n // selections are unaffected.\n result = [...result, buildSkillConfigForId(id, installedSkillConfigs)];\n }\n }\n\n return result;\n}\n\n/** True when configs hold an active (non-excluded) project-scope entry for the agent. */\nfunction agentHasProjectActive(configs: AgentScopeConfig[], name: AgentName): boolean {\n return configs.some((ac) => ac.name === name && ac.scope === \"project\" && !ac.excluded);\n}\n\n/** True when configs hold an active (non-excluded) global-scope entry for the agent. */\nfunction agentHasGlobalActive(configs: AgentScopeConfig[], name: AgentName): boolean {\n return configs.some((ac) => ac.name === name && ac.scope === \"global\" && !ac.excluded);\n}\n\n/** True when configs hold an excluded global-scope tombstone for the agent. */\nfunction agentHasGlobalTombstone(configs: AgentScopeConfig[], name: AgentName): boolean {\n return configs.some((ac) => ac.name === name && ac.scope === \"global\" && ac.excluded);\n}\n\n/** Agent side of isDualScopePair (D-233): active project entry + global tombstone for the same agent. */\nfunction isDualScopeAgentPair(configs: AgentScopeConfig[], name: AgentName): boolean {\n return agentHasProjectActive(configs, name) && agentHasGlobalTombstone(configs, name);\n}\n\n/**\n * Excluded tombstone entries. Preserved across reconcile/preselect merges so a dual-scope\n * pair's tombstone half survives (D-223/D-227) and the pair can be restored later.\n */\nfunction collectTombstones<T extends { excluded?: boolean }>(configs: T[]): T[] {\n return configs.filter((entry) => entry.excluded);\n}\n\n/**\n * Applies an agent toggle: deselect removes the agent's entries; select restores an excluded\n * entry or adds a new one.\n *\n * A deselect never masks a globally-installed agent (D-277). Every reachable deselect here is\n * one the project owns — an active global install and a live `[P][G]` pair are both refused\n * upstream in `toggleAgent`, and a global-scope edit owns everything it can see — so the only\n * correct outcome is a clean removal.\n */\nfunction applyAgentToggle(\n configs: AgentScopeConfig[],\n agent: AgentName,\n isSelected: boolean,\n): AgentScopeConfig[] {\n if (isSelected) {\n return configs.filter((ac) => ac.name !== agent);\n }\n\n const existingExcluded = configs.find((ac) => ac.name === agent && ac.excluded);\n if (existingExcluded) {\n return configs.map((ac) => {\n if (ac.name !== agent || !ac.excluded) return ac;\n const { excluded: _excluded, ...active } = ac;\n return active;\n });\n }\n return [...configs, { name: agent, scope: \"global\" as const }];\n}\n\n/**\n * True when an existing agent entry must outlive a domain-roster rebuild, which merges rather than\n * replaces. Two kinds survive:\n *\n * - ALL excluded tombstones, so a dual-scope pair (active entry plus a tombstone at the other\n * scope) survives preselection — the agent-side mirror of the D-223 fix in\n * `populateFromSkillIds`. Deliberately NOT filtered by roster membership (D-227).\n * - Every entry the project does not own, so a globally-installed agent outside the selected\n * domains' roster is never silently uninstalled by a project edit (D-277).\n *\n * Entries the roster itself rebuilds are excluded — `buildAgentConfigForName` re-derives those.\n */\nfunction survivesRosterRebuild(\n agentConfig: AgentScopeConfig,\n roster: ReadonlySet<AgentName>,\n): boolean {\n if (agentConfig.excluded) return true;\n return !isProjectOwned(agentConfig) && !roster.has(agentConfig.name);\n}\n\n/**\n * D-233: restores the `[P][G]` pair when re-selecting an inherited-global agent row whose\n * global install is recorded as a tombstone in the project snapshot — mirrors\n * reconcileSkillConfigs' restore branch.\n */\nfunction restoreDualScopeAgent(\n selectedAgents: AgentName[],\n agentConfigs: AgentScopeConfig[],\n agent: AgentName,\n) {\n return {\n selectedAgents: [...selectedAgents, agent],\n agentConfigs: [\n ...agentConfigs.filter((ac) => ac.name !== agent),\n { name: agent, scope: \"project\" as const },\n { name: agent, scope: \"global\" as const, excluded: true },\n ],\n };\n}\n\n/**\n * Next selectedAgents after a toggle. A deselect drops the agent; re-enabling a tombstoned\n * agent is already listed; a fresh add appends.\n */\nfunction nextSelectedAgents(\n selectedAgents: AgentName[],\n agent: AgentName,\n flags: { isSelected: boolean; isInList: boolean },\n): AgentName[] {\n if (flags.isSelected) return selectedAgents.filter((a) => a !== agent);\n if (flags.isInList) return selectedAgents;\n return [...selectedAgents, agent];\n}\n\n/** Builds a SkillConfig for a resolved skill ID, preferring saved config values. */\nfunction buildSkillConfigForId(id: SkillId, savedConfigs?: SkillConfig[] | null): SkillConfig {\n // Prefer project-scoped entry over global when duplicates exist (D-198 defensive fix)\n const saved =\n savedConfigs?.find((sc) => sc.id === id && !sc.excluded && sc.scope === \"project\") ??\n savedConfigs?.find((sc) => sc.id === id && !sc.excluded);\n const skill = matrix.skills[id];\n const primarySource = primarySourceName(skill);\n return {\n id,\n scope: saved?.scope ?? \"global\",\n source: resolveEffectiveSource(saved?.source, primarySource),\n };\n}\n\n/**\n * Builds an active AgentScopeConfig for a name, preferring a saved project-scoped\n * active entry. Mirrors buildSkillConfigForId on the agent path so preselection can\n * separate active-entry construction from tombstone preservation (D-227).\n */\nfunction buildAgentConfigForName(\n name: AgentName,\n savedConfigs?: AgentScopeConfig[],\n): AgentScopeConfig {\n // Prefer project-scoped active entry over global when duplicates exist.\n const saved =\n savedConfigs?.find((ac) => ac.name === name && !ac.excluded && ac.scope === \"project\") ??\n savedConfigs?.find((ac) => ac.name === name && !ac.excluded);\n return {\n name,\n scope: saved?.scope ?? \"global\",\n // Model and effort are the user's deliberate choice, not something the roster re-derives —\n // they survive the rebuild on the same terms as scope.\n ...(saved?.model !== undefined && { model: saved.model }),\n ...(saved?.effort !== undefined && { effort: saved.effort }),\n };\n}\n\n/** Restores skill configs for a domain: clears excluded flags on restored skills, adds new defaults for unknown skills. */\nfunction restoreSkillConfigs(\n existingConfigs: SkillConfig[],\n restoredIds: SkillId[],\n): SkillConfig[] {\n const restoredSet = new Set(restoredIds);\n const existingIds = new Set(existingConfigs.map((sc) => sc.id));\n\n const updated = existingConfigs.map((sc) => {\n if (!restoredSet.has(sc.id) || !sc.excluded) return sc;\n const { excluded: _excluded, ...active } = sc;\n return active;\n });\n\n const newConfigs = restoredIds.filter((id) => !existingIds.has(id)).map(createDefaultSkillConfig);\n\n return [...updated, ...newConfigs];\n}\n\n/**\n * Built-in agent names grouped by domain prefix. Custom domains return no preselected agents.\n * The consolidated `pm` and `reviewer` are cross-domain by role, so every domain rosters both;\n * the preselection union dedupes when several selected domains bring them.\n */\nconst DOMAIN_AGENTS: Partial<Record<Domain, AgentName[]>> = {\n web: [\"web-developer\", \"web-researcher\", \"web-tester\", \"pm\", \"reviewer\"],\n api: [\"api-developer\", \"api-researcher\", \"api-tester\", \"pm\", \"reviewer\"],\n cli: [\"cli-developer\", \"cli-tester\", \"cli-researcher\", \"pm\", \"reviewer\"],\n ai: [\"ai-developer\", \"ai-researcher\", \"ai-tester\", \"pm\", \"reviewer\"],\n};\n\nfunction resolveSkillForPopulation(\n skillId: SkillId,\n): { domain: Domain; subcat: Category; techId: SkillId } | null {\n const { skills } = matrix;\n const skill = skills[skillId];\n if (!skill?.category) {\n warn(\n `Installed skill '${skillId}' is not present in the loaded source — it may have been removed or renamed`,\n );\n return null;\n }\n\n const domain = getCategoryDomain(skill.category);\n if (!domain) {\n warn(`Installed skill '${skillId}' has unknown category '${skill.category}' — skipping`);\n return null;\n }\n\n // Boundary cast: domain lookup confirmed category exists in matrix\n const subcat = skill.category as Category;\n return { domain, subcat, techId: skillId };\n}\n\n/** Adds a skill to selections[domain][category] if absent; true when newly added. */\nfunction addToDomainSelections(\n selections: DomainSelections,\n domain: Domain,\n category: Category,\n skillId: SkillId,\n): boolean {\n const categorySelections = (selections[domain] ??= {});\n const skillList = (categorySelections[category] ??= []);\n if (skillList.includes(skillId)) return false;\n skillList.push(skillId);\n return true;\n}\n\n/** The one marketplace a skill can be installed from as a plugin. */\nfunction marketplaceSourceName(skill: ResolvedSkill): string {\n return (\n skill.availableSources?.find((source) => source.type !== \"local\")?.name ??\n DEFAULT_PUBLIC_SOURCE_NAME\n );\n}\n\n/** The `SkillConfig.source` value an install mode writes for this skill. */\nfunction sourceForInstallMode(skill: ResolvedSkill, mode: Exclude<InstallMode, \"mixed\">): string {\n return mode === \"eject\" ? EJECT_SOURCE : marketplaceSourceName(skill);\n}\n\n/**\n * The install mode a persisted `source` value stands for. The per-skill half of\n * `deriveInstallMode`, which asks the same question of a whole selection: `eject` names the\n * project's own copy and every other value names the marketplace the plugin comes from.\n */\nfunction installModeOfSource(source: string | undefined): Exclude<InstallMode, \"mixed\"> {\n return source === EJECT_SOURCE ? \"eject\" : \"plugin\";\n}\n\n/** The two cells of one skill's install-mode control, with the current mode selected. */\nfunction buildInstallModeOptions(selectedMode: Exclude<InstallMode, \"mixed\">): SourceOption[] {\n return INSTALL_MODES.map((mode) => ({ mode, selected: mode === selectedMode }));\n}\n\n/**\n * D-257: the snapshot slots this session has emptied — an `(id, scope)` the hydration snapshot\n * holds ACTIVE that no live config entry occupies any more. The emptying leaves nothing behind to\n * render from: a PROJECT skill is dropped outright (applySkillRemoval leaves no tombstone, unlike\n * the global case), and so is ANY skill deselected while editing from global scope, where\n * `reconcileSkillConfigs` passes `null` so `applySkillRemoval` drops rather than tombstones. In\n * both the snapshot is the only surviving record, so without this set the row would vanish and the\n * user would lose sight of what saving is about to remove. Deliberately ungated on edit context:\n * `computeScopeDiff`, which the confirm step classifies removals with, has no global-scope gate\n * either, so neither surface may have one.\n *\n * Keyed per `(id, scope)` SLOT, mirroring `computeScopeDiff`'s removal match (D-271) — removal is a\n * property of the slot, not of the id. Collapsing a dual-scope `[P][G]` pair to `[G]` empties the\n * PROJECT slot while the global one survives, so the skill renders twice on BOTH surfaces (`-` at\n * Project, `•`/lock at Global) instead of the survivor masking the loss. A tombstone OCCUPIES its\n * slot, so re-activating a masked global install is never a removal. Selection is not consulted:\n * `skillConfigs` is the same input the confirm step diffs, which is what keeps the two agreeing.\n *\n * Snapshot TOMBSTONES are deliberately not removal candidates — narrower than `computeScopeDiff`,\n * whose removal filter counts them. A tombstone is a MASK over a global install, not an install of\n * its own: dropping it deletes nothing (the global copy survives), so a red pending-removal row for\n * one would announce a deletion that never happens. No store transition reaches that shape anyway —\n * every path that drops a tombstone fills the same slot with an active entry (`toggleSkillScope`\n * P→G, `applySkillRemoval`'s dual-scope collapse).\n */\nfunction collectRemovedInstalledEntries(\n installedSkillConfigs: SkillConfig[] | null,\n skillConfigs: SkillConfig[],\n): SkillConfig[] {\n if (!installedSkillConfigs) return [];\n const occupiedSlots = new Set(skillConfigs.map((sc) => skillSlotKey(sc.id, sc.scope)));\n return installedSkillConfigs.filter(\n (sc) =>\n !sc.excluded &&\n !occupiedSlots.has(skillSlotKey(sc.id, sc.scope)) &&\n // A skill the marketplace no longer carries has nothing to render — hydration already\n // skipped it into unresolvableSkillIds, and getSkillById would throw on it downstream.\n !!matrix.skills[sc.id],\n );\n}\n\n/**\n * D-258: every `(id, scope)` slot the hydration snapshot occupies — the baseline a Sources row is\n * \"added\" against, and the direct counterpart of `computeScopeDiff`'s `prevSkillKeySet`. Tombstones\n * count as occupied, exactly as that set does (D-232), so re-reading a stored tombstone never flags\n * a spurious addition.\n *\n * A missing snapshot (a first `init`, nothing installed at either scope) collapses to the EMPTY set\n * rather than to a distinct \"no baseline\" state, because the two are the same answer: an empty\n * baseline occupies no slot, so every row is new — which is what `classifyDiffRow` already tells the\n * confirm step for a null baseline, and what an empty snapshot already told this surface.\n */\nfunction collectInstalledSkillSlots(\n installedSkillConfigs: SkillConfig[] | null,\n): ReadonlySet<string> {\n if (!installedSkillConfigs) return new Set();\n return new Set(installedSkillConfigs.map((sc) => skillSlotKey(sc.id, sc.scope)));\n}\n\n/**\n * D-258: the `{ added: true }` fragment for a row whose SLOT is new this session, `{}` otherwise —\n * spread into the row so the Sources tab flags it with the info panel's added-diff marker (`+`).\n * Keying on the id alone would miss a skill adopted at project scope while it stays installed\n * globally: the id is old, but the project slot is new — which is exactly how `classifyDiffRow`\n * classifies the same change on the confirm step. Never gated on `isEditingFromGlobalScope`: an\n * addition is an addition regardless of edit context.\n */\nfunction addedSlotFlag(\n installedSkillSlots: ReadonlySet<string>,\n id: SkillId,\n scope: SkillScope | undefined,\n): { added?: true } {\n return installedSkillSlots.has(skillSlotKey(id, scope)) ? {} : { added: true };\n}\n\n/** Copy of the options with the persisted source's mode selected — for rows pinned to it. */\nfunction withSelectedMode(options: SourceOption[], source: string | undefined): SourceOption[] {\n const mode = installModeOfSource(source);\n return options.map((option) => ({ ...option, selected: option.mode === mode }));\n}\n\n/**\n * The per-skill inputs every source row shares: the canonical id, the live config entry driving\n * scope/lock classification, and the source options with the effective source preselected. A\n * pending-removal row re-pins that selection to its persisted source (`withSelectedSource`).\n */\nfunction resolveSkillRowInputs(\n id: SkillId,\n skillConfigs: SkillConfig[],\n): { skillId: SkillId; configEntry: SkillConfig | undefined; options: SourceOption[] } {\n const skill = getSkillById(id);\n const configEntry = skillConfigs.find((sc) => sc.id === skill.id);\n const selectedSource = resolveEffectiveSource(\n configEntry?.source,\n skill.activeSource?.name,\n primarySourceName(skill),\n );\n return {\n skillId: skill.id,\n configEntry,\n options: buildInstallModeOptions(installModeOfSource(selectedSource)),\n };\n}\n\ntype SourceRowContext = {\n configEntry: SkillConfig | undefined;\n installedSkillConfigs: SkillConfig[] | null;\n isEditingFromGlobalScope: boolean;\n /** D-258: `(id, scope)` slots the snapshot occupies — the baseline each row's `+` derives from. */\n installedSkillSlots: ReadonlySet<string>;\n};\n\n/**\n * D-257: the inert row for a snapshot slot this session emptied. Carries the PERSISTED scope and\n * source so the row stays visible and shows what saving removes. Deliberately NOT readOnly: that\n * renders a lock, which reads as \"installed globally\" rather than \"about to be removed\". Carries no\n * added flag either — the slot it renders comes FROM the snapshot, so it can never be new this\n * session.\n */\nfunction toPendingRemovalRow(\n skillId: SkillId,\n options: SourceOption[],\n removedInstalledEntry: SkillConfig,\n): SourceRow {\n return {\n skillId,\n options: withSelectedMode(options, removedInstalledEntry.source),\n scope: removedInstalledEntry.scope,\n disabled: true,\n };\n}\n\n/**\n * True when an already-emitted row renders the snapshot entry's `(id, scope)` slot, which makes a\n * pending-removal row for it a duplicate rather than a loss.\n *\n * The one shape that reaches this: a global install adopted at project scope WITHOUT a tombstone\n * (`[G]` snapshot, `[P]` live — the historical no-tombstone dual-scope shape `computeScopeDiff`\n * calls an inherited global). Its global slot is unoccupied in the live config, yet the locked\n * global row already renders it FROM the snapshot, so the install is inherited, not removed.\n */\nfunction isSlotAlreadyRendered(rows: SourceRow[], removedInstalledEntry: SkillConfig): boolean {\n const slot = skillSlotKey(removedInstalledEntry.id, removedInstalledEntry.scope);\n return rows.some((row) => skillSlotKey(row.skillId, row.scope) === slot);\n}\n\n/**\n * The locked row for a global install this project may not change from here — the lock reads\n * \"installed globally, not yours to edit at project scope\". Its source is pinned to the installed\n * one, and its added flag derives from the GLOBAL slot, so an install the project merely inherits\n * stays a plain lock rather than reading as new.\n */\nfunction toLockedGlobalRow(\n skillId: SkillId,\n options: SourceOption[],\n installedSource: string | undefined,\n installedSkillSlots: ReadonlySet<string>,\n): SourceRow {\n return {\n skillId,\n options: withSelectedMode(options, installedSource),\n scope: \"global\" as const,\n readOnly: true,\n ...addedSlotFlag(installedSkillSlots, skillId, \"global\"),\n };\n}\n\n/**\n * Classifies one skill's LIVE config entry into its source-grid rows: a locked global row for\n * excluded-global entries, locked global + editable project rows for skills re-scoped\n * global→project this session, or a single row otherwise. Pending-removal rows are not derived\n * here — they belong to snapshot slots with no live entry left (see `toPendingRemovalRow`).\n */\nfunction classifySkillSourceRows(\n skillId: SkillId,\n options: SourceOption[],\n context: SourceRowContext,\n): SourceRow[] {\n const { configEntry, installedSkillConfigs, isEditingFromGlobalScope, installedSkillSlots } =\n context;\n const installedGlobalConfig = findGlobalActive(installedSkillConfigs ?? [], skillId);\n\n const isExcludedGlobal = configEntry?.excluded && configEntry.scope === \"global\";\n if (isExcludedGlobal && !isEditingFromGlobalScope) {\n return [\n toLockedGlobalRow(\n skillId,\n options,\n installedGlobalConfig?.source ?? configEntry.source,\n installedSkillSlots,\n ),\n ];\n }\n\n if (!isEditingFromGlobalScope && installedGlobalConfig && configEntry?.scope === \"project\") {\n // Skill toggled from global to project — emit locked global copy + editable project copy. Each\n // half derives its own added flag from its own slot, so the `+` lands on the newly occupied\n // PROJECT row while the still-installed global row stays a plain lock.\n return [\n toLockedGlobalRow(skillId, options, installedGlobalConfig.source, installedSkillSlots),\n {\n skillId,\n options,\n scope: \"project\" as const,\n ...addedSlotFlag(installedSkillSlots, skillId, \"project\"),\n },\n ];\n }\n\n const readOnly = !isEditingFromGlobalScope && !!installedGlobalConfig;\n return [\n {\n skillId,\n options,\n ...(configEntry?.scope !== undefined && { scope: configEntry.scope }),\n ...(readOnly ? { readOnly: true as const } : {}),\n ...addedSlotFlag(installedSkillSlots, skillId, configEntry?.scope),\n },\n ];\n}\n\n/**\n * Visual grouping order in source-grid: global readOnly, global editable, then project. A\n * pending-removal (disabled) row is NOT a separate tier — it sorts by its own scope so it renders\n * inline with the other rows of that scope, matching the info panel, which shows removals in place\n * rather than in a dedicated section. Its trailing position within a scope section falls out of the\n * removal rows being appended after every live row, which the stable sort preserves.\n */\nfunction sourceRowSortTier(row: SourceRow): number {\n if (row.scope === \"global\" && row.readOnly) return 0;\n if (row.scope === \"global\") return 1;\n return 2;\n}\n\n/**\n * Wizard step identifiers for the multi-step init/edit flow.\n *\n * Progression: stack -> domains -> build -> sources -> agents -> confirm\n * The \"stack\" step shows all stacks + \"Start from scratch\" in a unified list.\n * The \"domains\" step shows domain selection (web, api, cli, mobile, shared).\n * Navigation is tracked via the `history` stack for goBack() support.\n */\nexport type WizardStep =\n | \"stack\" // Select stack or \"Start from scratch\"\n | \"domains\" // Select domains to configure\n | \"build\" // CategoryGrid for technology selection\n | \"sources\" // Choose skill sources (recommended vs custom)\n | \"agents\" // Select which agents to compile\n | \"confirm\"; // Final confirmation\n\n/** How the user builds their config: from a pre-built stack, or skill-by-skill. */\ntype WizardApproach = \"stack\" | \"scratch\";\n\n/** For the stack approach: use the stack's defaults, or customize its skills. */\ntype StackAction = \"defaults\" | \"customize\";\n\nexport const WIZARD_STEP_ORDER = [\n \"stack\",\n \"domains\",\n \"build\",\n \"sources\",\n \"agents\",\n \"confirm\",\n] as const satisfies readonly WizardStep[];\n\n/** Whether the loaded source offers any stack to choose between. */\nfunction offersStacks(): boolean {\n return matrix.suggestedStacks.length > 0;\n}\n\n/**\n * The steps this session's wizard actually runs, in order.\n *\n * `WIZARD_STEP_ORDER` is every step the wizard has; this is the ones it has\n * THIS time. The stack step exists only where there is a stack to choose, so a\n * source that ships none — the built-in catalogue standing in for the default\n * public marketplace alone — drops it: hydration opens past it (see\n * {@link FIRST_STEP_WITHOUT_STACKS}) and the tab bar is drawn without it. One\n * answer to \"does this run have a stack step\", so the tabs cannot advertise a\n * step the flow does not have.\n */\nexport function getActiveStepFlow(): WizardStep[] {\n if (offersStacks()) return [...WIZARD_STEP_ORDER];\n return WIZARD_STEP_ORDER.filter((step) => step !== \"stack\");\n}\n\n/** The flow's steps before the active one; none when the step is outside the flow. */\nfunction stepsBefore(flow: WizardStep[], step: WizardStep): WizardStep[] {\n const index = flow.indexOf(step);\n return index < 0 ? [] : flow.slice(0, index);\n}\n\n/**\n * Wizard store state and actions.\n *\n * The store uses a composition pattern: small, focused actions that each mutate\n * one or two state fields. Wizard step components compose these actions to build\n * up the full selection state incrementally (domains -> categories -> skills -> sources).\n *\n * State flow: stack/scratch selection -> domain selection -> per-domain skill\n * selection (build step) -> source customization -> agent selection -> confirmation.\n */\nexport type WizardState = {\n step: WizardStep;\n\n approach: WizardApproach | null;\n selectedStackId: string | null;\n stackAction: StackAction | null;\n\n selectedDomains: Domain[];\n\n currentDomainIndex: number;\n domainSelections: DomainSelections;\n /** Snapshot of stack-provided domain selections for restoration on domain re-toggle */\n _stackDomainSelections: DomainSelections | null;\n\n showLabels: boolean;\n\n skillConfigs: SkillConfig[];\n focusedSkillId: SkillId | null;\n\n /**\n * Skill ids from the saved config that could NOT be resolved against the currently-loaded\n * source matrix this session (`populateFromSkillIds` skipped them, warning per skill). The\n * wizard cannot represent them, so they are absent from its result and the merge removes them\n * like any other absent entry — this list is what lets `edit` name each one and say why it went\n * rather than dropping it in silence (CLI-450).\n */\n unresolvableSkillIds: SkillId[];\n\n showInfo: boolean;\n\n selectedAgents: AgentName[];\n agentConfigs: AgentScopeConfig[];\n focusedAgentId: AgentName | null;\n\n /** Snapshot of configs that were installed before the wizard opened, used for diff rendering */\n installedSkillConfigs: SkillConfig[] | null;\n installedAgentConfigs: AgentScopeConfig[] | null;\n\n /** True when running init (first-time setup), false when editing an existing installation */\n isInitMode: boolean;\n\n /** When true, scope toggling is disabled (editing from ~/.claude/ with no project to move items to) */\n isEditingFromGlobalScope: boolean;\n\n /** Temporary toast message shown in the wizard (auto-cleared after timeout) */\n toastMessage: string | null;\n\n /** Global skill configs to pre-select when a stack or scratch is chosen in init. Set by use-wizard-initialization. */\n globalPreselections: SkillConfig[] | null;\n\n /** Global agent preselections to restore after selectStack wipes selectedAgents/agentConfigs. Set by use-wizard-initialization. */\n globalAgentPreselections: { agents: AgentName[]; configs: AgentScopeConfig[] } | null;\n\n history: WizardStep[];\n\n /**\n * Navigate to a wizard step, pushing the current step onto history.\n * @param step - Target step to navigate to\n *\n * Side effects: sets `step`, appends previous step to `history`\n */\n setStep: (step: WizardStep) => void;\n /**\n * Set the wizard approach (stack-based or build-from-scratch).\n * @param approach - \"stack\" to use a pre-built template, \"scratch\" to select skills manually, null to reset\n *\n * Side effects: sets `approach`\n */\n setApproach: (approach: WizardApproach | null) => void;\n /**\n * Select a stack by ID, or null to deselect.\n * @param stackId - Stack identifier from suggestedStacks, or null to clear\n *\n * Side effects: sets `selectedStackId`\n */\n selectStack: (stackId: string | null) => void;\n /**\n * Set how to apply the selected stack.\n * @param action - \"defaults\" to use stack as-is, \"customize\" to enter the build step\n *\n * Side effects: sets `stackAction`\n */\n setStackAction: (action: StackAction) => void;\n /**\n * Prepare a build with no stack behind it — what \"Start from scratch\" means.\n *\n * Two surfaces arrive here and must agree on what it is: the stack step's\n * scratch row, and the hydration that opens the wizard past a stack step the\n * loaded source gave nothing to show on.\n *\n * Side effects: clears every stack-derived selection (via `selectStack(null)`),\n * sets `approach` to \"scratch\", restores `selectedAgents` / `agentConfigs` from\n * `globalAgentPreselections`, populates selections from `globalPreselections`,\n * and selects the scratch domains. Does NOT navigate — the caller owns the step.\n */\n startFromScratch: () => void;\n /**\n * Pre-populate domainSelections from a flat list of installed skill IDs.\n *\n * Used by `npx agents-inc edit` to restore wizard state from existing project config.\n * Looks up each skill's category and domain, warns for unresolvable skills.\n *\n * @param skillIds - Flat array of currently installed skill IDs\n * @param skills - Skill lookup providing category and displayName per skill ID\n * @param categories - Category definitions used to resolve category -> domain mapping\n *\n * Side effects: sets `domainSelections`, sets `selectedDomains` to domains found in the provided skill IDs\n */\n populateFromSkillIds: (skillIds: SkillId[], savedConfigs?: SkillConfig[]) => void;\n /**\n * Toggle a domain on or off in the selectedDomains list.\n * @param domain - Domain to toggle\n *\n * Side effects: adds or removes from `selectedDomains`\n */\n toggleDomain: (domain: Domain) => void;\n /**\n * Toggle a skill selection within a domain's category.\n *\n * When exclusive is true (radio behavior), selecting a new skill replaces any\n * existing selection in that category. When false (checkbox behavior),\n * the skill is added to or removed from the selection array.\n *\n * @param domain - Domain containing the category\n * @param category - Category within the domain\n * @param technology - Skill ID to toggle\n * @param exclusive - If true, only one skill can be selected per category (radio)\n *\n * Side effects: updates `domainSelections[domain][category]`\n */\n toggleTechnology: (\n domain: Domain,\n category: Category,\n technology: SkillId,\n exclusive: boolean,\n ) => void;\n /**\n * Advance to the next domain in the build step.\n * @returns true if advanced, false if already at the last domain\n *\n * Side effects: increments `currentDomainIndex`\n */\n nextDomain: () => boolean;\n /**\n * Go back to the previous domain in the build step.\n * @returns true if moved back, false if already at the first domain\n *\n * Side effects: decrements `currentDomainIndex`\n */\n prevDomain: () => boolean;\n /**\n * Set the current domain index directly.\n * @param index - Index to set (0-based, must be within selectedDomains range)\n *\n * Side effects: sets `currentDomainIndex` if index is valid, otherwise no-op\n */\n setCurrentDomainIndex: (index: number) => void;\n /** Toggle compatibility label visibility on skill tags in the build step grid. */\n toggleShowLabels: () => void;\n /**\n * Derive the install mode from skillConfigs source values.\n * If all skills use \"eject\" source, returns \"eject\". If all use non-eject, returns \"plugin\".\n * If mixed, returns \"mixed\". Returns \"eject\" when no skills are configured.\n */\n deriveInstallMode: () => InstallMode;\n /**\n * Toggle the scope of a specific skill between \"project\" and \"global\".\n * @param skillId - Skill to toggle scope for\n *\n * Side effects: updates `skillConfigs` entry for the skill\n */\n toggleSkillScope: (skillId: SkillId) => void;\n /**\n * Set the currently focused skill ID in the build step (for S hotkey).\n * @param id - Skill ID to focus, or null to clear\n *\n * Side effects: sets `focusedSkillId`\n */\n setFocusedSkillId: (id: SkillId | null) => void;\n /**\n * Seed `focusedSkillId` to the skill the build-step grid focuses first for the\n * active domain (row 0, col 0 of the filtered category rows). Runs synchronously\n * at build-step entry and on every domain change so the `s` scope hotkey — which\n * reads `focusedSkillId` — always resolves the visually-focused skill, with no\n * dependency on CategoryGrid's post-mount effect.\n *\n * Side effects: sets `focusedSkillId` (null when the active domain has no skills)\n */\n seedFocusedSkillForActiveDomain: () => void;\n /**\n * Set how a specific skill is installed: the project's own copy, or the marketplace plugin.\n * @param skillId - Skill to configure the install mode for\n * @param mode - `eject` for a local copy, `plugin` for the one marketplace\n * @param scope - Acting scope from the Sources row: only the active entry at this scope is\n * updated; a masked global tombstone for the same id keeps its source.\n *\n * Side effects: updates the active `skillConfigs` entry for the skill at `scope`, writing the\n * `source` value the mode resolves to. No-op with a warning on an empty skill id.\n */\n setInstallMode: (\n skillId: SkillId,\n mode: Exclude<InstallMode, \"mixed\">,\n scope: SkillScope | undefined,\n ) => void;\n /** Toggle the info overlay (selected skills and agents). */\n toggleInfo: () => void;\n /** Set a temporary toast message, or null to clear it. */\n setToastMessage: (message: string | null) => void;\n /**\n * Navigate to the previous wizard step using the history stack.\n * No-op when history is empty (e.g., edit flow starting at a mid-wizard step).\n *\n * Side effects: pops from `history`, sets `step` to the popped value\n */\n goBack: () => void;\n /**\n * Toggle an agent on or off in the selectedAgents list.\n * @param agent - Agent name to toggle\n *\n * Side effects: adds or removes from `selectedAgents`, syncs `agentConfigs`\n */\n toggleAgent: (agent: AgentName) => void;\n /**\n * Toggle the scope of a specific agent between \"project\" and \"global\".\n * @param agentName - Agent to toggle scope for\n *\n * Side effects: updates `agentConfigs` entry for the agent\n */\n toggleAgentScope: (agentName: AgentName) => void;\n /**\n * Set the currently focused agent ID in the agents step (for S hotkey).\n * @param id - Agent name to focus, or null to clear\n *\n * Side effects: sets `focusedAgentId`\n */\n setFocusedAgentId: (id: AgentName | null) => void;\n /**\n * Preselect agents based on selected domains from the first wizard step.\n * Matches domains against DOMAIN_AGENTS mapping.\n * Optional agents (meta/pattern) are excluded.\n *\n * The from-scratch path only: with a stack chosen, its declared roster stands and\n * this is a no-op. Selecting a stack is the statement of which sub-agents an\n * installation gets, and the user edits that roster in the agents step.\n *\n * Side effects: replaces `selectedAgents` with computed preselection, unless a\n * stack is selected\n */\n preselectAgentsFromDomains: () => void;\n /**\n * Preselect agents for a chosen stack: merges the stack's agent keys with global\n * agent preselections, preserving dual-scope tombstones. Parallels\n * populateFromSkillIds on the skill path.\n *\n * This is the roster the install gets — nothing downstream re-derives it.\n *\n * Side effects: replaces `selectedAgents` and `agentConfigs` with the merged result\n */\n preselectAgentsFromStack: (stackAgents: AgentName[]) => void;\n /** Reset all wizard state to initial values. */\n reset: () => void;\n\n /**\n * Collect all selected skill IDs across all domains and categories.\n * @returns Flat array of every selected SkillId (may contain duplicates if shared across domains)\n */\n getAllSelectedTechnologies: () => SkillId[];\n /**\n * Group selected skill IDs by domain.\n * @returns Partial record mapping each domain with selections to its skill ID array\n */\n getSelectedTechnologiesPerDomain: () => Partial<Record<Domain, SkillId[]>>;\n /**\n * Get the domain currently visible in the build step.\n * @returns The domain at currentDomainIndex, or null if no domains are selected\n */\n getCurrentDomain: () => Domain | null;\n /**\n * Count total selected technologies across all domains.\n * @returns Number of selected skill IDs\n */\n getTechnologyCount: () => number;\n /**\n * Compute which wizard steps are completed and which are skipped.\n * Used by WizardTabs to render step progress indicators. Both lists are read\n * against {@link getActiveStepFlow} — a step this run never has cannot be one\n * it completed.\n * @returns Object with completedSteps and skippedSteps string arrays\n */\n getStepProgress: () => { completedSteps: WizardStep[]; skippedSteps: WizardStep[] };\n /** @returns true if there is a next domain after the current one */\n canGoToNextDomain: () => boolean;\n /** @returns true if there is a previous domain before the current one */\n canGoToPreviousDomain: () => boolean;\n /** Set all selected skills to \"eject\" source. */\n setAllSourcesEject: () => void;\n /** Set all selected skills to install from the one marketplace. */\n setAllSourcesPlugin: () => void;\n\n /**\n * Build the source selection rows for the sources step UI.\n *\n * For each selected technology, resolves the canonical skill ID and marks which of the two\n * install modes its persisted `source` stands for.\n *\n * @returns Array of row objects, one per selected technology, each containing:\n * - `skillId` - Canonical resolved skill ID\n * - `options` - The two install-mode cells, with the current one selected\n */\n buildSourceRows: () => SourceRow[];\n};\n\n/** State-only fields from WizardState (excludes actions/getters). Used to type createInitialState(). */\ntype WizardStateData = Pick<\n WizardState,\n | \"step\"\n | \"approach\"\n | \"selectedStackId\"\n | \"stackAction\"\n | \"selectedDomains\"\n | \"currentDomainIndex\"\n | \"domainSelections\"\n | \"_stackDomainSelections\"\n | \"showLabels\"\n | \"skillConfigs\"\n | \"focusedSkillId\"\n | \"unresolvableSkillIds\"\n | \"showInfo\"\n | \"selectedAgents\"\n | \"agentConfigs\"\n | \"focusedAgentId\"\n | \"installedSkillConfigs\"\n | \"installedAgentConfigs\"\n | \"isInitMode\"\n | \"isEditingFromGlobalScope\"\n | \"toastMessage\"\n | \"globalPreselections\"\n | \"globalAgentPreselections\"\n | \"history\"\n>;\n\nexport const createInitialState = (overrides?: Partial<WizardStateData>): WizardStateData => ({\n step: \"stack\",\n approach: null,\n selectedStackId: null,\n stackAction: null,\n selectedDomains: [],\n currentDomainIndex: 0,\n domainSelections: {},\n /** Snapshot of domainSelections from populateFromSkillIds, used to restore on domain re-toggle */\n _stackDomainSelections: null,\n showLabels: false,\n skillConfigs: [],\n focusedSkillId: null,\n unresolvableSkillIds: [],\n showInfo: false,\n selectedAgents: [],\n agentConfigs: [],\n focusedAgentId: null,\n installedSkillConfigs: null,\n installedAgentConfigs: null,\n isInitMode: false,\n isEditingFromGlobalScope: false,\n toastMessage: null,\n globalPreselections: null,\n globalAgentPreselections: null,\n history: [],\n ...overrides,\n});\n\nexport const useWizardStore = create<WizardState>((set, get) => ({\n ...createInitialState(),\n\n setStep: (step) => {\n set((state) => ({\n step,\n history: [...state.history, state.step],\n }));\n if (step === \"build\") get().seedFocusedSkillForActiveDomain();\n },\n\n setApproach: (approach) => set({ approach }),\n\n selectStack: (stackId) =>\n set({\n selectedStackId: stackId,\n domainSelections: {},\n _stackDomainSelections: null,\n selectedDomains: [],\n skillConfigs: [],\n selectedAgents: [],\n agentConfigs: [],\n currentDomainIndex: 0,\n stackAction: null,\n }),\n\n setStackAction: (action) => set({ stackAction: action }),\n\n startFromScratch: () => {\n // selectStack(null) wipes the agent selections along with the stack's, so the\n // global ones are put back rather than left as collateral.\n get().selectStack(null);\n get().setApproach(\"scratch\");\n\n const { globalAgentPreselections, globalPreselections } = get();\n if (globalAgentPreselections) {\n set({\n selectedAgents: globalAgentPreselections.agents,\n agentConfigs: globalAgentPreselections.configs,\n });\n }\n if (globalPreselections?.length) {\n get().populateFromSkillIds(\n globalPreselections.map((skill) => skill.id),\n globalPreselections,\n );\n }\n\n // Additive: a scratch domain the preselections already brought stays on.\n for (const domain of DEFAULT_SCRATCH_DOMAINS) {\n if (!get().selectedDomains.includes(domain)) get().toggleDomain(domain);\n }\n },\n\n populateFromSkillIds: (skillIds, savedConfigs) =>\n set(() => {\n const domainSelections: DomainSelections = {};\n const resolvedSkillIds: SkillId[] = [];\n const unresolvableSkillIds: SkillId[] = [];\n\n for (const skillId of skillIds) {\n const resolved = resolveSkillForPopulation(skillId);\n if (!resolved) {\n unresolvableSkillIds.push(skillId);\n continue;\n }\n\n const { domain, subcat, techId } = resolved;\n if (addToDomainSelections(domainSelections, domain, subcat, techId)) {\n resolvedSkillIds.push(techId);\n }\n }\n\n const selectedDomains = orderDomains(typedKeys<Domain>(domainSelections));\n\n const skillConfigs: SkillConfig[] = resolvedSkillIds.map((id) =>\n buildSkillConfigForId(id, savedConfigs),\n );\n\n // Preserve excluded entries so they flow through to wizard result.\n // D-223: allow an excluded tombstone to coexist with an active entry for the\n // same skill id at a different scope (render layer computes secondaryScope\n // from the pair).\n const excludedConfigs = collectTombstones(savedConfigs ?? []);\n\n return {\n domainSelections,\n _stackDomainSelections: structuredClone(domainSelections),\n selectedDomains,\n skillConfigs: [...skillConfigs, ...excludedConfigs],\n unresolvableSkillIds,\n };\n }),\n\n toggleDomain: (domain) =>\n set((state) => {\n const isSelected = state.selectedDomains.includes(domain);\n if (isSelected) {\n const { [domain]: _removed, ...remainingSelections } = state.domainSelections;\n const removedSkillIds = _removed\n ? collectSkillIdsFromSelections(_removed)\n : new Set<SkillId>();\n\n return {\n selectedDomains: state.selectedDomains.filter((d) => d !== domain),\n domainSelections: remainingSelections,\n // Global-scope edit: no overlay, so deselecting a domain uninstalls its skills cleanly\n // rather than tombstoning them in the global config (D-233 Scenario C).\n skillConfigs: applySkillRemoval(\n state.skillConfigs,\n removedSkillIds,\n state.isEditingFromGlobalScope ? null : state.installedSkillConfigs,\n ),\n };\n }\n\n // Restore stack selections for this domain if a stack snapshot exists\n const stackSelections = state._stackDomainSelections?.[domain];\n if (stackSelections) {\n const restoredSkillIds = [...collectSkillIdsFromSelections(stackSelections)];\n\n return {\n selectedDomains: orderDomains([...state.selectedDomains, domain]),\n domainSelections: {\n ...state.domainSelections,\n [domain]: structuredClone(stackSelections),\n },\n skillConfigs: restoreSkillConfigs(state.skillConfigs, restoredSkillIds),\n };\n }\n\n return {\n selectedDomains: orderDomains([...state.selectedDomains, domain]),\n };\n }),\n\n toggleTechnology: (domain, category, technology, exclusive) =>\n set((state) => {\n const installed = state.installedSkillConfigs ?? [];\n const currentSelections = state.domainSelections[domain]?.[category] || [];\n const isSelected = currentSelections.includes(technology);\n\n // Block a GLOBAL-OWNED entry from being changed at project scope. On a SELECT only the\n // active-global arm applies (an inherited-global row is read-only in both directions); the\n // stale-collapse arm is a deselect guard, kept behind isSelected so the re-select restore\n // path (reconcileSkillConfigs rebuilds `[P][G]`) still runs. The project half of a\n // persisted `[P][G]` pair is project-owned and passes both ways.\n const isGlobalLocked = isSelected\n ? isGloballyLockedSkill(installed, state.skillConfigs, technology)\n : hasGlobalActive(installed, technology);\n if (isGlobalLocked && !state.isEditingFromGlobalScope) {\n return { toastMessage: TOAST_MESSAGES.GLOBAL_SKILLS_LOCKED };\n }\n\n if (isSelected) {\n const categoryDef = matrix.categories[category];\n if (categoryDef?.exclusive && categoryDef.required) {\n const categorySkillCount = typedValues(matrix.skills).filter(\n (s) => s.category === category,\n ).length;\n if (categorySkillCount <= 1) {\n return { toastMessage: TOAST_MESSAGES.ONLY_SKILL_IN_CATEGORY };\n }\n }\n }\n\n // In exclusive mode, selecting a new skill replaces the current one. Block if that would\n // implicitly drop a skill this scope may not drop THIS WAY: a radio swap must never\n // tombstone a global install, and never unmask one either.\n if (exclusive && !isSelected) {\n const wouldDropLockedSkill = currentSelections.some((selectedId) =>\n blocksExclusiveSwap(installed, state.skillConfigs, selectedId),\n );\n if (wouldDropLockedSkill && !state.isEditingFromGlobalScope) {\n return { toastMessage: TOAST_MESSAGES.GLOBAL_SKILLS_LOCKED };\n }\n }\n\n let newSelections: SkillId[];\n if (exclusive) {\n newSelections = isSelected ? [] : [technology];\n } else {\n newSelections = isSelected\n ? currentSelections.filter((t) => t !== technology)\n : [...currentSelections, technology];\n }\n\n const removed = currentSelections.filter((id) => !newSelections.includes(id));\n const added = newSelections.filter((id) => !currentSelections.includes(id));\n\n const skillConfigs = reconcileSkillConfigs(\n state.skillConfigs,\n added,\n removed,\n state.installedSkillConfigs,\n state.isEditingFromGlobalScope,\n );\n\n // A dual-scope deselect collapses to a single active inherited-global entry, so the\n // skill is still genuinely active. Keep it in the domain selection (mirroring what a\n // save-and-reopen re-derives via populateFromSkillIds) instead of dropping it purely\n // because it left the pre-removal selection array. A genuine full removal leaves no\n // active entry and still drops normally.\n const stillActiveAfterRemoval =\n isSelected &&\n !newSelections.includes(technology) &&\n (hasProjectActive(skillConfigs, technology) || hasGlobalActive(skillConfigs, technology));\n const resolvedSelections = stillActiveAfterRemoval\n ? [...newSelections, technology]\n : newSelections;\n\n return {\n skillConfigs,\n domainSelections: {\n ...state.domainSelections,\n [domain]: {\n ...state.domainSelections[domain],\n [category]: resolvedSelections,\n },\n },\n };\n }),\n\n nextDomain: () => {\n const state = get();\n if (state.currentDomainIndex < state.selectedDomains.length - 1) {\n set({\n currentDomainIndex: state.currentDomainIndex + 1,\n });\n get().seedFocusedSkillForActiveDomain();\n return true;\n }\n return false;\n },\n\n prevDomain: () => {\n const state = get();\n if (state.currentDomainIndex > 0) {\n set({\n currentDomainIndex: state.currentDomainIndex - 1,\n });\n get().seedFocusedSkillForActiveDomain();\n return true;\n }\n return false;\n },\n\n setCurrentDomainIndex: (index) => {\n const state = get();\n if (index >= 0 && index < state.selectedDomains.length) {\n set({ currentDomainIndex: index });\n get().seedFocusedSkillForActiveDomain();\n }\n },\n\n toggleShowLabels: () => set((state) => ({ showLabels: !state.showLabels })),\n\n deriveInstallMode: (): InstallMode => {\n const { skillConfigs } = get();\n return sharedDeriveInstallMode(skillConfigs);\n },\n\n toggleSkillScope: (skillId) =>\n set((state) => {\n if (state.isEditingFromGlobalScope) return state;\n\n const config = state.skillConfigs.find((sc) => sc.id === skillId && !sc.excluded);\n if (!config) return state;\n\n // Guard: block project eject → global when global eject already exists (would overwrite)\n if (wouldOverwriteGlobalEject(config, state.skillConfigs, state.installedSkillConfigs)) {\n return { toastMessage: TOAST_MESSAGES.ALREADY_EJECTED_AT_GLOBAL };\n }\n\n // A real global install exists when the snapshot carries EITHER an active global entry\n // or a global tombstone: a tombstone means the skill IS installed globally and this\n // project was overriding it. Both must re-create the tombstone on a G→P toggle, so an\n // in-session collapse→`s` restores a genuine `[P][G]` pair rather than a bare `[P]` that\n // loses the still-real global install. (Matches the agent-path derivation, which already\n // counts tombstones.)\n const wasInstalledGlobally =\n state.installedSkillConfigs?.some((sc) => sc.id === skillId && sc.scope === \"global\") ??\n false;\n const newScope = config.scope === \"project\" ? (\"global\" as const) : (\"project\" as const);\n\n const rescoped = state.skillConfigs.map((sc) =>\n sc.id === skillId && !sc.excluded ? { ...sc, scope: newScope } : sc,\n );\n\n if (newScope === \"project\") {\n // Moving global → project: add the excluded global tombstone if not already there.\n // Gated on wasInstalledGlobally so fresh init toggles don't create spurious tombstones.\n const needsTombstone =\n wasInstalledGlobally && !rescoped.some((sc) => sc.id === skillId && sc.excluded);\n return {\n skillConfigs: needsTombstone\n ? [\n ...rescoped,\n { id: skillId, scope: \"global\" as const, excluded: true, source: config.source },\n ]\n : rescoped,\n };\n }\n\n // Moving project → global: always drop any excluded global tombstone for this id.\n // An active entry at global scope supersedes any tombstone at the same scope — the\n // invariant \"no active + tombstone at the same (id, scope)\" must hold. Unconditional\n // removal (not gated on wasInstalledGlobally) heals the D-224 case where the prior\n // G→P produced a tombstone that installedSkillConfigs-derived wasInstalledGlobally\n // cannot see (because its `!sc.excluded` filter ignores the tombstone itself).\n return { skillConfigs: rescoped.filter((sc) => !(sc.id === skillId && sc.excluded)) };\n }),\n\n setFocusedSkillId: (id) => set({ focusedSkillId: id }),\n\n seedFocusedSkillForActiveDomain: () => {\n const state = get();\n // Mirror the build-step renderer's domain fallback (use-build-step-props.ts):\n // when no domain is selected the grid still renders FALLBACK_DOMAIN and\n // highlights a cell, so the seed must resolve that same domain — never null\n // while a cell is visibly focused — or the cold `s` toggle no-ops on a desync.\n const domain = state.getCurrentDomain() ?? FALLBACK_DOMAIN;\n const categories = buildCategoriesForDomain(\n domain,\n state.getAllSelectedTechnologies(),\n undefined,\n state.skillConfigs,\n );\n set({ focusedSkillId: categories[0]?.options[0]?.id ?? null });\n },\n\n setInstallMode: (skillId, mode, scope) =>\n set((state) => {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- SkillId is a string union with no empty member, so the type rules this out; the guard is for a runtime caller handing over an empty id\n if (!skillId) {\n warn(\"Ignoring setInstallMode call with empty skillId\");\n return state;\n }\n const source = sourceForInstallMode(getSkillById(skillId), mode);\n return {\n skillConfigs: withActiveEntrySource(state.skillConfigs, skillId, scope, source),\n };\n }),\n\n toggleInfo: () => set((state) => ({ showInfo: !state.showInfo })),\n\n setToastMessage: (message) => set({ toastMessage: message }),\n\n goBack: () =>\n set((state) => {\n if (state.history.length === 0) return state;\n const history = [...state.history];\n const previousStep = history.pop()!;\n return {\n step: previousStep,\n history,\n };\n }),\n\n toggleAgent: (agent) =>\n set((state) => {\n const installed = state.installedAgentConfigs ?? [];\n\n // A live dual-scope `[P][G]` row is locked to `s`: the selection key is inert on it and\n // emits the same global-locked toast an inherited-global row shows. Mirrors the skill-side\n // dual-scope arm in toggleTechnology — `s` alone collapses and restores the pair.\n const isDualScopePairRow =\n !state.isEditingFromGlobalScope && isDualScopeAgentPair(state.agentConfigs, agent);\n if (isDualScopePairRow) {\n return { toastMessage: TOAST_MESSAGES.GLOBAL_AGENTS_LOCKED };\n }\n\n const isInheritedGlobalReselect =\n !state.isEditingFromGlobalScope &&\n agentHasGlobalActive(state.agentConfigs, agent) &&\n !agentHasProjectActive(state.agentConfigs, agent) &&\n !state.selectedAgents.includes(agent) &&\n agentHasGlobalTombstone(installed, agent);\n if (isInheritedGlobalReselect) {\n return restoreDualScopeAgent(state.selectedAgents, state.agentConfigs, agent);\n }\n\n // Block a globally-installed agent from being changed at project scope. Mirrors the\n // skill-path guard in toggleTechnology: fire on an active global entry in the snapshot,\n // or on a snapshot tombstone paired with a LIVE plain active global entry (the stale\n // state a persisted `[P][G]` reaches after an in-session collapse). A freshly-added\n // global agent (absent from the snapshot) stays freely deselectable.\n const isActiveGlobal =\n agentHasGlobalActive(installed, agent) ||\n (agentHasGlobalTombstone(installed, agent) &&\n agentHasGlobalActive(state.agentConfigs, agent));\n if (isActiveGlobal && !state.isEditingFromGlobalScope) {\n return { toastMessage: TOAST_MESSAGES.GLOBAL_AGENTS_LOCKED };\n }\n\n const isInList = state.selectedAgents.includes(agent);\n const hasExcludedTombstone = state.agentConfigs.some(\n (ac) => ac.name === agent && ac.excluded,\n );\n // An agent is effectively selected only if it's in the list WITHOUT an excluded tombstone.\n // When it has an excluded tombstone, it's visually \"off\" and toggling means \"re-enable\".\n const isSelected = isInList && !hasExcludedTombstone;\n\n return {\n selectedAgents: nextSelectedAgents(state.selectedAgents, agent, { isSelected, isInList }),\n agentConfigs: applyAgentToggle(state.agentConfigs, agent, isSelected),\n };\n }),\n\n toggleAgentScope: (agentName) =>\n set((state) => {\n if (state.isEditingFromGlobalScope) return state;\n\n const config = state.agentConfigs.find((ac) => ac.name === agentName && !ac.excluded);\n if (!config) return state;\n\n // Counts a global tombstone as \"installed globally\" (a tombstone means a real global\n // install this project overrides), so an in-session collapse→`s` restores a genuine\n // `[P][G]` pair.\n const wasInstalledGlobally =\n state.installedAgentConfigs?.some((ac) => ac.name === agentName && ac.scope === \"global\") ??\n false;\n const newScope = config.scope === \"project\" ? (\"global\" as const) : (\"project\" as const);\n\n const rescoped = state.agentConfigs.map((ac) =>\n ac.name === agentName && !ac.excluded ? { ...ac, scope: newScope } : ac,\n );\n\n if (newScope === \"project\") {\n // Moving global → project: add the excluded global tombstone if not already there.\n // Gated on wasInstalledGlobally so fresh init toggles don't create spurious tombstones.\n const needsTombstone =\n wasInstalledGlobally && !rescoped.some((ac) => ac.name === agentName && ac.excluded);\n return {\n agentConfigs: needsTombstone\n ? [...rescoped, { name: agentName, scope: \"global\" as const, excluded: true }]\n : rescoped,\n };\n }\n\n // Moving project → global: always drop any excluded global tombstone for this name.\n // Symmetric with toggleSkillScope — see its comment for the invariant.\n return { agentConfigs: rescoped.filter((ac) => !(ac.name === agentName && ac.excluded)) };\n }),\n\n setFocusedAgentId: (id) => set({ focusedAgentId: id }),\n\n preselectAgentsFromDomains: () =>\n set((state) => {\n // A stack declares its own roster, and that declaration is the whole of what\n // choosing it means. Deriving one from the domains would run later in the step\n // order and overwrite it, so the from-scratch path stops here.\n if (state.selectedStackId) return state;\n\n // Deduped: several selected domains each roster the shared `reviewer`.\n const sorted = [\n ...new Set(state.selectedDomains.flatMap((domain) => DOMAIN_AGENTS[domain] ?? [])),\n ].sort();\n const roster = new Set(sorted);\n const merged = sorted.map((name) => buildAgentConfigForName(name, state.agentConfigs));\n const retained = state.agentConfigs.filter((ac) => survivesRosterRebuild(ac, roster));\n return {\n selectedAgents: sorted,\n agentConfigs: [...merged, ...retained],\n };\n }),\n\n preselectAgentsFromStack: (stackAgents) =>\n set((state) => {\n const savedConfigs = state.globalAgentPreselections?.configs ?? [];\n const globalAgents = state.globalAgentPreselections?.agents ?? [];\n const mergedAgents = [...new Set([...stackAgents, ...globalAgents])].sort();\n const merged = mergedAgents.map((name) => buildAgentConfigForName(name, savedConfigs));\n // Preserve dual-scope tombstones, same invariant as preselectAgentsFromDomains (D-227).\n const excludedConfigs = collectTombstones(savedConfigs);\n return {\n selectedAgents: mergedAgents,\n agentConfigs: [...merged, ...excludedConfigs],\n };\n }),\n\n reset: () => set(createInitialState()),\n\n getAllSelectedTechnologies: () => {\n return typedValues(get().domainSelections).flatMap(flattenCategorySelections);\n },\n\n getSelectedTechnologiesPerDomain: () => {\n const entries = typedEntries(get().domainSelections)\n .map(([domain, domainSel]) => [domain, flattenCategorySelections(domainSel)] as const)\n .filter(([, techs]) => techs.length > 0);\n return typedFromEntries(entries);\n },\n\n getCurrentDomain: () => {\n const state = get();\n return state.selectedDomains[state.currentDomainIndex] || null;\n },\n\n getTechnologyCount: () => {\n return get().getAllSelectedTechnologies().length;\n },\n\n getStepProgress: () => {\n const state = get();\n const isStackDefaults =\n state.approach === \"stack\" && !!state.selectedStackId && state.stackAction === \"defaults\";\n const skippedSteps: WizardStep[] = isStackDefaults ? [\"build\", \"sources\", \"agents\"] : [];\n const precedingSteps = stepsBefore(getActiveStepFlow(), state.step);\n const completedSteps = precedingSteps.filter((step) => !skippedSteps.includes(step));\n return { completedSteps, skippedSteps };\n },\n\n canGoToNextDomain: () => {\n const state = get();\n return state.currentDomainIndex < state.selectedDomains.length - 1;\n },\n\n canGoToPreviousDomain: () => {\n const state = get();\n return state.currentDomainIndex > 0;\n },\n\n setAllSourcesEject: () => {\n set((state) => ({\n // Never touch tombstones — a dual-scope skill's excluded global tombstone keeps describing\n // the masked global install. The bulk set-all applies only to active entries, mirroring the\n // D-262 per-skill setSkillSource/setSourceSelection guard (D-265).\n skillConfigs: state.skillConfigs.map((sc) =>\n sc.excluded ? sc : { ...sc, source: EJECT_SOURCE },\n ),\n }));\n },\n\n setAllSourcesPlugin: () => {\n set((state) => ({\n // Never touch tombstones (see setAllSourcesEject) — the excluded global tombstone keeps its\n // marketplace source describing the masked global install (D-265).\n skillConfigs: state.skillConfigs.map((sc) =>\n sc.excluded ? sc : { ...sc, source: marketplaceSourceName(getSkillById(sc.id)) },\n ),\n }));\n },\n\n buildSourceRows: () => {\n const state = get();\n const selectedTechnologies = get().getAllSelectedTechnologies();\n const { skillConfigs } = state;\n\n // Include inherited global skills (in skillConfigs but not in domainSelections)\n const selectedSet = new Set(selectedTechnologies);\n const inheritedSkillIds = skillConfigs\n .filter((sc) => !sc.excluded && !selectedSet.has(sc.id))\n .map((sc) => sc.id);\n const allActiveIds = new Set([...inheritedSkillIds, ...selectedTechnologies]);\n const excludedGlobalIds = skillConfigs\n .filter((sc) => isGlobalTombstone(sc) && !allActiveIds.has(sc.id))\n .map((sc) => sc.id);\n // Every skill with a LIVE config entry or selection. Internally disjoint by construction, so no\n // skill contributes live rows twice.\n const liveSkillIds = [...inheritedSkillIds, ...selectedTechnologies, ...excludedGlobalIds];\n // D-258: the slot baseline additions are measured against. Each emitted row checks its OWN\n // `(id, scope)` slot, so a skill adopted at a second scope registers as added on the newly\n // occupied row alone — the same classification the confirm step's computeScopeDiff makes.\n const installedSkillSlots = collectInstalledSkillSlots(state.installedSkillConfigs);\n const resolveRowInputs = (id: SkillId) => resolveSkillRowInputs(id, skillConfigs);\n\n const liveRows: SourceRow[] = liveSkillIds.flatMap((id) => {\n const { skillId, configEntry, options } = resolveRowInputs(id);\n return classifySkillSourceRows(skillId, options, {\n configEntry,\n installedSkillConfigs: state.installedSkillConfigs,\n isEditingFromGlobalScope: state.isEditingFromGlobalScope,\n installedSkillSlots,\n });\n });\n\n // D-257/D-271: slots the session emptied. A skill may appear in BOTH lists — a collapsed\n // dual-scope pair keeps its surviving global row and gains a project pending-removal row — so\n // rows are no longer disjoint per SKILL. They stay disjoint per `(id, scope)` SLOT: a snapshot\n // slot a live row already renders never gets a removal row on top of it.\n const pendingRemovalRows: SourceRow[] = collectRemovedInstalledEntries(\n state.installedSkillConfigs,\n skillConfigs,\n )\n .filter((entry) => !isSlotAlreadyRendered(liveRows, entry))\n .map((entry) => {\n const { skillId, options } = resolveRowInputs(entry.id);\n return toPendingRemovalRow(skillId, options, entry);\n });\n\n // Stable sort by scope tier: global readOnly, global editable, then project. Pending-removal\n // rows carry no dedicated tier — they sort by their own scope and, being appended last, trail\n // inline within that scope section. Matches source-grid grouping so navigation indices align\n // with render order.\n const rows = [...liveRows, ...pendingRemovalRows];\n rows.sort((a, b) => sourceRowSortTier(a) - sourceRowSortTier(b));\n\n return rows;\n },\n}));\n\nexport type HydrateOptions = {\n initialStep?: WizardStep;\n initialDomains?: Domain[];\n initialAgents?: AgentName[];\n installedSkillIds?: SkillId[];\n installedSkillConfigs?: SkillConfig[];\n installedAgentConfigs?: AgentScopeConfig[];\n isEditingFromGlobalScope?: boolean;\n};\n\n/**\n * Imperatively hydrates the wizard store before the first render.\n *\n * Must be called BEFORE render(<Wizard ... />) so React captures the correct\n * initial snapshot on the first frame. Running this inside a render-phase hook\n * causes a one-frame flash of the default \"stack\" step before the jump to the\n * intended step is committed to stdout.\n */\nexport function hydrateWizardStore(options: HydrateOptions): void {\n useWizardStore.setState(createInitialState());\n\n const { initialStep } = options;\n const isEditFlow = initialStep !== undefined;\n if (isEditFlow) {\n hydrateForEdit(initialStep, options);\n } else {\n hydrateForInit(options);\n }\n}\n\n/** Edit flow: populate from installed skills, then jump straight to the saved step. */\nfunction hydrateForEdit(initialStep: WizardStep, options: HydrateOptions): void {\n const {\n initialDomains,\n initialAgents,\n installedSkillIds,\n installedSkillConfigs,\n installedAgentConfigs,\n isEditingFromGlobalScope,\n } = options;\n\n if (installedSkillIds?.length) {\n useWizardStore.getState().populateFromSkillIds(installedSkillIds, installedSkillConfigs);\n }\n\n useWizardStore.setState({\n isInitMode: false,\n // Jump directly to initialStep with empty history. The user is starting\n // fresh at this step — no prior steps to navigate back through.\n step: initialStep,\n history: [],\n approach: \"scratch\",\n // Saved domains/agents from config override what populateFromSkillIds derived.\n ...(initialDomains?.length ? { selectedDomains: initialDomains, currentDomainIndex: 0 } : {}),\n ...(initialAgents?.length ? { selectedAgents: initialAgents } : {}),\n ...(initialAgents?.length && installedAgentConfigs?.length\n ? { agentConfigs: installedAgentConfigs }\n : {}),\n // Snapshot installed configs for diff rendering in SkillAgentSummary\n ...(installedSkillConfigs?.length || installedAgentConfigs?.length\n ? {\n installedSkillConfigs: installedSkillConfigs ?? null,\n installedAgentConfigs: installedAgentConfigs ?? null,\n }\n : {}),\n ...(isEditingFromGlobalScope ? { isEditingFromGlobalScope: true } : {}),\n });\n\n useWizardStore.getState().seedFocusedSkillForActiveDomain();\n}\n\n/** Where an init opens when the loaded source offers no stack to choose. */\nconst FIRST_STEP_WITHOUT_STACKS: WizardStep = \"domains\";\n\n/**\n * Init flow: skills are not populated yet — the stack step runs first, so saved\n * selections are stored as preselections for stack-selection.tsx to merge after\n * the stack/scratch choice (selectStack wipes agents).\n *\n * Unless the loaded source offers no stacks at all — a custom marketplace that\n * ships none, since the built-in catalogue stands in for the default public\n * marketplace alone. That step would then hold nothing but its own \"Start from\n * scratch\" row, so the wizard opens where that row leads, having done what\n * pressing it does.\n */\nfunction hydrateForInit(options: HydrateOptions): void {\n const {\n initialDomains,\n initialAgents,\n installedSkillConfigs,\n installedAgentConfigs,\n isEditingFromGlobalScope,\n } = options;\n\n useWizardStore.setState({\n isInitMode: true,\n ...(initialDomains?.length ? { selectedDomains: initialDomains, currentDomainIndex: 0 } : {}),\n ...(initialAgents?.length ? { selectedAgents: initialAgents } : {}),\n ...(initialAgents?.length && installedAgentConfigs?.length\n ? { agentConfigs: installedAgentConfigs }\n : {}),\n // Snapshot installed configs for diff rendering in SkillAgentSummary\n ...(installedSkillConfigs?.length || installedAgentConfigs?.length\n ? {\n installedSkillConfigs: installedSkillConfigs ?? null,\n installedAgentConfigs: installedAgentConfigs ?? null,\n }\n : {}),\n ...(isEditingFromGlobalScope ? { isEditingFromGlobalScope: true } : {}),\n ...(installedSkillConfigs?.length ? { globalPreselections: installedSkillConfigs } : {}),\n ...(initialAgents?.length || installedAgentConfigs?.length\n ? {\n globalAgentPreselections: {\n agents: initialAgents ?? [],\n configs: installedAgentConfigs ?? [],\n },\n }\n : {}),\n });\n\n if (!offersStacks()) {\n useWizardStore.getState().startFromScratch();\n // Nothing behind this step: the stack step it would have been pushed onto\n // never rendered, so the history it would have gone into stays empty.\n useWizardStore.setState({ step: FIRST_STEP_WITHOUT_STACKS, history: [] });\n }\n\n useWizardStore.getState().seedFocusedSkillForActiveDomain();\n}\n","import { sortBy } from \"remeda\";\nimport { typedValues } from \"../../utils/typed-object.js\";\nimport type { CategorySelections, Domain, SkillId } from \"../../types/index.js\";\nimport type { SkillConfig } from \"../../types/config.js\";\nimport { getAvailableSkills, getCellState, getUnmetRequiredBy } from \"../matrix/index.js\";\nimport { matrix, getSkillById } from \"../matrix/matrix-provider.js\";\nimport { deriveScopeBadges } from \"./scope-diff.js\";\nimport type { CategoryRow, CategoryOption } from \"../../components/wizard/category-grid.js\";\n\nexport type BuildStepValidation = {\n valid: boolean;\n message?: string;\n};\n\nexport function validateBuildStep(\n categories: CategoryRow[],\n selections: CategorySelections,\n): BuildStepValidation {\n const emptyRequired = categories.find(\n (category) => category.required && !selections[category.id]?.length,\n );\n return emptyRequired\n ? {\n valid: true,\n message: `No skills selected in ${emptyRequired.displayName} (required category)`,\n }\n : { valid: true };\n}\n\n// Build CategoryRow[] from matrix for a domain\nexport function buildCategoriesForDomain(\n domain: Domain,\n allSelections: SkillId[],\n installedSkillIds?: SkillId[],\n skillConfigs?: SkillConfig[],\n): CategoryRow[] {\n const categories = sortBy(\n typedValues(matrix.categories).filter((cat) => cat.domain === domain),\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- CategoryDefinition declares this required, but an auto-synthesized category for a custom skill can arrive without it — the type is stricter than the data\n (cat) => cat.order ?? 0,\n );\n\n const categoryRows: CategoryRow[] = categories.map((cat) => {\n const skillOptions = getAvailableSkills(cat.id, allSelections);\n\n // Sort options by displayName so the grid order is deterministic across\n // machines and source types (readdir/insertion order is otherwise unstable).\n // Lowercased ordinal comparison keeps the order locale-independent.\n const sortedOptions = sortBy(skillOptions, (skill) =>\n getSkillById(skill.id).displayName.toLowerCase(),\n );\n\n const options: CategoryOption[] = sortedOptions.map((skill) => {\n const activeConfig = skillConfigs?.find((sc) => sc.id === skill.id && !sc.excluded);\n const excludedConfig = skillConfigs?.find((sc) => sc.id === skill.id && sc.excluded);\n const { secondaryScope } = deriveScopeBadges(activeConfig, excludedConfig);\n const local = getSkillById(skill.id).local;\n const requiredBy = skill.selected ? undefined : getUnmetRequiredBy(skill.id, allSelections);\n return {\n id: skill.id,\n // The cell verdict, not the raw advisory: inside a pick-one category\n // the shared semantics forgive what the swap resolves.\n state: getCellState(skill.id, allSelections),\n selected: skill.selected,\n ...(local !== undefined && { local }),\n installed: installedSkillIds?.includes(skill.id) || false,\n ...(activeConfig?.scope !== undefined && { scope: activeConfig.scope }),\n ...(secondaryScope !== undefined && { secondaryScope }),\n ...(activeConfig?.source !== undefined && { source: activeConfig.source }),\n hasUnmetRequirements: skill.hasUnmetRequirements,\n ...(skill.unmetRequirementsReason !== undefined && {\n unmetRequirementsReason: skill.unmetRequirementsReason,\n }),\n ...(requiredBy !== undefined && { requiredBy }),\n };\n });\n\n return {\n id: cat.id,\n displayName: cat.displayName,\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- CategoryDefinition declares this required, but an auto-synthesized category for a custom skill can arrive without it — the type is stricter than the data\n required: cat.required ?? false,\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- CategoryDefinition declares this required, but an auto-synthesized category for a custom skill can arrive without it — the type is stricter than the data\n exclusive: cat.exclusive ?? true,\n options,\n };\n });\n\n return categoryRows.filter((row) => row.options.length > 0);\n}\n","import type { AgentScopeConfig, SkillConfig, SkillScope } from \"../../types/config\";\nimport type { AgentName, SkillId } from \"../../types/index\";\n\nexport type ScopeDiffInput = {\n currentSkills: SkillConfig[];\n currentAgents: AgentScopeConfig[];\n installedSkillConfigs: SkillConfig[] | null;\n installedAgentConfigs: AgentScopeConfig[] | null;\n isInitMode: boolean;\n};\n\nexport type DiffRowStatus = \"added\" | \"mode-changed\" | \"removed\" | \"unchanged\";\n\nexport type SkillDiffRow = {\n id: SkillId;\n source: string;\n status: DiffRowStatus;\n};\n\nexport type AgentDiffRow = {\n name: AgentName;\n status: Exclude<DiffRowStatus, \"mode-changed\">;\n};\n\nexport type ScopeDiff = {\n projectSkillRows: SkillDiffRow[];\n globalSkillRows: SkillDiffRow[];\n projectAgentRows: AgentDiffRow[];\n globalAgentRows: AgentDiffRow[];\n hasContent: boolean;\n};\n\n/**\n * Computes the per-scope diff rows the confirm-step summary renders.\n *\n * The diff baseline keeps tombstones as first-class entries. A tombstone\n * occupies the (id, scope) slot: it means \"the global install still exists but\n * is silenced at project scope\" (D-223 dual-scope indicator). Treating the slot\n * as occupied — for both the previous-key set (isNew detection) and removal\n * (slot-occupancy match) — prevents a dual-scope G→P toggle from rendering a\n * spurious `-` at Global (D-230) or a spurious `+` at Global on the next edit\n * when the stored tombstone is re-read (D-232). See D-225 investigation 09 for\n * the full derivation; mode-change (`~`) tracking filters to active baseline\n * entries because tombstones don't represent a live install.\n */\nexport function computeScopeDiff(input: ScopeDiffInput): ScopeDiff {\n const { currentSkills, currentAgents, installedSkillConfigs, installedAgentConfigs, isInitMode } =\n input;\n\n const projectSkills = currentSkills.filter((s) => s.scope === \"project\" && !s.excluded);\n const globalSkills = currentSkills.filter((s) => s.scope === \"global\" && !s.excluded);\n const projectAgents = currentAgents.filter((a) => a.scope === \"project\" && !a.excluded);\n const globalAgents = currentAgents.filter((a) => a.scope === \"global\" && !a.excluded);\n const excludedGlobalSkills = currentSkills.filter((s) => s.scope === \"global\" && !!s.excluded);\n const excludedGlobalAgents = currentAgents.filter((a) => a.scope === \"global\" && !!a.excluded);\n\n const prevSkillKeySet = installedSkillConfigs\n ? new Set(installedSkillConfigs.map((s) => skillSlotKey(s.id, s.scope)))\n : null;\n const prevSourceMap = installedSkillConfigs\n ? new Map(\n installedSkillConfigs\n .filter((s) => !s.excluded)\n .map((s) => [skillSlotKey(s.id, s.scope), s.source]),\n )\n : null;\n const prevAgentKeySet = installedAgentConfigs\n ? new Set(installedAgentConfigs.map((a) => agentSlotKey(a.name, a.scope)))\n : null;\n\n // Active baseline globals that are overridden at project scope without a\n // tombstone in current state — a historical no-tombstone dual-scope shape.\n // Current tombstones are handled directly via `uniqueExcludedGlobalSkills`.\n const activeSkillBaseline = installedSkillConfigs\n ? installedSkillConfigs.filter((s) => !s.excluded)\n : [];\n const activeAgentBaseline = installedAgentConfigs\n ? installedAgentConfigs.filter((a) => !a.excluded)\n : [];\n const inheritedGlobalSkills = activeSkillBaseline.filter(\n (s) =>\n s.scope === \"global\" &&\n !globalSkills.some((g) => g.id === s.id) &&\n !excludedGlobalSkills.some((e) => e.id === s.id) &&\n projectSkills.some((p) => p.id === s.id),\n );\n const inheritedGlobalAgents = activeAgentBaseline.filter(\n (a) =>\n a.scope === \"global\" &&\n !globalAgents.some((g) => g.name === a.name) &&\n !excludedGlobalAgents.some((e) => e.name === a.name) &&\n projectAgents.some((p) => p.name === a.name),\n );\n\n // Slot-occupancy match: a baseline entry at (id, scope) is considered\n // removed only if nothing — active OR tombstone — occupies that slot in\n // current. A current tombstone at the same key keeps the slot occupied\n // (dual-scope indicator, not a removal). See D-230 / D-232.\n const removedSkills = installedSkillConfigs\n ? installedSkillConfigs.filter(\n (s) => !currentSkills.some((c) => c.id === s.id && c.scope === s.scope),\n )\n : [];\n const removedAgents = installedAgentConfigs\n ? installedAgentConfigs.filter(\n (a) => !currentAgents.some((c) => c.name === a.name && c.scope === a.scope),\n )\n : [];\n\n // `uniqueExcludedGlobalSkills` dedups the current tombstone row against any\n // inherited-global entry for the same id so the Global section never shows\n // two rows for the same skill. Under the slot-occupancy removal match above,\n // a current tombstone at (id, global) keeps the slot occupied and therefore\n // cannot collide with the removed-global rows — no further dedup needed there.\n const inheritedSkillIdSet = new Set(inheritedGlobalSkills.map((s) => s.id));\n const uniqueExcludedGlobalSkills = excludedGlobalSkills.filter(\n (s) => !inheritedSkillIdSet.has(s.id),\n );\n const allGlobalSkills = [\n ...globalSkills,\n ...inheritedGlobalSkills,\n ...uniqueExcludedGlobalSkills,\n ];\n const inheritedAgentNameSet = new Set(inheritedGlobalAgents.map((a) => a.name));\n const uniqueExcludedGlobalAgents = excludedGlobalAgents.filter(\n (a) => !inheritedAgentNameSet.has(a.name),\n );\n const allGlobalAgents = [\n ...globalAgents,\n ...inheritedGlobalAgents,\n ...uniqueExcludedGlobalAgents,\n ];\n\n const removedGlobalSkills = isInitMode ? [] : removedSkills.filter((s) => s.scope === \"global\");\n const removedProjectSkills = removedSkills.filter((s) => s.scope === \"project\");\n const removedGlobalAgents = isInitMode ? [] : removedAgents.filter((a) => a.scope === \"global\");\n const removedProjectAgents = removedAgents.filter((a) => a.scope === \"project\");\n\n const projectSkillRows = [\n ...projectSkills.map((s) => classifyDiffRow(s, prevSkillKeySet, prevSourceMap)),\n ...removedProjectSkills.map(toRemovedSkillRow),\n ];\n const globalSkillRows = [\n ...allGlobalSkills.map((s) => classifyDiffRow(s, prevSkillKeySet, prevSourceMap)),\n ...removedGlobalSkills.map(toRemovedSkillRow),\n ];\n const projectAgentRows = [\n ...projectAgents.map((a) => classifyAgentDiffRow(a, prevAgentKeySet)),\n ...removedProjectAgents.map(toRemovedAgentRow),\n ];\n const globalAgentRows = [\n ...allGlobalAgents.map((a) => classifyAgentDiffRow(a, prevAgentKeySet)),\n ...removedGlobalAgents.map(toRemovedAgentRow),\n ];\n\n const hasContent =\n projectSkillRows.length > 0 ||\n globalSkillRows.length > 0 ||\n projectAgentRows.length > 0 ||\n globalAgentRows.length > 0;\n\n return { projectSkillRows, globalSkillRows, projectAgentRows, globalAgentRows, hasContent };\n}\n\n/**\n * The `(id, scope)` SLOT key this module diffs on. Exported so every surface that computes its own\n * session diff — notably the wizard store's Sources tab — keys on the same slot rather than on the\n * id alone, which is what let one skill read as added on one surface and unchanged on the other\n * (todo/D-271 secondary hardening).\n */\nexport function skillSlotKey(id: SkillId, scope: SkillScope | undefined): string {\n return `${id}:${scope}`;\n}\n\n/**\n * The `(name, scope)` SLOT key for an agent — the agent-side counterpart of {@link skillSlotKey},\n * exported for the same reason. Today only this module diffs agent slots, so there is nothing to\n * disagree with; the helper exists so that a second surface routes through it from the start rather\n * than re-deriving the key on `name` alone, which is precisely how the skill side reached D-278.\n */\nexport function agentSlotKey(name: AgentName, scope: SkillScope | undefined): string {\n return `${name}:${scope}`;\n}\n\nexport type ScopeBadges = {\n scope: SkillScope | undefined;\n secondaryScope: SkillScope | undefined;\n};\n\n/** Bracketed scope badge: `[G]` for global, `[P]` for project. */\nexport function formatScopeTag(scope: SkillScope): \"[G]\" | \"[P]\" {\n return scope === \"global\" ? \"[G]\" : \"[P]\";\n}\n\n/**\n * D-223: derives the primary + secondary scope badges for a row from its active\n * entry and its excluded tombstone. A tombstone at the OTHER scope renders as a\n * secondary badge (`[P][G]`); a same-scope tombstone renders nothing extra.\n */\nexport function deriveScopeBadges(\n activeConfig: { scope: SkillScope } | undefined,\n excludedConfig: { scope: SkillScope } | undefined,\n): ScopeBadges {\n const secondaryScope =\n excludedConfig && activeConfig && excludedConfig.scope !== activeConfig.scope\n ? excludedConfig.scope\n : undefined;\n return { scope: activeConfig?.scope, secondaryScope };\n}\n\n/**\n * Classifies an active skill entry against the baseline: added, mode-changed, or unchanged.\n *\n * The comparison is still on `source`, because that field IS where a skill's install mode is\n * recorded: `eject` means the project's own copy and anything else names the marketplace the\n * plugin comes from. With no second marketplace to move between, a `source` that changed is a\n * mode that changed, which is what the `~` marker has always meant to a reader.\n */\nfunction classifyDiffRow(\n skill: SkillConfig,\n prevKeySet: Set<string> | null,\n prevSourceMap: Map<string, string> | null,\n): SkillDiffRow {\n const key = skillSlotKey(skill.id, skill.scope);\n const isNew = prevKeySet === null || !prevKeySet.has(key);\n const prevSource = prevSourceMap?.get(key);\n const modeChanged = !isNew && prevSource != null && prevSource !== skill.source;\n if (modeChanged) {\n return { id: skill.id, source: skill.source, status: \"mode-changed\" };\n }\n return { id: skill.id, source: skill.source, status: isNew ? \"added\" : \"unchanged\" };\n}\n\nfunction classifyAgentDiffRow(\n agent: AgentScopeConfig,\n prevKeySet: Set<string> | null,\n): AgentDiffRow {\n const isNew = prevKeySet === null || !prevKeySet.has(agentSlotKey(agent.name, agent.scope));\n return { name: agent.name, status: isNew ? \"added\" : \"unchanged\" };\n}\n\nfunction toRemovedSkillRow(skill: SkillConfig): SkillDiffRow {\n return { id: skill.id, source: skill.source, status: \"removed\" };\n}\n\nfunction toRemovedAgentRow(agent: AgentScopeConfig): AgentDiffRow {\n return { name: agent.name, status: \"removed\" };\n}\n","import { DOMAIN_ORDER } from \"@workspace/matrix\";\nimport type { Domain } from \"../../types/index.js\";\n\n/**\n * Sort domains into canonical display order: custom domains first (alphabetically),\n * then built-in domains in the shared DOMAIN_ORDER.\n */\nexport function orderDomains(domains: Domain[]): Domain[] {\n const builtInSet = new Set<Domain>(DOMAIN_ORDER);\n return [\n ...domains.filter((d) => !builtInSet.has(d)).sort(),\n ...DOMAIN_ORDER.filter((d) => domains.includes(d)),\n ];\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
useRowScroll
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-V3ENMW7G.js";
|
|
5
5
|
import {
|
|
6
6
|
KEY_SPACE
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-2HC2SNRP.js";
|
|
8
8
|
import {
|
|
9
9
|
CLI_COLORS,
|
|
10
10
|
UI_SYMBOLS
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-R54KIGN2.js";
|
|
12
12
|
|
|
13
13
|
// src/cli/components/wizard/checkbox-grid.tsx
|
|
14
14
|
import { Box, Text, useInput } from "ink";
|
|
@@ -62,8 +62,8 @@ var CheckboxGrid = ({
|
|
|
62
62
|
const checkbox = isSelected ? "[\u2713]" : "[ ]";
|
|
63
63
|
const pointer = isFocused ? UI_SYMBOLS.CHEVRON : UI_SYMBOLS.CHEVRON_SPACER;
|
|
64
64
|
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
65
|
-
/* @__PURE__ */ jsx(Text, { color:
|
|
66
|
-
/* @__PURE__ */ jsxs(Text, {
|
|
65
|
+
/* @__PURE__ */ jsx(Text, { ...isFocused && { color: CLI_COLORS.PRIMARY }, children: pointer }),
|
|
66
|
+
/* @__PURE__ */ jsxs(Text, { ...(isSelected || isFocused) && { color: CLI_COLORS.PRIMARY }, bold: isFocused, children: [
|
|
67
67
|
" ",
|
|
68
68
|
checkbox,
|
|
69
69
|
" ",
|
|
@@ -108,4 +108,4 @@ var CheckboxGrid = ({
|
|
|
108
108
|
export {
|
|
109
109
|
CheckboxGrid
|
|
110
110
|
};
|
|
111
|
-
//# sourceMappingURL=chunk-
|
|
111
|
+
//# sourceMappingURL=chunk-IO4VEW5U.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/components/wizard/checkbox-grid.tsx"],"sourcesContent":["import { Box, Text, useInput } from \"ink\";\nimport React, { useState } from \"react\";\nimport { CLI_COLORS, UI_SYMBOLS } from \"../../consts.js\";\nimport { useRowScroll } from \"../hooks/use-row-scroll.js\";\nimport { KEY_SPACE } from \"./hotkeys.js\";\n\nexport type CheckboxItem<T extends string = string> = {\n id: T;\n label: string;\n description: string;\n};\n\nexport type CheckboxGridProps<T extends string = string> = {\n subtitle?: string;\n items: CheckboxItem<T>[];\n selectedIds: T[];\n /** Available height in terminal lines for the scrollable viewport. 0 = no constraint. */\n availableHeight?: number;\n onToggle: (id: T) => void;\n onContinue: () => void;\n onBack: () => void;\n /** Message shown when nothing is selected */\n emptyMessage?: string;\n};\n\nexport const CheckboxGrid = <T extends string = string>({\n subtitle,\n items,\n selectedIds,\n availableHeight = 0,\n onToggle,\n onContinue,\n onBack,\n emptyMessage = \"Please select at least one item\",\n}: CheckboxGridProps<T>): React.ReactElement => {\n // Items + continue option at the end\n const totalItems = items.length + 1;\n const [focusedIndex, setFocusedIndex] = useState(0);\n\n useInput((input, key) => {\n if (key.escape) {\n onBack();\n return;\n }\n\n if (key.upArrow || input === \"k\") {\n setFocusedIndex((prev) => (prev <= 0 ? totalItems - 1 : prev - 1));\n return;\n }\n\n if (key.downArrow || input === \"j\") {\n setFocusedIndex((prev) => (prev >= totalItems - 1 ? 0 : prev + 1));\n return;\n }\n\n if (key.return) {\n onContinue();\n return;\n }\n\n if (input === KEY_SPACE) {\n const item = items[focusedIndex];\n if (item) {\n onToggle(item.id);\n }\n }\n });\n\n // The Domains grid clips silently: `useRowScroll` exposes no hidden-line\n // counts and this view renders no `ScrollAffordance`, by owner decision — see\n // the hook's doc comment.\n // When focus is on continue (past items), scroll to show last items\n const effectiveRow = focusedIndex >= items.length ? items.length - 1 : focusedIndex;\n const { scrollEnabled, scrollTop } = useRowScroll({\n focusedIndex: effectiveRow,\n itemCount: items.length,\n availableHeight,\n });\n\n const itemElements = items.map((item, index) => {\n const isFocused = index === focusedIndex;\n const isSelected = selectedIds.includes(item.id);\n const checkbox = isSelected ? \"[\\u2713]\" : \"[ ]\";\n const pointer = isFocused ? UI_SYMBOLS.CHEVRON : UI_SYMBOLS.CHEVRON_SPACER;\n\n return (\n <Text key={item.id}>\n <Text
|
|
1
|
+
{"version":3,"sources":["../src/cli/components/wizard/checkbox-grid.tsx"],"sourcesContent":["import { Box, Text, useInput } from \"ink\";\nimport React, { useState } from \"react\";\nimport { CLI_COLORS, UI_SYMBOLS } from \"../../consts.js\";\nimport { useRowScroll } from \"../hooks/use-row-scroll.js\";\nimport { KEY_SPACE } from \"./hotkeys.js\";\n\nexport type CheckboxItem<T extends string = string> = {\n id: T;\n label: string;\n description: string;\n};\n\nexport type CheckboxGridProps<T extends string = string> = {\n subtitle?: string;\n items: CheckboxItem<T>[];\n selectedIds: T[];\n /** Available height in terminal lines for the scrollable viewport. 0 = no constraint. */\n availableHeight?: number;\n onToggle: (id: T) => void;\n onContinue: () => void;\n onBack: () => void;\n /** Message shown when nothing is selected */\n emptyMessage?: string;\n};\n\nexport const CheckboxGrid = <T extends string = string>({\n subtitle,\n items,\n selectedIds,\n availableHeight = 0,\n onToggle,\n onContinue,\n onBack,\n emptyMessage = \"Please select at least one item\",\n}: CheckboxGridProps<T>): React.ReactElement => {\n // Items + continue option at the end\n const totalItems = items.length + 1;\n const [focusedIndex, setFocusedIndex] = useState(0);\n\n useInput((input, key) => {\n if (key.escape) {\n onBack();\n return;\n }\n\n if (key.upArrow || input === \"k\") {\n setFocusedIndex((prev) => (prev <= 0 ? totalItems - 1 : prev - 1));\n return;\n }\n\n if (key.downArrow || input === \"j\") {\n setFocusedIndex((prev) => (prev >= totalItems - 1 ? 0 : prev + 1));\n return;\n }\n\n if (key.return) {\n onContinue();\n return;\n }\n\n if (input === KEY_SPACE) {\n const item = items[focusedIndex];\n if (item) {\n onToggle(item.id);\n }\n }\n });\n\n // The Domains grid clips silently: `useRowScroll` exposes no hidden-line\n // counts and this view renders no `ScrollAffordance`, by owner decision — see\n // the hook's doc comment.\n // When focus is on continue (past items), scroll to show last items\n const effectiveRow = focusedIndex >= items.length ? items.length - 1 : focusedIndex;\n const { scrollEnabled, scrollTop } = useRowScroll({\n focusedIndex: effectiveRow,\n itemCount: items.length,\n availableHeight,\n });\n\n const itemElements = items.map((item, index) => {\n const isFocused = index === focusedIndex;\n const isSelected = selectedIds.includes(item.id);\n const checkbox = isSelected ? \"[\\u2713]\" : \"[ ]\";\n const pointer = isFocused ? UI_SYMBOLS.CHEVRON : UI_SYMBOLS.CHEVRON_SPACER;\n\n return (\n <Text key={item.id}>\n <Text {...(isFocused && { color: CLI_COLORS.PRIMARY })}>{pointer}</Text>\n <Text {...((isSelected || isFocused) && { color: CLI_COLORS.PRIMARY })} bold={isFocused}>\n {\" \"}\n {checkbox} {item.label}\n </Text>\n {item.description && (\n <Text dimColor>\n {\" \"}\n {item.description}\n </Text>\n )}\n </Text>\n );\n });\n\n const footerElement =\n selectedIds.length > 0 ? (\n <Text>\n {\"\\n\"}Selected: <Text color={CLI_COLORS.WARNING}>{selectedIds.join(\", \")}</Text>\n </Text>\n ) : emptyMessage ? (\n <Text dimColor>\n {\"\\n\"}\n {emptyMessage}\n </Text>\n ) : null;\n\n return (\n <Box flexDirection=\"column\">\n {subtitle && <Text dimColor>{subtitle}</Text>}\n <Box\n flexDirection=\"column\"\n {...(scrollEnabled && { height: availableHeight, overflow: \"hidden\" as const })}\n >\n <Box\n flexDirection=\"column\"\n marginTop={scrollTop > 0 ? -scrollTop : 0}\n {...(scrollEnabled && { flexShrink: 0 })}\n >\n {itemElements}\n </Box>\n </Box>\n {footerElement}\n </Box>\n );\n};\n"],"mappings":";;;;;;;;;;;;;AAAA,SAAS,KAAK,MAAM,gBAAgB;AACpC,SAAgB,gBAAgB;AAsFxB,cACA,YADA;AA9DD,IAAM,eAAe,CAA4B;AAAA,EACtD;AAAA,EACA;AAAA,EACA;AAAA,EACA,kBAAkB;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AACjB,MAAgD;AAE9C,QAAM,aAAa,MAAM,SAAS;AAClC,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,CAAC;AAElD,WAAS,CAAC,OAAO,QAAQ;AACvB,QAAI,IAAI,QAAQ;AACd,aAAO;AACP;AAAA,IACF;AAEA,QAAI,IAAI,WAAW,UAAU,KAAK;AAChC,sBAAgB,CAAC,SAAU,QAAQ,IAAI,aAAa,IAAI,OAAO,CAAE;AACjE;AAAA,IACF;AAEA,QAAI,IAAI,aAAa,UAAU,KAAK;AAClC,sBAAgB,CAAC,SAAU,QAAQ,aAAa,IAAI,IAAI,OAAO,CAAE;AACjE;AAAA,IACF;AAEA,QAAI,IAAI,QAAQ;AACd,iBAAW;AACX;AAAA,IACF;AAEA,QAAI,UAAU,WAAW;AACvB,YAAM,OAAO,MAAM,YAAY;AAC/B,UAAI,MAAM;AACR,iBAAS,KAAK,EAAE;AAAA,MAClB;AAAA,IACF;AAAA,EACF,CAAC;AAMD,QAAM,eAAe,gBAAgB,MAAM,SAAS,MAAM,SAAS,IAAI;AACvE,QAAM,EAAE,eAAe,UAAU,IAAI,aAAa;AAAA,IAChD,cAAc;AAAA,IACd,WAAW,MAAM;AAAA,IACjB;AAAA,EACF,CAAC;AAED,QAAM,eAAe,MAAM,IAAI,CAAC,MAAM,UAAU;AAC9C,UAAM,YAAY,UAAU;AAC5B,UAAM,aAAa,YAAY,SAAS,KAAK,EAAE;AAC/C,UAAM,WAAW,aAAa,aAAa;AAC3C,UAAM,UAAU,YAAY,WAAW,UAAU,WAAW;AAE5D,WACE,qBAAC,QACC;AAAA,0BAAC,QAAM,GAAI,aAAa,EAAE,OAAO,WAAW,QAAQ,GAAK,mBAAQ;AAAA,MACjE,qBAAC,QAAM,IAAK,cAAc,cAAc,EAAE,OAAO,WAAW,QAAQ,GAAI,MAAM,WAC3E;AAAA;AAAA,QACA;AAAA,QAAS;AAAA,QAAE,KAAK;AAAA,SACnB;AAAA,MACC,KAAK,eACJ,qBAAC,QAAK,UAAQ,MACX;AAAA;AAAA,QACA,KAAK;AAAA,SACR;AAAA,SAVO,KAAK,EAYhB;AAAA,EAEJ,CAAC;AAED,QAAM,gBACJ,YAAY,SAAS,IACnB,qBAAC,QACE;AAAA;AAAA,IAAK;AAAA,IAAU,oBAAC,QAAK,OAAO,WAAW,SAAU,sBAAY,KAAK,IAAI,GAAE;AAAA,KAC3E,IACE,eACF,qBAAC,QAAK,UAAQ,MACX;AAAA;AAAA,IACA;AAAA,KACH,IACE;AAEN,SACE,qBAAC,OAAI,eAAc,UAChB;AAAA,gBAAY,oBAAC,QAAK,UAAQ,MAAE,oBAAS;AAAA,IACtC;AAAA,MAAC;AAAA;AAAA,QACC,eAAc;AAAA,QACb,GAAI,iBAAiB,EAAE,QAAQ,iBAAiB,UAAU,SAAkB;AAAA,QAE7E;AAAA,UAAC;AAAA;AAAA,YACC,eAAc;AAAA,YACd,WAAW,YAAY,IAAI,CAAC,YAAY;AAAA,YACvC,GAAI,iBAAiB,EAAE,YAAY,EAAE;AAAA,YAErC;AAAA;AAAA,QACH;AAAA;AAAA,IACF;AAAA,IACC;AAAA,KACH;AAEJ;","names":[]}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
WIZARD_STEP_ORDER
|
|
4
|
-
} from "./chunk-5UWKOOIS.js";
|
|
5
2
|
import {
|
|
6
3
|
CLI_COLORS
|
|
7
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-R54KIGN2.js";
|
|
8
5
|
|
|
9
6
|
// src/cli/components/wizard/wizard-tabs.tsx
|
|
10
7
|
import { Box, Text } from "ink";
|
|
8
|
+
import "react";
|
|
11
9
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
12
10
|
var WIZARD_STEP_LABELS = {
|
|
13
11
|
stack: "Stack",
|
|
@@ -17,12 +15,11 @@ var WIZARD_STEP_LABELS = {
|
|
|
17
15
|
agents: "Agents",
|
|
18
16
|
confirm: "Confirm"
|
|
19
17
|
};
|
|
20
|
-
|
|
21
|
-
id,
|
|
22
|
-
|
|
23
|
-
}));
|
|
18
|
+
function wizardTabsFor(flow) {
|
|
19
|
+
return flow.map((id) => ({ id, label: WIZARD_STEP_LABELS[id] }));
|
|
20
|
+
}
|
|
24
21
|
function formatStepLabel(stepId) {
|
|
25
|
-
return
|
|
22
|
+
return WIZARD_STEP_LABELS[stepId];
|
|
26
23
|
}
|
|
27
24
|
var getStepState = (stepId, currentStep, completedSteps, skippedSteps) => {
|
|
28
25
|
if (completedSteps.includes(stepId)) return "completed";
|
|
@@ -51,7 +48,7 @@ var Tab = ({ step, state }) => {
|
|
|
51
48
|
}
|
|
52
49
|
}
|
|
53
50
|
};
|
|
54
|
-
var TabDropdown = ({ items, activeId, isSubNav, inline }) => /* @__PURE__ */ jsx(
|
|
51
|
+
var TabDropdown = ({ items, activeId, isSubNav = false, inline }) => /* @__PURE__ */ jsx(
|
|
55
52
|
Box,
|
|
56
53
|
{
|
|
57
54
|
...inline ? {} : { position: "absolute", marginTop: 2 },
|
|
@@ -168,8 +165,8 @@ var WizardTabs = ({
|
|
|
168
165
|
};
|
|
169
166
|
|
|
170
167
|
export {
|
|
171
|
-
|
|
168
|
+
wizardTabsFor,
|
|
172
169
|
formatStepLabel,
|
|
173
170
|
WizardTabs
|
|
174
171
|
};
|
|
175
|
-
//# sourceMappingURL=chunk-
|
|
172
|
+
//# sourceMappingURL=chunk-JC7DBPRD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/components/wizard/wizard-tabs.tsx"],"sourcesContent":["import { Box, Text } from \"ink\";\nimport React from \"react\";\nimport { CLI_COLORS } from \"../../consts.js\";\nimport type { WizardStep } from \"../../stores/wizard-store.js\";\nimport type { Domain } from \"../../types/index.js\";\n\ntype WizardTabStep = {\n id: WizardStep;\n label: string;\n};\n\nexport type TabDropdownItem = {\n id: string;\n label: string;\n};\n\nexport type TabDropdownProps = {\n items: TabDropdownItem[];\n activeId?: string | undefined;\n isSubNav?: boolean | undefined;\n inline?: boolean | undefined;\n};\n\nexport type DomainNavProps = {\n domains: Domain[];\n activeDomain: Domain;\n getDomainLabel: (domain: Domain) => string;\n};\n\nexport type WizardTabsProps = {\n steps: WizardTabStep[];\n currentStep: WizardStep;\n completedSteps: WizardStep[];\n skippedSteps?: WizardStep[] | undefined;\n version?: string | undefined;\n domainNav?: DomainNavProps | undefined;\n dropdowns?: Partial<Record<WizardStep, TabDropdownProps>>;\n};\n\n/** Display label per wizard step. Step ORDER has a single source: WIZARD_STEP_ORDER. */\nconst WIZARD_STEP_LABELS = {\n stack: \"Stack\",\n domains: \"Domains\",\n build: \"Skills\",\n sources: \"Sources\",\n agents: \"Agents\",\n confirm: \"Confirm\",\n} as const satisfies Record<WizardStep, string>;\n\n/**\n * The tabs for a step flow — one per step that flow runs, in its order.\n *\n * A function of the flow rather than a constant over `WIZARD_STEP_ORDER`: a\n * source that ships no stacks gives the wizard no stack step (see\n * `getActiveStepFlow`), and a bar drawn from every step the wizard HAS would\n * paint a Stack tab for a step this run never opens.\n */\nexport function wizardTabsFor(flow: WizardStep[]): WizardTabStep[] {\n return flow.map((id) => ({ id, label: WIZARD_STEP_LABELS[id] }));\n}\n\n/** Tab label for a wizard step. */\nexport function formatStepLabel(stepId: WizardStep): string {\n return WIZARD_STEP_LABELS[stepId];\n}\n\ntype StepState = \"completed\" | \"current\" | \"pending\" | \"skipped\";\n\nconst getStepState = (\n stepId: WizardStep,\n currentStep: WizardStep,\n completedSteps: WizardStep[],\n skippedSteps: WizardStep[],\n): StepState => {\n if (completedSteps.includes(stepId)) return \"completed\";\n if (stepId === currentStep) return \"current\";\n if (skippedSteps.includes(stepId)) return \"skipped\";\n return \"pending\";\n};\n\ntype TabProps = {\n step: WizardTabStep;\n state: StepState;\n};\n\nconst Tab: React.FC<TabProps> = ({ step, state }) => {\n const label = step.label;\n\n switch (state) {\n case \"current\":\n return (\n <Text color={CLI_COLORS.BLACK} backgroundColor={CLI_COLORS.WARNING} bold>\n {\" \"}\n {label}{\" \"}\n </Text>\n );\n case \"completed\":\n return <Text>{label}</Text>;\n case \"skipped\":\n return <Text dimColor>{label}</Text>;\n case \"pending\":\n return <Text color={CLI_COLORS.UNFOCUSED}>{label}</Text>;\n default: {\n const _exhaustive: never = state;\n return _exhaustive;\n }\n }\n};\n\nconst TabDropdown: React.FC<TabDropdownProps> = ({ items, activeId, isSubNav = false, inline }) => (\n <Box\n {...(inline ? {} : { position: \"absolute\" as const, marginTop: 2 })}\n flexDirection=\"row\"\n columnGap={0}\n paddingLeft={1}\n paddingRight={1}\n borderStyle=\"single\"\n borderColor=\"blackBright\"\n flexWrap=\"wrap\"\n >\n {items.map((item) => {\n const isActive = item.id === activeId;\n return (\n <Text\n key={item.id}\n dimColor={!isSubNav}\n bold={isSubNav && isActive}\n color={isActive ? CLI_COLORS.WARNING : CLI_COLORS.UNFOCUSED}\n >\n {\" \"}\n {item.label}{\" \"}\n </Text>\n );\n })}\n </Box>\n);\n\nconst DomainNav: React.FC<DomainNavProps & { inline?: boolean }> = ({\n domains,\n activeDomain,\n getDomainLabel,\n inline,\n}) => (\n <TabDropdown\n items={domains.map((d) => ({ id: d, label: getDomainLabel(d) }))}\n activeId={activeDomain}\n isSubNav\n inline={inline}\n />\n);\n\nconst DOMAIN_NAV_CHAR_THRESHOLD = 25;\n\nconst getStepJustifyContent = (stepId: WizardStep): \"flex-start\" | \"flex-end\" | \"center\" => {\n if (stepId === \"stack\") return \"flex-start\";\n if (stepId === \"confirm\") return \"flex-end\";\n return \"center\";\n};\n\nconst isDomainNavOverThreshold = (domainNav: DomainNavProps): boolean =>\n domainNav.domains.reduce((sum, d) => sum + domainNav.getDomainLabel(d).length, 0) >\n DOMAIN_NAV_CHAR_THRESHOLD;\n\nexport const WizardTabs: React.FC<WizardTabsProps> = ({\n steps,\n currentStep,\n completedSteps,\n skippedSteps = [],\n version,\n domainNav,\n dropdowns,\n}) => {\n const shouldHoistDomainNav = domainNav ? isDomainNavOverThreshold(domainNav) : false;\n\n return (\n <>\n <Box\n flexDirection=\"row\"\n columnGap={2}\n borderColor=\"blackBright\"\n borderStyle=\"single\"\n paddingX={1}\n alignItems=\"center\"\n marginBottom={shouldHoistDomainNav ? 0 : 3}\n >\n {steps.map((step) => {\n const state = getStepState(step.id, currentStep, completedSteps, skippedSteps);\n\n if (step.id === \"build\" && domainNav && !shouldHoistDomainNav) {\n return (\n <Box\n key={step.id}\n position=\"relative\"\n display=\"flex\"\n justifyContent={getStepJustifyContent(step.id)}\n >\n <Tab step={step} state={state} />\n <DomainNav {...domainNav} />\n </Box>\n );\n }\n\n // Generic dropdown for any step\n const dropdown = dropdowns?.[step.id];\n if (dropdown) {\n return (\n <Box\n key={step.id}\n position=\"relative\"\n display=\"flex\"\n justifyContent={getStepJustifyContent(step.id)}\n >\n <Tab step={step} state={state} />\n <TabDropdown {...dropdown} />\n </Box>\n );\n }\n\n return <Tab key={step.id} step={step} state={state} />;\n })}\n <Box flexGrow={1} justifyContent=\"flex-end\">\n <Text dimColor>{`v${version}`}</Text>\n </Box>\n </Box>\n {domainNav && shouldHoistDomainNav && (\n <Box>\n <DomainNav {...domainNav} inline />\n </Box>\n )}\n </>\n );\n};\n"],"mappings":";;;;;;AAAA,SAAS,KAAK,YAAY;AAC1B,OAAkB;AA0FV,SAoFJ,UA9ES,KANL;AAnDR,IAAM,qBAAqB;AAAA,EACzB,OAAO;AAAA,EACP,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AACX;AAUO,SAAS,cAAc,MAAqC;AACjE,SAAO,KAAK,IAAI,CAAC,QAAQ,EAAE,IAAI,OAAO,mBAAmB,EAAE,EAAE,EAAE;AACjE;AAGO,SAAS,gBAAgB,QAA4B;AAC1D,SAAO,mBAAmB,MAAM;AAClC;AAIA,IAAM,eAAe,CACnB,QACA,aACA,gBACA,iBACc;AACd,MAAI,eAAe,SAAS,MAAM,EAAG,QAAO;AAC5C,MAAI,WAAW,YAAa,QAAO;AACnC,MAAI,aAAa,SAAS,MAAM,EAAG,QAAO;AAC1C,SAAO;AACT;AAOA,IAAM,MAA0B,CAAC,EAAE,MAAM,MAAM,MAAM;AACnD,QAAM,QAAQ,KAAK;AAEnB,UAAQ,OAAO;AAAA,IACb,KAAK;AACH,aACE,qBAAC,QAAK,OAAO,WAAW,OAAO,iBAAiB,WAAW,SAAS,MAAI,MACrE;AAAA;AAAA,QACA;AAAA,QAAO;AAAA,SACV;AAAA,IAEJ,KAAK;AACH,aAAO,oBAAC,QAAM,iBAAM;AAAA,IACtB,KAAK;AACH,aAAO,oBAAC,QAAK,UAAQ,MAAE,iBAAM;AAAA,IAC/B,KAAK;AACH,aAAO,oBAAC,QAAK,OAAO,WAAW,WAAY,iBAAM;AAAA,IACnD,SAAS;AACP,YAAM,cAAqB;AAC3B,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEA,IAAM,cAA0C,CAAC,EAAE,OAAO,UAAU,WAAW,OAAO,OAAO,MAC3F;AAAA,EAAC;AAAA;AAAA,IACE,GAAI,SAAS,CAAC,IAAI,EAAE,UAAU,YAAqB,WAAW,EAAE;AAAA,IACjE,eAAc;AAAA,IACd,WAAW;AAAA,IACX,aAAa;AAAA,IACb,cAAc;AAAA,IACd,aAAY;AAAA,IACZ,aAAY;AAAA,IACZ,UAAS;AAAA,IAER,gBAAM,IAAI,CAAC,SAAS;AACnB,YAAM,WAAW,KAAK,OAAO;AAC7B,aACE;AAAA,QAAC;AAAA;AAAA,UAEC,UAAU,CAAC;AAAA,UACX,MAAM,YAAY;AAAA,UAClB,OAAO,WAAW,WAAW,UAAU,WAAW;AAAA,UAEjD;AAAA;AAAA,YACA,KAAK;AAAA,YAAO;AAAA;AAAA;AAAA,QANR,KAAK;AAAA,MAOZ;AAAA,IAEJ,CAAC;AAAA;AACH;AAGF,IAAM,YAA6D,CAAC;AAAA,EAClE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MACE;AAAA,EAAC;AAAA;AAAA,IACC,OAAO,QAAQ,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,OAAO,eAAe,CAAC,EAAE,EAAE;AAAA,IAC/D,UAAU;AAAA,IACV,UAAQ;AAAA,IACR;AAAA;AACF;AAGF,IAAM,4BAA4B;AAElC,IAAM,wBAAwB,CAAC,WAA6D;AAC1F,MAAI,WAAW,QAAS,QAAO;AAC/B,MAAI,WAAW,UAAW,QAAO;AACjC,SAAO;AACT;AAEA,IAAM,2BAA2B,CAAC,cAChC,UAAU,QAAQ,OAAO,CAAC,KAAK,MAAM,MAAM,UAAU,eAAe,CAAC,EAAE,QAAQ,CAAC,IAChF;AAEK,IAAM,aAAwC,CAAC;AAAA,EACpD;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe,CAAC;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AACF,MAAM;AACJ,QAAM,uBAAuB,YAAY,yBAAyB,SAAS,IAAI;AAE/E,SACE,iCACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,eAAc;AAAA,QACd,WAAW;AAAA,QACX,aAAY;AAAA,QACZ,aAAY;AAAA,QACZ,UAAU;AAAA,QACV,YAAW;AAAA,QACX,cAAc,uBAAuB,IAAI;AAAA,QAExC;AAAA,gBAAM,IAAI,CAAC,SAAS;AACnB,kBAAM,QAAQ,aAAa,KAAK,IAAI,aAAa,gBAAgB,YAAY;AAE7E,gBAAI,KAAK,OAAO,WAAW,aAAa,CAAC,sBAAsB;AAC7D,qBACE;AAAA,gBAAC;AAAA;AAAA,kBAEC,UAAS;AAAA,kBACT,SAAQ;AAAA,kBACR,gBAAgB,sBAAsB,KAAK,EAAE;AAAA,kBAE7C;AAAA,wCAAC,OAAI,MAAY,OAAc;AAAA,oBAC/B,oBAAC,aAAW,GAAG,WAAW;AAAA;AAAA;AAAA,gBANrB,KAAK;AAAA,cAOZ;AAAA,YAEJ;AAGA,kBAAM,WAAW,YAAY,KAAK,EAAE;AACpC,gBAAI,UAAU;AACZ,qBACE;AAAA,gBAAC;AAAA;AAAA,kBAEC,UAAS;AAAA,kBACT,SAAQ;AAAA,kBACR,gBAAgB,sBAAsB,KAAK,EAAE;AAAA,kBAE7C;AAAA,wCAAC,OAAI,MAAY,OAAc;AAAA,oBAC/B,oBAAC,eAAa,GAAG,UAAU;AAAA;AAAA;AAAA,gBANtB,KAAK;AAAA,cAOZ;AAAA,YAEJ;AAEA,mBAAO,oBAAC,OAAkB,MAAY,SAArB,KAAK,EAA8B;AAAA,UACtD,CAAC;AAAA,UACD,oBAAC,OAAI,UAAU,GAAG,gBAAe,YAC/B,8BAAC,QAAK,UAAQ,MAAE,cAAI,OAAO,IAAG,GAChC;AAAA;AAAA;AAAA,IACF;AAAA,IACC,aAAa,wBACZ,oBAAC,OACC,8BAAC,aAAW,GAAG,WAAW,QAAM,MAAC,GACnC;AAAA,KAEJ;AAEJ;","names":[]}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
isDomain
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-NXQAY5U7.js";
|
|
5
5
|
import {
|
|
6
6
|
findStack
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-35VOK7XE.js";
|
|
8
8
|
|
|
9
9
|
// src/cli/components/wizard/utils.ts
|
|
10
10
|
var DOMAIN_DISPLAY_NAMES = {
|
|
@@ -18,17 +18,6 @@ var DOMAIN_DISPLAY_NAMES = {
|
|
|
18
18
|
meta: "Meta",
|
|
19
19
|
shared: "Shared"
|
|
20
20
|
};
|
|
21
|
-
var BUILT_IN_DOMAIN_DESCRIPTIONS = {
|
|
22
|
-
web: "Frontend web applications",
|
|
23
|
-
api: "Backend APIs and services",
|
|
24
|
-
ai: "AI and LLM integrations",
|
|
25
|
-
cli: "Command-line tools",
|
|
26
|
-
mobile: "Mobile applications",
|
|
27
|
-
desktop: "Desktop applications",
|
|
28
|
-
infra: "CI/CD, deployment, and infrastructure",
|
|
29
|
-
meta: "Design patterns, code review, and research methodology",
|
|
30
|
-
shared: "Shared utilities and methodology"
|
|
31
|
-
};
|
|
32
21
|
function getDomainDisplayName(domain) {
|
|
33
22
|
return (isDomain(domain) ? DOMAIN_DISPLAY_NAMES[domain] : null) ?? domain.charAt(0).toUpperCase() + domain.slice(1);
|
|
34
23
|
}
|
|
@@ -40,8 +29,7 @@ function getStackName(stackId) {
|
|
|
40
29
|
}
|
|
41
30
|
|
|
42
31
|
export {
|
|
43
|
-
BUILT_IN_DOMAIN_DESCRIPTIONS,
|
|
44
32
|
getDomainDisplayName,
|
|
45
33
|
getStackName
|
|
46
34
|
};
|
|
47
|
-
//# sourceMappingURL=chunk-
|
|
35
|
+
//# sourceMappingURL=chunk-JFAUQDZM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/components/wizard/utils.ts"],"sourcesContent":["import { findStack } from \"../../lib/matrix/matrix-provider.js\";\nimport { orderDomains } from \"../../lib/wizard/index.js\";\nimport type { Domain } from \"../../types/index.js\";\nimport { isDomain } from \"../../utils/type-guards.js\";\n\nconst DOMAIN_DISPLAY_NAMES: Record<Domain, string> = {\n web: \"Web\",\n api: \"API\",\n ai: \"AI\",\n cli: \"CLI\",\n mobile: \"Mobile\",\n desktop: \"Desktop\",\n infra: \"Infrastructure\",\n meta: \"Meta\",\n shared: \"Shared\",\n};\n\nexport function getDomainDisplayName(domain: string): string {\n return (\n (isDomain(domain) ? DOMAIN_DISPLAY_NAMES[domain] : null) ??\n domain.charAt(0).toUpperCase() + domain.slice(1)\n );\n}\n\n/**\n * Display name of the selected stack, or `undefined` when the wizard is\n * building from scratch — the state each caller renders in its own words\n * (\"your custom stack\" in the tab dropdown, \"none\" in the summary panel).\n *\n * The lookup asserts. `selectedStackId` is written by `selectStack` and nothing\n * else: `createInitialState` starts it at `null`, neither hydration path\n * restores it from config, and `selectStack`'s only non-null argument is\n * `focusedStack.id` in `stack-selection.tsx`, taken from\n * `matrix.suggestedStacks` — the same array `findStack` searches. The matrix is\n * never rebuilt while the wizard is mounted (`initializeMatrix` runs during\n * startup loading; the wizard's own add/remove-source path writes config and\n * re-reads a summary). So a miss is a bug, not a stack to label with its raw id\n * — the same conclusion `wizard.tsx` already reaches for the same lookup.\n */\nexport function getStackName(stackId: string | null): string | undefined {\n if (!stackId) return undefined;\n const stack = findStack(stackId);\n if (!stack) throw new Error(`Stack not found: ${stackId}`);\n return stack.name;\n}\n\nexport { orderDomains };\n"],"mappings":";;;;;;;;;AAKA,IAAM,uBAA+C;AAAA,EACnD,KAAK;AAAA,EACL,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,OAAO;AAAA,EACP,MAAM;AAAA,EACN,QAAQ;AACV;AAEO,SAAS,qBAAqB,QAAwB;AAC3D,UACG,SAAS,MAAM,IAAI,qBAAqB,MAAM,IAAI,SACnD,OAAO,OAAO,CAAC,EAAE,YAAY,IAAI,OAAO,MAAM,CAAC;AAEnD;AAiBO,SAAS,aAAa,SAA4C;AACvE,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,QAAQ,UAAU,OAAO;AAC/B,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,oBAAoB,OAAO,EAAE;AACzD,SAAO,MAAM;AACf;","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
MIN_TERMINAL_SIZE
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-R54KIGN2.js";
|
|
5
5
|
|
|
6
6
|
// src/cli/utils/terminal.ts
|
|
7
7
|
function clearTerminalScreen() {
|
|
@@ -20,4 +20,4 @@ export {
|
|
|
20
20
|
isTerminalLargeEnough,
|
|
21
21
|
formatTerminalTooSmallMessage
|
|
22
22
|
};
|
|
23
|
-
//# sourceMappingURL=chunk-
|
|
23
|
+
//# sourceMappingURL=chunk-K3O3YQYG.js.map
|