claudeup 6.2.1 → 6.3.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/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 +38 -13
- 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 +183 -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 +1 -2
- 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
|
@@ -19,18 +19,18 @@ import { ALIAS_FLAGS, type AliasFlag } from "../data/alias-flags.js";
|
|
|
19
19
|
* Per-flag value shapes. Discriminator is `kind`, mirroring `AliasFlag.kind`.
|
|
20
20
|
*/
|
|
21
21
|
export type FlagValue =
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
22
|
+
| { kind: "boolean"; enabled: boolean }
|
|
23
|
+
| { kind: "tri-state"; state: "unset" | "on" | "off" }
|
|
24
|
+
| { kind: "select"; enabled: boolean; value: string }
|
|
25
|
+
| { kind: "text"; enabled: boolean; value: string }
|
|
26
|
+
| { kind: "optional-text"; enabled: boolean; value: string }
|
|
27
|
+
| { kind: "text-list"; enabled: boolean; values: string[] }
|
|
28
|
+
| {
|
|
29
|
+
kind: "multi-with-custom";
|
|
30
|
+
enabled: boolean;
|
|
31
|
+
picked: string[];
|
|
32
|
+
custom: string[];
|
|
33
|
+
};
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* In-memory alias configuration. No `version` field since this isn't
|
|
@@ -38,13 +38,13 @@ export type FlagValue =
|
|
|
38
38
|
* and flag values come from the rc file.
|
|
39
39
|
*/
|
|
40
40
|
export interface AliasConfig {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Name of the shell alias. Default `"c"` so users can keep typing the
|
|
43
|
+
* vanilla `claude` command alongside the wrapped one.
|
|
44
|
+
*/
|
|
45
|
+
aliasName: string;
|
|
46
|
+
/** Per-flag state, keyed by `AliasFlag.id`. */
|
|
47
|
+
flags: Record<string, FlagValue>;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/** Default alias name. POSIX/fish-safe, short, and not in conflict with `claude`. */
|
|
@@ -59,12 +59,12 @@ export const DEFAULT_ALIAS_NAME = "c";
|
|
|
59
59
|
* set, and avoids the need to quote the name on either side of the `=`.
|
|
60
60
|
*/
|
|
61
61
|
export function validateAliasName(name: string): string | null {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
if (name.length === 0) return "Alias name cannot be empty.";
|
|
63
|
+
if (name.length > 32) return "Alias name is unreasonably long (>32 chars).";
|
|
64
|
+
if (!/^[A-Za-z_][A-Za-z0-9_-]*$/.test(name)) {
|
|
65
|
+
return "Alias name must start with a letter or underscore and contain only letters, digits, _ or -.";
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
/**
|
|
@@ -72,11 +72,11 @@ export function validateAliasName(name: string): string | null {
|
|
|
72
72
|
* the rc file (every flag disabled, default alias name).
|
|
73
73
|
*/
|
|
74
74
|
export function defaultAliasConfig(): AliasConfig {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
75
|
+
const flags: Record<string, FlagValue> = {};
|
|
76
|
+
for (const flag of ALIAS_FLAGS) {
|
|
77
|
+
flags[flag.id] = defaultValueFor(flag);
|
|
78
|
+
}
|
|
79
|
+
return { aliasName: DEFAULT_ALIAS_NAME, flags };
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
// ─── Channels ⊇ dev-load (derive-on-display) ───────────────────────────
|
|
@@ -99,7 +99,7 @@ export const DEVLOAD_FLAG_ID = "dangerously-load-development-channels";
|
|
|
99
99
|
|
|
100
100
|
/** Read the value list off a flag value, or `[]` for non-list kinds. */
|
|
101
101
|
export function listValues(v: FlagValue | undefined): string[] {
|
|
102
|
-
|
|
102
|
+
return v && v.kind === "text-list" ? v.values : [];
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
/**
|
|
@@ -108,11 +108,11 @@ export function listValues(v: FlagValue | undefined): string[] {
|
|
|
108
108
|
* flag is enabled (a disabled dev-load contributes nothing to the alias).
|
|
109
109
|
*/
|
|
110
110
|
export function derivedChannelValues(
|
|
111
|
-
|
|
111
|
+
flags: Record<string, FlagValue>,
|
|
112
112
|
): string[] {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
const devload = flags[DEVLOAD_FLAG_ID];
|
|
114
|
+
if (!devload || devload.kind !== "text-list" || !devload.enabled) return [];
|
|
115
|
+
return devload.values;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
/**
|
|
@@ -123,50 +123,50 @@ export function derivedChannelValues(
|
|
|
123
123
|
* it stops emitting. Pure — leaves `channels.values` untouched.
|
|
124
124
|
*/
|
|
125
125
|
export function withoutDerivedChannel(
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
flags: Record<string, FlagValue>,
|
|
127
|
+
value: string,
|
|
128
128
|
): Record<string, FlagValue> {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
129
|
+
const cur = flags[DEVLOAD_FLAG_ID];
|
|
130
|
+
if (!cur || cur.kind !== "text-list") return flags;
|
|
131
|
+
const values = cur.values.filter((v) => v !== value);
|
|
132
|
+
return {
|
|
133
|
+
...flags,
|
|
134
|
+
[DEVLOAD_FLAG_ID]: {
|
|
135
|
+
kind: "text-list",
|
|
136
|
+
enabled: values.length > 0 && cur.enabled,
|
|
137
|
+
values,
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
export function defaultValueFor(flag: AliasFlag): FlagValue {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
143
|
+
switch (flag.kind) {
|
|
144
|
+
case "boolean":
|
|
145
|
+
return { kind: "boolean", enabled: false };
|
|
146
|
+
case "tri-state":
|
|
147
|
+
return { kind: "tri-state", state: "unset" };
|
|
148
|
+
case "select":
|
|
149
|
+
return {
|
|
150
|
+
kind: "select",
|
|
151
|
+
enabled: false,
|
|
152
|
+
value: flag.options?.[0]?.value ?? "",
|
|
153
|
+
};
|
|
154
|
+
case "text":
|
|
155
|
+
return { kind: "text", enabled: false, value: "" };
|
|
156
|
+
case "optional-text":
|
|
157
|
+
return { kind: "optional-text", enabled: false, value: "" };
|
|
158
|
+
case "text-list":
|
|
159
|
+
return {
|
|
160
|
+
kind: "text-list",
|
|
161
|
+
enabled: false,
|
|
162
|
+
values: [...(flag.defaultValues ?? [])],
|
|
163
|
+
};
|
|
164
|
+
case "multi-with-custom":
|
|
165
|
+
return {
|
|
166
|
+
kind: "multi-with-custom",
|
|
167
|
+
enabled: false,
|
|
168
|
+
picked: [],
|
|
169
|
+
custom: [...(flag.defaultValues ?? [])],
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
172
|
}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import fs from "fs-extra";
|
|
2
|
-
import path from "node:path";
|
|
3
1
|
import os from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import fs from "fs-extra";
|
|
4
4
|
import semver from "semver";
|
|
5
5
|
import type {
|
|
6
|
-
ClaudeSettings,
|
|
7
6
|
ClaudeLocalSettings,
|
|
8
|
-
|
|
9
|
-
MarketplaceSource,
|
|
7
|
+
ClaudeSettings,
|
|
10
8
|
DiscoveredMarketplace,
|
|
11
|
-
InstalledPluginsRegistry,
|
|
12
9
|
InstalledPluginEntry,
|
|
10
|
+
InstalledPluginsRegistry,
|
|
11
|
+
MarketplaceSource,
|
|
12
|
+
McpServerConfig,
|
|
13
13
|
} from "../types/index.js";
|
|
14
14
|
import { parsePluginId } from "../utils/string-utils.js";
|
|
15
|
-
import { inheritablePaths } from "./git-worktree.js";
|
|
16
15
|
import { repoHeadSha } from "./content-drift.js";
|
|
16
|
+
import { inheritablePaths } from "./git-worktree.js";
|
|
17
17
|
|
|
18
18
|
const CLAUDE_DIR = ".claude";
|
|
19
19
|
const SETTINGS_FILE = "settings.json";
|
|
@@ -143,6 +143,7 @@ export async function addMcpServer(
|
|
|
143
143
|
// Extract env vars from config - they go to settings.local.json, not .mcp.json
|
|
144
144
|
const envVars = config.env || {};
|
|
145
145
|
const configWithoutEnv: McpServerConfig = { ...config };
|
|
146
|
+
// biome-ignore lint/performance/noDelete: this object is serialised into .mcp.json; the env key must be absent, not present as undefined
|
|
146
147
|
delete configWithoutEnv.env;
|
|
147
148
|
|
|
148
149
|
// Add to .mcp.json (without env vars)
|
|
@@ -437,13 +438,18 @@ export async function removeLocalInstalledPluginVersion(
|
|
|
437
438
|
// or { type: "command", command: "..." }. Writing a bare string causes a validation error
|
|
438
439
|
// that skips the entire settings file.
|
|
439
440
|
|
|
440
|
-
function wrapStatusLine(
|
|
441
|
+
function wrapStatusLine(
|
|
442
|
+
template: string,
|
|
443
|
+
): { type: string; template: string } | undefined {
|
|
441
444
|
if (!template) return undefined;
|
|
442
445
|
return { type: "template", template };
|
|
443
446
|
}
|
|
444
447
|
|
|
445
448
|
function unwrapStatusLine(
|
|
446
|
-
value:
|
|
449
|
+
value:
|
|
450
|
+
| string
|
|
451
|
+
| { type: string; template?: string; command?: string }
|
|
452
|
+
| undefined,
|
|
447
453
|
): string | undefined {
|
|
448
454
|
if (!value) return undefined;
|
|
449
455
|
if (typeof value === "string") return value; // legacy format
|
|
@@ -459,6 +465,7 @@ export async function setStatusLine(
|
|
|
459
465
|
if (wrapped) {
|
|
460
466
|
settings.statusLine = wrapped;
|
|
461
467
|
} else {
|
|
468
|
+
// biome-ignore lint/performance/noDelete: settings is written back to settings.json; the key must be removed from the file, and readers check `Object.keys`/`in`
|
|
462
469
|
delete settings.statusLine;
|
|
463
470
|
}
|
|
464
471
|
await writeSettings(settings, projectPath);
|
|
@@ -478,6 +485,7 @@ export async function setGlobalStatusLine(template: string): Promise<void> {
|
|
|
478
485
|
if (wrapped) {
|
|
479
486
|
settings.statusLine = wrapped;
|
|
480
487
|
} else {
|
|
488
|
+
// biome-ignore lint/performance/noDelete: settings is written back to settings.json; the key must be removed from the file, and readers check `Object.keys`/`in`
|
|
481
489
|
delete settings.statusLine;
|
|
482
490
|
}
|
|
483
491
|
await writeGlobalSettings(settings);
|
|
@@ -678,7 +686,9 @@ export async function saveGlobalInstalledPluginVersion(
|
|
|
678
686
|
*/
|
|
679
687
|
export async function gapFillInstalledPluginVersions(
|
|
680
688
|
projectPath?: string,
|
|
681
|
-
): Promise<
|
|
689
|
+
): Promise<
|
|
690
|
+
Array<{ pluginId: string; oldVersion: string; newVersion: string }>
|
|
691
|
+
> {
|
|
682
692
|
const globalVersions = await getGlobalInstalledPluginVersions();
|
|
683
693
|
|
|
684
694
|
// Collect versions from project and local scopes
|
|
@@ -749,8 +759,7 @@ export async function gapFillInstalledPluginVersions(
|
|
|
749
759
|
// Apply bumps in a single write
|
|
750
760
|
if (bumped.length > 0) {
|
|
751
761
|
const settings = await readGlobalSettings();
|
|
752
|
-
settings.installedPluginVersions =
|
|
753
|
-
settings.installedPluginVersions || {};
|
|
762
|
+
settings.installedPluginVersions = settings.installedPluginVersions || {};
|
|
754
763
|
for (const { pluginId, newVersion } of bumped) {
|
|
755
764
|
settings.installedPluginVersions[pluginId] = newVersion;
|
|
756
765
|
}
|
|
@@ -1048,10 +1057,10 @@ async function decodeProjectDirName(encoded: string): Promise<string | null> {
|
|
|
1048
1057
|
|
|
1049
1058
|
// Build path greedily: at each step, try joining the next segment with a dash first
|
|
1050
1059
|
// (preserving directory names like "circl-infra"), fall back to slash (new path segment)
|
|
1051
|
-
let current =
|
|
1060
|
+
let current = `/${segments[0]}`;
|
|
1052
1061
|
for (let i = 1; i < segments.length; i++) {
|
|
1053
|
-
const withDash = current
|
|
1054
|
-
const withSlash = current
|
|
1062
|
+
const withDash = `${current}-${segments[i]}`;
|
|
1063
|
+
const withSlash = `${current}/${segments[i]}`;
|
|
1055
1064
|
// Prefer dash (keeps compound names intact) if that path prefix exists
|
|
1056
1065
|
if (await fs.pathExists(withDash)) {
|
|
1057
1066
|
current = withDash;
|
|
@@ -1388,6 +1397,7 @@ export async function cleanupExtraKnownMarketplaces(
|
|
|
1388
1397
|
changed &&
|
|
1389
1398
|
Object.keys(globalSettings.extraKnownMarketplaces).length === 0
|
|
1390
1399
|
) {
|
|
1400
|
+
// biome-ignore lint/performance/noDelete: the empty block is removed from settings.json entirely; an undefined value would survive Object.keys and `in` checks
|
|
1391
1401
|
delete globalSettings.extraKnownMarketplaces;
|
|
1392
1402
|
}
|
|
1393
1403
|
if (changed) {
|
|
@@ -1417,6 +1427,7 @@ export async function cleanupExtraKnownMarketplaces(
|
|
|
1417
1427
|
changed &&
|
|
1418
1428
|
Object.keys(projectSettings.extraKnownMarketplaces).length === 0
|
|
1419
1429
|
) {
|
|
1430
|
+
// biome-ignore lint/performance/noDelete: the empty block is removed from settings.json entirely; an undefined value would survive Object.keys and `in` checks
|
|
1420
1431
|
delete projectSettings.extraKnownMarketplaces;
|
|
1421
1432
|
}
|
|
1422
1433
|
if (changed) {
|
|
@@ -1452,9 +1463,7 @@ async function readKnownMarketplaces(): Promise<KnownMarketplaces> {
|
|
|
1452
1463
|
* enough: an entry with a missing directory means the clone was lost; a
|
|
1453
1464
|
* directory without an entry means Claude Code doesn't know about it.
|
|
1454
1465
|
*/
|
|
1455
|
-
export async function isMarketplaceRegistered(
|
|
1456
|
-
name: string,
|
|
1457
|
-
): Promise<boolean> {
|
|
1466
|
+
export async function isMarketplaceRegistered(name: string): Promise<boolean> {
|
|
1458
1467
|
const known = await readKnownMarketplaces();
|
|
1459
1468
|
if (!known[name]) return false;
|
|
1460
1469
|
const marketplacesDir = path.join(
|
|
@@ -50,9 +50,15 @@ export function clearContentDriftCache(): void {
|
|
|
50
50
|
cache.clear();
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
function git(
|
|
53
|
+
function git(
|
|
54
|
+
cwd: string,
|
|
55
|
+
args: string[],
|
|
56
|
+
): Promise<{ code: number; out: string }> {
|
|
54
57
|
return new Promise((resolve) => {
|
|
55
|
-
const child = spawn("git", args, {
|
|
58
|
+
const child = spawn("git", args, {
|
|
59
|
+
cwd,
|
|
60
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
61
|
+
});
|
|
56
62
|
let out = "";
|
|
57
63
|
child.stdout.on("data", (d) => {
|
|
58
64
|
out += String(d);
|
|
@@ -154,7 +160,11 @@ export async function hasContentDrift(q: DriftQuery): Promise<boolean> {
|
|
|
154
160
|
const rel = (q.sourcePath ?? `plugins/${q.pluginName}`).replace(/^\.\//, "");
|
|
155
161
|
|
|
156
162
|
// The recorded commit must exist locally, or the diff is meaningless.
|
|
157
|
-
const known = await git(repo, [
|
|
163
|
+
const known = await git(repo, [
|
|
164
|
+
"cat-file",
|
|
165
|
+
"-e",
|
|
166
|
+
`${q.installedSha}^{commit}`,
|
|
167
|
+
]);
|
|
158
168
|
if (known.code !== 0) {
|
|
159
169
|
cache.set(key, false);
|
|
160
170
|
return false;
|