libretto 0.5.0 → 0.5.1

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 (116) hide show
  1. package/README.md +106 -36
  2. package/dist/cli/cli.js +22 -97
  3. package/dist/cli/commands/browser.js +86 -59
  4. package/dist/cli/commands/execution.js +199 -86
  5. package/dist/cli/commands/init.js +30 -8
  6. package/dist/cli/commands/logs.js +4 -5
  7. package/dist/cli/commands/shared.js +30 -29
  8. package/dist/cli/commands/snapshot.js +26 -39
  9. package/dist/cli/core/ai-config.js +9 -2
  10. package/dist/cli/core/api-snapshot-analyzer.js +15 -5
  11. package/dist/cli/core/browser.js +132 -29
  12. package/dist/cli/core/context.js +4 -1
  13. package/dist/cli/core/session-telemetry.js +5 -2
  14. package/dist/cli/core/session.js +21 -8
  15. package/dist/cli/core/snapshot-analyzer.js +14 -31
  16. package/dist/cli/core/snapshot-api-config.js +2 -6
  17. package/dist/cli/core/telemetry.js +10 -2
  18. package/dist/cli/framework/simple-cli.js +45 -25
  19. package/dist/cli/router.js +14 -21
  20. package/dist/cli/workers/run-integration-runtime.js +24 -5
  21. package/dist/cli/workers/run-integration-worker-protocol.js +3 -1
  22. package/dist/cli/workers/run-integration-worker.js +1 -4
  23. package/dist/index.d.ts +1 -2
  24. package/dist/index.js +7 -10
  25. package/dist/runtime/download/download.js +5 -1
  26. package/dist/runtime/extract/extract.js +11 -2
  27. package/dist/runtime/network/network.js +8 -1
  28. package/dist/runtime/recovery/agent.js +6 -2
  29. package/dist/runtime/recovery/errors.js +3 -1
  30. package/dist/runtime/recovery/recovery.js +3 -1
  31. package/dist/shared/condense-dom/condense-dom.js +6 -13
  32. package/dist/shared/config/config.d.ts +1 -9
  33. package/dist/shared/config/config.js +0 -18
  34. package/dist/shared/config/index.d.ts +2 -1
  35. package/dist/shared/config/index.js +0 -10
  36. package/dist/shared/debug/pause.js +9 -3
  37. package/dist/shared/instrumentation/instrument.js +101 -5
  38. package/dist/shared/llm/ai-sdk-adapter.js +3 -1
  39. package/dist/shared/llm/client.js +3 -1
  40. package/dist/shared/logger/index.js +4 -1
  41. package/dist/shared/run/api.js +3 -1
  42. package/dist/shared/run/browser.js +7 -2
  43. package/dist/shared/state/session-state.d.ts +2 -1
  44. package/dist/shared/state/session-state.js +5 -2
  45. package/dist/shared/visualization/ghost-cursor.js +19 -10
  46. package/dist/shared/visualization/highlight.js +9 -6
  47. package/dist/shared/workflow/workflow.d.ts +4 -5
  48. package/dist/shared/workflow/workflow.js +3 -5
  49. package/package.json +6 -2
  50. package/scripts/check-skills-sync.mjs +25 -0
  51. package/scripts/compare-eval-summary.mjs +47 -0
  52. package/scripts/postinstall.mjs +15 -15
  53. package/scripts/prepare-release.sh +97 -0
  54. package/scripts/skills-libretto.mjs +103 -0
  55. package/scripts/summarize-evals.mjs +135 -0
  56. package/scripts/sync-skills.mjs +12 -0
  57. package/skills/libretto/SKILL.md +113 -49
  58. package/skills/libretto/references/code-generation-rules.md +208 -0
  59. package/skills/libretto/references/configuration-file-reference.md +53 -0
  60. package/skills/libretto/references/site-security-review.md +143 -0
  61. package/src/cli/cli.ts +23 -110
  62. package/src/cli/commands/browser.ts +94 -70
  63. package/src/cli/commands/execution.ts +233 -102
  64. package/src/cli/commands/init.ts +32 -9
  65. package/src/cli/commands/logs.ts +7 -7
  66. package/src/cli/commands/shared.ts +36 -37
  67. package/src/cli/commands/snapshot.ts +44 -59
  68. package/src/cli/core/ai-config.ts +12 -3
  69. package/src/cli/core/api-snapshot-analyzer.ts +17 -6
  70. package/src/cli/core/browser.ts +178 -41
  71. package/src/cli/core/context.ts +7 -2
  72. package/src/cli/core/session-telemetry.ts +19 -8
  73. package/src/cli/core/session.ts +21 -7
  74. package/src/cli/core/snapshot-analyzer.ts +26 -46
  75. package/src/cli/core/snapshot-api-config.ts +170 -175
  76. package/src/cli/core/telemetry.ts +16 -3
  77. package/src/cli/framework/simple-cli.ts +144 -77
  78. package/src/cli/router.ts +13 -21
  79. package/src/cli/workers/run-integration-runtime.ts +36 -9
  80. package/src/cli/workers/run-integration-worker-protocol.ts +2 -0
  81. package/src/cli/workers/run-integration-worker.ts +1 -4
  82. package/src/index.ts +73 -66
  83. package/src/runtime/download/download.ts +62 -58
  84. package/src/runtime/download/index.ts +5 -5
  85. package/src/runtime/extract/extract.ts +71 -61
  86. package/src/runtime/network/index.ts +3 -3
  87. package/src/runtime/network/network.ts +99 -93
  88. package/src/runtime/recovery/agent.ts +217 -212
  89. package/src/runtime/recovery/errors.ts +107 -104
  90. package/src/runtime/recovery/index.ts +3 -3
  91. package/src/runtime/recovery/recovery.ts +38 -35
  92. package/src/shared/condense-dom/condense-dom.ts +15 -18
  93. package/src/shared/config/config.ts +0 -19
  94. package/src/shared/config/index.ts +0 -5
  95. package/src/shared/debug/pause.ts +57 -51
  96. package/src/shared/instrumentation/errors.ts +64 -62
  97. package/src/shared/instrumentation/index.ts +5 -5
  98. package/src/shared/instrumentation/instrument.ts +339 -209
  99. package/src/shared/llm/ai-sdk-adapter.ts +58 -55
  100. package/src/shared/llm/client.ts +181 -174
  101. package/src/shared/llm/types.ts +39 -39
  102. package/src/shared/logger/index.ts +11 -4
  103. package/src/shared/logger/logger.ts +312 -306
  104. package/src/shared/logger/sinks.ts +118 -114
  105. package/src/shared/paths/paths.ts +50 -49
  106. package/src/shared/paths/repo-root.ts +17 -17
  107. package/src/shared/run/api.ts +5 -1
  108. package/src/shared/run/browser.ts +12 -3
  109. package/src/shared/state/index.ts +9 -9
  110. package/src/shared/state/session-state.ts +46 -43
  111. package/src/shared/visualization/ghost-cursor.ts +161 -148
  112. package/src/shared/visualization/highlight.ts +89 -86
  113. package/src/shared/visualization/index.ts +13 -13
  114. package/src/shared/workflow/workflow.ts +19 -25
  115. package/skills/libretto/references/reverse-engineering-network-requests.md +0 -39
  116. package/skills/libretto/references/user-action-log.md +0 -31
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {
4
+ cpSync,
5
+ existsSync,
6
+ mkdirSync,
7
+ readFileSync,
8
+ readdirSync,
9
+ rmSync,
10
+ } from "node:fs";
11
+ import { relative, resolve, join } from "node:path";
12
+
13
+ export const SKILL_DIRS = [
14
+ "skills/libretto",
15
+ ".agents/skills/libretto",
16
+ ".claude/skills/libretto",
17
+ ];
18
+
19
+ function walkFiles(dir, baseDir = dir) {
20
+ const entries = readdirSync(dir, { withFileTypes: true }).sort((a, b) =>
21
+ a.name.localeCompare(b.name),
22
+ );
23
+ const files = [];
24
+
25
+ for (const entry of entries) {
26
+ const fullPath = join(dir, entry.name);
27
+ if (entry.isDirectory()) {
28
+ files.push(...walkFiles(fullPath, baseDir));
29
+ continue;
30
+ }
31
+ if (entry.isFile()) files.push(relative(baseDir, fullPath));
32
+ }
33
+
34
+ return files;
35
+ }
36
+
37
+ export function syncSkillDir(sourceDir, destDir) {
38
+ rmSync(destDir, { recursive: true, force: true });
39
+ mkdirSync(destDir, { recursive: true });
40
+ cpSync(sourceDir, destDir, { recursive: true });
41
+ }
42
+
43
+ export function syncRepoSkills(repoRoot) {
44
+ const sourceDir = resolve(repoRoot, "skills/libretto");
45
+ for (const dir of SKILL_DIRS.slice(1)) {
46
+ syncSkillDir(sourceDir, resolve(repoRoot, dir));
47
+ }
48
+ }
49
+
50
+ export function compareSkillDirs(repoRoot) {
51
+ const roots = SKILL_DIRS.map((dir) => ({
52
+ label: dir,
53
+ absPath: resolve(repoRoot, dir),
54
+ }));
55
+ const missing = roots.filter(({ absPath }) => !existsSync(absPath));
56
+ const mismatches = [];
57
+
58
+ if (missing.length > 0) {
59
+ return {
60
+ ok: false,
61
+ issues: missing.map(({ label }) => `missing directory: ${label}`),
62
+ };
63
+ }
64
+
65
+ const expectedFiles = walkFiles(roots[0].absPath);
66
+ const expectedFileSet = new Set(expectedFiles);
67
+
68
+ for (const root of roots.slice(1)) {
69
+ const actualFiles = walkFiles(root.absPath);
70
+ const actualFileSet = new Set(actualFiles);
71
+
72
+ for (const file of expectedFiles) {
73
+ if (!actualFileSet.has(file)) {
74
+ mismatches.push(`${root.label} is missing file: ${file}`);
75
+ }
76
+ }
77
+
78
+ for (const file of actualFiles) {
79
+ if (!expectedFileSet.has(file)) {
80
+ mismatches.push(`${root.label} has unexpected file: ${file}`);
81
+ }
82
+ }
83
+ }
84
+
85
+ for (const file of expectedFiles) {
86
+ const expectedContent = readFileSync(join(roots[0].absPath, file));
87
+ for (const root of roots.slice(1)) {
88
+ const targetPath = join(root.absPath, file);
89
+ if (!existsSync(targetPath)) continue;
90
+ const actualContent = readFileSync(targetPath);
91
+ if (!expectedContent.equals(actualContent)) {
92
+ mismatches.push(
93
+ `${root.label} differs from ${roots[0].label}: ${file}`,
94
+ );
95
+ }
96
+ }
97
+ }
98
+
99
+ return {
100
+ ok: mismatches.length === 0,
101
+ issues: mismatches,
102
+ };
103
+ }
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readdirSync, readFileSync, writeFileSync } from "node:fs";
4
+ import { basename, join, resolve } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ function usage() {
8
+ console.error(
9
+ "Usage: node scripts/summarize-evals.mjs <score-dir> <summary-json-path>",
10
+ );
11
+ }
12
+
13
+ function normalizeFailureRecord(failure) {
14
+ return {
15
+ criterion: String(failure?.criterion ?? "").trim(),
16
+ reason: String(failure?.reason ?? "").trim(),
17
+ };
18
+ }
19
+
20
+ function normalizeRecord(record) {
21
+ const failures = Array.isArray(record?.failures)
22
+ ? record.failures
23
+ .map(normalizeFailureRecord)
24
+ .filter(
25
+ (failure) =>
26
+ failure.criterion.length > 0 && failure.reason.length > 0,
27
+ )
28
+ : [];
29
+
30
+ return {
31
+ name: String(record?.name ?? "").trim(),
32
+ passed: Number(record?.passed ?? 0),
33
+ total: Number(record?.total ?? 0),
34
+ percent: Number(record?.percent ?? 0),
35
+ failures,
36
+ };
37
+ }
38
+
39
+ export function loadScoreRecords(scoreDirArg) {
40
+ const scoreDir = resolve(scoreDirArg);
41
+ return readdirSync(scoreDir, { withFileTypes: true })
42
+ .filter((entry) => entry.isFile() && entry.name.endsWith(".json"))
43
+ .map((entry) =>
44
+ JSON.parse(readFileSync(join(scoreDir, entry.name), "utf8")),
45
+ )
46
+ .map(normalizeRecord)
47
+ .sort((a, b) => String(a.name).localeCompare(String(b.name)));
48
+ }
49
+
50
+ export function buildSummary(records) {
51
+ const passed = records.reduce(
52
+ (sum, record) => sum + Number(record.passed || 0),
53
+ 0,
54
+ );
55
+ const total = records.reduce(
56
+ (sum, record) => sum + Number(record.total || 0),
57
+ 0,
58
+ );
59
+ const percent = total > 0 ? Number(((passed / total) * 100).toFixed(2)) : 0;
60
+ const failingRecords = records.filter((record) => record.failures.length > 0);
61
+
62
+ return {
63
+ generatedAt: new Date().toISOString(),
64
+ recordCount: records.length,
65
+ passed,
66
+ total,
67
+ percent,
68
+ failingRecordCount: failingRecords.length,
69
+ records,
70
+ };
71
+ }
72
+
73
+ export function buildMarkdown(summary, summaryPathArg) {
74
+ const lines = [
75
+ "# Eval Summary",
76
+ "",
77
+ `- Overall score: \`${summary.percent}%\``,
78
+ `- Passed criteria: \`${summary.passed}/${summary.total}\``,
79
+ `- Recorded score entries: \`${summary.recordCount}\``,
80
+ `- Failed evals: \`${summary.failingRecordCount}\``,
81
+ `- Summary file: \`${basename(summaryPathArg)}\``,
82
+ ];
83
+
84
+ if (summary.records.length > 0) {
85
+ lines.push("", "## Breakdown", "");
86
+ for (const record of summary.records) {
87
+ const status = record.failures.length > 0 ? "fail" : "pass";
88
+ lines.push(
89
+ `- ${status} \`${record.name}\`: \`${record.percent}%\` (${record.passed}/${record.total})`,
90
+ );
91
+ }
92
+ }
93
+
94
+ if (summary.failingRecordCount > 0) {
95
+ lines.push("", "## Failed Evals", "");
96
+ for (const record of summary.records.filter(
97
+ (candidate) => candidate.failures.length > 0,
98
+ )) {
99
+ lines.push(`### \`${record.name}\``);
100
+ lines.push("");
101
+ lines.push(
102
+ `- Score: \`${record.percent}%\` (${record.passed}/${record.total})`,
103
+ );
104
+ for (const failure of record.failures) {
105
+ lines.push(`- ${failure.criterion}: ${failure.reason}`);
106
+ }
107
+ lines.push("");
108
+ }
109
+ }
110
+
111
+ return `${lines.join("\n").trimEnd()}\n`;
112
+ }
113
+
114
+ function main(argv) {
115
+ const [, , scoreDirArg, summaryPathArg] = argv;
116
+
117
+ if (!scoreDirArg || !summaryPathArg) {
118
+ usage();
119
+ process.exit(1);
120
+ }
121
+
122
+ const summaryPath = resolve(summaryPathArg);
123
+ const records = loadScoreRecords(scoreDirArg);
124
+ const summary = buildSummary(records);
125
+
126
+ writeFileSync(summaryPath, `${JSON.stringify(summary, null, 2)}\n`, "utf8");
127
+ process.stdout.write(buildMarkdown(summary, summaryPath));
128
+ }
129
+
130
+ if (
131
+ process.argv[1] &&
132
+ resolve(process.argv[1]) === fileURLToPath(import.meta.url)
133
+ ) {
134
+ main(process.argv);
135
+ }
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ import { SKILL_DIRS, syncRepoSkills } from "./skills-libretto.mjs";
7
+
8
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
+ const repoRoot = join(__dirname, "..");
10
+
11
+ syncRepoSkills(repoRoot);
12
+ console.log(`libretto: synced skill mirrors across ${SKILL_DIRS.join(", ")}`);
@@ -1,34 +1,36 @@
1
1
  ---
2
2
  name: libretto
3
- description: "Browser automation CLI for inspecting live pages, prototyping interactions, and running browser workflows."
3
+ description: "Browser automation CLI for building, maintaining, and running browser automation workflows by inspecting live pages and prototyping interactions."
4
4
  license: MIT
5
5
  metadata:
6
6
  author: saffron-health
7
- version: "0.4.0"
7
+ version: "0.4.2"
8
8
  ---
9
9
 
10
- # Libretto
10
+ ## How Libretto Works
11
11
 
12
- Use `npx libretto` to inspect live browser state, prototype interactions, and run existing browser workflows.
12
+ - Libretto is a CLI for exploring live websites and building or debugging reusable browser automation scripts.
13
+ - Use Libretto to inspect the real site first: open pages, observe state, inspect requests, and prototype interactions before writing code.
14
+ - Libretto work must end in script changes. Create or edit the workflow file instead of stopping at interactive exploration.
13
15
 
14
- ## Intro
16
+ ## Default Integration Approach
15
17
 
16
- - Use this skill when the truth is on the page.
17
- - Prefer Libretto when you need to see what the browser is doing, not when you only need to edit source files.
18
- - Treat Libretto as a session-based workflow: open a page, inspect it, try a focused action, then turn what you learned into code outside the CLI.
19
- - When building a new integration, prefer reverse-engineering network requests first. Fall back to browser automation when the request path is unclear, too fragile, or blocked by anti-bot systems.
18
+ - Prefer network requests first for new integrations.
19
+ - Read `references/site-security-review.md` before committing to a network-first approach on a new site.
20
+ - Fall back to passive interception or Playwright-driven UI automation when the security review rules network requests out, the request path is not workable, or the user explicitly asks for Playwright.
20
21
 
