@supatest/cli 0.0.27 → 0.0.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +35 -9
  2. package/dist/index.js +306 -10
  3. package/package.json +7 -7
package/README.md CHANGED
@@ -15,9 +15,11 @@ Supatest AI is an AI-powered CLI that helps you write and maintain E2E tests. Us
15
15
  ## Requirements
16
16
 
17
17
  - **Node.js 18+** - [Download Node.js](https://nodejs.org/)
18
- - **Supatest API Key** - [Get your key](https://supatest.ai)
18
+ - **Supatest Account** - [Sign up at supatest.ai](https://supatest.ai)
19
19
 
20
- ## Installation
20
+ ## Getting Started
21
+
22
+ ### 1. Install
21
23
 
22
24
  ```bash
23
25
  npm install -g @supatest/cli
@@ -29,19 +31,43 @@ Or use npx without installing:
29
31
  npx @supatest/cli
30
32
  ```
31
33
 
32
- ## Usage
33
-
34
- ### Interactive Mode
34
+ ### 2. Login
35
35
 
36
- Run without arguments to start interactive mode:
36
+ Start the CLI and authenticate:
37
37
 
38
38
  ```bash
39
39
  supatest
40
40
  ```
41
41
 
42
- On first run, type `/login` to authenticate with your Supatest account.
42
+ ```
43
+ > /login
44
+ ```
45
+
46
+ This opens your browser to authenticate with your Supatest account.
47
+
48
+ ### 3. Setup
49
+
50
+ Check that your environment has the required tools:
51
+
52
+ ```
53
+ > /setup
54
+ ```
55
+
56
+ This verifies prerequisites like browsers and test frameworks are properly configured.
57
+
58
+ ### 4. Discover
59
+
60
+ Scan your project to detect existing test files and configuration:
61
+
62
+ ```
63
+ > /discover
64
+ ```
65
+
66
+ This helps Supatest understand your test structure so it can work with your existing setup.
67
+
68
+ ### 5. Start Testing
43
69
 
44
- Then describe what you want to test:
70
+ Now describe what you want to test:
45
71
 
46
72
  ```
47
73
  > Write a test for the login flow
@@ -113,7 +139,7 @@ test:
113
139
  ## Support
114
140
 
115
141
  - [Documentation](https://supatest.ai/docs)
116
- - [GitHub Issues](https://github.com/AvereSoftware/supatest/issues)
142
+ - [GitHub Issues](https://github.com/supatest-ai/supatest/issues)
117
143
 
118
144
  ## License
119
145
 
package/dist/index.js CHANGED
@@ -19,15 +19,15 @@ You are Supatest AI, an E2E test builder that iteratively creates, runs, and fix
19
19
  </role>
20
20
 
21
21
  <context>
22
- First, check if SUPATEST.md contains test framework information.
22
+ First, check if .supatest/SUPATEST.md contains test framework information.
23
23
 
24
24
  If yes: Read it and use the documented framework, patterns, and conventions.
25
25
 
26
- If no: Run discovery once, then write findings to SUPATEST.md:
26
+ If no: Run discovery once, then write findings to .supatest/SUPATEST.md:
27
27
  - Detect framework from package.json dependencies
28
28
  - Find test command from package.json scripts
29
29
  - Read 2-3 existing tests to learn patterns (structure, page objects, selectors, test data setup)
30
- - Write a "Test Framework" section to SUPATEST.md with your findings
30
+ - Write a "Test Framework" section to .supatest/SUPATEST.md with your findings
31
31
 
32
32
  This ensures discovery happens once and persists across sessions.
33
33
  </context>
@@ -82,6 +82,66 @@ A test is complete when it passes 2+ times consistently with resilient selectors
82
82
  }
83
83
  });
84
84
 
85
+ // src/prompts/discover.ts
86
+ var discoverPrompt;
87
+ var init_discover = __esm({
88
+ "src/prompts/discover.ts"() {
89
+ "use strict";
90
+ discoverPrompt = `Discover and document the test framework setup for this project.
91
+
92
+ **Your task:**
93
+ 1. Read package.json to identify the test framework (Playwright, WebDriverIO, Cypress, Cucumber, etc.)
94
+ 2. Find the test command in package.json scripts
95
+ 3. Locate existing tests and read 3-5 files to understand:
96
+ - Test file structure and naming conventions
97
+ - Page objects, fixtures, or helper utilities
98
+ - Selector strategies used (data-testid, roles, CSS, XPath)
99
+ - Test data setup patterns (API calls vs UI setup)
100
+ - Assertion patterns and custom matchers
101
+ - Wait strategies and timing patterns
102
+ 4. Identify best practices and patterns used in the codebase:
103
+ - How are tests organized (by feature, by page, by user flow)?
104
+ - Are there shared utilities or custom commands?
105
+ - How is authentication handled in tests?
106
+ - Are there environment-specific configurations?
107
+ 5. Write your findings to .supatest/SUPATEST.md in a "Test Framework" section
108
+
109
+ **Example SUPATEST.md output:**
110
+
111
+ ## Test Framework
112
+
113
+ - **Framework:** WebDriverIO + Cucumber
114
+ - **Config:** wdio.conf.js
115
+ - **Test command:** \`yarn start -- --provider browser --tags @tag_name\`
116
+
117
+ ### Project Structure
118
+ - Features: src/features/
119
+ - Step definitions: src/step_definitions/ui/
120
+ - Page objects: src/pages/common/
121
+ - API utilities: src/utils/endpoints/
122
+
123
+ ### Conventions
124
+ - Selectors: data-testid preferred, fallback to aria selectors
125
+ - Test data: Created via API endpoints before tests run
126
+ - Naming: snake_case for feature files, camelCase for step definitions
127
+ - Tags: @smoke for critical paths, @regression for full suite
128
+
129
+ ### Patterns
130
+ - Page Object Model with lazy element initialization
131
+ - API helpers for test data setup/teardown
132
+ - Custom wait utilities in src/utils/waits.js
133
+ - Shared authentication flow via login.task.js
134
+
135
+ ### Best Practices
136
+ - Each test creates isolated test data
137
+ - Cleanup in afterEach hooks
138
+ - No hard-coded waits, use explicit element conditions
139
+ - Screenshots on failure via reporter config
140
+
141
+ If .supatest/SUPATEST.md already has a "Test Framework" section, report what's there and ask if the user wants to refresh it.`;
142
+ }
143
+ });
144
+
85
145
  // src/prompts/fixer.ts
