Skip to content

feat(tui): add hierarchical skill group selector with tab navigation - #2995

Open
creatiVision wants to merge 4 commits into
MoonshotAI:mainfrom
creatiVision:feat/skill-group-selector
Open

feat(tui): add hierarchical skill group selector with tab navigation#2995
creatiVision wants to merge 4 commits into
MoonshotAI:mainfrom
creatiVision:feat/skill-group-selector

Conversation

@creatiVision

@creatiVision creatiVision commented Aug 17, 2026

Copy link
Copy Markdown

Problem

When managing multiple skills across categories or directory trees (e.g. cv/ops/semaphore, security/audit), the /skill command previously rendered skills in flat lists or cluttered pseudo-groups without clear visual boundaries. Furthermore:

  1. Navigating top-level categories was less intuitive compared to the /model selector's tab strip navigation.
  2. Typing /skill in the terminal input editor re-triggered argument autocomplete suggestions, requiring users to select /skill a second time before entering interactive selection mode.

What changed

  1. Tab Strip Group Navigation:

    • Integrated renderTabStrip utility into SkillSelectorComponent (apps/kimi-code/src/tui/components/dialogs/skill-selector.ts).
    • Top-level skill groups are now rendered in a tab strip ([All] [cv] [security] [Uncategorized]).
    • Pressing Tab / Shift-Tab cycles through top-level group tabs with brand-highlighted backgrounds, matching the exact UX of the /model selector (TabbedModelSelectorComponent).
  2. Hierarchical Group & Category Resolution:

    • Refactored buildSkillGroupTree and resolveGroupPathsForSkill in skill-group-tree.ts to strictly enforce metadata precedence order:
      1. groups (string array in skill metadata)
      2. category (slash-delimited string in skill metadata)
      3. Parent directory path relative to skill root
      4. Fallback to Uncategorized

    Examples of Metadata & Category Interaction

    • Example A: Explicit groups metadata (Highest Priority)

      name: semaphore-deploy
      groups: ["cv/ops", "deployments"]

      Result: The skill is placed under both cv › ops and deployments group tabs. Explicit groups override any category field or folder location.

    • Example B: category metadata

      name: static-audit
      category: security/static-analysis

      Result: Placed under security › static-analysis. Used when groups is not defined.

    • Example C: Relative Directory Path (Automatic Hierarchy)
      File location: ~/.gemini/skills/cv/ops/semaphore/SKILL.md
      Result: Automatically derived as cv › ops › semaphore without requiring metadata configuration.

    • Example D: Fallback
      A top-level skill with no groups, no category, and no subfolder location lands in the Uncategorized top-level tab.

  3. Subtree Fuzzy Search & Immediate Invocation:

    • Added filterItem callback to SearchableList so fuzzy searching queries match direct skills as well as descendant skills in subtrees when a search query is active, while keeping direct lists clean when query is empty.
    • Updated custom-editor.ts to suppress subcommand autocomplete reopening for /skill and /skills.
    • Updated handleSkillCommand in dispatch.ts to immediately launch runSkillSelector when running /skill without arguments.

Checklist

  • I have read the CONTRIBUTING document.
  • I have explained the problem above.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

@changeset-bot

changeset-bot Bot commented Aug 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 995bcc8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@moonshot-ai/kimi-code Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 48b17c7e4a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +337 to +339
if (page.items.length === 0 && query.workspaceIds !== undefined) {
const legacy = await this.listLegacy(query);
if (legacy.items.length > 0) return legacy;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Merge authoritative sessions when the cache is partially populated

When a workspace contains both a projected session and an older on-disk session missing from the read model, page.items.length is nonzero, so the authoritative fallback never runs and /sessions and --continue still hide the older session. The fallback must reconcile authoritative results with every workspace-scoped page, not only completely empty pages.

AGENTS.md reference: packages/agent-core-v2/AGENTS.md:L81-L83

Useful? React with 👍 / 👎.

Comment on lines +210 to +214
name: 'skill',
aliases: ['skills'],
description: 'Select skill from hierarchical group selector',
priority: 90,
availability: 'always',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Block /skill while a turn is active

When the session is streaming or compacting, this always availability bypasses the busy gate that existing dynamic skill commands use. Selecting a skill then calls sendSkillActivation during the active turn, where it can fail with an agent-busy error after the command has already been consumed; make this command idle-only or perform the same busy check immediately before activation.

Useful? React with 👍 / 👎.

Comment thread scripts/kimi-fork-update.sh Outdated
Comment on lines +18 to +21
*laptop*|kimi-*)
# kimi-laptop (Default)
FORK_REPO="${KIMI_FORK_REPO:-/media/work-data/002_cv-projects/cv_ai_kimi-code-cli-fork}"
REPO_DIR="${KIMI_FORK_SYMLINK:-/home/mb/.kimi-code-cli}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Remove machine-specific identifiers from the public script

This committed script exposes a real user home path and workstation-specific repository layout, with additional private host and fork identifiers later in the file. Besides leaking internal context, hosts matching the broad patterns silently select these destructive defaults; require environment configuration and use neutral placeholders in the tracked script.

AGENTS.md reference: AGENTS.md:L78-L80

Useful? React with 👍 / 👎.

Comment thread .gitignore Outdated
Comment on lines +48 to +49
plugins/**
plugins

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve the official plugin ignore exceptions

The final plugins/** and plugins rules override the earlier !plugins/official/ and !plugins/marketplace.json exceptions. Consequently, newly generated or synced files under plugins/official are ignored and can be omitted from a release without appearing in git status; keep the parent traversable and re-include the intended paths after the broad ignore rule.

Useful? React with 👍 / 👎.

Comment on lines +101 to +107
const parts = pathStr.split('/');
let current: Json = root;
for (const part of parts) {
if (typeof current !== 'object' || current === null || Array.isArray(current)) {
throw new Error(`Unable to resolve reference path: ${pointer}`);
}
current = (current as JsonRecord)[part] ?? null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Decode escaped JSON Pointer segments before dereferencing

Local JSON Schema references use JSON Pointer escaping, so a definition key containing / or ~ is referenced with ~1 or ~0. Splitting and looking up the raw segments makes valid schemas such as #/$defs/a~1b appear unresolved, causing listTools() to fail for that MCP server now that every schema is passed through this dereferencer; decode each segment before lookup.

Useful? React with 👍 / 👎.

@creatiVision
creatiVision force-pushed the feat/skill-group-selector branch from 48b17c7 to 5c0a79d Compare August 17, 2026 06:08
@creatiVision

Copy link
Copy Markdown
Author

Rebased branch onto latest upstream main — merge conflicts are resolved and PR history is cleaned down to the 3 relevant commits for the skill selector feature.

@creatiVision
creatiVision force-pushed the feat/skill-group-selector branch from 115be53 to 8c750f5 Compare August 17, 2026 23:23
@creatiVision

Copy link
Copy Markdown
Author

Addressed Codex feedback:

  • Busy-gated /skill command: Updated /skill in registry.ts to default idle availability and added active turn/compacting busy checks in handleSkillCommand before opening the selector dialog and before calling sendSkillActivation.
  • Note: The other 4 automated comments originated from earlier fork commits that have since been cleaned up during the rebase onto origin/main.

@creatiVision
creatiVision force-pushed the feat/skill-group-selector branch from 8c750f5 to 995bcc8 Compare August 17, 2026 23:28
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