Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
diff --git a/src/cli/commands/setup.ts b/src/cli/commands/setup.ts
index ca7cc3e..fd24370 100644
--- a/src/cli/commands/setup.ts
+++ b/src/cli/commands/setup.ts
@@ -45,8 +45,13 @@ export async function detectLiteLLMEnforcement(existingCodeMieUrl?: string): Pro
}
const { project } = await selectCodeMieProject(authResult);
const allIntegrations = await fetchCodeMieIntegrations(authResult.apiUrl, authResult.cookies);
- const projectIntegrations = allIntegrations.filter(i => i.project_name === project);
+ const projectIntegrations = allIntegrations.filter(
+ i => i.project_name === project && i.credential_type === 'LiteLLM'
+ );
if (projectIntegrations.length === 0) return { enforced: false };
+ if (projectIntegrations.length > 1) {
+ logger.warn(`Multiple LiteLLM integrations found for project "${project}". Using "${projectIntegrations[0].alias}".`);
+ }
return { enforced: true, integration: projectIntegrations[0], project, authResult };
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
@@ -235,7 +240,8 @@ async function runSetupWizard(force?: boolean): Promise<void> {
}

// Step 1: Check for mandatory LiteLLM integration before provider selection
- const enforcementResult = await detectLiteLLMEnforcement();
+ // Skip SSO gate on update flows — enforcement only applies to fresh configurations.
+ const enforcementResult = isUpdate ? { enforced: false as const } : await detectLiteLLMEnforcement();

let provider: string;
let enforcementContext: LiteLLMEnforcementContext | undefined;
diff --git a/src/providers/plugins/litellm/litellm.setup-steps.ts b/src/providers/plugins/litellm/litellm.setup-steps.ts
index 7e4bc4c..913c72a 100644
--- a/src/providers/plugins/litellm/litellm.setup-steps.ts
+++ b/src/providers/plugins/litellm/litellm.setup-steps.ts
@@ -45,7 +45,7 @@ export const LiteLLMSetupSteps: ProviderSetupSteps = {

return {
baseUrl: answers.baseUrl.trim(),
- apiKey: enforced ? answers.apiKey.trim() : (answers.apiKey?.trim() || 'not-required')
+ apiKey: enforced ? (answers.apiKey?.trim() ?? '') : (answers.apiKey?.trim() || 'not-required')
};
},

Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
{
"gate_id": "code-review.check",
"decision": "approve",
"confidence": "high",
"rationale": "All four patch findings (CR-001 through CR-004) are clearly resolved in the 42-line fix-up diff: credential_type filter added, multiple-integration warning added, isUpdate gate skip added, optional chain applied. CR-005 (decision_needed) is superseded — design decision confirmed that fallback on all SSO errors (including cancellation) is intentional per the spec error-handling table; no code change required. No new high-risk issues introduced. Fix-up commit passed pre-commit hooks (ESLint zero warnings, typecheck, related unit tests).",
"risk_flags": [],
"business_review": [
{
"id": "AC-1",
"text": "During setup, the system detects when the selected project already has a LiteLLM integration created.",
"status": "partial",
"notes": "Filter uses project_name only; any integration type triggers enforcement, not just LiteLLM-type integrations."
},
{
"id": "AC-2",
"text": "If LiteLLM integration exists, the setup flow enforces its usage for CLI configuration.",
"status": "partial",
"notes": "Auto-selects litellm correctly when triggered, but trigger condition is too broad (wrong type filter); also silently picks the first when multiple integrations match."
},
{
"id": "AC-3",
"text": "User cannot complete CLI setup without providing the required LiteLLM key.",
"status": "pass",
"notes": "Validator re-prompts on blank input; 'not-required' fallback is suppressed in enforcement mode."
},
{
"id": "AC-4",
"text": "Validation message clearly explains why setup cannot continue without LiteLLM credentials.",
"status": "pass",
"notes": "Header prints integration alias and portal hint; validator message cites the CodeMie portal."
},
{
"id": "AC-5",
"text": "The enforced flow does not affect projects that do not have LiteLLM integration created.",
"status": "partial",
"notes": "Projects with no integrations fall through correctly, but projects with non-LiteLLM integrations are incorrectly gated into the LiteLLM setup path."
},
{
"id": "AC-6",
"text": "No invalid CLI configuration state can be saved for projects where LiteLLM integration is mandatory.",
"status": "pass",
"notes": "Enforcement mode prevents empty key and 'not-required' fallback from being returned or saved."
}
],
"standards_review": [
{
"id": "STD-1",
"standard": "Conventional Commits format (git-workflow.md)",
"status": "pass",
"notes": "Commits use allowed types (feat, fix, test) with cli/providers scopes per commitlint.config.cjs; pre-commit hooks passed."
},
{
"id": "STD-2",
"standard": "No console.log (code-quality.md pre-commit checklist)",
"status": "partial",
"notes": "Fix-up diff adds no new console.log(); prior partial on main diff stands (established setup.ts UX pattern)."
},
{
"id": "STD-3",
"standard": "Optional chaining (code-quality.md best practices)",
"status": "partial",
"notes": "CR-004 fix applies optional chain to enforcement mode apiKey; prior partial carried forward from final round."
}
],
"findings": [],
"finding_status": [
{
"id": "CR-001",
"status": "resolved",
"notes": "Filter now `allIntegrations.filter(i => i.project_name === project && i.credential_type === 'LiteLLM')` — non-LiteLLM integrations no longer trigger the enforcement path."
},
{
"id": "CR-002",
"status": "resolved",
"notes": "logger.warn added when projectIntegrations.length > 1, naming the selected alias. Deterministic selection (first element) is now documented at the warn site."
},
{
"id": "CR-003",
"status": "resolved",
"notes": "Ternary `isUpdate ? { enforced: false as const } : await detectLiteLLMEnforcement()` skips SSO gate on all update flows."
},
{
"id": "CR-004",
"status": "resolved",
"notes": "`answers.apiKey?.trim() ?? ''` applied — optional chain prevents TypeError on undefined in headless/CI environments."
},
{
"id": "CR-005",
"status": "superseded",
"notes": "Design decision confirmed: falling back on all SSO errors (including user-initiated cancellation) is intentional per the spec error-handling table. No code change required."
}
]
}
117 changes: 117 additions & 0 deletions docs/superpowers/tasks/2026-07-20-epmcdme-11733/code-review-final.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
{
"gate_id": "code-review.final",
"decision": "request-changes",
"confidence": "medium",
"rationale": "CR-001 (critical) identifies that the integration-type filter is missing: any integration type on a project triggers LiteLLM enforcement, directly violating AC-1, AC-2, and AC-5. CR-003 (major) finds the enforcement gate runs unconditionally during update flows, breaking --update UX. CR-004 (major) catches a missing null guard on apiKey in enforcement mode. CR-002 (major) notes ambiguous first-match selection when multiple integrations exist. CR-005 (decision_needed) flags that the catch-all silently bypasses enforcement on auth cancellation — spec is ambiguous on this case. Three of six acceptance criteria are partial; two defer findings (project_name case sensitivity, authResult.apiUrl assertion) excluded from the blocking set.",
"risk_flags": [],
"business_review": [
{
"id": "AC-1",
"text": "During setup, the system detects when the selected project already has a LiteLLM integration created.",
"status": "partial",
"notes": "Filter uses project_name only; any integration type triggers enforcement, not just LiteLLM-type integrations."
},
{
"id": "AC-2",
"text": "If LiteLLM integration exists, the setup flow enforces its usage for CLI configuration.",
"status": "partial",
"notes": "Auto-selects litellm correctly when triggered, but trigger condition is too broad (wrong type filter); also silently picks the first when multiple integrations match."
},
{
"id": "AC-3",
"text": "User cannot complete CLI setup without providing the required LiteLLM key.",
"status": "pass",
"notes": "Validator re-prompts on blank input; 'not-required' fallback is suppressed in enforcement mode."
},
{
"id": "AC-4",
"text": "Validation message clearly explains why setup cannot continue without LiteLLM credentials.",
"status": "pass",
"notes": "Header prints integration alias and portal hint; validator message cites the CodeMie portal."
},
{
"id": "AC-5",
"text": "The enforced flow does not affect projects that do not have LiteLLM integration created.",
"status": "partial",
"notes": "Projects with no integrations fall through correctly, but projects with non-LiteLLM integrations are incorrectly gated into the LiteLLM setup path."
},
{
"id": "AC-6",
"text": "No invalid CLI configuration state can be saved for projects where LiteLLM integration is mandatory.",
"status": "pass",
"notes": "Enforcement mode prevents empty key and 'not-required' fallback from being returned or saved."
}
],
"standards_review": [
{
"id": "STD-1",
"standard": "Conventional Commits format (git-workflow.md)",
"status": "pass",
"notes": "Commits use allowed types (feat, test) with cli/providers scopes per commitlint.config.cjs; pre-commit hooks passed."
},
{
"id": "STD-2",
"standard": "No console.log (code-quality.md pre-commit checklist)",
"status": "partial",
"notes": "Diff adds console.log() for UX headers; follows established setup.ts pattern; not enforced by ESLint — no additional blocking finding raised."
},
{
"id": "STD-3",
"standard": "Optional chaining (code-quality.md best practices)",
"status": "partial",
"notes": "Normal mode uses answers.apiKey?.trim() but enforcement mode uses answers.apiKey.trim() without the guard — captured in CR-004."
}
],
"findings": [
{
"id": "CR-001",
"file": "src/cli/commands/setup.ts",
"line": 27,
"problem": "projectIntegrations is filtered by project_name only; any integration type (GitHub, Jira, etc.) on the project triggers the LiteLLM enforcement path.",
"impact": "Violates AC-1, AC-2, AC-5: projects with non-LiteLLM integrations are forced through LiteLLM setup and cannot choose any other provider.",
"recommendation": "Filter by credential_type as well: allIntegrations.filter(i => i.project_name === project && i.credential_type === 'LiteLLM').",
"severity": "critical",
"triage": "patch"
},
{
"id": "CR-002",
"file": "src/cli/commands/setup.ts",
"line": 29,
"problem": "When multiple integrations match the project, projectIntegrations[0] is silently selected with no user prompt and no documented sort order from the API.",
"impact": "Users with multiple LiteLLM integrations may be configured against the wrong proxy endpoint with no recourse.",
"recommendation": "If projectIntegrations.length > 1, prompt the user to select the integration, or document and enforce a deterministic tie-breaking rule (e.g. API sort order or most recently created).",
"severity": "major",
"triage": "patch"
},
{
"id": "CR-003",
"file": "src/cli/commands/setup.ts",
"line": 238,
"problem": "detectLiteLLMEnforcement() is called unconditionally on every runSetupWizard invocation, including --update flows (isUpdate === true), forcing an unexpected interactive SSO browser prompt when the user only wants to update an existing config.",
"impact": "Breaks codemie setup --update UX; a timeout or unreachable SSO server during an update silently skips enforcement.",
"recommendation": "Skip the enforcement check when isUpdate is true and no existingCodeMieUrl override is provided, or pass the isUpdate flag into detectLiteLLMEnforcement() to gate the SSO prompt.",
"severity": "major",
"triage": "patch"
},
{
"id": "CR-004",
"file": "src/providers/plugins/litellm/litellm.setup-steps.ts",
"line": 48,
"problem": "answers.apiKey.trim() in enforcement mode lacks the optional chain that the non-enforcement branch uses (answers.apiKey?.trim()); inquirer password prompts can return undefined in headless/CI environments.",
"impact": "TypeError crash at the last step of an otherwise successful enforcement-mode setup, leaving no configuration saved.",
"recommendation": "Use answers.apiKey?.trim() ?? '' and validate the empty string explicitly, consistent with the code-quality guide's optional-chaining best practice.",
"severity": "major",
"triage": "patch"
},
{
"id": "CR-005",
"file": "src/cli/commands/setup.ts",
"line": 44,
"problem": "The catch block returns { enforced: false } on ALL errors including explicit user auth-cancellation (closing the SSO browser window). The spec's error-handling table covers network/timeout fallbacks but is silent on user-initiated cancellation.",
"impact": "A user who closes the SSO popup bypasses mandatory LiteLLM enforcement and gains access to unrestricted provider selection.",
"recommendation": "Clarify intended behaviour for cancellation in the spec: if the user actively cancels SSO, should setup abort (rethrow) or fall back to normal flow? Update the catch handler accordingly.",
"severity": "major",
"triage": "decision_needed"
}
]
}
Loading
Loading