waygraph 0.15.10 → 0.15.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -8
- package/dist/auto-explore.d.ts +7 -6
- package/dist/auto-explore.js +43 -32
- package/dist/auto-session-ipc.d.ts +13 -0
- package/dist/auto-session-ipc.js +119 -7
- package/dist/auto-session.d.ts +16 -13
- package/dist/auto-session.js +72 -53
- package/dist/block-inject.d.ts +15 -0
- package/dist/block-inject.js +91 -0
- package/dist/browser.d.ts +15 -0
- package/dist/browser.js +29 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +323 -47
- package/dist/coverage-gap.d.ts +11 -0
- package/dist/coverage-gap.js +119 -0
- package/dist/graph.d.ts +1 -0
- package/dist/graph.js +99 -91
- package/dist/pilot-overlay.d.ts +7 -1
- package/dist/pilot-overlay.js +59 -41
- package/dist/pilot.d.ts +7 -2
- package/dist/pilot.js +34 -7
- package/dist/practices-check.d.ts +14 -0
- package/dist/practices-check.js +115 -0
- package/examples/saucedemo/.waygraph-auto/0b937f26.log +4 -0
- package/examples/saucedemo/.waygraph-auto/2b608ed3.log +0 -0
- package/examples/saucedemo/.waygraph-auto/2c13567b.log +0 -0
- package/examples/saucedemo/.waygraph-auto/2d9622a5.log +4 -0
- package/examples/saucedemo/.waygraph-auto/305f6e5b.log +4 -0
- package/examples/saucedemo/.waygraph-auto/418befbf.log +4 -0
- package/examples/saucedemo/.waygraph-auto/483bd763.log +4 -0
- package/examples/saucedemo/.waygraph-auto/48e3596a.log +0 -0
- package/examples/saucedemo/.waygraph-auto/4adfbe91.json +8 -0
- package/examples/saucedemo/.waygraph-auto/4adfbe91.log +0 -0
- package/examples/saucedemo/.waygraph-auto/4f0e33e7.log +4 -0
- package/examples/saucedemo/.waygraph-auto/5bd8fedf.log +4 -0
- package/examples/saucedemo/.waygraph-auto/5dde8331.log +4 -0
- package/examples/saucedemo/.waygraph-auto/68903903.log +0 -0
- package/examples/saucedemo/.waygraph-auto/78bbf43c.log +0 -0
- package/examples/saucedemo/.waygraph-auto/7efa646f.log +4 -0
- package/examples/saucedemo/.waygraph-auto/9cecb0c8.log +4 -0
- package/examples/saucedemo/.waygraph-auto/aae57d51.log +0 -0
- package/examples/saucedemo/.waygraph-auto/b61a08d8.log +0 -0
- package/examples/saucedemo/.waygraph-auto/c946f66c.log +0 -0
- package/examples/saucedemo/.waygraph-auto/cd0d5b50.log +4 -0
- package/examples/saucedemo/.waygraph-auto/cd5b203c.log +0 -0
- package/examples/saucedemo/.waygraph-auto/ea2df316.log +4 -0
- package/examples/saucedemo/.waygraph-auto/ee60ed24.log +0 -0
- package/examples/saucedemo/.waygraph-auto/f2b6cb1b.log +0 -0
- package/examples/saucedemo/.waygraph-auto/f392cdc8.log +0 -0
- package/package.json +2 -2
- package/templates/scaffold/package.json +1 -1
- package/templates/skills/waygraph-convention.skill.md +11 -1
- package/templates/skills/waygraph-pilot.skill.md +104 -10
package/dist/cli.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* run Execute; `--blocks` `--data` `--non-headless` `--video`
|
|
9
9
|
*
|
|
10
10
|
* Also: list / nav / validate / check / graph / init / agent-dive / traverse / try
|
|
11
|
+
* Skills: --skill / --skill-pilot / --skill-pilot-blind / --skill-convention
|
|
11
12
|
* Aliases (one release): `chain` -> run/demo --blocks; `--autoplay` -> `--auto-next`
|
|
12
13
|
*
|
|
13
14
|
* "project" defaults to cwd. Flags beat WAYGRAPH_* env.
|
|
@@ -20,8 +21,11 @@ import { spawn } from "node:child_process";
|
|
|
20
21
|
import { discoverGraph, toMermaid, findOrphanBlocks, findBlockPath } from "./graph.js";
|
|
21
22
|
import { checkMap } from "./map-check.js";
|
|
22
23
|
import { runAutoExplore } from "./auto-explore-run.js";
|
|
23
|
-
import { pilotStart } from "./pilot.js";
|
|
24
|
-
import {
|
|
24
|
+
import { pilotStart, pilotAttach } from "./pilot.js";
|
|
25
|
+
import { browserStart, listBrowserSessions, stopBrowserSession, stopAllBrowserSessions, } from "./browser.js";
|
|
26
|
+
import { resolveInjectRoots } from "./block-inject.js";
|
|
27
|
+
import { collectPracticeWarnings, practiceKindLabel } from "./practices-check.js";
|
|
28
|
+
import { spawnDetachedSession, requestSession, runAttachLoop, runAutoServeCommand, resolveSessionMeta, } from "./auto-session-ipc.js";
|
|
25
29
|
import { runAgentDive, printSkillIndex, readSkillMarkdown, SKILL_FLAG_MAP, } from "./agent-dive.js";
|
|
26
30
|
import { runTraverse } from "./traverse-run.js";
|
|
27
31
|
import { parseMinEdgeCoverage } from "./traverse-coverage.js";
|
|
@@ -6679,7 +6683,8 @@ function initCommand(projectName) {
|
|
|
6679
6683
|
console.log(" npx playwright install chromium");
|
|
6680
6684
|
console.log(" npm test");
|
|
6681
6685
|
console.log(" waygraph list # .flow.ts → export map");
|
|
6682
|
-
console.log(" waygraph check # nav hygiene + orphan Blocks");
|
|
6686
|
+
console.log(" waygraph check # nav hygiene + inline selectors + bad practices + orphan Blocks");
|
|
6687
|
+
console.log(" waygraph typecheck # tsc --noEmit + bad-practice warnings (use --no-practices to skip)");
|
|
6683
6688
|
console.log(" waygraph auto # interactive explore (headed panel)");
|
|
6684
6689
|
console.log(" waygraph auto --cli # same menus in the terminal");
|
|
6685
6690
|
console.log(" waygraph demo --blocks exampleFlow");
|
|
@@ -6687,7 +6692,17 @@ function initCommand(projectName) {
|
|
|
6687
6692
|
console.log("");
|
|
6688
6693
|
console.log(" Layout: see STRUCTURE.md (or https://deviate-dv8.github.io/waygraph/scaffold.html)");
|
|
6689
6694
|
}
|
|
6690
|
-
|
|
6695
|
+
function printPracticeReport(projectDir, practiceWarnings) {
|
|
6696
|
+
if (practiceWarnings.length === 0) {
|
|
6697
|
+
console.log(`waygraph check: no bad-practice patterns under ${projectDir}`);
|
|
6698
|
+
return;
|
|
6699
|
+
}
|
|
6700
|
+
for (const w of practiceWarnings) {
|
|
6701
|
+
console.warn(`waygraph check: ${w.file} (${practiceKindLabel(w.kind)}) — ${w.detail}`);
|
|
6702
|
+
}
|
|
6703
|
+
console.log(`waygraph check: ${practiceWarnings.length} bad-practice warning${practiceWarnings.length === 1 ? "" : "s"}`);
|
|
6704
|
+
}
|
|
6705
|
+
async function checkCommand(projectDir, opts) {
|
|
6691
6706
|
const files = discoverBlocks(projectDir);
|
|
6692
6707
|
const navWarnings = [];
|
|
6693
6708
|
const selWarnings = [];
|
|
@@ -6717,7 +6732,25 @@ async function checkCommand(projectDir) {
|
|
|
6717
6732
|
}
|
|
6718
6733
|
}
|
|
6719
6734
|
}
|
|
6720
|
-
|
|
6735
|
+
const practiceWarnings = collectPracticeWarnings(projectDir, walkDir, {
|
|
6736
|
+
disabled: opts?.noPractices === true,
|
|
6737
|
+
});
|
|
6738
|
+
return { navWarnings, selWarnings, practiceWarnings };
|
|
6739
|
+
}
|
|
6740
|
+
async function runTypecheckCommand(projectDir, noPractices) {
|
|
6741
|
+
const { spawnSync } = await import("node:child_process");
|
|
6742
|
+
const tsc = spawnSync("npx", ["tsc", "--noEmit"], {
|
|
6743
|
+
cwd: projectDir,
|
|
6744
|
+
stdio: "inherit",
|
|
6745
|
+
shell: false,
|
|
6746
|
+
});
|
|
6747
|
+
if (tsc.status !== 0) {
|
|
6748
|
+
process.exit(tsc.status === null ? 1 : tsc.status);
|
|
6749
|
+
}
|
|
6750
|
+
if (noPractices)
|
|
6751
|
+
return;
|
|
6752
|
+
const practiceWarnings = collectPracticeWarnings(projectDir, walkDir);
|
|
6753
|
+
printPracticeReport(projectDir, practiceWarnings);
|
|
6721
6754
|
}
|
|
6722
6755
|
function takeFlagValue(argv, i, a, flag) {
|
|
6723
6756
|
if (a.startsWith(flag + "=")) {
|
|
@@ -6802,6 +6835,29 @@ function parseRunFlags(argv) {
|
|
|
6802
6835
|
else if (a === "--non-headless") {
|
|
6803
6836
|
out.nonHeadless = true;
|
|
6804
6837
|
}
|
|
6838
|
+
else if (a === "--headless") {
|
|
6839
|
+
out.headlessBrowser = true;
|
|
6840
|
+
}
|
|
6841
|
+
else if (a === "--json") {
|
|
6842
|
+
out.json = true;
|
|
6843
|
+
}
|
|
6844
|
+
else if (a === "--no-practices") {
|
|
6845
|
+
out.noPractices = true;
|
|
6846
|
+
}
|
|
6847
|
+
else if (a === "--blank") {
|
|
6848
|
+
out.blank = true;
|
|
6849
|
+
}
|
|
6850
|
+
else if (a === "--goto" || a.startsWith("--goto=")) {
|
|
6851
|
+
const t = takeFlagValue(argv, i, a, "--goto");
|
|
6852
|
+
out.goto = t.value;
|
|
6853
|
+
i = t.nextI;
|
|
6854
|
+
}
|
|
6855
|
+
else if (a === "--inject" || a.startsWith("--inject=")) {
|
|
6856
|
+
const t = takeFlagValue(argv, i, a, "--inject");
|
|
6857
|
+
out.inject ??= [];
|
|
6858
|
+
out.inject.push(t.value);
|
|
6859
|
+
i = t.nextI;
|
|
6860
|
+
}
|
|
6805
6861
|
else if (a === "--cli") {
|
|
6806
6862
|
out.cli = true;
|
|
6807
6863
|
}
|
|
@@ -7130,6 +7186,20 @@ async function expandSpecFlowFiles(projectDir, spec) {
|
|
|
7130
7186
|
}
|
|
7131
7187
|
const args = process.argv.slice(2);
|
|
7132
7188
|
const command = args[0];
|
|
7189
|
+
function printBrowserSessionsList(sessions, projectDir) {
|
|
7190
|
+
if (sessions.length === 0) {
|
|
7191
|
+
console.log(`No live browser sessions for ${projectDir}.`);
|
|
7192
|
+
console.log(" waygraph browser start");
|
|
7193
|
+
return;
|
|
7194
|
+
}
|
|
7195
|
+
console.log(`Live browser sessions (${projectDir}):`);
|
|
7196
|
+
for (const s of sessions) {
|
|
7197
|
+
const mode = s.headless ? "headless" : "headful";
|
|
7198
|
+
console.log(` ${s.sessionId} ${mode} pid ${s.pid}`);
|
|
7199
|
+
console.log(` waygraph browser attach ${s.sessionId}`);
|
|
7200
|
+
console.log(` waygraph browser stop ${s.sessionId}`);
|
|
7201
|
+
}
|
|
7202
|
+
}
|
|
7133
7203
|
function usage() {
|
|
7134
7204
|
console.log(`waygraph -- graph project tool + engine CLI
|
|
7135
7205
|
|
|
@@ -7208,16 +7278,23 @@ Primary (less is more):
|
|
|
7208
7278
|
label,tone?,size?,weight?}], todos[], todoIndex?,
|
|
7209
7279
|
todoTitle?, holdMs? (0=until next), clear:true.
|
|
7210
7280
|
Missing selectors listed in response, not fatal.
|
|
7211
|
-
waygraph
|
|
7212
|
-
|
|
7213
|
-
|
|
7214
|
-
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
|
|
7218
|
-
|
|
7219
|
-
|
|
7220
|
-
|
|
7281
|
+
waygraph browser List live browser sessions for cwd
|
|
7282
|
+
waygraph browser start Open a new session (headful by default, about:blank)
|
|
7283
|
+
--inject preset|path Merge an external Block library (e.g. saucedemo)
|
|
7284
|
+
--goto <url> Navigate on start (disables blank page)
|
|
7285
|
+
--blank Force about:blank on start (default)
|
|
7286
|
+
--headless Headless session (default is visible browser)
|
|
7287
|
+
--cli After start, open the terminal picker (auto attach)
|
|
7288
|
+
waygraph browser sessions [project] List live browser sessions (attach/status/stop by id)
|
|
7289
|
+
waygraph browser send|status|attach|… Same session control as auto (see auto send/status/…)
|
|
7290
|
+
waygraph pilot start Bootstrap: browser session + whole-project graph +
|
|
7291
|
+
starting snapshot in one call. Does not plan or act.
|
|
7292
|
+
--inject / --goto / --headless / --cli / --base-url Same flags as browser start
|
|
7293
|
+
waygraph pilot sessions [project] List live sessions (prefer attach over a new start)
|
|
7294
|
+
waygraph pilot attach <sessionId> Graph + snapshot for an existing session (no new browser)
|
|
7295
|
+
waygraph pilot send|status|highlight|… Same session control as browser/auto
|
|
7296
|
+
waygraph auto Interactive explore (picker) — NOT the persistent browser
|
|
7297
|
+
layer; use browser/pilot for agent-driven sessions.
|
|
7221
7298
|
waygraph demo [--blocks <flow|file|spec>] Watch with step overlay (QA path)
|
|
7222
7299
|
--data '{...}' Mem seed JSON (or inline flow({...}))
|
|
7223
7300
|
--auto-next Auto-advance steps (alias: --autoplay)
|
|
@@ -7243,7 +7320,8 @@ Primary (less is more):
|
|
|
7243
7320
|
--ff-disabled Same as demo (dispute: expand FF)
|
|
7244
7321
|
|
|
7245
7322
|
Also:
|
|
7246
|
-
waygraph list | nav | validate | check | graph | init <name>
|
|
7323
|
+
waygraph list | nav | validate | check | typecheck | graph | init <name>
|
|
7324
|
+
check/typecheck --no-practices Skip bad-practice warnings
|
|
7247
7325
|
waygraph map [project] Waygraph Map convention enforcement: every static
|
|
7248
7326
|
Nav/Page url must verbatim-match its src/map/ folder
|
|
7249
7327
|
path ((group) segments excluded) - exits 1 on a
|
|
@@ -7667,15 +7745,207 @@ Agents shipped: waygraph-planner, waygraph-author, waygraph-healer.
|
|
|
7667
7745
|
break;
|
|
7668
7746
|
}
|
|
7669
7747
|
case "graph":
|
|
7670
|
-
case "auto":
|
|
7748
|
+
case "auto":
|
|
7749
|
+
case "browser":
|
|
7750
|
+
case "pilot": {
|
|
7751
|
+
if (command === "browser") {
|
|
7752
|
+
const sub = args[1];
|
|
7753
|
+
if (sub === "sessions") {
|
|
7754
|
+
const rest = args.slice(2);
|
|
7755
|
+
const json = rest.includes("--json");
|
|
7756
|
+
const positional = rest.filter((a) => a !== "--json");
|
|
7757
|
+
const proj = resolve(positional[0] ?? process.cwd());
|
|
7758
|
+
const sessions = listBrowserSessions(proj);
|
|
7759
|
+
if (json) {
|
|
7760
|
+
console.log(JSON.stringify(sessions, null, 2));
|
|
7761
|
+
}
|
|
7762
|
+
else {
|
|
7763
|
+
printBrowserSessionsList(sessions, proj);
|
|
7764
|
+
}
|
|
7765
|
+
break;
|
|
7766
|
+
}
|
|
7767
|
+
if (sub === "stop") {
|
|
7768
|
+
const proj = resolve(args[3] ?? process.cwd());
|
|
7769
|
+
const target = args[2];
|
|
7770
|
+
if (!target) {
|
|
7771
|
+
console.error("waygraph browser stop: usage: waygraph browser stop <sessionId|--all> [project]");
|
|
7772
|
+
process.exit(1);
|
|
7773
|
+
}
|
|
7774
|
+
if (target === "--all") {
|
|
7775
|
+
const n = stopAllBrowserSessions(proj);
|
|
7776
|
+
console.log(JSON.stringify({ stopped: n, projectDir: proj }));
|
|
7777
|
+
break;
|
|
7778
|
+
}
|
|
7779
|
+
if (!stopBrowserSession(proj, target)) {
|
|
7780
|
+
console.error(`waygraph browser stop: no such session "${target}"`);
|
|
7781
|
+
process.exit(1);
|
|
7782
|
+
}
|
|
7783
|
+
console.log(JSON.stringify({ stopped: target }));
|
|
7784
|
+
break;
|
|
7785
|
+
}
|
|
7786
|
+
if (!sub) {
|
|
7787
|
+
const proj = resolve(process.cwd());
|
|
7788
|
+
printBrowserSessionsList(listBrowserSessions(proj), proj);
|
|
7789
|
+
break;
|
|
7790
|
+
}
|
|
7791
|
+
if (sub === "start" || sub.startsWith("-")) {
|
|
7792
|
+
const flagArgs = sub === "start" ? args.slice(2) : args.slice(1);
|
|
7793
|
+
const flags = parseRunFlags(flagArgs);
|
|
7794
|
+
applyRunFlags(flags);
|
|
7795
|
+
const proj = resolve(flags.positionals[0] ?? process.cwd());
|
|
7796
|
+
if (!existsSync(proj)) {
|
|
7797
|
+
console.error(`waygraph browser: no such directory: ${proj}`);
|
|
7798
|
+
process.exit(1);
|
|
7799
|
+
}
|
|
7800
|
+
const baseURL = flags.baseUrl ?? process.env.WAYGRAPH_BASE_URL ?? resolveBaseUrl(proj);
|
|
7801
|
+
try {
|
|
7802
|
+
const inject = flags.inject?.length ? resolveInjectRoots(flags.inject, proj) : undefined;
|
|
7803
|
+
const result = await browserStart({
|
|
7804
|
+
projectDir: proj,
|
|
7805
|
+
...(inject?.length ? { inject } : {}),
|
|
7806
|
+
...(baseURL ? { baseURL } : {}),
|
|
7807
|
+
...(flags.goto
|
|
7808
|
+
? { startUrl: flags.goto, skipInitialNavigation: false }
|
|
7809
|
+
: { skipInitialNavigation: flags.blank !== false }),
|
|
7810
|
+
headless: flags.headlessBrowser === true,
|
|
7811
|
+
});
|
|
7812
|
+
console.log(JSON.stringify(result));
|
|
7813
|
+
if (!result.headless) {
|
|
7814
|
+
console.error(`Session ${result.sessionId} started. List all: waygraph browser sessions`);
|
|
7815
|
+
}
|
|
7816
|
+
if (flags.cli) {
|
|
7817
|
+
await runAttachLoop(proj, result.sessionId);
|
|
7818
|
+
}
|
|
7819
|
+
}
|
|
7820
|
+
catch (err) {
|
|
7821
|
+
console.error(`waygraph browser start: ${err instanceof Error ? err.message : String(err)}`);
|
|
7822
|
+
process.exit(1);
|
|
7823
|
+
}
|
|
7824
|
+
break;
|
|
7825
|
+
}
|
|
7826
|
+
if (sub !== "send" &&
|
|
7827
|
+
sub !== "status" &&
|
|
7828
|
+
sub !== "attach" &&
|
|
7829
|
+
sub !== "dom" &&
|
|
7830
|
+
sub !== "trace" &&
|
|
7831
|
+
sub !== "console" &&
|
|
7832
|
+
sub !== "storage" &&
|
|
7833
|
+
sub !== "upload" &&
|
|
7834
|
+
sub !== "click" &&
|
|
7835
|
+
sub !== "type" &&
|
|
7836
|
+
sub !== "press" &&
|
|
7837
|
+
sub !== "goto" &&
|
|
7838
|
+
sub !== "reload" &&
|
|
7839
|
+
sub !== "reach" &&
|
|
7840
|
+
sub !== "resync" &&
|
|
7841
|
+
sub !== "highlight") {
|
|
7842
|
+
console.error("waygraph browser: usage:\n" +
|
|
7843
|
+
" waygraph browser List live sessions (cwd)\n" +
|
|
7844
|
+
" waygraph browser start [--inject preset|path] [--goto <url>] [--blank] [--headless] [--cli] [project]\n" +
|
|
7845
|
+
" waygraph browser sessions [--json] [project]\n" +
|
|
7846
|
+
" waygraph browser stop <sessionId|--all> [project]\n" +
|
|
7847
|
+
" waygraph browser send|status|attach|highlight|dom|… <sessionId> … (same as auto)");
|
|
7848
|
+
process.exit(1);
|
|
7849
|
+
}
|
|
7850
|
+
}
|
|
7851
|
+
if (command === "pilot") {
|
|
7852
|
+
if (args[1] === "sessions") {
|
|
7853
|
+
const rest = args.slice(2);
|
|
7854
|
+
const json = rest.includes("--json");
|
|
7855
|
+
const positional = rest.filter((a) => a !== "--json");
|
|
7856
|
+
const proj = resolve(positional[0] ?? process.cwd());
|
|
7857
|
+
const sessions = listBrowserSessions(proj);
|
|
7858
|
+
if (json) {
|
|
7859
|
+
console.log(JSON.stringify(sessions, null, 2));
|
|
7860
|
+
}
|
|
7861
|
+
else {
|
|
7862
|
+
printBrowserSessionsList(sessions, proj);
|
|
7863
|
+
}
|
|
7864
|
+
break;
|
|
7865
|
+
}
|
|
7866
|
+
if (args[1] === "attach") {
|
|
7867
|
+
const sessionId = args[2];
|
|
7868
|
+
if (!sessionId) {
|
|
7869
|
+
console.error("waygraph pilot attach: usage: waygraph pilot attach <sessionId> [--inject …] [project]");
|
|
7870
|
+
process.exit(1);
|
|
7871
|
+
}
|
|
7872
|
+
const flags = parseRunFlags(args.slice(3));
|
|
7873
|
+
const proj = resolve(flags.positionals[0] ?? process.cwd());
|
|
7874
|
+
try {
|
|
7875
|
+
const inject = flags.inject?.length ? resolveInjectRoots(flags.inject, proj) : undefined;
|
|
7876
|
+
const result = await pilotAttach(proj, sessionId, inject);
|
|
7877
|
+
console.log(JSON.stringify(result));
|
|
7878
|
+
}
|
|
7879
|
+
catch (err) {
|
|
7880
|
+
console.error(`waygraph pilot attach: ${err instanceof Error ? err.message : String(err)}`);
|
|
7881
|
+
process.exit(1);
|
|
7882
|
+
}
|
|
7883
|
+
break;
|
|
7884
|
+
}
|
|
7885
|
+
if (args[1] === "start" || !args[1] || args[1].startsWith("-")) {
|
|
7886
|
+
const flagArgs = args[1] === "start" ? args.slice(2) : args.slice(1);
|
|
7887
|
+
const flags = parseRunFlags(flagArgs);
|
|
7888
|
+
applyRunFlags(flags);
|
|
7889
|
+
const proj = resolve(flags.positionals[0] ?? process.cwd());
|
|
7890
|
+
const baseURL = flags.baseUrl ?? process.env.WAYGRAPH_BASE_URL ?? resolveBaseUrl(proj);
|
|
7891
|
+
try {
|
|
7892
|
+
const result = await pilotStart({
|
|
7893
|
+
projectDir: proj,
|
|
7894
|
+
...(flags.inject?.length ? { injectTokens: flags.inject } : {}),
|
|
7895
|
+
...(baseURL ? { baseURL } : {}),
|
|
7896
|
+
...(flags.goto
|
|
7897
|
+
? { startUrl: flags.goto, skipInitialNavigation: false }
|
|
7898
|
+
: flags.blank === true
|
|
7899
|
+
? { skipInitialNavigation: true }
|
|
7900
|
+
: {}),
|
|
7901
|
+
headless: flags.headlessBrowser === true,
|
|
7902
|
+
});
|
|
7903
|
+
console.log(JSON.stringify(result));
|
|
7904
|
+
if (!result.headless) {
|
|
7905
|
+
console.error(`Chromium window opened (session ${result.sessionId}). ` +
|
|
7906
|
+
`Terminal menu: waygraph pilot attach ${result.sessionId}`);
|
|
7907
|
+
}
|
|
7908
|
+
if (flags.cli) {
|
|
7909
|
+
await runAttachLoop(proj, result.sessionId);
|
|
7910
|
+
}
|
|
7911
|
+
}
|
|
7912
|
+
catch (err) {
|
|
7913
|
+
console.error(`waygraph pilot start: ${err instanceof Error ? err.message : String(err)}`);
|
|
7914
|
+
process.exit(1);
|
|
7915
|
+
}
|
|
7916
|
+
break;
|
|
7917
|
+
}
|
|
7918
|
+
if (args[1] !== "send" &&
|
|
7919
|
+
args[1] !== "status" &&
|
|
7920
|
+
args[1] !== "dom" &&
|
|
7921
|
+
args[1] !== "trace" &&
|
|
7922
|
+
args[1] !== "console" &&
|
|
7923
|
+
args[1] !== "storage" &&
|
|
7924
|
+
args[1] !== "upload" &&
|
|
7925
|
+
args[1] !== "click" &&
|
|
7926
|
+
args[1] !== "type" &&
|
|
7927
|
+
args[1] !== "press" &&
|
|
7928
|
+
args[1] !== "goto" &&
|
|
7929
|
+
args[1] !== "reload" &&
|
|
7930
|
+
args[1] !== "reach" &&
|
|
7931
|
+
args[1] !== "resync" &&
|
|
7932
|
+
args[1] !== "highlight") {
|
|
7933
|
+
console.error("waygraph pilot: usage:\n" +
|
|
7934
|
+
" waygraph pilot start [--inject …] [--goto <url>] [--headless] [--cli] [--base-url <url>] [project]\n" +
|
|
7935
|
+
" waygraph pilot sessions [project] # list live browser sessions\n" +
|
|
7936
|
+
" waygraph pilot attach <sessionId> # graph + snapshot for an existing session\n" +
|
|
7937
|
+
" waygraph pilot send|status|attach|highlight|… <sessionId> … (controls browser — same as browser/auto)");
|
|
7938
|
+
process.exit(1);
|
|
7939
|
+
}
|
|
7940
|
+
}
|
|
7671
7941
|
// auto send|status|attach <sessionId> - session control against a
|
|
7672
7942
|
// --detach'd background session. Intercepted before the normal
|
|
7673
7943
|
// project-directory resolution below, same pattern `try demo|auto|
|
|
7674
7944
|
// auto:cli` already uses for a sub-verb positional.
|
|
7675
|
-
if (command === "auto" &&
|
|
7945
|
+
if ((command === "auto" || command === "browser" || command === "pilot") &&
|
|
7676
7946
|
(args[1] === "send" ||
|
|
7677
7947
|
args[1] === "status" ||
|
|
7678
|
-
args[1] === "attach" ||
|
|
7948
|
+
(args[1] === "attach" && command !== "pilot") ||
|
|
7679
7949
|
args[1] === "dom" ||
|
|
7680
7950
|
args[1] === "trace" ||
|
|
7681
7951
|
args[1] === "console" ||
|
|
@@ -7692,10 +7962,17 @@ Agents shipped: waygraph-planner, waygraph-author, waygraph-healer.
|
|
|
7692
7962
|
const sub = args[1];
|
|
7693
7963
|
const sessionId = args[2];
|
|
7694
7964
|
if (!sessionId) {
|
|
7695
|
-
console.error(`waygraph
|
|
7965
|
+
console.error(`waygraph ${command} ${sub}: missing <sessionId>`);
|
|
7696
7966
|
process.exit(1);
|
|
7697
7967
|
}
|
|
7698
|
-
const
|
|
7968
|
+
const projHint = resolve(process.cwd());
|
|
7969
|
+
const meta = resolveSessionMeta(sessionId, projHint);
|
|
7970
|
+
if (!meta) {
|
|
7971
|
+
console.error(`waygraph ${command} ${sub}: no such session "${sessionId}" — ` +
|
|
7972
|
+
"wrong directory? run `waygraph pilot sessions` from the project that started it");
|
|
7973
|
+
process.exit(1);
|
|
7974
|
+
}
|
|
7975
|
+
const proj = meta.projectDir;
|
|
7699
7976
|
if (sub === "attach") {
|
|
7700
7977
|
await runAttachLoop(proj, sessionId);
|
|
7701
7978
|
break;
|
|
@@ -8025,29 +8302,6 @@ Agents shipped: waygraph-planner, waygraph-author, waygraph-healer.
|
|
|
8025
8302
|
await runAutoExplore(proj, baseURL ? { cli: cliPicker, baseURL } : { cli: cliPicker });
|
|
8026
8303
|
break;
|
|
8027
8304
|
}
|
|
8028
|
-
case "pilot": {
|
|
8029
|
-
if (args[1] !== "start") {
|
|
8030
|
-
console.error('waygraph pilot: usage: waygraph pilot start [--non-headless] [--base-url <url>] [--data <json>]');
|
|
8031
|
-
process.exit(1);
|
|
8032
|
-
}
|
|
8033
|
-
const flags = parseRunFlags(args.slice(2));
|
|
8034
|
-
applyRunFlags(flags);
|
|
8035
|
-
const proj = resolve(process.cwd());
|
|
8036
|
-
const baseURL = flags.baseUrl ?? process.env.WAYGRAPH_BASE_URL ?? resolveBaseUrl(proj);
|
|
8037
|
-
try {
|
|
8038
|
-
const result = await pilotStart({
|
|
8039
|
-
projectDir: proj,
|
|
8040
|
-
...(baseURL ? { baseURL } : {}),
|
|
8041
|
-
...(flags.nonHeadless ? { headless: false } : {}),
|
|
8042
|
-
});
|
|
8043
|
-
console.log(JSON.stringify(result));
|
|
8044
|
-
}
|
|
8045
|
-
catch (err) {
|
|
8046
|
-
console.error(`waygraph pilot start: ${err instanceof Error ? err.message : String(err)}`);
|
|
8047
|
-
process.exit(1);
|
|
8048
|
-
}
|
|
8049
|
-
break;
|
|
8050
|
-
}
|
|
8051
8305
|
// Hidden: the detached server's own entry point, spawned by
|
|
8052
8306
|
// `spawnDetachedSession` (auto --cli --detach). Not documented in
|
|
8053
8307
|
// usage() - not meant to be invoked directly by a person.
|
|
@@ -8061,10 +8315,22 @@ Agents shipped: waygraph-planner, waygraph-author, waygraph-healer.
|
|
|
8061
8315
|
process.exit(1);
|
|
8062
8316
|
}
|
|
8063
8317
|
const baseURL = flags.baseUrl ?? process.env.WAYGRAPH_BASE_URL ?? resolveBaseUrl(proj);
|
|
8318
|
+
const inject = flags.inject?.length ? resolveInjectRoots(flags.inject, proj) : undefined;
|
|
8319
|
+
let headless;
|
|
8320
|
+
if (flags.headlessBrowser)
|
|
8321
|
+
headless = true;
|
|
8322
|
+
else if (flags.nonHeadless)
|
|
8323
|
+
headless = false;
|
|
8064
8324
|
await runAutoServeCommand({
|
|
8065
8325
|
projectDir: proj,
|
|
8066
8326
|
...(baseURL ? { baseURL } : {}),
|
|
8067
|
-
...(flags.
|
|
8327
|
+
...(flags.goto
|
|
8328
|
+
? { startUrl: flags.goto, skipInitialNavigation: false }
|
|
8329
|
+
: flags.blank === true
|
|
8330
|
+
? { skipInitialNavigation: true }
|
|
8331
|
+
: {}),
|
|
8332
|
+
...(headless !== undefined ? { headless } : {}),
|
|
8333
|
+
...(inject?.length ? { inject } : {}),
|
|
8068
8334
|
}, sessionId);
|
|
8069
8335
|
break;
|
|
8070
8336
|
}
|
|
@@ -8095,9 +8361,16 @@ Agents shipped: waygraph-planner, waygraph-author, waygraph-healer.
|
|
|
8095
8361
|
}
|
|
8096
8362
|
break;
|
|
8097
8363
|
}
|
|
8364
|
+
case "typecheck": {
|
|
8365
|
+
const flags = parseRunFlags(args.slice(1));
|
|
8366
|
+
const proj = resolve(flags.positionals[0] ?? process.cwd());
|
|
8367
|
+
await runTypecheckCommand(proj, flags.noPractices === true);
|
|
8368
|
+
break;
|
|
8369
|
+
}
|
|
8098
8370
|
case "check": {
|
|
8099
|
-
const
|
|
8100
|
-
const
|
|
8371
|
+
const flags = parseRunFlags(args.slice(1));
|
|
8372
|
+
const proj = resolve(flags.positionals[0] ?? process.cwd());
|
|
8373
|
+
const { navWarnings, selWarnings, practiceWarnings } = await checkCommand(proj, flags.noPractices ? { noPractices: true } : undefined);
|
|
8101
8374
|
if (navWarnings.length === 0) {
|
|
8102
8375
|
console.log(`waygraph check: no navigation found outside NavBlocks under ${proj}`);
|
|
8103
8376
|
}
|
|
@@ -8118,6 +8391,9 @@ Agents shipped: waygraph-planner, waygraph-author, waygraph-healer.
|
|
|
8118
8391
|
}
|
|
8119
8392
|
console.log(`waygraph check: ${selWarnings.length} inline-selector warning${selWarnings.length === 1 ? "" : "s"}`);
|
|
8120
8393
|
}
|
|
8394
|
+
if (!flags.noPractices) {
|
|
8395
|
+
printPracticeReport(proj, practiceWarnings);
|
|
8396
|
+
}
|
|
8121
8397
|
const orphans = await findOrphanBlocks(proj);
|
|
8122
8398
|
printOrphanReport(proj, orphans);
|
|
8123
8399
|
// Warnings only - never fail exit code. Orphans are reported for
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { BlockEntry } from "./auto-explore.js";
|
|
2
|
+
/** Collect selector strings from NavClick blocks and Block/*Sel source files. */
|
|
3
|
+
export declare function collectKnownInteractionSelectors(projectDir: string, library: {
|
|
4
|
+
navBlocks: BlockEntry[];
|
|
5
|
+
}, inject?: string[]): string[];
|
|
6
|
+
export interface UnmappedInteractionPayload {
|
|
7
|
+
knownPathnames: string[];
|
|
8
|
+
knownSelectors: string[];
|
|
9
|
+
}
|
|
10
|
+
/** Page-side: warn once per unmapped same-origin href or visible button. */
|
|
11
|
+
export declare function unmappedInteractionsPageScript(payload: UnmappedInteractionPayload): void;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime coverage-gap helpers: detect live page links/buttons with no Block
|
|
3
|
+
* selector or NavBlock URL in the loaded library (surfaced via console.warn →
|
|
4
|
+
* `auto console`, same channel as unmapped href detection).
|
|
5
|
+
*/
|
|
6
|
+
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { exploreRoots } from "./block-inject.js";
|
|
9
|
+
const SELECTOR_LITERAL = /(?:locator|visible|click|fill|press|selector|getByRole)\(\s*['"`]([^'"`]+)['"`]/g;
|
|
10
|
+
const HASH_ID = /#([a-zA-Z][\w-]*)/g;
|
|
11
|
+
const DATA_TEST = /\[data-test(?:-id)?(?:=["'`][^"'`]+["'`])?\]/g;
|
|
12
|
+
function walkBlockAndSelFiles(dir, acc = []) {
|
|
13
|
+
for (const name of readdirSync(dir)) {
|
|
14
|
+
const p = join(dir, name);
|
|
15
|
+
try {
|
|
16
|
+
const st = statSync(p);
|
|
17
|
+
if (st.isDirectory()) {
|
|
18
|
+
if (name === "node_modules" || name === "dist" || name.startsWith("."))
|
|
19
|
+
continue;
|
|
20
|
+
walkBlockAndSelFiles(p, acc);
|
|
21
|
+
}
|
|
22
|
+
else if (/\.(block|sel)\.ts$/.test(name)) {
|
|
23
|
+
acc.push(p);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
/* ignore */
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return acc;
|
|
31
|
+
}
|
|
32
|
+
/** Collect selector strings from NavClick blocks and Block/*Sel source files. */
|
|
33
|
+
export function collectKnownInteractionSelectors(projectDir, library, inject) {
|
|
34
|
+
const out = new Set();
|
|
35
|
+
for (const entry of library.navBlocks) {
|
|
36
|
+
const click = entry.block
|
|
37
|
+
.__waygraphNavClick;
|
|
38
|
+
if (typeof click === "string")
|
|
39
|
+
out.add(click);
|
|
40
|
+
}
|
|
41
|
+
for (const root of exploreRoots(projectDir, inject)) {
|
|
42
|
+
for (const file of walkBlockAndSelFiles(root)) {
|
|
43
|
+
const src = readFileSync(file, "utf-8");
|
|
44
|
+
for (const re of [SELECTOR_LITERAL, HASH_ID, DATA_TEST]) {
|
|
45
|
+
re.lastIndex = 0;
|
|
46
|
+
let m;
|
|
47
|
+
while ((m = re.exec(src))) {
|
|
48
|
+
const sel = m[0].startsWith("#") || m[0].startsWith("[") ? m[0] : (m[1] ?? m[0]);
|
|
49
|
+
if (sel.startsWith("#") || sel.includes("data-test") || sel.includes("[")) {
|
|
50
|
+
out.add(sel.startsWith("#") || sel.startsWith("[") ? sel : `#${sel}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return Array.from(out);
|
|
57
|
+
}
|
|
58
|
+
/** Page-side: warn once per unmapped same-origin href or visible button. */
|
|
59
|
+
export function unmappedInteractionsPageScript(payload) {
|
|
60
|
+
const w = window;
|
|
61
|
+
w.__wgWarnedPaths ??= new Set();
|
|
62
|
+
w.__wgWarnedButtons ??= new Set();
|
|
63
|
+
const matchesKnown = (el, selectors) => {
|
|
64
|
+
for (const sel of selectors) {
|
|
65
|
+
try {
|
|
66
|
+
if (el.matches(sel))
|
|
67
|
+
return true;
|
|
68
|
+
if (document.querySelector(sel) === el)
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
/* invalid selector in library — skip */
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
};
|
|
77
|
+
const seenPaths = new Set();
|
|
78
|
+
for (const a of Array.from(document.querySelectorAll("a[href]"))) {
|
|
79
|
+
let url;
|
|
80
|
+
try {
|
|
81
|
+
url = new URL(a.getAttribute("href") || "", location.href);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (url.origin !== location.origin)
|
|
87
|
+
continue;
|
|
88
|
+
const path = url.pathname;
|
|
89
|
+
if (seenPaths.has(path) || payload.knownPathnames.includes(path) || w.__wgWarnedPaths.has(path)) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
seenPaths.add(path);
|
|
93
|
+
w.__wgWarnedPaths.add(path);
|
|
94
|
+
console.warn(`[waygraph] unmapped nav link on this page: ${path} - no NavBlock covers this URL; add defineNavBlock`);
|
|
95
|
+
}
|
|
96
|
+
const buttonLike = Array.from(document.querySelectorAll('button, [role="button"], input[type="submit"], input[type="button"]')).filter((el) => {
|
|
97
|
+
const style = window.getComputedStyle(el);
|
|
98
|
+
return style.visibility !== "hidden" && style.display !== "none";
|
|
99
|
+
});
|
|
100
|
+
for (const el of buttonLike) {
|
|
101
|
+
const id = el.id;
|
|
102
|
+
const dt = el.getAttribute("data-test") ?? el.getAttribute("data-testid");
|
|
103
|
+
const name = el.getAttribute("name");
|
|
104
|
+
const label = id
|
|
105
|
+
? `#${id}`
|
|
106
|
+
: dt
|
|
107
|
+
? `[data-test="${dt}"]`
|
|
108
|
+
: name
|
|
109
|
+
? `[name="${name}"]`
|
|
110
|
+
: el.textContent?.trim().slice(0, 40) || el.tagName.toLowerCase();
|
|
111
|
+
const key = `${el.tagName}:${label}`;
|
|
112
|
+
if (w.__wgWarnedButtons.has(key))
|
|
113
|
+
continue;
|
|
114
|
+
if (matchesKnown(el, payload.knownSelectors))
|
|
115
|
+
continue;
|
|
116
|
+
w.__wgWarnedButtons.add(key);
|
|
117
|
+
console.warn(`[waygraph] unmapped button on this page: ${label} - no Method/NavClick Block selector matches; consider defineMethodBlock or defineNavClickBlock`);
|
|
118
|
+
}
|
|
119
|
+
}
|
package/dist/graph.d.ts
CHANGED