21
22
  ## Setup
22
23
 
23
24
  - Ask the user to set up snapshot analysis before relying on `snapshot` for page understanding.
24
- - Use `npx libretto init` for first-time setup.
25
- - If they already have credentials, `npx libretto ai configure openai|anthropic|gemini|vertex` is enough.
25
+ - Use `npx libretto init` for first-time workspace setup.
26
+ - If credentials are already available, `npx libretto ai configure openai|anthropic|gemini|vertex` is usually enough.
26
27
 
27
- ## Rules
28
+ ## Working Rules
28
29
 
29
30
  - Announce which session you are using and what page you are on.
30
31
  - Ask instead of guessing when it is unclear what to click, type, or submit.
31
- - Use `snapshot` to understand unknown page state before trying multiple selectors.
32
+ - Read and follow guidelines in `references/code-generation-rules.md` before generating or editing production workflow code.
33
+ - After interactive exploration and code generation, test key logic with `exec`, then verify the workflow file with `run --headless`.
32
34
  - Get explicit user confirmation before mutating actions or replaying network requests that may have side effects.
33
35
  - Never run multiple `exec` commands at the same time.
34
36
  - Keep the browser session open until the user says the session is done.
@@ -38,6 +40,7 @@ Use `npx libretto` to inspect live browser state, prototype interactions, and ru
38
40
  ### `open`
39
41
 
40
42
  - Open a page before using `exec` or `snapshot`.
43
+ - Use `open` at the start of script authoring when you need live page state to decide how the workflow should work.
41
44
  - Use headed mode when the user needs to log in or watch the workflow.
42
45
 
43
46
  ```bash
@@ -45,37 +48,83 @@ npx libretto open https://example.com --headed
45
48
  npx libretto open https://example.com --headless --session debug-example
46
49
  ```
47
50
 
48
- ### `exec`
51
+ ### `connect`
49
52
 
50
- - Use `exec` for focused inspection and short-lived interaction experiments.
51
- - Let failures throw. Do not hide `exec` failures with `try/catch`.
53
+ - Use `connect` to attach to any existing Chrome DevTools Protocol (CDP) endpoint — a browser started with `--remote-debugging-port`, an Electron app, or any other CDP-compatible target.
54
+ - After connecting, `exec`, `snapshot`, `pages`, and all other session commands work normally.
55
+ - Libretto does not manage the connected process's lifecycle. `close` clears the session but does not terminate the remote process.
52
56
 
53
57
  ```bash
54
- npx libretto exec "return await page.url()"
55
- npx libretto exec "return await page.locator('button').count()"
56
- npx libretto exec --visualize "await page.locator('button:has-text(\"Continue\")').click()"
58
+ npx libretto connect http://127.0.0.1:9222 --session my-session
59
+ npx libretto connect http://127.0.0.1:9223 --session another-session
57
60
  ```
