claudeup 4.16.0 → 4.18.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.
Files changed (77) hide show
  1. package/package.json +1 -1
  2. package/src/__tests__/alias-parser.test.ts +317 -0
  3. package/src/__tests__/alias-shell-writer.test.ts +661 -0
  4. package/src/__tests__/alias-store.test.ts +86 -0
  5. package/src/__tests__/gitignore-fixer.test.ts +64 -1
  6. package/src/__tests__/gitignore-prerun.test.ts +2 -2
  7. package/src/__tests__/gitignore-service.test.ts +42 -0
  8. package/src/__tests__/marketplaces.test.ts +40 -0
  9. package/src/__tests__/plugin-manager-fallback.test.ts +120 -0
  10. package/src/__tests__/useGitignoreModal.test.ts +2 -2
  11. package/src/data/alias-flags.js +196 -0
  12. package/src/data/alias-flags.ts +291 -0
  13. package/src/data/gitignore-reasons.js +97 -0
  14. package/src/data/gitignore-reasons.ts +103 -0
  15. package/src/data/marketplaces.js +19 -1
  16. package/src/data/marketplaces.ts +17 -1
  17. package/src/services/alias-settings.js +51 -0
  18. package/src/services/alias-settings.ts +63 -0
  19. package/src/services/alias-shell-writer.js +764 -0
  20. package/src/services/alias-shell-writer.ts +873 -0
  21. package/src/services/alias-store.js +77 -0
  22. package/src/services/alias-store.ts +112 -0
  23. package/src/services/gitignore-fixer.js +70 -10
  24. package/src/services/gitignore-fixer.ts +76 -9
  25. package/src/services/gitignore-prerun.js +3 -3
  26. package/src/services/gitignore-prerun.ts +3 -3
  27. package/src/services/gitignore-service.js +20 -2
  28. package/src/services/gitignore-service.ts +23 -1
  29. package/src/services/marketplace-fetcher.js +96 -0
  30. package/src/services/marketplace-fetcher.ts +137 -0
  31. package/src/services/plugin-manager.js +6 -59
  32. package/src/services/plugin-manager.ts +16 -91
  33. package/src/services/skillsmp-client.js +29 -9
  34. package/src/services/skillsmp-client.ts +38 -8
  35. package/src/types/gitignore.ts +1 -1
  36. package/src/types/index.ts +1 -0
  37. package/src/ui/App.js +10 -4
  38. package/src/ui/App.tsx +9 -3
  39. package/src/ui/components/TabBar.js +2 -1
  40. package/src/ui/components/TabBar.tsx +2 -1
  41. package/src/ui/components/layout/FooterHints.js +29 -0
  42. package/src/ui/components/layout/FooterHints.tsx +52 -0
  43. package/src/ui/components/layout/ScreenLayout.js +2 -1
  44. package/src/ui/components/layout/ScreenLayout.tsx +12 -3
  45. package/src/ui/components/layout/index.js +1 -0
  46. package/src/ui/components/layout/index.ts +5 -0
  47. package/src/ui/components/modals/SelectModal.js +8 -1
  48. package/src/ui/components/modals/SelectModal.tsx +12 -1
  49. package/src/ui/hooks/useGitignoreModal.js +7 -8
  50. package/src/ui/hooks/useGitignoreModal.ts +8 -9
  51. package/src/ui/renderers/gitignoreRenderers.js +36 -23
  52. package/src/ui/renderers/gitignoreRenderers.tsx +50 -41
  53. package/src/ui/screens/AliasScreen.js +1008 -0
  54. package/src/ui/screens/AliasScreen.tsx +1402 -0
  55. package/src/ui/screens/CliToolsScreen.js +6 -1
  56. package/src/ui/screens/CliToolsScreen.tsx +6 -1
  57. package/src/ui/screens/EnvVarsScreen.js +6 -1
  58. package/src/ui/screens/EnvVarsScreen.tsx +6 -1
  59. package/src/ui/screens/GitignoreScreen.js +189 -88
  60. package/src/ui/screens/GitignoreScreen.tsx +312 -132
  61. package/src/ui/screens/McpRegistryScreen.js +13 -2
  62. package/src/ui/screens/McpRegistryScreen.tsx +13 -2
  63. package/src/ui/screens/McpScreen.js +6 -1
  64. package/src/ui/screens/McpScreen.tsx +6 -1
  65. package/src/ui/screens/ModelSelectorScreen.js +8 -2
  66. package/src/ui/screens/ModelSelectorScreen.tsx +8 -2
  67. package/src/ui/screens/PluginsScreen.js +13 -2
  68. package/src/ui/screens/PluginsScreen.tsx +13 -2
  69. package/src/ui/screens/ProfilesScreen.js +8 -1
  70. package/src/ui/screens/ProfilesScreen.tsx +8 -1
  71. package/src/ui/screens/SkillsScreen.js +21 -4
  72. package/src/ui/screens/SkillsScreen.tsx +39 -5
  73. package/src/ui/screens/StatusLineScreen.js +7 -1
  74. package/src/ui/screens/StatusLineScreen.tsx +7 -1
  75. package/src/ui/screens/index.js +1 -0
  76. package/src/ui/screens/index.ts +1 -0
  77. package/src/ui/state/types.ts +4 -2
@@ -0,0 +1,291 @@
1
+ /**
2
+ * Catalog of `claude` CLI flags that the Alias tab can compose into a managed
3
+ * shell alias. Verified against `claude --help` output and binary string scan
4
+ * of the shipped Claude Code 2.1.x binary.
5
+ *
6
+ * The catalog drives both the UI editor for each flag and the renderer that
7
+ * emits the final `alias claude='claude …'` string per shell.
8
+ */
9
+
10
+ export type FlagKind =
11
+ /** On/off. AliasFlagState: { enabled: boolean } */
12
+ | "boolean"
13
+ /** Three states: unset / on / off. Used for `--chrome` vs `--no-chrome`. */
14
+ | "tri-state"
15
+ /** Single value from a fixed list. AliasFlagState: { enabled, value }. */
16
+ | "select"
17
+ /** Free-form string. AliasFlagState: { enabled, value }. */
18
+ | "text"
19
+ /** Optional free-form string (flag works with or without a value). */
20
+ | "optional-text"
21
+ /** Repeatable list of strings. AliasFlagState: { enabled, values }. */
22
+ | "text-list"
23
+ /**
24
+ * Picklist of common tokens + free-form custom tokens, joined with commas.
25
+ * AliasFlagState: { enabled, picked: string[], custom: string[] }.
26
+ * Used for `--debug` because Claude Code's category set is dynamic.
27
+ */
28
+ | "multi-with-custom";
29
+
30
+ export interface FlagSelectOption {
31
+ label: string;
32
+ value: string;
33
+ }
34
+
35
+ export interface AliasFlag {
36
+ /** Stable identifier; used as JSON key in `~/.claude/claudeup-alias.json`. */
37
+ id: string;
38
+ /** Human label shown in the UI list. */
39
+ label: string;
40
+ /**
41
+ * The CLI flag as it appears on the command line. For tri-state, this is the
42
+ * "on" form (e.g. `--chrome`); the "off" form is in `triStateOff`.
43
+ */
44
+ flag: string;
45
+ /** Optional short flag (e.g. `-d` for `--debug`). Not currently rendered. */
46
+ shortFlag?: string;
47
+ kind: FlagKind;
48
+ /** One-line description shown in the detail panel. */
49
+ description: string;
50
+ /** Allowed values for `kind: "select"`. */
51
+ options?: FlagSelectOption[];
52
+ /** "Off" form for tri-state flags (e.g. `--no-chrome`). */
53
+ triStateOff?: string;
54
+ /** Picklist tokens for `kind: "multi-with-custom"`. */
55
+ picklist?: string[];
56
+ /** Default seed values for `text-list` and `multi-with-custom`. */
57
+ defaultValues?: string[];
58
+ /**
59
+ * Validation key. The renderer / UI consult this to grey out conflicting
60
+ * fields. Two flags with the same `xorGroup` are mutually exclusive.
61
+ */
62
+ xorGroup?: string;
63
+ /**
64
+ * The flag is only valid when another flag (by id) is enabled.
65
+ * UI greys out and shows "requires --xxx".
66
+ */
67
+ requires?: string;
68
+ /**
69
+ * If true, the value supports `{token}` placeholders that expand at shell
70
+ * eval time (not at write time). The renderer emits the value as a mix of
71
+ * literal segments and shell substitution segments instead of one quoted
72
+ * token. See `TEMPLATE_TOKENS` for the supported placeholder set.
73
+ */
74
+ templated?: boolean;
75
+ /** UI grouping; rendered as section headers in the list. */
76
+ group: FlagGroup;
77
+ }
78
+
79
+ /**
80
+ * Tokens supported in `templated: true` flag values. Description text is
81
+ * shown to the user; the actual shell-substitution code lives in the writer
82
+ * because it needs per-shell variants.
83
+ *
84
+ * Intentionally excludes `{hash}` for v1 — its only portable form is
85
+ * `od -An -N4 -tx1 /dev/urandom | tr -d ' \n'`, which looks frightening in a
86
+ * shell rc file. Users who want randomness can pair this prefix with bare
87
+ * `--remote-control` (which already auto-generates a unique suffix).
88
+ */
89
+ export const TEMPLATE_TOKENS: Array<{ token: string; description: string }> = [
90
+ { token: "{folder}", description: "current directory's basename" },
91
+ { token: "{day}", description: "day of month, zero-padded (01-31)" },
92
+ { token: "{month}", description: "month, zero-padded (01-12)" },
93
+ { token: "{year}", description: "4-digit year" },
94
+ ];
95
+
96
+ export type FlagGroup =
97
+ | "danger"
98
+ | "session"
99
+ | "context"
100
+ | "channels"
101
+ | "debug"
102
+ | "integration"
103
+ | "worktree";
104
+
105
+ export const FLAG_GROUPS: Array<{ id: FlagGroup; label: string }> = [
106
+ { id: "danger", label: "Permissions" },
107
+ { id: "session", label: "Session" },
108
+ { id: "context", label: "Context" },
109
+ { id: "channels", label: "Development channels" },
110
+ { id: "debug", label: "Debug" },
111
+ { id: "integration", label: "Integrations" },
112
+ { id: "worktree", label: "Worktree" },
113
+ ];
114
+
115
+ export const ALIAS_FLAGS: AliasFlag[] = [
116
+ {
117
+ id: "dangerously-skip-permissions",
118
+ label: "Skip permission checks",
119
+ flag: "--dangerously-skip-permissions",
120
+ kind: "boolean",
121
+ group: "danger",
122
+ description:
123
+ "Bypass all permission checks. Recommended only for sandboxes with no internet access.",
124
+ },
125
+ {
126
+ id: "allow-dangerously-skip-permissions",
127
+ label: "Allow skip-permissions opt-in",
128
+ flag: "--allow-dangerously-skip-permissions",
129
+ kind: "boolean",
130
+ group: "danger",
131
+ description:
132
+ "Enable the skip-permissions option without making it the default.",
133
+ },
134
+ {
135
+ id: "remote-control",
136
+ label: "Remote control",
137
+ flag: "--remote-control",
138
+ kind: "boolean",
139
+ group: "session",
140
+ description:
141
+ "Start an interactive session with Remote Control enabled. Claude Code auto-generates a unique session name; use --remote-control-session-name-prefix to customize it.",
142
+ },
143
+ {
144
+ id: "remote-control-session-name-prefix",
145
+ label: "Remote control name prefix",
146
+ flag: "--remote-control-session-name-prefix",
147
+ kind: "text",
148
+ templated: true,
149
+ group: "session",
150
+ description:
151
+ "Prefix for auto-generated Remote Control session names. Default: hostname. Supports template tokens that expand on each shell invocation: {folder} (basename of $PWD), {day}, {month}, {year}. Example: dev-{folder}-{day} → dev-myproject-15.",
152
+ },
153
+ {
154
+ id: "ide",
155
+ label: "Auto-connect to IDE",
156
+ flag: "--ide",
157
+ kind: "boolean",
158
+ group: "integration",
159
+ description:
160
+ "Automatically connect to IDE on startup if exactly one valid IDE is available.",
161
+ },
162
+ {
163
+ id: "chrome",
164
+ label: "Chrome integration",
165
+ flag: "--chrome",
166
+ triStateOff: "--no-chrome",
167
+ kind: "tri-state",
168
+ group: "integration",
169
+ description:
170
+ "Enable or disable Claude in Chrome integration. Unset leaves the project default in place.",
171
+ },
172
+ {
173
+ id: "effort",
174
+ label: "Effort level",
175
+ flag: "--effort",
176
+ kind: "select",
177
+ group: "session",
178
+ description: "Effort level for the current session.",
179
+ options: [
180
+ { label: "low", value: "low" },
181
+ { label: "medium", value: "medium" },
182
+ { label: "high", value: "high" },
183
+ { label: "xhigh", value: "xhigh" },
184
+ { label: "max", value: "max" },
185
+ ],
186
+ },
187
+ {
188
+ id: "system-prompt",
189
+ label: "System prompt",
190
+ flag: "--system-prompt",
191
+ kind: "text",
192
+ group: "context",
193
+ xorGroup: "system-prompt",
194
+ description:
195
+ "System prompt text to use for the session. Mutually exclusive with --system-prompt-file.",
196
+ },
197
+ {
198
+ id: "system-prompt-file",
199
+ label: "System prompt file",
200
+ flag: "--system-prompt-file",
201
+ kind: "text",
202
+ group: "context",
203
+ xorGroup: "system-prompt",
204
+ description:
205
+ "Path to a file whose contents replace the default system prompt. Mutually exclusive with --system-prompt.",
206
+ },
207
+ {
208
+ id: "append-system-prompt",
209
+ label: "Append system prompt",
210
+ flag: "--append-system-prompt",
211
+ kind: "text",
212
+ group: "context",
213
+ description:
214
+ "Text appended to the default system prompt. Composes with --system-prompt.",
215
+ },
216
+ {
217
+ id: "append-system-prompt-file",
218
+ label: "Append system prompt file",
219
+ flag: "--append-system-prompt-file",
220
+ kind: "text",
221
+ group: "context",
222
+ description:
223
+ "Path to a file whose contents are appended to the default system prompt.",
224
+ },
225
+ {
226
+ id: "dangerously-load-development-channels",
227
+ label: "Load development channels",
228
+ flag: "--dangerously-load-development-channels",
229
+ kind: "text-list",
230
+ group: "channels",
231
+ description:
232
+ "Sideload development channels from magus plugins. Default seed: claudish (the only magus plugin currently shipping channels).",
233
+ defaultValues: ["plugin:claudish@magus"],
234
+ },
235
+ {
236
+ id: "debug",
237
+ label: "Debug categories",
238
+ flag: "--debug",
239
+ shortFlag: "-d",
240
+ kind: "multi-with-custom",
241
+ group: "debug",
242
+ description:
243
+ "Enable debug mode with optional category filter. Pick from common categories or add custom tokens (including negations like !file).",
244
+ picklist: [
245
+ "api",
246
+ "hooks",
247
+ "mcp",
248
+ "tools",
249
+ "plugins",
250
+ "skills",
251
+ "settings",
252
+ "permissions",
253
+ "auth",
254
+ "cache",
255
+ "statsig",
256
+ "file",
257
+ "1p",
258
+ ],
259
+ defaultValues: [],
260
+ },
261
+ {
262
+ id: "worktree",
263
+ label: "Use worktree",
264
+ flag: "--worktree",
265
+ kind: "boolean",
266
+ group: "worktree",
267
+ description:
268
+ "Create a git worktree for the session. Prerequisite for --tmux.",
269
+ },
270
+ {
271
+ id: "tmux",
272
+ label: "tmux session",
273
+ flag: "--tmux",
274
+ kind: "select",
275
+ group: "worktree",
276
+ requires: "worktree",
277
+ description:
278
+ "Create a tmux session for the worktree. Requires --worktree. iTerm2 native panes when available; classic for traditional tmux.",
279
+ options: [
280
+ { label: "auto (iTerm2 native)", value: "" },
281
+ { label: "classic", value: "classic" },
282
+ ],
283
+ },
284
+ ];
285
+
286
+ /** Look up a flag by id. Throws if missing — keeps callers honest. */
287
+ export function getFlagById(id: string): AliasFlag {
288
+ const f = ALIAS_FLAGS.find((flag) => flag.id === id);
289
+ if (!f) throw new Error(`Unknown alias flag id: ${id}`);
290
+ return f;
291
+ }
@@ -0,0 +1,97 @@
1
+ const RULE_REASONS = new Map([
2
+ [
3
+ key("ignore", ".claude/settings.local.json"),
4
+ "Personal Claude settings belong to each developer and should not be shared.",
5
+ ],
6
+ [
7
+ key("ignore", ".claude/scheduled_tasks.lock"),
8
+ "Runtime lock files are local process state and should be regenerated locally.",
9
+ ],
10
+ [
11
+ key("ignore", ".claude/cache/"),
12
+ "Claude cache contents are local generated data and can grow quickly.",
13
+ ],
14
+ [
15
+ key("ignore", ".claude/.statusline-worktree-*"),
16
+ "Statusline worktree markers are local runtime state.",
17
+ ],
18
+ [
19
+ key("ignore", "ai-docs/sessions/"),
20
+ "Session transcripts and scratch notes are local working artifacts.",
21
+ ],
22
+ [
23
+ key("ignore", ".mnemex/"),
24
+ "Local memory/index data is machine-specific and regenerated by tooling.",
25
+ ],
26
+ [
27
+ key("ignore", ".claudemem/"),
28
+ "Local claudemem indexes are generated from the repo and should not be committed.",
29
+ ],
30
+ [
31
+ key("ignore", "gtd/sessions/"),
32
+ "Local GTD session artifacts are personal workflow state.",
33
+ ],
34
+ [
35
+ key("ignore", ".agents/"),
36
+ "Local agent workspaces and runtime artifacts should stay out of team history.",
37
+ ],
38
+ [
39
+ key("ignore", "node_modules/"),
40
+ "Installed dependencies are restored from the package manager lockfile.",
41
+ ],
42
+ [
43
+ key("ignore", ".env"),
44
+ "Environment files commonly contain local secrets and machine-specific values.",
45
+ ],
46
+ [
47
+ key("ignore", ".env.local"),
48
+ "Local environment overrides commonly contain secrets or developer-specific values.",
49
+ ],
50
+ [
51
+ key("ignore", ".DS_Store"),
52
+ "macOS Finder metadata is machine-generated noise.",
53
+ ],
54
+ [
55
+ key("ignore", "dist/"),
56
+ "Distribution output is generated from source during builds.",
57
+ ],
58
+ [
59
+ key("ignore", "build/"),
60
+ "Build output is generated from source during builds.",
61
+ ],
62
+ [
63
+ key("ignore", "*.log"),
64
+ "Log files are runtime output and usually contain noisy local details.",
65
+ ],
66
+ [
67
+ key("ignore", "coverage/"),
68
+ "Coverage reports are generated by test runs.",
69
+ ],
70
+ [
71
+ key("ignore", ".cache/"),
72
+ "Tool cache contents are local generated data.",
73
+ ],
74
+ [
75
+ key("track", ".claude/settings.json"),
76
+ "Team Claude settings should be committed so everyone gets the same project behavior.",
77
+ ],
78
+ [
79
+ key("track", ".mcp.json"),
80
+ "Team MCP configuration should be committed so shared tools are discoverable.",
81
+ ],
82
+ [
83
+ key("track", "plugin.json"),
84
+ "The plugin manifest is source metadata required by the plugin system.",
85
+ ],
86
+ ]);
87
+ export function getGitignoreRuleReason(action, pattern) {
88
+ const reason = RULE_REASONS.get(key(action, pattern));
89
+ if (reason)
90
+ return reason;
91
+ return action === "ignore"
92
+ ? "A managed rule says this path should stay out of git."
93
+ : "A managed rule says this path should stay tracked in git.";
94
+ }
95
+ function key(action, pattern) {
96
+ return `${action}:${pattern}`;
97
+ }
@@ -0,0 +1,103 @@
1
+ import type { RuleAction } from "../types/gitignore.js";
2
+
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
+ ],
88
+ ]);
89
+
90
+ export function getGitignoreRuleReason(
91
+ action: RuleAction,
92
+ pattern: string,
93
+ ): 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.";
99
+ }
100
+
101
+ function key(action: RuleAction, pattern: string): string {
102
+ return `${action}:${pattern}`;
103
+ }
@@ -18,6 +18,7 @@ function normalizeRepo(repo) {
18
18
  export const deprecatedMarketplaces = {
19
19
  "mag-claude-plugins": "magus",
20
20
  "MadAppGang-claude-code": "magus",
21
+ superpowers: "superpowers-marketplace",
21
22
  };
22
23
  export const defaultMarketplaces = [
23
24
  {
@@ -41,6 +42,16 @@ export const defaultMarketplaces = [
41
42
  description: "Official Anthropic-managed directory of high quality Claude Code plugins",
42
43
  official: true,
43
44
  },
45
+ {
46
+ name: "superpowers-marketplace",
47
+ displayName: "Superpowers",
48
+ source: {
49
+ source: "github",
50
+ repo: "obra/superpowers-marketplace",
51
+ },
52
+ description: "Curated Claude Code plugins for skills and workflows",
53
+ featured: true,
54
+ },
44
55
  {
45
56
  name: "claude-code-plugins",
46
57
  displayName: "Anthropic Deprecated",
@@ -50,6 +61,7 @@ export const defaultMarketplaces = [
50
61
  },
51
62
  description: "Legacy demo plugins from Anthropic (deprecated - use Official instead)",
52
63
  official: true,
64
+ deprecated: true,
53
65
  },
54
66
  ];
55
67
  export function getMarketplaceByName(name) {
@@ -93,6 +105,7 @@ export function getAllMarketplaces(localMarketplaces) {
93
105
  description: defaultMp?.description || local.description || "",
94
106
  official: defaultMp?.official ?? repo.toLowerCase().includes("anthropics/"),
95
107
  featured: defaultMp?.featured,
108
+ deprecated: defaultMp?.deprecated,
96
109
  });
97
110
  }
98
111
  }
