@agents-inc/cli 0.41.1 → 0.42.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 +24 -0
- package/README.md +1 -1
- package/dist/{chunk-ZLHGJSRK.js → chunk-2TWELY5M.js} +3 -3
- package/dist/{chunk-4UTPJXUX.js → chunk-33D24DAF.js} +22 -54
- package/dist/chunk-33D24DAF.js.map +1 -0
- package/dist/{chunk-UKTYDNWJ.js → chunk-44QCEK7E.js} +5 -5
- package/dist/chunk-44QCEK7E.js.map +1 -0
- package/dist/{chunk-NYP5SB2V.js → chunk-52SI5XJH.js} +2 -2
- package/dist/{chunk-GEDWVX6Y.js → chunk-53K3URKF.js} +8 -8
- package/dist/chunk-53K3URKF.js.map +1 -0
- package/dist/{chunk-2D6LKRHW.js → chunk-ALWLM5MC.js} +2 -2
- package/dist/{chunk-AVVYFEMF.js → chunk-AQANPOLS.js} +2 -2
- package/dist/{chunk-5TMB53BV.js → chunk-BKW34TKI.js} +7 -4
- package/dist/chunk-BKW34TKI.js.map +1 -0
- package/dist/{chunk-BFISETQG.js → chunk-EFZN22TO.js} +3 -3
- package/dist/{chunk-7FBM7V3E.js → chunk-EPJ2GJNJ.js} +10 -10
- package/dist/chunk-EPJ2GJNJ.js.map +1 -0
- package/dist/{chunk-SILUTTV7.js → chunk-FDY6SGSA.js} +2 -2
- package/dist/{chunk-342YB6TQ.js → chunk-FF4Z7MHY.js} +20 -13
- package/dist/chunk-FF4Z7MHY.js.map +1 -0
- package/dist/{chunk-ACVJVYMC.js → chunk-GCN7GGWE.js} +15 -27
- package/dist/chunk-GCN7GGWE.js.map +1 -0
- package/dist/{chunk-FHBICUXB.js → chunk-GIZ6DENW.js} +4 -4
- package/dist/{chunk-KC2SIUIA.js → chunk-GVRZY5KI.js} +4 -5
- package/dist/chunk-GVRZY5KI.js.map +1 -0
- package/dist/{chunk-LJRP4SWY.js → chunk-HMGWGWFT.js} +2 -2
- package/dist/{chunk-H6H3COI5.js → chunk-IUYU2TP6.js} +5 -5
- package/dist/{chunk-AH7XHAKN.js → chunk-J73KIP6Z.js} +2 -2
- package/dist/chunk-J73KIP6Z.js.map +1 -0
- package/dist/{chunk-TJAZ7QCF.js → chunk-KA253GGY.js} +4 -4
- package/dist/{chunk-4SYXPG7L.js → chunk-MHET2RG2.js} +8 -3
- package/dist/chunk-MHET2RG2.js.map +1 -0
- package/dist/{chunk-DV4ALU5I.js → chunk-MI4NWOWD.js} +17 -24
- package/dist/chunk-MI4NWOWD.js.map +1 -0
- package/dist/chunk-MMD26LKJ.js +20 -0
- package/dist/chunk-MMD26LKJ.js.map +1 -0
- package/dist/{chunk-MNPPGIZQ.js → chunk-ORJPGZVD.js} +5 -5
- package/dist/{chunk-R52N7DBG.js → chunk-PEAPABFI.js} +4 -4
- package/dist/chunk-PEAPABFI.js.map +1 -0
- package/dist/{chunk-BK7TANUV.js → chunk-PP7NDFFE.js} +4 -3
- package/dist/{chunk-BK7TANUV.js.map → chunk-PP7NDFFE.js.map} +1 -1
- package/dist/{chunk-TTXV55NQ.js → chunk-SYHRJG5G.js} +6 -3
- package/dist/chunk-SYHRJG5G.js.map +1 -0
- package/dist/{chunk-NZYKDVRL.js → chunk-UVVNWF43.js} +2 -2
- package/dist/{chunk-NZYKDVRL.js.map → chunk-UVVNWF43.js.map} +1 -1
- package/dist/{chunk-XJXJZ2MJ.js → chunk-UXNHU7Y7.js} +5 -1
- package/dist/chunk-UXNHU7Y7.js.map +1 -0
- package/dist/{chunk-YLJYAQSG.js → chunk-VHGIQN5O.js} +7 -5
- package/dist/chunk-VHGIQN5O.js.map +1 -0
- package/dist/{chunk-4LT6RXMY.js → chunk-WMAALRQI.js} +4 -4
- package/dist/{chunk-PURJZ72D.js → chunk-X34RGEFX.js} +2 -2
- package/dist/{chunk-423MJ6DT.js → chunk-XRA4LHTJ.js} +5 -5
- package/dist/chunk-ZHJEZ7AU.js +40 -0
- package/dist/chunk-ZHJEZ7AU.js.map +1 -0
- package/dist/commands/build/marketplace.js +3 -3
- package/dist/commands/build/plugins.js +5 -5
- package/dist/commands/build/stack.js +5 -5
- package/dist/commands/compile.js +6 -6
- package/dist/commands/config/get.js +4 -4
- package/dist/commands/config/index.js +5 -5
- package/dist/commands/config/path.js +4 -4
- package/dist/commands/config/set-project.js +4 -4
- package/dist/commands/config/show.js +5 -5
- package/dist/commands/config/unset-project.js +4 -4
- package/dist/commands/diff.js +4 -4
- package/dist/commands/doctor.js +4 -4
- package/dist/commands/edit.js +37 -30
- package/dist/commands/edit.js.map +1 -1
- package/dist/commands/eject.js +33 -13
- package/dist/commands/eject.js.map +1 -1
- package/dist/commands/import/skill.js +5 -5
- package/dist/commands/info.js +5 -5
- package/dist/commands/init.js +27 -27
- package/dist/commands/list.js +4 -4
- package/dist/commands/new/agent.js +5 -5
- package/dist/commands/new/skill.js +4 -4
- package/dist/commands/outdated.js +4 -4
- package/dist/commands/search.js +7 -7
- package/dist/commands/uninstall.js +6 -6
- package/dist/commands/update.js +6 -6
- package/dist/commands/validate.js +5 -5
- package/dist/commands/version/bump.js +4 -4
- package/dist/commands/version/index.js +4 -4
- package/dist/commands/version/set.js +4 -4
- package/dist/commands/version/show.js +4 -4
- package/dist/components/skill-search/skill-search.js +3 -3
- package/dist/components/wizard/category-grid.js +2 -2
- package/dist/components/wizard/category-grid.test.js +2 -2
- package/dist/components/wizard/checkbox-grid.js +3 -2
- package/dist/components/wizard/checkbox-grid.test.js +16 -22
- package/dist/components/wizard/checkbox-grid.test.js.map +1 -1
- package/dist/components/wizard/domain-selection.js +8 -7
- package/dist/components/wizard/help-modal.js +2 -2
- package/dist/components/wizard/menu-item.js +25 -4
- package/dist/components/wizard/menu-item.js.map +1 -1
- package/dist/components/wizard/search-modal.js +2 -2
- package/dist/components/wizard/search-modal.test.js +2 -2
- package/dist/components/wizard/section-progress.js +2 -2
- package/dist/components/wizard/section-progress.test.js +2 -2
- package/dist/components/wizard/selection-card.js +10 -0
- package/dist/components/wizard/source-grid.js +3 -3
- package/dist/components/wizard/source-grid.test.js +3 -3
- package/dist/components/wizard/stack-selection.js +8 -8
- package/dist/components/wizard/step-agents.js +7 -6
- package/dist/components/wizard/step-agents.test.js +8 -8
- package/dist/components/wizard/step-agents.test.js.map +1 -1
- package/dist/components/wizard/step-build.js +7 -7
- package/dist/components/wizard/step-build.test.js +18 -18
- package/dist/components/wizard/step-build.test.js.map +1 -1
- package/dist/components/wizard/step-confirm.js +3 -2
- package/dist/components/wizard/step-confirm.test.js +7 -6
- package/dist/components/wizard/step-confirm.test.js.map +1 -1
- package/dist/components/wizard/step-refine.js +2 -2
- package/dist/components/wizard/step-refine.test.js +2 -2
- package/dist/components/wizard/step-settings.js +5 -5
- package/dist/components/wizard/step-settings.test.js +8 -8
- package/dist/components/wizard/step-sources.js +10 -9
- package/dist/components/wizard/step-sources.test.js +14 -12
- package/dist/components/wizard/step-sources.test.js.map +1 -1
- package/dist/components/wizard/step-stack.js +11 -11
- package/dist/components/wizard/step-stack.test.js +15 -16
- package/dist/components/wizard/step-stack.test.js.map +1 -1
- package/dist/components/wizard/view-title.js +2 -2
- package/dist/components/wizard/wizard-layout.js +8 -8
- package/dist/components/wizard/wizard-tabs.js +2 -2
- package/dist/components/wizard/wizard-tabs.test.js +2 -2
- package/dist/components/wizard/wizard.js +24 -24
- package/dist/hooks/init.js +3 -3
- package/dist/{source-manager-PPABS6BC.js → source-manager-ABK5COKX.js} +4 -4
- package/dist/source-manager-ABK5COKX.js.map +1 -0
- package/dist/stores/wizard-store.js +5 -5
- package/dist/stores/wizard-store.test.js +6 -6
- package/package.json +1 -1
- package/src/schemas/project-config.schema.json +7 -0
- package/dist/chunk-342YB6TQ.js.map +0 -1
- package/dist/chunk-4SYXPG7L.js.map +0 -1
- package/dist/chunk-4UTPJXUX.js.map +0 -1
- package/dist/chunk-5TMB53BV.js.map +0 -1
- package/dist/chunk-7FBM7V3E.js.map +0 -1
- package/dist/chunk-ACVJVYMC.js.map +0 -1
- package/dist/chunk-AH7XHAKN.js.map +0 -1
- package/dist/chunk-DV4ALU5I.js.map +0 -1
- package/dist/chunk-GEDWVX6Y.js.map +0 -1
- package/dist/chunk-KC2SIUIA.js.map +0 -1
- package/dist/chunk-KXM7KOPE.js +0 -31
- package/dist/chunk-KXM7KOPE.js.map +0 -1
- package/dist/chunk-R52N7DBG.js.map +0 -1
- package/dist/chunk-TTXV55NQ.js.map +0 -1
- package/dist/chunk-UKTYDNWJ.js.map +0 -1
- package/dist/chunk-WS6OQIEN.js +0 -32
- package/dist/chunk-WS6OQIEN.js.map +0 -1
- package/dist/chunk-XJXJZ2MJ.js.map +0 -1
- package/dist/chunk-YLJYAQSG.js.map +0 -1
- /package/dist/{chunk-ZLHGJSRK.js.map → chunk-2TWELY5M.js.map} +0 -0
- /package/dist/{chunk-NYP5SB2V.js.map → chunk-52SI5XJH.js.map} +0 -0
- /package/dist/{chunk-2D6LKRHW.js.map → chunk-ALWLM5MC.js.map} +0 -0
- /package/dist/{chunk-AVVYFEMF.js.map → chunk-AQANPOLS.js.map} +0 -0
- /package/dist/{chunk-BFISETQG.js.map → chunk-EFZN22TO.js.map} +0 -0
- /package/dist/{chunk-SILUTTV7.js.map → chunk-FDY6SGSA.js.map} +0 -0
- /package/dist/{chunk-FHBICUXB.js.map → chunk-GIZ6DENW.js.map} +0 -0
- /package/dist/{chunk-LJRP4SWY.js.map → chunk-HMGWGWFT.js.map} +0 -0
- /package/dist/{chunk-H6H3COI5.js.map → chunk-IUYU2TP6.js.map} +0 -0
- /package/dist/{chunk-TJAZ7QCF.js.map → chunk-KA253GGY.js.map} +0 -0
- /package/dist/{chunk-MNPPGIZQ.js.map → chunk-ORJPGZVD.js.map} +0 -0
- /package/dist/{chunk-4LT6RXMY.js.map → chunk-WMAALRQI.js.map} +0 -0
- /package/dist/{chunk-PURJZ72D.js.map → chunk-X34RGEFX.js.map} +0 -0
- /package/dist/{chunk-423MJ6DT.js.map → chunk-XRA4LHTJ.js.map} +0 -0
- /package/dist/{source-manager-PPABS6BC.js.map → components/wizard/selection-card.js.map} +0 -0
|
@@ -3,8 +3,8 @@ import {
|
|
|
3
3
|
WIZARD_STEPS,
|
|
4
4
|
WizardTabs,
|
|
5
5
|
formatStepLabel
|
|
6
|
-
} from "../../chunk-
|
|
7
|
-
import "../../chunk-
|
|
6
|
+
} from "../../chunk-MHET2RG2.js";
|
|
7
|
+
import "../../chunk-PP7NDFFE.js";
|
|
8
8
|
import "../../chunk-DHET7RCE.js";
|
|
9
9
|
export {
|
|
10
10
|
WIZARD_STEPS,
|
|
@@ -12,8 +12,8 @@ import {
|
|
|
12
12
|
WIZARD_STEPS,
|
|
13
13
|
WizardTabs,
|
|
14
14
|
formatStepLabel
|
|
15
|
-
} from "../../chunk-
|
|
16
|
-
import "../../chunk-
|
|
15
|
+
} from "../../chunk-MHET2RG2.js";
|
|
16
|
+
import "../../chunk-PP7NDFFE.js";
|
|
17
17
|
import {
|
|
18
18
|
init_esm_shims
|
|
19
19
|
} from "../../chunk-DHET7RCE.js";
|
|
@@ -1,37 +1,37 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
Wizard
|
|
4
|
-
} from "../../chunk-
|
|
5
|
-
import "../../chunk-
|
|
6
|
-
import "../../chunk-
|
|
7
|
-
import "../../chunk-
|
|
8
|
-
import "../../chunk-
|
|
9
|
-
import "../../chunk-
|
|
10
|
-
import "../../chunk-
|
|
11
|
-
import "../../chunk-
|
|
4
|
+
} from "../../chunk-FF4Z7MHY.js";
|
|
5
|
+
import "../../chunk-WMAALRQI.js";
|
|
6
|
+
import "../../chunk-ORJPGZVD.js";
|
|
7
|
+
import "../../chunk-MHET2RG2.js";
|
|
8
|
+
import "../../chunk-BKW34TKI.js";
|
|
9
|
+
import "../../chunk-IUYU2TP6.js";
|
|
10
|
+
import "../../chunk-33D24DAF.js";
|
|
11
|
+
import "../../chunk-EFZN22TO.js";
|
|
12
12
|
import "../../chunk-7SOPVGDV.js";
|
|
13
|
-
import "../../chunk-
|
|
14
|
-
import "../../chunk-
|
|
15
|
-
import "../../chunk-
|
|
13
|
+
import "../../chunk-MI4NWOWD.js";
|
|
14
|
+
import "../../chunk-ZHJEZ7AU.js";
|
|
15
|
+
import "../../chunk-VHGIQN5O.js";
|
|
16
|
+
import "../../chunk-EPJ2GJNJ.js";
|
|
16
17
|
import "../../chunk-YRVTXSXP.js";
|
|
17
|
-
import "../../chunk-KXM7KOPE.js";
|
|
18
18
|
import "../../chunk-K77I4XGL.js";
|
|
19
|
-
import "../../chunk-
|
|
20
|
-
import "../../chunk-
|
|
21
|
-
import "../../chunk-
|
|
22
|
-
import "../../chunk-
|
|
23
|
-
import "../../chunk-
|
|
19
|
+
import "../../chunk-GVRZY5KI.js";
|
|
20
|
+
import "../../chunk-GCN7GGWE.js";
|
|
21
|
+
import "../../chunk-MMD26LKJ.js";
|
|
22
|
+
import "../../chunk-44QCEK7E.js";
|
|
23
|
+
import "../../chunk-52SI5XJH.js";
|
|
24
24
|
import "../../chunk-KUV24B5M.js";
|
|
25
|
-
import "../../chunk-
|
|
25
|
+
import "../../chunk-53K3URKF.js";
|
|
26
26
|
import "../../chunk-GG4BSB6S.js";
|
|
27
|
-
import "../../chunk-
|
|
27
|
+
import "../../chunk-AQANPOLS.js";
|
|
28
28
|
import "../../chunk-U3IGFMCY.js";
|
|
29
|
-
import "../../chunk-
|
|
30
|
-
import "../../chunk-
|
|
31
|
-
import "../../chunk-
|
|
29
|
+
import "../../chunk-XRA4LHTJ.js";
|
|
30
|
+
import "../../chunk-FDY6SGSA.js";
|
|
31
|
+
import "../../chunk-SYHRJG5G.js";
|
|
32
32
|
import "../../chunk-T4EXUIBY.js";
|
|
33
|
-
import "../../chunk-
|
|
34
|
-
import "../../chunk-
|
|
33
|
+
import "../../chunk-UXNHU7Y7.js";
|
|
34
|
+
import "../../chunk-PP7NDFFE.js";
|
|
35
35
|
import "../../chunk-DHET7RCE.js";
|
|
36
36
|
export {
|
|
37
37
|
Wizard
|
package/dist/hooks/init.js
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import {
|
|
3
3
|
loadDefaultMappings,
|
|
4
4
|
resolveSource
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-SYHRJG5G.js";
|
|
6
6
|
import "../chunk-T4EXUIBY.js";
|
|
7
|
-
import "../chunk-
|
|
8
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-UXNHU7Y7.js";
|
|
8
|
+
import "../chunk-PP7NDFFE.js";
|
|
9
9
|
import {
|
|
10
10
|
init_esm_shims
|
|
11
11
|
} from "../chunk-DHET7RCE.js";
|
|
@@ -3,14 +3,14 @@ import {
|
|
|
3
3
|
addSource,
|
|
4
4
|
getSourceSummary,
|
|
5
5
|
removeSource
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-SYHRJG5G.js";
|
|
7
7
|
import "./chunk-T4EXUIBY.js";
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
8
|
+
import "./chunk-UXNHU7Y7.js";
|
|
9
|
+
import "./chunk-PP7NDFFE.js";
|
|
10
10
|
import "./chunk-DHET7RCE.js";
|
|
11
11
|
export {
|
|
12
12
|
addSource,
|
|
13
13
|
getSourceSummary,
|
|
14
14
|
removeSource
|
|
15
15
|
};
|
|
16
|
-
//# sourceMappingURL=source-manager-
|
|
16
|
+
//# sourceMappingURL=source-manager-ABK5COKX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
useWizardStore
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
6
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-XRA4LHTJ.js";
|
|
5
|
+
import "../chunk-FDY6SGSA.js";
|
|
6
|
+
import "../chunk-SYHRJG5G.js";
|
|
7
7
|
import "../chunk-T4EXUIBY.js";
|
|
8
|
-
import "../chunk-
|
|
9
|
-
import "../chunk-
|
|
8
|
+
import "../chunk-UXNHU7Y7.js";
|
|
9
|
+
import "../chunk-PP7NDFFE.js";
|
|
10
10
|
import "../chunk-DHET7RCE.js";
|
|
11
11
|
export {
|
|
12
12
|
useWizardStore
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
createMockMatrix,
|
|
4
4
|
createMockSkill
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-J73KIP6Z.js";
|
|
6
6
|
import {
|
|
7
7
|
beforeEach,
|
|
8
8
|
describe,
|
|
@@ -11,16 +11,16 @@ import {
|
|
|
11
11
|
} from "../chunk-XY3XDVMI.js";
|
|
12
12
|
import {
|
|
13
13
|
useWizardStore
|
|
14
|
-
} from "../chunk-
|
|
15
|
-
import "../chunk-
|
|
16
|
-
import "../chunk-
|
|
14
|
+
} from "../chunk-XRA4LHTJ.js";
|
|
15
|
+
import "../chunk-FDY6SGSA.js";
|
|
16
|
+
import "../chunk-SYHRJG5G.js";
|
|
17
17
|
import {
|
|
18
18
|
typedKeys
|
|
19
19
|
} from "../chunk-T4EXUIBY.js";
|
|
20
|
-
import "../chunk-
|
|
20
|
+
import "../chunk-UXNHU7Y7.js";
|
|
21
21
|
import {
|
|
22
22
|
DEFAULT_PRESELECTED_SKILLS
|
|
23
|
-
} from "../chunk-
|
|
23
|
+
} from "../chunk-PP7NDFFE.js";
|
|
24
24
|
import {
|
|
25
25
|
init_esm_shims
|
|
26
26
|
} from "../chunk-DHET7RCE.js";
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/components/wizard/wizard.tsx","../src/cli/components/hooks/use-wizard-initialization.ts","../src/cli/components/hooks/use-build-step-props.ts"],"sourcesContent":["import React, { useCallback } from \"react\";\nimport { Box, Text, useApp, useInput, useStdout } from \"ink\";\nimport { ThemeProvider } from \"@inkjs/ui\";\nimport { useWizardStore, type WizardStep } from \"../../stores/wizard-store.js\";\nimport { CLI_COLORS } from \"../../consts.js\";\nimport { cliTheme } from \"../themes/default.js\";\nimport { WizardLayout } from \"./wizard-layout.js\";\nimport { StepStack } from \"./step-stack.js\";\nimport { StepBuild } from \"./step-build.js\";\nimport { StepConfirm } from \"./step-confirm.js\";\nimport { StepSources } from \"./step-sources.js\";\nimport { StepSettings } from \"./step-settings.js\";\nimport { StepAgents } from \"./step-agents.js\";\nimport { resolveAlias, validateSelection } from \"../../lib/matrix/index.js\";\nimport type {\n AgentName,\n DomainSelections,\n MergedSkillsMatrix,\n SkillId,\n} from \"../../types/index.js\";\nimport { getStackName } from \"./utils.js\";\nimport { warn } from \"../../utils/logger.js\";\nimport { useWizardInitialization } from \"../hooks/use-wizard-initialization.js\";\nimport { useBuildStepProps } from \"../hooks/use-build-step-props.js\";\n\nexport type WizardResultV2 = {\n selectedSkills: SkillId[];\n selectedAgents: AgentName[];\n selectedStackId: string | null;\n domainSelections: DomainSelections;\n sourceSelections: Partial<Record<SkillId, string>>;\n expertMode: boolean;\n installMode: \"plugin\" | \"local\";\n cancelled: boolean;\n validation: {\n valid: boolean;\n errors: Array<{ message: string }>;\n warnings: Array<{ message: string }>;\n };\n};\n\ntype WizardProps = {\n matrix: MergedSkillsMatrix;\n onComplete: (result: WizardResultV2) => void;\n onCancel: () => void;\n version?: string;\n marketplaceLabel?: string;\n logo?: string;\n initialStep?: WizardStep;\n initialInstallMode?: \"plugin\" | \"local\";\n initialExpertMode?: boolean;\n installedSkillIds?: SkillId[];\n projectDir?: string;\n};\n\nconst MIN_TERMINAL_WIDTH = 80;\nconst MIN_TERMINAL_HEIGHT = 15;\n\nexport const Wizard: React.FC<WizardProps> = ({\n matrix,\n onComplete,\n onCancel,\n version,\n marketplaceLabel,\n logo,\n initialStep,\n initialInstallMode,\n initialExpertMode,\n installedSkillIds,\n projectDir,\n}) => {\n const store = useWizardStore();\n const { exit } = useApp();\n const { stdout } = useStdout();\n\n const terminalWidth = stdout.columns || MIN_TERMINAL_WIDTH;\n const terminalHeight = stdout.rows || MIN_TERMINAL_HEIGHT;\n const isNarrowTerminal = terminalWidth < MIN_TERMINAL_WIDTH;\n const isShortTerminal = terminalHeight < MIN_TERMINAL_HEIGHT;\n\n useWizardInitialization({\n matrix,\n initialStep,\n initialInstallMode,\n initialExpertMode,\n installedSkillIds,\n });\n\n const buildStepProps = useBuildStepProps({ store, matrix, installedSkillIds });\n\n useInput((input, key) => {\n // ESC is handled by step-settings.tsx's own useKeyboardNavigation hook\n if (store.showSettings) {\n if (input === \"g\" || input === \"G\") {\n store.toggleSettings();\n }\n return;\n }\n\n if (store.showHelp) {\n if (key.escape || input === \"?\") {\n store.toggleHelp();\n }\n return;\n }\n\n if (input === \"?\") {\n store.toggleHelp();\n return;\n }\n\n if (key.escape) {\n // At the initial stack/scratch selection (approach not yet set), ESC cancels the wizard.\n // StackSelection handles its own ESC via the onCancel prop.\n // Other steps that don't have their own escape handler use goBack.\n if (\n store.step !== \"build\" &&\n store.step !== \"confirm\" &&\n store.step !== \"sources\" &&\n store.step !== \"agents\" &&\n store.step !== \"stack\"\n ) {\n store.goBack();\n }\n return;\n }\n\n if ((input === \"a\" || input === \"A\") && store.step === \"build\" && store.selectedStackId) {\n store.setStackAction(\"defaults\");\n store.setStep(\"confirm\");\n return;\n }\n\n if ((input === \"g\" || input === \"G\") && store.step === \"sources\") {\n store.toggleSettings();\n return;\n }\n\n if (input === \"e\" || input === \"E\") {\n store.toggleExpertMode();\n return;\n }\n if (input === \"p\" || input === \"P\") {\n store.toggleInstallMode();\n }\n });\n\n const handleComplete = useCallback(() => {\n let allSkills: SkillId[];\n\n if (store.selectedStackId && store.stackAction === \"defaults\") {\n const stack = matrix.suggestedStacks.find((s) => s.id === store.selectedStackId);\n if (!stack) {\n warn(`Stack not found in matrix: '${store.selectedStackId}'`);\n }\n allSkills = [...(stack?.allSkillIds || [])];\n } else {\n const techNames = store.getAllSelectedTechnologies();\n allSkills = techNames.map((tech) => {\n const resolved = resolveAlias(tech, matrix);\n if (!matrix.skills[resolved]) {\n warn(\n `Technology '${tech}' could not be resolved to a skill ID - it may be missing from skill_aliases`,\n );\n }\n return resolved;\n });\n }\n\n const methodologySkills = store.getDefaultMethodologySkills();\n for (const skill of methodologySkills) {\n if (!allSkills.includes(skill)) {\n allSkills.push(skill);\n }\n }\n\n const validation = validateSelection(allSkills, matrix);\n\n const result: WizardResultV2 = {\n selectedSkills: allSkills,\n selectedAgents: store.selectedAgents,\n selectedStackId: store.selectedStackId,\n domainSelections: store.domainSelections,\n sourceSelections: store.sourceSelections,\n expertMode: store.expertMode,\n installMode: store.installMode,\n cancelled: false,\n validation,\n };\n\n onComplete(result);\n exit();\n }, [store, matrix, onComplete, exit]);\n\n const handleCancel = useCallback(() => {\n onCancel();\n exit();\n }, [onCancel, exit]);\n\n const renderStep = () => {\n switch (store.step) {\n case \"stack\":\n return <StepStack matrix={matrix} onCancel={handleCancel} />;\n\n case \"build\":\n return <StepBuild {...buildStepProps} />;\n\n case \"sources\": {\n if (store.showSettings) {\n return (\n <StepSettings\n projectDir={projectDir || process.cwd()}\n onClose={() => store.toggleSettings()}\n />\n );\n }\n return (\n <StepSources\n matrix={matrix}\n projectDir={projectDir}\n onContinue={() => {\n store.preselectAgentsFromDomains();\n store.setStep(\"agents\");\n }}\n onBack={store.goBack}\n />\n );\n }\n\n case \"agents\":\n return <StepAgents />;\n\n case \"confirm\": {\n const stackName = getStackName(store.selectedStackId, matrix);\n const technologyCount = store.getTechnologyCount();\n return (\n <StepConfirm\n onComplete={handleComplete}\n stackName={stackName}\n selectedDomains={store.selectedDomains}\n domainSelections={store.domainSelections}\n technologyCount={technologyCount}\n skillCount={technologyCount}\n agentCount={store.selectedAgents.length}\n installMode={store.installMode}\n onBack={store.goBack}\n />\n );\n }\n\n default:\n return null;\n }\n };\n\n if (isNarrowTerminal || isShortTerminal) {\n const issue = isNarrowTerminal\n ? `too narrow (${terminalWidth} columns, need ${MIN_TERMINAL_WIDTH})`\n : `too short (${terminalHeight} rows, need ${MIN_TERMINAL_HEIGHT})`;\n\n return (\n <ThemeProvider theme={cliTheme}>\n <Box flexDirection=\"column\" padding={1}>\n <Text color={CLI_COLORS.WARNING}>Terminal {issue}. Please resize your terminal.</Text>\n </Box>\n </ThemeProvider>\n );\n }\n\n return (\n <ThemeProvider theme={cliTheme}>\n <WizardLayout version={version} marketplaceLabel={marketplaceLabel} logo={logo}>\n {renderStep()}\n </WizardLayout>\n </ThemeProvider>\n );\n};\n","import { useRef } from \"react\";\nimport { useWizardStore, type WizardStep } from \"../../stores/wizard-store.js\";\nimport type { MergedSkillsMatrix, SkillId } from \"../../types/index.js\";\n\ntype UseWizardInitializationOptions = {\n matrix: MergedSkillsMatrix;\n initialStep?: WizardStep;\n initialInstallMode?: \"plugin\" | \"local\";\n initialExpertMode?: boolean;\n installedSkillIds?: SkillId[];\n};\n\n/**\n * Runs one-time wizard store initialization before the first render.\n * Populates step, approach, install mode, and skill selections from props.\n */\nexport function useWizardInitialization({\n matrix,\n initialStep,\n initialInstallMode,\n initialExpertMode,\n installedSkillIds,\n}: UseWizardInitializationOptions): void {\n const initialized = useRef(false);\n\n if (!initialized.current) {\n initialized.current = true;\n\n if (initialStep) {\n if (installedSkillIds?.length) {\n useWizardStore\n .getState()\n .populateFromSkillIds(installedSkillIds, matrix.skills, matrix.categories);\n }\n useWizardStore.setState({ step: initialStep, approach: \"scratch\" });\n }\n if (initialInstallMode) {\n useWizardStore.setState({ installMode: initialInstallMode });\n }\n if (initialExpertMode) {\n useWizardStore.setState({ expertMode: initialExpertMode });\n }\n }\n}\n","import { useCallback } from \"react\";\nimport type { Domain, MergedSkillsMatrix, SkillId } from \"../../types/index.js\";\nimport type { WizardState } from \"../../stores/wizard-store.js\";\nimport type { StepBuildProps } from \"../wizard/step-build.js\";\n\ntype UseBuildStepPropsOptions = {\n store: WizardState;\n matrix: MergedSkillsMatrix;\n installedSkillIds?: SkillId[];\n};\n\nexport function useBuildStepProps({\n store,\n matrix,\n installedSkillIds,\n}: UseBuildStepPropsOptions): StepBuildProps {\n const currentDomain = store.getCurrentDomain();\n const defaultDomains: Domain[] = [\"web\"];\n const effectiveDomains =\n store.selectedDomains.length > 0 ? store.selectedDomains : defaultDomains;\n\n const allSelections = store.getAllSelectedTechnologies();\n\n const activeDomain: Domain = currentDomain || effectiveDomains[0] || \"web\";\n\n const onToggle = useCallback(\n (subcategoryId: Parameters<StepBuildProps[\"onToggle\"]>[0], techId: SkillId) => {\n const domain: Domain = store.getCurrentDomain() || \"web\";\n const cat = matrix.categories[subcategoryId];\n store.toggleTechnology(domain, subcategoryId, techId, cat?.exclusive ?? true);\n },\n [store, matrix],\n );\n\n const onContinue = useCallback(() => {\n if (!store.nextDomain()) {\n store.setStep(\"sources\");\n }\n }, [store]);\n\n const onBack = useCallback(() => {\n if (!store.prevDomain()) {\n store.goBack();\n }\n }, [store]);\n\n return {\n matrix,\n domain: activeDomain,\n selectedDomains: effectiveDomains,\n selections: store.domainSelections[activeDomain] || {},\n allSelections,\n showLabels: store.showLabels,\n expertMode: store.expertMode,\n installedSkillIds,\n onToggle,\n onToggleLabels: store.toggleShowLabels,\n onContinue,\n onBack,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA,SAAgB,eAAAA,oBAAmB;AACnC,SAAS,KAAK,MAAM,QAAQ,UAAU,iBAAiB;AACvD,SAAS,qBAAqB;;;ACF9B;AAAA,SAAS,cAAc;AAgBhB,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAyC;AACvC,QAAM,cAAc,OAAO,KAAK;AAEhC,MAAI,CAAC,YAAY,SAAS;AACxB,gBAAY,UAAU;AAEtB,QAAI,aAAa;AACf,UAAI,mBAAmB,QAAQ;AAC7B,uBACG,SAAS,EACT,qBAAqB,mBAAmB,OAAO,QAAQ,OAAO,UAAU;AAAA,MAC7E;AACA,qBAAe,SAAS,EAAE,MAAM,aAAa,UAAU,UAAU,CAAC;AAAA,IACpE;AACA,QAAI,oBAAoB;AACtB,qBAAe,SAAS,EAAE,aAAa,mBAAmB,CAAC;AAAA,IAC7D;AACA,QAAI,mBAAmB;AACrB,qBAAe,SAAS,EAAE,YAAY,kBAAkB,CAAC;AAAA,IAC3D;AAAA,EACF;AACF;;;AC3CA;AAAA,SAAS,mBAAmB;AAWrB,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AACF,GAA6C;AAC3C,QAAM,gBAAgB,MAAM,iBAAiB;AAC7C,QAAM,iBAA2B,CAAC,KAAK;AACvC,QAAM,mBACJ,MAAM,gBAAgB,SAAS,IAAI,MAAM,kBAAkB;AAE7D,QAAM,gBAAgB,MAAM,2BAA2B;AAEvD,QAAM,eAAuB,iBAAiB,iBAAiB,CAAC,KAAK;AAErE,QAAM,WAAW;AAAA,IACf,CAAC,eAA0D,WAAoB;AAC7E,YAAM,SAAiB,MAAM,iBAAiB,KAAK;AACnD,YAAM,MAAM,OAAO,WAAW,aAAa;AAC3C,YAAM,iBAAiB,QAAQ,eAAe,QAAQ,KAAK,aAAa,IAAI;AAAA,IAC9E;AAAA,IACA,CAAC,OAAO,MAAM;AAAA,EAChB;AAEA,QAAM,aAAa,YAAY,MAAM;AACnC,QAAI,CAAC,MAAM,WAAW,GAAG;AACvB,YAAM,QAAQ,SAAS;AAAA,IACzB;AAAA,EACF,GAAG,CAAC,KAAK,CAAC;AAEV,QAAM,SAAS,YAAY,MAAM;AAC/B,QAAI,CAAC,MAAM,WAAW,GAAG;AACvB,YAAM,OAAO;AAAA,IACf;AAAA,EACF,GAAG,CAAC,KAAK,CAAC;AAEV,SAAO;AAAA,IACL;AAAA,IACA,QAAQ;AAAA,IACR,iBAAiB;AAAA,IACjB,YAAY,MAAM,iBAAiB,YAAY,KAAK,CAAC;AAAA,IACrD;AAAA,IACA,YAAY,MAAM;AAAA,IAClB,YAAY,MAAM;AAAA,IAClB;AAAA,IACA;AAAA,IACA,gBAAgB,MAAM;AAAA,IACtB;AAAA,IACA;AAAA,EACF;AACF;;;AF8Ie,cA6DL,YA7DK;AAnJf,IAAM,qBAAqB;AAC3B,IAAM,sBAAsB;AAErB,IAAM,SAAgC,CAAC;AAAA,EAC5C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAAM;AACJ,QAAM,QAAQ,eAAe;AAC7B,QAAM,EAAE,KAAK,IAAI,OAAO;AACxB,QAAM,EAAE,OAAO,IAAI,UAAU;AAE7B,QAAM,gBAAgB,OAAO,WAAW;AACxC,QAAM,iBAAiB,OAAO,QAAQ;AACtC,QAAM,mBAAmB,gBAAgB;AACzC,QAAM,kBAAkB,iBAAiB;AAEzC,0BAAwB;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,iBAAiB,kBAAkB,EAAE,OAAO,QAAQ,kBAAkB,CAAC;AAE7E,WAAS,CAAC,OAAO,QAAQ;AAEvB,QAAI,MAAM,cAAc;AACtB,UAAI,UAAU,OAAO,UAAU,KAAK;AAClC,cAAM,eAAe;AAAA,MACvB;AACA;AAAA,IACF;AAEA,QAAI,MAAM,UAAU;AAClB,UAAI,IAAI,UAAU,UAAU,KAAK;AAC/B,cAAM,WAAW;AAAA,MACnB;AACA;AAAA,IACF;AAEA,QAAI,UAAU,KAAK;AACjB,YAAM,WAAW;AACjB;AAAA,IACF;AAEA,QAAI,IAAI,QAAQ;AAId,UACE,MAAM,SAAS,WACf,MAAM,SAAS,aACf,MAAM,SAAS,aACf,MAAM,SAAS,YACf,MAAM,SAAS,SACf;AACA,cAAM,OAAO;AAAA,MACf;AACA;AAAA,IACF;AAEA,SAAK,UAAU,OAAO,UAAU,QAAQ,MAAM,SAAS,WAAW,MAAM,iBAAiB;AACvF,YAAM,eAAe,UAAU;AAC/B,YAAM,QAAQ,SAAS;AACvB;AAAA,IACF;AAEA,SAAK,UAAU,OAAO,UAAU,QAAQ,MAAM,SAAS,WAAW;AAChE,YAAM,eAAe;AACrB;AAAA,IACF;AAEA,QAAI,UAAU,OAAO,UAAU,KAAK;AAClC,YAAM,iBAAiB;AACvB;AAAA,IACF;AACA,QAAI,UAAU,OAAO,UAAU,KAAK;AAClC,YAAM,kBAAkB;AAAA,IAC1B;AAAA,EACF,CAAC;AAED,QAAM,iBAAiBC,aAAY,MAAM;AACvC,QAAI;AAEJ,QAAI,MAAM,mBAAmB,MAAM,gBAAgB,YAAY;AAC7D,YAAM,QAAQ,OAAO,gBAAgB,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM,eAAe;AAC/E,UAAI,CAAC,OAAO;AACV,aAAK,+BAA+B,MAAM,eAAe,GAAG;AAAA,MAC9D;AACA,kBAAY,CAAC,GAAI,OAAO,eAAe,CAAC,CAAE;AAAA,IAC5C,OAAO;AACL,YAAM,YAAY,MAAM,2BAA2B;AACnD,kBAAY,UAAU,IAAI,CAAC,SAAS;AAClC,cAAM,WAAW,aAAa,MAAM,MAAM;AAC1C,YAAI,CAAC,OAAO,OAAO,QAAQ,GAAG;AAC5B;AAAA,YACE,eAAe,IAAI;AAAA,UACrB;AAAA,QACF;AACA,eAAO;AAAA,MACT,CAAC;AAAA,IACH;AAEA,UAAM,oBAAoB,MAAM,4BAA4B;AAC5D,eAAW,SAAS,mBAAmB;AACrC,UAAI,CAAC,UAAU,SAAS,KAAK,GAAG;AAC9B,kBAAU,KAAK,KAAK;AAAA,MACtB;AAAA,IACF;AAEA,UAAM,aAAa,kBAAkB,WAAW,MAAM;AAEtD,UAAM,SAAyB;AAAA,MAC7B,gBAAgB;AAAA,MAChB,gBAAgB,MAAM;AAAA,MACtB,iBAAiB,MAAM;AAAA,MACvB,kBAAkB,MAAM;AAAA,MACxB,kBAAkB,MAAM;AAAA,MACxB,YAAY,MAAM;AAAA,MAClB,aAAa,MAAM;AAAA,MACnB,WAAW;AAAA,MACX;AAAA,IACF;AAEA,eAAW,MAAM;AACjB,SAAK;AAAA,EACP,GAAG,CAAC,OAAO,QAAQ,YAAY,IAAI,CAAC;AAEpC,QAAM,eAAeA,aAAY,MAAM;AACrC,aAAS;AACT,SAAK;AAAA,EACP,GAAG,CAAC,UAAU,IAAI,CAAC;AAEnB,QAAM,aAAa,MAAM;AACvB,YAAQ,MAAM,MAAM;AAAA,MAClB,KAAK;AACH,eAAO,oBAAC,aAAU,QAAgB,UAAU,cAAc;AAAA,MAE5D,KAAK;AACH,eAAO,oBAAC,aAAW,GAAG,gBAAgB;AAAA,MAExC,KAAK,WAAW;AACd,YAAI,MAAM,cAAc;AACtB,iBACE;AAAA,YAAC;AAAA;AAAA,cACC,YAAY,cAAc,QAAQ,IAAI;AAAA,cACtC,SAAS,MAAM,MAAM,eAAe;AAAA;AAAA,UACtC;AAAA,QAEJ;AACA,eACE;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA;AAAA,YACA,YAAY,MAAM;AAChB,oBAAM,2BAA2B;AACjC,oBAAM,QAAQ,QAAQ;AAAA,YACxB;AAAA,YACA,QAAQ,MAAM;AAAA;AAAA,QAChB;AAAA,MAEJ;AAAA,MAEA,KAAK;AACH,eAAO,oBAAC,cAAW;AAAA,MAErB,KAAK,WAAW;AACd,cAAM,YAAY,aAAa,MAAM,iBAAiB,MAAM;AAC5D,cAAM,kBAAkB,MAAM,mBAAmB;AACjD,eACE;AAAA,UAAC;AAAA;AAAA,YACC,YAAY;AAAA,YACZ;AAAA,YACA,iBAAiB,MAAM;AAAA,YACvB,kBAAkB,MAAM;AAAA,YACxB;AAAA,YACA,YAAY;AAAA,YACZ,YAAY,MAAM,eAAe;AAAA,YACjC,aAAa,MAAM;AAAA,YACnB,QAAQ,MAAM;AAAA;AAAA,QAChB;AAAA,MAEJ;AAAA,MAEA;AACE,eAAO;AAAA,IACX;AAAA,EACF;AAEA,MAAI,oBAAoB,iBAAiB;AACvC,UAAM,QAAQ,mBACV,eAAe,aAAa,kBAAkB,kBAAkB,MAChE,cAAc,cAAc,eAAe,mBAAmB;AAElE,WACE,oBAAC,iBAAc,OAAO,UACpB,8BAAC,OAAI,eAAc,UAAS,SAAS,GACnC,+BAAC,QAAK,OAAO,WAAW,SAAS;AAAA;AAAA,MAAU;AAAA,MAAM;AAAA,OAA8B,GACjF,GACF;AAAA,EAEJ;AAEA,SACE,oBAAC,iBAAc,OAAO,UACpB,8BAAC,gBAAa,SAAkB,kBAAoC,MACjE,qBAAW,GACd,GACF;AAEJ;","names":["useCallback","useCallback"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/components/wizard/wizard-tabs.tsx"],"sourcesContent":["import React from \"react\";\nimport { Box, Text } from \"ink\";\nimport { CLI_COLORS } from \"../../consts.js\";\nimport type { WizardStep } from \"../../stores/wizard-store.js\";\n\ntype WizardTabStep = {\n id: WizardStep;\n label: string;\n number: number;\n};\n\nexport type WizardTabsProps = {\n steps: WizardTabStep[];\n currentStep: WizardStep;\n completedSteps: WizardStep[];\n skippedSteps?: WizardStep[];\n version?: string;\n};\n\nexport const WIZARD_STEPS: WizardTabStep[] = [\n { id: \"stack\", label: \"Stack\", number: 1 },\n { id: \"build\", label: \"Build\", number: 2 },\n { id: \"sources\", label: \"Sources\", number: 3 },\n { id: \"agents\", label: \"Agents\", number: 4 },\n { id: \"confirm\", label: \"Confirm\", number: 5 },\n];\n\ntype FormattedStepLabel = {\n /** The step number indicator, e.g. \"[1]\" */\n prefix: string;\n /** The step label text, e.g. \"Stack\" */\n label: string;\n /** The complete formatted string, e.g. \"[1] Stack\" */\n full: string;\n};\n\n/** Format a wizard step as its tab label parts and full string, e.g. \"[1] Stack\" */\nexport function formatStepLabel(stepId: WizardStep): FormattedStepLabel {\n const step = WIZARD_STEPS.find((s) => s.id === stepId);\n if (!step) return { prefix: \"\", label: stepId, full: stepId };\n const prefix = `[${step.number}]`;\n return { prefix, label: step.label, full: `${prefix} ${step.label}` };\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.number}] ${step.label}`;\n\n switch (state) {\n case \"current\":\n return <Text color={CLI_COLORS.PRIMARY}>{label}</Text>;\n case \"completed\":\n return <Text>{label}</Text>;\n case \"skipped\":\n return <Text dimColor>{label}</Text>;\n case \"pending\":\n default:\n return <Text color={CLI_COLORS.UNFOCUSED}>{label}</Text>;\n }\n};\n\nexport const WizardTabs: React.FC<WizardTabsProps> = ({\n steps,\n currentStep,\n completedSteps,\n skippedSteps = [],\n version,\n}) => {\n return (\n <Box\n flexDirection=\"row\"\n columnGap={2}\n borderRight={false}\n borderLeft={false}\n borderColor=\"blackBright\"\n borderStyle=\"single\"\n paddingRight={1}\n >\n {steps.map((step) => {\n const state = getStepState(step.id, currentStep, completedSteps, skippedSteps);\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 );\n};\n"],"mappings":";;;;;;;;;AAAA;AACA,SAAS,KAAK,YAAY;AAmEb,cAmBT,YAnBS;AAjDN,IAAM,eAAgC;AAAA,EAC3C,EAAE,IAAI,SAAS,OAAO,SAAS,QAAQ,EAAE;AAAA,EACzC,EAAE,IAAI,SAAS,OAAO,SAAS,QAAQ,EAAE;AAAA,EACzC,EAAE,IAAI,WAAW,OAAO,WAAW,QAAQ,EAAE;AAAA,EAC7C,EAAE,IAAI,UAAU,OAAO,UAAU,QAAQ,EAAE;AAAA,EAC3C,EAAE,IAAI,WAAW,OAAO,WAAW,QAAQ,EAAE;AAC/C;AAYO,SAAS,gBAAgB,QAAwC;AACtE,QAAM,OAAO,aAAa,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM;AACrD,MAAI,CAAC,KAAM,QAAO,EAAE,QAAQ,IAAI,OAAO,QAAQ,MAAM,OAAO;AAC5D,QAAM,SAAS,IAAI,KAAK,MAAM;AAC9B,SAAO,EAAE,QAAQ,OAAO,KAAK,OAAO,MAAM,GAAG,MAAM,IAAI,KAAK,KAAK,GAAG;AACtE;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,IAAI,KAAK,MAAM,KAAK,KAAK,KAAK;AAE5C,UAAQ,OAAO;AAAA,IACb,KAAK;AACH,aAAO,oBAAC,QAAK,OAAO,WAAW,SAAU,iBAAM;AAAA,IACjD,KAAK;AACH,aAAO,oBAAC,QAAM,iBAAM;AAAA,IACtB,KAAK;AACH,aAAO,oBAAC,QAAK,UAAQ,MAAE,iBAAM;AAAA,IAC/B,KAAK;AAAA,IACL;AACE,aAAO,oBAAC,QAAK,OAAO,WAAW,WAAY,iBAAM;AAAA,EACrD;AACF;AAEO,IAAM,aAAwC,CAAC;AAAA,EACpD;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe,CAAC;AAAA,EAChB;AACF,MAAM;AACJ,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAc;AAAA,MACd,WAAW;AAAA,MACX,aAAa;AAAA,MACb,YAAY;AAAA,MACZ,aAAY;AAAA,MACZ,aAAY;AAAA,MACZ,cAAc;AAAA,MAEb;AAAA,cAAM,IAAI,CAAC,SAAS;AACnB,gBAAM,QAAQ,aAAa,KAAK,IAAI,aAAa,gBAAgB,YAAY;AAE7E,iBAAO,oBAAC,OAAkB,MAAY,SAArB,KAAK,EAA8B;AAAA,QACtD,CAAC;AAAA,QACD,oBAAC,OAAI,UAAU,GAAG,gBAAe,YAC/B,8BAAC,QAAK,UAAQ,MAAE,cAAI,OAAO,IAAG,GAChC;AAAA;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/components/wizard/step-sources.tsx"],"sourcesContent":["import React, { useState, useCallback } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport { useWizardStore } from \"../../stores/wizard-store.js\";\nimport type {\n BoundSkillCandidate,\n MergedSkillsMatrix,\n SkillAlias,\n SkillId,\n} from \"../../types/index.js\";\nimport { CLI_COLORS, DEFAULT_BRANDING } from \"../../consts.js\";\nimport { useMeasuredHeight } from \"../hooks/use-measured-height.js\";\nimport { SourceGrid } from \"./source-grid.js\";\nimport { ViewTitle } from \"./view-title.js\";\nimport { searchExtraSources } from \"../../lib/loading/multi-source-loader.js\";\nimport { resolveAllSources } from \"../../lib/configuration/index.js\";\n\nexport type StepSourcesProps = {\n matrix: MergedSkillsMatrix;\n projectDir?: string;\n onContinue: () => void;\n onBack: () => void;\n};\n\ntype SourcesView = \"choice\" | \"customize\";\n\nexport const StepSources: React.FC<StepSourcesProps> = ({\n matrix,\n projectDir,\n onContinue,\n onBack,\n}) => {\n const store = useWizardStore();\n const [view, setView] = useState<SourcesView>(\"choice\");\n const [choiceIndex, setChoiceIndex] = useState(0);\n const [isGridSearching, setIsGridSearching] = useState(false);\n const { ref: gridRef, measuredHeight: gridHeight } = useMeasuredHeight();\n\n const handleGridSelect = useCallback(\n (skillId: SkillId, sourceId: string) => {\n store.setSourceSelection(skillId, sourceId);\n },\n [store],\n );\n\n const handleSearch = useCallback(\n async (alias: SkillAlias): Promise<BoundSkillCandidate[]> => {\n if (!projectDir) return [];\n try {\n const sources = await resolveAllSources(projectDir);\n return await searchExtraSources(alias, sources.extras);\n } catch {\n return [];\n }\n },\n [projectDir],\n );\n\n const handleBind = useCallback(\n (candidate: BoundSkillCandidate) => {\n store.bindSkill({\n id: candidate.id,\n sourceUrl: candidate.sourceUrl,\n sourceName: candidate.sourceName,\n boundTo: candidate.alias,\n description: candidate.description,\n });\n },\n [store],\n );\n\n const handleSearchStateChange = useCallback((active: boolean) => {\n setIsGridSearching(active);\n }, []);\n\n useInput((_input, key) => {\n if (view === \"choice\") {\n if (key.return) {\n if (choiceIndex === 0) {\n onContinue();\n } else {\n store.setCustomizeSources(true);\n setView(\"customize\");\n }\n }\n if (key.escape) {\n onBack();\n }\n if (key.upArrow || key.downArrow) {\n setChoiceIndex((prev) => (prev === 0 ? 1 : 0));\n }\n } else if (view === \"customize\") {\n if (isGridSearching) return;\n\n if (key.return) {\n onContinue();\n }\n if (key.escape) {\n store.setCustomizeSources(false);\n setView(\"choice\");\n }\n }\n });\n\n if (view === \"customize\") {\n const rows = store.buildSourceRows(matrix);\n return (\n <Box flexDirection=\"column\" width=\"100%\" flexGrow={1} flexBasis={0}>\n <ViewTitle>Customize skill sources</ViewTitle>\n <Box ref={gridRef} flexGrow={1} flexBasis={0}>\n <SourceGrid\n rows={rows}\n availableHeight={gridHeight}\n onSelect={handleGridSelect}\n onSearch={handleSearch}\n onBind={handleBind}\n onSearchStateChange={handleSearchStateChange}\n />\n </Box>\n </Box>\n );\n }\n\n const selectedTechnologies = store.getAllSelectedTechnologies();\n const rows = store.buildSourceRows(matrix);\n const isRecommendedSelected = choiceIndex === 0;\n const hasLocalSkills = rows.some((row) =>\n row.options.some((o) => o.installed && o.id === \"local\"),\n );\n\n return (\n <Box flexDirection=\"column\" paddingX={2}>\n <Text>\n Your stack includes{\" \"}\n <Text color={CLI_COLORS.PRIMARY} bold>\n {selectedTechnologies.length}\n </Text>{\" \"}\n technologies.\n </Text>\n <Text> </Text>\n\n <Box\n borderStyle=\"round\"\n borderColor={isRecommendedSelected ? CLI_COLORS.SUCCESS : CLI_COLORS.NEUTRAL}\n paddingX={2}\n paddingY={1}\n marginBottom={1}\n >\n <Box flexDirection=\"column\">\n <Text\n color={isRecommendedSelected ? CLI_COLORS.SUCCESS : undefined}\n bold={isRecommendedSelected}\n >\n {isRecommendedSelected ? \">\" : \"\\u25CB\"}{\" \"}\n {hasLocalSkills\n ? \"Use installed skill sources\"\n : \"Use all recommended skills (verified)\"}\n </Text>\n <Text> </Text>\n <Text dimColor>\n {hasLocalSkills\n ? \"Keep your current local and public skill selections.\"\n : \"This is the fastest option. All skills are verified and\"}\n </Text>\n {!hasLocalSkills && <Text dimColor>maintained by {DEFAULT_BRANDING.NAME}</Text>}\n </Box>\n </Box>\n\n <Box\n borderStyle=\"round\"\n borderColor={!isRecommendedSelected ? CLI_COLORS.SUCCESS : CLI_COLORS.NEUTRAL}\n paddingX={2}\n paddingY={1}\n >\n <Box flexDirection=\"column\">\n <Text\n color={!isRecommendedSelected ? CLI_COLORS.SUCCESS : undefined}\n bold={!isRecommendedSelected}\n >\n {!isRecommendedSelected ? \">\" : \"\\u25CB\"} Customize skill sources\n </Text>\n <Text> </Text>\n <Text dimColor>Choose alternative skills for each technology</Text>\n </Box>\n </Box>\n </Box>\n );\n};\n"],"mappings":"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","names":["rows"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/components/wizard/step-confirm.tsx"],"sourcesContent":["import React from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport type { DomainSelections, Domain } from \"../../types/index.js\";\nimport { CLI_COLORS } from \"../../consts.js\";\nimport { getDomainDisplayName } from \"./utils.js\";\n\ntype StepConfirmProps = {\n onComplete: () => void;\n stackName?: string;\n selectedDomains?: Domain[];\n domainSelections?: DomainSelections;\n technologyCount?: number;\n skillCount?: number;\n agentCount?: number;\n installMode?: \"plugin\" | \"local\";\n onBack?: () => void;\n};\n\nexport const StepConfirm: React.FC<StepConfirmProps> = ({\n onComplete,\n stackName,\n selectedDomains,\n domainSelections,\n technologyCount,\n skillCount,\n agentCount,\n installMode,\n onBack,\n}) => {\n useInput((_input, key) => {\n if (key.return) {\n onComplete();\n }\n if (key.escape && onBack) {\n onBack();\n }\n });\n\n const domainsText = selectedDomains?.map(getDomainDisplayName).join(\" + \") || \"\";\n const title = stackName\n ? `Ready to install ${stackName}`\n : `Ready to install your custom stack${domainsText ? ` (${domainsText})` : \"\"}`;\n\n return (\n <Box flexDirection=\"column\" paddingX={2}>\n <Text bold color={CLI_COLORS.SUCCESS}>\n {title}\n </Text>\n <Text> </Text>\n\n {domainSelections && selectedDomains && !stackName && (\n <Box flexDirection=\"column\" marginBottom={1}>\n {selectedDomains.map((domain) => {\n const selections = domainSelections[domain] || {};\n const techs = Object.values(selections).flat();\n if (techs.length === 0) return null;\n return (\n <Text key={domain}>\n <Text bold>{getDomainDisplayName(domain)}:</Text> <Text>{techs.join(\", \")}</Text>\n </Text>\n );\n })}\n </Box>\n )}\n\n <Box flexDirection=\"column\" marginY={1}>\n {technologyCount !== undefined && (\n <Text>\n <Text dimColor>Technologies:</Text> <Text bold>{technologyCount}</Text>\n </Text>\n )}\n {skillCount !== undefined && (\n <Text>\n <Text dimColor>Skills:</Text> <Text bold>{skillCount}</Text>{\" \"}\n <Text color={CLI_COLORS.SUCCESS}>(all verified)</Text>\n </Text>\n )}\n {agentCount !== undefined && (\n <Text>\n <Text dimColor>Agents:</Text> <Text bold>{agentCount}</Text>\n </Text>\n )}\n {installMode && (\n <Text>\n <Text dimColor>Install mode:</Text>{\" \"}\n <Text bold>{installMode === \"plugin\" ? \"Plugin\" : \"Local\"}</Text>\n </Text>\n )}\n </Box>\n\n <Box marginTop={1}>\n <Text dimColor>ENTER install ESC go back</Text>\n </Box>\n </Box>\n );\n};\n"],"mappings":";;;;;;;;;;;;AAAA;AACA,SAAS,KAAK,MAAM,gBAAgB;AA4C9B,cAaU,YAbV;AA3BC,IAAM,cAA0C,CAAC;AAAA,EACtD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAAM;AACJ,WAAS,CAAC,QAAQ,QAAQ;AACxB,QAAI,IAAI,QAAQ;AACd,iBAAW;AAAA,IACb;AACA,QAAI,IAAI,UAAU,QAAQ;AACxB,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AAED,QAAM,cAAc,iBAAiB,IAAI,oBAAoB,EAAE,KAAK,KAAK,KAAK;AAC9E,QAAM,QAAQ,YACV,oBAAoB,SAAS,KAC7B,qCAAqC,cAAc,KAAK,WAAW,MAAM,EAAE;AAE/E,SACE,qBAAC,OAAI,eAAc,UAAS,UAAU,GACpC;AAAA,wBAAC,QAAK,MAAI,MAAC,OAAO,WAAW,SAC1B,iBACH;AAAA,IACA,oBAAC,QAAK,eAAC;AAAA,IAEN,oBAAoB,mBAAmB,CAAC,aACvC,oBAAC,OAAI,eAAc,UAAS,cAAc,GACvC,0BAAgB,IAAI,CAAC,WAAW;AAC/B,YAAM,aAAa,iBAAiB,MAAM,KAAK,CAAC;AAChD,YAAM,QAAQ,OAAO,OAAO,UAAU,EAAE,KAAK;AAC7C,UAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,aACE,qBAAC,QACC;AAAA,6BAAC,QAAK,MAAI,MAAE;AAAA,+BAAqB,MAAM;AAAA,UAAE;AAAA,WAAC;AAAA,QAAO;AAAA,QAAC,oBAAC,QAAM,gBAAM,KAAK,IAAI,GAAE;AAAA,WADjE,MAEX;AAAA,IAEJ,CAAC,GACH;AAAA,IAGF,qBAAC,OAAI,eAAc,UAAS,SAAS,GAClC;AAAA,0BAAoB,UACnB,qBAAC,QACC;AAAA,4BAAC,QAAK,UAAQ,MAAC,2BAAa;AAAA,QAAO;AAAA,QAAC,oBAAC,QAAK,MAAI,MAAE,2BAAgB;AAAA,SAClE;AAAA,MAED,eAAe,UACd,qBAAC,QACC;AAAA,4BAAC,QAAK,UAAQ,MAAC,qBAAO;AAAA,QAAO;AAAA,QAAC,oBAAC,QAAK,MAAI,MAAE,sBAAW;AAAA,QAAQ;AAAA,QAC7D,oBAAC,QAAK,OAAO,WAAW,SAAS,4BAAc;AAAA,SACjD;AAAA,MAED,eAAe,UACd,qBAAC,QACC;AAAA,4BAAC,QAAK,UAAQ,MAAC,qBAAO;AAAA,QAAO;AAAA,QAAC,oBAAC,QAAK,MAAI,MAAE,sBAAW;AAAA,SACvD;AAAA,MAED,eACC,qBAAC,QACC;AAAA,4BAAC,QAAK,UAAQ,MAAC,2BAAa;AAAA,QAAQ;AAAA,QACpC,oBAAC,QAAK,MAAI,MAAE,0BAAgB,WAAW,WAAW,SAAQ;AAAA,SAC5D;AAAA,OAEJ;AAAA,IAEA,oBAAC,OAAI,WAAW,GACd,8BAAC,QAAK,UAAQ,MAAC,uCAAyB,GAC1C;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/components/wizard/step-build.tsx","../src/cli/lib/wizard/index.ts","../src/cli/components/hooks/use-framework-filtering.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 } from \"../../consts.js\";\nimport { useFrameworkFiltering } from \"../hooks/use-framework-filtering.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 showLabels: boolean;\n expertMode: boolean;\n /** Skill IDs already installed on disk, shown with a dimmed checkmark */\n installedSkillIds?: SkillId[];\n onToggle: (subcategoryId: Subcategory, technologyId: SkillId) => void;\n onToggleLabels: () => 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\nexport const StepBuild: React.FC<StepBuildProps> = ({\n matrix,\n domain: activeDomain,\n selectedDomains,\n selections,\n allSelections,\n showLabels,\n expertMode,\n installedSkillIds,\n onToggle,\n onToggleLabels,\n onContinue,\n onBack,\n}) => {\n const [validationError, setValidationError] = useState<string | undefined>(undefined);\n const { ref: gridRef, measuredHeight: gridHeight } = useMeasuredHeight();\n\n const categories = useFrameworkFiltering({\n domain: activeDomain,\n allSelections,\n matrix,\n expertMode,\n selections,\n installedSkillIds,\n });\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} flexBasis={0}>\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} bold={isActive}>\n {getDomainDisplayName(domain)}\n </Text>\n );\n })}\n </Box>\n </Box>\n <ViewTitle>{`[2] Customize your ${getDomainDisplayName(activeDomain)} stack`}</ViewTitle>\n\n <Box ref={gridRef} flexGrow={1} flexBasis={0}>\n <CategoryGrid\n key={activeDomain}\n categories={categories}\n availableHeight={gridHeight}\n expertMode={expertMode}\n showLabels={showLabels}\n onToggle={onToggle}\n onToggleLabels={onToggleLabels}\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 { 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 installedSkillIds?: SkillId[];\n};\n\nexport function useFrameworkFiltering({\n domain,\n allSelections,\n matrix,\n expertMode,\n selections,\n installedSkillIds,\n}: UseFrameworkFilteringOptions): CategoryRow[] {\n return useMemo(\n () =>\n buildCategoriesForDomain(\n domain,\n allSelections,\n matrix,\n expertMode,\n selections,\n installedSkillIds,\n ),\n [domain, allSelections, matrix, expertMode, selections, installedSkillIds],\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA,SAAgB,gBAAgB;AAChC,SAAS,KAAK,MAAM,gBAAgB;;;ACDpC;;;ACAA;AAAA,SAAS,eAAe;AAmBjB,SAAS,sBAAsB;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAgD;AAC9C,SAAO;AAAA,IACL,MACE;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACF,CAAC,QAAQ,eAAe,QAAQ,YAAY,YAAY,iBAAiB;AAAA,EAC3E;AACF;;;AFEQ,SACE,KADF;AAJR,IAAM,SAAgC,CAAC,EAAE,gBAAgB,MAAM;AAC7D,SACE,oBAAC,OAAI,eAAc,UAAS,WAAW,GACpC,6BACC,qBAAC,OAAI,eAAc,UAAS,cAAc,GACxC;AAAA,wBAAC,QAAK,OAAO,WAAW,SAAU,2BAAgB;AAAA,IAClD,oBAAC,QAAK,UAAQ,MAAC,kFAAoE;AAAA,KACrF,GAEJ;AAEJ;AAEO,IAAM,YAAsC,CAAC;AAAA,EAClD;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAAM;AACJ,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,SAA6B,MAAS;AACpF,QAAM,EAAE,KAAK,SAAS,gBAAgB,WAAW,IAAI,kBAAkB;AAEvE,QAAM,aAAa,sBAAsB;AAAA,IACvC,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,WAAS,CAAC,QAAQ,QAAQ;AACxB,QAAI,IAAI,QAAQ;AACd,YAAM,aAAa,kBAAkB,YAAY,UAAU;AAC3D,UAAI,WAAW,OAAO;AACpB,2BAAmB,MAAS;AAC5B,mBAAW;AAAA,MACb,OAAO;AACL,2BAAmB,WAAW,OAAO;AAAA,MACvC;AAAA,IACF,WAAW,IAAI,QAAQ;AACrB,yBAAmB,MAAS;AAC5B,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AAED,SACE,qBAAC,OAAI,eAAc,UAAS,OAAM,QAAO,UAAU,GAAG,WAAW,GAC/D;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAW;AAAA,QACX,eAAc;AAAA,QACd,gBAAe;AAAA,QACf,cAAc;AAAA,QACd,cAAc;AAAA,QACd,WAAW;AAAA,QACX,WAAW;AAAA,QACX,aAAa;AAAA,QACb,YAAY;AAAA,QACZ,aAAa,WAAW;AAAA,QACxB,aAAY;AAAA,QAEZ,8BAAC,OAAI,WAAW,GAAG,eAAc,OAC9B,0BAAgB,IAAI,CAAC,WAAW;AAC/B,gBAAM,WAAW,WAAW;AAC5B,iBACE,oBAAC,QAAkB,OAAO,WAAW,WAAW,UAAU,QAAW,MAAM,UACxE,+BAAqB,MAAM,KADnB,MAEX;AAAA,QAEJ,CAAC,GACH;AAAA;AAAA,IACF;AAAA,IACA,oBAAC,aAAW,gCAAsB,qBAAqB,YAAY,CAAC,UAAS;AAAA,IAE7E,oBAAC,OAAI,KAAK,SAAS,UAAU,GAAG,WAAW,GACzC;AAAA,MAAC;AAAA;AAAA,QAEC;AAAA,QACA,iBAAiB;AAAA,QACjB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,MANK;AAAA,IAOP,GACF;AAAA,IAEA,oBAAC,UAAO,iBAAkC;AAAA,KAC5C;AAEJ;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/components/wizard/checkbox-grid.tsx"],"sourcesContent":["import React, { useState } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport { CLI_COLORS, UI_SYMBOLS } from \"../../consts.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 title: string;\n subtitle: string;\n items: CheckboxItem<T>[];\n selectedIds: T[];\n onToggle: (id: T) => void;\n onContinue: () => void;\n onBack: () => void;\n /** Label for the continue button, e.g. \"Continue with 3 domain(s)\" */\n continueLabel?: (count: number) => string;\n /** Message shown when nothing is selected */\n emptyMessage?: string;\n};\n\nexport const CheckboxGrid = <T extends string = string>({\n title,\n subtitle,\n items,\n selectedIds,\n onToggle,\n onContinue,\n onBack,\n continueLabel = (count) => `Continue with ${count} item(s)`,\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 === \" \") {\n const item = items[focusedIndex];\n if (item) {\n onToggle(item.id);\n }\n }\n });\n\n const continueIndex = items.length;\n\n return (\n <Box flexDirection=\"column\">\n <Text bold>{title}</Text>\n <Text dimColor>{subtitle}</Text>\n <Text> </Text>\n {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.CURRENT : \" \";\n\n return (\n <Text key={item.id}>\n <Text color={isFocused ? CLI_COLORS.PRIMARY : undefined}>{pointer}</Text>\n <Text color={isSelected || isFocused ? CLI_COLORS.PRIMARY : undefined} bold={isFocused}>\n {\" \"}\n {checkbox} {item.label}\n </Text>\n <Text dimColor> - {item.description}</Text>\n </Text>\n );\n })}\n <Text\n color={focusedIndex === continueIndex ? CLI_COLORS.PRIMARY : undefined}\n bold={focusedIndex === continueIndex}\n >\n {focusedIndex === continueIndex ? UI_SYMBOLS.CURRENT : \" \"} {\"\\u2192\"}{\" \"}\n {continueLabel(selectedIds.length)}\n </Text>\n {selectedIds.length > 0 ? (\n <Text>\n {\"\\n\"}Selected: <Text color={CLI_COLORS.PRIMARY}>{selectedIds.join(\", \")}</Text>\n </Text>\n ) : emptyMessage ? (\n <Text dimColor>\n {\"\\n\"}\n {emptyMessage}\n </Text>\n ) : null}\n <Text dimColor>\n {\"\\n\"}\n {\"\\u2191\"}/{\"\\u2193\"} navigate SPACE toggle ENTER continue ESC back\n </Text>\n </Box>\n );\n};\n"],"mappings":";;;;;;;;;;AAAA;AAAA,SAAgB,gBAAgB;AAChC,SAAS,KAAK,MAAM,gBAAgB;AAuE9B,cAYM,YAZN;AAhDC,IAAM,eAAe,CAA4B;AAAA,EACtD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB,CAAC,UAAU,iBAAiB,KAAK;AAAA,EACjD,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,KAAK;AACjB,YAAM,OAAO,MAAM,YAAY;AAC/B,UAAI,MAAM;AACR,iBAAS,KAAK,EAAE;AAAA,MAClB;AAAA,IACF;AAAA,EACF,CAAC;AAED,QAAM,gBAAgB,MAAM;AAE5B,SACE,qBAAC,OAAI,eAAc,UACjB;AAAA,wBAAC,QAAK,MAAI,MAAE,iBAAM;AAAA,IAClB,oBAAC,QAAK,UAAQ,MAAE,oBAAS;AAAA,IACzB,oBAAC,QAAK,eAAC;AAAA,IACN,MAAM,IAAI,CAAC,MAAM,UAAU;AAC1B,YAAM,YAAY,UAAU;AAC5B,YAAM,aAAa,YAAY,SAAS,KAAK,EAAE;AAC/C,YAAM,WAAW,aAAa,aAAa;AAC3C,YAAM,UAAU,YAAY,WAAW,UAAU;AAEjD,aACE,qBAAC,QACC;AAAA,4BAAC,QAAK,OAAO,YAAY,WAAW,UAAU,QAAY,mBAAQ;AAAA,QAClE,qBAAC,QAAK,OAAO,cAAc,YAAY,WAAW,UAAU,QAAW,MAAM,WAC1E;AAAA;AAAA,UACA;AAAA,UAAS;AAAA,UAAE,KAAK;AAAA,WACnB;AAAA,QACA,qBAAC,QAAK,UAAQ,MAAC;AAAA;AAAA,UAAI,KAAK;AAAA,WAAY;AAAA,WAN3B,KAAK,EAOhB;AAAA,IAEJ,CAAC;AAAA,IACD;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,iBAAiB,gBAAgB,WAAW,UAAU;AAAA,QAC7D,MAAM,iBAAiB;AAAA,QAEtB;AAAA,2BAAiB,gBAAgB,WAAW,UAAU;AAAA,UAAI;AAAA,UAAE;AAAA,UAAU;AAAA,UACtE,cAAc,YAAY,MAAM;AAAA;AAAA;AAAA,IACnC;AAAA,IACC,YAAY,SAAS,IACpB,qBAAC,QACE;AAAA;AAAA,MAAK;AAAA,MAAU,oBAAC,QAAK,OAAO,WAAW,SAAU,sBAAY,KAAK,IAAI,GAAE;AAAA,OAC3E,IACE,eACF,qBAAC,QAAK,UAAQ,MACX;AAAA;AAAA,MACA;AAAA,OACH,IACE;AAAA,IACJ,qBAAC,QAAK,UAAQ,MACX;AAAA;AAAA,MACA;AAAA,MAAS;AAAA,MAAE;AAAA,MAAS;AAAA,OACvB;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/lib/__tests__/test-fixtures.ts","../src/cli/lib/__tests__/helpers.ts"],"sourcesContent":["import type { CategoryPath, ResolvedSkill, SkillDisplayName, SkillId } from \"../../types\";\nimport { createMockSkill } from \"./helpers\";\n\ninterface SkillFixtureConfig {\n id: SkillId;\n category: CategoryPath;\n displayName?: SkillDisplayName;\n description: string;\n tags: string[];\n}\n\nconst SKILL_FIXTURES: Record<string, SkillFixtureConfig> = {\n react: {\n id: \"web-framework-react\",\n category: \"web-framework\",\n displayName: \"react\",\n description: \"React framework for building user interfaces\",\n tags: [\"react\", \"web\", \"ui\", \"component\"],\n },\n zustand: {\n id: \"web-state-zustand\",\n category: \"web-client-state\",\n displayName: \"zustand\",\n description: \"Bear necessities state management\",\n tags: [\"state\", \"react\", \"zustand\"],\n },\n hono: {\n id: \"api-framework-hono\",\n category: \"api-api\",\n displayName: \"hono\",\n description: \"Lightweight web framework for the edge\",\n tags: [\"api\", \"api\", \"edge\", \"serverless\"],\n },\n vitest: {\n id: \"web-testing-vitest\",\n category: \"web-testing\",\n displayName: \"vitest\",\n description: \"Next generation testing framework\",\n tags: [\"testing\", \"vitest\", \"unit\"],\n },\n vue: {\n id: \"web-framework-vue\",\n category: \"web-framework\",\n displayName: \"vue\",\n description: \"Progressive JavaScript framework\",\n tags: [\"vue\", \"web\", \"reactive\"],\n },\n \"auth-patterns\": {\n id: \"api-security-auth-patterns\",\n category: \"api-security\",\n description: \"Authentication and authorization patterns\",\n tags: [\"auth\", \"security\", \"jwt\", \"oauth\"],\n },\n drizzle: {\n id: \"api-database-drizzle\",\n category: \"api-database\",\n displayName: \"drizzle\",\n description: \"TypeScript ORM for SQL databases\",\n tags: [\"database\", \"orm\", \"sql\"],\n },\n methodology: {\n id: \"meta-methodology-anti-over-engineering\",\n category: \"shared-methodology\",\n description: \"Surgical implementation, not architectural innovation\",\n tags: [\"methodology\", \"foundational\"],\n },\n \"scss-modules\": {\n id: \"web-styling-scss-modules\",\n category: \"web-styling\",\n displayName: \"scss-modules\",\n description: \"CSS Modules with SCSS\",\n tags: [\"css\", \"scss\", \"modules\"],\n },\n} as const;\n\nexport type TestSkillName = keyof typeof SKILL_FIXTURES;\n\nexport function getTestSkill(\n name: TestSkillName,\n overrides?: Partial<ResolvedSkill>,\n): ResolvedSkill {\n const config = SKILL_FIXTURES[name];\n const { id, category, ...defaults } = config;\n return createMockSkill(id, category, { ...defaults, ...overrides });\n}\n","import path from \"path\";\nimport os from \"os\";\nimport { fileURLToPath } from \"url\";\nimport { mkdtemp, rm, mkdir, writeFile, readFile, stat } from \"fs/promises\";\nimport { parse as parseYaml, stringify as stringifyYaml } from \"yaml\";\nimport { run, Errors } from \"@oclif/core\";\nimport ansis from \"ansis\";\nimport { DEFAULT_BRANDING, DEFAULT_PLUGIN_NAME, STANDARD_FILES } from \"../../consts\";\nimport { typedEntries } from \"../../utils/typed-object\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nexport const CLI_ROOT = path.resolve(__dirname, \"../../../..\");\n\nexport const OUTPUT_STRINGS = {\n CONFIG_HEADER: `${DEFAULT_BRANDING.NAME} Configuration`,\n CONFIG_PATHS_HEADER: \"Configuration File Paths\",\n CONFIG_LAYERS_HEADER: \"Configuration Layers:\",\n CONFIG_PRECEDENCE: \"Precedence: flag > env > project > global > default\",\n SOURCE_LABEL: \"Source:\",\n MARKETPLACE_LABEL: \"Marketplace:\",\n AGENTS_SOURCE_LABEL: \"Agents Source:\",\n GLOBAL_LABEL: \"Global:\",\n PROJECT_LABEL: \"Project:\",\n\n // Setup/Init outputs\n INIT_HEADER: `${DEFAULT_BRANDING.NAME} Setup`,\n INIT_SUCCESS: `${DEFAULT_BRANDING.NAME} initialized successfully!`,\n LOADING_MATRIX: \"Loading skills matrix...\",\n LOADING_SKILLS: \"Loading skills...\",\n LOADING_AGENTS: \"Loading agent partials...\",\n\n // Plugin/installation outputs\n NO_PLUGIN_FOUND: \"No plugin found\",\n NO_INSTALLATION_FOUND: \"No installation found\",\n NO_PLUGIN_INSTALLATION: \"No plugin installation found\",\n NOT_INSTALLED: `${DEFAULT_BRANDING.NAME} is not installed`,\n UNINSTALL_HEADER: `${DEFAULT_BRANDING.NAME} Uninstall`,\n UNINSTALL_COMPLETE: `${DEFAULT_BRANDING.NAME} has been uninstalled`,\n EJECT_HEADER: `${DEFAULT_BRANDING.NAME} Eject`,\n\n // Doctor command outputs\n DOCTOR_HEADER: `${DEFAULT_BRANDING.NAME} Doctor`,\n\n // Error message patterns (lowercase for case-insensitive matching)\n ERROR_MISSING_ARG: \"missing required arg\",\n ERROR_UNEXPECTED_ARG: \"unexpected argument\",\n ERROR_UNKNOWN_FLAG: \"unknown flag\",\n ERROR_PARSE: \"parse\",\n} as const;\n\n/**\n * Run a CLI command and capture its output.\n *\n * Bun's `console.log` does not go through `process.stdout.write`, so\n * `@oclif/test`'s `runCommand` (which only intercepts `process.stdout.write`)\n * returns empty stdout/stderr in bun. This helper intercepts both layers\n * to work correctly in both Node.js and bun environments.\n */\nexport async function runCliCommand(args: string[]) {\n const origStdoutWrite = process.stdout.write;\n const origStderrWrite = process.stderr.write;\n const origLog = console.log;\n const origWarn = console.warn;\n const origError = console.error;\n\n const stdoutBuf: string[] = [];\n const stderrBuf: string[] = [];\n\n // Intercept process.stdout/stderr.write (Node.js path)\n process.stdout.write = function (str: unknown, encoding?: unknown, cb?: unknown): boolean {\n stdoutBuf.push(String(str));\n if (typeof encoding === \"function\") {\n (encoding as () => void)();\n } else if (typeof cb === \"function\") {\n (cb as () => void)();\n }\n return true;\n } as typeof process.stdout.write;\n\n process.stderr.write = function (str: unknown, encoding?: unknown, cb?: unknown): boolean {\n stderrBuf.push(String(str));\n if (typeof encoding === \"function\") {\n (encoding as () => void)();\n } else if (typeof cb === \"function\") {\n (cb as () => void)();\n }\n return true;\n } as typeof process.stderr.write;\n\n // Intercept console methods (bun path — console.log bypasses process.stdout.write)\n console.log = (...logArgs: unknown[]) => {\n stdoutBuf.push(logArgs.map(String).join(\" \") + \"\\n\");\n };\n console.warn = (...warnArgs: unknown[]) => {\n stderrBuf.push(warnArgs.map(String).join(\" \") + \"\\n\");\n };\n console.error = (...errArgs: unknown[]) => {\n stderrBuf.push(errArgs.map(String).join(\" \") + \"\\n\");\n };\n\n let error: (Error & Partial<Errors.CLIError>) | undefined;\n try {\n await run(args, { root: CLI_ROOT });\n } catch (e) {\n if (e instanceof Error) {\n error = Object.assign(e, { message: ansis.strip(e.message) }) as Error &\n Partial<Errors.CLIError>;\n }\n } finally {\n process.stdout.write = origStdoutWrite;\n process.stderr.write = origStderrWrite;\n console.log = origLog;\n console.warn = origWarn;\n console.error = origError;\n }\n\n return {\n stdout: stdoutBuf.map((s) => ansis.strip(s)).join(\"\"),\n stderr: stderrBuf.map((s) => ansis.strip(s)).join(\"\"),\n error,\n };\n}\nimport type {\n AgentConfig,\n AgentDefinition,\n CategoryDefinition,\n CategoryPath,\n CompileContext,\n Domain,\n DomainSelections,\n ExtractedSkillMetadata,\n MergedSkillsMatrix,\n ResolvedSkill,\n ResolvedStack,\n Skill,\n SkillDefinition,\n SkillDisplayName,\n SkillId,\n Stack,\n StackAgentConfig,\n Subcategory,\n} from \"../../types\";\nimport type { WizardResultV2 } from \"../../components/wizard/wizard\";\nimport type { SourceLoadResult } from \"../loading/source-loader\";\nimport type { ResolvedConfig } from \"../configuration/config\";\nimport { useWizardStore } from \"../../stores/wizard-store\";\nimport { resolveAlias, validateSelection } from \"../matrix\";\nimport type { TestProjectConfig } from \"./fixtures/create-test-source\";\nimport { getTestSkill } from \"./test-fixtures\";\n\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n const s = await stat(filePath);\n return s.isFile();\n } catch {\n return false;\n }\n}\n\nexport async function directoryExists(dirPath: string): Promise<boolean> {\n try {\n const s = await stat(dirPath);\n return s.isDirectory();\n } catch {\n return false;\n }\n}\n\nexport async function readTestYaml<T>(filePath: string): Promise<T> {\n const content = await readFile(filePath, \"utf-8\");\n // Boundary cast: YAML parse returns `unknown`, caller provides expected type\n return parseYaml(content) as T;\n}\n\nexport function buildWizardResult(\n selectedSkills: SkillId[],\n overrides?: Partial<WizardResultV2>,\n): WizardResultV2 {\n return {\n selectedSkills,\n selectedAgents: [],\n selectedStackId: null,\n domainSelections: {} as DomainSelections,\n sourceSelections: {},\n expertMode: false,\n installMode: \"local\",\n cancelled: false,\n validation: { valid: true, errors: [], warnings: [] },\n ...overrides,\n };\n}\n\nexport function buildSourceResult(\n matrix: MergedSkillsMatrix,\n sourcePath: string,\n overrides?: Partial<SourceLoadResult>,\n): SourceLoadResult {\n const sourceConfig: ResolvedConfig = {\n source: sourcePath,\n sourceOrigin: \"flag\",\n };\n return {\n matrix,\n sourceConfig,\n sourcePath,\n isLocal: true,\n ...overrides,\n };\n}\n\n/**\n * Lightweight frontmatter parser for test assertions.\n * Returns raw key-value pairs (unlike the production parseFrontmatter which\n * returns typed SkillFrontmatter with Zod validation).\n */\nexport function parseTestFrontmatter(content: string): Record<string, unknown> | null {\n if (!content.startsWith(\"---\")) {\n return null;\n }\n\n const endIndex = content.indexOf(\"---\", 3);\n if (endIndex === -1) {\n return null;\n }\n\n const yamlContent = content.slice(3, endIndex).trim();\n try {\n // Boundary cast: YAML parse returns `unknown`\n return parseYaml(yamlContent) as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\nexport async function createTempDir(prefix = \"cc-test-\"): Promise<string> {\n return mkdtemp(path.join(os.tmpdir(), prefix));\n}\n\nconst CLEANUP_MAX_RETRIES = 3;\nconst CLEANUP_RETRY_DELAY_MS = 100;\n\nexport async function cleanupTempDir(dirPath: string): Promise<void> {\n for (let attempt = 0; attempt < CLEANUP_MAX_RETRIES; attempt++) {\n try {\n await rm(dirPath, { recursive: true, force: true });\n return;\n } catch (error: unknown) {\n const isRetryable =\n error instanceof Error &&\n \"code\" in error &&\n (error as NodeJS.ErrnoException).code === \"ENOTEMPTY\";\n if (!isRetryable || attempt === CLEANUP_MAX_RETRIES - 1) {\n throw error;\n }\n // Transient ENOTEMPTY on macOS: kernel hasn't released directory entries yet\n await new Promise((resolve) => setTimeout(resolve, CLEANUP_RETRY_DELAY_MS));\n }\n }\n}\n\nexport interface TestDirs {\n tempDir: string;\n projectDir: string;\n pluginDir: string;\n skillsDir: string;\n agentsDir: string;\n}\n\nexport async function createTestDirs(prefix = \"cc-test-\"): Promise<TestDirs> {\n const tempDir = await createTempDir(prefix);\n const projectDir = path.join(tempDir, \"project\");\n const pluginDir = path.join(projectDir, \".claude\", \"plugins\", DEFAULT_PLUGIN_NAME);\n const skillsDir = path.join(pluginDir, \"skills\");\n const agentsDir = path.join(pluginDir, \"agents\");\n\n await mkdir(skillsDir, { recursive: true });\n await mkdir(agentsDir, { recursive: true });\n\n return { tempDir, projectDir, pluginDir, skillsDir, agentsDir };\n}\n\nexport async function cleanupTestDirs(dirs: TestDirs): Promise<void> {\n await cleanupTempDir(dirs.tempDir);\n}\n\nexport function createMockSkill(\n id: SkillId,\n category: CategoryPath,\n overrides?: Partial<ResolvedSkill>,\n): ResolvedSkill {\n return {\n id,\n description: `${id} skill`,\n category,\n categoryExclusive: false,\n tags: [],\n author: \"@test\",\n conflictsWith: [],\n recommends: [],\n requires: [],\n alternatives: [],\n discourages: [],\n compatibleWith: [],\n requiresSetup: [],\n providesSetupFor: [],\n path: `skills/${category}/${id}/`,\n ...overrides,\n };\n}\n\n/**\n * Creates a mock ExtractedSkillMetadata for testing.\n * Used when mocking extractAllSkills() return values.\n */\nexport function createMockExtractedSkill(\n id: SkillId,\n overrides?: Partial<ExtractedSkillMetadata>,\n): ExtractedSkillMetadata {\n // Derive directory path and category from the skill ID convention: \"domain-subcategory-name\"\n const segments = id.split(\"-\");\n const domain = segments[0] ?? \"web\";\n const subcategory = segments[1] ?? \"framework\";\n const name = segments.slice(2).join(\"-\") || \"skill\";\n const directoryPath = `${domain}/${subcategory}/${name}`;\n\n return {\n id,\n directoryPath,\n description: `${id} skill`,\n category: `${domain}-${subcategory}` as CategoryPath,\n categoryExclusive: true,\n author: \"@test\",\n tags: [],\n compatibleWith: [],\n conflictsWith: [],\n requires: [],\n requiresSetup: [],\n providesSetupFor: [],\n path: `skills/${directoryPath}/`,\n ...overrides,\n };\n}\n\nexport function createMockMatrix(\n skills: Record<string, ResolvedSkill>,\n overrides?: Partial<MergedSkillsMatrix>,\n): MergedSkillsMatrix {\n return {\n version: \"1.0.0\",\n categories: {} as Record<Subcategory, import(\"../../types\").CategoryDefinition>,\n skills,\n suggestedStacks: [],\n displayNameToId: {} as Record<SkillDisplayName, SkillId>,\n displayNames: {} as Record<SkillId, SkillDisplayName>,\n generatedAt: new Date().toISOString(),\n ...overrides,\n };\n}\n\nexport function createMockAgent(\n name: string,\n overrides?: Partial<AgentDefinition>,\n): AgentDefinition {\n return {\n title: name,\n description: `${name} agent`,\n tools: [\"Read\", \"Write\", \"Edit\", \"Grep\", \"Glob\", \"Bash\"],\n model: \"opus\",\n permissionMode: \"default\",\n ...overrides,\n };\n}\n\nexport function createMockAgentConfig(\n name: string,\n skills: Skill[] = [],\n overrides?: Partial<AgentConfig>,\n): AgentConfig {\n return {\n name,\n title: `${name} agent`,\n description: `Test ${name}`,\n tools: [\"Read\", \"Write\"],\n skills,\n path: name,\n ...overrides,\n };\n}\n\nexport function createMockSkillEntry(\n id: SkillId,\n preloaded = false,\n overrides?: Partial<Skill>,\n): Skill {\n return {\n id,\n path: `skills/${id}/`,\n description: `${id} skill`,\n usage: `when working with ${id}`,\n preloaded,\n ...overrides,\n };\n}\n\nexport function createCompileContext(overrides?: Partial<CompileContext>): CompileContext {\n return {\n stackId: \"test-stack\",\n verbose: false,\n projectRoot: \"/project\",\n outputDir: `/project/.claude/plugins/${DEFAULT_PLUGIN_NAME}`,\n ...overrides,\n };\n}\n\nexport function createSkillContent(name: string, description = \"A test skill\"): string {\n return `---\nname: ${name}\ndescription: ${description}\ncategory: test\n---\n\n# ${name}\n\nThis is a test skill.\n`;\n}\n\nfunction createMetadataContent(author = \"@test\"): string {\n return `version: 1\nauthor: ${author}\n`;\n}\n\nexport function createAgentYamlContent(name: string, description = `Test ${name} agent`): string {\n return `id: ${name}\ntitle: ${name} Agent\ndescription: ${description}\ntools:\n - Read\n - Write`;\n}\n\nexport async function writeTestSkill(\n skillsDir: string,\n skillName: string,\n options?: {\n author?: string;\n description?: string;\n /** Extra fields to merge into metadata.yaml (e.g., forkedFrom, cliName) */\n extraMetadata?: Record<string, unknown>;\n /** Skip metadata.yaml creation entirely */\n skipMetadata?: boolean;\n /** Custom SKILL.md content (overrides default generated content) */\n skillContent?: string;\n },\n): Promise<string> {\n const skillDir = path.join(skillsDir, skillName);\n await mkdir(skillDir, { recursive: true });\n\n await writeFile(\n path.join(skillDir, STANDARD_FILES.SKILL_MD),\n options?.skillContent ?? createSkillContent(skillName, options?.description),\n );\n\n if (!options?.skipMetadata) {\n if (options?.extraMetadata) {\n const metadata = {\n version: 1,\n author: options?.author ?? \"@test\",\n ...options.extraMetadata,\n };\n await writeFile(path.join(skillDir, STANDARD_FILES.METADATA_YAML), stringifyYaml(metadata));\n } else {\n await writeFile(\n path.join(skillDir, STANDARD_FILES.METADATA_YAML),\n createMetadataContent(options?.author),\n );\n }\n }\n\n return skillDir;\n}\n\n/**\n * Creates a source-level skill directory with SKILL.md and rich metadata.yaml.\n * Use this when testing `extractAllSkills()` and `mergeMatrixWithSkills()`.\n *\n * Unlike `writeTestSkill()` which creates installed skills, this writes skills\n * in the source directory layout (under `src/skills/<domain>/<subcategory>/<name>/`).\n */\nexport async function writeSourceSkill(\n skillsDir: string,\n directoryPath: string,\n config: {\n id: string;\n description: string;\n category: string;\n author?: string;\n tags?: string[];\n categoryExclusive?: boolean;\n content?: string;\n },\n): Promise<string> {\n const skillDir = path.join(skillsDir, directoryPath);\n await mkdir(skillDir, { recursive: true });\n\n await writeFile(\n path.join(skillDir, STANDARD_FILES.SKILL_MD),\n createSkillContent(config.id, config.description),\n );\n\n const metadata: Record<string, unknown> = {\n cliName: config.id,\n category: config.category,\n author: config.author ?? \"@test\",\n version: \"1\",\n };\n if (config.tags) {\n metadata.tags = config.tags;\n }\n if (config.categoryExclusive !== undefined) {\n metadata.categoryExclusive = config.categoryExclusive;\n }\n\n await writeFile(path.join(skillDir, STANDARD_FILES.METADATA_YAML), stringifyYaml(metadata));\n\n return skillDir;\n}\n\nexport async function writeTestAgent(\n agentsDir: string,\n agentName: string,\n options?: { description?: string },\n): Promise<string> {\n const agentDir = path.join(agentsDir, agentName);\n await mkdir(agentDir, { recursive: true });\n\n await writeFile(\n path.join(agentDir, STANDARD_FILES.AGENT_YAML),\n createAgentYamlContent(agentName, options?.description),\n );\n\n return agentDir;\n}\n\nexport function createMockCategory(\n id: Subcategory,\n displayName: string,\n overrides?: Partial<CategoryDefinition>,\n): CategoryDefinition {\n return {\n id,\n displayName,\n description: `${displayName} category`,\n exclusive: true,\n required: false,\n order: 0,\n ...overrides,\n };\n}\n\nexport function createMockResolvedStack(\n id: string,\n name: string,\n overrides?: Partial<ResolvedStack>,\n): ResolvedStack {\n return {\n id,\n name,\n description: `${name} stack`,\n audience: [],\n skills: {},\n allSkillIds: [],\n philosophy: \"\",\n ...overrides,\n };\n}\n\n/**\n * Builds a comprehensive test matrix with 13 skills across 7 categories,\n * 2 suggested stacks, display name mappings, and relationship data\n * (conflicts, recommends). Includes all 6 DEFAULT_PRESELECTED_SKILLS\n * (methodology) so wizard handleComplete can resolve them.\n * @returns A fully populated MergedSkillsMatrix with realistic test data\n */\nexport function createComprehensiveMatrix(\n overrides?: Partial<MergedSkillsMatrix>,\n): MergedSkillsMatrix {\n // Skill categories use domain-prefixed Subcategory IDs (matching production\n // metadata.yaml and the categories map keys, e.g., \"web-framework\", \"api-api\").\n const skills = {\n \"web-framework-react\": getTestSkill(\"react\", { category: \"web-framework\" }),\n \"web-framework-vue\": getTestSkill(\"vue\", {\n category: \"web-framework\",\n conflictsWith: [{ skillId: \"web-framework-react\", reason: \"Choose one framework\" }],\n }),\n \"web-state-zustand\": getTestSkill(\"zustand\", {\n category: \"web-client-state\",\n recommends: [{ skillId: \"web-framework-react\", reason: \"Works great with React\" }],\n }),\n \"web-styling-scss-modules\": getTestSkill(\"scss-modules\", { category: \"web-styling\" }),\n \"api-framework-hono\": getTestSkill(\"hono\", { category: \"api-api\" }),\n \"api-database-drizzle\": getTestSkill(\"drizzle\", { category: \"api-database\" }),\n \"web-testing-vitest\": getTestSkill(\"vitest\", { category: \"web-testing\" }),\n // Methodology skills (DEFAULT_PRESELECTED_SKILLS) — auto-injected by wizard\n \"meta-methodology-investigation-requirements\": createMockSkill(\n \"meta-methodology-investigation-requirements\",\n \"shared-methodology\",\n { description: \"Never speculate - read actual code first\", categoryExclusive: false },\n ),\n \"meta-methodology-anti-over-engineering\": createMockSkill(\n \"meta-methodology-anti-over-engineering\",\n \"shared-methodology\",\n {\n description: \"Surgical implementation, not architectural innovation\",\n categoryExclusive: false,\n },\n ),\n \"meta-methodology-success-criteria\": createMockSkill(\n \"meta-methodology-success-criteria\",\n \"shared-methodology\",\n { description: \"Explicit, measurable criteria defining done\", categoryExclusive: false },\n ),\n \"meta-methodology-write-verification\": createMockSkill(\n \"meta-methodology-write-verification\",\n \"shared-methodology\",\n { description: \"Verify work was actually saved\", categoryExclusive: false },\n ),\n \"meta-methodology-improvement-protocol\": createMockSkill(\n \"meta-methodology-improvement-protocol\",\n \"shared-methodology\",\n { description: \"Evidence-based self-improvement\", categoryExclusive: false },\n ),\n \"meta-methodology-context-management\": createMockSkill(\n \"meta-methodology-context-management\",\n \"shared-methodology\",\n { description: \"Maintain project continuity across sessions\", categoryExclusive: false },\n ),\n };\n\n const categories = {\n \"web-framework\": createMockCategory(\"web-framework\" as Subcategory, \"Framework\", {\n domain: \"web\" as Domain,\n exclusive: true,\n required: true,\n }),\n \"web-client-state\": createMockCategory(\"web-client-state\" as Subcategory, \"State\", {\n domain: \"web\" as Domain,\n order: 1,\n }),\n \"web-styling\": createMockCategory(\"web-styling\" as Subcategory, \"Styling\", {\n domain: \"web\" as Domain,\n order: 2,\n }),\n \"api-api\": createMockCategory(\"api-api\" as Subcategory, \"Backend Framework\", {\n domain: \"api\" as Domain,\n exclusive: true,\n required: true,\n }),\n \"api-database\": createMockCategory(\"api-database\" as Subcategory, \"Database\", {\n domain: \"api\" as Domain,\n order: 1,\n }),\n \"web-testing\": createMockCategory(\"web-testing\" as Subcategory, \"Testing\", {\n domain: \"shared\" as Domain,\n exclusive: false,\n order: 10,\n }),\n \"shared-methodology\": createMockCategory(\"shared-methodology\" as Subcategory, \"Methodology\", {\n domain: \"shared\" as Domain,\n exclusive: false,\n required: false,\n order: 11,\n }),\n } as Record<Subcategory, CategoryDefinition>;\n\n const suggestedStacks: ResolvedStack[] = [\n createMockResolvedStack(\"nextjs-fullstack\", \"Next.js Fullstack\", {\n description: \"Complete Next.js stack with React and Hono\",\n audience: [\"startups\", \"enterprise\"],\n skills: {\n \"web-developer\": {\n \"web-framework\": \"web-framework-react\",\n \"web-client-state\": \"web-state-zustand\",\n \"web-styling\": \"web-styling-scss-modules\",\n },\n \"api-developer\": {\n \"api-api\": \"api-framework-hono\",\n \"api-database\": \"api-database-drizzle\",\n },\n } as ResolvedStack[\"skills\"],\n allSkillIds: [\n \"web-framework-react\",\n \"web-state-zustand\",\n \"web-styling-scss-modules\",\n \"api-framework-hono\",\n \"api-database-drizzle\",\n ],\n philosophy: \"Modern, type-safe fullstack development\",\n }),\n createMockResolvedStack(\"vue-stack\", \"Vue Stack\", {\n description: \"Vue.js frontend stack\",\n audience: [\"startups\"],\n skills: {\n \"web-developer\": {\n \"web-framework\": \"web-framework-vue\",\n },\n } as ResolvedStack[\"skills\"],\n allSkillIds: [\"web-framework-vue\"],\n philosophy: \"Progressive framework approach\",\n }),\n ];\n\n const displayNameToId = {\n react: \"web-framework-react\",\n vue: \"web-framework-vue\",\n zustand: \"web-state-zustand\",\n \"scss-modules\": \"web-styling-scss-modules\",\n hono: \"api-framework-hono\",\n drizzle: \"api-database-drizzle\",\n vitest: \"web-testing-vitest\",\n \"investigation-requirements\": \"meta-methodology-investigation-requirements\",\n \"anti-over-engineering\": \"meta-methodology-anti-over-engineering\",\n \"success-criteria\": \"meta-methodology-success-criteria\",\n \"write-verification\": \"meta-methodology-write-verification\",\n \"improvement-protocol\": \"meta-methodology-improvement-protocol\",\n \"context-management\": \"meta-methodology-context-management\",\n // Double cast needed: object literal's string keys are not assignable to branded\n // SkillDisplayName/SkillId types without going through `unknown` first (boundary cast)\n } as unknown as Record<SkillDisplayName, SkillId>;\n\n const displayNames = {} as Record<SkillId, SkillDisplayName>;\n for (const [displayName, fullId] of typedEntries(displayNameToId)) {\n (displayNames as Record<string, string>)[fullId] = displayName;\n }\n\n return createMockMatrix(skills, {\n categories,\n suggestedStacks,\n displayNameToId,\n displayNames,\n ...overrides,\n });\n}\n\n/**\n * Builds a lightweight test matrix with 4 skills, 4 categories, and 2 stacks.\n * Use instead of createComprehensiveMatrix when relationship data is not needed.\n * @returns A minimal MergedSkillsMatrix for basic integration tests\n */\nexport function createBasicMatrix(overrides?: Partial<MergedSkillsMatrix>): MergedSkillsMatrix {\n // Domain-prefixed Subcategory IDs — see createComprehensiveMatrix comment\n const skills = {\n \"web-framework-react\": getTestSkill(\"react\", { category: \"web-framework\" }),\n \"web-state-zustand\": getTestSkill(\"zustand\", { category: \"web-client-state\" }),\n \"api-framework-hono\": getTestSkill(\"hono\", { category: \"api-api\" }),\n \"web-testing-vitest\": getTestSkill(\"vitest\", { category: \"web-testing\" }),\n };\n\n const suggestedStacks: ResolvedStack[] = [\n createMockResolvedStack(\"react-fullstack\", \"React Fullstack\", {\n allSkillIds: [\"web-framework-react\", \"web-state-zustand\", \"api-framework-hono\"],\n }),\n createMockResolvedStack(\"testing-stack\", \"Testing Stack\", {\n allSkillIds: [\"web-testing-vitest\"],\n }),\n ];\n\n return createMockMatrix(skills, {\n suggestedStacks,\n categories: {\n \"web-framework\": createMockCategory(\"web-framework\" as Subcategory, \"Framework\", {\n domain: \"web\" as Domain,\n exclusive: true,\n required: true,\n }),\n \"web-client-state\": createMockCategory(\"web-client-state\" as Subcategory, \"State\", {\n domain: \"web\" as Domain,\n order: 1,\n }),\n \"api-api\": createMockCategory(\"api-api\" as Subcategory, \"Backend Framework\", {\n domain: \"api\" as Domain,\n exclusive: true,\n required: true,\n }),\n \"web-testing\": createMockCategory(\"web-testing\" as Subcategory, \"Testing Framework\", {\n domain: \"shared\" as Domain,\n exclusive: false,\n }),\n } as Record<Subcategory, CategoryDefinition>,\n ...overrides,\n });\n}\n\n/**\n * Replicates `handleComplete` from wizard.tsx for the \"customize\" path.\n *\n * Given the wizard store state (after simulated user selections), this\n * builds the same WizardResultV2 that the real wizard produces:\n * 1. Collects all selected technologies from domainSelections\n * 2. Resolves aliases to canonical skill IDs\n * 3. Adds methodology skills (DEFAULT_PRESELECTED_SKILLS)\n * 4. Runs validation\n */\nexport function buildWizardResultFromStore(\n matrix: MergedSkillsMatrix,\n overrides?: Partial<WizardResultV2>,\n): WizardResultV2 {\n const store = useWizardStore.getState();\n\n let allSkills: SkillId[];\n\n if (store.selectedStackId && store.stackAction === \"defaults\") {\n const stack = matrix.suggestedStacks.find((s) => s.id === store.selectedStackId);\n allSkills = [...(stack?.allSkillIds || [])];\n } else {\n const techNames = store.getAllSelectedTechnologies();\n allSkills = techNames.map((tech) => resolveAlias(tech, matrix));\n }\n\n const methodologySkills = store.getDefaultMethodologySkills();\n for (const skill of methodologySkills) {\n if (!allSkills.includes(skill)) {\n allSkills.push(skill);\n }\n }\n\n const validation = validateSelection(allSkills, matrix);\n\n return {\n selectedSkills: allSkills,\n selectedAgents: store.selectedAgents,\n selectedStackId: store.selectedStackId,\n domainSelections: store.domainSelections,\n sourceSelections: store.sourceSelections,\n expertMode: store.expertMode,\n installMode: store.installMode,\n cancelled: false,\n validation,\n ...overrides,\n };\n}\n\n/**\n * Simulates a user selecting specific skills via the wizard store.\n *\n * Sets up domainSelections as if the user toggled each skill in the build step,\n * using the matrix to look up the correct domain and subcategory per skill.\n */\nexport function simulateSkillSelections(\n skillIds: SkillId[],\n matrix: MergedSkillsMatrix,\n selectedDomains: string[],\n): void {\n const domainSelections: DomainSelections = {};\n\n for (const skillId of skillIds) {\n const skill = matrix.skills[skillId];\n if (!skill) continue;\n\n // Boundary cast: skill.category is a Subcategory at runtime\n const subcategory = skill.category as Subcategory;\n const categoryDef = matrix.categories[subcategory];\n const domain = categoryDef?.domain;\n if (!domain) continue;\n\n if (!domainSelections[domain]) {\n domainSelections[domain] = {};\n }\n if (!domainSelections[domain][subcategory]) {\n domainSelections[domain][subcategory] = [];\n }\n if (!domainSelections[domain][subcategory].includes(skillId)) {\n domainSelections[domain][subcategory].push(skillId);\n }\n }\n\n useWizardStore.setState({\n domainSelections,\n selectedDomains: selectedDomains as Domain[],\n approach: \"scratch\",\n step: \"confirm\",\n });\n}\n\n/**\n * Extracts skill IDs from a stack assignment value, which may be:\n * - A bare string (e.g., \"web-framework-react\")\n * - An object with .id (e.g., { id: \"web-framework-react\", preloaded: true })\n * - An array of strings or objects\n */\nexport function extractSkillIdsFromAssignment(assignment: unknown): string[] {\n if (typeof assignment === \"string\") {\n return [assignment];\n }\n if (Array.isArray(assignment)) {\n return assignment.flatMap((item) => extractSkillIdsFromAssignment(item));\n }\n if (typeof assignment === \"object\" && assignment !== null && \"id\" in assignment) {\n return [String((assignment as { id: string }).id)];\n }\n return [];\n}\n\nexport function buildTestProjectConfig(\n agents: string[],\n skills: Array<string | { id: string }>,\n overrides?: Partial<TestProjectConfig>,\n): TestProjectConfig {\n return {\n name: \"test-project\",\n description: \"Test project\",\n agents,\n skills,\n ...overrides,\n };\n}\n\nexport function createMockSkillDefinition(\n id: SkillId,\n overrides?: Partial<SkillDefinition>,\n): SkillDefinition {\n return {\n id,\n path: `skills/${id}/`,\n description: `${id} skill`,\n ...overrides,\n };\n}\n\nexport function createMockStack(\n id: string,\n config: {\n name: string;\n description?: string;\n agents: Record<string, StackAgentConfig>;\n philosophy?: string;\n },\n): Stack {\n return {\n id,\n name: config.name,\n description: config.description ?? \"\",\n // Boundary cast: test callers may pass arbitrary agent names (e.g., \"nonexistent-agent\")\n agents: config.agents as Stack[\"agents\"],\n philosophy: config.philosophy,\n };\n}\n\nexport { getTestSkill } from \"./test-fixtures\";\nexport type { TestSkillName } from \"./test-fixtures\";\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/components/wizard/stack-selection.tsx"],"sourcesContent":["import React, { useState } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport { DEFAULT_SCRATCH_DOMAINS } from \"../../consts.js\";\nimport { useWizardStore } from \"../../stores/wizard-store.js\";\nimport type { MergedSkillsMatrix } from \"../../types/index.js\";\nimport { getDomainsFromStack } from \"./utils.js\";\nimport { MenuItem } from \"./menu-item.js\";\nimport { ViewTitle } from \"./view-title.js\";\n\nconst INITIAL_FOCUSED_INDEX = 0;\nconst SCRATCH_LABEL = \"Start from scratch\";\nconst SCRATCH_DESCRIPTION = \"Select domains and skills manually\";\n\n/** Number of extra items after the stack list (scratch option) */\nconst EXTRA_ITEMS_COUNT = 1;\n\n/** Width of the horizontal divider between stack list and scratch option */\nconst DIVIDER_WIDTH = 30;\n\nexport type StackSelectionProps = {\n matrix: MergedSkillsMatrix;\n onCancel?: () => void;\n};\n\nexport const StackSelection: React.FC<StackSelectionProps> = ({ matrix, onCancel }) => {\n const { selectStack, setApproach, setStackAction, populateFromSkillIds, toggleDomain } =\n useWizardStore();\n const [focusedIndex, setFocusedIndex] = useState(INITIAL_FOCUSED_INDEX);\n\n const stacks = matrix.suggestedStacks;\n const stackCount = stacks.length;\n const scratchIndex = stackCount;\n const totalItems = stackCount + EXTRA_ITEMS_COUNT;\n\n useInput((input, key) => {\n if (key.escape) {\n if (onCancel) {\n onCancel();\n }\n return;\n }\n\n if (key.return) {\n if (focusedIndex === scratchIndex) {\n selectStack(null);\n setApproach(\"scratch\");\n\n for (const domain of DEFAULT_SCRATCH_DOMAINS) {\n toggleDomain(domain);\n }\n return;\n }\n\n const focusedStack = stacks[focusedIndex];\n if (focusedStack) {\n selectStack(focusedStack.id);\n setStackAction(\"customize\");\n populateFromSkillIds(focusedStack.allSkillIds, matrix.skills, matrix.categories);\n\n const stackDomains = getDomainsFromStack(focusedStack, matrix.categories);\n for (const domain of stackDomains) {\n toggleDomain(domain);\n }\n\n setApproach(\"stack\");\n }\n return;\n }\n\n if (key.upArrow || input === \"k\") {\n setFocusedIndex((prev) => Math.max(0, prev - 1));\n return;\n }\n if (key.downArrow || input === \"j\") {\n setFocusedIndex((prev) => Math.min(totalItems - 1, prev + 1));\n }\n });\n\n return (\n <Box flexDirection=\"column\">\n <ViewTitle>[1] Choose a stack</ViewTitle>\n <Box flexDirection=\"column\" marginTop={1}>\n {stacks.map((stack, index) => (\n <MenuItem\n key={stack.id}\n label={stack.name}\n description={stack.description}\n isFocused={index === focusedIndex}\n />\n ))}\n {stackCount > 0 && (\n <Box marginTop={1}>\n <Text dimColor>{\"\\u2500\".repeat(DIVIDER_WIDTH)}</Text>\n </Box>\n )}\n <Box marginTop={stackCount > 0 ? 0 : undefined}>\n <MenuItem\n label={SCRATCH_LABEL}\n description={SCRATCH_DESCRIPTION}\n isFocused={focusedIndex === scratchIndex}\n />\n </Box>\n </Box>\n <Box marginTop={1}>\n <Text dimColor>\n {\"\\u2191\"}/{\"\\u2193\"} navigate ENTER select ESC quit\n </Text>\n </Box>\n </Box>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA,SAAgB,gBAAgB;AAChC,SAAS,KAAK,MAAM,gBAAgB;AA+E9B,cACA,YADA;AAvEN,IAAM,wBAAwB;AAC9B,IAAM,gBAAgB;AACtB,IAAM,sBAAsB;AAG5B,IAAM,oBAAoB;AAG1B,IAAM,gBAAgB;AAOf,IAAM,iBAAgD,CAAC,EAAE,QAAQ,SAAS,MAAM;AACrF,QAAM,EAAE,aAAa,aAAa,gBAAgB,sBAAsB,aAAa,IACnF,eAAe;AACjB,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,qBAAqB;AAEtE,QAAM,SAAS,OAAO;AACtB,QAAM,aAAa,OAAO;AAC1B,QAAM,eAAe;AACrB,QAAM,aAAa,aAAa;AAEhC,WAAS,CAAC,OAAO,QAAQ;AACvB,QAAI,IAAI,QAAQ;AACd,UAAI,UAAU;AACZ,iBAAS;AAAA,MACX;AACA;AAAA,IACF;AAEA,QAAI,IAAI,QAAQ;AACd,UAAI,iBAAiB,cAAc;AACjC,oBAAY,IAAI;AAChB,oBAAY,SAAS;AAErB,mBAAW,UAAU,yBAAyB;AAC5C,uBAAa,MAAM;AAAA,QACrB;AACA;AAAA,MACF;AAEA,YAAM,eAAe,OAAO,YAAY;AACxC,UAAI,cAAc;AAChB,oBAAY,aAAa,EAAE;AAC3B,uBAAe,WAAW;AAC1B,6BAAqB,aAAa,aAAa,OAAO,QAAQ,OAAO,UAAU;AAE/E,cAAM,eAAe,oBAAoB,cAAc,OAAO,UAAU;AACxE,mBAAW,UAAU,cAAc;AACjC,uBAAa,MAAM;AAAA,QACrB;AAEA,oBAAY,OAAO;AAAA,MACrB;AACA;AAAA,IACF;AAEA,QAAI,IAAI,WAAW,UAAU,KAAK;AAChC,sBAAgB,CAAC,SAAS,KAAK,IAAI,GAAG,OAAO,CAAC,CAAC;AAC/C;AAAA,IACF;AACA,QAAI,IAAI,aAAa,UAAU,KAAK;AAClC,sBAAgB,CAAC,SAAS,KAAK,IAAI,aAAa,GAAG,OAAO,CAAC,CAAC;AAAA,IAC9D;AAAA,EACF,CAAC;AAED,SACE,qBAAC,OAAI,eAAc,UACjB;AAAA,wBAAC,aAAU,gCAAkB;AAAA,IAC7B,qBAAC,OAAI,eAAc,UAAS,WAAW,GACpC;AAAA,aAAO,IAAI,CAAC,OAAO,UAClB;AAAA,QAAC;AAAA;AAAA,UAEC,OAAO,MAAM;AAAA,UACb,aAAa,MAAM;AAAA,UACnB,WAAW,UAAU;AAAA;AAAA,QAHhB,MAAM;AAAA,MAIb,CACD;AAAA,MACA,aAAa,KACZ,oBAAC,OAAI,WAAW,GACd,8BAAC,QAAK,UAAQ,MAAE,mBAAS,OAAO,aAAa,GAAE,GACjD;AAAA,MAEF,oBAAC,OAAI,WAAW,aAAa,IAAI,IAAI,QACnC;AAAA,QAAC;AAAA;AAAA,UACC,OAAO;AAAA,UACP,aAAa;AAAA,UACb,WAAW,iBAAiB;AAAA;AAAA,MAC9B,GACF;AAAA,OACF;AAAA,IACA,oBAAC,OAAI,WAAW,GACd,+BAAC,QAAK,UAAQ,MACX;AAAA;AAAA,MAAS;AAAA,MAAE;AAAA,MAAS;AAAA,OACvB,GACF;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/components/wizard/category-grid.tsx","../src/cli/components/hooks/use-category-grid-input.ts"],"sourcesContent":["import React, { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\n\nimport { Box, type DOMElement, Text, measureElement } from \"ink\";\n\nimport { CLI_COLORS, SCROLL_VIEWPORT } from \"../../consts.js\";\nimport type { SkillId, Subcategory } from \"../../types/index.js\";\nimport { isSectionLocked, useCategoryGridInput } 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 /** Available height in terminal lines for the scrollable viewport. 0 = no constraint. */\n availableHeight?: number;\n showLabels: boolean;\n expertMode: boolean;\n onToggle: (categoryId: Subcategory, technologyId: SkillId) => void;\n onToggleLabels: () => 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};\n\nconst SYMBOL_REQUIRED = \"*\";\n\n/**\n * Priority order for skill states in the initial sort.\n * Lower numbers appear first. Selected skills are sorted above all states.\n */\nconst STATE_PRIORITY: Record<OptionState, number> = {\n recommended: 0,\n normal: 1,\n discouraged: 2,\n disabled: 3,\n};\n\n/**\n * Sort options by: selected first, then by state priority.\n * Within each group, original matrix order is preserved (stable sort).\n */\nconst stableSortByState = (options: CategoryOption[]): CategoryOption[] => {\n return [...options].sort((a, b) => {\n if (a.selected !== b.selected) return a.selected ? -1 : 1;\n return STATE_PRIORITY[a.state] - STATE_PRIORITY[b.state];\n });\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 + 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 return index;\n};\n\ntype SkillTagProps = {\n option: CategoryOption;\n isFocused: boolean;\n isLocked: boolean;\n showLabels: boolean;\n};\n\nconst getCompatibilityLabel = (option: CategoryOption, isLocked: boolean): string | null => {\n if (option.selected) return \"(selected)\";\n if (isLocked || option.state === \"disabled\") return \"(disabled)\";\n if (option.state === \"recommended\") return \"(recommended)\";\n if (option.state === \"discouraged\") return \"(discouraged)\";\n return null;\n};\n\nconst SkillTag: React.FC<SkillTagProps> = ({ option, isFocused, isLocked, showLabels }) => {\n const getTextColor = (): string => {\n if (isLocked || option.state === \"disabled\") return CLI_COLORS.NEUTRAL;\n if (option.selected) return CLI_COLORS.PRIMARY;\n if (option.state === \"recommended\") return CLI_COLORS.UNFOCUSED;\n if (option.state === \"discouraged\") return CLI_COLORS.WARNING;\n // Normal unselected: muted color to clearly contrast with selected (cyan) skills\n return CLI_COLORS.NEUTRAL;\n };\n\n const getStateBorderColor = (): string => {\n if (isLocked || option.state === \"disabled\") return CLI_COLORS.NEUTRAL;\n if (option.selected) return CLI_COLORS.PRIMARY;\n if (option.state === \"recommended\") return CLI_COLORS.UNFOCUSED;\n if (option.state === \"discouraged\") return CLI_COLORS.WARNING;\n return CLI_COLORS.UNFOCUSED;\n };\n\n const isBold = isFocused || option.selected;\n const textColor = getTextColor();\n const compatibilityLabel = showLabels ? getCompatibilityLabel(option, isLocked) : null;\n\n return (\n <Box\n marginRight={1}\n borderColor={isFocused ? getStateBorderColor() : CLI_COLORS.NEUTRAL}\n borderStyle=\"single\"\n borderDimColor={!isFocused}\n flexShrink={0}\n >\n <>\n <Text color={textColor} bold={isBold} dimColor={false}>\n {\" \"}\n {option.label}{\" \"}\n </Text>\n {compatibilityLabel && <Text dimColor>{compatibilityLabel} </Text>}\n </>\n </Box>\n );\n};\n\ntype CategorySectionProps = {\n category: CategoryRow;\n options: CategoryOption[];\n isLocked: boolean;\n isFocused: boolean;\n focusedOptionIndex: number;\n showLabels: boolean;\n};\n\nconst CategorySection: React.FC<CategorySectionProps> = ({\n category,\n options,\n isLocked,\n isFocused,\n focusedOptionIndex,\n showLabels,\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 <SkillTag\n key={option.id}\n option={option}\n isFocused={isFocused && index === focusedOptionIndex && !isLocked}\n isLocked={isLocked}\n showLabels={showLabels}\n />\n ))}\n </Box>\n </Box>\n );\n};\n\ntype ProcessedCategory = CategoryRow & { sortedOptions: CategoryOption[] };\n\nexport const CategoryGrid: React.FC<CategoryGridProps> = ({\n categories,\n availableHeight = 0,\n showLabels,\n expertMode,\n onToggle,\n onToggleLabels,\n defaultFocusedRow = 0,\n defaultFocusedCol = 0,\n onFocusChange,\n}) => {\n // Cache the initial sort order per category so toggling selections does not reorder skills.\n // The ref resets when the component remounts (e.g., domain change via key={activeDomain}).\n const initialOrderRef = useRef<Map<string, SkillId[]>>(new Map());\n\n const processedCategories = useMemo(\n () =>\n categories.map((category) => {\n const cached = initialOrderRef.current.get(category.id);\n if (cached) {\n const orderMap = new Map(cached.map((id, idx) => [id, idx]));\n const sorted = [...category.options].sort((a, b) => {\n const aIdx = orderMap.get(a.id) ?? Infinity;\n const bIdx = orderMap.get(b.id) ?? Infinity;\n return aIdx - bIdx;\n });\n return { ...category, sortedOptions: sorted };\n }\n const sorted = stableSortByState(category.options);\n initialOrderRef.current.set(\n category.id,\n sorted.map((o) => o.id),\n );\n return { ...category, sortedOptions: sorted };\n }),\n [categories],\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 { focusedRow, focusedCol, setFocused, moveFocus } = useFocusedListItem(\n processedCategories.length,\n getColCount,\n {\n wrap: true,\n isRowLocked,\n findValidCol,\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 onToggleLabels,\n });\n\n const sectionRefs = useRef<(DOMElement | null)[]>([]);\n const [sectionHeights, setSectionHeights] = useState<number[]>([]);\n const [scrollTopPx, setScrollTopPx] = useState(0);\n\n const setSectionRef = useCallback((index: number, el: DOMElement | null) => {\n sectionRefs.current[index] = el;\n }, []);\n\n useEffect(() => {\n const heights = sectionRefs.current.map((el) => {\n if (el) {\n const { height } = measureElement(el);\n return height;\n }\n return 0;\n });\n setSectionHeights((prev) => {\n if (prev.length === heights.length && prev.every((h, i) => h === heights[i])) {\n return prev;\n }\n return heights;\n });\n });\n\n const scrollEnabled = availableHeight > 0 && availableHeight >= SCROLL_VIEWPORT.MIN_VIEWPORT_ROWS;\n\n useEffect(() => {\n if (!scrollEnabled || sectionHeights.length === 0) return;\n\n let topOfFocused = 0;\n for (let i = 0; i < focusedRow; i++) {\n topOfFocused += sectionHeights[i] ?? 0;\n }\n const focusedHeight = sectionHeights[focusedRow] ?? 0;\n const bottomOfFocused = topOfFocused + focusedHeight;\n\n setScrollTopPx((prev) => {\n if (topOfFocused < prev) {\n return topOfFocused;\n }\n if (bottomOfFocused > prev + availableHeight) {\n return bottomOfFocused - availableHeight;\n }\n return prev;\n });\n }, [focusedRow, sectionHeights, scrollEnabled, availableHeight]);\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 const sectionElements = processedCategories.map((category, index) => {\n const isLocked = isSectionLocked(category.id, categories);\n\n return (\n <Box key={category.id} ref={(el) => setSectionRef(index, el)} flexShrink={0}>\n <CategorySection\n category={category}\n options={category.sortedOptions}\n isLocked={isLocked}\n isFocused={index === focusedRow}\n focusedOptionIndex={focusedCol}\n showLabels={showLabels}\n />\n </Box>\n );\n });\n\n // When no height constraint, render flat (tests, or before first measurement)\n if (!scrollEnabled) {\n return (\n <Box flexDirection=\"column\" flexGrow={1} overflow=\"hidden\">\n {sectionElements}\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" height={availableHeight} overflow=\"hidden\">\n <Box flexDirection=\"column\" marginTop={scrollTopPx > 0 ? -scrollTopPx : 0} flexShrink={0}>\n {sectionElements}\n </Box>\n </Box>\n );\n};\n","import { useCallback, useEffect, useRef } 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 = \"web-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 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 onToggleLabels: () => void;\n};\n\nexport function useCategoryGridInput({\n processedCategories,\n categories,\n focusedRow,\n focusedCol,\n setFocused,\n moveFocus,\n onToggle,\n onToggleLabels,\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 }\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 // Store the latest handler in a ref so that the useInput effect never needs to\n // re-register on the event emitter. This avoids a stale-closure race condition\n // where, after a domain switch (CategoryGrid remount via key={activeDomain}),\n // the useInput effect may not yet have re-registered the updated handler when\n // the first keypress arrives — causing the first space press to be silently lost.\n type InputKey = {\n leftArrow: boolean;\n rightArrow: boolean;\n upArrow: boolean;\n downArrow: boolean;\n tab: boolean;\n shift: boolean;\n };\n\n const handlerRef = useRef<((input: string, key: InputKey) => void) | null>(null);\n handlerRef.current = (input: string, key: InputKey) => {\n if (key.tab && key.shift) {\n onToggleLabels();\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 onToggleLabels();\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 // Stable handler reference — never changes, so useInput's effect registers once\n const stableHandler = useCallback((input: string, key: InputKey) => {\n handlerRef.current?.(input, key);\n }, []);\n\n useInput(stableHandler);\n}\n"],"mappings":"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","names":["useCallback","useEffect","useRef","useRef","sorted","useCallback","useEffect"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/components/wizard/domain-selection.tsx"],"sourcesContent":["import React from \"react\";\nimport { useWizardStore } from \"../../stores/wizard-store.js\";\nimport type { Domain } from \"../../types/index.js\";\nimport { CheckboxGrid, type CheckboxItem } from \"./checkbox-grid.js\";\n\nconst AVAILABLE_DOMAINS: CheckboxItem<Domain>[] = [\n { id: \"web\", label: \"Web\", description: \"Frontend web applications\" },\n { id: \"api\", label: \"API\", description: \"Backend APIs and services\" },\n { id: \"cli\", label: \"CLI\", description: \"Command-line tools\" },\n { id: \"mobile\", label: \"Mobile\", description: \"Mobile applications\" },\n];\n\nexport const DomainSelection: React.FC = () => {\n const { selectedDomains, toggleDomain, setStep, setApproach, selectStack } = useWizardStore();\n\n const handleBack = () => {\n setApproach(null);\n selectStack(null);\n };\n\n return (\n <CheckboxGrid\n title=\"Select domains to configure:\"\n subtitle=\"Select one or more domains, then continue\"\n items={AVAILABLE_DOMAINS}\n selectedIds={selectedDomains}\n onToggle={toggleDomain}\n onContinue={() => setStep(\"build\")}\n onBack={handleBack}\n continueLabel={(count) => `Continue with ${count} domain(s)`}\n emptyMessage=\"Please select at least one domain\"\n />\n );\n};\n"],"mappings":";;;;;;;;;;;;AAAA;AAqBI;AAhBJ,IAAM,oBAA4C;AAAA,EAChD,EAAE,IAAI,OAAO,OAAO,OAAO,aAAa,4BAA4B;AAAA,EACpE,EAAE,IAAI,OAAO,OAAO,OAAO,aAAa,4BAA4B;AAAA,EACpE,EAAE,IAAI,OAAO,OAAO,OAAO,aAAa,qBAAqB;AAAA,EAC7D,EAAE,IAAI,UAAU,OAAO,UAAU,aAAa,sBAAsB;AACtE;AAEO,IAAM,kBAA4B,MAAM;AAC7C,QAAM,EAAE,iBAAiB,cAAc,SAAS,aAAa,YAAY,IAAI,eAAe;AAE5F,QAAM,aAAa,MAAM;AACvB,gBAAY,IAAI;AAChB,gBAAY,IAAI;AAAA,EAClB;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAM;AAAA,MACN,UAAS;AAAA,MACT,OAAO;AAAA,MACP,aAAa;AAAA,MACb,UAAU;AAAA,MACV,YAAY,MAAM,QAAQ,OAAO;AAAA,MACjC,QAAQ;AAAA,MACR,eAAe,CAAC,UAAU,iBAAiB,KAAK;AAAA,MAChD,cAAa;AAAA;AAAA,EACf;AAEJ;","names":[]}
|
package/dist/chunk-KXM7KOPE.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
CLI_COLORS
|
|
4
|
-
} from "./chunk-BK7TANUV.js";
|
|
5
|
-
import {
|
|
6
|
-
init_esm_shims
|
|
7
|
-
} from "./chunk-DHET7RCE.js";
|
|
8
|
-
|
|
9
|
-
// src/cli/components/wizard/view-title.tsx
|
|
10
|
-
init_esm_shims();
|
|
11
|
-
import { Box, Text } from "ink";
|
|
12
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
13
|
-
var TITLE_HORIZONTAL_PADDING = 6;
|
|
14
|
-
var ViewTitle = ({ children }) => {
|
|
15
|
-
const padding = " ".repeat(children.length + TITLE_HORIZONTAL_PADDING);
|
|
16
|
-
const paddingHalf = " ".repeat(TITLE_HORIZONTAL_PADDING / 2);
|
|
17
|
-
return /* @__PURE__ */ jsxs(Box, { marginBottom: 1, flexDirection: "column", children: [
|
|
18
|
-
/* @__PURE__ */ jsx(Text, { backgroundColor: CLI_COLORS.WARNING, children: padding }),
|
|
19
|
-
/* @__PURE__ */ jsxs(Text, { backgroundColor: CLI_COLORS.WARNING, bold: true, color: "#000", children: [
|
|
20
|
-
paddingHalf,
|
|
21
|
-
children,
|
|
22
|
-
paddingHalf
|
|
23
|
-
] }),
|
|
24
|
-
/* @__PURE__ */ jsx(Text, { backgroundColor: CLI_COLORS.WARNING, children: padding })
|
|
25
|
-
] });
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export {
|
|
29
|
-
ViewTitle
|
|
30
|
-
};
|
|
31
|
-
//# sourceMappingURL=chunk-KXM7KOPE.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/components/wizard/view-title.tsx"],"sourcesContent":["import React from \"react\";\nimport { Box, Text } from \"ink\";\nimport { CLI_COLORS } from \"../../consts.js\";\n\nconst TITLE_HORIZONTAL_PADDING = 6;\n\ntype ViewTitleProps = {\n children: string;\n};\n\nexport const ViewTitle: React.FC<ViewTitleProps> = ({ children }) => {\n const padding = \" \".repeat(children.length + TITLE_HORIZONTAL_PADDING);\n const paddingHalf = \" \".repeat(TITLE_HORIZONTAL_PADDING / 2);\n\n return (\n <Box marginBottom={1} flexDirection=\"column\">\n <Text backgroundColor={CLI_COLORS.WARNING}>{padding}</Text>\n <Text backgroundColor={CLI_COLORS.WARNING} bold color=\"#000\">\n {paddingHalf}\n {children}\n {paddingHalf}\n </Text>\n <Text backgroundColor={CLI_COLORS.WARNING}>{padding}</Text>\n </Box>\n );\n};\n"],"mappings":";;;;;;;;;AAAA;AACA,SAAS,KAAK,YAAY;AAepB,cACA,YADA;AAZN,IAAM,2BAA2B;AAM1B,IAAM,YAAsC,CAAC,EAAE,SAAS,MAAM;AACnE,QAAM,UAAU,IAAI,OAAO,SAAS,SAAS,wBAAwB;AACrE,QAAM,cAAc,IAAI,OAAO,2BAA2B,CAAC;AAE3D,SACE,qBAAC,OAAI,cAAc,GAAG,eAAc,UAClC;AAAA,wBAAC,QAAK,iBAAiB,WAAW,SAAU,mBAAQ;AAAA,IACpD,qBAAC,QAAK,iBAAiB,WAAW,SAAS,MAAI,MAAC,OAAM,QACnD;AAAA;AAAA,MACA;AAAA,MACA;AAAA,OACH;AAAA,IACA,oBAAC,QAAK,iBAAiB,WAAW,SAAU,mBAAQ;AAAA,KACtD;AAEJ;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/components/wizard/step-refine.tsx"],"sourcesContent":["import React from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport { CLI_COLORS, DEFAULT_BRANDING } from \"../../consts.js\";\n\nexport type RefineAction = \"all-recommended\" | \"customize\" | null;\n\nexport type StepRefineProps = {\n technologyCount: number;\n refineAction: RefineAction;\n onSelectAction: (action: \"all-recommended\" | \"customize\") => void;\n onContinue: () => void;\n onBack: () => void;\n};\n\nexport const StepRefine: React.FC<StepRefineProps> = ({\n technologyCount,\n refineAction,\n onSelectAction,\n onContinue,\n onBack,\n}) => {\n useInput((_input, key) => {\n if (key.return) {\n onContinue();\n }\n if (key.escape) {\n onBack();\n }\n if (key.upArrow || key.downArrow) {\n onSelectAction(refineAction === \"all-recommended\" ? \"customize\" : \"all-recommended\");\n }\n });\n\n const isRecommendedSelected = refineAction === \"all-recommended\" || refineAction === null;\n\n return (\n <Box flexDirection=\"column\" paddingX={2}>\n <Text>\n Your stack includes{\" \"}\n <Text color={CLI_COLORS.PRIMARY} bold>\n {technologyCount}\n </Text>{\" \"}\n technologies.\n </Text>\n <Text> </Text>\n\n <Box\n borderStyle=\"round\"\n borderColor={isRecommendedSelected ? CLI_COLORS.SUCCESS : CLI_COLORS.NEUTRAL}\n paddingX={2}\n paddingY={1}\n marginBottom={1}\n >\n <Box flexDirection=\"column\">\n <Text\n color={isRecommendedSelected ? CLI_COLORS.SUCCESS : undefined}\n bold={isRecommendedSelected}\n >\n {isRecommendedSelected ? \">\" : \"○\"} Use all recommended skills (verified){\" \"}\n <Text dimColor>{isRecommendedSelected ? \"(Selected)\" : \"(Not selected)\"}</Text>\n </Text>\n <Text> </Text>\n <Text dimColor>This is the fastest option. All skills are verified and</Text>\n <Text dimColor>maintained by {DEFAULT_BRANDING.NAME}</Text>\n </Box>\n </Box>\n\n <Box\n borderStyle=\"round\"\n borderColor={!isRecommendedSelected ? CLI_COLORS.SUCCESS : CLI_COLORS.NEUTRAL}\n paddingX={2}\n paddingY={1}\n >\n <Box flexDirection=\"column\">\n <Text\n color={!isRecommendedSelected ? CLI_COLORS.SUCCESS : undefined}\n bold={!isRecommendedSelected}\n >\n {!isRecommendedSelected ? \">\" : \"○\"} Customize skill sources{\" \"}\n <Text dimColor>{!isRecommendedSelected ? \"(Selected)\" : \"(Not selected)\"}</Text>\n </Text>\n <Text> </Text>\n <Text dimColor>Choose alternative skills for each technology</Text>\n </Box>\n </Box>\n\n <Box marginTop={1}>\n <Text dimColor>\n {\"\\u2191\"}/{\"\\u2193\"} navigate ENTER continue ESC back\n </Text>\n </Box>\n </Box>\n );\n};\n"],"mappings":";;;;;;;;;;AAAA;AACA,SAAS,KAAK,MAAM,gBAAgB;AAoC9B,SAEE,KAFF;AAvBC,IAAM,aAAwC,CAAC;AAAA,EACpD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAAM;AACJ,WAAS,CAAC,QAAQ,QAAQ;AACxB,QAAI,IAAI,QAAQ;AACd,iBAAW;AAAA,IACb;AACA,QAAI,IAAI,QAAQ;AACd,aAAO;AAAA,IACT;AACA,QAAI,IAAI,WAAW,IAAI,WAAW;AAChC,qBAAe,iBAAiB,oBAAoB,cAAc,iBAAiB;AAAA,IACrF;AAAA,EACF,CAAC;AAED,QAAM,wBAAwB,iBAAiB,qBAAqB,iBAAiB;AAErF,SACE,qBAAC,OAAI,eAAc,UAAS,UAAU,GACpC;AAAA,yBAAC,QAAK;AAAA;AAAA,MACgB;AAAA,MACpB,oBAAC,QAAK,OAAO,WAAW,SAAS,MAAI,MAClC,2BACH;AAAA,MAAQ;AAAA,MAAI;AAAA,OAEd;AAAA,IACA,oBAAC,QAAK,eAAC;AAAA,IAEP;AAAA,MAAC;AAAA;AAAA,QACC,aAAY;AAAA,QACZ,aAAa,wBAAwB,WAAW,UAAU,WAAW;AAAA,QACrE,UAAU;AAAA,QACV,UAAU;AAAA,QACV,cAAc;AAAA,QAEd,+BAAC,OAAI,eAAc,UACjB;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,wBAAwB,WAAW,UAAU;AAAA,cACpD,MAAM;AAAA,cAEL;AAAA,wCAAwB,MAAM;AAAA,gBAAI;AAAA,gBAAuC;AAAA,gBAC1E,oBAAC,QAAK,UAAQ,MAAE,kCAAwB,eAAe,kBAAiB;AAAA;AAAA;AAAA,UAC1E;AAAA,UACA,oBAAC,QAAK,eAAC;AAAA,UACP,oBAAC,QAAK,UAAQ,MAAC,qEAAuD;AAAA,UACtE,qBAAC,QAAK,UAAQ,MAAC;AAAA;AAAA,YAAe,iBAAiB;AAAA,aAAK;AAAA,WACtD;AAAA;AAAA,IACF;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,aAAY;AAAA,QACZ,aAAa,CAAC,wBAAwB,WAAW,UAAU,WAAW;AAAA,QACtE,UAAU;AAAA,QACV,UAAU;AAAA,QAEV,+BAAC,OAAI,eAAc,UACjB;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,CAAC,wBAAwB,WAAW,UAAU;AAAA,cACrD,MAAM,CAAC;AAAA,cAEN;AAAA,iBAAC,wBAAwB,MAAM;AAAA,gBAAI;AAAA,gBAAyB;AAAA,gBAC7D,oBAAC,QAAK,UAAQ,MAAE,WAAC,wBAAwB,eAAe,kBAAiB;AAAA;AAAA;AAAA,UAC3E;AAAA,UACA,oBAAC,QAAK,eAAC;AAAA,UACP,oBAAC,QAAK,UAAQ,MAAC,2DAA6C;AAAA,WAC9D;AAAA;AAAA,IACF;AAAA,IAEA,oBAAC,OAAI,WAAW,GACd,+BAAC,QAAK,UAAQ,MACX;AAAA;AAAA,MAAS;AAAA,MAAE;AAAA,MAAS;AAAA,OACvB,GACF;AAAA,KACF;AAEJ;","names":[]}
|