58
61
 
59
62
  ### `snapshot`
60
63
 
61
64
  - Use `snapshot` as the primary page observation tool.
62
- - When you want analysis, provide both `--objective` and `--context`.
63
- - If you only need the PNG and HTML files, omit `--objective`. That runs capture-only mode and skips AI analysis.
64
- - When using `--objective`, expect analysis to take time. Use a timeout of at least 2 minutes for shell-wrapped calls.
65
+ - Always provide both `--objective` and `--context`.
66
+ - Use it before guessing at selectors, after workflow failures, and whenever the visible page state is unclear.
67
+ - When analysis is involved, expect it to take time. Use a timeout of at least 2 minutes for shell-wrapped calls.
65
68
 
66
69
  ```bash
67
- npx libretto snapshot
68
70
  npx libretto snapshot \
69
71
  --objective "Find the sign-in form and submit button" \
70
72
  --context "I just opened the login page and need the email field, password field, and submit button."
71
73
  npx libretto snapshot \
74
+ --session debug-example \
75
+ --page <page-id> \
72
76
  --objective "Explain why the table is empty" \
73
- --context "I opened the referrals page and expected rows after applying filters."
77
+ --context "I opened the referrals page, applied filters, and expected rows to appear."
78
+ ```
79
+
80
+ ### `exec`
81
+
82
+ - Use `exec` for focused inspection and short-lived interaction experiments.
83
+ - Use `exec` to validate selectors, inspect data, or prototype a step before you encode it in the workflow file.
84
+ - Let failures throw. Do not hide `exec` failures with `try/catch` or `.catch()`.
85
+ - Do not run multiple `exec` commands in parallel.
86
+
87
+ ```bash
88
+ npx libretto exec "return await page.url()"
89
+ npx libretto exec "return await page.locator('button').count()"
90
+ npx libretto exec --visualize "await page.locator('button:has-text(\"Continue\")').click()"
91
+ ```
92
+
93
+ ### `pages`
94
+
95
+ - Use `pages` when a popup, new tab, or second page appears.
96
+ - If `exec`, `snapshot`, `network`, or `actions` complains about multiple pages, list page ids first and then pass `--page`.
97
+
98
+ ```bash
99
+ npx libretto pages --session debug-example
100
+ npx libretto exec --session debug-example --page <page-id> "return await page.url()"
101
+ ```
102
+
103
+ ### `network`
104
+
105
+ - Use `network` to inspect the requests the page already made.
106
+ - Prefer this when discovering how a site loads data or when validating whether a network-first approach is workable.
107
+ - Filter aggressively by method, URL pattern, and page when the log is noisy.
108
+
109
+ ```bash
110
+ npx libretto network --session debug-example --last 20
111
+ npx libretto network --session debug-example --method POST --filter 'referral|patient'
112
+ npx libretto network --session debug-example --page <page-id>
113
+ ```
114
+
115
+ ### `actions`
116
+
117
+ - Use `actions` when you need a quick record of recent user or agent interactions in the current session.
118
+ - Keep it lightweight. It is a helper for orientation, not the main integration-building workflow.
119
+
120
+ ```bash
121
+ npx libretto actions --session debug-example --last 20
122
+ npx libretto actions --session debug-example --action click --source user
74
123
  ```
