@oss-autopilot/core 0.41.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.
- package/LICENSE +21 -0
- package/README.md +85 -0
- package/dist/cli.bundle.cjs +17657 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.js +325 -0
- package/dist/commands/check-integration.d.ts +10 -0
- package/dist/commands/check-integration.js +192 -0
- package/dist/commands/comments.d.ts +24 -0
- package/dist/commands/comments.js +311 -0
- package/dist/commands/config.d.ts +11 -0
- package/dist/commands/config.js +82 -0
- package/dist/commands/daily.d.ts +29 -0
- package/dist/commands/daily.js +433 -0
- package/dist/commands/dashboard-data.d.ts +45 -0
- package/dist/commands/dashboard-data.js +132 -0
- package/dist/commands/dashboard-templates.d.ts +23 -0
- package/dist/commands/dashboard-templates.js +1627 -0
- package/dist/commands/dashboard.d.ts +18 -0
- package/dist/commands/dashboard.js +134 -0
- package/dist/commands/dismiss.d.ts +13 -0
- package/dist/commands/dismiss.js +49 -0
- package/dist/commands/init.d.ts +10 -0
- package/dist/commands/init.js +27 -0
- package/dist/commands/local-repos.d.ts +14 -0
- package/dist/commands/local-repos.js +155 -0
- package/dist/commands/parse-list.d.ts +13 -0
- package/dist/commands/parse-list.js +139 -0
- package/dist/commands/read.d.ts +12 -0
- package/dist/commands/read.js +33 -0
- package/dist/commands/search.d.ts +10 -0
- package/dist/commands/search.js +74 -0
- package/dist/commands/setup.d.ts +15 -0
- package/dist/commands/setup.js +276 -0
- package/dist/commands/shelve.d.ts +13 -0
- package/dist/commands/shelve.js +49 -0
- package/dist/commands/snooze.d.ts +18 -0
- package/dist/commands/snooze.js +83 -0
- package/dist/commands/startup.d.ts +33 -0
- package/dist/commands/startup.js +197 -0
- package/dist/commands/status.d.ts +10 -0
- package/dist/commands/status.js +43 -0
- package/dist/commands/track.d.ts +16 -0
- package/dist/commands/track.js +59 -0
- package/dist/commands/validation.d.ts +43 -0
- package/dist/commands/validation.js +112 -0
- package/dist/commands/vet.d.ts +10 -0
- package/dist/commands/vet.js +36 -0
- package/dist/core/checklist-analysis.d.ts +17 -0
- package/dist/core/checklist-analysis.js +39 -0
- package/dist/core/ci-analysis.d.ts +78 -0
- package/dist/core/ci-analysis.js +163 -0
- package/dist/core/comment-utils.d.ts +15 -0
- package/dist/core/comment-utils.js +52 -0
- package/dist/core/concurrency.d.ts +5 -0
- package/dist/core/concurrency.js +15 -0
- package/dist/core/daily-logic.d.ts +77 -0
- package/dist/core/daily-logic.js +512 -0
- package/dist/core/display-utils.d.ts +10 -0
- package/dist/core/display-utils.js +100 -0
- package/dist/core/errors.d.ts +24 -0
- package/dist/core/errors.js +34 -0
- package/dist/core/github-stats.d.ts +73 -0
- package/dist/core/github-stats.js +272 -0
- package/dist/core/github.d.ts +19 -0
- package/dist/core/github.js +60 -0
- package/dist/core/http-cache.d.ts +97 -0
- package/dist/core/http-cache.js +269 -0
- package/dist/core/index.d.ts +15 -0
- package/dist/core/index.js +15 -0
- package/dist/core/issue-conversation.d.ts +29 -0
- package/dist/core/issue-conversation.js +231 -0
- package/dist/core/issue-discovery.d.ts +85 -0
- package/dist/core/issue-discovery.js +589 -0
- package/dist/core/issue-filtering.d.ts +51 -0
- package/dist/core/issue-filtering.js +103 -0
- package/dist/core/issue-scoring.d.ts +40 -0
- package/dist/core/issue-scoring.js +92 -0
- package/dist/core/issue-vetting.d.ts +49 -0
- package/dist/core/issue-vetting.js +536 -0
- package/dist/core/logger.d.ts +21 -0
- package/dist/core/logger.js +49 -0
- package/dist/core/maintainer-analysis.d.ts +10 -0
- package/dist/core/maintainer-analysis.js +59 -0
- package/dist/core/pagination.d.ts +11 -0
- package/dist/core/pagination.js +20 -0
- package/dist/core/pr-monitor.d.ts +109 -0
- package/dist/core/pr-monitor.js +594 -0
- package/dist/core/review-analysis.d.ts +72 -0
- package/dist/core/review-analysis.js +163 -0
- package/dist/core/state.d.ts +371 -0
- package/dist/core/state.js +1089 -0
- package/dist/core/types.d.ts +507 -0
- package/dist/core/types.js +34 -0
- package/dist/core/utils.d.ts +249 -0
- package/dist/core/utils.js +422 -0
- package/dist/formatters/json.d.ts +269 -0
- package/dist/formatters/json.js +88 -0
- package/package.json +67 -0
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* OSS Autopilot CLI
|
|
4
|
+
* Entry point with commander for argument parsing
|
|
5
|
+
*
|
|
6
|
+
* Supports --json flag for structured output (used by Claude Code plugin)
|
|
7
|
+
*
|
|
8
|
+
* Performance: Command modules are lazy-loaded via dynamic import() so that
|
|
9
|
+
* only the invoked command's code is evaluated. The preAction hook uses an
|
|
10
|
+
* async token fetch to avoid blocking the event loop on `gh auth token`.
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* OSS Autopilot CLI
|
|
4
|
+
* Entry point with commander for argument parsing
|
|
5
|
+
*
|
|
6
|
+
* Supports --json flag for structured output (used by Claude Code plugin)
|
|
7
|
+
*
|
|
8
|
+
* Performance: Command modules are lazy-loaded via dynamic import() so that
|
|
9
|
+
* only the invoked command's code is evaluated. The preAction hook uses an
|
|
10
|
+
* async token fetch to avoid blocking the event loop on `gh auth token`.
|
|
11
|
+
*/
|
|
12
|
+
import { Command } from 'commander';
|
|
13
|
+
import { getGitHubTokenAsync, enableDebug, debug } from './core/index.js';
|
|
14
|
+
const VERSION = (() => {
|
|
15
|
+
try {
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const pkgPath = path.join(path.dirname(process.argv[1]), '..', 'package.json');
|
|
21
|
+
return JSON.parse(fs.readFileSync(pkgPath, 'utf-8')).version;
|
|
22
|
+
}
|
|
23
|
+
catch (_err) {
|
|
24
|
+
// package.json may not be readable in all bundle/install configurations ā fall back to safe default
|
|
25
|
+
return '0.0.0';
|
|
26
|
+
}
|
|
27
|
+
})();
|
|
28
|
+
// Commands that skip the preAction GitHub token check.
|
|
29
|
+
// startup handles auth internally (returns authError in JSON instead of process.exit).
|
|
30
|
+
const LOCAL_ONLY_COMMANDS = [
|
|
31
|
+
'help',
|
|
32
|
+
'status',
|
|
33
|
+
'config',
|
|
34
|
+
'read',
|
|
35
|
+
'untrack',
|
|
36
|
+
'version',
|
|
37
|
+
'setup',
|
|
38
|
+
'checkSetup',
|
|
39
|
+
'dashboard',
|
|
40
|
+
'parse-issue-list',
|
|
41
|
+
'check-integration',
|
|
42
|
+
'local-repos',
|
|
43
|
+
'startup',
|
|
44
|
+
'shelve',
|
|
45
|
+
'unshelve',
|
|
46
|
+
'dismiss',
|
|
47
|
+
'undismiss',
|
|
48
|
+
'snooze',
|
|
49
|
+
'unsnooze',
|
|
50
|
+
];
|
|
51
|
+
const program = new Command();
|
|
52
|
+
program
|
|
53
|
+
.name('oss-autopilot')
|
|
54
|
+
.description('AI-powered autopilot for managing open source contributions')
|
|
55
|
+
.version(VERSION)
|
|
56
|
+
.option('--debug', 'Enable debug logging');
|
|
57
|
+
// Daily check command
|
|
58
|
+
program
|
|
59
|
+
.command('daily')
|
|
60
|
+
.description('Run daily check on all tracked PRs')
|
|
61
|
+
.option('--json', 'Output as JSON')
|
|
62
|
+
.action(async (options) => {
|
|
63
|
+
const { runDaily } = await import('./commands/daily.js');
|
|
64
|
+
await runDaily({ json: options.json });
|
|
65
|
+
});
|
|
66
|
+
// Status command
|
|
67
|
+
program
|
|
68
|
+
.command('status')
|
|
69
|
+
.description('Show current status and stats')
|
|
70
|
+
.option('--json', 'Output as JSON')
|
|
71
|
+
.option('--offline', 'Use cached data only (no GitHub API calls)')
|
|
72
|
+
.action(async (options) => {
|
|
73
|
+
const { runStatus } = await import('./commands/status.js');
|
|
74
|
+
await runStatus({ json: options.json, offline: options.offline });
|
|
75
|
+
});
|
|
76
|
+
// Search command
|
|
77
|
+
program
|
|
78
|
+
.command('search [count]')
|
|
79
|
+
.description('Search for new issues to work on')
|
|
80
|
+
.option('--json', 'Output as JSON')
|
|
81
|
+
.action(async (count, options) => {
|
|
82
|
+
const { runSearch } = await import('./commands/search.js');
|
|
83
|
+
await runSearch({ maxResults: parseInt(count) || 5, json: options.json });
|
|
84
|
+
});
|
|
85
|
+
// Vet command
|
|
86
|
+
program
|
|
87
|
+
.command('vet <issue-url>')
|
|
88
|
+
.description('Vet a specific issue before working on it')
|
|
89
|
+
.option('--json', 'Output as JSON')
|
|
90
|
+
.action(async (issueUrl, options) => {
|
|
91
|
+
const { runVet } = await import('./commands/vet.js');
|
|
92
|
+
await runVet({ issueUrl, json: options.json });
|
|
93
|
+
});
|
|
94
|
+
// Track command
|
|
95
|
+
program
|
|
96
|
+
.command('track <pr-url>')
|
|
97
|
+
.description('Add a PR to track')
|
|
98
|
+
.option('--json', 'Output as JSON')
|
|
99
|
+
.action(async (prUrl, options) => {
|
|
100
|
+
const { runTrack } = await import('./commands/track.js');
|
|
101
|
+
await runTrack({ prUrl, json: options.json });
|
|
102
|
+
});
|
|
103
|
+
// Untrack command
|
|
104
|
+
program
|
|
105
|
+
.command('untrack <pr-url>')
|
|
106
|
+
.description('Stop tracking a PR')
|
|
107
|
+
.option('--json', 'Output as JSON')
|
|
108
|
+
.action(async (prUrl, options) => {
|
|
109
|
+
const { runUntrack } = await import('./commands/track.js');
|
|
110
|
+
await runUntrack({ prUrl, json: options.json });
|
|
111
|
+
});
|
|
112
|
+
// Read command (mark as read)
|
|
113
|
+
program
|
|
114
|
+
.command('read [pr-url]')
|
|
115
|
+
.description('Mark PR comments as read')
|
|
116
|
+
.option('--all', 'Mark all PRs as read')
|
|
117
|
+
.option('--json', 'Output as JSON')
|
|
118
|
+
.action(async (prUrl, options) => {
|
|
119
|
+
const { runRead } = await import('./commands/read.js');
|
|
120
|
+
await runRead({ prUrl, all: options.all, json: options.json });
|
|
121
|
+
});
|
|
122
|
+
// Comments command
|
|
123
|
+
program
|
|
124
|
+
.command('comments <pr-url>')
|
|
125
|
+
.description('Show all comments on a PR')
|
|
126
|
+
.option('--bots', 'Include bot comments')
|
|
127
|
+
.option('--json', 'Output as JSON')
|
|
128
|
+
.action(async (prUrl, options) => {
|
|
129
|
+
const { runComments } = await import('./commands/comments.js');
|
|
130
|
+
await runComments({ prUrl, showBots: options.bots, json: options.json });
|
|
131
|
+
});
|
|
132
|
+
// Post command
|
|
133
|
+
program
|
|
134
|
+
.command('post <url> [message...]')
|
|
135
|
+
.description('Post a comment to a PR or issue')
|
|
136
|
+
.option('--stdin', 'Read message from stdin')
|
|
137
|
+
.option('--json', 'Output as JSON')
|
|
138
|
+
.action(async (url, messageParts, options) => {
|
|
139
|
+
const { runPost } = await import('./commands/comments.js');
|
|
140
|
+
const message = options.stdin ? undefined : messageParts.join(' ');
|
|
141
|
+
await runPost({ url, message, stdin: options.stdin, json: options.json });
|
|
142
|
+
});
|
|
143
|
+
// Claim command
|
|
144
|
+
program
|
|
145
|
+
.command('claim <issue-url> [message...]')
|
|
146
|
+
.description('Claim an issue by posting a comment')
|
|
147
|
+
.option('--json', 'Output as JSON')
|
|
148
|
+
.action(async (issueUrl, messageParts, options) => {
|
|
149
|
+
const { runClaim } = await import('./commands/comments.js');
|
|
150
|
+
const message = messageParts.length > 0 ? messageParts.join(' ') : undefined;
|
|
151
|
+
await runClaim({ issueUrl, message, json: options.json });
|
|
152
|
+
});
|
|
153
|
+
// Config command
|
|
154
|
+
program
|
|
155
|
+
.command('config [key] [value]')
|
|
156
|
+
.description('Show or update configuration')
|
|
157
|
+
.option('--json', 'Output as JSON')
|
|
158
|
+
.action(async (key, value, options) => {
|
|
159
|
+
const { runConfig } = await import('./commands/config.js');
|
|
160
|
+
await runConfig({ key, value, json: options.json });
|
|
161
|
+
});
|
|
162
|
+
// Init command
|
|
163
|
+
program
|
|
164
|
+
.command('init <username>')
|
|
165
|
+
.description('Initialize with your GitHub username and import open PRs')
|
|
166
|
+
.option('--json', 'Output as JSON')
|
|
167
|
+
.action(async (username, options) => {
|
|
168
|
+
const { runInit } = await import('./commands/init.js');
|
|
169
|
+
await runInit({ username, json: options.json });
|
|
170
|
+
});
|
|
171
|
+
// Setup command
|
|
172
|
+
program
|
|
173
|
+
.command('setup')
|
|
174
|
+
.description('Interactive setup / configuration')
|
|
175
|
+
.option('--reset', 'Re-run setup even if already complete')
|
|
176
|
+
.option('--set <settings...>', 'Set specific values (key=value)')
|
|
177
|
+
.option('--json', 'Output as JSON')
|
|
178
|
+
.action(async (options) => {
|
|
179
|
+
const { runSetup } = await import('./commands/setup.js');
|
|
180
|
+
await runSetup({ reset: options.reset, set: options.set, json: options.json });
|
|
181
|
+
});
|
|
182
|
+
// Check setup command
|
|
183
|
+
program
|
|
184
|
+
.command('checkSetup')
|
|
185
|
+
.description('Check if setup is complete')
|
|
186
|
+
.option('--json', 'Output as JSON')
|
|
187
|
+
.action(async (options) => {
|
|
188
|
+
const { runCheckSetup } = await import('./commands/setup.js');
|
|
189
|
+
await runCheckSetup({ json: options.json });
|
|
190
|
+
});
|
|
191
|
+
// Dashboard command
|
|
192
|
+
program
|
|
193
|
+
.command('dashboard')
|
|
194
|
+
.description('Generate HTML stats dashboard')
|
|
195
|
+
.option('--open', 'Open in browser')
|
|
196
|
+
.option('--json', 'Output as JSON')
|
|
197
|
+
.option('--offline', 'Use cached data only (no GitHub API calls)')
|
|
198
|
+
.action(async (options) => {
|
|
199
|
+
const { runDashboard } = await import('./commands/dashboard.js');
|
|
200
|
+
await runDashboard({ open: options.open, json: options.json, offline: options.offline });
|
|
201
|
+
});
|
|
202
|
+
// Parse issue list command (#82)
|
|
203
|
+
program
|
|
204
|
+
.command('parse-issue-list <path>')
|
|
205
|
+
.description('Parse a markdown issue list into structured JSON')
|
|
206
|
+
.option('--json', 'Output as JSON')
|
|
207
|
+
.action(async (filePath, options) => {
|
|
208
|
+
const { runParseList } = await import('./commands/parse-list.js');
|
|
209
|
+
await runParseList({ filePath, json: options.json });
|
|
210
|
+
});
|
|
211
|
+
// Check integration command (#83)
|
|
212
|
+
program
|
|
213
|
+
.command('check-integration')
|
|
214
|
+
.description('Detect new files not referenced by the codebase')
|
|
215
|
+
.option('--base <branch>', 'Base branch to compare against', 'main')
|
|
216
|
+
.option('--json', 'Output as JSON')
|
|
217
|
+
.action(async (options) => {
|
|
218
|
+
const { runCheckIntegration } = await import('./commands/check-integration.js');
|
|
219
|
+
await runCheckIntegration({ base: options.base, json: options.json });
|
|
220
|
+
});
|
|
221
|
+
// Local repos command (#84)
|
|
222
|
+
program
|
|
223
|
+
.command('local-repos')
|
|
224
|
+
.description('Scan filesystem for local git clones')
|
|
225
|
+
.option('--scan', 'Force re-scan (ignores cache)')
|
|
226
|
+
.option('--paths <dirs...>', 'Directories to scan')
|
|
227
|
+
.option('--json', 'Output as JSON')
|
|
228
|
+
.action(async (options) => {
|
|
229
|
+
const { runLocalRepos } = await import('./commands/local-repos.js');
|
|
230
|
+
await runLocalRepos({ scan: options.scan, paths: options.paths, json: options.json });
|
|
231
|
+
});
|
|
232
|
+
// Startup command (combines auth, setup, daily, dashboard, issue list)
|
|
233
|
+
program
|
|
234
|
+
.command('startup')
|
|
235
|
+
.description('Run all pre-flight checks and daily fetch in one call')
|
|
236
|
+
.option('--json', 'Output as JSON')
|
|
237
|
+
.action(async (options) => {
|
|
238
|
+
const { runStartup } = await import('./commands/startup.js');
|
|
239
|
+
await runStartup({ json: options.json });
|
|
240
|
+
});
|
|
241
|
+
// Shelve command
|
|
242
|
+
program
|
|
243
|
+
.command('shelve <pr-url>')
|
|
244
|
+
.description('Shelve a PR (exclude from capacity and actionable issues)')
|
|
245
|
+
.option('--json', 'Output as JSON')
|
|
246
|
+
.action(async (prUrl, options) => {
|
|
247
|
+
const { runShelve } = await import('./commands/shelve.js');
|
|
248
|
+
await runShelve({ prUrl, json: options.json });
|
|
249
|
+
});
|
|
250
|
+
// Unshelve command
|
|
251
|
+
program
|
|
252
|
+
.command('unshelve <pr-url>')
|
|
253
|
+
.description('Unshelve a PR (include in capacity and actionable issues again)')
|
|
254
|
+
.option('--json', 'Output as JSON')
|
|
255
|
+
.action(async (prUrl, options) => {
|
|
256
|
+
const { runUnshelve } = await import('./commands/shelve.js');
|
|
257
|
+
await runUnshelve({ prUrl, json: options.json });
|
|
258
|
+
});
|
|
259
|
+
// Dismiss command
|
|
260
|
+
program
|
|
261
|
+
.command('dismiss <issue-url>')
|
|
262
|
+
.description('Dismiss issue reply notifications (resurfaces on new activity)')
|
|
263
|
+
.option('--json', 'Output as JSON')
|
|
264
|
+
.action(async (issueUrl, options) => {
|
|
265
|
+
const { runDismiss } = await import('./commands/dismiss.js');
|
|
266
|
+
await runDismiss({ issueUrl, json: options.json });
|
|
267
|
+
});
|
|
268
|
+
// Undismiss command
|
|
269
|
+
program
|
|
270
|
+
.command('undismiss <issue-url>')
|
|
271
|
+
.description('Undismiss an issue (re-enable reply notifications)')
|
|
272
|
+
.option('--json', 'Output as JSON')
|
|
273
|
+
.action(async (issueUrl, options) => {
|
|
274
|
+
const { runUndismiss } = await import('./commands/dismiss.js');
|
|
275
|
+
await runUndismiss({ issueUrl, json: options.json });
|
|
276
|
+
});
|
|
277
|
+
// Snooze command
|
|
278
|
+
program
|
|
279
|
+
.command('snooze <pr-url>')
|
|
280
|
+
.description('Snooze CI failure notifications for a PR')
|
|
281
|
+
.requiredOption('--reason <reason>', 'Reason for snoozing (e.g., "upstream infrastructure issue")')
|
|
282
|
+
.option('--days <days>', 'Number of days to snooze (default: 7)', '7')
|
|
283
|
+
.option('--json', 'Output as JSON')
|
|
284
|
+
.action(async (prUrl, options) => {
|
|
285
|
+
const { runSnooze } = await import('./commands/snooze.js');
|
|
286
|
+
await runSnooze({ prUrl, reason: options.reason, days: parseInt(options.days, 10), json: options.json });
|
|
287
|
+
});
|
|
288
|
+
// Unsnooze command
|
|
289
|
+
program
|
|
290
|
+
.command('unsnooze <pr-url>')
|
|
291
|
+
.description('Unsnooze a PR (re-enable CI failure notifications)')
|
|
292
|
+
.option('--json', 'Output as JSON')
|
|
293
|
+
.action(async (prUrl, options) => {
|
|
294
|
+
const { runUnsnooze } = await import('./commands/snooze.js');
|
|
295
|
+
await runUnsnooze({ prUrl, json: options.json });
|
|
296
|
+
});
|
|
297
|
+
// Validate GitHub token before running commands that need it
|
|
298
|
+
program.hook('preAction', async (thisCommand, actionCommand) => {
|
|
299
|
+
// Enable debug logging if --debug flag is set
|
|
300
|
+
const globalOpts = thisCommand.opts();
|
|
301
|
+
if (globalOpts.debug) {
|
|
302
|
+
enableDebug();
|
|
303
|
+
debug('cli', `Running command: ${actionCommand.name()}`);
|
|
304
|
+
}
|
|
305
|
+
// actionCommand is the command being executed (e.g., 'status', 'daily')
|
|
306
|
+
const commandName = actionCommand.name();
|
|
307
|
+
if (!LOCAL_ONLY_COMMANDS.includes(commandName)) {
|
|
308
|
+
const token = await getGitHubTokenAsync();
|
|
309
|
+
if (!token) {
|
|
310
|
+
console.error('Error: GitHub authentication required.');
|
|
311
|
+
console.error('');
|
|
312
|
+
console.error('Option 1 (Recommended): Install and authenticate GitHub CLI');
|
|
313
|
+
console.error(' Install: https://cli.github.com/');
|
|
314
|
+
console.error(' Then run: gh auth login');
|
|
315
|
+
console.error('');
|
|
316
|
+
console.error('Option 2: Set GITHUB_TOKEN environment variable');
|
|
317
|
+
console.error(' export GITHUB_TOKEN="your-github-token-here"');
|
|
318
|
+
console.error('');
|
|
319
|
+
console.error('Then run your command again.');
|
|
320
|
+
process.exit(1);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
// Parse and execute
|
|
325
|
+
program.parse();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check integration command (#83)
|
|
3
|
+
* Detects new files in the current branch that aren't referenced elsewhere
|
|
4
|
+
*/
|
|
5
|
+
interface CheckIntegrationOptions {
|
|
6
|
+
base: string;
|
|
7
|
+
json?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function runCheckIntegration(options: CheckIntegrationOptions): Promise<void>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check integration command (#83)
|
|
3
|
+
* Detects new files in the current branch that aren't referenced elsewhere
|
|
4
|
+
*/
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import { execFileSync } from 'child_process';
|
|
7
|
+
import { outputJson, outputJsonError } from '../formatters/json.js';
|
|
8
|
+
import { debug } from '../core/index.js';
|
|
9
|
+
/** File extensions we consider "code" that should be imported/referenced */
|
|
10
|
+
const CODE_EXTENSIONS = new Set([
|
|
11
|
+
'.ts',
|
|
12
|
+
'.tsx',
|
|
13
|
+
'.js',
|
|
14
|
+
'.jsx',
|
|
15
|
+
'.mjs',
|
|
16
|
+
'.cjs',
|
|
17
|
+
'.py',
|
|
18
|
+
'.rb',
|
|
19
|
+
'.go',
|
|
20
|
+
'.rs',
|
|
21
|
+
'.java',
|
|
22
|
+
'.kt',
|
|
23
|
+
'.vue',
|
|
24
|
+
'.svelte',
|
|
25
|
+
]);
|
|
26
|
+
/** Files that are typically entry points or config, not expected to be imported */
|
|
27
|
+
const IGNORED_PATTERNS = [
|
|
28
|
+
/^\./, // dotfiles
|
|
29
|
+
/\.(test|spec|e2e)\.[^.]+$/, // test files
|
|
30
|
+
/\.(config|rc)\.[^.]+$/, // config files
|
|
31
|
+
/__tests__\//, // test directories
|
|
32
|
+
/\.d\.ts$/, // type declarations
|
|
33
|
+
/\.md$/, // documentation
|
|
34
|
+
/\.json$/, // json files
|
|
35
|
+
/\.ya?ml$/, // yaml files
|
|
36
|
+
];
|
|
37
|
+
/** Get the basename without extension for import matching (TS/JS) */
|
|
38
|
+
function getImportName(filePath) {
|
|
39
|
+
const ext = path.extname(filePath);
|
|
40
|
+
const base = path.basename(filePath, ext);
|
|
41
|
+
// index files are imported by their directory name
|
|
42
|
+
if (base === 'index') {
|
|
43
|
+
return path.basename(path.dirname(filePath));
|
|
44
|
+
}
|
|
45
|
+
return base;
|
|
46
|
+
}
|
|
47
|
+
/** Suggest likely entry points for a new file based on its location */
|
|
48
|
+
function suggestEntryPoints(newFile, existingFiles) {
|
|
49
|
+
const dir = path.dirname(newFile);
|
|
50
|
+
const suggestions = [];
|
|
51
|
+
// Look for index files in the same directory
|
|
52
|
+
for (const ext of ['.ts', '.tsx', '.js', '.jsx']) {
|
|
53
|
+
const indexFile = path.join(dir, `index${ext}`);
|
|
54
|
+
if (existingFiles.includes(indexFile)) {
|
|
55
|
+
suggestions.push(indexFile);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Look for barrel/index in parent directory
|
|
59
|
+
const parentDir = path.dirname(dir);
|
|
60
|
+
for (const ext of ['.ts', '.tsx', '.js', '.jsx']) {
|
|
61
|
+
const parentIndex = path.join(parentDir, `index${ext}`);
|
|
62
|
+
if (existingFiles.includes(parentIndex)) {
|
|
63
|
+
suggestions.push(parentIndex);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return [...new Set(suggestions)];
|
|
67
|
+
}
|
|
68
|
+
export async function runCheckIntegration(options) {
|
|
69
|
+
const base = options.base;
|
|
70
|
+
// Get new files added in this branch vs base
|
|
71
|
+
let newFiles;
|
|
72
|
+
try {
|
|
73
|
+
const output = execFileSync('git', ['diff', '--name-only', '--diff-filter=A', `${base}...HEAD`], {
|
|
74
|
+
encoding: 'utf-8',
|
|
75
|
+
timeout: 10000,
|
|
76
|
+
}).trim();
|
|
77
|
+
newFiles = output ? output.split('\n').filter(Boolean) : [];
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
81
|
+
if (options.json) {
|
|
82
|
+
outputJsonError(`Failed to run git diff: ${msg}`);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
console.error(`Error: Failed to run git diff: ${msg}`);
|
|
86
|
+
}
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
// Filter to code files, excluding tests, configs, etc.
|
|
90
|
+
const codeFiles = newFiles.filter((f) => {
|
|
91
|
+
const ext = path.extname(f);
|
|
92
|
+
if (!CODE_EXTENSIONS.has(ext))
|
|
93
|
+
return false;
|
|
94
|
+
return !IGNORED_PATTERNS.some((p) => p.test(f));
|
|
95
|
+
});
|
|
96
|
+
if (codeFiles.length === 0) {
|
|
97
|
+
const result = { newFiles: [], unreferencedCount: 0 };
|
|
98
|
+
if (options.json) {
|
|
99
|
+
outputJson(result);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
console.log('\nNo new code files to check.');
|
|
103
|
+
}
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
// Get all tracked files in the repo for reference checking
|
|
107
|
+
let allFiles;
|
|
108
|
+
try {
|
|
109
|
+
allFiles = execFileSync('git', ['ls-files'], {
|
|
110
|
+
encoding: 'utf-8',
|
|
111
|
+
timeout: 10000,
|
|
112
|
+
})
|
|
113
|
+
.trim()
|
|
114
|
+
.split('\n')
|
|
115
|
+
.filter(Boolean);
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
// git ls-files failed (e.g. not a git repo) ā proceed without reference list
|
|
119
|
+
debug('check-integration', 'git ls-files failed, reference checking will be skipped', err);
|
|
120
|
+
allFiles = [];
|
|
121
|
+
}
|
|
122
|
+
// For each new file, search for references in the repo
|
|
123
|
+
const results = [];
|
|
124
|
+
for (const newFile of codeFiles) {
|
|
125
|
+
const importName = getImportName(newFile);
|
|
126
|
+
const fileWithoutExt = newFile.replace(/\.[^.]+$/, '');
|
|
127
|
+
// Search for references using git grep (fast, respects .gitignore)
|
|
128
|
+
let referencedBy = [];
|
|
129
|
+
// Search for: import from './filename', require('./filename'), or just the filename stem
|
|
130
|
+
const patterns = [
|
|
131
|
+
importName, // bare name (covers most import patterns)
|
|
132
|
+
];
|
|
133
|
+
// Also search for path-based imports (relative path without extension)
|
|
134
|
+
if (fileWithoutExt.includes('/')) {
|
|
135
|
+
patterns.push(fileWithoutExt);
|
|
136
|
+
}
|
|
137
|
+
for (const pattern of patterns) {
|
|
138
|
+
try {
|
|
139
|
+
const grepOutput = execFileSync('git', ['grep', '-l', '--', pattern], {
|
|
140
|
+
encoding: 'utf-8',
|
|
141
|
+
timeout: 10000,
|
|
142
|
+
}).trim();
|
|
143
|
+
if (grepOutput) {
|
|
144
|
+
const matches = grepOutput.split('\n').filter((f) => f !== newFile);
|
|
145
|
+
referencedBy.push(...matches);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
// git grep exit code 1 = no matches (expected), exit code 2+ = real error
|
|
150
|
+
const exitCode = error && typeof error === 'object' && 'status' in error ? error.status : null;
|
|
151
|
+
if (exitCode !== null && exitCode !== 1) {
|
|
152
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
153
|
+
console.error(`Warning: git grep failed for "${pattern}": ${msg}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// Deduplicate
|
|
158
|
+
referencedBy = [...new Set(referencedBy)];
|
|
159
|
+
const isIntegrated = referencedBy.length > 0;
|
|
160
|
+
const info = {
|
|
161
|
+
path: newFile,
|
|
162
|
+
referencedBy,
|
|
163
|
+
isIntegrated,
|
|
164
|
+
};
|
|
165
|
+
if (!isIntegrated) {
|
|
166
|
+
info.suggestedEntryPoints = suggestEntryPoints(newFile, allFiles);
|
|
167
|
+
}
|
|
168
|
+
results.push(info);
|
|
169
|
+
}
|
|
170
|
+
const unreferencedCount = results.filter((r) => !r.isIntegrated).length;
|
|
171
|
+
const output = { newFiles: results, unreferencedCount };
|
|
172
|
+
if (options.json) {
|
|
173
|
+
outputJson(output);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
console.log(`\nš Integration Check (base: ${base})\n`);
|
|
177
|
+
console.log(`New files: ${results.length} | Unreferenced: ${unreferencedCount}\n`);
|
|
178
|
+
for (const file of results) {
|
|
179
|
+
const status = file.isIntegrated ? 'ā
' : 'ā ļø';
|
|
180
|
+
console.log(`${status} ${file.path}`);
|
|
181
|
+
if (file.isIntegrated) {
|
|
182
|
+
console.log(` Referenced by: ${file.referencedBy.join(', ')}`);
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
console.log(' Not referenced by any file');
|
|
186
|
+
if (file.suggestedEntryPoints && file.suggestedEntryPoints.length > 0) {
|
|
187
|
+
console.log(` Suggested entry points: ${file.suggestedEntryPoints.join(', ')}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comments, Post, and Claim commands
|
|
3
|
+
* Handles GitHub comment interactions
|
|
4
|
+
*/
|
|
5
|
+
interface CommentsOptions {
|
|
6
|
+
prUrl: string;
|
|
7
|
+
showBots?: boolean;
|
|
8
|
+
json?: boolean;
|
|
9
|
+
}
|
|
10
|
+
interface PostOptions {
|
|
11
|
+
url: string;
|
|
12
|
+
message?: string;
|
|
13
|
+
stdin?: boolean;
|
|
14
|
+
json?: boolean;
|
|
15
|
+
}
|
|
16
|
+
interface ClaimOptions {
|
|
17
|
+
issueUrl: string;
|
|
18
|
+
message?: string;
|
|
19
|
+
json?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare function runComments(options: CommentsOptions): Promise<void>;
|
|
22
|
+
export declare function runPost(options: PostOptions): Promise<void>;
|
|
23
|
+
export declare function runClaim(options: ClaimOptions): Promise<void>;
|
|
24
|
+
export {};
|