Skip to content

feat: add Skills management tab to dashboard#57

Merged
2witstudios merged 5 commits into
mainfrom
ppg/skills-tab
Feb 26, 2026
Merged

feat: add Skills management tab to dashboard#57
2witstudios merged 5 commits into
mainfrom
ppg/skills-tab

Conversation

@2witstudios

@2witstudios 2witstudios commented Feb 26, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds a new Skills tab to the PPG macOS dashboard for managing Claude Code skills (~/.claude/skills/{name}/SKILL.md)
  • Supports full CRUD: list, create, edit, delete skills with YAML frontmatter (name, description, user-invocable)
  • Import from existing ppg prompts and swarms to auto-generate skills
  • Reference file management (add/remove/edit files in references/ subdirectory)
  • Follows exact patterns from existing PromptsView/SwarmsView (Theme colors, layout, button styles)

Test plan

  • Build the Xcode project — should compile without errors
  • Launch the app — Skills tab should appear in sidebar with sparkles icon
  • Click Skills tab — should show empty state if no skills exist, or list existing skills
  • Click "New Skill" — dialog should create ~/.claude/skills/{name}/SKILL.md
  • Edit and Save — should update the SKILL.md file atomically
  • Click "Import" — should list available prompts and swarms, generate appropriate SKILL.md
  • Add/Remove reference files — should create/delete files in references/ subdirectory
  • Delete skill — should remove entire skill directory after confirmation
  • Verify created skills appear as /slash-commands in Claude Code

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added an Agent Config section accessible from the sidebar.
    • New Agent Config workspace with segmented views: CLAUDE.md editor, Skills, and Agents.
    • CLAUDE.md editor for browsing, editing, and saving multi-source rule files.
    • Agents config editor for viewing/editing .ppg/config.yaml per project.
    • Added a new Skills section with full create/edit/rename/delete and reference management.
  • UX

    • Improved single‑pane navigation consistency when switching between sections.

Add a new Skills tab to the PPG macOS dashboard for managing Claude Code
skills (~/.claude/skills/{name}/SKILL.md) directly from the UI.

Features:
- List, create, edit, delete Claude Code skills
- Import from existing ppg prompts and swarms
- Manage reference files in skills' references/ subdirectory
- YAML frontmatter parsing (name, description, user-invocable)
- Kebab-case name validation with directory rename on save

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Feb 26, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@2witstudios has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 20 minutes and 38 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 5e3ab50 and 406f141.

📒 Files selected for processing (4)
  • PPG CLI/PPG CLI/ClaudeMdEditorView.swift
  • PPG CLI/PPG CLI/ContentTabViewController.swift
  • PPG CLI/PPG CLI/PpgAgentsView.swift
  • PPG CLI/PPG CLI/SkillsView.swift
📝 Walkthrough

Walkthrough

Adds an Agent Config feature (UI + controller wiring), a CLAUDE.md editor, PPG agents editor, and a full Skills management UI; integrates Agent Config and Skills into sidebar, dashboard, and content view controllers with lifecycle and single-pane consistency updates.

Changes

