docs: use node installer flow for Codex/OpenCode#410
docs: use node installer flow for Codex/OpenCode#410
Conversation
📝 WalkthroughWalkthroughThe pull request introduces a centralized installer framework replacing direct git clone operations. A new Changes
Sequence DiagramsequenceDiagram
actor User
participant CLI as CLI Entry Point
participant Installer as installer-core
participant Git as Git Operations
participant FS as File System
participant Symlink as Symlink Manager
User->>CLI: run install command
CLI->>Installer: parseArgs(argv)
Installer-->>CLI: parsed config
CLI->>Installer: runInstall(config)
Installer->>Git: gitEnsureCloned(repo, path)
Git->>FS: clone repo to /tmp
Git-->>Installer: repo cloned
Installer->>Git: gitCheckoutRef(ref)
Git-->>Installer: ref checked out
Installer->>FS: resolveInstallConfig(paths)
FS-->>Installer: config resolved
Installer->>Symlink: ensureSymlink(source, target)
Symlink->>FS: create symlink ~/.codex/superpowers
Symlink-->>Installer: symlink created
Installer-->>CLI: installation complete
CLI-->>User: success message
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.opencode/INSTALL.md (1)
3-7:⚠️ Potential issue | 🟠 MajorAdd Node.js to prerequisites.
The installer now relies on
nodecommands, but the prerequisites omit Node.js.📌 Suggested doc update
## Prerequisites - [OpenCode.ai](https://opencode.ai) installed - Git installed +- Node.js installed (for `node ... install/upgrade`)
🤖 Fix all issues with AI agents
In @.codex/INSTALL.md:
- Around line 5-20: Add a "Prerequisites" section at the top of
.codex/INSTALL.md that explicitly lists Node.js (specify a minimum version,
e.g., Node 14+ or current LTS) and Git as required before running the installer,
and include brief instructions for verifying them (e.g., run node --version and
git --version) plus links or pointers for installing them; update the
Installation text in the existing "Run installer" section to reference this new
Prerequisites section so first-time installs won't fail due to missing node/git.
In `@lib/installer-core.js`:
- Around line 196-200: The update flag is incorrectly computed because
rawArgs['no-update'] can be the string "false" (truthy); modify the logic that
sets update (where rawArgs and defaults are assembled, referencing rawArgs and
the update property) to normalize string booleans first (e.g., coerce
"true"/"false" to actual booleans or add a small helper like parseBoolean to
handle strings, undefined, and actual booleans) and then compute update as
!(normalizedNoUpdate || false) so that --no-update=false yields update=true.
- Around line 323-325: The code calls gitFastForwardUpdate unconditionally after
gitCheckoutRef which can fail if the checkout left a detached HEAD; modify the
flow in installer-core.js to detect whether the repo is on a detached HEAD
before attempting to fast-forward: after gitCheckoutRef({ repoDir: config.dir,
ref: config.ref }) and only if config.update is true, run a check (e.g., run git
rev-parse --abbrev-ref HEAD or use a new helper isDetachedHead(repoDir)) and
skip calling gitFastForwardUpdate({ repoDir: config.dir }) when detached;
alternatively, add an isDetached flag return from gitCheckoutRef and use it to
conditionally skip gitFastForwardUpdate.
| ## Installation | ||
|
|
||
| 1. **Clone superpowers repository**: | ||
| 1. **Run installer** | ||
| This will clone/update the central repo at `~/.superpowers` and link it into Codex at `~/.codex/superpowers`. | ||
|
|
||
| If this is your first install (and `~/.superpowers` does not exist yet), run from a temporary clone: | ||
| ```bash | ||
| mkdir -p ~/.codex/superpowers | ||
| cd ~/.codex/superpowers | ||
| git clone https://github.com/obra/superpowers.git . | ||
| git clone https://github.com/obra/superpowers.git /tmp/superpowers | ||
| node /tmp/superpowers/.codex/superpowers-codex install | ||
| ``` | ||
|
|
||
| 2. **Create personal skills directory**: | ||
| If you already have `~/.superpowers` installed, you can run: | ||
| ```bash | ||
| mkdir -p ~/.codex/skills | ||
| node ~/.superpowers/.codex/superpowers-codex upgrade | ||
| ``` | ||
|
|
There was a problem hiding this comment.
Add Node.js + Git prerequisites to avoid failed installs.
The new flow requires node and git, but the doc doesn’t state that. Without this, a first-time setup will fail.
📌 Suggested doc update
## Installation
+
+## Prerequisites
+
+- Node.js installed (for `node ... install/upgrade`)
+- Git installed
+
1. **Run installer**
This will clone/update the central repo at `~/.superpowers` and link it into Codex at `~/.codex/superpowers`.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ## Installation | |
| 1. **Clone superpowers repository**: | |
| 1. **Run installer** | |
| This will clone/update the central repo at `~/.superpowers` and link it into Codex at `~/.codex/superpowers`. | |
| If this is your first install (and `~/.superpowers` does not exist yet), run from a temporary clone: | |
| ```bash | |
| mkdir -p ~/.codex/superpowers | |
| cd ~/.codex/superpowers | |
| git clone https://github.com/obra/superpowers.git . | |
| git clone https://github.com/obra/superpowers.git /tmp/superpowers | |
| node /tmp/superpowers/.codex/superpowers-codex install | |
| ``` | |
| 2. **Create personal skills directory**: | |
| If you already have `~/.superpowers` installed, you can run: | |
| ```bash | |
| mkdir -p ~/.codex/skills | |
| node ~/.superpowers/.codex/superpowers-codex upgrade | |
| ``` | |
| ## Installation | |
| ## Prerequisites | |
| - Node.js installed (for `node ... install/upgrade`) | |
| - Git installed | |
| 1. **Run installer** | |
| This will clone/update the central repo at `~/.superpowers` and link it into Codex at `~/.codex/superpowers`. | |
| If this is your first install (and `~/.superpowers` does not exist yet), run from a temporary clone: |
🤖 Prompt for AI Agents
In @.codex/INSTALL.md around lines 5 - 20, Add a "Prerequisites" section at the
top of .codex/INSTALL.md that explicitly lists Node.js (specify a minimum
version, e.g., Node 14+ or current LTS) and Git as required before running the
installer, and include brief instructions for verifying them (e.g., run node
--version and git --version) plus links or pointers for installing them; update
the Installation text in the existing "Run installer" section to reference this
new Prerequisites section so first-time installs won't fail due to missing
node/git.
| ref: rawArgs.ref || defaults.ref, | ||
| dir: path.resolve(expandHome(rawArgs.dir || defaults.dir)), | ||
| force: Boolean(rawArgs.force || false), | ||
| update: !(rawArgs['no-update'] || false), | ||
| }; |
There was a problem hiding this comment.
Normalize --no-update string values.
--no-update=false currently disables updates because the string "false" is truthy. Consider normalizing string booleans.
🧹 Suggested fix: normalize boolean args
function resolveInstallConfig(rawArgs) {
const defaults = getDefaults();
@@
return {
@@
- update: !(rawArgs['no-update'] || false),
+ update: !(
+ rawArgs['no-update'] === true ||
+ rawArgs['no-update'] === 'true'
+ ),
};
}🤖 Prompt for AI Agents
In `@lib/installer-core.js` around lines 196 - 200, The update flag is incorrectly
computed because rawArgs['no-update'] can be the string "false" (truthy); modify
the logic that sets update (where rawArgs and defaults are assembled,
referencing rawArgs and the update property) to normalize string booleans first
(e.g., coerce "true"/"false" to actual booleans or add a small helper like
parseBoolean to handle strings, undefined, and actual booleans) and then compute
update as !(normalizedNoUpdate || false) so that --no-update=false yields
update=true.
| gitCheckoutRef({ repoDir: config.dir, ref: config.ref }); | ||
| if (config.update) gitFastForwardUpdate({ repoDir: config.dir }); | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cd "$REPO_ROOT" 2>/dev/null || true
# Check file exists and get basic info
if [ -f "lib/installer-core.js" ]; then
wc -l lib/installer-core.js
echo "---"
# Read lines around 323-325
sed -n '310,340p' lib/installer-core.js | cat -n
fiRepository: obra/superpowers
Length of output: 1297
🏁 Script executed:
cd "$REPO_ROOT" 2>/dev/null || true
# Search for gitFastForwardUpdate function
rg -n "function gitFastForwardUpdate|const gitFastForwardUpdate" lib/installer-core.js
echo "---"
# Search for resolveInstallConfig function
rg -n "function resolveInstallConfig|const resolveInstallConfig" lib/installer-core.js
echo "---"
# Search for gitCheckoutRef function
rg -n "function gitCheckoutRef|const gitCheckoutRef" lib/installer-core.jsRepository: obra/superpowers
Length of output: 209
🏁 Script executed:
cd "$REPO_ROOT" 2>/dev/null || true
# Read gitCheckoutRef and gitFastForwardUpdate implementations
sed -n '128,145p' lib/installer-core.js | cat -n
echo "---"
# Read resolveInstallConfig implementation
sed -n '187,210p' lib/installer-core.js | cat -nRepository: obra/superpowers
Length of output: 1563
🏁 Script executed:
cd "$REPO_ROOT" 2>/dev/null || true
# Look for the run() function and how it works
rg -n "function run\(" lib/installer-core.js -A 10 | head -30
echo "---"
# Check how args are parsed/normalized
rg -n "process.argv\|parseArg\|rawArgs" lib/installer-core.js | head -20Repository: obra/superpowers
Length of output: 304
🌐 Web query:
git pull --ff-only detached HEAD error
💡 Result:
When you’re in a detached HEAD state (git status shows HEAD detached at ...), git pull often fails because there’s no current branch for Git to update/merge into, so it may say:
- “You are not currently on a branch. Please specify which branch you want to merge with …
git pull <remote> <branch>” [2][3]
And if you (or your config) use --ff-only, it can also fail with:
- “fatal: Not possible to fast-forward, aborting” when your local history can’t be fast-forwarded to the remote (diverged / would require a merge commit) [1][4].
Fix (most common)
- Switch back to the branch you meant to update, then pull:
git switch main # or your branch name
git pull --ff-onlyDetached-HEAD pull failures are resolved by re-attaching HEAD to a branch first. [2][3]
If you need to keep work you did while detached
Create a branch at the detached commit, then proceed normally:
git switch -c my-work
git pull --ff-only(or later merge/cherry-pick into your target branch). [2]
If --ff-only is what’s blocking you
Your branches have diverged; choose one:
- Rebase your local commits onto the pulled changes:
[5]
git pull --rebase
- Or allow a merge (not fast-forward-only):
[4]
git pull --no-ff
If you paste the exact error text and git status output, I can tell you which of the above applies.
Sources: [1] [2] [3] [4] [5]
🏁 Script executed:
cd "$REPO_ROOT" 2>/dev/null || true
# Find where rawArgs comes from and how it's constructed
rg -n "runInstallOrUpgrade\(" lib/installer-core.js -B 5 -A 2 | head -40
echo "---"
# Search for main function or entry point
rg -n "function main\|process\.argv" lib/installer-core.js | head -20Repository: obra/superpowers
Length of output: 505
🏁 Script executed:
cd "$REPO_ROOT" 2>/dev/null || true
# Look at the full file to understand arg flow
head -50 lib/installer-core.js | cat -n
echo "---"
# Search for where runInstall/runUpgrade is called
rg -n "runInstall\(|runUpgrade\(" lib/installer-core.jsRepository: obra/superpowers
Length of output: 1549
🏁 Script executed:
cd "$REPO_ROOT" 2>/dev/null || true
# Search for exports and main entry point
tail -30 lib/installer-core.js | cat -nRepository: obra/superpowers
Length of output: 939
🏁 Script executed:
cd "$REPO_ROOT" 2>/dev/null || true
# Find parseArgs function
rg -n "function parseArgs" lib/installer-core.js -A 20Repository: obra/superpowers
Length of output: 619
🏁 Script executed:
cd "$REPO_ROOT" 2>/dev/null || true
# Get the rest of parseArgs function
rg -n "function parseArgs" lib/installer-core.js -A 35Repository: obra/superpowers
Length of output: 934
🏁 Script executed:
cd "$REPO_ROOT" 2>/dev/null || true
# Look for any tests related to install/upgrade
find . -name "*.test.js" -o -name "*.spec.js" -o -name "*test*" -type f 2>/dev/null | grep -E "(test|spec)" | head -10
echo "---"
# Check if there's documentation
find . -name "README*" -o -name "*.md" -type f 2>/dev/null | head -10Repository: obra/superpowers
Length of output: 767
🏁 Script executed:
cd "$REPO_ROOT" 2>/dev/null || true
# Verify the git command syntax in the suggestion
# Check what git rev-parse --abbrev-ref HEAD returns in different states
echo "Testing git rev-parse behavior documentation..."
# Just confirm the file structure looks good for the fix
sed -n '128,145p' lib/installer-core.jsRepository: obra/superpowers
Length of output: 705
Skip git pull when checkout is detached.
If --ref is a tag, commit hash, or remote branch (e.g., origin/main), git checkout leaves you on a detached HEAD, and git pull --ff-only fails with "You are not currently on a branch". This causes install/upgrade to error even though the repo is clean.
🔧 Suggested fix: detect detached HEAD before pulling
+function gitCurrentBranch(repoDir) {
+ const r = run('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd: repoDir });
+ if (r.status !== 0) {
+ throw new Error(r.stderr.trim() || 'git rev-parse failed');
+ }
+ return r.stdout.trim();
+}
+
function runInstallOrUpgrade(ide, args, mode) {
const config = resolveInstallConfig(args);
@@
gitCheckoutRef({ repoDir: config.dir, ref: config.ref });
- if (config.update) gitFastForwardUpdate({ repoDir: config.dir });
+ if (config.update) {
+ const branch = gitCurrentBranch(config.dir);
+ if (branch !== 'HEAD') {
+ gitFastForwardUpdate({ repoDir: config.dir });
+ } else {
+ run('git', ['fetch', '--all', '--prune'], { cwd: config.dir });
+ }
+ }🤖 Prompt for AI Agents
In `@lib/installer-core.js` around lines 323 - 325, The code calls
gitFastForwardUpdate unconditionally after gitCheckoutRef which can fail if the
checkout left a detached HEAD; modify the flow in installer-core.js to detect
whether the repo is on a detached HEAD before attempting to fast-forward: after
gitCheckoutRef({ repoDir: config.dir, ref: config.ref }) and only if
config.update is true, run a check (e.g., run git rev-parse --abbrev-ref HEAD or
use a new helper isDetachedHead(repoDir)) and skip calling
gitFastForwardUpdate({ repoDir: config.dir }) when detached; alternatively, add
an isDetached flag return from gitCheckoutRef and use it to conditionally skip
gitFastForwardUpdate.
Updates the Codex/OpenCode INSTALL.md docs to use the new node-based installer commands introduced in #409.
node ... install, updates vianode ... upgradeThis keeps defaults upstream-friendly (obra/superpowers@main) and avoids fork-specific flags.