86
146
  var fixerPrompt;
87
147
  var init_fixer = __esm({
@@ -228,6 +288,7 @@ var init_prompts = __esm({
228
288
  "src/prompts/index.ts"() {
229
289
  "use strict";
230
290
  init_builder();
291
+ init_discover();
231
292
  init_fixer();
232
293
  init_planner();
233
294
  }
@@ -477,7 +538,7 @@ function getToolDisplayName(toolName) {
477
538
  };
478
539
  return displayNameMap[toolName] || toolName;
479
540
  }
480
- var AVAILABLE_MODELS, DEFAULT_MODEL_ID, DATE_SUFFIX_REGEX, CONTEXT_WINDOWS, util, objectUtil, ZodParsedType, getParsedType, ZodIssueCode, ZodError, errorMap, overrideErrorMap, makeIssue, ParseStatus, INVALID, DIRTY, OK, isAborted, isDirty, isValid, isAsync, errorUtil, ParseInputLazyPath, handleResult, ZodType, cuidRegex, cuid2Regex, ulidRegex, uuidRegex, nanoidRegex, jwtRegex, durationRegex, emailRegex, _emojiRegex, emojiRegex, ipv4Regex, ipv4CidrRegex, ipv6Regex, ipv6CidrRegex, base64Regex, base64urlRegex, dateRegexSource, dateRegex, ZodString, ZodNumber, ZodBigInt, ZodBoolean, ZodDate, ZodSymbol, ZodUndefined, ZodNull, ZodAny, ZodUnknown, ZodNever, ZodVoid, ZodArray, ZodObject, ZodUnion, getDiscriminator, ZodDiscriminatedUnion, ZodIntersection, ZodTuple, ZodRecord, ZodMap, ZodSet, ZodFunction, ZodLazy, ZodLiteral, ZodEnum, ZodNativeEnum, ZodPromise, ZodEffects, ZodOptional, ZodNullable, ZodDefault, ZodCatch, ZodNaN, ZodBranded, ZodPipeline, ZodReadonly, ZodFirstPartyTypeKind, stringType, numberType, booleanType, dateType, unknownType, arrayType, objectType, unionType, discriminatedUnionType, recordType, functionType, lazyType, literalType, enumType, promiseType, coerce, MAX_API_KEY_NAME_LENGTH, apiKeySchema, apiKeyUsageSchema, createApiKeyRequestSchema, apiKeyResponseSchema, apiKeyUsageSummarySchema, genericErrorSchema, validationErrorSchema, feedbackCategorySchema, FEEDBACK_CATEGORIES, createFeedbackSchema, feedbackResponseSchema, listFeedbackQuerySchema, feedbackListResponseSchema, healthMetricSchema, healthMetricDailyItemSchema, healthMetricsWithDailySchema, healthAnalyticsPeriodSchema, healthAnalyticsDailyItemSchema, healthAnalyticsResponseSchema, MAX_TIMEZONE_CHAR_LENGTH, organizationSchema, organizationSettingsSchema, textBlockSchema, toolUseBlockSchema, toolResultBlockSchema, thinkingBlockSchema, imageBlockSchema, contentBlockSchema, sessionSchema, createSessionRequestSchema, updateSessionRequestSchema, messageSchema, createMessageRequestSchema, cliEventSchema, createCLISessionRequestSchema, queryResultSchema, queryTurnSchema, queryContentSchema, queryUsageSchema, querySchema, runStatusSchema, testResultStatusSchema, testOutcomeSchema, attachmentKindSchema, stepCategorySchema, runSummarySchema, ciMetadataSchema, gitMetadataSchema, playwrightConfigSchema, errorInfoSchema, locationSchema, sourceSnippetSchema, runSchema, annotationSchema, testSchema, testResultSchema, baseStepSchema, stepSchema, attachmentSchema, listRunsQuerySchema, listTestsQuerySchema, runsListResponseSchema, runDetailResponseSchema, testsListResponseSchema, testDetailResponseSchema, testHistoryItemSchema, testHistoryResponseSchema, topOffenderSchema, topOffendersResponseSchema, trendPointSchema, trendsResponseSchema, errorCategorySchema, failureClusterSchema, newFailureSchema, runInsightsResponseSchema, FailureCategoryEnum, SelectorTypeEnum, FailureCategoryStatsSchema, FailureCategoriesResponseSchema, FailingSelectorStatsSchema, FailingSelectorsResponseSchema, newFailureItemSchema, newFailuresResponseSchema, flakyTestItemSchema, flakyTestsResponseSchema, slowestTestItemSchema, slowestTestsResponseSchema, runSummaryEmailFailureSchema, runSummaryEmailReportSchema, sendRunReportRequestSchema;
541
+ var AVAILABLE_MODELS, DEFAULT_MODEL_ID, DATE_SUFFIX_REGEX, CONTEXT_WINDOWS, util, objectUtil, ZodParsedType, getParsedType, ZodIssueCode, ZodError, errorMap, overrideErrorMap, makeIssue, ParseStatus, INVALID, DIRTY, OK, isAborted, isDirty, isValid, isAsync, errorUtil, ParseInputLazyPath, handleResult, ZodType, cuidRegex, cuid2Regex, ulidRegex, uuidRegex, nanoidRegex, jwtRegex, durationRegex, emailRegex, _emojiRegex, emojiRegex, ipv4Regex, ipv4CidrRegex, ipv6Regex, ipv6CidrRegex, base64Regex, base64urlRegex, dateRegexSource, dateRegex, ZodString, ZodNumber, ZodBigInt, ZodBoolean, ZodDate, ZodSymbol, ZodUndefined, ZodNull, ZodAny, ZodUnknown, ZodNever, ZodVoid, ZodArray, ZodObject, ZodUnion, getDiscriminator, ZodDiscriminatedUnion, ZodIntersection, ZodTuple, ZodRecord, ZodMap, ZodSet, ZodFunction, ZodLazy, ZodLiteral, ZodEnum, ZodNativeEnum, ZodPromise, ZodEffects, ZodOptional, ZodNullable, ZodDefault, ZodCatch, ZodNaN, ZodBranded, ZodPipeline, ZodReadonly, ZodFirstPartyTypeKind, stringType, numberType, booleanType, dateType, unknownType, arrayType, objectType, unionType, discriminatedUnionType, recordType, functionType, lazyType, literalType, enumType, promiseType, coerce, MAX_API_KEY_NAME_LENGTH, apiKeySchema, apiKeyUsageSchema, createApiKeyRequestSchema, apiKeyResponseSchema, apiKeyUsageSummarySchema, genericErrorSchema, validationErrorSchema, feedbackCategorySchema, FEEDBACK_CATEGORIES, createFeedbackSchema, feedbackResponseSchema, listFeedbackQuerySchema, feedbackListResponseSchema, healthMetricSchema, healthMetricDailyItemSchema, healthMetricsWithDailySchema, healthAnalyticsPeriodSchema, healthAnalyticsDailyItemSchema, healthAnalyticsResponseSchema, MAX_TIMEZONE_CHAR_LENGTH, organizationSchema, organizationSettingsSchema, textBlockSchema, toolUseBlockSchema, toolResultBlockSchema, thinkingBlockSchema, imageBlockSchema, contentBlockSchema, sessionSchema, createSessionRequestSchema, updateSessionRequestSchema, messageSchema, createMessageRequestSchema, cliEventSchema, createCLISessionRequestSchema, queryResultSchema, queryTurnSchema, queryContentSchema, queryUsageSchema, querySchema, runStatusSchema, testResultStatusSchema, testOutcomeSchema, attachmentKindSchema, stepCategorySchema, runSummarySchema, ciMetadataSchema, gitMetadataSchema, playwrightConfigSchema, errorInfoSchema, locationSchema, sourceSnippetSchema, runSchema, annotationSchema, testSchema, testResultSchema, baseStepSchema, stepSchema, attachmentSchema, listRunsQuerySchema, listTestsQuerySchema, runsListResponseSchema, runDetailResponseSchema, testsListResponseSchema, testDetailResponseSchema, testHistoryItemSchema, testHistoryResponseSchema, topOffenderSchema, topOffendersResponseSchema, trendPointSchema, trendsResponseSchema, errorCategorySchema, failureClusterSchema, newFailureSchema, runInsightsResponseSchema, FailureCategoryEnum, SelectorTypeEnum, FailureCategoryStatsSchema, FailureCategoriesResponseSchema, FailingSelectorStatsSchema, FailingSelectorsResponseSchema, newFailureItemSchema, newFailuresResponseSchema, flakyTestItemSchema, flakyTestsResponseSchema, slowestTestItemSchema, slowestTestsResponseSchema, runSummaryEmailFailureSchema, runSummaryEmailReportSchema, sendRunReportRequestSchema, metricWithTrendSchema, weekOverWeekMetricsSchema, ciComputeTimeSchema, investigationCandidateSchema, failureCategoryBreakdownSchema, stabilityTrendSchema, folderStabilitySchema, managerReportSchema, managerReportQuerySchema, sendManagerReportRequestSchema, reportAttachmentLinkSchema, developerReportRegressionSchema, developerReportFlakyTestSchema, developerReportSlowTestSchema, developerRunSummaryReportSchema, executiveReportStatusSchema, executiveMetricSchema, executiveKeyMetricsSchema, executiveTrendPointSchema, executiveTrendsSchema, executiveFlakyOffenderSchema, executiveSlowestOffenderSchema, executiveTopOffendersSchema, executiveReportSchema, executiveReportQuerySchema, sendExecutiveReportRequestSchema;
481
542
  var init_shared_es = __esm({
482
543
  "../shared/dist/shared.es.mjs"() {
483
544
  "use strict";
@@ -4957,24 +5018,248 @@ var init_shared_es = __esm({
4957
5018
  startedAt: stringType(),
4958
5019
  endedAt: stringType().optional(),
4959
5020
  durationMs: numberType(),
4960
- // Git info
4961
5021
  branch: stringType().optional(),
4962
5022
  commit: stringType().optional(),
4963
5023
  commitMessage: stringType().optional(),
4964
- // Summary stats
4965
5024
  totalTests: numberType(),
4966
5025
  passedTests: numberType(),
4967
5026
  failedTests: numberType(),
4968
5027
  flakyTests: numberType(),
4969
5028
  skippedTests: numberType(),
4970
5029
  passRate: numberType(),
4971
- // Top failures
4972
5030
  topFailures: arrayType(runSummaryEmailFailureSchema)
4973
5031
  });
4974
5032
  sendRunReportRequestSchema = objectType({
4975
5033
  runId: stringType(),
4976
5034
  emails: arrayType(stringType().email())
4977
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
+ });
4978
5263
  }
4979
5264
  });
4980
5265
 
@@ -5233,7 +5518,7 @@ var CLI_VERSION;
5233
5518
  var init_version = __esm({
5234
5519
  "src/version.ts"() {
5235
5520
  "use strict";
5236
- CLI_VERSION = "0.0.27";
5521
+ CLI_VERSION = "0.0.29";
5237
5522
  }
5238
5523
  });
5239
5524
 
@@ -6199,6 +6484,9 @@ var init_agent = __esm({
6199
6484
  async run(config2) {
6200
6485
  this.abortController = new AbortController();
6201
6486
  this.presenter.onStart(config2);
6487
+ if (config2.providerSessionId) {
6488
+ this.presenter.onLog(`Resuming with providerSessionId: ${config2.providerSessionId}`);
6489
+ }
6202
6490
  const claudeCodePath = await this.resolveClaudeCodePath();
6203
6491
  let prompt = config2.task;
6204
6492
  if (config2.logs) {
@@ -10028,7 +10316,7 @@ var init_HelpMenu = __esm({
10028
10316
  /* @__PURE__ */ React22.createElement(Text17, { bold: true, color: theme.text.accent }, "\u{1F4D6} Supatest AI CLI - Help"),
10029
10317
  /* @__PURE__ */ React22.createElement(Box19, { marginTop: 1 }),
10030
10318
  /* @__PURE__ */ React22.createElement(Text17, { bold: true, color: theme.text.secondary }, "Slash Commands:"),
10031
- /* @__PURE__ */ React22.createElement(Box19, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/help"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " or "), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/?"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Toggle this help menu")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/resume"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Resume a previous session")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/clear"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Clear message history")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/model"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Cycle through available models")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/setup"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Initial setup for Supatest CLI")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/feedback"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Report an issue or request a feature")), isAuthenticated ? /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/logout"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Log out of Supatest")) : /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/login"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Authenticate with Supatest")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/exit"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Exit the CLI"))),
10319
+ /* @__PURE__ */ React22.createElement(Box19, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/help"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " or "), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/?"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Toggle this help menu")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/resume"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Resume a previous session")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/clear"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Clear message history")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/model"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Cycle through available models")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/setup"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Initial setup for Supatest CLI")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/discover"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Discover test framework and write to SUPATEST.md")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/feedback"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Report an issue or request a feature")), isAuthenticated ? /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/logout"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Log out of Supatest")) : /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/login"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Authenticate with Supatest")), /* @__PURE__ */ React22.createElement(Text17, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/exit"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " - Exit the CLI"))),
10032
10320
  customCommands.length > 0 && /* @__PURE__ */ React22.createElement(React22.Fragment, null, /* @__PURE__ */ React22.createElement(Box19, { marginTop: 1 }), /* @__PURE__ */ React22.createElement(Text17, { bold: true, color: theme.text.secondary }, "Project Commands:"), /* @__PURE__ */ React22.createElement(Box19, { flexDirection: "column", marginLeft: 2, marginTop: 0 }, customCommands.slice(0, 5).map((cmd) => /* @__PURE__ */ React22.createElement(Text17, { key: cmd.name }, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.accent }, "/", cmd.name), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, cmd.description ? ` - ${cmd.description}` : ""))), customCommands.length > 5 && /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, "...and ", customCommands.length - 5, " more (use Tab to autocomplete)"))),
10033
10321
  /* @__PURE__ */ React22.createElement(Box19, { marginTop: 1 }),
10034
10322
  /* @__PURE__ */ React22.createElement(Text17, { bold: true, color: theme.text.secondary }, "Keyboard Shortcuts:"),
@@ -10227,6 +10515,7 @@ var init_InputPrompt = __esm({
10227
10515
  { name: "/fix", desc: "Fix failing tests" },
10228
10516
  { name: "/feedback", desc: "Report an issue" },
10229
10517
  { name: "/setup", desc: "Install Playwright browsers" },
10518
+ { name: "/discover", desc: "Discover test framework" },
10230
10519
  { name: "/login", desc: "Authenticate with Supatest" },
10231
10520
  { name: "/logout", desc: "Log out" },
10232
10521
  { name: "/exit", desc: "Exit CLI" }
@@ -10681,6 +10970,7 @@ var init_App = __esm({
10681
10970
  init_shared_es();
10682
10971
  init_login();
10683
10972
  init_setup();
10973
+ init_prompts();
10684
10974
  init_command_discovery();
10685
10975
  init_stdio();
10686
10976
  init_token_storage();
@@ -10918,6 +11208,10 @@ var init_App = __esm({
10918
11208
  }
10919
11209
  return;
10920
11210
  }
11211
+ if (command === "/discover") {
11212
+ onSubmitTask?.(discoverPrompt);
11213
+ return;
11214
+ }
10921
11215
  const projectDir = config2.cwd || process.cwd();
10922
11216
  const spaceIndex = trimmedTask.indexOf(" ");
10923
11217
  const commandName = spaceIndex > 0 ? trimmedTask.slice(1, spaceIndex) : trimmedTask.slice(1);
@@ -11676,7 +11970,9 @@ var init_interactive = __esm({
11676
11970
  const uiMessages = convertQueriesToUIMessages(queries);
11677
11971
  setSessionId(session.id);
11678
11972
  setContextSessionId(session.id);
11679
- setProviderSessionId(session.providerSessionId || void 0);
11973
+ const resumeProviderSessionId = session.providerSessionId || void 0;
11974
+ logger.debug(`Resume session: ${session.id}, providerSessionId: ${resumeProviderSessionId || "NOT SET"}`);
11975
+ setProviderSessionId(resumeProviderSessionId);
11680
11976
  const contextData = await apiClient.getSessionContext(session.id);
11681
11977
  if (contextData.contextTokens > 0) {
11682
11978
  const cacheRead = contextData.cacheReadTokens || 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supatest/cli",
3
- "version": "0.0.27",
3
+ "version": "0.0.29",
4
4
  "description": "Supatest CLI - AI-powered task automation for CI/CD",
5
5
  "type": "module",
6
6
  "bin": {
@@ -38,7 +38,7 @@
38
38
  "access": "public"
39
39
  },
40
40
  "dependencies": {
41
- "@anthropic-ai/claude-agent-sdk": "^0.1.61",
41
+ "@anthropic-ai/claude-agent-sdk": "^0.2.1",
42
42
  "@anthropic-ai/sdk": "^0.71.2",
43
43
  "@types/inquirer": "^9.0.0",
44
44
  "ansi-escapes": "^7.0.0",
@@ -52,6 +52,7 @@
52
52
  "ink-gradient": "^3.0.0",
53
53
  "ink-spinner": "^5.0.0",
54
54
  "inquirer": "^10.0.0",
55
+ "latest-version": "^9.0.0",
55
56
  "lowlight": "^3.3.0",
56
57
  "marked": "^15.0.0",
57
58
  "marked-terminal": "^7.3.0",
@@ -59,18 +60,17 @@
59
60
  "patch-package": "^8.0.1",
60
61
  "postinstall-postinstall": "^2.1.0",
61
62
  "react": "^19.0.0",
63
+ "semver": "^7.6.0",
62
64
  "string-width": "^8.1.0",
63
65
  "strip-ansi": "^7.1.2",
64
66
  "undici": "^7.16.0",
65
- "wrap-ansi": "^9.0.2",
66
- "latest-version": "^9.0.0",
67
- "semver": "^7.6.0"
67
+ "wrap-ansi": "^9.0.2"
68
68
  },
69
69
  "devDependencies": {
70
70
  "@types/node": "^20.12.12",
71
- "dotenv": "^16.6.1",
72
- "@types/semver": "^7.5.8",
73
71
  "@types/react": "^19.0.0",
72
+ "@types/semver": "^7.5.8",
73
+ "dotenv": "^16.6.1",
74
74
  "nodemon": "^3.1.11",
75
75
  "tsup": "^8.5.1",
76
76
  "tsx": "^4.10.0",