@dadado/agent-kit-cli 6.3.0 → 6.4.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 (45) hide show
  1. package/dashboard/dashboard-data.mjs +14 -2
  2. package/dashboard/dashboard.html +139 -4
  3. package/dashboard/lib/chat-sessions.d.mts +63 -0
  4. package/dashboard/lib/chat-sessions.mjs +430 -0
  5. package/dashboard/serve.mjs +5 -1
  6. package/dist/{add-VA2W7I6P.js → add-HFDX3U3R.js} +2 -2
  7. package/dist/{chunk-6GYKOHIL.js → chunk-3OM7JODZ.js} +2 -2
  8. package/dist/{chunk-Z7RYIHHX.js → chunk-6OTJEQEC.js} +3 -3
  9. package/dist/{chunk-MAPOQEA2.js → chunk-6RRG3WOE.js} +8 -1
  10. package/dist/{chunk-6KUSG7BJ.js → chunk-7BEHUBEJ.js} +3 -1
  11. package/dist/{chunk-VQBI3JVS.js → chunk-ANPSYWQO.js} +2 -2
  12. package/dist/{chunk-WMVMBPCR.js → chunk-AXLXEAVJ.js} +1 -1
  13. package/dist/{chunk-25J2SMJQ.js → chunk-E465FAWM.js} +1 -1
  14. package/dist/{chunk-U26MRIWN.js → chunk-H46W7NHC.js} +1 -1
  15. package/dist/{chunk-NNKOWUWM.js → chunk-HHR56LJQ.js} +64 -28
  16. package/dist/{chunk-SV2BR2LZ.js → chunk-IMIVTDMK.js} +7 -7
  17. package/dist/chunk-IRTS7WEA.js +374 -0
  18. package/dist/{chunk-MO63KZBP.js → chunk-KX3OUY4W.js} +8 -6
  19. package/dist/chunk-MWTXI6C5.js +223 -0
  20. package/dist/{chunk-JEI6HACW.js → chunk-NJVWTROP.js} +24 -377
  21. package/dist/{chunk-OVOXRH5W.js → chunk-Q4PCMRM5.js} +1 -1
  22. package/dist/{chunk-GUQYNUV4.js → chunk-R7DA4KXG.js} +1 -1
  23. package/dist/{chunk-OWWFKGKS.js → chunk-SE7ZIRLX.js} +2 -2
  24. package/dist/{chunk-KVY425NM.js → chunk-TZ5XYIEF.js} +4 -2
  25. package/dist/{chunk-Z7ABX5QI.js → chunk-WOHZ4T5V.js} +4 -2
  26. package/dist/{chunk-YRFSTHR5.js → chunk-XEEB4YSA.js} +13 -219
  27. package/dist/{chunk-QTBTKEXK.js → chunk-ZPZVROS5.js} +1 -1
  28. package/dist/{contribute-NLM2BN6Z.js → contribute-PP7HMPW7.js} +3 -3
  29. package/dist/{countdown-QXJD6TDB.js → countdown-LAUZMI7U.js} +10 -8
  30. package/dist/{diff-HHG4JEDE.js → diff-HYWC4G6Y.js} +4 -4
  31. package/dist/{doctor-TWF2M3S3.js → doctor-2X3HBRJJ.js} +8 -8
  32. package/dist/engine-info-4ZWEIHSX.js +89 -0
  33. package/dist/{ground-control-NMYYIVCV.js → ground-control-WKV2BMOW.js} +5 -4
  34. package/dist/{guard-EHXF76ID.js → guard-ZZRQW3DJ.js} +107 -8
  35. package/dist/{hook-VP7FHRSZ.js → hook-UEYR4M3Z.js} +10 -4
  36. package/dist/index.js +26 -22
  37. package/dist/{init-UOTV4ASJ.js → init-UF5VEIJX.js} +11 -11
  38. package/dist/{install-VCHPXQV3.js → install-6ILVA22C.js} +11 -11
  39. package/dist/{run-R66G276Q.js → run-2EKD2BEC.js} +12 -9
  40. package/dist/{run-plan-24Z5K4P3.js → run-plan-G72XMC5Z.js} +11 -7
  41. package/dist/sessions-O7MYEMCT.js +343 -0
  42. package/dist/{setup-global-M6LKGJHQ.js → setup-global-ZZEY5KWE.js} +5 -5
  43. package/dist/{status-5UKVIKOI.js → status-4LK2QXVQ.js} +6 -6
  44. package/dist/{update-VQBGEUWS.js → update-FPR2NZND.js} +9 -9
  45. package/package.json +1 -1
