workos 0.5.4 → 0.6.0

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.
@@ -122,6 +122,17 @@ export interface InstallerEvents {
122
122
  message: string;
123
123
  stack?: string;
124
124
  };
125
+ 'agent:retry': {
126
+ attempt: number;
127
+ maxRetries: number;
128
+ };
129
+ 'validation:retry:start': {
130
+ attempt: number;
131
+ };
132
+ 'validation:retry:complete': {
133
+ attempt: number;
134
+ passed: boolean;
135
+ };
125
136
  'validation:start': {
126
137
  framework: string;
127
138
  };
@@ -1 +1 @@
1
- {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/lib/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAuFtC,MAAM,OAAO,qBAAsB,SAAQ,YAAY;IACrD,IAAI,CAA+B,KAAQ,EAAE,OAA2B;QACtE,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACpC,CAAC;IAED,EAAE,CAA+B,KAAQ,EAAE,QAA+C;QACxF,OAAO,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACnC,CAAC;IAED,GAAG,CAA+B,KAAQ,EAAE,QAA+C;QACzF,OAAO,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACpC,CAAC;IAED,IAAI,CAA+B,KAAQ,EAAE,QAA+C;QAC1F,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACrC,CAAC;CACF;AAED,MAAM,UAAU,2BAA2B;IACzC,OAAO,IAAI,qBAAqB,EAAE,CAAC;AACrC,CAAC","sourcesContent":["import { EventEmitter } from 'events';\n\nexport interface InstallerEvents {\n status: { message: string };\n output: { text: string; isError?: boolean };\n 'file:write': { path: string; content: string };\n 'file:edit': { path: string; oldContent: string; newContent: string };\n 'prompt:request': { id: string; message: string; options?: string[] };\n 'prompt:response': { id: string; value: string };\n 'confirm:request': { id: string; message: string; warning?: string; files?: string[] };\n 'confirm:response': { id: string; confirmed: boolean };\n 'credentials:request': { requiresApiKey: boolean };\n 'credentials:response': { apiKey: string; clientId: string };\n complete: { success: boolean; summary?: string };\n error: { message: string; stack?: string };\n\n 'state:enter': { state: string };\n 'state:exit': { state: string };\n 'auth:checking': Record<string, never>;\n 'auth:required': Record<string, never>;\n 'auth:success': Record<string, never>;\n 'auth:failure': { message: string };\n 'detection:start': Record<string, never>;\n 'detection:complete': { integration: string };\n 'detection:none': Record<string, never>;\n 'git:checking': Record<string, never>;\n 'git:clean': Record<string, never>;\n 'git:dirty': { files: string[] };\n 'git:dirty:confirmed': Record<string, never>;\n 'git:dirty:cancelled': Record<string, never>;\n 'credentials:gathering': { requiresApiKey: boolean };\n 'credentials:found': Record<string, never>;\n // Credential discovery events\n 'credentials:env:detected': { files: string[] };\n 'credentials:env:prompt': { files: string[] };\n 'credentials:env:scanning': Record<string, never>;\n 'credentials:env:found': { sourcePath: string };\n 'credentials:env:notfound': Record<string, never>;\n // Device auth events\n 'device:started': { verificationUri: string; verificationUriComplete: string; userCode: string };\n 'device:polling': Record<string, never>;\n 'device:success': { email?: string };\n 'device:timeout': Record<string, never>;\n 'device:error': { message: string };\n // Staging API events\n 'staging:fetching': Record<string, never>;\n 'staging:success': Record<string, never>;\n 'staging:error': { message: string; statusCode?: number };\n 'config:start': Record<string, never>;\n 'config:complete': Record<string, never>;\n 'agent:start': Record<string, never>;\n 'agent:progress': { step: string; detail?: string };\n 'agent:success': { summary?: string };\n 'agent:failure': { message: string; stack?: string };\n\n 'validation:start': { framework: string };\n 'validation:issues': { issues: import('./validation/types.js').ValidationIssue[] };\n 'validation:complete': { passed: boolean; issueCount: number; durationMs: number };\n\n // Branch check events\n 'branch:checking': Record<string, never>;\n 'branch:protected': { branch: string };\n 'branch:prompt': { branch: string };\n 'branch:created': { branch: string };\n 'branch:create:failed': { error: string };\n 'branch:skipped': Record<string, never>;\n\n // Post-install events\n 'postinstall:changes': { files: string[] };\n 'postinstall:nochanges': Record<string, never>;\n 'postinstall:commit:prompt': Record<string, never>;\n 'postinstall:commit:generating': Record<string, never>;\n 'postinstall:commit:committing': { message: string };\n 'postinstall:commit:success': { message: string };\n 'postinstall:commit:failed': { error: string };\n 'postinstall:pr:prompt': Record<string, never>;\n 'postinstall:pr:generating': Record<string, never>;\n 'postinstall:pr:pushing': Record<string, never>;\n 'postinstall:pr:creating': Record<string, never>;\n 'postinstall:pr:success': { url: string };\n 'postinstall:pr:failed': { error: string };\n 'postinstall:push:failed': { error: string };\n 'postinstall:manual': { instructions: string };\n}\n\nexport type InstallerEventName = keyof InstallerEvents;\n\nexport class InstallerEventEmitter extends EventEmitter {\n emit<K extends InstallerEventName>(event: K, payload: InstallerEvents[K]): boolean {\n return super.emit(event, payload);\n }\n\n on<K extends InstallerEventName>(event: K, listener: (payload: InstallerEvents[K]) => void): this {\n return super.on(event, listener);\n }\n\n off<K extends InstallerEventName>(event: K, listener: (payload: InstallerEvents[K]) => void): this {\n return super.off(event, listener);\n }\n\n once<K extends InstallerEventName>(event: K, listener: (payload: InstallerEvents[K]) => void): this {\n return super.once(event, listener);\n }\n}\n\nexport function createInstallerEventEmitter(): InstallerEventEmitter {\n return new InstallerEventEmitter();\n}\n"]}
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/lib/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AA2FtC,MAAM,OAAO,qBAAsB,SAAQ,YAAY;IACrD,IAAI,CAA+B,KAAQ,EAAE,OAA2B;QACtE,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACpC,CAAC;IAED,EAAE,CAA+B,KAAQ,EAAE,QAA+C;QACxF,OAAO,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACnC,CAAC;IAED,GAAG,CAA+B,KAAQ,EAAE,QAA+C;QACzF,OAAO,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACpC,CAAC;IAED,IAAI,CAA+B,KAAQ,EAAE,QAA+C;QAC1F,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACrC,CAAC;CACF;AAED,MAAM,UAAU,2BAA2B;IACzC,OAAO,IAAI,qBAAqB,EAAE,CAAC;AACrC,CAAC","sourcesContent":["import { EventEmitter } from 'events';\n\nexport interface InstallerEvents {\n status: { message: string };\n output: { text: string; isError?: boolean };\n 'file:write': { path: string; content: string };\n 'file:edit': { path: string; oldContent: string; newContent: string };\n 'prompt:request': { id: string; message: string; options?: string[] };\n 'prompt:response': { id: string; value: string };\n 'confirm:request': { id: string; message: string; warning?: string; files?: string[] };\n 'confirm:response': { id: string; confirmed: boolean };\n 'credentials:request': { requiresApiKey: boolean };\n 'credentials:response': { apiKey: string; clientId: string };\n complete: { success: boolean; summary?: string };\n error: { message: string; stack?: string };\n\n 'state:enter': { state: string };\n 'state:exit': { state: string };\n 'auth:checking': Record<string, never>;\n 'auth:required': Record<string, never>;\n 'auth:success': Record<string, never>;\n 'auth:failure': { message: string };\n 'detection:start': Record<string, never>;\n 'detection:complete': { integration: string };\n 'detection:none': Record<string, never>;\n 'git:checking': Record<string, never>;\n 'git:clean': Record<string, never>;\n 'git:dirty': { files: string[] };\n 'git:dirty:confirmed': Record<string, never>;\n 'git:dirty:cancelled': Record<string, never>;\n 'credentials:gathering': { requiresApiKey: boolean };\n 'credentials:found': Record<string, never>;\n // Credential discovery events\n 'credentials:env:detected': { files: string[] };\n 'credentials:env:prompt': { files: string[] };\n 'credentials:env:scanning': Record<string, never>;\n 'credentials:env:found': { sourcePath: string };\n 'credentials:env:notfound': Record<string, never>;\n // Device auth events\n 'device:started': { verificationUri: string; verificationUriComplete: string; userCode: string };\n 'device:polling': Record<string, never>;\n 'device:success': { email?: string };\n 'device:timeout': Record<string, never>;\n 'device:error': { message: string };\n // Staging API events\n 'staging:fetching': Record<string, never>;\n 'staging:success': Record<string, never>;\n 'staging:error': { message: string; statusCode?: number };\n 'config:start': Record<string, never>;\n 'config:complete': Record<string, never>;\n 'agent:start': Record<string, never>;\n 'agent:progress': { step: string; detail?: string };\n 'agent:success': { summary?: string };\n 'agent:failure': { message: string; stack?: string };\n 'agent:retry': { attempt: number; maxRetries: number };\n\n 'validation:retry:start': { attempt: number };\n 'validation:retry:complete': { attempt: number; passed: boolean };\n\n 'validation:start': { framework: string };\n 'validation:issues': { issues: import('./validation/types.js').ValidationIssue[] };\n 'validation:complete': { passed: boolean; issueCount: number; durationMs: number };\n\n // Branch check events\n 'branch:checking': Record<string, never>;\n 'branch:protected': { branch: string };\n 'branch:prompt': { branch: string };\n 'branch:created': { branch: string };\n 'branch:create:failed': { error: string };\n 'branch:skipped': Record<string, never>;\n\n // Post-install events\n 'postinstall:changes': { files: string[] };\n 'postinstall:nochanges': Record<string, never>;\n 'postinstall:commit:prompt': Record<string, never>;\n 'postinstall:commit:generating': Record<string, never>;\n 'postinstall:commit:committing': { message: string };\n 'postinstall:commit:success': { message: string };\n 'postinstall:commit:failed': { error: string };\n 'postinstall:pr:prompt': Record<string, never>;\n 'postinstall:pr:generating': Record<string, never>;\n 'postinstall:pr:pushing': Record<string, never>;\n 'postinstall:pr:creating': Record<string, never>;\n 'postinstall:pr:success': { url: string };\n 'postinstall:pr:failed': { error: string };\n 'postinstall:push:failed': { error: string };\n 'postinstall:manual': { instructions: string };\n}\n\nexport type InstallerEventName = keyof InstallerEvents;\n\nexport class InstallerEventEmitter extends EventEmitter {\n emit<K extends InstallerEventName>(event: K, payload: InstallerEvents[K]): boolean {\n return super.emit(event, payload);\n }\n\n on<K extends InstallerEventName>(event: K, listener: (payload: InstallerEvents[K]) => void): this {\n return super.on(event, listener);\n }\n\n off<K extends InstallerEventName>(event: K, listener: (payload: InstallerEvents[K]) => void): this {\n return super.off(event, listener);\n }\n\n once<K extends InstallerEventName>(event: K, listener: (payload: InstallerEvents[K]) => void): this {\n return super.once(event, listener);\n }\n}\n\nexport function createInstallerEventEmitter(): InstallerEventEmitter {\n return new InstallerEventEmitter();\n}\n"]}
@@ -7,3 +7,15 @@ export interface BuildResult {
7
7
  stderr: string;
8
8
  }
9
9
  export declare function runBuildValidation(projectDir: string, timeoutMs?: number): Promise<BuildResult>;
10
+ export declare function detectPackageManager(projectDir: string): 'pnpm' | 'yarn' | 'npm';
11
+ export declare function hasBuildScriptInPackageJson(projectDir: string): Promise<boolean>;
12
+ export interface BuildCommand {
13
+ command: string;
14
+ args: string[];
15
+ }
16
+ /**
17
+ * Detect the build command for a project by checking ecosystem markers.
18
+ * Returns null if no build system detected — caller should skip build validation.
19
+ */
20
+ export declare function detectBuildCommand(projectDir: string): Promise<BuildCommand | null>;
21
+ export declare function parseBuildErrors(output: string): string[];
@@ -1,5 +1,5 @@
1
1
  import { spawn } from 'child_process';
2
- import { existsSync } from 'fs';
2
+ import { existsSync, readdirSync } from 'fs';
3
3
  import { readFile } from 'fs/promises';
4
4
  import { join } from 'path';
5
5
  export async function runBuildValidation(projectDir, timeoutMs = 60000) {
@@ -79,14 +79,14 @@ export async function runBuildValidation(projectDir, timeoutMs = 60000) {
79
79
  });
80
80
  });
81
81
  }
82
- function detectPackageManager(projectDir) {
82
+ export function detectPackageManager(projectDir) {
83
83
  if (existsSync(join(projectDir, 'pnpm-lock.yaml')))
84
84
  return 'pnpm';
85
85
  if (existsSync(join(projectDir, 'yarn.lock')))
86
86
  return 'yarn';
87
87
  return 'npm';
88
88
  }
89
- async function hasBuildScriptInPackageJson(projectDir) {
89
+ export async function hasBuildScriptInPackageJson(projectDir) {
90
90
  try {
91
91
  const content = await readFile(join(projectDir, 'package.json'), 'utf-8');
92
92
  const pkg = JSON.parse(content);
@@ -96,7 +96,38 @@ async function hasBuildScriptInPackageJson(projectDir) {
96
96
  return false;
97
97
  }
98
98
  }
99
- function parseBuildErrors(output) {
99
+ /**
100
+ * Detect the build command for a project by checking ecosystem markers.
101
+ * Returns null if no build system detected — caller should skip build validation.
102
+ */
103
+ export async function detectBuildCommand(projectDir) {
104
+ const pm = detectPackageManager(projectDir);
105
+ if (await hasBuildScriptInPackageJson(projectDir)) {
106
+ const args = pm === 'npm' ? ['run', 'build'] : ['build'];
107
+ return { command: pm, args };
108
+ }
109
+ if (existsSync(join(projectDir, 'go.mod'))) {
110
+ return { command: 'go', args: ['build', './...'] };
111
+ }
112
+ if (existsSync(join(projectDir, 'mix.exs'))) {
113
+ return { command: 'mix', args: ['compile'] };
114
+ }
115
+ try {
116
+ const files = readdirSync(projectDir);
117
+ if (files.some((f) => f.endsWith('.csproj'))) {
118
+ return { command: 'dotnet', args: ['build'] };
119
+ }
120
+ }
121
+ catch {
122
+ // Can't read directory
123
+ }
124
+ if (existsSync(join(projectDir, 'build.gradle.kts')) || existsSync(join(projectDir, 'build.gradle'))) {
125
+ const gradlew = existsSync(join(projectDir, 'gradlew')) ? './gradlew' : 'gradle';
126
+ return { command: gradlew, args: ['build'] };
127
+ }
128
+ return null;
129
+ }
130
+ export function parseBuildErrors(output) {
100
131
  const errors = [];
101
132
  // TypeScript errors: "file.ts(line,col): error TS..."
102
133
  const tsErrors = output.match(/[\w./]+\.\w+\(\d+,\d+\):\s*error\s+TS\d+:.+/g);
@@ -1 +1 @@
1
- {"version":3,"file":"build-validator.js","sourceRoot":"","sources":["../../../src/lib/validation/build-validator.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from 'child_process';\nimport { existsSync } from 'fs';\nimport { readFile } from 'fs/promises';\nimport { join } from 'path';\nimport type { ValidationIssue } from './types.js';\n\nexport interface BuildResult {\n success: boolean;\n issues: ValidationIssue[];\n durationMs: number;\n stdout: string;\n stderr: string;\n}\n\nexport async function runBuildValidation(projectDir: string, timeoutMs: number = 60000): Promise<BuildResult> {\n const startTime = Date.now();\n const pm = detectPackageManager(projectDir);\n const hasBuildScript = await hasBuildScriptInPackageJson(projectDir);\n\n if (!hasBuildScript) {\n return {\n success: true,\n issues: [],\n durationMs: Date.now() - startTime,\n stdout: '',\n stderr: '',\n };\n }\n\n return new Promise((resolve) => {\n const args = pm === 'npm' ? ['run', 'build'] : ['build'];\n const proc = spawn(pm, args, {\n cwd: projectDir,\n shell: true,\n timeout: timeoutMs,\n });\n\n let stdout = '';\n let stderr = '';\n\n proc.stdout?.on('data', (data: Buffer) => {\n stdout += data.toString();\n });\n proc.stderr?.on('data', (data: Buffer) => {\n stderr += data.toString();\n });\n\n proc.on('close', (code) => {\n const issues: ValidationIssue[] = [];\n\n if (code !== 0) {\n // Parse errors from output\n const errors = parseBuildErrors(stdout + stderr);\n for (const error of errors) {\n issues.push({\n type: 'file',\n severity: 'error',\n message: `Build error: ${error}`,\n hint: 'Fix the error and run build again',\n });\n }\n\n // Fallback if no specific errors parsed\n if (issues.length === 0) {\n issues.push({\n type: 'file',\n severity: 'error',\n message: 'Build failed',\n hint: `Run \\`${pm} ${args.join(' ')}\\` to see full output`,\n });\n }\n }\n\n resolve({\n success: code === 0,\n issues,\n durationMs: Date.now() - startTime,\n stdout,\n stderr,\n });\n });\n\n proc.on('error', (err) => {\n resolve({\n success: false,\n issues: [\n {\n type: 'file',\n severity: 'warning',\n message: `Could not run build: ${err.message}`,\n hint: 'Build validation skipped',\n },\n ],\n durationMs: Date.now() - startTime,\n stdout: '',\n stderr: '',\n });\n });\n });\n}\n\nfunction detectPackageManager(projectDir: string): 'pnpm' | 'yarn' | 'npm' {\n if (existsSync(join(projectDir, 'pnpm-lock.yaml'))) return 'pnpm';\n if (existsSync(join(projectDir, 'yarn.lock'))) return 'yarn';\n return 'npm';\n}\n\nasync function hasBuildScriptInPackageJson(projectDir: string): Promise<boolean> {\n try {\n const content = await readFile(join(projectDir, 'package.json'), 'utf-8');\n const pkg = JSON.parse(content) as { scripts?: { build?: string } };\n return !!pkg.scripts?.build;\n } catch {\n return false;\n }\n}\n\nfunction parseBuildErrors(output: string): string[] {\n const errors: string[] = [];\n\n // TypeScript errors: \"file.ts(line,col): error TS...\"\n const tsErrors = output.match(/[\\w./]+\\.\\w+\\(\\d+,\\d+\\):\\s*error\\s+TS\\d+:.+/g);\n if (tsErrors) {\n errors.push(...tsErrors.slice(0, 5)); // Limit to first 5\n }\n\n // Next.js errors: \"Error: ...\"\n const nextErrors = output.match(/Error:\\s+.+/g);\n if (nextErrors) {\n errors.push(...nextErrors.slice(0, 5));\n }\n\n // ESLint errors: \"file.ts line:col error ...\"\n const eslintErrors = output.match(/[\\w./]+:\\d+:\\d+\\s+error\\s+.+/g);\n if (eslintErrors) {\n errors.push(...eslintErrors.slice(0, 5));\n }\n\n return errors;\n}\n"]}
1
+ {"version":3,"file":"build-validator.js","sourceRoot":"","sources":["../../../src/lib/validation/build-validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,IAAI,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAW5B,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,UAAkB,EAAE,YAAoB,KAAK;IACpF,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,EAAE,GAAG,oBAAoB,CAAC,UAAU,CAAC,CAAC;IAC5C,MAAM,cAAc,GAAG,MAAM,2BAA2B,CAAC,UAAU,CAAC,CAAC;IAErE,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,OAAO;YACL,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,EAAE;YACV,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;YAClC,MAAM,EAAE,EAAE;YACV,MAAM,EAAE,EAAE;SACX,CAAC;IACJ,CAAC;IAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,IAAI,GAAG,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QACzD,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE;YAC3B,GAAG,EAAE,UAAU;YACf,KAAK,EAAE,IAAI;YACX,OAAO,EAAE,SAAS;SACnB,CAAC,CAAC;QAEH,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,MAAM,GAAG,EAAE,CAAC;QAEhB,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE;YACvC,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE;YACvC,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACxB,MAAM,MAAM,GAAsB,EAAE,CAAC;YAErC,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;gBACf,2BAA2B;gBAC3B,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;gBACjD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;oBAC3B,MAAM,CAAC,IAAI,CAAC;wBACV,IAAI,EAAE,MAAM;wBACZ,QAAQ,EAAE,OAAO;wBACjB,OAAO,EAAE,gBAAgB,KAAK,EAAE;wBAChC,IAAI,EAAE,mCAAmC;qBAC1C,CAAC,CAAC;gBACL,CAAC;gBAED,wCAAwC;gBACxC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACxB,MAAM,CAAC,IAAI,CAAC;wBACV,IAAI,EAAE,MAAM;wBACZ,QAAQ,EAAE,OAAO;wBACjB,OAAO,EAAE,cAAc;wBACvB,IAAI,EAAE,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,uBAAuB;qBAC3D,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAED,OAAO,CAAC;gBACN,OAAO,EAAE,IAAI,KAAK,CAAC;gBACnB,MAAM;gBACN,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;gBAClC,MAAM;gBACN,MAAM;aACP,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACvB,OAAO,CAAC;gBACN,OAAO,EAAE,KAAK;gBACd,MAAM,EAAE;oBACN;wBACE,IAAI,EAAE,MAAM;wBACZ,QAAQ,EAAE,SAAS;wBACnB,OAAO,EAAE,wBAAwB,GAAG,CAAC,OAAO,EAAE;wBAC9C,IAAI,EAAE,0BAA0B;qBACjC;iBACF;gBACD,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;gBAClC,MAAM,EAAE,EAAE;gBACV,MAAM,EAAE,EAAE;aACX,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,UAAkB;IACrD,IAAI,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC;IAClE,IAAI,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC;IAC7D,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAAC,UAAkB;IAClE,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC;QAC1E,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAqC,CAAC;QACpE,OAAO,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAOD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,UAAkB;IACzD,MAAM,EAAE,GAAG,oBAAoB,CAAC,UAAU,CAAC,CAAC;IAC5C,IAAI,MAAM,2BAA2B,CAAC,UAAU,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,GAAG,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QACzD,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IAC/B,CAAC;IAED,IAAI,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC;QAC3C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC;IACrD,CAAC;IAED,IAAI,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC;QAC5C,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC;IAC/C,CAAC;IAED,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC;QACtC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;YAC7C,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;QAChD,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,uBAAuB;IACzB,CAAC;IAED,IAAI,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,kBAAkB,CAAC,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC;QACrG,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC;QACjF,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;IAC/C,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,sDAAsD;IACtD,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAC9E,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,mBAAmB;IAC3D,CAAC;IAED,+BAA+B;IAC/B,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAChD,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACzC,CAAC;IAED,8CAA8C;IAC9C,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;IACnE,IAAI,YAAY,EAAE,CAAC;QACjB,MAAM,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC3C,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import { spawn } from 'child_process';\nimport { existsSync, readdirSync } from 'fs';\nimport { readFile } from 'fs/promises';\nimport { join } from 'path';\nimport type { ValidationIssue } from './types.js';\n\nexport interface BuildResult {\n success: boolean;\n issues: ValidationIssue[];\n durationMs: number;\n stdout: string;\n stderr: string;\n}\n\nexport async function runBuildValidation(projectDir: string, timeoutMs: number = 60000): Promise<BuildResult> {\n const startTime = Date.now();\n const pm = detectPackageManager(projectDir);\n const hasBuildScript = await hasBuildScriptInPackageJson(projectDir);\n\n if (!hasBuildScript) {\n return {\n success: true,\n issues: [],\n durationMs: Date.now() - startTime,\n stdout: '',\n stderr: '',\n };\n }\n\n return new Promise((resolve) => {\n const args = pm === 'npm' ? ['run', 'build'] : ['build'];\n const proc = spawn(pm, args, {\n cwd: projectDir,\n shell: true,\n timeout: timeoutMs,\n });\n\n let stdout = '';\n let stderr = '';\n\n proc.stdout?.on('data', (data: Buffer) => {\n stdout += data.toString();\n });\n proc.stderr?.on('data', (data: Buffer) => {\n stderr += data.toString();\n });\n\n proc.on('close', (code) => {\n const issues: ValidationIssue[] = [];\n\n if (code !== 0) {\n // Parse errors from output\n const errors = parseBuildErrors(stdout + stderr);\n for (const error of errors) {\n issues.push({\n type: 'file',\n severity: 'error',\n message: `Build error: ${error}`,\n hint: 'Fix the error and run build again',\n });\n }\n\n // Fallback if no specific errors parsed\n if (issues.length === 0) {\n issues.push({\n type: 'file',\n severity: 'error',\n message: 'Build failed',\n hint: `Run \\`${pm} ${args.join(' ')}\\` to see full output`,\n });\n }\n }\n\n resolve({\n success: code === 0,\n issues,\n durationMs: Date.now() - startTime,\n stdout,\n stderr,\n });\n });\n\n proc.on('error', (err) => {\n resolve({\n success: false,\n issues: [\n {\n type: 'file',\n severity: 'warning',\n message: `Could not run build: ${err.message}`,\n hint: 'Build validation skipped',\n },\n ],\n durationMs: Date.now() - startTime,\n stdout: '',\n stderr: '',\n });\n });\n });\n}\n\nexport function detectPackageManager(projectDir: string): 'pnpm' | 'yarn' | 'npm' {\n if (existsSync(join(projectDir, 'pnpm-lock.yaml'))) return 'pnpm';\n if (existsSync(join(projectDir, 'yarn.lock'))) return 'yarn';\n return 'npm';\n}\n\nexport async function hasBuildScriptInPackageJson(projectDir: string): Promise<boolean> {\n try {\n const content = await readFile(join(projectDir, 'package.json'), 'utf-8');\n const pkg = JSON.parse(content) as { scripts?: { build?: string } };\n return !!pkg.scripts?.build;\n } catch {\n return false;\n }\n}\n\nexport interface BuildCommand {\n command: string;\n args: string[];\n}\n\n/**\n * Detect the build command for a project by checking ecosystem markers.\n * Returns null if no build system detected — caller should skip build validation.\n */\nexport async function detectBuildCommand(projectDir: string): Promise<BuildCommand | null> {\n const pm = detectPackageManager(projectDir);\n if (await hasBuildScriptInPackageJson(projectDir)) {\n const args = pm === 'npm' ? ['run', 'build'] : ['build'];\n return { command: pm, args };\n }\n\n if (existsSync(join(projectDir, 'go.mod'))) {\n return { command: 'go', args: ['build', './...'] };\n }\n\n if (existsSync(join(projectDir, 'mix.exs'))) {\n return { command: 'mix', args: ['compile'] };\n }\n\n try {\n const files = readdirSync(projectDir);\n if (files.some((f) => f.endsWith('.csproj'))) {\n return { command: 'dotnet', args: ['build'] };\n }\n } catch {\n // Can't read directory\n }\n\n if (existsSync(join(projectDir, 'build.gradle.kts')) || existsSync(join(projectDir, 'build.gradle'))) {\n const gradlew = existsSync(join(projectDir, 'gradlew')) ? './gradlew' : 'gradle';\n return { command: gradlew, args: ['build'] };\n }\n\n return null;\n}\n\nexport function parseBuildErrors(output: string): string[] {\n const errors: string[] = [];\n\n // TypeScript errors: \"file.ts(line,col): error TS...\"\n const tsErrors = output.match(/[\\w./]+\\.\\w+\\(\\d+,\\d+\\):\\s*error\\s+TS\\d+:.+/g);\n if (tsErrors) {\n errors.push(...tsErrors.slice(0, 5)); // Limit to first 5\n }\n\n // Next.js errors: \"Error: ...\"\n const nextErrors = output.match(/Error:\\s+.+/g);\n if (nextErrors) {\n errors.push(...nextErrors.slice(0, 5));\n }\n\n // ESLint errors: \"file.ts line:col error ...\"\n const eslintErrors = output.match(/[\\w./]+:\\d+:\\d+\\s+error\\s+.+/g);\n if (eslintErrors) {\n errors.push(...eslintErrors.slice(0, 5));\n }\n\n return errors;\n}\n"]}
@@ -1,3 +1,4 @@
1
- export { validateInstallation, type ValidateOptions } from './validator.js';
1
+ export { validateInstallation, validatePackages, validateEnvVars, validateFiles, validateFrameworkSpecific, type ValidateOptions, } from './validator.js';
2
2
  export { runBuildValidation, type BuildResult } from './build-validator.js';
3
- export type { ValidationResult, ValidationRules, ValidationIssue, ValidationSeverity, ValidationIssueType, PackageRule, EnvVarRule, FileRule, VariantRules, } from './types.js';
3
+ export { runQuickChecks, runTypecheckValidation, quickCheckValidateAndFormat } from './quick-checks.js';
4
+ export type { ValidationResult, ValidationRules, ValidationIssue, ValidationSeverity, ValidationIssueType, PackageRule, EnvVarRule, FileRule, VariantRules, QuickCheckResult, QuickChecksOutput, } from './types.js';
@@ -1,3 +1,4 @@
1
- export { validateInstallation } from './validator.js';
1
+ export { validateInstallation, validatePackages, validateEnvVars, validateFiles, validateFrameworkSpecific, } from './validator.js';
2
2
  export { runBuildValidation } from './build-validator.js';
3
+ export { runQuickChecks, runTypecheckValidation, quickCheckValidateAndFormat } from './quick-checks.js';
3
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/validation/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAwB,MAAM,gBAAgB,CAAC;AAC5E,OAAO,EAAE,kBAAkB,EAAoB,MAAM,sBAAsB,CAAC","sourcesContent":["export { validateInstallation, type ValidateOptions } from './validator.js';\nexport { runBuildValidation, type BuildResult } from './build-validator.js';\nexport type {\n ValidationResult,\n ValidationRules,\n ValidationIssue,\n ValidationSeverity,\n ValidationIssueType,\n PackageRule,\n EnvVarRule,\n FileRule,\n VariantRules,\n} from './types.js';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/validation/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,yBAAyB,GAE1B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,kBAAkB,EAAoB,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC","sourcesContent":["export {\n validateInstallation,\n validatePackages,\n validateEnvVars,\n validateFiles,\n validateFrameworkSpecific,\n type ValidateOptions,\n} from './validator.js';\nexport { runBuildValidation, type BuildResult } from './build-validator.js';\nexport { runQuickChecks, runTypecheckValidation, quickCheckValidateAndFormat } from './quick-checks.js';\nexport type {\n ValidationResult,\n ValidationRules,\n ValidationIssue,\n ValidationSeverity,\n ValidationIssueType,\n PackageRule,\n EnvVarRule,\n FileRule,\n VariantRules,\n QuickCheckResult,\n QuickChecksOutput,\n} from './types.js';\n"]}
@@ -0,0 +1,19 @@
1
+ import type { QuickCheckResult, QuickChecksOutput } from './types.js';
2
+ /**
3
+ * Run fast deterministic checks: typecheck first, then build.
4
+ * Short-circuits: if typecheck fails, skip build (build will fail too).
5
+ */
6
+ export declare function runQuickChecks(projectDir: string, options?: {
7
+ skipBuild?: boolean;
8
+ timeoutMs?: number;
9
+ }): Promise<QuickChecksOutput>;
10
+ /**
11
+ * Run typecheck only (tsc --noEmit or framework equivalent).
12
+ * Faster than full build — catches type errors in ~5s.
13
+ */
14
+ export declare function runTypecheckValidation(projectDir: string, timeoutMs?: number): Promise<QuickCheckResult>;
15
+ /**
16
+ * Validation callback suitable for RetryConfig.validateAndFormat.
17
+ * Returns null if checks pass, or an agent-ready error prompt if they fail.
18
+ */
19
+ export declare function quickCheckValidateAndFormat(workingDirectory: string): Promise<string | null>;
@@ -0,0 +1,191 @@
1
+ import { spawn } from 'child_process';
2
+ import { readFile } from 'fs/promises';
3
+ import { join } from 'path';
4
+ import { detectBuildCommand, detectPackageManager, parseBuildErrors } from './build-validator.js';
5
+ const DEFAULT_TYPECHECK_TIMEOUT_MS = 30_000;
6
+ const DEFAULT_BUILD_TIMEOUT_MS = 60_000;
7
+ /**
8
+ * Run fast deterministic checks: typecheck first, then build.
9
+ * Short-circuits: if typecheck fails, skip build (build will fail too).
10
+ */
11
+ export async function runQuickChecks(projectDir, options) {
12
+ const startTime = Date.now();
13
+ const results = [];
14
+ const typecheckResult = await runTypecheckValidation(projectDir, options?.timeoutMs ?? DEFAULT_TYPECHECK_TIMEOUT_MS);
15
+ results.push(typecheckResult);
16
+ if (typecheckResult.passed && !options?.skipBuild) {
17
+ results.push(await runBuildQuickCheck(projectDir, options?.timeoutMs ?? DEFAULT_BUILD_TIMEOUT_MS));
18
+ }
19
+ const passed = results.every((r) => r.passed);
20
+ return {
21
+ passed,
22
+ results,
23
+ agentRetryPrompt: passed ? null : formatForAgent(results),
24
+ totalDurationMs: Date.now() - startTime,
25
+ };
26
+ }
27
+ function passResult(phase, startTime) {
28
+ return { passed: true, phase, issues: [], agentPrompt: null, durationMs: Date.now() - startTime };
29
+ }
30
+ /**
31
+ * Run typecheck only (tsc --noEmit or framework equivalent).
32
+ * Faster than full build — catches type errors in ~5s.
33
+ */
34
+ export async function runTypecheckValidation(projectDir, timeoutMs = DEFAULT_TYPECHECK_TIMEOUT_MS) {
35
+ const startTime = Date.now();
36
+ const typecheckCmd = await detectTypecheckCommand(projectDir);
37
+ if (!typecheckCmd) {
38
+ return passResult('typecheck', startTime);
39
+ }
40
+ const { exitCode, stdout, stderr } = await spawnCommand(typecheckCmd.command, typecheckCmd.args, projectDir, timeoutMs);
41
+ if (exitCode === 0) {
42
+ return passResult('typecheck', startTime);
43
+ }
44
+ const output = stdout + stderr;
45
+ const errors = parseTypecheckErrors(output);
46
+ const issues = errors.map((error) => ({
47
+ type: 'file',
48
+ severity: 'error',
49
+ message: `Type error: ${error}`,
50
+ hint: 'Fix the type error and run typecheck again',
51
+ }));
52
+ if (issues.length === 0) {
53
+ issues.push({
54
+ type: 'file',
55
+ severity: 'error',
56
+ message: 'Typecheck failed',
57
+ hint: `Run \`${typecheckCmd.command} ${typecheckCmd.args.join(' ')}\` to see full output`,
58
+ });
59
+ }
60
+ return {
61
+ passed: false,
62
+ phase: 'typecheck',
63
+ issues,
64
+ agentPrompt: formatTypecheckErrors(errors, output),
65
+ durationMs: Date.now() - startTime,
66
+ };
67
+ }
68
+ async function runBuildQuickCheck(projectDir, timeoutMs) {
69
+ const startTime = Date.now();
70
+ const buildCmd = await detectBuildCommand(projectDir);
71
+ if (!buildCmd) {
72
+ return passResult('build', startTime);
73
+ }
74
+ const { exitCode, stdout, stderr } = await spawnCommand(buildCmd.command, buildCmd.args, projectDir, timeoutMs);
75
+ if (exitCode === 0) {
76
+ return passResult('build', startTime);
77
+ }
78
+ const output = stdout + stderr;
79
+ const errors = parseBuildErrors(output);
80
+ const issues = errors.length > 0
81
+ ? errors.map((e) => ({
82
+ type: 'file',
83
+ severity: 'error',
84
+ message: `Build error: ${e}`,
85
+ hint: 'Fix the error and run build again',
86
+ }))
87
+ : [
88
+ {
89
+ type: 'file',
90
+ severity: 'error',
91
+ message: 'Build failed',
92
+ hint: `Run \`${buildCmd.command} ${buildCmd.args.join(' ')}\` to see full output`,
93
+ },
94
+ ];
95
+ return {
96
+ passed: false,
97
+ phase: 'build',
98
+ issues,
99
+ agentPrompt: formatBuildErrors(issues),
100
+ durationMs: Date.now() - startTime,
101
+ };
102
+ }
103
+ async function detectTypecheckCommand(projectDir) {
104
+ const pm = detectPackageManager(projectDir);
105
+ try {
106
+ const content = await readFile(join(projectDir, 'package.json'), 'utf-8');
107
+ const pkg = JSON.parse(content);
108
+ const scriptName = pkg.scripts?.typecheck ? 'typecheck' : pkg.scripts?.['type-check'] ? 'type-check' : null;
109
+ if (scriptName) {
110
+ const args = pm === 'npm' ? ['run', scriptName] : [scriptName];
111
+ return { command: pm, args };
112
+ }
113
+ }
114
+ catch {
115
+ // No package.json or malformed
116
+ }
117
+ try {
118
+ await readFile(join(projectDir, 'tsconfig.json'), 'utf-8');
119
+ return { command: 'npx', args: ['tsc', '--noEmit'] };
120
+ }
121
+ catch {
122
+ return null;
123
+ }
124
+ }
125
+ function parseTypecheckErrors(output) {
126
+ // Match both TS error formats:
127
+ // src/file.ts(line,col): error TS2345: ...
128
+ // src/file.ts:line:col - error TS2345: ... (tsc --pretty)
129
+ const pattern = /[\w./]+\.\w+(?:\(\d+,\d+\):\s*|:\d+:\d+\s*-\s*)error\s+TS\d+:.+/g;
130
+ const matches = output.match(pattern);
131
+ return matches ? [...new Set(matches)].slice(0, 10) : [];
132
+ }
133
+ function formatTypecheckErrors(errors, rawOutput) {
134
+ if (errors.length === 0) {
135
+ // Couldn't parse specific errors — give raw output
136
+ const truncated = rawOutput.slice(0, 2000);
137
+ return `The typecheck failed. Here is the output:\n\n${truncated}\n\nFix the type errors shown above.`;
138
+ }
139
+ const lines = errors.map((error) => {
140
+ // Extract file:line info and error description
141
+ const fileMatch = error.match(/([\w./]+\.\w+)[:(]\d+/);
142
+ const tsMatch = error.match(/error\s+(TS\d+):\s*(.+)/);
143
+ if (fileMatch && tsMatch) {
144
+ return `- ${fileMatch[1]}: ${tsMatch[2]} (${tsMatch[1]})`;
145
+ }
146
+ return `- ${error}`;
147
+ });
148
+ return `The typecheck failed with ${errors.length} error${errors.length === 1 ? '' : 's'}:\n\n${lines.join('\n')}\n\nFix these type errors in the indicated files.`;
149
+ }
150
+ function formatBuildErrors(issues) {
151
+ const errorMessages = issues.map((i) => `- ${i.message}`);
152
+ return `The build failed:\n\n${errorMessages.join('\n')}\n\nFix these build errors.`;
153
+ }
154
+ function formatForAgent(results) {
155
+ return results
156
+ .filter((r) => !r.passed && r.agentPrompt)
157
+ .map((r) => r.agentPrompt)
158
+ .join('\n\n');
159
+ }
160
+ /**
161
+ * Validation callback suitable for RetryConfig.validateAndFormat.
162
+ * Returns null if checks pass, or an agent-ready error prompt if they fail.
163
+ */
164
+ export async function quickCheckValidateAndFormat(workingDirectory) {
165
+ const result = await runQuickChecks(workingDirectory);
166
+ return result.passed ? null : result.agentRetryPrompt;
167
+ }
168
+ function spawnCommand(command, args, cwd, timeoutMs) {
169
+ return new Promise((resolve) => {
170
+ const proc = spawn(command, args, {
171
+ cwd,
172
+ shell: true,
173
+ timeout: timeoutMs,
174
+ });
175
+ let stdout = '';
176
+ let stderr = '';
177
+ proc.stdout?.on('data', (data) => {
178
+ stdout += data.toString();
179
+ });
180
+ proc.stderr?.on('data', (data) => {
181
+ stderr += data.toString();
182
+ });
183
+ proc.on('close', (code) => {
184
+ resolve({ exitCode: code ?? 1, stdout, stderr });
185
+ });
186
+ proc.on('error', () => {
187
+ resolve({ exitCode: 1, stdout, stderr });
188
+ });
189
+ });
190
+ }
191
+ //# sourceMappingURL=quick-checks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quick-checks.js","sourceRoot":"","sources":["../../../src/lib/validation/quick-checks.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from 'child_process';\nimport { readFile } from 'fs/promises';\nimport { join } from 'path';\nimport type { QuickCheckResult, QuickChecksOutput, ValidationIssue } from './types.js';\nimport { detectBuildCommand, detectPackageManager, parseBuildErrors } from './build-validator.js';\n\nconst DEFAULT_TYPECHECK_TIMEOUT_MS = 30_000;\nconst DEFAULT_BUILD_TIMEOUT_MS = 60_000;\n\n/**\n * Run fast deterministic checks: typecheck first, then build.\n * Short-circuits: if typecheck fails, skip build (build will fail too).\n */\nexport async function runQuickChecks(\n projectDir: string,\n options?: { skipBuild?: boolean; timeoutMs?: number },\n): Promise<QuickChecksOutput> {\n const startTime = Date.now();\n const results: QuickCheckResult[] = [];\n\n const typecheckResult = await runTypecheckValidation(projectDir, options?.timeoutMs ?? DEFAULT_TYPECHECK_TIMEOUT_MS);\n results.push(typecheckResult);\n\n if (typecheckResult.passed && !options?.skipBuild) {\n results.push(await runBuildQuickCheck(projectDir, options?.timeoutMs ?? DEFAULT_BUILD_TIMEOUT_MS));\n }\n\n const passed = results.every((r) => r.passed);\n\n return {\n passed,\n results,\n agentRetryPrompt: passed ? null : formatForAgent(results),\n totalDurationMs: Date.now() - startTime,\n };\n}\n\nfunction passResult(phase: QuickCheckResult['phase'], startTime: number): QuickCheckResult {\n return { passed: true, phase, issues: [], agentPrompt: null, durationMs: Date.now() - startTime };\n}\n\n/**\n * Run typecheck only (tsc --noEmit or framework equivalent).\n * Faster than full build — catches type errors in ~5s.\n */\nexport async function runTypecheckValidation(\n projectDir: string,\n timeoutMs: number = DEFAULT_TYPECHECK_TIMEOUT_MS,\n): Promise<QuickCheckResult> {\n const startTime = Date.now();\n const typecheckCmd = await detectTypecheckCommand(projectDir);\n\n if (!typecheckCmd) {\n return passResult('typecheck', startTime);\n }\n\n const { exitCode, stdout, stderr } = await spawnCommand(\n typecheckCmd.command,\n typecheckCmd.args,\n projectDir,\n timeoutMs,\n );\n\n if (exitCode === 0) {\n return passResult('typecheck', startTime);\n }\n\n const output = stdout + stderr;\n const errors = parseTypecheckErrors(output);\n const issues: ValidationIssue[] = errors.map((error) => ({\n type: 'file',\n severity: 'error',\n message: `Type error: ${error}`,\n hint: 'Fix the type error and run typecheck again',\n }));\n\n if (issues.length === 0) {\n issues.push({\n type: 'file',\n severity: 'error',\n message: 'Typecheck failed',\n hint: `Run \\`${typecheckCmd.command} ${typecheckCmd.args.join(' ')}\\` to see full output`,\n });\n }\n\n return {\n passed: false,\n phase: 'typecheck',\n issues,\n agentPrompt: formatTypecheckErrors(errors, output),\n durationMs: Date.now() - startTime,\n };\n}\n\nasync function runBuildQuickCheck(projectDir: string, timeoutMs: number): Promise<QuickCheckResult> {\n const startTime = Date.now();\n const buildCmd = await detectBuildCommand(projectDir);\n\n if (!buildCmd) {\n return passResult('build', startTime);\n }\n\n const { exitCode, stdout, stderr } = await spawnCommand(buildCmd.command, buildCmd.args, projectDir, timeoutMs);\n\n if (exitCode === 0) {\n return passResult('build', startTime);\n }\n\n const output = stdout + stderr;\n const errors = parseBuildErrors(output);\n const issues: ValidationIssue[] =\n errors.length > 0\n ? errors.map((e) => ({\n type: 'file',\n severity: 'error',\n message: `Build error: ${e}`,\n hint: 'Fix the error and run build again',\n }))\n : [\n {\n type: 'file',\n severity: 'error',\n message: 'Build failed',\n hint: `Run \\`${buildCmd.command} ${buildCmd.args.join(' ')}\\` to see full output`,\n },\n ];\n\n return {\n passed: false,\n phase: 'build',\n issues,\n agentPrompt: formatBuildErrors(issues),\n durationMs: Date.now() - startTime,\n };\n}\n\ninterface TypecheckCommand {\n command: string;\n args: string[];\n}\n\nasync function detectTypecheckCommand(projectDir: string): Promise<TypecheckCommand | null> {\n const pm = detectPackageManager(projectDir);\n\n try {\n const content = await readFile(join(projectDir, 'package.json'), 'utf-8');\n const pkg = JSON.parse(content) as { scripts?: Record<string, string> };\n\n const scriptName = pkg.scripts?.typecheck ? 'typecheck' : pkg.scripts?.['type-check'] ? 'type-check' : null;\n if (scriptName) {\n const args = pm === 'npm' ? ['run', scriptName] : [scriptName];\n return { command: pm, args };\n }\n } catch {\n // No package.json or malformed\n }\n\n try {\n await readFile(join(projectDir, 'tsconfig.json'), 'utf-8');\n return { command: 'npx', args: ['tsc', '--noEmit'] };\n } catch {\n return null;\n }\n}\n\nfunction parseTypecheckErrors(output: string): string[] {\n // Match both TS error formats:\n // src/file.ts(line,col): error TS2345: ...\n // src/file.ts:line:col - error TS2345: ... (tsc --pretty)\n const pattern = /[\\w./]+\\.\\w+(?:\\(\\d+,\\d+\\):\\s*|:\\d+:\\d+\\s*-\\s*)error\\s+TS\\d+:.+/g;\n const matches = output.match(pattern);\n return matches ? [...new Set(matches)].slice(0, 10) : [];\n}\n\nfunction formatTypecheckErrors(errors: string[], rawOutput: string): string {\n if (errors.length === 0) {\n // Couldn't parse specific errors — give raw output\n const truncated = rawOutput.slice(0, 2000);\n return `The typecheck failed. Here is the output:\\n\\n${truncated}\\n\\nFix the type errors shown above.`;\n }\n\n const lines = errors.map((error) => {\n // Extract file:line info and error description\n const fileMatch = error.match(/([\\w./]+\\.\\w+)[:(]\\d+/);\n const tsMatch = error.match(/error\\s+(TS\\d+):\\s*(.+)/);\n\n if (fileMatch && tsMatch) {\n return `- ${fileMatch[1]}: ${tsMatch[2]} (${tsMatch[1]})`;\n }\n return `- ${error}`;\n });\n\n return `The typecheck failed with ${errors.length} error${errors.length === 1 ? '' : 's'}:\\n\\n${lines.join('\\n')}\\n\\nFix these type errors in the indicated files.`;\n}\n\nfunction formatBuildErrors(issues: ValidationIssue[]): string {\n const errorMessages = issues.map((i) => `- ${i.message}`);\n return `The build failed:\\n\\n${errorMessages.join('\\n')}\\n\\nFix these build errors.`;\n}\n\nfunction formatForAgent(results: QuickCheckResult[]): string {\n return results\n .filter((r) => !r.passed && r.agentPrompt)\n .map((r) => r.agentPrompt!)\n .join('\\n\\n');\n}\n\n/**\n * Validation callback suitable for RetryConfig.validateAndFormat.\n * Returns null if checks pass, or an agent-ready error prompt if they fail.\n */\nexport async function quickCheckValidateAndFormat(workingDirectory: string): Promise<string | null> {\n const result = await runQuickChecks(workingDirectory);\n return result.passed ? null : result.agentRetryPrompt;\n}\n\nfunction spawnCommand(\n command: string,\n args: string[],\n cwd: string,\n timeoutMs: number,\n): Promise<{ exitCode: number; stdout: string; stderr: string }> {\n return new Promise((resolve) => {\n const proc = spawn(command, args, {\n cwd,\n shell: true,\n timeout: timeoutMs,\n });\n\n let stdout = '';\n let stderr = '';\n\n proc.stdout?.on('data', (data: Buffer) => {\n stdout += data.toString();\n });\n proc.stderr?.on('data', (data: Buffer) => {\n stderr += data.toString();\n });\n\n proc.on('close', (code) => {\n resolve({ exitCode: code ?? 1, stdout, stderr });\n });\n\n proc.on('error', () => {\n resolve({ exitCode: 1, stdout, stderr });\n });\n });\n}\n"]}
@@ -38,4 +38,19 @@ export interface ValidationRules {
38
38
  files: FileRule[];
39
39
  variants?: Record<string, VariantRules>;
40
40
  }
41
+ export interface QuickCheckResult {
42
+ passed: boolean;
43
+ phase: 'typecheck' | 'build';
44
+ issues: ValidationIssue[];
45
+ /** Formatted for agent consumption — actionable, not just error messages */
46
+ agentPrompt: string | null;
47
+ durationMs: number;
48
+ }
49
+ export interface QuickChecksOutput {
50
+ passed: boolean;
51
+ results: QuickCheckResult[];
52
+ /** Combined agent-ready prompt summarizing all failures */
53
+ agentRetryPrompt: string | null;
54
+ totalDurationMs: number;
55
+ }
41
56
  export type { BuildResult } from './build-validator.js';
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/lib/validation/types.ts"],"names":[],"mappings":"","sourcesContent":["export type ValidationSeverity = 'error' | 'warning';\nexport type ValidationIssueType = 'package' | 'env' | 'file' | 'pattern';\n\nexport interface ValidationIssue {\n type: ValidationIssueType;\n severity: ValidationSeverity;\n message: string;\n hint?: string; // How to fix\n}\n\nexport interface ValidationResult {\n passed: boolean;\n framework: string;\n issues: ValidationIssue[];\n durationMs: number;\n}\n\n// Rule definitions (matches JSON schema)\nexport interface PackageRule {\n name: string;\n location?: 'dependencies' | 'devDependencies' | 'any'; // default: 'any'\n}\n\nexport interface EnvVarRule {\n name: string;\n required?: boolean; // default: true\n alternates?: string[]; // alternate env var names (e.g., VITE_WORKOS_CLIENT_ID)\n}\n\nexport interface FileRule {\n path: string; // glob pattern, e.g., \"middleware.ts\" or \"app/**/callback/**/route.ts\"\n mustContain?: string[]; // strings that must appear in file\n mustContainAny?: string[]; // at least one must appear\n}\n\nexport interface VariantRules {\n files?: FileRule[];\n packages?: PackageRule[];\n envVars?: EnvVarRule[];\n}\n\nexport interface ValidationRules {\n framework: string;\n packages: PackageRule[];\n envVars: EnvVarRule[];\n files: FileRule[];\n variants?: Record<string, VariantRules>;\n}\n\n// Re-export BuildResult from build-validator\nexport type { BuildResult } from './build-validator.js';\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/lib/validation/types.ts"],"names":[],"mappings":"","sourcesContent":["export type ValidationSeverity = 'error' | 'warning';\nexport type ValidationIssueType = 'package' | 'env' | 'file' | 'pattern';\n\nexport interface ValidationIssue {\n type: ValidationIssueType;\n severity: ValidationSeverity;\n message: string;\n hint?: string; // How to fix\n}\n\nexport interface ValidationResult {\n passed: boolean;\n framework: string;\n issues: ValidationIssue[];\n durationMs: number;\n}\n\n// Rule definitions (matches JSON schema)\nexport interface PackageRule {\n name: string;\n location?: 'dependencies' | 'devDependencies' | 'any'; // default: 'any'\n}\n\nexport interface EnvVarRule {\n name: string;\n required?: boolean; // default: true\n alternates?: string[]; // alternate env var names (e.g., VITE_WORKOS_CLIENT_ID)\n}\n\nexport interface FileRule {\n path: string; // glob pattern, e.g., \"middleware.ts\" or \"app/**/callback/**/route.ts\"\n mustContain?: string[]; // strings that must appear in file\n mustContainAny?: string[]; // at least one must appear\n}\n\nexport interface VariantRules {\n files?: FileRule[];\n packages?: PackageRule[];\n envVars?: EnvVarRule[];\n}\n\nexport interface ValidationRules {\n framework: string;\n packages: PackageRule[];\n envVars: EnvVarRule[];\n files: FileRule[];\n variants?: Record<string, VariantRules>;\n}\n\nexport interface QuickCheckResult {\n passed: boolean;\n phase: 'typecheck' | 'build';\n issues: ValidationIssue[];\n /** Formatted for agent consumption — actionable, not just error messages */\n agentPrompt: string | null;\n durationMs: number;\n}\n\nexport interface QuickChecksOutput {\n passed: boolean;\n results: QuickCheckResult[];\n /** Combined agent-ready prompt summarizing all failures */\n agentRetryPrompt: string | null;\n totalDurationMs: number;\n}\n\n// Re-export BuildResult from build-validator\nexport type { BuildResult } from './build-validator.js';\n"]}
@@ -1,6 +1,13 @@
1
- import type { ValidationResult } from './types.js';
1
+ import type { ValidationResult, ValidationRules, ValidationIssue } from './types.js';
2
2
  export interface ValidateOptions {
3
3
  variant?: string;
4
4
  runBuild?: boolean;
5
5
  }
6
6
  export declare function validateInstallation(framework: string, projectDir: string, options?: ValidateOptions): Promise<ValidationResult>;
7
+ export declare function validatePackages(rules: ValidationRules, projectDir: string): Promise<ValidationIssue[]>;
8
+ export declare function validateEnvVars(rules: ValidationRules, projectDir: string): Promise<ValidationIssue[]>;
9
+ export declare function validateFiles(rules: ValidationRules, projectDir: string): Promise<ValidationIssue[]>;
10
+ /**
11
+ * Framework-specific cross-validations that require reading multiple sources.
12
+ */
13
+ export declare function validateFrameworkSpecific(framework: string, projectDir: string): Promise<ValidationIssue[]>;
@@ -17,11 +17,11 @@ export async function validateInstallation(framework, projectDir, options = {})
17
17
  };
18
18
  }
19
19
  // Run validations
20
- await validatePackages(rules, projectDir, issues);
21
- await validateEnvVars(rules, projectDir, issues);
22
- await validateFiles(rules, projectDir, issues);
20
+ issues.push(...(await validatePackages(rules, projectDir)));
21
+ issues.push(...(await validateEnvVars(rules, projectDir)));
22
+ issues.push(...(await validateFiles(rules, projectDir)));
23
23
  // Run framework-specific cross-validations
24
- await validateFrameworkSpecific(framework, projectDir, issues);
24
+ issues.push(...(await validateFrameworkSpecific(framework, projectDir)));
25
25
  // Run build validation if enabled
26
26
  if (options.runBuild !== false) {
27
27
  const buildResult = await runBuildValidation(projectDir);
@@ -55,17 +55,18 @@ async function loadRules(framework, variant) {
55
55
  return null; // No rules for this framework yet
56
56
  }
57
57
  }
58
- async function validatePackages(rules, projectDir, issues) {
58
+ export async function validatePackages(rules, projectDir) {
59
+ const issues = [];
59
60
  const pkgPath = join(projectDir, 'package.json');
60
61
  if (!existsSync(pkgPath))
61
- return;
62
+ return issues;
62
63
  let pkg;
63
64
  try {
64
65
  pkg = JSON.parse(await readFile(pkgPath, 'utf-8'));
65
66
  }
66
67
  catch {
67
68
  // Malformed package.json - skip package validation
68
- return;
69
+ return issues;
69
70
  }
70
71
  const deps = (pkg.dependencies || {});
71
72
  const devDeps = (pkg.devDependencies || {});
@@ -82,8 +83,10 @@ async function validatePackages(rules, projectDir, issues) {
82
83
  });
83
84
  }
84
85
  }
86
+ return issues;
85
87
  }
86
- async function validateEnvVars(rules, projectDir, issues) {
88
+ export async function validateEnvVars(rules, projectDir) {
89
+ const issues = [];
87
90
  const envPath = join(projectDir, '.env.local');
88
91
  let envContent = '';
89
92
  try {
@@ -98,7 +101,7 @@ async function validateEnvVars(rules, projectDir, issues) {
98
101
  hint: 'Create .env.local with required environment variables',
99
102
  });
100
103
  }
101
- return;
104
+ return issues;
102
105
  }
103
106
  for (const rule of rules.envVars) {
104
107
  // Check primary name and any alternates
@@ -119,8 +122,10 @@ async function validateEnvVars(rules, projectDir, issues) {
119
122
  });
120
123
  }
121
124
  }
125
+ return issues;
122
126
  }
123
- async function validateFiles(rules, projectDir, issues) {
127
+ export async function validateFiles(rules, projectDir) {
128
+ const issues = [];
124
129
  for (const rule of rules.files) {
125
130
  let matches;
126
131
  try {
@@ -177,11 +182,13 @@ async function validateFiles(rules, projectDir, issues) {
177
182
  }
178
183
  }
179
184
  }
185
+ return issues;
180
186
  }
181
187
  /**
182
188
  * Framework-specific cross-validations that require reading multiple sources.
183
189
  */
184
- async function validateFrameworkSpecific(framework, projectDir, issues) {
190
+ export async function validateFrameworkSpecific(framework, projectDir) {
191
+ const issues = [];
185
192
  // Universal cross-validations
186
193
  await validateCredentialFormats(projectDir, issues);
187
194
  await validateDuplicateEnvVars(projectDir, issues);
@@ -204,6 +211,7 @@ async function validateFrameworkSpecific(framework, projectDir, issues) {
204
211
  await validateCookiePasswordLength(projectDir, issues, 'WORKOS_COOKIE_PASSWORD');
205
212
  break;
206
213
  }
214
+ return issues;
207
215
  }
208
216
  /**
209
217
  * Validates that the Next.js redirect URI matches an existing callback route.