Skip to content

feat(skills): AI generation, grid/list view, collapsible sections, skip-modal create#2814

Open
ricardo-leiva wants to merge 2 commits into
PostHog:mainfrom
ricardo-leiva:feat/skills-improvements
Open

feat(skills): AI generation, grid/list view, collapsible sections, skip-modal create#2814
ricardo-leiva wants to merge 2 commits into
PostHog:mainfrom
ricardo-leiva:feat/skills-improvements

Conversation

@ricardo-leiva

@ricardo-leiva ricardo-leiva commented Jun 21, 2026

Copy link
Copy Markdown

Problem

Managing skills in PostHog Code was friction-heavy for users with a growing skill library:

  • Creating a skill required clicking through a modal dialog — an extra unnecessary step
  • Writing skill content from scratch required knowing the markdown format with no guidance
  • Long skill lists (especially the bundled PostHog Code skills) had no grouping and became unmanageable
  • Names were displayed as raw slugs (auditing-endpoints) rather than readable text
  • No way to collapse sections when working with a large set of installed skills

Changes

Screenshot 2026-06-20 at 6 00 51 PM Screenshot 2026-06-20 at 6 00 27 PM Screenshot 2026-06-20 at 6 00 19 PM

Screen recording of how it looks here: https://drive.google.com/file/d/1DBUyEwWGVp3ZzqcTPmpcbUOvmCwHWHuk/view?usp=drive_link

Skip-modal skill creation
Clicking "New Skill" immediately creates a skill with an auto-incremented name (new-skill, new-skill-2, …) and opens it directly in edit mode via a new initialEditing prop on SkillDetailPanel. The confirmation dialog is removed — one click and you're editing.

AI-assisted content generation
A "Generate with AI" button in the editor calls a new SkillGeneratorService — an injectable @posthog/core service backed by LlmGatewayService. It fills both the description field and the markdown body in one click. The model returns a structured response (DESCRIPTION: line + <body> block) that is parsed and applied without overwriting content the user has already typed. CodeMirror is remounted via an editorKey to pick up generated body content.

Grid / list view toggle
A Finder-style grid card view sits alongside the existing list rows, toggled from a secondary toolbar strip. The preference persists to localStorage via a new skillsViewStore (zustand persist). SkillGridCard uses a semantic <button> element (fixes an a11y lint violation on the previous <div role="button">).

Collapsible sections with expand/collapse all
Every source section (PostHog Code, Your skills, Repository, …) and every sub-category within bundled/codex sources has a chevron toggle. A secondary toolbar strip below search shows "Collapse all / Expand all" alongside the view toggle (with tooltip).

Humanised skill names
Kebab-case slugs are rendered as Title Case across all card and grid views (auditing-endpoints → "Auditing Endpoints").

Bug fix: stale "No description" warning
The warning callout remained visible while the user was already typing a description in the editor. It is now hidden when isEditing is true.

How did you test this?

  • Launched the Electron app and exercised the full skills flow: created a skill via "New Skill" (no dialog, opens directly in edit mode), ran AI generation, toggled grid/list view, collapsed and expanded individual sections and sub-categories, used "Collapse all / Expand all"
  • Confirmed the "No description" callout disappears once the editor is open
  • pnpm typecheck — 22/22 tasks clean
  • pnpm lint (Biome) — 0 errors (fixed SkillGridCard semantic element violation in the same pass)
  • pnpm --filter @posthog/core test — 29/29 passing
  • node scripts/check-host-boundaries.mjs — no new violations

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

…le sections, skip-modal create

- Skip "New Skill" modal: clicking the button immediately creates a skill
  with an auto-incremented name (new-skill, new-skill-2, …) and opens
  the editor directly in edit mode via a new `initialEditing` prop on
  SkillDetailPanel.

- AI-assisted generation: a "Generate with AI" button in the editor
  calls the new SkillGeneratorService (injectable, lives in @posthog/core)
  via LlmGatewayService. The service returns a structured response parsed
  into both `description` and `body`, filling the form in one click.
  CodeMirror is remounted via an editorKey to pick up the generated body.

- Grid / list view toggle: Finder-style grid cards vs list rows, persisted
  to localStorage via a new skillsViewStore (zustand persist). SkillGridCard
  is a new semantic <button> component replacing the old <div role="button">.

