@bakers-build/addon-herdr 0.1.2-next.93 → 0.2.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.
@@ -0,0 +1,19 @@
1
+ export { renderTemplate } from './config.js';
2
+ export { restoreWritable } from './fs.js';
3
+ export { type LaunchScript, type LaunchWatchOptions, watchLaunch, writeLaunchScript, } from './launch-script.js';
4
+ export { truncateOpenTabTitle, validateBranch } from './workspace.js';
5
+ /** Runs `git <args>`; resolves with stdout, rejects on non-zero exit. */
6
+ export type BaseRefGitExec = (args: string[]) => Promise<string>;
7
+ /**
8
+ * Resolve (and fetch) the base ref a NEW worktree branch is created from, so a
9
+ * run starts from the pushed remote state, not the host's local branch.
10
+ *
11
+ * A ticket's `override` (its `origin/<base_branch>`) wins only when it resolves
12
+ * on the remote after a fetch; otherwise the configured `baseBranch`, else the
13
+ * upstream of the repo's HEAD (e.g. `origin/develop`). A repo with no upstream
14
+ * yields `undefined`. Fetch failures are non-fatal (offline / unknown remote).
15
+ */
16
+ export declare function resolveBaseRef(root: string, execGit: BaseRefGitExec, opts: {
17
+ baseBranch?: string;
18
+ override?: string;
19
+ }): Promise<string | undefined>;
@@ -0,0 +1,72 @@
1
+ // The herdr pieces a sibling host addon (`@bakers-build/addon-orca`) reuses
2
+ // rather than copies: the 0600 launch wrapper + its watch, the label template,
3
+ // the Drupal write-bit restore and the worktree base-ref resolution.
4
+ export { renderTemplate } from './config.js';
5
+ export { restoreWritable } from './fs.js';
6
+ export { watchLaunch, writeLaunchScript, } from './launch-script.js';
7
+ export { truncateOpenTabTitle, validateBranch } from './workspace.js';
8
+ /**
9
+ * Resolve (and fetch) the base ref a NEW worktree branch is created from, so a
10
+ * run starts from the pushed remote state, not the host's local branch.
11
+ *
12
+ * A ticket's `override` (its `origin/<base_branch>`) wins only when it resolves
13
+ * on the remote after a fetch; otherwise the configured `baseBranch`, else the
14
+ * upstream of the repo's HEAD (e.g. `origin/develop`). A repo with no upstream
15
+ * yields `undefined`. Fetch failures are non-fatal (offline / unknown remote).
16
+ */
17
+ export async function resolveBaseRef(root, execGit, opts) {
18
+ const fetch = async (ref) => {
19
+ const slash = ref.indexOf('/');
20
+ if (slash <= 0)
21
+ return;
22
+ try {
23
+ await execGit([
24
+ '-C',
25
+ root,
26
+ 'fetch',
27
+ ref.slice(0, slash),
28
+ ref.slice(slash + 1),
29
+ ]);
30
+ }
31
+ catch {
32
+ // Non-fatal: use whatever the local tracking ref already points at.
33
+ }
34
+ };
35
+ if (opts.override !== undefined) {
36
+ await fetch(opts.override);
37
+ try {
38
+ await execGit([
39
+ '-C',
40
+ root,
41
+ 'rev-parse',
42
+ '--verify',
43
+ '--quiet',
44
+ `${opts.override}^{commit}`,
45
+ ]);
46
+ return opts.override;
47
+ }
48
+ catch {
49
+ // Not on the remote — fall back to the default base below.
50
+ }
51
+ }
52
+ let ref = opts.baseBranch;
53
+ if (!ref) {
54
+ try {
55
+ ref =
56
+ (await execGit([
57
+ '-C',
58
+ root,
59
+ 'rev-parse',
60
+ '--abbrev-ref',
61
+ '--symbolic-full-name',
62
+ '@{u}',
63
+ ])).trim() || undefined;
64
+ }
65
+ catch {
66
+ return undefined;
67
+ }
68
+ }
69
+ if (ref !== undefined)
70
+ await fetch(ref);
71
+ return ref;
72
+ }
@@ -89,6 +89,8 @@ export interface HerdrWorkspaceOptions {
89
89
  /** Optional logger for the best-effort open-layout path (defaults to noop). */
90
90
  logger?: ConductorLogger;
91
91
  }
92
+ /** Git-safe branch validation: safe ref chars only, no leading dot, no `..`. */
93
+ export declare function validateBranch(branch: string): void;
92
94
  export declare class HerdrWorkspace implements BbWorkspace {
93
95
  private readonly options;
94
96
  private readonly execHerdr;
@@ -106,23 +108,6 @@ export declare class HerdrWorkspace implements BbWorkspace {
106
108
  /** Herdr-backed intent-level agent host for `bakers ui` (BB-190). */
107
109
  readonly agentHost: AgentLaunchHost;
108
110
  constructor(options: HerdrWorkspaceOptions);
109
- /**
110
- * Resolve the base ref for a new worktree branch. Prefers the configured
111
- * `baseBranch`; otherwise the remote tracking branch of the repo's HEAD
112
- * (e.g. `origin/develop`). Best-effort: a repo with no upstream returns
113
- * `undefined`, leaving herdr to fall back to the parent workspace HEAD.
114
- */
115
- private resolveBaseRef;
116
- /**
117
- * Fetch the base ref's remote branch so the new worktree starts from the
118
- * latest pushed state. Best-effort: offline / unknown remote is non-fatal.
119
- */
120
- private fetchBaseRef;
121
- /**
122
- * Verify a base-ref candidate resolves on the remote (after fetching it).
123
- * Returns the ref when it exists, else undefined so the caller falls back.
124
- */
125
- private verifyRemoteRef;
126
111
  /**
127
112
  * Ensure the branch's worktree + herdr workspace exist.
128
113
  *
@@ -5,6 +5,7 @@ import { herdrAgentHost } from './agents.js';
5
5
  import { renderTemplate } from './config.js';
6
6
  import { applyPaneLayout, isRecord, parseJson } from './pane-layout.js';
7
7
  import { deriveHerdrRoot } from './root-anchor.js';
8
+ import { resolveBaseRef } from './shared.js';
8
9
  /** Max `{title}` length substituted into the overview **tab** label (BB-374). */
9
10
  export const OPEN_TAB_TITLE_MAX = 30;
10
11
  /** Cap overview-tab `{title}` and append `...` when truncated. */
@@ -94,7 +95,7 @@ function parseWorktreeRootPane(output) {
94
95
  return tabId !== undefined ? { paneId, tabId } : { paneId };
95
96
  }
96
97
  /** Git-safe branch validation: safe ref chars only, no leading dot, no `..`. */
97
- function validateBranch(branch) {
98
+ export function validateBranch(branch) {
98
99
  if (!/^[A-Za-z0-9._/-]+$/.test(branch) ||
99
100
  branch.startsWith('.') ||
100
101
  branch.includes('..')) {
@@ -128,70 +129,6 @@ export class HerdrWorkspace {
128
129
  this.logger = options.logger ?? noopLogger;
129
130
  this.agentHost = herdrAgentHost(this.execHerdr);
130
131
  }
131
- /**
132
- * Resolve the base ref for a new worktree branch. Prefers the configured
133
- * `baseBranch`; otherwise the remote tracking branch of the repo's HEAD
134
- * (e.g. `origin/develop`). Best-effort: a repo with no upstream returns
135
- * `undefined`, leaving herdr to fall back to the parent workspace HEAD.
136
- */
137
- async resolveBaseRef() {
138
- if (this.options.baseBranch) {
139
- return this.options.baseBranch;
140
- }
141
- try {
142
- const ref = (await this.execGit([
143
- '-C',
144
- this.root,
145
- 'rev-parse',
146
- '--abbrev-ref',
147
- '--symbolic-full-name',
148
- '@{u}',
149
- ])).trim();
150
- return ref || undefined;
151
- }
152
- catch {
153
- return undefined;
154
- }
155
- }
156
- /**
157
- * Fetch the base ref's remote branch so the new worktree starts from the
158
- * latest pushed state. Best-effort: offline / unknown remote is non-fatal.
159
- */
160
- async fetchBaseRef(baseRef) {
161
- const slash = baseRef.indexOf('/');
162
- if (slash <= 0) {
163
- return;
164
- }
165
- const remote = baseRef.slice(0, slash);
166
- const branch = baseRef.slice(slash + 1);
167
- try {
168
- await this.execGit(['-C', this.root, 'fetch', remote, branch]);
169
- }
170
- catch {
171
- // Non-fatal: use whatever the local tracking ref already points at.
172
- }
173
- }
174
- /**
175
- * Verify a base-ref candidate resolves on the remote (after fetching it).
176
- * Returns the ref when it exists, else undefined so the caller falls back.
177
- */
178
- async verifyRemoteRef(ref) {
179
- await this.fetchBaseRef(ref);
180
- try {
181
- await this.execGit([
182
- '-C',
183
- this.root,
184
- 'rev-parse',
185
- '--verify',
186
- '--quiet',
187
- `${ref}^{commit}`,
188
- ]);
189
- return ref;
190
- }
191
- catch {
192
- return undefined;
193
- }
194
- }
195
132
  /**
196
133
  * Ensure the branch's worktree + herdr workspace exist.
197
134
  *
@@ -235,16 +172,12 @@ export class HerdrWorkspace {
235
172
  // host's local checked-out branch. A ticket passes its `origin/<base_branch>`
236
173
  // as the override; use it only when it resolves on the remote, else fall
237
174
  // back to the default base.
238
- let baseRef;
239
- if (baseRefOverride !== undefined) {
240
- baseRef = await this.verifyRemoteRef(baseRefOverride);
241
- }
242
- if (baseRef === undefined) {
243
- baseRef = await this.resolveBaseRef();
244
- if (baseRef !== undefined) {
245
- await this.fetchBaseRef(baseRef);
246
- }
247
- }
175
+ const baseRef = await resolveBaseRef(this.root, this.execGit, {
176
+ ...(this.options.baseBranch
177
+ ? { baseBranch: this.options.baseBranch }
178
+ : {}),
179
+ ...(baseRefOverride !== undefined ? { override: baseRefOverride } : {}),
180
+ });
248
181
  const createOutput = await this.execHerdr([
249
182
  'worktree',
250
183
  'create',
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@bakers-build/addon-herdr",
3
- "version": "0.1.2-next.93",
3
+ "version": "0.2.0",
4
4
  "description": "bakers.build herdr integration: spawn executor + per-ticket worktree workspace.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "exports": {
8
8
  ".": "./dist/src/index.js",
9
- "./preset": "./dist/src/preset.js"
9
+ "./preset": "./dist/src/preset.js",
10
+ "./shared": "./dist/src/shared.js"
10
11
  },
11
12
  "files": [
12
13
  "dist/src"
@@ -20,10 +21,10 @@
20
21
  "directory": "bakers-cli/addons/herdr"
21
22
  },
22
23
  "dependencies": {
23
- "@bakers-build/addon-workspace-git": "^0.1.2-next.93"
24
+ "@bakers-build/addon-workspace-git": "^0.2.0"
24
25
  },
25
26
  "peerDependencies": {
26
- "@bakers-build/conductor": "^0.1.2-next.93",
27
- "@bakers-build/core": "^0.1.2-next.93"
27
+ "@bakers-build/conductor": "^0.2.0",
28
+ "@bakers-build/core": "^0.2.0"
28
29
  }
29
30
  }