playwright-intelligence-engine 2.0.0 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +229 -229
  2. package/dist/cli/index.d.ts.map +1 -1
  3. package/dist/core/version.d.ts +3 -3
  4. package/dist/index.js +81 -24
  5. package/package.json +76 -76
  6. package/src/authoring/author.ts +81 -81
  7. package/src/authoring/generation-contract.ts +283 -283
  8. package/src/authoring/hybrid-author.ts +115 -115
  9. package/src/authoring/index.ts +7 -7
  10. package/src/authoring/llm-author.ts +282 -282
  11. package/src/authoring/naming/name-plan.ts +28 -28
  12. package/src/authoring/naming/name-planner.ts +339 -339
  13. package/src/authoring/naming/naming-linter.ts +292 -292
  14. package/src/cli/index.ts +1238 -1172
  15. package/src/collectors/git-diff-collector.ts +190 -190
  16. package/src/core/version.ts +25 -25
  17. package/src/diagnostics/diagnostic-report.ts +204 -204
  18. package/src/diagnostics/failure-classifier.ts +701 -701
  19. package/src/evaluation/repository-runner.ts +197 -197
  20. package/src/gate/quality.ts +101 -101
  21. package/src/generator/architecture-generator.ts +582 -582
  22. package/src/generator/assertion-quality-gate.ts +88 -88
  23. package/src/generator/candidate-writer.ts +288 -288
  24. package/src/generator/test-generator.ts +52 -52
  25. package/src/index.ts +107 -107
  26. package/src/intelligence/evidence-bundle.ts +300 -300
  27. package/src/intelligence/generation-agent.ts +561 -561
  28. package/src/intelligence/generation-verifier.ts +1115 -1115
  29. package/src/intelligence/locator-disambiguation-engine.ts +331 -331
  30. package/src/intelligence/project-intelligence.ts +140 -140
  31. package/src/intelligence/repository-profile.ts +796 -796
  32. package/src/intelligence/trace-proposal-workflow.ts +318 -318
  33. package/src/intelligence/workflow-planner.ts +966 -966
  34. package/src/mcp/server.ts +970 -970
  35. package/src/perception/app-model.ts +32 -32
  36. package/src/perception/crawler.ts +263 -263
  37. package/src/perception/index.ts +3 -3
  38. package/src/repair/assertion-hasher.ts +386 -386
  39. package/src/repair/diff-allowlist.ts +612 -612
  40. package/src/repair/intent-validator.ts +761 -761
  41. package/src/repair/repair-planner.ts +467 -467
  42. package/src/repair/statement-patcher.ts +544 -544
  43. package/src/reproduce/interactive-workflow-runner.ts +828 -828
  44. package/src/sandbox/execution-runner.ts +408 -408
  45. package/src/selector/abstention-policy.ts +175 -175
  46. package/src/selector/candidate-extractor.ts +343 -343
  47. package/src/selector/repair-candidate-ranker.ts +92 -92
  48. package/src/selector/selector-scorer.ts +344 -344
