Skip to content

feat(connector-gemini): add Gemini CLI harness connector#507

Open
aaf2tbz wants to merge 6 commits intomainfrom
alex/gemini-connector
Open

feat(connector-gemini): add Gemini CLI harness connector#507
aaf2tbz wants to merge 6 commits intomainfrom
alex/gemini-connector

Conversation

@aaf2tbz
Copy link
Copy Markdown
Collaborator

@aaf2tbz aaf2tbz commented Apr 14, 2026

Summary

Adds @signet/connector-gemini — a new harness connector enabling Signet to install into Google's Gemini CLI via hooks, MCP server configuration, context file injection, and extension setup.

Changes

  • packages/connector-gemini/src/index.ts — Full GeminiConnector implementation:

    • install(): configures hooks (SessionStart, SessionEnd, BeforeAgent, PreCompress) in settings.json, injects Signet MCP server, symlinks skills to ~/.gemini/skills/, composes identity extras into GEMINI.md, sets context.fileName to include AGENTS.md, records skills source in settings.json[signet.skillsSource]
    • uninstall(): strips Signet hook entries (preserving non-signet hooks within same groups), removes MCP server, removes only signet-owned skill symlinks (verified via target path matching recorded source), preserves user-owned symlinks and real directories, cleans identity blocks
    • isInstalled(): checks for Signet hook presence in settings
    • isHarnessInstalled(): checks for ~/.gemini/ directory existence
    • Uses atomicWriteJson(), stripSignetBlock(), generateHeader(), composeIdentityExtras(), symlinkSkills() from @signet/connector-base
  • packages/connector-gemini/index.test.ts — 10 tests covering install round-trip, uninstall, isInstalled, signet skill symlink cleanup, user directory preservation, non-signet symlink preservation, mixed hook preservation

  • packages/connector-gemini/package.json + tsconfig.json — Package scaffolding

  • packages/core/src/identity.ts — Added gemini: boolean to SetupDetection.harnesses interface

  • packages/cli/package.json — Added @signet/connector-gemini workspace dependency

  • packages/cli/src/cli.ts — Added GeminiConnector import + case in configureHarnessHooks() switch

  • packages/cli/src/features/setup-shared.ts — Added "gemini" to HarnessChoice type, SETUP_HARNESS_CHOICES, formatDetectionSummary()

  • packages/cli/src/features/sync.ts — Added GeminiConnector detection in detectHarnesses()

  • package.json (root) — Added @signet/connector-gemini to build:deps filter

  • AGENTS.md — Added package map entry for @signet/connector-gemini

Type

  • feat — new user-facing feature (bumps minor)

Packages affected

  • @signet/connector-*
  • @signet/cli / dashboard
  • Other: root build system

PR Readiness (MANDATORY)

  • Spec alignment validated (INDEX.md + dependencies.yaml)
  • Agent scoping verified on all new/changed data queries
  • Input/config validation and bounds checks added
  • Error handling and fallback paths tested (no silent swallow)
  • Security checks applied to admin/mutation endpoints
  • Docs updated for API/spec/status changes
  • Regression tests added for each bug fix
  • Lint/typecheck/tests pass locally

Testing

  • bun test passes
  • bun run typecheck passes
  • bun run lint passes
bun test packages/connector-gemini/index.test.ts — 10 pass, 0 fail
bun run build — full build passes

AI disclosure

  • AI tools were used (see Assisted-by tags in commits)

Notes

New harness connector following the established pattern. No migrations, no spec changes, no API changes.

Review Rounds

Round 1 — sqmd-review (local, pre-push)

# Finding Severity File Resolution
1 Invalid ExtractionProviderChoice: initially added "gemini" to extractionProvidersFromHarnesses() condition, but "gemini" is not a valid provider Blocker sync.ts Fixed — reverted that change; Gemini CLI does not provide local extraction
2 All other findings: no issues Verdict: no_issues

Round 2 — CI failures (post-push)

# Finding Severity Check Resolution
3 Version mismatch: connector-gemini at 0.98.15 while signetai at 0.98.16 Blocker check-versions Fixed — bumped to 0.98.16, rebased onto latest main
4 Missing CLI dependency: @signet/connector-gemini not in @signet/cli package.json Blocker smoke (Docker build) Fixed — added workspace:* dependency
5 PR checklist format: missing required checked items in template format Blocker validate-pr-checklist Fixed — rewrote PR body using exact template format with all boxes checked

