@legioncodeinc/rflectr 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/dist/cli.js +4 -1
  2. package/dist/cli.js.map +1 -0
  3. package/package.json +4 -1
  4. package/.markdown-link-check.json +0 -7
  5. package/AGENTS.md +0 -169
  6. package/assets/733630021_1421561133353555_3999689754075308337_n.jpg +0 -0
  7. package/assets/github-home-image.png +0 -0
  8. package/assets/og-image.jpg +0 -0
  9. package/assets/og-image.png +0 -0
  10. package/assets/og-image.psd +0 -0
  11. package/assets/rflectr-no-bg.png +0 -0
  12. package/assets/vertex-models.example.json +0 -14
  13. package/library/README.md +0 -39
  14. package/library/issues/README.md +0 -46
  15. package/library/issues/backlog/README.md +0 -26
  16. package/library/issues/completed/README.md +0 -13
  17. package/library/issues/in-work/README.md +0 -13
  18. package/library/knowledge/README.md +0 -34
  19. package/library/knowledge/private/README.md +0 -40
  20. package/library/knowledge/private/ai/README.md +0 -8
  21. package/library/knowledge/private/ai/model-discovery-classification.md +0 -81
  22. package/library/knowledge/private/ai/translation-layer.md +0 -88
  23. package/library/knowledge/private/architecture/README.md +0 -10
  24. package/library/knowledge/private/architecture/launch-flow-claude.md +0 -93
  25. package/library/knowledge/private/architecture/system-overview.md +0 -108
  26. package/library/knowledge/private/auth/README.md +0 -9
  27. package/library/knowledge/private/auth/oauth-device-flows.md +0 -95
  28. package/library/knowledge/private/data/README.md +0 -8
  29. package/library/knowledge/private/data/preferences-config.md +0 -87
  30. package/library/knowledge/private/data/provider-registry.md +0 -126
  31. package/library/knowledge/private/infrastructure/README.md +0 -7
  32. package/library/knowledge/private/infrastructure/server-gateway.md +0 -87
  33. package/library/knowledge/private/integrations/README.md +0 -8
  34. package/library/knowledge/private/integrations/harnesses.md +0 -87
  35. package/library/knowledge/private/integrations/local-proxy.md +0 -82
  36. package/library/knowledge/private/security/README.md +0 -9
  37. package/library/knowledge/private/security/credential-storage.md +0 -129
  38. package/library/knowledge/private/standards/documentation-framework.md +0 -154
  39. package/library/knowledge/public/README.md +0 -49
  40. package/library/knowledge/public/faqs/README.md +0 -7
  41. package/library/knowledge/public/faqs/troubleshooting.md +0 -92
  42. package/library/knowledge/public/guides/README.md +0 -13
  43. package/library/knowledge/public/guides/ai-agents.md +0 -273
  44. package/library/knowledge/public/guides/api-server.md +0 -108
  45. package/library/knowledge/public/guides/claude-desktop.md +0 -382
  46. package/library/knowledge/public/guides/codex.md +0 -296
  47. package/library/knowledge/public/guides/gemini-cli.md +0 -105
  48. package/library/knowledge/public/guides/model-compatibility.md +0 -80
  49. package/library/knowledge/public/guides/providers.md +0 -90
  50. package/library/knowledge/public/overview/README.md +0 -7
  51. package/library/knowledge/public/overview/what-is-rflectr.md +0 -71
  52. package/library/notes/README.md +0 -21
  53. package/library/requirements/README.md +0 -51
  54. package/library/requirements/backlog/README.md +0 -30
  55. package/library/requirements/completed/README.md +0 -14
  56. package/library/requirements/completed/prd-001-cli-core-launch-orchestration/prd-001-cli-core-launch-orchestration-index.md +0 -205
  57. package/library/requirements/completed/prd-001-cli-core-launch-orchestration/qa/.gitkeep +0 -0
  58. package/library/requirements/completed/prd-002-provider-registry/prd-002-provider-registry-index.md +0 -263
  59. package/library/requirements/completed/prd-002-provider-registry/qa/.gitkeep +0 -0
  60. package/library/requirements/completed/prd-003-model-discovery-classification/prd-003-model-discovery-classification-index.md +0 -260
  61. package/library/requirements/completed/prd-003-model-discovery-classification/qa/.gitkeep +0 -0
  62. package/library/requirements/completed/prd-004-translation-layer/prd-004-translation-layer-index.md +0 -196
  63. package/library/requirements/completed/prd-004-translation-layer/qa/.gitkeep +0 -0
  64. package/library/requirements/completed/prd-005-local-proxy-catalog-routing/prd-005-local-proxy-catalog-routing-index.md +0 -176
  65. package/library/requirements/completed/prd-005-local-proxy-catalog-routing/qa/.gitkeep +0 -0
  66. package/library/requirements/completed/prd-006-credential-storage/prd-006-credential-storage-index.md +0 -190
  67. package/library/requirements/completed/prd-006-credential-storage/qa/.gitkeep +0 -0
  68. package/library/requirements/completed/prd-007-oauth-device-flows/prd-007-oauth-device-flows-index.md +0 -208
  69. package/library/requirements/completed/prd-007-oauth-device-flows/qa/.gitkeep +0 -0
  70. package/library/requirements/completed/prd-008-preferences-tiers-favorites/prd-008-preferences-tiers-favorites-index.md +0 -249
  71. package/library/requirements/completed/prd-008-preferences-tiers-favorites/qa/.gitkeep +0 -0
  72. package/library/requirements/completed/prd-009-codex-integration/prd-009-codex-integration-index.md +0 -212
  73. package/library/requirements/completed/prd-009-codex-integration/qa/.gitkeep +0 -0
  74. package/library/requirements/completed/prd-010-gemini-cli-integration/prd-010-gemini-cli-integration-index.md +0 -211
  75. package/library/requirements/completed/prd-010-gemini-cli-integration/qa/.gitkeep +0 -0
  76. package/library/requirements/completed/prd-011-claude-desktop-integration/prd-011-claude-desktop-integration-index.md +0 -228
  77. package/library/requirements/completed/prd-011-claude-desktop-integration/qa/.gitkeep +0 -0
  78. package/library/requirements/completed/prd-012-server-gateway/prd-012-server-gateway-index.md +0 -356
  79. package/library/requirements/completed/prd-012-server-gateway/qa/.gitkeep +0 -0
  80. package/library/requirements/in-work/README.md +0 -19
  81. package/library/requirements/reports/README.md +0 -31
  82. package/scripts/refresh-models-dev-cache.mjs +0 -34
  83. package/test-proxy.ts +0 -19
  84. package/test-split.js +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legioncodeinc/rflectr",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -24,6 +24,9 @@
24
24
  "vertex"
25
25
  ],
26
26
  "type": "module",
27
+ "files": [
28
+ "dist"
29
+ ],
27
30
  "bin": {
28
31
  "rflectr": "dist/cli.js"
29
32
  },
@@ -1,7 +0,0 @@
1
- {
2
- "ignorePatterns": [
3
- {
4
- "pattern": "^https://www\\.npmjs\\.com/package/"
5
- }
6
- ]
7
- }
package/AGENTS.md DELETED
@@ -1,169 +0,0 @@
1
- # AGENTS.md
2
-
3
- This file provides guidance to Codex (Codex.ai/code) when working with code in this repository. Note that the codebase supports Claude Code, OpenAI Codex, and Google Gemini CLI.
4
-
5
- ## Commands
6
-
7
- ```bash
8
- npm run build # compile TypeScript → dist/cli.js (via tsup, ESM, shebang injected)
9
- npm test # run all tests with vitest
10
- npm run typecheck # type-check without emitting (tsc --noEmit)
11
- npm run dev # watch mode build
12
-
13
- # Run a single test file
14
- npx vitest run tests/env.test.ts
15
- npx vitest run tests/models.test.ts
16
-
17
- # Test the CLI locally (already npm-linked)
18
- rflectr --help
19
- rflectr models # manage favorite models for mid-session switching
20
- rflectr Codex --dry-run # simulate full first-run without writing anything
21
- rflectr Codex --setup # re-ask subscription tier
22
- rflectr Codex --trace # write debug log to /tmp/rflectr-debug.log and print errors on exit
23
- rflectr server # foreground OpenCode/registry API gateway
24
- rflectr server --vertex # foreground Vertex AI gateway (gcloud ADC)
25
- rflectr codex # Codex CLI with registry providers (see library/knowledge/public/guides/codex.md)
26
- rflectr codex-app # Codex desktop app (macOS/Windows; see library/knowledge/public/guides/codex.md)
27
- rflectr gemini # Gemini CLI with registry providers (see library/knowledge/public/guides/gemini-cli.md)
28
-
29
- # Rebuild after code changes before testing manually
30
- npm run build && rflectr --version
31
- ```
32
-
33
- ## Architecture
34
-
35
- **Entry point:** `src/cli.ts` orchestrates the full flow. Every other module is a focused unit with no side effects at import time.
36
-
37
- **Data flow (`rflectr Codex`):**
38
- ```
39
- cli.ts
40
- → findClaudeBinary() [launch.ts — locate Codex binary]
41
- → fetchLocalProviders() [providers.ts — ephemeral opencode serve, GET /config/providers, normalize]
42
- → p.select "Which provider?" [shown when local providers are available]
43
-
44
- ── OpenCode cloud path (default) ──
45
- → resolveOrCollectApiKey() [reads env, OS credential store (all platforms), or prompts user]
46
- → askSubscriptionTier() [prompts.ts — one-time question, saved to conf store]
47
- → getModels() [models.ts — API fetch + cache enrichment + format classification]
48
- → runWizard() [prompts.ts — backend/model selector, filters unsupported]
49
-
50
- ── Local provider path ──
51
- → pickLocalModel() [prompts.ts — filter/select model from local provider]
52
-
53
- ── Shared launch (no favorites) ──
54
- → startProxy() [proxy.ts — single-model wrapper around startProxyCatalog]
55
- → buildChildEnv(baseUrl, …) [env.ts — removes 17 conflicting vars, sets OpenCode vars]
56
- → launchClaude() [launch.ts — spawn with stdio:inherit]
57
- → proxyHandle.close() [stops proxy after Codex exits]
58
-
59
- ── Switch-menu launch (favorites.length > 0) ──
60
- → buildCatalogRoutes() [catalog.ts — starting model + favorites, max 20]
61
- → startProxyCatalog() [proxy.ts — multi-route proxy, alias IDs per model]
62
- → buildChildEnv(…, gatewayDiscovery=true) [sets CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1]
63
- → launchClaudeViaCatalog() [cli.ts — shared launch + trace cleanup]
64
- ```
65
-
66
- **`rflectr models`:** Interactive favorites manager (`src/favorites.ts`). Reads/writes `favoriteModels` in config. Saves once on Done. Stale favorites (unavailable models) are silently skipped when building the catalog.
67
-
68
- **Catalog routing** (`src/catalog.ts`): `localModelToRoute`, `zenGoModelToRoute`, `makeRouteResolver`, `buildCatalogRoutes`. Routes built only for starting model + favorites — not the full model list. Alias IDs via `aliasModelId()` in proxy so Codex sees unique model names in `/model`.
69
-
70
- **Critical URL constraint:** `BACKENDS.baseUrl` in `constants.ts` must NOT include `/v1`. The Anthropic SDK appends `/v1/messages` automatically. Setting it to `https://opencode.ai/zen/v1` would cause requests to hit `/zen/v1/v1/messages` → 404.
71
-
72
- **Model discovery two-source merge:**
73
- - Primary: `GET {backendUrl}/v1/models` (no auth needed, returns available IDs)
74
- - Enrichment: `~/.cache/opencode/models.json` (written by OpenCode CLI) — provides `name`, `family`, `cost`, `provider.npm`
75
- - `isAnthropicNative`: true when `modelFormat === 'anthropic'`
76
- - `modelFormat`: classified from `provider.npm` in cache, or by ID-prefix heuristic:
77
- - `@ai-sdk/anthropic` or `Codex-*` → `'anthropic'` (direct passthrough)
78
- - `@ai-sdk/openai` or `gpt-*` → `'unsupported'` in the **cloud OpenCode wizard** (OpenCode Zen/Go proxy layer; not direct OpenAI). Use the **local OpenAI provider** instead for GPT models.
79
- - `@ai-sdk/google` or `gemini-*` → `'unsupported'` (needs model-specific endpoints)
80
- - Everything else → `'openai'` (routed through the SDK adapter via the local proxy)
81
- - `sourceBackend`: set from the backend that was queried — critical for `go` tier which shows Zen free models + Go paid models in one list, so the correct `ANTHROPIC_BASE_URL` can be set per selected model
82
-
83
- **Translation layer — the Vercel AI SDK adapter** (`src/sdk-adapter.ts` + `src/provider-factory.ts`): All non-Anthropic providers route through the Vercel AI SDK (`ai` + `@ai-sdk/*`, the same packages OpenCode loads), which owns wire format, endpoint selection, and provider quirks. This is the **single** translation path — there is no hand-rolled per-provider translation.
84
-
85
- - **`provider-factory.ts`** — `createLanguageModel({ npm, modelId, apiKey, baseURL })` (async) maps whatever `api.npm` OpenCode assigns to an SDK `LanguageModel` via dynamic `import(npm)` + `create*` factory discovery. Special branches for OpenAI/xAI Responses API selection and openai-compatible/openrouter base URLs. `isSdkMigratedNpm(npm)` is true for any npm except `@ai-sdk/anthropic`. `modelPrefersResponsesApi(modelId)` selects `provider.responses(id)` over `provider.chat(id)` for OpenAI/xAI models that require the Responses API (GPT-5.4+, GPT-5.5, `*-codex`, o-series, xAI `*-multi-agent`). OpenCode's bundled SDK provider packages ship as npm `dependencies` (externalized in tsup, loaded on demand).
86
- - **`sdk-adapter.ts`** — Anthropic `/v1/messages` ↔ SDK, one turn per request (Codex owns the tool loop). `translateRequest(body, npm)` builds the SDK call params (messages, tools, tool_choice, system) and folds inline `role:'system'` messages — Codex injects the skills list / system-reminders this way — into the system prompt so they aren't dropped. `streamAnthropicResponse` maps the SDK `fullStream` to Anthropic SSE; `generateAnthropicResponse` handles non-streaming. `thought_signature` round-trips: encoded into the Anthropic `tool_use.id` as `{id}::ts::{signature}` and decoded back into `providerOptions.google.thoughtSignature` (Gemini puts the signature on the tool-call parts, captured at `tool-input-start`). The SDK handles Gemini's strict `thought_signature` echo-back correctly — the reason a hand-rolled Gemini-native path used to be required.
87
-
88
- **Local proxy** (`src/proxy.ts`): a local HTTP server on `127.0.0.1:<random-port>` that accepts Anthropic-format requests at `/v1/messages` and dispatches per route (`startProxyCatalog`/`startProxy`): `modelFormat === 'anthropic'` → direct passthrough to the provider's Anthropic endpoint; otherwise → `isSdkMigratedNpm(route.npm)` → the SDK adapter. Each `ProxyRoute` carries `npm` + `baseURL`. `GET /v1/models` returns a synthetic catalog including `context_window` per model (via `formatAnthropicModelEntry` / `resolveContextWindow`) so Codex's status bar shows accurate remaining context. `aliasModelId()` rewrites non-`Codex-*` ids to `anthropic-{provider}__{id}` so gateway model discovery accepts them.
89
-
90
- **Subscription tiers** control which models are shown and whether a backend selector appears:
91
- - `free` / `zen`: always Zen backend, no backend selector
92
- - `go`: Go backend, but also fetches Zen for free models — combined list, backend inferred from `sourceBackend` of selected model
93
- - `both`: shows backend selector
94
-
95
- **Env isolation:** `buildChildEnv()` copies `process.env`, deletes all 17 vars in `CONFLICTING_ENV_VARS`, then sets `ANTHROPIC_BASE_URL`, `ANTHROPIC_API_KEY`, `ANTHROPIC_MODEL`. `launchClaude()` also passes `--model`. Isolation applies to the child process only — the parent shell is not mutated (except `OPENCODE_API_KEY` during key setup). Codex may persist the model to `~/.Codex/settings.json` independently; that is outside rflectr's control.
96
-
97
- **Preferences** (at `~/.rflectr/config.json`, migrated from legacy `conf` path on first read): `lastBackend`, `lastModel`, `lastProvider`, `recentModelsByProvider`, `favoriteModels`, `subscriptionTier`, and a 1-hour model list cache. Override path with `RFLECTR_HOME`. All writes are skipped when `dryRun === true`.
98
-
99
- **API key storage** uses `@napi-rs/keyring` (installed as `optionalDependencies`) for cross-platform credential store access. The module is loaded via dynamic `import()` so a missing native binary degrades gracefully. `tsup.config.ts` marks `@napi-rs/keyring` and all `@ai-sdk/*` provider packages as `external` so they resolve from `node_modules` at runtime (keeps `dist/cli.js` small).
100
-
101
- On startup, `resolveOrCollectApiKey()` silently calls `readFromCredentialStore()` — if a key is found the prompt is skipped entirely.
102
-
103
- Save options per platform:
104
- - **macOS** (4 options): Keychain only | Keychain + `~/.zshrc` auto-load | shell profile (plaintext) | session only
105
- - The `~/.zshrc` auto-load line uses the `security` CLI directly (so the shell can source it): `export OPENCODE_API_KEY="$(security find-generic-password -s rflectr -a rflectr -w 2>/dev/null)"`
106
- - **Windows** (3 options): Windows Credential Manager | `setx` user env var (plaintext) | session only
107
- - `setx` is called with `stdio: ['pipe','pipe','pipe']` to suppress its "SUCCESS" stdout
108
- - **Linux desktop** (3 options): Secret Service (GNOME Keyring / KWallet) | shell profile (plaintext) | session only
109
- - Secret Service availability is probed via a test `getPassword()` call — returns false if the daemon isn't running
110
- - **Linux headless** (2 options): shell profile | session only — shown with a `p.log.info` note explaining why secure storage is unavailable
111
-
112
- In all cases `process.env['OPENCODE_API_KEY']` is set immediately so the key is active for the current session regardless of save choice.
113
-
114
- **Local provider discovery** (`src/providers.ts`): `fetchLocalProviders()` spawns `opencode serve --port 0`, waits for the listening URL in stdout/stderr (10s timeout, spinner shown in CLI), fetches `GET /config/providers`, then kills the process. `normalizeProviders()` (called internally) skips OAuth providers (empty key), and classifies each model via `resolveEndpoint(npm, apiUrl)`: `@ai-sdk/anthropic` → passthrough; `@ai-sdk/openai-compatible` without `api.url` → skip; any other non-empty `api.npm` → SDK adapter (`format: 'openai'`). OpenCode is the source of truth for which providers/models appear — rflectr does not maintain a per-package allowlist. Each model captures `api.npm`, `api.url` (`apiBaseUrl`), and `api.id` (`upstreamModelId` for SDK/upstream calls; catalog `id` stays for Codex's picker). Cost display in Codex is inaccurate for non-Anthropic models (Codex applies its own pricing table); documented limitation.
115
-
116
- **Local provider routing:** Two paths depending on `model.modelFormat`:
117
- - `'anthropic'`: `buildChildEnv(model.baseUrl, model.id, provider.apiKey)` — no proxy, Codex talks directly to the provider's Anthropic-compatible endpoint. The `baseUrl` must NOT include `/v1` (the Anthropic SDK appends it).
118
- - `'openai'`: `startProxy(model.completionsUrl ?? '', model.id, trace, contextWindow, { npm, baseURL, upstreamModelId })` — SDK adapter proxy on a random local port; `buildChildEnv('http://127.0.0.1', model.id, provider.apiKey, proxyPort)`. The route's `npm` selects the SDK provider via dynamic import; `baseURL` (`api.url`) is used for openai-compatible / openrouter providers. `completionsUrl` is optional for SDK-first-party packages (SDK owns endpoints).
119
-
120
- **Providers that need a non-empty API key:** `normalizeProviders` skips any provider with an empty `key` field (to filter OAuth-only providers like OpenAI/xAI configured via browser login). Local providers that don't validate keys (e.g. Ollama) must still have a non-empty placeholder key set in OpenCode (e.g. `"ollama"`).
121
-
122
- **Server command local providers** (`src/server/index.ts`): `loadServerModels()` fetches the provider catalog via `fetchProviderCatalog({ agent: 'server' })` and converts all registry providers to `ServerModelInfo[]` via `localProvidersToServerModels`. The router (`src/server/router.ts`) `handleAnthropicMessages`: anthropic-format → forward raw to `{baseUrl}/v1/messages`; openai-format → `isSdkMigratedNpm(npm)` guard → `createLanguageModel` + `streamAnthropicResponse`/`generateAnthropicResponse` (same SDK adapter as the CLI proxy). `GET /models` strips `apiKey` from output. Spinner shows `"N models (M from registry providers)"`.
123
-
124
- **Stale free models:** `STALE_FREE_MODELS` in `constants.ts` contains models whose free promotion ended but the API still returns them. Currently only `qwen3.6-plus-free`. These are filtered out in `mergeModels()`.
125
-
126
- **Recent models per provider** (`src/prompts.ts`, `src/cli.ts`, `src/types.ts`, `src/config.ts`): `UserPreferences.recentModelsByProvider: Record<string, string[]>` stores up to 3 recently used model IDs per provider. `pickLocalModel()` shows them at the top of the picker with a `'recent'` hint, plus a "Browse all models →" option. On launch, `cli.ts` prepends the selected model id and saves back (deduped, max 3). Skipped on `--dry-run`.
127
-
128
- **Large catalog UX** (`src/prompts.ts`): `MODEL_SEARCH_THRESHOLD = 25` — lists above this show search or paginated browse. `MODEL_PAGE_SIZE = 15` — prev/next pagination. `selectModelWithSearch`, `selectLargeCatalog`, `pickModelFromPagedList`.
129
-
130
- **Shared upstream forwarding** (`src/upstream-forward.ts`): `relayAnthropicMessages`, `postJsonUpstream`, anthropic header helpers — used by `proxy.ts` and `server/router.ts`.
131
-
132
- **Provider catalog helpers** (`src/provider-catalog.ts`): `fetchProviderCatalog`, `resolveLocalProviders`, `providersForPicker`, `localProvidersToServerModels`, `resolveProvidersForDisplay`, `formatRegistryAuthLabel` — registry-first catalog resolution used by CLI, server, and providers command.
133
-
134
- **Tests** cover pure functions: `env.ts`, `models.ts`, `sdk-adapter.ts`, `provider-factory.ts`, `proxy.ts` (`aliasModelId`), `providers.ts`, `catalog.ts`, `favorites.ts`, `prompts.ts`, `upstream-forward.ts`, `config.ts`, `tool-search.ts`, `cli.ts` (help text), server modules. Interactive launch flow and real-provider behavior verified manually.
135
-
136
- ## Key constraints
137
-
138
- - `settings.json` is never touched by rflectr. Launch config is env-var-only, passed to the child process (plus `--model`). This avoids the backup/restore problem that `ollama launch Codex` has. **Caveat:** Codex itself persists the launched model to `~/.Codex/settings.json`, so bare `Codex` later may still show an rflectr alias (e.g. `anthropic-opencode-go__deepseek-v4-flash`). Gateway discovery caches at `~/.Codex/cache/gateway-models.json`. Reset with `Codex --model sonnet` or by editing/removing those files.
139
- - `--dry-run` ignores all saved state (env key, Keychain, tier, preferences) and skips all writes. Used to simulate a fresh first-run experience.
140
- - When adding a new backend, update `BACKENDS` in `constants.ts`, the `BackendConfig` id union in `types.ts`, and the subscription tier logic in `prompts.ts` and `cli.ts`.
141
- - `buildChildEnv(baseUrl: string, model, apiKey, proxyPort?)` — takes a plain string URL, not a `BackendConfig`. When `proxyPort` is set, `ANTHROPIC_BASE_URL` is always `http://127.0.0.1:{proxyPort}` regardless of `baseUrl`.
142
- - `startProxy(completionsUrl, modelId, debug, contextWindow?, sdk?)` — single-model wrapper around `startProxyCatalog`; `sdk` carries `{ npm, baseURL }` to select the SDK provider.
143
- - `startProxyCatalog(routes, startingAliasId, debug)` — multi-route catalog proxy for switch-menu sessions.
144
- - `MAX_MODEL_CATALOG = 20` in `constants.ts` — favorites cap and max routes in catalog.
145
-
146
- **Codex favorites catalog:** When `prefs.favoriteModels.length > 0`, `rflectr codex` and `rflectr codex-app` enter favorites mode on launch:
147
- - Shared resolver (`src/favorites-resolver.ts`) resolves each favorite to a `{providerId, providerName, model, apiKey}` entry, filtering by `agent: 'codex'` blacklist.
148
- - Codex CLI builds a `CodexProxyRoute[]` from resolved entries and starts a single multi-route proxy (`startCodexProxy(routes, { requireAuth: true })`).
149
- - The proxy port is exposed to the child via `OPENAI_API_KEY=proxy-local`.
150
- - Catalog slugs are `${providerId}__${modelId}` (CLI) or `codexAppModelSlug(modelId)` (App).
151
- - `--restore` globs `models-*.json` (CLI) and `app-models-*.json` (App); the new files are `models-favorites.json` and `app-models-favorites.json`.
152
- - Zen/Go favorites are skipped in Codex (use Claude or Desktop gateway).
153
-
154
- ## Release status (v0.3.0)
155
-
156
- Current version is **v0.3.0** — official launch release with the native provider registry, complete Claude/Codex app help, unified OpenCode Zen / Go setup, duplicate-provider migration, stable post-import refreshes, agent boot flags (`--provider` / `--model`), `rflectr --ai`, favorites catalogs, reasoning capability metadata, and new native Chinese provider templates (DeepSeek, Zhipu, Moonshot) with improved models endpoint routing.
157
-
158
- **Known limitations (by design):**
159
- - Cost display in Codex is always inaccurate for non-Anthropic models.
160
- - OAuth-authenticated providers (no stored key) are silently skipped.
161
- - `@ai-sdk/github-copilot` won't work — OpenCode loads it from internal `@opencode-ai/core`, not a public npm factory we can ship.
162
- - Bedrock/Azure/Vertex may need env-based auth beyond a simple `apiKey` forwarded from OpenCode.
163
- - Providers with custom auth mechanisms (e.g. Azure OpenAI with deployment URLs) are not fully supported.
164
- - The `::ts::` separator in tool_use ids encodes `thought_signature`; would break if a signature ever literally contained `::ts::`. Extremely unlikely.
165
- - In switch-menu (gateway-discovery) mode the displayed context window reflects the **launch** model and does NOT update on live `/model` switch. Codex's gateway model discovery only carries `id` + `display_name` (no `context_window`) and fetches `/v1/models` once at startup, so `CLAUDE_CODE_MAX_CONTEXT_TOKENS` (fixed at launch) is the only lever. Single-model launches show the correct window.
166
-
167
- **Provider quirks (documented from testing):**
168
- - **Mistral free tier:** strict API rate limits (HTTP 429, code `1300`). Tool-heavy Codex sessions burn quota quickly (parallel title-generation requests, Skill injection, multi-turn tool loops). The SDK handles Mistral message ordering; throttling is unaffected.
169
- - **OpenAI direct (`@ai-sdk/openai` local provider):** newer models (GPT-5.4+, GPT-5.5, `*-codex`, o-series) require the Responses API — `provider-factory.modelPrefersResponsesApi()` selects `openai.responses(id)` for them, `openai.chat(id)` otherwise. OpenCode catalog IDs may differ from upstream API IDs — `upstreamModelId` uses OpenCode's `api.id` (e.g. `gpt-5.5-fast` → `gpt-5.5`). GPT-5.5 reasoning round-trips via encrypted content in `thinking.signature`. Cloud OpenCode Zen/Go GPT models remain hidden in the wizard (`unsupported`); use the local OpenAI provider for GPT access.
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -1,14 +0,0 @@
1
- [
2
- {
3
- "id": "claude-sonnet-4-6",
4
- "display_name": "Claude Sonnet 4.6"
5
- },
6
- {
7
- "id": "claude-opus-4-6",
8
- "display_name": "Claude Opus 4.6"
9
- },
10
- {
11
- "id": "claude-haiku-4-5",
12
- "display_name": "Claude Haiku 4.5"
13
- }
14
- ]
package/library/README.md DELETED
@@ -1,39 +0,0 @@
1
- ---
2
- ai_description: |
3
- This is the root of the repository's documentation library (schema v2).
4
- You own everything under library/ except notes/, which is human-only.
5
- Sub-trees: knowledge/ (public and private docs), requirements/ (product
6
- work: PRDs), issues/ (reactive bug/incident work: IRDs), notes/ (junk
7
- drawer, read-only to agents).
8
- Schema reference: legion-shared/standards/library-schema-v2.md.
9
- Standardize script: pnpm standardize-library --repository <name>.
10
- human_description: |
11
- Root of this repository's documentation library.
12
- - knowledge/: reference documentation split by audience (public vs private)
13
- - requirements/: planned product work (PRDs) with backlog/in-work/completed lifecycle
14
- - issues/: reactive bug and incident work (IRDs) with same lifecycle
15
- - notes/: unstructured scratch space — only humans write here
16
- Run `pnpm standardize-library --repository <name>` to scaffold any missing structure.
17
- ---
18
-
19
- # Library
20
-
21
- Documentation root for this repository. Schema version: **v2**.
22
-
23
- See [`legion-shared/standards/library-schema-v2.md`](../../legion-shared/standards/library-schema-v2.md) for the full specification.
24
-
25
- ## Top-level layout
26
-
27
- | Folder | What goes here |
28
- |---|---|
29
- | `knowledge/public/` | End-user / customer-facing docs: overviews, guides, FAQs |
30
- | `knowledge/private/` | Internal engineering and business docs: ADRs, standards, domain knowledge |
31
- | `requirements/` | Product and feature work: PRDs in backlog/in-work/completed |
32
- | `issues/` | Reactive bug and incident work: IRDs in backlog/in-work/completed |
33
- | `notes/` | Human-only scratch space |
34
-
35
- ## What does NOT belong here
36
-
37
- - Brand assets → `legion-shared/brands/`
38
- - Wiki entity pages → `legion-wiki/<repo>/wiki/` (derived, never edit)
39
- - Library mirrors → `legion-wiki/<repo>/library/` (derived, never edit)
@@ -1,46 +0,0 @@
1
- ---
2
- ai_description: |
3
- This folder contains all reactive bug and incident work (IRDs).
4
- It is a PEER of requirements/, not nested under it.
5
- Sub-folders: backlog/, in-work/, completed/ — same lifecycle as requirements/.
6
- IRD folder naming: ird-<###>-<kebab-slug>/
7
- IRD numbers match the GitHub issue number for this repo.
8
- Never invent IRD numbers — a GitHub issue must exist first.
9
- IRDs are single-scope: one issue per IRD, no sub-IRDs.
10
- Do NOT put PRDs here — those go in requirements/.
11
- human_description: |
12
- Reactive bug and incident work (IRDs), organized by lifecycle stage.
13
- - backlog/: tracked issues with a fix plan, not yet started
14
- - in-work/: issues currently being fixed
15
- - completed/: resolved issues (move entire folder)
16
- IRD numbers match GitHub issue numbers. Create an IRD only after the
17
- GitHub issue exists.
18
- ---
19
-
20
- # Issues
21
-
22
- Reactive bug and incident work (IRDs), organized by lifecycle state.
23
-
24
- ## Sub-folders
25
-
26
- | Folder | State | Description |
27
- |---|---|---|
28
- | `backlog/` | Tracked | IRDs with a fix plan, not yet in progress |
29
- | `in-work/` | Active | Issues currently being resolved |
30
- | `completed/` | Resolved | Entire IRD folder moves here when the issue closes |
31
-
32
- ## IRD folder structure
33
-
34
- ```
35
- ird-042-stale-cache/
36
- ird-042-stale-cache-index.md single-scope fix plan
37
- qa/
38
- ird-042-stale-cache-qa.md QA audit (written by quality-guardian)
39
- ```
40
-
41
- ## Naming rules
42
-
43
- - Folder: `ird-<###>-<kebab-slug>/`
44
- - Index: `ird-<###>-<kebab-slug>-index.md`
45
- - IRD number = GitHub issue number (never invented)
46
- - No sub-IRDs (scope one issue per IRD)
@@ -1,26 +0,0 @@
1
- ---
2
- ai_description: |
3
- Contains IRD folders for tracked issues not yet in active fix work.
4
- Create a new IRD here only AFTER the GitHub issue exists for this repo.
5
- IRD folder: ird-<###>-<slug>/ where ### = GitHub issue number.
6
- Must contain: ird-<###>-<slug>-index.md (the fix plan) and qa/ folder.
7
- IRDs are single-scope: do not add sub-IRDs.
8
- human_description: |
9
- IRDs planned but not yet in active fix work. Create IRDs here.
10
- - Naming: ird-042-stale-cache/ with ird-042-stale-cache-index.md inside
11
- - IRD number must match the GitHub issue number
12
- - Create only after the GitHub issue exists
13
- Move to in-work/ when fix work begins.
14
- ---
15
-
16
- # Issues — Backlog
17
-
18
- Tracked issues with a fix plan, not yet in active resolution.
19
-
20
- ## Creating a new IRD
21
-
22
- 1. Confirm the GitHub issue number (e.g., #42).
23
- 2. Create `ird-042-<kebab-slug>/`.
24
- 3. Create `ird-042-<slug>-index.md` — the single-scope fix plan.
25
- 4. Create `qa/` subfolder (empty; `quality-guardian` writes here).
26
- 5. No sub-IRDs — keep scope to one issue.
@@ -1,13 +0,0 @@
1
- ---
2
- ai_description: |
3
- Resolved IRD folders. Entire ird-<###>-<slug>/ folders move here from
4
- in-work/ when the corresponding GitHub issue is closed and verified.
5
- Read-only after landing — do NOT edit or re-open IRDs here.
6
- human_description: |
7
- Resolved issue folders. Move entire ird-NNN-slug/ here from in-work/
8
- when the GitHub issue is closed and the fix is confirmed. Read-only.
9
- ---
10
-
11
- # Issues — Completed
12
-
13
- Resolved IRD folders. Entire `ird-<###>-<slug>/` folders land here when the GitHub issue closes and the fix is confirmed. Do not edit files here after landing.
@@ -1,13 +0,0 @@
1
- ---
2
- ai_description: |
3
- IRD folders actively being resolved. Mirror of requirements/in-work/
4
- but for issues. Move entire ird-<###>-<slug>/ folder from backlog/
5
- here when fix work begins, then to completed/ when the issue closes.
6
- human_description: |
7
- IRDs currently being fixed. Move folder from backlog/ here when work
8
- starts, and to completed/ when the GitHub issue is closed.
9
- ---
10
-
11
- # Issues — In Work
12
-
13
- IRDs currently being resolved. Move from `backlog/` → here when fix work starts, then `completed/` when the GitHub issue closes.
@@ -1,34 +0,0 @@
1
- ---
2
- ai_description: |
3
- This folder contains all reference documentation for this repository,
4
- split by intended audience: public/ for end-users, private/ for internal
5
- team and AI agents. When filing a new doc, default to private/. Promote
6
- to public/ only when the content is intentionally customer-facing.
7
- Allowed writes: knowledge/public/<domain>/<slug>.md and
8
- knowledge/private/<domain>/<slug>.md. ADRs always go in
9
- knowledge/private/architecture/ADR-<n>-<slug>.md.
10
- Never write to knowledge/ itself (write to the sub-folders).
11
- human_description: |
12
- Reference documentation split by audience.
13
- - public/: docs that will eventually be surfaced to customers or published
14
- - private/: internal engineering, architecture, business, and strategy docs
15
- When adding a new doc, pick the right subdomain folder inside public/ or
16
- private/. If the domain doesn't exist yet, create it.
17
- ---
18
-
19
- # Knowledge
20
-
21
- Reference documentation for this repository, organized by audience.
22
-
23
- ## Sub-folders
24
-
25
- | Folder | Audience | Typical content |
26
- |---|---|---|
27
- | `public/` | End-users, customers, external | Overviews, user guides, FAQs |
28
- | `private/` | Internal team + AI agents | ADRs, standards, architecture, domain engineering docs |
29
-
30
- ## Decision rule: public vs private
31
-
32
- > "Would I publish this on a help center or product docs site?"
33
-
34
- Yes → `public/`. No → `private/`. When in doubt, `private/`.
@@ -1,40 +0,0 @@
1
- ---
2
- ai_description: |
3
- This folder contains internal engineering and business documentation.
4
- ADRs MUST live in architecture/ADR-<n>-<kebab-slug>.md.
5
- Engineering standards MUST live in standards/documentation-framework.md.
6
- Other domain folders (<domain>/) are repo-specific and may be created as
7
- needed (ai/, auth/, data/, frontend/, infrastructure/, integrations/,
8
- marketing/, operations/, personas/, reporting/, roadmap/, scanners/,
9
- security/, strategy/, etc.).
10
- Do NOT file customer-facing content here (that goes in knowledge/public/).
11
- Write path: library/knowledge/private/<domain>/<kebab-slug>.md.
12
- human_description: |
13
- Internal engineering and business documentation.
14
- - architecture/: Architecture Decision Records (ADRs)
15
- - standards/: Documentation framework and coding standards
16
- - <domain>/: Any repo-specific knowledge domain (ai/, auth/, data/, etc.)
17
- Default landing zone for any doc that does not need to be customer-facing.
18
- When creating a new domain folder, add a README.md explaining what belongs.
19
- ---
20
-
21
- # Knowledge — Private
22
-
23
- Internal documentation for engineers, product, and AI agents.
24
-
25
- ## Required sub-folders (always present)
26
-
27
- | Folder | Contents |
28
- |---|---|
29
- | `architecture/` | ADRs: `ADR-<n>-<kebab-slug>.md`. Locked decisions with context, alternatives, consequences. |
30
- | `standards/` | `documentation-framework.md` and any repo-specific writing rules. |
31
-
32
- ## Optional domain folders
33
-
34
- Create any of these as needed: `ai/`, `auth/`, `data/`, `frontend/`, `infrastructure/`, `integrations/`, `marketing/`, `operations/`, `personas/`, `reporting/`, `roadmap/`, `scanners/`, `security/`, `strategy/`, `reference/`, `<product>-ux-ui/`.
35
-
36
- ## What does NOT belong here
37
-
38
- - Customer-facing content (put in `knowledge/public/`)
39
- - PRDs or IRDs (put in `requirements/` or `issues/`)
40
- - Brand assets (put in `legion-shared/brands/`)
@@ -1,8 +0,0 @@
1
- # AI
2
-
3
- The model-routing brain: wire-format translation and model discovery/classification.
4
-
5
- | Doc | Covers |
6
- |---|---|
7
- | [`translation-layer.md`](translation-layer.md) | The Vercel AI SDK adapter + provider factory — the single translation path; Responses-API selection; thought_signature round-trip. |
8
- | [`model-discovery-classification.md`](model-discovery-classification.md) | `classifyModelFormat`, the two-source merge, context-window resolution, cost-display limitation. |
@@ -1,81 +0,0 @@
1
- # Model Discovery & Classification
2
-
3
- > Category: Ai | Version: 1.0 | Date: June 2026 | Status: Active
4
-
5
- How `rflectr` builds the model list a user picks from, and how it decides whether each model is forwarded raw or translated. Read [`translation-layer.md`](translation-layer.md) for what happens *after* a model is classified.
6
-
7
- **Related:**
8
- - [`translation-layer.md`](translation-layer.md)
9
- - [`../data/provider-registry.md`](../data/provider-registry.md)
10
- - Source: `src/constants.ts` (`classifyModelFormat`), `src/models.ts`, `src/context-window.ts`, `src/context-model-id.ts`, `src/registry/materialize.ts`
11
-
12
- ---
13
-
14
- ## The format decision
15
-
16
- Every model carries a `modelFormat` that drives the launch branch (`'anthropic'` = direct passthrough, anything else = SDK adapter proxy). It is computed by `classifyModelFormat(modelId, providerNpm)` in `src/constants.ts`:
17
-
18
- ```ts
19
- if (providerNpm === '@ai-sdk/anthropic') return 'anthropic';
20
- if (providerNpm === '@ai-sdk/openai') return 'unsupported';
21
- if (providerNpm === '@ai-sdk/google') return 'unsupported';
22
- // Fallback: ID-prefix heuristics when no cache npm is known
23
- if (id.startsWith('claude-')) return 'anthropic';
24
- if (id.startsWith('gpt-')) return 'unsupported';
25
- if (id.startsWith('gemini-')) return 'unsupported';
26
- return 'openai';
27
- ```
28
-
29
- The four values mean:
30
-
31
- | `modelFormat` | Meaning |
32
- |---|---|
33
- | `anthropic` | Direct passthrough to the provider's Anthropic endpoint. `isAnthropicNative` is true. |
34
- | `openai` | Routed through the SDK adapter via the local proxy. The catch-all for everything that isn't natively Anthropic. |
35
- | `unsupported` | Hidden in the **cloud OpenCode wizard** only. GPT/Gemini through OpenCode Zen/Go's proxy layer needs model-specific endpoints that the cloud path can't provide. |
36
-
37
- > **Important nuance:** `unsupported` is a *cloud-wizard* restriction, not a global one. To use GPT or Gemini models, configure the **local OpenAI / Google provider** (which carries the real `@ai-sdk/openai` / `@ai-sdk/google` npm) — those route through the SDK adapter normally. The `unsupported` classification only blocks the OpenCode Zen/Go proxy layer where direct OpenAI/Google access isn't available.
38
-
39
- ---
40
-
41
- ## The two-source merge
42
-
43
- The cloud (OpenCode Zen/Go) model list is built from two sources merged together:
44
-
45
- ```mermaid
46
- flowchart TD
47
- api["GET {backendUrl}/v1/models — available ids (no auth)"]
48
- cache["~/.cache/opencode/models.json — name, family, cost, provider.npm"]
49
- api --> merge["mergeModels()"]
50
- cache --> merge
51
- merge --> enrich["enriched ModelInfo with modelFormat, sourceBackend, contextWindow"]
52
- ```
53
-
54
- - **Primary:** `GET {backendUrl}/v1/models` returns the available model ids (no auth required).
55
- - **Enrichment:** `~/.cache/opencode/models.json` (written by the OpenCode CLI, path in `OPENCODE_CACHE_PATH`) supplies `name`, `family`, `cost`, and `provider.npm`. It is optional enrichment, never a runtime dependency.
56
-
57
- `sourceBackend` is set from the backend that was queried. This matters for the `go` subscription tier, which shows Zen free models *and* Go paid models in one combined list — `sourceBackend` lets the launcher set the correct `ANTHROPIC_BASE_URL` per selected model.
58
-
59
- ### Stale free models
60
-
61
- `STALE_FREE_MODELS` in `src/constants.ts` lists models whose free promotion ended but the API still returns. They are filtered out in `mergeModels()`. (Historically this held `qwen3.6-plus-free`; treat the constant as the source of truth.)
62
-
63
- ---
64
-
65
- ## Registry models
66
-
67
- Registry providers (`~/.rflectr/providers.json`) carry their own `CachedModel[]`, each already stamped with `modelFormat`, `npm`, `upstreamModelId`, `contextWindow`, `cost`, `supportedParameters`, and `reasoning`. `materializeRegistry` (`src/registry/materialize.ts`) converts those into runtime `LocalProviderModel`s and applies per-agent hiding via `shouldHideModel()` — e.g. Zen/Go favorites are hidden from Codex, which has no gateway path for them. See [`../data/provider-registry.md`](../data/provider-registry.md).
68
-
69
- ---
70
-
71
- ## Context window resolution
72
-
73
- The status bar in Claude Code shows remaining context, which depends on a correct window. `resolveContextWindow(modelId, contextWindow?)` (`src/context-window.ts`) picks the window; `buildChildEnv` writes it to `CLAUDE_CODE_MAX_CONTEXT_TOKENS`. The proxy's synthetic `GET /v1/models` includes `context_window` per model (`formatAnthropicModelEntry`) so the host can render it.
74
-
75
- A model id may carry a `[1m]` suffix to denote a 1-million-token context variant; `stripOneMContextSuffix` / `claudeCodeClientModelId` (`src/context-model-id.ts`) separate the wire id from the display id and the context hint. In switch-menu mode the window is fixed at launch and does not change on live `/model` switch (see [`../architecture/launch-flow-claude.md`](../architecture/launch-flow-claude.md#the-context-window-caveat)).
76
-
77
- ---
78
-
79
- ## Cost display is inaccurate for non-Anthropic models
80
-
81
- Claude Code applies its own internal pricing table to whatever model id it sees, so the cost it shows for a Groq/DeepSeek/Gemini model is wrong. This is a documented, unfixable-from-here limitation — the host owns its pricing display.