@@ -105,7 +118,7 @@ export function getAllMarketplaces(localMarketplaces) {
105
118
  seenRepos.add(repo);
106
119
  }
107
120
  }
108
- // Sort: Magus first, then alphabetically
121
+ // Sort: Magus first, deprecated last, then alphabetically
109
122
  return Array.from(all.values()).sort((a, b) => {
110
123
  // Magus (MadAppGang) always first
111
124
  const aIsMag = a.source.repo?.toLowerCase().includes("madappgang/");
@@ -114,6 +127,11 @@ export function getAllMarketplaces(localMarketplaces) {
114
127
  return -1;
115
128
  if (!aIsMag && bIsMag)
116
129
  return 1;
130
+ // Deprecated entries always last
131
+ if (a.deprecated && !b.deprecated)
132
+ return 1;
133
+ if (!a.deprecated && b.deprecated)
134
+ return -1;
117
135
  // Then alphabetically by display name
118
136
  return (a.displayName || a.name).localeCompare(b.displayName || b.name);
119
137
  });
@@ -23,6 +23,7 @@ function normalizeRepo(repo: string): string {
23
23
  export const deprecatedMarketplaces: Record<string, string> = {
24
24
  "mag-claude-plugins": "magus",
25
25
  "MadAppGang-claude-code": "magus",
26
+ superpowers: "superpowers-marketplace",
26
27
  };
27
28
 
28
29
  export const defaultMarketplaces: Marketplace[] = [
@@ -49,6 +50,16 @@ export const defaultMarketplaces: Marketplace[] = [
49
50
  "Official Anthropic-managed directory of high quality Claude Code plugins",
50
51
  official: true,
51
52
  },
53
+ {
54
+ name: "superpowers-marketplace",
55
+ displayName: "Superpowers",
56
+ source: {
57
+ source: "github",
58
+ repo: "obra/superpowers-marketplace",
59
+ },
60
+ description: "Curated Claude Code plugins for skills and workflows",
61
+ featured: true,
62
+ },
52
63
  {
53
64
  name: "claude-code-plugins",
54
65
  displayName: "Anthropic Deprecated",
@@ -59,6 +70,7 @@ export const defaultMarketplaces: Marketplace[] = [
59
70
  description:
60
71
  "Legacy demo plugins from Anthropic (deprecated - use Official instead)",
61
72
  official: true,
73
+ deprecated: true,
62
74
  },
63
75
  ];
64
76
 
@@ -113,6 +125,7 @@ export function getAllMarketplaces(
113
125
  official:
114
126
  defaultMp?.official ?? repo.toLowerCase().includes("anthropics/"),
115
127
  featured: defaultMp?.featured,
128
+ deprecated: defaultMp?.deprecated,
116
129
  });
117
130
  }
118
131
  }
