@girardmedia/bootspring 2.0.21 → 2.0.23

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 (159) hide show
  1. package/bin/bootspring.js +5 -0
  2. package/cli/org.js +474 -0
  3. package/cli/preseed/index.js +16 -0
  4. package/cli/preseed/interactive.js +143 -0
  5. package/cli/preseed/templates.js +227 -0
  6. package/cli/preseed.js +9 -301
  7. package/cli/seed/builders/ai-context-builder.js +85 -0
  8. package/cli/seed/builders/index.js +13 -0
  9. package/cli/seed/builders/seed-builder.js +272 -0
  10. package/cli/seed/extractors/content-extractors.js +383 -0
  11. package/cli/seed/extractors/index.js +47 -0
  12. package/cli/seed/extractors/metadata-extractors.js +167 -0
  13. package/cli/seed/extractors/section-extractor.js +54 -0
  14. package/cli/seed/extractors/stack-extractors.js +228 -0
  15. package/cli/seed/index.js +18 -0
  16. package/cli/seed/utils/folder-structure.js +84 -0
  17. package/cli/seed/utils/index.js +11 -0
  18. package/cli/seed.js +23 -1074
  19. package/core/api-client.js +77 -0
  20. package/core/entitlements.js +36 -0
  21. package/core/organizations.js +223 -0
  22. package/core/policies.js +51 -6
  23. package/core/policy-matrix.js +303 -0
  24. package/core/project-context.js +1 -0
  25. package/dist/cli/index.d.ts +3 -0
  26. package/dist/cli/index.js +3220 -0
  27. package/dist/cli/index.js.map +1 -0
  28. package/dist/context-McpJQa_2.d.ts +5710 -0
  29. package/dist/core/index.d.ts +635 -0
  30. package/dist/core/index.js +2593 -0
  31. package/dist/core/index.js.map +1 -0
  32. package/dist/index-QqbeEiDm.d.ts +857 -0
  33. package/dist/index-UiYCgwiH.d.ts +174 -0
  34. package/dist/index.d.ts +453 -0
  35. package/dist/index.js +44228 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/mcp/index.d.ts +1 -0
  38. package/dist/mcp/index.js +41173 -0
  39. package/dist/mcp/index.js.map +1 -0
  40. package/generators/index.ts +82 -0
  41. package/intelligence/orchestrator/config/failure-signatures.js +48 -0
  42. package/intelligence/orchestrator/config/index.js +23 -0
  43. package/intelligence/orchestrator/config/pack-lifecycle.js +262 -0
  44. package/intelligence/orchestrator/config/phases.js +111 -0
  45. package/intelligence/orchestrator/config/remediation.js +150 -0
  46. package/intelligence/orchestrator/config/workflows.js +168 -0
  47. package/intelligence/orchestrator/core/index.js +16 -0
  48. package/intelligence/orchestrator/core/state-manager.js +88 -0
  49. package/intelligence/orchestrator/core/telemetry.js +24 -0
  50. package/intelligence/orchestrator/index.js +17 -0
  51. package/intelligence/orchestrator.js +17 -512
  52. package/mcp/contracts/mcp-contract.v1.json +1 -1
  53. package/package.json +16 -3
  54. package/src/cli/agent.ts +703 -0
  55. package/src/cli/analyze.ts +640 -0
  56. package/src/cli/audit.ts +707 -0
  57. package/src/cli/auth.ts +930 -0
  58. package/src/cli/billing.ts +364 -0
  59. package/src/cli/build.ts +1089 -0
  60. package/src/cli/business.ts +508 -0
  61. package/src/cli/checkpoint-utils.ts +236 -0
  62. package/src/cli/checkpoint.ts +757 -0
  63. package/src/cli/cloud-sync.ts +534 -0
  64. package/src/cli/content.ts +273 -0
  65. package/src/cli/context.ts +667 -0
  66. package/src/cli/dashboard.ts +133 -0
  67. package/src/cli/deploy.ts +704 -0
  68. package/src/cli/doctor.ts +480 -0
  69. package/src/cli/fundraise.ts +494 -0
  70. package/src/cli/generate.ts +346 -0
  71. package/src/cli/github-cmd.ts +566 -0
  72. package/src/cli/health.ts +599 -0
  73. package/src/cli/index.ts +113 -0
  74. package/src/cli/init.ts +838 -0
  75. package/src/cli/legal.ts +495 -0
  76. package/src/cli/log.ts +316 -0
  77. package/src/cli/loop.ts +1660 -0
  78. package/src/cli/manager.ts +878 -0
  79. package/src/cli/mcp.ts +275 -0
  80. package/src/cli/memory.ts +346 -0
  81. package/src/cli/metrics.ts +590 -0
  82. package/src/cli/monitor.ts +960 -0
  83. package/src/cli/mvp.ts +662 -0
  84. package/src/cli/onboard.ts +663 -0
  85. package/src/cli/orchestrator.ts +622 -0
  86. package/src/cli/plugin.ts +483 -0
  87. package/src/cli/prd.ts +671 -0
  88. package/src/cli/preseed-start.ts +1633 -0
  89. package/src/cli/preseed.ts +2434 -0
  90. package/src/cli/project.ts +526 -0
  91. package/src/cli/quality.ts +885 -0
  92. package/src/cli/security.ts +1079 -0
  93. package/src/cli/seed.ts +1224 -0
  94. package/src/cli/skill.ts +537 -0
  95. package/src/cli/suggest.ts +1225 -0
  96. package/src/cli/switch.ts +518 -0
  97. package/src/cli/task.ts +780 -0
  98. package/src/cli/telemetry.ts +172 -0
  99. package/src/cli/todo.ts +627 -0
  100. package/src/cli/types.ts +15 -0
  101. package/src/cli/update.ts +334 -0
  102. package/src/cli/visualize.ts +609 -0
  103. package/src/cli/watch.ts +895 -0
  104. package/src/cli/workspace.ts +709 -0
  105. package/src/core/action-recorder.ts +673 -0
  106. package/src/core/analyze-workflow.ts +1453 -0
  107. package/src/core/api-client.ts +1120 -0
  108. package/src/core/audit-workflow.ts +1681 -0
  109. package/src/core/auth.ts +471 -0
  110. package/src/core/build-orchestrator.ts +509 -0
  111. package/src/core/build-state.ts +621 -0
  112. package/src/core/checkpoint-engine.ts +482 -0
  113. package/src/core/config.ts +1285 -0
  114. package/src/core/context-loader.ts +694 -0
  115. package/src/core/context.ts +410 -0
  116. package/src/core/deploy-workflow.ts +1085 -0
  117. package/src/core/entitlements.ts +322 -0
  118. package/src/core/github-sync.ts +720 -0
  119. package/src/core/index.ts +981 -0
  120. package/src/core/ingest.ts +1186 -0
  121. package/src/core/metrics-engine.ts +886 -0
  122. package/src/core/mvp.ts +847 -0
  123. package/src/core/onboard-workflow.ts +1293 -0
  124. package/src/core/policies.ts +81 -0
  125. package/src/core/preseed-workflow.ts +1163 -0
  126. package/src/core/preseed.ts +1826 -0
  127. package/src/core/project-context.ts +380 -0
  128. package/src/core/project-state.ts +699 -0
  129. package/src/core/r2-sync.ts +691 -0
  130. package/src/core/scaffold.ts +1715 -0
  131. package/src/core/session.ts +286 -0
  132. package/src/core/task-extractor.ts +799 -0
  133. package/src/core/telemetry.ts +371 -0
  134. package/src/core/tier-enforcement.ts +737 -0
  135. package/src/core/utils.ts +437 -0
  136. package/src/index.ts +29 -0
  137. package/src/intelligence/agent-collab.ts +2376 -0
  138. package/src/intelligence/auto-suggest.ts +713 -0
  139. package/src/intelligence/content-gen.ts +1351 -0
  140. package/src/intelligence/cross-project.ts +1692 -0
  141. package/src/intelligence/git-memory.ts +529 -0
  142. package/src/intelligence/index.ts +318 -0
  143. package/src/intelligence/orchestrator.ts +534 -0
  144. package/src/intelligence/prd.ts +466 -0
  145. package/src/intelligence/recommendations.ts +982 -0
  146. package/src/intelligence/workflow-composer.ts +1472 -0
  147. package/src/mcp/capabilities.ts +233 -0
  148. package/src/mcp/index.ts +37 -0
  149. package/src/mcp/registry.ts +1268 -0
  150. package/src/mcp/response-formatter.ts +797 -0
  151. package/src/mcp/server.ts +240 -0
  152. package/src/types/agent.ts +69 -0
  153. package/src/types/config.ts +86 -0
  154. package/src/types/context.ts +77 -0
  155. package/src/types/index.ts +53 -0
  156. package/src/types/mcp.ts +91 -0
  157. package/src/types/skills.ts +47 -0
  158. package/src/types/workflow.ts +155 -0
  159. package/generators/index.js +0 -18
