From 8454ae4f41904af875879a9d3201c6479eab216b Mon Sep 17 00:00:00 2001 From: catlog22 Date: Wed, 4 Feb 2026 15:24:34 +0800 Subject: [PATCH] feat: add quick install templates and index status to CLI hooks and home locales feat: enhance MCP manager with interactive question feature and update locales feat: implement tags and available models management in settings page fix: improve process termination logic in stop command for React frontend fix: update view command to default to 'js' frontend feat: add Recommended MCP Wizard component for dynamic server configuration --- ccw/docs-site/.docusaurus/registry.js | 82 ++-- ccw/docs-site/.docusaurus/routes.js | 35 -- .../.docusaurus/routesChunkNames.json | 177 +++----- ccw/docs-site/.docusaurus/site-metadata.json | 4 +- .../dashboard/widgets/WorkflowTaskWidget.tsx | 32 ++ .../src/components/layout/Sidebar.tsx | 21 +- .../src/components/mcp/CcwToolsMcpCard.tsx | 14 +- .../src/components/mcp/ConfigTypeToggle.tsx | 76 ++-- .../src/components/mcp/McpServerDialog.tsx | 16 + .../components/mcp/RecommendedMcpSection.tsx | 373 ++++++++-------- .../components/mcp/RecommendedMcpWizard.tsx | 363 +++++++++++++++ ccw/frontend/src/lib/api.ts | 189 +++++++- ccw/frontend/src/locales/en/cli-hooks.json | 3 + ccw/frontend/src/locales/en/home.json | 3 + ccw/frontend/src/locales/en/mcp-manager.json | 4 + ccw/frontend/src/locales/zh/cli-hooks.json | 3 + ccw/frontend/src/locales/zh/home.json | 3 + ccw/frontend/src/locales/zh/mcp-manager.json | 4 + ccw/frontend/src/pages/McpManagerPage.tsx | 55 +-- ccw/frontend/src/pages/SettingsPage.tsx | 419 +++++++++--------- ccw/frontend/src/types/store.ts | 1 + ccw/src/commands/stop.ts | 32 +- ccw/src/commands/view.ts | 2 +- .../dashboard-js/views/mcp-manager.js | 2 + 24 files changed, 1186 insertions(+), 727 deletions(-) create mode 100644 ccw/frontend/src/components/mcp/RecommendedMcpWizard.tsx diff --git a/ccw/docs-site/.docusaurus/registry.js b/ccw/docs-site/.docusaurus/registry.js index 1935039b..440b1fda 100644 --- a/ccw/docs-site/.docusaurus/registry.js +++ b/ccw/docs-site/.docusaurus/registry.js @@ -1,46 +1,38 @@ export default { - "__comp---theme-debug-config-23-a-2ff": [() => import(/* webpackChunkName: "__comp---theme-debug-config-23-a-2ff" */ "@theme/DebugConfig"), "@theme/DebugConfig", require.resolveWeak("@theme/DebugConfig")], - "__comp---theme-debug-contentba-8-ce7": [() => import(/* webpackChunkName: "__comp---theme-debug-contentba-8-ce7" */ "@theme/DebugContent"), "@theme/DebugContent", require.resolveWeak("@theme/DebugContent")], - "__comp---theme-debug-global-dataede-0fa": [() => import(/* webpackChunkName: "__comp---theme-debug-global-dataede-0fa" */ "@theme/DebugGlobalData"), "@theme/DebugGlobalData", require.resolveWeak("@theme/DebugGlobalData")], - "__comp---theme-debug-registry-679-501": [() => import(/* webpackChunkName: "__comp---theme-debug-registry-679-501" */ "@theme/DebugRegistry"), "@theme/DebugRegistry", require.resolveWeak("@theme/DebugRegistry")], - "__comp---theme-debug-routes-946-699": [() => import(/* webpackChunkName: "__comp---theme-debug-routes-946-699" */ "@theme/DebugRoutes"), "@theme/DebugRoutes", require.resolveWeak("@theme/DebugRoutes")], - "__comp---theme-debug-site-metadata-68-e-3d4": [() => import(/* webpackChunkName: "__comp---theme-debug-site-metadata-68-e-3d4" */ "@theme/DebugSiteMetadata"), "@theme/DebugSiteMetadata", require.resolveWeak("@theme/DebugSiteMetadata")], - "__comp---theme-doc-item-178-a40": [() => import(/* webpackChunkName: "__comp---theme-doc-item-178-a40" */ "@theme/DocItem"), "@theme/DocItem", require.resolveWeak("@theme/DocItem")], - "__comp---theme-doc-roota-94-67a": [() => import(/* webpackChunkName: "__comp---theme-doc-roota-94-67a" */ "@theme/DocRoot"), "@theme/DocRoot", require.resolveWeak("@theme/DocRoot")], - "__comp---theme-doc-version-roota-7-b-5de": [() => import(/* webpackChunkName: "__comp---theme-doc-version-roota-7-b-5de" */ "@theme/DocVersionRoot"), "@theme/DocVersionRoot", require.resolveWeak("@theme/DocVersionRoot")], - "__comp---theme-docs-root-5-e-9-0b6": [() => import(/* webpackChunkName: "__comp---theme-docs-root-5-e-9-0b6" */ "@theme/DocsRoot"), "@theme/DocsRoot", require.resolveWeak("@theme/DocsRoot")], - "__props---docs-docsa-20-f19": [() => import(/* webpackChunkName: "__props---docs-docsa-20-f19" */ "@generated/docusaurus-plugin-content-docs/default/p/docs-docs-fbb.json"), "@generated/docusaurus-plugin-content-docs/default/p/docs-docs-fbb.json", require.resolveWeak("@generated/docusaurus-plugin-content-docs/default/p/docs-docs-fbb.json")], - "__props---docs-docusaurus-debug-content-344-8d5": [() => import(/* webpackChunkName: "__props---docs-docusaurus-debug-content-344-8d5" */ "@generated/docusaurus-plugin-debug/default/p/docs-docusaurus-debug-content-a52.json"), "@generated/docusaurus-plugin-debug/default/p/docs-docusaurus-debug-content-a52.json", require.resolveWeak("@generated/docusaurus-plugin-debug/default/p/docs-docusaurus-debug-content-a52.json")], - "content---docs-docs-commands-cli-cli-init-056-2d3": [() => import(/* webpackChunkName: "content---docs-docs-commands-cli-cli-init-056-2d3" */ "@site/docs/commands/cli/cli-init.mdx"), "@site/docs/commands/cli/cli-init.mdx", require.resolveWeak("@site/docs/commands/cli/cli-init.mdx")], - "content---docs-docs-commands-cli-codex-reviewf-1-b-532": [() => import(/* webpackChunkName: "content---docs-docs-commands-cli-codex-reviewf-1-b-532" */ "@site/docs/commands/cli/codex-review.mdx"), "@site/docs/commands/cli/codex-review.mdx", require.resolveWeak("@site/docs/commands/cli/codex-review.mdx")], - "content---docs-docs-commands-general-ccw-coordinatord-55-a04": [() => import(/* webpackChunkName: "content---docs-docs-commands-general-ccw-coordinatord-55-a04" */ "@site/docs/commands/general/ccw-coordinator.mdx"), "@site/docs/commands/general/ccw-coordinator.mdx", require.resolveWeak("@site/docs/commands/general/ccw-coordinator.mdx")], - "content---docs-docs-commands-general-ccw-debug-97-c-2c8": [() => import(/* webpackChunkName: "content---docs-docs-commands-general-ccw-debug-97-c-2c8" */ "@site/docs/commands/general/ccw-debug.mdx"), "@site/docs/commands/general/ccw-debug.mdx", require.resolveWeak("@site/docs/commands/general/ccw-debug.mdx")], - "content---docs-docs-commands-general-ccw-plan-04-d-aa3": [() => import(/* webpackChunkName: "content---docs-docs-commands-general-ccw-plan-04-d-aa3" */ "@site/docs/commands/general/ccw-plan.mdx"), "@site/docs/commands/general/ccw-plan.mdx", require.resolveWeak("@site/docs/commands/general/ccw-plan.mdx")], - "content---docs-docs-commands-general-ccw-testcce-a5d": [() => import(/* webpackChunkName: "content---docs-docs-commands-general-ccw-testcce-a5d" */ "@site/docs/commands/general/ccw-test.mdx"), "@site/docs/commands/general/ccw-test.mdx", require.resolveWeak("@site/docs/commands/general/ccw-test.mdx")], - "content---docs-docs-commands-general-ccwf-48-4f9": [() => import(/* webpackChunkName: "content---docs-docs-commands-general-ccwf-48-4f9" */ "@site/docs/commands/general/ccw.mdx"), "@site/docs/commands/general/ccw.mdx", require.resolveWeak("@site/docs/commands/general/ccw.mdx")], - "content---docs-docs-commands-general-codex-coordinatorf-92-965": [() => import(/* webpackChunkName: "content---docs-docs-commands-general-codex-coordinatorf-92-965" */ "@site/docs/commands/general/codex-coordinator.mdx"), "@site/docs/commands/general/codex-coordinator.mdx", require.resolveWeak("@site/docs/commands/general/codex-coordinator.mdx")], - "content---docs-docs-commands-general-flow-createfab-5a7": [() => import(/* webpackChunkName: "content---docs-docs-commands-general-flow-createfab-5a7" */ "@site/docs/commands/general/flow-create.mdx"), "@site/docs/commands/general/flow-create.mdx", require.resolveWeak("@site/docs/commands/general/flow-create.mdx")], - "content---docs-docs-commands-issue-issue-convert-to-plan-5-c-7-428": [() => import(/* webpackChunkName: "content---docs-docs-commands-issue-issue-convert-to-plan-5-c-7-428" */ "@site/docs/commands/issue/issue-convert-to-plan.md"), "@site/docs/commands/issue/issue-convert-to-plan.md", require.resolveWeak("@site/docs/commands/issue/issue-convert-to-plan.md")], - "content---docs-docs-commands-issue-issue-discover-1-e-3-61d": [() => import(/* webpackChunkName: "content---docs-docs-commands-issue-issue-discover-1-e-3-61d" */ "@site/docs/commands/issue/issue-discover.md"), "@site/docs/commands/issue/issue-discover.md", require.resolveWeak("@site/docs/commands/issue/issue-discover.md")], - "content---docs-docs-commands-issue-issue-executefe-8-121": [() => import(/* webpackChunkName: "content---docs-docs-commands-issue-issue-executefe-8-121" */ "@site/docs/commands/issue/issue-execute.md"), "@site/docs/commands/issue/issue-execute.md", require.resolveWeak("@site/docs/commands/issue/issue-execute.md")], - "content---docs-docs-commands-issue-issue-from-brainstorm-2-ec-ca5": [() => import(/* webpackChunkName: "content---docs-docs-commands-issue-issue-from-brainstorm-2-ec-ca5" */ "@site/docs/commands/issue/issue-from-brainstorm.md"), "@site/docs/commands/issue/issue-from-brainstorm.md", require.resolveWeak("@site/docs/commands/issue/issue-from-brainstorm.md")], - "content---docs-docs-commands-issue-issue-new-4-ad-afb": [() => import(/* webpackChunkName: "content---docs-docs-commands-issue-issue-new-4-ad-afb" */ "@site/docs/commands/issue/issue-new.md"), "@site/docs/commands/issue/issue-new.md", require.resolveWeak("@site/docs/commands/issue/issue-new.md")], - "content---docs-docs-commands-issue-issue-plana-6-c-1b0": [() => import(/* webpackChunkName: "content---docs-docs-commands-issue-issue-plana-6-c-1b0" */ "@site/docs/commands/issue/issue-plan.md"), "@site/docs/commands/issue/issue-plan.md", require.resolveWeak("@site/docs/commands/issue/issue-plan.md")], - "content---docs-docs-commands-issue-issue-queue-1-ba-06f": [() => import(/* webpackChunkName: "content---docs-docs-commands-issue-issue-queue-1-ba-06f" */ "@site/docs/commands/issue/issue-queue.md"), "@site/docs/commands/issue/issue-queue.md", require.resolveWeak("@site/docs/commands/issue/issue-queue.md")], - "content---docs-docs-commands-memory-memory-compact-7-a-1-f85": [() => import(/* webpackChunkName: "content---docs-docs-commands-memory-memory-compact-7-a-1-f85" */ "@site/docs/commands/memory/memory-compact.mdx"), "@site/docs/commands/memory/memory-compact.mdx", require.resolveWeak("@site/docs/commands/memory/memory-compact.mdx")], - "content---docs-docs-commands-memory-memory-docs-full-cli-4-cc-b15": [() => import(/* webpackChunkName: "content---docs-docs-commands-memory-memory-docs-full-cli-4-cc-b15" */ "@site/docs/commands/memory/memory-docs-full-cli.mdx"), "@site/docs/commands/memory/memory-docs-full-cli.mdx", require.resolveWeak("@site/docs/commands/memory/memory-docs-full-cli.mdx")], - "content---docs-docs-commands-memory-memory-docs-related-cli-60-e-28a": [() => import(/* webpackChunkName: "content---docs-docs-commands-memory-memory-docs-related-cli-60-e-28a" */ "@site/docs/commands/memory/memory-docs-related-cli.mdx"), "@site/docs/commands/memory/memory-docs-related-cli.mdx", require.resolveWeak("@site/docs/commands/memory/memory-docs-related-cli.mdx")], - "content---docs-docs-commands-memory-memory-load-157-920": [() => import(/* webpackChunkName: "content---docs-docs-commands-memory-memory-load-157-920" */ "@site/docs/commands/memory/memory-load.mdx"), "@site/docs/commands/memory/memory-load.mdx", require.resolveWeak("@site/docs/commands/memory/memory-load.mdx")], - "content---docs-docs-commands-memory-memory-update-full-666-28e": [() => import(/* webpackChunkName: "content---docs-docs-commands-memory-memory-update-full-666-28e" */ "@site/docs/commands/memory/memory-update-full.mdx"), "@site/docs/commands/memory/memory-update-full.mdx", require.resolveWeak("@site/docs/commands/memory/memory-update-full.mdx")], - "content---docs-docs-commands-memory-memory-update-related-611-f0a": [() => import(/* webpackChunkName: "content---docs-docs-commands-memory-memory-update-related-611-f0a" */ "@site/docs/commands/memory/memory-update-related.mdx"), "@site/docs/commands/memory/memory-update-related.mdx", require.resolveWeak("@site/docs/commands/memory/memory-update-related.mdx")], - "content---docs-docs-faqea-3-29f": [() => import(/* webpackChunkName: "content---docs-docs-faqea-3-29f" */ "@site/docs/faq.mdx"), "@site/docs/faq.mdx", require.resolveWeak("@site/docs/faq.mdx")], - "content---docs-docs-overview-188-8fe": [() => import(/* webpackChunkName: "content---docs-docs-overview-188-8fe" */ "@site/docs/overview.mdx"), "@site/docs/overview.mdx", require.resolveWeak("@site/docs/overview.mdx")], - "content---docs-docs-workflows-faqbcf-a47": [() => import(/* webpackChunkName: "content---docs-docs-workflows-faqbcf-a47" */ "@site/docs/workflows/faq.mdx"), "@site/docs/workflows/faq.mdx", require.resolveWeak("@site/docs/workflows/faq.mdx")], - "content---docs-docs-workflows-introduction-9-f-4-dba": [() => import(/* webpackChunkName: "content---docs-docs-workflows-introduction-9-f-4-dba" */ "@site/docs/workflows/introduction.mdx"), "@site/docs/workflows/introduction.mdx", require.resolveWeak("@site/docs/workflows/introduction.mdx")], - "content---docs-docs-workflows-level-1-ultra-lightweightc-5-a-692": [() => import(/* webpackChunkName: "content---docs-docs-workflows-level-1-ultra-lightweightc-5-a-692" */ "@site/docs/workflows/level-1-ultra-lightweight.mdx"), "@site/docs/workflows/level-1-ultra-lightweight.mdx", require.resolveWeak("@site/docs/workflows/level-1-ultra-lightweight.mdx")], - "content---docs-docs-workflows-level-2-rapid-19-b-a2f": [() => import(/* webpackChunkName: "content---docs-docs-workflows-level-2-rapid-19-b-a2f" */ "@site/docs/workflows/level-2-rapid.mdx"), "@site/docs/workflows/level-2-rapid.mdx", require.resolveWeak("@site/docs/workflows/level-2-rapid.mdx")], - "content---docs-docs-workflows-level-3-standardbdb-19a": [() => import(/* webpackChunkName: "content---docs-docs-workflows-level-3-standardbdb-19a" */ "@site/docs/workflows/level-3-standard.mdx"), "@site/docs/workflows/level-3-standard.mdx", require.resolveWeak("@site/docs/workflows/level-3-standard.mdx")], - "content---docs-docs-workflows-level-4-brainstormd-04-69a": [() => import(/* webpackChunkName: "content---docs-docs-workflows-level-4-brainstormd-04-69a" */ "@site/docs/workflows/level-4-brainstorm.mdx"), "@site/docs/workflows/level-4-brainstorm.mdx", require.resolveWeak("@site/docs/workflows/level-4-brainstorm.mdx")], - "content---docs-docs-workflows-level-5-intelligent-186-435": [() => import(/* webpackChunkName: "content---docs-docs-workflows-level-5-intelligent-186-435" */ "@site/docs/workflows/level-5-intelligent.mdx"), "@site/docs/workflows/level-5-intelligent.mdx", require.resolveWeak("@site/docs/workflows/level-5-intelligent.mdx")], - "plugin---docs-docsaba-31e": [() => import(/* webpackChunkName: "plugin---docs-docsaba-31e" */ "@generated/docusaurus-plugin-content-docs/default/__plugin.json"), "@generated/docusaurus-plugin-content-docs/default/__plugin.json", require.resolveWeak("@generated/docusaurus-plugin-content-docs/default/__plugin.json")], - "plugin---docs-docusaurus-debugb-38-c84": [() => import(/* webpackChunkName: "plugin---docs-docusaurus-debugb-38-c84" */ "@generated/docusaurus-plugin-debug/default/__plugin.json"), "@generated/docusaurus-plugin-debug/default/__plugin.json", require.resolveWeak("@generated/docusaurus-plugin-debug/default/__plugin.json")],}; + "04db0a2e": [() => import(/* webpackChunkName: "04db0a2e" */ "@site/docs/commands/general/ccw-plan.mdx"), "@site/docs/commands/general/ccw-plan.mdx", require.resolveWeak("@site/docs/commands/general/ccw-plan.mdx")], + "0566a0a8": [() => import(/* webpackChunkName: "0566a0a8" */ "@site/docs/commands/cli/cli-init.mdx"), "@site/docs/commands/cli/cli-init.mdx", require.resolveWeak("@site/docs/commands/cli/cli-init.mdx")], + "157db180": [() => import(/* webpackChunkName: "157db180" */ "@site/docs/commands/memory/memory-load.mdx"), "@site/docs/commands/memory/memory-load.mdx", require.resolveWeak("@site/docs/commands/memory/memory-load.mdx")], + "17896441": [() => import(/* webpackChunkName: "17896441" */ "@theme/DocItem"), "@theme/DocItem", require.resolveWeak("@theme/DocItem")], + "186dcf4e": [() => import(/* webpackChunkName: "186dcf4e" */ "@site/docs/workflows/level-5-intelligent.mdx"), "@site/docs/workflows/level-5-intelligent.mdx", require.resolveWeak("@site/docs/workflows/level-5-intelligent.mdx")], + "18891827": [() => import(/* webpackChunkName: "18891827" */ "@site/docs/overview.mdx"), "@site/docs/overview.mdx", require.resolveWeak("@site/docs/overview.mdx")], + "19b64556": [() => import(/* webpackChunkName: "19b64556" */ "@site/docs/workflows/level-2-rapid.mdx"), "@site/docs/workflows/level-2-rapid.mdx", require.resolveWeak("@site/docs/workflows/level-2-rapid.mdx")], + "1bac9067": [() => import(/* webpackChunkName: "1bac9067" */ "@site/docs/commands/issue/issue-queue.md"), "@site/docs/commands/issue/issue-queue.md", require.resolveWeak("@site/docs/commands/issue/issue-queue.md")], + "1e3006f3": [() => import(/* webpackChunkName: "1e3006f3" */ "@site/docs/commands/issue/issue-discover.md"), "@site/docs/commands/issue/issue-discover.md", require.resolveWeak("@site/docs/commands/issue/issue-discover.md")], + "2ecf8b4a": [() => import(/* webpackChunkName: "2ecf8b4a" */ "@site/docs/commands/issue/issue-from-brainstorm.md"), "@site/docs/commands/issue/issue-from-brainstorm.md", require.resolveWeak("@site/docs/commands/issue/issue-from-brainstorm.md")], + "4ad7db0f": [() => import(/* webpackChunkName: "4ad7db0f" */ "@site/docs/commands/issue/issue-new.md"), "@site/docs/commands/issue/issue-new.md", require.resolveWeak("@site/docs/commands/issue/issue-new.md")], + "4cc74730": [() => import(/* webpackChunkName: "4cc74730" */ "@site/docs/commands/memory/memory-docs-full-cli.mdx"), "@site/docs/commands/memory/memory-docs-full-cli.mdx", require.resolveWeak("@site/docs/commands/memory/memory-docs-full-cli.mdx")], + "5c7b2278": [() => import(/* webpackChunkName: "5c7b2278" */ "@site/docs/commands/issue/issue-convert-to-plan.md"), "@site/docs/commands/issue/issue-convert-to-plan.md", require.resolveWeak("@site/docs/commands/issue/issue-convert-to-plan.md")], + "5e95c892": [() => import(/* webpackChunkName: "5e95c892" */ "@theme/DocsRoot"), "@theme/DocsRoot", require.resolveWeak("@theme/DocsRoot")], + "60eef997": [() => import(/* webpackChunkName: "60eef997" */ "@site/docs/commands/memory/memory-docs-related-cli.mdx"), "@site/docs/commands/memory/memory-docs-related-cli.mdx", require.resolveWeak("@site/docs/commands/memory/memory-docs-related-cli.mdx")], + "611877e1": [() => import(/* webpackChunkName: "611877e1" */ "@site/docs/commands/memory/memory-update-related.mdx"), "@site/docs/commands/memory/memory-update-related.mdx", require.resolveWeak("@site/docs/commands/memory/memory-update-related.mdx")], + "666bb1bf": [() => import(/* webpackChunkName: "666bb1bf" */ "@site/docs/commands/memory/memory-update-full.mdx"), "@site/docs/commands/memory/memory-update-full.mdx", require.resolveWeak("@site/docs/commands/memory/memory-update-full.mdx")], + "7a1ee27c": [() => import(/* webpackChunkName: "7a1ee27c" */ "@site/docs/commands/memory/memory-compact.mdx"), "@site/docs/commands/memory/memory-compact.mdx", require.resolveWeak("@site/docs/commands/memory/memory-compact.mdx")], + "97c6e66a": [() => import(/* webpackChunkName: "97c6e66a" */ "@site/docs/commands/general/ccw-debug.mdx"), "@site/docs/commands/general/ccw-debug.mdx", require.resolveWeak("@site/docs/commands/general/ccw-debug.mdx")], + "9f4ca91e": [() => import(/* webpackChunkName: "9f4ca91e" */ "@site/docs/workflows/introduction.mdx"), "@site/docs/workflows/introduction.mdx", require.resolveWeak("@site/docs/workflows/introduction.mdx")], + "a2065270": [() => import(/* webpackChunkName: "a2065270" */ "@generated/docusaurus-plugin-content-docs/default/p/docs-docs-fbb.json"), "@generated/docusaurus-plugin-content-docs/default/p/docs-docs-fbb.json", require.resolveWeak("@generated/docusaurus-plugin-content-docs/default/p/docs-docs-fbb.json")], + "a6c3df16": [() => import(/* webpackChunkName: "a6c3df16" */ "@site/docs/commands/issue/issue-plan.md"), "@site/docs/commands/issue/issue-plan.md", require.resolveWeak("@site/docs/commands/issue/issue-plan.md")], + "a7bd4aaa": [() => import(/* webpackChunkName: "a7bd4aaa" */ "@theme/DocVersionRoot"), "@theme/DocVersionRoot", require.resolveWeak("@theme/DocVersionRoot")], + "a94703ab": [() => import(/* webpackChunkName: "a94703ab" */ "@theme/DocRoot"), "@theme/DocRoot", require.resolveWeak("@theme/DocRoot")], + "aba21aa0": [() => import(/* webpackChunkName: "aba21aa0" */ "@generated/docusaurus-plugin-content-docs/default/__plugin.json"), "@generated/docusaurus-plugin-content-docs/default/__plugin.json", require.resolveWeak("@generated/docusaurus-plugin-content-docs/default/__plugin.json")], + "bcf6b37c": [() => import(/* webpackChunkName: "bcf6b37c" */ "@site/docs/workflows/faq.mdx"), "@site/docs/workflows/faq.mdx", require.resolveWeak("@site/docs/workflows/faq.mdx")], + "bdb2b105": [() => import(/* webpackChunkName: "bdb2b105" */ "@site/docs/workflows/level-3-standard.mdx"), "@site/docs/workflows/level-3-standard.mdx", require.resolveWeak("@site/docs/workflows/level-3-standard.mdx")], + "c5a82d8d": [() => import(/* webpackChunkName: "c5a82d8d" */ "@site/docs/workflows/level-1-ultra-lightweight.mdx"), "@site/docs/workflows/level-1-ultra-lightweight.mdx", require.resolveWeak("@site/docs/workflows/level-1-ultra-lightweight.mdx")], + "ccef5d0f": [() => import(/* webpackChunkName: "ccef5d0f" */ "@site/docs/commands/general/ccw-test.mdx"), "@site/docs/commands/general/ccw-test.mdx", require.resolveWeak("@site/docs/commands/general/ccw-test.mdx")], + "d045285b": [() => import(/* webpackChunkName: "d045285b" */ "@site/docs/workflows/level-4-brainstorm.mdx"), "@site/docs/workflows/level-4-brainstorm.mdx", require.resolveWeak("@site/docs/workflows/level-4-brainstorm.mdx")], + "d550a629": [() => import(/* webpackChunkName: "d550a629" */ "@site/docs/commands/general/ccw-coordinator.mdx"), "@site/docs/commands/general/ccw-coordinator.mdx", require.resolveWeak("@site/docs/commands/general/ccw-coordinator.mdx")], + "ea313555": [() => import(/* webpackChunkName: "ea313555" */ "@site/docs/faq.mdx"), "@site/docs/faq.mdx", require.resolveWeak("@site/docs/faq.mdx")], + "f1bf82ec": [() => import(/* webpackChunkName: "f1bf82ec" */ "@site/docs/commands/cli/codex-review.mdx"), "@site/docs/commands/cli/codex-review.mdx", require.resolveWeak("@site/docs/commands/cli/codex-review.mdx")], + "f4817052": [() => import(/* webpackChunkName: "f4817052" */ "@site/docs/commands/general/ccw.mdx"), "@site/docs/commands/general/ccw.mdx", require.resolveWeak("@site/docs/commands/general/ccw.mdx")], + "f9222419": [() => import(/* webpackChunkName: "f9222419" */ "@site/docs/commands/general/codex-coordinator.mdx"), "@site/docs/commands/general/codex-coordinator.mdx", require.resolveWeak("@site/docs/commands/general/codex-coordinator.mdx")], + "fabaf1c8": [() => import(/* webpackChunkName: "fabaf1c8" */ "@site/docs/commands/general/flow-create.mdx"), "@site/docs/commands/general/flow-create.mdx", require.resolveWeak("@site/docs/commands/general/flow-create.mdx")], + "fe8e3dcf": [() => import(/* webpackChunkName: "fe8e3dcf" */ "@site/docs/commands/issue/issue-execute.md"), "@site/docs/commands/issue/issue-execute.md", require.resolveWeak("@site/docs/commands/issue/issue-execute.md")],}; diff --git a/ccw/docs-site/.docusaurus/routes.js b/ccw/docs-site/.docusaurus/routes.js index 3d7e5278..d711bb5f 100644 --- a/ccw/docs-site/.docusaurus/routes.js +++ b/ccw/docs-site/.docusaurus/routes.js @@ -2,41 +2,6 @@ import React from 'react'; import ComponentCreator from '@docusaurus/ComponentCreator'; export default [ - { - path: '/docs/__docusaurus/debug', - component: ComponentCreator('/docs/__docusaurus/debug', 'e58'), - exact: true - }, - { - path: '/docs/__docusaurus/debug/config', - component: ComponentCreator('/docs/__docusaurus/debug/config', '2ce'), - exact: true - }, - { - path: '/docs/__docusaurus/debug/content', - component: ComponentCreator('/docs/__docusaurus/debug/content', '11b'), - exact: true - }, - { - path: '/docs/__docusaurus/debug/globalData', - component: ComponentCreator('/docs/__docusaurus/debug/globalData', 'f13'), - exact: true - }, - { - path: '/docs/__docusaurus/debug/metadata', - component: ComponentCreator('/docs/__docusaurus/debug/metadata', 'bff'), - exact: true - }, - { - path: '/docs/__docusaurus/debug/registry', - component: ComponentCreator('/docs/__docusaurus/debug/registry', '830'), - exact: true - }, - { - path: '/docs/__docusaurus/debug/routes', - component: ComponentCreator('/docs/__docusaurus/debug/routes', '13e'), - exact: true - }, { path: '/docs/docs', component: ComponentCreator('/docs/docs', '942'), diff --git a/ccw/docs-site/.docusaurus/routesChunkNames.json b/ccw/docs-site/.docusaurus/routesChunkNames.json index e754b302..8f4104e8 100644 --- a/ccw/docs-site/.docusaurus/routesChunkNames.json +++ b/ccw/docs-site/.docusaurus/routesChunkNames.json @@ -1,182 +1,139 @@ { - "/docs/__docusaurus/debug-e58": { - "__comp": "__comp---theme-debug-config-23-a-2ff", - "__context": { - "plugin": "plugin---docs-docusaurus-debugb-38-c84" - } - }, - "/docs/__docusaurus/debug/config-2ce": { - "__comp": "__comp---theme-debug-config-23-a-2ff", - "__context": { - "plugin": "plugin---docs-docusaurus-debugb-38-c84" - } - }, - "/docs/__docusaurus/debug/content-11b": { - "__comp": "__comp---theme-debug-contentba-8-ce7", - "__context": { - "plugin": "plugin---docs-docusaurus-debugb-38-c84" - }, - "__props": "__props---docs-docusaurus-debug-content-344-8d5" - }, - "/docs/__docusaurus/debug/globalData-f13": { - "__comp": "__comp---theme-debug-global-dataede-0fa", - "__context": { - "plugin": "plugin---docs-docusaurus-debugb-38-c84" - } - }, - "/docs/__docusaurus/debug/metadata-bff": { - "__comp": "__comp---theme-debug-site-metadata-68-e-3d4", - "__context": { - "plugin": "plugin---docs-docusaurus-debugb-38-c84" - } - }, - "/docs/__docusaurus/debug/registry-830": { - "__comp": "__comp---theme-debug-registry-679-501", - "__context": { - "plugin": "plugin---docs-docusaurus-debugb-38-c84" - } - }, - "/docs/__docusaurus/debug/routes-13e": { - "__comp": "__comp---theme-debug-routes-946-699", - "__context": { - "plugin": "plugin---docs-docusaurus-debugb-38-c84" - } - }, "/docs/docs-942": { - "__comp": "__comp---theme-docs-root-5-e-9-0b6", + "__comp": "5e95c892", "__context": { - "plugin": "plugin---docs-docsaba-31e" + "plugin": "aba21aa0" } }, "/docs/docs-a90": { - "__comp": "__comp---theme-doc-version-roota-7-b-5de", - "__props": "__props---docs-docsa-20-f19" + "__comp": "a7bd4aaa", + "__props": "a2065270" }, "/docs/docs-c2e": { - "__comp": "__comp---theme-doc-roota-94-67a" + "__comp": "a94703ab" }, "/docs/docs/commands/cli/cli-init-c74": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-commands-cli-cli-init-056-2d3" + "__comp": "17896441", + "content": "0566a0a8" }, "/docs/docs/commands/cli/codex-review-937": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-commands-cli-codex-reviewf-1-b-532" + "__comp": "17896441", + "content": "f1bf82ec" }, "/docs/docs/commands/general/ccw-3fb": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-commands-general-ccwf-48-4f9" + "__comp": "17896441", + "content": "f4817052" }, "/docs/docs/commands/general/ccw-coordinator-a90": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-commands-general-ccw-coordinatord-55-a04" + "__comp": "17896441", + "content": "d550a629" }, "/docs/docs/commands/general/ccw-debug-663": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-commands-general-ccw-debug-97-c-2c8" + "__comp": "17896441", + "content": "97c6e66a" }, "/docs/docs/commands/general/ccw-plan-40b": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-commands-general-ccw-plan-04-d-aa3" + "__comp": "17896441", + "content": "04db0a2e" }, "/docs/docs/commands/general/ccw-test-99d": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-commands-general-ccw-testcce-a5d" + "__comp": "17896441", + "content": "ccef5d0f" }, "/docs/docs/commands/general/codex-coordinator-996": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-commands-general-codex-coordinatorf-92-965" + "__comp": "17896441", + "content": "f9222419" }, "/docs/docs/commands/general/flow-create-d91": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-commands-general-flow-createfab-5a7" + "__comp": "17896441", + "content": "fabaf1c8" }, "/docs/docs/commands/issue/issue-convert-to-plan-d90": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-commands-issue-issue-convert-to-plan-5-c-7-428" + "__comp": "17896441", + "content": "5c7b2278" }, "/docs/docs/commands/issue/issue-discover-2a1": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-commands-issue-issue-discover-1-e-3-61d" + "__comp": "17896441", + "content": "1e3006f3" }, "/docs/docs/commands/issue/issue-execute-abb": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-commands-issue-issue-executefe-8-121" + "__comp": "17896441", + "content": "fe8e3dcf" }, "/docs/docs/commands/issue/issue-from-brainstorm-72b": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-commands-issue-issue-from-brainstorm-2-ec-ca5" + "__comp": "17896441", + "content": "2ecf8b4a" }, "/docs/docs/commands/issue/issue-new-c58": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-commands-issue-issue-new-4-ad-afb" + "__comp": "17896441", + "content": "4ad7db0f" }, "/docs/docs/commands/issue/issue-plan-fd2": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-commands-issue-issue-plana-6-c-1b0" + "__comp": "17896441", + "content": "a6c3df16" }, "/docs/docs/commands/issue/issue-queue-1ce": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-commands-issue-issue-queue-1-ba-06f" + "__comp": "17896441", + "content": "1bac9067" }, "/docs/docs/commands/memory/memory-compact-74c": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-commands-memory-memory-compact-7-a-1-f85" + "__comp": "17896441", + "content": "7a1ee27c" }, "/docs/docs/commands/memory/memory-docs-full-cli-7a4": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-commands-memory-memory-docs-full-cli-4-cc-b15" + "__comp": "17896441", + "content": "4cc74730" }, "/docs/docs/commands/memory/memory-docs-related-cli-fb4": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-commands-memory-memory-docs-related-cli-60-e-28a" + "__comp": "17896441", + "content": "60eef997" }, "/docs/docs/commands/memory/memory-load-c66": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-commands-memory-memory-load-157-920" + "__comp": "17896441", + "content": "157db180" }, "/docs/docs/commands/memory/memory-update-full-b80": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-commands-memory-memory-update-full-666-28e" + "__comp": "17896441", + "content": "666bb1bf" }, "/docs/docs/commands/memory/memory-update-related-f0d": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-commands-memory-memory-update-related-611-f0a" + "__comp": "17896441", + "content": "611877e1" }, "/docs/docs/faq-4b2": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-faqea-3-29f" + "__comp": "17896441", + "content": "ea313555" }, "/docs/docs/overview-7df": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-overview-188-8fe" + "__comp": "17896441", + "content": "18891827" }, "/docs/docs/workflows/faq-f47": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-workflows-faqbcf-a47" + "__comp": "17896441", + "content": "bcf6b37c" }, "/docs/docs/workflows/introduction-4cb": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-workflows-introduction-9-f-4-dba" + "__comp": "17896441", + "content": "9f4ca91e" }, "/docs/docs/workflows/level-1-ultra-lightweight-5c4": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-workflows-level-1-ultra-lightweightc-5-a-692" + "__comp": "17896441", + "content": "c5a82d8d" }, "/docs/docs/workflows/level-2-rapid-ad8": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-workflows-level-2-rapid-19-b-a2f" + "__comp": "17896441", + "content": "19b64556" }, "/docs/docs/workflows/level-3-standard-3ea": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-workflows-level-3-standardbdb-19a" + "__comp": "17896441", + "content": "bdb2b105" }, "/docs/docs/workflows/level-4-brainstorm-f4f": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-workflows-level-4-brainstormd-04-69a" + "__comp": "17896441", + "content": "d045285b" }, "/docs/docs/workflows/level-5-intelligent-84a": { - "__comp": "__comp---theme-doc-item-178-a40", - "content": "content---docs-docs-workflows-level-5-intelligent-186-435" + "__comp": "17896441", + "content": "186dcf4e" } } \ No newline at end of file diff --git a/ccw/docs-site/.docusaurus/site-metadata.json b/ccw/docs-site/.docusaurus/site-metadata.json index 085f2863..503133f5 100644 --- a/ccw/docs-site/.docusaurus/site-metadata.json +++ b/ccw/docs-site/.docusaurus/site-metadata.json @@ -12,9 +12,9 @@ "name": "@docusaurus/plugin-content-pages", "version": "3.9.2" }, - "docusaurus-plugin-debug": { + "docusaurus-plugin-sitemap": { "type": "package", - "name": "@docusaurus/plugin-debug", + "name": "@docusaurus/plugin-sitemap", "version": "3.9.2" }, "docusaurus-plugin-svgr": { diff --git a/ccw/frontend/src/components/dashboard/widgets/WorkflowTaskWidget.tsx b/ccw/frontend/src/components/dashboard/widgets/WorkflowTaskWidget.tsx index 5fab51c1..95fa6755 100644 --- a/ccw/frontend/src/components/dashboard/widgets/WorkflowTaskWidget.tsx +++ b/ccw/frontend/src/components/dashboard/widgets/WorkflowTaskWidget.tsx @@ -13,6 +13,7 @@ import { Sparkline } from '@/components/charts/Sparkline'; import { useWorkflowStatusCounts, generateMockWorkflowStatusCounts } from '@/hooks/useWorkflowStatusCounts'; import { useDashboardStats } from '@/hooks/useDashboardStats'; import { useProjectOverview } from '@/hooks/useProjectOverview'; +import { useIndexStatus } from '@/hooks/useIndex'; import { cn } from '@/lib/utils'; import { ListChecks, @@ -37,6 +38,7 @@ import { Sparkles, BarChart3, PieChart as PieChartIcon, + Database, } from 'lucide-react'; export interface WorkflowTaskWidgetProps { @@ -210,6 +212,7 @@ function WorkflowTaskWidgetComponent({ className }: WorkflowTaskWidgetProps) { const { data, isLoading } = useWorkflowStatusCounts(); const { stats, isLoading: statsLoading } = useDashboardStats({ refetchInterval: 60000 }); const { projectOverview, isLoading: projectLoading } = useProjectOverview(); + const { status: indexStatus } = useIndexStatus({ refetchInterval: 30000 }); const chartData = data || generateMockWorkflowStatusCounts(); const total = chartData.reduce((sum, item) => sum + item.count, 0); @@ -320,6 +323,35 @@ function WorkflowTaskWidgetComponent({ className }: WorkflowTaskWidgetProps) { {projectOverview?.developmentIndex?.enhancement?.length || 0} {formatMessage({ id: 'projectOverview.devIndex.category.enhancements' })} + + {/* Index Status Indicator */} +
+
+ + {indexStatus?.status === 'building' && ( + + + + + )} +
+ + {indexStatus?.totalFiles || 0} + + {formatMessage({ id: 'home.indexStatus.label' })} +
{/* Date + Expand Button */} diff --git a/ccw/frontend/src/components/layout/Sidebar.tsx b/ccw/frontend/src/components/layout/Sidebar.tsx index b3b3f2c9..3a5bb138 100644 --- a/ccw/frontend/src/components/layout/Sidebar.tsx +++ b/ccw/frontend/src/components/layout/Sidebar.tsx @@ -1,7 +1,7 @@ // ======================================== // Sidebar Component // ======================================== -// Collapsible navigation sidebar with 6-group accordion structure +// Collapsible navigation sidebar with 5-group accordion structure import { useCallback, useMemo } from 'react'; import { useIntl } from 'react-intl'; @@ -9,7 +9,6 @@ import { Home, FolderKanban, Workflow, - RefreshCw, AlertCircle, Sparkles, Terminal, @@ -60,7 +59,7 @@ interface NavGroupDef { }>; } -// Define the 6 navigation groups with their items +// Define the 5 navigation groups with their items const navGroupDefinitions: NavGroupDef[] = [ { id: 'overview', @@ -80,8 +79,8 @@ const navGroupDefinitions: NavGroupDef[] = [ { path: '/lite-tasks', labelKey: 'navigation.main.liteTasks', icon: Zap }, { path: '/orchestrator', labelKey: 'navigation.main.orchestrator', icon: Workflow }, { path: '/coordinator', labelKey: 'navigation.main.coordinator', icon: GitFork }, - { path: '/loops', labelKey: 'navigation.main.loops', icon: RefreshCw }, { path: '/history', labelKey: 'navigation.main.history', icon: Clock }, + { path: '/issues', labelKey: 'navigation.main.issues', icon: AlertCircle }, ], }, { @@ -93,14 +92,7 @@ const navGroupDefinitions: NavGroupDef[] = [ { path: '/prompts', labelKey: 'navigation.main.prompts', icon: History }, { path: '/skills', labelKey: 'navigation.main.skills', icon: Sparkles }, { path: '/commands', labelKey: 'navigation.main.commands', icon: Terminal }, - ], - }, - { - id: 'issues', - titleKey: 'navigation.groups.issues', - icon: AlertCircle, - items: [ - { path: '/issues', labelKey: 'navigation.main.issues', icon: AlertCircle }, + { path: '/settings/rules', labelKey: 'navigation.main.rules', icon: Shield }, ], }, { @@ -109,6 +101,7 @@ const navGroupDefinitions: NavGroupDef[] = [ icon: Wrench, items: [ { path: '/hooks', labelKey: 'navigation.main.hooks', icon: GitFork }, + { path: '/settings/mcp', labelKey: 'navigation.main.mcp', icon: Server }, ], }, { @@ -116,11 +109,9 @@ const navGroupDefinitions: NavGroupDef[] = [ titleKey: 'navigation.groups.configuration', icon: Cog, items: [ - { path: '/settings', labelKey: 'navigation.main.settings', icon: Settings }, - { path: '/settings/mcp', labelKey: 'navigation.main.mcp', icon: Server }, - { path: '/settings/rules', labelKey: 'navigation.main.rules', icon: Shield }, { path: '/settings/codexlens', labelKey: 'navigation.main.codexlens', icon: Sparkles }, { path: '/api-settings', labelKey: 'navigation.main.apiSettings', icon: Server }, + { path: '/settings', labelKey: 'navigation.main.settings', icon: Settings }, { path: '/help', labelKey: 'navigation.main.help', icon: HelpCircle }, ], }, diff --git a/ccw/frontend/src/components/mcp/CcwToolsMcpCard.tsx b/ccw/frontend/src/components/mcp/CcwToolsMcpCard.tsx index 93f42213..048d01d2 100644 --- a/ccw/frontend/src/components/mcp/CcwToolsMcpCard.tsx +++ b/ccw/frontend/src/components/mcp/CcwToolsMcpCard.tsx @@ -15,6 +15,9 @@ import { Database, FileText, HardDrive, + MessageCircleQuestion, + ChevronDown, + ChevronRight, } from 'lucide-react'; import { Card } from '@/components/ui/Card'; import { Button } from '@/components/ui/Button'; @@ -83,6 +86,7 @@ export const CCW_MCP_TOOLS: CcwTool[] = [ { name: 'edit_file', desc: 'Edit/replace content', core: true }, { name: 'read_file', desc: 'Read file contents', core: true }, { name: 'core_memory', desc: 'Core memory management', core: true }, + { name: 'ask_question', desc: 'Interactive questions (A2UI)', core: false }, ]; // ========== Component ========== @@ -211,13 +215,9 @@ export function CcwToolsMcpCard({
{isExpanded ? ( -
- ▼ -
+ ) : ( -
- ▶ -
+ )}
@@ -425,6 +425,8 @@ function getToolIcon(toolName: string): React.ReactElement { return ; case 'core_memory': return ; + case 'ask_question': + return ; default: return ; } diff --git a/ccw/frontend/src/components/mcp/ConfigTypeToggle.tsx b/ccw/frontend/src/components/mcp/ConfigTypeToggle.tsx index b9ca5ebe..1f64abf5 100644 --- a/ccw/frontend/src/components/mcp/ConfigTypeToggle.tsx +++ b/ccw/frontend/src/components/mcp/ConfigTypeToggle.tsx @@ -162,56 +162,32 @@ export function ConfigTypeToggle({ return ( <> -
- {/* Label */} -
- - {formatMessage({ id: 'mcp.configType.label' })} - - - {getConfigFileExtension(internalType)} - -
- - {/* Toggle Buttons */} -
- - -
- - {/* Current Format Display */} -
- - {getConfigFileExtension(internalType)} - - - {formatMessage({ id: 'mcp.configType.' + internalType.replace('-', '') })} - -
+ {/* Compact inline toggle */} +
+ +
{/* Warning Dialog */} diff --git a/ccw/frontend/src/components/mcp/McpServerDialog.tsx b/ccw/frontend/src/components/mcp/McpServerDialog.tsx index 7eb54cbc..aea3aaa5 100644 --- a/ccw/frontend/src/components/mcp/McpServerDialog.tsx +++ b/ccw/frontend/src/components/mcp/McpServerDialog.tsx @@ -31,6 +31,7 @@ import { } from '@/lib/api'; import { mcpServersKeys, useMcpTemplates } from '@/hooks'; import { cn } from '@/lib/utils'; +import { ConfigTypeToggle, type McpConfigType } from './ConfigTypeToggle'; // ========== Types ========== @@ -90,6 +91,7 @@ export function McpServerDialog({ const [errors, setErrors] = useState({}); const [argsInput, setArgsInput] = useState(''); const [envInput, setEnvInput] = useState(''); + const [configType, setConfigType] = useState('mcp-json'); // Initialize form from server prop (edit mode) useEffect(() => { @@ -458,6 +460,20 @@ export function McpServerDialog({
+ + {/* Config Type Toggle - Only for project scope */} + {formData.scope === 'project' && ( +
+ + {formatMessage({ id: 'mcp.configType.format' })}: + + +
+ )} {/* Enabled */} diff --git a/ccw/frontend/src/components/mcp/RecommendedMcpSection.tsx b/ccw/frontend/src/components/mcp/RecommendedMcpSection.tsx index f51d079e..0da00293 100644 --- a/ccw/frontend/src/components/mcp/RecommendedMcpSection.tsx +++ b/ccw/frontend/src/components/mcp/RecommendedMcpSection.tsx @@ -1,52 +1,30 @@ // ======================================== // Recommended MCP Section Component // ======================================== -// Display recommended MCP servers with one-click install functionality +// Display recommended MCP servers with wizard-based install functionality -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { useIntl } from 'react-intl'; -import { useMutation, useQueryClient } from '@tanstack/react-query'; import { Search, Globe, Sparkles, Download, Check, - Loader2, + Settings, + Key, + Zap, + Code2, } from 'lucide-react'; import { Card } from '@/components/ui/Card'; import { Button } from '@/components/ui/Button'; import { Badge } from '@/components/ui/Badge'; -import { - Dialog, - DialogContent, - DialogHeader, - DialogTitle, - DialogFooter, -} from '@/components/ui/Dialog'; -import { - createMcpServer, - fetchMcpServers, -} from '@/lib/api'; -import { mcpServersKeys } from '@/hooks'; -import { useNotifications } from '@/hooks/useNotifications'; +import { RecommendedMcpWizard, RecommendedMcpDefinition } from './RecommendedMcpWizard'; +import { fetchMcpConfig } from '@/lib/api'; import { cn } from '@/lib/utils'; // ========== Types ========== -/** - * Recommended server configuration - */ -export interface RecommendedServer { - id: string; - name: string; - description: string; - command: string; - args: string[]; - icon: React.ComponentType<{ className?: string }>; - category: 'search' | 'browser' | 'ai'; -} - /** * Props for RecommendedMcpSection component */ @@ -55,61 +33,135 @@ export interface RecommendedMcpSectionProps { onInstallComplete?: () => void; } -interface RecommendedServerCardProps { - server: RecommendedServer; - isInstalled: boolean; - isInstalling: boolean; - onInstall: (server: RecommendedServer) => void; -} - -// ========== Constants ========== +// ========== Platform Detection ========== +const isWindows = typeof navigator !== 'undefined' && navigator.platform?.toLowerCase().includes('win'); /** - * Pre-configured recommended MCP servers + * Build cross-platform MCP config + * On Windows, wraps npx/node/python commands with cmd /c for proper execution */ -const RECOMMENDED_SERVERS: RecommendedServer[] = [ +function buildCrossPlatformMcpConfig( + command: string, + args: string[] = [], + options: { env?: Record; type?: string } = {} +) { + const { env, type } = options; + + const windowsWrappedCommands = ['npx', 'npm', 'node', 'python', 'python3', 'pip', 'pip3', 'pnpm', 'yarn', 'bun']; + const needsWindowsWrapper = isWindows && windowsWrappedCommands.includes(command.toLowerCase()); + + const config: { command: string; args: string[]; env?: Record; type?: string } = needsWindowsWrapper + ? { command: 'cmd', args: ['/c', command, ...args] } + : { command, args }; + + if (type) config.type = type; + if (env && Object.keys(env).length > 0) config.env = env; + + return config; +} + +// ========== Recommended MCP Definitions ========== + +/** + * Pre-configured recommended MCP servers with field definitions + * Matches original JS version structure for full wizard support + */ +const RECOMMENDED_MCP_DEFINITIONS: RecommendedMcpDefinition[] = [ { id: 'ace-tool', - name: 'ACE Tool', - description: 'Advanced code search and context engine for intelligent code discovery', - command: 'mcp__ace-tool__search_context', - args: [], - icon: Search, + nameKey: 'mcp.ace-tool.name', + descKey: 'mcp.ace-tool.desc', + icon: 'search-code', category: 'search', + fields: [ + { + key: 'baseUrl', + labelKey: 'mcp.ace-tool.field.baseUrl', + type: 'text', + default: 'https://acemcp.heroman.wtf/relay/', + placeholder: 'https://acemcp.heroman.wtf/relay/', + required: true, + descKey: 'mcp.ace-tool.field.baseUrl.desc', + }, + { + key: 'token', + labelKey: 'mcp.ace-tool.field.token', + type: 'password', + default: '', + placeholder: 'ace_xxxxxxxxxxxxxxxx', + required: true, + descKey: 'mcp.ace-tool.field.token.desc', + }, + ], + buildConfig: (values) => buildCrossPlatformMcpConfig('npx', [ + 'ace-tool', + '--base-url', + values.baseUrl || 'https://acemcp.heroman.wtf/relay/', + '--token', + values.token, + ]), }, { id: 'chrome-devtools', - name: 'Chrome DevTools', - description: 'Browser automation and debugging tools for web development', - command: 'mcp__chrome-devtools', - args: [], - icon: Globe, + nameKey: 'mcp.chrome-devtools.name', + descKey: 'mcp.chrome-devtools.desc', + icon: 'chrome', category: 'browser', + fields: [], + buildConfig: () => buildCrossPlatformMcpConfig('npx', ['chrome-devtools-mcp@latest'], { type: 'stdio' }), }, { - id: 'exa-search', - name: 'Exa Search', - description: 'AI-powered web search with real-time crawling capabilities', - command: 'mcp__exa__search', - args: [], - icon: Sparkles, - category: 'ai', + id: 'exa', + nameKey: 'mcp.exa.name', + descKey: 'mcp.exa.desc', + icon: 'globe-2', + category: 'search', + fields: [ + { + key: 'apiKey', + labelKey: 'mcp.exa.field.apiKey', + type: 'password', + default: '', + placeholder: 'your-exa-api-key', + required: false, + descKey: 'mcp.exa.field.apiKey.desc', + }, + ], + buildConfig: (values) => { + const env = values.apiKey ? { EXA_API_KEY: values.apiKey } : undefined; + return buildCrossPlatformMcpConfig('npx', ['-y', 'exa-mcp-server'], { env }); + }, }, ]; +// ========== Icon Map ========== + +const ICON_MAP: Record> = { + 'search-code': Search, + 'chrome': Globe, + 'globe-2': Sparkles, + 'code-2': Code2, +}; + // ========== Helper Component ========== +interface RecommendedServerCardProps { + definition: RecommendedMcpDefinition; + isInstalled: boolean; + onInstall: (definition: RecommendedMcpDefinition) => void; +} + /** * Individual recommended server card */ function RecommendedServerCard({ - server, + definition, isInstalled, - isInstalling, onInstall, }: RecommendedServerCardProps) { const { formatMessage } = useIntl(); - const Icon = server.icon; + const Icon = ICON_MAP[definition.icon] || Settings; + const hasFields = definition.fields.length > 0; return ( @@ -129,7 +181,7 @@ function RecommendedServerCard({

- {server.name} + {formatMessage({ id: definition.nameKey })}

{isInstalled && ( @@ -138,39 +190,40 @@ function RecommendedServerCard({ )}

- {server.description} + {formatMessage({ id: definition.descKey })}

- {/* Install Button */} - {!isInstalled && ( + {/* Config info + Install */} +
+ {hasFields ? ( + + + {definition.fields.length} {formatMessage({ id: 'mcp.configRequired' })} + + ) : ( + + + {formatMessage({ id: 'mcp.noConfigNeeded' })} + + )} - )} - - {/* Installed Indicator */} - {isInstalled && ( -
- - {formatMessage({ id: 'mcp.recommended.actions.installed' })} -
- )} +
@@ -180,88 +233,64 @@ function RecommendedServerCard({ // ========== Main Component ========== /** - * Recommended MCP servers section with one-click install + * Recommended MCP servers section with wizard-based install */ export function RecommendedMcpSection({ onInstallComplete, }: RecommendedMcpSectionProps) { const { formatMessage } = useIntl(); - const queryClient = useQueryClient(); - const { success, error } = useNotifications(); - const [confirmDialogOpen, setConfirmDialogOpen] = useState(false); - const [selectedServer, setSelectedServer] = useState(null); - const [installingServerId, setInstallingServerId] = useState(null); + const [wizardOpen, setWizardOpen] = useState(false); + const [selectedDefinition, setSelectedDefinition] = useState(null); const [installedServerIds, setInstalledServerIds] = useState>(new Set()); // Check which servers are already installed const checkInstalledServers = async () => { try { - const data = await fetchMcpServers(); - const allServers = [...data.project, ...data.global]; - const installedIds = new Set( - allServers - .filter(s => s.command.startsWith('mcp__')) - .map(s => s.command) - ); + const data = await fetchMcpConfig(); + const installedIds = new Set(); + + const globalServers = data.globalServers || {}; + const userServers = data.userServers || {}; + for (const name of Object.keys(globalServers)) installedIds.add(name); + for (const name of Object.keys(userServers)) installedIds.add(name); + + const projects = data.projects || {}; + for (const proj of Object.values(projects)) { + const servers = (proj as any).mcpServers || {}; + for (const name of Object.keys(servers)) installedIds.add(name); + } + + if ((data as any).codex?.servers) { + for (const name of Object.keys((data as any).codex.servers)) installedIds.add(name); + } + setInstalledServerIds(installedIds); } catch { // Ignore errors during check } }; - // Check on mount - useState(() => { + useEffect(() => { checkInstalledServers(); - }); + }, []); - // Create server mutation - const createMutation = useMutation({ - mutationFn: (server: Omit) => - createMcpServer({ - command: server.command, - args: server.args, - scope: 'global', - enabled: true, - }), - onSuccess: (_, variables) => { - queryClient.invalidateQueries({ queryKey: mcpServersKeys.all }); - setInstalledServerIds(prev => new Set(prev).add(variables.command)); - setInstallingServerId(null); - setConfirmDialogOpen(false); - setSelectedServer(null); - success( - formatMessage({ id: 'mcp.recommended.actions.installed' }), - formatMessage({ id: 'mcp.recommended.servers.' + selectedServer?.id + '.name' }) - ); - onInstallComplete?.(); - }, - onError: () => { - setInstallingServerId(null); - error( - formatMessage({ id: 'mcp.dialog.validation.nameRequired' }), - formatMessage({ id: 'mcp.dialog.validation.commandRequired' }) - ); - }, - }); - - // Handle install click - const handleInstallClick = (server: RecommendedServer) => { - setSelectedServer(server); - setConfirmDialogOpen(true); + // Handle install click - open wizard + const handleInstallClick = (definition: RecommendedMcpDefinition) => { + setSelectedDefinition(definition); + setWizardOpen(true); }; - // Handle confirm install - const handleConfirmInstall = () => { - if (!selectedServer) return; - setInstallingServerId(selectedServer.id); - setConfirmDialogOpen(false); - createMutation.mutate(selectedServer); + // Handle wizard close + const handleWizardClose = () => { + setWizardOpen(false); + setSelectedDefinition(null); }; - // Check if server is installed - const isServerInstalled = (server: RecommendedServer) => { - return installedServerIds.has(server.command); + // Handle install complete + const handleInstallComplete = () => { + checkInstalledServers(); + onInstallComplete?.(); }; return ( @@ -279,66 +308,24 @@ export function RecommendedMcpSection({ {/* Server Cards */}
- {RECOMMENDED_SERVERS.map((server) => ( + {RECOMMENDED_MCP_DEFINITIONS.map((definition) => ( ))}
- {/* Confirmation Dialog */} - - - - - {formatMessage({ id: 'mcp.recommended.actions.install' })} {selectedServer?.name} - - -
-

- {formatMessage( - { id: 'mcp.recommended.description' }, - { server: selectedServer?.name } - )} -

-
- - {selectedServer?.command} {selectedServer?.args.join(' ')} - -
-
- - - - -
-
+ {/* Wizard Dialog */} + ); } diff --git a/ccw/frontend/src/components/mcp/RecommendedMcpWizard.tsx b/ccw/frontend/src/components/mcp/RecommendedMcpWizard.tsx new file mode 100644 index 00000000..97ed51db --- /dev/null +++ b/ccw/frontend/src/components/mcp/RecommendedMcpWizard.tsx @@ -0,0 +1,363 @@ +// ======================================== +// Recommended MCP Wizard Component +// ======================================== +// Dynamic configuration wizard for recommended MCP servers +// Supports text, password, and multi-select field types + +import { useState } from 'react'; +import { useIntl } from 'react-intl'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { Download, Loader2, X } from 'lucide-react'; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogFooter, +} from '@/components/ui/Dialog'; +import { Button } from '@/components/ui/Button'; +import { Input } from '@/components/ui/Input'; +import { Label } from '@/components/ui/Label'; +import { Badge } from '@/components/ui/Badge'; +import { + addGlobalMcpServer, + addProjectMcpServer, +} from '@/lib/api'; +import { mcpServersKeys } from '@/hooks'; +import { useNotifications } from '@/hooks/useNotifications'; +import { cn } from '@/lib/utils'; + +// ========== Types ========== + +/** + * Field definition for wizard + */ +export interface WizardField { + key: string; + labelKey: string; + descKey?: string; + type: 'text' | 'password' | 'multi-select'; + default?: string | string[]; + placeholder?: string; + required?: boolean; + options?: Array<{ + value: string; + label: string; + desc?: string; + }>; +} + +/** + * Recommended MCP server definition + */ +export interface RecommendedMcpDefinition { + id: string; + nameKey: string; + descKey: string; + icon: string; + category: string; + fields: WizardField[]; + buildConfig: (values: Record) => { + command: string; + args: string[]; + env?: Record; + type?: string; + }; +} + +/** + * Props for RecommendedMcpWizard component + */ +export interface RecommendedMcpWizardProps { + open: boolean; + onClose: () => void; + mcpDefinition: RecommendedMcpDefinition | null; + onInstallComplete?: () => void; +} + +// ========== Main Component ========== + +/** + * Wizard for installing recommended MCP servers with configuration + */ +export function RecommendedMcpWizard({ + open, + onClose, + mcpDefinition, + onInstallComplete, +}: RecommendedMcpWizardProps) { + const { formatMessage } = useIntl(); + const queryClient = useQueryClient(); + const { success: showSuccess, error: showError } = useNotifications(); + + // State for field values + const [fieldValues, setFieldValues] = useState>({}); + const [selectedScope, setSelectedScope] = useState<'project' | 'global'>('global'); + + // Initialize field values when dialog opens + const initializeFieldValues = () => { + if (!mcpDefinition) return; + + const initialValues: Record = {}; + for (const field of mcpDefinition.fields) { + if (field.default !== undefined) { + initialValues[field.key] = field.default; + } else if (field.type === 'multi-select') { + initialValues[field.key] = []; + } else { + initialValues[field.key] = ''; + } + } + setFieldValues(initialValues); + }; + + // Reset on open/close + const handleOpenChange = (newOpen: boolean) => { + if (newOpen && mcpDefinition) { + initializeFieldValues(); + } else { + setFieldValues({}); + onClose(); + } + }; + + // Install mutation + const installMutation = useMutation({ + mutationFn: async () => { + if (!mcpDefinition) throw new Error('No MCP definition'); + + const serverConfig = mcpDefinition.buildConfig(fieldValues); + + if (selectedScope === 'global') { + return addGlobalMcpServer(mcpDefinition.id, serverConfig); + } else { + return addProjectMcpServer(mcpDefinition.id, serverConfig); + } + }, + onSuccess: (result) => { + if (result.success) { + queryClient.invalidateQueries({ queryKey: mcpServersKeys.all }); + showSuccess( + formatMessage({ id: 'mcp.wizard.installSuccess' }), + formatMessage({ id: mcpDefinition!.nameKey }) + ); + handleOpenChange(false); + onInstallComplete?.(); + } else { + showError( + formatMessage({ id: 'mcp.wizard.installError' }), + result.error || 'Unknown error' + ); + } + }, + onError: (err: Error) => { + showError( + formatMessage({ id: 'mcp.wizard.installError' }), + err.message + ); + }, + }); + + // Handle field value change + const handleFieldChange = (key: string, value: any) => { + setFieldValues(prev => ({ + ...prev, + [key]: value, + })); + }; + + // Handle multi-select toggle + const handleMultiSelectToggle = (key: string, value: string) => { + const current = fieldValues[key] || []; + const newValue = current.includes(value) + ? current.filter((v: string) => v !== value) + : [...current, value]; + handleFieldChange(key, newValue); + }; + + // Validate required fields + const validateFields = (): boolean => { + if (!mcpDefinition) return false; + + for (const field of mcpDefinition.fields) { + if (field.required) { + const value = fieldValues[field.key]; + if (field.type === 'multi-select') { + if (!value || value.length === 0) return false; + } else { + if (!value || value.trim() === '') return false; + } + } + } + return true; + }; + + // Handle submit + const handleSubmit = () => { + if (!validateFields()) { + showError( + formatMessage({ id: 'mcp.wizard.validation' }), + formatMessage({ id: 'mcp.wizard.requiredFields' }) + ); + return; + } + installMutation.mutate(); + }; + + if (!mcpDefinition) return null; + + const hasFields = mcpDefinition.fields.length > 0; + + return ( + + + {/* Header */} + +
+
+ +
+
+ + {formatMessage({ id: 'mcp.wizard.install' })} {formatMessage({ id: mcpDefinition.nameKey })} + +

+ {formatMessage({ id: mcpDefinition.descKey })} +

+
+
+
+ + {/* Content */} +
+ {/* Fields */} + {hasFields && ( +
+ {mcpDefinition.fields.map((field) => ( +
+ + {field.descKey && ( +

+ {formatMessage({ id: field.descKey })} +

+ )} + + {/* Text/Password Input */} + {(field.type === 'text' || field.type === 'password') && ( + handleFieldChange(field.key, e.target.value)} + placeholder={field.placeholder} + className="font-mono text-sm" + /> + )} + + {/* Multi-Select */} + {field.type === 'multi-select' && field.options && ( +
+ {field.options.map((option) => { + const isSelected = (fieldValues[field.key] || []).includes(option.value); + return ( +
handleMultiSelectToggle(field.key, option.value)} + > + {}} + className="mt-0.5 w-4 h-4" + /> +
+
+ {option.label} +
+ {option.desc && ( +
+ {option.desc} +
+ )} +
+
+ ); + })} +
+ )} +
+ ))} +
+ )} + + {/* Scope Selection */} +
+ +
+ + +
+
+ + {/* No Configuration Needed Message */} + {!hasFields && ( +
+ {formatMessage({ id: 'mcp.noConfigNeeded' })} +
+ )} +
+ + {/* Footer */} + + + + +
+
+ ); +} + +export default RecommendedMcpWizard; diff --git a/ccw/frontend/src/lib/api.ts b/ccw/frontend/src/lib/api.ts index 3c68f05c..e8da84b8 100644 --- a/ccw/frontend/src/lib/api.ts +++ b/ccw/frontend/src/lib/api.ts @@ -2015,6 +2015,21 @@ export interface McpServersResponse { global: McpServer[]; } +/** + * Fetch complete MCP configuration from all sources + * Returns raw config including projects, globalServers, userServers, enterpriseServers + */ +export async function fetchMcpConfig(): Promise<{ + projects: Record; disabledMcpServers?: string[] }>; + globalServers: Record; + userServers: Record; + enterpriseServers: Record; + configSources: string[]; + codex?: { servers: Record; configPath: string }; +}> { + return fetchApi('/api/mcp-config'); +} + /** * Fetch all MCP servers (project and global scope) for a specific workspace * @param projectPath - Optional project path to filter data by workspace @@ -2551,6 +2566,47 @@ export async function deleteRule( }); } +/** + * Add MCP server to global scope (~/.claude.json mcpServers) + */ +export async function addGlobalMcpServer( + serverName: string, + serverConfig: { + command: string; + args?: string[]; + env?: Record; + type?: string; + } +): Promise<{ success: boolean; error?: string }> { + return fetchApi<{ success: boolean; error?: string }>('/api/mcp-add-global-server', { + method: 'POST', + body: JSON.stringify({ serverName, serverConfig }), + }); +} + +/** + * Copy/Add MCP server to project (.mcp.json or .claude.json) + */ +export async function copyMcpServerToProject( + serverName: string, + serverConfig: { + command: string; + args?: string[]; + env?: Record; + type?: string; + }, + projectPath?: string, + configType: 'mcp' | 'claude' = 'mcp' +): Promise<{ success: boolean; error?: string }> { + // Use current project path from URL or fallback + const path = projectPath || window.location.pathname.split('/').filter(Boolean)[0] || ''; + + return fetchApi<{ success: boolean; error?: string }>('/api/mcp-copy-server', { + method: 'POST', + body: JSON.stringify({ projectPath: path, serverName, serverConfig, configType }), + }); +} + // ========== CCW Tools MCP API ========== /** @@ -2565,15 +2621,111 @@ export interface CcwMcpConfig { } /** - * Fetch CCW Tools MCP configuration + * Platform detection for cross-platform MCP config */ -export async function fetchCcwMcpConfig(): Promise { - const data = await fetchApi('/api/mcp/ccw-config'); - return data; +const isWindows = typeof navigator !== 'undefined' && navigator.platform?.toLowerCase().includes('win'); + +/** + * Build CCW MCP server config + */ +function buildCcwMcpServerConfig(config: { + enabledTools?: string[]; + projectRoot?: string; + allowedDirs?: string; + disableSandbox?: boolean; +}): { command: string; args: string[]; env: Record } { + const env: Record = {}; + + if (config.enabledTools && config.enabledTools.length > 0) { + env.CCW_ENABLED_TOOLS = config.enabledTools.join(','); + } else { + env.CCW_ENABLED_TOOLS = 'all'; + } + + if (config.projectRoot) { + env.CCW_PROJECT_ROOT = config.projectRoot; + } + if (config.allowedDirs) { + env.CCW_ALLOWED_DIRS = config.allowedDirs; + } + if (config.disableSandbox) { + env.CCW_DISABLE_SANDBOX = '1'; + } + + // Cross-platform config + if (isWindows) { + return { + command: 'cmd', + args: ['/c', 'npx', '-y', 'ccw-mcp'], + env + }; + } + return { + command: 'npx', + args: ['-y', 'ccw-mcp'], + env + }; } /** - * Update CCW Tools MCP configuration + * Fetch CCW Tools MCP configuration by checking if ccw-tools server exists + */ +export async function fetchCcwMcpConfig(): Promise { + try { + const config = await fetchMcpConfig(); + + // Check if ccw-tools server exists in any config + let ccwServer: any = null; + + // Check global servers + if (config.globalServers?.['ccw-tools']) { + ccwServer = config.globalServers['ccw-tools']; + } + // Check user servers + if (!ccwServer && config.userServers?.['ccw-tools']) { + ccwServer = config.userServers['ccw-tools']; + } + // Check project servers + if (!ccwServer && config.projects) { + for (const proj of Object.values(config.projects)) { + if (proj.mcpServers?.['ccw-tools']) { + ccwServer = proj.mcpServers['ccw-tools']; + break; + } + } + } + + if (!ccwServer) { + return { + isInstalled: false, + enabledTools: [], + }; + } + + // Parse enabled tools from env + const env = ccwServer.env || {}; + const enabledToolsStr = env.CCW_ENABLED_TOOLS || 'all'; + const enabledTools = enabledToolsStr === 'all' + ? ['write_file', 'edit_file', 'read_file', 'core_memory', 'ask_question'] + : enabledToolsStr.split(',').map((t: string) => t.trim()); + + return { + isInstalled: true, + enabledTools, + projectRoot: env.CCW_PROJECT_ROOT, + allowedDirs: env.CCW_ALLOWED_DIRS, + disableSandbox: env.CCW_DISABLE_SANDBOX === '1', + }; + } catch { + return { + isInstalled: false, + enabledTools: [], + }; + } +} + +/** + * Update CCW Tools MCP configuration (re-install with new config) */ export async function updateCcwConfig(config: { enabledTools?: string[]; @@ -2581,27 +2733,40 @@ export async function updateCcwConfig(config: { allowedDirs?: string; disableSandbox?: boolean; }): Promise { - return fetchApi('/api/mcp/ccw-config', { - method: 'PATCH', - body: JSON.stringify(config), - }); + const serverConfig = buildCcwMcpServerConfig(config); + + // Install/update to global config + const result = await addGlobalMcpServer('ccw-tools', serverConfig); + if (!result.success) { + throw new Error(result.error || 'Failed to update CCW config'); + } + + return fetchCcwMcpConfig(); } /** * Install CCW Tools MCP server */ export async function installCcwMcp(): Promise { - return fetchApi('/api/mcp/ccw-install', { - method: 'POST', + const serverConfig = buildCcwMcpServerConfig({ + enabledTools: ['write_file', 'edit_file', 'read_file', 'core_memory', 'ask_question'], }); + + const result = await addGlobalMcpServer('ccw-tools', serverConfig); + if (!result.success) { + throw new Error(result.error || 'Failed to install CCW MCP'); + } + + return fetchCcwMcpConfig(); } /** * Uninstall CCW Tools MCP server */ export async function uninstallCcwMcp(): Promise { - await fetchApi('/api/mcp/ccw-uninstall', { + await fetchApi<{ success: boolean }>('/api/mcp-remove-global-server', { method: 'POST', + body: JSON.stringify({ serverName: 'ccw-tools' }), }); } diff --git a/ccw/frontend/src/locales/en/cli-hooks.json b/ccw/frontend/src/locales/en/cli-hooks.json index 9bf9e27c..84ce94d1 100644 --- a/ccw/frontend/src/locales/en/cli-hooks.json +++ b/ccw/frontend/src/locales/en/cli-hooks.json @@ -58,6 +58,9 @@ "description": "Create your first hook to automate your CLI workflow", "noHooksInEvent": "No hooks configured for this event" }, + "quickTemplates": { + "title": "Quick Install Templates" + }, "templates": { "title": "Quick Install Templates", "description": "One-click installation for common hook patterns", diff --git a/ccw/frontend/src/locales/en/home.json b/ccw/frontend/src/locales/en/home.json index 5ee15f48..2015b173 100644 --- a/ccw/frontend/src/locales/en/home.json +++ b/ccw/frontend/src/locales/en/home.json @@ -14,6 +14,9 @@ "runningLoops": "Running Loops", "openIssues": "Open Issues" }, + "indexStatus": { + "label": "Indexed" + }, "sections": { "statistics": "Statistics", "recentSessions": "Recent Sessions", diff --git a/ccw/frontend/src/locales/en/mcp-manager.json b/ccw/frontend/src/locales/en/mcp-manager.json index db6c7801..f8ad31ec 100644 --- a/ccw/frontend/src/locales/en/mcp-manager.json +++ b/ccw/frontend/src/locales/en/mcp-manager.json @@ -128,6 +128,10 @@ "core_memory": { "name": "core_memory", "desc": "Manage core memory entries" + }, + "ask_question": { + "name": "ask_question", + "desc": "Ask interactive questions through A2UI interface" } }, "paths": { diff --git a/ccw/frontend/src/locales/zh/cli-hooks.json b/ccw/frontend/src/locales/zh/cli-hooks.json index 73de71aa..36d55ff8 100644 --- a/ccw/frontend/src/locales/zh/cli-hooks.json +++ b/ccw/frontend/src/locales/zh/cli-hooks.json @@ -58,6 +58,9 @@ "description": "创建您的第一个钩子以自动化 CLI 工作流", "noHooksInEvent": "此事件未配置钩子" }, + "quickTemplates": { + "title": "快速安装模板" + }, "templates": { "title": "快速安装模板", "description": "常见钩子模式的一键安装", diff --git a/ccw/frontend/src/locales/zh/home.json b/ccw/frontend/src/locales/zh/home.json index 4c885c0a..e66aeb9a 100644 --- a/ccw/frontend/src/locales/zh/home.json +++ b/ccw/frontend/src/locales/zh/home.json @@ -14,6 +14,9 @@ "runningLoops": "运行中的循环", "openIssues": "开放问题" }, + "indexStatus": { + "label": "索引" + }, "sections": { "statistics": "统计", "recentSessions": "最近会话", diff --git a/ccw/frontend/src/locales/zh/mcp-manager.json b/ccw/frontend/src/locales/zh/mcp-manager.json index f8be0054..eabf44bd 100644 --- a/ccw/frontend/src/locales/zh/mcp-manager.json +++ b/ccw/frontend/src/locales/zh/mcp-manager.json @@ -128,6 +128,10 @@ "core_memory": { "name": "core_memory", "desc": "管理核心内存条目" + }, + "ask_question": { + "name": "ask_question", + "desc": "通过 A2UI 界面发起交互式问答" } }, "paths": { diff --git a/ccw/frontend/src/pages/McpManagerPage.tsx b/ccw/frontend/src/pages/McpManagerPage.tsx index 64dde50e..389bc927 100644 --- a/ccw/frontend/src/pages/McpManagerPage.tsx +++ b/ccw/frontend/src/pages/McpManagerPage.tsx @@ -31,7 +31,6 @@ import { CodexMcpEditableCard } from '@/components/mcp/CodexMcpEditableCard'; import { CcwToolsMcpCard } from '@/components/mcp/CcwToolsMcpCard'; import { McpTemplatesSection } from '@/components/mcp/McpTemplatesSection'; import { RecommendedMcpSection } from '@/components/mcp/RecommendedMcpSection'; -import { ConfigTypeToggle } from '@/components/mcp/ConfigTypeToggle'; import { WindowsCompatibilityWarning } from '@/components/mcp/WindowsCompatibilityWarning'; import { CrossCliCopyButton } from '@/components/mcp/CrossCliCopyButton'; import { AllProjectsTable } from '@/components/mcp/AllProjectsTable'; @@ -207,7 +206,6 @@ export function McpManagerPage() { const [editingServer, setEditingServer] = useState(undefined); const [cliMode, setCliMode] = useState('claude'); const [codexExpandedServers, setCodexExpandedServers] = useState>(new Set()); - const [configType, setConfigType] = useState<'mcp-json' | 'claude-json'>('mcp-json'); const { servers, @@ -391,14 +389,24 @@ export function McpManagerPage() {
{/* Page Header */}
-
-

- - {formatMessage({ id: 'mcp.title' })} -

-

- {formatMessage({ id: 'mcp.description' })} -

+
+
+

+ + {formatMessage({ id: 'mcp.title' })} +

+

+ {formatMessage({ id: 'mcp.description' })} +

+
+ {/* CLI Mode Badge Switcher */} +
+ +
- {/* CLI Mode Toggle */} - - {/* Tabbed Interface */} +
+ {/* Recommended MCP Servers */} + refetch()} /> + + {/* Templates Section */} - {/* Recommended MCP Servers */} - {cliMode === 'claude' && ( - refetch()} /> - )} - - {/* Config Type Toggle */} - {cliMode === 'claude' && ( - - )} - {/* Stats Cards - Claude mode only */} {cliMode === 'claude' && (
diff --git a/ccw/frontend/src/pages/SettingsPage.tsx b/ccw/frontend/src/pages/SettingsPage.tsx index e086998e..34c3a13f 100644 --- a/ccw/frontend/src/pages/SettingsPage.tsx +++ b/ccw/frontend/src/pages/SettingsPage.tsx @@ -17,11 +17,7 @@ import { ChevronDown, ChevronUp, Languages, - GitFork, - Scale, - Search, - Power, - PowerOff, + Plus, } from 'lucide-react'; import { Card } from '@/components/ui/Card'; import { Button } from '@/components/ui/Button'; @@ -29,12 +25,10 @@ import { Input } from '@/components/ui/Input'; import { Badge } from '@/components/ui/Badge'; import { ThemeSelector } from '@/components/shared/ThemeSelector'; import { useTheme } from '@/hooks'; -import { useHooks, useRules, useToggleHook, useToggleRule } from '@/hooks'; import { useConfigStore, selectCliTools, selectDefaultCliTool, selectUserPreferences } from '@/stores/configStore'; import type { CliToolConfig, UserPreferences } from '@/types/store'; import { cn } from '@/lib/utils'; import { LanguageSwitcher } from '@/components/layout/LanguageSwitcher'; -import { IndexManager } from '@/components/shared/IndexManager'; // ========== CLI Tool Card Component ========== @@ -47,6 +41,9 @@ interface CliToolCardProps { onToggleEnabled: () => void; onSetDefault: () => void; onUpdateModel: (field: 'primaryModel' | 'secondaryModel', value: string) => void; + onUpdateTags: (tags: string[]) => void; + onUpdateAvailableModels: (models: string[]) => void; + onUpdateSettingsFile: (settingsFile: string | undefined) => void; } function CliToolCard({ @@ -58,9 +55,49 @@ function CliToolCard({ onToggleEnabled, onSetDefault, onUpdateModel, + onUpdateTags, + onUpdateAvailableModels, + onUpdateSettingsFile, }: CliToolCardProps) { const { formatMessage } = useIntl(); + // Local state for tag and model input + const [tagInput, setTagInput] = useState(''); + const [modelInput, setModelInput] = useState(''); + + // Handler for adding tags + const handleAddTag = () => { + const newTag = tagInput.trim(); + if (newTag && !config.tags.includes(newTag)) { + onUpdateTags([...config.tags, newTag]); + setTagInput(''); + } + }; + + // Handler for removing tags + const handleRemoveTag = (tagToRemove: string) => { + onUpdateTags(config.tags.filter((t) => t !== tagToRemove)); + }; + + // Handler for adding available models + const handleAddModel = () => { + const newModel = modelInput.trim(); + const currentModels = config.availableModels || []; + if (newModel && !currentModels.includes(newModel)) { + onUpdateAvailableModels([...currentModels, newModel]); + setModelInput(''); + } + }; + + // Handler for removing available models + const handleRemoveModel = (modelToRemove: string) => { + const currentModels = config.availableModels || []; + onUpdateAvailableModels(currentModels.filter((m) => m !== modelToRemove)); + }; + + // Predefined tags + const predefinedTags = ['分析', 'Debug', 'implementation', 'refactoring', 'testing']; + return ( {/* Header */} @@ -157,6 +194,146 @@ function CliToolCard({ />
+ + {/* Tags Section */} +
+ +

+ {formatMessage({ id: 'apiSettings.cliSettings.tagsDescription' })} +

+
+
+ {config.tags.map((tag) => ( + + {tag} + + + ))} + setTagInput(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter') { + e.preventDefault(); + handleAddTag(); + } + }} + placeholder={config.tags.length === 0 ? formatMessage({ id: 'apiSettings.cliSettings.tagInputPlaceholder' }) : ''} + className="flex-1 min-w-[120px] bg-transparent border-0 outline-none text-sm placeholder:text-muted-foreground" + /> +
+ +
+ {/* Predefined Tags */} +
+ + {formatMessage({ id: 'apiSettings.cliSettings.predefinedTags' })}: + + {predefinedTags.map((predefinedTag) => ( + + ))} +
+
+ + {/* Available Models Section */} +
+ +
+
+ {(config.availableModels || []).map((model) => ( + + {model} + + + ))} + setModelInput(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter') { + e.preventDefault(); + handleAddModel(); + } + }} + placeholder={(config.availableModels || []).length === 0 ? formatMessage({ id: 'apiSettings.cliSettings.availableModelsPlaceholder' }) : ''} + className="flex-1 min-w-[120px] bg-transparent border-0 outline-none text-sm placeholder:text-muted-foreground" + /> +
+ +
+

+ {formatMessage({ id: 'apiSettings.cliSettings.availableModelsHint' })} +

+
+ + {/* Settings File */} +
+ + onUpdateSettingsFile(e.target.value || undefined)} + placeholder={formatMessage({ id: 'apiSettings.cliSettings.settingsFilePlaceholder' })} + /> +

+ {formatMessage({ id: 'apiSettings.cliSettings.settingsFileHint' })} +

+
+ {!isDefault && config.enabled && ( -
- )) - )} -
- - ); -} - -// ========== Rules Section Component ========== - -function RulesSection() { - const { formatMessage } = useIntl(); - const [searchQuery, setSearchQuery] = useState(''); - - const { rules, enabledCount, totalCount, isLoading } = useRules(); - const { toggleRule, isToggling } = useToggleRule(); - - const filteredRules = rules.filter(r => - r.name.toLowerCase().includes(searchQuery.toLowerCase()) || - (r.description && r.description.toLowerCase().includes(searchQuery.toLowerCase())) - ); - - return ( - -
-

- - {formatMessage({ id: 'settings.sections.rules' })} -

-
- - {enabledCount}/{totalCount} {formatMessage({ id: 'cliRules.stats.enabled' })} - -
-
- -
- - setSearchQuery(e.target.value)} - className="pl-9" - /> -
- -
- {isLoading ? ( -
- {[1, 2, 3].map((i) => ( -
- ))} -
- ) : filteredRules.length === 0 ? ( -
- -

{formatMessage({ id: 'cliRules.emptyState.title' })}

-
- ) : ( - filteredRules.map((rule) => ( -
-
-
- -
-
-

{rule.name}

- {rule.description && ( -

{rule.description}

- )} -
- {rule.category && ( - {rule.category} - )} - {rule.severity && ( - - {formatMessage({ id: `cliRules.severity.${rule.severity}` })} - - )} -
-
-
- -
- )) - )} -
- - ); -} - // ========== Main Page Component ========== export function SettingsPage() { @@ -408,6 +381,18 @@ export function SettingsPage() { updateCliTool(toolId, { [field]: value }); }; + const handleUpdateTags = (toolId: string, tags: string[]) => { + updateCliTool(toolId, { tags }); + }; + + const handleUpdateAvailableModels = (toolId: string, availableModels: string[]) => { + updateCliTool(toolId, { availableModels }); + }; + + const handleUpdateSettingsFile = (toolId: string, settingsFile: string | undefined) => { + updateCliTool(toolId, { settingsFile }); + }; + const handlePreferenceChange = (key: keyof UserPreferences, value: unknown) => { setUserPreferences({ [key]: value }); }; @@ -502,6 +487,9 @@ export function SettingsPage() { onToggleEnabled={() => handleToggleToolEnabled(toolId)} onSetDefault={() => handleSetDefaultTool(toolId)} onUpdateModel={(field, value) => handleUpdateModel(toolId, field, value)} + onUpdateTags={(tags) => handleUpdateTags(toolId, tags)} + onUpdateAvailableModels={(models) => handleUpdateAvailableModels(toolId, models)} + onUpdateSettingsFile={(settingsFile) => handleUpdateSettingsFile(toolId, settingsFile)} /> ))}
@@ -621,15 +609,6 @@ export function SettingsPage() {
- {/* Git Hooks */} - - - {/* Rules */} - - - {/* Index Manager */} - - {/* Reset Settings */}

diff --git a/ccw/frontend/src/types/store.ts b/ccw/frontend/src/types/store.ts index f268225a..d988d5d7 100644 --- a/ccw/frontend/src/types/store.ts +++ b/ccw/frontend/src/types/store.ts @@ -308,6 +308,7 @@ export interface CliToolConfig { tags: string[]; type: 'builtin' | 'cli-wrapper' | 'api-endpoint'; settingsFile?: string; + availableModels?: string[]; } export interface ApiEndpoints { diff --git a/ccw/src/commands/stop.ts b/ccw/src/commands/stop.ts index 1c87b75e..9256f43c 100644 --- a/ccw/src/commands/stop.ts +++ b/ccw/src/commands/stop.ts @@ -35,10 +35,17 @@ async function findProcessOnPort(port: number): Promise { */ async function killProcess(pid: string): Promise { try { - await execAsync(`taskkill /PID ${pid} /F`); + // Use PowerShell to avoid Git Bash path expansion issues with /PID + await execAsync(`powershell -Command "Stop-Process -Id ${pid} -Force -ErrorAction Stop"`); return true; } catch { - return false; + // Fallback to taskkill via cmd + try { + await execAsync(`cmd /c "taskkill /PID ${pid} /F"`); + return true; + } catch { + return false; + } } } @@ -52,7 +59,7 @@ export async function stopCommand(options: StopOptions): Promise { const force = options.force || false; console.log(chalk.blue.bold('\n CCW Dashboard\n')); - console.log(chalk.gray(` Checking server on port ${port}...`)); + console.log(chalk.gray(` Checking server on port ${port} and ${reactPort}...`)); try { // Try graceful shutdown via API first @@ -139,11 +146,18 @@ export async function stopCommand(options: StopOptions): Promise { console.log(chalk.green(' Main server killed successfully!')); // Also try to kill React frontend + console.log(chalk.gray(` Checking React frontend on port ${reactPort}...`)); const reactPid = await findProcessOnPort(reactPort); if (reactPid) { - console.log(chalk.cyan(` Cleaning up React frontend on port ${reactPort}...`)); - await killProcess(reactPid); - console.log(chalk.green(' React frontend stopped!')); + console.log(chalk.cyan(` Cleaning up React frontend (PID: ${reactPid})...`)); + const reactKilled = await killProcess(reactPid); + if (reactKilled) { + console.log(chalk.green(' React frontend stopped!')); + } else { + console.log(chalk.yellow(' Failed to stop React frontend')); + } + } else { + console.log(chalk.gray(' No React frontend running')); } console.log(chalk.green.bold('\n All processes stopped successfully!\n')); @@ -153,6 +167,12 @@ export async function stopCommand(options: StopOptions): Promise { process.exit(1); } } else { + // Also check React frontend port + const reactPid = await findProcessOnPort(reactPort); + if (reactPid) { + console.log(chalk.yellow(` React frontend running on port ${reactPort} (PID: ${reactPid})`)); + } + console.log(chalk.gray(`\n This is not a CCW server. Use --force to kill it:`)); console.log(chalk.white(` ccw stop --force\n`)); process.exit(0); diff --git a/ccw/src/commands/view.ts b/ccw/src/commands/view.ts index 577101b2..baabf78c 100644 --- a/ccw/src/commands/view.ts +++ b/ccw/src/commands/view.ts @@ -78,7 +78,7 @@ export async function viewCommand(options: ViewOptions): Promise { const host = options.host || '127.0.0.1'; const browserHost = host === '0.0.0.0' || host === '::' ? 'localhost' : host; // --new flag is shorthand for --frontend react - const frontend = options.new ? 'react' : (options.frontend || 'both'); + const frontend = options.new ? 'react' : (options.frontend || 'js'); // Resolve workspace path let workspacePath = process.cwd(); diff --git a/ccw/src/templates/dashboard-js/views/mcp-manager.js b/ccw/src/templates/dashboard-js/views/mcp-manager.js index 97e26eb5..9be2ef21 100644 --- a/ccw/src/templates/dashboard-js/views/mcp-manager.js +++ b/ccw/src/templates/dashboard-js/views/mcp-manager.js @@ -8,6 +8,8 @@ const CCW_MCP_TOOLS = [ { name: 'edit_file', desc: 'Edit/replace content', core: true }, { name: 'read_file', desc: 'Read file contents', core: true }, { name: 'core_memory', desc: 'Core memory management', core: true }, + // Optional tools + { name: 'ask_question', desc: 'Interactive questions (A2UI)', core: false }, ]; // Get currently enabled tools from installed config (Claude)