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.
Files changed (150) hide show
  1. package/README.md +22 -0
  2. package/bin/claudeup.js +6 -1
  3. package/package.json +6 -6
  4. package/src/__tests__/alias-adopt.test.ts +331 -329
  5. package/src/__tests__/alias-parser.test.ts +384 -376
  6. package/src/__tests__/alias-shell-writer.test.ts +638 -625
  7. package/src/__tests__/alias-store.test.ts +143 -143
  8. package/src/__tests__/catalog-cache-store.test.ts +265 -265
  9. package/src/__tests__/catalog-notice.test.ts +123 -123
  10. package/src/__tests__/cli-apply-seams.test.ts +12 -8
  11. package/src/__tests__/cli-router.test.ts +132 -3
  12. package/src/__tests__/cli-update-view.test.ts +27 -8
  13. package/src/__tests__/community-staleness.test.ts +0 -2
  14. package/src/__tests__/component-badge.test.ts +4 -4
  15. package/src/__tests__/content-drift.test.ts +5 -1
  16. package/src/__tests__/conventions-integration.test.ts +774 -689
  17. package/src/__tests__/conventions-manager.test.ts +1216 -1152
  18. package/src/__tests__/doctor-bins.test.ts +14 -6
  19. package/src/__tests__/doctor.test.ts +438 -425
  20. package/src/__tests__/dual-write-prevention.test.ts +277 -282
  21. package/src/__tests__/enabled-not-installed.test.ts +71 -63
  22. package/src/__tests__/file-locking.test.ts +196 -196
  23. package/src/__tests__/gap-fill-versions.test.ts +318 -311
  24. package/src/__tests__/github-budget.test.ts +170 -164
  25. package/src/__tests__/gitignore-detector.test.ts +160 -152
  26. package/src/__tests__/gitignore-fixer.test.ts +285 -247
  27. package/src/__tests__/gitignore-prerun.test.ts +57 -57
  28. package/src/__tests__/gitignore-resolver.test.ts +173 -146
  29. package/src/__tests__/gitignore-service.test.ts +121 -119
  30. package/src/__tests__/install-command.test.ts +3 -3
  31. package/src/__tests__/install-plan.test.ts +13 -7
  32. package/src/__tests__/manifest.test.ts +16 -5
  33. package/src/__tests__/markdown-renderer.test.ts +0 -1
  34. package/src/__tests__/marketplace-badge.test.ts +111 -90
  35. package/src/__tests__/marketplace-version.test.ts +64 -61
  36. package/src/__tests__/plugin-manager-fallback.test.ts +249 -239
  37. package/src/__tests__/plugin-requires.test.ts +18 -4
  38. package/src/__tests__/plugin-setup.test.ts +12 -12
  39. package/src/__tests__/profile-command.test.ts +1 -7
  40. package/src/__tests__/profile-materializer.test.ts +31 -8
  41. package/src/__tests__/profile-sync.test.ts +23 -5
  42. package/src/__tests__/registry-version-resolution.test.ts +61 -47
  43. package/src/__tests__/resolve-executable.test.ts +1 -1
  44. package/src/__tests__/resolver.test.ts +40 -15
  45. package/src/__tests__/scope-action.test.ts +52 -50
  46. package/src/__tests__/scope-squares.test.tsx +4 -5
  47. package/src/__tests__/skill-install-files.test.ts +111 -103
  48. package/src/__tests__/symlink-manager.test.ts +27 -10
  49. package/src/__tests__/theme-env.test.ts +281 -0
  50. package/src/__tests__/theme-resolve.test.ts +380 -0
  51. package/src/__tests__/update-apply.test.ts +1 -3
  52. package/src/__tests__/useGitignoreModal.test.ts +69 -61
  53. package/src/__tests__/version-snapshot.test.ts +181 -165
  54. package/src/cli/ansi.ts +23 -15
  55. package/src/cli/doctor.ts +10 -9
  56. package/src/cli/install.ts +3 -3
  57. package/src/cli/profile.ts +5 -4
  58. package/src/cli/prompt.ts +0 -1
  59. package/src/cli/router.ts +69 -2
  60. package/src/cli/update-view.ts +11 -6
  61. package/src/cli/update.ts +9 -4
  62. package/src/cli/upgrade.ts +6 -4
  63. package/src/data/alias-flags.ts +260 -260
  64. package/src/data/gitignore-defaults.ts +20 -20
  65. package/src/data/gitignore-reasons.ts +86 -92
  66. package/src/data/gitignore-templates.ts +14 -14
  67. package/src/data/predefined-profiles.ts +8 -9
  68. package/src/data/skill-repos.ts +43 -35
  69. package/src/main.tsx +29 -6
  70. package/src/opentui.d.ts +1 -3
  71. package/src/prerunner/index.ts +7 -8
  72. package/src/services/alias-settings.ts +20 -23
  73. package/src/services/alias-shell-writer.ts +800 -803
  74. package/src/services/alias-store.ts +77 -77
  75. package/src/services/claude-settings.ts +27 -18
  76. package/src/services/content-drift.ts +13 -3
  77. package/src/services/conventions-manager.ts +692 -617
  78. package/src/services/doctor.ts +464 -403
  79. package/src/services/gitignore-detector.ts +113 -103
  80. package/src/services/gitignore-fixer.ts +204 -189
  81. package/src/services/gitignore-prerun.ts +26 -26
  82. package/src/services/gitignore-resolver.ts +162 -144
  83. package/src/services/gitignore-service.ts +120 -117
  84. package/src/services/install-plan.ts +4 -4
  85. package/src/services/manifest.ts +1 -1
  86. package/src/services/marketplace-catalog-git.ts +5 -2
  87. package/src/services/marketplace-fetcher.ts +241 -235
  88. package/src/services/plugin-manager.ts +17 -20
  89. package/src/services/plugin-mcp-config.ts +2 -2
  90. package/src/services/plugin-requires.ts +9 -6
  91. package/src/services/plugin-setup.ts +7 -9
  92. package/src/services/plugin-version-check.ts +4 -4
  93. package/src/services/profile-materializer.ts +9 -5
  94. package/src/services/profile-sync.ts +12 -4
  95. package/src/services/profiles.ts +3 -3
  96. package/src/services/resolver.ts +10 -6
  97. package/src/services/settings-manager.ts +82 -46
  98. package/src/services/skills-manager.ts +11 -9
  99. package/src/services/symlink-manager.ts +2 -5
  100. package/src/services/toolchain.ts +3 -3
  101. package/src/services/update-cache.ts +1 -1
  102. package/src/services/update-engine.ts +4 -4
  103. package/src/services/update-plan.ts +9 -11
  104. package/src/services/version-snapshot.ts +88 -88
  105. package/src/types/gitignore.ts +28 -28
  106. package/src/ui/App.tsx +36 -36
  107. package/src/ui/adapters/settingsAdapter.ts +2 -2
  108. package/src/ui/components/CategoryHeader.tsx +1 -1
  109. package/src/ui/components/FlagDetailEditor.tsx +0 -0
  110. package/src/ui/components/ScopeIndicator.tsx +3 -1
  111. package/src/ui/components/ScrollableList.tsx +2 -1
  112. package/src/ui/components/StyledText.tsx +1 -1
  113. package/src/ui/components/layout/FooterHints.tsx +5 -3
  114. package/src/ui/components/layout/Panel.tsx +2 -2
  115. package/src/ui/components/layout/ScreenLayout.tsx +5 -4
  116. package/src/ui/components/modals/InputModal.tsx +7 -1
  117. package/src/ui/components/modals/ModalContainer.tsx +3 -3
  118. package/src/ui/components/modals/SelectModal.tsx +1 -1
  119. package/src/ui/components/primitives/DetailSection.tsx +1 -1
  120. package/src/ui/components/primitives/KeyValueLine.tsx +1 -1
  121. package/src/ui/components/primitives/ScopeSquares.tsx +2 -2
  122. package/src/ui/hooks/index.ts +0 -1
  123. package/src/ui/hooks/useGitignoreModal.ts +1 -5
  124. package/src/ui/hooks/useMismatchModal.ts +4 -10
  125. package/src/ui/renderers/cliToolRenderers.tsx +2 -2
  126. package/src/ui/renderers/gitignoreRenderers.tsx +2 -2
  127. package/src/ui/renderers/mcpRenderers.tsx +1 -1
  128. package/src/ui/renderers/pluginRenderers.tsx +3 -9
  129. package/src/ui/renderers/profileRenderers.tsx +9 -8
  130. package/src/ui/renderers/settingsRenderers.tsx +4 -4
  131. package/src/ui/renderers/skillRenderers.tsx +14 -14
  132. package/src/ui/screens/AliasScreen.tsx +31 -34
  133. package/src/ui/screens/CliToolsScreen.tsx +14 -13
  134. package/src/ui/screens/EnvVarsScreen.tsx +8 -8
  135. package/src/ui/screens/GitignoreScreen.tsx +15 -14
  136. package/src/ui/screens/McpRegistryScreen.tsx +10 -7
  137. package/src/ui/screens/McpScreen.tsx +10 -10
  138. package/src/ui/screens/PluginsScreen.tsx +17 -21
  139. package/src/ui/screens/ProfilesScreen.tsx +25 -24
  140. package/src/ui/screens/SkillsScreen.tsx +16 -14
  141. package/src/ui/state/AppContext.tsx +4 -8
  142. package/src/ui/state/DimensionsContext.tsx +3 -2
  143. package/src/ui/theme-mode.ts +20 -38
  144. package/src/ui/theme-resolve.ts +148 -0
  145. package/src/utils/clipboard.ts +5 -5
  146. package/src/utils/command-utils.ts +2 -2
  147. package/src/utils/config-dir.ts +1 -3
  148. package/src/utils/file-locking.ts +88 -86
  149. package/src/ui/hooks/useAsyncData.ts +0 -127
  150. package/src/ui/state/AnimationContext.tsx +0 -76
