bosun 0.36.0 → 0.36.2

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 (98) hide show
  1. package/.env.example +98 -16
  2. package/README.md +27 -0
  3. package/agent-event-bus.mjs +5 -5
  4. package/agent-pool.mjs +129 -12
  5. package/agent-prompts.mjs +7 -1
  6. package/agent-sdk.mjs +13 -2
  7. package/agent-supervisor.mjs +2 -2
  8. package/agent-work-report.mjs +1 -1
  9. package/anomaly-detector.mjs +6 -6
  10. package/autofix.mjs +15 -15
  11. package/bosun-skills.mjs +4 -4
  12. package/bosun.schema.json +160 -4
  13. package/claude-shell.mjs +11 -11
  14. package/cli.mjs +21 -21
  15. package/codex-config.mjs +19 -19
  16. package/codex-shell.mjs +180 -29
  17. package/config-doctor.mjs +27 -2
  18. package/config.mjs +60 -7
  19. package/copilot-shell.mjs +4 -4
  20. package/error-detector.mjs +1 -1
  21. package/fleet-coordinator.mjs +2 -2
  22. package/gemini-shell.mjs +692 -0
  23. package/github-oauth-portal.mjs +1 -1
  24. package/github-reconciler.mjs +2 -2
  25. package/kanban-adapter.mjs +741 -168
  26. package/merge-strategy.mjs +25 -25
  27. package/monitor.mjs +123 -105
  28. package/opencode-shell.mjs +22 -22
  29. package/package.json +7 -1
  30. package/postinstall.mjs +22 -22
  31. package/pr-cleanup-daemon.mjs +6 -6
  32. package/prepublish-check.mjs +4 -4
  33. package/presence.mjs +2 -2
  34. package/primary-agent.mjs +85 -7
  35. package/publish.mjs +1 -1
  36. package/review-agent.mjs +1 -1
  37. package/session-tracker.mjs +11 -0
  38. package/setup-web-server.mjs +429 -21
  39. package/setup.mjs +367 -12
  40. package/shared-knowledge.mjs +1 -1
  41. package/startup-service.mjs +9 -9
  42. package/stream-resilience.mjs +58 -4
  43. package/sync-engine.mjs +2 -2
  44. package/task-assessment.mjs +9 -9
  45. package/task-cli.mjs +1 -1
  46. package/task-complexity.mjs +71 -2
  47. package/task-context.mjs +1 -2
  48. package/task-executor.mjs +104 -41
  49. package/telegram-bot.mjs +825 -494
  50. package/telegram-sentinel.mjs +28 -28
  51. package/ui/app.js +256 -23
  52. package/ui/app.monolith.js +1 -1
  53. package/ui/components/agent-selector.js +4 -3
  54. package/ui/components/chat-view.js +101 -28
  55. package/ui/components/diff-viewer.js +3 -3
  56. package/ui/components/kanban-board.js +3 -3
  57. package/ui/components/session-list.js +255 -35
  58. package/ui/components/workspace-switcher.js +3 -3
  59. package/ui/demo.html +209 -194
  60. package/ui/index.html +3 -3
  61. package/ui/modules/icon-utils.js +206 -142
  62. package/ui/modules/icons.js +2 -27
  63. package/ui/modules/settings-schema.js +29 -5
  64. package/ui/modules/streaming.js +30 -2
  65. package/ui/modules/vision-stream.js +275 -0
  66. package/ui/modules/voice-client.js +102 -9
  67. package/ui/modules/voice-fallback.js +62 -6
  68. package/ui/modules/voice-overlay.js +594 -59
  69. package/ui/modules/voice.js +31 -38
  70. package/ui/setup.html +284 -34
  71. package/ui/styles/components.css +47 -0
  72. package/ui/styles/sessions.css +75 -0
  73. package/ui/tabs/agents.js +73 -43
  74. package/ui/tabs/chat.js +37 -40
  75. package/ui/tabs/control.js +2 -2
  76. package/ui/tabs/dashboard.js +1 -1
  77. package/ui/tabs/infra.js +10 -10
  78. package/ui/tabs/library.js +8 -8
  79. package/ui/tabs/logs.js +10 -10
  80. package/ui/tabs/settings.js +20 -20
  81. package/ui/tabs/tasks.js +76 -47
  82. package/ui-server.mjs +1761 -124
  83. package/update-check.mjs +13 -13
  84. package/ve-kanban.mjs +1 -1
  85. package/whatsapp-channel.mjs +5 -5
  86. package/workflow-engine.mjs +20 -1
  87. package/workflow-nodes.mjs +904 -4
  88. package/workflow-templates/agents.mjs +321 -7
  89. package/workflow-templates/ci-cd.mjs +6 -6
  90. package/workflow-templates/github.mjs +156 -84
  91. package/workflow-templates/planning.mjs +8 -8
  92. package/workflow-templates/reliability.mjs +8 -8
  93. package/workflow-templates/security.mjs +3 -3
  94. package/workflow-templates.mjs +15 -9
  95. package/workspace-manager.mjs +85 -1
  96. package/workspace-monitor.mjs +2 -2
  97. package/workspace-registry.mjs +2 -2
  98. package/worktree-manager.mjs +1 -1
