@claude-collective/cli 0.2.0 → 0.8.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 +178 -0
- package/README.md +1 -1
- package/dist/chunk-3HBTELJN.js +114 -0
- package/dist/chunk-3HBTELJN.js.map +1 -0
- package/dist/chunk-3ZCB5K33.js +54 -0
- package/dist/chunk-3ZCB5K33.js.map +1 -0
- package/dist/chunk-66UDJBF6.js +96 -0
- package/dist/chunk-66UDJBF6.js.map +1 -0
- package/dist/chunk-6LS7XO3H.js +31 -0
- package/dist/chunk-6LS7XO3H.js.map +1 -0
- package/dist/chunk-A3J6IAXK.js +57 -0
- package/dist/chunk-A3J6IAXK.js.map +1 -0
- package/dist/chunk-A65SBAAJ.js +69 -0
- package/dist/chunk-A65SBAAJ.js.map +1 -0
- package/dist/chunk-ALEPJ6YN.js +80 -0
- package/dist/chunk-ALEPJ6YN.js.map +1 -0
- package/dist/chunk-C4ZTIYFR.js +84 -0
- package/dist/chunk-C4ZTIYFR.js.map +1 -0
- package/dist/chunk-CIY5UBRB.js +453 -0
- package/dist/chunk-CIY5UBRB.js.map +1 -0
- package/dist/chunk-DHET7RCE.js +50 -0
- package/dist/chunk-DHET7RCE.js.map +1 -0
- package/dist/chunk-DHFFRMF6.js +31 -0
- package/dist/chunk-DHFFRMF6.js.map +1 -0
- package/dist/chunk-DKGL77IY.js +307 -0
- package/dist/chunk-DKGL77IY.js.map +1 -0
- package/dist/chunk-ED73HCW2.js +315 -0
- package/dist/chunk-ED73HCW2.js.map +1 -0
- package/dist/chunk-FNOYEXUE.js +308 -0
- package/dist/chunk-FNOYEXUE.js.map +1 -0
- package/dist/chunk-G2FBJOZG.js +141 -0
- package/dist/chunk-G2FBJOZG.js.map +1 -0
- package/dist/chunk-HNDT5QRB.js +120 -0
- package/dist/chunk-HNDT5QRB.js.map +1 -0
- package/dist/chunk-K7PTOVX4.js +158 -0
- package/dist/chunk-K7PTOVX4.js.map +1 -0
- package/dist/chunk-LQTST4WY.js +91 -0
- package/dist/chunk-LQTST4WY.js.map +1 -0
- package/dist/chunk-LVKRVFYR.js +54 -0
- package/dist/chunk-LVKRVFYR.js.map +1 -0
- package/dist/chunk-M7YCPFIX.js +108 -0
- package/dist/chunk-M7YCPFIX.js.map +1 -0
- package/dist/chunk-MJSFR562.js +57 -0
- package/dist/chunk-MJSFR562.js.map +1 -0
- package/dist/chunk-MMDXNZPF.js +69 -0
- package/dist/chunk-MMDXNZPF.js.map +1 -0
- package/dist/chunk-MYAVQ23U.js +356 -0
- package/dist/chunk-MYAVQ23U.js.map +1 -0
- package/dist/chunk-NGBFJJ7Q.js +124 -0
- package/dist/chunk-NGBFJJ7Q.js.map +1 -0
- package/dist/chunk-OLBOTK3O.js +64 -0
- package/dist/chunk-OLBOTK3O.js.map +1 -0
- package/dist/chunk-PPNTD5LO.js +330 -0
- package/dist/chunk-PPNTD5LO.js.map +1 -0
- package/dist/chunk-Q2LH2DAB.js +392 -0
- package/dist/chunk-Q2LH2DAB.js.map +1 -0
- package/dist/chunk-Q6DR5QUH.js +547 -0
- package/dist/chunk-Q6DR5QUH.js.map +1 -0
- package/dist/chunk-QESUUPOE.js +241 -0
- package/dist/chunk-QESUUPOE.js.map +1 -0
- package/dist/chunk-QGGSLMO3.js +607 -0
- package/dist/chunk-QGGSLMO3.js.map +1 -0
- package/dist/chunk-SEBPPFUW.js +478 -0
- package/dist/chunk-SEBPPFUW.js.map +1 -0
- package/dist/chunk-SYQ7R2JO.js +95 -0
- package/dist/chunk-SYQ7R2JO.js.map +1 -0
- package/dist/chunk-TOPAIL5W.js +22 -0
- package/dist/chunk-TOPAIL5W.js.map +1 -0
- package/dist/chunk-U4VYHKPM.js +110 -0
- package/dist/chunk-U4VYHKPM.js.map +1 -0
- package/dist/chunk-UOWHJ6BE.js +83 -0
- package/dist/chunk-UOWHJ6BE.js.map +1 -0
- package/dist/chunk-XKEG3SCV.js +86 -0
- package/dist/chunk-XKEG3SCV.js.map +1 -0
- package/dist/chunk-XY3XDVMI.js +15599 -0
- package/dist/chunk-XY3XDVMI.js.map +1 -0
- package/dist/chunk-Y3V43XCU.js +76 -0
- package/dist/chunk-Y3V43XCU.js.map +1 -0
- package/dist/chunk-YKXBGCFD.js +129 -0
- package/dist/chunk-YKXBGCFD.js.map +1 -0
- package/dist/cli-v2/defaults/agent-mappings.yaml +185 -0
- package/dist/commands/build/marketplace.js +254 -0
- package/dist/commands/build/marketplace.js.map +1 -0
- package/dist/commands/build/plugins.js +324 -0
- package/dist/commands/build/plugins.js.map +1 -0
- package/dist/commands/build/stack.js +169 -0
- package/dist/commands/build/stack.js.map +1 -0
- package/dist/commands/compile.js +461 -0
- package/dist/commands/compile.js.map +1 -0
- package/dist/commands/config/get.js +60 -0
- package/dist/commands/config/get.js.map +1 -0
- package/dist/commands/config/index.js +22 -0
- package/dist/commands/config/index.js.map +1 -0
- package/dist/commands/config/path.js +35 -0
- package/dist/commands/config/path.js.map +1 -0
- package/dist/commands/config/set-project.js +61 -0
- package/dist/commands/config/set-project.js.map +1 -0
- package/dist/commands/config/set.js +60 -0
- package/dist/commands/config/set.js.map +1 -0
- package/dist/commands/config/show.js +13 -0
- package/dist/commands/config/show.js.map +1 -0
- package/dist/commands/config/unset-project.js +57 -0
- package/dist/commands/config/unset-project.js.map +1 -0
- package/dist/commands/config/unset.js +56 -0
- package/dist/commands/config/unset.js.map +1 -0
- package/dist/commands/diff.js +755 -0
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/doctor.js +413 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/edit.js +254 -0
- package/dist/commands/edit.js.map +1 -0
- package/dist/commands/eject.js +208 -0
- package/dist/commands/eject.js.map +1 -0
- package/dist/commands/info.js +205 -0
- package/dist/commands/info.js.map +1 -0
- package/dist/commands/init.js +915 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/list.js +44 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/new/agent.js +230 -0
- package/dist/commands/new/agent.js.map +1 -0
- package/dist/commands/new/skill.js +204 -0
- package/dist/commands/new/skill.js.map +1 -0
- package/dist/commands/outdated.js +242 -0
- package/dist/commands/outdated.js.map +1 -0
- package/dist/commands/search.js +115 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/test-imports.js +92 -0
- package/dist/commands/test-imports.js.map +1 -0
- package/dist/commands/uninstall.js +309 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/commands/update.js +428 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/validate.js +375 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/commands/version/bump.js +95 -0
- package/dist/commands/version/bump.js.map +1 -0
- package/dist/commands/version/index.js +70 -0
- package/dist/commands/version/index.js.map +1 -0
- package/dist/commands/version/set.js +101 -0
- package/dist/commands/version/set.js.map +1 -0
- package/dist/commands/version/show.js +70 -0
- package/dist/commands/version/show.js.map +1 -0
- package/dist/components/common/confirm.js +9 -0
- package/dist/components/common/confirm.js.map +1 -0
- package/dist/components/common/message.js +24 -0
- package/dist/components/common/message.js.map +1 -0
- package/dist/components/common/spinner.js +14 -0
- package/dist/components/common/spinner.js.map +1 -0
- package/dist/components/wizard/category-grid.js +9 -0
- package/dist/components/wizard/category-grid.js.map +1 -0
- package/dist/components/wizard/category-grid.test.js +728 -0
- package/dist/components/wizard/category-grid.test.js.map +1 -0
- package/dist/components/wizard/section-progress.js +9 -0
- package/dist/components/wizard/section-progress.js.map +1 -0
- package/dist/components/wizard/section-progress.test.js +281 -0
- package/dist/components/wizard/section-progress.test.js.map +1 -0
- package/dist/components/wizard/step-approach.js +11 -0
- package/dist/components/wizard/step-approach.js.map +1 -0
- package/dist/components/wizard/step-build.js +15 -0
- package/dist/components/wizard/step-build.js.map +1 -0
- package/dist/components/wizard/step-build.test.js +729 -0
- package/dist/components/wizard/step-build.test.js.map +1 -0
- package/dist/components/wizard/step-confirm.js +9 -0
- package/dist/components/wizard/step-confirm.js.map +1 -0
- package/dist/components/wizard/step-refine.js +9 -0
- package/dist/components/wizard/step-refine.js.map +1 -0
- package/dist/components/wizard/step-refine.test.js +235 -0
- package/dist/components/wizard/step-refine.test.js.map +1 -0
- package/dist/components/wizard/step-stack-options.js +11 -0
- package/dist/components/wizard/step-stack-options.js.map +1 -0
- package/dist/components/wizard/step-stack.js +11 -0
- package/dist/components/wizard/step-stack.js.map +1 -0
- package/dist/components/wizard/wizard-tabs.js +11 -0
- package/dist/components/wizard/wizard-tabs.js.map +1 -0
- package/dist/components/wizard/wizard.js +20 -0
- package/dist/components/wizard/wizard.js.map +1 -0
- package/dist/hooks/init.js +41 -0
- package/dist/hooks/init.js.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/magic-string.es-RGXYGAW3.js +1316 -0
- package/dist/magic-string.es-RGXYGAW3.js.map +1 -0
- package/dist/stores/wizard-store.js +10 -0
- package/dist/stores/wizard-store.js.map +1 -0
- package/dist/stores/wizard-store.test.js +405 -0
- package/dist/stores/wizard-store.test.js.map +1 -0
- package/package.json +44 -25
- package/dist/cli/index.js +0 -6314
- package/dist/cli/index.js.map +0 -1
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
SectionProgress
|
|
4
|
+
} from "./chunk-LVKRVFYR.js";
|
|
5
|
+
import {
|
|
6
|
+
CategoryGrid
|
|
7
|
+
} from "./chunk-PPNTD5LO.js";
|
|
8
|
+
import {
|
|
9
|
+
init_esm_shims
|
|
10
|
+
} from "./chunk-DHET7RCE.js";
|
|
11
|
+
|
|
12
|
+
// src/cli-v2/components/wizard/step-build.tsx
|
|
13
|
+
init_esm_shims();
|
|
14
|
+
import { useState } from "react";
|
|
15
|
+
import { Box, Text, useInput } from "ink";
|
|
16
|
+
|
|
17
|
+
// src/cli-v2/lib/matrix-resolver.ts
|
|
18
|
+
init_esm_shims();
|
|
19
|
+
function resolveAlias(aliasOrId, matrix) {
|
|
20
|
+
return matrix.aliases[aliasOrId] || aliasOrId;
|
|
21
|
+
}
|
|
22
|
+
function isDisabled(skillId, currentSelections, matrix, options) {
|
|
23
|
+
if (options?.expertMode) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
const fullId = resolveAlias(skillId, matrix);
|
|
27
|
+
const skill = matrix.skills[fullId];
|
|
28
|
+
if (!skill) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
for (const selectedId of currentSelections) {
|
|
32
|
+
const selectedFullId = resolveAlias(selectedId, matrix);
|
|
33
|
+
if (skill.conflictsWith.some((c) => c.skillId === selectedFullId)) {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
const selectedSkill = matrix.skills[selectedFullId];
|
|
37
|
+
if (selectedSkill && selectedSkill.conflictsWith.some((c) => c.skillId === fullId)) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const resolvedSelections = currentSelections.map(
|
|
42
|
+
(s) => resolveAlias(s, matrix)
|
|
43
|
+
);
|
|
44
|
+
for (const requirement of skill.requires) {
|
|
45
|
+
if (requirement.needsAny) {
|
|
46
|
+
const hasAny = requirement.skillIds.some(
|
|
47
|
+
(reqId) => resolvedSelections.includes(reqId)
|
|
48
|
+
);
|
|
49
|
+
if (!hasAny) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
const hasAll = requirement.skillIds.every(
|
|
54
|
+
(reqId) => resolvedSelections.includes(reqId)
|
|
55
|
+
);
|
|
56
|
+
if (!hasAll) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
function getDisableReason(skillId, currentSelections, matrix) {
|
|
64
|
+
const fullId = resolveAlias(skillId, matrix);
|
|
65
|
+
const skill = matrix.skills[fullId];
|
|
66
|
+
if (!skill) {
|
|
67
|
+
return void 0;
|
|
68
|
+
}
|
|
69
|
+
const resolvedSelections = currentSelections.map(
|
|
70
|
+
(s) => resolveAlias(s, matrix)
|
|
71
|
+
);
|
|
72
|
+
for (const selectedId of resolvedSelections) {
|
|
73
|
+
const conflict = skill.conflictsWith.find((c) => c.skillId === selectedId);
|
|
74
|
+
if (conflict) {
|
|
75
|
+
const selectedSkill2 = matrix.skills[selectedId];
|
|
76
|
+
const selectedName = selectedSkill2?.name || selectedId;
|
|
77
|
+
return `${conflict.reason} (conflicts with ${selectedName})`;
|
|
78
|
+
}
|
|
79
|
+
const selectedSkill = matrix.skills[selectedId];
|
|
80
|
+
if (selectedSkill) {
|
|
81
|
+
const reverseConflict = selectedSkill.conflictsWith.find(
|
|
82
|
+
(c) => c.skillId === fullId
|
|
83
|
+
);
|
|
84
|
+
if (reverseConflict) {
|
|
85
|
+
const selectedName = selectedSkill.name;
|
|
86
|
+
return `${reverseConflict.reason} (conflicts with ${selectedName})`;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
for (const requirement of skill.requires) {
|
|
91
|
+
if (requirement.needsAny) {
|
|
92
|
+
const hasAny = requirement.skillIds.some(
|
|
93
|
+
(reqId) => resolvedSelections.includes(reqId)
|
|
94
|
+
);
|
|
95
|
+
if (!hasAny) {
|
|
96
|
+
const requiredNames = requirement.skillIds.map((id) => matrix.skills[id]?.name || id).join(" or ");
|
|
97
|
+
return `${requirement.reason} (requires ${requiredNames})`;
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
const missingIds = requirement.skillIds.filter(
|
|
101
|
+
(reqId) => !resolvedSelections.includes(reqId)
|
|
102
|
+
);
|
|
103
|
+
if (missingIds.length > 0) {
|
|
104
|
+
const missingNames = missingIds.map((id) => matrix.skills[id]?.name || id).join(", ");
|
|
105
|
+
return `${requirement.reason} (requires ${missingNames})`;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return void 0;
|
|
110
|
+
}
|
|
111
|
+
function isDiscouraged(skillId, currentSelections, matrix) {
|
|
112
|
+
const fullId = resolveAlias(skillId, matrix);
|
|
113
|
+
const skill = matrix.skills[fullId];
|
|
114
|
+
if (!skill) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
const resolvedSelections = currentSelections.map(
|
|
118
|
+
(s) => resolveAlias(s, matrix)
|
|
119
|
+
);
|
|
120
|
+
for (const selectedId of resolvedSelections) {
|
|
121
|
+
const selectedSkill = matrix.skills[selectedId];
|
|
122
|
+
if (selectedSkill && selectedSkill.discourages.some((d) => d.skillId === fullId)) {
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
if (skill.discourages.some((d) => d.skillId === selectedId)) {
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
function getDiscourageReason(skillId, currentSelections, matrix) {
|
|
132
|
+
const fullId = resolveAlias(skillId, matrix);
|
|
133
|
+
const skill = matrix.skills[fullId];
|
|
134
|
+
if (!skill) {
|
|
135
|
+
return void 0;
|
|
136
|
+
}
|
|
137
|
+
const resolvedSelections = currentSelections.map(
|
|
138
|
+
(s) => resolveAlias(s, matrix)
|
|
139
|
+
);
|
|
140
|
+
for (const selectedId of resolvedSelections) {
|
|
141
|
+
const selectedSkill = matrix.skills[selectedId];
|
|
142
|
+
if (selectedSkill) {
|
|
143
|
+
const discourage = selectedSkill.discourages.find(
|
|
144
|
+
(d) => d.skillId === fullId
|
|
145
|
+
);
|
|
146
|
+
if (discourage) {
|
|
147
|
+
return discourage.reason;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const reverseDiscourage = skill.discourages.find(
|
|
151
|
+
(d) => d.skillId === selectedId
|
|
152
|
+
);
|
|
153
|
+
if (reverseDiscourage) {
|
|
154
|
+
return reverseDiscourage.reason;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return void 0;
|
|
158
|
+
}
|
|
159
|
+
function isRecommended(skillId, currentSelections, matrix) {
|
|
160
|
+
const fullId = resolveAlias(skillId, matrix);
|
|
161
|
+
const skill = matrix.skills[fullId];
|
|
162
|
+
if (!skill) {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
const resolvedSelections = currentSelections.map(
|
|
166
|
+
(s) => resolveAlias(s, matrix)
|
|
167
|
+
);
|
|
168
|
+
for (const selectedId of resolvedSelections) {
|
|
169
|
+
const selectedSkill = matrix.skills[selectedId];
|
|
170
|
+
if (selectedSkill && selectedSkill.recommends.some((r) => r.skillId === fullId)) {
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
function getRecommendReason(skillId, currentSelections, matrix) {
|
|
177
|
+
const fullId = resolveAlias(skillId, matrix);
|
|
178
|
+
const skill = matrix.skills[fullId];
|
|
179
|
+
if (!skill) {
|
|
180
|
+
return void 0;
|
|
181
|
+
}
|
|
182
|
+
const resolvedSelections = currentSelections.map(
|
|
183
|
+
(s) => resolveAlias(s, matrix)
|
|
184
|
+
);
|
|
185
|
+
for (const selectedId of resolvedSelections) {
|
|
186
|
+
const selectedSkill = matrix.skills[selectedId];
|
|
187
|
+
if (selectedSkill) {
|
|
188
|
+
const recommendation = selectedSkill.recommends.find(
|
|
189
|
+
(r) => r.skillId === fullId
|
|
190
|
+
);
|
|
191
|
+
if (recommendation) {
|
|
192
|
+
return `${recommendation.reason} (recommended by ${selectedSkill.name})`;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return void 0;
|
|
197
|
+
}
|
|
198
|
+
function validateSelection(selections, matrix) {
|
|
199
|
+
const errors = [];
|
|
200
|
+
const warnings = [];
|
|
201
|
+
const resolvedSelections = selections.map((s) => resolveAlias(s, matrix));
|
|
202
|
+
for (let i = 0; i < resolvedSelections.length; i++) {
|
|
203
|
+
const skillA = matrix.skills[resolvedSelections[i]];
|
|
204
|
+
if (!skillA) continue;
|
|
205
|
+
for (let j = i + 1; j < resolvedSelections.length; j++) {
|
|
206
|
+
const skillBId = resolvedSelections[j];
|
|
207
|
+
const conflict = skillA.conflictsWith.find((c) => c.skillId === skillBId);
|
|
208
|
+
if (conflict) {
|
|
209
|
+
errors.push({
|
|
210
|
+
type: "conflict",
|
|
211
|
+
message: `${skillA.name} conflicts with ${matrix.skills[skillBId]?.name || skillBId}: ${conflict.reason}`,
|
|
212
|
+
skills: [skillA.id, skillBId]
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
for (const skillId of resolvedSelections) {
|
|
218
|
+
const skill = matrix.skills[skillId];
|
|
219
|
+
if (!skill) continue;
|
|
220
|
+
for (const requirement of skill.requires) {
|
|
221
|
+
if (requirement.needsAny) {
|
|
222
|
+
const hasAny = requirement.skillIds.some(
|
|
223
|
+
(reqId) => resolvedSelections.includes(reqId)
|
|
224
|
+
);
|
|
225
|
+
if (!hasAny) {
|
|
226
|
+
errors.push({
|
|
227
|
+
type: "missing_requirement",
|
|
228
|
+
message: `${skill.name} requires one of: ${requirement.skillIds.map((id) => matrix.skills[id]?.name || id).join(", ")}`,
|
|
229
|
+
skills: [skillId, ...requirement.skillIds]
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
} else {
|
|
233
|
+
const missingIds = requirement.skillIds.filter(
|
|
234
|
+
(reqId) => !resolvedSelections.includes(reqId)
|
|
235
|
+
);
|
|
236
|
+
if (missingIds.length > 0) {
|
|
237
|
+
errors.push({
|
|
238
|
+
type: "missing_requirement",
|
|
239
|
+
message: `${skill.name} requires: ${missingIds.map((id) => matrix.skills[id]?.name || id).join(", ")}`,
|
|
240
|
+
skills: [skillId, ...missingIds]
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const categorySelections = /* @__PURE__ */ new Map();
|
|
247
|
+
for (const skillId of resolvedSelections) {
|
|
248
|
+
const skill = matrix.skills[skillId];
|
|
249
|
+
if (!skill) continue;
|
|
250
|
+
const existing = categorySelections.get(skill.category) || [];
|
|
251
|
+
existing.push(skillId);
|
|
252
|
+
categorySelections.set(skill.category, existing);
|
|
253
|
+
}
|
|
254
|
+
for (const [categoryId, skillIds] of categorySelections.entries()) {
|
|
255
|
+
if (skillIds.length > 1) {
|
|
256
|
+
const category = matrix.categories[categoryId];
|
|
257
|
+
if (category?.exclusive) {
|
|
258
|
+
errors.push({
|
|
259
|
+
type: "category_exclusive",
|
|
260
|
+
message: `Category "${category.name}" only allows one selection, but multiple selected: ${skillIds.map((id) => matrix.skills[id]?.name || id).join(", ")}`,
|
|
261
|
+
skills: skillIds
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
for (const skillId of resolvedSelections) {
|
|
267
|
+
const skill = matrix.skills[skillId];
|
|
268
|
+
if (!skill) continue;
|
|
269
|
+
for (const recommendation of skill.recommends) {
|
|
270
|
+
if (!resolvedSelections.includes(recommendation.skillId)) {
|
|
271
|
+
const recommendedSkill = matrix.skills[recommendation.skillId];
|
|
272
|
+
if (recommendedSkill) {
|
|
273
|
+
const hasConflict = recommendedSkill.conflictsWith.some(
|
|
274
|
+
(c) => resolvedSelections.includes(c.skillId)
|
|
275
|
+
);
|
|
276
|
+
if (!hasConflict) {
|
|
277
|
+
warnings.push({
|
|
278
|
+
type: "missing_recommendation",
|
|
279
|
+
message: `${skill.name} recommends ${recommendedSkill.name}: ${recommendation.reason}`,
|
|
280
|
+
skills: [skillId, recommendation.skillId]
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
for (const skillId of resolvedSelections) {
|
|
288
|
+
const skill = matrix.skills[skillId];
|
|
289
|
+
if (!skill || skill.providesSetupFor.length === 0) continue;
|
|
290
|
+
const hasUsageSkill = skill.providesSetupFor.some(
|
|
291
|
+
(usageId) => resolvedSelections.includes(usageId)
|
|
292
|
+
);
|
|
293
|
+
if (!hasUsageSkill) {
|
|
294
|
+
warnings.push({
|
|
295
|
+
type: "unused_setup",
|
|
296
|
+
message: `Setup skill "${skill.name}" selected but no corresponding usage skills: ${skill.providesSetupFor.map((id) => matrix.skills[id]?.name || id).join(", ")}`,
|
|
297
|
+
skills: [skillId, ...skill.providesSetupFor]
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return {
|
|
302
|
+
valid: errors.length === 0,
|
|
303
|
+
errors,
|
|
304
|
+
warnings
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
function getAvailableSkills(categoryId, currentSelections, matrix, options) {
|
|
308
|
+
const skillOptions = [];
|
|
309
|
+
const resolvedSelections = currentSelections.map(
|
|
310
|
+
(s) => resolveAlias(s, matrix)
|
|
311
|
+
);
|
|
312
|
+
for (const skill of Object.values(matrix.skills)) {
|
|
313
|
+
if (skill.category !== categoryId) {
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
const disabled = isDisabled(skill.id, currentSelections, matrix, options);
|
|
317
|
+
const discouraged = !disabled && isDiscouraged(skill.id, currentSelections, matrix);
|
|
318
|
+
const recommended = !disabled && !discouraged && isRecommended(skill.id, currentSelections, matrix);
|
|
319
|
+
skillOptions.push({
|
|
320
|
+
id: skill.id,
|
|
321
|
+
alias: skill.alias,
|
|
322
|
+
name: skill.name,
|
|
323
|
+
description: skill.description,
|
|
324
|
+
disabled,
|
|
325
|
+
disabledReason: disabled ? getDisableReason(skill.id, currentSelections, matrix) : void 0,
|
|
326
|
+
discouraged,
|
|
327
|
+
discouragedReason: discouraged ? getDiscourageReason(skill.id, currentSelections, matrix) : void 0,
|
|
328
|
+
recommended,
|
|
329
|
+
recommendedReason: recommended ? getRecommendReason(skill.id, currentSelections, matrix) : void 0,
|
|
330
|
+
selected: resolvedSelections.includes(skill.id),
|
|
331
|
+
alternatives: skill.alternatives.map((a) => a.skillId)
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
return skillOptions;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// src/cli-v2/components/wizard/step-build.tsx
|
|
338
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
339
|
+
var MIN_DOMAINS_FOR_PROGRESS = 2;
|
|
340
|
+
function validateBuildStep(categories, selections) {
|
|
341
|
+
for (const category of categories) {
|
|
342
|
+
if (category.required) {
|
|
343
|
+
const categorySelections = selections[category.id] || [];
|
|
344
|
+
if (categorySelections.length === 0) {
|
|
345
|
+
return {
|
|
346
|
+
valid: false,
|
|
347
|
+
message: `Please select a ${category.name}`
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return { valid: true };
|
|
353
|
+
}
|
|
354
|
+
function computeOptionState(skill) {
|
|
355
|
+
if (skill.disabled) {
|
|
356
|
+
return "disabled";
|
|
357
|
+
}
|
|
358
|
+
if (skill.discouraged) {
|
|
359
|
+
return "discouraged";
|
|
360
|
+
}
|
|
361
|
+
if (skill.recommended) {
|
|
362
|
+
return "recommended";
|
|
363
|
+
}
|
|
364
|
+
return "normal";
|
|
365
|
+
}
|
|
366
|
+
function getDisplayLabel(skill) {
|
|
367
|
+
const authorPattern = /\s*\(@[^)]+\)\s*$/;
|
|
368
|
+
return skill.name.replace(authorPattern, "");
|
|
369
|
+
}
|
|
370
|
+
function getStateReason(skill) {
|
|
371
|
+
if (skill.disabled && skill.disabledReason) {
|
|
372
|
+
return skill.disabledReason;
|
|
373
|
+
}
|
|
374
|
+
if (skill.discouraged && skill.discouragedReason) {
|
|
375
|
+
return skill.discouragedReason;
|
|
376
|
+
}
|
|
377
|
+
if (skill.recommended && skill.recommendedReason) {
|
|
378
|
+
return skill.recommendedReason;
|
|
379
|
+
}
|
|
380
|
+
return void 0;
|
|
381
|
+
}
|
|
382
|
+
function buildCategoriesForDomain(domain, allSelections, matrix, expertMode) {
|
|
383
|
+
const subcategories = Object.values(matrix.categories).filter((cat) => cat.domain === domain && cat.parent).sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
384
|
+
const categoryRows = subcategories.map((cat) => {
|
|
385
|
+
const skillOptions = getAvailableSkills(cat.id, allSelections, matrix, {
|
|
386
|
+
expertMode
|
|
387
|
+
});
|
|
388
|
+
const options = skillOptions.map((skill) => ({
|
|
389
|
+
id: skill.alias || skill.id,
|
|
390
|
+
// Use alias for selection tracking
|
|
391
|
+
label: getDisplayLabel(skill),
|
|
392
|
+
// Clean display name without author
|
|
393
|
+
state: computeOptionState(skill),
|
|
394
|
+
stateReason: getStateReason(skill),
|
|
395
|
+
selected: skill.selected
|
|
396
|
+
}));
|
|
397
|
+
return {
|
|
398
|
+
id: cat.id,
|
|
399
|
+
name: cat.name,
|
|
400
|
+
required: cat.required ?? false,
|
|
401
|
+
exclusive: cat.exclusive ?? true,
|
|
402
|
+
options
|
|
403
|
+
};
|
|
404
|
+
});
|
|
405
|
+
return categoryRows;
|
|
406
|
+
}
|
|
407
|
+
function getDomainDisplayName(domain) {
|
|
408
|
+
const displayNames = {
|
|
409
|
+
web: "Web",
|
|
410
|
+
api: "API",
|
|
411
|
+
cli: "CLI",
|
|
412
|
+
mobile: "Mobile",
|
|
413
|
+
shared: "Shared"
|
|
414
|
+
};
|
|
415
|
+
return displayNames[domain] || domain.charAt(0).toUpperCase() + domain.slice(1);
|
|
416
|
+
}
|
|
417
|
+
function countSelections(categories) {
|
|
418
|
+
let selected = 0;
|
|
419
|
+
let total = 0;
|
|
420
|
+
for (const category of categories) {
|
|
421
|
+
for (const option of category.options) {
|
|
422
|
+
if (option.state !== "disabled") {
|
|
423
|
+
total++;
|
|
424
|
+
if (option.selected) {
|
|
425
|
+
selected++;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return { selected, total };
|
|
431
|
+
}
|
|
432
|
+
var Header = ({ domain, selectionCount }) => {
|
|
433
|
+
return /* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", marginBottom: 1, children: [
|
|
434
|
+
/* @__PURE__ */ jsxs(Text, { bold: true, children: [
|
|
435
|
+
"Configure your ",
|
|
436
|
+
/* @__PURE__ */ jsx(Text, { color: "cyan", children: getDomainDisplayName(domain) }),
|
|
437
|
+
" ",
|
|
438
|
+
"stack:"
|
|
439
|
+
] }),
|
|
440
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
441
|
+
selectionCount.selected,
|
|
442
|
+
"/",
|
|
443
|
+
selectionCount.total,
|
|
444
|
+
" selected"
|
|
445
|
+
] })
|
|
446
|
+
] });
|
|
447
|
+
};
|
|
448
|
+
var Footer = ({
|
|
449
|
+
showContinueHint,
|
|
450
|
+
validationError
|
|
451
|
+
}) => {
|
|
452
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
|
|
453
|
+
validationError && /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { color: "yellow", children: validationError }) }),
|
|
454
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
455
|
+
"\u2190",
|
|
456
|
+
"/",
|
|
457
|
+
"\u2192",
|
|
458
|
+
" options ",
|
|
459
|
+
"\u2191",
|
|
460
|
+
"/",
|
|
461
|
+
"\u2193",
|
|
462
|
+
" categories SPACE select TAB descriptions E expert ENTER continue ESC back"
|
|
463
|
+
] })
|
|
464
|
+
] });
|
|
465
|
+
};
|
|
466
|
+
var StepBuild = ({
|
|
467
|
+
matrix,
|
|
468
|
+
domain,
|
|
469
|
+
selectedDomains,
|
|
470
|
+
currentDomainIndex,
|
|
471
|
+
selections,
|
|
472
|
+
allSelections,
|
|
473
|
+
focusedRow,
|
|
474
|
+
focusedCol,
|
|
475
|
+
showDescriptions,
|
|
476
|
+
expertMode,
|
|
477
|
+
onToggle,
|
|
478
|
+
onFocusChange,
|
|
479
|
+
onToggleDescriptions,
|
|
480
|
+
onToggleExpertMode,
|
|
481
|
+
onContinue,
|
|
482
|
+
onBack
|
|
483
|
+
}) => {
|
|
484
|
+
const [validationError, setValidationError] = useState(
|
|
485
|
+
void 0
|
|
486
|
+
);
|
|
487
|
+
const categories = buildCategoriesForDomain(
|
|
488
|
+
domain,
|
|
489
|
+
allSelections,
|
|
490
|
+
matrix,
|
|
491
|
+
expertMode
|
|
492
|
+
);
|
|
493
|
+
const selectionCount = countSelections(categories);
|
|
494
|
+
const showProgress = selectedDomains.length >= MIN_DOMAINS_FOR_PROGRESS;
|
|
495
|
+
const isLastDomain = currentDomainIndex === selectedDomains.length - 1;
|
|
496
|
+
const nextDomain = isLastDomain ? void 0 : selectedDomains[currentDomainIndex + 1];
|
|
497
|
+
useInput((input, key) => {
|
|
498
|
+
if (key.return) {
|
|
499
|
+
const validation = validateBuildStep(categories, selections);
|
|
500
|
+
if (validation.valid) {
|
|
501
|
+
setValidationError(void 0);
|
|
502
|
+
onContinue();
|
|
503
|
+
} else {
|
|
504
|
+
setValidationError(validation.message);
|
|
505
|
+
}
|
|
506
|
+
} else if (key.escape) {
|
|
507
|
+
setValidationError(void 0);
|
|
508
|
+
onBack();
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
512
|
+
/* @__PURE__ */ jsx(Header, { domain, selectionCount }),
|
|
513
|
+
showProgress && /* @__PURE__ */ jsx(
|
|
514
|
+
SectionProgress,
|
|
515
|
+
{
|
|
516
|
+
label: "Domain",
|
|
517
|
+
current: getDomainDisplayName(domain),
|
|
518
|
+
index: currentDomainIndex + 1,
|
|
519
|
+
total: selectedDomains.length,
|
|
520
|
+
next: nextDomain ? getDomainDisplayName(nextDomain) : void 0
|
|
521
|
+
}
|
|
522
|
+
),
|
|
523
|
+
/* @__PURE__ */ jsx(
|
|
524
|
+
CategoryGrid,
|
|
525
|
+
{
|
|
526
|
+
categories,
|
|
527
|
+
focusedRow,
|
|
528
|
+
focusedCol,
|
|
529
|
+
showDescriptions,
|
|
530
|
+
expertMode,
|
|
531
|
+
onToggle,
|
|
532
|
+
onFocusChange,
|
|
533
|
+
onToggleDescriptions,
|
|
534
|
+
onToggleExpertMode
|
|
535
|
+
}
|
|
536
|
+
),
|
|
537
|
+
/* @__PURE__ */ jsx(Footer, { showContinueHint: true, validationError })
|
|
538
|
+
] });
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
export {
|
|
542
|
+
validateSelection,
|
|
543
|
+
validateBuildStep,
|
|
544
|
+
getDisplayLabel,
|
|
545
|
+
StepBuild
|
|
546
|
+
};
|
|
547
|
+
//# sourceMappingURL=chunk-Q6DR5QUH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli-v2/components/wizard/step-build.tsx","../src/cli-v2/lib/matrix-resolver.ts"],"sourcesContent":["/**\n * StepBuild component - Main Build step for domain-based technology selection.\n *\n * Uses CategoryGrid for 2D grid selection and SectionProgress for multi-domain\n * progress indication. Replaces the old linear category->subcategory flow.\n *\n * This component is stateless - all state is managed via props from the parent\n * wizard component, following React's controlled component pattern.\n */\nimport React, { useState } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport type { MergedSkillsMatrix } from \"../../types-matrix.js\";\nimport { getAvailableSkills } from \"../../lib/matrix-resolver.js\";\nimport {\n CategoryGrid,\n type CategoryRow,\n type CategoryOption,\n type OptionState,\n} from \"./category-grid.js\";\nimport { SectionProgress } from \"./section-progress.js\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface StepBuildProps {\n /** Skills matrix for category/skill lookup */\n matrix: MergedSkillsMatrix;\n /** Current domain being configured (e.g., 'web', 'api') */\n domain: string;\n /** All selected domains (for progress indicator) */\n selectedDomains: string[];\n /** Current domain index (0-based) */\n currentDomainIndex: number;\n /** Current selections by subcategory */\n selections: Record<string, string[]>;\n /** All current selections (for state calculation across domains) */\n allSelections: string[];\n /** Grid focus state */\n focusedRow: number;\n focusedCol: number;\n /** UI toggles */\n showDescriptions: boolean;\n expertMode: boolean;\n /** Callbacks */\n onToggle: (subcategoryId: string, technologyId: string) => void;\n onFocusChange: (row: number, col: number) => void;\n onToggleDescriptions: () => void;\n onToggleExpertMode: () => void;\n onContinue: () => void;\n onBack: () => void;\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/** Minimum number of domains to show progress indicator */\nconst MIN_DOMAINS_FOR_PROGRESS = 2;\n\n// =============================================================================\n// Validation\n// =============================================================================\n\nexport interface ValidationResult {\n valid: boolean;\n message?: string;\n}\n\n/**\n * Validate that required categories have at least one selection.\n */\nexport function validateBuildStep(\n categories: CategoryRow[],\n selections: Record<string, string[]>,\n): ValidationResult {\n for (const category of categories) {\n if (category.required) {\n const categorySelections = selections[category.id] || [];\n if (categorySelections.length === 0) {\n return {\n valid: false,\n message: `Please select a ${category.name}`,\n };\n }\n }\n }\n return { valid: true };\n}\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\n/**\n * Compute option state from skill flags.\n */\nfunction computeOptionState(skill: {\n disabled: boolean;\n discouraged: boolean;\n recommended: boolean;\n}): OptionState {\n if (skill.disabled) {\n return \"disabled\";\n }\n if (skill.discouraged) {\n return \"discouraged\";\n }\n if (skill.recommended) {\n return \"recommended\";\n }\n return \"normal\";\n}\n\n/**\n * Get clean display label for a skill option.\n * Uses name with author suffix stripped for accurate display.\n * e.g., \"React (@vince)\" -> \"React\", \"SCSS Modules (@vince)\" -> \"SCSS Modules\"\n */\nexport function getDisplayLabel(skill: {\n alias?: string;\n name: string;\n}): string {\n // Strip author suffix like \" (@vince)\" from name\n // This preserves the original capitalization (e.g., \"SCSS Modules\" stays as-is)\n const authorPattern = /\\s*\\(@[^)]+\\)\\s*$/;\n return skill.name.replace(authorPattern, \"\");\n}\n\n/**\n * Get state reason from skill flags.\n */\nfunction getStateReason(skill: {\n disabled: boolean;\n disabledReason?: string;\n discouraged: boolean;\n discouragedReason?: string;\n recommended: boolean;\n recommendedReason?: string;\n}): string | undefined {\n if (skill.disabled && skill.disabledReason) {\n return skill.disabledReason;\n }\n if (skill.discouraged && skill.discouragedReason) {\n return skill.discouragedReason;\n }\n if (skill.recommended && skill.recommendedReason) {\n return skill.recommendedReason;\n }\n return undefined;\n}\n\n/**\n * Build CategoryRow[] from matrix for a specific domain.\n *\n * Filters subcategories by domain and builds options using getAvailableSkills.\n */\nfunction buildCategoriesForDomain(\n domain: string,\n allSelections: string[],\n matrix: MergedSkillsMatrix,\n expertMode: boolean,\n): CategoryRow[] {\n // Get subcategories for the current domain (categories with parent and matching domain)\n const subcategories = Object.values(matrix.categories)\n .filter((cat) => cat.domain === domain && cat.parent)\n .sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n\n // Build CategoryRow for each subcategory\n const categoryRows: CategoryRow[] = subcategories.map((cat) => {\n // Get available skills with computed states\n const skillOptions = getAvailableSkills(cat.id, allSelections, matrix, {\n expertMode,\n });\n\n // Map skills to CategoryOption[]\n const options: CategoryOption[] = skillOptions.map((skill) => ({\n id: skill.alias || skill.id, // Use alias for selection tracking\n label: getDisplayLabel(skill), // Clean display name without author\n state: computeOptionState(skill),\n stateReason: getStateReason(skill),\n selected: skill.selected,\n }));\n\n return {\n id: cat.id,\n name: cat.name,\n required: cat.required ?? false,\n exclusive: cat.exclusive ?? true,\n options,\n };\n });\n\n return categoryRows;\n}\n\n/**\n * Get display name for a domain.\n */\nfunction getDomainDisplayName(domain: string): string {\n const displayNames: Record<string, string> = {\n web: \"Web\",\n api: \"API\",\n cli: \"CLI\",\n mobile: \"Mobile\",\n shared: \"Shared\",\n };\n return (\n displayNames[domain] || domain.charAt(0).toUpperCase() + domain.slice(1)\n );\n}\n\n/**\n * Count selected options across categories.\n */\nfunction countSelections(categories: CategoryRow[]): {\n selected: number;\n total: number;\n} {\n let selected = 0;\n let total = 0;\n for (const category of categories) {\n for (const option of category.options) {\n if (option.state !== \"disabled\") {\n total++;\n if (option.selected) {\n selected++;\n }\n }\n }\n }\n return { selected, total };\n}\n\n// =============================================================================\n// Header Component (Domain info with selection count)\n// =============================================================================\n\ninterface HeaderProps {\n domain: string;\n selectionCount: { selected: number; total: number };\n}\n\nconst Header: React.FC<HeaderProps> = ({ domain, selectionCount }) => {\n return (\n <Box justifyContent=\"space-between\" marginBottom={1}>\n <Text bold>\n Configure your <Text color=\"cyan\">{getDomainDisplayName(domain)}</Text>{\" \"}\n stack:\n </Text>\n <Text dimColor>\n {selectionCount.selected}/{selectionCount.total} selected\n </Text>\n </Box>\n );\n};\n\n// =============================================================================\n// Footer Component (Keyboard Help)\n// =============================================================================\n\ninterface FooterProps {\n showContinueHint: boolean;\n validationError?: string;\n}\n\nconst Footer: React.FC<FooterProps> = ({\n showContinueHint,\n validationError,\n}) => {\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n {/* Validation error message */}\n {validationError && (\n <Box marginBottom={1}>\n <Text color=\"yellow\">{validationError}</Text>\n </Box>\n )}\n\n {/* Keyboard shortcuts help */}\n <Text dimColor>\n {\"\\u2190\"}/{\"\\u2192\"} options {\"\\u2191\"}/{\"\\u2193\"} categories SPACE\n select TAB descriptions E expert ENTER continue ESC back\n </Text>\n </Box>\n );\n};\n\n// =============================================================================\n// Main Component\n// =============================================================================\n\nexport const StepBuild: React.FC<StepBuildProps> = ({\n matrix,\n domain,\n selectedDomains,\n currentDomainIndex,\n selections,\n allSelections,\n focusedRow,\n focusedCol,\n showDescriptions,\n expertMode,\n onToggle,\n onFocusChange,\n onToggleDescriptions,\n onToggleExpertMode,\n onContinue,\n onBack,\n}) => {\n // Validation state for showing error messages\n const [validationError, setValidationError] = useState<string | undefined>(\n undefined,\n );\n\n // Build categories for the current domain\n const categories = buildCategoriesForDomain(\n domain,\n allSelections,\n matrix,\n expertMode,\n );\n\n // Selection count for header\n const selectionCount = countSelections(categories);\n\n // Multi-domain progress\n const showProgress = selectedDomains.length >= MIN_DOMAINS_FOR_PROGRESS;\n const isLastDomain = currentDomainIndex === selectedDomains.length - 1;\n const nextDomain = isLastDomain\n ? undefined\n : selectedDomains[currentDomainIndex + 1];\n\n // Handle keyboard input for Enter and Escape\n useInput((input, key) => {\n if (key.return) {\n // Validate before continuing\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\">\n {/* Header with domain and selection count */}\n <Header domain={domain} selectionCount={selectionCount} />\n\n {/* Progress indicator for multi-domain */}\n {showProgress && (\n <SectionProgress\n label=\"Domain\"\n current={getDomainDisplayName(domain)}\n index={currentDomainIndex + 1}\n total={selectedDomains.length}\n next={nextDomain ? getDomainDisplayName(nextDomain) : undefined}\n />\n )}\n\n {/* Category grid */}\n <CategoryGrid\n categories={categories}\n focusedRow={focusedRow}\n focusedCol={focusedCol}\n showDescriptions={showDescriptions}\n expertMode={expertMode}\n onToggle={onToggle}\n onFocusChange={onFocusChange}\n onToggleDescriptions={onToggleDescriptions}\n onToggleExpertMode={onToggleExpertMode}\n />\n\n {/* Footer with keyboard hints */}\n <Footer showContinueHint validationError={validationError} />\n </Box>\n );\n};\n","import type {\n MergedSkillsMatrix,\n ResolvedSkill,\n SkillOption,\n SelectionValidation,\n ValidationError,\n ValidationWarning,\n} from \"../types-matrix\";\n\nexport function resolveAlias(\n aliasOrId: string,\n matrix: MergedSkillsMatrix,\n): string {\n return matrix.aliases[aliasOrId] || aliasOrId;\n}\n\nexport function getDependentSkills(\n skillId: string,\n currentSelections: string[],\n matrix: MergedSkillsMatrix,\n): string[] {\n const fullId = resolveAlias(skillId, matrix);\n const skill = matrix.skills[fullId];\n\n if (!skill) return [];\n\n const resolvedSelections = currentSelections.map((s) =>\n resolveAlias(s, matrix),\n );\n const dependents: string[] = [];\n\n for (const selectedId of resolvedSelections) {\n if (selectedId === fullId) continue;\n\n const selectedSkill = matrix.skills[selectedId];\n if (!selectedSkill) continue;\n\n for (const requirement of selectedSkill.requires) {\n if (requirement.needsAny) {\n const satisfiedReqs = requirement.skillIds.filter((reqId) =>\n resolvedSelections.includes(reqId),\n );\n if (satisfiedReqs.length === 1 && satisfiedReqs[0] === fullId) {\n dependents.push(selectedId);\n }\n } else {\n if (requirement.skillIds.includes(fullId)) {\n dependents.push(selectedId);\n }\n }\n }\n }\n\n return dependents;\n}\n\nexport interface SkillCheckOptions {\n expertMode?: boolean;\n}\n\nexport function isDisabled(\n skillId: string,\n currentSelections: string[],\n matrix: MergedSkillsMatrix,\n options?: SkillCheckOptions,\n): boolean {\n if (options?.expertMode) {\n return false;\n }\n\n const fullId = resolveAlias(skillId, matrix);\n const skill = matrix.skills[fullId];\n\n if (!skill) {\n return false;\n }\n\n for (const selectedId of currentSelections) {\n const selectedFullId = resolveAlias(selectedId, matrix);\n\n if (skill.conflictsWith.some((c) => c.skillId === selectedFullId)) {\n return true;\n }\n\n const selectedSkill = matrix.skills[selectedFullId];\n if (\n selectedSkill &&\n selectedSkill.conflictsWith.some((c) => c.skillId === fullId)\n ) {\n return true;\n }\n }\n\n const resolvedSelections = currentSelections.map((s) =>\n resolveAlias(s, matrix),\n );\n\n for (const requirement of skill.requires) {\n if (requirement.needsAny) {\n const hasAny = requirement.skillIds.some((reqId) =>\n resolvedSelections.includes(reqId),\n );\n if (!hasAny) {\n return true;\n }\n } else {\n const hasAll = requirement.skillIds.every((reqId) =>\n resolvedSelections.includes(reqId),\n );\n if (!hasAll) {\n return true;\n }\n }\n }\n\n return false;\n}\n\nexport function getDisableReason(\n skillId: string,\n currentSelections: string[],\n matrix: MergedSkillsMatrix,\n): string | undefined {\n const fullId = resolveAlias(skillId, matrix);\n const skill = matrix.skills[fullId];\n\n if (!skill) {\n return undefined;\n }\n\n const resolvedSelections = currentSelections.map((s) =>\n resolveAlias(s, matrix),\n );\n\n for (const selectedId of resolvedSelections) {\n const conflict = skill.conflictsWith.find((c) => c.skillId === selectedId);\n if (conflict) {\n const selectedSkill = matrix.skills[selectedId];\n const selectedName = selectedSkill?.name || selectedId;\n return `${conflict.reason} (conflicts with ${selectedName})`;\n }\n\n const selectedSkill = matrix.skills[selectedId];\n if (selectedSkill) {\n const reverseConflict = selectedSkill.conflictsWith.find(\n (c) => c.skillId === fullId,\n );\n if (reverseConflict) {\n const selectedName = selectedSkill.name;\n return `${reverseConflict.reason} (conflicts with ${selectedName})`;\n }\n }\n }\n\n for (const requirement of skill.requires) {\n if (requirement.needsAny) {\n const hasAny = requirement.skillIds.some((reqId) =>\n resolvedSelections.includes(reqId),\n );\n if (!hasAny) {\n const requiredNames = requirement.skillIds\n .map((id) => matrix.skills[id]?.name || id)\n .join(\" or \");\n return `${requirement.reason} (requires ${requiredNames})`;\n }\n } else {\n const missingIds = requirement.skillIds.filter(\n (reqId) => !resolvedSelections.includes(reqId),\n );\n if (missingIds.length > 0) {\n const missingNames = missingIds\n .map((id) => matrix.skills[id]?.name || id)\n .join(\", \");\n return `${requirement.reason} (requires ${missingNames})`;\n }\n }\n }\n\n return undefined;\n}\n\nexport function isDiscouraged(\n skillId: string,\n currentSelections: string[],\n matrix: MergedSkillsMatrix,\n): boolean {\n const fullId = resolveAlias(skillId, matrix);\n const skill = matrix.skills[fullId];\n\n if (!skill) {\n return false;\n }\n\n const resolvedSelections = currentSelections.map((s) =>\n resolveAlias(s, matrix),\n );\n\n for (const selectedId of resolvedSelections) {\n const selectedSkill = matrix.skills[selectedId];\n if (\n selectedSkill &&\n selectedSkill.discourages.some((d) => d.skillId === fullId)\n ) {\n return true;\n }\n\n if (skill.discourages.some((d) => d.skillId === selectedId)) {\n return true;\n }\n }\n\n return false;\n}\n\nexport function getDiscourageReason(\n skillId: string,\n currentSelections: string[],\n matrix: MergedSkillsMatrix,\n): string | undefined {\n const fullId = resolveAlias(skillId, matrix);\n const skill = matrix.skills[fullId];\n\n if (!skill) {\n return undefined;\n }\n\n const resolvedSelections = currentSelections.map((s) =>\n resolveAlias(s, matrix),\n );\n\n for (const selectedId of resolvedSelections) {\n const selectedSkill = matrix.skills[selectedId];\n if (selectedSkill) {\n const discourage = selectedSkill.discourages.find(\n (d) => d.skillId === fullId,\n );\n if (discourage) {\n return discourage.reason;\n }\n }\n\n const reverseDiscourage = skill.discourages.find(\n (d) => d.skillId === selectedId,\n );\n if (reverseDiscourage) {\n return reverseDiscourage.reason;\n }\n }\n\n return undefined;\n}\n\nexport function isRecommended(\n skillId: string,\n currentSelections: string[],\n matrix: MergedSkillsMatrix,\n): boolean {\n const fullId = resolveAlias(skillId, matrix);\n const skill = matrix.skills[fullId];\n\n if (!skill) {\n return false;\n }\n\n const resolvedSelections = currentSelections.map((s) =>\n resolveAlias(s, matrix),\n );\n\n for (const selectedId of resolvedSelections) {\n const selectedSkill = matrix.skills[selectedId];\n if (\n selectedSkill &&\n selectedSkill.recommends.some((r) => r.skillId === fullId)\n ) {\n return true;\n }\n }\n\n return false;\n}\n\nexport function getRecommendReason(\n skillId: string,\n currentSelections: string[],\n matrix: MergedSkillsMatrix,\n): string | undefined {\n const fullId = resolveAlias(skillId, matrix);\n const skill = matrix.skills[fullId];\n\n if (!skill) {\n return undefined;\n }\n\n const resolvedSelections = currentSelections.map((s) =>\n resolveAlias(s, matrix),\n );\n\n for (const selectedId of resolvedSelections) {\n const selectedSkill = matrix.skills[selectedId];\n if (selectedSkill) {\n const recommendation = selectedSkill.recommends.find(\n (r) => r.skillId === fullId,\n );\n if (recommendation) {\n return `${recommendation.reason} (recommended by ${selectedSkill.name})`;\n }\n }\n }\n\n return undefined;\n}\n\nexport function validateSelection(\n selections: string[],\n matrix: MergedSkillsMatrix,\n): SelectionValidation {\n const errors: ValidationError[] = [];\n const warnings: ValidationWarning[] = [];\n const resolvedSelections = selections.map((s) => resolveAlias(s, matrix));\n\n for (let i = 0; i < resolvedSelections.length; i++) {\n const skillA = matrix.skills[resolvedSelections[i]];\n if (!skillA) continue;\n\n for (let j = i + 1; j < resolvedSelections.length; j++) {\n const skillBId = resolvedSelections[j];\n const conflict = skillA.conflictsWith.find((c) => c.skillId === skillBId);\n if (conflict) {\n errors.push({\n type: \"conflict\",\n message: `${skillA.name} conflicts with ${matrix.skills[skillBId]?.name || skillBId}: ${conflict.reason}`,\n skills: [skillA.id, skillBId],\n });\n }\n }\n }\n\n for (const skillId of resolvedSelections) {\n const skill = matrix.skills[skillId];\n if (!skill) continue;\n\n for (const requirement of skill.requires) {\n if (requirement.needsAny) {\n const hasAny = requirement.skillIds.some((reqId) =>\n resolvedSelections.includes(reqId),\n );\n if (!hasAny) {\n errors.push({\n type: \"missing_requirement\",\n message: `${skill.name} requires one of: ${requirement.skillIds.map((id) => matrix.skills[id]?.name || id).join(\", \")}`,\n skills: [skillId, ...requirement.skillIds],\n });\n }\n } else {\n const missingIds = requirement.skillIds.filter(\n (reqId) => !resolvedSelections.includes(reqId),\n );\n if (missingIds.length > 0) {\n errors.push({\n type: \"missing_requirement\",\n message: `${skill.name} requires: ${missingIds.map((id) => matrix.skills[id]?.name || id).join(\", \")}`,\n skills: [skillId, ...missingIds],\n });\n }\n }\n }\n }\n\n const categorySelections = new Map<string, string[]>();\n for (const skillId of resolvedSelections) {\n const skill = matrix.skills[skillId];\n if (!skill) continue;\n\n const existing = categorySelections.get(skill.category) || [];\n existing.push(skillId);\n categorySelections.set(skill.category, existing);\n }\n\n for (const [categoryId, skillIds] of categorySelections.entries()) {\n if (skillIds.length > 1) {\n const category = matrix.categories[categoryId];\n if (category?.exclusive) {\n errors.push({\n type: \"category_exclusive\",\n message: `Category \"${category.name}\" only allows one selection, but multiple selected: ${skillIds.map((id) => matrix.skills[id]?.name || id).join(\", \")}`,\n skills: skillIds,\n });\n }\n }\n }\n\n for (const skillId of resolvedSelections) {\n const skill = matrix.skills[skillId];\n if (!skill) continue;\n\n for (const recommendation of skill.recommends) {\n if (!resolvedSelections.includes(recommendation.skillId)) {\n const recommendedSkill = matrix.skills[recommendation.skillId];\n if (recommendedSkill) {\n const hasConflict = recommendedSkill.conflictsWith.some((c) =>\n resolvedSelections.includes(c.skillId),\n );\n if (!hasConflict) {\n warnings.push({\n type: \"missing_recommendation\",\n message: `${skill.name} recommends ${recommendedSkill.name}: ${recommendation.reason}`,\n skills: [skillId, recommendation.skillId],\n });\n }\n }\n }\n }\n }\n\n for (const skillId of resolvedSelections) {\n const skill = matrix.skills[skillId];\n if (!skill || skill.providesSetupFor.length === 0) continue;\n\n const hasUsageSkill = skill.providesSetupFor.some((usageId) =>\n resolvedSelections.includes(usageId),\n );\n if (!hasUsageSkill) {\n warnings.push({\n type: \"unused_setup\",\n message: `Setup skill \"${skill.name}\" selected but no corresponding usage skills: ${skill.providesSetupFor.map((id) => matrix.skills[id]?.name || id).join(\", \")}`,\n skills: [skillId, ...skill.providesSetupFor],\n });\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n };\n}\n\nexport function getAvailableSkills(\n categoryId: string,\n currentSelections: string[],\n matrix: MergedSkillsMatrix,\n options?: SkillCheckOptions,\n): SkillOption[] {\n const skillOptions: SkillOption[] = [];\n const resolvedSelections = currentSelections.map((s) =>\n resolveAlias(s, matrix),\n );\n\n for (const skill of Object.values(matrix.skills)) {\n if (skill.category !== categoryId) {\n continue;\n }\n\n const disabled = isDisabled(skill.id, currentSelections, matrix, options);\n const discouraged =\n !disabled && isDiscouraged(skill.id, currentSelections, matrix);\n const recommended =\n !disabled &&\n !discouraged &&\n isRecommended(skill.id, currentSelections, matrix);\n\n skillOptions.push({\n id: skill.id,\n alias: skill.alias,\n name: skill.name,\n description: skill.description,\n disabled,\n disabledReason: disabled\n ? getDisableReason(skill.id, currentSelections, matrix)\n : undefined,\n discouraged,\n discouragedReason: discouraged\n ? getDiscourageReason(skill.id, currentSelections, matrix)\n : undefined,\n recommended,\n recommendedReason: recommended\n ? getRecommendReason(skill.id, currentSelections, matrix)\n : undefined,\n selected: resolvedSelections.includes(skill.id),\n alternatives: skill.alternatives.map((a) => a.skillId),\n });\n }\n\n return skillOptions;\n}\n\nexport function getSkillsByCategory(\n categoryId: string,\n matrix: MergedSkillsMatrix,\n): ResolvedSkill[] {\n const skills: ResolvedSkill[] = [];\n\n for (const skill of Object.values(matrix.skills)) {\n if (skill.category === categoryId) {\n skills.push(skill);\n }\n }\n\n return skills;\n}\n\nexport function isCategoryAllDisabled(\n categoryId: string,\n currentSelections: string[],\n matrix: MergedSkillsMatrix,\n options?: SkillCheckOptions,\n): { disabled: boolean; reason?: string } {\n if (options?.expertMode) {\n return { disabled: false };\n }\n\n const skills = getSkillsByCategory(categoryId, matrix);\n\n if (skills.length === 0) {\n return { disabled: false };\n }\n\n const disabledSkills: Array<{ skillId: string; reason: string | undefined }> =\n [];\n\n for (const skill of skills) {\n if (isDisabled(skill.id, currentSelections, matrix, options)) {\n disabledSkills.push({\n skillId: skill.id,\n reason: getDisableReason(skill.id, currentSelections, matrix),\n });\n }\n }\n\n if (disabledSkills.length === skills.length) {\n const firstReason = disabledSkills[0]?.reason;\n const shortReason = firstReason?.split(\" (\")[0] || \"requirements not met\";\n return { disabled: true, reason: shortReason };\n }\n\n return { disabled: false };\n}\n\nexport function getSubcategories(\n parentCategoryId: string,\n matrix: MergedSkillsMatrix,\n): string[] {\n const subcategories: string[] = [];\n\n for (const category of Object.values(matrix.categories)) {\n if (category.parent === parentCategoryId) {\n subcategories.push(category.id);\n }\n }\n\n subcategories.sort((a, b) => {\n const catA = matrix.categories[a];\n const catB = matrix.categories[b];\n return (catA?.order ?? 0) - (catB?.order ?? 0);\n });\n\n return subcategories;\n}\n\nexport function getTopLevelCategories(matrix: MergedSkillsMatrix): string[] {\n const topLevel: string[] = [];\n\n for (const category of Object.values(matrix.categories)) {\n if (!category.parent) {\n topLevel.push(category.id);\n }\n }\n\n topLevel.sort((a, b) => {\n const catA = matrix.categories[a];\n const catB = matrix.categories[b];\n return (catA?.order ?? 0) - (catB?.order ?? 0);\n });\n\n return topLevel;\n}\n"],"mappings":"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","names":["selectedSkill"]}
|