magus-cli 7.3.1 → 7.4.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 +4 -4
- package/src/__tests__/blind/c1-npm-package.test.ts +206 -0
- package/src/__tests__/blind/c10-colour-roles.test.ts +265 -0
- package/src/__tests__/blind/c11-cli-parse-refusal.test.ts +454 -0
- package/src/__tests__/blind/c12-skill-frontmatter.test.ts +323 -0
- package/src/__tests__/blind/c13-wrong-shape.test.ts +603 -0
- package/src/__tests__/blind/c14-cli-row-label.test.ts +259 -0
- package/src/__tests__/blind/c15-probe-ownership.test.ts +533 -0
- package/src/__tests__/blind/c16-nested-wrong-shape.test.ts +297 -0
- package/src/__tests__/blind/c17-enter-label.test.ts +625 -0
- package/src/__tests__/blind/c18-hook-shape.test.ts +354 -0
- package/src/__tests__/blind/c2-settings-parse.test.ts +350 -0
- package/src/__tests__/blind/c4-key-dispatch.test.ts +397 -0
- package/src/__tests__/blind/c5-keymap.test.ts +461 -0
- package/src/__tests__/blind/c6-request-gate.test.ts +133 -0
- package/src/__tests__/blind/c7-tab-tiers.test.ts +320 -0
- package/src/__tests__/blind/c8-text-width.test.ts +137 -0
- package/src/__tests__/blind/c9-cli-tool-version.test.ts +239 -0
- package/src/__tests__/blind/child-run.ts +114 -0
- package/src/__tests__/cli-tools-adapter.test.ts +5 -2
- package/src/__tests__/component-badge.test.ts +20 -36
- package/src/__tests__/footer-hints.test.ts +1 -1
- package/src/__tests__/manifest.test.ts +25 -17
- package/src/__tests__/marketplace-badge.test.ts +4 -4
- package/src/__tests__/models-screen-state.test.ts +36 -13
- package/src/__tests__/scope-squares.test.tsx +28 -114
- package/src/__tests__/shutdown.test.ts +578 -0
- package/src/__tests__/styles-manager.test.ts +4 -4
- package/src/__tests__/styles-screen-state.test.ts +26 -12
- package/src/__tests__/tabbar-layout.test.ts +44 -32
- package/src/__tests__/theme-adaptive-colors.test.ts +6 -59
- package/src/__tests__/tool-probe.test.ts +47 -22
- package/src/__tests__/uppercase-keybindings.test.ts +25 -12
- package/src/cli/ansi.ts +2 -2
- package/src/cli/bootstrap.ts +33 -2
- package/src/cli/doctor.ts +105 -14
- package/src/cli/install.ts +8 -2
- package/src/cli/profile.ts +18 -1
- package/src/cli/router.ts +31 -18
- package/src/cli/update-view.ts +1 -1
- package/src/data/cli-tools.ts +9 -1
- package/src/services/claude-settings.ts +151 -73
- package/src/services/cli-tool-commands.ts +15 -5
- package/src/services/cli-tool-version.ts +101 -0
- package/src/services/hook-registration.ts +23 -6
- package/src/services/manifest.ts +117 -52
- package/src/services/plugin-manager.ts +19 -5
- package/src/services/profile-adopt.ts +7 -2
- package/src/services/profile-startup.ts +87 -7
- package/src/services/profiles.ts +36 -23
- package/src/services/settings-file.ts +158 -4
- package/src/services/settings-manager.ts +19 -5
- package/src/services/skills-manager.ts +46 -0
- package/src/services/tool-probe.ts +272 -45
- package/src/services/toolchain.ts +39 -0
- package/src/services/update-engine.ts +5 -0
- package/src/services/update-plan.ts +9 -0
- package/src/tui.tsx +48 -21
- package/src/ui/App.tsx +58 -197
- package/src/ui/adapters/cliToolsAdapter.ts +141 -12
- package/src/ui/adapters/pluginsAdapter.ts +10 -7
- package/src/ui/adapters/settingsAdapter.ts +0 -21
- package/src/ui/adapters/skillsAdapter.ts +27 -8
- package/src/ui/adapters/stylesAdapter.ts +15 -11
- package/src/ui/components/CategoryHeader.tsx +7 -1
- package/src/ui/components/EmptyFilterState.tsx +2 -2
- package/src/ui/components/FlagDetailEditor.tsx +0 -0
- package/src/ui/components/ScreenErrorBoundary.tsx +105 -0
- package/src/ui/components/TabBar.tsx +93 -21
- package/src/ui/components/layout/FooterHints.tsx +6 -27
- package/src/ui/components/layout/ScreenLayout.tsx +68 -42
- package/src/ui/components/layout/index.ts +1 -5
- package/src/ui/components/modals/ConfirmModal.tsx +2 -19
- package/src/ui/components/modals/HelpOverlay.tsx +186 -0
- package/src/ui/components/modals/MessageModal.tsx +3 -15
- package/src/ui/components/modals/ModalContainer.tsx +99 -60
- package/src/ui/components/primitives/ErrorNotice.tsx +38 -0
- package/src/ui/components/primitives/ListCategoryRow.tsx +18 -7
- package/src/ui/components/primitives/MetaText.tsx +1 -1
- package/src/ui/components/primitives/ScopeDetail.tsx +31 -26
- package/src/ui/components/primitives/ScopeSquares.tsx +23 -28
- package/src/ui/components/primitives/SelectableRow.tsx +24 -1
- package/src/ui/components/primitives/index.ts +4 -3
- package/src/ui/hooks/useProfileStartupModal.ts +15 -3
- package/src/ui/input/KeyRouter.tsx +180 -0
- package/src/ui/input/key-dispatch.ts +149 -0
- package/src/ui/keymap.ts +898 -0
- package/src/ui/renderers/cliToolRenderers.tsx +128 -66
- package/src/ui/renderers/gitignoreRenderers.tsx +41 -43
- package/src/ui/renderers/markdownRenderer.tsx +2 -2
- package/src/ui/renderers/mcpRenderers.tsx +28 -14
- package/src/ui/renderers/modelRenderers.tsx +12 -13
- package/src/ui/renderers/modelVisuals.tsx +0 -2
- package/src/ui/renderers/pluginRenderers.tsx +47 -54
- package/src/ui/renderers/profileRenderers.tsx +32 -37
- package/src/ui/renderers/settingsRenderers.tsx +84 -55
- package/src/ui/renderers/skillRenderers.tsx +237 -190
- package/src/ui/renderers/styleRenderers.tsx +13 -2
- package/src/ui/runtime/shutdown.ts +268 -0
- package/src/ui/screens/AliasScreen.tsx +118 -100
- package/src/ui/screens/CliToolsScreen.tsx +98 -87
- package/src/ui/screens/EnvVarsScreen.tsx +44 -42
- package/src/ui/screens/GitignoreScreen.tsx +50 -55
- package/src/ui/screens/McpRegistryScreen.tsx +52 -86
- package/src/ui/screens/McpScreen.tsx +47 -33
- package/src/ui/screens/ModelsScreen.tsx +59 -104
- package/src/ui/screens/PluginsScreen.tsx +123 -127
- package/src/ui/screens/ProfilesScreen.tsx +49 -46
- package/src/ui/screens/SkillsScreen.tsx +184 -204
- package/src/ui/screens/StylesScreen.tsx +104 -157
- package/src/ui/state/AppContext.tsx +6 -0
- package/src/ui/state/DimensionsContext.tsx +1 -10
- package/src/ui/state/reducer.ts +5 -8
- package/src/ui/state/types.ts +11 -9
- package/src/ui/text-width.ts +341 -0
- package/src/ui/theme-mode.ts +34 -71
- package/src/ui/theme.ts +50 -28
- package/src/utils/request-gate.ts +37 -0
- package/src/ui/components/ScopeIndicator.tsx +0 -63
- package/src/ui/components/SearchInput.tsx +0 -57
- package/src/ui/hooks/index.ts +0 -2
- package/src/ui/hooks/useKeyboard.ts +0 -24
- package/src/ui/hooks/useKeyboardHandler.ts +0 -63
|
@@ -5,30 +5,37 @@ import type {
|
|
|
5
5
|
SettingsSettingItem,
|
|
6
6
|
} from "../adapters/settingsAdapter.js";
|
|
7
7
|
import {
|
|
8
|
-
CATEGORY_BG,
|
|
9
|
-
CATEGORY_COLOR,
|
|
10
8
|
CATEGORY_DESCRIPTIONS,
|
|
11
9
|
CATEGORY_LABELS,
|
|
12
10
|
formatValue,
|
|
13
11
|
} from "../adapters/settingsAdapter.js";
|
|
14
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
ListCategoryRow,
|
|
14
|
+
ScopeDot,
|
|
15
|
+
ScopeKeyChip,
|
|
16
|
+
rowLead,
|
|
17
|
+
} from "../components/primitives/index.js";
|
|
15
18
|
import type { ItemRenderer } from "../registry.js";
|
|
19
|
+
import { displayWidth, padTo } from "../text-width.js";
|
|
16
20
|
import { theme } from "../theme.js";
|
|
17
21
|
|
|
18
22
|
// ─── Category renderer ─────────────────────────────────────────────────────────
|
|
19
23
|
|
|
24
|
+
/**
|
|
25
|
+
* A category is a group, so every header wears the one neutral header style.
|
|
26
|
+
* They used to wear four unrelated hues — green, teal, purple, blue, amber — none
|
|
27
|
+
* of which meant anything, and two of which (amber, green) meant "warning" and
|
|
28
|
+
* "on" everywhere else. "Recommended" keeps its ★, which is the distinction.
|
|
29
|
+
*/
|
|
20
30
|
const categoryRenderer: ItemRenderer<SettingsCategoryItem> = {
|
|
21
31
|
renderRow: ({ item, isSelected }) => {
|
|
22
32
|
const star = item.category === "recommended" ? "★ " : "";
|
|
23
|
-
const label = `${star}${CATEGORY_LABELS[item.category]}`;
|
|
24
|
-
const bg = isSelected ? theme.selection.bg : CATEGORY_BG[item.category];
|
|
25
|
-
|
|
26
33
|
return (
|
|
27
|
-
<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
<ListCategoryRow
|
|
35
|
+
selected={isSelected}
|
|
36
|
+
title={`${star}${CATEGORY_LABELS[item.category]}`}
|
|
37
|
+
expandable={false}
|
|
38
|
+
/>
|
|
32
39
|
);
|
|
33
40
|
},
|
|
34
41
|
|
|
@@ -36,7 +43,7 @@ const categoryRenderer: ItemRenderer<SettingsCategoryItem> = {
|
|
|
36
43
|
const cat = item.category;
|
|
37
44
|
return (
|
|
38
45
|
<box flexDirection="column">
|
|
39
|
-
<text fg={
|
|
46
|
+
<text fg={theme.colors.info}>
|
|
40
47
|
<strong>{CATEGORY_LABELS[cat]}</strong>
|
|
41
48
|
</text>
|
|
42
49
|
<box marginTop={1}>
|
|
@@ -49,34 +56,70 @@ const categoryRenderer: ItemRenderer<SettingsCategoryItem> = {
|
|
|
49
56
|
|
|
50
57
|
// ─── Setting renderer ──────────────────────────────────────────────────────────
|
|
51
58
|
|
|
59
|
+
/** The lead cell, the ●/○ indicator and the space after it. */
|
|
60
|
+
const ROW_PREFIX = 3;
|
|
61
|
+
/** Columns always kept between a label and its value. */
|
|
62
|
+
const LABEL_GAP = 1;
|
|
63
|
+
/** Width assumed when a caller does not say — the old fixed label column. */
|
|
64
|
+
const DEFAULT_ROW_WIDTH = 45;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* One setting row: `▌● Label……………… value`, fitted to `width` in cells.
|
|
68
|
+
*
|
|
69
|
+
* The label used to be `name.padEnd(28)`, which only pads: a name of 28 or more
|
|
70
|
+
* characters ran straight into its value — "Auto-approve Project MCP Serversoff",
|
|
71
|
+
* "AI Attribution in Commits & PRson". Now the value is measured first and keeps
|
|
72
|
+
* its whole width, the label gets what is left less a one-cell gap, and a label
|
|
73
|
+
* that does not fit is cut with `…`. Everything is counted in terminal cells, so
|
|
74
|
+
* a wide glyph in a value cannot push it into the divider.
|
|
75
|
+
*/
|
|
76
|
+
function settingRowParts(
|
|
77
|
+
name: string,
|
|
78
|
+
value: string,
|
|
79
|
+
width: number,
|
|
80
|
+
): { label: string; value: string } {
|
|
81
|
+
const valueWidth = displayWidth(value);
|
|
82
|
+
const labelWidth = Math.max(
|
|
83
|
+
1,
|
|
84
|
+
width - ROW_PREFIX - LABEL_GAP - valueWidth - 1,
|
|
85
|
+
);
|
|
86
|
+
return { label: padTo(name, labelWidth), value };
|
|
87
|
+
}
|
|
88
|
+
|
|
52
89
|
const settingRenderer: ItemRenderer<SettingsSettingItem> = {
|
|
53
|
-
renderRow: ({ item, isSelected }) => {
|
|
90
|
+
renderRow: ({ item, isSelected, width = DEFAULT_ROW_WIDTH }) => {
|
|
54
91
|
const { setting } = item;
|
|
55
92
|
const indicator = item.isDefault ? "○" : "●";
|
|
56
|
-
const
|
|
93
|
+
const parts = settingRowParts(
|
|
94
|
+
setting.name,
|
|
95
|
+
formatValue(setting, item.effectiveValue),
|
|
96
|
+
width,
|
|
97
|
+
);
|
|
98
|
+
const gap = " ".repeat(LABEL_GAP);
|
|
57
99
|
|
|
58
100
|
if (isSelected) {
|
|
59
101
|
return (
|
|
60
102
|
<text bg={theme.selection.bg} fg={theme.selection.fg}>
|
|
61
|
-
{
|
|
62
|
-
{indicator} {
|
|
63
|
-
{
|
|
103
|
+
{rowLead(true)}
|
|
104
|
+
{indicator} {parts.label}
|
|
105
|
+
{gap}
|
|
106
|
+
{parts.value}{" "}
|
|
64
107
|
</text>
|
|
65
108
|
);
|
|
66
109
|
}
|
|
67
110
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
: theme.colors.info;
|
|
71
|
-
const valueColor = item.isDefault
|
|
72
|
-
? theme.colors.muted
|
|
73
|
-
: theme.colors.success;
|
|
111
|
+
// Set vs default is one fact, said once: the dot, and the value's ink.
|
|
112
|
+
const tone = item.isDefault ? theme.colors.muted : theme.colors.success;
|
|
74
113
|
|
|
75
114
|
return (
|
|
76
115
|
<text fg={theme.colors.text}>
|
|
77
|
-
<span fg={
|
|
78
|
-
|
|
79
|
-
|
|
116
|
+
<span fg={tone}>
|
|
117
|
+
{rowLead(false)}
|
|
118
|
+
{indicator}{" "}
|
|
119
|
+
</span>
|
|
120
|
+
<span>{parts.label}</span>
|
|
121
|
+
<span>{gap}</span>
|
|
122
|
+
<span fg={tone}>{parts.value}</span>
|
|
80
123
|
</text>
|
|
81
124
|
);
|
|
82
125
|
},
|
|
@@ -117,7 +160,7 @@ const settingRenderer: ItemRenderer<SettingsSettingItem> = {
|
|
|
117
160
|
<box marginTop={1}>
|
|
118
161
|
<text fg={theme.colors.text}>
|
|
119
162
|
<span fg={theme.colors.muted}>Stored </span>
|
|
120
|
-
<span fg={theme.colors.
|
|
163
|
+
<span fg={theme.colors.info}>{storageDesc}</span>
|
|
121
164
|
</text>
|
|
122
165
|
</box>
|
|
123
166
|
{setting.defaultValue !== undefined && (
|
|
@@ -136,34 +179,22 @@ const settingRenderer: ItemRenderer<SettingsSettingItem> = {
|
|
|
136
179
|
</text>
|
|
137
180
|
<box marginTop={1} flexDirection="column">
|
|
138
181
|
<text fg={theme.colors.text}>
|
|
139
|
-
<
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
</span>
|
|
143
|
-
<span fg={userIsSet ? theme.
|
|
144
|
-
{userIsSet ? " ● " : " ○ "}
|
|
145
|
-
</span>
|
|
146
|
-
<span fg={theme.scopes.user}>User</span>
|
|
147
|
-
<span> global</span>
|
|
148
|
-
<span fg={userIsSet ? theme.scopes.user : theme.colors.muted}>
|
|
182
|
+
<ScopeKeyChip scopeKey="u" />
|
|
183
|
+
<ScopeDot installed={userIsSet} />
|
|
184
|
+
<span>User</span>
|
|
185
|
+
<span fg={theme.colors.muted}> global</span>
|
|
186
|
+
<span fg={userIsSet ? theme.colors.success : theme.colors.muted}>
|
|
149
187
|
{" "}
|
|
150
188
|
{userValue}
|
|
151
189
|
</span>
|
|
152
190
|
</text>
|
|
153
191
|
<text fg={theme.colors.text}>
|
|
154
|
-
<
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
</span>
|
|
192
|
+
<ScopeKeyChip scopeKey="p" />
|
|
193
|
+
<ScopeDot installed={projectIsSet} />
|
|
194
|
+
<span>Project</span>
|
|
195
|
+
<span fg={theme.colors.muted}> team</span>
|
|
158
196
|
<span
|
|
159
|
-
fg={projectIsSet ? theme.
|
|
160
|
-
>
|
|
161
|
-
{projectIsSet ? " ● " : " ○ "}
|
|
162
|
-
</span>
|
|
163
|
-
<span fg={theme.scopes.project}>Project</span>
|
|
164
|
-
<span> team</span>
|
|
165
|
-
<span
|
|
166
|
-
fg={projectIsSet ? theme.scopes.project : theme.colors.muted}
|
|
197
|
+
fg={projectIsSet ? theme.colors.success : theme.colors.muted}
|
|
167
198
|
>
|
|
168
199
|
{" "}
|
|
169
200
|
{projectValue}
|
|
@@ -192,15 +223,16 @@ export const settingsRenderers: {
|
|
|
192
223
|
setting: settingRenderer,
|
|
193
224
|
};
|
|
194
225
|
|
|
226
|
+
/** A list row, fitted to `width` — the list panel's usable columns. */
|
|
195
227
|
export function renderSettingRow(
|
|
196
228
|
item: SettingsBrowserItem,
|
|
197
|
-
_index: number,
|
|
198
229
|
isSelected: boolean,
|
|
230
|
+
width: number,
|
|
199
231
|
): React.ReactNode {
|
|
200
232
|
if (item.kind === "category") {
|
|
201
|
-
return settingsRenderers.category.renderRow({ item, isSelected });
|
|
233
|
+
return settingsRenderers.category.renderRow({ item, isSelected, width });
|
|
202
234
|
}
|
|
203
|
-
return settingsRenderers.setting.renderRow({ item, isSelected });
|
|
235
|
+
return settingsRenderers.setting.renderRow({ item, isSelected, width });
|
|
204
236
|
}
|
|
205
237
|
|
|
206
238
|
export function renderSettingDetail(
|
|
@@ -213,6 +245,3 @@ export function renderSettingDetail(
|
|
|
213
245
|
}
|
|
214
246
|
return settingsRenderers.setting.renderDetail({ item });
|
|
215
247
|
}
|
|
216
|
-
|
|
217
|
-
// Suppress unused import warning — MetaText may be used in future extensions
|
|
218
|
-
void (MetaText as unknown);
|