Skip to content

feat(codex): migrate .codex skills to .agents#1157

Open
satyaborg wants to merge 6 commits into
Fission-AI:mainfrom
satyaborg:feat/migrate-codex-skills-to-agents
Open

feat(codex): migrate .codex skills to .agents#1157
satyaborg wants to merge 6 commits into
Fission-AI:mainfrom
satyaborg:feat/migrate-codex-skills-to-agents

Conversation

@satyaborg
Copy link
Copy Markdown

@satyaborg satyaborg commented Jun 2, 2026

summary

  • migrates legacy .codex/skills/openspec-*to .agents/skills/openspec-*
  • detects and regenerates skills under new canonical path
  • applies the same cleanup behaviour to workspace setup/update
  • updates docs and tests

tests

  • pnpm run build
  • pnpm run lint
  • focused Vitest suite: 8 files, 260
    tests
  • git diff --check
  • manually verify wiring

Summary by CodeRabbit

  • New Features

    • Codex skills now install under .agents/skills; legacy .codex/skills OpenSpec-managed directories are detected and migrated during init/update/workspace flows, preserving unmanaged content and reporting how many managed legacy dirs were removed.
  • Documentation

    • CLI, commands, and supported-tools docs updated to reference .agents/skills and to advise checking the selected tool’s skill directory.
  • Tests

    • Suites expanded for migration, preservation, failure handling, reporting, and cross‑platform paths.

@satyaborg satyaborg requested a review from TabishB as a code owner June 2, 2026 05:48
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 2, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e8973bd0-9b87-4001-a945-7d349684ff04

📥 Commits

Reviewing files that changed from the base of the PR and between f992845 and 732bf7c.

📒 Files selected for processing (1)
  • openspec/changes/migrate-codex-skills-to-agents/proposal.md
✅ Files skipped from review due to trivial changes (1)
  • openspec/changes/migrate-codex-skills-to-agents/proposal.md

📝 Walkthrough

Walkthrough

Migrates Codex skill generation from .codex/skills to .agents/skills, adds legacy-detection metadata and shared path helpers, implements a migration module to remove managed legacy directories after successful regeneration, integrates removals into init/update/workspace flows, refactors drift/migration checks, and updates tests/docs.

Changes

Codex Skills Migration

Layer / File(s) Summary
Design, Specification, and Requirements
openspec/changes/migrate-codex-skills-to-agents/*
Proposal, design, specs, and tasks define migration: generate under .agents/skills, detect legacy .codex/skills, remove only managed OpenSpec legacy dirs after successful replacement, preserve unmanaged content, and list tests.
User-Facing Documentation Updates
docs/cli.md, docs/commands.md, docs/supported-tools.md
Docs updated to reference .agents/skills for Codex, added migration notes, and replaced Claude-specific troubleshooting with tool-agnostic guidance.
Tool Configuration & Shared Path Helpers
src/core/config.ts, src/core/shared/tool-detection.ts, src/core/shared/index.ts
AIToolOption gains legacySkillsDirs; Codex uses skillsDir: '.agents' with legacySkillsDirs/detectionPaths. New helpers resolve current/legacy skill directories and OpenSpec-managed skill names with cross-platform path handling.
Codex Legacy Migration Core Module
src/core/codex-skill-migration.ts
New helpers: compute managed legacy Codex skill directories, detect existence, and remove managed legacy directories asynchronously while returning a removal count.
Init Command Integration and Tests
src/core/init.ts, test/core/init.test.ts
Init imports removal helper, accumulates migrated count, includes it in success output, and tests verify .agents/skills generation and managed-legacy removal while preserving unmanaged content.
Update Command Integration and Tests
src/core/update.ts, test/core/update.test.ts
Update calls removal helper in per-tool loop and legacy-upgrade path, accumulates counts, prints summary, and tests cover current .agents updates, legacy-to-current migration, and failure behavior.
Workspace Skills Integration and Tests
src/core/workspace/skills.ts, test/core/workspace/skills.test.ts
Workspace report type gains migrated_legacy_codex_skill_count; generation/update call removal helper for Codex after writes; tests exercise managed removal, unmanaged preservation, and error paths.
Profile Drift & Migration Refactor and Tests
src/core/profile-sync-drift.ts, src/core/migration.ts, test/core/migration.test.ts, test/core/shared/tool-detection.test.ts
Refactors to use shared getToolCurrentSkillDirectory/getToolLegacySkillDirectories; drift triggers on managed legacy dirs; migration scanning checks all resolved skill directories; tests updated for dual-path detection and version reading.
Tool Availability Detection and Tests
test/core/available-tools.test.ts
Tests assert Codex AI_TOOLS metadata contains legacy metadata and that detection works from both .agents/skills and .codex/skills.
Workspace Output and Command Tests
src/commands/workspace.ts, test/commands/workspace.test.ts
Workspace command prints a human-readable migration summary when legacy Codex skills are removed; tests adjust assertions to .agents layout and validate migration behavior.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Fission-AI/OpenSpec#736: Overlaps profile sync/drift refactoring and detection of installed workflows across skill directories.

Suggested reviewers

  • TabishB
  • alfred-openspec

Poem

🐰 Hooray, I hop and sing,
Skills move homes, a gentle spring.
From .codex nests to .agents fields,
Managed crumbs removed, the tidy yields.
Unmanaged keepsakes safe—what joy I bring!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.81% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(codex): migrate .codex skills to .agents' accurately and concisely describes the main change—migrating legacy Codex skill directories from .codex/skills to .agents/skills.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 and usage tips.

@satyaborg
Copy link
Copy Markdown
Author

related to #1143, this PR handles the separate path migration from legacy .codex/skills to current .agents/skills, including safe cleanup and workspace coverage

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
test/core/migration.test.ts (1)

90-101: 💤 Low value

CODEX_HOME appears unnecessary for this test.

migrateIfNeeded scans projectDir and resolves legacy skills via the tool's legacySkillsDirs; it does not read CODEX_HOME. Setting it (Line 91) doesn't affect the assertions and may mislead future readers into thinking global resolution is exercised. Consider dropping it unless it guards against an environment side-effect.

🤖 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 `@test/core/migration.test.ts` around lines 90 - 101, The test unnecessarily
sets process.env.CODEX_HOME; remove the assignment (process.env.CODEX_HOME =
...) on the test that calls migrateIfNeeded so the test only relies on
projectDir-based legacy detection; locate the lines around
migrateIfNeeded(projectDir, [ensureCodexTool()]), writeLegacyCodexSkill(...) and
readRawConfig() in test/core/migration.test.ts and delete the CODEX_HOME
assignment (or clear it with delete process.env.CODEX_HOME) to avoid misleading
readers or environment side-effects.
src/core/profile-sync-drift.ts (1)

206-222: 💤 Low value

Reuse getToolSkillDirectories instead of rebuilding the current+legacy list.

migration.ts already uses the shared getToolSkillDirectories helper for exactly this (current dir + legacy when included). The manual construction here duplicates that logic; the directory list is only consumed inside the includeSkills block.

♻️ Proposed refactor
-  const installed = new Set<WorkflowId>();
-  const skillDirectories = [getToolCurrentSkillDirectory(projectPath, tool)].filter(
-    (skillsDir): skillsDir is string => skillsDir !== null
-  );
-
-  if (options.includeSkills) {
-    skillDirectories.push(...getToolLegacySkillDirectories(projectPath, tool));
-
-    for (const skillsDir of skillDirectories) {
+  const installed = new Set<WorkflowId>();
+
+  if (options.includeSkills) {
+    for (const skillsDir of getToolSkillDirectories(projectPath, tool)) {
       for (const workflow of ALL_WORKFLOWS) {
         const dirName = WORKFLOW_TO_SKILL_DIR[workflow];
         const skillFile = path.join(skillsDir, dirName, 'SKILL.md');
         if (fs.existsSync(skillFile)) {
           installed.add(workflow);
         }
       }
     }
   }

Update the import to pull getToolSkillDirectories from ./shared/index.js.

🤖 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 `@src/core/profile-sync-drift.ts` around lines 206 - 222, Replace the manual
construction of skillDirectories (which uses getToolCurrentSkillDirectory and
getToolLegacySkillDirectories) with the shared helper getToolSkillDirectories;
import getToolSkillDirectories from ./shared/index.js and set skillDirectories =
getToolSkillDirectories(projectPath, tool, options.includeSkills) (or call the
helper in the same way other code does), then keep the existing loop over
skillDirectories/ALL_WORKFLOWS/WORKFLOW_TO_SKILL_DIR to populate installed —
remove the duplicated legacy/current logic and its imports.
🤖 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 `@src/core/codex-skill-migration.ts`:
- Around line 33-40: The removeManagedLegacyCodexSkills function currently
propagates any rm error and fails the whole setup; change it to perform
best-effort per-directory removal by wrapping the await fs.promises.rm(skillDir,
...) call in a try/catch inside the for loop, log or warn on error (e.g., using
processLogger.warn or console.warn) including the skillDir and error, do not
rethrow so the loop continues, and only increment removed when rm succeeds; keep
using getManagedLegacyCodexSkillDirectories to locate dirs and return the
accurate removed count.

In `@src/core/init.ts`:
- Around line 551-553: The migration call removeManagedLegacyCodexSkills is
currently inside the skillTemplates loop and runs once per template; move the
call so it runs once per tool by relocating the await
removeManagedLegacyCodexSkills(projectPath) out of the for (const { template,
dirName } of skillTemplates) loop and into the surrounding scope that iterates
tools where you check if (tool.value === 'codex'), updating
migratedLegacyCodexSkillCount accordingly; ensure the reference to
migratedLegacyCodexSkillCount and projectPath remain correct and that the call
executes exactly once when tool.value === 'codex'.

In `@src/core/update.ts`:
- Around line 708-710: The call to removeManagedLegacyCodexSkills inside
upgradeLegacyTools discards its returned migration count, so update
upgradeLegacyTools to return that count (or include it on the existing
newlyConfigured return object) and then update the caller to destructure the
returned value and add it into migratedLegacyCodexSkillCount; specifically,
change upgradeLegacyTools to accumulate the result of
removeManagedLegacyCodexSkills into a local migrated count and return it, and at
the call site add that returned count to the existing
migratedLegacyCodexSkillCount accumulator.

---

Nitpick comments:
In `@src/core/profile-sync-drift.ts`:
- Around line 206-222: Replace the manual construction of skillDirectories
(which uses getToolCurrentSkillDirectory and getToolLegacySkillDirectories) with
the shared helper getToolSkillDirectories; import getToolSkillDirectories from
./shared/index.js and set skillDirectories =
getToolSkillDirectories(projectPath, tool, options.includeSkills) (or call the
helper in the same way other code does), then keep the existing loop over
skillDirectories/ALL_WORKFLOWS/WORKFLOW_TO_SKILL_DIR to populate installed —
remove the duplicated legacy/current logic and its imports.

In `@test/core/migration.test.ts`:
- Around line 90-101: The test unnecessarily sets process.env.CODEX_HOME; remove
the assignment (process.env.CODEX_HOME = ...) on the test that calls
migrateIfNeeded so the test only relies on projectDir-based legacy detection;
locate the lines around migrateIfNeeded(projectDir, [ensureCodexTool()]),
writeLegacyCodexSkill(...) and readRawConfig() in test/core/migration.test.ts
and delete the CODEX_HOME assignment (or clear it with delete
process.env.CODEX_HOME) to avoid misleading readers or environment side-effects.
🪄 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: CHILL

Plan: Pro

Run ID: 5edf0d17-cf24-4d64-927d-e9ded3b6ac71

📥 Commits

Reviewing files that changed from the base of the PR and between bc7ab26 and 7afe47b.

📒 Files selected for processing (28)
  • docs/cli.md
  • docs/commands.md
  • docs/supported-tools.md
  • openspec/changes/migrate-codex-skills-to-agents/.openspec.yaml
  • openspec/changes/migrate-codex-skills-to-agents/design.md
  • openspec/changes/migrate-codex-skills-to-agents/proposal.md
  • openspec/changes/migrate-codex-skills-to-agents/specs/ai-tool-paths/spec.md
  • openspec/changes/migrate-codex-skills-to-agents/specs/cli-init/spec.md
  • openspec/changes/migrate-codex-skills-to-agents/specs/cli-update/spec.md
  • openspec/changes/migrate-codex-skills-to-agents/specs/workspace-links/spec.md
  • openspec/changes/migrate-codex-skills-to-agents/tasks.md
  • src/commands/workspace.ts
  • src/core/codex-skill-migration.ts
  • src/core/config.ts
  • src/core/init.ts
  • src/core/migration.ts
  • src/core/profile-sync-drift.ts
  • src/core/shared/index.ts
  • src/core/shared/tool-detection.ts
  • src/core/update.ts
  • src/core/workspace/skills.ts
  • test/commands/workspace.test.ts
  • test/core/available-tools.test.ts
  • test/core/init.test.ts
  • test/core/migration.test.ts
  • test/core/shared/tool-detection.test.ts
  • test/core/update.test.ts
  • test/core/workspace/skills.test.ts

Comment on lines +33 to +40
export async function removeManagedLegacyCodexSkills(projectPath: string): Promise<number> {
let removed = 0;

for (const skillDir of getManagedLegacyCodexSkillDirectories(projectPath)) {
if (!fs.existsSync(skillDir)) continue;
await fs.promises.rm(skillDir, { recursive: true, force: true });
removed++;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don’t fail full setup on legacy cleanup errors.

A single rm failure here bubbles up and marks Codex setup as failed in init/update/workspace flows, even when new .agents/skills were already generated. Make cleanup best-effort per directory (warn and continue), while still returning accurate removal count.

Suggested patch
 export async function removeManagedLegacyCodexSkills(projectPath: string): Promise<number> {
   let removed = 0;

   for (const skillDir of getManagedLegacyCodexSkillDirectories(projectPath)) {
     if (!fs.existsSync(skillDir)) continue;
-    await fs.promises.rm(skillDir, { recursive: true, force: true });
-    removed++;
+    try {
+      await fs.promises.rm(skillDir, { recursive: true, force: true });
+      removed++;
+    } catch {
+      // Best-effort cleanup: don't fail setup if legacy deletion is blocked.
+      continue;
+    }
   }

   return removed;
 }
🤖 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 `@src/core/codex-skill-migration.ts` around lines 33 - 40, The
removeManagedLegacyCodexSkills function currently propagates any rm error and
fails the whole setup; change it to perform best-effort per-directory removal by
wrapping the await fs.promises.rm(skillDir, ...) call in a try/catch inside the
for loop, log or warn on error (e.g., using processLogger.warn or console.warn)
including the skillDir and error, do not rethrow so the loop continues, and only
increment removed when rm succeeds; keep using
getManagedLegacyCodexSkillDirectories to locate dirs and return the accurate
removed count.

Comment thread src/core/init.ts
Comment on lines +551 to +553
if (tool.value === 'codex') {
migratedLegacyCodexSkillCount += await removeManagedLegacyCodexSkills(projectPath);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Move legacy migration outside the skill template loop.

The removeManagedLegacyCodexSkills call is currently inside the for (const { template, dirName } of skillTemplates) loop (lines 538-549), so it will execute once per skill template rather than once per tool. For core profile with 5 skills, this means the migration runs 5 times, which is inefficient and may produce incorrect counts if the function is not idempotent.

🐛 Proposed fix: move migration after the skill loop
           await FileSystemUtils.writeFile(skillFile, skillContent);
         }
-
-        if (tool.value === 'codex') {
-          migratedLegacyCodexSkillCount += await removeManagedLegacyCodexSkills(projectPath);
-        }
       }
+
+      if (shouldGenerateSkills && tool.value === 'codex') {
+        migratedLegacyCodexSkillCount += await removeManagedLegacyCodexSkills(projectPath);
+      }
+
       if (!shouldGenerateSkills) {
🤖 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 `@src/core/init.ts` around lines 551 - 553, The migration call
removeManagedLegacyCodexSkills is currently inside the skillTemplates loop and
runs once per template; move the call so it runs once per tool by relocating the
await removeManagedLegacyCodexSkills(projectPath) out of the for (const {
template, dirName } of skillTemplates) loop and into the surrounding scope that
iterates tools where you check if (tool.value === 'codex'), updating
migratedLegacyCodexSkillCount accordingly; ensure the reference to
migratedLegacyCodexSkillCount and projectPath remain correct and that the call
executes exactly once when tool.value === 'codex'.

Comment thread src/core/update.ts
Copy link
Copy Markdown
Collaborator

@alfred-openspec alfred-openspec left a comment

Choose a reason for hiding this comment

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

Thanks for pushing this migration. I found one cleanup gap: commands-only Codex updates can leave managed legacy .codex/skills directories in place even after --force, and this still needs a rebase or stack on #1143 so it keeps the Codex skills-first behavior instead of reintroducing prompt generation.

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.

2 participants