| name | sap-sp02 |
| description | Downloads a SAP spool request to a local text file via transaction
SP02 (Output Controller — own spool requests). Drives the GUI in
three steps: locate the spool by number on the SP02 list (matched
across all columns, so the driver adapts to whatever column the
layout uses), open its contents via the Display-contents button,
then Save to local file — picking the export format radio (default
Unconverted = plain text) and entering the target path/filename.
Works for any list-style spool that SP02 can render (executable
reports, ALV grids printed to spool, etc.). Format defaults to
Unconverted plain text but accepts Spreadsheet / Rich text / HTML
via the FORMAT_INDEX argument.
Prerequisites: Active SAP GUI session (use /sap-login first), and
the target spool must belong to the logged-in user (or appear in
the user's default SP02 selection).
|
| argument-hint | <SPOOL_NUMBER> <OUTPUT_PATH> [--format=text|csv|rtf|html] [--row=<N>] |
SAP SP02 — Spool Download Skill
You download a SAP spool request to a local text file using SP02 GUI
scripting. The spool number is the SAP-assigned TSP01-RQIDENT (e.g.
397) — the same number ABAP reports print to the joblog as
"Spool request created."
Task: $ARGUMENTS
Shared Resources
| File | Token | Purpose |
|---|
<SAP_DEV_CORE_SHARED_DIR>/rules/skill_operating_rules.md | (rule) | Mandatory operating rules |
<SAP_DEV_CORE_SHARED_DIR>/rules/language_independence_rules.md | (rule) | 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 |
<SKILL_DIR>/references/sap_sp02_download.vbs | many | SP02 driver: list scan + F2 + tbar[1]/btn[48] + format radio + DY_PATH/DY_FILENAME |
<SAP_DEV_CORE_SHARED_DIR>/rules/sap_gui_security_handling.md | (rule) | SAP GUI Security dialog handling — the spool download to a local file (Step 3, via DY_PATH/DY_FILENAME) is SAP-GUI-side file IO, so it can raise the modal "SAP GUI Security" dialog (which suspends the Scripting API and hangs cscript). Pre-check + OS-level watcher wrap that download. |
<SAP_DEV_CORE_SHARED_DIR>/scripts/sap_gui_security_precheck.ps1 | (script) | Read-only allow-list pre-check (saprules.xml) — ALLOWED (exit 0) / NOT_COVERED (exit 1). Used by Step 3 before the spool download. |
<SAP_DEV_CORE_SHARED_DIR>/scripts/sap_gui_security_sidecar.ps1 | (script) | OS-level (Win32) watcher that auto-dismisses the SAP GUI Security dialog (ticks Remember + clicks Allow). Launched as a background process before the Step 3 download. |
Step 0 — Resolve Work Directory
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. Resolve sap-dev-core paths: 2 levels up from <SKILL_DIR> to the plugin root, then settings.json and (if present) settings.local.json.
| Setting | Default if blank |
|---|
work_dir | C:\sap_dev_work |
Set {WORK_TEMP} = {work_dir}\temp. Ensure it exists:
cmd /c if not exist "{WORK_TEMP}" mkdir "{WORK_TEMP}"
Set {RUN_TEMP} = the per-run scratch dir (Get-SapRunTemp mints + creates {work_dir}\temp\run_<id>):
powershell -NoProfile -ExecutionPolicy Bypass -Command ". '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_connection_lib.ps1'; Write-Output ('RUN_TEMP=' + (Get-SapRunTemp))"
Per the CLAUDE.md "Two-bucket temp model" write this skill's generated scratch (*_run.ps1 / *_run.vbs and the _run.json state) under {RUN_TEMP}; keep {WORK_TEMP} (base) only for Get-SapCurrentSessionPath -WorkTemp.
Step 0.5 — Start Logging
powershell -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_log_helper.ps1" -Action start -StateFile "{RUN_TEMP}\sap_sp02_run.json" -Skill sap-sp02 -ParamsJson "{\"spool\":\"<SPOOL_NUMBER>\"}"
State file: {RUN_TEMP}\sap_sp02_run.json. Best-effort.
Step 1 — Parse Arguments
| Arg | Required | Notes |
|---|
SPOOL_NUMBER | yes (or --row=<N>) | SAP spool request number, numeric, e.g. 397. Matches TSP01-RQIDENT. |
OUTPUT_PATH | yes | Full local path, e.g. C:\Temp\SP02_397.txt. The skill splits this into OUTPUT_DIR (parent + trailing \) and OUTPUT_FILE (basename). |
--format=<fmt> | no | text (default = Unconverted, index 0), csv (Spreadsheet, index 1), rtf (Rich text, index 2), html (HTML, index 3). The exact mapping depends on SAP GUI version — verify with a one-time recording on a new release. |
--row=<N> | no | Bypass the label match and tick row <N> directly (0-based on the user-area). Use when you already know the row from a probe. |
--col=<N> | no | Restrict the spool-number match to one column index. Default: match the number in any column — the driver adapts to whatever column the layout uses (col 3 on S/4HANA 400, col 4 elsewhere). Set only to disambiguate a list where another cell could equal the number. |
Validation:
SPOOL_NUMBER: numeric, no leading zeros (the VBS list-scan trims
whitespace before comparing).
OUTPUT_PATH: parent directory must exist on the local machine. If it
doesn't, create it with cmd /c mkdir <dir> first.
OUTPUT_FILE extension: .txt for --format=text, .csv for
--format=csv, etc. The VBS does NOT enforce a match; SAP writes
whatever extension you supply.
If neither SPOOL_NUMBER nor --row is supplied, ask the user.
Step 2 — Ensure SAP GUI Session
Run /sap-login if no session is active. The skill never recreates the
session; it only drives the existing one.
Step 3 — Generate and Run the VBS
Split OUTPUT_PATH into OUTPUT_DIR (must end with \) and
OUTPUT_FILE. Map --format=<fmt> to FORMAT_INDEX (text→0,
csv→1, rtf→2, html→3).
Write {RUN_TEMP}\sap_sp02_download_run.ps1:
$skillDir = '<SKILL_DIR>'
$content = [System.IO.File]::ReadAllText("$skillDir\references\sap_sp02_download.vbs", [System.Text.Encoding]::UTF8)
$content = $content.Replace('%%SPOOL_NUMBER%%', 'THE_SPOOL_NUMBER')
$content = $content.Replace('%%ROW_INDEX%%', 'THE_ROW_INDEX') # empty if auto-scan
$content = $content.Replace('%%SPOOL_NUM_COL%%', 'THE_SPOOL_NUM_COL') # empty = match number in any column (recommended)
$content = $content.Replace('%%FORMAT_INDEX%%', 'THE_FORMAT_INDEX') # empty for default 0
$content = $content.Replace('%%OUTPUT_DIR%%', 'THE_OUTPUT_DIR') # MUST end with '\'
$content = $content.Replace('%%OUTPUT_FILE%%', 'THE_OUTPUT_FILE')
# Session-attach plumbing (Phase 3.5 multi-connection aware). Resolution:
# explicit --session > SAPDEV_SESSION_PATH > sole-
# connection auto-default > refuse. See sap_attach_lib.vbs for details.
$sessionPath = '' # set to the parsed --session value if supplied
$content = $content.Replace('%%SESSION_PATH%%', $sessionPath)
$content = $content.Replace('%%ATTACH_LIB_VBS%%', '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_attach_lib.vbs')
. '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_connection_lib.ps1'
$env:SAPDEV_SESSION_PATH = Get-SapCurrentSessionPath -WorkTemp '{WORK_TEMP}'
[System.IO.File]::WriteAllText('{RUN_TEMP}\sap_sp02_download_run.vbs', $content, [System.Text.UnicodeEncoding]::new($false, $true))
Write-Host 'Done'
Run the generator:
powershell -ExecutionPolicy Bypass -File "{RUN_TEMP}\sap_sp02_download_run.ps1"
Execute (with SAP GUI Security guard)
The spool download writes the rendered list to a local file via SP02's
DY_PATH / DY_FILENAME save dialog — SAP-GUI-side file IO, so it raises
the modal SAP GUI Security dialog when the output path isn't allow-listed
(Default Action = Ask), and that modal suspends the Scripting API, hanging the
cscript. Per shared/rules/sap_gui_security_handling.md, pre-check the rules and
run the OS-level watcher around the download. Run as one PowerShell block (the
32-bit cscript is inside it). Substitute THE_OUTPUT_PATH with the full output
path (OUTPUT_DIR + OUTPUT_FILE) and THE_SID / THE_CLIENT with the pinned
system / client:
$shared = '<SAP_DEV_CORE_SHARED_DIR>\scripts'
$out = 'THE_OUTPUT_PATH' # the local path SAP GUI will write (OUTPUT_DIR + OUTPUT_FILE)
# 1. Pre-check the allow-list (read-only; informational + lets us skip the watcher).
& "$shared\sap_gui_security_precheck.ps1" -Path $out -Access w -System 'THE_SID' -Client 'THE_CLIENT' -Transaction 'SP02' | Out-Host
$allowed = ($LASTEXITCODE -eq 0)
# 2. If not already allow-listed, launch the OS-level watcher BEFORE the
# (blocking) download. It detects the #32770 dialog and clicks Remember+Allow,
# which also persists a rule so subsequent runs pre-check ALLOWED.
$watcher = $null
if (-not $allowed) {
$watcher = Start-Process powershell -PassThru -WindowStyle Hidden -ArgumentList @(
'-NoProfile','-ExecutionPolicy','Bypass','-File',"$shared\sap_gui_security_sidecar.ps1",'-TimeoutSeconds','40')
Start-Sleep -Milliseconds 800
}
# 3. Run the spool download (32-bit cscript). If the dialog appears it blocks
# here until the watcher dismisses it; then the download completes.
& 'C:/Windows/SysWOW64/cscript.exe' //NoLogo '{RUN_TEMP}\sap_sp02_download_run.vbs'
# 4. Reap the watcher.
if ($watcher) { $watcher | Wait-Process -Timeout 45 -ErrorAction SilentlyContinue }
Step 4 — Interpret the Output
| Last line | Meaning |
|---|
SUCCESS: Spool <NUM> written to <PATH>. | Spool downloaded. The script also echoes INFO: File written: <path> (<bytes> bytes) for verification. |
ERROR: Spool <NUM> not found among <N> list labels … | No list label matched the spool number. Most common causes: the spool belongs to another user, it's scrolled below the visible page, it's filtered out of the SP02 selection, or the number was mistyped. See Troubleshooting below. |
ERROR: Could not press Save (<id>): … | The spool CONTENT wasn't displayed. The Save-to-local button (icon B_DOWN) exists only on the content-display screen, not the list — so this usually means the Display-contents step didn't navigate. Check the status bar and re-probe with /sap-gui-inspect. |
ERROR: Save dialog completed but file is not on disk | Path was rejected silently (permissions / locked file / wrong DY_PATH separator). Check the path and retry. |
Other ERROR: … | Surface verbatim and consult Step 7. |
Step 5 — Report
Tell the user:
- Spool number and the local path the file ended up at
- File size (echoed by the VBS as
INFO: File written: <path> (<bytes> bytes))
- Format used (Unconverted / Spreadsheet / RTF / HTML)
- The SAP status bar message after save (echoed as
INFO: SAP status: [<TYPE>] <TEXT>)
If the operator wants to inspect the contents, the file is plain text
(for --format=text) and can be Readd directly.
Step 6 — Clean Up
cmd /c del {RUN_TEMP}\sap_sp02_download_run.vbs & del {RUN_TEMP}\sap_sp02_download_run.ps1
Final — Log End
powershell -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_log_helper.ps1" -Action end -StateFile "{RUN_TEMP}\sap_sp02_run.json" -Status SUCCESS -ExitCode 0
On failure, substitute <CLASS> and a short message:
powershell -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_log_helper.ps1" -Action end -StateFile "{RUN_TEMP}\sap_sp02_run.json" -Status FAILED -ExitCode 1 -ErrorClass <CLASS> -ErrorMsg "<short>"
Suggested <CLASS>: SP02_NOT_FOUND, SP02_DOWNLOAD_FAILED, GUI_TIMEOUT.
Step 7 — Troubleshooting
When the auto-scan fails, the right next move is almost always
/sap-gui-inspect screenshot full (visual PNG + structural dump) to
inspect the live SP02 list:
| Symptom | Diagnose with | Fix |
|---|
Spool <NUM> not found but the operator sees it on screen | /sap-gui-inspect mode type filter GuiLabel — confirm the number is present and note its row | The driver already matches across all columns; if the spool is off-screen, maximize/scroll the list, else re-run with --row=<N> |
| Located the row, but Save fails on the content screen | /sap-gui-inspect on the content screen — list tbar[1] buttons + icon names | The Display-contents (B_DISP) or Save-to-local (B_DOWN) icon differs on this release; extend the icon match in the VBS |
| Spool belongs to a different user | Check SP02's selection screen | Either change User filter on the SP02 selection screen (Settings…) or run as the spool's owner |
| Display contents opens an empty window | Spool is binary (PDF/postscript) — Unconverted format won't help | Use --format=html or --format=csv if the report supports those, or download via SP01 binary path |
| Save button resolves to the wrong control | Release renumbered the toolbar | The driver locates Save by icon B_DOWN (not a fixed btn index) and falls back to btn[48]; if a release uses a different icon, re-probe and extend FindBtnByIcon |
| File-save dialog doesn't appear | A "List has been completely displayed" popup may be on top | Add an Enter dismiss in the popup-handling block; this can also be handled by running /sap-gui-inspect screenshot full between steps |
If any GUI step fails with "control could not be found by id", invoke
/sap-gui-inspect screenshot full first — it captures the live screen as a PNG
plus the structural component dump for the topmost window.
Component IDs (for reference)
| Element | ID |
|---|
| OK code | wnd[0]/tbar[0]/okcd |
| SP02 list checkbox (selection column, row N) | wnd[0]/usr/chk[1,N] (col 1; fallback scans the row for any GuiCheckBox) |
| SP02 list spool number (column varies) | wnd[0]/usr/lbl[<col>,N] — col 3 on S/4HANA 400, 4 elsewhere. Located by matching the number across all labels, not a fixed column. |
| Display contents (reach the content screen) | tbar[1] button with icon B_DISP (F6 / btn[6] on S/4HANA 400); sendVKey 2 (F2) fallback |
| Save to local file (content screen only) | tbar[1] button with icon B_DOWN (btn[48] on the tested release) |
| Format-selection radio | wnd[1]/usr/subSUBSCREEN_STEPLOOP:SAPLSPO5:0150/sub:SAPLSPO5:0150/radSPOPLI-SELFLAG[1,<idx>] |
| Format-selection Continue | wnd[1]/tbar[0]/btn[0] |
| Save dialog directory | wnd[1]/usr/ctxtDY_PATH |
| Save dialog filename | wnd[1]/usr/ctxtDY_FILENAME |
| Save dialog confirm | sendVKey 0 on wnd[1] |
| Status bar | wnd[0]/sbar |
The 0150 subscreen number on the format popup is from S/4HANA 1909.
On older / newer SAP GUI builds it can shift by one or two — re-record
if the radio path fails.
Limitations
- Own-user spools only. SP02 by default lists only the logged-in
user's spool requests. To download another user's spool, switch to
SP01 first or change SP02's selection criteria; this skill does not
drive the selection-screen flow.
- List-style spools only. Binary spools (PDF / OTF / PS) are not
meaningfully convertible via the Unconverted radio. Use the
appropriate format index, or download via SP01 binary path.
- No CHK[col,row] race-protection. The VBS does not re-verify the
spool number after ticking the checkbox — if the SP02 list re-ordered
between the scan and the tick (rare, but possible if the user has
background spool generation running), the wrong spool may be selected.
Hash the row's spool number before F2 if this matters in your context.
- Format index mapping is SAP-GUI-version-specific. The skill
documents
text/csv/rtf/html as 0/1/2/3, which holds on
S/4HANA 1909. On other releases verify with a one-time recording.