@@ -1,7 +1,13 @@
1
+ import {
2
+ DRIVER_PROTOCOL_VERSION,
3
+ DriverAgentSink,
4
+ createDriverAnswerReader,
5
+ createDriverEmitter
6
+ } from "./chunk-MWTXI6C5.js";
1
7
  import {
2
8
  classifyGateExit,
3
9
  landGateExit
4
- } from "./chunk-KVY425NM.js";
10
+ } from "./chunk-TZ5XYIEF.js";
5
11
  import {
6
12
  InputLine,
7
13
  ScrollRegion,
@@ -14,16 +20,17 @@ import {
14
20
  parsePlanFrontmatter
15
21
  } from "./chunk-L5V3W3DY.js";
16
22
  import {
17
- LineSplitter,
18
23
  RedactingStreamBuffer,
19
24
  claudeRedactions,
20
- formatHitlGateLine,
21
- formatHitlSummary,
22
- operatorQuitStop,
23
25
  parseStreamLine,
24
26
  redactSecrets,
25
27
  renderStreamEvent
26
- } from "./chunk-JEI6HACW.js";
28
+ } from "./chunk-NJVWTROP.js";
29
+ import {
30
+ LineSplitter,
31
+ formatHitlSummary,
32
+ operatorQuitStop
33
+ } from "./chunk-IRTS7WEA.js";
27
34
  import {
28
35
  TtySpinner,
29
36
  shouldUseVisualMotion,
@@ -491,218 +498,6 @@ async function withLiveTui(opts, run) {
491
498
  }
492
499
  }
493
500
 
