@supatest/cli 0.0.49 → 0.0.50
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 +746 -338
- package/package.json +4 -1
package/dist/index.js
CHANGED
|
@@ -24,9 +24,13 @@ You are Supatest AI, an E2E testing assistant. You explore applications, create
|
|
|
24
24
|
If .supatest/SUPATEST.md does NOT exist, you MUST run discovery before doing anything else:
|
|
25
25
|
1. Read package.json to detect the framework (Playwright, WebDriverIO, Cypress, etc.)
|
|
26
26
|
2. Read 2-3 existing test files to learn patterns (naming, selectors, page objects, assertions)
|
|
27
|
-
3. Write findings to .supatest/SUPATEST.md
|
|
27
|
+
3. Write findings to .supatest/SUPATEST.md including:
|
|
28
|
+
- Framework and test command
|
|
29
|
+
- File patterns and naming conventions
|
|
30
|
+
- **Selector strategy** \u2014 Document exactly what selectors the project uses (e.g., \`[data-test]\` attributes, \`getByRole\`, CSS classes). Your new tests MUST use the same selector strategy as existing tests.
|
|
31
|
+
- Page object patterns if present
|
|
28
32
|
|
|
29
|
-
This file persists across sessions \u2014 future runs skip discovery.
|
|
33
|
+
NEVER edit existing test files or write new tests until discovery is complete and SUPATEST.md exists. This file persists across sessions \u2014 future runs skip discovery.
|
|
30
34
|
</context>
|
|
31
35
|
|
|
32
36
|
<bias_to_action>
|
|
@@ -40,9 +44,14 @@ Determine what the user needs:
|
|
|
40
44
|
|
|
41
45
|
**Build** \u2014 The user wants test scripts created:
|
|
42
46
|
1. If you have enough context (source code, page objects, existing tests), write the test directly. If not, open the app with Agent Browser to see the actual page structure first.
|
|
43
|
-
2. Write tests using
|
|
44
|
-
3. Run tests in headless mode.
|
|
45
|
-
4. Fix failures and re-run. Max 5 attempts per test.
|
|
47
|
+
2. Write tests using the project's established selector strategy (from SUPATEST.md). If no strategy exists, prefer semantic locators. When creating multiple tests for the same page or flow, write them all before running.
|
|
48
|
+
3. Run tests in headless mode. **For newly written tests, run a single test file first** for faster feedback \u2014 don't run multiple test files in the first command. If a process hangs, kill it and check for interactive flags.
|
|
49
|
+
4. Fix failures and re-run. Max 5 attempts per test. **If the requested feature does not exist in the app, report this to the user rather than testing a substitute feature.**
|
|
50
|
+
|
|
51
|
+
**Testing with special users** (problem_user, error_user, etc.): These users have known bugs that only manifest with certain products or flows. You MUST:
|
|
52
|
+
1. Test ALL available products/items (not just 2-3) to find user-specific bugs
|
|
53
|
+
2. If a test fails due to unexpected app behavior (e.g., button doesn't respond, wrong data displayed), report it as a potential app bug \u2014 don't just pick a different product that works
|
|
54
|
+
3. Use Agent Browser to visually verify if something seems broken
|
|
46
55
|
|
|
47
56
|
**When to use Agent Browser during build:** If a test fails and the error is about a selector, missing element, or unexpected page state \u2014 open Agent Browser and snapshot the page before your next attempt. A snapshot takes seconds; re-running a full test to validate a guess takes much longer. The rule: if you've failed once on a selector/UI issue and haven't looked at the live page yet, look first.
|
|
48
57
|
</modes>
|
|
@@ -156,21 +165,143 @@ var init_fixer = __esm({
|
|
|
156
165
|
"src/prompts/fixer.ts"() {
|
|
157
166
|
"use strict";
|
|
158
167
|
fixerPrompt = `<role>
|
|
159
|
-
You are a Test Fixer Agent that debugs failing tests and fixes issues. You work with any test framework.
|
|
168
|
+
You are a Test Fixer Agent that debugs failing E2E tests and fixes issues. You work with any test framework.
|
|
169
|
+
You operate in one of two modes depending on the task you receive.
|
|
160
170
|
</role>
|
|
161
171
|
|
|
162
|
-
<
|
|
163
|
-
|
|
172
|
+
<modes>
|
|
173
|
+
**UNDERSTAND & FIX MODE** \u2014 When the task starts with "Analyze and fix":
|
|
174
|
+
Follow the 3-phase workflow: Understand \u2192 Plan \u2192 Fix. Never skip phases.
|
|
175
|
+
|
|
176
|
+
**QUICK FIX MODE** \u2014 When the task starts with "Fix the following":
|
|
177
|
+
Skip analysis. Go directly to investigating and fixing the provided tests.
|
|
178
|
+
</modes>
|
|
179
|
+
|
|
180
|
+
<understand_and_fix_workflow>
|
|
181
|
+
|
|
182
|
+
## Phase 1: Understand
|
|
183
|
+
|
|
184
|
+
Read each failing test's **source code** to understand what it verifies. Then analyze the provided error data.
|
|
185
|
+
|
|
186
|
+
Present your findings as a markdown table:
|
|
187
|
+
|
|
188
|
+
| # | Spec File | Test | What It Verifies | Failure Reason | Root Cause | Details |
|
|
189
|
+
|---|-----------|------|------------------|----------------|------------|---------|
|
|
190
|
+
|
|
191
|
+
Column guidelines:
|
|
192
|
+
- **Spec File**: Short filename with line (e.g., \`login.spec:25\`)
|
|
193
|
+
- **Test**: Short test name
|
|
194
|
+
- **What It Verifies**: Plain English \u2014 the user journey this test protects. Read the test code to determine this. Example: "User can log in with valid credentials" \u2014 not "clicks login button and asserts URL"
|
|
195
|
+
- **Failure Reason**: Plain English \u2014 what went wrong. Example: "Login button not found on page" \u2014 not the raw Playwright error message
|
|
196
|
+
- **Root Cause**: One of: \`Selector\`, \`Timing\`, \`State\`, \`Data\`, \`Logic\`, \`App Bug\`
|
|
197
|
+
- **Details**: Link to run details if URL was provided: \`[View](url)\`
|
|
198
|
+
|
|
199
|
+
After the table, add a **Patterns** section:
|
|
200
|
+
- Group tests that share the same root cause or would be fixed by the same change
|
|
201
|
+
- Note which fixes will cascade (e.g., "Fixing the login selector in tests #1 and #2 requires one change")
|
|
202
|
+
- Flag flaky tests (pass rate below 80% in history)
|
|
203
|
+
- Note tests that are already claimed/assigned to someone else
|
|
204
|
+
|
|
205
|
+
Example output:
|
|
206
|
+
\`\`\`
|
|
207
|
+
## Failure Analysis \u2014 6 failures across 3 spec files
|
|
208
|
+
|
|
209
|
+
| # | Spec File | Test | What It Verifies | Failure Reason | Root Cause | Details |
|
|
210
|
+
|---|-----------|------|------------------|----------------|------------|---------|
|
|
211
|
+
| 1 | login.spec:25 | User login | User can log in with valid credentials | Login button not found \u2014 selector changed | Selector | [View](url) |
|
|
212
|
+
| 2 | login.spec:48 | Wrong password | Error shown for invalid password | Same login button not found | Selector | [View](url) |
|
|
213
|
+
| 3 | cart.spec:102 | Add to cart | User can add item and see updated total | Expected $20.00 but got $25.00 | Data | [View](url) |
|
|
214
|
+
|
|
215
|
+
**Patterns:**
|
|
216
|
+
- **Login selector (2 tests)**: Tests #1 and #2 share the same broken selector \u2014 one fix resolves both
|
|
217
|
+
- **Cart pricing (1 test)**: Test #3 has a data mismatch \u2014 product price may have changed
|
|
218
|
+
- Test #2 is flaky (65% pass rate) \u2014 may also have an intermittent timing issue
|
|
219
|
+
\`\`\`
|
|
220
|
+
|
|
221
|
+
## Phase 2: Plan
|
|
222
|
+
|
|
223
|
+
After presenting your analysis, use the \`AskUserQuestion\` tool to let the user choose how to proceed.
|
|
224
|
+
|
|
225
|
+
Build the options dynamically based on your analysis:
|
|
226
|
+
|
|
227
|
+
\`\`\`json
|
|
228
|
+
{
|
|
229
|
+
"questions": [{
|
|
230
|
+
"question": "I found N failures grouped into M patterns. How would you like to proceed?",
|
|
231
|
+
"header": "Fix plan",
|
|
232
|
+
"options": [
|
|
233
|
+
{
|
|
234
|
+
"label": "Fix Group A: [short description] (N tests)",
|
|
235
|
+
"description": "[Which tests and what the fix involves]"
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
"label": "Fix all N failures",
|
|
239
|
+
"description": "Fix everything sequentially, starting with highest-confidence fixes"
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
"label": "Let me choose",
|
|
243
|
+
"description": "I will tell you which tests to focus on"
|
|
244
|
+
}
|
|
245
|
+
],
|
|
246
|
+
"multiSelect": false
|
|
247
|
+
}]
|
|
248
|
+
}
|
|
249
|
+
\`\`\`
|
|
250
|
+
|
|
251
|
+
Guidelines for building options:
|
|
252
|
+
- Create one option per failure group/pattern you identified
|
|
253
|
+
- Order by confidence \u2014 highest-confidence fix first
|
|
254
|
+
- Always include "Fix all" and "Let me choose" as the last two options
|
|
255
|
+
- Keep labels short (under 60 characters)
|
|
256
|
+
- Descriptions should explain what the fix involves
|
|
257
|
+
|
|
258
|
+
Wait for the user's response before proceeding.
|
|
259
|
+
|
|
260
|
+
## Phase 3: Fix
|
|
261
|
+
|
|
262
|
+
Fix only the tests the user selected. For each test:
|
|
263
|
+
1. Read the test file and relevant source/application code
|
|
264
|
+
2. Identify the minimal change needed
|
|
265
|
+
3. Make the fix
|
|
266
|
+
4. Run the test to verify: \`npx playwright test <file>:<line>\`
|
|
267
|
+
|
|
268
|
+
After completing each group of related fixes, output a progress table:
|
|
269
|
+
|
|
270
|
+
| # | Test | Status | Fix Applied |
|
|
271
|
+
|---|------|--------|-------------|
|
|
272
|
+
| 1 | login.spec:25 | \u2705 Fixed | Updated selector to \`data-testid="login-btn"\` |
|
|
273
|
+
| 2 | login.spec:48 | \u2705 Fixed | Same selector change |
|
|
274
|
+
| 3 | cart.spec:102 | \u{1F504} Fixing | Investigating price data source... |
|
|
275
|
+
|
|
276
|
+
If you completed a group and more groups remain, use \`AskUserQuestion\` to check in:
|
|
277
|
+
\`\`\`json
|
|
278
|
+
{
|
|
279
|
+
"questions": [{
|
|
280
|
+
"question": "Group A is fixed (2/2 passing). Continue with the next group?",
|
|
281
|
+
"header": "Continue",
|
|
282
|
+
"options": [
|
|
283
|
+
{ "label": "Yes, continue", "description": "Proceed to fix the next group" },
|
|
284
|
+
{ "label": "Stop here", "description": "I'll handle the rest later" }
|
|
285
|
+
],
|
|
286
|
+
"multiSelect": false
|
|
287
|
+
}]
|
|
288
|
+
}
|
|
289
|
+
\`\`\`
|
|
290
|
+
|
|
291
|
+
</understand_and_fix_workflow>
|
|
292
|
+
|
|
293
|
+
<quick_fix_workflow>
|
|
294
|
+
When in Quick Fix mode, follow this workflow:
|
|
164
295
|
|
|
165
296
|
1. **Analyze** \u2014 Read the error message and stack trace from the provided output
|
|
166
297
|
2. **Categorize** \u2014 Identify root cause: selector, timing, state, data, or logic
|
|
167
298
|
3. **Investigate** \u2014 Read the failing test and relevant source code
|
|
168
299
|
4. **Fix** \u2014 Make minimal, targeted changes. Don't weaken assertions or skip tests.
|
|
169
300
|
5. **Verify** \u2014 Run the single failing test in headless mode to confirm the fix. If a process hangs, kill it and check for interactive flags.
|
|
170
|
-
6. **Iterate** \u2014 If still failing after a fix attempt, and the error involves selectors, missing elements, or unexpected page state: open Agent Browser and snapshot the page before your next attempt.
|
|
301
|
+
6. **Iterate** \u2014 If still failing after a fix attempt, and the error involves selectors, missing elements, or unexpected page state: open Agent Browser and snapshot the page before your next attempt. Max 3 attempts per test.
|
|
171
302
|
|
|
172
303
|
Continue until all tests pass. After all individual fixes, run the full suite once to check for regressions.
|
|
173
|
-
</
|
|
304
|
+
</quick_fix_workflow>
|
|
174
305
|
|
|
175
306
|
<root_causes>
|
|
176
307
|
**Selector** \u2014 Element changed or locator fragile \u2192 update to roles/labels/test IDs (survive refactors unlike CSS classes)
|
|
@@ -178,6 +309,7 @@ Continue until all tests pass. After all individual fixes, run the full suite on
|
|
|
178
309
|
**State** \u2014 Test pollution or setup issue \u2192 ensure cleanup, add preconditions, refresh data
|
|
179
310
|
**Data** \u2014 Hardcoded or missing data \u2192 use dynamic data, create via API
|
|
180
311
|
**Logic** \u2014 Assertion wrong or outdated \u2192 update expectation to match actual behavior
|
|
312
|
+
**App Bug** \u2014 The application itself has a bug, not the test \u2192 report the bug, don't mask it in the test
|
|
181
313
|
</root_causes>
|
|
182
314
|
|
|
183
315
|
<agent_browser>
|
|
@@ -193,12 +325,15 @@ Re-snapshot after each interaction. Walk through the test flow manually to compa
|
|
|
193
325
|
</agent_browser>
|
|
194
326
|
|
|
195
327
|
<test_tagging>
|
|
196
|
-
|
|
328
|
+
**IMPORTANT**: Before completing any fix, check if the test you touched has metadata tags. If tags are missing, add them.
|
|
329
|
+
|
|
330
|
+
Required tags: @feature:name, @priority:critical|high|medium|low, @test_type:smoke|e2e|regression|integration|unit
|
|
331
|
+
Optional: @owner:email, @ticket:PROJ-123, @slow, @flaky, @key:value
|
|
197
332
|
|
|
198
|
-
**
|
|
333
|
+
**Playwright**: test("...", { tags: ['@feature:auth', '@priority:high', '@test_type:e2e'] }, async ({ page }) => { });
|
|
334
|
+
**WebdriverIO/Other**: it("... (@feature:auth @priority:high @test_type:e2e)", async () => { });
|
|
199
335
|
|
|
200
|
-
|
|
201
|
-
**WebdriverIO/Other**: it("... (@feature:auth @priority:high)", async () => { });
|
|
336
|
+
A fix is not complete until the test has proper tags.
|
|
202
337
|
</test_tagging>
|
|
203
338
|
|
|
204
339
|
<decisions>
|
|
@@ -206,6 +341,15 @@ If tests are missing metadata tags, add them \u2014 this is a Supatest platform
|
|
|
206
341
|
**Escalate:** 3 attempts with no progress, actual app bug found, requirements unclear \u2014 report what you tried and why it didn't work
|
|
207
342
|
</decisions>
|
|
208
343
|
|
|
344
|
+
<headless_mode>
|
|
345
|
+
When the task includes "[HEADLESS]":
|
|
346
|
+
- Do NOT use the AskUserQuestion tool
|
|
347
|
+
- Proceed automatically through all phases without pausing for input
|
|
348
|
+
- Still output the analysis table and progress updates for CI log visibility
|
|
349
|
+
- Fix all failures sequentially, starting with highest-confidence fixes
|
|
350
|
+
- Generate the final report at the end
|
|
351
|
+
</headless_mode>
|
|
352
|
+
|
|
209
353
|
<report>
|
|
210
354
|
Generate this once after all tests are addressed, not after each individual test.
|
|
211
355
|
|
|
@@ -854,7 +998,8 @@ var init_config = __esm({
|
|
|
854
998
|
anthropicModelName: getEnvVar("ANTHROPIC_MODEL_NAME", "claude-opus-4-5"),
|
|
855
999
|
headlessSystemPrompt: fixerPrompt,
|
|
856
1000
|
interactiveSystemPrompt: builderPrompt,
|
|
857
|
-
planSystemPrompt: plannerPrompt
|
|
1001
|
+
planSystemPrompt: plannerPrompt,
|
|
1002
|
+
fixSystemPrompt: fixerPrompt
|
|
858
1003
|
};
|
|
859
1004
|
}
|
|
860
1005
|
});
|
|
@@ -868,7 +1013,7 @@ function getModelById(id) {
|
|
|
868
1013
|
return AVAILABLE_MODELS.find((m) => m.id === baseId);
|
|
869
1014
|
}
|
|
870
1015
|
function getTierFromProviderModel(providerModel) {
|
|
871
|
-
if (providerModel === "glm-
|
|
1016
|
+
if (providerModel === "glm-5" || providerModel.startsWith("glm-5")) {
|
|
872
1017
|
return "premium";
|
|
873
1018
|
}
|
|
874
1019
|
return null;
|
|
@@ -1176,7 +1321,7 @@ function getToolGroupCounts(tools) {
|
|
|
1176
1321
|
);
|
|
1177
1322
|
return Object.entries(groups).map(([name, count]) => ({ name, count })).sort((a, b) => b.count - a.count);
|
|
1178
1323
|
}
|
|
1179
|
-
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, coverageTestItemSchema, secondaryTagMetricSchema, tagCoverageSchema, coverageStatsSchema, coverageAlertSchema, coverageDashboardResponseSchema, coverageDashboardQuerySchema, 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,
|
|
1324
|
+
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, coverageTestItemSchema, secondaryTagMetricSchema, tagCoverageSchema, coverageStatsSchema, coverageAlertSchema, coverageDashboardResponseSchema, coverageDashboardQuerySchema, 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, assigneeInfoSchema, assigneesListResponseSchema, testCatalogPrioritySchema, testCatalogTypeSchema, testCatalogAssignmentStatusSchema, testCatalogAssignmentSchema, testCatalogDefinitionSchema, testCatalogListItemSchema, testCatalogHistoryEntrySchema, testCatalogListQuerySchema, testCatalogListResponseSchema, testCatalogDetailResponseSchema, testCatalogFilterOptionsSchema, testCatalogAssigneeInfoSchema, testCatalogAssignRequestSchema, testCatalogUpdateAssignmentRequestSchema, testCatalogAssignmentResponseSchema, testCatalogFolderNodeSchema, testCatalogFolderTreeResponseSchema, testCatalogFolderTestsQuerySchema, SECONDS_PER_MINUTE, SECONDS_PER_HOUR, SECONDS_PER_DAY, SECONDS_PER_WEEK, SECONDS_PER_MONTH, SECONDS_PER_YEAR;
|
|
1180
1325
|
var init_shared_es = __esm({
|
|
1181
1326
|
"../shared/dist/shared.es.mjs"() {
|
|
1182
1327
|
"use strict";
|
|
@@ -5993,84 +6138,6 @@ var init_shared_es = __esm({
|
|
|
5993
6138
|
month: stringType().regex(/^\d{4}-\d{2}$/, "month must be in format YYYY-MM"),
|
|
5994
6139
|
emails: arrayType(stringType().email())
|
|
5995
6140
|
});
|
|
5996
|
-
fixAssignmentStatusSchema = enumType([
|
|
5997
|
-
"assigned",
|
|
5998
|
-
// Currently being worked on
|
|
5999
|
-
"completed",
|
|
6000
|
-
// Successfully fixed
|
|
6001
|
-
"failed"
|
|
6002
|
-
// Fix attempt failed
|
|
6003
|
-
]);
|
|
6004
|
-
fixAssignmentSchema = objectType({
|
|
6005
|
-
id: stringType(),
|
|
6006
|
-
testRunId: stringType(),
|
|
6007
|
-
// Reference to test_run table
|
|
6008
|
-
assignedTo: stringType().optional(),
|
|
6009
|
-
// User ID (Clerk user ID)
|
|
6010
|
-
assignedAt: stringType().optional(),
|
|
6011
|
-
// ISO timestamp
|
|
6012
|
-
status: fixAssignmentStatusSchema.optional(),
|
|
6013
|
-
completedAt: stringType().optional(),
|
|
6014
|
-
reason: stringType().optional(),
|
|
6015
|
-
createdAt: stringType(),
|
|
6016
|
-
updatedAt: stringType()
|
|
6017
|
-
});
|
|
6018
|
-
testWithAssignmentSchema = objectType({
|
|
6019
|
-
// Core test fields from Test schema
|
|
6020
|
-
id: stringType(),
|
|
6021
|
-
readableId: stringType().optional(),
|
|
6022
|
-
runId: stringType(),
|
|
6023
|
-
file: stringType(),
|
|
6024
|
-
title: stringType(),
|
|
6025
|
-
location: objectType({
|
|
6026
|
-
file: stringType(),
|
|
6027
|
-
line: numberType(),
|
|
6028
|
-
column: numberType().optional()
|
|
6029
|
-
}).optional(),
|
|
6030
|
-
status: enumType(["passed", "failed", "timedOut", "skipped", "interrupted"]),
|
|
6031
|
-
durationMs: numberType(),
|
|
6032
|
-
retryCount: numberType(),
|
|
6033
|
-
isFlaky: booleanType().optional(),
|
|
6034
|
-
// Assignment fields
|
|
6035
|
-
assignmentId: stringType().optional(),
|
|
6036
|
-
assignedTo: stringType().optional(),
|
|
6037
|
-
assignedAt: stringType().optional(),
|
|
6038
|
-
assignmentStatus: fixAssignmentStatusSchema.optional(),
|
|
6039
|
-
assignmentCompletedAt: stringType().optional(),
|
|
6040
|
-
assignmentReason: stringType().optional()
|
|
6041
|
-
});
|
|
6042
|
-
listAssignmentsQuerySchema = objectType({
|
|
6043
|
-
runId: stringType().optional(),
|
|
6044
|
-
assignedTo: stringType().optional(),
|
|
6045
|
-
status: fixAssignmentStatusSchema.optional(),
|
|
6046
|
-
file: stringType().optional(),
|
|
6047
|
-
search: stringType().optional(),
|
|
6048
|
-
page: coerce.number().min(1).default(1),
|
|
6049
|
-
limit: coerce.number().min(1).max(100).default(50)
|
|
6050
|
-
});
|
|
6051
|
-
listTestsWithAssignmentsQuerySchema = objectType({
|
|
6052
|
-
assignedTo: stringType().optional(),
|
|
6053
|
-
status: fixAssignmentStatusSchema.optional(),
|
|
6054
|
-
testStatus: enumType(["passed", "failed", "timedOut", "skipped", "interrupted"]).optional(),
|
|
6055
|
-
file: stringType().optional(),
|
|
6056
|
-
search: stringType().optional(),
|
|
6057
|
-
page: coerce.number().min(1).default(1),
|
|
6058
|
-
limit: coerce.number().min(1).max(100).default(50)
|
|
6059
|
-
});
|
|
6060
|
-
assignmentsListResponseSchema = objectType({
|
|
6061
|
-
assignments: arrayType(testWithAssignmentSchema),
|
|
6062
|
-
total: numberType(),
|
|
6063
|
-
page: numberType(),
|
|
6064
|
-
limit: numberType(),
|
|
6065
|
-
// Summary stats
|
|
6066
|
-
stats: objectType({
|
|
6067
|
-
total: numberType(),
|
|
6068
|
-
assigned: numberType(),
|
|
6069
|
-
completed: numberType(),
|
|
6070
|
-
failed: numberType(),
|
|
6071
|
-
unassigned: numberType()
|
|
6072
|
-
})
|
|
6073
|
-
});
|
|
6074
6141
|
assigneeInfoSchema = objectType({
|
|
6075
6142
|
id: stringType(),
|
|
6076
6143
|
name: stringType(),
|
|
@@ -6081,84 +6148,6 @@ var init_shared_es = __esm({
|
|
|
6081
6148
|
assigneesListResponseSchema = objectType({
|
|
6082
6149
|
assignees: arrayType(assigneeInfoSchema)
|
|
6083
6150
|
});
|
|
6084
|
-
assignTestsRequestSchema = objectType({
|
|
6085
|
-
testRunIds: arrayType(stringType()).min(1),
|
|
6086
|
-
assignedTo: stringType().min(1),
|
|
6087
|
-
notes: stringType().optional()
|
|
6088
|
-
});
|
|
6089
|
-
assignTestRequestSchema = objectType({
|
|
6090
|
-
testRunId: stringType(),
|
|
6091
|
-
assignedTo: stringType(),
|
|
6092
|
-
reason: stringType().optional()
|
|
6093
|
-
});
|
|
6094
|
-
reassignTestRequestSchema = objectType({
|
|
6095
|
-
assignedTo: stringType().min(1),
|
|
6096
|
-
notes: stringType().optional()
|
|
6097
|
-
});
|
|
6098
|
-
completeAssignmentsRequestSchema = objectType({
|
|
6099
|
-
assignmentIds: arrayType(stringType()).min(1),
|
|
6100
|
-
status: fixAssignmentStatusSchema,
|
|
6101
|
-
notes: stringType().optional(),
|
|
6102
|
-
fixSessionId: stringType().optional()
|
|
6103
|
-
});
|
|
6104
|
-
completeAssignmentRequestSchema = objectType({
|
|
6105
|
-
assignmentId: stringType(),
|
|
6106
|
-
success: booleanType()
|
|
6107
|
-
});
|
|
6108
|
-
bulkReassignRequestSchema = objectType({
|
|
6109
|
-
fromUser: stringType().min(1),
|
|
6110
|
-
toUser: stringType().min(1),
|
|
6111
|
-
status: fixAssignmentStatusSchema.optional(),
|
|
6112
|
-
notes: stringType().optional()
|
|
6113
|
-
});
|
|
6114
|
-
assignmentResponseSchema = objectType({
|
|
6115
|
-
assignment: fixAssignmentSchema
|
|
6116
|
-
});
|
|
6117
|
-
assignTestsResponseSchema = objectType({
|
|
6118
|
-
assigned: arrayType(
|
|
6119
|
-
objectType({
|
|
6120
|
-
id: stringType(),
|
|
6121
|
-
testRunId: stringType()
|
|
6122
|
-
})
|
|
6123
|
-
),
|
|
6124
|
-
conflicts: arrayType(
|
|
6125
|
-
objectType({
|
|
6126
|
-
testRunId: stringType(),
|
|
6127
|
-
file: stringType(),
|
|
6128
|
-
title: stringType(),
|
|
6129
|
-
currentAssignee: stringType()
|
|
6130
|
-
})
|
|
6131
|
-
)
|
|
6132
|
-
});
|
|
6133
|
-
testsWithAssignmentsResponseSchema = objectType({
|
|
6134
|
-
tests: arrayType(testWithAssignmentSchema),
|
|
6135
|
-
total: numberType(),
|
|
6136
|
-
page: numberType(),
|
|
6137
|
-
limit: numberType(),
|
|
6138
|
-
stats: objectType({
|
|
6139
|
-
total: numberType(),
|
|
6140
|
-
assigned: numberType(),
|
|
6141
|
-
completed: numberType(),
|
|
6142
|
-
failed: numberType(),
|
|
6143
|
-
unassigned: numberType()
|
|
6144
|
-
})
|
|
6145
|
-
});
|
|
6146
|
-
bulkReassignResponseSchema = objectType({
|
|
6147
|
-
reassigned: numberType(),
|
|
6148
|
-
conflicts: arrayType(
|
|
6149
|
-
objectType({
|
|
6150
|
-
testRunId: stringType(),
|
|
6151
|
-
file: stringType(),
|
|
6152
|
-
title: stringType(),
|
|
6153
|
-
reason: stringType()
|
|
6154
|
-
})
|
|
6155
|
-
)
|
|
6156
|
-
});
|
|
6157
|
-
userAssignmentStatsSchema = objectType({
|
|
6158
|
-
assigned: numberType(),
|
|
6159
|
-
completed: numberType(),
|
|
6160
|
-
failed: numberType()
|
|
6161
|
-
});
|
|
6162
6151
|
testCatalogPrioritySchema = enumType([
|
|
6163
6152
|
"critical",
|
|
6164
6153
|
"high",
|
|
@@ -6564,7 +6553,7 @@ var CLI_VERSION;
|
|
|
6564
6553
|
var init_version = __esm({
|
|
6565
6554
|
"src/version.ts"() {
|
|
6566
6555
|
"use strict";
|
|
6567
|
-
CLI_VERSION = "0.0.
|
|
6556
|
+
CLI_VERSION = "0.0.50";
|
|
6568
6557
|
}
|
|
6569
6558
|
});
|
|
6570
6559
|
|
|
@@ -7907,8 +7896,20 @@ ${projectInstructions}`,
|
|
|
7907
7896
|
stderr: (msg) => {
|
|
7908
7897
|
this.presenter.onLog(`[Agent Failure] ${msg}`);
|
|
7909
7898
|
},
|
|
7910
|
-
// Intercept AskUserQuestion
|
|
7899
|
+
// Intercept AskUserQuestion and ExitPlanMode tools to wait for user response
|
|
7911
7900
|
canUseTool: async (toolName, input) => {
|
|
7901
|
+
if (toolName === "ExitPlanMode") {
|
|
7902
|
+
const questionId2 = `plan-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
7903
|
+
this.presenter.onToolUse?.(toolName, input, questionId2);
|
|
7904
|
+
const answers2 = await this.createQuestionPromise(questionId2);
|
|
7905
|
+
if (answers2 && answers2.approved === "true") {
|
|
7906
|
+
this.presenter.onToolResult?.(questionId2, JSON.stringify({ approved: true }));
|
|
7907
|
+
return { behavior: "allow", updatedInput: input };
|
|
7908
|
+
}
|
|
7909
|
+
const feedbackMsg = answers2?.feedback || "User did not approve the plan.";
|
|
7910
|
+
this.presenter.onToolResult?.(questionId2, JSON.stringify({ approved: false, feedback: feedbackMsg }));
|
|
7911
|
+
return { behavior: "deny", message: feedbackMsg };
|
|
7912
|
+
}
|
|
7912
7913
|
if (toolName !== "AskUserQuestion") {
|
|
7913
7914
|
return { behavior: "allow", updatedInput: input };
|
|
7914
7915
|
}
|
|
@@ -8313,17 +8314,21 @@ var init_react = __esm({
|
|
|
8313
8314
|
});
|
|
8314
8315
|
}
|
|
8315
8316
|
onToolUse(tool, input, toolId) {
|
|
8316
|
-
const
|
|
8317
|
-
|
|
8318
|
-
|
|
8319
|
-
|
|
8320
|
-
|
|
8321
|
-
|
|
8322
|
-
|
|
8323
|
-
|
|
8324
|
-
|
|
8325
|
-
|
|
8326
|
-
|
|
8317
|
+
const interactiveTools = ["ExitPlanMode", "AskUserQuestion", "EnterPlanMode"];
|
|
8318
|
+
const isInteractiveTool = interactiveTools.includes(tool);
|
|
8319
|
+
if (!isInteractiveTool) {
|
|
8320
|
+
const message = {
|
|
8321
|
+
type: "tool",
|
|
8322
|
+
content: getToolDescription(tool, input),
|
|
8323
|
+
toolName: getToolDisplayName(tool),
|
|
8324
|
+
toolInput: input,
|
|
8325
|
+
toolResult: void 0,
|
|
8326
|
+
isExpanded: false,
|
|
8327
|
+
isPending: true,
|
|
8328
|
+
toolUseId: toolId
|
|
8329
|
+
};
|
|
8330
|
+
this.callbacks.addMessage(message);
|
|
8331
|
+
}
|
|
8327
8332
|
this.hasAssistantMessage = false;
|
|
8328
8333
|
this.hasThinkingMessage = false;
|
|
8329
8334
|
this.currentAssistantText = "";
|
|
@@ -8353,7 +8358,7 @@ var init_react = __esm({
|
|
|
8353
8358
|
});
|
|
8354
8359
|
}
|
|
8355
8360
|
} else if (tool === "ExitPlanMode") {
|
|
8356
|
-
this.callbacks.
|
|
8361
|
+
this.callbacks.onPlanApproval?.(toolId, input?.allowedPrompts);
|
|
8357
8362
|
} else if (tool === "EnterPlanMode") {
|
|
8358
8363
|
this.callbacks.onEnterPlanMode?.();
|
|
8359
8364
|
} else if (tool === "AskUserQuestion") {
|
|
@@ -8523,6 +8528,7 @@ var init_SessionContext = __esm({
|
|
|
8523
8528
|
const [toolGroupsExpanded, setToolGroupsExpanded] = useState(false);
|
|
8524
8529
|
const [staticRemountKey, setStaticRemountKey] = useState(0);
|
|
8525
8530
|
const [pendingQuestion, setPendingQuestion] = useState(null);
|
|
8531
|
+
const [pendingPlanApproval, setPendingPlanApproval] = useState(null);
|
|
8526
8532
|
const addMessage = useCallback(
|
|
8527
8533
|
(message) => {
|
|
8528
8534
|
const expandableTools = ["Bash", "BashOutput", "Command Output"];
|
|
@@ -8655,7 +8661,9 @@ var init_SessionContext = __esm({
|
|
|
8655
8661
|
staticRemountKey,
|
|
8656
8662
|
refreshStatic,
|
|
8657
8663
|
pendingQuestion,
|
|
8658
|
-
setPendingQuestion
|
|
8664
|
+
setPendingQuestion,
|
|
8665
|
+
pendingPlanApproval,
|
|
8666
|
+
setPendingPlanApproval
|
|
8659
8667
|
}), [
|
|
8660
8668
|
messages,
|
|
8661
8669
|
addMessage,
|
|
@@ -8681,7 +8689,8 @@ var init_SessionContext = __esm({
|
|
|
8681
8689
|
llmProvider,
|
|
8682
8690
|
staticRemountKey,
|
|
8683
8691
|
refreshStatic,
|
|
8684
|
-
pendingQuestion
|
|
8692
|
+
pendingQuestion,
|
|
8693
|
+
pendingPlanApproval
|
|
8685
8694
|
]);
|
|
8686
8695
|
const usageStatsValue = useMemo(() => ({
|
|
8687
8696
|
usageStats,
|
|
@@ -9622,7 +9631,7 @@ var init_MessageList = __esm({
|
|
|
9622
9631
|
});
|
|
9623
9632
|
StaticHeader.displayName = "StaticHeader";
|
|
9624
9633
|
MessageList = memo2(({ terminalWidth, currentFolder, gitBranch, headless = false, queuedTasks = [] }) => {
|
|
9625
|
-
const { messages, updateMessageById, isAgentRunning, staticRemountKey, toolGroupsExpanded, toggleToolGroups } = useSession();
|
|
9634
|
+
const { messages, updateMessageById, isAgentRunning, staticRemountKey, toolGroupsExpanded, toggleToolGroups, pendingQuestion, pendingPlanApproval } = useSession();
|
|
9626
9635
|
const handleToggle = useCallback2((id, currentExpanded) => {
|
|
9627
9636
|
updateMessageById(id, { isExpanded: !currentExpanded });
|
|
9628
9637
|
}, [updateMessageById]);
|
|
@@ -9765,15 +9774,6 @@ var init_MessageList = __esm({
|
|
|
9765
9774
|
}
|
|
9766
9775
|
flushToolGroup();
|
|
9767
9776
|
};
|
|
9768
|
-
const completeMessages = [];
|
|
9769
|
-
const inProgressMessages = [];
|
|
9770
|
-
for (const msg of messages) {
|
|
9771
|
-
if (isMessageComplete(msg)) {
|
|
9772
|
-
completeMessages.push(msg);
|
|
9773
|
-
} else {
|
|
9774
|
-
inProgressMessages.push(msg);
|
|
9775
|
-
}
|
|
9776
|
-
}
|
|
9777
9777
|
let splitIndex = messages.length;
|
|
9778
9778
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
9779
9779
|
if (isMessageComplete(messages[i])) {
|
|
@@ -9781,10 +9781,10 @@ var init_MessageList = __esm({
|
|
|
9781
9781
|
break;
|
|
9782
9782
|
}
|
|
9783
9783
|
}
|
|
9784
|
-
const
|
|
9785
|
-
const
|
|
9786
|
-
processTurn(
|
|
9787
|
-
processTurn(
|
|
9784
|
+
const completeMessages = messages.slice(0, splitIndex);
|
|
9785
|
+
const inProgressMessages = messages.slice(splitIndex);
|
|
9786
|
+
processTurn(completeMessages, completed);
|
|
9787
|
+
processTurn(inProgressMessages, currentTurn);
|
|
9788
9788
|
return { completedGroups: completed, currentTurnGroups: currentTurn };
|
|
9789
9789
|
}, [messages]);
|
|
9790
9790
|
const staticItems = useMemo3(
|
|
@@ -9804,7 +9804,7 @@ var init_MessageList = __esm({
|
|
|
9804
9804
|
headless,
|
|
9805
9805
|
staticRemountKey
|
|
9806
9806
|
}
|
|
9807
|
-
), staticItems.length > 0 && /* @__PURE__ */ React13.createElement(Static, { items: staticItems, key: `messages-${staticRemountKey}
|
|
9807
|
+
), staticItems.length > 0 && /* @__PURE__ */ React13.createElement(Static, { items: staticItems, key: `messages-${staticRemountKey}` }, (item) => {
|
|
9808
9808
|
if (item._isGroup) {
|
|
9809
9809
|
const content2 = renderGroupedMessage(item);
|
|
9810
9810
|
if (!content2) {
|
|
@@ -9823,7 +9823,7 @@ var init_MessageList = __esm({
|
|
|
9823
9823
|
return null;
|
|
9824
9824
|
}
|
|
9825
9825
|
return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", key: group.type === "group" ? `current-group-${idx}` : group.messages[0].id, width: "100%" }, content);
|
|
9826
|
-
}), /* @__PURE__ */ React13.createElement(QueuedMessageDisplay, { messageQueue: queuedTasks }), isAgentRunning && !hasPendingAssistant && /* @__PURE__ */ React13.createElement(LoadingMessage, { headless, key: "loading" }), /* @__PURE__ */ React13.createElement(Box12, { height: 1 }));
|
|
9826
|
+
}), /* @__PURE__ */ React13.createElement(QueuedMessageDisplay, { messageQueue: queuedTasks }), isAgentRunning && !hasPendingAssistant && !pendingQuestion && !pendingPlanApproval && /* @__PURE__ */ React13.createElement(LoadingMessage, { headless, key: "loading" }), /* @__PURE__ */ React13.createElement(Box12, { height: 1 }));
|
|
9827
9827
|
});
|
|
9828
9828
|
MessageList.displayName = "MessageList";
|
|
9829
9829
|
}
|
|
@@ -12394,6 +12394,9 @@ var init_FeedbackDialog = __esm({
|
|
|
12394
12394
|
});
|
|
12395
12395
|
|
|
12396
12396
|
// src/fix/context-builder.ts
|
|
12397
|
+
function getFrontendUrl() {
|
|
12398
|
+
return process.env.SUPATEST_FRONTEND_URL || "https://app.supatest.dev";
|
|
12399
|
+
}
|
|
12397
12400
|
function buildTestContext(ctx) {
|
|
12398
12401
|
const { test, history } = ctx;
|
|
12399
12402
|
const lines = [];
|
|
@@ -12447,6 +12450,20 @@ function buildTestContext(ctx) {
|
|
|
12447
12450
|
lines.push("```");
|
|
12448
12451
|
lines.push("");
|
|
12449
12452
|
}
|
|
12453
|
+
const attachments = lastResult.attachments;
|
|
12454
|
+
if (attachments && attachments.length > 0) {
|
|
12455
|
+
const screenshots = attachments.filter((a) => a.kind === "screenshot");
|
|
12456
|
+
const traces = attachments.filter((a) => a.kind === "trace");
|
|
12457
|
+
const videos = attachments.filter((a) => a.kind === "video");
|
|
12458
|
+
const parts = [];
|
|
12459
|
+
if (screenshots.length > 0) parts.push(`${screenshots.length} screenshot(s)`);
|
|
12460
|
+
if (traces.length > 0) parts.push(`${traces.length} trace(s)`);
|
|
12461
|
+
if (videos.length > 0) parts.push(`${videos.length} video(s)`);
|
|
12462
|
+
if (parts.length > 0) {
|
|
12463
|
+
lines.push(`## Attachments: ${parts.join(", ")}`);
|
|
12464
|
+
lines.push("");
|
|
12465
|
+
}
|
|
12466
|
+
}
|
|
12450
12467
|
}
|
|
12451
12468
|
if (history && history.history.length > 0) {
|
|
12452
12469
|
const passCount = history.history.filter((h) => h.status === "passed").length;
|
|
@@ -12470,7 +12487,7 @@ ${buildTestContext(ctx)}`;
|
|
|
12470
12487
|
});
|
|
12471
12488
|
return sections.join("\n\n");
|
|
12472
12489
|
}
|
|
12473
|
-
function
|
|
12490
|
+
function buildQuickFixPrompt(tests) {
|
|
12474
12491
|
const intro = tests.length === 1 ? "Fix the following failing test:" : `Fix the following ${tests.length} failing tests:`;
|
|
12475
12492
|
const context = buildMultipleTestsContext(tests);
|
|
12476
12493
|
const instructions = `
|
|
@@ -12489,6 +12506,130 @@ Focus on fixing the actual issue, not just suppressing the error. If the test is
|
|
|
12489
12506
|
${context}
|
|
12490
12507
|
${instructions}`;
|
|
12491
12508
|
}
|
|
12509
|
+
function formatTestForAnalysis(ctx, index, runId, frontendUrl, assignment) {
|
|
12510
|
+
const { test, history } = ctx;
|
|
12511
|
+
const lines = [];
|
|
12512
|
+
const testRunDetailUrl = `${frontendUrl}/runs/${runId}/tests/${test.id}`;
|
|
12513
|
+
lines.push(`### Test ${index + 1}: ${test.title}`);
|
|
12514
|
+
lines.push(`- **File**: ${test.file}${test.location ? `:${test.location.line}` : ""}`);
|
|
12515
|
+
lines.push(`- **Status**: ${test.status} | **Duration**: ${test.durationMs}ms | **Retries**: ${test.retryCount}`);
|
|
12516
|
+
if (test.projectName) {
|
|
12517
|
+
lines.push(`- **Browser/Project**: ${test.projectName}`);
|
|
12518
|
+
}
|
|
12519
|
+
if (test.tags && test.tags.length > 0) {
|
|
12520
|
+
lines.push(`- **Tags**: ${test.tags.join(", ")}`);
|
|
12521
|
+
}
|
|
12522
|
+
lines.push(`- **Details**: [View run details](${testRunDetailUrl})`);
|
|
12523
|
+
if (assignment) {
|
|
12524
|
+
lines.push(`- **Assignment**: Claimed by ${assignment.assignedTo} (${assignment.status})`);
|
|
12525
|
+
}
|
|
12526
|
+
lines.push("");
|
|
12527
|
+
const lastResult = test.results[test.results.length - 1];
|
|
12528
|
+
if (lastResult) {
|
|
12529
|
+
if (lastResult.errors && lastResult.errors.length > 0) {
|
|
12530
|
+
lines.push("**Error:**");
|
|
12531
|
+
for (const error of lastResult.errors) {
|
|
12532
|
+
lines.push(error.message);
|
|
12533
|
+
if (error.stack) {
|
|
12534
|
+
lines.push("```");
|
|
12535
|
+
lines.push(error.stack);
|
|
12536
|
+
lines.push("```");
|
|
12537
|
+
}
|
|
12538
|
+
}
|
|
12539
|
+
lines.push("");
|
|
12540
|
+
}
|
|
12541
|
+
if (lastResult.steps && lastResult.steps.length > 0) {
|
|
12542
|
+
lines.push("**Execution Steps:**");
|
|
12543
|
+
for (const step of lastResult.steps) {
|
|
12544
|
+
const failed = step.error ? " \u2190 FAILED" : "";
|
|
12545
|
+
const duration = `[${step.durationMs}ms]`;
|
|
12546
|
+
lines.push(`- ${duration} ${step.category}: ${step.title}${failed}`);
|
|
12547
|
+
if (step.error) {
|
|
12548
|
+
lines.push(` Error: ${step.error.message}`);
|
|
12549
|
+
}
|
|
12550
|
+
}
|
|
12551
|
+
lines.push("");
|
|
12552
|
+
}
|
|
12553
|
+
if (lastResult.stdout && lastResult.stdout.length > 0) {
|
|
12554
|
+
const stdout = lastResult.stdout.join("\n");
|
|
12555
|
+
const truncated = stdout.length > 500 ? stdout.slice(0, 500) + "\n... (truncated)" : stdout;
|
|
12556
|
+
lines.push("**Console Output (stdout):**");
|
|
12557
|
+
lines.push("```");
|
|
12558
|
+
lines.push(truncated);
|
|
12559
|
+
lines.push("```");
|
|
12560
|
+
lines.push("");
|
|
12561
|
+
}
|
|
12562
|
+
if (lastResult.stderr && lastResult.stderr.length > 0) {
|
|
12563
|
+
const stderr = lastResult.stderr.join("\n");
|
|
12564
|
+
const truncated = stderr.length > 500 ? stderr.slice(0, 500) + "\n... (truncated)" : stderr;
|
|
12565
|
+
lines.push("**Console Output (stderr):**");
|
|
12566
|
+
lines.push("```");
|
|
12567
|
+
lines.push(truncated);
|
|
12568
|
+
lines.push("```");
|
|
12569
|
+
lines.push("");
|
|
12570
|
+
}
|
|
12571
|
+
const attachments = lastResult.attachments;
|
|
12572
|
+
if (attachments && attachments.length > 0) {
|
|
12573
|
+
const screenshots = attachments.filter((a) => a.kind === "screenshot");
|
|
12574
|
+
const traces = attachments.filter((a) => a.kind === "trace");
|
|
12575
|
+
const videos = attachments.filter((a) => a.kind === "video");
|
|
12576
|
+
const parts = [];
|
|
12577
|
+
if (screenshots.length > 0) parts.push(`${screenshots.length} screenshot(s)`);
|
|
12578
|
+
if (traces.length > 0) parts.push(`${traces.length} trace(s)`);
|
|
12579
|
+
if (videos.length > 0) parts.push(`${videos.length} video(s)`);
|
|
12580
|
+
if (parts.length > 0) {
|
|
12581
|
+
lines.push(`**Attachments**: ${parts.join(", ")} \u2014 [view in dashboard](${testRunDetailUrl})`);
|
|
12582
|
+
lines.push("");
|
|
12583
|
+
}
|
|
12584
|
+
}
|
|
12585
|
+
}
|
|
12586
|
+
if (history && history.history.length > 0) {
|
|
12587
|
+
const passCount = history.history.filter((h) => h.status === "passed").length;
|
|
12588
|
+
const totalCount = history.history.length;
|
|
12589
|
+
const passRate = Math.round(passCount / totalCount * 100);
|
|
12590
|
+
const sparkline = history.history.map((h) => h.status === "passed" ? "\u2713" : "\u2717").join("");
|
|
12591
|
+
lines.push(`**History**: ${sparkline} (${passRate}% pass rate over last ${totalCount} runs)`);
|
|
12592
|
+
if (passRate > 0 && passRate < 80) {
|
|
12593
|
+
lines.push(`\u26A0 This test appears flaky \u2014 passes ${passRate}% of the time`);
|
|
12594
|
+
}
|
|
12595
|
+
lines.push("");
|
|
12596
|
+
}
|
|
12597
|
+
return lines.join("\n");
|
|
12598
|
+
}
|
|
12599
|
+
function buildUnderstandAndFixPrompt(tests, run, assignments, options) {
|
|
12600
|
+
const frontendUrl = getFrontendUrl();
|
|
12601
|
+
const runDetailUrl = `${frontendUrl}/runs/${run.id}`;
|
|
12602
|
+
const lines = [];
|
|
12603
|
+
lines.push("Analyze and fix the following test failures.\n");
|
|
12604
|
+
lines.push("## Run Information");
|
|
12605
|
+
lines.push(`- **Run**: ${run.readableId || run.id}`);
|
|
12606
|
+
lines.push(`- **Branch**: ${run.git?.branch || "unknown"}${run.git?.commit ? ` @ ${run.git.commit.slice(0, 7)}` : ""}`);
|
|
12607
|
+
if (run.git?.commitMessage) {
|
|
12608
|
+
lines.push(`- **Commit**: ${run.git.commitMessage}`);
|
|
12609
|
+
}
|
|
12610
|
+
if (run.ci?.provider) {
|
|
12611
|
+
lines.push(`- **CI**: ${run.ci.provider}${run.ci.workflow ? ` / ${run.ci.workflow}` : ""}`);
|
|
12612
|
+
}
|
|
12613
|
+
lines.push(`- **Results**: ${run.summary.failed} failed / ${run.summary.total} total (${run.summary.passed} passed, ${run.summary.flaky} flaky, ${run.summary.skipped} skipped)`);
|
|
12614
|
+
lines.push(`- **Duration**: ${Math.round(run.durationMs / 1e3)}s`);
|
|
12615
|
+
lines.push(`- **Dashboard**: [View full run](${runDetailUrl})`);
|
|
12616
|
+
lines.push("");
|
|
12617
|
+
if (options?.headless) {
|
|
12618
|
+
lines.push("[HEADLESS]\n");
|
|
12619
|
+
}
|
|
12620
|
+
const specFiles = new Set(tests.map((t) => t.test.file));
|
|
12621
|
+
lines.push(`## Failing Tests \u2014 ${tests.length} failure${tests.length !== 1 ? "s" : ""} across ${specFiles.size} spec file${specFiles.size !== 1 ? "s" : ""}`);
|
|
12622
|
+
lines.push("");
|
|
12623
|
+
for (let i = 0; i < tests.length; i++) {
|
|
12624
|
+
const testId = tests[i].test.id;
|
|
12625
|
+
const assignment = assignments?.get(testId) || null;
|
|
12626
|
+
lines.push(formatTestForAnalysis(tests[i], i, run.id, frontendUrl, assignment));
|
|
12627
|
+
if (i < tests.length - 1) {
|
|
12628
|
+
lines.push("---\n");
|
|
12629
|
+
}
|
|
12630
|
+
}
|
|
12631
|
+
return lines.join("\n");
|
|
12632
|
+
}
|
|
12492
12633
|
var init_context_builder = __esm({
|
|
12493
12634
|
"src/fix/context-builder.ts"() {
|
|
12494
12635
|
"use strict";
|
|
@@ -12924,7 +13065,7 @@ var init_FixFlow = __esm({
|
|
|
12924
13065
|
onStartFix,
|
|
12925
13066
|
initialRunId
|
|
12926
13067
|
}) => {
|
|
12927
|
-
const [step, setStep] = useState8(
|
|
13068
|
+
const [step, setStep] = useState8("select-run");
|
|
12928
13069
|
const [selectedRun, setSelectedRun] = useState8(null);
|
|
12929
13070
|
const [selectedTests, setSelectedTests] = useState8([]);
|
|
12930
13071
|
const [assignments, setAssignments] = useState8(/* @__PURE__ */ new Map());
|
|
@@ -12932,22 +13073,38 @@ var init_FixFlow = __esm({
|
|
|
12932
13073
|
const [assignmentIds, setAssignmentIds] = useState8(/* @__PURE__ */ new Map());
|
|
12933
13074
|
const [loadingProgress, setLoadingProgress] = useState8({ current: 0, total: 0 });
|
|
12934
13075
|
const [loadError, setLoadError] = useState8(null);
|
|
13076
|
+
const [workflowMode, setWorkflowMode] = useState8(null);
|
|
13077
|
+
const [modeSelectionIndex, setModeSelectionIndex] = useState8(0);
|
|
12935
13078
|
useInput4((input, key) => {
|
|
12936
13079
|
if (key.escape || input === "q") {
|
|
12937
|
-
if (step === "
|
|
13080
|
+
if (step === "choosing-mode" && selectedRun) {
|
|
12938
13081
|
setSelectedRun(null);
|
|
12939
13082
|
setStep("select-run");
|
|
13083
|
+
} else if (step === "select-tests" && selectedRun) {
|
|
13084
|
+
setStep("choosing-mode");
|
|
12940
13085
|
} else if (step === "select-run") {
|
|
12941
13086
|
onCancel();
|
|
12942
|
-
} else if ((step === "loading-details" || step === "claiming-tests" || step === "error") && loadError) {
|
|
13087
|
+
} else if ((step === "loading-details" || step === "loading-analysis" || step === "claiming-tests" || step === "error") && loadError) {
|
|
12943
13088
|
setLoadError(null);
|
|
12944
|
-
|
|
13089
|
+
if (workflowMode === "quick-fix") {
|
|
13090
|
+
setStep("select-tests");
|
|
13091
|
+
} else {
|
|
13092
|
+
setStep("choosing-mode");
|
|
13093
|
+
}
|
|
13094
|
+
}
|
|
13095
|
+
}
|
|
13096
|
+
if (step === "choosing-mode") {
|
|
13097
|
+
if (key.upArrow || key.downArrow) {
|
|
13098
|
+
setModeSelectionIndex((prev) => prev === 0 ? 1 : 0);
|
|
13099
|
+
} else if (key.return) {
|
|
13100
|
+
const mode = modeSelectionIndex === 0 ? "understand-and-fix" : "quick-fix";
|
|
13101
|
+
handleModeSelect(mode);
|
|
12945
13102
|
}
|
|
12946
13103
|
}
|
|
12947
13104
|
});
|
|
12948
13105
|
const handleRunSelect = (run) => {
|
|
12949
13106
|
setSelectedRun(run);
|
|
12950
|
-
setStep("
|
|
13107
|
+
setStep("choosing-mode");
|
|
12951
13108
|
fetchAssignments(run.id);
|
|
12952
13109
|
};
|
|
12953
13110
|
const fetchAssignments = async (runId) => {
|
|
@@ -12990,12 +13147,88 @@ var init_FixFlow = __esm({
|
|
|
12990
13147
|
console.error("Failed to load assignments:", err);
|
|
12991
13148
|
}
|
|
12992
13149
|
};
|
|
13150
|
+
const handleModeSelect = (mode) => {
|
|
13151
|
+
setWorkflowMode(mode);
|
|
13152
|
+
if (mode === "understand-and-fix") {
|
|
13153
|
+
setStep("loading-analysis");
|
|
13154
|
+
loadAndStartAnalysis();
|
|
13155
|
+
} else {
|
|
13156
|
+
setStep("select-tests");
|
|
13157
|
+
}
|
|
13158
|
+
};
|
|
13159
|
+
const loadAndStartAnalysis = async () => {
|
|
13160
|
+
if (!selectedRun) return;
|
|
13161
|
+
setLoadError(null);
|
|
13162
|
+
try {
|
|
13163
|
+
const result = await apiClient.getRunTestsCatalog(selectedRun.id, {
|
|
13164
|
+
status: "failed",
|
|
13165
|
+
limit: 1e3
|
|
13166
|
+
});
|
|
13167
|
+
if (result.tests.length === 0) {
|
|
13168
|
+
setLoadError("No failed tests found in this run.");
|
|
13169
|
+
setStep("error");
|
|
13170
|
+
return;
|
|
13171
|
+
}
|
|
13172
|
+
setLoadingProgress({ current: 0, total: result.tests.length });
|
|
13173
|
+
const testIds = result.tests.map((t) => t.id);
|
|
13174
|
+
const testDetailsArray = await apiClient.getTestDetailsBulk(testIds);
|
|
13175
|
+
const testDetails = testDetailsArray.filter(
|
|
13176
|
+
(detail) => detail !== null
|
|
13177
|
+
);
|
|
13178
|
+
const histories = await Promise.allSettled(
|
|
13179
|
+
result.tests.map(
|
|
13180
|
+
(t) => apiClient.getTestHistory(t.testId, 10).catch(() => null)
|
|
13181
|
+
)
|
|
13182
|
+
);
|
|
13183
|
+
const testContexts = testDetails.map((test, index) => {
|
|
13184
|
+
const historyResult = histories[index];
|
|
13185
|
+
const history = historyResult?.status === "fulfilled" && historyResult.value ? historyResult.value : void 0;
|
|
13186
|
+
return { test, history };
|
|
13187
|
+
});
|
|
13188
|
+
const assignmentInfoMap = /* @__PURE__ */ new Map();
|
|
13189
|
+
for (const test of result.tests) {
|
|
13190
|
+
if (test.assignment) {
|
|
13191
|
+
assignmentInfoMap.set(test.id, {
|
|
13192
|
+
assignedTo: test.assignment.assignedTo,
|
|
13193
|
+
status: test.assignment.status
|
|
13194
|
+
});
|
|
13195
|
+
}
|
|
13196
|
+
}
|
|
13197
|
+
const prompt = buildUnderstandAndFixPrompt(
|
|
13198
|
+
testContexts,
|
|
13199
|
+
selectedRun,
|
|
13200
|
+
assignmentInfoMap
|
|
13201
|
+
);
|
|
13202
|
+
setSelectedTests(result.tests);
|
|
13203
|
+
setStep("fixing");
|
|
13204
|
+
onStartFix(prompt, result.tests);
|
|
13205
|
+
} catch (err) {
|
|
13206
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
13207
|
+
if (errorMessage.includes("network") || errorMessage.includes("fetch")) {
|
|
13208
|
+
setLoadError(`Network error: Unable to connect to the server.
|
|
13209
|
+
|
|
13210
|
+
Please check your internet connection and try again.
|
|
13211
|
+
|
|
13212
|
+
Error: ${errorMessage}`);
|
|
13213
|
+
} else if (errorMessage.includes("auth") || errorMessage.includes("unauthorized")) {
|
|
13214
|
+
setLoadError(`Authentication error: Please run 'supatest auth' to log in.
|
|
13215
|
+
|
|
13216
|
+
Error: ${errorMessage}`);
|
|
13217
|
+
} else {
|
|
13218
|
+
setLoadError(`Failed to load test details:
|
|
13219
|
+
|
|
13220
|
+
${errorMessage}
|
|
13221
|
+
|
|
13222
|
+
Press ESC to go back and try again.`);
|
|
13223
|
+
}
|
|
13224
|
+
setStep("error");
|
|
13225
|
+
}
|
|
13226
|
+
};
|
|
12993
13227
|
const handleTestsSelect = async (tests) => {
|
|
12994
13228
|
setSelectedTests(tests);
|
|
12995
13229
|
setStep("claiming-tests");
|
|
12996
13230
|
setLoadError(null);
|
|
12997
13231
|
try {
|
|
12998
|
-
const assignmentMap = /* @__PURE__ */ new Map();
|
|
12999
13232
|
const testCatalogUuids = [];
|
|
13000
13233
|
const testRunToCatalogMap = /* @__PURE__ */ new Map();
|
|
13001
13234
|
for (const test of tests) {
|
|
@@ -13009,6 +13242,7 @@ var init_FixFlow = __esm({
|
|
|
13009
13242
|
const result = await apiClient.assignTestsBulk({
|
|
13010
13243
|
testIds: testCatalogUuids
|
|
13011
13244
|
});
|
|
13245
|
+
const assignmentMap = /* @__PURE__ */ new Map();
|
|
13012
13246
|
for (const assignment of result.successful) {
|
|
13013
13247
|
const testRunId = testRunToCatalogMap.get(assignment.testId);
|
|
13014
13248
|
if (testRunId) {
|
|
@@ -13021,10 +13255,7 @@ var init_FixFlow = __esm({
|
|
|
13021
13255
|
if (testRunId) {
|
|
13022
13256
|
const test = tests.find((t) => t.id === testRunId);
|
|
13023
13257
|
if (test) {
|
|
13024
|
-
conflicts.push({
|
|
13025
|
-
test,
|
|
13026
|
-
assignee: conflict.assignedTo
|
|
13027
|
-
});
|
|
13258
|
+
conflicts.push({ test, assignee: conflict.assignedTo });
|
|
13028
13259
|
}
|
|
13029
13260
|
}
|
|
13030
13261
|
}
|
|
@@ -13056,7 +13287,7 @@ Please select different tests.`
|
|
|
13056
13287
|
(detail) => detail !== null
|
|
13057
13288
|
);
|
|
13058
13289
|
const testContexts = testDetails.map((test) => ({ test }));
|
|
13059
|
-
const prompt =
|
|
13290
|
+
const prompt = buildQuickFixPrompt(testContexts);
|
|
13060
13291
|
setStep("fixing");
|
|
13061
13292
|
onStartFix(prompt, testsToLoad);
|
|
13062
13293
|
} catch (err) {
|
|
@@ -13085,14 +13316,10 @@ Press ESC to go back and try again.`);
|
|
|
13085
13316
|
onCancel();
|
|
13086
13317
|
};
|
|
13087
13318
|
const handleTestCancel = () => {
|
|
13088
|
-
|
|
13089
|
-
setAssignments(/* @__PURE__ */ new Map());
|
|
13090
|
-
setStep("select-run");
|
|
13319
|
+
setStep("choosing-mode");
|
|
13091
13320
|
};
|
|
13092
|
-
const markAssignmentsComplete = async (
|
|
13093
|
-
if (assignmentIds.size === 0)
|
|
13094
|
-
return;
|
|
13095
|
-
}
|
|
13321
|
+
const markAssignmentsComplete = async () => {
|
|
13322
|
+
if (assignmentIds.size === 0) return;
|
|
13096
13323
|
try {
|
|
13097
13324
|
await Promise.all(
|
|
13098
13325
|
Array.from(assignmentIds.values()).map(
|
|
@@ -13106,21 +13333,6 @@ Press ESC to go back and try again.`);
|
|
|
13106
13333
|
console.error("Failed to mark assignments as complete:", err);
|
|
13107
13334
|
}
|
|
13108
13335
|
};
|
|
13109
|
-
const releaseAssignments = async () => {
|
|
13110
|
-
if (assignmentIds.size === 0) {
|
|
13111
|
-
return;
|
|
13112
|
-
}
|
|
13113
|
-
try {
|
|
13114
|
-
await Promise.all(
|
|
13115
|
-
Array.from(assignmentIds.values()).map(
|
|
13116
|
-
(assignmentId) => apiClient.releaseAssignment(assignmentId)
|
|
13117
|
-
)
|
|
13118
|
-
);
|
|
13119
|
-
setAssignmentIds(/* @__PURE__ */ new Map());
|
|
13120
|
-
} catch (err) {
|
|
13121
|
-
console.error("Failed to release assignments:", err);
|
|
13122
|
-
}
|
|
13123
|
-
};
|
|
13124
13336
|
useEffect8(() => {
|
|
13125
13337
|
if (step === "complete") {
|
|
13126
13338
|
markAssignmentsComplete();
|
|
@@ -13137,10 +13349,47 @@ Press ESC to go back and try again.`);
|
|
|
13137
13349
|
onSelect: handleRunSelect
|
|
13138
13350
|
}
|
|
13139
13351
|
);
|
|
13140
|
-
case "
|
|
13141
|
-
|
|
13142
|
-
|
|
13352
|
+
case "choosing-mode": {
|
|
13353
|
+
const failed = selectedRun?.summary?.failed ?? 0;
|
|
13354
|
+
const branch = selectedRun?.git?.branch || "unknown";
|
|
13355
|
+
const commit = selectedRun?.git?.commit?.slice(0, 7) || "";
|
|
13356
|
+
if (failed === 0) {
|
|
13357
|
+
return /* @__PURE__ */ React22.createElement(Box19, { borderColor: "green", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React22.createElement(Text17, { bold: true, color: "green" }, "No Failed Tests"), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, "All tests passed in this run. Nothing to fix!"), /* @__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")));
|
|
13143
13358
|
}
|
|
13359
|
+
const modes = [
|
|
13360
|
+
{
|
|
13361
|
+
label: "Understand & Fix",
|
|
13362
|
+
description: `Agent analyzes all ${failed} failure${failed !== 1 ? "s" : ""}, shows a summary table with root causes and patterns, then guides you through fixing`
|
|
13363
|
+
},
|
|
13364
|
+
{
|
|
13365
|
+
label: "Quick Fix",
|
|
13366
|
+
description: "Select specific tests to fix immediately \u2014 skip analysis and go straight to fixing"
|
|
13367
|
+
}
|
|
13368
|
+
];
|
|
13369
|
+
return /* @__PURE__ */ React22.createElement(Box19, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React22.createElement(Box19, { marginBottom: 1 }, /* @__PURE__ */ React22.createElement(Text17, { bold: true, color: "cyan" }, branch, commit && /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " @ ", commit), /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, " \u2022 "), /* @__PURE__ */ React22.createElement(Text17, { color: "red" }, failed, " failed"))), /* @__PURE__ */ React22.createElement(Box19, { marginBottom: 1 }, /* @__PURE__ */ React22.createElement(Text17, { bold: true, color: theme.text.primary }, "How would you like to proceed?")), /* @__PURE__ */ React22.createElement(Box19, { flexDirection: "column", marginBottom: 1 }, modes.map((mode, index) => {
|
|
13370
|
+
const isSelected = index === modeSelectionIndex;
|
|
13371
|
+
const indicator = isSelected ? "\u25B6 " : " ";
|
|
13372
|
+
return /* @__PURE__ */ React22.createElement(Box19, { flexDirection: "column", gap: 0, key: mode.label }, /* @__PURE__ */ React22.createElement(Box19, null, /* @__PURE__ */ React22.createElement(
|
|
13373
|
+
Text17,
|
|
13374
|
+
{
|
|
13375
|
+
backgroundColor: isSelected ? theme.text.accent : void 0,
|
|
13376
|
+
bold: isSelected,
|
|
13377
|
+
color: isSelected ? "black" : theme.text.primary
|
|
13378
|
+
},
|
|
13379
|
+
indicator,
|
|
13380
|
+
mode.label
|
|
13381
|
+
)), /* @__PURE__ */ React22.createElement(Box19, { marginLeft: 4 }, /* @__PURE__ */ React22.createElement(
|
|
13382
|
+
Text17,
|
|
13383
|
+
{
|
|
13384
|
+
color: isSelected ? theme.text.primary : theme.text.dim,
|
|
13385
|
+
dimColor: !isSelected
|
|
13386
|
+
},
|
|
13387
|
+
mode.description
|
|
13388
|
+
)));
|
|
13389
|
+
})), /* @__PURE__ */ React22.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, /* @__PURE__ */ React22.createElement(Text17, { bold: true }, "\u2191\u2193"), " navigate \u2022 ", /* @__PURE__ */ React22.createElement(Text17, { bold: true }, "Enter"), " select \u2022 ", /* @__PURE__ */ React22.createElement(Text17, { bold: true }, "ESC"), " back")));
|
|
13390
|
+
}
|
|
13391
|
+
case "select-tests":
|
|
13392
|
+
if (!selectedRun) return null;
|
|
13144
13393
|
return /* @__PURE__ */ React22.createElement(
|
|
13145
13394
|
TestSelector,
|
|
13146
13395
|
{
|
|
@@ -13151,6 +13400,8 @@ Press ESC to go back and try again.`);
|
|
|
13151
13400
|
run: selectedRun
|
|
13152
13401
|
}
|
|
13153
13402
|
);
|
|
13403
|
+
case "loading-analysis":
|
|
13404
|
+
return /* @__PURE__ */ React22.createElement(Box19, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React22.createElement(Text17, { bold: true, color: "cyan" }, "Analyzing Failures..."), /* @__PURE__ */ React22.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, "Loading test details, error data, and execution history for all failed tests...")), loadingProgress.total > 0 && /* @__PURE__ */ React22.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text17, { color: "yellow" }, loadingProgress.current, " / ", loadingProgress.total, " tests loaded")));
|
|
13154
13405
|
case "claiming-tests":
|
|
13155
13406
|
return /* @__PURE__ */ React22.createElement(Box19, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React22.createElement(Text17, { bold: true, color: "cyan" }, "Claiming Tests..."), /* @__PURE__ */ React22.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, "Assigning ", selectedTests.length, " test", selectedTests.length !== 1 ? "s" : "", " to you...")));
|
|
13156
13407
|
case "loading-details":
|
|
@@ -13160,8 +13411,10 @@ Press ESC to go back and try again.`);
|
|
|
13160
13411
|
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")));
|
|
13161
13412
|
case "error":
|
|
13162
13413
|
return /* @__PURE__ */ React22.createElement(Box19, { borderColor: "red", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React22.createElement(Text17, { bold: true, color: "red" }, "Error"), /* @__PURE__ */ React22.createElement(Box19, { marginTop: 1 }, /* @__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")));
|
|
13163
|
-
case "fixing":
|
|
13164
|
-
|
|
13414
|
+
case "fixing": {
|
|
13415
|
+
const modeLabel = workflowMode === "understand-and-fix" ? "Analyzing & Fixing" : "Fixing";
|
|
13416
|
+
return /* @__PURE__ */ React22.createElement(Box19, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React22.createElement(Text17, { bold: true, color: "cyan" }, modeLabel, " ", selectedTests.length, " Test", selectedTests.length !== 1 ? "s" : "", "..."), /* @__PURE__ */ React22.createElement(Box19, { marginTop: 1 }, selectedTests.slice(0, 10).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))), selectedTests.length > 10 && /* @__PURE__ */ React22.createElement(Box19, null, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, "... and ", selectedTests.length - 10, " more"))), /* @__PURE__ */ React22.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, workflowMode === "understand-and-fix" ? "The agent will analyze all failures, present findings, and guide you through fixing..." : "The agent will now analyze and fix each test...")));
|
|
13417
|
+
}
|
|
13165
13418
|
case "complete":
|
|
13166
13419
|
return /* @__PURE__ */ React22.createElement(Box19, { borderColor: "green", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React22.createElement(Text17, { bold: true, color: "green" }, "Tests Marked as Complete"), /* @__PURE__ */ React22.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, "\u2713 ", selectedTests.length, " test", selectedTests.length !== 1 ? "s have" : " has", " been marked as fixed")), /* @__PURE__ */ React22.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text17, { color: theme.text.dim }, "Press any key to continue...")));
|
|
13167
13420
|
default:
|
|
@@ -14125,9 +14378,87 @@ var init_ProviderSelector = __esm({
|
|
|
14125
14378
|
}
|
|
14126
14379
|
});
|
|
14127
14380
|
|
|
14128
|
-
// src/ui/components/
|
|
14381
|
+
// src/ui/components/PlanApprovalSelector.tsx
|
|
14129
14382
|
import { Box as Box26, Text as Text24, useInput as useInput10 } from "ink";
|
|
14130
14383
|
import React29, { useState as useState16 } from "react";
|
|
14384
|
+
var OPTIONS, PlanApprovalSelector;
|
|
14385
|
+
var init_PlanApprovalSelector = __esm({
|
|
14386
|
+
"src/ui/components/PlanApprovalSelector.tsx"() {
|
|
14387
|
+
"use strict";
|
|
14388
|
+
init_theme();
|
|
14389
|
+
OPTIONS = [
|
|
14390
|
+
{ label: "Build it", description: "Approve the plan and start building" },
|
|
14391
|
+
{ label: "Give feedback", description: "Send feedback to revise the plan" }
|
|
14392
|
+
];
|
|
14393
|
+
PlanApprovalSelector = ({
|
|
14394
|
+
allowedPrompts,
|
|
14395
|
+
onApprove,
|
|
14396
|
+
onFeedback
|
|
14397
|
+
}) => {
|
|
14398
|
+
const [selectedIndex, setSelectedIndex] = useState16(0);
|
|
14399
|
+
const [feedbackMode, setFeedbackMode] = useState16(false);
|
|
14400
|
+
const [feedbackText, setFeedbackText] = useState16("");
|
|
14401
|
+
useInput10((input, key) => {
|
|
14402
|
+
if (feedbackMode) {
|
|
14403
|
+
if (key.return) {
|
|
14404
|
+
if (feedbackText.trim()) {
|
|
14405
|
+
onFeedback(feedbackText.trim());
|
|
14406
|
+
}
|
|
14407
|
+
} else if (key.escape) {
|
|
14408
|
+
setFeedbackMode(false);
|
|
14409
|
+
setFeedbackText("");
|
|
14410
|
+
} else if (key.backspace || key.delete) {
|
|
14411
|
+
setFeedbackText((prev) => prev.slice(0, -1));
|
|
14412
|
+
} else if (input && !key.ctrl && !key.meta) {
|
|
14413
|
+
setFeedbackText((prev) => prev + input);
|
|
14414
|
+
}
|
|
14415
|
+
return;
|
|
14416
|
+
}
|
|
14417
|
+
if (key.upArrow) {
|
|
14418
|
+
setSelectedIndex((prev) => prev > 0 ? prev - 1 : OPTIONS.length - 1);
|
|
14419
|
+
} else if (key.downArrow) {
|
|
14420
|
+
setSelectedIndex((prev) => prev < OPTIONS.length - 1 ? prev + 1 : 0);
|
|
14421
|
+
} else if (key.return) {
|
|
14422
|
+
if (selectedIndex === 0) {
|
|
14423
|
+
onApprove();
|
|
14424
|
+
} else {
|
|
14425
|
+
setFeedbackMode(true);
|
|
14426
|
+
}
|
|
14427
|
+
} else if (key.escape) {
|
|
14428
|
+
onFeedback("Plan not approved by user.");
|
|
14429
|
+
}
|
|
14430
|
+
});
|
|
14431
|
+
if (feedbackMode) {
|
|
14432
|
+
return /* @__PURE__ */ React29.createElement(Box26, { borderColor: theme.border.accent, borderStyle: "round", flexDirection: "column", paddingX: 2, paddingY: 1 }, /* @__PURE__ */ React29.createElement(Box26, { marginBottom: 1 }, /* @__PURE__ */ React29.createElement(Text24, { bold: true, color: theme.text.accent }, "What changes would you like to the plan?")), /* @__PURE__ */ React29.createElement(Box26, { marginBottom: 1 }, /* @__PURE__ */ React29.createElement(Text24, { color: theme.text.primary }, ">", " ", feedbackText, /* @__PURE__ */ React29.createElement(Text24, { backgroundColor: theme.text.accent }, " "))), /* @__PURE__ */ React29.createElement(Box26, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { color: theme.text.dim }, /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "Enter"), " submit feedback \u2022 ", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "ESC"), " back to options")));
|
|
14433
|
+
}
|
|
14434
|
+
return /* @__PURE__ */ React29.createElement(Box26, { borderColor: theme.border.accent, borderStyle: "round", flexDirection: "column", paddingX: 2, paddingY: 1 }, /* @__PURE__ */ React29.createElement(Box26, { marginBottom: 1 }, /* @__PURE__ */ React29.createElement(Text24, { bold: true, color: theme.text.accent }, "Plan ready for review")), allowedPrompts && allowedPrompts.length > 0 && /* @__PURE__ */ React29.createElement(Box26, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React29.createElement(Text24, { color: theme.text.dim }, "The agent wants to:"), allowedPrompts.map((p, i) => /* @__PURE__ */ React29.createElement(Text24, { color: theme.text.dim, key: `${p.prompt}-${i}` }, "\u2022 ", p.prompt))), /* @__PURE__ */ React29.createElement(Box26, { flexDirection: "column", marginBottom: 1 }, OPTIONS.map((option, index) => {
|
|
14435
|
+
const isSelected = index === selectedIndex;
|
|
14436
|
+
const indicator = isSelected ? "\u25B6 " : " ";
|
|
14437
|
+
return /* @__PURE__ */ React29.createElement(Box26, { flexDirection: "column", gap: 0, key: option.label }, /* @__PURE__ */ React29.createElement(Box26, null, /* @__PURE__ */ React29.createElement(
|
|
14438
|
+
Text24,
|
|
14439
|
+
{
|
|
14440
|
+
backgroundColor: isSelected ? theme.text.accent : void 0,
|
|
14441
|
+
bold: isSelected,
|
|
14442
|
+
color: isSelected ? "black" : theme.text.primary
|
|
14443
|
+
},
|
|
14444
|
+
indicator,
|
|
14445
|
+
option.label
|
|
14446
|
+
)), /* @__PURE__ */ React29.createElement(Box26, { marginLeft: 4 }, /* @__PURE__ */ React29.createElement(
|
|
14447
|
+
Text24,
|
|
14448
|
+
{
|
|
14449
|
+
color: isSelected ? theme.text.primary : theme.text.dim,
|
|
14450
|
+
dimColor: !isSelected
|
|
14451
|
+
},
|
|
14452
|
+
option.description
|
|
14453
|
+
)));
|
|
14454
|
+
})), /* @__PURE__ */ React29.createElement(Box26, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { color: theme.text.dim }, /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "\u2191\u2193"), " navigate \u2022 ", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "Enter"), " select \u2022 ", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "ESC"), " cancel")));
|
|
14455
|
+
};
|
|
14456
|
+
}
|
|
14457
|
+
});
|
|
14458
|
+
|
|
14459
|
+
// src/ui/components/QuestionSelector.tsx
|
|
14460
|
+
import { Box as Box27, Text as Text25, useInput as useInput11 } from "ink";
|
|
14461
|
+
import React30, { useState as useState17 } from "react";
|
|
14131
14462
|
var QuestionSelector;
|
|
14132
14463
|
var init_QuestionSelector = __esm({
|
|
14133
14464
|
"src/ui/components/QuestionSelector.tsx"() {
|
|
@@ -14144,11 +14475,11 @@ var init_QuestionSelector = __esm({
|
|
|
14144
14475
|
}) => {
|
|
14145
14476
|
const hasOptions = options && options.length > 0;
|
|
14146
14477
|
const allOptions = hasOptions && allowCustomAnswer ? [...options, { label: "Other", description: "Provide a custom response" }] : options;
|
|
14147
|
-
const [selectedIndex, setSelectedIndex] =
|
|
14148
|
-
const [selectedItems, setSelectedItems] =
|
|
14149
|
-
const [customInputMode, setCustomInputMode] =
|
|
14150
|
-
const [customAnswer, setCustomAnswer] =
|
|
14151
|
-
|
|
14478
|
+
const [selectedIndex, setSelectedIndex] = useState17(0);
|
|
14479
|
+
const [selectedItems, setSelectedItems] = useState17(/* @__PURE__ */ new Set());
|
|
14480
|
+
const [customInputMode, setCustomInputMode] = useState17(!hasOptions);
|
|
14481
|
+
const [customAnswer, setCustomAnswer] = useState17("");
|
|
14482
|
+
useInput11((input, key) => {
|
|
14152
14483
|
if (customInputMode) {
|
|
14153
14484
|
if (key.return) {
|
|
14154
14485
|
if (customAnswer.trim()) {
|
|
@@ -14211,15 +14542,15 @@ var init_QuestionSelector = __esm({
|
|
|
14211
14542
|
}
|
|
14212
14543
|
});
|
|
14213
14544
|
if (customInputMode) {
|
|
14214
|
-
return /* @__PURE__ */
|
|
14545
|
+
return /* @__PURE__ */ React30.createElement(Box27, { borderColor: theme.border.accent, borderStyle: "round", flexDirection: "column", paddingX: 2, paddingY: 1 }, /* @__PURE__ */ React30.createElement(Box27, { marginBottom: 1 }, /* @__PURE__ */ React30.createElement(Text25, { bold: true, color: theme.text.accent }, question)), /* @__PURE__ */ React30.createElement(Box27, { marginBottom: 1 }, /* @__PURE__ */ React30.createElement(Text25, { color: theme.text.dim }, hasOptions ? "Enter your custom answer:" : "Type your answer:")), /* @__PURE__ */ React30.createElement(Box27, { marginBottom: 1 }, /* @__PURE__ */ React30.createElement(Text25, { color: theme.text.primary }, ">", " ", customAnswer, /* @__PURE__ */ React30.createElement(Text25, { backgroundColor: theme.text.accent }, " "))), /* @__PURE__ */ React30.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React30.createElement(Text25, { color: theme.text.dim }, /* @__PURE__ */ React30.createElement(Text25, { bold: true }, "Enter"), " submit \u2022 ", /* @__PURE__ */ React30.createElement(Text25, { bold: true }, "ESC"), " ", hasOptions ? "back to options" : "cancel")));
|
|
14215
14546
|
}
|
|
14216
|
-
return /* @__PURE__ */
|
|
14547
|
+
return /* @__PURE__ */ React30.createElement(Box27, { borderColor: theme.border.accent, borderStyle: "round", flexDirection: "column", paddingX: 2, paddingY: 1 }, /* @__PURE__ */ React30.createElement(Box27, { marginBottom: 1 }, /* @__PURE__ */ React30.createElement(Text25, { bold: true, color: theme.text.accent }, question)), multiSelect && /* @__PURE__ */ React30.createElement(Box27, { marginBottom: 1 }, /* @__PURE__ */ React30.createElement(Text25, { color: theme.text.dim }, "(Select multiple options with ", /* @__PURE__ */ React30.createElement(Text25, { bold: true }, "Space"), ", submit with ", /* @__PURE__ */ React30.createElement(Text25, { bold: true }, "S"), ")")), /* @__PURE__ */ React30.createElement(Box27, { flexDirection: "column", marginBottom: 1 }, allOptions.map((option, index) => {
|
|
14217
14548
|
const isSelected = index === selectedIndex;
|
|
14218
14549
|
const isChosen = selectedItems.has(index);
|
|
14219
14550
|
const indicator = isSelected ? "\u25B6 " : " ";
|
|
14220
14551
|
const checkbox = multiSelect ? isChosen ? "[\u2713] " : "[ ] " : "";
|
|
14221
|
-
return /* @__PURE__ */
|
|
14222
|
-
|
|
14552
|
+
return /* @__PURE__ */ React30.createElement(Box27, { flexDirection: "column", gap: 0, key: `${option.label}-${index}` }, /* @__PURE__ */ React30.createElement(Box27, null, /* @__PURE__ */ React30.createElement(
|
|
14553
|
+
Text25,
|
|
14223
14554
|
{
|
|
14224
14555
|
backgroundColor: isSelected ? theme.text.accent : void 0,
|
|
14225
14556
|
bold: isSelected,
|
|
@@ -14228,22 +14559,22 @@ var init_QuestionSelector = __esm({
|
|
|
14228
14559
|
indicator,
|
|
14229
14560
|
checkbox,
|
|
14230
14561
|
option.label
|
|
14231
|
-
)), /* @__PURE__ */
|
|
14232
|
-
|
|
14562
|
+
)), /* @__PURE__ */ React30.createElement(Box27, { marginLeft: multiSelect ? 6 : 4 }, /* @__PURE__ */ React30.createElement(
|
|
14563
|
+
Text25,
|
|
14233
14564
|
{
|
|
14234
14565
|
color: isSelected ? theme.text.primary : theme.text.dim,
|
|
14235
14566
|
dimColor: !isSelected
|
|
14236
14567
|
},
|
|
14237
14568
|
option.description
|
|
14238
14569
|
)));
|
|
14239
|
-
})), /* @__PURE__ */
|
|
14570
|
+
})), /* @__PURE__ */ React30.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React30.createElement(Text25, { color: theme.text.dim }, /* @__PURE__ */ React30.createElement(Text25, { bold: true }, "\u2191\u2193"), " navigate \u2022 ", multiSelect ? /* @__PURE__ */ React30.createElement(React30.Fragment, null, /* @__PURE__ */ React30.createElement(Text25, { bold: true }, "Space"), " toggle \u2022 ", /* @__PURE__ */ React30.createElement(Text25, { bold: true }, "S"), " submit \u2022", " ") : /* @__PURE__ */ React30.createElement(React30.Fragment, null, /* @__PURE__ */ React30.createElement(Text25, { bold: true }, "Enter"), " select \u2022", " "), /* @__PURE__ */ React30.createElement(Text25, { bold: true }, "ESC"), " cancel")));
|
|
14240
14571
|
};
|
|
14241
14572
|
}
|
|
14242
14573
|
});
|
|
14243
14574
|
|
|
14244
14575
|
// src/ui/components/SessionSelector.tsx
|
|
14245
|
-
import { Box as
|
|
14246
|
-
import
|
|
14576
|
+
import { Box as Box28, Text as Text26, useInput as useInput12 } from "ink";
|
|
14577
|
+
import React31, { useEffect as useEffect12, useState as useState18 } from "react";
|
|
14247
14578
|
function getSessionPrefix(authMethod) {
|
|
14248
14579
|
return authMethod === "api-key" ? "[Team]" : "[Me]";
|
|
14249
14580
|
}
|
|
@@ -14258,12 +14589,12 @@ var init_SessionSelector = __esm({
|
|
|
14258
14589
|
onSelect,
|
|
14259
14590
|
onCancel
|
|
14260
14591
|
}) => {
|
|
14261
|
-
const [allSessions, setAllSessions] =
|
|
14262
|
-
const [selectedIndex, setSelectedIndex] =
|
|
14263
|
-
const [isLoading, setIsLoading] =
|
|
14264
|
-
const [hasMore, setHasMore] =
|
|
14265
|
-
const [totalSessions, setTotalSessions] =
|
|
14266
|
-
const [error, setError] =
|
|
14592
|
+
const [allSessions, setAllSessions] = useState18([]);
|
|
14593
|
+
const [selectedIndex, setSelectedIndex] = useState18(0);
|
|
14594
|
+
const [isLoading, setIsLoading] = useState18(false);
|
|
14595
|
+
const [hasMore, setHasMore] = useState18(true);
|
|
14596
|
+
const [totalSessions, setTotalSessions] = useState18(0);
|
|
14597
|
+
const [error, setError] = useState18(null);
|
|
14267
14598
|
useEffect12(() => {
|
|
14268
14599
|
loadMoreSessions();
|
|
14269
14600
|
}, []);
|
|
@@ -14290,7 +14621,7 @@ var init_SessionSelector = __esm({
|
|
|
14290
14621
|
setIsLoading(false);
|
|
14291
14622
|
}
|
|
14292
14623
|
};
|
|
14293
|
-
|
|
14624
|
+
useInput12((input, key) => {
|
|
14294
14625
|
if (allSessions.length === 0) {
|
|
14295
14626
|
if (key.escape || input === "q") {
|
|
14296
14627
|
onCancel();
|
|
@@ -14314,13 +14645,13 @@ var init_SessionSelector = __esm({
|
|
|
14314
14645
|
}
|
|
14315
14646
|
});
|
|
14316
14647
|
if (error) {
|
|
14317
|
-
return /* @__PURE__ */
|
|
14648
|
+
return /* @__PURE__ */ React31.createElement(Box28, { borderColor: "red", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React31.createElement(Text26, { bold: true, color: "red" }, "Error Loading Sessions"), /* @__PURE__ */ React31.createElement(Text26, { color: theme.text.dim }, error), /* @__PURE__ */ React31.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React31.createElement(Text26, { color: theme.text.dim }, "Press ", /* @__PURE__ */ React31.createElement(Text26, { bold: true }, "ESC"), " to cancel")));
|
|
14318
14649
|
}
|
|
14319
14650
|
if (allSessions.length === 0 && isLoading) {
|
|
14320
|
-
return /* @__PURE__ */
|
|
14651
|
+
return /* @__PURE__ */ React31.createElement(Box28, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React31.createElement(Text26, { bold: true, color: "cyan" }, "Loading Sessions..."), /* @__PURE__ */ React31.createElement(Text26, { color: theme.text.dim }, "Fetching your sessions from the server"));
|
|
14321
14652
|
}
|
|
14322
14653
|
if (allSessions.length === 0 && !isLoading) {
|
|
14323
|
-
return /* @__PURE__ */
|
|
14654
|
+
return /* @__PURE__ */ React31.createElement(Box28, { borderColor: "yellow", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React31.createElement(Text26, { bold: true, color: "yellow" }, "No Sessions Found"), /* @__PURE__ */ React31.createElement(Text26, { color: theme.text.dim }, "No previous sessions available. Start a new session!"), /* @__PURE__ */ React31.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React31.createElement(Text26, { color: theme.text.dim }, "Press ", /* @__PURE__ */ React31.createElement(Text26, { bold: true }, "ESC"), " to cancel")));
|
|
14324
14655
|
}
|
|
14325
14656
|
const VISIBLE_ITEMS3 = 10;
|
|
14326
14657
|
let startIndex;
|
|
@@ -14340,7 +14671,7 @@ var init_SessionSelector = __esm({
|
|
|
14340
14671
|
const visibleSessions = allSessions.slice(startIndex, endIndex);
|
|
14341
14672
|
const MAX_TITLE_WIDTH = 50;
|
|
14342
14673
|
const PREFIX_WIDTH = 6;
|
|
14343
|
-
return /* @__PURE__ */
|
|
14674
|
+
return /* @__PURE__ */ React31.createElement(Box28, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React31.createElement(Box28, { marginBottom: 1 }, /* @__PURE__ */ React31.createElement(Text26, { bold: true, color: "cyan" }, "Select a Session to Resume")), /* @__PURE__ */ React31.createElement(Box28, { flexDirection: "column" }, visibleSessions.map((item, index) => {
|
|
14344
14675
|
const actualIndex = startIndex + index;
|
|
14345
14676
|
const isSelected = actualIndex === selectedIndex;
|
|
14346
14677
|
const title = item.session.title || "Untitled session";
|
|
@@ -14364,8 +14695,8 @@ var init_SessionSelector = __esm({
|
|
|
14364
14695
|
const prefixColor = item.prefix === "[Me]" ? "cyan" : "yellow";
|
|
14365
14696
|
const indicator = isSelected ? "\u25B6 " : " ";
|
|
14366
14697
|
const bgColor = isSelected ? theme.text.accent : void 0;
|
|
14367
|
-
return /* @__PURE__ */
|
|
14368
|
-
})), /* @__PURE__ */
|
|
14698
|
+
return /* @__PURE__ */ React31.createElement(Box28, { key: item.session.id, width: "100%" }, /* @__PURE__ */ React31.createElement(Text26, { backgroundColor: bgColor, bold: isSelected, color: isSelected ? "black" : theme.text.primary }, indicator), /* @__PURE__ */ React31.createElement(Text26, { backgroundColor: bgColor, bold: isSelected, color: prefixColor }, prefix), /* @__PURE__ */ React31.createElement(Text26, { backgroundColor: bgColor, bold: isSelected, color: isSelected ? "black" : theme.text.primary }, displayTitle), /* @__PURE__ */ React31.createElement(Text26, { backgroundColor: bgColor, color: theme.text.dim }, "(", dateStr, ")"));
|
|
14699
|
+
})), /* @__PURE__ */ React31.createElement(Box28, { flexDirection: "column", marginTop: 1 }, (allSessions.length > VISIBLE_ITEMS3 || totalSessions > allSessions.length) && /* @__PURE__ */ React31.createElement(Box28, { marginBottom: 1 }, /* @__PURE__ */ React31.createElement(Text26, { color: "yellow" }, "Showing ", startIndex + 1, "-", endIndex, " of ", totalSessions || allSessions.length, " sessions", hasMore && !isLoading && /* @__PURE__ */ React31.createElement(Text26, { color: theme.text.dim }, " \u2022 Scroll for more"))), /* @__PURE__ */ React31.createElement(Box28, null, /* @__PURE__ */ React31.createElement(Text26, { color: theme.text.dim }, "Use ", /* @__PURE__ */ React31.createElement(Text26, { bold: true }, "\u2191\u2193"), " to navigate \u2022 ", /* @__PURE__ */ React31.createElement(Text26, { bold: true }, "Enter"), " to select \u2022 ", /* @__PURE__ */ React31.createElement(Text26, { bold: true }, "ESC"), " to cancel")), isLoading && /* @__PURE__ */ React31.createElement(Box28, { marginTop: 1 }, /* @__PURE__ */ React31.createElement(Text26, { color: "cyan" }, "Loading more sessions..."))));
|
|
14369
14700
|
};
|
|
14370
14701
|
}
|
|
14371
14702
|
});
|
|
@@ -14416,9 +14747,9 @@ var init_useOverlayEscapeGuard = __esm({
|
|
|
14416
14747
|
// src/ui/App.tsx
|
|
14417
14748
|
import { execSync as execSync5 } from "child_process";
|
|
14418
14749
|
import { homedir as homedir9 } from "os";
|
|
14419
|
-
import { Box as
|
|
14750
|
+
import { Box as Box29, Text as Text27, useApp as useApp2, useStdout as useStdout2 } from "ink";
|
|
14420
14751
|
import Spinner4 from "ink-spinner";
|
|
14421
|
-
import
|
|
14752
|
+
import React32, { useCallback as useCallback5, useEffect as useEffect14, useRef as useRef8, useState as useState19 } from "react";
|
|
14422
14753
|
var getGitBranch2, getCurrentFolder2, AppContent, App;
|
|
14423
14754
|
var init_App = __esm({
|
|
14424
14755
|
"src/ui/App.tsx"() {
|
|
@@ -14445,6 +14776,7 @@ var init_App = __esm({
|
|
|
14445
14776
|
init_MessageList();
|
|
14446
14777
|
init_ModelSelector();
|
|
14447
14778
|
init_ProviderSelector();
|
|
14779
|
+
init_PlanApprovalSelector();
|
|
14448
14780
|
init_QuestionSelector();
|
|
14449
14781
|
init_SessionSelector();
|
|
14450
14782
|
init_SessionContext();
|
|
@@ -14468,37 +14800,37 @@ var init_App = __esm({
|
|
|
14468
14800
|
}
|
|
14469
14801
|
return cwd;
|
|
14470
14802
|
};
|
|
14471
|
-
AppContent = ({ config: config2, sessionId, webUrl, queuedTasks = [], onExit, onSubmitTask, apiClient, onResumeSession, onClearSession, onQuestionAnswer }) => {
|
|
14803
|
+
AppContent = ({ config: config2, sessionId, webUrl, queuedTasks = [], onExit, onSubmitTask, apiClient, onResumeSession, onClearSession, onQuestionAnswer, onPlanAnswer }) => {
|
|
14472
14804
|
const { exit } = useApp2();
|
|
14473
14805
|
const { stdout } = useStdout2();
|
|
14474
|
-
const { addMessage, clearMessages, isAgentRunning, setSessionId, setWebUrl, setShouldInterruptAgent, setIsAgentRunning, toggleAllToolOutputs, allToolsExpanded, selectedModel, setSelectedModel, refreshStatic, toggleToolGroups, llmProvider, setLlmProvider, pendingQuestion, setPendingQuestion } = useSession();
|
|
14806
|
+
const { addMessage, clearMessages, isAgentRunning, setSessionId, setWebUrl, setShouldInterruptAgent, setIsAgentRunning, toggleAllToolOutputs, allToolsExpanded, selectedModel, setSelectedModel, refreshStatic, toggleToolGroups, llmProvider, setLlmProvider, pendingQuestion, setPendingQuestion, pendingPlanApproval, setPendingPlanApproval, agentMode, setAgentMode } = useSession();
|
|
14475
14807
|
useModeToggle();
|
|
14476
|
-
const [terminalWidth, setTerminalWidth] =
|
|
14477
|
-
const [showInput, setShowInput] =
|
|
14478
|
-
const [gitBranch] =
|
|
14479
|
-
const [currentFolder] =
|
|
14808
|
+
const [terminalWidth, setTerminalWidth] = useState19(process.stdout.columns || 80);
|
|
14809
|
+
const [showInput, setShowInput] = useState19(true);
|
|
14810
|
+
const [gitBranch] = useState19(() => getGitBranch2());
|
|
14811
|
+
const [currentFolder] = useState19(() => getCurrentFolder2(config2.cwd));
|
|
14480
14812
|
const hasInputContentRef = useRef8(false);
|
|
14481
|
-
const [exitWarning, setExitWarning] =
|
|
14813
|
+
const [exitWarning, setExitWarning] = useState19(null);
|
|
14482
14814
|
const inputPromptRef = useRef8(null);
|
|
14483
|
-
const [showSessionSelector, setShowSessionSelector] =
|
|
14484
|
-
const [showModelSelector, setShowModelSelector] =
|
|
14485
|
-
const [showProviderSelector, setShowProviderSelector] =
|
|
14486
|
-
const [claudeMaxAvailable, setClaudeMaxAvailable] =
|
|
14487
|
-
const [showFeedbackDialog, setShowFeedbackDialog] =
|
|
14488
|
-
const [isLoadingSession, setIsLoadingSession] =
|
|
14489
|
-
const [showFixFlow, setShowFixFlow] =
|
|
14490
|
-
const [fixRunId, setFixRunId] =
|
|
14491
|
-
const [showMcpServers, setShowMcpServers] =
|
|
14492
|
-
const [showMcpAdd, setShowMcpAdd] =
|
|
14493
|
-
const [showMcpSelector, setShowMcpSelector] =
|
|
14494
|
-
const [mcpSelectorAction, setMcpSelectorAction] =
|
|
14815
|
+
const [showSessionSelector, setShowSessionSelector] = useState19(false);
|
|
14816
|
+
const [showModelSelector, setShowModelSelector] = useState19(false);
|
|
14817
|
+
const [showProviderSelector, setShowProviderSelector] = useState19(false);
|
|
14818
|
+
const [claudeMaxAvailable, setClaudeMaxAvailable] = useState19(false);
|
|
14819
|
+
const [showFeedbackDialog, setShowFeedbackDialog] = useState19(false);
|
|
14820
|
+
const [isLoadingSession, setIsLoadingSession] = useState19(false);
|
|
14821
|
+
const [showFixFlow, setShowFixFlow] = useState19(false);
|
|
14822
|
+
const [fixRunId, setFixRunId] = useState19(void 0);
|
|
14823
|
+
const [showMcpServers, setShowMcpServers] = useState19(false);
|
|
14824
|
+
const [showMcpAdd, setShowMcpAdd] = useState19(false);
|
|
14825
|
+
const [showMcpSelector, setShowMcpSelector] = useState19(false);
|
|
14826
|
+
const [mcpSelectorAction, setMcpSelectorAction] = useState19(
|
|
14495
14827
|
"remove"
|
|
14496
14828
|
);
|
|
14497
|
-
const [mcpServers, setMcpServers] =
|
|
14498
|
-
const [authState, setAuthState] =
|
|
14829
|
+
const [mcpServers, setMcpServers] = useState19([]);
|
|
14830
|
+
const [authState, setAuthState] = useState19(
|
|
14499
14831
|
() => config2.supatestApiKey ? "authenticated" /* Authenticated */ : "unauthenticated" /* Unauthenticated */
|
|
14500
14832
|
);
|
|
14501
|
-
const [showAuthDialog, setShowAuthDialog] =
|
|
14833
|
+
const [showAuthDialog, setShowAuthDialog] = useState19(false);
|
|
14502
14834
|
const { isOverlayOpen, isCancelSuppressed, markOverlayClosed } = useOverlayEscapeGuard({
|
|
14503
14835
|
overlays: [
|
|
14504
14836
|
showSessionSelector,
|
|
@@ -14510,7 +14842,8 @@ var init_App = __esm({
|
|
|
14510
14842
|
showMcpServers,
|
|
14511
14843
|
showMcpAdd,
|
|
14512
14844
|
showMcpSelector,
|
|
14513
|
-
!!pendingQuestion
|
|
14845
|
+
!!pendingQuestion,
|
|
14846
|
+
!!pendingPlanApproval
|
|
14514
14847
|
]
|
|
14515
14848
|
});
|
|
14516
14849
|
useEffect14(() => {
|
|
@@ -14526,6 +14859,11 @@ var init_App = __esm({
|
|
|
14526
14859
|
setWebUrl(webUrl);
|
|
14527
14860
|
}
|
|
14528
14861
|
}, [sessionId, webUrl, setSessionId, setWebUrl]);
|
|
14862
|
+
useEffect14(() => {
|
|
14863
|
+
if (!isAgentRunning && agentMode === "fix") {
|
|
14864
|
+
setAgentMode("build");
|
|
14865
|
+
}
|
|
14866
|
+
}, [isAgentRunning, agentMode, setAgentMode]);
|
|
14529
14867
|
const handleLogin = async () => {
|
|
14530
14868
|
setShowAuthDialog(false);
|
|
14531
14869
|
setAuthState("authenticating" /* Authenticating */);
|
|
@@ -14895,6 +15233,31 @@ var init_App = __esm({
|
|
|
14895
15233
|
setPendingQuestion(null);
|
|
14896
15234
|
markOverlayClosed();
|
|
14897
15235
|
};
|
|
15236
|
+
const handlePlanApprove = () => {
|
|
15237
|
+
if (!pendingPlanApproval) return;
|
|
15238
|
+
addMessage({
|
|
15239
|
+
type: "user",
|
|
15240
|
+
content: "Plan approved \u2014 build it."
|
|
15241
|
+
});
|
|
15242
|
+
setAgentMode("build");
|
|
15243
|
+
if (onPlanAnswer) {
|
|
15244
|
+
onPlanAnswer(pendingPlanApproval.toolUseId, true);
|
|
15245
|
+
}
|
|
15246
|
+
setPendingPlanApproval(null);
|
|
15247
|
+
markOverlayClosed();
|
|
15248
|
+
};
|
|
15249
|
+
const handlePlanFeedback = (feedback) => {
|
|
15250
|
+
if (!pendingPlanApproval) return;
|
|
15251
|
+
addMessage({
|
|
15252
|
+
type: "user",
|
|
15253
|
+
content: feedback
|
|
15254
|
+
});
|
|
15255
|
+
if (onPlanAnswer) {
|
|
15256
|
+
onPlanAnswer(pendingPlanApproval.toolUseId, false, feedback);
|
|
15257
|
+
}
|
|
15258
|
+
setPendingPlanApproval(null);
|
|
15259
|
+
markOverlayClosed();
|
|
15260
|
+
};
|
|
14898
15261
|
const handleFixFlowCancel = () => {
|
|
14899
15262
|
markOverlayClosed();
|
|
14900
15263
|
setShowFixFlow(false);
|
|
@@ -14920,6 +15283,7 @@ var init_App = __esm({
|
|
|
14920
15283
|
});
|
|
14921
15284
|
return;
|
|
14922
15285
|
}
|
|
15286
|
+
setAgentMode("fix");
|
|
14923
15287
|
addMessage({
|
|
14924
15288
|
type: "user",
|
|
14925
15289
|
content: prompt
|
|
@@ -15114,14 +15478,14 @@ var init_App = __esm({
|
|
|
15114
15478
|
});
|
|
15115
15479
|
}
|
|
15116
15480
|
}, []);
|
|
15117
|
-
return /* @__PURE__ */
|
|
15118
|
-
|
|
15481
|
+
return /* @__PURE__ */ React32.createElement(
|
|
15482
|
+
Box29,
|
|
15119
15483
|
{
|
|
15120
15484
|
flexDirection: "column",
|
|
15121
15485
|
paddingX: 1
|
|
15122
15486
|
},
|
|
15123
|
-
/* @__PURE__ */
|
|
15124
|
-
showSessionSelector && apiClient && /* @__PURE__ */
|
|
15487
|
+
/* @__PURE__ */ React32.createElement(MessageList, { currentFolder, gitBranch, queuedTasks, terminalWidth }),
|
|
15488
|
+
showSessionSelector && apiClient && /* @__PURE__ */ React32.createElement(
|
|
15125
15489
|
SessionSelector,
|
|
15126
15490
|
{
|
|
15127
15491
|
apiClient,
|
|
@@ -15129,8 +15493,8 @@ var init_App = __esm({
|
|
|
15129
15493
|
onSelect: handleSessionSelect
|
|
15130
15494
|
}
|
|
15131
15495
|
),
|
|
15132
|
-
isLoadingSession && /* @__PURE__ */
|
|
15133
|
-
showModelSelector && /* @__PURE__ */
|
|
15496
|
+
isLoadingSession && /* @__PURE__ */ React32.createElement(Box29, { borderColor: "cyan", borderStyle: "round", flexDirection: "column", padding: 1 }, /* @__PURE__ */ React32.createElement(Box29, { flexDirection: "row" }, /* @__PURE__ */ React32.createElement(Box29, { width: 2 }, /* @__PURE__ */ React32.createElement(Text27, { color: theme.text.accent }, /* @__PURE__ */ React32.createElement(Spinner4, { type: "dots" }))), /* @__PURE__ */ React32.createElement(Text27, { bold: true, color: "cyan" }, "Loading session...")), /* @__PURE__ */ React32.createElement(Text27, { color: theme.text.dim }, "Fetching queries and context")),
|
|
15497
|
+
showModelSelector && /* @__PURE__ */ React32.createElement(
|
|
15134
15498
|
ModelSelector,
|
|
15135
15499
|
{
|
|
15136
15500
|
currentModel: selectedModel,
|
|
@@ -15139,7 +15503,7 @@ var init_App = __esm({
|
|
|
15139
15503
|
onSelect: handleModelSelect
|
|
15140
15504
|
}
|
|
15141
15505
|
),
|
|
15142
|
-
showProviderSelector && /* @__PURE__ */
|
|
15506
|
+
showProviderSelector && /* @__PURE__ */ React32.createElement(
|
|
15143
15507
|
ProviderSelector,
|
|
15144
15508
|
{
|
|
15145
15509
|
currentProvider: llmProvider,
|
|
@@ -15147,20 +15511,20 @@ var init_App = __esm({
|
|
|
15147
15511
|
onSelect: handleProviderSelect
|
|
15148
15512
|
}
|
|
15149
15513
|
),
|
|
15150
|
-
showAuthDialog && /* @__PURE__ */
|
|
15514
|
+
showAuthDialog && /* @__PURE__ */ React32.createElement(
|
|
15151
15515
|
AuthDialog,
|
|
15152
15516
|
{
|
|
15153
15517
|
onLogin: handleLogin
|
|
15154
15518
|
}
|
|
15155
15519
|
),
|
|
15156
|
-
showFeedbackDialog && /* @__PURE__ */
|
|
15520
|
+
showFeedbackDialog && /* @__PURE__ */ React32.createElement(
|
|
15157
15521
|
FeedbackDialog,
|
|
15158
15522
|
{
|
|
15159
15523
|
onCancel: handleFeedbackCancel,
|
|
15160
15524
|
onSubmit: handleFeedbackSubmit
|
|
15161
15525
|
}
|
|
15162
15526
|
),
|
|
15163
|
-
showFixFlow && apiClient && /* @__PURE__ */
|
|
15527
|
+
showFixFlow && apiClient && /* @__PURE__ */ React32.createElement(
|
|
15164
15528
|
FixFlow,
|
|
15165
15529
|
{
|
|
15166
15530
|
apiClient,
|
|
@@ -15170,7 +15534,7 @@ var init_App = __esm({
|
|
|
15170
15534
|
onStartFix: handleFixStart
|
|
15171
15535
|
}
|
|
15172
15536
|
),
|
|
15173
|
-
showMcpServers && /* @__PURE__ */
|
|
15537
|
+
showMcpServers && /* @__PURE__ */ React32.createElement(
|
|
15174
15538
|
McpServersDisplay,
|
|
15175
15539
|
{
|
|
15176
15540
|
cwd: config2.cwd,
|
|
@@ -15180,8 +15544,8 @@ var init_App = __esm({
|
|
|
15180
15544
|
onTest: handleMcpTest
|
|
15181
15545
|
}
|
|
15182
15546
|
),
|
|
15183
|
-
showMcpAdd && /* @__PURE__ */
|
|
15184
|
-
showMcpSelector && /* @__PURE__ */
|
|
15547
|
+
showMcpAdd && /* @__PURE__ */ React32.createElement(McpAddDialog, { onCancel: handleMcpAddCancel, onConfirm: handleMcpAddConfirm }),
|
|
15548
|
+
showMcpSelector && /* @__PURE__ */ React32.createElement(
|
|
15185
15549
|
McpServerSelector,
|
|
15186
15550
|
{
|
|
15187
15551
|
action: mcpSelectorAction,
|
|
@@ -15190,7 +15554,7 @@ var init_App = __esm({
|
|
|
15190
15554
|
servers: mcpServers
|
|
15191
15555
|
}
|
|
15192
15556
|
),
|
|
15193
|
-
pendingQuestion && /* @__PURE__ */
|
|
15557
|
+
/* @__PURE__ */ React32.createElement(Box29, { flexDirection: "column" }, !showAuthDialog && /* @__PURE__ */ React32.createElement(AuthBanner, { authState }), pendingQuestion && /* @__PURE__ */ React32.createElement(
|
|
15194
15558
|
QuestionSelector,
|
|
15195
15559
|
{
|
|
15196
15560
|
multiSelect: pendingQuestion.multiSelect,
|
|
@@ -15199,8 +15563,14 @@ var init_App = __esm({
|
|
|
15199
15563
|
options: pendingQuestion.options || [],
|
|
15200
15564
|
question: pendingQuestion.question
|
|
15201
15565
|
}
|
|
15202
|
-
),
|
|
15203
|
-
|
|
15566
|
+
), pendingPlanApproval && /* @__PURE__ */ React32.createElement(
|
|
15567
|
+
PlanApprovalSelector,
|
|
15568
|
+
{
|
|
15569
|
+
allowedPrompts: pendingPlanApproval.allowedPrompts,
|
|
15570
|
+
onApprove: handlePlanApprove,
|
|
15571
|
+
onFeedback: handlePlanFeedback
|
|
15572
|
+
}
|
|
15573
|
+
), showInput && !showSessionSelector && !showAuthDialog && !showModelSelector && !showProviderSelector && !showFeedbackDialog && !showFixFlow && !showMcpServers && !showMcpAdd && !showMcpSelector && !pendingQuestion && !pendingPlanApproval && !isLoadingSession && /* @__PURE__ */ React32.createElement(Box29, { flexDirection: "column", marginTop: 0, width: "100%" }, exitWarning && /* @__PURE__ */ React32.createElement(Box29, { marginBottom: 0, paddingX: 1 }, /* @__PURE__ */ React32.createElement(Text27, { color: "yellow" }, exitWarning)), /* @__PURE__ */ React32.createElement(
|
|
15204
15574
|
InputPrompt,
|
|
15205
15575
|
{
|
|
15206
15576
|
currentFolder,
|
|
@@ -15216,7 +15586,7 @@ var init_App = __esm({
|
|
|
15216
15586
|
);
|
|
15217
15587
|
};
|
|
15218
15588
|
App = (props) => {
|
|
15219
|
-
return /* @__PURE__ */
|
|
15589
|
+
return /* @__PURE__ */ React32.createElement(AppContent, { ...props });
|
|
15220
15590
|
};
|
|
15221
15591
|
}
|
|
15222
15592
|
});
|
|
@@ -15246,13 +15616,32 @@ var init_useBracketedPaste = __esm({
|
|
|
15246
15616
|
}
|
|
15247
15617
|
});
|
|
15248
15618
|
|
|
15619
|
+
// src/utils/prompt-routing.ts
|
|
15620
|
+
function getSystemPromptForMode(mode) {
|
|
15621
|
+
switch (mode) {
|
|
15622
|
+
case "plan":
|
|
15623
|
+
return config.planSystemPrompt;
|
|
15624
|
+
case "fix":
|
|
15625
|
+
return config.fixSystemPrompt;
|
|
15626
|
+
case "build":
|
|
15627
|
+
default:
|
|
15628
|
+
return config.interactiveSystemPrompt;
|
|
15629
|
+
}
|
|
15630
|
+
}
|
|
15631
|
+
var init_prompt_routing = __esm({
|
|
15632
|
+
async "src/utils/prompt-routing.ts"() {
|
|
15633
|
+
"use strict";
|
|
15634
|
+
await init_config();
|
|
15635
|
+
}
|
|
15636
|
+
});
|
|
15637
|
+
|
|
15249
15638
|
// src/modes/interactive.tsx
|
|
15250
15639
|
var interactive_exports = {};
|
|
15251
15640
|
__export(interactive_exports, {
|
|
15252
15641
|
runInteractive: () => runInteractive
|
|
15253
15642
|
});
|
|
15254
15643
|
import { render as render2 } from "ink";
|
|
15255
|
-
import
|
|
15644
|
+
import React33, { useEffect as useEffect16, useRef as useRef9 } from "react";
|
|
15256
15645
|
function getToolDescription2(toolName, input) {
|
|
15257
15646
|
switch (toolName) {
|
|
15258
15647
|
case "Read":
|
|
@@ -15385,7 +15774,7 @@ async function runInteractive(config2) {
|
|
|
15385
15774
|
webUrl = session.webUrl;
|
|
15386
15775
|
}
|
|
15387
15776
|
const { unmount, waitUntilExit } = render2(
|
|
15388
|
-
/* @__PURE__ */
|
|
15777
|
+
/* @__PURE__ */ React33.createElement(
|
|
15389
15778
|
InteractiveApp,
|
|
15390
15779
|
{
|
|
15391
15780
|
apiClient,
|
|
@@ -15435,7 +15824,6 @@ var AgentRunner, InteractiveAppContent, InteractiveApp;
|
|
|
15435
15824
|
var init_interactive = __esm({
|
|
15436
15825
|
async "src/modes/interactive.tsx"() {
|
|
15437
15826
|
"use strict";
|
|
15438
|
-
await init_config();
|
|
15439
15827
|
await init_agent();
|
|
15440
15828
|
init_message_bridge();
|
|
15441
15829
|
init_react();
|
|
@@ -15447,6 +15835,7 @@ var init_interactive = __esm({
|
|
|
15447
15835
|
init_mouse();
|
|
15448
15836
|
init_claude_max();
|
|
15449
15837
|
init_logger();
|
|
15838
|
+
await init_prompt_routing();
|
|
15450
15839
|
init_settings_loader();
|
|
15451
15840
|
init_stdio();
|
|
15452
15841
|
init_version();
|
|
@@ -15465,7 +15854,8 @@ var init_interactive = __esm({
|
|
|
15465
15854
|
planFilePath,
|
|
15466
15855
|
selectedModel,
|
|
15467
15856
|
llmProvider,
|
|
15468
|
-
setPendingQuestion
|
|
15857
|
+
setPendingQuestion,
|
|
15858
|
+
setPendingPlanApproval
|
|
15469
15859
|
} = useSession();
|
|
15470
15860
|
const { setUsageStats } = useUsageStats();
|
|
15471
15861
|
const agentRef = useRef9(null);
|
|
@@ -15515,6 +15905,14 @@ var init_interactive = __esm({
|
|
|
15515
15905
|
onEnterPlanMode: () => {
|
|
15516
15906
|
if (isMounted) setAgentMode("plan");
|
|
15517
15907
|
},
|
|
15908
|
+
onPlanApproval: (toolId, allowedPrompts) => {
|
|
15909
|
+
if (isMounted) {
|
|
15910
|
+
setPendingPlanApproval({
|
|
15911
|
+
toolUseId: toolId,
|
|
15912
|
+
allowedPrompts
|
|
15913
|
+
});
|
|
15914
|
+
}
|
|
15915
|
+
},
|
|
15518
15916
|
onTurnComplete: () => {
|
|
15519
15917
|
if (isMounted) onTurnComplete?.();
|
|
15520
15918
|
},
|
|
@@ -15558,7 +15956,7 @@ var init_interactive = __esm({
|
|
|
15558
15956
|
planFilePath,
|
|
15559
15957
|
selectedModel,
|
|
15560
15958
|
oauthToken,
|
|
15561
|
-
systemPromptAppend: agentMode
|
|
15959
|
+
systemPromptAppend: getSystemPromptForMode(agentMode)
|
|
15562
15960
|
};
|
|
15563
15961
|
const agent2 = new CoreAgent(presenter, messageBridge);
|
|
15564
15962
|
agentRef.current = agent2;
|
|
@@ -15605,18 +16003,18 @@ var init_interactive = __esm({
|
|
|
15605
16003
|
setIsAgentRunning
|
|
15606
16004
|
} = useSession();
|
|
15607
16005
|
const { setUsageStats } = useUsageStats();
|
|
15608
|
-
const [sessionId, setSessionId] =
|
|
15609
|
-
const [currentTask, setCurrentTask] =
|
|
15610
|
-
const [taskId, setTaskId] =
|
|
15611
|
-
const [shouldRunAgent, setShouldRunAgent] =
|
|
15612
|
-
const [taskQueue, setTaskQueue] =
|
|
15613
|
-
const [providerSessionId, setProviderSessionId] =
|
|
15614
|
-
const messageBridgeRef =
|
|
15615
|
-
const agentRef =
|
|
15616
|
-
const lastSubmitRef =
|
|
15617
|
-
const [pendingInjected, setPendingInjected] =
|
|
15618
|
-
const pendingInjectedRef =
|
|
15619
|
-
|
|
16006
|
+
const [sessionId, setSessionId] = React33.useState(initialSessionId);
|
|
16007
|
+
const [currentTask, setCurrentTask] = React33.useState(config2.task);
|
|
16008
|
+
const [taskId, setTaskId] = React33.useState(0);
|
|
16009
|
+
const [shouldRunAgent, setShouldRunAgent] = React33.useState(!!config2.task);
|
|
16010
|
+
const [taskQueue, setTaskQueue] = React33.useState([]);
|
|
16011
|
+
const [providerSessionId, setProviderSessionId] = React33.useState();
|
|
16012
|
+
const messageBridgeRef = React33.useRef(null);
|
|
16013
|
+
const agentRef = React33.useRef(null);
|
|
16014
|
+
const lastSubmitRef = React33.useRef(null);
|
|
16015
|
+
const [pendingInjected, setPendingInjected] = React33.useState([]);
|
|
16016
|
+
const pendingInjectedRef = React33.useRef([]);
|
|
16017
|
+
React33.useEffect(() => {
|
|
15620
16018
|
pendingInjectedRef.current = pendingInjected;
|
|
15621
16019
|
}, [pendingInjected]);
|
|
15622
16020
|
const handleSubmitTask = async (task) => {
|
|
@@ -15680,7 +16078,7 @@ var init_interactive = __esm({
|
|
|
15680
16078
|
if (shouldRunAgent && !messageBridgeRef.current) {
|
|
15681
16079
|
messageBridgeRef.current = new MessageBridge(providerSessionId || "");
|
|
15682
16080
|
}
|
|
15683
|
-
|
|
16081
|
+
React33.useEffect(() => {
|
|
15684
16082
|
if (!shouldRunAgent && taskQueue.length > 0) {
|
|
15685
16083
|
const [nextTask, ...remaining] = taskQueue;
|
|
15686
16084
|
setTaskQueue(remaining);
|
|
@@ -15694,26 +16092,35 @@ var init_interactive = __esm({
|
|
|
15694
16092
|
setShouldRunAgent(true);
|
|
15695
16093
|
}
|
|
15696
16094
|
}, [shouldRunAgent, taskQueue, addMessage, providerSessionId]);
|
|
15697
|
-
const handleClearSession =
|
|
16095
|
+
const handleClearSession = React33.useCallback(() => {
|
|
15698
16096
|
setSessionId(void 0);
|
|
15699
16097
|
setContextSessionId(void 0);
|
|
15700
16098
|
setProviderSessionId(void 0);
|
|
15701
16099
|
setTaskQueue([]);
|
|
15702
16100
|
setPendingInjected([]);
|
|
15703
16101
|
}, [setContextSessionId]);
|
|
15704
|
-
const handleQuestionAnswer =
|
|
16102
|
+
const handleQuestionAnswer = React33.useCallback((questionId, answers) => {
|
|
15705
16103
|
if (agentRef.current) {
|
|
15706
16104
|
return agentRef.current.submitAnswer(questionId, answers);
|
|
15707
16105
|
}
|
|
15708
16106
|
return false;
|
|
15709
16107
|
}, []);
|
|
15710
|
-
|
|
16108
|
+
const handlePlanAnswer = React33.useCallback((toolUseId, approved, feedback) => {
|
|
16109
|
+
if (!agentRef.current) return false;
|
|
16110
|
+
if (approved) {
|
|
16111
|
+
return agentRef.current.submitAnswer(toolUseId, { approved: "true" });
|
|
16112
|
+
}
|
|
16113
|
+
const result = agentRef.current.submitAnswer(toolUseId, { feedback: feedback || "User did not approve the plan." });
|
|
16114
|
+
return result;
|
|
16115
|
+
}, []);
|
|
16116
|
+
return /* @__PURE__ */ React33.createElement(React33.Fragment, null, /* @__PURE__ */ React33.createElement(
|
|
15711
16117
|
App,
|
|
15712
16118
|
{
|
|
15713
16119
|
apiClient,
|
|
15714
16120
|
config: { ...config2, task: currentTask },
|
|
15715
16121
|
onClearSession: handleClearSession,
|
|
15716
16122
|
onExit,
|
|
16123
|
+
onPlanAnswer: handlePlanAnswer,
|
|
15717
16124
|
onQuestionAnswer: handleQuestionAnswer,
|
|
15718
16125
|
onResumeSession: async (session) => {
|
|
15719
16126
|
try {
|
|
@@ -15773,7 +16180,7 @@ var init_interactive = __esm({
|
|
|
15773
16180
|
sessionId,
|
|
15774
16181
|
webUrl
|
|
15775
16182
|
}
|
|
15776
|
-
), shouldRunAgent && currentTask && sessionId && messageBridgeRef.current && /* @__PURE__ */
|
|
16183
|
+
), shouldRunAgent && currentTask && sessionId && messageBridgeRef.current && /* @__PURE__ */ React33.createElement(
|
|
15777
16184
|
AgentRunner,
|
|
15778
16185
|
{
|
|
15779
16186
|
apiClient,
|
|
@@ -15801,7 +16208,7 @@ var init_interactive = __esm({
|
|
|
15801
16208
|
useBracketedPaste();
|
|
15802
16209
|
const settings = loadSupatestSettings(props.config.cwd || process.cwd());
|
|
15803
16210
|
const initialProvider = settings.llmProvider || "supatest-managed";
|
|
15804
|
-
return /* @__PURE__ */
|
|
16211
|
+
return /* @__PURE__ */ React33.createElement(KeypressProvider, null, /* @__PURE__ */ React33.createElement(SessionProvider, { initialLlmProvider: initialProvider, initialModel: props.config.selectedModel }, /* @__PURE__ */ React33.createElement(InteractiveAppContent, { ...props })));
|
|
15805
16212
|
};
|
|
15806
16213
|
}
|
|
15807
16214
|
});
|
|
@@ -16392,8 +16799,9 @@ program.name("supatest").description(
|
|
|
16392
16799
|
plan: config.planSystemPrompt
|
|
16393
16800
|
};
|
|
16394
16801
|
logger.raw(getBanner());
|
|
16802
|
+
const headlessTask = headlessMode === "fix" ? `[HEADLESS] ${prompt}` : prompt;
|
|
16395
16803
|
const result = await runAgent({
|
|
16396
|
-
task:
|
|
16804
|
+
task: headlessTask,
|
|
16397
16805
|
logs,
|
|
16398
16806
|
supatestApiKey,
|
|
16399
16807
|
supatestApiUrl,
|