@elhu/pit 0.1.1

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 (192) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +380 -0
  3. package/dist/adapters/claude-code.d.ts +70 -0
  4. package/dist/adapters/claude-code.d.ts.map +1 -0
  5. package/dist/adapters/claude-code.js +166 -0
  6. package/dist/adapters/claude-code.js.map +1 -0
  7. package/dist/adapters/index.d.ts +16 -0
  8. package/dist/adapters/index.d.ts.map +1 -0
  9. package/dist/adapters/index.js +49 -0
  10. package/dist/adapters/index.js.map +1 -0
  11. package/dist/adapters/opencode.d.ts +53 -0
  12. package/dist/adapters/opencode.d.ts.map +1 -0
  13. package/dist/adapters/opencode.js +120 -0
  14. package/dist/adapters/opencode.js.map +1 -0
  15. package/dist/adapters/process-utils.d.ts +29 -0
  16. package/dist/adapters/process-utils.d.ts.map +1 -0
  17. package/dist/adapters/process-utils.js +96 -0
  18. package/dist/adapters/process-utils.js.map +1 -0
  19. package/dist/adapters/types.d.ts +41 -0
  20. package/dist/adapters/types.d.ts.map +1 -0
  21. package/dist/adapters/types.js +6 -0
  22. package/dist/adapters/types.js.map +1 -0
  23. package/dist/assets.generated.d.ts +13 -0
  24. package/dist/assets.generated.d.ts.map +1 -0
  25. package/dist/assets.generated.js +162 -0
  26. package/dist/assets.generated.js.map +1 -0
  27. package/dist/beads.d.ts +85 -0
  28. package/dist/beads.d.ts.map +1 -0
  29. package/dist/beads.js +120 -0
  30. package/dist/beads.js.map +1 -0
  31. package/dist/cli.d.ts +3 -0
  32. package/dist/cli.d.ts.map +1 -0
  33. package/dist/cli.js +39 -0
  34. package/dist/cli.js.map +1 -0
  35. package/dist/commands/add.d.ts +10 -0
  36. package/dist/commands/add.d.ts.map +1 -0
  37. package/dist/commands/add.js +58 -0
  38. package/dist/commands/add.js.map +1 -0
  39. package/dist/commands/cleanup.d.ts +13 -0
  40. package/dist/commands/cleanup.d.ts.map +1 -0
  41. package/dist/commands/cleanup.js +174 -0
  42. package/dist/commands/cleanup.js.map +1 -0
  43. package/dist/commands/daemon.d.ts +3 -0
  44. package/dist/commands/daemon.d.ts.map +1 -0
  45. package/dist/commands/daemon.js +162 -0
  46. package/dist/commands/daemon.js.map +1 -0
  47. package/dist/commands/init.d.ts +20 -0
  48. package/dist/commands/init.d.ts.map +1 -0
  49. package/dist/commands/init.js +125 -0
  50. package/dist/commands/init.js.map +1 -0
  51. package/dist/commands/install-keybinding.d.ts +61 -0
  52. package/dist/commands/install-keybinding.d.ts.map +1 -0
  53. package/dist/commands/install-keybinding.js +138 -0
  54. package/dist/commands/install-keybinding.js.map +1 -0
  55. package/dist/commands/install-status.d.ts +35 -0
  56. package/dist/commands/install-status.d.ts.map +1 -0
  57. package/dist/commands/install-status.js +115 -0
  58. package/dist/commands/install-status.js.map +1 -0
  59. package/dist/commands/log.d.ts +7 -0
  60. package/dist/commands/log.d.ts.map +1 -0
  61. package/dist/commands/log.js +60 -0
  62. package/dist/commands/log.js.map +1 -0
  63. package/dist/commands/pause.d.ts +12 -0
  64. package/dist/commands/pause.d.ts.map +1 -0
  65. package/dist/commands/pause.js +47 -0
  66. package/dist/commands/pause.js.map +1 -0
  67. package/dist/commands/resume.d.ts +12 -0
  68. package/dist/commands/resume.d.ts.map +1 -0
  69. package/dist/commands/resume.js +59 -0
  70. package/dist/commands/resume.js.map +1 -0
  71. package/dist/commands/shared.d.ts +7 -0
  72. package/dist/commands/shared.d.ts.map +1 -0
  73. package/dist/commands/shared.js +56 -0
  74. package/dist/commands/shared.js.map +1 -0
  75. package/dist/commands/start.d.ts +12 -0
  76. package/dist/commands/start.d.ts.map +1 -0
  77. package/dist/commands/start.js +274 -0
  78. package/dist/commands/start.js.map +1 -0
  79. package/dist/commands/status.d.ts +24 -0
  80. package/dist/commands/status.d.ts.map +1 -0
  81. package/dist/commands/status.js +101 -0
  82. package/dist/commands/status.js.map +1 -0
  83. package/dist/commands/stop.d.ts +11 -0
  84. package/dist/commands/stop.d.ts.map +1 -0
  85. package/dist/commands/stop.js +52 -0
  86. package/dist/commands/stop.js.map +1 -0
  87. package/dist/commands/teardown.d.ts +15 -0
  88. package/dist/commands/teardown.d.ts.map +1 -0
  89. package/dist/commands/teardown.js +72 -0
  90. package/dist/commands/teardown.js.map +1 -0
  91. package/dist/config.d.ts +58 -0
  92. package/dist/config.d.ts.map +1 -0
  93. package/dist/config.js +129 -0
  94. package/dist/config.js.map +1 -0
  95. package/dist/daemon/client.d.ts +38 -0
  96. package/dist/daemon/client.d.ts.map +1 -0
  97. package/dist/daemon/client.js +254 -0
  98. package/dist/daemon/client.js.map +1 -0
  99. package/dist/daemon/context.d.ts +63 -0
  100. package/dist/daemon/context.d.ts.map +1 -0
  101. package/dist/daemon/context.js +14 -0
  102. package/dist/daemon/context.js.map +1 -0
  103. package/dist/daemon/handlers.d.ts +79 -0
  104. package/dist/daemon/handlers.d.ts.map +1 -0
  105. package/dist/daemon/handlers.js +1260 -0
  106. package/dist/daemon/handlers.js.map +1 -0
  107. package/dist/daemon/index.d.ts +6 -0
  108. package/dist/daemon/index.d.ts.map +1 -0
  109. package/dist/daemon/index.js +7 -0
  110. package/dist/daemon/index.js.map +1 -0
  111. package/dist/daemon/lifecycle.d.ts +56 -0
  112. package/dist/daemon/lifecycle.d.ts.map +1 -0
  113. package/dist/daemon/lifecycle.js +341 -0
  114. package/dist/daemon/lifecycle.js.map +1 -0
  115. package/dist/daemon/protocol.d.ts +174 -0
  116. package/dist/daemon/protocol.d.ts.map +1 -0
  117. package/dist/daemon/protocol.js +3 -0
  118. package/dist/daemon/protocol.js.map +1 -0
  119. package/dist/daemon/recovery.d.ts +37 -0
  120. package/dist/daemon/recovery.d.ts.map +1 -0
  121. package/dist/daemon/recovery.js +197 -0
  122. package/dist/daemon/recovery.js.map +1 -0
  123. package/dist/daemon/server.d.ts +31 -0
  124. package/dist/daemon/server.d.ts.map +1 -0
  125. package/dist/daemon/server.js +294 -0
  126. package/dist/daemon/server.js.map +1 -0
  127. package/dist/daemon/socket.d.ts +18 -0
  128. package/dist/daemon/socket.d.ts.map +1 -0
  129. package/dist/daemon/socket.js +36 -0
  130. package/dist/daemon/socket.js.map +1 -0
  131. package/dist/daemon/state.d.ts +60 -0
  132. package/dist/daemon/state.d.ts.map +1 -0
  133. package/dist/daemon/state.js +156 -0
  134. package/dist/daemon/state.js.map +1 -0
  135. package/dist/daemon/systemd.d.ts +19 -0
  136. package/dist/daemon/systemd.d.ts.map +1 -0
  137. package/dist/daemon/systemd.js +131 -0
  138. package/dist/daemon/systemd.js.map +1 -0
  139. package/dist/hooks/claude-code-hook.d.ts +32 -0
  140. package/dist/hooks/claude-code-hook.d.ts.map +1 -0
  141. package/dist/hooks/claude-code-hook.js +112 -0
  142. package/dist/hooks/claude-code-hook.js.map +1 -0
  143. package/dist/instructions-template.d.ts +9 -0
  144. package/dist/instructions-template.d.ts.map +1 -0
  145. package/dist/instructions-template.js +123 -0
  146. package/dist/instructions-template.js.map +1 -0
  147. package/dist/logger.d.ts +25 -0
  148. package/dist/logger.d.ts.map +1 -0
  149. package/dist/logger.js +44 -0
  150. package/dist/logger.js.map +1 -0
  151. package/dist/loop.d.ts +88 -0
  152. package/dist/loop.d.ts.map +1 -0
  153. package/dist/loop.js +161 -0
  154. package/dist/loop.js.map +1 -0
  155. package/dist/orchestrator-instructions-template.d.ts +13 -0
  156. package/dist/orchestrator-instructions-template.d.ts.map +1 -0
  157. package/dist/orchestrator-instructions-template.js +147 -0
  158. package/dist/orchestrator-instructions-template.js.map +1 -0
  159. package/dist/output.d.ts +12 -0
  160. package/dist/output.d.ts.map +1 -0
  161. package/dist/output.js +25 -0
  162. package/dist/output.js.map +1 -0
  163. package/dist/plugin/pit.js +57 -0
  164. package/dist/session.d.ts +55 -0
  165. package/dist/session.d.ts.map +1 -0
  166. package/dist/session.js +135 -0
  167. package/dist/session.js.map +1 -0
  168. package/dist/setup.d.ts +92 -0
  169. package/dist/setup.d.ts.map +1 -0
  170. package/dist/setup.js +382 -0
  171. package/dist/setup.js.map +1 -0
  172. package/dist/shell-quote.d.ts +16 -0
  173. package/dist/shell-quote.d.ts.map +1 -0
  174. package/dist/shell-quote.js +18 -0
  175. package/dist/shell-quote.js.map +1 -0
  176. package/dist/signals.d.ts +17 -0
  177. package/dist/signals.d.ts.map +1 -0
  178. package/dist/signals.js +26 -0
  179. package/dist/signals.js.map +1 -0
  180. package/dist/state-machine.d.ts +74 -0
  181. package/dist/state-machine.d.ts.map +1 -0
  182. package/dist/state-machine.js +153 -0
  183. package/dist/state-machine.js.map +1 -0
  184. package/dist/tmux.d.ts +101 -0
  185. package/dist/tmux.d.ts.map +1 -0
  186. package/dist/tmux.js +208 -0
  187. package/dist/tmux.js.map +1 -0
  188. package/dist/worktree.d.ts +33 -0
  189. package/dist/worktree.d.ts.map +1 -0
  190. package/dist/worktree.js +116 -0
  191. package/dist/worktree.js.map +1 -0
  192. package/package.json +66 -0
