diff --git a/apps/desktop/scripts/run-tests.mjs b/apps/desktop/scripts/run-tests.mjs index 4ace8807..53d77133 100644 --- a/apps/desktop/scripts/run-tests.mjs +++ b/apps/desktop/scripts/run-tests.mjs @@ -62,6 +62,7 @@ const behaviorTests = [ ".test-dist/tests/knowledge-store.test.js", ".test-dist/tests/control-center-service-barrels.test.js", ".test-dist/tests/mcp-toolkit-catalog.test.js", + ".test-dist/tests/integrations-view-toolkit-split.test.js", ".test-dist/tests/integrations-view-state.test.js", ".test-dist/tests/familiars-view-state.test.js", ".test-dist/tests/agent-setup-actions.test.js", diff --git a/apps/desktop/src/check-packaging-contract.ts b/apps/desktop/src/check-packaging-contract.ts index bfe13dd6..000e94e3 100644 --- a/apps/desktop/src/check-packaging-contract.ts +++ b/apps/desktop/src/check-packaging-contract.ts @@ -119,6 +119,7 @@ const controlCenterIntegrationsSource = readFileSync(join(appDir, "src", "render const controlCenterIntegrationsStateSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "integrations-view-state.ts"), "utf8"); const controlCenterIntegrationsSharedSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "integrations-view-shared.tsx"), "utf8"); const controlCenterIntegrationsToolingSectionsSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "integrations-view-tooling-sections.tsx"), "utf8"); +const controlCenterIntegrationsMcpToolkitSectionSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "integrations-view-mcp-toolkit-section.tsx"), "utf8"); const openApiChatSource = readFileSync(join(appDir, "src", "openapi-chat.ts"), "utf8"); const openApiChatSettingsSource = readFileSync(join(appDir, "src", "openapi-chat-settings.ts"), "utf8"); const openApiChatPresentationSource = readFileSync(join(appDir, "src", "openapi-chat-presentation.ts"), "utf8"); @@ -204,6 +205,7 @@ const controlCenterIntegrationsCombinedSource = [ controlCenterIntegrationsSource, controlCenterIntegrationsSharedSource, controlCenterIntegrationsToolingSectionsSource, + controlCenterIntegrationsMcpToolkitSectionSource, ].join("\n"); const controlCenterSharedCombinedSource = [ controlCenterSharedSource, @@ -490,7 +492,10 @@ assert.match(controlCenterIntegrationsSource, /from "\.\/integrations-view-tooli assert.match(controlCenterIntegrationsStateSource, /export function useIntegrationsViewState/, "Control Center integrations state seam must export the route hook."); assert.match(controlCenterIntegrationsStateSource, /from "\.\/route-data-loaders"/, "Control Center integrations state seam must compose shared route data loaders."); assert.match(controlCenterIntegrationsStateSource, /from "\.\/integrations-view-shared"/, "Control Center integrations state seam must compose the extracted integrations shared helpers."); +assert.match(controlCenterIntegrationsToolingSectionsSource, /from "\.\/integrations-view-mcp-toolkit-section"/, "Control Center integrations tooling shell must import the extracted MCP toolkit section seam."); assert.match(controlCenterIntegrationsToolingSectionsSource, /export function McpToolkitSection/, "Control Center integrations tooling seam must own the curated toolkit panel."); +assert.match(controlCenterIntegrationsMcpToolkitSectionSource, /export function McpToolkitSection/, "Control Center integrations toolkit seam must export the curated toolkit panel."); +assert.match(controlCenterIntegrationsMcpToolkitSectionSource, /buildPersistentToolkitBundle/, "Control Center integrations toolkit seam must own persistent bundle composition."); assert.match(controlCenterIntegrationsCombinedSource, /Persistent Full Access/, "Control Center integrations seam must retain the persistent toolkit install path."); assert.match(controlCenterIntegrationsCombinedSource, /pi install npm:@familiaros\/pi/, "Control Center integrations seam must retain Pi install guidance."); assert.match(openApiChatSource, /from "\.\/openapi-chat-settings(?:\.js)?"/, "OpenAPI chat must import the extracted settings seam."); diff --git a/apps/desktop/src/renderer/src/codemap.md b/apps/desktop/src/renderer/src/codemap.md index 8a3da83b..c4c464df 100644 --- a/apps/desktop/src/renderer/src/codemap.md +++ b/apps/desktop/src/renderer/src/codemap.md @@ -10,7 +10,7 @@ React/Tailwind source for the Control Center management UI. This renderer presen - **Route Modules**: Each major Control Center route now lives in `control-center/*.tsx`, so dashboard, familiars, integrations, plugins, and settings can evolve independently without re-growing a single renderer god file. - **Dashboard**: Reads a narrowed dashboard snapshot for default familiar preview, install/catalog counts, plugin health, update status, and activity totals. - **Familiars**: `familiars-view-state.ts` now owns installed/catalog/Codex data loading, selection state, and familiar actions while `familiars-view-helpers.ts` holds the denser familiar list, remote-page planning, focus/status helpers, and `familiars-view-effects.ts` owns the detail focus trap plus catalog/logging hydration effects so the route shell stays focused on filters, cards, detail rendering, and animated sprite previews. -- **Integrations**: Card-first setup UI for Claude Code, OpenCode, Cursor, and Pi guidance, with `integrations-view-state.ts` owning route state/effects/actions, extracted integrations shared helpers plus tooling detail sections, and `mcp-toolkit-catalog.ts` staying as a thin lookup surface over a small data barrel plus tier-split starter/system/advanced catalog seams and the shared type contract in `mcp-toolkit-catalog-types.ts`. +- **Integrations**: Card-first setup UI for Claude Code, OpenCode, Cursor, and Pi guidance, with `integrations-view-state.ts` owning route state/effects/actions, extracted integrations shared helpers plus tooling detail sections, the curated toolkit panel now split into its own route-local seam, and `mcp-toolkit-catalog.ts` staying as a thin lookup surface over a small data barrel plus tier-split starter/system/advanced catalog seams and the shared type contract in `mcp-toolkit-catalog-types.ts`. - **Plugins**: Gallery-first plugin hub for installed/catalog/local/broken filters, catalog refresh, local load, install/update/uninstall, enable/disable, config modal, command execution, runtime/status display, and broken-state feedback. - **Plugin Config Editor Seam**: `plugins-view-config-helpers.ts` now owns config draft materialization while `plugins-view-config-fields.tsx` owns the recursive config field editor, keeping the main plugins route focused on state, install flows, and inspector actions. - **Settings**: `settings-view.tsx` now orchestrates route state/data loading while `settings-view-state.ts` owns route-local state bundling, `settings-view-state-actions.ts` stays as a thin mutation shell over extracted plugin and knowledge binders plus shared action-option types, route-local tab modules own general, reactions, plugin-settings shell, extracted OpenAPI/TTS/plugin-platform panes, the TTS pane now composes extracted row and prop-type seams, knowledge-store panes, and extracted settings helper shells/actions keep mutations out of the route component. @@ -37,7 +37,8 @@ React/Tailwind source for the Control Center management UI. This renderer presen - `control-center/integrations-view.tsx`: Coding-agent integrations route shell and action wiring. - `control-center/integrations-view-state.ts`: Integrations route state, effect loading, async action wiring, and derived card/detail metadata. - `control-center/integrations-view-shared.tsx`: Route-local integrations icons, path field, and status-tone helpers. -- `control-center/integrations-view-tooling-sections.tsx`: Extracted MCP-tooling, FamiliarOS-MCP, curated toolkit, and Pi detail panels. +- `control-center/integrations-view-tooling-sections.tsx`: Integrations tooling shell for MCP-tool servers, FamiliarOS MCP controls, Pi guidance, and the extracted curated toolkit panel. +- `control-center/integrations-view-mcp-toolkit-section.tsx`: Extracted curated toolkit panel with manual versus persistent host install flows, catalog details, and follow-up guidance. - `control-center/plugins-view.tsx`: Plugin management route. - `control-center/plugins-view-config-helpers.ts`: Extracted plugin config draft/materialization helpers for the plugins route. - `control-center/plugins-view-config-fields.tsx`: Extracted recursive plugin config field editor for the plugins route inspector. diff --git a/apps/desktop/src/renderer/src/control-center/integrations-view-mcp-toolkit-section.tsx b/apps/desktop/src/renderer/src/control-center/integrations-view-mcp-toolkit-section.tsx new file mode 100644 index 00000000..b72e2c5b --- /dev/null +++ b/apps/desktop/src/renderer/src/control-center/integrations-view-mcp-toolkit-section.tsx @@ -0,0 +1,268 @@ +import { getMcpToolkitEntry, mcpToolkitEntries, mcpToolkitTierLabels } from "../mcp-toolkit-catalog"; +import { + buildPersistentToolkitBundle, + Button, + CopyIcon, + ExternalLinkIcon, + InstallIcon, + StatusPill, + type McpToolkitInstallMode, + type McpToolkitInstallResult, + type McpToolkitPersistentTarget, +} from "./shared"; +import { getPersistentToolkitFollowUps } from "./shared"; +import { mcpToolkitTrustTone } from "./integrations-view-shared"; + +export type McpToolkitSectionProps = { + selectedToolkitId: string; + onSelectedToolkitIdChange: (toolkitId: string) => void; + toolkitInstallMode: McpToolkitInstallMode; + onToolkitInstallModeChange: (mode: McpToolkitInstallMode) => void; + toolkitPersistentTarget: McpToolkitPersistentTarget; + onToolkitPersistentTargetChange: (target: McpToolkitPersistentTarget) => void; + toolkitLastInstall: McpToolkitInstallResult | null; + isBusy: boolean; + onInstallToolkitHost: () => Promise; + onCopyText: (label: string, value: string) => Promise; + onOpenDocs: (url: string) => Promise; +}; + +export function McpToolkitSection({ + selectedToolkitId, + onSelectedToolkitIdChange, + toolkitInstallMode, + onToolkitInstallModeChange, + toolkitPersistentTarget, + onToolkitPersistentTargetChange, + toolkitLastInstall, + isBusy, + onInstallToolkitHost, + onCopyText, + onOpenDocs, +}: McpToolkitSectionProps) { + const selectedToolkit = getMcpToolkitEntry(selectedToolkitId) ?? mcpToolkitEntries[0]; + const persistentBundle = buildPersistentToolkitBundle(toolkitPersistentTarget); + const persistentFollowUps = getPersistentToolkitFollowUps(); + + return ( + <> +
+
Install ChoiceToolkit behavior
+

+ Choose how you want to set up MCP tools for external host agents like Claude Code or Codex CLI. +

+
+ + +
+ + Manual Setup shows copy-paste commands for each tool so you can install them yourself into your host agent. Persistent Full Access lets FamiliarOS register the supported baseline automatically into Claude Code or Codex CLI. + +
+ +
+
CatalogBrowse & Details
+

+ Click any tool below to view its details, installation snippets, and safety notes. +

+
+ {(["starter", "system", "advanced"] as const).map((tier) => { + const entries = mcpToolkitEntries.filter((entry) => entry.tier === tier); + return ( +
+
+ {mcpToolkitTierLabels[tier]} + {entries.length} options +
+
+ {entries.map((entry) => ( + + ))} +
+
+ ); + })} +
+
+ + {toolkitInstallMode === "manual" ? ( + <> +
+
+
+ {mcpToolkitTierLabels[selectedToolkit.tier]} + {selectedToolkit.name} +
+
+ {selectedToolkit.badge} + +
+
+ +

{selectedToolkit.summary}

+ +
+
+ Why it matters +

{selectedToolkit.whyItMatters}

+
+
+ VectorShell fit +

{selectedToolkit.vectorShellFit}

+
+
+ +
+ Permission boundary +

{selectedToolkit.safety}

+
+ +
+ {selectedToolkit.tags.map((tag) => ( + {tag} + ))} +
+
+ + {selectedToolkit.snippets?.length ? ( +
+
Install SurfaceReady-to-paste snippets
+
+ {selectedToolkit.snippets.map((snippet) => ( +
+
+
+ {snippet.label} + {snippet.description && {snippet.description}} +
+ +
+
{snippet.value}
+
+ ))} +
+
+ ) : null} + + {selectedToolkit.notes?.length ? ( +
+
NotesSelection guidance
+
    + {selectedToolkit.notes.map((note) =>
  • {note}
  • )} +
+
+ ) : null} + + ) : ( + <> +
+
Persistent InstallChoose your host
+

+ This mode is for the “install it for good” path. FamiliarOS can install the supported persistent baseline for you now, and it also shows the exact matching bundle if you prefer to run it yourself. +

+
+ + +
+
+ +
+
+
+ Persistent Full Access + {persistentBundle.label} +
+
+ + +
+
+

{persistentBundle.description}

+
{persistentBundle.value}
+
+ +
+
Included NowStable persistent baseline
+
+ {["filesystem", "playwright", "browser-use", "memory", "fetch-web", "sequential-thinking"].map((id) => { + const entry = getMcpToolkitEntry(id); + return entry ? {entry.name} : null; + })} + Context7 / Docs +
+ + These are the lanes FamiliarOS can attempt automatically today. Browser Use and Fetch / Web still depend on `uvx`, and Browser Use still needs its own runtime credential in the host environment. + +
+ + {toolkitLastInstall && toolkitLastInstall.target === toolkitPersistentTarget && ( +
+
Last Install{toolkitLastInstall.label}
+ {toolkitLastInstall.installed.length > 0 && ( +
+ {toolkitLastInstall.installed.map((entry) => ( +
+
+ {entry.name} + Installed +
+ {entry.detail} +
+ ))} +
+ )} + {toolkitLastInstall.skipped.length > 0 && ( +
+ {toolkitLastInstall.skipped.map((entry) => ( +
+
+ {entry.name} + Skipped +
+ {entry.detail} +
+ ))} +
+ )} + {toolkitLastInstall.notes.length > 0 && ( +
    + {toolkitLastInstall.notes.map((note) =>
  • {note}
  • )} +
+ )} +
+ )} + +
+
Manual Follow-UpStill better done explicitly
+

+ These lanes stay outside the one-shot persistent bundle for now because they are more dependent on auth, provider choice, host environment, or high-risk permission boundaries. +

+
+ {persistentFollowUps.map((name) => ( + {name} + ))} +
+
+ + )} + + ); +} diff --git a/apps/desktop/src/renderer/src/control-center/integrations-view-tooling-sections.tsx b/apps/desktop/src/renderer/src/control-center/integrations-view-tooling-sections.tsx index 91c847c2..3c5ddead 100644 --- a/apps/desktop/src/renderer/src/control-center/integrations-view-tooling-sections.tsx +++ b/apps/desktop/src/renderer/src/control-center/integrations-view-tooling-sections.tsx @@ -1,25 +1,22 @@ import { useI18n } from "../i18n"; -import { getMcpToolkitEntry, mcpToolkitEntries, mcpToolkitTierLabels } from "../mcp-toolkit-catalog"; +import { mcpToolkitEntries, mcpToolkitTierLabels } from "../mcp-toolkit-catalog"; import { - buildPersistentToolkitBundle, Button, commandModeLabelKeys, CopyIcon, - ExternalLinkIcon, InstallIcon, NextIcon, RefreshIcon, Spinner, - StatusPill, type AgentSetupSnapshot, type FamiliarOSMcpServerHealth, type FamiliarOSMcpServerPreview, - type McpToolkitInstallMode, - type McpToolkitInstallResult, - type McpToolkitPersistentTarget, } from "./shared"; -import { getPersistentToolkitFollowUps } from "./shared"; -import { PathField, mcpToolkitTrustTone } from "./integrations-view-shared"; +import { PathField } from "./integrations-view-shared"; +import { + McpToolkitSection as McpToolkitSectionSeam, + type McpToolkitSectionProps, +} from "./integrations-view-mcp-toolkit-section"; export function McpToolServersSection({ vanillaChatTools, @@ -212,254 +209,6 @@ export function PiIntegrationSection() { ); } -export function McpToolkitSection({ - selectedToolkitId, - onSelectedToolkitIdChange, - toolkitInstallMode, - onToolkitInstallModeChange, - toolkitPersistentTarget, - onToolkitPersistentTargetChange, - toolkitLastInstall, - isBusy, - onInstallToolkitHost, - onCopyText, - onOpenDocs, -}: { - selectedToolkitId: string; - onSelectedToolkitIdChange: (toolkitId: string) => void; - toolkitInstallMode: McpToolkitInstallMode; - onToolkitInstallModeChange: (mode: McpToolkitInstallMode) => void; - toolkitPersistentTarget: McpToolkitPersistentTarget; - onToolkitPersistentTargetChange: (target: McpToolkitPersistentTarget) => void; - toolkitLastInstall: McpToolkitInstallResult | null; - isBusy: boolean; - onInstallToolkitHost: () => Promise; - onCopyText: (label: string, value: string) => Promise; - onOpenDocs: (url: string) => Promise; -}) { - const selectedToolkit = getMcpToolkitEntry(selectedToolkitId) ?? mcpToolkitEntries[0]; - const persistentBundle = buildPersistentToolkitBundle(toolkitPersistentTarget); - const persistentFollowUps = getPersistentToolkitFollowUps(); - - return ( - <> -
-
Install ChoiceToolkit behavior
-

- Choose how you want to set up MCP tools for external host agents like Claude Code or Codex CLI. -

-
- - -
- - Manual Setup shows copy-paste commands for each tool so you can install them yourself into your host agent. Persistent Full Access lets FamiliarOS register the supported baseline automatically into Claude Code or Codex CLI. - -
- -
-
CatalogBrowse & Details
-

- Click any tool below to view its details, installation snippets, and safety notes. -

-
- {(["starter", "system", "advanced"] as const).map((tier) => { - const entries = mcpToolkitEntries.filter((entry) => entry.tier === tier); - return ( -
-
- {mcpToolkitTierLabels[tier]} - {entries.length} options -
-
- {entries.map((entry) => ( - - ))} -
-
- ); - })} -
-
- - {toolkitInstallMode === "manual" ? ( - <> -
-
-
- {mcpToolkitTierLabels[selectedToolkit.tier]} - {selectedToolkit.name} -
-
- {selectedToolkit.badge} - -
-
- -

{selectedToolkit.summary}

- -
-
- Why it matters -

{selectedToolkit.whyItMatters}

-
-
- VectorShell fit -

{selectedToolkit.vectorShellFit}

-
-
- -
- Permission boundary -

{selectedToolkit.safety}

-
- -
- {selectedToolkit.tags.map((tag) => ( - {tag} - ))} -
-
- - {selectedToolkit.snippets?.length ? ( -
-
Install SurfaceReady-to-paste snippets
-
- {selectedToolkit.snippets.map((snippet) => ( -
-
-
- {snippet.label} - {snippet.description && {snippet.description}} -
- -
-
{snippet.value}
-
- ))} -
-
- ) : null} - - {selectedToolkit.notes?.length ? ( -
-
NotesSelection guidance
-
    - {selectedToolkit.notes.map((note) =>
  • {note}
  • )} -
-
- ) : null} - - ) : ( - <> -
-
Persistent InstallChoose your host
-

- This mode is for the “install it for good” path. FamiliarOS can install the supported persistent baseline for you now, and it also shows the exact matching bundle if you prefer to run it yourself. -

-
- - -
-
- -
-
-
- Persistent Full Access - {persistentBundle.label} -
-
- - -
-
-

{persistentBundle.description}

-
{persistentBundle.value}
-
- -
-
Included NowStable persistent baseline
-
- {["filesystem", "playwright", "browser-use", "memory", "fetch-web", "sequential-thinking"].map((id) => { - const entry = getMcpToolkitEntry(id); - return entry ? {entry.name} : null; - })} - Context7 / Docs -
- - These are the lanes FamiliarOS can attempt automatically today. Browser Use and Fetch / Web still depend on `uvx`, and Browser Use still needs its own runtime credential in the host environment. - -
- - {toolkitLastInstall && toolkitLastInstall.target === toolkitPersistentTarget && ( -
-
Last Install{toolkitLastInstall.label}
- {toolkitLastInstall.installed.length > 0 && ( -
- {toolkitLastInstall.installed.map((entry) => ( -
-
- {entry.name} - Installed -
- {entry.detail} -
- ))} -
- )} - {toolkitLastInstall.skipped.length > 0 && ( -
- {toolkitLastInstall.skipped.map((entry) => ( -
-
- {entry.name} - Skipped -
- {entry.detail} -
- ))} -
- )} - {toolkitLastInstall.notes.length > 0 && ( -
    - {toolkitLastInstall.notes.map((note) =>
  • {note}
  • )} -
- )} -
- )} - -
-
Manual Follow-UpStill better done explicitly
-

- These lanes stay outside the one-shot persistent bundle for now because they are more dependent on auth, provider choice, host environment, or high-risk permission boundaries. -

-
- {persistentFollowUps.map((name) => ( - {name} - ))} -
-
- - )} - - ); +export function McpToolkitSection(props: McpToolkitSectionProps) { + return ; } diff --git a/apps/desktop/tests/integrations-view-toolkit-split.test.ts b/apps/desktop/tests/integrations-view-toolkit-split.test.ts new file mode 100644 index 00000000..58741e64 --- /dev/null +++ b/apps/desktop/tests/integrations-view-toolkit-split.test.ts @@ -0,0 +1,15 @@ +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const desktopRoot = process.env.FAMILIAROS_DESKTOP_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), ".."); +const toolingSectionsSource = readFileSync(resolve(desktopRoot, "src/renderer/src/control-center/integrations-view-tooling-sections.tsx"), "utf8"); +const toolkitSectionSource = readFileSync(resolve(desktopRoot, "src/renderer/src/control-center/integrations-view-mcp-toolkit-section.tsx"), "utf8"); + +assert.match(toolingSectionsSource, /from "\.\/integrations-view-mcp-toolkit-section"/, "Integrations tooling shell must import the extracted MCP toolkit section seam."); +assert.match(toolkitSectionSource, /export function McpToolkitSection/, "Extracted integrations toolkit seam must export the curated toolkit section."); +assert.match(toolkitSectionSource, /buildPersistentToolkitBundle/, "Extracted integrations toolkit seam must own persistent bundle composition."); +assert.match(toolkitSectionSource, /getPersistentToolkitFollowUps/, "Extracted integrations toolkit seam must own persistent follow-up guidance."); + +console.error("Integrations toolkit split validation passed.");