@@ -1,140 +1,140 @@
1
- import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
- import { buildRepositoryProfile, computeRepositoryProfileFingerprint, isLikelyProjectRoot, isLikelyRepositoryContainer, RepositoryConventionProfile } from './repository-profile.js';
4
- import { effectiveReliabilityScore, StabilityLedger, StabilityLedgerData, RepairFeedbackOutcome } from '../memory/stability-ledger.js';
5
-
6
- export interface ProjectIntelligenceSnapshot {
7
- projectRoot: string;
8
- profile?: RepositoryConventionProfile | undefined;
9
- ledger: StabilityLedgerData;
10
- }
11
-
12
- async function readProfile(projectRoot: string): Promise<RepositoryConventionProfile | undefined> {
13
- try {
14
- const raw = await fs.readFile(path.join(projectRoot, '.pie', 'repository-profile.json'), 'utf8');
15
- return JSON.parse(raw) as RepositoryConventionProfile;
16
- } catch {
17
- return undefined;
18
- }
19
- }
20
-
21
- /** Loads project-scoped memory without requiring a cloud service or model. */
22
- export async function loadProjectIntelligence(projectRoot = process.cwd()): Promise<ProjectIntelligenceSnapshot> {
23
- const root = path.resolve(projectRoot);
24
- const ledger = new StabilityLedger(root);
25
- await ledger.init();
26
- return {
27
- projectRoot: root,
28
- profile: await readProfile(root),
29
- ledger: ledger.getSnapshot(),
30
- };
31
- }
32
-
33
- /**
34
- * Resolves the small set of equivalent Playwright page-property prefixes that
35
- * appear when a selector moves between a Page Object and a generated ranking
36
- * expression. The locator itself remains unchanged; only the lookup key is
37
- * normalized for project memory.
38
- */
39
- function selectorLookupVariants(selector: string): string[] {
40
- const trimmed = selector.trim();
41
- if (!trimmed) return [];
42
- const variants = new Set<string>([trimmed]);
43
- const pageProperty = /^(?:this\.)?(?:page|_page|browserPage)\b/;
44
- if (pageProperty.test(trimmed)) {
45
- variants.add(trimmed.replace(pageProperty, 'page'));
46
- variants.add(trimmed.replace(pageProperty, 'this.page'));
47
- }
48
- return [...variants];
49
- }
50
-
51
- /** Creates a profile once, then returns the same project-scoped intelligence snapshot. */
52
- export async function bootstrapProjectIntelligence(projectRoot = process.cwd()): Promise<ProjectIntelligenceSnapshot> {
53
- const root = path.resolve(projectRoot);
54
- const profilePath = path.join(root, '.pie', 'repository-profile.json');
55
- const existing = await readProfile(root);
56
- if (!existing && !(await isLikelyProjectRoot(root)) && (await isLikelyRepositoryContainer(root))) {
57
- // Evaluation and workspace containers are not repositories. Avoid
58
- // recursively profiling every child repository when invoked from one.
59
- return loadProjectIntelligence(root);
60
- }
61
- let profile = existing;
62
- let shouldWrite = false;
63
- if (!profile) {
64
- profile = await buildRepositoryProfile(root);
65
- shouldWrite = true;
66
- } else if (!profile.sourceFingerprint) {
67
- // Migrate profiles created before source-change detection was introduced.
68
- profile = await buildRepositoryProfile(root);
69
- shouldWrite = true;
70
- } else {
71
- const currentFingerprint = await computeRepositoryProfileFingerprint(root);
72
- if (currentFingerprint !== profile.sourceFingerprint) {
73
- profile = await buildRepositoryProfile(root);
74
- shouldWrite = true;
75
- }
76
- }
77
- if (shouldWrite) {
78
- await fs.mkdir(path.dirname(profilePath), { recursive: true });
79
- await fs.writeFile(profilePath, `${JSON.stringify(profile, null, 2)}\n`, 'utf8');
80
- }
81
- const snapshot = await loadProjectIntelligence(root);
82
- return { ...snapshot, profile };
83
- }
84
-
85
- export function getHistoricalSelectorScore(snapshot: ProjectIntelligenceSnapshot | undefined, selector: string): number | undefined {
86
- if (!snapshot) return undefined;
87
- for (const variant of selectorLookupVariants(selector)) {
88
- const score = snapshot.ledger.selectors[variant]?.reliabilityScore;
89
- if (score !== undefined) return score;
90
- }
91
- return undefined;
92
- }
93
-
94
- /** Reads project memory with time decay for ranking while retaining raw history for audit. */
95
- export function getDecayedHistoricalSelectorScore(snapshot: ProjectIntelligenceSnapshot | undefined, selector: string, now = Date.now()): number | undefined {
96
- if (!snapshot) return undefined;
97
- for (const variant of selectorLookupVariants(selector)) {
98
- const record = snapshot.ledger.selectors[variant];
99
- if (record) return effectiveReliabilityScore(record, now);
100
- }
101
- return undefined;
102
- }
103
-
104
- export function shouldLearnFromFailure(category: string): boolean {
105
- return new Set(['LOCATOR_DRIFT', 'AMBIGUOUS_MATCH', 'TIMING_RACE', 'DYNAMIC_CONTENT_SLOW']).has(category);
106
- }
107
-
108
- export async function recordProjectFailure(
109
- projectRoot: string,
110
- selector: string | undefined,
111
- category: string,
112
- ): Promise<void> {
113
- if (!selector || !shouldLearnFromFailure(category)) return;
114
- const ledger = new StabilityLedger(path.resolve(projectRoot));
115
- await ledger.init();
116
- await ledger.recordFailure(selector, category);
117
- }
118
-
119
- /** Records a selector that passed semantic and isolated execution checks. */
120
- export async function recordProjectSemanticVerification(projectRoot: string, selector: string | undefined): Promise<void> {
121
- if (!selector) return;
122
- const ledger = new StabilityLedger(path.resolve(projectRoot));
123
- await ledger.init();
124
- await ledger.recordSemanticVerification(selector);
125
- }
126
-
127
- /** Records that an operator accepted a verified repair for this project. */
128
- export async function recordProjectRepairAccepted(projectRoot: string, selector: string | undefined): Promise<void> {
129
- if (!selector) return;
130
- const ledger = new StabilityLedger(path.resolve(projectRoot));
131
- await ledger.init();
132
- await ledger.recordRepairAccepted(selector);
133
- }
134
-
135
- export async function recordProjectRepairFeedback(projectRoot: string, selector: string | undefined, outcome: RepairFeedbackOutcome): Promise<void> {
136
- if (!selector) return;
137
- const ledger = new StabilityLedger(path.resolve(projectRoot));
138
- await ledger.init();
139
- await ledger.recordRepairFeedback(selector, outcome);
140
- }
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { buildRepositoryProfile, computeRepositoryProfileFingerprint, isLikelyProjectRoot, isLikelyRepositoryContainer, RepositoryConventionProfile } from './repository-profile.js';
4
+ import { effectiveReliabilityScore, StabilityLedger, StabilityLedgerData, RepairFeedbackOutcome } from '../memory/stability-ledger.js';
5
+
6
+ export interface ProjectIntelligenceSnapshot {
7
+ projectRoot: string;
8
+ profile?: RepositoryConventionProfile | undefined;
9
+ ledger: StabilityLedgerData;
10
+ }
11
+
12
+ async function readProfile(projectRoot: string): Promise<RepositoryConventionProfile | undefined> {
13
+ try {
14
+ const raw = await fs.readFile(path.join(projectRoot, '.pie', 'repository-profile.json'), 'utf8');
15
+ return JSON.parse(raw) as RepositoryConventionProfile;
16
+ } catch {
17
+ return undefined;
18
+ }
19
+ }
20
+
21
+ /** Loads project-scoped memory without requiring a cloud service or model. */
22
+ export async function loadProjectIntelligence(projectRoot = process.cwd()): Promise<ProjectIntelligenceSnapshot> {
23
+ const root = path.resolve(projectRoot);
24
+ const ledger = new StabilityLedger(root);
25
+ await ledger.init();
26
+ return {
27
+ projectRoot: root,
28
+ profile: await readProfile(root),
29
+ ledger: ledger.getSnapshot(),
30
+ };
31
+ }
32
+
33
+ /**
34
+ * Resolves the small set of equivalent Playwright page-property prefixes that
35
+ * appear when a selector moves between a Page Object and a generated ranking
36
+ * expression. The locator itself remains unchanged; only the lookup key is
37
+ * normalized for project memory.
38
+ */
39
+ function selectorLookupVariants(selector: string): string[] {
40
+ const trimmed = selector.trim();
41
+ if (!trimmed) return [];
42
+ const variants = new Set<string>([trimmed]);
43
+ const pageProperty = /^(?:this\.)?(?:page|_page|browserPage)\b/;
44
+ if (pageProperty.test(trimmed)) {
45
+ variants.add(trimmed.replace(pageProperty, 'page'));
46
+ variants.add(trimmed.replace(pageProperty, 'this.page'));
47
+ }
48
+ return [...variants];
49
+ }
50
+
51
+ /** Creates a profile once, then returns the same project-scoped intelligence snapshot. */
52
+ export async function bootstrapProjectIntelligence(projectRoot = process.cwd()): Promise<ProjectIntelligenceSnapshot> {
53
+ const root = path.resolve(projectRoot);
54
+ const profilePath = path.join(root, '.pie', 'repository-profile.json');
55
+ const existing = await readProfile(root);
56
+ if (!existing && !(await isLikelyProjectRoot(root)) && (await isLikelyRepositoryContainer(root))) {
57
+ // Evaluation and workspace containers are not repositories. Avoid
58
+ // recursively profiling every child repository when invoked from one.
59
+ return loadProjectIntelligence(root);
60
+ }
61
+ let profile = existing;
62
+ let shouldWrite = false;
63
+ if (!profile) {
64
+ profile = await buildRepositoryProfile(root);
65
+ shouldWrite = true;
66
+ } else if (!profile.sourceFingerprint) {
67
+ // Migrate profiles created before source-change detection was introduced.
68
+ profile = await buildRepositoryProfile(root);
69
+ shouldWrite = true;
70
+ } else {
71
+ const currentFingerprint = await computeRepositoryProfileFingerprint(root);
72
+ if (currentFingerprint !== profile.sourceFingerprint) {
73
+ profile = await buildRepositoryProfile(root);
74
+ shouldWrite = true;
75
+ }
76
+ }
77
+ if (shouldWrite) {
78
+ await fs.mkdir(path.dirname(profilePath), { recursive: true });
79
+ await fs.writeFile(profilePath, `${JSON.stringify(profile, null, 2)}\n`, 'utf8');
80
+ }
81
+ const snapshot = await loadProjectIntelligence(root);
82
+ return { ...snapshot, profile };
83
+ }
84
+
85
+ export function getHistoricalSelectorScore(snapshot: ProjectIntelligenceSnapshot | undefined, selector: string): number | undefined {
86
+ if (!snapshot) return undefined;
87
+ for (const variant of selectorLookupVariants(selector)) {
88
+ const score = snapshot.ledger.selectors[variant]?.reliabilityScore;
89
+ if (score !== undefined) return score;
90
+ }
91
+ return undefined;
92
+ }
93
+
94
+ /** Reads project memory with time decay for ranking while retaining raw history for audit. */
95
+ export function getDecayedHistoricalSelectorScore(snapshot: ProjectIntelligenceSnapshot | undefined, selector: string, now = Date.now()): number | undefined {
96
+ if (!snapshot) return undefined;
97
+ for (const variant of selectorLookupVariants(selector)) {
98
+ const record = snapshot.ledger.selectors[variant];
99
+ if (record) return effectiveReliabilityScore(record, now);
100
+ }
101
+ return undefined;
102
+ }
103
+
104
+ export function shouldLearnFromFailure(category: string): boolean {
105
+ return new Set(['LOCATOR_DRIFT', 'AMBIGUOUS_MATCH', 'TIMING_RACE', 'DYNAMIC_CONTENT_SLOW']).has(category);
106
+ }
107
+
108
+ export async function recordProjectFailure(
109
+ projectRoot: string,
110
+ selector: string | undefined,
111
+ category: string,
112
+ ): Promise<void> {
113
+ if (!selector || !shouldLearnFromFailure(category)) return;
114
+ const ledger = new StabilityLedger(path.resolve(projectRoot));
115
+ await ledger.init();
116
+ await ledger.recordFailure(selector, category);
117
+ }
118
+
119
+ /** Records a selector that passed semantic and isolated execution checks. */
120
+ export async function recordProjectSemanticVerification(projectRoot: string, selector: string | undefined): Promise<void> {
121
+ if (!selector) return;
122
+ const ledger = new StabilityLedger(path.resolve(projectRoot));
123
+ await ledger.init();
124
+ await ledger.recordSemanticVerification(selector);
125
+ }
126
+
127
+ /** Records that an operator accepted a verified repair for this project. */
128
+ export async function recordProjectRepairAccepted(projectRoot: string, selector: string | undefined): Promise<void> {
129
+ if (!selector) return;
130
+ const ledger = new StabilityLedger(path.resolve(projectRoot));
131
+ await ledger.init();
132
+ await ledger.recordRepairAccepted(selector);
133
+ }
134
+
135
+ export async function recordProjectRepairFeedback(projectRoot: string, selector: string | undefined, outcome: RepairFeedbackOutcome): Promise<void> {
136
+ if (!selector) return;
137
+ const ledger = new StabilityLedger(path.resolve(projectRoot));
138
+ await ledger.init();
139
+ await ledger.recordRepairFeedback(selector, outcome);
140
+ }