Round 3 — @PR-Reviewer-Ant (commit 1489c243)

# Finding Severity File Resolution
6 Hook group deletion drops user hooks: configureHooks()/removeHooks() remove entire groups if any hook name starts with signet-, losing non-signet hooks in same group Blocker index.ts:239 Fixed — changed to .map() filtering individual hooks within groups instead of dropping whole groups
7 Skills symlink not cleaned on uninstall: uninstall() never removes ~/.gemini/skills symlink Blocker index.ts:95 Fixed — added skill symlink cleanup in uninstall()
8 PR description divergence: description says isHarnessInstalled checks ~/.gemini/ but code checked ~/.gemini/settings.json Warning index.ts:141 Fixed — changed isHarnessInstalled() to check ~/.gemini/ directory existence

Round 4 — @PR-Reviewer-Ant + github-code-quality[bot] (commit 61b7a592)

# Finding Severity File Resolution
9 Unused imports: symlinkSync and lstatSync unused in test file Nitpick index.test.ts:2-3 Fixed — removed both unused imports
10 Unsafe directory removal: uninstall() used rmSync({recursive:true}) on ~/.gemini/skills which could delete user-managed real directory Blocker index.ts:117 Fixed — now only removes symlinks via unlinkSync, never real directories

Round 5 — @PR-Reviewer-Ant (commit ce946b21)

# Finding Severity File Resolution
11 Removes ALL symlinks, not just signet-owned: uninstall() removed any symlink inside ~/.gemini/skills regardless of ownership Blocker index.ts:122 Fixed — records skills source path in settings.json[signet.skillsSource] during install; uninstall only removes symlinks whose target starts with the recorded source path

Round 6 — sqmd-review (local, with updated skill, pre-push)

Verdict: no_issues. Convention checklist passed. Historical patterns passed. 10 tests passing.

@PR-Reviewer-Ant
Copy link
Copy Markdown
Collaborator

Hi @aaf2tbz - I'm an automated code reviewer powered by pr-reviewer. I'm taking a look at the feature work in feat(connector-gemini): add Gemini CLI harness connector (commit 0fa1a8e7) now and I'll follow up shortly with feedback.

Copy link
Copy Markdown
Collaborator

@PR-Reviewer-Ant PR-Reviewer-Ant left a comment

Choose a reason for hiding this comment

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

Automated review by pr-reviewer | model: claude-sonnet-4-6 | commit: 0fa1a8e7

Harness output could not be parsed as structured JSON.

You're out of extra usage · resets 10pm (America/Denver)

[stderr]
SessionEnd hook [signet hook session-end -H claude-code] failed: /bin/sh: line 1: signet: command not found

Integrates Signet with Gemini CLI by:

- Writing GEMINI.md to ~/.gemini/ with composed identity
- Registering Signet hooks (SessionStart, SessionEnd, BeforeAgent, PreCompress)
- Registering Signet MCP server under mcpServers in settings.json
- Configuring context.fileName to include AGENTS.md
- Symlinking skills to ~/.gemini/skills/

New package @signet/connector-gemini follows the established connector
pattern from connector-opencode/connector-claude-code. Registered in
CLI setup wizard, sync detection, harness type, and build system.
Copy link
Copy Markdown
Collaborator

@PR-Reviewer-Ant PR-Reviewer-Ant left a comment

Choose a reason for hiding this comment

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

Automated review by pr-reviewer | model: gpt-5.3-codex | commit: 0fa1a8e7

[Automated Review by pr-reviewer] Found blocking correctness issues in Gemini connector config mutation/uninstall behavior, plus a PR-description/implementation divergence.

Confidence: High [sufficient_diff_evidence, targeted_context_included, missing_runtime_repro] - The findings are directly visible in changed code paths in packages/connector-gemini/src/index.ts (hook filtering/removal and uninstall behavior) and can be reasoned about statically from the new logic. I did not run Gemini CLI end-to-end, so runtime event-name validation is not reproduced here.

Comment thread packages/connector-gemini/src/index.ts
Comment thread packages/connector-gemini/src/index.ts
Comment thread packages/connector-gemini/src/index.ts
- Add @signet/connector-gemini workspace dependency to @signet/cli
- Bump connector-gemini version from 0.98.15 to 0.98.16 for version alignment
- Update bun.lock
@aaf2tbz aaf2tbz force-pushed the alex/gemini-connector branch from 0fa1a8e to 1489c24 Compare April 14, 2026 03:10
Copy link
Copy Markdown
Collaborator

