bosun 0.41.2 → 0.41.3

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 (71) hide show
  1. package/.env.example +1 -1
  2. package/agent/agent-prompt-catalog.mjs +971 -0
  3. package/agent/agent-prompts.mjs +2 -970
  4. package/agent/agent-supervisor.mjs +6 -3
  5. package/agent/autofix-git.mjs +33 -0
  6. package/agent/autofix-prompts.mjs +151 -0
  7. package/agent/autofix.mjs +11 -175
  8. package/agent/bosun-skills.mjs +3 -2
  9. package/bosun.config.example.json +17 -0
  10. package/bosun.schema.json +87 -188
  11. package/cli.mjs +34 -1
  12. package/config/config-doctor.mjs +5 -250
  13. package/config/config-file-names.mjs +5 -0
  14. package/config/config.mjs +89 -493
  15. package/config/executor-config.mjs +493 -0
  16. package/config/repo-root.mjs +1 -2
  17. package/config/workspace-health.mjs +242 -0
  18. package/git/git-safety.mjs +15 -0
  19. package/github/github-oauth-portal.mjs +46 -0
  20. package/infra/library-manager-utils.mjs +22 -0
  21. package/infra/library-manager-well-known-sources.mjs +578 -0
  22. package/infra/library-manager.mjs +512 -1030
  23. package/infra/monitor.mjs +28 -9
  24. package/infra/session-tracker.mjs +10 -7
  25. package/kanban/kanban-adapter.mjs +17 -1
  26. package/lib/codebase-audit-manifests.mjs +117 -0
  27. package/lib/codebase-audit.mjs +18 -115
  28. package/package.json +18 -3
  29. package/server/ui-server.mjs +1194 -79
  30. package/shell/codex-config-file.mjs +178 -0
  31. package/shell/codex-config.mjs +538 -575
  32. package/task/task-cli.mjs +54 -3
  33. package/task/task-executor.mjs +143 -13
  34. package/task/task-store.mjs +409 -1
  35. package/telegram/telegram-bot.mjs +127 -0
  36. package/tools/apply-pr-suggestions.mjs +401 -0
  37. package/tools/syntax-check.mjs +21 -9
  38. package/ui/app.js +3 -14
  39. package/ui/components/kanban-board.js +227 -4
  40. package/ui/components/session-list.js +85 -5
  41. package/ui/demo-defaults.js +334 -80
  42. package/ui/demo.html +155 -0
  43. package/ui/modules/session-api.js +96 -0
  44. package/ui/modules/settings-schema.js +1 -2
  45. package/ui/modules/state.js +21 -3
  46. package/ui/setup.html +4 -5
  47. package/ui/styles/components.css +58 -4
  48. package/ui/tabs/agents.js +12 -15
  49. package/ui/tabs/control.js +1 -0
  50. package/ui/tabs/library.js +484 -22
  51. package/ui/tabs/manual-flows.js +105 -29
  52. package/ui/tabs/tasks.js +785 -140
  53. package/ui/tabs/telemetry.js +129 -11
  54. package/ui/tabs/workflow-canvas-utils.mjs +130 -0
  55. package/ui/tabs/workflows.js +293 -23
  56. package/voice/voice-tool-definitions.mjs +757 -0
  57. package/voice/voice-tools.mjs +34 -778
  58. package/workflow/manual-flow-audit.mjs +165 -0
  59. package/workflow/manual-flows.mjs +164 -259
  60. package/workflow/workflow-engine.mjs +147 -58
  61. package/workflow/workflow-nodes/definitions.mjs +1207 -0
  62. package/workflow/workflow-nodes/transforms.mjs +612 -0
  63. package/workflow/workflow-nodes.mjs +304 -52
  64. package/workflow/workflow-templates.mjs +313 -191
  65. package/workflow-templates/_helpers.mjs +154 -0
  66. package/workflow-templates/agents.mjs +61 -4
  67. package/workflow-templates/code-quality.mjs +7 -7
  68. package/workflow-templates/github.mjs +20 -10
  69. package/workflow-templates/task-batch.mjs +20 -9
  70. package/workflow-templates/task-lifecycle.mjs +31 -6
  71. package/workspace/worktree-manager.mjs +277 -3
@@ -0,0 +1,578 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { nowISO, toStringArray, uniqueStrings } from "./library-manager-utils.mjs";
3
+
4
+ const wellKnownSourceProbeCache = new Map();
5
+ const TRUSTED_GITHUB_OWNERS = new Set(["microsoft", "github", "azure", "desktop", "canonical", "mastra-ai"]);
6
+
7
+ export const WELL_KNOWN_AGENT_SOURCES = Object.freeze([
8
+ // ── Microsoft — Official ──────────────────────────────────────────────────
9
+ {
10
+ id: "microsoft-skills",
11
+ name: "Microsoft Skills",
12
+ repoUrl: "https://github.com/microsoft/skills.git",
13
+ defaultBranch: "main",
14
+ description: "Microsoft-maintained backend, frontend, planner, infrastructure, and scaffolder agents with hundreds of Azure SDK skills.",
15
+ owner: "microsoft",
16
+ trustTier: "official",
17
+ importCoverage: "high",
18
+ estimatedPlugins: 180,
19
+ focuses: ["backend", "frontend", "planner", "infra", "scaffolding", "azure"],
20
+ },
21
+ {
22
+ id: "microsoft-hve-core",
23
+ name: "Microsoft HVE Core",
24
+ repoUrl: "https://github.com/microsoft/hve-core.git",
25
+ defaultBranch: "main",
26
+ description: "Core HVE agent library with domain and plugin agent templates and experimental skills.",
27
+ owner: "microsoft",
28
+ trustTier: "official",
29
+ importCoverage: "high",
30
+ estimatedPlugins: 60,
31
+ focuses: ["core", "plugins", "platform"],
32
+ },
33
+ {
34
+ id: "microsoft-vscode",
35
+ name: "Microsoft VS Code",
36
+ repoUrl: "https://github.com/microsoft/vscode.git",
37
+ defaultBranch: "main",
38
+ description: "VS Code editor skills for hygiene, testing, and extension development workflows.",
39
+ owner: "microsoft",
40
+ trustTier: "official",
41
+ importCoverage: "medium",
42
+ estimatedPlugins: 15,
43
+ focuses: ["vscode", "editor", "extensions", "testing"],
44
+ },
45
+ {
46
+ id: "microsoft-powertoys",
47
+ name: "Microsoft PowerToys",
48
+ repoUrl: "https://github.com/microsoft/PowerToys.git",
49
+ defaultBranch: "main",
50
+ description: "PowerToys development skills for Windows utility and plugin engineering.",
51
+ owner: "microsoft",
52
+ trustTier: "official",
53
+ importCoverage: "medium",
54
+ estimatedPlugins: 10,
55
+ focuses: ["windows", "utilities", "c-sharp", "plugins"],
56
+ },
57
+ {
58
+ id: "microsoft-typespec",
59
+ name: "Microsoft TypeSpec",
60
+ repoUrl: "https://github.com/microsoft/typespec.git",
61
+ defaultBranch: "main",
62
+ description: "TypeSpec API definition language skills for code generation and API design workflows.",
63
+ owner: "microsoft",
64
+ trustTier: "official",
65
+ importCoverage: "medium",
66
+ estimatedPlugins: 20,
67
+ focuses: ["api", "code-generation", "typescript", "openapi"],
68
+ },
69
+ {
70
+ id: "microsoft-copilot-for-azure",
71
+ name: "GitHub Copilot for Azure",
72
+ repoUrl: "https://github.com/microsoft/GitHub-Copilot-for-Azure.git",
73
+ defaultBranch: "main",
74
+ description: "Azure-focused Copilot skills for cloud infrastructure, deployment, and resource management.",
75
+ owner: "microsoft",
76
+ trustTier: "official",
77
+ importCoverage: "high",
78
+ estimatedPlugins: 45,
79
+ focuses: ["azure", "cloud", "infrastructure", "deployment"],
80
+ },
81
+ {
82
+ id: "microsoft-vscode-python-environments",
83
+ name: "Microsoft VS Code Python Environments",
84
+ repoUrl: "https://github.com/microsoft/vscode-python-environments.git",
85
+ defaultBranch: "main",
86
+ description: "Maintainer, reviewer, and documentation agents for a production VS Code extension.",
87
+ owner: "microsoft",
88
+ trustTier: "official",
89
+ importCoverage: "medium",
90
+ estimatedPlugins: 8,
91
+ focuses: ["vscode", "python", "extension", "maintainer"],
92
+ },
93
+ {
94
+ id: "microsoft-vscode-docs",
95
+ name: "Microsoft VS Code Documentation",
96
+ repoUrl: "https://github.com/microsoft/vscode-docs.git",
97
+ defaultBranch: "main",
98
+ description: "Skills for VS Code documentation authoring, editing, and review workflows.",
99
+ owner: "microsoft",
100
+ trustTier: "official",
101
+ importCoverage: "medium",
102
+ estimatedPlugins: 12,
103
+ focuses: ["documentation", "vscode", "markdown", "authoring"],
104
+ },
105
+ {
106
+ id: "microsoft-windowsappsdk",
107
+ name: "Microsoft Windows App SDK",
108
+ repoUrl: "https://github.com/microsoft/WindowsAppSDK.git",
109
+ defaultBranch: "main",
110
+ description: "Windows App SDK skills for WinUI and Windows platform development.",
111
+ owner: "microsoft",
112
+ trustTier: "official",
113
+ importCoverage: "medium",
114
+ estimatedPlugins: 8,
115
+ focuses: ["windows", "winui", "sdk", "desktop"],
116
+ },
117
+ {
118
+ id: "microsoft-vscode-java-pack",
119
+ name: "Microsoft VS Code Java Pack",
120
+ repoUrl: "https://github.com/microsoft/vscode-java-pack.git",
121
+ defaultBranch: "main",
122
+ description: "Java development skills for VS Code including debugging, testing, and project management.",
123
+ owner: "microsoft",
124
+ trustTier: "official",
125
+ importCoverage: "medium",
126
+ estimatedPlugins: 10,
127
+ focuses: ["java", "vscode", "debugging", "testing"],
128
+ },
129
+ {
130
+ id: "microsoft-duroxide",
131
+ name: "Microsoft Duroxide",
132
+ repoUrl: "https://github.com/microsoft/duroxide.git",
133
+ defaultBranch: "main",
134
+ description: "Durable Functions in Rust — skills for building resilient serverless workflows.",
135
+ owner: "microsoft",
136
+ trustTier: "official",
137
+ importCoverage: "medium",
138
+ estimatedPlugins: 6,
139
+ focuses: ["rust", "serverless", "durable-functions", "workflows"],
140
+ },
141
+ {
142
+ id: "microsoft-ebpf-for-windows",
143
+ name: "Microsoft eBPF for Windows",
144
+ repoUrl: "https://github.com/microsoft/ebpf-for-windows.git",
145
+ defaultBranch: "main",
146
+ description: "eBPF development skills for Windows kernel and networking instrumentation.",
147
+ owner: "microsoft",
148
+ trustTier: "official",
149
+ importCoverage: "medium",
150
+ estimatedPlugins: 6,
151
+ focuses: ["ebpf", "windows", "kernel", "networking"],
152
+ },
153
+ // ── GitHub — Official ─────────────────────────────────────────────────────
154
+ {
155
+ id: "github-copilot-sdk",
156
+ name: "GitHub Copilot SDK",
157
+ repoUrl: "https://github.com/github/copilot-sdk.git",
158
+ defaultBranch: "main",
159
+ description: "Official GitHub workflow-authoring and docs-maintenance agents for Copilot SDK projects.",
160
+ owner: "github",
161
+ trustTier: "official",
162
+ importCoverage: "medium",
163
+ estimatedPlugins: 10,
164
+ focuses: ["copilot", "workflow", "docs"],
165
+ },
166
+ {
167
+ id: "github-desktop",
168
+ name: "GitHub Desktop",
169
+ repoUrl: "https://github.com/desktop/desktop.git",
170
+ defaultBranch: "development",
171
+ description: "GitHub Desktop app agent profiles for Electron, TypeScript, and Git workflow development.",
172
+ owner: "desktop",
173
+ trustTier: "official",
174
+ importCoverage: "medium",
175
+ estimatedPlugins: 10,
176
+ focuses: ["electron", "typescript", "git", "desktop"],
177
+ },
178
+ // ── Azure — Official ──────────────────────────────────────────────────────
179
+ {
180
+ id: "azure-sdk-for-js",
181
+ name: "Azure SDK for JavaScript",
182
+ repoUrl: "https://github.com/Azure/azure-sdk-for-js.git",
183
+ defaultBranch: "main",
184
+ description: "Azure JavaScript SDK repo with agentic workflow authoring guidance and prompts.",
185
+ owner: "azure",
186
+ trustTier: "official",
187
+ importCoverage: "medium",
188
+ estimatedPlugins: 15,
189
+ focuses: ["azure", "javascript", "sdk", "workflow"],
190
+ },
191
+ // ── Community — Verified ──────────────────────────────────────────────────
192
+ {
193
+ id: "mastra-ai-mastra",
194
+ name: "Mastra AI Framework",
195
+ repoUrl: "https://github.com/mastra-ai/mastra.git",
196
+ defaultBranch: "main",
197
+ description: "AI agent framework with extensive prompt templates for issue tracking, code review, and workflow automation.",
198
+ owner: "mastra-ai",
199
+ trustTier: "community",
200
+ importCoverage: "high",
201
+ estimatedPlugins: 40,
202
+ focuses: ["ai", "agents", "prompts", "automation"],
203
+ },
204
+ {
205
+ id: "z3prover-z3",
206
+ name: "Z3 Theorem Prover",
207
+ repoUrl: "https://github.com/Z3Prover/z3.git",
208
+ defaultBranch: "master",
209
+ description: "Z3 SMT solver agent profiles for formal verification and constraint solving workflows.",
210
+ owner: "Z3Prover",
211
+ trustTier: "community",
212
+ importCoverage: "low",
213
+ estimatedPlugins: 3,
214
+ focuses: ["formal-verification", "smt", "solver", "c++"],
215
+ },
216
+ {
217
+ id: "likec4-likec4",
218
+ name: "LikeC4",
219
+ repoUrl: "https://github.com/likec4/likec4.git",
220
+ defaultBranch: "main",
221
+ description: "Architecture-as-code tool with agents for diagram generation and architecture documentation.",
222
+ owner: "likec4",
223
+ trustTier: "community",
224
+ importCoverage: "medium",
225
+ estimatedPlugins: 12,
226
+ focuses: ["architecture", "diagrams", "documentation", "c4"],
227
+ },
228
+ {
229
+ id: "canonical-copilot-collections",
230
+ name: "Canonical Copilot Collections",
231
+ repoUrl: "https://github.com/canonical/copilot-collections.git",
232
+ defaultBranch: "main",
233
+ description: "Canonical's curated collection of Copilot agent definitions for Ubuntu and open-source development.",
234
+ owner: "canonical",
235
+ trustTier: "community",
236
+ importCoverage: "high",
237
+ estimatedPlugins: 50,
238
+ focuses: ["ubuntu", "linux", "open-source", "devops"],
239
+ },
240
+ {
241
+ id: "playwright-mcp-prompts",
242
+ name: "Playwright MCP Prompts",
243
+ repoUrl: "https://github.com/debs-obrien/playwright-mcp-prompts.git",
244
+ defaultBranch: "main",
245
+ description: "Prompt templates for Playwright end-to-end testing, page objects, and test generation.",
246
+ owner: "debs-obrien",
247
+ trustTier: "community",
248
+ importCoverage: "high",
249
+ estimatedPlugins: 25,
250
+ focuses: ["playwright", "testing", "e2e", "automation"],
251
+ },
252
+ {
253
+ id: "copilot-prompts-collection",
254
+ name: "GitHub Copilot Prompts",
255
+ repoUrl: "https://github.com/raffertyuy/github-copilot-prompts.git",
256
+ defaultBranch: "main",
257
+ description: "Curated collection of GitHub Copilot prompt files for code review, refactoring, and documentation.",
258
+ owner: "raffertyuy",
259
+ trustTier: "community",
260
+ importCoverage: "high",
261
+ estimatedPlugins: 30,
262
+ focuses: ["prompts", "code-review", "refactoring", "docs"],
263
+ },
264
+ {
265
+ id: "copilot-kit",
266
+ name: "Copilot Kit",
267
+ repoUrl: "https://github.com/TheSethRose/Copilot-Kit.git",
268
+ defaultBranch: "main",
269
+ description: "Comprehensive Copilot customization kit with agent profiles, skills, and prompt templates.",
270
+ owner: "TheSethRose",
271
+ trustTier: "community",
272
+ importCoverage: "high",
273
+ estimatedPlugins: 35,
274
+ focuses: ["copilot", "agents", "skills", "prompts"],
275
+ },
276
+ {
277
+ id: "dataplat-dbatools",
278
+ name: "dbatools",
279
+ repoUrl: "https://github.com/dataplat/dbatools.git",
280
+ defaultBranch: "development",
281
+ description: "SQL Server and database administration prompts for DBA workflows and automation.",
282
+ owner: "dataplat",
283
+ trustTier: "community",
284
+ importCoverage: "medium",
285
+ estimatedPlugins: 10,
286
+ focuses: ["sql-server", "database", "powershell", "administration"],
287
+ },
288
+ {
289
+ id: "finops-focus-spec",
290
+ name: "FinOps FOCUS Spec",
291
+ repoUrl: "https://github.com/FinOps-Open-Cost-and-Usage-Spec/FOCUS_Spec.git",
292
+ defaultBranch: "working_draft",
293
+ description: "FinOps specification prompts for cloud cost management and financial operations workflows.",
294
+ owner: "FinOps-Open-Cost-and-Usage-Spec",
295
+ trustTier: "community",
296
+ importCoverage: "medium",
297
+ estimatedPlugins: 8,
298
+ focuses: ["finops", "cloud-costs", "specification", "governance"],
299
+ },
300
+ // ── MCP Tool Repositories ──────────────────────────────────────────────────
301
+ {
302
+ id: "modelcontextprotocol-servers",
303
+ name: "MCP Official Servers",
304
+ repoUrl: "https://github.com/modelcontextprotocol/servers.git",
305
+ defaultBranch: "main",
306
+ description: "Official Model Context Protocol reference servers — filesystem, GitHub, Git, Postgres, Slack, Google Maps, Puppeteer, and more.",
307
+ owner: "modelcontextprotocol",
308
+ trustTier: "official",
309
+ importCoverage: "high",
310
+ estimatedPlugins: 25,
311
+ focuses: ["mcp", "tools", "filesystem", "database", "web", "search"],
312
+ },
313
+ {
314
+ id: "github-mcp-server",
315
+ name: "GitHub MCP Server",
316
+ repoUrl: "https://github.com/github/github-mcp-server.git",
317
+ defaultBranch: "main",
318
+ description: "Official GitHub MCP server for repository management, issues, pull requests, and code search.",
319
+ owner: "github",
320
+ trustTier: "official",
321
+ importCoverage: "medium",
322
+ estimatedPlugins: 3,
323
+ focuses: ["mcp", "github", "issues", "pull-requests", "code-search"],
324
+ },
325
+ {
326
+ id: "punkpeye-awesome-mcp-servers",
327
+ name: "Awesome MCP Servers",
328
+ repoUrl: "https://github.com/punkpeye/awesome-mcp-servers.git",
329
+ defaultBranch: "main",
330
+ description: "Community-curated list of MCP server implementations — databases, dev tools, cloud platforms, AI services, and productivity tools.",
331
+ owner: "punkpeye",
332
+ trustTier: "community",
333
+ importCoverage: "low",
334
+ estimatedPlugins: 5,
335
+ focuses: ["mcp", "tools", "community", "catalog"],
336
+ },
337
+ ]);
338
+
339
+ function clampNumber(value, min, max) {
340
+ const numeric = Number(value);
341
+ if (!Number.isFinite(numeric)) return min;
342
+ return Math.max(min, Math.min(max, numeric));
343
+ }
344
+
345
+ function normalizeWellKnownSource(source = {}) {
346
+ const repoUrl = String(source.repoUrl || "").trim();
347
+ const github = repoUrl.match(/^https:\/\/github\.com\/([^/]+)\/([^/.]+)(?:\.git)?$/i);
348
+ const owner = String(source.owner || (github?.[1] || "")).trim();
349
+ const repo = String(source.repo || (github?.[2] || "")).trim();
350
+ return {
351
+ ...source,
352
+ owner: owner || null,
353
+ repo: repo || null,
354
+ provider: source.provider || (github ? "github" : null),
355
+ importCoverage: String(source.importCoverage || "medium"),
356
+ focuses: toStringArray(source.focuses),
357
+ };
358
+ }
359
+
360
+ function compareWellKnownSources(a, b) {
361
+ // Sort by estimated plugin count (most first), then trust score, then name
362
+ const pluginDelta = Number(b?.estimatedPlugins || 0) - Number(a?.estimatedPlugins || 0);
363
+ if (pluginDelta !== 0) return pluginDelta;
364
+ const delta = Number(b?.trust?.score || 0) - Number(a?.trust?.score || 0);
365
+ if (delta !== 0) return delta;
366
+ return String(a?.name || "").localeCompare(String(b?.name || ""));
367
+ }
368
+
369
+ export function computeWellKnownSourceTrust(source, probe = {}, options = {}) {
370
+ const nowMs = Number(options?.nowMs || Date.now());
371
+ const normalized = normalizeWellKnownSource(source);
372
+ const reasons = [];
373
+ let score = 20;
374
+
375
+ if (normalized.trustTier === "official") {
376
+ score += 25;
377
+ reasons.push("official-maintainer");
378
+ }
379
+ if (TRUSTED_GITHUB_OWNERS.has(String(normalized.owner || "").toLowerCase())) {
380
+ score += 15;
381
+ reasons.push("trusted-owner");
382
+ }
383
+ if (normalized.importCoverage === "high") {
384
+ score += 12;
385
+ reasons.push("high-import-coverage");
386
+ } else if (normalized.importCoverage === "medium") {
387
+ score += 6;
388
+ reasons.push("import-coverage");
389
+ }
390
+ if (normalized.provider === "github") {
391
+ score += 4;
392
+ reasons.push("github-source");
393
+ }
394
+
395
+ const stars = Number(probe?.stars || 0);
396
+ if (stars >= 10000) {
397
+ score += 10;
398
+ reasons.push("popular-repo");
399
+ } else if (stars >= 1000) {
400
+ score += 6;
401
+ reasons.push("established-repo");
402
+ } else if (stars >= 100) {
403
+ score += 3;
404
+ }
405
+
406
+ const daysSincePush = Number.isFinite(probe?.daysSincePush)
407
+ ? Number(probe.daysSincePush)
408
+ : (probe?.pushedAt ? Math.max(0, (nowMs - Date.parse(probe.pushedAt)) / 86400000) : null);
409
+ if (daysSincePush != null) {
410
+ if (daysSincePush <= 45) {
411
+ score += 10;
412
+ reasons.push("recently-updated");
413
+ } else if (daysSincePush <= 180) {
414
+ score += 6;
415
+ reasons.push("active-updates");
416
+ } else if (daysSincePush <= 365) {
417
+ score += 2;
418
+ } else if (daysSincePush > 730) {
419
+ score -= 16;
420
+ reasons.push("stale-upstream");
421
+ }
422
+ }
423
+
424
+ if (probe?.reachable === true) {
425
+ score += 8;
426
+ reasons.push("remote-reachable");
427
+ } else if (probe?.reachable === false) {
428
+ score -= 28;
429
+ reasons.push("remote-unreachable");
430
+ }
431
+
432
+ if (probe?.branchExists === true) {
433
+ score += 6;
434
+ reasons.push("branch-ok");
435
+ } else if (probe?.branchExists === false) {
436
+ score -= 22;
437
+ reasons.push("branch-missing");
438
+ }
439
+
440
+ if (probe?.archived === true) {
441
+ score -= 45;
442
+ reasons.push("archived");
443
+ }
444
+ if (probe?.disabled === true) {
445
+ score -= 45;
446
+ reasons.push("disabled");
447
+ }
448
+
449
+ score = Math.round(clampNumber(score, 0, 100));
450
+ // Hard-disable only for unreachable/archived/disabled repos — low score gets a warning but remains importable
451
+ const hardBlocked = probe?.archived === true || probe?.disabled === true || probe?.reachable === false || probe?.branchExists === false;
452
+ const enabled = !hardBlocked;
453
+ const lowTrust = score < 55;
454
+ const status = hardBlocked ? "disabled" : score >= 85 ? "healthy" : score >= 65 ? "warning" : score >= 55 ? "degraded" : "low-trust";
455
+
456
+ return {
457
+ score,
458
+ status,
459
+ enabled,
460
+ lowTrust,
461
+ reasons: uniqueStrings(reasons),
462
+ };
463
+ }
464
+
465
+ function buildWellKnownSourceResult(source, probe = null, options = {}) {
466
+ const normalized = normalizeWellKnownSource(source);
467
+ const trust = computeWellKnownSourceTrust(normalized, probe || {}, options);
468
+ return {
469
+ ...normalized,
470
+ trust,
471
+ probe: probe ? { ...probe } : null,
472
+ enabled: trust.enabled,
473
+ status: trust.status,
474
+ };
475
+ }
476
+
477
+ export function listWellKnownAgentSources() {
478
+ return WELL_KNOWN_AGENT_SOURCES
479
+ .map((source) => buildWellKnownSourceResult(source))
480
+ .sort(compareWellKnownSources);
481
+ }
482
+
483
+ export function clearWellKnownAgentSourceProbeCache() {
484
+ wellKnownSourceProbeCache.clear();
485
+ }
486
+
487
+ async function fetchGithubRepoProbe(source, options = {}) {
488
+ const normalized = normalizeWellKnownSource(source);
489
+ if (normalized.provider !== "github" || !normalized.owner || !normalized.repo) {
490
+ return { checkedAt: nowISO(), reachable: false, branchExists: false, error: "Unsupported repository provider" };
491
+ }
492
+
493
+ const fetchImpl = options.fetchImpl || globalThis.fetch;
494
+ const spawnImpl = options.spawnImpl || spawnSync;
495
+ const branch = String(normalized.defaultBranch || "main").trim() || "main";
496
+ const headers = {
497
+ "Accept": "application/vnd.github+json",
498
+ "User-Agent": "bosun-library-manager",
499
+ };
500
+ if (process.env.GITHUB_TOKEN) headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
501
+
502
+ let repoMeta = null;
503
+ let repoError = null;
504
+ if (typeof fetchImpl === "function") {
505
+ try {
506
+ const response = await fetchImpl(`https://api.github.com/repos/${normalized.owner}/${normalized.repo}`, { headers });
507
+ if (response?.ok) {
508
+ repoMeta = await response.json();
509
+ } else {
510
+ repoError = `GitHub API returned ${Number(response?.status || 0) || "error"}`;
511
+ }
512
+ } catch (err) {
513
+ repoError = err?.message || String(err);
514
+ }
515
+ } else {
516
+ repoError = "fetch unavailable";
517
+ }
518
+
519
+ let reachable = false;
520
+ let branchExists = false;
521
+ let gitError = null;
522
+ try {
523
+ const remote = spawnImpl("git", ["ls-remote", "--exit-code", "--heads", normalized.repoUrl, branch], {
524
+ encoding: "utf8",
525
+ stdio: ["ignore", "pipe", "pipe"],
526
+ timeout: Number(options.timeoutMs || 15000),
527
+ });
528
+ const stdout = String(remote?.stdout || "").trim();
529
+ reachable = Number(remote?.status) === 0 || stdout.length > 0;
530
+ branchExists = reachable && stdout.length > 0;
531
+ if (!reachable || !branchExists) {
532
+ gitError = String(remote?.stderr || remote?.stdout || "git ls-remote failed").trim() || null;
533
+ }
534
+ } catch (err) {
535
+ gitError = err?.message || String(err);
536
+ }
537
+
538
+ return {
539
+ checkedAt: nowISO(),
540
+ reachable,
541
+ branchExists,
542
+ defaultBranch: String(repoMeta?.default_branch || branch || "main"),
543
+ archived: repoMeta?.archived === true,
544
+ disabled: repoMeta?.disabled === true,
545
+ stars: Number(repoMeta?.stargazers_count || 0),
546
+ forks: Number(repoMeta?.forks_count || 0),
547
+ openIssues: Number(repoMeta?.open_issues_count || 0),
548
+ pushedAt: repoMeta?.pushed_at || null,
549
+ daysSincePush: repoMeta?.pushed_at ? Math.max(0, Math.round((Date.now() - Date.parse(repoMeta.pushed_at)) / 86400000)) : null,
550
+ apiReachable: Boolean(repoMeta),
551
+ importReady: reachable && branchExists && repoMeta?.archived !== true && repoMeta?.disabled !== true,
552
+ error: gitError || repoError || null,
553
+ };
554
+ }
555
+
556
+ export async function probeWellKnownAgentSources(options = {}) {
557
+ const nowMs = Number(options?.nowMs || Date.now());
558
+ const ttlMs = Math.max(1000, Number(options?.ttlMs || 30 * 60 * 1000));
559
+ const sourceId = String(options?.sourceId || "").trim().toLowerCase();
560
+ const refresh = options?.refresh === true;
561
+ const sources = WELL_KNOWN_AGENT_SOURCES.filter((source) => !sourceId || source.id === sourceId);
562
+ const results = [];
563
+
564
+ for (const source of sources) {
565
+ const cacheKey = source.id;
566
+ const cached = wellKnownSourceProbeCache.get(cacheKey) || null;
567
+ if (!refresh && cached && (nowMs - Number(cached.cachedAt || 0)) < ttlMs) {
568
+ results.push(buildWellKnownSourceResult(source, cached.probe, { nowMs }));
569
+ continue;
570
+ }
571
+
572
+ const probe = await fetchGithubRepoProbe(source, options);
573
+ wellKnownSourceProbeCache.set(cacheKey, { cachedAt: nowMs, probe });
574
+ results.push(buildWellKnownSourceResult(source, probe, { nowMs }));
575
+ }
576
+
577
+ return results.sort(compareWellKnownSources);
578
+ }