75
124
 
76
125
  ### `run`
77
126
 
78
- - Use `run` to execute an existing Libretto workflow.
127
+ - Use `run` to verify a workflow file after creating it or editing it.
79
128
  - If the workflow fails, Libretto keeps the browser open. Inspect the failed state with `snapshot` and `exec` before editing code.
80
129
  - If the workflow pauses, resume it with `npx libretto resume --session <name>`.
81
130
  - Re-run the same workflow after each fix to verify the browser behavior end to end.
@@ -86,6 +135,34 @@ npx libretto run ./integration.ts main --params '{"status":"open"}'
86
135
  npx libretto run ./integration.ts main --auth-profile app.example.com --headed
87
136
  ```
88
137
 
138
+ ### `resume`
139
+
140
+ - Workflows pause by calling `await pause()` in the workflow file.
141
+ - Use `resume` when a workflow hit `await pause()`.
142
+ - Keep resuming the same session until the workflow completes or pauses again.
143
+
144
+ ```bash
145
+ npx libretto resume --session debug-example
146
+ ```
147
+
148
+ ### `save`
149
+
150
+ - Use `save` when the user logs in manually and wants to reuse that authenticated browser state later.
151
+
152
+ ```bash
153
+ npx libretto save app.example.com
154
+ ```
155
+
156
+ ### `close`
157
+
158
+ - Use `close` only when the user is done with the session.
159
+ - `close --all` is available for workspace cleanup.
160
+
161
+ ```bash
162
+ npx libretto close --session debug-example
163
+ npx libretto close --all
164
+ ```
165
+
89
166
  ## Examples
90
167
 
91
168
  ### Building new browser automation workflows
@@ -95,31 +172,16 @@ npx libretto run ./integration.ts main --auth-profile app.example.com --headed
95
172
  ```text
