@agents-inc/cli 0.74.12 → 0.74.13
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 +12 -0
- package/dist/{chunk-GJWHA6P4.js → chunk-4EPFKZB5.js} +3 -3
- package/dist/{chunk-DZUZVT5X.js → chunk-746DCNGM.js} +2 -2
- package/dist/{chunk-TDHEAHXD.js → chunk-7Q6RMYR3.js} +2 -2
- package/dist/chunk-7Q6RMYR3.js.map +1 -0
- package/dist/{chunk-6RGNXTTG.js → chunk-7X4F4HCO.js} +14 -21
- package/dist/chunk-7X4F4HCO.js.map +1 -0
- package/dist/{chunk-JBQRRJRY.js → chunk-A5TTDOP4.js} +8 -8
- package/dist/{chunk-62FBXUNR.js → chunk-CBXMOWQY.js} +2 -2
- package/dist/{chunk-PJUC2WCA.js → chunk-GHBZ7NUI.js} +10 -6
- package/dist/chunk-GHBZ7NUI.js.map +1 -0
- package/dist/{chunk-7M4INWUR.js → chunk-JNQKCZA3.js} +4 -7
- package/dist/chunk-JNQKCZA3.js.map +1 -0
- package/dist/{chunk-MZQKSK3J.js → chunk-JQSJH72Q.js} +17 -8
- package/dist/{chunk-MZQKSK3J.js.map → chunk-JQSJH72Q.js.map} +1 -1
- package/dist/{chunk-NO2XIAJC.js → chunk-MYSIW4HY.js} +2 -2
- package/dist/{chunk-2DKJM7RO.js → chunk-OZ4DXMRF.js} +22 -7
- package/dist/chunk-OZ4DXMRF.js.map +1 -0
- package/dist/{chunk-AKW425U6.js → chunk-Q3F36QZZ.js} +2 -2
- package/dist/{chunk-I7WXUAWA.js → chunk-RKZFLLER.js} +2 -2
- package/dist/{chunk-DG2T7KYG.js → chunk-SASGCSBZ.js} +2 -2
- package/dist/{chunk-YUSLPHXO.js → chunk-TKNPMYKJ.js} +3 -3
- package/dist/{chunk-HGTC76BX.js → chunk-V36FRPAU.js} +4 -2
- package/dist/chunk-V36FRPAU.js.map +1 -0
- package/dist/commands/edit.js +14 -14
- package/dist/commands/init.js +15 -15
- package/dist/components/wizard/category-grid.js +1 -1
- package/dist/components/wizard/category-grid.test.js +1 -1
- package/dist/components/wizard/checkbox-grid.js +2 -2
- package/dist/components/wizard/checkbox-grid.test.js +2 -2
- package/dist/components/wizard/domain-selection.js +3 -3
- package/dist/components/wizard/source-grid.js +1 -1
- package/dist/components/wizard/source-grid.test.js +1 -1
- package/dist/components/wizard/step-agents.js +2 -2
- package/dist/components/wizard/step-agents.test.js +3 -3
- package/dist/components/wizard/step-agents.test.js.map +1 -1
- package/dist/components/wizard/step-build.js +3 -3
- package/dist/components/wizard/step-build.test.js +3 -3
- package/dist/components/wizard/step-confirm.js +2 -2
- package/dist/components/wizard/step-confirm.test.js +2 -2
- 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 +2 -2
- package/dist/components/wizard/step-settings.test.js +2 -2
- package/dist/components/wizard/step-sources.js +4 -4
- package/dist/components/wizard/step-sources.test.js +14 -158
- package/dist/components/wizard/step-sources.test.js.map +1 -1
- package/dist/components/wizard/step-stack.js +4 -4
- package/dist/components/wizard/step-stack.test.js +4 -4
- package/dist/components/wizard/view-title.js +1 -2
- package/dist/components/wizard/wizard-layout.js +2 -2
- package/dist/components/wizard/wizard.js +14 -14
- package/dist/hooks/init.js +15 -15
- package/package.json +1 -1
- package/dist/chunk-2DKJM7RO.js.map +0 -1
- package/dist/chunk-6RGNXTTG.js.map +0 -1
- package/dist/chunk-7M4INWUR.js.map +0 -1
- package/dist/chunk-HGTC76BX.js.map +0 -1
- package/dist/chunk-PJUC2WCA.js.map +0 -1
- package/dist/chunk-TDHEAHXD.js.map +0 -1
- /package/dist/{chunk-GJWHA6P4.js.map → chunk-4EPFKZB5.js.map} +0 -0
- /package/dist/{chunk-DZUZVT5X.js.map → chunk-746DCNGM.js.map} +0 -0
- /package/dist/{chunk-JBQRRJRY.js.map → chunk-A5TTDOP4.js.map} +0 -0
- /package/dist/{chunk-62FBXUNR.js.map → chunk-CBXMOWQY.js.map} +0 -0
- /package/dist/{chunk-NO2XIAJC.js.map → chunk-MYSIW4HY.js.map} +0 -0
- /package/dist/{chunk-AKW425U6.js.map → chunk-Q3F36QZZ.js.map} +0 -0
- /package/dist/{chunk-I7WXUAWA.js.map → chunk-RKZFLLER.js.map} +0 -0
- /package/dist/{chunk-DG2T7KYG.js.map → chunk-SASGCSBZ.js.map} +0 -0
- /package/dist/{chunk-YUSLPHXO.js.map → chunk-TKNPMYKJ.js.map} +0 -0
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
ARROW_DOWN,
|
|
4
|
-
ARROW_UP,
|
|
5
3
|
ENTER,
|
|
6
4
|
ESCAPE,
|
|
7
5
|
INPUT_DELAY_MS,
|
|
@@ -26,15 +24,15 @@ import {
|
|
|
26
24
|
} from "../../chunk-XY3XDVMI.js";
|
|
27
25
|
import {
|
|
28
26
|
StepSources
|
|
29
|
-
} from "../../chunk-
|
|
30
|
-
import "../../chunk-
|
|
27
|
+
} from "../../chunk-7X4F4HCO.js";
|
|
28
|
+
import "../../chunk-V36FRPAU.js";
|
|
31
29
|
import "../../chunk-K77I4XGL.js";
|
|
32
30
|
import "../../chunk-MWGDG4QN.js";
|
|
33
|
-
import "../../chunk-
|
|
31
|
+
import "../../chunk-7Q6RMYR3.js";
|
|
34
32
|
import "../../chunk-7SOPVGDV.js";
|
|
35
33
|
import "../../chunk-UFKDY45I.js";
|
|
36
34
|
import "../../chunk-P2FHS5IS.js";
|
|
37
|
-
import "../../chunk-
|
|
35
|
+
import "../../chunk-JNQKCZA3.js";
|
|
38
36
|
import "../../chunk-AUNBGZS4.js";
|
|
39
37
|
import "../../chunk-WF5PMBIR.js";
|
|
40
38
|
import "../../chunk-KUV24B5M.js";
|
|
@@ -49,10 +47,7 @@ import {
|
|
|
49
47
|
} from "../../chunk-NG2GGK6P.js";
|
|
50
48
|
import "../../chunk-4CRWKTNQ.js";
|
|
51
49
|
import "../../chunk-SRIH4U5Y.js";
|
|
52
|
-
import
|
|
53
|
-
DEFAULT_BRANDING,
|
|
54
|
-
UI_SYMBOLS
|
|
55
|
-
} from "../../chunk-EGMQ3SXN.js";
|
|
50
|
+
import "../../chunk-EGMQ3SXN.js";
|
|
56
51
|
import "../../chunk-NKKYTCBH.js";
|
|
57
52
|
import {
|
|
58
53
|
init_esm_shims
|
|
@@ -86,108 +81,11 @@ describe("StepSources component", () => {
|
|
|
86
81
|
cleanup?.();
|
|
87
82
|
cleanup = void 0;
|
|
88
83
|
});
|
|
89
|
-
describe("choice view rendering", () => {
|
|
90
|
-
it("should render technology count", () => {
|
|
91
|
-
const { lastFrame, unmount } = renderStepSources();
|
|
92
|
-
cleanup = unmount;
|
|
93
|
-
const output = lastFrame();
|
|
94
|
-
globalExpect(output).toContain("2");
|
|
95
|
-
globalExpect(output).toContain("technologies");
|
|
96
|
-
});
|
|
97
|
-
it("should render 'Use all recommended' option", () => {
|
|
98
|
-
const { lastFrame, unmount } = renderStepSources();
|
|
99
|
-
cleanup = unmount;
|
|
100
|
-
const output = lastFrame();
|
|
101
|
-
globalExpect(output).toContain("Use all recommended skills (verified)");
|
|
102
|
-
});
|
|
103
|
-
it("should render 'Customize skill sources' option", () => {
|
|
104
|
-
const { lastFrame, unmount } = renderStepSources();
|
|
105
|
-
cleanup = unmount;
|
|
106
|
-
const output = lastFrame();
|
|
107
|
-
globalExpect(output).toContain("Customize skill sources");
|
|
108
|
-
});
|
|
109
|
-
it("should render verification description", () => {
|
|
110
|
-
const { lastFrame, unmount } = renderStepSources();
|
|
111
|
-
cleanup = unmount;
|
|
112
|
-
const output = lastFrame();
|
|
113
|
-
globalExpect(output).toContain("fastest option");
|
|
114
|
-
globalExpect(output).toContain("verified");
|
|
115
|
-
globalExpect(output).toContain(DEFAULT_BRANDING.NAME);
|
|
116
|
-
});
|
|
117
|
-
it("should render customize description", () => {
|
|
118
|
-
const { lastFrame, unmount } = renderStepSources();
|
|
119
|
-
cleanup = unmount;
|
|
120
|
-
const output = lastFrame();
|
|
121
|
-
globalExpect(output).toContain("Choose alternative skills for each technology");
|
|
122
|
-
});
|
|
123
|
-
it("should show recommended as default selected", () => {
|
|
124
|
-
const { lastFrame, unmount } = renderStepSources();
|
|
125
|
-
cleanup = unmount;
|
|
126
|
-
const output = lastFrame();
|
|
127
|
-
globalExpect(output).toContain(UI_SYMBOLS.CHEVRON);
|
|
128
|
-
globalExpect(output).toContain("Use all recommended");
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
describe("choice view keyboard navigation", () => {
|
|
132
|
-
it("should toggle options with arrow up", async () => {
|
|
133
|
-
const { lastFrame, stdin, unmount } = renderStepSources();
|
|
134
|
-
cleanup = unmount;
|
|
135
|
-
await delay(RENDER_DELAY_MS);
|
|
136
|
-
let output = lastFrame();
|
|
137
|
-
globalExpect(output).toContain("Use all recommended");
|
|
138
|
-
stdin.write(ARROW_UP);
|
|
139
|
-
await delay(INPUT_DELAY_MS);
|
|
140
|
-
output = lastFrame();
|
|
141
|
-
globalExpect(output).toContain("Customize skill sources");
|
|
142
|
-
});
|
|
143
|
-
it("should toggle options with arrow down", async () => {
|
|
144
|
-
const { stdin, unmount } = renderStepSources();
|
|
145
|
-
cleanup = unmount;
|
|
146
|
-
await delay(RENDER_DELAY_MS);
|
|
147
|
-
stdin.write(ARROW_DOWN);
|
|
148
|
-
await delay(INPUT_DELAY_MS);
|
|
149
|
-
});
|
|
150
|
-
it("should call onContinue when Enter pressed on 'Use all recommended'", async () => {
|
|
151
|
-
const onContinue = vi.fn();
|
|
152
|
-
const { stdin, unmount } = renderStepSources({ onContinue });
|
|
153
|
-
cleanup = unmount;
|
|
154
|
-
await delay(RENDER_DELAY_MS);
|
|
155
|
-
stdin.write(ENTER);
|
|
156
|
-
await delay(INPUT_DELAY_MS);
|
|
157
|
-
globalExpect(onContinue).toHaveBeenCalledTimes(1);
|
|
158
|
-
});
|
|
159
|
-
it("should call onBack when Escape pressed", async () => {
|
|
160
|
-
const onBack = vi.fn();
|
|
161
|
-
const { stdin, unmount } = renderStepSources({ onBack });
|
|
162
|
-
cleanup = unmount;
|
|
163
|
-
await delay(RENDER_DELAY_MS);
|
|
164
|
-
stdin.write(ESCAPE);
|
|
165
|
-
await delay(INPUT_DELAY_MS);
|
|
166
|
-
globalExpect(onBack).toHaveBeenCalledTimes(1);
|
|
167
|
-
});
|
|
168
|
-
it("should switch to customize view when Enter pressed on 'Customize'", async () => {
|
|
169
|
-
const { lastFrame, stdin, unmount } = renderStepSources();
|
|
170
|
-
cleanup = unmount;
|
|
171
|
-
await delay(RENDER_DELAY_MS);
|
|
172
|
-
stdin.write(ARROW_DOWN);
|
|
173
|
-
await delay(INPUT_DELAY_MS);
|
|
174
|
-
stdin.write(ENTER);
|
|
175
|
-
await delay(INPUT_DELAY_MS);
|
|
176
|
-
const output = lastFrame();
|
|
177
|
-
globalExpect(output).toContain("Customize skill sources");
|
|
178
|
-
globalExpect(output).toContain("React");
|
|
179
|
-
globalExpect(output).toContain("Zustand");
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
84
|
describe("customize view", () => {
|
|
183
85
|
it("should show source grid with selected technologies", async () => {
|
|
184
|
-
const { lastFrame,
|
|
86
|
+
const { lastFrame, unmount } = renderStepSources();
|
|
185
87
|
cleanup = unmount;
|
|
186
88
|
await delay(RENDER_DELAY_MS);
|
|
187
|
-
stdin.write(ARROW_DOWN);
|
|
188
|
-
await delay(INPUT_DELAY_MS);
|
|
189
|
-
stdin.write(ENTER);
|
|
190
|
-
await delay(INPUT_DELAY_MS);
|
|
191
89
|
const output = lastFrame();
|
|
192
90
|
globalExpect(output).toContain("React");
|
|
193
91
|
globalExpect(output).toContain("Zustand");
|
|
@@ -198,39 +96,23 @@ describe("StepSources component", () => {
|
|
|
198
96
|
const { stdin, unmount } = renderStepSources({ onContinue });
|
|
199
97
|
cleanup = unmount;
|
|
200
98
|
await delay(RENDER_DELAY_MS);
|
|
201
|
-
stdin.write(ARROW_DOWN);
|
|
202
|
-
await delay(INPUT_DELAY_MS);
|
|
203
|
-
stdin.write(ENTER);
|
|
204
|
-
await delay(INPUT_DELAY_MS);
|
|
205
99
|
stdin.write(ENTER);
|
|
206
100
|
await delay(INPUT_DELAY_MS);
|
|
207
101
|
globalExpect(onContinue).toHaveBeenCalledTimes(1);
|
|
208
102
|
});
|
|
209
|
-
it("should
|
|
103
|
+
it("should call onBack when Escape pressed in customize view", async () => {
|
|
210
104
|
const onBack = vi.fn();
|
|
211
|
-
const {
|
|
105
|
+
const { stdin, unmount } = renderStepSources({ onBack });
|
|
212
106
|
cleanup = unmount;
|
|
213
107
|
await delay(RENDER_DELAY_MS);
|
|
214
|
-
stdin.write(ARROW_DOWN);
|
|
215
|
-
await delay(INPUT_DELAY_MS);
|
|
216
|
-
stdin.write(ENTER);
|
|
217
|
-
await delay(INPUT_DELAY_MS);
|
|
218
|
-
globalExpect(lastFrame()).toContain("Customize skill sources");
|
|
219
108
|
stdin.write(ESCAPE);
|
|
220
109
|
await delay(INPUT_DELAY_MS);
|
|
221
|
-
|
|
222
|
-
globalExpect(output).toContain("Use all recommended skills (verified)");
|
|
223
|
-
globalExpect(output).toContain("Customize skill sources");
|
|
224
|
-
globalExpect(onBack).not.toHaveBeenCalled();
|
|
110
|
+
globalExpect(onBack).toHaveBeenCalledTimes(1);
|
|
225
111
|
});
|
|
226
112
|
it("should show ViewTitle in customize view", async () => {
|
|
227
|
-
const { lastFrame,
|
|
113
|
+
const { lastFrame, unmount } = renderStepSources();
|
|
228
114
|
cleanup = unmount;
|
|
229
115
|
await delay(RENDER_DELAY_MS);
|
|
230
|
-
stdin.write(ARROW_DOWN);
|
|
231
|
-
await delay(INPUT_DELAY_MS);
|
|
232
|
-
stdin.write(ENTER);
|
|
233
|
-
await delay(INPUT_DELAY_MS);
|
|
234
116
|
const output = lastFrame();
|
|
235
117
|
globalExpect(output).toContain("Customize skill sources");
|
|
236
118
|
});
|
|
@@ -243,8 +125,7 @@ describe("StepSources component", () => {
|
|
|
243
125
|
const { lastFrame, unmount } = renderStepSources();
|
|
244
126
|
cleanup = unmount;
|
|
245
127
|
const output = lastFrame();
|
|
246
|
-
globalExpect(output).toContain("
|
|
247
|
-
globalExpect(output).toContain("technologies");
|
|
128
|
+
globalExpect(output).toContain("Customize skill sources");
|
|
248
129
|
});
|
|
249
130
|
it("should handle single technology", () => {
|
|
250
131
|
useWizardStore.setState({
|
|
@@ -257,10 +138,10 @@ describe("StepSources component", () => {
|
|
|
257
138
|
const { lastFrame, unmount } = renderStepSources();
|
|
258
139
|
cleanup = unmount;
|
|
259
140
|
const output = lastFrame();
|
|
260
|
-
globalExpect(output).toContain("
|
|
261
|
-
globalExpect(output).toContain("
|
|
141
|
+
globalExpect(output).toContain("Customize skill sources");
|
|
142
|
+
globalExpect(output).toContain("React");
|
|
262
143
|
});
|
|
263
|
-
it("should handle multiple Enter presses
|
|
144
|
+
it("should handle multiple Enter presses in customize view", async () => {
|
|
264
145
|
const onContinue = vi.fn();
|
|
265
146
|
const { stdin, unmount } = renderStepSources({ onContinue });
|
|
266
147
|
cleanup = unmount;
|
|
@@ -272,30 +153,5 @@ describe("StepSources component", () => {
|
|
|
272
153
|
globalExpect(onContinue).toHaveBeenCalledTimes(2);
|
|
273
154
|
});
|
|
274
155
|
});
|
|
275
|
-
describe("store integration", () => {
|
|
276
|
-
it("should set customizeSources when switching to customize view", async () => {
|
|
277
|
-
const { stdin, unmount } = renderStepSources();
|
|
278
|
-
cleanup = unmount;
|
|
279
|
-
await delay(RENDER_DELAY_MS);
|
|
280
|
-
stdin.write(ARROW_DOWN);
|
|
281
|
-
await delay(INPUT_DELAY_MS);
|
|
282
|
-
stdin.write(ENTER);
|
|
283
|
-
await delay(INPUT_DELAY_MS);
|
|
284
|
-
globalExpect(useWizardStore.getState().customizeSources).toBe(true);
|
|
285
|
-
});
|
|
286
|
-
it("should reset customizeSources when escaping from customize view", async () => {
|
|
287
|
-
const { stdin, unmount } = renderStepSources();
|
|
288
|
-
cleanup = unmount;
|
|
289
|
-
await delay(RENDER_DELAY_MS);
|
|
290
|
-
stdin.write(ARROW_DOWN);
|
|
291
|
-
await delay(INPUT_DELAY_MS);
|
|
292
|
-
stdin.write(ENTER);
|
|
293
|
-
await delay(INPUT_DELAY_MS);
|
|
294
|
-
globalExpect(useWizardStore.getState().customizeSources).toBe(true);
|
|
295
|
-
stdin.write(ESCAPE);
|
|
296
|
-
await delay(INPUT_DELAY_MS);
|
|
297
|
-
globalExpect(useWizardStore.getState().customizeSources).toBe(false);
|
|
298
|
-
});
|
|
299
|
-
});
|
|
300
156
|
});
|
|
301
157
|
//# sourceMappingURL=step-sources.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/cli/components/wizard/step-sources.test.tsx"],"sourcesContent":["import { render } from \"ink-testing-library\";\nimport { describe, expect, it, afterEach, beforeEach, vi } from \"vitest\";\nimport { StepSources, type StepSourcesProps } from \"./step-sources\";\nimport { useWizardStore } from \"../../stores/wizard-store\";\nimport { initializeMatrix } from \"../../lib/matrix/matrix-provider\";\nimport {\n ENTER,\n ESCAPE,\n ARROW_UP,\n ARROW_DOWN,\n RENDER_DELAY_MS,\n INPUT_DELAY_MS,\n delay,\n} from \"../../lib/__tests__/test-constants\";\nimport { DEFAULT_BRANDING, UI_SYMBOLS } from \"../../consts\";\nimport { WEB_PAIR_MATRIX } from \"../../lib/__tests__/mock-data/mock-matrices\";\n\nconst mockMatrix = WEB_PAIR_MATRIX;\n\nconst defaultProps: StepSourcesProps = {\n onContinue: vi.fn(),\n onBack: vi.fn(),\n};\n\nconst renderStepSources = (props: Partial<StepSourcesProps> = {}) => {\n return render(<StepSources {...defaultProps} {...props} />);\n};\n\ndescribe(\"StepSources component\", () => {\n let cleanup: (() => void) | undefined;\n\n beforeEach(() => {\n initializeMatrix(mockMatrix);\n // Set up some selected technologies so the step has data to display\n useWizardStore.setState({\n domainSelections: {\n web: {\n \"web-framework\": [\"web-framework-react\"],\n \"web-client-state\": [\"web-state-zustand\"],\n },\n },\n });\n });\n\n afterEach(() => {\n cleanup?.();\n cleanup = undefined;\n });\n\n describe(\"choice view rendering\", () => {\n it(\"should render technology count\", () => {\n const { lastFrame, unmount } = renderStepSources();\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"2\");\n expect(output).toContain(\"technologies\");\n });\n\n it(\"should render 'Use all recommended' option\", () => {\n const { lastFrame, unmount } = renderStepSources();\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Use all recommended skills (verified)\");\n });\n\n it(\"should render 'Customize skill sources' option\", () => {\n const { lastFrame, unmount } = renderStepSources();\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Customize skill sources\");\n });\n\n it(\"should render verification description\", () => {\n const { lastFrame, unmount } = renderStepSources();\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"fastest option\");\n expect(output).toContain(\"verified\");\n expect(output).toContain(DEFAULT_BRANDING.NAME);\n });\n\n it(\"should render customize description\", () => {\n const { lastFrame, unmount } = renderStepSources();\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"Choose alternative skills for each technology\");\n });\n\n it(\"should show recommended as default selected\", () => {\n const { lastFrame, unmount } = renderStepSources();\n cleanup = unmount;\n\n const output = lastFrame();\n // The chevron indicator should be on the recommended option\n expect(output).toContain(UI_SYMBOLS.CHEVRON);\n expect(output).toContain(\"Use all recommended\");\n });\n });\n\n describe(\"choice view keyboard navigation\", () => {\n it(\"should toggle options with arrow up\", async () => {\n const { lastFrame, stdin, unmount } = renderStepSources();\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n\n // Initially recommended is selected\n let output = lastFrame();\n expect(output).toContain(\"Use all recommended\");\n\n // Press up to switch to customize\n stdin.write(ARROW_UP);\n await delay(INPUT_DELAY_MS);\n\n output = lastFrame();\n // Both options should still be present\n expect(output).toContain(\"Customize skill sources\");\n });\n\n it(\"should toggle options with arrow down\", async () => {\n const { stdin, unmount } = renderStepSources();\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n stdin.write(ARROW_DOWN);\n await delay(INPUT_DELAY_MS);\n\n // Should have toggled\n });\n\n it(\"should call onContinue when Enter pressed on 'Use all recommended'\", async () => {\n const onContinue = vi.fn();\n const { stdin, unmount } = renderStepSources({ onContinue });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n stdin.write(ENTER);\n await delay(INPUT_DELAY_MS);\n\n expect(onContinue).toHaveBeenCalledTimes(1);\n });\n\n it(\"should call onBack when Escape pressed\", async () => {\n const onBack = vi.fn();\n const { stdin, unmount } = renderStepSources({ onBack });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n stdin.write(ESCAPE);\n await delay(INPUT_DELAY_MS);\n\n expect(onBack).toHaveBeenCalledTimes(1);\n });\n\n it(\"should switch to customize view when Enter pressed on 'Customize'\", async () => {\n const { lastFrame, stdin, unmount } = renderStepSources();\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n\n // Move to customize option\n stdin.write(ARROW_DOWN);\n await delay(INPUT_DELAY_MS);\n\n // Press Enter to switch to customize view\n stdin.write(ENTER);\n await delay(INPUT_DELAY_MS);\n\n const output = lastFrame();\n // Should show the grid view with skill names\n expect(output).toContain(\"Customize skill sources\");\n // Should show the selected technologies (title-cased display names)\n expect(output).toContain(\"React\");\n expect(output).toContain(\"Zustand\");\n });\n });\n\n describe(\"customize view\", () => {\n it(\"should show source grid with selected technologies\", async () => {\n const { lastFrame, stdin, unmount } = renderStepSources();\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n\n // Switch to customize view\n stdin.write(ARROW_DOWN);\n await delay(INPUT_DELAY_MS);\n stdin.write(ENTER);\n await delay(INPUT_DELAY_MS);\n\n const output = lastFrame();\n expect(output).toContain(\"React\");\n expect(output).toContain(\"Zustand\");\n expect(output).toContain(\"Agents Inc\");\n });\n\n it(\"should call onContinue when Enter pressed in customize view\", async () => {\n const onContinue = vi.fn();\n const { stdin, unmount } = renderStepSources({ onContinue });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n\n // Switch to customize view\n stdin.write(ARROW_DOWN);\n await delay(INPUT_DELAY_MS);\n stdin.write(ENTER);\n await delay(INPUT_DELAY_MS);\n\n // Press Enter to continue\n stdin.write(ENTER);\n await delay(INPUT_DELAY_MS);\n\n expect(onContinue).toHaveBeenCalledTimes(1);\n });\n\n it(\"should go back to choice view when Escape pressed in customize view\", async () => {\n const onBack = vi.fn();\n const { lastFrame, stdin, unmount } = renderStepSources({ onBack });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n\n // Switch to customize view\n stdin.write(ARROW_DOWN);\n await delay(INPUT_DELAY_MS);\n stdin.write(ENTER);\n await delay(INPUT_DELAY_MS);\n\n // Verify we're in customize view\n expect(lastFrame()).toContain(\"Customize skill sources\");\n\n // Press Escape to go back to choice view\n stdin.write(ESCAPE);\n await delay(INPUT_DELAY_MS);\n\n const output = lastFrame();\n // Should be back at choice view with both options\n expect(output).toContain(\"Use all recommended skills (verified)\");\n expect(output).toContain(\"Customize skill sources\");\n\n // onBack should NOT have been called (escape returns to choice, not to build)\n expect(onBack).not.toHaveBeenCalled();\n });\n\n it(\"should show ViewTitle in customize view\", async () => {\n const { lastFrame, stdin, unmount } = renderStepSources();\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n\n // Switch to customize view\n stdin.write(ARROW_DOWN);\n await delay(INPUT_DELAY_MS);\n stdin.write(ENTER);\n await delay(INPUT_DELAY_MS);\n\n const output = lastFrame();\n expect(output).toContain(\"Customize skill sources\");\n });\n });\n\n describe(\"edge cases\", () => {\n it(\"should handle zero technologies\", () => {\n useWizardStore.setState({\n domainSelections: {},\n });\n\n const { lastFrame, unmount } = renderStepSources();\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"0\");\n expect(output).toContain(\"technologies\");\n });\n\n it(\"should handle single technology\", () => {\n useWizardStore.setState({\n domainSelections: {\n web: {\n \"web-framework\": [\"web-framework-react\"],\n },\n },\n });\n\n const { lastFrame, unmount } = renderStepSources();\n cleanup = unmount;\n\n const output = lastFrame();\n expect(output).toContain(\"1\");\n expect(output).toContain(\"technologies\");\n });\n\n it(\"should handle multiple Enter presses on recommended\", async () => {\n const onContinue = vi.fn();\n const { stdin, unmount } = renderStepSources({ onContinue });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n stdin.write(ENTER);\n await delay(INPUT_DELAY_MS);\n stdin.write(ENTER);\n await delay(INPUT_DELAY_MS);\n\n expect(onContinue).toHaveBeenCalledTimes(2);\n });\n });\n\n describe(\"store integration\", () => {\n it(\"should set customizeSources when switching to customize view\", async () => {\n const { stdin, unmount } = renderStepSources();\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n\n // Switch to customize\n stdin.write(ARROW_DOWN);\n await delay(INPUT_DELAY_MS);\n stdin.write(ENTER);\n await delay(INPUT_DELAY_MS);\n\n expect(useWizardStore.getState().customizeSources).toBe(true);\n });\n\n it(\"should reset customizeSources when escaping from customize view\", async () => {\n const { stdin, unmount } = renderStepSources();\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n\n // Switch to customize\n stdin.write(ARROW_DOWN);\n await delay(INPUT_DELAY_MS);\n stdin.write(ENTER);\n await delay(INPUT_DELAY_MS);\n\n expect(useWizardStore.getState().customizeSources).toBe(true);\n\n // Escape back\n stdin.write(ESCAPE);\n await delay(INPUT_DELAY_MS);\n\n expect(useWizardStore.getState().customizeSources).toBe(false);\n });\n });\n});\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/cli/components/wizard/step-sources.test.tsx"],"sourcesContent":["import { render } from \"ink-testing-library\";\nimport { describe, expect, it, afterEach, beforeEach, vi } from \"vitest\";\nimport { StepSources, type StepSourcesProps } from \"./step-sources\";\nimport { useWizardStore } from \"../../stores/wizard-store\";\nimport { initializeMatrix } from \"../../lib/matrix/matrix-provider\";\nimport {\n ENTER,\n ESCAPE,\n RENDER_DELAY_MS,\n INPUT_DELAY_MS,\n delay,\n} from \"../../lib/__tests__/test-constants\";\nimport { WEB_PAIR_MATRIX } from \"../../lib/__tests__/mock-data/mock-matrices\";\n\nconst mockMatrix = WEB_PAIR_MATRIX;\n\nconst defaultProps: StepSourcesProps = {\n onContinue: vi.fn(),\n onBack: vi.fn(),\n};\n\nconst renderStepSources = (props: Partial<StepSourcesProps> = {}) => {\n return render(<StepSources {...defaultProps} {...props} />);\n};\n\ndescribe(\"StepSources component\", () => {\n let cleanup: (() => void) | undefined;\n\n beforeEach(() => {\n initializeMatrix(mockMatrix);\n // Set up some selected technologies so the step has data to display\n useWizardStore.setState({\n domainSelections: {\n web: {\n \"web-framework\": [\"web-framework-react\"],\n \"web-client-state\": [\"web-state-zustand\"],\n },\n },\n });\n });\n\n afterEach(() => {\n cleanup?.();\n cleanup = undefined;\n });\n\n describe(\"customize view\", () => {\n it(\"should show source grid with selected technologies\", async () => {\n const { lastFrame, unmount } = renderStepSources();\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n\n const output = lastFrame();\n expect(output).toContain(\"React\");\n expect(output).toContain(\"Zustand\");\n expect(output).toContain(\"Agents Inc\");\n });\n\n it(\"should call onContinue when Enter pressed in customize view\", async () => {\n const onContinue = vi.fn();\n const { stdin, unmount } = renderStepSources({ onContinue });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n\n // Press Enter to continue (starts directly in customize view)\n stdin.write(ENTER);\n await delay(INPUT_DELAY_MS);\n\n expect(onContinue).toHaveBeenCalledTimes(1);\n });\n\n it(\"should call onBack when Escape pressed in customize view\", async () => {\n const onBack = vi.fn();\n const { stdin, unmount } = renderStepSources({ onBack });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n\n // Press Escape (starts directly in customize view, goes back)\n stdin.write(ESCAPE);\n await delay(INPUT_DELAY_MS);\n\n expect(onBack).toHaveBeenCalledTimes(1);\n });\n\n it(\"should show ViewTitle in customize view\", async () => {\n const { lastFrame, unmount } = renderStepSources();\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n\n const output = lastFrame();\n expect(output).toContain(\"Customize skill sources\");\n });\n });\n\n describe(\"edge cases\", () => {\n it(\"should handle zero technologies\", () => {\n useWizardStore.setState({\n domainSelections: {},\n });\n\n const { lastFrame, unmount } = renderStepSources();\n cleanup = unmount;\n\n const output = lastFrame();\n // Should still render the customize view title\n expect(output).toContain(\"Customize skill sources\");\n });\n\n it(\"should handle single technology\", () => {\n useWizardStore.setState({\n domainSelections: {\n web: {\n \"web-framework\": [\"web-framework-react\"],\n },\n },\n });\n\n const { lastFrame, unmount } = renderStepSources();\n cleanup = unmount;\n\n const output = lastFrame();\n // Should show the single technology in the source grid\n expect(output).toContain(\"Customize skill sources\");\n expect(output).toContain(\"React\");\n });\n\n it(\"should handle multiple Enter presses in customize view\", async () => {\n const onContinue = vi.fn();\n const { stdin, unmount } = renderStepSources({ onContinue });\n cleanup = unmount;\n\n await delay(RENDER_DELAY_MS);\n stdin.write(ENTER);\n await delay(INPUT_DELAY_MS);\n stdin.write(ENTER);\n await delay(INPUT_DELAY_MS);\n\n expect(onContinue).toHaveBeenCalledTimes(2);\n });\n });\n});\n"],"mappings":"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","names":[]}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
StepStack
|
|
4
|
-
} from "../../chunk-
|
|
4
|
+
} from "../../chunk-TKNPMYKJ.js";
|
|
5
5
|
import "../../chunk-2YFC7IJ6.js";
|
|
6
6
|
import "../../chunk-K77I4XGL.js";
|
|
7
7
|
import "../../chunk-MWGDG4QN.js";
|
|
8
|
-
import "../../chunk-
|
|
8
|
+
import "../../chunk-MYSIW4HY.js";
|
|
9
9
|
import "../../chunk-LVNNP7T4.js";
|
|
10
|
-
import "../../chunk-
|
|
10
|
+
import "../../chunk-CBXMOWQY.js";
|
|
11
11
|
import "../../chunk-XMLCXRTS.js";
|
|
12
|
-
import "../../chunk-
|
|
12
|
+
import "../../chunk-JNQKCZA3.js";
|
|
13
13
|
import "../../chunk-AUNBGZS4.js";
|
|
14
14
|
import "../../chunk-EWGFIBBU.js";
|
|
15
15
|
import "../../chunk-6W4H2K3Z.js";
|
|
@@ -27,15 +27,15 @@ import {
|
|
|
27
27
|
} from "../../chunk-XY3XDVMI.js";
|
|
28
28
|
import {
|
|
29
29
|
StepStack
|
|
30
|
-
} from "../../chunk-
|
|
30
|
+
} from "../../chunk-TKNPMYKJ.js";
|
|
31
31
|
import "../../chunk-2YFC7IJ6.js";
|
|
32
32
|
import "../../chunk-K77I4XGL.js";
|
|
33
33
|
import "../../chunk-MWGDG4QN.js";
|
|
34
|
-
import "../../chunk-
|
|
34
|
+
import "../../chunk-MYSIW4HY.js";
|
|
35
35
|
import "../../chunk-LVNNP7T4.js";
|
|
36
|
-
import "../../chunk-
|
|
36
|
+
import "../../chunk-CBXMOWQY.js";
|
|
37
37
|
import "../../chunk-XMLCXRTS.js";
|
|
38
|
-
import "../../chunk-
|
|
38
|
+
import "../../chunk-JNQKCZA3.js";
|
|
39
39
|
import "../../chunk-AUNBGZS4.js";
|
|
40
40
|
import {
|
|
41
41
|
useWizardStore
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
WizardLayout
|
|
4
|
-
} from "../../chunk-
|
|
4
|
+
} from "../../chunk-OZ4DXMRF.js";
|
|
5
5
|
import "../../chunk-SXGBPQY6.js";
|
|
6
|
-
import "../../chunk-
|
|
6
|
+
import "../../chunk-V36FRPAU.js";
|
|
7
7
|
import "../../chunk-2PZ7LBFT.js";
|
|
8
8
|
import "../../chunk-EWGFIBBU.js";
|
|
9
9
|
import "../../chunk-CBYRFAUN.js";
|
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
Wizard
|
|
4
|
-
} from "../../chunk-
|
|
5
|
-
import "../../chunk-
|
|
4
|
+
} from "../../chunk-A5TTDOP4.js";
|
|
5
|
+
import "../../chunk-OZ4DXMRF.js";
|
|
6
6
|
import "../../chunk-SXGBPQY6.js";
|
|
7
|
-
import "../../chunk-
|
|
8
|
-
import "../../chunk-
|
|
9
|
-
import "../../chunk-
|
|
10
|
-
import "../../chunk-
|
|
7
|
+
import "../../chunk-746DCNGM.js";
|
|
8
|
+
import "../../chunk-7X4F4HCO.js";
|
|
9
|
+
import "../../chunk-V36FRPAU.js";
|
|
10
|
+
import "../../chunk-TKNPMYKJ.js";
|
|
11
11
|
import "../../chunk-2YFC7IJ6.js";
|
|
12
|
-
import "../../chunk-
|
|
13
|
-
import "../../chunk-
|
|
12
|
+
import "../../chunk-JQSJH72Q.js";
|
|
13
|
+
import "../../chunk-4EPFKZB5.js";
|
|
14
14
|
import "../../chunk-K77I4XGL.js";
|
|
15
|
-
import "../../chunk-
|
|
15
|
+
import "../../chunk-RKZFLLER.js";
|
|
16
16
|
import "../../chunk-MWGDG4QN.js";
|
|
17
|
-
import "../../chunk-
|
|
17
|
+
import "../../chunk-7Q6RMYR3.js";
|
|
18
18
|
import "../../chunk-7SOPVGDV.js";
|
|
19
19
|
import "../../chunk-UFKDY45I.js";
|
|
20
|
-
import "../../chunk-
|
|
20
|
+
import "../../chunk-GHBZ7NUI.js";
|
|
21
21
|
import "../../chunk-P2FHS5IS.js";
|
|
22
|
-
import "../../chunk-
|
|
22
|
+
import "../../chunk-MYSIW4HY.js";
|
|
23
23
|
import "../../chunk-LVNNP7T4.js";
|
|
24
|
-
import "../../chunk-
|
|
24
|
+
import "../../chunk-CBXMOWQY.js";
|
|
25
25
|
import "../../chunk-XMLCXRTS.js";
|
|
26
|
-
import "../../chunk-
|
|
26
|
+
import "../../chunk-JNQKCZA3.js";
|
|
27
27
|
import "../../chunk-AUNBGZS4.js";
|
|
28
28
|
import "../../chunk-2PZ7LBFT.js";
|
|
29
29
|
import "../../chunk-WF5PMBIR.js";
|
package/dist/hooks/init.js
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
showDashboard
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
6
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-SASGCSBZ.js";
|
|
5
|
+
import "../chunk-A5TTDOP4.js";
|
|
6
|
+
import "../chunk-OZ4DXMRF.js";
|
|
7
7
|
import "../chunk-SXGBPQY6.js";
|
|
8
|
-
import "../chunk-
|
|
9
|
-
import "../chunk-
|
|
10
|
-
import "../chunk-
|
|
11
|
-
import "../chunk-
|
|
8
|
+
import "../chunk-746DCNGM.js";
|
|
9
|
+
import "../chunk-7X4F4HCO.js";
|
|
10
|
+
import "../chunk-V36FRPAU.js";
|
|
11
|
+
import "../chunk-TKNPMYKJ.js";
|
|
12
12
|
import "../chunk-2YFC7IJ6.js";
|
|
13
|
-
import "../chunk-
|
|
14
|
-
import "../chunk-
|
|
13
|
+
import "../chunk-JQSJH72Q.js";
|
|
14
|
+
import "../chunk-4EPFKZB5.js";
|
|
15
15
|
import "../chunk-K77I4XGL.js";
|
|
16
|
-
import "../chunk-
|
|
16
|
+
import "../chunk-RKZFLLER.js";
|
|
17
17
|
import "../chunk-MWGDG4QN.js";
|
|
18
|
-
import "../chunk-
|
|
18
|
+
import "../chunk-7Q6RMYR3.js";
|
|
19
19
|
import "../chunk-7SOPVGDV.js";
|
|
20
20
|
import "../chunk-UFKDY45I.js";
|
|
21
|
-
import "../chunk-
|
|
21
|
+
import "../chunk-GHBZ7NUI.js";
|
|
22
22
|
import "../chunk-P2FHS5IS.js";
|
|
23
|
-
import "../chunk-
|
|
23
|
+
import "../chunk-MYSIW4HY.js";
|
|
24
24
|
import "../chunk-LVNNP7T4.js";
|
|
25
|
-
import "../chunk-
|
|
25
|
+
import "../chunk-CBXMOWQY.js";
|
|
26
26
|
import "../chunk-XMLCXRTS.js";
|
|
27
|
-
import "../chunk-
|
|
27
|
+
import "../chunk-JNQKCZA3.js";
|
|
28
28
|
import "../chunk-AUNBGZS4.js";
|
|
29
29
|
import "../chunk-2PZ7LBFT.js";
|
|
30
30
|
import "../chunk-WF5PMBIR.js";
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/components/wizard/wizard-layout.tsx","../src/cli/components/hooks/use-terminal-dimensions.ts"],"sourcesContent":["import { Box, Static, Text } from \"ink\";\nimport React, { Fragment } from \"react\";\nimport { CLI_COLORS } from \"../../consts.js\";\nimport type { StartupMessage } from \"../../utils/logger.js\";\nimport { FEATURE_FLAGS } from \"../../lib/feature-flags.js\";\nimport { useWizardStore } from \"../../stores/wizard-store.js\";\nimport { useTerminalDimensions } from \"../hooks/use-terminal-dimensions.js\";\nimport { HelpModal } from \"./help-modal.js\";\nimport {\n HOTKEY_HELP,\n HOTKEY_SCOPE,\n HOTKEY_SETTINGS,\n HOTKEY_TOGGLE_LABELS,\n KEY_LABEL_ARROWS_VERT,\n KEY_LABEL_ENTER,\n KEY_LABEL_ESC,\n KEY_LABEL_SPACE,\n} from \"./hotkeys.js\";\nimport { WIZARD_STEPS, WizardTabs } from \"./wizard-tabs.js\";\n\ntype KeyHintProps = {\n isVisible?: boolean;\n isActive?: boolean;\n label: string;\n values: string[];\n};\n\nconst DefinitionItem: React.FC<KeyHintProps> = ({\n isVisible = true,\n isActive = false,\n label,\n values,\n}) => {\n if (!isVisible) {\n return null;\n }\n\n return (\n <Text>\n {values.map((value) => (\n <Fragment key={value}>\n <Text\n backgroundColor=\"black\"\n color={isActive ? CLI_COLORS.PRIMARY : CLI_COLORS.UNFOCUSED}\n >\n {\" \"}\n {value}{\" \"}\n </Text>{\" \"}\n </Fragment>\n ))}\n <Text color={isActive ? CLI_COLORS.PRIMARY : undefined}>{label}</Text>\n </Text>\n );\n};\n\nconst HOT_KEYS: { label: string; values: string[] }[] = [\n { label: \"navigate\", values: [KEY_LABEL_ARROWS_VERT] },\n { label: \"select\", values: [KEY_LABEL_SPACE] },\n { label: \"continue\", values: [KEY_LABEL_ENTER] },\n { label: \"back\", values: [KEY_LABEL_ESC] },\n];\n\nconst WizardFooter = () => {\n return (\n <Box\n columnGap={2}\n borderTop\n borderRight={false}\n borderBottom\n borderLeft={false}\n borderColor=\"blackBright\"\n borderStyle=\"single\"\n paddingLeft={1}\n paddingRight={1}\n >\n {HOT_KEYS.map((hotkey) => (\n <DefinitionItem {...hotkey} key={hotkey.label} />\n ))}\n </Box>\n );\n};\n\ntype WizardLayoutProps = {\n version?: string;\n logo?: string;\n startupMessages?: StartupMessage[];\n children: React.ReactNode;\n};\n\nexport const WizardLayout: React.FC<WizardLayoutProps> = ({\n version,\n logo,\n startupMessages,\n children,\n}) => {\n const store = useWizardStore();\n const { completedSteps, skippedSteps } = store.getStepProgress();\n const { rows: terminalHeight } = useTerminalDimensions();\n\n return (\n <>\n {startupMessages && startupMessages.length > 0 && (\n <Static items={startupMessages}>\n {(msg, index) => (\n <Box key={index}>\n <Text\n color={msg.level === \"warn\" ? \"yellow\" : msg.level === \"error\" ? \"red\" : undefined}\n >\n {msg.level === \"warn\" ? ` Warning: ${msg.text}` : msg.text}\n </Text>\n </Box>\n )}\n </Static>\n )}\n <Box flexDirection=\"column\" paddingX={1} height={terminalHeight}>\n {logo && store.step === \"stack\" && (\n <Box flexDirection=\"row\" marginTop={1} columnGap={1}>\n <Text>{logo}</Text>\n </Box>\n )}\n <WizardTabs\n steps={WIZARD_STEPS}\n currentStep={store.step}\n completedSteps={completedSteps}\n skippedSteps={skippedSteps}\n version={version}\n />\n {store.showHelp ? (\n <HelpModal currentStep={store.step} />\n ) : (\n <>\n <Box flexDirection=\"column\" flexGrow={1} flexBasis={0} marginTop={1}>\n {children}\n </Box>\n <Box paddingX={1} columnGap={2} marginTop={2}>\n <DefinitionItem\n label=\"Labels\"\n values={[HOTKEY_TOGGLE_LABELS.label]}\n isVisible={store.step === \"build\"}\n isActive={store.showLabels}\n />\n <DefinitionItem\n label=\"Scope\"\n values={[HOTKEY_SCOPE.label]}\n isVisible={store.step === \"build\"}\n />\n <DefinitionItem\n label=\"Settings\"\n values={[HOTKEY_SETTINGS.label]}\n isVisible={store.step === \"sources\" && FEATURE_FLAGS.SOURCE_SEARCH}\n isActive={store.showSettings}\n />\n <DefinitionItem label=\"Help\" values={[HOTKEY_HELP.label]} />\n </Box>\n <WizardFooter />\n </>\n )}\n </Box>\n </>\n );\n};\n","import { useState, useEffect } from \"react\";\nimport { useStdout } from \"ink\";\n\nconst DEFAULT_COLUMNS = 80;\nconst DEFAULT_ROWS = 24;\n\nexport type TerminalDimensions = {\n /** Terminal width in columns */\n columns: number;\n /** Terminal height in rows */\n rows: number;\n};\n\n/**\n * Tracks terminal dimensions reactively. Re-renders on resize.\n *\n * Falls back to DEFAULT_COLUMNS x DEFAULT_ROWS when stdout is not a TTY\n * (e.g., piped output, CI environments, tests).\n */\nexport function useTerminalDimensions(): TerminalDimensions {\n const { stdout } = useStdout();\n\n const [dimensions, setDimensions] = useState<TerminalDimensions>(() => ({\n columns: stdout.columns || DEFAULT_COLUMNS,\n rows: stdout.rows || DEFAULT_ROWS,\n }));\n\n useEffect(() => {\n const handleResize = () => {\n setDimensions({\n columns: stdout.columns || DEFAULT_COLUMNS,\n rows: stdout.rows || DEFAULT_ROWS,\n });\n };\n\n stdout.on(\"resize\", handleResize);\n return () => {\n stdout.off(\"resize\", handleResize);\n };\n }, [stdout]);\n\n return dimensions;\n}\n"],"mappings":"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","names":["Fragment"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/components/wizard/step-sources.tsx"],"sourcesContent":["import { Box, Text, useInput } from \"ink\";\nimport React, { useCallback, useState } from \"react\";\nimport { CLI_COLORS, DEFAULT_BRANDING } from \"../../consts.js\";\nimport { FEATURE_FLAGS } from \"../../lib/feature-flags.js\";\nimport { resolveAllSources } from \"../../lib/configuration/index.js\";\nimport { searchExtraSources } from \"../../lib/loading/multi-source-loader.js\";\nimport { useWizardStore } from \"../../stores/wizard-store.js\";\nimport type { BoundSkillCandidate, SkillAlias, SkillId } from \"../../types/index.js\";\nimport { useMeasuredHeight } from \"../hooks/use-measured-height.js\";\nimport {\n HOTKEY_SET_ALL_LOCAL,\n HOTKEY_SET_ALL_PLUGIN,\n KEY_LABEL_ENTER,\n KEY_LABEL_ESC,\n isHotkey,\n} from \"./hotkeys.js\";\nimport { SelectionCard } from \"./selection-card.js\";\nimport { SourceGrid } from \"./source-grid.js\";\nimport { ViewTitle } from \"./view-title.js\";\n\nexport type StepSourcesProps = {\n projectDir?: string;\n onContinue: () => void;\n onBack: () => void;\n};\n\ntype SourcesView = \"choice\" | \"customize\";\n\nexport const StepSources: React.FC<StepSourcesProps> = ({ projectDir, onContinue, onBack }) => {\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 (isHotkey(input, HOTKEY_SET_ALL_LOCAL)) {\n store.setAllSourcesLocal();\n }\n if (isHotkey(input, HOTKEY_SET_ALL_PLUGIN)) {\n store.setAllSourcesPlugin();\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();\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={FEATURE_FLAGS.SOURCE_SEARCH ? handleSearch : undefined}\n onBind={FEATURE_FLAGS.SOURCE_SEARCH ? handleBind : undefined}\n onSearchStateChange={FEATURE_FLAGS.SOURCE_SEARCH ? handleSearchStateChange : undefined}\n />\n </Box>\n <Box marginTop={1}>\n <Text dimColor>\n {HOTKEY_SET_ALL_LOCAL.label} set all local {HOTKEY_SET_ALL_PLUGIN.label} set all plugin{\" \"}\n {KEY_LABEL_ENTER} continue {KEY_LABEL_ESC} back\n </Text>\n </Box>\n </Box>\n );\n }\n\n const selectedTechnologies = store.getAllSelectedTechnologies();\n const rows = store.buildSourceRows();\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 <SelectionCard\n label={\n hasLocalSkills ? \"Use installed skill sources\" : \"Use all recommended skills (verified)\"\n }\n description={\n hasLocalSkills\n ? \"Keep your current local and public skill selections.\"\n : [\n `This is the fastest option. All skills are verified and maintained by ${DEFAULT_BRANDING.NAME}`,\n ]\n }\n isFocused={isRecommendedSelected}\n marginBottom={1}\n />\n\n <SelectionCard\n label=\"Customize skill sources\"\n description=\"Choose alternative skills for each technology\"\n isFocused={!isRecommendedSelected}\n />\n </Box>\n );\n};\n"],"mappings":"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","names":["rows"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/components/wizard/view-title.tsx"],"sourcesContent":["import { Box, Text } from \"ink\";\nimport React from \"react\";\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 bold color={CLI_COLORS.WARNING}>\n {paddingHalf}\n {children}\n {paddingHalf}\n </Text>\n <Text backgroundColor={CLI_COLORS.WARNING}>{padding}</Text>\n </Box>\n );\n};\n"],"mappings":";;;;;;;;;AAAA;AAAA,SAAS,KAAK,YAAY;AAgBpB,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,MAAI,MAAC,OAAO,WAAW,SAC1B;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/lib/feature-flags.ts"],"sourcesContent":["export const FEATURE_FLAGS = {\n // Controls whether the search pill appears in the source grid (step-sources)\n SOURCE_SEARCH: false,\n} as const;\n"],"mappings":";;;;;;AAAA;AAAO,IAAM,gBAAgB;AAAA;AAAA,EAE3B,eAAe;AACjB;","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, useMemo, useRef } from \"react\";\n\nimport { Box, Text } from \"ink\";\n\nimport { CLI_COLORS } from \"../../consts.js\";\nimport { getSkillById } from \"../../lib/matrix/matrix-provider.js\";\nimport type { Category, OptionState, SkillId } from \"../../types/index.js\";\nimport { isSectionLocked, useCategoryGridInput } from \"../hooks/use-category-grid-input.js\";\nimport { useFocusedListItem } from \"../hooks/use-focused-list-item.js\";\nimport { useSectionScroll } from \"../hooks/use-section-scroll.js\";\n\nexport type CategoryOption = {\n id: SkillId;\n state: OptionState;\n selected: boolean;\n local?: boolean;\n installed?: boolean;\n scope?: \"project\" | \"global\";\n /** True when selected but has unmet dependency requirements (shown dimmed) */\n hasUnmetRequirements?: boolean;\n /** Explains unmet requirements (shown in label when D pressed) */\n unmetRequirementsReason?: string;\n};\n\nexport type CategoryRow = {\n id: Category;\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 onToggle: (categoryId: Category, 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 /** Optional callback fired with the resolved SkillId of the focused cell */\n onFocusedSkillChange?: (skillId: SkillId | null) => void;\n};\n\nconst SYMBOL_REQUIRED = \"*\";\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): string | null => {\n if (option.selected && option.hasUnmetRequirements && option.unmetRequirementsReason) {\n return `(${option.unmetRequirementsReason})`;\n }\n if (option.selected) return null;\n if (option.state.status === \"incompatible\") return \"(incompatible)\";\n if (option.state.status === \"recommended\") return \"(recommended)\";\n if (option.state.status === \"discouraged\") return \"(discouraged)\";\n return null;\n};\n\nconst SkillTag: React.FC<SkillTagProps> = ({ option, isFocused, isLocked, showLabels }) => {\n const getTextColor = (): string => {\n if (option.selected) return CLI_COLORS.PRIMARY;\n if (option.state.status === \"incompatible\") return CLI_COLORS.ERROR;\n if (option.state.status === \"recommended\") return CLI_COLORS.UNFOCUSED;\n if (option.state.status === \"discouraged\") return CLI_COLORS.WARNING;\n\n return CLI_COLORS.NEUTRAL;\n };\n\n const getStateBorderColor = (): string => {\n if (option.selected) return CLI_COLORS.PRIMARY;\n if (option.state.status === \"incompatible\") return CLI_COLORS.ERROR;\n if (option.state.status === \"recommended\") return CLI_COLORS.UNFOCUSED;\n if (option.state.status === \"discouraged\") return CLI_COLORS.WARNING;\n return CLI_COLORS.UNFOCUSED;\n };\n\n const textColor = getTextColor();\n const hasUnmetDeps = option.selected && !!option.hasUnmetRequirements;\n const compatibilityLabel = hasUnmetDeps\n ? getCompatibilityLabel(option)\n : showLabels && isFocused\n ? getCompatibilityLabel(option)\n : null;\n\n return (\n <Box\n marginRight={1}\n borderColor={isFocused ? getStateBorderColor() : CLI_COLORS.NEUTRAL}\n borderStyle=\"single\"\n flexShrink={0}\n >\n <>\n <Text color={textColor} bold dimColor={option.selected && !!option.hasUnmetRequirements}>\n {\" \"}\n {getSkillById(option.id).displayName}{\" \"}\n </Text>\n {option.scope && <Text dimColor>{option.scope === \"global\" ? \"G \" : \"P \"}</Text>}\n {compatibilityLabel && <Text dimColor>{compatibilityLabel} </Text>}\n </>\n </Box>\n );\n};\n\ntype CategorySectionProps = {\n isFirst: boolean;\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 isFirst,\n category,\n options,\n isLocked,\n isFocused,\n focusedOptionIndex,\n showLabels,\n}) => {\n const selectedCount = options.filter((o) => o.selected).length;\n\n const selectionCounter = category.exclusive ? `(${selectedCount} of 1)` : null;\n\n return (\n <Box flexDirection=\"column\" marginTop={isFirst ? 0 : 1}>\n <Box flexDirection=\"row\">\n {isFocused ? (\n <Text color=\"#000\" backgroundColor={CLI_COLORS.WHITE}>\n {` ${category.displayName}${category.required ? ` ${SYMBOL_REQUIRED}` : \"\"}${selectionCounter ? ` ${selectionCounter}` : \"\"} `}\n </Text>\n ) : (\n <>\n <Text color=\"gray\">{category.displayName}</Text>\n {category.required && <Text color={CLI_COLORS.ERROR}> {SYMBOL_REQUIRED}</Text>}\n {selectionCounter && <Text dimColor> {selectionCounter}</Text>}\n </>\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 onToggle,\n onToggleLabels,\n defaultFocusedRow = 0,\n defaultFocusedCol = 0,\n onFocusChange,\n onFocusedSkillChange,\n}) => {\n const processedCategories = useMemo(\n () => categories.map((category) => ({ ...category, sortedOptions: category.options })),\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 handleFocusChange = useCallback(\n (row: number, col: number) => {\n if (showLabels) onToggleLabels();\n onFocusChange?.(row, col);\n const skill = processedCategories[row]?.sortedOptions[col];\n onFocusedSkillChange?.(skill?.id ?? null);\n },\n [showLabels, onToggleLabels, onFocusChange, processedCategories, onFocusedSkillChange],\n );\n\n const { focusedRow, focusedCol, setFocused, moveFocus } = useFocusedListItem(\n processedCategories.length,\n getColCount,\n {\n wrap: true,\n isRowLocked,\n findValidCol,\n onChange: handleFocusChange,\n initialRow: defaultFocusedRow,\n initialCol: defaultFocusedCol,\n },\n );\n\n const mountedRef = useRef(false);\n if (!mountedRef.current) {\n mountedRef.current = true;\n const skill = processedCategories[defaultFocusedRow]?.sortedOptions[defaultFocusedCol];\n onFocusedSkillChange?.(skill?.id ?? null);\n }\n\n useCategoryGridInput({\n processedCategories,\n categories,\n focusedRow,\n focusedCol,\n setFocused,\n moveFocus,\n onToggle,\n onToggleLabels,\n });\n\n const { setSectionRef, scrollEnabled, scrollTopPx } = useSectionScroll({\n sectionCount: processedCategories.length,\n focusedIndex: focusedRow,\n availableHeight,\n });\n\n if (categories.length === 0) {\n return (\n <Box flexDirection=\"column\">\n <Text dimColor>No categories to display.</Text>\n </Box>\n );\n }\n\n const noShrink = scrollEnabled ? { flexShrink: 0 } : {};\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)} {...noShrink}>\n <CategorySection\n category={category}\n options={category.sortedOptions}\n isLocked={isLocked}\n isFocused={index === focusedRow}\n focusedOptionIndex={focusedCol}\n showLabels={showLabels}\n isFirst={index === 0}\n />\n </Box>\n );\n });\n\n return (\n <Box\n flexDirection=\"column\"\n {...(scrollEnabled\n ? { height: availableHeight, overflow: \"hidden\" as const }\n : { flexGrow: 1 })}\n >\n <Box flexDirection=\"column\" marginTop={scrollTopPx > 0 ? -scrollTopPx : 0} {...noShrink}>\n {sectionElements}\n </Box>\n </Box>\n );\n};\n","import { useCallback, useEffect, useRef } from \"react\";\nimport { useInput } from \"ink\";\n\nimport type { Category, SkillId } from \"../../types/index.js\";\nimport type { CategoryOption, CategoryRow } from \"../wizard/category-grid.js\";\nimport { HOTKEY_TOGGLE_LABELS, isHotkey } from \"../wizard/hotkeys.js\";\n\nconst FRAMEWORK_CATEGORY_ID = \"web-framework\";\n\n// Locked = non-framework section when no framework is selected\nexport const isSectionLocked = (categoryId: Category, 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: Category; 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: Category, 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 (isHotkey(input, HOTKEY_TOGGLE_LABELS)) {\n onToggleLabels();\n return;\n }\n\n if (input === \" \") {\n if (currentLocked) return;\n const currentOption = currentOptions[focusedCol];\n if (currentOption) {\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","useRef","useCallback","useRef"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/components/wizard/source-grid.tsx","../src/cli/components/hooks/use-source-grid-search-modal.ts"],"sourcesContent":["import React, { useCallback } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport type { BoundSkillCandidate, SkillAlias, SkillId } from \"../../types/index.js\";\nimport { CLI_COLORS, SOURCE_DISPLAY_NAMES } from \"../../consts.js\";\nimport { getSkillById } from \"../../lib/matrix/matrix-provider.js\";\nimport { useFocusedListItem } from \"../hooks/use-focused-list-item.js\";\nimport { useSectionScroll } from \"../hooks/use-section-scroll.js\";\nimport { useSourceGridSearchModal } from \"../hooks/use-source-grid-search-modal.js\";\nimport { SearchModal } from \"./search-modal.js\";\n\nconst SEARCH_PILL_LABEL = \"\\u2315 Search\";\n\nexport type SourceOption = {\n id: string;\n selected: boolean;\n installed: boolean;\n};\n\nexport type SourceRow = {\n skillId: SkillId;\n options: SourceOption[];\n};\n\nexport type SourceGridProps = {\n rows: SourceRow[];\n /** Available height in terminal lines for the scrollable viewport. 0 = no constraint. */\n availableHeight?: number;\n onSelect: (skillId: SkillId, sourceId: string) => void;\n onSearch?: (alias: SkillAlias) => Promise<BoundSkillCandidate[]>;\n onBind?: (candidate: BoundSkillCandidate) => void;\n onSearchStateChange?: (active: boolean) => 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\ntype SearchPillProps = {\n isFocused: boolean;\n};\n\nconst SearchPill: React.FC<SearchPillProps> = ({ isFocused }) => {\n const borderColor = isFocused ? CLI_COLORS.UNFOCUSED : CLI_COLORS.NEUTRAL;\n\n return (\n <Box marginRight={1} borderColor={borderColor} borderStyle=\"single\" borderDimColor={!isFocused}>\n <Text dimColor={!isFocused} bold={isFocused}>\n {\" \"}\n {SEARCH_PILL_LABEL}{\" \"}\n </Text>\n </Box>\n );\n};\n\ntype SourceSectionProps = {\n row: SourceRow;\n isFocused: boolean;\n focusedOptionIndex: number;\n showSearchPill: boolean;\n};\n\nfunction formatSourceLabel(option: SourceOption): string {\n const displayName = SOURCE_DISPLAY_NAMES[option.id] ?? option.id;\n const prefix = option.installed ? \"\\u2713 \" : \"\";\n return `${prefix}${displayName}`;\n}\n\nconst SourceTag: React.FC<{ option: SourceOption; isFocused: boolean }> = ({\n option,\n isFocused,\n}) => {\n const getBorderColor = (): string => {\n if (isFocused) {\n return option.selected ? CLI_COLORS.PRIMARY : CLI_COLORS.UNFOCUSED;\n }\n return option.selected ? CLI_COLORS.PRIMARY : CLI_COLORS.NEUTRAL;\n };\n\n const textColor = option.selected ? CLI_COLORS.PRIMARY : CLI_COLORS.NEUTRAL;\n const isBold = isFocused || option.selected;\n\n return (\n <Box\n marginRight={1}\n borderColor={getBorderColor()}\n borderStyle=\"single\"\n borderDimColor={!isFocused && !option.selected}\n >\n <Text color={textColor} bold={isBold} dimColor={false}>\n {\" \"}\n {formatSourceLabel(option)}{\" \"}\n </Text>\n </Box>\n );\n};\n\nconst SourceSection: React.FC<SourceSectionProps> = ({\n row,\n isFocused,\n focusedOptionIndex,\n showSearchPill,\n}) => {\n const searchPillIndex = row.options.length;\n\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Box flexDirection=\"row\">\n <Text>{getSkillById(row.skillId).displayName}</Text>\n </Box>\n\n <Box flexDirection=\"row\" flexWrap=\"wrap\" marginTop={0}>\n {row.options.map((option, index) => (\n <SourceTag\n key={option.id}\n option={option}\n isFocused={isFocused && index === focusedOptionIndex}\n />\n ))}\n {showSearchPill && (\n <SearchPill isFocused={isFocused && focusedOptionIndex === searchPillIndex} />\n )}\n </Box>\n </Box>\n );\n};\n\n/** Total navigable columns for a row (options + search pill if applicable) */\nconst getNavigableCount = (row: SourceRow, showSearchPill: boolean): number => {\n return row.options.length + (showSearchPill ? 1 : 0);\n};\n\nexport const SourceGrid: React.FC<SourceGridProps> = ({\n rows,\n availableHeight = 0,\n onSelect,\n onSearch,\n onBind,\n onSearchStateChange,\n defaultFocusedRow = 0,\n defaultFocusedCol = 0,\n onFocusChange,\n}) => {\n const {\n searchModal,\n searchResults,\n searchAlias,\n handleSearchTrigger,\n handleBind,\n handleCloseSearch,\n } = useSourceGridSearchModal({ rows, onSearch, onBind, onSearchStateChange });\n\n const showSearchPill = !!onSearch;\n\n const getColCount = useCallback(\n (row: number): number => {\n const rowData = rows[row];\n return rowData ? getNavigableCount(rowData, showSearchPill) : 0;\n },\n [rows, showSearchPill],\n );\n\n const { focusedRow, focusedCol, moveFocus } = useFocusedListItem(rows.length, getColCount, {\n wrap: true,\n onChange: onFocusChange,\n initialRow: defaultFocusedRow,\n initialCol: defaultFocusedCol,\n });\n\n const { setSectionRef, scrollEnabled, scrollTopPx } = useSectionScroll({\n sectionCount: rows.length,\n focusedIndex: focusedRow,\n availableHeight,\n });\n\n useInput(\n useCallback(\n (\n input: string,\n key: {\n leftArrow: boolean;\n rightArrow: boolean;\n upArrow: boolean;\n downArrow: boolean;\n return: boolean;\n },\n ) => {\n if (input === \" \") {\n const currentRow = rows[focusedRow];\n if (!currentRow) return;\n if (showSearchPill && focusedCol === currentRow.options.length) {\n void handleSearchTrigger(focusedRow);\n return;\n }\n if (focusedCol < currentRow.options.length) {\n const currentOption = currentRow.options[focusedCol];\n if (currentOption) {\n onSelect(currentRow.skillId, currentOption.id);\n }\n }\n return;\n }\n\n const isLeft = key.leftArrow;\n const isRight = key.rightArrow;\n const isUp = key.upArrow;\n const isDown = key.downArrow;\n\n if (isLeft) {\n moveFocus(\"left\");\n } else if (isRight) {\n moveFocus(\"right\");\n } else if (isUp) {\n moveFocus(\"up\");\n } else if (isDown) {\n moveFocus(\"down\");\n }\n },\n [rows, focusedRow, focusedCol, onSelect, showSearchPill, handleSearchTrigger, moveFocus],\n ),\n { isActive: !searchModal.isOpen },\n );\n\n if (rows.length === 0) {\n return (\n <Box flexDirection=\"column\">\n <Text dimColor>No skills to display.</Text>\n </Box>\n );\n }\n\n const noShrink = scrollEnabled ? { flexShrink: 0 } : {};\n\n const sectionElements = rows.map((row, rowIndex) => (\n <Box key={row.skillId} ref={(el) => setSectionRef(rowIndex, el)} {...noShrink}>\n <SourceSection\n row={row}\n isFocused={rowIndex === focusedRow}\n focusedOptionIndex={focusedCol}\n showSearchPill={showSearchPill}\n />\n </Box>\n ));\n\n const searchModalElement = searchModal.isOpen && (\n <SearchModal\n results={searchResults}\n alias={searchAlias}\n onBind={handleBind}\n onClose={handleCloseSearch}\n />\n );\n\n return (\n <Box\n flexDirection=\"column\"\n {...(scrollEnabled ? { height: availableHeight } : { flexGrow: 1 })}\n >\n <Box flexDirection=\"column\" overflow=\"hidden\" flexGrow={1}>\n <Box flexDirection=\"column\" marginTop={scrollTopPx > 0 ? -scrollTopPx : 0} {...noShrink}>\n {sectionElements}\n </Box>\n </Box>\n {searchModalElement}\n </Box>\n );\n};\n","import { useCallback, useState } from \"react\";\nimport type { BoundSkillCandidate, SkillAlias } from \"../../types/index.js\";\nimport { matrix } from \"../../lib/matrix/matrix-provider.js\";\nimport { useModalState } from \"./use-modal-state.js\";\nimport type { SourceRow } from \"../wizard/source-grid.js\";\n\ntype UseSourceGridSearchModalOptions = {\n rows: SourceRow[];\n onSearch?: (alias: SkillAlias) => Promise<BoundSkillCandidate[]>;\n onBind?: (candidate: BoundSkillCandidate) => void;\n onSearchStateChange?: (active: boolean) => void;\n};\n\ntype UseSourceGridSearchModalResult = {\n searchModal: { isOpen: boolean };\n searchResults: BoundSkillCandidate[];\n searchAlias: string;\n handleSearchTrigger: (rowIndex: number) => Promise<void>;\n handleBind: (candidate: BoundSkillCandidate) => void;\n handleCloseSearch: () => void;\n};\n\nexport function useSourceGridSearchModal({\n rows,\n onSearch,\n onBind,\n onSearchStateChange,\n}: UseSourceGridSearchModalOptions): UseSourceGridSearchModalResult {\n const searchModal = useModalState<number>();\n const [searchResults, setSearchResults] = useState<BoundSkillCandidate[]>([]);\n const [searchAlias, setSearchAlias] = useState(\"\");\n\n const resetSearch = useCallback(() => {\n searchModal.close();\n setSearchResults([]);\n setSearchAlias(\"\");\n onSearchStateChange?.(false);\n }, [onSearchStateChange, searchModal]);\n\n const handleSearchTrigger = useCallback(\n async (rowIndex: number) => {\n const row = rows[rowIndex];\n if (!row || !onSearch) return;\n\n const alias = matrix.slugMap.idToSlug[row.skillId];\n if (!alias) return;\n setSearchAlias(alias);\n searchModal.open(rowIndex);\n onSearchStateChange?.(true);\n\n const results = await onSearch(alias);\n setSearchResults(results);\n },\n [rows, onSearch, onSearchStateChange, searchModal],\n );\n\n const handleBind = useCallback(\n (candidate: BoundSkillCandidate) => {\n onBind?.(candidate);\n resetSearch();\n },\n [onBind, resetSearch],\n );\n\n const handleCloseSearch = useCallback(() => {\n resetSearch();\n }, [resetSearch]);\n\n return {\n searchModal: { isOpen: searchModal.isOpen },\n searchResults,\n searchAlias,\n handleSearchTrigger,\n handleBind,\n handleCloseSearch,\n };\n}\n"],"mappings":"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","names":["useCallback","useCallback"]}
|
|
File without changes
|