@@ -1,103 +1,97 @@
1
1
  import type { RuleAction } from "../types/gitignore.js";
2
2
 
3
3
  const RULE_REASONS = new Map<string, string>([
4
- [
5
- key("ignore", ".claude/settings.local.json"),
6
- "Personal Claude settings belong to each developer and should not be shared.",
7
- ],
8
- [
9
- key("ignore", ".claude/scheduled_tasks.lock"),
10
- "Runtime lock files are local process state and should be regenerated locally.",
11
- ],
12
- [
13
- key("ignore", ".claude/cache/"),
14
- "Claude cache contents are local generated data and can grow quickly.",
15
- ],
16
- [
17
- key("ignore", ".claude/.statusline-worktree-*"),
18
- "Statusline worktree markers are local runtime state.",
19
- ],
20
- [
21
- key("ignore", "ai-docs/sessions/"),
22
- "Session transcripts and scratch notes are local working artifacts.",
23
- ],
24
- [
25
- key("ignore", ".mnemex/"),
26
- "Local memory/index data is machine-specific and regenerated by tooling.",
27
- ],
28
- [
29
- key("ignore", ".claudemem/"),
30
- "Local claudemem indexes are generated from the repo and should not be committed.",
31
- ],
32
- [
33
- key("ignore", "gtd/sessions/"),
34
- "Local GTD session artifacts are personal workflow state.",
35
- ],
36
- [
37
- key("ignore", ".agents/"),
38
- "Local agent workspaces and runtime artifacts should stay out of team history.",
39
- ],
40
- [
41
- key("ignore", "node_modules/"),
42
- "Installed dependencies are restored from the package manager lockfile.",
43
- ],
44
- [
45
- key("ignore", ".env"),
46
- "Environment files commonly contain local secrets and machine-specific values.",
47
- ],
48
- [
49
- key("ignore", ".env.local"),
50
- "Local environment overrides commonly contain secrets or developer-specific values.",
51
- ],
52
- [
53
- key("ignore", ".DS_Store"),
54
- "macOS Finder metadata is machine-generated noise.",
55
- ],
56
- [
57
- key("ignore", "dist/"),
58
- "Distribution output is generated from source during builds.",
59
- ],
60
- [
61
- key("ignore", "build/"),
62
- "Build output is generated from source during builds.",
63
- ],
64
- [
65
- key("ignore", "*.log"),
66
- "Log files are runtime output and usually contain noisy local details.",
67
- ],
68
- [
69
- key("ignore", "coverage/"),
70
- "Coverage reports are generated by test runs.",
71
- ],
72
- [
73
- key("ignore", ".cache/"),
74
- "Tool cache contents are local generated data.",
75
- ],
76
- [
77
- key("track", ".claude/settings.json"),
78
- "Team Claude settings should be committed so everyone gets the same project behavior.",
79
- ],
80
- [
81
- key("track", ".mcp.json"),
82
- "Team MCP configuration should be committed so shared tools are discoverable.",
83
- ],
84
- [
85
- key("track", "plugin.json"),
86
- "The plugin manifest is source metadata required by the plugin system.",
87
- ],
4
+ [
5
+ key("ignore", ".claude/settings.local.json"),
6
+ "Personal Claude settings belong to each developer and should not be shared.",
7
+ ],
8
+ [
9
+ key("ignore", ".claude/scheduled_tasks.lock"),
10
+ "Runtime lock files are local process state and should be regenerated locally.",
11
+ ],
12
+ [
13
+ key("ignore", ".claude/cache/"),
14
+ "Claude cache contents are local generated data and can grow quickly.",
15
+ ],
16
+ [
17
+ key("ignore", ".claude/.statusline-worktree-*"),
18
+ "Statusline worktree markers are local runtime state.",
19
+ ],
20
+ [
21
+ key("ignore", "ai-docs/sessions/"),
22
+ "Session transcripts and scratch notes are local working artifacts.",
23
+ ],
24
+ [
25
+ key("ignore", ".mnemex/"),
26
+ "Local memory/index data is machine-specific and regenerated by tooling.",
27
+ ],
28
+ [
29
+ key("ignore", ".claudemem/"),
30
+ "Local claudemem indexes are generated from the repo and should not be committed.",
31
+ ],
32
+ [
33
+ key("ignore", "gtd/sessions/"),
34
+ "Local GTD session artifacts are personal workflow state.",
35
+ ],
36
+ [
37
+ key("ignore", ".agents/"),
38
+ "Local agent workspaces and runtime artifacts should stay out of team history.",
39
+ ],
40
+ [
41
+ key("ignore", "node_modules/"),
42
+ "Installed dependencies are restored from the package manager lockfile.",
43
+ ],
44
+ [
45
+ key("ignore", ".env"),
46
+ "Environment files commonly contain local secrets and machine-specific values.",
47
+ ],
48
+ [
49
+ key("ignore", ".env.local"),
50
+ "Local environment overrides commonly contain secrets or developer-specific values.",
51
+ ],
52
+ [
53
+ key("ignore", ".DS_Store"),
54
+ "macOS Finder metadata is machine-generated noise.",
55
+ ],
56
+ [
57
+ key("ignore", "dist/"),
58
+ "Distribution output is generated from source during builds.",
59
+ ],
60
+ [
61
+ key("ignore", "build/"),
62
+ "Build output is generated from source during builds.",
63
+ ],
64
+ [
65
+ key("ignore", "*.log"),
66
+ "Log files are runtime output and usually contain noisy local details.",
67
+ ],
68
+ [key("ignore", "coverage/"), "Coverage reports are generated by test runs."],
69
+ [key("ignore", ".cache/"), "Tool cache contents are local generated data."],
70
+ [
71
+ key("track", ".claude/settings.json"),
72
+ "Team Claude settings should be committed so everyone gets the same project behavior.",
73
+ ],
74
+ [
75
+ key("track", ".mcp.json"),
76
+ "Team MCP configuration should be committed so shared tools are discoverable.",
77
+ ],
78
+ [
79
+ key("track", "plugin.json"),
80
+ "The plugin manifest is source metadata required by the plugin system.",
81
+ ],
88
82
  ]);
89
83
 
90
84
  export function getGitignoreRuleReason(
91
- action: RuleAction,
92
- pattern: string,
85
+ action: RuleAction,
86
+ pattern: string,
93
87
  ): string {
94
- const reason = RULE_REASONS.get(key(action, pattern));
95
- if (reason) return reason;
96
- return action === "ignore"
97
- ? "A managed rule says this path should stay out of git."
98
- : "A managed rule says this path should stay tracked in git.";
88
+ const reason = RULE_REASONS.get(key(action, pattern));
89
+ if (reason) return reason;
90
+ return action === "ignore"
91
+ ? "A managed rule says this path should stay out of git."
92
+ : "A managed rule says this path should stay tracked in git.";
99
93
  }
100
94
 
101
95
  function key(action: RuleAction, pattern: string): string {
102
- return `${action}:${pattern}`;
96
+ return `${action}:${pattern}`;
103
97
  }
@@ -6,20 +6,20 @@ import type { GitignoreManifest } from "../types/gitignore.js";
6
6
  * apply when explicitly requested.
7
7
  */
8
8
  export const BUILTIN_TEMPLATES: Record<string, GitignoreManifest> = {
9
- "common-dev": {
10
- ignore: [
11
- "node_modules/",
12
- ".env",
13
- ".env.local",
14
- ".DS_Store",
15
- "dist/",
16
- "build/",
17
- "*.log",
18
- "coverage/",
19
- ".cache/",
20
- ],
21
- track: [],
22
- },
9
+ "common-dev": {
10
+ ignore: [
11
+ "node_modules/",
12
+ ".env",
13
+ ".env.local",
14
+ ".DS_Store",
15
+ "dist/",
16
+ "build/",
17
+ "*.log",
18
+ "coverage/",
19
+ ".cache/",
20
+ ],
21
+ track: [],
22
+ },
23
23
  };
