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 +1 -0
- package/abap/CLAUDE.md +150 -25
- package/abap/CLAUDE.slim.md +5 -4
- package/abap/guidelines/abaplint.md +2 -0
- package/abap/guidelines/cds-testing.md +12 -0
- package/abap/guidelines/cds.md +7 -0
- package/abap/guidelines/debug-dump.md +4 -0
- package/abap/guidelines/debug-session.md +20 -2
- package/abap/guidelines/run-probe-classes.md +43 -0
- package/abap/guidelines/string-template.md +66 -1
- package/bin/abapgit-agent +3 -2
- package/package.json +1 -1
- package/src/commands/guide.js +17 -0
- package/src/commands/inspect.js +7 -4
- package/src/commands/pull.js +30 -13
- package/src/commands/unit.js +2 -1
- package/src/config.js +13 -1
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
|
-
<
|
|
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
|
-
#
|
|
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 #
|
|
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
|
-
|
|
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
|
-
|
|
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`
|
|
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
|
|
750
|
-
3. ✗ Don't
|
|
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
|
-
**
|
|
767
|
-
1. ✓
|
|
768
|
-
2. ✓
|
|
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
|
|
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
|
|
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
|
|
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
|
|
package/abap/CLAUDE.slim.md
CHANGED
|
@@ -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
|
|
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
|
|
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
|
package/abap/guidelines/cds.md
CHANGED
|
@@ -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.
|
|
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
package/src/commands/guide.js
CHANGED
|
@@ -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;
|
package/src/commands/inspect.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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}`);
|
package/src/commands/pull.js
CHANGED
|
@@ -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.
|
|
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
|
-
//
|
|
529
|
-
//
|
|
530
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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) {
|
package/src/commands/unit.js
CHANGED
|
@@ -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
|
};
|