canary-test-cli 5.15.0 → 6.0.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/agent/frameworks/registry.json +655 -0
- package/bin/canary.js +20 -15
- package/dist/doctor-manifest.d.ts +94 -0
- package/dist/doctor.d.ts +67 -0
- package/dist/engine/analysis/cli.js +270 -0
- package/dist/engine/analysis/engine.js +146 -0
- package/dist/engine/analysis/reports.js +0 -0
- package/dist/engine/analysis/rows.js +9 -0
- package/dist/engine/cli-commands.js +618 -0
- package/dist/engine/cli-common.js +60 -0
- package/dist/engine/cli.core.js +208 -0
- package/dist/engine/cli.js +31 -0
- package/dist/engine/company-knowledge-cli.js +201 -0
- package/dist/engine/core/ci-env.js +33 -0
- package/dist/engine/core/classifier.js +192 -0
- package/dist/engine/core/company-knowledge.js +765 -0
- package/dist/engine/core/config-validation.js +74 -0
- package/dist/engine/core/detection.js +48 -0
- package/dist/engine/core/domain-scanner.js +212 -0
- package/dist/engine/core/environment-detect.js +410 -0
- package/dist/engine/core/executor.js +181 -0
- package/dist/engine/core/feedback.js +93 -0
- package/dist/engine/core/fixture-scanner.js +173 -0
- package/dist/engine/core/framework-registry.js +123 -0
- package/dist/engine/core/mcp-validator.js +218 -0
- package/dist/engine/core/metadata-scanner.js +147 -0
- package/dist/engine/core/migrator.js +1112 -0
- package/dist/engine/core/overlays.js +176 -0
- package/dist/engine/core/pattern-healer.js +147 -0
- package/dist/engine/core/pattern-matcher.js +255 -0
- package/dist/engine/core/quality-scorer.js +213 -0
- package/dist/engine/core/recommender.js +152 -0
- package/dist/engine/core/reporter.js +211 -0
- package/dist/engine/core/scaffolder.js +236 -0
- package/dist/engine/core/skill-registry.js +522 -0
- package/dist/engine/core/static-linter.js +237 -0
- package/dist/engine/core/ticket-updater.js +639 -0
- package/dist/engine/core/workflow-discovery.js +693 -0
- package/dist/engine/guardian/agent-tier.js +338 -0
- package/dist/engine/guardian/analysis-emit.js +201 -0
- package/dist/engine/guardian/cli.js +787 -0
- package/dist/engine/guardian/coverage.js +1055 -0
- package/dist/engine/guardian/delta-emitter.js +46 -0
- package/dist/engine/guardian/diff-extractor.js +257 -0
- package/dist/engine/guardian/hard-gate.js +373 -0
- package/dist/engine/guardian/impact-mapper.js +121 -0
- package/dist/engine/guardian/pr-check.js +975 -0
- package/dist/engine/guardian/pr-comment.js +200 -0
- package/dist/engine/guardian/summary-emitter.js +94 -0
- package/dist/engine/guardian/tier.js +58 -0
- package/dist/engine/history/cli.js +303 -0
- package/dist/engine/history/detector.js +68 -0
- package/dist/engine/history/ndjson-store.js +177 -0
- package/dist/engine/history/record.js +14 -0
- package/dist/engine/history/schema.js +59 -0
- package/dist/engine/history/store.js +47 -0
- package/dist/engine/history/supabase-store.js +113 -0
- package/dist/engine/main-deps.js +105 -0
- package/dist/engine/mcp-server.js +647 -0
- package/dist/engine/package.json +4 -0
- package/dist/engine/skills-cli.js +181 -0
- package/dist/engine/ui/banner.js +50 -0
- package/dist/engine/util/coalesce.js +12 -0
- package/dist/engine/util/round.js +43 -0
- package/dist/engine/workflow-cli.js +242 -0
- package/dist/engine-checks.d.ts +49 -0
- package/dist/overlay-commands.d.ts +81 -0
- package/dist/overlay-conflicts.d.ts +33 -0
- package/dist/overlay-lint.d.ts +19 -0
- package/dist/overlays-registry.d.ts +74 -0
- package/dist/reporters/testtracker.d.ts +89 -0
- package/dist/reporters/testtracker.js +195 -0
- package/dist/router.d.ts +12 -0
- package/dist/router.js +4 -4
- package/dist/skill-requirements.d.ts +57 -0
- package/dist/source-spec.d.ts +20 -0
- package/package.json +30 -6
- package/bin/canary +0 -0
- package/scripts/install.js +0 -104
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canary Scaffolder - bootstraps new test suites with Gold Standard configs.
|
|
3
|
+
*
|
|
4
|
+
* Faithful TypeScript port of `agent/core/scaffolder.py`. Provides templates and
|
|
5
|
+
* logic to initialize directory structures and configuration files for the
|
|
6
|
+
* supported testing frameworks.
|
|
7
|
+
*
|
|
8
|
+
* Python->TS nuances:
|
|
9
|
+
* - The emitted config-file bodies are a **byte-exact contract**: the template
|
|
10
|
+
* strings reproduce the Python triple-quoted literals character for
|
|
11
|
+
* character, including the trailing newline. The single non-ASCII glyph the
|
|
12
|
+
* Python source contains (an em-dash, U+2014, in the wdio comment) is written
|
|
13
|
+
* as a `\u{2014}` escape so this source stays ASCII while the emitted byte is
|
|
14
|
+
* identical. Python `open(path, "w")` writes `\n` verbatim on POSIX;
|
|
15
|
+
* `writeFileSync` does the same.
|
|
16
|
+
* - `Path(project_root).resolve()` -> `resolve(projectRoot)`;
|
|
17
|
+
* `Path.exists()` -> `existsSync`; `dir.mkdir(parents=True)` ->
|
|
18
|
+
* `mkdirSync(p, { recursive: true })`.
|
|
19
|
+
* - Python truthiness (`""`/`null` falsy) for the degrade path via
|
|
20
|
+
* {@link pyTruthy}.
|
|
21
|
+
* - `raise ValueError` -> `throw new Error` (mirrors reporter.ts).
|
|
22
|
+
*/
|
|
23
|
+
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
24
|
+
import { join, resolve } from 'node:path';
|
|
25
|
+
import { FrameworkRegistry } from './framework-registry.js';
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Python-compatibility helper (copied locally per-module, matching reporter.ts)
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
/**
|
|
30
|
+
* Python-truthiness for the values used here: `null`/`undefined`/`""` are falsy
|
|
31
|
+
* (mirrors `if x:`).
|
|
32
|
+
*/
|
|
33
|
+
function pyTruthy(value) {
|
|
34
|
+
if (value === null || value === undefined || value === false)
|
|
35
|
+
return false;
|
|
36
|
+
if (value === 0 || value === '')
|
|
37
|
+
return false;
|
|
38
|
+
if (Array.isArray(value))
|
|
39
|
+
return value.length > 0;
|
|
40
|
+
if (typeof value === 'object')
|
|
41
|
+
return Object.keys(value).length > 0;
|
|
42
|
+
return Boolean(value);
|
|
43
|
+
}
|
|
44
|
+
// Exported so the migrator port can compute would-create / already-present sets
|
|
45
|
+
// in its dry-run path (Python: `from agent.core.scaffolder import TEMPLATES`).
|
|
46
|
+
export const TEMPLATES = {
|
|
47
|
+
playwright: {
|
|
48
|
+
files: {
|
|
49
|
+
'playwright.config.ts': `import { defineConfig, devices } from '@playwright/test';
|
|
50
|
+
|
|
51
|
+
export default defineConfig({
|
|
52
|
+
testDir: './tests/e2e',
|
|
53
|
+
fullyParallel: true,
|
|
54
|
+
forbidOnly: !!process.env.CI,
|
|
55
|
+
retries: process.env.CI ? 2 : 0,
|
|
56
|
+
workers: process.env.CI ? 1 : undefined,
|
|
57
|
+
reporter: 'html',
|
|
58
|
+
use: {
|
|
59
|
+
trace: 'on-first-retry',
|
|
60
|
+
},
|
|
61
|
+
projects: [
|
|
62
|
+
{
|
|
63
|
+
name: 'chromium',
|
|
64
|
+
use: { ...devices['Desktop Chrome'] },
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
});
|
|
68
|
+
`,
|
|
69
|
+
},
|
|
70
|
+
dirs: ['tests/e2e'],
|
|
71
|
+
},
|
|
72
|
+
vitest: {
|
|
73
|
+
files: {
|
|
74
|
+
'vitest.config.ts': `import { defineConfig } from 'vitest/config';
|
|
75
|
+
|
|
76
|
+
export default defineConfig({
|
|
77
|
+
test: {
|
|
78
|
+
environment: 'node',
|
|
79
|
+
include: ['tests/unit/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
`,
|
|
83
|
+
},
|
|
84
|
+
dirs: ['tests/unit'],
|
|
85
|
+
},
|
|
86
|
+
pytest: {
|
|
87
|
+
files: {
|
|
88
|
+
'pytest.ini': `[pytest]
|
|
89
|
+
testpaths = tests
|
|
90
|
+
python_files = test_*.py *_test.py
|
|
91
|
+
python_classes = Test*
|
|
92
|
+
python_functions = test_*
|
|
93
|
+
`,
|
|
94
|
+
},
|
|
95
|
+
dirs: ['tests'],
|
|
96
|
+
},
|
|
97
|
+
k6: {
|
|
98
|
+
files: {
|
|
99
|
+
'k6.config.js': `export const options = {
|
|
100
|
+
vus: 10,
|
|
101
|
+
duration: '30s',
|
|
102
|
+
};
|
|
103
|
+
`,
|
|
104
|
+
},
|
|
105
|
+
dirs: ['tests/performance'],
|
|
106
|
+
},
|
|
107
|
+
wdio: {
|
|
108
|
+
files: {
|
|
109
|
+
'wdio.conf.ts': `import type { Options } from "@wdio/types";
|
|
110
|
+
|
|
111
|
+
// Appium + WebdriverIO config. Fill in the capabilities stub below for the
|
|
112
|
+
// device/platform under test (Android shown; add an iOS entry as needed).
|
|
113
|
+
export const config: Options.Testrunner = {
|
|
114
|
+
runner: "local",
|
|
115
|
+
specs: ["./tests/**/*.spec.ts"],
|
|
116
|
+
maxInstances: 1,
|
|
117
|
+
// Appium capabilities stub \u{2014} replace deviceName / app / versions to match
|
|
118
|
+
// your emulator or real device.
|
|
119
|
+
capabilities: [
|
|
120
|
+
{
|
|
121
|
+
platformName: "Android",
|
|
122
|
+
"appium:automationName": "UiAutomator2",
|
|
123
|
+
"appium:deviceName": "Android Emulator",
|
|
124
|
+
"appium:app": "./app/build/outputs/apk/debug/app-debug.apk",
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
framework: "mocha",
|
|
128
|
+
mochaOpts: {
|
|
129
|
+
ui: "bdd",
|
|
130
|
+
timeout: 60000,
|
|
131
|
+
},
|
|
132
|
+
reporters: ["spec"],
|
|
133
|
+
// Requires the Appium service: \`npm i -D @wdio/appium-service appium\`.
|
|
134
|
+
services: ["appium"],
|
|
135
|
+
};
|
|
136
|
+
`,
|
|
137
|
+
},
|
|
138
|
+
dirs: ['tests'],
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
* Handles initialization and scaffolding of test suites.
|
|
143
|
+
*
|
|
144
|
+
* Uses predefined templates to create the necessary boilerplate for frameworks
|
|
145
|
+
* like Playwright, Vitest, Pytest, and k6.
|
|
146
|
+
*/
|
|
147
|
+
export class Scaffolder {
|
|
148
|
+
/**
|
|
149
|
+
* Create the directory structure and config files for a framework.
|
|
150
|
+
*
|
|
151
|
+
* `framework` is lowercased (matching `FrameworkRegistry.capabilities`).
|
|
152
|
+
* `projectRoot` defaults to the current directory. Returns a summary of the
|
|
153
|
+
* created/skipped files and directories, or a loud-degrade result for a known
|
|
154
|
+
* framework with no template (Python: `scaffold`).
|
|
155
|
+
*/
|
|
156
|
+
scaffold(framework, projectRoot = '.') {
|
|
157
|
+
framework = framework.toLowerCase();
|
|
158
|
+
if (!Object.prototype.hasOwnProperty.call(TEMPLATES, framework)) {
|
|
159
|
+
return this.degrade(framework);
|
|
160
|
+
}
|
|
161
|
+
const template = TEMPLATES[framework];
|
|
162
|
+
const root = resolve(projectRoot);
|
|
163
|
+
const createdFiles = [];
|
|
164
|
+
const createdDirs = [];
|
|
165
|
+
// 1. Create directories.
|
|
166
|
+
for (const d of template.dirs) {
|
|
167
|
+
const dirPath = join(root, d);
|
|
168
|
+
if (!existsSync(dirPath)) {
|
|
169
|
+
mkdirSync(dirPath, { recursive: true });
|
|
170
|
+
createdDirs.push(d);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// 2. Create files.
|
|
174
|
+
// Byte-for-byte identical to the Python oracle on POSIX/UTF-8. NOTE: Python
|
|
175
|
+
// uses text-mode `open(path, "w")`, so on Windows / a non-UTF-8 locale it
|
|
176
|
+
// would translate LF -> os.linesep (CRLF) and encode in the locale codec.
|
|
177
|
+
// We intentionally write LF + UTF-8 on every platform: deterministic,
|
|
178
|
+
// cross-platform-identical scaffolds beat replicating locale-dependent
|
|
179
|
+
// output, and LF configs run fine on Windows toolchains.
|
|
180
|
+
for (const [name, content] of Object.entries(template.files)) {
|
|
181
|
+
const filePath = join(root, name);
|
|
182
|
+
if (!existsSync(filePath)) {
|
|
183
|
+
writeFileSync(filePath, content, 'utf-8');
|
|
184
|
+
createdFiles.push(name);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return {
|
|
188
|
+
framework,
|
|
189
|
+
status: 'scaffolded',
|
|
190
|
+
created_files: createdFiles,
|
|
191
|
+
created_dirs: createdDirs,
|
|
192
|
+
skipped_files: Object.keys(template.files).filter((f) => !createdFiles.includes(f)),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* No scaffold template for this framework (Python: `_degrade`).
|
|
197
|
+
*
|
|
198
|
+
* A framework canary knows (registry entry) degrades loudly with actionable
|
|
199
|
+
* guidance instead of crashing - the adopter still learns how to run it
|
|
200
|
+
* (#294/#295 fail-loud). A framework canary does *not* know is genuinely
|
|
201
|
+
* invalid input and throws (Python `ValueError`).
|
|
202
|
+
*/
|
|
203
|
+
degrade(framework) {
|
|
204
|
+
const entry = new FrameworkRegistry().findByName(framework);
|
|
205
|
+
if (entry === null) {
|
|
206
|
+
throw new Error(`Unknown framework: '${framework}'. ` +
|
|
207
|
+
'Run `canary frameworks list` to see supported frameworks.');
|
|
208
|
+
}
|
|
209
|
+
// Python `entry.get("execution_command")` -> null for an absent key.
|
|
210
|
+
const execCmd = entry.execution_command ?? null;
|
|
211
|
+
const runNote = pyTruthy(execCmd)
|
|
212
|
+
? `canary can run it via: ${execCmd}`
|
|
213
|
+
: 'canary does not yet have a run command for it either';
|
|
214
|
+
const guidance = `No scaffold template for '${framework}' yet \u{2014} canary won't create ` +
|
|
215
|
+
`boilerplate for it. Set the suite up manually; ${runNote}. ` +
|
|
216
|
+
'See `canary frameworks list` for capability tiers.';
|
|
217
|
+
return {
|
|
218
|
+
framework,
|
|
219
|
+
status: 'unsupported',
|
|
220
|
+
created_files: [],
|
|
221
|
+
created_dirs: [],
|
|
222
|
+
skipped_files: [],
|
|
223
|
+
guidance,
|
|
224
|
+
execution_command: execCmd,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Frameworks canary can scaffold - the single source of truth for the
|
|
230
|
+
* `scaffold` capability, derived from the templates that actually exist
|
|
231
|
+
* (Python: `scaffoldable_frameworks`).
|
|
232
|
+
*/
|
|
233
|
+
export function scaffoldableFrameworks() {
|
|
234
|
+
return new Set(Object.keys(TEMPLATES));
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=scaffolder.js.map
|