@yasserkhanorg/e2e-agents 1.0.1 → 1.1.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.
@@ -1 +1 @@
1
- {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/agent/git.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,eAAe;IAC5B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,YAAY,GAAG,QAAQ,CAAC;CAC1C;AAED,MAAM,WAAW,gBAAgB;IAC7B,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAChC;AA8CD,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,eAAe,CAuC3G"}
1
+ {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/agent/git.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,eAAe;IAC5B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,YAAY,GAAG,QAAQ,CAAC;CAC1C;AAED,MAAM,WAAW,gBAAgB;IAC7B,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAChC;AA8CD,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,eAAe,CA2C3G"}
package/dist/agent/git.js CHANGED
@@ -66,17 +66,20 @@ function getChangedFiles(appRoot, since, options) {
66
66
  baseStrategy = 'merge-base';
67
67
  }
68
68
  }
69
- const diffFiles = runGit(['diff', '--name-only', `${baseRef}..HEAD`, '--', '.'], appRoot);
69
+ // Get repo root so we capture ALL changed files (including server/, webapp/, etc.)
70
+ // not just files under the appRoot subdirectory.
71
+ const repoRoot = runGitRaw(['rev-parse', '--show-toplevel'], appRoot)?.trim() || appRoot;
72
+ const diffFiles = runGit(['diff', '--name-only', `${baseRef}..HEAD`], repoRoot);
70
73
  if (!diffFiles) {
71
74
  return { files: [], error: 'git diff failed' };
72
75
  }
73
76
  diffFiles.forEach((file) => files.add(file));
74
77
  if (options?.includeUncommitted) {
75
- const staged = runGit(['diff', '--name-only', '--cached', '--', '.'], appRoot) || [];
78
+ const staged = runGit(['diff', '--name-only', '--cached'], repoRoot) || [];
76
79
  staged.forEach((file) => files.add(file));
77
- const unstaged = runGit(['diff', '--name-only', '--', '.'], appRoot) || [];
80
+ const unstaged = runGit(['diff', '--name-only'], repoRoot) || [];
78
81
  unstaged.forEach((file) => files.add(file));
79
- const statusOutput = runGitRaw(['status', '--porcelain', '--', '.'], appRoot);
82
+ const statusOutput = runGitRaw(['status', '--porcelain'], repoRoot);
80
83
  if (statusOutput) {
81
84
  const statusLines = statusOutput.split('\n').filter(Boolean);
82
85
  parseStatusLines(statusLines).forEach((file) => files.add(file));
@@ -0,0 +1,2 @@
1
+ export declare function runInitCommand(yes?: boolean): Promise<void>;
2
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"AAgIA,wBAAsB,cAAc,CAAC,GAAG,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CA6F/D"}
@@ -0,0 +1,220 @@
1
+ "use strict";
2
+ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
3
+ // See LICENSE.txt for license information.
4
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
5
+ if (k2 === undefined) k2 = k;
6
+ var desc = Object.getOwnPropertyDescriptor(m, k);
7
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
8
+ desc = { enumerable: true, get: function() { return m[k]; } };
9
+ }
10
+ Object.defineProperty(o, k2, desc);
11
+ }) : (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ o[k2] = m[k];
14
+ }));
15
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
16
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
17
+ }) : function(o, v) {
18
+ o["default"] = v;
19
+ });
20
+ var __importStar = (this && this.__importStar) || (function () {
21
+ var ownKeys = function(o) {
22
+ ownKeys = Object.getOwnPropertyNames || function (o) {
23
+ var ar = [];
24
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
25
+ return ar;
26
+ };
27
+ return ownKeys(o);
28
+ };
29
+ return function (mod) {
30
+ if (mod && mod.__esModule) return mod;
31
+ var result = {};
32
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
33
+ __setModuleDefault(result, mod);
34
+ return result;
35
+ };
36
+ })();
37
+ Object.defineProperty(exports, "__esModule", { value: true });
38
+ exports.runInitCommand = runInitCommand;
39
+ const fs_1 = require("fs");
40
+ const child_process_1 = require("child_process");
41
+ const path_1 = require("path");
42
+ const readline = __importStar(require("readline"));
43
+ const CONFIG_FILENAME = 'e2e-ai-agents.config.json';
44
+ function createInterface() {
45
+ return readline.createInterface({
46
+ input: process.stdin,
47
+ output: process.stdout,
48
+ });
49
+ }
50
+ function ask(rl, question, defaultValue) {
51
+ const suffix = defaultValue ? ` (${defaultValue})` : '';
52
+ return new Promise((resolve) => {
53
+ rl.question(`${question}${suffix}: `, (answer) => {
54
+ resolve(answer.trim() || defaultValue || '');
55
+ });
56
+ });
57
+ }
58
+ function detectFramework(appPath) {
59
+ const resolvedPath = (0, path_1.resolve)(appPath);
60
+ const pkgPath = (0, path_1.join)(resolvedPath, 'package.json');
61
+ if (!(0, fs_1.existsSync)(pkgPath)) {
62
+ return 'auto';
63
+ }
64
+ try {
65
+ const pkg = JSON.parse((0, fs_1.readFileSync)(pkgPath, 'utf-8'));
66
+ const allDeps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
67
+ if (allDeps['@playwright/test'] || allDeps.playwright) {
68
+ return 'playwright';
69
+ }
70
+ if (allDeps.cypress) {
71
+ return 'cypress';
72
+ }
73
+ if (allDeps['selenium-webdriver'] || allDeps.webdriverio) {
74
+ return 'selenium';
75
+ }
76
+ }
77
+ catch {
78
+ // ignore
79
+ }
80
+ return 'auto';
81
+ }
82
+ function detectGitDefaultBranch(appPath) {
83
+ try {
84
+ const result = (0, child_process_1.execFileSync)('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
85
+ cwd: (0, path_1.resolve)(appPath),
86
+ encoding: 'utf-8',
87
+ stdio: ['pipe', 'pipe', 'pipe'],
88
+ }).trim();
89
+ return `origin/${result}`;
90
+ }
91
+ catch {
92
+ return 'origin/main';
93
+ }
94
+ }
95
+ function detectTestsRoot(appPath) {
96
+ const resolvedPath = (0, path_1.resolve)(appPath);
97
+ const candidates = [
98
+ 'e2e-tests/playwright',
99
+ 'e2e-tests',
100
+ 'e2e',
101
+ 'tests/e2e',
102
+ 'test/e2e',
103
+ 'tests',
104
+ 'test',
105
+ ];
106
+ for (const candidate of candidates) {
107
+ if ((0, fs_1.existsSync)((0, path_1.join)(resolvedPath, candidate))) {
108
+ return candidate;
109
+ }
110
+ }
111
+ return undefined;
112
+ }
113
+ function buildConfig(answers) {
114
+ const config = {
115
+ path: answers.path,
116
+ framework: answers.framework,
117
+ git: { since: answers.gitSince },
118
+ impact: {
119
+ dependencyGraph: { enabled: true, maxDepth: 3 },
120
+ traceability: { enabled: true },
121
+ aiFlow: {
122
+ enabled: answers.enableAi,
123
+ provider: answers.enableAi ? answers.provider : undefined,
124
+ },
125
+ },
126
+ policy: {
127
+ enforcementMode: answers.enforcementMode,
128
+ blockOnActions: ['must-add-tests'],
129
+ },
130
+ };
131
+ if (answers.testsRoot && answers.testsRoot !== '.' && answers.testsRoot !== answers.path) {
132
+ config.testsRoot = answers.testsRoot;
133
+ }
134
+ return config;
135
+ }
136
+ function printNextSteps() {
137
+ console.log('');
138
+ console.log(' Next steps:');
139
+ console.log(' 1. Set your API key: export ANTHROPIC_API_KEY=sk-ant-...');
140
+ console.log(' 2. Test connectivity: npx e2e-ai-agents llm-health');
141
+ console.log(' 3. Run impact analysis: npx e2e-ai-agents impact');
142
+ console.log(' 4. Add to CI: see examples/github-actions/pr-impact.yml');
143
+ console.log('');
144
+ }
145
+ async function runInitCommand(yes = false) {
146
+ const targetDir = process.cwd();
147
+ const configPath = (0, path_1.join)(targetDir, CONFIG_FILENAME);
148
+ if ((0, fs_1.existsSync)(configPath)) {
149
+ console.error(`${CONFIG_FILENAME} already exists in this directory.`);
150
+ console.error('Remove it first if you want to re-initialize.');
151
+ process.exit(1);
152
+ }
153
+ // Non-interactive mode: auto-detect everything and write immediately
154
+ if (yes) {
155
+ const appPath = '.';
156
+ const answers = {
157
+ path: appPath,
158
+ testsRoot: detectTestsRoot(appPath) || '.',
159
+ framework: detectFramework(appPath),
160
+ gitSince: detectGitDefaultBranch(appPath),
161
+ provider: 'auto',
162
+ enableAi: true,
163
+ enforcementMode: 'advisory',
164
+ };
165
+ const config = buildConfig(answers);
166
+ const json = JSON.stringify(config, null, 2) + '\n';
167
+ (0, fs_1.writeFileSync)(configPath, json, 'utf-8');
168
+ console.log(`Created ${CONFIG_FILENAME}`);
169
+ printNextSteps();
170
+ return;
171
+ }
172
+ console.log('');
173
+ console.log(' e2e-ai-agents init');
174
+ console.log(' ==================');
175
+ console.log('');
176
+ console.log(' This will create an e2e-ai-agents.config.json in the current directory.');
177
+ console.log('');
178
+ const rl = createInterface();
179
+ try {
180
+ const appPath = await ask(rl, ' Path to your web app root', '.');
181
+ const detectedFramework = detectFramework(appPath);
182
+ const framework = await ask(rl, ' Test framework (auto | playwright | cypress | selenium)', detectedFramework);
183
+ const detectedTestsRoot = detectTestsRoot(appPath);
184
+ const testsRoot = await ask(rl, ' Path to tests root (relative to app root, "." if same)', detectedTestsRoot || '.');
185
+ const detectedBranch = detectGitDefaultBranch(appPath);
186
+ const gitSince = await ask(rl, ' Git ref to diff against', detectedBranch);
187
+ const providerAnswer = await ask(rl, ' LLM provider for AI features (anthropic | openai | ollama | auto)', 'auto');
188
+ const enableAi = providerAnswer !== 'none';
189
+ const enforcementMode = await ask(rl, ' Policy enforcement mode (advisory | warn | block)', 'advisory');
190
+ const answers = {
191
+ path: appPath,
192
+ testsRoot,
193
+ framework,
194
+ gitSince,
195
+ provider: providerAnswer,
196
+ enableAi,
197
+ enforcementMode,
198
+ };
199
+ const config = buildConfig(answers);
200
+ const json = JSON.stringify(config, null, 2) + '\n';
201
+ console.log('');
202
+ console.log(' Generated config:');
203
+ console.log('');
204
+ for (const line of json.split('\n')) {
205
+ console.log(` ${line}`);
206
+ }
207
+ const confirm = await ask(rl, ' Write this config? (Y/n)', 'Y');
208
+ if (confirm.toLowerCase() !== 'y' && confirm !== '') {
209
+ console.log(' Aborted.');
210
+ process.exit(0);
211
+ }
212
+ (0, fs_1.writeFileSync)(configPath, json, 'utf-8');
213
+ console.log('');
214
+ console.log(` Created ${CONFIG_FILENAME}`);
215
+ printNextSteps();
216
+ }
217
+ finally {
218
+ rl.close();
219
+ }
220
+ }
@@ -137,7 +137,7 @@ for (const [flag, def] of Object.entries(FLAGS)) {
137
137
  }
138
138
  }
139
139
  const COMMANDS = new Set([
140
- 'impact', 'plan', 'heal', 'suggest', 'generate',
140
+ 'init', 'impact', 'plan', 'heal', 'suggest', 'generate',
141
141
  'finalize-generated-tests', 'feedback',
142
142
  'traceability-capture', 'traceability-ingest',
143
143
  'analyze', 'llm-health',
@@ -1,5 +1,5 @@
1
1
  import type { AnalysisProfile, FrameworkType } from '../agent/config.js';
2
- export type Command = 'impact' | 'plan' | 'heal' | 'suggest' | 'generate' | 'finalize-generated-tests' | 'feedback' | 'traceability-capture' | 'traceability-ingest' | 'analyze' | 'llm-health';
2
+ export type Command = 'init' | 'impact' | 'plan' | 'heal' | 'suggest' | 'generate' | 'finalize-generated-tests' | 'feedback' | 'traceability-capture' | 'traceability-ingest' | 'analyze' | 'llm-health';
3
3
  export interface ParsedArgs {
4
4
  command?: Command;
5
5
  configPath?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/cli/types.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,eAAe,EAAE,aAAa,EAAC,MAAM,oBAAoB,CAAC;AAEvE,MAAM,MAAM,OAAO,GACf,QAAQ,GACN,MAAM,GACN,MAAM,GACN,SAAS,GACT,UAAU,GACV,0BAA0B,GAC1B,UAAU,GACV,sBAAsB,GACtB,qBAAqB,GACrB,SAAS,GACT,YAAY,CAAC;AAEnB,MAAM,WAAW,UAAU;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,QAAQ,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC/D,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,qBAAqB,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC;IACtD,kBAAkB,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,gBAAgB,GAAG,eAAe,CAAC,CAAC;IAC3E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,6BAA6B,CAAC,EAAE,MAAM,CAAC;IACvC,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,4BAA4B,CAAC,EAAE,MAAM,CAAC;IACtC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC9B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/cli/types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../src/cli/usage.ts"],"names":[],"mappings":"AAGA,wBAAgB,UAAU,IAAI,IAAI,CAkFjC"}
1
+ {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../src/cli/usage.ts"],"names":[],"mappings":"AAGA,wBAAgB,UAAU,IAAI,IAAI,CAmFjC"}
package/dist/cli/usage.js CHANGED
@@ -6,6 +6,7 @@ exports.printUsage = printUsage;
6
6
  function printUsage() {
7
7
  console.log([
8
8
  'Usage:',
9
+ ' e2e-ai-agents init [--yes]',
9
10
  ' e2e-ai-agents impact --path <app-root> [options]',
10
11
  ' e2e-ai-agents plan --path <app-root> [options]',
11
12
  ' e2e-ai-agents suggest --path <app-root> [options]',
package/dist/cli.js CHANGED
@@ -15,9 +15,15 @@ const heal_js_1 = require("./cli/commands/heal.js");
15
15
  const impact_js_1 = require("./cli/commands/impact.js");
16
16
  const plan_js_1 = require("./cli/commands/plan.js");
17
17
  const generate_js_1 = require("./cli/commands/generate.js");
18
+ const init_js_1 = require("./cli/commands/init.js");
18
19
  async function main() {
19
20
  const args = (0, parse_args_js_1.parseArgs)(process.argv.slice(2));
20
21
  const autoConfig = (0, parse_args_js_1.resolveAutoConfig)(args);
22
+ if (args.command === 'init') {
23
+ const hasYes = process.argv.includes('--yes') || process.argv.includes('-y');
24
+ await (0, init_js_1.runInitCommand)(hasYes);
25
+ return;
26
+ }
21
27
  if (args.help || !args.command) {
22
28
  (0, usage_js_1.printUsage)();
23
29
  process.exit(args.command ? 0 : 1);
@@ -63,17 +63,20 @@ export function getChangedFiles(appRoot, since, options) {
63
63
  baseStrategy = 'merge-base';
64
64
  }
65
65
  }
66
- const diffFiles = runGit(['diff', '--name-only', `${baseRef}..HEAD`, '--', '.'], appRoot);
66
+ // Get repo root so we capture ALL changed files (including server/, webapp/, etc.)
67
+ // not just files under the appRoot subdirectory.
68
+ const repoRoot = runGitRaw(['rev-parse', '--show-toplevel'], appRoot)?.trim() || appRoot;
69
+ const diffFiles = runGit(['diff', '--name-only', `${baseRef}..HEAD`], repoRoot);
67
70
  if (!diffFiles) {
68
71
  return { files: [], error: 'git diff failed' };
69
72
  }
70
73
  diffFiles.forEach((file) => files.add(file));
71
74
  if (options?.includeUncommitted) {
72
- const staged = runGit(['diff', '--name-only', '--cached', '--', '.'], appRoot) || [];
75
+ const staged = runGit(['diff', '--name-only', '--cached'], repoRoot) || [];
73
76
  staged.forEach((file) => files.add(file));
74
- const unstaged = runGit(['diff', '--name-only', '--', '.'], appRoot) || [];
77
+ const unstaged = runGit(['diff', '--name-only'], repoRoot) || [];
75
78
  unstaged.forEach((file) => files.add(file));
76
- const statusOutput = runGitRaw(['status', '--porcelain', '--', '.'], appRoot);
79
+ const statusOutput = runGitRaw(['status', '--porcelain'], repoRoot);
77
80
  if (statusOutput) {
78
81
  const statusLines = statusOutput.split('\n').filter(Boolean);
79
82
  parseStatusLines(statusLines).forEach((file) => files.add(file));
@@ -0,0 +1,184 @@
1
+ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2
+ // See LICENSE.txt for license information.
3
+ import { existsSync, writeFileSync, readFileSync } from 'fs';
4
+ import { execFileSync } from 'child_process';
5
+ import { join, resolve } from 'path';
6
+ import * as readline from 'readline';
7
+ const CONFIG_FILENAME = 'e2e-ai-agents.config.json';
8
+ function createInterface() {
9
+ return readline.createInterface({
10
+ input: process.stdin,
11
+ output: process.stdout,
12
+ });
13
+ }
14
+ function ask(rl, question, defaultValue) {
15
+ const suffix = defaultValue ? ` (${defaultValue})` : '';
16
+ return new Promise((resolve) => {
17
+ rl.question(`${question}${suffix}: `, (answer) => {
18
+ resolve(answer.trim() || defaultValue || '');
19
+ });
20
+ });
21
+ }
22
+ function detectFramework(appPath) {
23
+ const resolvedPath = resolve(appPath);
24
+ const pkgPath = join(resolvedPath, 'package.json');
25
+ if (!existsSync(pkgPath)) {
26
+ return 'auto';
27
+ }
28
+ try {
29
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
30
+ const allDeps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
31
+ if (allDeps['@playwright/test'] || allDeps.playwright) {
32
+ return 'playwright';
33
+ }
34
+ if (allDeps.cypress) {
35
+ return 'cypress';
36
+ }
37
+ if (allDeps['selenium-webdriver'] || allDeps.webdriverio) {
38
+ return 'selenium';
39
+ }
40
+ }
41
+ catch {
42
+ // ignore
43
+ }
44
+ return 'auto';
45
+ }
46
+ function detectGitDefaultBranch(appPath) {
47
+ try {
48
+ const result = execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
49
+ cwd: resolve(appPath),
50
+ encoding: 'utf-8',
51
+ stdio: ['pipe', 'pipe', 'pipe'],
52
+ }).trim();
53
+ return `origin/${result}`;
54
+ }
55
+ catch {
56
+ return 'origin/main';
57
+ }
58
+ }
59
+ function detectTestsRoot(appPath) {
60
+ const resolvedPath = resolve(appPath);
61
+ const candidates = [
62
+ 'e2e-tests/playwright',
63
+ 'e2e-tests',
64
+ 'e2e',
65
+ 'tests/e2e',
66
+ 'test/e2e',
67
+ 'tests',
68
+ 'test',
69
+ ];
70
+ for (const candidate of candidates) {
71
+ if (existsSync(join(resolvedPath, candidate))) {
72
+ return candidate;
73
+ }
74
+ }
75
+ return undefined;
76
+ }
77
+ function buildConfig(answers) {
78
+ const config = {
79
+ path: answers.path,
80
+ framework: answers.framework,
81
+ git: { since: answers.gitSince },
82
+ impact: {
83
+ dependencyGraph: { enabled: true, maxDepth: 3 },
84
+ traceability: { enabled: true },
85
+ aiFlow: {
86
+ enabled: answers.enableAi,
87
+ provider: answers.enableAi ? answers.provider : undefined,
88
+ },
89
+ },
90
+ policy: {
91
+ enforcementMode: answers.enforcementMode,
92
+ blockOnActions: ['must-add-tests'],
93
+ },
94
+ };
95
+ if (answers.testsRoot && answers.testsRoot !== '.' && answers.testsRoot !== answers.path) {
96
+ config.testsRoot = answers.testsRoot;
97
+ }
98
+ return config;
99
+ }
100
+ function printNextSteps() {
101
+ console.log('');
102
+ console.log(' Next steps:');
103
+ console.log(' 1. Set your API key: export ANTHROPIC_API_KEY=sk-ant-...');
104
+ console.log(' 2. Test connectivity: npx e2e-ai-agents llm-health');
105
+ console.log(' 3. Run impact analysis: npx e2e-ai-agents impact');
106
+ console.log(' 4. Add to CI: see examples/github-actions/pr-impact.yml');
107
+ console.log('');
108
+ }
109
+ export async function runInitCommand(yes = false) {
110
+ const targetDir = process.cwd();
111
+ const configPath = join(targetDir, CONFIG_FILENAME);
112
+ if (existsSync(configPath)) {
113
+ console.error(`${CONFIG_FILENAME} already exists in this directory.`);
114
+ console.error('Remove it first if you want to re-initialize.');
115
+ process.exit(1);
116
+ }
117
+ // Non-interactive mode: auto-detect everything and write immediately
118
+ if (yes) {
119
+ const appPath = '.';
120
+ const answers = {
121
+ path: appPath,
122
+ testsRoot: detectTestsRoot(appPath) || '.',
123
+ framework: detectFramework(appPath),
124
+ gitSince: detectGitDefaultBranch(appPath),
125
+ provider: 'auto',
126
+ enableAi: true,
127
+ enforcementMode: 'advisory',
128
+ };
129
+ const config = buildConfig(answers);
130
+ const json = JSON.stringify(config, null, 2) + '\n';
131
+ writeFileSync(configPath, json, 'utf-8');
132
+ console.log(`Created ${CONFIG_FILENAME}`);
133
+ printNextSteps();
134
+ return;
135
+ }
136
+ console.log('');
137
+ console.log(' e2e-ai-agents init');
138
+ console.log(' ==================');
139
+ console.log('');
140
+ console.log(' This will create an e2e-ai-agents.config.json in the current directory.');
141
+ console.log('');
142
+ const rl = createInterface();
143
+ try {
144
+ const appPath = await ask(rl, ' Path to your web app root', '.');
145
+ const detectedFramework = detectFramework(appPath);
146
+ const framework = await ask(rl, ' Test framework (auto | playwright | cypress | selenium)', detectedFramework);
147
+ const detectedTestsRoot = detectTestsRoot(appPath);
148
+ const testsRoot = await ask(rl, ' Path to tests root (relative to app root, "." if same)', detectedTestsRoot || '.');
149
+ const detectedBranch = detectGitDefaultBranch(appPath);
150
+ const gitSince = await ask(rl, ' Git ref to diff against', detectedBranch);
151
+ const providerAnswer = await ask(rl, ' LLM provider for AI features (anthropic | openai | ollama | auto)', 'auto');
152
+ const enableAi = providerAnswer !== 'none';
153
+ const enforcementMode = await ask(rl, ' Policy enforcement mode (advisory | warn | block)', 'advisory');
154
+ const answers = {
155
+ path: appPath,
156
+ testsRoot,
157
+ framework,
158
+ gitSince,
159
+ provider: providerAnswer,
160
+ enableAi,
161
+ enforcementMode,
162
+ };
163
+ const config = buildConfig(answers);
164
+ const json = JSON.stringify(config, null, 2) + '\n';
165
+ console.log('');
166
+ console.log(' Generated config:');
167
+ console.log('');
168
+ for (const line of json.split('\n')) {
169
+ console.log(` ${line}`);
170
+ }
171
+ const confirm = await ask(rl, ' Write this config? (Y/n)', 'Y');
172
+ if (confirm.toLowerCase() !== 'y' && confirm !== '') {
173
+ console.log(' Aborted.');
174
+ process.exit(0);
175
+ }
176
+ writeFileSync(configPath, json, 'utf-8');
177
+ console.log('');
178
+ console.log(` Created ${CONFIG_FILENAME}`);
179
+ printNextSteps();
180
+ }
181
+ finally {
182
+ rl.close();
183
+ }
184
+ }
@@ -131,7 +131,7 @@ for (const [flag, def] of Object.entries(FLAGS)) {
131
131
  }
132
132
  }
133
133
  const COMMANDS = new Set([
134
- 'impact', 'plan', 'heal', 'suggest', 'generate',
134
+ 'init', 'impact', 'plan', 'heal', 'suggest', 'generate',
135
135
  'finalize-generated-tests', 'feedback',
136
136
  'traceability-capture', 'traceability-ingest',
137
137
  'analyze', 'llm-health',
@@ -3,6 +3,7 @@
3
3
  export function printUsage() {
4
4
  console.log([
5
5
  'Usage:',
6
+ ' e2e-ai-agents init [--yes]',
6
7
  ' e2e-ai-agents impact --path <app-root> [options]',
7
8
  ' e2e-ai-agents plan --path <app-root> [options]',
8
9
  ' e2e-ai-agents suggest --path <app-root> [options]',
package/dist/esm/cli.js CHANGED
@@ -13,9 +13,15 @@ import { runHealCommand } from './cli/commands/heal.js';
13
13
  import { runImpactCommand } from './cli/commands/impact.js';
14
14
  import { runPlanCommand } from './cli/commands/plan.js';
15
15
  import { runGenerateCommand } from './cli/commands/generate.js';
16
+ import { runInitCommand } from './cli/commands/init.js';
16
17
  async function main() {
17
18
  const args = parseArgs(process.argv.slice(2));
18
19
  const autoConfig = resolveAutoConfig(args);
20
+ if (args.command === 'init') {
21
+ const hasYes = process.argv.includes('--yes') || process.argv.includes('-y');
22
+ await runInitCommand(hasYes);
23
+ return;
24
+ }
19
25
  if (args.help || !args.command) {
20
26
  printUsage();
21
27
  process.exit(args.command ? 0 : 1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yasserkhanorg/e2e-agents",
3
- "version": "1.0.1",
3
+ "version": "1.1.1",
4
4
  "description": "AI-powered E2E test impact analysis, generation, and healing. Analyzes code changes to identify affected Playwright tests, detects coverage gaps, and generates or repairs specs using pluggable LLM providers (Claude, OpenAI, Ollama). Includes MCP server, traceability, and CI/CD integration.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/esm/index.js",