worktree-flow 0.0.20 → 0.0.22

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.
@@ -24,6 +24,9 @@ export class NodeFileSystem {
24
24
  copyFileSync(src, dest) {
25
25
  fs.copyFileSync(src, dest);
26
26
  }
27
+ cpSync(src, dest, options) {
28
+ fs.cpSync(src, dest, options);
29
+ }
27
30
  rmSync(path, options) {
28
31
  fs.rmSync(path, options);
29
32
  }
@@ -6,16 +6,9 @@ import { createUseCases } from '../usecases/usecases.js';
6
6
  export async function runCheckout(branchName, useCases, services, deps) {
7
7
  const { sourcePath, destPath } = services.config.getRequired();
8
8
  const config = services.config.load();
9
- let shouldRunPostCheckout = false;
10
- if (config.postCheckout) {
11
- shouldRunPostCheckout = await deps.confirm({
12
- message: `Run "${config.postCheckout}" in all workspaces?`,
13
- default: true,
14
- });
15
- }
16
- services.console.log('\nChecking for branch...');
17
9
  try {
18
10
  // 1. Fetch all repos from source-path
11
+ console.log('');
19
12
  await useCases.fetchAllRepos.execute({
20
13
  sourcePath,
21
14
  fetchCacheTtlSeconds: config.fetchCacheTtlSeconds,
@@ -25,7 +18,9 @@ export async function runCheckout(branchName, useCases, services, deps) {
25
18
  sourcePath,
26
19
  branchName,
27
20
  });
28
- // 3. Display per-repo branch check results
21
+ // 3. Display only repos that have changes (matching or errored)
22
+ const discoveredCount = discoverResult.branchCheckResults.filter((r) => r.hasBranch).length;
23
+ console.log(chalk.bold(`\nFound ${discoveredCount} repos with branch "${branchName}"`));
29
24
  for (const checkResult of discoverResult.branchCheckResults) {
30
25
  if (checkResult.error) {
31
26
  services.console.log(`${checkResult.repoName}... ${chalk.red(`error: ${checkResult.error}`)}`);
@@ -33,15 +28,21 @@ export async function runCheckout(branchName, useCases, services, deps) {
33
28
  else if (checkResult.hasBranch) {
34
29
  services.console.log(`${checkResult.repoName}... ${chalk.green('found')}`);
35
30
  }
36
- else {
37
- services.console.log(`${checkResult.repoName}... ${chalk.dim('no branch')}`);
38
- }
39
31
  }
40
32
  // 4. Throw error if no repos match
41
33
  if (discoverResult.matchingRepos.length === 0) {
42
34
  throw new Error(`Branch "${branchName}" not found in any repo.`);
43
35
  }
44
- // 5. Create workspace directory, placeholder config, AGENTS.md, tmux session
36
+ // 5. Prompt for post-checkout
37
+ let shouldRunPostCheckout = false;
38
+ if (config.postCheckout) {
39
+ console.log('');
40
+ shouldRunPostCheckout = await deps.confirm({
41
+ message: `Run "${config.postCheckout}" in all workspaces?`,
42
+ default: true,
43
+ });
44
+ }
45
+ // 6. Create workspace directory, placeholder config, AGENTS.md, tmux session
45
46
  const workspaceResult = await useCases.createWorkspace.execute({
46
47
  branchName,
47
48
  sourcePath,
@@ -50,7 +51,7 @@ export async function runCheckout(branchName, useCases, services, deps) {
50
51
  });
51
52
  const { workspacePath, tmuxCreated } = workspaceResult;
52
53
  const sessionName = tmuxCreated ? branchName : undefined;
53
- // 6. For each matching repo in parallel: detect base branch, then addToWorkspace
54
+ // 7. For each matching repo in parallel: detect base branch, then addToWorkspace
54
55
  const results = await Promise.allSettled(discoverResult.matchingRepos.map(async (repoPath) => {
55
56
  const repoName = path.basename(repoPath);
56
57
  // Resolve per-repo post-checkout command
@@ -80,7 +81,7 @@ export async function runCheckout(branchName, useCases, services, deps) {
80
81
  .filter((r) => r.postCheckoutRan);
81
82
  const postCheckoutSuccess = postCheckoutResults.filter((r) => r.postCheckoutSuccess).length;
82
83
  const postCheckoutTotal = postCheckoutResults.length;
83
- // 7. Display results
84
+ // 8. Display results
84
85
  services.console.log(`\nCreated workspace at ${chalk.cyan(workspacePath)} with ${successCount}/${totalCount} repos.`);
85
86
  if (tmuxCreated) {
86
87
  services.console.log(`Created tmux session: ${chalk.cyan(branchName)}`);
package/dist/lib/fetch.js CHANGED
@@ -68,7 +68,7 @@ export class FetchService {
68
68
  await Promise.all(workers);
69
69
  if (!silent) {
70
70
  // Clear the progress line and show summary
71
- this.console.write('\r');
71
+ this.console.write('\r\x1b[K');
72
72
  const cacheInfo = cachedCount > 0 ? ` (${cachedCount} cached)` : '';
73
73
  if (failed > 0) {
74
74
  this.console.log(`Fetched ${total - failed}/${total} repos${cacheInfo} ${chalk.yellow(`(${failed} failed)`)}`);
package/dist/lib/tmux.js CHANGED
@@ -54,18 +54,14 @@ export class TmuxService {
54
54
  ]);
55
55
  }
56
56
  async addPane(sessionName, worktreePath) {
57
- await this.shell.execFile('tmux', [
57
+ const { stdout } = await this.shell.execFile('tmux', [
58
58
  'split-window',
59
59
  '-t',
60
60
  sessionName,
61
61
  '-c',
62
62
  worktreePath,
63
- ]);
64
- const { stdout } = await this.shell.execFile('tmux', [
65
- 'display-message',
66
- '-p',
67
- '-t',
68
- sessionName,
63
+ '-P',
64
+ '-F',
69
65
  '#{pane_index}',
70
66
  ]);
71
67
  await this.shell.execFile('tmux', [
@@ -23,6 +23,12 @@ export class WorkspaceDirectoryService {
23
23
  this.fs.copyFileSync(agentsPath, path.join(workspacePath, 'AGENTS.md'));
24
24
  }
25
25
  }
26
+ copyDevcontainer(sourcePath, workspacePath) {
27
+ const devcontainerPath = path.join(sourcePath, '.devcontainer');
28
+ if (this.fs.existsSync(devcontainerPath)) {
29
+ this.fs.cpSync(devcontainerPath, path.join(workspacePath, '.devcontainer'), { recursive: true });
30
+ }
31
+ }
26
32
  detectWorkspace(cwd, destPath) {
27
33
  const normalizedCwd = path.resolve(cwd);
28
34
  const normalizedDest = path.resolve(destPath);
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Use case for creating a new workspace directory with initial config, AGENTS.md copy,
3
- * and an optional tmux session (root pane only, no worktrees yet).
3
+ * .devcontainer copy, and an optional tmux session (root pane only, no worktrees yet).
4
4
  */
5
5
  export class CreateWorkspaceUseCase {
6
6
  workspaceDir;
@@ -18,7 +18,9 @@ export class CreateWorkspaceUseCase {
18
18
  this.workspaceConfig.savePlaceholder(workspacePath);
19
19
  // 3. Copy AGENTS.md if it exists in source-path
20
20
  this.workspaceDir.copyAgentsMd(params.sourcePath, workspacePath);
21
- // 4. Create tmux session (root pane only) if enabled
21
+ // 4. Copy .devcontainer if it exists in source-path
22
+ this.workspaceDir.copyDevcontainer(params.sourcePath, workspacePath);
23
+ // 5. Create tmux session (root pane only) if enabled
22
24
  let tmuxCreated = false;
23
25
  if (params.tmux) {
24
26
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "worktree-flow",
3
- "version": "0.0.20",
3
+ "version": "0.0.22",
4
4
  "description": "Manage git worktrees across a poly-repo environment",
5
5
  "type": "module",
6
6
  "bin": {