magus-cli 0.0.0-placeholder → 7.0.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 +309 -0
- package/bin/magus.js +170 -0
- package/package.json +67 -3
- package/scripts/build-binaries.ts +113 -0
- package/scripts/capture-builtin.ts +412 -0
- package/scripts/check-optional-deps.ts +49 -0
- package/scripts/test-isolated.ts +49 -0
- package/scripts/verify-binary.ts +95 -0
- package/scripts/verify-community-registry.ts +272 -0
- package/src/__tests__/active-closure.test.ts +184 -0
- package/src/__tests__/alias-adopt.test.ts +365 -0
- package/src/__tests__/alias-parser.test.ts +417 -0
- package/src/__tests__/alias-shell-writer.test.ts +681 -0
- package/src/__tests__/alias-store.test.ts +163 -0
- package/src/__tests__/binary-signing.test.ts +120 -0
- package/src/__tests__/blackbox-active-closure.test.ts +119 -0
- package/src/__tests__/blackbox-convergence.test.ts +334 -0
- package/src/__tests__/blackbox-parity.test.ts +684 -0
- package/src/__tests__/blackbox-plugin-state.test.ts +523 -0
- package/src/__tests__/blackbox-tool-state.test.ts +669 -0
- package/src/__tests__/cache-provenance.test.ts +84 -0
- package/src/__tests__/capture-builtin.test.ts +92 -0
- package/src/__tests__/catalog-cache-store.test.ts +309 -0
- package/src/__tests__/catalog-notice.test.ts +155 -0
- package/src/__tests__/cli-ansi.test.ts +297 -0
- package/src/__tests__/cli-apply-seams.test.ts +288 -0
- package/src/__tests__/cli-live.test.ts +391 -0
- package/src/__tests__/cli-router.test.ts +240 -0
- package/src/__tests__/cli-tool-commands.test.ts +239 -0
- package/src/__tests__/cli-tools-adapter.test.ts +294 -0
- package/src/__tests__/cli-update-view.test.ts +445 -0
- package/src/__tests__/community-fetch.test.ts +545 -0
- package/src/__tests__/community-registry.test.ts +269 -0
- package/src/__tests__/community-staleness.test.ts +720 -0
- package/src/__tests__/component-badge.test.ts +98 -0
- package/src/__tests__/component-icons.test.ts +94 -0
- package/src/__tests__/content-drift.test.ts +353 -0
- package/src/__tests__/conventions-integration.test.ts +830 -0
- package/src/__tests__/conventions-manager.test.ts +1236 -0
- package/src/__tests__/doctor-bins.test.ts +43 -0
- package/src/__tests__/doctor.test.ts +463 -0
- package/src/__tests__/dotenv.test.ts +195 -0
- package/src/__tests__/enabled-not-installed.test.ts +94 -0
- package/src/__tests__/file-locking.test.ts +208 -0
- package/src/__tests__/fixtures/tool-probe/brew-list-versions-tmux.txt +2 -0
- package/src/__tests__/fixtures/tool-probe/brew-outdated-behind-homebrew-spec.txt +29 -0
- package/src/__tests__/fixtures/tool-probe/brew-outdated-tmux-current.txt +7 -0
- package/src/__tests__/fixtures/tool-probe/brew-outdated-unknown-formula.txt +6 -0
- package/src/__tests__/fixtures/tool-probe/go-version-m-tmux-mcp.txt +27 -0
- package/src/__tests__/fixtures/tool-probe/npm-dist-tags-missing-scoped.txt +4 -0
- package/src/__tests__/fixtures/tool-probe/npm-dist-tags-openai-codex.txt +4 -0
- package/src/__tests__/fixtures/tool-probe/pypi-cowsay.txt +3 -0
- package/src/__tests__/fixtures/tool-probe/uv-receipt-browser-use.toml +10 -0
- package/src/__tests__/fixtures/tool-probe/uv-receipt-cowsay-git.toml +8 -0
- package/src/__tests__/fixtures/tool-probe/uv-receipt-cowsay-pinned.toml +8 -0
- package/src/__tests__/fixtures/tool-probe/uv-receipt-graphifyy.toml +7 -0
- package/src/__tests__/fixtures/tool-probe/uv-receipt-mcp.toml +6 -0
- package/src/__tests__/fixtures/tool-probe/uv-receipt-serena-agent.toml +9 -0
- package/src/__tests__/fixtures/tool-probe/uv-tool-list-outdated-pinned.txt +5 -0
- package/src/__tests__/fixtures/tool-probe/uv-tool-list-outdated.txt +11 -0
- package/src/__tests__/fixtures/tool-probe/uv-tool-list.txt +22 -0
- package/src/__tests__/footer-hints.test.ts +40 -0
- package/src/__tests__/format-command.test.ts +78 -0
- package/src/__tests__/git-worktree.test.ts +108 -0
- package/src/__tests__/github-budget.test.ts +206 -0
- package/src/__tests__/gitignore-check.test.ts +71 -0
- package/src/__tests__/gitignore-detector.test.ts +176 -0
- package/src/__tests__/gitignore-fixer.test.ts +317 -0
- package/src/__tests__/gitignore-resolver.test.ts +193 -0
- package/src/__tests__/gitignore-service.test.ts +137 -0
- package/src/__tests__/go-module.test.ts +43 -0
- package/src/__tests__/hook-import-policy.test.ts +90 -0
- package/src/__tests__/hook-process.test.ts +256 -0
- package/src/__tests__/hook-registration.test.ts +224 -0
- package/src/__tests__/install-command.test.ts +81 -0
- package/src/__tests__/install-plan.test.ts +104 -0
- package/src/__tests__/launcher-signal.test.ts +308 -0
- package/src/__tests__/local-marketplace-scan.test.ts +174 -0
- package/src/__tests__/manifest.test.ts +281 -0
- package/src/__tests__/markdown-renderer.test.ts +142 -0
- package/src/__tests__/marketplace-badge.test.ts +140 -0
- package/src/__tests__/marketplace-refresh.test.ts +335 -0
- package/src/__tests__/marketplace-version.test.ts +84 -0
- package/src/__tests__/marketplaces.test.ts +128 -0
- package/src/__tests__/mate-availability.test.ts +156 -0
- package/src/__tests__/mate-catalog.test.ts +295 -0
- package/src/__tests__/mcp-registry.test.ts +158 -0
- package/src/__tests__/model-visuals.test.tsx +2466 -0
- package/src/__tests__/models-adapter.test.ts +332 -0
- package/src/__tests__/models-cli.test.ts +273 -0
- package/src/__tests__/models-core.test.ts +802 -0
- package/src/__tests__/models-manager.test.ts +500 -0
- package/src/__tests__/models-presets-marketplace.test.ts +168 -0
- package/src/__tests__/models-screen-state.test.ts +315 -0
- package/src/__tests__/moved-marketplace.test.ts +86 -0
- package/src/__tests__/open-file.test.ts +59 -0
- package/src/__tests__/owned-marketplaces.test.ts +93 -0
- package/src/__tests__/plugin-checkout.test.ts +66 -0
- package/src/__tests__/plugin-cli-argv.test.ts +109 -0
- package/src/__tests__/plugin-contents.test.ts +371 -0
- package/src/__tests__/plugin-manager-fallback.test.ts +320 -0
- package/src/__tests__/plugin-releases.test.ts +98 -0
- package/src/__tests__/plugin-requires.test.ts +187 -0
- package/src/__tests__/plugin-row-text.test.ts +336 -0
- package/src/__tests__/plugin-setup.test.ts +1016 -0
- package/src/__tests__/profile-adopt.test.ts +177 -0
- package/src/__tests__/profile-command.test.ts +110 -0
- package/src/__tests__/profile-materializer.test.ts +238 -0
- package/src/__tests__/profile-sync.test.ts +154 -0
- package/src/__tests__/registry-version-resolution.test.ts +88 -0
- package/src/__tests__/resolve-executable.test.ts +42 -0
- package/src/__tests__/resolver.test.ts +351 -0
- package/src/__tests__/scope-action.test.ts +134 -0
- package/src/__tests__/scope-squares.test.tsx +164 -0
- package/src/__tests__/selection-scope.test.ts +100 -0
- package/src/__tests__/settings-file.test.ts +179 -0
- package/src/__tests__/shell-script-callers.test.ts +92 -0
- package/src/__tests__/skill-install-files.test.ts +136 -0
- package/src/__tests__/skills-catalog-client.test.ts +48 -0
- package/src/__tests__/style-wrap.test.ts +220 -0
- package/src/__tests__/styles-manager.test.ts +1098 -0
- package/src/__tests__/styles-origins.test.ts +414 -0
- package/src/__tests__/styles-screen-state.test.ts +457 -0
- package/src/__tests__/styles-status-line.test.ts +72 -0
- package/src/__tests__/styles-sync.test.ts +452 -0
- package/src/__tests__/symlink-manager.test.ts +180 -0
- package/src/__tests__/tabbar-layout.test.ts +100 -0
- package/src/__tests__/terminology-filler.test.ts +214 -0
- package/src/__tests__/theme-adaptive-colors.test.ts +354 -0
- package/src/__tests__/theme-env.test.ts +281 -0
- package/src/__tests__/theme-resolve.test.ts +380 -0
- package/src/__tests__/tool-probe.test.ts +1400 -0
- package/src/__tests__/toolchain.test.ts +232 -0
- package/src/__tests__/ui-version-writers.test.ts +88 -0
- package/src/__tests__/update-apply.test.ts +637 -0
- package/src/__tests__/update-plan.test.ts +1373 -0
- package/src/__tests__/uppercase-keybindings.test.ts +101 -0
- package/src/__tests__/useGitignoreModal.test.ts +79 -0
- package/src/__tests__/version-check-refresh.test.ts +45 -0
- package/src/__tests__/version-snapshot.test.ts +212 -0
- package/src/__tests__/version-unknown-state.test.ts +388 -0
- package/src/__tests__/worktree-registry-resolution.test.ts +107 -0
- package/src/cli/ansi.ts +520 -0
- package/src/cli/bootstrap.ts +134 -0
- package/src/cli/doctor.ts +218 -0
- package/src/cli/hook.ts +129 -0
- package/src/cli/install.ts +382 -0
- package/src/cli/live.ts +374 -0
- package/src/cli/models.ts +297 -0
- package/src/cli/profile.ts +197 -0
- package/src/cli/prompt.ts +32 -0
- package/src/cli/router.ts +207 -0
- package/src/cli/update-view.ts +526 -0
- package/src/cli/update.ts +613 -0
- package/src/cli/upgrade.ts +97 -0
- package/src/data/alias-flags.ts +301 -0
- package/src/data/cli-tools.ts +146 -0
- package/src/data/community-styles.ts +531 -0
- package/src/data/gitignore-defaults.ts +33 -0
- package/src/data/gitignore-reasons.ts +93 -0
- package/src/data/gitignore-templates.ts +25 -0
- package/src/data/marketplaces.ts +278 -0
- package/src/data/mcp-servers.ts +51 -0
- package/src/data/models-presets.ts +329 -0
- package/src/data/predefined-profiles.ts +252 -0
- package/src/data/settings-catalog.ts +714 -0
- package/src/data/skill-repos.ts +91 -0
- package/src/data/styles/asd-ste100.md +38 -0
- package/src/data/styles/calibrated.md +24 -0
- package/src/data/styles/direct.md +23 -0
- package/src/data/styles/evidence-first.md +22 -0
- package/src/data/styles/explanatory.md +23 -0
- package/src/data/styles/index.ts +57 -0
- package/src/data/styles/no-slop.md +32 -0
- package/src/data/styles/plain-language.md +23 -0
- package/src/data/styles/structured.md +30 -0
- package/src/data/styles/terminology.md +28 -0
- package/src/data/styles/terse.md +20 -0
- package/src/main.tsx +110 -0
- package/src/markdown.d.ts +13 -0
- package/src/opentui.d.ts +203 -0
- package/src/services/active-closure.ts +117 -0
- package/src/services/alias-settings.ts +60 -0
- package/src/services/alias-shell-writer.ts +1244 -0
- package/src/services/alias-store.ts +172 -0
- package/src/services/binary-signing.ts +78 -0
- package/src/services/catalog-cache-store.ts +316 -0
- package/src/services/claude-cli.ts +304 -0
- package/src/services/claude-runner.ts +31 -0
- package/src/services/claude-settings.ts +1589 -0
- package/src/services/cli-tool-commands.ts +133 -0
- package/src/services/community-fetcher.ts +90 -0
- package/src/services/community-styles.ts +1194 -0
- package/src/services/content-drift.ts +208 -0
- package/src/services/conventions-manager.ts +945 -0
- package/src/services/doctor-bins.ts +63 -0
- package/src/services/doctor.ts +570 -0
- package/src/services/dotenv.ts +181 -0
- package/src/services/git-worktree.ts +129 -0
- package/src/services/github-budget.ts +274 -0
- package/src/services/gitignore-check.ts +63 -0
- package/src/services/gitignore-detector.ts +167 -0
- package/src/services/gitignore-fixer.ts +280 -0
- package/src/services/gitignore-resolver.ts +208 -0
- package/src/services/gitignore-service.ts +167 -0
- package/src/services/hook-registration.ts +218 -0
- package/src/services/install-plan.ts +107 -0
- package/src/services/local-marketplace.ts +587 -0
- package/src/services/manifest.ts +250 -0
- package/src/services/marketplace-catalog-git.ts +173 -0
- package/src/services/marketplace-catalog.ts +129 -0
- package/src/services/marketplace-fetcher.ts +400 -0
- package/src/services/marketplace-refresh.ts +253 -0
- package/src/services/marketplace-sync.ts +145 -0
- package/src/services/mate-availability.ts +133 -0
- package/src/services/mate-catalog.ts +265 -0
- package/src/services/mcp-registry.ts +171 -0
- package/src/services/models-core.ts +969 -0
- package/src/services/models-manager.ts +606 -0
- package/src/services/plugin-checkout.ts +186 -0
- package/src/services/plugin-manager.ts +878 -0
- package/src/services/plugin-mcp-config.ts +242 -0
- package/src/services/plugin-releases.ts +47 -0
- package/src/services/plugin-requires.ts +227 -0
- package/src/services/plugin-setup.ts +965 -0
- package/src/services/profile-adopt.ts +206 -0
- package/src/services/profile-materializer.ts +133 -0
- package/src/services/profile-sync.ts +158 -0
- package/src/services/profiles.ts +225 -0
- package/src/services/resolver.ts +344 -0
- package/src/services/settings-file.ts +69 -0
- package/src/services/settings-manager.ts +307 -0
- package/src/services/skills-manager.ts +1111 -0
- package/src/services/skillsmp-client.ts +193 -0
- package/src/services/styles-manager.ts +1294 -0
- package/src/services/symlink-manager.ts +189 -0
- package/src/services/terminology-filler.ts +266 -0
- package/src/services/tool-probe.ts +1637 -0
- package/src/services/toolchain.ts +431 -0
- package/src/services/update-engine.ts +691 -0
- package/src/services/update-plan.ts +863 -0
- package/src/services/version-check.ts +127 -0
- package/src/services/version-snapshot.ts +189 -0
- package/src/tui.tsx +112 -0
- package/src/types/bun.d.ts +27 -0
- package/src/types/gitignore.ts +68 -0
- package/src/types/index.ts +447 -0
- package/src/ui/App.tsx +538 -0
- package/src/ui/adapters/catalogNotice.ts +122 -0
- package/src/ui/adapters/cliToolsAdapter.ts +176 -0
- package/src/ui/adapters/modelsAdapter.ts +213 -0
- package/src/ui/adapters/pluginsAdapter.ts +413 -0
- package/src/ui/adapters/settingsAdapter.ts +168 -0
- package/src/ui/adapters/skillsAdapter.ts +229 -0
- package/src/ui/adapters/stylesAdapter.ts +403 -0
- package/src/ui/components/CategoryHeader.tsx +64 -0
- package/src/ui/components/EmptyFilterState.tsx +32 -0
- package/src/ui/components/FlagDetailEditor.tsx +0 -0
- package/src/ui/components/ScopeIndicator.tsx +63 -0
- package/src/ui/components/ScrollableList.tsx +95 -0
- package/src/ui/components/SearchInput.tsx +57 -0
- package/src/ui/components/StyledText.tsx +75 -0
- package/src/ui/components/TabBar.tsx +101 -0
- package/src/ui/components/layout/FooterHints.tsx +78 -0
- package/src/ui/components/layout/Panel.tsx +63 -0
- package/src/ui/components/layout/ProgressBar.tsx +44 -0
- package/src/ui/components/layout/ScopeTabs.tsx +54 -0
- package/src/ui/components/layout/ScreenLayout.tsx +321 -0
- package/src/ui/components/layout/index.ts +9 -0
- package/src/ui/components/modals/ConfirmModal.tsx +62 -0
- package/src/ui/components/modals/InputModal.tsx +75 -0
- package/src/ui/components/modals/LoadingModal.tsx +51 -0
- package/src/ui/components/modals/MessageModal.tsx +66 -0
- package/src/ui/components/modals/ModalContainer.tsx +159 -0
- package/src/ui/components/modals/SelectModal.tsx +90 -0
- package/src/ui/components/modals/index.ts +6 -0
- package/src/ui/components/primitives/ActionHints.tsx +44 -0
- package/src/ui/components/primitives/DetailSection.tsx +23 -0
- package/src/ui/components/primitives/KeyValueLine.tsx +26 -0
- package/src/ui/components/primitives/ListCategoryRow.tsx +45 -0
- package/src/ui/components/primitives/MetaText.tsx +40 -0
- package/src/ui/components/primitives/ScopeDetail.tsx +67 -0
- package/src/ui/components/primitives/ScopeSquares.tsx +58 -0
- package/src/ui/components/primitives/SelectableRow.tsx +30 -0
- package/src/ui/components/primitives/index.ts +9 -0
- package/src/ui/hooks/index.ts +2 -0
- package/src/ui/hooks/useGitignoreModal.ts +95 -0
- package/src/ui/hooks/useKeyboard.ts +24 -0
- package/src/ui/hooks/useKeyboardHandler.ts +63 -0
- package/src/ui/registry.ts +33 -0
- package/src/ui/renderers/cliToolRenderers.tsx +326 -0
- package/src/ui/renderers/gitignoreRenderers.tsx +80 -0
- package/src/ui/renderers/markdownRenderer.tsx +302 -0
- package/src/ui/renderers/mcpRenderers.tsx +149 -0
- package/src/ui/renderers/modelRenderers.tsx +1309 -0
- package/src/ui/renderers/modelVisuals.tsx +1402 -0
- package/src/ui/renderers/pluginRenderers.tsx +1089 -0
- package/src/ui/renderers/pluginRowText.ts +70 -0
- package/src/ui/renderers/pluginStatusText.ts +56 -0
- package/src/ui/renderers/profileRenderers.tsx +406 -0
- package/src/ui/renderers/settingsRenderers.tsx +218 -0
- package/src/ui/renderers/skillRenderers.tsx +479 -0
- package/src/ui/renderers/styleRenderers.tsx +813 -0
- package/src/ui/screens/AliasScreen.tsx +1558 -0
- package/src/ui/screens/CliToolsScreen.tsx +367 -0
- package/src/ui/screens/EnvVarsScreen.tsx +221 -0
- package/src/ui/screens/GitignoreScreen.tsx +562 -0
- package/src/ui/screens/McpRegistryScreen.tsx +366 -0
- package/src/ui/screens/McpScreen.tsx +256 -0
- package/src/ui/screens/ModelsScreen.tsx +542 -0
- package/src/ui/screens/PluginsScreen.tsx +1464 -0
- package/src/ui/screens/ProfilesScreen.tsx +438 -0
- package/src/ui/screens/SkillsScreen.tsx +853 -0
- package/src/ui/screens/StylesScreen.tsx +1064 -0
- package/src/ui/screens/index.ts +11 -0
- package/src/ui/state/AppContext.tsx +233 -0
- package/src/ui/state/DimensionsContext.tsx +154 -0
- package/src/ui/state/reducer.ts +810 -0
- package/src/ui/state/types.ts +503 -0
- package/src/ui/theme-mode.ts +352 -0
- package/src/ui/theme-resolve.ts +147 -0
- package/src/ui/theme.ts +175 -0
- package/src/utils/clipboard.ts +58 -0
- package/src/utils/command-utils.ts +144 -0
- package/src/utils/config-dir.ts +45 -0
- package/src/utils/file-locking.ts +146 -0
- package/src/utils/fuzzy-search.ts +138 -0
- package/src/utils/go-module.ts +36 -0
- package/src/utils/open-file.ts +84 -0
- package/src/utils/run.ts +67 -0
- package/src/utils/string-utils.ts +88 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import type { LocalMarketplace } from "../services/local-marketplace.js";
|
|
2
|
+
import type { Marketplace } from "../types/index.js";
|
|
3
|
+
import { formatMarketplaceName } from "../utils/string-utils.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Known repo aliases — GitHub redirects the old repo to the new one,
|
|
7
|
+
* so these should be treated as identical for deduplication.
|
|
8
|
+
*/
|
|
9
|
+
const REPO_ALIASES: Record<string, string> = {
|
|
10
|
+
"madappgang/claude-code": "madappgang/magus",
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/** Normalize a repo string through known aliases. */
|
|
14
|
+
function normalizeRepo(repo: string): string {
|
|
15
|
+
const lower = repo.toLowerCase();
|
|
16
|
+
return REPO_ALIASES[lower] || lower;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Deprecated marketplace names that should be hidden if the canonical
|
|
21
|
+
* marketplace is already present. Maps deprecated name → canonical name.
|
|
22
|
+
*/
|
|
23
|
+
export const deprecatedMarketplaces: Record<string, string> = {
|
|
24
|
+
"mag-claude-plugins": "magus",
|
|
25
|
+
"MadAppGang-claude-code": "magus",
|
|
26
|
+
superpowers: "superpowers-marketplace",
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const defaultMarketplaces: Marketplace[] = [
|
|
30
|
+
{
|
|
31
|
+
name: "magus",
|
|
32
|
+
displayName: "Magus",
|
|
33
|
+
source: {
|
|
34
|
+
source: "github",
|
|
35
|
+
repo: "MadAppGang/magus",
|
|
36
|
+
},
|
|
37
|
+
description:
|
|
38
|
+
"Professional plugins for frontend, backend, and code analysis",
|
|
39
|
+
official: false,
|
|
40
|
+
featured: true, // Always show plugins (expanded by default)
|
|
41
|
+
owned: true, // Ours: always listed, sorted to the top
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "magus-marketing",
|
|
45
|
+
displayName: "Magus Marketing",
|
|
46
|
+
source: {
|
|
47
|
+
source: "github",
|
|
48
|
+
repo: "MadAppGang/magus-marketing",
|
|
49
|
+
},
|
|
50
|
+
description: "AI image generation and video editing plugins",
|
|
51
|
+
official: false,
|
|
52
|
+
owned: true, // Ours: always listed even when uninstalled, sorted to the top
|
|
53
|
+
// Not featured: a niche channel, so keep it collapsed by default.
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: "magus-alpha",
|
|
57
|
+
displayName: "Magus Alpha",
|
|
58
|
+
source: {
|
|
59
|
+
source: "github",
|
|
60
|
+
repo: "MadAppGang/magus-alpha",
|
|
61
|
+
},
|
|
62
|
+
description:
|
|
63
|
+
"Experimental plugins with evolving interfaces — may change or be withdrawn without notice",
|
|
64
|
+
official: false,
|
|
65
|
+
owned: true, // Ours: always listed even when uninstalled, sorted to the top
|
|
66
|
+
// Not featured: experimental, so keep it collapsed by default.
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: "claude-plugins-official",
|
|
70
|
+
displayName: "Anthropic Official",
|
|
71
|
+
source: {
|
|
72
|
+
source: "github",
|
|
73
|
+
repo: "anthropics/claude-plugins-official",
|
|
74
|
+
},
|
|
75
|
+
description:
|
|
76
|
+
"Official Anthropic-managed directory of high quality Claude Code plugins",
|
|
77
|
+
official: true,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: "superpowers-marketplace",
|
|
81
|
+
displayName: "Superpowers",
|
|
82
|
+
source: {
|
|
83
|
+
source: "github",
|
|
84
|
+
repo: "obra/superpowers-marketplace",
|
|
85
|
+
},
|
|
86
|
+
description: "Curated Claude Code plugins for skills and workflows",
|
|
87
|
+
featured: true,
|
|
88
|
+
},
|
|
89
|
+
// Community marketplaces. Each `name` MUST equal the upstream
|
|
90
|
+
// .claude-plugin/marketplace.json "name" field — plugin ids are built as
|
|
91
|
+
// `${plugin.name}@${marketplaceName}`, so a mismatch here breaks install
|
|
92
|
+
// with a "not found in marketplace" error rather than anything obvious.
|
|
93
|
+
//
|
|
94
|
+
// All carry `featured` because that is the only flag that makes a
|
|
95
|
+
// third-party marketplace visible: the gate in plugin-manager is
|
|
96
|
+
// `configured || official || featured || owned`, and an unflagged entry is
|
|
97
|
+
// just a name→repo mapping for auto-recovery. `official` means Anthropic
|
|
98
|
+
// and `owned` means MadAppGang, so neither applies.
|
|
99
|
+
{
|
|
100
|
+
name: "mattpocock",
|
|
101
|
+
displayName: "Matt Pocock",
|
|
102
|
+
source: {
|
|
103
|
+
source: "github",
|
|
104
|
+
repo: "mattpocock/skills",
|
|
105
|
+
},
|
|
106
|
+
description:
|
|
107
|
+
"Agent skills for real engineering — grilling, spec and ticket flows, TDD, code review, domain modelling",
|
|
108
|
+
featured: true,
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: "ecc",
|
|
112
|
+
displayName: "Everything Claude Code",
|
|
113
|
+
source: {
|
|
114
|
+
source: "github",
|
|
115
|
+
repo: "affaan-m/everything-claude-code",
|
|
116
|
+
},
|
|
117
|
+
description:
|
|
118
|
+
"Operator layer for agent harnesses — 68 agents, 286 skills, and command shims",
|
|
119
|
+
featured: true,
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: "ponytail",
|
|
123
|
+
displayName: "Ponytail",
|
|
124
|
+
source: {
|
|
125
|
+
source: "github",
|
|
126
|
+
repo: "DietrichGebert/ponytail",
|
|
127
|
+
},
|
|
128
|
+
description:
|
|
129
|
+
"Pushes the agent toward the smallest solution that works — YAGNI, and the standard library before custom code",
|
|
130
|
+
featured: true,
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
name: "addy-agent-skills",
|
|
134
|
+
displayName: "Addy Osmani Skills",
|
|
135
|
+
source: {
|
|
136
|
+
source: "github",
|
|
137
|
+
repo: "addyosmani/agent-skills",
|
|
138
|
+
},
|
|
139
|
+
description: "Production-grade engineering skills for AI coding agents",
|
|
140
|
+
featured: true,
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
name: "humanizer",
|
|
144
|
+
displayName: "Humanizer",
|
|
145
|
+
source: {
|
|
146
|
+
source: "github",
|
|
147
|
+
repo: "blader/humanizer",
|
|
148
|
+
},
|
|
149
|
+
description: "Removes signs of AI-generated writing from text",
|
|
150
|
+
featured: true,
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
name: "opendesign",
|
|
154
|
+
displayName: "OpenDesign",
|
|
155
|
+
source: {
|
|
156
|
+
source: "github",
|
|
157
|
+
repo: "manalkaff/opendesign",
|
|
158
|
+
},
|
|
159
|
+
description:
|
|
160
|
+
"Designs and redesigns frontend UI and marketing graphics, routing to specialist skills per artifact type",
|
|
161
|
+
featured: true,
|
|
162
|
+
},
|
|
163
|
+
// A community port, deliberately, because the original cannot be installed.
|
|
164
|
+
// pstack is published by Cursor in cursor/plugins, whose manifests all live
|
|
165
|
+
// under `.cursor-plugin/` — measured over the whole tree: 61 `.cursor-plugin`
|
|
166
|
+
// files, 0 `.claude-plugin` files. Claude Code reads
|
|
167
|
+
// `.claude-plugin/marketplace.json` only, so `claude plugin marketplace add
|
|
168
|
+
// cursor/plugins` cannot resolve it. Same reason github/spec-kit is absent.
|
|
169
|
+
//
|
|
170
|
+
// Of the ports that relocate the manifest, this is the most starred: 95
|
|
171
|
+
// against pstack-claude's 94. backnotprop/pstack has more (140) but carries
|
|
172
|
+
// no Claude manifest either, and peadar/pstack (261) is an unrelated C
|
|
173
|
+
// stack-trace tool.
|
|
174
|
+
{
|
|
175
|
+
name: "open-pstack",
|
|
176
|
+
displayName: "pstack",
|
|
177
|
+
source: {
|
|
178
|
+
source: "github",
|
|
179
|
+
repo: "ericlitman/open-pstack",
|
|
180
|
+
},
|
|
181
|
+
description:
|
|
182
|
+
"Rigorous, parallelizable agent workflows that favour going deep before going fast. Community port of Cursor's pstack",
|
|
183
|
+
featured: true,
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
name: "claude-code-plugins",
|
|
187
|
+
displayName: "Anthropic Deprecated",
|
|
188
|
+
source: {
|
|
189
|
+
source: "github",
|
|
190
|
+
repo: "anthropics/claude-code",
|
|
191
|
+
},
|
|
192
|
+
description:
|
|
193
|
+
"Legacy demo plugins from Anthropic (deprecated - use Official instead)",
|
|
194
|
+
official: true,
|
|
195
|
+
deprecated: true,
|
|
196
|
+
},
|
|
197
|
+
];
|
|
198
|
+
|
|
199
|
+
export function getMarketplaceByName(name: string): Marketplace | undefined {
|
|
200
|
+
return defaultMarketplaces.find((m) => m.name === name);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Get all available marketplaces from local cache + hardcoded defaults
|
|
204
|
+
// Local cache is primary source of truth, defaults are fallback
|
|
205
|
+
// Deduplicates by normalized repo URL to avoid showing same marketplace twice
|
|
206
|
+
export function getAllMarketplaces(
|
|
207
|
+
localMarketplaces?: Map<string, LocalMarketplace>,
|
|
208
|
+
): Marketplace[] {
|
|
209
|
+
const all = new Map<string, Marketplace>();
|
|
210
|
+
const seenRepos = new Set<string>();
|
|
211
|
+
|
|
212
|
+
// Primary source: local cache (what's actually cloned)
|
|
213
|
+
if (localMarketplaces) {
|
|
214
|
+
for (const [name, local] of localMarketplaces) {
|
|
215
|
+
const repo = normalizeRepo(local.gitRepo || "");
|
|
216
|
+
|
|
217
|
+
// Skip deprecated marketplaces if their canonical replacement exists
|
|
218
|
+
// (or will be added from defaults)
|
|
219
|
+
const canonical = deprecatedMarketplaces[name];
|
|
220
|
+
if (canonical) {
|
|
221
|
+
// If canonical already in the map or in defaults, skip this entry
|
|
222
|
+
if (
|
|
223
|
+
all.has(canonical) ||
|
|
224
|
+
defaultMarketplaces.some((m) => m.name === canonical)
|
|
225
|
+
) {
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Skip if another marketplace already claimed this repo URL
|
|
231
|
+
if (repo && seenRepos.has(repo)) continue;
|
|
232
|
+
if (repo) seenRepos.add(repo);
|
|
233
|
+
|
|
234
|
+
// Check if this marketplace has defaults (for official/featured flags)
|
|
235
|
+
const defaultMp = defaultMarketplaces.find((m) => m.name === name);
|
|
236
|
+
|
|
237
|
+
all.set(name, {
|
|
238
|
+
name,
|
|
239
|
+
// Prefer default displayName over stale local clone data
|
|
240
|
+
displayName:
|
|
241
|
+
defaultMp?.displayName || local.name || formatMarketplaceName(name),
|
|
242
|
+
source: {
|
|
243
|
+
source: "github" as const,
|
|
244
|
+
repo: defaultMp?.source.repo || local.gitRepo || "",
|
|
245
|
+
},
|
|
246
|
+
description: defaultMp?.description || local.description || "",
|
|
247
|
+
official:
|
|
248
|
+
defaultMp?.official ?? repo.toLowerCase().includes("anthropics/"),
|
|
249
|
+
featured: defaultMp?.featured,
|
|
250
|
+
deprecated: defaultMp?.deprecated,
|
|
251
|
+
owned: defaultMp?.owned,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Fallback: hardcoded defaults (only if their repo isn't already represented)
|
|
257
|
+
for (const mp of defaultMarketplaces) {
|
|
258
|
+
const repo = normalizeRepo(mp.source.repo || "");
|
|
259
|
+
if (!all.has(mp.name) && !seenRepos.has(repo)) {
|
|
260
|
+
all.set(mp.name, mp);
|
|
261
|
+
if (repo) seenRepos.add(repo);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Sort: Magus first, deprecated last, then alphabetically
|
|
266
|
+
return Array.from(all.values()).sort((a, b) => {
|
|
267
|
+
// Magus (MadAppGang) always first
|
|
268
|
+
const aIsMag = a.source.repo?.toLowerCase().includes("madappgang/");
|
|
269
|
+
const bIsMag = b.source.repo?.toLowerCase().includes("madappgang/");
|
|
270
|
+
if (aIsMag && !bIsMag) return -1;
|
|
271
|
+
if (!aIsMag && bIsMag) return 1;
|
|
272
|
+
// Deprecated entries always last
|
|
273
|
+
if (a.deprecated && !b.deprecated) return 1;
|
|
274
|
+
if (!a.deprecated && b.deprecated) return -1;
|
|
275
|
+
// Then alphabetically by display name
|
|
276
|
+
return (a.displayName || a.name).localeCompare(b.displayName || b.name);
|
|
277
|
+
});
|
|
278
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { McpServer } from "../types/index.js";
|
|
2
|
+
|
|
3
|
+
export function getMcpServersByCategory(
|
|
4
|
+
servers: McpServer[],
|
|
5
|
+
): Record<string, McpServer[]> {
|
|
6
|
+
const categories: Record<string, McpServer[]> = {};
|
|
7
|
+
for (const server of servers) {
|
|
8
|
+
if (!categories[server.category]) categories[server.category] = [];
|
|
9
|
+
categories[server.category].push(server);
|
|
10
|
+
}
|
|
11
|
+
return categories;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const categoryOrder = [
|
|
15
|
+
"browser",
|
|
16
|
+
"ai",
|
|
17
|
+
"design",
|
|
18
|
+
"dev-tools",
|
|
19
|
+
"cloud",
|
|
20
|
+
"database",
|
|
21
|
+
"seo",
|
|
22
|
+
"productivity",
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
export function getCategoryDisplayName(category: string): string {
|
|
26
|
+
const names: Record<string, string> = {
|
|
27
|
+
browser: "Browser & Automation",
|
|
28
|
+
ai: "AI & Search",
|
|
29
|
+
design: "Design & Creative",
|
|
30
|
+
"dev-tools": "Developer Tools",
|
|
31
|
+
cloud: "Cloud & Infrastructure",
|
|
32
|
+
database: "Database",
|
|
33
|
+
seo: "SEO & Analytics",
|
|
34
|
+
productivity: "Productivity",
|
|
35
|
+
};
|
|
36
|
+
return names[category] || category;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function getCategoryIcon(category: string): string {
|
|
40
|
+
const icons: Record<string, string> = {
|
|
41
|
+
browser: "🌐",
|
|
42
|
+
ai: "🤖",
|
|
43
|
+
design: "🎨",
|
|
44
|
+
"dev-tools": "🛠️",
|
|
45
|
+
cloud: "☁️",
|
|
46
|
+
database: "🗄️",
|
|
47
|
+
seo: "📈",
|
|
48
|
+
productivity: "📋",
|
|
49
|
+
};
|
|
50
|
+
return icons[category] || "📦";
|
|
51
|
+
}
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in presets and the shared agent → tier table.
|
|
3
|
+
*
|
|
4
|
+
* Hand-written constants rather than a glob over data files, for the same reason
|
|
5
|
+
* `src/data/styles/index.ts` hand-lists its embedded presets: a glob cannot resolve at
|
|
6
|
+
* `bun build --compile` time, and this module has to survive into the compiled binary — the
|
|
7
|
+
* hook path reads a project's own `models.json`, but `magus models list` and the TUI read
|
|
8
|
+
* these.
|
|
9
|
+
*
|
|
10
|
+
* Tiers name what a subagent is FOR, on one axis: `smart`, `normal`, `cheap`, plus `main` for
|
|
11
|
+
* the orchestrator. Which model serves each is the PRESET's call, and `smart` is not always
|
|
12
|
+
* its largest spend: `fable-advisor` sends the judgement calls to a different model (fable at
|
|
13
|
+
* `medium`) rather than a harder-working one. What holds everywhere is the other half: the
|
|
14
|
+
* thread dispatching the work is never strictly the biggest spend in the preset.
|
|
15
|
+
*/
|
|
16
|
+
import type { Effort, Grade, ModelsConfig } from "../services/models-core.js";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Which tier each subagent gets, shared by every preset.
|
|
20
|
+
*
|
|
21
|
+
* Keys are `subagent_type` strings exactly as the Agent tool receives them: bare names for
|
|
22
|
+
* Claude Code's built-ins, `plugin:name` for plugin agents. A name absent from this table
|
|
23
|
+
* falls to the preset's `fallback`, so a newly installed plugin routes sensibly on day one
|
|
24
|
+
* instead of silently inheriting the orchestrator's model.
|
|
25
|
+
*/
|
|
26
|
+
export const DEFAULT_AGENT_GRADES: Record<string, Grade> = {
|
|
27
|
+
// `smart` is for JUDGEMENT — work where the agent decides something a person would
|
|
28
|
+
// argue about, and where being wrong is expensive to undo.
|
|
29
|
+
Plan: "smart",
|
|
30
|
+
"dev:architect": "smart",
|
|
31
|
+
"dev:reviewer": "smart",
|
|
32
|
+
"dev:debugger": "smart",
|
|
33
|
+
"multimodel:deep-analyst": "smart",
|
|
34
|
+
|
|
35
|
+
// `normal`: transforms material it is GIVEN. Real work, bounded judgement.
|
|
36
|
+
//
|
|
37
|
+
// `spec-writer` reads an interview log and writes it up as a spec — the decisions were
|
|
38
|
+
// made during the interview, and this turns answers into structure. `synthesizer` merges
|
|
39
|
+
// reviews it is handed into one report against thresholds it is handed. Neither decides
|
|
40
|
+
// what is true; both were on `smart` because "writes an important document" got confused
|
|
41
|
+
// with "makes an expensive judgement".
|
|
42
|
+
"dev:spec-writer": "normal",
|
|
43
|
+
"dev:aggregator": "normal",
|
|
44
|
+
|
|
45
|
+
// Reading a lot is not analysing.
|
|
46
|
+
//
|
|
47
|
+
// `analyze` is read-only: it locates implementations, traces a path and reports
|
|
48
|
+
// file:line. That is high-volume retrieval with light inference — the same shape as
|
|
49
|
+
// `Explore`, with more structure in the output. It sat on `smart` because "investigates"
|
|
50
|
+
// sounds like thinking; what it actually does is look.
|
|
51
|
+
"code-search:analyze": "cheap",
|
|
52
|
+
|
|
53
|
+
// Search and bookkeeping: bounded, mechanical, high-volume.
|
|
54
|
+
Explore: "cheap",
|
|
55
|
+
"dev:stack-detector": "cheap",
|
|
56
|
+
"dev:docs": "cheap",
|
|
57
|
+
"terminal:tui-navigator": "cheap",
|
|
58
|
+
"statusline-setup": "cheap",
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The three workflows the `dev` plugin actually runs, and the agents each one dispatches.
|
|
63
|
+
*
|
|
64
|
+
* A single "17 agents" bar counts every agent magus has ever heard of, weighted equally —
|
|
65
|
+
* which is not how a session spends anything. Nobody runs all seventeen; they run ONE of
|
|
66
|
+
* these, and what it costs depends on which handful it reaches for. `/dev:investigate`
|
|
67
|
+
* touching two cheap agents and `/dev:dev` touching five across three tiers are different
|
|
68
|
+
* answers to "what will this preset cost me", and the lump sum shows neither.
|
|
69
|
+
*
|
|
70
|
+
* Read out of `plugins/dev/commands/{dev,debug,investigate}.md` — agents only. Those files
|
|
71
|
+
* also name skills (`dev:context-detection`, `code-search:investigate`) which are not
|
|
72
|
+
* dispatched through the Agent tool and so are not routed at all.
|
|
73
|
+
*
|
|
74
|
+
* ## `external` — the steps that leave Claude Code, whatever the preset says
|
|
75
|
+
*
|
|
76
|
+
* A workflow's use of an outside model is a property of the WORKFLOW, not of the routing
|
|
77
|
+
* config. `/dev:dev` runs a multi-model plan review at Phase 3 and a multi-model code review
|
|
78
|
+
* at Phase 5 because its command file says so, and switching from `Sonnet` to `Opus main`
|
|
79
|
+
* does not change that. So these steps are declared here beside the agents and are drawn
|
|
80
|
+
* under every preset, rather than being read out of `config.agents` — where they would
|
|
81
|
+
* appear only for a project that happened to have hand-bound a slot.
|
|
82
|
+
*
|
|
83
|
+
* Read out of the same command files:
|
|
84
|
+
* `plugins/dev/commands/dev.md:138` Plan review (external models via claudish) → dev:architect
|
|
85
|
+
* `plugins/dev/commands/dev.md:140` Code review (external models via claudish) → dev:architect
|
|
86
|
+
*
|
|
87
|
+
* `debug.md` and `investigate.md` contain no claudish reference at all, so both are empty
|
|
88
|
+
* here — and that absence is worth drawing, because "this flow stays inside Claude Code" is
|
|
89
|
+
* exactly as useful to know as the opposite.
|
|
90
|
+
*
|
|
91
|
+
* A STEP, not an agent: `dev:architect` appears in `agents` above as well, because the same
|
|
92
|
+
* agent does the Phase 3 planning on a Claude model. Marking the agent external would be
|
|
93
|
+
* wrong in both directions — it would move the planning outside and merge the two reviews
|
|
94
|
+
* into it.
|
|
95
|
+
*/
|
|
96
|
+
export interface WorkflowStep {
|
|
97
|
+
/** What the phase is called in the command file, for the reader who goes looking. */
|
|
98
|
+
step: string;
|
|
99
|
+
/** The agent the step dispatches. Recorded for provenance; routing is claudish's. */
|
|
100
|
+
agent: string;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface Workflow {
|
|
104
|
+
name: string;
|
|
105
|
+
agents: string[];
|
|
106
|
+
external: WorkflowStep[];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export const WORKFLOWS: Workflow[] = [
|
|
110
|
+
{
|
|
111
|
+
name: "dev",
|
|
112
|
+
agents: [
|
|
113
|
+
"dev:stack-detector",
|
|
114
|
+
"dev:architect",
|
|
115
|
+
"dev:developer",
|
|
116
|
+
"dev:qa-engineer",
|
|
117
|
+
"dev:reviewer",
|
|
118
|
+
],
|
|
119
|
+
external: [
|
|
120
|
+
{ step: "plan review", agent: "dev:architect" },
|
|
121
|
+
{ step: "code review", agent: "dev:architect" },
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
name: "debug",
|
|
126
|
+
agents: [
|
|
127
|
+
"dev:stack-detector",
|
|
128
|
+
"code-search:analyze",
|
|
129
|
+
"dev:debugger",
|
|
130
|
+
"dev:developer",
|
|
131
|
+
],
|
|
132
|
+
external: [],
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: "investigate",
|
|
136
|
+
agents: ["code-search:analyze", "dev:researcher"],
|
|
137
|
+
external: [],
|
|
138
|
+
},
|
|
139
|
+
];
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Agents whose model is a CAPABILITY choice, not a spend choice.
|
|
143
|
+
*
|
|
144
|
+
* The three tiers answer "how much model", and that is the wrong question for an agent that
|
|
145
|
+
* reads images. `designer:review` compares a rendered screen against a reference and
|
|
146
|
+
* `designer:ui` audits a screenshot for usability: what they need is the model that SEES
|
|
147
|
+
* best, which is not a point on a cheap-to-smart line and does not move when the preset
|
|
148
|
+
* changes.
|
|
149
|
+
*
|
|
150
|
+
* They are deliberately absent from the tier table above and from this map until the model
|
|
151
|
+
* is chosen from the live catalogue rather than from memory. Absent means they fall to the
|
|
152
|
+
* preset's `fallback`, which is a defensible default and an honest one; pinning them to a
|
|
153
|
+
* model id guessed here is neither.
|
|
154
|
+
*/
|
|
155
|
+
export const VISION_AGENTS = ["designer:review", "designer:ui"] as const;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* The effort the external slots run at, per preset.
|
|
159
|
+
*
|
|
160
|
+
* A preset cannot say WHICH model serves a mate — a catalogue id shipped in this repo goes
|
|
161
|
+
* stale, which is the whole reason bindings live in a project's own `mates` block. It can say
|
|
162
|
+
* how hard that work should try, and that half does not depend on which model answers.
|
|
163
|
+
*
|
|
164
|
+
* `xhigh` almost everywhere: a slot is reached for the judgement calls — plan review, code
|
|
165
|
+
* review — and those are the calls worth spending on whichever model takes them. `Sonnet` is
|
|
166
|
+
* the exception because it is the economy preset, and a preset whose whole point is to spend
|
|
167
|
+
* less would be lying if the work it sent outside ignored that.
|
|
168
|
+
*/
|
|
169
|
+
const MATE_EFFORT_DEFAULT: Effort = "xhigh";
|
|
170
|
+
|
|
171
|
+
function preset(
|
|
172
|
+
name: string,
|
|
173
|
+
main: ModelsConfig["main"],
|
|
174
|
+
grades: ModelsConfig["grades"],
|
|
175
|
+
mateEffort: Effort = MATE_EFFORT_DEFAULT,
|
|
176
|
+
): ModelsConfig {
|
|
177
|
+
return {
|
|
178
|
+
version: 1,
|
|
179
|
+
preset: name,
|
|
180
|
+
main,
|
|
181
|
+
grades,
|
|
182
|
+
agents: { ...DEFAULT_AGENT_GRADES },
|
|
183
|
+
fallback: "normal",
|
|
184
|
+
mateEffort,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* What each preset is CALLED on screen.
|
|
190
|
+
*
|
|
191
|
+
* The id (`fable-advisor`) is the machine name: it is what `models use` takes, what lands in
|
|
192
|
+
* the committed config, and what must never change. The label is what a person reads, and it
|
|
193
|
+
* says which model leads and which one helps — the thing you actually choose between. Kept
|
|
194
|
+
* beside the presets rather than in the UI so the CLI and the TUI cannot disagree.
|
|
195
|
+
*/
|
|
196
|
+
export const PRESET_LABELS: Record<string, string> = {
|
|
197
|
+
"fable-advisor": "Opus with Fable help",
|
|
198
|
+
"fable-lead": "Fable main",
|
|
199
|
+
"fable-top": "Fable top",
|
|
200
|
+
"opus-lead": "Opus main",
|
|
201
|
+
"sonnet-economy": "Sonnet",
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
/** The screen name for a preset id, falling back to the id for a hand-edited config. */
|
|
205
|
+
export function presetLabel(id: string): string {
|
|
206
|
+
return PRESET_LABELS[id] ?? id;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* The shipped presets.
|
|
211
|
+
*
|
|
212
|
+
* Effort is set per seat and reaches subagents through `modelSettings` — measured by
|
|
213
|
+
* `benches/agent-model-routing/` (AMR-1), which found alias keys honoured and a routed
|
|
214
|
+
* subagent running at its model's effort while the main thread kept its own.
|
|
215
|
+
*
|
|
216
|
+
* ## Effort belongs to the MODEL, so a preset gets one value per model
|
|
217
|
+
*
|
|
218
|
+
* `modelSettings` is keyed by model. Two seats naming the same model therefore cannot carry
|
|
219
|
+
* different efforts, and the validator rejects that rather than letting one silently win.
|
|
220
|
+
* That is the only cross-seat rule here: **within one preset, a model appears at exactly one
|
|
221
|
+
* effort.** Which effort is a per-preset choice — `fable-lead` runs fable at `medium` and
|
|
222
|
+
* `fable-top` runs it at `xhigh`, and that difference is the whole distinction between them.
|
|
223
|
+
*
|
|
224
|
+
* Presets never collide with each other, because only one is ever applied to a project.
|
|
225
|
+
*
|
|
226
|
+
* ## The levelling these presets apply
|
|
227
|
+
*
|
|
228
|
+
* Each one fixes every model it names at a single effort, which is what makes it writable:
|
|
229
|
+
*
|
|
230
|
+
* opus xhigh wherever it appears
|
|
231
|
+
* fable medium in `fable-lead`, xhigh in `fable-top`
|
|
232
|
+
* sonnet high in the `cheap` seat, xhigh when it holds the whole preset
|
|
233
|
+
* haiku not used
|
|
234
|
+
*
|
|
235
|
+
* A tier therefore differs from its neighbour by MODEL, not by a throttle on one model. That
|
|
236
|
+
* is not a style choice: a ramp like "fable xhigh on `smart`, fable low on `cheap`" is exactly
|
|
237
|
+
* what the `modelSettings` keying cannot express.
|
|
238
|
+
*/
|
|
239
|
+
export const BUILT_IN_PRESETS: ModelsConfig[] = [
|
|
240
|
+
// The default: opus runs everything except the judgement calls, which go to fable.
|
|
241
|
+
//
|
|
242
|
+
// Opus takes `main` and `normal` at `xhigh`, fable takes `smart` at its ordinary `medium`,
|
|
243
|
+
// and sonnet picks up the cheap seat at `high`. The id says what fable DOES here — it
|
|
244
|
+
// advises on the hard work rather than running the thread.
|
|
245
|
+
preset(
|
|
246
|
+
"fable-advisor",
|
|
247
|
+
{ model: "opus", effort: "xhigh" },
|
|
248
|
+
{
|
|
249
|
+
smart: { model: "fable", effort: "medium" },
|
|
250
|
+
normal: { model: "opus", effort: "xhigh" },
|
|
251
|
+
cheap: { model: "sonnet", effort: "high" },
|
|
252
|
+
},
|
|
253
|
+
),
|
|
254
|
+
|
|
255
|
+
// Fable runs everything it can, at its ordinary `medium`, with sonnet on the cheap seat.
|
|
256
|
+
//
|
|
257
|
+
// "Fable on the main orchestrator" — the request, literally, extended to the tiers it
|
|
258
|
+
// dispatches. Paired with `fable-top` below: the same model on `main` and `smart`, one
|
|
259
|
+
// effort apart.
|
|
260
|
+
preset(
|
|
261
|
+
"fable-lead",
|
|
262
|
+
{ model: "fable", effort: "medium" },
|
|
263
|
+
{
|
|
264
|
+
smart: { model: "fable", effort: "medium" },
|
|
265
|
+
normal: { model: "fable", effort: "medium" },
|
|
266
|
+
cheap: { model: "sonnet", effort: "high" },
|
|
267
|
+
},
|
|
268
|
+
),
|
|
269
|
+
|
|
270
|
+
// Fable at full stretch on the thread and the judgement calls; opus does the building.
|
|
271
|
+
//
|
|
272
|
+
// `fable-lead` runs fable at `medium` in three seats; this runs it at `xhigh` in two and
|
|
273
|
+
// hands `normal` — the highest-volume tier, since it is also the `fallback` — to opus.
|
|
274
|
+
// The difference between the two presets is fable's effort, which is why it has to BE two
|
|
275
|
+
// presets rather than one with a ramp.
|
|
276
|
+
preset(
|
|
277
|
+
"fable-top",
|
|
278
|
+
{ model: "fable", effort: "xhigh" },
|
|
279
|
+
{
|
|
280
|
+
smart: { model: "fable", effort: "xhigh" },
|
|
281
|
+
normal: { model: "opus", effort: "xhigh" },
|
|
282
|
+
cheap: { model: "sonnet", effort: "high" },
|
|
283
|
+
},
|
|
284
|
+
),
|
|
285
|
+
|
|
286
|
+
// Opus everywhere it is reached at all, at `xhigh`; sonnet only on the cheap seat.
|
|
287
|
+
//
|
|
288
|
+
// `main`, `smart` and `normal` are one spend decision wearing three names. That is legal
|
|
289
|
+
// precisely because they agree on effort, and it is the point of the preset rather than a
|
|
290
|
+
// compromise: the only thing NOT on opus is the searching and transcription.
|
|
291
|
+
preset(
|
|
292
|
+
"opus-lead",
|
|
293
|
+
{ model: "opus", effort: "xhigh" },
|
|
294
|
+
{
|
|
295
|
+
smart: { model: "opus", effort: "xhigh" },
|
|
296
|
+
normal: { model: "opus", effort: "xhigh" },
|
|
297
|
+
cheap: { model: "sonnet", effort: "high" },
|
|
298
|
+
},
|
|
299
|
+
),
|
|
300
|
+
|
|
301
|
+
// Sonnet, at `xhigh`, in all four seats.
|
|
302
|
+
//
|
|
303
|
+
// This preset deliberately has no tier differentiation: its saving is the model, and the
|
|
304
|
+
// effort is not throttled on top of it. Every routed agent runs sonnet at `xhigh`,
|
|
305
|
+
// including `cheap` — so the hook still pins the model (a session started on opus does
|
|
306
|
+
// not leak into its subagents) while the tier table itself makes no distinction.
|
|
307
|
+
// The one preset whose mates run at `medium` rather than `xhigh`: an economy preset that
|
|
308
|
+
// spent freely the moment work left Claude Code would not be one.
|
|
309
|
+
preset(
|
|
310
|
+
"sonnet-economy",
|
|
311
|
+
{ model: "sonnet", effort: "xhigh" },
|
|
312
|
+
{
|
|
313
|
+
smart: { model: "sonnet", effort: "xhigh" },
|
|
314
|
+
normal: { model: "sonnet", effort: "xhigh" },
|
|
315
|
+
cheap: { model: "sonnet", effort: "xhigh" },
|
|
316
|
+
},
|
|
317
|
+
"medium",
|
|
318
|
+
),
|
|
319
|
+
];
|
|
320
|
+
|
|
321
|
+
export const DEFAULT_PRESET = "fable-advisor";
|
|
322
|
+
|
|
323
|
+
export function findPreset(name: string): ModelsConfig | undefined {
|
|
324
|
+
return BUILT_IN_PRESETS.find((p) => p.preset === name);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export function presetNames(): string[] {
|
|
328
|
+
return BUILT_IN_PRESETS.map((p) => p.preset);
|
|
329
|
+
}
|