@supatest/cli 0.0.32 → 0.0.34
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.
- package/dist/index.js +838 -619
- package/package.json +7 -6
package/dist/index.js
CHANGED
|
@@ -336,11 +336,78 @@ 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") {
|
|
347
|
+
return SMALL_COST_LABEL;
|
|
348
|
+
}
|
|
349
|
+
if (modelId === "medium") {
|
|
350
|
+
return MEDIUM_COST_LABEL;
|
|
351
|
+
}
|
|
352
|
+
if (modelId === "premium") {
|
|
353
|
+
return PREMIUM_COST_LABEL;
|
|
354
|
+
}
|
|
355
|
+
const model = getModelById(modelId);
|
|
356
|
+
if (model) {
|
|
357
|
+
if (model.costMultiplier === SMALL_COST_MULTIPLIER) {
|
|
358
|
+
return SMALL_COST_LABEL;
|
|
359
|
+
}
|
|
360
|
+
if (model.costMultiplier === MEDIUM_COST_MULTIPLIER) {
|
|
361
|
+
return MEDIUM_COST_LABEL;
|
|
362
|
+
}
|
|
363
|
+
if (model.costMultiplier === PREMIUM_COST_MULTIPLIER) {
|
|
364
|
+
return PREMIUM_COST_LABEL;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return MEDIUM_COST_LABEL;
|
|
368
|
+
}
|
|
339
369
|
function getModelDisplayName(id) {
|
|
340
|
-
|
|
341
|
-
|
|
370
|
+
if (MODEL_TIERS.includes(id)) {
|
|
371
|
+
const tierNames = {
|
|
372
|
+
small: "Small",
|
|
373
|
+
medium: "Medium",
|
|
374
|
+
premium: "Premium"
|
|
375
|
+
};
|
|
376
|
+
return tierNames[id] || id;
|
|
377
|
+
}
|
|
378
|
+
const anthropicModel = getModelById(id);
|
|
379
|
+
if (anthropicModel) {
|
|
380
|
+
return anthropicModel.name;
|
|
381
|
+
}
|
|
382
|
+
const tier = getTierFromProviderModel(id);
|
|
383
|
+
if (tier && !id.startsWith("claude-")) {
|
|
384
|
+
const tierNames = {
|
|
385
|
+
small: "Small",
|
|
386
|
+
medium: "Medium",
|
|
387
|
+
premium: "Premium"
|
|
388
|
+
};
|
|
389
|
+
return tierNames[tier];
|
|
390
|
+
}
|
|
391
|
+
return id;
|
|
392
|
+
}
|
|
393
|
+
function resolveTierToAnthropicModel(tier) {
|
|
394
|
+
const tierToModel = {
|
|
395
|
+
small: "claude-haiku-4-5",
|
|
396
|
+
medium: "claude-sonnet-4-5",
|
|
397
|
+
premium: "claude-opus-4-5"
|
|
398
|
+
};
|
|
399
|
+
return tierToModel[tier];
|
|
400
|
+
}
|
|
401
|
+
function resolveToAnthropicModel(model) {
|
|
402
|
+
if (MODEL_TIERS.includes(model)) {
|
|
403
|
+
return resolveTierToAnthropicModel(model);
|
|
404
|
+
}
|
|
405
|
+
return model;
|
|
342
406
|
}
|
|
343
407
|
function isValidModelId(id) {
|
|
408
|
+
if (MODEL_TIERS.includes(id)) {
|
|
409
|
+
return true;
|
|
410
|
+
}
|
|
344
411
|
return AVAILABLE_MODELS.some((m) => id === m.id || id.startsWith(`${m.id}-`));
|
|
345
412
|
}
|
|
346
413
|
function getErrorMap() {
|
|
@@ -538,7 +605,31 @@ function getToolDisplayName(toolName) {
|
|
|
538
605
|
};
|
|
539
606
|
return displayNameMap[toolName] || toolName;
|
|
540
607
|
}
|
|
541
|
-
|
|
608
|
+
function getToolCategory(toolName) {
|
|
609
|
+
const name = toolName.toLowerCase();
|
|
610
|
+
if (name.includes("read")) return "Read";
|
|
611
|
+
if (name.includes("write")) return "Write";
|
|
612
|
+
if (name.includes("edit")) return "Edit";
|
|
613
|
+
if (name.includes("bash") || name.includes("command")) return "Bash";
|
|
614
|
+
if (name.includes("glob")) return "Glob";
|
|
615
|
+
if (name.includes("grep")) return "Grep";
|
|
616
|
+
if (name.includes("task")) return "Task";
|
|
617
|
+
if (name.includes("todo")) return "Todo";
|
|
618
|
+
return getToolDisplayName(toolName);
|
|
619
|
+
}
|
|
620
|
+
function getToolGroupCounts(tools) {
|
|
621
|
+
const groups = tools.reduce(
|
|
622
|
+
(acc, tool) => {
|
|
623
|
+
const toolName = tool.toolName || tool.name || "";
|
|
624
|
+
const category = getToolCategory(toolName);
|
|
625
|
+
acc[category] = (acc[category] || 0) + 1;
|
|
626
|
+
return acc;
|
|
627
|
+
},
|
|
628
|
+
{}
|
|
629
|
+
);
|
|
630
|
+
return Object.entries(groups).map(([name, count]) => ({ name, count })).sort((a, b) => b.count - a.count);
|
|
631
|
+
}
|
|
632
|
+
var AVAILABLE_MODELS, DATE_SUFFIX_REGEX, SMALL_COST_MULTIPLIER, MEDIUM_COST_MULTIPLIER, PREMIUM_COST_MULTIPLIER, SMALL_COST_LABEL, MEDIUM_COST_LABEL, PREMIUM_COST_LABEL, 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
633
|
var init_shared_es = __esm({
|
|
543
634
|
"../shared/dist/shared.es.mjs"() {
|
|
544
635
|
"use strict";
|
|
@@ -547,23 +638,32 @@ var init_shared_es = __esm({
|
|
|
547
638
|
id: "claude-sonnet-4-5",
|
|
548
639
|
name: "Sonnet 4.5",
|
|
549
640
|
description: "Fast & capable",
|
|
550
|
-
contextWindow: 2e5
|
|
641
|
+
contextWindow: 2e5,
|
|
642
|
+
costMultiplier: 1
|
|
551
643
|
},
|
|
552
644
|
{
|
|
553
645
|
id: "claude-opus-4-5",
|
|
554
646
|
name: "Opus 4.5",
|
|
555
647
|
description: "Most capable",
|
|
556
|
-
contextWindow: 2e5
|
|
648
|
+
contextWindow: 2e5,
|
|
649
|
+
costMultiplier: 2
|
|
557
650
|
},
|
|
558
651
|
{
|
|
559
652
|
id: "claude-haiku-4-5",
|
|
560
653
|
name: "Haiku 4.5",
|
|
561
654
|
description: "Lightweight",
|
|
562
|
-
contextWindow: 2e5
|
|
655
|
+
contextWindow: 2e5,
|
|
656
|
+
costMultiplier: 0.5
|
|
563
657
|
}
|
|
564
658
|
];
|
|
565
|
-
DEFAULT_MODEL_ID = "claude-sonnet-4-5";
|
|
566
659
|
DATE_SUFFIX_REGEX = /-\d{8}$/;
|
|
660
|
+
SMALL_COST_MULTIPLIER = 0.5;
|
|
661
|
+
MEDIUM_COST_MULTIPLIER = 1;
|
|
662
|
+
PREMIUM_COST_MULTIPLIER = 2;
|
|
663
|
+
SMALL_COST_LABEL = "0.5x";
|
|
664
|
+
MEDIUM_COST_LABEL = "1x";
|
|
665
|
+
PREMIUM_COST_LABEL = "2x";
|
|
666
|
+
MODEL_TIERS = ["small", "medium", "premium"];
|
|
567
667
|
CONTEXT_WINDOWS = Object.fromEntries(
|
|
568
668
|
AVAILABLE_MODELS.map((m) => [m.id, m.contextWindow])
|
|
569
669
|
);
|
|
@@ -5003,263 +5103,6 @@ var init_shared_es = __esm({
|
|
|
5003
5103
|
days: numberType()
|
|
5004
5104
|
})
|
|
5005
5105
|
});
|
|
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
5106
|
}
|
|
5264
5107
|
});
|
|
5265
5108
|
|
|
@@ -5522,7 +5365,7 @@ var CLI_VERSION;
|
|
|
5522
5365
|
var init_version = __esm({
|
|
5523
5366
|
"src/version.ts"() {
|
|
5524
5367
|
"use strict";
|
|
5525
|
-
CLI_VERSION = "0.0.
|
|
5368
|
+
CLI_VERSION = "0.0.34";
|
|
5526
5369
|
}
|
|
5527
5370
|
});
|
|
5528
5371
|
|
|
@@ -6463,6 +6306,7 @@ var CoreAgent;
|
|
|
6463
6306
|
var init_agent = __esm({
|
|
6464
6307
|
async "src/core/agent.ts"() {
|
|
6465
6308
|
"use strict";
|
|
6309
|
+
init_shared_es();
|
|
6466
6310
|
await init_config();
|
|
6467
6311
|
init_command_discovery();
|
|
6468
6312
|
init_error_logger();
|
|
@@ -6531,6 +6375,8 @@ ${projectInstructions}`,
|
|
|
6531
6375
|
"ANTHROPIC_API_KEY",
|
|
6532
6376
|
"ANTHROPIC_BASE_URL",
|
|
6533
6377
|
"ANTHROPIC_AUTH_TOKEN",
|
|
6378
|
+
"ZAI_API_KEY",
|
|
6379
|
+
"ZAI_BASE_URL",
|
|
6534
6380
|
"CLAUDE_CODE_AUTH_TOKEN",
|
|
6535
6381
|
"CLAUDE_CODE_OAUTH_TOKEN",
|
|
6536
6382
|
"CLAUDE_API_KEY"
|
|
@@ -6549,15 +6395,17 @@ ${projectInstructions}`,
|
|
|
6549
6395
|
if (config2.oauthToken) {
|
|
6550
6396
|
cleanEnv.ANTHROPIC_API_KEY = "";
|
|
6551
6397
|
cleanEnv.ANTHROPIC_BASE_URL = "";
|
|
6398
|
+
cleanEnv.ZAI_API_KEY = "";
|
|
6399
|
+
cleanEnv.ZAI_BASE_URL = "";
|
|
6552
6400
|
this.presenter.onLog(`Auth: Using Claude Max (default Claude Code credentials)`);
|
|
6553
|
-
logger.debug("[agent] Claude Max mode: Using default ~/.claude/ config, cleared
|
|
6401
|
+
logger.debug("[agent] Claude Max mode: Using default ~/.claude/ config, cleared provider credentials");
|
|
6554
6402
|
} else {
|
|
6555
6403
|
const internalConfigDir = join6(homedir2(), ".supatest", "claude-internal");
|
|
6556
6404
|
cleanEnv.CLAUDE_CONFIG_DIR = internalConfigDir;
|
|
6557
6405
|
cleanEnv.ANTHROPIC_API_KEY = config2.supatestApiKey || "";
|
|
6558
6406
|
cleanEnv.ANTHROPIC_BASE_URL = process.env.ANTHROPIC_BASE_URL || "";
|
|
6559
|
-
this.presenter.onLog(`Auth: Using Supatest API
|
|
6560
|
-
logger.debug("[agent] API
|
|
6407
|
+
this.presenter.onLog(`Auth: Using Supatest API (provider determined by backend)${process.env.ANTHROPIC_BASE_URL ? ` (base: ${process.env.ANTHROPIC_BASE_URL})` : ""}`);
|
|
6408
|
+
logger.debug("[agent] API mode: Set ANTHROPIC_API_KEY, ANTHROPIC_BASE_URL, and CLAUDE_CONFIG_DIR");
|
|
6561
6409
|
}
|
|
6562
6410
|
cleanEnv.ANTHROPIC_AUTH_TOKEN = "";
|
|
6563
6411
|
cleanEnv.CLAUDE_CODE_AUTH_TOKEN = "";
|
|
@@ -6567,12 +6415,14 @@ ${projectInstructions}`,
|
|
|
6567
6415
|
CLAUDE_CONFIG_DIR: cleanEnv.CLAUDE_CONFIG_DIR || "(using default ~/.claude/)"
|
|
6568
6416
|
});
|
|
6569
6417
|
cleanEnv.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS = "1";
|
|
6418
|
+
const selectedModel = config2.selectedModel || "premium";
|
|
6419
|
+
const defaultModel = resolveToAnthropicModel(selectedModel);
|
|
6570
6420
|
const queryOptions = {
|
|
6571
6421
|
// AbortController for cancellation support
|
|
6572
6422
|
abortController: this.abortController,
|
|
6573
6423
|
maxTurns: config2.maxIterations,
|
|
6574
6424
|
cwd,
|
|
6575
|
-
model:
|
|
6425
|
+
model: defaultModel,
|
|
6576
6426
|
permissionMode: isPlanMode ? "plan" : "bypassPermissions",
|
|
6577
6427
|
allowDangerouslySkipPermissions: !isPlanMode,
|
|
6578
6428
|
pathToClaudeCodeExecutable: claudeCodePath,
|
|
@@ -6965,6 +6815,7 @@ var init_react = __esm({
|
|
|
6965
6815
|
toolInput: input,
|
|
6966
6816
|
toolResult: void 0,
|
|
6967
6817
|
isExpanded: false,
|
|
6818
|
+
isPending: true,
|
|
6968
6819
|
toolUseId: toolId
|
|
6969
6820
|
};
|
|
6970
6821
|
this.callbacks.addMessage(message);
|
|
@@ -7010,7 +6861,8 @@ var init_react = __esm({
|
|
|
7010
6861
|
}
|
|
7011
6862
|
onToolResult(toolId, result) {
|
|
7012
6863
|
this.callbacks.updateMessageByToolId(toolId, {
|
|
7013
|
-
toolResult: result
|
|
6864
|
+
toolResult: result,
|
|
6865
|
+
isPending: false
|
|
7014
6866
|
});
|
|
7015
6867
|
streamEventAsync(this.apiClient, this.sessionId, {
|
|
7016
6868
|
type: "tool_result",
|
|
@@ -7124,16 +6976,16 @@ var init_react = __esm({
|
|
|
7124
6976
|
});
|
|
7125
6977
|
|
|
7126
6978
|
// src/ui/contexts/SessionContext.tsx
|
|
7127
|
-
import React, { createContext, useCallback, useContext, useState } from "react";
|
|
6979
|
+
import React, { createContext, useCallback, useContext, useMemo, useState } from "react";
|
|
7128
6980
|
var SessionContext, SessionProvider, useSession;
|
|
7129
6981
|
var init_SessionContext = __esm({
|
|
7130
6982
|
"src/ui/contexts/SessionContext.tsx"() {
|
|
7131
6983
|
"use strict";
|
|
7132
|
-
init_shared_es();
|
|
7133
6984
|
SessionContext = createContext(null);
|
|
7134
6985
|
SessionProvider = ({
|
|
7135
6986
|
children,
|
|
7136
|
-
initialModel
|
|
6987
|
+
initialModel,
|
|
6988
|
+
initialLlmProvider = "supatest-managed"
|
|
7137
6989
|
}) => {
|
|
7138
6990
|
const [messages, setMessages] = useState([]);
|
|
7139
6991
|
const [todos, setTodos] = useState([]);
|
|
@@ -7150,8 +7002,10 @@ var init_SessionContext = __esm({
|
|
|
7150
7002
|
const [webUrl, setWebUrl] = useState();
|
|
7151
7003
|
const [agentMode, setAgentMode] = useState("build");
|
|
7152
7004
|
const [planFilePath, setPlanFilePath] = useState();
|
|
7153
|
-
const [selectedModel, setSelectedModel] = useState(initialModel ||
|
|
7005
|
+
const [selectedModel, setSelectedModel] = useState(initialModel || "premium");
|
|
7006
|
+
const [llmProvider, setLlmProvider] = useState(initialLlmProvider);
|
|
7154
7007
|
const [allToolsExpanded, setAllToolsExpanded] = useState(true);
|
|
7008
|
+
const [toolGroupsExpanded, setToolGroupsExpanded] = useState(false);
|
|
7155
7009
|
const [staticRemountKey, setStaticRemountKey] = useState(0);
|
|
7156
7010
|
const addMessage = useCallback(
|
|
7157
7011
|
(message) => {
|
|
@@ -7234,7 +7088,10 @@ var init_SessionContext = __esm({
|
|
|
7234
7088
|
return newValue;
|
|
7235
7089
|
});
|
|
7236
7090
|
}, []);
|
|
7237
|
-
const
|
|
7091
|
+
const toggleToolGroups = useCallback(() => {
|
|
7092
|
+
setToolGroupsExpanded((prev) => !prev);
|
|
7093
|
+
}, []);
|
|
7094
|
+
const value = useMemo(() => ({
|
|
7238
7095
|
messages,
|
|
7239
7096
|
addMessage,
|
|
7240
7097
|
updateLastMessage,
|
|
@@ -7244,6 +7101,8 @@ var init_SessionContext = __esm({
|
|
|
7244
7101
|
loadMessages,
|
|
7245
7102
|
toggleAllToolOutputs,
|
|
7246
7103
|
allToolsExpanded,
|
|
7104
|
+
toolGroupsExpanded,
|
|
7105
|
+
toggleToolGroups,
|
|
7247
7106
|
todos,
|
|
7248
7107
|
setTodos,
|
|
7249
7108
|
stats,
|
|
@@ -7264,9 +7123,37 @@ var init_SessionContext = __esm({
|
|
|
7264
7123
|
setPlanFilePath,
|
|
7265
7124
|
selectedModel,
|
|
7266
7125
|
setSelectedModel,
|
|
7126
|
+
llmProvider,
|
|
7127
|
+
setLlmProvider,
|
|
7267
7128
|
staticRemountKey,
|
|
7268
7129
|
refreshStatic
|
|
7269
|
-
}
|
|
7130
|
+
}), [
|
|
7131
|
+
messages,
|
|
7132
|
+
addMessage,
|
|
7133
|
+
updateLastMessage,
|
|
7134
|
+
updateMessageById,
|
|
7135
|
+
updateMessageByToolId,
|
|
7136
|
+
clearMessages,
|
|
7137
|
+
loadMessages,
|
|
7138
|
+
toggleAllToolOutputs,
|
|
7139
|
+
allToolsExpanded,
|
|
7140
|
+
toolGroupsExpanded,
|
|
7141
|
+
toggleToolGroups,
|
|
7142
|
+
todos,
|
|
7143
|
+
stats,
|
|
7144
|
+
updateStats,
|
|
7145
|
+
usageStats,
|
|
7146
|
+
isAgentRunning,
|
|
7147
|
+
shouldInterruptAgent,
|
|
7148
|
+
sessionId,
|
|
7149
|
+
webUrl,
|
|
7150
|
+
agentMode,
|
|
7151
|
+
planFilePath,
|
|
7152
|
+
selectedModel,
|
|
7153
|
+
llmProvider,
|
|
7154
|
+
staticRemountKey,
|
|
7155
|
+
refreshStatic
|
|
7156
|
+
]);
|
|
7270
7157
|
return /* @__PURE__ */ React.createElement(SessionContext.Provider, { value }, children);
|
|
7271
7158
|
};
|
|
7272
7159
|
useSession = () => {
|
|
@@ -7305,7 +7192,7 @@ var init_theme = __esm({
|
|
|
7305
7192
|
text: {
|
|
7306
7193
|
primary: "#FFFFFF",
|
|
7307
7194
|
secondary: "#A0AEC0",
|
|
7308
|
-
dim: "#
|
|
7195
|
+
dim: "#8C99B2",
|
|
7309
7196
|
accent: "#38B2AC",
|
|
7310
7197
|
// Cyan/teal accent
|
|
7311
7198
|
success: "#48BB78",
|
|
@@ -7321,7 +7208,7 @@ var init_theme = __esm({
|
|
|
7321
7208
|
},
|
|
7322
7209
|
// Borders
|
|
7323
7210
|
border: {
|
|
7324
|
-
default: "#
|
|
7211
|
+
default: "#8C99B2",
|
|
7325
7212
|
accent: "#38B2AC",
|
|
7326
7213
|
error: "#F56565"
|
|
7327
7214
|
},
|
|
@@ -7353,7 +7240,6 @@ var init_theme = __esm({
|
|
|
7353
7240
|
|
|
7354
7241
|
// src/ui/components/Header.tsx
|
|
7355
7242
|
import { Box, Text } from "ink";
|
|
7356
|
-
import Gradient from "ink-gradient";
|
|
7357
7243
|
import React2 from "react";
|
|
7358
7244
|
var Header;
|
|
7359
7245
|
var init_Header = __esm({
|
|
@@ -7362,7 +7248,7 @@ var init_Header = __esm({
|
|
|
7362
7248
|
init_banner();
|
|
7363
7249
|
init_version();
|
|
7364
7250
|
init_theme();
|
|
7365
|
-
Header = ({ currentFolder, gitBranch, headless = false }) => {
|
|
7251
|
+
Header = React2.memo(({ currentFolder, gitBranch, headless = false }) => {
|
|
7366
7252
|
const version = CLI_VERSION;
|
|
7367
7253
|
const banner = getBanner();
|
|
7368
7254
|
const infoParts = [`v${version}`];
|
|
@@ -7383,11 +7269,11 @@ var init_Header = __esm({
|
|
|
7383
7269
|
marginTop: 5,
|
|
7384
7270
|
width: "100%"
|
|
7385
7271
|
},
|
|
7386
|
-
/* @__PURE__ */ React2.createElement(
|
|
7272
|
+
/* @__PURE__ */ React2.createElement(Text, { color: theme.text.accent }, banner),
|
|
7387
7273
|
/* @__PURE__ */ React2.createElement(Box, { justifyContent: "center", marginTop: 0 }, /* @__PURE__ */ React2.createElement(Text, { color: theme.text.dim }, infoLine)),
|
|
7388
7274
|
!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
7275
|
);
|
|
7390
|
-
};
|
|
7276
|
+
});
|
|
7391
7277
|
}
|
|
7392
7278
|
});
|
|
7393
7279
|
|
|
@@ -7395,7 +7281,7 @@ var init_Header = __esm({
|
|
|
7395
7281
|
import chalk2 from "chalk";
|
|
7396
7282
|
import { Box as Box2, Text as Text2 } from "ink";
|
|
7397
7283
|
import { all, createLowlight } from "lowlight";
|
|
7398
|
-
import React3, { useMemo } from "react";
|
|
7284
|
+
import React3, { useMemo as useMemo2 } from "react";
|
|
7399
7285
|
function parseMarkdownSections(text) {
|
|
7400
7286
|
const sections = [];
|
|
7401
7287
|
const lines = text.split(/\r?\n/);
|
|
@@ -7555,7 +7441,7 @@ var init_markdown = __esm({
|
|
|
7555
7441
|
text,
|
|
7556
7442
|
isPending = false
|
|
7557
7443
|
}) => {
|
|
7558
|
-
const sections =
|
|
7444
|
+
const sections = useMemo2(() => parseMarkdownSections(text), [text]);
|
|
7559
7445
|
const elements = sections.map((section, index) => {
|
|
7560
7446
|
if (section.type === "table" && section.tableRows) {
|
|
7561
7447
|
return /* @__PURE__ */ React3.createElement(Table, { key: `table-${index}`, rows: section.tableRows });
|
|
@@ -8088,32 +7974,66 @@ var init_ToolMessage = __esm({
|
|
|
8088
7974
|
}
|
|
8089
7975
|
});
|
|
8090
7976
|
|
|
8091
|
-
// src/ui/components/messages/
|
|
7977
|
+
// src/ui/components/messages/ToolGroup.tsx
|
|
8092
7978
|
import { Box as Box9, Text as Text9 } from "ink";
|
|
8093
7979
|
import React10 from "react";
|
|
7980
|
+
var ToolGroup;
|
|
7981
|
+
var init_ToolGroup = __esm({
|
|
7982
|
+
"src/ui/components/messages/ToolGroup.tsx"() {
|
|
7983
|
+
"use strict";
|
|
7984
|
+
init_shared_es();
|
|
7985
|
+
init_theme();
|
|
7986
|
+
init_ToolMessage();
|
|
7987
|
+
ToolGroup = ({
|
|
7988
|
+
tools,
|
|
7989
|
+
isExpanded,
|
|
7990
|
+
onToggle,
|
|
7991
|
+
onToggleTool
|
|
7992
|
+
}) => {
|
|
7993
|
+
const toolGroups = getToolGroupCounts(tools);
|
|
7994
|
+
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(
|
|
7995
|
+
ToolMessage,
|
|
7996
|
+
{
|
|
7997
|
+
description: tool.description,
|
|
7998
|
+
id: tool.id,
|
|
7999
|
+
input: tool.input,
|
|
8000
|
+
isExpanded: tool.isExpanded,
|
|
8001
|
+
key: tool.id,
|
|
8002
|
+
onToggle: onToggleTool,
|
|
8003
|
+
result: tool.result,
|
|
8004
|
+
toolName: tool.toolName
|
|
8005
|
+
}
|
|
8006
|
+
))));
|
|
8007
|
+
};
|
|
8008
|
+
}
|
|
8009
|
+
});
|
|
8010
|
+
|
|
8011
|
+
// src/ui/components/messages/UserMessage.tsx
|
|
8012
|
+
import { Box as Box10, Text as Text10 } from "ink";
|
|
8013
|
+
import React11 from "react";
|
|
8094
8014
|
var UserMessage;
|
|
8095
8015
|
var init_UserMessage = __esm({
|
|
8096
8016
|
"src/ui/components/messages/UserMessage.tsx"() {
|
|
8097
8017
|
"use strict";
|
|
8098
8018
|
init_theme();
|
|
8099
8019
|
UserMessage = ({ text }) => {
|
|
8100
|
-
return /* @__PURE__ */
|
|
8101
|
-
|
|
8020
|
+
return /* @__PURE__ */ React11.createElement(Box10, { alignItems: "center", flexDirection: "row", marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text10, { color: theme.text.info }, "\u{1F464} "), /* @__PURE__ */ React11.createElement(
|
|
8021
|
+
Box10,
|
|
8102
8022
|
{
|
|
8103
8023
|
borderColor: theme.text.dim,
|
|
8104
8024
|
borderStyle: "round",
|
|
8105
8025
|
paddingLeft: 1,
|
|
8106
8026
|
paddingRight: 1
|
|
8107
8027
|
},
|
|
8108
|
-
/* @__PURE__ */
|
|
8028
|
+
/* @__PURE__ */ React11.createElement(Text10, { color: theme.text.secondary }, text)
|
|
8109
8029
|
));
|
|
8110
8030
|
};
|
|
8111
8031
|
}
|
|
8112
8032
|
});
|
|
8113
8033
|
|
|
8114
8034
|
// src/ui/components/QueuedMessageDisplay.tsx
|
|
8115
|
-
import { Box as
|
|
8116
|
-
import
|
|
8035
|
+
import { Box as Box11, Text as Text11 } from "ink";
|
|
8036
|
+
import React12 from "react";
|
|
8117
8037
|
var MAX_DISPLAYED_QUEUED_MESSAGES, QueuedMessageDisplay;
|
|
8118
8038
|
var init_QueuedMessageDisplay = __esm({
|
|
8119
8039
|
"src/ui/components/QueuedMessageDisplay.tsx"() {
|
|
@@ -8126,8 +8046,8 @@ var init_QueuedMessageDisplay = __esm({
|
|
|
8126
8046
|
if (messageQueue.length === 0) {
|
|
8127
8047
|
return null;
|
|
8128
8048
|
}
|
|
8129
|
-
return /* @__PURE__ */
|
|
8130
|
-
|
|
8049
|
+
return /* @__PURE__ */ React12.createElement(
|
|
8050
|
+
Box11,
|
|
8131
8051
|
{
|
|
8132
8052
|
borderColor: theme.border.default,
|
|
8133
8053
|
borderStyle: "round",
|
|
@@ -8136,20 +8056,20 @@ var init_QueuedMessageDisplay = __esm({
|
|
|
8136
8056
|
marginTop: 0,
|
|
8137
8057
|
paddingX: 1
|
|
8138
8058
|
},
|
|
8139
|
-
/* @__PURE__ */
|
|
8059
|
+
/* @__PURE__ */ React12.createElement(Box11, { marginBottom: 0 }, /* @__PURE__ */ React12.createElement(Text11, { bold: true, color: theme.text.secondary }, "Queued (", messageQueue.length, ")")),
|
|
8140
8060
|
messageQueue.slice(0, MAX_DISPLAYED_QUEUED_MESSAGES).map((message, index) => {
|
|
8141
8061
|
const preview = message.replace(/\s+/g, " ");
|
|
8142
|
-
return /* @__PURE__ */
|
|
8062
|
+
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
8063
|
}),
|
|
8144
|
-
messageQueue.length > MAX_DISPLAYED_QUEUED_MESSAGES && /* @__PURE__ */
|
|
8064
|
+
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
8065
|
);
|
|
8146
8066
|
};
|
|
8147
8067
|
}
|
|
8148
8068
|
});
|
|
8149
8069
|
|
|
8150
8070
|
// src/ui/components/MessageList.tsx
|
|
8151
|
-
import { Box as
|
|
8152
|
-
import
|
|
8071
|
+
import { Box as Box12, Static } from "ink";
|
|
8072
|
+
import React13, { useMemo as useMemo4, useRef } from "react";
|
|
8153
8073
|
var MessageList;
|
|
8154
8074
|
var init_MessageList = __esm({
|
|
8155
8075
|
"src/ui/components/MessageList.tsx"() {
|
|
@@ -8161,17 +8081,18 @@ var init_MessageList = __esm({
|
|
|
8161
8081
|
init_LoadingMessage();
|
|
8162
8082
|
init_ThinkingMessage();
|
|
8163
8083
|
init_TodoMessage();
|
|
8084
|
+
init_ToolGroup();
|
|
8164
8085
|
init_ToolMessage();
|
|
8165
8086
|
init_UserMessage();
|
|
8166
8087
|
init_QueuedMessageDisplay();
|
|
8167
8088
|
MessageList = ({ terminalWidth, currentFolder, gitBranch, headless = false, queuedTasks = [] }) => {
|
|
8168
|
-
const { messages, updateMessageById, isAgentRunning, staticRemountKey } = useSession();
|
|
8169
|
-
const renderMessage = (message) => {
|
|
8089
|
+
const { messages, updateMessageById, isAgentRunning, staticRemountKey, toolGroupsExpanded, toggleToolGroups } = useSession();
|
|
8090
|
+
const renderMessage = (message, isInGroup = false) => {
|
|
8170
8091
|
switch (message.type) {
|
|
8171
8092
|
case "user":
|
|
8172
|
-
return /* @__PURE__ */
|
|
8093
|
+
return /* @__PURE__ */ React13.createElement(UserMessage, { key: message.id, text: message.content });
|
|
8173
8094
|
case "assistant":
|
|
8174
|
-
return /* @__PURE__ */
|
|
8095
|
+
return /* @__PURE__ */ React13.createElement(
|
|
8175
8096
|
AssistantMessage,
|
|
8176
8097
|
{
|
|
8177
8098
|
isPending: message.isPending,
|
|
@@ -8181,7 +8102,22 @@ var init_MessageList = __esm({
|
|
|
8181
8102
|
}
|
|
8182
8103
|
);
|
|
8183
8104
|
case "tool":
|
|
8184
|
-
|
|
8105
|
+
if (isInGroup) {
|
|
8106
|
+
return /* @__PURE__ */ React13.createElement(
|
|
8107
|
+
ToolMessage,
|
|
8108
|
+
{
|
|
8109
|
+
description: message.content,
|
|
8110
|
+
id: message.id,
|
|
8111
|
+
input: message.toolInput,
|
|
8112
|
+
isExpanded: message.isExpanded,
|
|
8113
|
+
key: message.id,
|
|
8114
|
+
onToggle: (id) => updateMessageById(id, { isExpanded: !message.isExpanded }),
|
|
8115
|
+
result: message.toolResult,
|
|
8116
|
+
toolName: message.toolName || "Unknown"
|
|
8117
|
+
}
|
|
8118
|
+
);
|
|
8119
|
+
}
|
|
8120
|
+
return /* @__PURE__ */ React13.createElement(
|
|
8185
8121
|
ToolMessage,
|
|
8186
8122
|
{
|
|
8187
8123
|
description: message.content,
|
|
@@ -8195,7 +8131,7 @@ var init_MessageList = __esm({
|
|
|
8195
8131
|
}
|
|
8196
8132
|
);
|
|
8197
8133
|
case "thinking":
|
|
8198
|
-
return /* @__PURE__ */
|
|
8134
|
+
return /* @__PURE__ */ React13.createElement(
|
|
8199
8135
|
ThinkingMessage,
|
|
8200
8136
|
{
|
|
8201
8137
|
content: message.content,
|
|
@@ -8206,7 +8142,7 @@ var init_MessageList = __esm({
|
|
|
8206
8142
|
}
|
|
8207
8143
|
);
|
|
8208
8144
|
case "error":
|
|
8209
|
-
return /* @__PURE__ */
|
|
8145
|
+
return /* @__PURE__ */ React13.createElement(
|
|
8210
8146
|
ErrorMessage,
|
|
8211
8147
|
{
|
|
8212
8148
|
key: message.id,
|
|
@@ -8215,37 +8151,134 @@ var init_MessageList = __esm({
|
|
|
8215
8151
|
}
|
|
8216
8152
|
);
|
|
8217
8153
|
case "todo":
|
|
8218
|
-
return /* @__PURE__ */
|
|
8154
|
+
return /* @__PURE__ */ React13.createElement(TodoMessage, { key: message.id, todos: message.todos || [] });
|
|
8219
8155
|
default:
|
|
8220
8156
|
return null;
|
|
8221
8157
|
}
|
|
8222
8158
|
};
|
|
8223
|
-
const
|
|
8159
|
+
const renderGroupedMessage = (group) => {
|
|
8160
|
+
if (group.type === "group") {
|
|
8161
|
+
return /* @__PURE__ */ React13.createElement(
|
|
8162
|
+
ToolGroup,
|
|
8163
|
+
{
|
|
8164
|
+
isExpanded: toolGroupsExpanded,
|
|
8165
|
+
key: `group-${group.messages[0].id}`,
|
|
8166
|
+
onToggle: toggleToolGroups,
|
|
8167
|
+
onToggleTool: (id) => {
|
|
8168
|
+
const msg = group.messages.find((m) => m.id === id);
|
|
8169
|
+
if (msg) {
|
|
8170
|
+
updateMessageById(id, { isExpanded: !msg.isExpanded });
|
|
8171
|
+
}
|
|
8172
|
+
},
|
|
8173
|
+
tools: group.messages.map((msg) => ({
|
|
8174
|
+
id: msg.id,
|
|
8175
|
+
toolName: msg.toolName || "Unknown",
|
|
8176
|
+
description: msg.content,
|
|
8177
|
+
input: msg.toolInput,
|
|
8178
|
+
result: msg.toolResult,
|
|
8179
|
+
isExpanded: msg.isExpanded
|
|
8180
|
+
}))
|
|
8181
|
+
}
|
|
8182
|
+
);
|
|
8183
|
+
}
|
|
8184
|
+
return renderMessage(group.messages[0]);
|
|
8185
|
+
};
|
|
8186
|
+
const lastUserMessageIndex = useMemo4(() => {
|
|
8187
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
8188
|
+
if (messages[i].type === "user") {
|
|
8189
|
+
return i;
|
|
8190
|
+
}
|
|
8191
|
+
}
|
|
8192
|
+
return -1;
|
|
8193
|
+
}, [messages]);
|
|
8194
|
+
const hasPendingAssistant = useMemo4(
|
|
8195
|
+
() => messages.some((m) => m.type === "assistant" && m.isPending),
|
|
8196
|
+
[messages]
|
|
8197
|
+
);
|
|
8198
|
+
const completedBoundaryRef = useRef(-1);
|
|
8199
|
+
const completedBoundaryKey = useMemo4(() => {
|
|
8200
|
+
const currentBoundary = lastUserMessageIndex;
|
|
8201
|
+
if (currentBoundary !== completedBoundaryRef.current) {
|
|
8202
|
+
completedBoundaryRef.current = currentBoundary;
|
|
8203
|
+
return `boundary-${currentBoundary}`;
|
|
8204
|
+
}
|
|
8205
|
+
return `boundary-${completedBoundaryRef.current}`;
|
|
8206
|
+
}, [lastUserMessageIndex]);
|
|
8207
|
+
const { completedGroups, currentTurnGroups } = useMemo4(() => {
|
|
8224
8208
|
const completed = [];
|
|
8225
|
-
const
|
|
8226
|
-
|
|
8227
|
-
|
|
8228
|
-
|
|
8209
|
+
const currentTurn = [];
|
|
8210
|
+
const processTurn = (turnMessages2, targetArray) => {
|
|
8211
|
+
let currentToolGroup = [];
|
|
8212
|
+
const flushToolGroup = () => {
|
|
8213
|
+
if (currentToolGroup.length === 0) return;
|
|
8214
|
+
if (currentToolGroup.length === 1) {
|
|
8215
|
+
targetArray.push({ type: "single", messages: [...currentToolGroup] });
|
|
8216
|
+
} else {
|
|
8217
|
+
targetArray.push({ type: "group", messages: [...currentToolGroup] });
|
|
8218
|
+
}
|
|
8219
|
+
currentToolGroup = [];
|
|
8220
|
+
};
|
|
8221
|
+
for (const msg of turnMessages2) {
|
|
8222
|
+
if (msg.type === "tool") {
|
|
8223
|
+
currentToolGroup.push(msg);
|
|
8224
|
+
} else {
|
|
8225
|
+
flushToolGroup();
|
|
8226
|
+
targetArray.push({ type: "single", messages: [msg] });
|
|
8227
|
+
}
|
|
8228
|
+
}
|
|
8229
|
+
flushToolGroup();
|
|
8230
|
+
};
|
|
8231
|
+
let turnMessages = [];
|
|
8232
|
+
for (let i = 0; i < lastUserMessageIndex; i++) {
|
|
8233
|
+
const msg = messages[i];
|
|
8234
|
+
if (msg.type === "user") {
|
|
8235
|
+
processTurn(turnMessages, completed);
|
|
8236
|
+
turnMessages = [];
|
|
8237
|
+
completed.push({ type: "single", messages: [msg] });
|
|
8229
8238
|
} else {
|
|
8230
|
-
|
|
8239
|
+
turnMessages.push(msg);
|
|
8231
8240
|
}
|
|
8232
8241
|
}
|
|
8233
|
-
|
|
8234
|
-
|
|
8235
|
-
|
|
8242
|
+
processTurn(turnMessages, completed);
|
|
8243
|
+
if (lastUserMessageIndex >= 0) {
|
|
8244
|
+
completed.push({ type: "single", messages: [messages[lastUserMessageIndex]] });
|
|
8245
|
+
}
|
|
8246
|
+
const currentTurnMessages = lastUserMessageIndex >= 0 ? messages.slice(lastUserMessageIndex + 1) : messages;
|
|
8247
|
+
processTurn(currentTurnMessages, currentTurn);
|
|
8248
|
+
return { completedGroups: completed, currentTurnGroups: currentTurn };
|
|
8249
|
+
}, [messages, lastUserMessageIndex, completedBoundaryKey]);
|
|
8250
|
+
const staticItems = useMemo4(() => [
|
|
8236
8251
|
{ id: "header", type: "header" },
|
|
8237
|
-
...
|
|
8238
|
-
|
|
8239
|
-
|
|
8252
|
+
...completedGroups.map((group, idx) => {
|
|
8253
|
+
if (group.type === "group") {
|
|
8254
|
+
return { ...group, _isGroup: true, id: `group-${idx}` };
|
|
8255
|
+
}
|
|
8256
|
+
return { ...group.messages[0], _isMessage: true };
|
|
8257
|
+
})
|
|
8258
|
+
], [completedGroups]);
|
|
8259
|
+
return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column" }, /* @__PURE__ */ React13.createElement(Static, { items: staticItems, key: staticRemountKey }, (item) => {
|
|
8240
8260
|
if (item.type === "header") {
|
|
8241
|
-
return /* @__PURE__ */
|
|
8261
|
+
return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", key: "header" }, /* @__PURE__ */ React13.createElement(Header, { currentFolder, gitBranch, headless }));
|
|
8262
|
+
}
|
|
8263
|
+
if (item._isGroup) {
|
|
8264
|
+
const content2 = renderGroupedMessage(item);
|
|
8265
|
+
if (!content2) {
|
|
8266
|
+
return null;
|
|
8267
|
+
}
|
|
8268
|
+
return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", key: item.id, width: "100%" }, content2);
|
|
8242
8269
|
}
|
|
8243
8270
|
const content = renderMessage(item);
|
|
8244
8271
|
if (!content) {
|
|
8245
8272
|
return null;
|
|
8246
8273
|
}
|
|
8247
|
-
return /* @__PURE__ */
|
|
8248
|
-
}),
|
|
8274
|
+
return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", key: item.id, width: "100%" }, content);
|
|
8275
|
+
}), currentTurnGroups.map((group, idx) => {
|
|
8276
|
+
const content = renderGroupedMessage(group);
|
|
8277
|
+
if (!content) {
|
|
8278
|
+
return null;
|
|
8279
|
+
}
|
|
8280
|
+
return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", key: group.type === "group" ? `current-group-${idx}` : group.messages[0].id, width: "100%" }, content);
|
|
8281
|
+
}), /* @__PURE__ */ React13.createElement(QueuedMessageDisplay, { messageQueue: queuedTasks }), isAgentRunning && !hasPendingAssistant && /* @__PURE__ */ React13.createElement(LoadingMessage, { headless, key: "loading" }));
|
|
8249
8282
|
};
|
|
8250
8283
|
}
|
|
8251
8284
|
});
|
|
@@ -8372,21 +8405,21 @@ var init_encryption = __esm({
|
|
|
8372
8405
|
});
|
|
8373
8406
|
|
|
8374
8407
|
// src/utils/token-storage.ts
|
|
8375
|
-
import { existsSync as
|
|
8376
|
-
import { homedir as
|
|
8377
|
-
import { join as
|
|
8408
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync4, unlinkSync as unlinkSync2, writeFileSync } from "fs";
|
|
8409
|
+
import { homedir as homedir4 } from "os";
|
|
8410
|
+
import { join as join7 } from "path";
|
|
8378
8411
|
function getTokenFilePath() {
|
|
8379
8412
|
const apiUrl = process.env.SUPATEST_API_URL || PRODUCTION_API_URL;
|
|
8380
8413
|
if (apiUrl === PRODUCTION_API_URL) {
|
|
8381
|
-
return
|
|
8414
|
+
return join7(CONFIG_DIR, "token.json");
|
|
8382
8415
|
}
|
|
8383
|
-
return
|
|
8416
|
+
return join7(CONFIG_DIR, "token.local.json");
|
|
8384
8417
|
}
|
|
8385
8418
|
function isV2Format(stored) {
|
|
8386
8419
|
return "version" in stored && stored.version === 2;
|
|
8387
8420
|
}
|
|
8388
8421
|
function ensureConfigDir() {
|
|
8389
|
-
if (!
|
|
8422
|
+
if (!existsSync5(CONFIG_DIR)) {
|
|
8390
8423
|
mkdirSync2(CONFIG_DIR, { recursive: true, mode: 448 });
|
|
8391
8424
|
}
|
|
8392
8425
|
}
|
|
@@ -8406,11 +8439,11 @@ function saveToken(token, expiresAt) {
|
|
|
8406
8439
|
}
|
|
8407
8440
|
function loadToken() {
|
|
8408
8441
|
const tokenFile = getTokenFilePath();
|
|
8409
|
-
if (!
|
|
8442
|
+
if (!existsSync5(tokenFile)) {
|
|
8410
8443
|
return null;
|
|
8411
8444
|
}
|
|
8412
8445
|
try {
|
|
8413
|
-
const data =
|
|
8446
|
+
const data = readFileSync4(tokenFile, "utf8");
|
|
8414
8447
|
const stored = JSON.parse(data);
|
|
8415
8448
|
let payload;
|
|
8416
8449
|
if (isV2Format(stored)) {
|
|
@@ -8439,7 +8472,7 @@ function loadToken() {
|
|
|
8439
8472
|
}
|
|
8440
8473
|
function removeToken() {
|
|
8441
8474
|
const tokenFile = getTokenFilePath();
|
|
8442
|
-
if (
|
|
8475
|
+
if (existsSync5(tokenFile)) {
|
|
8443
8476
|
unlinkSync2(tokenFile);
|
|
8444
8477
|
}
|
|
8445
8478
|
}
|
|
@@ -8448,10 +8481,10 @@ var init_token_storage = __esm({
|
|
|
8448
8481
|
"src/utils/token-storage.ts"() {
|
|
8449
8482
|
"use strict";
|
|
8450
8483
|
init_encryption();
|
|
8451
|
-
CONFIG_DIR =
|
|
8484
|
+
CONFIG_DIR = join7(homedir4(), ".supatest");
|
|
8452
8485
|
PRODUCTION_API_URL = "https://code-api.supatest.ai";
|
|
8453
8486
|
STORAGE_VERSION = 2;
|
|
8454
|
-
TOKEN_FILE =
|
|
8487
|
+
TOKEN_FILE = join7(CONFIG_DIR, "token.json");
|
|
8455
8488
|
}
|
|
8456
8489
|
});
|
|
8457
8490
|
|
|
@@ -8920,6 +8953,128 @@ var init_login = __esm({
|
|
|
8920
8953
|
}
|
|
8921
8954
|
});
|
|
8922
8955
|
|
|
8956
|
+
// src/utils/claude-max.ts
|
|
8957
|
+
import { execSync as execSync5 } from "child_process";
|
|
8958
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
|
|
8959
|
+
import { homedir as homedir5 } from "os";
|
|
8960
|
+
import { join as join8 } from "path";
|
|
8961
|
+
function isClaudeMaxAvailable() {
|
|
8962
|
+
const platform2 = process.platform;
|
|
8963
|
+
logger.debug("[claude-max] Checking Claude Code credentials", { platform: platform2 });
|
|
8964
|
+
if (platform2 === "darwin") {
|
|
8965
|
+
return checkMacOSKeychain();
|
|
8966
|
+
} else {
|
|
8967
|
+
return checkCredentialsFile();
|
|
8968
|
+
}
|
|
8969
|
+
}
|
|
8970
|
+
function checkMacOSKeychain() {
|
|
8971
|
+
try {
|
|
8972
|
+
const credentialsJson = execSync5(
|
|
8973
|
+
'security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null',
|
|
8974
|
+
{ encoding: "utf-8" }
|
|
8975
|
+
).trim();
|
|
8976
|
+
if (!credentialsJson) {
|
|
8977
|
+
logger.debug("[claude-max] No credentials found in macOS keychain");
|
|
8978
|
+
return false;
|
|
8979
|
+
}
|
|
8980
|
+
const credentials = JSON.parse(credentialsJson);
|
|
8981
|
+
const hasOauth = !!credentials.claudeAiOauth?.accessToken;
|
|
8982
|
+
logger.debug("[claude-max] macOS keychain credentials check", {
|
|
8983
|
+
hasOauth,
|
|
8984
|
+
hasRefreshToken: !!credentials.claudeAiOauth?.refreshToken
|
|
8985
|
+
});
|
|
8986
|
+
return hasOauth;
|
|
8987
|
+
} catch (error) {
|
|
8988
|
+
logger.debug("[claude-max] Error checking macOS keychain", {
|
|
8989
|
+
error: error instanceof Error ? error.message : String(error)
|
|
8990
|
+
});
|
|
8991
|
+
return false;
|
|
8992
|
+
}
|
|
8993
|
+
}
|
|
8994
|
+
function checkCredentialsFile() {
|
|
8995
|
+
try {
|
|
8996
|
+
const credentialsPath = join8(homedir5(), ".claude", ".credentials.json");
|
|
8997
|
+
if (!existsSync6(credentialsPath)) {
|
|
8998
|
+
logger.debug("[claude-max] Credentials file not found", { path: credentialsPath });
|
|
8999
|
+
return false;
|
|
9000
|
+
}
|
|
9001
|
+
const credentialsJson = readFileSync5(credentialsPath, "utf-8");
|
|
9002
|
+
const credentials = JSON.parse(credentialsJson);
|
|
9003
|
+
const hasOauth = !!credentials.claudeAiOauth?.accessToken;
|
|
9004
|
+
logger.debug("[claude-max] Credentials file check", {
|
|
9005
|
+
path: credentialsPath,
|
|
9006
|
+
hasOauth,
|
|
9007
|
+
hasRefreshToken: !!credentials.claudeAiOauth?.refreshToken
|
|
9008
|
+
});
|
|
9009
|
+
return hasOauth;
|
|
9010
|
+
} catch (error) {
|
|
9011
|
+
logger.debug("[claude-max] Error checking credentials file", {
|
|
9012
|
+
error: error instanceof Error ? error.message : String(error)
|
|
9013
|
+
});
|
|
9014
|
+
return false;
|
|
9015
|
+
}
|
|
9016
|
+
}
|
|
9017
|
+
var init_claude_max = __esm({
|
|
9018
|
+
"src/utils/claude-max.ts"() {
|
|
9019
|
+
"use strict";
|
|
9020
|
+
init_logger();
|
|
9021
|
+
}
|
|
9022
|
+
});
|
|
9023
|
+
|
|
9024
|
+
// src/utils/settings-loader.ts
|
|
9025
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "fs";
|
|
9026
|
+
import { join as join9 } from "path";
|
|
9027
|
+
function loadSupatestSettings(cwd) {
|
|
9028
|
+
const settingsPath = join9(cwd, ".supatest", "settings.json");
|
|
9029
|
+
if (!existsSync7(settingsPath)) {
|
|
9030
|
+
return {};
|
|
9031
|
+
}
|
|
9032
|
+
try {
|
|
9033
|
+
const content = readFileSync6(settingsPath, "utf-8");
|
|
9034
|
+
return JSON.parse(content);
|
|
9035
|
+
} catch (error) {
|
|
9036
|
+
console.warn(
|
|
9037
|
+
`Warning: Failed to load settings from ${settingsPath}:`,
|
|
9038
|
+
error instanceof Error ? error.message : String(error)
|
|
9039
|
+
);
|
|
9040
|
+
return {};
|
|
9041
|
+
}
|
|
9042
|
+
}
|
|
9043
|
+
function saveSupatestSettings(cwd, settings) {
|
|
9044
|
+
const settingsDir = join9(cwd, ".supatest");
|
|
9045
|
+
const settingsPath = join9(settingsDir, "settings.json");
|
|
9046
|
+
try {
|
|
9047
|
+
if (!existsSync7(settingsDir)) {
|
|
9048
|
+
mkdirSync3(settingsDir, { recursive: true });
|
|
9049
|
+
}
|
|
9050
|
+
const existingSettings = loadSupatestSettings(cwd);
|
|
9051
|
+
const mergedSettings = {
|
|
9052
|
+
...existingSettings,
|
|
9053
|
+
...settings,
|
|
9054
|
+
// Preserve nested objects like permissions and hooks
|
|
9055
|
+
permissions: {
|
|
9056
|
+
...existingSettings.permissions,
|
|
9057
|
+
...settings.permissions
|
|
9058
|
+
},
|
|
9059
|
+
hooks: {
|
|
9060
|
+
...existingSettings.hooks,
|
|
9061
|
+
...settings.hooks
|
|
9062
|
+
}
|
|
9063
|
+
};
|
|
9064
|
+
writeFileSync2(settingsPath, JSON.stringify(mergedSettings, null, 2), "utf-8");
|
|
9065
|
+
} catch (error) {
|
|
9066
|
+
console.warn(
|
|
9067
|
+
`Warning: Failed to save settings to ${settingsPath}:`,
|
|
9068
|
+
error instanceof Error ? error.message : String(error)
|
|
9069
|
+
);
|
|
9070
|
+
}
|
|
9071
|
+
}
|
|
9072
|
+
var init_settings_loader = __esm({
|
|
9073
|
+
"src/utils/settings-loader.ts"() {
|
|
9074
|
+
"use strict";
|
|
9075
|
+
}
|
|
9076
|
+
});
|
|
9077
|
+
|
|
8923
9078
|
// src/ui/types/auth.ts
|
|
8924
9079
|
var init_auth = __esm({
|
|
8925
9080
|
"src/ui/types/auth.ts"() {
|
|
@@ -8928,8 +9083,8 @@ var init_auth = __esm({
|
|
|
8928
9083
|
});
|
|
8929
9084
|
|
|
8930
9085
|
// src/ui/components/AuthBanner.tsx
|
|
8931
|
-
import { Box as
|
|
8932
|
-
import
|
|
9086
|
+
import { Box as Box14, Text as Text12 } from "ink";
|
|
9087
|
+
import React16 from "react";
|
|
8933
9088
|
var AuthBanner;
|
|
8934
9089
|
var init_AuthBanner = __esm({
|
|
8935
9090
|
"src/ui/components/AuthBanner.tsx"() {
|
|
@@ -8941,10 +9096,10 @@ var init_AuthBanner = __esm({
|
|
|
8941
9096
|
return null;
|
|
8942
9097
|
}
|
|
8943
9098
|
if (authState === "authenticating" /* Authenticating */) {
|
|
8944
|
-
return /* @__PURE__ */
|
|
9099
|
+
return /* @__PURE__ */ React16.createElement(Box14, { marginBottom: 0, paddingX: 1 }, /* @__PURE__ */ React16.createElement(Text12, { color: theme.text.info }, "Authenticating..."));
|
|
8945
9100
|
}
|
|
8946
|
-
return /* @__PURE__ */
|
|
8947
|
-
|
|
9101
|
+
return /* @__PURE__ */ React16.createElement(
|
|
9102
|
+
Box14,
|
|
8948
9103
|
{
|
|
8949
9104
|
borderColor: theme.text.warning,
|
|
8950
9105
|
borderStyle: "round",
|
|
@@ -8952,10 +9107,10 @@ var init_AuthBanner = __esm({
|
|
|
8952
9107
|
paddingX: 1,
|
|
8953
9108
|
paddingY: 0
|
|
8954
9109
|
},
|
|
8955
|
-
/* @__PURE__ */
|
|
8956
|
-
/* @__PURE__ */
|
|
8957
|
-
/* @__PURE__ */
|
|
8958
|
-
/* @__PURE__ */
|
|
9110
|
+
/* @__PURE__ */ React16.createElement(Text12, { bold: true, color: theme.text.warning }, "Not logged in"),
|
|
9111
|
+
/* @__PURE__ */ React16.createElement(Text12, { color: theme.text.dim }, " - Type "),
|
|
9112
|
+
/* @__PURE__ */ React16.createElement(Text12, { color: theme.text.info }, "/login"),
|
|
9113
|
+
/* @__PURE__ */ React16.createElement(Text12, { color: theme.text.dim }, " to authenticate")
|
|
8959
9114
|
);
|
|
8960
9115
|
};
|
|
8961
9116
|
}
|
|
@@ -9177,12 +9332,12 @@ var init_mouse = __esm({
|
|
|
9177
9332
|
|
|
9178
9333
|
// src/ui/contexts/KeypressContext.tsx
|
|
9179
9334
|
import { useStdin as useStdin2 } from "ink";
|
|
9180
|
-
import
|
|
9335
|
+
import React17, {
|
|
9181
9336
|
createContext as createContext2,
|
|
9182
9337
|
useCallback as useCallback2,
|
|
9183
9338
|
useContext as useContext2,
|
|
9184
9339
|
useEffect as useEffect4,
|
|
9185
|
-
useRef as
|
|
9340
|
+
useRef as useRef3
|
|
9186
9341
|
} from "react";
|
|
9187
9342
|
function charLengthAt(str, i) {
|
|
9188
9343
|
if (str.length <= i) {
|
|
@@ -9469,7 +9624,7 @@ function KeypressProvider({
|
|
|
9469
9624
|
debugKeystrokeLogging
|
|
9470
9625
|
}) {
|
|
9471
9626
|
const { stdin, setRawMode } = useStdin2();
|
|
9472
|
-
const subscribers =
|
|
9627
|
+
const subscribers = useRef3(/* @__PURE__ */ new Set()).current;
|
|
9473
9628
|
const subscribe = useCallback2(
|
|
9474
9629
|
(handler) => subscribers.add(handler),
|
|
9475
9630
|
[subscribers]
|
|
@@ -9500,7 +9655,7 @@ function KeypressProvider({
|
|
|
9500
9655
|
}
|
|
9501
9656
|
};
|
|
9502
9657
|
}, [stdin, setRawMode, config2, debugKeystrokeLogging, broadcast]);
|
|
9503
|
-
return /* @__PURE__ */
|
|
9658
|
+
return /* @__PURE__ */ React17.createElement(KeypressContext.Provider, { value: { subscribe, unsubscribe } }, children);
|
|
9504
9659
|
}
|
|
9505
9660
|
var BACKSLASH_ENTER_TIMEOUT, ESC_TIMEOUT, PASTE_TIMEOUT, KEY_INFO_MAP, kUTF16SurrogateThreshold, MAC_ALT_KEY_CHARACTER_MAP, KeypressContext;
|
|
9506
9661
|
var init_KeypressContext = __esm({
|
|
@@ -9632,8 +9787,8 @@ var init_useKeypress = __esm({
|
|
|
9632
9787
|
});
|
|
9633
9788
|
|
|
9634
9789
|
// src/ui/components/AuthDialog.tsx
|
|
9635
|
-
import { Box as
|
|
9636
|
-
import
|
|
9790
|
+
import { Box as Box15, Text as Text13 } from "ink";
|
|
9791
|
+
import React18 from "react";
|
|
9637
9792
|
var AuthDialog;
|
|
9638
9793
|
var init_AuthDialog = __esm({
|
|
9639
9794
|
"src/ui/components/AuthDialog.tsx"() {
|
|
@@ -9649,8 +9804,8 @@ var init_AuthDialog = __esm({
|
|
|
9649
9804
|
},
|
|
9650
9805
|
{ isActive: true }
|
|
9651
9806
|
);
|
|
9652
|
-
return /* @__PURE__ */
|
|
9653
|
-
|
|
9807
|
+
return /* @__PURE__ */ React18.createElement(
|
|
9808
|
+
Box15,
|
|
9654
9809
|
{
|
|
9655
9810
|
borderColor: theme.border.accent,
|
|
9656
9811
|
borderStyle: "round",
|
|
@@ -9658,18 +9813,18 @@ var init_AuthDialog = __esm({
|
|
|
9658
9813
|
paddingX: 2,
|
|
9659
9814
|
paddingY: 1
|
|
9660
9815
|
},
|
|
9661
|
-
/* @__PURE__ */
|
|
9662
|
-
/* @__PURE__ */
|
|
9663
|
-
/* @__PURE__ */
|
|
9664
|
-
/* @__PURE__ */
|
|
9816
|
+
/* @__PURE__ */ React18.createElement(Text13, { bold: true, color: theme.text.primary }, "Welcome to Supatest CLI"),
|
|
9817
|
+
/* @__PURE__ */ React18.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React18.createElement(Text13, { color: theme.text.secondary }, "Authentication is required to use Supatest CLI.")),
|
|
9818
|
+
/* @__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"))),
|
|
9819
|
+
/* @__PURE__ */ React18.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React18.createElement(Text13, { color: theme.text.dim, italic: true }, "Press Enter or L to login"))
|
|
9665
9820
|
);
|
|
9666
9821
|
};
|
|
9667
9822
|
}
|
|
9668
9823
|
});
|
|
9669
9824
|
|
|
9670
9825
|
// src/ui/components/FeedbackDialog.tsx
|
|
9671
|
-
import { Box as
|
|
9672
|
-
import
|
|
9826
|
+
import { Box as Box16, Text as Text14, useInput } from "ink";
|
|
9827
|
+
import React19, { useState as useState5 } from "react";
|
|
9673
9828
|
var CATEGORY_ORDER, FeedbackDialog;
|
|
9674
9829
|
var init_FeedbackDialog = __esm({
|
|
9675
9830
|
"src/ui/components/FeedbackDialog.tsx"() {
|
|
@@ -9764,21 +9919,21 @@ var init_FeedbackDialog = __esm({
|
|
|
9764
9919
|
});
|
|
9765
9920
|
const renderDescription = () => {
|
|
9766
9921
|
if (!description && focus !== "description") {
|
|
9767
|
-
return /* @__PURE__ */
|
|
9922
|
+
return /* @__PURE__ */ React19.createElement(Text14, { color: theme.text.dim, italic: true }, "Enter your feedback...");
|
|
9768
9923
|
}
|
|
9769
9924
|
if (focus === "description") {
|
|
9770
9925
|
const before = description.slice(0, cursorPosition);
|
|
9771
9926
|
const charAtCursor = description[cursorPosition] || " ";
|
|
9772
9927
|
const after = description.slice(cursorPosition + 1);
|
|
9773
9928
|
if (!description) {
|
|
9774
|
-
return /* @__PURE__ */
|
|
9929
|
+
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
9930
|
}
|
|
9776
|
-
return /* @__PURE__ */
|
|
9931
|
+
return /* @__PURE__ */ React19.createElement(Text14, { color: theme.text.primary }, before, /* @__PURE__ */ React19.createElement(Text14, { inverse: true }, charAtCursor), after);
|
|
9777
9932
|
}
|
|
9778
|
-
return /* @__PURE__ */
|
|
9933
|
+
return /* @__PURE__ */ React19.createElement(Text14, { color: theme.text.primary }, description);
|
|
9779
9934
|
};
|
|
9780
|
-
return /* @__PURE__ */
|
|
9781
|
-
|
|
9935
|
+
return /* @__PURE__ */ React19.createElement(
|
|
9936
|
+
Box16,
|
|
9782
9937
|
{
|
|
9783
9938
|
borderColor: theme.border.accent,
|
|
9784
9939
|
borderStyle: "round",
|
|
@@ -9786,18 +9941,18 @@ var init_FeedbackDialog = __esm({
|
|
|
9786
9941
|
paddingX: 2,
|
|
9787
9942
|
paddingY: 1
|
|
9788
9943
|
},
|
|
9789
|
-
/* @__PURE__ */
|
|
9790
|
-
/* @__PURE__ */
|
|
9791
|
-
/* @__PURE__ */
|
|
9792
|
-
/* @__PURE__ */
|
|
9944
|
+
/* @__PURE__ */ React19.createElement(Text14, { bold: true, color: theme.text.accent }, "Report Issue"),
|
|
9945
|
+
/* @__PURE__ */ React19.createElement(Box16, { marginTop: 1 }),
|
|
9946
|
+
/* @__PURE__ */ React19.createElement(Text14, { bold: true, color: theme.text.secondary }, "Category:"),
|
|
9947
|
+
/* @__PURE__ */ React19.createElement(Box16, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, CATEGORY_ORDER.map((category) => {
|
|
9793
9948
|
const isSelected = selectedCategory === category;
|
|
9794
9949
|
const info = FEEDBACK_CATEGORIES[category];
|
|
9795
|
-
return /* @__PURE__ */
|
|
9950
|
+
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
9951
|
})),
|
|
9797
|
-
/* @__PURE__ */
|
|
9798
|
-
/* @__PURE__ */
|
|
9799
|
-
/* @__PURE__ */
|
|
9800
|
-
|
|
9952
|
+
/* @__PURE__ */ React19.createElement(Box16, { marginTop: 1 }),
|
|
9953
|
+
/* @__PURE__ */ React19.createElement(Text14, { bold: true, color: theme.text.secondary }, "Description:"),
|
|
9954
|
+
/* @__PURE__ */ React19.createElement(
|
|
9955
|
+
Box16,
|
|
9801
9956
|
{
|
|
9802
9957
|
borderColor: focus === "description" ? theme.border.accent : theme.border.default,
|
|
9803
9958
|
borderStyle: "round",
|
|
@@ -9808,8 +9963,8 @@ var init_FeedbackDialog = __esm({
|
|
|
9808
9963
|
},
|
|
9809
9964
|
renderDescription()
|
|
9810
9965
|
),
|
|
9811
|
-
/* @__PURE__ */
|
|
9812
|
-
/* @__PURE__ */
|
|
9966
|
+
/* @__PURE__ */ React19.createElement(Box16, { marginTop: 1 }),
|
|
9967
|
+
/* @__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
9968
|
);
|
|
9814
9969
|
};
|
|
9815
9970
|
}
|
|
@@ -9918,8 +10073,8 @@ var init_context_builder = __esm({
|
|
|
9918
10073
|
});
|
|
9919
10074
|
|
|
9920
10075
|
// src/ui/components/RunSelector.tsx
|
|
9921
|
-
import { Box as
|
|
9922
|
-
import
|
|
10076
|
+
import { Box as Box17, Text as Text15, useInput as useInput2 } from "ink";
|
|
10077
|
+
import React20, { useEffect as useEffect6, useState as useState6 } from "react";
|
|
9923
10078
|
function formatRelativeTime(date) {
|
|
9924
10079
|
const now = /* @__PURE__ */ new Date();
|
|
9925
10080
|
const diffMs = now.getTime() - date.getTime();
|
|
@@ -10013,13 +10168,13 @@ var init_RunSelector = __esm({
|
|
|
10013
10168
|
}
|
|
10014
10169
|
});
|
|
10015
10170
|
if (error) {
|
|
10016
|
-
return /* @__PURE__ */
|
|
10171
|
+
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
10172
|
}
|
|
10018
10173
|
if (allRuns.length === 0 && isLoading) {
|
|
10019
|
-
return /* @__PURE__ */
|
|
10174
|
+
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
10175
|
}
|
|
10021
10176
|
if (allRuns.length === 0 && !isLoading) {
|
|
10022
|
-
return /* @__PURE__ */
|
|
10177
|
+
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
10178
|
}
|
|
10024
10179
|
let startIndex;
|
|
10025
10180
|
let endIndex;
|
|
@@ -10036,7 +10191,7 @@ var init_RunSelector = __esm({
|
|
|
10036
10191
|
}
|
|
10037
10192
|
}
|
|
10038
10193
|
const visibleRuns = allRuns.slice(startIndex, endIndex);
|
|
10039
|
-
return /* @__PURE__ */
|
|
10194
|
+
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
10195
|
const actualIndex = startIndex + index;
|
|
10041
10196
|
const isSelected = actualIndex === selectedIndex;
|
|
10042
10197
|
const branch = run.git?.branch || "unknown";
|
|
@@ -10048,15 +10203,15 @@ var init_RunSelector = __esm({
|
|
|
10048
10203
|
const hasFailures = failed > 0;
|
|
10049
10204
|
const indicator = isSelected ? "\u25B6 " : " ";
|
|
10050
10205
|
const bgColor = isSelected ? theme.text.accent : void 0;
|
|
10051
|
-
return /* @__PURE__ */
|
|
10052
|
-
})), /* @__PURE__ */
|
|
10206
|
+
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));
|
|
10207
|
+
})), /* @__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
10208
|
};
|
|
10054
10209
|
}
|
|
10055
10210
|
});
|
|
10056
10211
|
|
|
10057
10212
|
// src/ui/components/TestSelector.tsx
|
|
10058
|
-
import { Box as
|
|
10059
|
-
import
|
|
10213
|
+
import { Box as Box18, Text as Text16, useInput as useInput3 } from "ink";
|
|
10214
|
+
import React21, { useEffect as useEffect7, useState as useState7 } from "react";
|
|
10060
10215
|
var PAGE_SIZE2, VISIBLE_ITEMS2, TestSelector;
|
|
10061
10216
|
var init_TestSelector = __esm({
|
|
10062
10217
|
"src/ui/components/TestSelector.tsx"() {
|
|
@@ -10168,13 +10323,13 @@ var init_TestSelector = __esm({
|
|
|
10168
10323
|
}
|
|
10169
10324
|
});
|
|
10170
10325
|
if (error) {
|
|
10171
|
-
return /* @__PURE__ */
|
|
10326
|
+
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
10327
|
}
|
|
10173
10328
|
if (allTests.length === 0 && isLoading) {
|
|
10174
|
-
return /* @__PURE__ */
|
|
10329
|
+
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
10330
|
}
|
|
10176
10331
|
if (allTests.length === 0 && !isLoading) {
|
|
10177
|
-
return /* @__PURE__ */
|
|
10332
|
+
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
10333
|
}
|
|
10179
10334
|
const testStartIndex = Math.max(0, cursorIndex - 1);
|
|
10180
10335
|
const adjustedStart = isOnFixAll ? 0 : Math.max(0, testStartIndex - Math.floor(VISIBLE_ITEMS2 / 2));
|
|
@@ -10182,8 +10337,8 @@ var init_TestSelector = __esm({
|
|
|
10182
10337
|
const visibleTests = allTests.slice(adjustedStart, adjustedEnd);
|
|
10183
10338
|
const branch = run.git?.branch || "unknown";
|
|
10184
10339
|
const commit = run.git?.commit?.slice(0, 7) || "";
|
|
10185
|
-
return /* @__PURE__ */
|
|
10186
|
-
|
|
10340
|
+
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(
|
|
10341
|
+
Text16,
|
|
10187
10342
|
{
|
|
10188
10343
|
backgroundColor: isOnFixAll ? theme.text.accent : void 0,
|
|
10189
10344
|
bold: isOnFixAll,
|
|
@@ -10195,7 +10350,7 @@ var init_TestSelector = __esm({
|
|
|
10195
10350
|
" Failed Test",
|
|
10196
10351
|
allTests.length !== 1 ? "s" : "",
|
|
10197
10352
|
"]"
|
|
10198
|
-
)), /* @__PURE__ */
|
|
10353
|
+
)), /* @__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
10354
|
const actualIndex = adjustedStart + index;
|
|
10200
10355
|
const itemIndex = actualIndex + 1;
|
|
10201
10356
|
const isSelected = itemIndex === cursorIndex;
|
|
@@ -10206,15 +10361,15 @@ var init_TestSelector = __esm({
|
|
|
10206
10361
|
const title = test.title;
|
|
10207
10362
|
const indicator = isSelected ? "\u25B6 " : " ";
|
|
10208
10363
|
const bgColor = isSelected ? theme.text.accent : void 0;
|
|
10209
|
-
return /* @__PURE__ */
|
|
10210
|
-
})), /* @__PURE__ */
|
|
10364
|
+
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));
|
|
10365
|
+
})), /* @__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
10366
|
};
|
|
10212
10367
|
}
|
|
10213
10368
|
});
|
|
10214
10369
|
|
|
10215
10370
|
// src/ui/components/FixFlow.tsx
|
|
10216
|
-
import { Box as
|
|
10217
|
-
import
|
|
10371
|
+
import { Box as Box19, Text as Text17, useInput as useInput4 } from "ink";
|
|
10372
|
+
import React22, { useState as useState8 } from "react";
|
|
10218
10373
|
var FixFlow;
|
|
10219
10374
|
var init_FixFlow = __esm({
|
|
10220
10375
|
"src/ui/components/FixFlow.tsx"() {
|
|
@@ -10285,7 +10440,7 @@ var init_FixFlow = __esm({
|
|
|
10285
10440
|
};
|
|
10286
10441
|
switch (step) {
|
|
10287
10442
|
case "select-run":
|
|
10288
|
-
return /* @__PURE__ */
|
|
10443
|
+
return /* @__PURE__ */ React22.createElement(
|
|
10289
10444
|
RunSelector,
|
|
10290
10445
|
{
|
|
10291
10446
|
apiClient,
|
|
@@ -10298,7 +10453,7 @@ var init_FixFlow = __esm({
|
|
|
10298
10453
|
if (!selectedRun) {
|
|
10299
10454
|
return null;
|
|
10300
10455
|
}
|
|
10301
|
-
return /* @__PURE__ */
|
|
10456
|
+
return /* @__PURE__ */ React22.createElement(
|
|
10302
10457
|
TestSelector,
|
|
10303
10458
|
{
|
|
10304
10459
|
apiClient,
|
|
@@ -10309,13 +10464,13 @@ var init_FixFlow = __esm({
|
|
|
10309
10464
|
);
|
|
10310
10465
|
case "loading-details":
|
|
10311
10466
|
if (loadError) {
|
|
10312
|
-
return /* @__PURE__ */
|
|
10467
|
+
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
10468
|
}
|
|
10314
|
-
return /* @__PURE__ */
|
|
10469
|
+
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
10470
|
case "fixing":
|
|
10316
|
-
return /* @__PURE__ */
|
|
10471
|
+
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
10472
|
case "complete":
|
|
10318
|
-
return /* @__PURE__ */
|
|
10473
|
+
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
10474
|
default:
|
|
10320
10475
|
return null;
|
|
10321
10476
|
}
|
|
@@ -10324,8 +10479,8 @@ var init_FixFlow = __esm({
|
|
|
10324
10479
|
});
|
|
10325
10480
|
|
|
10326
10481
|
// src/ui/components/HelpMenu.tsx
|
|
10327
|
-
import { Box as
|
|
10328
|
-
import
|
|
10482
|
+
import { Box as Box20, Text as Text18, useInput as useInput5 } from "ink";
|
|
10483
|
+
import React23, { useEffect as useEffect9, useState as useState9 } from "react";
|
|
10329
10484
|
var HelpMenu;
|
|
10330
10485
|
var init_HelpMenu = __esm({
|
|
10331
10486
|
"src/ui/components/HelpMenu.tsx"() {
|
|
@@ -10344,8 +10499,8 @@ var init_HelpMenu = __esm({
|
|
|
10344
10499
|
onClose();
|
|
10345
10500
|
}
|
|
10346
10501
|
});
|
|
10347
|
-
return /* @__PURE__ */
|
|
10348
|
-
|
|
10502
|
+
return /* @__PURE__ */ React23.createElement(
|
|
10503
|
+
Box20,
|
|
10349
10504
|
{
|
|
10350
10505
|
borderColor: theme.border.accent,
|
|
10351
10506
|
borderStyle: "round",
|
|
@@ -10353,21 +10508,21 @@ var init_HelpMenu = __esm({
|
|
|
10353
10508
|
paddingX: 2,
|
|
10354
10509
|
paddingY: 1
|
|
10355
10510
|
},
|
|
10356
|
-
/* @__PURE__ */
|
|
10357
|
-
/* @__PURE__ */
|
|
10358
|
-
/* @__PURE__ */
|
|
10359
|
-
/* @__PURE__ */
|
|
10360
|
-
customCommands.length > 0 && /* @__PURE__ */
|
|
10361
|
-
/* @__PURE__ */
|
|
10362
|
-
/* @__PURE__ */
|
|
10363
|
-
/* @__PURE__ */
|
|
10364
|
-
/* @__PURE__ */
|
|
10365
|
-
/* @__PURE__ */
|
|
10366
|
-
/* @__PURE__ */
|
|
10367
|
-
/* @__PURE__ */
|
|
10368
|
-
/* @__PURE__ */
|
|
10369
|
-
/* @__PURE__ */
|
|
10370
|
-
/* @__PURE__ */
|
|
10511
|
+
/* @__PURE__ */ React23.createElement(Text18, { bold: true, color: theme.text.accent }, "\u{1F4D6} Supatest AI CLI - Help"),
|
|
10512
|
+
/* @__PURE__ */ React23.createElement(Box20, { marginTop: 1 }),
|
|
10513
|
+
/* @__PURE__ */ React23.createElement(Text18, { bold: true, color: theme.text.secondary }, "Slash Commands:"),
|
|
10514
|
+
/* @__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"))),
|
|
10515
|
+
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)"))),
|
|
10516
|
+
/* @__PURE__ */ React23.createElement(Box20, { marginTop: 1 }),
|
|
10517
|
+
/* @__PURE__ */ React23.createElement(Text18, { bold: true, color: theme.text.secondary }, "Keyboard Shortcuts:"),
|
|
10518
|
+
/* @__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"))),
|
|
10519
|
+
/* @__PURE__ */ React23.createElement(Box20, { marginTop: 1 }),
|
|
10520
|
+
/* @__PURE__ */ React23.createElement(Text18, { bold: true, color: theme.text.secondary }, "File References:"),
|
|
10521
|
+
/* @__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"')),
|
|
10522
|
+
/* @__PURE__ */ React23.createElement(Box20, { marginTop: 1 }),
|
|
10523
|
+
/* @__PURE__ */ React23.createElement(Text18, { bold: true, color: theme.text.secondary }, "Tips:"),
|
|
10524
|
+
/* @__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")),
|
|
10525
|
+
/* @__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
10526
|
);
|
|
10372
10527
|
};
|
|
10373
10528
|
}
|
|
@@ -10456,12 +10611,27 @@ var init_file_completion = __esm({
|
|
|
10456
10611
|
});
|
|
10457
10612
|
|
|
10458
10613
|
// src/ui/components/ModelSelector.tsx
|
|
10459
|
-
import { Box as
|
|
10460
|
-
import
|
|
10461
|
-
function
|
|
10462
|
-
|
|
10463
|
-
|
|
10464
|
-
|
|
10614
|
+
import { Box as Box21, Text as Text19, useInput as useInput6 } from "ink";
|
|
10615
|
+
import React24, { useState as useState10 } from "react";
|
|
10616
|
+
function getAvailableModels(isClaudeMax) {
|
|
10617
|
+
if (isClaudeMax) {
|
|
10618
|
+
return AVAILABLE_MODELS.map((m) => ({
|
|
10619
|
+
id: m.id,
|
|
10620
|
+
name: m.name,
|
|
10621
|
+
description: m.description
|
|
10622
|
+
}));
|
|
10623
|
+
}
|
|
10624
|
+
return MODEL_TIERS.map((tier) => ({
|
|
10625
|
+
id: tier,
|
|
10626
|
+
name: getModelDisplayName(tier),
|
|
10627
|
+
description: tier === "small" ? "Fast & lightweight" : tier === "medium" ? "Balanced performance" : "Most capable"
|
|
10628
|
+
}));
|
|
10629
|
+
}
|
|
10630
|
+
function getNextModel(currentModel, isClaudeMax = false) {
|
|
10631
|
+
const models = getAvailableModels(isClaudeMax);
|
|
10632
|
+
const currentIndex = models.findIndex((m) => m.id === currentModel);
|
|
10633
|
+
const nextIndex = (currentIndex + 1) % models.length;
|
|
10634
|
+
return models[nextIndex].id;
|
|
10465
10635
|
}
|
|
10466
10636
|
var ModelSelector;
|
|
10467
10637
|
var init_ModelSelector = __esm({
|
|
@@ -10472,27 +10642,29 @@ var init_ModelSelector = __esm({
|
|
|
10472
10642
|
ModelSelector = ({
|
|
10473
10643
|
currentModel,
|
|
10474
10644
|
onSelect,
|
|
10475
|
-
onCancel
|
|
10645
|
+
onCancel,
|
|
10646
|
+
isClaudeMax = false
|
|
10476
10647
|
}) => {
|
|
10477
|
-
const
|
|
10648
|
+
const models = getAvailableModels(isClaudeMax);
|
|
10649
|
+
const currentIndex = models.findIndex((m) => m.id === currentModel);
|
|
10478
10650
|
const [selectedIndex, setSelectedIndex] = useState10(currentIndex >= 0 ? currentIndex : 0);
|
|
10479
10651
|
useInput6((input, key) => {
|
|
10480
10652
|
if (key.upArrow) {
|
|
10481
|
-
setSelectedIndex((prev) => prev > 0 ? prev - 1 :
|
|
10653
|
+
setSelectedIndex((prev) => prev > 0 ? prev - 1 : models.length - 1);
|
|
10482
10654
|
} else if (key.downArrow) {
|
|
10483
|
-
setSelectedIndex((prev) => prev <
|
|
10655
|
+
setSelectedIndex((prev) => prev < models.length - 1 ? prev + 1 : 0);
|
|
10484
10656
|
} else if (key.return) {
|
|
10485
|
-
onSelect(
|
|
10657
|
+
onSelect(models[selectedIndex].id);
|
|
10486
10658
|
} else if (key.escape || input === "q") {
|
|
10487
10659
|
onCancel();
|
|
10488
10660
|
}
|
|
10489
10661
|
});
|
|
10490
|
-
return /* @__PURE__ */
|
|
10662
|
+
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
10663
|
const isSelected = index === selectedIndex;
|
|
10492
10664
|
const isCurrent = model.id === currentModel;
|
|
10493
10665
|
const indicator = isSelected ? "\u25B6 " : " ";
|
|
10494
|
-
return /* @__PURE__ */
|
|
10495
|
-
|
|
10666
|
+
return /* @__PURE__ */ React24.createElement(Box21, { gap: 1, key: model.id }, /* @__PURE__ */ React24.createElement(
|
|
10667
|
+
Text19,
|
|
10496
10668
|
{
|
|
10497
10669
|
backgroundColor: isSelected ? theme.text.accent : void 0,
|
|
10498
10670
|
bold: isSelected,
|
|
@@ -10500,15 +10672,15 @@ var init_ModelSelector = __esm({
|
|
|
10500
10672
|
},
|
|
10501
10673
|
indicator,
|
|
10502
10674
|
model.name.padEnd(12)
|
|
10503
|
-
), /* @__PURE__ */
|
|
10504
|
-
|
|
10675
|
+
), /* @__PURE__ */ React24.createElement(
|
|
10676
|
+
Text19,
|
|
10505
10677
|
{
|
|
10506
10678
|
backgroundColor: isSelected ? theme.text.accent : void 0,
|
|
10507
10679
|
color: isSelected ? "black" : theme.text.dim
|
|
10508
10680
|
},
|
|
10509
10681
|
model.description
|
|
10510
|
-
), isCurrent && /* @__PURE__ */
|
|
10511
|
-
})), /* @__PURE__ */
|
|
10682
|
+
), isCurrent && /* @__PURE__ */ React24.createElement(Text19, { color: theme.text.success }, " (current)"));
|
|
10683
|
+
})), /* @__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
10684
|
};
|
|
10513
10685
|
}
|
|
10514
10686
|
});
|
|
@@ -10516,8 +10688,8 @@ var init_ModelSelector = __esm({
|
|
|
10516
10688
|
// src/ui/components/InputPrompt.tsx
|
|
10517
10689
|
import path5 from "path";
|
|
10518
10690
|
import chalk4 from "chalk";
|
|
10519
|
-
import { Box as
|
|
10520
|
-
import
|
|
10691
|
+
import { Box as Box22, Text as Text20 } from "ink";
|
|
10692
|
+
import React25, { forwardRef, useEffect as useEffect10, useImperativeHandle, useState as useState11 } from "react";
|
|
10521
10693
|
var InputPrompt;
|
|
10522
10694
|
var init_InputPrompt = __esm({
|
|
10523
10695
|
"src/ui/components/InputPrompt.tsx"() {
|
|
@@ -10537,7 +10709,8 @@ var init_InputPrompt = __esm({
|
|
|
10537
10709
|
cwd,
|
|
10538
10710
|
currentFolder,
|
|
10539
10711
|
gitBranch,
|
|
10540
|
-
onInputChange
|
|
10712
|
+
onInputChange,
|
|
10713
|
+
isClaudeMax = false
|
|
10541
10714
|
}, ref) => {
|
|
10542
10715
|
const { messages, agentMode, selectedModel, setSelectedModel, isAgentRunning, usageStats } = useSession();
|
|
10543
10716
|
const [value, setValue] = useState11("");
|
|
@@ -10552,6 +10725,7 @@ var init_InputPrompt = __esm({
|
|
|
10552
10725
|
{ name: "/resume", desc: "Resume session" },
|
|
10553
10726
|
{ name: "/clear", desc: "Clear history" },
|
|
10554
10727
|
{ name: "/model", desc: "Change model" },
|
|
10728
|
+
{ name: "/provider", desc: "Select LLM provider" },
|
|
10555
10729
|
{ name: "/fix", desc: "Fix failing tests" },
|
|
10556
10730
|
{ name: "/feedback", desc: "Report an issue" },
|
|
10557
10731
|
{ name: "/setup", desc: "Install Playwright browsers" },
|
|
@@ -10694,7 +10868,7 @@ var init_InputPrompt = __esm({
|
|
|
10694
10868
|
}
|
|
10695
10869
|
}
|
|
10696
10870
|
if (key.shift && key.name === "m" && !isAgentRunning) {
|
|
10697
|
-
setSelectedModel(getNextModel(selectedModel));
|
|
10871
|
+
setSelectedModel(getNextModel(selectedModel, isClaudeMax));
|
|
10698
10872
|
return;
|
|
10699
10873
|
}
|
|
10700
10874
|
if (input === "?" && value.length === 0 && onHelpToggle) {
|
|
@@ -10783,8 +10957,8 @@ var init_InputPrompt = __esm({
|
|
|
10783
10957
|
}
|
|
10784
10958
|
charCount += lineLength + 1;
|
|
10785
10959
|
}
|
|
10786
|
-
return /* @__PURE__ */
|
|
10787
|
-
|
|
10960
|
+
return /* @__PURE__ */ React25.createElement(Box22, { flexDirection: "column", width: "100%" }, showSuggestions && /* @__PURE__ */ React25.createElement(
|
|
10961
|
+
Box22,
|
|
10788
10962
|
{
|
|
10789
10963
|
borderColor: theme.border.default,
|
|
10790
10964
|
borderStyle: "round",
|
|
@@ -10795,12 +10969,12 @@ var init_InputPrompt = __esm({
|
|
|
10795
10969
|
suggestions.map((item, idx) => {
|
|
10796
10970
|
const isSeparator = item.startsWith("\u2500\u2500\u2500\u2500\u2500");
|
|
10797
10971
|
if (isSeparator) {
|
|
10798
|
-
return /* @__PURE__ */
|
|
10972
|
+
return /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.dim, key: item }, " ", item);
|
|
10799
10973
|
}
|
|
10800
|
-
return /* @__PURE__ */
|
|
10974
|
+
return /* @__PURE__ */ React25.createElement(Text20, { color: idx === activeSuggestion ? theme.text.accent : theme.text.dim, key: item }, idx === activeSuggestion ? "\u276F " : " ", item);
|
|
10801
10975
|
})
|
|
10802
|
-
), /* @__PURE__ */
|
|
10803
|
-
|
|
10976
|
+
), /* @__PURE__ */ React25.createElement(
|
|
10977
|
+
Box22,
|
|
10804
10978
|
{
|
|
10805
10979
|
borderColor: disabled ? theme.border.default : theme.border.accent,
|
|
10806
10980
|
borderStyle: "round",
|
|
@@ -10810,24 +10984,96 @@ var init_InputPrompt = __esm({
|
|
|
10810
10984
|
paddingX: 1,
|
|
10811
10985
|
width: "100%"
|
|
10812
10986
|
},
|
|
10813
|
-
/* @__PURE__ */
|
|
10987
|
+
/* @__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
10988
|
if (idx === cursorLine && !disabled) {
|
|
10815
10989
|
const before = line.slice(0, cursorCol);
|
|
10816
10990
|
const charAtCursor = line[cursorCol] || " ";
|
|
10817
10991
|
const after = line.slice(cursorCol + 1);
|
|
10818
|
-
return /* @__PURE__ */
|
|
10992
|
+
return /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.primary, key: idx }, before, chalk4.inverse(charAtCursor), after);
|
|
10819
10993
|
}
|
|
10820
|
-
return /* @__PURE__ */
|
|
10821
|
-
})), !hasContent && disabled && /* @__PURE__ */
|
|
10822
|
-
), /* @__PURE__ */
|
|
10994
|
+
return /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.primary, key: idx }, line);
|
|
10995
|
+
})), !hasContent && disabled && /* @__PURE__ */ React25.createElement(Text20, { color: theme.text.dim, italic: true }, "Waiting for agent to complete...")))
|
|
10996
|
+
), /* @__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
10997
|
});
|
|
10824
10998
|
InputPrompt.displayName = "InputPrompt";
|
|
10825
10999
|
}
|
|
10826
11000
|
});
|
|
10827
11001
|
|
|
11002
|
+
// src/ui/components/ProviderSelector.tsx
|
|
11003
|
+
import { Box as Box23, Text as Text21, useInput as useInput7 } from "ink";
|
|
11004
|
+
import React26, { useState as useState12 } from "react";
|
|
11005
|
+
var PROVIDERS, ProviderSelector;
|
|
11006
|
+
var init_ProviderSelector = __esm({
|
|
11007
|
+
"src/ui/components/ProviderSelector.tsx"() {
|
|
11008
|
+
"use strict";
|
|
11009
|
+
init_theme();
|
|
11010
|
+
PROVIDERS = [
|
|
11011
|
+
{
|
|
11012
|
+
id: "supatest-managed",
|
|
11013
|
+
name: "Supatest Managed",
|
|
11014
|
+
description: "Uses Supatest API with available models"
|
|
11015
|
+
},
|
|
11016
|
+
{
|
|
11017
|
+
id: "claude-max",
|
|
11018
|
+
name: "Claude Max",
|
|
11019
|
+
description: "Direct Claude Max subscription (requires Claude Code login)"
|
|
11020
|
+
}
|
|
11021
|
+
];
|
|
11022
|
+
ProviderSelector = ({
|
|
11023
|
+
currentProvider,
|
|
11024
|
+
onSelect,
|
|
11025
|
+
onCancel,
|
|
11026
|
+
claudeMaxAvailable
|
|
11027
|
+
}) => {
|
|
11028
|
+
const availableProviders = PROVIDERS.filter(
|
|
11029
|
+
(p) => p.id !== "claude-max" || claudeMaxAvailable
|
|
11030
|
+
);
|
|
11031
|
+
const currentIndex = availableProviders.findIndex(
|
|
11032
|
+
(p) => p.id === currentProvider
|
|
11033
|
+
);
|
|
11034
|
+
const [selectedIndex, setSelectedIndex] = useState12(
|
|
11035
|
+
currentIndex >= 0 ? currentIndex : 0
|
|
11036
|
+
);
|
|
11037
|
+
useInput7((input, key) => {
|
|
11038
|
+
if (key.upArrow) {
|
|
11039
|
+
setSelectedIndex(
|
|
11040
|
+
(prev) => prev > 0 ? prev - 1 : availableProviders.length - 1
|
|
11041
|
+
);
|
|
11042
|
+
} else if (key.downArrow) {
|
|
11043
|
+
setSelectedIndex(
|
|
11044
|
+
(prev) => prev < availableProviders.length - 1 ? prev + 1 : 0
|
|
11045
|
+
);
|
|
11046
|
+
} else if (key.return) {
|
|
11047
|
+
onSelect(availableProviders[selectedIndex].id);
|
|
11048
|
+
} else if (key.escape || input === "q") {
|
|
11049
|
+
onCancel();
|
|
11050
|
+
}
|
|
11051
|
+
});
|
|
11052
|
+
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) => {
|
|
11053
|
+
const isSelected = index === selectedIndex;
|
|
11054
|
+
const isCurrent = provider.id === currentProvider;
|
|
11055
|
+
const indicator = isSelected ? "\u25B6 " : " ";
|
|
11056
|
+
return /* @__PURE__ */ React26.createElement(
|
|
11057
|
+
Text21,
|
|
11058
|
+
{
|
|
11059
|
+
backgroundColor: isSelected ? theme.text.accent : void 0,
|
|
11060
|
+
bold: isSelected,
|
|
11061
|
+
color: isSelected ? "black" : theme.text.primary,
|
|
11062
|
+
key: provider.id
|
|
11063
|
+
},
|
|
11064
|
+
indicator,
|
|
11065
|
+
provider.name,
|
|
11066
|
+
isCurrent && /* @__PURE__ */ React26.createElement(Text21, { color: isSelected ? "black" : theme.text.success }, " (current)"),
|
|
11067
|
+
/* @__PURE__ */ React26.createElement(Text21, { color: isSelected ? "black" : theme.text.dim }, " - ", provider.description)
|
|
11068
|
+
);
|
|
11069
|
+
})), /* @__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")));
|
|
11070
|
+
};
|
|
11071
|
+
}
|
|
11072
|
+
});
|
|
11073
|
+
|
|
10828
11074
|
// src/ui/components/SessionSelector.tsx
|
|
10829
|
-
import { Box as
|
|
10830
|
-
import
|
|
11075
|
+
import { Box as Box24, Text as Text22, useInput as useInput8 } from "ink";
|
|
11076
|
+
import React27, { useEffect as useEffect11, useState as useState13 } from "react";
|
|
10831
11077
|
function getSessionPrefix(authMethod) {
|
|
10832
11078
|
return authMethod === "api-key" ? "[Team]" : "[Me]";
|
|
10833
11079
|
}
|
|
@@ -10842,12 +11088,12 @@ var init_SessionSelector = __esm({
|
|
|
10842
11088
|
onSelect,
|
|
10843
11089
|
onCancel
|
|
10844
11090
|
}) => {
|
|
10845
|
-
const [allSessions, setAllSessions] =
|
|
10846
|
-
const [selectedIndex, setSelectedIndex] =
|
|
10847
|
-
const [isLoading, setIsLoading] =
|
|
10848
|
-
const [hasMore, setHasMore] =
|
|
10849
|
-
const [totalSessions, setTotalSessions] =
|
|
10850
|
-
const [error, setError] =
|
|
11091
|
+
const [allSessions, setAllSessions] = useState13([]);
|
|
11092
|
+
const [selectedIndex, setSelectedIndex] = useState13(0);
|
|
11093
|
+
const [isLoading, setIsLoading] = useState13(false);
|
|
11094
|
+
const [hasMore, setHasMore] = useState13(true);
|
|
11095
|
+
const [totalSessions, setTotalSessions] = useState13(0);
|
|
11096
|
+
const [error, setError] = useState13(null);
|
|
10851
11097
|
useEffect11(() => {
|
|
10852
11098
|
loadMoreSessions();
|
|
10853
11099
|
}, []);
|
|
@@ -10874,7 +11120,7 @@ var init_SessionSelector = __esm({
|
|
|
10874
11120
|
setIsLoading(false);
|
|
10875
11121
|
}
|
|
10876
11122
|
};
|
|
10877
|
-
|
|
11123
|
+
useInput8((input, key) => {
|
|
10878
11124
|
if (allSessions.length === 0) {
|
|
10879
11125
|
if (key.escape || input === "q") {
|
|
10880
11126
|
onCancel();
|
|
@@ -10898,13 +11144,13 @@ var init_SessionSelector = __esm({
|
|
|
10898
11144
|
}
|
|
10899
11145
|
});
|
|
10900
11146
|
if (error) {
|
|
10901
|
-
return /* @__PURE__ */
|
|
11147
|
+
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
11148
|
}
|
|
10903
11149
|
if (allSessions.length === 0 && isLoading) {
|
|
10904
|
-
return /* @__PURE__ */
|
|
11150
|
+
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
11151
|
}
|
|
10906
11152
|
if (allSessions.length === 0 && !isLoading) {
|
|
10907
|
-
return /* @__PURE__ */
|
|
11153
|
+
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
11154
|
}
|
|
10909
11155
|
const VISIBLE_ITEMS3 = 10;
|
|
10910
11156
|
let startIndex;
|
|
@@ -10924,7 +11170,7 @@ var init_SessionSelector = __esm({
|
|
|
10924
11170
|
const visibleSessions = allSessions.slice(startIndex, endIndex);
|
|
10925
11171
|
const MAX_TITLE_WIDTH = 50;
|
|
10926
11172
|
const PREFIX_WIDTH = 6;
|
|
10927
|
-
return /* @__PURE__ */
|
|
11173
|
+
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
11174
|
const actualIndex = startIndex + index;
|
|
10929
11175
|
const isSelected = actualIndex === selectedIndex;
|
|
10930
11176
|
const title = item.session.title || "Untitled session";
|
|
@@ -10948,8 +11194,8 @@ var init_SessionSelector = __esm({
|
|
|
10948
11194
|
const prefixColor = item.prefix === "[Me]" ? "cyan" : "yellow";
|
|
10949
11195
|
const indicator = isSelected ? "\u25B6 " : " ";
|
|
10950
11196
|
const bgColor = isSelected ? theme.text.accent : void 0;
|
|
10951
|
-
return /* @__PURE__ */
|
|
10952
|
-
})), /* @__PURE__ */
|
|
11197
|
+
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, ")"));
|
|
11198
|
+
})), /* @__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
11199
|
};
|
|
10954
11200
|
}
|
|
10955
11201
|
});
|
|
@@ -10980,18 +11226,18 @@ var init_useModeToggle = __esm({
|
|
|
10980
11226
|
});
|
|
10981
11227
|
|
|
10982
11228
|
// src/ui/hooks/useOverlayEscapeGuard.ts
|
|
10983
|
-
import { useCallback as useCallback3, useMemo as
|
|
11229
|
+
import { useCallback as useCallback3, useMemo as useMemo5, useRef as useRef4 } from "react";
|
|
10984
11230
|
var useOverlayEscapeGuard;
|
|
10985
11231
|
var init_useOverlayEscapeGuard = __esm({
|
|
10986
11232
|
"src/ui/hooks/useOverlayEscapeGuard.ts"() {
|
|
10987
11233
|
"use strict";
|
|
10988
11234
|
useOverlayEscapeGuard = ({ overlays, suppressionMs = 250 }) => {
|
|
10989
|
-
const suppressUntilRef =
|
|
11235
|
+
const suppressUntilRef = useRef4(0);
|
|
10990
11236
|
const markOverlayClosed = useCallback3(() => {
|
|
10991
11237
|
suppressUntilRef.current = Date.now() + suppressionMs;
|
|
10992
11238
|
}, [suppressionMs]);
|
|
10993
11239
|
const isCancelSuppressed = useCallback3(() => Date.now() < suppressUntilRef.current, []);
|
|
10994
|
-
const isOverlayOpen =
|
|
11240
|
+
const isOverlayOpen = useMemo5(() => overlays.some(Boolean), [overlays]);
|
|
10995
11241
|
return { isOverlayOpen, isCancelSuppressed, markOverlayClosed };
|
|
10996
11242
|
};
|
|
10997
11243
|
}
|
|
@@ -11000,9 +11246,9 @@ var init_useOverlayEscapeGuard = __esm({
|
|
|
11000
11246
|
// src/ui/App.tsx
|
|
11001
11247
|
import { execSync as execSync6 } from "child_process";
|
|
11002
11248
|
import { homedir as homedir6 } from "os";
|
|
11003
|
-
import { Box as
|
|
11249
|
+
import { Box as Box25, Text as Text23, useApp as useApp2, useStdout as useStdout2 } from "ink";
|
|
11004
11250
|
import Spinner3 from "ink-spinner";
|
|
11005
|
-
import
|
|
11251
|
+
import React28, { useEffect as useEffect13, useRef as useRef5, useState as useState14 } from "react";
|
|
11006
11252
|
var getGitBranch2, getCurrentFolder2, AppContent, App;
|
|
11007
11253
|
var init_App = __esm({
|
|
11008
11254
|
"src/ui/App.tsx"() {
|
|
@@ -11011,7 +11257,9 @@ var init_App = __esm({
|
|
|
11011
11257
|
init_login();
|
|
11012
11258
|
init_setup();
|
|
11013
11259
|
init_prompts();
|
|
11260
|
+
init_claude_max();
|
|
11014
11261
|
init_command_discovery();
|
|
11262
|
+
init_settings_loader();
|
|
11015
11263
|
init_stdio();
|
|
11016
11264
|
init_token_storage();
|
|
11017
11265
|
init_version();
|
|
@@ -11023,6 +11271,7 @@ var init_App = __esm({
|
|
|
11023
11271
|
init_InputPrompt();
|
|
11024
11272
|
init_MessageList();
|
|
11025
11273
|
init_ModelSelector();
|
|
11274
|
+
init_ProviderSelector();
|
|
11026
11275
|
init_SessionSelector();
|
|
11027
11276
|
init_SessionContext();
|
|
11028
11277
|
init_useKeypress();
|
|
@@ -11048,28 +11297,29 @@ var init_App = __esm({
|
|
|
11048
11297
|
AppContent = ({ config: config2, sessionId, webUrl, queuedTasks = [], onExit, onSubmitTask, apiClient, onResumeSession, onClearSession }) => {
|
|
11049
11298
|
const { exit } = useApp2();
|
|
11050
11299
|
const { stdout } = useStdout2();
|
|
11051
|
-
const { addMessage, clearMessages, isAgentRunning, messages, setSessionId, setWebUrl, setShouldInterruptAgent, setIsAgentRunning, toggleAllToolOutputs, allToolsExpanded, selectedModel, setSelectedModel, refreshStatic } = useSession();
|
|
11300
|
+
const { addMessage, clearMessages, isAgentRunning, messages, setSessionId, setWebUrl, setShouldInterruptAgent, setIsAgentRunning, toggleAllToolOutputs, allToolsExpanded, selectedModel, setSelectedModel, refreshStatic, toggleToolGroups, llmProvider, setLlmProvider } = useSession();
|
|
11052
11301
|
useModeToggle();
|
|
11053
|
-
const [terminalWidth, setTerminalWidth] =
|
|
11054
|
-
const [showHelp, setShowHelp] =
|
|
11055
|
-
const [showInput, setShowInput] =
|
|
11056
|
-
const [gitBranch] =
|
|
11057
|
-
const [currentFolder] =
|
|
11058
|
-
const [hasInputContent, setHasInputContent] =
|
|
11059
|
-
const [exitWarning, setExitWarning] =
|
|
11060
|
-
const inputPromptRef =
|
|
11061
|
-
const [showSessionSelector, setShowSessionSelector] =
|
|
11062
|
-
const [showModelSelector, setShowModelSelector] =
|
|
11063
|
-
const [
|
|
11064
|
-
const [
|
|
11065
|
-
const [
|
|
11066
|
-
const [
|
|
11067
|
-
const [
|
|
11302
|
+
const [terminalWidth, setTerminalWidth] = useState14(process.stdout.columns || 80);
|
|
11303
|
+
const [showHelp, setShowHelp] = useState14(false);
|
|
11304
|
+
const [showInput, setShowInput] = useState14(true);
|
|
11305
|
+
const [gitBranch] = useState14(() => getGitBranch2());
|
|
11306
|
+
const [currentFolder] = useState14(() => getCurrentFolder2(config2.cwd));
|
|
11307
|
+
const [hasInputContent, setHasInputContent] = useState14(false);
|
|
11308
|
+
const [exitWarning, setExitWarning] = useState14(null);
|
|
11309
|
+
const inputPromptRef = useRef5(null);
|
|
11310
|
+
const [showSessionSelector, setShowSessionSelector] = useState14(false);
|
|
11311
|
+
const [showModelSelector, setShowModelSelector] = useState14(false);
|
|
11312
|
+
const [showProviderSelector, setShowProviderSelector] = useState14(false);
|
|
11313
|
+
const [showFeedbackDialog, setShowFeedbackDialog] = useState14(false);
|
|
11314
|
+
const [isLoadingSession, setIsLoadingSession] = useState14(false);
|
|
11315
|
+
const [showFixFlow, setShowFixFlow] = useState14(false);
|
|
11316
|
+
const [fixRunId, setFixRunId] = useState14(void 0);
|
|
11317
|
+
const [authState, setAuthState] = useState14(
|
|
11068
11318
|
() => config2.supatestApiKey ? "authenticated" /* Authenticated */ : "unauthenticated" /* Unauthenticated */
|
|
11069
11319
|
);
|
|
11070
|
-
const [showAuthDialog, setShowAuthDialog] =
|
|
11320
|
+
const [showAuthDialog, setShowAuthDialog] = useState14(false);
|
|
11071
11321
|
const { isOverlayOpen, isCancelSuppressed, markOverlayClosed } = useOverlayEscapeGuard({
|
|
11072
|
-
overlays: [showHelp, showSessionSelector, showAuthDialog, showModelSelector, showFeedbackDialog, showFixFlow]
|
|
11322
|
+
overlays: [showHelp, showSessionSelector, showAuthDialog, showModelSelector, showProviderSelector, showFeedbackDialog, showFixFlow]
|
|
11073
11323
|
});
|
|
11074
11324
|
useEffect13(() => {
|
|
11075
11325
|
if (!config2.supatestApiKey) {
|
|
@@ -11184,6 +11434,10 @@ var init_App = __esm({
|
|
|
11184
11434
|
setShowModelSelector(true);
|
|
11185
11435
|
return;
|
|
11186
11436
|
}
|
|
11437
|
+
if (command === "/provider") {
|
|
11438
|
+
setShowProviderSelector(true);
|
|
11439
|
+
return;
|
|
11440
|
+
}
|
|
11187
11441
|
if (command === "/fix" || command.startsWith("/fix ")) {
|
|
11188
11442
|
if (authState !== "authenticated" /* Authenticated */) {
|
|
11189
11443
|
addMessage({
|
|
@@ -11319,6 +11573,20 @@ var init_App = __esm({
|
|
|
11319
11573
|
markOverlayClosed();
|
|
11320
11574
|
setShowModelSelector(false);
|
|
11321
11575
|
};
|
|
11576
|
+
const handleProviderSelect = (provider) => {
|
|
11577
|
+
setShowProviderSelector(false);
|
|
11578
|
+
markOverlayClosed();
|
|
11579
|
+
setLlmProvider(provider);
|
|
11580
|
+
saveSupatestSettings(config2.cwd || process.cwd(), { llmProvider: provider });
|
|
11581
|
+
addMessage({
|
|
11582
|
+
type: "assistant",
|
|
11583
|
+
content: `LLM Provider changed to: ${provider === "claude-max" ? "Claude Max" : "Supatest Managed"}. This setting has been saved and will persist across sessions.`
|
|
11584
|
+
});
|
|
11585
|
+
};
|
|
11586
|
+
const handleProviderSelectorCancel = () => {
|
|
11587
|
+
markOverlayClosed();
|
|
11588
|
+
setShowProviderSelector(false);
|
|
11589
|
+
};
|
|
11322
11590
|
const handleFeedbackSubmit = async (category, description) => {
|
|
11323
11591
|
setShowFeedbackDialog(false);
|
|
11324
11592
|
markOverlayClosed();
|
|
@@ -11394,7 +11662,7 @@ var init_App = __esm({
|
|
|
11394
11662
|
markOverlayClosed();
|
|
11395
11663
|
setShowHelp(false);
|
|
11396
11664
|
};
|
|
11397
|
-
const isInitialMount =
|
|
11665
|
+
const isInitialMount = useRef5(true);
|
|
11398
11666
|
useEffect13(() => {
|
|
11399
11667
|
const handleResize = () => {
|
|
11400
11668
|
setTerminalWidth(process.stdout.columns || 80);
|
|
@@ -11463,7 +11731,7 @@ var init_App = __esm({
|
|
|
11463
11731
|
clearTerminalViewportAndScrollback();
|
|
11464
11732
|
}
|
|
11465
11733
|
if (key.ctrl && key.name === "o") {
|
|
11466
|
-
|
|
11734
|
+
toggleToolGroups();
|
|
11467
11735
|
}
|
|
11468
11736
|
},
|
|
11469
11737
|
{ isActive: !isOverlayOpen }
|
|
@@ -11476,21 +11744,21 @@ var init_App = __esm({
|
|
|
11476
11744
|
});
|
|
11477
11745
|
}
|
|
11478
11746
|
}, []);
|
|
11479
|
-
return /* @__PURE__ */
|
|
11480
|
-
|
|
11747
|
+
return /* @__PURE__ */ React28.createElement(
|
|
11748
|
+
Box25,
|
|
11481
11749
|
{
|
|
11482
11750
|
flexDirection: "column",
|
|
11483
11751
|
paddingX: 1
|
|
11484
11752
|
},
|
|
11485
|
-
/* @__PURE__ */
|
|
11486
|
-
showHelp && /* @__PURE__ */
|
|
11753
|
+
/* @__PURE__ */ React28.createElement(MessageList, { currentFolder, gitBranch, queuedTasks, terminalWidth }),
|
|
11754
|
+
showHelp && /* @__PURE__ */ React28.createElement(
|
|
11487
11755
|
HelpMenu,
|
|
11488
11756
|
{
|
|
11489
11757
|
isAuthenticated: authState === "authenticated" /* Authenticated */,
|
|
11490
11758
|
onClose: handleHelpClose
|
|
11491
11759
|
}
|
|
11492
11760
|
),
|
|
11493
|
-
showSessionSelector && apiClient && /* @__PURE__ */
|
|
11761
|
+
showSessionSelector && apiClient && /* @__PURE__ */ React28.createElement(
|
|
11494
11762
|
SessionSelector,
|
|
11495
11763
|
{
|
|
11496
11764
|
apiClient,
|
|
@@ -11498,29 +11766,39 @@ var init_App = __esm({
|
|
|
11498
11766
|
onSelect: handleSessionSelect
|
|
11499
11767
|
}
|
|
11500
11768
|
),
|
|
11501
|
-
isLoadingSession && /* @__PURE__ */
|
|
11502
|
-
showModelSelector && /* @__PURE__ */
|
|
11769
|
+
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")),
|
|
11770
|
+
showModelSelector && /* @__PURE__ */ React28.createElement(
|
|
11503
11771
|
ModelSelector,
|
|
11504
11772
|
{
|
|
11505
11773
|
currentModel: selectedModel,
|
|
11774
|
+
isClaudeMax: !!config2.oauthToken,
|
|
11506
11775
|
onCancel: handleModelSelectorCancel,
|
|
11507
11776
|
onSelect: handleModelSelect
|
|
11508
11777
|
}
|
|
11509
11778
|
),
|
|
11510
|
-
|
|
11779
|
+
showProviderSelector && /* @__PURE__ */ React28.createElement(
|
|
11780
|
+
ProviderSelector,
|
|
11781
|
+
{
|
|
11782
|
+
claudeMaxAvailable: isClaudeMaxAvailable(),
|
|
11783
|
+
currentProvider: llmProvider,
|
|
11784
|
+
onCancel: handleProviderSelectorCancel,
|
|
11785
|
+
onSelect: handleProviderSelect
|
|
11786
|
+
}
|
|
11787
|
+
),
|
|
11788
|
+
showAuthDialog && /* @__PURE__ */ React28.createElement(
|
|
11511
11789
|
AuthDialog,
|
|
11512
11790
|
{
|
|
11513
11791
|
onLogin: handleLogin
|
|
11514
11792
|
}
|
|
11515
11793
|
),
|
|
11516
|
-
showFeedbackDialog && /* @__PURE__ */
|
|
11794
|
+
showFeedbackDialog && /* @__PURE__ */ React28.createElement(
|
|
11517
11795
|
FeedbackDialog,
|
|
11518
11796
|
{
|
|
11519
11797
|
onCancel: handleFeedbackCancel,
|
|
11520
11798
|
onSubmit: handleFeedbackSubmit
|
|
11521
11799
|
}
|
|
11522
11800
|
),
|
|
11523
|
-
showFixFlow && apiClient && /* @__PURE__ */
|
|
11801
|
+
showFixFlow && apiClient && /* @__PURE__ */ React28.createElement(
|
|
11524
11802
|
FixFlow,
|
|
11525
11803
|
{
|
|
11526
11804
|
apiClient,
|
|
@@ -11530,12 +11808,13 @@ var init_App = __esm({
|
|
|
11530
11808
|
onStartFix: handleFixStart
|
|
11531
11809
|
}
|
|
11532
11810
|
),
|
|
11533
|
-
/* @__PURE__ */
|
|
11811
|
+
/* @__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
11812
|
InputPrompt,
|
|
11535
11813
|
{
|
|
11536
11814
|
currentFolder,
|
|
11537
11815
|
cwd: config2.cwd,
|
|
11538
11816
|
gitBranch,
|
|
11817
|
+
isClaudeMax: !!config2.oauthToken,
|
|
11539
11818
|
onHelpToggle: () => setShowHelp((prev) => !prev),
|
|
11540
11819
|
onInputChange: (val) => setHasInputContent(val.trim().length > 0),
|
|
11541
11820
|
onSubmit: handleSubmitTask,
|
|
@@ -11546,7 +11825,7 @@ var init_App = __esm({
|
|
|
11546
11825
|
);
|
|
11547
11826
|
};
|
|
11548
11827
|
App = (props) => {
|
|
11549
|
-
return /* @__PURE__ */
|
|
11828
|
+
return /* @__PURE__ */ React28.createElement(AppContent, { ...props });
|
|
11550
11829
|
};
|
|
11551
11830
|
}
|
|
11552
11831
|
});
|
|
@@ -11582,7 +11861,7 @@ __export(interactive_exports, {
|
|
|
11582
11861
|
runInteractive: () => runInteractive
|
|
11583
11862
|
});
|
|
11584
11863
|
import { render as render2 } from "ink";
|
|
11585
|
-
import
|
|
11864
|
+
import React29, { useEffect as useEffect15, useRef as useRef6 } from "react";
|
|
11586
11865
|
function getToolDescription2(toolName, input) {
|
|
11587
11866
|
switch (toolName) {
|
|
11588
11867
|
case "Read":
|
|
@@ -11715,7 +11994,7 @@ async function runInteractive(config2) {
|
|
|
11715
11994
|
webUrl = session.webUrl;
|
|
11716
11995
|
}
|
|
11717
11996
|
const { unmount, waitUntilExit } = render2(
|
|
11718
|
-
/* @__PURE__ */
|
|
11997
|
+
/* @__PURE__ */ React29.createElement(
|
|
11719
11998
|
InteractiveApp,
|
|
11720
11999
|
{
|
|
11721
12000
|
apiClient,
|
|
@@ -11770,7 +12049,9 @@ var init_interactive = __esm({
|
|
|
11770
12049
|
init_SessionContext();
|
|
11771
12050
|
init_useBracketedPaste();
|
|
11772
12051
|
init_mouse();
|
|
12052
|
+
init_claude_max();
|
|
11773
12053
|
init_logger();
|
|
12054
|
+
init_settings_loader();
|
|
11774
12055
|
init_stdio();
|
|
11775
12056
|
init_version();
|
|
11776
12057
|
AgentRunner = ({ config: config2, sessionId, apiClient, messageBridge, onComplete, onTurnComplete }) => {
|
|
@@ -11787,9 +12068,10 @@ var init_interactive = __esm({
|
|
|
11787
12068
|
agentMode,
|
|
11788
12069
|
setAgentMode,
|
|
11789
12070
|
planFilePath,
|
|
11790
|
-
selectedModel
|
|
12071
|
+
selectedModel,
|
|
12072
|
+
llmProvider
|
|
11791
12073
|
} = useSession();
|
|
11792
|
-
const agentRef =
|
|
12074
|
+
const agentRef = useRef6(null);
|
|
11793
12075
|
useEffect15(() => {
|
|
11794
12076
|
if (shouldInterruptAgent && agentRef.current) {
|
|
11795
12077
|
agentRef.current.abort();
|
|
@@ -11803,7 +12085,7 @@ var init_interactive = __esm({
|
|
|
11803
12085
|
try {
|
|
11804
12086
|
const supatestApiKey = apiClient.getApiKey?.() || config2.supatestApiKey || "";
|
|
11805
12087
|
const proxyUrl = config2.supatestApiUrl || "https://code-api.supatest.ai";
|
|
11806
|
-
const baseUrl = `${proxyUrl}/v1/sessions/${sessionId}
|
|
12088
|
+
const baseUrl = `${proxyUrl}/v1/sessions/${sessionId}`;
|
|
11807
12089
|
process.env.ANTHROPIC_BASE_URL = baseUrl;
|
|
11808
12090
|
process.env.ANTHROPIC_API_KEY = supatestApiKey;
|
|
11809
12091
|
const presenter = new ReactPresenter(
|
|
@@ -11844,12 +12126,22 @@ var init_interactive = __esm({
|
|
|
11844
12126
|
sessionId,
|
|
11845
12127
|
config2.verbose
|
|
11846
12128
|
);
|
|
12129
|
+
let oauthToken;
|
|
12130
|
+
if (llmProvider === "claude-max") {
|
|
12131
|
+
if (isClaudeMaxAvailable()) {
|
|
12132
|
+
oauthToken = "use-claude-max";
|
|
12133
|
+
logger.info("Using Claude Max subscription for LLM calls");
|
|
12134
|
+
} else {
|
|
12135
|
+
logger.warn("Claude Max selected but not available. Falling back to Supatest Managed.");
|
|
12136
|
+
}
|
|
12137
|
+
}
|
|
11847
12138
|
const runConfig = {
|
|
11848
12139
|
...config2,
|
|
11849
12140
|
supatestApiKey,
|
|
11850
12141
|
mode: agentMode,
|
|
11851
12142
|
planFilePath,
|
|
11852
12143
|
selectedModel,
|
|
12144
|
+
oauthToken,
|
|
11853
12145
|
systemPromptAppend: agentMode === "plan" ? config.planSystemPrompt : config2.systemPromptAppend
|
|
11854
12146
|
};
|
|
11855
12147
|
const agent2 = new CoreAgent(presenter, messageBridge);
|
|
@@ -11894,17 +12186,17 @@ var init_interactive = __esm({
|
|
|
11894
12186
|
setIsAgentRunning,
|
|
11895
12187
|
setUsageStats
|
|
11896
12188
|
} = useSession();
|
|
11897
|
-
const [sessionId, setSessionId] =
|
|
11898
|
-
const [currentTask, setCurrentTask] =
|
|
11899
|
-
const [taskId, setTaskId] =
|
|
11900
|
-
const [shouldRunAgent, setShouldRunAgent] =
|
|
11901
|
-
const [taskQueue, setTaskQueue] =
|
|
11902
|
-
const [providerSessionId, setProviderSessionId] =
|
|
11903
|
-
const messageBridgeRef =
|
|
11904
|
-
const lastSubmitRef =
|
|
11905
|
-
const [pendingInjected, setPendingInjected] =
|
|
11906
|
-
const pendingInjectedRef =
|
|
11907
|
-
|
|
12189
|
+
const [sessionId, setSessionId] = React29.useState(initialSessionId);
|
|
12190
|
+
const [currentTask, setCurrentTask] = React29.useState(config2.task);
|
|
12191
|
+
const [taskId, setTaskId] = React29.useState(0);
|
|
12192
|
+
const [shouldRunAgent, setShouldRunAgent] = React29.useState(!!config2.task);
|
|
12193
|
+
const [taskQueue, setTaskQueue] = React29.useState([]);
|
|
12194
|
+
const [providerSessionId, setProviderSessionId] = React29.useState();
|
|
12195
|
+
const messageBridgeRef = React29.useRef(null);
|
|
12196
|
+
const lastSubmitRef = React29.useRef(null);
|
|
12197
|
+
const [pendingInjected, setPendingInjected] = React29.useState([]);
|
|
12198
|
+
const pendingInjectedRef = React29.useRef([]);
|
|
12199
|
+
React29.useEffect(() => {
|
|
11908
12200
|
pendingInjectedRef.current = pendingInjected;
|
|
11909
12201
|
}, [pendingInjected]);
|
|
11910
12202
|
const handleSubmitTask = async (task) => {
|
|
@@ -11967,7 +12259,7 @@ var init_interactive = __esm({
|
|
|
11967
12259
|
if (shouldRunAgent && !messageBridgeRef.current) {
|
|
11968
12260
|
messageBridgeRef.current = new MessageBridge(providerSessionId || "");
|
|
11969
12261
|
}
|
|
11970
|
-
|
|
12262
|
+
React29.useEffect(() => {
|
|
11971
12263
|
if (!shouldRunAgent && taskQueue.length > 0) {
|
|
11972
12264
|
const [nextTask, ...remaining] = taskQueue;
|
|
11973
12265
|
setTaskQueue(remaining);
|
|
@@ -11981,14 +12273,14 @@ var init_interactive = __esm({
|
|
|
11981
12273
|
setShouldRunAgent(true);
|
|
11982
12274
|
}
|
|
11983
12275
|
}, [shouldRunAgent, taskQueue, addMessage, providerSessionId]);
|
|
11984
|
-
const handleClearSession =
|
|
12276
|
+
const handleClearSession = React29.useCallback(() => {
|
|
11985
12277
|
setSessionId(void 0);
|
|
11986
12278
|
setContextSessionId(void 0);
|
|
11987
12279
|
setProviderSessionId(void 0);
|
|
11988
12280
|
setTaskQueue([]);
|
|
11989
12281
|
setPendingInjected([]);
|
|
11990
12282
|
}, [setContextSessionId]);
|
|
11991
|
-
return /* @__PURE__ */
|
|
12283
|
+
return /* @__PURE__ */ React29.createElement(React29.Fragment, null, /* @__PURE__ */ React29.createElement(
|
|
11992
12284
|
App,
|
|
11993
12285
|
{
|
|
11994
12286
|
apiClient,
|
|
@@ -12051,7 +12343,7 @@ var init_interactive = __esm({
|
|
|
12051
12343
|
sessionId,
|
|
12052
12344
|
webUrl
|
|
12053
12345
|
}
|
|
12054
|
-
), shouldRunAgent && currentTask && sessionId && messageBridgeRef.current && /* @__PURE__ */
|
|
12346
|
+
), shouldRunAgent && currentTask && sessionId && messageBridgeRef.current && /* @__PURE__ */ React29.createElement(
|
|
12055
12347
|
AgentRunner,
|
|
12056
12348
|
{
|
|
12057
12349
|
apiClient,
|
|
@@ -12074,7 +12366,9 @@ var init_interactive = __esm({
|
|
|
12074
12366
|
};
|
|
12075
12367
|
InteractiveApp = (props) => {
|
|
12076
12368
|
useBracketedPaste();
|
|
12077
|
-
|
|
12369
|
+
const settings = loadSupatestSettings(props.config.cwd || process.cwd());
|
|
12370
|
+
const initialProvider = settings.llmProvider || "supatest-managed";
|
|
12371
|
+
return /* @__PURE__ */ React29.createElement(KeypressProvider, null, /* @__PURE__ */ React29.createElement(SessionProvider, { initialLlmProvider: initialProvider, initialModel: props.config.selectedModel }, /* @__PURE__ */ React29.createElement(InteractiveAppContent, { ...props })));
|
|
12078
12372
|
};
|
|
12079
12373
|
}
|
|
12080
12374
|
});
|
|
@@ -12090,7 +12384,7 @@ import { Command } from "commander";
|
|
|
12090
12384
|
init_api_client();
|
|
12091
12385
|
import chalk3 from "chalk";
|
|
12092
12386
|
import { render } from "ink";
|
|
12093
|
-
import
|
|
12387
|
+
import React15 from "react";
|
|
12094
12388
|
|
|
12095
12389
|
// src/ui/HeadlessApp.tsx
|
|
12096
12390
|
await init_agent();
|
|
@@ -12099,8 +12393,8 @@ init_MessageList();
|
|
|
12099
12393
|
init_SessionContext();
|
|
12100
12394
|
import { execSync as execSync2 } from "child_process";
|
|
12101
12395
|
import { homedir as homedir3 } from "os";
|
|
12102
|
-
import { Box as
|
|
12103
|
-
import
|
|
12396
|
+
import { Box as Box13, useApp } from "ink";
|
|
12397
|
+
import React14, { useEffect as useEffect2, useRef as useRef2, useState as useState3 } from "react";
|
|
12104
12398
|
var getGitBranch = () => {
|
|
12105
12399
|
try {
|
|
12106
12400
|
return execSync2("git rev-parse --abbrev-ref HEAD", { encoding: "utf8" }).trim();
|
|
@@ -12126,14 +12420,14 @@ var HeadlessAgentRunner = ({ config: config2, sessionId, apiClient, onComplete }
|
|
|
12126
12420
|
setTodos,
|
|
12127
12421
|
setUsageStats
|
|
12128
12422
|
} = useSession();
|
|
12129
|
-
const agentRef =
|
|
12423
|
+
const agentRef = useRef2(null);
|
|
12130
12424
|
useEffect2(() => {
|
|
12131
12425
|
let isMounted = true;
|
|
12132
12426
|
const runAgent2 = async () => {
|
|
12133
12427
|
setIsAgentRunning(true);
|
|
12134
12428
|
try {
|
|
12135
12429
|
const proxyUrl = config2.supatestApiUrl || "https://code-api.supatest.ai";
|
|
12136
|
-
const baseUrl = `${proxyUrl}/v1/sessions/${sessionId}
|
|
12430
|
+
const baseUrl = `${proxyUrl}/v1/sessions/${sessionId}`;
|
|
12137
12431
|
process.env.ANTHROPIC_BASE_URL = baseUrl;
|
|
12138
12432
|
process.env.ANTHROPIC_API_KEY = config2.supatestApiKey;
|
|
12139
12433
|
const presenter = new ReactPresenter(
|
|
@@ -12214,7 +12508,7 @@ var HeadlessAppContent = ({
|
|
|
12214
12508
|
const [gitBranch] = useState3(() => getGitBranch());
|
|
12215
12509
|
const [currentFolder] = useState3(() => getCurrentFolder());
|
|
12216
12510
|
const [terminalWidth, setTerminalWidth] = useState3(process.stdout.columns || 80);
|
|
12217
|
-
const hasCompletedRef =
|
|
12511
|
+
const hasCompletedRef = useRef2(false);
|
|
12218
12512
|
useEffect2(() => {
|
|
12219
12513
|
setSessionId(sessionId);
|
|
12220
12514
|
if (webUrl) {
|
|
@@ -12244,7 +12538,7 @@ var HeadlessAppContent = ({
|
|
|
12244
12538
|
onComplete(success, providerSessionId);
|
|
12245
12539
|
exit();
|
|
12246
12540
|
};
|
|
12247
|
-
return /* @__PURE__ */
|
|
12541
|
+
return /* @__PURE__ */ React14.createElement(Box13, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React14.createElement(
|
|
12248
12542
|
MessageList,
|
|
12249
12543
|
{
|
|
12250
12544
|
currentFolder,
|
|
@@ -12252,7 +12546,7 @@ var HeadlessAppContent = ({
|
|
|
12252
12546
|
headless: true,
|
|
12253
12547
|
terminalWidth
|
|
12254
12548
|
}
|
|
12255
|
-
), /* @__PURE__ */
|
|
12549
|
+
), /* @__PURE__ */ React14.createElement(
|
|
12256
12550
|
HeadlessAgentRunner,
|
|
12257
12551
|
{
|
|
12258
12552
|
apiClient,
|
|
@@ -12263,7 +12557,7 @@ var HeadlessAppContent = ({
|
|
|
12263
12557
|
));
|
|
12264
12558
|
};
|
|
12265
12559
|
var HeadlessApp = (props) => {
|
|
12266
|
-
return /* @__PURE__ */
|
|
12560
|
+
return /* @__PURE__ */ React14.createElement(SessionProvider, { initialModel: props.config.selectedModel }, /* @__PURE__ */ React14.createElement(HeadlessAppContent, { ...props }));
|
|
12267
12561
|
};
|
|
12268
12562
|
|
|
12269
12563
|
// src/modes/headless.ts
|
|
@@ -12332,7 +12626,7 @@ async function runAgent(config2) {
|
|
|
12332
12626
|
}
|
|
12333
12627
|
};
|
|
12334
12628
|
const { unmount, waitUntilExit } = render(
|
|
12335
|
-
|
|
12629
|
+
React15.createElement(HeadlessApp, {
|
|
12336
12630
|
config: config2,
|
|
12337
12631
|
sessionId,
|
|
12338
12632
|
webUrl,
|
|
@@ -12407,77 +12701,12 @@ Updating Supatest CLI ${CLI_VERSION} \u2192 ${latest}...`);
|
|
|
12407
12701
|
|
|
12408
12702
|
// src/index.ts
|
|
12409
12703
|
init_banner();
|
|
12410
|
-
|
|
12411
|
-
// src/utils/claude-max.ts
|
|
12412
|
-
init_logger();
|
|
12413
|
-
import { execSync as execSync4 } from "child_process";
|
|
12414
|
-
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
12415
|
-
import { homedir as homedir4 } from "os";
|
|
12416
|
-
import { join as join7 } from "path";
|
|
12417
|
-
function isClaudeMaxAvailable() {
|
|
12418
|
-
const platform2 = process.platform;
|
|
12419
|
-
logger.debug("[claude-max] Checking Claude Code credentials", { platform: platform2 });
|
|
12420
|
-
if (platform2 === "darwin") {
|
|
12421
|
-
return checkMacOSKeychain();
|
|
12422
|
-
} else {
|
|
12423
|
-
return checkCredentialsFile();
|
|
12424
|
-
}
|
|
12425
|
-
}
|
|
12426
|
-
function checkMacOSKeychain() {
|
|
12427
|
-
try {
|
|
12428
|
-
const credentialsJson = execSync4(
|
|
12429
|
-
'security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null',
|
|
12430
|
-
{ encoding: "utf-8" }
|
|
12431
|
-
).trim();
|
|
12432
|
-
if (!credentialsJson) {
|
|
12433
|
-
logger.debug("[claude-max] No credentials found in macOS keychain");
|
|
12434
|
-
return false;
|
|
12435
|
-
}
|
|
12436
|
-
const credentials = JSON.parse(credentialsJson);
|
|
12437
|
-
const hasOauth = !!credentials.claudeAiOauth?.accessToken;
|
|
12438
|
-
logger.debug("[claude-max] macOS keychain credentials check", {
|
|
12439
|
-
hasOauth,
|
|
12440
|
-
hasRefreshToken: !!credentials.claudeAiOauth?.refreshToken
|
|
12441
|
-
});
|
|
12442
|
-
return hasOauth;
|
|
12443
|
-
} catch (error) {
|
|
12444
|
-
logger.debug("[claude-max] Error checking macOS keychain", {
|
|
12445
|
-
error: error instanceof Error ? error.message : String(error)
|
|
12446
|
-
});
|
|
12447
|
-
return false;
|
|
12448
|
-
}
|
|
12449
|
-
}
|
|
12450
|
-
function checkCredentialsFile() {
|
|
12451
|
-
try {
|
|
12452
|
-
const credentialsPath = join7(homedir4(), ".claude", ".credentials.json");
|
|
12453
|
-
if (!existsSync5(credentialsPath)) {
|
|
12454
|
-
logger.debug("[claude-max] Credentials file not found", { path: credentialsPath });
|
|
12455
|
-
return false;
|
|
12456
|
-
}
|
|
12457
|
-
const credentialsJson = readFileSync4(credentialsPath, "utf-8");
|
|
12458
|
-
const credentials = JSON.parse(credentialsJson);
|
|
12459
|
-
const hasOauth = !!credentials.claudeAiOauth?.accessToken;
|
|
12460
|
-
logger.debug("[claude-max] Credentials file check", {
|
|
12461
|
-
path: credentialsPath,
|
|
12462
|
-
hasOauth,
|
|
12463
|
-
hasRefreshToken: !!credentials.claudeAiOauth?.refreshToken
|
|
12464
|
-
});
|
|
12465
|
-
return hasOauth;
|
|
12466
|
-
} catch (error) {
|
|
12467
|
-
logger.debug("[claude-max] Error checking credentials file", {
|
|
12468
|
-
error: error instanceof Error ? error.message : String(error)
|
|
12469
|
-
});
|
|
12470
|
-
return false;
|
|
12471
|
-
}
|
|
12472
|
-
}
|
|
12473
|
-
|
|
12474
|
-
// src/index.ts
|
|
12475
12704
|
init_error_logger();
|
|
12476
12705
|
init_logger();
|
|
12477
12706
|
|
|
12478
12707
|
// src/utils/node-version.ts
|
|
12479
12708
|
init_logger();
|
|
12480
|
-
import { execSync as
|
|
12709
|
+
import { execSync as execSync4 } from "child_process";
|
|
12481
12710
|
var MINIMUM_NODE_VERSION2 = 18;
|
|
12482
12711
|
function parseVersion2(versionString) {
|
|
12483
12712
|
const cleaned = versionString.trim().replace(/^v/, "");
|
|
@@ -12494,7 +12723,7 @@ function parseVersion2(versionString) {
|
|
|
12494
12723
|
}
|
|
12495
12724
|
function getNodeVersion2() {
|
|
12496
12725
|
try {
|
|
12497
|
-
const versionOutput =
|
|
12726
|
+
const versionOutput = execSync4("node --version", {
|
|
12498
12727
|
encoding: "utf-8",
|
|
12499
12728
|
stdio: ["ignore", "pipe", "ignore"]
|
|
12500
12729
|
});
|
|
@@ -12611,7 +12840,7 @@ program.name("supatest").description(
|
|
|
12611
12840
|
"-m, --claude-max-iterations <number>",
|
|
12612
12841
|
"Maximum number of iterations",
|
|
12613
12842
|
"100"
|
|
12614
|
-
).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>", "
|
|
12843
|
+
).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) => {
|
|
12615
12844
|
try {
|
|
12616
12845
|
checkNodeVersion2();
|
|
12617
12846
|
await checkAndAutoUpdate();
|
|
@@ -12648,17 +12877,6 @@ program.name("supatest").description(
|
|
|
12648
12877
|
let supatestApiKey;
|
|
12649
12878
|
let oauthToken;
|
|
12650
12879
|
const supatestApiUrl = options.supatestApiUrl || config.supatestApiUrl;
|
|
12651
|
-
if (options.claudeMax) {
|
|
12652
|
-
if (!isClaudeMaxAvailable()) {
|
|
12653
|
-
logger.error("Claude Max not available. Please ensure:");
|
|
12654
|
-
logger.error(" 1. Claude Code is installed");
|
|
12655
|
-
logger.error(" 2. You are logged in with a Claude Max subscription");
|
|
12656
|
-
logger.error(" 3. Run 'claude' and authenticate if needed");
|
|
12657
|
-
process.exit(1);
|
|
12658
|
-
}
|
|
12659
|
-
oauthToken = "use-claude-max";
|
|
12660
|
-
logger.info("Using Claude Max subscription for LLM calls");
|
|
12661
|
-
}
|
|
12662
12880
|
if (isHeadlessMode) {
|
|
12663
12881
|
supatestApiKey = normalizeSupatestKey(
|
|
12664
12882
|
options.supatestApiKey || config.supatestApiKey,
|
|
@@ -12684,15 +12902,16 @@ program.name("supatest").description(
|
|
|
12684
12902
|
}
|
|
12685
12903
|
}
|
|
12686
12904
|
let selectedModel;
|
|
12687
|
-
const modelOption = options.model ||
|
|
12688
|
-
if (modelOption) {
|
|
12689
|
-
|
|
12690
|
-
|
|
12691
|
-
|
|
12905
|
+
const modelOption = options.model || "medium";
|
|
12906
|
+
if (modelOption && isValidModelId(modelOption)) {
|
|
12907
|
+
selectedModel = modelOption;
|
|
12908
|
+
} else {
|
|
12909
|
+
if (modelOption && modelOption !== "medium") {
|
|
12692
12910
|
const validModels = AVAILABLE_MODELS.map((m) => m.id).join(", ");
|
|
12693
|
-
logger.warn(`Invalid model "${modelOption}". Valid models: ${validModels}`);
|
|
12911
|
+
logger.warn(`Invalid model "${modelOption}". Valid models: ${validModels} or tier names: small, medium, premium`);
|
|
12694
12912
|
logger.warn("Using default model instead.");
|
|
12695
12913
|
}
|
|
12914
|
+
selectedModel = "medium";
|
|
12696
12915
|
}
|
|
12697
12916
|
if (isHeadlessMode) {
|
|
12698
12917
|
if (!prompt) {
|
|
@@ -12704,7 +12923,7 @@ program.name("supatest").description(
|
|
|
12704
12923
|
logs,
|
|
12705
12924
|
supatestApiKey,
|
|
12706
12925
|
supatestApiUrl,
|
|
12707
|
-
maxIterations: Number.parseInt(options.maxIterations || "
|
|
12926
|
+
maxIterations: Number.parseInt(options.maxIterations || "100", 10),
|
|
12708
12927
|
verbose: options.verbose || false,
|
|
12709
12928
|
cwd: options.cwd,
|
|
12710
12929
|
systemPromptAppend: config.headlessSystemPrompt,
|
|
@@ -12720,7 +12939,7 @@ program.name("supatest").description(
|
|
|
12720
12939
|
logs,
|
|
12721
12940
|
supatestApiKey,
|
|
12722
12941
|
supatestApiUrl,
|
|
12723
|
-
maxIterations: Number.parseInt(options.maxIterations || "
|
|
12942
|
+
maxIterations: Number.parseInt(options.maxIterations || "100", 10),
|
|
12724
12943
|
verbose: options.verbose || false,
|
|
12725
12944
|
cwd: options.cwd,
|
|
12726
12945
|
systemPromptAppend: config.interactiveSystemPrompt,
|