24
24
 
25
25
  export type BuiltinTemplateName = keyof typeof BUILTIN_TEMPLATES;
@@ -15,7 +15,7 @@ export const PREDEFINED_PROFILES: PredefinedProfile[] = [
15
15
  name: "Must Have",
16
16
  description: "Essential plugins every Claude Code user should have",
17
17
  icon: "★",
18
- magusPlugins: ["statusline", "multimodel"],
18
+ magusPlugins: ["setup", "multimodel"],
19
19
  anthropicPlugins: [
20
20
  "claude-code-setup",
21
21
  "claude-md-management",
@@ -41,7 +41,7 @@ export const PREDEFINED_PROFILES: PredefinedProfile[] = [
41
41
  "Must Have + full dev toolkit: code analysis, browser, terminal, design, review",
42
42
  icon: "▶",
43
43
  magusPlugins: [
44
- "statusline",
44
+ "setup",
45
45
  "multimodel",
46
46
  "code-analysis",
47
47
  "dev",
@@ -78,14 +78,13 @@ export const PREDEFINED_PROFILES: PredefinedProfile[] = [
78
78
  {
79
79
  id: "frontend-pro",
80
80
  name: "Frontend Pro",
81
- description:
82
- "UI implementation, design fidelity, browser-driven workflows",
81
+ description: "UI implementation, design fidelity, browser-driven workflows",
83
82
  icon: "★",
84
83
  magusPlugins: [
85
84
  "dev",
86
85
  "code-analysis",
87
86
  "terminal",
88
- "statusline",
87
+ "setup",
89
88
  "designer",
90
89
  "browser-use",
91
90
  "multimodel",
@@ -126,7 +125,7 @@ export const PREDEFINED_PROFILES: PredefinedProfile[] = [
126
125
  "dev",
127
126
  "code-analysis",
128
127
  "terminal",
129
- "statusline",
128
+ "setup",
130
129
  "multimodel",
131
130
  "gtd",
132
131
  ],
@@ -161,7 +160,7 @@ export const PREDEFINED_PROFILES: PredefinedProfile[] = [
161
160
  "dev",
162
161
  "code-analysis",
163
162
  "terminal",
164
- "statusline",
163
+ "setup",
165
164
  "multimodel",
166
165
  "gtd",
167
166
  "browser-use",
@@ -198,7 +197,7 @@ export const PREDEFINED_PROFILES: PredefinedProfile[] = [
198
197
  "dev",
199
198
  "code-analysis",
200
199
  "terminal",
201
- "statusline",
200
+ "setup",
202
201
  "seo@magus-marketing",
203
202
  "browser-use",
204
203
  "image-generate@magus-marketing",
@@ -233,7 +232,7 @@ export const PREDEFINED_PROFILES: PredefinedProfile[] = [
233
232
  "dev",
234
233
  "code-analysis",
235
234
  "terminal",
236
- "statusline",
235
+ "setup",
237
236
  "multimodel",
238
237
  "gtd",
239
238
  ],
@@ -12,11 +12,11 @@ export interface RecommendedSkill {
12
12
  // ─── Skill Sets (grouped skills from a single repo) ──────────────────────────
13
13
 
14
14
  export interface RecommendedSkillSet {
15
- name: string;
16
- repo: string;
17
- description: string;
18
- icon: string;
19
- stars?: number;
15
+ name: string;
16
+ repo: string;
17
+ description: string;
18
+ icon: string;
19
+ stars?: number;
20
20
  }
21
21
 
22
22
  // ─── Star reliability classification ────────────────────────────────────────
@@ -24,11 +24,11 @@ export interface RecommendedSkillSet {
24
24
 
25
25
  /** Mega-repos: huge projects that happen to include skills. Stars are about the project. */
26
26
  const MEGA_REPOS = new Set([
27
- "microsoft/vscode",
28
- "Significant-Gravitas/AutoGPT",
29
- "n8n-io/n8n",
30
- "anomalyco/opencode",
31
- "shadcn-ui/ui",
27
+ "microsoft/vscode",
28
+ "Significant-Gravitas/AutoGPT",
29
+ "n8n-io/n8n",
30
+ "anomalyco/opencode",
31
+ "shadcn-ui/ui",
32
32
  ]);
33
33
 
34
34
  /** Skill-dump repos: large collections where stars reflect quantity, not quality. */
@@ -37,12 +37,12 @@ const MEGA_REPOS = new Set([
37
37
  // list still carry the old one; dropping either silently reclassifies the repo
38
38
  // as "dedicated" and its stars start reading as a quality signal again.
39
39
  const SKILL_DUMP_REPOS = new Set([
40
- "affaan-m/everything-claude-code",
41
- "openclaw/openclaw",
42
- "jh941213/my-claude-code-asset",
43
- "jh941213/my-cc-harness", // renamed from my-claude-code-asset
44
- "inferen-sh/skills",
45
- "inference-sh/skills", // renamed from inferen-sh
40
+ "affaan-m/everything-claude-code",
41
+ "openclaw/openclaw",
42
+ "jh941213/my-claude-code-asset",
43
+ "jh941213/my-cc-harness", // renamed from my-claude-code-asset
44
+ "inferen-sh/skills",
45
+ "inference-sh/skills", // renamed from inferen-sh
46
46
  ]);
47
47
 
48
48
  /**
@@ -51,27 +51,35 @@ const SKILL_DUMP_REPOS = new Set([
51
51
  * - "mega-repo": Stars are about a larger project, not the skills (e.g. microsoft/vscode)
52
52
  * - "skill-dump": Large skill collection — stars reflect popularity/quantity, not individual quality
53
53
  */
54
- export function classifyStarReliability(repo: string, stars?: number): StarReliability {
55
- if (MEGA_REPOS.has(repo)) return "mega-repo";
56
- if (SKILL_DUMP_REPOS.has(repo)) return "skill-dump";
57
- // Heuristic: unknown repos with >50K stars are likely mega-repos
58
- if (stars && stars > 50000) return "mega-repo";
59
- return "dedicated";
54
+ export function classifyStarReliability(
55
+ repo: string,
56
+ stars?: number,
57
+ ): StarReliability {
58
+ if (MEGA_REPOS.has(repo)) return "mega-repo";
59
+ if (SKILL_DUMP_REPOS.has(repo)) return "skill-dump";
60
+ // Heuristic: unknown repos with >50K stars are likely mega-repos
61
+ if (stars && stars > 50000) return "mega-repo";
62
+ return "dedicated";
60
63
  }
61
64
 
62
- export const STAR_RELIABILITY_INFO: Record<StarReliability, { label: string; description: string }> = {
63
- dedicated: {
64
- label: "Skill repo",
65
- description: "Stars reflect this skill repository's quality",
66
- },
67
- "mega-repo": {
68
- label: "Project repo",
69
- description: "Stars are for the parent project, not the skills. Skill quality may vary.",
70
- },
71
- "skill-dump": {
72
- label: "Skill collection",
73
- description: "Large skill collection stars reflect repo popularity, not individual skill quality.",
74
- },
65
+ export const STAR_RELIABILITY_INFO: Record<
66
+ StarReliability,
67
+ { label: string; description: string }
68
+ > = {
69
+ dedicated: {
70
+ label: "Skill repo",
71
+ description: "Stars reflect this skill repository's quality",
72
+ },
73
+ "mega-repo": {
74
+ label: "Project repo",
75
+ description:
76
+ "Stars are for the parent project, not the skills. Skill quality may vary.",
77
+ },
78
+ "skill-dump": {
79
+ label: "Skill collection",
80
+ description:
81
+ "Large skill collection — stars reflect repo popularity, not individual skill quality.",
82
+ },
75
83
  };
76
84
 
77
85
  export const DEFAULT_SKILL_REPOS: SkillSource[] = [
package/src/main.tsx CHANGED
@@ -9,6 +9,7 @@ import { route } from "./cli/router.js";
9
9
  import { loadProjectDotenv } from "./services/dotenv.js";
10
10
  import { App } from "./ui/App.js";
11
11
  import { setThemeMode } from "./ui/theme-mode.js";
12
+ import { resolveTheme, snapshotThemeEnv } from "./ui/theme-resolve.js";
12
13
 
13
14
  export const VERSION = (pkg as { version: string }).version;
14
15
 
@@ -16,6 +17,11 @@ export const VERSION = (pkg as { version: string }).version;
16
17
  // No need for manual ANSI escape codes
17
18
 
18
19
  async function main(): Promise<void> {
20
+ // FIRST, before anything can touch process.env: the theme is read from the
21
+ // environment claudeup was started with, never from a project's .env. The
22
+ // dotenv load below merges into process.env, so the only thing separating
23
+ // the two is that this line runs before it (pinned by theme-env.test.ts).
24
+ const themeEnv = snapshotThemeEnv();
19
25
  const args = process.argv.slice(2);
20
26
 
21
27
  // Load the project's .env ourselves. Bun's standalone autoload is compiled OFF
@@ -43,6 +49,11 @@ async function main(): Promise<void> {
43
49
  process.exit(outcome.exitCode ?? 0);
44
50
  }
45
51
 
52
+ // Both streams must be TTYs for the OSC probe: `claudeup < file` has a tty
53
+ // stdout but the reply arrives on stdin, so the probe would wait for nothing.
54
+ const isInteractive =
55
+ process.stdin.isTTY === true && process.stdout.isTTY === true;
56
+
46
57
  // Create OpenTUI renderer (handles alternate screen buffer automatically).
47
58
  //
48
59
  // The background is the terminal's own, not a colour of ours: painting an
@@ -80,12 +91,24 @@ async function main(): Promise<void> {
80
91
  if (text.length > 0) renderer.copyToClipboardOSC52(text);
81
92
  });
82
93
 
83
- // Ask the terminal whether it is light or dark, once. Only the disabled-row
84
- // tint needs this — see src/ui/theme-mode.ts for why that one case cannot be
85
- // solved the way the rest of the palette is. Bounded wait: a terminal that
86
- // ignores the OSC query must not delay first paint, and `null` simply means
87
- // no tint.
88
- setThemeMode(await renderer.waitForThemeMode(250).catch(() => null));
94
+ // Resolve light vs dark once: --theme, CLAUDEUP_THEME, TERM_THEME, the OSC
95
+ // probe, COLORFGBG, dark — see src/ui/theme-resolve.ts. Only the disabled-row
96
+ // tint and the chips need this (src/ui/theme-mode.ts explains why that one
97
+ // case cannot be solved the way the rest of the palette is).
98
+ //
99
+ // The renderer sends OSC 10/11 at creation regardless: @opentui/core 0.1.107
100
+ // has no option to suppress it. So the rule "TERM_THEME skips the probe" is
101
+ // satisfied by NOT AWAITING — when steps 1-3 answer, `probe` is never called,
102
+ // `waitForThemeMode(250)` never runs, first paint is not delayed, and
103
+ // `renderer.themeMode` is never read anywhere in claudeup. Any late reply the
104
+ // terminal sends is consumed by OpenTUI's own handleSequence and ignored here.
105
+ const { mode } = await resolveTheme({
106
+ flag: outcome.theme,
107
+ env: themeEnv,
108
+ isInteractive,
109
+ probe: () => renderer.waitForThemeMode(250).catch(() => null),
110
+ });
111
+ setThemeMode(mode);
89
112
 
90
113
  const root = createRoot(renderer);
91
114
 
package/src/opentui.d.ts CHANGED
@@ -7,8 +7,8 @@
7
7
  * ReactElement | null.
8
8
  */
9
9
 
10
- import type { ReactNode } from "react";
11
10
  import type { RGBA } from "@opentui/core";
11
+ import type { ReactNode } from "react";
12
12
 
13
13
  /**
14
14
  * What OpenTUI actually accepts for a colour: a CSS string OR an RGBA, which is
@@ -201,5 +201,3 @@ interface MouseEvent {
201
201
  y: number;
202
202
  button: number;
203
203
  }
204
-
205
- export {};
@@ -2,8 +2,8 @@ import os from "node:os";
2
2
  import path from "node:path";
3
3
  import fs from "fs-extra";
4
4
  import {
5
- isClaudeAvailable,
6
5
  type PluginScope,
6
+ isClaudeAvailable,
7
7
  repairPlugin,
8
8
  updatePlugin,
9
9
  } from "../services/claude-cli.js";
@@ -18,13 +18,13 @@ import {
18
18
  saveLocalInstalledPluginVersion,
19
19
  writeGlobalSettings,
20
20
  } from "../services/claude-settings.js";
21
+ import { clearContentDriftCache } from "../services/content-drift.js";
21
22
  import {
22
23
  checkGitignore,
23
24
  formatPrerunWarning as formatGitignoreWarning,
24
25
  } from "../services/gitignore-prerun.js";
25
26
  import { refreshRegisteredMarketplaces } from "../services/marketplace-refresh.js";
26
27
  import { autoAddMissingMarketplaces } from "../services/marketplace-sync.js";
27
- import { clearContentDriftCache } from "../services/content-drift.js";
28
28
  import {
29
29
  clearMarketplaceCache,
30
30
  compareVersions,
@@ -77,8 +77,8 @@ async function ensureTmuxContinuityHooks(): Promise<string | null> {
77
77
  }
78
78
 
79
79
  // Append hooks, preserving any existing entries in SessionStart and Stop
80
- const sessionStartGroups = existingHooks["SessionStart"] ?? [];
81
- const stopGroups = existingHooks["Stop"] ?? [];
80
+ const sessionStartGroups = existingHooks.SessionStart ?? [];
81
+ const stopGroups = existingHooks.Stop ?? [];
82
82
 
83
83
  sessionStartGroups.push({
84
84
  hooks: [
@@ -168,7 +168,7 @@ export async function prerunClaude(
168
168
  const addedHooks = await ensureTmuxContinuityHooks();
169
169
  if (addedHooks) {
170
170
  console.log(
171
- `✓ Added tmux-claude-continuity hooks to ~/.claude/settings.json`,
171
+ "✓ Added tmux-claude-continuity hooks to ~/.claude/settings.json",
172
172
  );
173
173
  }
174
174
 
@@ -270,9 +270,8 @@ export async function prerunClaude(
270
270
  if (await fs.pathExists(path.join(marketplacesDir, mpName))) {
271
271
  continue;
272
272
  }
273
- const repo = defaultMarketplaces.find(
274
- (m) => m.name === mpName,
275
- )?.source.repo;
273
+ const repo = defaultMarketplaces.find((m) => m.name === mpName)
274
+ ?.source.repo;
276
275
  if (!repo) continue;
277
276
  try {
278
277
  await addMarketplace(repo);
@@ -16,19 +16,16 @@
16
16
  * block and are read on screen mount via `parseAliasFromRc`.
17
17
  */
18
18
 
19
- import {
20
- readGlobalSettings,
21
- writeGlobalSettings,
22
- } from "./claude-settings.js";
23
19
  import { DEFAULT_ALIAS_NAME, validateAliasName } from "./alias-store.js";
20
+ import { readGlobalSettings, writeGlobalSettings } from "./claude-settings.js";
24
21
 
25
22
  interface ClaudeupSettings {
26
- aliasName?: string;
23
+ aliasName?: string;
27
24
  }
28
25
 
29
26
  interface SettingsWithClaudeup {
30
- claudeup?: ClaudeupSettings;
31
- [key: string]: unknown;
27
+ claudeup?: ClaudeupSettings;
28
+ [key: string]: unknown;
32
29
  }
33
30
 
34
31
  /**
@@ -36,11 +33,11 @@ interface SettingsWithClaudeup {
36
33
  * Returns the default `"c"` when the key is absent or invalid.
37
34
  */
38
35
  export async function loadAliasName(): Promise<string> {
39
- const settings = (await readGlobalSettings()) as SettingsWithClaudeup;
40
- const stored = settings.claudeup?.aliasName;
41
- if (typeof stored !== "string") return DEFAULT_ALIAS_NAME;
42
- if (validateAliasName(stored) !== null) return DEFAULT_ALIAS_NAME;
43
- return stored;
36
+ const settings = (await readGlobalSettings()) as SettingsWithClaudeup;
37
+ const stored = settings.claudeup?.aliasName;
38
+ if (typeof stored !== "string") return DEFAULT_ALIAS_NAME;
39
+ if (validateAliasName(stored) !== null) return DEFAULT_ALIAS_NAME;
40
+ return stored;
44
41
  }
45
42
 
46
43
  /**
@@ -49,15 +46,15 @@ export async function loadAliasName(): Promise<string> {
49
46
  * if the name fails validation — caller should validate first.
50
47
  */
51
48
  export async function saveAliasName(name: string): Promise<void> {
52
- if (validateAliasName(name) !== null) return;
53
- const settings = (await readGlobalSettings()) as SettingsWithClaudeup;
54
- const next: SettingsWithClaudeup = {
55
- ...settings,
56
- claudeup: {
57
- ...(settings.claudeup ?? {}),
58
- aliasName: name,
59
- },
60
- };
61
- // claude-settings.writeGlobalSettings accepts the same shape we received.
62
- await writeGlobalSettings(next as Parameters<typeof writeGlobalSettings>[0]);
49
+ if (validateAliasName(name) !== null) return;
50
+ const settings = (await readGlobalSettings()) as SettingsWithClaudeup;
51
+ const next: SettingsWithClaudeup = {
52
+ ...settings,
53
+ claudeup: {
54
+ ...(settings.claudeup ?? {}),
55
+ aliasName: name,
56
+ },
57
+ };
58
+ // claude-settings.writeGlobalSettings accepts the same shape we received.
59
+ await writeGlobalSettings(next as Parameters<typeof writeGlobalSettings>[0]);
63
60
  }