Skip to content

Installer hardcodes upstream danielmiessler/PAI clone URL — fork edits never reach runtime via reinstall #115

@virtualian

Description

@virtualian

Summary

The installer's repository step in engine/actions.ts hardcodes https://github.com/danielmiessler/PAI.git as the clone source. Users running on a fork (e.g. virtualian/pai) cannot use the installer to populate or update their installation from their own fork — every install/reinstall pulls upstream regardless. This decouples the user's fork-side work from anything reachable via the installer, and silently regresses fork content if reinstall is ever used.

Discovered while investigating regression risk for the cleanup work in #108#110.

Evidence

Releases/v4.0.3+/.claude/PAI-Install/engine/actions.ts:

498:export async function runRepository(...) {
503:  const paiDir = state.detection?.paiDir || join(homedir(), \".claude\");
...
509:    const isGitRepo = existsSync(join(paiDir, \".git\"));
511:      const pullResult = tryExec(`cd \"${paiDir}\" && git pull origin main 2>&1`, 60000);
...
524:    if (!existsSync(paiDir)) {
525:      mkdirSync(paiDir, { recursive: true });
527:    }
528:    const cloneResult = tryExec(
529:      `git clone https://github.com/danielmiessler/PAI.git \"${paiDir}\" 2>&1`,
...
539:        const initResult = tryExec(`cd \"${paiDir}\" && git init && git remote add origin https://github.com/danielmiessler/PAI.git && git fetch origin && git checkout -b main origin/main 2>&1`, 120000);

Three independent code paths, all hardcoding the same upstream URL:

  1. Fresh clone (line 529): git clone danielmiessler/PAI
  2. Fallback init+fetch (line 539): git remote add origin danielmiessler/PAI
  3. Existing-install update (line 511): git pull origin main — runs against whatever remote is already configured, so this path would respect a fork remote if ~/.claude/.git/ already exists with a fork remote configured. But neither of the creation paths sets up a fork remote.

There is no env var, settings.json field, or CLI flag that overrides the URL. config-gen.ts has a pai.repoUrl field but it's only emitted into the generated settings.json — it is not consumed by engine/actions.ts:runRepository.

Impact

  1. Forks cannot bootstrap from the installer. Running install.sh on a fresh machine from a fork checkout still clones danielmiessler/PAI, not the fork. All fork-specific work (e.g. the two-root separation in Separate PAI installation from CC config: CLAUDE_CONFIG_DIR + PAI_DIR=~/.pai #98/Implement CLAUDE_CONFIG_DIR + PAI_DIR two-root separation #101/Restore CorrectionMode fast-path and loadLatestSynthesis (hotfix) #104/Add CI symbol guard for critical hook files #105/Complete two-root path separation (supersedes #103) #106 in virtualian/pai) is invisible to a fresh install.
  2. Reinstall on a fork silently replaces fork content with upstream. If the user ever reinstalls into a populated ~/.claude/, the fallback init+fetch+checkout path at line 539 will overlay upstream files on top of fork-specific files in ~/.claude/. Files only present in the fork would remain (init doesn't delete), but any file that exists in both (e.g. CLAUDE.md, PAI/Algorithm/v3.7.0.md) would be overwritten with the upstream version.
  3. Combined with Skills separation is documentation-only: Claude Code harness reads ~/.claude/skills/, not ~/.pai/skills/ #110, this means a fresh install populates the harness-read path with UPSTREAM skill content, not fork content. Since Claude Code reads skills from ~/.claude/skills/ (not ~/.pai/skills/), and the installer fills ~/.claude/skills/ from upstream, fork-side skill modifications are doubly invisible.
  4. Combined with Two-root separation incomplete: stale ~/.claude/PAI/ tree and residual ~/.claude/PAI/ references in canonical ~/.pai/PAI/ #108, this means the installer-mirror-recreation problem can't be solved by patching the fork's installer alone — the patched installer would never reach a user's machine through the upstream-cloning code path.

Reproduction

grep -n 'danielmiessler/PAI' Releases/v4.0.3+/.claude/PAI-Install/engine/actions.ts
# Lines 529 and 539

Suggested fixes (not prescriptive)

  1. Make the clone URL configurable. Options, in increasing complexity:
    • Env var (PAI_REPO_URL) read at install time
    • Settings.json field (pai.repoUrl) — already exists in config-gen.ts! Just plumb it through to runRepository
    • CLI flag (install.sh --repo-url=...)
  2. Detect existing fork remote. When ~/.claude/.git/ already exists, read git remote get-url origin and preserve it across update operations — don't overwrite with hardcoded upstream URL.
  3. Document fork-mode install. Even with a fix, fork users need to know how to invoke the installer with their fork URL.

Why this is a peer to #108-#110

#108-#110 all share the pattern: the two-root separation declared a clean architecture but the runtime/installer didn't follow through. This issue is the same shape applied to the install source itself. Fork users live in a half-state where the architecture is documented in their fork but the installer cannot reach it.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions