Deminiko commited on
Commit
a05d7b0
·
1 Parent(s): 681e773

feat: Complete GitHub App authentication, documentation generation, and test improvements

Browse files

- Implement GitHub App authentication (Octokit) with fallback to personal access token
- Add @octokit/auth-app and @octokit/rest dependencies for secure GitHub API access
- Update GitHubService to use Octokit instead of axios for better API compatibility
- Create DocGeneratorAgent for auto-generating project documentation
- Add docs API endpoints for project, file, and README documentation
- Improve scanner report formatting with better issue presentation
- Update server startup logging with environment info and health check URL
- Add error handling for server startup failures
- Create comprehensive test suite (test-ai-agent.js) for CI/CD validation
- Update GitHub Actions workflow to use environment secrets
- Update installation guide with both token auth methods
- Add GitHub App setup instructions to INSTALLATION.md
- Fix environment configuration to support both auth methods

.env.example CHANGED
@@ -7,6 +7,11 @@ HF_USERNAME=NLarchive
7
  HF_SPACE_NAME=my-webapp-hf
8
  GITHUB_BRANCH=main
9
 
 
 
 
 
 
10
  # Optional
11
  DRY_RUN=false
12
  FORCE_PUSH=false
 
7
  HF_SPACE_NAME=my-webapp-hf
8
  GITHUB_BRANCH=main
9
 
10
+ # GitHub App Authentication (for AI Agent)
11
+ GH_APP_ID=your_github_app_id
12
+ GH_APP_PRIVATE_KEY_B64=your_base64_encoded_private_key
13
+ GH_APP_INSTALLATION_ID=your_installation_id
14
+
15
  # Optional
16
  DRY_RUN=false
17
  FORCE_PUSH=false
.github/workflows/sync-to-hf.yml CHANGED
@@ -33,9 +33,11 @@ jobs:
33
  - name: Push to Hugging Face Space
34
  env:
35
  HF_TOKEN: ${{ secrets.HF_TOKEN }}
 
 
36
  run: |
37
  # Add space remote (ignore if exists)
38
- git remote add space https://NLarchive:[email protected]/spaces/NLarchive/my-webapp-hf || true
39
 
40
  # Force push to the space
41
- git push --force https://NLarchive:[email protected]/spaces/NLarchive/my-webapp-hf main
 
33
  - name: Push to Hugging Face Space
34
  env:
35
  HF_TOKEN: ${{ secrets.HF_TOKEN }}
36
+ HF_USERNAME: ${{ secrets.HF_USERNAME }}
37
+ HF_SPACE_NAME: ${{ secrets.HF_SPACE_NAME }}
38
  run: |
39
  # Add space remote (ignore if exists)
40
+ git remote add space https://$HF_USERNAME:[email protected]/spaces/$HF_USERNAME/$HF_SPACE_NAME || true
41
 
42
  # Force push to the space
43
+ git push --force https://$HF_USERNAME:[email protected]/spaces/$HF_USERNAME/$HF_SPACE_NAME main
public/.env.example CHANGED
@@ -10,11 +10,18 @@
10
  # Get from: https://makersuite.google.com/app/apikey
11
  GEMINI_API_KEY=your_gemini_api_key_here
12
 
13
- # GitHub Personal Access Token
 
14
  # Get from: https://github.com/settings/tokens
15
  # Required scopes: repo, issues, actions
16
  GITHUB_TOKEN=your_github_token_here
17
 
 
 
 
 
 
 
18
  # Hugging Face Token
19
  # Get from: https://huggingface.co/settings/tokens
20
  HF_TOKEN=your_huggingface_token_here
 
10
  # Get from: https://makersuite.google.com/app/apikey
11
  GEMINI_API_KEY=your_gemini_api_key_here
12
 
13
+ # GitHub Authentication (Choose ONE method)
14
+ # Option 1: Personal Access Token (Simple)
15
  # Get from: https://github.com/settings/tokens
16
  # Required scopes: repo, issues, actions
17
  GITHUB_TOKEN=your_github_token_here
18
 
19
+ # Option 2: GitHub App (Recommended for production)
20
+ # Get from: https://github.com/settings/apps
21
+ GH_APP_ID=your_github_app_id
22
+ GH_APP_PRIVATE_KEY_B64=your_base64_encoded_private_key
23
+ GH_APP_INSTALLATION_ID=your_app_installation_id
24
+
25
  # Hugging Face Token
26
  # Get from: https://huggingface.co/settings/tokens
27
  HF_TOKEN=your_huggingface_token_here
public/INSTALLATION.md CHANGED
@@ -78,7 +78,9 @@ Visit: http://localhost:3000
78
  4. Paste into .env GEMINI_API_KEY=
79
  ```
80
 
81
- #### 2. GitHub Personal Access Token
 
 
82
  ```
83
  1. Go: https://github.com/settings/tokens
84
  2. Click "Generate new token" → "Generate new token (classic)"
@@ -91,6 +93,41 @@ Visit: http://localhost:3000
91
  6. Paste into .env GITHUB_TOKEN=
92
  ```
93
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  #### 3. Hugging Face Token
95
  ```
96
  1. Go: https://huggingface.co/settings/tokens
 
78
  4. Paste into .env GEMINI_API_KEY=
79
  ```
80
 
81
+ #### 2. GitHub Authentication (Choose One)
82
+
83
+ **Option A: Personal Access Token (Simple)**
84
  ```
85
  1. Go: https://github.com/settings/tokens
86
  2. Click "Generate new token" → "Generate new token (classic)"
 
93
  6. Paste into .env GITHUB_TOKEN=
94
  ```
95
 
96
+ **Option B: GitHub App (Recommended for Production)**
97
+ ```
98
+ 1. Go: https://github.com/settings/apps
99
+ 2. Click "New GitHub App"
100
+ 3. Configure:
101
+ - Name: "AI Agent HF"
102
+ - Homepage URL: https://huggingface.co/spaces/YourUsername/your-space
103
+ - Webhook URL: Leave empty
104
+ - Permissions:
105
+ ✓ Repository permissions → Contents: Read & write
106
+ ✓ Repository permissions → Issues: Read & write
107
+ ✓ Repository permissions → Pull requests: Read & write
108
+ ✓ Repository permissions → Actions: Read & write
109
+ ✓ Repository permissions → Metadata: Read
110
+ 4. Generate private key (download .pem file)
111
+ 5. Install app on your repository
112
+ 6. Get installation ID from app settings
113
+ 7. Convert private key to base64:
114
+ ```bash
115
+ # On Windows PowerShell
116
+ [Convert]::ToBase64String([IO.File]::ReadAllBytes("path\to\private-key.pem"))
117
+ ```
118
+ 8. Configure .env:
119
+ ```
120
+ GH_APP_ID=your_app_id
121
+ GH_APP_PRIVATE_KEY_B64=your_base64_private_key
122
+ GH_APP_INSTALLATION_ID=your_installation_id
123
+ ```
124
+ 9. Test authentication:
125
+ ```bash
126
+ cd scripts
127
+ node test-github-auth.js
128
+ ```
129
+ ```
130
+
131
  #### 3. Hugging Face Token
132
  ```
