Skip to content

Extension scripts need an absolute project root for reliable path resolution #3308

Description

@rhuss

Problem

Extension commands that invoke helper scripts need a reliable absolute path to those scripts at runtime. The project-relative paths that rewrite_project_relative_paths() produces (e.g., .specify/extensions/spex/scripts/foo.sh) only work when the agent's working directory is the project root. In practice, CWD shifts during a session and the relative paths break.

The most common scenario where this happens is git worktrees. When an agent creates a worktree for isolated feature development, the working directory changes to the worktree root, which is a different filesystem location from the main repo. Subagent spawning can also reset CWD, as can explicit cd commands in multi-step workflows. Any of these cause a relative script path to resolve against the wrong directory.

What we're doing today

In our extension (cc-spex), we currently work around this by using the Claude Code plugin system. Our scripts live in the plugin cache directory (~/.claude/plugins/cache/spex-plugin/spex/5.8.0/scripts/), and a context hook injects the absolute plugin root path into every prompt. Commands reference scripts as <PLUGIN_ROOT>/scripts/foo.sh, which always resolves correctly regardless of CWD.

This works, but it's entirely Claude Code-specific. The plugin cache, the context hook, the <plugin-root> injection are all Claude Code infrastructure. None of it exists on other agents, so the approach doesn't generalize.

The git rev-parse workaround

The agent-agnostic fallback is:

REPO_ROOT=$(git rev-parse --show-toplevel)
"$REPO_ROOT/.specify/extensions/spex/scripts/foo.sh"

This works from any CWD within the repo, including worktrees (--show-toplevel returns the worktree root, not the main repo root). But it requires a git invocation every time a command references a script, and it assumes the project is a git repository.

Proposal

A __PROJECT_ROOT__ template variable, resolved at install time to the absolute project path, would solve this cleanly. It fits the existing pattern of __AGENT__ and __SPECKIT_COMMAND_*__ substitutions in the template pipeline, and it would give extension authors a reliable way to reference their own scripts from any working directory.

Commands would reference scripts as:

__PROJECT_ROOT__/.specify/extensions/spex/scripts/foo.sh

This stays materialized (the absolute path is baked into the installed command file), project-local, and agent-agnostic.

One tradeoff: the resolved path becomes stale if the project directory moves. But that's the same tradeoff __AGENT__ already makes (agent name is baked in at install time), and specify init --refresh would re-resolve it.

Related: #3303 (extension portability discussion, where the maintainer suggested using extension-local paths for script discovery).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions