zapdev 1.12.0 → 1.14.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.
- package/README.md +5 -5
- package/dist/cli.js +193 -58
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -114,7 +114,7 @@ Stages all changes, scans them with Gitleaks when installed, generates Conventio
|
|
|
114
114
|
- **Unified review:** generates messages in parallel, then presents every message with its repository name and branch. Repositories with pending changes are shown in bold when terminal styling is enabled.
|
|
115
115
|
- **Actions:** commit all, commit only a named repository, edit a named repository's message, or cancel. Editing returns to the review menu.
|
|
116
116
|
|
|
117
|
-
Repositories with no new changes can still send existing commits. Unselected or cancelled changes remain staged; failed or unselected commit drafts are not pushed. The branch is chosen before committing, never when pushing. Push confirmation is asked once, only for repositories with a
|
|
117
|
+
Repositories with no new changes can still send existing commits. Unselected or cancelled changes remain staged; failed or unselected commit drafts are not pushed. The branch is chosen before committing, never when pushing. Push confirmation is asked once, only for repositories with a configured upstream remote; it is omitted when none have one.
|
|
118
118
|
|
|
119
119
|
```bash
|
|
120
120
|
zapdev commit
|
|
@@ -127,7 +127,7 @@ zapdev commit
|
|
|
127
127
|
| `--effort <effort>` | Override the reasoning effort |
|
|
128
128
|
| `-t, --type <type>` | Force an exact lowercase Conventional Commit type (`feat`, `fix`, `chore`, etc.) |
|
|
129
129
|
| `-p, --push` | Skip push confirmation and push the current branch |
|
|
130
|
-
| `--pull` | Pull fast-forward updates in
|
|
130
|
+
| `--pull` | Pull fast-forward updates in repositories with an upstream remote before staging or message generation |
|
|
131
131
|
| `-s, --staged` | Commit only changes that are already staged |
|
|
132
132
|
| `-y, --yes` | Skip commit review; still confirm push unless `--push` is set |
|
|
133
133
|
|
|
@@ -143,13 +143,13 @@ Before contacting the LLM endpoint, zapdev runs `gitleaks git --staged --verbose
|
|
|
143
143
|
|
|
144
144
|
Preparation and commit failures are reported per repository while the others continue. A push failure stops all remaining sends. Any failure produces a nonzero exit code.
|
|
145
145
|
|
|
146
|
-
With `--pull`, repositories are pulled one at a time before any staging, secret scan, or message generation. Git uses each current branch's configured upstream.
|
|
146
|
+
With `--pull`, repositories are pulled one at a time before any staging, secret scan, or message generation. Git uses each current branch's configured upstream. Repositories without a configured upstream remote are skipped with a warning, even if they have a single remote. Divergent history, a missing upstream branch, or conflicting local changes stop the entire flow before staging; no rebase, merge commit, or automatic stash is performed. Earlier successful pulls remain applied.
|
|
147
147
|
|
|
148
|
-
In a terminal,
|
|
148
|
+
In a terminal, commit, pull, and push output is streamed into Clack task logs; silent commands create no output block. Git keeps terminal input, but stdout/stderr are pipes: hooks requiring a TTY on those streams are not supported. Git controls hook stdin; hooks can read from `/dev/tty`, although prompts written there appear outside the blocks. `--yes` skips commit review, not native prompts. Without a terminal, stdout/stderr are streamed directly. Each hook has a 60-second deadline, including time spent answering prompts; override it with `ZD_HOOK_TIMEOUT=120 zapdev commit`.
|
|
149
149
|
|
|
150
150
|
Immediately before each selected commit, zapdev asks for a local branch only when the current name exactly matches `main`, `master`, `principal`, `dev`, or `development`, including with `--yes`. The first prompt starts empty; subsequent prompts reuse the previous input across repositories. Press Enter to accept the prefilled name; an empty or cleared input keeps the current branch and leaves the next prompt empty. A different name switches to the existing local branch or creates it from HEAD, without forcing or discarding pending changes. Other branches and repositories with nothing to commit never trigger this prompt. Cancelling stops the remaining commits and all pushes; earlier commits remain local.
|
|
151
151
|
|
|
152
|
-
Pushes always target a remote branch with the current local branch's name, with no destination input. They use the current branch's upstream remote
|
|
152
|
+
Pushes always target a remote branch with the current local branch's name, with no destination input. They use only the current branch's configured upstream remote and run `git push <remote> HEAD:refs/heads/<current-branch>`. Repositories without a configured upstream remote are skipped with a warning, even if they have a single remote. Pushes run directly, without a preliminary fetch or history comparison; Git handles up-to-date branches and rejects non-fast-forward updates. A missing destination branch can be created even without new changes. No rebase, merge, retry, or force-push.
|
|
153
153
|
|
|
154
154
|
Without a TTY, zapdev commits prepared repositories on nonprotected branches automatically; protected-branch commits are refused and changes remain staged. `--push` can publish without a TTY; without it, commits stay local. Repositories without new changes do not trigger an LLM call, branch prompt, or new commit.
|
|
155
155
|
|
package/dist/cli.js
CHANGED
|
@@ -830,6 +830,7 @@ function isUnicodeSupported() {
|
|
|
830
830
|
}
|
|
831
831
|
var unicode = isUnicodeSupported();
|
|
832
832
|
var isCI = () => process.env.CI === "true";
|
|
833
|
+
var isTTY = (o2) => o2.isTTY === true;
|
|
833
834
|
var unicodeOr = (o2, e2) => unicode ? o2 : e2;
|
|
834
835
|
var S_STEP_ACTIVE = unicodeOr("\u25C6", "*");
|
|
835
836
|
var S_STEP_CANCEL = unicodeOr("\u25A0", "x");
|
|
@@ -1075,7 +1076,7 @@ var spinner = ({
|
|
|
1075
1076
|
const x2 = getColumns(n4), k2 = I3?.styleFrame ?? W, g2 = (e2) => {
|
|
1076
1077
|
const r3 = e2 > 1 ? O2 ?? settings.messages.error : G ?? settings.messages.cancel;
|
|
1077
1078
|
S2 = e2 === 1, d2 && (a3(r3, e2), S2 && typeof h3 == "function" && h3());
|
|
1078
|
-
}, f2 = () => g2(2), i3 = () => g2(1),
|
|
1079
|
+
}, f2 = () => g2(2), i3 = () => g2(1), A2 = () => {
|
|
1079
1080
|
process.on("uncaughtExceptionMonitor", f2), process.on("unhandledRejection", f2), process.on("SIGINT", i3), process.on("SIGTERM", i3), process.on("exit", g2), m2 && m2.addEventListener("abort", i3);
|
|
1080
1081
|
}, H = () => {
|
|
1081
1082
|
process.removeListener("uncaughtExceptionMonitor", f2), process.removeListener("unhandledRejection", f2), process.removeListener("SIGINT", i3), process.removeListener("SIGTERM", i3), process.removeListener("exit", g2), m2 && m2.removeEventListener("abort", i3);
|
|
@@ -1096,7 +1097,7 @@ var spinner = ({
|
|
|
1096
1097
|
d2 = true, M = block({ output: n4 }), s2 = C2(e2), w2 = performance.now(), N2 && n4.write(`${styleText2("gray", S_BAR)}
|
|
1097
1098
|
`);
|
|
1098
1099
|
let r3 = 0, t3 = 0;
|
|
1099
|
-
|
|
1100
|
+
A2(), T2 = setInterval(() => {
|
|
1100
1101
|
if (u4 && s2 === p2)
|
|
1101
1102
|
return;
|
|
1102
1103
|
y2(), p2 = s2;
|
|
@@ -1224,6 +1225,123 @@ ${b2}
|
|
|
1224
1225
|
}).prompt();
|
|
1225
1226
|
};
|
|
1226
1227
|
var i = `${styleText2("gray", S_BAR)} `;
|
|
1228
|
+
var A = (l3) => l3.replace(/\x1b\[(?:\d+;)*\d*[ABCDEFGHfJKSTsu]|\x1b\[(s|u)/g, "");
|
|
1229
|
+
var taskLog = (l3) => {
|
|
1230
|
+
const r3 = l3.output ?? process.stdout, O2 = getColumns(r3), i3 = styleText2("gray", S_BAR), p2 = l3.spacing ?? 1, k2 = 3, m2 = l3.retainLog === true, d2 = !isCI() && isTTY(r3);
|
|
1231
|
+
r3.write(`${i3}
|
|
1232
|
+
`), r3.write(`${styleText2("green", S_STEP_SUBMIT)} ${l3.title}
|
|
1233
|
+
`);
|
|
1234
|
+
for (let e2 = 0; e2 < p2; e2++)
|
|
1235
|
+
r3.write(`${i3}
|
|
1236
|
+
`);
|
|
1237
|
+
const n4 = [
|
|
1238
|
+
{
|
|
1239
|
+
value: "",
|
|
1240
|
+
full: ""
|
|
1241
|
+
}
|
|
1242
|
+
];
|
|
1243
|
+
let v2 = false;
|
|
1244
|
+
const f2 = (e2) => {
|
|
1245
|
+
if (n4.length === 0)
|
|
1246
|
+
return;
|
|
1247
|
+
let s2 = 0;
|
|
1248
|
+
e2 && (s2 += p2 + 2);
|
|
1249
|
+
for (const t3 of n4) {
|
|
1250
|
+
const { value: o2, result: a3 } = t3;
|
|
1251
|
+
let g2 = a3?.message ?? o2;
|
|
1252
|
+
if (g2.length === 0)
|
|
1253
|
+
continue;
|
|
1254
|
+
a3 === void 0 && t3.header !== void 0 && t3.header !== "" && (g2 += `
|
|
1255
|
+
${t3.header}`);
|
|
1256
|
+
const E2 = g2.split(`
|
|
1257
|
+
`).reduce((b2, w2) => w2 === "" ? b2 + 1 : b2 + Math.ceil((w2.length + k2) / O2), 0);
|
|
1258
|
+
s2 += E2;
|
|
1259
|
+
}
|
|
1260
|
+
s2 > 0 && (s2 += 1, r3.write(import_sisteransi2.erase.lines(s2)));
|
|
1261
|
+
}, h3 = (e2, s2, t3) => {
|
|
1262
|
+
const o2 = t3 ? `${e2.full}
|
|
1263
|
+
${e2.value}` : e2.value;
|
|
1264
|
+
e2.header !== void 0 && e2.header !== "" && log.message(
|
|
1265
|
+
e2.header.split(`
|
|
1266
|
+
`).map((a3) => styleText2("bold", a3)),
|
|
1267
|
+
{
|
|
1268
|
+
output: r3,
|
|
1269
|
+
secondarySymbol: i3,
|
|
1270
|
+
symbol: i3,
|
|
1271
|
+
spacing: 0
|
|
1272
|
+
}
|
|
1273
|
+
), log.message(
|
|
1274
|
+
o2.split(`
|
|
1275
|
+
`).map((a3) => styleText2("dim", a3)),
|
|
1276
|
+
{
|
|
1277
|
+
output: r3,
|
|
1278
|
+
secondarySymbol: i3,
|
|
1279
|
+
symbol: i3,
|
|
1280
|
+
spacing: s2 ?? p2
|
|
1281
|
+
}
|
|
1282
|
+
);
|
|
1283
|
+
}, T2 = () => {
|
|
1284
|
+
for (const e2 of n4) {
|
|
1285
|
+
const { header: s2, value: t3, full: o2 } = e2;
|
|
1286
|
+
(s2 === void 0 || s2.length === 0) && t3.length === 0 || h3(e2, void 0, m2 === true && o2.length > 0);
|
|
1287
|
+
}
|
|
1288
|
+
}, L = (e2, s2, t3) => {
|
|
1289
|
+
if (f2(false), (t3?.raw !== true || !v2) && e2.value !== "" && (e2.value += `
|
|
1290
|
+
`), e2.value += A(s2), v2 = t3?.raw === true, l3.limit !== void 0) {
|
|
1291
|
+
const o2 = e2.value.split(`
|
|
1292
|
+
`), a3 = o2.length - l3.limit;
|
|
1293
|
+
if (a3 > 0) {
|
|
1294
|
+
const g2 = o2.splice(0, a3);
|
|
1295
|
+
m2 && (e2.full += (e2.full === "" ? "" : `
|
|
1296
|
+
`) + g2.join(`
|
|
1297
|
+
`));
|
|
1298
|
+
}
|
|
1299
|
+
e2.value = o2.join(`
|
|
1300
|
+
`);
|
|
1301
|
+
}
|
|
1302
|
+
d2 && y2();
|
|
1303
|
+
}, y2 = () => {
|
|
1304
|
+
for (const e2 of n4)
|
|
1305
|
+
e2.result ? e2.result.status === "error" ? log.error(e2.result.message, { output: r3, secondarySymbol: i3, spacing: 0 }) : log.success(e2.result.message, { output: r3, secondarySymbol: i3, spacing: 0 }) : e2.value !== "" && h3(e2, 0);
|
|
1306
|
+
}, B = (e2, s2) => {
|
|
1307
|
+
f2(false), e2.result = s2, d2 && y2();
|
|
1308
|
+
};
|
|
1309
|
+
return {
|
|
1310
|
+
message(e2, s2) {
|
|
1311
|
+
L(n4[0], e2, s2);
|
|
1312
|
+
},
|
|
1313
|
+
group(e2) {
|
|
1314
|
+
const s2 = {
|
|
1315
|
+
header: e2,
|
|
1316
|
+
value: "",
|
|
1317
|
+
full: ""
|
|
1318
|
+
};
|
|
1319
|
+
return n4.push(s2), {
|
|
1320
|
+
message(t3, o2) {
|
|
1321
|
+
L(s2, t3, o2);
|
|
1322
|
+
},
|
|
1323
|
+
error(t3) {
|
|
1324
|
+
B(s2, {
|
|
1325
|
+
status: "error",
|
|
1326
|
+
message: t3
|
|
1327
|
+
});
|
|
1328
|
+
},
|
|
1329
|
+
success(t3) {
|
|
1330
|
+
B(s2, {
|
|
1331
|
+
status: "success",
|
|
1332
|
+
message: t3
|
|
1333
|
+
});
|
|
1334
|
+
}
|
|
1335
|
+
};
|
|
1336
|
+
},
|
|
1337
|
+
error(e2, s2) {
|
|
1338
|
+
f2(true), log.error(e2, { output: r3, secondarySymbol: i3, spacing: 1 }), s2?.showLog !== false && T2(), n4.splice(1, n4.length - 1), n4[0].value = "", n4[0].full = "";
|
|
1339
|
+
},
|
|
1340
|
+
success(e2, s2) {
|
|
1341
|
+
f2(true), log.success(e2, { output: r3, secondarySymbol: i3, spacing: 1 }), s2?.showLog === true && T2(), n4.splice(1, n4.length - 1), n4[0].value = "", n4[0].full = "";
|
|
1342
|
+
}
|
|
1343
|
+
};
|
|
1344
|
+
};
|
|
1227
1345
|
var text = (e2) => new n2({
|
|
1228
1346
|
validate: e2.validate,
|
|
1229
1347
|
placeholder: e2.placeholder,
|
|
@@ -1736,7 +1854,7 @@ function _getBuiltinFlags(long, short, userNames, userAliases) {
|
|
|
1736
1854
|
// package.json
|
|
1737
1855
|
var package_default = {
|
|
1738
1856
|
name: "zapdev",
|
|
1739
|
-
version: "1.
|
|
1857
|
+
version: "1.14.0",
|
|
1740
1858
|
description: "Fast, performant and lightweight git chores and dev tools using your terminal and an OpenAI-compatible LLM endpoint.",
|
|
1741
1859
|
type: "module",
|
|
1742
1860
|
bin: {
|
|
@@ -1796,7 +1914,7 @@ var package_default = {
|
|
|
1796
1914
|
|
|
1797
1915
|
// src/commands/commit.ts
|
|
1798
1916
|
import { basename } from "node:path";
|
|
1799
|
-
import { styleText as
|
|
1917
|
+
import { styleText as styleText4 } from "node:util";
|
|
1800
1918
|
|
|
1801
1919
|
// src/lib/config.ts
|
|
1802
1920
|
function resolveHookTimeout(env = process.env) {
|
|
@@ -2208,18 +2326,23 @@ function createHookReporter(onHook, onTimeout, timeout) {
|
|
|
2208
2326
|
}
|
|
2209
2327
|
|
|
2210
2328
|
// src/lib/git.ts
|
|
2211
|
-
async function git(args, cwd, onHook) {
|
|
2329
|
+
async function git(args, cwd, onHook, onOutput) {
|
|
2212
2330
|
const timeout = resolveHookTimeout();
|
|
2213
|
-
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY && ["commit", "pull", "push"
|
|
2331
|
+
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY && ["commit", "pull", "push"].includes(args[0] ?? ""));
|
|
2214
2332
|
const terminalState = interactive && process.platform !== "win32" ? execFileSync("stty", ["-g"], { stdio: ["inherit", "pipe", "ignore"], encoding: "utf8" }).trim() : void 0;
|
|
2215
2333
|
const child = R2("git", args, {
|
|
2216
2334
|
nodeOptions: {
|
|
2217
2335
|
cwd,
|
|
2218
2336
|
detached: !interactive && process.platform !== "win32",
|
|
2219
2337
|
env: { ...process.env, GIT_TRACE2_EVENT: "3" },
|
|
2220
|
-
stdio: interactive ? ["inherit",
|
|
2338
|
+
stdio: interactive ? ["inherit", onOutput ? "pipe" : "inherit", onOutput ? "pipe" : "inherit", "pipe"] : ["pipe", "pipe", "pipe", "pipe"]
|
|
2221
2339
|
}
|
|
2222
2340
|
});
|
|
2341
|
+
if (onOutput) {
|
|
2342
|
+
for (const stream of ["stdout", "stderr"]) {
|
|
2343
|
+
child.process?.[stream]?.setEncoding("utf8").on("data", (chunk) => onOutput(chunk, stream));
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2223
2346
|
let timeoutError;
|
|
2224
2347
|
let hookFailureReported = false;
|
|
2225
2348
|
const { report, close } = createHookReporter((event) => {
|
|
@@ -2247,7 +2370,7 @@ async function git(args, cwd, onHook) {
|
|
|
2247
2370
|
if (timeoutError) throw timeoutError;
|
|
2248
2371
|
if (result.exitCode !== 0) {
|
|
2249
2372
|
const message = result.stderr.trim() || `git ${args.join(" ")} failed`;
|
|
2250
|
-
if (interactive) throw new GitOutputError(message, hookFailureReported);
|
|
2373
|
+
if (interactive || onOutput) throw new GitOutputError(message, hookFailureReported);
|
|
2251
2374
|
throw new Error(message);
|
|
2252
2375
|
}
|
|
2253
2376
|
return result.stdout;
|
|
@@ -2297,8 +2420,8 @@ async function getStagedDiffStats(repo) {
|
|
|
2297
2420
|
}
|
|
2298
2421
|
return stats;
|
|
2299
2422
|
}
|
|
2300
|
-
async function commit(repo, message, onHook) {
|
|
2301
|
-
await git(["commit", "--quiet", "-m", message], repo, onHook);
|
|
2423
|
+
async function commit(repo, message, onHook, onOutput) {
|
|
2424
|
+
await git(["commit", "--quiet", "-m", message], repo, onHook, onOutput);
|
|
2302
2425
|
}
|
|
2303
2426
|
async function currentBranch(repo) {
|
|
2304
2427
|
return (await git(["symbolic-ref", "--short", "HEAD"], repo)).trim();
|
|
@@ -2317,25 +2440,14 @@ async function getRepoStatus(repo) {
|
|
|
2317
2440
|
hasChanges: lines.some((line) => !line.startsWith("#"))
|
|
2318
2441
|
};
|
|
2319
2442
|
}
|
|
2320
|
-
async function
|
|
2443
|
+
async function getUpstreamRemote(repo, branch) {
|
|
2321
2444
|
const remotes = (await git(["remote"], repo)).trim().split("\n").filter(Boolean);
|
|
2322
2445
|
const upstream = (await git(["for-each-ref", "--format=%(upstream:remotename)", `refs/heads/${branch}`], repo)).trim();
|
|
2323
|
-
|
|
2324
|
-
return remotes.length === 1 ? remotes[0] : null;
|
|
2325
|
-
}
|
|
2326
|
-
async function push(repo, remote, branch, onHook) {
|
|
2327
|
-
await git(["check-ref-format", "--branch", branch], repo);
|
|
2328
|
-
await git(["push", "--", remote, `HEAD:refs/heads/${branch}`], repo, onHook);
|
|
2446
|
+
return remotes.includes(upstream) ? upstream : null;
|
|
2329
2447
|
}
|
|
2330
|
-
async function
|
|
2331
|
-
if (await tryGit(["rev-parse", "--verify", "HEAD"], repo) === null) return false;
|
|
2448
|
+
async function push(repo, remote, branch, onHook, onOutput) {
|
|
2332
2449
|
await git(["check-ref-format", "--branch", branch], repo);
|
|
2333
|
-
|
|
2334
|
-
const refs = new Set((await git(["ls-remote", "--", remote, ref, "HEAD"], repo, onHook)).trim().split("\n").map((line) => line.split(" ")[1]));
|
|
2335
|
-
const target = refs.has(ref) ? ref : refs.has("HEAD") ? "HEAD" : null;
|
|
2336
|
-
if (!target) return true;
|
|
2337
|
-
await git(["fetch", "--quiet", "--", remote, target], repo, onHook);
|
|
2338
|
-
return Boolean((await git(["rev-list", "--max-count=1", "FETCH_HEAD..HEAD"], repo)).trim());
|
|
2450
|
+
await git(["push", "--", remote, `HEAD:refs/heads/${branch}`], repo, onHook, onOutput);
|
|
2339
2451
|
}
|
|
2340
2452
|
async function findRepos(path) {
|
|
2341
2453
|
const root = await repoRoot(path);
|
|
@@ -2469,6 +2581,42 @@ var COMMIT_TYPES = [
|
|
|
2469
2581
|
"revert"
|
|
2470
2582
|
];
|
|
2471
2583
|
|
|
2584
|
+
// src/commands/git-task.ts
|
|
2585
|
+
import { styleText as styleText3 } from "node:util";
|
|
2586
|
+
async function runGitTask(label, operation, successMessage, run) {
|
|
2587
|
+
const title = `${label}: ${operation}`;
|
|
2588
|
+
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
2589
|
+
let task;
|
|
2590
|
+
const onOutput = (chunk, stream) => {
|
|
2591
|
+
if (!interactive) {
|
|
2592
|
+
process[stream].write(chunk);
|
|
2593
|
+
return;
|
|
2594
|
+
}
|
|
2595
|
+
task ??= taskLog({ title, limit: 10, retainLog: true });
|
|
2596
|
+
task.message(chunk, { raw: true });
|
|
2597
|
+
};
|
|
2598
|
+
const onHook = (event) => {
|
|
2599
|
+
const hook = `${label}: ${event.name}`;
|
|
2600
|
+
const message = event.phase === "start" ? hook : `${hook}: ${event.exitCode === 0 ? "completed" : `failed (exit ${event.exitCode})`}`;
|
|
2601
|
+
if (task) task.message(message);
|
|
2602
|
+
else if (event.phase === "start") log.step(styleText3("bold", message));
|
|
2603
|
+
else if (event.exitCode === 0) log.success(message);
|
|
2604
|
+
else log.error(message);
|
|
2605
|
+
};
|
|
2606
|
+
log.info(title);
|
|
2607
|
+
try {
|
|
2608
|
+
await run(onHook, onOutput);
|
|
2609
|
+
if (task) task.success(successMessage, { showLog: true });
|
|
2610
|
+
else log.success(successMessage);
|
|
2611
|
+
return true;
|
|
2612
|
+
} catch (error) {
|
|
2613
|
+
const message = `${label}: ${operation} failed${error instanceof GitOutputError ? "" : `: ${errorMessage(error)}`}`;
|
|
2614
|
+
if (task) task.error(message);
|
|
2615
|
+
else if (!(error instanceof GitOutputError && error.hookFailureReported)) log.error(message);
|
|
2616
|
+
return false;
|
|
2617
|
+
}
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2472
2620
|
// src/commands/commit.ts
|
|
2473
2621
|
var commitCommand = defineCommand({
|
|
2474
2622
|
meta: {
|
|
@@ -2541,13 +2689,20 @@ var commitCommand = defineCommand({
|
|
|
2541
2689
|
for (const repo of repos) {
|
|
2542
2690
|
const label = basename(repo);
|
|
2543
2691
|
try {
|
|
2544
|
-
|
|
2545
|
-
await
|
|
2692
|
+
const branch = await currentBranch(repo);
|
|
2693
|
+
if (!await getUpstreamRemote(repo, branch)) {
|
|
2694
|
+
log.warn(`${label}: no configured upstream remote. Skipping pull.`);
|
|
2695
|
+
continue;
|
|
2696
|
+
}
|
|
2546
2697
|
} catch (error) {
|
|
2547
2698
|
reportGitFailure(`${label}: pull failed`, error);
|
|
2548
2699
|
process.exitCode = 1;
|
|
2549
2700
|
return;
|
|
2550
2701
|
}
|
|
2702
|
+
if (!await runGitTask(label, "pull", `${label}: pulled`, (onHook, onOutput) => git(["pull", "--ff-only", "--no-rebase", "--no-autostash"], repo, onHook, onOutput))) {
|
|
2703
|
+
process.exitCode = 1;
|
|
2704
|
+
return;
|
|
2705
|
+
}
|
|
2551
2706
|
}
|
|
2552
2707
|
}
|
|
2553
2708
|
let scan;
|
|
@@ -2566,7 +2721,7 @@ var commitCommand = defineCommand({
|
|
|
2566
2721
|
const { repo } = repository;
|
|
2567
2722
|
const { branch, hasChanges } = await getRepoStatus(repo);
|
|
2568
2723
|
repository.label = `${basename(repo)} (${branch})`;
|
|
2569
|
-
repository.pendingLabel = hasChanges ?
|
|
2724
|
+
repository.pendingLabel = hasChanges ? styleText4("bold", repository.label) : repository.label;
|
|
2570
2725
|
if (!args.staged) await stageAll(repo);
|
|
2571
2726
|
const diff = await getStagedDiff(repo);
|
|
2572
2727
|
if (!diff.trim()) return null;
|
|
@@ -2615,13 +2770,14 @@ var commitCommand = defineCommand({
|
|
|
2615
2770
|
if (branch !== current) {
|
|
2616
2771
|
await switchBranch(draft.repo, branch);
|
|
2617
2772
|
draft.label = `${basename(draft.repo)} (${branch})`;
|
|
2618
|
-
draft.pendingLabel =
|
|
2773
|
+
draft.pendingLabel = styleText4("bold", draft.label);
|
|
2619
2774
|
}
|
|
2620
2775
|
}
|
|
2621
|
-
|
|
2622
|
-
|
|
2776
|
+
if (!await runGitTask(draft.label, "commit", `${draft.label}: committed ${draft.message}`, (onHook, onOutput) => commit(draft.repo, draft.message, onHook, onOutput))) {
|
|
2777
|
+
process.exitCode = 1;
|
|
2778
|
+
continue;
|
|
2779
|
+
}
|
|
2623
2780
|
toSend.push(draft);
|
|
2624
|
-
log.success(`${draft.label}: committed ${draft.message}`);
|
|
2625
2781
|
} catch (error) {
|
|
2626
2782
|
reportGitFailure(`${draft.pendingLabel}: commit failed`, error);
|
|
2627
2783
|
process.exitCode = 1;
|
|
@@ -2634,9 +2790,9 @@ var commitCommand = defineCommand({
|
|
|
2634
2790
|
for (const { repo, label } of toSend) {
|
|
2635
2791
|
try {
|
|
2636
2792
|
const branch = await currentBranch(repo);
|
|
2637
|
-
const remote = await
|
|
2793
|
+
const remote = await getUpstreamRemote(repo, branch);
|
|
2638
2794
|
if (remote) destinations.push({ repo, label, branch, remote });
|
|
2639
|
-
else log.warn(`${label}: no
|
|
2795
|
+
else log.warn(`${label}: no configured upstream remote. Skipping push; commit remains local.`);
|
|
2640
2796
|
} catch (error) {
|
|
2641
2797
|
reportGitFailure(`${label}: push failed`, error);
|
|
2642
2798
|
process.exitCode = 1;
|
|
@@ -2657,17 +2813,7 @@ var commitCommand = defineCommand({
|
|
|
2657
2813
|
}
|
|
2658
2814
|
if (shouldPush) {
|
|
2659
2815
|
for (const { repo, label, branch, remote } of destinations) {
|
|
2660
|
-
|
|
2661
|
-
log.info(`${label}: checking unpublished commits (${remote}/${branch}).`);
|
|
2662
|
-
if (!await hasUnpushedCommits(repo, remote, branch, reportHooks(label))) {
|
|
2663
|
-
log.info(`${label}: no unpushed commits. Skipping.`);
|
|
2664
|
-
continue;
|
|
2665
|
-
}
|
|
2666
|
-
log.info(`${label}: pushing ${remote}/${branch}`);
|
|
2667
|
-
await push(repo, remote, branch, reportHooks(label));
|
|
2668
|
-
log.success(`${label}: pushed to ${remote}/${branch}`);
|
|
2669
|
-
} catch (error) {
|
|
2670
|
-
reportGitFailure(`${label}: push failed`, error);
|
|
2816
|
+
if (!await runGitTask(label, "push", `${label}: pushed to ${remote}/${branch}`, (onHook, onOutput) => push(repo, remote, branch, onHook, onOutput))) {
|
|
2671
2817
|
process.exitCode = 1;
|
|
2672
2818
|
outro("Sending stopped after a Git failure.");
|
|
2673
2819
|
return;
|
|
@@ -2681,21 +2827,10 @@ function reportGitFailure(message, error) {
|
|
|
2681
2827
|
if (error instanceof GitOutputError && error.hookFailureReported) return;
|
|
2682
2828
|
log.error(error instanceof GitOutputError ? message : `${message}: ${errorMessage(error)}`);
|
|
2683
2829
|
}
|
|
2684
|
-
function reportHooks(label) {
|
|
2685
|
-
return (event) => {
|
|
2686
|
-
const hook = `${label}: ${event.name}`;
|
|
2687
|
-
if (event.phase === "start") {
|
|
2688
|
-
log.step(styleText3("bold", hook));
|
|
2689
|
-
} else {
|
|
2690
|
-
if (event.exitCode === 0) log.success(`${hook}: completed`);
|
|
2691
|
-
else log.error(`${hook}: failed (exit ${event.exitCode})`);
|
|
2692
|
-
}
|
|
2693
|
-
};
|
|
2694
|
-
}
|
|
2695
2830
|
async function reviewMessages(drafts, canPrompt) {
|
|
2696
2831
|
while (true) {
|
|
2697
2832
|
for (const { stats, pendingLabel, message } of drafts) {
|
|
2698
|
-
log.message(`(${
|
|
2833
|
+
log.message(`(${styleText4("green", `+${stats.additions}`)} ${styleText4("red", `-${stats.deletions}`)}) ${pendingLabel}: ${message}`);
|
|
2699
2834
|
}
|
|
2700
2835
|
if (!canPrompt) return drafts;
|
|
2701
2836
|
const action = await select({
|
|
@@ -2728,7 +2863,7 @@ async function reviewMessages(drafts, canPrompt) {
|
|
|
2728
2863
|
}
|
|
2729
2864
|
|
|
2730
2865
|
// src/cli.ts
|
|
2731
|
-
updateSettings({ withGuide:
|
|
2866
|
+
updateSettings({ withGuide: true });
|
|
2732
2867
|
var main = defineCommand({
|
|
2733
2868
|
meta: {
|
|
2734
2869
|
name: "zapdev",
|