zapdev 1.18.0 → 2.0.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.
Files changed (3) hide show
  1. package/README.md +2 -2
  2. package/dist/cli.js +30 -17
  3. 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
- Only repositories successfully committed during the current run are offered for push or pushed with `--push`. 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.
117
+ Only repositories successfully committed during the current run are offered for push or pushed with `--push`. 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 resolved push remote; it is omitted when none have one.
118
118
 
119
119
  ```bash
120
120
  zapdev commit
@@ -149,7 +149,7 @@ Each step uses one spinner: start, wait, stop. Normal Git and hook output is cap
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 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. No rebase, merge, retry, or force-push.
152
+ Pushes always target a remote branch with the current local branch's name, with no destination input. The remote is the branch's configured upstream remote, otherwise `origin`, otherwise the only configured remote. With no remote or multiple remotes and no `origin`, push is skipped with a warning. The first successful push sets the upstream when none exists; existing upstreams are preserved. Pushes run directly, without a preliminary fetch or history comparison; Git handles up-to-date branches and rejects non-fast-forward updates. 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
@@ -1736,7 +1736,7 @@ function _getBuiltinFlags(long, short, userNames, userAliases) {
1736
1736
  // package.json
1737
1737
  var package_default = {
1738
1738
  name: "zapdev",
1739
- version: "1.18.0",
1739
+ version: "2.0.0",
1740
1740
  description: "Fast, performant and lightweight git chores and dev tools using your terminal and an OpenAI-compatible LLM endpoint.",
1741
1741
  type: "module",
1742
1742
  bin: {
@@ -2323,13 +2323,24 @@ async function getRepoStatus(repo) {
2323
2323
  };
2324
2324
  }
2325
2325
  async function getUpstreamRemote(repo, branch) {
2326
- const remotes = (await git(["remote"], repo)).trim().split("\n").filter(Boolean);
2326
+ const remotes = await getRemotes(repo);
2327
2327
  const upstream = (await git(["for-each-ref", "--format=%(upstream:remotename)", `refs/heads/${branch}`], repo)).trim();
2328
2328
  return remotes.includes(upstream) ? upstream : null;
2329
2329
  }
2330
+ async function getPushRemote(repo, branch) {
2331
+ const upstream = await getUpstreamRemote(repo, branch);
2332
+ if (upstream) return upstream;
2333
+ const remotes = await getRemotes(repo);
2334
+ return remotes.includes("origin") ? "origin" : remotes.length === 1 ? remotes[0] : null;
2335
+ }
2336
+ async function getRemotes(repo) {
2337
+ return (await git(["remote"], repo)).trim().split("\n").filter(Boolean);
2338
+ }
2330
2339
  async function push(repo, remote, branch, onHook, onOutput) {
2331
2340
  await git(["check-ref-format", "--branch", branch], repo);
2332
- await git(["push", "--", remote, `HEAD:refs/heads/${branch}`], repo, onHook, onOutput);
2341
+ const current = await currentBranch(repo);
2342
+ const upstream = (await git(["for-each-ref", "--format=%(upstream)", `refs/heads/${current}`], repo)).trim();
2343
+ await git(["push", ...!upstream ? ["--set-upstream"] : [], "--", remote, `HEAD:refs/heads/${branch}`], repo, onHook, onOutput);
2333
2344
  }
2334
2345
  async function findRepos(path) {
2335
2346
  const root = await repoRoot(path);
@@ -2464,9 +2475,9 @@ var COMMIT_TYPES = [
2464
2475
  ];
2465
2476
 
2466
2477
  // src/commands/git-task.ts
2467
- async function runTask(title, successMessage, run) {
2478
+ async function runTask(title, successMessage, run, withGuide = true) {
2468
2479
  const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
2469
- const loader = interactive ? spinner({ withGuide: false }) : void 0;
2480
+ const loader = interactive ? spinner({ withGuide }) : void 0;
2470
2481
  const wasRaw = Boolean(process.stdin.isRaw);
2471
2482
  loader?.start(title);
2472
2483
  if (loader) {
@@ -2476,7 +2487,7 @@ async function runTask(title, successMessage, run) {
2476
2487
  try {
2477
2488
  const result = await run(() => void 0);
2478
2489
  if (loader) loader.stop(successMessage);
2479
- else log.success(successMessage);
2490
+ else log.success(successMessage, { spacing: 0 });
2480
2491
  return result;
2481
2492
  } catch (error) {
2482
2493
  loader?.clear();
@@ -2541,7 +2552,7 @@ var commitCommand = defineCommand({
2541
2552
  effort: args.effort
2542
2553
  });
2543
2554
  } catch (error) {
2544
- log.error(errorMessage(error));
2555
+ log.error(errorMessage(error), { spacing: 0 });
2545
2556
  process.exitCode = 1;
2546
2557
  return;
2547
2558
  }
@@ -2572,7 +2583,7 @@ var commitCommand = defineCommand({
2572
2583
  return results2;
2573
2584
  });
2574
2585
  for (const result of pulls) {
2575
- if (result.status === "fulfilled" && result.value) log.warn(result.value);
2586
+ if (result.status === "fulfilled" && result.value) log.warn(result.value, { spacing: 0 });
2576
2587
  }
2577
2588
  }
2578
2589
  const repositories = repos.map((repo) => ({ repo, label: basename(repo), pendingLabel: basename(repo) }));
@@ -2597,12 +2608,12 @@ var commitCommand = defineCommand({
2597
2608
  }
2598
2609
  return { results: results2, scan: scan2 };
2599
2610
  });
2600
- if (!scan) log.info("Gitleaks not found, skipping secret scan.");
2611
+ if (!scan) log.info("Gitleaks not found, skipping secret scan.", { spacing: 0 });
2601
2612
  const drafts = [];
2602
2613
  for (const [index, result] of results.entries()) {
2603
2614
  if (result.status === "fulfilled") {
2604
2615
  if (result.value) drafts.push(result.value);
2605
- else log.info(`${repositories[index].pendingLabel}: nothing to commit.`);
2616
+ else log.info(`${repositories[index].pendingLabel}: nothing to commit.`, { spacing: 0 });
2606
2617
  }
2607
2618
  }
2608
2619
  const selected = drafts.length ? await reviewMessages(drafts, interactive && !args.yes) : [];
@@ -2613,6 +2624,7 @@ var commitCommand = defineCommand({
2613
2624
  const toSend = [];
2614
2625
  let previousBranchInput = "";
2615
2626
  for (const draft of selected) {
2627
+ let withGuide = true;
2616
2628
  const current = await currentBranch(draft.repo);
2617
2629
  if (/^(main|master|principal|dev|development)$/.test(current)) {
2618
2630
  if (!interactive) throw new Error("Committing on a protected branch requires a terminal to choose a branch. Changes remain staged.");
@@ -2625,6 +2637,7 @@ var commitCommand = defineCommand({
2625
2637
  return;
2626
2638
  }
2627
2639
  previousBranchInput = answer.trim();
2640
+ withGuide = Boolean(previousBranchInput);
2628
2641
  const branch = previousBranchInput || current;
2629
2642
  if (branch !== current) {
2630
2643
  await switchBranch(draft.repo, branch);
@@ -2632,7 +2645,7 @@ var commitCommand = defineCommand({
2632
2645
  draft.pendingLabel = styleText3("bold", draft.label);
2633
2646
  }
2634
2647
  }
2635
- await runTask(`${draft.label}: commit`, `${draft.label}: committed ${draft.message}`, (onOutput) => commit(draft.repo, draft.message, void 0, onOutput));
2648
+ await runTask(`${draft.label}: commit`, `${draft.label}: committed ${draft.message}`, (onOutput) => commit(draft.repo, draft.message, void 0, onOutput), withGuide);
2636
2649
  toSend.push(draft);
2637
2650
  }
2638
2651
  if (toSend.length === 0) return;
@@ -2640,9 +2653,9 @@ var commitCommand = defineCommand({
2640
2653
  const destinations = [];
2641
2654
  for (const { repo, label } of toSend) {
2642
2655
  const branch = await currentBranch(repo);
2643
- const remote = await getUpstreamRemote(repo, branch);
2656
+ const remote = await getPushRemote(repo, branch);
2644
2657
  if (remote) destinations.push({ repo, label, branch, remote });
2645
- else log.warn(`${label}: no configured upstream remote. Skipping push; commit remains local.`);
2658
+ else log.warn(`${label}: no unambiguous push remote. Skipping push; commit remains local.`, { spacing: 0 });
2646
2659
  }
2647
2660
  let shouldPush = Boolean(args.push);
2648
2661
  if (!shouldPush && interactive && destinations.length) {
@@ -2651,7 +2664,7 @@ var commitCommand = defineCommand({
2651
2664
  initialValue: false
2652
2665
  });
2653
2666
  if (isCancel(answer)) {
2654
- outro("Committed. Not pushed.");
2667
+ cancel("Committed. Not pushed.");
2655
2668
  return;
2656
2669
  }
2657
2670
  shouldPush = answer;
@@ -2663,15 +2676,15 @@ var commitCommand = defineCommand({
2663
2676
  }
2664
2677
  if (interactive) outro("Done.");
2665
2678
  } catch (error) {
2666
- log.error(errorMessage(error));
2679
+ log.error(errorMessage(error), { spacing: 0 });
2667
2680
  process.exitCode = 1;
2668
2681
  }
2669
2682
  }
2670
2683
  });
2671
2684
  async function reviewMessages(drafts, canPrompt) {
2672
2685
  while (true) {
2673
- for (const { stats, pendingLabel, message } of drafts) {
2674
- log.message(`(${styleText3("green", `+${stats.additions}`)} ${styleText3("red", `-${stats.deletions}`)}) ${pendingLabel}: ${message}`);
2686
+ for (const [index, { stats, pendingLabel, message }] of drafts.entries()) {
2687
+ log.message(`(${styleText3("green", `+${stats.additions}`)} ${styleText3("red", `-${stats.deletions}`)}) ${pendingLabel}: ${message}`, { spacing: index === 0 ? 1 : 0 });
2675
2688
  }
2676
2689
  if (!canPrompt) return drafts;
2677
2690
  const action = await select({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zapdev",
3
- "version": "1.18.0",
3
+ "version": "2.0.0",
4
4
  "description": "Fast, performant and lightweight git chores and dev tools using your terminal and an OpenAI-compatible LLM endpoint.",
5
5
  "type": "module",
6
6
  "bin": {