Skip to content

feat(platforms): add Hermes Agent platform support#110

Merged
legeling merged 3 commits intolegeling:mainfrom
caohong:debug-hermes
Apr 30, 2026
Merged

feat(platforms): add Hermes Agent platform support#110
legeling merged 3 commits intolegeling:mainfrom
caohong:debug-hermes

Conversation

@caohong
Copy link
Copy Markdown
Contributor

@caohong caohong commented Apr 29, 2026

Summary

Add Hermes Agent as a custom skill platform with full support for nested skill scanning and icon display.

Changes

File Description
apps/desktop/src/main/services/skill-installer.ts Add Hermes Agent platform with nested skill scanning support
packages/shared/constants/platforms.ts Add Hermes platform constant definition
apps/desktop/src/renderer/components/ui/PlatformIcon.tsx Add Hermes icon to platform icon mapping
apps/desktop/src/renderer/assets/platforms/hermes.svg Hermes Agent platform icon asset

Test Status

  • 902/947 tests pass - 45 failing tests are pre-existing localStorage/storage mock issues (unrelated to this change)

Commits

76e25e0 feat(platforms): add Hermes Agent icon for skill platform UI
759f799 feat(desktop): add Hermes Agent platform and fix nested skill scanning

This PR follows the contribution guidelines in CONTRIBUTING.md

Summary by CodeRabbit

发布说明

  • 新功能

    • 添加对 Hermes Agent 平台的支持,包含平台图标与系统技能目录配置。
  • 改进

    • 增强本地技能发现,支持在嵌套目录中定位技能文件,提升兼容性。
    • 优化扫描展示信息与回退策略,使用更可靠的名称/描述来源并在嵌套遍历失败时记录警告以继续扫描。

caohong added 2 commits April 29, 2026 08:59
- Add Hermes Agent to SKILL_PLATFORMS with skills dir at ~/.hermes/skills
- Support one-level nested directory scanning for category-nested SKILL.md files
  (e.g., ~/.hermes/skills/apple/apple-reminders/SKILL.md)
- Add hermes.svg icon asset (Caduceus/double-snake staff design)
- Register Hermes in PLATFORM_ICONS and FALLBACK_ICONS mappings
- Hermes platform entry already existed in platforms.ts, but the UI
  had no icon import, so Hermes appeared without an icon in the UI
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 29, 2026

@caohong is attempting to deploy a commit to the legeling's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 29, 2026

📝 Walkthrough

Walkthrough

新增对 Hermes Agent 平台的支持(常量与 UI 图标映射),并重构桌面端技能扫描逻辑以在扫描路径的一层或两层子目录中发现 SKILL.md,调整展示名/描述回退来源及局部错误处理与日志策略。

Changes

Cohort / File(s) Summary
Hermes 平台常量
packages/shared/constants/platforms.ts
新增 id 为 hermes 的平台条目,包含名称、图标引用与各平台(darwin/win32/linux)的默认技能目录。
UI 图标映射
apps/desktop/src/renderer/components/ui/PlatformIcon.tsx
引入 Hermes SVG,添加至 PLATFORM_ICONS;为 Hermes 提供 BotIcon 回退以处理无图或加载失败场景。
技能扫描逻辑增强
apps/desktop/src/main/services/skill-installer.ts
新增 collectSkillDirs 私有静态函数以在子目录或二级嵌套中查找 SKILL.mdscanLocalscanLocalPreview 重构为使用该助手,使用 path.basename(skillFolderPath) 作为名称回退,作者/描述回退改为基于 manifest(缺失时为 undefined),并在嵌套读取失败时局部记录警告后继续扫描。

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

诗歌

🐰 我挖出新径寻技能,轻嗅 Hermes 的风,

嵌套目录不再藏匿,清单与图标都整齐,
小警告悄声飘过,扫描继续向前冲,
新平台加入宴席,兔耳欢跃乐融融。

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding Hermes Agent platform support. It is concise, specific, and directly aligned with the primary changeset objective across all modified files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@qodo-code-review
Copy link
Copy Markdown

Review Summary by Qodo

