waygraph 0.15.52 → 0.15.54

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 (156) hide show
  1. package/README.md +8 -1
  2. package/dist/analysis/graph.d.ts +3 -0
  3. package/dist/analysis/map-check.d.ts +3 -2
  4. package/dist/auto-session/helpers.d.ts +10 -0
  5. package/dist/blocks-select.d.ts +3 -0
  6. package/dist/blocks-select.js +2 -0
  7. package/dist/cli/flags.js +3 -1
  8. package/dist/cli/usage.js +4 -1
  9. package/dist/cli.js +4 -1
  10. package/dist/commands/agent-dive-scaffold.d.ts +0 -20
  11. package/dist/commands/agent-dive-scaffold.js +3 -3
  12. package/dist/commands/init.js +1 -1
  13. package/dist/commands/upgrade.d.ts +1 -1
  14. package/dist/commands/upgrade.js +7 -1
  15. package/dist/config.d.ts +55 -19
  16. package/dist/config.js +55 -13
  17. package/dist/core/block.d.ts +7 -2
  18. package/dist/core/block.js +7 -3
  19. package/dist/core/blocks/api.d.ts +1 -0
  20. package/dist/core/blocks/method.d.ts +2 -0
  21. package/dist/core/blocks/method.js +1 -0
  22. package/dist/core/blocks/nav.d.ts +1 -0
  23. package/dist/core/blocks/page.d.ts +1 -0
  24. package/dist/core/branch-regression.d.ts +1 -0
  25. package/dist/core/compose.d.ts +1 -0
  26. package/dist/core/engine.d.ts +2 -0
  27. package/dist/core/engine.js +3 -1
  28. package/dist/core/flow.d.ts +11 -0
  29. package/dist/core/flow.js +32 -8
  30. package/dist/core/map-builder.d.ts +34 -1
  31. package/dist/core/map-builder.js +42 -4
  32. package/dist/core/run-graph.d.ts +2 -0
  33. package/dist/core/run-graph.js +1 -0
  34. package/dist/errors.d.ts +1 -0
  35. package/dist/extensions.d.ts +1 -0
  36. package/dist/extensions.js +1 -0
  37. package/dist/highlights/demo-pace.d.ts +4 -1
  38. package/dist/highlights/demo-pace.js +28 -3
  39. package/dist/highlights/device.d.ts +2 -0
  40. package/dist/highlights/device.js +2 -0
  41. package/dist/highlights/types.d.ts +6 -0
  42. package/dist/index.d.ts +2 -2
  43. package/dist/pilot-overlay/action-ring.d.ts +0 -9
  44. package/dist/pilot-overlay/action-ring.js +1 -1
  45. package/dist/pilot-overlay/panel.d.ts +2 -1
  46. package/dist/runner/interaction-patch.js +32 -1
  47. package/dist/runner/main.js +5 -2
  48. package/dist/runner/step-mode.js +10 -4
  49. package/dist/session/auto-explore-run.js +6 -0
  50. package/dist/session/auto-explore.d.ts +2 -1
  51. package/dist/session/auto-session-ipc.d.ts +1 -2
  52. package/dist/session/auto-session-ipc.js +1 -1
  53. package/dist/session/block-inject.d.ts +0 -13
  54. package/dist/session/block-inject.js +3 -3
  55. package/dist/session/overlay-beacon.d.ts +7 -0
  56. package/dist/session/overlay-beacon.js +5 -0
  57. package/dist/session/pilot.d.ts +1 -2
  58. package/dist/session/pilot.js +1 -2
  59. package/dist/traverse/coverage.d.ts +4 -4
  60. package/dist/traverse/coverage.js +4 -1
  61. package/dist/traverse/lease.d.ts +1 -0
  62. package/dist/traverse/lease.js +1 -0
  63. package/dist/traverse/run.d.ts +3 -0
  64. package/dist/types.d.ts +1 -1
  65. package/examples/routed-demo/src/flows/routed-demo-map.flow.ts +1 -1
  66. package/examples/routed-demo/src/flows/routed-demo.flow.ts +2 -2
  67. package/examples/saucedemo/README.md +11 -12
  68. package/examples/saucedemo/config/waygraph.config.ts +17 -0
  69. package/examples/saucedemo/docs/CONVENTION.md +1 -2
  70. package/examples/saucedemo/docs/HELPERS.md +1 -1
  71. package/examples/saucedemo/package-lock.json +10 -3
  72. package/examples/saucedemo/package.json +12 -29
  73. package/examples/saucedemo/src/blocks/saucedemo-web/cart/nav-cart.block.ts +0 -49
  74. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts +22 -25
  75. package/examples/saucedemo/src/blocks/saucedemo-web/inventory/inventory.page.block.ts +0 -6
  76. package/examples/saucedemo/src/blocks/saucedemo-web/inventory/methods/add-to-cart.effect.block.ts +38 -96
  77. package/examples/saucedemo/src/blocks/saucedemo-web/methods/fill-password.method.block.ts +1 -26
  78. package/examples/saucedemo/src/blocks/saucedemo-web/methods/fill-username.method.block.ts +1 -45
  79. package/examples/saucedemo/src/blocks/saucedemo-web/methods/submit-login.method.block.ts +14 -77
  80. package/examples/saucedemo/src/flows/api-demo.flow.ts +17 -11
  81. package/examples/saucedemo/src/flows/cart-bulk.flow.ts +19 -14
  82. package/examples/saucedemo/src/flows/cart-continue.flow.ts +10 -8
  83. package/examples/saucedemo/src/flows/cart-mem.flow.ts +7 -7
  84. package/examples/saucedemo/src/flows/catalog-logout.flow.ts +13 -8
  85. package/examples/saucedemo/src/flows/checkout-then-browse-again.flow.ts +26 -48
  86. package/examples/saucedemo/src/flows/checkout.flow.ts +70 -86
  87. package/examples/saucedemo/src/flows/device-showcase.flow.ts +24 -25
  88. package/examples/saucedemo/src/flows/login.flow.ts +11 -18
  89. package/examples/saucedemo/src/flows/mem-stub-demo.flow.ts +9 -20
  90. package/examples/saucedemo/src/flows/shop.flow.ts +15 -20
  91. package/examples/saucedemo/src/flows/smoke.flow.ts +12 -27
  92. package/examples/saucedemo/src/flows/stub-on-error.flow.ts +24 -43
  93. package/examples/saucedemo/src/flows/viewer-blocked.flow.ts +26 -34
  94. package/examples/saucedemo/src/states/checkout.mem-stubs.ts +11 -0
  95. package/examples/saucedemo/src/stubs/device-showcase.ts +193 -0
  96. package/examples/saucedemo/src/stubs/login.ts +37 -0
  97. package/examples/saucedemo/tests/chain-flow-episodes.spec.ts +7 -9
  98. package/examples/saucedemo/tests/submit-login-branch.spec.ts +18 -5
  99. package/package.json +12 -5
  100. package/templates/quickstart/README.md +4 -7
  101. package/templates/quickstart/config/waygraph.config.ts +17 -0
  102. package/templates/quickstart/docs/CONVENTION.md +1 -2
  103. package/templates/quickstart/docs/HELPERS.md +1 -1
  104. package/templates/quickstart/package.json +14 -14
  105. package/templates/quickstart/src/blocks/SITE-MAP.md +14 -3
  106. package/templates/quickstart/src/blocks/saucedemo-web/cart/nav-cart.block.ts +0 -49
  107. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts +22 -25
  108. package/templates/quickstart/src/blocks/saucedemo-web/inventory/inventory.page.block.ts +0 -6
  109. package/templates/quickstart/src/blocks/saucedemo-web/inventory/methods/add-to-cart.effect.block.ts +38 -96
  110. package/templates/quickstart/src/blocks/saucedemo-web/methods/fill-password.method.block.ts +1 -26
  111. package/templates/quickstart/src/blocks/saucedemo-web/methods/fill-username.method.block.ts +1 -45
  112. package/templates/quickstart/src/blocks/saucedemo-web/methods/submit-login.method.block.ts +14 -77
  113. package/templates/quickstart/src/flows/api-demo.flow.ts +43 -0
  114. package/templates/quickstart/src/flows/cart-bulk.flow.ts +19 -14
  115. package/templates/quickstart/src/flows/cart-continue.flow.ts +10 -8
  116. package/templates/quickstart/src/flows/cart-mem.flow.ts +7 -7
  117. package/templates/quickstart/src/flows/catalog-logout.flow.ts +13 -8
  118. package/templates/quickstart/src/flows/checkout-then-browse-again.flow.ts +26 -48
  119. package/templates/quickstart/src/flows/checkout.flow.ts +70 -63
  120. package/templates/quickstart/src/flows/device-showcase.flow.ts +24 -25
  121. package/templates/quickstart/src/flows/login.flow.ts +11 -18
  122. package/templates/quickstart/src/flows/mem-stub-demo.flow.ts +12 -0
  123. package/templates/quickstart/src/flows/shop.flow.ts +15 -20
  124. package/templates/quickstart/src/flows/smoke.flow.ts +12 -27
  125. package/templates/quickstart/src/flows/stub-on-error.flow.ts +31 -0
  126. package/templates/quickstart/src/flows/viewer-blocked.flow.ts +26 -34
  127. package/templates/quickstart/src/states/checkout.mem-stubs.ts +11 -0
  128. package/templates/quickstart/src/stubs/device-showcase.ts +193 -0
  129. package/templates/quickstart/src/stubs/login.ts +37 -0
  130. package/templates/quickstart/tests/chain-flow.spec.ts +7 -10
  131. package/templates/scaffold/README.md +1 -1
  132. package/templates/scaffold/STRUCTURE.md +5 -2
  133. package/templates/scaffold/config/waygraph.config.ts +24 -5
  134. package/templates/scaffold/package.json +6 -6
  135. package/templates/scaffold/src/flows/(external)/hoppscotch/auth.flow.ts +36 -0
  136. package/templates/scaffold/src/flows/(external)/hoppscotch/get.flow.ts +24 -0
  137. package/templates/scaffold/src/flows/(external)/hoppscotch/post.flow.ts +34 -0
  138. package/templates/scaffold/src/flows/(user-stories)/email-verification.flow.ts +47 -0
  139. package/templates/scaffold/src/flows/README.md +35 -14
  140. package/templates/scaffold/src/flows/{shop.flow.ts → home/cart.flow.ts} +18 -17
  141. package/templates/scaffold/src/flows/{example.flow.ts → home/hello.flow.ts} +12 -13
  142. package/templates/scaffold/tests/example.spec.ts +5 -5
  143. package/templates/scaffold/tests/hoppscotch.spec.ts +3 -3
  144. package/templates/scaffold/tests/mail-verify.spec.ts +7 -7
  145. package/templates/skills/waygraph-convention.skill.md +14 -6
  146. package/examples/saucedemo/playwright.config.ts +0 -19
  147. package/examples/saucedemo/scripts/drive-auto-cli.mjs +0 -93
  148. package/examples/saucedemo/scripts/prove-add-to-cart-menu.mjs +0 -126
  149. package/examples/saucedemo/scripts/prove-auto-graph.mjs +0 -92
  150. package/examples/saucedemo/scripts/prove-wrong-login-runtime.mjs +0 -63
  151. package/templates/quickstart/playwright.config.ts +0 -14
  152. package/templates/quickstart/scripts/drive-auto-cli.mjs +0 -93
  153. package/templates/scaffold/src/flows/hoppscotch-auth.flow.ts +0 -35
  154. package/templates/scaffold/src/flows/hoppscotch-get.flow.ts +0 -23
  155. package/templates/scaffold/src/flows/hoppscotch-post.flow.ts +0 -33
  156. package/templates/scaffold/src/flows/mail-verify.flow.ts +0 -46
package/README.md CHANGED
@@ -16,7 +16,14 @@ export const SubmitLoginBlock = defineMethodBlock<LoginPage, LoggedIn>({
16
16
  });
17
17
 
18
18
  // A Flow = Blocks in order.
19
- export const loginFlow = engine.defineFlow([start, NavLoginBlock, FillUsernameBlock, FillPasswordBlock, SubmitLoginBlock, end]);
19
+ export const loginFlow = engine
20
+ .map()
21
+ .start()
22
+ .gotoPage(NavLoginBlock)
23
+ .method(FillUsernameBlock)
24
+ .method(FillPasswordBlock)
25
+ .method(SubmitLoginBlock)
26
+ .end();
20
27
  ```
21
28
 
22
29
  ## Try it in 60 seconds
@@ -8,6 +8,7 @@ import { type BlocksSelect } from "../blocks-select.js";
8
8
  export interface WaygraphNode {
9
9
  checkpoint: string;
10
10
  }
11
+ /** A move in the graph: a Block from one Checkpoint tag to another. */
11
12
  export interface WaygraphEdge {
12
13
  /** The Block's own runtime `.name`. */
13
14
  block: string;
@@ -18,11 +19,13 @@ export interface WaygraphEdge {
18
19
  to: string;
19
20
  kind: "nav" | "action";
20
21
  }
22
+ /** A Block `waygraph graph` found but could not place in the graph, with the reason. */
21
23
  export interface SkippedBlock {
22
24
  block: string;
23
25
  file: string;
24
26
  reason: string;
25
27
  }
28
+ /** The static graph of a project: Checkpoint nodes, Block edges, skipped and orphan Blocks. */
26
29
  export interface WaygraphGraph {
27
30
  nodes: WaygraphNode[];
28
31
  edges: WaygraphEdge[];
@@ -1,9 +1,9 @@
1
- export interface MapViolation {
1
+ interface MapViolation {
2
2
  file: string;
3
3
  block: string;
4
4
  reason: string;
5
5
  }
6
- export interface MapNode {
6
+ interface MapNode {
7
7
  block: string;
8
8
  checkpoint: string | undefined;
9
9
  file: string;
@@ -16,3 +16,4 @@ export interface MapCheckResult {
16
16
  violations: MapViolation[];
17
17
  }
18
18
  export declare function checkMap(projectDir: string): Promise<MapCheckResult>;
19
+ export {};
@@ -3,6 +3,7 @@ import type { BlockEntry, ExploreMenu } from "../session/auto-explore.js";
3
3
  import type { PickResult } from "../session/auto-explore-run.js";
4
4
  /** True when a stub-phase result actually carries authored content worth keeping. */
5
5
  export declare function stubPhaseHasContent(r: StubPhaseResult): boolean;
6
+ /** One available move in a session snapshot: a Block and the Checkpoint it leads to. */
6
7
  export interface SessionSnapshotEdge {
7
8
  /** 1-based, matching the numbers a human/agent picks by (same as printCliMenu). */
8
9
  index: number;
@@ -12,10 +13,12 @@ export interface SessionSnapshotEdge {
12
13
  label?: string;
13
14
  description?: string;
14
15
  }
16
+ /** A group of moves in a session snapshot, by page. */
15
17
  export interface SessionSnapshotSection {
16
18
  title: string;
17
19
  edges: SessionSnapshotEdge[];
18
20
  }
21
+ /** The state of a live session: where it is, the moves available from there, and the trace so far. */
19
22
  export interface SessionSnapshot {
20
23
  here: string | null;
21
24
  sections: SessionSnapshotSection[];
@@ -82,6 +85,7 @@ export declare function walkFullDom(root: Element, caps: FullDomCaps): {
82
85
  tree: FullDomNode;
83
86
  truncated: boolean;
84
87
  };
88
+ /** What `AutoSession.start` needs: project, selection, headless mode and the session id. */
85
89
  export interface AutoSessionInit {
86
90
  projectDir: string;
87
91
  baseURL?: string;
@@ -102,6 +106,7 @@ export interface AutoSessionInit {
102
106
  */
103
107
  sessionId?: string;
104
108
  }
109
+ /** The result of running one menu pick in a live session. */
105
110
  export type ApplyPickResult = {
106
111
  ok: true;
107
112
  snapshot: SessionSnapshot;
@@ -110,6 +115,7 @@ export type ApplyPickResult = {
110
115
  ok: false;
111
116
  error: string;
112
117
  };
118
+ /** The result of running a multi-step route in a live session. */
113
119
  export type ApplyPathResult = {
114
120
  ok: true;
115
121
  path: string[];
@@ -141,6 +147,7 @@ export interface TraceStep {
141
147
  stubOnError?: StubPhaseResult;
142
148
  }
143
149
  export declare const TRACE_MAX_STEPS = 500;
150
+ /** Which stub file a live session wrote: before, after or on-error. */
144
151
  export type StubFileKind = "image" | "pdf" | "video";
145
152
  /**
146
153
  * Real, minimal, valid fixture files bundled at `assets/stubs/` - not
@@ -180,11 +187,13 @@ export interface ConsoleLogEntry {
180
187
  timestamp: string;
181
188
  }
182
189
  export declare const CONSOLE_LOG_MAX_ENTRIES = 200;
190
+ /** One service worker registration as reported by a live session. */
183
191
  export interface ServiceWorkerRegistrationInfo {
184
192
  scope: string;
185
193
  activeUrl?: string;
186
194
  state?: string;
187
195
  }
196
+ /** One cookie as reported by a live session's `auto cookies`. */
188
197
  export interface CookieInfo {
189
198
  name: string;
190
199
  value: string;
@@ -193,6 +202,7 @@ export interface CookieInfo {
193
202
  httpOnly: boolean;
194
203
  secure: boolean;
195
204
  }
205
+ /** Local and session storage of a live session's page. */
196
206
  export interface StorageSnapshot {
197
207
  localStorage: Record<string, string>;
198
208
  sessionStorage: Record<string, string>;
@@ -6,6 +6,7 @@
6
6
  * - otherwise -> bare token (flow export / checkpoint / block name) — caller decides
7
7
  */
8
8
  export type BlocksSelectKind = "regex" | "glob" | "bare";
9
+ /** A parsed `--blocks` selector: which Blocks, by name, glob or file, a command should use. */
9
10
  export interface BlocksSelect {
10
11
  kind: BlocksSelectKind;
11
12
  /** Original token. */
@@ -16,9 +17,11 @@ export interface BlocksSelect {
16
17
  }
17
18
  /** True when the token is file-select (glob/regex), not a bare flow/checkpoint name. */
18
19
  export declare function isFileSelectToken(token: string): boolean;
20
+ /** Parses a `--blocks` argument into a selector. */
19
21
  export declare function parseBlocksSelect(token: string): BlocksSelect;
20
22
  /** Glob → RegExp (simple: ** / * / ? only; no brace expansion). */
21
23
  export declare function globToRegExp(glob: string): RegExp;
24
+ /** True when a Block name and file match a parsed `--blocks` selector. */
22
25
  export declare function matchBlocksSelect(select: BlocksSelect, opts: {
23
26
  relFile: string;
24
27
  blockName?: string;
@@ -14,6 +14,7 @@ export function isFileSelectToken(token) {
14
14
  }
15
15
  return token.includes("*");
16
16
  }
17
+ /** Parses a `--blocks` argument into a selector. */
17
18
  export function parseBlocksSelect(token) {
18
19
  const raw = token.trim();
19
20
  if (raw.length >= 2 && raw.startsWith("/") && raw.endsWith("/")) {
@@ -52,6 +53,7 @@ export function globToRegExp(glob) {
52
53
  }
53
54
  return new RegExp("^" + out + "$");
54
55
  }
56
+ /** True when a Block name and file match a parsed `--blocks` selector. */
55
57
  export function matchBlocksSelect(select, opts) {
56
58
  const file = opts.relFile.replace(/\\/g, "/");
57
59
  const name = opts.blockName ?? "";
package/dist/cli/flags.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { basename, relative, resolve } from "node:path";
3
3
  import { existsSync, readFileSync } from "node:fs";
4
4
  import { walkDir } from "./discover.js";
5
- import { resolveBaseUrl } from "../config.js";
5
+ import { loadWaygraphConfig, resolveBaseUrl } from "../config.js";
6
6
  function takeFlagValue(argv, i, a, flag) {
7
7
  if (a.startsWith(flag + "=")) {
8
8
  return { value: a.slice(flag.length + 1), nextI: i };
@@ -323,6 +323,8 @@ export { resolveBaseUrl };
323
323
  * Call after applyRunFlags so explicit flags already won.
324
324
  */
325
325
  export async function applyDemoDefaults(projectDir) {
326
+ // Config first: its defaults (headed, autoNext, pace...) must land before the demo defaults below fill the same variables.
327
+ await loadWaygraphConfig(projectDir);
326
328
  if (process.env.WAYGRAPH_STEP === undefined)
327
329
  process.env.WAYGRAPH_STEP = "1";
328
330
  if (process.env.WAYGRAPH_STEP === "1" && process.env.WAYGRAPH_HEADED === undefined) {
package/dist/cli/usage.js CHANGED
@@ -186,13 +186,16 @@ Primary (less is more):
186
186
  has a trace - test report opens it.
187
187
 
188
188
  Also:
189
+ --config <name|file> (any command) use another config instead of waygraph.config.ts, e.g.
190
+ waygraph test --config staging (config/waygraph.staging.config.ts)
189
191
  --env <mode> (any command) load .env.<mode> / .env.<mode>.local on top of .env, e.g.
190
192
  waygraph test --env staging (default: NODE_ENV, else development)
191
- waygraph upgrade [project] [paths...] [--apply] [--overwrite] [--diff]
193
+ waygraph upgrade [project] [paths...] [--apply] [--overwrite] [--diff] [--skills]
192
194
  Compare a project with the scaffold in this waygraph:
193
195
  lists new and differing files and a stale waygraph
194
196
  dependency. Dry run unless --apply; only the paths you
195
197
  name are copied, differing files only with --overwrite.
198
+ --skills refreshes .agents/skills to this waygraph's.
196
199
  waygraph extensions install [project] [--update]
197
200
  Download the browser extensions listed in
198
201
  waygraph.config.* (extensions: ["hoppscotch"]) into
package/dist/cli.js CHANGED
@@ -16,6 +16,7 @@
16
16
  */
17
17
  import { SKILL_FLAG_MAP, printSkillIndex, readSkillMarkdown } from "./commands/agent-dive-scaffold.js";
18
18
  import { takeEnvFlag } from "./env-file.js";
19
+ import { takeConfigFlag } from "./config.js";
19
20
  import { usage } from "./cli/usage.js";
20
21
  import { listCase, navCase } from "./commands/list.js";
21
22
  import { checkCase, typecheckCase, validateCase } from "./commands/check.js";
@@ -29,8 +30,10 @@ import { tryCase } from "./commands/try.js";
29
30
  import { upgradeCase } from "./commands/upgrade.js";
30
31
  import { extensionsCase } from "./commands/extensions.js";
31
32
  import { mapCase } from "./commands/map.js";
32
- const args = takeEnvFlag(process.argv.slice(2));
33
+ const args = takeConfigFlag(takeEnvFlag(process.argv.slice(2)));
33
34
  const command = args[0];
35
+ if (command)
36
+ process.env.WAYGRAPH_COMMAND = command;
34
37
  async function main() {
35
38
  if (!command || command === "--help" || command === "-h") {
36
39
  usage();
@@ -1,24 +1,4 @@
1
1
  export type AgentDiveLoop = "claude" | "opencode" | "cursor" | "vscode";
2
- export interface AgentSpec {
3
- name: string;
4
- description: string;
5
- model?: string;
6
- color?: string;
7
- tools: string[];
8
- instructions: string;
9
- }
10
- /** Minimal YAML-ish frontmatter parser for `*.agent.md` templates. */
11
- export declare function parseAgentSpec(filePath: string): AgentSpec;
12
- export declare function loadAgentSpecs(): AgentSpec[];
13
- /**
14
- * Claude Code Skills (`.claude/skills/<name>/SKILL.md`) - a different
15
- * mechanism than the agent persona files above: loadable instructions
16
- * triggered by task shape, not a persona. Claude-Code-specific (no
17
- * equivalent in the opencode/cursor/vscode loops), additive to the
18
- * `claude` loop only. Reuses the same `name: .../description: ...` +
19
- * body frontmatter shape `parseAgentSpec` already parses.
20
- */
21
- export declare function loadSkillSpecs(): AgentSpec[];
22
2
  /** CLI flag -> packaged skill file stem (under templates/skills/). */
23
3
  export declare const SKILL_FLAG_MAP: {
24
4
  readonly "--skill-pilot": "waygraph-pilot";
@@ -16,7 +16,7 @@ function skillsTemplateDir() {
16
16
  return join(packageRoot(), "templates", "skills");
17
17
  }
18
18
  /** Minimal YAML-ish frontmatter parser for `*.agent.md` templates. */
19
- export function parseAgentSpec(filePath) {
19
+ function parseAgentSpec(filePath) {
20
20
  const raw = readFileSync(filePath, "utf-8");
21
21
  if (!raw.startsWith("---\n")) {
22
22
  throw new Error(`agent-dive: missing frontmatter in ${filePath}`);
@@ -58,7 +58,7 @@ export function parseAgentSpec(filePath) {
58
58
  instructions: body,
59
59
  };
60
60
  }
61
- export function loadAgentSpecs() {
61
+ function loadAgentSpecs() {
62
62
  const dir = agentsTemplateDir();
63
63
  if (!existsSync(dir)) {
64
64
  throw new Error(`agent-dive: templates missing at ${dir}`);
@@ -76,7 +76,7 @@ export function loadAgentSpecs() {
76
76
  * `claude` loop only. Reuses the same `name: .../description: ...` +
77
77
  * body frontmatter shape `parseAgentSpec` already parses.
78
78
  */
79
- export function loadSkillSpecs() {
79
+ function loadSkillSpecs() {
80
80
  const dir = skillsTemplateDir();
81
81
  if (!existsSync(dir))
82
82
  return [];
@@ -85,7 +85,7 @@ function initCommand(projectName) {
85
85
  console.log(" waygraph typecheck # tsc --noEmit + bad-practice warnings (use --no-practices to skip)");
86
86
  console.log(" waygraph auto # interactive explore (headed panel)");
87
87
  console.log(" waygraph auto --cli # same menus in the terminal");
88
- console.log(" waygraph demo --blocks exampleFlow");
88
+ console.log(" waygraph demo --blocks homeHelloFlow");
89
89
  console.log(" waygraph graph # static state graph JSON");
90
90
  console.log(" .agents/skills/ # waygraph skills for coding agents (waygraph agent-dive adds agent personas)");
91
91
  console.log("");
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `waygraph upgrade [project] [paths...] [--apply] [--overwrite] [--diff]`
2
+ * `waygraph upgrade [project] [paths...] [--apply] [--overwrite] [--diff] [--skills]`
3
3
  * Compares the project with the scaffold shipped in this waygraph. Dry run by default.
4
4
  * Only the paths you name are copied, because a real project deliberately drops parts of the demo.
5
5
  */
@@ -21,13 +21,19 @@ const isOlder = (a, b) => {
21
21
  return false;
22
22
  };
23
23
  /**
24
- * `waygraph upgrade [project] [paths...] [--apply] [--overwrite] [--diff]`
24
+ * `waygraph upgrade [project] [paths...] [--apply] [--overwrite] [--diff] [--skills]`
25
25
  * Compares the project with the scaffold shipped in this waygraph. Dry run by default.
26
26
  * Only the paths you name are copied, because a real project deliberately drops parts of the demo.
27
27
  */
28
28
  export async function upgradeCase(args) {
29
29
  const flags = new Set(args.filter((a) => a.startsWith("--")));
30
30
  const positional = args.slice(1).filter((a) => !a.startsWith("--"));
31
+ // --skills: the agent skills are waygraph's own text, never edited by a project, so refresh them in place.
32
+ if (flags.has("--skills")) {
33
+ flags.add("--apply");
34
+ flags.add("--overwrite");
35
+ positional.push(".agents/skills");
36
+ }
31
37
  const first = positional[0];
32
38
  const hasProject = first !== undefined && existsSync(join(resolve(first), "package.json"));
33
39
  const proj = resolve(hasProject ? first : process.cwd());
package/dist/config.d.ts CHANGED
@@ -1,29 +1,62 @@
1
1
  import type { PlaywrightTestConfig } from "@playwright/test";
2
2
  import type { ExtensionSpec } from "./extensions.js";
3
3
  import type { WebServerConfig } from "./webserver.js";
4
+ import type { DemoPace } from "./highlights.js";
5
+ /** Where `--video` recordings are saved and at what size. */
6
+ export interface VideoSettings {
7
+ /** Where recordings are saved: absolute, `~/…`, or relative to the project. Default `~/Downloads/waygraph-videos/<project>` (the project's `.waygraph-videos/` where there is no Downloads folder, and on CI). */
8
+ dir?: string;
9
+ /** Recording size, e.g. `"1920x1080"` (demo default) or `"1280x720"` (run default). */
10
+ viewport?: string;
11
+ }
12
+ /** Settings `waygraph run` and `waygraph demo` share. */
13
+ export interface RunSettings {
14
+ /** Show the browser window. */
15
+ headed?: boolean;
16
+ /**
17
+ * `run`: Playwright's `slowMo`, ms before every operation. `demo`: waygraph's own pause after each action (default 350),
18
+ * applied once per action rather than per keystroke, and removed by a step's `pace: "blitz"`.
19
+ */
20
+ slowMo?: number;
21
+ video?: VideoSettings;
22
+ }
23
+ /** Settings for `waygraph demo`. */
24
+ export interface DemoSettings extends RunSettings {
25
+ /** Default pace for flows and steps that set none: `"fast"`, `"slow"`, `"blitz"`, or a multiplier like `0.5` / `2`. Narration gates and dwell scale with it; `"blitz"` also skips typing animation. */
26
+ pace?: DemoPace;
27
+ /**
28
+ * What the named paces mean, as multipliers of normal dwell and gate time (1 = normal): blitz 0.05, fast 0.33, slow 2 by
29
+ * default. Lower is faster. `blitz` also skips the cursor, ring pops and typing animation.
30
+ * @example speeds: { fast: 0.5, blitz: 0.1 }
31
+ */
32
+ speeds?: {
33
+ blitz?: number;
34
+ fast?: number;
35
+ slow?: number;
36
+ };
37
+ /** Typing animation: ms per character (default 30). `0` types instantly. */
38
+ typingMs?: number;
39
+ /** Advance steps automatically instead of waiting for Next. */
40
+ autoNext?: boolean;
41
+ /** Shorter Next / auto-next gates; keeps the cursor and typing animation. */
42
+ fast?: boolean;
43
+ }
44
+ /** The project config in `config/waygraph.config.ts`: base URL, extensions, run and demo settings, web server and the Playwright block. */
4
45
  export interface WaygraphConfig {
5
46
  /** Same value `resolveBaseUrl` would otherwise read from package.json's `waygraph.baseUrl`. */
6
47
  baseUrl?: string;
7
48
  /** Chromium extensions to load in every run: `"hoppscotch"`, a Chrome Web Store id, or `{ path }` to an unpacked one. */
8
49
  extensions?: ExtensionSpec[];
9
- /** Recording with `--video` (`waygraph run` / `demo`). */
10
- video?: {
11
- /** Where recordings are saved: absolute, `~/…`, or relative to the project. Default `~/Downloads/waygraph-videos/<project>` (the project's `.waygraph-videos/` where there is no Downloads folder, and on CI). */
12
- dir?: string;
13
- /** Recording size, e.g. `"1920x1080"` (demo default) or `"1280x720"` (run default). */
14
- viewport?: string;
15
- };
16
- /** Milliseconds to pause before each browser action in `run` / `demo`, to make it watchable. */
50
+ /** Recording with `--video` (`waygraph run` / `demo`); `run` and `demo` below can override it. */
51
+ video?: VideoSettings;
52
+ /** Default for `run` and `demo`: milliseconds to pause around each browser action, so a run is watchable. */
17
53
  slowMo?: number;
18
- /** Show the browser window. Default: `demo` and `auto` are headed, `run` is headless. */
54
+ /** Default for `run` and `demo`: show the browser window. Without it `demo` and `auto` are headed and `run` is headless. */
19
55
  headed?: boolean;
20
- /** `waygraph demo` defaults. */
21
- demo?: {
22
- /** Advance steps automatically instead of waiting for Next. */
23
- autoNext?: boolean;
24
- /** Blitz through fast-forward steps. */
25
- fast?: boolean;
26
- };
56
+ /** Settings for `waygraph run` only. They override the shared ones above. */
57
+ run?: RunSettings;
58
+ /** Settings for `waygraph demo` only. They override the shared ones above. */
59
+ demo?: DemoSettings;
27
60
  /** Folder of test files, relative to the project. Default `"tests"`. */
28
61
  testDir?: string;
29
62
  /** App to start around `waygraph test`, `run`, `demo` and `auto` (same fields as Playwright's `webServer`). */
@@ -42,6 +75,8 @@ export interface ConfigContext {
42
75
  * @example export default defineConfig(({ env }) => ({ baseUrl: env.BASE_URL }))
43
76
  */
44
77
  export declare function defineConfig(config: WaygraphConfig | ((context: ConfigContext) => WaygraphConfig)): WaygraphConfig;
78
+ /** Removes `--config <file>` / `--config=<file>` from argv and records it as `WAYGRAPH_CONFIG`, so every command reads that config. */
79
+ export declare function takeConfigFlag(argv: string[]): string[];
45
80
  /**
46
81
  * Looks for the first `waygraph.config.*` in `projectDir` and imports it - `.ts` works
47
82
  * unmodified because `bin/waygraph` already registers `tsx/esm` before `dist/cli.js` ever runs
@@ -72,7 +107,8 @@ export declare function resolveBaseUrl(projectDir: string): Promise<string | und
72
107
  /** Unpacked extension directories for a run: `WAYGRAPH_EXTENSIONS` if set, else the config file's `extensions` (fetched on first use). */
73
108
  export declare function configuredExtensions(projectDir: string): Promise<string[]>;
74
109
  /**
75
- * Applies the config's run settings as the `WAYGRAPH_*` variables the runner reads. A flag, a shell variable or a `.env`
76
- * value that is already set always wins, so the config only supplies defaults.
110
+ * Applies the config's run settings as the `WAYGRAPH_*` variables the runner reads, for the command being run
111
+ * (`WAYGRAPH_COMMAND`, set by the CLI). The `run` / `demo` section overrides the shared top-level values. A flag, a shell
112
+ * variable or a `.env` value that is already set always wins, so the config only supplies defaults.
77
113
  */
78
- export declare function applyConfigEnv(config: WaygraphConfig | undefined): void;
114
+ export declare function applyConfigEnv(config: WaygraphConfig | undefined, command?: string | undefined): void;
package/dist/config.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // setting, for consumers who'd rather have a real typed file than a JSON field. Real ask: "I
4
4
  // think its time to add waygraph config now."
5
5
  import { existsSync, readFileSync } from "node:fs";
6
- import { join } from "node:path";
6
+ import { isAbsolute, join } from "node:path";
7
7
  import { pathToFileURL } from "node:url";
8
8
  import { extensionPathsFromEnv } from "./browser.js";
9
9
  import { envMode, loadEnvFiles } from "./env-file.js";
@@ -22,6 +22,33 @@ export function defineConfig(config) {
22
22
  const CONFIG_NAMES = ["waygraph.config.ts", "waygraph.config.mts", "waygraph.config.mjs", "waygraph.config.js", "waygraph.config.cjs"];
23
23
  /** `config/` first, then the project root. */
24
24
  const CONFIG_FILENAMES = [...CONFIG_NAMES.map((n) => join("config", n)), ...CONFIG_NAMES];
25
+ /**
26
+ * Which config file(s) to try. `--config <file>` / `WAYGRAPH_CONFIG` picks one: a path (`config/staging.ts`), or a short name
27
+ * (`staging` -> `config/waygraph.staging.config.ts`, also tried in the project root). Without it, `waygraph.config.*`.
28
+ */
29
+ function configCandidates() {
30
+ const chosen = process.env.WAYGRAPH_CONFIG;
31
+ if (!chosen)
32
+ return CONFIG_FILENAMES;
33
+ if (/[\\/]/.test(chosen) || /\.[cm]?[jt]s$/.test(chosen))
34
+ return [chosen];
35
+ const names = ["ts", "mts", "mjs", "js", "cjs"].map((e) => `waygraph.${chosen}.config.${e}`);
36
+ return [...names.map((n) => join("config", n)), ...names, chosen];
37
+ }
38
+ /** Removes `--config <file>` / `--config=<file>` from argv and records it as `WAYGRAPH_CONFIG`, so every command reads that config. */
39
+ export function takeConfigFlag(argv) {
40
+ const out = [];
41
+ for (let i = 0; i < argv.length; i++) {
42
+ const a = argv[i];
43
+ if (a === "--config" && argv[i + 1] !== undefined)
44
+ process.env.WAYGRAPH_CONFIG = argv[++i];
45
+ else if (a.startsWith("--config="))
46
+ process.env.WAYGRAPH_CONFIG = a.slice("--config=".length);
47
+ else
48
+ out.push(a);
49
+ }
50
+ return out;
51
+ }
25
52
  /**
26
53
  * Looks for the first `waygraph.config.*` in `projectDir` and imports it - `.ts` works
27
54
  * unmodified because `bin/waygraph` already registers `tsx/esm` before `dist/cli.js` ever runs
@@ -32,10 +59,14 @@ const CONFIG_FILENAMES = [...CONFIG_NAMES.map((n) => join("config", n)), ...CONF
32
59
  */
33
60
  export async function loadWaygraphConfig(projectDir) {
34
61
  loadEnvFiles(projectDir);
35
- for (const name of CONFIG_FILENAMES) {
36
- const p = join(projectDir, name);
37
- if (!existsSync(p))
62
+ for (const name of configCandidates()) {
63
+ const p = isAbsolute(name) ? name : join(projectDir, name);
64
+ if (!existsSync(p)) {
65
+ if (process.env.WAYGRAPH_CONFIG && name === process.env.WAYGRAPH_CONFIG) {
66
+ throw new Error(`waygraph: config "${process.env.WAYGRAPH_CONFIG}" not found (looked for ${p})`);
67
+ }
38
68
  continue;
69
+ }
39
70
  let mod;
40
71
  try {
41
72
  mod = (await import(pathToFileURL(p).href));
@@ -108,20 +139,31 @@ export async function configuredExtensions(projectDir) {
108
139
  return resolveExtensions((await loadWaygraphConfig(projectDir))?.extensions, projectDir);
109
140
  }
110
141
  /**
111
- * Applies the config's run settings as the `WAYGRAPH_*` variables the runner reads. A flag, a shell variable or a `.env`
112
- * value that is already set always wins, so the config only supplies defaults.
142
+ * Applies the config's run settings as the `WAYGRAPH_*` variables the runner reads, for the command being run
143
+ * (`WAYGRAPH_COMMAND`, set by the CLI). The `run` / `demo` section overrides the shared top-level values. A flag, a shell
144
+ * variable or a `.env` value that is already set always wins, so the config only supplies defaults.
113
145
  */
114
- export function applyConfigEnv(config) {
146
+ export function applyConfigEnv(config, command = process.env.WAYGRAPH_COMMAND) {
115
147
  if (!config)
116
148
  return;
149
+ const isDemo = command === "demo" || command === "try";
150
+ const section = isDemo ? config.demo : command === "run" ? config.run : undefined;
117
151
  const set = (name, value) => {
118
152
  if (value !== undefined && process.env[name] === undefined)
119
153
  process.env[name] = value;
120
154
  };
121
- set("WAYGRAPH_VIDEO_DIR", config.video?.dir);
122
- set("WAYGRAPH_VIDEO_VIEWPORT", config.video?.viewport);
123
- set("WAYGRAPH_SLOWMO", config.slowMo === undefined ? undefined : String(config.slowMo));
124
- set("WAYGRAPH_HEADED", config.headed === undefined ? undefined : config.headed ? "1" : "0");
125
- set("WAYGRAPH_AUTOPLAY", config.demo?.autoNext === undefined ? undefined : config.demo.autoNext ? "1" : "0");
126
- set("WAYGRAPH_DEMO_FAST", config.demo?.fast ? "1" : undefined);
155
+ const flag = (b) => (b === undefined ? undefined : b ? "1" : "0");
156
+ const video = { ...config.video, ...section?.video };
157
+ const slowMo = section?.slowMo ?? config.slowMo;
158
+ set("WAYGRAPH_VIDEO_DIR", video.dir);
159
+ set("WAYGRAPH_VIDEO_VIEWPORT", video.viewport);
160
+ set("WAYGRAPH_SLOWMO", slowMo === undefined ? undefined : String(slowMo));
161
+ set("WAYGRAPH_HEADED", flag(section?.headed ?? config.headed));
162
+ if (isDemo) {
163
+ set("WAYGRAPH_AUTOPLAY", flag(config.demo?.autoNext));
164
+ set("WAYGRAPH_DEMO_FAST", config.demo?.fast ? "1" : undefined);
165
+ set("WAYGRAPH_DEMO_PACE", config.demo?.pace === undefined ? undefined : String(config.demo.pace));
166
+ set("WAYGRAPH_PACE_SPEEDS", config.demo?.speeds ? JSON.stringify(config.demo.speeds) : undefined);
167
+ set("WAYGRAPH_TYPING_MS", config.demo?.typingMs === undefined ? undefined : String(config.demo.typingMs));
168
+ }
127
169
  }
@@ -18,6 +18,8 @@ export declare function findBlockIndex(middle: readonly Block<any, any>[], block
18
18
  export declare function branch<In extends Checkpoint<string>, Out extends Checkpoint<string>>(block: Block<In, Out>, routes: {
19
19
  [K in Out["__state"]]: Block<Extract<Out, Checkpoint<K>>, any> | null;
20
20
  }): DefinedBlock<In, any>;
21
+ /** Copy non-enumerable waygraph markers so decorate/withVerify keep map() kind checks. */
22
+ export declare function copyWaygraphRuntime(from: object, to: object): void;
21
23
  /**
22
24
  * Builds a Block with `withVerify`/`modVerify`/`modVerifyAll` attached as
23
25
  * methods - `LoginBlock.withVerify([...])`, discoverable by typing
@@ -60,11 +62,14 @@ type StubPhaseFn<Out extends Checkpoint<string>> = import("../highlights.js").Hi
60
62
  * Attach / chain a stubBefore lifecycle on a Block. Prefer
61
63
  * `(ctx) => { ctx.ring(...); const x = ctx.mem?.get(Key); }`.
62
64
  * @example FillUsernameBlock.stubBefore((ctx) => { ctx.ring("username", { ... }) })
65
+ * @deprecated Use the chained method: `Block.stubBefore((ctx) => …)`.
63
66
  */
64
67
  export declare function withStubBefore<B extends Block<any, any>>(block: B, stub: StubPhaseFn<any>): B;
65
- /** @see {@link withStubBefore} */
68
+ /** @see {@link withStubBefore} * @deprecated Use the chained method: `Block.stubAfter((ctx) => …)`.
69
+ */
66
70
  export declare function withStubAfter<B extends Block<any, any>>(block: B, stub: StubPhaseFn<any>): B;
67
- /** @see {@link withStubBefore} */
71
+ /** @see {@link withStubBefore} * @deprecated Use the chained method: `Block.stubOnError((ctx) => …)`.
72
+ */
68
73
  export declare function withStubOnError<B extends Block<any, any>>(block: B, stub: StubPhaseFn<any>): B;
69
74
  /**
70
75
  * Answers the browser's own dialog (`confirm`, `alert`, `prompt`, `beforeunload`) that this Block's action
@@ -75,12 +75,13 @@ function stripMethods(block) {
75
75
  };
76
76
  }
77
77
  /** Copy non-enumerable waygraph markers so decorate/withVerify keep map() kind checks. */
78
- function copyWaygraphRuntime(from, to) {
78
+ export function copyWaygraphRuntime(from, to) {
79
79
  for (const key of [
80
80
  "__waygraphKind",
81
81
  "__waygraphSalt",
82
82
  "__waygraphNavUrl",
83
83
  "__waygraphNavClick",
84
+ "__waygraphNavFrom",
84
85
  ]) {
85
86
  const desc = Object.getOwnPropertyDescriptor(from, key);
86
87
  if (!desc)
@@ -201,15 +202,18 @@ function withStubPhase(block, phase, stub) {
201
202
  * Attach / chain a stubBefore lifecycle on a Block. Prefer
202
203
  * `(ctx) => { ctx.ring(...); const x = ctx.mem?.get(Key); }`.
203
204
  * @example FillUsernameBlock.stubBefore((ctx) => { ctx.ring("username", { ... }) })
205
+ * @deprecated Use the chained method: `Block.stubBefore((ctx) => …)`.
204
206
  */
205
207
  export function withStubBefore(block, stub) {
206
208
  return withStubPhase(block, "stubBefore", stub);
207
209
  }
208
- /** @see {@link withStubBefore} */
210
+ /** @see {@link withStubBefore} * @deprecated Use the chained method: `Block.stubAfter((ctx) => …)`.
211
+ */
209
212
  export function withStubAfter(block, stub) {
210
213
  return withStubPhase(block, "stubAfter", stub);
211
214
  }
212
- /** @see {@link withStubBefore} */
215
+ /** @see {@link withStubBefore} * @deprecated Use the chained method: `Block.stubOnError((ctx) => …)`.
216
+ */
213
217
  export function withStubOnError(block, stub) {
214
218
  return withStubPhase(block, "stubOnError", stub);
215
219
  }
@@ -10,6 +10,7 @@ export interface ApiCallResult {
10
10
  /** `response.json()` if the body parses as JSON, else the raw text (or `undefined` if neither). */
11
11
  body: unknown;
12
12
  }
13
+ /** Options for `defineApiBlock`: the HTTP call, how its result maps to a Checkpoint, and the demo network panel. */
13
14
  export interface ApiBlockOptions<In extends Checkpoint<string>, Out extends Checkpoint<string>> {
14
15
  name: string;
15
16
  description?: string;
@@ -24,9 +24,11 @@ export type MethodBlock<In extends Checkpoint<string>, Out extends Checkpoint<st
24
24
  };
25
25
  /** @deprecated Prefer {@link MethodBlock} - same type; Action was the 0.7 name. */
26
26
  export type ActionBlock<In extends Checkpoint<string>, Out extends Checkpoint<string>> = MethodBlock<In, Out>;
27
+ /** Builds a Block for one page action (fill, submit, click) that does not navigate. */
27
28
  export declare function defineMethodBlock<In extends Checkpoint<string>, Out extends Checkpoint<string>>(base: Parameters<typeof defineBlock<In, Out>>[0]): MethodBlock<In, Out>;
28
29
  /** @deprecated Prefer {@link defineMethodBlock} - identical helper (0.7 name). */
29
30
  export declare const defineActionBlock: typeof defineMethodBlock;
31
+ /** Options for `defineAssertBlock`: a Checkpoint to stay on and the Traits that must hold. */
30
32
  export interface AssertBlockOptions<Out extends Checkpoint<string> = Checkpoint<string>> {
31
33
  name: string;
32
34
  description?: string;
@@ -1,6 +1,7 @@
1
1
  // Split out of the former 2,900-line engine.ts (see src/ARCHITECTURE.md). Behavior unchanged.
2
2
  import { checkpoint } from "../../types.js";
3
3
  import { defineBlock } from "../block.js";
4
+ /** Builds a Block for one page action (fill, submit, click) that does not navigate. */
4
5
  export function defineMethodBlock(base) {
5
6
  const built = defineBlock(base);
6
7
  Object.defineProperty(built, "__waygraphSalt", {
@@ -123,6 +123,7 @@ export type NavClickBlockOptions<Out extends Checkpoint<string>> = {
123
123
  highlights?: readonly WaygraphHighlight[] | ((out: Out) => readonly WaygraphHighlight[]);
124
124
  instanceOptions?: Block<Checkpoint<string>, Out>["instanceOptions"];
125
125
  };
126
+ /** A Block whose only action is clicking a link; built with `defineNavClickBlock`. */
126
127
  export type NavClickBlock<Out extends Checkpoint<string>> = NavBlock<Out>;
127
128
  /** @see {@link NavClickBlockOptions} */
128
129
  export declare function defineNavClickBlock<Out extends Checkpoint<string>>(options: NavClickBlockOptions<Out>): NavClickBlock<Out>;
@@ -4,6 +4,7 @@ import { MemPage } from "../../mem-page.js";
4
4
  import type { MemKey } from "../../mem-page.js";
5
5
  /** One Block (or lazy factory) registered on a {@link PageBlock}. */
6
6
  export type PageMethodEntry = DefinedBlock<any, any> | (() => DefinedBlock<any, any>);
7
+ /** Options for `definePageBlock`: the Checkpoint a page hub confirms and the Methods that hang off it. */
7
8
  export type PageBlockOptions<Out extends Checkpoint<string>> = {
8
9
  name: string;
9
10
  description?: string;