claudeup 6.2.1 → 6.3.2
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/README.md +22 -0
- package/bin/claudeup.js +6 -1
- package/package.json +6 -6
- package/src/__tests__/alias-adopt.test.ts +331 -329
- package/src/__tests__/alias-parser.test.ts +384 -376
- package/src/__tests__/alias-shell-writer.test.ts +638 -625
- package/src/__tests__/alias-store.test.ts +143 -143
- package/src/__tests__/catalog-cache-store.test.ts +265 -265
- package/src/__tests__/catalog-notice.test.ts +123 -123
- package/src/__tests__/cli-apply-seams.test.ts +12 -8
- package/src/__tests__/cli-router.test.ts +132 -3
- package/src/__tests__/cli-update-view.test.ts +27 -8
- package/src/__tests__/community-staleness.test.ts +0 -2
- package/src/__tests__/component-badge.test.ts +4 -4
- package/src/__tests__/content-drift.test.ts +5 -1
- package/src/__tests__/conventions-integration.test.ts +774 -689
- package/src/__tests__/conventions-manager.test.ts +1216 -1152
- package/src/__tests__/doctor-bins.test.ts +14 -6
- package/src/__tests__/doctor.test.ts +438 -425
- package/src/__tests__/dual-write-prevention.test.ts +277 -282
- package/src/__tests__/enabled-not-installed.test.ts +71 -63
- package/src/__tests__/file-locking.test.ts +196 -196
- package/src/__tests__/gap-fill-versions.test.ts +318 -311
- package/src/__tests__/github-budget.test.ts +170 -164
- package/src/__tests__/gitignore-detector.test.ts +160 -152
- package/src/__tests__/gitignore-fixer.test.ts +285 -247
- package/src/__tests__/gitignore-prerun.test.ts +57 -57
- package/src/__tests__/gitignore-resolver.test.ts +173 -146
- package/src/__tests__/gitignore-service.test.ts +121 -119
- package/src/__tests__/install-command.test.ts +3 -3
- package/src/__tests__/install-plan.test.ts +13 -7
- package/src/__tests__/manifest.test.ts +16 -5
- package/src/__tests__/markdown-renderer.test.ts +0 -1
- package/src/__tests__/marketplace-badge.test.ts +111 -90
- package/src/__tests__/marketplace-version.test.ts +64 -61
- package/src/__tests__/plugin-manager-fallback.test.ts +249 -239
- package/src/__tests__/plugin-requires.test.ts +18 -4
- package/src/__tests__/plugin-setup.test.ts +12 -12
- package/src/__tests__/profile-command.test.ts +1 -7
- package/src/__tests__/profile-materializer.test.ts +31 -8
- package/src/__tests__/profile-sync.test.ts +23 -5
- package/src/__tests__/registry-version-resolution.test.ts +61 -47
- package/src/__tests__/resolve-executable.test.ts +1 -1
- package/src/__tests__/resolver.test.ts +40 -15
- package/src/__tests__/scope-action.test.ts +52 -50
- package/src/__tests__/scope-squares.test.tsx +4 -5
- package/src/__tests__/skill-install-files.test.ts +111 -103
- package/src/__tests__/symlink-manager.test.ts +27 -10
- package/src/__tests__/theme-env.test.ts +281 -0
- package/src/__tests__/theme-resolve.test.ts +380 -0
- package/src/__tests__/update-apply.test.ts +1 -3
- package/src/__tests__/useGitignoreModal.test.ts +69 -61
- package/src/__tests__/version-snapshot.test.ts +181 -165
- package/src/cli/ansi.ts +23 -15
- package/src/cli/doctor.ts +10 -9
- package/src/cli/install.ts +3 -3
- package/src/cli/profile.ts +5 -4
- package/src/cli/prompt.ts +0 -1
- package/src/cli/router.ts +69 -2
- package/src/cli/update-view.ts +11 -6
- package/src/cli/update.ts +9 -4
- package/src/cli/upgrade.ts +6 -4
- package/src/data/alias-flags.ts +260 -260
- package/src/data/gitignore-defaults.ts +20 -20
- package/src/data/gitignore-reasons.ts +86 -92
- package/src/data/gitignore-templates.ts +14 -14
- package/src/data/predefined-profiles.ts +8 -9
- package/src/data/skill-repos.ts +43 -35
- package/src/main.tsx +29 -6
- package/src/opentui.d.ts +1 -3
- package/src/prerunner/index.ts +7 -8
- package/src/services/alias-settings.ts +20 -23
- package/src/services/alias-shell-writer.ts +800 -803
- package/src/services/alias-store.ts +77 -77
- package/src/services/claude-settings.ts +27 -18
- package/src/services/content-drift.ts +13 -3
- package/src/services/conventions-manager.ts +692 -617
- package/src/services/doctor.ts +464 -403
- package/src/services/gitignore-detector.ts +113 -103
- package/src/services/gitignore-fixer.ts +204 -189
- package/src/services/gitignore-prerun.ts +26 -26
- package/src/services/gitignore-resolver.ts +162 -144
- package/src/services/gitignore-service.ts +120 -117
- package/src/services/install-plan.ts +4 -4
- package/src/services/manifest.ts +1 -1
- package/src/services/marketplace-catalog-git.ts +5 -2
- package/src/services/marketplace-fetcher.ts +241 -235
- package/src/services/plugin-manager.ts +17 -20
- package/src/services/plugin-mcp-config.ts +2 -2
- package/src/services/plugin-requires.ts +9 -6
- package/src/services/plugin-setup.ts +7 -9
- package/src/services/plugin-version-check.ts +4 -4
- package/src/services/profile-materializer.ts +9 -5
- package/src/services/profile-sync.ts +12 -4
- package/src/services/profiles.ts +3 -3
- package/src/services/resolver.ts +10 -6
- package/src/services/settings-manager.ts +82 -46
- package/src/services/skills-manager.ts +11 -9
- package/src/services/symlink-manager.ts +2 -5
- package/src/services/toolchain.ts +3 -3
- package/src/services/update-cache.ts +1 -1
- package/src/services/update-engine.ts +4 -4
- package/src/services/update-plan.ts +9 -11
- package/src/services/version-snapshot.ts +88 -88
- package/src/types/gitignore.ts +28 -28
- package/src/ui/App.tsx +36 -36
- package/src/ui/adapters/settingsAdapter.ts +2 -2
- package/src/ui/components/CategoryHeader.tsx +1 -1
- package/src/ui/components/FlagDetailEditor.tsx +0 -0
- package/src/ui/components/ScopeIndicator.tsx +3 -1
- package/src/ui/components/ScrollableList.tsx +2 -1
- package/src/ui/components/StyledText.tsx +1 -1
- package/src/ui/components/layout/FooterHints.tsx +5 -3
- package/src/ui/components/layout/Panel.tsx +2 -2
- package/src/ui/components/layout/ScreenLayout.tsx +5 -4
- package/src/ui/components/modals/InputModal.tsx +7 -1
- package/src/ui/components/modals/ModalContainer.tsx +3 -3
- package/src/ui/components/modals/SelectModal.tsx +1 -1
- package/src/ui/components/primitives/DetailSection.tsx +1 -1
- package/src/ui/components/primitives/KeyValueLine.tsx +1 -1
- package/src/ui/components/primitives/ScopeSquares.tsx +2 -2
- package/src/ui/hooks/index.ts +0 -1
- package/src/ui/hooks/useGitignoreModal.ts +1 -5
- package/src/ui/hooks/useMismatchModal.ts +4 -10
- package/src/ui/renderers/cliToolRenderers.tsx +2 -2
- package/src/ui/renderers/gitignoreRenderers.tsx +2 -2
- package/src/ui/renderers/mcpRenderers.tsx +1 -1
- package/src/ui/renderers/pluginRenderers.tsx +3 -9
- package/src/ui/renderers/profileRenderers.tsx +9 -8
- package/src/ui/renderers/settingsRenderers.tsx +4 -4
- package/src/ui/renderers/skillRenderers.tsx +14 -14
- package/src/ui/screens/AliasScreen.tsx +31 -34
- package/src/ui/screens/CliToolsScreen.tsx +14 -13
- package/src/ui/screens/EnvVarsScreen.tsx +8 -8
- package/src/ui/screens/GitignoreScreen.tsx +15 -14
- package/src/ui/screens/McpRegistryScreen.tsx +10 -7
- package/src/ui/screens/McpScreen.tsx +10 -10
- package/src/ui/screens/PluginsScreen.tsx +17 -21
- package/src/ui/screens/ProfilesScreen.tsx +25 -24
- package/src/ui/screens/SkillsScreen.tsx +16 -14
- package/src/ui/state/AppContext.tsx +4 -8
- package/src/ui/state/DimensionsContext.tsx +3 -2
- package/src/ui/theme-mode.ts +20 -38
- package/src/ui/theme-resolve.ts +148 -0
- package/src/utils/clipboard.ts +5 -5
- package/src/utils/command-utils.ts +2 -2
- package/src/utils/config-dir.ts +1 -3
- package/src/utils/file-locking.ts +88 -86
- package/src/ui/hooks/useAsyncData.ts +0 -127
- package/src/ui/state/AnimationContext.tsx +0 -76
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { useCallback } from "react";
|
|
2
|
-
import { useApp } from "../state/AppContext.js";
|
|
3
2
|
import {
|
|
4
|
-
fixAllPluginVersionMismatches,
|
|
5
3
|
type VersionMismatchInfo,
|
|
4
|
+
fixAllPluginVersionMismatches,
|
|
6
5
|
} from "../../services/plugin-version-check.js";
|
|
6
|
+
import { useApp } from "../state/AppContext.js";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Shared hook for showing the plugin version mismatch modal.
|
|
@@ -52,11 +52,7 @@ export function useMismatchModal() {
|
|
|
52
52
|
modal: {
|
|
53
53
|
type: "message",
|
|
54
54
|
title: "Plugin Versions Aligned",
|
|
55
|
-
message:
|
|
56
|
-
`Updated ${totalUpdated} project entr${totalUpdated === 1 ? "y" : "ies"} in ` +
|
|
57
|
-
`installed_plugins.json.\n\n` +
|
|
58
|
-
`Restart Claude Code for the new versions to load. ` +
|
|
59
|
-
`The running session still has the old plugin code in memory.`,
|
|
55
|
+
message: `Updated ${totalUpdated} project entr${totalUpdated === 1 ? "y" : "ies"} in installed_plugins.json.\n\nRestart Claude Code for the new versions to load. The running session still has the old plugin code in memory.`,
|
|
60
56
|
variant: "success",
|
|
61
57
|
onDismiss: dismiss,
|
|
62
58
|
},
|
|
@@ -68,9 +64,7 @@ export function useMismatchModal() {
|
|
|
68
64
|
modal: {
|
|
69
65
|
type: "message",
|
|
70
66
|
title: "Fix Failed",
|
|
71
|
-
message:
|
|
72
|
-
`Could not align plugin versions: ${error instanceof Error ? error.message : String(error)}\n\n` +
|
|
73
|
-
`Try running claudeup again, or manually update the plugin in each project.`,
|
|
67
|
+
message: `Could not align plugin versions: ${error instanceof Error ? error.message : String(error)}\n\nTry running claudeup again, or manually update the plugin in each project.`,
|
|
74
68
|
variant: "error",
|
|
75
69
|
onDismiss: dismiss,
|
|
76
70
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import type React from "react";
|
|
2
2
|
import type { CliTool } from "../../data/cli-tools.js";
|
|
3
3
|
import {
|
|
4
4
|
type InstallMethod,
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "../../services/cli-tool-commands.js";
|
|
8
8
|
import type { Command } from "../../utils/command-utils.js";
|
|
9
9
|
import { formatCommand } from "../../utils/command-utils.js";
|
|
10
|
-
import {
|
|
10
|
+
import { type UiColor, theme } from "../theme.js";
|
|
11
11
|
|
|
12
12
|
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
13
13
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import type React from "react";
|
|
2
2
|
import type { ResolvedRule, Violation } from "../../types/gitignore.js";
|
|
3
3
|
import { theme } from "../theme.js";
|
|
4
4
|
|
|
@@ -39,7 +39,7 @@ const MARKER_NONE = " ";
|
|
|
39
39
|
const MAX_PATH_LEN = 22;
|
|
40
40
|
|
|
41
41
|
function truncatePath(p: string): string {
|
|
42
|
-
return p.length > MAX_PATH_LEN ? p.slice(0, MAX_PATH_LEN - 1)
|
|
42
|
+
return p.length > MAX_PATH_LEN ? `${p.slice(0, MAX_PATH_LEN - 1)}…` : p;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
export function renderRuleRow(
|
|
@@ -724,18 +724,15 @@ const COMPONENT_NOUN: Record<PluginComponentKind, string> = {
|
|
|
724
724
|
/**
|
|
725
725
|
* The type badge: one glyph in a padded, washed cell.
|
|
726
726
|
*
|
|
727
|
-
*
|
|
727
|
+
* Two states, and each exists for a reason.
|
|
728
728
|
*
|
|
729
729
|
* On a *selected* row the whole line is painted in the selection colour, so the
|
|
730
730
|
* badge drops its own fill — a chip of a third colour inside an inverted row is
|
|
731
731
|
* the noisiest thing on the screen, and the selection already says where you
|
|
732
732
|
* are.
|
|
733
733
|
*
|
|
734
|
-
*
|
|
735
|
-
*
|
|
736
|
-
* unknown page risks a bright band across every row of a long list.
|
|
737
|
-
*
|
|
738
|
-
* Otherwise: a wash of the kind's hue, quiet enough to sit on hundreds of rows.
|
|
734
|
+
* Otherwise: a wash of the kind's hue for the resolved mode, quiet enough to
|
|
735
|
+
* sit on hundreds of rows.
|
|
739
736
|
*/
|
|
740
737
|
function componentBadge(
|
|
741
738
|
kind: PluginComponentKind,
|
|
@@ -745,9 +742,6 @@ function componentBadge(
|
|
|
745
742
|
if (isSelected) return ` ${icon} `;
|
|
746
743
|
|
|
747
744
|
const chip = badgeColors(kind);
|
|
748
|
-
if (!chip) {
|
|
749
|
-
return <span fg={COMPONENT_COLOR[kind]}>{` ${icon} `}</span>;
|
|
750
|
-
}
|
|
751
745
|
return <span bg={chip.bg} fg={chip.fg}>{` ${icon} `}</span>;
|
|
752
746
|
}
|
|
753
747
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import type { ItemRenderer } from "../registry.js";
|
|
3
|
-
import type { ProfileEntry } from "../../types/index.js";
|
|
1
|
+
import type React from "react";
|
|
4
2
|
import type { PredefinedProfile } from "../../data/predefined-profiles.js";
|
|
5
|
-
import {
|
|
3
|
+
import type { ProfileEntry } from "../../types/index.js";
|
|
4
|
+
import type { ItemRenderer } from "../registry.js";
|
|
5
|
+
import { type UiColor, theme } from "../theme.js";
|
|
6
6
|
|
|
7
7
|
// ─── List item types ───────────────────────────────────────────────────────────
|
|
8
8
|
|
|
@@ -15,7 +15,7 @@ export type ProfileListItem =
|
|
|
15
15
|
|
|
16
16
|
function truncate(s: string, maxLen: number): string {
|
|
17
17
|
if (s.length <= maxLen) return s;
|
|
18
|
-
return s.slice(0, maxLen - 1)
|
|
18
|
+
return `${s.slice(0, maxLen - 1)}\u2026`;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export function humanizeValue(_key: string, value: unknown): string {
|
|
@@ -143,9 +143,9 @@ const predefinedRenderer: ItemRenderer<{
|
|
|
143
143
|
([k]) => k !== "env",
|
|
144
144
|
);
|
|
145
145
|
const envMap =
|
|
146
|
-
(profile.settings
|
|
147
|
-
const tasksOn = envMap
|
|
148
|
-
const teamsOn = envMap
|
|
146
|
+
(profile.settings.env as Record<string, string> | undefined) ?? {};
|
|
147
|
+
const tasksOn = envMap.CLAUDE_CODE_ENABLE_TASKS === "true";
|
|
148
|
+
const teamsOn = envMap.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS === "true";
|
|
149
149
|
|
|
150
150
|
return (
|
|
151
151
|
<box flexDirection="column">
|
|
@@ -298,6 +298,7 @@ const savedRenderer: ItemRenderer<{ kind: "saved"; entry: ProfileEntry }> = {
|
|
|
298
298
|
<text fg={theme.colors.warning}> (none)</text>
|
|
299
299
|
) : (
|
|
300
300
|
wrapNames(cleanPlugins).map((line, i) => (
|
|
301
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: word-wrapped lines of a static preview; lines carry no identity and can repeat, so the row number is the only stable key
|
|
301
302
|
<text key={i} fg={theme.colors.text}>
|
|
302
303
|
{" "}
|
|
303
304
|
{line}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import type { ItemRenderer } from "../registry.js";
|
|
1
|
+
import type React from "react";
|
|
3
2
|
import type {
|
|
4
3
|
SettingsBrowserItem,
|
|
5
4
|
SettingsCategoryItem,
|
|
6
5
|
SettingsSettingItem,
|
|
7
6
|
} from "../adapters/settingsAdapter.js";
|
|
8
7
|
import {
|
|
9
|
-
CATEGORY_LABELS,
|
|
10
8
|
CATEGORY_BG,
|
|
11
9
|
CATEGORY_COLOR,
|
|
12
10
|
CATEGORY_DESCRIPTIONS,
|
|
11
|
+
CATEGORY_LABELS,
|
|
13
12
|
formatValue,
|
|
14
13
|
} from "../adapters/settingsAdapter.js";
|
|
15
|
-
import {
|
|
14
|
+
import { MetaText, SelectableRow } from "../components/primitives/index.js";
|
|
15
|
+
import type { ItemRenderer } from "../registry.js";
|
|
16
16
|
import { theme } from "../theme.js";
|
|
17
17
|
|
|
18
18
|
// ─── Category renderer ─────────────────────────────────────────────────────────
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import
|
|
1
|
+
import type React from "react";
|
|
2
|
+
import { STAR_RELIABILITY_INFO } from "../../data/skill-repos.js";
|
|
3
|
+
import type { StarReliability } from "../../types/index.js";
|
|
3
4
|
import type {
|
|
4
5
|
SkillBrowserItem,
|
|
5
6
|
SkillCategoryItem,
|
|
6
|
-
SkillSkillItem,
|
|
7
7
|
SkillSetItem,
|
|
8
|
+
SkillSkillItem,
|
|
8
9
|
} from "../adapters/skillsAdapter.js";
|
|
9
|
-
import type { StarReliability } from "../../types/index.js";
|
|
10
|
-
import { STAR_RELIABILITY_INFO } from "../../data/skill-repos.js";
|
|
11
10
|
import {
|
|
12
|
-
SelectableRow,
|
|
13
|
-
ListCategoryRow,
|
|
14
|
-
ScopeSquares,
|
|
15
|
-
ScopeDetail,
|
|
16
11
|
ActionHints,
|
|
17
|
-
MetaText,
|
|
18
|
-
KeyValueLine,
|
|
19
12
|
DetailSection,
|
|
13
|
+
KeyValueLine,
|
|
14
|
+
ListCategoryRow,
|
|
15
|
+
MetaText,
|
|
16
|
+
ScopeDetail,
|
|
17
|
+
ScopeSquares,
|
|
18
|
+
SelectableRow,
|
|
20
19
|
} from "../components/primitives/index.js";
|
|
21
|
-
import {
|
|
20
|
+
import type { ItemRenderer } from "../registry.js";
|
|
21
|
+
import { type UiColor, theme } from "../theme.js";
|
|
22
22
|
|
|
23
23
|
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
24
24
|
|
|
@@ -92,7 +92,7 @@ const MAX_SKILL_NAME_LEN = 35;
|
|
|
92
92
|
|
|
93
93
|
function truncateName(name: string): string {
|
|
94
94
|
return name.length > MAX_SKILL_NAME_LEN
|
|
95
|
-
? name.slice(0, MAX_SKILL_NAME_LEN - 1)
|
|
95
|
+
? `${name.slice(0, MAX_SKILL_NAME_LEN - 1)}\u2026`
|
|
96
96
|
: name;
|
|
97
97
|
}
|
|
98
98
|
|
|
@@ -282,7 +282,7 @@ const skillSetRenderer: ItemRenderer<SkillSetItem> = {
|
|
|
282
282
|
const arrow = expanded ? "\u25BC" : "\u25B6";
|
|
283
283
|
const displayName =
|
|
284
284
|
skillSet.name.length > MAX_SET_NAME_LEN
|
|
285
|
-
? skillSet.name.slice(0, MAX_SET_NAME_LEN - 1)
|
|
285
|
+
? `${skillSet.name.slice(0, MAX_SET_NAME_LEN - 1)}\u2026`
|
|
286
286
|
: skillSet.name;
|
|
287
287
|
|
|
288
288
|
// Count installed child skills
|
|
@@ -1,46 +1,41 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
useState,
|
|
4
|
-
useCallback,
|
|
5
|
-
useMemo,
|
|
6
|
-
useRef,
|
|
7
|
-
} from "react";
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
8
3
|
import type { ScrollBoxRenderable } from "@opentui/core";
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
import { ScreenLayout, type FooterHint } from "../components/layout/index.js";
|
|
12
|
-
import { FlagDetailEditor } from "../components/FlagDetailEditor.js";
|
|
4
|
+
import type React from "react";
|
|
5
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
13
6
|
import {
|
|
14
7
|
ALIAS_FLAGS,
|
|
15
|
-
FLAG_GROUPS,
|
|
16
8
|
type AliasFlag,
|
|
9
|
+
FLAG_GROUPS,
|
|
17
10
|
} from "../../data/alias-flags.js";
|
|
18
|
-
import {
|
|
19
|
-
defaultAliasConfig,
|
|
20
|
-
defaultValueFor,
|
|
21
|
-
validateAliasName,
|
|
22
|
-
derivedChannelValues,
|
|
23
|
-
withoutDerivedChannel,
|
|
24
|
-
CHANNELS_FLAG_ID,
|
|
25
|
-
DEFAULT_ALIAS_NAME,
|
|
26
|
-
type AliasConfig,
|
|
27
|
-
type FlagValue,
|
|
28
|
-
} from "../../services/alias-store.js";
|
|
29
11
|
import { loadAliasName, saveAliasName } from "../../services/alias-settings.js";
|
|
30
12
|
import {
|
|
13
|
+
type AdoptableAlias,
|
|
14
|
+
type FlagValidationIssue,
|
|
15
|
+
type Segment,
|
|
16
|
+
type ShellTarget,
|
|
31
17
|
detectShells,
|
|
18
|
+
findAdoptableAlias,
|
|
19
|
+
parseAliasFromRc,
|
|
32
20
|
renderArgs,
|
|
33
21
|
validateConfig,
|
|
34
22
|
writeAliasToShell,
|
|
35
|
-
parseAliasFromRc,
|
|
36
|
-
findAdoptableAlias,
|
|
37
|
-
type ShellTarget,
|
|
38
|
-
type FlagValidationIssue,
|
|
39
|
-
type Segment,
|
|
40
|
-
type AdoptableAlias,
|
|
41
23
|
} from "../../services/alias-shell-writer.js";
|
|
42
|
-
import {
|
|
43
|
-
|
|
24
|
+
import {
|
|
25
|
+
type AliasConfig,
|
|
26
|
+
CHANNELS_FLAG_ID,
|
|
27
|
+
DEFAULT_ALIAS_NAME,
|
|
28
|
+
type FlagValue,
|
|
29
|
+
defaultAliasConfig,
|
|
30
|
+
defaultValueFor,
|
|
31
|
+
derivedChannelValues,
|
|
32
|
+
validateAliasName,
|
|
33
|
+
withoutDerivedChannel,
|
|
34
|
+
} from "../../services/alias-store.js";
|
|
35
|
+
import { FlagDetailEditor } from "../components/FlagDetailEditor.js";
|
|
36
|
+
import { type FooterHint, ScreenLayout } from "../components/layout/index.js";
|
|
37
|
+
import { useKeyboardHandler } from "../hooks/useKeyboardHandler.js";
|
|
38
|
+
import { useApp, useModal } from "../state/AppContext.js";
|
|
44
39
|
import { theme } from "../theme.js";
|
|
45
40
|
|
|
46
41
|
interface FlagRow {
|
|
@@ -463,8 +458,7 @@ export function AliasScreen() {
|
|
|
463
458
|
// enough today; recurse defensively in case a chain emerges).
|
|
464
459
|
const queue = [id];
|
|
465
460
|
const seen = new Set<string>();
|
|
466
|
-
|
|
467
|
-
const cur = queue.shift()!;
|
|
461
|
+
for (let cur = queue.shift(); cur !== undefined; cur = queue.shift()) {
|
|
468
462
|
if (seen.has(cur)) continue;
|
|
469
463
|
seen.add(cur);
|
|
470
464
|
for (const dependent of ALIAS_FLAGS) {
|
|
@@ -1052,7 +1046,7 @@ function summarizeValue(value: FlagValue): string {
|
|
|
1052
1046
|
}
|
|
1053
1047
|
|
|
1054
1048
|
function truncate(s: string, n: number): string {
|
|
1055
|
-
return s.length <= n ? s : s.slice(0, n - 1)
|
|
1049
|
+
return s.length <= n ? s : `${s.slice(0, n - 1)}…`;
|
|
1056
1050
|
}
|
|
1057
1051
|
|
|
1058
1052
|
/**
|
|
@@ -1447,6 +1441,7 @@ function renderPreviewLines(
|
|
|
1447
1441
|
];
|
|
1448
1442
|
segments.forEach((seg, i) => {
|
|
1449
1443
|
lines.push(
|
|
1444
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: alias arguments are positional by definition and identical tokens can legitimately repeat; the preview re-renders whole
|
|
1450
1445
|
<text key={`arg-${i}`} fg={theme.colors.text}>
|
|
1451
1446
|
{" "}
|
|
1452
1447
|
{renderPreviewSegment(seg)}
|
|
@@ -1472,12 +1467,14 @@ function renderPreviewSegment(seg: Segment): React.ReactNode {
|
|
|
1472
1467
|
seg.parts.forEach((p, i) => {
|
|
1473
1468
|
if (p.kind === "literal") {
|
|
1474
1469
|
spans.push(
|
|
1470
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: parts of one composite argument are positional text fragments with no identity of their own
|
|
1475
1471
|
<span key={`p-${i}`} fg={theme.colors.text}>
|
|
1476
1472
|
{p.text}
|
|
1477
1473
|
</span>,
|
|
1478
1474
|
);
|
|
1479
1475
|
} else {
|
|
1480
1476
|
spans.push(
|
|
1477
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: same positional fragment list as above
|
|
1481
1478
|
<span key={`p-${i}`} fg={theme.colors.info}>
|
|
1482
1479
|
{substitutionToToken(p.posix)}
|
|
1483
1480
|
</span>,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { exec, execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
1
3
|
import React, { useEffect, useCallback, useState, useRef } from "react";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
+
import { ScreenLayout } from "../components/layout/index.js";
|
|
5
|
+
import { useKeyboard } from "../hooks/useKeyboard.js";
|
|
4
6
|
import { useApp, useModal } from "../state/AppContext.js";
|
|
5
7
|
import { useDimensions } from "../state/DimensionsContext.js";
|
|
6
|
-
import { useKeyboard } from "../hooks/useKeyboard.js";
|
|
7
|
-
import { ScreenLayout } from "../components/layout/index.js";
|
|
8
8
|
|
|
9
9
|
import { cliTools } from "../../data/cli-tools.js";
|
|
10
10
|
import {
|
|
@@ -15,12 +15,12 @@ import {
|
|
|
15
15
|
} from "../../services/cli-tool-commands.js";
|
|
16
16
|
import type { Command } from "../../utils/command-utils.js";
|
|
17
17
|
import { formatCommand } from "../../utils/command-utils.js";
|
|
18
|
+
import { ScrollableList } from "../components/ScrollableList.js";
|
|
18
19
|
import {
|
|
19
|
-
renderCliToolRow,
|
|
20
|
-
renderCliToolDetail,
|
|
21
20
|
type CliToolStatus,
|
|
21
|
+
renderCliToolDetail,
|
|
22
|
+
renderCliToolRow,
|
|
22
23
|
} from "../renderers/cliToolRenderers.js";
|
|
23
|
-
import { ScrollableList } from "../components/ScrollableList.js";
|
|
24
24
|
import { theme } from "../theme.js";
|
|
25
25
|
|
|
26
26
|
const execAsync = promisify(exec);
|
|
@@ -123,9 +123,10 @@ async function detectInstallMethods(
|
|
|
123
123
|
if (method && !methods.includes(method)) methods.push(method);
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
|
|
126
|
+
const primary = methods[0];
|
|
127
|
+
if (primary === undefined)
|
|
127
128
|
return { primary: fallback as InstallMethod, all: [] };
|
|
128
|
-
return { primary
|
|
129
|
+
return { primary, all: methods, brewFormula };
|
|
129
130
|
} catch {
|
|
130
131
|
return { primary: fallback as InstallMethod, all: [] };
|
|
131
132
|
}
|
|
@@ -212,8 +213,8 @@ async function getLatestVersion(
|
|
|
212
213
|
}
|
|
213
214
|
|
|
214
215
|
function compareVersions(v1: string, v2: string): number {
|
|
215
|
-
const parts1 = v1.split(/[-.]/).map((p) => parseInt(p, 10) || 0);
|
|
216
|
-
const parts2 = v2.split(/[-.]/).map((p) => parseInt(p, 10) || 0);
|
|
216
|
+
const parts1 = v1.split(/[-.]/).map((p) => Number.parseInt(p, 10) || 0);
|
|
217
|
+
const parts2 = v2.split(/[-.]/).map((p) => Number.parseInt(p, 10) || 0);
|
|
217
218
|
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
218
219
|
const p1 = parts1[i] || 0;
|
|
219
220
|
const p2 = parts2[i] || 0;
|
|
@@ -250,7 +251,7 @@ export function CliToolsScreen() {
|
|
|
250
251
|
(index: number, updates: Partial<CliToolStatus>) => {
|
|
251
252
|
setToolStatuses((prev) => {
|
|
252
253
|
const newStatuses = [...prev];
|
|
253
|
-
newStatuses[index] = { ...newStatuses[index]
|
|
254
|
+
newStatuses[index] = { ...newStatuses[index], ...updates };
|
|
254
255
|
cachedToolStatuses = newStatuses;
|
|
255
256
|
return newStatuses;
|
|
256
257
|
});
|
|
@@ -260,7 +261,7 @@ export function CliToolsScreen() {
|
|
|
260
261
|
|
|
261
262
|
const fetchVersionInfo = useCallback(async () => {
|
|
262
263
|
for (let i = 0; i < cliTools.length; i++) {
|
|
263
|
-
const tool = cliTools[i]
|
|
264
|
+
const tool = cliTools[i];
|
|
264
265
|
|
|
265
266
|
// Run all checks in parallel, then update with all results
|
|
266
267
|
Promise.all([
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import React, { useEffect, useCallback, useMemo } from "react";
|
|
2
|
-
import { useApp, useModal } from "../state/AppContext.js";
|
|
3
|
-
import { useDimensions } from "../state/DimensionsContext.js";
|
|
4
|
-
import { useKeyboard } from "../hooks/useKeyboard.js";
|
|
5
|
-
import { ScreenLayout } from "../components/layout/index.js";
|
|
6
2
|
import { SETTINGS_CATALOG } from "../../data/settings-catalog.js";
|
|
7
3
|
import {
|
|
4
|
+
discoverOutputStyles,
|
|
8
5
|
readAllSettingsBothScopes,
|
|
9
6
|
writeSettingValue,
|
|
10
|
-
discoverOutputStyles,
|
|
11
7
|
} from "../../services/settings-manager.js";
|
|
12
8
|
import {
|
|
13
|
-
buildSettingsBrowserItems,
|
|
14
9
|
type SettingsBrowserItem,
|
|
10
|
+
buildSettingsBrowserItems,
|
|
15
11
|
} from "../adapters/settingsAdapter.js";
|
|
12
|
+
import { ScrollableList } from "../components/ScrollableList.js";
|
|
13
|
+
import { ScreenLayout } from "../components/layout/index.js";
|
|
14
|
+
import { useKeyboard } from "../hooks/useKeyboard.js";
|
|
16
15
|
import {
|
|
17
|
-
renderSettingRow,
|
|
18
16
|
renderSettingDetail,
|
|
17
|
+
renderSettingRow,
|
|
19
18
|
} from "../renderers/settingsRenderers.js";
|
|
20
|
-
import {
|
|
19
|
+
import { useApp, useModal } from "../state/AppContext.js";
|
|
20
|
+
import { useDimensions } from "../state/DimensionsContext.js";
|
|
21
21
|
import { theme } from "../theme.js";
|
|
22
22
|
|
|
23
23
|
export function SettingsScreen() {
|
|
@@ -1,24 +1,25 @@
|
|
|
1
|
-
import React, { useEffect, useState, useCallback, useMemo } from "react";
|
|
2
1
|
import { spawn } from "node:child_process";
|
|
3
2
|
import { appendFileSync } from "node:fs";
|
|
4
3
|
import { useRenderer } from "@opentui/react";
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
4
|
+
import type React from "react";
|
|
5
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
6
|
+
import { getGitignoreRuleReason } from "../../data/gitignore-reasons.js";
|
|
7
|
+
import { BUILTIN_TEMPLATES } from "../../data/gitignore-templates.js";
|
|
8
8
|
import {
|
|
9
|
-
|
|
9
|
+
type GitignoreState,
|
|
10
10
|
applyDestructiveFix,
|
|
11
|
-
ensureProjectManifestForEdit,
|
|
12
11
|
applyTemplate,
|
|
13
|
-
|
|
12
|
+
ensureProjectManifestForEdit,
|
|
13
|
+
loadGitignoreState,
|
|
14
14
|
} from "../../services/gitignore-service.js";
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
15
|
+
import type { ResolvedRule, Tier, Violation } from "../../types/gitignore.js";
|
|
16
|
+
import { ScreenLayout } from "../components/layout/index.js";
|
|
17
|
+
import { useKeyboardHandler } from "../hooks/useKeyboardHandler.js";
|
|
17
18
|
import {
|
|
18
|
-
renderRuleRow,
|
|
19
19
|
type RuleRow,
|
|
20
|
+
renderRuleRow,
|
|
20
21
|
} from "../renderers/gitignoreRenderers.js";
|
|
21
|
-
import
|
|
22
|
+
import { useApp, useModal } from "../state/AppContext.js";
|
|
22
23
|
import { theme } from "../theme.js";
|
|
23
24
|
|
|
24
25
|
// Clean rows are grouped by the manifest that defined them. Order = display
|
|
@@ -229,9 +230,7 @@ export function GitignoreScreen() {
|
|
|
229
230
|
: "";
|
|
230
231
|
await modal.message(
|
|
231
232
|
"Template applied",
|
|
232
|
-
`Wrote ${r.appliedTo}\n\
|
|
233
|
-
`Added ${r.added.ignore.length} ignore + ${r.added.track.length} track entries.` +
|
|
234
|
-
conflictNote,
|
|
233
|
+
`Wrote ${r.appliedTo}\n\nAdded ${r.added.ignore.length} ignore + ${r.added.track.length} track entries.${conflictNote}`,
|
|
235
234
|
"success",
|
|
236
235
|
);
|
|
237
236
|
} catch (err) {
|
|
@@ -436,6 +435,7 @@ function renderDetail(
|
|
|
436
435
|
<box flexDirection="column" marginTop={1}>
|
|
437
436
|
<text fg={theme.colors.warning}>Warnings:</text>
|
|
438
437
|
{warnings.map((w, i) => (
|
|
438
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: warnings are plain strings that may repeat and are replaced as a whole list; the index is the only stable key
|
|
439
439
|
<text fg={theme.colors.muted} key={i}>
|
|
440
440
|
{w}
|
|
441
441
|
</text>
|
|
@@ -552,6 +552,7 @@ function renderWarnings(warnings: string[]): React.ReactNode {
|
|
|
552
552
|
<box flexDirection="column" marginTop={1}>
|
|
553
553
|
<text fg={theme.colors.warning}>Warnings:</text>
|
|
554
554
|
{warnings.map((w, i) => (
|
|
555
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: warnings are plain strings that may repeat and are replaced as a whole list; the index is the only stable key
|
|
555
556
|
<text fg={theme.colors.muted} key={i}>
|
|
556
557
|
{w}
|
|
557
558
|
</text>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import React, { useEffect, useCallback, useState, useRef } from "react";
|
|
2
|
+
import { ScreenLayout } from "../components/layout/index.js";
|
|
3
|
+
import { useKeyboard } from "../hooks/useKeyboard.js";
|
|
2
4
|
import { useApp, useModal, useNavigation } from "../state/AppContext.js";
|
|
3
5
|
import { useDimensions } from "../state/DimensionsContext.js";
|
|
4
|
-
import { useKeyboard } from "../hooks/useKeyboard.js";
|
|
5
|
-
import { ScreenLayout } from "../components/layout/index.js";
|
|
6
6
|
|
|
7
|
-
import { searchMcpServers, formatDate } from "../../services/mcp-registry.js";
|
|
8
7
|
import { addMcpServer, setAllowMcp } from "../../services/claude-settings.js";
|
|
8
|
+
import { formatDate, searchMcpServers } from "../../services/mcp-registry.js";
|
|
9
9
|
import type { McpRegistryServer, McpServerConfig } from "../../types/index.js";
|
|
10
10
|
import { ScrollableList } from "../components/ScrollableList.js";
|
|
11
11
|
import { theme } from "../theme.js";
|
|
@@ -43,8 +43,8 @@ function deduplicateServers(servers: McpRegistryServer[]): McpRegistryServer[] {
|
|
|
43
43
|
function compareVersions(a: McpRegistryServer, b: McpRegistryServer): number {
|
|
44
44
|
// Try semver comparison first
|
|
45
45
|
if (a.version && b.version) {
|
|
46
|
-
const aParts = a.version.split(".").map((n) => parseInt(n, 10) || 0);
|
|
47
|
-
const bParts = b.version.split(".").map((n) => parseInt(n, 10) || 0);
|
|
46
|
+
const aParts = a.version.split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
47
|
+
const bParts = b.version.split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
48
48
|
|
|
49
49
|
for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
|
|
50
50
|
const aVal = aParts[i] || 0;
|
|
@@ -120,14 +120,17 @@ export function McpRegistryScreen() {
|
|
|
120
120
|
[loadServers],
|
|
121
121
|
);
|
|
122
122
|
|
|
123
|
+
// Initial load runs once with the query the screen opened with; later edits
|
|
124
|
+
// go through debouncedSearch, so the live query is deliberately not a dep.
|
|
125
|
+
const initialQueryRef = useRef(searchQuery);
|
|
123
126
|
useEffect(() => {
|
|
124
|
-
loadServers(
|
|
127
|
+
loadServers(initialQueryRef.current);
|
|
125
128
|
return () => {
|
|
126
129
|
if (searchTimeoutRef.current) {
|
|
127
130
|
clearTimeout(searchTimeoutRef.current);
|
|
128
131
|
}
|
|
129
132
|
};
|
|
130
|
-
}, []);
|
|
133
|
+
}, [loadServers]);
|
|
131
134
|
|
|
132
135
|
// Keyboard handling
|
|
133
136
|
useKeyboard((event) => {
|
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
import React, { useEffect, useCallback, useMemo } from "react";
|
|
2
|
+
import { ScreenLayout } from "../components/layout/index.js";
|
|
3
|
+
import { useKeyboard } from "../hooks/useKeyboard.js";
|
|
2
4
|
import { useApp, useModal, useNavigation } from "../state/AppContext.js";
|
|
3
5
|
import { useDimensions } from "../state/DimensionsContext.js";
|
|
4
|
-
import { useKeyboard } from "../hooks/useKeyboard.js";
|
|
5
|
-
import { ScreenLayout } from "../components/layout/index.js";
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
|
-
getMcpServersByCategory,
|
|
9
|
-
getCategoryDisplayName,
|
|
10
8
|
categoryOrder,
|
|
9
|
+
getCategoryDisplayName,
|
|
10
|
+
getMcpServersByCategory,
|
|
11
11
|
} from "../../data/mcp-servers.js";
|
|
12
|
-
import { fetchCuratedMcpServers } from "../../services/mcp-registry.js";
|
|
13
12
|
import {
|
|
14
13
|
addMcpServer,
|
|
15
|
-
removeMcpServer,
|
|
16
|
-
getInstalledMcpServers,
|
|
17
14
|
getEnabledMcpServers,
|
|
15
|
+
getInstalledMcpServers,
|
|
16
|
+
removeMcpServer,
|
|
18
17
|
} from "../../services/claude-settings.js";
|
|
18
|
+
import { fetchCuratedMcpServers } from "../../services/mcp-registry.js";
|
|
19
19
|
import type { McpServer, McpServerConfig } from "../../types/index.js";
|
|
20
|
+
import { ScrollableList } from "../components/ScrollableList.js";
|
|
20
21
|
import {
|
|
21
|
-
renderMcpRow,
|
|
22
|
-
renderMcpDetail,
|
|
23
22
|
type McpListItem,
|
|
23
|
+
renderMcpDetail,
|
|
24
|
+
renderMcpRow,
|
|
24
25
|
} from "../renderers/mcpRenderers.js";
|
|
25
|
-
import { ScrollableList } from "../components/ScrollableList.js";
|
|
26
26
|
|
|
27
27
|
export function McpScreen() {
|
|
28
28
|
const { state, dispatch } = useApp();
|