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.
- package/README.md +55 -2
- package/dist/index.js +37 -11
- 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`) —
|
|
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
|
|
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
|
-
|
|
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 {
|
|
989
|
+
const { aFile, bFile, start } = positions[i];
|
|
988
990
|
const end = i + 1 < positions.length ? positions[i + 1].start : rawDiff.length;
|
|
989
|
-
|
|
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
|
|
1021
|
-
|
|
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
|
-
|
|
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 >=
|
|
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 >=
|
|
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.
|
|
2144
|
-
description: "
|
|
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.
|
|
4
|
-
"description": "
|
|
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",
|