@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.
- package/LICENSE +21 -0
- package/dist/ai-cli.d.ts +31 -0
- package/dist/ai-cli.js +238 -0
- package/dist/ai-cli.js.map +1 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +50663 -0
- package/dist/bin.js.map +1 -0
- package/dist/data-dir.d.ts +9 -0
- package/dist/data-dir.js +13 -0
- package/dist/data-dir.js.map +1 -0
- package/dist/envelope.d.ts +11 -0
- package/dist/envelope.js +7 -0
- package/dist/envelope.js.map +1 -0
- package/dist/explore-api.d.ts +187 -0
- package/dist/explore-api.js +302 -0
- package/dist/explore-api.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/init-keys.d.ts +14 -0
- package/dist/init-keys.js +18 -0
- package/dist/init-keys.js.map +1 -0
- package/dist/init-mcp.d.ts +87 -0
- package/dist/init-mcp.js +198 -0
- package/dist/init-mcp.js.map +1 -0
- package/dist/init-skills.d.ts +80 -0
- package/dist/init-skills.js +256 -0
- package/dist/init-skills.js.map +1 -0
- package/dist/journey-api.d.ts +37 -0
- package/dist/journey-api.js +55 -0
- package/dist/journey-api.js.map +1 -0
- package/dist/load-api.d.ts +48 -0
- package/dist/load-api.js +84 -0
- package/dist/load-api.js.map +1 -0
- package/dist/mcp-api.d.ts +116 -0
- package/dist/mcp-api.js +359 -0
- package/dist/mcp-api.js.map +1 -0
- package/dist/mission-api.d.ts +56 -0
- package/dist/mission-api.js +65 -0
- package/dist/mission-api.js.map +1 -0
- package/dist/program.d.ts +77 -0
- package/dist/program.js +1938 -0
- package/dist/program.js.map +1 -0
- package/dist/record-api.d.ts +74 -0
- package/dist/record-api.js +93 -0
- package/dist/record-api.js.map +1 -0
- package/dist/regression-api.d.ts +17 -0
- package/dist/regression-api.js +16 -0
- package/dist/regression-api.js.map +1 -0
- package/dist/self-heal-adapter.d.ts +31 -0
- package/dist/self-heal-adapter.js +70 -0
- package/dist/self-heal-adapter.js.map +1 -0
- package/dist/source-api.d.ts +147 -0
- package/dist/source-api.js +208 -0
- package/dist/source-api.js.map +1 -0
- package/dist/source-run-api.d.ts +57 -0
- package/dist/source-run-api.js +91 -0
- package/dist/source-run-api.js.map +1 -0
- package/dist/ui-api.d.ts +35 -0
- package/dist/ui-api.js +369 -0
- package/dist/ui-api.js.map +1 -0
- package/dist/ux-api.d.ts +75 -0
- package/dist/ux-api.js +231 -0
- package/dist/ux-api.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +37 -0
- package/dist/version.js.map +1 -0
- package/package.json +85 -0
- package/skills/jevitate-explore/SKILL.md +108 -0
- package/skills/jevitate-load-test/SKILL.md +42 -0
- package/skills/jevitate-mission-scope/SKILL.md +107 -0
- package/skills/jevitate-record/SKILL.md +78 -0
- package/skills/jevitate-run-journey/SKILL.md +72 -0
- package/skills/jevitate-sources/SKILL.md +88 -0
- package/skills/jevitate-ux-review/SKILL.md +68 -0
- package/ui/app.js +538 -0
- 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;
|
package/dist/version.js
ADDED
|
@@ -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.
|