From f84d50fb117283f2c815f2e7005f200f581b067e Mon Sep 17 00:00:00 2001 From: Kafu <153478754+python-rust@users.noreply.github.com> Date: Wed, 16 Sep 2026 12:16:18 +0800 Subject: [PATCH 01/13] feat(fde): add China-focused prompt and MCP presets --- .trellis/spec/frontend/index.md | 2 + .trellis/spec/frontend/mcp-fde-catalog.md | 82 +++ .trellis/spec/frontend/mcp.md | 3 + .trellis/spec/frontend/prompt-presets.md | 96 +++ .trellis/spec/frontend/prompts-memory.md | 10 +- .trellis/spec/frontend/quality-guidelines.md | 5 + config/playwright.config.ts | 1 + src/pages/mcp/Discovery.tsx | 1 + src/pages/mcp/catalog.ts | 431 ++++++++++++- src/pages/prompts/Page.tsx | 155 ++++- src/pages/prompts/PresetBrowser.tsx | 150 +++++ src/pages/prompts/page.css | 9 + src/pages/prompts/presets.ts | 588 ++++++++++++++++++ tests/browser/fde-presets.spec.ts | 71 +++ tests/browser/scroll-ownership.spec.ts | 26 +- tests/renderer/features/featurePages.test.tsx | 21 + tests/renderer/features/mcpCatalog.test.ts | 161 ++++- tests/renderer/pages/prompts/Page.test.tsx | 143 ++++- tests/renderer/pages/prompts/presets.test.ts | 53 ++ 19 files changed, 1957 insertions(+), 51 deletions(-) create mode 100644 .trellis/spec/frontend/mcp-fde-catalog.md create mode 100644 .trellis/spec/frontend/prompt-presets.md create mode 100644 src/pages/prompts/PresetBrowser.tsx create mode 100644 src/pages/prompts/presets.ts create mode 100644 tests/browser/fde-presets.spec.ts create mode 100644 tests/renderer/pages/prompts/presets.test.ts diff --git a/.trellis/spec/frontend/index.md b/.trellis/spec/frontend/index.md index 27c52bc12..4be26c838 100644 --- a/.trellis/spec/frontend/index.md +++ b/.trellis/spec/frontend/index.md @@ -46,7 +46,9 @@ focused feature owner. Apply [Component Guidelines](./component-guidelines.md), | [Assignments](./assignments.md) | Shared seven-target selection and serialized mutations. | | [Skills](./skills.md) | Discovery, installed items, backups and assignment. | | [MCP](./mcp.md) | Catalog/launch validation, CRUD, installation and assignment. | +| [FDE MCP Catalogue](./mcp-fde-catalog.md) | China-oriented recipes, restricted tools, env credentials and discovery. | | [Prompts and Memory](./prompts-memory.md) | Native content CRUD, editor/dirty state and directory operations. | +| [Prompt Presets](./prompt-presets.md) | Static FDE catalogue, preview, draft copying and disabled library saves. | ## Historical discovery routers diff --git a/.trellis/spec/frontend/mcp-fde-catalog.md b/.trellis/spec/frontend/mcp-fde-catalog.md new file mode 100644 index 000000000..85c076ff0 --- /dev/null +++ b/.trellis/spec/frontend/mcp-fde-catalog.md @@ -0,0 +1,82 @@ +# FDE MCP Catalogue Recipes + +## 1. Scope / Trigger + +Read before changing FDE discovery membership or China-oriented cloud/data +recipes. [MCP](./mcp.md) owns installation, targets, secret display and native +mutation behavior; this document owns the catalogue-only additions. + +## 2. Signatures and owners + +`src/pages/mcp/catalog.ts` remains the single recipe owner. Its existing +`McpCatalogItem.build(values, apps, platform): McpServer` validates required +fields/targets and uses `npxSpec` for Windows/macOS launch differences. +`McpCatalogCategory` and `McpCatalogFilterId` admit `fde`; Discovery intersects +this category with ordinary search. No new installer, Port or endpoint exists. + +## 3. Contracts + +- The FDE filter includes relevant existing domestic collaboration, mapping, + API and deployment entries plus the new cloud/data entries; do not clone + existing IDs to create a second catalogue. Other install-mode filters retain + their existing meaning. No-field configuration does not imply no login. +- New FDE recipes are `provenance: official`, `maturity: verify`, linked to + upstream setup documentation and carry actual runtime/auth/risk notes. + Configuration review is not authentication, availability, audit certification + or a guarantee of mainland connectivity. Revalidate upstream when editing. +- Credentials/DSNs use password fields and `env`, never argv, search or card + text. `CONNECTION_STRING` for DMS identifies an authorized database rather + than carrying its password. Optional STS values are omitted when blank. + +| ID / launcher | Required configuration and conservative defaults | +| ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `cloudbase` / npx `@cloudbase/cloudbase-mcp@latest` | Vendor login and environment selection occur later; cloud privilege, no automatic login/deploy. | +| `aliyun-dms` / uvx `alibabacloud-dms-mcp-server@latest` | `ALIBABA_CLOUD_ACCESS_KEY_ID`, `ALIBABA_CLOUD_ACCESS_KEY_SECRET`, required `CONNECTION_STRING`, optional `ALIBABA_CLOUD_SECURITY_TOKEN`; write privilege, not a SQL firewall. | +| `aliyun-dataworks` / npx `alibabacloud-dataworks-mcp-server@latest` | Alibaba AK pair, `REGION`, fixed `TOOL_NAMES=ListProjects`; read-only tool selection, not general DataWorks write access. | +| `aliyun-ack` / uvx `alibabacloud-ack-mcp-server@latest` | `ACCESS_KEY_ID`, `ACCESS_KEY_SECRET`, `KUBECONFIG_MODE=ACK_PRIVATE`; no `--allow-write`, requires cluster network/RAM/RBAC. | +| `aliyun-rds` / uvx `alibabacloud-rds-openapi-mcp-server@latest` | Alibaba AK pair, optional STS, `SERVER_TRANSPORT=stdio`, `ENABLE_WRITE_TOOLS=false`; still cloud privilege, not guaranteed read-only. | +| `aliyun-cloudops` / uvx `alibaba-cloud-ops-mcp-server@latest` | Alibaba AK pair; fixed `--transport stdio --env domestic --services ecs --visible-tools ECS_DescribeInstances`; do not add broad/local execution tools. | +| `dbhub` / npx `@bytebase/dbhub@latest --transport stdio` | Required secret `DSN`; Node >=22.5. Do not invent legacy `--readonly`; database account permissions must enforce read-only. | +| `starrocks` / `uv run --with mcp-server-starrocks mcp-server-starrocks --mode stdio` | Required secret `STARROCKS_URL`; optional `STARROCKS_SSL_CA` sets both `STARROCKS_SSL_VERIFY_CERT=true` and `STARROCKS_SSL_VERIFY_IDENTITY=true`. Missing CA does not guarantee secure transport. | + +Tool-selection flags complement server-side permissions; instructions and +privilege labels do not enforce authorization. DMS, DBHub and StarRocks can +write SQL; RDS and CloudBase can change cloud resources. The catalogue does not +execute these packages to validate connectivity or request customer secrets. +Package resolution remains upstream-owned; no SDK/runtime dependency is added +to FyAgent's own lockfiles by adding a recipe. + +## 4. Validation & Error Matrix + +| Condition | Required behavior | +| ------------------------------------------------------------ | ----------------------------------------------------------------------------- | +| Required field missing / no target | Existing builder throws `UserFacingError` before native installation. | +| Optional STS or CA blank | Omit optional env keys, do not create fictional credentials/TLS state. | +| Platform is Windows | npx uses existing `cmd /c` adapter; uv/uvx retain their documented argv. | +| Database account can write | Keep write/cloud risk even when the intended workflow only queries. | +| Upstream only provides source/binary or publisher is unclear | Do not invent an installable npm/PyPI recipe or mark community code official. | +| FDE filter + search | Both apply; unrelated catalogue entries are excluded. | + +## 5. Good / Base / Bad Cases + +Good: a restricted CloudOps inventory configuration is built for one chosen +Agent, then validated by the customer in a test account. Base: a DBHub DSN +stays in env, with database permissions and TLS independently configured. +Bad: claim a prose “read only” instruction prevents SQL writes, or equate a +successfully saved CloudBase config with a successful login/deployment. + +## 6. Tests Required + +`tests/renderer/features/mcpCatalog.test.ts` covers all new IDs, category, +official/verify metadata, nonempty docs/risk, required fields, zero targets, +macOS/Windows identity generation and no private values in argv/search. +Assert exact env keys, restricted tool selectors, STS omission and conditional +CA verification. `featurePages.test.tsx` checks FDE/search intersection while +retaining install-mode and ordinary install/assignment regressions. +These deterministic tests do not replace authenticated customer-side smoke. + +## 7. Wrong vs Correct + +Wrong: DSN in `--dsn` argv, an unverified `--readonly` flag, or an unrestricted +CloudOps toolset labeled read-only. Correct: DSN in env, accurately stated +database permissions, and the documented inventory-only CloudOps selector. diff --git a/.trellis/spec/frontend/mcp.md b/.trellis/spec/frontend/mcp.md index c10aa53e0..2ff2bc92d 100644 --- a/.trellis/spec/frontend/mcp.md +++ b/.trellis/spec/frontend/mcp.md @@ -26,6 +26,9 @@ live-file ordering, import, and conflict semantics are owned by QoderWork/TRAE external MCP preflight is not part of this management page; it uses `ExternalMcpPort` in the relevant Agent configuration flow. +FDE discovery membership and cloud/data command/env defaults are owned by +[FDE MCP Catalogue](./mcp-fde-catalog.md); do not duplicate those recipes here. + ## 2. Signatures The current management Port is exactly: diff --git a/.trellis/spec/frontend/prompt-presets.md b/.trellis/spec/frontend/prompt-presets.md new file mode 100644 index 000000000..84861fbca --- /dev/null +++ b/.trellis/spec/frontend/prompt-presets.md @@ -0,0 +1,96 @@ +# Prompt Preset Catalogue + +## 1. Scope / Trigger + +Read before changing the static FDE catalogue, its browser, or copying a preset +into an application library. Native CRUD, live-file authority and seven-app +identity remain in [Prompts and Memory](./prompts-memory.md). + +## 2. Signatures and owners + +`src/pages/prompts/presets.ts` owns `FDE_PROMPT_PRESETS`, the closed six-value +`PromptPresetCategory`, `PROMPT_PRESET_CATEGORIES` and: + +```ts +searchPromptPresets( + query: string, + category: PromptPresetCategory | "all", +): readonly PromptPreset[]; +``` + +`PromptPreset` has stable `id`, `name`, `category`, `description`, `tags`, +`inputs`, `approach`, `deliverables`, `evaluation`, `example`, and composed +`content`. `PresetBrowser.tsx` owns search/filter/preview selection only; +`Page.tsx` owns application, editor, discard intent and native mutations. +There is no new Port, native command, preference or persistence schema. + +## 3. Contracts + +- The catalogue is original static content, not an application library, a + vendor-endorsed template or a measured model benchmark. Each scenario has + specific inputs, implementation choices, deliverables, acceptance cases and + an example. The common contract covers evidence, untrusted external text, + tool approval and data minimization without demanding a fixed report format. +- Search matches every whitespace-separated, case-insensitive term in the + scenario metadata/body, intersected with the chosen category. Do not search + credentials, native user records or only the shared boilerplate. +- Use shared `FeatureTabs` / `FeatureTabPanel` with `layout="workspace"`, + `FeatureSearch`, `FeatureList`, `SplitPanes` and `Button`. The preset panel + mounts on selection and unmounts on exit. The library/editor stays mounted + while browsing, so its draft is not discarded by a view-only tab change. +- Preview/search is available without native access. `使用此预设` requires + an existing successful library query and no active mutation. Browsing or + selecting a catalogue row never invokes a native write. +- Applying a preset creates a new draft containing copies of name, + description and content. It does not use the catalogue ID as a library ID, + mutate an existing row, save, enable, install MCPs or interpolate secrets. +- Replacing a dirty draft uses the existing `preset` discard intent and + shared ConfirmDialog. Cancel preserves the draft; confirm copies the preset + and returns to the library. App/route changes retain their existing guards. + Capture the Use button's origin with its persistent preset-tab return anchor: + the button unmounts on confirmation, and the shared dialog redirects an + inactive tab anchor to its currently active sibling instead of stealing focus. +- Save uses `prompt-` plus 16 cryptographically random bytes encoded as 32 hex + characters for new renderer-created records (`crypto.getRandomValues`), + preserves existing IDs on edit and sets new records `enabled: false`. + Do not require the secure-context-only `crypto.randomUUID` API or use a + timestamp as uniqueness. Repeated copies cannot replace an enabled record. `upsert` and authoritative + rereads are unchanged; enable remains a separate explicit operation. + +## 4. Validation & Error Matrix + +| Condition | Required behavior | +| -------------------------------------------- | ------------------------------------------------------------------------- | +| No native library data / initial read failed | Browsing works, use is disabled; no synthetic native success. | +| Search/category matches nothing | Empty result with clear-filter action; no stale preview. | +| Browse while editor is dirty | Preserve the editor and its route blocker. | +| Apply while dirty | Confirm before replacing; cancel keeps user content. | +| Use or save while busy | Existing write lock prevents duplicate native operations. | +| Save fails | Retain editable draft and error; no enable or success claim. | +| Save succeeds, reread fails | Retain the existing uncertainty warning, not an invented effective state. | +| Copy same preset again | Independent disabled record with a new opaque ID. | + +## 5. Good / Base / Bad Cases + +Good: preview an environmental-report preset, edit project details, save a new +disabled Claude record, then separately choose whether to enable it. Base: +browser preview can read all templates but cannot save to a desktop library. +Bad: auto-seed every application, overwrite a preset-named existing record, or +invoke a suggested integration merely because its name appears in prompt text. + +## 6. Tests Required + +`tests/renderer/pages/prompts/presets.test.ts` checks unique IDs, complete +scenario fields, category/search intersections and shared safety boundaries. +`Page.test.tsx` covers zero-write preview, dirty cancellation/confirmation, +repeated copies, all seven libraries, disabled saves and native-only browsing. +Keep existing failed-save/readback, route guards and live-file tests. +`tests/browser/fde-presets.spec.ts` checks bounded readable preview, filters, +empty results and native-only behavior in Chromium/WebKit. Static structure +and browser fixtures do not establish model output quality or native writes. + +## 7. Wrong vs Correct + +Wrong: `upsert(app, { ...preset, id: preset.id, enabled: true })` on selection. +Correct: create an unsaved draft, explicitly save with a fresh 128-bit opaque ID and +`enabled: false`, then let the existing enable/readback workflow act separately. diff --git a/.trellis/spec/frontend/prompts-memory.md b/.trellis/spec/frontend/prompts-memory.md index 4be019aab..e5c2a31a3 100644 --- a/.trellis/spec/frontend/prompts-memory.md +++ b/.trellis/spec/frontend/prompts-memory.md @@ -14,7 +14,8 @@ directly; all effects cross `FeaturePorts` and the existing `src/shared/platform/**` boundary. Reuse is the default: Prompts and Memory share `FeatureSearch`, `FeatureList`, and Memory type `FeatureTabs`. New chrome that the other page will need goes in `src/shared/ui` on the -first commit. See [Frontend Reuse](./reuse.md). +first commit. See [Frontend Reuse](./reuse.md). Static FDE catalogue and +draft-only selection are owned by [Prompt Presets](./prompt-presets.md). The selected-Agent `提示词` section may present the existing prompt library, enable one entry, and link to `/prompts`, but it remains a consumer of this @@ -126,6 +127,9 @@ paths above. UUID primitive. A timestamp is display metadata, not uniqueness: repeated imports must not replace an enabled row or change its live projection. Existing persisted IDs remain valid and are not migrated. +- Renderer-created entries also use `prompt-` plus a fresh 128-bit random + hex ID rather than timestamps; existing records retain IDs when edited. Preset + selection creates an unsaved draft and new saves remain disabled. - The live-file inspector is collapsed by default under the editor. It reports the current native file content and is not an editable second source of truth. Do not keep it as a third always-open column that steals @@ -192,6 +196,8 @@ The fixed resource mapping is: completed but refresh failed; do not announce synchronized state. - Application, document, tab, daily-file, and route transitions share the same dirty-discard confirmation flow. Do not use `window.confirm`. + The Prompts library/preset tabs only change the view and preserve the editor; + applying a preset, not browsing it, requires dirty-discard confirmation. - Prompts select the application with `CatalogMasterDetail`, not a ` { + const value = event.target.value; + const match = PROMPT_PRESET_CATEGORIES.find( + (item) => item.id === value, + ); + if (value === "all" || match) setCategory(match?.id ?? "all"); + }} + > + + {PROMPT_PRESET_CATEGORIES.map((item) => ( + + ))} + + + {selected ? ( +
+ +
+
+

领域预设 · {items.length}

+
+ + {items.map((item) => ( + setSelectedId(item.id)} + > + + {item.description} + + + ))} + +
+
+
+
+
+

{selected.name}

+
+

+ 用于 {appLabel},保存后可单独启用。 +

+
+ +
+