@@ -126,13 +139,16 @@ export function getAllMarketplaces(
126
139
  }
127
140
  }
128
141
 
129
- // Sort: Magus first, then alphabetically
142
+ // Sort: Magus first, deprecated last, then alphabetically
130
143
  return Array.from(all.values()).sort((a, b) => {
131
144
  // Magus (MadAppGang) always first
132
145
  const aIsMag = a.source.repo?.toLowerCase().includes("madappgang/");
133
146
  const bIsMag = b.source.repo?.toLowerCase().includes("madappgang/");
134
147
  if (aIsMag && !bIsMag) return -1;
135
148
  if (!aIsMag && bIsMag) return 1;
149
+ // Deprecated entries always last
150
+ if (a.deprecated && !b.deprecated) return 1;
151
+ if (!a.deprecated && b.deprecated) return -1;
136
152
  // Then alphabetically by display name
137
153
  return (a.displayName || a.name).localeCompare(b.displayName || b.name);
138
154
  });
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Read/write the managed-alias name in Claude Code's `settings.json`.
3
+ *
4
+ * The alias is computer-wide (one name per user), so it lives in
5
+ * `~/.claude/settings.json` only — no project scope. We store it under a
6
+ * `claudeup` namespace to avoid colliding with Claude Code's own keys:
7
+ *
8
+ * {
9
+ * "model": "sonnet",
10
+ * "claudeup": {
11
+ * "aliasName": "c"
12
+ * }
13
+ * }
14
+ *
15
+ * Flag values are NOT stored here. They live in the shell rc file's managed
16
+ * block and are read on screen mount via `parseAliasFromRc`.
17
+ */
18
+ import { readGlobalSettings, writeGlobalSettings, } from "./claude-settings.js";
19
+ import { DEFAULT_ALIAS_NAME, validateAliasName } from "./alias-store.js";
20
+ /**
21
+ * Load the user's preferred alias name from `~/.claude/settings.json`.
22
+ * Returns the default `"c"` when the key is absent or invalid.
23
+ */
24
+ export async function loadAliasName() {
25
+ const settings = (await readGlobalSettings());
26
+ const stored = settings.claudeup?.aliasName;
27
+ if (typeof stored !== "string")
28
+ return DEFAULT_ALIAS_NAME;
29
+ if (validateAliasName(stored) !== null)
30
+ return DEFAULT_ALIAS_NAME;
31
+ return stored;
32
+ }
33
+ /**
34
+ * Save the alias name to `~/.claude/settings.json`, preserving all other
35
+ * keys (including unrelated Claude Code settings). No-op without throwing
36
+ * if the name fails validation — caller should validate first.
37
+ */
38
+ export async function saveAliasName(name) {
39
+ if (validateAliasName(name) !== null)
40
+ return;
41
+ const settings = (await readGlobalSettings());
42
+ const next = {
43
+ ...settings,
44
+ claudeup: {
45
+ ...(settings.claudeup ?? {}),
46
+ aliasName: name,
47
+ },
48
+ };
49
+ // claude-settings.writeGlobalSettings accepts the same shape we received.
50
+ await writeGlobalSettings(next);
51
+ }