Add Hermes Agent platform with nested skill scanning

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add Hermes Agent platform with nested skill directory scanning support
• Support one-level nested directory structures for category-organized skills
• Add Hermes platform icon asset and UI mappings
• Refactor skill scanning logic to handle both flat and nested skill layouts
Diagram
flowchart LR
  A["Hermes Agent Platform"] --> B["Nested Skill Scanning"]
  A --> C["Platform Icon Asset"]
  B --> D["Support flat & nested layouts"]
  C --> E["UI Icon Mappings"]
  E --> F["PlatformIcon Component"]
Loading

Grey Divider

File Changes

1. apps/desktop/src/main/services/skill-installer.ts ✨ Enhancement +70/-26

Implement nested skill directory scanning logic

• Refactored skill scanning logic to support one-level nested directory structures
• Changed from checking entry.name to using path.basename(skillFolderPath) for consistency
• Added two-phase directory checking: first for direct SKILL.md, then for nested subdirectories
• Applied same nested scanning pattern to both scanSkillsFromDefaultPaths() and
 scanSkillsFromPlatforms() methods

apps/desktop/src/main/services/skill-installer.ts


2. packages/shared/constants/platforms.ts ⚙️ Configuration changes +10/-0

Register Hermes Agent platform configuration

• Added Hermes Agent platform entry to SKILL_PLATFORMS array
• Configured skills directory paths for darwin, win32, and linux platforms
• Set platform icon to "Bot" for UI display

packages/shared/constants/platforms.ts


3. apps/desktop/src/renderer/components/ui/PlatformIcon.tsx ✨ Enhancement +3/-0

Add Hermes icon to platform UI mappings

• Imported hermes.svg icon asset
• Added hermes platform to PLATFORM_ICONS mapping
• Added hermes platform to FALLBACK_ICONS mapping with BotIcon

apps/desktop/src/renderer/components/ui/PlatformIcon.tsx


View more (1)
4. apps/desktop/src/renderer/assets/platforms/hermes.svg 📝 Documentation +0/-0

Add Hermes platform icon asset

• New SVG icon asset for Hermes Agent platform
• Caduceus/double-snake staff design representing Hermes

apps/desktop/src/renderer/assets/platforms/hermes.svg


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review Bot commented Apr 29, 2026

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (1) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Empty catch hides fs errors📘 Rule violation ☼ Reliability
Description
catch {} was added around directory reads during skill scanning, silently swallowing errors and
making missing skills/debugging failures hard to detect. This violates the requirement to avoid
silent failures in catch blocks.
Code

apps/desktop/src/main/services/skill-installer.ts[539]

+              } catch { /* ignore directory read errors */ }
Evidence
PR Compliance ID 4 forbids empty catch blocks and silent failures. The new `catch { /* ignore
directory read errors */ } blocks swallow fs.readdir()` failures without logging or propagating,
so scan results can be incomplete with no diagnostic output.

AGENTS.md
apps/desktop/src/main/services/skill-installer.ts[528-540]
apps/desktop/src/main/services/skill-installer.ts[685-697]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Skill scanning introduces empty `catch {}` blocks that silently ignore `fs.readdir()` failures, leading to missing skills with no diagnostics.
## Issue Context
The scanner now checks nested directories (e.g., Hermes category nesting). If a nested directory is unreadable, the current code drops it without logging or surfacing the failure.
## Fix Focus Areas
- apps/desktop/src/main/services/skill-installer.ts[528-540]
- apps/desktop/src/main/services/skill-installer.ts[685-697]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Hardcoded Hermes Agent name 📘 Rule violation ⚙ Maintainability
Description
A new user-visible platform label Hermes Agent is hardcoded in source instead of being localized
via react-i18next. This introduces non-localized UI text in the platform list/settings UI.
Code

packages/shared/constants/platforms.ts[R176-183]

+    id: "hermes",
+    name: "Hermes Agent",
+    icon: "Bot",
+    skillsDir: {
+      darwin: "~/.hermes/skills",
+      win32: "%USERPROFILE%\\.hermes\\skills",
+      linux: "~/.hermes/skills",
+    },
Evidence
PR Compliance ID 7 forbids hardcoded user-facing strings in source. platform.name is rendered
directly in the settings UI, and the PR adds name: "Hermes Agent" as a new literal string.

