Skip to content

feat: integrate antigravity-cli support into installer scripts and fix test suite hangs#59

Open
dpolishuk wants to merge 4 commits into
mainfrom
feature/antigravity-cli-integration
Open

feat: integrate antigravity-cli support into installer scripts and fix test suite hangs#59
dpolishuk wants to merge 4 commits into
mainfrom
feature/antigravity-cli-integration

Conversation

@dpolishuk
Copy link
Copy Markdown
Owner

@dpolishuk dpolishuk commented May 24, 2026

This PR implements full first-class integration for Antigravity-CLI (agy) across both installer scripts (install.sh and install.ts).

Changes

  • install.sh: Updates install_antigravity() to import using agy plugin import, and registers antigravity to receive graphify companion configurations. Corrects missing-binary detection to exit gracefully without crashing scripts in a set -e pipeline.
  • install.ts: Adds full antigravity host metadata configuration including status checks, version pattern checks, installation, and uninstallation routines. Corrects claude-mem and graphify rules for antigravity.
  • tests/install-script.test.js: Resolves system bash incompatibility on macOS by symlinking a modern bash path for the missing-Bun test cases. Mocks npm and npx dynamically within installEnv() to prevent network-bound requests (e.g. @linear/sdk) from hanging indefinitely in sandboxed or offline testing environments.

All 68 integration tests have been run and pass successfully.

Summary by CodeRabbit

  • New Features
    • Added Antigravity CLI agent support to the installer with full lifecycle management including detection, installation, validation, status reporting, and uninstallation.
    • Antigravity CLI now integrates with third-party tools like graphify and claude-mem during installation.
    • Added command-line options to explicitly select or configure Antigravity during setup.

Review Change Stack

Copilot AI review requested due to automatic review settings May 24, 2026 19:44
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 24, 2026

Warning

Review limit reached

@dpolishuk, we couldn't start this review because you've used your available PR reviews for now.

Your plan includes 1 review of capacity. Refill in 26 minutes and 30 seconds.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more review capacity refills, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than trial, open-source, and free plans. In all cases, review capacity refills continuously over time.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bc7dc3eb-350a-46a7-af6c-02e239da4aba

📥 Commits

Reviewing files that changed from the base of the PR and between 6fbbd62 and 12c24e0.

📒 Files selected for processing (4)
  • .beads/issues.jsonl
  • scripts/install.sh
  • scripts/install.ts
  • tests/install-script.test.js
📝 Walkthrough

Walkthrough

The pull request adds Antigravity CLI (agy) as a first-class installer agent across bash and TypeScript installers. It implements agent detection via agy --version, plugin installation/management via agy plugin import/uninstall, integrates with third-party tools (graphify, claude-mem), exposes CLI flags (--antigravity and --hosts support), and updates test infrastructure to support deterministic PATH-based test execution.

Changes

Antigravity CLI Agent Integration

Layer / File(s) Summary
Issue tracking and bash agent infrastructure
.beads/issues.jsonl, scripts/install.sh
Tracks Antigravity integration work and adds detect_antigravity(), install_antigravity(), validate_antigravity(), uninstall_antigravity(), and status_antigravity() functions to establish Antigravity as a first-class agent with timeout-guarded plugin operations and version checking.
Bash installer: third-party tools and CLI integration
scripts/install.sh
Wires Antigravity into install_graphify_for_agents() and install_third_party_tools() (claude-mem), adds --antigravity CLI flag and antigravity host parsing, updates help text, and extends --status mode to report Antigravity status.
TypeScript installer: host and feature support
scripts/install.ts
Adds Antigravity as a HOSTS entry with agy --version detection and plugin import/uninstall logic, includes Antigravity in GRAPHIFY_TARGETS, and extends claude-mem feature definition to support Antigravity with matching install invocations.
Bash invocation refactoring in feature installers
scripts/install.ts
Refactors br, bv, and graphify feature installers to use bash -c instead of bash -lc in Bun.spawnSync calls.
Test infrastructure setup and case updates
tests/install-script.test.js
Hardens installEnv() and linkManifestRuntime() to ensure bash and npm/npx availability in PATH, updates test cases to resolve bash from system instead of hardcoding /bin/bash, removes bun symlink injection from Pi tests, and updates claude-mem skip assertion to reflect Antigravity CLI in host-selection messaging.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • dpolishuk/xpowers#55: This PR's Antigravity integration reuses the graphify and claude-mem third-party tool selection paths introduced in PR #55, sharing the same install orchestration logic.
  • dpolishuk/xpowers#53: Both PRs modify shared agent-selection plumbing in scripts/install.sh (AGENT_LABELS, AGENT_ORDER, --hosts parsing), so Antigravity and Pi/universal-bootstrap changes converge at the same code paths.

