abapgit-agent 1.17.8 → 1.17.9

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/README.md CHANGED
@@ -68,6 +68,7 @@ abapgit-agent syntax --files src/zcl_my_class.clas.abap # Check syntax before
68
68
  abapgit-agent inspect --files src/zcl_my_class.clas.abap # Code Inspector after pull
69
69
  abapgit-agent unit --files src/zcl_my_test.clas.testclasses.abap # Run AUnit tests
70
70
  abapgit-agent run --class ZCL_MY_RUNNER # Execute class headlessly
71
+ abapgit-agent run --program ZMY_PROGRAM # Execute report/program headlessly
71
72
  ```
72
73
 
73
74
  ### Explore
package/abap/CLAUDE.md CHANGED
@@ -243,7 +243,7 @@ When a class needs local helper classes or test doubles, create separate include
243
243
  ```
244
244
  Adding .clas.testclasses.abap to an existing class?
245
245
  └── Update the .clas.xml → set WITH_UNIT_TESTS flag:
246
- <clas:abapClassProperties ... abpUnitTestable="true" ... />
246
+ <WITH_UNIT_TESTS>X</WITH_UNIT_TESTS> (inside the <VSEOCLASS> block)
247
247
  WITHOUT this flag, abapGit will not push/activate the test include.
248
248
 
249
249
  Adding .clas.locals_def.abap (local type definitions)?
@@ -424,6 +424,10 @@ abapgit-agent unit --files src/zcl_test1.clas.testclasses.abap,src/zcl_test2.cla
424
424
 
425
425
  After activating a class, stop and tell the user: `"Class is activated. Run with: abapgit-agent run --class ZCL_MY_CLASS"`
426
426
 
427
+ For ABAP programs (PROG type), use `--program` instead: `abapgit-agent run --program ZR_MY_REPORT`
428
+
429
+ For how to write a runner class (`out->write()` usage, output format): `abapgit-agent ref --topic run-probe-classes`
430
+
427
431
  ---
428
432
 
429
433
  ### 10. Never Run `drop` Command Without Explicit Permission
@@ -559,12 +563,21 @@ Minimal correct sequence:
559
563
  ```bash
560
564
  abapgit-agent view --objects ZCL_FOO --full --lines # 0. get exact line number from output hint
561
565
  abapgit-agent debug set --objects ZCL_FOO:42 # 1. set breakpoint (use line from step 0)
566
+ # ⚠️ Avoid: LOOP AT ... ASSIGNING headers — ADT registers them but ABAP runtime skips them
567
+ # Set BP on the first statement INSIDE the loop body instead
562
568
  abapgit-agent debug attach --json > /tmp/a.json 2>&1 & # 2. attach (background)
563
569
  until grep -q "Listener active" /tmp/a.json 2>/dev/null; do sleep 0.3; done
564
- abapgit-agent unit --files src/zcl_foo.clas.testclasses.abap > /tmp/t.json 2>&1 & # 3. trigger
565
- # poll for session, then inspect
570
+ abapgit-agent unit --files src/zcl_foo.clas.testclasses.abap > /tmp/t.json 2>&1 & # 3. trigger (background &)
571
+ # 4. POLL until session appears — do NOT call vars/stack before this completes
572
+ SESSION=""
573
+ for i in $(seq 1 30); do
574
+ sleep 0.5
575
+ SESSION=$(grep -o '"session":"[^"]*"' /tmp/a.json 2>/dev/null | head -1 | cut -d'"' -f4)
576
+ [ -n "$SESSION" ] && break
577
+ done
578
+ # 5. inspect (session is now live)
566
579
  abapgit-agent debug vars --json
567
- abapgit-agent debug step --type continue --json # 4. release
580
+ abapgit-agent debug step --type continue --json # 6. release
568
581
  ```
569
582
 
570
583
  → `abapgit-agent ref --topic debug-session`
@@ -666,10 +679,8 @@ git checkout -b feature/my-change
666
679
  abapgit-agent syntax --files src/<name>.clas.abap
667
680
  ls .abaplint.json 2>/dev/null && abapgit-agent lint # abaplint (if configured)
668
681
  git add . && git commit -m "feat: description"
669
- git push origin feature/my-change
670
682
  git fetch origin main && git rebase origin/main
671
- git push origin feature/my-change --force-with-lease
672
- abapgit-agent pull --files src/<name>.clas.abap --sync-xml
683
+ abapgit-agent pull --files src/<name>.clas.abap --sync-xml # --sync-xml amends + pushes internally
673
684
  ```
674
685
 
675
686
  → `abapgit-agent ref --topic branch-workflow`
@@ -685,12 +696,80 @@ git pull origin main
685
696
  abapgit-agent syntax --files src/<name>.clas.abap
686
697
  ls .abaplint.json 2>/dev/null && abapgit-agent lint # abaplint (if configured)
687
698
  git add . && git commit -m "feat: description"
688
- git push origin main
689
- abapgit-agent pull --files src/<name>.clas.abap --sync-xml
699
+ abapgit-agent pull --files src/<name>.clas.abap --sync-xml # --sync-xml amends + pushes internally
690
700
  ```
691
701
 
702
+ <!-- AI-CONDENSED-START -->
703
+ # AI Agent Instructions
704
+
705
+ ## Step 1: Read project config before doing anything
706
+
707
+ - Read `.abapGitAgent` → get `folder` (ABAP source folder) and `workflow.mode`
708
+ - Read `guidelines/objects.local.md` → naming conventions for this project (if file exists)
709
+ - Read `.abapgit-agent.json` → safeguards, conflict detection, inspect variant
710
+
711
+ ---
712
+
692
713
  ### AI Tool Guidelines
693
714
 
