@agents-inc/cli 0.32.1
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 +462 -0
- package/LICENSE +21 -0
- package/README.md +179 -0
- package/config/skills-matrix.yaml +926 -0
- package/config/stacks.yaml +2186 -0
- package/dist/chunk-3ZOIOVKT.js +365 -0
- package/dist/chunk-3ZOIOVKT.js.map +1 -0
- package/dist/chunk-4RAY5AOI.js +78 -0
- package/dist/chunk-4RAY5AOI.js.map +1 -0
- package/dist/chunk-5PIKNCZX.js +234 -0
- package/dist/chunk-5PIKNCZX.js.map +1 -0
- package/dist/chunk-66UDJBF6.js +96 -0
- package/dist/chunk-66UDJBF6.js.map +1 -0
- package/dist/chunk-7SOPVGDV.js +24 -0
- package/dist/chunk-7SOPVGDV.js.map +1 -0
- package/dist/chunk-A27LOC4Z.js +95 -0
- package/dist/chunk-A27LOC4Z.js.map +1 -0
- package/dist/chunk-B2UBHA66.js +301 -0
- package/dist/chunk-B2UBHA66.js.map +1 -0
- package/dist/chunk-BZN2Z5P7.js +882 -0
- package/dist/chunk-BZN2Z5P7.js.map +1 -0
- package/dist/chunk-BZQBJP34.js +186 -0
- package/dist/chunk-BZQBJP34.js.map +1 -0
- package/dist/chunk-DC5AK3LW.js +105 -0
- package/dist/chunk-DC5AK3LW.js.map +1 -0
- package/dist/chunk-DHET7RCE.js +50 -0
- package/dist/chunk-DHET7RCE.js.map +1 -0
- package/dist/chunk-EMJ2ZKS7.js +346 -0
- package/dist/chunk-EMJ2ZKS7.js.map +1 -0
- package/dist/chunk-FJQRVFMB.js +48 -0
- package/dist/chunk-FJQRVFMB.js.map +1 -0
- package/dist/chunk-FZGYSLJL.js +85 -0
- package/dist/chunk-FZGYSLJL.js.map +1 -0
- package/dist/chunk-H566H3MQ.js +87 -0
- package/dist/chunk-H566H3MQ.js.map +1 -0
- package/dist/chunk-IYG2LAIM.js +90 -0
- package/dist/chunk-IYG2LAIM.js.map +1 -0
- package/dist/chunk-IZZ4IIEG.js +29 -0
- package/dist/chunk-IZZ4IIEG.js.map +1 -0
- package/dist/chunk-JMVWYAHT.js +63 -0
- package/dist/chunk-JMVWYAHT.js.map +1 -0
- package/dist/chunk-LAPCUV4D.js +191 -0
- package/dist/chunk-LAPCUV4D.js.map +1 -0
- package/dist/chunk-LGUI3PMO.js +109 -0
- package/dist/chunk-LGUI3PMO.js.map +1 -0
- package/dist/chunk-MM7NK5N2.js +4542 -0
- package/dist/chunk-MM7NK5N2.js.map +1 -0
- package/dist/chunk-N6S7ZRIL.js +31 -0
- package/dist/chunk-N6S7ZRIL.js.map +1 -0
- package/dist/chunk-O4D67NN7.js +24 -0
- package/dist/chunk-O4D67NN7.js.map +1 -0
- package/dist/chunk-ODUOU55D.js +56 -0
- package/dist/chunk-ODUOU55D.js.map +1 -0
- package/dist/chunk-OGJIZ6QH.js +497 -0
- package/dist/chunk-OGJIZ6QH.js.map +1 -0
- package/dist/chunk-OMV7TLWD.js +340 -0
- package/dist/chunk-OMV7TLWD.js.map +1 -0
- package/dist/chunk-PBEHPQLK.js +146 -0
- package/dist/chunk-PBEHPQLK.js.map +1 -0
- package/dist/chunk-QPTOIZAT.js +32 -0
- package/dist/chunk-QPTOIZAT.js.map +1 -0
- package/dist/chunk-R3XFQKPG.js +111 -0
- package/dist/chunk-R3XFQKPG.js.map +1 -0
- package/dist/chunk-R74PZWQS.js +69 -0
- package/dist/chunk-R74PZWQS.js.map +1 -0
- package/dist/chunk-SO22IQPY.js +45 -0
- package/dist/chunk-SO22IQPY.js.map +1 -0
- package/dist/chunk-T4EXUIBY.js +19 -0
- package/dist/chunk-T4EXUIBY.js.map +1 -0
- package/dist/chunk-U3IGFMCY.js +31 -0
- package/dist/chunk-U3IGFMCY.js.map +1 -0
- package/dist/chunk-UICL22RT.js +318 -0
- package/dist/chunk-UICL22RT.js.map +1 -0
- package/dist/chunk-UX2H2K2G.js +183 -0
- package/dist/chunk-UX2H2K2G.js.map +1 -0
- package/dist/chunk-W2ZSCZ2U.js +93 -0
- package/dist/chunk-W2ZSCZ2U.js.map +1 -0
- package/dist/chunk-WEUVWHMA.js +189 -0
- package/dist/chunk-WEUVWHMA.js.map +1 -0
- package/dist/chunk-XY3XDVMI.js +15599 -0
- package/dist/chunk-XY3XDVMI.js.map +1 -0
- package/dist/chunk-YND42IXK.js +233 -0
- package/dist/chunk-YND42IXK.js.map +1 -0
- package/dist/chunk-YZTWZVGX.js +41 -0
- package/dist/chunk-YZTWZVGX.js.map +1 -0
- package/dist/chunk-Z4TWOP3H.js +81 -0
- package/dist/chunk-Z4TWOP3H.js.map +1 -0
- package/dist/cli/defaults/agent-mappings.yaml +271 -0
- package/dist/commands/build/marketplace.js +252 -0
- package/dist/commands/build/marketplace.js.map +1 -0
- package/dist/commands/build/plugins.js +114 -0
- package/dist/commands/build/plugins.js.map +1 -0
- package/dist/commands/build/stack.js +153 -0
- package/dist/commands/build/stack.js.map +1 -0
- package/dist/commands/compile.js +354 -0
- package/dist/commands/compile.js.map +1 -0
- package/dist/commands/config/get.js +61 -0
- package/dist/commands/config/get.js.map +1 -0
- package/dist/commands/config/index.js +23 -0
- package/dist/commands/config/index.js.map +1 -0
- package/dist/commands/config/path.js +34 -0
- package/dist/commands/config/path.js.map +1 -0
- package/dist/commands/config/set-project.js +61 -0
- package/dist/commands/config/set-project.js.map +1 -0
- package/dist/commands/config/show.js +14 -0
- package/dist/commands/config/show.js.map +1 -0
- package/dist/commands/config/unset-project.js +57 -0
- package/dist/commands/config/unset-project.js.map +1 -0
- package/dist/commands/diff.js +742 -0
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/doctor.js +370 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/edit.js +301 -0
- package/dist/commands/edit.js.map +1 -0
- package/dist/commands/eject.js +262 -0
- package/dist/commands/eject.js.map +1 -0
- package/dist/commands/import/skill.js +361 -0
- package/dist/commands/import/skill.js.map +1 -0
- package/dist/commands/info.js +217 -0
- package/dist/commands/info.js.map +1 -0
- package/dist/commands/init.js +443 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/list.js +49 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/new/agent.js +224 -0
- package/dist/commands/new/agent.js.map +1 -0
- package/dist/commands/new/skill.js +199 -0
- package/dist/commands/new/skill.js.map +1 -0
- package/dist/commands/outdated.js +176 -0
- package/dist/commands/outdated.js.map +1 -0
- package/dist/commands/search.js +288 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/uninstall.js +302 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/commands/update.js +304 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/validate.js +389 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/commands/version/bump.js +79 -0
- package/dist/commands/version/bump.js.map +1 -0
- package/dist/commands/version/index.js +54 -0
- package/dist/commands/version/index.js.map +1 -0
- package/dist/commands/version/set.js +86 -0
- package/dist/commands/version/set.js.map +1 -0
- package/dist/commands/version/show.js +54 -0
- package/dist/commands/version/show.js.map +1 -0
- package/dist/components/common/confirm.js +9 -0
- package/dist/components/common/confirm.js.map +1 -0
- package/dist/components/common/confirm.test.js +203 -0
- package/dist/components/common/confirm.test.js.map +1 -0
- package/dist/components/common/message.js +20 -0
- package/dist/components/common/message.js.map +1 -0
- package/dist/components/common/spinner.js +14 -0
- package/dist/components/common/spinner.js.map +1 -0
- package/dist/components/skill-search/skill-search.js +12 -0
- package/dist/components/skill-search/skill-search.js.map +1 -0
- package/dist/components/wizard/category-grid.js +11 -0
- package/dist/components/wizard/category-grid.js.map +1 -0
- package/dist/components/wizard/category-grid.test.js +997 -0
- package/dist/components/wizard/category-grid.test.js.map +1 -0
- package/dist/components/wizard/domain-selection.js +14 -0
- package/dist/components/wizard/domain-selection.js.map +1 -0
- package/dist/components/wizard/help-modal.js +10 -0
- package/dist/components/wizard/help-modal.js.map +1 -0
- package/dist/components/wizard/menu-item.js +10 -0
- package/dist/components/wizard/menu-item.js.map +1 -0
- package/dist/components/wizard/search-modal.js +11 -0
- package/dist/components/wizard/search-modal.js.map +1 -0
- package/dist/components/wizard/search-modal.test.js +218 -0
- package/dist/components/wizard/search-modal.test.js.map +1 -0
- package/dist/components/wizard/section-progress.js +10 -0
- package/dist/components/wizard/section-progress.js.map +1 -0
- package/dist/components/wizard/section-progress.test.js +192 -0
- package/dist/components/wizard/section-progress.test.js.map +1 -0
- package/dist/components/wizard/source-grid.js +14 -0
- package/dist/components/wizard/source-grid.js.map +1 -0
- package/dist/components/wizard/source-grid.test.js +504 -0
- package/dist/components/wizard/source-grid.test.js.map +1 -0
- package/dist/components/wizard/stack-selection.js +17 -0
- package/dist/components/wizard/stack-selection.js.map +1 -0
- package/dist/components/wizard/step-build.js +17 -0
- package/dist/components/wizard/step-build.js.map +1 -0
- package/dist/components/wizard/step-build.test.js +600 -0
- package/dist/components/wizard/step-build.test.js.map +1 -0
- package/dist/components/wizard/step-confirm.js +12 -0
- package/dist/components/wizard/step-confirm.js.map +1 -0
- package/dist/components/wizard/step-confirm.test.js +366 -0
- package/dist/components/wizard/step-confirm.test.js.map +1 -0
- package/dist/components/wizard/step-refine.js +10 -0
- package/dist/components/wizard/step-refine.js.map +1 -0
- package/dist/components/wizard/step-refine.test.js +237 -0
- package/dist/components/wizard/step-refine.test.js.map +1 -0
- package/dist/components/wizard/step-settings.js +17 -0
- package/dist/components/wizard/step-settings.js.map +1 -0
- package/dist/components/wizard/step-settings.test.js +243 -0
- package/dist/components/wizard/step-settings.test.js.map +1 -0
- package/dist/components/wizard/step-sources.js +20 -0
- package/dist/components/wizard/step-sources.js.map +1 -0
- package/dist/components/wizard/step-sources.test.js +294 -0
- package/dist/components/wizard/step-sources.test.js.map +1 -0
- package/dist/components/wizard/step-stack.js +19 -0
- package/dist/components/wizard/step-stack.js.map +1 -0
- package/dist/components/wizard/step-stack.test.js +357 -0
- package/dist/components/wizard/step-stack.test.js.map +1 -0
- package/dist/components/wizard/view-title.js +10 -0
- package/dist/components/wizard/view-title.js.map +1 -0
- package/dist/components/wizard/wizard-layout.js +16 -0
- package/dist/components/wizard/wizard-layout.js.map +1 -0
- package/dist/components/wizard/wizard-tabs.js +14 -0
- package/dist/components/wizard/wizard-tabs.js.map +1 -0
- package/dist/components/wizard/wizard-tabs.test.js +294 -0
- package/dist/components/wizard/wizard-tabs.test.js.map +1 -0
- package/dist/components/wizard/wizard.js +35 -0
- package/dist/components/wizard/wizard.js.map +1 -0
- package/dist/config/skills-matrix.yaml +926 -0
- package/dist/config/stacks.yaml +2186 -0
- package/dist/hooks/init.js +40 -0
- package/dist/hooks/init.js.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/magic-string.es-RGXYGAW3.js +1316 -0
- package/dist/magic-string.es-RGXYGAW3.js.map +1 -0
- package/dist/source-manager-SBPPLOQQ.js +16 -0
- package/dist/source-manager-SBPPLOQQ.js.map +1 -0
- package/dist/src/agents/_templates/agent.liquid +140 -0
- package/dist/src/agents/developer/api-developer/agent.yaml +12 -0
- package/dist/src/agents/developer/api-developer/critical-reminders.md +23 -0
- package/dist/src/agents/developer/api-developer/critical-requirements.md +11 -0
- package/dist/src/agents/developer/api-developer/examples.md +72 -0
- package/dist/src/agents/developer/api-developer/intro.md +22 -0
- package/dist/src/agents/developer/api-developer/output-format.md +359 -0
- package/dist/src/agents/developer/api-developer/workflow.md +471 -0
- package/dist/src/agents/developer/cli-developer/agent.yaml +12 -0
- package/dist/src/agents/developer/cli-developer/critical-reminders.md +28 -0
- package/dist/src/agents/developer/cli-developer/critical-requirements.md +15 -0
- package/dist/src/agents/developer/cli-developer/examples.md +68 -0
- package/dist/src/agents/developer/cli-developer/intro.md +23 -0
- package/dist/src/agents/developer/cli-developer/output-format.md +216 -0
- package/dist/src/agents/developer/cli-developer/workflow.md +509 -0
- package/dist/src/agents/developer/web-architecture/agent.yaml +12 -0
- package/dist/src/agents/developer/web-architecture/critical-reminders.md +27 -0
- package/dist/src/agents/developer/web-architecture/critical-requirements.md +35 -0
- package/dist/src/agents/developer/web-architecture/examples.md +187 -0
- package/dist/src/agents/developer/web-architecture/intro.md +35 -0
- package/dist/src/agents/developer/web-architecture/output-format.md +261 -0
- package/dist/src/agents/developer/web-architecture/workflow.md +599 -0
- package/dist/src/agents/developer/web-developer/agent.yaml +12 -0
- package/dist/src/agents/developer/web-developer/critical-reminders.md +17 -0
- package/dist/src/agents/developer/web-developer/critical-requirements.md +15 -0
- package/dist/src/agents/developer/web-developer/examples.md +109 -0
- package/dist/src/agents/developer/web-developer/intro.md +5 -0
- package/dist/src/agents/developer/web-developer/output-format.md +213 -0
- package/dist/src/agents/developer/web-developer/workflow.md +459 -0
- package/dist/src/agents/meta/agent-summoner/agent.yaml +12 -0
- package/dist/src/agents/meta/agent-summoner/critical-reminders.md +31 -0
- package/dist/src/agents/meta/agent-summoner/critical-requirements.md +27 -0
- package/dist/src/agents/meta/agent-summoner/examples.md +176 -0
- package/dist/src/agents/meta/agent-summoner/intro.md +9 -0
- package/dist/src/agents/meta/agent-summoner/output-format.md +115 -0
- package/dist/src/agents/meta/agent-summoner/workflow.md +1540 -0
- package/dist/src/agents/meta/documentor/agent.yaml +11 -0
- package/dist/src/agents/meta/documentor/critical-reminders.md +23 -0
- package/dist/src/agents/meta/documentor/critical-requirements.md +13 -0
- package/dist/src/agents/meta/documentor/examples.md +147 -0
- package/dist/src/agents/meta/documentor/intro.md +11 -0
- package/dist/src/agents/meta/documentor/output-format.md +237 -0
- package/dist/src/agents/meta/documentor/workflow.md +1271 -0
- package/dist/src/agents/meta/skill-summoner/agent.yaml +13 -0
- package/dist/src/agents/meta/skill-summoner/critical-reminders.md +73 -0
- package/dist/src/agents/meta/skill-summoner/critical-requirements.md +62 -0
- package/dist/src/agents/meta/skill-summoner/examples.md +116 -0
- package/dist/src/agents/meta/skill-summoner/intro.md +5 -0
- package/dist/src/agents/meta/skill-summoner/output-format.md +279 -0
- package/dist/src/agents/meta/skill-summoner/workflow.md +1485 -0
- package/dist/src/agents/migration/cli-migrator/agent.yaml +12 -0
- package/dist/src/agents/migration/cli-migrator/anti-patterns.md +158 -0
- package/dist/src/agents/migration/cli-migrator/conversion-mappings.md +63 -0
- package/dist/src/agents/migration/cli-migrator/critical-reminders.md +17 -0
- package/dist/src/agents/migration/cli-migrator/critical-requirements.md +13 -0
- package/dist/src/agents/migration/cli-migrator/intro.md +15 -0
- package/dist/src/agents/migration/cli-migrator/output-format.md +164 -0
- package/dist/src/agents/migration/cli-migrator/workflow.md +230 -0
- package/dist/src/agents/pattern/pattern-scout/agent.yaml +10 -0
- package/dist/src/agents/pattern/pattern-scout/critical-reminders.md +58 -0
- package/dist/src/agents/pattern/pattern-scout/critical-requirements.md +17 -0
- package/dist/src/agents/pattern/pattern-scout/examples.md +93 -0
- package/dist/src/agents/pattern/pattern-scout/intro.md +3 -0
- package/dist/src/agents/pattern/pattern-scout/output-format.md +196 -0
- package/dist/src/agents/pattern/pattern-scout/workflow.md +1901 -0
- package/dist/src/agents/pattern/web-pattern-critique/agent.yaml +12 -0
- package/dist/src/agents/pattern/web-pattern-critique/critical-reminders.md +13 -0
- package/dist/src/agents/pattern/web-pattern-critique/critical-requirements.md +11 -0
- package/dist/src/agents/pattern/web-pattern-critique/examples.md +56 -0
- package/dist/src/agents/pattern/web-pattern-critique/intro.md +5 -0
- package/dist/src/agents/pattern/web-pattern-critique/output-format.md +257 -0
- package/dist/src/agents/pattern/web-pattern-critique/workflow.md +674 -0
- package/dist/src/agents/planning/web-pm/agent.yaml +12 -0
- package/dist/src/agents/planning/web-pm/critical-reminders.md +21 -0
- package/dist/src/agents/planning/web-pm/critical-requirements.md +17 -0
- package/dist/src/agents/planning/web-pm/examples.md +85 -0
- package/dist/src/agents/planning/web-pm/intro.md +3 -0
- package/dist/src/agents/planning/web-pm/output-format.md +228 -0
- package/dist/src/agents/planning/web-pm/workflow.md +393 -0
- package/dist/src/agents/researcher/api-researcher/agent.yaml +10 -0
- package/dist/src/agents/researcher/api-researcher/critical-reminders.md +27 -0
- package/dist/src/agents/researcher/api-researcher/critical-requirements.md +13 -0
- package/dist/src/agents/researcher/api-researcher/examples.md +116 -0
- package/dist/src/agents/researcher/api-researcher/intro.md +32 -0
- package/dist/src/agents/researcher/api-researcher/output-format.md +135 -0
- package/dist/src/agents/researcher/api-researcher/workflow.md +261 -0
- package/dist/src/agents/researcher/web-researcher/agent.yaml +10 -0
- package/dist/src/agents/researcher/web-researcher/critical-reminders.md +23 -0
- package/dist/src/agents/researcher/web-researcher/critical-requirements.md +11 -0
- package/dist/src/agents/researcher/web-researcher/examples.md +126 -0
- package/dist/src/agents/researcher/web-researcher/intro.md +31 -0
- package/dist/src/agents/researcher/web-researcher/output-format.md +112 -0
- package/dist/src/agents/researcher/web-researcher/workflow.md +322 -0
- package/dist/src/agents/reviewer/api-reviewer/agent.yaml +12 -0
- package/dist/src/agents/reviewer/api-reviewer/critical-reminders.md +16 -0
- package/dist/src/agents/reviewer/api-reviewer/critical-requirements.md +13 -0
- package/dist/src/agents/reviewer/api-reviewer/examples.md +54 -0
- package/dist/src/agents/reviewer/api-reviewer/intro.md +22 -0
- package/dist/src/agents/reviewer/api-reviewer/output-format.md +288 -0
- package/dist/src/agents/reviewer/api-reviewer/workflow.md +369 -0
- package/dist/src/agents/reviewer/cli-reviewer/agent.yaml +12 -0
- package/dist/src/agents/reviewer/cli-reviewer/critical-reminders.md +17 -0
- package/dist/src/agents/reviewer/cli-reviewer/critical-requirements.md +13 -0
- package/dist/src/agents/reviewer/cli-reviewer/examples.md +83 -0
- package/dist/src/agents/reviewer/cli-reviewer/intro.md +21 -0
- package/dist/src/agents/reviewer/cli-reviewer/output-format.md +330 -0
- package/dist/src/agents/reviewer/cli-reviewer/workflow.md +294 -0
- package/dist/src/agents/reviewer/web-reviewer/agent.yaml +12 -0
- package/dist/src/agents/reviewer/web-reviewer/critical-reminders.md +17 -0
- package/dist/src/agents/reviewer/web-reviewer/critical-requirements.md +11 -0
- package/dist/src/agents/reviewer/web-reviewer/examples.md +79 -0
- package/dist/src/agents/reviewer/web-reviewer/intro.md +20 -0
- package/dist/src/agents/reviewer/web-reviewer/output-format.md +253 -0
- package/dist/src/agents/reviewer/web-reviewer/workflow.md +228 -0
- package/dist/src/agents/tester/cli-tester/agent.yaml +12 -0
- package/dist/src/agents/tester/cli-tester/critical-reminders.md +19 -0
- package/dist/src/agents/tester/cli-tester/critical-requirements.md +17 -0
- package/dist/src/agents/tester/cli-tester/examples.md +80 -0
- package/dist/src/agents/tester/cli-tester/intro.md +19 -0
- package/dist/src/agents/tester/cli-tester/output-format.md +232 -0
- package/dist/src/agents/tester/cli-tester/workflow.md +304 -0
- package/dist/src/agents/tester/web-tester/agent.yaml +12 -0
- package/dist/src/agents/tester/web-tester/critical-reminders.md +15 -0
- package/dist/src/agents/tester/web-tester/critical-requirements.md +11 -0
- package/dist/src/agents/tester/web-tester/examples.md +68 -0
- package/dist/src/agents/tester/web-tester/intro.md +18 -0
- package/dist/src/agents/tester/web-tester/output-format.md +252 -0
- package/dist/src/agents/tester/web-tester/workflow.md +507 -0
- package/dist/stores/wizard-store.js +13 -0
- package/dist/stores/wizard-store.js.map +1 -0
- package/dist/stores/wizard-store.test.js +689 -0
- package/dist/stores/wizard-store.test.js.map +1 -0
- package/package.json +134 -0
- package/src/agents/_templates/agent.liquid +140 -0
- package/src/agents/developer/api-developer/agent.yaml +12 -0
- package/src/agents/developer/api-developer/critical-reminders.md +23 -0
- package/src/agents/developer/api-developer/critical-requirements.md +11 -0
- package/src/agents/developer/api-developer/examples.md +72 -0
- package/src/agents/developer/api-developer/intro.md +22 -0
- package/src/agents/developer/api-developer/output-format.md +359 -0
- package/src/agents/developer/api-developer/workflow.md +471 -0
- package/src/agents/developer/cli-developer/agent.yaml +12 -0
- package/src/agents/developer/cli-developer/critical-reminders.md +28 -0
- package/src/agents/developer/cli-developer/critical-requirements.md +15 -0
- package/src/agents/developer/cli-developer/examples.md +68 -0
- package/src/agents/developer/cli-developer/intro.md +23 -0
- package/src/agents/developer/cli-developer/output-format.md +216 -0
- package/src/agents/developer/cli-developer/workflow.md +509 -0
- package/src/agents/developer/web-architecture/agent.yaml +12 -0
- package/src/agents/developer/web-architecture/critical-reminders.md +27 -0
- package/src/agents/developer/web-architecture/critical-requirements.md +35 -0
- package/src/agents/developer/web-architecture/examples.md +187 -0
- package/src/agents/developer/web-architecture/intro.md +35 -0
- package/src/agents/developer/web-architecture/output-format.md +261 -0
- package/src/agents/developer/web-architecture/workflow.md +599 -0
- package/src/agents/developer/web-developer/agent.yaml +12 -0
- package/src/agents/developer/web-developer/critical-reminders.md +17 -0
- package/src/agents/developer/web-developer/critical-requirements.md +15 -0
- package/src/agents/developer/web-developer/examples.md +109 -0
- package/src/agents/developer/web-developer/intro.md +5 -0
- package/src/agents/developer/web-developer/output-format.md +213 -0
- package/src/agents/developer/web-developer/workflow.md +459 -0
- package/src/agents/meta/agent-summoner/agent.yaml +12 -0
- package/src/agents/meta/agent-summoner/critical-reminders.md +31 -0
- package/src/agents/meta/agent-summoner/critical-requirements.md +27 -0
- package/src/agents/meta/agent-summoner/examples.md +176 -0
- package/src/agents/meta/agent-summoner/intro.md +9 -0
- package/src/agents/meta/agent-summoner/output-format.md +115 -0
- package/src/agents/meta/agent-summoner/workflow.md +1540 -0
- package/src/agents/meta/documentor/agent.yaml +11 -0
- package/src/agents/meta/documentor/critical-reminders.md +23 -0
- package/src/agents/meta/documentor/critical-requirements.md +13 -0
- package/src/agents/meta/documentor/examples.md +147 -0
- package/src/agents/meta/documentor/intro.md +11 -0
- package/src/agents/meta/documentor/output-format.md +237 -0
- package/src/agents/meta/documentor/workflow.md +1271 -0
- package/src/agents/meta/skill-summoner/agent.yaml +13 -0
- package/src/agents/meta/skill-summoner/critical-reminders.md +73 -0
- package/src/agents/meta/skill-summoner/critical-requirements.md +62 -0
- package/src/agents/meta/skill-summoner/examples.md +116 -0
- package/src/agents/meta/skill-summoner/intro.md +5 -0
- package/src/agents/meta/skill-summoner/output-format.md +279 -0
- package/src/agents/meta/skill-summoner/workflow.md +1485 -0
- package/src/agents/migration/cli-migrator/agent.yaml +12 -0
- package/src/agents/migration/cli-migrator/anti-patterns.md +158 -0
- package/src/agents/migration/cli-migrator/conversion-mappings.md +63 -0
- package/src/agents/migration/cli-migrator/critical-reminders.md +17 -0
- package/src/agents/migration/cli-migrator/critical-requirements.md +13 -0
- package/src/agents/migration/cli-migrator/intro.md +15 -0
- package/src/agents/migration/cli-migrator/output-format.md +164 -0
- package/src/agents/migration/cli-migrator/workflow.md +230 -0
- package/src/agents/pattern/pattern-scout/agent.yaml +10 -0
- package/src/agents/pattern/pattern-scout/critical-reminders.md +58 -0
- package/src/agents/pattern/pattern-scout/critical-requirements.md +17 -0
- package/src/agents/pattern/pattern-scout/examples.md +93 -0
- package/src/agents/pattern/pattern-scout/intro.md +3 -0
- package/src/agents/pattern/pattern-scout/output-format.md +196 -0
- package/src/agents/pattern/pattern-scout/workflow.md +1901 -0
- package/src/agents/pattern/web-pattern-critique/agent.yaml +12 -0
- package/src/agents/pattern/web-pattern-critique/critical-reminders.md +13 -0
- package/src/agents/pattern/web-pattern-critique/critical-requirements.md +11 -0
- package/src/agents/pattern/web-pattern-critique/examples.md +56 -0
- package/src/agents/pattern/web-pattern-critique/intro.md +5 -0
- package/src/agents/pattern/web-pattern-critique/output-format.md +257 -0
- package/src/agents/pattern/web-pattern-critique/workflow.md +674 -0
- package/src/agents/planning/web-pm/agent.yaml +12 -0
- package/src/agents/planning/web-pm/critical-reminders.md +21 -0
- package/src/agents/planning/web-pm/critical-requirements.md +17 -0
- package/src/agents/planning/web-pm/examples.md +85 -0
- package/src/agents/planning/web-pm/intro.md +3 -0
- package/src/agents/planning/web-pm/output-format.md +228 -0
- package/src/agents/planning/web-pm/workflow.md +393 -0
- package/src/agents/researcher/api-researcher/agent.yaml +10 -0
- package/src/agents/researcher/api-researcher/critical-reminders.md +27 -0
- package/src/agents/researcher/api-researcher/critical-requirements.md +13 -0
- package/src/agents/researcher/api-researcher/examples.md +116 -0
- package/src/agents/researcher/api-researcher/intro.md +32 -0
- package/src/agents/researcher/api-researcher/output-format.md +135 -0
- package/src/agents/researcher/api-researcher/workflow.md +261 -0
- package/src/agents/researcher/web-researcher/agent.yaml +10 -0
- package/src/agents/researcher/web-researcher/critical-reminders.md +23 -0
- package/src/agents/researcher/web-researcher/critical-requirements.md +11 -0
- package/src/agents/researcher/web-researcher/examples.md +126 -0
- package/src/agents/researcher/web-researcher/intro.md +31 -0
- package/src/agents/researcher/web-researcher/output-format.md +112 -0
- package/src/agents/researcher/web-researcher/workflow.md +322 -0
- package/src/agents/reviewer/api-reviewer/agent.yaml +12 -0
- package/src/agents/reviewer/api-reviewer/critical-reminders.md +16 -0
- package/src/agents/reviewer/api-reviewer/critical-requirements.md +13 -0
- package/src/agents/reviewer/api-reviewer/examples.md +54 -0
- package/src/agents/reviewer/api-reviewer/intro.md +22 -0
- package/src/agents/reviewer/api-reviewer/output-format.md +288 -0
- package/src/agents/reviewer/api-reviewer/workflow.md +369 -0
- package/src/agents/reviewer/cli-reviewer/agent.yaml +12 -0
- package/src/agents/reviewer/cli-reviewer/critical-reminders.md +17 -0
- package/src/agents/reviewer/cli-reviewer/critical-requirements.md +13 -0
- package/src/agents/reviewer/cli-reviewer/examples.md +83 -0
- package/src/agents/reviewer/cli-reviewer/intro.md +21 -0
- package/src/agents/reviewer/cli-reviewer/output-format.md +330 -0
- package/src/agents/reviewer/cli-reviewer/workflow.md +294 -0
- package/src/agents/reviewer/web-reviewer/agent.yaml +12 -0
- package/src/agents/reviewer/web-reviewer/critical-reminders.md +17 -0
- package/src/agents/reviewer/web-reviewer/critical-requirements.md +11 -0
- package/src/agents/reviewer/web-reviewer/examples.md +79 -0
- package/src/agents/reviewer/web-reviewer/intro.md +20 -0
- package/src/agents/reviewer/web-reviewer/output-format.md +253 -0
- package/src/agents/reviewer/web-reviewer/workflow.md +228 -0
- package/src/agents/tester/cli-tester/agent.yaml +12 -0
- package/src/agents/tester/cli-tester/critical-reminders.md +19 -0
- package/src/agents/tester/cli-tester/critical-requirements.md +17 -0
- package/src/agents/tester/cli-tester/examples.md +80 -0
- package/src/agents/tester/cli-tester/intro.md +19 -0
- package/src/agents/tester/cli-tester/output-format.md +232 -0
- package/src/agents/tester/cli-tester/workflow.md +304 -0
- package/src/agents/tester/web-tester/agent.yaml +12 -0
- package/src/agents/tester/web-tester/critical-reminders.md +15 -0
- package/src/agents/tester/web-tester/critical-requirements.md +11 -0
- package/src/agents/tester/web-tester/examples.md +68 -0
- package/src/agents/tester/web-tester/intro.md +18 -0
- package/src/agents/tester/web-tester/output-format.md +252 -0
- package/src/agents/tester/web-tester/workflow.md +507 -0
- package/src/schemas/agent-frontmatter.schema.json +84 -0
- package/src/schemas/agent.schema.json +93 -0
- package/src/schemas/hooks.schema.json +47 -0
- package/src/schemas/marketplace.schema.json +119 -0
- package/src/schemas/metadata.schema.json +113 -0
- package/src/schemas/plugin.schema.json +130 -0
- package/src/schemas/project-config.schema.json +125 -0
- package/src/schemas/project-source-config.schema.json +81 -0
- package/src/schemas/skill-frontmatter.schema.json +42 -0
- package/src/schemas/skills-matrix.schema.json +467 -0
- package/src/schemas/stack.schema.json +191 -0
- package/src/schemas/stacks.schema.json +111 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/components/wizard/category-grid.tsx","../src/cli/components/hooks/use-virtual-scroll.ts","../src/cli/components/hooks/use-category-grid-input.ts"],"sourcesContent":["import React, { useCallback, useMemo } from \"react\";\n\nimport { Box, Text } from \"ink\";\nimport { sortBy } from \"remeda\";\n\nimport type { SkillId, Subcategory } from \"../../types/index.js\";\nimport { CLI_COLORS, SCROLL_VIEWPORT, UI_SYMBOLS } from \"../../consts.js\";\nimport { useVirtualScroll } from \"../hooks/use-virtual-scroll.js\";\nimport {\n findValidStartColumn,\n isSectionLocked,\n useCategoryGridInput,\n} from \"../hooks/use-category-grid-input.js\";\nimport { useFocusedListItem } from \"../hooks/use-focused-list-item.js\";\n\nexport type OptionState = \"normal\" | \"recommended\" | \"discouraged\" | \"disabled\";\n\nexport type CategoryOption = {\n id: SkillId;\n label: string;\n state: OptionState;\n stateReason?: string;\n selected: boolean;\n local?: boolean;\n installed?: boolean;\n};\n\nexport type CategoryRow = {\n id: Subcategory;\n displayName: string;\n required: boolean;\n exclusive: boolean;\n options: CategoryOption[];\n};\n\nexport type CategoryGridProps = {\n categories: CategoryRow[];\n showDescriptions: boolean;\n expertMode: boolean;\n onToggle: (categoryId: Subcategory, technologyId: SkillId) => void;\n onToggleDescriptions: () => void;\n /** Optional initial focus row (default: 0). Use with React `key` to reset. */\n defaultFocusedRow?: number;\n /** Optional initial focus col (default: 0). Use with React `key` to reset. */\n defaultFocusedCol?: number;\n /** Optional callback fired whenever the focused position changes */\n onFocusChange?: (row: number, col: number) => void;\n /** Available height in terminal rows for the category list. When undefined, all categories render. */\n availableHeight?: number;\n /** Terminal width in columns, used for tag wrapping estimation. */\n terminalWidth?: number;\n};\n\nconst SYMBOL_REQUIRED = \"*\";\n\n// Recommended first, discouraged last. Expert mode preserves original order.\nconst sortOptions = (options: CategoryOption[], expertMode: boolean): CategoryOption[] => {\n if (expertMode) {\n return options;\n }\n\n const stateOrder: Record<OptionState, number> = {\n recommended: 0,\n normal: 1,\n discouraged: 2,\n disabled: 3,\n };\n\n return sortBy([...options], (o) => stateOrder[o.state]);\n};\n\nconst findNextValidOption = (\n options: CategoryOption[],\n currentIndex: number,\n direction: 1 | -1,\n wrap = true,\n): number => {\n const length = options.length;\n if (length === 0) return currentIndex;\n\n let index = currentIndex;\n let attempts = 0;\n\n while (attempts < length) {\n index += direction;\n\n if (wrap) {\n if (index < 0) index = length - 1;\n if (index >= length) index = 0;\n } else {\n if (index < 0) index = 0;\n if (index >= length) index = length - 1;\n }\n\n if (options[index] && options[index].state !== \"disabled\") {\n return index;\n }\n\n attempts++;\n }\n\n return currentIndex;\n};\n\ntype SkillTagProps = {\n option: CategoryOption;\n isFocused: boolean;\n isLocked: boolean;\n};\n\nconst getStateSuffix = (state: OptionState, isLocked: boolean): string | null => {\n if (isLocked || state === \"disabled\") return \"(disabled)\";\n if (state === \"recommended\") return \"(recommended)\";\n if (state === \"discouraged\") return \"(discouraged)\";\n return null;\n};\n\nconst getStateSymbol = (option: CategoryOption, isLocked: boolean): string => {\n if (isLocked || option.state === \"disabled\") return UI_SYMBOLS.DISABLED;\n if (option.selected) return UI_SYMBOLS.SELECTED;\n if (option.state === \"discouraged\") return UI_SYMBOLS.DISCOURAGED;\n return UI_SYMBOLS.UNSELECTED;\n};\n\nconst SkillTag: React.FC<SkillTagProps> = ({ option, isFocused, isLocked }) => {\n const getColor = (): { text: string; border: string } => {\n if (isLocked || option.state === \"disabled\") {\n return {\n text: CLI_COLORS.NEUTRAL,\n border: CLI_COLORS.NEUTRAL,\n };\n }\n if (option.selected) {\n return {\n text: CLI_COLORS.PRIMARY,\n border: CLI_COLORS.PRIMARY,\n };\n }\n if (option.state === \"recommended\") {\n return {\n text: CLI_COLORS.UNFOCUSED,\n border: CLI_COLORS.NEUTRAL,\n };\n }\n if (option.state === \"discouraged\") {\n return {\n text: CLI_COLORS.WARNING,\n border: CLI_COLORS.WARNING,\n };\n }\n // Normal unselected: muted color to clearly contrast with selected (cyan) skills\n return {\n text: CLI_COLORS.NEUTRAL,\n border: CLI_COLORS.NEUTRAL,\n };\n };\n\n const isBold = isFocused || option.selected;\n const isDimmed = isLocked || option.state === \"disabled\";\n const isBorderDimmed = isDimmed || (!option.selected && !isFocused);\n const focusBorderColor = option.selected ? CLI_COLORS.PRIMARY : CLI_COLORS.UNFOCUSED;\n const colors = getColor();\n const stateSuffix = getStateSuffix(option.state, isLocked);\n const stateSymbol = getStateSymbol(option, isLocked);\n\n return (\n <Box\n marginRight={1}\n borderColor={isFocused ? focusBorderColor : colors.border}\n borderStyle=\"single\"\n borderDimColor={isBorderDimmed}\n >\n <Text color={colors.text} bold={isBold} dimColor={false}>\n {\" \"}\n {option.local && (\n <>\n <Text backgroundColor={CLI_COLORS.NEUTRAL}> L </Text>{\" \"}\n </>\n )}\n {option.installed && <Text dimColor>{UI_SYMBOLS.SELECTED} </Text>}\n {!option.installed && <Text dimColor={isDimmed}>{stateSymbol} </Text>}\n {option.label}\n {stateSuffix && <Text dimColor> {stateSuffix}</Text>}{\" \"}\n </Text>\n </Box>\n );\n};\n\ntype CategorySectionProps = {\n category: CategoryRow;\n options: CategoryOption[];\n isLocked: boolean;\n isFocused: boolean;\n focusedOptionIndex: number;\n showDescriptions: boolean;\n};\n\nconst CategorySection: React.FC<CategorySectionProps> = ({\n category,\n options,\n isLocked,\n isFocused,\n focusedOptionIndex,\n showDescriptions,\n}) => {\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Box flexDirection=\"row\">\n <Text dimColor={isLocked}>{category.displayName}</Text>\n {category.required && (\n <Text color={isLocked ? CLI_COLORS.NEUTRAL : CLI_COLORS.ERROR} dimColor={isLocked}>\n {\" \"}\n {SYMBOL_REQUIRED}\n </Text>\n )}\n </Box>\n\n <Box flexDirection=\"row\" flexWrap=\"wrap\" marginTop={0}>\n {options.map((option, index) => (\n <Box key={option.id} flexDirection=\"column\">\n <SkillTag\n option={option}\n isFocused={isFocused && index === focusedOptionIndex && !isLocked}\n isLocked={isLocked}\n />\n {showDescriptions && option.stateReason && !isLocked && (\n <Box marginLeft={1} marginBottom={0}>\n <Text dimColor wrap=\"truncate-end\">\n {option.stateReason}\n </Text>\n </Box>\n )}\n </Box>\n ))}\n </Box>\n </Box>\n );\n};\n\ntype ProcessedCategory = CategoryRow & { sortedOptions: CategoryOption[] };\n\n/**\n * Estimate the rendered height of a category section in terminal rows.\n *\n * Each category consists of:\n * - 1 line for the category name (+ margin-top)\n * - N lines for the skill tags (based on count and terminal width wrapping)\n *\n * Tag wrapping: skill tags use flexWrap=\"wrap\". Each tag is approximately\n * AVG_TAG_WIDTH chars wide. The number of rows is ceil(tags * tagWidth / terminalWidth).\n */\nconst estimateCategoryHeight = (category: ProcessedCategory, terminalWidth: number): number => {\n const { CATEGORY_NAME_LINES, AVG_TAG_WIDTH, CATEGORY_MARGIN_LINES } = SCROLL_VIEWPORT;\n const optionCount = category.sortedOptions.length;\n const tagsPerRow = Math.max(1, Math.floor(terminalWidth / AVG_TAG_WIDTH));\n const tagRows = Math.ceil(optionCount / tagsPerRow);\n return CATEGORY_NAME_LINES + tagRows + CATEGORY_MARGIN_LINES;\n};\n\ntype ScrollIndicatorProps = {\n count: number;\n direction: \"above\" | \"below\";\n};\n\nconst ScrollIndicator: React.FC<ScrollIndicatorProps> = ({ count, direction }) => {\n if (count === 0) return null;\n\n const arrow = direction === \"above\" ? UI_SYMBOLS.SCROLL_UP : UI_SYMBOLS.SCROLL_DOWN;\n const label = `${arrow} ${count} more ${count === 1 ? \"category\" : \"categories\"} ${direction}`;\n\n return (\n <Box paddingLeft={1} marginTop={direction === \"below\" ? 1 : 0}>\n <Text dimColor>{label}</Text>\n </Box>\n );\n};\n\nconst DEFAULT_TERMINAL_WIDTH = 80;\n\nexport const CategoryGrid: React.FC<CategoryGridProps> = ({\n categories,\n showDescriptions,\n expertMode,\n onToggle,\n onToggleDescriptions,\n defaultFocusedRow = 0,\n defaultFocusedCol = 0,\n onFocusChange,\n availableHeight,\n terminalWidth,\n}) => {\n const processedCategories = useMemo(\n () =>\n categories.map((category) => ({\n ...category,\n sortedOptions: sortOptions(category.options, expertMode),\n })),\n [categories, expertMode],\n );\n\n const getColCount = useCallback(\n (row: number): number => processedCategories[row]?.sortedOptions.length ?? 0,\n [processedCategories],\n );\n\n const isRowLocked = useCallback(\n (row: number): boolean => {\n const cat = processedCategories[row];\n return cat ? isSectionLocked(cat.id, categories) : false;\n },\n [processedCategories, categories],\n );\n\n const findValidCol = useCallback(\n (row: number, currentCol: number, direction: 1 | -1): number => {\n const options = processedCategories[row]?.sortedOptions || [];\n const catId = processedCategories[row]?.id;\n if (catId && isSectionLocked(catId, categories)) return currentCol;\n return findNextValidOption(options, currentCol, direction, true);\n },\n [processedCategories, categories],\n );\n\n const adjustCol = useCallback(\n (row: number, clampedCol: number): number => {\n const options = processedCategories[row]?.sortedOptions || [];\n if (options[clampedCol]?.state === \"disabled\") {\n return findValidStartColumn(options);\n }\n return clampedCol;\n },\n [processedCategories],\n );\n\n const { focusedRow, focusedCol, setFocused, moveFocus } = useFocusedListItem(\n processedCategories.length,\n getColCount,\n {\n wrap: true,\n isRowLocked,\n findValidCol,\n adjustCol,\n onChange: onFocusChange,\n initialRow: defaultFocusedRow,\n initialCol: defaultFocusedCol,\n },\n );\n\n useCategoryGridInput({\n processedCategories,\n categories,\n focusedRow,\n focusedCol,\n setFocused,\n moveFocus,\n onToggle,\n onToggleDescriptions,\n });\n\n const { visibleItems, startIndex, hiddenAbove, hiddenBelow, isScrollable } = useVirtualScroll({\n items: processedCategories,\n availableHeight: availableHeight ?? Infinity,\n focusedIndex: focusedRow,\n estimateItemHeight: estimateCategoryHeight,\n terminalWidth: terminalWidth ?? DEFAULT_TERMINAL_WIDTH,\n });\n\n if (categories.length === 0) {\n return (\n <Box flexDirection=\"column\">\n <Text dimColor>No categories to display.</Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\">\n {isScrollable && <ScrollIndicator count={hiddenAbove} direction=\"above\" />}\n\n {visibleItems.map((category, visibleIndex) => {\n const originalIndex = startIndex + visibleIndex;\n const isLocked = isSectionLocked(category.id, categories);\n\n return (\n <CategorySection\n key={category.id}\n category={category}\n options={category.sortedOptions}\n isLocked={isLocked}\n isFocused={originalIndex === focusedRow}\n focusedOptionIndex={focusedCol}\n showDescriptions={showDescriptions}\n />\n );\n })}\n\n {isScrollable && <ScrollIndicator count={hiddenBelow} direction=\"below\" />}\n </Box>\n );\n};\n","import { useMemo } from \"react\";\nimport { SCROLL_VIEWPORT } from \"../../consts.js\";\n\nexport type VirtualScrollOptions<T> = {\n /** All items to potentially display */\n items: T[];\n /** Maximum height in terminal rows for the scrollable area */\n availableHeight: number;\n /** Current focused item index (drives auto-scroll) */\n focusedIndex: number;\n /** Estimate the height of a single item in terminal rows */\n estimateItemHeight: (item: T, terminalWidth: number) => number;\n /** Current terminal width (for tag wrapping estimation) */\n terminalWidth: number;\n};\n\nexport type VirtualScrollResult<T> = {\n /** Items visible in the current viewport window */\n visibleItems: T[];\n /** Index of the first visible item in the original array */\n startIndex: number;\n /** Index past the last visible item in the original array */\n endIndex: number;\n /** Number of items hidden above the viewport */\n hiddenAbove: number;\n /** Number of items hidden below the viewport */\n hiddenBelow: number;\n /** Whether the content is scrollable (total items exceed viewport) */\n isScrollable: boolean;\n};\n\n/**\n * Pure computation for virtual scroll windowing.\n *\n * Computes a contiguous window of items that fits within `availableHeight`,\n * ensuring the `focusedIndex` item is always visible. When the focused item\n * moves outside the current window, the window shifts to include it.\n *\n * Height is estimated per-item via `estimateItemHeight` rather than measured\n * post-render, avoiding the chicken-and-egg measurement problem with Ink.\n */\nexport function computeVirtualScroll<T>({\n items,\n availableHeight,\n focusedIndex,\n estimateItemHeight,\n terminalWidth,\n}: VirtualScrollOptions<T>): VirtualScrollResult<T> {\n const totalItems = items.length;\n\n if (totalItems === 0) {\n return {\n visibleItems: [],\n startIndex: 0,\n endIndex: 0,\n hiddenAbove: 0,\n hiddenBelow: 0,\n isScrollable: false,\n };\n }\n\n // Compute heights for each item\n const heights = items.map((item) => estimateItemHeight(item, terminalWidth));\n const totalHeight = heights.reduce((sum, h) => sum + h, 0);\n\n // If everything fits, no scrolling needed\n if (totalHeight <= availableHeight) {\n return {\n visibleItems: items,\n startIndex: 0,\n endIndex: totalItems,\n hiddenAbove: 0,\n hiddenBelow: 0,\n isScrollable: false,\n };\n }\n\n // Reserve space for scroll indicators when content overflows\n const viewportHeight = Math.max(\n SCROLL_VIEWPORT.MIN_VIEWPORT_ROWS,\n availableHeight - SCROLL_VIEWPORT.SCROLL_INDICATOR_HEIGHT * 2,\n );\n\n // Clamp focused index\n const safeFocused = Math.max(0, Math.min(focusedIndex, totalItems - 1));\n\n // Find a window that includes the focused item and fills the viewport.\n // Strategy: start from focused item and expand outward.\n let startIndex = safeFocused;\n let endIndex = safeFocused + 1;\n let usedHeight = heights[safeFocused] ?? 0;\n\n // Expand downward first\n while (endIndex < totalItems) {\n const nextHeight = heights[endIndex] ?? 0;\n if (usedHeight + nextHeight > viewportHeight) break;\n usedHeight += nextHeight;\n endIndex++;\n }\n\n // Expand upward with remaining space\n while (startIndex > 0) {\n const prevHeight = heights[startIndex - 1] ?? 0;\n if (usedHeight + prevHeight > viewportHeight) break;\n usedHeight += prevHeight;\n startIndex--;\n }\n\n // If we still have room after expanding up, try expanding down again\n while (endIndex < totalItems) {\n const nextHeight = heights[endIndex] ?? 0;\n if (usedHeight + nextHeight > viewportHeight) break;\n usedHeight += nextHeight;\n endIndex++;\n }\n\n return {\n visibleItems: items.slice(startIndex, endIndex),\n startIndex,\n endIndex,\n hiddenAbove: startIndex,\n hiddenBelow: totalItems - endIndex,\n isScrollable: true,\n };\n}\n\n/**\n * React hook wrapper around `computeVirtualScroll`.\n * Memoizes the result based on input changes.\n */\nexport function useVirtualScroll<T>(options: VirtualScrollOptions<T>): VirtualScrollResult<T> {\n const { items, availableHeight, focusedIndex, estimateItemHeight, terminalWidth } = options;\n return useMemo(\n () => computeVirtualScroll(options),\n [items, availableHeight, focusedIndex, estimateItemHeight, terminalWidth],\n );\n}\n","import { useCallback, useEffect } from \"react\";\nimport { useInput } from \"ink\";\n\nimport type { Subcategory, SkillId } from \"../../types/index.js\";\nimport type { CategoryOption, CategoryRow } from \"../wizard/category-grid.js\";\n\nconst FRAMEWORK_CATEGORY_ID = \"framework\";\n\n// Locked = non-framework section when no framework is selected\nexport const isSectionLocked = (categoryId: Subcategory, categories: CategoryRow[]): boolean => {\n if (categoryId === FRAMEWORK_CATEGORY_ID) {\n return false;\n }\n\n const frameworkCategory = categories.find((cat) => cat.id === FRAMEWORK_CATEGORY_ID);\n if (!frameworkCategory) return false;\n\n return !frameworkCategory.options.some((opt) => opt.selected);\n};\n\nexport const findValidStartColumn = (options: CategoryOption[]): number => {\n for (let i = 0; i < options.length; i++) {\n if (options[i] && options[i].state !== \"disabled\") {\n return i;\n }\n }\n return 0;\n};\n\n/** Find next unlocked section index (wrapping, direction: forward) */\nexport const findNextUnlockedIndex = (\n processed: { id: Subcategory; sortedOptions: CategoryOption[] }[],\n currentIndex: number,\n allCategories: CategoryRow[],\n): number => {\n const length = processed.length;\n if (length === 0) return currentIndex;\n\n let index = currentIndex;\n let attempts = 0;\n\n while (attempts < length) {\n index += 1;\n if (index >= length) index = 0;\n\n const category = processed[index];\n if (category && !isSectionLocked(category.id, allCategories)) {\n return index;\n }\n\n attempts++;\n }\n\n return currentIndex;\n};\n\ntype ProcessedCategory = CategoryRow & { sortedOptions: CategoryOption[] };\n\ntype UseCategoryGridInputOptions = {\n processedCategories: ProcessedCategory[];\n categories: CategoryRow[];\n focusedRow: number;\n focusedCol: number;\n setFocused: (row: number, col: number) => void;\n moveFocus: (direction: \"up\" | \"down\" | \"left\" | \"right\") => void;\n onToggle: (categoryId: Subcategory, technologyId: SkillId) => void;\n onToggleDescriptions: () => void;\n};\n\nexport function useCategoryGridInput({\n processedCategories,\n categories,\n focusedRow,\n focusedCol,\n setFocused,\n moveFocus,\n onToggle,\n onToggleDescriptions,\n}: UseCategoryGridInputOptions): void {\n const currentRow = processedCategories[focusedRow];\n const currentOptions = currentRow?.sortedOptions || [];\n const currentLocked = currentRow ? isSectionLocked(currentRow.id, categories) : false;\n\n // Adjust column when current row's options change externally (e.g. option becomes disabled)\n useEffect(() => {\n if (!currentRow) return;\n\n const maxCol = currentOptions.length - 1;\n if (focusedCol > maxCol) {\n const newCol = Math.max(0, maxCol);\n setFocused(focusedRow, newCol);\n } else if (currentOptions[focusedCol]?.state === \"disabled\") {\n const validCol = findValidStartColumn(currentOptions);\n if (validCol !== focusedCol) {\n setFocused(focusedRow, validCol);\n }\n }\n }, [focusedRow, currentOptions, focusedCol, setFocused, currentRow]);\n\n // Bounce off locked sections when a section becomes locked (e.g. framework deselected)\n useEffect(() => {\n if (currentRow && currentLocked) {\n const nextUnlocked = findNextUnlockedIndex(processedCategories, focusedRow, categories);\n if (nextUnlocked !== focusedRow) {\n const newRowOptions = processedCategories[nextUnlocked]?.sortedOptions || [];\n const newCol = findValidStartColumn(newRowOptions);\n setFocused(nextUnlocked, newCol);\n }\n }\n }, [currentRow, currentLocked, focusedRow, processedCategories, categories, setFocused]);\n\n useInput(\n useCallback(\n (\n input: string,\n key: {\n leftArrow: boolean;\n rightArrow: boolean;\n upArrow: boolean;\n downArrow: boolean;\n tab: boolean;\n shift: boolean;\n },\n ) => {\n if (key.tab && key.shift) {\n onToggleDescriptions();\n return;\n }\n\n if (key.tab && !key.shift) {\n const nextSection = findNextUnlockedIndex(processedCategories, focusedRow, categories);\n if (nextSection !== focusedRow) {\n const newRowOptions = processedCategories[nextSection]?.sortedOptions || [];\n const newCol = findValidStartColumn(newRowOptions);\n setFocused(nextSection, newCol);\n }\n return;\n }\n\n if (input === \"d\" || input === \"D\") {\n onToggleDescriptions();\n return;\n }\n\n if (input === \" \") {\n if (currentLocked) return;\n const currentOption = currentOptions[focusedCol];\n if (currentOption && currentOption.state !== \"disabled\") {\n onToggle(currentRow.id, currentOption.id);\n }\n return;\n }\n\n const isLeft = key.leftArrow || input === \"h\";\n const isRight = key.rightArrow || input === \"l\";\n const isUp = key.upArrow || input === \"k\";\n const isDown = key.downArrow || input === \"j\";\n\n if (isLeft) {\n if (currentLocked) return;\n moveFocus(\"left\");\n } else if (isRight) {\n if (currentLocked) return;\n moveFocus(\"right\");\n } else if (isUp) {\n moveFocus(\"up\");\n } else if (isDown) {\n moveFocus(\"down\");\n }\n },\n [\n focusedRow,\n focusedCol,\n currentOptions,\n currentRow,\n currentLocked,\n processedCategories,\n categories,\n onToggle,\n onToggleDescriptions,\n setFocused,\n moveFocus,\n ],\n ),\n );\n}\n"],"mappings":"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","names":["useCallback","useMemo","useMemo","useCallback"]}
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
ViewTitle
|
|
4
|
+
} from "./chunk-O4D67NN7.js";
|
|
5
|
+
import {
|
|
6
|
+
getDomainDisplayName
|
|
7
|
+
} from "./chunk-SO22IQPY.js";
|
|
8
|
+
import {
|
|
9
|
+
CategoryGrid
|
|
10
|
+
} from "./chunk-OGJIZ6QH.js";
|
|
11
|
+
import {
|
|
12
|
+
getAvailableSkills,
|
|
13
|
+
resolveAlias
|
|
14
|
+
} from "./chunk-MM7NK5N2.js";
|
|
15
|
+
import {
|
|
16
|
+
CLI_COLORS,
|
|
17
|
+
SCROLL_VIEWPORT,
|
|
18
|
+
UI_SYMBOLS
|
|
19
|
+
} from "./chunk-LAPCUV4D.js";
|
|
20
|
+
import {
|
|
21
|
+
init_esm_shims
|
|
22
|
+
} from "./chunk-DHET7RCE.js";
|
|
23
|
+
|
|
24
|
+
// src/cli/components/wizard/step-build.tsx
|
|
25
|
+
init_esm_shims();
|
|
26
|
+
import { useState as useState3 } from "react";
|
|
27
|
+
import { Box, Text, useInput } from "ink";
|
|
28
|
+
|
|
29
|
+
// src/cli/lib/wizard/index.ts
|
|
30
|
+
init_esm_shims();
|
|
31
|
+
|
|
32
|
+
// src/cli/lib/wizard/build-step-logic.ts
|
|
33
|
+
init_esm_shims();
|
|
34
|
+
import { sortBy } from "remeda";
|
|
35
|
+
var FRAMEWORK_SUBCATEGORY_ID = "framework";
|
|
36
|
+
var WEB_DOMAIN_ID = "web";
|
|
37
|
+
function validateBuildStep(categories, selections) {
|
|
38
|
+
for (const category of categories) {
|
|
39
|
+
if (category.required) {
|
|
40
|
+
const categorySelections = selections[category.id] || [];
|
|
41
|
+
if (categorySelections.length === 0) {
|
|
42
|
+
return {
|
|
43
|
+
valid: false,
|
|
44
|
+
message: `Select at least one skill from the ${category.displayName} category. Use arrow keys to navigate, then SPACE to select.`
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return { valid: true };
|
|
50
|
+
}
|
|
51
|
+
function computeOptionState(skill) {
|
|
52
|
+
if (skill.disabled) {
|
|
53
|
+
return "disabled";
|
|
54
|
+
}
|
|
55
|
+
if (skill.discouraged) {
|
|
56
|
+
return "discouraged";
|
|
57
|
+
}
|
|
58
|
+
if (skill.recommended) {
|
|
59
|
+
return "recommended";
|
|
60
|
+
}
|
|
61
|
+
return "normal";
|
|
62
|
+
}
|
|
63
|
+
function getSkillDisplayLabel(skill) {
|
|
64
|
+
return skill.displayName || skill.id;
|
|
65
|
+
}
|
|
66
|
+
function getStateReason(skill) {
|
|
67
|
+
if (skill.disabled && skill.disabledReason) {
|
|
68
|
+
return skill.disabledReason;
|
|
69
|
+
}
|
|
70
|
+
if (skill.discouraged && skill.discouragedReason) {
|
|
71
|
+
return skill.discouragedReason;
|
|
72
|
+
}
|
|
73
|
+
if (skill.recommended && skill.recommendedReason) {
|
|
74
|
+
return skill.recommendedReason;
|
|
75
|
+
}
|
|
76
|
+
return void 0;
|
|
77
|
+
}
|
|
78
|
+
function isFrameworkSelected(selections) {
|
|
79
|
+
const frameworkSelections = selections[FRAMEWORK_SUBCATEGORY_ID] ?? [];
|
|
80
|
+
return frameworkSelections.length > 0;
|
|
81
|
+
}
|
|
82
|
+
function getSelectedFrameworks(selections, matrix) {
|
|
83
|
+
const frameworkSelections = selections[FRAMEWORK_SUBCATEGORY_ID] ?? [];
|
|
84
|
+
return frameworkSelections.map((alias) => resolveAlias(alias, matrix));
|
|
85
|
+
}
|
|
86
|
+
function isCompatibleWithSelectedFrameworks(skillId, selectedFrameworkIds, matrix) {
|
|
87
|
+
const skill = matrix.skills[skillId];
|
|
88
|
+
if (!skill) return false;
|
|
89
|
+
if (skill.compatibleWith.length === 0) {
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
return selectedFrameworkIds.some((frameworkId) => skill.compatibleWith.includes(frameworkId));
|
|
93
|
+
}
|
|
94
|
+
function buildCategoriesForDomain(domain, allSelections, matrix, expertMode, selections, parentDomainSelections, installedSkillIds) {
|
|
95
|
+
const frameworkSource = parentDomainSelections ?? selections;
|
|
96
|
+
const frameworkSelected = isFrameworkSelected(frameworkSource);
|
|
97
|
+
const selectedFrameworkIds = frameworkSelected ? getSelectedFrameworks(frameworkSource, matrix) : [];
|
|
98
|
+
const subcategories = sortBy(
|
|
99
|
+
Object.values(matrix.categories).filter((cat) => cat.domain === domain),
|
|
100
|
+
(cat) => cat.order ?? 0
|
|
101
|
+
);
|
|
102
|
+
const categoryRows = subcategories.map((cat) => {
|
|
103
|
+
const skillOptions = getAvailableSkills(cat.id, allSelections, matrix, {
|
|
104
|
+
expertMode
|
|
105
|
+
});
|
|
106
|
+
const useFrameworkFilter = (domain === WEB_DOMAIN_ID || parentDomainSelections !== void 0) && cat.id !== FRAMEWORK_SUBCATEGORY_ID && frameworkSelected;
|
|
107
|
+
const filteredSkillOptions = useFrameworkFilter ? skillOptions.filter(
|
|
108
|
+
(skill) => isCompatibleWithSelectedFrameworks(skill.id, selectedFrameworkIds, matrix)
|
|
109
|
+
) : skillOptions;
|
|
110
|
+
const options = filteredSkillOptions.map((skill) => ({
|
|
111
|
+
id: skill.id,
|
|
112
|
+
label: getSkillDisplayLabel(skill),
|
|
113
|
+
state: computeOptionState(skill),
|
|
114
|
+
stateReason: getStateReason(skill),
|
|
115
|
+
selected: skill.selected,
|
|
116
|
+
local: matrix.skills[skill.id]?.local,
|
|
117
|
+
installed: installedSkillIds?.includes(skill.id) || false
|
|
118
|
+
}));
|
|
119
|
+
return {
|
|
120
|
+
id: cat.id,
|
|
121
|
+
displayName: cat.displayName,
|
|
122
|
+
required: cat.required ?? false,
|
|
123
|
+
exclusive: cat.exclusive ?? true,
|
|
124
|
+
options
|
|
125
|
+
};
|
|
126
|
+
});
|
|
127
|
+
return categoryRows.filter((row) => row.options.length > 0);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// src/cli/components/hooks/use-framework-filtering.ts
|
|
131
|
+
init_esm_shims();
|
|
132
|
+
import { useMemo } from "react";
|
|
133
|
+
function useFrameworkFiltering({
|
|
134
|
+
domain,
|
|
135
|
+
allSelections,
|
|
136
|
+
matrix,
|
|
137
|
+
expertMode,
|
|
138
|
+
selections,
|
|
139
|
+
parentDomainSelections,
|
|
140
|
+
installedSkillIds
|
|
141
|
+
}) {
|
|
142
|
+
return useMemo(
|
|
143
|
+
() => buildCategoriesForDomain(
|
|
144
|
+
domain,
|
|
145
|
+
allSelections,
|
|
146
|
+
matrix,
|
|
147
|
+
expertMode,
|
|
148
|
+
selections,
|
|
149
|
+
parentDomainSelections,
|
|
150
|
+
installedSkillIds
|
|
151
|
+
),
|
|
152
|
+
[
|
|
153
|
+
domain,
|
|
154
|
+
allSelections,
|
|
155
|
+
matrix,
|
|
156
|
+
expertMode,
|
|
157
|
+
selections,
|
|
158
|
+
parentDomainSelections,
|
|
159
|
+
installedSkillIds
|
|
160
|
+
]
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/cli/components/hooks/use-terminal-dimensions.ts
|
|
165
|
+
init_esm_shims();
|
|
166
|
+
import { useState, useEffect } from "react";
|
|
167
|
+
import { useStdout } from "ink";
|
|
168
|
+
var DEFAULT_COLUMNS = 80;
|
|
169
|
+
var DEFAULT_ROWS = 24;
|
|
170
|
+
function useTerminalDimensions() {
|
|
171
|
+
const { stdout } = useStdout();
|
|
172
|
+
const [dimensions, setDimensions] = useState(() => ({
|
|
173
|
+
columns: stdout.columns || DEFAULT_COLUMNS,
|
|
174
|
+
rows: stdout.rows || DEFAULT_ROWS
|
|
175
|
+
}));
|
|
176
|
+
useEffect(() => {
|
|
177
|
+
const handleResize = () => {
|
|
178
|
+
setDimensions({
|
|
179
|
+
columns: stdout.columns || DEFAULT_COLUMNS,
|
|
180
|
+
rows: stdout.rows || DEFAULT_ROWS
|
|
181
|
+
});
|
|
182
|
+
};
|
|
183
|
+
stdout.on("resize", handleResize);
|
|
184
|
+
return () => {
|
|
185
|
+
stdout.off("resize", handleResize);
|
|
186
|
+
};
|
|
187
|
+
}, [stdout]);
|
|
188
|
+
return dimensions;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// src/cli/components/hooks/use-measured-height.ts
|
|
192
|
+
init_esm_shims();
|
|
193
|
+
import { useRef, useState as useState2, useEffect as useEffect2 } from "react";
|
|
194
|
+
import { measureElement, useStdout as useStdout2 } from "ink";
|
|
195
|
+
function useMeasuredHeight() {
|
|
196
|
+
const ref = useRef(null);
|
|
197
|
+
const [measuredHeight, setMeasuredHeight] = useState2(0);
|
|
198
|
+
const { stdout } = useStdout2();
|
|
199
|
+
useEffect2(() => {
|
|
200
|
+
const measure = () => {
|
|
201
|
+
if (ref.current) {
|
|
202
|
+
const { height } = measureElement(ref.current);
|
|
203
|
+
setMeasuredHeight((prev) => prev !== height ? height : prev);
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
measure();
|
|
207
|
+
stdout.on("resize", measure);
|
|
208
|
+
return () => {
|
|
209
|
+
stdout.off("resize", measure);
|
|
210
|
+
};
|
|
211
|
+
}, [stdout]);
|
|
212
|
+
return { ref, measuredHeight };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// src/cli/components/wizard/step-build.tsx
|
|
216
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
217
|
+
var Footer = ({ validationError }) => {
|
|
218
|
+
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginTop: 1, children: validationError && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
219
|
+
/* @__PURE__ */ jsx(Text, { color: CLI_COLORS.WARNING, children: validationError }),
|
|
220
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Press ESC to go back, or select a skill and press ENTER to continue." })
|
|
221
|
+
] }) });
|
|
222
|
+
};
|
|
223
|
+
var LegendRow = () => {
|
|
224
|
+
return /* @__PURE__ */ jsxs(Box, { paddingLeft: 1, columnGap: 2, children: [
|
|
225
|
+
/* @__PURE__ */ jsxs(Text, { color: CLI_COLORS.PRIMARY, children: [
|
|
226
|
+
UI_SYMBOLS.SELECTED,
|
|
227
|
+
" active"
|
|
228
|
+
] }),
|
|
229
|
+
/* @__PURE__ */ jsxs(Text, { color: CLI_COLORS.UNFOCUSED, children: [
|
|
230
|
+
UI_SYMBOLS.UNSELECTED,
|
|
231
|
+
" recommended"
|
|
232
|
+
] }),
|
|
233
|
+
/* @__PURE__ */ jsxs(Text, { color: CLI_COLORS.WARNING, children: [
|
|
234
|
+
UI_SYMBOLS.DISCOURAGED,
|
|
235
|
+
" discouraged"
|
|
236
|
+
] }),
|
|
237
|
+
/* @__PURE__ */ jsxs(Text, { color: CLI_COLORS.NEUTRAL, children: [
|
|
238
|
+
UI_SYMBOLS.DISABLED,
|
|
239
|
+
" disabled"
|
|
240
|
+
] })
|
|
241
|
+
] });
|
|
242
|
+
};
|
|
243
|
+
var StepBuild = ({
|
|
244
|
+
matrix,
|
|
245
|
+
domain: activeDomain,
|
|
246
|
+
selectedDomains,
|
|
247
|
+
selections,
|
|
248
|
+
allSelections,
|
|
249
|
+
showDescriptions,
|
|
250
|
+
expertMode,
|
|
251
|
+
parentDomainSelections,
|
|
252
|
+
installedSkillIds,
|
|
253
|
+
onToggle,
|
|
254
|
+
onToggleDescriptions,
|
|
255
|
+
onContinue,
|
|
256
|
+
onBack
|
|
257
|
+
}) => {
|
|
258
|
+
const [validationError, setValidationError] = useState3(void 0);
|
|
259
|
+
const { columns } = useTerminalDimensions();
|
|
260
|
+
const { ref: gridRef, measuredHeight: gridHeight } = useMeasuredHeight();
|
|
261
|
+
const categories = useFrameworkFiltering({
|
|
262
|
+
domain: activeDomain,
|
|
263
|
+
allSelections,
|
|
264
|
+
matrix,
|
|
265
|
+
expertMode,
|
|
266
|
+
selections,
|
|
267
|
+
parentDomainSelections,
|
|
268
|
+
installedSkillIds
|
|
269
|
+
});
|
|
270
|
+
const availableHeight = gridHeight > 0 ? Math.max(SCROLL_VIEWPORT.MIN_VIEWPORT_ROWS, gridHeight) : Infinity;
|
|
271
|
+
useInput((_input, key) => {
|
|
272
|
+
if (key.return) {
|
|
273
|
+
const validation = validateBuildStep(categories, selections);
|
|
274
|
+
if (validation.valid) {
|
|
275
|
+
setValidationError(void 0);
|
|
276
|
+
onContinue();
|
|
277
|
+
} else {
|
|
278
|
+
setValidationError(validation.message);
|
|
279
|
+
}
|
|
280
|
+
} else if (key.escape) {
|
|
281
|
+
setValidationError(void 0);
|
|
282
|
+
onBack();
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width: "100%", flexGrow: 1, children: [
|
|
286
|
+
/* @__PURE__ */ jsxs(
|
|
287
|
+
Box,
|
|
288
|
+
{
|
|
289
|
+
columnGap: 2,
|
|
290
|
+
flexDirection: "row",
|
|
291
|
+
justifyContent: "space-between",
|
|
292
|
+
marginBottom: 1,
|
|
293
|
+
paddingRight: 1,
|
|
294
|
+
marginTop: -1,
|
|
295
|
+
borderTop: false,
|
|
296
|
+
borderRight: false,
|
|
297
|
+
borderLeft: false,
|
|
298
|
+
borderColor: CLI_COLORS.NEUTRAL,
|
|
299
|
+
borderStyle: "single",
|
|
300
|
+
children: [
|
|
301
|
+
/* @__PURE__ */ jsx(Box, { columnGap: 2, flexDirection: "row", children: selectedDomains.map((domain) => {
|
|
302
|
+
const isActive = domain === activeDomain;
|
|
303
|
+
return /* @__PURE__ */ jsxs(Text, { color: isActive ? CLI_COLORS.PRIMARY : void 0, children: [
|
|
304
|
+
isActive ? UI_SYMBOLS.CURRENT : UI_SYMBOLS.UNSELECTED,
|
|
305
|
+
" ",
|
|
306
|
+
getDomainDisplayName(domain)
|
|
307
|
+
] }, domain);
|
|
308
|
+
}) }),
|
|
309
|
+
/* @__PURE__ */ jsx(LegendRow, {})
|
|
310
|
+
]
|
|
311
|
+
}
|
|
312
|
+
),
|
|
313
|
+
/* @__PURE__ */ jsxs(ViewTitle, { children: [
|
|
314
|
+
"Customize your ",
|
|
315
|
+
getDomainDisplayName(activeDomain),
|
|
316
|
+
" stack"
|
|
317
|
+
] }),
|
|
318
|
+
/* @__PURE__ */ jsx(Box, { ref: gridRef, flexGrow: 1, children: /* @__PURE__ */ jsx(
|
|
319
|
+
CategoryGrid,
|
|
320
|
+
{
|
|
321
|
+
categories,
|
|
322
|
+
expertMode,
|
|
323
|
+
showDescriptions,
|
|
324
|
+
onToggle,
|
|
325
|
+
onToggleDescriptions,
|
|
326
|
+
availableHeight,
|
|
327
|
+
terminalWidth: columns
|
|
328
|
+
},
|
|
329
|
+
activeDomain
|
|
330
|
+
) }),
|
|
331
|
+
/* @__PURE__ */ jsx(Footer, { validationError })
|
|
332
|
+
] });
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
export {
|
|
336
|
+
validateBuildStep,
|
|
337
|
+
getSkillDisplayLabel,
|
|
338
|
+
StepBuild
|
|
339
|
+
};
|
|
340
|
+
//# sourceMappingURL=chunk-OMV7TLWD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/components/wizard/step-build.tsx","../src/cli/lib/wizard/index.ts","../src/cli/lib/wizard/build-step-logic.ts","../src/cli/components/hooks/use-framework-filtering.ts","../src/cli/components/hooks/use-terminal-dimensions.ts","../src/cli/components/hooks/use-measured-height.ts"],"sourcesContent":["import React, { useState } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport type {\n Domain,\n MergedSkillsMatrix,\n SkillId,\n Subcategory,\n SubcategorySelections,\n} from \"../../types/index.js\";\nimport { validateBuildStep } from \"../../lib/wizard/index.js\";\nimport { CLI_COLORS, SCROLL_VIEWPORT, UI_SYMBOLS } from \"../../consts.js\";\nimport { useFrameworkFiltering } from \"../hooks/use-framework-filtering.js\";\nimport { useTerminalDimensions } from \"../hooks/use-terminal-dimensions.js\";\nimport { useMeasuredHeight } from \"../hooks/use-measured-height.js\";\nimport { CategoryGrid } from \"./category-grid.js\";\nimport { ViewTitle } from \"./view-title.js\";\nimport { getDomainDisplayName } from \"./utils.js\";\n\nexport type StepBuildProps = {\n matrix: MergedSkillsMatrix;\n domain: Domain;\n selectedDomains: Domain[];\n selections: SubcategorySelections;\n allSelections: SkillId[];\n showDescriptions: boolean;\n expertMode: boolean;\n /** For framework-first filtering on sub-domains (e.g., web-extras inherits from web) */\n parentDomainSelections?: SubcategorySelections;\n /** Skill IDs already installed on disk, shown with a dimmed checkmark */\n installedSkillIds?: SkillId[];\n onToggle: (subcategoryId: Subcategory, technologyId: SkillId) => void;\n onToggleDescriptions: () => void;\n onContinue: () => void;\n onBack: () => void;\n};\n\ntype FooterProps = {\n validationError?: string;\n};\n\nconst Footer: React.FC<FooterProps> = ({ validationError }) => {\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n {validationError && (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Text color={CLI_COLORS.WARNING}>{validationError}</Text>\n <Text dimColor>Press ESC to go back, or select a skill and press ENTER to continue.</Text>\n </Box>\n )}\n </Box>\n );\n};\n\nconst LegendRow: React.FC = () => {\n return (\n <Box paddingLeft={1} columnGap={2}>\n <Text color={CLI_COLORS.PRIMARY}>{UI_SYMBOLS.SELECTED} active</Text>\n <Text color={CLI_COLORS.UNFOCUSED}>{UI_SYMBOLS.UNSELECTED} recommended</Text>\n <Text color={CLI_COLORS.WARNING}>{UI_SYMBOLS.DISCOURAGED} discouraged</Text>\n <Text color={CLI_COLORS.NEUTRAL}>{UI_SYMBOLS.DISABLED} disabled</Text>\n </Box>\n );\n};\n\nexport const StepBuild: React.FC<StepBuildProps> = ({\n matrix,\n domain: activeDomain,\n selectedDomains,\n selections,\n allSelections,\n showDescriptions,\n expertMode,\n parentDomainSelections,\n installedSkillIds,\n onToggle,\n onToggleDescriptions,\n onContinue,\n onBack,\n}) => {\n const [validationError, setValidationError] = useState<string | undefined>(undefined);\n const { columns } = useTerminalDimensions();\n const { ref: gridRef, measuredHeight: gridHeight } = useMeasuredHeight();\n\n const categories = useFrameworkFiltering({\n domain: activeDomain,\n allSelections,\n matrix,\n expertMode,\n selections,\n parentDomainSelections,\n installedSkillIds,\n });\n\n const availableHeight =\n gridHeight > 0 ? Math.max(SCROLL_VIEWPORT.MIN_VIEWPORT_ROWS, gridHeight) : Infinity;\n\n useInput((_input, key) => {\n if (key.return) {\n const validation = validateBuildStep(categories, selections);\n if (validation.valid) {\n setValidationError(undefined);\n onContinue();\n } else {\n setValidationError(validation.message);\n }\n } else if (key.escape) {\n setValidationError(undefined);\n onBack();\n }\n });\n\n return (\n <Box flexDirection=\"column\" width=\"100%\" flexGrow={1}>\n <Box\n columnGap={2}\n flexDirection=\"row\"\n justifyContent=\"space-between\"\n marginBottom={1}\n paddingRight={1}\n marginTop={-1}\n borderTop={false}\n borderRight={false}\n borderLeft={false}\n borderColor={CLI_COLORS.NEUTRAL}\n borderStyle=\"single\"\n >\n <Box columnGap={2} flexDirection=\"row\">\n {selectedDomains.map((domain) => {\n const isActive = domain === activeDomain;\n return (\n <Text key={domain} color={isActive ? CLI_COLORS.PRIMARY : undefined}>\n {isActive ? UI_SYMBOLS.CURRENT : UI_SYMBOLS.UNSELECTED}{\" \"}\n {getDomainDisplayName(domain)}\n </Text>\n );\n })}\n </Box>\n <LegendRow />\n </Box>\n <ViewTitle>Customize your {getDomainDisplayName(activeDomain)} stack</ViewTitle>\n\n <Box ref={gridRef} flexGrow={1}>\n <CategoryGrid\n key={activeDomain}\n categories={categories}\n expertMode={expertMode}\n showDescriptions={showDescriptions}\n onToggle={onToggle}\n onToggleDescriptions={onToggleDescriptions}\n availableHeight={availableHeight}\n terminalWidth={columns}\n />\n </Box>\n\n <Footer validationError={validationError} />\n </Box>\n );\n};\n","export {\n type BuildStepValidation,\n validateBuildStep,\n computeOptionState,\n getSkillDisplayLabel,\n buildCategoriesForDomain,\n} from \"./build-step-logic\";\n","import { sortBy } from \"remeda\";\nimport type {\n Domain,\n MergedSkillsMatrix,\n SkillId,\n SubcategorySelections,\n} from \"../../types/index.js\";\nimport { getAvailableSkills, resolveAlias } from \"../matrix/index.js\";\nimport type {\n CategoryRow,\n CategoryOption,\n OptionState,\n} from \"../../components/wizard/category-grid.js\";\n\nconst FRAMEWORK_SUBCATEGORY_ID = \"framework\";\nconst WEB_DOMAIN_ID = \"web\";\n\nexport type BuildStepValidation = {\n valid: boolean;\n message?: string;\n};\n\nexport function validateBuildStep(\n categories: CategoryRow[],\n selections: SubcategorySelections,\n): BuildStepValidation {\n for (const category of categories) {\n if (category.required) {\n const categorySelections = selections[category.id] || [];\n if (categorySelections.length === 0) {\n return {\n valid: false,\n message: `Select at least one skill from the ${category.displayName} category. Use arrow keys to navigate, then SPACE to select.`,\n };\n }\n }\n }\n return { valid: true };\n}\n\nexport function computeOptionState(skill: {\n disabled: boolean;\n discouraged: boolean;\n recommended: boolean;\n}): OptionState {\n if (skill.disabled) {\n return \"disabled\";\n }\n if (skill.discouraged) {\n return \"discouraged\";\n }\n if (skill.recommended) {\n return \"recommended\";\n }\n return \"normal\";\n}\n\nexport function getSkillDisplayLabel(skill: { displayName?: string; id: string }): string {\n return skill.displayName || skill.id;\n}\n\nfunction getStateReason(skill: {\n disabled: boolean;\n disabledReason?: string;\n discouraged: boolean;\n discouragedReason?: string;\n recommended: boolean;\n recommendedReason?: string;\n}): string | undefined {\n if (skill.disabled && skill.disabledReason) {\n return skill.disabledReason;\n }\n if (skill.discouraged && skill.discouragedReason) {\n return skill.discouragedReason;\n }\n if (skill.recommended && skill.recommendedReason) {\n return skill.recommendedReason;\n }\n return undefined;\n}\n\nfunction isFrameworkSelected(selections: SubcategorySelections): boolean {\n const frameworkSelections = selections[FRAMEWORK_SUBCATEGORY_ID] ?? [];\n return frameworkSelections.length > 0;\n}\n\nfunction getSelectedFrameworks(\n selections: SubcategorySelections,\n matrix: MergedSkillsMatrix,\n): SkillId[] {\n const frameworkSelections = selections[FRAMEWORK_SUBCATEGORY_ID] ?? [];\n return frameworkSelections.map((alias) => resolveAlias(alias, matrix));\n}\n\nfunction isCompatibleWithSelectedFrameworks(\n skillId: SkillId,\n selectedFrameworkIds: SkillId[],\n matrix: MergedSkillsMatrix,\n): boolean {\n const skill = matrix.skills[skillId];\n if (!skill) return false;\n\n // No compatibleWith = compatible with all (allows legacy skills to appear)\n if (skill.compatibleWith.length === 0) {\n return true;\n }\n\n return selectedFrameworkIds.some((frameworkId) => skill.compatibleWith.includes(frameworkId));\n}\n\n// Build CategoryRow[] from matrix for a domain, with framework-first filtering for web\nexport function buildCategoriesForDomain(\n domain: Domain,\n allSelections: SkillId[],\n matrix: MergedSkillsMatrix,\n expertMode: boolean,\n selections: SubcategorySelections,\n parentDomainSelections?: SubcategorySelections,\n installedSkillIds?: SkillId[],\n): CategoryRow[] {\n const frameworkSource = parentDomainSelections ?? selections;\n const frameworkSelected = isFrameworkSelected(frameworkSource);\n const selectedFrameworkIds = frameworkSelected\n ? getSelectedFrameworks(frameworkSource, matrix)\n : [];\n\n const subcategories = sortBy(\n Object.values(matrix.categories).filter((cat) => cat.domain === domain),\n (cat) => cat.order ?? 0,\n );\n\n const categoryRows: CategoryRow[] = subcategories.map((cat) => {\n const skillOptions = getAvailableSkills(cat.id, allSelections, matrix, {\n expertMode,\n });\n\n const useFrameworkFilter =\n (domain === WEB_DOMAIN_ID || parentDomainSelections !== undefined) &&\n cat.id !== FRAMEWORK_SUBCATEGORY_ID &&\n frameworkSelected;\n const filteredSkillOptions = useFrameworkFilter\n ? skillOptions.filter((skill) =>\n isCompatibleWithSelectedFrameworks(skill.id, selectedFrameworkIds, matrix),\n )\n : skillOptions;\n\n const options: CategoryOption[] = filteredSkillOptions.map((skill) => ({\n id: skill.id,\n label: getSkillDisplayLabel(skill),\n state: computeOptionState(skill),\n stateReason: getStateReason(skill),\n selected: skill.selected,\n local: matrix.skills[skill.id]?.local,\n installed: installedSkillIds?.includes(skill.id) || false,\n }));\n\n return {\n id: cat.id,\n displayName: cat.displayName,\n required: cat.required ?? false,\n exclusive: cat.exclusive ?? true,\n options,\n };\n });\n\n return categoryRows.filter((row) => row.options.length > 0);\n}\n","import { useMemo } from \"react\";\nimport type {\n Domain,\n MergedSkillsMatrix,\n SkillId,\n SubcategorySelections,\n} from \"../../types/index.js\";\nimport { buildCategoriesForDomain } from \"../../lib/wizard/index.js\";\nimport type { CategoryRow } from \"../wizard/category-grid.js\";\n\ntype UseFrameworkFilteringOptions = {\n domain: Domain;\n allSelections: SkillId[];\n matrix: MergedSkillsMatrix;\n expertMode: boolean;\n selections: SubcategorySelections;\n parentDomainSelections?: SubcategorySelections;\n installedSkillIds?: SkillId[];\n};\n\nexport function useFrameworkFiltering({\n domain,\n allSelections,\n matrix,\n expertMode,\n selections,\n parentDomainSelections,\n installedSkillIds,\n}: UseFrameworkFilteringOptions): CategoryRow[] {\n return useMemo(\n () =>\n buildCategoriesForDomain(\n domain,\n allSelections,\n matrix,\n expertMode,\n selections,\n parentDomainSelections,\n installedSkillIds,\n ),\n [\n domain,\n allSelections,\n matrix,\n expertMode,\n selections,\n parentDomainSelections,\n installedSkillIds,\n ],\n );\n}\n","import { useState, useEffect } from \"react\";\nimport { useStdout } from \"ink\";\n\nconst DEFAULT_COLUMNS = 80;\nconst DEFAULT_ROWS = 24;\n\nexport type TerminalDimensions = {\n /** Terminal width in columns */\n columns: number;\n /** Terminal height in rows */\n rows: number;\n};\n\n/**\n * Tracks terminal dimensions reactively. Re-renders on resize.\n *\n * Falls back to DEFAULT_COLUMNS x DEFAULT_ROWS when stdout is not a TTY\n * (e.g., piped output, CI environments, tests).\n */\nexport function useTerminalDimensions(): TerminalDimensions {\n const { stdout } = useStdout();\n\n const [dimensions, setDimensions] = useState<TerminalDimensions>(() => ({\n columns: stdout.columns || DEFAULT_COLUMNS,\n rows: stdout.rows || DEFAULT_ROWS,\n }));\n\n useEffect(() => {\n const handleResize = () => {\n setDimensions({\n columns: stdout.columns || DEFAULT_COLUMNS,\n rows: stdout.rows || DEFAULT_ROWS,\n });\n };\n\n stdout.on(\"resize\", handleResize);\n return () => {\n stdout.off(\"resize\", handleResize);\n };\n }, [stdout]);\n\n return dimensions;\n}\n","import { useRef, useState, useEffect } from \"react\";\nimport { type DOMElement, measureElement, useStdout } from \"ink\";\n\n/**\n * Measures the computed height of a Box element using Ink's Yoga layout engine.\n *\n * Returns a ref to attach to a Box with `flexGrow={1}` and the measured height.\n * The Box must be inside a parent chain with a constrained height (e.g., an\n * explicit `height` prop on an ancestor) so Yoga can compute the remaining space.\n *\n * Returns 0 before the first layout pass. Re-measures on terminal resize.\n */\nexport function useMeasuredHeight(): {\n ref: React.Ref<DOMElement>;\n measuredHeight: number;\n} {\n const ref = useRef<DOMElement>(null);\n const [measuredHeight, setMeasuredHeight] = useState(0);\n const { stdout } = useStdout();\n\n useEffect(() => {\n const measure = () => {\n if (ref.current) {\n const { height } = measureElement(ref.current);\n setMeasuredHeight((prev) => (prev !== height ? height : prev));\n }\n };\n\n measure();\n\n stdout.on(\"resize\", measure);\n return () => {\n stdout.off(\"resize\", measure);\n };\n }, [stdout]);\n\n return { ref, measuredHeight };\n}\n"],"mappings":"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","names":["useState","useState","useEffect","useStdout","useState"]}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
WIZARD_STEPS,
|
|
4
|
+
WizardTabs
|
|
5
|
+
} from "./chunk-IYG2LAIM.js";
|
|
6
|
+
import {
|
|
7
|
+
HelpModal
|
|
8
|
+
} from "./chunk-FZGYSLJL.js";
|
|
9
|
+
import {
|
|
10
|
+
useWizardStore
|
|
11
|
+
} from "./chunk-3ZOIOVKT.js";
|
|
12
|
+
import {
|
|
13
|
+
CLI_COLORS
|
|
14
|
+
} from "./chunk-LAPCUV4D.js";
|
|
15
|
+
import {
|
|
16
|
+
init_esm_shims
|
|
17
|
+
} from "./chunk-DHET7RCE.js";
|
|
18
|
+
|
|
19
|
+
// src/cli/components/wizard/wizard-layout.tsx
|
|
20
|
+
init_esm_shims();
|
|
21
|
+
import { Fragment } from "react";
|
|
22
|
+
import { Box, Text } from "ink";
|
|
23
|
+
import { Fragment as Fragment2, jsx, jsxs } from "react/jsx-runtime";
|
|
24
|
+
import { createElement } from "react";
|
|
25
|
+
var DefinitionItem = ({
|
|
26
|
+
isVisible = true,
|
|
27
|
+
isActive = false,
|
|
28
|
+
label,
|
|
29
|
+
values
|
|
30
|
+
}) => {
|
|
31
|
+
if (!isVisible) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
35
|
+
values.map((value) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
36
|
+
/* @__PURE__ */ jsxs(Text, { backgroundColor: "black", color: CLI_COLORS.UNFOCUSED, children: [
|
|
37
|
+
" ",
|
|
38
|
+
value,
|
|
39
|
+
" "
|
|
40
|
+
] }),
|
|
41
|
+
" "
|
|
42
|
+
] }, value)),
|
|
43
|
+
/* @__PURE__ */ jsx(Text, { color: isActive ? CLI_COLORS.PRIMARY : void 0, children: label })
|
|
44
|
+
] });
|
|
45
|
+
};
|
|
46
|
+
var HOT_KEYS = [
|
|
47
|
+
{ label: "navigate", values: ["\u2190/\u2192", "\u2191/\u2193"] },
|
|
48
|
+
{ label: "select", values: ["SPACE"] },
|
|
49
|
+
{ label: "continue", values: ["ENTER"] },
|
|
50
|
+
{ label: "back", values: ["ESC"] }
|
|
51
|
+
];
|
|
52
|
+
var WizardFooter = () => {
|
|
53
|
+
const store = useWizardStore();
|
|
54
|
+
return /* @__PURE__ */ jsxs(
|
|
55
|
+
Box,
|
|
56
|
+
{
|
|
57
|
+
columnGap: 2,
|
|
58
|
+
borderTop: true,
|
|
59
|
+
borderRight: false,
|
|
60
|
+
borderBottom: true,
|
|
61
|
+
borderLeft: false,
|
|
62
|
+
borderColor: "blackBright",
|
|
63
|
+
borderStyle: "single",
|
|
64
|
+
paddingLeft: 1,
|
|
65
|
+
paddingRight: 1,
|
|
66
|
+
children: [
|
|
67
|
+
/* @__PURE__ */ jsx(
|
|
68
|
+
DefinitionItem,
|
|
69
|
+
{
|
|
70
|
+
label: "Accept defaults",
|
|
71
|
+
values: ["A"],
|
|
72
|
+
isVisible: store.step === "build" && !!store.selectedStackId
|
|
73
|
+
}
|
|
74
|
+
),
|
|
75
|
+
HOT_KEYS.map((hotkey) => /* @__PURE__ */ createElement(DefinitionItem, { ...hotkey, key: hotkey.label }))
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
};
|
|
80
|
+
var WizardLayout = ({
|
|
81
|
+
version,
|
|
82
|
+
marketplaceLabel,
|
|
83
|
+
brandingName,
|
|
84
|
+
terminalHeight,
|
|
85
|
+
children
|
|
86
|
+
}) => {
|
|
87
|
+
const store = useWizardStore();
|
|
88
|
+
const { completedSteps, skippedSteps } = store.getStepProgress();
|
|
89
|
+
const constrainedHeight = store.step === "build" ? terminalHeight : void 0;
|
|
90
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, height: constrainedHeight, children: [
|
|
91
|
+
/* @__PURE__ */ jsx(
|
|
92
|
+
WizardTabs,
|
|
93
|
+
{
|
|
94
|
+
steps: WIZARD_STEPS,
|
|
95
|
+
currentStep: store.step,
|
|
96
|
+
completedSteps,
|
|
97
|
+
skippedSteps,
|
|
98
|
+
version,
|
|
99
|
+
brandingName
|
|
100
|
+
}
|
|
101
|
+
),
|
|
102
|
+
marketplaceLabel && /* @__PURE__ */ jsxs(Box, { paddingLeft: 1, marginTop: 1, children: [
|
|
103
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Marketplace: " }),
|
|
104
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: marketplaceLabel })
|
|
105
|
+
] }),
|
|
106
|
+
store.showHelp ? /* @__PURE__ */ jsx(HelpModal, { currentStep: store.step }) : /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
107
|
+
/* @__PURE__ */ jsx(Box, { flexGrow: 1, marginTop: 1, children }),
|
|
108
|
+
/* @__PURE__ */ jsxs(Box, { paddingX: 1, columnGap: 2, marginTop: 2, children: [
|
|
109
|
+
/* @__PURE__ */ jsx(DefinitionItem, { label: "Expert mode", values: ["E"], isActive: store.expertMode }),
|
|
110
|
+
/* @__PURE__ */ jsx(
|
|
111
|
+
DefinitionItem,
|
|
112
|
+
{
|
|
113
|
+
label: "Descriptions",
|
|
114
|
+
values: ["D"],
|
|
115
|
+
isVisible: store.step === "build",
|
|
116
|
+
isActive: store.showDescriptions
|
|
117
|
+
}
|
|
118
|
+
),
|
|
119
|
+
/* @__PURE__ */ jsx(
|
|
120
|
+
DefinitionItem,
|
|
121
|
+
{
|
|
122
|
+
label: "Plugin mode",
|
|
123
|
+
values: ["P"],
|
|
124
|
+
isActive: store.installMode === "plugin"
|
|
125
|
+
}
|
|
126
|
+
),
|
|
127
|
+
/* @__PURE__ */ jsx(
|
|
128
|
+
DefinitionItem,
|
|
129
|
+
{
|
|
130
|
+
label: "Settings",
|
|
131
|
+
values: ["G"],
|
|
132
|
+
isVisible: store.step === "sources",
|
|
133
|
+
isActive: store.showSettings
|
|
134
|
+
}
|
|
135
|
+
),
|
|
136
|
+
/* @__PURE__ */ jsx(DefinitionItem, { label: "Help", values: ["?"] })
|
|
137
|
+
] }),
|
|
138
|
+
/* @__PURE__ */ jsx(WizardFooter, {})
|
|
139
|
+
] })
|
|
140
|
+
] });
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export {
|
|
144
|
+
WizardLayout
|
|
145
|
+
};
|
|
146
|
+
//# sourceMappingURL=chunk-PBEHPQLK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/components/wizard/wizard-layout.tsx"],"sourcesContent":["import React, { Fragment } from \"react\";\nimport { Box, Text } from \"ink\";\nimport { useWizardStore } from \"../../stores/wizard-store.js\";\nimport { CLI_COLORS } from \"../../consts.js\";\nimport { WizardTabs, WIZARD_STEPS } from \"./wizard-tabs.js\";\nimport { HelpModal } from \"./help-modal.js\";\n\ntype KeyHintProps = {\n isVisible?: boolean;\n isActive?: boolean;\n label: string;\n values: string[];\n};\n\nconst DefinitionItem: React.FC<KeyHintProps> = ({\n isVisible = true,\n isActive = false,\n label,\n values,\n}) => {\n if (!isVisible) {\n return null;\n }\n\n return (\n <Text>\n {values.map((value) => (\n <Fragment key={value}>\n <Text backgroundColor=\"black\" color={CLI_COLORS.UNFOCUSED}>\n {\" \"}\n {value}{\" \"}\n </Text>{\" \"}\n </Fragment>\n ))}\n <Text color={isActive ? CLI_COLORS.PRIMARY : undefined}>{label}</Text>\n </Text>\n );\n};\n\nconst HOT_KEYS: { label: string; values: string[] }[] = [\n { label: \"navigate\", values: [\"\\u2190/\\u2192\", \"\\u2191/\\u2193\"] },\n { label: \"select\", values: [\"SPACE\"] },\n { label: \"continue\", values: [\"ENTER\"] },\n { label: \"back\", values: [\"ESC\"] },\n];\n\nconst WizardFooter = () => {\n const store = useWizardStore();\n\n return (\n <Box\n columnGap={2}\n borderTop\n borderRight={false}\n borderBottom\n borderLeft={false}\n borderColor=\"blackBright\"\n borderStyle=\"single\"\n paddingLeft={1}\n paddingRight={1}\n >\n <DefinitionItem\n label=\"Accept defaults\"\n values={[\"A\"]}\n isVisible={store.step === \"build\" && !!store.selectedStackId}\n />\n {HOT_KEYS.map((hotkey) => (\n <DefinitionItem {...hotkey} key={hotkey.label} />\n ))}\n </Box>\n );\n};\n\ntype WizardLayoutProps = {\n version?: string;\n marketplaceLabel?: string;\n brandingName?: string;\n /** Terminal height in rows, used to constrain the layout for flexGrow measurement */\n terminalHeight: number;\n children: React.ReactNode;\n};\n\nexport const WizardLayout: React.FC<WizardLayoutProps> = ({\n version,\n marketplaceLabel,\n brandingName,\n terminalHeight,\n children,\n}) => {\n const store = useWizardStore();\n const { completedSteps, skippedSteps } = store.getStepProgress();\n\n // Constrain height only during the build step so flexGrow-based measurement\n // can determine the grid area. Other steps grow to fit their content.\n const constrainedHeight = store.step === \"build\" ? terminalHeight : undefined;\n\n return (\n <Box flexDirection=\"column\" paddingX={1} height={constrainedHeight}>\n <WizardTabs\n steps={WIZARD_STEPS}\n currentStep={store.step}\n completedSteps={completedSteps}\n skippedSteps={skippedSteps}\n version={version}\n brandingName={brandingName}\n />\n {marketplaceLabel && (\n <Box paddingLeft={1} marginTop={1}>\n <Text dimColor>Marketplace: </Text>\n <Text bold>{marketplaceLabel}</Text>\n </Box>\n )}\n {store.showHelp ? (\n <HelpModal currentStep={store.step} />\n ) : (\n <>\n <Box flexGrow={1} marginTop={1}>\n {children}\n </Box>\n <Box paddingX={1} columnGap={2} marginTop={2}>\n <DefinitionItem label=\"Expert mode\" values={[\"E\"]} isActive={store.expertMode} />\n <DefinitionItem\n label=\"Descriptions\"\n values={[\"D\"]}\n isVisible={store.step === \"build\"}\n isActive={store.showDescriptions}\n />\n <DefinitionItem\n label=\"Plugin mode\"\n values={[\"P\"]}\n isActive={store.installMode === \"plugin\"}\n />\n <DefinitionItem\n label=\"Settings\"\n values={[\"G\"]}\n isVisible={store.step === \"sources\"}\n isActive={store.showSettings}\n />\n <DefinitionItem label=\"Help\" values={[\"?\"]} />\n </Box>\n <WizardFooter />\n </>\n )}\n </Box>\n );\n};\n"],"mappings":"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","names":["Fragment"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
CLI_COLORS,
|
|
4
|
+
UI_SYMBOLS
|
|
5
|
+
} from "./chunk-LAPCUV4D.js";
|
|
6
|
+
import {
|
|
7
|
+
init_esm_shims
|
|
8
|
+
} from "./chunk-DHET7RCE.js";
|
|
9
|
+
|
|
10
|
+
// src/cli/components/wizard/menu-item.tsx
|
|
11
|
+
init_esm_shims();
|
|
12
|
+
import { Box, Text } from "ink";
|
|
13
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
14
|
+
var { CHEVRON, CHEVRON_SPACER } = UI_SYMBOLS;
|
|
15
|
+
var MenuItem = ({
|
|
16
|
+
label,
|
|
17
|
+
description,
|
|
18
|
+
isFocused = false,
|
|
19
|
+
isActive = false
|
|
20
|
+
}) => {
|
|
21
|
+
const showCyan = isFocused || isActive;
|
|
22
|
+
return /* @__PURE__ */ jsxs(Box, { columnGap: 1, children: [
|
|
23
|
+
/* @__PURE__ */ jsx(Text, { color: isFocused ? CLI_COLORS.PRIMARY : void 0, children: isFocused ? CHEVRON : CHEVRON_SPACER }),
|
|
24
|
+
/* @__PURE__ */ jsx(Text, { bold: isFocused, color: showCyan ? CLI_COLORS.PRIMARY : void 0, children: label }),
|
|
25
|
+
isFocused && description && /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${description}` })
|
|
26
|
+
] });
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export {
|
|
30
|
+
MenuItem
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=chunk-QPTOIZAT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/components/wizard/menu-item.tsx"],"sourcesContent":["import React from \"react\";\nimport { Box, Text } from \"ink\";\nimport { CLI_COLORS, UI_SYMBOLS } from \"../../consts.js\";\n\nconst { CHEVRON, CHEVRON_SPACER } = UI_SYMBOLS;\n\ntype MenuItemProps = {\n label: string;\n description?: string;\n isFocused?: boolean;\n isActive?: boolean;\n};\n\nexport const MenuItem: React.FC<MenuItemProps> = ({\n label,\n description,\n isFocused = false,\n isActive = false,\n}) => {\n const showCyan = isFocused || isActive;\n\n return (\n <Box columnGap={1}>\n <Text color={isFocused ? CLI_COLORS.PRIMARY : undefined}>\n {isFocused ? CHEVRON : CHEVRON_SPACER}\n </Text>\n <Text bold={isFocused} color={showCyan ? CLI_COLORS.PRIMARY : undefined}>\n {label}\n </Text>\n {isFocused && description && <Text dimColor>{` ${description}`}</Text>}\n </Box>\n );\n};\n"],"mappings":";;;;;;;;;;AAAA;AACA,SAAS,KAAK,YAAY;AAqBtB,SACE,KADF;AAlBJ,IAAM,EAAE,SAAS,eAAe,IAAI;AAS7B,IAAM,WAAoC,CAAC;AAAA,EAChD;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ,WAAW;AACb,MAAM;AACJ,QAAM,WAAW,aAAa;AAE9B,SACE,qBAAC,OAAI,WAAW,GACd;AAAA,wBAAC,QAAK,OAAO,YAAY,WAAW,UAAU,QAC3C,sBAAY,UAAU,gBACzB;AAAA,IACA,oBAAC,QAAK,MAAM,WAAW,OAAO,WAAW,WAAW,UAAU,QAC3D,iBACH;AAAA,IACC,aAAa,eAAe,oBAAC,QAAK,UAAQ,MAAE,cAAI,WAAW,IAAG;AAAA,KACjE;AAEJ;","names":[]}
|