@supatest/cli 0.0.31 → 0.0.33

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 (2) hide show
  1. package/dist/index.js +746 -555
  2. package/package.json +7 -6
package/dist/index.js CHANGED
@@ -336,11 +336,64 @@ function getModelById(id) {
336
336
  const baseId = normalizeModelId(id);
337
337
  return AVAILABLE_MODELS.find((m) => m.id === baseId);
338
338
  }
339
+ function getTierFromProviderModel(providerModel) {
340
+ if (providerModel === "glm-4.7" || providerModel.startsWith("glm-4.7")) {
341
+ return "premium";
342
+ }
343
+ return null;
344
+ }
345
+ function getModelCostLabel(modelId) {
346
+ if (modelId === "small") return "0.5x";
347
+ if (modelId === "medium") return "1x";
348
+ if (modelId === "premium") return "2x";
349
+ const model = getModelById(modelId);
350
+ if (model) {
351
+ return model.costMultiplier === 0.5 ? "0.5x" : model.costMultiplier === 1 ? "1x" : model.costMultiplier === 2 ? "2x" : "1x";
352
+ }
353
+ return "1x";
354
+ }
339
355
  function getModelDisplayName(id) {
340
- const model = getModelById(id);
341
- return model?.name ?? id;
356
+ if (MODEL_TIERS.includes(id)) {
357
+ const tierNames = {
358
+ small: "Small",
359
+ medium: "Medium",
360
+ premium: "Premium"
361
+ };
362
+ return tierNames[id] || id;
363
+ }
364
+ const anthropicModel = getModelById(id);
365
+ if (anthropicModel) {
366
+ return anthropicModel.name;
367
+ }
368
+ const tier = getTierFromProviderModel(id);
369
+ if (tier && !id.startsWith("claude-")) {
370
+ const tierNames = {
371
+ small: "Small",
372
+ medium: "Medium",
373
+ premium: "Premium"
374
+ };
375
+ return tierNames[tier];
376
+ }
377
+ return id;
378
+ }
379
+ function resolveTierToAnthropicModel(tier) {
380
+ const tierToModel = {
381
+ small: "claude-haiku-4-5",
382
+ medium: "claude-sonnet-4-5",
383
+ premium: "claude-opus-4-5"
384
+ };
385
+ return tierToModel[tier];
386
+ }
387
+ function resolveToAnthropicModel(model) {
388
+ if (MODEL_TIERS.includes(model)) {
389
+ return resolveTierToAnthropicModel(model);
390
+ }
391
+ return model;
342
392
  }
343
393
  function isValidModelId(id) {
394
+ if (MODEL_TIERS.includes(id)) {
395
+ return true;
396
+ }
344
397
  return AVAILABLE_MODELS.some((m) => id === m.id || id.startsWith(`${m.id}-`));
345
398
  }
346
399
  function getErrorMap() {
@@ -538,7 +591,7 @@ function getToolDisplayName(toolName) {
538
591
  };
539
592
  return displayNameMap[toolName] || toolName;
540
593
  }
541
- var AVAILABLE_MODELS, DEFAULT_MODEL_ID, DATE_SUFFIX_REGEX, CONTEXT_WINDOWS, util, objectUtil, ZodParsedType, getParsedType, ZodIssueCode, ZodError, errorMap, overrideErrorMap, makeIssue, ParseStatus, INVALID, DIRTY, OK, isAborted, isDirty, isValid, isAsync, errorUtil, ParseInputLazyPath, handleResult, ZodType, cuidRegex, cuid2Regex, ulidRegex, uuidRegex, nanoidRegex, jwtRegex, durationRegex, emailRegex, _emojiRegex, emojiRegex, ipv4Regex, ipv4CidrRegex, ipv6Regex, ipv6CidrRegex, base64Regex, base64urlRegex, dateRegexSource, dateRegex, ZodString, ZodNumber, ZodBigInt, ZodBoolean, ZodDate, ZodSymbol, ZodUndefined, ZodNull, ZodAny, ZodUnknown, ZodNever, ZodVoid, ZodArray, ZodObject, ZodUnion, getDiscriminator, ZodDiscriminatedUnion, ZodIntersection, ZodTuple, ZodRecord, ZodMap, ZodSet, ZodFunction, ZodLazy, ZodLiteral, ZodEnum, ZodNativeEnum, ZodPromise, ZodEffects, ZodOptional, ZodNullable, ZodDefault, ZodCatch, ZodNaN, ZodBranded, ZodPipeline, ZodReadonly, ZodFirstPartyTypeKind, stringType, numberType, booleanType, dateType, unknownType, arrayType, objectType, unionType, discriminatedUnionType, recordType, functionType, lazyType, literalType, enumType, promiseType, coerce, MAX_API_KEY_NAME_LENGTH, apiKeySchema, apiKeyUsageSchema, createApiKeyRequestSchema, apiKeyResponseSchema, apiKeyUsageSummarySchema, genericErrorSchema, validationErrorSchema, feedbackCategorySchema, FEEDBACK_CATEGORIES, createFeedbackSchema, feedbackResponseSchema, listFeedbackQuerySchema, feedbackListResponseSchema, healthMetricSchema, healthMetricDailyItemSchema, healthMetricsWithDailySchema, healthAnalyticsPeriodSchema, healthAnalyticsDailyItemSchema, healthAnalyticsResponseSchema, MAX_TIMEZONE_CHAR_LENGTH, organizationSchema, organizationSettingsSchema, textBlockSchema, toolUseBlockSchema, toolResultBlockSchema, thinkingBlockSchema, imageBlockSchema, contentBlockSchema, sessionSchema, createSessionRequestSchema, updateSessionRequestSchema, messageSchema, createMessageRequestSchema, cliEventSchema, createCLISessionRequestSchema, queryResultSchema, queryTurnSchema, queryContentSchema, queryUsageSchema, querySchema, runStatusSchema, testResultStatusSchema, testOutcomeSchema, attachmentKindSchema, stepCategorySchema, runSummarySchema, ciMetadataSchema, gitMetadataSchema, playwrightConfigSchema, errorInfoSchema, locationSchema, sourceSnippetSchema, runSchema, annotationSchema, testSchema, testResultSchema, baseStepSchema, stepSchema, attachmentSchema, listRunsQuerySchema, listTestsQuerySchema, runsListResponseSchema, runDetailResponseSchema, testsListResponseSchema, testDetailResponseSchema, testHistoryItemSchema, testHistoryResponseSchema, topOffenderSchema, topOffendersResponseSchema, trendPointSchema, trendsResponseSchema, errorCategorySchema, failureClusterSchema, newFailureSchema, runInsightsResponseSchema, FailureCategoryEnum, SelectorTypeEnum, FailureCategoryStatsSchema, FailureCategoriesResponseSchema, FailingSelectorStatsSchema, FailingSelectorsResponseSchema, newFailureItemSchema, newFailuresResponseSchema, flakyTestItemSchema, flakyTestsResponseSchema, slowestTestItemSchema, slowestTestsResponseSchema, runSummaryEmailFailureSchema, runSummaryEmailReportSchema, sendRunReportRequestSchema, metricWithTrendSchema, weekOverWeekMetricsSchema, ciComputeTimeSchema, investigationCandidateSchema, failureCategoryBreakdownSchema, stabilityTrendSchema, folderStabilitySchema, managerReportSchema, managerReportQuerySchema, sendManagerReportRequestSchema, reportAttachmentLinkSchema, developerReportRegressionSchema, developerReportFlakyTestSchema, developerReportSlowTestSchema, developerRunSummaryReportSchema, executiveReportStatusSchema, executiveMetricSchema, executiveKeyMetricsSchema, executiveTrendPointSchema, executiveTrendsSchema, executiveFlakyOffenderSchema, executiveSlowestOffenderSchema, executiveTopOffendersSchema, executiveReportSchema, executiveReportQuerySchema, sendExecutiveReportRequestSchema;
594
+ var AVAILABLE_MODELS, DATE_SUFFIX_REGEX, MODEL_TIERS, CONTEXT_WINDOWS, util, objectUtil, ZodParsedType, getParsedType, ZodIssueCode, ZodError, errorMap, overrideErrorMap, makeIssue, ParseStatus, INVALID, DIRTY, OK, isAborted, isDirty, isValid, isAsync, errorUtil, ParseInputLazyPath, handleResult, ZodType, cuidRegex, cuid2Regex, ulidRegex, uuidRegex, nanoidRegex, jwtRegex, durationRegex, emailRegex, _emojiRegex, emojiRegex, ipv4Regex, ipv4CidrRegex, ipv6Regex, ipv6CidrRegex, base64Regex, base64urlRegex, dateRegexSource, dateRegex, ZodString, ZodNumber, ZodBigInt, ZodBoolean, ZodDate, ZodSymbol, ZodUndefined, ZodNull, ZodAny, ZodUnknown, ZodNever, ZodVoid, ZodArray, ZodObject, ZodUnion, getDiscriminator, ZodDiscriminatedUnion, ZodIntersection, ZodTuple, ZodRecord, ZodMap, ZodSet, ZodFunction, ZodLazy, ZodLiteral, ZodEnum, ZodNativeEnum, ZodPromise, ZodEffects, ZodOptional, ZodNullable, ZodDefault, ZodCatch, ZodNaN, ZodBranded, ZodPipeline, ZodReadonly, ZodFirstPartyTypeKind, stringType, numberType, booleanType, dateType, unknownType, arrayType, objectType, unionType, discriminatedUnionType, recordType, functionType, lazyType, literalType, enumType, promiseType, coerce, MAX_API_KEY_NAME_LENGTH, apiKeySchema, apiKeyUsageSchema, createApiKeyRequestSchema, apiKeyResponseSchema, apiKeyUsageSummarySchema, genericErrorSchema, validationErrorSchema, feedbackCategorySchema, FEEDBACK_CATEGORIES, createFeedbackSchema, feedbackResponseSchema, listFeedbackQuerySchema, feedbackListResponseSchema, healthMetricSchema, healthMetricDailyItemSchema, healthMetricsWithDailySchema, healthAnalyticsPeriodSchema, healthAnalyticsDailyItemSchema, healthAnalyticsResponseSchema, MAX_TIMEZONE_CHAR_LENGTH, organizationSchema, organizationSettingsSchema, textBlockSchema, toolUseBlockSchema, toolResultBlockSchema, thinkingBlockSchema, imageBlockSchema, contentBlockSchema, sessionSchema, createSessionRequestSchema, updateSessionRequestSchema, messageSchema, createMessageRequestSchema, cliEventSchema, createCLISessionRequestSchema, queryResultSchema, queryTurnSchema, queryContentSchema, queryUsageSchema, querySchema, runStatusSchema, testResultStatusSchema, testOutcomeSchema, attachmentKindSchema, stepCategorySchema, runSummarySchema, ciMetadataSchema, gitMetadataSchema, playwrightConfigSchema, errorInfoSchema, locationSchema, sourceSnippetSchema, runSchema, annotationSchema, testSchema, testResultSchema, baseStepSchema, stepSchema, attachmentSchema, listRunsQuerySchema, listTestsQuerySchema, runsListResponseSchema, runDetailResponseSchema, testsListResponseSchema, testDetailResponseSchema, testHistoryItemSchema, testHistoryResponseSchema, topOffenderSchema, topOffendersResponseSchema, trendPointSchema, trendsResponseSchema, errorCategorySchema, failureClusterSchema, newFailureSchema, runInsightsResponseSchema, FailureCategoryEnum, SelectorTypeEnum, FailureCategoryStatsSchema, FailureCategoriesResponseSchema, FailingSelectorStatsSchema, FailingSelectorsResponseSchema, newFailureItemSchema, newFailuresResponseSchema, flakyTestItemSchema, flakyTestsResponseSchema, slowestTestItemSchema, slowestTestsResponseSchema;
542
595
  var init_shared_es = __esm({
543
596
  "../shared/dist/shared.es.mjs"() {
544
597
  "use strict";
@@ -547,23 +600,26 @@ var init_shared_es = __esm({
547
600
  id: "claude-sonnet-4-5",
548
601
  name: "Sonnet 4.5",
549
602
  description: "Fast & capable",
550
- contextWindow: 2e5
603
+ contextWindow: 2e5,
604
+ costMultiplier: 1
551
605
  },
552
606
  {
553
607
  id: "claude-opus-4-5",
554
608
  name: "Opus 4.5",
555
609
  description: "Most capable",
556
- contextWindow: 2e5
610
+ contextWindow: 2e5,
611
+ costMultiplier: 2
557
612
  },
558
613
  {
559
614
  id: "claude-haiku-4-5",
560
615
  name: "Haiku 4.5",
561
616
  description: "Lightweight",
562
- contextWindow: 2e5
617
+ contextWindow: 2e5,
618
+ costMultiplier: 0.5
563
619
  }
564
620
  ];
565
- DEFAULT_MODEL_ID = "claude-sonnet-4-5";
566
621
  DATE_SUFFIX_REGEX = /-\d{8}$/;
622
+ MODEL_TIERS = ["small", "medium", "premium"];
567
623
  CONTEXT_WINDOWS = Object.fromEntries(
568
624
  AVAILABLE_MODELS.map((m) => [m.id, m.contextWindow])
569
625
  );
@@ -5003,263 +5059,6 @@ var init_shared_es = __esm({
5003
5059
  days: numberType()
5004
5060
  })
5005
5061
  });
5006
- runSummaryEmailFailureSchema = objectType({
5007
- testRunId: stringType(),
5008
- testId: stringType(),
5009
- title: stringType(),
5010
- file: stringType(),
5011
- errorMessage: stringType().nullable(),
5012
- errorStack: stringType().nullable()
5013
- });
5014
- runSummaryEmailReportSchema = objectType({
5015
- runId: stringType(),
5016
- readableId: stringType().optional(),
5017
- runDetailsUrl: stringType(),
5018
- startedAt: stringType(),
5019
- endedAt: stringType().optional(),
5020
- durationMs: numberType(),
5021
- branch: stringType().optional(),
5022
- commit: stringType().optional(),
5023
- commitMessage: stringType().optional(),
5024
- totalTests: numberType(),
5025
- passedTests: numberType(),
5026
- failedTests: numberType(),
5027
- flakyTests: numberType(),
5028
- skippedTests: numberType(),
5029
- passRate: numberType(),
5030
- topFailures: arrayType(runSummaryEmailFailureSchema)
5031
- });
5032
- sendRunReportRequestSchema = objectType({
5033
- runId: stringType(),
5034
- emails: arrayType(stringType().email())
5035
- });
5036
- metricWithTrendSchema = objectType({
5037
- current: numberType(),
5038
- previous: numberType(),
5039
- change: numberType(),
5040
- percentChange: numberType().nullable()
5041
- });
5042
- weekOverWeekMetricsSchema = objectType({
5043
- passRate: metricWithTrendSchema,
5044
- flakyTestCount: metricWithTrendSchema,
5045
- newFailures: metricWithTrendSchema,
5046
- totalRuns: metricWithTrendSchema
5047
- });
5048
- ciComputeTimeSchema = objectType({
5049
- failedRunsMs: numberType(),
5050
- retriedRunsMs: numberType(),
5051
- totalWastedMs: numberType(),
5052
- failedRunsHours: numberType(),
5053
- retriedRunsHours: numberType(),
5054
- totalWastedHours: numberType()
5055
- });
5056
- investigationCandidateSchema = objectType({
5057
- testId: stringType(),
5058
- testRunId: stringType(),
5059
- runId: stringType(),
5060
- file: stringType(),
5061
- title: stringType(),
5062
- flakeCount: numberType(),
5063
- passRate: numberType(),
5064
- avgDurationMs: numberType(),
5065
- ciTimeImpactMs: numberType(),
5066
- ciTimeImpactHours: numberType(),
5067
- category: FailureCategoryEnum.nullable(),
5068
- firstFlakyAt: stringType().nullable()
5069
- });
5070
- failureCategoryBreakdownSchema = objectType({
5071
- category: FailureCategoryEnum,
5072
- count: numberType(),
5073
- percentage: numberType()
5074
- });
5075
- stabilityTrendSchema = enumType(["improving", "degrading", "stable"]);
5076
- folderStabilitySchema = objectType({
5077
- folder: stringType(),
5078
- passRate: numberType(),
5079
- previousPassRate: numberType(),
5080
- trend: stabilityTrendSchema,
5081
- testCount: numberType(),
5082
- failureCount: numberType()
5083
- });
5084
- managerReportSchema = objectType({
5085
- period: objectType({
5086
- start: stringType(),
5087
- end: stringType(),
5088
- days: numberType()
5089
- }),
5090
- weekOverWeek: weekOverWeekMetricsSchema,
5091
- ciComputeTime: ciComputeTimeSchema,
5092
- investigationCandidates: arrayType(investigationCandidateSchema),
5093
- failureCategories: arrayType(failureCategoryBreakdownSchema),
5094
- folderStability: arrayType(folderStabilitySchema)
5095
- });
5096
- managerReportQuerySchema = objectType({
5097
- startDate: stringType().regex(
5098
- /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?)?$/,
5099
- "startDate must be in format YYYY-MM-DD or ISO datetime"
5100
- ),
5101
- endDate: stringType().regex(
5102
- /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?)?$/,
5103
- "endDate must be in format YYYY-MM-DD or ISO datetime"
5104
- )
5105
- });
5106
- sendManagerReportRequestSchema = objectType({
5107
- startDate: stringType(),
5108
- endDate: stringType(),
5109
- emails: arrayType(stringType().email())
5110
- });
5111
- reportAttachmentLinkSchema = objectType({
5112
- name: stringType(),
5113
- kind: attachmentKindSchema,
5114
- url: stringType().optional()
5115
- });
5116
- developerReportRegressionSchema = objectType({
5117
- testRunId: stringType(),
5118
- testId: stringType(),
5119
- title: stringType(),
5120
- file: stringType(),
5121
- errorMessage: stringType().nullable(),
5122
- errorStack: stringType().nullable(),
5123
- aiSummary: stringType().nullable(),
5124
- attachments: arrayType(reportAttachmentLinkSchema),
5125
- lastStableRun: objectType({
5126
- runId: stringType(),
5127
- date: stringType(),
5128
- branch: stringType().optional()
5129
- }).nullable()
5130
- });
5131
- developerReportFlakyTestSchema = objectType({
5132
- testRunId: stringType(),
5133
- testId: stringType(),
5134
- title: stringType(),
5135
- file: stringType(),
5136
- passRate: numberType(),
5137
- category: errorCategorySchema.nullable(),
5138
- lastPassedRun: objectType({
5139
- runId: stringType(),
5140
- date: stringType()
5141
- }).nullable()
5142
- });
5143
- developerReportSlowTestSchema = objectType({
5144
- testRunId: stringType(),
5145
- testId: stringType(),
5146
- title: stringType(),
5147
- file: stringType(),
5148
- durationMs: numberType(),
5149
- historicalAvgMs: numberType(),
5150
- slowdownFactor: numberType()
5151
- // how many times slower than average
5152
- });
5153
- developerRunSummaryReportSchema = objectType({
5154
- runId: stringType(),
5155
- readableId: stringType().optional(),
5156
- runDetailsUrl: stringType(),
5157
- startedAt: stringType(),
5158
- endedAt: stringType().optional(),
5159
- durationMs: numberType(),
5160
- branch: stringType().optional(),
5161
- commit: stringType().optional(),
5162
- commitMessage: stringType().optional(),
5163
- // Summary counts
5164
- totalTests: numberType(),
5165
- passedTests: numberType(),
5166
- failedTests: numberType(),
5167
- flakyTests: numberType(),
5168
- skippedTests: numberType(),
5169
- passRate: numberType(),
5170
- // Sections
5171
- newRegressions: arrayType(developerReportRegressionSchema),
5172
- knownFlaky: arrayType(developerReportFlakyTestSchema),
5173
- slowTests: arrayType(developerReportSlowTestSchema)
5174
- });
5175
- executiveReportStatusSchema = enumType([
5176
- "healthy",
5177
- "needs_attention",
5178
- "critical"
5179
- ]);
5180
- executiveMetricSchema = objectType({
5181
- /** Current month's value */
5182
- value: numberType(),
5183
- /** Previous month's value */
5184
- previousValue: numberType(),
5185
- /** Percentage change from previous month (null if previous was 0) */
5186
- change: numberType().nullable()
5187
- });
5188
- executiveKeyMetricsSchema = objectType({
5189
- /** CI First-Try Pass Rate: COUNT(runs WHERE failed=0 AND flaky=0) / COUNT(runs) */
5190
- ciFirstTryPassRate: executiveMetricSchema,
5191
- /** Total Test Pass Rate from health analytics */
5192
- testPassRate: executiveMetricSchema,
5193
- /** Count of distinct tests with flaky outcomes */
5194
- flakyTestCount: executiveMetricSchema,
5195
- /** CI compute time lost on failed/retried runs in hours */
5196
- ciComputeLostHours: executiveMetricSchema
5197
- });
5198
- executiveTrendPointSchema = objectType({
5199
- date: stringType(),
5200
- value: numberType()
5201
- });
5202
- executiveTrendsSchema = objectType({
5203
- /** Daily pass rate values for sparkline */
5204
- dailyPassRate: arrayType(executiveTrendPointSchema),
5205
- /** Daily flaky count values for sparkline */
5206
- dailyFlakyCount: arrayType(executiveTrendPointSchema)
5207
- });
5208
- executiveFlakyOffenderSchema = objectType({
5209
- testId: stringType(),
5210
- title: stringType(),
5211
- file: stringType(),
5212
- /** Number of flaky occurrences */
5213
- flakyCount: numberType(),
5214
- /** CI time impact in hours */
5215
- ciTimeImpactHours: numberType()
5216
- });
5217
- executiveSlowestOffenderSchema = objectType({
5218
- testId: stringType(),
5219
- title: stringType(),
5220
- file: stringType(),
5221
- /** Average duration in milliseconds */
5222
- avgDurationMs: numberType(),
5223
- /** Trend percentage change from previous period (null if no previous data) */
5224
- trend: numberType().nullable()
5225
- });
5226
- executiveTopOffendersSchema = objectType({
5227
- /** Top 3 most flaky tests with CI time impact */
5228
- mostFlaky: arrayType(executiveFlakyOffenderSchema),
5229
- /** Top 3 slowest tests with trend */
5230
- slowest: arrayType(executiveSlowestOffenderSchema)
5231
- });
5232
- executiveReportSchema = objectType({
5233
- /** Month in format YYYY-MM */
5234
- month: stringType(),
5235
- /** When the report was generated */
5236
- generatedAt: stringType(),
5237
- /** URL to view full report in dashboard */
5238
- reportUrl: stringType(),
5239
- /** Overall status: healthy, needs_attention, or critical */
5240
- status: executiveReportStatusSchema,
5241
- /** 4 key metrics with month-over-month comparison */
5242
- keyMetrics: executiveKeyMetricsSchema,
5243
- /** 30-day trend data for sparklines */
5244
- trends: executiveTrendsSchema,
5245
- /** Top offenders (most flaky and slowest tests) */
5246
- topOffenders: executiveTopOffendersSchema,
5247
- /** Auto-generated signal messages based on thresholds */
5248
- signals: arrayType(stringType())
5249
- });
5250
- executiveReportQuerySchema = objectType({
5251
- month: stringType().regex(
5252
- /^\d{4}-\d{2}$/,
5253
- "month must be in format YYYY-MM"
5254
- )
5255
- });
5256
- sendExecutiveReportRequestSchema = objectType({
5257
- month: stringType().regex(
5258
- /^\d{4}-\d{2}$/,
5259
- "month must be in format YYYY-MM"
5260
- ),
5261
- emails: arrayType(stringType().email())
5262
- });
5263
5062
  }
