Repository navigation
Feature/sites auto install plugin - #2228
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR adds a bundled Wegent Sites plugin lifecycle, including build staging, backend publication, idempotent installation, Sites UI integration, desktop E2E coverage, and documentation. It also adds plugin mention/detail navigation and documents explicit terminal-launch behavior. ChangesBuilt-in Sites plugin lifecycle
Plugin mention and detail navigation
Explicit terminal launch design
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
Suggested reviewers: Sequence Diagram(s)sequenceDiagram
participant Build
participant Backend
participant SitesPage
participant PluginAPI
participant PluginWorkspace
Build->>Backend: stage and publish wegent-sites
SitesPage->>PluginAPI: ensure builtin plugin installed
PluginAPI-->>SitesPage: installed plugin metadata
SitesPage->>SitesPage: queue plugin trial reference
SitesPage->>PluginWorkspace: navigate to plugin detail route
PluginWorkspace->>PluginAPI: load marketplace and installed plugin data
PluginWorkspace-->>SitesPage: render matching plugin detail
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 89aca53f2e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| "prepare:bundled-plugins": "node scripts/sync-bundled-sites-plugin.mjs", | ||
| "prepare:codex": "node scripts/prepare-codex-binary.mjs", | ||
| "build": "tsc -b && vite build", | ||
| "build:tauri": "pnpm run prepare:bundled-plugins && pnpm run build", |
There was a problem hiding this comment.
Provision the bundled plugin in clean builds
In a clean checkout, build:tauri always runs prepare:bundled-plugins, but the sync script requires an untracked sibling repository at ../wegent-skills (or WEWORK_SITES_PLUGIN_SOURCE). The existing desktop E2E and macOS/Windows release jobs in .github/workflows/wework-e2e.yml and .github/workflows/wework-app.yml check out only this repository and provide neither source, so every tauri build now fails before compilation with “Sites plugin not found”; the plugin must be supplied or fetched as part of those builds, ideally from a pinned source.
AGENTS.md reference: wework/AGENTS.md:L32-L33
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
wework/src/api/local/codexPlugins.ts (1)
788-854: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider decomposing
ensureLocalMarketplacePlugin.The method inlines validation, existing-plugin lookup, marketplace reconciliation, registration, path verification, and install+verify in one ~67-line block. Extracting helpers (e.g.
ensureMarketplaceConfigured(reference, state)andinstallAndVerifyPlugin(...)) would ease future maintenance without changing behavior.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@wework/src/api/local/codexPlugins.ts` around lines 788 - 854, Decompose ensureLocalMarketplacePlugin into focused helpers while preserving its behavior: keep runtime/reference validation and existing-plugin lookup in the main flow, extract marketplace reconciliation/registration into an ensureMarketplaceConfigured helper, and extract plugin installation plus refreshed-state verification into an installAndVerifyPlugin helper. Reuse the existing state and marketplace symbols so error handling and return behavior remain unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/superpowers/plans/2026-07-22-wework-explicit-terminal-launch.md`:
- Around line 77-94: Resolve the undefined terminalSequenceRef in
openTerminalTab by either introducing and consistently updating that ref for
every terminal-tab creation path, or retaining the existing terminalSequence
state with a functional updater. Ensure initial and reopen-after-empty creation
still call createTerminalTab(index) without a tool, while the add-menu path
opens with the terminal tool.
- Around line 907-915: Update the final-diff verification commands in Step 6 to
use the target branch merge base, such as git merge-base main HEAD, instead of
the fixed HEAD~4 base. Apply the stable base consistently to the git diff
--check and git diff --stat commands while preserving the existing git status
check and expected-results guidance.
---
Nitpick comments:
In `@wework/src/api/local/codexPlugins.ts`:
- Around line 788-854: Decompose ensureLocalMarketplacePlugin into focused
helpers while preserving its behavior: keep runtime/reference validation and
existing-plugin lookup in the main flow, extract marketplace
reconciliation/registration into an ensureMarketplaceConfigured helper, and
extract plugin installation plus refreshed-state verification into an
installAndVerifyPlugin helper. Reuse the existing state and marketplace symbols
so error handling and return behavior remain unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: d92808ef-ef3e-4d55-bed4-38dcb80474c5
📒 Files selected for processing (20)
.gitignoredocs/superpowers/plans/2026-07-22-wework-explicit-terminal-launch.mddocs/superpowers/specs/2026-07-22-wework-explicit-terminal-launch-design.mdwework/README.mdwework/e2e/desktop/task-flow.e2e.mjswework/package.jsonwework/scripts/dev-mac-app.shwework/scripts/sync-bundled-sites-plugin.mjswework/src-tauri/bundled-plugins/wegent-marketplace/.agents/plugins/marketplace.jsonwework/src-tauri/src/bundled_plugins.rswework/src-tauri/src/lib.rswework/src-tauri/tauri.conf.jsonwework/src/App.plugins.test.tsxwework/src/api/local/codexPlugins.test.tswework/src/api/local/codexPlugins.tswework/src/i18n/locales/en/sites.jsonwework/src/i18n/locales/zh-CN/sites.jsonwework/src/lib/bundled-plugins.test.tswework/src/lib/bundled-plugins.tswework/src/pages/SitesPage.tsx
| function createTerminalTab( | ||
| index: number, | ||
| defaultOpenTool?: BottomWorkspacePanelTab['defaultOpenTool'] | ||
| ): BottomWorkspacePanelTab { | ||
| return { id: `terminal-${index}`, title: `Terminal ${index}`, defaultOpenTool } | ||
| } | ||
| ``` | ||
|
|
||
| Keep both the initial state and the reopen-after-empty effect on `createTerminalTab(index)` with no tool. Change the existing add-menu Terminal path to call: | ||
|
|
||
| ```tsx | ||
| const openTerminalTab = () => { | ||
| const tab = createTerminalTab(terminalSequenceRef.current, 'terminal') | ||
| terminalSequenceRef.current += 1 | ||
| setTabs(current => [...current, tab]) | ||
| setActiveTabId(tab.id) | ||
| } | ||
| ``` |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win
Define terminalSequenceRef or keep the existing state-based sequence.
The proposed openTerminalTab uses terminalSequenceRef.current, but the plan never introduces that ref or replaces the existing terminalSequence state used by BottomWorkspacePanel.tsx. Implementing this literally will fail type checking. Either define and consistently update the ref across all tab-creation paths, or retain the existing functional state updater.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/superpowers/plans/2026-07-22-wework-explicit-terminal-launch.md` around
lines 77 - 94, Resolve the undefined terminalSequenceRef in openTerminalTab by
either introducing and consistently updating that ref for every terminal-tab
creation path, or retaining the existing terminalSequence state with a
functional updater. Ensure initial and reopen-after-empty creation still call
createTerminalTab(index) without a tool, while the add-menu path opens with the
terminal tool.
| - [ ] **Step 6: Review the final diff against the specification** | ||
|
|
||
| ```bash | ||
| git diff HEAD~4 --check | ||
| git diff HEAD~4 --stat | ||
| git status --short | ||
| ``` | ||
|
|
||
| Expected: no whitespace errors, only the planned Wework component/test/E2E files differ, and the worktree is clean after the planned commits. |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Use a stable diff base instead of HEAD~4.
HEAD~4 assumes the repository has exactly four preceding implementation commits, which is not reliable after squashing, extra commits, or committing these documents separately. Use the target-branch merge base instead, such as git diff "$(git merge-base main HEAD)" HEAD --check, so the final verification examines the actual PR diff.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/superpowers/plans/2026-07-22-wework-explicit-terminal-launch.md` around
lines 907 - 915, Update the final-diff verification commands in Step 6 to use
the target branch merge base, such as git merge-base main HEAD, instead of the
fixed HEAD~4 base. Apply the stable base consistently to the git diff --check
and git diff --stat commands while preserving the existing git status check and
expected-results guidance.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@pnpm-workspace.yaml`:
- Around line 13-16: Update the packageExtensions selector for tiptap-markdown
to use the same ^0.8.10 range declared by the package dependency, ensuring the
`@tiptap/pm` injection applies to all compatible resolved versions.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: b86abe89-00f7-4c6d-bd08-92b22a98fda3
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (1)
pnpm-workspace.yaml
| packageExtensions: | ||
| 'tiptap-markdown@0.8.10': | ||
| dependencies: | ||
| '@tiptap/pm': ^2.27.2 |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python - <<'PY'
import json
import re
with open("wework/package.json") as f:
spec = json.load(f)["dependencies"]["tiptap-markdown"]
workspace = open("pnpm-workspace.yaml").read()
match = re.search(r"'tiptap-markdown@([^']+)'", workspace)
assert match, "packageExtensions selector missing"
selector = match.group(1)
assert selector == spec, f"selector {selector!r} does not match dependency {spec!r}"
PY
rg -n "tiptap-markdown|`@tiptap/pm`" pnpm-lock.yamlRepository: wecode-ai/Wegent
Length of output: 298
🌐 Web query:
pnpm packageExtensions semver range selector tilde caret exact version documentation
💡 Result:
The pnpm packageExtensions feature allows you to extend package definitions with additional dependencies, optionalDependencies, peerDependencies, and peerDependenciesMeta [1][2][3]. In the packageExtensions configuration, keys act as selectors to target specific packages [1][3]. These selectors support package names optionally followed by a semver range [1][3]. Key syntax rules for packageExtensions selectors: - Package Name Only: Using just the package name (e.g., react-redux) applies the extension to all versions of that package [1][3]. - Semver Range: You can append a semver range (e.g., react-redux@1 or react-redux@^2.0.0) to target only the versions of the package that satisfy that specific range [1][3][4]. - Semver Compatibility: The underlying implementation uses the semver library to check if a package's version satisfies the specified range selector [4]. You can use standard semver operators, including tildes (~), carets (^), and exact versions, as they are part of valid semver range expressions [4]. Configuration location: In recent versions of pnpm, packageExtensions must be defined in the pnpm-workspace.yaml file (or equivalent project configuration) [1][5]. Historically, it was sometimes configured within the pnpm field of package.json, but this is no longer supported in newer versions [6][5]. Example: packageExtensions: react-redux@1: peerDependencies: react-dom: "*" express@^4.0.0: dependencies: some-dep: "1.2.3" When a package is installed, pnpm uses these selectors to determine which extensions to apply to that package's manifest [4]. If the version of the package matches the selector's range, the defined dependencies are merged into the package's existing manifest fields [4].
Citations:
- 1: https://pnpm.io/10.x/settings
- 2: https://pnpm.io/settings
- 3: https://github.com/pnpm/pnpm.io/blob/main/versioned_docs/version-10.x/settings.md
- 4: https://github.com/pnpm/pnpm/blob/e1e29c15/hooks/read-package-hook/src/createPackageExtender.ts
- 5: https://pnpm.io/package_json
- 6: https://github.com/pnpm/pnpm.io/blob/main/versioned_docs_archived/version-8.x/package_json.md
🌐 Web query:
pnpm-lock.yaml tiptap-markdown 0.8.10 @tiptap/pm pnpm-lock.yaml
💡 Result:
The package tiptap-markdown version 0.8.10 is a community-maintained extension for the Tiptap editor [1][2][3]. It is distinct from the official @tiptap/markdown package, which is maintained by the Tiptap team [4]. Regarding the relationship between tiptap-markdown@0.8.10 and @tiptap/pm: 1. Dependency Issue: Users have reported issues where @tiptap/pm was incorrectly listed as a devDependency in certain versions of tiptap-markdown, leading to resolution errors in strict environments like Yarn PnP or when strict dependency checking is enabled [5]. 2. pnpm-lock.yaml Context: In a pnpm-lock.yaml file, tiptap-markdown will typically be listed alongside its required dependencies [6][7]. Because tiptap-markdown (specifically the community version) relies on ProseMirror functionality provided by Tiptap, it requires @tiptap/pm and @tiptap/core as peer or runtime dependencies [1][3]. 3. Versioning: tiptap-markdown version 0.8.10 was released in May 2025 [1]. When using pnpm, the pnpm-lock.yaml file will record the specific dependency graph, often showing tiptap-markdown linked to the specific versions of @tiptap/core and @tiptap/pm it was installed with [6][7]. If you are encountering errors related to @tiptap/pm in your project's pnpm-lock.yaml or build process, it is likely due to a mismatch between the expected peer dependency version of @tiptap/pm and what is installed in your project [5]. Ensure that your project's Tiptap version aligns with the requirements of the tiptap-markdown extension you are using [8]. Note that official Tiptap ecosystem tools are now often scoped under @tiptap/, so verify if you should be using the official @tiptap/markdown instead [4].
Citations:
- 1: https://registry.npmjs.org/%40yunger%2Ftiptap-markdown
- 2: https://registry.npmjs.org/tiptap-markdown
- 3: https://deepwiki.com/aguingand/tiptap-markdown
- 4: https://registry.npmjs.org/@tiptap/markdown
- 5: put @tiptap/pm in depedencies aguingand/tiptap-markdown#88
- 6: https://github.com/kongying-tavern/docs/blob/468dd7466bc2ba43d9aaa48ca33c1ff1192ae911/pnpm-lock.yaml
- 7: https://github.com/kongying-tavern/docs/blob/f0092bb3/pnpm-lock.yaml
- 8: https://www.npmjs.com/package/tiptap-markdown
Align the packageExtensions selector with the tiptap-markdown dependency range.
wework/package.json allows tiptap-markdown versions ^0.8.10, while packageExtensions only targets 0.8.10. If the lockfile refreshes to 0.8.11 or another compatible version, the injected @tiptap/pm dependency can drop. Use 'tiptap-markdown@^0.8.10', or pin the package dependency to 0.8.10.
Suggested change
- 'tiptap-markdown@0.8.10':
+ 'tiptap-markdown@^0.8.10':📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| packageExtensions: | |
| 'tiptap-markdown@0.8.10': | |
| dependencies: | |
| '@tiptap/pm': ^2.27.2 | |
| packageExtensions: | |
| 'tiptap-markdown@^0.8.10': | |
| dependencies: | |
| '`@tiptap/pm`': ^2.27.2 |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@pnpm-workspace.yaml` around lines 13 - 16, Update the packageExtensions
selector for tiptap-markdown to use the same ^0.8.10 range declared by the
package dependency, ensuring the `@tiptap/pm` injection applies to all compatible
resolved versions.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
wework/src/components/plugins/PluginsWorkspace.tsx (1)
292-295: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueExtract and reuse
normalizedPluginIdentity.
PluginsWorkspacedefines this exact normalizer locally, whilecodexPlugins.tshas the same private helper for the same case-insensitive identity comparisons. Move it to a shared utility/module and import it here to avoid keeping two identical definitions in sync.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@wework/src/components/plugins/PluginsWorkspace.tsx` around lines 292 - 295, Move the shared normalizedPluginIdentity helper out of PluginsWorkspace and codexPlugins.ts into a common utility/module, export it there, and import/reuse it in both callers. Remove both local private definitions while preserving the existing trim-and-lowercase behavior for string identities and the empty-string fallback for non-strings.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@wework/src/components/plugins/PluginsWorkspace.tsx`:
- Around line 711-712: Update the requested plugin deep-link handling around
requestedPluginReference and requestedPluginHandledRef so route search changes
are reparsed and resolved. Make the memo depend on a normalized current search
value, and reset the one-shot handled flag when that route reference changes,
preserving single handling per distinct plugin/marketplace reference.
---
Nitpick comments:
In `@wework/src/components/plugins/PluginsWorkspace.tsx`:
- Around line 292-295: Move the shared normalizedPluginIdentity helper out of
PluginsWorkspace and codexPlugins.ts into a common utility/module, export it
there, and import/reuse it in both callers. Remove both local private
definitions while preserving the existing trim-and-lowercase behavior for string
identities and the empty-string fallback for non-strings.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 9fb0d0b8-a1fc-4428-b3d5-67aa70064205
📒 Files selected for processing (13)
wework/e2e/desktop/task-flow.e2e.mjswework/src/api/local/codexPlugins.tswework/src/components/chat/MessageList.test.tsxwework/src/components/chat/MessageList.tsxwework/src/components/chat/composer/ComposerMentionNodeView.tswework/src/components/chat/composer/ComposerProseMirrorEditor.test.tsxwework/src/components/chat/composer/ComposerProseMirrorEditor.tsxwework/src/components/chat/composer/ComposerTextarea.tsxwework/src/components/chat/composer/composerMentions.tswework/src/components/plugins/PluginsWorkspace.test.tsxwework/src/components/plugins/PluginsWorkspace.tsxwework/src/features/plugins/pluginNavigation.test.tswework/src/features/plugins/pluginNavigation.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- wework/e2e/desktop/task-flow.e2e.mjs
- wework/src/api/local/codexPlugins.ts
| const requestedPluginReference = useMemo(() => parsePluginDetailRoute(window.location.search), []) | ||
| const requestedPluginHandledRef = useRef(false) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check whether PluginsWorkspace is keyed/remounted per full-search navigation, and readState's marketplaceId semantics
rg -n 'PluginsWorkspace' --type=tsx -g '!*.test.tsx' wework/src -C3
rg -n -A20 'function readState' wework/src/api/local/codexPlugins.ts
rg -n 'popstate' wework/src -C3Repository: wecode-ai/Wegent
Length of output: 6590
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== locate relevant files =="
fd -a 'PluginsWorkspace.tsx|codexPlugins.ts|App.tsx|navigation.ts' wework/src | sed 's#^\./##'
echo "== codexPlugins readState =="
sed -n '480,640p' wework/src/api/local/codexPlugins.ts
echo "== AppsPage search route section =="
sed -n '560,720p' wework/src/pages/AppsPage.tsx
echo "== AppsPage MarketplaceWorkspace usage search if applicable =="
rg -n 'MarketplaceWorkspace|readState\(|selectedMarketplaceId|pluginMarketplaceState|readState' wework/src/pages/AppsPage.tsx -C 5
echo "== PluginsWorkspace outline =="
ast-grep outline wework/src/components/plugins/PluginsWorkspace.tsx --view compact | sed -n '1,240p' || trueRepository: wecode-ai/Wegent
Length of output: 11772
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== search route navigation outline =="
ast-grep outline wework/src/pages/plugin-route-navigation.ts --view compact | sed -n '1,220p' || true
echo "== plugin route navigation relevant sections =="
sed -n '1,260p' wework/src/pages/plugin-route-navigation.ts
echo "== marketplace workspace relevant sections =="
sed -n '1320,1595p' wework/src/components/plugins/PluginsWorkspace.tsx
echo "== readState dependency / stale-state usages and skip reload references =="
rg -n 'cachedState|refresh|readState\(|pluginMarketplaceState|selectedMarketplaceId|marketplaceItems|marketplacePath' wework/src/components/plugins/PluginsWorkspace.tsx -C 6
echo "== locate key component route mounts =="
rg -n 'plugin-route-navigation|PluginRouteNavigation|MarketplaceWorkspace|PluginsWorkspace' wework/src --glob '!*.test.*' -C 4Repository: wecode-ai/Wegent
Length of output: 22707
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== codexPlugins installedPluginMatchesReference =="
rg -n -A25 'function installedPluginMatchesReference|installedPluginMatchesReference' wework/src -C 5
echo "== normalizedPluginIdentity and selectedMarketplaceKey helpers =="
rg -n -A10 'function normalizedPluginIdentity|normalizedPluginIdentity|selectedMarketplaceKey|localMarketplaceKey' wework/src -C 5
echo "== local marketplace API readState implementation =="
rg -n 'async readState|readState.*marketplaceId|marketplaceItems|cachedState' wework/src -C 5
echo "== test behavior relevant to deep link marketplaces if available =="
rg -n 'requestedPluginReference|marketplaceName|marketplaceItems|readState\(' wework/src/components/plugins wework/src/pages wework/src/App.plugins.test.tsx -C 4Repository: wecode-ai/Wegent
Length of output: 48974
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== locate parsePluginDetailRoute and useParams hooks =="
rg -n 'function parsePluginDetailRoute|parsePluginDetailRoute|useParams|useSearchParams|useLocation|search' wework/src/pages/PluginsPage.tsx wework/src/components/layout wework/src/App.tsx wework/src/lib -C 4
fd -a 'useSearchParams.*|useLocation.*|useParams.*' wework/src/hooks wework/src/lib | sed 's#^\./##' | xargs -r -I{} sh -c 'echo "--- {}"; cat -n "{}"'
echo "== PluginsPage relevant routing sections =="
sed -n '1,120p' wework/src/pages/PluginsPage.tsx
sed -n '220,285p' wework/src/pages/PluginsPage.tsx
echo "== layout path state effects =="
sed -n '160,215p' wework/src/components/layout/DesktopWorkbenchLayout.tsx
sed -n '220,245p' wework/src/components/layout/MobileWorkbenchLayout.tsxRepository: wecode-ai/Wegent
Length of output: 50372
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== exact parsePluginDetailRoute definition =="
rg -n -A30 'parsePluginDetailRoute' wework/src -C 6
echo "== focused usage of window.location.search in PluginsWorkspace =="
rg -n 'window\.location\.search|location\.search|parsePluginDetailRoute|requestedPluginReference' wework/src/components/plugins/PluginsWorkspace.tsx -C 4
echo "== focused route matching definitions =="
rg -n 'function getAppsSectionFromLocation|function parse.*Route|section.*from.*location|case \"/plugins\"|\"/plugins\"' wework/src wework/src/pages -C 4Repository: wecode-ai/Wegent
Length of output: 10762
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== focused route matching definitions =="
rg -n 'getAppsSectionFromLocation|parse.*Route|section.*from.*location|/plugins|plugins' wework/src wework/src/pages -C 4 | sed -n '1,260p'
echo "== PluginsWorkspace imports around parsePluginDetailRoute usage only =="
sed -n '1,35p' wework/src/components/plugins/PluginsWorkspace.tsxRepository: wecode-ai/Wegent
Length of output: 24078
Re-run deep-link resolution when the plugin route search changes
readState({ marketplaceId }) already scopes marketplace items by marketplace.name, and installed-plugin matching validates both plugin and marketplace names. If PluginsWorkspace is reused for /plugins?...?plugin=…&marketplace=… changes, requestedPluginReference = useMemo(..., []) keeps the initial parsed search while requestedPluginHandledRef blocks later plugin references from being resolved. Reset the one-shot flag or make resolution depend on a normalized route search.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@wework/src/components/plugins/PluginsWorkspace.tsx` around lines 711 - 712,
Update the requested plugin deep-link handling around requestedPluginReference
and requestedPluginHandledRef so route search changes are reparsed and resolved.
Make the memo depend on a normalized current search value, and reset the
one-shot handled flag when that route reference changes, preserving single
handling per distinct plugin/marketplace reference.
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (2)
wework/src/components/sites/SitesWorkspace.tsx (1)
347-360: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicated Create-button markup across both render branches.
The
sitesUnavailablebranch's Create button (347-360) is now near-identical to the one in the main render path (415-428) — same classes, same spinner/plus logic, same label. Consider extracting a sharedCreateSiteButtonsub-component to avoid maintaining two copies in lockstep.As per coding guidelines, "Before adding code, search for and reuse existing components, services, utilities, and patterns; extract shared logic instead of duplicating it."
♻️ Suggested extraction
function CreateSiteButton({ creating, onCreate, label }: { creating: boolean; onCreate: () => void; label: string }) { return ( <button type="button" data-testid="sites-create-button" disabled={creating} onClick={onCreate} className="flex h-8 items-center gap-1.5 rounded-lg border border-border bg-background px-3 text-sm font-medium text-text-primary transition-colors hover:bg-surface disabled:cursor-wait disabled:opacity-60" > {creating ? ( <Loader2 className="h-3.5 w-3.5 animate-spin" aria-hidden="true" /> ) : ( <Plus className="h-3.5 w-3.5" aria-hidden="true" /> )} {label} </button> ) }Also applies to: 415-428
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@wework/src/components/sites/SitesWorkspace.tsx` around lines 347 - 360, Extract the duplicated Create button markup into a shared CreateSiteButton component near the existing SitesWorkspace helpers, accepting creating, onCreate, and label props. Replace the buttons in both the sitesUnavailable branch and the main render path with this component, preserving their current behavior, styling, test ID, icons, disabled state, and translated label.Source: Coding guidelines
backend/app/services/installed_plugin_service.py (1)
315-319: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winHardcoded
user_id=0duplicates theBUILTIN_PLUGIN_OWNER_IDconstant.
builtin_plugin_service.pypublishes built-in items withuser_id=BUILTIN_PLUGIN_OWNER_ID, but this lookup uses the literal0. If the constant's value ever changes, this lookup silently breaks while still type-checking fine.♻️ Proposed fix
+from app.services.builtin_plugin_service import BUILTIN_PLUGIN_OWNER_ID ... row = self._find_marketplace_item( db, - user_id=0, + user_id=BUILTIN_PLUGIN_OWNER_ID, plugin_key=plugin_key, )🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@backend/app/services/installed_plugin_service.py` around lines 315 - 319, Update the _find_marketplace_item call in the built-in plugin lookup to pass BUILTIN_PLUGIN_OWNER_ID instead of the hardcoded user_id=0, reusing the existing constant imported or defined for built-in plugin ownership.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@backend/app/services/builtin_plugin_service.py`:
- Around line 39-55: Update the built-in plugin discovery flow to raise an error
when the source directory is missing or when the required “wegent-sites” plugin
directory is absent, instead of returning an empty list or silently continuing.
Preserve the existing behavior for other optional plugin directories, and ensure
the startup synchronization path receives the failure rather than reporting
success with zero items.
- Around line 93-102: Update the packaging loop in the builtin plugin archive
creation flow to reject symlinks before calling path.stat() or
path.read_bytes(). Use the path’s non-following symlink check and fail packaging
when any staged entry is a symlink, ensuring only regular files physically
contained in plugin_dir are archived.
- Around line 79-84: Update _resolve_plugins_dir so the repository fallback is
used only when BUILTIN_PLUGINS_DIR is the canonical container default; for any
other configured path, including nonexistent absolute paths, return the
configured Path unchanged and let the downstream missing-bundle validation fail.
In `@backend/app/services/installed_plugin_service.py`:
- Around line 308-336: The install path used by install_builtin_plugin and
install_marketplace_plugin must enforce idempotency under concurrent requests.
Add a database-level unique constraint covering user_id, plugin kind, and the
marketplace source identifier (pluginKey/catalogItemId), then handle uniqueness
conflicts by returning the existing InstalledPlugin rather than creating a
duplicate; preserve the current behavior for already-installed and unavailable
plugins.
In `@docs/en/wework/developer-guide/wework-codex-plugins.md`:
- Line 26: Document device synchronization as best-effort by updating
docs/en/wework/developer-guide/wework-codex-plugins.md:26 to say “attempts to
synchronize,” docs/en/wework/plugins-and-skills.md:13 to say “attempts to
synchronize it,” docs/zh/wework/developer-guide/wework-codex-plugins.md:26 to
say “尝试同步到,” and docs/zh/wework/plugins-and-skills.md:17 to say “并尝试同步到.”
In `@wework/e2e/desktop/task-flow.e2e.mjs`:
- Around line 1068-1105: Remove the local canned handlers and fixtures,
including installedSitesPlugin, that intercept builtin-install and marketplace
requests in this E2E flow. Let the test send real backend requests and consume
the backend’s actual response so publication, authentication, idempotency,
persistence, and response-contract behavior are exercised; apply the same change
to the corresponding logic near the additionally referenced section.
In `@wework/src/components/sites/SitesWorkspace.tsx`:
- Around line 166-169: Add the full message as a title attribute on the inner
span rendering message within the SitesWorkspace error display, while preserving
the existing truncate styling and layout. Ensure the title uses the same message
value so users can reveal text that is visually truncated.
---
Nitpick comments:
In `@backend/app/services/installed_plugin_service.py`:
- Around line 315-319: Update the _find_marketplace_item call in the built-in
plugin lookup to pass BUILTIN_PLUGIN_OWNER_ID instead of the hardcoded
user_id=0, reusing the existing constant imported or defined for built-in plugin
ownership.
In `@wework/src/components/sites/SitesWorkspace.tsx`:
- Around line 347-360: Extract the duplicated Create button markup into a shared
CreateSiteButton component near the existing SitesWorkspace helpers, accepting
creating, onCreate, and label props. Replace the buttons in both the
sitesUnavailable branch and the main render path with this component, preserving
their current behavior, styling, test ID, icons, disabled state, and translated
label.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: f83a7651-3bcf-4e7f-92ae-f33c1a89023a
📒 Files selected for processing (37)
.env.example.gitignorebackend/.env.examplebackend/app/api/endpoints/installed_plugins.pybackend/app/core/config.pybackend/app/main.pybackend/app/services/builtin_plugin_service.pybackend/app/services/installed_plugin_service.pybackend/init_data/README.mdbackend/tests/services/test_installed_plugin_upload.pybuild_image.shbuild_image_mac.shdocker-compose.ymldocs/en/wework/developer-guide/wework-codex-plugins.mddocs/en/wework/plugins-and-skills.mddocs/zh/wework/developer-guide/wework-codex-plugins.mddocs/zh/wework/plugins-and-skills.mdpackage.jsonscripts/sync-builtin-sites-plugin.mjswework/e2e/desktop/task-flow.e2e.mjswework/src/App.plugins.test.tsxwework/src/api/adapters.test.tswework/src/api/local/codexPlugins.tswework/src/api/plugins.tswework/src/components/plugins/PluginsWorkspace.test.tsxwework/src/components/plugins/PluginsWorkspace.tsxwework/src/components/sites/SitesWorkspace.test.tsxwework/src/components/sites/SitesWorkspace.tsxwework/src/features/plugins/builtinPlugins.tswework/src/features/plugins/pluginNavigation.test.tswework/src/features/plugins/pluginNavigation.tswework/src/features/plugins/pluginTrial.test.tswework/src/features/plugins/pluginTrial.tswework/src/i18n/locales/en/sites.jsonwework/src/i18n/locales/zh-CN/sites.jsonwework/src/pages/PluginsPage.tsxwework/src/pages/SitesPage.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
- wework/src/features/plugins/pluginNavigation.test.ts
- wework/src/features/plugins/pluginNavigation.ts
- wework/src/pages/SitesPage.tsx
| for path in sorted(plugin_dir.rglob("*")): | ||
| if not path.is_file() or path.name == ".DS_Store": | ||
| continue | ||
| relative_path = path.relative_to(plugin_dir).as_posix() | ||
| info = zipfile.ZipInfo(relative_path, date_time=ZIP_TIMESTAMP) | ||
| info.create_system = 3 | ||
| mode = stat.S_IMODE(path.stat().st_mode) | ||
| info.external_attr = mode << 16 | ||
| info.compress_type = zipfile.ZIP_DEFLATED | ||
| archive.writestr(info, path.read_bytes()) |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Reject symlinks before packaging the public plugin.
is_file() and read_bytes() follow symlinks. A staged plugin can therefore include a symlink to a readable file outside plugin_dir, and its contents are uploaded with visibility="public". Reject symlinks (or enforce resolved paths remain under the plugin root) before reading files.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/app/services/builtin_plugin_service.py` around lines 93 - 102, Update
the packaging loop in the builtin plugin archive creation flow to reject
symlinks before calling path.stat() or path.read_bytes(). Use the path’s
non-following symlink check and fail packaging when any staged entry is a
symlink, ensuring only regular files physically contained in plugin_dir are
archived.
| def install_builtin_plugin( | ||
| self, | ||
| *, | ||
| db: Session, | ||
| user_id: int, | ||
| plugin_key: str, | ||
| ) -> InstalledPlugin: | ||
| row = self._find_marketplace_item( | ||
| db, | ||
| user_id=0, | ||
| plugin_key=plugin_key, | ||
| ) | ||
| if not row or not row.is_active: | ||
| raise HTTPException( | ||
| status_code=503, | ||
| detail=f"Built-in plugin is unavailable: {plugin_key}", | ||
| ) | ||
| spec = self._get_spec(row) | ||
| if spec.get("visibility") != "public": | ||
| raise HTTPException( | ||
| status_code=503, | ||
| detail=f"Built-in plugin is unavailable: {plugin_key}", | ||
| ) | ||
| return self.install_marketplace_plugin( | ||
| db=db, | ||
| user_id=user_id, | ||
| marketplace_id=row.id, | ||
| ) | ||
|
|
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check whether Kind has a DB-level unique constraint that would prevent duplicate InstalledPlugin rows
fd -e py 'kind.py' backend/app/models | xargs cat -n | rg -n -A5 -B5 'UniqueConstraint|unique=True|__table_args__'Repository: wecode-ai/Wegent
Length of output: 154
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate and inspect the relevant service/model/schema definitions and callers.
printf '--- matching files ---\n'
fd -e py 'installed_plugin_service.py|plugins?|migration' backend | sed -n '1,120p'
printf '\n--- installed_plugin_service outline ---\n'
ast-grep outline backend/app/services/installed_plugin_service.py --view expanded || true
printf '\n--- service relevant lines ---\n'
cat -n backend/app/services/installed_plugin_service.py | sed -n '1,420p'
printf '\n--- installed plugin model(s) ---\n'
rg -n "class Installed|InstalledPlugin|installed_plugins|installed_plugin" backend/app/models backend -g '*.py' | sed -n '1,200p'Repository: wecode-ai/Wegent
Length of output: 245
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- matching files ---'
fd -e py 'installed_plugin_service.py|plugins?|migration' backend 2>/dev/null | sed -n '1,160p'
printf '%s\n' ''
printf '%s\n' '--- installed_plugin_service outline ---'
ast-grep outline backend/app/services/installed_plugin_service.py --view expanded 2>/dev/null || true
printf '%s\n' ''
printf '%s\n' '--- service relevant lines ---'
cat -n backend/app/services/installed_plugin_service.py | sed -n '1,420p'
printf '%s\n' ''
printf '%s\n' '--- installed plugin model(s) and usages ---'
rg -n "class Installed|InstalledPlugin|installed_plugins|installed_plugin" backend/app/models backend -g '*.py' 2>/dev/null | sed -n '1,240p'
printf '%s\n' ''
printf '%s\n' '--- constraints/unique installs searches ---'
rg -n "UniqueConstraint|unique=True|__table_args__|install_builtin_plugin|install_marketplace_plugin|_find_installed_plugin" backend -g '*.py' 2>/dev/null | sed -n '1,240p'Repository: wecode-ai/Wegent
Length of output: 39910
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- kind model ---'
cat -n backend/app/models/kind.py | sed -n '1,220p'
printf '%s\n' ''
printf '%s\n' '--- installed plugin service helper section ---'
cat -n backend/app/services/installed_plugin_service.py | sed -n '420,500p'
printf '%s\n' ''
printf '%s\n' '--- installed_plugins API section ---'
cat -n backend/app/api/endpoints/installed_plugins.py | sed -n '90,165p'
printf '%s\n' ''
printf '%s\n' '--- migration references to kinds table / unique constraints ---'
rg -n "kind.*table|InstalledPlugin|PluginMarketplaceItem|UniqueConstraint|unique|create_index|op.add_column|op.alter_column" backend/alembic backend/app/models -g '*.py' 2>/dev/null | sed -n '1,260p'
printf '%s\n' ''
printf '%s\n' '--- behavioral model read for uniqueness ---'
python3 - <<'PY'
import ast
from pathlib import Path
p = Path('backend/app/models/kind.py')
tree = ast.parse(p.read_text())
for node in tree.body:
if not isinstance(node, ast.ClassDef) or node.name != 'Kind':
continue
print("Class Kind found")
for stmt in node.body:
if isinstance(stmt, ast.Assign) and len(stmt.targets) == 1:
target = stmt.targets[0]
if isinstance(target, ast.Name) and target.id in {"__tablename__", "__table_args__"}:
print(ast.unparse(stmt))
PYRepository: wecode-ai/Wegent
Length of output: 21909
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- installed plugin upload/concurrence tests ---'
cat -n backend/tests/services/test_installed_plugin_upload.py | sed -n '100,260p'
printf '%s\n' ''
printf '%s\n' '--- kinds performance indexes migration ---'
cat -n backend/alembic/versions/20260314_a7b8c9d0e1f3_add_kinds_performance_indexes.py | sed -n '1,160p'
printf '%s\n' ''
printf '%s\n' '--- shared Kind model ---'
if [ -f shared/models/db.py ]; then
cat -n shared/models/db.py | sed -n '1,220p'
elif [ -f shared/models/kind.py ]; then
cat -n shared/models/kind.py | sed -n '1,220p'
else
fd -e py 'db.py|kind.py' shared -x cat -n {} | sed -n '1,260p'
fi
printf '%s\n' ''
printf '%s\n' '--- precise uniques and index uniqueness in relevant migrations/models ---'
python3 - <<'PY'
import ast
from pathlib import Path
terms = {
"InstalledPlugin",
"PluginMarketplaceItem",
"__table_args__",
"UniqueConstraint",
"unique=True",
"create_index",
"create_unique_constraint",
"op.add_column",
}
for path in (
Path("shared/models/db.py"),
Path("backend/app/models/kind.py"),
Path("backend/alembic/versions/20260314_a7b8c9d0e1f3_add_kinds_performance_indexes.py"),
):
if not path.exists():
continue
print(f"FILE {path}")
text = path.read_text()
tree = ast.parse(text)
for line_no, line in enumerate(text.splitlines(), 1):
if any(t in line for t in terms):
print(f"{line_no}: {line.strip()}")
print("--- AST hits ---")
for node in ast.walk(tree):
if isinstance(node, (ast.Assign, ast.CreateIndex, ast.CreateUniqueConstraint, ast.Call)):
src = ast.get_source_segment(text, node) or ""
if any(t in src for t in terms):
print(ast.dump(node, include_attributes=True)[:400] if hasattr(ast, "dump") else str(node))
PYRepository: wecode-ai/Wegent
Length of output: 10606
Make ensure-installed idempotent under concurrency.
install_builtin_plugin() documents the /builtin/{plugin_key}/ensure-installed endpoint as idempotent, but it relies only on _find_installed_plugin() before creating a new InstalledPlugin row. There is no DB-level unique constraint for (user_id, kind, source pluginKey/catalogItemId), so concurrent calls for the same user can both miss the existing install and create duplicate rows. Add the missing uniqueness protection or use atomic/locking behavior for the install path.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/app/services/installed_plugin_service.py` around lines 308 - 336, The
install path used by install_builtin_plugin and install_marketplace_plugin must
enforce idempotency under concurrent requests. Add a database-level unique
constraint covering user_id, plugin kind, and the marketplace source identifier
(pluginKey/catalogItemId), then handle uniqueness conflicts by returning the
existing InstalledPlugin rather than creating a duplicate; preserve the current
behavior for already-installed and unavailable plugins.
| function installedSitesPlugin() { | ||
| const marketplacePlugin = sitesMarketplacePlugin(true) | ||
| return { | ||
| apiVersion: 'agent.wecode.io/v1', | ||
| kind: 'InstalledPlugin', | ||
| metadata: { | ||
| name: 'wegent-sites', | ||
| namespace: 'default', | ||
| labels: { id: '601' }, | ||
| }, | ||
| spec: { | ||
| source: { | ||
| type: 'marketplace', | ||
| providerKey: 'wegent-marketplace', | ||
| pluginKey: 'wegent-sites', | ||
| catalogItemId: '501', | ||
| marketplace: 'wegent', | ||
| }, | ||
| displayName: '站点', | ||
| description: marketplacePlugin.description, | ||
| version: marketplacePlugin.version, | ||
| author: marketplacePlugin.author, | ||
| installState: 'installed', | ||
| enabled: true, | ||
| componentStates: {}, | ||
| manifest: marketplacePlugin.manifest, | ||
| components: marketplacePlugin.components, | ||
| interface: marketplacePlugin.interface, | ||
| packageRef: { | ||
| storageKey: 'skill-binaries/601', | ||
| checksum: 'sha256:desktop-e2e-sites', | ||
| sizeBytes: 1024, | ||
| }, | ||
| sourcePayload: { filename: 'wegent-sites.zip' }, | ||
| }, | ||
| status: { state: 'Available' }, | ||
| } | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
Exercise the real backend for this E2E flow.
These local handlers replace the new builtin-install and marketplace requests with canned responses, and the marketplace response is always marked installed. Consequently, this test does not validate backend publication, authentication, idempotency, persistence, or the real response contract.
As per coding guidelines, E2E tests must use real backend requests and must not replace integrations with mocks.
Also applies to: 2821-2833
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@wework/e2e/desktop/task-flow.e2e.mjs` around lines 1068 - 1105, Remove the
local canned handlers and fixtures, including installedSitesPlugin, that
intercept builtin-install and marketplace requests in this E2E flow. Let the
test send real backend requests and consume the backend’s actual response so
publication, authentication, idempotency, persistence, and response-contract
behavior are exercised; apply the same change to the corresponding logic near
the additionally referenced section.
Source: Coding guidelines
| <span className="flex min-w-0 items-center gap-2 text-sm text-text-secondary"> | ||
| <AlertCircle className="h-4 w-4 shrink-0 text-danger" aria-hidden="true" /> | ||
| <span className="truncate">{message}</span> | ||
| </span> |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Truncated error text has no way to reveal the full message.
message is truncated with CSS ellipsis but no title attribute is set, so sighted users on narrow viewports can't see the full error (e.g. the longer backend-upgrade-required copy).
🩹 Proposed fix
<span className="flex min-w-0 items-center gap-2 text-sm text-text-secondary">
<AlertCircle className="h-4 w-4 shrink-0 text-danger" aria-hidden="true" />
- <span className="truncate">{message}</span>
+ <span className="truncate" title={message}>{message}</span>
</span>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <span className="flex min-w-0 items-center gap-2 text-sm text-text-secondary"> | |
| <AlertCircle className="h-4 w-4 shrink-0 text-danger" aria-hidden="true" /> | |
| <span className="truncate">{message}</span> | |
| </span> | |
| <span className="flex min-w-0 items-center gap-2 text-sm text-text-secondary"> | |
| <AlertCircle className="h-4 w-4 shrink-0 text-danger" aria-hidden="true" /> | |
| <span className="truncate" title={message}>{message}</span> | |
| </span> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@wework/src/components/sites/SitesWorkspace.tsx` around lines 166 - 169, Add
the full message as a title attribute on the inner span rendering message within
the SitesWorkspace error display, while preserving the existing truncate styling
and layout. Ensure the title uses the same message value so users can reveal
text that is visually truncated.
11b6f96 to
5c0e2f4
Compare
3ca87ac to
8cf01f4
Compare
8cf01f4 to
de55596
Compare
Summary by CodeRabbit