Skip to content

Add AI skills for ramenctl commands - #455

Draft
nirs wants to merge 20 commits into
RamenDR:mainfrom
nirs:init-skills
Draft

Add AI skills for ramenctl commands#455
nirs wants to merge 20 commits into
RamenDR:mainfrom
nirs:init-skills

Conversation

@nirs

@nirs nirs commented May 16, 2026

Copy link
Copy Markdown
Member

Why skills?

ramenctl makes disaster recovery manageable, but adopting it still
requires learning commands, flags, and output conventions. AI coding
assistants can handle this for the user — configuring clusters,
running validations, interpreting reports — but only if they know
how ramenctl works.

Skills are structured markdown files that teach an AI agent how to
drive ramenctl. Each skill covers one subcommand: when to use it,
prerequisites, step-by-step workflow, and how to interpret results.
This makes it easy to get started with ramenctl and reduces the
learning curve for new users.

What this looks like in practice

INT. CURSOR CHAT — DAY

USER
        I ran ramenctl init -a cursor. Help me configure ramenctl
        for my clusters. My kubeconfigs are in ocp/: hub.yaml is the
        hub, c1.yaml and c2.yaml are the managed clusters.

AGENT
        (Edits config.yaml: fills kubeconfigs, reads ClusterClaims on c1
        and c2, matches names on the hub, sets clusterSet.)
        Your config is ready.

USER
        Can you check the clusters?

AGENT
        (Runs ramenctl validate clusters -o ocp/clusters.)
        Validation completed (91 ok, 0 warning, 0 problem).
        (Opens validate-clusters.html in the browser.)

USER
        Validate an application too.

AGENT
        (Lists DRPCs on the hub.)
        I found two applications:
          1. rbd4 (openshift-dr-ops) — FailedOver
          2. appset-deploy-rbd (openshift-gitops) — Deployed
        Which one should I validate?

USER
        rbd4.

AGENT
        (Runs ramenctl validate application --name rbd4 --namespace
        openshift-dr-ops -o ocp/rbd4.)
        All clear. Opening validate-application.html.

Installing skills with ramenctl init

Skills are embedded in the ramenctl binary as Go templates and
installed by ramenctl init. The --agent (-a) flag selects the
format for your AI agent:

$ ramenctl init -a bob
⭐ Using config "config.yaml"

🔎 Initializing ...
   ✅ Created config file "config.yaml" - please modify for your clusters
   ✅ Created skills for Bob in ".bob/skills/"
   ✅ Created context file "AGENTS.md"
      Use "/mode advanced" in Bob to enable skills

✅ Init completed

Supported agents:

Agent Flag Skills directory Context file
Bob -a bob .bob/skills/ .bob/rules/ramenctl.md
Claude Code -a claude .claude/skills/ CLAUDE.md
Cursor -a cursor .cursor/skills/ .cursor/rules/ramenctl.mdc
Generic (default) .agents/skills/ AGENTS.md

Installation uses a write-once model — existing skill files and context
files are never overwritten, preserving user modifications. Running
init again skips existing files with a warning.

Implementation

  • Skill templates live in pkg/skills/templates/skills/ as Go
    text/template files. They support dynamic command names
    (ramenctl vs odf dr).
  • Agent context templates live in pkg/skills/templates/agents/,
    one per agent, providing project overview and skill discovery
    instructions tailored to each agent's format.
  • pkg/skills package is split into three focused files:
    • agent.go: agent definitions, validation, and Agents() list
    • skills.go: skill/command types and template rendering
    • install.go: installation orchestration and file I/O
  • Public API: Install(commandName, agent), ValidateAgent(agent),
    Agents(). Adding a new agent is one constant + one map entry +
    one template file.
  • ramenctl init validates the --agent flag early via
    PreRunE, then runs two steps with the same pattern:
    config.Install() and skills.Install().
  • console.Warn() and console.StepHint() added for non-fatal
    warnings and indented hints.

Tested with

  • Cursor
  • Bob

Issues

  • Bob cannot run test commands. It claims to run the command with 25m timeout but the hardness forces a timeout after 3 minutes. Then Bob may retry the command ignoring instructions and create a mess.
  • Claude Code not tested yet
  • Simplify skill package install
  • Consider not installing skills by default, since agents may not be able to run all commands reliably. If this feature is opt-in we can ship it earlier.

Fixes #453

@coderabbitai

coderabbitai Bot commented May 16, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: b11720fa-37df-469c-98c9-bdb06fdec15b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a skills subsystem and CLI integration: embedded templates for six skills and five agents, rendering and write-once installation logic, agent validation and --agent flag for ramenctl init, console helpers, config helper refactor, tests covering installs and errors, and expanded docs.

Changes

AI Agent Skills Installation

