contribute-now 0.4.0-dev.d48d9e6 → 0.4.0-dev.ff615ec

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 +55 -2
  2. package/dist/index.js +37 -11
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -64,7 +64,7 @@ bun install -g contribute-now
64
64
  ## Prerequisites
65
65
 
66
66
  - **[Git](https://git-scm.com/)** — required
67
- - **[GitHub CLI](https://cli.github.com)** (`gh`) — optional; enables role auto-detection and PR creation
67
+ - **[GitHub CLI](https://cli.github.com)** (`gh`) — recommended; required for PR creation, role detection, and merge status checks
68
68
  - **[GitHub Copilot](https://github.com/features/copilot)** — optional; enables AI features
69
69
 
70
70
  ---
@@ -129,10 +129,13 @@ Stage your changes and create a validated, AI-generated commit message matching
129
129
  contrib commit # AI-generated message
130
130
  contrib commit --no-ai # manual entry, still validated
131
131
  contrib commit --model gpt-4.1 # specific AI model
132
+ contrib commit --group # AI groups changes into atomic commits
132
133
  ```
133
134
 
134
135
  After the AI generates a message, you can **accept**, **edit**, **regenerate**, or **write manually**. Messages are always validated against your convention — with a soft warning if they don't match (you can still commit).
135
136
 
137
+ **Group commit mode** (`--group`): AI analyzes all staged and unstaged changes, groups related files into logical atomic commits, and generates a commit message for each group. Great for splitting a large set of changes into clean, reviewable commits.
138
+
136
139
  ---
137
140
 
138
141
  ### `contrib update`
@@ -180,6 +183,55 @@ contrib status
180
183
 
181
184
  ---
182
185
 
186
+ ### `contrib doctor`
187
+
188
+ Diagnose the contribute-now CLI environment and configuration. Checks tools, dependencies, config, git state, fork setup, workflow, and environment.
189
+
190
+ ```bash
191
+ contrib doctor # pretty-printed report
192
+ contrib doctor --json # machine-readable JSON output
193
+ ```
194
+
195
+ Checks include:
196
+ - CLI version and runtime (Bun/Node)
197
+ - git and GitHub CLI availability and authentication
198
+ - `.contributerc.json` validity and `.gitignore` status
199
+ - Git repo state (uncommitted changes, lock files, shallow clone)
200
+ - Fork and remote configuration
201
+ - Workflow and branch setup
202
+
203
+ ---
204
+
205
+ ### `contrib log`
206
+
207
+ Show a colorized, workflow-aware commit log with graph visualization.
208
+
209
+ ```bash
210
+ contrib log # last 20 commits with graph
211
+ contrib log -n 50 # last 50 commits
212
+ contrib log --all # all branches
213
+ contrib log --no-graph # flat view without graph lines
214
+ contrib log -b feature/x # log for a specific branch
215
+ ```
216
+
217
+ Protected branches (main, dev) are highlighted, and the current branch is color-coded for quick orientation.
218
+
219
+ ---
220
+
221
+ ### `contrib branch`
222
+
223
+ List branches with workflow-aware labels and tracking status.
224
+
225
+ ```bash
226
+ contrib branch # local branches
227
+ contrib branch --all # local + remote branches
228
+ contrib branch --remote # remote branches only
229
+ ```
230
+
231
+ Branches are annotated with workflow labels (e.g., base, dev, feature) and tracking info (upstream, gone, no remote).
232
+
233
+ ---
234
+
183
235
  ### `contrib hook`
184
236
 
185
237
  Install or uninstall a `commit-msg` git hook that validates every commit against your configured convention — no Husky or lint-staged needed.
@@ -210,8 +262,9 @@ contrib validate "added stuff" # exit 1
210
262
  All AI features are powered by **GitHub Copilot** via `@github/copilot-sdk` and are entirely **optional** — every command has a manual fallback.
211
263
 
212
264
  | Command | AI Feature | Fallback |
213
- |---------|------------|---------|
265
+ |---------|------------|----------|
214
266
  | `commit` | Generate commit message from staged diff | Type manually |
267
+ | `commit --group` | Group related changes into atomic commits | Manual staging + commit |
215
268
  | `start` | Suggest branch name from natural language | Prefix picker + manual |
216
269
  | `update` | Conflict resolution guidance | Standard git instructions |
217
270
  | `submit` | Generate PR title and body | `gh pr create --fill` or manual |
package/dist/index.js CHANGED
@@ -978,15 +978,21 @@ var BATCH_CONFIG = {
978
978
  };
979
979
  function parseDiffByFile(rawDiff) {
980
980
  const sections = new Map;
981
- const headerPattern = /^diff --git a\/(.+?) b\//gm;
981
+ const headerPattern = /^diff --git a\/(.+?) b\/(.+?)$/gm;
982
982
  const positions = [];
983
983
  for (let match = headerPattern.exec(rawDiff);match !== null; match = headerPattern.exec(rawDiff)) {
984
- positions.push({ file: match[1], start: match.index });
984
+ const aFile = match[1];
985
+ const bFile = match[2] ?? aFile;
986
+ positions.push({ aFile, bFile, start: match.index });
985
987
  }
986
988
  for (let i = 0;i < positions.length; i++) {
987
- const { file, start } = positions[i];
989
+ const { aFile, bFile, start } = positions[i];
988
990
  const end = i + 1 < positions.length ? positions[i + 1].start : rawDiff.length;
989
- sections.set(file, rawDiff.slice(start, end));
991
+ const section = rawDiff.slice(start, end);
992
+ sections.set(aFile, section);
993
+ if (bFile && bFile !== aFile) {
994
+ sections.set(bFile, section);
995
+ }
990
996
  }
991
997
  return sections;
992
998
  }
@@ -1017,10 +1023,15 @@ function createCompactDiff(files, rawDiff, maxTotalChars = BATCH_CONFIG.MAX_COMP
1017
1023
  parts.push(`${header}
1018
1024
  ${section}`);
1019
1025
  } else {
1020
- const truncated = section.slice(0, perFileBudget - header.length - 20);
1021
- parts.push(`${header}
1026
+ const availableForBody = perFileBudget - header.length - 20;
1027
+ if (availableForBody <= 0) {
1028
+ parts.push(header);
1029
+ } else {
1030
+ const truncated = section.slice(0, availableForBody);
1031
+ parts.push(`${header}
1022
1032
  ${truncated}
1023
1033
  ...(truncated)`);
1034
+ }
1024
1035
  }
1025
1036
  } else {
1026
1037
  parts.push(`[${file}] (new/binary file — no diff available)`);
@@ -1263,12 +1274,24 @@ NOTE: Processing batch ${batchNum}/${totalBatches} of a large changeset. Group o
1263
1274
  if (Array.isArray(parsed)) {
1264
1275
  for (const group of parsed) {
1265
1276
  if (Array.isArray(group.files) && typeof group.message === "string") {
1266
- allGroups.push(group);
1277
+ const batchFileSet = new Set(batchFiles);
1278
+ const filteredFiles = group.files.filter((f) => batchFileSet.has(f));
1279
+ if (filteredFiles.length > 0) {
1280
+ allGroups.push({ ...group, files: filteredFiles });
1281
+ }
1267
1282
  }
1268
1283
  }
1269
1284
  }
1270
1285
  } catch {}
1271
1286
  }
1287
+ const groupedFiles = new Set(allGroups.flatMap((g) => g.files));
1288
+ const ungrouped = files.filter((f) => !groupedFiles.has(f));
1289
+ if (ungrouped.length > 0) {
1290
+ allGroups.push({
1291
+ files: ungrouped,
1292
+ message: `chore: update ${ungrouped.length} remaining file${ungrouped.length !== 1 ? "s" : ""}`
1293
+ });
1294
+ }
1272
1295
  if (allGroups.length === 0) {
1273
1296
  throw new Error("AI could not group any files even with batch processing");
1274
1297
  }
@@ -1870,7 +1893,7 @@ ${pc6.bold("Changed files:")}`);
1870
1893
  warn(`AI unavailable: ${copilotError}`);
1871
1894
  warn("Falling back to manual commit message entry.");
1872
1895
  } else {
1873
- const spinnerMsg = stagedFiles.length >= 15 ? `Generating commit message with AI (${stagedFiles.length} files — using optimized batching)...` : "Generating commit message with AI...";
1896
+ const spinnerMsg = stagedFiles.length >= BATCH_CONFIG.LARGE_CHANGESET_THRESHOLD ? `Generating commit message with AI (${stagedFiles.length} files — using optimized batching)...` : "Generating commit message with AI...";
1874
1897
  const spinner = createSpinner(spinnerMsg);
1875
1898
  commitMessage = await generateCommitMessage(diff, stagedFiles, args.model, config.commitConvention);
1876
1899
  if (commitMessage) {
@@ -1962,7 +1985,7 @@ ${pc6.bold("Changed files:")}`);
1962
1985
  for (const f of changedFiles) {
1963
1986
  console.log(` ${pc6.dim("•")} ${f}`);
1964
1987
  }
