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.
- package/.env.example +98 -16
- package/README.md +27 -0
- package/agent-event-bus.mjs +5 -5
- package/agent-pool.mjs +129 -12
- package/agent-prompts.mjs +7 -1
- package/agent-sdk.mjs +13 -2
- package/agent-supervisor.mjs +2 -2
- package/agent-work-report.mjs +1 -1
- package/anomaly-detector.mjs +6 -6
- package/autofix.mjs +15 -15
- package/bosun-skills.mjs +4 -4
- package/bosun.schema.json +160 -4
- package/claude-shell.mjs +11 -11
- package/cli.mjs +21 -21
- package/codex-config.mjs +19 -19
- package/codex-shell.mjs +180 -29
- package/config-doctor.mjs +27 -2
- package/config.mjs +60 -7
- package/copilot-shell.mjs +4 -4
- package/error-detector.mjs +1 -1
- package/fleet-coordinator.mjs +2 -2
- package/gemini-shell.mjs +692 -0
- package/github-oauth-portal.mjs +1 -1
- package/github-reconciler.mjs +2 -2
- package/kanban-adapter.mjs +741 -168
- package/merge-strategy.mjs +25 -25
- package/monitor.mjs +123 -105
- package/opencode-shell.mjs +22 -22
- package/package.json +7 -1
- package/postinstall.mjs +22 -22
- package/pr-cleanup-daemon.mjs +6 -6
- package/prepublish-check.mjs +4 -4
- package/presence.mjs +2 -2
- package/primary-agent.mjs +85 -7
- package/publish.mjs +1 -1
- package/review-agent.mjs +1 -1
- package/session-tracker.mjs +11 -0
- package/setup-web-server.mjs +429 -21
- package/setup.mjs +367 -12
- package/shared-knowledge.mjs +1 -1
- package/startup-service.mjs +9 -9
- package/stream-resilience.mjs +58 -4
- package/sync-engine.mjs +2 -2
- package/task-assessment.mjs +9 -9
- package/task-cli.mjs +1 -1
- package/task-complexity.mjs +71 -2
- package/task-context.mjs +1 -2
- package/task-executor.mjs +104 -41
- package/telegram-bot.mjs +825 -494
- package/telegram-sentinel.mjs +28 -28
- package/ui/app.js +256 -23
- package/ui/app.monolith.js +1 -1
- package/ui/components/agent-selector.js +4 -3
- package/ui/components/chat-view.js +101 -28
- package/ui/components/diff-viewer.js +3 -3
- package/ui/components/kanban-board.js +3 -3
- package/ui/components/session-list.js +255 -35
- package/ui/components/workspace-switcher.js +3 -3
- package/ui/demo.html +209 -194
- package/ui/index.html +3 -3
- package/ui/modules/icon-utils.js +206 -142
- package/ui/modules/icons.js +2 -27
- package/ui/modules/settings-schema.js +29 -5
- package/ui/modules/streaming.js +30 -2
- package/ui/modules/vision-stream.js +275 -0
- package/ui/modules/voice-client.js +102 -9
- package/ui/modules/voice-fallback.js +62 -6
- package/ui/modules/voice-overlay.js +594 -59
- package/ui/modules/voice.js +31 -38
- package/ui/setup.html +284 -34
- package/ui/styles/components.css +47 -0
- package/ui/styles/sessions.css +75 -0
- package/ui/tabs/agents.js +73 -43
- package/ui/tabs/chat.js +37 -40
- package/ui/tabs/control.js +2 -2
- package/ui/tabs/dashboard.js +1 -1
- package/ui/tabs/infra.js +10 -10
- package/ui/tabs/library.js +8 -8
- package/ui/tabs/logs.js +10 -10
- package/ui/tabs/settings.js +20 -20
- package/ui/tabs/tasks.js +76 -47
- package/ui-server.mjs +1761 -124
- package/update-check.mjs +13 -13
- package/ve-kanban.mjs +1 -1
- package/whatsapp-channel.mjs +5 -5
- package/workflow-engine.mjs +20 -1
- package/workflow-nodes.mjs +904 -4
- package/workflow-templates/agents.mjs +321 -7
- package/workflow-templates/ci-cd.mjs +6 -6
- package/workflow-templates/github.mjs +156 -84
- package/workflow-templates/planning.mjs +8 -8
- package/workflow-templates/reliability.mjs +8 -8
- package/workflow-templates/security.mjs +3 -3
- package/workflow-templates.mjs +15 -9
- package/workspace-manager.mjs +85 -1
- package/workspace-monitor.mjs +2 -2
- package/workspace-registry.mjs +2 -2
- package/worktree-manager.mjs +1 -1
|
@@ -132,7 +132,7 @@ Respond with JSON: { "action": "<choice>", "reason": "<why>", "message": "<optio
|
|
|
132
132
|
}, { x: 700, y: 680 }),
|
|
133
133
|
|
|
134
134
|
node("do-escalate", "notify.telegram", "Escalate to Human", {
|
|
135
|
-
message: "
|
|
135
|
+
message: ":eye: PR #{{prNumber}} needs manual review: {{decision.reason}}",
|
|
136
136
|
}, { x: 900, y: 680 }),
|
|
137
137
|
|
|
138
138
|
node("action-succeeded", "condition.expression", "Action Succeeded?", {
|
|
@@ -142,7 +142,7 @@ Respond with JSON: { "action": "<choice>", "reason": "<why>", "message": "<optio
|
|
|
142
142
|
|
|
143
143
|
node("notify-action-failed", "notify.telegram", "Escalate Action Failure", {
|
|
144
144
|
message:
|
|
145
|
-
"
|
|
145
|
+
":alert: PR #{{prNumber}} workflow action failed after retries ({{decision.action}}). " +
|
|
146
146
|
"Reason: {{decision.reason}}. Manual follow-up required.",
|
|
147
147
|
}, { x: 760, y: 850 }),
|
|
148
148
|
|
|
@@ -300,7 +300,7 @@ export const PR_CONFLICT_RESOLVER_TEMPLATE = {
|
|
|
300
300
|
id: "template-pr-conflict-resolver",
|
|
301
301
|
name: "PR Conflict Resolver",
|
|
302
302
|
description:
|
|
303
|
-
"
|
|
303
|
+
":alert: SUPERSEDED for bosun-managed repos — use the Bosun PR Watchdog " +
|
|
304
304
|
"(template-bosun-pr-watchdog) instead. The Watchdog consolidates conflict " +
|
|
305
305
|
"resolution, CI-failure repair, diff-safety review, and merge into one " +
|
|
306
306
|
"cycle with a single gh API call and a mandatory review gate before any merge. " +
|
|
@@ -413,7 +413,7 @@ export const PR_CONFLICT_RESOLVER_TEMPLATE = {
|
|
|
413
413
|
}, { x: 200, y: 800 }),
|
|
414
414
|
|
|
415
415
|
node("notify-fixed", "notify.telegram", "Notify Resolved", {
|
|
416
|
-
message: "
|
|
416
|
+
message: ":settings: PR #{{targetPrNumber}} conflict resolved — awaiting CI and Watchdog review before merge",
|
|
417
417
|
silent: true,
|
|
418
418
|
}, { x: 200, y: 960 }),
|
|
419
419
|
|
|
@@ -507,7 +507,7 @@ export const STALE_PR_REAPER_TEMPLATE = {
|
|
|
507
507
|
}, { x: 200, y: 800 }),
|
|
508
508
|
|
|
509
509
|
node("summary", "notify.telegram", "Summary", {
|
|
510
|
-
message: "
|
|
510
|
+
message: ":trash: Stale PR cleanup complete",
|
|
511
511
|
silent: true,
|
|
512
512
|
}, { x: 200, y: 950 }),
|
|
513
513
|
|
|
@@ -596,19 +596,19 @@ Generate professional release notes in the following format:
|
|
|
596
596
|
|
|
597
597
|
# What's Changed
|
|
598
598
|
|
|
599
|
-
##
|
|
599
|
+
## :rocket: Features
|
|
600
600
|
- [list feat: commits with PR references]
|
|
601
601
|
|
|
602
|
-
##
|
|
602
|
+
## :bug: Bug Fixes
|
|
603
603
|
- [list fix: commits with PR references]
|
|
604
604
|
|
|
605
|
-
##
|
|
605
|
+
## :settings: Improvements
|
|
606
606
|
- [list refactor/perf/style commits]
|
|
607
607
|
|
|
608
|
-
##
|
|
608
|
+
## :u1f4da: Documentation
|
|
609
609
|
- [list docs: commits]
|
|
610
610
|
|
|
611
|
-
##
|
|
611
|
+
## :hammer: Internal
|
|
612
612
|
- [list chore/ci/build commits]
|
|
613
613
|
|
|
614
614
|
Omit empty sections. Include contributor attribution. Be concise.`,
|
|
@@ -669,8 +669,8 @@ export const BOSUN_PR_WATCHDOG_TEMPLATE = {
|
|
|
669
669
|
id: "template-bosun-pr-watchdog",
|
|
670
670
|
name: "Bosun PR Watchdog",
|
|
671
671
|
description:
|
|
672
|
-
"Scans open bosun-attached PRs on a schedule. Makes
|
|
673
|
-
"fetch and classify
|
|
672
|
+
"Scans open bosun-attached PRs on a schedule. Makes one gh pr list call " +
|
|
673
|
+
"per target repo to fetch and classify PRs, then: labels conflicting or failing-CI PRs " +
|
|
674
674
|
"with bosun-needs-fix and dispatches a repair agent; sends merge candidates " +
|
|
675
675
|
"through a MANDATORY agent review gate that checks diff stats before any " +
|
|
676
676
|
"merge — preventing destructive PRs (e.g. -183k lines) from being silently " +
|
|
@@ -682,6 +682,9 @@ export const BOSUN_PR_WATCHDOG_TEMPLATE = {
|
|
|
682
682
|
mergeMethod: "squash", // squash | merge | rebase
|
|
683
683
|
labelNeedsFix: "bosun-needs-fix", // applied to CI failures and conflicts
|
|
684
684
|
labelNeedsReview: "bosun-needs-human-review", // applied when review agent flags a suspicious diff
|
|
685
|
+
// auto: active workspace repos from bosun.config.json (fallback current repo)
|
|
686
|
+
// all/current/<owner/repo>/comma,list also supported.
|
|
687
|
+
repoScope: "auto",
|
|
685
688
|
maxPrs: 25,
|
|
686
689
|
intervalMs: 300_000, // 5 minutes
|
|
687
690
|
// Merge-safety thresholds checked by the review agent:
|
|
@@ -696,73 +699,141 @@ export const BOSUN_PR_WATCHDOG_TEMPLATE = {
|
|
|
696
699
|
}, { x: 400, y: 50 }),
|
|
697
700
|
|
|
698
701
|
// ─────────────────────────────────────────────────────────────────────────
|
|
699
|
-
// STEP 1:
|
|
700
|
-
//
|
|
701
|
-
// from this one response, so no repeated gh pr list calls later.
|
|
702
|
+
// STEP 1: One gh pr list per target repo, then classify+label in-memory.
|
|
703
|
+
// This avoids duplicate fetches and keeps per-cycle gh traffic bounded.
|
|
702
704
|
// ─────────────────────────────────────────────────────────────────────────
|
|
703
705
|
node("fetch-and-classify", "action.run_command", "Fetch, Classify & Label PRs", {
|
|
704
706
|
// Fetches all open bosun-attached PRs with every field needed for
|
|
705
|
-
// classification.
|
|
707
|
+
// classification. Runs one list call per target repo (auto-discovered
|
|
708
|
+
// from bosun.config.json workspaces by default), then:
|
|
706
709
|
// • Classifies each PR into: ready | conflict | ci_failure | pending | draft
|
|
707
710
|
// • Labels conflict/ci_failure PRs with bosun-needs-fix (skips if already present)
|
|
708
711
|
// • Outputs a JSON summary used by all downstream nodes/agents
|
|
709
|
-
// Total gh API calls this node makes:
|
|
712
|
+
// Total gh API calls this node makes: R list calls + N edits
|
|
713
|
+
// (R = target repos, N = newly-broken PRs needing fix label).
|
|
710
714
|
command: [
|
|
711
|
-
"
|
|
712
|
-
"
|
|
713
|
-
"
|
|
714
|
-
"
|
|
715
|
+
"node -e \"",
|
|
716
|
+
"const fs=require('fs');",
|
|
717
|
+
"const path=require('path');",
|
|
718
|
+
"const {execFileSync}=require('child_process');",
|
|
715
719
|
"const LABEL_FIX='{{labelNeedsFix}}';",
|
|
716
|
-
"const
|
|
717
|
-
"
|
|
718
|
-
"
|
|
719
|
-
"
|
|
720
|
-
"
|
|
721
|
-
"
|
|
722
|
-
"
|
|
723
|
-
"
|
|
724
|
-
" const
|
|
725
|
-
"
|
|
726
|
-
"
|
|
727
|
-
"
|
|
728
|
-
"
|
|
729
|
-
"
|
|
730
|
-
" const
|
|
731
|
-
" const
|
|
732
|
-
"
|
|
733
|
-
"
|
|
734
|
-
"
|
|
735
|
-
"
|
|
736
|
-
"
|
|
737
|
-
"
|
|
738
|
-
"
|
|
739
|
-
"
|
|
740
|
-
"
|
|
720
|
+
"const MAX_PRS=Math.max(1,Number('{{maxPrs}}')||25);",
|
|
721
|
+
"const REPO_SCOPE=String('{{repoScope}}'||'auto').trim();",
|
|
722
|
+
"const FIELDS='number,title,headRefName,baseRefName,isDraft,mergeable,statusCheckRollup,labels,url';",
|
|
723
|
+
"const FAIL_STATES=new Set(['FAILURE','ERROR','TIMED_OUT','CANCELLED','STARTUP_FAILURE']);",
|
|
724
|
+
"const PEND_STATES=new Set(['PENDING','IN_PROGRESS','QUEUED','WAITING','REQUESTED','EXPECTED']);",
|
|
725
|
+
"const CONFLICT_MERGEABLES=new Set(['CONFLICTING','BEHIND','DIRTY']);",
|
|
726
|
+
"function ghJson(args){const out=execFileSync('gh',args,{encoding:'utf8',stdio:['pipe','pipe','pipe']}).trim();return out?JSON.parse(out):[];}",
|
|
727
|
+
"function configPath(){",
|
|
728
|
+
" const home=String(process.env.BOSUN_HOME||process.env.VK_PROJECT_DIR||'').trim();",
|
|
729
|
+
" return home?path.join(home,'bosun.config.json'):path.join(process.cwd(),'bosun.config.json');",
|
|
730
|
+
"}",
|
|
731
|
+
"function collectReposFromConfig(){",
|
|
732
|
+
" const repos=[];",
|
|
733
|
+
" try{",
|
|
734
|
+
" const cfg=JSON.parse(fs.readFileSync(configPath(),'utf8'));",
|
|
735
|
+
" const workspaces=Array.isArray(cfg?.workspaces)?cfg.workspaces:[];",
|
|
736
|
+
" if(workspaces.length>0){",
|
|
737
|
+
" const active=String(cfg?.activeWorkspace||'').trim().toLowerCase();",
|
|
738
|
+
" const activeWs=active?workspaces.find(w=>String(w?.id||'').trim().toLowerCase()===active):null;",
|
|
739
|
+
" const wsList=activeWs?[activeWs]:workspaces;",
|
|
740
|
+
" for(const ws of wsList){",
|
|
741
|
+
" for(const repo of (Array.isArray(ws?.repos)?ws.repos:[])){",
|
|
742
|
+
" const slug=typeof repo==='string'?String(repo).trim():String(repo?.slug||'').trim();",
|
|
743
|
+
" if(slug) repos.push(slug);",
|
|
744
|
+
" }",
|
|
741
745
|
" }",
|
|
742
|
-
" }
|
|
743
|
-
"
|
|
744
|
-
"
|
|
745
|
-
"
|
|
746
|
-
"
|
|
746
|
+
" }",
|
|
747
|
+
" if(repos.length===0){",
|
|
748
|
+
" for(const repo of (Array.isArray(cfg?.repos)?cfg.repos:[])){",
|
|
749
|
+
" const slug=typeof repo==='string'?String(repo).trim():String(repo?.slug||'').trim();",
|
|
750
|
+
" if(slug) repos.push(slug);",
|
|
747
751
|
" }",
|
|
748
|
-
" } else if(hasPend){",
|
|
749
|
-
" pending.push(pr.number);",
|
|
750
|
-
" } else if(checks.length>0&&!hasFixLabel){",
|
|
751
|
-
" /* CI all-passing, no conflicts, not draft — a review candidate */",
|
|
752
|
-
" readyCandidates.push({n:pr.number,branch:pr.headRefName,base:pr.baseRefName,url:pr.url,title:pr.title});",
|
|
753
752
|
" }",
|
|
753
|
+
" }catch{}",
|
|
754
|
+
" return repos;",
|
|
755
|
+
"}",
|
|
756
|
+
"function resolveRepoTargets(){",
|
|
757
|
+
" if(REPO_SCOPE&&REPO_SCOPE!=='auto'&&REPO_SCOPE!=='all'&&REPO_SCOPE!=='current'){",
|
|
758
|
+
" return [...new Set(REPO_SCOPE.split(',').map(v=>v.trim()).filter(Boolean))];",
|
|
759
|
+
" }",
|
|
760
|
+
" if(REPO_SCOPE==='current') return [''];",
|
|
761
|
+
" const fromConfig=collectReposFromConfig();",
|
|
762
|
+
" if(fromConfig.length>0) return [...new Set(fromConfig)];",
|
|
763
|
+
" const envRepo=String(process.env.GITHUB_REPOSITORY||'').trim();",
|
|
764
|
+
" if(envRepo) return [envRepo];",
|
|
765
|
+
" return [''];",
|
|
766
|
+
"}",
|
|
767
|
+
"function parseRepoFromUrl(url){",
|
|
768
|
+
" const raw=String(url||'');",
|
|
769
|
+
" const marker='github.com/';",
|
|
770
|
+
" const idx=raw.toLowerCase().indexOf(marker);",
|
|
771
|
+
" if(idx<0) return '';",
|
|
772
|
+
" const tail=raw.slice(idx+marker.length).split('/');",
|
|
773
|
+
" if(tail.length<2) return '';",
|
|
774
|
+
" const owner=String(tail[0]||'').trim();",
|
|
775
|
+
" const repo=String(tail[1]||'').trim();",
|
|
776
|
+
" return owner&&repo?(owner+'/'+repo):'';",
|
|
777
|
+
"}",
|
|
778
|
+
"const repoTargets=resolveRepoTargets();",
|
|
779
|
+
"const prs=[];",
|
|
780
|
+
"const repoErrors=[];",
|
|
781
|
+
"for(const target of repoTargets){",
|
|
782
|
+
" const repo=String(target||'').trim();",
|
|
783
|
+
" const args=['pr','list','--label','bosun-attached','--state','open','--json',FIELDS,'--limit',String(MAX_PRS)];",
|
|
784
|
+
" if(repo) args.push('--repo',repo);",
|
|
785
|
+
" try{",
|
|
786
|
+
" const list=ghJson(args);",
|
|
787
|
+
" for(const pr of (Array.isArray(list)?list:[])){",
|
|
788
|
+
" const prRepo=repo||parseRepoFromUrl(pr?.url)||String(process.env.GITHUB_REPOSITORY||'').trim();",
|
|
789
|
+
" prs.push({...pr,__repo:prRepo});",
|
|
790
|
+
" }",
|
|
791
|
+
" }catch(e){",
|
|
792
|
+
" repoErrors.push({repo:repo||'current',error:String(e?.message||e)});",
|
|
793
|
+
" }",
|
|
794
|
+
"}",
|
|
795
|
+
"const readyCandidates=[],conflicts=[],ciFailures=[],pending=[],drafted=[];",
|
|
796
|
+
"let newlyLabeled=0;",
|
|
797
|
+
"for(const pr of prs){",
|
|
798
|
+
" const labels=(pr.labels||[]).map(l=>typeof l==='string'?l:l?.name).filter(Boolean);",
|
|
799
|
+
" const hasFixLabel=labels.includes(LABEL_FIX);",
|
|
800
|
+
" const checks=pr.statusCheckRollup||[];",
|
|
801
|
+
" const hasFail=checks.some(c=>FAIL_STATES.has(c.conclusion||c.state||''));",
|
|
802
|
+
" const hasPend=checks.some(c=>PEND_STATES.has(c.conclusion||c.state||''));",
|
|
803
|
+
" const isConflict=CONFLICT_MERGEABLES.has(String(pr.mergeable||'').toUpperCase());",
|
|
804
|
+
" const isDraft=pr.isDraft===true;",
|
|
805
|
+
" const repo=String(pr.__repo||'').trim();",
|
|
806
|
+
" if(isDraft){drafted.push({n:pr.number,repo});continue;}",
|
|
807
|
+
" if(isConflict){",
|
|
808
|
+
" conflicts.push({n:pr.number,repo,branch:pr.headRefName,base:pr.baseRefName,url:pr.url});",
|
|
809
|
+
" if(!hasFixLabel){",
|
|
810
|
+
" try{const editArgs=['pr','edit',String(pr.number),'--add-label',LABEL_FIX];if(repo)editArgs.push('--repo',repo);execFileSync('gh',editArgs,{encoding:'utf8',stdio:['pipe','pipe','pipe']});newlyLabeled++;}",
|
|
811
|
+
" catch(e){process.stderr.write('label err '+(repo?repo+' ':'')+'#'+pr.number+': '+(e?.message||e)+'\\\\n');}",
|
|
812
|
+
" }",
|
|
813
|
+
" } else if(hasFail){",
|
|
814
|
+
" ciFailures.push({n:pr.number,repo,branch:pr.headRefName,url:pr.url});",
|
|
815
|
+
" if(!hasFixLabel){",
|
|
816
|
+
" try{const editArgs=['pr','edit',String(pr.number),'--add-label',LABEL_FIX];if(repo)editArgs.push('--repo',repo);execFileSync('gh',editArgs,{encoding:'utf8',stdio:['pipe','pipe','pipe']});newlyLabeled++;}",
|
|
817
|
+
" catch(e){process.stderr.write('label err '+(repo?repo+' ':'')+'#'+pr.number+': '+(e?.message||e)+'\\\\n');}",
|
|
818
|
+
" }",
|
|
819
|
+
" } else if(hasPend){",
|
|
820
|
+
" pending.push({n:pr.number,repo});",
|
|
821
|
+
" } else if(checks.length>0&&!hasFixLabel){",
|
|
822
|
+
" readyCandidates.push({n:pr.number,repo,branch:pr.headRefName,base:pr.baseRefName,url:pr.url,title:pr.title});",
|
|
754
823
|
" }",
|
|
755
|
-
"
|
|
756
|
-
"
|
|
757
|
-
"
|
|
758
|
-
"
|
|
759
|
-
"
|
|
760
|
-
"
|
|
761
|
-
"
|
|
762
|
-
"
|
|
763
|
-
"
|
|
764
|
-
"
|
|
765
|
-
"
|
|
824
|
+
"}",
|
|
825
|
+
"console.log(JSON.stringify({",
|
|
826
|
+
" total:prs.length,",
|
|
827
|
+
" reposScanned:repoTargets.length,",
|
|
828
|
+
" repoErrors,",
|
|
829
|
+
" readyCandidates,",
|
|
830
|
+
" conflicts,",
|
|
831
|
+
" ciFailures,",
|
|
832
|
+
" pending:pending.length,",
|
|
833
|
+
" drafted:drafted.length,",
|
|
834
|
+
" newlyLabeled,",
|
|
835
|
+
" fixNeeded:conflicts.length+ciFailures.length",
|
|
836
|
+
"}));",
|
|
766
837
|
"\"",
|
|
767
838
|
].join(" "),
|
|
768
839
|
continueOnError: false,
|
|
@@ -795,8 +866,9 @@ export const BOSUN_PR_WATCHDOG_TEMPLATE = {
|
|
|
795
866
|
prompt:
|
|
796
867
|
"You are a Bosun PR repair agent. A watchdog workflow has identified " +
|
|
797
868
|
"bosun-attached PRs that need fixing.\n\n" +
|
|
798
|
-
"
|
|
799
|
-
"
|
|
869
|
+
"For EACH target repo, list PRs needing work. Always include --repo <owner/repo>.\n" +
|
|
870
|
+
"Example:\n" +
|
|
871
|
+
" gh pr list --repo <owner/repo> --label bosun-needs-fix --label bosun-attached --state open \\\n" +
|
|
800
872
|
" --json number,title,headRefName,baseRefName,mergeable,statusCheckRollup,labels,url \\\n" +
|
|
801
873
|
" --limit 10\n\n" +
|
|
802
874
|
"For each PR returned, follow the appropriate path:\n\n" +
|
|
@@ -809,13 +881,13 @@ export const BOSUN_PR_WATCHDOG_TEMPLATE = {
|
|
|
809
881
|
"6. git push --force-with-lease origin <headRefName>\n\n" +
|
|
810
882
|
"CI FAILURE (statusCheckRollup has FAILURE/ERROR/TIMED_OUT entries):\n" +
|
|
811
883
|
"1. git checkout <headRefName>\n" +
|
|
812
|
-
"2. Inspect CI logs: gh run list --branch <headRefName> --limit 3\n" +
|
|
813
|
-
" Then: gh run view <run-id> --log-failed\n" +
|
|
884
|
+
"2. Inspect CI logs: gh run list --repo <owner/repo> --branch <headRefName> --limit 3\n" +
|
|
885
|
+
" Then: gh run view <run-id> --repo <owner/repo> --log-failed\n" +
|
|
814
886
|
"3. Fix the root cause (lint, type error, failing test, build error).\n" +
|
|
815
887
|
"4. Commit: fix(<scope>): <description> (conventional commit format)\n" +
|
|
816
888
|
"5. Push the branch — CI will re-trigger automatically.\n\n" +
|
|
817
889
|
"AFTER fixing either type:\n" +
|
|
818
|
-
"- Remove the bosun-needs-fix label: gh pr edit <number> --remove-label bosun-needs-fix\n\n" +
|
|
890
|
+
"- Remove the bosun-needs-fix label: gh pr edit <number> --repo <owner/repo> --remove-label bosun-needs-fix\n\n" +
|
|
819
891
|
"STRICT RULES:\n" +
|
|
820
892
|
"- Fix only what breaks CI or causes the conflict. No scope creep.\n" +
|
|
821
893
|
"- Do NOT merge, close, or approve any PR.\n" +
|
|
@@ -847,33 +919,33 @@ export const BOSUN_PR_WATCHDOG_TEMPLATE = {
|
|
|
847
919
|
"any PR is merged. Your job is to inspect each merge candidate and decide " +
|
|
848
920
|
"whether it is safe to merge.\n\n" +
|
|
849
921
|
"MERGE CANDIDATES (CI passing, no conflicts, bosun-attached):\n" +
|
|
850
|
-
" Run: gh pr list --label bosun-attached --state open \\\n" +
|
|
922
|
+
" Run per target repo: gh pr list --repo <owner/repo> --label bosun-attached --state open \\\n" +
|
|
851
923
|
" --json number,title,headRefName,isDraft,statusCheckRollup,labels,url \\\n" +
|
|
852
924
|
" --limit {{maxPrs}}\n" +
|
|
853
925
|
" Filter to PRs where: not isDraft, CI all-passing, no bosun-needs-fix label.\n\n" +
|
|
854
926
|
"FOR EACH CANDIDATE — before merging, run:\n" +
|
|
855
|
-
" gh pr view <number> --json number,title,additions,deletions,changedFiles,body,baseRefName\n\n" +
|
|
927
|
+
" gh pr view <number> --repo <owner/repo> --json number,title,additions,deletions,changedFiles,body,baseRefName\n\n" +
|
|
856
928
|
"SAFETY CHECKS (ALL must pass before merging):\n\n" +
|
|
857
929
|
"1. DESTRUCTIVE DIFF CHECK:\n" +
|
|
858
930
|
" If (deletions > additions × {{suspiciousDeletionRatio}}) AND (deletions > {{minDestructiveDeletions}}):\n" +
|
|
859
931
|
" → This PR deletes far more than it adds — HOLD IT.\n" +
|
|
860
|
-
" → Run: gh pr edit <number> --add-label {{labelNeedsReview}}\n" +
|
|
861
|
-
" → Run: gh pr comment <number> --body '
|
|
932
|
+
" → Run: gh pr edit <number> --repo <owner/repo> --add-label {{labelNeedsReview}}\n" +
|
|
933
|
+
" → Run: gh pr comment <number> --repo <owner/repo> --body ':alert: **Bosun Review Agent: merge held** — " +
|
|
862
934
|
"This PR deletes significantly more lines than it adds (deletions: <X>, additions: <Y>). " +
|
|
863
935
|
"A human should verify this is intentional before merging.'\n" +
|
|
864
936
|
" → Do NOT merge this PR. Move to next candidate.\n\n" +
|
|
865
937
|
"2. DIFF SANITY CHECK (for PRs that pass the ratio check):\n" +
|
|
866
|
-
" Run: gh pr diff <number> | head -200\n" +
|
|
938
|
+
" Run: gh pr diff <number> --repo <owner/repo> | head -200\n" +
|
|
867
939
|
" Look for: mass file deletions, removal of entire modules/directories, " +
|
|
868
940
|
" files changed that are unrelated to the PR description.\n" +
|
|
869
941
|
" If something looks wrong → HOLD with bosun-needs-human-review label + comment.\n\n" +
|
|
870
942
|
"3. CI STATUS RECONFIRM:\n" +
|
|
871
|
-
" Run: gh pr checks <number> --json name,state,conclusion\n" +
|
|
943
|
+
" Run: gh pr checks <number> --repo <owner/repo> --json name,state,conclusion\n" +
|
|
872
944
|
" Ensure ALL checks have conclusion SUCCESS/SKIPPED/NEUTRAL. " +
|
|
873
945
|
" If any are pending or failing → do NOT merge (CI may still be running).\n\n" +
|
|
874
946
|
"MERGE (only if ALL checks pass):\n" +
|
|
875
|
-
" gh pr merge <number> --{{mergeMethod}} --delete-branch\n" +
|
|
876
|
-
" Log:
|
|
947
|
+
" gh pr merge <number> --repo <owner/repo> --{{mergeMethod}} --delete-branch\n" +
|
|
948
|
+
" Log: :check: Merged PR #<number> — <title>\n\n" +
|
|
877
949
|
"STRICT RULES:\n" +
|
|
878
950
|
"- NEVER merge if ANY safety check fails. When in doubt, HOLD.\n" +
|
|
879
951
|
"- NEVER merge PRs without the bosun-attached label.\n" +
|
|
@@ -889,7 +961,7 @@ export const BOSUN_PR_WATCHDOG_TEMPLATE = {
|
|
|
889
961
|
|
|
890
962
|
node("notify", "notify.telegram", "Watchdog Report", {
|
|
891
963
|
message:
|
|
892
|
-
"
|
|
964
|
+
":bug: Bosun PR Watchdog cycle complete — " +
|
|
893
965
|
"fix-dispatched: {{fixNeeded}} | candidates-reviewed: {{readyCandidates}}",
|
|
894
966
|
silent: true,
|
|
895
967
|
}, { x: 400, y: 900 }),
|
|
@@ -915,16 +987,16 @@ export const BOSUN_PR_WATCHDOG_TEMPLATE = {
|
|
|
915
987
|
],
|
|
916
988
|
metadata: {
|
|
917
989
|
author: "bosun",
|
|
918
|
-
version:
|
|
990
|
+
version: 3,
|
|
919
991
|
createdAt: "2025-07-01T00:00:00Z",
|
|
920
|
-
templateVersion: "2.
|
|
992
|
+
templateVersion: "2.1.0",
|
|
921
993
|
tags: ["github", "pr", "ci", "merge", "watchdog", "bosun-attached", "safety"],
|
|
922
994
|
replaces: {
|
|
923
995
|
module: "agent-hooks.mjs",
|
|
924
996
|
functions: ["registerBuiltinHooks (PostPR block)"],
|
|
925
997
|
calledFrom: [],
|
|
926
998
|
description:
|
|
927
|
-
"v2: Consolidates
|
|
999
|
+
"v2: Consolidates PR polling into one gh pr list fetch per target repo per cycle. " +
|
|
928
1000
|
"Adds mandatory review gate agent that checks diff stats (additions/deletions " +
|
|
929
1001
|
"ratio) and diff content before any merge — preventing destructive PRs from " +
|
|
930
1002
|
"being auto-merged. Adds conflict detection via the 'mergeable' field. " +
|
|
@@ -77,7 +77,7 @@ export const TASK_PLANNER_TEMPLATE = {
|
|
|
77
77
|
}, { x: 200, y: 830 }),
|
|
78
78
|
|
|
79
79
|
node("notify-done", "notify.telegram", "Notify Tasks Created", {
|
|
80
|
-
message: "
|
|
80
|
+
message: ":folder: Task planner created {{materialize-tasks.createdCount}} backlog tasks (skipped {{materialize-tasks.skippedCount}} duplicates).",
|
|
81
81
|
silent: true,
|
|
82
82
|
}, { x: 200, y: 960 }),
|
|
83
83
|
|
|
@@ -171,7 +171,7 @@ export const TASK_REPLENISH_TEMPLATE = {
|
|
|
171
171
|
}, { x: 400, y: 570 }),
|
|
172
172
|
|
|
173
173
|
node("notify", "notify.telegram", "Notify", {
|
|
174
|
-
message: "
|
|
174
|
+
message: ":refresh: Scheduled replenishment created {{materialize-tasks.createdCount}} tasks (skipped {{materialize-tasks.skippedCount}}).",
|
|
175
175
|
silent: true,
|
|
176
176
|
}, { x: 400, y: 700 }),
|
|
177
177
|
|
|
@@ -265,7 +265,7 @@ Format as a Telegram-friendly message with emoji headers. Include:
|
|
|
265
265
|
}, { x: 400, y: 380 }),
|
|
266
266
|
|
|
267
267
|
node("send-report", "notify.telegram", "Send Report", {
|
|
268
|
-
message: "
|
|
268
|
+
message: ":chart: **Daily Bosun Report** ({{reportTimezone}})\n\n{{reportOutput}}",
|
|
269
269
|
}, { x: 400, y: 540 }),
|
|
270
270
|
],
|
|
271
271
|
edges: [
|
|
@@ -360,19 +360,19 @@ Analyze the following data from the past {{lookbackDays}} days:
|
|
|
360
360
|
|
|
361
361
|
Generate a retrospective report with these sections:
|
|
362
362
|
|
|
363
|
-
###
|
|
363
|
+
### :chart: Key Metrics
|
|
364
364
|
- Tasks completed vs created
|
|
365
365
|
- Average task cycle time
|
|
366
366
|
- PR merge rate and average review time
|
|
367
367
|
- Agent success rate
|
|
368
368
|
|
|
369
|
-
###
|
|
369
|
+
### :check: What Went Well
|
|
370
370
|
- Highlight successful patterns and wins
|
|
371
371
|
|
|
372
|
-
###
|
|
372
|
+
### :close: What Didn't Go Well
|
|
373
373
|
- Identify bottlenecks and recurring issues
|
|
374
374
|
|
|
375
|
-
###
|
|
375
|
+
### :target: Action Items
|
|
376
376
|
For each improvement suggestion, output a line:
|
|
377
377
|
ACTION: [title] | [description]
|
|
378
378
|
|
|
@@ -399,7 +399,7 @@ Only create tasks if {{createImprovementTasks}} is true.`,
|
|
|
399
399
|
}, { x: 250, y: 830 }),
|
|
400
400
|
|
|
401
401
|
node("send-report", "notify.telegram", "Send Retro Report", {
|
|
402
|
-
message: "
|
|
402
|
+
message: ":clipboard: **Weekly Retrospective** (past {{lookbackDays}} days)\n\n{{retroOutput}}",
|
|
403
403
|
}, { x: 400, y: 980 }),
|
|
404
404
|
|
|
405
405
|
node("log-no-actions", "notify.log", "No Actions Needed", {
|
|
@@ -65,7 +65,7 @@ export const ERROR_RECOVERY_TEMPLATE = {
|
|
|
65
65
|
}, { x: 90, y: 760 }),
|
|
66
66
|
|
|
67
67
|
node("escalate", "notify.telegram", "Escalate to Human", {
|
|
68
|
-
message: "
|
|
68
|
+
message: ":alert: Task **{{taskTitle}}** failed after {{maxRetries}} attempts. Manual intervention needed.\n\nLast error: {{lastError}}",
|
|
69
69
|
}, { x: 600, y: 620 }),
|
|
70
70
|
],
|
|
71
71
|
edges: [
|
|
@@ -167,7 +167,7 @@ export const ANOMALY_WATCHDOG_TEMPLATE = {
|
|
|
167
167
|
}, { x: 400, y: 550 }),
|
|
168
168
|
|
|
169
169
|
node("alert-telegram", "notify.telegram", "Alert Human", {
|
|
170
|
-
message: "
|
|
170
|
+
message: ":alert: Agent anomaly detected: **{{anomalyType}}**\nSession: {{sessionId}}\nTask: {{taskTitle}}\nIntervention: auto-applied\nThresholds: stall={{stallThresholdMs}}ms token={{maxTokenUsage}} maxErrors={{maxConsecutiveErrors}}",
|
|
171
171
|
}, { x: 400, y: 700 }),
|
|
172
172
|
],
|
|
173
173
|
edges: [
|
|
@@ -334,7 +334,7 @@ export const HEALTH_CHECK_TEMPLATE = {
|
|
|
334
334
|
}, { x: 400, y: 380 }),
|
|
335
335
|
|
|
336
336
|
node("alert", "notify.telegram", "Alert Issues Found", {
|
|
337
|
-
message: "
|
|
337
|
+
message: ":heart: Health check found issues — run `bosun doctor` for details",
|
|
338
338
|
}, { x: 200, y: 540 }),
|
|
339
339
|
|
|
340
340
|
node("all-ok", "notify.telegram", "All Healthy", {
|
|
@@ -480,7 +480,7 @@ export const TASK_FINALIZATION_GUARD_TEMPLATE = {
|
|
|
480
480
|
}, { x: 240, y: 1040 }),
|
|
481
481
|
|
|
482
482
|
node("notify-fail", "notify.telegram", "Notify Finalization Failure", {
|
|
483
|
-
message: "
|
|
483
|
+
message: ":alert: Task finalization failed for **{{taskTitle}}** ({{taskId}}). Repair workflow handoff triggered.",
|
|
484
484
|
}, { x: 540, y: 900 }),
|
|
485
485
|
],
|
|
486
486
|
edges: [
|
|
@@ -621,12 +621,12 @@ export const TASK_REPAIR_WORKTREE_TEMPLATE = {
|
|
|
621
621
|
}, { x: 560, y: 880 }),
|
|
622
622
|
|
|
623
623
|
node("notify-success", "notify.telegram", "Notify Repair Success", {
|
|
624
|
-
message: "
|
|
624
|
+
message: ":check: Repair workflow recovered **{{taskTitle}}** ({{taskId}}) and moved it to inreview.",
|
|
625
625
|
silent: true,
|
|
626
626
|
}, { x: 250, y: 1160 }),
|
|
627
627
|
|
|
628
628
|
node("notify-escalate", "notify.telegram", "Escalate Repair Failure", {
|
|
629
|
-
message: "
|
|
629
|
+
message: ":alert: Repair workflow could not recover **{{taskTitle}}** ({{taskId}}). Manual intervention required.",
|
|
630
630
|
}, { x: 560, y: 1020 }),
|
|
631
631
|
|
|
632
632
|
node("no-worktree", "notify.log", "Missing Worktree Context", {
|
|
@@ -827,7 +827,7 @@ Output as JSON: { "severity": "...", "category": "...", "rootCause": "...", "imp
|
|
|
827
827
|
}, { x: 400, y: 540, outputs: ["yes", "no"] }),
|
|
828
828
|
|
|
829
829
|
node("create-incident-task", "action.create_task", "Create Incident Task", {
|
|
830
|
-
title: "
|
|
830
|
+
title: ":alert: Incident: {{incidentCategory}}",
|
|
831
831
|
description: "Auto-detected incident.\n\nEvidence: {{evidence}}\n\nClassification: {{classification}}",
|
|
832
832
|
tags: ["incident", "auto-detected"],
|
|
833
833
|
priority: "high",
|
|
@@ -854,7 +854,7 @@ Be conservative — prefer safe mitigations over aggressive fixes.`,
|
|
|
854
854
|
}, { x: 250, y: 840 }),
|
|
855
855
|
|
|
856
856
|
node("alert-critical", "notify.telegram", "Critical Incident Alert", {
|
|
857
|
-
message: "
|
|
857
|
+
message: ":alert: **CRITICAL INCIDENT**\n\nCategory: {{incidentCategory}}\nRoot cause: {{rootCause}}\n\nAgent investigating. Immediate attention may be required.",
|
|
858
858
|
}, { x: 150, y: 540 }),
|
|
859
859
|
|
|
860
860
|
node("alert-standard", "notify.log", "Log Incident", {
|
|
@@ -87,11 +87,11 @@ Limit auto-generated fix PRs to {{maxAutoFixPRs}} in this run.`,
|
|
|
87
87
|
}, { x: 50, y: 900 }),
|
|
88
88
|
|
|
89
89
|
node("alert-critical", "notify.telegram", "Alert: Critical Vuln", {
|
|
90
|
-
message: "
|
|
90
|
+
message: ":alert: **CRITICAL vulnerability** found in dependencies!\n\nRun `npm audit` for details. Manual review required.",
|
|
91
91
|
}, { x: 350, y: 750 }),
|
|
92
92
|
|
|
93
93
|
node("alert-high", "notify.telegram", "Alert: High Severity", {
|
|
94
|
-
message: "
|
|
94
|
+
message: ":alert: **High severity** dependency vulnerability detected.\n\nAuto-fix changes prepared and handed off to Bosun PR lifecycle management. Please review and merge when ready.",
|
|
95
95
|
silent: true,
|
|
96
96
|
}, { x: 550, y: 750 }),
|
|
97
97
|
|
|
@@ -201,7 +201,7 @@ Respond as JSON: { "findings": [{ "type": "...", "severity": "critical|high|low"
|
|
|
201
201
|
}, { x: 200, y: 650, outputs: ["yes", "no"] }),
|
|
202
202
|
|
|
203
203
|
node("alert-secret", "notify.telegram", "Alert: Secret Exposed!", {
|
|
204
|
-
message: "
|
|
204
|
+
message: ":lock: **SECRET EXPOSED** in recent commits!\n\nThe secret scanner found credentials in the repository. Immediate rotation required.\n\nCheck agent output for classification details.",
|
|
205
205
|
}, { x: 100, y: 810 }),
|
|
206
206
|
|
|
207
207
|
node("create-issue", "action.run_command", "Create Remediation Issue", {
|
package/workflow-templates.mjs
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Templates are split into category modules for easy extension:
|
|
9
9
|
* workflow-templates/github.mjs — PR Merge Strategy, Triage, Conflict Resolver, Stale Reaper, Release Drafter
|
|
10
|
-
* workflow-templates/agents.mjs — Frontend Agent, Review Agent, Custom Agent, Session Monitor, Backend Agent
|
|
10
|
+
* workflow-templates/agents.mjs — Frontend Agent, Review Agent, Custom Agent, Session Monitor, Backend Agent, Meeting Orchestrator + Subworkflow Chain
|
|
11
11
|
* workflow-templates/planning.mjs — Task Planner, Task Replenish, Nightly Report, Sprint Retrospective
|
|
12
12
|
* workflow-templates/ci-cd.mjs — Build & Deploy, Release Pipeline, Canary Deploy
|
|
13
13
|
* workflow-templates/reliability.mjs — Error Recovery, Anomaly Watchdog, Workspace Hygiene, Health Check, Task Finalization Guard, Task Repair Worktree, Incident Response
|
|
@@ -53,6 +53,8 @@ import {
|
|
|
53
53
|
CUSTOM_AGENT_TEMPLATE,
|
|
54
54
|
AGENT_SESSION_MONITOR_TEMPLATE,
|
|
55
55
|
BACKEND_AGENT_TEMPLATE,
|
|
56
|
+
VOICE_VIDEO_PARALLEL_ROLLOUT_TEMPLATE,
|
|
57
|
+
MEETING_SUBWORKFLOW_CHAIN_TEMPLATE,
|
|
56
58
|
} from "./workflow-templates/agents.mjs";
|
|
57
59
|
|
|
58
60
|
// Planning
|
|
@@ -102,6 +104,8 @@ export {
|
|
|
102
104
|
CUSTOM_AGENT_TEMPLATE,
|
|
103
105
|
AGENT_SESSION_MONITOR_TEMPLATE,
|
|
104
106
|
BACKEND_AGENT_TEMPLATE,
|
|
107
|
+
VOICE_VIDEO_PARALLEL_ROLLOUT_TEMPLATE,
|
|
108
|
+
MEETING_SUBWORKFLOW_CHAIN_TEMPLATE,
|
|
105
109
|
TASK_PLANNER_TEMPLATE,
|
|
106
110
|
TASK_REPLENISH_TEMPLATE,
|
|
107
111
|
NIGHTLY_REPORT_TEMPLATE,
|
|
@@ -127,13 +131,13 @@ export {
|
|
|
127
131
|
|
|
128
132
|
/** Category metadata for UI grouping. */
|
|
129
133
|
export const TEMPLATE_CATEGORIES = Object.freeze({
|
|
130
|
-
github: { label: "GitHub", icon: "
|
|
131
|
-
agents: { label: "Agents", icon: "
|
|
132
|
-
planning: { label: "Planning", icon: "
|
|
133
|
-
"ci-cd": { label: "CI / CD", icon: "
|
|
134
|
-
reliability: { label: "Reliability", icon: "
|
|
135
|
-
security: { label: "Security", icon: "
|
|
136
|
-
custom: { label: "Custom", icon: "
|
|
134
|
+
github: { label: "GitHub", icon: ":git:", order: 1 },
|
|
135
|
+
agents: { label: "Agents", icon: ":bot:", order: 2 },
|
|
136
|
+
planning: { label: "Planning", icon: ":clipboard:", order: 3 },
|
|
137
|
+
"ci-cd": { label: "CI / CD", icon: ":refresh:", order: 4 },
|
|
138
|
+
reliability: { label: "Reliability", icon: ":shield:", order: 5 },
|
|
139
|
+
security: { label: "Security", icon: ":lock:", order: 6 },
|
|
140
|
+
custom: { label: "Custom", icon: ":settings:", order: 7 },
|
|
137
141
|
});
|
|
138
142
|
|
|
139
143
|
export const WORKFLOW_TEMPLATES = Object.freeze([
|
|
@@ -150,6 +154,8 @@ export const WORKFLOW_TEMPLATES = Object.freeze([
|
|
|
150
154
|
CUSTOM_AGENT_TEMPLATE,
|
|
151
155
|
AGENT_SESSION_MONITOR_TEMPLATE,
|
|
152
156
|
BACKEND_AGENT_TEMPLATE,
|
|
157
|
+
VOICE_VIDEO_PARALLEL_ROLLOUT_TEMPLATE,
|
|
158
|
+
MEETING_SUBWORKFLOW_CHAIN_TEMPLATE,
|
|
153
159
|
// ── Planning ──
|
|
154
160
|
TASK_PLANNER_TEMPLATE,
|
|
155
161
|
TASK_REPLENISH_TEMPLATE,
|
|
@@ -442,7 +448,7 @@ export const WORKFLOW_SETUP_PROFILES = Object.freeze({
|
|
|
442
448
|
workflowAutomationEnabled: true,
|
|
443
449
|
templateIds: Object.freeze([
|
|
444
450
|
"template-pr-merge-strategy",
|
|
445
|
-
"template-pr-
|
|
451
|
+
"template-bosun-pr-watchdog",
|
|
446
452
|
"template-review-agent",
|
|
447
453
|
"template-backend-agent",
|
|
448
454
|
"template-task-planner",
|