AGENTS.md
packages/shared/constants/platforms.ts[175-184]
apps/desktop/src/renderer/components/settings/SkillSettings.tsx[367-380]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A new platform display name is hardcoded (`Hermes Agent`) and is rendered directly in the UI.
## Issue Context
`SKILL_PLATFORMS` is used by the renderer to display `platform.name` in settings, so literals added here become user-facing UI strings.
## Fix Focus Areas
- packages/shared/constants/platforms.ts[175-184]
- apps/desktop/src/renderer/components/settings/SkillSettings.tsx[367-380]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Hardcoded Local skill found text📘 Rule violation ⚙ Maintainability
Description
New/modified fallback text for skill descriptions and authors is hardcoded in the main-process
scanning/import flow. This can surface non-localized strings to users via stored skill metadata.
Code

apps/desktop/src/main/services/skill-installer.ts[R552-562]

              const sanitized = sanitizeImportedSkillDraft(
                {
                  name: parsedSkill?.frontmatter.name,
-                    fallbackName: manifest.name || entry.name,
+                    fallbackName: manifest.name || path.basename(skillFolderPath),
                  description: parsedSkill?.frontmatter.description,
                  fallbackDescription:
                    manifest.description ||
-                      `Local skill found in ${entry.name}`,
+                      `Local skill found in ${path.basename(skillFolderPath)}`,
                  version: parsedSkill?.frontmatter.version,
                  fallbackVersion: manifest.version,
                  author: parsedSkill?.frontmatter.author,
Evidence
PR Compliance ID 7 forbids introducing hardcoded user-facing strings. The PR modifies/introduces
literal fallback strings like Local skill found in ... and Local for author when importing
skills, which are likely displayed as metadata in the UI.

AGENTS.md
apps/desktop/src/main/services/skill-installer.ts[552-565]
apps/desktop/src/main/services/skill-installer.ts[727-754]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The skill import/scan flow hardcodes fallback description/author strings (e.g., `Local skill found in ...`, `Local`). These are likely user-visible and should be localized.
## Issue Context
This code runs in the main process and populates skill metadata. A common approach is to store `null`/empty fallbacks (or a stable key) and let the renderer format the user-visible fallback with `t()`.
## Fix Focus Areas
- apps/desktop/src/main/services/skill-installer.ts[552-565]
- apps/desktop/src/main/services/skill-installer.ts[727-754]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Relative import for hermesIcon📘 Rule violation ⚙ Maintainability
Description
The new Hermes icon import uses a relative path even though @renderer/* path aliases are
configured. This violates the module import alias convention and reduces import
consistency/maintainability.
Code

apps/desktop/src/renderer/components/ui/PlatformIcon.tsx[33]

+import hermesIcon from "../../assets/platforms/hermes.svg";
Evidence
PR Compliance ID 28 requires using defined path aliases like @renderer/ for renderer imports. The
PR adds import hermesIcon from "../../assets/platforms/hermes.svg"; even though @renderer/* is
configured in tsconfig.json.

AGENTS.md
apps/desktop/src/renderer/components/ui/PlatformIcon.tsx[33-33]
apps/desktop/tsconfig.json[19-27]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A new renderer asset import uses a relative path despite an existing `@renderer/*` alias.
## Issue Context
The desktop TS config defines `@renderer/*` -> `src/renderer/*`, so assets under `src/renderer/assets/...` can be imported via the alias.
## Fix Focus Areas
- apps/desktop/src/renderer/components/ui/PlatformIcon.tsx[33-33]
- apps/desktop/tsconfig.json[19-27]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Advisory comments

5. Duplicated nested scan logic🐞 Bug ⚙ Maintainability
Description
The nested directory discovery logic (dirsToCheck/baseDir/skillDirs) is duplicated in both
scanLocal() and scanLocalPreview(), making it easy for future fixes/platform-specific nesting rules
to be applied to one path but not the other. This increases long-term risk of inconsistent skill
discovery behavior between import and preview.
Code

apps/desktop/src/main/services/skill-installer.ts[R672-699]

+          const dirsToCheck: string[] = [];
        for (const entry of entries) {
          if (entry.isDirectory()) {
-              const skillFolderPath = path.join(scanPath, entry.name);
-              const skillMdPath = path.join(skillFolderPath, "SKILL.md");
+              dirsToCheck.push(path.join(scanPath, entry.name));
+            }
+          }
-              if (await fileExists(skillMdPath)) {
-                try {
+          for (const baseDir of dirsToCheck) {
+            const skillDirs: string[] = [];
+            const directMd = path.join(baseDir, "SKILL.md");
+            if (await fileExists(directMd)) {
+              skillDirs.push(baseDir);
+            } else {
+              // Check subdirectories for category-nested structures (e.g., Hermes)
+              try {
+                const subEntries = await fs.readdir(baseDir, { withFileTypes: true });
+                for (const sub of subEntries) {
+                  if (sub.isDirectory()) {
+                    const nestedDir = path.join(baseDir, sub.name);
+                    if (await fileExists(path.join(nestedDir, "SKILL.md"))) {
+                      skillDirs.push(nestedDir);
+                    }
+                  }
+                }
+              } catch { /* ignore directory read errors */ }
+            }
+
+            for (const skillFolderPath of skillDirs) {
Evidence
The same nested scanning algorithm was added twice (once for import, once for preview). Any future
change (e.g., deeper nesting, filtering, error handling) must be duplicated perfectly to avoid
behavior drift.

apps/desktop/src/main/services/skill-installer.ts[512-617]
apps/desktop/src/main/services/skill-installer.ts[670-770]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Nested skill directory discovery logic is duplicated across `scanLocal()` and `scanLocalPreview()`.
### Issue Context
Both methods build `dirsToCheck`, detect `SKILL.md` directly or one-level nested, then iterate `skillDirs`. This is the same logic with minor context differences.
### Fix Focus Areas
- apps/desktop/src/main/services/skill-installer.ts[512-617]
- apps/desktop/src/main/services/skill-installer.ts[670-770]
### Suggested fix
- Extract a private helper like `discoverSkillDirs(scanPath: string): Promise<string[]>` or `discoverSkillDirsUnder(baseDir: string): Promise<string[]>`.
- Reuse it from both scan methods, keeping platform-specific metadata handling (e.g., `platformName`, `skillMap`) in the callers.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread apps/desktop/src/main/services/skill-installer.ts Outdated
Comment on lines +176 to +183
id: "hermes",
name: "Hermes Agent",
icon: "Bot",
skillsDir: {
darwin: "~/.hermes/skills",
win32: "%USERPROFILE%\\.hermes\\skills",
linux: "~/.hermes/skills",
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Hardcoded hermes agent name 📘 Rule violation ⚙ Maintainability

A new user-visible platform label Hermes Agent is hardcoded in source instead of being localized
via react-i18next. This introduces non-localized UI text in the platform list/settings UI.
Agent Prompt
## Issue description
A new platform display name is hardcoded (`Hermes Agent`) and is rendered directly in the UI.

## Issue Context
`SKILL_PLATFORMS` is used by the renderer to display `platform.name` in settings, so literals added here become user-facing UI strings.

## Fix Focus Areas
- packages/shared/constants/platforms.ts[175-184]
- apps/desktop/src/renderer/components/settings/SkillSettings.tsx[367-380]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread apps/desktop/src/main/services/skill-installer.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
apps/desktop/src/main/services/skill-installer.ts (1)

515-540: 建议抽取共享的“目录发现”逻辑,避免双处实现漂移。

scanLocalscanLocalPreview 中一层嵌套扫描实现基本重复,后续若再改扫描规则容易出现不一致。建议提取为私有 helper(例如 collectSkillDirs(scanPath))复用。

Also applies to: 672-697

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/services/skill-installer.ts` around lines 515 - 540,
The directory discovery logic in scanLocal and scanLocalPreview is duplicated
and should be extracted into a single private helper (e.g.,
collectSkillDirs(scanPath)) to avoid drift; implement collectSkillDirs that
accepts a scanPath, uses the existing pattern (iterate entries, push
directories, check for SKILL.md directly and in one-level subdirectories using
fileExists and fs.readdir with withFileTypes), returns the array of discovered
skill directories, and replace the duplicated loops in scanLocal and
scanLocalPreview to call collectSkillDirs(scanPath) so all directory-resolution
logic (including error handling) lives in one place.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/desktop/src/main/services/skill-installer.ts`:
- Line 539: Two empty catch blocks that read "catch { /* ignore directory read
errors */ }" silently swallow filesystem errors; replace each with a catch (err)
{ logger.warn("Failed reading skill directory, skipping", err) } (or
logger.debug if more appropriate) so EACCES/EPERM and other IO errors are
recorded; locate the two occurrences of the exact snippet "catch { /* ignore
directory read errors */ }" in skill-installer.ts and use the module's existing
logger (or console.warn if no logger is available) to log the error with context
instead of swallowing it.

