abapgit-agent 1.5.0 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/README.md +1 -0
  2. package/abap/guidelines/00_index.md +36 -0
  3. package/abap/guidelines/01_sql.md +88 -0
  4. package/abap/guidelines/02_exceptions.md +176 -0
  5. package/abap/guidelines/03_testing.md +269 -0
  6. package/abap/guidelines/04_cds.md +136 -0
  7. package/abap/guidelines/05_classes.md +58 -0
  8. package/abap/guidelines/06_objects.md +110 -0
  9. package/abap/guidelines/07_json.md +24 -0
  10. package/abap/guidelines/08_abapgit.md +222 -0
  11. package/abap/guidelines/09_unit_testable_code.md +568 -0
  12. package/bin/abapgit-agent +513 -38
  13. package/bin/abgagt +24 -0
  14. package/package.json +8 -2
  15. package/src/abap-client.js +65 -2
  16. package/src/agent.js +57 -3
  17. package/src/config.js +1 -1
  18. package/src/ref-search.js +1037 -0
  19. package/.abapGitAgent.example +0 -11
  20. package/.github/workflows/release.yml +0 -60
  21. package/API.md +0 -710
  22. package/CLAUDE.md +0 -1058
  23. package/CLAUDE_MEM.md +0 -88
  24. package/ERROR_HANDLING.md +0 -30
  25. package/INSTALL.md +0 -155
  26. package/RELEASE_NOTES.md +0 -143
  27. package/abap/CLAUDE.md +0 -1010
  28. package/abap/copilot-instructions.md +0 -79
  29. package/abap/package.devc.xml +0 -10
  30. package/abap/zcl_abgagt_agent.clas.abap +0 -420
  31. package/abap/zcl_abgagt_agent.clas.xml +0 -15
  32. package/abap/zcl_abgagt_cmd_factory.clas.abap +0 -48
  33. package/abap/zcl_abgagt_cmd_factory.clas.xml +0 -15
  34. package/abap/zcl_abgagt_command_create.clas.abap +0 -95
  35. package/abap/zcl_abgagt_command_create.clas.xml +0 -15
  36. package/abap/zcl_abgagt_command_import.clas.abap +0 -138
  37. package/abap/zcl_abgagt_command_import.clas.xml +0 -15
  38. package/abap/zcl_abgagt_command_inspect.clas.abap +0 -456
  39. package/abap/zcl_abgagt_command_inspect.clas.testclasses.abap +0 -121
  40. package/abap/zcl_abgagt_command_inspect.clas.xml +0 -16
  41. package/abap/zcl_abgagt_command_preview.clas.abap +0 -386
  42. package/abap/zcl_abgagt_command_preview.clas.xml +0 -15
  43. package/abap/zcl_abgagt_command_pull.clas.abap +0 -80
  44. package/abap/zcl_abgagt_command_pull.clas.testclasses.abap +0 -87
  45. package/abap/zcl_abgagt_command_pull.clas.xml +0 -16
  46. package/abap/zcl_abgagt_command_tree.clas.abap +0 -237
  47. package/abap/zcl_abgagt_command_tree.clas.xml +0 -15
  48. package/abap/zcl_abgagt_command_unit.clas.abap +0 -297
  49. package/abap/zcl_abgagt_command_unit.clas.xml +0 -15
  50. package/abap/zcl_abgagt_command_view.clas.abap +0 -240
  51. package/abap/zcl_abgagt_command_view.clas.xml +0 -15
  52. package/abap/zcl_abgagt_resource_create.clas.abap +0 -71
  53. package/abap/zcl_abgagt_resource_create.clas.xml +0 -15
  54. package/abap/zcl_abgagt_resource_health.clas.abap +0 -25
  55. package/abap/zcl_abgagt_resource_health.clas.xml +0 -15
  56. package/abap/zcl_abgagt_resource_import.clas.abap +0 -66
  57. package/abap/zcl_abgagt_resource_import.clas.xml +0 -15
  58. package/abap/zcl_abgagt_resource_inspect.clas.abap +0 -63
  59. package/abap/zcl_abgagt_resource_inspect.clas.xml +0 -15
  60. package/abap/zcl_abgagt_resource_preview.clas.abap +0 -67
  61. package/abap/zcl_abgagt_resource_preview.clas.xml +0 -15
  62. package/abap/zcl_abgagt_resource_pull.clas.abap +0 -71
  63. package/abap/zcl_abgagt_resource_pull.clas.xml +0 -15
  64. package/abap/zcl_abgagt_resource_tree.clas.abap +0 -70
  65. package/abap/zcl_abgagt_resource_tree.clas.xml +0 -15
  66. package/abap/zcl_abgagt_resource_unit.clas.abap +0 -64
  67. package/abap/zcl_abgagt_resource_unit.clas.xml +0 -15
  68. package/abap/zcl_abgagt_resource_view.clas.abap +0 -68
  69. package/abap/zcl_abgagt_resource_view.clas.xml +0 -15
  70. package/abap/zcl_abgagt_rest_handler.clas.abap +0 -32
  71. package/abap/zcl_abgagt_rest_handler.clas.xml +0 -15
  72. package/abap/zcl_abgagt_util.clas.abap +0 -93
  73. package/abap/zcl_abgagt_util.clas.testclasses.abap +0 -84
  74. package/abap/zcl_abgagt_util.clas.xml +0 -16
  75. package/abap/zcl_abgagt_viewer_clas.clas.abap +0 -58
  76. package/abap/zcl_abgagt_viewer_clas.clas.xml +0 -15
  77. package/abap/zcl_abgagt_viewer_ddls.clas.abap +0 -83
  78. package/abap/zcl_abgagt_viewer_ddls.clas.xml +0 -15
  79. package/abap/zcl_abgagt_viewer_dtel.clas.abap +0 -98
  80. package/abap/zcl_abgagt_viewer_dtel.clas.xml +0 -15
  81. package/abap/zcl_abgagt_viewer_factory.clas.abap +0 -41
  82. package/abap/zcl_abgagt_viewer_factory.clas.xml +0 -15
  83. package/abap/zcl_abgagt_viewer_intf.clas.abap +0 -58
  84. package/abap/zcl_abgagt_viewer_intf.clas.xml +0 -15
  85. package/abap/zcl_abgagt_viewer_stru.clas.abap +0 -59
  86. package/abap/zcl_abgagt_viewer_stru.clas.xml +0 -15
  87. package/abap/zcl_abgagt_viewer_tabl.clas.abap +0 -59
  88. package/abap/zcl_abgagt_viewer_tabl.clas.xml +0 -15
  89. package/abap/zcl_abgagt_viewer_ttyp.clas.abap +0 -93
  90. package/abap/zcl_abgagt_viewer_ttyp.clas.xml +0 -15
  91. package/abap/zif_abgagt_agent.intf.abap +0 -53
  92. package/abap/zif_abgagt_agent.intf.xml +0 -15
  93. package/abap/zif_abgagt_cmd_factory.intf.abap +0 -7
  94. package/abap/zif_abgagt_cmd_factory.intf.xml +0 -15
  95. package/abap/zif_abgagt_command.intf.abap +0 -26
  96. package/abap/zif_abgagt_command.intf.xml +0 -15
  97. package/abap/zif_abgagt_util.intf.abap +0 -28
  98. package/abap/zif_abgagt_util.intf.xml +0 -15
  99. package/abap/zif_abgagt_viewer.intf.abap +0 -12
  100. package/abap/zif_abgagt_viewer.intf.xml +0 -15
  101. package/docs/commands.md +0 -142
  102. package/docs/create-command.md +0 -129
  103. package/docs/health-command.md +0 -89
  104. package/docs/import-command.md +0 -195
  105. package/docs/init-command.md +0 -189
  106. package/docs/inspect-command.md +0 -169
  107. package/docs/list-command.md +0 -289
  108. package/docs/preview-command.md +0 -528
  109. package/docs/pull-command.md +0 -202
  110. package/docs/status-command.md +0 -68
  111. package/docs/tree-command.md +0 -303
  112. package/docs/unit-command.md +0 -167
  113. package/docs/view-command.md +0 -501
  114. package/img/claude.png +0 -0
  115. package/scripts/claude-integration.js +0 -351
  116. package/scripts/release.js +0 -298
  117. package/scripts/release.sh +0 -60
  118. package/scripts/test-integration.js +0 -139
  119. package/scripts/unrelease.js +0 -277
package/bin/abapgit-agent CHANGED
@@ -3,7 +3,8 @@
3
3
  * ABAP Git Agent - CLI Tool
4
4
  *
5
5
  * Usage:
6
- * abapgit-agent init --folder <folder> --package <package>
6
+ * abapgit-agent init --folder <folder> --package <package> # Initialize: copies config, CLAUDE.md, guidelines
7
+ * abapgit-agent init --update # Update existing files to latest version
7
8
  * abapgit-agent create
8
9
  * abapgit-agent import [--message <message>]
9
10
  * abapgit-agent pull [--branch <branch>]
@@ -23,6 +24,27 @@ const TERM_WIDTH = getTermWidth();
23
24
 
24
25
  const COOKIE_FILE = '.abapgit_agent_cookies.txt';
25
26
 
27
+ /**
28
+ * Convert ISO date formats (YYYY-MM-DD) to ABAP DATS format (YYYYMMDD) in WHERE clause
29
+ * This allows users to use familiar ISO date formats while ensuring compatibility with ABAP SQL
30
+ * @param {string} whereClause - SQL WHERE clause
31
+ * @returns {string} - WHERE clause with dates converted to YYYYMMDD format
32
+ */
33
+ function convertDatesInWhereClause(whereClause) {
34
+ if (!whereClause) return whereClause;
35
+
36
+ // Pattern to match ISO date format: 'YYYY-MM-DD'
37
+ const isoDatePattern = /'\d{4}-\d{2}-\d{2}'/g;
38
+
39
+ return whereClause.replace(isoDatePattern, (match) => {
40
+ // Extract YYYY, MM, DD from 'YYYY-MM-DD'
41
+ const dateContent = match.slice(1, -1); // Remove quotes: YYYY-MM-DD
42
+ const [year, month, day] = dateContent.split('-');
43
+ // Return in ABAP format: 'YYYYMMDD'
44
+ return `'${year}${month}${day}'`;
45
+ });
46
+ }
47
+
26
48
  /**
27
49
  * Get CLI version from package.json
28
50
  */
