Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
fa60faf
feat(32-01): create lib/provider-claude.cjs with provider interface
snipcodeit Mar 5, 2026
97102bf
feat(32-01): reduce lib/claude.cjs to backward-compat shim
snipcodeit Mar 5, 2026
95709cb
docs(32-01): complete provider interface contract plan
snipcodeit Mar 5, 2026
80614a5
feat(32-02): create lib/provider-manager.cjs with runtime provider re…
snipcodeit Mar 5, 2026
c22192a
feat(32-02): wire bin/mgw.cjs and lib/index.cjs through ProviderManager
snipcodeit Mar 5, 2026
a0d55fe
docs(32-02): complete provider-manager plan
snipcodeit Mar 5, 2026
67a8682
docs(33): create phase 33 plans for Gemini and OpenCode provider impl…
snipcodeit Mar 5, 2026
8c1d372
feat(33-01): create GeminiProvider implementing MGW provider interface
snipcodeit Mar 5, 2026
13cd93b
feat(33-02): create OpenCodeProvider implementing MGW provider interface
snipcodeit Mar 5, 2026
9da15e6
feat(33-03): register GeminiProvider and OpenCodeProvider in Provider…
snipcodeit Mar 5, 2026
2b76591
docs(33): complete Gemini and OpenCode provider implementation phase …
snipcodeit Mar 5, 2026
240c672
docs(34): create phase 34 plan for gsd-adapter generalization
snipcodeit Mar 5, 2026
01f7ae7
feat(34-01): implement resolveGsdRoot() 3-tier path resolution
snipcodeit Mar 5, 2026
011d210
docs(35): create phase plan for command installation multi-cli support
snipcodeit Mar 5, 2026
321754b
feat(35-01): add multi-CLI provider detection to mgw-install
snipcodeit Mar 5, 2026
97e9df7
fix(review): correct provider path mismatches and installer guard bugs
snipcodeit Mar 5, 2026
1158cd2
fix(review): help command ignores --provider, bad error messages, gem…
snipcodeit Mar 5, 2026
e4ac26b
fix(review): installer cleanup ordering, shim namespace leak
snipcodeit Mar 5, 2026
ec3df53
merge: resolve conflicts with main (error infrastructure + providers)
Mar 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions .planning/phases/32-provider-interface-design/32-01-SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
---
phase: 32-provider-interface-design
plan: "01"
subsystem: api
tags: [provider-interface, claude, abstraction, backward-compat]

requires: []
provides:
- lib/provider-claude.cjs implementing MGW provider contract (PROVIDER_ID, assertAvailable, invoke, getCommandsDir)
- lib/claude.cjs reduced to backward-compat shim with legacy aliases
affects:
- 32-02-provider-manager

tech-stack:
added: []
patterns:
- "Provider interface contract: every lib/provider-*.cjs exports PROVIDER_ID, assertAvailable(), invoke(), getCommandsDir()"
- "Backward-compat shim pattern: old module re-exports from new with legacy aliases"

key-files:
created:
- lib/provider-claude.cjs
modified:
- lib/claude.cjs

key-decisions:
- "Renamed assertClaudeAvailable -> assertAvailable and invokeClaude -> invoke to establish generic provider contract names"
- "lib/claude.cjs becomes a shim (not deleted) so bin/mgw.cjs and lib/index.cjs need no changes in this plan"
- "PROVIDER_ID constant added to make provider identity explicit and registry-ready"

patterns-established:
- "Provider module pattern: lib/provider-{id}.cjs with PROVIDER_ID, assertAvailable, invoke, getCommandsDir"
- "Shim pattern: backward-compat shim spreads new module and adds legacy aliases"

requirements-completed:
- MP-01

duration: 2min
completed: 2026-03-05
---

# Phase 32 Plan 01: Provider Interface Design Summary

**Claude CLI provider extracted to lib/provider-claude.cjs with generic provider contract; lib/claude.cjs reduced to a backward-compat shim with legacy aliases**

## Performance

- **Duration:** 2 min
- **Started:** 2026-03-05T06:17:27Z
- **Completed:** 2026-03-05T06:19:30Z
- **Tasks:** 2
- **Files modified:** 2

## Accomplishments
- Created lib/provider-claude.cjs with provider interface contract (PROVIDER_ID='claude', assertAvailable, invoke, getCommandsDir)
- Reduced lib/claude.cjs to a thin shim that re-exports from provider-claude.cjs and adds legacy aliases
- All existing callers (bin/mgw.cjs, lib/index.cjs) continue to work without modification

## Task Commits

Each task was committed atomically:

1. **Task 1: Create lib/provider-claude.cjs** - `fa60faf` (feat)
2. **Task 2: Reduce lib/claude.cjs to backward-compat shim** - `97102bf` (feat)

## Files Created/Modified
- `lib/provider-claude.cjs` - Canonical Claude CLI provider implementing the MGW provider interface
- `lib/claude.cjs` - Backward-compat shim re-exporting from provider-claude.cjs with legacy aliases

## Decisions Made
- Renamed assertClaudeAvailable -> assertAvailable and invokeClaude -> invoke to establish generic provider contract names that all future providers will implement
- Kept lib/claude.cjs as a shim (rather than deleting it) so bin/mgw.cjs and lib/index.cjs need no changes in this plan — wiring through ProviderManager is plan 32-02's responsibility
- Added PROVIDER_ID constant to make provider identity explicit and enable registry-based resolution

## Deviations from Plan

None - plan executed exactly as written.

## Issues Encountered
None.

## User Setup Required
None - no external service configuration required.

## Next Phase Readiness
- lib/provider-claude.cjs ready for 32-02 to import into ProviderManager registry
- Provider contract shape established; ProviderManager can now resolve by PROVIDER_ID
- Both verify commands pass, backward compat preserved

---
*Phase: 32-provider-interface-design*
*Completed: 2026-03-05*
116 changes: 116 additions & 0 deletions .planning/phases/32-provider-interface-design/32-02-SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
---
phase: 32-provider-interface-design
plan: "02"
subsystem: api
tags: [provider-manager, abstraction, cli, barrel-export, multi-provider]

requires:
- phase: 32-01
provides: lib/provider-claude.cjs implementing MGW provider contract (PROVIDER_ID, assertAvailable, invoke, getCommandsDir)
provides:
- lib/provider-manager.cjs with ProviderManager registry resolving active provider at runtime
- lib/index.cjs barrel exports ProviderManager for convenient import
- bin/mgw.cjs wired through ProviderManager with --provider flag for future provider selection
affects: []

tech-stack:
added: []
patterns:
- "ProviderManager registry pattern: central resolution point mapping PROVIDER_ID strings to provider modules"
- "Provider flag pattern: --provider CLI flag selects AI provider without touching command logic"
- "Barrel spread pattern: ...require('./provider-manager.cjs') adds ProviderManager to lib/index.cjs"

key-files:
created:
- lib/provider-manager.cjs
modified:
- lib/index.cjs
- bin/mgw.cjs

key-decisions:
- "ProviderManager implemented as a plain object (not a class) with getProvider and listProviders — simplest shape that satisfies the registry contract"
- "getProvider() defaults to 'claude' when no providerId given — zero-config for existing usage"
- "getProvider('unknown') throws with helpful error listing available providers"
- "--provider global flag added to mgw CLI to enable future provider selection without code changes"
- "help command getCommandsDir() call fixed to route through ProviderManager.getProvider() — bug caught during Task 2"

patterns-established:
- "ProviderManager as single resolution point: future providers added to registry dict, no command logic changes needed"
- "Provider selection: opts.provider from CLI flag flows directly to ProviderManager.getProvider(opts.provider)"

requirements-completed:
- MP-02
- MP-03

duration: 8min
completed: 2026-03-05
---

# Phase 32 Plan 02: Provider Interface Design Summary

**ProviderManager registry built in lib/provider-manager.cjs and wired into bin/mgw.cjs + lib/index.cjs, completing the multi-provider abstraction layer with --provider flag support**

## Performance

- **Duration:** 8 min
- **Started:** 2026-03-05T06:20:00Z
- **Completed:** 2026-03-05T06:28:00Z
- **Tasks:** 2
- **Files modified:** 3

## Accomplishments
- Created lib/provider-manager.cjs with ProviderManager registry (getProvider, listProviders)
- Wired bin/mgw.cjs to use ProviderManager instead of direct lib/claude.cjs import
- Added --provider global flag to mgw CLI for future provider selection
- Added ProviderManager to lib/index.cjs barrel so callers can import from single entry point
- Fixed a pre-existing bug where help command called bare getCommandsDir() without a provider reference

## Task Commits

Each task was committed atomically:

1. **Task 1: Create lib/provider-manager.cjs** - `80614a5` (feat)
2. **Task 2: Wire bin/mgw.cjs and lib/index.cjs through ProviderManager** - `c22192a` (feat)

## Files Created/Modified
- `lib/provider-manager.cjs` - Runtime provider resolution registry; maps PROVIDER_ID strings to provider modules
- `lib/index.cjs` - Added ...require('./provider-manager.cjs') barrel spread
- `bin/mgw.cjs` - Replaced lib/claude.cjs import with ProviderManager; added --provider flag; routed all provider calls through provider.* methods

## Decisions Made
- ProviderManager implemented as a plain object singleton (not a class) — simplest shape satisfying the registry contract with no instantiation overhead
- Default provider is 'claude' when no --provider flag given — ensures zero-config backward compatibility
- Error message on unknown provider explicitly lists available providers: `Unknown provider: "X". Available: claude`
- The --provider flag value flows directly as opts.provider into ProviderManager.getProvider(opts.provider)

## Deviations from Plan

### Auto-fixed Issues

**1. [Rule 1 - Bug] Fixed help command calling bare getCommandsDir() after import removed**
- **Found during:** Task 2 (Wire bin/mgw.cjs and lib/index.cjs through ProviderManager)
- **Issue:** bin/mgw.cjs help command (line 511) called `getCommandsDir()` directly, but the new import replaced the old destructured `{ assertClaudeAvailable, invokeClaude, getCommandsDir }` import with `{ ProviderManager }`. The bare `getCommandsDir` reference would throw ReferenceError at runtime.
- **Fix:** Changed `getCommandsDir()` to `ProviderManager.getProvider().getCommandsDir()` to route through the provider abstraction consistently.
- **Files modified:** bin/mgw.cjs
- **Verification:** `node --check bin/mgw.cjs` passes; no remaining bare `getCommandsDir` references outside of provider.* calls.
- **Committed in:** `c22192a` (Task 2 commit)

---

**Total deviations:** 1 auto-fixed (Rule 1 - Bug)
**Impact on plan:** Fix was necessary for correctness — help command would have thrown ReferenceError at runtime. No scope creep.

## Issues Encountered
- Plan 32-02 had uncommitted working tree changes for bin/mgw.cjs and lib/index.cjs from a previous partial execution. These were verified against the plan specification, the bug fix was applied, and the commit was created.

## User Setup Required
None - no external service configuration required.

## Next Phase Readiness
- Multi-provider abstraction is complete; future providers add a lib/provider-{id}.cjs and register in provider-manager.cjs registry
- All existing mgw commands work through the new abstraction (backward compatible)
- --provider flag is wired and ready for future provider implementations

---
*Phase: 32-provider-interface-design*
*Completed: 2026-03-05*
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
---
phase: 33-gemini-opencode-provider-implementation
plan: "01"
type: execute
wave: 1
depends_on: []
files_modified:
- lib/provider-gemini.cjs
autonomous: true
requirements:
- MP-04

must_haves:
truths:
- "GeminiProvider module exports PROVIDER_ID, assertAvailable, invoke, getCommandsDir"
- "Missing gemini binary produces a clear, actionable error message"
- "GeminiProvider.getCommandsDir() returns the path to an appropriate commands directory"
- "GeminiProvider.invoke() spawns the gemini CLI with equivalent streaming/quiet/dry-run behaviour to ClaudeProvider"
artifacts:
- path: "lib/provider-gemini.cjs"
provides: "Gemini CLI provider implementing the MGW provider interface"
exports: ["PROVIDER_ID", "assertAvailable", "invoke", "getCommandsDir"]
key_links:
- from: "lib/provider-gemini.cjs"
to: "lib/provider-manager.cjs"
via: "require() in registry dict"
pattern: "require\\('./provider-gemini\\.cjs'\\)"
---

<objective>
Create lib/provider-gemini.cjs implementing the MGW provider interface contract so GeminiProvider can be registered in ProviderManager and invoked via --provider gemini.

Purpose: Phase 32 established the provider contract and ProviderManager registry. This plan fills in the Gemini side of the multi-provider feature — an exact structural mirror of provider-claude.cjs adapted for the gemini CLI.

Output: lib/provider-gemini.cjs with PROVIDER_ID='gemini', assertAvailable(), invoke(), getCommandsDir()
</objective>

<execution_context>
@/home/ubuntu/.claude/get-shit-done/workflows/execute-plan.md
@/home/ubuntu/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/STATE.md

# Provider contract established in Phase 32 — mirror this shape exactly
# Key interfaces from lib/provider-claude.cjs:

```javascript
// PROVIDER_ID {string} — identifies provider in registry
const PROVIDER_ID = 'claude';

// assertAvailable() — synchronous; process.exit(1) on failure
// checks binary exists (ENOENT), then checks auth/status
function assertAvailable() { ... }

// invoke(commandFile, userPrompt, opts) — Promise<{exitCode, output}>
// opts: { quiet, dryRun, model, json }
// streams output by default (stdio: inherit), buffers when quiet
function invoke(commandFile, userPrompt, opts) { ... }

// getCommandsDir() — returns absolute path; throws if missing
function getCommandsDir() { ... }

module.exports = { PROVIDER_ID, assertAvailable, invoke, getCommandsDir };
```

# Phase 32-02 pattern from SUMMARY:
# "future providers add a lib/provider-{id}.cjs and register in provider-manager.cjs registry"
# ProviderManager.getProvider('gemini') must work after 33-03 registers this module.

# Gemini CLI specifics (research-informed decisions to embed in plan):
# - Binary name: gemini (Google's Gemini CLI tool)
# - Auth check: `gemini --version` for binary presence; no separate auth status command exists
# like claude's `claude auth status` — use `gemini --version` only; if it exits 0, consider available
# - Invocation: `gemini -p <prompt>` (non-interactive mode) — equivalent to claude -p
# - Model flag: `--model <model-id>` if supported, omit if not
# - Commands directory: ~/.gemini/commands/ (user-level, not bundled with mgw)
# getCommandsDir() should return path.join(os.homedir(), '.gemini', 'commands')
# and throw a clear message if it doesn't exist (similar to ClaudeProvider)
# - No --system-prompt-file equivalent in gemini CLI:
# Pass commandFile content inline by prepending it to the user prompt
# (read file contents, prepend as system context block, then append user prompt)
# - No --output-format json equivalent: ignore opts.json (gemini doesn't support it)
</context>

<tasks>

<task type="auto">
<name>Task 1: Create lib/provider-gemini.cjs</name>
<files>lib/provider-gemini.cjs</files>
<action>
Create lib/provider-gemini.cjs following the exact same module shape as lib/provider-claude.cjs.

Key implementation details:

**PROVIDER_ID:** `'gemini'`