715
+ **When working on an unfamiliar ABAP topic, syntax, or pattern:**
716
+ 1. ✗ Never guess or assume ABAP syntax — it is strict and errors waste time
717
+ 2. ✓ Search the Guidelines Index below and fetch the relevant `ref --topic` before writing code
718
+ 3. ✓ For unknown classes or methods: search local git repo first, then `abapgit-agent ref`, then `abapgit-agent view --objects <CLASS>` to query the ABAP system
719
+
720
+ **Before modifying any existing ABAP object:**
721
+ 1. ✓ Always read the current file(s) before editing — never overwrite blindly
722
+ 2. ✓ For a class: read the `.clas.abap` file; also read `.clas.locals_def.abap`, `.clas.locals_imp.abap`, `.clas.testclasses.abap` if they exist and are relevant
723
+
724
+ **When `abapgit-agent syntax` fails:**
725
+ 1. ✓ Fix the error locally and re-run syntax — do NOT commit or proceed
726
+ 2. ✗ Never commit a file that failed syntax check
727
+ 3. ✓ Only proceed to `[abaplint] → commit` once syntax passes
728
+
729
+ **When creating a new ABAP object:**
730
+ 1. ✓ Customer namespace (Z*/Y* name AND Z*/Y*/$* package) → write files directly, no confirmation needed
731
+ 2. ✓ SAP namespace or SAP-delivered package → show creation summary and wait for explicit user confirmation before writing any files
732
+ 3. ✓ Summary format: object name, type, package, files to be created
733
+ 4. ✗ Never pick a package yourself by running `abapgit-agent tree` — determine from `objects.local.md` or `.abapGitAgent`, or ask the user
734
+ 5. ✓ Before naming anything: check naming length limits (`ref --topic naming-limits`) — TABL field names max 16 chars, most others 30 chars
735
+
736
+ **When writing unit tests for a class that reads a CDS view:**
737
+ 1. ✓ Always use `CL_CDS_TEST_ENVIRONMENT` to provide test data — do NOT mock the database layer manually
738
+ 2. ✗ Never insert test data directly into database tables for CDS view testing
739
+ 3. ✓ For full setup pattern: `abapgit-agent ref --topic cds-testing`
740
+
741
+ **When writing unit tests (mocking dependencies):**
742
+ 1. ✓ Default to ABAP Test Double Framework (`cl_abap_testdouble=>create` / `configure_call`) — covers 90%+ of cases
743
+ 2. ✓ Use a manual test double class (`ltd_mock_xxx FOR TESTING`) only when stateful behaviour is needed (call counting, varying results per call)
744
+ 3. ✗ Never write a manual mock class when the framework can do it
745
+ 4. ✓ For full API reference and class design rules: `abapgit-agent ref --topic unit-testable-code`
746
+
747
+ **When adding local helper or test include files to a class:**
748
+ 1. ✓ `clas.testclasses.abap` requires `<WITH_UNIT_TESTS>X</WITH_UNIT_TESTS>` in the `.clas.xml` `<VSEOCLASS>` block — without it abapGit will not activate the test include
749
+ 2. ✓ `clas.locals_def.abap` requires `<CLSCCINCL>X</CLSCCINCL>` in the `.clas.xml`
750
+ 3. ✓ For exact XML flag placement: `abapgit-agent ref --topic object-creation`
751
+ 4. ✓ Test method names (`FOR TESTING`) are subject to the 30-char method name limit — count before writing (e.g. `test_returns_data_sorted_by_revenue` = 35 chars → invalid)
752
+
753
+ **When `abapgit-agent run` is relevant:**
754
+ 1. ✗ Never call `abapgit-agent run` unless the user explicitly asks — a `run` command present in the user's prompt counts as explicit authorization; no confirmation step needed
755
+ 2. ✓ After activating a class implementing `IF_OO_ADT_CLASSRUN`: stop and tell the user how to run it manually
756
+ 3. ✓ Before calling `run`: verify the object type — `--class` for CLAS, `--program` for PROG. Check the file extension (`.clas.abap` vs `.prog.abap`) if unsure
757
+ 4. ✓ For full guidelines: `abapgit-agent ref --topic run-probe-classes`
758
+
759
+ **When `abapgit-agent drop` is relevant:**
760
+ 1. ✗ Never call `abapgit-agent drop` unless the user explicitly confirms — it physically deletes the object from ABAP, irreversible without re-pull
761
+ 2. ✓ When an object is in a broken state (stale include, activation permanently blocked): suggest `drop --pull` and wait for confirmation
762
+ 3. ✓ Supported types for drop: CLAS, INTF, PROG, TABL, TTYP only — DTEL is not supported
763
+
764
+ **When using `abapgit-agent debug` commands:**
765
+ 1. ✓ Always use `--json` for ALL debug commands (`attach`, `vars`, `stack`, `step`) — human output is not machine-parseable
766
+ 2. ✗ Never run `abapgit-agent debug attach` without `--json` and background redirect (`> /tmp/a.json 2>&1 &`)
767
+ 3. ✓ Always attach BEFORE triggering — wait for `"Listener active"` in output before firing the trigger
768
+ 4. ✓ **Run the trigger in background (`&`)** — `unit` or `run --class`, never foreground
769
+ 5. ✓ **Poll attach.json for `"session":"..."` BEFORE calling `vars`/`stack`** — do NOT call vars/stack immediately after the trigger; the breakpoint has not fired yet. Loop until the session ID appears.
770
+ 6. ✓ Always finish a debug session with `abapgit-agent debug step --type continue --json` to release the frozen work process
771
+ 7. ✓ For full debug session guide (complete poll loop, stale session diagnosis): `abapgit-agent ref --topic debug-session`
772
+
694
773
  **Read `.abapGitAgent` to determine workflow mode:**
695
774
 
696
775
  **When `workflow.mode = "branch"`:**
@@ -700,7 +779,8 @@ abapgit-agent pull --files src/<name>.clas.abap --sync-xml
700
779
  4. ✓ Use `--force-with-lease` after rebase (never `--force`)
701
780
  5. ✓ Create PR with squash merge when feature complete
702
781
  6. ✗ Never commit directly to default branch
703
- 7. ✗ Never use `git push --force` (always use `--force-with-lease`)
782
+ 7. ✗ Never use `git push --force` (always use `--force-with-lease`) — `pull --sync-xml` handles its own push internally; no manual push needed after it
783
+ 8. ✓ **Push the feature branch to remote before the first `abapgit-agent pull`** — the ABAP system reads from the remote URL; a branch that only exists locally is invisible to it. If `git push` fails with "no upstream branch", use `git push --set-upstream origin <branch>` first.
704
784
 
705
785
  **When `workflow.mode = "trunk"` or not set:**
706
786
  1. ✓ Commit directly to default branch
@@ -716,7 +796,8 @@ abapgit-agent pull --files src/<name>.clas.abap --sync-xml
716
796
  3. ✗ Don't suggest or run a full pull without specifying files
717
797
 
718
798
  **When `safeguards.requireFilesForPull = false` or not set:**