494
- // src/plan-loop/driver-events.ts
495
- import { createInterface } from "readline";
496
- import { StringDecoder as StringDecoder2 } from "string_decoder";
497
- var DRIVER_PROTOCOL_MAJOR = 1;
498
- var DRIVER_PROTOCOL_VERSION = "1.1.0";
499
- var DRIVER_EVENTS_FORMATS = ["ndjson"];
500
- var INBOUND_MAX_CHARS = 64 * 1024;
501
- var AGENT_PENDING_MAX_CHARS = 4 * 1024 * 1024;
502
- function formatDriverEvent(event, now = () => /* @__PURE__ */ new Date()) {
503
- return `${JSON.stringify({ v: DRIVER_PROTOCOL_MAJOR, ts: now().toISOString(), ...event })}
504
- `;
505
- }
506
- function createDriverEmitter(write = (text) => {
507
- process.stdout.write(text);
508
- }, now) {
509
- return {
510
- emit(event) {
511
- try {
512
- write(formatDriverEvent(event, now));
513
- } catch {
514
- }
515
- }
516
- };
517
- }
518
- function parseDriverInbound(line) {
519
- const text = line.trim();
520
- if (text.length > INBOUND_MAX_CHARS) return { ok: false, error: "line too long" };
521
- let value;
522
- try {
523
- value = JSON.parse(text);
524
- } catch {
525
- return { ok: false, error: "not JSON" };
526
- }
527
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
528
- return { ok: false, error: "not a JSON object" };
529
- }
530
- const msg = value;
531
- if (msg.v !== void 0 && msg.v !== DRIVER_PROTOCOL_MAJOR) {
532
- return { ok: false, error: `unsupported protocol v${String(msg.v)}` };
533
- }
534
- if (msg.type === "stop") {
535
- return {
536
- ok: true,
537
- message: { type: "stop", ...typeof msg.reason === "string" ? { reason: msg.reason } : {} }
538
- };
539
- }
540
- if (msg.type !== "hitl_reply")
541
- return { ok: false, error: `unknown type ${JSON.stringify(msg.type)}` };
542
- if (typeof msg.askId !== "string" || !/^[a-z0-9-]+$/.test(msg.askId)) {
543
- return { ok: false, error: "hitl_reply.askId must be a kebab-case string" };
544
- }
545
- if (typeof msg.reply === "number") {
546
- if (!Number.isInteger(msg.reply) || msg.reply < 1) {
547
- return { ok: false, error: "hitl_reply.reply number must be a positive integer" };
548
- }
549
- return { ok: true, message: { type: "hitl_reply", askId: msg.askId, reply: msg.reply } };
550
- }
551
- if (typeof msg.reply === "string" && msg.reply.trim() && !/[\r\n]/.test(msg.reply)) {
552
- return { ok: true, message: { type: "hitl_reply", askId: msg.askId, reply: msg.reply.trim() } };
553
- }
554
- return { ok: false, error: "hitl_reply.reply must be a number or a one-line non-empty string" };
555
- }
556
- function inboundToRawAnswer(gate, parsed) {
557
- if (!parsed.ok) return { kind: "stop", cause: "protocol error", detail: parsed.error };
558
- const message = parsed.message;
559
- if (message.type === "stop") return { kind: "stop", cause: "driver stop" };
560
- if (message.askId !== gate.askId) {
561
- return {
562
- kind: "stop",
563
- cause: "protocol error",
564
- detail: `reply for ${message.askId} while gate ${gate.askId} is open`
565
- };
566
- }
567
- return { kind: "line", text: String(message.reply) };
568
- }
569
- function createDriverAnswerReader(opts) {
570
- const input = opts.input ?? process.stdin;
571
- const queue = [];
572
- let ended = false;
573
- let waiter = null;
574
- let rl = null;
575
- const ensure = () => {
576
- if (rl) return;
577
- rl = createInterface({ input, terminal: false, crlfDelay: Number.POSITIVE_INFINITY });
578
- rl.on("line", (line) => {
579
- if (!line.trim()) return;
580
- if (waiter) {
581
- const w = waiter;
582
- waiter = null;
583
- w(line);
584
- } else queue.push(line);
585
- });
586
- rl.on("close", () => {
587
- ended = true;
588
- if (waiter) {
589
- const w = waiter;
590
- waiter = null;
591
- w(null);
592
- }
593
- });
594
- };
595
- const nextLine = (signal) => new Promise((resolve) => {
596
- const queued = queue.shift();
597
- if (queued !== void 0) {
598
- resolve(queued);
599
- return;
600
- }
601
- if (ended) {
602
- resolve(null);
603
- return;
604
- }
605
- const onAbort = () => {
606
- if (waiter === settle) waiter = null;
607
- resolve(null);
608
- };
609
- const settle = (line) => {
610
- signal?.removeEventListener("abort", onAbort);
611
- resolve(line);
612
- };
613
- if (signal?.aborted) {
614
- resolve(null);
615
- return;
616
- }
617
- signal?.addEventListener("abort", onAbort, { once: true });
618
- waiter = settle;
619
- });
620
- return {
621
- read: async (gate, readOpts) => {
622
- ensure();
623
- opts.emitter.emit({
624
- type: "hitl_gate",
625
- tick: opts.tick(),
626
- askId: gate.askId,
627
- labels: [...gate.labels],
628
- detection: gate.detection,
629
- line: formatHitlGateLine(gate)
630
- });
631
- const line = await nextLine(readOpts.signal);
632
- if (line === null) return { kind: "eof" };
633
- return inboundToRawAnswer(gate, parseDriverInbound(line));
634
- },
635
- close: () => {
636
- const r = rl;
637
- rl = null;
638
- r?.close();
639
- if (typeof input.pause === "function") {
640
- input.pause();
641
- }
642
- }
643
- };
644
- }
645
- var DriverAgentSink = class {
646
- constructor(emitter, tick) {
647
- this.emitter = emitter;
648
- this.tick = tick;
649
- }
650
- emitter;
651
- tick;
652
- pending = "";
653
- dropped = false;
654
- decoder = new StringDecoder2("utf8");
655
- feed(chunk) {
656
- try {
657
- const text = typeof chunk === "string" ? chunk : this.decoder.write(chunk);
658
- if (!text) return;
659
- const lines = (this.pending + text).split("\n");
660
- this.pending = lines.pop() ?? "";
661
- for (const line of lines) {
662
- if (this.dropped) {
663
- this.dropped = false;
664
- continue;
665
- }
666
- this.take(line);
667
- }
668
- if (this.pending.length > AGENT_PENDING_MAX_CHARS) {
669
- this.pending = "";
670
- this.dropped = true;
671
- }
672
- } catch {
673
- }
674
- }
675
- end() {
676
- try {
677
- const rest = this.pending + this.decoder.end();
678
- this.pending = "";
679
- if (rest && !this.dropped) this.take(rest);
680
- this.dropped = false;
681
- } catch {
682
- }
683
- }
684
- take(raw) {
685
- const line = raw.replace(/\r$/, "");
686
- if (!line.trim()) return;
687
- const trimmed = line.trim();
688
- if (trimmed.startsWith("{")) {
689
- try {
690
- const event = JSON.parse(trimmed);
691
- if (typeof event === "object" && event !== null && !Array.isArray(event)) {
692
- this.emitter.emit({
693
- type: "agent_event",
694
- tick: this.tick,
695
- event
696
- });
697
- return;
698
- }
699
- } catch {
700
- }
701
- }
702
- this.emitter.emit({ type: "agent_text", tick: this.tick, text: line });
703
- }
704
- };
705
-
706
501
  // src/plan-loop/run-loop.ts