96
173
  <example>
97
174
  [Context: The user wants to build a new browser workflow and does not yet know the page structure]
98
- Assistant: Let me open the target page in headed mode so we can inspect the real workflow.
175
+ Assistant: I'll inspect the real site first if needed, but before I finish I'll create `target-workflow.ts` so the task produces reusable automation code.
99
176
  Assistant: [Runs `npx libretto open https://target.example.com --headed`]
100
- Assistant: I'll first determine whether this flow can be built from captured network requests instead of UI-only automation.
101
- Assistant: [Runs `npx libretto snapshot --objective "Find the next required action" --context "We are starting the workflow from the landing page."`]
102
- Assistant: If the page exposes a clear and stable request path, I'll prioritize that. If the request path is unclear, fragile, or heavily defended, I'll fall back to browser automation.
103
- Assistant: [Uses `network`, `snapshot`, and `exec` as needed to prove the approach]
177
+ Assistant: [Reads `references/site-security-review.md` before choosing between passive network inspection, direct browser fetch calls, and Playwright-first automation]
178
+ Assistant: [Runs `npx libretto snapshot --objective "Find the next required action" --context "We are starting the workflow from the landing page and need the first meaningful step."`]
179
+ Assistant: [Uses `network`, `snapshot`, and `exec` as needed to understand the site and decide the implementation path]
180
+ Assistant: [Reads `references/code-generation-rules.md` before writing production workflow code]
104
181
  Assistant: I found the working path. I'll now update the workflow file outside Libretto and verify it with `npx libretto run ...`.
105
182
  </example>
106
183
  ```
107
184
 
108
- #### Network requests
109
-
110
- ```text
111
- <example>
112
- [Context: The user wants to build an integration using network requests]
113
- Assistant: [Reads `references/reverse-engineering-network-requests.md`]
114
- Assistant: Let me open the page in headed mode. Perform the workflow and I'll use the network log to recreate it.
115
- Assistant: [Runs `npx libretto open https://target.example.com --headed`]
116
- [User performs workflow]
117
- User: I've completed the workflow
118
- Assistant: [Runs `npx libretto network --method POST --last 20`]
119
- Assistant: I found the relevant requests. I'll recreate the workflow from those requests, then test the resulting script with `npx libretto run ...`.
120
- </example>
121
- ```
122
-
123
185
  ### Debugging existing workflows
124
186
 
125
187
  ```text
@@ -130,13 +192,15 @@ Assistant: [Runs `npx libretto run ./integration.ts main --session debug-flow --
130
192
  Assistant: The workflow failed and Libretto kept the browser open. I'll inspect the page state before changing code.
131
193
  Assistant: [Runs `npx libretto snapshot --session debug-flow --objective "Find the blocking error or broken selector target" --context "The workflow just failed after trying to continue from the review step, and I need to identify the visible blocker on the current page."`]
132
194
  Assistant: [Runs `npx libretto exec --session debug-flow "...focused inspection or prototype..."`]
195
+ Assistant: [Reads `references/code-generation-rules.md` before patching the workflow file]
133
196
  Assistant: I found the issue. I'll patch the workflow code, then rerun `npx libretto run ...` to verify the fix.
134
197
  </example>
135
198
  ```
136
199
 
137
200
  ## References
138
201
 
139
- - For reverse-engineering captured requests, read `references/reverse-engineering-network-requests.md`.
140
- - For incorporating manual browser steps the user performed, read `references/user-action-log.md`.
141
- - For saving and reusing login state, read `references/auth-profiles.md`.
142
- - For multiple open pages and page targeting, read `references/pages-and-page-targeting.md`.
202
+ - Read `references/configuration-file-reference.md` when you need to inspect or change `.libretto/config.json` for snapshot model selection or viewport defaults.
203
+ - Read `references/site-security-review.md` before reviewing the site's security posture and deciding whether to lead with network requests, passive interception, or Playwright DOM automation on a new site.
204
+ - Read `references/code-generation-rules.md` before writing or editing production workflow files.
205
+ - Read `references/auth-profiles.md` when the site requires login and the simplest path is to save local browser state.
206
+ - Read `references/pages-and-page-targeting.md` when a session has multiple open pages or you need `--page`.