In `@apps/desktop/src/renderer/components/ui/PlatformIcon.tsx`:
- Line 33: PlatformIcon currently imports platform SVG/PNG assets (e.g.,
hermes.svg) which violates AGENTS.md; update the PlatformIcon component so it
uses lucide-react icons as the primary source: remove direct asset imports
(e.g., hermesIcon and other platform asset imports), create a mapping from
platform identifiers to lucide-react icon components inside PlatformIcon, render
the lucide-react icon first (with the existing fallback behavior preserved to
load legacy asset files only if no matching lucide icon exists), and add a
comment referencing AGENTS.md to document any deliberate exceptions if you must
keep a legacy asset fallback.

---

Nitpick comments:
In `@apps/desktop/src/main/services/skill-installer.ts`:
- Around line 515-540: The directory discovery logic in scanLocal and
scanLocalPreview is duplicated and should be extracted into a single private
helper (e.g., collectSkillDirs(scanPath)) to avoid drift; implement
collectSkillDirs that accepts a scanPath, uses the existing pattern (iterate
entries, push directories, check for SKILL.md directly and in one-level
subdirectories using fileExists and fs.readdir with withFileTypes), returns the
array of discovered skill directories, and replace the duplicated loops in
scanLocal and scanLocalPreview to call collectSkillDirs(scanPath) so all
directory-resolution logic (including error handling) lives in one place.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5ccd66a2-6bab-4557-95e3-4a6d7f852acd

