-
Notifications
You must be signed in to change notification settings - Fork 35
feat: enhance user tier management and model access: #257
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,9 @@ import chalk from "chalk" | |
| import { theme, heavyDivider, CARET } from "src/cli/utils/tui.ts" | ||
| import { type ModelProvider, providerMeta } from "src/cli/ai/provider.ts" | ||
| import { getCliConfig, saveCliConfig, saveProviderApiKey, getByokSessionKey } from "src/lib/cli-config.ts" | ||
| import { getStoredToken } from "src/lib/token" | ||
| import { getCurrentUser } from "src/lib/api-client" | ||
| import prisma from "src/lib/prisma" | ||
|
|
||
| interface ModelEntry { | ||
| value: string | ||
|
|
@@ -13,6 +16,33 @@ interface ModelEntry { | |
| desc: string | ||
| } | ||
|
|
||
| // ── Tier detection ────────────────────────────────────────────── | ||
| export type UserTier = "spark" | "spark-premium" | "pro" | "ultra" | "none" | ||
|
|
||
| async function getUserTier(): Promise<UserTier> { | ||
| try { | ||
| const token = await getStoredToken() | ||
| if (!token?.access_token) return "none" | ||
|
|
||
| const result = await getCurrentUser() | ||
| if (!result.ok) return "none" | ||
|
|
||
| const subscription = await prisma.subscription.findFirst({ | ||
| where: { | ||
| userId: result.user.id, | ||
| status: { in: ["active", "trialing"] }, | ||
| }, | ||
| include: { plan: true }, | ||
| orderBy: { createdAt: "desc" }, | ||
| }) | ||
|
|
||
| if (!subscription?.plan) return "none" | ||
| return subscription.plan.tier as UserTier | ||
| } catch { | ||
| return "none" | ||
| } | ||
| } | ||
|
|
||
| const SECTION_CLOUD = "__section_cloud__" | ||
| const SECTION_BYOK = "__section_byok__" | ||
| const SECTION_CONCENTRATEAI = "__section_concentrateai__" | ||
|
|
@@ -22,15 +52,13 @@ const SECTION_MINIMAX = "__section_minimax__" | |
| const SECTION_NVIDIA = "__section_nvidia__" | ||
| const SECTION_OPENROUTER = "__section_openrouter__" | ||
| const SECTION_ORCAROUTER = "__section_orcarouter__" | ||
| const SECTION_CLOUD_PREMIUM = "__section_cloud_premium__" | ||
|
|
||
| export const ALL_SECTIONS = new Set([ | ||
| SECTION_CLOUD, SECTION_BYOK, | ||
| SECTION_CONCENTRATEAI, SECTION_MERGEDEV, | ||
| SECTION_GOOGLE, SECTION_MINIMAX, | ||
| SECTION_NVIDIA, SECTION_OPENROUTER, | ||
| SECTION_ORCAROUTER, | ||
| SECTION_CLOUD_PREMIUM, | ||
| ]) | ||
|
|
||
| const SECTION_LABELS: Record<string, string> = { | ||
|
|
@@ -43,21 +71,24 @@ const SECTION_LABELS: Record<string, string> = { | |
| [SECTION_NVIDIA]: "NVIDIA NIM", | ||
| [SECTION_OPENROUTER]: "OpenRouter", | ||
| [SECTION_ORCAROUTER]: "OrcaRouter", | ||
| [SECTION_CLOUD_PREMIUM]: "Supercode Cloud Premium", | ||
| } | ||
|
|
||
| const isMainSection = (v: string) => v === SECTION_CLOUD || v === SECTION_BYOK || v === SECTION_CLOUD_PREMIUM | ||
| const isMainSection = (v: string) => v === SECTION_CLOUD || v === SECTION_BYOK | ||
|
|
||
| // Models available through the Supercode cloud proxy (no API key needed) | ||
| export const CLOUD_MODELS: ModelEntry[] = [ | ||
| { value: "deepseek-v4-flash", label: "DeepSeek V4 Flash", provider: "supercode", cost: "free", desc: "Fast & capable" }, | ||
| // { value: "glm-5.2", label: "GLM 5.2", provider: "supercode", cost: "free", desc: "Latest GLM" }, | ||
| // { value: "glm-5.1", label: "GLM 5.1", provider: "supercode", cost: "free", desc: "Stable & reliable" }, | ||
| // { value: "kimi-k2-6", label: "Kimi K2.6", provider: "supercode", cost: "free", desc: "Long context" }, | ||
| // { value: "minimax-m3", label: "MiniMax M3", provider: "supercode", cost: "free", desc: "Fast & smart" }, | ||
| { value: "hy3", label: "Hunyuan Hy3", provider: "supercode", cost: "free", desc: "Tencent flagship" }, | ||
| { value: "mimo-v2.5", label: "Mimo v2.5", provider: "supercode", cost: "free", desc: "Novita" }, | ||
| // { value: "fireworks/nemotron-3-ultra-nvfp4", label: "Nemotron 3 Ultra NVFP4", provider: "supercode", cost: "free", desc: "Fireworks" }, | ||
| { value: "deepseek-v4-flash", label: "DeepSeek V4 Flash", provider: "supercode", cost: "", desc: "Fast & capable" }, | ||
| { value: "kimi-k2-6", label: "Kimi K2.6", provider: "supercode", cost: "", desc: "Long context" }, | ||
| { value: "kimi-k2-7-code", label: "Kimi K2.7 Code", provider: "supercode", cost: "", desc: "Code specialist" }, | ||
| { value: "kimi-k3", label: "Kimi K3", provider: "supercode", cost: "", desc: "Moonshot latest" }, | ||
| { value: "minimax-m3", label: "MiniMax M3", provider: "supercode", cost: "", desc: "Fast & smart" }, | ||
| { value: "glm-5.2", label: "GLM 5.2", provider: "supercode", cost: "", desc: "Latest GLM" }, | ||
| { value: "glm-5.1", label: "GLM 5.1", provider: "supercode", cost: "", desc: "Stable & reliable" }, | ||
| { value: "mimo-v2.5", label: "Mimo v2.5", provider: "supercode", cost: "", desc: "Novita" }, | ||
| { value: "hy3", label: "Hunyuan Hy3", provider: "supercode", cost: "", desc: "Tencent flagship" }, | ||
| { value: "gemini-2.5-flash", label: "Gemini 2.5 Flash", provider: "supercode", cost: "", desc: "Google smart & fast" }, | ||
| { value: "meta/llama-3.3-70b-instruct", label: "Llama 3.3 70B", provider: "supercode", cost: "", desc: "Open weights" }, | ||
| { value: "orcarouter/auto", label: "OrcaRouter Auto", provider: "supercode", cost: "", desc: "Auto-pick cheapest" }, | ||
| ] | ||
|
|
||
| // Models available when you bring your own API key (BYOK) | ||
|
|
@@ -154,7 +185,7 @@ export const BYOK_MODELS: ModelEntry[] = [ | |
| { value: "openai/gpt-4.1-nano", label: "GPT-4.1 Nano", provider: "openrouter", cost: "0.3x", desc: "Tiny & fast" }, | ||
| { value: "openai/o3-mini", label: "o3-mini", provider: "openrouter", cost: "3x", desc: "Reasoning mini" }, | ||
| { value: "openai/o4-mini", label: "o4-mini", provider: "openrouter", cost: "3x", desc: "Reasoning v4 mini" }, | ||
| { value: "openai/gpt-oss-120b:free", label: "GPT OSS 120B", provider: "openrouter", cost: "free", desc: "Open-weight free" }, | ||
| { value: "openai/gpt-oss-120b:free", label: "GPT OSS 120B", provider: "openrouter", cost: "", desc: "Open-weight" }, | ||
| { value: "x-ai/grok-3", label: "Grok 3", provider: "openrouter", cost: "10x", desc: "xAI flagship" }, | ||
| { value: "x-ai/grok-3-mini", label: "Grok 3 Mini", provider: "openrouter", cost: "5x", desc: "Compact Grok" }, | ||
| { value: "x-ai/grok-3-mini-fast", label: "Grok 3 Mini Fast", provider: "openrouter", cost: "5x", desc: "Fast Grok" }, | ||
|
|
@@ -201,28 +232,56 @@ export const BYOK_MODELS: ModelEntry[] = [ | |
| { value: "kimi/kimi-k3", label: "Kimi K3", provider: "orcarouter", cost: "", desc: "Moonshot latest" }, | ||
| { value: "kimi/kimi-k2.6", label: "Kimi K2.6", provider: "orcarouter", cost: "", desc: "Long context" }, | ||
| { value: "minimax/minimax-m3", label: "MiniMax M3", provider: "orcarouter", cost: "", desc: "Fast & smart" }, | ||
| { value: "orcarouter/auto", label: "OrcaRouter Auto", provider: "orcarouter", cost: "0x", desc: "Auto-pick cheapest" }, | ||
| { value: "orcarouter/auto", label: "OrcaRouter Auto", provider: "orcarouter", cost: "", desc: "Auto-pick cheapest" }, | ||
| ] | ||
|
|
||
| // Set of premium cloud models that require Supercode Cloud Premium | ||
| // Set of premium cloud models that require Pro or higher tier (frontier models) | ||
| export const PREMIUM_CLOUD_MODELS = new Set([ | ||
| "glm-5.2", | ||
| "kimi-k2-7-code", | ||
| "kimi-k3", | ||
| "minimax-m3", | ||
| "anthropic/claude-sonnet-4.6", | ||
| "anthropic/claude-opus-4.7", | ||
| "anthropic/claude-opus-4-8", | ||
| "openai/gpt-5.5", | ||
| "grok/grok-4-fast-reasoning", | ||
| "gemini-2.5-pro", | ||
| "deepseek/deepseek-reasoner", | ||
| ]) | ||
|
|
||
| export const MODELS: ModelEntry[] = [ | ||
| { value: SECTION_CLOUD, label: "Supercode Cloud", provider: "supercode", cost: "", desc: "" }, | ||
| ...CLOUD_MODELS, | ||
| { value: SECTION_CLOUD_PREMIUM, label: "Supercode Cloud Premium", provider: "supercode", cost: "", desc: "" }, | ||
| { value: "glm-5.2", label: "GLM 5.2", provider: "supercode", cost: "", desc: "Latest GLM" }, | ||
| { value: "kimi-k2-7-code", label: "Kimi K2.7 Code", provider: "supercode", cost: "", desc: "Code specialist" }, | ||
| { value: "kimi-k3", label: "Kimi K3", provider: "supercode", cost: "", desc: "Moonshot latest" }, | ||
| { value: "minimax-m3", label: "MiniMax M3", provider: "supercode", cost: "", desc: "Fast & smart" }, | ||
| { value: SECTION_BYOK, label: "Bring Your Own Key", provider: "supercode", cost: "", desc: "" }, | ||
| ...BYOK_MODELS, | ||
| ] | ||
| // Tier-organized model lists | ||
| export const TIER_MODELS: Record<UserTier, ModelEntry[]> = { | ||
| "spark": [ | ||
| { value: SECTION_CLOUD, label: "Supercode Cloud", provider: "supercode", cost: "", desc: "" }, | ||
| ...CLOUD_MODELS, | ||
| { value: SECTION_BYOK, label: "Bring Your Own Key", provider: "supercode", cost: "", desc: "" }, | ||
| ...BYOK_MODELS, | ||
| ], | ||
| "spark-premium": [ | ||
| { value: SECTION_CLOUD, label: "Supercode Cloud", provider: "supercode", cost: "", desc: "" }, | ||
| ...CLOUD_MODELS, | ||
| { value: SECTION_BYOK, label: "Bring Your Own Key", provider: "supercode", cost: "", desc: "" }, | ||
| ...BYOK_MODELS, | ||
| ], | ||
| "pro": [ | ||
| { value: SECTION_CLOUD, label: "Supercode Cloud", provider: "supercode", cost: "", desc: "" }, | ||
| ...CLOUD_MODELS, | ||
| { value: SECTION_BYOK, label: "Bring Your Own Key", provider: "supercode", cost: "", desc: "" }, | ||
| ...BYOK_MODELS, | ||
| ], | ||
| "ultra": [ | ||
| { value: SECTION_CLOUD, label: "Supercode Cloud", provider: "supercode", cost: "", desc: "" }, | ||
| ...CLOUD_MODELS, | ||
| { value: SECTION_BYOK, label: "Bring Your Own Key", provider: "supercode", cost: "", desc: "" }, | ||
| ...BYOK_MODELS, | ||
| ], | ||
| "none": [ | ||
| { value: SECTION_CLOUD, label: "Supercode Cloud", provider: "supercode", cost: "", desc: "" }, | ||
| ...CLOUD_MODELS, | ||
| { value: SECTION_BYOK, label: "Bring Your Own Key", provider: "supercode", cost: "", desc: "" }, | ||
| ...BYOK_MODELS, | ||
| ], | ||
| } | ||
|
Comment on lines
+249
to
+281
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift 🧩 Analysis chain🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
ast-grep outline apps/supercode-cli/server/src/cli/commands/slashCommands/model.ts --items all
rg -nP --type ts -C 5 '\bpickModel\s*\(' apps/supercode-cli/server/src
rg -nP --type ts -C 5 '\bisModelAllowedForTier\s*\(' apps/supercode-cli/server/src
rg -nP --type ts -C 5 '\b(saveCliConfig|getCliConfig)\s*\(' apps/supercode-cli/server/srcRepository: yashdev9274/supercli Length of output: 43057 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
echo "== model.ts outline =="
wc -l apps/supercode-cli/server/src/cli/commands/slashCommands/model.ts
sed -n '1,140p' apps/supercode-cli/server/src/cli/commands/slashCommands/model.ts
printf '\n== model.ts tier and TIER_MODELS ==\n'
sed -n '140,310p' apps/supercode-cli/server/src/cli/commands/slashCommands/model.ts
printf '\n== model.ts ModelPicker and pickModel ==\n'
sed -n '540,755p' apps/supercode-cli/server/src/cli/commands/slashCommands/model.ts
echo "== model access and plan gate =="
sed -n '1,180p' apps/supercode-cli/server/src/lib/model-access.ts
sed -n '1,120p' apps/supercode-cli/server/src/lib/plan-gate.ts
echo "== usages of model selector and pickModel =="
rg -n --type ts 'pickModel\(|providerMeta\[[0-9a-z"]+\]|MIN_TIER|minTier|spark|h3|minimax-m3|model-access|plan-gate|getCurrentUser|isModelAllowedForTier' apps/supercode-cli/server/srcRepository: yashdev9274/supercli Length of output: 42298 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
echo "== migration / seed / model data files =="
git ls-files 'apps/supercode-cli/server/**' | rg '(migrations|seed|prisma|data|supercode|model|minimax|hy3|tier)' | head -200
echo "== target model names in data/migrations =="
rg -n --hidden --glob '!.git/**' 'minimax-m3|h3|hy3|minTier|spark|seed' apps/supercode-cli/server -g '*.ts' -g '*.json' -g '*.prisma' -g '*.sql' | head -300Repository: yashdev9274/supercli Length of output: 26665 Do not expose Spark-gated cloud models to the
🤖 Prompt for AI Agents |
||
|
|
||
| // Default model list (fallback) | ||
| export const MODELS: ModelEntry[] = TIER_MODELS["none"] | ||
|
|
||
| export class ModelPicker { | ||
| items: ModelEntry[] = MODELS | ||
|
|
@@ -377,28 +436,13 @@ export class ModelPicker { | |
| const name = chalk.hex( | ||
| isCurrent ? theme.green : theme.greenGlow, | ||
| )(m.label.padEnd(22)) | ||
| const cost = chalk.hex( | ||
| m.cost === "free" ? theme.greenGlow : theme.muted, | ||
| )(m.cost.padEnd(6)) | ||
| const cost = chalk.hex(theme.muted)(m.cost.padEnd(6)) | ||
| const desc = chalk.hex(theme.muted)(m.desc.padEnd(20)) | ||
| const marker = isCurrent | ||
| ? ` ${chalk.bgHex(theme.amber).hex(theme.black).bold(" current ")}` | ||
| : "" | ||
| const isPremium = PREMIUM_CLOUD_MODELS.has(m.value) | ||
| const cloudTag = | ||
| m.provider === "supercode" && !isCurrent && !isPremium | ||
| ? ` ${chalk.bgHex(theme.green).hex(theme.black).bold(" CLOUD ")}` | ||
| : "" | ||
| const premiumTag = | ||
| isPremium && !isCurrent | ||
| ? ` ${chalk.bgHex(theme.amber).hex(theme.black).bold(" PREMIUM ")}` | ||
| : "" | ||
| const freeTag = | ||
| !isCurrent && m.cost === "free" && m.provider !== "supercode" | ||
| ? ` ${chalk.bgHex(theme.green).hex(theme.black).bold(" FREE ")}` | ||
| : "" | ||
|
|
||
| const label = `${prefix} ${name} ${cost}${desc}${marker}${cloudTag}${premiumTag}${freeTag}` | ||
|
|
||
| const label = `${prefix} ${name} ${cost}${desc}${marker}` | ||
|
|
||
| if (isSelected) { | ||
| const bg = chalk.bgHex(theme.greenDeep) | ||
|
|
@@ -507,9 +551,12 @@ export async function pickModel( | |
| const currentProvider = stored?.provider || "supercode" | ||
| const currentModel = stored?.model || "deepseek-v4-flash" | ||
|
|
||
| // Get user tier for model filtering | ||
| const userTier = await getUserTier() | ||
|
|
||
| const picker = new ModelPicker() | ||
| if (providerFilter) { | ||
| picker.items = MODELS.filter((m) => m.provider === providerFilter) | ||
| picker.items = TIER_MODELS[userTier].filter((m) => m.provider === providerFilter) | ||
| if (opts?.allowCustom) { | ||
| picker.items.push({ | ||
| value: "__custom__", | ||
|
|
@@ -520,6 +567,8 @@ export async function pickModel( | |
| }) | ||
| } | ||
| picker.setFilter("") | ||
| } else { | ||
| picker.items = TIER_MODELS[userTier] | ||
| } | ||
| const cols = process.stdout.columns ?? 80 | ||
|
|
||
|
|
@@ -608,15 +657,23 @@ export async function pickModel( | |
| return { provider: selected.provider, model: trimmed } | ||
| } | ||
|
|
||
| // Premium cloud models require Supercode Cloud Premium | ||
| if (PREMIUM_CLOUD_MODELS.has(selected.value)) { | ||
| // Premium cloud models (frontier) require Pro or higher tier | ||
| const frontierModels = new Set([ | ||
| "anthropic/claude-sonnet-4.6", | ||
| "anthropic/claude-opus-4.7", | ||
| "anthropic/claude-opus-4-8", | ||
| "openai/gpt-5.5", | ||
| "grok/grok-4-fast-reasoning", | ||
| "gemini-2.5-pro", | ||
| "deepseek/deepseek-reasoner", | ||
| ]) | ||
| if (frontierModels.has(selected.value) && userTier !== "pro" && userTier !== "ultra") { | ||
| process.stdout.write("\n") | ||
| process.stdout.write( | ||
| ` ${chalk.hex(theme.amber)("◆")} ${chalk.hex(theme.green).bold("Supercode Cloud Premium")} | ||
| ` ${chalk.hex(theme.amber)("◆")} ${chalk.hex(theme.green).bold("Pro Plan Required")} | ||
|
|
||
| ${chalk.hex(theme.muted)("This model is available on Supercode Cloud Premium.")} | ||
| ${chalk.hex(theme.muted)("Upgrade your plan to access premium models.")} | ||
| ${chalk.hex(theme.muted)("→ https://supercode.ai/pricing")} | ||
| ${chalk.hex(theme.muted)("This model requires a Pro or Ultra subscription.")} | ||
| ${chalk.hex(theme.muted)("Run /upgrade to subscribe.")} | ||
|
|
||
| ` | ||
| ) | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -24,16 +24,28 @@ export function tierIndex(tier: string): number { | |||||||||||||||||||||||
| /** | ||||||||||||||||||||||||
| * Checks whether a model slug is allowed for the user's plan tier. | ||||||||||||||||||||||||
| * Model gating is backed by the `Model` table's `minTier` field. | ||||||||||||||||||||||||
| * | ||||||||||||||||||||||||
| * Tier ladder: spark < spark-premium < pro < ultra | ||||||||||||||||||||||||
| * Open models use minTier "spark" so both Spark and Spark Premium get them. | ||||||||||||||||||||||||
| * Spark Premium is the paid open-models tier (higher limits/credits, same catalog). | ||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||
| export async function isModelAllowedForTier( | ||||||||||||||||||||||||
| modelSlug: string, | ||||||||||||||||||||||||
| userTier: string, | ||||||||||||||||||||||||
| ): Promise<boolean> { | ||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||
| const models = await ensureCache() | ||||||||||||||||||||||||
| const model = models.find( | ||||||||||||||||||||||||
| (m) => m.slug === modelSlug || modelSlug.includes(m.slug), | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| const normalized = modelSlug.trim().toLowerCase() | ||||||||||||||||||||||||
| const model = models.find((m) => { | ||||||||||||||||||||||||
| const slug = m.slug.toLowerCase() | ||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||
| slug === normalized || | ||||||||||||||||||||||||
| normalized === slug || | ||||||||||||||||||||||||
| normalized.endsWith(`/${slug}`) || | ||||||||||||||||||||||||
| normalized.includes(slug) || | ||||||||||||||||||||||||
| slug.includes(normalized) | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
|
Comment on lines
+38
to
+47
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
ast-grep outline apps/supercode-cli/server/src/lib/model-access.ts --items all
rg -nP --type ts -C 4 '\bisModelAllowedForTier\s*\(' apps/supercode-cli/server/srcRepository: yashdev9274/supercli Length of output: 2279 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- model-access.ts ---'
cat -n apps/supercode-cli/server/src/lib/model-access.ts | sed -n '1,90p'
printf '%s\n' '--- plan-gate.ts relevant section ---'
cat -n apps/supercode-cli/server/src/lib/plan-gate.ts | sed -n '1,90p'
printf '%s\n' '--- occurrences of isModelAllowedForTier results usages ---'
rg -nP --type ts -C 3 'isModelAllowedForTier|modelAllowed|allowed' apps/supercode-cli/server/src
printf '%s\n' '--- behavior probe for current matching rules ---'
python3 - <<'PY'
models = [
{"slug": "deepseek-v4-flash"},
{"slug": "openai-gpt-4"},
{"slug": "spark-premium"},
]
test_cases = [
"deepseek-v4-flash-preview",
"spark-premium-extra",
"gpt-4",
"openai",
" openai-gpt-4 ",
" openai-gpt-4 /spark-premium ",
]
for modelSlug in test_cases:
normalized = modelSlug.strip().lower()
found = []
for m in models:
slug = m["slug"].lower()
matches = (
slug == normalized or
normalized == slug or # same as first
normalized.endswith(f"/{slug}") or
normalized.find(slug) != -1 or
slug.find(normalized) != -1
)
if matches:
found.append(m["slug"])
print(f"{modelSlug!r} -> {found}")
PYRepository: yashdev9274/supercli Length of output: 35872 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- index.ts model handling relevant section ---'
cat -n apps/supercode-cli/server/src/index.ts | sed -n '55,90p'
cat -n apps/supercode-cli/server/src/index.ts | sed -n '320,345p'
printf '%s\n' '--- chat gate handling relevant sections ---'
cat -n apps/supercode-cli/server/src/cli/ai/chat/chat.ts | sed -n '2168,2195p'
cat -n apps/supercode-cli/server/src/cli/ai/chat/chat.ts | sed -n '2272,2298p'
cat -n apps/supercode-cli/server/src/cli/ai/chat/chat.ts | sed -n '2354,2375p'
printf '%s\n' '--- AI provider/model name handling relevant sections ---'
fd -a 'provider.ts' apps/supercode-cli/server/src/cli/ai | while read -r f; do
printf '\n=== %s ===\n' "$f"
cat -n "$f" | sed -n '1,120p'
done
printf '%s\n' '--- request-counter and credit-meter model usage ---'
cat -n apps/supercode-cli/server/src/lib/request-counter.ts | sed -n '1,90p'
cat -n apps/supercode-cli/server/src/lib/credit-meter.ts | sed -n '1,120p'
printf '%s\n' '--- deterministic probe of current matcher against catalog-like slugs ---'
python3 - <<'PY'
catalog = ["deepseek-v4-flash", "openai-gpt-4", "spark-premium", "claude-sonnet-4"]
cases = [
("deepseek-v4-flash-preview", False),
("openai-gpt-5", False),
("gpt-4", True),
("claude-sonnet", True),
("my/deepseek-v4-flash-preview", False),
("/ deepseek-v4-flash-preview", False),
]
for input_name, expected_partial in cases:
normalized = input_name.strip().lower()
hits = [m for m in catalog if (normalized == m or normalized.endswith(f"/{m}") or normalized.find(m) != -1 or m.find(normalized) != -1)]
print(f"{input_name!r} -> hits={hits}, has_expected_partial={any(h in normalized or normalized.startswith(h) for h in hits)}")
PYRepository: yashdev9274/supercli Length of output: 19507 Restrict Current substring matching lets names that do not appear in the Allow only exact normalized slugs and explicit provider-qualified suffixes such as 🤖 Prompt for AI Agents |
||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||
| if (!model) return false | ||||||||||||||||||||||||
| return tierIndex(userTier) >= tierIndex(model.minTier) | ||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||
|
|
@@ -43,8 +55,11 @@ export async function isModelAllowedForTier( | |||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| export function getUpgradeSuggestion(userTier: string): string { | ||||||||||||||||||||||||
| if (tierIndex(userTier) < tierIndex("spark-premium")) { | ||||||||||||||||||||||||
| return "To access more open models and higher limits, run /upgrade (Spark Premium)" | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| if (tierIndex(userTier) < tierIndex("pro")) { | ||||||||||||||||||||||||
| return "To access premium models, run /upgrade" | ||||||||||||||||||||||||
| return "To access premium models (Claude, GPT, etc.), run /upgrade" | ||||||||||||||||||||||||
|
Comment on lines
+58
to
+62
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Correct the Spark Premium benefit text. Spark Premium uses the same open-model catalog as Spark. The message at Line 59 incorrectly promises “more open models.” Describe higher limits and credits instead. Proposed fix- return "To access more open models and higher limits, run /upgrade (Spark Premium)"
+ return "To access the paid open-model tier with higher limits and credits, run /upgrade (Spark Premium)"📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| if (tierIndex(userTier) < tierIndex("ultra")) { | ||||||||||||||||||||||||
| return "To access all models unrestricted, run /upgrade" | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: yashdev9274/supercli
Length of output: 28499
🌐 Web query:
Prisma 7.8.0@prisma/client7.8.0 release notes compatibility prisma client runtime💡 Result:
The Prisma 7.8.0 release, published on April 22, 2026, focuses on performance enhancements and bug fixes for the Prisma Client and migration tools [1][2]. A key addition in version 7.8.0 is the
queryPlanCacheMaxSizeoption in thePrismaClientconstructor [3][2][4]. This allows developers to control the query plan cache size: - Setting this to0disables the cache entirely [3][2][5]. - Omitting it uses the default cache size [3][2][5]. - Increasing the value can improve performance for applications executing many unique queries, while decreasing it can reduce memory usage [2][6][5]. Regarding Prisma Client runtime compatibility, Prisma maintains support for multiple environments. The internal runtime targets identified for the client include 'nodejs', 'workerd', 'vercel-edge', and 'deno' [7]. The public-facing runtime options supported by the client generator comprise 'nodejs', 'deno', 'bun', 'workerd', 'cloudflare', 'vercel-edge', and 'edge-light' [7][8]. The client handles environment detection to route requests to the appropriate runtime logic [7][9]. Bug fixes in this release include: - Resolved issues with equality filter panics and incorrect JSONB casts for PostgreSQL JSON list columns [1][2]. - Fixed case-insensitive JSON field filtering [1][2]. - Addressed incorrect parameterization of enums with@mapand fixed a database parameter limit check (P2029) [1][6]. - Corrected a regression causing missing SQL Server VARCHAR casts [1][2]. - Fixed PostgreSQL introspection regarding sequence defaults [1][2]. - Included minor updates for the@prisma/adapter-d1to handle savepoint operations as no-ops [1][2].Citations:
🏁 Script executed:
Repository: yashdev9274/supercli
Length of output: 409
🏁 Script executed:
Repository: yashdev9274/supercli
Length of output: 15566
Align the Prisma CLI version with the Prisma Client.
@prisma/clientresolves to7.8.0, butprismaresolves to7.5.0. Adb:generate+ seed run will use the lower Prisma CLI if generated types were last produced by it; regenerate Prisma to7.8.0so the CLI, generated client, and seed path use the same client runtime.🤖 Prompt for AI Agents