saffron-ai 0.9.2 → 0.9.4
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 +219 -29
- package/README.md +3 -1
- package/dist-pkg/cli.js +383 -246
- package/package.json +1 -1
- package/skills/saffron/SKILL.md +13 -1
- package/skills/saffron/references/config.md +40 -5
- package/skills/saffron/references/syntax.md +6 -1
package/package.json
CHANGED
package/skills/saffron/SKILL.md
CHANGED
|
@@ -132,7 +132,7 @@ Scenario: Checkout happy path
|
|
|
132
132
|
|
|
133
133
|
```bash
|
|
134
134
|
npx saffron run # record misses, replay hits, heal failures
|
|
135
|
-
npx saffron run --filter @smoke # by tag (
|
|
135
|
+
npx saffron run --filter @smoke # by tag; or an expression: "@e2e and not @wip", "(@smoke or @sanity) and not @flaky"
|
|
136
136
|
npx saffron run --no-agent # replay only (CI without AI access)
|
|
137
137
|
npx saffron status # what is cached, pending, tagged; vocabulary health
|
|
138
138
|
npx saffron diff # what a proposal changes, action by action: the evidence
|
|
@@ -141,7 +141,10 @@ npx saffron accept --all # promote verified proposals to caches
|
|
|
141
141
|
npx saffron accept <file> [<file>...] # promote chosen ones
|
|
142
142
|
npx saffron reject <file> [<file>...] # discard chosen ones; the agent retries next run
|
|
143
143
|
npx saffron report # open the HTML report
|
|
144
|
+
npx saffron dashboard # suite quality: failing, flaky, never run, slowest, per tag
|
|
144
145
|
npx saffron run --rerecord --filter @t # a recording is wrong: record it fresh
|
|
146
|
+
npx saffron trace "<scenario>" # a red or healed scenario, step by step, with the page at every action ("trace" on in config)
|
|
147
|
+
npx saffron runs # this branch's CI runs; a heal made there: saffron import --run <id>
|
|
145
148
|
```
|
|
146
149
|
|
|
147
150
|
Results: **green** = cached pass · **yellow** = pending review (an AI
|
|
@@ -168,6 +171,15 @@ unless deliberately authoring a manual cache (`"recordedBy": "manual"`).
|
|
|
168
171
|
To change behavior, change the `.feature`/`.saffron` text and let the run
|
|
169
172
|
re-record (mostly seeded from existing step recordings).
|
|
170
173
|
|
|
174
|
+
**Review a CI heal.** A heal made in CI travels as a bundle (`saffron
|
|
175
|
+
export` in the workflow, uploaded as an artifact). On the reviewer's
|
|
176
|
+
checkout, `saffron runs` lists the branch's CI runs and `saffron import
|
|
177
|
+
--run <id>` (or `saffron import <bundle>`) files the run's proposals,
|
|
178
|
+
report and traces here, each checked against this checkout; then
|
|
179
|
+
`saffron diff`, `saffron accept` and commit `.saffron/cache/` as usual.
|
|
180
|
+
With `saffron export --branch <name>` CI pushes the proposals as a branch
|
|
181
|
+
with a draft pull request instead: switch to it, accept, commit, push.
|
|
182
|
+
|
|
171
183
|
## Anti-patterns that cost money or hide bugs
|
|
172
184
|
|
|
173
185
|
| Don't | Do |
|
|
@@ -63,13 +63,14 @@ your-project/
|
|
|
63
63
|
| `provider` | Whose agent records and heals, on its login on this machine: `claude` (default), `codex` (ChatGPT plan), `antigravity` (Google AI Pro/Ultra via the `agy` CLI, experimental), `cursor`. Also `--provider` |
|
|
64
64
|
| `model` | Model in the provider's own names (unset: the provider's default). A `--provider` run on another provider ignores it |
|
|
65
65
|
| `healModel` | Cheaper model for heal sessions only |
|
|
66
|
+
| `ci` | Where `saffron runs` and `import --run` find CI runs: `{"provider": "github" \| "azure", "organization": ..., "project": ...}`. Unset, an Azure Repos remote means Azure Pipelines (through `az`), anything else GitHub Actions (through `gh`). `organization` and `project` are for a GitHub repository built by Azure Pipelines |
|
|
66
67
|
|
|
67
68
|
## CLI
|
|
68
69
|
|
|
69
70
|
| Command | Purpose |
|
|
70
71
|
|---|---|
|
|
71
|
-
| `saffron run [paths] [--filter
|
|
72
|
-
| `saffron trace [scenario] [--port n] [--no-open] [--json]` | Open the execution replay of a scenario from the last run (scenario name, feature path or `feature:scenario`; default: every traced scenario in one replay, opening on a red one, else a yellow one); `--json` prints the parsed trace |
|
|
72
|
+
| `saffron run [paths] [--filter <tag expression>] [--no-agent] [--strict] [--browser b] [--workers n] [--shard i/n] [--shard-by scenario\|file] [--provider p] [--heal-model m] [--headed] [--trace mode]` | Run; cached replay, agent on misses/failures. `--filter` takes tags or an expression (`"@e2e and not @wip"`). `--shard 2/4` runs one part of the suite on one CI machine and writes `shard-2-of-4.json` instead of `latest.json`; `--trace retain-on-failure` keeps an execution trace of each scenario that is not green |
|
|
73
|
+
| `saffron trace [scenario] [--port n] [--no-open] [--json] [--run startedAt]` | Open the execution replay of a scenario from the last run (scenario name, feature path or `feature:scenario`; default: every traced scenario in one replay, opening on a red one, else a yellow one); `--json` prints the parsed trace; `--run` opens it only while the last run is that one |
|
|
73
74
|
| `saffron accept [files... \| --all] [--include-unverified] [--with-feature-edit] [--propagate]` | Promote proposals (`--all` skips UNVERIFIED ones unless `--include-unverified`); `--with-feature-edit` rewrites adapted steps in the feature file; `--propagate` applies a heal's locator fix to every cache using that locator |
|
|
74
75
|
| `saffron reject [files... \| --all]` | Discard proposals |
|
|
75
76
|
| `saffron prune [--yes] [--check] [--json]` | List recordings no scenario owns any more; `--yes` deletes them, `--check` exits 1 for CI |
|
|
@@ -80,9 +81,14 @@ your-project/
|
|
|
80
81
|
| `saffron mcp` / `saffron lsp` | MCP tools (`search_steps`, `list_step_sets`, `project_status`) for AI assistants / language server for editors |
|
|
81
82
|
| `saffron init [--examples] [--agents list]` | Install this skill into the project's agent directories, register MCP, add an AGENTS.md block, scaffold config; `--examples` adds the Saucedemo example suite |
|
|
82
83
|
| `saffron report [--merge [paths]]` | Open the latest HTML report; `--merge` combines a complete set of shard reports into `latest.json`, `latest.html` and one history line |
|
|
84
|
+
| `saffron dashboard [--stdout] [--no-open]` | The suite's quality from the run history (failing and for how long, flaky, repeatedly healed, never run, slowest, trends, per tag); writes and opens `.saffron/reports/dashboard.html`, `--stdout` prints it |
|
|
85
|
+
| `saffron export [-o file] [--branch name [--base branch]]` | In CI: pack the pending proposals, the run's report, screenshots and traces into `.saffron/reports/saffron-bundle.zip` to upload as an artifact. `--branch` also pushes the proposals as a new branch and opens a draft pull request (GitHub through `gh`, Azure DevOps through `az`); exit 1 only when the branch is pushed but the pull request could not be opened |
|
|
86
|
+
| `saffron runs [--branch b] [--limit n] [--provider github\|azure] [--json]` | The branch's CI runs, with how many of each run's proposals are pending here; exit 2 when `gh` or `az` is missing or signed out |
|
|
87
|
+
| `saffron import <bundles...> \| --run <id> [--overwrite] [--allow-unknown-repository] [--json]` | Bring a CI run's bundle into this checkout: its proposals (each checked against the files here), report and traces. Then `saffron diff` and `saffron accept` as usual. Exit 1 when a proposal was not imported, 2 when the bundle was refused |
|
|
83
88
|
|
|
84
89
|
Exit codes: 0 green/yellow, 1 red (or yellow with `--strict`), 2 usage /
|
|
85
|
-
preflight (e.g. a missing `{env:VAR}`)
|
|
90
|
+
preflight (e.g. a missing `{env:VAR}`) or any expected failure, printed as
|
|
91
|
+
one line (`SAFFRON_DEBUG=1` shows the stack).
|
|
86
92
|
|
|
87
93
|
## AI access
|
|
88
94
|
|
|
@@ -114,8 +120,37 @@ npx playwright install chromium
|
|
|
114
120
|
npx saffron run --no-agent --strict # replay committed caches; no AI, no surprises
|
|
115
121
|
```
|
|
116
122
|
|
|
117
|
-
Record
|
|
118
|
-
|
|
123
|
+
Record new scenarios on developer machines. A CI job that may heal (it
|
|
124
|
+
has the provider's login or key) runs without `--no-agent`, and its heals
|
|
125
|
+
travel to the reviewer as a bundle, never lost with the CI workspace:
|
|
126
|
+
|
|
127
|
+
```yaml
|
|
128
|
+
# GitHub Actions
|
|
129
|
+
- run: npx saffron run --strict
|
|
130
|
+
- name: Saffron bundle
|
|
131
|
+
if: failure()
|
|
132
|
+
run: npx saffron export --out saffron-bundle.zip
|
|
133
|
+
- uses: actions/upload-artifact@v4
|
|
134
|
+
if: failure()
|
|
135
|
+
with:
|
|
136
|
+
name: saffron-bundle
|
|
137
|
+
path: saffron-bundle.zip
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Azure Pipelines is the same: `saffron export --out
|
|
141
|
+
$(Build.ArtifactStagingDirectory)/saffron-bundle.zip`, then publish it as
|
|
142
|
+
the `saffron-bundle` artifact. Keep the artifact name starting with
|
|
143
|
+
`saffron-bundle`: that is what `saffron import --run` and both editors'
|
|
144
|
+
CI runs views download. Upload a file outside `.saffron/` (`--out`), as
|
|
145
|
+
`upload-artifact` leaves hidden folders out.
|
|
146
|
+
|
|
147
|
+
On the reviewer's checkout: `npx saffron runs`, then `npx saffron import
|
|
148
|
+
--run <id>`, then `saffron diff`, `saffron accept` and commit
|
|
149
|
+
`.saffron/cache/`. For review in a pull request instead, add `--branch
|
|
150
|
+
saffron/heal-<run id>` to the export step: CI pushes the proposals (not
|
|
151
|
+
the caches) as a branch and opens a draft pull request whose description
|
|
152
|
+
says how to accept them; it needs a token that may push and open pull
|
|
153
|
+
requests.
|
|
119
154
|
|
|
120
155
|
## Reading a report
|
|
121
156
|
|
|
@@ -232,4 +232,9 @@ Scenario: Checkout happy path
|
|
|
232
232
|
Scenario: ...
|
|
233
233
|
```
|
|
234
234
|
|
|
235
|
-
`saffron run --filter @smoke`.
|
|
235
|
+
`saffron run --filter @smoke`. `--filter` takes a tag expression:
|
|
236
|
+
`and`, `or`, `not` and parentheses, with `and` binding tighter than `or`
|
|
237
|
+
(`--filter "@e2e and not @wip"`, `--filter "(@smoke or @sanity) and not
|
|
238
|
+
@flaky"`). A comma, `|`, a space or a repeated `--filter` between plain
|
|
239
|
+
tags means `or`; once `and`, `not` or a parenthesis appears, join every
|
|
240
|
+
tag with `and` or `or`. The `@` may be left out.
|