Layer / File(s) Summary
Agent infrastructure
pkg/skills/agent.go
Five agent constants and internal registry mapping agents to display names, skills directories, context files, and optional hints; exported Agents() and ValidateAgent() functions.
Template rendering engine
pkg/skills/skills.go
Embeds templates/ and provides Command/Skill types, a static skills list, renderSkill() and renderContextFile() for rendering Go templates, and directoryName() slug utility.
Installation orchestration
pkg/skills/install.go
Install() coordinates installSkills() and installContextFile(); per-skill directories receive rendered SKILL.md files with skip-if-exists semantics; context files written with exclusive-create; FS helpers and O_EXCL enforce write-once semantics.
Console helpers
pkg/console/console.go
Adds Warn() to emit non-fatal warnings to stderr and StepHint() to print indented suggestion lines under step-level messages.
Config refactoring
pkg/config/config.go
CreateSampleConfig renamed to unexported createSampleConfig; Install() calls the helper and wraps file-exists error to preserve os.ErrExist.
Init command integration
cmd/commands/init.go
Imports pkg/skills, adds --agent flag and agentName var, validates agent in PreRunE, Run delegates to runInit() which runs config.Install() then skills.Install(), and prints completion or failure.
Agent context templates
pkg/skills/templates/agents/*.tmpl
Templates for Cursor, Claude, Codex, Bob, Generic that render per-agent context/rules files (Cursor rules include alwaysApply: true; Bob includes advanced-mode hint; Generic produces AGENTS.md index).
Skill documentation templates
pkg/skills/templates/skills/*.tmpl
Six skill templates (init, validate-clusters, validate-application, gather-application, test-run, test-clean) that render user-facing SKILL.md content with workflows, outputs, and troubleshooting.
Skills test suite
pkg/skills/skills_test.go
Tests validate installation across agents, SKILL.md/context file presence and content, slugification (command → directory), write-once/skip semantics, selective regen, and filesystem/context error cases; includes TestValidateAgent.
Documentation & rules
docs/*, README.md, .cursor/rules/project.mdc
Adds/updates docs to describe AI skills, --agent behavior, safe re-init and test plan details; README agentic usage example; project rule forbids Co-authored-by trailers.

Sequence Diagram(s)

sequenceDiagram
  participant ComponentA
  participant ComponentB
  ComponentA->>ComponentB: observable interaction
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • raghavendra-talur
  • netzzer
  • ELENAGER
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 51.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add AI skills for ramenctl commands' directly describes the main feature added in this PR: embedding and installing AI skills for ramenctl via templates.
Linked Issues check ✅ Passed All primary objectives from issue #453 are implemented: extended init with --agent flag, embedded skill templates, per-agent context files, write-once semantics, six skills installed, PreRunE validation, and documentation updates.
Out of Scope Changes check ✅ Passed All changes directly support AI skills installation or necessary refactoring (config.CreateSampleConfig→unexported, new console helpers, agent registry). No unrelated modifications detected.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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

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

@nirs nirs changed the title Init skills Add AI skills for ramenctl commands May 16, 2026
@nirs
nirs marked this pull request as ready for review May 16, 2026 09:13
coderabbitai[bot]

This comment was marked as outdated.

@nirs
nirs marked this pull request as draft May 17, 2026 10:38
@nirs
nirs force-pushed the init-skills branch 2 times, most recently from 3e8d18a to e709294 Compare May 17, 2026 22:01
@nirs
nirs marked this pull request as ready for review May 17, 2026 22:05

@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

🤖 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 `@docs/skills.md`:
- Around line 69-76: The "Available skills" table contains markdown links
pointing to generated files (e.g., [ramenctl-init](ramenctl-init/SKILL.md),
[ramenctl-validate-clusters](ramenctl-validate-clusters/SKILL.md),
[ramenctl-validate-application](ramenctl-validate-application/SKILL.md),
[ramenctl-gather-application](ramenctl-gather-application/SKILL.md),
[ramenctl-test-run](ramenctl-test-run/SKILL.md),
[ramenctl-test-clean](ramenctl-test-clean/SKILL.md)) which are not in the repo
and produce broken links; update the table to remove or replace these dead
targets by either converting the linked labels to plain text (e.g.,
ramenctl-init, ramenctl-validate-clusters, etc.) or pointing them to a stable
docs location that exists in the repo/site instead, ensuring each entry uses an
existing path or no link at all.

In `@pkg/skills/install.go`:
- Around line 46-57: The current logic in pkg/skills/install.go skips installing
a skill whenever skillDir exists (using os.Mkdir and isDir), which prevents
retry when the directory was created but SKILL.md was never written; change the
check so that you only skip when the SKILL.md file exists inside skillDir.
Concretely: after detecting an existing directory (skillDir), test for the
presence of filepath.Join(skillDir, "SKILL.md") and only append to skipped
(cmd.Slug+"-"+skill.Name) and continue if that SKILL.md exists; if SKILL.md is
missing, proceed with creation/writing of the SKILL.md file. Apply the same
SKILL.md existence check to the other similar block around the code handling
lines 65-68 so retries can repair incomplete installs.
- Around line 18-21: The exported Install path currently panics on unknown agent
names; change the behavior in the agents lookup (the block referencing
agents[agentName]) to return false (and optionally an error log) instead of
calling panic so the caller can handle failure; ensure the lookup in the Install
function (or the surrounding function that references agent, ok :=
agents[agentName]) uses the ValidateAgent contract and returns false when ok is
false rather than terminating the process.

In `@pkg/skills/skills_test.go`:
- Line 25: Update the section comment strings that currently read like "// ---
Install per agent ---" (and the other similar markers "// --- ... ---") so they
end with proper punctuation (e.g., change to "// --- Install per agent. ---");
locate each occurrence by searching for the exact comment text in
pkg/skills/skills_test.go (including the other instances mentioned) and add a
trailing period to conform to the Go comment punctuation guideline.
🪄 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 Plus

Run ID: 80e66e35-8df7-4687-9891-93e66c34ce35

📥 Commits

Reviewing files that changed from the base of the PR and between 8cc6235 and e709294.

📒 Files selected for processing (25)
  • README.md
  • cmd/commands/init.go
  • docs/gather.md
  • docs/init.md
  • docs/skills.md
  • docs/test.md
  • docs/test/plan.md
  • docs/validate.md
  • pkg/config/config.go
  • pkg/console/console.go
  • pkg/skills/agent.go
  • pkg/skills/install.go
  • pkg/skills/skills.go
  • pkg/skills/skills_test.go
  • pkg/skills/templates/agents/bob.tmpl
  • pkg/skills/templates/agents/claude.tmpl
  • pkg/skills/templates/agents/codex.tmpl
  • pkg/skills/templates/agents/cursor.tmpl
  • pkg/skills/templates/agents/generic.tmpl
  • pkg/skills/templates/skills/gather-application.tmpl
  • pkg/skills/templates/skills/init.tmpl
  • pkg/skills/templates/skills/test-clean.tmpl
  • pkg/skills/templates/skills/test-run.tmpl
  • pkg/skills/templates/skills/validate-application.tmpl
  • pkg/skills/templates/skills/validate-clusters.tmpl
✅ Files skipped from review due to trivial changes (10)
  • pkg/skills/templates/skills/test-run.tmpl
  • docs/gather.md
  • pkg/skills/templates/agents/claude.tmpl
  • docs/validate.md
  • pkg/skills/templates/agents/generic.tmpl
  • pkg/skills/templates/agents/codex.tmpl
  • pkg/skills/templates/agents/bob.tmpl
  • pkg/skills/templates/skills/init.tmpl
  • pkg/skills/templates/skills/test-clean.tmpl
  • docs/test.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/console/console.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (3)
**/*.go