719
- 1. ✓ `--files` is optional use it for speed, omit for full pull
799
+ 1. ✓ Always use `--files` to pull only the files you changed faster and safer
800
+ 2. ✓ Full pull (without `--files`) is only acceptable when you explicitly need to activate all objects (e.g. initial repo setup)
720
801
 
721
802
  **When `safeguards.disablePull = true`:**
722
803
  1. ✗ Do not run `abapgit-agent pull` at all
@@ -746,8 +827,12 @@ abapgit-agent pull --files src/<name>.clas.abap --sync-xml
746
827
 
747
828
  **When `conflictDetection.mode = "abort"`:**
748
829
  1. ✓ Conflict detection is active — pull aborts if ABAP system was edited since last pull
749
- 2. ✓ If pull is aborted with conflict error, inform user and suggest `--conflict-mode ignore` to override for that run
750
- 3. ✗ Don't silently add `--conflict-mode ignore` always tell the user about the conflict
830
+ 2. ✓ If pull is aborted with conflict error: **stop, inform the user about the conflict, and wait for their decision** — do NOT automatically retry with `--conflict-mode ignore`
831
+ 3. ✗ Don't add `--conflict-mode ignore` unless the user explicitly asks you to override the conflict
832
+ 4. ✓ You may mention `--conflict-mode ignore` as an option the user can choose, but never run it on your own
833
+ 5. ✓ **Distinguishing real vs. activation-noise conflicts (`SYSTEM_EDIT`):**
834
+ - **Activation noise** (safe to inform user it can be ignored): `System changed by` is *your own user*, timestamp within seconds/minutes of your last pull — the ABAP serializer normalised the object hash after activation. Tell the user: "This is activation noise — safe to re-run with `--conflict-mode ignore`."
835
+ - **Real conflict**: `System changed by` is a *different user*, or the timestamp is not recent. Stop and wait for user decision.
751
836
 
752
837
  **When `transports.allowCreate = false`:**
753
838
  1. ✗ Do not run `abapgit-agent transport create`
@@ -763,42 +848,82 @@ abapgit-agent pull --files src/<name>.clas.abap --sync-xml
763
848
  1. ✗ Do not run `abapgit-agent transport release`
764
849
  2. ✓ Inform the user that transport release is disabled for this project
765
850
 
766
- **After every pull that creates or modifies ABAP objects:**
767
- 1. ✓ Always pass `--sync-xml` rewrites any XML metadata files that differ from the ABAP serializer output, amends the commit, and re-pulls so git and the ABAP system stay in sync
768
- 2. ✓ If pull output shows `⚠️ X XML file(s) differ from serializer output`, re-run immediately with `--sync-xml`
851
+ **Before every `git commit` (when `.abaplint.json` exists AND changed files include `.abap` or `.xml` ABAP source files):**
852
+ 1. ✓ Run `abapgit-agent lint` before committing no exceptions for ABAP file type or workflow mode (trunk or branch)
853
+ 2. ✓ Run lint even when syntax is skipped (dependent files, XML-only types, DCLS, ENHO)
854
+ 3. ✓ Correct pre-commit sequence: syntax (if applicable for CLAS/INTF/PROG/DDLS/FUGR) → `abapgit-agent lint` → `git commit`
855
+ 4. ✗ Never commit ABAP files without running lint first when `.abaplint.json` exists
856
+ 5. ✓ Non-ABAP changes only (docs, config, JS): skip lint and syntax — they do not apply
857
+ 6. ✓ Before applying any abaplint quickfix, run `abapgit-agent ref --topic abaplint` first — the `prefer_inline` fix has a known silent type truncation bug
858
+
859
+ **Before running `abapgit-agent pull`:**
860
+ 1. ✓ Always verify that ALL changed files are committed — `abapGit reads from git, not from local disk`
861
+ 2. ✗ Never run `pull` if there are uncommitted changes (`git status` shows modified/untracked files that should be pulled)
862
+ 3. ✓ In branch workflow: verify all commits are pushed — run `git log origin/<branch>..HEAD`. If this shows any commits, push first with `git push` (or `git push --set-upstream origin <branch>` if no upstream yet).
863
+ 4. ✓ With `--sync-xml`: `git add` → `git commit` → `git push` → `abapgit-agent pull --sync-xml` — push first so the ABAP system can read your changes; `--sync-xml` will then amend and re-push automatically if XML diffs are found after activation
864
+ 5. ✓ Without `--sync-xml`: `git add` → `git commit` → `git push` → `abapgit-agent pull` — same flow, push manually
865
+
866
+ **If pull returns `SUCCESS` with `ACTIVATED_COUNT: 0` and no errors:**
867
+ - ✓ **`LOCAL_XML_FILES` only contains `package.devc.xml`:** The branch does not exist on the remote. Fix: `git push --set-upstream origin <branch>`, then re-run the pull.
868
+ - ✓ **`LOCAL_XML_FILES` is empty:** Two possible causes:
869
+ - (a) Objects are already at the latest version — verify with `abapgit-agent view --objects <NAME>`. If the object exists and looks correct, no action needed.
870
+ - (b) Commits not pushed yet — run `git log origin/<branch>..HEAD` to check. If unpushed commits exist, run `git push` then retry pull.
871
+ - ✓ **Message is "Activation cancelled. Check the inactive objects."**: The ABAP system rejected the object during activation but returned no detailed error. Run `abapgit-agent inspect --files <file>` to surface the compile error (the object is already on the system after the failed pull). For DDLS: also verify that the `define view entity <NAME>` name in the `.asddls` source exactly matches the `<DDLNAME>` in the `.ddls.xml` (case-sensitive — the DDL name must be UPPER CASE). Fix the error locally, run `syntax` to verify, then re-commit and re-pull.
872
+
873
+ **After every pull:**
874
+ 1. ✓ If pull output shows `⚠️ X XML file(s) differ from serializer output` — re-run immediately with `--sync-xml`, even on an initial setup pull with no ABAP objects (`.abapgit.xml` can drift too)
875
+ 2. ✓ When you authored the objects: always pass `--sync-xml` — rewrites XML metadata files that differ from the ABAP serializer output, amends the commit, and re-pulls so git and the ABAP system stay in sync
769
876
  3. ✗ Never leave a pull without `--sync-xml` when you authored the objects — abapGit will show **M (modified)** permanently otherwise
877
+ 4. ✗ **Never use `--sync-xml` after a failed pull** — if the pull itself failed (errors, activation cancelled, object not created), the serializer output reflects the broken object state. Using `--sync-xml` will write an empty or partial XML back to disk and corrupt the file. Fix the error first, verify pull succeeds, then apply `--sync-xml`.
770
878
 
