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.
Files changed (79) hide show
  1. package/agent/frameworks/registry.json +655 -0
  2. package/bin/canary.js +20 -15
  3. package/dist/doctor-manifest.d.ts +94 -0
  4. package/dist/doctor.d.ts +67 -0
  5. package/dist/engine/analysis/cli.js +270 -0
  6. package/dist/engine/analysis/engine.js +146 -0
  7. package/dist/engine/analysis/reports.js +0 -0
  8. package/dist/engine/analysis/rows.js +9 -0
  9. package/dist/engine/cli-commands.js +618 -0
  10. package/dist/engine/cli-common.js +60 -0
  11. package/dist/engine/cli.core.js +208 -0
  12. package/dist/engine/cli.js +31 -0
  13. package/dist/engine/company-knowledge-cli.js +201 -0
  14. package/dist/engine/core/ci-env.js +33 -0
  15. package/dist/engine/core/classifier.js +192 -0
  16. package/dist/engine/core/company-knowledge.js +765 -0
  17. package/dist/engine/core/config-validation.js +74 -0
  18. package/dist/engine/core/detection.js +48 -0
  19. package/dist/engine/core/domain-scanner.js +212 -0
  20. package/dist/engine/core/environment-detect.js +410 -0
  21. package/dist/engine/core/executor.js +181 -0
  22. package/dist/engine/core/feedback.js +93 -0
  23. package/dist/engine/core/fixture-scanner.js +173 -0
  24. package/dist/engine/core/framework-registry.js +123 -0
  25. package/dist/engine/core/mcp-validator.js +218 -0
  26. package/dist/engine/core/metadata-scanner.js +147 -0
  27. package/dist/engine/core/migrator.js +1112 -0
  28. package/dist/engine/core/overlays.js +176 -0
  29. package/dist/engine/core/pattern-healer.js +147 -0
  30. package/dist/engine/core/pattern-matcher.js +255 -0
  31. package/dist/engine/core/quality-scorer.js +213 -0
  32. package/dist/engine/core/recommender.js +152 -0
  33. package/dist/engine/core/reporter.js +211 -0
  34. package/dist/engine/core/scaffolder.js +236 -0
  35. package/dist/engine/core/skill-registry.js +522 -0
  36. package/dist/engine/core/static-linter.js +237 -0
  37. package/dist/engine/core/ticket-updater.js +639 -0
  38. package/dist/engine/core/workflow-discovery.js +693 -0
  39. package/dist/engine/guardian/agent-tier.js +338 -0
  40. package/dist/engine/guardian/analysis-emit.js +201 -0
  41. package/dist/engine/guardian/cli.js +787 -0
  42. package/dist/engine/guardian/coverage.js +1055 -0
  43. package/dist/engine/guardian/delta-emitter.js +46 -0
  44. package/dist/engine/guardian/diff-extractor.js +257 -0
  45. package/dist/engine/guardian/hard-gate.js +373 -0
  46. package/dist/engine/guardian/impact-mapper.js +121 -0
  47. package/dist/engine/guardian/pr-check.js +975 -0
  48. package/dist/engine/guardian/pr-comment.js +200 -0
  49. package/dist/engine/guardian/summary-emitter.js +94 -0
  50. package/dist/engine/guardian/tier.js +58 -0
  51. package/dist/engine/history/cli.js +303 -0
  52. package/dist/engine/history/detector.js +68 -0
  53. package/dist/engine/history/ndjson-store.js +177 -0
  54. package/dist/engine/history/record.js +14 -0
  55. package/dist/engine/history/schema.js +59 -0
  56. package/dist/engine/history/store.js +47 -0
  57. package/dist/engine/history/supabase-store.js +113 -0
  58. package/dist/engine/main-deps.js +105 -0
  59. package/dist/engine/mcp-server.js +647 -0
  60. package/dist/engine/package.json +4 -0
  61. package/dist/engine/skills-cli.js +181 -0
  62. package/dist/engine/ui/banner.js +50 -0
  63. package/dist/engine/util/coalesce.js +12 -0
  64. package/dist/engine/util/round.js +43 -0
  65. package/dist/engine/workflow-cli.js +242 -0
  66. package/dist/engine-checks.d.ts +49 -0
  67. package/dist/overlay-commands.d.ts +81 -0
  68. package/dist/overlay-conflicts.d.ts +33 -0
  69. package/dist/overlay-lint.d.ts +19 -0
  70. package/dist/overlays-registry.d.ts +74 -0
  71. package/dist/reporters/testtracker.d.ts +89 -0
  72. package/dist/reporters/testtracker.js +195 -0
  73. package/dist/router.d.ts +12 -0
  74. package/dist/router.js +4 -4
  75. package/dist/skill-requirements.d.ts +57 -0
  76. package/dist/source-spec.d.ts +20 -0
  77. package/package.json +30 -6
  78. package/bin/canary +0 -0
  79. 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