📄 CodeRabbit inference engine (.cursor/rules/project.mdc)

Use proper punctuation in comments (end sentences with periods) in Go source files

Keep files focused - separate files for different concerns (e.g., html.go, yaml.go, summary.go)

Files:

  • pkg/skills/agent.go
  • cmd/commands/init.go
  • pkg/config/config.go
  • pkg/skills/skills.go
  • pkg/skills/install.go
  • pkg/skills/skills_test.go
**/*.{go,yaml,yml,sh,makefile,Makefile}

📄 CodeRabbit inference engine (.cursor/rules/project.mdc)

All files need SPDX license headers - check existing files for the format

Files:

  • pkg/skills/agent.go
  • cmd/commands/init.go
  • pkg/config/config.go
  • pkg/skills/skills.go
  • pkg/skills/install.go
  • pkg/skills/skills_test.go
**/*_test.go

📄 CodeRabbit inference engine (.cursor/rules/project.mdc)

Use helpers.FakeTime(t) for time-dependent tests to ensure reproducibility in Go tests

Files:

  • pkg/skills/skills_test.go
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: RamenDR/ramenctl

Timestamp: 2026-05-17T22:06:03.056Z
Learning: Discuss the design with the user before making code changes
Learnt from: CR
Repo: RamenDR/ramenctl

Timestamp: 2026-05-17T22:06:03.056Z
Learning: Show example changes to make sure we are in the right direction before making code changes
Learnt from: CR
Repo: RamenDR/ramenctl

Timestamp: 2026-05-17T22:06:03.056Z
Learning: Ask for more details if the user request is not clear
Learnt from: CR
Repo: RamenDR/ramenctl

Timestamp: 2026-05-17T22:06:03.056Z
Learning: List possible alternatives to make the change
Learnt from: CR
Repo: RamenDR/ramenctl

Timestamp: 2026-05-17T22:06:03.056Z
Learning: Do the minimal change needed, no extra code that is not needed right now
Learnt from: CR
Repo: RamenDR/ramenctl

Timestamp: 2026-05-17T22:06:03.056Z
Learning: Avoid unrelated changes such as spelling, whitespace, etc.
Learnt from: CR
Repo: RamenDR/ramenctl

Timestamp: 2026-05-17T22:06:03.056Z
Learning: Keep changes small to make human review easy and avoid mistakes
Learnt from: CR
Repo: RamenDR/ramenctl

Timestamp: 2026-05-17T22:06:03.056Z
Learning: Run `make pre-commit` to format code and check lint and spelling errors before committing
Learnt from: CR
Repo: RamenDR/ramenctl

Timestamp: 2026-05-17T22:06:03.056Z
Learning: Use package tests for quick iteration with `go test ./pkg/foo/...`
Learnt from: CR
Repo: RamenDR/ramenctl

Timestamp: 2026-05-17T22:06:03.056Z
Learning: Before committing, run `make test` to catch issues early
Learnt from: CR
Repo: RamenDR/ramenctl

Timestamp: 2026-05-17T22:06:03.056Z
Learning: Check existing code for error formatting conventions when implementing error handling
Learnt from: CR
Repo: RamenDR/ramenctl

Timestamp: 2026-05-17T22:06:03.056Z
Learning: NEVER commit directly to main branch
Learnt from: CR
Repo: RamenDR/ramenctl

Timestamp: 2026-05-17T22:06:03.056Z
Learning: NEVER push to GitHub without user review
Learnt from: CR
Repo: RamenDR/ramenctl

Timestamp: 2026-05-17T22:06:03.056Z
Learning: Keep commits small and focused
Learnt from: CR
Repo: RamenDR/ramenctl

Timestamp: 2026-05-17T22:06:03.056Z
Learning: For unrelated changes, create a new branch from main
Learnt from: CR
Repo: RamenDR/ramenctl

Timestamp: 2026-05-17T22:06:03.056Z
Learning: Avoid complicated git operations. The user can rebase later
Learnt from: CR
Repo: RamenDR/ramenctl