📥 Commits

Reviewing files that changed from the base of the PR and between 540969d and 76e25e0.

⛔ Files ignored due to path filters (1)
  • apps/desktop/src/renderer/assets/platforms/hermes.svg is excluded by !**/*.svg
📒 Files selected for processing (3)
  • apps/desktop/src/main/services/skill-installer.ts
  • apps/desktop/src/renderer/components/ui/PlatformIcon.tsx
  • packages/shared/constants/platforms.ts

Comment thread apps/desktop/src/main/services/skill-installer.ts Outdated
import qoderworkIcon from "../../assets/platforms/qoderwork.png";
import codebuddyLightIcon from "../../assets/platforms/codebuddy-light.svg";
import codebuddyDarkIcon from "../../assets/platforms/codebuddy-dark.svg";
import hermesIcon from "../../assets/platforms/hermes.svg";
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) 定位规范文档(大小写不敏感)
fd -i 'gemini.md'

# 2) 查看规范中与图标约束相关的内容
fd -i 'gemini.md' --exec rg -n "lucide-react|icon|SVG|custom" {}

# 3) 盘点 renderer/components 下平台资产导入现状,判断是否已有统一例外
rg -n --type=tsx 'assets/platforms/.*\.(svg|png)' apps/desktop/src/renderer/components

Repository: legeling/PromptHub

Length of output: 90


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) 定位并查看 GEMINI.md 规范文档
echo "=== 查找 GEMINI.md ==="
find . -iname "gemini.md" -type f

# 2) 使用正确的文件类型搜索 tsx 文件中的 SVG 导入
echo "=== 搜索 renderer/components 中的 SVG/PNG 平台资产导入 ==="
rg -t ts 'assets/platforms' apps/desktop/src/renderer/components

# 3) 查看 PlatformIcon.tsx 文件的完整实现
echo "=== 查看 PlatformIcon.tsx 内容 ==="
cat -n apps/desktop/src/renderer/components/ui/PlatformIcon.tsx

Repository: legeling/PromptHub

Length of output: 8729


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) 查找可能的规范文档(README、GUIDELINES、STYLE等)
echo "=== 查找规范相关文档 ==="
find . -maxdepth 2 -type f \( -iname "README*" -o -iname "*GUIDELINE*" -o -iname "*STYLE*" -o -iname "*CONVENTION*" -o -iname "*.md" \) | head -20

# 2) 搜索仓库中关于图标规范的任何提及
echo "=== 搜索仓库中关于 lucide-react 图标的提及 ==="
rg -t md "lucide-react" | head -20

# 3) 检查是否有其他组件同样使用自定义 SVG/PNG 资产
echo "=== 检查其他组件的图标导入模式 ==="
rg -t ts "import.*from.*assets/.*\.(svg|png)" apps/desktop/src/renderer/components --count

Repository: legeling/PromptHub

Length of output: 835


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 查看 AGENTS.md 中的图标规范部分
echo "=== AGENTS.md 中的图标规范 ==="
rg -B 5 -A 5 "lucide-react" AGENTS.md

Repository: legeling/PromptHub

Length of output: 1770


整个 PlatformIcon.tsx 违反了 AGENTS.md 中"仅使用 lucide-react"的规范。

Line 33 的 hermes.svg 导入遵循了文件既有的模式,但该模式本身违反架构规范。AGENTS.md 明确要求:"Use lucide-react for all icons. Do not import other icon libraries"。

该文件包含 18+ 个自定义平台资产(PNG 和 SVG)导入,应重构为:

  • 仅使用 lucide-react 作为主要图标源,或
  • 为平台图标建立明确的例外规则并在 AGENTS.md 中记录

组件的 fallback 机制设计合理,但当前实现优先加载自定义资产而非默认使用 lucide。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/components/ui/PlatformIcon.tsx` at line 33,
PlatformIcon currently imports platform SVG/PNG assets (e.g., hermes.svg) which
violates AGENTS.md; update the PlatformIcon component so it uses lucide-react
icons as the primary source: remove direct asset imports (e.g., hermesIcon and
other platform asset imports), create a mapping from platform identifiers to
lucide-react icon components inside PlatformIcon, render the lucide-react icon
first (with the existing fallback behavior preserved to load legacy asset files
only if no matching lucide icon exists), and add a comment referencing AGENTS.md
to document any deliberate exceptions if you must keep a legacy asset fallback.

- Extract collectSkillDirs() shared helper to eliminate duplicated nested
  scan logic between scanLocal() and scanLocalPreview()
- Replace empty catch{} blocks with console.warn() to avoid silently
  swallowing fs.readdir() errors
- Replace hardcoded fallback strings ("Local skill found in ...", "Local")
  with undefined to let sanitizeImportedSkillDraft handle defaults
- Use @renderer/ path alias for hermes.svg import instead of relative path

Note: "Hermes Agent" platform name follows the same convention as all
other 15 platforms in platforms.ts (all use hardcoded English names).
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/desktop/src/main/services/skill-installer.ts`:
- Around line 526-547: The collectSkillDirs logic currently stops scanning
subdirectories when baseDir contains SKILL.md (the else at the directMd check),
missing nested skill dirs; modify collectSkillDirs so that after detecting
directMd (directMd or baseDir/SKILL.md) it still scans the immediate children:
always run the fs.readdir + subEntries loop (or remove the else) for baseDir,
checking each sub.isDirectory() and testing path.join(nestedDir, "SKILL.md") to
push nestedDir into result; keep the existing try/catch and only skip on real
read errors.
- Line 642: There are extra closing braces before two catch blocks that break
the try/catch pairing in skill-installer.ts; remove the stray '}' immediately
before each "catch (e)" occurrence so that the catch clauses directly follow
their corresponding try blocks (specifically the two "catch (e)" blocks shown in
the diff), ensuring the try/catch structures around those sections (look for the
surrounding try blocks and the nearby functions in skill-installer.ts) are
properly paired and parse without error.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 848e4fdf-b27b-468c-93ce-0a8ed42d77b7

📥 Commits

Reviewing files that changed from the base of the PR and between 76e25e0 and f81d1b1.

📒 Files selected for processing (2)
  • apps/desktop/src/main/services/skill-installer.ts
  • apps/desktop/src/renderer/components/ui/PlatformIcon.tsx
✅ Files skipped from review due to trivial changes (1)
  • apps/desktop/src/renderer/components/ui/PlatformIcon.tsx

Comment on lines +526 to +547
if (await fileExists(directMd)) {
result.push(baseDir);
} else {
// Check subdirectories for category-nested structures (e.g., Hermes)
try {
const subEntries = await fs.readdir(baseDir, { withFileTypes: true });
for (const sub of subEntries) {
if (sub.isDirectory()) {
const nestedDir = path.join(baseDir, sub.name);
if (await fileExists(path.join(nestedDir, "SKILL.md"))) {
result.push(nestedDir);
}
}
}
} catch (err) {
console.warn(
`Failed reading skill directory: ${baseDir}, skipping`,
err,
);
}
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

collectSkillDirs 会漏扫“父目录和子目录都含 SKILL.md”的场景。

Line 528 的 else 使得只要 baseDir/SKILL.md 存在,就不会继续扫描一层子目录;这会漏掉同目录树下的嵌套技能。

🔧 建议修正
-      if (await fileExists(directMd)) {
-        result.push(baseDir);
-      } else {
-        // Check subdirectories for category-nested structures (e.g., Hermes)
-        try {
-          const subEntries = await fs.readdir(baseDir, { withFileTypes: true });
-          for (const sub of subEntries) {
-            if (sub.isDirectory()) {
-              const nestedDir = path.join(baseDir, sub.name);
-              if (await fileExists(path.join(nestedDir, "SKILL.md"))) {
-                result.push(nestedDir);
-              }
-            }
-          }
-        } catch (err) {
-          console.warn(
-            `Failed reading skill directory: ${baseDir}, skipping`,
-            err,
-          );
-        }
-      }
+      if (await fileExists(directMd)) {
+        result.push(baseDir);
+      }
+
+      // 也扫描一层子目录,避免漏掉“父目录与子目录都含 SKILL.md”的情况
+      try {
+        const subEntries = await fs.readdir(baseDir, { withFileTypes: true });
+        for (const sub of subEntries) {
+          if (sub.isDirectory()) {
+            const nestedDir = path.join(baseDir, sub.name);
+            if (await fileExists(path.join(nestedDir, "SKILL.md"))) {
+              result.push(nestedDir);
+            }
+          }
+        }
+      } catch (err) {
+        console.warn(
+          `Failed reading skill directory: ${baseDir}, skipping`,
+          err,
+        );
+      }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/services/skill-installer.ts` around lines 526 - 547,
The collectSkillDirs logic currently stops scanning subdirectories when baseDir
contains SKILL.md (the else at the directMd check), missing nested skill dirs;
modify collectSkillDirs so that after detecting directMd (directMd or
baseDir/SKILL.md) it still scans the immediate children: always run the
fs.readdir + subEntries loop (or remove the else) for baseDir, checking each
sub.isDirectory() and testing path.join(nestedDir, "SKILL.md") to push nestedDir
into result; keep the existing try/catch and only skip on real read errors.

}
}
} catch (e) {
} catch (e) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
sed -n '620,780p' apps/desktop/src/main/services/skill-installer.ts
rg -n '^\s*}\s*catch\s*\(' apps/desktop/src/main/services/skill-installer.ts

Repository: legeling/PromptHub

Length of output: 7340


🏁 Script executed:

# Get the exact number of lines in the file to understand its structure
wc -l apps/desktop/src/main/services/skill-installer.ts

Repository: legeling/PromptHub

Length of output: 116


🏁 Script executed:

# Read the specific sections around lines 640-650 and 765-775 to see bracket matching
sed -n '635,650p' apps/desktop/src/main/services/skill-installer.ts | cat -n
echo "---"
sed -n '760,775p' apps/desktop/src/main/services/skill-installer.ts | cat -n

Repository: legeling/PromptHub

Length of output: 1100


🏁 Script executed:

# Check if there's a TypeScript/JavaScript syntax validator available
# Try to find any configuration files for linters like Biome
find . -maxdepth 2 -name "biome.json" -o -name ".biomerc" -o -name "tsconfig.json" | head -10

Repository: legeling/PromptHub

Length of output: 75


🏁 Script executed:

# Count opening and closing braces in relevant sections to verify structure
python3 << 'PYEOF'
import re

# Read the file
with open('apps/desktop/src/main/services/skill-installer.ts', 'r') as f:
    lines = f.readlines()

# Extract sections around problematic lines
section1 = lines[620:650]  # Around line 642
section2 = lines[755:780]  # Around line 768

def analyze_braces(lines, start_line):
    """Analyze brace balance in a section"""
    open_count = 0
    close_count = 0
    for i, line in enumerate(lines):
        open_count += line.count('{')
        close_count += line.count('}')
    return open_count, close_count

o1, c1 = analyze_braces(section1, 620)
o2, c2 = analyze_braces(section2, 755)

print(f"Section 1 (lines 620-650): {o1} opens, {c1} closes, diff={o1-c1}")
print(f"Section 2 (lines 755-780): {o2} opens, {c2} closes, diff={o2-c2}")

# Print the actual lines to inspect
print("\n=== Section 1 (lines 620-650) ===")
for i, line in enumerate(section1, start=620):
    print(f"{i}: {line.rstrip()}")

print("\n=== Section 2 (lines 755-780) ===")
for i, line in enumerate(section2, start=755):
    print(f"{i}: {line.rstrip()}")
PYEOF

Repository: legeling/PromptHub

Length of output: 2486


🏁 Script executed:

# Check if Biome is installed and can be run
which biome || npm list -g biome 2>/dev/null || echo "Biome not found globally"
cd apps/desktop && npm list biome 2>/dev/null | head -20

Repository: legeling/PromptHub

Length of output: 254


🏁 Script executed:

# Try to run Biome on the file if available
if command -v biome &> /dev/null; then
  biome check apps/desktop/src/main/services/skill-installer.ts 2>&1 | head -100
elif [ -f "apps/desktop/node_modules/.bin/biome" ]; then
  apps/desktop/node_modules/.bin/biome check apps/desktop/src/main/services/skill-installer.ts 2>&1 | head -100
else
  echo "Biome not available in this environment"
fi

Repository: legeling/PromptHub

Length of output: 2953


🏁 Script executed:

# Get wider context around the first try block (likely starts earlier)
# Look backwards from line 642 to find where the try starts
sed -n '550,650p' apps/desktop/src/main/services/skill-installer.ts | cat -n

Repository: legeling/PromptHub

Length of output: 4681


🏁 Script executed:

# Check the structure around line 768 with more context
sed -n '700,780p' apps/desktop/src/main/services/skill-installer.ts | cat -n

Repository: legeling/PromptHub

Length of output: 4367


try/catch 块结构存在多余闭合块导致语法错误,是阻断发布问题。

Biome 已确认在第 642 行和第 768 行处的 catch 语句报出 parse error。在这两处 catch 之前各存在 2 个多余的 } 闭合符,导致 catch 与其对应的 try 块断裂,引发解析失败。

请删除第 641 行和第 767 行各多余的一个 },使 catch 子句能正确关联其对应的 try 块。

修正方案

第一处(行 640-643):

                }
              }
-           }
-         } catch (e) {
+         } catch (e) {
          console.error(`Failed to scan path: ${scanPath}`, e);

第二处(行 766-769):

                }
              }
-           }
-         } catch (e) {
+         } catch (e) {
          console.error(`Failed to scan path: ${scanPath}`, e);
🧰 Tools
🪛 Biome (2.4.13)

[error] 642-642: Expected a catch clause but instead found '}'.

(parse)


[error] 642-642: Expected a statement but instead found 'catch (e)'.

(parse)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/services/skill-installer.ts` at line 642, There are
extra closing braces before two catch blocks that break the try/catch pairing in
skill-installer.ts; remove the stray '}' immediately before each "catch (e)"
occurrence so that the catch clauses directly follow their corresponding try
blocks (specifically the two "catch (e)" blocks shown in the diff), ensuring the
try/catch structures around those sections (look for the surrounding try blocks
and the nearby functions in skill-installer.ts) are properly paired and parse
without error.

@legeling
Copy link
Copy Markdown
Owner

nice

@legeling
Copy link
Copy Markdown
Owner

coderabbit发现了一些问题,可以先看着改一改

@legeling legeling merged commit 55de5a9 into legeling:main Apr 30, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants