bosun 0.40.7 → 0.40.9
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/README.md +31 -1
- package/agent/agent-endpoint.mjs +58 -32
- package/agent/agent-pool.mjs +9 -1
- package/agent/autofix.mjs +14 -17
- package/agent/bosun-skills.mjs +7 -112
- package/agent/skills/skill-codebase-audit.md +111 -0
- package/bosun-tui.mjs +141 -0
- package/cli.mjs +49 -9
- package/config/config.mjs +6 -2
- package/config/repo-root.mjs +28 -0
- package/desktop/main.mjs +14 -2
- package/git/git-safety.mjs +1 -1
- package/git/sdk-conflict-resolver.mjs +18 -22
- package/github/github-oauth-portal.mjs +24 -11
- package/infra/container-runner.mjs +26 -16
- package/infra/library-manager.mjs +43 -4
- package/infra/monitor.mjs +205 -39
- package/infra/runtime-accumulator.mjs +206 -0
- package/infra/session-tracker.mjs +18 -0
- package/infra/tracing.mjs +293 -0
- package/kanban/kanban-adapter.mjs +32 -17
- package/kanban/vk-log-stream.mjs +21 -6
- package/lib/codebase-audit.mjs +807 -0
- package/lib/session-insights.mjs +78 -33
- package/package.json +18 -3
- package/server/setup-web-server.mjs +12 -7
- package/server/ui-server.mjs +378 -50
- package/setup.mjs +13 -11
- package/shell/codex-config.mjs +9 -1
- package/shell/codex-model-profiles.mjs +9 -1
- package/shell/codex-shell.mjs +11 -3
- package/task/task-executor.mjs +40 -12
- package/task/task-store.mjs +47 -5
- package/tools/prepublish-check.mjs +269 -10
- package/tui/app.mjs +160 -0
- package/tui/components/status-header.mjs +145 -0
- package/tui/lib/ws-bridge.mjs +193 -0
- package/tui/screens/agents.mjs +294 -0
- package/tui/screens/status.mjs +197 -0
- package/tui/screens/tasks.mjs +260 -0
- package/ui/app.legacy.js +13 -1464
- package/ui/demo-defaults.js +1643 -64
- package/ui/demo.html +4 -2
- package/ui/index.html +2 -2
- package/ui/modules/state.js +13 -0
- package/ui/tabs/dashboard.js +40 -399
- package/ui/vendor/es-module-shims.js +1172 -753
- package/voice/voice-action-dispatcher.mjs +3 -2
- package/voice/voice-auth-manager.mjs +10 -1
- package/voice/voice-relay.mjs +1 -1
- package/voice/voice-tools.mjs +92 -32
- package/workflow/manual-flows.mjs +10 -8
- package/workflow/pipeline.mjs +319 -0
- package/workflow/workflow-contract.mjs +242 -0
- package/workflow/workflow-engine.mjs +17 -2
- package/workflow/workflow-nodes.mjs +292 -45
- package/workflow/workflow-templates.mjs +11 -1
- package/workflow-templates/code-quality.mjs +307 -0
- package/workflow-templates/issue-continuation.mjs +243 -0
- package/workspace/shared-state-manager.mjs +14 -7
- package/workspace/workspace-monitor.mjs +25 -10
- package/workspace/worktree-manager.mjs +12 -5
package/README.md
CHANGED
|
@@ -144,17 +144,47 @@ Key places to start:
|
|
|
144
144
|
|
|
145
145
|
Bosun enforces a strict quality pipeline in both local hooks and CI:
|
|
146
146
|
|
|
147
|
-
- **Pre-commit hooks**
|
|
147
|
+
- **Pre-commit hooks** run syntax checks and warn when staged source files are missing `CLAUDE:SUMMARY` annotations.
|
|
148
148
|
- **Pre-push hooks** run targeted checks based on changed files (Go, portal, docs).
|
|
149
149
|
- **Demo load smoke test** runs in `npm test` and blocks push if `site/index.html` or `site/ui/demo.html` fails to load required assets.
|
|
150
150
|
- **Prepublish checks** validate package contents and release readiness.
|
|
151
151
|
|
|
152
|
+
### Codebase annotation audit
|
|
153
|
+
|
|
154
|
+
Use `bosun audit` to generate and validate repo-level annotations that help future agents navigate the codebase without extra runtime context:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
# Coverage report for supported source files
|
|
158
|
+
bosun audit scan
|
|
159
|
+
|
|
160
|
+
# Add missing file summaries and risky-function warnings
|
|
161
|
+
bosun audit generate
|
|
162
|
+
bosun audit warn
|
|
163
|
+
|
|
164
|
+
# Rebuild lean manifests and the file responsibility index
|
|
165
|
+
bosun audit manifest
|
|
166
|
+
bosun audit index
|
|
167
|
+
bosun audit trim
|
|
168
|
+
|
|
169
|
+
# CI-safe conformity gate
|
|
170
|
+
bosun audit --ci
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Notes:
|
|
174
|
+
|
|
175
|
+
- `bosun audit --ci` exits non-zero on missing summaries, stale warnings, stale `INDEX.map` entries, or credential-like secrets.
|
|
176
|
+
- `.githooks/pre-commit` already warns on newly staged files that are missing `CLAUDE:SUMMARY`.
|
|
177
|
+
- GitHub Actions can opt into the audit gate by setting the repository variable `BOSUN_AUDIT_CI=1`.
|
|
178
|
+
|
|
152
179
|
Local commands you can run any time:
|
|
153
180
|
|
|
154
181
|
```bash
|
|
155
182
|
# Syntax + tests for bosun package
|
|
156
183
|
npm test
|
|
157
184
|
|
|
185
|
+
# Annotation conformity gate
|
|
186
|
+
npm run audit:ci
|
|
187
|
+
|
|
158
188
|
# Prepublish safety checks
|
|
159
189
|
npm run prepublishOnly
|
|
160
190
|
|
package/agent/agent-endpoint.mjs
CHANGED
|
@@ -358,7 +358,11 @@ export class AgentEndpoint {
|
|
|
358
358
|
*/
|
|
359
359
|
async _killProcessOnPort(port) {
|
|
360
360
|
try {
|
|
361
|
-
const {
|
|
361
|
+
const { spawnSync } = await import("node:child_process");
|
|
362
|
+
const portNumber = Number.parseInt(String(port), 10);
|
|
363
|
+
if (!Number.isInteger(portNumber) || portNumber <= 0 || portNumber > 65535) {
|
|
364
|
+
throw new Error(`invalid port: ${port}`);
|
|
365
|
+
}
|
|
362
366
|
const isWindows = process.platform === "win32";
|
|
363
367
|
let output;
|
|
364
368
|
const pids = new Set();
|
|
@@ -402,12 +406,29 @@ export class AgentEndpoint {
|
|
|
402
406
|
};
|
|
403
407
|
|
|
404
408
|
if (isWindows) {
|
|
405
|
-
// Windows: netstat -ano
|
|
406
|
-
|
|
409
|
+
// Windows: netstat -ano then filter in-process to avoid shell command injection.
|
|
410
|
+
const netstatRes = spawnSync("netstat", ["-ano"], {
|
|
407
411
|
encoding: "utf8",
|
|
408
412
|
timeout: 5000,
|
|
409
|
-
|
|
410
|
-
|
|
413
|
+
windowsHide: true,
|
|
414
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
415
|
+
});
|
|
416
|
+
if (netstatRes.error) throw netstatRes.error;
|
|
417
|
+
if (netstatRes.status !== 0) {
|
|
418
|
+
throw new Error(
|
|
419
|
+
String(
|
|
420
|
+
netstatRes.stderr ||
|
|
421
|
+
netstatRes.stdout ||
|
|
422
|
+
`netstat exited with status ${netstatRes.status}`,
|
|
423
|
+
).trim(),
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
output = String(netstatRes.stdout || "").trim();
|
|
427
|
+
const lines = output
|
|
428
|
+
.split("\n")
|
|
429
|
+
.filter(
|
|
430
|
+
(line) => line.includes("LISTENING") && line.includes(`:${portNumber}`),
|
|
431
|
+
);
|
|
411
432
|
for (const line of lines) {
|
|
412
433
|
const parts = line.trim().split(/\s+/);
|
|
413
434
|
const pid = parts[parts.length - 1];
|
|
@@ -417,29 +438,37 @@ export class AgentEndpoint {
|
|
|
417
438
|
}
|
|
418
439
|
} else {
|
|
419
440
|
// Linux/macOS: lsof -i
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
+
const lsofRes = spawnSync("lsof", ["-ti", `:${portNumber}`], {
|
|
442
|
+
encoding: "utf8",
|
|
443
|
+
timeout: 5000,
|
|
444
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
445
|
+
});
|
|
446
|
+
if (lsofRes.error) throw lsofRes.error;
|
|
447
|
+
// lsof returns exit code 1 when no processes found (port is free)
|
|
448
|
+
if (lsofRes.status === 1) {
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
if (lsofRes.status !== 0) {
|
|
452
|
+
throw new Error(
|
|
453
|
+
String(
|
|
454
|
+
lsofRes.stderr ||
|
|
455
|
+
lsofRes.stdout ||
|
|
456
|
+
`lsof exited with status ${lsofRes.status}`,
|
|
457
|
+
).trim(),
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
output = String(lsofRes.stdout || "").trim();
|
|
461
|
+
const pidList = output.split("\n").filter((pid) => pid.trim());
|
|
462
|
+
for (const pid of pidList) {
|
|
463
|
+
if (pid && /^\d+$/.test(pid) && !protectedPids.has(pid)) {
|
|
464
|
+
pids.add(pid);
|
|
441
465
|
}
|
|
442
|
-
|
|
466
|
+
}
|
|
467
|
+
if (pidList.length > 0 && pids.size === 0) {
|
|
468
|
+
console.log(
|
|
469
|
+
`${TAG} Port ${portNumber} held by own process tree (PIDs: ${pidList.join(", ")}) — skipping kill`,
|
|
470
|
+
);
|
|
471
|
+
return;
|
|
443
472
|
}
|
|
444
473
|
}
|
|
445
474
|
|
|
@@ -490,10 +519,7 @@ export class AgentEndpoint {
|
|
|
490
519
|
}
|
|
491
520
|
} else {
|
|
492
521
|
// Graceful SIGTERM first — only escalate to SIGKILL if still alive
|
|
493
|
-
|
|
494
|
-
encoding: "utf8",
|
|
495
|
-
timeout: 5000,
|
|
496
|
-
});
|
|
522
|
+
process.kill(Number(pid), "SIGTERM");
|
|
497
523
|
}
|
|
498
524
|
} catch (killErr) {
|
|
499
525
|
/* may already be dead — log for diagnostics */
|
|
@@ -528,7 +554,7 @@ export class AgentEndpoint {
|
|
|
528
554
|
try {
|
|
529
555
|
process.kill(Number(pid), 0); // probe — throws if dead
|
|
530
556
|
console.warn(`${TAG} PID ${pid} still alive after SIGTERM — sending SIGKILL`);
|
|
531
|
-
|
|
557
|
+
process.kill(Number(pid), "SIGKILL");
|
|
532
558
|
} catch {
|
|
533
559
|
/* already dead — good */
|
|
534
560
|
}
|
package/agent/agent-pool.mjs
CHANGED
|
@@ -682,7 +682,15 @@ async function withTemporaryEnv(overrides, fn) {
|
|
|
682
682
|
function buildCodexSdkOptions(envInput = process.env) {
|
|
683
683
|
const { env: resolvedEnv } = resolveCodexProfileRuntime(envInput);
|
|
684
684
|
const baseUrl = resolvedEnv.OPENAI_BASE_URL || "";
|
|
685
|
-
const isAzure =
|
|
685
|
+
const isAzure = (() => {
|
|
686
|
+
try {
|
|
687
|
+
const parsed = new URL(baseUrl);
|
|
688
|
+
const host = String(parsed.hostname || "").toLowerCase();
|
|
689
|
+
return host === "openai.azure.com" || host.endsWith(".openai.azure.com");
|
|
690
|
+
} catch {
|
|
691
|
+
return false;
|
|
692
|
+
}
|
|
693
|
+
})();
|
|
686
694
|
const env = { ...resolvedEnv };
|
|
687
695
|
// Always strip OPENAI_BASE_URL — for Azure we use config overrides,
|
|
688
696
|
// for non-Azure the CLI should use its built-in endpoint.
|
package/agent/autofix.mjs
CHANGED
|
@@ -536,7 +536,15 @@ export function runCodexExec(
|
|
|
536
536
|
// Otherwise strip OPENAI_BASE_URL so the CLI uses its ChatGPT OAuth.
|
|
537
537
|
const { env: codexEnv } = resolveCodexProfileRuntime(process.env);
|
|
538
538
|
const baseUrl = codexEnv.OPENAI_BASE_URL || "";
|
|
539
|
-
const isAzure =
|
|
539
|
+
const isAzure = (() => {
|
|
540
|
+
try {
|
|
541
|
+
const parsed = new URL(baseUrl);
|
|
542
|
+
const host = String(parsed.hostname || "").toLowerCase();
|
|
543
|
+
return host === "openai.azure.com" || host.endsWith(".openai.azure.com");
|
|
544
|
+
} catch {
|
|
545
|
+
return false;
|
|
546
|
+
}
|
|
547
|
+
})();
|
|
540
548
|
if (isAzure) {
|
|
541
549
|
// Map OPENAI_API_KEY → AZURE_OPENAI_API_KEY for Azure auth header
|
|
542
550
|
if (codexEnv.OPENAI_API_KEY && !codexEnv.AZURE_OPENAI_API_KEY) {
|
|
@@ -568,22 +576,11 @@ export function runCodexExec(
|
|
|
568
576
|
// Node double-killing the child with SIGTERM before our handler runs.
|
|
569
577
|
env: codexEnv,
|
|
570
578
|
};
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
// Arguments are passed as an array so shell word-splitting is safe.
|
|
577
|
-
child = spawn("codex", args, {
|
|
578
|
-
...spawnOptions,
|
|
579
|
-
shell: true,
|
|
580
|
-
});
|
|
581
|
-
} else {
|
|
582
|
-
child = spawn("codex", args, {
|
|
583
|
-
...spawnOptions,
|
|
584
|
-
shell: false,
|
|
585
|
-
});
|
|
586
|
-
}
|
|
579
|
+
const codexBin = process.platform === "win32" ? "codex.cmd" : "codex";
|
|
580
|
+
child = spawn(codexBin, args, {
|
|
581
|
+
...spawnOptions,
|
|
582
|
+
shell: false,
|
|
583
|
+
});
|
|
587
584
|
} catch (err) {
|
|
588
585
|
return promiseResolve({
|
|
589
586
|
success: false,
|
package/agent/bosun-skills.mjs
CHANGED
|
@@ -23,6 +23,10 @@ import { fileURLToPath } from "node:url";
|
|
|
23
23
|
const __filename = fileURLToPath(import.meta.url);
|
|
24
24
|
const __dirname = dirname(__filename);
|
|
25
25
|
|
|
26
|
+
function readBuiltinSkillFile(filename) {
|
|
27
|
+
return readFileSync(resolve(__dirname, "skills", filename), "utf8");
|
|
28
|
+
}
|
|
29
|
+
|
|
26
30
|
// ── Analytics stream path (same file task-executor writes to) ────────────────
|
|
27
31
|
const _SKILL_STREAM_PATH = resolve(
|
|
28
32
|
__dirname,
|
|
@@ -765,7 +769,7 @@ await engine.process(data);
|
|
|
765
769
|
`,
|
|
766
770
|
},
|
|
767
771
|
{
|
|
768
|
-
filename: "codebase-
|
|
772
|
+
filename: "skill-codebase-audit.md",
|
|
769
773
|
title: "Codebase Annotation Audit",
|
|
770
774
|
tags: [
|
|
771
775
|
"audit", "annotation", "documentation", "summary", "inventory",
|
|
@@ -773,117 +777,7 @@ await engine.process(data);
|
|
|
773
777
|
"manifest", "conformity", "regeneration", "claude", "copilot",
|
|
774
778
|
],
|
|
775
779
|
scope: "global",
|
|
776
|
-
content:
|
|
777
|
-
|
|
778
|
-
## Purpose
|
|
779
|
-
Systematically audit and annotate a codebase so that *future* AI agents can
|
|
780
|
-
navigate it 4× faster, use 20% fewer tokens, and avoid false-positive changes.
|
|
781
|
-
This skill is **documentation-only** — it MUST NOT fix bugs, refactor code,
|
|
782
|
-
or change program behavior.
|
|
783
|
-
|
|
784
|
-
## Philosophy — LEAN Annotations
|
|
785
|
-
|
|
786
|
-
Modern AI coding SDKs (Copilot, Codex, Claude Code) already auto-compact
|
|
787
|
-
context. Adding a memory/compaction layer on top is wasteful. What *does* help
|
|
788
|
-
is **repo-level documentation** that agents read at the start of a session:
|
|
789
|
-
summaries, warnings, architectural notes, and module manifests. These cost zero
|
|
790
|
-
runtime tokens and dramatically reduce exploration time.
|
|
791
|
-
|
|
792
|
-
## Annotation Format
|
|
793
|
-
|
|
794
|
-
Use structured comment headers that agents are trained to recognize:
|
|
795
|
-
|
|
796
|
-
\\\`\\\`\\\`
|
|
797
|
-
// BOSUN:SUMMARY — <module-name>
|
|
798
|
-
// <1–3 sentence summary of purpose, key types, and public API>
|
|
799
|
-
\\\`\\\`\\\`
|
|
800
|
-
|
|
801
|
-
\\\`\\\`\\\`
|
|
802
|
-
// BOSUN:WARN — <module-name>
|
|
803
|
-
// <non-obvious pitfall, race condition, or constraint agents MUST know>
|
|
804
|
-
\\\`\\\`\\\`
|
|
805
|
-
|
|
806
|
-
- Place annotations at the **top of the file**, after imports / shebang.
|
|
807
|
-
- Keep each annotation to ≤ 3 lines.
|
|
808
|
-
- Do NOT annotate trivial files (configs, lockfiles, generated code).
|
|
809
|
-
|
|
810
|
-
## 6-Phase Audit Process
|
|
811
|
-
|
|
812
|
-
### Phase 1 — Inventory
|
|
813
|
-
Enumerate every source file. For each file record:
|
|
814
|
-
| Field | Value |
|
|
815
|
-
|-------|-------|
|
|
816
|
-
| path | relative from repo root |
|
|
817
|
-
| lang | file extension / language |
|
|
818
|
-
| lines | line count |
|
|
819
|
-
| has_summary | yes / no |
|
|
820
|
-
| has_warn | yes / no |
|
|
821
|
-
| category | core / util / test / config / generated |
|
|
822
|
-
|
|
823
|
-
Output: \\\`.bosun/audit/inventory.json\\\`
|
|
824
|
-
|
|
825
|
-
### Phase 2 — Summaries
|
|
826
|
-
For every file where \\\`has_summary === false\\\` and \\\`category !== "generated"\\\`:
|
|
827
|
-
1. Read the file.
|
|
828
|
-
2. Write a \\\`BOSUN:SUMMARY\\\` comment at the top.
|
|
829
|
-
3. Stage the file.
|
|
830
|
-
|
|
831
|
-
### Phase 3 — Warnings
|
|
832
|
-
For every file, check for non-obvious constraints:
|
|
833
|
-
- Singleton/caching requirements (must be module-scope)
|
|
834
|
-
- Async fire-and-forget patterns (unhandled rejections)
|
|
835
|
-
- Order-dependent initialization
|
|
836
|
-
- Platform-specific behavior (Windows paths, etc.)
|
|
837
|
-
|
|
838
|
-
Add \\\`BOSUN:WARN\\\` comments where found.
|
|
839
|
-
|
|
840
|
-
### Phase 4 — Manifest Audit
|
|
841
|
-
Ensure \\\`AGENTS.md\\\` (or equivalent) at repo root is accurate:
|
|
842
|
-
- Lists all top-level modules with 1-line descriptions.
|
|
843
|
-
- Documents build / test / lint commands.
|
|
844
|
-
- Documents environment variables.
|
|
845
|
-
- Documents commit conventions.
|
|
846
|
-
- Lists known constraints or gotchas.
|
|
847
|
-
|
|
848
|
-
If the file is outdated or missing sections, append corrections.
|
|
849
|
-
|
|
850
|
-
### Phase 5 — Conformity Check
|
|
851
|
-
Re-scan all annotations and validate:
|
|
852
|
-
- \\\`BOSUN:SUMMARY\\\` is present in every non-trivial source file.
|
|
853
|
-
- \\\`BOSUN:WARN\\\` exists for files with known pitfalls.
|
|
854
|
-
- No stale annotations reference symbols/functions that no longer exist.
|
|
855
|
-
|
|
856
|
-
Output: \\\`.bosun/audit/conformity-report.json\\\`
|
|
857
|
-
|
|
858
|
-
### Phase 6 — Regeneration Schedule
|
|
859
|
-
Annotations rot. Add a \\\`.bosun/audit/schedule.json\\\` with:
|
|
860
|
-
\\\`\\\`\\\`json
|
|
861
|
-
{
|
|
862
|
-
"lastFullAudit": "<ISO timestamp>",
|
|
863
|
-
"nextRecommendedAudit": "<ISO timestamp + 30 days>",
|
|
864
|
-
"filesAudited": <count>,
|
|
865
|
-
"summariesAdded": <count>,
|
|
866
|
-
"warningsAdded": <count>,
|
|
867
|
-
"conformityScore": <0-100>
|
|
868
|
-
}
|
|
869
|
-
\\\`\\\`\\\`
|
|
870
|
-
|
|
871
|
-
## Hard Rules
|
|
872
|
-
|
|
873
|
-
1. **Do NOT change program behavior.** Only add/update comments and documentation.
|
|
874
|
-
2. **Do NOT refactor, fix bugs, or rename symbols.** Documentation only.
|
|
875
|
-
3. **Do NOT annotate generated files** (lockfiles, build output, .min.js, etc.).
|
|
876
|
-
4. **Keep summaries ≤ 3 lines.** Agents need density, not essays.
|
|
877
|
-
5. **Keep warnings actionable.** "This is complex" is useless.
|
|
878
|
-
"Must call init() before query() — throws otherwise" is helpful.
|
|
879
|
-
6. **Stage files individually** — never \\\`git add .\\\`.
|
|
880
|
-
7. **Commit with** \\\`docs(audit): annotate <module>\\\` — not feat/fix.
|
|
881
|
-
|
|
882
|
-
## Success Metrics
|
|
883
|
-
- A/B tested: annotated repos show 4× faster agent navigation.
|
|
884
|
-
- 20% fewer tokens consumed per task.
|
|
885
|
-
- Zero false-positive code changes from confused agents.
|
|
886
|
-
`,
|
|
780
|
+
content: readBuiltinSkillFile("skill-codebase-audit.md"),
|
|
887
781
|
},
|
|
888
782
|
{
|
|
889
783
|
filename: "custom-tool-creation.md",
|
|
@@ -1298,3 +1192,4 @@ export function buildRelevantSkillsPromptBlock(bosunHome, taskTitle, taskDescrip
|
|
|
1298
1192
|
|
|
1299
1193
|
return lines.join("\n").trim();
|
|
1300
1194
|
}
|
|
1195
|
+
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Skill: Codebase Annotation Audit
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
Systematically audit and annotate a codebase so that *future* AI agents can
|
|
5
|
+
navigate it 4× faster, use 20% fewer tokens, and avoid false-positive changes.
|
|
6
|
+
This skill is **documentation-only** — it MUST NOT fix bugs, refactor code,
|
|
7
|
+
or change program behavior.
|
|
8
|
+
|
|
9
|
+
## Philosophy — LEAN Annotations
|
|
10
|
+
|
|
11
|
+
Modern AI coding SDKs (Copilot, Codex, Claude Code) already auto-compact
|
|
12
|
+
context. Adding a memory/compaction layer on top is wasteful. What *does* help
|
|
13
|
+
is **repo-level documentation** that agents read at the start of a session:
|
|
14
|
+
summaries, warnings, architectural notes, and module manifests. These cost zero
|
|
15
|
+
runtime tokens and dramatically reduce exploration time.
|
|
16
|
+
|
|
17
|
+
## Annotation Format
|
|
18
|
+
|
|
19
|
+
Use structured comment headers that agents are trained to recognize:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
// CLAUDE:SUMMARY — <module-name>
|
|
23
|
+
// <1–3 sentence summary of purpose, key types, and public API>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
// CLAUDE:WARN — <module-name>
|
|
28
|
+
// <non-obvious pitfall, race condition, or constraint agents MUST know>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
- Place annotations at the **top of the file**, after imports / shebang.
|
|
32
|
+
- Keep each annotation to ≤ 3 lines.
|
|
33
|
+
- Do NOT annotate trivial files (configs, lockfiles, generated code).
|
|
34
|
+
|
|
35
|
+
## 6-Phase Audit
|
|
36
|
+
|
|
37
|
+
### Phase 1 — Inventory
|
|
38
|
+
Enumerate every source file. For each file record:
|
|
39
|
+
| Field | Value |
|
|
40
|
+
|-------|-------|
|
|
41
|
+
| path | relative from repo root |
|
|
42
|
+
| lang | file extension / language |
|
|
43
|
+
| lines | line count |
|
|
44
|
+
| has_summary | yes / no |
|
|
45
|
+
| has_warn | yes / no |
|
|
46
|
+
| category | core / util / test / config / generated |
|
|
47
|
+
|
|
48
|
+
Output: `.bosun/audit/inventory.json`
|
|
49
|
+
|
|
50
|
+
### Phase 2 — Summaries
|
|
51
|
+
For every file where `has_summary === false` and `category !== "generated"`:
|
|
52
|
+
1. Read the file.
|
|
53
|
+
2. Write a `CLAUDE:SUMMARY` comment at the top.
|
|
54
|
+
3. Stage the file.
|
|
55
|
+
|
|
56
|
+
### Phase 3 — Warnings
|
|
57
|
+
For every file, check for non-obvious constraints:
|
|
58
|
+
- Singleton/caching requirements (must be module-scope)
|
|
59
|
+
- Async fire-and-forget patterns (unhandled rejections)
|
|
60
|
+
- Order-dependent initialization
|
|
61
|
+
- Platform-specific behavior (Windows paths, etc.)
|
|
62
|
+
|
|
63
|
+
Add `CLAUDE:WARN` comments where found.
|
|
64
|
+
|
|
65
|
+
### Phase 4 — Manifest Audit
|
|
66
|
+
Ensure `AGENTS.md` (or equivalent) at repo root is accurate:
|
|
67
|
+
- Lists all top-level modules with 1-line descriptions.
|
|
68
|
+
- Documents build / test / lint commands.
|
|
69
|
+
- Documents environment variables.
|
|
70
|
+
- Documents commit conventions.
|
|
71
|
+
- Lists known constraints or gotchas.
|
|
72
|
+
|
|
73
|
+
If the file is outdated or missing sections, append corrections.
|
|
74
|
+
|
|
75
|
+
### Phase 5 — Conformity Check
|
|
76
|
+
Re-scan all annotations and validate:
|
|
77
|
+
- `CLAUDE:SUMMARY` is present in every non-trivial source file.
|
|
78
|
+
- `CLAUDE:WARN` exists for files with known pitfalls.
|
|
79
|
+
- No stale annotations reference symbols/functions that no longer exist.
|
|
80
|
+
|
|
81
|
+
Output: `.bosun/audit/conformity-report.json`
|
|
82
|
+
|
|
83
|
+
### Phase 6 — Regeneration Schedule
|
|
84
|
+
Annotations rot. Add a `.bosun/audit/schedule.json` with:
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"lastFullAudit": "<ISO timestamp>",
|
|
88
|
+
"nextRecommendedAudit": "<ISO timestamp + 30 days>",
|
|
89
|
+
"filesAudited": <count>,
|
|
90
|
+
"summariesAdded": <count>,
|
|
91
|
+
"warningsAdded": <count>,
|
|
92
|
+
"conformityScore": <0-100>
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Hard Rules
|
|
97
|
+
|
|
98
|
+
1. **Do NOT change program behavior.** Only add/update comments and documentation.
|
|
99
|
+
2. **Do NOT refactor, fix bugs, or rename symbols.** Documentation only.
|
|
100
|
+
3. **Do NOT annotate generated files** (lockfiles, build output, `.min.js`, etc.).
|
|
101
|
+
4. **Keep summaries ≤ 3 lines.** Agents need density, not essays.
|
|
102
|
+
5. **Keep warnings actionable.** "This is complex" is useless.
|
|
103
|
+
"Must call init() before query() — throws otherwise" is helpful.
|
|
104
|
+
6. **Stage files individually** — never `git add .`.
|
|
105
|
+
7. **Commit with** `docs(audit): annotate <module>` — not `feat`/`fix`.
|
|
106
|
+
|
|
107
|
+
## Success Metrics
|
|
108
|
+
- A/B tested: annotated repos show 4× faster agent navigation.
|
|
109
|
+
- 20% fewer tokens consumed per task.
|
|
110
|
+
- Zero false-positive code changes from confused agents.
|
|
111
|
+
|
package/bosun-tui.mjs
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* bosun-tui — Terminal User Interface for Bosun
|
|
5
|
+
*
|
|
6
|
+
* A terminal-based UI for monitoring Bosun agents, tasks, and workflows.
|
|
7
|
+
* Built with Ink (React-like CLI framework).
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* bosun-tui # Start the TUI
|
|
11
|
+
* bosun-tui --help # Show help
|
|
12
|
+
* bosun-tui --port 3080 # Connect to specific port
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { resolve, dirname } from "node:path";
|
|
16
|
+
import { fileURLToPath } from "node:url";
|
|
17
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
18
|
+
|
|
19
|
+
const __dirname = dirname(fileURLToPath(new URL(".", import.meta.url)));
|
|
20
|
+
|
|
21
|
+
function showHelp() {
|
|
22
|
+
const version = JSON.parse(
|
|
23
|
+
readFileSync(resolve(__dirname, "package.json"), "utf8"),
|
|
24
|
+
).version;
|
|
25
|
+
|
|
26
|
+
console.log(`
|
|
27
|
+
bosun-tui v${version}
|
|
28
|
+
Terminal User Interface for Bosun
|
|
29
|
+
|
|
30
|
+
USAGE
|
|
31
|
+
bosun-tui [options]
|
|
32
|
+
|
|
33
|
+
OPTIONS
|
|
34
|
+
--port <n> UI server port to connect (default: 3080 or TELEGRAM_UI_PORT env)
|
|
35
|
+
--host <host> UI server host (default: localhost)
|
|
36
|
+
--connect Connect to existing UI server (don't start monitor)
|
|
37
|
+
--screen <name> Initial screen (tasks|agents|status)
|
|
38
|
+
--refresh <ms> Stats refresh interval (default: 2000ms)
|
|
39
|
+
--help Show this help
|
|
40
|
+
--version Show version
|
|
41
|
+
|
|
42
|
+
SCREENS
|
|
43
|
+
tasks Kanban board with task CRUD
|
|
44
|
+
agents Live agent session table
|
|
45
|
+
status System status overview
|
|
46
|
+
|
|
47
|
+
KEYBOARD NAVIGATION
|
|
48
|
+
Tab / Shift+Tab Navigate between panels
|
|
49
|
+
↑↓←→ Navigate within panels
|
|
50
|
+
Enter Select / Execute action
|
|
51
|
+
Esc Back / Close modal
|
|
52
|
+
c Create new task (tasks screen)
|
|
53
|
+
r Refresh data
|
|
54
|
+
q Quit
|
|
55
|
+
|
|
56
|
+
EXAMPLES
|
|
57
|
+
bosun-tui --port 3080
|
|
58
|
+
bosun-tui --screen tasks
|
|
59
|
+
bosun-tui --connect --port 3080
|
|
60
|
+
`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function getArgValue(flag, defaultValue = "") {
|
|
64
|
+
const args = process.argv.slice(2);
|
|
65
|
+
const match = args.find((arg) => arg.startsWith(`${flag}=`));
|
|
66
|
+
if (match) {
|
|
67
|
+
return match.slice(flag.length + 1).trim();
|
|
68
|
+
}
|
|
69
|
+
const idx = args.indexOf(flag);
|
|
70
|
+
if (idx >= 0 && args[idx + 1] && !args[idx + 1].startsWith("--")) {
|
|
71
|
+
return args[idx + 1].trim();
|
|
72
|
+
}
|
|
73
|
+
return defaultValue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function getArgFlag(flag) {
|
|
77
|
+
const args = process.argv.slice(2);
|
|
78
|
+
return args.includes(flag);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function main() {
|
|
82
|
+
const args = process.argv.slice(2);
|
|
83
|
+
|
|
84
|
+
if (getArgFlag("--help") || args.includes("-h")) {
|
|
85
|
+
showHelp();
|
|
86
|
+
process.exit(0);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (getArgFlag("--version") || args.includes("-v")) {
|
|
90
|
+
const version = JSON.parse(
|
|
91
|
+
readFileSync(resolve(__dirname, "package.json"), "utf8"),
|
|
92
|
+
).version;
|
|
93
|
+
console.log(`bosun-tui v${version}`);
|
|
94
|
+
process.exit(0);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const port = Number(getArgValue("--port", process.env.TELEGRAM_UI_PORT || "3080")) || 3080;
|
|
98
|
+
const host = getArgValue("--host", "localhost");
|
|
99
|
+
const connectOnly = getArgFlag("--connect");
|
|
100
|
+
const initialScreen = getArgValue("--screen", "status");
|
|
101
|
+
const refreshMs = Number(getArgValue("--refresh", "2000")) || 2000;
|
|
102
|
+
|
|
103
|
+
console.log(`[bosun-tui] Starting...`);
|
|
104
|
+
console.log(`[bosun-tui] Connecting to ${host}:${port}`);
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const { render } = await import("ink");
|
|
108
|
+
const importErrors = [];
|
|
109
|
+
|
|
110
|
+
let App;
|
|
111
|
+
try {
|
|
112
|
+
const appModule = await import("./tui/app.mjs");
|
|
113
|
+
App = appModule.default;
|
|
114
|
+
} catch (importErr) {
|
|
115
|
+
importErrors.push(`App: ${importErr.message}`);
|
|
116
|
+
console.error(`[bosun-tui] Failed to import TUI app: ${importErr.message}`);
|
|
117
|
+
console.log(`[bosun-tui] TUI requires ink. Install with: npm install ink`);
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const { waitUntilExit } = await import("ink");
|
|
122
|
+
|
|
123
|
+
const app = render(
|
|
124
|
+
App({
|
|
125
|
+
host,
|
|
126
|
+
port,
|
|
127
|
+
connectOnly,
|
|
128
|
+
initialScreen,
|
|
129
|
+
refreshMs,
|
|
130
|
+
}),
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
process.exitCode = await waitUntilExit(app);
|
|
134
|
+
} catch (err) {
|
|
135
|
+
console.error(`[bosun-tui] Failed to start: ${err.message}`);
|
|
136
|
+
console.log(`[bosun-tui] Ensure bosun is running or use --connect to connect to an existing UI server`);
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
main();
|