Timestamp: 2026-05-17T22:06:03.056Z
Learning: Commit messages should explain why the change was made - what are we trying to do
Learnt from: CR
Repo: RamenDR/ramenctl

Timestamp: 2026-05-17T22:06:03.056Z
Learning: Commit messages should explain how the change affects the user - what is the new or modified behavior
Learnt from: CR
Repo: RamenDR/ramenctl

Timestamp: 2026-05-17T22:06:03.056Z
Learning: If the change affects performance, include measurements and description of how we measured in commit messages
Learnt from: CR
Repo: RamenDR/ramenctl

Timestamp: 2026-05-17T22:06:03.056Z
Learning: If the change modifies the output, include example output with and without the change in commit messages
Learnt from: CR
Repo: RamenDR/ramenctl

Timestamp: 2026-05-17T22:06:03.056Z
Learning: If the change introduces new logs, show example logs including the changed or new logs in commit messages
Learnt from: CR
Repo: RamenDR/ramenctl

Timestamp: 2026-05-17T22:06:03.056Z
Learning: If several alternatives were considered, explain why we chose the particular solution in commit messages
Learnt from: CR
Repo: RamenDR/ramenctl

Timestamp: 2026-05-17T22:06:03.056Z
Learning: Discuss the negative effects of the change if any in commit messages
Learnt from: CR
Repo: RamenDR/ramenctl

Timestamp: 2026-05-17T22:06:03.056Z
Learning: If the change includes new APIs, describe the new APIs and how they are used in commit messages
Learnt from: CR
Repo: RamenDR/ramenctl

Timestamp: 2026-05-17T22:06:03.056Z
Learning: Avoid describing details that are best seen in the diff in commit messages
Learnt from: CR
Repo: RamenDR/ramenctl

Timestamp: 2026-05-17T22:06:03.056Z
Learning: Include `Assisted-by: Cursor/{model}` footer with full model version (e.g., `Cursor/Claude Opus 4.6`) in commit messages
Learnt from: CR
Repo: RamenDR/ramenctl

Timestamp: 2026-05-17T22:06:03.056Z
Learning: NEVER add `Co-authored-by` trailer in commit messages - use only `Assisted-by`
📚 Learning: 2026-05-17T21:55:49.135Z
Learnt from: nirs
Repo: RamenDR/ramenctl PR: 455
File: docs/init.md:123-126
Timestamp: 2026-05-17T21:55:49.135Z
Learning: This repository does not use markdownlint for enforcement. During code reviews, do not flag or comment on markdownlint rule warnings (e.g., MD028, MD014, MD040 or any other markdownlint rule IDs) in markdown files, even if they appear in diff output or editor warnings.

Applied to files:

  • README.md
  • docs/init.md
  • docs/skills.md
  • docs/test/plan.md
📚 Learning: 2026-05-17T21:55:51.068Z
Learnt from: nirs
Repo: RamenDR/ramenctl PR: 455
File: docs/test/plan.md:209-209
Timestamp: 2026-05-17T21:55:51.068Z
Learning: Do not report or flag violations that would be raised by the markdownlint rules (the “MD*” family, e.g., MD040 for fenced-code-language). Since this repository does not use markdownlint, markdown formatting checks tied to markdownlint should be ignored across all Markdown files.

Applied to files:

  • README.md
  • docs/init.md
  • docs/skills.md
  • docs/test/plan.md
🪛 markdownlint-cli2 (0.22.1)
README.md

[warning] 87-87: Dollar signs used before commands without showing output

(MD014, commands-show-output)

docs/init.md

[warning] 126-126: Blank line inside blockquote

(MD028, no-blanks-blockquote)

docs/skills.md

[warning] 84-84: Dollar signs used before commands without showing output

(MD014, commands-show-output)

docs/test/plan.md

[warning] 209-209: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 272-272: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 301-301: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 328-328: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 355-355: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 381-381: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 403-403: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 432-432: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (18)
pkg/skills/skills_test.go (1)

27-83: LGTM!

Also applies to: 87-97, 101-161, 165-207, 211-236, 240-317

README.md (1)

80-90: LGTM!

Also applies to: 103-103

docs/init.md (1)

6-7: LGTM!

Also applies to: 11-11, 17-17, 23-23, 26-30, 33-42, 50-64, 73-83, 97-106, 123-126

docs/skills.md (1)

1-65: LGTM!

Also applies to: 78-117

docs/test/plan.md (1)

24-30: LGTM!

Also applies to: 199-199, 209-219, 224-228, 242-242, 256-259, 260-390, 401-447

pkg/config/config.go (3)

35-49: LGTM!


51-78: LGTM!


80-81: LGTM!

cmd/commands/init.go (4)

6-21: LGTM!


23-35: LGTM!


37-41: LGTM!


43-59: LGTM!

pkg/skills/templates/agents/cursor.tmpl (1)

1-17: LGTM!

pkg/skills/templates/skills/gather-application.tmpl (1)

1-112: LGTM!

pkg/skills/templates/skills/validate-application.tmpl (1)

1-164: LGTM!

pkg/skills/templates/skills/validate-clusters.tmpl (1)

1-122: LGTM!

pkg/skills/agent.go (1)

1-78: LGTM!

pkg/skills/skills.go (1)

1-110: LGTM!