@@ -142,10 +142,94 @@ function extractSlug(url) {
142
142
  return "";
143
143
  }
144
144
 
145
+ function extractGithubSlug(url) {
146
+ const raw = String(url || "").trim();
147
+ if (!raw) return "";
148
+ if (!/github\.com[:/]/i.test(raw)) return "";
149
+ return extractSlug(raw);
150
+ }
151
+
145
152
  function resolveRepoUrl(repo) {
146
153
  return repo.url || (repo.slug ? `https://github.com/${repo.slug.replace(/\.git$/i, "")}.git` : "");
147
154
  }
148
155
 
156
+ function normalizeWorkspaceRepoEntry(repo, index = 0) {
157
+ const raw =
158
+ typeof repo === "string"
159
+ ? { slug: repo }
160
+ : (repo && typeof repo === "object" ? repo : null);
161
+ if (!raw) return null;
162
+ const slug = String(raw.slug || extractGithubSlug(raw.url) || "").trim();
163
+ const url = String(raw.url || resolveRepoUrl({ slug }) || "").trim();
164
+ const name = String(raw.name || raw.id || extractRepoName(slug || url || raw.path || ""))
165
+ .trim()
166
+ .replace(/\.git$/i, "");
167
+ if (!name && !slug && !url) return null;
168
+ return {
169
+ name: name || `repo-${index + 1}`,
170
+ slug,
171
+ url,
172
+ primary: raw.primary === true || index === 0,
173
+ };
174
+ }
175
+
176
+ function normalizeWorkspaceEntry(workspace, index = 0) {
177
+ if (!workspace || typeof workspace !== "object") return null;
178
+ const fallbackId = `workspace-${index + 1}`;
179
+ const id = normalizeId(workspace.id || workspace.name || fallbackId);
180
+ if (!id) return null;
181
+ const repos = Array.isArray(workspace.repos)
182
+ ? workspace.repos
183
+ .map((repo, repoIndex) => normalizeWorkspaceRepoEntry(repo, repoIndex))
184
+ .filter(Boolean)
185
+ : [];
186
+ let activeRepo = String(workspace.activeRepo || "").trim();
187
+ if (!activeRepo && repos[0]?.name) activeRepo = repos[0].name;
188
+ if (activeRepo && !repos.some((repo) => repo.name === activeRepo)) {
189
+ activeRepo = repos[0]?.name || "";
190
+ }
191
+ return {
192
+ ...workspace,
193
+ id,
194
+ name: String(workspace.name || workspace.id || id).trim() || id,
195
+ repos,
196
+ createdAt: workspace.createdAt || new Date().toISOString(),
197
+ activeRepo: activeRepo || null,
198
+ };
199
+ }
200
+
201
+ function normalizeWorkspaceList(workspaces) {
202
+ if (!Array.isArray(workspaces)) return [];
203
+ const deduped = new Map();
204
+ for (let i = 0; i < workspaces.length; i += 1) {
205
+ const normalized = normalizeWorkspaceEntry(workspaces[i], i);
206
+ if (!normalized) continue;
207
+ const existing = deduped.get(normalized.id);
208
+ if (!existing) {
209
+ deduped.set(normalized.id, normalized);
210
+ continue;
211
+ }
212
+ const mergedRepos = [...existing.repos];
213
+ const existingRepoNames = new Set(existing.repos.map((repo) => repo.name));
214
+ for (const repo of normalized.repos) {
215
+ if (existingRepoNames.has(repo.name)) continue;
216
+ existingRepoNames.add(repo.name);
217
+ mergedRepos.push(repo);
218
+ }
219
+ deduped.set(normalized.id, {
220
+ ...existing,
221
+ ...normalized,
222
+ repos: mergedRepos,
223
+ activeRepo:
224
+ normalized.activeRepo ||
225
+ existing.activeRepo ||
226
+ mergedRepos[0]?.name ||
227
+ null,
228
+ });
229
+ }
230
+ return [...deduped.values()];
231
+ }
232
+
149
233
  function makeNonGitBackupPath(repoPath) {
150
234
  const stamp = new Date().toISOString().replace(/[:.]/g, "-");
151
235
  let candidate = `${repoPath}.non-git-backup-${stamp}`;
@@ -224,7 +308,7 @@ function saveBosunConfig(configDir, config) {
224
308
 
225
309
  function getWorkspacesFromConfig(configDir) {
226
310
  const config = loadBosunConfig(configDir);
227
- return Array.isArray(config.workspaces) ? config.workspaces : [];
311
+ return normalizeWorkspaceList(config.workspaces);
228
312
  }
229
313
 
230
314
  function saveWorkspacesToConfig(configDir, workspaces, activeWorkspace) {
@@ -193,7 +193,7 @@ class WorkspaceMonitor {
193
193
 
194
194
  await this.logWorkspace(
195
195
  attemptId,
196
- `[${new Date().toISOString()}] ⚠️ STUCK DETECTED: ${stuckCheck.reason}\n` +
196
+ `[${new Date().toISOString()}] :alert: STUCK DETECTED: ${stuckCheck.reason}\n` +
197
197
  ` Time since last progress: ${Math.round((now - state.lastProgressAt) / 60000)} minutes\n` +
198
198
  ` Recommendation: ${stuckCheck.recommendation}\n\n`,
199
199
  );
@@ -214,7 +214,7 @@ class WorkspaceMonitor {
214
214
  for (const warning of warnings) {
215
215
  await this.logWorkspace(
216
216
  attemptId,
217
- `[${new Date().toISOString()}] ⚠️ ${warning}\n`,
217
+ `[${new Date().toISOString()}] :alert: ${warning}\n`,
218
218
  );
219
219
  }
220
220
 
@@ -224,11 +224,11 @@ export function formatRegistryDiagnostics(errors, warnings) {
224
224
  const parts = [];
225
225
  if (errors && errors.length > 0) {
226
226
  parts.push(
227
- `❌ Registry errors:\n${errors.map((e) => ` • ${e}`).join("\n")}`,
227
+ `:close: Registry errors:\n${errors.map((e) => ` • ${e}`).join("\n")}`,
228
228
  );
229
229
  }
230
230
  if (warnings && warnings.length > 0) {
231
- parts.push(`⚠️ ${warnings.map((w) => w).join("\n⚠️ ")}`);
231
+ parts.push(`:alert: ${warnings.map((w) => w).join("\n:alert: ")}`);
232
232
  }
233
233
  return parts.length > 0 ? parts.join("\n") : null;
234
234
  }
@@ -73,7 +73,7 @@ function fixGitConfigCorruption(repoRoot) {
73
73
  });
74
74
  if (bareResult.stdout?.trim() === "true") {
75
75
  console.warn(
76
- `${TAG} ⚠️ Detected core.bare=true on main repo — fixing git config corruption`,
76
+ `${TAG} :alert: Detected core.bare=true on main repo — fixing git config corruption`,
77
77
  );
78
78
  spawnSync("git", ["config", "--unset", "core.bare"], {
79
79
  cwd: repoRoot,