@@ -516,12 +538,14 @@ async function processInspectResult(res) {
516
538
  const errorCount = res.ERROR_COUNT !== undefined ? res.ERROR_COUNT : (res.error_count || 0);
517
539
  const errors = res.ERRORS !== undefined ? res.ERRORS : (res.errors || []);
518
540
  const warnings = res.WARNINGS !== undefined ? res.WARNINGS : (res.warnings || []);
541
+ const infos = res.INFOS !== undefined ? res.INFOS : (res.infos || []);
519
542
 
520
- if (errorCount > 0 || warnings.length > 0) {
543
+ if (errorCount > 0 || warnings.length > 0 || infos.length > 0) {
521
544
  if (errorCount > 0) {
522
545
  console.log(`❌ ${objectType} ${objectName} - Syntax check failed (${errorCount} error(s)):`);
523
546
  } else {
524
- console.log(`⚠️ ${objectType} ${objectName} - Syntax check passed with warnings (${warnings.length}):`);
547
+ const total = warnings.length + infos.length;
548
+ console.log(`⚠️ ${objectType} ${objectName} - Syntax check passed with warnings (${total}):`);
525
549
  }
526
550
  console.log('\nErrors:');
527
551
  console.log('─'.repeat(60));
@@ -530,8 +554,16 @@ async function processInspectResult(res) {
530
554
  const line = err.LINE || err.line || '?';
531
555
  const column = err.COLUMN || err.column || '?';
532
556
  const text = err.TEXT || err.text || 'Unknown error';
557
+ const methodName = err.METHOD_NAME || err.method_name;
558
+ const sobjname = err.SOBJNAME || err.sobjname;
533
559
 
560
+ if (methodName) {
561
+ console.log(` Method: ${methodName}`);
562
+ }
534
563
  console.log(` Line ${line}, Column ${column}:`);
564
+ if (sobjname && sobjname.includes('====')) {
565
+ console.log(` Include: ${sobjname}`);
566
+ }
535
567
  console.log(` ${text}`);
536
568
  console.log('');
537
569
  }
@@ -543,7 +575,38 @@ async function processInspectResult(res) {
543
575
  for (const warn of warnings) {
544
576
  const line = warn.LINE || warn.line || '?';
545
577
  const text = warn.MESSAGE || warn.message || 'Unknown warning';
546
- console.log(` Line ${line}: ${text}`);
578
+ const methodName = warn.METHOD_NAME || warn.method_name;
579
+ const sobjname = warn.SOBJNAME || warn.sobjname;
580
+
581
+ if (methodName) {
582
+ console.log(` Method: ${methodName}`);
583
+ }
584
+ console.log(` Line ${line}:`);
585
+ if (sobjname && sobjname.includes('====')) {
586
+ console.log(` Include: ${sobjname}`);
587
+ }
588
+ console.log(` ${text}`);
589
+ }
590
+ }
591
+
592
+ // Show infos if any
593
+ if (infos.length > 0) {
594
+ console.log('Info:');
595
+ console.log('─'.repeat(60));
596
+ for (const info of infos) {
597
+ const line = info.LINE || info.line || '?';
598
+ const text = info.MESSAGE || info.message || 'Unknown info';
599
+ const methodName = info.METHOD_NAME || info.method_name;
600
+ const sobjname = info.SOBJNAME || info.sobjname;
601
+
602
+ if (methodName) {
603
+ console.log(` Method: ${methodName}`);
604
+ }
605
+ console.log(` Line ${line}:`);
606
+ if (sobjname && sobjname.includes('====')) {
607
+ console.log(` Include: ${sobjname}`);
608
+ }
609
+ console.log(` ${text}`);
547
610
  }
548
611
  }
549
612
  } else if (success === true || success === 'X') {
@@ -630,7 +693,7 @@ async function runUnitTests(options) {
630
693
  /**
631
694
  * Run unit test for a single file
632
695
  */
633
- async function runUnitTestForFile(sourceFile, csrfToken, config) {
696
+ async function runUnitTestForFile(sourceFile, csrfToken, config, coverage = false) {
634
697
  console.log(` Running unit test for: ${sourceFile}`);
635
698
 
636
699
  try {
@@ -665,7 +728,8 @@ async function runUnitTestForFile(sourceFile, csrfToken, config) {
665
728
 
666
729
  // Send files array to unit endpoint (ABAP expects string_table of file names)
667
730
  const data = {
668
- files: [sourceFile]
731
+ files: [sourceFile],
732
+ coverage: coverage
669
733
  };
670
734
 
671
735
  const result = await request('POST', '/sap/bc/z_abapgit_agent/unit', data, { csrfToken });
@@ -678,6 +742,9 @@ async function runUnitTestForFile(sourceFile, csrfToken, config) {
678
742
  const message = result.MESSAGE || result.message || '';
679
743
  const errors = result.ERRORS || result.errors || [];
680
744
 
745
+ // Handle coverage data
746
+ const coverageStats = result.COVERAGE_STATS || result.coverage_stats;
747
+
681
748
  if (testCount === 0) {
682
749
  console.log(` ➖ ${objName} - No unit tests`);
683
750
  } else if (success === 'X' || success === true) {
@@ -688,6 +755,17 @@ async function runUnitTestForFile(sourceFile, csrfToken, config) {
688
755
 
689
756
  console.log(` Tests: ${testCount} | Passed: ${passedCount} | Failed: ${failedCount}`);
690
757
 
758
+ // Display coverage if available
759
+ if (coverage && coverageStats) {
760
+ const totalLines = coverageStats.TOTAL_LINES || coverageStats.total_lines || 0;
761
+ const coveredLines = coverageStats.COVERED_LINES || coverageStats.covered_lines || 0;
762
+ const coverageRate = coverageStats.COVERAGE_RATE || coverageStats.coverage_rate || 0;
763
+
764
+ console.log(` 📊 Coverage: ${coverageRate}%`);
765
+ console.log(` Total Lines: ${totalLines}`);
766
+ console.log(` Covered Lines: ${coveredLines}`);
767
+ }
768
+
691
769
  if (failedCount > 0 && errors.length > 0) {
692
770
  for (const err of errors) {
693
771
  const className = err.CLASS_NAME || err.class_name || '?';
@@ -706,11 +784,11 @@ async function runUnitTestForFile(sourceFile, csrfToken, config) {
706
784
  /**
707
785
  * Run tree command and return raw result
708
786
  */
709
- async function runTreeCommand(packageName, depth, includeObjects, csrfToken, config) {
787
+ async function runTreeCommand(packageName, depth, includeTypes, csrfToken, config) {
710
788
  const data = {
711
789
  package: packageName,
712
790
  depth: depth,
713
- include_objects: includeObjects
791
+ include_objects: includeTypes
714
792
  };
715
793
 
716
794
  return await request('POST', '/sap/bc/z_abapgit_agent/tree', data, { csrfToken });
@@ -719,13 +797,13 @@ async function runTreeCommand(packageName, depth, includeObjects, csrfToken, con
719
797
  /**
720
798
  * Display tree output in human-readable format
721
799
  */
722
- async function displayTreeOutput(packageName, depth, includeObjects) {
800
+ async function displayTreeOutput(packageName, depth, includeTypes) {
723
801
  const config = loadConfig();
724
802
  const csrfToken = await fetchCsrfToken(config);
725
803
 
726
804
  console.log(`\n Getting package tree for: ${packageName}`);
727
805
 
728
- const result = await runTreeCommand(packageName, depth, includeObjects, csrfToken, config);
806
+ const result = await runTreeCommand(packageName, depth, includeTypes, csrfToken, config);
729
807
 
730
808
  // Handle uppercase keys from ABAP
731
809
  const success = result.SUCCESS || result.success;
@@ -765,7 +843,7 @@ async function displayTreeOutput(packageName, depth, includeObjects) {
765
843
  console.log(` OBJECTS: ${totalObjects}`);
766
844
 
767
845
  // Display object types if available
768
- if (includeObjects && objectTypes.length > 0) {
846
+ if (includeTypes && objectTypes.length > 0) {
769
847
  const typeStr = objectTypes.map(t => `${t.OBJECT || t.object}=${t.COUNT || t.count}`).join(' ');
770
848
  console.log(` TYPES: ${typeStr}`);
771
849
  }
@@ -1068,12 +1146,86 @@ async function pull(gitUrl, branch = 'main', files = null, transportRequest = nu
1068
1146
  }
1069
1147
  }
1070
1148
 
1149
+ /**
1150
+ * Copy a file if source exists (helper for init --update)
1151
+ * @param {string} srcPath - Source file path
1152
+ * @param {string} destPath - Destination file path
1153
+ * @param {string} label - Label for console output
1154
+ * @param {boolean} createParentDir - Whether to create parent directory
1155
+ * @returns {Promise<void>}
1156
+ */
1157
+ async function copyFileIfExists(srcPath, destPath, label, createParentDir = false) {
1158
+ try {
1159
+ if (fs.existsSync(srcPath)) {
1160
+ if (createParentDir) {
1161
+ const parentDir = pathModule.dirname(destPath);
1162
+ if (!fs.existsSync(parentDir)) {
1163
+ fs.mkdirSync(parentDir, { recursive: true });
1164
+ }
1165
+ }
1166
+ fs.copyFileSync(srcPath, destPath);
1167
+ console.log(`✅ Updated ${label}`);
1168
+ } else {
1169
+ console.log(`⚠️ ${label} not found in abapgit-agent`);
1170
+ }
1171
+ } catch (error) {
1172
+ console.error(`Error copying ${label}: ${error.message}`);
1173
+ }
1174
+ }
1175
+
1176
+ /**
1177
+ * Copy guidelines folder (helper for init --update)
1178
+ * @param {string} srcPath - Source folder path
1179
+ * @param {string} destPath - Destination folder path
1180
+ * @param {boolean} overwrite - Whether to overwrite existing files
1181
+ * @returns {Promise<void>}
1182
+ */
1183
+ async function copyGuidelinesFolder(srcPath, destPath, overwrite = false) {
1184
+ try {
1185
+ if (fs.existsSync(srcPath)) {
1186
+ // Create destination directory if needed
1187
+ if (!fs.existsSync(destPath)) {
1188
+ fs.mkdirSync(destPath, { recursive: true });
1189
+ }
1190
+
1191
+ const files = fs.readdirSync(srcPath);
1192
+ let copied = 0;
1193
+
1194
+ for (const file of files) {
1195
+ if (file.endsWith('.md')) {
1196
+ const srcFile = pathModule.join(srcPath, file);
1197
+ const destFile = pathModule.join(destPath, file);
1198
+
1199
+ // Skip if file exists and not overwriting
1200
+ if (fs.existsSync(destFile) && !overwrite) {
1201
+ continue;
1202
+ }
1203
+
1204
+ fs.copyFileSync(srcFile, destFile);
1205
+ copied++;
1206
+ }
1207
+ }
1208
+
1209
+ if (copied > 0) {
1210
+ console.log(`✅ Updated guidelines/ (${copied} files)`);
1211
+ } else {
1212
+ console.log(`⚠️ No guideline files found`);
1213
+ }
1214
+ } else {
1215
+ console.log(`⚠️ guidelines folder not found in abapgit-agent`);
1216
+ }
1217
+ } catch (error) {
1218
+ console.error(`Error copying guidelines: ${error.message}`);
1219
+ }
1220
+ }
1221
+
1071
1222
  /**
1072
1223
  * Run init command - Initialize local configuration
1073
1224
  */
1074
1225
  async function runInit(args) {
1075
1226
  const folderArgIndex = args.indexOf('--folder');
1076
1227
  const packageArgIndex = args.indexOf('--package');
1228
+ const updateMode = args.includes('--update');
1077
1229
 
1078
1230
  // Get parameters
1079
1231
  const folder = folderArgIndex !== -1 && folderArgIndex + 1 < args.length
@@ -1084,18 +1236,14 @@ async function runInit(args) {
1084
1236
  ? args[packageArgIndex + 1]
1085
1237
  : null;
1086
1238
 
1087
- // Validate package is required
1088
- if (!packageName) {
1239
+ // Validate package is required for non-update mode
1240
+ if (!updateMode && !packageName) {
1089
1241
  console.error('Error: --package is required');
1090
1242
  console.error('Usage: abapgit-agent init --folder /src --package ZMY_PACKAGE');
1243
+ console.error(' abapgit-agent init --update # Update files to latest version');
1091
1244
  process.exit(1);
1092
1245
  }
1093
1246
 
1094
- console.log(`\n🚀 Initializing abapGit Agent for local repository`);
1095
- console.log(` Folder: ${folder}`);
1096
- console.log(` Package: ${packageName}`);
1097
- console.log('');
1098
-
1099
1247
  // Check if current folder is git repo root
1100
1248
  const gitDir = pathModule.join(process.cwd(), '.git');
1101
1249
  if (!fs.existsSync(gitDir)) {
@@ -1104,6 +1252,53 @@ async function runInit(args) {
1104
1252
  process.exit(1);
1105
1253
  }
1106
1254
 
1255
+ // In update mode, just copy the files and return
1256
+ if (updateMode) {
1257
+ console.log(`\n🔄 Updating abapGit Agent files`);
1258
+ console.log('');
1259
+
1260
+ // Copy CLAUDE.md
1261
+ await copyFileIfExists(
1262
+ pathModule.join(__dirname, '..', 'abap', 'CLAUDE.md'),
1263
+ pathModule.join(process.cwd(), 'CLAUDE.md'),
1264
+ 'CLAUDE.md'
1265
+ );
1266
+
1267
+ // Copy copilot-instructions.md
1268
+ await copyFileIfExists(
1269
+ pathModule.join(__dirname, '..', 'abap', '.github', 'copilot-instructions.md'),
1270
+ pathModule.join(process.cwd(), '.github', 'copilot-instructions.md'),
1271
+ '.github/copilot-instructions.md',
1272
+ true // create parent dir
1273
+ );
1274
+
1275
+ // Copy guidelines folder to project root
1276
+ await copyGuidelinesFolder(
1277
+ pathModule.join(__dirname, '..', 'abap', 'guidelines'),
1278
+ pathModule.join(process.cwd(), 'guidelines'),
1279
+ true // overwrite
1280
+ );
1281
+
1282
+ console.log(`
1283
+ 📋 Update complete!
1284
+ Run 'abapgit-agent ref --list-topics' to see available topics.
1285
+ `);
1286
+ return;
1287
+ }
1288
+
1289
+ // Validate package is required
1290
+ if (!packageName) {
1291
+ console.error('Error: --package is required');
1292
+ console.error('Usage: abapgit-agent init --folder /src --package ZMY_PACKAGE');
1293
+ console.error(' abapgit-agent init --update # Update files to latest version');
1294
+ process.exit(1);
1295
+ }
1296
+
1297
+ console.log(`\n🚀 Initializing abapGit Agent for local repository`);
1298
+ console.log(` Folder: ${folder}`);
1299
+ console.log(` Package: ${packageName}`);
1300
+ console.log('');
1301
+
1107
1302
  // Detect git remote URL
1108
1303
  const gitUrl = getGitRemoteUrl();
1109
1304
  if (!gitUrl) {
@@ -1188,7 +1383,7 @@ async function runInit(args) {
1188
1383
  }
1189
1384
 
1190
1385
  // Copy copilot-instructions.md for GitHub Copilot
1191
- const copilotMdPath = pathModule.join(__dirname, '..', 'abap', 'copilot-instructions.md');
1386
+ const copilotMdPath = pathModule.join(__dirname, '..', 'abap', '.github', 'copilot-instructions.md');
1192
1387
  const githubDir = pathModule.join(process.cwd(), '.github');
1193
1388
  const localCopilotMdPath = pathModule.join(githubDir, 'copilot-instructions.md');
1194
1389
  try {
@@ -1206,6 +1401,35 @@ async function runInit(args) {
1206
1401
  console.error(`Error copying copilot-instructions.md: ${error.message}`);
1207
1402
  }
1208
1403
 
1404
+ // Copy guidelines folder to project root
1405
+ const guidelinesSrcPath = pathModule.join(__dirname, '..', 'abap', 'guidelines');
1406
+ const guidelinesDestPath = pathModule.join(process.cwd(), 'guidelines');
1407
+ try {
1408
+ if (fs.existsSync(guidelinesSrcPath)) {
1409
+ if (!fs.existsSync(guidelinesDestPath)) {
1410
+ // Create guidelines directory
1411
+ fs.mkdirSync(guidelinesDestPath, { recursive: true });
1412
+ // Copy all files from guidelines folder
1413
+ const files = fs.readdirSync(guidelinesSrcPath);
1414
+ for (const file of files) {
1415
+ if (file.endsWith('.md')) {
1416
+ fs.copyFileSync(
1417
+ pathModule.join(guidelinesSrcPath, file),
1418
+ pathModule.join(guidelinesDestPath, file)
1419
+ );
1420
+ }
1421
+ }
1422
+ console.log(`✅ Created guidelines/ (${files.filter(f => f.endsWith('.md')).length} files)`);
1423
+ } else {
1424
+ console.log(`⚠️ guidelines/ already exists, skipped`);
1425
+ }
1426
+ } else {
1427
+ console.log(`⚠️ guidelines folder not found in abap/ directory`);
1428
+ }
1429
+ } catch (error) {
1430
+ console.error(`Error copying guidelines: ${error.message}`);
1431
+ }
1432
+
1209
1433
  // Create folder
1210
1434
  const folderPath = pathModule.join(process.cwd(), folder);
1211
1435
  try {
@@ -1253,8 +1477,9 @@ async function main() {
1253
1477
  const command = args[0];
1254
1478
 
1255
1479
  // Check if ABAP integration is enabled for this repo
1256
- // (skip for init, help commands)
1257
- if (command && command !== 'init' && command !== 'help' && command !== '--help' && command !== '-h') {
1480
+ // (skip for init, help, ref commands - these don't need ABAP connection)
1481
+ const noAbapRequired = ['init', 'help', '--help', '-h', 'ref'];
1482
+ if (command && !noAbapRequired.includes(command)) {
1258
1483
  if (!isAbapIntegrationEnabled()) {
1259
1484
  console.log(`
1260
1485
  ⚠️ ABAP Git Agent not configured for this repository.
@@ -1278,7 +1503,7 @@ To enable integration:
1278
1503
  }
1279
1504
 
1280
1505
  // Version compatibility check for commands that interact with ABAP
1281
- const abapCommands = ['create', 'import', 'pull', 'inspect', 'unit', 'tree', 'view', 'preview'];
1506
+ const abapCommands = ['create', 'import', 'pull', 'inspect', 'unit', 'tree', 'view', 'preview', 'list'];
1282
1507
  if (command && abapCommands.includes(command)) {
1283
1508
  await checkVersionCompatibility();
1284
1509
  }
@@ -1558,21 +1783,25 @@ Examples:
1558
1783
  const filesArgIndex = args.indexOf('--files');
1559
1784
  if (filesArgIndex === -1 || filesArgIndex + 1 >= args.length) {
1560
1785
  console.error('Error: --files parameter required');
1561
- console.error('Usage: abapgit-agent unit --files <file1>,<file2>,...');
1786
+ console.error('Usage: abapgit-agent unit --files <file1>,<file2>,... [--coverage]');
1562
1787
  console.error('Example: abapgit-agent unit --files zcl_my_test.clas.abap');
1788
+ console.error('Example: abapgit-agent unit --files zcl_my_test.clas.abap --coverage');
1563
1789
  process.exit(1);
1564
1790
  }
1565
1791
 
1566
1792
  const files = args[filesArgIndex + 1].split(',').map(f => f.trim());
1567
1793
 
1568
- console.log(`\n Running unit tests for ${files.length} file(s)`);
1794
+ // Check for coverage option
1795
+ const coverage = args.includes('--coverage');
1796
+
1797
+ console.log(`\n Running unit tests for ${files.length} file(s)${coverage ? ' (with coverage)' : ''}`);
1569
1798
  console.log('');
1570
1799
 
1571
1800
  const config = loadConfig();
1572
1801
  const csrfToken = await fetchCsrfToken(config);
1573
1802
 
1574
1803
  for (const sourceFile of files) {
1575
- await runUnitTestForFile(sourceFile, csrfToken, config);
1804
+ await runUnitTestForFile(sourceFile, csrfToken, config, coverage);
1576
1805
  }
1577
1806
  break;
1578
1807
  }
@@ -1581,7 +1810,7 @@ Examples:
1581
1810
  const packageArgIndex = args.indexOf('--package');
1582
1811
  if (packageArgIndex === -1) {
1583
1812
  console.error('Error: --package parameter required');
1584
- console.error('Usage: abapgit-agent tree --package <package> [--depth <n>] [--include-objects] [--json]');
1813
+ console.error('Usage: abapgit-agent tree --package <package> [--depth <n>] [--include-types] [--json]');
1585
1814
  console.error('Example: abapgit-agent tree --package ZMY_PACKAGE');
1586
1815
  process.exit(1);
1587
1816
  }
@@ -1595,7 +1824,7 @@ Examples:
1595
1824
  console.error(' or escape the $ character:');
1596
1825
  console.error(' abapgit-agent tree --package \\$ZMY_PACKAGE');
1597
1826
  console.error('');
1598
- console.error('Usage: abapgit-agent tree --package <package> [--depth <n>] [--include-objects] [--json]');
1827
+ console.error('Usage: abapgit-agent tree --package <package> [--depth <n>] [--include-types] [--json]');
1599
1828
  console.error('Example: abapgit-agent tree --package ZMY_PACKAGE');
1600
1829
  process.exit(1);
1601
1830
  }
@@ -1605,7 +1834,7 @@ Examples:
1605
1834
  // Check for empty/whitespace-only package name
1606
1835
  if (!packageName || packageName.trim() === '') {
1607
1836
  console.error('Error: --package parameter cannot be empty');
1608
- console.error('Usage: abapgit-agent tree --package <package> [--depth <n>] [--include-objects] [--json]');
1837
+ console.error('Usage: abapgit-agent tree --package <package> [--depth <n>] [--include-types] [--json]');
1609
1838
  console.error('Example: abapgit-agent tree --package ZMY_PACKAGE');
1610
1839
  process.exit(1);
1611
1840
  }
@@ -1621,8 +1850,8 @@ Examples:
1621
1850
  }
1622
1851
  }
1623
1852
 
1624
- // Optional include-objects parameter
1625
- const includeObjects = args.includes('--include-objects');
1853
+ // Optional include-types parameter (--include-objects is deprecated alias)
1854
+ const includeTypes = args.includes('--include-types') || args.includes('--include-objects');
1626
1855
 
1627
1856
  // Optional json parameter
1628
1857
  const jsonOutput = args.includes('--json');
@@ -1630,10 +1859,141 @@ Examples:
1630
1859
  if (jsonOutput) {
1631
1860
  const config = loadConfig();
1632
1861
  const csrfToken = await fetchCsrfToken(config);
1633
- const result = await runTreeCommand(packageName, depth, includeObjects, csrfToken, config);
1862
+ const result = await runTreeCommand(packageName, depth, includeTypes, csrfToken, config);
1634
1863
  console.log(JSON.stringify(result, null, 2));
1635
1864
  } else {
1636
- await displayTreeOutput(packageName, depth, includeObjects);
1865
+ await displayTreeOutput(packageName, depth, includeTypes);
1866
+ }
1867
+ break;
1868
+ }
1869
+
1870
+ case 'list': {
1871
+ const packageArgIndex = args.indexOf('--package');
1872
+ if (packageArgIndex === -1) {
1873
+ console.error('Error: --package parameter required');
1874
+ console.error('Usage: abapgit-agent list --package <package> [--type <types>] [--name <pattern>] [--limit <n>] [--offset <n>] [--json]');
1875
+ console.error('Example: abapgit-agent list --package $ZMY_PACKAGE');
1876
+ console.error('Example: abapgit-agent list --package $ZMY_PACKAGE --type CLAS,INTF');
1877
+ process.exit(1);
1878
+ }
1879
+
1880
+ // Check if package value is missing
1881
+ if (packageArgIndex + 1 >= args.length) {
1882
+ console.error('Error: --package parameter value is missing');
1883
+ process.exit(1);
1884
+ }
1885
+
1886
+ const packageName = args[packageArgIndex + 1];
1887
+
1888
+ // Validate package name
1889
+ if (!packageName || packageName.trim() === '') {
1890
+ console.error('Error: --package parameter cannot be empty');
1891
+ process.exit(1);
1892
+ }
1893
+
1894
+ // Optional type parameter
1895
+ const typeArgIndex = args.indexOf('--type');
1896
+ const type = typeArgIndex !== -1 && typeArgIndex + 1 < args.length ? args[typeArgIndex + 1] : null;
1897
+
1898
+ // Optional name pattern
1899
+ const nameArgIndex = args.indexOf('--name');
1900
+ const name = nameArgIndex !== -1 && nameArgIndex + 1 < args.length ? args[nameArgIndex + 1] : null;
1901
+
1902
+ // Optional limit
1903
+ const limitArgIndex = args.indexOf('--limit');
1904
+ let limit = 100;
1905
+ if (limitArgIndex !== -1 && limitArgIndex + 1 < args.length) {
1906
+ limit = parseInt(args[limitArgIndex + 1], 10);
1907
+ if (isNaN(limit) || limit < 1) {
1908
+ console.error('Error: --limit must be a positive number');
1909
+ process.exit(1);
1910
+ }
1911
+ if (limit > 1000) {
1912
+ console.error('Error: --limit value too high (max: 1000)');
1913
+ process.exit(1);
1914
+ }
1915
+ }
1916
+
1917
+ // Optional offset
1918
+ const offsetArgIndex = args.indexOf('--offset');
1919
+ let offset = 0;
1920
+ if (offsetArgIndex !== -1 && offsetArgIndex + 1 < args.length) {
1921
+ offset = parseInt(args[offsetArgIndex + 1], 10);
1922
+ if (isNaN(offset) || offset < 0) {
1923
+ console.error('Error: --offset must be a non-negative number');
1924
+ process.exit(1);
1925
+ }
1926
+ }
1927
+
1928
+ // Optional json parameter
1929
+ const jsonOutput = args.includes('--json');
1930
+
1931
+ const config = loadConfig();
1932
+ const csrfToken = await fetchCsrfToken(config);
1933
+
1934
+ const data = {
1935
+ package: packageName,
1936
+ limit: limit,
1937
+ offset: offset
1938
+ };
1939
+
1940
+ if (type) {
1941
+ data.type = type;
1942
+ }
1943
+
1944
+ if (name) {
1945
+ data.name = name;
1946
+ }
1947
+
1948
+ const result = await request('POST', '/sap/bc/z_abapgit_agent/list', data, { csrfToken });
1949
+
1950
+ // Handle uppercase keys from ABAP
1951
+ const success = result.SUCCESS || result.success;
1952
+ const error = result.ERROR || result.error;
1953
+ const objects = result.OBJECTS || result.objects || [];
1954
+ const byType = result.BY_TYPE || result.by_type || [];
1955
+ const total = result.TOTAL || result.total || 0;
1956
+
1957
+ if (!success || error) {
1958
+ console.error(`\n Error: ${error || 'Failed to list objects'}`);
1959
+ process.exit(1);
1960
+ }
1961
+
1962
+ if (jsonOutput) {
1963
+ console.log(JSON.stringify(result, null, 2));
1964
+ } else {
1965
+ // Display human-readable output
1966
+ let title = `Objects in ${packageName}`;
1967
+ if (type) {
1968
+ title += ` (${type} only`;
1969
+ if (total !== objects.length) {
1970
+ title += `, Total: ${total}`;
1971
+ }
1972
+ title += ')';
1973
+ } else if (total !== objects.length) {
1974
+ title += ` (Total: ${total})`;
1975
+ }
1976
+ console.log(`\n${title}\n`);
1977
+
1978
+ // Group objects by type
1979
+ const objectsByType = {};
1980
+ for (const obj of objects) {
1981
+ const objType = (obj.TYPE || obj.type || '???').toUpperCase();
1982
+ if (!objectsByType[objType]) {
1983
+ objectsByType[objType] = [];
1984
+ }
1985
+ objectsByType[objType].push(obj.NAME || obj.name);
1986
+ }
1987
+
1988
+ // Display grouped objects
1989
+ for (const objType of Object.keys(objectsByType).sort()) {
1990
+ const objNames = objectsByType[objType];
1991
+ console.log(` ${objType} (${objNames.length})`);
1992
+ for (const objName of objNames) {
1993
+ console.log(` ${objName}`);
1994
+ }
1995
+ console.log('');
1996
+ }
1637
1997
  }
1638
1998
  break;
1639
1999
  }
@@ -1881,7 +2241,7 @@ Examples:
1881
2241
  }
1882
2242
 
1883
2243
  if (where) {
1884
- data.where = where;
2244
+ data.where = convertDatesInWhereClause(where);
1885
2245
  }
1886
2246
 
1887
2247
  if (columns) {
@@ -2079,6 +2439,87 @@ Examples:
2079
2439
  break;
2080
2440
  }
2081
2441
 
2442
+ case 'ref': {
2443
+ const refSearch = require('../src/ref-search');
2444
+ const topicIndex = args.indexOf('--topic');
2445
+ const listTopics = args.includes('--list-topics') || args.includes('-l');
2446
+ const listRepos = args.includes('--list-repos') || args.includes('-r');
2447
+ const exportGuidelines = args.includes('--export') || args.includes('-e');
2448
+ const jsonOutput = args.includes('--json');
2449
+
2450
+ if (exportGuidelines) {
2451
+ const result = await refSearch.exportGuidelines();
2452
+ if (jsonOutput) {
2453
+ console.log(JSON.stringify(result, null, 2));
2454
+ } else {
2455
+ refSearch.displayExportResult(result);
2456
+ }
2457
+ break;
2458
+ }
2459
+
2460
+ if (listRepos) {
2461
+ const result = await refSearch.listRepositories();
2462
+ if (jsonOutput) {
2463
+ console.log(JSON.stringify(result, null, 2));
2464
+ } else {
2465
+ refSearch.displayRepositories(result);
2466
+ }
2467
+ break;
2468
+ }
2469
+
2470
+ if (listTopics) {
2471
+ const result = await refSearch.listTopics();
2472
+ if (jsonOutput) {
2473
+ console.log(JSON.stringify(result, null, 2));
2474
+ } else {
2475
+ refSearch.displayTopics(result);
2476
+ }
2477
+ break;
2478
+ }
2479
+
2480
+ if (topicIndex !== -1 && topicIndex + 1 < args.length) {
2481
+ const topic = args[topicIndex + 1];
2482
+ const result = await refSearch.getTopic(topic);
2483
+ if (jsonOutput) {
2484
+ console.log(JSON.stringify(result, null, 2));
2485
+ } else {
2486
+ refSearch.displayTopic(result);
2487
+ }
2488
+ break;
2489
+ }
2490
+
2491
+ // Pattern search (default)
2492
+ const patternIndex = args.findIndex((arg, idx) => idx > 0 && !arg.startsWith('--'));
2493
+ if (patternIndex === -1) {
2494
+ console.error('Error: No pattern specified');
2495
+ console.error('');
2496
+ console.error('Usage:');
2497
+ console.error(' abapgit-agent ref <pattern> Search for pattern');
2498
+ console.error(' abapgit-agent ref --topic <name> View specific topic');
2499
+ console.error(' abapgit-agent ref --list-topics List available topics');
2500
+ console.error(' abapgit-agent ref --list-repos List reference repositories');
2501
+ console.error(' abapgit-agent ref --export Export local guidelines to reference folder');
2502
+ console.error('');
2503
+ console.error('Examples:');
2504
+ console.error(' abapgit-agent ref "CORRESPONDING"');
2505
+ console.error(' abapgit-agent ref --topic exceptions');
2506
+ console.error(' abapgit-agent ref --list-topics');
2507
+ console.error(' abapgit-agent ref --list-repos');
2508
+ console.error(' abapgit-agent ref --export');
2509
+ process.exit(1);
2510
+ }
2511
+
2512
+ const pattern = args[patternIndex];
2513
+ const result = await refSearch.searchPattern(pattern);
2514
+
2515
+ if (jsonOutput) {
2516
+ console.log(JSON.stringify(result, null, 2));
2517
+ } else {
2518
+ refSearch.displaySearchResults(result);
2519
+ }
2520
+ break;
2521
+ }
2522
+
2082
2523
  case 'help':
2083
2524
  case '--help':
2084
2525
  case '-h':
@@ -2091,6 +2532,8 @@ Usage:
2091
2532
  Commands:
2092
2533
  init --folder <folder> --package <package>
2093
2534
  Initialize local configuration for an existing git repository.
2535
+ init --update
2536
+ Update existing files (CLAUDE.md, copilot-instructions.md, guidelines) to latest version.
2094
2537
 
2095
2538
  create
2096
2539
  Create abapGit online repository in ABAP system.
@@ -2112,12 +2555,30 @@ Commands:
2112
2555
  unit --files <file1>,<file2>,...
2113
2556
  Run AUnit tests for ABAP test class files (.testclasses.abap)
2114
2557
 
2115
- tree --package <package> [--depth <n>] [--include-objects] [--json]
2558
+ tree --package <package> [--depth <n>] [--include-types] [--json]
2116
2559
  Display package hierarchy tree from ABAP system
2117
2560
 
2561
+ list --package <package> [--type <types>] [--name <pattern>] [--limit <n>] [--offset <n>] [--json]
2562
+ List ABAP objects in a package with filtering and pagination
2563
+
2118
2564
  view --objects <obj1>,<obj2>,... [--type <type>] [--json]
2119
2565
  View ABAP object definitions from the ABAP system
2120
2566
 
2567
+ ref <pattern> [--json]
2568
+ Search ABAP reference repositories for patterns. Requires referenceFolder in .abapGitAgent.
2569
+
2570
+ ref --topic <topic> [--json]
2571
+ View specific topic from cheat sheets (exceptions, sql, unit-tests, etc.)
2572
+
2573
+ ref --list-topics
2574
+ List available topics for reference search
2575
+
2576
+ ref --list-repos
2577
+ List all reference repositories in the reference folder
2578
+
2579
+ ref --export
2580
+ Export custom guidelines (abap/guidelines/) to reference folder
2581
+
2121
2582
  health
2122
2583
  Check if ABAP REST API is healthy
2123
2584
 
@@ -2126,6 +2587,7 @@ Commands:
2126
2587
 
2127
2588
  Examples:
2128
2589
  abapgit-agent init --folder /src --package ZMY_PACKAGE # Initialize
2590
+ abapgit-agent init --update # Update files to latest
2129
2591
  abapgit-agent create # Create repo
2130
2592
  abapgit-agent import # Import objects to git
2131
2593
  abapgit-agent import --message "Initial import" # Import with message
@@ -2135,14 +2597,27 @@ Examples:
2135
2597
  abapgit-agent pull --transport DEVK900001 # With transport
2136
2598
  abapgit-agent inspect --files zcl_my_class.clas.abap # Syntax check
2137
2599
  abapgit-agent unit --files zcl_my_test.clas.testclasses.abap # Run tests
2138
- abapgit-agent tree --package $ZMY_PACKAGE # Show package tree
2139
- abapgit-agent tree --package $ZMY_PACKAGE --depth 2 # Shallow tree
2140
- abapgit-agent tree --package $ZMY_PACKAGE --include-objects # With object counts
2141
- abapgit-agent tree --package $ZMY_PACKAGE --json # JSON output
2600
+ abapgit-agent tree --package \$ZMY_PACKAGE # Show package tree
2601
+ abapgit-agent tree --package \$ZMY_PACKAGE --depth 2 # Shallow tree
2602
+ abapgit-agent tree --package \$ZMY_PACKAGE --include-types # With type counts
2603
+ abapgit-agent tree --package \$ZMY_PACKAGE --json # JSON output
2604
+ abapgit-agent list --package $ZMY_PACKAGE # List all objects
2605
+ abapgit-agent list --package $ZMY_PACKAGE --type CLAS,INTF # Filter by type
2606
+ abapgit-agent list --package $ZMY_PACKAGE --name ZCL_* # Filter by name
2607
+ abapgit-agent list --package $ZMY_PACKAGE --limit 50 # Limit results
2608
+ abapgit-agent list --package $ZMY_PACKAGE --json # JSON output
2142
2609
  abapgit-agent view --objects ZCL_MY_CLASS # View class definition
2143
2610
  abapgit-agent view --objects ZIF_MY_INTERFACE --type INTF # View interface
2144
2611
  abapgit-agent view --objects ZMY_TABLE --type TABL # View table structure
2145
2612
  abapgit-agent view --objects ZCL_CLASS1,ZCL_CLASS2 --json # Multiple objects
2613
+ abapgit-agent ref "CORRESPONDING" # Search all reference repos
2614
+ abapgit-agent ref "CX_SY_" # Search exceptions
2615
+ abapgit-agent ref --topic exceptions # View exception topic
2616
+ abapgit-agent ref --topic sql # View SQL topic
2617
+ abapgit-agent ref --topic unit-tests # View unit test topic
2618
+ abapgit-agent ref --list-topics # List all topics
2619
+ abapgit-agent ref --list-repos # List reference repositories
2620
+ abapgit-agent ref --export # Export custom guidelines
2146
2621
  abapgit-agent health
2147
2622
  abapgit-agent status
2148
2623
  `);