@jevitate/cli 0.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.
Files changed (77) hide show
  1. package/LICENSE +21 -0
  2. package/dist/ai-cli.d.ts +31 -0
  3. package/dist/ai-cli.js +238 -0
  4. package/dist/ai-cli.js.map +1 -0
  5. package/dist/bin.d.ts +2 -0
  6. package/dist/bin.js +50663 -0
  7. package/dist/bin.js.map +1 -0
  8. package/dist/data-dir.d.ts +9 -0
  9. package/dist/data-dir.js +13 -0
  10. package/dist/data-dir.js.map +1 -0
  11. package/dist/envelope.d.ts +11 -0
  12. package/dist/envelope.js +7 -0
  13. package/dist/envelope.js.map +1 -0
  14. package/dist/explore-api.d.ts +187 -0
  15. package/dist/explore-api.js +302 -0
  16. package/dist/explore-api.js.map +1 -0
  17. package/dist/index.d.ts +2 -0
  18. package/dist/index.js +3 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/init-keys.d.ts +14 -0
  21. package/dist/init-keys.js +18 -0
  22. package/dist/init-keys.js.map +1 -0
  23. package/dist/init-mcp.d.ts +87 -0
  24. package/dist/init-mcp.js +198 -0
  25. package/dist/init-mcp.js.map +1 -0
  26. package/dist/init-skills.d.ts +80 -0
  27. package/dist/init-skills.js +256 -0
  28. package/dist/init-skills.js.map +1 -0
  29. package/dist/journey-api.d.ts +37 -0
  30. package/dist/journey-api.js +55 -0
  31. package/dist/journey-api.js.map +1 -0
  32. package/dist/load-api.d.ts +48 -0
  33. package/dist/load-api.js +84 -0
  34. package/dist/load-api.js.map +1 -0
  35. package/dist/mcp-api.d.ts +116 -0
  36. package/dist/mcp-api.js +359 -0
  37. package/dist/mcp-api.js.map +1 -0
  38. package/dist/mission-api.d.ts +56 -0
  39. package/dist/mission-api.js +65 -0
  40. package/dist/mission-api.js.map +1 -0
  41. package/dist/program.d.ts +77 -0
  42. package/dist/program.js +1938 -0
  43. package/dist/program.js.map +1 -0
  44. package/dist/record-api.d.ts +74 -0
  45. package/dist/record-api.js +93 -0
  46. package/dist/record-api.js.map +1 -0
  47. package/dist/regression-api.d.ts +17 -0
  48. package/dist/regression-api.js +16 -0
  49. package/dist/regression-api.js.map +1 -0
  50. package/dist/self-heal-adapter.d.ts +31 -0
  51. package/dist/self-heal-adapter.js +70 -0
  52. package/dist/self-heal-adapter.js.map +1 -0
  53. package/dist/source-api.d.ts +147 -0
  54. package/dist/source-api.js +208 -0
  55. package/dist/source-api.js.map +1 -0
  56. package/dist/source-run-api.d.ts +57 -0
  57. package/dist/source-run-api.js +91 -0
  58. package/dist/source-run-api.js.map +1 -0
  59. package/dist/ui-api.d.ts +35 -0
  60. package/dist/ui-api.js +369 -0
  61. package/dist/ui-api.js.map +1 -0
  62. package/dist/ux-api.d.ts +75 -0
  63. package/dist/ux-api.js +231 -0
  64. package/dist/ux-api.js.map +1 -0
  65. package/dist/version.d.ts +1 -0
  66. package/dist/version.js +37 -0
  67. package/dist/version.js.map +1 -0
  68. package/package.json +85 -0
  69. package/skills/jevitate-explore/SKILL.md +108 -0
  70. package/skills/jevitate-load-test/SKILL.md +42 -0
  71. package/skills/jevitate-mission-scope/SKILL.md +107 -0
  72. package/skills/jevitate-record/SKILL.md +78 -0
  73. package/skills/jevitate-run-journey/SKILL.md +72 -0
  74. package/skills/jevitate-sources/SKILL.md +88 -0
  75. package/skills/jevitate-ux-review/SKILL.md +68 -0
  76. package/ui/app.js +538 -0
  77. package/ui/index.html +546 -0
