@mknrt/autotests-overkill 1.1.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.
- package/README.md +117 -0
- package/assets/overkill-logo.svg +8 -0
- package/assets/overkill-small.svg +8 -0
- package/dist/connectors/index.d.ts +4 -0
- package/dist/connectors/index.js +4 -0
- package/dist/indexer/index.d.ts +4 -0
- package/dist/indexer/index.js +4 -0
- package/dist/mcp/index.d.ts +2 -0
- package/dist/mcp/index.js +2 -0
- package/dist/src/appContext.d.ts +21 -0
- package/dist/src/appContext.js +40 -0
- package/dist/src/cli/indexKnowledge.d.ts +1 -0
- package/dist/src/cli/indexKnowledge.js +11 -0
- package/dist/src/cli/main.d.ts +19 -0
- package/dist/src/cli/main.js +59 -0
- package/dist/src/cli/runAcceptance.d.ts +21 -0
- package/dist/src/cli/runAcceptance.js +131 -0
- package/dist/src/cli/runMcpServer.d.ts +1 -0
- package/dist/src/cli/runMcpServer.js +10 -0
- package/dist/src/cli/shared.d.ts +1 -0
- package/dist/src/cli/shared.js +7 -0
- package/dist/src/config/consumerConfig.d.ts +126 -0
- package/dist/src/config/consumerConfig.js +63 -0
- package/dist/src/connectors/autotests2Connector.d.ts +41 -0
- package/dist/src/connectors/autotests2Connector.js +94 -0
- package/dist/src/connectors/caseplatformConnector.d.ts +35 -0
- package/dist/src/connectors/caseplatformConnector.js +251 -0
- package/dist/src/connectors/ciArtifactsConnector.d.ts +23 -0
- package/dist/src/connectors/ciArtifactsConnector.js +14 -0
- package/dist/src/connectors/diffConnector.d.ts +1 -0
- package/dist/src/connectors/diffConnector.js +4 -0
- package/dist/src/connectors/filesystemConnector.d.ts +4 -0
- package/dist/src/connectors/filesystemConnector.js +15 -0
- package/dist/src/connectors/gitlabConnector.d.ts +14 -0
- package/dist/src/connectors/gitlabConnector.js +64 -0
- package/dist/src/connectors/reportPortalConnector.d.ts +14 -0
- package/dist/src/connectors/reportPortalConnector.js +84 -0
- package/dist/src/contracts/confidence.d.ts +8 -0
- package/dist/src/contracts/confidence.js +33 -0
- package/dist/src/contracts/errors.d.ts +6 -0
- package/dist/src/contracts/errors.js +18 -0
- package/dist/src/contracts/toolOutput.d.ts +113 -0
- package/dist/src/contracts/toolOutput.js +35 -0
- package/dist/src/domain/analyzeDiffImpact.d.ts +22 -0
- package/dist/src/domain/analyzeDiffImpact.js +52 -0
- package/dist/src/domain/analyzeTestGaps.d.ts +22 -0
- package/dist/src/domain/analyzeTestGaps.js +41 -0
- package/dist/src/domain/buildRerunScope.d.ts +23 -0
- package/dist/src/domain/buildRerunScope.js +17 -0
- package/dist/src/domain/buildSetupTeardownPlan.d.ts +23 -0
- package/dist/src/domain/buildSetupTeardownPlan.js +13 -0
- package/dist/src/domain/findExistingTestAssets.d.ts +22 -0
- package/dist/src/domain/findExistingTestAssets.js +50 -0
- package/dist/src/domain/findFrontendContract.d.ts +22 -0
- package/dist/src/domain/findFrontendContract.js +13 -0
- package/dist/src/domain/findFrontendRuntimeLogic.d.ts +22 -0
- package/dist/src/domain/findFrontendRuntimeLogic.js +28 -0
- package/dist/src/domain/findTestAreaContext.d.ts +23 -0
- package/dist/src/domain/findTestAreaContext.js +30 -0
- package/dist/src/domain/generateSpecBlueprint.d.ts +23 -0
- package/dist/src/domain/generateSpecBlueprint.js +213 -0
- package/dist/src/domain/mapFeatureToExistingCoverage.d.ts +22 -0
- package/dist/src/domain/mapFeatureToExistingCoverage.js +56 -0
- package/dist/src/domain/recommendSelectorStrategy.d.ts +22 -0
- package/dist/src/domain/recommendSelectorStrategy.js +13 -0
- package/dist/src/domain/shared.d.ts +5 -0
- package/dist/src/domain/shared.js +36 -0
- package/dist/src/domain/summarizeCiContext.d.ts +22 -0
- package/dist/src/domain/summarizeCiContext.js +35 -0
- package/dist/src/domain/triageFailedRun.d.ts +25 -0
- package/dist/src/domain/triageFailedRun.js +46 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.js +4 -0
- package/dist/src/indexer/extractors/frontendContractExtractor.d.ts +17 -0
- package/dist/src/indexer/extractors/frontendContractExtractor.js +109 -0
- package/dist/src/indexer/extractors/frontendRuntimeExtractor.d.ts +14 -0
- package/dist/src/indexer/extractors/frontendRuntimeExtractor.js +110 -0
- package/dist/src/indexer/extractors/testAssetExtractor.d.ts +13 -0
- package/dist/src/indexer/extractors/testAssetExtractor.js +17 -0
- package/dist/src/indexer/refreshPipeline.d.ts +12 -0
- package/dist/src/indexer/refreshPipeline.js +156 -0
- package/dist/src/indexer/snapshotStore.d.ts +10 -0
- package/dist/src/indexer/snapshotStore.js +45 -0
- package/dist/src/knowledge/database.d.ts +3 -0
- package/dist/src/knowledge/database.js +8 -0
- package/dist/src/knowledge/migrations.d.ts +1 -0
- package/dist/src/knowledge/migrations.js +28 -0
- package/dist/src/knowledge/repositories.d.ts +28 -0
- package/dist/src/knowledge/repositories.js +78 -0
- package/dist/src/mcp/registerTools.d.ts +11 -0
- package/dist/src/mcp/registerTools.js +118 -0
- package/dist/src/mcp/server.d.ts +4 -0
- package/dist/src/mcp/server.js +29 -0
- package/dist/src/utils/fileUtils.d.ts +4 -0
- package/dist/src/utils/fileUtils.js +16 -0
- package/dist/src/utils/pathUtils.d.ts +7 -0
- package/dist/src/utils/pathUtils.js +59 -0
- package/dist/src/utils/textUtils.d.ts +3 -0
- package/dist/src/utils/textUtils.js +26 -0
- package/docs/architecture/overview.md +31 -0
- package/docs/consumer-integration.md +64 -0
- package/docs/operator-cookbook.md +39 -0
- package/docs/plugin-packaging.md +33 -0
- package/docs/superpowers/plans/2026-04-19-autotests-overkill-v1.md +600 -0
- package/docs/tool-catalog.md +34 -0
- package/overkill.config.example.json +16 -0
- package/package.json +73 -0
- package/skills/overkill-debug-failure/SKILL.md +13 -0
- package/skills/overkill-find-gaps/SKILL.md +13 -0
- package/skills/overkill-generate-setup/SKILL.md +12 -0
- package/skills/overkill-generate-test/SKILL.md +15 -0
- package/skills/overkill-impact-analysis/SKILL.md +12 -0
- package/skills/overkill-onboard/SKILL.md +16 -0
- package/skills/overkill-prompt-builder/SKILL.md +13 -0
- package/skills/overkill-reuse-project-patterns/SKILL.md +13 -0
- package/skills/overkill-review-test-draft/SKILL.md +10 -0
- package/templates/prompts/debug-failure.md +3 -0
- package/templates/prompts/find-gaps.md +3 -0
- package/templates/prompts/generate-test.md +3 -0
- package/templates/prompts/impact-analysis.md +3 -0
- package/templates/spec-blueprints/default.md +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Autotests Overkill
|
|
2
|
+
|
|
3
|
+
`Autotests Overkill` is a standalone AI-assisted testing platform for `autotests2`, `caseplatform-web`, and CI artifacts. It is designed for Codex-style work: inspect existing coverage, extract frontend contracts and runtime logic, generate draft specs, triage failures, and narrow rerun scope without silently mutating consumer repositories.
|
|
4
|
+
|
|
5
|
+
## What v1.1 adds
|
|
6
|
+
|
|
7
|
+
- Legacy widget contract extraction for files such as `dateRangeInput_control.js` and `dateRangeInput_singleton.js`
|
|
8
|
+
- Stronger spec blueprints that reuse real `autotests2` setup, commands, helpers, and selector patterns
|
|
9
|
+
- Acceptance coverage for both modern and legacy widgets
|
|
10
|
+
- Knowledge-store-backed retrieval for asset search, area context, impact analysis, and blueprint support
|
|
11
|
+
- Optional ReportPortal and GitLab adapters with graceful artifacts-first fallback
|
|
12
|
+
- Codex plugin packaging via `.codex-plugin/plugin.json` and `.mcp.json`
|
|
13
|
+
|
|
14
|
+
## Repository layout
|
|
15
|
+
|
|
16
|
+
- `src/` implementation code
|
|
17
|
+
- `mcp/` MCP entrypoint notes
|
|
18
|
+
- `skills/` Codex skills for end-to-end workflows
|
|
19
|
+
- `indexer/` indexing entrypoint notes
|
|
20
|
+
- `connectors/` connector notes
|
|
21
|
+
- `templates/` prompt and blueprint templates
|
|
22
|
+
- `docs/` operator and integration documentation
|
|
23
|
+
- `tests/` unit, integration, and acceptance checks
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
Published npm package install:
|
|
28
|
+
|
|
29
|
+
1. Run `npm install -D @mknrt/autotests-overkill`.
|
|
30
|
+
2. Copy `node_modules/@mknrt/autotests-overkill/overkill.config.example.json` to `overkill.config.json`.
|
|
31
|
+
3. Set `repos.autotests2`, `repos.caseplatformWeb`, and `ci.artifactsRoot`.
|
|
32
|
+
4. Run `npx autotests-overkill index`.
|
|
33
|
+
|
|
34
|
+
Repository-local development:
|
|
35
|
+
|
|
36
|
+
1. Run `npm install`.
|
|
37
|
+
2. Copy `overkill.config.example.json` to `overkill.config.json`.
|
|
38
|
+
3. Set `repos.autotests2`, `repos.caseplatformWeb`, and `ci.artifactsRoot`.
|
|
39
|
+
4. Run `npm run build`.
|
|
40
|
+
5. Run `npm run index`.
|
|
41
|
+
|
|
42
|
+
## Daily workflow
|
|
43
|
+
|
|
44
|
+
1. Refresh local knowledge with `npm run index`.
|
|
45
|
+
2. Start the MCP server through the checked-in MCP manifest or by running `npx autotests-overkill mcp`.
|
|
46
|
+
3. Use `find_frontend_contract`, `find_existing_test_assets`, and `generate_spec_blueprint` for a new feature draft.
|
|
47
|
+
4. Use `summarize_ci_context`, `triage_failed_run`, and `analyze_diff_impact` for failure or change triage.
|
|
48
|
+
5. Review generated output manually before editing `autotests2`.
|
|
49
|
+
|
|
50
|
+
CLI commands:
|
|
51
|
+
|
|
52
|
+
- `npx autotests-overkill index`
|
|
53
|
+
- `npx autotests-overkill mcp`
|
|
54
|
+
- `npx autotests-overkill acceptance coverage caption`
|
|
55
|
+
- `npx autotests-overkill acceptance blueprint dateRangeField`
|
|
56
|
+
|
|
57
|
+
## Verification commands
|
|
58
|
+
|
|
59
|
+
- `npm run verify`
|
|
60
|
+
- `npm run mcp:smoke`
|
|
61
|
+
- `npm run acceptance:coverage`
|
|
62
|
+
- `npm run acceptance:coverage:legacy`
|
|
63
|
+
- `npm run acceptance:blueprint`
|
|
64
|
+
- `npm run acceptance:failure`
|
|
65
|
+
- `npm run acceptance:impact`
|
|
66
|
+
|
|
67
|
+
## Consumer config
|
|
68
|
+
|
|
69
|
+
The platform reads `overkill.config.json` with these keys:
|
|
70
|
+
|
|
71
|
+
- `repos.autotests2`
|
|
72
|
+
- `repos.caseplatformWeb`
|
|
73
|
+
- `ci.artifactsRoot`
|
|
74
|
+
- `ci.reportPortal.endpoint`
|
|
75
|
+
- `ci.reportPortal.project`
|
|
76
|
+
- `ci.reportPortal.token`
|
|
77
|
+
- `ci.gitlab.baseUrl`
|
|
78
|
+
- `ci.gitlab.projectId`
|
|
79
|
+
- `ci.gitlab.token`
|
|
80
|
+
- `cacheDir`
|
|
81
|
+
|
|
82
|
+
The default path is local and artifacts-first. Missing ReportPortal or GitLab credentials do not break tools; the platform keeps using local stats, screenshots, videos, and captured API payloads.
|
|
83
|
+
|
|
84
|
+
## MCP tools
|
|
85
|
+
|
|
86
|
+
- `find_existing_test_assets`
|
|
87
|
+
- `find_test_area_context`
|
|
88
|
+
- `find_frontend_contract`
|
|
89
|
+
- `find_frontend_runtime_logic`
|
|
90
|
+
- `map_feature_to_existing_coverage`
|
|
91
|
+
- `analyze_test_gaps`
|
|
92
|
+
- `generate_spec_blueprint`
|
|
93
|
+
- `recommend_selector_strategy`
|
|
94
|
+
- `build_setup_teardown_plan`
|
|
95
|
+
- `analyze_diff_impact`
|
|
96
|
+
- `triage_failed_run`
|
|
97
|
+
- `build_rerun_scope`
|
|
98
|
+
- `summarize_ci_context`
|
|
99
|
+
|
|
100
|
+
Every tool returns the same normalized shape:
|
|
101
|
+
|
|
102
|
+
- `summary`
|
|
103
|
+
- `evidence`
|
|
104
|
+
- `recommended_actions`
|
|
105
|
+
- `repo_paths`
|
|
106
|
+
- `confidence`
|
|
107
|
+
- `warnings`
|
|
108
|
+
|
|
109
|
+
## Codex plugin packaging
|
|
110
|
+
|
|
111
|
+
This repository can be consumed either from a Git checkout or as a published npm package:
|
|
112
|
+
|
|
113
|
+
- `.codex-plugin/plugin.json` exposes the plugin metadata
|
|
114
|
+
- `.mcp.json` registers the local MCP server command for checkout-based usage
|
|
115
|
+
- `skills/` is auto-discovered as the bundled skill set
|
|
116
|
+
|
|
117
|
+
See [consumer-integration.md](/C:/gitrep/autotests-overkill/docs/consumer-integration.md), [operator-cookbook.md](/C:/gitrep/autotests-overkill/docs/operator-cookbook.md), and [plugin-packaging.md](/C:/gitrep/autotests-overkill/docs/plugin-packaging.md) for setup details.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
|
|
2
|
+
<rect width="256" height="256" rx="40" fill="#0F4C81"/>
|
|
3
|
+
<path d="M58 176 92 90l29 61 35-81" stroke="#F4F7FB" stroke-linecap="round" stroke-linejoin="round" stroke-width="18"/>
|
|
4
|
+
<circle cx="58" cy="176" r="12" fill="#FFD166"/>
|
|
5
|
+
<circle cx="92" cy="90" r="12" fill="#FFD166"/>
|
|
6
|
+
<circle cx="121" cy="151" r="12" fill="#FFD166"/>
|
|
7
|
+
<circle cx="156" cy="70" r="12" fill="#FFD166"/>
|
|
8
|
+
</svg>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" fill="none">
|
|
2
|
+
<rect width="64" height="64" rx="14" fill="#0F4C81"/>
|
|
3
|
+
<path d="M18 45 28 20l8 18 10-22" stroke="#F4F7FB" stroke-linecap="round" stroke-linejoin="round" stroke-width="5"/>
|
|
4
|
+
<circle cx="18" cy="45" r="4" fill="#FFD166"/>
|
|
5
|
+
<circle cx="28" cy="20" r="4" fill="#FFD166"/>
|
|
6
|
+
<circle cx="36" cy="38" r="4" fill="#FFD166"/>
|
|
7
|
+
<circle cx="46" cy="16" r="4" fill="#FFD166"/>
|
|
8
|
+
</svg>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Autotests2Connector } from './connectors/autotests2Connector.js';
|
|
2
|
+
import { CaseplatformConnector } from './connectors/caseplatformConnector.js';
|
|
3
|
+
import { CiArtifactsConnector } from './connectors/ciArtifactsConnector.js';
|
|
4
|
+
import { GitLabConnector } from './connectors/gitlabConnector.js';
|
|
5
|
+
import { ReportPortalConnector } from './connectors/reportPortalConnector.js';
|
|
6
|
+
import { type ConsumerConfig, type ConsumerConfigInput } from './config/consumerConfig.js';
|
|
7
|
+
import { createKnowledgeDatabase } from './knowledge/database.js';
|
|
8
|
+
export type AppContext = {
|
|
9
|
+
config: ConsumerConfig;
|
|
10
|
+
database: ReturnType<typeof createKnowledgeDatabase>;
|
|
11
|
+
autotests2: Autotests2Connector;
|
|
12
|
+
caseplatform: CaseplatformConnector;
|
|
13
|
+
ciArtifacts: CiArtifactsConnector;
|
|
14
|
+
gitlab: GitLabConnector;
|
|
15
|
+
reportPortal: ReportPortalConnector;
|
|
16
|
+
};
|
|
17
|
+
export declare function createAppContext(input?: {
|
|
18
|
+
config?: ConsumerConfig | ConsumerConfigInput;
|
|
19
|
+
}): Promise<AppContext>;
|
|
20
|
+
export declare function resolveRuntimeConfigPath(): string;
|
|
21
|
+
export declare function createRuntimeAppContext(): Promise<AppContext>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { Autotests2Connector } from './connectors/autotests2Connector.js';
|
|
4
|
+
import { CaseplatformConnector } from './connectors/caseplatformConnector.js';
|
|
5
|
+
import { CiArtifactsConnector } from './connectors/ciArtifactsConnector.js';
|
|
6
|
+
import { GitLabConnector } from './connectors/gitlabConnector.js';
|
|
7
|
+
import { ReportPortalConnector } from './connectors/reportPortalConnector.js';
|
|
8
|
+
import { createDefaultConsumerConfig, loadConsumerConfig, parseConsumerConfig } from './config/consumerConfig.js';
|
|
9
|
+
import { createKnowledgeDatabase } from './knowledge/database.js';
|
|
10
|
+
import { ensureDirectory } from './utils/fileUtils.js';
|
|
11
|
+
export async function createAppContext(input) {
|
|
12
|
+
const config = input?.config
|
|
13
|
+
? ('cacheDir' in input.config ? input.config : parseConsumerConfig(input.config))
|
|
14
|
+
: createDefaultConsumerConfig();
|
|
15
|
+
ensureDirectory(config.cacheDir);
|
|
16
|
+
const database = createKnowledgeDatabase(path.join(config.cacheDir, 'knowledge.db'));
|
|
17
|
+
return {
|
|
18
|
+
config,
|
|
19
|
+
database,
|
|
20
|
+
autotests2: new Autotests2Connector(config.repos.autotests2),
|
|
21
|
+
caseplatform: new CaseplatformConnector(config.repos.caseplatformWeb),
|
|
22
|
+
ciArtifacts: new CiArtifactsConnector(),
|
|
23
|
+
gitlab: new GitLabConnector(config.ci.gitlab),
|
|
24
|
+
reportPortal: new ReportPortalConnector(config.ci.reportPortal),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function resolveRuntimeConfigPath() {
|
|
28
|
+
const explicitPath = process.env.OVERKILL_CONFIG_PATH;
|
|
29
|
+
if (explicitPath) {
|
|
30
|
+
return path.resolve(process.cwd(), explicitPath);
|
|
31
|
+
}
|
|
32
|
+
return path.resolve(process.cwd(), 'overkill.config.json');
|
|
33
|
+
}
|
|
34
|
+
export async function createRuntimeAppContext() {
|
|
35
|
+
const configPath = resolveRuntimeConfigPath();
|
|
36
|
+
const config = fs.existsSync(configPath)
|
|
37
|
+
? loadConsumerConfig(configPath)
|
|
38
|
+
: createDefaultConsumerConfig();
|
|
39
|
+
return createAppContext({ config });
|
|
40
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runIndexKnowledge(): Promise<void>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createRuntimeAppContext } from '../appContext.js';
|
|
2
|
+
import { refreshKnowledge } from '../indexer/refreshPipeline.js';
|
|
3
|
+
import { isDirectExecution } from './shared.js';
|
|
4
|
+
export async function runIndexKnowledge() {
|
|
5
|
+
const context = await createRuntimeAppContext();
|
|
6
|
+
const result = await refreshKnowledge(context);
|
|
7
|
+
console.log(JSON.stringify(result, null, 2));
|
|
8
|
+
}
|
|
9
|
+
if (isDirectExecution(import.meta.url)) {
|
|
10
|
+
await runIndexKnowledge();
|
|
11
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type ParsedCliArgs = {
|
|
2
|
+
command: 'index';
|
|
3
|
+
args: string[];
|
|
4
|
+
} | {
|
|
5
|
+
command: 'mcp';
|
|
6
|
+
args: string[];
|
|
7
|
+
} | {
|
|
8
|
+
command: 'acceptance';
|
|
9
|
+
args: string[];
|
|
10
|
+
} | {
|
|
11
|
+
command: 'help';
|
|
12
|
+
args: [];
|
|
13
|
+
} | {
|
|
14
|
+
command: 'invalid';
|
|
15
|
+
args: string[];
|
|
16
|
+
};
|
|
17
|
+
export declare function parseCliArgs(argv: string[]): ParsedCliArgs;
|
|
18
|
+
export declare function formatCliHelp(): string;
|
|
19
|
+
export declare function runCli(argv?: string[]): Promise<void>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { runIndexKnowledge } from './indexKnowledge.js';
|
|
2
|
+
import { runAcceptanceCli } from './runAcceptance.js';
|
|
3
|
+
import { startMcpServer } from './runMcpServer.js';
|
|
4
|
+
import { isDirectExecution } from './shared.js';
|
|
5
|
+
export function parseCliArgs(argv) {
|
|
6
|
+
const [command, ...args] = argv;
|
|
7
|
+
if (!command || command === '--help' || command === '-h' || command === 'help') {
|
|
8
|
+
return { command: 'help', args: [] };
|
|
9
|
+
}
|
|
10
|
+
if (command === 'index') {
|
|
11
|
+
return { command, args };
|
|
12
|
+
}
|
|
13
|
+
if (command === 'mcp') {
|
|
14
|
+
return { command, args };
|
|
15
|
+
}
|
|
16
|
+
if (command === 'acceptance') {
|
|
17
|
+
return { command, args };
|
|
18
|
+
}
|
|
19
|
+
return { command: 'invalid', args: [command, ...args] };
|
|
20
|
+
}
|
|
21
|
+
export function formatCliHelp() {
|
|
22
|
+
return [
|
|
23
|
+
'autotests-overkill <command>',
|
|
24
|
+
'',
|
|
25
|
+
'Commands:',
|
|
26
|
+
' index Refresh the local knowledge cache.',
|
|
27
|
+
' mcp Start the MCP server.',
|
|
28
|
+
' acceptance <mode> [args...] Run acceptance checks.',
|
|
29
|
+
'',
|
|
30
|
+
'Examples:',
|
|
31
|
+
' npx @mknrt/autotests-overkill index',
|
|
32
|
+
' npx @mknrt/autotests-overkill mcp',
|
|
33
|
+
' npx @mknrt/autotests-overkill acceptance coverage caption',
|
|
34
|
+
].join('\n');
|
|
35
|
+
}
|
|
36
|
+
export async function runCli(argv = process.argv.slice(2)) {
|
|
37
|
+
const parsed = parseCliArgs(argv);
|
|
38
|
+
if (parsed.command === 'help') {
|
|
39
|
+
console.log(formatCliHelp());
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (parsed.command === 'invalid') {
|
|
43
|
+
console.error(`Unknown command '${parsed.args[0]}'.\n\n${formatCliHelp()}`);
|
|
44
|
+
process.exitCode = 1;
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (parsed.command === 'index') {
|
|
48
|
+
await runIndexKnowledge();
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (parsed.command === 'mcp') {
|
|
52
|
+
await startMcpServer();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
await runAcceptanceCli(parsed.args);
|
|
56
|
+
}
|
|
57
|
+
if (isDirectExecution(import.meta.url)) {
|
|
58
|
+
await runCli();
|
|
59
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type AppContext } from '../appContext.js';
|
|
2
|
+
import type { NormalizedToolOutput } from '../contracts/toolOutput.js';
|
|
3
|
+
export type AcceptanceMode = 'coverage' | 'failure' | 'impact' | 'blueprint';
|
|
4
|
+
export type AcceptanceCheck = {
|
|
5
|
+
name: string;
|
|
6
|
+
passed: boolean;
|
|
7
|
+
detail: string;
|
|
8
|
+
};
|
|
9
|
+
export type AcceptanceReport = {
|
|
10
|
+
mode: AcceptanceMode;
|
|
11
|
+
target: string;
|
|
12
|
+
checks: AcceptanceCheck[];
|
|
13
|
+
output: Record<string, unknown>;
|
|
14
|
+
};
|
|
15
|
+
export declare function evaluateAcceptanceMode(mode: AcceptanceMode, args: string[], context: AppContext): Promise<AcceptanceReport>;
|
|
16
|
+
export declare function evaluateCoverageAcceptance(contract: NormalizedToolOutput, gaps: NormalizedToolOutput, widget: string): AcceptanceCheck[];
|
|
17
|
+
export declare function evaluateFailureAcceptance(triage: NormalizedToolOutput): AcceptanceCheck[];
|
|
18
|
+
export declare function evaluateImpactAcceptance(impact: NormalizedToolOutput, changedFile: string): AcceptanceCheck[];
|
|
19
|
+
export declare function evaluateBlueprintAcceptance(blueprint: NormalizedToolOutput, feature: string): AcceptanceCheck[];
|
|
20
|
+
export declare function assertPassingChecks(report: AcceptanceReport): void;
|
|
21
|
+
export declare function runAcceptanceCli(argv?: string[]): Promise<void>;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { createRuntimeAppContext } from '../appContext.js';
|
|
2
|
+
import { analyzeDiffImpact } from '../domain/analyzeDiffImpact.js';
|
|
3
|
+
import { analyzeTestGaps } from '../domain/analyzeTestGaps.js';
|
|
4
|
+
import { findFrontendContract } from '../domain/findFrontendContract.js';
|
|
5
|
+
import { generateSpecBlueprint } from '../domain/generateSpecBlueprint.js';
|
|
6
|
+
import { triageFailedRun } from '../domain/triageFailedRun.js';
|
|
7
|
+
import { isDirectExecution } from './shared.js';
|
|
8
|
+
export async function evaluateAcceptanceMode(mode, args, context) {
|
|
9
|
+
if (mode === 'coverage') {
|
|
10
|
+
const widget = args[0] ?? 'caption';
|
|
11
|
+
const [contract, gaps] = await Promise.all([
|
|
12
|
+
findFrontendContract({ widget }, context),
|
|
13
|
+
analyzeTestGaps({ widget }, context),
|
|
14
|
+
]);
|
|
15
|
+
return {
|
|
16
|
+
mode,
|
|
17
|
+
target: widget,
|
|
18
|
+
checks: evaluateCoverageAcceptance(contract, gaps, widget),
|
|
19
|
+
output: { contract, gaps },
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
if (mode === 'failure') {
|
|
23
|
+
const triage = await triageFailedRun({
|
|
24
|
+
statsFile: `${context.config.repos.autotests2}/cypress/reports/test-stats.json`,
|
|
25
|
+
screenshotsRoot: `${context.config.repos.autotests2}/cypress/screenshots`,
|
|
26
|
+
videosRoot: `${context.config.repos.autotests2}/cypress/videos`,
|
|
27
|
+
apiCaptureRoot: `${context.config.repos.autotests2}/cypress/reports/api-capture`,
|
|
28
|
+
}, context);
|
|
29
|
+
return {
|
|
30
|
+
mode,
|
|
31
|
+
target: 'latest-failure-artifacts',
|
|
32
|
+
checks: evaluateFailureAcceptance(triage),
|
|
33
|
+
output: { triage },
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (mode === 'impact') {
|
|
37
|
+
const changedFile = args[0] ?? 'C:/gitrep/caseplatform-web/vcm/jqVCM/widget/caption/CaptionSingleton.ts';
|
|
38
|
+
const impact = await analyzeDiffImpact({ changedFiles: [changedFile] }, context);
|
|
39
|
+
return {
|
|
40
|
+
mode,
|
|
41
|
+
target: changedFile,
|
|
42
|
+
checks: evaluateImpactAcceptance(impact, changedFile),
|
|
43
|
+
output: { impact },
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const feature = args[0] ?? 'caption';
|
|
47
|
+
const area = args[1];
|
|
48
|
+
const blueprint = await generateSpecBlueprint({ feature, area }, context);
|
|
49
|
+
return {
|
|
50
|
+
mode,
|
|
51
|
+
target: feature,
|
|
52
|
+
checks: evaluateBlueprintAcceptance(blueprint, feature),
|
|
53
|
+
output: { blueprint },
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export function evaluateCoverageAcceptance(contract, gaps, widget) {
|
|
57
|
+
const hasNonEmptyContract = !/0 properties, 0 events, and 0 methods/.test(contract.summary);
|
|
58
|
+
return [
|
|
59
|
+
check('contract evidence', contract.evidence.length > 0, `Frontend contract for '${widget}' must contain evidence-backed files/snippets.`, `Collected ${contract.evidence.length} contract evidence item(s).`),
|
|
60
|
+
check('contract usefulness', hasNonEmptyContract, `Frontend contract for '${widget}' is empty, so coverage acceptance must fail instead of silently passing.`, contract.summary),
|
|
61
|
+
check('gap evidence', gaps.evidence.length > 0, `Gap analysis for '${widget}' must contain evidence and likely gap signals.`, `Collected ${gaps.evidence.length} gap evidence item(s).`),
|
|
62
|
+
];
|
|
63
|
+
}
|
|
64
|
+
export function evaluateFailureAcceptance(triage) {
|
|
65
|
+
const hasStats = triage.evidence.some((item) => item.label === 'stats report');
|
|
66
|
+
const hasArtifacts = triage.evidence.some((item) => /failure screenshot|failure video|failed spec/.test(item.label));
|
|
67
|
+
return [
|
|
68
|
+
check('failure evidence', triage.evidence.length > 0, 'Failure acceptance requires evidence-backed triage output.', `Collected ${triage.evidence.length} evidence item(s).`),
|
|
69
|
+
check('stats report', hasStats, 'Failure acceptance requires the stats report evidence entry.', 'Stats report evidence is present.'),
|
|
70
|
+
check('artifact context', hasArtifacts, 'Failure acceptance requires at least one screenshot, video, or failed-spec evidence item.', 'Artifact context is present in the triage output.'),
|
|
71
|
+
];
|
|
72
|
+
}
|
|
73
|
+
export function evaluateImpactAcceptance(impact, changedFile) {
|
|
74
|
+
const impactedSpecs = impact.evidence.filter((item) => item.label === 'impacted spec');
|
|
75
|
+
return [
|
|
76
|
+
check('impacted specs', impactedSpecs.length > 0, `Impact acceptance for '${changedFile}' must not pass when no impacted specs are found.`, `Mapped ${impactedSpecs.length} impacted spec(s).`),
|
|
77
|
+
check('spec repo paths', impact.repo_paths.some((repoPath) => repoPath.endsWith('.cy.js')), `Impact acceptance for '${changedFile}' must include Cypress spec paths, not just the changed file.`, `Spec paths: ${impact.repo_paths.filter((repoPath) => repoPath.endsWith('.cy.js')).length}`),
|
|
78
|
+
];
|
|
79
|
+
}
|
|
80
|
+
export function evaluateBlueprintAcceptance(blueprint, feature) {
|
|
81
|
+
const draft = blueprint.evidence.find((item) => item.label === 'blueprint')?.snippet ?? '';
|
|
82
|
+
const hasReferenceSpecs = blueprint.evidence.some((item) => item.label === 'reference spec');
|
|
83
|
+
const hasImportTarget = /support\/base(?:Conf)?/.test(draft);
|
|
84
|
+
const hasSetupShape = draft.includes('before(() => {') && draft.includes('beforeEach(() => {');
|
|
85
|
+
const hasSelectorGuidance = blueprint.recommended_actions.some((action) => action.toLowerCase().includes('selector'));
|
|
86
|
+
const hasReuseGuidance = blueprint.recommended_actions.some((action) => action.toLowerCase().includes('reuse') || action.toLowerCase().includes('borrow'));
|
|
87
|
+
return [
|
|
88
|
+
check('reference specs', hasReferenceSpecs, `Blueprint acceptance for '${feature}' requires real reference specs from autotests2.`, 'Reference specs are attached as evidence.'),
|
|
89
|
+
check('non-placeholder import', draft.length > 0 && !draft.includes("from '...'") && hasImportTarget, `Blueprint acceptance for '${feature}' requires a real import target instead of demo-level placeholders.`, 'Blueprint contains a concrete support import target.'),
|
|
90
|
+
check('setup guidance', hasSetupShape && draft.includes('cy.getWidget('), `Blueprint acceptance for '${feature}' requires usable before/beforeEach structure and widget access guidance.`, 'Blueprint contains before/beforeEach hooks and widget access patterns.'),
|
|
91
|
+
check('selector and reuse guidance', hasSelectorGuidance && hasReuseGuidance, `Blueprint acceptance for '${feature}' requires explicit selector and reuse guidance.`, 'Recommended actions include selector and reuse guidance.'),
|
|
92
|
+
];
|
|
93
|
+
}
|
|
94
|
+
export function assertPassingChecks(report) {
|
|
95
|
+
const failedChecks = report.checks.filter((checkResult) => !checkResult.passed);
|
|
96
|
+
if (failedChecks.length === 0) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const details = failedChecks.map((checkResult) => `${checkResult.name}: ${checkResult.detail}`).join(' | ');
|
|
100
|
+
throw new Error(`${report.mode} acceptance failed for '${report.target}'. ${details}`);
|
|
101
|
+
}
|
|
102
|
+
function check(name, passed, failedDetail, passedDetail) {
|
|
103
|
+
return {
|
|
104
|
+
name,
|
|
105
|
+
passed,
|
|
106
|
+
detail: passed ? passedDetail : failedDetail,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
export async function runAcceptanceCli(argv = process.argv.slice(2)) {
|
|
110
|
+
const [rawMode, ...args] = argv;
|
|
111
|
+
if (!isAcceptanceMode(rawMode)) {
|
|
112
|
+
console.error(`Unknown acceptance mode '${rawMode}'. Use coverage, failure, impact, or blueprint.`);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
const context = await createRuntimeAppContext();
|
|
116
|
+
const report = await evaluateAcceptanceMode(rawMode, args, context);
|
|
117
|
+
try {
|
|
118
|
+
assertPassingChecks(report);
|
|
119
|
+
console.log(JSON.stringify(report, null, 2));
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
console.error(JSON.stringify(report, null, 2));
|
|
123
|
+
throw error;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function isAcceptanceMode(value) {
|
|
127
|
+
return value === 'coverage' || value === 'failure' || value === 'impact' || value === 'blueprint';
|
|
128
|
+
}
|
|
129
|
+
if (isDirectExecution(import.meta.url)) {
|
|
130
|
+
await runAcceptanceCli();
|
|
131
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function startMcpServer(): Promise<void>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createRuntimeAppContext } from '../appContext.js';
|
|
2
|
+
import { runMcpServer } from '../mcp/server.js';
|
|
3
|
+
import { isDirectExecution } from './shared.js';
|
|
4
|
+
export async function startMcpServer() {
|
|
5
|
+
const context = await createRuntimeAppContext();
|
|
6
|
+
await runMcpServer(context);
|
|
7
|
+
}
|
|
8
|
+
if (isDirectExecution(import.meta.url)) {
|
|
9
|
+
await startMcpServer();
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isDirectExecution(moduleUrl: string): boolean;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
export function isDirectExecution(moduleUrl) {
|
|
4
|
+
const currentFile = fileURLToPath(moduleUrl);
|
|
5
|
+
const entryFile = process.argv[1] ? path.resolve(process.argv[1]) : '';
|
|
6
|
+
return currentFile === entryFile;
|
|
7
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const consumerConfigSchema: z.ZodObject<{
|
|
3
|
+
repos: z.ZodObject<{
|
|
4
|
+
autotests2: z.ZodString;
|
|
5
|
+
caseplatformWeb: z.ZodString;
|
|
6
|
+
}, "strict", z.ZodTypeAny, {
|
|
7
|
+
autotests2: string;
|
|
8
|
+
caseplatformWeb: string;
|
|
9
|
+
}, {
|
|
10
|
+
autotests2: string;
|
|
11
|
+
caseplatformWeb: string;
|
|
12
|
+
}>;
|
|
13
|
+
ci: z.ZodObject<{
|
|
14
|
+
artifactsRoot: z.ZodString;
|
|
15
|
+
reportPortal: z.ZodOptional<z.ZodObject<{
|
|
16
|
+
endpoint: z.ZodString;
|
|
17
|
+
project: z.ZodOptional<z.ZodString>;
|
|
18
|
+
token: z.ZodOptional<z.ZodString>;
|
|
19
|
+
}, "strict", z.ZodTypeAny, {
|
|
20
|
+
endpoint: string;
|
|
21
|
+
project?: string | undefined;
|
|
22
|
+
token?: string | undefined;
|
|
23
|
+
}, {
|
|
24
|
+
endpoint: string;
|
|
25
|
+
project?: string | undefined;
|
|
26
|
+
token?: string | undefined;
|
|
27
|
+
}>>;
|
|
28
|
+
gitlab: z.ZodOptional<z.ZodObject<{
|
|
29
|
+
baseUrl: z.ZodString;
|
|
30
|
+
projectId: z.ZodOptional<z.ZodString>;
|
|
31
|
+
token: z.ZodOptional<z.ZodString>;
|
|
32
|
+
}, "strict", z.ZodTypeAny, {
|
|
33
|
+
baseUrl: string;
|
|
34
|
+
token?: string | undefined;
|
|
35
|
+
projectId?: string | undefined;
|
|
36
|
+
}, {
|
|
37
|
+
baseUrl: string;
|
|
38
|
+
token?: string | undefined;
|
|
39
|
+
projectId?: string | undefined;
|
|
40
|
+
}>>;
|
|
41
|
+
}, "strict", z.ZodTypeAny, {
|
|
42
|
+
artifactsRoot: string;
|
|
43
|
+
reportPortal?: {
|
|
44
|
+
endpoint: string;
|
|
45
|
+
project?: string | undefined;
|
|
46
|
+
token?: string | undefined;
|
|
47
|
+
} | undefined;
|
|
48
|
+
gitlab?: {
|
|
49
|
+
baseUrl: string;
|
|
50
|
+
token?: string | undefined;
|
|
51
|
+
projectId?: string | undefined;
|
|
52
|
+
} | undefined;
|
|
53
|
+
}, {
|
|
54
|
+
artifactsRoot: string;
|
|
55
|
+
reportPortal?: {
|
|
56
|
+
endpoint: string;
|
|
57
|
+
project?: string | undefined;
|
|
58
|
+
token?: string | undefined;
|
|
59
|
+
} | undefined;
|
|
60
|
+
gitlab?: {
|
|
61
|
+
baseUrl: string;
|
|
62
|
+
token?: string | undefined;
|
|
63
|
+
projectId?: string | undefined;
|
|
64
|
+
} | undefined;
|
|
65
|
+
}>;
|
|
66
|
+
cacheDir: z.ZodOptional<z.ZodString>;
|
|
67
|
+
projectHints: z.ZodOptional<z.ZodObject<{
|
|
68
|
+
defaultWidget: z.ZodOptional<z.ZodString>;
|
|
69
|
+
}, "strict", z.ZodTypeAny, {
|
|
70
|
+
defaultWidget?: string | undefined;
|
|
71
|
+
}, {
|
|
72
|
+
defaultWidget?: string | undefined;
|
|
73
|
+
}>>;
|
|
74
|
+
}, "strict", z.ZodTypeAny, {
|
|
75
|
+
repos: {
|
|
76
|
+
autotests2: string;
|
|
77
|
+
caseplatformWeb: string;
|
|
78
|
+
};
|
|
79
|
+
ci: {
|
|
80
|
+
artifactsRoot: string;
|
|
81
|
+
reportPortal?: {
|
|
82
|
+
endpoint: string;
|
|
83
|
+
project?: string | undefined;
|
|
84
|
+
token?: string | undefined;
|
|
85
|
+
} | undefined;
|
|
86
|
+
gitlab?: {
|
|
87
|
+
baseUrl: string;
|
|
88
|
+
token?: string | undefined;
|
|
89
|
+
projectId?: string | undefined;
|
|
90
|
+
} | undefined;
|
|
91
|
+
};
|
|
92
|
+
cacheDir?: string | undefined;
|
|
93
|
+
projectHints?: {
|
|
94
|
+
defaultWidget?: string | undefined;
|
|
95
|
+
} | undefined;
|
|
96
|
+
}, {
|
|
97
|
+
repos: {
|
|
98
|
+
autotests2: string;
|
|
99
|
+
caseplatformWeb: string;
|
|
100
|
+
};
|
|
101
|
+
ci: {
|
|
102
|
+
artifactsRoot: string;
|
|
103
|
+
reportPortal?: {
|
|
104
|
+
endpoint: string;
|
|
105
|
+
project?: string | undefined;
|
|
106
|
+
token?: string | undefined;
|
|
107
|
+
} | undefined;
|
|
108
|
+
gitlab?: {
|
|
109
|
+
baseUrl: string;
|
|
110
|
+
token?: string | undefined;
|
|
111
|
+
projectId?: string | undefined;
|
|
112
|
+
} | undefined;
|
|
113
|
+
};
|
|
114
|
+
cacheDir?: string | undefined;
|
|
115
|
+
projectHints?: {
|
|
116
|
+
defaultWidget?: string | undefined;
|
|
117
|
+
} | undefined;
|
|
118
|
+
}>;
|
|
119
|
+
export type ConsumerConfigInput = z.input<typeof consumerConfigSchema>;
|
|
120
|
+
export type ConsumerConfig = z.output<typeof consumerConfigSchema> & {
|
|
121
|
+
cacheDir: string;
|
|
122
|
+
};
|
|
123
|
+
export declare function resolveRepoPath(value: string, baseDir?: string): string;
|
|
124
|
+
export declare function parseConsumerConfig(input: ConsumerConfigInput, baseDir?: string): ConsumerConfig;
|
|
125
|
+
export declare function loadConsumerConfig(configPath?: string): ConsumerConfig;
|
|
126
|
+
export declare function createDefaultConsumerConfig(baseDir?: string): ConsumerConfig;
|