claudeup 3.7.2 → 3.9.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/package.json +1 -1
- package/src/data/settings-catalog.js +612 -0
- package/src/data/settings-catalog.ts +689 -0
- package/src/data/skill-repos.js +86 -0
- package/src/data/skill-repos.ts +97 -0
- package/src/services/plugin-manager.js +2 -0
- package/src/services/plugin-manager.ts +3 -0
- package/src/services/profiles.js +161 -0
- package/src/services/profiles.ts +225 -0
- package/src/services/settings-manager.js +108 -0
- package/src/services/settings-manager.ts +140 -0
- package/src/services/skills-manager.js +239 -0
- package/src/services/skills-manager.ts +328 -0
- package/src/services/skillsmp-client.js +67 -0
- package/src/services/skillsmp-client.ts +89 -0
- package/src/types/index.ts +101 -1
- package/src/ui/App.js +23 -18
- package/src/ui/App.tsx +27 -23
- package/src/ui/components/TabBar.js +9 -8
- package/src/ui/components/TabBar.tsx +15 -19
- package/src/ui/components/layout/ScreenLayout.js +8 -14
- package/src/ui/components/layout/ScreenLayout.tsx +51 -58
- package/src/ui/components/modals/ModalContainer.js +43 -11
- package/src/ui/components/modals/ModalContainer.tsx +44 -12
- package/src/ui/components/modals/SelectModal.js +4 -18
- package/src/ui/components/modals/SelectModal.tsx +10 -21
- package/src/ui/screens/CliToolsScreen.js +2 -2
- package/src/ui/screens/CliToolsScreen.tsx +8 -8
- package/src/ui/screens/EnvVarsScreen.js +248 -116
- package/src/ui/screens/EnvVarsScreen.tsx +419 -184
- package/src/ui/screens/McpRegistryScreen.tsx +18 -6
- package/src/ui/screens/McpScreen.js +1 -1
- package/src/ui/screens/McpScreen.tsx +15 -5
- package/src/ui/screens/ModelSelectorScreen.js +3 -5
- package/src/ui/screens/ModelSelectorScreen.tsx +12 -16
- package/src/ui/screens/PluginsScreen.js +154 -66
- package/src/ui/screens/PluginsScreen.tsx +280 -97
- package/src/ui/screens/ProfilesScreen.js +255 -0
- package/src/ui/screens/ProfilesScreen.tsx +487 -0
- package/src/ui/screens/SkillsScreen.js +325 -0
- package/src/ui/screens/SkillsScreen.tsx +574 -0
- package/src/ui/screens/StatusLineScreen.js +2 -2
- package/src/ui/screens/StatusLineScreen.tsx +10 -12
- package/src/ui/screens/index.js +3 -2
- package/src/ui/screens/index.ts +3 -2
- package/src/ui/state/AppContext.js +2 -1
- package/src/ui/state/AppContext.tsx +2 -0
- package/src/ui/state/reducer.js +151 -19
- package/src/ui/state/reducer.ts +167 -19
- package/src/ui/state/types.ts +58 -14
- package/src/utils/clipboard.js +56 -0
- package/src/utils/clipboard.ts +58 -0
|
@@ -91,7 +91,7 @@ export function useModal() {
|
|
|
91
91
|
});
|
|
92
92
|
});
|
|
93
93
|
},
|
|
94
|
-
select: (title, message, options) => {
|
|
94
|
+
select: (title, message, options, defaultIndex) => {
|
|
95
95
|
return new Promise((resolve) => {
|
|
96
96
|
dispatch({
|
|
97
97
|
type: "SHOW_MODAL",
|
|
@@ -100,6 +100,7 @@ export function useModal() {
|
|
|
100
100
|
title,
|
|
101
101
|
message,
|
|
102
102
|
options,
|
|
103
|
+
defaultIndex,
|
|
103
104
|
onSelect: (value) => {
|
|
104
105
|
dispatch({ type: "HIDE_MODAL" });
|
|
105
106
|
resolve(value);
|
|
@@ -153,6 +153,7 @@ export function useModal() {
|
|
|
153
153
|
title: string,
|
|
154
154
|
message: string,
|
|
155
155
|
options: { label: string; value: string; description?: string }[],
|
|
156
|
+
defaultIndex?: number,
|
|
156
157
|
): Promise<string | null> => {
|
|
157
158
|
return new Promise((resolve) => {
|
|
158
159
|
dispatch({
|
|
@@ -162,6 +163,7 @@ export function useModal() {
|
|
|
162
163
|
title,
|
|
163
164
|
message,
|
|
164
165
|
options,
|
|
166
|
+
defaultIndex,
|
|
165
167
|
onSelect: (value: string) => {
|
|
166
168
|
dispatch({ type: "HIDE_MODAL" });
|
|
167
169
|
resolve(value);
|
package/src/ui/state/reducer.js
CHANGED
|
@@ -34,9 +34,9 @@ export const initialState = {
|
|
|
34
34
|
presets: { status: "idle" },
|
|
35
35
|
currentPreset: null,
|
|
36
36
|
},
|
|
37
|
-
|
|
37
|
+
settings: {
|
|
38
38
|
selectedIndex: 0,
|
|
39
|
-
|
|
39
|
+
values: { status: "idle" },
|
|
40
40
|
},
|
|
41
41
|
cliTools: {
|
|
42
42
|
selectedIndex: 0,
|
|
@@ -47,6 +47,17 @@ export const initialState = {
|
|
|
47
47
|
searchQuery: "",
|
|
48
48
|
taskSize: "large",
|
|
49
49
|
},
|
|
50
|
+
profiles: {
|
|
51
|
+
selectedIndex: 0,
|
|
52
|
+
profiles: { status: "idle" },
|
|
53
|
+
},
|
|
54
|
+
skills: {
|
|
55
|
+
scope: "user",
|
|
56
|
+
selectedIndex: 0,
|
|
57
|
+
searchQuery: "",
|
|
58
|
+
skills: { status: "idle" },
|
|
59
|
+
updateStatus: null,
|
|
60
|
+
},
|
|
50
61
|
};
|
|
51
62
|
export function appReducer(state, action) {
|
|
52
63
|
switch (action.type) {
|
|
@@ -54,7 +65,12 @@ export function appReducer(state, action) {
|
|
|
54
65
|
// Navigation
|
|
55
66
|
// =========================================================================
|
|
56
67
|
case "NAVIGATE":
|
|
57
|
-
return {
|
|
68
|
+
return {
|
|
69
|
+
...state,
|
|
70
|
+
currentRoute: action.route,
|
|
71
|
+
// Clear search state when navigating away
|
|
72
|
+
isSearching: false,
|
|
73
|
+
};
|
|
58
74
|
// =========================================================================
|
|
59
75
|
// Plugins Screen
|
|
60
76
|
// =========================================================================
|
|
@@ -268,38 +284,38 @@ export function appReducer(state, action) {
|
|
|
268
284
|
},
|
|
269
285
|
};
|
|
270
286
|
// =========================================================================
|
|
271
|
-
//
|
|
287
|
+
// Settings Screen
|
|
272
288
|
// =========================================================================
|
|
273
|
-
case "
|
|
289
|
+
case "SETTINGS_SELECT":
|
|
274
290
|
return {
|
|
275
291
|
...state,
|
|
276
|
-
|
|
277
|
-
...state.
|
|
292
|
+
settings: {
|
|
293
|
+
...state.settings,
|
|
278
294
|
selectedIndex: action.index,
|
|
279
295
|
},
|
|
280
296
|
};
|
|
281
|
-
case "
|
|
297
|
+
case "SETTINGS_DATA_LOADING":
|
|
282
298
|
return {
|
|
283
299
|
...state,
|
|
284
|
-
|
|
285
|
-
...state.
|
|
286
|
-
|
|
300
|
+
settings: {
|
|
301
|
+
...state.settings,
|
|
302
|
+
values: { status: "loading" },
|
|
287
303
|
},
|
|
288
304
|
};
|
|
289
|
-
case "
|
|
305
|
+
case "SETTINGS_DATA_SUCCESS":
|
|
290
306
|
return {
|
|
291
307
|
...state,
|
|
292
|
-
|
|
293
|
-
...state.
|
|
294
|
-
|
|
308
|
+
settings: {
|
|
309
|
+
...state.settings,
|
|
310
|
+
values: { status: "success", data: action.values },
|
|
295
311
|
},
|
|
296
312
|
};
|
|
297
|
-
case "
|
|
313
|
+
case "SETTINGS_DATA_ERROR":
|
|
298
314
|
return {
|
|
299
315
|
...state,
|
|
300
|
-
|
|
301
|
-
...state.
|
|
302
|
-
|
|
316
|
+
settings: {
|
|
317
|
+
...state.settings,
|
|
318
|
+
values: { status: "error", error: action.error },
|
|
303
319
|
},
|
|
304
320
|
};
|
|
305
321
|
// =========================================================================
|
|
@@ -380,6 +396,122 @@ export function appReducer(state, action) {
|
|
|
380
396
|
},
|
|
381
397
|
};
|
|
382
398
|
// =========================================================================
|
|
399
|
+
// Profiles Screen
|
|
400
|
+
// =========================================================================
|
|
401
|
+
case "PROFILES_SELECT":
|
|
402
|
+
return {
|
|
403
|
+
...state,
|
|
404
|
+
profiles: {
|
|
405
|
+
...state.profiles,
|
|
406
|
+
selectedIndex: action.index,
|
|
407
|
+
},
|
|
408
|
+
};
|
|
409
|
+
case "PROFILES_DATA_LOADING":
|
|
410
|
+
return {
|
|
411
|
+
...state,
|
|
412
|
+
profiles: {
|
|
413
|
+
...state.profiles,
|
|
414
|
+
profiles: { status: "loading" },
|
|
415
|
+
},
|
|
416
|
+
};
|
|
417
|
+
case "PROFILES_DATA_SUCCESS":
|
|
418
|
+
return {
|
|
419
|
+
...state,
|
|
420
|
+
profiles: {
|
|
421
|
+
...state.profiles,
|
|
422
|
+
profiles: { status: "success", data: action.profiles },
|
|
423
|
+
},
|
|
424
|
+
};
|
|
425
|
+
case "PROFILES_DATA_ERROR":
|
|
426
|
+
return {
|
|
427
|
+
...state,
|
|
428
|
+
profiles: {
|
|
429
|
+
...state.profiles,
|
|
430
|
+
profiles: { status: "error", error: action.error },
|
|
431
|
+
},
|
|
432
|
+
};
|
|
433
|
+
// =========================================================================
|
|
434
|
+
// Skills Screen
|
|
435
|
+
// =========================================================================
|
|
436
|
+
case "SKILLS_SET_SCOPE":
|
|
437
|
+
return {
|
|
438
|
+
...state,
|
|
439
|
+
skills: {
|
|
440
|
+
...state.skills,
|
|
441
|
+
scope: action.scope,
|
|
442
|
+
selectedIndex: 0,
|
|
443
|
+
skills: { status: "loading" },
|
|
444
|
+
},
|
|
445
|
+
};
|
|
446
|
+
case "SKILLS_TOGGLE_SCOPE":
|
|
447
|
+
return appReducer(state, {
|
|
448
|
+
type: "SKILLS_SET_SCOPE",
|
|
449
|
+
scope: state.skills.scope === "user" ? "project" : "user",
|
|
450
|
+
});
|
|
451
|
+
case "SKILLS_SELECT":
|
|
452
|
+
return {
|
|
453
|
+
...state,
|
|
454
|
+
skills: {
|
|
455
|
+
...state.skills,
|
|
456
|
+
selectedIndex: action.index,
|
|
457
|
+
},
|
|
458
|
+
};
|
|
459
|
+
case "SKILLS_SET_SEARCH":
|
|
460
|
+
return {
|
|
461
|
+
...state,
|
|
462
|
+
skills: {
|
|
463
|
+
...state.skills,
|
|
464
|
+
searchQuery: action.query,
|
|
465
|
+
selectedIndex: 0,
|
|
466
|
+
},
|
|
467
|
+
};
|
|
468
|
+
case "SKILLS_DATA_LOADING":
|
|
469
|
+
return {
|
|
470
|
+
...state,
|
|
471
|
+
skills: {
|
|
472
|
+
...state.skills,
|
|
473
|
+
skills: { status: "loading" },
|
|
474
|
+
},
|
|
475
|
+
};
|
|
476
|
+
case "SKILLS_DATA_SUCCESS":
|
|
477
|
+
return {
|
|
478
|
+
...state,
|
|
479
|
+
skills: {
|
|
480
|
+
...state.skills,
|
|
481
|
+
skills: { status: "success", data: action.skills },
|
|
482
|
+
},
|
|
483
|
+
};
|
|
484
|
+
case "SKILLS_DATA_ERROR":
|
|
485
|
+
return {
|
|
486
|
+
...state,
|
|
487
|
+
skills: {
|
|
488
|
+
...state.skills,
|
|
489
|
+
skills: { status: "error", error: action.error },
|
|
490
|
+
},
|
|
491
|
+
};
|
|
492
|
+
case "SKILLS_UPDATE_STATUS":
|
|
493
|
+
return {
|
|
494
|
+
...state,
|
|
495
|
+
skills: {
|
|
496
|
+
...state.skills,
|
|
497
|
+
updateStatus: action.updates,
|
|
498
|
+
},
|
|
499
|
+
};
|
|
500
|
+
case "SKILLS_UPDATE_ITEM": {
|
|
501
|
+
if (state.skills.skills.status !== "success")
|
|
502
|
+
return state;
|
|
503
|
+
return {
|
|
504
|
+
...state,
|
|
505
|
+
skills: {
|
|
506
|
+
...state.skills,
|
|
507
|
+
skills: {
|
|
508
|
+
status: "success",
|
|
509
|
+
data: state.skills.skills.data.map((s) => s.name === action.name ? { ...s, ...action.updates } : s),
|
|
510
|
+
},
|
|
511
|
+
},
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
// =========================================================================
|
|
383
515
|
// Modals
|
|
384
516
|
// =========================================================================
|
|
385
517
|
case "SHOW_MODAL":
|
package/src/ui/state/reducer.ts
CHANGED
|
@@ -44,9 +44,9 @@ export const initialState: AppState = {
|
|
|
44
44
|
currentPreset: null,
|
|
45
45
|
},
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
settings: {
|
|
48
48
|
selectedIndex: 0,
|
|
49
|
-
|
|
49
|
+
values: { status: "idle" },
|
|
50
50
|
},
|
|
51
51
|
|
|
52
52
|
cliTools: {
|
|
@@ -59,6 +59,19 @@ export const initialState: AppState = {
|
|
|
59
59
|
searchQuery: "",
|
|
60
60
|
taskSize: "large",
|
|
61
61
|
},
|
|
62
|
+
|
|
63
|
+
profiles: {
|
|
64
|
+
selectedIndex: 0,
|
|
65
|
+
profiles: { status: "idle" },
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
skills: {
|
|
69
|
+
scope: "user",
|
|
70
|
+
selectedIndex: 0,
|
|
71
|
+
searchQuery: "",
|
|
72
|
+
skills: { status: "idle" },
|
|
73
|
+
updateStatus: null,
|
|
74
|
+
},
|
|
62
75
|
};
|
|
63
76
|
|
|
64
77
|
export function appReducer(state: AppState, action: AppAction): AppState {
|
|
@@ -67,7 +80,12 @@ export function appReducer(state: AppState, action: AppAction): AppState {
|
|
|
67
80
|
// Navigation
|
|
68
81
|
// =========================================================================
|
|
69
82
|
case "NAVIGATE":
|
|
70
|
-
return {
|
|
83
|
+
return {
|
|
84
|
+
...state,
|
|
85
|
+
currentRoute: action.route,
|
|
86
|
+
// Clear search state when navigating away
|
|
87
|
+
isSearching: false,
|
|
88
|
+
};
|
|
71
89
|
|
|
72
90
|
// =========================================================================
|
|
73
91
|
// Plugins Screen
|
|
@@ -304,41 +322,41 @@ export function appReducer(state: AppState, action: AppAction): AppState {
|
|
|
304
322
|
};
|
|
305
323
|
|
|
306
324
|
// =========================================================================
|
|
307
|
-
//
|
|
325
|
+
// Settings Screen
|
|
308
326
|
// =========================================================================
|
|
309
|
-
case "
|
|
327
|
+
case "SETTINGS_SELECT":
|
|
310
328
|
return {
|
|
311
329
|
...state,
|
|
312
|
-
|
|
313
|
-
...state.
|
|
330
|
+
settings: {
|
|
331
|
+
...state.settings,
|
|
314
332
|
selectedIndex: action.index,
|
|
315
333
|
},
|
|
316
334
|
};
|
|
317
335
|
|
|
318
|
-
case "
|
|
336
|
+
case "SETTINGS_DATA_LOADING":
|
|
319
337
|
return {
|
|
320
338
|
...state,
|
|
321
|
-
|
|
322
|
-
...state.
|
|
323
|
-
|
|
339
|
+
settings: {
|
|
340
|
+
...state.settings,
|
|
341
|
+
values: { status: "loading" },
|
|
324
342
|
},
|
|
325
343
|
};
|
|
326
344
|
|
|
327
|
-
case "
|
|
345
|
+
case "SETTINGS_DATA_SUCCESS":
|
|
328
346
|
return {
|
|
329
347
|
...state,
|
|
330
|
-
|
|
331
|
-
...state.
|
|
332
|
-
|
|
348
|
+
settings: {
|
|
349
|
+
...state.settings,
|
|
350
|
+
values: { status: "success", data: action.values },
|
|
333
351
|
},
|
|
334
352
|
};
|
|
335
353
|
|
|
336
|
-
case "
|
|
354
|
+
case "SETTINGS_DATA_ERROR":
|
|
337
355
|
return {
|
|
338
356
|
...state,
|
|
339
|
-
|
|
340
|
-
...state.
|
|
341
|
-
|
|
357
|
+
settings: {
|
|
358
|
+
...state.settings,
|
|
359
|
+
values: { status: "error", error: action.error },
|
|
342
360
|
},
|
|
343
361
|
};
|
|
344
362
|
|
|
@@ -428,6 +446,136 @@ export function appReducer(state: AppState, action: AppAction): AppState {
|
|
|
428
446
|
},
|
|
429
447
|
};
|
|
430
448
|
|
|
449
|
+
// =========================================================================
|
|
450
|
+
// Profiles Screen
|
|
451
|
+
// =========================================================================
|
|
452
|
+
case "PROFILES_SELECT":
|
|
453
|
+
return {
|
|
454
|
+
...state,
|
|
455
|
+
profiles: {
|
|
456
|
+
...state.profiles,
|
|
457
|
+
selectedIndex: action.index,
|
|
458
|
+
},
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
case "PROFILES_DATA_LOADING":
|
|
462
|
+
return {
|
|
463
|
+
...state,
|
|
464
|
+
profiles: {
|
|
465
|
+
...state.profiles,
|
|
466
|
+
profiles: { status: "loading" },
|
|
467
|
+
},
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
case "PROFILES_DATA_SUCCESS":
|
|
471
|
+
return {
|
|
472
|
+
...state,
|
|
473
|
+
profiles: {
|
|
474
|
+
...state.profiles,
|
|
475
|
+
profiles: { status: "success", data: action.profiles },
|
|
476
|
+
},
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
case "PROFILES_DATA_ERROR":
|
|
480
|
+
return {
|
|
481
|
+
...state,
|
|
482
|
+
profiles: {
|
|
483
|
+
...state.profiles,
|
|
484
|
+
profiles: { status: "error", error: action.error },
|
|
485
|
+
},
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
// =========================================================================
|
|
489
|
+
// Skills Screen
|
|
490
|
+
// =========================================================================
|
|
491
|
+
case "SKILLS_SET_SCOPE":
|
|
492
|
+
return {
|
|
493
|
+
...state,
|
|
494
|
+
skills: {
|
|
495
|
+
...state.skills,
|
|
496
|
+
scope: action.scope,
|
|
497
|
+
selectedIndex: 0,
|
|
498
|
+
skills: { status: "loading" },
|
|
499
|
+
},
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
case "SKILLS_TOGGLE_SCOPE":
|
|
503
|
+
return appReducer(state, {
|
|
504
|
+
type: "SKILLS_SET_SCOPE",
|
|
505
|
+
scope: state.skills.scope === "user" ? "project" : "user",
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
case "SKILLS_SELECT":
|
|
509
|
+
return {
|
|
510
|
+
...state,
|
|
511
|
+
skills: {
|
|
512
|
+
...state.skills,
|
|
513
|
+
selectedIndex: action.index,
|
|
514
|
+
},
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
case "SKILLS_SET_SEARCH":
|
|
518
|
+
return {
|
|
519
|
+
...state,
|
|
520
|
+
skills: {
|
|
521
|
+
...state.skills,
|
|
522
|
+
searchQuery: action.query,
|
|
523
|
+
selectedIndex: 0,
|
|
524
|
+
},
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
case "SKILLS_DATA_LOADING":
|
|
528
|
+
return {
|
|
529
|
+
...state,
|
|
530
|
+
skills: {
|
|
531
|
+
...state.skills,
|
|
532
|
+
skills: { status: "loading" },
|
|
533
|
+
},
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
case "SKILLS_DATA_SUCCESS":
|
|
537
|
+
return {
|
|
538
|
+
...state,
|
|
539
|
+
skills: {
|
|
540
|
+
...state.skills,
|
|
541
|
+
skills: { status: "success", data: action.skills },
|
|
542
|
+
},
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
case "SKILLS_DATA_ERROR":
|
|
546
|
+
return {
|
|
547
|
+
...state,
|
|
548
|
+
skills: {
|
|
549
|
+
...state.skills,
|
|
550
|
+
skills: { status: "error", error: action.error },
|
|
551
|
+
},
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
case "SKILLS_UPDATE_STATUS":
|
|
555
|
+
return {
|
|
556
|
+
...state,
|
|
557
|
+
skills: {
|
|
558
|
+
...state.skills,
|
|
559
|
+
updateStatus: action.updates,
|
|
560
|
+
},
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
case "SKILLS_UPDATE_ITEM": {
|
|
564
|
+
if (state.skills.skills.status !== "success") return state;
|
|
565
|
+
return {
|
|
566
|
+
...state,
|
|
567
|
+
skills: {
|
|
568
|
+
...state.skills,
|
|
569
|
+
skills: {
|
|
570
|
+
status: "success",
|
|
571
|
+
data: state.skills.skills.data.map((s) =>
|
|
572
|
+
s.name === action.name ? { ...s, ...action.updates } : s,
|
|
573
|
+
),
|
|
574
|
+
},
|
|
575
|
+
},
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
|
|
431
579
|
// =========================================================================
|
|
432
580
|
// Modals
|
|
433
581
|
// =========================================================================
|
package/src/ui/state/types.ts
CHANGED
|
@@ -2,6 +2,8 @@ import type {
|
|
|
2
2
|
Marketplace,
|
|
3
3
|
McpServer,
|
|
4
4
|
StatusLineConfig,
|
|
5
|
+
ProfileEntry,
|
|
6
|
+
SkillInfo,
|
|
5
7
|
} from "../../types/index.js";
|
|
6
8
|
import type { PluginInfo } from "../../services/plugin-manager.js";
|
|
7
9
|
|
|
@@ -13,19 +15,21 @@ export type Screen =
|
|
|
13
15
|
| "plugins"
|
|
14
16
|
| "mcp"
|
|
15
17
|
| "mcp-registry"
|
|
16
|
-
| "
|
|
17
|
-
| "env-vars"
|
|
18
|
+
| "settings"
|
|
18
19
|
| "cli-tools"
|
|
19
|
-
| "model-selector"
|
|
20
|
+
| "model-selector"
|
|
21
|
+
| "profiles"
|
|
22
|
+
| "skills";
|
|
20
23
|
|
|
21
24
|
export type Route =
|
|
22
25
|
| { screen: "plugins" }
|
|
23
26
|
| { screen: "mcp" }
|
|
24
27
|
| { screen: "mcp-registry"; query?: string }
|
|
25
|
-
| { screen: "
|
|
26
|
-
| { screen: "env-vars" }
|
|
28
|
+
| { screen: "settings" }
|
|
27
29
|
| { screen: "cli-tools" }
|
|
28
|
-
| { screen: "model-selector" }
|
|
30
|
+
| { screen: "model-selector" }
|
|
31
|
+
| { screen: "profiles" }
|
|
32
|
+
| { screen: "skills" };
|
|
29
33
|
|
|
30
34
|
// ============================================================================
|
|
31
35
|
// Async Data Types
|
|
@@ -68,6 +72,7 @@ export type ModalState =
|
|
|
68
72
|
title: string;
|
|
69
73
|
message: string;
|
|
70
74
|
options: SelectOption[];
|
|
75
|
+
defaultIndex?: number;
|
|
71
76
|
onSelect: (value: string) => void;
|
|
72
77
|
onCancel: () => void;
|
|
73
78
|
}
|
|
@@ -125,9 +130,14 @@ export interface StatusLineScreenState {
|
|
|
125
130
|
currentPreset: string | null;
|
|
126
131
|
}
|
|
127
132
|
|
|
128
|
-
export interface
|
|
133
|
+
export interface ScopedSettingValues {
|
|
134
|
+
user: string | undefined;
|
|
135
|
+
project: string | undefined;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export interface SettingsScreenState {
|
|
129
139
|
selectedIndex: number;
|
|
130
|
-
|
|
140
|
+
values: AsyncData<Map<string, ScopedSettingValues>>;
|
|
131
141
|
}
|
|
132
142
|
|
|
133
143
|
export interface CliTool {
|
|
@@ -156,6 +166,21 @@ export interface ModelSelectorScreenState {
|
|
|
156
166
|
taskSize: "large" | "small";
|
|
157
167
|
}
|
|
158
168
|
|
|
169
|
+
export interface ProfilesScreenState {
|
|
170
|
+
selectedIndex: number;
|
|
171
|
+
profiles: AsyncData<ProfileEntry[]>;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export interface SkillsScreenState {
|
|
175
|
+
scope: "user" | "project";
|
|
176
|
+
selectedIndex: number;
|
|
177
|
+
searchQuery: string;
|
|
178
|
+
/** Available skills from Tree API, cross-referenced with lock files */
|
|
179
|
+
skills: AsyncData<SkillInfo[]>;
|
|
180
|
+
/** Null until check completes; then Map<skillName, hasUpdate> */
|
|
181
|
+
updateStatus: Map<string, boolean> | null;
|
|
182
|
+
}
|
|
183
|
+
|
|
159
184
|
// ============================================================================
|
|
160
185
|
// App State
|
|
161
186
|
// ============================================================================
|
|
@@ -181,9 +206,11 @@ export interface AppState {
|
|
|
181
206
|
mcp: McpScreenState;
|
|
182
207
|
mcpRegistry: McpRegistryScreenState;
|
|
183
208
|
statusline: StatusLineScreenState;
|
|
184
|
-
|
|
209
|
+
settings: SettingsScreenState;
|
|
185
210
|
cliTools: CliToolsScreenState;
|
|
186
211
|
modelSelector: ModelSelectorScreenState;
|
|
212
|
+
profiles: ProfilesScreenState;
|
|
213
|
+
skills: SkillsScreenState;
|
|
187
214
|
}
|
|
188
215
|
|
|
189
216
|
// ============================================================================
|
|
@@ -239,11 +266,11 @@ export type AppAction =
|
|
|
239
266
|
}
|
|
240
267
|
| { type: "STATUSLINE_DATA_ERROR"; error: Error }
|
|
241
268
|
|
|
242
|
-
//
|
|
243
|
-
| { type: "
|
|
244
|
-
| { type: "
|
|
245
|
-
| { type: "
|
|
246
|
-
| { type: "
|
|
269
|
+
// Settings screen
|
|
270
|
+
| { type: "SETTINGS_SELECT"; index: number }
|
|
271
|
+
| { type: "SETTINGS_DATA_LOADING" }
|
|
272
|
+
| { type: "SETTINGS_DATA_SUCCESS"; values: Map<string, ScopedSettingValues> }
|
|
273
|
+
| { type: "SETTINGS_DATA_ERROR"; error: Error }
|
|
247
274
|
|
|
248
275
|
// CLI tools screen
|
|
249
276
|
| { type: "CLITOOLS_SELECT"; index: number }
|
|
@@ -269,5 +296,22 @@ export type AppAction =
|
|
|
269
296
|
// Search state (blocks global key handlers)
|
|
270
297
|
| { type: "SET_SEARCHING"; isSearching: boolean }
|
|
271
298
|
|
|
299
|
+
// Profiles screen
|
|
300
|
+
| { type: "PROFILES_SELECT"; index: number }
|
|
301
|
+
| { type: "PROFILES_DATA_LOADING" }
|
|
302
|
+
| { type: "PROFILES_DATA_SUCCESS"; profiles: ProfileEntry[] }
|
|
303
|
+
| { type: "PROFILES_DATA_ERROR"; error: Error }
|
|
304
|
+
|
|
305
|
+
// Skills screen
|
|
306
|
+
| { type: "SKILLS_SET_SCOPE"; scope: "user" | "project" }
|
|
307
|
+
| { type: "SKILLS_TOGGLE_SCOPE" }
|
|
308
|
+
| { type: "SKILLS_SELECT"; index: number }
|
|
309
|
+
| { type: "SKILLS_SET_SEARCH"; query: string }
|
|
310
|
+
| { type: "SKILLS_DATA_LOADING" }
|
|
311
|
+
| { type: "SKILLS_DATA_SUCCESS"; skills: SkillInfo[] }
|
|
312
|
+
| { type: "SKILLS_DATA_ERROR"; error: Error }
|
|
313
|
+
| { type: "SKILLS_UPDATE_STATUS"; updates: Map<string, boolean> }
|
|
314
|
+
| { type: "SKILLS_UPDATE_ITEM"; name: string; updates: Partial<SkillInfo> }
|
|
315
|
+
|
|
272
316
|
// Data refresh - triggers screens to refetch
|
|
273
317
|
| { type: "DATA_REFRESH_COMPLETE" };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
/**
|
|
3
|
+
* Write text to the system clipboard.
|
|
4
|
+
* macOS: uses pbcopy
|
|
5
|
+
* Linux: uses xclip
|
|
6
|
+
* Throws ClipboardUnavailableError if no clipboard tool is available.
|
|
7
|
+
*/
|
|
8
|
+
export async function writeClipboard(text) {
|
|
9
|
+
if (process.platform === "darwin") {
|
|
10
|
+
execSync("pbcopy", { input: text });
|
|
11
|
+
}
|
|
12
|
+
else if (process.platform === "linux") {
|
|
13
|
+
execSync("xclip -selection clipboard", { input: text });
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
throw new ClipboardUnavailableError(`Clipboard not supported on platform: ${process.platform}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Read text from the system clipboard.
|
|
21
|
+
* macOS: uses pbpaste
|
|
22
|
+
* Linux: uses xclip
|
|
23
|
+
* Throws ClipboardUnavailableError if no clipboard tool is available.
|
|
24
|
+
*/
|
|
25
|
+
export async function readClipboard() {
|
|
26
|
+
if (process.platform === "darwin") {
|
|
27
|
+
return execSync("pbpaste").toString();
|
|
28
|
+
}
|
|
29
|
+
else if (process.platform === "linux") {
|
|
30
|
+
return execSync("xclip -selection clipboard -o").toString();
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
throw new ClipboardUnavailableError(`Clipboard not supported on platform: ${process.platform}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export class ClipboardUnavailableError extends Error {
|
|
37
|
+
constructor(message) {
|
|
38
|
+
super(message);
|
|
39
|
+
this.name = "ClipboardUnavailableError";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Check if clipboard operations are available on this platform/system */
|
|
43
|
+
export function isClipboardAvailable() {
|
|
44
|
+
if (process.platform === "darwin")
|
|
45
|
+
return true;
|
|
46
|
+
if (process.platform === "linux") {
|
|
47
|
+
try {
|
|
48
|
+
execSync("which xclip", { stdio: "ignore" });
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
}
|