@hivemoot-dev/cli 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.
package/dist/index.js ADDED
@@ -0,0 +1,900 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Command, InvalidArgumentError } from "commander";
5
+
6
+ // src/config/types.ts
7
+ var CliError = class extends Error {
8
+ constructor(message, code, exitCode = 1) {
9
+ super(message);
10
+ this.code = code;
11
+ this.exitCode = exitCode;
12
+ this.name = "CliError";
13
+ }
14
+ };
15
+
16
+ // src/config/loader.ts
17
+ import yaml from "js-yaml";
18
+
19
+ // src/github/client.ts
20
+ import { execFile } from "child_process";
21
+ import { promisify } from "util";
22
+ var execFileAsync = promisify(execFile);
23
+ var ghToken;
24
+ function setGhToken(token) {
25
+ ghToken = token;
26
+ }
27
+ async function gh(args) {
28
+ try {
29
+ const opts = {
30
+ timeout: 3e4
31
+ };
32
+ if (ghToken) {
33
+ opts.env = { ...process.env, GH_TOKEN: ghToken };
34
+ }
35
+ const { stdout } = await execFileAsync("gh", args, opts);
36
+ return stdout.trim();
37
+ } catch (err) {
38
+ const error = err;
39
+ if (error.code === "ENOENT") {
40
+ throw new CliError(
41
+ "gh CLI not found. Install: https://cli.github.com",
42
+ "GH_NOT_FOUND",
43
+ 2
44
+ );
45
+ }
46
+ const stderr = error.stderr ?? error.message ?? "";
47
+ if (/gh auth login|not logged in|authentication required/i.test(stderr)) {
48
+ throw new CliError(
49
+ "Not authenticated. Pass --github-token <token>, set GITHUB_TOKEN, or run: gh auth login",
50
+ "GH_NOT_AUTHENTICATED",
51
+ 2
52
+ );
53
+ }
54
+ if (/rate.?limit|API rate limit/i.test(stderr)) {
55
+ throw new CliError(
56
+ "GitHub rate limited. Try again later.",
57
+ "RATE_LIMITED",
58
+ 3
59
+ );
60
+ }
61
+ throw new CliError(stderr || "gh command failed", "GH_ERROR", 1);
62
+ }
63
+ }
64
+
65
+ // src/config/loader.ts
66
+ var ROLE_SLUG_RE = /^[a-z][a-z0-9_]{0,49}$/;
67
+ var MAX_DESCRIPTION_LENGTH = 500;
68
+ var MAX_INSTRUCTIONS_LENGTH = 1e4;
69
+ function validateTeamConfig(raw) {
70
+ if (!raw.team) {
71
+ throw new CliError(
72
+ "No team config in .github/hivemoot.yml. Run: hivemoot init",
73
+ "NO_TEAM_CONFIG",
74
+ 1
75
+ );
76
+ }
77
+ const { team } = raw;
78
+ if (!team.roles || typeof team.roles !== "object" || Object.keys(team.roles).length === 0) {
79
+ throw new CliError(
80
+ "Config error: team.roles must contain at least one role",
81
+ "INVALID_CONFIG",
82
+ 1
83
+ );
84
+ }
85
+ const validatedRoles = {};
86
+ for (const [slug, role] of Object.entries(team.roles)) {
87
+ if (!ROLE_SLUG_RE.test(slug)) {
88
+ throw new CliError(
89
+ `Config error: invalid role slug "${slug}" \u2014 must match /^[a-z][a-z0-9_]{0,49}$/`,
90
+ "INVALID_CONFIG",
91
+ 1
92
+ );
93
+ }
94
+ if (typeof role !== "object" || role === null) {
95
+ throw new CliError(
96
+ `Config error: role "${slug}" must be an object`,
97
+ "INVALID_CONFIG",
98
+ 1
99
+ );
100
+ }
101
+ const r = role;
102
+ if (typeof r.description !== "string" || r.description.length === 0) {
103
+ throw new CliError(
104
+ `Config error: role "${slug}" is missing a description`,
105
+ "INVALID_CONFIG",
106
+ 1
107
+ );
108
+ }
109
+ if (r.description.length > MAX_DESCRIPTION_LENGTH) {
110
+ throw new CliError(
111
+ `Config error: role "${slug}" description exceeds ${MAX_DESCRIPTION_LENGTH} characters`,
112
+ "INVALID_CONFIG",
113
+ 1
114
+ );
115
+ }
116
+ if (typeof r.instructions !== "string" || r.instructions.length === 0) {
117
+ throw new CliError(
118
+ `Config error: role "${slug}" is missing instructions`,
119
+ "INVALID_CONFIG",
120
+ 1
121
+ );
122
+ }
123
+ if (r.instructions.length > MAX_INSTRUCTIONS_LENGTH) {
124
+ throw new CliError(
125
+ `Config error: role "${slug}" instructions exceeds ${MAX_INSTRUCTIONS_LENGTH} characters`,
126
+ "INVALID_CONFIG",
127
+ 1
128
+ );
129
+ }
130
+ validatedRoles[slug] = {
131
+ description: r.description,
132
+ instructions: r.instructions
133
+ };
134
+ }
135
+ return {
136
+ name: typeof team.name === "string" ? team.name : void 0,
137
+ roles: validatedRoles
138
+ };
139
+ }
140
+ async function loadTeamConfig(repo) {
141
+ let rawJson;
142
+ try {
143
+ rawJson = await gh([
144
+ "api",
145
+ `repos/${repo.owner}/${repo.repo}/contents/.github/hivemoot.yml`
146
+ ]);
147
+ } catch (err) {
148
+ if (err instanceof Error && /404|Not Found/i.test(err.message)) {
149
+ throw new CliError(
150
+ "No .github/hivemoot.yml found. Run: hivemoot init",
151
+ "CONFIG_NOT_FOUND",
152
+ 1
153
+ );
154
+ }
155
+ throw err;
156
+ }
157
+ let content;
158
+ try {
159
+ const parsed = JSON.parse(rawJson);
160
+ content = Buffer.from(parsed.content, "base64").toString("utf-8");
161
+ } catch (err) {
162
+ const detail = err instanceof Error ? `: ${err.message}` : "";
163
+ throw new CliError(
164
+ `Config error: failed to decode .github/hivemoot.yml content${detail}`,
165
+ "INVALID_CONFIG",
166
+ 1
167
+ );
168
+ }
169
+ let config;
170
+ try {
171
+ config = yaml.load(content, { schema: yaml.JSON_SCHEMA });
172
+ } catch (err) {
173
+ const detail = err instanceof Error ? `: ${err.message}` : "";
174
+ throw new CliError(
175
+ `Config error: invalid YAML in .github/hivemoot.yml${detail}`,
176
+ "INVALID_CONFIG",
177
+ 1
178
+ );
179
+ }
180
+ if (typeof config !== "object" || config === null) {
181
+ throw new CliError(
182
+ "Config error: .github/hivemoot.yml must be a YAML object",
183
+ "INVALID_CONFIG",
184
+ 1
185
+ );
186
+ }
187
+ return validateTeamConfig(config);
188
+ }
189
+
190
+ // src/github/repo.ts
191
+ var GITHUB_NAME_RE = /^[a-zA-Z0-9._-]+$/;
192
+ async function resolveRepo(repoFlag) {
193
+ if (repoFlag) {
194
+ const parts = repoFlag.split("/");
195
+ if (parts.length !== 2 || !parts[0] || !parts[1]) {
196
+ throw new CliError(
197
+ `Invalid repo format: "${repoFlag}". Expected OWNER/REPO`,
198
+ "GH_ERROR",
199
+ 1
200
+ );
201
+ }
202
+ if (!GITHUB_NAME_RE.test(parts[0]) || !GITHUB_NAME_RE.test(parts[1])) {
203
+ throw new CliError(
204
+ `Invalid repo name: "${repoFlag}". Owner and repo must match ${GITHUB_NAME_RE}`,
205
+ "GH_ERROR",
206
+ 1
207
+ );
208
+ }
209
+ return { owner: parts[0], repo: parts[1] };
210
+ }
211
+ try {
212
+ const json = await gh(["repo", "view", "--json", "owner,name"]);
213
+ let data;
214
+ try {
215
+ data = JSON.parse(json);
216
+ } catch {
217
+ throw new CliError(
218
+ "Failed to parse repo info from gh CLI. Use --repo OWNER/REPO",
219
+ "GH_ERROR",
220
+ 1
221
+ );
222
+ }
223
+ const owner = typeof data.owner === "object" && data.owner !== null ? data.owner.login : data.owner;
224
+ const name = data.name;
225
+ if (typeof owner !== "string" || !owner || typeof name !== "string" || !name) {
226
+ throw new CliError(
227
+ "Could not detect repo owner/name. Use --repo OWNER/REPO",
228
+ "NOT_GIT_REPO",
229
+ 2
230
+ );
231
+ }
232
+ return { owner, repo: name };
233
+ } catch (err) {
234
+ if (err instanceof CliError) {
235
+ throw err;
236
+ }
237
+ throw new CliError(
238
+ "Not in a git repository. Use --repo OWNER/REPO",
239
+ "NOT_GIT_REPO",
240
+ 2
241
+ );
242
+ }
243
+ }
244
+
245
+ // src/github/issues.ts
246
+ async function fetchIssues(repo) {
247
+ const json = await gh([
248
+ "issue",
249
+ "list",
250
+ "-R",
251
+ `${repo.owner}/${repo.repo}`,
252
+ "--state",
253
+ "open",
254
+ "--json",
255
+ "number,title,labels,assignees,author,comments,createdAt,updatedAt,url",
256
+ "--limit",
257
+ "200"
258
+ ]);
259
+ let parsed;
260
+ try {
261
+ parsed = JSON.parse(json);
262
+ } catch {
263
+ throw new CliError(
264
+ "Failed to parse issues response from gh CLI",
265
+ "GH_ERROR",
266
+ 1
267
+ );
268
+ }
269
+ if (!Array.isArray(parsed)) {
270
+ throw new CliError(
271
+ "Unexpected issues response format from gh CLI",
272
+ "GH_ERROR",
273
+ 1
274
+ );
275
+ }
276
+ return parsed;
277
+ }
278
+
279
+ // src/github/pulls.ts
280
+ async function fetchPulls(repo) {
281
+ const json = await gh([
282
+ "pr",
283
+ "list",
284
+ "-R",
285
+ `${repo.owner}/${repo.repo}`,
286
+ "--state",
287
+ "open",
288
+ "--json",
289
+ "number,title,state,author,labels,comments,reviews,createdAt,updatedAt,url,isDraft,reviewDecision,mergeable,statusCheckRollup,closingIssuesReferences",
290
+ "--limit",
291
+ "200"
292
+ ]);
293
+ let parsed;
294
+ try {
295
+ parsed = JSON.parse(json);
296
+ } catch {
297
+ throw new CliError(
298
+ "Failed to parse pull requests response from gh CLI",
299
+ "GH_ERROR",
300
+ 1
301
+ );
302
+ }
303
+ if (!Array.isArray(parsed)) {
304
+ throw new CliError(
305
+ "Unexpected pull requests response format from gh CLI",
306
+ "GH_ERROR",
307
+ 1
308
+ );
309
+ }
310
+ return parsed;
311
+ }
312
+
313
+ // src/github/user.ts
314
+ async function fetchCurrentUser() {
315
+ const login = await gh(["api", "user", "--jq", ".login"]);
316
+ if (!login) {
317
+ throw new CliError(
318
+ "Could not determine GitHub username. Pass --github-token <token>, set GITHUB_TOKEN, or run: gh auth login",
319
+ "GH_NOT_AUTHENTICATED",
320
+ 2
321
+ );
322
+ }
323
+ return login;
324
+ }
325
+
326
+ // src/summary/utils.ts
327
+ function hasLabel(labels, keyword) {
328
+ return labels.some(
329
+ (l) => l.name.toLowerCase().split(/[:\-_]/).some((seg) => seg === keyword)
330
+ );
331
+ }
332
+ function hasExactLabel(labels, labelName) {
333
+ return labels.some((l) => l.name.toLowerCase() === labelName.toLowerCase());
334
+ }
335
+ function daysSince(dateStr, now) {
336
+ const diff = now.getTime() - new Date(dateStr).getTime();
337
+ return Math.max(0, Math.floor(diff / (1e3 * 60 * 60 * 24)));
338
+ }
339
+ function hasCIFailure(pr) {
340
+ if (!pr.statusCheckRollup) return false;
341
+ return pr.statusCheckRollup.some((check) => {
342
+ const conclusion = check.conclusion?.toUpperCase();
343
+ const state = check.state?.toUpperCase();
344
+ return conclusion === "FAILURE" || state === "FAILURE";
345
+ });
346
+ }
347
+ function checkStatus(pr) {
348
+ if (!pr.statusCheckRollup || pr.statusCheckRollup.length === 0) return null;
349
+ const hasFailing = hasCIFailure(pr);
350
+ if (hasFailing) return "checks failing";
351
+ const hasPending = pr.statusCheckRollup.some((c) => {
352
+ const s = c.state?.toUpperCase();
353
+ return s === "PENDING" || s === "QUEUED" || s === "IN_PROGRESS";
354
+ });
355
+ if (hasPending) return "checks pending";
356
+ return "checks passing";
357
+ }
358
+ function mergeStatus(pr) {
359
+ if (pr.mergeable === "CONFLICTING") return "has conflicts";
360
+ if (pr.mergeable === "MERGEABLE") return "no conflicts";
361
+ return null;
362
+ }
363
+ function approvalCount(pr) {
364
+ if (!pr.reviews || pr.reviews.length === 0) return 0;
365
+ const latestByAuthor = /* @__PURE__ */ new Map();
366
+ for (const review of pr.reviews) {
367
+ latestByAuthor.set(review.author.login, review.state);
368
+ }
369
+ let count = 0;
370
+ for (const state of latestByAuthor.values()) {
371
+ if (state === "APPROVED") count++;
372
+ }
373
+ return count;
374
+ }
375
+
376
+ // src/summary/alerts.ts
377
+ var STALE_DISCUSSION_DAYS = 3;
378
+ var PR_WAITING_REVIEW_DAYS = 2;
379
+ function lastCommentDate(issue) {
380
+ if (issue.comments.length === 0) return null;
381
+ return issue.comments.reduce(
382
+ (latest, c) => c.createdAt > latest.createdAt ? c : latest
383
+ ).createdAt;
384
+ }
385
+ function generateAlerts(issues, prs, now = /* @__PURE__ */ new Date()) {
386
+ const alerts = [];
387
+ for (const issue of issues) {
388
+ if (!hasLabel(issue.labels, "discuss") && !hasExactLabel(issue.labels, "phase:discussion")) continue;
389
+ const lastComment = lastCommentDate(issue);
390
+ const referenceDate = lastComment ?? issue.createdAt;
391
+ const days = daysSince(referenceDate, now);
392
+ if (days >= STALE_DISCUSSION_DAYS) {
393
+ alerts.push({
394
+ icon: "\u26A0\uFE0F",
395
+ message: `#${issue.number} in discussion ${days} days, no recent comments`
396
+ });
397
+ }
398
+ }
399
+ for (const issue of issues) {
400
+ if (hasLabel(issue.labels, "blocked")) {
401
+ alerts.push({
402
+ icon: "\u{1F6D1}",
403
+ message: `#${issue.number} is blocked and needs human help`
404
+ });
405
+ }
406
+ }
407
+ const intakePRs = prs.filter((pr) => hasExactLabel(pr.labels, "implementation"));
408
+ for (const pr of intakePRs) {
409
+ if (pr.isDraft) continue;
410
+ if (pr.reviewDecision === "APPROVED" || pr.reviewDecision === "CHANGES_REQUESTED") continue;
411
+ const days = daysSince(pr.createdAt, now);
412
+ if (days >= PR_WAITING_REVIEW_DAYS) {
413
+ alerts.push({
414
+ icon: "\u26A0\uFE0F",
415
+ message: `PR #${pr.number} waiting on review ${days} days`
416
+ });
417
+ }
418
+ }
419
+ for (const pr of intakePRs) {
420
+ if (pr.isDraft) continue;
421
+ if (hasCIFailure(pr)) {
422
+ alerts.push({
423
+ icon: "\u26A0\uFE0F",
424
+ message: `PR #${pr.number} has CI failures`
425
+ });
426
+ }
427
+ }
428
+ return alerts;
429
+ }
430
+
431
+ // src/summary/builder.ts
432
+ function timeAgo(dateStr, now) {
433
+ const days = daysSince(dateStr, now);
434
+ if (days === 0) return "today";
435
+ if (days === 1) return "1 day old";
436
+ return `${days} days old`;
437
+ }
438
+ function compactChecks(raw) {
439
+ if (raw === null) return null;
440
+ if (raw === "checks failing") return "failing";
441
+ if (raw === "checks pending") return "pending";
442
+ if (raw === "checks passing") return "passing";
443
+ return raw;
444
+ }
445
+ function compactMergeable(raw) {
446
+ if (raw === null) return null;
447
+ if (raw === "has conflicts") return "conflicts";
448
+ if (raw === "no conflicts") return "clean";
449
+ return raw;
450
+ }
451
+ function classifyIssue(issue, now) {
452
+ const age = timeAgo(issue.createdAt, now);
453
+ const assigned = issue.assignees.length > 0 ? issue.assignees.map((a) => a.login).join(", ") : void 0;
454
+ const tags = issue.labels.map((l) => l.name);
455
+ const author = issue.author.login;
456
+ const comments = issue.comments.length;
457
+ const base = {
458
+ number: issue.number,
459
+ title: issue.title,
460
+ tags,
461
+ author,
462
+ comments,
463
+ age
464
+ };
465
+ if (hasLabel(issue.labels, "blocked")) {
466
+ return {
467
+ bucket: "blocked",
468
+ item: { ...base, assigned }
469
+ };
470
+ }
471
+ if (hasExactLabel(issue.labels, "phase:voting") || hasExactLabel(issue.labels, "phase:extended-voting")) {
472
+ return { bucket: "voteOn", item: base };
473
+ }
474
+ if (hasExactLabel(issue.labels, "phase:discussion")) {
475
+ return { bucket: "discuss", item: base };
476
+ }
477
+ if (hasExactLabel(issue.labels, "phase:ready-to-implement")) {
478
+ return { bucket: "implement", item: { ...base, assigned } };
479
+ }
480
+ if (hasLabel(issue.labels, "vote")) {
481
+ return { bucket: "voteOn", item: base };
482
+ }
483
+ if (hasLabel(issue.labels, "discuss")) {
484
+ return { bucket: "discuss", item: base };
485
+ }
486
+ return {
487
+ bucket: "implement",
488
+ item: { ...base, assigned }
489
+ };
490
+ }
491
+ function classifyPR(pr, now) {
492
+ const age = timeAgo(pr.createdAt, now);
493
+ const tags = pr.labels.map((l) => l.name);
494
+ const author = pr.author.login;
495
+ const comments = pr.comments.length;
496
+ const ciFailing = hasCIFailure(pr);
497
+ const checks = compactChecks(checkStatus(pr));
498
+ const merge = compactMergeable(mergeStatus(pr));
499
+ const approvals = approvalCount(pr);
500
+ if (pr.isDraft || ciFailing || pr.reviewDecision === "CHANGES_REQUESTED") {
501
+ let status2;
502
+ if (pr.isDraft) status2 = "draft";
503
+ else if (pr.reviewDecision === "CHANGES_REQUESTED") status2 = "changes-requested";
504
+ else status2 = "waiting";
505
+ return {
506
+ bucket: "addressFeedback",
507
+ item: { number: pr.number, title: pr.title, tags, author, comments, age, status: status2, checks, mergeable: merge, approvals }
508
+ };
509
+ }
510
+ const status = pr.reviewDecision === "APPROVED" ? "approved" : "waiting";
511
+ return {
512
+ bucket: "reviewPRs",
513
+ item: { number: pr.number, title: pr.title, tags, author, comments, age, status, checks, mergeable: merge, approvals }
514
+ };
515
+ }
516
+ function buildCompetitionMap(prs, currentUser) {
517
+ const map = /* @__PURE__ */ new Map();
518
+ for (const pr of prs) {
519
+ if (!hasLabel(pr.labels, "implementation")) continue;
520
+ if (pr.author.login === currentUser) continue;
521
+ for (const ref of pr.closingIssuesReferences) {
522
+ map.set(ref.number, (map.get(ref.number) ?? 0) + 1);
523
+ }
524
+ }
525
+ return map;
526
+ }
527
+ function buildSummary(repo, issues, prs, currentUser, now = /* @__PURE__ */ new Date()) {
528
+ const voteOn = [];
529
+ const discuss = [];
530
+ const implement = [];
531
+ const reviewPRs = [];
532
+ const addressFeedback = [];
533
+ for (const issue of issues) {
534
+ const { bucket, item } = classifyIssue(issue, now);
535
+ if (bucket === "voteOn") voteOn.push(item);
536
+ else if (bucket === "discuss") discuss.push(item);
537
+ else if (bucket === "implement") implement.push(item);
538
+ }
539
+ const competitionMap = buildCompetitionMap(prs, currentUser);
540
+ for (const item of implement) {
541
+ const count = competitionMap.get(item.number) ?? 0;
542
+ if (count > 0) {
543
+ item.competingPRs = count;
544
+ }
545
+ }
546
+ for (const pr of prs) {
547
+ const { bucket, item } = classifyPR(pr, now);
548
+ if (bucket === "reviewPRs") reviewPRs.push(item);
549
+ else addressFeedback.push(item);
550
+ }
551
+ const alerts = generateAlerts(issues, prs, now);
552
+ const driveDiscussion = [];
553
+ const driveImplementation = [];
554
+ const filteredDiscuss = discuss.filter((item) => {
555
+ if (item.author === currentUser) {
556
+ driveDiscussion.push(item);
557
+ return false;
558
+ }
559
+ return true;
560
+ });
561
+ const filteredVoteOn = voteOn.filter((item) => {
562
+ if (item.author === currentUser && item.tags.includes("phase:extended-voting")) {
563
+ driveDiscussion.push(item);
564
+ return false;
565
+ }
566
+ return true;
567
+ });
568
+ const filteredReviewPRs = reviewPRs.filter((item) => {
569
+ if (item.author === currentUser) {
570
+ driveImplementation.push(item);
571
+ return false;
572
+ }
573
+ return true;
574
+ });
575
+ const filteredAddressFeedback = addressFeedback.filter((item) => {
576
+ if (item.author === currentUser) {
577
+ driveImplementation.push(item);
578
+ return false;
579
+ }
580
+ return true;
581
+ });
582
+ return {
583
+ repo,
584
+ currentUser,
585
+ driveDiscussion,
586
+ driveImplementation,
587
+ voteOn: filteredVoteOn,
588
+ discuss: filteredDiscuss,
589
+ implement,
590
+ reviewPRs: filteredReviewPRs,
591
+ addressFeedback: filteredAddressFeedback,
592
+ alerts
593
+ };
594
+ }
595
+
596
+ // src/output/formatter.ts
597
+ import chalk from "chalk";
598
+ var DIVIDER_WIDTH = 50;
599
+ function sectionDivider(title, count) {
600
+ const label = ` ${title} (${count}) `;
601
+ const remaining = Math.max(0, DIVIDER_WIDTH - label.length - 2);
602
+ return chalk.dim("\u2500\u2500") + chalk.bold(label) + chalk.dim("\u2500".repeat(remaining));
603
+ }
604
+ function formatTags(tags) {
605
+ if (tags.length === 0) return "";
606
+ return " " + tags.map((t) => chalk.magenta(`[${t}]`)).join(" ");
607
+ }
608
+ function kv(key, value) {
609
+ return `${key}: ${chalk.dim(String(value))}`;
610
+ }
611
+ function formatMeta(item, sectionType, currentUser) {
612
+ const isYou = item.author === currentUser;
613
+ const authorVal = isYou ? chalk.green(`${item.author} (you)`) : chalk.dim(item.author);
614
+ const parts = [`by: ${authorVal}`];
615
+ if (sectionType === "implement") {
616
+ parts.push(kv("assigned", item.assigned ?? "--"));
617
+ parts.push(kv("comments", item.comments));
618
+ parts.push(kv("age", item.age));
619
+ if (item.competingPRs !== void 0) {
620
+ parts.push(kv("competing", item.competingPRs));
621
+ }
622
+ } else if (sectionType === "vote" || sectionType === "discuss" || sectionType === "driveDiscussion") {
623
+ parts.push(kv("comments", item.comments));
624
+ parts.push(kv("age", item.age));
625
+ } else {
626
+ if (item.status !== void 0) parts.push(kv("status", item.status));
627
+ if (item.checks !== void 0 && item.checks !== null) parts.push(kv("checks", item.checks));
628
+ if (item.mergeable !== void 0 && item.mergeable !== null) parts.push(kv("merge", item.mergeable));
629
+ if (item.approvals !== void 0) parts.push(kv("approvals", item.approvals));
630
+ parts.push(kv("comments", item.comments));
631
+ parts.push(kv("age", item.age));
632
+ }
633
+ return parts.join(" ");
634
+ }
635
+ function formatItem(item, currentUser, sectionType) {
636
+ const isYou = item.author === currentUser;
637
+ const prefix = isYou ? chalk.green("\u2605") : " ";
638
+ const num = chalk.cyan(`#${item.number}`);
639
+ const tags = formatTags(item.tags);
640
+ const titleLine = `${prefix} ${num} ${item.title}${tags}`;
641
+ const metaLine = ` ${formatMeta(item, sectionType, currentUser)}`;
642
+ return `${titleLine}
643
+ ${metaLine}`;
644
+ }
645
+ function formatSection(title, items, currentUser, sectionType, limit) {
646
+ if (items.length === 0) return "";
647
+ const displayed = limit ? items.slice(0, limit) : items;
648
+ const header = sectionDivider(title, items.length);
649
+ const itemBlocks = displayed.map((item) => formatItem(item, currentUser, sectionType));
650
+ const parts = [header, ...itemBlocks];
651
+ if (limit && items.length > limit) {
652
+ parts.push(chalk.dim(` ... and ${items.length - limit} more`));
653
+ }
654
+ return parts.join("\n\n");
655
+ }
656
+ function formatSummaryBody(summary, limit) {
657
+ const u = summary.currentUser;
658
+ const sections = [];
659
+ if (summary.alerts.length > 0) {
660
+ const alertLines = [
661
+ sectionDivider("REQUIRES YOUR ATTENTION", summary.alerts.length),
662
+ ...summary.alerts.map((a) => ` ${a.icon} ${chalk.yellow(a.message)}`)
663
+ ];
664
+ sections.push(alertLines.join("\n"));
665
+ }
666
+ sections.push(
667
+ ...[
668
+ formatSection("DRIVE THE DISCUSSION", summary.driveDiscussion, u, "driveDiscussion", limit),
669
+ formatSection("DRIVE THE IMPLEMENTATION", summary.driveImplementation, u, "driveImplementation", limit),
670
+ formatSection("VOTE ON ISSUES", summary.voteOn, u, "vote", limit),
671
+ formatSection("DISCUSS ISSUES", summary.discuss, u, "discuss", limit),
672
+ formatSection("READY TO IMPLEMENT ISSUES", summary.implement, u, "implement", limit),
673
+ formatSection("REVIEW PRs", summary.reviewPRs, u, "reviewPRs", limit),
674
+ formatSection("ADDRESS FEEDBACK PRs", summary.addressFeedback, u, "addressFeedback", limit)
675
+ ].filter(Boolean)
676
+ );
677
+ if (sections.length === 0) {
678
+ return chalk.dim(" No open issues or PRs.");
679
+ }
680
+ return sections.join("\n\n");
681
+ }
682
+ function formatBuzz(roleName, role, summary, limit) {
683
+ const lines = [
684
+ chalk.bold(`ROLE: ${roleName}`) + ` \u2014 ${role.description}`,
685
+ "",
686
+ chalk.bold("INSTRUCTIONS:"),
687
+ role.instructions.trimEnd(),
688
+ "",
689
+ `You are working on ${chalk.bold(`${summary.repo.owner}/${summary.repo.repo}`)}, logged in as ${chalk.green(summary.currentUser)}`,
690
+ "",
691
+ formatSummaryBody(summary, limit)
692
+ ];
693
+ return lines.join("\n");
694
+ }
695
+ function formatStatus(summary, limit) {
696
+ const lines = [
697
+ `You are working on ${chalk.bold(`${summary.repo.owner}/${summary.repo.repo}`)}, logged in as ${chalk.green(summary.currentUser)}`,
698
+ "",
699
+ formatSummaryBody(summary, limit)
700
+ ];
701
+ return lines.join("\n");
702
+ }
703
+ function formatRoles(teamConfig, repoFullName) {
704
+ const nameLabel = teamConfig.name ? ` (${teamConfig.name})` : "";
705
+ const lines = [
706
+ chalk.bold(`ROLES \u2014 ${repoFullName}${nameLabel}`),
707
+ ""
708
+ ];
709
+ const slugs = Object.keys(teamConfig.roles);
710
+ const maxLen = Math.max(...slugs.map((s) => s.length));
711
+ for (const slug of slugs) {
712
+ const role = teamConfig.roles[slug];
713
+ const padded = slug.padEnd(maxLen + 2);
714
+ lines.push(` ${chalk.cyan(padded)}${role.description}`);
715
+ }
716
+ return lines.join("\n");
717
+ }
718
+
719
+ // src/output/json.ts
720
+ function jsonBuzz(roleName, role, summary) {
721
+ return JSON.stringify(
722
+ {
723
+ role: {
724
+ name: roleName,
725
+ description: role.description,
726
+ instructions: role.instructions
727
+ },
728
+ summary: {
729
+ repo: `${summary.repo.owner}/${summary.repo.repo}`,
730
+ currentUser: summary.currentUser,
731
+ driveDiscussion: summary.driveDiscussion,
732
+ driveImplementation: summary.driveImplementation,
733
+ voteOn: summary.voteOn,
734
+ discuss: summary.discuss,
735
+ implement: summary.implement,
736
+ reviewPRs: summary.reviewPRs,
737
+ addressFeedback: summary.addressFeedback,
738
+ alerts: summary.alerts
739
+ }
740
+ },
741
+ null,
742
+ 2
743
+ );
744
+ }
745
+ function jsonStatus(summary) {
746
+ return JSON.stringify(
747
+ {
748
+ repo: `${summary.repo.owner}/${summary.repo.repo}`,
749
+ currentUser: summary.currentUser,
750
+ driveDiscussion: summary.driveDiscussion,
751
+ driveImplementation: summary.driveImplementation,
752
+ voteOn: summary.voteOn,
753
+ discuss: summary.discuss,
754
+ implement: summary.implement,
755
+ reviewPRs: summary.reviewPRs,
756
+ addressFeedback: summary.addressFeedback,
757
+ alerts: summary.alerts
758
+ },
759
+ null,
760
+ 2
761
+ );
762
+ }
763
+ function jsonRoles(teamConfig) {
764
+ const roles = Object.entries(teamConfig.roles).map(([slug, role]) => ({
765
+ name: slug,
766
+ description: role.description
767
+ }));
768
+ return JSON.stringify({ roles }, null, 2);
769
+ }
770
+
771
+ // src/commands/buzz.ts
772
+ async function buzzCommand(options) {
773
+ const repo = await resolveRepo(options.repo);
774
+ const [issues, prs, currentUser] = await Promise.all([
775
+ fetchIssues(repo),
776
+ fetchPulls(repo),
777
+ fetchCurrentUser()
778
+ ]);
779
+ const summary = buildSummary(repo, issues, prs, currentUser);
780
+ if (options.role) {
781
+ const teamConfig = await loadTeamConfig(repo);
782
+ if (!Object.hasOwn(teamConfig.roles, options.role)) {
783
+ const available = Object.keys(teamConfig.roles).join(", ");
784
+ throw new CliError(
785
+ `Role '${options.role}' not found. Available: ${available}. Run: hivemoot roles`,
786
+ "ROLE_NOT_FOUND",
787
+ 1
788
+ );
789
+ }
790
+ const roleConfig = teamConfig.roles[options.role];
791
+ if (options.json) {
792
+ console.log(jsonBuzz(options.role, roleConfig, summary));
793
+ } else {
794
+ console.log(formatBuzz(options.role, roleConfig, summary, options.limit));
795
+ }
796
+ } else {
797
+ if (options.json) {
798
+ console.log(jsonStatus(summary));
799
+ } else {
800
+ console.log(formatStatus(summary, options.limit));
801
+ }
802
+ }
803
+ }
804
+
805
+ // src/commands/roles.ts
806
+ async function rolesCommand(options) {
807
+ const repo = await resolveRepo(options.repo);
808
+ const teamConfig = await loadTeamConfig(repo);
809
+ if (options.json) {
810
+ console.log(jsonRoles(teamConfig));
811
+ } else {
812
+ console.log(formatRoles(teamConfig, `${repo.owner}/${repo.repo}`));
813
+ }
814
+ }
815
+
816
+ // src/commands/init.ts
817
+ async function initCommand() {
818
+ const template = `# Hivemoot team configuration
819
+ # Place this file at .github/hivemoot.yml in your repository.
820
+ #
821
+ # Roles define personas (who the agent is), not workflow.
822
+ # Workflow details belong in .agent/skills/.
823
+
824
+ team:
825
+ roles:
826
+ pm:
827
+ description: "Product manager focused on user value and clarity"
828
+ instructions: |
829
+ You think from the user's perspective.
830
+ Evaluate ideas by the problem they solve and who benefits.
831
+ Push for clear requirements and well-scoped proposals.
832
+ Ask "why does this matter?" before "how do we build it?"
833
+
834
+ engineer:
835
+ description: "Software engineer focused on clean implementation"
836
+ instructions: |
837
+ You care about code quality, patterns, and maintainability.
838
+ Favor simple, proven approaches over clever solutions.
839
+ Write clean code with good test coverage.
840
+ Build on existing conventions in the codebase.
841
+
842
+ architect:
843
+ description: "Architect focused on system design and long-term health"
844
+ instructions: |
845
+ You think about how pieces fit together.
846
+ Evaluate proposals for scalability, consistency, and technical debt.
847
+ Guard the system's boundaries and abstractions.
848
+ Push back when short-term wins create long-term problems.
849
+
850
+ qa:
851
+ description: "QA engineer focused on reliability and edge cases"
852
+ instructions: |
853
+ You think about what can go wrong.
854
+ Find edge cases, race conditions, and failure modes others miss.
855
+ Push for thorough error handling and defensive design.
856
+ Ask "what happens when this fails?" about everything.
857
+ `;
858
+ console.log(template);
859
+ }
860
+
861
+ // src/index.ts
862
+ function parseLimit(value) {
863
+ const n = parseInt(value, 10);
864
+ if (isNaN(n) || n <= 0) {
865
+ throw new InvalidArgumentError("Must be a positive integer.");
866
+ }
867
+ return n;
868
+ }
869
+ var program = new Command();
870
+ program.name("hivemoot").description("CLI for Hivemoot agents \u2014 role instructions and repo work summaries").version("0.1.0").option("--github-token <token>", "GitHub personal access token (or set GITHUB_TOKEN env var)");
871
+ program.hook("preAction", () => {
872
+ const token = program.opts().githubToken;
873
+ if (token) {
874
+ setGhToken(token);
875
+ }
876
+ });
877
+ program.command("buzz").description("Get role instructions and repo work summary (omit --role for summary only)").option("--role <role>", "Role to assume (e.g. engineer, tech_lead)").option("--json", "Output as JSON").option("--limit <n>", "Max items per section", parseLimit).option("--repo <owner/repo>", "Target repository (default: detect from git)").action(buzzCommand);
878
+ program.command("roles").description("List available roles from team config").option("--json", "Output as JSON").option("--repo <owner/repo>", "Target repository (default: detect from git)").action(rolesCommand);
879
+ program.command("init").description("Print a sample .github/hivemoot.yml template").action(initCommand);
880
+ program.exitOverride();
881
+ try {
882
+ await program.parseAsync(process.argv);
883
+ } catch (err) {
884
+ if (err instanceof CliError) {
885
+ const isJson = process.argv.includes("--json");
886
+ if (isJson) {
887
+ console.log(JSON.stringify({ error: { code: err.code, message: err.message } }, null, 2));
888
+ } else {
889
+ console.error(`Error: ${err.message}`);
890
+ }
891
+ process.exit(err.exitCode);
892
+ }
893
+ if (err instanceof Error && "exitCode" in err) {
894
+ const exitCode = err.exitCode;
895
+ process.exit(exitCode);
896
+ }
897
+ console.error("Unexpected error:", err);
898
+ process.exit(1);
899
+ }
900
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/config/types.ts","../src/config/loader.ts","../src/github/client.ts","../src/github/repo.ts","../src/github/issues.ts","../src/github/pulls.ts","../src/github/user.ts","../src/summary/utils.ts","../src/summary/alerts.ts","../src/summary/builder.ts","../src/output/formatter.ts","../src/output/json.ts","../src/commands/buzz.ts","../src/commands/roles.ts","../src/commands/init.ts"],"sourcesContent":["import { Command, InvalidArgumentError } from \"commander\";\nimport { buzzCommand } from \"./commands/buzz.js\";\nimport { rolesCommand } from \"./commands/roles.js\";\nimport { initCommand } from \"./commands/init.js\";\nimport { CliError } from \"./config/types.js\";\nimport { setGhToken } from \"./github/client.js\";\n\nfunction parseLimit(value: string): number {\n const n = parseInt(value, 10);\n if (isNaN(n) || n <= 0) {\n throw new InvalidArgumentError(\"Must be a positive integer.\");\n }\n return n;\n}\n\nconst program = new Command();\n\nprogram\n .name(\"hivemoot\")\n .description(\"CLI for Hivemoot agents — role instructions and repo work summaries\")\n .version(\"0.1.0\")\n .option(\"--github-token <token>\", \"GitHub personal access token (or set GITHUB_TOKEN env var)\");\n\nprogram.hook(\"preAction\", () => {\n const token = program.opts().githubToken as string | undefined;\n if (token) {\n setGhToken(token);\n }\n});\n\nprogram\n .command(\"buzz\")\n .description(\"Get role instructions and repo work summary (omit --role for summary only)\")\n .option(\"--role <role>\", \"Role to assume (e.g. engineer, tech_lead)\")\n .option(\"--json\", \"Output as JSON\")\n .option(\"--limit <n>\", \"Max items per section\", parseLimit)\n .option(\"--repo <owner/repo>\", \"Target repository (default: detect from git)\")\n .action(buzzCommand);\n\nprogram\n .command(\"roles\")\n .description(\"List available roles from team config\")\n .option(\"--json\", \"Output as JSON\")\n .option(\"--repo <owner/repo>\", \"Target repository (default: detect from git)\")\n .action(rolesCommand);\n\nprogram\n .command(\"init\")\n .description(\"Print a sample .github/hivemoot.yml template\")\n .action(initCommand);\n\n// Global error handler\nprogram.exitOverride();\n\ntry {\n await program.parseAsync(process.argv);\n} catch (err) {\n if (err instanceof CliError) {\n // Check if parent command requested --json output\n const isJson = process.argv.includes(\"--json\");\n if (isJson) {\n console.log(JSON.stringify({ error: { code: err.code, message: err.message } }, null, 2));\n } else {\n console.error(`Error: ${err.message}`);\n }\n process.exit(err.exitCode);\n }\n\n // Commander exits for --help, --version, etc.\n if (err instanceof Error && \"exitCode\" in err) {\n const exitCode = (err as Error & { exitCode: number }).exitCode;\n process.exit(exitCode);\n }\n\n console.error(\"Unexpected error:\", err);\n process.exit(1);\n}\n","// ── YAML Config Types ──────────────────────────────────────────────\n\nexport interface RoleConfig {\n description: string;\n instructions: string;\n}\n\nexport interface TeamConfig {\n name?: string;\n roles: Record<string, RoleConfig>;\n}\n\nexport interface HivemootConfig {\n version?: number;\n governance?: unknown;\n team?: TeamConfig;\n}\n\n// ── GitHub Data Types ──────────────────────────────────────────────\n\nexport interface GitHubIssue {\n number: number;\n title: string;\n labels: Array<{ name: string }>;\n assignees: Array<{ login: string }>;\n author: { login: string };\n comments: Array<{ createdAt: string }>;\n createdAt: string;\n updatedAt: string;\n url: string;\n}\n\nexport interface StatusCheck {\n context: string;\n state: string | undefined;\n conclusion: string | null;\n}\n\nexport interface PRReview {\n state: string;\n author: { login: string };\n}\n\nexport interface GitHubPR {\n number: number;\n title: string;\n state: string;\n author: { login: string };\n labels: Array<{ name: string }>;\n comments: Array<{ createdAt: string }>;\n reviews: PRReview[];\n createdAt: string;\n updatedAt: string;\n url: string;\n isDraft: boolean;\n reviewDecision: string;\n mergeable: string;\n statusCheckRollup: StatusCheck[] | null;\n closingIssuesReferences: Array<{ number: number }>;\n}\n\n// ── Repo Identity ──────────────────────────────────────────────────\n\nexport interface RepoRef {\n owner: string;\n repo: string;\n}\n\n// ── Summary Types ──────────────────────────────────────────────────\n\nexport interface SummaryItem {\n number: number;\n title: string;\n tags: string[];\n author: string;\n // Common fields\n comments: number;\n age: string; // \"today\" | \"1 day old\" | \"3 days old\"\n // Issue-specific\n assigned?: string; // comma-separated logins, or undefined (= unassigned)\n competingPRs?: number; // only on implement items with competing PRs\n // PR-specific\n status?: string; // \"waiting\" | \"approved\" | \"changes-requested\" | \"draft\"\n checks?: string | null; // \"passing\" | \"failing\" | \"pending\" | null\n mergeable?: string | null; // \"clean\" | \"conflicts\" | null\n approvals?: number;\n}\n\nexport interface Alert {\n icon: string;\n message: string;\n}\n\nexport interface RepoSummary {\n repo: RepoRef;\n currentUser: string;\n driveDiscussion: SummaryItem[];\n driveImplementation: SummaryItem[];\n voteOn: SummaryItem[];\n discuss: SummaryItem[];\n implement: SummaryItem[];\n reviewPRs: SummaryItem[];\n addressFeedback: SummaryItem[];\n alerts: Alert[];\n}\n\n// ── CLI Options ────────────────────────────────────────────────────\n\nexport interface BuzzOptions {\n role?: string;\n json?: boolean;\n limit?: number;\n repo?: string;\n}\n\nexport interface RolesOptions {\n json?: boolean;\n repo?: string;\n}\n\n// ── Error Types ────────────────────────────────────────────────────\n\nexport type ErrorCode =\n | \"GH_NOT_FOUND\"\n | \"GH_NOT_AUTHENTICATED\"\n | \"NOT_GIT_REPO\"\n | \"CONFIG_NOT_FOUND\"\n | \"NO_TEAM_CONFIG\"\n | \"ROLE_NOT_FOUND\"\n | \"INVALID_CONFIG\"\n | \"RATE_LIMITED\"\n | \"GH_ERROR\";\n\nexport class CliError extends Error {\n constructor(\n message: string,\n public readonly code: ErrorCode,\n public readonly exitCode: number = 1,\n ) {\n super(message);\n this.name = \"CliError\";\n }\n}\n","import yaml from \"js-yaml\";\nimport { gh } from \"../github/client.js\";\nimport type { HivemootConfig, TeamConfig, RepoRef, RoleConfig } from \"./types.js\";\nimport { CliError } from \"./types.js\";\n\nconst ROLE_SLUG_RE = /^[a-z][a-z0-9_]{0,49}$/;\nconst MAX_DESCRIPTION_LENGTH = 500;\nconst MAX_INSTRUCTIONS_LENGTH = 10_000;\n\nfunction validateTeamConfig(raw: HivemootConfig): TeamConfig {\n if (!raw.team) {\n throw new CliError(\n \"No team config in .github/hivemoot.yml. Run: hivemoot init\",\n \"NO_TEAM_CONFIG\",\n 1,\n );\n }\n\n const { team } = raw;\n\n if (!team.roles || typeof team.roles !== \"object\" || Object.keys(team.roles).length === 0) {\n throw new CliError(\n \"Config error: team.roles must contain at least one role\",\n \"INVALID_CONFIG\",\n 1,\n );\n }\n\n const validatedRoles: Record<string, RoleConfig> = {};\n\n for (const [slug, role] of Object.entries(team.roles)) {\n if (!ROLE_SLUG_RE.test(slug)) {\n throw new CliError(\n `Config error: invalid role slug \"${slug}\" — must match /^[a-z][a-z0-9_]{0,49}$/`,\n \"INVALID_CONFIG\",\n 1,\n );\n }\n\n if (typeof role !== \"object\" || role === null) {\n throw new CliError(\n `Config error: role \"${slug}\" must be an object`,\n \"INVALID_CONFIG\",\n 1,\n );\n }\n\n const r = role as unknown as Record<string, unknown>;\n\n if (typeof r.description !== \"string\" || r.description.length === 0) {\n throw new CliError(\n `Config error: role \"${slug}\" is missing a description`,\n \"INVALID_CONFIG\",\n 1,\n );\n }\n\n if (r.description.length > MAX_DESCRIPTION_LENGTH) {\n throw new CliError(\n `Config error: role \"${slug}\" description exceeds ${MAX_DESCRIPTION_LENGTH} characters`,\n \"INVALID_CONFIG\",\n 1,\n );\n }\n\n if (typeof r.instructions !== \"string\" || r.instructions.length === 0) {\n throw new CliError(\n `Config error: role \"${slug}\" is missing instructions`,\n \"INVALID_CONFIG\",\n 1,\n );\n }\n\n if (r.instructions.length > MAX_INSTRUCTIONS_LENGTH) {\n throw new CliError(\n `Config error: role \"${slug}\" instructions exceeds ${MAX_INSTRUCTIONS_LENGTH} characters`,\n \"INVALID_CONFIG\",\n 1,\n );\n }\n\n validatedRoles[slug] = {\n description: r.description,\n instructions: r.instructions,\n };\n }\n\n return {\n name: typeof team.name === \"string\" ? team.name : undefined,\n roles: validatedRoles,\n };\n}\n\nexport async function loadTeamConfig(repo: RepoRef): Promise<TeamConfig> {\n let rawJson: string;\n\n try {\n rawJson = await gh([\n \"api\",\n `repos/${repo.owner}/${repo.repo}/contents/.github/hivemoot.yml`,\n ]);\n } catch (err) {\n if (err instanceof Error && /404|Not Found/i.test(err.message)) {\n throw new CliError(\n \"No .github/hivemoot.yml found. Run: hivemoot init\",\n \"CONFIG_NOT_FOUND\",\n 1,\n );\n }\n throw err;\n }\n\n let content: string;\n try {\n const parsed = JSON.parse(rawJson);\n content = Buffer.from(parsed.content, \"base64\").toString(\"utf-8\");\n } catch (err) {\n const detail = err instanceof Error ? `: ${err.message}` : \"\";\n throw new CliError(\n `Config error: failed to decode .github/hivemoot.yml content${detail}`,\n \"INVALID_CONFIG\",\n 1,\n );\n }\n\n let config: HivemootConfig;\n try {\n config = yaml.load(content, { schema: yaml.JSON_SCHEMA }) as HivemootConfig;\n } catch (err) {\n const detail = err instanceof Error ? `: ${err.message}` : \"\";\n throw new CliError(\n `Config error: invalid YAML in .github/hivemoot.yml${detail}`,\n \"INVALID_CONFIG\",\n 1,\n );\n }\n\n if (typeof config !== \"object\" || config === null) {\n throw new CliError(\n \"Config error: .github/hivemoot.yml must be a YAML object\",\n \"INVALID_CONFIG\",\n 1,\n );\n }\n\n return validateTeamConfig(config);\n}\n","import { execFile } from \"child_process\";\nimport { promisify } from \"util\";\nimport { CliError } from \"../config/types.js\";\n\nconst execFileAsync = promisify(execFile);\n\nlet ghToken: string | undefined;\n\n/** Set the GitHub token used for all subsequent `gh` calls. */\nexport function setGhToken(token: string): void {\n ghToken = token;\n}\n\n/**\n * Execute a `gh` CLI command and return stdout.\n * All GitHub I/O goes through this single function.\n */\nexport async function gh(args: string[]): Promise<string> {\n try {\n const opts: { timeout: number; env?: NodeJS.ProcessEnv } = {\n timeout: 30_000,\n };\n if (ghToken) {\n opts.env = { ...process.env, GH_TOKEN: ghToken };\n }\n const { stdout } = await execFileAsync(\"gh\", args, opts);\n return stdout.trim();\n } catch (err: unknown) {\n const error = err as NodeJS.ErrnoException & {\n stderr?: string;\n code?: string | number;\n };\n\n if (error.code === \"ENOENT\") {\n throw new CliError(\n \"gh CLI not found. Install: https://cli.github.com\",\n \"GH_NOT_FOUND\",\n 2,\n );\n }\n\n const stderr = error.stderr ?? error.message ?? \"\";\n\n if (/gh auth login|not logged in|authentication required/i.test(stderr)) {\n throw new CliError(\n \"Not authenticated. Pass --github-token <token>, set GITHUB_TOKEN, or run: gh auth login\",\n \"GH_NOT_AUTHENTICATED\",\n 2,\n );\n }\n\n if (/rate.?limit|API rate limit/i.test(stderr)) {\n throw new CliError(\n \"GitHub rate limited. Try again later.\",\n \"RATE_LIMITED\",\n 3,\n );\n }\n\n throw new CliError(stderr || \"gh command failed\", \"GH_ERROR\", 1);\n }\n}\n","import { CliError, type RepoRef } from \"../config/types.js\";\nimport { gh } from \"./client.js\";\n\nconst GITHUB_NAME_RE = /^[a-zA-Z0-9._-]+$/;\n\nexport async function resolveRepo(repoFlag?: string): Promise<RepoRef> {\n if (repoFlag) {\n const parts = repoFlag.split(\"/\");\n if (parts.length !== 2 || !parts[0] || !parts[1]) {\n throw new CliError(\n `Invalid repo format: \"${repoFlag}\". Expected OWNER/REPO`,\n \"GH_ERROR\",\n 1,\n );\n }\n if (!GITHUB_NAME_RE.test(parts[0]) || !GITHUB_NAME_RE.test(parts[1])) {\n throw new CliError(\n `Invalid repo name: \"${repoFlag}\". Owner and repo must match ${GITHUB_NAME_RE}`,\n \"GH_ERROR\",\n 1,\n );\n }\n return { owner: parts[0], repo: parts[1] };\n }\n\n try {\n const json = await gh([\"repo\", \"view\", \"--json\", \"owner,name\"]);\n let data: { owner?: { login?: string } | string; name?: string };\n try {\n data = JSON.parse(json);\n } catch {\n throw new CliError(\n \"Failed to parse repo info from gh CLI. Use --repo OWNER/REPO\",\n \"GH_ERROR\",\n 1,\n );\n }\n // gh returns owner as an object { login: \"...\" } not a plain string\n const owner =\n typeof data.owner === \"object\" && data.owner !== null\n ? data.owner.login\n : data.owner;\n const name = data.name;\n if (typeof owner !== \"string\" || !owner || typeof name !== \"string\" || !name) {\n throw new CliError(\n \"Could not detect repo owner/name. Use --repo OWNER/REPO\",\n \"NOT_GIT_REPO\",\n 2,\n );\n }\n return { owner, repo: name };\n } catch (err) {\n if (err instanceof CliError) {\n throw err;\n }\n throw new CliError(\n \"Not in a git repository. Use --repo OWNER/REPO\",\n \"NOT_GIT_REPO\",\n 2,\n );\n }\n}\n","import type { GitHubIssue, RepoRef } from \"../config/types.js\";\nimport { CliError } from \"../config/types.js\";\nimport { gh } from \"./client.js\";\n\nexport async function fetchIssues(repo: RepoRef): Promise<GitHubIssue[]> {\n const json = await gh([\n \"issue\",\n \"list\",\n \"-R\",\n `${repo.owner}/${repo.repo}`,\n \"--state\",\n \"open\",\n \"--json\",\n \"number,title,labels,assignees,author,comments,createdAt,updatedAt,url\",\n \"--limit\",\n \"200\",\n ]);\n let parsed: unknown;\n try {\n parsed = JSON.parse(json);\n } catch {\n throw new CliError(\n \"Failed to parse issues response from gh CLI\",\n \"GH_ERROR\",\n 1,\n );\n }\n if (!Array.isArray(parsed)) {\n throw new CliError(\n \"Unexpected issues response format from gh CLI\",\n \"GH_ERROR\",\n 1,\n );\n }\n return parsed as GitHubIssue[];\n}\n","import type { GitHubPR, RepoRef } from \"../config/types.js\";\nimport { CliError } from \"../config/types.js\";\nimport { gh } from \"./client.js\";\n\nexport async function fetchPulls(repo: RepoRef): Promise<GitHubPR[]> {\n const json = await gh([\n \"pr\",\n \"list\",\n \"-R\",\n `${repo.owner}/${repo.repo}`,\n \"--state\",\n \"open\",\n \"--json\",\n \"number,title,state,author,labels,comments,reviews,createdAt,updatedAt,url,isDraft,reviewDecision,mergeable,statusCheckRollup,closingIssuesReferences\",\n \"--limit\",\n \"200\",\n ]);\n let parsed: unknown;\n try {\n parsed = JSON.parse(json);\n } catch {\n throw new CliError(\n \"Failed to parse pull requests response from gh CLI\",\n \"GH_ERROR\",\n 1,\n );\n }\n if (!Array.isArray(parsed)) {\n throw new CliError(\n \"Unexpected pull requests response format from gh CLI\",\n \"GH_ERROR\",\n 1,\n );\n }\n return parsed as GitHubPR[];\n}\n","import { gh } from \"./client.js\";\nimport { CliError } from \"../config/types.js\";\n\n/**\n * Return the login of the currently authenticated GitHub user.\n */\nexport async function fetchCurrentUser(): Promise<string> {\n const login = await gh([\"api\", \"user\", \"--jq\", \".login\"]);\n if (!login) {\n throw new CliError(\n \"Could not determine GitHub username. Pass --github-token <token>, set GITHUB_TOKEN, or run: gh auth login\",\n \"GH_NOT_AUTHENTICATED\",\n 2,\n );\n }\n return login;\n}\n","import type { GitHubPR } from \"../config/types.js\";\n\nexport function hasLabel(labels: Array<{ name: string }>, keyword: string): boolean {\n return labels.some((l) =>\n l.name.toLowerCase().split(/[:\\-_]/).some((seg) => seg === keyword),\n );\n}\n\nexport function hasExactLabel(labels: Array<{ name: string }>, labelName: string): boolean {\n return labels.some((l) => l.name.toLowerCase() === labelName.toLowerCase());\n}\n\nexport function daysSince(dateStr: string, now: Date): number {\n const diff = now.getTime() - new Date(dateStr).getTime();\n return Math.max(0, Math.floor(diff / (1000 * 60 * 60 * 24)));\n}\n\nexport function hasCIFailure(pr: GitHubPR): boolean {\n if (!pr.statusCheckRollup) return false;\n return pr.statusCheckRollup.some((check) => {\n const conclusion = check.conclusion?.toUpperCase();\n const state = check.state?.toUpperCase();\n return conclusion === \"FAILURE\" || state === \"FAILURE\";\n });\n}\n\n/**\n * Summarize all status checks into a single label.\n * Returns null when there are no checks to report.\n */\nexport function checkStatus(pr: GitHubPR): string | null {\n if (!pr.statusCheckRollup || pr.statusCheckRollup.length === 0) return null;\n\n const hasFailing = hasCIFailure(pr);\n if (hasFailing) return \"checks failing\";\n\n const hasPending = pr.statusCheckRollup.some((c) => {\n const s = c.state?.toUpperCase();\n return s === \"PENDING\" || s === \"QUEUED\" || s === \"IN_PROGRESS\";\n });\n if (hasPending) return \"checks pending\";\n\n return \"checks passing\";\n}\n\n/**\n * Return a merge-conflict label, or null if no conflict info.\n */\nexport function mergeStatus(pr: GitHubPR): string | null {\n if (pr.mergeable === \"CONFLICTING\") return \"has conflicts\";\n if (pr.mergeable === \"MERGEABLE\") return \"no conflicts\";\n return null;\n}\n\n/**\n * Count unique approvals (latest review per author).\n */\nexport function approvalCount(pr: GitHubPR): number {\n if (!pr.reviews || pr.reviews.length === 0) return 0;\n const latestByAuthor = new Map<string, string>();\n for (const review of pr.reviews) {\n latestByAuthor.set(review.author.login, review.state);\n }\n let count = 0;\n for (const state of latestByAuthor.values()) {\n if (state === \"APPROVED\") count++;\n }\n return count;\n}\n","import type { GitHubIssue, GitHubPR, Alert } from \"../config/types.js\";\nimport { hasLabel, hasExactLabel, daysSince, hasCIFailure } from \"./utils.js\";\n\nconst STALE_DISCUSSION_DAYS = 3;\nconst PR_WAITING_REVIEW_DAYS = 2;\n\nfunction lastCommentDate(issue: GitHubIssue): string | null {\n if (issue.comments.length === 0) return null;\n return issue.comments.reduce((latest, c) =>\n c.createdAt > latest.createdAt ? c : latest,\n ).createdAt;\n}\n\nexport function generateAlerts(\n issues: GitHubIssue[],\n prs: GitHubPR[],\n now: Date = new Date(),\n): Alert[] {\n const alerts: Alert[] = [];\n\n // Stale discussions: discuss-labeled issues with no recent comments\n for (const issue of issues) {\n if (!hasLabel(issue.labels, \"discuss\") && !hasExactLabel(issue.labels, \"phase:discussion\")) continue;\n const lastComment = lastCommentDate(issue);\n const referenceDate = lastComment ?? issue.createdAt;\n const days = daysSince(referenceDate, now);\n if (days >= STALE_DISCUSSION_DAYS) {\n alerts.push({\n icon: \"\\u26a0\\ufe0f\",\n message: `#${issue.number} in discussion ${days} days, no recent comments`,\n });\n }\n }\n\n // Blocked issues: need human intervention\n for (const issue of issues) {\n if (hasLabel(issue.labels, \"blocked\")) {\n alerts.push({\n icon: \"\\ud83d\\uded1\",\n message: `#${issue.number} is blocked and needs human help`,\n });\n }\n }\n\n // Only PRs in the governance intake (with \"implementation\" label) generate attention items\n const intakePRs = prs.filter((pr) => hasExactLabel(pr.labels, \"implementation\"));\n\n // PRs waiting on review too long (skip approved/changes-requested/drafts)\n for (const pr of intakePRs) {\n if (pr.isDraft) continue;\n if (pr.reviewDecision === \"APPROVED\" || pr.reviewDecision === \"CHANGES_REQUESTED\") continue;\n const days = daysSince(pr.createdAt, now);\n if (days >= PR_WAITING_REVIEW_DAYS) {\n alerts.push({\n icon: \"\\u26a0\\ufe0f\",\n message: `PR #${pr.number} waiting on review ${days} days`,\n });\n }\n }\n\n // CI failures on non-draft PRs\n for (const pr of intakePRs) {\n if (pr.isDraft) continue;\n if (hasCIFailure(pr)) {\n alerts.push({\n icon: \"\\u26a0\\ufe0f\",\n message: `PR #${pr.number} has CI failures`,\n });\n }\n }\n\n return alerts;\n}\n","import type {\n GitHubIssue,\n GitHubPR,\n RepoRef,\n RepoSummary,\n SummaryItem,\n} from \"../config/types.js\";\nimport { generateAlerts } from \"./alerts.js\";\nimport { hasLabel, hasExactLabel, daysSince, hasCIFailure, checkStatus, mergeStatus, approvalCount } from \"./utils.js\";\n\nfunction timeAgo(dateStr: string, now: Date): string {\n const days = daysSince(dateStr, now);\n if (days === 0) return \"today\";\n if (days === 1) return \"1 day old\";\n return `${days} days old`;\n}\n\n/** Map verbose check labels to compact values for structured output. */\nfunction compactChecks(raw: string | null): string | null {\n if (raw === null) return null;\n if (raw === \"checks failing\") return \"failing\";\n if (raw === \"checks pending\") return \"pending\";\n if (raw === \"checks passing\") return \"passing\";\n return raw;\n}\n\n/** Map verbose merge labels to compact values for structured output. */\nfunction compactMergeable(raw: string | null): string | null {\n if (raw === null) return null;\n if (raw === \"has conflicts\") return \"conflicts\";\n if (raw === \"no conflicts\") return \"clean\";\n return raw;\n}\n\nfunction classifyIssue(\n issue: GitHubIssue,\n now: Date,\n): { bucket: \"voteOn\" | \"discuss\" | \"implement\" | \"blocked\"; item: SummaryItem } {\n const age = timeAgo(issue.createdAt, now);\n const assigned =\n issue.assignees.length > 0\n ? issue.assignees.map((a) => a.login).join(\", \")\n : undefined;\n\n const tags = issue.labels.map((l) => l.name);\n const author = issue.author.login;\n const comments = issue.comments.length;\n\n const base: SummaryItem = {\n number: issue.number,\n title: issue.title,\n tags,\n author,\n comments,\n age,\n };\n\n // Blocked issues are excluded from all actionable buckets\n if (hasLabel(issue.labels, \"blocked\")) {\n return {\n bucket: \"blocked\",\n item: { ...base, assigned },\n };\n }\n\n // Bot governance labels (exact match)\n if (hasExactLabel(issue.labels, \"phase:voting\") || hasExactLabel(issue.labels, \"phase:extended-voting\")) {\n return { bucket: \"voteOn\", item: base };\n }\n if (hasExactLabel(issue.labels, \"phase:discussion\")) {\n return { bucket: \"discuss\", item: base };\n }\n if (hasExactLabel(issue.labels, \"phase:ready-to-implement\")) {\n return { bucket: \"implement\", item: { ...base, assigned } };\n }\n\n // Keyword fallback (repos without the bot)\n if (hasLabel(issue.labels, \"vote\")) {\n return { bucket: \"voteOn\", item: base };\n }\n if (hasLabel(issue.labels, \"discuss\")) {\n return { bucket: \"discuss\", item: base };\n }\n\n return {\n bucket: \"implement\",\n item: { ...base, assigned },\n };\n}\n\nfunction classifyPR(\n pr: GitHubPR,\n now: Date,\n): { bucket: \"reviewPRs\" | \"addressFeedback\"; item: SummaryItem } {\n const age = timeAgo(pr.createdAt, now);\n const tags = pr.labels.map((l) => l.name);\n const author = pr.author.login;\n const comments = pr.comments.length;\n const ciFailing = hasCIFailure(pr);\n const checks = compactChecks(checkStatus(pr));\n const merge = compactMergeable(mergeStatus(pr));\n const approvals = approvalCount(pr);\n\n if (pr.isDraft || ciFailing || pr.reviewDecision === \"CHANGES_REQUESTED\") {\n let status: string;\n if (pr.isDraft) status = \"draft\";\n else if (pr.reviewDecision === \"CHANGES_REQUESTED\") status = \"changes-requested\";\n else status = \"waiting\";\n\n return {\n bucket: \"addressFeedback\",\n item: { number: pr.number, title: pr.title, tags, author, comments, age, status, checks, mergeable: merge, approvals },\n };\n }\n\n const status = pr.reviewDecision === \"APPROVED\" ? \"approved\" : \"waiting\";\n\n return {\n bucket: \"reviewPRs\",\n item: { number: pr.number, title: pr.title, tags, author, comments, age, status, checks, mergeable: merge, approvals },\n };\n}\n\nfunction buildCompetitionMap(prs: GitHubPR[], currentUser: string): Map<number, number> {\n const map = new Map<number, number>();\n for (const pr of prs) {\n if (!hasLabel(pr.labels, \"implementation\")) continue;\n if (pr.author.login === currentUser) continue;\n for (const ref of pr.closingIssuesReferences) {\n map.set(ref.number, (map.get(ref.number) ?? 0) + 1);\n }\n }\n return map;\n}\n\nexport function buildSummary(\n repo: RepoRef,\n issues: GitHubIssue[],\n prs: GitHubPR[],\n currentUser: string,\n now: Date = new Date(),\n): RepoSummary {\n const voteOn: SummaryItem[] = [];\n const discuss: SummaryItem[] = [];\n const implement: SummaryItem[] = [];\n const reviewPRs: SummaryItem[] = [];\n const addressFeedback: SummaryItem[] = [];\n\n for (const issue of issues) {\n const { bucket, item } = classifyIssue(issue, now);\n if (bucket === \"voteOn\") voteOn.push(item);\n else if (bucket === \"discuss\") discuss.push(item);\n else if (bucket === \"implement\") implement.push(item);\n // \"blocked\" issues intentionally excluded from all buckets\n }\n\n // Annotate implement items with competing PR counts\n const competitionMap = buildCompetitionMap(prs, currentUser);\n for (const item of implement) {\n const count = competitionMap.get(item.number) ?? 0;\n if (count > 0) {\n item.competingPRs = count;\n }\n }\n\n for (const pr of prs) {\n const { bucket, item } = classifyPR(pr, now);\n if (bucket === \"reviewPRs\") reviewPRs.push(item);\n else addressFeedback.push(item);\n }\n\n const alerts = generateAlerts(issues, prs, now);\n\n // Extract authored items into \"drive\" buckets so the agent knows what it owns\n const driveDiscussion: SummaryItem[] = [];\n const driveImplementation: SummaryItem[] = [];\n\n const filteredDiscuss = discuss.filter((item) => {\n if (item.author === currentUser) { driveDiscussion.push(item); return false; }\n return true;\n });\n\n const filteredVoteOn = voteOn.filter((item) => {\n if (item.author === currentUser && item.tags.includes(\"phase:extended-voting\")) {\n driveDiscussion.push(item);\n return false;\n }\n return true;\n });\n\n const filteredReviewPRs = reviewPRs.filter((item) => {\n if (item.author === currentUser) { driveImplementation.push(item); return false; }\n return true;\n });\n\n const filteredAddressFeedback = addressFeedback.filter((item) => {\n if (item.author === currentUser) { driveImplementation.push(item); return false; }\n return true;\n });\n\n return {\n repo,\n currentUser,\n driveDiscussion,\n driveImplementation,\n voteOn: filteredVoteOn,\n discuss: filteredDiscuss,\n implement,\n reviewPRs: filteredReviewPRs,\n addressFeedback: filteredAddressFeedback,\n alerts,\n };\n}\n","import chalk from \"chalk\";\nimport type { RepoSummary, RoleConfig, SummaryItem, TeamConfig } from \"../config/types.js\";\n\nconst DIVIDER_WIDTH = 50;\n\n// Section types determine which metadata keys appear on the second line\ntype SectionType = \"vote\" | \"discuss\" | \"implement\" | \"reviewPRs\" | \"addressFeedback\" | \"driveDiscussion\" | \"driveImplementation\";\n\nfunction sectionDivider(title: string, count: number): string {\n const label = ` ${title} (${count}) `;\n const remaining = Math.max(0, DIVIDER_WIDTH - label.length - 2);\n return chalk.dim(\"──\") + chalk.bold(label) + chalk.dim(\"─\".repeat(remaining));\n}\n\nfunction formatTags(tags: string[]): string {\n if (tags.length === 0) return \"\";\n return \" \" + tags.map((t) => chalk.magenta(`[${t}]`)).join(\" \");\n}\n\nfunction kv(key: string, value: string | number): string {\n return `${key}: ${chalk.dim(String(value))}`;\n}\n\nfunction formatMeta(item: SummaryItem, sectionType: SectionType, currentUser: string): string {\n const isYou = item.author === currentUser;\n const authorVal = isYou ? chalk.green(`${item.author} (you)`) : chalk.dim(item.author);\n const parts: string[] = [`by: ${authorVal}`];\n\n if (sectionType === \"implement\") {\n parts.push(kv(\"assigned\", item.assigned ?? \"--\"));\n parts.push(kv(\"comments\", item.comments));\n parts.push(kv(\"age\", item.age));\n if (item.competingPRs !== undefined) {\n parts.push(kv(\"competing\", item.competingPRs));\n }\n } else if (sectionType === \"vote\" || sectionType === \"discuss\" || sectionType === \"driveDiscussion\") {\n parts.push(kv(\"comments\", item.comments));\n parts.push(kv(\"age\", item.age));\n } else {\n // PR sections: reviewPRs, addressFeedback, driveImplementation\n if (item.status !== undefined) parts.push(kv(\"status\", item.status));\n if (item.checks !== undefined && item.checks !== null) parts.push(kv(\"checks\", item.checks));\n if (item.mergeable !== undefined && item.mergeable !== null) parts.push(kv(\"merge\", item.mergeable));\n if (item.approvals !== undefined) parts.push(kv(\"approvals\", item.approvals));\n parts.push(kv(\"comments\", item.comments));\n parts.push(kv(\"age\", item.age));\n }\n\n return parts.join(\" \");\n}\n\nfunction formatItem(item: SummaryItem, currentUser: string, sectionType: SectionType): string {\n const isYou = item.author === currentUser;\n const prefix = isYou ? chalk.green(\"★\") : \" \";\n const num = chalk.cyan(`#${item.number}`);\n const tags = formatTags(item.tags);\n\n const titleLine = `${prefix} ${num} ${item.title}${tags}`;\n const metaLine = ` ${formatMeta(item, sectionType, currentUser)}`;\n\n return `${titleLine}\\n${metaLine}`;\n}\n\nfunction formatSection(\n title: string,\n items: SummaryItem[],\n currentUser: string,\n sectionType: SectionType,\n limit?: number,\n): string {\n if (items.length === 0) return \"\";\n\n const displayed = limit ? items.slice(0, limit) : items;\n const header = sectionDivider(title, items.length);\n const itemBlocks = displayed.map((item) => formatItem(item, currentUser, sectionType));\n\n const parts = [header, ...itemBlocks];\n\n if (limit && items.length > limit) {\n parts.push(chalk.dim(` ... and ${items.length - limit} more`));\n }\n\n return parts.join(\"\\n\\n\");\n}\n\nfunction formatSummaryBody(summary: RepoSummary, limit?: number): string {\n const u = summary.currentUser;\n const sections: string[] = [];\n\n // Attention items at the top\n if (summary.alerts.length > 0) {\n const alertLines = [\n sectionDivider(\"REQUIRES YOUR ATTENTION\", summary.alerts.length),\n ...summary.alerts.map((a) => ` ${a.icon} ${chalk.yellow(a.message)}`),\n ];\n sections.push(alertLines.join(\"\\n\"));\n }\n\n sections.push(\n ...[\n formatSection(\"DRIVE THE DISCUSSION\", summary.driveDiscussion, u, \"driveDiscussion\", limit),\n formatSection(\"DRIVE THE IMPLEMENTATION\", summary.driveImplementation, u, \"driveImplementation\", limit),\n formatSection(\"VOTE ON ISSUES\", summary.voteOn, u, \"vote\", limit),\n formatSection(\"DISCUSS ISSUES\", summary.discuss, u, \"discuss\", limit),\n formatSection(\"READY TO IMPLEMENT ISSUES\", summary.implement, u, \"implement\", limit),\n formatSection(\"REVIEW PRs\", summary.reviewPRs, u, \"reviewPRs\", limit),\n formatSection(\"ADDRESS FEEDBACK PRs\", summary.addressFeedback, u, \"addressFeedback\", limit),\n ].filter(Boolean),\n );\n\n if (sections.length === 0) {\n return chalk.dim(\" No open issues or PRs.\");\n }\n\n return sections.join(\"\\n\\n\");\n}\n\nexport function formatBuzz(\n roleName: string,\n role: RoleConfig,\n summary: RepoSummary,\n limit?: number,\n): string {\n const lines = [\n chalk.bold(`ROLE: ${roleName}`) + ` — ${role.description}`,\n \"\",\n chalk.bold(\"INSTRUCTIONS:\"),\n role.instructions.trimEnd(),\n \"\",\n `You are working on ${chalk.bold(`${summary.repo.owner}/${summary.repo.repo}`)}, logged in as ${chalk.green(summary.currentUser)}`,\n \"\",\n formatSummaryBody(summary, limit),\n ];\n\n return lines.join(\"\\n\");\n}\n\nexport function formatStatus(summary: RepoSummary, limit?: number): string {\n const lines = [\n `You are working on ${chalk.bold(`${summary.repo.owner}/${summary.repo.repo}`)}, logged in as ${chalk.green(summary.currentUser)}`,\n \"\",\n formatSummaryBody(summary, limit),\n ];\n\n return lines.join(\"\\n\");\n}\n\nexport function formatRoles(teamConfig: TeamConfig, repoFullName: string): string {\n const nameLabel = teamConfig.name ? ` (${teamConfig.name})` : \"\";\n const lines = [\n chalk.bold(`ROLES — ${repoFullName}${nameLabel}`),\n \"\",\n ];\n\n const slugs = Object.keys(teamConfig.roles);\n const maxLen = Math.max(...slugs.map((s) => s.length));\n\n for (const slug of slugs) {\n const role = teamConfig.roles[slug];\n const padded = slug.padEnd(maxLen + 2);\n lines.push(` ${chalk.cyan(padded)}${role.description}`);\n }\n\n return lines.join(\"\\n\");\n}\n","import type { RepoSummary, RoleConfig, TeamConfig } from \"../config/types.js\";\n\nexport function jsonBuzz(\n roleName: string,\n role: RoleConfig,\n summary: RepoSummary,\n): string {\n return JSON.stringify(\n {\n role: {\n name: roleName,\n description: role.description,\n instructions: role.instructions,\n },\n summary: {\n repo: `${summary.repo.owner}/${summary.repo.repo}`,\n currentUser: summary.currentUser,\n driveDiscussion: summary.driveDiscussion,\n driveImplementation: summary.driveImplementation,\n voteOn: summary.voteOn,\n discuss: summary.discuss,\n implement: summary.implement,\n reviewPRs: summary.reviewPRs,\n addressFeedback: summary.addressFeedback,\n alerts: summary.alerts,\n },\n },\n null,\n 2,\n );\n}\n\nexport function jsonStatus(summary: RepoSummary): string {\n return JSON.stringify(\n {\n repo: `${summary.repo.owner}/${summary.repo.repo}`,\n currentUser: summary.currentUser,\n driveDiscussion: summary.driveDiscussion,\n driveImplementation: summary.driveImplementation,\n voteOn: summary.voteOn,\n discuss: summary.discuss,\n implement: summary.implement,\n reviewPRs: summary.reviewPRs,\n addressFeedback: summary.addressFeedback,\n alerts: summary.alerts,\n },\n null,\n 2,\n );\n}\n\nexport function jsonRoles(teamConfig: TeamConfig): string {\n const roles = Object.entries(teamConfig.roles).map(([slug, role]) => ({\n name: slug,\n description: role.description,\n }));\n\n return JSON.stringify({ roles }, null, 2);\n}\n","import { CliError, type BuzzOptions } from \"../config/types.js\";\nimport { loadTeamConfig } from \"../config/loader.js\";\nimport { resolveRepo } from \"../github/repo.js\";\nimport { fetchIssues } from \"../github/issues.js\";\nimport { fetchPulls } from \"../github/pulls.js\";\nimport { fetchCurrentUser } from \"../github/user.js\";\nimport { buildSummary } from \"../summary/builder.js\";\nimport { formatBuzz, formatStatus } from \"../output/formatter.js\";\nimport { jsonBuzz, jsonStatus } from \"../output/json.js\";\n\nexport async function buzzCommand(options: BuzzOptions): Promise<void> {\n const repo = await resolveRepo(options.repo);\n\n const [issues, prs, currentUser] = await Promise.all([\n fetchIssues(repo),\n fetchPulls(repo),\n fetchCurrentUser(),\n ]);\n\n const summary = buildSummary(repo, issues, prs, currentUser);\n\n if (options.role) {\n const teamConfig = await loadTeamConfig(repo);\n if (!Object.hasOwn(teamConfig.roles, options.role)) {\n const available = Object.keys(teamConfig.roles).join(\", \");\n throw new CliError(\n `Role '${options.role}' not found. Available: ${available}. Run: hivemoot roles`,\n \"ROLE_NOT_FOUND\",\n 1,\n );\n }\n const roleConfig = teamConfig.roles[options.role];\n\n if (options.json) {\n console.log(jsonBuzz(options.role, roleConfig, summary));\n } else {\n console.log(formatBuzz(options.role, roleConfig, summary, options.limit));\n }\n } else {\n if (options.json) {\n console.log(jsonStatus(summary));\n } else {\n console.log(formatStatus(summary, options.limit));\n }\n }\n}\n","import type { RolesOptions } from \"../config/types.js\";\nimport { loadTeamConfig } from \"../config/loader.js\";\nimport { resolveRepo } from \"../github/repo.js\";\nimport { formatRoles } from \"../output/formatter.js\";\nimport { jsonRoles } from \"../output/json.js\";\n\nexport async function rolesCommand(options: RolesOptions): Promise<void> {\n const repo = await resolveRepo(options.repo);\n const teamConfig = await loadTeamConfig(repo);\n\n if (options.json) {\n console.log(jsonRoles(teamConfig));\n } else {\n console.log(formatRoles(teamConfig, `${repo.owner}/${repo.repo}`));\n }\n}\n","export async function initCommand(): Promise<void> {\n const template = `# Hivemoot team configuration\n# Place this file at .github/hivemoot.yml in your repository.\n#\n# Roles define personas (who the agent is), not workflow.\n# Workflow details belong in .agent/skills/.\n\nteam:\n roles:\n pm:\n description: \"Product manager focused on user value and clarity\"\n instructions: |\n You think from the user's perspective.\n Evaluate ideas by the problem they solve and who benefits.\n Push for clear requirements and well-scoped proposals.\n Ask \"why does this matter?\" before \"how do we build it?\"\n\n engineer:\n description: \"Software engineer focused on clean implementation\"\n instructions: |\n You care about code quality, patterns, and maintainability.\n Favor simple, proven approaches over clever solutions.\n Write clean code with good test coverage.\n Build on existing conventions in the codebase.\n\n architect:\n description: \"Architect focused on system design and long-term health\"\n instructions: |\n You think about how pieces fit together.\n Evaluate proposals for scalability, consistency, and technical debt.\n Guard the system's boundaries and abstractions.\n Push back when short-term wins create long-term problems.\n\n qa:\n description: \"QA engineer focused on reliability and edge cases\"\n instructions: |\n You think about what can go wrong.\n Find edge cases, race conditions, and failure modes others miss.\n Push for thorough error handling and defensive design.\n Ask \"what happens when this fails?\" about everything.\n`;\n\n console.log(template);\n}\n"],"mappings":"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","names":["status"]}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@hivemoot-dev/cli",
3
+ "version": "0.1.0",
4
+ "description": "CLI for Hivemoot agents — role instructions and repo work summaries",
5
+ "type": "module",
6
+ "bin": {
7
+ "hivemoot": "./dist/index.js"
8
+ },
9
+ "publishConfig": {
10
+ "access": "public"
11
+ },
12
+ "files": [
13
+ "dist"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsup",
17
+ "dev": "tsup --watch",
18
+ "typecheck": "tsc --noEmit",
19
+ "test": "vitest run",
20
+ "test:watch": "vitest",
21
+ "lint": "tsc --noEmit"
22
+ },
23
+ "engines": {
24
+ "node": ">=20"
25
+ },
26
+ "dependencies": {
27
+ "chalk": "^5.4.1",
28
+ "commander": "^12.1.0",
29
+ "js-yaml": "^4.1.0"
30
+ },
31
+ "devDependencies": {
32
+ "@types/js-yaml": "^4.0.9",
33
+ "@types/node": "^20.17.0",
34
+ "tsup": "^8.3.0",
35
+ "typescript": "^5.6.0",
36
+ "vitest": "^2.1.0"
37
+ }
38
+ }