worktree-flow 0.0.7 → 0.0.8
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.
- package/README.md +7 -0
- package/dist/cli.js +2 -0
- package/dist/commands/config.js +10 -0
- package/dist/commands/tmux.js +45 -0
- package/dist/lib/config.js +1 -0
- package/dist/usecases/resumeTmuxSessions.js +47 -0
- package/dist/usecases/usecases.js +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/worktree-flow)
|
|
6
6
|
[](https://www.npmjs.com/package/worktree-flow)
|
|
7
|
+
[](https://github.com/simonpratt/worktree-flow/actions/workflows/build.yml)
|
|
7
8
|
[](https://github.com/simonpratt/worktree-flow/blob/master/LICENSE)
|
|
8
9
|
|
|
9
10
|
Stop juggling branches across repos. `flow` creates isolated workspaces with git worktrees from multiple repositories, all on the same branch, with a single command.
|
|
@@ -85,6 +86,12 @@ Remove a workspace and all its worktrees. Fetches latest, checks for uncommitted
|
|
|
85
86
|
|
|
86
87
|
Remove stale workspaces in bulk. Finds workspaces where all worktrees are clean, fully merged, and haven't been committed to in over 7 days.
|
|
87
88
|
|
|
89
|
+
### `flow tmux resume`
|
|
90
|
+
|
|
91
|
+
Create tmux sessions for all workspaces that don't already have one. Each session is created with split panes (one for the workspace root and one for each worktree) using a tiled layout. Skips workspaces that already have active sessions.
|
|
92
|
+
|
|
93
|
+
Requires `tmux` to be installed and the `tmux` config option to be enabled.
|
|
94
|
+
|
|
88
95
|
### `flow config set <key> <value>`
|
|
89
96
|
|
|
90
97
|
Configure flow settings. See [Configuration](#configuration) below.
|
package/dist/cli.js
CHANGED
|
@@ -10,6 +10,7 @@ import { registerRemoveCommand } from './commands/remove.js';
|
|
|
10
10
|
import { registerStatusCommand } from './commands/status.js';
|
|
11
11
|
import { registerPruneCommand } from './commands/prune.js';
|
|
12
12
|
import { registerFetchCommand } from './commands/fetch.js';
|
|
13
|
+
import { registerTmuxCommand } from './commands/tmux.js';
|
|
13
14
|
const program = new Command();
|
|
14
15
|
program
|
|
15
16
|
.name('flow')
|
|
@@ -25,4 +26,5 @@ registerRemoveCommand(program);
|
|
|
25
26
|
registerStatusCommand(program);
|
|
26
27
|
registerPruneCommand(program);
|
|
27
28
|
registerFetchCommand(program);
|
|
29
|
+
registerTmuxCommand(program);
|
|
28
30
|
program.parse();
|
package/dist/commands/config.js
CHANGED
|
@@ -45,6 +45,16 @@ export function registerConfigCommand(program) {
|
|
|
45
45
|
const displayValue = isDefault ? chalk.gray(value) : chalk.green(value);
|
|
46
46
|
services.console.log(` ${chalk.cyan(key)}: ${displayValue}`);
|
|
47
47
|
}
|
|
48
|
+
// Display per-repo post-checkout commands
|
|
49
|
+
const perRepoCommands = displayConfig.perRepoPostCheckout;
|
|
50
|
+
if (Object.keys(perRepoCommands).length > 0) {
|
|
51
|
+
services.console.log('');
|
|
52
|
+
services.console.log(chalk.bold('Per-repo post-checkout commands:'));
|
|
53
|
+
services.console.log('');
|
|
54
|
+
for (const [repo, command] of Object.entries(perRepoCommands)) {
|
|
55
|
+
services.console.log(` ${chalk.cyan(repo)}: ${chalk.green(command)}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
48
58
|
services.console.log('');
|
|
49
59
|
});
|
|
50
60
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { createServices } from '../lib/services.js';
|
|
3
|
+
import { createUseCases } from '../usecases/usecases.js';
|
|
4
|
+
export async function runTmuxResume(useCases, services) {
|
|
5
|
+
const { destPath } = services.config.getRequired();
|
|
6
|
+
const result = await useCases.resumeTmuxSessions.execute({ destPath });
|
|
7
|
+
if (result.totalWorkspaces === 0) {
|
|
8
|
+
services.console.log('No workspaces found.');
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
// Display results
|
|
12
|
+
if (result.sessionsCreated > 0) {
|
|
13
|
+
services.console.log(chalk.green(`✓ Created ${result.sessionsCreated} tmux session${result.sessionsCreated === 1 ? '' : 's'}`));
|
|
14
|
+
}
|
|
15
|
+
if (result.sessionsSkipped > 0) {
|
|
16
|
+
services.console.log(chalk.blue(`○ Skipped ${result.sessionsSkipped} existing session${result.sessionsSkipped === 1 ? '' : 's'}`));
|
|
17
|
+
}
|
|
18
|
+
if (result.errors.length > 0) {
|
|
19
|
+
services.console.log(chalk.red('\nErrors:'));
|
|
20
|
+
for (const error of result.errors) {
|
|
21
|
+
services.console.log(chalk.red(` ${error.workspace}: ${error.error}`));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// Summary
|
|
25
|
+
services.console.log(chalk.dim(`\nTotal: ${result.totalWorkspaces} workspace${result.totalWorkspaces === 1 ? '' : 's'}`));
|
|
26
|
+
}
|
|
27
|
+
export function registerTmuxCommand(program) {
|
|
28
|
+
const tmuxCommand = program
|
|
29
|
+
.command('tmux')
|
|
30
|
+
.description('Manage tmux sessions for workspaces');
|
|
31
|
+
tmuxCommand
|
|
32
|
+
.command('resume')
|
|
33
|
+
.description('Create tmux sessions for all workspaces that don\'t have one')
|
|
34
|
+
.action(async () => {
|
|
35
|
+
const services = createServices();
|
|
36
|
+
const useCases = createUseCases(services);
|
|
37
|
+
try {
|
|
38
|
+
await runTmuxResume(useCases, services);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
services.console.error(error.message);
|
|
42
|
+
services.process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
package/dist/lib/config.js
CHANGED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Use case for resuming tmux sessions across all workspaces.
|
|
3
|
+
* Creates sessions for workspaces that don't already have one.
|
|
4
|
+
*/
|
|
5
|
+
export class ResumeTmuxSessionsUseCase {
|
|
6
|
+
workspaceDir;
|
|
7
|
+
tmux;
|
|
8
|
+
constructor(workspaceDir, tmux) {
|
|
9
|
+
this.workspaceDir = workspaceDir;
|
|
10
|
+
this.tmux = tmux;
|
|
11
|
+
}
|
|
12
|
+
async execute(params) {
|
|
13
|
+
// 1. List all workspaces
|
|
14
|
+
const workspaces = this.workspaceDir.listWorkspaces(params.destPath);
|
|
15
|
+
let sessionsCreated = 0;
|
|
16
|
+
let sessionsSkipped = 0;
|
|
17
|
+
const errors = [];
|
|
18
|
+
// 2. Try to create tmux session for each workspace
|
|
19
|
+
for (const workspace of workspaces) {
|
|
20
|
+
try {
|
|
21
|
+
const worktreeDirs = this.workspaceDir.getWorktreeDirs(workspace.path);
|
|
22
|
+
// Try to create session - will throw on duplicate session
|
|
23
|
+
await this.tmux.createSession(workspace.path, workspace.name, worktreeDirs);
|
|
24
|
+
sessionsCreated++;
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
// Check if session already exists
|
|
28
|
+
if (error.message?.includes('duplicate session')) {
|
|
29
|
+
sessionsSkipped++;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
// Other error
|
|
33
|
+
errors.push({
|
|
34
|
+
workspace: workspace.name,
|
|
35
|
+
error: error.message || 'Unknown error',
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
totalWorkspaces: workspaces.length,
|
|
42
|
+
sessionsCreated,
|
|
43
|
+
sessionsSkipped,
|
|
44
|
+
errors,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -9,6 +9,7 @@ import { ListWorkspacesWithStatusUseCase } from './listWorkspacesWithStatus.js';
|
|
|
9
9
|
import { FetchAllReposUseCase } from './fetchAllRepos.js';
|
|
10
10
|
import { FetchWorkspaceReposUseCase } from './fetchWorkspaceRepos.js';
|
|
11
11
|
import { FetchUsedReposUseCase } from './fetchUsedRepos.js';
|
|
12
|
+
import { ResumeTmuxSessionsUseCase } from './resumeTmuxSessions.js';
|
|
12
13
|
/**
|
|
13
14
|
* Factory function for creating all use cases with their service dependencies.
|
|
14
15
|
* Use cases orchestrate workflows by coordinating multiple services.
|
|
@@ -26,5 +27,6 @@ export function createUseCases(services) {
|
|
|
26
27
|
checkWorkspaceStatus: new CheckWorkspaceStatusUseCase(services.workspaceDir, services.status),
|
|
27
28
|
discoverPrunableWorkspaces: new DiscoverPrunableWorkspacesUseCase(services.workspaceDir, services.status, services.git),
|
|
28
29
|
listWorkspacesWithStatus: new ListWorkspacesWithStatusUseCase(services.workspaceDir, services.status),
|
|
30
|
+
resumeTmuxSessions: new ResumeTmuxSessionsUseCase(services.workspaceDir, services.tmux),
|
|
29
31
|
};
|
|
30
32
|
}
|