abapgit-agent 1.7.1 → 1.8.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 (41) hide show
  1. package/.abapGitAgent.example +11 -0
  2. package/README.md +7 -7
  3. package/abap/.github/copilot-instructions.md +254 -0
  4. package/abap/CLAUDE.md +432 -0
  5. package/abap/guidelines/00_index.md +8 -0
  6. package/abap/guidelines/01_sql.md +8 -0
  7. package/abap/guidelines/02_exceptions.md +8 -0
  8. package/abap/guidelines/03_testing.md +8 -0
  9. package/abap/guidelines/04_cds.md +8 -0
  10. package/abap/guidelines/05_classes.md +8 -0
  11. package/abap/guidelines/06_objects.md +8 -0
  12. package/abap/guidelines/07_json.md +8 -0
  13. package/abap/guidelines/08_abapgit.md +8 -0
  14. package/abap/guidelines/09_unit_testable_code.md +8 -0
  15. package/bin/abapgit-agent +61 -2789
  16. package/package.json +25 -5
  17. package/src/agent.js +213 -20
  18. package/src/commands/create.js +102 -0
  19. package/src/commands/delete.js +72 -0
  20. package/src/commands/health.js +24 -0
  21. package/src/commands/help.js +111 -0
  22. package/src/commands/import.js +99 -0
  23. package/src/commands/init.js +321 -0
  24. package/src/commands/inspect.js +184 -0
  25. package/src/commands/list.js +143 -0
  26. package/src/commands/preview.js +277 -0
  27. package/src/commands/pull.js +278 -0
  28. package/src/commands/ref.js +96 -0
  29. package/src/commands/status.js +52 -0
  30. package/src/commands/syntax.js +290 -0
  31. package/src/commands/tree.js +209 -0
  32. package/src/commands/unit.js +133 -0
  33. package/src/commands/view.js +215 -0
  34. package/src/commands/where.js +138 -0
  35. package/src/config.js +11 -1
  36. package/src/utils/abap-http.js +347 -0
  37. package/src/{ref-search.js → utils/abap-reference.js} +119 -1
  38. package/src/utils/git-utils.js +58 -0
  39. package/src/utils/validators.js +72 -0
  40. package/src/utils/version-check.js +80 -0
  41. package/src/abap-client.js +0 -523
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Help command - Display usage information
3
+ */
4
+
5
+ module.exports = {
6
+ name: 'help',
7
+ description: 'Display usage information and available commands',
8
+ requiresAbapConfig: false,
9
+ requiresVersionCheck: false,
10
+
11
+ async execute(args, context) {
12
+ console.log(`
13
+ ABAP Git Agent
14
+
15
+ Usage:
16
+ abapgit-agent <command> [options]
17
+
18
+ Commands:
19
+ init --folder <folder> --package <package>
20
+ Initialize local configuration for an existing git repository.
21
+ init --update
22
+ Update existing files (CLAUDE.md, copilot-instructions.md, guidelines) to latest version.
23
+
24
+ create
25
+ Create abapGit online repository in ABAP system.
26
+ Auto-detects URL from git remote and package from .abapGitAgent.
27
+
28
+ import [--message <message>]
29
+ Import existing objects from package to git repository.
30
+ Uses the git remote URL to find the abapGit online repository.
31
+
32
+ pull [--url <git-url>] [--branch <branch>] [--files <file1,file2,...>] [--transport <request>]
33
+ Pull and activate repository in ABAP system.
34
+ Auto-detects git remote and branch from current directory.
35
+ Use --files to pull only specific files.
36
+ Use --transport to specify a transport request.
37
+
38
+ inspect --files <file1>,<file2>,...
39
+ Inspect ABAP source file(s) for issues. Currently runs syntax check.
40
+
41
+ syntax --files <file1>,<file2>,... [--cloud] [--json]
42
+ Check syntax of ABAP source files WITHOUT pull/activation.
43
+ Reads source from local files and checks directly in ABAP system.
44
+ Use --cloud for ABAP Cloud (BTP) stricter syntax checking.
45
+
46
+ unit --files <file1>,<file2>,...
47
+ Run AUnit tests for ABAP test class files (.testclasses.abap)
48
+
49
+ tree --package <package> [--depth <n>] [--include-types] [--json]
50
+ Display package hierarchy tree from ABAP system
51
+
52
+ list --package <package> [--type <types>] [--name <pattern>] [--limit <n>] [--offset <n>] [--json]
53
+ List ABAP objects in a package with filtering and pagination
54
+
55
+ view --objects <obj1>,<obj2>,... [--type <type>] [--json]
56
+ View ABAP object definitions from the ABAP system
57
+
58
+ where --objects <obj1>,<obj2>,... [--type <type>] [--limit <n>] [--json]
59
+ Find where-used list for ABAP objects (classes, interfaces, programs)
60
+
61
+ ref <pattern> [--json]
62
+ Search ABAP reference repositories for patterns. Requires referenceFolder in .abapGitAgent.
63
+
64
+ ref --topic <topic> [--json]
65
+ View specific topic from cheat sheets (exceptions, sql, unit-tests, etc.)
66
+
67
+ ref --list-topics
68
+ List available topics for reference search
69
+
70
+ ref --list-repos
71
+ List all reference repositories in the reference folder
72
+
73
+ ref --clone <repo> [--name <folder>]
74
+ Clone a GitHub repository to the reference folder
75
+ - Use full URL: https://github.com/user/repo.git
76
+ - Or short name: user/repo or user/repo (assumes github.com)
77
+
78
+ health
79
+ Check if ABAP REST API is healthy
80
+
81
+ status
82
+ Check if ABAP integration is configured for this repo
83
+
84
+ Examples:
85
+ abapgit-agent init --folder /src --package ZMY_PACKAGE # Initialize
86
+ abapgit-agent init --update # Update files to latest
87
+ abapgit-agent create # Create repo
88
+ abapgit-agent delete # Delete repo
89
+ abapgit-agent import # Import objects to git
90
+ abapgit-agent import --message "Initial import" # Import with message
91
+ abapgit-agent pull # Auto-detect from git
92
+ abapgit-agent pull --branch develop # Specific branch
93
+ abapgit-agent pull --files src/zcl_my_class.clas.abap # Specific file
94
+ abapgit-agent pull --transport DEVK900001 # With transport
95
+ abapgit-agent inspect --files src/zcl_my_class.clas.abap # Syntax check
96
+ abapgit-agent syntax --files src/zcl_my_class.clas.abap # Check without pull
97
+ abapgit-agent syntax --files src/zmy_prog.prog.abap --cloud # ABAP Cloud check
98
+ abapgit-agent unit --files src/zcl_my_test.clas.testclasses.abap # Run unit tests
99
+ abapgit-agent tree --package $MY_PACKAGE # Display package tree
100
+ abapgit-agent list --package $MY_PACKAGE --type CLAS,INTF # List classes & interfaces
101
+ abapgit-agent view --objects ZCL_MY_CLASS # View class definition
102
+ abapgit-agent where --objects ZCL_MY_CLASS # Find where class is used
103
+ abapgit-agent ref "CORRESPONDING" # Search for pattern
104
+ abapgit-agent ref --topic exceptions # View exceptions topic
105
+ abapgit-agent health # Health check
106
+ abapgit-agent status # Configuration status
107
+
108
+ For more info: https://github.tools.sap/I045696/abapgit-agent
109
+ `);
110
+ }
111
+ };
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Import command - Import existing objects from package to git repository
3
+ */
4
+
5
+ module.exports = {
6
+ name: 'import',
7
+ description: 'Import existing objects from package to git repository',
8
+ requiresAbapConfig: true,
9
+ requiresVersionCheck: true,
10
+
11
+ async execute(args, context) {
12
+ const { loadConfig, gitUtils, AbapHttp } = context;
13
+
14
+ // Show help if requested
15
+ const helpIndex = args.findIndex(a => a === '--help' || a === '-h');
16
+ if (helpIndex !== -1) {
17
+ console.log(`
18
+ Usage:
19
+ abapgit-agent import [--message <message>]
20
+
21
+ Description:
22
+ Import existing objects from package to git repository.
23
+ Uses the git remote URL to find the abapGit online repository.
24
+
25
+ Prerequisites:
26
+ - Run "abapgit-agent create" first or create repository in abapGit UI
27
+ - Package must have objects to import
28
+
29
+ Options:
30
+ --message Commit message (default: "feat: initial import from ABAP package <package>")
31
+
32
+ Examples:
33
+ abapgit-agent import
34
+ abapgit-agent import --message "Initial import from SAP"
35
+ `);
36
+ return;
37
+ }
38
+
39
+ // Get parameters from config
40
+ const config = loadConfig();
41
+ const repoUrl = gitUtils.getRemoteUrl();
42
+
43
+ if (!repoUrl) {
44
+ console.error('Error: No git remote configured. Please configure a remote origin.');
45
+ process.exit(1);
46
+ }
47
+
48
+ const messageArgIndex = args.indexOf('--message');
49
+ let commitMessage = null;
50
+ if (messageArgIndex !== -1 && messageArgIndex + 1 < args.length) {
51
+ commitMessage = args[messageArgIndex + 1];
52
+ }
53
+
54
+ console.log(`\n📦 Importing objects to git repository`);
55
+ console.log(` URL: ${repoUrl}`);
56
+ if (commitMessage) {
57
+ console.log(` Message: ${commitMessage}`);
58
+ }
59
+
60
+ const http = new AbapHttp(config);
61
+ const csrfToken = await http.fetchCsrfToken();
62
+
63
+ const data = {
64
+ url: repoUrl
65
+ };
66
+
67
+ if (commitMessage) {
68
+ data.message = commitMessage;
69
+ }
70
+
71
+ if (config.gitUsername) {
72
+ data.username = config.gitUsername;
73
+ }
74
+
75
+ if (config.gitPassword) {
76
+ data.password = config.gitPassword;
77
+ }
78
+
79
+ const result = await http.post('/sap/bc/z_abapgit_agent/import', data, { csrfToken });
80
+
81
+ console.log('\n');
82
+
83
+ // Handle uppercase keys from ABAP
84
+ const success = result.SUCCESS || result.success;
85
+ const filesStaged = result.FILES_STAGED || result.files_staged;
86
+ const abapCommitMessage = result.COMMIT_MESSAGE || result.commit_message;
87
+ const error = result.ERROR || result.error;
88
+
89
+ if (success === 'X' || success === true) {
90
+ console.log(`✅ Objects imported successfully!`);
91
+ console.log(` Files staged: ${filesStaged}`);
92
+ console.log(` Commit: ${commitMessage || abapCommitMessage}`);
93
+ } else {
94
+ console.log(`❌ Import failed`);
95
+ console.log(` Error: ${error || 'Unknown error'}`);
96
+ process.exit(1);
97
+ }
98
+ }
99
+ };
@@ -0,0 +1,321 @@
1
+ /**
2
+ * Init command - Initialize local repository configuration
3
+ */
4
+
5
+ const pathModule = require('path');
6
+ const fs = require('fs');
7
+
8
+ /**
9
+ * Copy a file if source exists (helper for init --update)
10
+ */
11
+ async function copyFileIfExists(srcPath, destPath, label, createParentDir = false) {
12
+ try {
13
+ if (fs.existsSync(srcPath)) {
14
+ if (createParentDir) {
15
+ const parentDir = pathModule.dirname(destPath);
16
+ if (!fs.existsSync(parentDir)) {
17
+ fs.mkdirSync(parentDir, { recursive: true });
18
+ }
19
+ }
20
+ fs.copyFileSync(srcPath, destPath);
21
+ console.log(`✅ Updated ${label}`);
22
+ } else {
23
+ console.log(`⚠️ ${label} not found in abapgit-agent`);
24
+ }
25
+ } catch (error) {
26
+ console.error(`Error copying ${label}: ${error.message}`);
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Copy guidelines folder (helper for init --update)
32
+ */
33
+ async function copyGuidelinesFolder(srcPath, destPath, overwrite = false) {
34
+ try {
35
+ if (fs.existsSync(srcPath)) {
36
+ // Create destination directory if needed
37
+ if (!fs.existsSync(destPath)) {
38
+ fs.mkdirSync(destPath, { recursive: true });
39
+ }
40
+
41
+ const files = fs.readdirSync(srcPath);
42
+ let copied = 0;
43
+
44
+ for (const file of files) {
45
+ if (file.endsWith('.md')) {
46
+ const srcFile = pathModule.join(srcPath, file);
47
+ const destFile = pathModule.join(destPath, file);
48
+
49
+ // Skip if file exists and not overwriting
50
+ if (fs.existsSync(destFile) && !overwrite) {
51
+ continue;
52
+ }
53
+
54
+ fs.copyFileSync(srcFile, destFile);
55
+ copied++;
56
+ }
57
+ }
58
+
59
+ if (copied > 0) {
60
+ console.log(`✅ Updated guidelines/ (${copied} files)`);
61
+ } else {
62
+ console.log(`⚠️ No guideline files found`);
63
+ }
64
+ } else {
65
+ console.log(`⚠️ guidelines folder not found in abapgit-agent`);
66
+ }
67
+ } catch (error) {
68
+ console.error(`Error copying guidelines: ${error.message}`);
69
+ }
70
+ }
71
+
72
+ module.exports = {
73
+ name: 'init',
74
+ description: 'Initialize local repository configuration',
75
+ requiresAbapConfig: false,
76
+ requiresVersionCheck: false,
77
+
78
+ async execute(args, context) {
79
+ const { gitUtils } = context;
80
+
81
+ const folderArgIndex = args.indexOf('--folder');
82
+ const packageArgIndex = args.indexOf('--package');
83
+ const updateMode = args.includes('--update');
84
+
85
+ // Get parameters
86
+ let folder = folderArgIndex !== -1 && folderArgIndex + 1 < args.length
87
+ ? args[folderArgIndex + 1]
88
+ : '/src/';
89
+
90
+ // Normalize folder path: ensure it starts with / and ends with /
91
+ folder = folder.trim();
92
+ if (!folder.startsWith('/')) {
93
+ folder = '/' + folder;
94
+ }
95
+ if (!folder.endsWith('/')) {
96
+ folder = folder + '/';
97
+ }
98
+
99
+ const packageName = packageArgIndex !== -1 && packageArgIndex + 1 < args.length
100
+ ? args[packageArgIndex + 1]
101
+ : null;
102
+
103
+ // Validate package is required for non-update mode
104
+ if (!updateMode && !packageName) {
105
+ console.error('Error: --package is required');
106
+ console.error('Usage: abapgit-agent init --folder /src --package ZMY_PACKAGE');
107
+ console.error(' abapgit-agent init --update # Update files to latest version');
108
+ process.exit(1);
109
+ }
110
+
111
+ // Check if current folder is git repo root
112
+ const gitDir = pathModule.join(process.cwd(), '.git');
113
+ if (!fs.existsSync(gitDir)) {
114
+ console.error('Error: Current folder is not a git repository.');
115
+ console.error('Run this command from the root folder of your git repository.');
116
+ process.exit(1);
117
+ }
118
+
119
+ // In update mode, just copy the files and return
120
+ if (updateMode) {
121
+ console.log(`\n🔄 Updating abapGit Agent files`);
122
+ console.log('');
123
+
124
+ // Copy CLAUDE.md
125
+ await copyFileIfExists(
126
+ pathModule.join(__dirname, '..', '..', 'abap', 'CLAUDE.md'),
127
+ pathModule.join(process.cwd(), 'CLAUDE.md'),
128
+ 'CLAUDE.md'
129
+ );
130
+
131
+ // Copy copilot-instructions.md
132
+ await copyFileIfExists(
133
+ pathModule.join(__dirname, '..', '..', 'abap', '.github', 'copilot-instructions.md'),
134
+ pathModule.join(process.cwd(), '.github', 'copilot-instructions.md'),
135
+ '.github/copilot-instructions.md',
136
+ true // create parent dir
137
+ );
138
+
139
+ // Copy guidelines folder to project root
140
+ await copyGuidelinesFolder(
141
+ pathModule.join(__dirname, '..', '..', 'abap', 'guidelines'),
142
+ pathModule.join(process.cwd(), 'guidelines'),
143
+ true // overwrite
144
+ );
145
+
146
+ console.log(`
147
+ 📋 Update complete!
148
+ Run 'abapgit-agent ref --list-topics' to see available topics.
149
+ `);
150
+ return;
151
+ }
152
+
153
+ // Validate package is required
154
+ if (!packageName) {
155
+ console.error('Error: --package is required');
156
+ console.error('Usage: abapgit-agent init --folder /src --package ZMY_PACKAGE');
157
+ console.error(' abapgit-agent init --update # Update files to latest version');
158
+ process.exit(1);
159
+ }
160
+
161
+ console.log(`\n🚀 Initializing abapGit Agent for local repository`);
162
+ console.log(` Folder: ${folder}`);
163
+ console.log(` Package: ${packageName}`);
164
+ console.log('');
165
+
166
+ // Detect git remote URL
167
+ const gitUrl = gitUtils.getRemoteUrl();
168
+ if (!gitUrl) {
169
+ console.error('Error: No git remote configured.');
170
+ console.error('Configure a remote with: git remote add origin <url>');
171
+ process.exit(1);
172
+ }
173
+ console.log(`📌 Git remote: ${gitUrl}`);
174
+
175
+ // Check if .abapGitAgent already exists
176
+ const configPath = pathModule.join(process.cwd(), '.abapGitAgent');
177
+ if (fs.existsSync(configPath)) {
178
+ console.error('Error: .abapGitAgent already exists.');
179
+ console.error('To reinitialize, delete the existing file first.');
180
+ process.exit(1);
181
+ }
182
+
183
+ // Copy .abapGitAgent.example to .abapGitAgent
184
+ const samplePath = pathModule.join(__dirname, '..', '..', '.abapGitAgent.example');
185
+ if (!fs.existsSync(samplePath)) {
186
+ console.error('Error: .abapGitAgent.example not found.');
187
+ process.exit(1);
188
+ }
189
+
190
+ try {
191
+ // Read sample and update with package/folder
192
+ const sampleContent = fs.readFileSync(samplePath, 'utf8');
193
+ const config = JSON.parse(sampleContent);
194
+ config.package = packageName;
195
+ config.folder = folder;
196
+
197
+ // Write updated config
198
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
199
+ console.log(`✅ Created .abapGitAgent`);
200
+ } catch (error) {
201
+ console.error(`Error creating .abapGitAgent: ${error.message}`);
202
+ process.exit(1);
203
+ }
204
+
205
+ // Update .gitignore
206
+ const gitignorePath = pathModule.join(process.cwd(), '.gitignore');
207
+ const ignoreEntries = ['.abapGitAgent', '.abapgit_agent_cookies.txt'];
208
+ let existingIgnore = '';
209
+
210
+ if (fs.existsSync(gitignorePath)) {
211
+ existingIgnore = fs.readFileSync(gitignorePath, 'utf8');
212
+ }
213
+
214
+ let updated = false;
215
+ let newIgnoreContent = existingIgnore;
216
+
217
+ for (const entry of ignoreEntries) {
218
+ if (!newIgnoreContent.includes(entry)) {
219
+ if (newIgnoreContent && !newIgnoreContent.endsWith('\n')) {
220
+ newIgnoreContent += '\n';
221
+ }
222
+ newIgnoreContent += entry + '\n';
223
+ updated = true;
224
+ }
225
+ }
226
+
227
+ if (updated) {
228
+ fs.writeFileSync(gitignorePath, newIgnoreContent);
229
+ console.log(`✅ Updated .gitignore`);
230
+ } else {
231
+ fs.writeFileSync(gitignorePath, newIgnoreContent);
232
+ console.log(`✅ .gitignore already up to date`);
233
+ }
234
+
235
+ // Copy CLAUDE.md
236
+ const claudeMdPath = pathModule.join(__dirname, '..', '..', 'abap', 'CLAUDE.md');
237
+ const localClaudeMdPath = pathModule.join(process.cwd(), 'CLAUDE.md');
238
+ try {
239
+ if (fs.existsSync(claudeMdPath)) {
240
+ fs.copyFileSync(claudeMdPath, localClaudeMdPath);
241
+ console.log(`✅ Created CLAUDE.md`);
242
+ } else {
243
+ console.log(`⚠️ CLAUDE.md not found in abap/ directory`);
244
+ }
245
+ } catch (error) {
246
+ console.error(`Error copying CLAUDE.md: ${error.message}`);
247
+ }
248
+
249
+ // Copy copilot-instructions.md for GitHub Copilot
250
+ const copilotMdPath = pathModule.join(__dirname, '..', '..', 'abap', '.github', 'copilot-instructions.md');
251
+ const githubDir = pathModule.join(process.cwd(), '.github');
252
+ const localCopilotMdPath = pathModule.join(githubDir, 'copilot-instructions.md');
253
+ try {
254
+ if (fs.existsSync(copilotMdPath)) {
255
+ // Ensure .github directory exists
256
+ if (!fs.existsSync(githubDir)) {
257
+ fs.mkdirSync(githubDir, { recursive: true });
258
+ }
259
+ fs.copyFileSync(copilotMdPath, localCopilotMdPath);
260
+ console.log(`✅ Created .github/copilot-instructions.md`);
261
+ } else {
262
+ console.log(`⚠️ copilot-instructions.md not found in abap/ directory`);
263
+ }
264
+ } catch (error) {
265
+ console.error(`Error copying copilot-instructions.md: ${error.message}`);
266
+ }
267
+
268
+ // Copy guidelines folder to project root
269
+ const guidelinesSrcPath = pathModule.join(__dirname, '..', '..', 'abap', 'guidelines');
270
+ const guidelinesDestPath = pathModule.join(process.cwd(), 'guidelines');
271
+ try {
272
+ if (fs.existsSync(guidelinesSrcPath)) {
273
+ if (!fs.existsSync(guidelinesDestPath)) {
274
+ // Create guidelines directory
275
+ fs.mkdirSync(guidelinesDestPath, { recursive: true });
276
+ // Copy all files from guidelines folder
277
+ const files = fs.readdirSync(guidelinesSrcPath);
278
+ for (const file of files) {
279
+ if (file.endsWith('.md')) {
280
+ fs.copyFileSync(
281
+ pathModule.join(guidelinesSrcPath, file),
282
+ pathModule.join(guidelinesDestPath, file)
283
+ );
284
+ }
285
+ }
286
+ console.log(`✅ Created guidelines/ (${files.filter(f => f.endsWith('.md')).length} files)`);
287
+ } else {
288
+ console.log(`⚠️ guidelines/ already exists, skipped`);
289
+ }
290
+ } else {
291
+ console.log(`⚠️ guidelines folder not found in abap/ directory`);
292
+ }
293
+ } catch (error) {
294
+ console.error(`Error copying guidelines: ${error.message}`);
295
+ }
296
+
297
+ // Create folder
298
+ const folderPath = pathModule.join(process.cwd(), folder);
299
+ try {
300
+ if (!fs.existsSync(folderPath)) {
301
+ fs.mkdirSync(folderPath, { recursive: true });
302
+ console.log(`✅ Created folder: ${folder}`);
303
+
304
+ // Create .gitkeep
305
+ const gitkeepPath = pathModule.join(folderPath, '.gitkeep');
306
+ fs.writeFileSync(gitkeepPath, '');
307
+ } else {
308
+ console.log(`✅ Folder already exists: ${folder}`);
309
+ }
310
+ } catch (error) {
311
+ console.error(`Error creating folder: ${error.message}`);
312
+ }
313
+
314
+ console.log(`
315
+ 📋 Next steps:
316
+ 1. Edit .abapGitAgent with your ABAP credentials (host, user, password)
317
+ 2. Run 'abapgit-agent create --import' to create online repository
318
+ 3. Run 'abapgit-agent pull' to activate objects
319
+ `);
320
+ }
321
+ };