Poem

A fluffy agent hops into view,
Antigravity binds old friends with new—
With plugins imported and statuses shown,
This CLI finds a cozy home.
agy --version, the bashful test,
Makes graphify and claude-mem their best! 🐰✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.17% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and specifically describes the main change: integrating Antigravity-CLI support into installer scripts while addressing test suite issues.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/antigravity-cli-integration

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds first-class Antigravity CLI (agy) host support to both installer entrypoints (scripts/install.sh and scripts/install.ts) and updates the installer test suite to avoid hangs and improve macOS bash compatibility.

Changes:

  • Add antigravity host detection/install/status/validate/uninstall support (shell + Bun installers) and include it in Graphify/claude-mem host targeting.
  • Update installer execution paths and help text to recognize antigravity as a supported host.
  • Harden tests by providing a modern bash in PATH and mocking npm/npx to prevent network-bound hangs.

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/install-script.test.js Adds PATH shims (bash/npm/npx) and adjusts spawn behavior to prevent macOS bash issues and test hangs.
scripts/install.ts Adds antigravity host configuration and extends Graphify/claude-mem targeting and help text.
scripts/install.sh Adds antigravity integration and expands Graphify/claude-mem agent targeting and status/validate/uninstall flows.
bun.lock Updates Bun lockfile to reflect current dependency graph.
.beads/issues.jsonl Adds Antigravity installer integration tracking issues/spec text.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/install-script.test.js Outdated
Comment on lines +48 to +80
if (env.PATH) {
const parts = env.PATH.split(path.delimiter)
const firstPart = parts[0]
if (firstPart && fs.existsSync(firstPart)) {
const bashSymlink = path.join(firstPart, "bash")
if (!fs.existsSync(bashSymlink)) {
try {
const bashPath = findExecutable("bash") || "/bin/bash"
fs.symlinkSync(bashPath, bashSymlink)
} catch (e) {
// Ignore errors
}
}
const npmMock = path.join(firstPart, "npm")
if (!fs.existsSync(npmMock)) {
try {
fs.writeFileSync(npmMock, "#!/usr/bin/env bash\nexit 0\n", "utf8")
fs.chmodSync(npmMock, 0o755)
} catch (e) {
// Ignore errors
}
}
const npxMock = path.join(firstPart, "npx")
if (!fs.existsSync(npxMock)) {
try {
fs.writeFileSync(npxMock, "#!/usr/bin/env bash\nexit 0\n", "utf8")
fs.chmodSync(npxMock, 0o755)
} catch (e) {
// Ignore errors
}
}
}
}
Comment thread scripts/install.sh Outdated
Comment on lines +372 to +376
detect_antigravity() {
if command -v agy &>/dev/null; then
# Verify it is our Antigravity CLI (v1.0.x)
local ver
if ver=$(timeout 2s agy --version 2>/dev/null); then
Comment thread scripts/install.sh
Comment on lines +973 to +986
local agy_stderr
if [[ "$USE_SYMLINKS" == true ]]; then
# agy doesn't have a direct 'link' for local plugins that works like 'extensions link'
# but we can import it.
agy_stderr=$(timeout 10s agy plugin import "$ext_dir" 2>&1) || {
error "agy plugin import failed: ${agy_stderr}"
return 1
}
else
agy_stderr=$(timeout 10s agy plugin import "$ext_dir" 2>&1) || {
error "agy plugin import failed: ${agy_stderr}"
return 1
}
fi
Comment thread .beads/issues.jsonl Outdated
{"id":"bd-4j3","title":"Feature: Hyperpowers OpenCode bundle + beads triage","description":"## Requirements (IMMUTABLE)\n- Hyperpowers installs via git clone to `~/.config/opencode/hyperpowers` with plugin symlink, matching Superpowers pattern.\n- OpenCode auto-discovers from plural directories: `.opencode/commands`, `.opencode/agents`, `.opencode/plugins`, `.opencode/skills` (no singular dirs remaining).\n- Add `/beads-triage` command and `beads-triage` skill in OpenCode bundle.\n- `beads-triage` runs `bv --robot-triage` directly; if `bv` is missing, it installs via the official install script and retries.\n- `beads-triage` accepts optional scope args (label/track/etc.) and passes them through to `bv --robot-triage`.\n- Skill output is raw JSON only (no summary).\n- Installation docs explain bundle layout, plugin symlink, and `beads-triage` usage.\n\n## Success Criteria (MUST ALL BE TRUE)\n- [ ] OpenCode discovers commands, agents, skills, and plugin from plural `.opencode/*` directories.\n- [ ] `/beads-triage` runs `bv --robot-triage` and returns raw JSON.\n- [ ] Missing `bv` triggers automated install before running triage.\n- [ ] Optional scope args are passed to `bv --robot-triage`.\n- [ ] Installation docs updated with new bundle layout and triage instructions.\n- [ ] No singular `.opencode/command`, `.opencode/agent`, `.opencode/plugin` directories remain.\n\n## Anti-Patterns (FORBIDDEN)\n- ❌ Running bare `bv` (must always use `--robot-triage` to avoid TUI blocking).\n- ❌ Summarizing triage output (raw JSON only).\n- ❌ Prompting user to manually run `bv --robot-triage` (skill must run it directly).\n- ❌ Keeping singular `.opencode/command`, `.opencode/agent`, `.opencode/plugin` directories.\n\n## Approach\nAdopt the Superpowers-style local bundle: clone to `~/.config/opencode/hyperpowers` and symlink the plugin into `~/.config/opencode/plugins/`. Migrate `.opencode` directories to plural names so OpenCode auto-discovers commands, agents, plugins, and skills. Add a `beads-triage` skill and `/beads-triage` command that executes `bv --robot-triage` with optional args; if `bv` is missing, install using the official script before executing. Update installation docs to explain the bundle layout and triage usage.\n\n## Architecture\n- `.opencode/skills/beads-triage/SKILL.md` - New skill driving `bv --robot-triage` behavior\n- `.opencode/commands/beads-triage.md` - Command wrapper for the skill\n- `.opencode/plugins/hyperpowers-skills.ts` - Plugin for `use_skill`/`find_skills` tools and bootstrap injection\n- `.opencode/INSTALL.md` - Updated installation docs\n\n## Design Rationale\n### Problem\nHyperpowers needs a one-step OpenCode installation that bundles commands, agents, hooks, and skills, plus actionable bv graph analysis via bd integration.\n\n### Research Findings\n**Codebase:**\n- `.opencode/command`, `.opencode/agent`, `.opencode/plugin` exist today (singular) and should be migrated to plural for OpenCode auto-discovery.\n- `hyperpowers-skills.ts` already provides `use_skill`/`find_skills` tool behavior.\n\n**External:**\n- OpenCode docs confirm auto-discovery from plural `.opencode/commands`, `.opencode/agents`, `.opencode/plugins`, `.opencode/skills`.\n- `bv --robot-triage` provides JSON output and is the safe non-TUI entry point.\n- `bv` official install uses a curl-based install script.\n\n### Approaches Considered\n1. **Local bundle with plural `.opencode/*` (Chosen)**\n - Pros: Matches Superpowers pattern; zero npm packaging complexity; OpenCode auto-discovery works.\n - Cons: Requires git clone + symlink step.\n - **Chosen because:** Aligns with OpenCode discovery and existing hyperpowers skill tooling.\n\n2. **Hybrid bundle + npm plugin**\n - Pros: Smaller local footprint.\n - Cons: Two install methods; more moving parts.\n - **Rejected because:** Adds complexity with little benefit.\n\n3. **Npm-only plugin**\n - Pros: Single install command.\n - Cons: OpenCode doesn’t auto-discover skills/commands from npm packages.\n - **Rejected because:** Not supported by OpenCode.\n\n### Scope Boundaries\n**In scope:**\n- Bundle layout migration to plural `.opencode/*`\n- New `beads-triage` skill + command\n- Updated installation docs\n\n**Out of scope (deferred/never):**\n- Additional bv robot modes (insights/graph) beyond triage\n- CLI wrappers outside OpenCode\n\n### Open Questions\n- None","status":"closed","priority":2,"issue_type":"epic","owner":"dmitry.polishuk@gmail.com","created_at":"2026-01-20T14:06:03.637086+03:00","created_by":"Dmitry Polishuk","updated_at":"2026-01-20T14:42:39.95947+03:00","closed_at":"2026-01-20T14:42:39.95947+03:00","close_reason":"All tasks completed; implementation reviewed and approved."}
{"id":"bd-4lt","title":"Convert executing-plans to Flow skill","description":"Convert executing-plans skill to Kimi CLI Flow format.\n\nPlan execution workflow:\n1. Load next ready task from bd\n2. Execute task\n3. Review learnings\n4. Update/create follow-up tasks\n5. Stop for user review (or continue in Ralph mode)\n\n```mermaid\nflowchart TD\n BEGIN[Start Execution] --\u003e READY[bd ready - get next task]\n READY --\u003e HAS_TASK{Task available?}\n HAS_TASK --\u003e|No| END[All tasks complete]\n HAS_TASK --\u003e|Yes| CLAIM[Mark in_progress]\n CLAIM --\u003e EXECUTE[Execute task]\n EXECUTE --\u003e VERIFY[Verify completion]\n VERIFY --\u003e PASSED{Verification passed?}\n PASSED --\u003e|No| FIX[Fix issues]\n FIX --\u003e VERIFY\n PASSED --\u003e|Yes| LEARNINGS[Document learnings]\n LEARNINGS --\u003e UPDATE[Update bd task]\n UPDATE --\u003e CLOSE[Close task]\n CLOSE --\u003e CHECKPOINT{User checkpoint?}\n CHECKPOINT --\u003e|Yes| PAUSE[Wait for user]\n PAUSE --\u003e READY\n CHECKPOINT --\u003e|No| READY\n```\n\n## Acceptance Criteria\n- [ ] type: flow in frontmatter\n- [ ] Task execution loop as Mermaid flowchart\n- [ ] bd integration points shown\n- [ ] User checkpoint decision node","status":"closed","priority":2,"issue_type":"task","owner":"dmitry.polishuk@gmail.com","created_at":"2026-02-01T20:27:42.242455611+02:00","created_by":"Dmitry Polishuk","updated_at":"2026-02-01T20:39:52.605836715+02:00","closed_at":"2026-02-01T20:39:52.605836715+02:00","close_reason":"Converted to Flow skill with Mermaid flowchart","dependencies":[{"issue_id":"bd-4lt","depends_on_id":"bd-n1c","type":"blocks","created_at":"2026-02-01T20:31:05.098994075+02:00","created_by":"Dmitry Polishuk"},{"issue_id":"bd-4lt","depends_on_id":"bd-o7a","type":"parent-child","created_at":"2026-02-01T20:31:15.495663858+02:00","created_by":"Dmitry Polishuk"}]}
{"id":"bd-5","title":"Phase 4: PostToolUse Hook (Context Tracker)","design":"## Goal\nImplement hook that tracks file edits and maintains context.\n\n## Effort Estimate\n8-10 hours\n\n## Success Criteria\n- [ ] PostToolUse hook created and configured in hooks.json\n- [ ] Hook tracks Edit and Write tool usage\n- [ ] Context logged with: timestamp, repo, file_path, tool_name\n- [ ] Log stored in hooks/context/edit-log.txt\n- [ ] Log rotation prevents unbounded growth (max 1000 lines)\n- [ ] Non-blocking operation (always returns success)\n- [ ] Hook handles missing/malformed input gracefully\n- [ ] Manual testing shows correct logging of file edits\n- [ ] Context accessible to downstream hooks (bd-6)\n- [ ] Race condition prevention via file locking\n\n## Implementation Steps (ADDED BY writing-plans, REFINED BY SRE)\n\n### Step Group 1: Create Context Storage Infrastructure\n\n**Files:**\n- Create: hooks/context/ (directory)\n- Create: hooks/post-tool-use/01-track-edits.sh\n- Modify: hooks/hooks.json\n\n**Step 1: Create directories**\nRun:\n\\`\\`\\`bash\nmkdir -p hooks/post-tool-use hooks/context\n\\`\\`\\`\n\n**Step 2: Create the edit tracker script**\n\nCreate \\`hooks/post-tool-use/01-track-edits.sh\\`:\n\n\\`\\`\\`bash\n#!/usr/bin/env bash\nset -euo pipefail\n\n# Configuration\nCONTEXT_DIR=\"$(dirname \"$0\")/../context\"\nLOG_FILE=\"$CONTEXT_DIR/edit-log.txt\"\nLOCK_FILE=\"$CONTEXT_DIR/.edit-log.lock\"\nMAX_LOG_LINES=1000\nLOCK_TIMEOUT=5\n\n# Create context dir and log if doesn't exist\nmkdir -p \"$CONTEXT_DIR\"\ntouch \"$LOG_FILE\"\n\n# Acquire lock with timeout\nacquire_lock() {\n local count=0\n while [ $count -lt $LOCK_TIMEOUT ]; do\n if mkdir \"$LOCK_FILE\" 2\u003e/dev/null; then\n return 0\n fi\n sleep 0.2\n count=$((count + 1))\n done\n # Log but don't fail - non-blocking requirement\n echo \"Warning: Could not acquire lock\" \u003e\u00262\n return 1\n}\n\n# Release lock\nrelease_lock() {\n rmdir \"$LOCK_FILE\" 2\u003e/dev/null || true\n}\n\n# Clean up lock on exit\ntrap release_lock EXIT\n\n# Function to log edit\nlog_edit() {\n local file_path=\"$1\"\n local tool_name=\"$2\"\n local timestamp=$(date +\"%Y-%m-%d %H:%M:%S\")\n local repo=$(find_repo \"$file_path\")\n\n if acquire_lock; then\n echo \"$timestamp | $repo | $tool_name | $file_path\" \u003e\u003e \"$LOG_FILE\"\n release_lock\n fi\n}\n\n# Function to find repo root\nfind_repo() {\n local file_path=\"$1\"\n if [ -z \"$file_path\" ] || [ \"$file_path\" = \"null\" ]; then\n echo \"unknown\"\n return\n fi\n\n local dir\n dir=$(dirname \"$file_path\" 2\u003e/dev/null || echo \"/\")\n while [ \"$dir\" != \"/\" ] \u0026\u0026 [ -n \"$dir\" ]; do\n if [ -d \"$dir/.git\" ]; then\n basename \"$dir\"\n return\n fi\n dir=$(dirname \"$dir\" 2\u003e/dev/null || echo \"/\")\n done\n echo \"unknown\"\n}\n\n# Read tool use event from stdin (with timeout to prevent hanging)\nif ! read -t 2 -r tool_use_json; then\n echo '{\"decision\": \"continue\"}'\n exit 0\nfi\n\n# Validate JSON to prevent injection\nif ! echo \"$tool_use_json\" | jq empty 2\u003e/dev/null; then\n echo '{\"decision\": \"continue\"}'\n exit 0\nfi\n\n# Extract tool name and file path from tool use\ntool_name=$(echo \"$tool_use_json\" | jq -r '.tool.name // .tool_name // \"unknown\"' 2\u003e/dev/null || echo \"unknown\")\nfile_path=\"\"\n\ncase \"$tool_name\" in\n \"Edit\"|\"Write\")\n file_path=$(echo \"$tool_use_json\" | jq -r '.tool.input.file_path // .tool_input.file_path // \"null\"' 2\u003e/dev/null || echo \"null\")\n ;;\n \"MultiEdit\")\n # MultiEdit has multiple files - log each\n echo \"$tool_use_json\" | jq -r '.tool.input.edits[]?.file_path // .tool_input.edits[]?.file_path // empty' 2\u003e/dev/null | while read -r path; do\n if [ -n \"$path\" ] \u0026\u0026 [ \"$path\" != \"null\" ]; then\n log_edit \"$path\" \"$tool_name\"\n fi\n done\n echo '{\"decision\": \"continue\"}'\n exit 0\n ;;\nesac\n\n# Log single edit\nif [ -n \"$file_path\" ] \u0026\u0026 [ \"$file_path\" != \"null\" ]; then\n log_edit \"$file_path\" \"$tool_name\"\nfi\n\n# Rotate log if too large (with lock)\nif acquire_lock; then\n line_count=$(wc -l \u003c \"$LOG_FILE\" 2\u003e/dev/null || echo \"0\")\n if [ \"$line_count\" -gt \"$MAX_LOG_LINES\" ]; then\n tail -n \"$MAX_LOG_LINES\" \"$LOG_FILE\" \u003e \"$LOG_FILE.tmp\"\n mv \"$LOG_FILE.tmp\" \"$LOG_FILE\"\n fi\n release_lock\nfi\n\n# Return success (non-blocking)\necho '{\"decision\": \"continue\"}'\n\\`\\`\\`\n\n**Step 3: Make script executable**\nRun:\n\\`\\`\\`bash\nchmod +x hooks/post-tool-use/01-track-edits.sh\n\\`\\`\\`\n\n### Step Group 2: Create Context Query Utilities\n\n**Step 4: Create context query utilities**\n\nCreate \\`hooks/utils/context-query.sh\\`:\n\n\\`\\`\\`bash\n#!/usr/bin/env bash\nset -euo pipefail\n\nCONTEXT_DIR=\"$(dirname \"$0\")/../context\"\nLOG_FILE=\"$CONTEXT_DIR/edit-log.txt\"\n\n# Get files edited since timestamp\nget_recent_edits() {\n local since=\"${1:-}\"\n \n if [ ! -f \"$LOG_FILE\" ]; then\n return 0\n fi\n \n if [ -z \"$since\" ]; then\n cat \"$LOG_FILE\" 2\u003e/dev/null || true\n else\n awk -v since=\"$since\" -F '|' '$1 \u003e= since' \"$LOG_FILE\" 2\u003e/dev/null || true\n fi\n}\n\n# Get unique files edited in current session\nget_session_files() {\n local session_start=\"${1:-}\"\n \n get_recent_edits \"$session_start\" | \\\\\n awk -F '|' '{gsub(/^[ \\\\t]+|[ \\\\t]+$/, \"\", $4); print $4}' | \\\\\n sort -u\n}\n\n# Check if specific file was edited\nwas_file_edited() {\n local file_path=\"$1\"\n local since=\"${2:-}\"\n \n get_recent_edits \"$since\" | grep -q \"$(printf '%q' \"$file_path\")\" 2\u003e/dev/null\n}\n\n# Get edit count by repo\nget_repo_stats() {\n local since=\"${1:-}\"\n \n get_recent_edits \"$since\" | \\\\\n awk -F '|' '{gsub(/^[ \\\\t]+|[ \\\\t]+$/, \"\", $2); print $2}' | \\\\\n sort | uniq -c | sort -rn\n}\n\n# Clear log (for testing)\nclear_log() {\n if [ -f \"$LOG_FILE\" ]; then\n \u003e \"$LOG_FILE\"\n fi\n}\n\\`\\`\\`\n\n## Key Considerations (ADDED BY SRE REVIEW)\n\n**Race Condition Prevention**:\n- Multiple hooks may run concurrently\n- Use directory-based locking (atomic on most filesystems)\n- Timeout on lock acquisition to prevent deadlock\n- Non-blocking requirement means we log warning but continue\n\n**Input Validation**:\n- Malformed JSON could cause jq to hang or error\n- Use jq empty to validate JSON structure first\n- Read with timeout to prevent hanging on stdin\n\n**Path Security**:\n- File paths may contain spaces, quotes, or shell metacharacters\n- Use proper quoting in all path operations\n- Validate paths don't contain directory traversal (..)\n\n**Disk Full Scenarios**:\n- All writes use \u003e\u003e which will fail gracefully if disk full\n- Log rotation prevents unbounded growth\n- Non-blocking means we continue even if write fails\n\n## Anti-patterns\n- ❌ Blocking on I/O or lock acquisition\n- ❌ Unbounded log growth without rotation\n- ❌ Crashing on malformed input\n- ❌ Not handling concurrent access\n- ❌ Shell injection via unquoted paths\n- ❌ Hanging on stdin read","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-10-30T13:57:03.298717-04:00","updated_at":"2025-10-30T15:36:27.582323-04:00","closed_at":"2025-10-30T15:36:27.582323-04:00","dependencies":[{"issue_id":"bd-5","depends_on_id":"bd-1","type":"parent-child","created_at":"2025-10-30T13:57:10.497915-04:00","created_by":"ryan"},{"issue_id":"bd-5","depends_on_id":"bd-4","type":"blocks","created_at":"2025-10-30T13:57:16.843566-04:00","created_by":"ryan"}]}
{"id":"bd-50","title":"Feature: Antigravity-CLI Installer Integration","design":"## Requirements (IMMUTABLE)\n- Add first-class support for Antigravity-CLI (agy) to scripts/install.sh.\n- Detection must identify 'agy' in the system PATH.\n- Installation must use the extension-based approach: 'agy extensions install'.\n- Must leverage the existing .gemini-extension/ directory as the source.\n- Update --help and --hosts documentation to include 'antigravity'.\n- Ensure 'status', 'validate', and 'uninstall' flows are implemented for agy.\n\n## Success Criteria (MUST ALL BE TRUE)\n- [ ] 'detect_antigravity' correctly identifies the 'agy' binary.\n- [ ] 'install_antigravity' successfully runs 'agy extensions install'.\n- [ ] 'status_antigravity' correctly reports the installation state.\n- [ ] 'scripts/install.sh --hosts antigravity' works end-to-end.\n- [ ] All existing hosts (claude, gemini, etc.) remain functional.\n- [ ] Tests in tests/install-script.test.js are updated or verified.\n\n## Anti-Patterns (FORBIDDEN)\n- ❌ NO duplication of the .gemini-extension/ folder specifically for antigravity.\n- ❌ NO hardcoded paths to /Users/dpolishuk/ - must use generic home/PATH logic.\n- ❌ NO skipping the validation step after installation.\n\n## Approach\nImplement dedicated functions (detect, install, status, validate, uninstall) for antigravity within scripts/install.sh. Register 'antigravity' in the AGENT_ORDER and relevant mapping arrays. Use the 'agy' command for all interactions.\n\n## Architecture\n- scripts/install.sh: Main entry point and logic hub.\n- .gemini-extension/: Shared source for both Gemini and Antigravity extensions.\n\n## Design Rationale\n### Problem\nAntigravity-CLI is a key host for our users but currently lacks an automated setup flow in the unified installer, requiring manual configuration.\n\n### Research Findings\n- Binary is 'agy', version 1.0.1.\n- Supports 'extensions' subcommand similar to Gemini CLI.\n- Shares data roots but identifies as a separate host.\n\n## Scope Boundaries\n**In scope:**\n- PATH-based detection of 'agy'.\n- Extension installation to 'agy'.\n- Documentation updates.\n\n**Out of scope:**\n- Installing the 'agy' binary itself (assumed pre-installed).\n- Modifying the 'agy' extension architecture.","status":"closed","priority":2,"issue_type":"epic","owner":"dmitry.polishuk@gmail.com","created_at":"2026-05-22T15:00:16.0299+03:00","created_by":"Dmitry Polishuk","updated_at":"2026-05-24T22:39:19.633239+03:00","closed_at":"2026-05-24T22:39:19.633239+03:00","close_reason":"Closed"}
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed. Updated the bd-50 epic requirements and success criteria inside .beads/issues.jsonl to match the actual intended command/API (agy plugin import).

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6fbbd622cb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/install.sh
if [[ "$USE_SYMLINKS" == true ]]; then
# agy doesn't have a direct 'link' for local plugins that works like 'extensions link'
# but we can import it.
agy_stderr=$(timeout 10s agy plugin import "$ext_dir" 2>&1) || {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Avoid hard dependency on GNU timeout for Antigravity install

install_antigravity() now wraps agy plugin import with timeout, but there is no fallback when timeout is unavailable (common on macOS where only gtimeout exists). In that environment, timeout exits 127 and the host install fails even when agy itself works, so --antigravity/--hosts antigravity cannot complete. This regression is introduced in this commit and also affects new Antigravity status/validation paths that use the same assumption.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed. Added a transparent fallback shim function for the timeout command inside scripts/install.sh. If GNU timeout is not installed (e.g. default macOS), it attempts to use Homebrews gtimeout`, or executes the command directly without a timeout limit, preventing regression failures.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scripts/install.sh (1)

1100-1118: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Propagate uninstall failures instead of always reporting success.

Both uninstall branches swallow CLI errors with || true, so a timeout or rejected uninstall still prints a green check in the final summary. That leaves stale integrations behind while telling the user cleanup succeeded.

🛠️ Proposed fix
 uninstall_gemini() {
   if command -v gemini &>/dev/null; then
     if [[ "$DRY_RUN" == true ]]; then
       info "Would run: gemini extensions uninstall xpowers"
     else
-      timeout 5s gemini extensions uninstall xpowers 2>/dev/null || true
+      if ! timeout 5s gemini extensions uninstall xpowers 2>/dev/null; then
+        warn "gemini extensions uninstall xpowers failed"
+        return 1
+      fi
     fi
   fi
 }
 
 uninstall_antigravity() {
   if command -v agy &>/dev/null; then
     if [[ "$DRY_RUN" == true ]]; then
       info "Would run: agy plugin uninstall xpowers"
     else
-      timeout 5s agy plugin uninstall xpowers 2>/dev/null || true
+      if ! timeout 5s agy plugin uninstall xpowers 2>/dev/null; then
+        warn "agy plugin uninstall xpowers failed"
+        return 1
+      fi
     fi
   fi
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/install.sh` around lines 1100 - 1118, The uninstall functions
uninstall_gemini() and uninstall_antigravity() currently swallow failures by
appending "|| true" to the timeout commands, so timeouts or CLI errors always
appear successful; remove the "|| true" and instead capture and propagate the
command exit status (e.g., store "$?" into a variable), on failure call the
existing error/logger helper or return a non-zero status so the caller can
detect failures (preserve the DRY_RUN branch behavior), and ensure any error
messages include which command failed (gemini/agy and the uninstall invocation)
so the final summary can reflect actual success/failure.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.beads/issues.jsonl:
- Around line 19-21: The epic bd-50's immutable requirements still state the
installer should use "agy extensions install" but the implemented command is
"agy plugin import"; update the bd-50 description and Requirements to replace
"agy extensions install" with "agy plugin import" (and adjust any related
wording that asserts the immutable contract) so the epic accurately reflects the
shipped behavior and avoids a tracker mismatch.

In `@scripts/install.sh`:
- Around line 372-382: The
detect_antigravity/install_antigravity/validate_antigravity/uninstall_antigravity/status_antigravity
functions call GNU `timeout` directly and thus must use a portable fallback and
correct uninstall behavior; add or use a helper (e.g., find_timeout) that
prefers `timeout` or `gtimeout` and returns a command to invoke with a timeout,
replace all raw `timeout` invocations in detect_antigravity,
install_antigravity, validate_antigravity, uninstall_antigravity, and
status_antigravity to use that helper, remove the unconditional "|| true" in
uninstall_antigravity so its exit status is propagated (so the caller if
"uninstall_${agent}" can detect failure), and fix the Graphify mapping to call
"graphify antigravity install" instead of "graphify install --platform gemini"
so the correct subcommand is used.
- Around line 1568-1571: The antigravity) case currently sets
try_command="graphify install --platform gemini" and graphify_args=(install
--platform gemini), which installs the wrong layout; update the antigravity)
block to use Graphify v4's Antigravity installer by setting
try_command="graphify antigravity install" and graphify_args=(antigravity
install) (adjust the label "Antigravity CLI" remains unchanged) so the script
invokes the correct subcommand.

