Skip to content

--skills-only --scope project incorrectly skips Codex #6284

Open

Description

@rob7thousand

Describe the issue

databricks aitools install skips Codex when --scope project and
--skills-only are used together:

Skipped Codex CLI: does not support project-scoped skills

Codex supports repository-scoped raw skills under
<repo>/.agents/skills:

https://developers.openai.com/codex/skills#where-codex-loads-local-skills

Codex does not appear to support project-scoped plugin installation:
codex plugin add and codex plugin marketplace add expose no project
or --scope option. However, plugin scope should not prevent installation
when --skills-only explicitly requests raw skill files.

Steps to reproduce

Run from a project directory:

databricks aitools install \
  --agents claude-code,codex \
  --scope project \
  --skills-only

Expected behavior

Because --skills-only explicitly requests raw skill files, the CLI should
install the Codex skills into:

<project>/.agents/skills

Claude Code should continue using:

<project>/.claude/skills

Whether Codex plugins support project scope should not affect the
--skills-only installation path.

Actual behavior

Claude Code is processed, but Codex is skipped:

Skipped Codex CLI: does not support project-scoped skills

Possible cause

The agent model appears to use a single SupportsProjectScope capability:

https://github.com/databricks/cli/blob/main/libs/aitools/agents/agents.go

The Codex registry entry does not set SupportsProjectScope or
ProjectConfigDir, so SupportsProjectScope defaults to false.

This may correctly reflect the lack of project-scoped Codex plugin
installation, but it also blocks project-scoped raw skills when
--skills-only is used.

The capabilities may need to be distinguished by delivery method, for
example:

  • Project-scoped plugin installation
  • Project-scoped raw-skill installation

For Codex, raw-skill project scope should be enabled with a project
configuration directory of .agents, producing:

<project>/.agents/skills

Environment

Databricks CLI v1.12.1
Codex CLI 0.147.0

Related issue

Related to #6037, which proposes installing skills into the
vendor-neutral .agents/skills directory. This report specifically
concerns --skills-only being rejected because Codex plugin and
raw-skill scope capabilities appear to be treated as one capability.

