abapgit-agent 1.4.0 → 1.6.0

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 (115) hide show
  1. package/README.md +2 -0
  2. package/abap/guidelines/00_index.md +35 -0
  3. package/abap/guidelines/01_sql.md +72 -0
  4. package/abap/guidelines/02_exceptions.md +108 -0
  5. package/abap/guidelines/03_testing.md +252 -0
  6. package/abap/guidelines/04_cds.md +120 -0
  7. package/abap/guidelines/05_classes.md +50 -0
  8. package/abap/guidelines/06_objects.md +103 -0
  9. package/abap/guidelines/07_json.md +22 -0
  10. package/abap/guidelines/08_abapgit.md +193 -0
  11. package/bin/abapgit-agent +597 -44
  12. package/bin/abgagt +24 -0
  13. package/package.json +11 -3
  14. package/src/abap-client.js +65 -2
  15. package/src/agent.js +58 -4
  16. package/src/config.js +9 -2
  17. package/src/ref-search.js +989 -0
  18. package/.abapGitAgent.example +0 -11
  19. package/.github/workflows/release.yml +0 -57
  20. package/API.md +0 -710
  21. package/CLAUDE.md +0 -1031
  22. package/CLAUDE_MEM.md +0 -88
  23. package/ERROR_HANDLING.md +0 -30
  24. package/INSTALL.md +0 -155
  25. package/RELEASE_NOTES.md +0 -143
  26. package/abap/CLAUDE.md +0 -898
  27. package/abap/copilot-instructions.md +0 -79
  28. package/abap/package.devc.xml +0 -10
  29. package/abap/zcl_abgagt_agent.clas.abap +0 -420
  30. package/abap/zcl_abgagt_agent.clas.xml +0 -15
  31. package/abap/zcl_abgagt_cmd_factory.clas.abap +0 -48
  32. package/abap/zcl_abgagt_cmd_factory.clas.xml +0 -15
  33. package/abap/zcl_abgagt_command_create.clas.abap +0 -95
  34. package/abap/zcl_abgagt_command_create.clas.xml +0 -15
  35. package/abap/zcl_abgagt_command_import.clas.abap +0 -138
  36. package/abap/zcl_abgagt_command_import.clas.xml +0 -15
  37. package/abap/zcl_abgagt_command_inspect.clas.abap +0 -411
  38. package/abap/zcl_abgagt_command_inspect.clas.testclasses.abap +0 -121
  39. package/abap/zcl_abgagt_command_inspect.clas.xml +0 -16
  40. package/abap/zcl_abgagt_command_preview.clas.abap +0 -386
  41. package/abap/zcl_abgagt_command_preview.clas.xml +0 -15
  42. package/abap/zcl_abgagt_command_pull.clas.abap +0 -80
  43. package/abap/zcl_abgagt_command_pull.clas.testclasses.abap +0 -87
  44. package/abap/zcl_abgagt_command_pull.clas.xml +0 -16
  45. package/abap/zcl_abgagt_command_tree.clas.abap +0 -237
  46. package/abap/zcl_abgagt_command_tree.clas.xml +0 -15
  47. package/abap/zcl_abgagt_command_unit.clas.abap +0 -297
  48. package/abap/zcl_abgagt_command_unit.clas.xml +0 -15
  49. package/abap/zcl_abgagt_command_view.clas.abap +0 -240
  50. package/abap/zcl_abgagt_command_view.clas.xml +0 -15
  51. package/abap/zcl_abgagt_resource_create.clas.abap +0 -71
  52. package/abap/zcl_abgagt_resource_create.clas.xml +0 -15
  53. package/abap/zcl_abgagt_resource_health.clas.abap +0 -25
  54. package/abap/zcl_abgagt_resource_health.clas.xml +0 -15
  55. package/abap/zcl_abgagt_resource_import.clas.abap +0 -66
  56. package/abap/zcl_abgagt_resource_import.clas.xml +0 -15
  57. package/abap/zcl_abgagt_resource_inspect.clas.abap +0 -62
  58. package/abap/zcl_abgagt_resource_inspect.clas.xml +0 -15
  59. package/abap/zcl_abgagt_resource_preview.clas.abap +0 -67
  60. package/abap/zcl_abgagt_resource_preview.clas.xml +0 -15
  61. package/abap/zcl_abgagt_resource_pull.clas.abap +0 -71
  62. package/abap/zcl_abgagt_resource_pull.clas.xml +0 -15
  63. package/abap/zcl_abgagt_resource_tree.clas.abap +0 -70
  64. package/abap/zcl_abgagt_resource_tree.clas.xml +0 -15
  65. package/abap/zcl_abgagt_resource_unit.clas.abap +0 -64
  66. package/abap/zcl_abgagt_resource_unit.clas.xml +0 -15
  67. package/abap/zcl_abgagt_resource_view.clas.abap +0 -68
  68. package/abap/zcl_abgagt_resource_view.clas.xml +0 -15
  69. package/abap/zcl_abgagt_rest_handler.clas.abap +0 -32
  70. package/abap/zcl_abgagt_rest_handler.clas.xml +0 -15
  71. package/abap/zcl_abgagt_util.clas.abap +0 -93
  72. package/abap/zcl_abgagt_util.clas.testclasses.abap +0 -84
  73. package/abap/zcl_abgagt_util.clas.xml +0 -16
  74. package/abap/zcl_abgagt_viewer_clas.clas.abap +0 -58
  75. package/abap/zcl_abgagt_viewer_clas.clas.xml +0 -15
  76. package/abap/zcl_abgagt_viewer_ddls.clas.abap +0 -83
  77. package/abap/zcl_abgagt_viewer_ddls.clas.xml +0 -15
  78. package/abap/zcl_abgagt_viewer_dtel.clas.abap +0 -98
  79. package/abap/zcl_abgagt_viewer_dtel.clas.xml +0 -15
  80. package/abap/zcl_abgagt_viewer_factory.clas.abap +0 -41
  81. package/abap/zcl_abgagt_viewer_factory.clas.xml +0 -15
  82. package/abap/zcl_abgagt_viewer_intf.clas.abap +0 -58
  83. package/abap/zcl_abgagt_viewer_intf.clas.xml +0 -15
  84. package/abap/zcl_abgagt_viewer_stru.clas.abap +0 -59
  85. package/abap/zcl_abgagt_viewer_stru.clas.xml +0 -15
  86. package/abap/zcl_abgagt_viewer_tabl.clas.abap +0 -59
  87. package/abap/zcl_abgagt_viewer_tabl.clas.xml +0 -15
  88. package/abap/zcl_abgagt_viewer_ttyp.clas.abap +0 -93
  89. package/abap/zcl_abgagt_viewer_ttyp.clas.xml +0 -15
  90. package/abap/zif_abgagt_agent.intf.abap +0 -53
  91. package/abap/zif_abgagt_agent.intf.xml +0 -15
  92. package/abap/zif_abgagt_cmd_factory.intf.abap +0 -7
  93. package/abap/zif_abgagt_cmd_factory.intf.xml +0 -15
  94. package/abap/zif_abgagt_command.intf.abap +0 -26
  95. package/abap/zif_abgagt_command.intf.xml +0 -15
  96. package/abap/zif_abgagt_util.intf.abap +0 -28
  97. package/abap/zif_abgagt_util.intf.xml +0 -15
  98. package/abap/zif_abgagt_viewer.intf.abap +0 -12
  99. package/abap/zif_abgagt_viewer.intf.xml +0 -15
  100. package/docs/commands.md +0 -142
  101. package/docs/create-command.md +0 -129
  102. package/docs/health-command.md +0 -89
  103. package/docs/import-command.md +0 -195
  104. package/docs/init-command.md +0 -189
  105. package/docs/inspect-command.md +0 -158
  106. package/docs/preview-command.md +0 -528
  107. package/docs/pull-command.md +0 -188
  108. package/docs/status-command.md +0 -68
  109. package/docs/tree-command.md +0 -303
  110. package/docs/unit-command.md +0 -167
  111. package/docs/view-command.md +0 -501
  112. package/img/claude.png +0 -0
  113. package/scripts/claude-integration.js +0 -351
  114. package/scripts/release.sh +0 -60
  115. package/scripts/test-integration.js +0 -139
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,95 @@ 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
+
48
+ /**
49
+ * Get CLI version from package.json
50
+ */
51
+ function getCliVersion() {
52
+ const packageJsonPath = pathModule.join(__dirname, '..', 'package.json');
53
+ if (fs.existsSync(packageJsonPath)) {
54
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
55
+ return pkg.version || '1.0.0';
56
+ }
57
+ return '1.0.0';
58
+ }
59
+
60
+ /**
61
+ * Check version compatibility between CLI and ABAP API
62
+ */
63
+ async function checkVersionCompatibility() {
64
+ const cliVersion = getCliVersion();
65
+
66
+ try {
67
+ const config = loadConfig();
68
+ const https = require('https');
69
+ const url = new URL(`/sap/bc/z_abapgit_agent/health`, `https://${config.host}:${config.sapport}`);
70
+
71
+ return new Promise((resolve) => {
72
+ const options = {
73
+ hostname: url.hostname,
74
+ port: url.port,
75
+ path: url.pathname,
76
+ method: 'GET',
77
+ headers: {
78
+ 'Authorization': `Basic ${Buffer.from(`${config.user}:${config.password}`).toString('base64')}`,
79
+ 'sap-client': config.client,
80
+ 'sap-language': config.language || 'EN',
81
+ 'Content-Type': 'application/json'
82
+ },
83
+ agent: new https.Agent({ rejectUnauthorized: false })
84
+ };
85
+
86
+ const req = https.request(options, (res) => {
87
+ let body = '';
88
+ res.on('data', chunk => body += chunk);
89
+ res.on('end', () => {
90
+ try {
91
+ const result = JSON.parse(body);
92
+ const apiVersion = result.version || '1.0.0';
93
+
94
+ if (cliVersion !== apiVersion) {
95
+ console.log(`\n⚠️ Version mismatch: CLI ${cliVersion}, ABAP API ${apiVersion}`);
96
+ console.log(' Some commands may not work correctly.');
97
+ console.log(' Update ABAP code: abapgit-agent pull\n');
98
+ }
99
+ resolve({ cliVersion, apiVersion, compatible: cliVersion === apiVersion });
100
+ } catch (e) {
101
+ resolve({ cliVersion, apiVersion: null, compatible: false, error: e.message });
102
+ }
103
+ });
104
+ });
105
+
106
+ req.on('error', (e) => {
107
+ resolve({ cliVersion, apiVersion: null, compatible: false, error: e.message });
108
+ });
109
+ req.end();
110
+ });
111
+ } catch (error) {
112
+ return { cliVersion, apiVersion: null, compatible: false, error: error.message };
113
+ }
114
+ }
115
+
26
116
  /**
27
117
  * Load configuration from .abapGitAgent in current working directory
28
118
  */
@@ -42,10 +132,19 @@ function loadConfig() {
42
132
  password: process.env.ABAP_PASSWORD,
43
133
  language: process.env.ABAP_LANGUAGE || 'EN',
44
134
  gitUsername: process.env.GIT_USERNAME,
45
- gitPassword: process.env.GIT_PASSWORD
135
+ gitPassword: process.env.GIT_PASSWORD,
136
+ transport: process.env.ABAP_TRANSPORT
46
137
  };
47
138
  }
48
139
 
140
+ /**
141
+ * Get transport request from config or environment
142
+ */
143
+ function getTransport() {
144
+ const config = loadConfig();
145
+ return config.transport;
146
+ }
147
+
49
148
  /**
50
149
  * Check if ABAP integration is configured for this repo
51
150
  */
@@ -385,7 +484,7 @@ async function syntaxCheckSource(sourceFile, csrfToken, config) {
385
484
  /**
386
485
  * Inspect all files in one request
387
486
  */
388
- async function inspectAllFiles(files, csrfToken, config) {
487
+ async function inspectAllFiles(files, csrfToken, config, variant = null) {
389
488
  // Convert files to uppercase names (same as syntaxCheckSource does)
390
489
  const fileNames = files.map(f => {
391
490
  const baseName = pathModule.basename(f).toUpperCase();
@@ -398,6 +497,11 @@ async function inspectAllFiles(files, csrfToken, config) {
398
497
  files: fileNames
399
498
  };
400
499
 
500
+ // Add variant if specified
501
+ if (variant) {
502
+ data.variant = variant;
503
+ }
504
+
401
505
  const result = await request('POST', '/sap/bc/z_abapgit_agent/inspect', data, { csrfToken: csrfToken });
402
506
 
403
507
  // Handle both table result and old single result
@@ -624,11 +728,11 @@ async function runUnitTestForFile(sourceFile, csrfToken, config) {
624
728
  /**
625
729
  * Run tree command and return raw result
626
730
  */
627
- async function runTreeCommand(packageName, depth, includeObjects, csrfToken, config) {
731
+ async function runTreeCommand(packageName, depth, includeTypes, csrfToken, config) {
628
732
  const data = {
629
733
  package: packageName,
630
734
  depth: depth,
631
- include_objects: includeObjects
735
+ include_objects: includeTypes
632
736
  };
633
737
 
634
738
  return await request('POST', '/sap/bc/z_abapgit_agent/tree', data, { csrfToken });
@@ -637,13 +741,13 @@ async function runTreeCommand(packageName, depth, includeObjects, csrfToken, con
637
741
  /**
638
742
  * Display tree output in human-readable format
639
743
  */
640
- async function displayTreeOutput(packageName, depth, includeObjects) {
744
+ async function displayTreeOutput(packageName, depth, includeTypes) {
641
745
  const config = loadConfig();
642
746
  const csrfToken = await fetchCsrfToken(config);
643
747
 
644
748
  console.log(`\n Getting package tree for: ${packageName}`);
645
749
 
646
- const result = await runTreeCommand(packageName, depth, includeObjects, csrfToken, config);
750
+ const result = await runTreeCommand(packageName, depth, includeTypes, csrfToken, config);
647
751
 
648
752
  // Handle uppercase keys from ABAP
649
753
  const success = result.SUCCESS || result.success;
@@ -683,7 +787,7 @@ async function displayTreeOutput(packageName, depth, includeObjects) {
683
787
  console.log(` OBJECTS: ${totalObjects}`);
684
788
 
685
789
  // Display object types if available
686
- if (includeObjects && objectTypes.length > 0) {
790
+ if (includeTypes && objectTypes.length > 0) {
687
791
  const typeStr = objectTypes.map(t => `${t.OBJECT || t.object}=${t.COUNT || t.count}`).join(' ');
688
792
  console.log(` TYPES: ${typeStr}`);
689
793
  }
@@ -986,12 +1090,86 @@ async function pull(gitUrl, branch = 'main', files = null, transportRequest = nu
986
1090
  }
987
1091
  }
988
1092
 
1093
+ /**
1094
+ * Copy a file if source exists (helper for init --update)
1095
+ * @param {string} srcPath - Source file path
1096
+ * @param {string} destPath - Destination file path
1097
+ * @param {string} label - Label for console output
1098
+ * @param {boolean} createParentDir - Whether to create parent directory
1099
+ * @returns {Promise<void>}
1100
+ */
1101
+ async function copyFileIfExists(srcPath, destPath, label, createParentDir = false) {
1102
+ try {
1103
+ if (fs.existsSync(srcPath)) {
1104
+ if (createParentDir) {
1105
+ const parentDir = pathModule.dirname(destPath);
1106
+ if (!fs.existsSync(parentDir)) {
1107
+ fs.mkdirSync(parentDir, { recursive: true });
1108
+ }
1109
+ }
1110
+ fs.copyFileSync(srcPath, destPath);
1111
+ console.log(`✅ Updated ${label}`);
1112
+ } else {
1113
+ console.log(`⚠️ ${label} not found in abapgit-agent`);
1114
+ }
1115
+ } catch (error) {
1116
+ console.error(`Error copying ${label}: ${error.message}`);
1117
+ }
1118
+ }
1119
+
1120
+ /**
1121
+ * Copy guidelines folder (helper for init --update)
1122
+ * @param {string} srcPath - Source folder path
1123
+ * @param {string} destPath - Destination folder path
1124
+ * @param {boolean} overwrite - Whether to overwrite existing files
1125
+ * @returns {Promise<void>}
1126
+ */
1127
+ async function copyGuidelinesFolder(srcPath, destPath, overwrite = false) {
1128
+ try {
1129
+ if (fs.existsSync(srcPath)) {
1130
+ // Create destination directory if needed
1131
+ if (!fs.existsSync(destPath)) {
1132
+ fs.mkdirSync(destPath, { recursive: true });
1133
+ }
1134
+
1135
+ const files = fs.readdirSync(srcPath);
1136
+ let copied = 0;
1137
+
1138
+ for (const file of files) {
1139
+ if (file.endsWith('.md')) {
1140
+ const srcFile = pathModule.join(srcPath, file);
1141
+ const destFile = pathModule.join(destPath, file);
1142
+
1143
+ // Skip if file exists and not overwriting
1144
+ if (fs.existsSync(destFile) && !overwrite) {
1145
+ continue;
1146
+ }
1147
+
1148
+ fs.copyFileSync(srcFile, destFile);
1149
+ copied++;
1150
+ }
1151
+ }
1152
+
1153
+ if (copied > 0) {
1154
+ console.log(`✅ Updated guidelines/ (${copied} files)`);
1155
+ } else {
1156
+ console.log(`⚠️ No guideline files found`);
1157
+ }
1158
+ } else {
1159
+ console.log(`⚠️ guidelines folder not found in abapgit-agent`);
1160
+ }
1161
+ } catch (error) {
1162
+ console.error(`Error copying guidelines: ${error.message}`);
1163
+ }
1164
+ }
1165
+
989
1166
  /**
990
1167
  * Run init command - Initialize local configuration
991
1168
  */
992
1169
  async function runInit(args) {
993
1170
  const folderArgIndex = args.indexOf('--folder');
994
1171
  const packageArgIndex = args.indexOf('--package');
1172
+ const updateMode = args.includes('--update');
995
1173
 
996
1174
  // Get parameters
997
1175
  const folder = folderArgIndex !== -1 && folderArgIndex + 1 < args.length
@@ -1002,18 +1180,14 @@ async function runInit(args) {
1002
1180
  ? args[packageArgIndex + 1]
1003
1181
  : null;
1004
1182
 
1005
- // Validate package is required
1006
- if (!packageName) {
1183
+ // Validate package is required for non-update mode
1184
+ if (!updateMode && !packageName) {
1007
1185
  console.error('Error: --package is required');
1008
1186
  console.error('Usage: abapgit-agent init --folder /src --package ZMY_PACKAGE');
1187
+ console.error(' abapgit-agent init --update # Update files to latest version');
1009
1188
  process.exit(1);
1010
1189
  }
1011
1190
 
1012
- console.log(`\n🚀 Initializing abapGit Agent for local repository`);
1013
- console.log(` Folder: ${folder}`);
1014
- console.log(` Package: ${packageName}`);
1015
- console.log('');
1016
-
1017
1191
  // Check if current folder is git repo root
1018
1192
  const gitDir = pathModule.join(process.cwd(), '.git');
1019
1193
  if (!fs.existsSync(gitDir)) {
@@ -1022,6 +1196,53 @@ async function runInit(args) {
1022
1196
  process.exit(1);
1023
1197
  }
1024
1198
 
1199
+ // In update mode, just copy the files and return
1200
+ if (updateMode) {
1201
+ console.log(`\n🔄 Updating abapGit Agent files`);
1202
+ console.log('');
1203
+
1204
+ // Copy CLAUDE.md
1205
+ await copyFileIfExists(
1206
+ pathModule.join(__dirname, '..', 'abap', 'CLAUDE.md'),
1207
+ pathModule.join(process.cwd(), 'CLAUDE.md'),
1208
+ 'CLAUDE.md'
1209
+ );
1210
+
1211
+ // Copy copilot-instructions.md
1212
+ await copyFileIfExists(
1213
+ pathModule.join(__dirname, '..', 'abap', '.github', 'copilot-instructions.md'),
1214
+ pathModule.join(process.cwd(), '.github', 'copilot-instructions.md'),
1215
+ '.github/copilot-instructions.md',
1216
+ true // create parent dir
1217
+ );
1218
+
1219
+ // Copy guidelines folder to project root
1220
+ await copyGuidelinesFolder(
1221
+ pathModule.join(__dirname, '..', 'abap', 'guidelines'),
1222
+ pathModule.join(process.cwd(), 'guidelines'),
1223
+ true // overwrite
1224
+ );
1225
+
1226
+ console.log(`
1227
+ 📋 Update complete!
1228
+ Run 'abapgit-agent ref --list-topics' to see available topics.
1229
+ `);
1230
+ return;
1231
+ }
1232
+
1233
+ // Validate package is required
1234
+ if (!packageName) {
1235
+ console.error('Error: --package is required');
1236
+ console.error('Usage: abapgit-agent init --folder /src --package ZMY_PACKAGE');
1237
+ console.error(' abapgit-agent init --update # Update files to latest version');
1238
+ process.exit(1);
1239
+ }
1240
+
1241
+ console.log(`\n🚀 Initializing abapGit Agent for local repository`);
1242
+ console.log(` Folder: ${folder}`);
1243
+ console.log(` Package: ${packageName}`);
1244
+ console.log('');
1245
+
1025
1246
  // Detect git remote URL
1026
1247
  const gitUrl = getGitRemoteUrl();
1027
1248
  if (!gitUrl) {
@@ -1106,7 +1327,7 @@ async function runInit(args) {
1106
1327
  }
1107
1328
 
1108
1329
  // Copy copilot-instructions.md for GitHub Copilot
1109
- const copilotMdPath = pathModule.join(__dirname, '..', 'abap', 'copilot-instructions.md');
1330
+ const copilotMdPath = pathModule.join(__dirname, '..', 'abap', '.github', 'copilot-instructions.md');
1110
1331
  const githubDir = pathModule.join(process.cwd(), '.github');
1111
1332
  const localCopilotMdPath = pathModule.join(githubDir, 'copilot-instructions.md');
1112
1333
  try {
@@ -1124,6 +1345,35 @@ async function runInit(args) {
1124
1345
  console.error(`Error copying copilot-instructions.md: ${error.message}`);
1125
1346
  }
1126
1347
 
1348
+ // Copy guidelines folder to project root
1349
+ const guidelinesSrcPath = pathModule.join(__dirname, '..', 'abap', 'guidelines');
1350
+ const guidelinesDestPath = pathModule.join(process.cwd(), 'guidelines');
1351
+ try {
1352
+ if (fs.existsSync(guidelinesSrcPath)) {
1353
+ if (!fs.existsSync(guidelinesDestPath)) {
1354
+ // Create guidelines directory
1355
+ fs.mkdirSync(guidelinesDestPath, { recursive: true });
1356
+ // Copy all files from guidelines folder
1357
+ const files = fs.readdirSync(guidelinesSrcPath);
1358
+ for (const file of files) {
1359
+ if (file.endsWith('.md')) {
1360
+ fs.copyFileSync(
1361
+ pathModule.join(guidelinesSrcPath, file),
1362
+ pathModule.join(guidelinesDestPath, file)
1363
+ );
1364
+ }
1365
+ }
1366
+ console.log(`✅ Created guidelines/ (${files.filter(f => f.endsWith('.md')).length} files)`);
1367
+ } else {
1368
+ console.log(`⚠️ guidelines/ already exists, skipped`);
1369
+ }
1370
+ } else {
1371
+ console.log(`⚠️ guidelines folder not found in abap/ directory`);
1372
+ }
1373
+ } catch (error) {
1374
+ console.error(`Error copying guidelines: ${error.message}`);
1375
+ }
1376
+
1127
1377
  // Create folder
1128
1378
  const folderPath = pathModule.join(process.cwd(), folder);
1129
1379
  try {
@@ -1171,8 +1421,9 @@ async function main() {
1171
1421
  const command = args[0];
1172
1422
 
1173
1423
  // Check if ABAP integration is enabled for this repo
1174
- // (skip for init, help commands)
1175
- if (command && command !== 'init' && command !== 'help' && command !== '--help' && command !== '-h') {
1424
+ // (skip for init, help, ref commands - these don't need ABAP connection)
1425
+ const noAbapRequired = ['init', 'help', '--help', '-h', 'ref'];
1426
+ if (command && !noAbapRequired.includes(command)) {
1176
1427
  if (!isAbapIntegrationEnabled()) {
1177
1428
  console.log(`
1178
1429
  ⚠️ ABAP Git Agent not configured for this repository.
@@ -1195,6 +1446,12 @@ To enable integration:
1195
1446
  }
1196
1447
  }
1197
1448
 
1449
+ // Version compatibility check for commands that interact with ABAP
1450
+ const abapCommands = ['create', 'import', 'pull', 'inspect', 'unit', 'tree', 'view', 'preview', 'list'];
1451
+ if (command && abapCommands.includes(command)) {
1452
+ await checkVersionCompatibility();
1453
+ }
1454
+
1198
1455
  try {
1199
1456
  switch (command) {
1200
1457
  case 'init':
@@ -1390,16 +1647,21 @@ Examples:
1390
1647
  let gitUrl = urlArgIndex !== -1 ? args[urlArgIndex + 1] : null;
1391
1648
  let branch = branchArgIndex !== -1 ? args[branchArgIndex + 1] : getGitBranch();
1392
1649
  let files = null;
1650
+
1651
+ // Transport: CLI arg takes priority, then config/environment, then null
1393
1652
  let transportRequest = null;
1653
+ if (transportArgIndex !== -1 && transportArgIndex + 1 < args.length) {
1654
+ // Explicit --transport argument
1655
+ transportRequest = args[transportArgIndex + 1];
1656
+ } else {
1657
+ // Fall back to config or environment variable
1658
+ transportRequest = getTransport();
1659
+ }
1394
1660
 
1395
1661
  if (filesArgIndex !== -1 && filesArgIndex + 1 < args.length) {
1396
1662
  files = args[filesArgIndex + 1].split(',').map(f => f.trim());
1397
1663
  }
1398
1664
 
1399
- if (transportArgIndex !== -1 && transportArgIndex + 1 < args.length) {
1400
- transportRequest = args[transportArgIndex + 1];
1401
- }
1402
-
1403
1665
  if (!gitUrl) {
1404
1666
  gitUrl = getGitRemoteUrl();
1405
1667
  if (!gitUrl) {
@@ -1427,31 +1689,32 @@ Examples:
1427
1689
  break;
1428
1690
 
1429
1691
  case 'inspect': {
1430
- // TODO: Implement full inspect feature with:
1431
- // - Syntax check (currently implemented via /inspect)
1432
- // - Code Inspector checks (SE51, SCI)
1433
- // - ATC checks (SATC)
1434
- // - Custom rule checks
1435
- // Add --check-type parameter to specify which check to run
1436
-
1437
1692
  const filesArgIndex = args.indexOf('--files');
1438
1693
  if (filesArgIndex === -1 || filesArgIndex + 1 >= args.length) {
1439
1694
  console.error('Error: --files parameter required');
1440
- console.error('Usage: abapgit-agent inspect --files <file1>,<file2>,...');
1695
+ console.error('Usage: abapgit-agent inspect --files <file1>,<file2>,... [--variant <check-variant>]');
1441
1696
  console.error('Example: abapgit-agent inspect --files zcl_my_class.clas.abap');
1697
+ console.error('Example: abapgit-agent inspect --files zcl_my_class.clas.abap --variant ALL_CHECKS');
1442
1698
  process.exit(1);
1443
1699
  }
1444
1700
 
1445
1701
  const filesSyntaxCheck = args[filesArgIndex + 1].split(',').map(f => f.trim());
1446
1702
 
1703
+ // Parse optional --variant parameter
1704
+ const variantArgIndex = args.indexOf('--variant');
1705
+ const variant = variantArgIndex !== -1 ? args[variantArgIndex + 1] : null;
1706
+
1447
1707
  console.log(`\n Inspect for ${filesSyntaxCheck.length} file(s)`);
1708
+ if (variant) {
1709
+ console.log(` Using variant: ${variant}`);
1710
+ }
1448
1711
  console.log('');
1449
1712
 
1450
1713
  const config = loadConfig();
1451
1714
  const csrfToken = await fetchCsrfToken(config);
1452
1715
 
1453
1716
  // Send all files in one request
1454
- const results = await inspectAllFiles(filesSyntaxCheck, csrfToken, config);
1717
+ const results = await inspectAllFiles(filesSyntaxCheck, csrfToken, config, variant);
1455
1718
 
1456
1719
  // Process results
1457
1720
  for (const result of results) {
@@ -1485,15 +1748,37 @@ Examples:
1485
1748
 
1486
1749
  case 'tree': {
1487
1750
  const packageArgIndex = args.indexOf('--package');
1488
- if (packageArgIndex === -1 || packageArgIndex + 1 >= args.length) {
1751
+ if (packageArgIndex === -1) {
1489
1752
  console.error('Error: --package parameter required');
1490
- console.error('Usage: abapgit-agent tree --package <package> [--depth <n>] [--include-objects] [--json]');
1491
- console.error('Example: abapgit-agent tree --package $ZMY_PACKAGE');
1753
+ console.error('Usage: abapgit-agent tree --package <package> [--depth <n>] [--include-types] [--json]');
1754
+ console.error('Example: abapgit-agent tree --package ZMY_PACKAGE');
1755
+ process.exit(1);
1756
+ }
1757
+
1758
+ // Check if package value is missing (happens when shell variable expands to empty)
1759
+ if (packageArgIndex + 1 >= args.length) {
1760
+ console.error('Error: --package parameter value is missing');
1761
+ console.error('');
1762
+ console.error('Tip: If you are using a shell variable, make sure to quote it:');
1763
+ console.error(' abapgit-agent tree --package "$ZMY_PACKAGE"');
1764
+ console.error(' or escape the $ character:');
1765
+ console.error(' abapgit-agent tree --package \\$ZMY_PACKAGE');
1766
+ console.error('');
1767
+ console.error('Usage: abapgit-agent tree --package <package> [--depth <n>] [--include-types] [--json]');
1768
+ console.error('Example: abapgit-agent tree --package ZMY_PACKAGE');
1492
1769
  process.exit(1);
1493
1770
  }
1494
1771
 
1495
1772
  const packageName = args[packageArgIndex + 1];
1496
1773
 
1774
+ // Check for empty/whitespace-only package name
1775
+ if (!packageName || packageName.trim() === '') {
1776
+ console.error('Error: --package parameter cannot be empty');
1777
+ console.error('Usage: abapgit-agent tree --package <package> [--depth <n>] [--include-types] [--json]');
1778
+ console.error('Example: abapgit-agent tree --package ZMY_PACKAGE');
1779
+ process.exit(1);
1780
+ }
1781
+
1497
1782
  // Optional depth parameter
1498
1783
  const depthArgIndex = args.indexOf('--depth');
1499
1784
  let depth = 3;
@@ -1505,8 +1790,8 @@ Examples:
1505
1790
  }
1506
1791
  }
1507
1792
 
1508
- // Optional include-objects parameter
1509
- const includeObjects = args.includes('--include-objects');
1793
+ // Optional include-types parameter (--include-objects is deprecated alias)
1794
+ const includeTypes = args.includes('--include-types') || args.includes('--include-objects');
1510
1795
 
1511
1796
  // Optional json parameter
1512
1797
  const jsonOutput = args.includes('--json');
@@ -1514,10 +1799,141 @@ Examples:
1514
1799
  if (jsonOutput) {
1515
1800
  const config = loadConfig();
1516
1801
  const csrfToken = await fetchCsrfToken(config);
1517
- const result = await runTreeCommand(packageName, depth, includeObjects, csrfToken, config);
1802
+ const result = await runTreeCommand(packageName, depth, includeTypes, csrfToken, config);
1518
1803
  console.log(JSON.stringify(result, null, 2));
1519
1804
  } else {
1520
- await displayTreeOutput(packageName, depth, includeObjects);
1805
+ await displayTreeOutput(packageName, depth, includeTypes);
1806
+ }
1807
+ break;
1808
+ }
1809
+
1810
+ case 'list': {
1811
+ const packageArgIndex = args.indexOf('--package');
1812
+ if (packageArgIndex === -1) {
1813
+ console.error('Error: --package parameter required');
1814
+ console.error('Usage: abapgit-agent list --package <package> [--type <types>] [--name <pattern>] [--limit <n>] [--offset <n>] [--json]');
1815
+ console.error('Example: abapgit-agent list --package $ZMY_PACKAGE');
1816
+ console.error('Example: abapgit-agent list --package $ZMY_PACKAGE --type CLAS,INTF');
1817
+ process.exit(1);
1818
+ }
1819
+
1820
+ // Check if package value is missing
1821
+ if (packageArgIndex + 1 >= args.length) {
1822
+ console.error('Error: --package parameter value is missing');
1823
+ process.exit(1);
1824
+ }
1825
+
1826
+ const packageName = args[packageArgIndex + 1];
1827
+
1828
+ // Validate package name
1829
+ if (!packageName || packageName.trim() === '') {
1830
+ console.error('Error: --package parameter cannot be empty');
1831
+ process.exit(1);
1832
+ }
1833
+
1834
+ // Optional type parameter
1835
+ const typeArgIndex = args.indexOf('--type');
1836
+ const type = typeArgIndex !== -1 && typeArgIndex + 1 < args.length ? args[typeArgIndex + 1] : null;
1837
+
1838
+ // Optional name pattern
1839
+ const nameArgIndex = args.indexOf('--name');
1840
+ const name = nameArgIndex !== -1 && nameArgIndex + 1 < args.length ? args[nameArgIndex + 1] : null;
1841
+
1842
+ // Optional limit
1843
+ const limitArgIndex = args.indexOf('--limit');
1844
+ let limit = 100;
1845
+ if (limitArgIndex !== -1 && limitArgIndex + 1 < args.length) {
1846
+ limit = parseInt(args[limitArgIndex + 1], 10);
1847
+ if (isNaN(limit) || limit < 1) {
1848
+ console.error('Error: --limit must be a positive number');
1849
+ process.exit(1);
1850
+ }
1851
+ if (limit > 1000) {
1852
+ console.error('Error: --limit value too high (max: 1000)');
1853
+ process.exit(1);
1854
+ }
1855
+ }
1856
+
1857
+ // Optional offset
1858
+ const offsetArgIndex = args.indexOf('--offset');
1859
+ let offset = 0;
1860
+ if (offsetArgIndex !== -1 && offsetArgIndex + 1 < args.length) {
1861
+ offset = parseInt(args[offsetArgIndex + 1], 10);
1862
+ if (isNaN(offset) || offset < 0) {
1863
+ console.error('Error: --offset must be a non-negative number');
1864
+ process.exit(1);
1865
+ }
1866
+ }
1867
+
1868
+ // Optional json parameter
1869
+ const jsonOutput = args.includes('--json');
1870
+
1871
+ const config = loadConfig();
1872
+ const csrfToken = await fetchCsrfToken(config);
1873
+
1874
+ const data = {
1875
+ package: packageName,
1876
+ limit: limit,
1877
+ offset: offset
1878
+ };
1879
+
1880
+ if (type) {
1881
+ data.type = type;
1882
+ }
1883
+
1884
+ if (name) {
1885
+ data.name = name;
1886
+ }
1887
+
1888
+ const result = await request('POST', '/sap/bc/z_abapgit_agent/list', data, { csrfToken });
1889
+
1890
+ // Handle uppercase keys from ABAP
1891
+ const success = result.SUCCESS || result.success;
1892
+ const error = result.ERROR || result.error;
1893
+ const objects = result.OBJECTS || result.objects || [];
1894
+ const byType = result.BY_TYPE || result.by_type || [];
1895
+ const total = result.TOTAL || result.total || 0;
1896
+
1897
+ if (!success || error) {
1898
+ console.error(`\n Error: ${error || 'Failed to list objects'}`);
1899
+ process.exit(1);
1900
+ }
1901
+
1902
+ if (jsonOutput) {
1903
+ console.log(JSON.stringify(result, null, 2));
1904
+ } else {
1905
+ // Display human-readable output
1906
+ let title = `Objects in ${packageName}`;
1907
+ if (type) {
1908
+ title += ` (${type} only`;
1909
+ if (total !== objects.length) {
1910
+ title += `, Total: ${total}`;
1911
+ }
1912
+ title += ')';
1913
+ } else if (total !== objects.length) {
1914
+ title += ` (Total: ${total})`;
1915
+ }
1916
+ console.log(`\n${title}\n`);
1917
+
1918
+ // Group objects by type
1919
+ const objectsByType = {};
1920
+ for (const obj of objects) {
1921
+ const objType = (obj.TYPE || obj.type || '???').toUpperCase();
1922
+ if (!objectsByType[objType]) {
1923
+ objectsByType[objType] = [];
1924
+ }
1925
+ objectsByType[objType].push(obj.NAME || obj.name);
1926
+ }
1927
+
1928
+ // Display grouped objects
1929
+ for (const objType of Object.keys(objectsByType).sort()) {
1930
+ const objNames = objectsByType[objType];
1931
+ console.log(` ${objType} (${objNames.length})`);
1932
+ for (const objName of objNames) {
1933
+ console.log(` ${objName}`);
1934
+ }
1935
+ console.log('');
1936
+ }
1521
1937
  }
1522
1938
  break;
1523
1939
  }
@@ -1765,7 +2181,7 @@ Examples:
1765
2181
  }
1766
2182
 
1767
2183
  if (where) {
1768
- data.where = where;
2184
+ data.where = convertDatesInWhereClause(where);
1769
2185
  }
1770
2186
 
1771
2187
  if (columns) {
@@ -1963,6 +2379,109 @@ Examples:
1963
2379
  break;
1964
2380
  }
1965
2381
 
2382
+ case 'ref': {
2383
+ const refSearch = require('../src/ref-search');
2384
+ const topicIndex = args.indexOf('--topic');
2385
+ const listTopics = args.includes('--list-topics') || args.includes('-l');
2386
+ const listRepos = args.includes('--list-repos') || args.includes('-r');
2387
+ const exportGuidelines = args.includes('--export') || args.includes('-e');
2388
+ const jsonOutput = args.includes('--json');
2389
+
2390
+ if (exportGuidelines) {
2391
+ const result = await refSearch.exportGuidelines();
2392
+ if (jsonOutput) {
2393
+ console.log(JSON.stringify(result, null, 2));
2394
+ } else {
2395
+ refSearch.displayExportResult(result);
2396
+ }
2397
+ break;
2398
+ }
2399
+
2400
+ if (listRepos) {
2401
+ const result = await refSearch.listRepositories();
2402
+ if (jsonOutput) {
2403
+ console.log(JSON.stringify(result, null, 2));
2404
+ } else {
2405
+ refSearch.displayRepositories(result);
2406
+ }
2407
+ break;
2408
+ }
2409
+
2410
+ if (listTopics) {
2411
+ const result = await refSearch.listTopics();
2412
+ if (jsonOutput) {
2413
+ console.log(JSON.stringify(result, null, 2));
2414
+ } else {
2415
+ refSearch.displayTopics(result);
2416
+ }
2417
+ break;
2418
+ }
2419
+
2420
+ if (topicIndex !== -1 && topicIndex + 1 < args.length) {
2421
+ const topic = args[topicIndex + 1];
2422
+ const result = await refSearch.getTopic(topic);
2423
+ if (jsonOutput) {
2424
+ console.log(JSON.stringify(result, null, 2));
2425
+ } else {
2426
+ refSearch.displayTopic(result);
2427
+ }
2428
+ break;
2429
+ }
2430
+
2431
+ // Pattern search (default)
2432
+ const patternIndex = args.findIndex((arg, idx) => idx > 0 && !arg.startsWith('--'));
2433
+ if (patternIndex === -1) {
2434
+ console.error('Error: No pattern specified');
2435
+ console.error('');
2436
+ console.error('Usage:');
2437
+ console.error(' abapgit-agent ref <pattern> Search for pattern');
2438
+ console.error(' abapgit-agent ref --topic <name> View specific topic');
2439
+ console.error(' abapgit-agent ref --list-topics List available topics');
2440
+ console.error(' abapgit-agent ref --list-repos List reference repositories');
2441
+ console.error(' abapgit-agent ref --export Export local guidelines to reference folder');
2442
+ console.error('');
2443
+ console.error('Examples:');
2444
+ console.error(' abapgit-agent ref "CORRESPONDING"');
2445
+ console.error(' abapgit-agent ref --topic exceptions');
2446
+ console.error(' abapgit-agent ref --list-topics');
2447
+ console.error(' abapgit-agent ref --list-repos');
2448
+ console.error(' abapgit-agent ref --export');
2449
+ process.exit(1);
2450
+ }
2451
+
2452
+ const pattern = args[patternIndex];
2453
+ const result = await refSearch.searchPattern(pattern);
2454
+
2455
+ // Also search guidelines if available
2456
+ const guidelinesResult = await refSearch.searchGuidelines(pattern);
2457
+ if (guidelinesResult && guidelinesResult.guidelinesFound && guidelinesResult.matches.length > 0) {
2458
+ result.guidelines = guidelinesResult;
2459
+ }
2460
+
2461
+ if (jsonOutput) {
2462
+ console.log(JSON.stringify(result, null, 2));
2463
+ } else {
2464
+ refSearch.displaySearchResults(result);
2465
+ // Display guidelines results if found
2466
+ if (guidelinesResult && guidelinesResult.guidelinesFound && guidelinesResult.matches.length > 0) {
2467
+ console.log('\n 📋 Custom Guidelines:');
2468
+ for (const match of guidelinesResult.matches.slice(0, 5)) {
2469
+ console.log(` 📄 ${match.file} (line ${match.line}):`);
2470
+ const lines = match.context.split('\n');
2471
+ lines.forEach((line, idx) => {
2472
+ const prefix = idx === 1 ? ' → ' : ' ';
2473
+ const trimmed = line.slice(0, 80);
2474
+ console.log(`${prefix}${trimmed}`);
2475
+ });
2476
+ }
2477
+ if (guidelinesResult.matches.length > 5) {
2478
+ console.log(` ... and ${guidelinesResult.matches.length - 5} more matches`);
2479
+ }
2480
+ }
2481
+ }
2482
+ break;
2483
+ }
2484
+
1966
2485
  case 'help':
1967
2486
  case '--help':
1968
2487
  case '-h':
@@ -1975,6 +2494,8 @@ Usage:
1975
2494
  Commands:
1976
2495
  init --folder <folder> --package <package>
1977
2496
  Initialize local configuration for an existing git repository.
2497
+ init --update
2498
+ Update existing files (CLAUDE.md, copilot-instructions.md, guidelines) to latest version.
1978
2499
 
1979
2500
  create
1980
2501
  Create abapGit online repository in ABAP system.
@@ -1996,12 +2517,30 @@ Commands:
1996
2517
  unit --files <file1>,<file2>,...
1997
2518
  Run AUnit tests for ABAP test class files (.testclasses.abap)
1998
2519
 
1999
- tree --package <package> [--depth <n>] [--include-objects] [--json]
2520
+ tree --package <package> [--depth <n>] [--include-types] [--json]
2000
2521
  Display package hierarchy tree from ABAP system
2001
2522
 
2523
+ list --package <package> [--type <types>] [--name <pattern>] [--limit <n>] [--offset <n>] [--json]
2524
+ List ABAP objects in a package with filtering and pagination
2525
+
2002
2526
  view --objects <obj1>,<obj2>,... [--type <type>] [--json]
2003
2527
  View ABAP object definitions from the ABAP system
2004
2528
 
2529
+ ref <pattern> [--json]
2530
+ Search ABAP reference repositories for patterns. Requires referenceFolder in .abapGitAgent.
2531
+
2532
+ ref --topic <topic> [--json]
2533
+ View specific topic from cheat sheets (exceptions, sql, unit-tests, etc.)
2534
+
2535
+ ref --list-topics
2536
+ List available topics for reference search
2537
+
2538
+ ref --list-repos
2539
+ List all reference repositories in the reference folder
2540
+
2541
+ ref --export
2542
+ Export custom guidelines (abap/guidelines/) to reference folder
2543
+
2005
2544
  health
2006
2545
  Check if ABAP REST API is healthy
2007
2546
 
@@ -2010,6 +2549,7 @@ Commands:
2010
2549
 
2011
2550
  Examples:
2012
2551
  abapgit-agent init --folder /src --package ZMY_PACKAGE # Initialize
2552
+ abapgit-agent init --update # Update files to latest
2013
2553
  abapgit-agent create # Create repo
2014
2554
  abapgit-agent import # Import objects to git
2015
2555
  abapgit-agent import --message "Initial import" # Import with message
@@ -2019,14 +2559,27 @@ Examples:
2019
2559
  abapgit-agent pull --transport DEVK900001 # With transport
2020
2560
  abapgit-agent inspect --files zcl_my_class.clas.abap # Syntax check
2021
2561
  abapgit-agent unit --files zcl_my_test.clas.testclasses.abap # Run tests
2022
- abapgit-agent tree --package $ZMY_PACKAGE # Show package tree
2023
- abapgit-agent tree --package $ZMY_PACKAGE --depth 2 # Shallow tree
2024
- abapgit-agent tree --package $ZMY_PACKAGE --include-objects # With object counts
2025
- abapgit-agent tree --package $ZMY_PACKAGE --json # JSON output
2562
+ abapgit-agent tree --package \$ZMY_PACKAGE # Show package tree
2563
+ abapgit-agent tree --package \$ZMY_PACKAGE --depth 2 # Shallow tree
2564
+ abapgit-agent tree --package \$ZMY_PACKAGE --include-types # With type counts
2565
+ abapgit-agent tree --package \$ZMY_PACKAGE --json # JSON output
2566
+ abapgit-agent list --package $ZMY_PACKAGE # List all objects
2567
+ abapgit-agent list --package $ZMY_PACKAGE --type CLAS,INTF # Filter by type
2568
+ abapgit-agent list --package $ZMY_PACKAGE --name ZCL_* # Filter by name
2569
+ abapgit-agent list --package $ZMY_PACKAGE --limit 50 # Limit results
2570
+ abapgit-agent list --package $ZMY_PACKAGE --json # JSON output
2026
2571
  abapgit-agent view --objects ZCL_MY_CLASS # View class definition
2027
2572
  abapgit-agent view --objects ZIF_MY_INTERFACE --type INTF # View interface
2028
2573
  abapgit-agent view --objects ZMY_TABLE --type TABL # View table structure
2029
2574
  abapgit-agent view --objects ZCL_CLASS1,ZCL_CLASS2 --json # Multiple objects
2575
+ abapgit-agent ref "CORRESPONDING" # Search all reference repos
2576
+ abapgit-agent ref "CX_SY_" # Search exceptions
2577
+ abapgit-agent ref --topic exceptions # View exception topic
2578
+ abapgit-agent ref --topic sql # View SQL topic
2579
+ abapgit-agent ref --topic unit-tests # View unit test topic
2580
+ abapgit-agent ref --list-topics # List all topics
2581
+ abapgit-agent ref --list-repos # List reference repositories
2582
+ abapgit-agent ref --export # Export custom guidelines
2030
2583
  abapgit-agent health
2031
2584
  abapgit-agent status
2032
2585
  `);