Cohort / File(s) Summary
Sidebar / Navigation
PPG CLI/PPG CLI/SidebarViewController.swift
Adds .agentConfig tab, agentConfigRow, and onAgentConfigClicked callback; wires UI and selection/deselection logic.
Split / Dashboard Controller
PPG CLI/PPG CLI/DashboardSplitViewController.swift
Adds showAgentConfigView() and integrates agent-config state into refresh/title accessory logic.
Content / Tab Controller
PPG CLI/PPG CLI/ContentTabViewController.swift
Adds agentConfigView, agentConfigConstraints, isShowingAgentConfigView, and showAgentConfigView(projects:); updates show/switch methods to remove agentConfigView for single-pane consistency.
AgentConfig UI
PPG CLI/PPG CLI/AgentConfigView.swift
New AgentConfigView with segmented control switching among ClaudeMdEditorView, SkillsView, and PpgAgentsView; implements lazy child instantiation and configure(projects:).
CLAUDE.md Editor
PPG CLI/PPG CLI/ClaudeMdEditorView.swift
New ClaudeMdEditorView: file discovery across project/user locations, editor UI, dirty-tracking, save/load and unsaved-change prompts.
PPG Agents Editor
PPG CLI/PPG CLI/PpgAgentsView.swift
New PpgAgentsView: project/config.yaml discovery, editor UI, load/save, dirty-tracking and unsaved-change prompts.
Skills Feature
PPG CLI/PPG CLI/SkillsView.swift
Large new SkillsView and SkillFileInfo model: skill scanning, YAML frontmatter parsing, list/detail editor, import/rename/save/delete, reference file management.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Sidebar as SidebarViewController
    participant Dashboard as DashboardSplitViewController
    participant Content as ContentTabViewController
    participant AgentConfig as AgentConfigView
    participant Child as (ClaudeMd|Skills|Agents)

    User->>Sidebar: Clicks Agent Config tab
    Sidebar->>Dashboard: onAgentConfigClicked()
    Dashboard->>Content: showAgentConfigView()
    Content->>Content: instantiate/attach AgentConfigView (if needed)
    Content->>AgentConfig: configure(projects:)
    AgentConfig->>Child: showChild(at: 0..2)
    Child-->>User: display chosen editor (ClaudeMd / Skills / Agents)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • refactor: full rebrand ppg → pogu #50 — Modifies the same UI controllers (ContentTabViewController, DashboardSplitViewController, SidebarViewController); likely overlaps with navigation and pane wiring.

Poem

🐰📦 I hopped in with tabs and tiny claws,
A config, editors, and skillful laws.
Segments that swap, files that save,
A burrow of features, tidy and brave!

🚥 Pre-merge checks | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description describes only a Skills tab, but the PR (per objectives) actually implements an Agent Config tab containing CLAUDE.md editor, Skills, and Agents. The description does not match the actual implementation scope. Update description to match the actual scope: include CLAUDE.md editor, SkillsView, and PpgAgentsView within the Agent Config tab structure, and remove the template checklist items (npm test/typecheck are irrelevant for this macOS AppKit project).
Docstring Coverage ⚠️ Warning Docstring coverage is 41.79% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title mentions 'Skills management tab' but the PR actually implements a broader Agent Config tab with CLAUDE.md editor, Skills, and Agents views. Update the title to reflect the broader scope: 'feat: add Agent Config tab with CLAUDE.md editor, Skills, and Agents management' or similar.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ppg/skills-tab

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

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

@2witstudios

Copy link
Copy Markdown
Owner Author

Code review

Found 4 issues:

  1. Import dialog type switcher is non-functional -- populateItems() is called once at setup (line 809), and importTypeChanged(_:) is an empty no-op (line 873). When the user switches the Type popup from "Prompt" to "Swarm", itemPopup never repopulates. After dismissal, itemPopup.indexOfSelectedItem indexes into the wrong array (swarms instead of prompts), selecting and importing the wrong item.

func populateItems() {
itemPopup.removeAllItems()
if typePopup.indexOfSelectedItem == 0 {
for p in prompts { itemPopup.addItem(withTitle: "\(p.projectName)/\(p.name)") }
if let first = prompts.first { nameInput.stringValue = first.name }
} else {
for s in swarms { itemPopup.addItem(withTitle: "\(s.projectName)/\(s.name)") }
if let first = swarms.first { nameInput.stringValue = first.name }
}
}
populateItems()
typePopup.target = self
typePopup.action = #selector(importTypeChanged(_:))
// Store closures via objc associated objects is complex, use a simpler approach:
// We'll just re-populate after the dialog based on final selection

@objc private func importTypeChanged(_ sender: NSPopUpButton) {
// Re-population handled inline; this is a no-op target for the popup
}

  1. Unsaved body edits silently discarded when navigating to/from reference files -- backToSkillClicked() restores editorTextView.string = skill.body from the cached model (line 996), not from the editor's previous content. If a user edits the SKILL.md body, double-clicks a reference file, then clicks "Back to SKILL.md", their unsaved edits are lost with no warning.