- Collapsible sections: every source section (PostHog Code, Your skills,
  Repository…) and every sub-category within bundled/codex sources has a
  chevron toggle. A "Collapse all / Expand all" one-click control appears
  in a secondary toolbar strip below search.

- Humanise skill names: kebab-case slugs like `auditing-endpoints` are
  displayed as "Auditing Endpoints" in cards and the grid.

- Bug fix: "No description" warning callout was shown while the user was
  already typing in the editor. Hidden when isEditing is true.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TpGjPYBD4pgZpsAHjozzsN
@greptile-apps

greptile-apps Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Comments Outside Diff (3)

  1. packages/ui/src/features/skills/SkillsView.tsx, line 574-600 (link)

    P1 Silent failure after exhausting name candidates

    When all names new-skill through new-skill-19 are already taken, the loop exits after the i=20 iteration (which sets name = "new-skill-20" but never tries it) and falls through without throwing. The outer catch block is never reached, so setIsCreating(false) runs cleanly via finally and the user sees the spinner disappear with no skill created and no error toast. The i <= 20 guard inside the catch is also always true (it mirrors the loop condition), making it dead code that implies incorrect intent.

    The loop should either attempt new-skill-20 and throw if it also conflicts, or throw explicitly after exhausting all candidates.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: packages/ui/src/features/skills/SkillsView.tsx
    Line: 574-600
    
    Comment:
    **Silent failure after exhausting name candidates**
    
    When all names `new-skill` through `new-skill-19` are already taken, the loop exits after the i=20 iteration (which sets `name = "new-skill-20"` but never tries it) and falls through without throwing. The outer `catch` block is never reached, so `setIsCreating(false)` runs cleanly via `finally` and the user sees the spinner disappear with no skill created and no error toast. The `i <= 20` guard inside the catch is also always true (it mirrors the loop condition), making it dead code that implies incorrect intent.
    
    The loop should either attempt `new-skill-20` and throw if it also conflicts, or throw explicitly after exhausting all candidates.
    
    How can I resolve this? If you propose a fix, please make it concise.
  2. packages/ui/src/features/skills/SkillDetailPanel.tsx, line 325-343 (link)

    P2 hasAutoEnteredEdit ref is not reset across skill changes

    SkillDetailPanel has no key prop in SkillsView.tsx, so the same component instance is reused when the selected skill changes. hasAutoEnteredEdit is initialised once to false and set to true on the first auto-edit trigger, but it is never reset. If the user creates skill A (ref → true, enters edit mode) and then immediately creates skill B — without closing the panel — the panel re-renders with initialEditing={true} for skill B, but the ref guard blocks the setIsEditing(true) call and the panel stays in view mode. Adding key={selectedSkill.path} on SkillDetailPanel in SkillsView.tsx would give each skill a fresh component instance and reset the ref.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: packages/ui/src/features/skills/SkillDetailPanel.tsx
    Line: 325-343
    
    Comment:
    **`hasAutoEnteredEdit` ref is not reset across skill changes**
    
    `SkillDetailPanel` has no `key` prop in `SkillsView.tsx`, so the same component instance is reused when the selected skill changes. `hasAutoEnteredEdit` is initialised once to `false` and set to `true` on the first auto-edit trigger, but it is never reset. If the user creates skill A (ref → `true`, enters edit mode) and then immediately creates skill B — without closing the panel — the panel re-renders with `initialEditing={true}` for skill B, but the ref guard blocks the `setIsEditing(true)` call and the panel stays in view mode. Adding `key={selectedSkill.path}` on `SkillDetailPanel` in `SkillsView.tsx` would give each skill a fresh component instance and reset the ref.
    
    How can I resolve this? If you propose a fix, please make it concise.
  3. packages/ui/src/features/skills/SkillsView.tsx, line 765-815 (link)

    P2 Grid/list rendering logic duplicated across two call sites

    The viewMode === "grid" ? <div className="grid grid-cols-2 gap-2">…</div> : <Flex direction="column" gap="1">…</Flex> pattern now appears both here (inline for the bundled/codex category level) and inside SkillSection in SkillCard.tsx. Any future change to the card layout or grid columns needs to be made in both places. The simplicity rules ask for OnceAndOnlyOnce — it would be worth extracting a small SkillCardList component or reusing SkillSection (passing a sub-section without a source-level header) to keep the rendering logic in one place.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: packages/ui/src/features/skills/SkillsView.tsx
    Line: 765-815
    
    Comment:
    **Grid/list rendering logic duplicated across two call sites**
    
    The `viewMode === "grid" ? <div className="grid grid-cols-2 gap-2">…</div> : <Flex direction="column" gap="1">…</Flex>` pattern now appears both here (inline for the bundled/codex category level) and inside `SkillSection` in `SkillCard.tsx`. Any future change to the card layout or grid columns needs to be made in both places. The simplicity rules ask for OnceAndOnlyOnce — it would be worth extracting a small `SkillCardList` component or reusing `SkillSection` (passing a sub-section without a source-level header) to keep the rendering logic in one place.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