Comment thread docs/skills.md Outdated
Comment thread pkg/skills/install.go
Comment thread pkg/skills/install.go
Comment thread pkg/skills/skills_test.go Outdated
Comment thread pkg/skills/install.go
if err := os.MkdirAll(agent.SkillsDir, 0o755); err != nil {
console.Error("failed to create directory %q: %s", agent.SkillsDir, err)
return false
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Can be removed, creating a skill directory can use os.MkdirAll.

Comment thread pkg/skills/install.go
console.Error("expected directory but found file %q", skillDir)
return false
}
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This can be simplified to os.MkdirAll() call - it already handles existing directory, and any other error is fatal.

Comment thread pkg/skills/install.go

func isRegularFile(path string) bool {
info, err := os.Stat(path)
return err == nil && info.Mode().IsRegular()

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This hides errors in os.Stat(). Should return (bool, error) so the caller can report fatal errors.

Comment thread pkg/skills/install.go

dir := filepath.Dir(agent.ContextFile)
if dir != "." {
if err := os.MkdirAll(dir, 0o755); err != nil {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

should not create world readable directory.

Comment thread pkg/skills/install.go
}

func installSkills(cmd Command, agent agent) bool {
if err := os.MkdirAll(agent.SkillsDir, 0o755); err != nil {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

should not create world readable directory.

Comment thread pkg/skills/install.go
for _, skill := range skills {
skillDir := filepath.Join(agent.SkillsDir, cmd.Slug+"-"+skill.Name)

if err := os.Mkdir(skillDir, 0o755); err != nil {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

should not create world readable directory.

Comment thread pkg/skills/install.go
func isDir(path string) bool {
info, err := os.Stat(path)
return err == nil && info.IsDir()
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

same as isRegularFile - hides error, but we should be able to remove it.

deploy, protect, failover, relocate, unprotect, undeploy.

**This typically takes 15-20 minutes per test case.** Multiple test cases
run in parallel.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Need to tell the agent to NEVER run this in the background. When testing bob, it started the tests in the background, and this makes it very hard to monitor progress and ruin the user experience.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

There is not way to prevent running in background in bob without failing. Current instructions works well for Cursor and fail with bob when running the test command. I'll file a bob bug report.

@caioribeiroclw-pixel

Copy link
Copy Markdown

Thanks for the pointer — I checked #455 and ran the focused package test locally:

$ go test ./pkg/skills
ok  github.com/ramendr/ramenctl/pkg/skills  0.014s

The implementation is cleaner than I expected: each agent has its own context template, and Codex/generic sharing .agents/skills is reasonable if that is the intended common fallback.

One edge case I’d consider before merging: installContextFile() is write-once for the context file, but skills are agent-specific. That means this sequence can silently produce a mismatched install:

ramenctl init -a generic   # creates AGENTS.md pointing at .agents/skills/...
ramenctl init -a bob       # creates .bob/skills/... but AGENTS.md already exists, so it is not updated

The command succeeds and prints Bob’s advanced-mode hint, but the existing AGENTS.md may still point to .agents/skills/..., not .bob/skills/.... The same general issue applies any time the context file already exists and does not mention the newly installed skill directory.

Possible small fix without abandoning write-once ownership:

  • when context file exists, inspect whether it contains agent.SkillsDir or the expected command/skill index/agent marker;
  • if not, warn more explicitly, e.g. Context file "AGENTS.md" already exists and may not reference ".bob/skills/"; add the skill index manually or run in a clean directory;
  • add a regression test for generic → bob (or existing AGENTS.md without .bob/skills).

This is exactly the kind of “files installed, semantics not wired” fidelity issue that tends to be hard to debug later. The current tests are good on fresh installs; this would cover the mixed-agent/idempotent path.

@nirs

nirs commented May 18, 2026

Copy link
Copy Markdown
Member Author

One edge case I’d consider before merging: installContextFile() is write-once for the context file, but skills are agent-specific. That means this sequence can silently produce a mismatched install:

ramenctl init -a generic   # creates AGENTS.md pointing at .agents/skills/...
ramenctl init -a bob       # creates .bob/skills/... but AGENTS.md already exists, so it is not updated

The command succeeds and prints Bob’s advanced-mode hint, but the existing AGENTS.md may still point to .agents/skills/..., not .bob/skills/.... The same general issue applies any time the context file already exists and does not mention the newly installed skill directory.

Possible small fix without abandoning write-once ownership:

  • when context file exists, inspect whether it contains agent.SkillsDir or the expected command/skill index/agent marker;
  • if not, warn more explicitly, e.g. Context file "AGENTS.md" already exists and may not reference ".bob/skills/"; add the skill index manually or run in a clean directory;
  • add a regression test for generic → bob (or existing AGENTS.md without .bob/skills).

This is exactly the kind of “files installed, semantics not wired” fidelity issue that tends to be hard to debug later. The current tests are good on fresh installs; this would cover the mixed-agent/idempotent path.

Thanks for looking at this, this is unlikely usage for ramenctl but possible. I think the right way would be to add an agent specific section in AGENETNS.md like:

## ramenctl skills for bob

Content for bob...

## ramenctl skills

Content for generic agent ...

The command can parse the markdown and make sure it has the right section, or warn if the section does not exist.

In the worst case AGENTS.md will have content for both bob and generic agent, so we waste some tokens, but the agent should be able to find the skills.

Or maybe we have a way to add bob instructions in .bob/ like cursor/claude?

Comment thread pkg/skills/skills_test.go
t.Errorf("%s should not contain template syntax", path)
}
}
}

@nirs nirs May 18, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Missing tests - install twice with different agents

  • Install generic after bob was installed
    • instructions for generic agent should be added to AGENTS.md
    • validate skills for both agents
  • Install bob after generic was installed
    • instructions for bob should be added to AGENTS.md
    • validate skills for both agents
  • Install codex after bob
    • instructions for codex should be added to AGENTS.md
    • validate skills for both agents
  • Install bob after codex
    • instructions for bob should be added to AGENTS.md
    • validate skills for both agents
  • Install same agent twice
    • agent specific section appears once

@caioribeiroclw-pixel

Copy link
Copy Markdown

That sectioned AGENTS.md approach feels like the right compromise to me.

I’d make the sections deliberately machine-detectable, not just human-readable, because this is where idempotency tends to get slippery. Something like:

<!-- ramenctl:skills agent=bob dir=.bob/skills command=ramenctl -->
## ramenctl skills for Bob
...
<!-- /ramenctl:skills -->

<!-- ramenctl:skills agent=generic dir=.agents/skills command=ramenctl -->
## ramenctl skills for generic agents
...
<!-- /ramenctl:skills -->

Then the installer can keep the write-once/low-risk model but still answer a precise question: “does the existing context file wire the skills dir I just installed?”

Minimal behavior I’d aim for in this PR:

  1. fresh bob install creates .bob/skills and an AGENTS.md section that mentions .bob/skills and advanced mode;
  2. generic -> bob either appends a Bob-specific section or warns that the existing AGENTS.md does not reference .bob/skills;
  3. repeated bob -> bob is idempotent and does not duplicate the section;
  4. future codex -> bob can share the same mechanism without treating all AGENTS.md consumers as equivalent.

If Bob has/gets a native context path under .bob/, that would be cleaner long term because it avoids mixing Codex/generic/Bob instructions in one AGENTS.md. But until then, marked sections seem safer than relying on headings alone: they keep token waste bounded, make tests straightforward, and give users a clear warning instead of a silent “files installed but not discoverable” state.

@nirs

nirs commented May 18, 2026

Copy link
Copy Markdown
Member Author

If Bob has/gets a native context path under .bob/, that would be cleaner long term because it avoids mixing Codex/generic/Bob instructions in one AGENTS.md. But until then, marked sections seem safer than relying on headings alone: they keep token waste bounded, make tests straightforward, and give users a clear warning instead of a silent “files installed but not discoverable” state.

Bob supports .bob/rules/*.md - we should use it instead of AGENTS.md.
https://internal.bob.ibm.com/docs/ide/configuration/rules

This leaves only Codex and Generic sharing AGENTS.md. Since both use .agents/skills, we can have the same content in the context file. The skill index is not needed by codex, but some wasted context for codex is better than having to maintain per agent markdown sections for one agent.

With this we can actually remove codex from the list since we don't have specific codex configuration, the generic configuration works.

@caioribeiroclw-pixel

Copy link
Copy Markdown

That makes the shape much cleaner.

If Bob has native .bob/rules/*.md, I would use that and stop routing Bob through AGENTS.md. Then the model is easier to explain and test:

cursor   -> .cursor/rules/...        native rule discovery + metadata
claude   -> .claude/skills/...       native skills
bob      -> .bob/rules/...           native Bob rules / advanced-mode note
codex    -> AGENTS.md + .agents/...  generic AGENTS.md path, if no Codex-only behavior
generic  -> AGENTS.md + .agents/...  same fallback/index

In that version I agree with removing codex as a distinct target unless there is a Codex-specific semantic difference. Otherwise --agent codex risks implying a fidelity level that is not actually different from generic.

The acceptance criterion I’d keep is still the same, just simpler:

  1. each advertised agent target writes to that agent’s native discovery surface when one exists;
  2. if two targets share the exact same files/content, expose one target name or document the alias explicitly;
  3. fresh install + repeat install are idempotent;
  4. mixed-agent installs never leave a success message where the new agent’s native discovery path is absent/unreferenced;
  5. tests assert the semantic surface, not only file existence: Cursor gets .cursor/rules, Bob gets .bob/rules, Claude gets .claude/skills, generic gets AGENTS.md/.agents.

That avoids the token-waste issue in AGENTS.md and gives users a better mental model: native where possible, generic fallback only where necessary.

@caioribeiroclw-pixel

Copy link
Copy Markdown

Quick follow-up with a concrete artifact from this thread: I shipped the Bob/native-rules mapping in pluribus-context@0.3.19.

The relevant behavior is intentionally small:

npx --yes pluribus-context@latest init --tools bob
npx --yes pluribus-context@latest sync
npx --yes pluribus-context@latest audit --json --fidelity-report

The Bob target now writes .bob/rules/pluribus.md, and the fidelity report marks Bob as:

{
  "toolId": "bob",
  "nativeDiscoverySurface": ".bob/rules/*.md",
  "genericFallback": false,
  "manualActivationRequired": false
}

So the external sanity check I’d apply here is: if ramenctl init --agent bob writes to .bob/rules/..., Bob should show up as a native target; if codex and generic share the same AGENTS.md/.agents output, that should be explicit as an alias/fallback, not advertised as a separate semantic surface.

No need to adopt Pluribus, but the native-vs-fallback vocabulary is now executable if it helps test/review this PR.

@caioribeiroclw-pixel caioribeiroclw-pixel 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.

Re-checked the refreshed branch at f35cad61 against the native-discovery concern from the May thread. The Bob path is now wired to .bob/rules/ramenctl.md while skills stay under .bob/skills, and the focused test asserts that native context file:

$ go test ./pkg/skills
ok github.com/ramendr/ramenctl/pkg/skills 0.015s

I also verified Bob no longer routes through AGENTS.md. That resolves the mixed-agent/write-once mismatch I raised: Bob, Cursor, and Claude now use their own discovery surfaces; Codex/generic remain the shared AGENTS.md fallback. The current mapping looks consistent to me.

@nirs

nirs commented Jul 16, 2026

Copy link
Copy Markdown
Member Author

@caioribeiroclw-pixel Thanks for reviewing! But this is a draft and not ready for review. I rebased for a demo, but this needs more works.

@caioribeiroclw-pixel

Copy link
Copy Markdown

You're right — sorry, I jumped the review boundary. I treated the force-push plus green focused tests as review-ready even though the PR is still marked draft.

The native-path check was useful to me, but the formal approval was premature. I won't add further review feedback until you mark it ready or explicitly ask. Thanks for correcting me.

@nirs

nirs commented Jul 16, 2026

Copy link
Copy Markdown
Member Author

@caioribeiroclw-pixel Your review it is very helpful. I just don't want to waste your time on reviewing partial work.

nirs added 15 commits July 20, 2026 19:37
We use only the Assisted-by trailer to credit AI assistance.
The Co-authored-by trailer is added automatically by Cursor
and should not be included.

Assisted-by: Cursor/Claude Opus 4.6
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
Add the init skill that guides AI agents through configuring
config.yaml for disaster recovery clusters. The skill covers:

- Discovering kubeconfigs and configuring clusters
- Looking up OCM cluster names and finding the clusterSet
- Discovering DRPolicies and matching peer classes for pvcSpecs
- Configuring test options (drPolicy, pvcSpecs, tests)

Skills use Go templates with {{.CommandName}} and {{.CommandSlug}}
to support both "ramenctl" and "odf dr" command names. YAML
frontmatter is always included for tool auto-discovery.

Assisted-by: Cursor/Claude Opus 4.6
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
Guide AI agents through running validate clusters, checking the
result, opening the HTML report, inspecting gathered data, and
troubleshooting problems.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
Guide AI agents through looking up protected applications, running
validate application, opening the HTML report, inspecting gathered
data, troubleshooting problems, and suggesting validate clusters
for a complete picture.

Assisted-by: Cursor/Claude Opus 4.6
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
Guide AI agents through looking up protected applications, running
gather application, and inspecting the gathered data structure.
The gathered data can be archived and uploaded for bug reports.

Assisted-by: Cursor/Claude Opus 4.6
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
Guide AI agents through running DR flow tests and cleaning up
afterwards. The agent will not clean up without an explicit user
request to preserve evidence from failed tests. Both skills
require passing validate clusters before proceeding.

Assisted-by: Cursor/Claude Opus 4.6
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
Add per-agent context file templates that introduce ramenctl to AI
agents. Every agent gets a short overview explaining that ramenctl
manages disaster recovery and listing the available commands (init,
validate, gather, test).

Each template targets the agent's native context file format:

- cursor.tmpl  → .cursor/rules/ramenctl.mdc with alwaysApply frontmatter
  so the overview is always in context when working in the project.
- claude.tmpl  → CLAUDE.md, plain markdown overview.
- codex.tmpl   → AGENTS.md, plain markdown overview.
- bob.tmpl     → AGENTS.md with an instruction to switch to advanced
  mode, which is required for Bob to discover skills.
- generic.tmpl → AGENTS.md with a full skill index listing each
  SKILL.md path, since a generic agent may not auto-discover skills.

Cursor, Claude Code, and Codex auto-discover skills in their native
directories, so their context files only need the project overview.
Bob and generic agents need extra help: Bob needs to be told to enable
advanced mode, and generic agents need explicit paths to each skill.

Assisted-by: Cursor/Claude Opus 4.6
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
Add Warn for non-fatal warnings at step level, and StepHint for
suggestions indented under step messages (Pass/Warn). Hint remains
for top-level suggestions (e.g. after Info in browser.go).

The ⚠️  emoji is two Unicode code points: ⚠ (U+26A0) + variation selector
(U+FE0F). The variation selector forces color emoji rendering but
consumes one visual cell in some terminals (e.g. macOS Terminal.app),
eating the trailing space. We compensate with an extra space after ⚠️  to
align with single code point emojis like ✅.

Ideally the console would be stateful, tracking the current nesting
level so a single Hint function could indent correctly. For now we
use two functions with hardcoded indentation.

Assisted-by: Cursor/Claude Opus 4.6
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
Install(commandName, agent) installs skill files and a context file
for the specified AI agent. Each agent gets skills in its expected
directory and a context file that instructs the agent to read the
matching skill before responding to user requests.

Installation uses a write-once model - existing skill files and
context files are never overwritten, preserving user modifications.
A second run skips existing files with a warning.

Public API:

- Install(commandName, agent) - install skills and context file for
  the specified agent. Returns true on success.
- ValidateAgent(agent) - return error if the agent name is not
  supported. Used for early flag validation.
- Agents() - return sorted list of supported agent names. Used to
  generate the --agent flag help text.

Supported agents: bob, claude, codex, cursor, generic (default).

The package is split into three files:
- agent.go: agent definitions, validation, and agent list
- skills.go: skill/command types and template rendering
- install.go: installation orchestration and file I/O

Assisted-by: Cursor/Claude Opus 4.6
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
Extend `ramenctl init` to install AI agent skills after creating the
configuration file. The --agent (-a) flag selects the target agent,
with the agent list generated from skills.Agents().

The init command now runs two steps with the same pattern:
- config.Install() - create config file (warn if exists)
- skills.Install() - install skills and context file

Add config.Install() to handle console output and write-once
behavior, matching the skills.Install() pattern. The previous
CreateSampleConfig() is now private since callers should use
Install() instead.

The --agent flag is validated early in PreRunE so cobra shows
usage on invalid input.

Example run:

    % ramenctl init -h
    Create configuration file and install AI skills

    Usage:
      ramenctl init [flags]

    Flags:
      -a, --agent string     AI agent to install skills for (bob, claude, codex, cursor, generic) (default "generic")
          --envfile string   ramen testing environment file
      -h, --help             help for init

    Global Flags:
      -c, --config string   configuration file (default "config.yaml")
          --interactive     enable interactive features (default auto)

    % ramenctl init -a bob
    ⭐ Using config "config.yaml"

    🔎 Initializing ...
       ✅ Created config file "config.yaml" - please modify for your clusters
       ✅ Created skills for Bob in ".bob/skills/"
       ✅ Created context file "AGENTS.md"
          Use "/mode advanced" in Bob to enable skills

    ✅ Init completed

Assisted-by: Cursor/Claude Opus 4.6
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
Add docs/skills.md describing agentic usage, available skills,
where skills are installed per agent, and how to add a new agent.

Update docs/init.md with current help output, example output, and
a brief AI skills section linking to docs/skills.md for details.

Add tips about agentic usage to docs/validate.md, docs/gather.md,
and docs/test.md.

Assisted-by: Cursor/Claude Opus 4.6
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
New test cases:
- Init with each agent (cursor, claude, codex, bob)
- Init with invalid --agent
- Re-init is safe (write-once model)

Updated test cases:
- Create default config: includes skills and context file output
- Create named config: includes skills installation
- Create config from envfile: includes skills installation
- Config already exists: skills and context file still installed

Assisted-by: Cursor/Claude Opus 4.6
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
Highlight that ramenctl is agentic-ready out of the box and link
to the new AI skills guide.

Assisted-by: Cursor/Claude Opus 4.6
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
Based on feedback from bob, the instructions should be more explicit.
Reword to work better with bob.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
NOTE: need to rebase and fix the right commit.
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
@nirs
nirs force-pushed the init-skills branch 3 times, most recently from 792ee1b to 93f8d73 Compare July 20, 2026 18:01
Without this, the agent picks an output directory on its own (e.g.
odf/namespace-application) and the user has no way to change it.

Add a "Pick an output directory" step to the validate-application,
gather-application, test-run, and test-clean skills. The
validate-clusters skill already had this step but used a vague <env>
placeholder.

All skills now suggest a unique name with a timestamp including hours
and minutes (e.g., out/clusters-2026-07-20-19-46). This ensures
consistent naming across runs and avoids reusing an existing
directory, which causes output files with numeric suffixes (-2, -3)
that confuse the agent when inspecting results.

Also rename <output-dir> to <output-directory> to match the step
heading and reinforce that the agent should use the directory chosen
in the previous step.

Assisted-by: Cursor/Claude Opus 4.6
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
nirs added 2 commits July 20, 2026 21:37
When a ramenctl command times out (e.g. slow S3 endpoint), the agent
retries it in the background with a new output directory. This wastes
time and creates unexpected partial output.

Add rules to all agents context to not run commands in the background
and not retry on timeout — report it and let the user decide.

Assisted-by: Cursor/Claude Opus 4.6
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
When an S3 endpoint is slow, ramenctl hangs because it has no timeout
on S3 calls. The agent shell harness kills the command after 120
seconds, then the agent retries in the background with a new output
directory — wasting time and creating unexpected partial output.

Remove time estimates from the skills ("takes a few seconds locally
and about a minute on remote clusters") since the agent uses them to
guess a timeout. Add explicit rules to all agent context files:
- Run ramenctl commands without a timeout (timeout=0 for bob)
- Do not run commands in the background
- Do not retry on timeout — report it and let the user decide

The real fix is adding S3 call timeouts in ramenctl itself so it
fails fast instead of hanging. These skill changes are a workaround
until that is done.

Assisted-by: Cursor/Claude Opus 4.6
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
nirs added 2 commits July 20, 2026 22:32
The agent invents a config schema when extracting the hub kubeconfig:

    kubectl get drpc -A --kubeconfig \
        $(yq '.clusters[] | select(.role == "hub") | .kubeconfig' config.yaml)

The config does not have a role field — clusters are named keys. Tell
the agent to read the hub kubeconfig path from clusters.hub.kubeconfig
in the config file instead of guessing the schema.

Assisted-by: Cursor/Claude Opus 4.6
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
The previous generic "do not retry" rules were not strong enough —
bob kept using 120-180s timeouts and retrying commands in the
background.

Use bob's own suggested format with CRITICAL/ALWAYS/NEVER emphasis
and exact execute_command XML examples showing timeout=0 and
background=false. Also add "NEVER cancel or kill a running command."

Whether this helps depends on the harness — if it enforces a 180s
timeout regardless of what the agent requests, these rules cannot
override it. The real fix is separating running commands from
watching progress (RamenDR#459) so the agent can safely timeout on progress
and restart without losing work.

Assisted-by: Cursor/Claude Opus 4.6
Signed-off-by: Nir Soffer <nsoffer@redhat.com>
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.

ramenctl init: install AI skills alongside config

2 participants