saffron-ai 0.8.5 → 0.9.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.
- package/CHANGELOG.md +259 -0
- package/README.md +6 -5
- package/dist-pkg/cli.js +848 -139
- package/package.json +3 -3
- package/skills/saffron/SKILL.md +1 -1
- package/skills/saffron/references/config.md +5 -2
- package/templates/saucedemo/saffron.config.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,265 @@ All notable changes to Saffron, in one place: the `saffron-ai` runner (npm),
|
|
|
4
4
|
the VS Code extension (`ChathurangaJayasinghe.saffron-vscode`) and the
|
|
5
5
|
JetBrains plugin (`ai.saffron.jetbrains`). Dates are publish dates.
|
|
6
6
|
|
|
7
|
+
## 0.9.1
|
|
8
|
+
|
|
9
|
+
Released 2026-09-29.
|
|
10
|
+
|
|
11
|
+
Runner
|
|
12
|
+
|
|
13
|
+
- **Tag expressions in `--filter`.** `--filter "@e2e and not @wip"`,
|
|
14
|
+
`"(@smoke or @sanity) and not @flaky"`: `and`, `or`, `not` and
|
|
15
|
+
parentheses, in the shape Cucumber and pytest use, with `and` binding
|
|
16
|
+
tighter than `or`. The list forms keep their meaning: `@a,@b`, `@a @b`,
|
|
17
|
+
`@a|@b` and a repeated flag all mean `or`, and the `@` may be left off.
|
|
18
|
+
A malformed expression stops the run with exit 2 and names the problem.
|
|
19
|
+
- **Replay fixes.** A network assertion checked without the run's
|
|
20
|
+
response log (the check after a heal) now checks the response body too:
|
|
21
|
+
a heal could pass while the API answered `{"ok":false}`. A file upload
|
|
22
|
+
armed for the next file chooser is set by the click that opens it,
|
|
23
|
+
which waits for a chooser that opens late up to the action timeout. If
|
|
24
|
+
no chooser opens, or the files cannot be set (a missing file), that
|
|
25
|
+
click fails, and so does each retry of it, instead of the runner ending
|
|
26
|
+
or the scenario passing with nothing uploaded. `{env:VAR}` in the
|
|
27
|
+
pattern of `expectMatches` and `expectAttribute` is resolved like
|
|
28
|
+
every other field.
|
|
29
|
+
- **A click through a locator chain records the element it clicked.**
|
|
30
|
+
When the agent targets an element with a selector string, Playwright
|
|
31
|
+
MCP reports a chain such as `locator('article:has(...)')
|
|
32
|
+
.locator('role=button[name="Select room"]')`. The recorder kept only the
|
|
33
|
+
first link, so replay clicked the whole card instead of its button, and
|
|
34
|
+
a refine pass then re-clicked the room under the next step. Chains of
|
|
35
|
+
locators, getBy calls, `filter({ hasText })`, `nth`, `first` and `last`
|
|
36
|
+
are now kept whole as one selector; a chain part with no selector form
|
|
37
|
+
is not recorded rather than recorded as a different element. The agent
|
|
38
|
+
is also told to act through snapshot refs, which record as role and
|
|
39
|
+
name.
|
|
40
|
+
- **Dates picked through a selector string move with the calendar.** A
|
|
41
|
+
date in a fallback selector or test id (`role=button[name="2026-10-11"]`)
|
|
42
|
+
is now templated to `{date+N}` like one in a name or label, and replay
|
|
43
|
+
resolves it there, so the recording does not expire the next day.
|
|
44
|
+
- **Open Replay opens the run its row lists.** `saffron trace --run
|
|
45
|
+
<startedAt>` refuses a later run ("The last run changed since this was
|
|
46
|
+
listed"), and both editors pass it, so an older row no longer opens a
|
|
47
|
+
newer run's trace.
|
|
48
|
+
- **A decision under way keeps its lock.** A proposal lock held by a live
|
|
49
|
+
process on this machine is never broken for its age, so a paused
|
|
50
|
+
decision cannot be overtaken.
|
|
51
|
+
|
|
52
|
+
Editors (VS Code extension 0.3.2, JetBrains plugin 0.3.2)
|
|
53
|
+
|
|
54
|
+
- **VS Code: a project nested inside a bigger workspace folder finds its
|
|
55
|
+
data folder.** `{data:...}` completion, hover and diagnostics resolve
|
|
56
|
+
against the nearest `saffron.config.json` above the file, not the
|
|
57
|
+
workspace root.
|
|
58
|
+
- **JetBrains: the Run Tagged tooltip** says the CLI also takes tag
|
|
59
|
+
expressions.
|
|
60
|
+
- **Open Replay opens the run its row lists**, with saffron-ai 0.9.1 or
|
|
61
|
+
later; with an older runner it opens the scenario as before.
|
|
62
|
+
- **JetBrains: the tool window opens on IntelliJ 2026.2.** The Dashboard
|
|
63
|
+
tab crashed the whole Saffron tool window there (NoClassDefFoundError:
|
|
64
|
+
JBCefApp), because 2026.2 moved the embedded browser into its own
|
|
65
|
+
plugin. The plugin now depends on it where it exists, and without it
|
|
66
|
+
the Dashboard falls back to Open in Browser.
|
|
67
|
+
- **VS Code: `.saffron` files show the Saffron mark** in the explorer and
|
|
68
|
+
tabs, under any file icon theme that has no icon of its own for them
|
|
69
|
+
(Seti, the default, included).
|
|
70
|
+
|
|
71
|
+
## 0.9.0
|
|
72
|
+
|
|
73
|
+
Released 2026-09-27.
|
|
74
|
+
|
|
75
|
+
Runner
|
|
76
|
+
|
|
77
|
+
- **Execution replay: `saffron trace`.** Not a video. With `"trace":
|
|
78
|
+
"retain-on-failure"` (or `"on"`) in the config, or `--trace <mode>` for
|
|
79
|
+
one run, a scenario keeps an execution trace, and `saffron trace` opens
|
|
80
|
+
it as a replay in your browser: your Given, When and Then on the left,
|
|
81
|
+
each step opening onto the actions Saffron replayed and the Playwright
|
|
82
|
+
calls behind them; the page at the selected action in the middle,
|
|
83
|
+
rendered as a live DOM you can inspect, before, during or after the
|
|
84
|
+
action, with a filmstrip of the whole run; on the right the failure, the
|
|
85
|
+
retries, the call log, the network during the selection, and what the
|
|
86
|
+
agent changed. The failed step is selected when you arrive. Traces live
|
|
87
|
+
next to the screenshots under `.saffron/artifacts/`, are kept for the
|
|
88
|
+
latest run and pruned with the rest. The report names the command for
|
|
89
|
+
every traced scenario, as `feature:scenario`. The page opens on `127.0.0.1` or `localhost`, a
|
|
90
|
+
forwarded port included, and keeps what it loaded when a later run
|
|
91
|
+
replaces the files. A tab left open from before `saffron trace` was
|
|
92
|
+
started again says to reload the page instead of loading the new run's
|
|
93
|
+
files. The trace is Playwright's own format, rendered by
|
|
94
|
+
the snapshot engine of the Playwright Saffron runs with; the replay
|
|
95
|
+
around it is Saffron's. The Saucedemo example config ships with
|
|
96
|
+
`retain-on-failure`.
|
|
97
|
+
- **Secrets in a trace.** `{env:VAR}` values, including one a data file
|
|
98
|
+
holds, are masked back to their tokens before the trace is kept: in the
|
|
99
|
+
event log, the DOM snapshots and stylesheets, and every request and
|
|
100
|
+
response body, URL, header and WebSocket message, in the spellings the
|
|
101
|
+
value takes there (JSON escapes, percent-encoding as browsers and
|
|
102
|
+
servers write it, HTML entities, CSS escapes). The unmasked trace never
|
|
103
|
+
reaches `.saffron/artifacts/`, even when a run is killed while saving
|
|
104
|
+
it. The scheme, host and port of a URL are kept, so the replay can
|
|
105
|
+
still load the page's stylesheets and images; a secret anywhere else in
|
|
106
|
+
it, credentials included, is written `%7Benv:NAME%7D`. A value
|
|
107
|
+
shorter than three characters, one inside base64 or another binary
|
|
108
|
+
encoding, a value in a URL's host, and the filmstrip's pixels are not
|
|
109
|
+
masked, so look through a trace before attaching it to a public issue.
|
|
110
|
+
- **Review a heal from the replay.** The selected step shows what the
|
|
111
|
+
proposal changes for it, the elements Playwright actually matched are
|
|
112
|
+
outlined in the page, and the Saffron panel has **Accept recording** and
|
|
113
|
+
**Reject proposal**. Both act only on the exact proposal associated with
|
|
114
|
+
this run: if it was replaced, accepted or rejected since, the replay
|
|
115
|
+
refuses and says why. One that went stale against the scenario or the
|
|
116
|
+
committed recording, or can no longer be read, cannot be accepted but
|
|
117
|
+
can still be rejected. An unverified recording, or one this run showed
|
|
118
|
+
failing, needs an explicit acknowledgement first. Suggested feature
|
|
119
|
+
rewrites are listed with the command that applies them (`saffron accept
|
|
120
|
+
--with-feature-edit`); they and propagation to other recordings stay
|
|
121
|
+
with `saffron accept`.
|
|
122
|
+
- **Accept or reject the proposal you listed.** `saffron status --json`
|
|
123
|
+
gives each pending proposal a `revision`, and `saffron accept
|
|
124
|
+
<file>#<revision>` or `saffron reject <file>#<revision>` decides that
|
|
125
|
+
proposal only if it is still the one listed. One a later run replaced
|
|
126
|
+
is refused ("changed since it was listed. Refresh and review it
|
|
127
|
+
again."), exit 1, and left in place. A decision and a run filing a
|
|
128
|
+
proposal never interleave, so what is checked is what is promoted, with
|
|
129
|
+
or without a revision. Accept and reject act only on files under
|
|
130
|
+
`.saffron/proposals`: `saffron reject features/login.saffron` used to
|
|
131
|
+
delete the feature file.
|
|
132
|
+
- **Proof replays and the agent in the trace.** A recorded or healed
|
|
133
|
+
scenario also keeps a trace of each proof replay (`proof-1.zip`,
|
|
134
|
+
`proof-2.zip`) with its own step outcomes, and **Trace** at the top of
|
|
135
|
+
the page switches between them and the run itself. The agent's own
|
|
136
|
+
browser calls are recorded too, under the step it was working on and in
|
|
137
|
+
words ("click Login button", "read the page"); what it looked at before
|
|
138
|
+
naming a step is filed under the step it named next. A healed step is
|
|
139
|
+
marked ✦, and a healed run opens on it.
|
|
140
|
+
If that cannot start with the installed Playwright, Saffron says why,
|
|
141
|
+
once, and the agent works through the standard browser connection
|
|
142
|
+
without being traced.
|
|
143
|
+
- **Masking fixes outside the trace.** A secret whose value appears in
|
|
144
|
+
another variable's name or in a `{data:...}` key no longer corrupts the
|
|
145
|
+
recording (`ROLE=ADMIN` beside `{env:ADMIN_PASSWORD}` used to be
|
|
146
|
+
recorded as `{env:{env:ROLE}_PASSWORD}`, which never replayed).
|
|
147
|
+
Three-character values (a CVV, a short PIN) are now masked in
|
|
148
|
+
recordings, reports and history too, as in the trace; in a recording a
|
|
149
|
+
value shorter than six characters is masked where it stands alone or
|
|
150
|
+
where the step joins it to other text (`"qa{env:ID}"`), so `ENV=dev`
|
|
151
|
+
never turns `/devices` into `/{env:ENV}ices`. Reports mask
|
|
152
|
+
a value's URL-encoded and HTML-escaped forms. A recording masks a
|
|
153
|
+
secret in every field replay reads (labels, placeholders, test ids,
|
|
154
|
+
fallback selectors, frame names, drag targets, URL patterns, the text
|
|
155
|
+
for a prompt dialog), not only typed values and URLs, and replay
|
|
156
|
+
resolves the token in each of them: a selector recorded as
|
|
157
|
+
`td.{env:NAME}` still finds its element. A
|
|
158
|
+
`{env:VAR}` written as a value in a data file is masked in recordings
|
|
159
|
+
and reports like one written in the step.
|
|
160
|
+
- **The HTML report embeds only the run's own screenshots.** A merged
|
|
161
|
+
report (`saffron report --merge`) could be made to embed any file the
|
|
162
|
+
merging machine could read, such as `.env`; it now embeds pictures from
|
|
163
|
+
`.saffron/artifacts/` only, and escapes the setup and teardown lines.
|
|
164
|
+
- **Evidence from an interrupted run is not mixed in.** Screenshots and
|
|
165
|
+
traces are kept at fixed paths that the next run replaces. A run
|
|
166
|
+
stopped before it wrote its report used to leave the previous report
|
|
167
|
+
pointing at the new files; the IDE panels now leave out such a
|
|
168
|
+
screenshot and `saffron trace` refuses such a trace.
|
|
169
|
+
- **Playwright 1.63 and trace format 9.** The runner now depends on
|
|
170
|
+
Playwright 1.63, and the replay reads both trace formats: 8 (up to
|
|
171
|
+
1.62) and 9 (1.63 and later), which files snapshots under the call by
|
|
172
|
+
phase and screencast frames under their own folder. A project that
|
|
173
|
+
resolved 1.63 saw broken filmstrip tiles and "No snapshot for this
|
|
174
|
+
selection".
|
|
175
|
+
- **The first snapshot shown no longer arrives unstyled.** The snapshot
|
|
176
|
+
engine forgets a trace while its first frame is still loading, so that
|
|
177
|
+
frame's stylesheets and fonts came back missing and the page looked
|
|
178
|
+
bare until the next step was selected. The replay now warms the engine
|
|
179
|
+
with a hidden frame first.
|
|
180
|
+
- **A step with no snapshot of its own shows the last page there was.** A
|
|
181
|
+
step the agent only inspected (the accessibility tree, the title, the
|
|
182
|
+
console) leaves no DOM snapshot, since Playwright takes them around
|
|
183
|
+
interactions. Instead of an empty pane, the replay shows the page after
|
|
184
|
+
the last interaction before that step and says so above it. A step
|
|
185
|
+
that never ran, after a failure, shows no page: "This step was never
|
|
186
|
+
reached." An action
|
|
187
|
+
that took no snapshot (reading the title, say) shows the page as the
|
|
188
|
+
last snapshot before it left it, not as its step began. With several
|
|
189
|
+
tabs open it only borrows from the same tab; Playwright 1.63 does not
|
|
190
|
+
record which tab such an action ran in, so when that is unknown the
|
|
191
|
+
replay says so, and the filmstrip marks no frame, rather than show
|
|
192
|
+
another tab. A tab opened later in the run changes nothing before it, and one
|
|
193
|
+
closed changes nothing after.
|
|
194
|
+
- **Every traced scenario in one replay.** `saffron trace` with no name
|
|
195
|
+
(or a name several scenarios share) opens them all, with a Scenario
|
|
196
|
+
picker in the header, starting on a red one, else a yellow one. A run
|
|
197
|
+
with `"trace": "on"` where everything passed opens too. `--json` with no
|
|
198
|
+
name prints the scenario the replay opens on. Other scenarios' traces
|
|
199
|
+
are read when first picked and checked against their digest; one that
|
|
200
|
+
cannot be read says why.
|
|
201
|
+
- **A step lists what happened in the order it happened.** Saffron's page
|
|
202
|
+
check, the recorded actions, the agent's and the calls outside any step
|
|
203
|
+
are interleaved by time, and the arrow keys follow that order. A call
|
|
204
|
+
outside every step is filed under the step running when it started. The
|
|
205
|
+
keys keep working after a click on a button or tab, leave Cmd, Ctrl and
|
|
206
|
+
Alt shortcuts to the browser, and the selected row scrolls into view.
|
|
207
|
+
- **✦ marks only a step the agent healed.** In a first recording, a step
|
|
208
|
+
the agent worked through reads "recorded by the agent". In a proof
|
|
209
|
+
replay, or a run that replayed a pending proposal without the agent, a
|
|
210
|
+
step the proposal changes reads "ran the proposed recording". Neither
|
|
211
|
+
is called healed, and Details names the kind of run: Heal review, First
|
|
212
|
+
recording, New recording or Proposed recording.
|
|
213
|
+
- **A pending proposal that failed is shown apart.** When a run replays a
|
|
214
|
+
pending proposal, it fails, and the run goes on to the committed
|
|
215
|
+
recording or records again, the proposal's attempt is one section,
|
|
216
|
+
"Earlier attempt: the pending proposal", instead of both executions
|
|
217
|
+
being merged into the steps.
|
|
218
|
+
- **A heal whose assertion does not hold as written says so on that
|
|
219
|
+
step.** The failed step shows the guard's "Assertion failed as written"
|
|
220
|
+
reason instead of what replay missed first, and the execution trace
|
|
221
|
+
shows the steps the agent carried after it.
|
|
222
|
+
- **A failed agent session no longer marks every step failed.** When a
|
|
223
|
+
heal did not succeed, the report and the replay showed all steps as
|
|
224
|
+
failed, including the ones replay had passed before the agent took
|
|
225
|
+
over. They now show what happened: passed up to the step replay stopped
|
|
226
|
+
on, that step failed with its reason, the rest skipped. When the agent
|
|
227
|
+
carried that step and failed on a later one, the later step is the
|
|
228
|
+
failed one, with the agent's reason. A failed recording shows the steps
|
|
229
|
+
the agent got through the same way.
|
|
230
|
+
- `saffron trace` and `saffron report` keep working on a machine with no
|
|
231
|
+
browser opener (a container, an SSH or WSL remote): they print the
|
|
232
|
+
address to open instead of crashing.
|
|
233
|
+
|
|
234
|
+
Editors (VS Code extension 0.3.0, JetBrains plugin 0.3.0)
|
|
235
|
+
|
|
236
|
+
- **Open Replay** on a scenario in the Last run panel runs `saffron trace`
|
|
237
|
+
for it, by feature and scenario name, so two features with a scenario
|
|
238
|
+
of the same name are told apart. JetBrains also gets `trace` as a run
|
|
239
|
+
configuration command: its *Scenario* field takes a name or
|
|
240
|
+
`feature:scenario` as it is or wrapped whole in double quotes, and blank
|
|
241
|
+
opens every traced scenario, starting on a failed one.
|
|
242
|
+
- **A Last run row stays with its run.** In VS Code, a row (its
|
|
243
|
+
screenshot, Open Replay, Open Scenario) acts on the project it came
|
|
244
|
+
from, even after the panel has moved to another one, and says so if
|
|
245
|
+
that project is no longer open.
|
|
246
|
+
In both editors, a screenshot is checked against the digest its run
|
|
247
|
+
recorded when you open it, so a picture a later run put at the same
|
|
248
|
+
path is refused instead of shown as this run's, and what opens is a copy
|
|
249
|
+
of the picture that was checked, named after the scenario. VS Code keeps
|
|
250
|
+
the copies in the extension's own storage, one folder per window,
|
|
251
|
+
removed when the window closes; JetBrains in the IDE's system folder,
|
|
252
|
+
removed after a day. Neither uses the shared temporary folder. In
|
|
253
|
+
JetBrains, double-clicking such a row no longer jumps to the scenario
|
|
254
|
+
over the message saying why. In VS Code, Open Scenario on a row whose
|
|
255
|
+
feature file was renamed or deleted since the run says so.
|
|
256
|
+
`saffron status --json` now reports each picture's `sha256`.
|
|
257
|
+
- **Accept and reject decide the proposal you saw.** Both Proposals
|
|
258
|
+
panels send the revision they listed, so a proposal a later run
|
|
259
|
+
replaced after the panel showed it is refused with a prompt to refresh,
|
|
260
|
+
not decided unseen. A replaced proposal comes back unticked in both.
|
|
261
|
+
With a runner that lists no revision, they send the file as before.
|
|
262
|
+
VS Code's Accept all tooltip now says what `--all` does: it accepts
|
|
263
|
+
what is pending when it runs, skipping UNVERIFIED proposals and
|
|
264
|
+
refusing STALE ones.
|
|
265
|
+
|
|
7
266
|
## 0.8.5
|
|
8
267
|
|
|
9
268
|
Released 2026-09-24.
|
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Saffron
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**End-to-end tests written in plain language, with zero-token cached replay and runtime AI healing.**
|
|
4
4
|
|
|
5
5
|
Free to use · [npm: saffron-ai](https://www.npmjs.com/package/saffron-ai) · [site](https://saffron-ai.io)
|
|
6
6
|
|
|
7
|
-
Write tests as
|
|
7
|
+
Write tests in plain language, as `.saffron` files or standard `.feature` files. `.saffron` is Saffron's own test language and goes further than Gherkin: step sets for reusable flows, `{env:VAR}` tokens that keep secrets out of recordings and reports, and `{data:...}` references to data files. There are no step definitions and no glue code. On the first run, an AI agent (Claude Agent SDK + Playwright MCP) executes each scenario in a real browser and records what it did into a **cache**. A human-reviewable JSON action list committed to git. Every run after that, Saffron replays the cache in a real browser: **zero AI calls, zero tokens**, at the speed of the browser and your app. The cache is Saffron's own JSON, not a generated script; replay runs on Playwright's browser engine, and Playwright MCP is only how the agent operates the browser while it records or heals.
|
|
8
8
|
|
|
9
9
|
When the UI changes and a cached step fails at runtime, the agent takes over mid-execution with full scenario context, adapts, and finishes the run, then files a **cache proposal** you accept or reject, like a snapshot test update. Reports state exactly what was adapted and what it cost.
|
|
10
10
|
|
|
@@ -22,7 +22,7 @@ When the UI changes and a cached step fails at runtime, the agent takes over mid
|
|
|
22
22
|
|
|
23
23
|
Like a maturing Cucumber suite, most of a new feature file is steps you already have. Saffron derives a **step index** from committed caches and, when recording a new scenario, replays every seeded island of known steps zero-AI. The agent records only the genuinely novel steps, wherever they sit. Exact step text is the identity (Cucumber-style); quoted values may differ (`"admin"` seeds from `"bob"`); data-table steps seed on matching keys. Measured on a live site: a 6-step scenario with one novel step recorded for **$0.15 / 6 AI calls**, vs $2.07 for a full unseeded recording of comparable length.
|
|
24
24
|
|
|
25
|
-
**Step sets** (shipped): `.saffron` files
|
|
25
|
+
**Step sets** (shipped): `.saffron` files add the `StepSet:` keyword for named, reusable step sequences invoked with `StepSet <name>` inside any scenario. Sets expand at parse time, so their steps cache and seed like ordinary steps; editing a set makes every invoking scenario honestly stale (re-recorded mostly seeded), and heal edits route to the set definition, one fix, every caller follows. Sets are **project-wide**: keep application-wide flows in a sets-only library file (convention: `features/shared.steps.saffron`) and invoke them from any feature.
|
|
26
26
|
|
|
27
27
|
**IDE integration & authoring** (shipped): `saffron steps` lists the project vocabulary with recorded/divergent/unrecorded badges (`--snippets` for native VS Code completion), `saffron mcp` serves it to AI assistants, `saffron lsp` brings completion/navigation/diagnostics to **JetBrains** (incl. Community editions via LSP4IJ) and Neovim, `saffron author` drafts feature files from prose in your own vocabulary, and duplicate wordings that record identical actions get **behavior-proven rename proposals**. The **Saffron VS Code extension** is on the [Marketplace](https://marketplace.visualstudio.com/items?itemName=ChathurangaJayasinghe.saffron-vscode) (`code --install-extension ChathurangaJayasinghe.saffron-vscode`). JetBrains users get the same in one click from the **Saffron JetBrains plugin** ([Marketplace](https://plugins.jetbrains.com/plugin/34240-saffron), [source](https://github.com/s-chathuranga-j/saffron-jetbrains-plugin)): `.saffron` file type, bundled grammar, LSP4IJ wiring. Both editors also **run from the IDE**: right-click Run on `.saffron` files and folders, Saffron run configurations (JetBrains), and a Saffron panel with feature files, tags to run, proposals to accept or reject, vocabulary health, config, and the run report as an in-editor dashboard, all fed by `saffron status --json`.
|
|
28
28
|
|
|
@@ -72,7 +72,7 @@ node dist/cli/index.js -p examples accept --all
|
|
|
72
72
|
|
|
73
73
|
| Command | What it does |
|
|
74
74
|
|---|---|
|
|
75
|
-
| `saffron run [paths] [--headed] [--filter
|
|
75
|
+
| `saffron run [paths] [--headed] [--filter expr] [--rerecord] [--no-agent] [--strict] [--browser b] [--no-hooks] [--workers n] [--heal-model m] [--no-verify] [--no-reuse] [--model m] [--storage-state f] [--trace mode]` | Run features. Cached replays are deterministic; misses/failures escalate to the agent (unless `--no-agent`). Replay cross-browser with `--browser firefox\|webkit`, parallelize with `--workers N`, heal on a cheaper model with `--heal-model`, keep an execution trace with `--trace retain-on-failure`. Exit 1 on red (and on yellow with `--strict`). |
|
|
76
76
|
| `saffron accept [files... \| --all] [--include-unverified] [--with-feature-edit] [--propagate]` | Promote cache proposals to committed caches (`--all` skips UNVERIFIED ones unless `--include-unverified`); `--with-feature-edit` also rewrites the adapted steps in the `.feature` file (and keeps the cache in sync); `--propagate` applies the heal's locator fixes to every other cache using the same locator. One heal repairs N scenarios before they ever fail. No args: list pending proposals. |
|
|
77
77
|
| `saffron reject [files... \| --all]` | Discard proposals; the agent will try again next run. |
|
|
78
78
|
| `saffron status [--json]` | Project overview for people, IDE panels and dashboards: feature files and scenarios with cache state, tags, pending proposals, last run, history, vocabulary health (divergent steps, duplicate wordings proven by identical recordings), effective config. |
|
|
@@ -82,6 +82,7 @@ node dist/cli/index.js -p examples accept --all
|
|
|
82
82
|
| `saffron lsp` | Run the Saffron language server over stdio for JetBrains (LSP4IJ/Ultimate), Neovim, any LSP editor: badge completion, StepSet go-to-definition, hover, diagnostics. Highlighting for JetBrains comes from the bundled TextMate grammar (`textmate/saffron` in the package); run `npx saffron lsp` in a terminal for setup steps. |
|
|
83
83
|
| `saffron init [--agents list] [--examples]` | Make the project agent-ready (`--examples` installs the Saucedemo example suite): installs the bundled **Saffron agent skill** into `.claude/skills` and `.agents/skills` (Copilot/Cursor via `--agents`), registers the `saffron mcp` server in `.mcp.json` / `.cursor/mcp.json` / `.vscode/mcp.json`, adds a managed block to `AGENTS.md`/`CLAUDE.md`, scaffolds config. |
|
|
84
84
|
| `saffron report` | Open the latest HTML report. |
|
|
85
|
+
| `saffron trace [scenario] [--port n] [--no-open] [--json]` | Open the execution replay of the last run (every traced scenario, or the one you name): its Given / When / Then, the page inspectable at every action, the network alongside, and what the agent changed. Needs `"trace": "retain-on-failure"` (or `"on"`). |
|
|
85
86
|
|
|
86
87
|
Configuration lives in `saffron.config.json` at your project root:
|
|
87
88
|
|
|
@@ -130,7 +131,7 @@ The full execution-model diagram is on the site: https://saffron-ai.io/docs
|
|
|
130
131
|
proposals/ pending AI-generated cache updates (review these)
|
|
131
132
|
history.jsonl one line per run: trends (commit recommended)
|
|
132
133
|
reports/ latest.html / latest.json (gitignore these)
|
|
133
|
-
artifacts/
|
|
134
|
+
artifacts/ screenshots and traces from the latest run (gitignore these)
|
|
134
135
|
features/ your .saffron and .feature files
|
|
135
136
|
```
|
|
136
137
|
|