| name | sap-gui-probe |
| description | Drive a SAP transaction step by step against a natural-language scenario,
dumping each screen's full property tree via /sap-gui-inspect and
emitting a synthesized recording-style VBS at the end. Designed as a
skill-authoring aid: probe SE37 before writing a new /sap-se37 flow.
Captures more than the SAP recorder -- not just findById paths and
actions, but also Changeable, Tooltip, IconName, popup transitions, and
the program/transaction/screen identity at every step.
Capture modes:
* drive (default) -- Claude drives the transaction live (Steps 0-4).
* --record [<vbs>] -- fallback when Claude can't reliably drive the flow
(unfamiliar or write-heavy transaction, or when a real operator's exact
clicks are wanted as ground truth): guides you to record it with SAP
GUI's built-in Script Recording and Playback, then parses the saved VBS
into the same findById/action map. Replaces the former /sap-gui-record
skill; parsing an already-saved recording needs no live session.
Two safety modes (drive only):
* mode=confirm (default) -- read-only actions auto-proceed; write
actions (Save / Activate / Delete and the matching VKey codes 11, 14,
27, 28, 33) pause for explicit user confirmation.
* mode=auto -- opt-in via trailing `--auto` flag in the scenario;
every action proceeds without prompting.
Prerequisites: Active SAP GUI session (use /sap-login first).
|
| argument-hint | <TXN>: <scenario> | <TXN>: <scenario> --record [<vbs-path>] e.g. 'SE37: display FM RFC_READ_TABLE then exit' (append --auto to skip confirmations) |
SAP GUI Probe Skill
You drive a SAP transaction step by step against a natural-language
scenario the user supplied. At every screen transition you call the
sap-gui-inspect VBS to capture a full property dump, you emit one
small action JSON describing the next move, you classify that action as
READ or WRITE, you (optionally) confirm with the user, and you execute it.
When the scenario is complete you synthesize every action into one
replayable VBS. The output folder is the deliverable -- a fresh skill
author can read it and write a deterministic skill for the probed flow.
Task: $ARGUMENTS
Shared Resources
| File | Purpose |
|---|
<SAP_DEV_CORE_SHARED_DIR>/rules/safety_policy.md | Rule 0 (highest priority) — environment guard; enforced by the drive-mode write classifier via sap_safety_gate.ps1 |
<SAP_DEV_CORE_SHARED_DIR>/rules/skill_operating_rules.md | Mandatory operating rules |
<SAP_DEV_CORE_SHARED_DIR>/rules/settings_lookup.md | Settings model — merge per-key on .value (env var → settings.local.json → userconfig.json → settings.json); non-per-connection writes go to userconfig.json |
<SAP_DEV_CORE_SHARED_DIR>/rules/language_independence_rules.md | GUI-scripting language independence — identify by component ID + DDIC field name, status-bar checks via MessageType codes (S/W/E/I/A), VKey instead of menu-text, no branching on .Text/.Tooltip/window titles |
<SAP_DEV_CORE_SHARED_DIR>/rules/sap_gui_scripting_reference.md | Component-ID grammar + type-prefix / VKey / toolbar tables + runtime gotchas. Used by --record mode (Mode R) to decode a recorded VBS into findById paths. Promoted from the retired sap-gui-record skill. |
Step 0 — Resolve work directory and run folder
Resolve work_dir via the env-aware helper — do NOT take work_dir from a direct settings.json read (that ignores the SAPDEV_AI_WORK_DIR env var and userconfig.json). Use the WORK_DIR= value printed by:
powershell -NoProfile -ExecutionPolicy Bypass -Command ". '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_settings_lib.ps1'; . '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_connection_lib.ps1'; Write-Output ('WORK_DIR=' + (Get-SapWorkDir))"
The settings note below still applies to the OTHER keys.
Settings reads/writes follow shared/rules/settings_lookup.md — merge per-key on the .value field (env var → settings.local.json → userconfig.json → settings.json); non-per-connection writes go to userconfig.json. Read sap-dev-core's settings.json (go 2 levels up from <SKILL_DIR> to the
plugin root, then settings.json).
| Setting | Default if blank |
|---|
work_dir | C:\sap_dev_work |
Derive:
{WORK_TEMP} = {work_dir}\temp
{TS} = current timestamp yyyyMMdd-HHmmssfff (milliseconds — REQUIRED for parallel-safety; two probes started in the same second under a parallel scaffolder run would otherwise collide on folder name)
{PID} = the orchestrator's process ID — append as an extra suffix to guarantee uniqueness across simultaneous AI sessions on the same host. PowerShell: $PID. Bash on Windows: echo $$ (the cygwin/git-bash PID, distinct per shell). Either works.
{TXN} = transaction code extracted from the scenario in Step 1
{RUN_FOLDER} = {work_dir}\probes\{TXN}_{TS}_p{PID}
Ensure folders exist (one Bash call, two mkdir):
cmd /c if not exist "{WORK_TEMP}" mkdir "{WORK_TEMP}"
cmd /c if not exist "{RUN_FOLDER}" mkdir "{RUN_FOLDER}"
Why ms + pid? Today's parallel scaffolder spawns N sub-agents from one process; each sub-agent invokes this skill in its own cscript run, but they all derive {TS} from Get-Date -Format 'yyyyMMdd-HHmmss' and can land in the same second. Adding fff (ms) and the process ID makes collisions effectively impossible in practice. Sub-agents under the SAME orchestrator process share {PID} but have different {TS} ms; sub-agents under DIFFERENT orchestrator processes (the typical AI-session-vs-AI-session case) have different {PID}. Either axis alone is sufficient; using both is belt-and-braces.
Step 0.5 — Start logging
State file: {RUN_FOLDER}\sap_gui_probe_run.json. Best-effort.
powershell -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_log_helper.ps1" -Action start -StateFile "{RUN_FOLDER}\sap_gui_probe_run.json" -Skill sap-gui-probe -ParamsJson "{\"txn\":\"<TXN>\",\"mode\":\"<MODE>\",\"scenario\":\"<short scenario>\"}"
Step 0.6 — Resolve active SAP GUI session
Pick the SAP GUI session to drive. Resolution order (first hit wins):
- Explicit
--session "/app/con[N]/ses[M]" flag in $ARGUMENTS → use it verbatim.
- AI-session pin → run:
. '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_connection_lib.ps1'
$sessionPath = Get-SapCurrentSessionPath -WorkTemp '{WORK_TEMP}'
Get-SapCurrentSessionPath resolves the AI-session id (via parent-PID walk), looks up ai_sessions[<id>].connection_id in session_registry.json, finds a usable session on that connection block, and returns its path. Returns empty when no pin and no sole-conn fallback.
- Exactly one connection attached →
Get-SapCurrentSessionPath already handles this via its sole-conn fallback; returns /app/con[0]/ses[0] automatically. Preserves today's behaviour for single-connection users.
- Multiple connections, no pin → empty return. Refuse with: "multiple SAP GUI connections detected and no active session pinned; run /sap-login first to pick one." Log end Status=FAILED ErrorClass=NO_PIN.
The resolved path is stored as {SESSION_PATH} and propagated to every subsequent step:
- Dump calls in Step 2.1 / 2.7 pass
-SessionPath "{SESSION_PATH}" to sap_gui_probe_dump.ps1.
- Action JSONs in Step 2.3 include a
"session": "{SESSION_PATH}" field, which sap_gui_probe_action.vbs resolves via oApp.findById(...).
Stale pin guard: if findById({SESSION_PATH}) returns Nothing (the user closed the session), fall back through the same order starting at step 2. If step 3 fires, the probe aborts cleanly.
Phase 4.2 note: prior versions of this skill read {WORK_TEMP}\sap_active_session.json for the session path AND used userConfig.sap_pinned_session as a fallback. Both removed. Session path resolution now goes through Get-SapCurrentSessionPath; version info (when needed) comes from Get-SapCurrentConnectionProfile. Cross-AI-session persistence lives in connections.json via default_target_id.
Also stamp the probe's run state with version info copied from the pin file (if present):
gui_version_raw, gui_major
server_release_marker, server_release_raw, system_name, client
These propagate into sap_gui_probe_run.json and the synthesized.vbs header.
Step 0.7 — Pre-flight: GUI session must be live
Run the canonical login probe (no template substitution -- it's a static
script). Aborts the probe if no session is attached.
C:/Windows/SysWOW64/cscript.exe //NoLogo "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_check_gui_login_status.vbs"
If the last line is anything other than STATUS: LOGGED_IN, stop and tell
the user to run /sap-login first. Log end with Status=FAILED ErrorClass=NO_SESSION.
Step 1 — Parse the scenario
The argument is opaque free text. You (Claude) parse it. Extract:
-
TXN code -- usually the leading token before the first colon. Heuristic:
match ^([A-Z][A-Z0-9_/]{1,19})\s*[:\-] against the trimmed scenario.
If no clean match, ask the user once for the TXN.
-
Mode flag -- if --auto appears anywhere (case-insensitive), set
MODE = auto. Otherwise MODE = confirm. Strip the flag from the
working copy of the scenario.
2b. Session flag -- if --session "<path>" or --session <path> (no
quotes) appears, capture <path> and strip the flag. Overrides the
Step 0.6 resolution and pins the probe to that session for every dump
and action call.
2c. Scenario-type flag -- if --scenario-type <type> appears, capture
<type> and strip the flag. Allowed values:
| scenario_type | Meaning |
|---|
success (default) | Happy-path probe. Abort on any status-bar E/A or unexpected popup. |
not_found | Object-doesn't-exist probe. Don't halt on entry-screen error messages — they ARE the expected end state. |
auth_error | Missing-permission probe. Don't halt on authorization-error messages. |
popup_recovery | Probe of a known recoverable popup chain (e.g. SAPLSETX master-language popup). Don't halt on unexpected wnd[1]; record signature and continue. |
validation_error | Field-validation-failure probe. Don't halt on sbar E immediately following a SET_TEXT (field rejected value). |
Stored in sap_gui_probe_run.json (state file) and exported as env var
SAPDEV_PROBE_SCENARIO_TYPE so any sub-process (action.vbs, dump.ps1)
can read it. Logged via sap_log_helper -ParamsJson '{... "scenario_type":"<type>" ...}'.
2d. Record flag -- if --record appears (optionally followed by a path to
an already-saved .vbs), set CAPTURE = record and strip the flag + path.
Otherwise CAPTURE = drive. When CAPTURE = record, skip Steps 1.5–4
entirely and go to the Mode R — Manual Record section below — record
mode does not drive the session, so session-claim, the drive loop,
synthesize, and cleanup do not apply. Steps 0 / 0.5 / Final still run.
-
Flow summary -- a one-line plan in your own words. Echo it to the
user before Step 2 so they can correct course early. Example:
Probing SE37 in confirm mode (scenario-type=success). Plan: open SE37 → enter FM
RFC_READ_TABLE → press Display → land on the FM display tabs → F3
back twice to SAP Easy Access. ~6 steps expected.
-
Step counter -- initialise N = 1. Max steps = 30 (hard cap).
If MODE = auto, emit a single line warning before Step 2:
⚠️ AUTO MODE — write actions (Save/Activate/Delete) will run without confirmation.
If scenario_type ≠ success, emit a single line warning before Step 2:
ℹ️ FAILURE-EXPECTED MODE (<type>) — the probe tolerates errors that would normally abort the run. End state will be classified in Final.
Step 1.5 — Claim the session (parallel-safety) — REQUIRED
Before doing ANY work against the resolved {SESSION_PATH}, claim the
session via the shared owner-lock helper. This prevents two parallel
probe runs from accidentally landing on the same session and corrupting
each other's state. The lock is advisory but it surfaces accidents loud
and early rather than silently after destruction.
powershell -ExecutionPolicy Bypass -File "<SKILL_DIR>\references\sap_session_owner.ps1" -Action claim -SessionPath "{SESSION_PATH}" -OwnerSkill "sap-gui-probe" -OwnerRunId "%SAPDEV_RUN_ID%" -WorkTemp "{WORK_TEMP}"
Read the last line of stdout:
CLAIMED → proceed.
DENIED: held by <skill> pid=<pid> age=<sec> → another agent has the
session locked. Abort cleanly with ABANDONED: session {SESSION_PATH} already claimed by <skill>.
Do NOT proceed — the scaffolder dispatcher should have assigned a free
session and didn't; failing fast lets the operator see the misalignment.
Best-effort release on EVERY exit path (success, failure, abandon):
powershell -ExecutionPolicy Bypass -File "<SKILL_DIR>\references\sap_session_owner.ps1" -Action release -SessionPath "{SESSION_PATH}" -WorkTemp "{WORK_TEMP}"
The lock has a 10-minute TTL — a crashed probe doesn't permanently
block its session.
Step 2 — The dump / decide / classify / confirm / act / dump loop
2.0 — Stale-state guard (parallel-safety) — REQUIRED before step 01
Before action 1, dump the current screen state and verify the session is
at SAP Easy Access. When the scaffolder is running probes in parallel,
a session may have been left mid-flow by a previous probe that crashed,
or by an unrelated agent. If the session is NOT at Easy Access, send
/n (reset to Easy Access), sleep 600 ms, dump again, and verify.
powershell -ExecutionPolicy Bypass -File "<SKILL_DIR>\references\sap_gui_probe_dump.ps1" -Mode wnd -Filter 0 -OutputFile "{RUN_FOLDER}\step_00_pre.txt" -SessionPath "{SESSION_PATH}"
Read the dump. Easy Access is identified by any of these matches
(SAP renames the transaction and renumbers the screen across releases —
match liberally, don't pick one canonical pair):
Transaction: is empty, SMEN, or SESSION_MANAGER.
Screen: is 100, 101, or 40.
Program: is SAPLSMTR_NAVIGATION (most reliable single signal — the
program code is stable across S/4HANA / ECC / BW; only the transaction
alias and screen number drift).
Also require:
- No popup window (
POPUP WINDOW wnd[1] absent).
If any check fails, send a reset action and re-dump:
echo {"verb":"SET_OKCD","value":"/n","session":"{SESSION_PATH}","note":"reset stale state"} > "{RUN_FOLDER}\step_00_reset.json"
C:/Windows/SysWOW64/cscript.exe //NoLogo "<SKILL_DIR>\references\sap_gui_probe_action.vbs" "{RUN_FOLDER}\step_00_reset.json"
powershell -ExecutionPolicy Bypass -File "<SKILL_DIR>\references\sap_gui_probe_dump.ps1" -Mode wnd -Filter 0 -OutputFile "{RUN_FOLDER}\step_00_post.txt" -SessionPath "{SESSION_PATH}"
If the second dump STILL shows non-Easy-Access state, abort with
ABANDONED: session not reachable from stale state rather than corrupting
another probe's session by issuing destructive keystrokes (Shift+F3 can
close a session entirely — that's how today's CUKY runner destroyed
ses[3]).
For each step until the scenario is complete or N > 30:
2.1 — Dump the current screen ("before")
powershell -ExecutionPolicy Bypass -File "<SKILL_DIR>\references\sap_gui_probe_dump.ps1" -Mode wnd -Filter 0 -OutputFile "{RUN_FOLDER}\step_NN_before.txt" -SessionPath "{SESSION_PATH}"
NN is the zero-padded step counter (01, 02, ...). Use -Mode wnd -Filter 0
for the main window; switch to -Mode tree (no filter) when you expect a
popup so all of wnd[1]..wnd[5] are captured too. Use -Mode id -Filter <path>
when you only need to verify one component.
After the call, Read the dump file (limit ~120 lines is usually enough)
to extract:
- Title (
MAIN WINDOW wnd[0] Title: [...])
- Program / Transaction / Screen (header lines)
- Any popup window present (
POPUP WINDOW wnd[1])
- The findById paths of the controls you need for the next action
2.2 — Decide the next action
Based on (a) the dump, (b) the original scenario, (c) the prior actions
already logged in this run, decide the single next move. Possible verbs:
| Verb | Use when | Required JSON fields |
|---|
SET_OKCD | navigate via OK-Code box (/nMM03, /n, /i) | value |
SET_TEXT | type into a normal field | target, value |
SEND_VKEY | press a function key (Enter=0, F3=3, F4=4, F8=8, Ctrl+S=11, Ctrl+F2=26, Ctrl+F3=27, Shift+F2=14) | vkey (and target=wnd[0] or the popup) |
PRESS | click a toolbar / popup button | target |
SELECT_ROW | tick a GuiTableControl row (uses getAbsoluteRow(n).Selected = True) | target, row |
DOUBLE_CLICK | double-click (tree node, ALV cell, status-bar long-msg) | target |
2.3 — Write the action JSON
Write {RUN_FOLDER}\step_NN_action.json with this exact shape (flat JSON
only -- no comments, no nested objects). Always include note -- the
synthesized.vbs uses it as the per-step comment, and the classifier in
Step 2.4 reads it for the write-keyword check on PRESS.
{
"verb": "SET_TEXT",
"target": "wnd[0]/usr/ctxtRMMG1-MATNR",
"value": "ZHKAMATVer7001",
"session": "/app/con[0]/ses[0]",
"note": "Enter material number"
}
The session field is optional. When present, sap_gui_probe_action.vbs resolves it via oApp.findById(...); when absent, the dispatcher falls back to the first connection's first session (preserves single-session behaviour). Step 2 of this skill always populates session from {SESSION_PATH} so every action runs against the pinned session.
2.4 — Classify the action
powershell -ExecutionPolicy Bypass -File "<SKILL_DIR>\references\sap_gui_probe_classify_action.ps1" -ActionPath "{RUN_FOLDER}\step_NN_action.json"
Last line on stdout is READ or WRITE. The full ruleset is in the
script header.
2.5 — Confirm if WRITE and MODE=confirm
Rule 0 first (safety_policy.md; first write-classified action of a drive run; --record
parsing never runs it): before executing the FIRST write-classified action of a drive run — in
confirm mode AND in --auto mode alike — the gate must pass once:
powershell -NoProfile -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_safety_gate.ps1" -Action assert -Skill sap-gui-probe —
SAFETY: ALLOW (0) proceed; TYPED_CONFIRM_REQUIRED (3) -> the operator types the shown
PROD <SID>/<CLIENT> token, re-run with -ConfirmationText '<their verbatim answer>', proceed only
on ALLOW_CONFIRMED; REFUSED class=<C> (1) / ERROR (2) -> STOP, end FAILED with
-ErrorClass <C>, relay the remediation lines — never bypass or work around it manually. The
per-action confirm/auto machinery below still applies after ALLOW/ALLOW_CONFIRMED.
If the classifier returned WRITE and the mode is confirm, pause and
ask the user with AskUserQuestion. Surface:
- The action JSON (rendered as a one-liner:
PRESS wnd[0]/tbar[0]/btn[11] — Save)
- The current screen identity (Program / Transaction / Screen number / Title from the dump)
- Two options:
Proceed, Abort run.
If the user picks Abort, jump to Step 4 and log end Status=ABANDONED.
If MODE=auto, skip the confirmation entirely.
2.6 — Run the action
C:/Windows/SysWOW64/cscript.exe //NoLogo "<SKILL_DIR>\references\sap_gui_probe_action.vbs" "{RUN_FOLDER}\step_NN_action.json"
Last line is DONE or ERROR: <text>. On error: read the run folder's
last *_before.txt to surface current screen state, log a diagnostic
to the user, and stop (do not auto-retry). The user picks: abort, or
edit the action JSON manually and tell you to re-run that step.
2.7 — Dump the resulting screen ("after")
Same call as Step 2.1 but with -OutputFile "{RUN_FOLDER}\step_NN_after.txt".
Use -Mode tree (no window scope) here -- a popup may have appeared.
2.8 — Compare and decide whether to continue
Read the new dump's header. Compare Program / Transaction / Screen between
before and after. Also read step_NN_post.json (sidecar emitted by
action.vbs) for the post-action sbar MessageType + popup signature.
Cases:
- Different screen -- progress; increment
N, loop.
- Same screen, no popup, sbar clean -- NOOP. The action didn't change anything.
Surface the situation to the user (action verb, target, current screen)
with AskUserQuestion:
Continue with a different action, Abort.
- Same main screen but a popup appeared -- handle the popup in the next
iteration; increment
N, loop. (You'll see POPUP WINDOW wnd[1] in the
dump.)
- Status-bar MessageType
E / A -- branch on scenario_type (see
tolerance table below). Default behaviour: abort with a diagnostic.
- Scenario complete -- exit the loop and go to Step 3.
Tolerance table (per scenario_type)
When the post-action sbar reports MessageType E/A OR an unexpected
wnd[1] popup appears, the abort decision branches on scenario_type:
| scenario_type | sbar E/A handling | Unexpected wnd[1] handling |
|---|
success | Abort with diagnostic. Today's behaviour. | Abort. |
not_found | If step ≥ 2: record and end the run as observed (the error IS the expected end state). If step 1 (still at entry screen): treat as anomaly, abort. | Record signature, attempt default-Continue dismiss, continue. |
auth_error | Record and end. Authorization errors are the expected end state regardless of step. | Record signature, dismiss, continue. |
popup_recovery | Don't auto-abort on W; surface E/A as anomaly. | Record signature and dismiss with Continue (wnd[1]/tbar[0]/btn[0]). Continue the loop — the probe was authored to drive AT LEAST one popup recovery. |
validation_error | If the previous step was a SET_TEXT and current sbar is E/W: record the field-validation message and end (the rejection IS the expected end state). Otherwise abort. | Record, dismiss, continue. |
"Record" means: write the current dump header + sbar text + popup
signature into the probe's run state file (Step 4 picks them up). Don't
prompt the user — the probe runs unattended in failure-expected mode.
If N > 30, abort: tell the user the hard cap was hit and the scenario may
need to be split; log end Status=ABANDONED.
Step 3 — Synthesize the replay VBS
powershell -ExecutionPolicy Bypass -File "<SKILL_DIR>\references\sap_gui_probe_synthesize.ps1" -RunFolder "{RUN_FOLDER}"
Output is {RUN_FOLDER}\synthesized.vbs. The script reads every
step_NN_action.json in alphanumeric order, emits one VBS line per action
with a step-delimited comment + the note, and ends with WScript.Echo "REPLAY DONE".
Step 4 — Cleanup + end-of-run summary
4a — Cleanup
Best-effort return to SAP Easy Access. Use SET_OKCD via the action dispatcher:
echo {"verb":"SET_OKCD","value":"/n","note":"return to Easy Access"} > "{RUN_FOLDER}\step_99_action.json"
C:/Windows/SysWOW64/cscript.exe //NoLogo "<SKILL_DIR>\references\sap_gui_probe_action.vbs" "{RUN_FOLDER}\step_99_action.json"
If a modal popup is open and /n doesn't work, surface that to the user
and stop -- don't keep retrying.
4b — Write end-of-run summary into run state
MANDATORY. Even on the success scenario_type, the scaffolder's merge
step REQUIRES sap_gui_probe_run.json to contain scenario_type and
observed{} — without it, the merge defaults scenario_type='success'
and the per-mode failure-modes documentation is empty. Skipping this
step silently breaks the failure-mode catalog.
Invoke the shared aggregator (no inline PowerShell — easier to keep all
probes consistent):
powershell -ExecutionPolicy Bypass -File "<SKILL_DIR>\references\sap_probe_end_of_run.ps1" `
-RunFolder "{RUN_FOLDER}" `
-ScenarioType "<scenario_type-from-Step-1>" `
-Aborted $false
(Pass -Aborted $true when the probe is exiting via abort/abandoned path.)
The helper:
- Walks every
step_NN_post.json in the run folder (excluding cleanup
step_99).
- Aggregates
final_message_type + final_sbar_text from the LAST
sidecar, distinct popups_seen[] (program, screen) pairs, and
completed_steps count.
- Merges into the existing run state file (or creates one). Idempotent
on re-run.
- Always exits 0 — never blocks the probe's own exit path.
Resulting state file shape:
{ "skill": "sap-gui-probe",
"scenario_type": "not_found",
"observed": {
"final_message_type": "E",
"final_sbar_text": "Material XYZ does not exist",
"popups_seen": [{"program":"SAPLSPO1","screen":"100"}],
"noops": [],
"completed_steps": 3,
"aborted": false } }
The probe does NOT itself decide whether observed matches the
declared scenario_type — that's the scaffolder merge step's job.
Final — Log end and report
On success:
powershell -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_log_helper.ps1" -Action end -StateFile "{RUN_FOLDER}\sap_gui_probe_run.json" -Status SUCCESS -ExitCode 0
On failure / abandon, swap -Status FAILED -ExitCode 1 -ErrorClass <CLASS> -ErrorMsg "<short>" or -Status ABANDONED -ExitCode 0. Suggested
classes: NO_SESSION, ACTION_FAILED, MAX_STEPS, USER_ABORTED.
Report to the user:
{RUN_FOLDER} path
synthesized.vbs path
- A short markdown table of every step:
# | screen-identity | verb | target/value | note
- Any notable edge cases discovered (NOOP steps, unexpected popups, write
actions that were skipped after user abort)
Mode R — Manual Record (fallback capture)
Reached only when $ARGUMENTS contains --record (Step 1, item 2d). Use this
when Claude cannot reliably drive the flow itself — an unfamiliar or write-heavy
transaction, a screen the drive loop can't navigate — or when a real operator's
exact clicks are wanted as ground truth. The operator records the flow with SAP
GUI's built-in recorder; Claude parses the saved VBS into the same
findById/action map the drive loop produces.
This mode does not drive the session (Steps 1.5–4 are skipped). It runs
Step 0 (work dir) and Step 0.5 (logging), then R1–R3 below, then Final.
Session requirement: guiding a fresh recording needs a live SAP GUI (the
operator records in their own session). Parsing an already-saved .vbs
(--record <path>) needs no session — if Step 0.7 aborted for NO_SESSION and
a valid path was supplied, ignore that abort and go straight to R2.
R1 — Have a recording already?
If --record was followed by a path, verify it and skip to R2:
powershell -Command "if (Test-Path '<VBS_PATH>') { 'EXISTS' } else { 'NOT FOUND' }"
Otherwise guide the operator (R1a), wait for the saved path, then parse.
R1a — Guide the operator to record
Present these steps, then WAIT for the operator to reply with the saved path:
- In SAP GUI, navigate to the screen where the target flow begins.
- Open the recorder: Customize Local Layout (Alt+F12) → Script Recording
and Playback… (or "More" ▸ SAP GUI settings and actions).
- In the dialog, set Save To:
{RUN_FOLDER}\recording.vbs, set
Encoding: Unicode, and press the red Record button.
- Perform the flow — every field, button, tab, menu, and key is captured.
- Press the orange Stop button.
- Reply with the path (or confirm
{RUN_FOLDER}\recording.vbs).
R2 — Parse the recorded VBS
Read the saved .vbs. Skip the boilerplate header (the first ~14 lines of
GetObject("SAPGUI") / application.Children(0) / WScript.ConnectObject
setup) down to the first session.findById(...) line. For every
session.findById("…") line capture:
- Component ID — the string inside
findById("…")
- Action — the method/property after the
) (.text = "…", .press,
.select, .sendVKey N, .doubleClick, …)
- Value — the assigned text, or the VKey number
Decode each ID's type and each VKey using
<SAP_DEV_CORE_SHARED_DIR>/rules/sap_gui_scripting_reference.md (component-type
prefix table + VKey table). Ignore .caretPosition and .setFocus lines —
cursor positioning, not meaningful actions.
Language-independence scan (REQUIRED). While parsing, flag every
.Text = "…" read-branch, .Tooltip, window-title, or menu-label comparison
as a defect per language_independence_rules.md — a recorded VBS captured under
EN logon must identify controls by ID + DDIC field name and check the status bar
via MessageType, or it silently breaks on JA/DE/ZH logons. Report these so the
skill author fixes them before shipping.
R3 — Emit the deliverables
The recorded .vbs already IS a replayable script, so no synthesize step is
needed. Instead:
- Copy/normalize the recording to
{RUN_FOLDER}\synthesized.vbs — the same
deliverable name the drive loop produces, so /sap-gui-skill-scaffold
consumes either capture mode uniformly.
- Write the run-state summary via the Step 4b aggregator contract — at minimum
{ "skill":"sap-gui-probe", "capture":"record", "completed_steps":<n>, "observed":{…} } — so downstream tooling sees a record-mode run identically
to a drive-mode run.
- Present the findById/action summary table
(
# | Component ID | Type | Action | Value) plus any language-independence
defects found in R2.
Then continue to Final (log end + report).
Recipes
SE37 display-FM probe (read-only, 6 steps):
/sap-gui-probe "SE37: display FM RFC_READ_TABLE; then F3 back to Easy Access"
MM03 view probe (read-only, 8 steps, demonstrates table-row tick):
/sap-gui-probe "MM03: display material ZHKAMATVer7001 Basic Data 1 view; F3 back to Easy Access"
SE38 activate probe (one write action, confirm-mode pauses once):
/sap-gui-probe "SE38: open program ZSANDBOX, run syntax check (Ctrl+F2), then Activate (Ctrl+F3), then exit"
Same flow without prompts (auto mode):
/sap-gui-probe "SE38: open ZSANDBOX, syntax check, activate, exit --auto"
Edge cases and gotchas
-
GuiTableControl row checkboxes are virtual. findById("...chkMSICHTAUSW-KZSEL[1,0]") fails on MM03's View Selection in S/4HANA -- the row's Selected property is reachable only via getAbsoluteRow(<n>).Selected = True. The SELECT_ROW verb does this; do not try to emit a findById for a row checkbox in SET_TEXT.
-
Popups break wnd[0] dumps. When you expect a popup (e.g. after pressing Display in SE38 with no source loaded, after Save), dump with -Mode tree (no window scope) so wnd[1] is captured. Use oSession.ActiveWindow.Id in your reasoning -- if a popup is open, the next action must address wnd[1]/..., not wnd[0]/....
-
AbapEditor swallows status-bar messages. SE38 / SE37 source editors don't update wnd[0]/sbar after Ctrl+F2 / Ctrl+F3 -- the message goes into wnd[0]/shellcont/shell/shellcont[1]/shell instead. For probe runs this means you can't classify success/failure from sbar alone; the dump after the action will tell you (look for the error grid).
-
Original-language popup on SAPLSETX. First write action on an object whose master language differs from logon language triggers a "Continue / Don't Save" popup. Your next dump will show POPUP WINDOW wnd[1] titled "Information"; press wnd[1]/tbar[0]/btn[0] (Continue) and proceed.
-
NOOP can mean the field rejected the value. If before/after dumps are identical, the most common cause is a field-level validation error left in the status bar. Read wnd[0]/sbar in the after-dump.
-
--auto is not a substitute for the safety classifier. Even in auto mode, the classifier still runs and writes _class annotation to the action JSON. Use that annotation in your end-of-run report to surface every WRITE action that ran, so the user can audit.