5264
5063
  });
5265
5064
 
@@ -5522,7 +5321,7 @@ var CLI_VERSION;
5522
5321
  var init_version = __esm({
5523
5322
  "src/version.ts"() {
5524
5323
  "use strict";
5525
- CLI_VERSION = "0.0.31";
5324
+ CLI_VERSION = "0.0.33";
5526
5325
  }
5527
5326
  });
5528
5327
 
@@ -6463,6 +6262,7 @@ var CoreAgent;
6463
6262
  var init_agent = __esm({
6464
6263
  async "src/core/agent.ts"() {
6465
6264
  "use strict";
6265
+ init_shared_es();
6466
6266
  await init_config();
6467
6267
  init_command_discovery();
6468
6268
  init_error_logger();
@@ -6531,6 +6331,8 @@ ${projectInstructions}`,
6531
6331
  "ANTHROPIC_API_KEY",
6532
6332
  "ANTHROPIC_BASE_URL",
6533
6333
  "ANTHROPIC_AUTH_TOKEN",
6334
+ "ZAI_API_KEY",
6335
+ "ZAI_BASE_URL",
6534
6336
  "CLAUDE_CODE_AUTH_TOKEN",
6535
6337
  "CLAUDE_CODE_OAUTH_TOKEN",
6536
6338
  "CLAUDE_API_KEY"
@@ -6549,15 +6351,17 @@ ${projectInstructions}`,
6549
6351
  if (config2.oauthToken) {
6550
6352
  cleanEnv.ANTHROPIC_API_KEY = "";
6551
6353
  cleanEnv.ANTHROPIC_BASE_URL = "";
6354
+ cleanEnv.ZAI_API_KEY = "";
6355
+ cleanEnv.ZAI_BASE_URL = "";
6552
6356
  this.presenter.onLog(`Auth: Using Claude Max (default Claude Code credentials)`);
6553
- logger.debug("[agent] Claude Max mode: Using default ~/.claude/ config, cleared ANTHROPIC_API_KEY and ANTHROPIC_BASE_URL");
6357
+ logger.debug("[agent] Claude Max mode: Using default ~/.claude/ config, cleared provider credentials");
6554
6358
  } else {
6555
6359
  const internalConfigDir = join6(homedir2(), ".supatest", "claude-internal");
6556
6360
  cleanEnv.CLAUDE_CONFIG_DIR = internalConfigDir;
6557
6361
  cleanEnv.ANTHROPIC_API_KEY = config2.supatestApiKey || "";
6558
6362
  cleanEnv.ANTHROPIC_BASE_URL = process.env.ANTHROPIC_BASE_URL || "";
6559
- this.presenter.onLog(`Auth: Using Supatest API key${process.env.ANTHROPIC_BASE_URL ? ` (base: ${process.env.ANTHROPIC_BASE_URL})` : ""}`);
6560
- logger.debug("[agent] API key mode: Set ANTHROPIC_API_KEY, ANTHROPIC_BASE_URL, and CLAUDE_CONFIG_DIR");
6363
+ this.presenter.onLog(`Auth: Using Supatest API (provider determined by backend)${process.env.ANTHROPIC_BASE_URL ? ` (base: ${process.env.ANTHROPIC_BASE_URL})` : ""}`);
6364
+ logger.debug("[agent] API mode: Set ANTHROPIC_API_KEY, ANTHROPIC_BASE_URL, and CLAUDE_CONFIG_DIR");
6561
6365
  }
6562
6366
  cleanEnv.ANTHROPIC_AUTH_TOKEN = "";
6563
6367
  cleanEnv.CLAUDE_CODE_AUTH_TOKEN = "";
@@ -6567,12 +6371,14 @@ ${projectInstructions}`,
6567
6371
  CLAUDE_CONFIG_DIR: cleanEnv.CLAUDE_CONFIG_DIR || "(using default ~/.claude/)"
6568
6372
  });
6569
6373
  cleanEnv.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS = "1";
6374
+ const selectedModel = config2.selectedModel || "premium";
6375
+ const defaultModel = resolveToAnthropicModel(selectedModel);
6570
6376
  const queryOptions = {
6571
6377
  // AbortController for cancellation support
6572
6378
  abortController: this.abortController,
6573
6379
  maxTurns: config2.maxIterations,
6574
6380
  cwd,
6575
- model: config2.selectedModel || config.anthropicModelName,
6381
+ model: defaultModel,
6576
6382
  permissionMode: isPlanMode ? "plan" : "bypassPermissions",
6577
6383
  allowDangerouslySkipPermissions: !isPlanMode,
6578
6384
  pathToClaudeCodeExecutable: claudeCodePath,
@@ -6965,6 +6771,7 @@ var init_react = __esm({
6965
6771
  toolInput: input,
6966
6772
  toolResult: void 0,
6967
6773
  isExpanded: false,
6774
+ isPending: true,
6968
6775
  toolUseId: toolId
6969
6776
  };
6970
6777
  this.callbacks.addMessage(message);
@@ -7010,7 +6817,8 @@ var init_react = __esm({
7010
6817
  }
7011
6818
  onToolResult(toolId, result) {
7012
6819
  this.callbacks.updateMessageByToolId(toolId, {
7013
- toolResult: result
6820
+ toolResult: result,
6821
+ isPending: false
7014
6822
  });
7015
6823
  streamEventAsync(this.apiClient, this.sessionId, {
7016
6824
  type: "tool_result",
@@ -7129,11 +6937,11 @@ var SessionContext, SessionProvider, useSession;
7129
6937
  var init_SessionContext = __esm({
7130
6938
  "src/ui/contexts/SessionContext.tsx"() {
7131
6939
  "use strict";
7132
- init_shared_es();
7133
6940
  SessionContext = createContext(null);
7134
6941
  SessionProvider = ({
7135
6942
  children,
7136
- initialModel
6943
+ initialModel,
6944
+ initialLlmProvider = "supatest-managed"
7137
6945
  }) => {
7138
6946
  const [messages, setMessages] = useState([]);
7139
6947
  const [todos, setTodos] = useState([]);
@@ -7150,8 +6958,10 @@ var init_SessionContext = __esm({
7150
6958
  const [webUrl, setWebUrl] = useState();
7151
6959
  const [agentMode, setAgentMode] = useState("build");
7152
6960
  const [planFilePath, setPlanFilePath] = useState();
7153
- const [selectedModel, setSelectedModel] = useState(initialModel || DEFAULT_MODEL_ID);
6961
+ const [selectedModel, setSelectedModel] = useState(initialModel || "premium");
6962
+ const [llmProvider, setLlmProvider] = useState(initialLlmProvider);
7154
6963
  const [allToolsExpanded, setAllToolsExpanded] = useState(true);
6964
+ const [toolGroupsExpanded, setToolGroupsExpanded] = useState(false);
7155
6965
  const [staticRemountKey, setStaticRemountKey] = useState(0);
7156
6966
  const addMessage = useCallback(
7157
6967
  (message) => {
@@ -7234,6 +7044,9 @@ var init_SessionContext = __esm({
7234
7044
  return newValue;
7235
7045
  });
7236
7046
  }, []);
7047
+ const toggleToolGroups = useCallback(() => {
7048
+ setToolGroupsExpanded((prev) => !prev);
7049
+ }, []);
7237
7050
  const value = {
7238
7051
  messages,
7239
7052
  addMessage,
@@ -7244,6 +7057,8 @@ var init_SessionContext = __esm({
7244
7057
  loadMessages,
7245
7058
  toggleAllToolOutputs,
7246
7059
  allToolsExpanded,
7060
+ toolGroupsExpanded,
7061
+ toggleToolGroups,
7247
7062
  todos,
7248
7063
  setTodos,
7249
7064
  stats,
@@ -7264,6 +7079,8 @@ var init_SessionContext = __esm({
7264
7079
  setPlanFilePath,
7265
7080
  selectedModel,
7266
7081
  setSelectedModel,
7082
+ llmProvider,
7083
+ setLlmProvider,
7267
7084
  staticRemountKey,
7268
7085
  refreshStatic
7269
7086
  };
@@ -7353,7 +7170,6 @@ var init_theme = __esm({
7353
7170
 
7354
7171
  // src/ui/components/Header.tsx
7355
7172
  import { Box, Text } from "ink";
7356
- import Gradient from "ink-gradient";
7357
7173
  import React2 from "react";
7358
7174
  var Header;
7359
7175
  var init_Header = __esm({
@@ -7383,7 +7199,7 @@ var init_Header = __esm({
7383
7199
  marginTop: 5,
7384
7200
  width: "100%"
7385
7201
  },
7386
- /* @__PURE__ */ React2.createElement(Gradient, { colors: ["#C96868", "#FF8C94"] }, /* @__PURE__ */ React2.createElement(Text, null, banner)),
7202
+ /* @__PURE__ */ React2.createElement(Text, { color: theme.text.accent }, banner),
7387
7203
  /* @__PURE__ */ React2.createElement(Box, { justifyContent: "center", marginTop: 0 }, /* @__PURE__ */ React2.createElement(Text, { color: theme.text.dim }, infoLine)),
7388
7204
  !headless && /* @__PURE__ */ React2.createElement(Box, { flexDirection: "column", marginTop: 1, paddingX: 2, width: "100%" }, /* @__PURE__ */ React2.createElement(Box, { flexDirection: "column", marginBottom: 0 }, /* @__PURE__ */ React2.createElement(Text, { color: theme.text.dim }, "\u{1F4A1} ", /* @__PURE__ */ React2.createElement(Text, { color: theme.text.secondary }, "Tip:"), " Use ", /* @__PURE__ */ React2.createElement(Text, { bold: true, color: theme.text.accent }, "@filename"), " to reference files, or ", /* @__PURE__ */ React2.createElement(Text, { bold: true, color: theme.text.accent }, "/help"), " for commands")), /* @__PURE__ */ React2.createElement(Box, { flexDirection: "column", marginTop: 0 }, /* @__PURE__ */ React2.createElement(Text, { color: theme.text.dim }, "\u2328\uFE0F ", /* @__PURE__ */ React2.createElement(Text, { color: theme.text.secondary }, "Shortcuts:"), " ", /* @__PURE__ */ React2.createElement(Text, { bold: true, color: theme.text.accent }, "Ctrl+H"), " help, ", /* @__PURE__ */ React2.createElement(Text, { bold: true, color: theme.text.accent }, "Ctrl+C"), " exit, ", /* @__PURE__ */ React2.createElement(Text, { bold: true, color: theme.text.accent }, "ESC"), " interrupt")), /* @__PURE__ */ React2.createElement(Box, { flexDirection: "column", marginTop: 0 }, /* @__PURE__ */ React2.createElement(Text, { color: theme.text.dim }, "\u{1F680} ", /* @__PURE__ */ React2.createElement(Text, { color: theme.text.secondary }, "Prompt Tips:"), " Be explicit with instructions, provide context, use examples, and think step-by-step")))
7389
7205
  );
@@ -8088,32 +7904,88 @@ var init_ToolMessage = __esm({
8088
7904
  }
8089
7905
  });
8090
7906
 
8091
- // src/ui/components/messages/UserMessage.tsx
7907
+ // src/ui/components/messages/ToolGroup.tsx
8092
7908
  import { Box as Box9, Text as Text9 } from "ink";
8093
7909
  import React10 from "react";
7910
+ function getToolCategory(toolName) {
7911
+ const name = toolName.toLowerCase();
7912
+ if (name.includes("read")) return "Read";
7913
+ if (name.includes("write")) return "Write";
7914
+ if (name.includes("edit")) return "Edit";
7915
+ if (name.includes("bash") || name.includes("command")) return "Bash";
7916
+ if (name.includes("glob")) return "Glob";
7917
+ if (name.includes("grep")) return "Grep";
7918
+ if (name.includes("task")) return "Task";
7919
+ if (name.includes("todo")) return "Todo";
7920
+ return toolName.charAt(0).toUpperCase() + toolName.slice(1).toLowerCase();
7921
+ }
7922
+ function getToolGroupCounts(tools) {
7923
+ const groups = tools.reduce(
7924
+ (acc, tool) => {
7925
+ const category = getToolCategory(tool.toolName);
7926
+ acc[category] = (acc[category] || 0) + 1;
7927
+ return acc;
7928
+ },
7929
+ {}
7930
+ );
7931
+ return Object.entries(groups).map(([name, count]) => ({ name, count })).sort((a, b) => b.count - a.count);
7932
+ }
7933
+ var ToolGroup;
7934
+ var init_ToolGroup = __esm({
7935
+ "src/ui/components/messages/ToolGroup.tsx"() {
7936
+ "use strict";
7937
+ init_theme();
7938
+ init_ToolMessage();
7939
+ ToolGroup = ({
7940
+ tools,
7941
+ isExpanded,
7942
+ onToggle,
7943
+ onToggleTool
7944
+ }) => {
7945
+ const toolGroups = getToolGroupCounts(tools);
7946
+ return /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "row" }, /* @__PURE__ */ React10.createElement(Text9, { color: theme.text.dim }, isExpanded ? "\u25BC " : "\u25B6 "), /* @__PURE__ */ React10.createElement(Text9, { color: theme.text.dim }, toolGroups.length > 0 ? toolGroups.map((group) => `${group.name} ${group.count}`).join(", ") : `${tools.length} tool${tools.length !== 1 ? "s" : ""}`), !isExpanded && /* @__PURE__ */ React10.createElement(Text9, { color: theme.text.dim }, " (ctrl+o to expand)")), isExpanded && /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", marginLeft: 2 }, tools.map((tool) => /* @__PURE__ */ React10.createElement(
7947
+ ToolMessage,
7948
+ {
7949
+ description: tool.description,
7950
+ id: tool.id,
7951
+ input: tool.input,
7952
+ isExpanded: tool.isExpanded,
7953
+ key: tool.id,
7954
+ onToggle: onToggleTool,
7955
+ result: tool.result,
7956
+ toolName: tool.toolName
7957
+ }
7958
+ ))));
7959
+ };
7960
+ }
7961
+ });
7962
+
7963
+ // src/ui/components/messages/UserMessage.tsx
7964
+ import { Box as Box10, Text as Text10 } from "ink";
7965
+ import React11 from "react";
8094
7966
  var UserMessage;
8095
7967
  var init_UserMessage = __esm({
8096
7968
  "src/ui/components/messages/UserMessage.tsx"() {
8097
7969
  "use strict";
8098
7970
  init_theme();
8099
7971
  UserMessage = ({ text }) => {
8100
- return /* @__PURE__ */ React10.createElement(Box9, { alignItems: "center", flexDirection: "row", marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text9, { color: theme.text.info }, "\u{1F464} "), /* @__PURE__ */ React10.createElement(
8101
- Box9,
7972
+ return /* @__PURE__ */ React11.createElement(Box10, { alignItems: "center", flexDirection: "row", marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text10, { color: theme.text.info }, "\u{1F464} "), /* @__PURE__ */ React11.createElement(
7973
+ Box10,
8102
7974
  {
8103
7975
  borderColor: theme.text.dim,
8104
7976
  borderStyle: "round",
8105
7977
  paddingLeft: 1,
8106
7978
  paddingRight: 1
8107
7979
  },
8108
- /* @__PURE__ */ React10.createElement(Text9, { color: theme.text.secondary }, text)
7980
+ /* @__PURE__ */ React11.createElement(Text10, { color: theme.text.secondary }, text)
8109
7981
  ));
8110
7982
  };
8111
7983
  }
8112
7984
  });
8113
7985
 
8114
7986
  // src/ui/components/QueuedMessageDisplay.tsx
8115
- import { Box as Box10, Text as Text10 } from "ink";
8116
- import React11 from "react";
7987
+ import { Box as Box11, Text as Text11 } from "ink";
7988
+ import React12 from "react";
8117
7989
  var MAX_DISPLAYED_QUEUED_MESSAGES, QueuedMessageDisplay;
8118
7990
  var init_QueuedMessageDisplay = __esm({
8119
7991
  "src/ui/components/QueuedMessageDisplay.tsx"() {
@@ -8126,8 +7998,8 @@ var init_QueuedMessageDisplay = __esm({
8126
7998
  if (messageQueue.length === 0) {
8127
7999
  return null;
8128
8000
  }
8129
- return /* @__PURE__ */ React11.createElement(
8130
- Box10,
8001
+ return /* @__PURE__ */ React12.createElement(
8002
+ Box11,
8131
8003
  {
8132
8004
  borderColor: theme.border.default,
8133
8005
  borderStyle: "round",
@@ -8136,20 +8008,20 @@ var init_QueuedMessageDisplay = __esm({
8136
8008
  marginTop: 0,
8137
8009
  paddingX: 1
8138
8010
  },
8139
- /* @__PURE__ */ React11.createElement(Box10, { marginBottom: 0 }, /* @__PURE__ */ React11.createElement(Text10, { bold: true, color: theme.text.secondary }, "Queued (", messageQueue.length, ")")),
8011
+ /* @__PURE__ */ React12.createElement(Box11, { marginBottom: 0 }, /* @__PURE__ */ React12.createElement(Text11, { bold: true, color: theme.text.secondary }, "Queued (", messageQueue.length, ")")),
8140
8012
  messageQueue.slice(0, MAX_DISPLAYED_QUEUED_MESSAGES).map((message, index) => {
8141
8013
  const preview = message.replace(/\s+/g, " ");
8142
- return /* @__PURE__ */ React11.createElement(Box10, { key: index, width: "100%" }, /* @__PURE__ */ React11.createElement(Text10, { color: theme.text.dim }, " ", index + 1, ". "), /* @__PURE__ */ React11.createElement(Text10, { color: theme.text.primary, wrap: "truncate" }, preview));
8014
+ return /* @__PURE__ */ React12.createElement(Box11, { key: index, width: "100%" }, /* @__PURE__ */ React12.createElement(Text11, { color: theme.text.dim }, " ", index + 1, ". "), /* @__PURE__ */ React12.createElement(Text11, { color: theme.text.primary, wrap: "truncate" }, preview));
8143
8015
  }),
8144
- messageQueue.length > MAX_DISPLAYED_QUEUED_MESSAGES && /* @__PURE__ */ React11.createElement(Box10, null, /* @__PURE__ */ React11.createElement(Text10, { color: theme.text.dim, italic: true }, "... (+", messageQueue.length - MAX_DISPLAYED_QUEUED_MESSAGES, " more)"))
8016
+ messageQueue.length > MAX_DISPLAYED_QUEUED_MESSAGES && /* @__PURE__ */ React12.createElement(Box11, null, /* @__PURE__ */ React12.createElement(Text11, { color: theme.text.dim, italic: true }, "... (+", messageQueue.length - MAX_DISPLAYED_QUEUED_MESSAGES, " more)"))
8145
8017
  );
8146
8018
  };
8147
8019
  }
8148
8020
  });
8149
8021
 
8150
8022
  // src/ui/components/MessageList.tsx
8151
- import { Box as Box11, Static } from "ink";
8152
- import React12, { useMemo as useMemo3 } from "react";
8023
+ import { Box as Box12, Static } from "ink";
8024
+ import React13, { useMemo as useMemo3 } from "react";
8153
8025
  var MessageList;
8154
8026
  var init_MessageList = __esm({
8155
8027
  "src/ui/components/MessageList.tsx"() {
@@ -8161,17 +8033,18 @@ var init_MessageList = __esm({
8161
8033
  init_LoadingMessage();
8162
8034
  init_ThinkingMessage();
8163
8035
  init_TodoMessage();
8036
+ init_ToolGroup();
8164
8037
  init_ToolMessage();
8165
8038
  init_UserMessage();
8166
8039
  init_QueuedMessageDisplay();
8167
8040
  MessageList = ({ terminalWidth, currentFolder, gitBranch, headless = false, queuedTasks = [] }) => {
8168
- const { messages, updateMessageById, isAgentRunning, staticRemountKey } = useSession();
8169
- const renderMessage = (message) => {
8041
+ const { messages, updateMessageById, isAgentRunning, staticRemountKey, toolGroupsExpanded, toggleToolGroups } = useSession();
8042
+ const renderMessage = (message, isInGroup = false) => {
8170
8043
  switch (message.type) {
8171
8044
  case "user":
8172
- return /* @__PURE__ */ React12.createElement(UserMessage, { key: message.id, text: message.content });
8045
+ return /* @__PURE__ */ React13.createElement(UserMessage, { key: message.id, text: message.content });
8173
8046
  case "assistant":
8174
- return /* @__PURE__ */ React12.createElement(
8047
+ return /* @__PURE__ */ React13.createElement(
8175
8048
  AssistantMessage,
8176
8049
  {
8177
8050
  isPending: message.isPending,
@@ -8181,7 +8054,22 @@ var init_MessageList = __esm({
8181
8054
  }
8182
8055
  );
8183
8056
  case "tool":
8184
- return /* @__PURE__ */ React12.createElement(
8057
+ if (isInGroup) {
8058
+ return /* @__PURE__ */ React13.createElement(
8059
+ ToolMessage,
8060
+ {
8061
+ description: message.content,
8062
+ id: message.id,
8063
+ input: message.toolInput,
8064
+ isExpanded: message.isExpanded,
8065
+ key: message.id,
8066
+ onToggle: (id) => updateMessageById(id, { isExpanded: !message.isExpanded }),
8067
+ result: message.toolResult,
8068
+ toolName: message.toolName || "Unknown"
8069
+ }
8070
+ );
8071
+ }
8072
+ return /* @__PURE__ */ React13.createElement(
8185
8073
  ToolMessage,
8186
8074
  {
8187
8075
  description: message.content,
@@ -8195,7 +8083,7 @@ var init_MessageList = __esm({
8195
8083
  }
8196
8084
  );
8197
8085
  case "thinking":
8198
- return /* @__PURE__ */ React12.createElement(
8086
+ return /* @__PURE__ */ React13.createElement(
8199
8087
  ThinkingMessage,
8200
8088
  {
8201
8089
  content: message.content,
@@ -8206,7 +8094,7 @@ var init_MessageList = __esm({
8206
8094
  }
8207
8095
  );
8208
8096
  case "error":
8209
- return /* @__PURE__ */ React12.createElement(
8097
+ return /* @__PURE__ */ React13.createElement(
8210
8098
  ErrorMessage,
8211
8099
  {
8212
8100
  key: message.id,
@@ -8215,37 +8103,120 @@ var init_MessageList = __esm({
8215
8103
  }
8216
8104
  );
8217
8105
  case "todo":
8218
- return /* @__PURE__ */ React12.createElement(TodoMessage, { key: message.id, todos: message.todos || [] });
8106
+ return /* @__PURE__ */ React13.createElement(TodoMessage, { key: message.id, todos: message.todos || [] });
8219
8107
  default:
8220
8108
  return null;
8221
8109
  }
8222
8110
  };
8223
- const { completedMessages, pendingMessages } = useMemo3(() => {
8111
+ const renderGroupedMessage = (group) => {
8112
+ if (group.type === "group") {
8113
+ return /* @__PURE__ */ React13.createElement(
8114
+ ToolGroup,
8115
+ {
8116
+ isExpanded: toolGroupsExpanded,
8117
+ key: `group-${group.messages[0].id}`,
8118
+ onToggle: toggleToolGroups,
8119
+ onToggleTool: (id) => {
8120
+ const msg = group.messages.find((m) => m.id === id);
8121
+ if (msg) {
8122
+ updateMessageById(id, { isExpanded: !msg.isExpanded });
8123
+ }
8124
+ },
8125
+ tools: group.messages.map((msg) => ({
8126
+ id: msg.id,
8127
+ toolName: msg.toolName || "Unknown",
8128
+ description: msg.content,
8129
+ input: msg.toolInput,
8130
+ result: msg.toolResult,
8131
+ isExpanded: msg.isExpanded
8132
+ }))
8133
+ }
8134
+ );
8135
+ }
8136
+ return renderMessage(group.messages[0]);
8137
+ };
8138
+ const { completedGroups, currentTurnGroups } = useMemo3(() => {
8224
8139
  const completed = [];
8225
- const pending = [];
8226
- for (const msg of messages) {
8227
- if (msg.isPending) {
8228
- pending.push(msg);
8140
+ const currentTurn = [];
8141
+ const processTurn = (turnMessages2, targetArray) => {
8142
+ let currentToolGroup = [];
8143
+ const flushToolGroup = () => {
8144
+ if (currentToolGroup.length === 0) return;
8145
+ if (currentToolGroup.length === 1) {
8146
+ targetArray.push({ type: "single", messages: [...currentToolGroup] });
8147
+ } else {
8148
+ targetArray.push({ type: "group", messages: [...currentToolGroup] });
8149
+ }
8150
+ currentToolGroup = [];
8151
+ };
8152
+ for (const msg of turnMessages2) {
8153
+ if (msg.type === "tool") {
8154
+ currentToolGroup.push(msg);
8155
+ } else {
8156
+ flushToolGroup();
8157
+ targetArray.push({ type: "single", messages: [msg] });
8158
+ }
8159
+ }
8160
+ flushToolGroup();
8161
+ };
8162
+ let lastUserMessageIndex = -1;
8163
+ for (let i = messages.length - 1; i >= 0; i--) {
8164
+ if (messages[i].type === "user") {
8165
+ lastUserMessageIndex = i;
8166
+ break;
8167
+ }
8168
+ }
8169
+ let turnMessages = [];
8170
+ for (let i = 0; i < lastUserMessageIndex; i++) {
8171
+ const msg = messages[i];
8172
+ if (msg.type === "user") {
8173
+ processTurn(turnMessages, completed);
8174
+ turnMessages = [];
8175
+ completed.push({ type: "single", messages: [msg] });
8229
8176
  } else {
8230
- completed.push(msg);
8177
+ turnMessages.push(msg);
8231
8178
  }
8232
8179
  }
8233
- return { completedMessages: completed, pendingMessages: pending };
8180
+ processTurn(turnMessages, completed);
8181
+ if (lastUserMessageIndex >= 0) {
8182
+ completed.push({ type: "single", messages: [messages[lastUserMessageIndex]] });
8183
+ }
8184
+ const currentTurnMessages = lastUserMessageIndex >= 0 ? messages.slice(lastUserMessageIndex + 1) : messages;
8185
+ processTurn(currentTurnMessages, currentTurn);
8186
+ return { completedGroups: completed, currentTurnGroups: currentTurn };
8234
8187
  }, [messages]);
8235
8188
  const staticItems = useMemo3(() => [
8236
8189
  { id: "header", type: "header" },
8237
- ...completedMessages.map((msg) => ({ ...msg, _isMessage: true }))
8238
- ], [completedMessages]);
8239
- return /* @__PURE__ */ React12.createElement(Box11, { flexDirection: "column" }, /* @__PURE__ */ React12.createElement(Static, { items: staticItems, key: staticRemountKey }, (item) => {
8190
+ ...completedGroups.map((group, idx) => {
8191
+ if (group.type === "group") {
8192
+ return { ...group, _isGroup: true, id: `group-${idx}` };
8193
+ }
8194
+ return { ...group.messages[0], _isMessage: true };
8195
+ })
8196
+ ], [completedGroups]);
8197
+ return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column" }, /* @__PURE__ */ React13.createElement(Static, { items: staticItems, key: staticRemountKey }, (item) => {
8240
8198
  if (item.type === "header") {
8241
- return /* @__PURE__ */ React12.createElement(Box11, { flexDirection: "column", key: "header" }, /* @__PURE__ */ React12.createElement(Header, { currentFolder, gitBranch, headless }));
8199
+ return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", key: "header" }, /* @__PURE__ */ React13.createElement(Header, { currentFolder, gitBranch, headless }));
8200
+ }
8201
+ if (item._isGroup) {
8202
+ const content2 = renderGroupedMessage(item);
8203
+ if (!content2) {
8204
+ return null;
8205
+ }
8206
+ return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", key: item.id, width: "100%" }, content2);
8242
8207
  }
8243
8208
  const content = renderMessage(item);
8244
8209
  if (!content) {
8245
8210
  return null;
8246
8211
  }
8247
- return /* @__PURE__ */ React12.createElement(Box11, { flexDirection: "column", key: item.id, width: "100%" }, content);
8248
- }), pendingMessages.map((message) => /* @__PURE__ */ React12.createElement(Box11, { flexDirection: "column", key: message.id, width: "100%" }, renderMessage(message))), /* @__PURE__ */ React12.createElement(QueuedMessageDisplay, { messageQueue: queuedTasks }), isAgentRunning && !messages.some((m) => m.type === "assistant" && m.isPending) && /* @__PURE__ */ React12.createElement(LoadingMessage, { headless, key: "loading" }));
8212
+ return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", key: item.id, width: "100%" }, content);
8213
+ }), currentTurnGroups.map((group, idx) => {
8214
+ const content = renderGroupedMessage(group);
8215
+ if (!content) {
8216
+ return null;
8217
+ }
8218
+ return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", key: group.type === "group" ? `current-group-${idx}` : group.messages[0].id, width: "100%" }, content);
8219
+ }), /* @__PURE__ */ React13.createElement(QueuedMessageDisplay, { messageQueue: queuedTasks }), isAgentRunning && !messages.some((m) => m.type === "assistant" && m.isPending) && /* @__PURE__ */ React13.createElement(LoadingMessage, { headless, key: "loading" }));
8249
8220
  };
8250
8221
  }
8251
8222
  });
@@ -8920,6 +8891,128 @@ var init_login = __esm({
8920
8891
  }
8921
8892
  });
8922
8893
 
8894
+ // src/utils/claude-max.ts
8895
+ import { execSync as execSync5 } from "child_process";
8896
+ import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
8897
+ import { homedir as homedir5 } from "os";
8898
+ import { join as join8 } from "path";
8899
+ function isClaudeMaxAvailable() {
8900
+ const platform2 = process.platform;
8901
+ logger.debug("[claude-max] Checking Claude Code credentials", { platform: platform2 });
8902
+ if (platform2 === "darwin") {
8903
+ return checkMacOSKeychain();
8904
+ } else {
8905
+ return checkCredentialsFile();
8906
+ }
8907
+ }
8908
+ function checkMacOSKeychain() {
8909
+ try {
8910
+ const credentialsJson = execSync5(
8911
+ 'security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null',
8912
+ { encoding: "utf-8" }
8913
+ ).trim();
8914
+ if (!credentialsJson) {
8915
+ logger.debug("[claude-max] No credentials found in macOS keychain");
8916
+ return false;
8917
+ }
8918
+ const credentials = JSON.parse(credentialsJson);
8919
+ const hasOauth = !!credentials.claudeAiOauth?.accessToken;
8920
+ logger.debug("[claude-max] macOS keychain credentials check", {
8921
+ hasOauth,
8922
+ hasRefreshToken: !!credentials.claudeAiOauth?.refreshToken
8923
+ });
8924
+ return hasOauth;
8925
+ } catch (error) {
8926
+ logger.debug("[claude-max] Error checking macOS keychain", {
8927
+ error: error instanceof Error ? error.message : String(error)
8928
+ });
8929
+ return false;
8930
+ }
8931
+ }
8932
+ function checkCredentialsFile() {
8933
+ try {
8934
+ const credentialsPath = join8(homedir5(), ".claude", ".credentials.json");
8935
+ if (!existsSync6(credentialsPath)) {
8936
+ logger.debug("[claude-max] Credentials file not found", { path: credentialsPath });
8937
+ return false;
8938
+ }
8939
+ const credentialsJson = readFileSync5(credentialsPath, "utf-8");
8940
+ const credentials = JSON.parse(credentialsJson);
8941
+ const hasOauth = !!credentials.claudeAiOauth?.accessToken;
8942
+ logger.debug("[claude-max] Credentials file check", {
8943
+ path: credentialsPath,
8944
+ hasOauth,
8945
+ hasRefreshToken: !!credentials.claudeAiOauth?.refreshToken
8946
+ });
8947
+ return hasOauth;
8948
+ } catch (error) {
8949
+ logger.debug("[claude-max] Error checking credentials file", {
8950
+ error: error instanceof Error ? error.message : String(error)
8951
+ });
8952
+ return false;
8953
+ }
8954
+ }
8955
+ var init_claude_max = __esm({
8956
+ "src/utils/claude-max.ts"() {
8957
+ "use strict";
8958
+ init_logger();
8959
+ }
8960
+ });
8961
+
8962
+ // src/utils/settings-loader.ts
8963
+ import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "fs";
8964
+ import { join as join9 } from "path";
8965
+ function loadSupatestSettings(cwd) {
8966
+ const settingsPath = join9(cwd, ".supatest", "settings.json");
8967
+ if (!existsSync7(settingsPath)) {
8968
+ return {};
8969
+ }
8970
+ try {
8971
+ const content = readFileSync6(settingsPath, "utf-8");
8972
+ return JSON.parse(content);
8973
+ } catch (error) {
8974
+ console.warn(
8975
+ `Warning: Failed to load settings from ${settingsPath}:`,
8976
+ error instanceof Error ? error.message : String(error)
8977
+ );
8978
+ return {};
8979
+ }
8980
+ }
8981
+ function saveSupatestSettings(cwd, settings) {
8982
+ const settingsDir = join9(cwd, ".supatest");
8983
+ const settingsPath = join9(settingsDir, "settings.json");
8984
+ try {
8985
+ if (!existsSync7(settingsDir)) {
8986
+ mkdirSync3(settingsDir, { recursive: true });
8987
+ }
8988
+ const existingSettings = loadSupatestSettings(cwd);
8989
+ const mergedSettings = {
8990
+ ...existingSettings,
8991
+ ...settings,
8992
+ // Preserve nested objects like permissions and hooks
8993
+ permissions: {
8994
+ ...existingSettings.permissions,
8995
+ ...settings.permissions
8996
+ },
8997
+ hooks: {
8998
+ ...existingSettings.hooks,
8999
+ ...settings.hooks
9000
+ }
9001
+ };
9002
+ writeFileSync2(settingsPath, JSON.stringify(mergedSettings, null, 2), "utf-8");
9003
+ } catch (error) {
9004
+ console.warn(
9005
+ `Warning: Failed to save settings to ${settingsPath}:`,
9006
+ error instanceof Error ? error.message : String(error)
9007
+ );
9008
+ }
9009
+ }
9010
+ var init_settings_loader = __esm({
9011
+ "src/utils/settings-loader.ts"() {
9012
+ "use strict";
9013
+ }
9014
+ });
9015
+
8923
9016
  // src/ui/types/auth.ts
8924
9017
  var init_auth = __esm({
8925
9018
  "src/ui/types/auth.ts"() {
@@ -8928,8 +9021,8 @@ var init_auth = __esm({
8928
9021
  });
8929
9022
 
8930
9023
  // src/ui/components/AuthBanner.tsx
8931
- import { Box as Box13, Text as Text11 } from "ink";
8932
- import React15 from "react";
9024
+ import { Box as Box14, Text as Text12 } from "ink";
9025
+ import React16 from "react";
8933
9026
  var AuthBanner;
8934
9027
  var init_AuthBanner = __esm({
8935
9028
  "src/ui/components/AuthBanner.tsx"() {
@@ -8941,10 +9034,10 @@ var init_AuthBanner = __esm({
8941
9034
  return null;
8942
9035
  }
8943
9036
  if (authState === "authenticating" /* Authenticating */) {
8944
- return /* @__PURE__ */ React15.createElement(Box13, { marginBottom: 0, paddingX: 1 }, /* @__PURE__ */ React15.createElement(Text11, { color: theme.text.info }, "Authenticating..."));
9037
+ return /* @__PURE__ */ React16.createElement(Box14, { marginBottom: 0, paddingX: 1 }, /* @__PURE__ */ React16.createElement(Text12, { color: theme.text.info }, "Authenticating..."));
8945
9038
  }
8946
- return /* @__PURE__ */ React15.createElement(
8947
- Box13,
9039
+ return /* @__PURE__ */ React16.createElement(
9040
+ Box14,
8948
9041
  {
8949
9042
  borderColor: theme.text.warning,
8950
9043
  borderStyle: "round",
@@ -8952,10 +9045,10 @@ var init_AuthBanner = __esm({
8952
9045
  paddingX: 1,
8953
9046
  paddingY: 0
8954
9047
  },
8955
- /* @__PURE__ */ React15.createElement(Text11, { bold: true, color: theme.text.warning }, "Not logged in"),
8956
- /* @__PURE__ */ React15.createElement(Text11, { color: theme.text.dim }, " - Type "),
8957
- /* @__PURE__ */ React15.createElement(Text11, { color: theme.text.info }, "/login"),
8958
- /* @__PURE__ */ React15.createElement(Text11, { color: theme.text.dim }, " to authenticate")
9048
+ /* @__PURE__ */ React16.createElement(Text12, { bold: true, color: theme.text.warning }, "Not logged in"),
9049
+ /* @__PURE__ */ React16.createElement(Text12, { color: theme.text.dim }, " - Type "),
9050
+ /* @__PURE__ */ React16.createElement(Text12, { color: theme.text.info }, "/login"),
9051
+ /* @__PURE__ */ React16.createElement(Text12, { color: theme.text.dim }, " to authenticate")
8959
9052
  );
8960
9053
  };
8961
9054
  }
@@ -9177,7 +9270,7 @@ var init_mouse = __esm({
9177
9270
 
9178
9271
  // src/ui/contexts/KeypressContext.tsx
9179
9272
  import { useStdin as useStdin2 } from "ink";
9180
- import React16, {
9273
+ import React17, {
9181
9274
  createContext as createContext2,
9182
9275
  useCallback as useCallback2,
9183
9276
  useContext as useContext2,
@@ -9500,7 +9593,7 @@ function KeypressProvider({
9500
9593
  }
9501
9594
  };
9502
9595
  }, [stdin, setRawMode, config2, debugKeystrokeLogging, broadcast]);
9503
- return /* @__PURE__ */ React16.createElement(KeypressContext.Provider, { value: { subscribe, unsubscribe } }, children);
9596
+ return /* @__PURE__ */ React17.createElement(KeypressContext.Provider, { value: { subscribe, unsubscribe } }, children);
9504
9597
  }
9505
9598
  var BACKSLASH_ENTER_TIMEOUT, ESC_TIMEOUT, PASTE_TIMEOUT, KEY_INFO_MAP, kUTF16SurrogateThreshold, MAC_ALT_KEY_CHARACTER_MAP, KeypressContext;
9506
9599
  var init_KeypressContext = __esm({
@@ -9632,8 +9725,8 @@ var init_useKeypress = __esm({
9632
9725
  });
9633
9726
 
9634
9727
  // src/ui/components/AuthDialog.tsx
9635
- import { Box as Box14, Text as Text12 } from "ink";
9636
- import React17 from "react";
9728
+ import { Box as Box15, Text as Text13 } from "ink";
9729
+ import React18 from "react";
9637
9730
  var AuthDialog;
9638
9731
  var init_AuthDialog = __esm({
9639
9732
  "src/ui/components/AuthDialog.tsx"() {
@@ -9649,8 +9742,8 @@ var init_AuthDialog = __esm({
9649
9742
  },
9650
9743
  { isActive: true }
9651
9744
  );
9652
- return /* @__PURE__ */ React17.createElement(
9653
- Box14,
9745
+ return /* @__PURE__ */ React18.createElement(
9746
+ Box15,
9654
9747
  {
9655
9748
  borderColor: theme.border.accent,
9656
9749
  borderStyle: "round",
@@ -9658,18 +9751,18 @@ var init_AuthDialog = __esm({
9658
9751
  paddingX: 2,
9659
9752
  paddingY: 1
9660
9753
  },
9661
- /* @__PURE__ */ React17.createElement(Text12, { bold: true, color: theme.text.primary }, "Welcome to Supatest CLI"),
9662
- /* @__PURE__ */ React17.createElement(Box14, { marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text12, { color: theme.text.secondary }, "Authentication is required to use Supatest CLI.")),
9663
- /* @__PURE__ */ React17.createElement(Box14, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React17.createElement(Box14, null, /* @__PURE__ */ React17.createElement(Text12, { color: theme.text.accent }, ">", " "), /* @__PURE__ */ React17.createElement(Text12, { color: theme.text.primary }, "[L] Login with browser"))),
9664
- /* @__PURE__ */ React17.createElement(Box14, { marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text12, { color: theme.text.dim, italic: true }, "Press Enter or L to login"))
9754
+ /* @__PURE__ */ React18.createElement(Text13, { bold: true, color: theme.text.primary }, "Welcome to Supatest CLI"),
9755
+ /* @__PURE__ */ React18.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React18.createElement(Text13, { color: theme.text.secondary }, "Authentication is required to use Supatest CLI.")),
9756
+ /* @__PURE__ */ React18.createElement(Box15, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React18.createElement(Box15, null, /* @__PURE__ */ React18.createElement(Text13, { color: theme.text.accent }, ">", " "), /* @__PURE__ */ React18.createElement(Text13, { color: theme.text.primary }, "[L] Login with browser"))),
9757
+ /* @__PURE__ */ React18.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React18.createElement(Text13, { color: theme.text.dim, italic: true }, "Press Enter or L to login"))
9665
9758
  );
9666
9759
  };
9667
9760
  }
9668
9761
  });
9669
9762
 
9670
9763
  // src/ui/components/FeedbackDialog.tsx
9671
- import { Box as Box15, Text as Text13, useInput } from "ink";
9672
- import React18, { useState as useState5 } from "react";
9764
+ import { Box as Box16, Text as Text14, useInput } from "ink";
9765
+ import React19, { useState as useState5 } from "react";
9673
9766
  var CATEGORY_ORDER, FeedbackDialog;
9674
9767
  var init_FeedbackDialog = __esm({
9675
9768
  "src/ui/components/FeedbackDialog.tsx"() {
@@ -9764,21 +9857,21 @@ var init_FeedbackDialog = __esm({
9764
9857
  });
9765
9858
  const renderDescription = () => {
9766
9859
  if (!description && focus !== "description") {
9767
- return /* @__PURE__ */ React18.createElement(Text13, { color: theme.text.dim, italic: true }, "Enter your feedback...");
9860
+ return /* @__PURE__ */ React19.createElement(Text14, { color: theme.text.dim, italic: true }, "Enter your feedback...");
9768
9861
  }
9769
9862
  if (focus === "description") {
9770
9863
  const before = description.slice(0, cursorPosition);
9771
9864
  const charAtCursor = description[cursorPosition] || " ";
9772
9865
  const after = description.slice(cursorPosition + 1);
9773
9866
  if (!description) {
9774
- return /* @__PURE__ */ React18.createElement(Text13, null, /* @__PURE__ */ React18.createElement(Text13, { inverse: true }, " "), /* @__PURE__ */ React18.createElement(Text13, { color: theme.text.dim, italic: true }, "Enter your feedback..."));
9867
+ return /* @__PURE__ */ React19.createElement(Text14, null, /* @__PURE__ */ React19.createElement(Text14, { inverse: true }, " "), /* @__PURE__ */ React19.createElement(Text14, { color: theme.text.dim, italic: true }, "Enter your feedback..."));
9775
9868
  }
9776
- return /* @__PURE__ */ React18.createElement(Text13, { color: theme.text.primary }, before, /* @__PURE__ */ React18.createElement(Text13, { inverse: true }, charAtCursor), after);
9869
+ return /* @__PURE__ */ React19.createElement(Text14, { color: theme.text.primary }, before, /* @__PURE__ */ React19.createElement(Text14, { inverse: true }, charAtCursor), after);
9777
9870
  }
9778
- return /* @__PURE__ */ React18.createElement(Text13, { color: theme.text.primary }, description);
9871
+ return /* @__PURE__ */ React19.createElement(Text14, { color: theme.text.primary }, description);
9779
9872
  };
9780
- return /* @__PURE__ */ React18.createElement(
9781
- Box15,
9873
+ return /* @__PURE__ */ React19.createElement(
9874
+ Box16,
9782
9875
  {
9783
9876
  borderColor: theme.border.accent,
9784
9877
  borderStyle: "round",
@@ -9786,18 +9879,18 @@ var init_FeedbackDialog = __esm({
9786
9879
  paddingX: 2,
9787
9880
  paddingY: 1
9788
9881
  },
9789
- /* @__PURE__ */ React18.createElement(Text13, { bold: true, color: theme.text.accent }, "Report Issue"),
9790
- /* @__PURE__ */ React18.createElement(Box15, { marginTop: 1 }),
9791
- /* @__PURE__ */ React18.createElement(Text13, { bold: true, color: theme.text.secondary }, "Category:"),
9792
- /* @__PURE__ */ React18.createElement(Box15, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, CATEGORY_ORDER.map((category) => {
9882
+ /* @__PURE__ */ React19.createElement(Text14, { bold: true, color: theme.text.accent }, "Report Issue"),
9883
+ /* @__PURE__ */ React19.createElement(Box16, { marginTop: 1 }),
9884
+ /* @__PURE__ */ React19.createElement(Text14, { bold: true, color: theme.text.secondary }, "Category:"),
9885
+ /* @__PURE__ */ React19.createElement(Box16, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, CATEGORY_ORDER.map((category) => {
9793
9886
  const isSelected = selectedCategory === category;
9794
9887
  const info = FEEDBACK_CATEGORIES[category];
9795
- return /* @__PURE__ */ React18.createElement(Box15, { flexDirection: "row", key: category }, /* @__PURE__ */ React18.createElement(Text13, { color: isSelected && focus === "category" ? theme.text.accent : theme.text.dim }, isSelected ? "\u25B6 " : " "), /* @__PURE__ */ React18.createElement(Text13, { color: isSelected ? theme.text.primary : theme.text.dim }, info.label), /* @__PURE__ */ React18.createElement(Text13, { color: theme.text.dim }, " ", info.description));
9888
+ return /* @__PURE__ */ React19.createElement(Box16, { flexDirection: "row", key: category }, /* @__PURE__ */ React19.createElement(Text14, { color: isSelected && focus === "category" ? theme.text.accent : theme.text.dim }, isSelected ? "\u25B6 " : " "), /* @__PURE__ */ React19.createElement(Text14, { color: isSelected ? theme.text.primary : theme.text.dim }, info.label), /* @__PURE__ */ React19.createElement(Text14, { color: theme.text.dim }, " ", info.description));
9796
9889
  })),
9797
- /* @__PURE__ */ React18.createElement(Box15, { marginTop: 1 }),
9798
- /* @__PURE__ */ React18.createElement(Text13, { bold: true, color: theme.text.secondary }, "Description:"),
9799
- /* @__PURE__ */ React18.createElement(
9800
- Box15,
9890
+ /* @__PURE__ */ React19.createElement(Box16, { marginTop: 1 }),
9891
+ /* @__PURE__ */ React19.createElement(Text14, { bold: true, color: theme.text.secondary }, "Description:"),
9892
+ /* @__PURE__ */ React19.createElement(
9893
+ Box16,
9801
9894
  {
9802
9895
  borderColor: focus === "description" ? theme.border.accent : theme.border.default,
9803
9896
  borderStyle: "round",
@@ -9808,8 +9901,8 @@ var init_FeedbackDialog = __esm({
9808
9901
  },
9809
9902
  renderDescription()
9810
9903
  ),
9811
- /* @__PURE__ */ React18.createElement(Box15, { marginTop: 1 }),
9812
- /* @__PURE__ */ React18.createElement(Text13, { color: theme.text.dim }, /* @__PURE__ */ React18.createElement(Text13, { bold: true }, "\u2191\u2193"), " category \u2022", " ", /* @__PURE__ */ React18.createElement(Text13, { bold: true }, "Tab"), " to description \u2022", " ", /* @__PURE__ */ React18.createElement(Text13, { bold: true }, "Enter"), " submit \u2022", " ", /* @__PURE__ */ React18.createElement(Text13, { bold: true }, "ESC"), " cancel")
9904
+ /* @__PURE__ */ React19.createElement(Box16, { marginTop: 1 }),
9905
+ /* @__PURE__ */ React19.createElement(Text14, { color: theme.text.dim }, /* @__PURE__ */ React19.createElement(Text14, { bold: true }, "\u2191\u2193"), " category \u2022", " ", /* @__PURE__ */ React19.createElement(Text14, { bold: true }, "Tab"), " to description \u2022", " ", /* @__PURE__ */ React19.createElement(Text14, { bold: true }, "Enter"), " submit \u2022", " ", /* @__PURE__ */ React19.createElement(Text14, { bold: true }, "ESC"), " cancel")
9813
9906
  );
9814
9907
  };
9815
9908
  }
@@ -9918,8 +10011,8 @@ var init_context_builder = __esm({
9918
10011
  });
9919
10012
 
9920
10013
  // src/ui/components/RunSelector.tsx
9921
- import { Box as Box16, Text as Text14, useInput as useInput2 } from "ink";
9922
- import React19, { useEffect as useEffect6, useState as useState6 } from "react";
10014
+ import { Box as Box17, Text as Text15, useInput as useInput2 } from "ink";
10015
+ import React20, { useEffect as useEffect6, useState as useState6 } from "react";
9923
10016
  function formatRelativeTime(date) {
9924
10017
  const now = /* @__PURE__ */ new Date();
9925
10018
  const diffMs = now.getTime() - date.getTime();
@@ -10013,13 +10106,13 @@ var init_RunSelector = __esm({
10013
10106
  }
10014
10107
  });
10015
10108
  if (error) {
10016
- return /* @__PURE__ */ React19.createElement(Box16, { borderColor: "red", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React19.createElement(Text14, { bold: true, color: "red" }, "Error Loading Runs"), /* @__PURE__ */ React19.createElement(Text14, { color: theme.text.dim }, error), /* @__PURE__ */ React19.createElement(Box16, { marginTop: 1 }, /* @__PURE__ */ React19.createElement(Text14, { color: theme.text.dim }, "Press ", /* @__PURE__ */ React19.createElement(Text14, { bold: true }, "ESC"), " to cancel")));
10109
+ return /* @__PURE__ */ React20.createElement(Box17, { borderColor: "red", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React20.createElement(Text15, { bold: true, color: "red" }, "Error Loading Runs"), /* @__PURE__ */ React20.createElement(Text15, { color: theme.text.dim }, error), /* @__PURE__ */ React20.createElement(Box17, { marginTop: 1 }, /* @__PURE__ */ React20.createElement(Text15, { color: theme.text.dim }, "Press ", /* @__PURE__ */ React20.createElement(Text15, { bold: true }, "ESC"), " to cancel")));
10017
10110
  }
10018
10111
  if (allRuns.length === 0 && isLoading) {
10019
- return /* @__PURE__ */ React19.createElement(Box16, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React19.createElement(Text14, { bold: true, color: "cyan" }, "Loading Runs..."), /* @__PURE__ */ React19.createElement(Text14, { color: theme.text.dim }, "Fetching test runs from the server"));
10112
+ return /* @__PURE__ */ React20.createElement(Box17, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React20.createElement(Text15, { bold: true, color: "cyan" }, "Loading Runs..."), /* @__PURE__ */ React20.createElement(Text15, { color: theme.text.dim }, "Fetching test runs from the server"));
10020
10113
  }
10021
10114
  if (allRuns.length === 0 && !isLoading) {
10022
- return /* @__PURE__ */ React19.createElement(Box16, { borderColor: "yellow", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React19.createElement(Text14, { bold: true, color: "yellow" }, "No Runs Found"), /* @__PURE__ */ React19.createElement(Text14, { color: theme.text.dim }, "No test runs available. Run your tests with the Supatest reporter first."), /* @__PURE__ */ React19.createElement(Box16, { marginTop: 1 }, /* @__PURE__ */ React19.createElement(Text14, { color: theme.text.dim }, "Press ", /* @__PURE__ */ React19.createElement(Text14, { bold: true }, "ESC"), " to cancel")));
10115
+ return /* @__PURE__ */ React20.createElement(Box17, { borderColor: "yellow", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React20.createElement(Text15, { bold: true, color: "yellow" }, "No Runs Found"), /* @__PURE__ */ React20.createElement(Text15, { color: theme.text.dim }, "No test runs available. Run your tests with the Supatest reporter first."), /* @__PURE__ */ React20.createElement(Box17, { marginTop: 1 }, /* @__PURE__ */ React20.createElement(Text15, { color: theme.text.dim }, "Press ", /* @__PURE__ */ React20.createElement(Text15, { bold: true }, "ESC"), " to cancel")));
10023
10116
  }
10024
10117
  let startIndex;
10025
10118
  let endIndex;
@@ -10036,7 +10129,7 @@ var init_RunSelector = __esm({
10036
10129
  }
10037
10130
  }
10038
10131
  const visibleRuns = allRuns.slice(startIndex, endIndex);
10039
- return /* @__PURE__ */ React19.createElement(Box16, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React19.createElement(Box16, { marginBottom: 1 }, /* @__PURE__ */ React19.createElement(Text14, { bold: true, color: "cyan" }, "Select a Run to Fix")), /* @__PURE__ */ React19.createElement(Box16, { flexDirection: "column" }, visibleRuns.map((run, index) => {
10132
+ return /* @__PURE__ */ React20.createElement(Box17, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React20.createElement(Box17, { marginBottom: 1 }, /* @__PURE__ */ React20.createElement(Text15, { bold: true, color: "cyan" }, "Select a Run to Fix")), /* @__PURE__ */ React20.createElement(Box17, { flexDirection: "column" }, visibleRuns.map((run, index) => {
10040
10133
  const actualIndex = startIndex + index;
10041
10134
  const isSelected = actualIndex === selectedIndex;
10042
10135
  const branch = run.git?.branch || "unknown";
@@ -10048,15 +10141,15 @@ var init_RunSelector = __esm({
10048
10141
  const hasFailures = failed > 0;
10049
10142
  const indicator = isSelected ? "\u25B6 " : " ";
10050
10143
  const bgColor = isSelected ? theme.text.accent : void 0;
10051
- return /* @__PURE__ */ React19.createElement(Box16, { key: run.id, width: "100%" }, /* @__PURE__ */ React19.createElement(Text14, { backgroundColor: bgColor, bold: isSelected, color: isSelected ? "black" : theme.text.primary }, indicator), /* @__PURE__ */ React19.createElement(Text14, { backgroundColor: bgColor, bold: isSelected, color: isSelected ? "black" : theme.text.primary }, branch, commit && /* @__PURE__ */ React19.createElement(Text14, { color: theme.text.dim }, " @ ", commit)), /* @__PURE__ */ React19.createElement(Text14, null, " "), /* @__PURE__ */ React19.createElement(Text14, { backgroundColor: bgColor, color: hasFailures ? isSelected ? "black" : "red" : isSelected ? "black" : "green" }, failed, " failed"), /* @__PURE__ */ React19.createElement(Text14, { backgroundColor: bgColor, color: isSelected ? "black" : theme.text.dim }, " / ", total, " total"), /* @__PURE__ */ React19.createElement(Text14, null, " "), /* @__PURE__ */ React19.createElement(Text14, { backgroundColor: bgColor, color: isSelected ? "black" : theme.text.dim }, timeAgo), /* @__PURE__ */ React19.createElement(Text14, null, " "), /* @__PURE__ */ React19.createElement(Text14, { backgroundColor: bgColor, color: isSelected ? "black" : theme.text.dim }, source));
10052
- })), /* @__PURE__ */ React19.createElement(Box16, { flexDirection: "column", marginTop: 1 }, (allRuns.length > VISIBLE_ITEMS || totalRuns > allRuns.length) && /* @__PURE__ */ React19.createElement(Box16, { marginBottom: 1 }, /* @__PURE__ */ React19.createElement(Text14, { color: "yellow" }, "Showing ", startIndex + 1, "-", endIndex, " of ", totalRuns || allRuns.length, " runs", hasMore && !isLoading && /* @__PURE__ */ React19.createElement(Text14, { color: theme.text.dim }, " \u2022 Scroll for more"))), /* @__PURE__ */ React19.createElement(Box16, null, /* @__PURE__ */ React19.createElement(Text14, { color: theme.text.dim }, "Use ", /* @__PURE__ */ React19.createElement(Text14, { bold: true }, "\u2191\u2193"), " to navigate \u2022 ", /* @__PURE__ */ React19.createElement(Text14, { bold: true }, "Enter"), " to select \u2022 ", /* @__PURE__ */ React19.createElement(Text14, { bold: true }, "ESC"), " to cancel")), isLoading && /* @__PURE__ */ React19.createElement(Box16, { marginTop: 1 }, /* @__PURE__ */ React19.createElement(Text14, { color: "cyan" }, "Loading more runs..."))));
10144
+ return /* @__PURE__ */ React20.createElement(Box17, { key: run.id, width: "100%" }, /* @__PURE__ */ React20.createElement(Text15, { backgroundColor: bgColor, bold: isSelected, color: isSelected ? "black" : theme.text.primary }, indicator), /* @__PURE__ */ React20.createElement(Text15, { backgroundColor: bgColor, bold: isSelected, color: isSelected ? "black" : theme.text.primary }, branch, commit && /* @__PURE__ */ React20.createElement(Text15, { color: theme.text.dim }, " @ ", commit)), /* @__PURE__ */ React20.createElement(Text15, null, " "), /* @__PURE__ */ React20.createElement(Text15, { backgroundColor: bgColor, color: hasFailures ? isSelected ? "black" : "red" : isSelected ? "black" : "green" }, failed, " failed"), /* @__PURE__ */ React20.createElement(Text15, { backgroundColor: bgColor, color: isSelected ? "black" : theme.text.dim }, " / ", total, " total"), /* @__PURE__ */ React20.createElement(Text15, null, " "), /* @__PURE__ */ React20.createElement(Text15, { backgroundColor: bgColor, color: isSelected ? "black" : theme.text.dim }, timeAgo), /* @__PURE__ */ React20.createElement(Text15, null, " "), /* @__PURE__ */ React20.createElement(Text15, { backgroundColor: bgColor, color: isSelected ? "black" : theme.text.dim }, source));
10145
+ })), /* @__PURE__ */ React20.createElement(Box17, { flexDirection: "column", marginTop: 1 }, (allRuns.length > VISIBLE_ITEMS || totalRuns > allRuns.length) && /* @__PURE__ */ React20.createElement(Box17, { marginBottom: 1 }, /* @__PURE__ */ React20.createElement(Text15, { color: "yellow" }, "Showing ", startIndex + 1, "-", endIndex, " of ", totalRuns || allRuns.length, " runs", hasMore && !isLoading && /* @__PURE__ */ React20.createElement(Text15, { color: theme.text.dim }, " \u2022 Scroll for more"))), /* @__PURE__ */ React20.createElement(Box17, null, /* @__PURE__ */ React20.createElement(Text15, { color: theme.text.dim }, "Use ", /* @__PURE__ */ React20.createElement(Text15, { bold: true }, "\u2191\u2193"), " to navigate \u2022 ", /* @__PURE__ */ React20.createElement(Text15, { bold: true }, "Enter"), " to select \u2022 ", /* @__PURE__ */ React20.createElement(Text15, { bold: true }, "ESC"), " to cancel")), isLoading && /* @__PURE__ */ React20.createElement(Box17, { marginTop: 1 }, /* @__PURE__ */ React20.createElement(Text15, { color: "cyan" }, "Loading more runs..."))));
10053
10146
  };
10054
10147
  }
10055
10148
  });
10056
10149
 
10057
10150
  // src/ui/components/TestSelector.tsx
10058
- import { Box as Box17, Text as Text15, useInput as useInput3 } from "ink";
10059
- import React20, { useEffect as useEffect7, useState as useState7 } from "react";
10151
+ import { Box as Box18, Text as Text16, useInput as useInput3 } from "ink";
10152
+ import React21, { useEffect as useEffect7, useState as useState7 } from "react";
10060
10153
  var PAGE_SIZE2, VISIBLE_ITEMS2, TestSelector;
10061
10154
  var init_TestSelector = __esm({
10062
10155
  "src/ui/components/TestSelector.tsx"() {
@@ -10168,13 +10261,13 @@ var init_TestSelector = __esm({
10168
10261
  }
10169
10262
  });
10170
10263
  if (error) {
10171
- return /* @__PURE__ */ React20.createElement(Box17, { borderColor: "red", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React20.createElement(Text15, { bold: true, color: "red" }, "Error Loading Tests"), /* @__PURE__ */ React20.createElement(Text15, { color: theme.text.dim }, error), /* @__PURE__ */ React20.createElement(Box17, { marginTop: 1 }, /* @__PURE__ */ React20.createElement(Text15, { color: theme.text.dim }, "Press ", /* @__PURE__ */ React20.createElement(Text15, { bold: true }, "ESC"), " to go back")));
10264
+ return /* @__PURE__ */ React21.createElement(Box18, { borderColor: "red", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React21.createElement(Text16, { bold: true, color: "red" }, "Error Loading Tests"), /* @__PURE__ */ React21.createElement(Text16, { color: theme.text.dim }, error), /* @__PURE__ */ React21.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React21.createElement(Text16, { color: theme.text.dim }, "Press ", /* @__PURE__ */ React21.createElement(Text16, { bold: true }, "ESC"), " to go back")));
10172
10265
  }
10173
10266
  if (allTests.length === 0 && isLoading) {
10174
- return /* @__PURE__ */ React20.createElement(Box17, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React20.createElement(Text15, { bold: true, color: "cyan" }, "Loading Failed Tests..."), /* @__PURE__ */ React20.createElement(Text15, { color: theme.text.dim }, "Fetching failed tests for this run"));
10267
+ return /* @__PURE__ */ React21.createElement(Box18, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React21.createElement(Text16, { bold: true, color: "cyan" }, "Loading Failed Tests..."), /* @__PURE__ */ React21.createElement(Text16, { color: theme.text.dim }, "Fetching failed tests for this run"));
10175
10268
  }
10176
10269
  if (allTests.length === 0 && !isLoading) {
10177
- return /* @__PURE__ */ React20.createElement(Box17, { borderColor: "green", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React20.createElement(Text15, { bold: true, color: "green" }, "No Failed Tests"), /* @__PURE__ */ React20.createElement(Text15, { color: theme.text.dim }, "All tests passed in this run. Nothing to fix!"), /* @__PURE__ */ React20.createElement(Box17, { marginTop: 1 }, /* @__PURE__ */ React20.createElement(Text15, { color: theme.text.dim }, "Press ", /* @__PURE__ */ React20.createElement(Text15, { bold: true }, "ESC"), " to go back")));
10270
+ return /* @__PURE__ */ React21.createElement(Box18, { borderColor: "green", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React21.createElement(Text16, { bold: true, color: "green" }, "No Failed Tests"), /* @__PURE__ */ React21.createElement(Text16, { color: theme.text.dim }, "All tests passed in this run. Nothing to fix!"), /* @__PURE__ */ React21.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React21.createElement(Text16, { color: theme.text.dim }, "Press ", /* @__PURE__ */ React21.createElement(Text16, { bold: true }, "ESC"), " to go back")));
10178
10271
  }
10179
10272
  const testStartIndex = Math.max(0, cursorIndex - 1);
10180
10273
  const adjustedStart = isOnFixAll ? 0 : Math.max(0, testStartIndex - Math.floor(VISIBLE_ITEMS2 / 2));
@@ -10182,8 +10275,8 @@ var init_TestSelector = __esm({
10182
10275
  const visibleTests = allTests.slice(adjustedStart, adjustedEnd);
10183
10276
  const branch = run.git?.branch || "unknown";
10184
10277
  const commit = run.git?.commit?.slice(0, 7) || "";
10185
- return /* @__PURE__ */ React20.createElement(Box17, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React20.createElement(Box17, { marginBottom: 1 }, /* @__PURE__ */ React20.createElement(Text15, { bold: true, color: "cyan" }, "Run: ", branch, commit && /* @__PURE__ */ React20.createElement(Text15, { color: theme.text.dim }, " @ ", commit), /* @__PURE__ */ React20.createElement(Text15, { color: theme.text.dim }, " \u2022 "), /* @__PURE__ */ React20.createElement(Text15, { color: "red" }, allTests.length, " failed test", allTests.length !== 1 ? "s" : ""))), /* @__PURE__ */ React20.createElement(Box17, { flexDirection: "column" }, /* @__PURE__ */ React20.createElement(Box17, { marginBottom: 1 }, /* @__PURE__ */ React20.createElement(
10186
- Text15,
10278
+ return /* @__PURE__ */ React21.createElement(Box18, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React21.createElement(Box18, { marginBottom: 1 }, /* @__PURE__ */ React21.createElement(Text16, { bold: true, color: "cyan" }, "Run: ", branch, commit && /* @__PURE__ */ React21.createElement(Text16, { color: theme.text.dim }, " @ ", commit), /* @__PURE__ */ React21.createElement(Text16, { color: theme.text.dim }, " \u2022 "), /* @__PURE__ */ React21.createElement(Text16, { color: "red" }, allTests.length, " failed test", allTests.length !== 1 ? "s" : ""))), /* @__PURE__ */ React21.createElement(Box18, { flexDirection: "column" }, /* @__PURE__ */ React21.createElement(Box18, { marginBottom: 1 }, /* @__PURE__ */ React21.createElement(
10279
+ Text16,
10187
10280
  {
10188
10281
  backgroundColor: isOnFixAll ? theme.text.accent : void 0,
10189
10282
  bold: isOnFixAll,
@@ -10195,7 +10288,7 @@ var init_TestSelector = __esm({
10195
10288
  " Failed Test",
10196
10289
  allTests.length !== 1 ? "s" : "",
10197
10290
  "]"
10198
- )), /* @__PURE__ */ React20.createElement(Box17, { marginBottom: 1 }, /* @__PURE__ */ React20.createElement(Text15, { color: theme.text.dim }, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")), visibleTests.map((test, index) => {
10291
+ )), /* @__PURE__ */ React21.createElement(Box18, { marginBottom: 1 }, /* @__PURE__ */ React21.createElement(Text16, { color: theme.text.dim }, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")), visibleTests.map((test, index) => {
10199
10292
  const actualIndex = adjustedStart + index;
10200
10293
  const itemIndex = actualIndex + 1;
10201
10294
  const isSelected = itemIndex === cursorIndex;
@@ -10206,15 +10299,15 @@ var init_TestSelector = __esm({
10206
10299
  const title = test.title;
10207
10300
  const indicator = isSelected ? "\u25B6 " : " ";
10208
10301
  const bgColor = isSelected ? theme.text.accent : void 0;
10209
- return /* @__PURE__ */ React20.createElement(Box17, { key: test.id, marginBottom: 0 }, /* @__PURE__ */ React20.createElement(Text15, { backgroundColor: bgColor, bold: isSelected, color: isSelected ? "black" : theme.text.primary }, indicator), /* @__PURE__ */ React20.createElement(Text15, { backgroundColor: bgColor, color: isChecked ? "green" : isSelected ? "black" : theme.text.dim }, checkbox), /* @__PURE__ */ React20.createElement(Text15, null, " "), /* @__PURE__ */ React20.createElement(Text15, { backgroundColor: bgColor, bold: isSelected, color: isSelected ? "black" : theme.text.primary }, file, line && /* @__PURE__ */ React20.createElement(Text15, { color: isSelected ? "black" : theme.text.dim }, ":", line), /* @__PURE__ */ React20.createElement(Text15, { color: isSelected ? "black" : theme.text.dim }, " - "), title));
10210
- })), /* @__PURE__ */ React20.createElement(Box17, { flexDirection: "column", marginTop: 1 }, allTests.length > VISIBLE_ITEMS2 && /* @__PURE__ */ React20.createElement(Box17, { marginBottom: 1 }, /* @__PURE__ */ React20.createElement(Text15, { color: "yellow" }, "Showing ", adjustedStart + 1, "-", adjustedEnd, " of ", totalTests || allTests.length, " failed tests", hasMore && !isLoading && /* @__PURE__ */ React20.createElement(Text15, { color: theme.text.dim }, " \u2022 Scroll for more"))), /* @__PURE__ */ React20.createElement(Box17, null, /* @__PURE__ */ React20.createElement(Text15, { color: theme.text.dim }, /* @__PURE__ */ React20.createElement(Text15, { bold: true }, "\u2191\u2193"), " navigate \u2022 ", /* @__PURE__ */ React20.createElement(Text15, { bold: true }, "Space"), " toggle \u2022 ", /* @__PURE__ */ React20.createElement(Text15, { bold: true }, "a"), " all \u2022 ", /* @__PURE__ */ React20.createElement(Text15, { bold: true }, "n"), " none \u2022 ", /* @__PURE__ */ React20.createElement(Text15, { bold: true }, "Enter"), " fix selected")), selectedTests.size > 0 && /* @__PURE__ */ React20.createElement(Box17, { marginTop: 1 }, /* @__PURE__ */ React20.createElement(Text15, { color: "green" }, selectedTests.size, " test", selectedTests.size !== 1 ? "s" : "", " selected")), isLoading && /* @__PURE__ */ React20.createElement(Box17, { marginTop: 1 }, /* @__PURE__ */ React20.createElement(Text15, { color: "cyan" }, "Loading more tests..."))));
10302
+ return /* @__PURE__ */ React21.createElement(Box18, { key: test.id, marginBottom: 0 }, /* @__PURE__ */ React21.createElement(Text16, { backgroundColor: bgColor, bold: isSelected, color: isSelected ? "black" : theme.text.primary }, indicator), /* @__PURE__ */ React21.createElement(Text16, { backgroundColor: bgColor, color: isChecked ? "green" : isSelected ? "black" : theme.text.dim }, checkbox), /* @__PURE__ */ React21.createElement(Text16, null, " "), /* @__PURE__ */ React21.createElement(Text16, { backgroundColor: bgColor, bold: isSelected, color: isSelected ? "black" : theme.text.primary }, file, line && /* @__PURE__ */ React21.createElement(Text16, { color: isSelected ? "black" : theme.text.dim }, ":", line), /* @__PURE__ */ React21.createElement(Text16, { color: isSelected ? "black" : theme.text.dim }, " - "), title));
10303
+ })), /* @__PURE__ */ React21.createElement(Box18, { flexDirection: "column", marginTop: 1 }, allTests.length > VISIBLE_ITEMS2 && /* @__PURE__ */ React21.createElement(Box18, { marginBottom: 1 }, /* @__PURE__ */ React21.createElement(Text16, { color: "yellow" }, "Showing ", adjustedStart + 1, "-", adjustedEnd, " of ", totalTests || allTests.length, " failed tests", hasMore && !isLoading && /* @__PURE__ */ React21.createElement(Text16, { color: theme.text.dim }, " \u2022 Scroll for more"))), /* @__PURE__ */ React21.createElement(Box18, null, /* @__PURE__ */ React21.createElement(Text16, { color: theme.text.dim }, /* @__PURE__ */ React21.createElement(Text16, { bold: true }, "\u2191\u2193"), " navigate \u2022 ", /* @__PURE__ */ React21.createElement(Text16, { bold: true }, "Space"), " toggle \u2022 ", /* @__PURE__ */ React21.createElement(Text16, { bold: true }, "a"), " all \u2022 ", /* @__PURE__ */ React21.createElement(Text16, { bold: true }, "n"), " none \u2022 ", /* @__PURE__ */ React21.createElement(Text16, { bold: true }, "Enter"), " fix selected")), selectedTests.size > 0 && /* @__PURE__ */ React21.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React21.createElement(Text16, { color: "green" }, selectedTests.size, " test", selectedTests.size !== 1 ? "s" : "", " selected")), isLoading && /* @__PURE__ */ React21.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React21.createElement(Text16, { color: "cyan" }, "Loading more tests..."))));
10211
10304
  };
10212
10305
  }
10213
10306
  });
10214
10307
 
10215
10308
  // src/ui/components/FixFlow.tsx
10216
- import { Box as Box18, Text as Text16, useInput as useInput4 } from "ink";
10217
- import React21, { useState as useState8 } from "react";
10309
+ import { Box as Box19, Text as Text17, useInput as useInput4 } from "ink";
10310
+ import React22, { useState as useState8 } from "react";
10218
10311
  var FixFlow;
10219
10312
  var init_FixFlow = __esm({
10220
10313
  "src/ui/components/FixFlow.tsx"() {
@@ -10285,7 +10378,7 @@ var init_FixFlow = __esm({
10285
10378
  };
10286
10379
  switch (step) {
10287
10380
  case "select-run":
10288
- return /* @__PURE__ */ React21.createElement(
10381
+ return /* @__PURE__ */ React22.createElement(
10289
10382
  RunSelector,
10290
10383
  {
10291
10384
  apiClient,
@@ -10298,7 +10391,7 @@ var init_FixFlow = __esm({
10298
10391
  if (!selectedRun) {
10299
10392
  return null;
10300
10393
  }
10301
- return /* @__PURE__ */ React21.createElement(
10394
+ return /* @__PURE__ */ React22.createElement(
10302
10395
  TestSelector,
10303
10396
  {
10304
10397
  apiClient,
@@ -10309,13 +10402,13 @@ var init_FixFlow = __esm({
10309
10402
  );
10310
10403
  case "loading-details":
10311
10404
  if (loadError) {
10312
- return /* @__PURE__ */ React21.createElement(Box18, { borderColor: "red", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React21.createElement(Text16, { bold: true, color: "red" }, "Error Loading Test Details"), /* @__PURE__ */ React21.createElement(Text16, { color: theme.text.dim }, loadError), /* @__PURE__ */ React21.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React21.createElement(Text16, { color: theme.text.dim }, "Press ", /* @__PURE__ */ React21.createElement(Text16, { bold: true }, "ESC"), " to go back")));
10405
+ return /* @__PURE__ */ React22.createElement(Box19, { borderColor: "red", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React22.createElement(Text17, { bold: true, color: "red" }, "Error Loading Test Details"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, loadError), /* @__PURE__ */ React22.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, "Press ", /* @__PURE__ */ React22.createElement(Text17, { bold: true }, "ESC"), " to go back")));
10313
10406
  }
10314
- return /* @__PURE__ */ React21.createElement(Box18, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React21.createElement(Text16, { bold: true, color: "cyan" }, "Loading Test Details..."), /* @__PURE__ */ React21.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React21.createElement(Text16, { color: theme.text.dim }, "Fetching error messages, stack traces, and execution steps...")), /* @__PURE__ */ React21.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React21.createElement(Text16, { color: "yellow" }, loadingProgress.current, " / ", loadingProgress.total, " tests loaded")));
10407
+ return /* @__PURE__ */ React22.createElement(Box19, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React22.createElement(Text17, { bold: true, color: "cyan" }, "Loading Test Details..."), /* @__PURE__ */ React22.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, "Fetching error messages, stack traces, and execution steps...")), /* @__PURE__ */ React22.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text17, { color: "yellow" }, loadingProgress.current, " / ", loadingProgress.total, " tests loaded")));
10315
10408
  case "fixing":
10316
- return /* @__PURE__ */ React21.createElement(Box18, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React21.createElement(Text16, { bold: true, color: "cyan" }, "Fixing ", selectedTests.length, " Test", selectedTests.length !== 1 ? "s" : "", "..."), /* @__PURE__ */ React21.createElement(Box18, { marginTop: 1 }, selectedTests.map((test, index) => /* @__PURE__ */ React21.createElement(Box18, { key: test.id }, /* @__PURE__ */ React21.createElement(Text16, { color: theme.text.dim }, index + 1, ". ", test.file.split("/").pop(), ":", test.location?.line || "", " - ", test.title)))), /* @__PURE__ */ React21.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React21.createElement(Text16, { color: theme.text.dim }, "The agent will now analyze and fix each test...")));
10409
+ return /* @__PURE__ */ React22.createElement(Box19, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React22.createElement(Text17, { bold: true, color: "cyan" }, "Fixing ", selectedTests.length, " Test", selectedTests.length !== 1 ? "s" : "", "..."), /* @__PURE__ */ React22.createElement(Box19, { marginTop: 1 }, selectedTests.map((test, index) => /* @__PURE__ */ React22.createElement(Box19, { key: test.id }, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, index + 1, ". ", test.file.split("/").pop(), ":", test.location?.line || "", " - ", test.title)))), /* @__PURE__ */ React22.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, "The agent will now analyze and fix each test...")));
10317
10410
  case "complete":
10318
- return /* @__PURE__ */ React21.createElement(Box18, { borderColor: "green", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React21.createElement(Text16, { bold: true, color: "green" }, "Fix Complete"), /* @__PURE__ */ React21.createElement(Text16, { color: theme.text.dim }, "Press any key to continue..."));
10411
+ return /* @__PURE__ */ React22.createElement(Box19, { borderColor: "green", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React22.createElement(Text17, { bold: true, color: "green" }, "Fix Complete"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, "Press any key to continue..."));
10319
10412
  default:
10320
10413
  return null;
10321
10414
  }
@@ -10324,8 +10417,8 @@ var init_FixFlow = __esm({
10324
10417
  });
10325
10418
 
10326
10419
  // src/ui/components/HelpMenu.tsx
10327
- import { Box as Box19, Text as Text17, useInput as useInput5 } from "ink";
10328
- import React22, { useEffect as useEffect9, useState as useState9 } from "react";
10420
+ import { Box as Box20, Text as Text18, useInput as useInput5 } from "ink";
10421
+ import React23, { useEffect as useEffect9, useState as useState9 } from "react";
10329
10422
  var HelpMenu;
10330
10423
  var init_HelpMenu = __esm({
10331
10424
  "src/ui/components/HelpMenu.tsx"() {
@@ -10344,8 +10437,8 @@ var init_HelpMenu = __esm({
10344
10437
  onClose();
10345
10438
  }
10346
10439
  });
10347
- return /* @__PURE__ */ React22.createElement(
10348
- Box19,
10440
+ return /* @__PURE__ */ React23.createElement(
10441
+ Box20,
10349
10442
  {
10350
10443
  borderColor: theme.border.accent,
10351
10444
  borderStyle: "round",
@@ -10353,21 +10446,21 @@ var init_HelpMenu = __esm({
10353
10446
  paddingX: 2,
10354
10447
  paddingY: 1
10355
10448
  },
10356
- /* @__PURE__ */ React22.createElement(Text17, { bold: true, color: theme.text.accent }, "\u{1F4D6} Supatest AI CLI - Help"),
10357
- /* @__PURE__ */ React22.createElement(Box19, { marginTop: 1 }),
10358
- /* @__PURE__ */ React22.createElement(Text17, { bold: true, color: theme.text.secondary }, "Slash Commands:"),
10359
- /* @__PURE__ */ React22.createElement(Box19, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/help"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " or "), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/?"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Toggle this help menu")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/resume"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Resume a previous session")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/clear"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Clear message history")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/model"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Cycle through available models")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/setup"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Initial setup for Supatest CLI")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/discover"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Discover test framework and write to SUPATEST.md")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/feedback"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Report an issue or request a feature")), isAuthenticated ? /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/logout"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Log out of Supatest")) : /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/login"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Authenticate with Supatest")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/exit"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Exit the CLI"))),
10360
- customCommands.length > 0 && /* @__PURE__ */ React22.createElement(React22.Fragment, null, /* @__PURE__ */ React22.createElement(Box19, { marginTop: 1 }), /* @__PURE__ */ React22.createElement(Text17, { bold: true, color: theme.text.secondary }, "Project Commands:"), /* @__PURE__ */ React22.createElement(Box19, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, customCommands.slice(0, 5).map((cmd) => /* @__PURE__ */ React22.createElement(Text17, { key: cmd.name }, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/", cmd.name), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, cmd.description ? ` - ${cmd.description}` : ""))), customCommands.length > 5 && /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, "...and ", customCommands.length - 5, " more (use Tab to autocomplete)"))),
10361
- /* @__PURE__ */ React22.createElement(Box19, { marginTop: 1 }),
10362
- /* @__PURE__ */ React22.createElement(Text17, { bold: true, color: theme.text.secondary }, "Keyboard Shortcuts:"),
10363
- /* @__PURE__ */ React22.createElement(Box19, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "?"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " ", "- Toggle help (when input is empty)")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "Ctrl+H"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Toggle help")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "Ctrl+C"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " ", "- Exit (or clear input if not empty)")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "Ctrl+D"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Exit immediately")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "Ctrl+L"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Clear terminal screen")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "Ctrl+U"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Clear current input line")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "ESC"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Interrupt running agent")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "Shift+Enter"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Add new line in input")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "ctrl+o"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Toggle tool outputs")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "Ctrl+M"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Cycle through models"))),
10364
- /* @__PURE__ */ React22.createElement(Box19, { marginTop: 1 }),
10365
- /* @__PURE__ */ React22.createElement(Text17, { bold: true, color: theme.text.secondary }, "File References:"),
10366
- /* @__PURE__ */ React22.createElement(Box19, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "@filename"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " ", "- Reference a file (autocomplete with Tab)")), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, 'Example: "Fix the bug in @src/app.ts"')),
10367
- /* @__PURE__ */ React22.createElement(Box19, { marginTop: 1 }),
10368
- /* @__PURE__ */ React22.createElement(Text17, { bold: true, color: theme.text.secondary }, "Tips:"),
10369
- /* @__PURE__ */ React22.createElement(Box19, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, "\u2022 Press Enter to submit your task"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, "\u2022 Use Shift+Enter to write multi-line prompts"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, "\u2022 Drag and drop files into the terminal to add file paths"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, "\u2022 The agent will automatically run tools and fix issues"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, "\u2022 Use Ctrl+L to clear the terminal screen without clearing the messages history")),
10370
- /* @__PURE__ */ React22.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, "Press ", /* @__PURE__ */ React22.createElement(Text17, { bold: true }, "ESC"), " or ", /* @__PURE__ */ React22.createElement(Text17, { bold: true }, "?"), " to close"))
10449
+ /* @__PURE__ */ React23.createElement(Text18, { bold: true, color: theme.text.accent }, "\u{1F4D6} Supatest AI CLI - Help"),
10450
+ /* @__PURE__ */ React23.createElement(Box20, { marginTop: 1 }),
10451
+ /* @__PURE__ */ React23.createElement(Text18, { bold: true, color: theme.text.secondary }, "Slash Commands:"),
10452
+ /* @__PURE__ */ React23.createElement(Box20, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "/help"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " or "), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "/?"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Toggle this help menu")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "/resume"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Resume a previous session")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "/clear"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Clear message history")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "/model"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Cycle through available models")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "/provider"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Select LLM provider (Supatest Managed or Claude Max)")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "/setup"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Initial setup for Supatest CLI")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "/discover"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Discover test framework and write to SUPATEST.md")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "/feedback"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Report an issue or request a feature")), isAuthenticated ? /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "/logout"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Log out of Supatest")) : /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "/login"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Authenticate with Supatest")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "/exit"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Exit the CLI"))),
10453
+ customCommands.length > 0 && /* @__PURE__ */ React23.createElement(React23.Fragment, null, /* @__PURE__ */ React23.createElement(Box20, { marginTop: 1 }), /* @__PURE__ */ React23.createElement(Text18, { bold: true, color: theme.text.secondary }, "Project Commands:"), /* @__PURE__ */ React23.createElement(Box20, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, customCommands.slice(0, 5).map((cmd) => /* @__PURE__ */ React23.createElement(Text18, { key: cmd.name }, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "/", cmd.name), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, cmd.description ? ` - ${cmd.description}` : ""))), customCommands.length > 5 && /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, "...and ", customCommands.length - 5, " more (use Tab to autocomplete)"))),
10454
+ /* @__PURE__ */ React23.createElement(Box20, { marginTop: 1 }),
10455
+ /* @__PURE__ */ React23.createElement(Text18, { bold: true, color: theme.text.secondary }, "Keyboard Shortcuts:"),
10456
+ /* @__PURE__ */ React23.createElement(Box20, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "?"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " ", "- Toggle help (when input is empty)")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "Ctrl+H"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Toggle help")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "Ctrl+C"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " ", "- Exit (or clear input if not empty)")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "Ctrl+D"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Exit immediately")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "Ctrl+L"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Clear terminal screen")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "Ctrl+U"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Clear current input line")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "ESC"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Interrupt running agent")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "Shift+Enter"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Add new line in input")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "ctrl+o"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Toggle tool outputs")), /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "Ctrl+M"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " - Cycle through models"))),
10457
+ /* @__PURE__ */ React23.createElement(Box20, { marginTop: 1 }),
10458
+ /* @__PURE__ */ React23.createElement(Text18, { bold: true, color: theme.text.secondary }, "File References:"),
10459
+ /* @__PURE__ */ React23.createElement(Box20, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, /* @__PURE__ */ React23.createElement(Text18, null, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.accent }, "@filename"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, " ", "- Reference a file (autocomplete with Tab)")), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, 'Example: "Fix the bug in @src/app.ts"')),
10460
+ /* @__PURE__ */ React23.createElement(Box20, { marginTop: 1 }),
10461
+ /* @__PURE__ */ React23.createElement(Text18, { bold: true, color: theme.text.secondary }, "Tips:"),
10462
+ /* @__PURE__ */ React23.createElement(Box20, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, "\u2022 Press Enter to submit your task"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, "\u2022 Use Shift+Enter to write multi-line prompts"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, "\u2022 Drag and drop files into the terminal to add file paths"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, "\u2022 The agent will automatically run tools and fix issues"), /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, "\u2022 Use Ctrl+L to clear the terminal screen without clearing the messages history")),
10463
+ /* @__PURE__ */ React23.createElement(Box20, { marginTop: 1 }, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, "Press ", /* @__PURE__ */ React23.createElement(Text18, { bold: true }, "ESC"), " or ", /* @__PURE__ */ React23.createElement(Text18, { bold: true }, "?"), " to close"))
10371
10464
  );
10372
10465
  };
10373
10466
  }
@@ -10456,12 +10549,27 @@ var init_file_completion = __esm({
10456
10549
  });
10457
10550
 
10458
10551
  // src/ui/components/ModelSelector.tsx
10459
- import { Box as Box20, Text as Text18, useInput as useInput6 } from "ink";
10460
- import React23, { useState as useState10 } from "react";
10461
- function getNextModel(currentModel) {
10462
- const currentIndex = AVAILABLE_MODELS.findIndex((m) => m.id === currentModel);
10463
- const nextIndex = (currentIndex + 1) % AVAILABLE_MODELS.length;
10464
- return AVAILABLE_MODELS[nextIndex].id;
10552
+ import { Box as Box21, Text as Text19, useInput as useInput6 } from "ink";
10553
+ import React24, { useState as useState10 } from "react";
10554
+ function getAvailableModels(isClaudeMax) {
10555
+ if (isClaudeMax) {
10556
+ return AVAILABLE_MODELS.map((m) => ({
10557
+ id: m.id,
10558
+ name: m.name,
10559
+ description: m.description
10560
+ }));
10561
+ }
10562
+ return MODEL_TIERS.map((tier) => ({
10563
+ id: tier,
10564
+ name: getModelDisplayName(tier),
10565
+ description: tier === "small" ? "Fast & lightweight" : tier === "medium" ? "Balanced performance" : "Most capable"
10566
+ }));
10567
+ }
10568
+ function getNextModel(currentModel, isClaudeMax = false) {
10569
+ const models = getAvailableModels(isClaudeMax);
10570
+ const currentIndex = models.findIndex((m) => m.id === currentModel);
10571
+ const nextIndex = (currentIndex + 1) % models.length;
10572
+ return models[nextIndex].id;
10465
10573
  }
10466
10574
  var ModelSelector;
10467
10575
  var init_ModelSelector = __esm({
@@ -10472,27 +10580,29 @@ var init_ModelSelector = __esm({
10472
10580
  ModelSelector = ({
10473
10581
  currentModel,
10474
10582
  onSelect,
10475
- onCancel
10583
+ onCancel,
10584
+ isClaudeMax = false
10476
10585
  }) => {
10477
- const currentIndex = AVAILABLE_MODELS.findIndex((m) => m.id === currentModel);
10586
+ const models = getAvailableModels(isClaudeMax);
10587
+ const currentIndex = models.findIndex((m) => m.id === currentModel);
10478
10588
  const [selectedIndex, setSelectedIndex] = useState10(currentIndex >= 0 ? currentIndex : 0);
10479
10589
  useInput6((input, key) => {
10480
10590
  if (key.upArrow) {
10481
- setSelectedIndex((prev) => prev > 0 ? prev - 1 : AVAILABLE_MODELS.length - 1);
10591
+ setSelectedIndex((prev) => prev > 0 ? prev - 1 : models.length - 1);
10482
10592
  } else if (key.downArrow) {
10483
- setSelectedIndex((prev) => prev < AVAILABLE_MODELS.length - 1 ? prev + 1 : 0);
10593
+ setSelectedIndex((prev) => prev < models.length - 1 ? prev + 1 : 0);
10484
10594
  } else if (key.return) {
10485
- onSelect(AVAILABLE_MODELS[selectedIndex].id);
10595
+ onSelect(models[selectedIndex].id);
10486
10596
  } else if (key.escape || input === "q") {
10487
10597
  onCancel();
10488
10598
  }
10489
10599
  });
10490
- return /* @__PURE__ */ React23.createElement(Box20, { borderColor: theme.border.accent, borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React23.createElement(Box20, { marginBottom: 1 }, /* @__PURE__ */ React23.createElement(Text18, { bold: true, color: theme.text.accent }, "Select Model")), /* @__PURE__ */ React23.createElement(Box20, { flexDirection: "column" }, AVAILABLE_MODELS.map((model, index) => {
10600
+ return /* @__PURE__ */ React24.createElement(Box21, { borderColor: theme.border.accent, borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React24.createElement(Box21, { marginBottom: 1 }, /* @__PURE__ */ React24.createElement(Text19, { bold: true, color: theme.text.accent }, "Select Model")), /* @__PURE__ */ React24.createElement(Box21, { marginBottom: 1 }, /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.dim }, "Cost: ", /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.info }, "0.5x"), " (Small) \u2022 ", /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.info }, "1x"), " (Medium) \u2022 ", /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.info }, "2x"), " (Premium)")), /* @__PURE__ */ React24.createElement(Box21, { flexDirection: "column" }, models.map((model, index) => {
10491
10601
  const isSelected = index === selectedIndex;
10492
10602
  const isCurrent = model.id === currentModel;
10493
10603
  const indicator = isSelected ? "\u25B6 " : " ";
10494
- return /* @__PURE__ */ React23.createElement(Box20, { gap: 1, key: model.id }, /* @__PURE__ */ React23.createElement(
10495
- Text18,
10604
+ return /* @__PURE__ */ React24.createElement(Box21, { gap: 1, key: model.id }, /* @__PURE__ */ React24.createElement(
10605
+ Text19,
10496
10606
  {
10497
10607
  backgroundColor: isSelected ? theme.text.accent : void 0,
10498
10608
  bold: isSelected,
@@ -10500,15 +10610,15 @@ var init_ModelSelector = __esm({
10500
10610
  },
10501
10611
  indicator,
10502
10612
  model.name.padEnd(12)
10503
- ), /* @__PURE__ */ React23.createElement(
10504
- Text18,
10613
+ ), /* @__PURE__ */ React24.createElement(
10614
+ Text19,
10505
10615
  {
10506
10616
  backgroundColor: isSelected ? theme.text.accent : void 0,
10507
10617
  color: isSelected ? "black" : theme.text.dim
10508
10618
  },
10509
10619
  model.description
10510
- ), isCurrent && /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.success }, " (current)"));
10511
- })), /* @__PURE__ */ React23.createElement(Box20, { marginTop: 1 }, /* @__PURE__ */ React23.createElement(Text18, { color: theme.text.dim }, /* @__PURE__ */ React23.createElement(Text18, { bold: true }, "\u2191\u2193"), " navigate \u2022 ", /* @__PURE__ */ React23.createElement(Text18, { bold: true }, "Enter"), " select \u2022 ", /* @__PURE__ */ React23.createElement(Text18, { bold: true }, "ESC"), " cancel")));
10620
+ ), isCurrent && /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.success }, " (current)"));
10621
+ })), /* @__PURE__ */ React24.createElement(Box21, { marginTop: 1 }, /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.dim }, /* @__PURE__ */ React24.createElement(Text19, { bold: true }, "\u2191\u2193"), " navigate \u2022 ", /* @__PURE__ */ React24.createElement(Text19, { bold: true }, "Enter"), " select \u2022 ", /* @__PURE__ */ React24.createElement(Text19, { bold: true }, "ESC"), " cancel")));
10512
10622
  };
10513
10623
  }
10514
10624
  });
@@ -10516,8 +10626,8 @@ var init_ModelSelector = __esm({
10516
10626
  // src/ui/components/InputPrompt.tsx
10517
10627
  import path5 from "path";
10518
10628
  import chalk4 from "chalk";
10519
- import { Box as Box21, Text as Text19 } from "ink";
10520
- import React24, { forwardRef, useEffect as useEffect10, useImperativeHandle, useState as useState11 } from "react";
10629
+ import { Box as Box22, Text as Text20 } from "ink";
10630
+ import React25, { forwardRef, useEffect as useEffect10, useImperativeHandle, useState as useState11 } from "react";
10521
10631
  var InputPrompt;
10522
10632
  var init_InputPrompt = __esm({
10523
10633
  "src/ui/components/InputPrompt.tsx"() {
@@ -10537,7 +10647,8 @@ var init_InputPrompt = __esm({
10537
10647
  cwd,
10538
10648
  currentFolder,
10539
10649
  gitBranch,
10540
- onInputChange
10650
+ onInputChange,
10651
+ isClaudeMax = false
10541
10652
  }, ref) => {
10542
10653
  const { messages, agentMode, selectedModel, setSelectedModel, isAgentRunning, usageStats } = useSession();
10543
10654
  const [value, setValue] = useState11("");
@@ -10552,6 +10663,7 @@ var init_InputPrompt = __esm({
10552
10663
  { name: "/resume", desc: "Resume session" },
10553
10664
  { name: "/clear", desc: "Clear history" },
10554
10665
  { name: "/model", desc: "Change model" },
10666
+ { name: "/provider", desc: "Select LLM provider" },
10555
10667
  { name: "/fix", desc: "Fix failing tests" },
10556
10668
  { name: "/feedback", desc: "Report an issue" },
10557
10669
  { name: "/setup", desc: "Install Playwright browsers" },
@@ -10694,7 +10806,7 @@ var init_InputPrompt = __esm({
10694
10806
  }
10695
10807
  }
10696
10808
  if (key.shift && key.name === "m" && !isAgentRunning) {
10697
- setSelectedModel(getNextModel(selectedModel));
10809
+ setSelectedModel(getNextModel(selectedModel, isClaudeMax));
10698
10810
  return;
10699
10811
  }
10700
10812
  if (input === "?" && value.length === 0 && onHelpToggle) {
@@ -10783,8 +10895,8 @@ var init_InputPrompt = __esm({
10783
10895
  }
10784
10896
  charCount += lineLength + 1;
10785
10897
  }
10786
- return /* @__PURE__ */ React24.createElement(Box21, { flexDirection: "column", width: "100%" }, showSuggestions && /* @__PURE__ */ React24.createElement(
10787
- Box21,
10898
+ return /* @__PURE__ */ React25.createElement(Box22, { flexDirection: "column", width: "100%" }, showSuggestions && /* @__PURE__ */ React25.createElement(
10899
+ Box22,
10788
10900
  {
10789
10901
  borderColor: theme.border.default,
10790
10902
  borderStyle: "round",
@@ -10795,12 +10907,12 @@ var init_InputPrompt = __esm({
10795
10907
  suggestions.map((item, idx) => {
10796
10908
  const isSeparator = item.startsWith("\u2500\u2500\u2500\u2500\u2500");
10797
10909
  if (isSeparator) {
10798
- return /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.dim, key: item }, " ", item);
10910
+ return /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.dim, key: item }, " ", item);
10799
10911
  }
10800
- return /* @__PURE__ */ React24.createElement(Text19, { color: idx === activeSuggestion ? theme.text.accent : theme.text.dim, key: item }, idx === activeSuggestion ? "\u276F " : " ", item);
10912
+ return /* @__PURE__ */ React25.createElement(Text20, { color: idx === activeSuggestion ? theme.text.accent : theme.text.dim, key: item }, idx === activeSuggestion ? "\u276F " : " ", item);
10801
10913
  })
10802
- ), /* @__PURE__ */ React24.createElement(
10803
- Box21,
10914
+ ), /* @__PURE__ */ React25.createElement(
10915
+ Box22,
10804
10916
  {
10805
10917
  borderColor: disabled ? theme.border.default : theme.border.accent,
10806
10918
  borderStyle: "round",
@@ -10810,24 +10922,96 @@ var init_InputPrompt = __esm({
10810
10922
  paddingX: 1,
10811
10923
  width: "100%"
10812
10924
  },
10813
- /* @__PURE__ */ React24.createElement(Box21, { flexDirection: "row" }, /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.accent }, "\u276F "), /* @__PURE__ */ React24.createElement(Box21, { flexDirection: "column", flexGrow: 1 }, !hasContent && !disabled && /* @__PURE__ */ React24.createElement(Text19, null, chalk4.inverse(placeholder.slice(0, 1)), /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.dim, italic: true }, placeholder.slice(1))), lines.length > 0 && /* @__PURE__ */ React24.createElement(Box21, { flexDirection: "column" }, lines.map((line, idx) => {
10925
+ /* @__PURE__ */ React25.createElement(Box22, { flexDirection: "row" }, /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.accent }, "\u276F "), /* @__PURE__ */ React25.createElement(Box22, { flexDirection: "column", flexGrow: 1 }, !hasContent && !disabled && /* @__PURE__ */ React25.createElement(Text20, null, chalk4.inverse(placeholder.slice(0, 1)), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.dim, italic: true }, placeholder.slice(1))), lines.length > 0 && /* @__PURE__ */ React25.createElement(Box22, { flexDirection: "column" }, lines.map((line, idx) => {
10814
10926
  if (idx === cursorLine && !disabled) {
10815
10927
  const before = line.slice(0, cursorCol);
10816
10928
  const charAtCursor = line[cursorCol] || " ";
10817
10929
  const after = line.slice(cursorCol + 1);
10818
- return /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.primary, key: idx }, before, chalk4.inverse(charAtCursor), after);
10930
+ return /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.primary, key: idx }, before, chalk4.inverse(charAtCursor), after);
10819
10931
  }
10820
- return /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.primary, key: idx }, line);
10821
- })), !hasContent && disabled && /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.dim, italic: true }, "Waiting for agent to complete...")))
10822
- ), /* @__PURE__ */ React24.createElement(Box21, { justifyContent: "space-between", paddingX: 1 }, /* @__PURE__ */ React24.createElement(Box21, { gap: 2 }, /* @__PURE__ */ React24.createElement(Box21, null, /* @__PURE__ */ React24.createElement(Text19, { color: agentMode === "plan" ? theme.status.inProgress : theme.text.dim }, agentMode === "plan" ? "\u23F8 plan" : "\u25B6 build"), /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.dim }, " (shift+tab)")), /* @__PURE__ */ React24.createElement(Box21, null, /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.dim }, "model:"), /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.info }, getModelDisplayName(selectedModel)), /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.dim }, " (shift+m)"))), /* @__PURE__ */ React24.createElement(Box21, null, /* @__PURE__ */ React24.createElement(Text19, { color: usageStats && usageStats.contextPct >= 90 ? theme.text.error : usageStats && usageStats.contextPct >= 75 ? theme.text.warning : theme.text.dim }, usageStats?.contextPct ?? 0, "% context used"), /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.dim }, " ", "(", usageStats ? usageStats.inputTokens >= 1e3 ? `${(usageStats.inputTokens / 1e3).toFixed(1)}K` : usageStats.inputTokens : 0, " / ", usageStats ? usageStats.contextWindow >= 1e3 ? `${(usageStats.contextWindow / 1e3).toFixed(0)}K` : usageStats.contextWindow : "200K", ")"))));
10932
+ return /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.primary, key: idx }, line);
10933
+ })), !hasContent && disabled && /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.dim, italic: true }, "Waiting for agent to complete...")))
10934
+ ), /* @__PURE__ */ React25.createElement(Box22, { justifyContent: "space-between", paddingX: 1 }, /* @__PURE__ */ React25.createElement(Box22, { gap: 2 }, /* @__PURE__ */ React25.createElement(Box22, null, /* @__PURE__ */ React25.createElement(Text20, { color: agentMode === "plan" ? theme.status.inProgress : theme.text.dim }, agentMode === "plan" ? "\u23F8 plan" : "\u25B6 build"), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.dim }, " (shift+tab)")), /* @__PURE__ */ React25.createElement(Box22, null, /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.dim }, "model:"), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.info }, getModelDisplayName(selectedModel)), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.dim }, " (Cost: "), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.info }, getModelCostLabel(selectedModel)), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.dim }, ") (shift+m)"))), /* @__PURE__ */ React25.createElement(Box22, null, /* @__PURE__ */ React25.createElement(Text20, { color: usageStats && usageStats.contextPct >= 90 ? theme.text.error : usageStats && usageStats.contextPct >= 75 ? theme.text.warning : theme.text.dim }, usageStats?.contextPct ?? 0, "% context used"), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.dim }, " ", "(", usageStats ? usageStats.inputTokens >= 1e3 ? `${(usageStats.inputTokens / 1e3).toFixed(1)}K` : usageStats.inputTokens : 0, " / ", usageStats ? usageStats.contextWindow >= 1e3 ? `${(usageStats.contextWindow / 1e3).toFixed(0)}K` : usageStats.contextWindow : "200K", ")"))));
10823
10935
  });
10824
10936
  InputPrompt.displayName = "InputPrompt";
10825
10937
  }
10826
10938
  });
10827
10939
 
10940
+ // src/ui/components/ProviderSelector.tsx
10941
+ import { Box as Box23, Text as Text21, useInput as useInput7 } from "ink";
10942
+ import React26, { useState as useState12 } from "react";
10943
+ var PROVIDERS, ProviderSelector;
10944
+ var init_ProviderSelector = __esm({
10945
+ "src/ui/components/ProviderSelector.tsx"() {
10946
+ "use strict";
10947
+ init_theme();
10948
+ PROVIDERS = [
10949
+ {
10950
+ id: "supatest-managed",
10951
+ name: "Supatest Managed",
10952
+ description: "Uses Supatest API with available models"
10953
+ },
10954
+ {
10955
+ id: "claude-max",
10956
+ name: "Claude Max",
10957
+ description: "Direct Claude Max subscription (requires Claude Code login)"
10958
+ }
10959
+ ];
10960
+ ProviderSelector = ({
10961
+ currentProvider,
10962
+ onSelect,
10963
+ onCancel,
10964
+ claudeMaxAvailable
10965
+ }) => {
10966
+ const availableProviders = PROVIDERS.filter(
10967
+ (p) => p.id !== "claude-max" || claudeMaxAvailable
10968
+ );
10969
+ const currentIndex = availableProviders.findIndex(
10970
+ (p) => p.id === currentProvider
10971
+ );
10972
+ const [selectedIndex, setSelectedIndex] = useState12(
10973
+ currentIndex >= 0 ? currentIndex : 0
10974
+ );
10975
+ useInput7((input, key) => {
10976
+ if (key.upArrow) {
10977
+ setSelectedIndex(
10978
+ (prev) => prev > 0 ? prev - 1 : availableProviders.length - 1
10979
+ );
10980
+ } else if (key.downArrow) {
10981
+ setSelectedIndex(
10982
+ (prev) => prev < availableProviders.length - 1 ? prev + 1 : 0
10983
+ );
10984
+ } else if (key.return) {
10985
+ onSelect(availableProviders[selectedIndex].id);
10986
+ } else if (key.escape || input === "q") {
10987
+ onCancel();
10988
+ }
10989
+ });
10990
+ return /* @__PURE__ */ React26.createElement(Box23, { borderColor: theme.border.accent, borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React26.createElement(Box23, { marginBottom: 1 }, /* @__PURE__ */ React26.createElement(Text21, { bold: true, color: theme.text.accent }, "Select LLM Provider")), /* @__PURE__ */ React26.createElement(Box23, { flexDirection: "column" }, availableProviders.map((provider, index) => {
10991
+ const isSelected = index === selectedIndex;
10992
+ const isCurrent = provider.id === currentProvider;
10993
+ const indicator = isSelected ? "\u25B6 " : " ";
10994
+ return /* @__PURE__ */ React26.createElement(
10995
+ Text21,
10996
+ {
10997
+ backgroundColor: isSelected ? theme.text.accent : void 0,
10998
+ bold: isSelected,
10999
+ color: isSelected ? "black" : theme.text.primary,
11000
+ key: provider.id
11001
+ },
11002
+ indicator,
11003
+ provider.name,
11004
+ isCurrent && /* @__PURE__ */ React26.createElement(Text21, { color: isSelected ? "black" : theme.text.success }, " (current)"),
11005
+ /* @__PURE__ */ React26.createElement(Text21, { color: isSelected ? "black" : theme.text.dim }, " - ", provider.description)
11006
+ );
11007
+ })), /* @__PURE__ */ React26.createElement(Box23, { marginTop: 1 }, /* @__PURE__ */ React26.createElement(Text21, { color: theme.text.dim }, /* @__PURE__ */ React26.createElement(Text21, { bold: true }, "\u2191\u2193"), " navigate \u2022 ", /* @__PURE__ */ React26.createElement(Text21, { bold: true }, "Enter"), " select \u2022", " ", /* @__PURE__ */ React26.createElement(Text21, { bold: true }, "ESC"), " cancel")));
11008
+ };
11009
+ }
11010
+ });
11011
+
10828
11012
  // src/ui/components/SessionSelector.tsx
10829
- import { Box as Box22, Text as Text20, useInput as useInput7 } from "ink";
10830
- import React25, { useEffect as useEffect11, useState as useState12 } from "react";
11013
+ import { Box as Box24, Text as Text22, useInput as useInput8 } from "ink";
11014
+ import React27, { useEffect as useEffect11, useState as useState13 } from "react";
10831
11015
  function getSessionPrefix(authMethod) {
10832
11016
  return authMethod === "api-key" ? "[Team]" : "[Me]";
10833
11017
  }
@@ -10842,12 +11026,12 @@ var init_SessionSelector = __esm({
10842
11026
  onSelect,
10843
11027
  onCancel
10844
11028
  }) => {
10845
- const [allSessions, setAllSessions] = useState12([]);
10846
- const [selectedIndex, setSelectedIndex] = useState12(0);
10847
- const [isLoading, setIsLoading] = useState12(false);
10848
- const [hasMore, setHasMore] = useState12(true);
10849
- const [totalSessions, setTotalSessions] = useState12(0);
10850
- const [error, setError] = useState12(null);
11029
+ const [allSessions, setAllSessions] = useState13([]);
11030
+ const [selectedIndex, setSelectedIndex] = useState13(0);
11031
+ const [isLoading, setIsLoading] = useState13(false);
11032
+ const [hasMore, setHasMore] = useState13(true);
11033
+ const [totalSessions, setTotalSessions] = useState13(0);
11034
+ const [error, setError] = useState13(null);
10851
11035
  useEffect11(() => {
10852
11036
  loadMoreSessions();
10853
11037
  }, []);
@@ -10874,7 +11058,7 @@ var init_SessionSelector = __esm({
10874
11058
  setIsLoading(false);
10875
11059
  }
10876
11060
  };
10877
- useInput7((input, key) => {
11061
+ useInput8((input, key) => {
10878
11062
  if (allSessions.length === 0) {
10879
11063
  if (key.escape || input === "q") {
10880
11064
  onCancel();
@@ -10898,13 +11082,13 @@ var init_SessionSelector = __esm({
10898
11082
  }
10899
11083
  });
10900
11084
  if (error) {
10901
- return /* @__PURE__ */ React25.createElement(Box22, { borderColor: "red", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React25.createElement(Text20, { bold: true, color: "red" }, "Error Loading Sessions"), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.dim }, error), /* @__PURE__ */ React25.createElement(Box22, { marginTop: 1 }, /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.dim }, "Press ", /* @__PURE__ */ React25.createElement(Text20, { bold: true }, "ESC"), " to cancel")));
11085
+ return /* @__PURE__ */ React27.createElement(Box24, { borderColor: "red", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React27.createElement(Text22, { bold: true, color: "red" }, "Error Loading Sessions"), /* @__PURE__ */ React27.createElement(Text22, { color: theme.text.dim }, error), /* @__PURE__ */ React27.createElement(Box24, { marginTop: 1 }, /* @__PURE__ */ React27.createElement(Text22, { color: theme.text.dim }, "Press ", /* @__PURE__ */ React27.createElement(Text22, { bold: true }, "ESC"), " to cancel")));
10902
11086
  }
10903
11087
  if (allSessions.length === 0 && isLoading) {
10904
- return /* @__PURE__ */ React25.createElement(Box22, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React25.createElement(Text20, { bold: true, color: "cyan" }, "Loading Sessions..."), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.dim }, "Fetching your sessions from the server"));
11088
+ return /* @__PURE__ */ React27.createElement(Box24, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React27.createElement(Text22, { bold: true, color: "cyan" }, "Loading Sessions..."), /* @__PURE__ */ React27.createElement(Text22, { color: theme.text.dim }, "Fetching your sessions from the server"));
10905
11089
  }
10906
11090
  if (allSessions.length === 0 && !isLoading) {
10907
- return /* @__PURE__ */ React25.createElement(Box22, { borderColor: "yellow", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React25.createElement(Text20, { bold: true, color: "yellow" }, "No Sessions Found"), /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.dim }, "No previous sessions available. Start a new conversation!"), /* @__PURE__ */ React25.createElement(Box22, { marginTop: 1 }, /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.dim }, "Press ", /* @__PURE__ */ React25.createElement(Text20, { bold: true }, "ESC"), " to cancel")));
11091
+ return /* @__PURE__ */ React27.createElement(Box24, { borderColor: "yellow", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React27.createElement(Text22, { bold: true, color: "yellow" }, "No Sessions Found"), /* @__PURE__ */ React27.createElement(Text22, { color: theme.text.dim }, "No previous sessions available. Start a new conversation!"), /* @__PURE__ */ React27.createElement(Box24, { marginTop: 1 }, /* @__PURE__ */ React27.createElement(Text22, { color: theme.text.dim }, "Press ", /* @__PURE__ */ React27.createElement(Text22, { bold: true }, "ESC"), " to cancel")));
10908
11092
  }
10909
11093
  const VISIBLE_ITEMS3 = 10;
10910
11094
  let startIndex;
@@ -10924,7 +11108,7 @@ var init_SessionSelector = __esm({
10924
11108
  const visibleSessions = allSessions.slice(startIndex, endIndex);
10925
11109
  const MAX_TITLE_WIDTH = 50;
10926
11110
  const PREFIX_WIDTH = 6;
10927
- return /* @__PURE__ */ React25.createElement(Box22, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React25.createElement(Box22, { marginBottom: 1 }, /* @__PURE__ */ React25.createElement(Text20, { bold: true, color: "cyan" }, "Select a Session to Resume")), /* @__PURE__ */ React25.createElement(Box22, { flexDirection: "column" }, visibleSessions.map((item, index) => {
11111
+ return /* @__PURE__ */ React27.createElement(Box24, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React27.createElement(Box24, { marginBottom: 1 }, /* @__PURE__ */ React27.createElement(Text22, { bold: true, color: "cyan" }, "Select a Session to Resume")), /* @__PURE__ */ React27.createElement(Box24, { flexDirection: "column" }, visibleSessions.map((item, index) => {
10928
11112
  const actualIndex = startIndex + index;
10929
11113
  const isSelected = actualIndex === selectedIndex;
10930
11114
  const title = item.session.title || "Untitled session";
@@ -10948,8 +11132,8 @@ var init_SessionSelector = __esm({
10948
11132
  const prefixColor = item.prefix === "[Me]" ? "cyan" : "yellow";
10949
11133
  const indicator = isSelected ? "\u25B6 " : " ";
10950
11134
  const bgColor = isSelected ? theme.text.accent : void 0;
10951
- return /* @__PURE__ */ React25.createElement(Box22, { key: item.session.id, width: "100%" }, /* @__PURE__ */ React25.createElement(Text20, { backgroundColor: bgColor, bold: isSelected, color: isSelected ? "black" : theme.text.primary }, indicator), /* @__PURE__ */ React25.createElement(Text20, { backgroundColor: bgColor, bold: isSelected, color: prefixColor }, prefix), /* @__PURE__ */ React25.createElement(Text20, { backgroundColor: bgColor, bold: isSelected, color: isSelected ? "black" : theme.text.primary }, displayTitle), /* @__PURE__ */ React25.createElement(Text20, { backgroundColor: bgColor, color: theme.text.dim }, "(", dateStr, ")"));
10952
- })), /* @__PURE__ */ React25.createElement(Box22, { flexDirection: "column", marginTop: 1 }, (allSessions.length > VISIBLE_ITEMS3 || totalSessions > allSessions.length) && /* @__PURE__ */ React25.createElement(Box22, { marginBottom: 1 }, /* @__PURE__ */ React25.createElement(Text20, { color: "yellow" }, "Showing ", startIndex + 1, "-", endIndex, " of ", totalSessions || allSessions.length, " sessions", hasMore && !isLoading && /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.dim }, " \u2022 Scroll for more"))), /* @__PURE__ */ React25.createElement(Box22, null, /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.dim }, "Use ", /* @__PURE__ */ React25.createElement(Text20, { bold: true }, "\u2191\u2193"), " to navigate \u2022 ", /* @__PURE__ */ React25.createElement(Text20, { bold: true }, "Enter"), " to select \u2022 ", /* @__PURE__ */ React25.createElement(Text20, { bold: true }, "ESC"), " to cancel")), isLoading && /* @__PURE__ */ React25.createElement(Box22, { marginTop: 1 }, /* @__PURE__ */ React25.createElement(Text20, { color: "cyan" }, "Loading more sessions..."))));
11135
+ return /* @__PURE__ */ React27.createElement(Box24, { key: item.session.id, width: "100%" }, /* @__PURE__ */ React27.createElement(Text22, { backgroundColor: bgColor, bold: isSelected, color: isSelected ? "black" : theme.text.primary }, indicator), /* @__PURE__ */ React27.createElement(Text22, { backgroundColor: bgColor, bold: isSelected, color: prefixColor }, prefix), /* @__PURE__ */ React27.createElement(Text22, { backgroundColor: bgColor, bold: isSelected, color: isSelected ? "black" : theme.text.primary }, displayTitle), /* @__PURE__ */ React27.createElement(Text22, { backgroundColor: bgColor, color: theme.text.dim }, "(", dateStr, ")"));
11136
+ })), /* @__PURE__ */ React27.createElement(Box24, { flexDirection: "column", marginTop: 1 }, (allSessions.length > VISIBLE_ITEMS3 || totalSessions > allSessions.length) && /* @__PURE__ */ React27.createElement(Box24, { marginBottom: 1 }, /* @__PURE__ */ React27.createElement(Text22, { color: "yellow" }, "Showing ", startIndex + 1, "-", endIndex, " of ", totalSessions || allSessions.length, " sessions", hasMore && !isLoading && /* @__PURE__ */ React27.createElement(Text22, { color: theme.text.dim }, " \u2022 Scroll for more"))), /* @__PURE__ */ React27.createElement(Box24, null, /* @__PURE__ */ React27.createElement(Text22, { color: theme.text.dim }, "Use ", /* @__PURE__ */ React27.createElement(Text22, { bold: true }, "\u2191\u2193"), " to navigate \u2022 ", /* @__PURE__ */ React27.createElement(Text22, { bold: true }, "Enter"), " to select \u2022 ", /* @__PURE__ */ React27.createElement(Text22, { bold: true }, "ESC"), " to cancel")), isLoading && /* @__PURE__ */ React27.createElement(Box24, { marginTop: 1 }, /* @__PURE__ */ React27.createElement(Text22, { color: "cyan" }, "Loading more sessions..."))));
10953
11137
  };
10954
11138
  }
10955
11139
  });
@@ -10999,10 +11183,10 @@ var init_useOverlayEscapeGuard = __esm({
10999
11183
 
11000
11184
  // src/ui/App.tsx
11001
11185
  import { execSync as execSync6 } from "child_process";
11002
- import { homedir as homedir5 } from "os";
11003
- import { Box as Box23, Text as Text21, useApp as useApp2, useStdout as useStdout2 } from "ink";
11186
+ import { homedir as homedir6 } from "os";
11187
+ import { Box as Box25, Text as Text23, useApp as useApp2, useStdout as useStdout2 } from "ink";
11004
11188
  import Spinner3 from "ink-spinner";
11005
- import React26, { useEffect as useEffect13, useRef as useRef4, useState as useState13 } from "react";
11189
+ import React28, { useEffect as useEffect13, useRef as useRef4, useState as useState14 } from "react";
11006
11190
  var getGitBranch2, getCurrentFolder2, AppContent, App;
11007
11191
  var init_App = __esm({
11008
11192
  "src/ui/App.tsx"() {
@@ -11011,7 +11195,9 @@ var init_App = __esm({
11011
11195
  init_login();
11012
11196
  init_setup();
11013
11197
  init_prompts();
11198
+ init_claude_max();
11014
11199
  init_command_discovery();
11200
+ init_settings_loader();
11015
11201
  init_stdio();
11016
11202
  init_token_storage();
11017
11203
  init_version();
@@ -11023,6 +11209,7 @@ var init_App = __esm({
11023
11209
  init_InputPrompt();
11024
11210
  init_MessageList();
11025
11211
  init_ModelSelector();
11212
+ init_ProviderSelector();
11026
11213
  init_SessionSelector();
11027
11214
  init_SessionContext();
11028
11215
  init_useKeypress();
@@ -11039,7 +11226,7 @@ var init_App = __esm({
11039
11226
  };
11040
11227
  getCurrentFolder2 = (configCwd) => {
11041
11228
  const cwd = configCwd || process.cwd();
11042
- const home = homedir5();
11229
+ const home = homedir6();
11043
11230
  if (cwd.startsWith(home)) {
11044
11231
  return `~${cwd.slice(home.length)}`;
11045
11232
  }
@@ -11048,28 +11235,29 @@ var init_App = __esm({
11048
11235
  AppContent = ({ config: config2, sessionId, webUrl, queuedTasks = [], onExit, onSubmitTask, apiClient, onResumeSession, onClearSession }) => {
11049
11236
  const { exit } = useApp2();
11050
11237
  const { stdout } = useStdout2();
11051
- const { addMessage, clearMessages, isAgentRunning, messages, setSessionId, setWebUrl, setShouldInterruptAgent, setIsAgentRunning, toggleAllToolOutputs, allToolsExpanded, selectedModel, setSelectedModel, refreshStatic } = useSession();
11238
+ const { addMessage, clearMessages, isAgentRunning, messages, setSessionId, setWebUrl, setShouldInterruptAgent, setIsAgentRunning, toggleAllToolOutputs, allToolsExpanded, selectedModel, setSelectedModel, refreshStatic, toggleToolGroups, llmProvider, setLlmProvider } = useSession();
11052
11239
  useModeToggle();
11053
- const [terminalWidth, setTerminalWidth] = useState13(process.stdout.columns || 80);
11054
- const [showHelp, setShowHelp] = useState13(false);
11055
- const [showInput, setShowInput] = useState13(true);
11056
- const [gitBranch] = useState13(() => getGitBranch2());
11057
- const [currentFolder] = useState13(() => getCurrentFolder2(config2.cwd));
11058
- const [hasInputContent, setHasInputContent] = useState13(false);
11059
- const [exitWarning, setExitWarning] = useState13(null);
11240
+ const [terminalWidth, setTerminalWidth] = useState14(process.stdout.columns || 80);
11241
+ const [showHelp, setShowHelp] = useState14(false);
11242
+ const [showInput, setShowInput] = useState14(true);
11243
+ const [gitBranch] = useState14(() => getGitBranch2());
11244
+ const [currentFolder] = useState14(() => getCurrentFolder2(config2.cwd));
11245
+ const [hasInputContent, setHasInputContent] = useState14(false);
11246
+ const [exitWarning, setExitWarning] = useState14(null);
11060
11247
  const inputPromptRef = useRef4(null);
11061
- const [showSessionSelector, setShowSessionSelector] = useState13(false);
11062
- const [showModelSelector, setShowModelSelector] = useState13(false);
11063
- const [showFeedbackDialog, setShowFeedbackDialog] = useState13(false);
11064
- const [isLoadingSession, setIsLoadingSession] = useState13(false);
11065
- const [showFixFlow, setShowFixFlow] = useState13(false);
11066
- const [fixRunId, setFixRunId] = useState13(void 0);
11067
- const [authState, setAuthState] = useState13(
11248
+ const [showSessionSelector, setShowSessionSelector] = useState14(false);
11249
+ const [showModelSelector, setShowModelSelector] = useState14(false);
11250
+ const [showProviderSelector, setShowProviderSelector] = useState14(false);
11251
+ const [showFeedbackDialog, setShowFeedbackDialog] = useState14(false);
11252
+ const [isLoadingSession, setIsLoadingSession] = useState14(false);
11253
+ const [showFixFlow, setShowFixFlow] = useState14(false);
11254
+ const [fixRunId, setFixRunId] = useState14(void 0);
11255
+ const [authState, setAuthState] = useState14(
11068
11256
  () => config2.supatestApiKey ? "authenticated" /* Authenticated */ : "unauthenticated" /* Unauthenticated */
11069
11257
  );
11070
- const [showAuthDialog, setShowAuthDialog] = useState13(false);
11258
+ const [showAuthDialog, setShowAuthDialog] = useState14(false);
11071
11259
  const { isOverlayOpen, isCancelSuppressed, markOverlayClosed } = useOverlayEscapeGuard({
11072
- overlays: [showHelp, showSessionSelector, showAuthDialog, showModelSelector, showFeedbackDialog, showFixFlow]
11260
+ overlays: [showHelp, showSessionSelector, showAuthDialog, showModelSelector, showProviderSelector, showFeedbackDialog, showFixFlow]
11073
11261
  });
11074
11262
  useEffect13(() => {
11075
11263
  if (!config2.supatestApiKey) {
@@ -11184,6 +11372,10 @@ var init_App = __esm({
11184
11372
  setShowModelSelector(true);
11185
11373
  return;
11186
11374
  }
11375
+ if (command === "/provider") {
11376
+ setShowProviderSelector(true);
11377
+ return;
11378
+ }
11187
11379
  if (command === "/fix" || command.startsWith("/fix ")) {
11188
11380
  if (authState !== "authenticated" /* Authenticated */) {
11189
11381
  addMessage({
@@ -11319,6 +11511,20 @@ var init_App = __esm({
11319
11511
  markOverlayClosed();
11320
11512
  setShowModelSelector(false);
11321
11513
  };
11514
+ const handleProviderSelect = (provider) => {
11515
+ setShowProviderSelector(false);
11516
+ markOverlayClosed();
11517
+ setLlmProvider(provider);
11518
+ saveSupatestSettings(config2.cwd || process.cwd(), { llmProvider: provider });
11519
+ addMessage({
11520
+ type: "assistant",
11521
+ content: `LLM Provider changed to: ${provider === "claude-max" ? "Claude Max" : "Supatest Managed"}. This setting has been saved and will persist across sessions.`
11522
+ });
11523
+ };
11524
+ const handleProviderSelectorCancel = () => {
11525
+ markOverlayClosed();
11526
+ setShowProviderSelector(false);
11527
+ };
11322
11528
  const handleFeedbackSubmit = async (category, description) => {
11323
11529
  setShowFeedbackDialog(false);
11324
11530
  markOverlayClosed();
@@ -11463,7 +11669,7 @@ var init_App = __esm({
11463
11669
  clearTerminalViewportAndScrollback();
11464
11670
  }
11465
11671
  if (key.ctrl && key.name === "o") {
11466
- toggleAllToolOutputs();
11672
+ toggleToolGroups();
11467
11673
  }
11468
11674
  },
11469
11675
  { isActive: !isOverlayOpen }
@@ -11476,21 +11682,21 @@ var init_App = __esm({
11476
11682
  });
11477
11683
  }
11478
11684
  }, []);
11479
- return /* @__PURE__ */ React26.createElement(
11480
- Box23,
11685
+ return /* @__PURE__ */ React28.createElement(
11686
+ Box25,
11481
11687
  {
11482
11688
  flexDirection: "column",
11483
11689
  paddingX: 1
11484
11690
  },
11485
- /* @__PURE__ */ React26.createElement(MessageList, { currentFolder, gitBranch, queuedTasks, terminalWidth }),
11486
- showHelp && /* @__PURE__ */ React26.createElement(
11691
+ /* @__PURE__ */ React28.createElement(MessageList, { currentFolder, gitBranch, queuedTasks, terminalWidth }),
11692
+ showHelp && /* @__PURE__ */ React28.createElement(
11487
11693
  HelpMenu,
11488
11694
  {
11489
11695
  isAuthenticated: authState === "authenticated" /* Authenticated */,
11490
11696
  onClose: handleHelpClose
11491
11697
  }
11492
11698
  ),
11493
- showSessionSelector && apiClient && /* @__PURE__ */ React26.createElement(
11699
+ showSessionSelector && apiClient && /* @__PURE__ */ React28.createElement(
11494
11700
  SessionSelector,
11495
11701
  {
11496
11702
  apiClient,
@@ -11498,29 +11704,39 @@ var init_App = __esm({
11498
11704
  onSelect: handleSessionSelect
11499
11705
  }
11500
11706
  ),
11501
- isLoadingSession && /* @__PURE__ */ React26.createElement(Box23, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React26.createElement(Box23, { flexDirection: "row" }, /* @__PURE__ */ React26.createElement(Box23, { width: 2 }, /* @__PURE__ */ React26.createElement(Text21, { color: theme.text.accent }, /* @__PURE__ */ React26.createElement(Spinner3, { type: "dots" }))), /* @__PURE__ */ React26.createElement(Text21, { bold: true, color: "cyan" }, "Loading session...")), /* @__PURE__ */ React26.createElement(Text21, { color: theme.text.dim }, "Fetching queries and context")),
11502
- showModelSelector && /* @__PURE__ */ React26.createElement(
11707
+ isLoadingSession && /* @__PURE__ */ React28.createElement(Box25, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React28.createElement(Box25, { flexDirection: "row" }, /* @__PURE__ */ React28.createElement(Box25, { width: 2 }, /* @__PURE__ */ React28.createElement(Text23, { color: theme.text.accent }, /* @__PURE__ */ React28.createElement(Spinner3, { type: "dots" }))), /* @__PURE__ */ React28.createElement(Text23, { bold: true, color: "cyan" }, "Loading session...")), /* @__PURE__ */ React28.createElement(Text23, { color: theme.text.dim }, "Fetching queries and context")),
11708
+ showModelSelector && /* @__PURE__ */ React28.createElement(
11503
11709
  ModelSelector,
11504
11710
  {
11505
11711
  currentModel: selectedModel,
11712
+ isClaudeMax: !!config2.oauthToken,
11506
11713
  onCancel: handleModelSelectorCancel,
11507
11714
  onSelect: handleModelSelect
11508
11715
  }
11509
11716
  ),
11510
- showAuthDialog && /* @__PURE__ */ React26.createElement(
11717
+ showProviderSelector && /* @__PURE__ */ React28.createElement(
11718
+ ProviderSelector,
11719
+ {
11720
+ claudeMaxAvailable: isClaudeMaxAvailable(),
11721
+ currentProvider: llmProvider,
11722
+ onCancel: handleProviderSelectorCancel,
11723
+ onSelect: handleProviderSelect
11724
+ }
11725
+ ),
11726
+ showAuthDialog && /* @__PURE__ */ React28.createElement(
11511
11727
  AuthDialog,
11512
11728
  {
11513
11729
  onLogin: handleLogin
11514
11730
  }
11515
11731
  ),
11516
- showFeedbackDialog && /* @__PURE__ */ React26.createElement(
11732
+ showFeedbackDialog && /* @__PURE__ */ React28.createElement(
11517
11733
  FeedbackDialog,
11518
11734
  {
11519
11735
  onCancel: handleFeedbackCancel,
11520
11736
  onSubmit: handleFeedbackSubmit
11521
11737
  }
11522
11738
  ),
11523
- showFixFlow && apiClient && /* @__PURE__ */ React26.createElement(
11739
+ showFixFlow && apiClient && /* @__PURE__ */ React28.createElement(
11524
11740
  FixFlow,
11525
11741
  {
11526
11742
  apiClient,
@@ -11530,12 +11746,13 @@ var init_App = __esm({
11530
11746
  onStartFix: handleFixStart
11531
11747
  }
11532
11748
  ),
11533
- /* @__PURE__ */ React26.createElement(Box23, { flexDirection: "column" }, !showAuthDialog && /* @__PURE__ */ React26.createElement(AuthBanner, { authState }), showInput && !showSessionSelector && !showAuthDialog && !showHelp && !showModelSelector && !showFeedbackDialog && !showFixFlow && !isLoadingSession && /* @__PURE__ */ React26.createElement(Box23, { flexDirection: "column", marginTop: 0, width: "100%" }, exitWarning && /* @__PURE__ */ React26.createElement(Box23, { marginBottom: 0, paddingX: 1 }, /* @__PURE__ */ React26.createElement(Text21, { color: "yellow" }, exitWarning)), /* @__PURE__ */ React26.createElement(
11749
+ /* @__PURE__ */ React28.createElement(Box25, { flexDirection: "column" }, !showAuthDialog && /* @__PURE__ */ React28.createElement(AuthBanner, { authState }), showInput && !showSessionSelector && !showAuthDialog && !showHelp && !showModelSelector && !showProviderSelector && !showFeedbackDialog && !showFixFlow && !isLoadingSession && /* @__PURE__ */ React28.createElement(Box25, { flexDirection: "column", marginTop: 0, width: "100%" }, exitWarning && /* @__PURE__ */ React28.createElement(Box25, { marginBottom: 0, paddingX: 1 }, /* @__PURE__ */ React28.createElement(Text23, { color: "yellow" }, exitWarning)), /* @__PURE__ */ React28.createElement(
11534
11750
  InputPrompt,
11535
11751
  {
11536
11752
  currentFolder,
11537
11753
  cwd: config2.cwd,
11538
11754
  gitBranch,
11755
+ isClaudeMax: !!config2.oauthToken,
11539
11756
  onHelpToggle: () => setShowHelp((prev) => !prev),
11540
11757
  onInputChange: (val) => setHasInputContent(val.trim().length > 0),
11541
11758
  onSubmit: handleSubmitTask,
@@ -11546,7 +11763,7 @@ var init_App = __esm({
11546
11763
  );
11547
11764
  };
11548
11765
  App = (props) => {
11549
- return /* @__PURE__ */ React26.createElement(AppContent, { ...props });
11766
+ return /* @__PURE__ */ React28.createElement(AppContent, { ...props });
11550
11767
  };
11551
11768
  }
11552
11769
  });
@@ -11582,7 +11799,7 @@ __export(interactive_exports, {
11582
11799
  runInteractive: () => runInteractive
11583
11800
  });
11584
11801
  import { render as render2 } from "ink";
11585
- import React27, { useEffect as useEffect15, useRef as useRef5 } from "react";
11802
+ import React29, { useEffect as useEffect15, useRef as useRef5 } from "react";
11586
11803
  function getToolDescription2(toolName, input) {
11587
11804
  switch (toolName) {
11588
11805
  case "Read":
@@ -11715,7 +11932,7 @@ async function runInteractive(config2) {
11715
11932
  webUrl = session.webUrl;
11716
11933
  }
11717
11934
  const { unmount, waitUntilExit } = render2(
11718
- /* @__PURE__ */ React27.createElement(
11935
+ /* @__PURE__ */ React29.createElement(
11719
11936
  InteractiveApp,
11720
11937
  {
11721
11938
  apiClient,
@@ -11770,7 +11987,9 @@ var init_interactive = __esm({
11770
11987
  init_SessionContext();
11771
11988
  init_useBracketedPaste();
11772
11989
  init_mouse();
11990
+ init_claude_max();
11773
11991
  init_logger();
11992
+ init_settings_loader();
11774
11993
  init_stdio();
11775
11994
  init_version();
11776
11995
  AgentRunner = ({ config: config2, sessionId, apiClient, messageBridge, onComplete, onTurnComplete }) => {
@@ -11787,7 +12006,8 @@ var init_interactive = __esm({
11787
12006
  agentMode,
11788
12007
  setAgentMode,
11789
12008
  planFilePath,
11790
- selectedModel
12009
+ selectedModel,
12010
+ llmProvider
11791
12011
  } = useSession();
11792
12012
  const agentRef = useRef5(null);
11793
12013
  useEffect15(() => {
@@ -11803,7 +12023,7 @@ var init_interactive = __esm({
11803
12023
  try {
11804
12024
  const supatestApiKey = apiClient.getApiKey?.() || config2.supatestApiKey || "";
11805
12025
  const proxyUrl = config2.supatestApiUrl || "https://code-api.supatest.ai";
11806
- const baseUrl = `${proxyUrl}/v1/sessions/${sessionId}/anthropic`;
12026
+ const baseUrl = `${proxyUrl}/v1/sessions/${sessionId}`;
11807
12027
  process.env.ANTHROPIC_BASE_URL = baseUrl;
11808
12028
  process.env.ANTHROPIC_API_KEY = supatestApiKey;
11809
12029
  const presenter = new ReactPresenter(
@@ -11844,12 +12064,22 @@ var init_interactive = __esm({
11844
12064
  sessionId,
11845
12065
  config2.verbose
11846
12066
  );
12067
+ let oauthToken;
12068
+ if (llmProvider === "claude-max") {
12069
+ if (isClaudeMaxAvailable()) {
12070
+ oauthToken = "use-claude-max";
12071
+ logger.info("Using Claude Max subscription for LLM calls");
12072
+ } else {
12073
+ logger.warn("Claude Max selected but not available. Falling back to Supatest Managed.");
12074
+ }
12075
+ }
11847
12076
  const runConfig = {
11848
12077
  ...config2,
11849
12078
  supatestApiKey,
11850
12079
  mode: agentMode,
11851
12080
  planFilePath,
11852
12081
  selectedModel,
12082
+ oauthToken,
11853
12083
  systemPromptAppend: agentMode === "plan" ? config.planSystemPrompt : config2.systemPromptAppend
11854
12084
  };
11855
12085
  const agent2 = new CoreAgent(presenter, messageBridge);
@@ -11894,17 +12124,17 @@ var init_interactive = __esm({
11894
12124
  setIsAgentRunning,
11895
12125
  setUsageStats
11896
12126
  } = useSession();
11897
- const [sessionId, setSessionId] = React27.useState(initialSessionId);
11898
- const [currentTask, setCurrentTask] = React27.useState(config2.task);
11899
- const [taskId, setTaskId] = React27.useState(0);
11900
- const [shouldRunAgent, setShouldRunAgent] = React27.useState(!!config2.task);
11901
- const [taskQueue, setTaskQueue] = React27.useState([]);
11902
- const [providerSessionId, setProviderSessionId] = React27.useState();
11903
- const messageBridgeRef = React27.useRef(null);
11904
- const lastSubmitRef = React27.useRef(null);
11905
- const [pendingInjected, setPendingInjected] = React27.useState([]);
11906
- const pendingInjectedRef = React27.useRef([]);
11907
- React27.useEffect(() => {
12127
+ const [sessionId, setSessionId] = React29.useState(initialSessionId);
12128
+ const [currentTask, setCurrentTask] = React29.useState(config2.task);
12129
+ const [taskId, setTaskId] = React29.useState(0);
12130
+ const [shouldRunAgent, setShouldRunAgent] = React29.useState(!!config2.task);
12131
+ const [taskQueue, setTaskQueue] = React29.useState([]);
12132
+ const [providerSessionId, setProviderSessionId] = React29.useState();
12133
+ const messageBridgeRef = React29.useRef(null);
12134
+ const lastSubmitRef = React29.useRef(null);
12135
+ const [pendingInjected, setPendingInjected] = React29.useState([]);
12136
+ const pendingInjectedRef = React29.useRef([]);
12137
+ React29.useEffect(() => {
11908
12138
  pendingInjectedRef.current = pendingInjected;
11909
12139
  }, [pendingInjected]);
11910
12140
  const handleSubmitTask = async (task) => {
@@ -11967,7 +12197,7 @@ var init_interactive = __esm({
11967
12197
  if (shouldRunAgent && !messageBridgeRef.current) {
11968
12198
  messageBridgeRef.current = new MessageBridge(providerSessionId || "");
11969
12199
  }
11970
- React27.useEffect(() => {
12200
+ React29.useEffect(() => {
11971
12201
  if (!shouldRunAgent && taskQueue.length > 0) {
11972
12202
  const [nextTask, ...remaining] = taskQueue;
11973
12203
  setTaskQueue(remaining);
@@ -11981,14 +12211,14 @@ var init_interactive = __esm({
11981
12211
  setShouldRunAgent(true);
11982
12212
  }
11983
12213
  }, [shouldRunAgent, taskQueue, addMessage, providerSessionId]);
11984
- const handleClearSession = React27.useCallback(() => {
12214
+ const handleClearSession = React29.useCallback(() => {
11985
12215
  setSessionId(void 0);
11986
12216
  setContextSessionId(void 0);
11987
12217
  setProviderSessionId(void 0);
11988
12218
  setTaskQueue([]);
11989
12219
  setPendingInjected([]);
11990
12220
  }, [setContextSessionId]);
11991
- return /* @__PURE__ */ React27.createElement(React27.Fragment, null, /* @__PURE__ */ React27.createElement(
12221
+ return /* @__PURE__ */ React29.createElement(React29.Fragment, null, /* @__PURE__ */ React29.createElement(
11992
12222
  App,
11993
12223
  {
11994
12224
  apiClient,
@@ -12051,7 +12281,7 @@ var init_interactive = __esm({
12051
12281
  sessionId,
12052
12282
  webUrl
12053
12283
  }
12054
- ), shouldRunAgent && currentTask && sessionId && messageBridgeRef.current && /* @__PURE__ */ React27.createElement(
12284
+ ), shouldRunAgent && currentTask && sessionId && messageBridgeRef.current && /* @__PURE__ */ React29.createElement(
12055
12285
  AgentRunner,
12056
12286
  {
12057
12287
  apiClient,
@@ -12074,7 +12304,9 @@ var init_interactive = __esm({
12074
12304
  };
12075
12305
  InteractiveApp = (props) => {
12076
12306
  useBracketedPaste();
12077
- return /* @__PURE__ */ React27.createElement(KeypressProvider, null, /* @__PURE__ */ React27.createElement(SessionProvider, { initialModel: props.config.selectedModel }, /* @__PURE__ */ React27.createElement(InteractiveAppContent, { ...props })));
12307
+ const settings = loadSupatestSettings(props.config.cwd || process.cwd());
12308
+ const initialProvider = settings.llmProvider || "supatest-managed";
12309
+ return /* @__PURE__ */ React29.createElement(KeypressProvider, null, /* @__PURE__ */ React29.createElement(SessionProvider, { initialLlmProvider: initialProvider, initialModel: props.config.selectedModel }, /* @__PURE__ */ React29.createElement(InteractiveAppContent, { ...props })));
12078
12310
  };
12079
12311
  }
12080
12312
  });
@@ -12090,7 +12322,7 @@ import { Command } from "commander";
12090
12322
  init_api_client();
12091
12323
  import chalk3 from "chalk";
12092
12324
  import { render } from "ink";
12093
- import React14 from "react";
12325
+ import React15 from "react";
12094
12326
 
12095
12327
  // src/ui/HeadlessApp.tsx
12096
12328
  await init_agent();
@@ -12099,8 +12331,8 @@ init_MessageList();
12099
12331
  init_SessionContext();
12100
12332
  import { execSync as execSync2 } from "child_process";
12101
12333
  import { homedir as homedir3 } from "os";
12102
- import { Box as Box12, useApp } from "ink";
12103
- import React13, { useEffect as useEffect2, useRef, useState as useState3 } from "react";
12334
+ import { Box as Box13, useApp } from "ink";
12335
+ import React14, { useEffect as useEffect2, useRef, useState as useState3 } from "react";
12104
12336
  var getGitBranch = () => {
12105
12337
  try {
12106
12338
  return execSync2("git rev-parse --abbrev-ref HEAD", { encoding: "utf8" }).trim();
@@ -12133,7 +12365,7 @@ var HeadlessAgentRunner = ({ config: config2, sessionId, apiClient, onComplete }
12133
12365
  setIsAgentRunning(true);
12134
12366
  try {
12135
12367
  const proxyUrl = config2.supatestApiUrl || "https://code-api.supatest.ai";
12136
- const baseUrl = `${proxyUrl}/v1/sessions/${sessionId}/anthropic`;
12368
+ const baseUrl = `${proxyUrl}/v1/sessions/${sessionId}`;
12137
12369
  process.env.ANTHROPIC_BASE_URL = baseUrl;
12138
12370
  process.env.ANTHROPIC_API_KEY = config2.supatestApiKey;
12139
12371
  const presenter = new ReactPresenter(
@@ -12244,7 +12476,7 @@ var HeadlessAppContent = ({
12244
12476
  onComplete(success, providerSessionId);
12245
12477
  exit();
12246
12478
  };
12247
- return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React13.createElement(
12479
+ return /* @__PURE__ */ React14.createElement(Box13, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React14.createElement(
12248
12480
  MessageList,
12249
12481
  {
12250
12482
  currentFolder,
@@ -12252,7 +12484,7 @@ var HeadlessAppContent = ({
12252
12484
  headless: true,
12253
12485
  terminalWidth
12254
12486
  }
12255
- ), /* @__PURE__ */ React13.createElement(
12487
+ ), /* @__PURE__ */ React14.createElement(
12256
12488
  HeadlessAgentRunner,
12257
12489
  {
12258
12490
  apiClient,
@@ -12263,7 +12495,7 @@ var HeadlessAppContent = ({
12263
12495
  ));
12264
12496
  };
12265
12497
  var HeadlessApp = (props) => {
12266
- return /* @__PURE__ */ React13.createElement(SessionProvider, { initialModel: props.config.selectedModel }, /* @__PURE__ */ React13.createElement(HeadlessAppContent, { ...props }));
12498
+ return /* @__PURE__ */ React14.createElement(SessionProvider, { initialModel: props.config.selectedModel }, /* @__PURE__ */ React14.createElement(HeadlessAppContent, { ...props }));
12267
12499
  };
12268
12500
 
12269
12501
  // src/modes/headless.ts
@@ -12332,7 +12564,7 @@ async function runAgent(config2) {
12332
12564
  }
12333
12565
  };
12334
12566
  const { unmount, waitUntilExit } = render(
12335
- React14.createElement(HeadlessApp, {
12567
+ React15.createElement(HeadlessApp, {
12336
12568
  config: config2,
12337
12569
  sessionId,
12338
12570
  webUrl,
@@ -12407,43 +12639,12 @@ Updating Supatest CLI ${CLI_VERSION} \u2192 ${latest}...`);
12407
12639
 
12408
12640
  // src/index.ts
12409
12641
  init_banner();
12410
-
12411
- // src/utils/claude-max.ts
12412
- init_logger();
12413
- import { execSync as execSync4 } from "child_process";
12414
- function isClaudeMaxAvailable() {
12415
- logger.debug("[claude-max] Checking if Claude Code credentials exist in keychain");
12416
- try {
12417
- const credentialsJson = execSync4(
12418
- 'security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null',
12419
- { encoding: "utf-8" }
12420
- ).trim();
12421
- if (!credentialsJson) {
12422
- logger.debug("[claude-max] No credentials found in keychain");
12423
- return false;
12424
- }
12425
- const credentials = JSON.parse(credentialsJson);
12426
- const hasOauth = !!credentials.claudeAiOauth?.accessToken;
12427
- logger.debug("[claude-max] Credentials check", {
12428
- hasOauth,
12429
- hasRefreshToken: !!credentials.claudeAiOauth?.refreshToken
12430
- });
12431
- return hasOauth;
12432
- } catch (error) {
12433
- logger.debug("[claude-max] Error checking keychain", {
12434
- error: error instanceof Error ? error.message : String(error)
12435
- });
12436
- return false;
12437
- }
12438
- }
12439
-
12440
- // src/index.ts
12441
12642
  init_error_logger();
12442
12643
  init_logger();
12443
12644
 
12444
12645
  // src/utils/node-version.ts
12445
12646
  init_logger();
12446
- import { execSync as execSync5 } from "child_process";
12647
+ import { execSync as execSync4 } from "child_process";
12447
12648
  var MINIMUM_NODE_VERSION2 = 18;
12448
12649
  function parseVersion2(versionString) {
12449
12650
  const cleaned = versionString.trim().replace(/^v/, "");
@@ -12460,7 +12661,7 @@ function parseVersion2(versionString) {
12460
12661
  }
12461
12662
  function getNodeVersion2() {
12462
12663
  try {
12463
- const versionOutput = execSync5("node --version", {
12664
+ const versionOutput = execSync4("node --version", {
12464
12665
  encoding: "utf-8",
12465
12666
  stdio: ["ignore", "pipe", "ignore"]
12466
12667
  });
@@ -12577,7 +12778,7 @@ program.name("supatest").description(
12577
12778
  "-m, --claude-max-iterations <number>",
12578
12779
  "Maximum number of iterations",
12579
12780
  "100"
12580
- ).option("--supatest-api-key <key>", "Supatest API key (or use SUPATEST_API_KEY env)").option("--supatest-api-url <url>", "Supatest API URL (or use SUPATEST_API_URL env, defaults to https://code-api.supatest.ai)").option("--headless", "Run in headless mode (for CI/CD, minimal output)").option("--verbose", "Enable verbose logging").option("--model <model>", "Claude model to use (or use ANTHROPIC_MODEL_NAME env)").option("--claude-max", "Use Claude Max subscription (requires Claude Code to be logged in)").action(async (task, options) => {
12781
+ ).option("--supatest-api-key <key>", "Supatest API key (or use SUPATEST_API_KEY env)").option("--supatest-api-url <url>", "Supatest API URL (or use SUPATEST_API_URL env, defaults to https://code-api.supatest.ai)").option("--headless", "Run in headless mode (for CI/CD, minimal output)").option("--verbose", "Enable verbose logging").option("--model <model>", "Model to use (or use ANTHROPIC_MODEL_NAME env). Use 'small', 'medium', or 'premium' for tier-based selection").action(async (task, options) => {
12581
12782
  try {
12582
12783
  checkNodeVersion2();
12583
12784
  await checkAndAutoUpdate();
@@ -12614,17 +12815,6 @@ program.name("supatest").description(
12614
12815
  let supatestApiKey;
12615
12816
  let oauthToken;
12616
12817
  const supatestApiUrl = options.supatestApiUrl || config.supatestApiUrl;
12617
- if (options.claudeMax) {
12618
- if (!isClaudeMaxAvailable()) {
12619
- logger.error("Claude Max not available. Please ensure:");
12620
- logger.error(" 1. Claude Code is installed");
12621
- logger.error(" 2. You are logged in with a Claude Max subscription");
12622
- logger.error(" 3. Run 'claude' and authenticate if needed");
12623
- process.exit(1);
12624
- }
12625
- oauthToken = "use-claude-max";
12626
- logger.info("Using Claude Max subscription for LLM calls");
12627
- }
12628
12818
  if (isHeadlessMode) {
12629
12819
  supatestApiKey = normalizeSupatestKey(
12630
12820
  options.supatestApiKey || config.supatestApiKey,
@@ -12650,15 +12840,16 @@ program.name("supatest").description(
12650
12840
  }
12651
12841
  }
12652
12842
  let selectedModel;
12653
- const modelOption = options.model || config.anthropicModelName;
12654
- if (modelOption) {
12655
- if (isValidModelId(modelOption)) {
12656
- selectedModel = modelOption;
12657
- } else {
12843
+ const modelOption = options.model || "medium";
12844
+ if (modelOption && isValidModelId(modelOption)) {
12845
+ selectedModel = modelOption;
12846
+ } else {
12847
+ if (modelOption && modelOption !== "medium") {
12658
12848
  const validModels = AVAILABLE_MODELS.map((m) => m.id).join(", ");
12659
- logger.warn(`Invalid model "${modelOption}". Valid models: ${validModels}`);
12849
+ logger.warn(`Invalid model "${modelOption}". Valid models: ${validModels} or tier names: small, medium, premium`);
12660
12850
  logger.warn("Using default model instead.");
12661
12851
  }
12852
+ selectedModel = "medium";
12662
12853
  }
12663
12854
  if (isHeadlessMode) {
12664
12855
  if (!prompt) {
@@ -12670,7 +12861,7 @@ program.name("supatest").description(
12670
12861
  logs,
12671
12862
  supatestApiKey,
12672
12863
  supatestApiUrl,
12673
- maxIterations: Number.parseInt(options.maxIterations || "1", 10),
12864
+ maxIterations: Number.parseInt(options.maxIterations || "100", 10),
12674
12865
  verbose: options.verbose || false,
12675
12866
  cwd: options.cwd,
12676
12867
  systemPromptAppend: config.headlessSystemPrompt,
@@ -12686,7 +12877,7 @@ program.name("supatest").description(
12686
12877
  logs,
12687
12878
  supatestApiKey,
12688
12879
  supatestApiUrl,
12689
- maxIterations: Number.parseInt(options.maxIterations || "1", 10),
12880
+ maxIterations: Number.parseInt(options.maxIterations || "100", 10),
12690
12881
  verbose: options.verbose || false,
12691
12882
  cwd: options.cwd,
12692
12883
  systemPromptAppend: config.interactiveSystemPrompt,