133
  1. Go: https://huggingface.co/settings/tokens
public/package-lock.json CHANGED
@@ -9,6 +9,8 @@
9
  "version": "1.0.0",
10
  "dependencies": {
11
  "@google/generative-ai": "^0.7.0",
 
 
12
  "axios": "^1.6.0",
13
  "body-parser": "^1.20.2",
14
  "cors": "^2.8.5",
@@ -30,6 +32,265 @@
30
  "node": ">=18.0.0"
31
  }
32
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  "node_modules/accepts": {
34
  "version": "1.3.8",
35
  "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
@@ -81,6 +342,11 @@
81
  "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
82
  "dev": true
83
  },
 
 
 
 
 
84
  "node_modules/binary-extensions": {
85
  "version": "2.3.0",
86
  "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
@@ -138,6 +404,16 @@
138
  "node": ">=8"
139
  }
140
  },
 
 
 
 
 
 
 
 
 
 
141
  "node_modules/bytes": {
142
  "version": "3.1.2",
143
  "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@@ -282,6 +558,11 @@
282
  "node": ">= 0.8"
283
  }
284
  },
 
 
 
 
 
285
  "node_modules/destroy": {
286
  "version": "1.2.0",
287
  "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
@@ -315,6 +596,14 @@
315
  "node": ">= 0.4"
316
  }
317
  },
 
 
 
 
 
 
 
 
318
  "node_modules/ee-first": {
319
  "version": "1.1.1",
320
  "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -718,6 +1007,95 @@
718
  "node": ">=0.12.0"
719
  }
720
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
721
  "node_modules/math-intrinsics": {
722
  "version": "1.1.0",
723
  "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -895,6 +1273,14 @@
895
  "node": ">= 0.8"
896
  }
897
  },
 
 
 
 
 
 
 
 
898
  "node_modules/parseurl": {
899
  "version": "1.3.3",
900
  "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -1019,7 +1405,6 @@
1019
  "version": "7.7.3",
1020
  "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
1021
  "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
1022
- "dev": true,
1023
  "bin": {
1024
  "semver": "bin/semver.js"
1025
  },
@@ -1229,6 +1614,25 @@
1229
  "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
1230
  "dev": true
1231
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1232
  "node_modules/unpipe": {
1233
  "version": "1.0.0",
1234
  "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
@@ -1252,6 +1656,11 @@
1252
  "engines": {
1253
  "node": ">= 0.8"
1254
  }
 
 
 
 
 
1255
  }
1256
  }
1257
  }
 
9
  "version": "1.0.0",
10
  "dependencies": {
11
  "@google/generative-ai": "^0.7.0",
12
+ "@octokit/auth-app": "^6.0.3",
13
+ "@octokit/rest": "^20.0.2",
14
  "axios": "^1.6.0",
15
  "body-parser": "^1.20.2",
16
  "cors": "^2.8.5",
 
32
  "node": ">=18.0.0"
33
  }
34
  },
35
+ "node_modules/@octokit/auth-app": {
36
+ "version": "6.1.4",
37
+ "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-6.1.4.tgz",
38
+ "integrity": "sha512-QkXkSOHZK4dA5oUqY5Dk3S+5pN2s1igPjEASNQV8/vgJgW034fQWR16u7VsNOK/EljA00eyjYF5mWNxWKWhHRQ==",
39
+ "dependencies": {
40
+ "@octokit/auth-oauth-app": "^7.1.0",
41
+ "@octokit/auth-oauth-user": "^4.1.0",
42
+ "@octokit/request": "^8.3.1",
43
+ "@octokit/request-error": "^5.1.0",
44
+ "@octokit/types": "^13.1.0",
45
+ "deprecation": "^2.3.1",
46
+ "lru-cache": "npm:@wolfy1339/lru-cache@^11.0.2-patch.1",
47
+ "universal-github-app-jwt": "^1.1.2",
48
+ "universal-user-agent": "^6.0.0"
49
+ },
50
+ "engines": {
51
+ "node": ">= 18"
52
+ }
53
+ },
54
+ "node_modules/@octokit/auth-oauth-app": {
55
+ "version": "7.1.0",
56
+ "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-7.1.0.tgz",
57
+ "integrity": "sha512-w+SyJN/b0l/HEb4EOPRudo7uUOSW51jcK1jwLa+4r7PA8FPFpoxEnHBHMITqCsc/3Vo2qqFjgQfz/xUUvsSQnA==",
58
+ "dependencies": {
59
+ "@octokit/auth-oauth-device": "^6.1.0",
60
+ "@octokit/auth-oauth-user": "^4.1.0",
61
+ "@octokit/request": "^8.3.1",
62
+ "@octokit/types": "^13.0.0",
63
+ "@types/btoa-lite": "^1.0.0",
64
+ "btoa-lite": "^1.0.0",
65
+ "universal-user-agent": "^6.0.0"
66
+ },
67
+ "engines": {
68
+ "node": ">= 18"
69
+ }
70
+ },
71
+ "node_modules/@octokit/auth-oauth-device": {
72
+ "version": "6.1.0",
73
+ "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-6.1.0.tgz",
74
+ "integrity": "sha512-FNQ7cb8kASufd6Ej4gnJ3f1QB5vJitkoV1O0/g6e6lUsQ7+VsSNRHRmFScN2tV4IgKA12frrr/cegUs0t+0/Lw==",
75
+ "dependencies": {
76
+ "@octokit/oauth-methods": "^4.1.0",
77
+ "@octokit/request": "^8.3.1",
78
+ "@octokit/types": "^13.0.0",
79
+ "universal-user-agent": "^6.0.0"
80
+ },
81
+ "engines": {
82
+ "node": ">= 18"
83
+ }
84
+ },
85
+ "node_modules/@octokit/auth-oauth-user": {
86
+ "version": "4.1.0",
87
+ "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-4.1.0.tgz",
88
+ "integrity": "sha512-FrEp8mtFuS/BrJyjpur+4GARteUCrPeR/tZJzD8YourzoVhRics7u7we/aDcKv+yywRNwNi/P4fRi631rG/OyQ==",
89
+ "dependencies": {
90
+ "@octokit/auth-oauth-device": "^6.1.0",
91
+ "@octokit/oauth-methods": "^4.1.0",
92
+ "@octokit/request": "^8.3.1",
93
+ "@octokit/types": "^13.0.0",
94
+ "btoa-lite": "^1.0.0",
95
+ "universal-user-agent": "^6.0.0"
96
+ },
97
+ "engines": {
98
+ "node": ">= 18"
99
+ }
100
+ },
101
+ "node_modules/@octokit/auth-token": {
102
+ "version": "4.0.0",
103
+ "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz",
104
+ "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==",
105
+ "engines": {
106
+ "node": ">= 18"
107
+ }
108
+ },
109
+ "node_modules/@octokit/core": {
110
+ "version": "5.2.2",
111
+ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.2.tgz",
112
+ "integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==",
113
+ "dependencies": {
114
+ "@octokit/auth-token": "^4.0.0",
115
+ "@octokit/graphql": "^7.1.0",
116
+ "@octokit/request": "^8.4.1",
117
+ "@octokit/request-error": "^5.1.1",
118
+ "@octokit/types": "^13.0.0",
119
+ "before-after-hook": "^2.2.0",
120
+ "universal-user-agent": "^6.0.0"
121
+ },
122
+ "engines": {
123
+ "node": ">= 18"
124
+ }
125
+ },
126
+ "node_modules/@octokit/endpoint": {
127
+ "version": "9.0.6",
128
+ "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz",
129
+ "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==",
130
+ "dependencies": {
131
+ "@octokit/types": "^13.1.0",
132
+ "universal-user-agent": "^6.0.0"
133
+ },
134
+ "engines": {
135
+ "node": ">= 18"
136
+ }
137
+ },
138
+ "node_modules/@octokit/graphql": {
139
+ "version": "7.1.1",
140
+ "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz",
141
+ "integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==",
142
+ "dependencies": {
143
+ "@octokit/request": "^8.4.1",
144
+ "@octokit/types": "^13.0.0",
145
+ "universal-user-agent": "^6.0.0"
146
+ },
147
+ "engines": {
148
+ "node": ">= 18"
149
+ }
150
+ },
151
+ "node_modules/@octokit/oauth-authorization-url": {
152
+ "version": "6.0.2",
153
+ "resolved": "https://registry.npmjs.org/@octokit/oauth-authorization-url/-/oauth-authorization-url-6.0.2.tgz",
154
+ "integrity": "sha512-CdoJukjXXxqLNK4y/VOiVzQVjibqoj/xHgInekviUJV73y/BSIcwvJ/4aNHPBPKcPWFnd4/lO9uqRV65jXhcLA==",
155
+ "engines": {
156
+ "node": ">= 18"
157
+ }
158
+ },
159
+ "node_modules/@octokit/oauth-methods": {
160
+ "version": "4.1.0",
161
+ "resolved": "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-4.1.0.tgz",
162
+ "integrity": "sha512-4tuKnCRecJ6CG6gr0XcEXdZtkTDbfbnD5oaHBmLERTjTMZNi2CbfEHZxPU41xXLDG4DfKf+sonu00zvKI9NSbw==",
163
+ "dependencies": {
164
+ "@octokit/oauth-authorization-url": "^6.0.2",
165
+ "@octokit/request": "^8.3.1",
166
+ "@octokit/request-error": "^5.1.0",
167
+ "@octokit/types": "^13.0.0",
168
+ "btoa-lite": "^1.0.0"
169
+ },
170
+ "engines": {
171
+ "node": ">= 18"
172
+ }
173
+ },
174
+ "node_modules/@octokit/openapi-types": {
175
+ "version": "24.2.0",
176
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
177
+ "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="
178
+ },
179
+ "node_modules/@octokit/plugin-paginate-rest": {
180
+ "version": "11.4.4-cjs.2",
181
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.4.4-cjs.2.tgz",
182
+ "integrity": "sha512-2dK6z8fhs8lla5PaOTgqfCGBxgAv/le+EhPs27KklPhm1bKObpu6lXzwfUEQ16ajXzqNrKMujsFyo9K2eaoISw==",
183
+ "dependencies": {
184
+ "@octokit/types": "^13.7.0"
185
+ },
186
+ "engines": {
187
+ "node": ">= 18"
188
+ },
189
+ "peerDependencies": {
190
+ "@octokit/core": "5"
191
+ }
192
+ },
193
+ "node_modules/@octokit/plugin-request-log": {
194
+ "version": "4.0.1",
195
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-4.0.1.tgz",
196
+ "integrity": "sha512-GihNqNpGHorUrO7Qa9JbAl0dbLnqJVrV8OXe2Zm5/Y4wFkZQDfTreBzVmiRfJVfE4mClXdihHnbpyyO9FSX4HA==",
197
+ "engines": {
198
+ "node": ">= 18"
199
+ },
200
+ "peerDependencies": {
201
+ "@octokit/core": "5"
202
+ }
203
+ },
204
+ "node_modules/@octokit/plugin-rest-endpoint-methods": {
205
+ "version": "13.3.2-cjs.1",
206
+ "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.3.2-cjs.1.tgz",
207
+ "integrity": "sha512-VUjIjOOvF2oELQmiFpWA1aOPdawpyaCUqcEBc/UOUnj3Xp6DJGrJ1+bjUIIDzdHjnFNO6q57ODMfdEZnoBkCwQ==",
208
+ "dependencies": {
209
+ "@octokit/types": "^13.8.0"
210
+ },
211
+ "engines": {
212
+ "node": ">= 18"
213
+ },
214
+ "peerDependencies": {
215
+ "@octokit/core": "^5"
216
+ }
217
+ },
218
+ "node_modules/@octokit/request": {
219
+ "version": "8.4.1",
220
+ "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz",
221
+ "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==",
222
+ "dependencies": {
223
+ "@octokit/endpoint": "^9.0.6",
224
+ "@octokit/request-error": "^5.1.1",
225
+ "@octokit/types": "^13.1.0",
226
+ "universal-user-agent": "^6.0.0"
227
+ },
228
+ "engines": {
229
+ "node": ">= 18"
230
+ }
231
+ },
232
+ "node_modules/@octokit/request-error": {
233
+ "version": "5.1.1",
234
+ "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz",
235
+ "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==",
236
+ "dependencies": {
237
+ "@octokit/types": "^13.1.0",
238
+ "deprecation": "^2.0.0",
239
+ "once": "^1.4.0"
240
+ },
241
+ "engines": {
242
+ "node": ">= 18"
243
+ }
244
+ },
245
+ "node_modules/@octokit/rest": {
246
+ "version": "20.1.2",
247
+ "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.1.2.tgz",
248
+ "integrity": "sha512-GmYiltypkHHtihFwPRxlaorG5R9VAHuk/vbszVoRTGXnAsY60wYLkh/E2XiFmdZmqrisw+9FaazS1i5SbdWYgA==",
249
+ "dependencies": {
250
+ "@octokit/core": "^5.0.2",
251
+ "@octokit/plugin-paginate-rest": "11.4.4-cjs.2",
252
+ "@octokit/plugin-request-log": "^4.0.0",
253
+ "@octokit/plugin-rest-endpoint-methods": "13.3.2-cjs.1"
254
+ },
255
+ "engines": {
256
+ "node": ">= 18"
257
+ }
258
+ },
259
+ "node_modules/@octokit/types": {
260
+ "version": "13.10.0",
261
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
262
+ "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
263
+ "dependencies": {
264
+ "@octokit/openapi-types": "^24.2.0"
265
+ }
266
+ },
267
+ "node_modules/@types/btoa-lite": {
268
+ "version": "1.0.2",
269
+ "resolved": "https://registry.npmjs.org/@types/btoa-lite/-/btoa-lite-1.0.2.tgz",
270
+ "integrity": "sha512-ZYbcE2x7yrvNFJiU7xJGrpF/ihpkM7zKgw8bha3LNJSesvTtUNxbpzaT7WXBIryf6jovisrxTBvymxMeLLj1Mg=="
271
+ },
272
+ "node_modules/@types/jsonwebtoken": {
273
+ "version": "9.0.10",
274
+ "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz",
275
+ "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==",
276
+ "dependencies": {
277
+ "@types/ms": "*",
278
+ "@types/node": "*"
279
+ }
280
+ },
281
+ "node_modules/@types/ms": {
282
+ "version": "2.1.0",
283
+ "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
284
+ "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="
285
+ },
286
+ "node_modules/@types/node": {
287
+ "version": "24.10.1",
288
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
289
+ "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
290
+ "dependencies": {
291
+ "undici-types": "~7.16.0"
292
+ }
293
+ },
294
  "node_modules/accepts": {
295
  "version": "1.3.8",
296
  "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
 
342
  "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
343
  "dev": true
344
  },
345
+ "node_modules/before-after-hook": {
346
+ "version": "2.2.3",
347
+ "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
348
+ "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
349
+ },
350
  "node_modules/binary-extensions": {
351
  "version": "2.3.0",
352
  "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
 
404
  "node": ">=8"
405
  }
406
  },
407
+ "node_modules/btoa-lite": {
408
+ "version": "1.0.0",
409
+ "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz",
410
+ "integrity": "sha512-gvW7InbIyF8AicrqWoptdW08pUxuhq8BEgowNajy9RhiE86fmGAGl+bLKo6oB8QP0CkqHLowfN0oJdKC/J6LbA=="
411
+ },
412
+ "node_modules/buffer-equal-constant-time": {
413
+ "version": "1.0.1",
414
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
415
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
416
+ },
417
  "node_modules/bytes": {
418
  "version": "3.1.2",
419
  "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
 
558
  "node": ">= 0.8"
559
  }
560
  },
561
+ "node_modules/deprecation": {
562
+ "version": "2.3.1",
563
+ "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
564
+ "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
565
+ },
566
  "node_modules/destroy": {
567
  "version": "1.2.0",
568
  "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
 
596
  "node": ">= 0.4"
597
  }
598
  },
599
+ "node_modules/ecdsa-sig-formatter": {
600
+ "version": "1.0.11",
601
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
602
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
603
+ "dependencies": {
604
+ "safe-buffer": "^5.0.1"
605
+ }
606
+ },
607
  "node_modules/ee-first": {
608
  "version": "1.1.1",
609
  "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
 
1007
  "node": ">=0.12.0"
1008
  }
1009
  },
1010
+ "node_modules/jsonwebtoken": {
1011
+ "version": "9.0.2",
1012
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
1013
+ "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
1014
+ "dependencies": {
1015
+ "jws": "^3.2.2",
1016
+ "lodash.includes": "^4.3.0",
1017
+ "lodash.isboolean": "^3.0.3",
1018
+ "lodash.isinteger": "^4.0.4",
1019
+ "lodash.isnumber": "^3.0.3",
1020
+ "lodash.isplainobject": "^4.0.6",
1021
+ "lodash.isstring": "^4.0.1",
1022
+ "lodash.once": "^4.0.0",
1023
+ "ms": "^2.1.1",
1024
+ "semver": "^7.5.4"
1025
+ },
1026
+ "engines": {
1027
+ "node": ">=12",
1028
+ "npm": ">=6"
1029
+ }
1030
+ },
1031
+ "node_modules/jsonwebtoken/node_modules/ms": {
1032
+ "version": "2.1.3",
1033
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1034
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
1035
+ },
1036
+ "node_modules/jwa": {
1037
+ "version": "1.4.2",
1038
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz",
1039
+ "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==",
1040
+ "dependencies": {
1041
+ "buffer-equal-constant-time": "^1.0.1",
1042
+ "ecdsa-sig-formatter": "1.0.11",
1043
+ "safe-buffer": "^5.0.1"
1044
+ }
1045
+ },
1046
+ "node_modules/jws": {
1047
+ "version": "3.2.2",
1048
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
1049
+ "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
1050
+ "dependencies": {
1051
+ "jwa": "^1.4.1",
1052
+ "safe-buffer": "^5.0.1"
1053
+ }
1054
+ },
1055
+ "node_modules/lodash.includes": {
1056
+ "version": "4.3.0",
1057
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
1058
+ "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
1059
+ },
1060
+ "node_modules/lodash.isboolean": {
1061
+ "version": "3.0.3",
1062
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
1063
+ "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
1064
+ },
1065
+ "node_modules/lodash.isinteger": {
1066
+ "version": "4.0.4",
1067
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
1068
+ "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
1069
+ },
1070
+ "node_modules/lodash.isnumber": {
1071
+ "version": "3.0.3",
1072
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
1073
+ "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="
1074
+ },
1075
+ "node_modules/lodash.isplainobject": {
1076
+ "version": "4.0.6",
1077
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
1078
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
1079
+ },
1080
+ "node_modules/lodash.isstring": {
1081
+ "version": "4.0.1",
1082
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
1083
+ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
1084
+ },
1085
+ "node_modules/lodash.once": {
1086
+ "version": "4.1.1",
1087
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
1088
+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
1089
+ },
1090
+ "node_modules/lru-cache": {
1091
+ "name": "@wolfy1339/lru-cache",
1092
+ "version": "11.0.2-patch.1",
1093
+ "resolved": "https://registry.npmjs.org/@wolfy1339/lru-cache/-/lru-cache-11.0.2-patch.1.tgz",
1094
+ "integrity": "sha512-BgYZfL2ADCXKOw2wJtkM3slhHotawWkgIRRxq4wEybnZQPjvAp71SPX35xepMykTw8gXlzWcWPTY31hlbnRsDA==",
1095
+ "engines": {
1096
+ "node": "18 >=18.20 || 20 || >=22"
1097
+ }
1098
+ },
1099
  "node_modules/math-intrinsics": {
1100
  "version": "1.1.0",
1101
  "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
 
1273
  "node": ">= 0.8"
1274
  }
1275
  },
1276
+ "node_modules/once": {
1277
+ "version": "1.4.0",
1278
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
1279
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
1280
+ "dependencies": {
1281
+ "wrappy": "1"
1282
+ }
1283
+ },
1284
  "node_modules/parseurl": {
1285
  "version": "1.3.3",
1286
  "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
 
1405
  "version": "7.7.3",
1406
  "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
1407
  "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
 
1408
  "bin": {
1409
  "semver": "bin/semver.js"
1410
  },
 
1614
  "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
1615
  "dev": true
1616
  },
1617
+ "node_modules/undici-types": {
1618
+ "version": "7.16.0",
1619
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
1620
+ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="
1621
+ },
1622
+ "node_modules/universal-github-app-jwt": {
1623
+ "version": "1.2.0",
1624
+ "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-1.2.0.tgz",
1625
+ "integrity": "sha512-dncpMpnsKBk0eetwfN8D8OUHGfiDhhJ+mtsbMl+7PfW7mYjiH8LIcqRmYMtzYLgSh47HjfdBtrBwIQ/gizKR3g==",
1626
+ "dependencies": {
1627
+ "@types/jsonwebtoken": "^9.0.0",
1628
+ "jsonwebtoken": "^9.0.2"
1629
+ }
1630
+ },
1631
+ "node_modules/universal-user-agent": {
1632
+ "version": "6.0.1",
1633
+ "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
1634
+ "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ=="
1635
+ },
1636
  "node_modules/unpipe": {
1637
  "version": "1.0.0",
1638
  "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
 
1656
  "engines": {
1657
  "node": ">= 0.8"
1658
  }
1659
+ },
1660
+ "node_modules/wrappy": {
1661
+ "version": "1.0.2",
1662
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
1663
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
1664
  }
1665
  }
1666
  }
public/package.json CHANGED
@@ -12,6 +12,8 @@
12
  },
13
  "dependencies": {
14
  "@google/generative-ai": "^0.7.0",
 
 
15
  "express": "^4.18.2",
16
  "axios": "^1.6.0",
17
  "dotenv": "^16.3.1",
 
12
  },
13
  "dependencies": {
14
  "@google/generative-ai": "^0.7.0",
15
+ "@octokit/auth-app": "^6.0.3",
16
+ "@octokit/rest": "^20.0.2",
17
  "express": "^4.18.2",
18
  "axios": "^1.6.0",
19
  "dotenv": "^16.3.1",
public/src/agents/doc-generator.js ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Documentation Generator Agent
3
+ * Auto-generates documentation for project files
4
+ */
5
+
6
+ import { projectService } from '../services/project.js';
7
+ import { geminiService } from '../services/gemini.js';
8
+ import { logger } from '../config/logger.js';
9
+
10
+ class DocGeneratorAgent {
11
+ /**
12
+ * Generate documentation for project structure
13
+ * @param {Object} structure - Project structure
14
+ * @returns {Promise<string>} Generated documentation
15
+ */
16
+ async generateProjectDocumentation(structure) {
17
+ try {
18
+ const prompt = `Generate concise documentation for this project structure. Include:
19
+ 1. Project overview
20
+ 2. Directory structure explanation
21
+ 3. Key files purpose
22
+ 4. How to get started
23
+
24
+ Project structure:
25
+ - Files: ${structure.files.map((f) => f.name).join(', ')}
26
+ - Directories: ${structure.directories.map((d) => d.name).join(', ')}`;
27
+
28
+ const doc = await geminiService.generate(prompt);
29
+ return doc;
30
+ } catch (error) {
31
+ logger.error('Failed to generate documentation', { error: error.message });
32
+ throw error;
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Generate file-specific documentation
38
+ * @param {string} filePath - Path to file
39
+ * @returns {Promise<string>} File documentation
40
+ */
41
+ async generateFileDocumentation(filePath) {
42
+ try {
43
+ const content = await projectService.getFileContent(filePath);
44
+ const language = this.getLanguageFromPath(filePath);
45
+
46
+ const prompt = `Analyze this ${language} file and generate:
47
+ 1. Purpose/Summary
48
+ 2. Key functions or exports
49
+ 3. Dependencies
50
+ 4. Usage example (if applicable)
51
+
52
+ Code:
53
+ \`\`\`${language}
54
+ ${content.substring(0, 2000)}
55
+ \`\`\``;
56
+
57
+ const doc = await geminiService.generate(prompt);
58
+ return doc;
59
+ } catch (error) {
60
+ logger.error('Failed to generate file documentation', {
61
+ error: error.message,
62
+ file: filePath,
63
+ });
64
+ throw error;
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Generate README for project
70
+ * @returns {Promise<string>} Generated README
71
+ */
72
+ async generateReadme() {
73
+ try {
74
+ const structure = await projectService.getProjectStructure();
75
+ const keyFiles = structure.files.filter(
76
+ (f) =>
77
+ f.name.match(/package\.json|\.env\.example|Dockerfile|docker-compose/i) ||
78
+ f.name.endsWith('.md')
79
+ );
80
+
81
+ const prompt = `Create a professional README.md for a Node.js AI agent project. Include:
82
+ 1. Project title and description
83
+ 2. Features
84
+ 3. Prerequisites
85
+ 4. Installation steps
86
+ 5. Configuration (with example .env)
87
+ 6. Running the application
88
+ 7. API documentation
89
+ 8. Architecture overview
90
+ 9. Contributing
91
+ 10. License
92
+
93
+ Key files in project: ${keyFiles.map((f) => f.name).join(', ')}`;
94
+
95
+ const readme = await geminiService.generate(prompt);
96
+ return readme;
97
+ } catch (error) {
98
+ logger.error('Failed to generate README', { error: error.message });
99
+ throw error;
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Helper: Get language from file path
105
+ * @param {string} filePath - File path
106
+ * @returns {string} Language
107
+ */
108
+ getLanguageFromPath(filePath) {
109
+ if (filePath.endsWith('.js')) return 'javascript';
110
+ if (filePath.endsWith('.ts')) return 'typescript';
111
+ if (filePath.endsWith('.jsx')) return 'jsx';
112
+ if (filePath.endsWith('.tsx')) return 'tsx';
113
+ if (filePath.endsWith('.py')) return 'python';
114
+ if (filePath.endsWith('.java')) return 'java';
115
+ if (filePath.endsWith('.go')) return 'go';
116
+ if (filePath.endsWith('.json')) return 'json';
117
+ if (filePath.endsWith('.yaml') || filePath.endsWith('.yml')) return 'yaml';
118
+ return 'text';
119
+ }
120
+ }
121
+
122
+ export const docGeneratorAgent = new DocGeneratorAgent();
public/src/agents/scanner.js CHANGED
@@ -188,14 +188,14 @@ For each suggestion provide:
188
  const issueBody = this.formatScanReportAsIssue(report);
189
 
190
  const issue = await githubService.createIssue({
191
- title: `🤖 AI Scanner: Issues Detected`,
192
  body: issueBody,
193
- labels: ['ai-detected', 'needs-review'],
194
  });
195
 
196
- logger.info('Created issue from scan', { issueNumber: issue.number });
197
  } catch (error) {
198
- logger.error('Failed to create issue', { error: error.message });
199
  }
200
  }
201
 
@@ -206,24 +206,39 @@ For each suggestion provide:
206
  */
207
  formatScanReportAsIssue(report) {
208
  let body = '## 🤖 AI Scanner Report\n\n';
 
 
209
 
210
  if (report.issues.length > 0) {
211
- body += '### Issues Found\n';
212
  report.issues.forEach((issue) => {
213
  body += `- **[${issue.severity}]** ${issue.message}\n`;
214
  body += ` - File: \`${issue.file}\`\n`;
215
  body += ` - Type: ${issue.type}\n\n`;
216
  });
 
 
217
  }
218
 
219
- if (report.recommendations.length > 0) {
220
- body += '### Recommendations\n';
221
  report.recommendations.forEach((rec) => {
222
- body += `- **[${rec.priority}]** ${rec.suggestion}\n`;
223
  });
224
  }
225
 
226
- body += `\n---\n_Scanned at: ${report.timestamp}_\n_Scan duration: ${report.duration}ms_`;
 
 
 
 
 
 
 
 
 
 
 
227
 
228
  return body;
229
  }
 
188
  const issueBody = this.formatScanReportAsIssue(report);
189
 
190
  const issue = await githubService.createIssue({
191
+ title: `🤖 AI Scanner: Issues Detected (${new Date().toLocaleDateString()})`,
192
  body: issueBody,
193
+ labels: ['ai-detected', 'needs-review', 'auto-generated'],
194
  });
195
 
196
+ logger.info('Created issue from scan', { issueNumber: issue.number, repo: config.GITHUB_REPO });
197
  } catch (error) {
198
+ logger.error('Failed to create issue', { error: error.message, repo: config.GITHUB_REPO });
199
  }
200
  }
201
 
 
206
  */
207
  formatScanReportAsIssue(report) {
208
  let body = '## 🤖 AI Scanner Report\n\n';
209
+ body += `**Repository:** ${config.GITHUB_REPO}\n`;
210
+ body += `**Branch:** ${config.GITHUB_BRANCH}\n\n`;
211
 
212
  if (report.issues.length > 0) {
213
+ body += '### 🚨 Issues Found\n';
214
  report.issues.forEach((issue) => {
215
  body += `- **[${issue.severity}]** ${issue.message}\n`;
216
  body += ` - File: \`${issue.file}\`\n`;
217
  body += ` - Type: ${issue.type}\n\n`;
218
  });
219
+ } else {
220
+ body += '### ✅ No Critical Issues Found\n\n';
221
  }
222
 
223
+ if (report.recommendations && report.recommendations.length > 0) {
224
+ body += '### 💡 Recommendations\n';
225
  report.recommendations.forEach((rec) => {
226
+ body += `- **[${rec.priority}]** ${rec.suggestion}\n\n`;
227
  });
228
  }
229
 
230
+ if (report.analysis && report.analysis.files.length > 0) {
231
+ body += '### 📊 Code Analysis\n';
232
+ report.analysis.files.forEach((f) => {
233
+ body += `- **${f.name}**: Analyzed successfully\n`;
234
+ });
235
+ body += '\n';
236
+ }
237
+
238
+ body += `---\n`;
239
+ body += `_Scanned at: ${report.timestamp}_\n`;
240
+ body += `_Scan duration: ${report.duration}ms_\n`;
241
+ body += `_By: AI Scanner Agent_`;
242
 
243
  return body;
244
  }
public/src/api/docs.js ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Documentation API Routes
3
+ * Endpoints for generating project documentation
4
+ */
5
+
6
+ import express from 'express';
7
+ import { docGeneratorAgent } from '../agents/doc-generator.js';
8
+ import { projectService } from '../services/project.js';
9
+ import { logger } from '../config/logger.js';
10
+
11
+ const router = express.Router();
12
+
13
+ /**
14
+ * POST /api/docs/project
15
+ * Generate project documentation
16
+ */
17
+ router.post('/project', async (req, res) => {
18
+ try {
19
+ logger.info('Generating project documentation');
20
+ const structure = await projectService.getProjectStructure();
21
+ const doc = await docGeneratorAgent.generateProjectDocumentation(structure);
22
+
23
+ res.json({
24
+ success: true,
25
+ documentation: doc,
26
+ timestamp: new Date().toISOString(),
27
+ });
28
+ } catch (error) {
29
+ logger.error('Failed to generate project documentation', { error: error.message });
30
+ res.status(500).json({
31
+ error: 'Documentation generation failed',
32
+ details: error.message,
33
+ });
34
+ }
35
+ });
36
+
37
+ /**
38
+ * POST /api/docs/file
39
+ * Generate documentation for a specific file
40
+ */
41
+ router.post('/file', async (req, res) => {
42
+ try {
43
+ const { filePath } = req.body;
44
+
45
+ if (!filePath) {
46
+ return res.status(400).json({
47
+ error: 'Missing required field: filePath',
48
+ });
49
+ }
50
+
51
+ logger.info('Generating file documentation', { file: filePath });
52
+ const doc = await docGeneratorAgent.generateFileDocumentation(filePath);
53
+
54
+ res.json({
55
+ success: true,
56
+ file: filePath,
57
+ documentation: doc,
58
+ timestamp: new Date().toISOString(),
59
+ });
60
+ } catch (error) {
61
+ logger.error('Failed to generate file documentation', { error: error.message });
62
+ res.status(500).json({
63
+ error: 'File documentation generation failed',
64
+ details: error.message,
65
+ });
66
+ }
67
+ });
68
+
69
+ /**
70
+ * POST /api/docs/readme
71
+ * Generate README for project
72
+ */
73
+ router.post('/readme', async (req, res) => {
74
+ try {
75
+ logger.info('Generating README');
76
+ const readme = await docGeneratorAgent.generateReadme();
77
+
78
+ res.json({
79
+ success: true,
80
+ readme,
81
+ timestamp: new Date().toISOString(),
82
+ });
83
+ } catch (error) {
84
+ logger.error('Failed to generate README', { error: error.message });
85
+ res.status(500).json({
86
+ error: 'README generation failed',
87
+ details: error.message,
88
+ });
89
+ }
90
+ });
91
+
92
+ export default router;
public/src/config/env.js CHANGED
@@ -10,9 +10,14 @@ export const config = {
10
 
11
  // API Keys
12
  GEMINI_API_KEY: process.env.GEMINI_API_KEY || '',
13
- GITHUB_TOKEN: process.env.GITHUB_TOKEN || '',
14
  HF_TOKEN: process.env.HF_TOKEN || '',
15
 
 
 
 
 
 
16
  // GitHub
17
  GITHUB_REPO: process.env.GITHUB_REPO || 'NLarchive/my-webapp-hf',
18
  GITHUB_OWNER: process.env.GITHUB_OWNER || 'NLarchive',
@@ -33,9 +38,17 @@ export const config = {
33
  };
34
 
35
  export function validateConfig() {
36
- const required = ['GEMINI_API_KEY', 'GITHUB_TOKEN'];
 
 
37
  const missing = required.filter(key => !config[key]);
38
 
 
 
 
 
 
 
39
  if (missing.length > 0) {
40
  console.error(`Missing required environment variables: ${missing.join(', ')}`);
41
  process.exit(1);
 
10
 
11
  // API Keys
12
  GEMINI_API_KEY: process.env.GEMINI_API_KEY || '',
13
+ GITHUB_TOKEN: process.env.GITHUB_TOKEN || '', // Fallback for backward compatibility
14
  HF_TOKEN: process.env.HF_TOKEN || '',
15
 
16
+ // GitHub App Authentication
17
+ GH_APP_ID: process.env.GH_APP_ID || '',
18
+ GH_APP_PRIVATE_KEY_B64: process.env.GH_APP_PRIVATE_KEY_B64 || '',
19
+ GH_APP_INSTALLATION_ID: process.env.GH_APP_INSTALLATION_ID || '',
20
+
21
  // GitHub
22
  GITHUB_REPO: process.env.GITHUB_REPO || 'NLarchive/my-webapp-hf',
23
  GITHUB_OWNER: process.env.GITHUB_OWNER || 'NLarchive',
 
38
  };
39
 
40
  export function validateConfig() {
41
+ const required = ['GEMINI_API_KEY'];
42
+ const githubAuth = ['GITHUB_TOKEN', 'GH_APP_ID', 'GH_APP_PRIVATE_KEY_B64', 'GH_APP_INSTALLATION_ID'];
43
+
44
  const missing = required.filter(key => !config[key]);
45
 
46
+ // Check if at least one GitHub auth method is configured
47
+ const hasGitHubAuth = githubAuth.some(key => config[key]);
48
+ if (!hasGitHubAuth) {
49
+ missing.push('GitHub authentication (GITHUB_TOKEN or GH_APP_* variables)');
50
+ }
51
+
52
  if (missing.length > 0) {
53
  console.error(`Missing required environment variables: ${missing.join(', ')}`);
54
  process.exit(1);
public/src/server.js CHANGED
@@ -14,6 +14,7 @@ import { scannerAgent } from './agents/scanner.js';
14
  import chatRoutes from './api/chat.js';
15
  import scannerRoutes from './api/scanner.js';
16
  import projectRoutes from './api/project.js';
 
17
 
18
  const app = express();
19
 
@@ -41,6 +42,7 @@ app.get('/health', (req, res) => {
41
  app.use('/api/chat', chatRoutes);
42
  app.use('/api/scanner', scannerRoutes);
43
  app.use('/api/project', projectRoutes);
 
44
 
45
  // Serve static files (HTML, CSS, JS)
46
  app.use(express.static('.'));
@@ -66,16 +68,25 @@ async function startServer() {
66
  validateConfig();
67
 
68
  // Start server
69
- app.listen(config.PORT, () => {
70
  logger.info(`Server started on port ${config.PORT}`);
 
 
71
  });
72
 
73
  // Start scanner if enabled
74
  if (config.ENABLE_AUTO_FIX) {
 
75
  scannerAgent.startPeriodicScanning();
76
  } else {
77
- logger.info('Auto-fix is disabled');
78
  }
 
 
 
 
 
 
79
  } catch (error) {
80
  logger.error('Failed to start server', { error: error.message });
81
  process.exit(1);
 
14
  import chatRoutes from './api/chat.js';
15
  import scannerRoutes from './api/scanner.js';
16
  import projectRoutes from './api/project.js';
17
+ import docsRoutes from './api/docs.js';
18
 
19
  const app = express();
20
 
 
42
  app.use('/api/chat', chatRoutes);
43
  app.use('/api/scanner', scannerRoutes);
44
  app.use('/api/project', projectRoutes);
45
+ app.use('/api/docs', docsRoutes);
46
 
47
  // Serve static files (HTML, CSS, JS)
48
  app.use(express.static('.'));
 
68
  validateConfig();
69
 
70
  // Start server
71
+ const server = app.listen(config.PORT, () => {
72
  logger.info(`Server started on port ${config.PORT}`);
73
+ logger.info(`Environment: ${config.NODE_ENV}`);
74
+ logger.info(`Health check: http://localhost:${config.PORT}/health`);
75
  });
76
 
77
  // Start scanner if enabled
78
  if (config.ENABLE_AUTO_FIX) {
79
+ logger.info('Starting periodic scanner...');
80
  scannerAgent.startPeriodicScanning();
81
  } else {
82
+ logger.info('Auto-fix is disabled (ENABLE_AUTO_FIX=false)');
83
  }
84
+
85
+ // Handle server errors
86
+ server.on('error', (err) => {
87
+ logger.error('Server error', { error: err.message });
88
+ process.exit(1);
89
+ });
90
  } catch (error) {
91
  logger.error('Failed to start server', { error: error.message });
92
  process.exit(1);
public/src/services/github.js CHANGED
@@ -3,19 +3,51 @@
3
  * Handles GitHub API interactions (PR creation, issue creation, commits, etc.)
4
  */
5
 
6
- import axios from 'axios';
 
7
  import { config } from '../config/env.js';
8
  import { logger } from '../config/logger.js';
9
 
10
  class GitHubService {
11
  constructor() {
12
- this.api = axios.create({
13
- baseURL: 'https://api.github.com',
14
- headers: {
15
- Authorization: `token ${config.GITHUB_TOKEN}`,
16
- Accept: 'application/vnd.github.v3+json',
17
- },
18
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  }
20
 
21
  /**
@@ -26,11 +58,15 @@ class GitHubService {
26
  async createIssue(issue) {
27
  try {
28
  const { title, body, labels = [], assignees = [] } = issue;
29
-
30
- const response = await this.api.post(
31
- `/repos/${config.GITHUB_REPO}/issues`,
32
- { title, body, labels, assignees }
33
- );
 
 
 
 
34
 
35
  logger.info('GitHub issue created', { issueNumber: response.data.number });
36
  return response.data;
@@ -55,10 +91,15 @@ class GitHubService {
55
  draft = false,
56
  } = pr;
57
 
58
- const response = await this.api.post(
59
- `/repos/${config.GITHUB_REPO}/pulls`,
60
- { title, body, head, base, draft }
61
- );
 
 
 
 
 
62
 
63
  logger.info('GitHub PR created', { prNumber: response.data.number });
64
  return response.data;
@@ -75,9 +116,11 @@ class GitHubService {
75
  */
76
  async getFileContents(path = '') {
77
  try {
78
- const response = await this.api.get(
79
- `/repos/${config.GITHUB_REPO}/contents/${path}`
80
- );
 
 
81
 
82
  return response.data;
83
  } catch (error) {
@@ -113,13 +156,13 @@ class GitHubService {
113
  */
114
  async triggerWorkflow(workflowId, inputs = {}) {
115
  try {
116
- const response = await this.api.post(
117
- `/repos/${config.GITHUB_REPO}/actions/workflows/${workflowId}/dispatches`,
118
- {
119
- ref: config.GITHUB_BRANCH,
120
- inputs,
121
- }
122
- );
123
 
124
  logger.info('Workflow triggered', { workflowId });
125
  return response.data;
@@ -135,7 +178,10 @@ class GitHubService {
135
  */
136
  async getRepoInfo() {
137
  try {
138
- const response = await this.api.get(`/repos/${config.GITHUB_REPO}`);
 
 
 
139
  return response.data;
140
  } catch (error) {
141
  logger.error('Failed to get repo info', { error: error.message });
@@ -150,10 +196,11 @@ class GitHubService {
150
  */
151
  async listIssues(state = 'open') {
152
  try {
153
- const response = await this.api.get(
154
- `/repos/${config.GITHUB_REPO}/issues`,
155
- { params: { state } }
156
- );
 
157
  return response.data;
158
  } catch (error) {
159
  logger.error('Failed to list issues', { error: error.message });
@@ -169,10 +216,12 @@ class GitHubService {
169
  */
170
  async addIssueComment(issueNumber, body) {
171
  try {
172
- const response = await this.api.post(
173
- `/repos/${config.GITHUB_REPO}/issues/${issueNumber}/comments`,
174
- { body }
175
- );
 
 
176
 
177
  logger.info('Issue comment added', { issueNumber });
178
  return response.data;
 
3
  * Handles GitHub API interactions (PR creation, issue creation, commits, etc.)
4
  */
5
 
6
+ import { Octokit } from '@octokit/rest';
7
+ import { createAppAuth } from '@octokit/auth-app';
8
  import { config } from '../config/env.js';
9
  import { logger } from '../config/logger.js';
10
 
11
  class GitHubService {
12
  constructor() {
13
+ this.octokit = null;
14
+ this.initializeOctokit();
15
+ }
16
+
17
+ async initializeOctokit() {
18
+ try {
19
+ // Try GitHub App authentication first
20
+ if (config.GH_APP_ID && config.GH_APP_PRIVATE_KEY_B64 && config.GH_APP_INSTALLATION_ID) {
21
+ const privateKey = Buffer.from(config.GH_APP_PRIVATE_KEY_B64, 'base64').toString('utf8');
22
+
23
+ const auth = createAppAuth({
24
+ appId: config.GH_APP_ID,
25
+ privateKey,
26
+ installationId: config.GH_APP_INSTALLATION_ID,
27
+ });
28
+
29
+ this.octokit = new Octokit({
30
+ authStrategy: auth,
31
+ auth: { type: 'installation' },
32
+ });
33
+
34
+ logger.info('GitHub App authentication initialized');
35
+ }
36
+ // Fallback to personal access token
37
+ else if (config.GITHUB_TOKEN) {
38
+ this.octokit = new Octokit({
39
+ auth: config.GITHUB_TOKEN,
40
+ });
41
+
42
+ logger.info('GitHub token authentication initialized');
43
+ }
44
+ else {
45
+ throw new Error('No GitHub authentication method configured');
46
+ }
47
+ } catch (error) {
48
+ logger.error('Failed to initialize GitHub client', { error: error.message });
49
+ throw error;
50
+ }
51
  }
52
 
53
  /**
 
58
  async createIssue(issue) {
59
  try {
60
  const { title, body, labels = [], assignees = [] } = issue;
61
+
62
+ const response = await this.octokit.issues.create({
63
+ owner: config.GITHUB_OWNER,
64
+ repo: config.GITHUB_REPO.split('/')[1],
65
+ title,
66
+ body,
67
+ labels,
68
+ assignees,
69
+ });
70
 
71
  logger.info('GitHub issue created', { issueNumber: response.data.number });
72
  return response.data;
 
91
  draft = false,
92
  } = pr;
93
 
94
+ const response = await this.octokit.pulls.create({
95
+ owner: config.GITHUB_OWNER,
96
+ repo: config.GITHUB_REPO.split('/')[1],
97
+ title,
98
+ body,
99
+ head,
100
+ base,
101
+ draft,
102
+ });
103
 
104
  logger.info('GitHub PR created', { prNumber: response.data.number });
105
  return response.data;
 
116
  */
117
  async getFileContents(path = '') {
118
  try {
119
+ const response = await this.octokit.repos.getContent({
120
+ owner: config.GITHUB_OWNER,
121
+ repo: config.GITHUB_REPO.split('/')[1],
122
+ path,
123
+ });
124
 
125
  return response.data;
126
  } catch (error) {
 
156
  */
157
  async triggerWorkflow(workflowId, inputs = {}) {
158
  try {
159
+ const response = await this.octokit.actions.createWorkflowDispatch({
160
+ owner: config.GITHUB_OWNER,
161
+ repo: config.GITHUB_REPO.split('/')[1],
162
+ workflow_id: workflowId,
163
+ ref: config.GITHUB_BRANCH,
164
+ inputs,
165
+ });
166
 
167
  logger.info('Workflow triggered', { workflowId });
168
  return response.data;
 
178
  */
179
  async getRepoInfo() {
180
  try {
181
+ const response = await this.octokit.repos.get({
182
+ owner: config.GITHUB_OWNER,
183
+ repo: config.GITHUB_REPO.split('/')[1],
184
+ });
185
  return response.data;
186
  } catch (error) {
187
  logger.error('Failed to get repo info', { error: error.message });
 
196
  */
197
  async listIssues(state = 'open') {
198
  try {
199
+ const response = await this.octokit.issues.listForRepo({
200
+ owner: config.GITHUB_OWNER,
201
+ repo: config.GITHUB_REPO.split('/')[1],
202
+ state,
203
+ });
204
  return response.data;
205
  } catch (error) {
206
  logger.error('Failed to list issues', { error: error.message });
 
216
  */
217
  async addIssueComment(issueNumber, body) {
218
  try {
219
+ const response = await this.octokit.issues.createComment({
220
+ owner: config.GITHUB_OWNER,
221
+ repo: config.GITHUB_REPO.split('/')[1],
222
+ issue_number: issueNumber,
223
+ body,
224
+ });
225
 
226
  logger.info('Issue comment added', { issueNumber });
227
  return response.data;