1965
- const spinner = createSpinner(changedFiles.length >= 15 ? `Asking AI to group ${changedFiles.length} file(s) into logical commits (using optimized batching)...` : `Asking AI to group ${changedFiles.length} file(s) into logical commits...`);
1988
+ const spinner = createSpinner(changedFiles.length >= BATCH_CONFIG.LARGE_CHANGESET_THRESHOLD ? `Asking AI to group ${changedFiles.length} file(s) into logical commits (using optimized batching)...` : `Asking AI to group ${changedFiles.length} file(s) into logical commits...`);
1966
1989
  const diffs = await getFullDiffForFiles(changedFiles);
1967
1990
  if (!diffs.trim()) {
1968
1991
  spinner.stop();
@@ -2140,8 +2163,8 @@ import pc7 from "picocolors";
2140
2163
  // package.json
2141
2164
  var package_default = {
2142
2165
  name: "contribute-now",
2143
- version: "0.4.0-dev.d48d9e6",
2144
- description: "Git workflow CLI for squash-merge two-branch models. Keeps dev in sync with main after squash merges.",
2166
+ version: "0.4.0-dev.ff615ec",
2167
+ description: "Developer CLI that automates git workflows branching, syncing, committing, and PRs — with multi-workflow and commit convention support.",
2145
2168
  type: "module",
2146
2169
  bin: {
2147
2170
  contrib: "dist/index.js",
@@ -2827,6 +2850,9 @@ async function shouldContinueSetupWithExistingConfig(options) {
2827
2850
  onWarn("Found .contributerc.json but it appears invalid.");
2828
2851
  const shouldContinue = await confirm2("Continue setup and overwrite invalid config?");
2829
2852
  if (!shouldContinue) {
2853
+ if (ensureIgnored()) {
2854
+ onInfo("Added .contributerc.json to .gitignore to avoid committing personal config.");
2855
+ }
2830
2856
  onInfo("Keeping existing file. Run setup again when ready to repair it.");
2831
2857
  return false;
2832
2858
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "contribute-now",
3
- "version": "0.4.0-dev.d48d9e6",
4
- "description": "Git workflow CLI for squash-merge two-branch models. Keeps dev in sync with main after squash merges.",
3
+ "version": "0.4.0-dev.ff615ec",
4
+ "description": "Developer CLI that automates git workflows branching, syncing, committing, and PRs — with multi-workflow and commit convention support.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "contrib": "dist/index.js",