Skip to content

feat: unified npx installer across all hivemind assistants#75

Open
kaghni wants to merge 1 commit intomainfrom
feat/unified-npx-installer
Open

feat: unified npx installer across all hivemind assistants#75
kaghni wants to merge 1 commit intomainfrom
feat/unified-npx-installer

Conversation

@kaghni
Copy link
Copy Markdown
Collaborator

@kaghni kaghni commented Apr 23, 2026

Summary

One command installs hivemind into every supported assistant — Claude Code, Codex, OpenClaw — with a single browser-based login shared across all three:

npx @activeloop/hivemind@latest install

The CLI auto-detects ~/.claude, ~/.codex, ~/.openclaw and wires each with the existing platform bundle. Marketplace (Claude Code) and ClawHub (OpenClaw) install paths keep working as discovery surfaces; all three paths converge on the same filesystem layout and the same ~/.deeplake/credentials.json, so users land in the same state regardless of entry point.

What changed

  • New src/cli/ — platform detection, per-assistant installers, thin wrapper around the existing device-flow login
  • New esbuild target produces bundle/cli.js; hivemind bin added to package.json
  • Package renamed from hivemind to @activeloop/hivemind — the unscoped name on npm has been squatted by an unrelated 2014 package ("Awesome web platform for distributed projects" by amark); our OpenClaw manifest npmSpec was pointing at that squatted name and is now corrected
  • codex/install.sh reduced to a one-line shim that execs the unified CLI
  • README.md and codex/INSTALL.md lead with the single npx line; per-assistant marketplace instructions moved into collapsible "Alternative install paths"

Subcommands

npx @activeloop/hivemind@latest install              # auto-detect, install to all
npx @activeloop/hivemind@latest install --only claude,codex
npx @activeloop/hivemind@latest claude install       # explicit per-assistant
npx @activeloop/hivemind@latest codex install
npx @activeloop/hivemind@latest claw install
npx @activeloop/hivemind@latest status               # what's wired up
npx @activeloop/hivemind@latest uninstall            # remove from all detected
npx @activeloop/hivemind@latest login                # explicit re-auth

Test plan

  • npm run typecheck clean
  • npm run build clean (9 CC + 8 Codex + 1 OpenClaw + 1 CLI bundle)
  • npm pack produces 34-file tarball; all expected artifacts present
  • npm install -g ./tarball.tgz into a throwaway prefix works; hivemind bin exposed
  • Sandbox HOME install: all 3 assistants wired, hook commands have correct absolute paths
  • Shared auth: existing ~/.deeplake/credentials.json reused across all installs, no repeated browser popup
  • Idempotency: re-running install yields 8 Claude hooks + 5 Codex hooks (no duplicates)
  • uninstall: settings.json cleared, ~/.codex/hooks.json removed, skill symlink removed, OpenClaw plugin dir removed
  • Capture hook bundle smoke test (loads and exits cleanly with synthetic hook input)
  • --only <platforms> filter works
  • Auto-detect edge case: missing ~/.codex → only claude,claw targeted
  • Real install on a live machine + in-assistant capture verification for each of Claude/Codex/OpenClaw (follow-up after merging; test machines typically have an existing marketplace/ClawHub install that would double-capture until uninstalled)

Follow-ups (not in this PR)

  • Publish @activeloop/hivemind to npm (first publish creates the @activeloop scope)
  • Republish OpenClaw plugin to ClawHub so the updated npmSpec: @activeloop/hivemind is live in the registry
  • Optional: loosen engines.node from >=22 to >=20 so Node 20 users don't see an EBADENGINE warning on npx

One command installs hivemind into every supported assistant (Claude Code,
Codex, OpenClaw) with a single browser-based login shared across all three:

    npx @activeloop/hivemind@latest install

The CLI auto-detects ~/.claude, ~/.codex, ~/.openclaw and wires each with
its existing bundle. Marketplace and ClawHub install paths keep working;
all three paths converge on the same filesystem layout and credentials.

- New src/cli/ and bundle/cli.js esbuild target
- Package renamed to @activeloop/hivemind (previous 'hivemind' on npm is
  squatted since 2014); openclaw plugin npmSpec updated to match
- codex/install.sh reduced to a shim that execs the unified CLI;
  README and codex/INSTALL.md lead with the single npx line
@claude
Copy link
Copy Markdown

claude Bot commented Apr 23, 2026

Claude finished @kaghni's task in 2m 12s —— View job


  • Read changed files
  • Identify bugs, security issues, correctness problems
  • Post inline comments

Found 2 issues (inline comments posted):

  • src/cli/install-codex.ts:58writeJson(HOOKS_PATH, buildHooksJson()) silently overwrites the user's entire hooks.json, destroying any existing Codex hooks. The Claude installer correctly merges hooks; Codex needs the same treatment.
  • src/cli/install-openclaw.ts:20-22copyDir (designed for directories, uses cpSync recursive:true) is called on individual files; should use copyFileSync to avoid the file landing inside an existing same-named directory.

Comment thread src/cli/install-codex.ts
if (existsSync(srcSkills)) copyDir(srcSkills, join(PLUGIN_DIR, "skills"));

tryEnableCodexHooks();
writeJson(HOOKS_PATH, buildHooksJson());
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Data loss: existing Codex hooks are silently overwritten. Unlike the Claude installer which has mergeHooks that strips only hivemind entries and preserves everything else, this call replaces the entire hooks.json on every install — any hooks the user added (e.g., a custom PostToolUse hook) are deleted without warning.

Read the existing file first, strip old hivemind entries by path, then merge buildHooksJson() into the result:

Suggested change
writeJson(HOOKS_PATH, buildHooksJson());
const existing = readJson<{ hooks?: Record<string, unknown[]> }>(HOOKS_PATH) ?? {};
const ours = buildHooksJson();
const merged: Record<string, unknown[]> = { ...(existing.hooks ?? {}) };
for (const [event, entries] of Object.entries(ours.hooks)) {
const stripped = (merged[event] ?? []).filter(
(e: unknown) => !((e as { hooks?: { command?: string }[] })?.hooks?.[0]?.command?.includes(`${PLUGIN_DIR}/bundle/`))
);
merged[event] = [...stripped, ...entries];
}
writeJson(HOOKS_PATH, { ...existing, hooks: merged });


ensureDir(PLUGIN_DIR);
copyDir(srcDist, join(PLUGIN_DIR, "dist"));
if (existsSync(srcManifest)) copyDir(srcManifest, join(PLUGIN_DIR, "openclaw.plugin.json"));
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Wrong function for single-file copy. copyDir calls cpSync with { recursive: true } and is designed for directories; calling it on a file path works today but is semantically wrong — if PLUGIN_DIR ever contains an existing directory named openclaw.plugin.json, the file lands inside it instead of replacing it. Use copyFileSync for individual files.

Suggested change
if (existsSync(srcManifest)) copyDir(srcManifest, join(PLUGIN_DIR, "openclaw.plugin.json"));
if (existsSync(srcManifest)) copyFileSync(srcManifest, join(PLUGIN_DIR, "openclaw.plugin.json"));
if (existsSync(srcPkg)) copyFileSync(srcPkg, join(PLUGIN_DIR, "package.json"));

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.

1 participant