@@ -0,0 +1,566 @@
1
+ /**
2
+ * Bootspring GitHub Command
3
+ * Manage GitHub integration
4
+ *
5
+ * Commands:
6
+ * connect [--url] Connect to a GitHub repository
7
+ * sync Sync latest commits, PRs, and stats
8
+ * status Show connection status and recent activity
9
+ * disconnect Remove GitHub connection
10
+ * activity Show recent repository activity
11
+ *
12
+ * @package bootspring
13
+ * @command github
14
+ */
15
+
16
+ // Type interfaces for JS modules
17
+ interface Colors {
18
+ reset: string;
19
+ bold: string;
20
+ dim: string;
21
+ cyan: string;
22
+ green: string;
23
+ yellow: string;
24
+ red: string;
25
+ magenta: string;
26
+ }
27
+
28
+ interface PrintModule {
29
+ error(msg: string): void;
30
+ dim(msg: string): void;
31
+ warning(msg: string): void;
32
+ success(msg: string): void;
33
+ }
34
+
35
+ interface Spinner {
36
+ start(): Spinner;
37
+ stop(): void;
38
+ succeed(text: string): void;
39
+ fail(text: string): void;
40
+ }
41
+
42
+ interface ParsedArgs {
43
+ _: string[];
44
+ url?: string | undefined;
45
+ json?: boolean | undefined;
46
+ limit?: string | undefined;
47
+ }
48
+
49
+ interface UtilsModule {
50
+ COLORS: Colors;
51
+ print: PrintModule;
52
+ createSpinner(text: string): Spinner;
53
+ parseArgs(args: string[]): ParsedArgs;
54
+ formatRelativeTime(date: Date): string;
55
+ }
56
+
57
+ interface ConfigModule {
58
+ load(): LoadedConfig;
59
+ }
60
+
61
+ interface LoadedConfig {
62
+ _projectRoot: string;
63
+ }
64
+
65
+ interface GitHubStats {
66
+ totalCommits: number | string;
67
+ openPRs: number;
68
+ closedPRs?: number | undefined;
69
+ contributors: number | string;
70
+ lastCommit?: string | undefined;
71
+ lastCommitSha?: string | undefined;
72
+ lastCommitMessage?: string | undefined;
73
+ }
74
+
75
+ interface ConnectResult {
76
+ success: boolean;
77
+ error?: string | undefined;
78
+ owner?: string | undefined;
79
+ repo?: string | undefined;
80
+ url?: string | undefined;
81
+ defaultBranch?: string | undefined;
82
+ isPrivate?: boolean | undefined;
83
+ description?: string | undefined;
84
+ stats?: GitHubStats | undefined;
85
+ }
86
+
87
+ interface SyncResult {
88
+ success: boolean;
89
+ error?: string | undefined;
90
+ stats: GitHubStats;
91
+ }
92
+
93
+ interface DisconnectResult {
94
+ success: boolean;
95
+ error?: string | undefined;
96
+ }
97
+
98
+ interface DetectedRepo {
99
+ owner: string;
100
+ repo: string;
101
+ }
102
+
103
+ interface GitHubStatus {
104
+ ghInstalled: boolean;
105
+ ghAuthenticated: boolean;
106
+ connected: boolean;
107
+ owner?: string | undefined;
108
+ repo?: string | undefined;
109
+ repositoryUrl?: string | undefined;
110
+ defaultBranch?: string | undefined;
111
+ lastSync?: string | undefined;
112
+ stats?: GitHubStats | undefined;
113
+ detected?: DetectedRepo | undefined;
114
+ }
115
+
116
+ interface ActivityItem {
117
+ type: 'commit' | 'pr';
118
+ date: string;
119
+ sha?: string | undefined;
120
+ message?: string | undefined;
121
+ author?: string | undefined;
122
+ number?: number | undefined;
123
+ title?: string | undefined;
124
+ state?: string | undefined;
125
+ }
126
+
127
+ interface GitHubSyncModule {
128
+ isGhInstalled(): boolean;
129
+ isGhAuthenticated(): boolean;
130
+ connectRepository(projectRoot: string, options: { url?: string | null | undefined }): ConnectResult;
131
+ syncGitHubData(projectRoot: string): SyncResult;
132
+ getStatus(projectRoot: string): GitHubStatus;
133
+ disconnectRepository(projectRoot: string): DisconnectResult;
134
+ getRecentActivity(owner: string, repo: string, limit: number): ActivityItem[];
135
+ }
136
+
137
+ interface TelemetryModule {
138
+ emitEvent(event: string, data: Record<string, unknown>, options: { projectRoot: string }): void;
139
+ }
140
+
141
+ const config = require('../core/config') as ConfigModule;
142
+ const utils = require('../core/utils') as UtilsModule;
143
+ const githubSync = require('../core/github-sync') as GitHubSyncModule;
144
+ const telemetry = require('../core/telemetry') as TelemetryModule;
145
+
146
+ // ============================================================================
147
+ // Command Handlers
148
+ // ============================================================================
149
+
150
+ interface ConnectOptions {
151
+ url?: string | null | undefined;
152
+ }
153
+
154
+ /**
155
+ * Connect to a GitHub repository
156
+ */
157
+ function handleConnect(projectRoot: string, options: ConnectOptions = {}): void {
158
+ console.log(`
159
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ GitHub Connection${utils.COLORS.reset}
160
+ `);
161
+
162
+ // Check gh CLI
163
+ if (!githubSync.isGhInstalled()) {
164
+ utils.print.error('GitHub CLI (gh) is not installed.');
165
+ console.log(`
166
+ ${utils.COLORS.bold}To install gh CLI:${utils.COLORS.reset}
167
+ macOS: brew install gh
168
+ Windows: winget install GitHub.cli
169
+ Linux: See https://cli.github.com/
170
+
171
+ ${utils.COLORS.dim}Or visit: https://cli.github.com/${utils.COLORS.reset}
172
+ `);
173
+ return;
174
+ }
175
+
176
+ if (!githubSync.isGhAuthenticated()) {
177
+ utils.print.error('GitHub CLI is not authenticated.');
178
+ console.log(`
179
+ ${utils.COLORS.bold}To authenticate:${utils.COLORS.reset}
180
+ gh auth login
181
+
182
+ ${utils.COLORS.dim}Follow the prompts to authenticate with GitHub.${utils.COLORS.reset}
183
+ `);
184
+ return;
185
+ }
186
+
187
+ // Try to connect
188
+ const spinner = utils.createSpinner('Connecting to GitHub...');
189
+ spinner.start();
190
+
191
+ const result = githubSync.connectRepository(projectRoot, {
192
+ url: options.url
193
+ });
194
+
195
+ if (!result.success) {
196
+ spinner.fail(`Connection failed: ${result.error}`);
197
+ return;
198
+ }
199
+
200
+ spinner.succeed('Connected to GitHub!');
201
+
202
+ console.log(`
203
+ ${utils.COLORS.bold}Repository:${utils.COLORS.reset} ${result.owner}/${result.repo}
204
+ ${utils.COLORS.bold}URL:${utils.COLORS.reset} ${result.url}
205
+ ${utils.COLORS.bold}Branch:${utils.COLORS.reset} ${result.defaultBranch}
206
+ ${result.isPrivate ? `${utils.COLORS.dim}(Private repository)${utils.COLORS.reset}` : ''}
207
+ ${result.description ? `${utils.COLORS.dim}${result.description}${utils.COLORS.reset}` : ''}
208
+ `);
209
+
210
+ if (result.stats) {
211
+ console.log(`${utils.COLORS.bold}Statistics:${utils.COLORS.reset}`);
212
+ console.log(` Commits: ${result.stats.totalCommits || 'N/A'}`);
213
+ console.log(` Open PRs: ${result.stats.openPRs || 0}`);
214
+ console.log(` Contributors: ${result.stats.contributors || 'N/A'}`);
215
+
216
+ if (result.stats.lastCommitMessage) {
217
+ console.log(`\n${utils.COLORS.bold}Latest Commit:${utils.COLORS.reset}`);
218
+ console.log(` ${utils.COLORS.dim}${result.stats.lastCommitSha}${utils.COLORS.reset} ${result.stats.lastCommitMessage}`);
219
+ }
220
+ }
221
+
222
+ console.log();
223
+
224
+ // Emit telemetry
225
+ telemetry.emitEvent('github:connect', {
226
+ owner: result.owner,
227
+ repo: result.repo,
228
+ isPrivate: result.isPrivate
229
+ }, { projectRoot });
230
+ }
231
+
232
+ interface SyncOptions {
233
+ json?: boolean | undefined;
234
+ }
235
+
236
+ /**
237
+ * Sync GitHub data
238
+ */
239
+ function handleSync(projectRoot: string, options: SyncOptions = {}): void {
240
+ const status = githubSync.getStatus(projectRoot);
241
+
242
+ if (!status.connected) {
243
+ utils.print.error('GitHub not connected.');
244
+ utils.print.dim('Run: bootspring github connect');
245
+ return;
246
+ }
247
+
248
+ const spinner = utils.createSpinner(`Syncing ${status.owner}/${status.repo}...`);
249
+ spinner.start();
250
+
251
+ const result = githubSync.syncGitHubData(projectRoot);
252
+
253
+ if (!result.success) {
254
+ spinner.fail(`Sync failed: ${result.error}`);
255
+ return;
256
+ }
257
+
258
+ spinner.succeed('Synced GitHub data!');
259
+
260
+ if (options.json) {
261
+ console.log(JSON.stringify(result.stats, null, 2));
262
+ return;
263
+ }
264
+
265
+ console.log(`
266
+ ${utils.COLORS.bold}Statistics:${utils.COLORS.reset}
267
+ Commits: ${result.stats.totalCommits}
268
+ Open PRs: ${result.stats.openPRs}
269
+ Closed PRs: ${result.stats.closedPRs}
270
+ Contributors: ${result.stats.contributors}
271
+ `);
272
+
273
+ if (result.stats.lastCommitMessage) {
274
+ console.log(`${utils.COLORS.bold}Latest Commit:${utils.COLORS.reset}`);
275
+ console.log(` ${utils.COLORS.dim}${result.stats.lastCommitSha}${utils.COLORS.reset} ${result.stats.lastCommitMessage}`);
276
+ if (result.stats.lastCommit) {
277
+ const date = new Date(result.stats.lastCommit);
278
+ console.log(` ${utils.COLORS.dim}${utils.formatRelativeTime(date)}${utils.COLORS.reset}`);
279
+ }
280
+ console.log();
281
+ }
282
+
283
+ // Emit telemetry
284
+ telemetry.emitEvent('github:sync', {
285
+ owner: status.owner,
286
+ repo: status.repo,
287
+ stats: result.stats
288
+ }, { projectRoot });
289
+ }
290
+
291
+ interface StatusOptions {
292
+ json?: boolean | undefined;
293
+ }
294
+
295
+ /**
296
+ * Show GitHub status
297
+ */
298
+ function handleStatus(projectRoot: string, options: StatusOptions = {}): void {
299
+ const status = githubSync.getStatus(projectRoot);
300
+
301
+ // JSON output
302
+ if (options.json) {
303
+ console.log(JSON.stringify(status, null, 2));
304
+ return;
305
+ }
306
+
307
+ console.log(`
308
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ GitHub Status${utils.COLORS.reset}
309
+ `);
310
+
311
+ // CLI status
312
+ console.log(`${utils.COLORS.bold}GitHub CLI:${utils.COLORS.reset}`);
313
+ console.log(` ${status.ghInstalled ? `${utils.COLORS.green}✓${utils.COLORS.reset}` : `${utils.COLORS.red}✗${utils.COLORS.reset}`} Installed`);
314
+ console.log(` ${status.ghAuthenticated ? `${utils.COLORS.green}✓${utils.COLORS.reset}` : `${utils.COLORS.red}✗${utils.COLORS.reset}`} Authenticated`);
315
+ console.log();
316
+
317
+ if (!status.connected) {
318
+ console.log(`${utils.COLORS.bold}Connection:${utils.COLORS.reset} ${utils.COLORS.dim}Not connected${utils.COLORS.reset}`);
319
+
320
+ if (status.detected) {
321
+ console.log(`\n${utils.COLORS.bold}Detected Repository:${utils.COLORS.reset}`);
322
+ console.log(` ${status.detected.owner}/${status.detected.repo}`);
323
+ console.log(`\n${utils.COLORS.bold}To connect:${utils.COLORS.reset}`);
324
+ console.log(' bootspring github connect');
325
+ } else {
326
+ console.log(`\n${utils.COLORS.bold}To connect:${utils.COLORS.reset}`);
327
+ console.log(' bootspring github connect --url https://github.com/owner/repo');
328
+ }
329
+ console.log();
330
+ return;
331
+ }
332
+
333
+ // Connected status
334
+ console.log(`${utils.COLORS.bold}Connection:${utils.COLORS.reset} ${utils.COLORS.green}✓ Connected${utils.COLORS.reset}`);
335
+ console.log(`${utils.COLORS.bold}Repository:${utils.COLORS.reset} ${status.owner}/${status.repo}`);
336
+ console.log(`${utils.COLORS.bold}URL:${utils.COLORS.reset} ${status.repositoryUrl}`);
337
+ console.log(`${utils.COLORS.bold}Branch:${utils.COLORS.reset} ${status.defaultBranch}`);
338
+
339
+ if (status.lastSync) {
340
+ const syncDate = new Date(status.lastSync);
341
+ console.log(`${utils.COLORS.bold}Last Sync:${utils.COLORS.reset} ${utils.formatRelativeTime(syncDate)}`);
342
+ }
343
+
344
+ console.log();
345
+
346
+ // Statistics
347
+ if (status.stats) {
348
+ console.log(`${utils.COLORS.bold}Statistics:${utils.COLORS.reset}`);
349
+ console.log(` Commits: ${status.stats.totalCommits}`);
350
+ console.log(` Open PRs: ${status.stats.openPRs}`);
351
+ console.log(` Closed PRs: ${status.stats.closedPRs}`);
352
+ console.log(` Contributors: ${status.stats.contributors}`);
353
+ console.log();
354
+
355
+ if (status.stats.lastCommitMessage) {
356
+ console.log(`${utils.COLORS.bold}Latest Commit:${utils.COLORS.reset}`);
357
+ console.log(` ${utils.COLORS.dim}${status.stats.lastCommitSha}${utils.COLORS.reset} ${status.stats.lastCommitMessage}`);
358
+ if (status.stats.lastCommit) {
359
+ const date = new Date(status.stats.lastCommit);
360
+ console.log(` ${utils.COLORS.dim}${utils.formatRelativeTime(date)}${utils.COLORS.reset}`);
361
+ }
362
+ console.log();
363
+ }
364
+ }
365
+
366
+ console.log(`${utils.COLORS.bold}Commands:${utils.COLORS.reset}`);
367
+ console.log(' bootspring github sync Refresh data');
368
+ console.log(' bootspring github activity View recent activity');
369
+ console.log(' bootspring github disconnect Remove connection');
370
+ console.log();
371
+ }
372
+
373
+ /**
374
+ * Disconnect from GitHub
375
+ */
376
+ function handleDisconnect(projectRoot: string): void {
377
+ const status = githubSync.getStatus(projectRoot);
378
+
379
+ if (!status.connected) {
380
+ utils.print.warning('GitHub is not connected.');
381
+ return;
382
+ }
383
+
384
+ const result = githubSync.disconnectRepository(projectRoot);
385
+
386
+ if (result.success) {
387
+ utils.print.success(`Disconnected from ${status.owner}/${status.repo}`);
388
+
389
+ // Emit telemetry
390
+ telemetry.emitEvent('github:disconnect', {
391
+ owner: status.owner,
392
+ repo: status.repo
393
+ }, { projectRoot });
394
+ } else {
395
+ utils.print.error(`Failed to disconnect: ${result.error}`);
396
+ }
397
+ }
398
+
399
+ interface ActivityOptions {
400
+ limit?: number | undefined;
401
+ json?: boolean | undefined;
402
+ }
403
+
404
+ /**
405
+ * Show recent activity
406
+ */
407
+ function handleActivity(projectRoot: string, options: ActivityOptions = {}): void {
408
+ const status = githubSync.getStatus(projectRoot);
409
+
410
+ if (!status.connected) {
411
+ utils.print.error('GitHub not connected.');
412
+ utils.print.dim('Run: bootspring github connect');
413
+ return;
414
+ }
415
+
416
+ const limit = options.limit || 10;
417
+
418
+ const spinner = utils.createSpinner('Fetching recent activity...');
419
+ spinner.start();
420
+
421
+ const owner = status.owner || '';
422
+ const repo = status.repo || '';
423
+ const activity = githubSync.getRecentActivity(owner, repo, limit);
424
+
425
+ spinner.stop();
426
+
427
+ if (!activity || activity.length === 0) {
428
+ utils.print.warning('No recent activity found.');
429
+ return;
430
+ }
431
+
432
+ // JSON output
433
+ if (options.json) {
434
+ console.log(JSON.stringify(activity, null, 2));
435
+ return;
436
+ }
437
+
438
+ console.log(`
439
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Recent Activity${utils.COLORS.reset}
440
+ ${utils.COLORS.dim}${status.owner}/${status.repo}${utils.COLORS.reset}
441
+ `);
442
+
443
+ for (const item of activity) {
444
+ const date = new Date(item.date);
445
+ const relativeTime = utils.formatRelativeTime(date);
446
+
447
+ if (item.type === 'commit') {
448
+ console.log(` ${utils.COLORS.cyan}●${utils.COLORS.reset} ${utils.COLORS.dim}${item.sha}${utils.COLORS.reset} ${item.message}`);
449
+ console.log(` ${utils.COLORS.dim}${item.author} · ${relativeTime}${utils.COLORS.reset}`);
450
+ } else if (item.type === 'pr') {
451
+ const stateIcon = item.state === 'open'
452
+ ? `${utils.COLORS.green}○${utils.COLORS.reset}`
453
+ : `${utils.COLORS.magenta}●${utils.COLORS.reset}`;
454
+ console.log(` ${stateIcon} PR #${item.number}: ${item.title}`);
455
+ console.log(` ${utils.COLORS.dim}${item.author} · ${item.state} · ${relativeTime}${utils.COLORS.reset}`);
456
+ }
457
+ console.log();
458
+ }
459
+ }
460
+
461
+ /**
462
+ * Show help
463
+ */
464
+ function showHelp(): void {
465
+ console.log(`
466
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring GitHub${utils.COLORS.reset}
467
+ ${utils.COLORS.dim}Manage GitHub integration${utils.COLORS.reset}
468
+
469
+ ${utils.COLORS.bold}Usage:${utils.COLORS.reset}
470
+ bootspring github [command] [options]
471
+
472
+ ${utils.COLORS.bold}Commands:${utils.COLORS.reset}
473
+ connect Connect to a GitHub repository
474
+ sync Sync latest commits, PRs, and stats
475
+ status Show connection status (default)
476
+ activity Show recent repository activity
477
+ disconnect Remove GitHub connection
478
+
479
+ ${utils.COLORS.bold}Options:${utils.COLORS.reset}
480
+ --url=<url> Specify repository URL for connect
481
+ --json Output as JSON
482
+ --limit=<n> Limit results for activity
483
+
484
+ ${utils.COLORS.bold}Examples:${utils.COLORS.reset}
485
+ bootspring github connect # Auto-detect from git
486
+ bootspring github connect --url https://github.com/user/repo
487
+ bootspring github sync # Refresh data
488
+ bootspring github activity --limit=5 # Recent activity
489
+
490
+ ${utils.COLORS.bold}Requirements:${utils.COLORS.reset}
491
+ - GitHub CLI (gh) must be installed: https://cli.github.com/
492
+ - Must be authenticated: gh auth login
493
+ `);
494
+ }
495
+
496
+ // ============================================================================
497
+ // Main Runner
498
+ // ============================================================================
499
+
500
+ /**
501
+ * Run github command
502
+ */
503
+ export async function run(args: string[]): Promise<void> {
504
+ const cfg = config.load();
505
+ const projectRoot = cfg._projectRoot;
506
+
507
+ // Parse arguments
508
+ const parsed = utils.parseArgs(args);
509
+ const subcommand = parsed._[0] || 'status';
510
+ const options = {
511
+ url: parsed.url || null,
512
+ json: parsed.json || false,
513
+ limit: parseInt(parsed.limit || '10', 10) || 10
514
+ };
515
+
516
+ switch (subcommand) {
517
+ case 'connect':
518
+ case 'c':
519
+ handleConnect(projectRoot, options);
520
+ break;
521
+
522
+ case 'sync':
523
+ case 's':
524
+ handleSync(projectRoot, options);
525
+ break;
526
+
527
+ case 'status':
528
+ case 'st':
529
+ handleStatus(projectRoot, options);
530
+ break;
531
+
532
+ case 'disconnect':
533
+ case 'd':
534
+ case 'remove':
535
+ handleDisconnect(projectRoot);
536
+ break;
537
+
538
+ case 'activity':
539
+ case 'log':
540
+ case 'a':
541
+ handleActivity(projectRoot, options);
542
+ break;
543
+
544
+ case 'help':
545
+ case '-h':
546
+ case '--help':
547
+ showHelp();
548
+ break;
549
+
550
+ default:
551
+ utils.print.error(`Unknown subcommand: ${subcommand}`);
552
+ showHelp();
553
+ }
554
+ }
555
+
556
+ // ============================================================================
557
+ // Exports
558
+ // ============================================================================
559
+
560
+ export {
561
+ handleConnect,
562
+ handleSync,
563
+ handleStatus,
564
+ handleDisconnect,
565
+ handleActivity
566
+ };