707
502
  import { readFile as readFile4, rm, unlink } from "fs/promises";
708
503
  import path5 from "path";
@@ -1525,6 +1320,5 @@ async function runPlanLoop(opts) {
1525
1320
  export {
1526
1321
  shouldUseLiveTui,
1527
1322
  withLiveTui,
1528
- DRIVER_EVENTS_FORMATS,
1529
1323
  runPlanLoop
1530
1324
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  gateExitNotePath
3
- } from "./chunk-KVY425NM.js";
3
+ } from "./chunk-TZ5XYIEF.js";
4
4
 
5
5
  // src/plan-loop/merge-detect.ts
6
6
  async function isAncestor(git, ref, baseRef) {
@@ -1,18 +1,18 @@
1
1
  import {
2
2
  REGISTRY_CLI_ARGS,
3
3
  resolveRegistryFromCli
4
- } from "./chunk-U26MRIWN.js";
4
+ } from "./chunk-H46W7NHC.js";
5
5
  import {
6
6
  logger
7
7
  } from "./chunk-ZB43YMHW.js";
8
8
  import {
9
9
  managedPairs
10
- } from "./chunk-25J2SMJQ.js";
10
+ } from "./chunk-E465FAWM.js";
11
11
  import {
12
12
  loadAgentKitManifest,
13
13
  normalizeRelPath,
14
14
  resolveContained
15
- } from "./chunk-6KUSG7BJ.js";
15
+ } from "./chunk-7BEHUBEJ.js";
16
16
  import "./chunk-C4Q77GGJ.js";
17
17
  import "./chunk-5B4DVO6C.js";
18
18
 
@@ -4,12 +4,12 @@ import {
4
4
  REVIEW_PACK_SLASH,
5
5
  parseReviewPackMaxBytes,
6
6
  reviewBackendMessage
7
- } from "./chunk-MO63KZBP.js";
7
+ } from "./chunk-KX3OUY4W.js";
8
8
  import {
9
9
  detectAgentBackend,
10
10
  listDetectBackendIds
11
- } from "./chunk-GUQYNUV4.js";
12
- import "./chunk-QTBTKEXK.js";
11
+ } from "./chunk-R7DA4KXG.js";
12
+ import "./chunk-ZPZVROS5.js";
13
13
  import {
14
14
  GATE_EXIT_MARKER_RELPATH,
15
15
  defaultExec,
@@ -20,23 +20,25 @@ import {
20
20
  readGateExitMarker,
21
21
  resolveGateExitRefs,
22
22
  writeGateExitMarker
23
- } from "./chunk-KVY425NM.js";
23
+ } from "./chunk-TZ5XYIEF.js";
24
24
  import "./chunk-MUTTNRX4.js";
25
25
  import {
26
26
  classifySlash,
27
27
  normalizeSlashName,
28
28
  promoteBlockedMessage,
29
29
  unknownSlashMessage
30
- } from "./chunk-Z7ABX5QI.js";
30
+ } from "./chunk-WOHZ4T5V.js";
31
31
  import {
32
32
  CLAUDE_MAX_BUDGET_USD_ENV,
33
33
  CLAUDE_MAX_TURNS_ENV,
34
- HITL_UNANSWERED_EXIT_CODE,
35
34
  RedactingStreamBuffer,
36
35
  redactSecrets
37
- } from "./chunk-JEI6HACW.js";
36
+ } from "./chunk-NJVWTROP.js";
37
+ import {
38
+ HITL_UNANSWERED_EXIT_CODE
39
+ } from "./chunk-IRTS7WEA.js";
38
40
  import "./chunk-WGLPZXNU.js";
