@samaksh/git-worker 0.2.0 → 0.2.2
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/dist/cli/index.js +1 -1
- package/dist/services/workspace/manager.js +1 -1
- package/dist/stages/select.js +135 -50
- package/package.json +4 -1
package/dist/cli/index.js
CHANGED
|
@@ -7,7 +7,7 @@ const program = new Command();
|
|
|
7
7
|
program
|
|
8
8
|
.name("git-worker")
|
|
9
9
|
.description("Safe, composable CLI/TUI for applying reviewed file changes across many Git branches")
|
|
10
|
-
.version("0.2.
|
|
10
|
+
.version("0.2.2")
|
|
11
11
|
.option("-r, --repo <path>", "Repository path or remote URL (default: current directory)")
|
|
12
12
|
.option("-c, --cached", "Select from previously cloned and cached repositories")
|
|
13
13
|
.option("-b, --branch <pattern>", "Branch pattern — glob (uat/*) or regex (/uat.*/)")
|
|
@@ -6,7 +6,7 @@ import { join, resolve, normalize } from "path";
|
|
|
6
6
|
import { simpleGit, CheckRepoActions } from "simple-git";
|
|
7
7
|
import { homedir } from "os";
|
|
8
8
|
import { createHash } from "crypto";
|
|
9
|
-
const GIT_WORKER_VERSION = "0.2.
|
|
9
|
+
const GIT_WORKER_VERSION = "0.2.2";
|
|
10
10
|
const LOCK_FILE = ".git-worker-lock";
|
|
11
11
|
/**
|
|
12
12
|
* Extract a human-readable repository name from a remote URL or file path.
|
package/dist/stages/select.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ok, err } from "../core/types.js";
|
|
2
2
|
import { workspaceError, inputCancelledError } from "../core/errors.js";
|
|
3
|
-
import { clack, sectionHeader, statusLine
|
|
3
|
+
import { clack, sectionHeader, statusLine } from "../utils/ui.js";
|
|
4
|
+
import inquirer from "inquirer";
|
|
4
5
|
import chalk from "chalk";
|
|
5
6
|
/**
|
|
6
7
|
* Matches a branch name against a pattern.
|
|
@@ -99,45 +100,44 @@ export const selectTargetsStage = async (context, deps) => {
|
|
|
99
100
|
label: `➜ Confirm and proceed with ${selectedCount} branch(es)`,
|
|
100
101
|
hint: "proceed to target file selection",
|
|
101
102
|
});
|
|
102
|
-
options.push({
|
|
103
|
-
value: "list",
|
|
104
|
-
label: "📋 Search & select branches from list",
|
|
105
|
-
hint: "type to filter, Space/Tab to toggle",
|
|
106
|
-
});
|
|
107
103
|
options.push({
|
|
108
104
|
value: "pattern",
|
|
109
105
|
label: "🔍 Filter by pattern or regex",
|
|
110
106
|
hint: "glob (uat/*) or regex (/uat.*/)",
|
|
111
107
|
});
|
|
112
108
|
options.push({
|
|
113
|
-
value: "
|
|
114
|
-
label:
|
|
115
|
-
hint: "
|
|
109
|
+
value: "list",
|
|
110
|
+
label: "📋 Select from branch list",
|
|
111
|
+
hint: "Space: toggle • a: select all • i: invert",
|
|
116
112
|
});
|
|
117
113
|
options.push({
|
|
118
114
|
value: "all",
|
|
119
|
-
label: `🌟 Select
|
|
115
|
+
label: `🌟 Select all (a) — all ${totalCount} branches`,
|
|
120
116
|
});
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
117
|
options.push({
|
|
124
|
-
value: "
|
|
125
|
-
label:
|
|
126
|
-
hint: "type to filter, Space/Tab to toggle",
|
|
118
|
+
value: "invert",
|
|
119
|
+
label: `🔄 Invert selection (i) — select other ${totalCount - selectedCount} branch(es)`,
|
|
127
120
|
});
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
128
123
|
options.push({
|
|
129
124
|
value: "pattern",
|
|
130
125
|
label: "🔍 Filter by pattern or regex",
|
|
131
|
-
hint: "glob (uat/*) or regex (/uat.*/)",
|
|
126
|
+
hint: "glob (uat/*) or regex (/uat.*/) — then select/review",
|
|
127
|
+
});
|
|
128
|
+
options.push({
|
|
129
|
+
value: "list",
|
|
130
|
+
label: "📋 Select from branch list",
|
|
131
|
+
hint: "Space: toggle • a: select all • i: invert",
|
|
132
132
|
});
|
|
133
133
|
options.push({
|
|
134
134
|
value: "all",
|
|
135
|
-
label: `🌟 Select
|
|
135
|
+
label: `🌟 Select all (a) — all ${totalCount} branches`,
|
|
136
136
|
hint: "select every branch in repository",
|
|
137
137
|
});
|
|
138
138
|
options.push({
|
|
139
139
|
value: "invert",
|
|
140
|
-
label: `🔄 Invert selection (select all ${totalCount} branches
|
|
140
|
+
label: `🔄 Invert selection (i) — select all ${totalCount} branches`,
|
|
141
141
|
});
|
|
142
142
|
}
|
|
143
143
|
const previewText = selectedCount > 0
|
|
@@ -188,7 +188,6 @@ export const selectTargetsStage = async (context, deps) => {
|
|
|
188
188
|
clack.log.warn(`Pattern '${pattern}' matched 0 of ${totalCount} branches.`);
|
|
189
189
|
continue;
|
|
190
190
|
}
|
|
191
|
-
selectedNames = new Set(matched.map((b) => b.name));
|
|
192
191
|
statusLine("ok", `Pattern '${pattern}' matched ${matched.length} branch(es):`);
|
|
193
192
|
for (const b of matched.slice(0, 5)) {
|
|
194
193
|
console.log(chalk.gray(` • ${b.name}`));
|
|
@@ -196,42 +195,128 @@ export const selectTargetsStage = async (context, deps) => {
|
|
|
196
195
|
if (matched.length > 5) {
|
|
197
196
|
console.log(chalk.gray(` ... and ${matched.length - 5} more`));
|
|
198
197
|
}
|
|
199
|
-
|
|
198
|
+
// Open interactive checkbox selection with the filtered branches
|
|
199
|
+
try {
|
|
200
|
+
const { picked } = await inquirer.prompt([
|
|
201
|
+
{
|
|
202
|
+
type: "checkbox",
|
|
203
|
+
name: "picked",
|
|
204
|
+
message: `Select from matched branches (${pattern}):`,
|
|
205
|
+
pageSize: 15,
|
|
206
|
+
choices: matched.map((b) => ({
|
|
207
|
+
name: `${b.name} (${b.isRemote ? `remote/${b.remote}` : "local"} ${b.sha.slice(0, 8)})`,
|
|
208
|
+
value: b.name,
|
|
209
|
+
checked: true,
|
|
210
|
+
})),
|
|
211
|
+
},
|
|
212
|
+
]);
|
|
213
|
+
selectedNames = new Set(picked);
|
|
214
|
+
statusLine("ok", `Selection updated: ${selectedNames.size} branch(es) selected`);
|
|
215
|
+
if (selectedNames.size > 0) {
|
|
216
|
+
const proceed = await clack.confirm({
|
|
217
|
+
message: `Proceed with ${selectedNames.size} selected branch(es)?`,
|
|
218
|
+
initialValue: true,
|
|
219
|
+
});
|
|
220
|
+
if (!clack.isCancel(proceed) && proceed) {
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
catch (e) {
|
|
227
|
+
if (e.isTtyError ||
|
|
228
|
+
e.name === "ExitPromptError" ||
|
|
229
|
+
e.message?.includes("force closed") ||
|
|
230
|
+
e.message?.includes("User force closed")) {
|
|
231
|
+
return err(inputCancelledError("Branch selection cancelled"));
|
|
232
|
+
}
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
200
235
|
}
|
|
201
236
|
if (choice === "list") {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
? `remote/${b.remote}
|
|
213
|
-
:
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
237
|
+
try {
|
|
238
|
+
const FILTER_ACTION = "__ACTION_FILTER_PATTERN__";
|
|
239
|
+
const choices = [
|
|
240
|
+
{
|
|
241
|
+
name: chalk.cyan("🔍 [Filter this list by pattern / regex...]"),
|
|
242
|
+
value: FILTER_ACTION,
|
|
243
|
+
checked: false,
|
|
244
|
+
},
|
|
245
|
+
new inquirer.Separator(),
|
|
246
|
+
...allBranches.map((b) => ({
|
|
247
|
+
name: `${b.name} (${b.isRemote ? `remote/${b.remote}` : "local"} ${b.sha.slice(0, 8)})`,
|
|
248
|
+
value: b.name,
|
|
249
|
+
checked: selectedNames.has(b.name),
|
|
250
|
+
})),
|
|
251
|
+
];
|
|
252
|
+
const { picked } = await inquirer.prompt([
|
|
253
|
+
{
|
|
254
|
+
type: "checkbox",
|
|
255
|
+
name: "picked",
|
|
256
|
+
message: "Select branches to process:",
|
|
257
|
+
pageSize: 15,
|
|
258
|
+
choices,
|
|
259
|
+
},
|
|
260
|
+
]);
|
|
261
|
+
if (picked.includes(FILTER_ACTION)) {
|
|
262
|
+
const patternInput = await clack.text({
|
|
263
|
+
message: "Enter glob pattern (e.g. uat/*) or regex (e.g. /uat.*/):",
|
|
264
|
+
placeholder: "/uat.*/ or uat/*",
|
|
265
|
+
validate: (v) => {
|
|
266
|
+
if (!v || v.trim().length === 0) {
|
|
267
|
+
return "Please enter a pattern or regex";
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
if (!clack.isCancel(patternInput)) {
|
|
272
|
+
const pattern = patternInput.trim();
|
|
273
|
+
const matched = allBranches.filter((b) => matchesBranchPattern(b.name, pattern));
|
|
274
|
+
if (matched.length === 0) {
|
|
275
|
+
clack.log.warn(`Pattern '${pattern}' matched 0 of ${totalCount} branches.`);
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
const { subPicked } = await inquirer.prompt([
|
|
279
|
+
{
|
|
280
|
+
type: "checkbox",
|
|
281
|
+
name: "subPicked",
|
|
282
|
+
message: `Select from matched branches (${pattern}):`,
|
|
283
|
+
pageSize: 15,
|
|
284
|
+
choices: matched.map((b) => ({
|
|
285
|
+
name: `${b.name} (${b.isRemote ? `remote/${b.remote}` : "local"} ${b.sha.slice(0, 8)})`,
|
|
286
|
+
value: b.name,
|
|
287
|
+
checked: true,
|
|
288
|
+
})),
|
|
289
|
+
},
|
|
290
|
+
]);
|
|
291
|
+
selectedNames = new Set(subPicked);
|
|
292
|
+
statusLine("ok", `Selection updated: ${selectedNames.size} branch(es) selected`);
|
|
293
|
+
if (selectedNames.size > 0) {
|
|
294
|
+
const proceed = await clack.confirm({
|
|
295
|
+
message: `Proceed with ${selectedNames.size} selected branch(es)?`,
|
|
296
|
+
initialValue: true,
|
|
297
|
+
});
|
|
298
|
+
if (!clack.isCancel(proceed) && proceed) {
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
222
304
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}
|
|
229
|
-
|
|
305
|
+
}
|
|
306
|
+
const actualPicked = picked.filter((p) => p !== FILTER_ACTION);
|
|
307
|
+
selectedNames = new Set(actualPicked);
|
|
308
|
+
statusLine("ok", `Selection updated: ${selectedNames.size} branch(es) selected`);
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
catch (e) {
|
|
312
|
+
if (e.isTtyError ||
|
|
313
|
+
e.name === "ExitPromptError" ||
|
|
314
|
+
e.message?.includes("force closed") ||
|
|
315
|
+
e.message?.includes("User force closed")) {
|
|
316
|
+
return err(inputCancelledError("Branch selection cancelled"));
|
|
317
|
+
}
|
|
230
318
|
continue;
|
|
231
319
|
}
|
|
232
|
-
selectedNames = new Set(picked);
|
|
233
|
-
statusLine("ok", `Selection updated: ${selectedNames.size} branch(es) selected`);
|
|
234
|
-
continue;
|
|
235
320
|
}
|
|
236
321
|
}
|
|
237
322
|
branches = allBranches.filter((b) => selectedNames.has(b.name));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@samaksh/git-worker",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Safe, composable TypeScript CLI/TUI for applying reviewed file changes across many Git branches",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -17,6 +17,9 @@
|
|
|
17
17
|
"README.md",
|
|
18
18
|
"docs"
|
|
19
19
|
],
|
|
20
|
+
"licenses": [
|
|
21
|
+
"MIT"
|
|
22
|
+
],
|
|
20
23
|
"scripts": {
|
|
21
24
|
"build": "tsc",
|
|
22
25
|
"prepublishOnly": "npm run build",
|