Repository navigation
Generate flat Cursor command wrappers during install - #47
Conversation
Cursor discovers skills by scanning .cursor/skills/*/SKILL.md — one directory per entry. Unlike Claude Code, it has no commands/ discovery system, so individual workflow phases (e.g., /start, /pr, /assess) were invisible to Cursor users. The installer now generates a flat wrapper directory per phase during `install.sh cursor`. For a workflow like code-review with commands start, continue, and clean, it creates: .cursor/skills/code-review-start/SKILL.md .cursor/skills/code-review-continue/SKILL.md .cursor/skills/code-review-clean/SKILL.md Each wrapper is a thin SKILL.md that reads the workflow's controller and dispatches the phase. A single .generated-wrappers manifest in the skills directory tracks generated directories for clean removal by uninstall.sh. Tested: full install (67 wrappers), selective install/uninstall, idempotent re-runs, and clean uninstall on flightctl project. Co-authored-by: Cursor <cursoragent@cursor.com>
WalkthroughThis PR implements generation and management of Cursor command markdown files during installation. The install script now extracts command metadata from workflow definitions, generates flat hyphenated command files in a Cursor-accessible directory, and the uninstall script mirrors that cleanup to remove only the generated files without deleting unrelated content. ChangesCursor command file generation and lifecycle
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Suggested labels
Poem
🚥 Pre-merge checks | ✅ 7 | ❌ 5❌ Failed checks (5 warnings)
✅ Passed checks (7 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 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 |
|
Thanks for tackling this, Gal — the problem is real and clearly articulated. Cursor's lack of Should these be
|
Address PR review feedback from @adalton: - Switch from generating wrapper SKILL.md directories in .cursor/skills/ to generating flat .md files in .cursor/commands/. This is Cursor's native command system (since 1.6), works at both user-level (~/.cursor/commands/) and project-level, eliminates the need for a manifest file, and keeps the skills directory clean (12 workflow dirs instead of 79). - Fix the prefix-matching bug in selective uninstall. The old approach matched wrapper names with "${wf}-"* which could collide if one workflow name prefixed another. The new approach validates each candidate file against the source workflow's commands/ directory. - Escape double quotes in extracted descriptions to prevent YAML injection in generated frontmatter. - Document recommended .gitignore pattern for project-level installs. - Document symlink chain fragility and how generated commands avoid it by using absolute paths to $INSTALL_DIR. - Fix CONTRIBUTING.md numbering and scope clarity for Claude Code items. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Thanks for the thorough review, @adalton. Pushed a commit (see 39105c0) addressing everything:
|
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
install.sh (1)
22-22:⚠️ Potential issue | 🟠 Major | ⚡ Quick winFix shell strict mode and Cursor command dispatch in
install.sh
install.shenables onlyset -e(line 22); update toset -euo pipefailso unset variables and pipeline failures don’t slip by.Suggested patch
-set -e +set -euo pipefail
generate_cursor_commands()always tells agents to read${INSTALL_DIR}/${wf}/skills/controller.mdfor every workflow (line 168); this breaks workflows that don’t include a controller (perCONTRIBUTING.md). Generate commands should conditionally reference../skills/controller.mdwhen present, otherwise fall back to../SKILL.mdor the phase skill, and use relative paths from the command file location.🤖 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 `@install.sh` at line 22, Update shell strictness and fix cursor command path selection: change the global shell flags in install.sh from set -e to set -euo pipefail to fail on unset vars and pipeline errors; and modify the generate_cursor_commands() function so it checks per-workflow whether a controller file exists and emits a relative path to the nearest skill file instead of always using ${INSTALL_DIR}/${wf}/skills/controller.md — specifically: when building commands for a workflow (variables wf, INSTALL_DIR) test for ../skills/controller.md relative to the command file, if present use that path, otherwise fall back to ../SKILL.md or the phase-specific skill file, and ensure paths are relative from the command file location when written to the generated command files.
uninstall.sh (1)
18-18:⚠️ Potential issue | 🟠 Major | ⚡ Quick winFix
uninstall.shshell strict mode and Cursor command cleanup
uninstall.shusesset -eonly; update toset -euo pipefailfor safer cleanup.remove_cursor_commands()only removes.cursor/commands/${wf}-*.mdwhen the corresponding${INSTALL_DIR}/${wf}/commands/${phase}.mdstill exists; if a phase is removed upstream, previously generated wrappers for that phase remain.Suggested patch
-set -e +set -euo pipefail🤖 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 `@uninstall.sh` at line 18, Update shell strict mode in uninstall.sh from using set -e to set -euo pipefail for safer error handling, and modify the remove_cursor_commands() function so it no longer only deletes .cursor/commands/${wf}-*.md when ${INSTALL_DIR}/${wf}/commands/${phase}.md exists; instead iterate over the actual files in .cursor/commands matching ${wf}-*.md, parse the phase/name from each filename and remove those wrapper files unconditionally (or after confirming their derived phase no longer exists upstream) so stale wrapper files are cleaned up; ensure the function still uses the wf and INSTALL_DIR variables and handles missing files/empty globs safely under the new strict mode.
🤖 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 `@CONTRIBUTING.md`:
- Around line 95-103: The docs currently instruct generated command files
(`.cursor/commands/{workflow}-{phase}.md`) to use absolute paths to
`$INSTALL_DIR`, which conflicts with the project's rule that all file references
must be relative for symlink compatibility; update the text to require relative
paths instead (e.g., reference the workflow/controller via a path relative to
the command file or the skill root) and explicitly state that generated wrappers
must resolve the workflow controller using relative references so they remain
portable and work when `.cursor/skills/{workflow}` is a symlink pointing to
`~/.ai-workflows/{workflow}`; remove the absolute-path example and replace it
with guidance to compute paths relative to the command file or workflow root
when producing dispatch prompts.
In `@install.sh`:
- Around line 166-170: The script currently assumes every workflow has a
controller by always referencing `${INSTALL_DIR}/${wf}/skills/controller.md`;
change the dispatch logic to first test for the existence of
`${INSTALL_DIR}/${wf}/skills/controller.md` and only use it when present,
otherwise fall back to the phase-specific command file (e.g.,
`${INSTALL_DIR}/${wf}/skills/${phase}.md` or dispatch the phase directly from
the workflow directory); update the generated Cursor command construction to
branch on this existence check so workflows without a controller produce valid
commands without breaking.
In `@README.md`:
- Around line 140-142: The table rows for "**User** (default)" and "**Project**
(`--project`)" in README.md use home-absolute paths (e.g.,
"~/.cursor/skills/<workflow>", "~/.cursor/commands/", "~/.claude/CLAUDE.md");
update these examples to use relative paths instead (e.g.,
".cursor/skills/<workflow>", ".cursor/commands/", ".claude/CLAUDE.md") and apply
the same change to the other occurrence noted around the later reference so all
workflow-facing path examples are relative and symlink-safe.
In `@uninstall.sh`:
- Around line 125-134: The loop in uninstall.sh that iterates WORKFLOWS and
cmd_file currently only deletes generated command files when the corresponding
source file exists, leaving orphan files when a source phase was removed; change
the conditional around "${INSTALL_DIR}/${wf}/commands/${suffix}.md" to remove
the generated file when the source file or phase no longer exists (i.e., if the
source path is missing/does not exist), so rm -f "$cmd_file" and increment
removed in that branch; reference the loop variables WORKFLOWS, cmds_dir,
cmd_file, base, suffix, INSTALL_DIR and the removed counter when making this
change.
---
Outside diff comments:
In `@install.sh`:
- Line 22: Update shell strictness and fix cursor command path selection: change
the global shell flags in install.sh from set -e to set -euo pipefail to fail on
unset vars and pipeline errors; and modify the generate_cursor_commands()
function so it checks per-workflow whether a controller file exists and emits a
relative path to the nearest skill file instead of always using
${INSTALL_DIR}/${wf}/skills/controller.md — specifically: when building commands
for a workflow (variables wf, INSTALL_DIR) test for ../skills/controller.md
relative to the command file, if present use that path, otherwise fall back to
../SKILL.md or the phase-specific skill file, and ensure paths are relative from
the command file location when written to the generated command files.
In `@uninstall.sh`:
- Line 18: Update shell strict mode in uninstall.sh from using set -e to set
-euo pipefail for safer error handling, and modify the remove_cursor_commands()
function so it no longer only deletes .cursor/commands/${wf}-*.md when
${INSTALL_DIR}/${wf}/commands/${phase}.md exists; instead iterate over the
actual files in .cursor/commands matching ${wf}-*.md, parse the phase/name from
each filename and remove those wrapper files unconditionally (or after
confirming their derived phase no longer exists upstream) so stale wrapper files
are cleaned up; ensure the function still uses the wf and INSTALL_DIR variables
and handles missing files/empty globs safely under the new strict mode.
🪄 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: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Enterprise
Run ID: 5f694770-e689-44c4-bb3b-85d1ffdcb4ae
📒 Files selected for processing (4)
CONTRIBUTING.mdREADME.mdinstall.shuninstall.sh
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
**/*.md
📄 CodeRabbit inference engine (AGENTS.md)
**/*.md: All file references must use relative paths for symlink compatibility
Do not use IDE-specific syntax in workflow content—use only plain markdownWorkflow content should be plain markdown with no IDE-specific syntax
Files:
CONTRIBUTING.mdREADME.md
⚙️ CodeRabbit configuration file
**/*.md: Cross-workflow consistency (ai-workflows conventions):
- All file references must be relative paths (never absolute) —
this is critical for symlink compatibility- No IDE-specific syntax (Cursor-specific, VS Code-specific, etc.)
- Consistent terminology within a workflow: pick one term, stick
with it- Schema field names and types must match between producer and
consumer files (e.g., if a field is defined in one phase skill
and consumed in another, names and types must agree)- No verbatim duplication of multi-line instruction blocks
across SKILL.md, guidelines.md, and controller.md — each has
a distinct role (shared phase names and brief references are
expected cross-referencing, not duplication)
Files:
CONTRIBUTING.mdREADME.md
**/*.sh
⚙️ CodeRabbit configuration file
**/*.sh: Shell script review (ai-workflows conventions):
- Must use set -euo pipefail for safety
- install.sh and uninstall.sh: verify auto-discovery logic
(scanning for */SKILL.md) is correct- validate-structure.sh: verify checks match current
CONTRIBUTING.md conventions- No hardcoded workflow lists — rely on SKILL.md auto-discovery
Files:
install.shuninstall.sh
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: flightctl/ai-workflows
Timestamp: 2026-05-25T16:48:06.798Z
Learning: Workflow directories must be named with lowercase letters and hyphens only
Learnt from: CR
Repo: flightctl/ai-workflows
Timestamp: 2026-05-25T16:48:06.798Z
Learning: Every workflow directory must contain SKILL.md, guidelines.md, and README.md as required files
Learnt from: CR
Repo: flightctl/ai-workflows
Timestamp: 2026-05-25T16:48:06.798Z
Learning: Use consistent terminology within a workflow and pick one term and stick with it
🪛 LanguageTool
README.md
[style] ~149-~149: Consider using the typographical ellipsis character here instead.
Context: ...ss, /bugfix:diagnose, /bugfix:fix, ... - /code-review:start, /code-review:c...
(ELLIPSIS)
[style] ~151-~151: Consider using the typographical ellipsis character here instead.
Context: ...ocs-writer:plan, /docs-writer:draft`, ... ### Cursor The installer generates fl...
(ELLIPSIS)
[style] ~157-~157: Consider using the typographical ellipsis character here instead.
Context: ...ss, /bugfix-diagnose, /bugfix-fix, ... - /code-review-start, /code-review-c...
(ELLIPSIS)
[style] ~159-~159: Consider using the typographical ellipsis character here instead.
Context: ...ocs-writer-plan, /docs-writer-draft, ... Cursor scans both project-level (.cur...
(ELLIPSIS)
[style] ~161-~161: This word has been used in one of the immediately preceding sentences. Using a synonym could make your text more interesting to read, unless the repetition is intentional.
Context: ...es — no manifest or wrapper directories needed. They are created by install.sh and c...
(EN_REPEATEDWORDS_NEED)
|
Tip For best results, initiate chat on the files or code changes.
Would you like me to open follow-up issues for each of those pre-existing warnings so they don't get lost? I can create one issue per concern (or a single grouped issue) and assign it to you. |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
|
@coderabbitai approve |
✅ Actions performedComments resolved and changes approved. |
Generate flat Cursor command wrappers during install
Summary
install.shand cleanup intouninstall.sh, replacing the standalonecursor-flatten.shdraft scriptSKILL.mdfiles at the top level of.cursor/skills/*/— it has nocommands/directory scanning like Claude Code. This means individual workflow phases (/start,/pr,/assess, etc.) were invisible to Cursor users.cursor/skills/code-review-start/SKILL.md) that reads the workflow's controller and dispatches the phaseHow it works
Install (
install.sh cursor):commands/*.mdin a workflow, generates a wrapper directory with a thinSKILL.md.generated-wrappersmanifest fileUninstall (
uninstall.sh cursor):.generated-wrappersmanifest--workflowsfor selective uninstall)Changes
install.sh— addedgenerate_cursor_command_wrappers()helper, called frominstall_cursor()uninstall.sh— addedremove_cursor_command_wrappers()helper, called fromuninstall_cursor()README.md— split Usage section into Claude Code (colon-namespaced) and Cursor (hyphen-namespaced) with examplesCONTRIBUTING.md— documented Cursor wrapper generation in Installation Internals sectionTesting
Tested on the flightctl project:
--workflows code-review,bugfix): 14 wrappers--workflows bugfix): removes only bugfix wrappers, leaves code-review intactMade with Cursor
Affected Workflows
All 12 workflows in the repository are affected by this change: ai-ready, bugfix, code-review, cve-fix, design, docs-writer, e2e, implement, kcs, prd, skill-reviewer, and triage. The changes apply uniformly across all workflows via the modified install/uninstall scripts.
Structural Changes to Commands/Skills
Cursor command discovery mechanism: The PR replaces per-workflow wrapper skill directories with a flat command file approach. Instead of creating directory structures like
.cursor/skills/{workflow}-{phase}/SKILL.md, the installer now generates simple.mdfiles directly in.cursor/commands/(project scope) or~/.cursor/commands/(user scope), using the naming convention{workflow}-{phase}.md(e.g.,bugfix-assess.md,code-review-start.md).Generated command file structure: Each generated file contains:
descriptionextracted from the workflow'scommands/{phase}.mdfile (with fallback to the correspondingskills/{phase}.md)controller.mdand execute the specified phaseSkills directory symlinks: Workflow directories remain symlinked into
.cursor/skills/{workflow}/for top-level skill discovery, and the_shared/directory is symlinked as before.Installation and Uninstallation Changes
Install (
install.sh):generate_cursor_commands(cmds_dir)function iterates all workflows and creates flat command files in the target commands directoryinstall_cursor()now creates both.cursor/skillsand.cursor/commandsdirectories and populates both.cursor/) or user level (~/.cursor/) based on installation scopeUninstall (
uninstall.sh):remove_cursor_commands(cmds_dir)function removes generated command files by matching{workflow}-{phase}.mdfilenames against actual source files incommands/uninstall_cursor()calls this cleanup before removing symlinks--workflows) correctly removes only commands belonging to specified workflowsCross-Workflow and Shared Resources
The
_shared/directory and its symlink handling remain unchanged. No modifications were made to shared recipes, review protocols, or cross-workflow conventions. All workflows adopt the same flat command naming and generation mechanism uniformly.Documentation Updates
README.md now documents the new flat slash-menu command format (
/bugfix-assess,/code-review-start, etc.) for Cursor users and recommends adding.cursor/commands/to project.gitignore. CONTRIBUTING.md added a new "Installation Internals" section explaining Cursor's dual discovery mechanisms (skills and commands directories), how the installer leverages both, and notes on symlink limitations in Cursor and how generated commands avoid relying on symlink resolution.