39
- import "./chunk-MAPOQEA2.js";
41
+ import "./chunk-6RRG3WOE.js";
40
42
  import "./chunk-QTON2GQX.js";
41
43
  import {
42
44
  logger
@@ -1,18 +1,18 @@
1
1
  import {
2
2
  REGISTRY_CLI_ARGS,
3
3
  resolveRegistryFromCli
4
- } from "./chunk-U26MRIWN.js";
4
+ } from "./chunk-H46W7NHC.js";
5
5
  import {
6
6
  logger
7
7
  } from "./chunk-ZB43YMHW.js";
8
8
  import {
9
9
  diffAgainstRegistry,
10
10
  summarizeDiff
11
- } from "./chunk-VQBI3JVS.js";
12
- import "./chunk-25J2SMJQ.js";
11
+ } from "./chunk-ANPSYWQO.js";
12
+ import "./chunk-E465FAWM.js";
13
13
  import {
14
14
  loadAgentKitManifest
15
- } from "./chunk-6KUSG7BJ.js";
15
+ } from "./chunk-7BEHUBEJ.js";
16
16
  import "./chunk-C4Q77GGJ.js";
17
17
  import "./chunk-5B4DVO6C.js";
18
18
 
@@ -2,20 +2,20 @@ import {
2
2
  assessHooksHealth,
3
3
  executeSafeReadinessFixes,
4
4
  refreshRepositoryProfile
5
- } from "./chunk-WMVMBPCR.js";
5
+ } from "./chunk-AXLXEAVJ.js";
6
6
  import {
7
7
  writeReadinessSnapshot
8
8
  } from "./chunk-HAFRAXGV.js";
9
- import "./chunk-NNKOWUWM.js";
9
+ import "./chunk-HHR56LJQ.js";
10
10
  import "./chunk-WGLPZXNU.js";
11
- import "./chunk-MAPOQEA2.js";
11
+ import "./chunk-6RRG3WOE.js";
12
12
  import {
13
13
  withCliProgress
14
14
  } from "./chunk-QTON2GQX.js";
15
15
  import {
16
16
  detectWorkspaceParent,
17
17
  workspaceParentAdvisoryLines
18
- } from "./chunk-OVOXRH5W.js";
18
+ } from "./chunk-Q4PCMRM5.js";
19
19
  import {
20
20
  createReadinessReport
21
21
  } from "./chunk-PNFF6OU3.js";
@@ -32,11 +32,11 @@ import {
32
32
  } from "./chunk-B3VR5YBQ.js";
33
33
  import {
34
34
  warnIfRunningCliBehindNpm
35
- } from "./chunk-6GYKOHIL.js";
35
+ } from "./chunk-3OM7JODZ.js";
36
36
  import "./chunk-PFEUZXVT.js";
37
- import "./chunk-VQBI3JVS.js";
38
- import "./chunk-25J2SMJQ.js";
39
- import "./chunk-6KUSG7BJ.js";
37
+ import "./chunk-ANPSYWQO.js";
38
+ import "./chunk-E465FAWM.js";
39
+ import "./chunk-7BEHUBEJ.js";
40
40
  import {
41
41
  KIT_VERSION
42
42
  } from "./chunk-7HDQEK6T.js";