package/dist/ux-api.js ADDED
@@ -0,0 +1,231 @@
1
+ // ux-api.ts — the CLI layer over @jevitate/ux. Two surfaces:
2
+ // - runUxReview(): offline analysis over a saved Recording (`jevitate ux`).
3
+ // - runUsabilityMission(): live analysis during an exploration
4
+ // (`explore --strategy usability`) — reuses explore()'s loop via the
5
+ // additive onSnapshot hook, collects evidence per screen, and analyzes
6
+ // ONCE post-run (proper batching/budget; no model calls slow the browser).
7
+ //
8
+ // This is the ONLY place @jevitate/ux meets @jevitate/explore — the dep
9
+ // direction stays ux ⟂ explore (both are consumed here, neither imports the
10
+ // other). Findings are advisory; a UX finding never gates a run.
11
+ import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises";
12
+ import { tmpdir } from "node:os";
13
+ import { join } from "node:path";
14
+ import { PlaywrightBrowserPort } from "@jevitate/playwright";
15
+ import { CastActor, BrowseTheWeb } from "@jevitate/screenplay";
16
+ import { explore, assertAuthorizedExploreTarget, } from "@jevitate/explore";
17
+ import { UxAnalyzer, a11yChecks, buildReport, loadV1Rubric, } from "@jevitate/ux";
18
+ import { resolveDataDir } from "./data-dir.js";
19
+ const DEFAULT_JUDGMENT_BUDGET = 40;
20
+ function emptyBehavior() {
21
+ return { noProgress: false, backtracks: 0, formReentry: 0, dwellMs: 0, errors: 0 };
22
+ }
23
+ /** explore's live `Control` → ux `Control` (same shape by design; drop `stability`). */
24
+ function toUxControl(c) {
25
+ return {
26
+ index: c.index,
27
+ role: c.role,
28
+ name: c.name,
29
+ tag: c.tag,
30
+ inputType: c.inputType,
31
+ enabled: c.enabled,
32
+ summary: c.summary,
33
+ descriptor: c.descriptor,
34
+ };
35
+ }
36
+ /** a11y facts computable from a live snapshot's controls (honest subset). */
37
+ function a11yFactsFromControls(controls) {
38
+ return {
39
+ controls: controls.map((c) => ({
40
+ controlRef: `control:${c.index}`,
41
+ accessibleName: c.name.trim().length > 0 ? c.name : null,
42
+ focusOrder: c.index,
43
+ targetSize: null,
44
+ contrastRatio: null,
45
+ })),
46
+ };
47
+ }
48
+ /** A live Snapshot (+ extracted page text) → one screen's UxEvidence. */
49
+ export function snapshotToEvidence(snap, visibleText, appContext, job, history) {
50
+ const controls = snap.controls.map(toUxControl);
51
+ return {
52
+ screenId: snap.signature,
53
+ url: snap.url,
54
+ controls,
55
+ visibleText,
56
+ appContext,
57
+ ...(job !== undefined ? { job } : {}),
58
+ history,
59
+ behavior: emptyBehavior(),
60
+ a11yFacts: a11yFactsFromControls(controls),
61
+ };
62
+ }
63
+ function descriptorSummary(d) {
64
+ const parts = [d.role, d.name ?? d.testId ?? d.text].filter((s) => !!s);
65
+ return parts.join(" ") || "control";
66
+ }
67
+ function stepTarget(step) {
68
+ if ("target" in step && step.target)
69
+ return step.target;
70
+ return undefined;
71
+ }
72
+ /**
73
+ * A saved Recording → per-page UxEvidence. A Recording is deterministic and
74
+ * thin: it carries the CONTROLS the user touched (via each step's descriptor)
75
+ * and the page url, but NOT the full visible text or a11y geometry. So offline
76
+ * analysis is honestly partial — items needing `visibleText`/`a11yFacts` Skip,
77
+ * and coverage reports it. (The live mode carries the rich per-screen snapshot.)
78
+ */
79
+ export function recordingToEvidence(recording, appContext, job) {
80
+ const screens = [];
81
+ const history = [];
82
+ for (let i = 0; i < recording.pages.length; i++) {
83
+ const page = recording.pages[i];
84
+ const controls = [];
85
+ let idx = 0;
86
+ const seen = new Set();
87
+ for (const rs of page.steps) {
88
+ const d = stepTarget(rs.step);
89
+ if (!d)
90
+ continue;
91
+ const key = JSON.stringify(d);
92
+ if (seen.has(key))
93
+ continue;
94
+ seen.add(key);
95
+ controls.push({
96
+ index: idx++,
97
+ role: d.role ?? "",
98
+ name: d.name ?? d.testId ?? d.text ?? "",
99
+ tag: "",
100
+ inputType: null,
101
+ enabled: true,
102
+ summary: descriptorSummary(d),
103
+ descriptor: d,
104
+ });
105
+ }
106
+ const screenId = `${page.url}#${i}`;
107
+ screens.push({
108
+ screenId,
109
+ url: page.url,
110
+ controls,
111
+ visibleText: "",
112
+ appContext,
113
+ ...(job !== undefined ? { job } : {}),
114
+ history: [...history],
115
+ behavior: emptyBehavior(),
116
+ a11yFacts: { controls: [] },
117
+ });
118
+ history.push({ screenId, url: page.url });
119
+ }
120
+ return screens;
121
+ }
122
+ /**
123
+ * Offline UX review. FAIL-FAST: an analyzer `failed` outcome throws
124
+ * `UxAnalysisFailedError` (the CLI maps it to a non-zero fail envelope) — never
125
+ * a fabricated "clean" report.
126
+ */
127
+ export async function runUxReview(opts) {
128
+ const analyzer = new UxAnalyzer({ judge: opts.judge, a11yChecker: a11yChecks });
129
+ const screens = recordingToEvidence(opts.recording, opts.appContext, opts.appContext.job);
130
+ const outcome = await analyzer.analyze({
131
+ screens,
132
+ rubric: loadV1Rubric(),
133
+ appContext: opts.appContext,
134
+ secrets: opts.secrets,
135
+ judgmentBudget: opts.judgmentBudget ?? DEFAULT_JUDGMENT_BUDGET,
136
+ });
137
+ if (outcome.kind === "failed") {
138
+ throw new UxAnalysisFailedError(outcome.reason, outcome.screenId, outcome.rubricItemId);
139
+ }
140
+ const report = buildReport(outcome);
141
+ const outDir = opts.outDir ?? resolveDataDir(["ux-reports"]);
142
+ await mkdir(outDir, { recursive: true });
143
+ const iso = (opts.nowIso ?? (() => new Date().toISOString()))();
144
+ const reportPath = join(outDir, `ux-${iso.replace(/[:.]/g, "-")}.json`);
145
+ await writeFile(reportPath, `${JSON.stringify(report, null, 2)}\n`, "utf8");
146
+ return { report, reportPath };
147
+ }
148
+ export class UxAnalysisFailedError extends Error {
149
+ screenId;
150
+ rubricItemId;
151
+ code = "E_UX_ANALYSIS";
152
+ constructor(reason, screenId, rubricItemId) {
153
+ super(`UX analysis failed: ${reason}${screenId ? ` (screen ${screenId})` : ""}`);
154
+ this.screenId = screenId;
155
+ this.rubricItemId = rubricItemId;
156
+ this.name = "UxAnalysisFailedError";
157
+ }
158
+ }
159
+ /**
160
+ * Live usability mission. Guardrail #1 authorizes BEFORE opening a browser.
161
+ * Reuses explore()'s loop (goal = the job) and, via the additive onSnapshot
162
+ * hook, collects one screen's evidence per observation — then analyzes ONCE
163
+ * after the run. A UX finding NEVER gates the loop (the hook's result is
164
+ * ignored by explore). The temp profile dir is always removed.
165
+ */
166
+ export async function runUsabilityMission(opts) {
167
+ const origin = assertAuthorizedExploreTarget(opts.url, opts.allowlist);
168
+ const portFactory = opts.browserPortFactory ?? (() => new PlaywrightBrowserPort());
169
+ const port = portFactory();
170
+ const profileDir = await mkdtemp(join(tmpdir(), "jevitate-usability-"));
171
+ const session = await port.open({
172
+ profileDir,
173
+ headless: true,
174
+ allowedOrigins: [...opts.allowlist],
175
+ baseUrl: origin,
176
+ });
177
+ const collected = [];
178
+ const history = [];
179
+ const extract = opts.extractText ??
180
+ (async (s) => s.page.evaluate(() => (typeof document !== "undefined" && document.body ? document.body.innerText : "")));
181
+ try {
182
+ const actor = CastActor.named("usability-mission").whoCan(new BrowseTheWeb(session, [...opts.allowlist]));
183
+ const run = await explore({
184
+ actor,
185
+ judge: opts.judge,
186
+ gen: opts.gen,
187
+ goal: opts.job,
188
+ allowlist: opts.allowlist,
189
+ startUrl: opts.url,
190
+ bounds: opts.bounds,
191
+ secrets: opts.secrets,
192
+ site: origin,
193
+ missionContext: "usability review: pursue the stated job as a plausible first-time user, using only what is on screen",
194
+ onSnapshot: async (snap) => {
195
+ let visibleText = "";
196
+ try {
197
+ visibleText = await extract(session);
198
+ }
199
+ catch {
200
+ visibleText = ""; // best-effort; the analyzer Skips visibleText items honestly
201
+ }
202
+ const ev = snapshotToEvidence(snap, visibleText, opts.appContext, opts.job, [...history]);
203
+ collected.push(ev);
204
+ history.push({ screenId: ev.screenId, url: ev.url });
205
+ },
206
+ });
207
+ const analyzer = new UxAnalyzer({ judge: opts.judge, a11yChecker: a11yChecks });
208
+ const outcome = await analyzer.analyze({
209
+ screens: collected,
210
+ rubric: loadV1Rubric(),
211
+ appContext: opts.appContext,
212
+ secrets: opts.secrets,
213
+ judgmentBudget: opts.judgmentBudget ?? DEFAULT_JUDGMENT_BUDGET,
214
+ });
215
+ if (outcome.kind === "failed") {
216
+ throw new UxAnalysisFailedError(outcome.reason, outcome.screenId, outcome.rubricItemId);
217
+ }
218
+ const report = buildReport(outcome);
219
+ const outDir = opts.outDir ?? resolveDataDir(["ux-reports"]);
220
+ await mkdir(outDir, { recursive: true });
221
+ const iso = (opts.nowIso ?? (() => new Date().toISOString()))();
222
+ const reportPath = join(outDir, `usability-${iso.replace(/[:.]/g, "-")}.json`);
223
+ await writeFile(reportPath, `${JSON.stringify(report, null, 2)}\n`, "utf8");
224
+ return { report, reportPath, stop: run.stop, screensObserved: collected.length };
225
+ }
226
+ finally {
227
+ await session.close();
228
+ await rm(profileDir, { recursive: true, force: true });
229
+ }
230
+ }
231
+ //# sourceMappingURL=ux-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ux-api.js","sourceRoot":"","sources":["../src/ux-api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function readCliVersion(): string;
@@ -0,0 +1,37 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { fileURLToPath } from "node:url";
3
+ import { dirname, join } from "node:path";
4
+ /**
5
+ * The CLI's real published version, resolved at RUNTIME from @jevitate/cli's
6
+ * own package.json — never a hardcoded literal that silently drifts from what
7
+ * npm actually shipped (ticket #27).
8
+ *
9
+ * The CLI ships BUNDLED (a single `dist/bin.js` built by build.mjs/esbuild),
10
+ * and npm always includes package.json alongside `dist/`. This module is
11
+ * inlined INTO that bundle, so at runtime `import.meta.url` points at
12
+ * `dist/bin.js` and `../package.json` resolves to the shipped
13
+ * `packages/cli/package.json`. The SAME `../package.json` also resolves
14
+ * correctly from the un-bundled `tsc` output (`dist/version.js`) and from the
15
+ * TypeScript source under `src/` during tests — every layout keeps this module
16
+ * exactly one directory below the package root.
17
+ *
18
+ * If the file is somehow unreadable (an exotic pack, a broken install) we fall
19
+ * back to a safe constant rather than crashing `--version`. This is a display
20
+ * value, not a safety decision, so a fallback here is benign.
21
+ */
22
+ const FALLBACK_VERSION = "0.0.0";
23
+ export function readCliVersion() {
24
+ try {
25
+ const here = dirname(fileURLToPath(import.meta.url));
26
+ const pkgPath = join(here, "..", "package.json");
27
+ const parsed = JSON.parse(readFileSync(pkgPath, "utf8"));
28
+ if (typeof parsed.version === "string" && parsed.version.length > 0) {
29
+ return parsed.version;
30
+ }
31
+ }
32
+ catch {
33
+ // fall through to the safe display fallback below
34
+ }
35
+ return FALLBACK_VERSION;
36
+ }
37
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,gBAAgB,GAAG,OAAO,CAAC;AAEjC,MAAM,UAAU,cAAc;IAC5B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACrD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;QACjD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAA0B,CAAC;QAClF,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpE,OAAO,MAAM,CAAC,OAAO,CAAC;QACxB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,kDAAkD;IACpD,CAAC;IACD,OAAO,gBAAgB,CAAC;AAC1B,CAAC"}
package/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "@jevitate/cli",
3
+ "version": "0.1.0",
4
+ "description": "Jevitate CLI — author, run, and orchestrate browser-automation Journeys from the command line.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/matt-cochran/jevitate.git",
10
+ "directory": "packages/cli"
11
+ },
12
+ "homepage": "https://jevitate.com",
13
+ "bugs": "https://github.com/matt-cochran/jevitate/issues",
14
+ "author": "Matt Cochran",
15
+ "keywords": [
16
+ "browser-automation",
17
+ "testing",
18
+ "e2e",
19
+ "playwright",
20
+ "screenplay",
21
+ "journeys",
22
+ "exploration",
23
+ "usability",
24
+ "mcp",
25
+ "cli",
26
+ "jevitate"
27
+ ],
28
+ "engines": {
29
+ "node": ">=20"
30
+ },
31
+ "bin": {
32
+ "jevitate": "dist/bin.js"
33
+ },
34
+ "main": "dist/index.js",
35
+ "types": "dist/index.d.ts",
36
+ "exports": {
37
+ ".": "./dist/index.js"
38
+ },
39
+ "files": [
40
+ "dist",
41
+ "skills",
42
+ "ui"
43
+ ],
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "dependencies": {
48
+ "@clack/prompts": "^0.9.1",
49
+ "@modelcontextprotocol/sdk": "^1.0.0",
50
+ "@openrouter/ai-sdk-provider": "^3.1.0",
51
+ "@typesafe-ai/sdk": "^0.6.0",
52
+ "ai": "^7.0.107",
53
+ "better-sqlite3": "^11.0.0",
54
+ "commander": "^12.0.0",
55
+ "playwright": "^1.49.0"
56
+ },
57
+ "devDependencies": {
58
+ "esbuild": "^0.28.2",
59
+ "@jevitate/ai-core": "0.1.0",
60
+ "@jevitate/daemon": "0.1.0",
61
+ "@jevitate/interpreter": "0.1.0",
62
+ "@jevitate/explore": "0.1.0",
63
+ "@jevitate/domain": "0.1.0",
64
+ "@jevitate/missions": "0.1.0",
65
+ "@jevitate/load": "0.1.0",
66
+ "@jevitate/inbox": "0.1.0",
67
+ "@jevitate/example-site": "0.1.0",
68
+ "@jevitate/mcp-facade": "0.1.0",
69
+ "@jevitate/recording": "0.1.0",
70
+ "@jevitate/playwright": "0.1.0",
71
+ "@jevitate/journey": "0.1.0",
72
+ "@jevitate/runtime": "0.1.0",
73
+ "@jevitate/regression": "0.1.0",
74
+ "@jevitate/recorder": "0.1.0",
75
+ "@jevitate/skills": "0.1.0",
76
+ "@jevitate/ux": "0.1.0",
77
+ "@jevitate/sources": "0.1.0",
78
+ "@jevitate/storage-sqlite": "0.1.0",
79
+ "@jevitate/screenplay": "0.1.0"
80
+ },
81
+ "scripts": {
82
+ "build": "tsc --build",
83
+ "bundle": "node ./build.mjs"
84
+ }
85
+ }
@@ -0,0 +1,108 @@
1
+ ---
2
+ name: jevitate-explore
3
+ description: Scopes and runs a bounded exploration mission via `jevitate explore` (goal, coverage, exploratory, adversarial, or capability-scoped feature strategies) or the MCP `queue_exploration` tool — drives Jev (not you) on an authorized target and emits a replayable Recording. Use when there is no existing Journey for what needs testing and a human wants autonomous, bounded exploration.
4
+ ---
5
+
6
+ You scope an exploration mission; you never drive the browser yourself. Jev
7
+ (the judgment gateway) makes every click/type/select decision inside the
8
+ exploration engine; a generative model writes only form text; an independent,
9
+ user-supplied assertion — not the model's own "done" signal — decides whether
10
+ a goal-based mission actually succeeded. Your entire job is composing a good
11
+ mission and reading the result honestly.
12
+
13
+ ## Before you start
14
+
15
+ - The target must already be authorized. This tool refuses an unknown or
16
+ unauthorized target by design (`E_UNAUTHORIZED_EXPLORE_TARGET` — authoring/test
17
+ plane only, never production writes). The allowlist defaults to the `--url`'s
18
+ own origin; widen it only with explicit `--allow <origin>` values the human
19
+ gave you. If you don't know whether a target is authorized, ask.
20
+ - Pick a strategy deliberately (default is `goal`):
21
+ - `--strategy goal` (default): needs `--goal` and `--success`. Reaches a
22
+ stated end state.
23
+ - `--strategy coverage` / `--strategy exploratory`: state-coverage by
24
+ induction; the frontier itself is the objective, so it takes no
25
+ goal/success.
26
+ - `--strategy adversarial`: bounded misuse ("try to break it") whose stop
27
+ decision comes from a trusted hard-signal defect oracle, never Jev's own
28
+ signal. Needs only `--url`.
29
+ - `--feature <name> --route <glob>`: capability-scoped feature testing
30
+ (model-free; no goal/success, no gateway selection needed).
31
+
32
+ ## Writing a goal-based mission
33
+
34
+ - Write a `--goal` in plain language describing the end state, not a sequence of
35
+ clicks ("place an order for one widget and reach the confirmation page," not
36
+ "click .add-to-cart then click #checkout").
37
+ - Write a `--success` assertion independent of what you expect Jev to report —
38
+ checkable from page state alone, e.g. `urlIncludes:/confirmation`. Jev's own
39
+ "goal met?" judgment is advisory; the assertion is what decides success. If
40
+ you cannot state a concrete, checkable assertion, the goal is too vague —
41
+ narrow it first.
42
+
43
+ ## Running it
44
+
45
+ - Goal: `jevitate explore --url <authorized-url> --goal "<goal>" --success
46
+ urlIncludes:/inbox [--allow <origin>] [--secret <value>] [--max-actions <n>]
47
+ [--max-decisions <n>] --real --json`. Model-driving strategies (`goal`,
48
+ `coverage`, `exploratory`, `adversarial`) need a gateway: `--real` (live
49
+ Jev + OpenRouter, after `jevitate ai setup`) or `--fake-ai` (a deterministic
50
+ pipeline smoke — it will NOT drive to a real goal). No selection fails closed.
51
+ - Adversarial: `jevitate explore --strategy adversarial --url <authorized-url>
52
+ --real --json`.
53
+ - Coverage: `jevitate explore --strategy coverage --url <authorized-url> --real
54
+ --json`.
55
+ - Feature: `jevitate explore --feature checkout --route "/cart/**" --url
56
+ <authorized-url> --json` (model-free).
57
+ - Authoring a promotable Journey: `jevitate explore-author-journey --url
58
+ <authorized-url> --goal "<goal>" --success <assertion> --id <journey-id>
59
+ --name "<name>" --real --json` — drives the goal-based mission and writes an
60
+ UNPROMOTED, parameterized Journey to the store. It is never auto-promoted.
61
+ - MCP: the `queue_exploration` tool — `queue_exploration({ target, goal, ... })`
62
+ — enqueues a bounded mission and returns a `missionId` immediately (it does
63
+ not run inline). `target` must be a pre-registered mission target id, not a
64
+ raw URL.
65
+
66
+ ## Reading the result
67
+
68
+ - The output is a deterministic `Recording`. Report the actual `outcome`, not an
69
+ optimistic gloss — a `blocked` or `exhausted` (budget) outcome with a partial
70
+ Recording is a precise repro of how far Jev got, but it is not success. A
71
+ goal-based run is success only when `outcome` is `succeeded`; adversarial CI
72
+ gates on `outcome === "defect"`; a coverage run gates on discovered defects.
73
+ - A successful goal-based Recording is a candidate to hand to `jevitate-record`
74
+ for postdoc review, or to author directly via `explore-author-journey` —
75
+ say so when it succeeds.
76
+
77
+ ## What you must never do
78
+
79
+ - Never widen `--allow`/the target beyond what the human explicitly authorized.
80
+ - Never treat Jev's in-loop "done" signal as the final word — only the
81
+ `--success` assertion result is, for goal-based runs.
82
+ - Never ask the exploration engine to do anything on a production target — this
83
+ is authoring/test-plane only, always.
84
+ - Never fall back to a generic browser-automation tool if `jevitate explore`
85
+ is unavailable — that bypasses every guardrail this skill enforces.
86
+
87
+ ## Running it through MCP (shipped)
88
+
89
+ - `jevitate mcp` starts the stdio MCP server; `queue_exploration` is one of its
90
+ allowlisted, wired tools. Register it in your harness with `jevitate mcp
91
+ --print-config <claude|cursor|codex|json>`, or let `jevitate init` register it
92
+ for each detected runtime.
93
+ - `queue_exploration` needs a PROMOTED mission target, not a raw URL. Register
94
+ and promote one first with `jevitate mission target add <id> ...` then
95
+ `jevitate mission target promote <id>` (see `jevitate-mission-scope`). It
96
+ enqueues and returns a `missionId` immediately — it never runs inline.
97
+
98
+ ## Known gaps
99
+
100
+ - Offline only: `queue_exploration` enqueues but does not itself run the
101
+ mission; a separate runner drains the queue. Treat the returned `missionId`
102
+ as "accepted," not "finished."
103
+ - The MCP server also registers 8 inbox/command-queue tools
104
+ (`queue_retrieval`, `queue_action`, `get_command`, `list_incoming`,
105
+ `get_thread`, `approve_action`, `cancel_command`, `get_site_health`) that
106
+ currently return a typed `not_implemented` error — they are reserved surface,
107
+ not usable yet. The four wired tools are `find_capabilities`, `run_journey`,
108
+ `queue_exploration`, and `ai_generate_text`.
@@ -0,0 +1,42 @@
1
+ ---
2
+ name: jevitate-load-test
3
+ description: Runs a seeded, human-paced concurrent load test of a promoted Journey via `jevitate load run`, against an explicitly authorized target only. Use when a user wants a capacity/throughput measurement, not a single functional run.
4
+ ---
5
+
6
+ You scope and run a throughput/capacity measurement, never a single functional
7
+ check (use `jevitate-run-journey` for that instead).
8
+
9
+ ## Before you start — authorization is mandatory, not optional
10
+
11
+ - `jevitate load run` refuses to start with zero `--authorized-origin` values
12
+ (`E_LOAD_RUN`, "at least one --authorized-origin is required"). This is not a
13
+ formality — running N concurrent actors against a target the operator hasn't
14
+ explicitly authorized for load is exactly the kind of irreversible,
15
+ high-blast-radius action this platform's guardrails exist to prevent. Never
16
+ add an `--authorized-origin` value the human did not explicitly state.
17
+
18
+ ## Running it
19
+
20
+ - `jevitate load run <journeyId> --authorized-origin <origin> [--authorized-origin
21
+ <origin2> ...] --param k=v --concurrency <n> --iterations <n> --seed <n>
22
+ --json`.
23
+ - `<journeyId>` must be a promoted Journey id (same discovery flow as
24
+ `jevitate-run-journey`: `jevitate journey find`/`find_capabilities` first).
25
+ - `--seed` makes the run reproducible — if the human wants to compare two
26
+ configurations, keep the seed fixed and vary only `--concurrency`/
27
+ `--iterations`.
28
+
29
+ ## Reading the result
30
+
31
+ - The report's metrics are always labeled `measured` or `modeled` — never
32
+ report a `modeled` number as if it were `measured`, and if the report says a
33
+ requested live run could not run, that is a failure to surface honestly, not
34
+ a number to approximate around.
35
+
36
+ ## What you must never do
37
+
38
+ - Never run a load test against a target without an explicit, human-stated
39
+ `--authorized-origin`.
40
+ - Never scale `--concurrency`/`--iterations` up beyond what the human asked for
41
+ "just to get a cleaner number" — a bigger run is a bigger real-world load on
42
+ someone's infrastructure.
@@ -0,0 +1,107 @@
1
+ ---
2
+ name: jevitate-mission-scope
3
+ description: Reads a diff, pull request, changelog, or user story and scopes change-driven testing — identifies at-risk routes/features, maps them to existing promoted Journeys, runs the most relevant ones, and recommends which newly-discovered repros to promote. Use when a user wants to know "what should I test" after a code change, rather than testing everything or picking targets blindly.
4
+ ---
5
+
6
+ You scope testing to what actually changed. You never drive a browser yourself —
7
+ Jev (the judgment gateway) makes browser-driving decisions inside the
8
+ exploration engine, and the deterministic Journey runner executes promoted
9
+ Journeys; your job is entirely the "what and why," spending tokens once to
10
+ target instead of on every click.
11
+
12
+ ## 1. Read the change
13
+
14
+ Given a diff, PR description, changelog entry, or user story, identify:
15
+ - Which routes/pages/features it plausibly touches (file paths → routes is a
16
+ judgment call — a change to `src/checkout/*` plausibly affects the checkout
17
+ flow even if no route string literally appears in the diff).
18
+ - Whether it's additive (a new feature, lower regression risk to *existing*
19
+ Journeys but a candidate for a *new* exploration goal) or modifying existing
20
+ behavior (higher regression risk — prioritize existing promoted Journeys that
21
+ exercise the touched area).
22
+ - Anything explicitly called out as risky (auth, payment, data deletion) —
23
+ weight these higher regardless of diff size.
24
+
25
+ State your reasoning in one or two sentences before acting — "what changed" and
26
+ "why this is (or isn't) at risk" — so the human reviewing your output can
27
+ sanity-check the judgment, not just the commands you ran.
28
+
29
+ ## 2. Map to existing promoted Journeys first
30
+
31
+ - `jevitate journey find "<term>" --json` (or MCP `find_capabilities`) once per
32
+ at-risk area you identified in step 1. Try more than one query term per area
33
+ if the first returns nothing plausible — a Journey's `name`/`description`
34
+ might not use the same words as the diff.
35
+ - For each match, run it: `jevitate journey run <id> --param k=v ... --json` (or
36
+ MCP `run_journey`). Read the `outcome` field; treat only `"ok"`/`"healed"` as
37
+ a pass, never a non-success outcome.
38
+ - If a promoted Journey exists for an at-risk area and it passes, that area has
39
+ regression coverage — say so plainly; you do not need to additionally explore
40
+ it from scratch.
41
+
42
+ ## 3. Where no promoted Journey covers an at-risk area
43
+
44
+ This is exactly the gap the exploration engine and `queue_exploration` exist
45
+ for. Prefer `jevitate-explore` (see that skill) or the `queue_exploration` MCP
46
+ tool to scope a **bounded** mission at the specific at-risk area:
47
+ - For a concrete end state, a goal-based mission: `jevitate explore --url <authorized-url>
48
+ --goal "<goal>" --success <assertion>` — the `--success` assertion written
49
+ from the diff/story's stated intent (e.g. `urlIncludes:/confirmation`), never
50
+ a vague "test everything" goal.
51
+ - For a named capability with route scoping: `jevitate explore --feature <name>
52
+ --route <glob> --url <authorized-url>` (model-free capability coverage).
53
+ - For "try to break the changed area": `jevitate explore --strategy adversarial
54
+ --url <authorized-url>` (bounded misuse + a trusted hard-signal defect oracle).
55
+ - For state coverage of the changed area: `jevitate explore --strategy coverage
56
+ --url <authorized-url>`.
57
+ - Only if you cannot reach an authorized target at all (or `jevitate explore`
58
+ is genuinely absent from your environment) do you stop and report the gap —
59
+ never fall back to a generic browser-automation tool; that would bypass every
60
+ guardrail (bounded budget, independent assertion, redaction, deterministic
61
+ Recording output) this platform exists to provide.
62
+
63
+ ## 4. Recommend promotion of new repros
64
+
65
+ After a `jevitate explore` mission produces a new `Recording` (a "discovered
66
+ repro"), you do not promote it yourself — promotion is a human-approval gate.
67
+ Two additive paths help here: `jevitate explore-author-journey` writes an
68
+ UNPROMOTED, parameterized Journey (still human-gated), and `jevitate regression
69
+ capture --from <recording.json> --id <id>` turns a failing Recording into a
70
+ committed regression artifact. Recommend, with reasoning: which discovered
71
+ repros are worth promoting into the durable regression suite (does it cover a
72
+ genuinely new, at-risk path? did it reveal a defect worth a permanent
73
+ prove-broken/prove-fixed test?), and which are one-off exploratory noise not
74
+ worth keeping. Narrow the regression set to what matters — quality over
75
+ quantity.
76
+
77
+ ## What you must never do
78
+
79
+ - Never test everything indiscriminately when a diff/story is available to
80
+ scope from — that defeats the entire purpose of this skill.
81
+ - Never invent a Journey id, route, or param value — every id you run must have
82
+ come from a `find`/`find_capabilities` result you saw in this session.
83
+ - Never promote anything yourself — recommend, and let a human decide.
84
+
85
+ ## Registering and promoting a mission target (for queue_exploration)
86
+
87
+ `queue_exploration` runs against a PROMOTED mission target, never a raw URL.
88
+ When you want to queue a scoped mission at an at-risk area via MCP:
89
+ - `jevitate mission target add <id> --url <authorized-url> [--goal ...] [--route
90
+ <glob> ...] --json` registers the target (unpromoted).
91
+ - `jevitate mission target list --json` shows registered targets and their
92
+ promoted/unpromoted state.
93
+ - `jevitate mission target promote <id> --json` promotes it — a human-gated act,
94
+ same as Journey promotion. Only a promoted target is enqueueable.
95
+
96
+ Then `queue_exploration({ target: "<id>", ... })` (via the `jevitate mcp`
97
+ server, registered with `jevitate mcp --print-config ...` or `jevitate init`)
98
+ enqueues a bounded mission and returns a `missionId`.
99
+
100
+ ## Known gaps
101
+
102
+ - The MCP inbox/command-queue tools (`queue_retrieval`, `queue_action`,
103
+ `get_command`, `list_incoming`, `get_thread`, `approve_action`,
104
+ `cancel_command`, `get_site_health`) are registered on the `jevitate mcp`
105
+ server but return a typed `not_implemented` error today — the wired tools are
106
+ `find_capabilities`, `run_journey`, `queue_exploration`, and
107
+ `ai_generate_text`. Scoping and running work fully through those four.