verifaied 0.25.0.dev53__tar.gz → 0.28.0.dev71__tar.gz

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 (67) hide show
  1. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/.gitignore +7 -1
  2. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/PKG-INFO +57 -11
  3. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/README.md +56 -10
  4. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/pyproject.toml +1 -1
  5. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/cli.py +133 -1
  6. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/recorder_driver.js +5 -5
  7. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/recorder_refs.js +8 -8
  8. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/repo_config.py +168 -23
  9. verifaied-0.28.0.dev71/src/verifaied/skill/SKILL.md +168 -0
  10. verifaied-0.28.0.dev71/src/verifaied/skill/reference/commands.md +194 -0
  11. verifaied-0.28.0.dev71/src/verifaied/skillcmd.py +147 -0
  12. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/testcmd.py +179 -21
  13. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/tests/test_repo_config.py +133 -5
  14. verifaied-0.28.0.dev71/tests/test_skill_parity.py +181 -0
  15. verifaied-0.28.0.dev71/tests/test_skillcmd.py +347 -0
  16. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/tests/test_testcmd.py +358 -17
  17. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/uv.lock +2 -2
  18. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/LICENSE +0 -0
  19. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/__init__.py +0 -0
  20. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/__main__.py +0 -0
  21. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/agent_mcp.py +0 -0
  22. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/agent_steps.py +0 -0
  23. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/analyzer.py +0 -0
  24. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/audit.py +0 -0
  25. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/client.py +0 -0
  26. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/config.py +0 -0
  27. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/failure_text.py +0 -0
  28. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/instrument.py +0 -0
  29. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/instrument_snippets.py +0 -0
  30. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/instrumenter.js +0 -0
  31. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/instrumenter.py +0 -0
  32. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/prompts.py +0 -0
  33. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/proxy.py +0 -0
  34. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/record.py +0 -0
  35. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/recorder_observer.js +0 -0
  36. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/recorder_picker.js +0 -0
  37. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/recorder_spec.js +0 -0
  38. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/run_reporter.js +0 -0
  39. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/source_index.py +0 -0
  40. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/stepcheck.py +0 -0
  41. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/subprocess_util.py +0 -0
  42. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/suite_report.py +0 -0
  43. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/uploader.py +0 -0
  44. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/src/verifaied/varcmd.py +0 -0
  45. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/tests/__init__.py +0 -0
  46. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/tests/conftest.py +0 -0
  47. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/tests/test_agent_mcp.py +0 -0
  48. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/tests/test_audit.py +0 -0
  49. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/tests/test_check.py +0 -0
  50. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/tests/test_check_done.py +0 -0
  51. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/tests/test_clear_manual.py +0 -0
  52. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/tests/test_cli.py +0 -0
  53. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/tests/test_client.py +0 -0
  54. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/tests/test_config.py +0 -0
  55. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/tests/test_instrument.py +0 -0
  56. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/tests/test_instrument_detect.py +0 -0
  57. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/tests/test_instrument_snippets.py +0 -0
  58. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/tests/test_instrumenter.py +0 -0
  59. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/tests/test_prompt_parity.py +0 -0
  60. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/tests/test_proxy.py +0 -0
  61. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/tests/test_record.py +0 -0
  62. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/tests/test_reporter_parity.py +0 -0
  63. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/tests/test_source_index.py +0 -0
  64. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/tests/test_stepcheck_parity.py +0 -0
  65. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/tests/test_suite_report.py +0 -0
  66. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/tests/test_uploader.py +0 -0
  67. {verifaied-0.25.0.dev53 → verifaied-0.28.0.dev71}/tests/test_varcmd.py +0 -0
@@ -69,12 +69,18 @@ TODO.md
69
69
  *.tsbuildinfo
70
70
 
71
71
  # Parallel worktrees. `.worktree.mk` is the per-checkout port/project block