771
879
  ---
772
880
 
773
881
  ### Quick Decision Tree for AI
774
882
 
883
+ **Before modifying an existing ABAP object: always read the current file(s) first.**
884
+
885
+ **`<folder>` below = the `folder` value from `.abapGitAgent` (e.g. `abap/` or `src/`)**
886
+
887
+ **`[abaplint]` = run `abapgit-agent lint` only if `.abaplint.json` exists in repo root. If syntax or lint fails → fix locally and re-run before proceeding to commit.**
888
+
775
889
  **When user asks to modify/create ABAP code:**
776
890
 
777
891
  ```
778
892
  Modified ABAP files?
779
893
  ├─ CLAS/INTF/PROG/DDLS/FUGR files?
780
894
  │ ├─ Independent files (no cross-dependencies)?
781
- │ │ └─ ✅ Use: syntax → [abaplint] → commit → push → pull --sync-xml
895
+ │ │ └─ ✅ Use: syntax → [abaplint] → commit → push → pull --files <folder>/<name>.<ext> --sync-xml
896
+ │ │ Examples:
897
+ │ │ syntax --files src/zcl_foo.clas.abap → commit → push → pull --files src/zcl_foo.clas.abap --sync-xml
898
+ │ │ syntax --files src/zc_my_view.ddls.asddls → commit → push → pull --files src/zc_my_view.ddls.asddls --sync-xml
899
+ │ │ ⚠️ DDLS: pass the .asddls file (not .xml) to both syntax and pull --files
782
900
  │ └─ Dependent files (interface + class, class uses class)?
783
- │ └─ ✅ Use: skip syntax → [abaplint] → commit → push → pull --sync-xml
901
+ │ └─ ✅ Use: skip syntax → [abaplint] → commit → push → pull --files <folder>/<intf>.<ext>.abap,<folder>/<class>.<ext>.abap --sync-xml
784
902
  └─ Other types (TABL, STRU, DTEL, TTYP, etc.)?
785
903
  ├─ XML-only objects (TABL, STRU, DTEL, TTYP, DOMA, MSAG)?
786
- │ └─ ✅ Use: skip syntax → [abaplint] → commit → push → pull --files abap/ztable.tabl.xml --sync-xml
904
+ │ └─ ✅ Use: skip syntax → [abaplint] → commit → push → pull --files <folder>/<name>.tabl.xml --sync-xml
787
905
  ├─ DCLS (CDS access control)?
788
- │ └─ ✅ Use: skip syntax → [abaplint] → commit → push → pull --files abap/zc_view.dcls.xml --sync-xml
906
+ │ └─ ✅ Use: skip syntax → [abaplint] → commit → push → pull --files <folder>/<name>.dcls.xml --sync-xml
789
907
  │ ⚠️ Pass the .xml file — pull --files does NOT accept .asdcls extensions
790
908
  ├─ ENHO (Enhancement)?
791
- │ └─ ✅ Use: syntax (optional) → [abaplint] → commit → push → pull --files abap/<name>.enho.xml --sync-xml
909
+ │ └─ ✅ Use: syntax (optional) → [abaplint] → commit → push → pull --files <folder>/<name>.enho.xml --sync-xml
792
910
  │ ⚠️ syntax checks basic errors in the hook body; semantic checks require pull
793
911
  │ ⚠️ Pass the .enho.xml file to pull — hash .abap files also work but xml is preferred
794
912
  │ → see: abapgit-agent ref --topic enho
795
913
  └─ Other complex objects?
796
- └─ ✅ Use: skip syntax → [abaplint] → commit → push → pull --sync-xml → (if errors: inspect)
797
-
798
- [abaplint] = run abapgit-agent lint only if .abaplint.json exists in repo root
799
- before applying any quickfix: run abapgit-agent ref --topic abaplint
914
+ └─ ✅ Use: skip syntax → [abaplint] → commit → push → pull --files <folder>/<name>.<ext> --sync-xml → (if errors: inspect)
800
915
  ```
801
916
 
917
+ **After pull succeeds:**
918
+ - ✓ If the class has test includes: run `abapgit-agent unit --files <folder>/<name>.clas.testclasses.abap` to verify all tests pass.
919
+
920
+ **First pull of a new CDS view + a class that depends on it:**
921
+ - ✓ Pull in two steps: first `pull --files <folder>/<name>.ddls.asddls --sync-xml` (activates the CDS view), then `pull --files <folder>/<name>.clas.abap --sync-xml` (activates the class against the now-live view).
922
+ - ⚠️ "Error updating where-used list" almost always means a **syntax error** in the class. Since the pull already attempted activation, run `abapgit-agent inspect --files <folder>/<name>.clas.abap` to surface the error. Fix it locally, run `syntax` to verify, then re-commit and re-pull. Only if syntax and inspect both pass cleanly does the dependency order matter — in that case, the CDS view was not yet active; retry after pulling the CDS view first.
923
+
924
+ **First pull of an interface + implementing class together:**
925
+ - ⚠️ "Error updating where-used list" almost always means a **syntax error** in the class. Since the pull already attempted activation, run `abapgit-agent inspect --files <folder>/<name>.clas.abap` to surface the error. Fix it locally, run `syntax` to verify, then re-commit and re-pull. Only if inspect passes cleanly is the ordering the cause — in that case, the interface was just activated and the class can now compile on a second attempt.
926
+
802
927
  → For creating new objects (what files to write): `abapgit-agent ref --topic object-creation`
803
928
  → For full workflow decision tree and error indicators: `abapgit-agent ref --topic workflow-detailed`
804
929
 