@objc private func backToSkillClicked() {
guard let idx = selectedIndex, idx < skills.count else { return }
let skill = skills[idx]
editingRefFile = nil
backButton.isHidden = true
editorTextView.string = skill.body
}

  1. Creating a skill with an existing name silently overwrites it -- Both newSkillClicked (line 726) and importClicked (line 854) call createDirectory(withIntermediateDirectories: true) which succeeds silently if the directory exists, then write SKILL.md without checking for an existing file. A user who types an existing skill name loses all content of that skill immediately with no confirmation.

do {
try fm.createDirectory(atPath: skillDir, withIntermediateDirectories: true)
let skeleton = Self.serializeSkill(name: name, description: "", userInvocable: false, body: "")
let path = (skillDir as NSString).appendingPathComponent("SKILL.md")
try skeleton.write(toFile: path, atomically: true, encoding: .utf8)
configure()

do {
try fm.createDirectory(atPath: skillDir, withIntermediateDirectories: true)
let skeleton = Self.serializeSkill(name: name, description: description, userInvocable: false, body: body)
let path = (skillDir as NSString).appendingPathComponent("SKILL.md")
try skeleton.write(toFile: path, atomically: true, encoding: .utf8)
configure()

  1. Import dialog accessory view height too small, clipping the name field -- The accessory frame is 130pt tall (line 754), but the controls require ~148pt (3 labels at 16pt + 3 controls at 26pt + gaps). The var y counter goes negative, positioning the name input at y=-12 where it is clipped. A previous commit (bc31199) fixed this exact class of bug in the prompts dialog.

let accessory = NSView(frame: NSRect(x: 0, y: 0, width: 300, height: 130))
var y: CGFloat = 130

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

… clipping, path traversal

Fix import type switcher by storing dialog state as instance properties so
importTypeChanged repopulates the item list when switching Prompt/Swarm.
Preserve unsaved body edits when navigating to/from reference files via
pendingBodyText. Add fileExists guards to prevent silent skill overwrites
in new/import/rename flows. Increase import dialog accessory height from
130pt to 160pt to prevent name field clipping. Add validateFilename() to
reject path traversal (../, /) in skill names and reference filenames.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

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

Inline comments:
In `@PPG` CLI/PPG CLI/ContentTabViewController.swift:
- Line 87: isShowingSkillsView currently checks presence via
skillsView?.superview != nil which can remain true after
restoreGrid(forEntryId:) because restoreGrid doesn't remove or detach
skillsView; update restoreGrid(forEntryId:) to explicitly remove or hide
skillsView (e.g. skillsView?.removeFromSuperview() or set skillsView?.isHidden =
true) and/or update any refresh logic to call a new helper (e.g.
hideSkillsViewIfNeeded()) so that isShowingSkillsView accurately reflects the UI
state after grid restoration.

In `@PPG` CLI/PPG CLI/SkillsView.swift:
- Around line 969-1031: The add/remove reference handlers (addRefClicked /
removeRefClicked) call configure() and loadSkillDetail(at:) which reload
SKILL.md from disk and overwrite any unsaved editor edits; before calling
configure()/loadSkillDetail(at:) detect unsaved SKILL.md changes in the current
skill editor (compare editor text to model or track an isDirty flag), and either
prompt the user to Save/Discard/Cancel or programmatically save the current
editor contents (invoke the existing save routine or add a
saveCurrentSkillEdits() helper) so unsaved edits are preserved or the operation
is aborted; apply the same change to the counterpart handler(s) that manage
references so adding/removing references no longer silently discards edits.
- Around line 887-892: The guard that silently returns when
itemPopup.indexOfSelectedItem is < 0 should instead provide user feedback; in
SkillsView.swift replace the silent early-return with a check that, if itemIdx <
0, presents an informative alert (e.g., NSAlert with a message like "No source
items exist for the selected type") or otherwise disables the Create action, and
do not clear importTypePopup/importItemPopup/importNameInput; update the code
around isPrompt, typePopup, itemPopup handling so the UI either shows the alert
or prevents Create when no source items exist.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0e801a9 and 1b0dd7a.

📒 Files selected for processing (4)
  • PPG CLI/PPG CLI/ContentTabViewController.swift
  • PPG CLI/PPG CLI/DashboardSplitViewController.swift
  • PPG CLI/PPG CLI/SidebarViewController.swift
  • PPG CLI/PPG CLI/SkillsView.swift

Comment thread PPG CLI/PPG CLI/ContentTabViewController.swift Outdated
Comment thread PPG CLI/PPG CLI/SkillsView.swift
Comment thread PPG CLI/PPG CLI/SkillsView.swift
2witstudios and others added 2 commits February 26, 2026 11:00
…r and ppg agents

Replace the single-purpose Skills sidebar tab with a comprehensive Agent Config
tab containing a segmented control that switches between three views:

- CLAUDE.md: Editor for all CLAUDE.md locations (project root, .claude/,
  CLAUDE.local.md, .claude/rules/*.md, ~/.claude/CLAUDE.md, ~/.claude/rules/*)
- Skills: Enhanced skill editor now scanning both personal (~/.claude/skills/)
  and project-level (.claude/skills/) skills, with full frontmatter support
  including argument-hint, disable-model-invocation, allowed-tools, model,
  context, and agent fields
- Agents: Raw YAML editor for .ppg/config.yaml agent definitions

Rename plumbing throughout sidebar, content controller, and split controller
from skills -> agentConfig. New skills default to user-invocable: true matching
Claude Code's official default.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…dit preservation

- Add agentConfigView/schedulesView removal in restoreGrid to keep
  isShowingAgentConfigView accurate after grid restoration
- Show informative alert when import dialog has no source items instead
  of failing silently
- Preserve unsaved SKILL.md body edits when adding/removing reference
  files by capturing draft text before configure()/loadSkillDetail() reload

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (1)
PPG CLI/PPG CLI/ContentTabViewController.swift (1)

479-483: ⚠️ Potential issue | 🟠 Major

restoreGrid does not detach all single-pane views.

schedulesView and agentConfigView are not removed here, so pane state can stay stale while grid is active.

💡 Suggested fix
         homeDashboardView?.removeFromSuperview()
         worktreeDetailView?.removeFromSuperview()
         promptsView?.removeFromSuperview()
         swarmsView?.removeFromSuperview()
+        schedulesView?.removeFromSuperview()
+        agentConfigView?.removeFromSuperview()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@PPG` CLI/PPG CLI/ContentTabViewController.swift around lines 479 - 483, In
restoreGrid, you currently remove homeDashboardView, worktreeDetailView,
promptsView, swarmsView and terminalViews from containerView but miss
schedulesView and agentConfigView, leaving stale single-pane state; update the
restoreGrid implementation to also check and call removeFromSuperview() for
schedulesView and agentConfigView (using the same containerView === check
pattern used for terminalViews or the existing direct removeFromSuperview calls)
so all single-pane views are detached when switching to grid.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@PPG` CLI/PPG CLI/ClaudeMdEditorView.swift:
- Around line 29-35: The initial selection can pick the leading separator
placeholder when there are no project entries; in configure(projects:), after
populateFileSwitcher() ensure you find the first valid file entry (skip
separator placeholders) instead of blindly selecting index 0 — e.g., check
fileEntries for the first non-separator entry and call
fileSwitcher.selectItem(at:) and loadFile(at:) with that index; apply the same
change to the other block around loadFile(at:) / fileSwitcher selection
referenced (lines ~167-173) so both places skip separator items when choosing
the initial selection.
- Around line 202-215: In fileSwitcherChanged(_:) the Cancel path still falls
through to loadFile(at:), so abort the switch when the user cancels: after
creating the NSAlert and calling alert.runModal(), check the returned value and
if it is not .alertFirstButtonReturn (i.e., the user cancelled) immediately
return from fileSwitcherChanged; otherwise proceed to call loadFile(at:
sender.indexOfSelectedItem). Ensure this logic references isDirty,
alert.runModal(), and loadFile(at:) so the method only calls loadFile when it’s
safe to switch.

In `@PPG` CLI/PPG CLI/PpgAgentsView.swift:
- Around line 181-193: The projectSwitcherChanged(_:) handler leaves the
NSPopUpButton selection on the new project when the user cancels, causing
subsequent saves to write to the wrong config; capture the current selection
index before prompting (e.g., prevIndex = sender.indexOfSelectedItem or store
selected item), and if the alert is cancelled revert the popup to prevIndex
before returning; apply the same pattern to the other project-switching handler
referenced around lines 197-200 so any canceled switch restores the UI selection
to the original project.

In `@PPG` CLI/PPG CLI/SkillsView.swift:
- Around line 833-847: The rename currently always builds newDir from the
personal skills root (skillsDir) which will move project-scoped skills out of
their project; change the destination logic used before calling
fm.moveItem(atPath:toPath:) so it preserves the skill's current scope: determine
the correct skills root (if the Skill instance indicates a project-scoped skill
use the project skills directory or the skill's current parent directory,
otherwise use the personal "~/.claude/skills"), then build newDir from that root
and newName (replace use of skillsDir variable when computing newDir); ensure
subsequent paths like newPath ((newDir as
NSString).appendingPathComponent("SKILL.md")) continue to use this scope-aware
newDir so project skills remain inside their project.

---

Duplicate comments:
In `@PPG` CLI/PPG CLI/ContentTabViewController.swift:
- Around line 479-483: In restoreGrid, you currently remove homeDashboardView,
worktreeDetailView, promptsView, swarmsView and terminalViews from containerView
but miss schedulesView and agentConfigView, leaving stale single-pane state;
update the restoreGrid implementation to also check and call
removeFromSuperview() for schedulesView and agentConfigView (using the same
containerView === check pattern used for terminalViews or the existing direct
removeFromSuperview calls) so all single-pane views are detached when switching
to grid.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1b0dd7a and 5e3ab50.

📒 Files selected for processing (7)
  • PPG CLI/PPG CLI/AgentConfigView.swift
  • PPG CLI/PPG CLI/ClaudeMdEditorView.swift
  • PPG CLI/PPG CLI/ContentTabViewController.swift
  • PPG CLI/PPG CLI/DashboardSplitViewController.swift
  • PPG CLI/PPG CLI/PpgAgentsView.swift
  • PPG CLI/PPG CLI/SidebarViewController.swift
  • PPG CLI/PPG CLI/SkillsView.swift
🚧 Files skipped from review as they are similar to previous changes (1)
  • PPG CLI/PPG CLI/DashboardSplitViewController.swift

Comment thread PPG CLI/PPG CLI/ClaudeMdEditorView.swift
Comment thread PPG CLI/PPG CLI/ClaudeMdEditorView.swift
Comment thread PPG CLI/PPG CLI/PpgAgentsView.swift
Comment thread PPG CLI/PPG CLI/SkillsView.swift Outdated
@2witstudios

Copy link
Copy Markdown
Owner Author

All 4 issues from the code review were addressed in 1b0dd7a, and the 3 CodeRabbit inline review comments have now been resolved in 396bcc9:

  1. restoreGrid not detaching agentConfigView — Added agentConfigView?.removeFromSuperview() and schedulesView?.removeFromSuperview() to restoreGrid(forEntryId:)
  2. Import dialog silent failure — Now shows an informative alert when no source items exist for the selected type
  3. Add/remove references discarding unsaved edits — Both handlers now capture and restore draft body text across the configure()/loadSkillDetail() reload cycle

…skip, rename path

- ClaudeMdEditorView: track currentFileIndex, skip separator in initial
  selection, restore popup on cancel, save uses tracked index
- PpgAgentsView: track currentConfigIndex, restore popup on cancel,
  save uses tracked index to prevent writing to wrong config file
- SkillsView: rename uses skill.skillDir parent instead of hardcoded
  ~/.claude/skills, preventing project-scoped skills from being moved

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@2witstudios
2witstudios merged commit a986830 into main Feb 26, 2026
3 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.

1 participant