zapdev 1.10.0 → 1.11.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 +11 -11
- package/dist/cli.js +34 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -113,7 +113,7 @@ Stages all changes, scans them with Gitleaks when installed, generates Conventio
|
|
|
113
113
|
- **Unified review:** generates messages in parallel, then presents every message with its repository name and branch. Repositories with pending changes are shown in bold and underlined when terminal styling is enabled.
|
|
114
114
|
- **Actions:** commit all, commit only a named repository, edit a named repository's message, or cancel. Editing returns to the review menu.
|
|
115
115
|
|
|
116
|
-
Repositories with no new changes can still send existing commits. Unselected or cancelled changes remain staged; failed or unselected commit drafts are not pushed.
|
|
116
|
+
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.
|
|
117
117
|
|
|
118
118
|
```bash
|
|
119
119
|
zapdev commit
|
|
@@ -125,7 +125,7 @@ zapdev commit
|
|
|
125
125
|
| `--model <model>` | Override the model |
|
|
126
126
|
| `--effort <effort>` | Override the reasoning effort |
|
|
127
127
|
| `-t, --type <type>` | Force an exact lowercase Conventional Commit type (`feat`, `fix`, `chore`, etc.) |
|
|
128
|
-
| `-p, --push` | Skip push confirmation
|
|
128
|
+
| `-p, --push` | Skip push confirmation and push the current branch |
|
|
129
129
|
| `-s, --staged` | Commit only changes that are already staged |
|
|
130
130
|
| `-y, --yes` | Skip commit review; still confirm push unless `--push` is set |
|
|
131
131
|
|
|
@@ -133,7 +133,7 @@ zapdev commit
|
|
|
133
133
|
zapdev commit -t feat # force the type
|
|
134
134
|
zapdev commit --staged # leave unstaged changes untouched
|
|
135
135
|
zapdev commit -y # commit automatically, then ask before pushing
|
|
136
|
-
zapdev commit -yp #
|
|
136
|
+
zapdev commit -yp # skip review and push confirmation; choose a branch if protected
|
|
137
137
|
```
|
|
138
138
|
|
|
139
139
|
Before contacting the LLM endpoint, zapdev runs `gitleaks git --staged --verbose` in each changed repository when Gitleaks is installed. A failed scan skips that repository without sending its diff; when Gitleaks is absent, the scan is skipped. The staged diff is sent to the configured endpoint, which may be remote.
|
|
@@ -142,11 +142,11 @@ Preparation and commit failures are reported per repository while the others con
|
|
|
142
142
|
|
|
143
143
|
In a terminal, Git and its hooks display live logs and support native prompts, even with `--yes` (which skips commit review, not native Git prompts). Git controls hook stdin; interactive hooks should read from the terminal, for example `/dev/tty`. Each hook has a 60-second deadline, including time spent answering prompts; override it with `ZD_HOOK_TIMEOUT=120 zapdev commit`.
|
|
144
144
|
|
|
145
|
-
|
|
145
|
+
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.
|
|
146
146
|
|
|
147
|
-
|
|
147
|
+
Pushes always target a remote branch with the current local branch's name, with no destination input. Classic pushes use the current branch's upstream remote, or the only configured remote if there is no upstream, and run `git push <remote> HEAD:refs/heads/<current-branch>`. Ambiguous remotes are skipped with a warning. Both modes check for unpublished commits against that remote branch, falling back to the remote's default branch (`HEAD`) if it is missing. If neither reference exists, publication is attempted. Classic mode compares HEAD; subtree mode compares its extracted history. Already published history is skipped. No rebase, merge, retry, or force-push.
|
|
148
148
|
|
|
149
|
-
Without a TTY, zapdev commits
|
|
149
|
+
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.
|
|
150
150
|
|
|
151
151
|
#### Subtree publication
|
|
152
152
|
|
|
@@ -167,15 +167,15 @@ Place an optional `zapdev.json` in the directory where you launch zapdev:
|
|
|
167
167
|
|
|
168
168
|
- **Scope:** only the launch directory's file is read, not parents or child repositories. Its mapping applies to newly committed repositories and repositories without new changes.
|
|
169
169
|
- **Configuration:** only `zapdev.json` selects subtree mode; no subtree CLI flag. Missing or empty `subtrees` uses classic push. Invalid configuration stops the command before staging. Replace the removed `isSubtree` option with an explicit mapping.
|
|
170
|
-
- **Destinations:** only mapped folders are published, using their configured remotes; folder and remote names need not match.
|
|
171
|
-
- **Publication:** check
|
|
170
|
+
- **Destinations:** only mapped folders are published, using their configured remotes; folder and remote names need not match. All subtrees use the parent repository's current branch name. The parent itself is not pushed. Detached HEAD is refused.
|
|
171
|
+
- **Publication:** check each subtree using the shared unpublished-commit rule above, then run `git subtree push --prefix=<folder> <remote> <current-branch>`. Subtrees without unpublished commits are skipped without creating remote branches; uncommitted changes are excluded.
|
|
172
172
|
- **Failures:** stop all remaining publications, leaving earlier publications intact. No automatic recovery.
|
|
173
173
|
|
|
174
|
-
Interactive runs still ask for confirmation, including with `--yes`; `--push` skips
|
|
174
|
+
Interactive runs still ask for publication confirmation, including with `--yes`; `--push` skips that confirmation. The commit and branch-selection flow is identical to classic mode.
|
|
175
175
|
|
|
176
176
|
```bash
|
|
177
177
|
zapdev commit # with subtrees configured, ask to publish after committing
|
|
178
|
-
zapdev commit -yp #
|
|
178
|
+
zapdev commit -yp # publish changed subtrees on the parent's current branch
|
|
179
179
|
```
|
|
180
180
|
|
|
181
181
|
### `zapdev subtree-init`
|
|
@@ -202,7 +202,7 @@ zapdev subtree-init my-project \
|
|
|
202
202
|
|
|
203
203
|
### Zed IDE
|
|
204
204
|
|
|
205
|
-
For a faster review and commit workflow, review and stage changes from Zed's Git panel, then run `zapdev commit -syp` from a task. The command commits only staged changes, skips commit review and push confirmation,
|
|
205
|
+
For a faster review and commit workflow, review and stage changes from Zed's Git panel, then run `zapdev commit -syp` from a task. The command commits only staged changes, skips commit review and push confirmation, and asks for a branch before committing only if the current branch is protected.
|
|
206
206
|
|
|
207
207
|
Add the following tasks to `.zed/tasks.json`:
|
|
208
208
|
|
package/dist/cli.js
CHANGED
|
@@ -554,7 +554,7 @@ function _getBuiltinFlags(long, short, userNames, userAliases) {
|
|
|
554
554
|
// package.json
|
|
555
555
|
var package_default = {
|
|
556
556
|
name: "zapdev",
|
|
557
|
-
version: "1.
|
|
557
|
+
version: "1.11.0",
|
|
558
558
|
description: "Fast, performant and lightweight git chores and dev tools using your terminal and an OpenAI-compatible LLM endpoint.",
|
|
559
559
|
type: "module",
|
|
560
560
|
bin: {
|
|
@@ -2323,6 +2323,12 @@ async function commit(repo, message, onHook, onNativeOutput) {
|
|
|
2323
2323
|
async function currentBranch(repo) {
|
|
2324
2324
|
return (await git(["symbolic-ref", "--short", "HEAD"], repo)).trim();
|
|
2325
2325
|
}
|
|
2326
|
+
async function switchBranch(repo, branch) {
|
|
2327
|
+
await git(["check-ref-format", "--branch", branch], repo);
|
|
2328
|
+
await git(["check-ref-format", `refs/heads/${branch}`], repo);
|
|
2329
|
+
const exists2 = await tryGit(["show-ref", "--verify", "--quiet", `refs/heads/${branch}`], repo) !== null;
|
|
2330
|
+
await git(exists2 ? ["switch", "--no-guess", "--", branch] : ["switch", "--no-track", "-c", branch], repo);
|
|
2331
|
+
}
|
|
2326
2332
|
async function getRepoStatus(repo) {
|
|
2327
2333
|
const lines = (await git(["status", "--porcelain=v2", "--branch", "--untracked-files=normal"], repo)).trim().split("\n");
|
|
2328
2334
|
const branch = lines.find((line) => line.startsWith("# branch.head "))?.slice(14) ?? "HEAD";
|
|
@@ -2520,7 +2526,7 @@ var commitCommand = defineCommand({
|
|
|
2520
2526
|
push: {
|
|
2521
2527
|
type: "boolean",
|
|
2522
2528
|
alias: "p",
|
|
2523
|
-
description: "Skip push confirmation
|
|
2529
|
+
description: "Skip push confirmation and push the current branch."
|
|
2524
2530
|
},
|
|
2525
2531
|
staged: {
|
|
2526
2532
|
type: "boolean",
|
|
@@ -2603,9 +2609,29 @@ var commitCommand = defineCommand({
|
|
|
2603
2609
|
return;
|
|
2604
2610
|
}
|
|
2605
2611
|
const toSend = [];
|
|
2612
|
+
let previousBranchInput = "";
|
|
2606
2613
|
for (const draft of selected) {
|
|
2607
|
-
log.info(`${draft.label}: committing`);
|
|
2608
2614
|
try {
|
|
2615
|
+
const current = await currentBranch(draft.repo);
|
|
2616
|
+
if (/^(main|master|principal|dev|development)$/.test(current)) {
|
|
2617
|
+
if (!interactive) throw new Error("Committing on a protected branch requires a terminal to choose a branch. Changes remain staged.");
|
|
2618
|
+
const answer = await text({
|
|
2619
|
+
message: `${basename(draft.repo)} (${current}): branch to commit to`,
|
|
2620
|
+
initialValue: previousBranchInput
|
|
2621
|
+
});
|
|
2622
|
+
if (isCancel(answer)) {
|
|
2623
|
+
cancel("Committing cancelled. Changes remain staged; earlier commits stay local.");
|
|
2624
|
+
return;
|
|
2625
|
+
}
|
|
2626
|
+
previousBranchInput = answer.trim();
|
|
2627
|
+
const branch = previousBranchInput || current;
|
|
2628
|
+
if (branch !== current) {
|
|
2629
|
+
await switchBranch(draft.repo, branch);
|
|
2630
|
+
draft.label = `${basename(draft.repo)} (${branch})`;
|
|
2631
|
+
draft.pendingLabel = styleText4(["bold", "underline"], draft.label);
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
log.info(`${draft.label}: committing`);
|
|
2609
2635
|
await commit(draft.repo, draft.message, reportHooks(draft.label), reportNativeOutput);
|
|
2610
2636
|
toSend.push(draft);
|
|
2611
2637
|
log.success(`${draft.label}: committed ${draft.message}`);
|
|
@@ -2629,12 +2655,6 @@ var commitCommand = defineCommand({
|
|
|
2629
2655
|
shouldPush = answer;
|
|
2630
2656
|
}
|
|
2631
2657
|
if (shouldPush) {
|
|
2632
|
-
if (!interactive) {
|
|
2633
|
-
log.error("Pushing requires a terminal to choose each destination branch. Commits remain local.");
|
|
2634
|
-
process.exitCode = 1;
|
|
2635
|
-
return;
|
|
2636
|
-
}
|
|
2637
|
-
let previousBranchInput = "";
|
|
2638
2658
|
for (const { repo, label } of toSend) {
|
|
2639
2659
|
try {
|
|
2640
2660
|
const current = await currentBranch(repo);
|
|
@@ -2644,24 +2664,14 @@ var commitCommand = defineCommand({
|
|
|
2644
2664
|
log.warn(`${label}: no remote or ambiguous remote choice. Skipping push; commit remains local.`);
|
|
2645
2665
|
continue;
|
|
2646
2666
|
}
|
|
2647
|
-
log.info(`${label}: checking unpublished commits${prefix ? ` in ${prefix}` : ""} (${remote}/${
|
|
2648
|
-
if (!await hasUnpushedCommits(repo, remote,
|
|
2667
|
+
log.info(`${label}: checking unpublished commits${prefix ? ` in ${prefix}` : ""} (${remote}/${current}).`);
|
|
2668
|
+
if (!await hasUnpushedCommits(repo, remote, current, reportHooks(label), prefix || void 0, reportNativeOutput)) {
|
|
2649
2669
|
log.info(`${label}: no unpushed commits${prefix ? ` in ${prefix}` : ""}. Skipping.`);
|
|
2650
2670
|
continue;
|
|
2651
2671
|
}
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
});
|
|
2656
|
-
if (isCancel(answer)) {
|
|
2657
|
-
outro("Sending cancelled. Remaining commits stay local.");
|
|
2658
|
-
return;
|
|
2659
|
-
}
|
|
2660
|
-
previousBranchInput = answer.trim();
|
|
2661
|
-
const branch = previousBranchInput || current;
|
|
2662
|
-
log.info(`${label}: pushing ${publishSubtreeMode ? `${prefix} \u2192 ` : ""}${remote}/${branch}`);
|
|
2663
|
-
await push(repo, remote, branch, reportHooks(label), prefix || void 0, reportNativeOutput);
|
|
2664
|
-
log.success(`${label}: pushed to ${remote}/${branch}`);
|
|
2672
|
+
log.info(`${label}: pushing ${publishSubtreeMode ? `${prefix} \u2192 ` : ""}${remote}/${current}`);
|
|
2673
|
+
await push(repo, remote, current, reportHooks(label), prefix || void 0, reportNativeOutput);
|
|
2674
|
+
log.success(`${label}: pushed to ${remote}/${current}`);
|
|
2665
2675
|
}
|
|
2666
2676
|
} catch (error) {
|
|
2667
2677
|
reportGitFailure(`${label}: ${publishSubtreeMode ? "publication" : "push"} failed`, error);
|