@@ -4,15 +4,16 @@ This project uses [abapgit-agent](https://github.com/SylvosCai/abapgit-agent) fo
4
4
 
5
5
  ## IMPORTANT: Before Writing Any ABAP Code
6
6
 
7
- Read the full ABAP development guide by running:
7
+ Read the condensed AI instructions by running:
8
8
 
9
9
  ```bash
10
- abapgit-agent guide
10
+ abapgit-agent guide --ai
11
11
  ```
12
12
 
13
- **Never pipe `abapgit-agent guide` or `abapgit-agent ref` through `head`, `tail`, or any other truncation command. Always read the full output.**
13
+ **Never pipe `abapgit-agent guide --ai` or `abapgit-agent ref` through `head`, `tail`, or any other truncation command. Always read the full output.**
14
14
 
15
- This guide covers: development workflow, ABAP syntax guidelines, object naming, unit testing, and debugging.
15
+ This covers: all AI tool rules, the workflow decision tree, and the full guidelines index.
16
+ For the complete human-readable guide (workflow examples, explanations): `abapgit-agent guide`
16
17
 
17
18
  > **Humans:** Full guide online at https://sylvoscai.github.io/abapgit-agent/pages/abap-coding-guidelines.html
18
19
 
@@ -207,6 +207,8 @@ on rv_result — no intermediate lv_response variable at all.
207
207
 
208
208
  Run this before pushing to catch issues early, matching what CI does.
209
209
 
210
+ **Always use `abapgit-agent lint` — never run `npx abaplint` or `@abaplint/cli` directly.**
211
+
210
212
  ```bash
211
213
  abapgit-agent lint
212
214
  ```
@@ -18,6 +18,10 @@ grand_parent: ABAP Development
18
18
  5. Key Classes and Methods - line 170
19
19
  6. Important Usage Notes - line 190
20
20
 
21
+ > **"TDD a CDS view" means: write the reader class with test doubles, not tests on the view itself.**
22
+ > CDS views have no test file — tests live in the reader class's `.clas.testclasses.abap`.
23
+ > Development sequence: create the CDS view first (activate it), then create the reader class, then write tests using `CL_CDS_TEST_ENVIRONMENT`.
24
+
21
25
  ---
22
26
 
23
27
  ## 1. When to Use CDS Test Doubles
@@ -42,6 +46,14 @@ without affecting production data, and keeps tests fast and isolated.
42
46
  The test class lives in `<classname>.clas.testclasses.abap`. The CDS view itself has no
43
47
  `.testclasses` file — test it through a regular ABAP class that reads it.
44
48
 
49
+ **Development sequence for a CDS view (CDS-first by necessity):**
50
+ > ⚠️ Unlike pure ABAP TDD, the test class cannot compile until the CDS view is active in
51
+ > the ABAP system. Activate the CDS view first, then write the reader class with test doubles.
52
+ 1. Write the CDS view DDL (`.ddls.asddls`)
53
+ 2. Write a reader class that SELECTs from the view
54
+ 3. Write test class in the reader's `.testclasses.abap` using `CL_CDS_TEST_ENVIRONMENT`
55
+ 4. Pull CDS view first, then pull reader class (DDIC must be active before class compiles)
56
+
45
57
  ```abap
46
58
  "-------------------------
47
59
  " CLASS DEFINITION
@@ -342,6 +342,13 @@ When working with CDS view syntax (arithmetic, built-in functions, aggregations,
342
342
 
343
343
  ### Best Practice: Use CDS View Entity as Type
344
344
 
345
+ > ⚠️ **SELECT field list order must match the CDS view field order** when selecting into a typed table. A mismatch causes a compile-time error ("data type of component X is not compatible with Y"). Safe alternatives:
346
+ > - `SELECT * FROM zc_my_view INTO TABLE @rt_data` — always order-safe
347
+ > - `SELECT ... INTO CORRESPONDING FIELDS OF TABLE @rt_data` — order-independent
348
+ > - `SELECT FROM zc_my_view FIELDS field1, field2 INTO TABLE @rt_data` — modern syntax, order-safe
349
+
350
+ > ⚠️ **Arithmetic/computed fields** (division, `cast`, expressions) produce `decfloat34` on the ABAP side — not `p`, `f`, or `dec`. If you use a custom struct, declare computed fields as `TYPE decfloat34`. The safest alternative is `SELECT * INTO @DATA(...)` which infers the correct type automatically.
351
+
345
352
  ```abap
346
353
  " ✅ RECOMMENDED - Use view entity directly
347
354
  TYPES ty_results TYPE STANDARD TABLE OF zc_my_view WITH DEFAULT KEY.
@@ -20,6 +20,10 @@ abapgit-agent dump --date TODAY --detail 1 # full detail: call stack + source
20
20
  The `--detail` output shows the exact failing line (`>>>>>` marker), call stack,
21
21
  and SAP's error analysis. Use it before asking the user to open ST22.
22
22
 
23
+ > **`???` in error analysis fields** (table name, access method, key values) is normal when the
24
+ > ABAP compiler inlined the table access (e.g. `itab[ 1 ]` style expressions). Focus on the
25
+ > `>>>>>` source line and the exception class — these are always reliable.
26
+
23
27
  Common filters:
24
28
  ```bash
25
29
  abapgit-agent dump --user DEVELOPER --date TODAY # specific user
@@ -163,7 +163,12 @@ until grep -q "Listener active" /tmp/attach.json 2>/dev/null; do sleep 0.3; done
163
163
  sleep 1 # brief extra window for the POST to reach ADT
164
164
 
165
165
  # Trigger in background — MUST stay alive for the whole session
166
+ # Use unit if a test exists, run --class if testing a class runner.
167
+ # Either way the trigger MUST run in background (&) — a foreground trigger
168
+ # blocks the terminal and if interrupted will break the debug session.
166
169
  abapgit-agent unit --files src/zcl_my_class.clas.testclasses.abap > /tmp/trigger.json 2>&1 &
170
+ # OR:
171
+ # abapgit-agent run --class ZCL_MY_CLASS > /tmp/trigger.json 2>&1 &
167
172
 
168
173
  # Poll until breakpoint fires and session JSON appears in attach output
169
174
  SESSION=""
@@ -173,6 +178,11 @@ for i in $(seq 1 30); do
173
178
  [ -n "$SESSION" ] && break
174
179
  done
175
180
 
181
+ # ⚠️ A session ID with "position":{} (empty) means the breakpoint has NOT fired yet,
182
+ # or the trigger already completed before hitting it. Do NOT call vars/stack until
183
+ # "position" contains "line" and "isActive":true. If position stays empty after the
184
+ # trigger finishes, the breakpoint was missed — re-delete, re-set, re-attach, re-trigger.
185
+
176
186
  # Inspect and step — each is an individual call, no --session needed
177
187
  abapgit-agent debug stack --json
178
188
  abapgit-agent debug vars --json
@@ -190,7 +200,7 @@ rm -f /tmp/attach.json /tmp/trigger.json
190
200
 
191
201
  > **Four rules for scripted mode:**
192
202
  > 1. Wait for `"Listener active"` in the attach output before firing the trigger — this message is printed to stderr (captured in `attach.json`) the moment the listener POST is about to reach ADT. A blind `sleep` is not reliable under system load.
193
- > 2. Keep the trigger process alive in the background for the entire session if it exits, the ABAP work process is released and the session ends
203
+ > 2. **Always run the trigger in background (`&`)** whether using `unit` or `run --class`. A foreground trigger blocks the terminal; if it is interrupted before the debug session completes, the ABAP work process is left frozen. The trigger must stay alive for the entire session.
194
204
  > 3. Always finish with `step --type continue` — this releases the frozen work process so the trigger can complete normally
195
205
  > 4. **Never pass `--session` to `step/vars/stack`** — it bypasses the daemon IPC and causes `noSessionAttached`. Omit it and let commands auto-load from the saved state file.
196
206
 
@@ -199,13 +209,21 @@ rm -f /tmp/attach.json /tmp/trigger.json
199
209
  ```bash
200
210
  abapgit-agent debug vars --json # all variables
201
211
  abapgit-agent debug vars --name LV_RESULT --json # one variable
202
- abapgit-agent debug vars --expand LT_DATA --json # drill into table/structure
212
+ abapgit-agent debug vars --expand LT_DATA --json # drill into table/structure — expands all rows
203
213
  abapgit-agent debug step --type over --json # step over
204
214
  abapgit-agent debug step --type into --json # step into
205
215
  abapgit-agent debug step --type continue --json # continue to next breakpoint / finish
206
216
  abapgit-agent debug stack --json # call stack (shows which test method is active)
207
217
  ```
208
218
 
219
+ > **`--expand` does not support table-row index notation.** `--expand "LT_DATA[1]"` returns "Variable not found". Use `--expand LT_DATA` to expand all rows, then `--name` on a specific scalar component.
220
+
221
+ > **Field symbols assigned via `ASSIGNING FIELD-SYMBOL(<fs>)` do not appear in `debug vars` output.** Only named variables (`DATA`, `FINAL`) are listed. To inspect the current loop row, expand the parent table:
222
+ > ```bash
223
+ > abapgit-agent debug vars --expand RT_DATA --json # table rows visible by index
224
+ > ```
225
+ > Alternatively, step over the assignment and use `--name` on a scalar component directly.
226
+
209
227
  > **`step --type continue` return values:**
210
228
  > - `{"continued":true,"finished":true}` — program ran to **completion** (ADT returned HTTP 500, session is over). Do not re-attach.
211
229
  > - `{"continued":true}` (no `finished` field) — program hit the **next breakpoint** and is still paused. You must re-attach to receive the suspension and continue inspecting:
@@ -23,6 +23,49 @@ User: "Now run it"
23
23
  → ✓ Run it
24
24
  ```
25
25
 
26
+ **For ABAP programs (PROG type)** — use `--program` instead of `--class`:
27
+
28
+ ```bash
29
+ abapgit-agent run --program ZR_MY_REPORT
30
+ ```
31
+
32
+ `--program` works identically to `--class` for programs that implement `IF_OO_ADT_CLASSRUN`. Use it whenever the object type is PROG, not CLAS.
33
+
34
+ ---
35
+
36
+ ## Writing a Runner Class (`IF_OO_ADT_CLASSRUN`)
37
+
38
+ `out->write()` accepts any ABAP data object and formats it automatically — no manual `WRITE` statements needed.
39
+
40
+ ```abap
41
+ CLASS zcl_my_runner DEFINITION PUBLIC FINAL CREATE PUBLIC.
42
+ PUBLIC SECTION.
43
+ INTERFACES if_oo_adt_classrun.
44
+ ENDCLASS.
45
+
46
+ CLASS zcl_my_runner IMPLEMENTATION.
47
+ METHOD if_oo_adt_classrun~main.
48
+ " Scalar
49
+ out->write( 'Hello!' ).
50
+
51
+ " Internal table — rendered as column headers + one row per entry
52
+ SELECT carrid, connid, price FROM sflight INTO TABLE @DATA(lt_flights).
53
+ out->write( data = lt_flights name = 'Flights' ).
54
+ ENDMETHOD.
55
+ ENDCLASS.
56
+ ```
57
+
58
+ **Output format (verified on live system):**
59
+
60
+ | Call | Output |
61
+ |------|--------|
62
+ | `out->write( 'text' )` | `text` |
63
+ | `out->write( lv_int )` | `42` |
64
+ | `out->write( data = ls_struc name = 'Label' )` | `Label` + field-name headers + one data row |
65
+ | `out->write( data = lt_itab name = 'Label' )` | `Label` + field-name headers + one row per table entry |
66
+
67
+ The `name =` parameter is optional — it adds a label above the output. Structures and internal tables are rendered as a columnar table with ABAP field names as headers.
68
+
26
69
  ---
27
70
 
28
71
  ## Probe Classes and `scratchWorkspace`
@@ -8,7 +8,7 @@ grand_parent: ABAP Development
8
8
 
9
9
  # String Templates
10
10
 
11
- **Searchable keywords**: string template, pipe, `|...|`, literal brace, escape, expression limiter, JSON, `\{`, `\}`
11
+ **Searchable keywords**: string template, pipe, `|...|`, literal brace, escape, expression limiter, JSON, `\{`, `\}`, WIDTH, ALIGN, ALPHA, CASE, ZERO, NUMBER, DATE, TIME, padding, formatting
12
12
 
13
13
  ## Syntax
14
14
 
@@ -70,6 +70,71 @@ DATA(s) = |line1\r\nline2|. " CR+LF
70
70
  DATA(s) = |{ lv_a }| && ` separator ` && |{ lv_b }|.
71
71
  ```
72
72
 
73
+ ## Formatting Options (verified on live system)
74
+
75
+ Formatting options go inside `{ expr OPTION = VALUE }`:
76
+
77
+ ### Alignment and padding
78
+
79
+ ```abap
80
+ " WIDTH pads/truncates to fixed width; ALIGN controls side
81
+ DATA(s) = |{ lv_carrier WIDTH = 10 ALIGN = LEFT }|. " 'LH '
82
+ DATA(s) = |{ lv_price WIDTH = 10 ALIGN = RIGHT }|. " ' 500'
83
+ DATA(s) = |{ lv_text WIDTH = 20 ALIGN = CENTER }|.
84
+ ```
85
+
86
+ `ALIGN` values: `LEFT` (default), `RIGHT`, `CENTER`.
87
+
88
+ ### ALPHA — leading-zero handling for numeric strings
89
+
90
+ ```abap
91
+ DATA(lv_connid) = '0400'.
92
+ |{ lv_connid ALPHA = IN }| " → '0400' (keep leading zeros)
93
+ |{ lv_connid ALPHA = OUT }| " → '400 ' (strip leading zeros)
94
+ ```
95
+
96
+ ### CASE — change letter case
97
+
98
+ ```abap
99
+ |{ lv_name CASE = UPPER }| " → 'JOHN'
100
+ |{ lv_name CASE = LOWER }| " → 'john'
101
+ ```
102
+
103
+ ### ZERO — show/hide zero values
104
+
105
+ ```abap
106
+ DATA(lv_zero) = 0.
107
+ |{ lv_zero ZERO = YES }| " → '0'
108
+ |{ lv_zero ZERO = NO }| " → '' (empty)
109
+ ```
110
+
111
+ ### NUMBER — numeric formatting
112
+
113
+ ```abap
114
+ DATA(lv_amount) = CONV decfloat34( '1234567.89' ).
115
+ |{ lv_amount NUMBER = USER }| " locale-aware: '1.234.567,89' (DE)
116
+ |{ lv_amount NUMBER = ENVIRONMENT }| " system locale
117
+ |{ lv_amount NUMBER = RAW }| " '1234567.89' (no formatting)
118
+ ```
119
+
120
+ ### DATE / TIME — date and time formatting
121
+
122
+ ```abap
123
+ |{ sy-datum DATE = USER }| " locale-aware: '22.04.2026' (DE)
124
+ |{ sy-datum DATE = ISO }| " '2026-04-22'
125
+ |{ sy-datum DATE = ENVIRONMENT }| " system locale
126
+ |{ sy-uzeit TIME = USER }| " '06:24:36'
127
+ |{ sy-uzeit TIME = ISO }| " '062436'
128
+ ```
129
+
130
+ ### Combining options
131
+
132
+ Multiple options are separated by spaces:
133
+
134
+ ```abap
135
+ |{ lv_value WIDTH = 15 ALIGN = RIGHT ZERO = YES }|
136
+ ```
137
+
73
138
  ## Performance Note
74
139
 
75
140
  A string template containing only literal text (no `{ }` expressions) is evaluated at
package/bin/abapgit-agent CHANGED
@@ -24,7 +24,7 @@ const gitUtils = require('../src/utils/git-utils');
24
24
  const versionCheck = require('../src/utils/version-check');
25
25
  const validators = require('../src/utils/validators');
26
26
  const { AbapHttp } = require('../src/utils/abap-http');
27
- const { loadConfig, getTransport, isAbapIntegrationEnabled, getSafeguards, getConflictSettings, getTransportSettings, getScratchWorkspace } = require('../src/config');
27
+ const { loadConfig, getTransport, isAbapIntegrationEnabled, getSafeguards, getConflictSettings, getTransportSettings, getScratchWorkspace, getInspectConfig } = require('../src/config');
28
28
 
29
29
  // Get terminal width for responsive table
30
30
  const getTermWidth = () => process.stdout.columns || 80;
@@ -89,7 +89,8 @@ To enable integration:
89
89
  getSafeguards,
90
90
  getConflictSettings,
91
91
  getTransportSettings,
92
- getScratchWorkspace
92
+ getScratchWorkspace,
93
+ getInspectConfig
93
94
  };
94
95
 
95
96
  // Execute command
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abapgit-agent",
3
- "version": "1.17.8",
3
+ "version": "1.17.9",
4
4
  "description": "ABAP Git Agent - Pull and activate ABAP code via abapGit from any git repository",
5
5
  "files": [
6
6
  "bin/",
@@ -178,6 +178,13 @@ module.exports = {
178
178
  console.log('');
179
179
  },
180
180
 
181
+ _extractAiContent(content) {
182
+ const marker = '<!-- AI-CONDENSED-START -->';
183
+ const markerIndex = content.indexOf(marker);
184
+ if (markerIndex === -1) return content;
185
+ return content.slice(markerIndex + marker.length).trimStart();
186
+ },
187
+
181
188
  async execute(args) {
182
189
  if (args.includes('--migrate')) {
183
190
  return this._runMigrate(args);
@@ -197,6 +204,16 @@ module.exports = {
197
204
 
198
205
  const content = fs.readFileSync(filePath, 'utf8');
199
206
 
207
+ if (args.includes('--ai')) {
208
+ const aiContent = this._extractAiContent(content);
209
+ if (args.includes('--json')) {
210
+ console.log(JSON.stringify({ path: filePath, content: aiContent }));
211
+ return;
212
+ }
213
+ console.log(aiContent);
214
+ return;
215
+ }
216
+
200
217
  if (args.includes('--json')) {
201
218
  console.log(JSON.stringify({ path: filePath, content }));
202
219
  return;
@@ -248,7 +248,7 @@ module.exports = {
248
248
  requiresVersionCheck: true,
249
249
 
250
250
  async execute(args, context) {
251
- const { loadConfig, AbapHttp } = context;
251
+ const { loadConfig, AbapHttp, getInspectConfig } = context;
252
252
 
253
253
  if (args.includes('--help') || args.includes('-h')) {
254
254
  console.log(`
@@ -287,9 +287,11 @@ Examples:
287
287
 
288
288
  const filesSyntaxCheck = args[filesArgIndex + 1].split(',').map(f => f.trim());
289
289
 
290
- // Parse optional --variant parameter
290
+ // Parse optional --variant parameter; fall back to project config
291
291
  const variantArgIndex = args.indexOf('--variant');
292
- const variant = variantArgIndex !== -1 ? args[variantArgIndex + 1] : null;
292
+ const variantArg = variantArgIndex !== -1 ? args[variantArgIndex + 1] : null;
293
+ const inspectConfig = getInspectConfig();
294
+ const variant = variantArg || inspectConfig.variant || null;
293
295
 
294
296
  // Parse optional --junit-output parameter
295
297
  const junitArgIndex = args.indexOf('--junit-output');
@@ -298,7 +300,8 @@ Examples:
298
300
  if (!jsonOutput) {
299
301
  console.log(`\n Inspect for ${filesSyntaxCheck.length} file(s)`);
300
302
  if (variant) {
301
- console.log(` Using variant: ${variant}`);
303
+ const source = variantArg ? '' : ' (from project config)';
304
+ console.log(` Using variant: ${variant}${source}`);
302
305
  }
303
306
  if (junitOutput) {
304
307
  console.log(` JUnit output: ${junitOutput}`);
@@ -362,6 +362,9 @@ Examples:
362
362
  if (success === 'X' || success === true) {
363
363
  console.log(`✅ Pull completed successfully!`);
364
364
  console.log(` Message: ${message || 'N/A'}`);
365
+ if (activatedCount === 0 && files && !isRepull) {
366
+ console.warn(`⚠️ ACTIVATED_COUNT: 0 — no objects were activated. Check for unpushed commits: git log origin/<branch>..HEAD`);
367
+ }
365
368
  } else if (failedCount === 0 && failedObjects.length === 0 &&
366
369
  activatedCount === 0 && logMessages.length === 0 &&
367
370
  (!message || /activation cancelled|nothing to activate|already active/i.test(message))) {
@@ -522,20 +525,40 @@ Examples:
522
525
  const quotedPaths = diffFiles.map(f => `"${f.relPath}"`).join(' ');
523
526
  execSync(`git add ${quotedPaths}`, { cwd: process.cwd() });
524
527
 
525
- // 3. Amend last commit
528
+ // 3. Capture pre-amend SHA before amending (used for --force-with-lease below)
529
+ const preAmendSha = execSync('git rev-parse HEAD', { cwd: process.cwd(), stdio: 'pipe' }).toString().trim();
530
+
531
+ // 3b. Check whether origin/<branch> already exists on the remote.
532
+ // Use git ls-remote rather than rev-parse origin/<branch> because the local
533
+ // remote-tracking ref may not exist even when the branch is on the remote
534
+ // (e.g. after a push that only wrote branch config but not refs/remotes/).
535
+ let remoteRefExists = false;
536
+ try {
537
+ const lsOut = execSync(`git ls-remote origin "refs/heads/${branch}"`, { cwd: process.cwd(), stdio: 'pipe' }).toString().trim();
538
+ remoteRefExists = lsOut.length > 0;
539
+ } catch (_) { /* cannot reach remote — treat as new branch */ }
540
+
541
+ // 4. Amend last commit
526
542
  execSync('git commit --amend --no-edit', { cwd: process.cwd() });
527
543
 
528
- // 4. Push with force-with-lease; fetch first so tracking ref is current
529
- // (the remote may have been force-pushed by another process between our last
530
- // fetch and this push a fetch makes --force-with-lease reliable)
544
+ // 5. Push with force-with-lease using the pre-amend SHA as the expected remote value.
545
+ // Using --force-with-lease=<refname>:<sha> tells git: "the remote must have exactly
546
+ // <preAmendSha> if it does, replace it with our amended commit".
547
+ // Plain --force-with-lease (no sha) re-checks the tracking ref which was just updated
548
+ // by git fetch, making it always fail after an amend.
531
549
  let pushed = false;
532
550
  try {
533
- try { execSync('git fetch origin', { cwd: process.cwd(), stdio: 'pipe' }); } catch (_) { /* no remote is fine */ }
534
551
  // Retry the push up to 3 times on transient server errors (e.g. GitHub Enterprise 500)
535
552
  let pushErr;
536
553
  for (let attempt = 1; attempt <= 3; attempt++) {
537
554
  try {
538
- execSync('git push --force-with-lease', { cwd: process.cwd(), stdio: 'pipe' });
555
+ // If origin/<branch> exists: use --force-with-lease=branch:preAmendSha so we only
556
+ // overwrite if the remote still has the pre-amend commit (safe concurrent guard).
557
+ // If origin/<branch> doesn't exist yet: plain push with --set-upstream (no lease needed).
558
+ const pushCmd = remoteRefExists
559
+ ? `git push --set-upstream origin ${branch} --force-with-lease=${branch}:${preAmendSha}`
560
+ : `git push --set-upstream origin ${branch}`;
561
+ execSync(pushCmd, { cwd: process.cwd(), stdio: 'pipe' });
539
562
  pushed = true;
540
563
  break;
541
564
  } catch (err) {
@@ -550,13 +573,7 @@ Examples:
550
573
  }
551
574
  }
552
575
  } catch (pushErr) {
553
- const msg = (pushErr.stderr || pushErr.stdout || pushErr.message || '').toString();
554
- if (msg.includes('no upstream branch') || msg.includes('has no upstream')) {
555
- // Branch not yet pushed — set upstream and force push (amend requires force)
556
- execSync(`git push --force-with-lease --set-upstream origin ${branch}`, { cwd: process.cwd(), stdio: 'pipe' });
557
- pushed = true;
558
- }
559
- // Any other push error (no remote at all, auth failure, etc.) → skip silently
576
+ // Any push error (no remote, auth failure, etc.) skip silently
560
577
  }
561
578
 
562
579
  if (pushed) {
@@ -166,8 +166,9 @@ async function runUnitTestForFile(sourceFile, csrfToken, config, coverage, http,
166
166
  // Handle uppercase keys from ABAP
167
167
  const success = result.SUCCESS || result.success;
168
168
  const testCount = result.TEST_COUNT || result.test_count || 0;
169
- const passedCount = result.PASSED_COUNT || result.passed_count || 0;
170
169
  const failedCount = result.FAILED_COUNT || result.failed_count || 0;
170
+ // ABAP AUnit API does not return individual passing method names — derive passed count
171
+ const passedCount = testCount - failedCount;
171
172
  const message = result.MESSAGE || result.message || '';
172
173
  const errors = result.ERRORS || result.errors || [];
173
174
 
package/src/config.js CHANGED
@@ -247,6 +247,17 @@ function getCoverageConfig() {
247
247
  };
248
248
  }
249
249
 
250
+ /**
251
+ * Get inspect settings from project-level config (.abapgit-agent.json)
252
+ * @returns {{ variant: string|null }}
253
+ */
254
+ function getInspectConfig() {
255
+ const projectConfig = loadProjectConfig();
256
+ return {
257
+ variant: projectConfig?.inspect?.variant || null,
258
+ };
259
+ }
260
+
250
261
  module.exports = {
251
262
  loadConfig,
252
263
  getAbapConfig,
@@ -261,5 +272,6 @@ module.exports = {
261
272
  getTransportHookConfig,
262
273
  getTransportSettings,
263
274
  getScratchWorkspace,
264
- getCoverageConfig
275
+ getCoverageConfig,
276
+ getInspectConfig
265
277
  };