@oss-autopilot/core 3.5.0 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/cli-registry.js +143 -1
  2. package/dist/cli.bundle.cjs +120 -108
  3. package/dist/commands/daily.d.ts +8 -0
  4. package/dist/commands/daily.js +21 -0
  5. package/dist/commands/dashboard-lifecycle.d.ts +7 -0
  6. package/dist/commands/dashboard-lifecycle.js +12 -2
  7. package/dist/commands/dashboard-process.d.ts +8 -0
  8. package/dist/commands/dashboard-process.js +20 -0
  9. package/dist/commands/features.d.ts +50 -0
  10. package/dist/commands/features.js +131 -0
  11. package/dist/commands/index.d.ts +5 -1
  12. package/dist/commands/index.js +4 -0
  13. package/dist/commands/scout-bridge.d.ts +12 -0
  14. package/dist/commands/scout-bridge.js +42 -2
  15. package/dist/commands/search.js +3 -1
  16. package/dist/commands/startup.js +75 -7
  17. package/dist/commands/vet-list.js +21 -5
  18. package/dist/commands/vet.js +3 -1
  19. package/dist/core/anti-llm-policy.d.ts +42 -13
  20. package/dist/core/anti-llm-policy.js +102 -13
  21. package/dist/core/ci-analysis.d.ts +32 -1
  22. package/dist/core/ci-analysis.js +92 -0
  23. package/dist/core/errors.d.ts +19 -0
  24. package/dist/core/errors.js +54 -0
  25. package/dist/core/index.d.ts +1 -1
  26. package/dist/core/index.js +1 -1
  27. package/dist/core/linked-pr-classification.d.ts +28 -0
  28. package/dist/core/linked-pr-classification.js +32 -0
  29. package/dist/core/pr-monitor.d.ts +1 -1
  30. package/dist/core/pr-monitor.js +31 -11
  31. package/dist/core/state-schema.d.ts +1 -0
  32. package/dist/core/state-schema.js +9 -0
  33. package/dist/core/state.d.ts +7 -0
  34. package/dist/core/state.js +10 -0
  35. package/dist/core/strategy.d.ts +21 -1
  36. package/dist/core/strategy.js +44 -0
  37. package/dist/core/types.d.ts +49 -0
  38. package/dist/formatters/json.d.ts +329 -35
  39. package/dist/formatters/json.js +102 -0
  40. package/package.json +2 -2
@@ -284,6 +284,97 @@ export const commands = [
284
284
  });
285
285
  },
286
286
  },
287
+ // ── Features ───────────────────────────────────────────────────────────
288
+ // scout 0.9.0 (#97/#98/#99): feature-scoped opportunities in repos with
289
+ // 3+ merged PRs, split into quick-wins / bigger-bets buckets.
290
+ {
291
+ name: 'features',
292
+ register(program) {
293
+ program
294
+ .command('features [count]')
295
+ .description('Find feature-scoped opportunities in repos with 3+ merged PRs')
296
+ .option('--json', 'Output as JSON')
297
+ .option('--anchor-threshold <n>', 'Override featuresAnchorThreshold (1-50)')
298
+ .option('--split-ratio <r>', 'Override featuresSplitRatio (0-1)')
299
+ .action(async (count, options) => {
300
+ const { FeaturesOutputSchema } = await import('./formatters/json.js');
301
+ await executeAction(options, async () => {
302
+ const { runFeatures, MAX_FEATURES_RESULTS } = await import('./commands/features.js');
303
+ let maxResults = 10;
304
+ if (count !== undefined) {
305
+ const parsed = Number(count);
306
+ if (!Number.isFinite(parsed) || parsed < 1 || !Number.isInteger(parsed)) {
307
+ throw new Error(`Invalid count "${count}". Must be a positive integer.`);
308
+ }
309
+ maxResults = parsed;
310
+ }
311
+ if (maxResults > MAX_FEATURES_RESULTS) {
312
+ console.warn(`Capping features to ${MAX_FEATURES_RESULTS} results (requested: ${maxResults})`);
313
+ maxResults = MAX_FEATURES_RESULTS;
314
+ }
315
+ let anchorThreshold;
316
+ if (options.anchorThreshold !== undefined) {
317
+ const parsed = Number(options.anchorThreshold);
318
+ if (!Number.isFinite(parsed) || !Number.isInteger(parsed) || parsed < 1 || parsed > 50) {
319
+ throw new Error(`Invalid --anchor-threshold "${options.anchorThreshold}". Must be an integer in [1, 50].`);
320
+ }
321
+ anchorThreshold = parsed;
322
+ }
323
+ let splitRatio;
324
+ if (options.splitRatio !== undefined) {
325
+ const parsed = Number(options.splitRatio);
326
+ if (!Number.isFinite(parsed) || parsed < 0 || parsed > 1) {
327
+ throw new Error(`Invalid --split-ratio "${options.splitRatio}". Must be a number in [0, 1].`);
328
+ }
329
+ splitRatio = parsed;
330
+ }
331
+ if (!options.json) {
332
+ console.log(`\nSearching for feature opportunities (max ${maxResults})...\n`);
333
+ }
334
+ return runFeatures({ maxResults, anchorThreshold, splitRatio });
335
+ }, (data) => {
336
+ if (data.anchorRepos.length > 0) {
337
+ console.log(`Anchor repos (${data.anchorRepos.length}): ${data.anchorRepos.join(', ')}`);
338
+ console.log('');
339
+ }
340
+ if (data.quickWins.length === 0 && data.biggerBets.length === 0) {
341
+ if (data.rateLimitWarning) {
342
+ console.warn(`\n${data.rateLimitWarning}\n`);
343
+ }
344
+ else if (data.message) {
345
+ console.log(data.message);
346
+ }
347
+ else {
348
+ console.log('No feature opportunities found.');
349
+ }
350
+ return;
351
+ }
352
+ if (data.rateLimitWarning) {
353
+ console.warn(`\n${data.rateLimitWarning}\n`);
354
+ }
355
+ const printBucket = (heading, candidates) => {
356
+ if (candidates.length === 0)
357
+ return;
358
+ console.log(`${heading} (${candidates.length}):\n`);
359
+ for (const candidate of candidates) {
360
+ const { issue, recommendation, reasonsToApprove, reasonsToSkip, viabilityScore } = candidate;
361
+ console.log(`[${recommendation.toUpperCase()}] ${issue.repo}#${issue.number}: ${issue.title}`);
362
+ console.log(` URL: ${issue.url}`);
363
+ console.log(` Viability: ${viabilityScore}/100`);
364
+ if (reasonsToApprove.length > 0)
365
+ console.log(` Approve: ${reasonsToApprove.join(', ')}`);
366
+ if (reasonsToSkip.length > 0)
367
+ console.log(` Skip: ${reasonsToSkip.join(', ')}`);
368
+ console.log('---');
369
+ }
370
+ console.log('');
371
+ };
372
+ printBucket('Quick wins', data.quickWins);
373
+ printBucket('Bigger bets', data.biggerBets);
374
+ }, FeaturesOutputSchema);
375
+ });
376
+ },
377
+ },
287
378
  // ── Vet ────────────────────────────────────────────────────────────────
288
379
  {
289
380
  name: 'vet',
@@ -329,6 +420,7 @@ export const commands = [
329
420
  console.log(` Claimed: ${data.summary.claimed}`);
330
421
  console.log(` Closed: ${data.summary.closed}`);
331
422
  console.log(` Has PR: ${data.summary.hasPR}`);
423
+ console.log(` Stalled PR: ${data.summary.hasStalledPR}`);
332
424
  console.log(` Errors: ${data.summary.errors}`);
333
425
  console.log('');
334
426
  for (const result of data.results) {
@@ -337,7 +429,8 @@ export const commands = [
337
429
  : result.listStatus === 'error'
338
430
  ? '\u274c'
339
431
  : '\u26a0\ufe0f';
340
- console.log(`${status} [${result.listStatus}] ${result.issue.repo}#${result.issue.number}: ${result.issue.title}`);
432
+ const annotation = result.listStatus === 'has_stalled_pr' ? ' (stalled PR, revive opportunity)' : '';
433
+ console.log(`${status} [${result.listStatus}] ${result.issue.repo}#${result.issue.number}: ${result.issue.title}${annotation}`);
341
434
  if (result.errorMessage) {
342
435
  console.log(` Error: ${result.errorMessage}`);
343
436
  }
@@ -411,6 +504,55 @@ export const commands = [
411
504
  });
412
505
  },
413
506
  },
507
+ // ── List Mark Done ─────────────────────────────────────────────────────
508
+ {
509
+ name: 'list-mark-done',
510
+ localOnly: true,
511
+ register(program) {
512
+ program
513
+ .command('list-mark-done <issue-url>')
514
+ .description('Mark an issue line in a curated list as done with strikethrough + Done sub-bullet (#1299)')
515
+ .requiredOption('--pr-url <url>', 'PR URL to record on the Done sub-bullet')
516
+ .requiredOption('--pr-status <text>', 'Trailing status, e.g. "merged" or "CI passing"')
517
+ .requiredOption('--list-path <file>', 'Path to the markdown issue list')
518
+ .option('--json', 'Output as JSON')
519
+ .action(async (issueUrl, options) => {
520
+ const { ListMarkDoneOutputSchema } = await import('./formatters/json.js');
521
+ await executeAction(options, async () => {
522
+ const result = await (await import('./commands/list-mark-done.js')).runMarkIssueListItemDone({
523
+ issueUrl,
524
+ prUrl: options.prUrl,
525
+ prStatus: options.prStatus,
526
+ listPath: options.listPath,
527
+ });
528
+ // Convert "URL not in list" into a real CLI error so the JSON
529
+ // envelope reports `success: false` and the process exits non-zero.
530
+ // The pure transform's success-shaped not-found return is fine for
531
+ // library consumers, but as a CLI command "I asked you to mark X
532
+ // and you couldn't find X" is a failure the caller must see.
533
+ // The "already marked done" case stays as a success-shape return
534
+ // (it's idempotent — the caller's intent was achieved).
535
+ if (!result.marked && result.reason === 'issue URL not found in the list') {
536
+ throw new Error(`Issue URL not found in ${result.filePath}: ${result.url}. ` +
537
+ `Verify --list-path and the issue URL.`);
538
+ }
539
+ return result;
540
+ }, (data) => {
541
+ if (data.marked) {
542
+ const headingNote = data.repoHeadingStruck ? ' (repo heading also struck)' : '';
543
+ console.log(`Marked ${data.url} done${headingNote}`);
544
+ console.log(` File: ${data.filePath}`);
545
+ console.log(` Remaining under repo: ${data.remainingUnderRepo}`);
546
+ }
547
+ else {
548
+ // Reach here only on the idempotent "already marked done" path.
549
+ console.log(`No mark: ${data.url} — ${data.reason ?? 'unchanged'}`);
550
+ console.log(` File: ${data.filePath}`);
551
+ }
552
+ }, ListMarkDoneOutputSchema);
553
+ });
554
+ },
555
+ },
414
556
  // ── Track ──────────────────────────────────────────────────────────────
415
557
  {
416
558
  name: 'track',