@@ -0,0 +1,74 @@
1
+ /**
2
+ * EpicStateMachine — per-epic state management for pit.
3
+ *
4
+ * Manages the SETUP → RUNNING → CLEARING → PAUSED / DONE lifecycle.
5
+ * All transitions are serialized through a per-epic mutex (via a promise chain)
6
+ * to prevent races between concurrent signal events and CLI commands.
7
+ *
8
+ * State transitions:
9
+ * SETUP → RUNNING (initial prompt sent)
10
+ * RUNNING → CLEARING (TICKET_COMPLETE signal)
11
+ * RUNNING → PAUSED (NEEDS_HUMAN_INPUT / permission / unrecognized idle / ticket timeout)
12
+ * RUNNING → DONE (EPIC_COMPLETE signal)
13
+ * RUNNING → PAUSED (manual pit pause)
14
+ * CLEARING → RUNNING (/clear + re-prompt sent)
15
+ * CLEARING → PAUSED (CLEARING pipeline failed)
16
+ * PAUSED → CLEARING (pit resume, no message)
17
+ * PAUSED → RUNNING (pit resume --message)
18
+ */
19
+ export type MachineState = "SETUP" | "RUNNING" | "CLEARING" | "PAUSED" | "DONE";
20
+ export type TransitionReason = "TUI_READY" | "TICKET_COMPLETE" | "EPIC_COMPLETE" | "NEEDS_HUMAN_INPUT" | "PERMISSION_ASKED" | "UNRECOGNIZED_IDLE" | "CLEARED" | "MANUAL_PAUSE" | "RESUME_NO_MESSAGE" | "RESUME_WITH_MESSAGE" | "MANUAL_STOP" | "TICKET_TIMEOUT";
21
+ export interface StateTransition {
22
+ from: MachineState;
23
+ to: MachineState;
24
+ reason: TransitionReason;
25
+ pauseReason?: string;
26
+ timestamp: string;
27
+ }
28
+ export interface EpicStateMachineOptions {
29
+ epic: string;
30
+ onTransition?: (transition: StateTransition) => void;
31
+ initialState?: MachineState;
32
+ initialPauseReason?: string | null;
33
+ }
34
+ /**
35
+ * Error thrown when a state transition is invalid for the current state.
36
+ */
37
+ export declare class InvalidTransitionError extends Error {
38
+ readonly from: MachineState;
39
+ readonly to: MachineState;
40
+ readonly reason: TransitionReason;
41
+ constructor(from: MachineState, to: MachineState, reason: TransitionReason);
42
+ }
43
+ export declare class EpicStateMachine {
44
+ private _state;
45
+ private _pauseReason;
46
+ private readonly epic;
47
+ private readonly onTransition?;
48
+ private mutexTail;
49
+ constructor(options: EpicStateMachineOptions);
50
+ /** The current state of this epic. */
51
+ get state(): MachineState;
52
+ /** The reason the epic is paused, if state is PAUSED. */
53
+ get pauseReason(): string | null;
54
+ /**
55
+ * Attempt a state transition, serialized through the mutex.
56
+ *
57
+ * @param to Target state
58
+ * @param reason Why this transition is happening
59
+ * @param opts Optional extra data (e.g. pauseReason for PAUSED transitions)
60
+ * @throws InvalidTransitionError if the transition is not valid from the current state
61
+ */
62
+ transition(to: MachineState, reason: TransitionReason, opts?: {
63
+ pauseReason?: string;
64
+ }): Promise<void>;
65
+ /**
66
+ * Execute a function while holding the mutex.
67
+ * The function receives the current state and can call transition() inside.
68
+ * Useful for compound operations that need to read + transition atomically.
69
+ */
70
+ withLock<T>(fn: (state: MachineState) => Promise<T>): Promise<T>;
71
+ private _applyTransition;
72
+ private _validateTransition;
73
+ }
74
+ //# sourceMappingURL=state-machine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-machine.d.ts","sourceRoot":"","sources":["../src/state-machine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEhF,MAAM,MAAM,gBAAgB,GACxB,WAAW,GACX,iBAAiB,GACjB,eAAe,GACf,mBAAmB,GACnB,kBAAkB,GAClB,mBAAmB,GACnB,SAAS,GACT,cAAc,GACd,mBAAmB,GACnB,qBAAqB,GACrB,aAAa,GACb,gBAAgB,CAAC;AAErB,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,YAAY,CAAC;IACnB,EAAE,EAAE,YAAY,CAAC;IACjB,MAAM,EAAE,gBAAgB,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,eAAe,KAAK,IAAI,CAAC;IACrD,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;aAE7B,IAAI,EAAE,YAAY;aAClB,EAAE,EAAE,YAAY;aAChB,MAAM,EAAE,gBAAgB;gBAFxB,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,YAAY,EAChB,MAAM,EAAE,gBAAgB;CAK3C;AAmBD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAwC;IAGtE,OAAO,CAAC,SAAS,CAAoC;gBAEzC,OAAO,EAAE,uBAAuB;IAO5C,sCAAsC;IACtC,IAAI,KAAK,IAAI,YAAY,CAExB;IAED,yDAAyD;IACzD,IAAI,WAAW,IAAI,MAAM,GAAG,IAAI,CAE/B;IAED;;;;;;;OAOG;IACG,UAAU,CACd,EAAE,EAAE,YAAY,EAChB,MAAM,EAAE,gBAAgB,EACxB,IAAI,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAC9B,OAAO,CAAC,IAAI,CAAC;IAkBhB;;;;OAIG;IACG,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAetE,OAAO,CAAC,gBAAgB;IA0BxB,OAAO,CAAC,mBAAmB;CAW5B"}
@@ -0,0 +1,153 @@
1
+ /**
2
+ * EpicStateMachine — per-epic state management for pit.
3
+ *
4
+ * Manages the SETUP → RUNNING → CLEARING → PAUSED / DONE lifecycle.
5
+ * All transitions are serialized through a per-epic mutex (via a promise chain)
6
+ * to prevent races between concurrent signal events and CLI commands.
7
+ *
8
+ * State transitions:
9
+ * SETUP → RUNNING (initial prompt sent)
10
+ * RUNNING → CLEARING (TICKET_COMPLETE signal)
11
+ * RUNNING → PAUSED (NEEDS_HUMAN_INPUT / permission / unrecognized idle / ticket timeout)
12
+ * RUNNING → DONE (EPIC_COMPLETE signal)
13
+ * RUNNING → PAUSED (manual pit pause)
14
+ * CLEARING → RUNNING (/clear + re-prompt sent)
15
+ * CLEARING → PAUSED (CLEARING pipeline failed)
16
+ * PAUSED → CLEARING (pit resume, no message)
17
+ * PAUSED → RUNNING (pit resume --message)
18
+ */
19
+ /**
20
+ * Error thrown when a state transition is invalid for the current state.
21
+ */
22
+ export class InvalidTransitionError extends Error {
23
+ from;
24
+ to;
25
+ reason;
26
+ constructor(from, to, reason) {
27
+ super(`Invalid transition from ${from} to ${to} (reason: ${reason})`);
28
+ this.from = from;
29
+ this.to = to;
30
+ this.reason = reason;
31
+ this.name = "InvalidTransitionError";
32
+ }
33
+ }
34
+ /**
35
+ * Per-epic state machine.
36
+ *
37
+ * State is stored in memory. Signal files are the persistent store; this
38
+ * class manages the in-memory representation and serializes transitions.
39
+ *
40
+ * Usage:
41
+ * const sm = new EpicStateMachine({ epic: "auth" });
42
+ * await sm.transition("RUNNING", "TUI_READY");
43
+ * // later:
44
+ * await sm.transition("CLEARING", "TICKET_COMPLETE");
45
+ */
46
+ /** Silently swallow a rejected promise — used to keep mutex tail alive. */
47
+ function _swallow(_err) {
48
+ // intentionally empty
49
+ }
50
+ export class EpicStateMachine {
51
+ _state;
52
+ _pauseReason;
53
+ epic;
54
+ onTransition;
55
+ // Mutex: a promise chain that serializes transitions
56
+ mutexTail = Promise.resolve();
57
+ constructor(options) {
58
+ this.epic = options.epic;
59
+ this.onTransition = options.onTransition;
60
+ this._state = options.initialState ?? "SETUP";
61
+ this._pauseReason = options.initialPauseReason ?? null;
62
+ }
63
+ /** The current state of this epic. */
64
+ get state() {
65
+ return this._state;
66
+ }
67
+ /** The reason the epic is paused, if state is PAUSED. */
68
+ get pauseReason() {
69
+ return this._pauseReason;
70
+ }
71
+ /**
72
+ * Attempt a state transition, serialized through the mutex.
73
+ *
74
+ * @param to Target state
75
+ * @param reason Why this transition is happening
76
+ * @param opts Optional extra data (e.g. pauseReason for PAUSED transitions)
77
+ * @throws InvalidTransitionError if the transition is not valid from the current state
78
+ */
79
+ async transition(to, reason, opts) {
80
+ // Enqueue onto mutex tail, catching to avoid unhandled rejection on the outer tail
81
+ const work = () => new Promise((resolve, reject) => {
82
+ try {
83
+ this._applyTransition(to, reason, opts);
84
+ resolve();
85
+ }
86
+ catch (err) {
87
+ reject(err);
88
+ }
89
+ });
90
+ // Chain: wait for previous work, then do ours
91
+ const next = this.mutexTail.then(work, work); // run even if previous failed
92
+ this.mutexTail = next.catch(_swallow); // swallow on tail to keep chain alive
93
+ return next; // caller gets the real error
94
+ }
95
+ /**
96
+ * Execute a function while holding the mutex.
97
+ * The function receives the current state and can call transition() inside.
98
+ * Useful for compound operations that need to read + transition atomically.
99
+ */
100
+ async withLock(fn) {
101
+ let result;
102
+ const work = async () => {
103
+ result = await fn(this._state);
104
+ };
105
+ const next = this.mutexTail.then(work);
106
+ this.mutexTail = next.catch(_swallow);
107
+ await next;
108
+ return result;
109
+ }
110
+ // ---------------------------------------------------------------------------
111
+ // Private
112
+ // ---------------------------------------------------------------------------
113
+ _applyTransition(to, reason, opts) {
114
+ const from = this._state;
115
+ this._validateTransition(from, to, reason);
116
+ this._state = to;
117
+ if (to === "PAUSED") {
118
+ this._pauseReason = opts?.pauseReason ?? null;
119
+ }
120
+ else {
121
+ this._pauseReason = null;
122
+ }
123
+ const transition = {
124
+ from,
125
+ to,
126
+ reason,
127
+ pauseReason: to === "PAUSED" ? (opts?.pauseReason ?? undefined) : undefined,
128
+ timestamp: new Date().toISOString(),
129
+ };
130
+ this.onTransition?.(transition);
131
+ }
132
+ _validateTransition(from, to, reason) {
133
+ // Build a set of allowed (from, to) pairs
134
+ const valid = VALID_TRANSITIONS.some((t) => t.from === from && t.to === to);
135
+ if (!valid) {
136
+ throw new InvalidTransitionError(from, to, reason);
137
+ }
138
+ }
139
+ }
140
+ const VALID_TRANSITIONS = [
141
+ { from: "SETUP", to: "RUNNING" }, // TUI ready → send initial prompt
142
+ { from: "RUNNING", to: "CLEARING" }, // TICKET_COMPLETE
143
+ { from: "RUNNING", to: "PAUSED" }, // NEEDS_HUMAN_INPUT / permission / manual pause / unrecognized idle
144
+ { from: "RUNNING", to: "DONE" }, // EPIC_COMPLETE
145
+ { from: "CLEARING", to: "RUNNING" }, // /clear + re-prompt sent
146
+ { from: "CLEARING", to: "PAUSED" }, // CLEARING pipeline failed
147
+ { from: "PAUSED", to: "CLEARING" }, // pit resume (no message)
148
+ { from: "PAUSED", to: "RUNNING" }, // pit resume --message
149
+ { from: "SETUP", to: "DONE" }, // MANUAL_STOP from setup
150
+ { from: "PAUSED", to: "DONE" }, // MANUAL_STOP from paused
151
+ { from: "CLEARING", to: "DONE" }, // MANUAL_STOP from clearing
152
+ ];
153
+ //# sourceMappingURL=state-machine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-machine.js","sourceRoot":"","sources":["../src/state-machine.ts"],"names":[],"mappings":"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"}
package/dist/tmux.d.ts ADDED
@@ -0,0 +1,101 @@
1
+ /**
2
+ * tmux helpers for pit.
3
+ *
4
+ * All tmux operations shell out to the `tmux` CLI via execTmux().
5
+ * TmuxError captures the exit code, stderr, and original args for debugging.
6
+ */
7
+ export declare class TmuxError extends Error {
8
+ readonly exitCode: number | null;
9
+ readonly stderr: string;
10
+ readonly args: string[];
11
+ constructor(message: string, exitCode: number | null, stderr: string, args: string[]);
12
+ }
13
+ /**
14
+ * Internal helper: run tmux with the given args.
15
+ * Throws TmuxError on non-zero exit or if tmux is not installed.
16
+ */
17
+ export declare function execTmux(...args: string[]): Promise<{
18
+ stdout: string;
19
+ stderr: string;
20
+ }>;
21
+ /**
22
+ * Returns true if the named tmux session exists, false otherwise.
23
+ * Never throws for "session not found" — that is a valid false result.
24
+ */
25
+ export declare function sessionExists(name: string): Promise<boolean>;
26
+ /**
27
+ * Creates a new detached tmux session with the given name.
28
+ * Throws if the session already exists or tmux is unavailable.
29
+ */
30
+ export declare function createSession(name: string): Promise<void>;
31
+ /**
32
+ * Kills the named tmux session.
33
+ * Throws if the session does not exist or cannot be killed.
34
+ */
35
+ export declare function killSession(name: string): Promise<void>;
36
+ /**
37
+ * Creates a new window in the given session with the given name and working directory.
38
+ * Optionally sets per-window environment variables via the `-e` flag on `new-window`
39
+ * (NOT session-scoped `set-environment`) so each window gets its own env.
40
+ * Throws if the session does not exist.
41
+ */
42
+ export declare function createWindow(session: string, name: string, cwd: string, env?: Record<string, string>): Promise<void>;
43
+ /**
44
+ * Returns true if a window with the given name exists in the given session.
45
+ * Returns false if the session or window does not exist — never throws for
46
+ * missing session/window.
47
+ */
48
+ export declare function windowExists(session: string, name: string): Promise<boolean>;
49
+ /**
50
+ * Returns the list of window names in the given session.
51
+ * Returns an empty array if the session does not exist — never throws.
52
+ */
53
+ export declare function getWindowList(session: string): Promise<string[]>;
54
+ /**
55
+ * Returns the name of the first window (index 0) in the given session.
56
+ * Returns null if the session does not exist or has no windows.
57
+ * Never throws.
58
+ */
59
+ export declare function getFirstWindowName(session: string): Promise<string | null>;
60
+ /**
61
+ * Kills the named window in the given session.
62
+ * Throws if the window does not exist.
63
+ */
64
+ export declare function killWindow(session: string, name: string): Promise<void>;
65
+ /**
66
+ * Sends keys to a window in the given session.
67
+ * Fire-and-forget: there is no confirmation of delivery.
68
+ * The `--` separator prevents tmux from interpreting the keys string as flags.
69
+ * When autoSubmit is true, `Enter` is sent after typing to submit the input.
70
+ */
71
+ export declare function sendKeys(session: string, window: string, keys: string, autoSubmit?: boolean): Promise<void>;
72
+ /**
73
+ * Send a prompt to the agent by sending the text and Enter in separate calls.
74
+ * This is more reliable than sending them together, especially for OpenCode.
75
+ */
76
+ export declare function sendPrompt(session: string, window: string, prompt: string): Promise<void>;
77
+ /**
78
+ * Send a clear command to OpenCode by sending the command and Enter in separate calls.
79
+ * This prevents the "/" from triggering the agent selector instead of being a command.
80
+ */
81
+ export declare function sendClearCommand(session: string, window: string, clearCommand: string): Promise<void>;
82
+ /**
83
+ * Captures the pane output of a window in the given session.
84
+ * Defaults to the last 50 lines with ANSI escape codes stripped.
85
+ */
86
+ export declare function capturePane(session: string, window: string, options?: {
87
+ lines?: number;
88
+ stripAnsi?: boolean;
89
+ }): Promise<string>;
90
+ /**
91
+ * Sends a BEL character (ASCII 7) to a window in the given session.
92
+ * Used to notify the human that attention is needed.
93
+ */
94
+ export declare function sendBell(session: string, window: string): Promise<void>;
95
+ /**
96
+ * Sets a tmux user option (prefixed with `@`) on the given session.
97
+ * User options are used for status bar content and other metadata.
98
+ * Example: setUserOption(session, "pit-status", "[auth: running 5/12]")
99
+ */
100
+ export declare function setUserOption(session: string, key: string, value: string): Promise<void>;
101
+ //# sourceMappingURL=tmux.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tmux.d.ts","sourceRoot":"","sources":["../src/tmux.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,qBAAa,SAAU,SAAQ,KAAK;aAGhB,QAAQ,EAAE,MAAM,GAAG,IAAI;aACvB,MAAM,EAAE,MAAM;aACd,IAAI,EAAE,MAAM,EAAE;gBAH9B,OAAO,EAAE,MAAM,EACC,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EAAE;CAKjC;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAqBvF;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAWlE;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/D;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE7D;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC3B,OAAO,CAAC,IAAI,CAAC,CAQf;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAelF;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAatE;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGhF;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE7E;AAED;;;;;GAKG;AACH,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,UAAU,UAAO,GAChB,OAAO,CAAC,IAAI,CAAC,CAMf;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAO/F;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAOf;AAOD;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,GAChD,OAAO,CAAC,MAAM,CAAC,CAcjB;AAED;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE7E;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE9F"}
package/dist/tmux.js ADDED
@@ -0,0 +1,208 @@
1
+ /**
2
+ * tmux helpers for pit.
3
+ *
4
+ * All tmux operations shell out to the `tmux` CLI via execTmux().
5
+ * TmuxError captures the exit code, stderr, and original args for debugging.
6
+ */
7
+ import { execFile } from "node:child_process";
8
+ export class TmuxError extends Error {
9
+ exitCode;
10
+ stderr;
11
+ args;
12
+ constructor(message, exitCode, stderr, args) {
13
+ super(message);
14
+ this.exitCode = exitCode;
15
+ this.stderr = stderr;
16
+ this.args = args;
17
+ this.name = "TmuxError";
18
+ }
19
+ }
20
+ /**
21
+ * Internal helper: run tmux with the given args.
22
+ * Throws TmuxError on non-zero exit or if tmux is not installed.
23
+ */
24
+ export function execTmux(...args) {
25
+ return new Promise((resolve, reject) => {
26
+ execFile("tmux", args, (error, stdout, stderr) => {
27
+ if (error) {
28
+ if (error.code === "ENOENT") {
29
+ reject(new TmuxError("tmux is not installed or not in PATH", null, "", args));
30
+ return;
31
+ }
32
+ reject(new TmuxError(`tmux exited with code ${error.code ?? "unknown"}: ${stderr.trim()}`, typeof error.code === "number" ? error.code : null, stderr, args));
33
+ return;
34
+ }
35
+ resolve({ stdout, stderr });
36
+ });
37
+ });
38
+ }
39
+ /**
40
+ * Returns true if the named tmux session exists, false otherwise.
41
+ * Never throws for "session not found" — that is a valid false result.
42
+ */
43
+ export async function sessionExists(name) {
44
+ try {
45
+ await execTmux("has-session", "-t", name);
46
+ return true;
47
+ }
48
+ catch (err) {
49
+ if (err instanceof TmuxError && err.exitCode !== null) {
50
+ // Non-zero exit means session not found — not an error condition
51
+ return false;
52
+ }
53
+ throw err;
54
+ }
55
+ }
56
+ /**
57
+ * Creates a new detached tmux session with the given name.
58
+ * Throws if the session already exists or tmux is unavailable.
59
+ */
60
+ export async function createSession(name) {
61
+ await execTmux("new-session", "-d", "-s", name);
62
+ }
63
+ /**
64
+ * Kills the named tmux session.
65
+ * Throws if the session does not exist or cannot be killed.
66
+ */
67
+ export async function killSession(name) {
68
+ await execTmux("kill-session", "-t", name);
69
+ }
70
+ /**
71
+ * Creates a new window in the given session with the given name and working directory.
72
+ * Optionally sets per-window environment variables via the `-e` flag on `new-window`
73
+ * (NOT session-scoped `set-environment`) so each window gets its own env.
74
+ * Throws if the session does not exist.
75
+ */
76
+ export async function createWindow(session, name, cwd, env) {
77
+ const args = ["new-window", "-t", session, "-n", name, "-c", cwd];
78
+ if (env) {
79
+ for (const [key, value] of Object.entries(env)) {
80
+ args.push("-e", `${key}=${value}`);
81
+ }
82
+ }
83
+ await execTmux(...args);
84
+ }
85
+ /**
86
+ * Returns true if a window with the given name exists in the given session.
87
+ * Returns false if the session or window does not exist — never throws for
88
+ * missing session/window.
89
+ */
90
+ export async function windowExists(session, name) {
91
+ try {
92
+ const { stdout } = await execTmux("list-windows", "-t", session, "-F", "#{window_name}");
93
+ return stdout
94
+ .split("\n")
95
+ .map((l) => l.trim())
96
+ .filter(Boolean)
97
+ .includes(name);
98
+ }
99
+ catch (err) {
100
+ if (err instanceof TmuxError && err.exitCode !== null) {
101
+ // Non-zero exit — session not found or other non-fatal error
102
+ return false;
103
+ }
104
+ throw err;
105
+ }
106
+ }
107
+ /**
108
+ * Returns the list of window names in the given session.
109
+ * Returns an empty array if the session does not exist — never throws.
110
+ */
111
+ export async function getWindowList(session) {
112
+ try {
113
+ const { stdout } = await execTmux("list-windows", "-t", session, "-F", "#{window_name}");
114
+ return stdout
115
+ .split("\n")
116
+ .map((l) => l.trim())
117
+ .filter(Boolean);
118
+ }
119
+ catch (err) {
120
+ if (err instanceof TmuxError && err.exitCode !== null) {
121
+ return [];
122
+ }
123
+ throw err;
124
+ }
125
+ }
126
+ /**
127
+ * Returns the name of the first window (index 0) in the given session.
128
+ * Returns null if the session does not exist or has no windows.
129
+ * Never throws.
130
+ */
131
+ export async function getFirstWindowName(session) {
132
+ const windows = await getWindowList(session);
133
+ return windows.length > 0 ? windows[0] : null;
134
+ }
135
+ /**
136
+ * Kills the named window in the given session.
137
+ * Throws if the window does not exist.
138
+ */
139
+ export async function killWindow(session, name) {
140
+ await execTmux("kill-window", "-t", `${session}:${name}`);
141
+ }
142
+ /**
143
+ * Sends keys to a window in the given session.
144
+ * Fire-and-forget: there is no confirmation of delivery.
145
+ * The `--` separator prevents tmux from interpreting the keys string as flags.
146
+ * When autoSubmit is true, `Enter` is sent after typing to submit the input.
147
+ */
148
+ export async function sendKeys(session, window, keys, autoSubmit = true) {
149
+ const args = ["send-keys", "-t", `${session}:${window}`, "--", keys];
150
+ if (autoSubmit) {
151
+ args.push("Enter");
152
+ }
153
+ await execTmux(...args);
154
+ }
155
+ /**
156
+ * Send a prompt to the agent by sending the text and Enter in separate calls.
157
+ * This is more reliable than sending them together, especially for OpenCode.
158
+ */
159
+ export async function sendPrompt(session, window, prompt) {
160
+ // Send the prompt text without Enter
161
+ await sendKeys(session, window, prompt, false);
162
+ // Wait a small moment for the text to appear
163
+ await new Promise((resolve) => setTimeout(resolve, 100));
164
+ // Send Enter separately
165
+ await sendKeys(session, window, "Enter", false);
166
+ }
167
+ /**
168
+ * Send a clear command to OpenCode by sending the command and Enter in separate calls.
169
+ * This prevents the "/" from triggering the agent selector instead of being a command.
170
+ */
171
+ export async function sendClearCommand(session, window, clearCommand) {
172
+ // Send the clear command text without Enter
173
+ await sendKeys(session, window, clearCommand, false);
174
+ // Wait for the command to be processed (longer delay than sendPrompt)
175
+ await new Promise((resolve) => setTimeout(resolve, 500));
176
+ // Send Enter separately
177
+ await sendKeys(session, window, "Enter", false);
178
+ }
179
+ /** ANSI escape code stripping regex: covers CSI, OSC, character set, and SI sequences. */
180
+ const ANSI_REGEX =
181
+ // eslint-disable-next-line no-control-regex
182
+ /\x1b\[[0-9;]*[a-zA-Z]|\x1b\].*?(?:\x07|\x1b\\)|\x1b[()][0-9A-Z]|\x1b[>=<]|\x0f/g;
183
+ /**
184
+ * Captures the pane output of a window in the given session.
185
+ * Defaults to the last 50 lines with ANSI escape codes stripped.
186
+ */
187
+ export async function capturePane(session, window, options) {
188
+ const lines = options?.lines ?? 50;
189
+ const stripAnsi = options?.stripAnsi ?? true;
190
+ const { stdout } = await execTmux("capture-pane", "-t", `${session}:${window}`, "-p", "-S", `-${lines}`);
191
+ return stripAnsi ? stdout.replace(ANSI_REGEX, "") : stdout;
192
+ }
193
+ /**
194
+ * Sends a BEL character (ASCII 7) to a window in the given session.
195
+ * Used to notify the human that attention is needed.
196
+ */
197
+ export async function sendBell(session, window) {
198
+ await execTmux("send-keys", "-t", `${session}:${window}`, String.fromCharCode(7));
199
+ }
200
+ /**
201
+ * Sets a tmux user option (prefixed with `@`) on the given session.
202
+ * User options are used for status bar content and other metadata.
203
+ * Example: setUserOption(session, "pit-status", "[auth: running 5/12]")
204
+ */
205
+ export async function setUserOption(session, key, value) {
206
+ await execTmux("set-option", "-t", session, `@${key}`, value);
207
+ }
208
+ //# sourceMappingURL=tmux.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tmux.js","sourceRoot":"","sources":["../src/tmux.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,MAAM,OAAO,SAAU,SAAQ,KAAK;IAGhB;IACA;IACA;IAJlB,YACE,OAAe,EACC,QAAuB,EACvB,MAAc,EACd,IAAc;QAE9B,KAAK,CAAC,OAAO,CAAC,CAAC;QAJC,aAAQ,GAAR,QAAQ,CAAe;QACvB,WAAM,GAAN,MAAM,CAAQ;QACd,SAAI,GAAJ,IAAI,CAAU;QAG9B,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;IAC1B,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,GAAG,IAAc;IACxC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YAC/C,IAAI,KAAK,EAAE,CAAC;gBACV,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;oBACvD,MAAM,CAAC,IAAI,SAAS,CAAC,sCAAsC,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC;oBAC9E,OAAO;gBACT,CAAC;gBACD,MAAM,CACJ,IAAI,SAAS,CACX,yBAAyB,KAAK,CAAC,IAAI,IAAI,SAAS,KAAK,MAAM,CAAC,IAAI,EAAE,EAAE,EACpE,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAClD,MAAM,EACN,IAAI,CACL,CACF,CAAC;gBACF,OAAO;YACT,CAAC;YACD,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAY;IAC9C,IAAI,CAAC;QACH,MAAM,QAAQ,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAC1C,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,SAAS,IAAI,GAAG,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtD,iEAAiE;YACjE,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAY;IAC9C,MAAM,QAAQ,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAClD,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAY;IAC5C,MAAM,QAAQ,CAAC,cAAc,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAC7C,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAAe,EACf,IAAY,EACZ,GAAW,EACX,GAA4B;IAE5B,MAAM,IAAI,GAAG,CAAC,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;IAClE,IAAI,GAAG,EAAE,CAAC;QACR,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/C,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI,KAAK,EAAE,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IACD,MAAM,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAe,EAAE,IAAY;IAC9D,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,QAAQ,CAAC,cAAc,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,gBAAgB,CAAC,CAAC;QACzF,OAAO,MAAM;aACV,KAAK,CAAC,IAAI,CAAC;aACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;aACpB,MAAM,CAAC,OAAO,CAAC;aACf,QAAQ,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,SAAS,IAAI,GAAG,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtD,6DAA6D;YAC7D,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAe;IACjD,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,QAAQ,CAAC,cAAc,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,gBAAgB,CAAC,CAAC;QACzF,OAAO,MAAM;aACV,KAAK,CAAC,IAAI,CAAC;aACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;aACpB,MAAM,CAAC,OAAO,CAAC,CAAC;IACrB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,SAAS,IAAI,GAAG,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtD,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,OAAe;IACtD,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC;IAC7C,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAChD,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAe,EAAE,IAAY;IAC5D,MAAM,QAAQ,CAAC,aAAa,EAAE,IAAI,EAAE,GAAG,OAAO,IAAI,IAAI,EAAE,CAAC,CAAC;AAC5D,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,OAAe,EACf,MAAc,EACd,IAAY,EACZ,UAAU,GAAG,IAAI;IAEjB,MAAM,IAAI,GAAG,CAAC,WAAW,EAAE,IAAI,EAAE,GAAG,OAAO,IAAI,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACrE,IAAI,UAAU,EAAE,CAAC;QACf,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACrB,CAAC;IACD,MAAM,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAe,EAAE,MAAc,EAAE,MAAc;IAC9E,qCAAqC;IACrC,MAAM,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IAC/C,6CAA6C;IAC7C,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;IAC/D,wBAAwB;IACxB,MAAM,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;AAClD,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,OAAe,EACf,MAAc,EACd,YAAoB;IAEpB,4CAA4C;IAC5C,MAAM,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC;IACrD,sEAAsE;IACtE,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;IAC/D,wBAAwB;IACxB,MAAM,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;AAClD,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU;AACd,4CAA4C;AAC5C,iFAAiF,CAAC;AAEpF;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,OAAe,EACf,MAAc,EACd,OAAiD;IAEjD,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC;IACnC,MAAM,SAAS,GAAG,OAAO,EAAE,SAAS,IAAI,IAAI,CAAC;IAE7C,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,QAAQ,CAC/B,cAAc,EACd,IAAI,EACJ,GAAG,OAAO,IAAI,MAAM,EAAE,EACtB,IAAI,EACJ,IAAI,EACJ,IAAI,KAAK,EAAE,CACZ,CAAC;IAEF,OAAO,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;AAC7D,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,OAAe,EAAE,MAAc;IAC5D,MAAM,QAAQ,CAAC,WAAW,EAAE,IAAI,EAAE,GAAG,OAAO,IAAI,MAAM,EAAE,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;AACpF,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAe,EAAE,GAAW,EAAE,KAAa;IAC7E,MAAM,QAAQ,CAAC,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,EAAE,KAAK,CAAC,CAAC;AAChE,CAAC"}
@@ -0,0 +1,33 @@
1
+ export interface WorktreeResult {
2
+ path: string;
3
+ created: boolean;
4
+ branch: string;
5
+ }
6
+ /**
7
+ * Creates a git worktree for `epic` under `worktreeDir`.
8
+ *
9
+ * Idempotent: if the worktree already exists (directory + .git file), returns
10
+ * `{ created: false }` without running any git commands.
11
+ *
12
+ * Branch-exists fallback: if `git worktree add -b <branch>` fails because the
13
+ * branch already exists, retries with `git worktree add <path> <branch>`
14
+ * (checkout existing branch, no -b).
15
+ */
16
+ export declare function createWorktree(worktreeDir: string, epic: string, baseBranch: string): Promise<WorktreeResult>;
17
+ /**
18
+ * Returns true if `<worktreeDir>/<epic>` exists as a directory AND contains a
19
+ * `.git` file (worktrees have a .git file, not a .git directory).
20
+ */
21
+ export declare function worktreeExists(worktreeDir: string, epic: string): Promise<boolean>;
22
+ /**
23
+ * Removes the worktree at `<worktreeDir>/<epic>` via `git worktree remove --force`.
24
+ * Throws on failure.
25
+ */
26
+ export declare function removeWorktree(worktreeDir: string, epic: string): Promise<void>;
27
+ /**
28
+ * Returns true if the worktree at the given absolute path has uncommitted
29
+ * changes (staged, unstaged, or untracked files).
30
+ * Returns false if the path does not exist or is not a git worktree.
31
+ */
32
+ export declare function isWorktreeDirty(worktreePath: string): Promise<boolean>;
33
+ //# sourceMappingURL=worktree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worktree.d.ts","sourceRoot":"","sources":["../src/worktree.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAMD;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAClC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,cAAc,CAAC,CAgDzB;AAMD;;;GAGG;AACH,wBAAsB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAaxF;AAMD;;;GAGG;AACH,wBAAsB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGrF;AAMD;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQ5E"}