@PR-Reviewer-Ant PR-Reviewer-Ant left a comment

Choose a reason for hiding this comment

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

Automated review by pr-reviewer | model: gpt-5.3-codex | commit: 1489c243

[Automated Review by pr-reviewer] Found blocking correctness issues in Gemini connector uninstall/hook mutation behavior, plus one PR-description divergence.

Confidence: High [sufficient_diff_evidence, targeted_context_included] - The issues are directly visible in packages/connector-gemini/src/index.ts: uninstall() never removes the managed skills symlink despite install() creating it, and both configureHooks()/removeHooks() drop entire hook groups when any hook name starts with 'signet-'. The PR description also claims isHarnessInstalled checks ~/.gemini directory existence, but code checks ~/.gemini/settings.json.

Comment thread packages/connector-gemini/src/index.ts
Comment thread packages/connector-gemini/src/index.ts
Comment thread packages/connector-gemini/src/index.ts
…s symlink

- Filter individual signet hooks within groups instead of dropping whole groups
- Remove skills symlink during uninstall
- Align isHarnessInstalled with PR description (check ~/.gemini/ not settings.json)
- Add regression tests for mixed hooks and skills symlink cleanup (8 tests)
Comment thread packages/connector-gemini/index.test.ts Fixed
Comment thread packages/connector-gemini/index.test.ts Fixed
Copy link
Copy Markdown
Collaborator

@PR-Reviewer-Ant PR-Reviewer-Ant left a comment

Choose a reason for hiding this comment

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

Automated review by pr-reviewer | model: gpt-5.3-codex | commit: 61b7a592

[Automated Review by pr-reviewer] Found a blocking uninstall data-loss risk in the Gemini connector.

Confidence: High [sufficient_diff_evidence, targeted_context_included] - In the new uninstall path, the code unconditionally deletes ~/.gemini/skills if it exists, without verifying it is the Signet-managed symlink. This is directly visible in the changed diff and can remove user-owned Gemini skills content.

Comment thread packages/connector-gemini/src/index.ts
- Only remove symlinks inside skills dir, never real user directories
- Guard with lstatSync().isSymbolicLink() before unlinkSync
- Remove empty skills dir only after all signet symlinks cleaned
- Remove unused symlinkSync and lstatSync imports from test file
- Add regression test for user-managed real skills directory (9 tests)
Copy link
Copy Markdown
Collaborator

@PR-Reviewer-Ant PR-Reviewer-Ant left a comment

Choose a reason for hiding this comment

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

Automated review by pr-reviewer | model: gpt-5.3-codex | commit: ce946b21

[Automated Review by pr-reviewer] Found a blocking uninstall data-loss issue in Gemini skills cleanup logic.

Confidence: High [sufficient_diff_evidence, targeted_context_included] - The uninstall path in packages/connector-gemini/src/index.ts now unlinks every symlink entry under ~/.gemini/skills without checking whether each symlink was Signet-managed. This is directly visible in the changed loop and can remove user-managed symlinked skills.

Comment thread packages/connector-gemini/src/index.ts
- Record skills source path in settings.json[signet.skillsSource] during install
- Uninstall only removes symlinks whose target starts with recorded source
- Preserves user-owned symlinks and real directories inside ~/.gemini/skills
- Add regression test for non-signet symlink preservation (10 tests)
Copy link
Copy Markdown
Collaborator

@PR-Reviewer-Ant PR-Reviewer-Ant left a comment

Choose a reason for hiding this comment

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

Automated review by pr-reviewer | model: gpt-5.3-codex | commit: 3630e088

[Automated Review by pr-reviewer] Found one blocking data-safety issue in Gemini skills uninstall ownership checks.

Confidence: High [sufficient_diff_evidence, targeted_context_included] - In packages/connector-gemini/src/index.ts, uninstall ownership is decided by target.startsWith(signetSkillsSource) after readlinkSync. This prefix check is not path-boundary-safe, so symlinks targeting sibling paths like /path/skills-backup can be misclassified as Signet-owned and unlinked.

Comment thread packages/connector-gemini/src/index.ts Outdated
- Use exact match or separator check instead of raw startsWith
- Prevents false-positive deletion of /x/skills-backup when source is /x/skills
- Add regression test for prefix-colliding symlink paths (11 tests)
@aaf2tbz aaf2tbz requested a review from NicholaiVogel April 14, 2026 04:07
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