72
- # the setup-worktree skill generates (`.env.worktree`, its shell twin, is
72
+ # `.agent/worktree-setup.sh` generates (`.env.worktree`, its shell twin, is
73
73
  # already covered by `**/.env.*` above). `.mcp.json` carries a live
74
74
  # `vr_live_…` bearer token — it is machine-local config, never repo config.
75
75
  .worktree.mk
76
76
  .mcp.json
77
77
 
78
+ # The coordinator's marker for the tree it is standing in: which slot it
79
+ # claims, and whether its build ever finished. Written per checkout, read
80
+ # by the daemon, meaningless anywhere else. Everything else in `.agent/` is
81
+ # committed on purpose.
82
+ .agent/worktree.json
83
+
78
84
  # Beads / Dolt files (added by bd init)
79
85
  .dolt/
80
86
  *.db
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: verifaied
3
- Version: 0.25.0.dev53
3
+ Version: 0.28.0.dev71
4
4
  Summary: Find what's untested in your code — locally, no account required
5
5
  Project-URL: Homepage, https://pypi.org/project/verifaied/
6
6
  Author: Kyle Richards
@@ -425,6 +425,11 @@ verifaied test run --all
425
425
  # Point any of them at another configured environment.
426
426
  verifaied test run --all --env stage
427
427
 
428
+ # How much of the run's clock goes on making the video watchable.
429
+ # `fast` (the default) marks what each step clicks and moves on;
430
+ # `watch` lingers on every action; `off` records without annotations.
431
+ verifaied test run "Settings/API tokens" --video watch
432
+
428
433
  # What's recorded, and how each last went on this branch.
429
434
  verifaied test list
430
435
 
@@ -918,11 +923,18 @@ it — the CLI finds the first one that has it, and `--runner-dir`
918
923
  overrides). This is Node Playwright, not the Python `verifaied[audit]`
919
924
  extra.
920
925
 
921
- **`test record` and `test studio` only run against Playwright 1.60.x.** Recording drives
922
- Playwright's recorder through an internal API — the same entry point
923
- `playwright codegen` uses — so it refuses any version it hasn't been
924
- verified against rather than opening a browser that might record nothing.
925
- `test run` uses only public API and works on any version.
926
+ **`test record` and `test studio` need `@playwright/test` 1.60 or later.**
927
+ Recording is driven by our own Node program through public Playwright API
928
+ — the selector generator behind `PWDEBUG=console`, the AI-mode
929
+ accessibility snapshot and the `aria-ref=` selectors its refs resolve
930
+ through — and every recording ends in a replay, whose video marks what
931
+ was clicked with `video.show`. 1.60 is where the last of those shipped,
932
+ so it is a floor and not an allowlist: anything newer is allowed, and
933
+ anything older is refused rather than opening a browser that would
934
+ record nothing. `test run` stays ungated. Below 1.60 the annotation is
935
+ not available, so set `replay_show_actions_ms = 0` and
936
+ `replay_video_step_banner = false` there — that writes exactly the
937
+ config the CLI wrote before this option existed.
926
938
 
927
939
  ### Exit codes
928
940
 
@@ -961,12 +973,46 @@ and `--no-replay` to skip the replay. `run` takes `--all` in place of a
961
973
  test name. `studio` takes `--no-replay` and takes no test name at all —
962
974
  what it records is whatever the web asks for.
963
975
 
976
+ ## `verifaied skill install` — hand the loop to your agent
977
+
978
+ The CLI ships the agent skill for using it. One command writes it where a
979
+ coding agent will find it:
980
+
981
+ ```bash
982
+ verifaied skill install # this project: .claude/skills/verifaied
983
+ verifaied skill install --user # every project: ~/.claude/skills/verifaied
984
+ ```
985
+
986
+ The skill teaches the loop below — run the tests with coverage, `verifaied
987
+ upload`, `verifaied check-done`, fix what comes back, repeat until done — plus
988
+ the vitest/Istanbul requirement for TypeScript, the recorder for browser
989
+ flows, `verifaied audit`, and the rules that keep the loop honest (no lowered
990
+ thresholds, no `# pragma: no cover`, no `xfail` over a real failure).
991
+
992
+ `install` with no flags writes into the **repository root** you are standing
993
+ in, so running it from a subdirectory still lands the skill where your agent
994
+ looks for it. Outside a repository it writes into the current directory.
995
+
996
+ Because it ships inside this package, the skill can never describe a command
997
+ the installed CLI does not have. It is two files, `SKILL.md` and a command
998
+ reference the agent opens only when it needs a flag, and they land in a
999
+ `verifaied` folder inside the skills directory — yours to read and, in the
1000
+ project-local case, to commit for the rest of the team.
1001
+
1002
+ Re-running the command writes nothing when the installed copy already matches.
1003
+ A copy that differs, because you edited it or because you have since upgraded
1004
+ the CLI, is left alone until you pass `--force`, which replaces the files
1005
+ verifAIed ships and never removes anything you added alongside them. Install
1006
+ somewhere else entirely with `--dir <path>`.
1007
+
1008
+ Exit codes: `0` installed, updated, or already up to date; `1` usage error
1009
+ (`--user` with `--dir`, or a differing copy without `--force`).
1010
+
964
1011
  ## Agent loop
965
1012
 
966
- We've found the most fruitful way to use verifAIed is to put a short
967
- loop into your coding agent's instructions file (`CLAUDE.md`,
968
- `AGENTS.md`, `.cursorrules`, `GEMINI.md`,
969
- `.github/copilot-instructions.md`) so it self-corrects on every change.
1013
+ Agents that do not read `.claude/skills/` want the same loop in their
1014
+ instructions file (`CLAUDE.md`, `AGENTS.md`, `.cursorrules`, `GEMINI.md`,
1015
+ `.github/copilot-instructions.md`), so they self-correct on every change.
970
1016
 
971
1017
  The easiest way is to let your agent write that loop into the rules
972
1018
  file for you. Paste the prompt below into your agent — it will inspect
@@ -1046,7 +1092,7 @@ After implementing or modifying any code in this repo, run this loop until verif
1046
1092
  ```
1047
1093
  2. Upload the results to verifAIed:
1048
1094
  ```
1049
- verifaied upload
1095
+ verifaied upload --junit junit.xml
1050
1096
  ```
1051
1097
  3. Ask if you're done: call the `check_done` tool on the verifAIed MCP server (or run `verifaied check-done`). It returns `{done, status, gaps}` for this branch — failing tests plus untested/partially-covered functions among the ones you changed.
1052
1098
  4. If `done` is true, stop — the branch is covered.
@@ -379,6 +379,11 @@ verifaied test run --all
379
379
  # Point any of them at another configured environment.
380
380
  verifaied test run --all --env stage
381
381
 
382
+ # How much of the run's clock goes on making the video watchable.
383
+ # `fast` (the default) marks what each step clicks and moves on;
384
+ # `watch` lingers on every action; `off` records without annotations.
385
+ verifaied test run "Settings/API tokens" --video watch
386
+
382
387
  # What's recorded, and how each last went on this branch.
383
388
  verifaied test list
384
389
 
@@ -872,11 +877,18 @@ it — the CLI finds the first one that has it, and `--runner-dir`
872
877
  overrides). This is Node Playwright, not the Python `verifaied[audit]`
873
878
  extra.
874
879
 
875
- **`test record` and `test studio` only run against Playwright 1.60.x.** Recording drives
876
- Playwright's recorder through an internal API — the same entry point
877
- `playwright codegen` uses — so it refuses any version it hasn't been
878
- verified against rather than opening a browser that might record nothing.
879
- `test run` uses only public API and works on any version.
880
+ **`test record` and `test studio` need `@playwright/test` 1.60 or later.**
881
+ Recording is driven by our own Node program through public Playwright API
882
+ — the selector generator behind `PWDEBUG=console`, the AI-mode
883
+ accessibility snapshot and the `aria-ref=` selectors its refs resolve
884
+ through — and every recording ends in a replay, whose video marks what
885
+ was clicked with `video.show`. 1.60 is where the last of those shipped,
886
+ so it is a floor and not an allowlist: anything newer is allowed, and
887
+ anything older is refused rather than opening a browser that would
888
+ record nothing. `test run` stays ungated. Below 1.60 the annotation is
889
+ not available, so set `replay_show_actions_ms = 0` and
890
+ `replay_video_step_banner = false` there — that writes exactly the
891
+ config the CLI wrote before this option existed.
880
892
 
881
893
  ### Exit codes
882
894
 
@@ -915,12 +927,46 @@ and `--no-replay` to skip the replay. `run` takes `--all` in place of a
915
927
  test name. `studio` takes `--no-replay` and takes no test name at all —
916
928
  what it records is whatever the web asks for.
917
929
 
930
+ ## `verifaied skill install` — hand the loop to your agent
931
+
932
+ The CLI ships the agent skill for using it. One command writes it where a
933
+ coding agent will find it:
934
+
935
+ ```bash
936
+ verifaied skill install # this project: .claude/skills/verifaied
937
+ verifaied skill install --user # every project: ~/.claude/skills/verifaied
938
+ ```
939
+
940
+ The skill teaches the loop below — run the tests with coverage, `verifaied
941
+ upload`, `verifaied check-done`, fix what comes back, repeat until done — plus
942
+ the vitest/Istanbul requirement for TypeScript, the recorder for browser
943
+ flows, `verifaied audit`, and the rules that keep the loop honest (no lowered
944
+ thresholds, no `# pragma: no cover`, no `xfail` over a real failure).
945
+
946
+ `install` with no flags writes into the **repository root** you are standing
947
+ in, so running it from a subdirectory still lands the skill where your agent
948
+ looks for it. Outside a repository it writes into the current directory.
949
+
950
+ Because it ships inside this package, the skill can never describe a command
951
+ the installed CLI does not have. It is two files, `SKILL.md` and a command
952
+ reference the agent opens only when it needs a flag, and they land in a
953
+ `verifaied` folder inside the skills directory — yours to read and, in the
954
+ project-local case, to commit for the rest of the team.
955
+
956
+ Re-running the command writes nothing when the installed copy already matches.
957
+ A copy that differs, because you edited it or because you have since upgraded
958
+ the CLI, is left alone until you pass `--force`, which replaces the files
959
+ verifAIed ships and never removes anything you added alongside them. Install
960
+ somewhere else entirely with `--dir <path>`.
961
+
962
+ Exit codes: `0` installed, updated, or already up to date; `1` usage error
963
+ (`--user` with `--dir`, or a differing copy without `--force`).
964
+
918
965
  ## Agent loop
919
966
 
920
- We've found the most fruitful way to use verifAIed is to put a short
921
- loop into your coding agent's instructions file (`CLAUDE.md`,
922
- `AGENTS.md`, `.cursorrules`, `GEMINI.md`,
923
- `.github/copilot-instructions.md`) so it self-corrects on every change.
967
+ Agents that do not read `.claude/skills/` want the same loop in their
968
+ instructions file (`CLAUDE.md`, `AGENTS.md`, `.cursorrules`, `GEMINI.md`,
969
+ `.github/copilot-instructions.md`), so they self-correct on every change.
924
970
 
925
971
  The easiest way is to let your agent write that loop into the rules
926
972
  file for you. Paste the prompt below into your agent — it will inspect
@@ -1000,7 +1046,7 @@ After implementing or modifying any code in this repo, run this loop until verif
1000
1046
  ```
1001
1047
  2. Upload the results to verifAIed:
1002
1048
  ```
1003
- verifaied upload
1049
+ verifaied upload --junit junit.xml
1004
1050
  ```
1005
1051
  3. Ask if you're done: call the `check_done` tool on the verifAIed MCP server (or run `verifaied check-done`). It returns `{done, status, gaps}` for this branch — failing tests plus untested/partially-covered functions among the ones you changed.
1006
1052
  4. If `done` is true, stop — the branch is covered.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "verifaied"
3
- version = "0.25.0.dev53"
3
+ version = "0.28.0.dev71"
4
4
  description = "Find what's untested in your code — locally, no account required"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -45,7 +45,20 @@ from verifaied.instrument import run_instrument
45
45
  from verifaied.prompts import build_prompt, format_line_ranges
46
46
  from verifaied.proxy import DEFAULT_PROXY_PORT
47
47
  from verifaied.record import DEFAULT_INTERVAL, DEFAULT_PORT, run_record
48
- from verifaied.repo_config import CONFIG_PATH, ENV_KEY, SECTION, RepoConfigError
48
+ from verifaied.repo_config import (
49
+ CONFIG_PATH,
50
+ ENV_KEY,
51
+ SECTION,
52
+ RepoConfigError,
53
+ VideoProfile,
54
+ )
55
+ from verifaied.skillcmd import (
56
+ SKILL_NAME,
57
+ SkillInstallError,
58
+ detect_project_root,
59
+ install_skill,
60
+ resolve_skills_dir,
61
+ )
49
62
  from verifaied.testcmd import (
50
63
  RecordedTestError,
51
64
  parse_test_ref,
@@ -85,6 +98,13 @@ var_app = typer.Typer(
85
98
  )
86
99
  app.add_typer(var_app, name="var")
87
100
 
101
+ skill_app = typer.Typer(
102
+ add_completion=False,
103
+ no_args_is_help=True,
104
+ help="The agent skill that teaches a coding agent to drive verifAIed.",
105
+ )
106
+ app.add_typer(skill_app, name="skill")
107
+
88
108
  console = Console()
89
109
  err_console = Console(stderr=True)
90
110
 
@@ -1758,6 +1778,16 @@ def test_record_command(
1758
1778
  "--token",
1759
1779
  help=f"API token (vr_live_...). Defaults to ${ENV_API_TOKEN}.",
1760
1780
  ),
1781
+ video: VideoProfile | None = typer.Option(
1782
+ None,
1783
+ "--video",
1784
+ help=(
1785
+ "How the replay that follows this recording should be paced. "
1786
+ "`fast` marks each click and moves on; `watch` lingers on "
1787
+ "every action; `off` records without annotations. Overrides "
1788
+ "replay_show_actions_ms / replay_slow_mo_ms for this run."
1789
+ ),
1790
+ ),
1761
1791
  ) -> None:
1762
1792
  """Record one flow from the terminal, without a studio running.
1763
1793
 
@@ -1850,6 +1880,7 @@ def test_record_command(
1850
1880
  # No `--pre` means "leave the stored chain alone", which is a
1851
1881
  # different instruction from "compose with nothing".
1852
1882
  pre=list(pre) or None,
1883
+ video=video.value if video is not None else None,
1853
1884
  )
1854
1885
  except (RecordedTestError, UploaderError) as e:
1855
1886
  err_console.print(f"[red]VerifAIed error[/red]: {e}")
@@ -1969,6 +2000,16 @@ def test_run_command(
1969
2000
  "--token",
1970
2001
  help=f"API token (vr_live_...). Defaults to ${ENV_API_TOKEN}.",
1971
2002
  ),
2003
+ video: VideoProfile | None = typer.Option(
2004
+ None,
2005
+ "--video",
2006
+ help=(
2007
+ "How the video should be paced. `fast` (the default) marks "
2008
+ "each click and moves on; `watch` lingers on every action; "
2009
+ "`off` records without annotations. Overrides "
2010
+ "replay_show_actions_ms / replay_slow_mo_ms for this run."
2011
+ ),
2012
+ ),
1972
2013
  ) -> None:
1973
2014
  """Replay a recorded test, capturing video, trace and coverage.
1974
2015
 
@@ -2024,6 +2065,7 @@ def test_run_command(
2024
2065
  proxy=proxy,
2025
2066
  proxy_target=proxy_target,
2026
2067
  proxy_port=proxy_port,
2068
+ video=video.value if video is not None else None,
2027
2069
  )
2028
2070
  except (RecordedTestError, UploaderError) as e:
2029
2071
  err_console.print(f"[red]VerifAIed error[/red]: {e}")
@@ -2406,3 +2448,93 @@ def var_unset_command(
2406
2448
  "the environment variable it named is still exported wherever you "
2407
2449
  "set it.[/dim]"
2408
2450
  )
2451
+
2452
+
2453
+ def _display_path(path: Path) -> str:
2454
+ """The shortest honest way to write a path: relative to cwd when it is under it."""
2455
+ try:
2456
+ return str(path.relative_to(Path.cwd()))
2457
+ except ValueError:
2458
+ return str(path)
2459
+
2460
+
2461
+ @skill_app.command("install")
2462
+ def skill_install_command(
2463
+ user: bool = typer.Option(
2464
+ False,
2465
+ "--user",
2466
+ help=(
2467
+ "Install into ~/.claude/skills/ — every repository on this "
2468
+ "machine — instead of this project's .claude/skills/."
2469
+ ),
2470
+ ),
2471
+ directory: Path | None = typer.Option(
2472
+ None,
2473
+ "--dir",
2474
+ "-d",
2475
+ help=(
2476
+ "The skills directory to install into, if it is neither this "
2477
+ "project's nor your home one. The skill lands in a "
2478
+ f"`{SKILL_NAME}` folder inside it."
2479
+ ),
2480
+ ),
2481
+ force: bool = typer.Option(
2482
+ False,
2483
+ "--force",
2484
+ "-f",
2485
+ help="Overwrite an installed copy whose files differ from this one's.",
2486
+ ),
2487
+ ) -> None:
2488
+ """Install the verifAIed skill, so your coding agent knows how to drive this.
2489
+
2490
+ The skill ships inside this CLI, so it always describes the commands
2491
+ you actually have. It teaches an agent the loop — run the tests with
2492
+ coverage, `verifaied upload`, `verifaied check-done`, fix what comes
2493
+ back, repeat until done — and the rules that keep the loop honest.
2494
+
2495
+ verifaied skill install # this project: .claude/skills/verifaied
2496
+ verifaied skill install --user # every project: ~/.claude/skills/verifaied
2497
+
2498
+ "This project" is the root of the repository you are standing in — run
2499
+ it from a subdirectory and the skill still lands where your agent will
2500
+ look for it.
2501
+
2502
+ Re-running it writes nothing when the installed copy already matches.
2503
+ A copy that differs — because you edited it, or because you have since
2504
+ upgraded the CLI — is left alone until you pass --force, which replaces
2505
+ the files verifAIed ships and never removes anything you added
2506
+ alongside them.
2507
+
2508
+ Exit codes:
2509
+
2510
+ 0 installed, updated, or already up to date
2511
+ 1 usage error (--user with --dir, or a differing copy without --force)
2512
+ """
2513
+ try:
2514
+ skills_dir = resolve_skills_dir(
2515
+ user=user,
2516
+ dest=directory,
2517
+ cwd=detect_project_root(Path.cwd()),
2518
+ home=Path.home(),
2519
+ )
2520
+ result = install_skill(skills_dir=skills_dir, force=force)
2521
+ except SkillInstallError as e:
2522
+ err_console.print(f"[red]VerifAIed error[/red]: {escape(str(e))}")
2523
+ raise typer.Exit(code=1) from e
2524
+
2525
+ where = escape(_display_path(result.path))
2526
+ if result.state == "unchanged":
2527
+ console.print(
2528
+ f"[green]Up to date[/green] — the verifAIed skill is already at "
2529
+ f"[bold]{where}[/bold] ({len(result.unchanged)} file(s))."
2530
+ )
2531
+ return
2532
+
2533
+ verb = "Updated" if result.state == "updated" else "Installed"
2534
+ console.print(f"[green]{verb}[/green] the verifAIed skill → [bold]{where}[/bold]")
2535
+ for rel in result.written:
2536
+ console.print(f" {escape(rel)}")
2537
+ console.print(
2538
+ f"\n[dim]Your coding agent picks it up on its next session — ask it to "
2539
+ f"use the {SKILL_NAME} skill.[/dim]"
2540
+ )
@@ -48,9 +48,9 @@
48
48
  // and derives nothing from it: the spec goes out verbatim and Python
49
49
  // owns the single parser, so the two halves can never drift.
50
50
  //
51
- // Every Playwright API below is public and verified on 1.60, which is why
52
- // the Python side gates recording on a version **floor** rather than an
53
- // allowlist. Two startup probes still run — the console API and the AI
51
+ // Every Playwright API below is public and available from 1.59, which is
52
+ // why the Python side gates recording on a version **floor** rather than
53
+ // an allowlist. Two startup probes still run — the console API and the AI
54
54
  // snapshot mode — because a browser that opened without either would
55
55
  // record nothing while looking like it worked.
56
56
  //
@@ -739,7 +739,7 @@ async function main() {
739
739
  );
740
740
  }
741
741
  // The assistant's whole view of the page is an AI-mode snapshot, and
742
- // every element it names is a ref out of one. Public API in 1.60, but
742
+ // every element it names is a ref out of one. Public API from 1.59, but
743
743
  // probed because the failure mode of a build without it is a snapshot
744
744
  // with no refs — and an assistant that can see the page and never touch
745
745
  // anything on it.
@@ -1384,7 +1384,7 @@ async function main() {
1384
1384
  * snapshot measures two seconds of blocked main thread on a
1385
1385
  * 92k-node app — and the only thing that reads the tree is the
1386
1386
  * studio's assistant, so the CLI says when somebody is going to.
1387
- * `page.ariaSnapshot()` is public API in 1.60 and needs no probe; the
1387
+ * `page.ariaSnapshot()` is public API from 1.59 and needs no probe; the
1388
1388
  * worst it can do is throw on a page that navigated mid-read, which
1389
1389
  * reports `page: null` and costs the next beat.
1390
1390
  */
@@ -12,14 +12,14 @@
12
12
  // same one the recorder writes for a click. What the browser runs is what
13
13
  // the spec saves.
14
14
  //
15
- // How refs behave in 1.60, because the checks below rest on it: a ref is
16
- // cached ON THE ELEMENT and reused across snapshots while its role and
17
- // accessible name hold, so an element keeps its number as the page moves
18
- // around it — but the counter lives in the document's injected script and
19
- // starts again at e1 after a cross-document navigation, where the same
20
- // number can name a different element on a similar page. So a ref written
21
- // against an earlier snapshot is trusted when the document is the same and
22
- // its role and name still match, and refused otherwise.
15
+ // How refs behave from 1.59 onward, because the checks below rest on it:
16
+ // a ref is cached ON THE ELEMENT and reused across snapshots while its
17
+ // role and accessible name hold, so an element keeps its number as the
18
+ // page moves around it — but the counter lives in the document's injected
19
+ // script and starts again at e1 after a cross-document navigation, where
20
+ // the same number can name a different element on a similar page. So a
21
+ // ref written against an earlier snapshot is trusted when the document is
22
+ // the same and its role and name still match, and refused otherwise.
23
23
  //
24
24
  // Everything here is string work: finding the refs a line names, reading
25
25
  // what a ref stood for in a given snapshot, checking one line's identity