@@ -0,0 +1,89 @@
1
+ import {
2
+ DRIVER_PROTOCOL_VERSION
3
+ } from "./chunk-MWTXI6C5.js";
4
+ import {
5
+ bundledDashboardCandidates
6
+ } from "./chunk-XDCCYBCJ.js";
7
+ import "./chunk-IRTS7WEA.js";
8
+ import "./chunk-ZB43YMHW.js";
9
+
10
+ // src/commands/engine-info.ts
11
+ import { defineCommand } from "citty";
12
+
13
+ // src/lifecycle/engine-info.ts
14
+ import { readFileSync } from "fs";
15
+ import { createRequire } from "module";
16
+ var require2 = createRequire(import.meta.url);
17
+ function loadPackageMetadata() {
18
+ for (const packagePath of ["../package.json", "../../package.json"]) {
19
+ try {
20
+ const metadata = require2(packagePath);
21
+ if (typeof metadata.version === "string" && metadata.name === "@dadado/agent-kit-cli") {
22
+ return metadata;
23
+ }
24
+ } catch {
25
+ }
26
+ }
27
+ throw new Error("Unable to resolve the Agent Kit CLI package.json");
28
+ }
29
+ var DATA_VERSION_RE = /^\s*dashboardDataVersion:\s*["'](\d+\.\d+\.\d+)["']/m;
30
+ function parseDashboardDataVersion(source) {
31
+ return DATA_VERSION_RE.exec(source)?.[1] ?? null;
32
+ }
33
+ function readDashboardDataVersion(candidates) {
34
+ for (const candidate of candidates) {
35
+ try {
36
+ const found = parseDashboardDataVersion(readFileSync(candidate, "utf8"));
37
+ if (found) return found;
38
+ } catch {
39
+ }
40
+ }
41
+ return null;
42
+ }
43
+ function buildEngineManifest(opts = {}) {
44
+ const metadata = opts.metadata ?? loadPackageMetadata();
45
+ const candidates = opts.dashboardCandidates ?? bundledDashboardCandidates("dashboard-data.mjs");
46
+ const node = metadata.engines?.node;
47
+ return {
48
+ manifestVersion: 1,
49
+ name: String(metadata.name),
50
+ version: String(metadata.version),
51
+ node: typeof node === "string" ? node : "",
52
+ driverProtocol: DRIVER_PROTOCOL_VERSION,
53
+ dashboardDataVersion: readDashboardDataVersion(candidates),
54
+ entry: "dist/index.js",
55
+ dashboardServe: "dashboard/serve.mjs"
56
+ };
57
+ }
58
+ function renderEngineManifest(m) {
59
+ return [
60
+ `${m.name} ${m.version}`,
61
+ `node ${m.node || "(unspecified)"}`,
62
+ `driver protocol ${m.driverProtocol}`,
63
+ `dashboard data ${m.dashboardDataVersion ?? "(no dashboard found)"}`,
64
+ `entry ${m.entry}`,
65
+ `dashboard server ${m.dashboardServe}`
66
+ ].join("\n");
67
+ }
68
+
69
+ // src/commands/engine-info.ts
70
+ var engineInfoCommand = defineCommand({
71
+ meta: {
72
+ name: "engine-info",
73
+ description: "Print the engine manifest: version, Node range, driver protocol, snapshot schema"
74
+ },
75
+ args: {
76
+ json: { type: "boolean", description: "Print the manifest as JSON", default: false }
77
+ },
78
+ run({ args }) {
79
+ const manifest = buildEngineManifest();
80
+ process.stdout.write(
81
+ args.json ? `${JSON.stringify(manifest, null, 2)}
82
+ ` : `${renderEngineManifest(manifest)}
83
+ `
84
+ );
85
+ }
86
+ });
87
+ export {
88
+ engineInfoCommand
89
+ };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  blobId,
3
3
  isTicketMerged
4
- } from "./chunk-QTBTKEXK.js";
4
+ } from "./chunk-ZPZVROS5.js";
5
5
  import {
6
6
  GATE_EXIT_BRANCH_PREFIX,
7
7
  GATE_EXIT_MARKER_RELPATH,
@@ -16,15 +16,16 @@ import {
16
16
  readGateExitMarker,
17
17
  resolveGateExitRefs,
18
18
  writeGateExitMarker
19
- } from "./chunk-KVY425NM.js";
19
+ } from "./chunk-TZ5XYIEF.js";
20
20
  import "./chunk-MUTTNRX4.js";
21
21
  import {
22
22
  redactSecrets
23
- } from "./chunk-JEI6HACW.js";
23
+ } from "./chunk-NJVWTROP.js";
24
+ import "./chunk-IRTS7WEA.js";
24
25
  import {
25
26
  findOnPath
26
27
  } from "./chunk-WGLPZXNU.js";
27
- import "./chunk-MAPOQEA2.js";
28
+ import "./chunk-6RRG3WOE.js";
28
29
  import "./chunk-QTON2GQX.js";
29
30
  import {
30
31
  logger
@@ -272,6 +272,55 @@ function ghTargetsPublicRepo(head) {
272
272
  if (!/^(?:[\w./-]+\/)?gh\s+pr\s+(?:create|merge)\b/.test(head)) return false;
273
273
  return PUBLIC_REPO_RE.test(head);
274
274
  }
275
+ var PR_MERGE_HEAD_RE = /^(?:[\w./-]+\/)?(gh\s+pr|glab\s+mr)\s+merge\b/;
276
+ var PR_MERGE_VALUE_FLAGS = /* @__PURE__ */ new Set([
277
+ "-R",
278
+ "--repo",
279
+ "-t",
280
+ "--subject",
281
+ "-b",
282
+ "--body",
283
+ "-F",
284
+ "--body-file",
285
+ "-A",
286
+ "--author-email",
287
+ "--match-head-commit",
288
+ "-m",
289
+ "--message",
290
+ "--squash-message",
291
+ "--sha"
292
+ ]);
293
+ function parsePrMergeHead(head) {
294
+ const m = PR_MERGE_HEAD_RE.exec(head);
295
+ if (!m?.[1]) return null;
296
+ const forge = m[1].startsWith("gh") ? "github" : "gitlab";
297
+ const tokens = head.slice(m[0].length).trim().split(/\s+/).filter(Boolean);
298
+ let selector = null;
299
+ let repo = null;
300
+ for (let i = 0; i < tokens.length; i++) {
301
+ const t = tokens[i];
302
+ const inline = /^(--?[\w-]+)=(.*)$/.exec(t);
303
+ if (inline) {
304
+ if (inline[1] === "-R" || inline[1] === "--repo") repo = inline[2] || null;
305
+ continue;
306
+ }
307
+ if (t.startsWith("-")) {
308
+ if (PR_MERGE_VALUE_FLAGS.has(t)) {
309
+ if ((t === "-R" || t === "--repo") && tokens[i + 1]) repo = tokens[i + 1];
310
+ i++;
311
+ }
312
+ continue;
313
+ }
314
+ if (selector === null) selector = t;
315
+ }
316
+ return { forge, selector, repo };
317
+ }
318
+ function prMergeKey(target) {
319
+ return `${target.forge}\0${target.repo ?? ""}\0${target.selector ?? ""}`;
320
+ }
321
+ function prMergeTargets(command) {
322
+ return shellSegments(command).map((segment) => parsePrMergeHead(canonicalGitHead(segment))).filter((t) => t !== null);
323
+ }
275
324
  function anyHeadMatches(command, re) {
276
325
  return shellInvocationHeads(command).some((head) => re.test(head));
277
326
  }
@@ -377,6 +426,23 @@ var SHELL_DENY_RULES = [
377
426
  }
378
427
  return false;
379
428
  })
429
+ },
430
+ {
431
+ // Claude settings allow `gh pr merge` / `glab mr merge` so the /git-staging merge
432
+ // skips the auto-mode classifier; this rule keeps that allowance to non-protected bases.
433
+ id: "pr-merge-protected-base",
434
+ description: "merging a PR/MR into main/master/prod bypasses /git-prod; an unresolved base (gh/glab could not read the PR/MR: not logged in, wrong selector, or no such PR) is denied too",
435
+ test: (cmd, opts) => {
436
+ const bases = opts?.prMergeBases;
437
+ if (!bases) return false;
438
+ return shellSegments(cmd).some((segment) => {
439
+ if (segmentHasAllowMainPushEnv(segment)) return false;
440
+ const target = parsePrMergeHead(canonicalGitHead(segment));
441
+ if (!target) return false;
442
+ const base = bases[prMergeKey(target)];
443
+ return typeof base !== "string" || isProtectedBranch(base);
444
+ });
445
+ }
380
446
  }
381
447
  ];
382
448
  function evaluateShellCommand(command, opts = {}) {
@@ -425,15 +491,47 @@ async function detectRemotes(cwd) {
425
491
  return void 0;
426
492
  }
427
493
  }
428
- var defaultGuardShellDeps = { detectCurrentBranch, detectRemotes };
429
- var GIT_OR_GH_RE = /\b(git|gh)\b/;
494
+ var PR_BASE_TIMEOUT_MS = 3e3;
495
+ async function resolvePrBase(target, cwd) {
496
+ const selector = target.selector ? [target.selector] : [];
497
+ try {
498
+ if (target.forge === "github") {
499
+ const repo2 = target.repo ? ["--repo", target.repo] : [];
500
+ const { stdout: stdout2 } = await execFileAsync(
501
+ "gh",
502
+ ["pr", "view", ...selector, ...repo2, "--json", "baseRefName", "-q", ".baseRefName"],
503
+ { encoding: "utf8", cwd, timeout: PR_BASE_TIMEOUT_MS }
504
+ );
505
+ return stdout2.trim() || null;
506
+ }
507
+ const repo = target.repo ? ["-R", target.repo] : [];
508
+ const { stdout } = await execFileAsync(
509
+ "glab",
510
+ ["mr", "view", ...selector, ...repo, "-F", "json"],
511
+ {
512
+ encoding: "utf8",
513
+ cwd,
514
+ timeout: PR_BASE_TIMEOUT_MS
515
+ }
516
+ );
517
+ const base = JSON.parse(stdout).target_branch;
518
+ return typeof base === "string" && base ? base : null;
519
+ } catch {
520
+ return null;
521
+ }
522
+ }
523
+ var defaultGuardShellDeps = { detectCurrentBranch, detectRemotes, resolvePrBase };
524
+ var GIT_OR_FORGE_RE = /\b(git|gh|glab)\b/;
430
525
  async function evaluateGuardShell(command, cwd = process.cwd(), deps = defaultGuardShellDeps) {
431
- if (!GIT_OR_GH_RE.test(command)) return evaluateShellCommand(command);
432
- const [currentBranch, remotes] = await Promise.all([
526
+ if (!GIT_OR_FORGE_RE.test(command)) return evaluateShellCommand(command);
527
+ const targets = prMergeTargets(command);
528
+ const [currentBranch, remotes, bases] = await Promise.all([
433
529
  deps.detectCurrentBranch(cwd),
434
- deps.detectRemotes(cwd)
530
+ deps.detectRemotes(cwd),
531
+ Promise.all(targets.map((t) => deps.resolvePrBase(t, cwd)))
435
532
  ]);
436
- return evaluateShellCommand(command, { currentBranch, remotes });
533
+ const prMergeBases = targets.length > 0 ? Object.fromEntries(targets.map((t, i) => [prMergeKey(t), bases[i] ?? null])) : void 0;
534
+ return evaluateShellCommand(command, { currentBranch, remotes, prMergeBases });
437
535
  }
438
536
  async function runGuardShellHook(formatArg, deps = {}) {
439
537
  const format = resolveGuardShellFormat(formatArg);
@@ -448,7 +546,8 @@ async function runGuardShellHook(formatArg, deps = {}) {
448
546
  }
449
547
  const result = await evaluateGuardShell(command, cwd, {
450
548
  detectCurrentBranch: deps.detectCurrentBranch ?? defaultGuardShellDeps.detectCurrentBranch,
451
- detectRemotes: deps.detectRemotes ?? defaultGuardShellDeps.detectRemotes
549
+ detectRemotes: deps.detectRemotes ?? defaultGuardShellDeps.detectRemotes,
550
+ resolvePrBase: deps.resolvePrBase ?? defaultGuardShellDeps.resolvePrBase
452
551
  });
453
552
  return formatGuardShellOutput(result, format);
454
553
  };
@@ -468,7 +567,7 @@ var guardCommand = defineCommand({
468
567
  shell: defineCommand({
469
568
  meta: {
470
569
  name: "shell",
471
- description: "Evaluate a shell command against the git-workflow / protected-branch deny-list (git checkout|restore|reset --hard|clean -fd + pushes to main/master/prod + direct git push/gh pr create|merge against the public repo). Not a general destructive-command guard: rm -rf, chmod, dd are allowed."
570
+ description: "Evaluate a shell command against the git-workflow / protected-branch deny-list (git checkout|restore|reset --hard|clean -fd + pushes to main/master/prod + gh pr merge / glab mr merge into main/master/prod or an unresolved base + direct git push/gh pr create|merge against the public repo). Not a general destructive-command guard: rm -rf, chmod, dd are allowed."
472
571
  },
473
572
  args: {
474
573
  json: {