@boopkit/cli 0.4.0 → 0.6.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.
@@ -15,6 +15,8 @@ export interface LoopOptions {
15
15
  skipPerms?: boolean;
16
16
  bare?: boolean;
17
17
  json?: boolean;
18
+ merge?: boolean | string;
19
+ watch?: boolean;
18
20
  }
19
21
  interface ClaudeRunResult {
20
22
  exitCode: number;
@@ -24,6 +26,79 @@ export interface PlanResult {
24
26
  featureId: string | null;
25
27
  costUsd: number | null;
26
28
  }
29
+ export type WorktreeMode = 'work-flow' | 'prefix';
30
+ export interface WorktreeContext {
31
+ path: string;
32
+ branch: string;
33
+ scope: string;
34
+ mode: WorktreeMode;
35
+ }
36
+ /**
37
+ * Hard requirement: beads must run in Dolt server mode so concurrent worktrees
38
+ * share one issue DB over TCP. Reads `.beads/metadata.json` and throws an
39
+ * actionable error on embedded/missing — call before any `bd` work.
40
+ */
41
+ export declare function assertDoltServerMode(projectDir: string, deps?: {
42
+ existsSyncFn?: (p: string) => boolean;
43
+ readFileFn?: (p: string, enc: string) => string;
44
+ }): void;
45
+ /**
46
+ * Discover the running shared Dolt server's port from `.beads/dolt-server.port`.
47
+ * Worktrees never start their own server — they reuse this one over TCP.
48
+ * Returns null when no port file is present (caller errors clearly). Liveness
49
+ * (pid still running) is best-effort and never blocks discovery.
50
+ */
51
+ export declare function discoverDoltServer(projectDir: string, deps?: {
52
+ existsSyncFn?: (p: string) => boolean;
53
+ readFileFn?: (p: string, enc: string) => string;
54
+ isPidAliveFn?: (pid: number) => boolean;
55
+ }): {
56
+ port: number;
57
+ pid?: number;
58
+ alive?: boolean;
59
+ } | null;
60
+ /**
61
+ * Create (or reuse) the loop's own git worktree at `../boop-<scope>` on branch
62
+ * `work/<scope>`, then pre-warm `npm install --prefer-offline`. This is what
63
+ * makes N loops run concurrently without colliding — each gets its own tree and
64
+ * branch; main is never checked out.
65
+ *
66
+ * Collision (Decision #4): if a worktree already exists at the path on the same
67
+ * branch, reuse it (and skip install if node_modules is present); if the path is
68
+ * occupied by something else, throw a clear error rather than silently suffixing.
69
+ */
70
+ export declare function createWorktree(scope: string, mode: WorktreeMode, projectDir: string, deps?: {
71
+ execSyncFn?: (cmd: string, opts: {
72
+ encoding: string;
73
+ cwd?: string;
74
+ stdio?: unknown;
75
+ }) => string;
76
+ existsSyncFn?: (p: string) => boolean;
77
+ copyFileFn?: (src: string, dest: string) => void;
78
+ readdirFn?: (p: string) => string[];
79
+ readFileFn?: (p: string) => string;
80
+ appendFileFn?: (p: string, data: string) => void;
81
+ }): WorktreeContext;
82
+ /**
83
+ * Write `.boop-worktree.json` into the worktree root. This is the contract
84
+ * Slice 2 (`boop loop --merge`) reads — `mode` drives its readiness query.
85
+ */
86
+ export declare function writeWorktreeMetadata(ctx: WorktreeContext, meta: {
87
+ workPath?: string;
88
+ flowPath?: string;
89
+ boopVersion: string;
90
+ startTime: string;
91
+ operator?: string;
92
+ budget?: string;
93
+ model?: string;
94
+ }, deps?: {
95
+ writeFileFn?: (p: string, data: string) => void;
96
+ }): Record<string, unknown>;
97
+ interface ReadyIssue {
98
+ id: string;
99
+ issue_type: string;
100
+ parent?: string;
101
+ }
27
102
  export declare function buildPlanningPrompt(workContent: string, flowContent: string, prefix?: string): string;
28
103
  export type RunClaudeFn = (prompt: string, opts: {
29
104
  budget: string;
@@ -32,6 +107,7 @@ export type RunClaudeFn = (prompt: string, opts: {
32
107
  sessionId: string;
33
108
  resume?: boolean;
34
109
  bare?: boolean;
110
+ cwd?: string;
35
111
  }, logFile: string, readableLogFile?: string) => Promise<ClaudeRunResult>;
36
112
  export declare function planWork(opts: {
37
113
  workPath: string;
@@ -55,9 +131,75 @@ export declare function findFallbackFeature(features: Array<{
55
131
  export declare function extractFeatureId(text: string): string | null;
56
132
  export declare function isUsageLimitText(text: string): boolean;
57
133
  export declare function isStandaloneUsageLimitMessage(text: string): boolean;
134
+ /**
135
+ * Resolve the model for a loop run. Precedence: an explicit `--model` flag, then
136
+ * the `ANTHROPIC_MODEL` environment variable, then the built-in default. The env
137
+ * fallback is what lets a nested `boop loop` (spawned by a Claude session that
138
+ * the parent loop launched with ANTHROPIC_MODEL set) inherit the same model
139
+ * instead of silently dropping to the default. Keep the default (claude-opus-4-8)
140
+ * in sync with the `--model` help text in cli.ts.
141
+ */
142
+ export declare function resolveModel(optionModel: string | undefined, env?: NodeJS.ProcessEnv): string;
143
+ /**
144
+ * Whether the loop should resume an issue that a session left open. A `blocked`
145
+ * issue was deliberately halted by the prior session because it judged it could
146
+ * not safely proceed — resuming just spends budget re-halting and risks the
147
+ * resume prompt being read as authorization to work past the block. Stop and
148
+ * surface it for the operator instead. Any other open status is genuinely
149
+ * unfinished work and is fine to resume.
150
+ */
151
+ export declare function shouldResumeUnclosedIssue(status: string | undefined): boolean;
58
152
  export declare function isIssueInScope(issue: {
59
153
  id: string;
60
154
  parent?: string;
61
155
  }, prefix: string, getParentById: (id: string) => string | null): boolean;
156
+ /**
157
+ * A workflow-owned feature root is a feature-type issue that owns child tasks
158
+ * (the workflow steps: understand → plan → … → closeout). Once those children
159
+ * close, `bd ready` surfaces the feature root itself — but it is a *container*,
160
+ * not directly claimable work. Claiming it lets a worker session absorb
161
+ * punch-list fixes or whole workflow steps (the boop-qna / wishfleet-O7 bug).
162
+ *
163
+ * `hasChildren` is only consulted for feature-type issues, so non-features never
164
+ * pay the cost of the children lookup.
165
+ */
166
+ export declare function isClaimableReadyIssue(issue: {
167
+ id: string;
168
+ issue_type: string;
169
+ }, hasChildren: (id: string) => boolean): boolean;
170
+ /**
171
+ * Decide whether the loop should close a feature root itself, as a safety net,
172
+ * when its workflow chain has completed. The intended owner of this close is the
173
+ * closeout child task (see boop-feature-dev-workflow SKILL step 6), but if that
174
+ * task never closed the root — or no closeout task exists — the feature would be
175
+ * orphaned open forever once filtered from claimable work. Only close when the
176
+ * feature is still open and every child is closed.
177
+ */
178
+ export declare function shouldCloseFeatureRoot(feature: {
179
+ status: string;
180
+ issue_type: string;
181
+ } | null, children: {
182
+ status: string;
183
+ }[]): boolean;
184
+ export declare function getMatchingReadyIssues(prefix: string, includeFeatures: boolean, deps?: {
185
+ execSyncFn?: (cmd: string, opts: {
186
+ encoding: string;
187
+ }) => string;
188
+ }): ReadyIssue[];
189
+ /**
190
+ * Default per-issue budget cap in USD. The planning phase and each task each get
191
+ * this cap. Single source of truth — imported by cli.ts for its --budget help
192
+ * text so the documented default and the runtime fallback can't drift.
193
+ */
194
+ export declare const DEFAULT_BUDGET_USD = "25.00";
195
+ /**
196
+ * Sent when resuming a session that ended without closing its issue. The opener
197
+ * must NOT read as operator authorization (the old "Approved. Continue..." text
198
+ * could be taken as permission to work past a block): an agent that marked the
199
+ * issue `blocked` made a deliberate safety call, and the block stands. The loop
200
+ * also skips resuming a `blocked` issue entirely (see shouldResumeUnclosedIssue)
201
+ * — this clause is the belt-and-suspenders for a block raised mid-resume.
202
+ */
203
+ export declare const RESUME_PROMPT = "The previous session ended without closing this issue. Reload the state and continue.\n\nIMPORTANT: If the previous session marked this issue blocked with a diagnostic, the block stands. Do not re-open it or work around it. Read the issue's notes and the previous session's commits, confirm the block is still appropriate, and stop without taking further action.\n\nOtherwise, complete the workflow:\n- Finish any remaining implementation\n- Run tests to verify\n- Commit changes referencing the issue ID\n- Close the issue with bd close\n- Push with: git pull --rebase && git push\n\nDo not pause for review of completed work. Complete everything now.";
62
204
  export declare function runLoop(options: LoopOptions): Promise<void>;
63
205
  export {};