**assertAvailable():**
- Run `gemini --version` via execSync with stdio piped
- On ENOENT: print clear install message (e.g. "Error: gemini CLI is not installed.\n\nInstall it with:\n npm install -g @google/gemini-cli\n\nThen run:\n gemini auth") and process.exit(1)
- On other error: print generic "gemini CLI check failed" message and process.exit(1)
- No separate auth status check (gemini has no `gemini auth status` equivalent) — binary presence is sufficient guard

**getCommandsDir():**
- Return `path.join(os.homedir(), '.gemini', 'commands')` (user-level commands directory)
- Require `os` from Node built-ins
- If directory does not exist, throw: `Commands directory not found at: {dir}\nRun: mgw install-commands --provider gemini\nto install MGW commands for the Gemini provider.`

**invoke(commandFile, userPrompt, opts):**
- `const o = opts || {}`
- Build args starting with `-p`
- Gemini CLI has no --system-prompt-file: if commandFile is provided, read its contents with `fs.readFileSync(commandFile, 'utf-8')` and prepend to the effective prompt as a system block:
```
<system>\n{fileContents}\n</system>\n\n{userPrompt || 'run'}
```
Pass this combined string as the single positional prompt argument.
- If opts.model: push `--model`, opts.model to args (gemini supports --model flag)
- Do NOT push --output-format json (not supported); ignore opts.json
- opts.dryRun: print "Would invoke: gemini " + args and resolve { exitCode: 0, output: '' }
- Spawn `gemini` with args; stdio: inherit when not quiet, ['pipe','pipe','pipe'] when quiet
- on 'error' with ENOENT: reject with `new Error('gemini CLI not found. Install with: npm install -g @google/gemini-cli')`
- on 'close': resolve { exitCode: code || 0, output }

**Exports:** `module.exports = { PROVIDER_ID, assertAvailable, invoke, getCommandsDir };`

Use `'use strict';` at top. Add JSDoc matching provider-claude.cjs style.
</action>
<verify>
<automated>node --check /home/ubuntu/projects/mgw/.worktrees/issue/157-multi-provider-ai-architecture-resear/lib/provider-gemini.cjs && node -e "const p = require('./lib/provider-gemini.cjs'); console.assert(p.PROVIDER_ID === 'gemini'); console.assert(typeof p.assertAvailable === 'function'); console.assert(typeof p.invoke === 'function'); console.assert(typeof p.getCommandsDir === 'function'); console.log('GeminiProvider interface OK');" --eval "$(cat)" 2>&1 || node -e "const p = require('/home/ubuntu/projects/mgw/.worktrees/issue/157-multi-provider-ai-architecture-resear/lib/provider-gemini.cjs'); console.assert(p.PROVIDER_ID === 'gemini'); console.assert(typeof p.assertAvailable === 'function'); console.assert(typeof p.invoke === 'function'); console.assert(typeof p.getCommandsDir === 'function'); console.log('GeminiProvider interface OK');"</automated>
</verify>
<done>lib/provider-gemini.cjs exists; exports PROVIDER_ID='gemini', assertAvailable, invoke, getCommandsDir; node --check passes; node -e require() confirms all four exports present</done>
</task>

</tasks>

<verification>
node --check lib/provider-gemini.cjs passes (no syntax errors)
node -e "const p = require('./lib/provider-gemini.cjs'); ['PROVIDER_ID','assertAvailable','invoke','getCommandsDir'].forEach(k => { if (!p[k]) throw new Error('missing: ' + k); }); console.log('OK');" passes
PROVIDER_ID === 'gemini'
</verification>

<success_criteria>
lib/provider-gemini.cjs implements the full MGW provider contract: PROVIDER_ID='gemini', assertAvailable() exits with clear error when gemini binary missing, getCommandsDir() returns ~/.gemini/commands path, invoke() spawns gemini with streaming/quiet/dry-run support.
</success_criteria>

<output>
After completion, create `.planning/phases/33-gemini-opencode-provider-implementation/33-01-SUMMARY.md`
</output>
Loading