Activity

  1. TangoEnSkai commented on Aug 18, 2026

    @TangoEnSkai

    I looked into this and your diagnosis is right — but the fix is wider than the Codex registry entry, so I would rather agree on the shape before sending a PR.

    Why the one-line fix does not work. Adding SupportsProjectScope: true + ProjectConfigDir: ".agents" to the Codex entry would also enable the plugin path, and codex plugin add has no project scope. The single field is read in ~12 branches across installer.go, update.go and uninstall.go (filterProjectAgents, incompatibleAgentNames, and the uninstall/update equivalents), and none of them distinguish delivery method — exactly the split you suggested.

    Proposed shape. Replace Agent.SupportsProjectScope with two capabilities:

    • SupportsProjectSkills — raw skill files can be written under the project root
    • SupportsProjectPlugin — the agent's plugin can be installed project-scoped

    Codex gets SupportsProjectSkills: true with ProjectConfigDir: ".agents", and no project plugin support. Claude Code, Pi, Gemini and Goose get both true, so their behaviour is unchanged.

    The part that needs threading. The scope gating in installer.go runs on InstallOptions, which does not carry the skills-only flag today — skillsOnly currently stops at cmd/aitools/install.go (selectAgents, buildPlan, planItemFor). So filterProjectAgents cannot tell which capability to check. It needs either a new InstallOptions field or an extra parameter. I would add the field, since update.go and uninstall.go need the same information.

    One design question I would rather you answer than guess at. <repo>/.agents/skills is not Codex-specific — this repository has its own .agents/{prompts,rules,skills} at the root, and other agents read that convention too. Installing there means the Databricks skills become visible to every agent honouring .agents, not just Codex, and aitools uninstall --scope project would remove a directory that is not exclusively ours. Is that acceptable, or would you want the project-scoped skills directory to stay namespaced per agent?

    Happy to send the PR once you confirm the split and the .agents question. If you would rather keep SupportsProjectScope and add a narrower ProjectSkillsDir-only capability, that is fine too — I just want to avoid guessing at a public capability model that spans eight agents.

  2. rob7thousand commented on Aug 19, 2026

    @rob7thousand
    Author

    I agree that the capability split is probably the right shape.

    • SupportsProjectSkills controls project-scoped skill delivery.
    • SupportsProjectPlugin controls project-scoped plugin installation.

    Codex should have SupportsProjectSkills: true, ProjectConfigDir: ".agents", and no project plugin support. Adding the delivery mode to InstallOptions gives the install path enough context to apply the correct capability, and update/uninstall can use the recorded delivery state when deciding which paths to manage.

    .agents/skills should be treated as a shared discovery path, not a Codex owned directory.

    The existing Databricks layout already provides a clean ownership boundary.

    <repo>/.databricks/aitools/skills/<skill>   # canonical Databricks files
    <repo>/.agents/skills/<skill>               # managed exposure
    <repo>/.claude/skills/<skill>               # managed exposure
    

    The entries in agent discovery directories can be per skill symlinks to the canonical Databricks files. Uninstall should only remove entries that it can verify are Databricks managed. It should never remove .agents/skills in its entirety or unrelated content.

    I checked how skills.sh handles this. It uses .agents/skills/<skill> as canonical storage. During targeted uninstall, it scans the install paths of other currently detected agents and retains the canonical skill when another path still exists. This protects active symlinks, but skills.sh does not persist which agents each skill was installed for. As a result, its decisions depend on which agents are detected at uninstall time rather than the original installation intent.

    From reading the current call order, I think hasRemainingSkillExposure is also effectively path based too. It appears to work when agents have distinct exposure paths pointing to the same canonical skill. After one exposure is removed, another path still exists. I do not think it preserves a shared exposure when multiple agents resolve to the same physical path. For example, if Codex and Cursor both use .agents/skills/databricks-core, targeted Codex uninstall appears to remove that entry first. The later check for Cursor would resolve to the same now missing path and therefore would not know that Cursor was another logical consumer.

    This is where an explicit exposure state could be beneficial, although not sure if its worth the overhead.. Databricks can extend the existing .state.json with skill exposure records that associate each physical path with the agents for which it was installed.

    For example:

    {
      "skill_exposures": {
        "databricks-core": [
          {
            "path": ".agents/skills/databricks-core",
            "agents": ["codex"]
          },
          {
            "path": ".claude/skills/databricks-core",
            "agents": ["claude-code"]
          }
        ]
      }
    }

    When multiple agents resolve to the same physical path, they share the same exposure entry.

    {
      "path": ".agents/skills/databricks-core",
      "agents": ["codex", "cursor"]
    }

    With that record, targeted Codex uninstall removes codex from the agent list but keeps the shared symlink while cursor remains. This is information that cannot be recovered reliably from the filesystem because one symlink does not reveal how many agents requested it.

    A targeted uninstall would then,

    1. Remove the requested agent from the matching exposure record.
    2. Remove the exposure when no recorded agents remain for that physical path.
    3. Remove the canonical skill when it has no remaining exposures.
    4. Update or remove the corresponding skill, file-provenance, and exposure records from .state.json.

    The state records installation intent. Before removing an exposure, the CLI should verify that a symlink points into .databricks/aitools/skills, or that a copied directory still matches the existing file provenance records.

    User modified or unrelated content should be preserved.

    If a skill is removed from the upstream manifest, the same rules apply. Update can remove its managed exposures, canonical files, and state records when everything is still unmodified. If the skill or one of its exposures has been modified, it should be retained with a warning. This is consistent with the existing skillPrunable and exposureRemovable behavior.

    There is one unavoidable case when agents share .agents/skills cannot be isolated from one another. If that exposure remains for another recorded agent, Codex can still discover it. That is inherent to the shared discovery path, but recording exposures in .state.json makes installation and cleanup behavior predictable.

    My thought is to use with the capability split, use .agents/skills as the shared project path, retain canonical files under .databricks/aitools/skills, and track skill exposures in .state.json.

  3. TangoEnSkai commented on Aug 22, 2026

    @TangoEnSkai

    Thanks — agreed on the capability split, and that answers my .agents question.

    Two corrections/observations after reading the code, so nobody rebuilds what is already there:

    The canonical + exposure layout already exists. agents.CanonicalSkillsDir is already .databricks/aitools/skills (libs/aitools/agents/skills.go:17), agent skills dirs already hold symlinks into it, and exposureRemovable / cleanup.go already refuse to delete anything that is not a symlink into the canonical dir or a byte-identical copy. So the ownership boundary you describe is the current design, not something to add.

    Your shared-path concern is real, but it is created by this change, not pre-existing. hasRemainingSkillExposure (libs/aitools/installer/uninstall.go:215) walks the registry and tests filepath.Join(agentDir, skillName) per agent. That works today only because every project-scoped agent has a distinct ProjectConfigDir (.claude, .pi, .gemini, .goose) — no two agents can collide. Pointing Codex at the shared .agents is the first time two agents could resolve to one physical path, and then removing it for one agent makes the check report "gone" for the other. Worth noting that function is itself one of the ~12 SupportsProjectScope call sites the split has to touch (line 221).

    On scoping. I would rather not put the exposure-tracking redesign in the same PR as the bug fix — AGENTS.md asks for one change per PR, and .state.json is persisted user state, so changing its shape deserves its own review. My suggestion:

    1. Capability split + Codex entry + delivery mode in InstallOptions. Fixes the reported bug.
    2. Exposure records in .state.json, if maintainers want it.

    But (1) alone makes Codex and Claude Code share .agents/skills only if a second agent is later pointed there — with just Codex on .agents, no collision exists yet, so (1) is safe to land on its own and (2) becomes required before any second agent moves to .agents.

    We are both outside contributors, so our agreeing does not settle it. Could a maintainer confirm the split and whether Codex should use the shared .agents/skills at all? I have the (1)-shaped change ready to write once someone signs off.

  4. github-actions commented on Oct 21, 2026

    @github-actions
    Contributor

    This issue has not received a response in a while. If you want to keep this issue open, please leave a comment below and auto-close will be canceled.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions