beeops 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.ja.md +156 -0
  3. package/README.md +80 -0
  4. package/bin/beeops.js +502 -0
  5. package/command/bo.md +120 -0
  6. package/contexts/agent-modes.json +100 -0
  7. package/contexts/code-reviewer.md +118 -0
  8. package/contexts/coder.md +247 -0
  9. package/contexts/default.md +1 -0
  10. package/contexts/en/agent-modes.json +100 -0
  11. package/contexts/en/code-reviewer.md +129 -0
  12. package/contexts/en/coder.md +247 -0
  13. package/contexts/en/default.md +1 -0
  14. package/contexts/en/fb.md +15 -0
  15. package/contexts/en/leader.md +158 -0
  16. package/contexts/en/log.md +16 -0
  17. package/contexts/en/queen.md +240 -0
  18. package/contexts/en/review-leader.md +190 -0
  19. package/contexts/en/reviewer-base.md +27 -0
  20. package/contexts/en/security-reviewer.md +200 -0
  21. package/contexts/en/test-auditor.md +146 -0
  22. package/contexts/en/tester.md +135 -0
  23. package/contexts/en/worker-base.md +69 -0
  24. package/contexts/fb.md +15 -0
  25. package/contexts/ja/agent-modes.json +100 -0
  26. package/contexts/ja/code-reviewer.md +129 -0
  27. package/contexts/ja/coder.md +247 -0
  28. package/contexts/ja/default.md +1 -0
  29. package/contexts/ja/fb.md +15 -0
  30. package/contexts/ja/leader.md +158 -0
  31. package/contexts/ja/log.md +17 -0
  32. package/contexts/ja/queen.md +240 -0
  33. package/contexts/ja/review-leader.md +190 -0
  34. package/contexts/ja/reviewer-base.md +27 -0
  35. package/contexts/ja/security-reviewer.md +200 -0
  36. package/contexts/ja/test-auditor.md +146 -0
  37. package/contexts/ja/tester.md +135 -0
  38. package/contexts/ja/worker-base.md +68 -0
  39. package/contexts/leader.md +158 -0
  40. package/contexts/log.md +16 -0
  41. package/contexts/queen.md +240 -0
  42. package/contexts/review-leader.md +190 -0
  43. package/contexts/reviewer-base.md +27 -0
  44. package/contexts/security-reviewer.md +200 -0
  45. package/contexts/test-auditor.md +146 -0
  46. package/contexts/tester.md +135 -0
  47. package/contexts/worker-base.md +69 -0
  48. package/hooks/checkpoint.py +89 -0
  49. package/hooks/prompt-context.py +139 -0
  50. package/hooks/resolve-log-path.py +93 -0
  51. package/hooks/run-log.py +429 -0
  52. package/package.json +42 -0
  53. package/scripts/launch-leader.sh +282 -0
  54. package/scripts/launch-worker.sh +184 -0
  55. package/skills/bo-dispatch/SKILL.md +299 -0
  56. package/skills/bo-issue-sync/SKILL.md +103 -0
  57. package/skills/bo-leader-dispatch/SKILL.md +211 -0
  58. package/skills/bo-log-writer/SKILL.md +101 -0
  59. package/skills/bo-review-backend/SKILL.md +234 -0
  60. package/skills/bo-review-database/SKILL.md +243 -0
  61. package/skills/bo-review-frontend/SKILL.md +236 -0
  62. package/skills/bo-review-operations/SKILL.md +268 -0
  63. package/skills/bo-review-process/SKILL.md +181 -0
  64. package/skills/bo-review-security/SKILL.md +214 -0
  65. package/skills/bo-review-security/references/finance-security.md +351 -0
  66. package/skills/bo-self-improver/SKILL.md +145 -0
  67. package/skills/bo-self-improver/refs/agent-manager.md +61 -0
  68. package/skills/bo-self-improver/refs/command-manager.md +46 -0
  69. package/skills/bo-self-improver/refs/skill-manager.md +59 -0
  70. package/skills/bo-self-improver/scripts/analyze.py +359 -0
  71. package/skills/bo-task-decomposer/SKILL.md +130 -0
package/bin/beeops.js ADDED
@@ -0,0 +1,502 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+ const { execSync } = require("child_process");
7
+
8
+ const PKG_DIR = path.resolve(__dirname, "..");
9
+ const COMMAND_SRC = path.join(PKG_DIR, "command", "bo.md");
10
+ const SKILLS_SRC = path.join(PKG_DIR, "skills");
11
+ const CONTEXTS_SRC = path.join(PKG_DIR, "contexts");
12
+ const HOOKS_DIR = path.join(PKG_DIR, "hooks");
13
+ const HOOK_SRC = path.join(HOOKS_DIR, "prompt-context.py");
14
+ const HOOK_STOP_SRC = path.join(HOOKS_DIR, "run-log.py");
15
+ const HOOK_POST_SRC = path.join(HOOKS_DIR, "checkpoint.py");
16
+ const HOME_DIR = process.env.HOME || process.env.USERPROFILE;
17
+
18
+ // ── Helpers ──
19
+
20
+ function getProjectRoot() {
21
+ try {
22
+ return execSync("git rev-parse --show-toplevel", { encoding: "utf8" }).trim();
23
+ } catch {
24
+ console.error("Error: Not inside a git repository.");
25
+ process.exit(1);
26
+ }
27
+ }
28
+
29
+ function ensureDir(dir) {
30
+ fs.mkdirSync(dir, { recursive: true });
31
+ }
32
+
33
+ function copyFile(src, dest) {
34
+ ensureDir(path.dirname(dest));
35
+ fs.copyFileSync(src, dest);
36
+ console.log(` copied: ${path.relative(process.cwd(), dest)}`);
37
+ }
38
+
39
+ function copyDir(src, dest) {
40
+ ensureDir(dest);
41
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
42
+ const srcPath = path.join(src, entry.name);
43
+ const destPath = path.join(dest, entry.name);
44
+ if (entry.isDirectory()) {
45
+ copyDir(srcPath, destPath);
46
+ } else {
47
+ copyFile(srcPath, destPath);
48
+ }
49
+ }
50
+ }
51
+
52
+ function commandExists(cmd) {
53
+ try {
54
+ execSync(`command -v ${cmd}`, { encoding: "utf8", stdio: "pipe" });
55
+ return true;
56
+ } catch {
57
+ return false;
58
+ }
59
+ }
60
+
61
+ function getCommandVersion(cmd, versionFlag) {
62
+ try {
63
+ return execSync(`${cmd} ${versionFlag || "--version"}`, {
64
+ encoding: "utf8",
65
+ stdio: "pipe",
66
+ }).trim().split("\n")[0];
67
+ } catch {
68
+ return null;
69
+ }
70
+ }
71
+
72
+ // ── Prerequisites check ──
73
+
74
+ function checkPrerequisites() {
75
+ console.log("Checking prerequisites...\n");
76
+ let allOk = true;
77
+ const warnings = [];
78
+
79
+ const checks = [
80
+ { cmd: "node", required: true, label: "Node.js", versionFlag: "--version" },
81
+ { cmd: "git", required: true, label: "git", versionFlag: "--version" },
82
+ { cmd: "tmux", required: true, label: "tmux", versionFlag: "-V" },
83
+ { cmd: "python3", required: true, label: "python3", versionFlag: "--version" },
84
+ { cmd: "claude", required: false, label: "Claude CLI", versionFlag: "--version" },
85
+ { cmd: "gh", required: false, label: "GitHub CLI (gh)", versionFlag: "--version" },
86
+ ];
87
+
88
+ for (const { cmd, required, label, versionFlag } of checks) {
89
+ if (commandExists(cmd)) {
90
+ const ver = getCommandVersion(cmd, versionFlag) || "";
91
+ console.log(` [ok] ${label} (${ver})`);
92
+ } else if (required) {
93
+ console.log(` [MISSING] ${label} — required`);
94
+ allOk = false;
95
+ } else {
96
+ console.log(` [warn] ${label} — not found (optional)`);
97
+ warnings.push(label);
98
+ }
99
+ }
100
+
101
+ // Node.js version check
102
+ const nodeVersion = parseInt(process.versions.node.split(".")[0], 10);
103
+ if (nodeVersion < 18) {
104
+ console.log(` [MISSING] Node.js >= 18 required (current: ${process.versions.node})`);
105
+ allOk = false;
106
+ }
107
+
108
+ console.log("");
109
+
110
+ if (!allOk) {
111
+ console.error("Some required prerequisites are missing. Install them and try again.");
112
+ process.exit(1);
113
+ }
114
+
115
+ if (warnings.length > 0) {
116
+ console.log(` Note: ${warnings.join(", ")} not found. Some features may not work.\n`);
117
+ }
118
+
119
+ return allOk;
120
+ }
121
+
122
+ // ── Hook registration ──
123
+
124
+ function resolveSettingsFile(root, mode) {
125
+ switch (mode) {
126
+ case "global":
127
+ return path.join(HOME_DIR, ".claude", "settings.json");
128
+ case "shared":
129
+ return path.join(root, ".claude", "settings.json");
130
+ case "local":
131
+ default:
132
+ return path.join(root, ".claude", "settings.local.json");
133
+ }
134
+ }
135
+
136
+ function settingsModeLabel(mode) {
137
+ switch (mode) {
138
+ case "global": return "~/.claude/settings.json";
139
+ case "shared": return ".claude/settings.json";
140
+ case "local":
141
+ default: return ".claude/settings.local.json";
142
+ }
143
+ }
144
+
145
+ function upsertHook(hooks, hookType, matchStr, command, matcher) {
146
+ if (!hooks[hookType]) hooks[hookType] = [];
147
+
148
+ const existingIdx = hooks[hookType].findIndex((entry) => {
149
+ // Check new format
150
+ if (entry.hooks) {
151
+ return entry.hooks.some((h) => h.command && h.command.includes(matchStr));
152
+ }
153
+ // Check old format (for migration)
154
+ return entry.type === "command" && entry.command && entry.command.includes(matchStr);
155
+ });
156
+
157
+ const hookEntry = { hooks: [{ type: "command", command }] };
158
+ if (matcher) hookEntry.matcher = matcher;
159
+
160
+ if (existingIdx >= 0) {
161
+ hooks[hookType][existingIdx] = hookEntry;
162
+ return "updated";
163
+ } else {
164
+ hooks[hookType].push(hookEntry);
165
+ return "added";
166
+ }
167
+ }
168
+
169
+ function updateSettingsHook(root, mode) {
170
+ const settingsFile = resolveSettingsFile(root, mode);
171
+ ensureDir(path.dirname(settingsFile));
172
+
173
+ let settings = {};
174
+ if (fs.existsSync(settingsFile)) {
175
+ try {
176
+ settings = JSON.parse(fs.readFileSync(settingsFile, "utf8"));
177
+ } catch {
178
+ console.warn(` warn: Could not parse ${path.basename(settingsFile)}, creating new one`);
179
+ settings = {};
180
+ }
181
+ }
182
+
183
+ if (!settings.hooks) settings.hooks = {};
184
+
185
+ const label = settingsModeLabel(mode);
186
+
187
+ // UserPromptSubmit: prompt-context.py
188
+ const r1 = upsertHook(settings.hooks, "UserPromptSubmit", "prompt-context.py", `python3 ${HOOK_SRC}`);
189
+ console.log(` ${r1}: ${label} (UserPromptSubmit hook)`);
190
+
191
+ // Stop: run-log.py
192
+ const r2 = upsertHook(settings.hooks, "Stop", "run-log.py", `python3 ${HOOK_STOP_SRC}`);
193
+ console.log(` ${r2}: ${label} (Stop hook)`);
194
+
195
+ // PostToolUse: checkpoint.py
196
+ const r3 = upsertHook(settings.hooks, "PostToolUse", "checkpoint.py", `python3 ${HOOK_POST_SRC}`);
197
+ console.log(` ${r3}: ${label} (PostToolUse hook)`);
198
+
199
+ fs.writeFileSync(settingsFile, JSON.stringify(settings, null, 2) + "\n");
200
+ }
201
+
202
+ // ── init ──
203
+
204
+ function init(opts) {
205
+ checkPrerequisites();
206
+
207
+ const root = getProjectRoot();
208
+ const claudeDir = path.join(root, ".claude");
209
+
210
+ console.log(`Initializing beeops in ${root}...\n`);
211
+
212
+ // 1. Copy command
213
+ copyFile(COMMAND_SRC, path.join(claudeDir, "commands", "bo.md"));
214
+
215
+ // 2. Copy skills
216
+ copyDir(
217
+ path.join(SKILLS_SRC, "bo-dispatch"),
218
+ path.join(claudeDir, "skills", "bo-dispatch")
219
+ );
220
+ copyDir(
221
+ path.join(SKILLS_SRC, "bo-leader-dispatch"),
222
+ path.join(claudeDir, "skills", "bo-leader-dispatch")
223
+ );
224
+ copyDir(
225
+ path.join(SKILLS_SRC, "bo-task-decomposer"),
226
+ path.join(claudeDir, "skills", "bo-task-decomposer")
227
+ );
228
+ copyDir(
229
+ path.join(SKILLS_SRC, "bo-issue-sync"),
230
+ path.join(claudeDir, "skills", "bo-issue-sync")
231
+ );
232
+ copyDir(
233
+ path.join(SKILLS_SRC, "bo-log-writer"),
234
+ path.join(claudeDir, "skills", "bo-log-writer")
235
+ );
236
+ copyDir(
237
+ path.join(SKILLS_SRC, "bo-self-improver"),
238
+ path.join(claudeDir, "skills", "bo-self-improver")
239
+ );
240
+ copyDir(
241
+ path.join(SKILLS_SRC, "bo-review-backend"),
242
+ path.join(claudeDir, "skills", "bo-review-backend")
243
+ );
244
+ copyDir(
245
+ path.join(SKILLS_SRC, "bo-review-frontend"),
246
+ path.join(claudeDir, "skills", "bo-review-frontend")
247
+ );
248
+ copyDir(
249
+ path.join(SKILLS_SRC, "bo-review-database"),
250
+ path.join(claudeDir, "skills", "bo-review-database")
251
+ );
252
+ copyDir(
253
+ path.join(SKILLS_SRC, "bo-review-operations"),
254
+ path.join(claudeDir, "skills", "bo-review-operations")
255
+ );
256
+ copyDir(
257
+ path.join(SKILLS_SRC, "bo-review-process"),
258
+ path.join(claudeDir, "skills", "bo-review-process")
259
+ );
260
+ copyDir(
261
+ path.join(SKILLS_SRC, "bo-review-security"),
262
+ path.join(claudeDir, "skills", "bo-review-security")
263
+ );
264
+
265
+ // 3. Clean up old names if exists
266
+ for (const old of [
267
+ "leader-dispatch", "ants-leader-dispatch", "ants-dispatch",
268
+ "meta-task-decomposer", "orch-issue-sync", "meta-log-writer", "meta-self-improver",
269
+ "review-backend", "review-frontend", "review-database",
270
+ "review-operations", "review-process", "review-security",
271
+ ]) {
272
+ const oldDir = path.join(claudeDir, "skills", old);
273
+ if (fs.existsSync(oldDir)) {
274
+ fs.rmSync(oldDir, { recursive: true });
275
+ console.log(` removed: .claude/skills/${old}/ (migrated to bo-*)`);
276
+ }
277
+ }
278
+ // Old command file
279
+ const oldCmd = path.join(claudeDir, "commands", "ants.md");
280
+ if (fs.existsSync(oldCmd)) {
281
+ fs.unlinkSync(oldCmd);
282
+ console.log(" removed: .claude/commands/ants.md (migrated to bo.md)");
283
+ }
284
+
285
+ // 4. Register hooks (UserPromptSubmit + Stop + PostToolUse)
286
+ updateSettingsHook(root, opts.hookMode);
287
+
288
+ // 5. Save locale preference
289
+ const boDir = path.join(claudeDir, "beeops");
290
+ ensureDir(boDir);
291
+ fs.writeFileSync(path.join(boDir, "locale"), opts.locale + "\n");
292
+ console.log(` locale: ${opts.locale} (saved to .claude/beeops/locale)`);
293
+
294
+ // 6. Copy contexts if --with-contexts
295
+ if (opts.withContexts) {
296
+ const destContexts = path.join(claudeDir, "beeops", "contexts");
297
+ copyDir(CONTEXTS_SRC, destContexts);
298
+ console.log("\n Contexts copied to .claude/beeops/contexts/ for customization.");
299
+ console.log(" Edit these files to customize agent behavior.");
300
+ console.log(" Delete a file to fall back to the package default.");
301
+ }
302
+
303
+ console.log("\nbeeops initialized successfully!");
304
+ console.log("Run /bo in Claude Code to start the Queen.");
305
+ }
306
+
307
+ // ── check ──
308
+
309
+ function findHookInSettings(settingsFile, hookType, matchStr) {
310
+ if (!fs.existsSync(settingsFile)) return null;
311
+ try {
312
+ const settings = JSON.parse(fs.readFileSync(settingsFile, "utf8"));
313
+ const entries = settings.hooks?.[hookType] || [];
314
+ const found = entries.find((entry) => {
315
+ // New format: { matcher, hooks: [{ type, command }] }
316
+ if (entry.hooks) {
317
+ return entry.hooks.some((h) => h.command && h.command.includes(matchStr));
318
+ }
319
+ // Old format: { type, command }
320
+ return entry.command && entry.command.includes(matchStr);
321
+ });
322
+ return found || null;
323
+ } catch {
324
+ return null;
325
+ }
326
+ }
327
+
328
+ function check() {
329
+ const root = getProjectRoot();
330
+ const claudeDir = path.join(root, ".claude");
331
+ let ok = true;
332
+
333
+ console.log("Checking beeops setup...\n");
334
+
335
+ // Check command
336
+ const cmdPath = path.join(claudeDir, "commands", "bo.md");
337
+ if (fs.existsSync(cmdPath)) {
338
+ console.log(" [ok] .claude/commands/bo.md");
339
+ } else {
340
+ console.log(" [missing] .claude/commands/bo.md");
341
+ ok = false;
342
+ }
343
+
344
+ // Check skills
345
+ const CORE_SKILLS = [
346
+ "bo-dispatch", "bo-leader-dispatch", "bo-task-decomposer", "bo-issue-sync",
347
+ "bo-log-writer", "bo-self-improver",
348
+ "bo-review-backend", "bo-review-frontend", "bo-review-database",
349
+ "bo-review-operations", "bo-review-process", "bo-review-security",
350
+ ];
351
+ for (const skill of CORE_SKILLS) {
352
+ const skillPath = path.join(claudeDir, "skills", skill, "SKILL.md");
353
+ if (fs.existsSync(skillPath)) {
354
+ console.log(` [ok] .claude/skills/${skill}/SKILL.md`);
355
+ } else {
356
+ console.log(` [missing] .claude/skills/${skill}/SKILL.md`);
357
+ ok = false;
358
+ }
359
+ }
360
+
361
+ // Check hook across all 3 settings locations
362
+ const settingsLocations = [
363
+ { file: path.join(claudeDir, "settings.local.json"), label: ".claude/settings.local.json" },
364
+ { file: path.join(claudeDir, "settings.json"), label: ".claude/settings.json" },
365
+ { file: path.join(HOME_DIR, ".claude", "settings.json"), label: "~/.claude/settings.json" },
366
+ ];
367
+
368
+ const HOOK_CHECKS = [
369
+ { hookType: "UserPromptSubmit", matchStr: "prompt-context.py", label: "UserPromptSubmit" },
370
+ { hookType: "Stop", matchStr: "run-log.py", label: "Stop" },
371
+ { hookType: "PostToolUse", matchStr: "checkpoint.py", label: "PostToolUse" },
372
+ ];
373
+
374
+ for (const { hookType, matchStr, label: hookLabel } of HOOK_CHECKS) {
375
+ let hookFound = false;
376
+ for (const { file, label } of settingsLocations) {
377
+ const hook = findHookInSettings(file, hookType, matchStr);
378
+ if (hook) {
379
+ console.log(` [ok] ${label} (${hookLabel} hook)`);
380
+ hookFound = true;
381
+ break;
382
+ }
383
+ }
384
+ if (!hookFound) {
385
+ if (hookType === "UserPromptSubmit") {
386
+ console.log(` [missing] ${hookLabel} hook not found`);
387
+ ok = false;
388
+ } else {
389
+ console.log(` [warn] ${hookLabel} hook not found (optional)`);
390
+ }
391
+ }
392
+ }
393
+
394
+ // Check local contexts (informational)
395
+ const localContexts = path.join(claudeDir, "beeops", "contexts");
396
+ if (fs.existsSync(localContexts)) {
397
+ const files = fs.readdirSync(localContexts);
398
+ console.log(` [info] .claude/beeops/contexts/ (${files.length} custom file(s))`);
399
+ }
400
+
401
+ // Check package resolvable
402
+ try {
403
+ require.resolve("beeops/package.json");
404
+ console.log(" [ok] beeops package resolvable");
405
+ } catch {
406
+ console.log(" [warn] beeops package not resolvable from cwd");
407
+ }
408
+
409
+ console.log(ok ? "\nAll checks passed!" : "\nSome checks failed. Run: npx beeops init");
410
+ process.exit(ok ? 0 : 1);
411
+ }
412
+
413
+ // ── Help / Version ──
414
+
415
+ function printHelp() {
416
+ console.log("beeops — 3-layer multi-agent orchestration for Claude Code\n");
417
+ console.log("Usage: beeops <command> [options]\n");
418
+ console.log("Commands:");
419
+ console.log(" init Install beeops into the current project");
420
+ console.log(" update Update beeops files (same as init)");
421
+ console.log(" check Verify beeops setup\n");
422
+ console.log("Options for init/update:");
423
+ console.log(" --local Register hook in .claude/settings.local.json (default)");
424
+ console.log(" --shared Register hook in .claude/settings.json (team-shared)");
425
+ console.log(" -g, --global Register hook in ~/.claude/settings.json (all projects)");
426
+ console.log(" --with-contexts Copy default contexts for customization");
427
+ console.log(" --locale <lang> Set locale (default: en, available: en, ja)\n");
428
+ console.log("Examples:");
429
+ console.log(" npx beeops init");
430
+ console.log(" npx beeops init --shared --locale ja");
431
+ console.log(" npx beeops init --with-contexts");
432
+ console.log(" npx beeops check");
433
+ }
434
+
435
+ function printVersion() {
436
+ const pkg = JSON.parse(fs.readFileSync(path.join(PKG_DIR, "package.json"), "utf8"));
437
+ console.log(`beeops v${pkg.version}`);
438
+ }
439
+
440
+ // ── Argument parsing ──
441
+
442
+ function parseArgs(argv) {
443
+ const args = argv.slice(2);
444
+ const opts = {
445
+ hookMode: "local",
446
+ withContexts: false,
447
+ locale: "en",
448
+ };
449
+
450
+ // Check for help/version flags first (anywhere in args)
451
+ if (args.includes("-h") || args.includes("--help") || args.includes("help")) {
452
+ printHelp();
453
+ process.exit(0);
454
+ }
455
+ if (args.includes("-v") || args.includes("--version") || args.includes("version")) {
456
+ printVersion();
457
+ process.exit(0);
458
+ }
459
+
460
+ const command = args[0];
461
+
462
+ for (let i = 1; i < args.length; i++) {
463
+ switch (args[i]) {
464
+ case "--global":
465
+ case "-g":
466
+ opts.hookMode = "global";
467
+ break;
468
+ case "--shared":
469
+ opts.hookMode = "shared";
470
+ break;
471
+ case "--local":
472
+ opts.hookMode = "local";
473
+ break;
474
+ case "--with-contexts":
475
+ opts.withContexts = true;
476
+ break;
477
+ case "--locale":
478
+ if (args[i + 1]) {
479
+ opts.locale = args[++i];
480
+ }
481
+ break;
482
+ }
483
+ }
484
+
485
+ return { command, opts };
486
+ }
487
+
488
+ // ── Main ──
489
+ const { command, opts } = parseArgs(process.argv);
490
+
491
+ switch (command) {
492
+ case "init":
493
+ case "update":
494
+ init(opts);
495
+ break;
496
+ case "check":
497
+ check();
498
+ break;
499
+ default:
500
+ printHelp();
501
+ process.exit(command ? 1 : 0);
502
+ }
package/command/bo.md ADDED
@@ -0,0 +1,120 @@
1
+ Launch a Queen session (beeops) in tmux and auto-display it.
2
+ queue.yaml generation and management is handled entirely by the Queen inside tmux.
3
+
4
+ ## Execution steps
5
+
6
+ ### Step 0: Resolve package paths
7
+
8
+ ```bash
9
+ PKG_DIR=$(node -e "console.log(require.resolve('beeops/package.json').replace('/package.json',''))")
10
+ BO_SCRIPTS_DIR="$PKG_DIR/scripts"
11
+ BO_CONTEXTS_DIR="$PKG_DIR/contexts"
12
+ ```
13
+
14
+ ### Step 1: Check for existing session
15
+
16
+ ```bash
17
+ SESSION="bo"
18
+
19
+ tmux has-session -t "$SESSION" 2>/dev/null && {
20
+ echo "An existing bo session was found."
21
+ echo " tmux attach -t bo # monitor"
22
+ echo " tmux kill-session -t bo # stop and restart"
23
+ # Stop here. Let the user decide.
24
+ }
25
+ ```
26
+
27
+ If an existing session is found, display instructions and **stop**. The user must explicitly kill the session before re-running.
28
+
29
+ ### Step 2: Start tmux session
30
+
31
+ ```bash
32
+ CWD=$(pwd)
33
+
34
+ tmux new-session -d -s "$SESSION" -n queen -c "$CWD" \
35
+ "unset CLAUDECODE; BO_QUEEN=1 BO_SCRIPTS_DIR='$BO_SCRIPTS_DIR' BO_CONTEXTS_DIR='$BO_CONTEXTS_DIR' claude --dangerously-skip-permissions; echo '--- Done (press Enter to close) ---'; read"
36
+ ```
37
+
38
+ ### Step 2.5: Configure tmux pane display
39
+
40
+ ```bash
41
+ # Show role name at top of each pane
42
+ tmux set-option -t "$SESSION" pane-border-status top
43
+
44
+ # Format: prefer @agent_label (not overridable by Claude Code), fallback to pane_title
45
+ tmux set-option -t "$SESSION" pane-border-format \
46
+ " #{?pane_active,#[bold],}#{?@agent_label,#{@agent_label},#{pane_title}}#[default] "
47
+
48
+ # Queen pane: gold border + title
49
+ tmux select-pane -t "$SESSION:queen.0" -T "👑 queen"
50
+ tmux set-option -p -t "$SESSION:queen.0" @agent_label "👑 queen" 2>/dev/null || true
51
+ tmux set-option -p -t "$SESSION:queen.0" allow-rename off 2>/dev/null || true
52
+ tmux set-option -p -t "$SESSION:queen.0" pane-border-style "fg=yellow" 2>/dev/null || true
53
+ ```
54
+
55
+ ### Step 3: Auto-attach to tmux session
56
+
57
+ ```bash
58
+ case "$(uname -s)" in
59
+ Darwin)
60
+ osascript -e '
61
+ tell application "Terminal"
62
+ activate
63
+ do script "tmux attach -t bo"
64
+ end tell
65
+ ' 2>/dev/null || echo "Open a new terminal and run: tmux attach -t bo"
66
+ ;;
67
+ *)
68
+ echo "Queen session started. Attach with: tmux attach -t bo"
69
+ ;;
70
+ esac
71
+ ```
72
+
73
+ On macOS, auto-opens Terminal.app and attaches to the tmux session.
74
+ On other platforms, prints the attach command for the user.
75
+
76
+ ### Step 4: Wait for startup
77
+
78
+ ```bash
79
+ for i in $(seq 1 60); do
80
+ sleep 2
81
+ if tmux capture-pane -t "$SESSION:queen" -p 2>/dev/null | grep -q 'Claude Code'; then
82
+ break
83
+ fi
84
+ done
85
+ ```
86
+
87
+ Polls for up to 120 seconds. Startup is complete when the `Claude Code` banner appears.
88
+
89
+ ### Step 5: Send initial prompt
90
+
91
+ If `$ARGUMENTS` is non-empty, pass the user's instruction directly. Otherwise, send a default instruction to sync issues.
92
+
93
+ ```bash
94
+ if [ -n "$ARGUMENTS" ]; then
95
+ INSTRUCTION="$ARGUMENTS"
96
+ else
97
+ INSTRUCTION="Sync GitHub Issues to queue.yaml and complete all tasks."
98
+ fi
99
+
100
+ tmux send-keys -t "$SESSION:queen" "$INSTRUCTION"
101
+ sleep 0.3
102
+ tmux send-keys -t "$SESSION:queen" Enter
103
+ ```
104
+
105
+ ### Step 6: Display status to user
106
+
107
+ After startup, display:
108
+
109
+ ```
110
+ Queen started (beeops). tmux session displayed.
111
+ queen window: main control loop
112
+ issue-{N}/review-{N}: Leader/Worker windows are added automatically
113
+ tmux kill-session -t bo # to stop
114
+ ```
115
+
116
+ ## Notes
117
+
118
+ - `$ARGUMENTS` contains the slash command arguments
119
+ - This command must be run in the **target project directory**
120
+ - queue.yaml generation and updates are managed by the Queen inside tmux