workflow-agent-cli 2.2.0 → 2.2.1
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/LICENSE +21 -0
- package/dist/{chunk-IPMSSOXR.js → chunk-JCDT4363.js} +48 -2
- package/dist/chunk-JCDT4363.js.map +1 -0
- package/dist/cli/index.js +459 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/config/index.d.ts +2 -2
- package/dist/config/index.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/schema-Bdv6BQqI.d.ts +437 -0
- package/dist/scripts/postinstall.js +0 -0
- package/dist/validators/index.d.ts +1 -1
- package/package.json +15 -15
- package/dist/chunk-IPMSSOXR.js.map +0 -1
- package/dist/schema-C1lmnd7L.d.ts +0 -256
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Workflow Agent Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -130,6 +130,50 @@ var CIConfigSchema = z.object({
|
|
|
130
130
|
/** Checks to run in CI pipeline */
|
|
131
131
|
checks: z.array(CICheckSchema).default(["lint", "typecheck", "format", "build", "test"])
|
|
132
132
|
});
|
|
133
|
+
var LLMProviderSchema = z.enum(["anthropic", "openai"]);
|
|
134
|
+
var PipelineConfigSchema = z.object({
|
|
135
|
+
/** Whether auto-heal is enabled for pipeline failures */
|
|
136
|
+
autoHeal: z.boolean().default(false),
|
|
137
|
+
/** Maximum number of retry attempts before giving up */
|
|
138
|
+
maxRetries: z.number().min(1).max(100).default(10),
|
|
139
|
+
/** Base backoff time in minutes (exponential growth) */
|
|
140
|
+
backoffMinutes: z.number().min(1).default(1),
|
|
141
|
+
/** Maximum backoff time in minutes */
|
|
142
|
+
maxBackoffMinutes: z.number().min(1).default(30),
|
|
143
|
+
/** Minimum confidence level required to apply a fix (0-1) */
|
|
144
|
+
minConfidence: z.number().min(0).max(1).default(0.7),
|
|
145
|
+
/** Whether to create a PR for fixes instead of direct commits */
|
|
146
|
+
createPullRequest: z.boolean().default(true),
|
|
147
|
+
/** Branches to monitor for auto-heal (empty = all branches) */
|
|
148
|
+
branches: z.array(z.string()).optional(),
|
|
149
|
+
/** Workflow names to exclude from auto-heal */
|
|
150
|
+
excludeWorkflows: z.array(z.string()).optional()
|
|
151
|
+
});
|
|
152
|
+
var VisualTestingConfigSchema = z.object({
|
|
153
|
+
/** Whether visual testing is enabled */
|
|
154
|
+
enabled: z.boolean().default(false),
|
|
155
|
+
/** LLM provider to use for visual comparison */
|
|
156
|
+
llmProvider: LLMProviderSchema.default("anthropic"),
|
|
157
|
+
/** Directory to store baseline screenshots */
|
|
158
|
+
baselineDir: z.string().default(".visual-baselines"),
|
|
159
|
+
/** Default viewport width */
|
|
160
|
+
viewportWidth: z.number().default(1280),
|
|
161
|
+
/** Default viewport height */
|
|
162
|
+
viewportHeight: z.number().default(720),
|
|
163
|
+
/** Whether to run visual tests on PRs */
|
|
164
|
+
runOnPullRequest: z.boolean().default(true),
|
|
165
|
+
/** Whether to block PR merge on visual differences */
|
|
166
|
+
blockOnDifference: z.boolean().default(false),
|
|
167
|
+
/** URLs to test (can include placeholders like {{baseUrl}}) */
|
|
168
|
+
urls: z.array(
|
|
169
|
+
z.object({
|
|
170
|
+
name: z.string(),
|
|
171
|
+
url: z.string(),
|
|
172
|
+
viewportWidth: z.number().optional(),
|
|
173
|
+
viewportHeight: z.number().optional()
|
|
174
|
+
})
|
|
175
|
+
).optional()
|
|
176
|
+
});
|
|
133
177
|
var WorkflowConfigSchema = z.object({
|
|
134
178
|
projectName: z.string().min(1),
|
|
135
179
|
scopes: z.array(ScopeSchema).min(1),
|
|
@@ -143,7 +187,9 @@ var WorkflowConfigSchema = z.object({
|
|
|
143
187
|
hooks: HooksConfigSchema.optional(),
|
|
144
188
|
guidelines: GuidelinesConfigSchema.optional(),
|
|
145
189
|
reservedScopeNames: z.array(z.string()).optional().default(DEFAULT_RESERVED_SCOPE_NAMES),
|
|
146
|
-
ci: CIConfigSchema.optional()
|
|
190
|
+
ci: CIConfigSchema.optional(),
|
|
191
|
+
pipeline: PipelineConfigSchema.optional(),
|
|
192
|
+
visualTesting: VisualTestingConfigSchema.optional()
|
|
147
193
|
}).superRefine((config, ctx) => {
|
|
148
194
|
const reservedNames = config.reservedScopeNames || DEFAULT_RESERVED_SCOPE_NAMES;
|
|
149
195
|
config.scopes.forEach((scope, index) => {
|
|
@@ -290,4 +336,4 @@ export {
|
|
|
290
336
|
validateConfig,
|
|
291
337
|
hasConfig
|
|
292
338
|
};
|
|
293
|
-
//# sourceMappingURL=chunk-
|
|
339
|
+
//# sourceMappingURL=chunk-JCDT4363.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config/index.ts","../src/config/schema.ts"],"sourcesContent":["import { cosmiconfig } from \"cosmiconfig\";\nimport {\n WorkflowConfig,\n WorkflowConfigSchema,\n validateScopeDefinitions,\n} from \"./schema.js\";\nimport { join } from \"path\";\nimport { existsSync } from \"fs\";\nimport { z } from \"zod\";\n\nconst explorer = cosmiconfig(\"workflow\", {\n searchPlaces: [\n \"workflow.config.ts\",\n \"workflow.config.js\",\n \"workflow.config.json\",\n \".workflowrc\",\n \".workflowrc.json\",\n \"package.json\",\n ],\n});\n\nexport async function loadConfig(\n cwd: string = process.cwd(),\n): Promise<WorkflowConfig | null> {\n try {\n const result = await explorer.search(cwd);\n\n if (!result || !result.config) {\n return null;\n }\n\n // Validate config against schema\n const validated = WorkflowConfigSchema.parse(result.config);\n return validated;\n } catch (error) {\n if (error instanceof z.ZodError) {\n // Format Zod errors to be more user-friendly\n const result = await explorer.search(cwd);\n const formattedErrors = error.errors\n .map((err) => {\n const path = err.path.join(\".\");\n\n // If error is in scopes array, show the scope name\n if (err.path[0] === \"scopes\" && typeof err.path[1] === \"number\") {\n const scopeIndex = err.path[1];\n const scopeName =\n result?.config?.scopes?.[scopeIndex]?.name ||\n `scope at index ${scopeIndex}`;\n const field = err.path[2] || \"definition\";\n\n // Add helpful suggestions for common errors\n let message = err.message;\n if (message.includes(\"reserved word\")) {\n const reservedMatch = message.match(\n /Scope name \"([^\"]+)\" is reserved/,\n );\n if (reservedMatch) {\n const suggestions: Record<string, string> = {\n docs: \"documentation\",\n test: \"testing\",\n config: \"configuration\",\n build: \"builds\",\n ci: \"cicd\",\n deps: \"dependencies\",\n };\n const badName = reservedMatch[1];\n const suggestion = suggestions[badName] || `${badName}-scope`;\n message = `${message}. Try renaming to \"${suggestion}\"`;\n }\n }\n\n return field === \"definition\"\n ? `Scope \"${scopeName}\": ${message}`\n : `Scope \"${scopeName}\" ${field}: ${message}`;\n }\n\n return `${path}: ${err.message}`;\n })\n .join(\"\\n • \");\n\n throw new Error(\n `Invalid workflow configuration:\\n • ${formattedErrors}\\n\\n💡 Fix these issues in workflow.config.json or run: workflow config validate`,\n );\n }\n\n if (error instanceof Error) {\n throw new Error(`Failed to load workflow config: ${error.message}`);\n }\n throw error;\n }\n}\n\nexport async function validateConfig(cwd: string = process.cwd()): Promise<{\n valid: boolean;\n errors: string[];\n warnings: string[];\n}> {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n try {\n const config = await loadConfig(cwd);\n if (!config) {\n errors.push(\"No configuration file found\");\n return { valid: false, errors, warnings };\n }\n\n // Additional validation beyond schema\n const scopeValidation = validateScopeDefinitions(config.scopes);\n errors.push(...scopeValidation.errors);\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n };\n } catch (error) {\n errors.push(error instanceof Error ? error.message : String(error));\n return { valid: false, errors, warnings };\n }\n}\n\nexport function hasConfig(cwd: string = process.cwd()): boolean {\n const configPaths = [\n \"workflow.config.ts\",\n \"workflow.config.js\",\n \"workflow.config.json\",\n \".workflowrc\",\n \".workflowrc.json\",\n ];\n\n return configPaths.some((path) => existsSync(join(cwd, path)));\n}\n\nexport {\n WorkflowConfig,\n WorkflowConfigSchema,\n Scope,\n BranchType,\n ConventionalType,\n validateScopeName,\n DEFAULT_RESERVED_SCOPE_NAMES,\n} from \"./schema.js\";\n","import { z } from \"zod\";\n\n// Default reserved scope names that cannot be used\nexport const DEFAULT_RESERVED_SCOPE_NAMES = [\n \"init\",\n \"create\",\n \"build\",\n \"test\",\n \"config\",\n \"docs\",\n \"ci\",\n \"deps\",\n];\n\n/**\n * Validates a scope name against reserved words and naming rules\n */\nexport function validateScopeName(\n name: string,\n reservedNames: string[] = DEFAULT_RESERVED_SCOPE_NAMES,\n): {\n valid: boolean;\n error?: string;\n suggestion?: string;\n} {\n if (reservedNames.includes(name)) {\n // Provide suggestions for common reserved words\n const suggestions: Record<string, string> = {\n docs: \"documentation\",\n test: \"testing\",\n config: \"configuration\",\n build: \"builds\",\n ci: \"cicd\",\n deps: \"dependencies\",\n };\n\n return {\n valid: false,\n error: `Scope name \"${name}\" is reserved`,\n suggestion: suggestions[name] || `${name}-scope`,\n };\n }\n\n if (!/^[a-z0-9-]+$/.test(name)) {\n return {\n valid: false,\n error: \"Scope name must be lowercase alphanumeric with hyphens\",\n };\n }\n\n if (name.length === 0 || name.length > 32) {\n return {\n valid: false,\n error: \"Scope name must be 1-32 characters\",\n };\n }\n\n return { valid: true };\n}\n\nexport const BranchTypeSchema = z.enum([\n \"feature\",\n \"bugfix\",\n \"hotfix\",\n \"chore\",\n \"refactor\",\n \"docs\",\n \"test\",\n \"release\",\n]);\n\nexport const ConventionalTypeSchema = z.enum([\n \"feat\",\n \"fix\",\n \"refactor\",\n \"chore\",\n \"docs\",\n \"test\",\n \"perf\",\n \"style\",\n \"ci\",\n \"build\",\n \"revert\",\n]);\n\nexport const ScopeSchema = z.object({\n name: z\n .string()\n .min(1)\n .max(32, \"Scope name must be 32 characters or less\")\n .regex(\n /^[a-z0-9-]+$/,\n \"Scope name must be lowercase alphanumeric with hyphens\",\n ),\n description: z\n .string()\n .min(10, \"Scope description must be at least 10 characters\"),\n allowedTypes: z.array(ConventionalTypeSchema).optional(),\n mandatoryGuidelines: z.array(z.string()).optional(),\n emoji: z.string().optional(),\n category: z\n .enum([\n \"auth\",\n \"features\",\n \"infrastructure\",\n \"documentation\",\n \"testing\",\n \"performance\",\n \"other\",\n ])\n .optional(),\n});\n\nexport const EnforcementLevelSchema = z.enum([\n \"strict\",\n \"advisory\",\n \"learning\",\n]);\n\nexport const AnalyticsConfigSchema = z.object({\n enabled: z.boolean().default(false),\n shareAnonymous: z.boolean().default(false),\n});\n\n// Pre-commit hook check types\nexport const HookCheckSchema = z.enum([\n \"validate-branch\",\n \"validate-commit\",\n \"check-guidelines\",\n \"validate-scopes\",\n]);\n\n// Git hooks configuration\nexport const HooksConfigSchema = z.object({\n /** Whether hooks are enabled */\n enabled: z.boolean().default(true),\n /** Checks to run on pre-commit */\n preCommit: z\n .array(HookCheckSchema)\n .default([\"validate-branch\", \"check-guidelines\"]),\n /** Checks to run on commit-msg */\n commitMsg: z.array(HookCheckSchema).default([\"validate-commit\"]),\n});\n\n// Guidelines configuration with mandatory templates and user overrides\nexport const GuidelinesConfigSchema = z.object({\n /** Additional templates to make mandatory (beyond the core set) */\n additionalMandatory: z.array(z.string()).optional(),\n /** Templates to make optional (override core mandatory templates) */\n optionalOverrides: z.array(z.string()).optional(),\n});\n\n// CI provider types\nexport const CIProviderSchema = z.enum([\"github\", \"gitlab\", \"bitbucket\"]);\n\n// CI check types\nexport const CICheckSchema = z.enum([\n \"lint\",\n \"typecheck\",\n \"format\",\n \"test\",\n \"build\",\n]);\n\n// CI/CD configuration\nexport const CIConfigSchema = z.object({\n /** Whether CI setup is enabled */\n enabled: z.boolean().default(true),\n /** CI provider (currently only github supported) */\n provider: CIProviderSchema.default(\"github\"),\n /** Checks to run in CI pipeline */\n checks: z\n .array(CICheckSchema)\n .default([\"lint\", \"typecheck\", \"format\", \"build\", \"test\"]),\n});\n\n// LLM provider types\nexport const LLMProviderSchema = z.enum([\"anthropic\", \"openai\"]);\n\n// Pipeline auto-heal configuration\nexport const PipelineConfigSchema = z.object({\n /** Whether auto-heal is enabled for pipeline failures */\n autoHeal: z.boolean().default(false),\n /** Maximum number of retry attempts before giving up */\n maxRetries: z.number().min(1).max(100).default(10),\n /** Base backoff time in minutes (exponential growth) */\n backoffMinutes: z.number().min(1).default(1),\n /** Maximum backoff time in minutes */\n maxBackoffMinutes: z.number().min(1).default(30),\n /** Minimum confidence level required to apply a fix (0-1) */\n minConfidence: z.number().min(0).max(1).default(0.7),\n /** Whether to create a PR for fixes instead of direct commits */\n createPullRequest: z.boolean().default(true),\n /** Branches to monitor for auto-heal (empty = all branches) */\n branches: z.array(z.string()).optional(),\n /** Workflow names to exclude from auto-heal */\n excludeWorkflows: z.array(z.string()).optional(),\n});\n\n// Visual testing configuration\nexport const VisualTestingConfigSchema = z.object({\n /** Whether visual testing is enabled */\n enabled: z.boolean().default(false),\n /** LLM provider to use for visual comparison */\n llmProvider: LLMProviderSchema.default(\"anthropic\"),\n /** Directory to store baseline screenshots */\n baselineDir: z.string().default(\".visual-baselines\"),\n /** Default viewport width */\n viewportWidth: z.number().default(1280),\n /** Default viewport height */\n viewportHeight: z.number().default(720),\n /** Whether to run visual tests on PRs */\n runOnPullRequest: z.boolean().default(true),\n /** Whether to block PR merge on visual differences */\n blockOnDifference: z.boolean().default(false),\n /** URLs to test (can include placeholders like {{baseUrl}}) */\n urls: z\n .array(\n z.object({\n name: z.string(),\n url: z.string(),\n viewportWidth: z.number().optional(),\n viewportHeight: z.number().optional(),\n }),\n )\n .optional(),\n});\n\nexport const WorkflowConfigSchema = z\n .object({\n projectName: z.string().min(1),\n scopes: z.array(ScopeSchema).min(1),\n branchTypes: z.array(BranchTypeSchema).optional(),\n conventionalTypes: z.array(ConventionalTypeSchema).optional(),\n enforcement: EnforcementLevelSchema.default(\"strict\"),\n language: z.string().default(\"en\"),\n analytics: AnalyticsConfigSchema.optional(),\n adapter: z.string().optional(),\n syncRemote: z.string().optional(),\n hooks: HooksConfigSchema.optional(),\n guidelines: GuidelinesConfigSchema.optional(),\n reservedScopeNames: z\n .array(z.string())\n .optional()\n .default(DEFAULT_RESERVED_SCOPE_NAMES),\n ci: CIConfigSchema.optional(),\n pipeline: PipelineConfigSchema.optional(),\n visualTesting: VisualTestingConfigSchema.optional(),\n })\n .superRefine((config, ctx) => {\n // Validate scopes against reserved names\n const reservedNames =\n config.reservedScopeNames || DEFAULT_RESERVED_SCOPE_NAMES;\n\n config.scopes.forEach((scope, index) => {\n const validation = validateScopeName(scope.name, reservedNames);\n if (!validation.valid) {\n let message = validation.error || \"Invalid scope name\";\n if (validation.suggestion) {\n message += `. Try renaming to \"${validation.suggestion}\"`;\n }\n\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [\"scopes\", index, \"name\"],\n message,\n });\n }\n });\n });\n\nexport type Scope = z.infer<typeof ScopeSchema>;\nexport type BranchType = z.infer<typeof BranchTypeSchema>;\nexport type ConventionalType = z.infer<typeof ConventionalTypeSchema>;\nexport type EnforcementLevel = z.infer<typeof EnforcementLevelSchema>;\nexport type AnalyticsConfig = z.infer<typeof AnalyticsConfigSchema>;\nexport type HookCheck = z.infer<typeof HookCheckSchema>;\nexport type HooksConfig = z.infer<typeof HooksConfigSchema>;\nexport type GuidelinesConfig = z.infer<typeof GuidelinesConfigSchema>;\nexport type CIProvider = z.infer<typeof CIProviderSchema>;\nexport type CICheck = z.infer<typeof CICheckSchema>;\nexport type CIConfig = z.infer<typeof CIConfigSchema>;\nexport type LLMProvider = z.infer<typeof LLMProviderSchema>;\nexport type PipelineConfig = z.infer<typeof PipelineConfigSchema>;\nexport type VisualTestingConfig = z.infer<typeof VisualTestingConfigSchema>;\nexport type WorkflowConfig = z.infer<typeof WorkflowConfigSchema>;\n\nexport const defaultBranchTypes: BranchType[] = [\n \"feature\",\n \"bugfix\",\n \"hotfix\",\n \"chore\",\n \"refactor\",\n \"docs\",\n \"test\",\n];\n\nexport const defaultConventionalTypes: ConventionalType[] = [\n \"feat\",\n \"fix\",\n \"refactor\",\n \"chore\",\n \"docs\",\n \"test\",\n \"perf\",\n \"style\",\n];\n\n/**\n * Validates scope definitions for duplicates, description quality, and category values\n * @param scopes Array of scope definitions to validate\n * @returns Object with validation result and error messages\n */\nexport function validateScopeDefinitions(scopes: Scope[]): {\n valid: boolean;\n errors: string[];\n} {\n const errors: string[] = [];\n const seenNames = new Set<string>();\n\n for (const scope of scopes) {\n // Check for duplicate names\n if (seenNames.has(scope.name)) {\n errors.push(`Duplicate scope name: \"${scope.name}\"`);\n }\n seenNames.add(scope.name);\n\n // Validate using schema (this will catch min length, reserved names, etc.)\n const result = ScopeSchema.safeParse(scope);\n if (!result.success) {\n result.error.errors.forEach((err) => {\n errors.push(`Scope \"${scope.name}\": ${err.message}`);\n });\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n}\n"],"mappings":"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","names":["z"]}
|
package/dist/cli/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
validateConfig,
|
|
12
12
|
validateScopeDefinitions,
|
|
13
13
|
validateScopeName
|
|
14
|
-
} from "../chunk-
|
|
14
|
+
} from "../chunk-JCDT4363.js";
|
|
15
15
|
|
|
16
16
|
// src/cli/index.ts
|
|
17
17
|
import { Command as Command2 } from "commander";
|
|
@@ -3006,6 +3006,443 @@ async function checkAction(cwd) {
|
|
|
3006
3006
|
}
|
|
3007
3007
|
}
|
|
3008
3008
|
|
|
3009
|
+
// src/cli/commands/fix.ts
|
|
3010
|
+
import { existsSync as existsSync10, readFileSync as readFileSync2, writeFileSync as writeFileSync3, mkdirSync } from "fs";
|
|
3011
|
+
import { dirname as dirname2, join as join10, relative } from "path";
|
|
3012
|
+
import { execa as execa3 } from "execa";
|
|
3013
|
+
import * as p7 from "@clack/prompts";
|
|
3014
|
+
import pc from "picocolors";
|
|
3015
|
+
function extractFilePaths(errorMessage, cwd) {
|
|
3016
|
+
const paths = /* @__PURE__ */ new Set();
|
|
3017
|
+
const patterns = [
|
|
3018
|
+
/(?:at\s+)?([\/\w\.\-]+\.(?:ts|js|tsx|jsx|mjs|cjs|vue|svelte))(?::\d+(?::\d+)?)?/g,
|
|
3019
|
+
/(?:Error in |from )\.?([\/\w\.\-]+\.(?:ts|js|tsx|jsx|mjs|cjs|vue|svelte))/g,
|
|
3020
|
+
/['"]([\/\w\.\-]+\.(?:ts|js|tsx|jsx|mjs|cjs|vue|svelte))['"]/g
|
|
3021
|
+
];
|
|
3022
|
+
for (const pattern of patterns) {
|
|
3023
|
+
let match;
|
|
3024
|
+
while ((match = pattern.exec(errorMessage)) !== null) {
|
|
3025
|
+
let filePath = match[1];
|
|
3026
|
+
if (!filePath.startsWith("/")) {
|
|
3027
|
+
filePath = join10(cwd, filePath);
|
|
3028
|
+
}
|
|
3029
|
+
if (existsSync10(filePath)) {
|
|
3030
|
+
paths.add(filePath);
|
|
3031
|
+
}
|
|
3032
|
+
}
|
|
3033
|
+
}
|
|
3034
|
+
return Array.from(paths);
|
|
3035
|
+
}
|
|
3036
|
+
function readFileContents(paths) {
|
|
3037
|
+
const contents = {};
|
|
3038
|
+
for (const filePath of paths) {
|
|
3039
|
+
try {
|
|
3040
|
+
if (existsSync10(filePath)) {
|
|
3041
|
+
contents[filePath] = readFileSync2(filePath, "utf-8");
|
|
3042
|
+
}
|
|
3043
|
+
} catch {
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
return contents;
|
|
3047
|
+
}
|
|
3048
|
+
async function generateFixWithLLM(errorMessage, _fileContents, _context) {
|
|
3049
|
+
console.log(pc.dim(" Analyzing error with LLM..."));
|
|
3050
|
+
return {
|
|
3051
|
+
analysis: `Error analysis for: ${errorMessage.slice(0, 100)}...`,
|
|
3052
|
+
rootCause: "Unable to determine root cause without LLM API access",
|
|
3053
|
+
suggestedFix: {
|
|
3054
|
+
description: "Manual intervention required - LLM API not configured",
|
|
3055
|
+
files: []
|
|
3056
|
+
},
|
|
3057
|
+
confidence: 0
|
|
3058
|
+
};
|
|
3059
|
+
}
|
|
3060
|
+
async function applyChanges(changes, dryRun) {
|
|
3061
|
+
for (const change of changes) {
|
|
3062
|
+
const actionColor = change.action === "create" ? pc.green : change.action === "delete" ? pc.red : pc.yellow;
|
|
3063
|
+
console.log(` ${actionColor(change.action.toUpperCase())} ${change.path}`);
|
|
3064
|
+
if (dryRun) {
|
|
3065
|
+
console.log(pc.dim(" (dry run - no changes made)"));
|
|
3066
|
+
continue;
|
|
3067
|
+
}
|
|
3068
|
+
switch (change.action) {
|
|
3069
|
+
case "create":
|
|
3070
|
+
case "modify":
|
|
3071
|
+
if (change.content) {
|
|
3072
|
+
const dir = dirname2(change.path);
|
|
3073
|
+
if (!existsSync10(dir)) {
|
|
3074
|
+
mkdirSync(dir, { recursive: true });
|
|
3075
|
+
}
|
|
3076
|
+
writeFileSync3(change.path, change.content);
|
|
3077
|
+
}
|
|
3078
|
+
break;
|
|
3079
|
+
case "delete":
|
|
3080
|
+
console.log(pc.dim(" (delete skipped for safety)"));
|
|
3081
|
+
break;
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
async function commitAndPush(message, cwd) {
|
|
3086
|
+
try {
|
|
3087
|
+
await execa3("git", ["add", "-A"], { cwd });
|
|
3088
|
+
const { stdout: status } = await execa3(
|
|
3089
|
+
"git",
|
|
3090
|
+
["status", "--porcelain"],
|
|
3091
|
+
{ cwd }
|
|
3092
|
+
);
|
|
3093
|
+
if (!status.trim()) {
|
|
3094
|
+
return { success: true };
|
|
3095
|
+
}
|
|
3096
|
+
await execa3("git", ["commit", "-m", message], { cwd });
|
|
3097
|
+
await execa3("git", ["push"], { cwd });
|
|
3098
|
+
return { success: true };
|
|
3099
|
+
} catch (error) {
|
|
3100
|
+
return {
|
|
3101
|
+
success: false,
|
|
3102
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3103
|
+
};
|
|
3104
|
+
}
|
|
3105
|
+
}
|
|
3106
|
+
async function fixCommand(options) {
|
|
3107
|
+
const cwd = process.cwd();
|
|
3108
|
+
console.log(pc.cyan("\n\u{1F527} Auto-Heal: Fixing Pipeline Error\n"));
|
|
3109
|
+
if (!options.error) {
|
|
3110
|
+
console.error(pc.red("Error: --error flag is required"));
|
|
3111
|
+
process.exit(1);
|
|
3112
|
+
}
|
|
3113
|
+
console.log(pc.bold("Error Message:"));
|
|
3114
|
+
console.log(pc.dim(options.error.slice(0, 500)));
|
|
3115
|
+
if (options.error.length > 500) {
|
|
3116
|
+
console.log(pc.dim(`... (${options.error.length - 500} more characters)`));
|
|
3117
|
+
}
|
|
3118
|
+
console.log("");
|
|
3119
|
+
let filePaths = options.files || [];
|
|
3120
|
+
if (filePaths.length === 0) {
|
|
3121
|
+
console.log(pc.dim("Extracting file paths from error..."));
|
|
3122
|
+
filePaths = extractFilePaths(options.error, cwd);
|
|
3123
|
+
}
|
|
3124
|
+
if (filePaths.length > 0) {
|
|
3125
|
+
console.log(pc.bold("\nRelevant Files:"));
|
|
3126
|
+
for (const path2 of filePaths) {
|
|
3127
|
+
console.log(` \u{1F4C4} ${relative(cwd, path2)}`);
|
|
3128
|
+
}
|
|
3129
|
+
console.log("");
|
|
3130
|
+
}
|
|
3131
|
+
const fileContents = readFileContents(filePaths);
|
|
3132
|
+
let context;
|
|
3133
|
+
if (options.context) {
|
|
3134
|
+
try {
|
|
3135
|
+
context = JSON.parse(options.context);
|
|
3136
|
+
} catch {
|
|
3137
|
+
context = options.context;
|
|
3138
|
+
}
|
|
3139
|
+
}
|
|
3140
|
+
console.log(pc.bold("Generating Fix...\n"));
|
|
3141
|
+
const fix = await generateFixWithLLM(options.error, fileContents, context);
|
|
3142
|
+
console.log(pc.bold("Analysis:"));
|
|
3143
|
+
console.log(` ${fix.analysis}`);
|
|
3144
|
+
console.log("");
|
|
3145
|
+
console.log(pc.bold("Root Cause:"));
|
|
3146
|
+
console.log(` ${fix.rootCause}`);
|
|
3147
|
+
console.log("");
|
|
3148
|
+
console.log(pc.bold("Suggested Fix:"));
|
|
3149
|
+
console.log(` ${fix.suggestedFix.description}`);
|
|
3150
|
+
console.log(` Confidence: ${Math.round(fix.confidence * 100)}%`);
|
|
3151
|
+
console.log("");
|
|
3152
|
+
if (fix.suggestedFix.files.length === 0) {
|
|
3153
|
+
console.log(
|
|
3154
|
+
pc.yellow("\u26A0\uFE0F No automatic fix available. Manual intervention required.")
|
|
3155
|
+
);
|
|
3156
|
+
process.exit(1);
|
|
3157
|
+
}
|
|
3158
|
+
if (fix.confidence < 0.5) {
|
|
3159
|
+
console.log(
|
|
3160
|
+
pc.yellow(
|
|
3161
|
+
`\u26A0\uFE0F Low confidence (${Math.round(fix.confidence * 100)}%). Skipping automatic fix.`
|
|
3162
|
+
)
|
|
3163
|
+
);
|
|
3164
|
+
process.exit(1);
|
|
3165
|
+
}
|
|
3166
|
+
if (!options.auto) {
|
|
3167
|
+
const shouldApply = await p7.confirm({
|
|
3168
|
+
message: "Apply suggested changes?",
|
|
3169
|
+
initialValue: false
|
|
3170
|
+
});
|
|
3171
|
+
if (p7.isCancel(shouldApply) || !shouldApply) {
|
|
3172
|
+
console.log(pc.dim("Fix cancelled."));
|
|
3173
|
+
process.exit(0);
|
|
3174
|
+
}
|
|
3175
|
+
}
|
|
3176
|
+
console.log(pc.bold("\nApplying Changes...\n"));
|
|
3177
|
+
await applyChanges(fix.suggestedFix.files, options.dryRun || false);
|
|
3178
|
+
if (options.dryRun) {
|
|
3179
|
+
console.log(pc.yellow("\n\u26A0\uFE0F Dry run complete. No changes were made."));
|
|
3180
|
+
process.exit(0);
|
|
3181
|
+
}
|
|
3182
|
+
if (options.auto) {
|
|
3183
|
+
console.log(pc.bold("\nCommitting and Pushing...\n"));
|
|
3184
|
+
const scope = filePaths.length > 0 ? dirname2(relative(cwd, filePaths[0])).split("/")[0] || "core" : "core";
|
|
3185
|
+
const commitMessage = `fix(${scope}): auto-heal pipeline failure
|
|
3186
|
+
|
|
3187
|
+
${fix.rootCause}
|
|
3188
|
+
|
|
3189
|
+
Auto-generated fix with ${Math.round(fix.confidence * 100)}% confidence.
|
|
3190
|
+
`;
|
|
3191
|
+
const result = await commitAndPush(commitMessage, cwd);
|
|
3192
|
+
if (result.success) {
|
|
3193
|
+
console.log(pc.green("\u2705 Fix applied, committed, and pushed!"));
|
|
3194
|
+
} else {
|
|
3195
|
+
console.log(pc.red(`\u274C Failed to commit/push: ${result.error}`));
|
|
3196
|
+
process.exit(1);
|
|
3197
|
+
}
|
|
3198
|
+
} else {
|
|
3199
|
+
console.log(pc.green("\n\u2705 Fix applied! Don't forget to commit and push."));
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
|
|
3203
|
+
// src/cli/commands/visual.ts
|
|
3204
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
3205
|
+
import { dirname as dirname3, join as join11 } from "path";
|
|
3206
|
+
import * as p8 from "@clack/prompts";
|
|
3207
|
+
import pc2 from "picocolors";
|
|
3208
|
+
function getBaselinesDir() {
|
|
3209
|
+
const cwd = process.cwd();
|
|
3210
|
+
return join11(cwd, ".visual-baselines");
|
|
3211
|
+
}
|
|
3212
|
+
function getMetadataPath() {
|
|
3213
|
+
return join11(getBaselinesDir(), "baselines.json");
|
|
3214
|
+
}
|
|
3215
|
+
function loadMetadata() {
|
|
3216
|
+
const metadataPath = getMetadataPath();
|
|
3217
|
+
if (!existsSync11(metadataPath)) {
|
|
3218
|
+
return {};
|
|
3219
|
+
}
|
|
3220
|
+
try {
|
|
3221
|
+
return JSON.parse(readFileSync3(metadataPath, "utf-8"));
|
|
3222
|
+
} catch {
|
|
3223
|
+
return {};
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3226
|
+
function saveMetadata(metadata) {
|
|
3227
|
+
const dir = getBaselinesDir();
|
|
3228
|
+
if (!existsSync11(dir)) {
|
|
3229
|
+
mkdirSync2(dir, { recursive: true });
|
|
3230
|
+
}
|
|
3231
|
+
writeFileSync4(getMetadataPath(), JSON.stringify(metadata, null, 2));
|
|
3232
|
+
}
|
|
3233
|
+
async function checkPlaywright() {
|
|
3234
|
+
try {
|
|
3235
|
+
await import("playwright");
|
|
3236
|
+
return true;
|
|
3237
|
+
} catch {
|
|
3238
|
+
return false;
|
|
3239
|
+
}
|
|
3240
|
+
}
|
|
3241
|
+
async function visualCaptureCommand(name, url, options) {
|
|
3242
|
+
console.log(pc2.cyan("\n\u{1F4F8} Capturing Visual Baseline\n"));
|
|
3243
|
+
const hasPlaywright = await checkPlaywright();
|
|
3244
|
+
if (!hasPlaywright) {
|
|
3245
|
+
console.log(pc2.yellow("\u26A0\uFE0F Playwright is not installed."));
|
|
3246
|
+
console.log(pc2.dim("Install it with: npm install playwright"));
|
|
3247
|
+
console.log(
|
|
3248
|
+
pc2.dim("Then install browsers: npx playwright install chromium")
|
|
3249
|
+
);
|
|
3250
|
+
process.exit(1);
|
|
3251
|
+
}
|
|
3252
|
+
const width = parseInt(options.width || "1280");
|
|
3253
|
+
const height = parseInt(options.height || "720");
|
|
3254
|
+
const delay = parseInt(options.delay || "0");
|
|
3255
|
+
console.log(` Name: ${pc2.bold(name)}`);
|
|
3256
|
+
console.log(` URL: ${url}`);
|
|
3257
|
+
console.log(` Viewport: ${width}x${height}`);
|
|
3258
|
+
console.log("");
|
|
3259
|
+
try {
|
|
3260
|
+
const { chromium } = await import("playwright");
|
|
3261
|
+
const browser = await chromium.launch({ headless: true });
|
|
3262
|
+
const context = await browser.newContext({
|
|
3263
|
+
viewport: { width, height }
|
|
3264
|
+
});
|
|
3265
|
+
const page = await context.newPage();
|
|
3266
|
+
console.log(pc2.dim(" Loading page..."));
|
|
3267
|
+
await page.goto(url, { waitUntil: "networkidle" });
|
|
3268
|
+
if (options.waitFor) {
|
|
3269
|
+
console.log(pc2.dim(` Waiting for selector: ${options.waitFor}`));
|
|
3270
|
+
await page.waitForSelector(options.waitFor, { timeout: 1e4 });
|
|
3271
|
+
}
|
|
3272
|
+
if (delay > 0) {
|
|
3273
|
+
console.log(pc2.dim(` Waiting ${delay}ms...`));
|
|
3274
|
+
await page.waitForTimeout(delay);
|
|
3275
|
+
}
|
|
3276
|
+
const baselinesDir = getBaselinesDir();
|
|
3277
|
+
const outputPath = options.output || join11(baselinesDir, "screenshots", `${name}.png`);
|
|
3278
|
+
const outputDir = dirname3(outputPath);
|
|
3279
|
+
if (!existsSync11(outputDir)) {
|
|
3280
|
+
mkdirSync2(outputDir, { recursive: true });
|
|
3281
|
+
}
|
|
3282
|
+
console.log(pc2.dim(" Capturing screenshot..."));
|
|
3283
|
+
await page.screenshot({
|
|
3284
|
+
path: outputPath,
|
|
3285
|
+
fullPage: options.fullPage
|
|
3286
|
+
});
|
|
3287
|
+
await browser.close();
|
|
3288
|
+
const metadata = loadMetadata();
|
|
3289
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3290
|
+
metadata[name] = {
|
|
3291
|
+
name,
|
|
3292
|
+
url,
|
|
3293
|
+
path: outputPath,
|
|
3294
|
+
width,
|
|
3295
|
+
height,
|
|
3296
|
+
createdAt: metadata[name]?.createdAt || now,
|
|
3297
|
+
updatedAt: now
|
|
3298
|
+
};
|
|
3299
|
+
saveMetadata(metadata);
|
|
3300
|
+
console.log(pc2.green(`
|
|
3301
|
+
\u2705 Baseline "${name}" saved to ${outputPath}`));
|
|
3302
|
+
} catch (error) {
|
|
3303
|
+
console.error(pc2.red("\n\u274C Failed to capture screenshot:"), error);
|
|
3304
|
+
process.exit(1);
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
async function visualCompareCommand(url, options) {
|
|
3308
|
+
console.log(pc2.cyan("\n\u{1F50D} Comparing Against Baseline\n"));
|
|
3309
|
+
const metadata = loadMetadata();
|
|
3310
|
+
const baseline = metadata[options.baseline];
|
|
3311
|
+
if (!baseline) {
|
|
3312
|
+
console.log(pc2.red(`\u274C Baseline "${options.baseline}" not found`));
|
|
3313
|
+
console.log(pc2.dim("Available baselines:"));
|
|
3314
|
+
Object.keys(metadata).forEach((name) => {
|
|
3315
|
+
console.log(pc2.dim(` - ${name}`));
|
|
3316
|
+
});
|
|
3317
|
+
process.exit(1);
|
|
3318
|
+
}
|
|
3319
|
+
if (!existsSync11(baseline.path)) {
|
|
3320
|
+
console.log(pc2.red(`\u274C Baseline screenshot not found: ${baseline.path}`));
|
|
3321
|
+
process.exit(1);
|
|
3322
|
+
}
|
|
3323
|
+
console.log(` Baseline: ${pc2.bold(options.baseline)}`);
|
|
3324
|
+
console.log(` URL: ${url}`);
|
|
3325
|
+
console.log(` Viewport: ${baseline.width}x${baseline.height}`);
|
|
3326
|
+
console.log("");
|
|
3327
|
+
const hasPlaywright = await checkPlaywright();
|
|
3328
|
+
if (!hasPlaywright) {
|
|
3329
|
+
console.log(pc2.yellow("\u26A0\uFE0F Playwright is not installed."));
|
|
3330
|
+
process.exit(1);
|
|
3331
|
+
}
|
|
3332
|
+
try {
|
|
3333
|
+
const { chromium } = await import("playwright");
|
|
3334
|
+
const browser = await chromium.launch({ headless: true });
|
|
3335
|
+
const context = await browser.newContext({
|
|
3336
|
+
viewport: { width: baseline.width, height: baseline.height }
|
|
3337
|
+
});
|
|
3338
|
+
const page = await context.newPage();
|
|
3339
|
+
console.log(pc2.dim(" Loading page..."));
|
|
3340
|
+
await page.goto(url, { waitUntil: "networkidle" });
|
|
3341
|
+
const baselinesDir = getBaselinesDir();
|
|
3342
|
+
const comparePath = options.output || join11(
|
|
3343
|
+
baselinesDir,
|
|
3344
|
+
"comparisons",
|
|
3345
|
+
`${options.baseline}-${Date.now()}.png`
|
|
3346
|
+
);
|
|
3347
|
+
const compareDir = dirname3(comparePath);
|
|
3348
|
+
if (!existsSync11(compareDir)) {
|
|
3349
|
+
mkdirSync2(compareDir, { recursive: true });
|
|
3350
|
+
}
|
|
3351
|
+
console.log(pc2.dim(" Capturing screenshot..."));
|
|
3352
|
+
await page.screenshot({ path: comparePath });
|
|
3353
|
+
await browser.close();
|
|
3354
|
+
const baselineBuffer = readFileSync3(baseline.path);
|
|
3355
|
+
const compareBuffer = readFileSync3(comparePath);
|
|
3356
|
+
const areSameSize = baselineBuffer.length === compareBuffer.length;
|
|
3357
|
+
const areIdentical = areSameSize && baselineBuffer.equals(compareBuffer);
|
|
3358
|
+
console.log("");
|
|
3359
|
+
if (areIdentical) {
|
|
3360
|
+
console.log(pc2.green("\u2705 Screenshots are identical"));
|
|
3361
|
+
} else {
|
|
3362
|
+
console.log(pc2.yellow("\u26A0\uFE0F Screenshots differ"));
|
|
3363
|
+
console.log(pc2.dim(` Baseline size: ${baselineBuffer.length} bytes`));
|
|
3364
|
+
console.log(pc2.dim(` Current size: ${compareBuffer.length} bytes`));
|
|
3365
|
+
console.log("");
|
|
3366
|
+
console.log(
|
|
3367
|
+
pc2.dim(
|
|
3368
|
+
"For detailed LLM-based comparison, use the GitHub App's visual testing."
|
|
3369
|
+
)
|
|
3370
|
+
);
|
|
3371
|
+
console.log(pc2.dim(`Comparison saved to: ${comparePath}`));
|
|
3372
|
+
process.exit(1);
|
|
3373
|
+
}
|
|
3374
|
+
} catch (error) {
|
|
3375
|
+
console.error(pc2.red("\n\u274C Failed to compare:"), error);
|
|
3376
|
+
process.exit(1);
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
3379
|
+
async function visualListCommand(options) {
|
|
3380
|
+
const metadata = loadMetadata();
|
|
3381
|
+
const baselines = Object.values(metadata);
|
|
3382
|
+
if (options.json) {
|
|
3383
|
+
console.log(JSON.stringify(baselines, null, 2));
|
|
3384
|
+
return;
|
|
3385
|
+
}
|
|
3386
|
+
if (baselines.length === 0) {
|
|
3387
|
+
console.log(pc2.dim("No baselines found."));
|
|
3388
|
+
console.log(pc2.dim("Create one with: workflow visual capture <name> <url>"));
|
|
3389
|
+
return;
|
|
3390
|
+
}
|
|
3391
|
+
console.log(pc2.cyan("\n\u{1F4F8} Visual Baselines\n"));
|
|
3392
|
+
for (const baseline of baselines) {
|
|
3393
|
+
console.log(` ${pc2.bold(baseline.name)}`);
|
|
3394
|
+
console.log(` URL: ${baseline.url}`);
|
|
3395
|
+
console.log(` Viewport: ${baseline.width}x${baseline.height}`);
|
|
3396
|
+
console.log(` Path: ${baseline.path}`);
|
|
3397
|
+
console.log(` Updated: ${baseline.updatedAt}`);
|
|
3398
|
+
console.log("");
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
3401
|
+
async function visualUpdateCommand(name, options) {
|
|
3402
|
+
const metadata = loadMetadata();
|
|
3403
|
+
const baseline = metadata[name];
|
|
3404
|
+
if (!baseline) {
|
|
3405
|
+
console.log(pc2.red(`\u274C Baseline "${name}" not found`));
|
|
3406
|
+
process.exit(1);
|
|
3407
|
+
}
|
|
3408
|
+
console.log(pc2.cyan(`
|
|
3409
|
+
\u{1F504} Updating baseline "${name}"...
|
|
3410
|
+
`));
|
|
3411
|
+
await visualCaptureCommand(name, baseline.url, {
|
|
3412
|
+
width: options.width || String(baseline.width),
|
|
3413
|
+
height: options.height || String(baseline.height),
|
|
3414
|
+
fullPage: options.fullPage,
|
|
3415
|
+
output: baseline.path
|
|
3416
|
+
});
|
|
3417
|
+
}
|
|
3418
|
+
async function visualApproveCommand(name) {
|
|
3419
|
+
const metadata = loadMetadata();
|
|
3420
|
+
const baseline = metadata[name];
|
|
3421
|
+
if (!baseline) {
|
|
3422
|
+
console.log(pc2.red(`\u274C Baseline "${name}" not found`));
|
|
3423
|
+
process.exit(1);
|
|
3424
|
+
}
|
|
3425
|
+
const confirm8 = await p8.confirm({
|
|
3426
|
+
message: `Update baseline "${name}" with the latest comparison?`,
|
|
3427
|
+
initialValue: false
|
|
3428
|
+
});
|
|
3429
|
+
if (p8.isCancel(confirm8) || !confirm8) {
|
|
3430
|
+
console.log(pc2.dim("Cancelled."));
|
|
3431
|
+
return;
|
|
3432
|
+
}
|
|
3433
|
+
const baselinesDir = getBaselinesDir();
|
|
3434
|
+
const comparisonsDir = join11(baselinesDir, "comparisons");
|
|
3435
|
+
if (!existsSync11(comparisonsDir)) {
|
|
3436
|
+
console.log(pc2.yellow("No comparisons found to approve."));
|
|
3437
|
+
return;
|
|
3438
|
+
}
|
|
3439
|
+
console.log(
|
|
3440
|
+
pc2.yellow(
|
|
3441
|
+
"To approve, re-capture the baseline with: workflow visual update " + name
|
|
3442
|
+
)
|
|
3443
|
+
);
|
|
3444
|
+
}
|
|
3445
|
+
|
|
3009
3446
|
// src/cli/index.ts
|
|
3010
3447
|
var program = new Command2();
|
|
3011
3448
|
program.name("workflow").description(
|
|
@@ -3039,5 +3476,26 @@ program.command("scope:create").description("Create a custom scope package").opt
|
|
|
3039
3476
|
"Comma-separated scopes (format: name:description:emoji:category)"
|
|
3040
3477
|
).option("--preset-name <preset>", "Preset display name").option("--output-dir <dir>", "Output directory").option("--no-test", "Skip test file generation").action(scopeCreateCommand);
|
|
3041
3478
|
program.command("scope:migrate").description("Migrate inline scopes to a custom package").option("--name <name>", "Package name for the preset").option("--output-dir <dir>", "Output directory").option("--keep-config", "Keep inline scopes in config after migration").action(scopeMigrateCommand);
|
|
3479
|
+
program.command("fix").description("Auto-heal pipeline failures using LLM").option("--error <error>", "Error message to fix (required)").option("--context <context>", "Additional context JSON").option("--files <files>", "Comma-separated file paths to analyze").option("--auto", "Apply fix automatically without confirmation").option("--dry-run", "Show what would be changed without applying").action((options) => {
|
|
3480
|
+
fixCommand({
|
|
3481
|
+
error: options.error,
|
|
3482
|
+
context: options.context,
|
|
3483
|
+
files: options.files?.split(","),
|
|
3484
|
+
auto: options.auto,
|
|
3485
|
+
dryRun: options.dryRun
|
|
3486
|
+
});
|
|
3487
|
+
});
|
|
3488
|
+
var visual = program.command("visual").description("Visual testing commands");
|
|
3489
|
+
visual.command("capture").description("Capture a new baseline screenshot").argument("<name>", "Name for the baseline").argument("<url>", "URL to capture").option("-w, --width <width>", "Viewport width", "1280").option("-h, --height <height>", "Viewport height", "720").option("--full-page", "Capture full page").option("-o, --output <path>", "Output path").option("--wait-for <selector>", "Wait for selector before capture").option("--delay <ms>", "Additional delay in ms").action(visualCaptureCommand);
|
|
3490
|
+
visual.command("compare").description("Compare a URL against a baseline").argument("<url>", "URL to compare").option("-b, --baseline <name>", "Baseline name to compare against (required)").option("-o, --output <path>", "Output path for comparison screenshot").option("-t, --threshold <percent>", "Difference threshold percentage").action((url, options) => {
|
|
3491
|
+
if (!options.baseline) {
|
|
3492
|
+
console.error("Error: --baseline is required");
|
|
3493
|
+
process.exit(1);
|
|
3494
|
+
}
|
|
3495
|
+
visualCompareCommand(url, options);
|
|
3496
|
+
});
|
|
3497
|
+
visual.command("list").description("List all baselines").option("--json", "Output as JSON").action(visualListCommand);
|
|
3498
|
+
visual.command("update").description("Update an existing baseline").argument("<name>", "Baseline name to update").option("-w, --width <width>", "Override viewport width").option("-h, --height <height>", "Override viewport height").option("--full-page", "Capture full page").action(visualUpdateCommand);
|
|
3499
|
+
visual.command("approve").description("Approve a comparison as the new baseline").argument("<name>", "Baseline name to approve").action(visualApproveCommand);
|
|
3042
3500
|
program.parse();
|
|
3043
3501
|
//# sourceMappingURL=index.js.map
|