In `@tests/install-script.test.js`:
- Around line 48-79: The test currently writes bash/npm/npx shims into the first
entry of env.PATH (firstPart), which can mutate real user/CI PATH entries;
instead create a test-owned temporary directory (e.g., via fs.mkdtempSync or
os.tmpdir) and prepend it to env.PATH for the test, then create the bashSymlink,
npmMock and npxMock files inside that temp dir (use findExecutable to locate
real bash if needed) so only test-controlled PATH entries are modified and no
real PATH entries are written to. Ensure you replace references that use
firstPart with the new temp dir and clean up the temp dir after the test.

---

Outside diff comments:
In `@scripts/install.sh`:
- Around line 1100-1118: The uninstall functions uninstall_gemini() and
uninstall_antigravity() currently swallow failures by appending "|| true" to the
timeout commands, so timeouts or CLI errors always appear successful; remove the
"|| true" and instead capture and propagate the command exit status (e.g., store
"$?" into a variable), on failure call the existing error/logger helper or
return a non-zero status so the caller can detect failures (preserve the DRY_RUN
branch behavior), and ensure any error messages include which command failed
(gemini/agy and the uninstall invocation) so the final summary can reflect
actual success/failure.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6d9d96ae-f090-42c5-b833-45ffe28e973f

📥 Commits

Reviewing files that changed from the base of the PR and between bc5ad19 and 6fbbd62.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • .beads/issues.jsonl
  • scripts/install.sh
  • scripts/install.ts
  • tests/install-script.test.js

Comment thread .beads/issues.jsonl Outdated
Comment thread scripts/install.sh
Comment thread scripts/install.sh Outdated
Comment thread tests/install-script.test.js Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 44eb223b49

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/install.sh Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 637cdccd64

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/install.ts Outdated
Comment thread scripts/install.sh Outdated
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.

2 participants