packages/ui/src/features/skills/SkillsView.tsx:574-600
**Silent failure after exhausting name candidates**

When all names `new-skill` through `new-skill-19` are already taken, the loop exits after the i=20 iteration (which sets `name = "new-skill-20"` but never tries it) and falls through without throwing. The outer `catch` block is never reached, so `setIsCreating(false)` runs cleanly via `finally` and the user sees the spinner disappear with no skill created and no error toast. The `i <= 20` guard inside the catch is also always true (it mirrors the loop condition), making it dead code that implies incorrect intent.

The loop should either attempt `new-skill-20` and throw if it also conflicts, or throw explicitly after exhausting all candidates.

### Issue 2 of 3
packages/ui/src/features/skills/SkillDetailPanel.tsx:325-343
**`hasAutoEnteredEdit` ref is not reset across skill changes**

`SkillDetailPanel` has no `key` prop in `SkillsView.tsx`, so the same component instance is reused when the selected skill changes. `hasAutoEnteredEdit` is initialised once to `false` and set to `true` on the first auto-edit trigger, but it is never reset. If the user creates skill A (ref → `true`, enters edit mode) and then immediately creates skill B — without closing the panel — the panel re-renders with `initialEditing={true}` for skill B, but the ref guard blocks the `setIsEditing(true)` call and the panel stays in view mode. Adding `key={selectedSkill.path}` on `SkillDetailPanel` in `SkillsView.tsx` would give each skill a fresh component instance and reset the ref.

### Issue 3 of 3
packages/ui/src/features/skills/SkillsView.tsx:765-815
**Grid/list rendering logic duplicated across two call sites**

The `viewMode === "grid" ? <div className="grid grid-cols-2 gap-2">…</div> : <Flex direction="column" gap="1">…</Flex>` pattern now appears both here (inline for the bundled/codex category level) and inside `SkillSection` in `SkillCard.tsx`. Any future change to the card layout or grid columns needs to be made in both places. The simplicity rules ask for OnceAndOnlyOnce — it would be worth extracting a small `SkillCardList` component or reusing `SkillSection` (passing a sub-section without a source-level header) to keep the rendering logic in one place.

Reviews (1): Last reviewed commit: "feat(skills): improve skills UX — AI gen..." | Re-trigger Greptile

… card list rendering

- Fix silent failure when all new-skill through new-skill-19 are taken:
  loop now runs to i=21 so new-skill-20 is actually attempted; at i=21
  the conflict error propagates to the outer catch and shows a toast
  instead of falling through silently. Removes the dead i<=20 guard
  inside the catch that mirrored the (now corrected) loop bound.

- Extract SkillCardList from the duplicated grid/list inline blocks.
  Both SkillSection (SkillCard.tsx) and the bundled/codex category
  renderer (SkillsView.tsx) now share the same component — grid columns,
  gap, and card types only need to change in one place.

- key={selectedSkill.path} was already present on SkillDetailPanel,
  so the hasAutoEnteredEdit ref resets correctly on skill change.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TpGjPYBD4pgZpsAHjozzsN
@charlesvien charlesvien added the Stamphog This will request an autostamp by stamphog on small changes label Jun 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Stamphog This will request an autostamp by stamphog on small changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants