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,503 @@
|
|
|
1
|
+
import type { CatalogModel } from "../../services/mate-catalog.js";
|
|
2
|
+
import type { ModelsConfig, ModelsStatus } from "../../services/models-core.js";
|
|
3
|
+
import type { PluginInfo } from "../../services/plugin-manager.js";
|
|
4
|
+
import type {
|
|
5
|
+
PluginComponent,
|
|
6
|
+
PluginSkillDetail,
|
|
7
|
+
} from "../../services/skills-manager.js";
|
|
8
|
+
import type { StylesSnapshot } from "../../services/styles-manager.js";
|
|
9
|
+
import type {
|
|
10
|
+
Marketplace,
|
|
11
|
+
McpServer,
|
|
12
|
+
ProfileEntry,
|
|
13
|
+
SkillInfo,
|
|
14
|
+
} from "../../types/index.js";
|
|
15
|
+
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Route Types
|
|
18
|
+
// ============================================================================
|
|
19
|
+
|
|
20
|
+
export type Screen =
|
|
21
|
+
| "plugins"
|
|
22
|
+
| "mcp"
|
|
23
|
+
| "mcp-registry"
|
|
24
|
+
| "settings"
|
|
25
|
+
| "cli-tools"
|
|
26
|
+
| "profiles"
|
|
27
|
+
| "skills"
|
|
28
|
+
| "styles"
|
|
29
|
+
| "gitignore"
|
|
30
|
+
| "alias"
|
|
31
|
+
| "models";
|
|
32
|
+
|
|
33
|
+
export type Route =
|
|
34
|
+
| { screen: "plugins" }
|
|
35
|
+
| { screen: "mcp" }
|
|
36
|
+
| { screen: "mcp-registry"; query?: string }
|
|
37
|
+
| { screen: "settings" }
|
|
38
|
+
| { screen: "cli-tools" }
|
|
39
|
+
| { screen: "profiles" }
|
|
40
|
+
| { screen: "skills" }
|
|
41
|
+
| { screen: "styles" }
|
|
42
|
+
| { screen: "gitignore" }
|
|
43
|
+
| { screen: "alias" }
|
|
44
|
+
| { screen: "models" };
|
|
45
|
+
|
|
46
|
+
// ============================================================================
|
|
47
|
+
// Async Data Types
|
|
48
|
+
// ============================================================================
|
|
49
|
+
|
|
50
|
+
export type AsyncData<T> =
|
|
51
|
+
| { status: "idle" }
|
|
52
|
+
/**
|
|
53
|
+
* `previous` carries the last successful value through a reload.
|
|
54
|
+
*
|
|
55
|
+
* Without it a refetch is indistinguishable from a first load, so a screen
|
|
56
|
+
* that already had data blanked itself to "Loading..." every time it
|
|
57
|
+
* remounted — and the router unmounts screens on every tab switch. Optional so
|
|
58
|
+
* that a genuine first load is still just `{ status: "loading" }`.
|
|
59
|
+
*/
|
|
60
|
+
| { status: "loading"; previous?: T }
|
|
61
|
+
| { status: "success"; data: T }
|
|
62
|
+
| { status: "error"; error: Error };
|
|
63
|
+
|
|
64
|
+
/** The value to render: fresh if loaded, else the last good one during a reload. */
|
|
65
|
+
export function asyncValue<T>(d: AsyncData<T>): T | undefined {
|
|
66
|
+
if (d.status === "success") return d.data;
|
|
67
|
+
if (d.status === "loading") return d.previous;
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ============================================================================
|
|
72
|
+
// Modal Types
|
|
73
|
+
// ============================================================================
|
|
74
|
+
|
|
75
|
+
export interface SelectOption {
|
|
76
|
+
label: string;
|
|
77
|
+
value: string;
|
|
78
|
+
description?: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export type ModalState =
|
|
82
|
+
| {
|
|
83
|
+
type: "confirm";
|
|
84
|
+
title: string;
|
|
85
|
+
message: string;
|
|
86
|
+
onConfirm: () => void;
|
|
87
|
+
onCancel: () => void;
|
|
88
|
+
}
|
|
89
|
+
| {
|
|
90
|
+
type: "input";
|
|
91
|
+
title: string;
|
|
92
|
+
label: string;
|
|
93
|
+
defaultValue?: string;
|
|
94
|
+
onSubmit: (value: string) => void;
|
|
95
|
+
onCancel: () => void;
|
|
96
|
+
}
|
|
97
|
+
| {
|
|
98
|
+
type: "select";
|
|
99
|
+
title: string;
|
|
100
|
+
message: string;
|
|
101
|
+
options: SelectOption[];
|
|
102
|
+
defaultIndex?: number;
|
|
103
|
+
onSelect: (value: string) => void;
|
|
104
|
+
onCancel: () => void;
|
|
105
|
+
}
|
|
106
|
+
| {
|
|
107
|
+
type: "message";
|
|
108
|
+
title: string;
|
|
109
|
+
message: string;
|
|
110
|
+
variant: "info" | "success" | "error";
|
|
111
|
+
onDismiss: () => void;
|
|
112
|
+
}
|
|
113
|
+
| {
|
|
114
|
+
type: "loading";
|
|
115
|
+
message: string;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// ============================================================================
|
|
119
|
+
// Progress Types
|
|
120
|
+
// ============================================================================
|
|
121
|
+
|
|
122
|
+
export interface ProgressState {
|
|
123
|
+
message: string;
|
|
124
|
+
current?: number;
|
|
125
|
+
total?: number;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ============================================================================
|
|
129
|
+
// Screen State Types
|
|
130
|
+
// ============================================================================
|
|
131
|
+
|
|
132
|
+
/** Read-only listing of what one plugin ships, fetched on demand. */
|
|
133
|
+
export interface PluginContentsEntry {
|
|
134
|
+
status: "loading" | "success" | "error";
|
|
135
|
+
components?: PluginComponent[];
|
|
136
|
+
error?: string;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** One skill's SKILL.md, fetched when the cursor settles on its row. */
|
|
140
|
+
export interface PluginSkillDetailEntry {
|
|
141
|
+
status: "loading" | "success" | "error";
|
|
142
|
+
detail?: PluginSkillDetail;
|
|
143
|
+
error?: string;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export interface PluginsScreenState {
|
|
147
|
+
scope: "project" | "global";
|
|
148
|
+
collapsedMarketplaces: Set<string>;
|
|
149
|
+
/**
|
|
150
|
+
* Plugin ids whose contents list is open. Opposite default to
|
|
151
|
+
* `collapsedMarketplaces`: listing a plugin's contents costs an HTTP call,
|
|
152
|
+
* so it stays shut until asked for.
|
|
153
|
+
*/
|
|
154
|
+
expandedPlugins: Set<string>;
|
|
155
|
+
pluginContents: Map<string, PluginContentsEntry>;
|
|
156
|
+
/** SKILL.md content, keyed `<repo>/<repoPath>`. */
|
|
157
|
+
skillDetails: Map<string, PluginSkillDetailEntry>;
|
|
158
|
+
selectedIndex: number;
|
|
159
|
+
searchQuery: string;
|
|
160
|
+
marketplaces: AsyncData<Marketplace[]>;
|
|
161
|
+
plugins: AsyncData<PluginInfo[]>;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export interface McpScreenState {
|
|
165
|
+
selectedIndex: number;
|
|
166
|
+
servers: AsyncData<McpServer[]>;
|
|
167
|
+
installedServers: Record<string, boolean>;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface McpRegistryScreenState {
|
|
171
|
+
searchQuery: string;
|
|
172
|
+
selectedIndex: number;
|
|
173
|
+
servers: AsyncData<McpServer[]>;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export interface ScopedSettingValues {
|
|
177
|
+
user: string | undefined;
|
|
178
|
+
project: string | undefined;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export interface SettingsScreenState {
|
|
182
|
+
selectedIndex: number;
|
|
183
|
+
values: AsyncData<Map<string, ScopedSettingValues>>;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export interface CliTool {
|
|
187
|
+
name: string;
|
|
188
|
+
displayName: string;
|
|
189
|
+
description: string;
|
|
190
|
+
installCommand: string;
|
|
191
|
+
checkCommand: string;
|
|
192
|
+
homebrewFormula?: string;
|
|
193
|
+
npmPackage?: string;
|
|
194
|
+
installedVersion?: string;
|
|
195
|
+
latestVersion?: string;
|
|
196
|
+
hasUpdate?: boolean;
|
|
197
|
+
isInstalled: boolean;
|
|
198
|
+
isChecking: boolean;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export interface CliToolsScreenState {
|
|
202
|
+
selectedIndex: number;
|
|
203
|
+
tools: AsyncData<CliTool[]>;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export interface ProfilesScreenState {
|
|
207
|
+
selectedIndex: number;
|
|
208
|
+
profiles: AsyncData<ProfileEntry[]>;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export interface SkillsScreenState {
|
|
212
|
+
scope: "user" | "project";
|
|
213
|
+
selectedIndex: number;
|
|
214
|
+
searchQuery: string;
|
|
215
|
+
/** Available skills from Tree API, cross-referenced with lock files */
|
|
216
|
+
skills: AsyncData<SkillInfo[]>;
|
|
217
|
+
/** Null until check completes; then Map<skillName, hasUpdate> */
|
|
218
|
+
updateStatus: Map<string, boolean> | null;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export interface StylesScreenState {
|
|
222
|
+
selectedIndex: number;
|
|
223
|
+
searchQuery: string;
|
|
224
|
+
/** Presets + importable styles + what is currently live, in one read. */
|
|
225
|
+
snapshot: AsyncData<StylesSnapshot>;
|
|
226
|
+
/**
|
|
227
|
+
* Pending selection, by source id. Staged rather than applied on each
|
|
228
|
+
* keystroke: composing writes a file and flips the harness's active output
|
|
229
|
+
* style, which is not something a stray Space press should do.
|
|
230
|
+
*/
|
|
231
|
+
selected: Set<string>;
|
|
232
|
+
/**
|
|
233
|
+
* The status line's current message, or null for the default line.
|
|
234
|
+
*
|
|
235
|
+
* App state for the same reason `isFilling` is: `Router` swaps the component
|
|
236
|
+
* type on a tab change, so anything held in the screen's own `useState` is
|
|
237
|
+
* destroyed by `9 → 1 → 9`. It also stops being cleared on a wall clock —
|
|
238
|
+
* the message now survives until the next action replaces it, so the last
|
|
239
|
+
* thing you did is still on screen whenever you look.
|
|
240
|
+
*/
|
|
241
|
+
status: { text: string; tone: "success" | "error" } | null;
|
|
242
|
+
/**
|
|
243
|
+
* True while a template fill (Claude Code over the whole project) is running.
|
|
244
|
+
*
|
|
245
|
+
* App state, not screen state, because the screen UNMOUNTS when you switch
|
|
246
|
+
* tabs. A fill runs for minutes, so switching away is the normal thing to do
|
|
247
|
+
* — and with the flag held locally the returning user saw no sign one was in
|
|
248
|
+
* flight and could launch a second subprocess on top of the first.
|
|
249
|
+
*/
|
|
250
|
+
isFilling: boolean;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* What the Models screen reads in one go.
|
|
255
|
+
*
|
|
256
|
+
* Both halves are needed and neither implies the other: `status` says whether
|
|
257
|
+
* the routing is in force, and `config` is the routing itself — which the
|
|
258
|
+
* screen needs in full to give a hand-edited config a row of its own.
|
|
259
|
+
*/
|
|
260
|
+
export interface ModelsSnapshot {
|
|
261
|
+
status: ModelsStatus;
|
|
262
|
+
/** The validated config, or null when there is none (or it failed validation). */
|
|
263
|
+
config: ModelsConfig | null;
|
|
264
|
+
/** Where `.claude/models.json` is, whether or not it exists. */
|
|
265
|
+
path: string;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export interface ModelsScreenState {
|
|
269
|
+
selectedIndex: number;
|
|
270
|
+
searchQuery: string;
|
|
271
|
+
/** Routing status + the config behind it, in one read. */
|
|
272
|
+
data: AsyncData<ModelsSnapshot>;
|
|
273
|
+
/**
|
|
274
|
+
* The status line's current message, or null for the default line.
|
|
275
|
+
*
|
|
276
|
+
* App state for the same reason the Styles screen's is: `Router` swaps the
|
|
277
|
+
* component type on a tab change, so anything held in the screen's own
|
|
278
|
+
* `useState` is destroyed by `0 → 1 → 0`. The message survives until the next
|
|
279
|
+
* action replaces it, so the last thing you did is still on screen.
|
|
280
|
+
*/
|
|
281
|
+
status: { text: string; tone: "success" | "error" } | null;
|
|
282
|
+
/**
|
|
283
|
+
* True while an apply or a clear is writing.
|
|
284
|
+
*
|
|
285
|
+
* App state, not screen state, because the screen UNMOUNTS on a tab switch.
|
|
286
|
+
* An apply rewrites the profile, re-materializes it and registers a hook —
|
|
287
|
+
* two of those running at once would interleave writes to the same files.
|
|
288
|
+
*/
|
|
289
|
+
isApplying: boolean;
|
|
290
|
+
/**
|
|
291
|
+
* Is `multimodel@magus` installed — i.e. is there anything to serve a mate?
|
|
292
|
+
*
|
|
293
|
+
* App state for the reason the two fields above are, plus one of its own. The screen
|
|
294
|
+
* unmounts on a tab switch, so a local flag would be destroyed by `0 → 1 → 0`; and unlike
|
|
295
|
+
* the two file reads the screen does on mount, THIS answer costs a marketplace resolution
|
|
296
|
+
* with network timeouts behind it. Re-asking it on every visit to the tab would pay that
|
|
297
|
+
* cost again for an answer that cannot change while the TUI is open.
|
|
298
|
+
*
|
|
299
|
+
* Starts false, which is also what a failed lookup yields (`areMatesAvailable` fails
|
|
300
|
+
* closed): both render as the screen exactly as it was before mates existed, rather than
|
|
301
|
+
* as three rows put there by an error.
|
|
302
|
+
*/
|
|
303
|
+
matesAvailable: boolean;
|
|
304
|
+
/**
|
|
305
|
+
* claudish's live model catalogue, or [] when it could not be read.
|
|
306
|
+
*
|
|
307
|
+
* App state for the reasons above, plus the one that matters most here: reading it spawns
|
|
308
|
+
* a process. Held per session, it is spawned once; held in the component it would be
|
|
309
|
+
* spawned on every visit to the tab.
|
|
310
|
+
*
|
|
311
|
+
* [] is deliberately NOT distinguished from "not yet loaded". Every consumer treats an
|
|
312
|
+
* empty catalogue as "unknown" and says nothing, so the loading frame, a machine without
|
|
313
|
+
* claudish, and a lookup that timed out all draw the same screen — bound ids as written,
|
|
314
|
+
* with no annotation. Advisory only: nothing here ever changes what is routed.
|
|
315
|
+
*/
|
|
316
|
+
mateCatalog: CatalogModel[];
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// ============================================================================
|
|
320
|
+
// App State
|
|
321
|
+
// ============================================================================
|
|
322
|
+
|
|
323
|
+
export interface AppState {
|
|
324
|
+
// Navigation
|
|
325
|
+
currentRoute: Route;
|
|
326
|
+
|
|
327
|
+
// Project context
|
|
328
|
+
projectPath: string;
|
|
329
|
+
|
|
330
|
+
// Global UI state
|
|
331
|
+
modal: ModalState | null;
|
|
332
|
+
progress: ProgressState | null;
|
|
333
|
+
isSearching: boolean;
|
|
334
|
+
// True while a screen has a focused sub-view open that owns Escape (e.g.
|
|
335
|
+
// the Alias tab's flag-detail editor). The global key handler skips its
|
|
336
|
+
// Escape→navigate branch so Escape backs out of the sub-view instead of
|
|
337
|
+
// jumping to the home tab. Mirrors how `modal`/`isSearching` gate keys.
|
|
338
|
+
detailActive: boolean;
|
|
339
|
+
|
|
340
|
+
// Refresh counter - incremented when marketplace sync completes
|
|
341
|
+
// Screens can watch this to know when to refetch data
|
|
342
|
+
dataRefreshVersion: number;
|
|
343
|
+
|
|
344
|
+
// Screen-specific state
|
|
345
|
+
plugins: PluginsScreenState;
|
|
346
|
+
mcp: McpScreenState;
|
|
347
|
+
mcpRegistry: McpRegistryScreenState;
|
|
348
|
+
settings: SettingsScreenState;
|
|
349
|
+
cliTools: CliToolsScreenState;
|
|
350
|
+
profiles: ProfilesScreenState;
|
|
351
|
+
skills: SkillsScreenState;
|
|
352
|
+
styles: StylesScreenState;
|
|
353
|
+
models: ModelsScreenState;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// ============================================================================
|
|
357
|
+
// Action Types
|
|
358
|
+
// ============================================================================
|
|
359
|
+
|
|
360
|
+
export type AppAction =
|
|
361
|
+
// Navigation
|
|
362
|
+
| { type: "NAVIGATE"; route: Route }
|
|
363
|
+
|
|
364
|
+
// Plugins screen
|
|
365
|
+
| { type: "PLUGINS_SET_SCOPE"; scope: "project" | "global" }
|
|
366
|
+
| { type: "PLUGINS_TOGGLE_SCOPE" }
|
|
367
|
+
| { type: "PLUGINS_TOGGLE_MARKETPLACE"; name: string }
|
|
368
|
+
| { type: "PLUGINS_TOGGLE_PLUGIN_CONTENTS"; pluginId: string }
|
|
369
|
+
| { type: "PLUGINS_CONTENTS_LOADING"; pluginId: string }
|
|
370
|
+
| {
|
|
371
|
+
type: "PLUGINS_CONTENTS_SUCCESS";
|
|
372
|
+
pluginId: string;
|
|
373
|
+
components: PluginComponent[];
|
|
374
|
+
}
|
|
375
|
+
| { type: "PLUGINS_CONTENTS_ERROR"; pluginId: string; error: string }
|
|
376
|
+
| { type: "PLUGINS_SKILL_DETAIL_LOADING"; key: string }
|
|
377
|
+
| {
|
|
378
|
+
type: "PLUGINS_SKILL_DETAIL_SUCCESS";
|
|
379
|
+
key: string;
|
|
380
|
+
detail: PluginSkillDetail;
|
|
381
|
+
}
|
|
382
|
+
| { type: "PLUGINS_SKILL_DETAIL_ERROR"; key: string; error: string }
|
|
383
|
+
| { type: "PLUGINS_SELECT"; index: number }
|
|
384
|
+
| { type: "PLUGINS_SET_SEARCH"; query: string }
|
|
385
|
+
| { type: "PLUGINS_DATA_LOADING" }
|
|
386
|
+
| {
|
|
387
|
+
type: "PLUGINS_DATA_SUCCESS";
|
|
388
|
+
marketplaces: Marketplace[];
|
|
389
|
+
plugins: PluginInfo[];
|
|
390
|
+
}
|
|
391
|
+
| { type: "PLUGINS_DATA_ERROR"; error: Error }
|
|
392
|
+
|
|
393
|
+
// MCP screen
|
|
394
|
+
| { type: "MCP_SELECT"; index: number }
|
|
395
|
+
| { type: "MCP_DATA_LOADING" }
|
|
396
|
+
| {
|
|
397
|
+
type: "MCP_DATA_SUCCESS";
|
|
398
|
+
servers: McpServer[];
|
|
399
|
+
installedServers: Record<string, boolean>;
|
|
400
|
+
}
|
|
401
|
+
| { type: "MCP_DATA_ERROR"; error: Error }
|
|
402
|
+
|
|
403
|
+
// MCP Registry screen
|
|
404
|
+
| { type: "MCP_REGISTRY_SET_QUERY"; query: string }
|
|
405
|
+
| { type: "MCPREGISTRY_SEARCH"; query: string } // Alias for MCP_REGISTRY_SET_QUERY
|
|
406
|
+
| { type: "MCPREGISTRY_SELECT"; index: number } // Alias for MCP_REGISTRY_SELECT
|
|
407
|
+
| { type: "MCP_REGISTRY_SELECT"; index: number }
|
|
408
|
+
| { type: "MCP_REGISTRY_DATA_LOADING" }
|
|
409
|
+
| { type: "MCP_REGISTRY_DATA_SUCCESS"; servers: McpServer[] }
|
|
410
|
+
| { type: "MCP_REGISTRY_DATA_ERROR"; error: Error }
|
|
411
|
+
|
|
412
|
+
// Settings screen
|
|
413
|
+
| { type: "SETTINGS_SELECT"; index: number }
|
|
414
|
+
| { type: "SETTINGS_DATA_LOADING" }
|
|
415
|
+
| { type: "SETTINGS_DATA_SUCCESS"; values: Map<string, ScopedSettingValues> }
|
|
416
|
+
| { type: "SETTINGS_DATA_ERROR"; error: Error }
|
|
417
|
+
|
|
418
|
+
// CLI tools screen
|
|
419
|
+
| { type: "CLITOOLS_SELECT"; index: number }
|
|
420
|
+
| { type: "CLITOOLS_DATA_LOADING" }
|
|
421
|
+
| { type: "CLITOOLS_DATA_SUCCESS"; tools: CliTool[] }
|
|
422
|
+
| { type: "CLITOOLS_DATA_ERROR"; error: Error }
|
|
423
|
+
| { type: "CLITOOLS_UPDATE_TOOL"; name: string; updates: Partial<CliTool> }
|
|
424
|
+
|
|
425
|
+
// Modals
|
|
426
|
+
| { type: "SHOW_MODAL"; modal: ModalState }
|
|
427
|
+
| { type: "HIDE_MODAL" }
|
|
428
|
+
|
|
429
|
+
// Progress
|
|
430
|
+
| { type: "SHOW_PROGRESS"; state: ProgressState }
|
|
431
|
+
| { type: "UPDATE_PROGRESS"; state: ProgressState }
|
|
432
|
+
| { type: "HIDE_PROGRESS" }
|
|
433
|
+
|
|
434
|
+
// Search state (blocks global key handlers)
|
|
435
|
+
| { type: "SET_SEARCHING"; isSearching: boolean }
|
|
436
|
+
|
|
437
|
+
// Detail sub-view state (gates global Escape→navigate)
|
|
438
|
+
| { type: "SET_DETAIL_ACTIVE"; detailActive: boolean }
|
|
439
|
+
|
|
440
|
+
// Profiles screen
|
|
441
|
+
| { type: "PROFILES_SELECT"; index: number }
|
|
442
|
+
| { type: "PROFILES_DATA_LOADING" }
|
|
443
|
+
| { type: "PROFILES_DATA_SUCCESS"; profiles: ProfileEntry[] }
|
|
444
|
+
| { type: "PROFILES_DATA_ERROR"; error: Error }
|
|
445
|
+
|
|
446
|
+
// Skills screen
|
|
447
|
+
| { type: "SKILLS_SET_SCOPE"; scope: "user" | "project" }
|
|
448
|
+
| { type: "SKILLS_TOGGLE_SCOPE" }
|
|
449
|
+
| { type: "SKILLS_SELECT"; index: number }
|
|
450
|
+
| { type: "SKILLS_SET_SEARCH"; query: string }
|
|
451
|
+
| { type: "SKILLS_DATA_LOADING" }
|
|
452
|
+
| { type: "SKILLS_DATA_SUCCESS"; skills: SkillInfo[] }
|
|
453
|
+
| { type: "SKILLS_DATA_ERROR"; error: Error }
|
|
454
|
+
| { type: "SKILLS_UPDATE_STATUS"; updates: Map<string, boolean> }
|
|
455
|
+
| { type: "SKILLS_UPDATE_ITEM"; name: string; updates: Partial<SkillInfo> }
|
|
456
|
+
|
|
457
|
+
// Styles screen
|
|
458
|
+
| { type: "STYLES_SELECT"; index: number }
|
|
459
|
+
| { type: "STYLES_SET_SEARCH"; query: string }
|
|
460
|
+
// Append/backspace are computed IN the reducer, from current state. A
|
|
461
|
+
// screen-side `searchQuery + char` reads a value captured at render time, so
|
|
462
|
+
// keystrokes arriving faster than React re-renders all overwrite each other
|
|
463
|
+
// and typing "slop" leaves just "p".
|
|
464
|
+
| { type: "STYLES_SEARCH_APPEND"; char: string }
|
|
465
|
+
| { type: "STYLES_SEARCH_BACKSPACE" }
|
|
466
|
+
| { type: "STYLES_DATA_LOADING" }
|
|
467
|
+
| { type: "STYLES_DATA_SUCCESS"; snapshot: StylesSnapshot }
|
|
468
|
+
| { type: "STYLES_DATA_ERROR"; error: Error }
|
|
469
|
+
| { type: "STYLES_TOGGLE"; id: string }
|
|
470
|
+
| {
|
|
471
|
+
type: "STYLES_STATUS_SET";
|
|
472
|
+
status: { text: string; tone: "success" | "error" };
|
|
473
|
+
}
|
|
474
|
+
| { type: "STYLES_STATUS_CLEAR" }
|
|
475
|
+
| { type: "STYLES_FILL_START" }
|
|
476
|
+
| { type: "STYLES_FILL_END" }
|
|
477
|
+
| { type: "STYLES_SET_SELECTION"; ids: string[] }
|
|
478
|
+
|
|
479
|
+
// Models screen
|
|
480
|
+
| { type: "MODELS_SELECT"; index: number }
|
|
481
|
+
| { type: "MODELS_SET_SEARCH"; query: string }
|
|
482
|
+
// Computed IN the reducer, from current state, for the same reason the
|
|
483
|
+
// STYLES_SEARCH_* pair is: a screen-side `searchQuery + char` reads a value
|
|
484
|
+
// captured at render time, so keystrokes arriving faster than React
|
|
485
|
+
// re-renders all overwrite each other.
|
|
486
|
+
| { type: "MODELS_SEARCH_APPEND"; char: string }
|
|
487
|
+
| { type: "MODELS_SEARCH_BACKSPACE" }
|
|
488
|
+
| { type: "MODELS_DATA_LOADING" }
|
|
489
|
+
| { type: "MODELS_DATA_SUCCESS"; snapshot: ModelsSnapshot }
|
|
490
|
+
| { type: "MODELS_DATA_ERROR"; error: Error }
|
|
491
|
+
| {
|
|
492
|
+
type: "MODELS_STATUS_SET";
|
|
493
|
+
status: { text: string; tone: "success" | "error" };
|
|
494
|
+
}
|
|
495
|
+
| { type: "MODELS_STATUS_CLEAR" }
|
|
496
|
+
| { type: "MODELS_APPLY_START" }
|
|
497
|
+
| { type: "MODELS_APPLY_END" }
|
|
498
|
+
// Dispatched once, when the plugin lookup behind the mate slots lands.
|
|
499
|
+
| { type: "MODELS_MATES_AVAILABLE"; available: boolean }
|
|
500
|
+
| { type: "MODELS_CATALOG_LOADED"; catalog: CatalogModel[] }
|
|
501
|
+
|
|
502
|
+
// Data refresh - triggers screens to refetch
|
|
503
|
+
| { type: "DATA_REFRESH_COMPLETE" };
|