gent-cli 24.0.0 → 26.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.
- package/QUICKSTART.md +6 -5
- package/README.md +7 -6
- package/package.json +3 -4
- package/src/commands/ai.js +15 -30
- package/src/commands/ask.js +4 -1
- package/src/commands/canonical.js +73 -14
- package/src/commands/changelog.js +2 -0
- package/src/commands/chat.js +80 -0
- package/src/commands/commit.js +1 -0
- package/src/commands/config.js +1 -16
- package/src/commands/docs.js +2 -0
- package/src/commands/doctor.js +12 -12
- package/src/commands/explain.js +3 -3
- package/src/commands/merge.js +77 -6
- package/src/commands/resolve.js +30 -6
- package/src/commands/review.js +4 -3
- package/src/commands/setup.js +2 -52
- package/src/commands/summary.js +5 -1
- package/src/index.js +20 -11
- package/src/utils/ai-service.js +86 -163
- package/src/utils/auth-storage.js +42 -7
- package/src/utils/user-config.js +3 -33
package/src/commands/merge.js
CHANGED
|
@@ -15,6 +15,8 @@ const { findMergeBase, mergeTreeEntries, autoMerge } = require('../utils/merge-e
|
|
|
15
15
|
const { storeTree, readBlob, hashBlob } = require('../utils/hash-engine');
|
|
16
16
|
const pet = require('./pet');
|
|
17
17
|
const journal = require('../utils/journal');
|
|
18
|
+
const ai = require('../utils/ai-service');
|
|
19
|
+
const reviewCommand = require('./review');
|
|
18
20
|
|
|
19
21
|
/**
|
|
20
22
|
* Merge a branch into the current branch
|
|
@@ -22,7 +24,8 @@ const journal = require('../utils/journal');
|
|
|
22
24
|
* @param {Object} options - Command options
|
|
23
25
|
*/
|
|
24
26
|
async function merge(sourceBranch, options) {
|
|
25
|
-
|
|
27
|
+
options = options || {};
|
|
28
|
+
const spinner = ora(options.abort ? 'Aborting merge...' : `Merging '${sourceBranch}'...`).start();
|
|
26
29
|
|
|
27
30
|
try {
|
|
28
31
|
const gentPath = await getGentPath();
|
|
@@ -32,6 +35,18 @@ async function merge(sourceBranch, options) {
|
|
|
32
35
|
const branches = repository.branches || {};
|
|
33
36
|
const currentBranch = repository.currentBranch;
|
|
34
37
|
|
|
38
|
+
if (options.abort) {
|
|
39
|
+
await abortMerge(gentPath, cwd, repository, spinner);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (options.continue) {
|
|
43
|
+
throw new Error('Legacy merge continuation uses "gent resolve"');
|
|
44
|
+
}
|
|
45
|
+
if (options.ai) {
|
|
46
|
+
await ai.prime();
|
|
47
|
+
if (!ai.isEnabled()) throw new Error(ai.disabledHint());
|
|
48
|
+
}
|
|
49
|
+
|
|
35
50
|
// Validate branches
|
|
36
51
|
if (!branches.hasOwnProperty(sourceBranch)) {
|
|
37
52
|
spinner.fail(chalk.red(`Branch '${sourceBranch}' not found`));
|
|
@@ -95,6 +110,10 @@ async function merge(sourceBranch, options) {
|
|
|
95
110
|
|
|
96
111
|
spinner.succeed(chalk.green(`Fast-forward merge: ${currentBranch} → ${theirsHash.substring(0, 7)}`));
|
|
97
112
|
await pet.celebrate('merge');
|
|
113
|
+
if (options.ai) {
|
|
114
|
+
console.log(chalk.bold.cyan('\nAI review of the completed merge'));
|
|
115
|
+
await reviewCommand(theirsHash, { head: true });
|
|
116
|
+
}
|
|
98
117
|
return;
|
|
99
118
|
}
|
|
100
119
|
|
|
@@ -134,8 +153,10 @@ async function merge(sourceBranch, options) {
|
|
|
134
153
|
}
|
|
135
154
|
}
|
|
136
155
|
|
|
137
|
-
|
|
138
|
-
|
|
156
|
+
if (!options.ai) {
|
|
157
|
+
console.log(chalk.yellow(`\nConflict markers: <<<<<<< HEAD / ======= / >>>>>>> ${sourceBranch}`));
|
|
158
|
+
console.log(chalk.cyan('Resolve conflicts, then run "gent resolve"'));
|
|
159
|
+
}
|
|
139
160
|
}
|
|
140
161
|
|
|
141
162
|
// Store merged tree
|
|
@@ -199,6 +220,10 @@ async function merge(sourceBranch, options) {
|
|
|
199
220
|
console.log(chalk.gray(` Ours: ${oursHash.substring(0, 7)} Theirs: ${theirsHash.substring(0, 7)}`));
|
|
200
221
|
console.log(chalk.green(` ${autoResolved} file(s) merged automatically`));
|
|
201
222
|
await pet.celebrate('merge');
|
|
223
|
+
if (options.ai) {
|
|
224
|
+
console.log(chalk.bold.cyan('\nAI review of the completed merge'));
|
|
225
|
+
await reviewCommand(mergeCommit.hash, { head: true });
|
|
226
|
+
}
|
|
202
227
|
} else {
|
|
203
228
|
// Stage the merge state for manual resolution
|
|
204
229
|
const staging = await readJSON(path.join(gentPath, STAGING_FILE));
|
|
@@ -212,7 +237,12 @@ async function merge(sourceBranch, options) {
|
|
|
212
237
|
conflicts: mergeResult.conflicts
|
|
213
238
|
};
|
|
214
239
|
await writeJSON(path.join(gentPath, STAGING_FILE), staging);
|
|
215
|
-
|
|
240
|
+
if (options.ai) {
|
|
241
|
+
process.exitCode = 0;
|
|
242
|
+
await require('./resolve')({ ai: true });
|
|
243
|
+
} else {
|
|
244
|
+
process.exitCode = 1;
|
|
245
|
+
}
|
|
216
246
|
}
|
|
217
247
|
|
|
218
248
|
} catch (error) {
|
|
@@ -254,8 +284,11 @@ function safePath(cwd, relativePath) {
|
|
|
254
284
|
|
|
255
285
|
async function assertCleanWorkingTree(gentPath, cwd, commit) {
|
|
256
286
|
const staging = await readJSON(path.join(gentPath, STAGING_FILE));
|
|
257
|
-
if (
|
|
258
|
-
throw new Error('
|
|
287
|
+
if (staging.mergeState) {
|
|
288
|
+
throw new Error('A merge is already in progress; run "gent resolve" or "gent merge --abort"');
|
|
289
|
+
}
|
|
290
|
+
if ((staging.entries || []).length || (staging.files || []).length) {
|
|
291
|
+
throw new Error('Commit, stash, or unstage current changes before merging');
|
|
259
292
|
}
|
|
260
293
|
for (const entry of treeOf(commit)) {
|
|
261
294
|
const fullPath = safePath(cwd, entry.name || entry.path);
|
|
@@ -266,6 +299,44 @@ async function assertCleanWorkingTree(gentPath, cwd, commit) {
|
|
|
266
299
|
}
|
|
267
300
|
}
|
|
268
301
|
|
|
302
|
+
async function abortMerge(gentPath, cwd, repository, spinner) {
|
|
303
|
+
const staging = await readJSON(path.join(gentPath, STAGING_FILE));
|
|
304
|
+
const state = staging.mergeState;
|
|
305
|
+
if (!state) {
|
|
306
|
+
spinner.info(chalk.yellow('No merge in progress'));
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const oursCommit = (repository.commits || []).find(commit => commit.hash === state.oursHash);
|
|
311
|
+
if (!oursCommit) throw new Error('Cannot abort merge: original commit is missing');
|
|
312
|
+
|
|
313
|
+
const oursTree = treeOf(oursCommit);
|
|
314
|
+
const mergeTree = state.mergedEntries || [];
|
|
315
|
+
const oursNames = new Set(oursTree.map(entry => entry.name || entry.path));
|
|
316
|
+
const restored = new Map();
|
|
317
|
+
for (const entry of oursTree) {
|
|
318
|
+
restored.set(entry.name || entry.path, await readBlob(gentPath, entry.hash));
|
|
319
|
+
}
|
|
320
|
+
for (const entry of mergeTree) {
|
|
321
|
+
const name = entry.name || entry.path;
|
|
322
|
+
if (!oursNames.has(name)) {
|
|
323
|
+
await fs.unlink(safePath(cwd, name)).catch(error => {
|
|
324
|
+
if (error.code !== 'ENOENT') throw error;
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
for (const [name, bytes] of restored) {
|
|
329
|
+
const fullPath = safePath(cwd, name);
|
|
330
|
+
await fs.mkdir(path.dirname(fullPath), { recursive: true });
|
|
331
|
+
await fs.writeFile(fullPath, bytes);
|
|
332
|
+
}
|
|
333
|
+
staging.entries = [];
|
|
334
|
+
staging.files = [];
|
|
335
|
+
staging.mergeState = null;
|
|
336
|
+
await writeJSON(path.join(gentPath, STAGING_FILE), staging);
|
|
337
|
+
spinner.succeed(chalk.green('Merge aborted; working tree restored'));
|
|
338
|
+
}
|
|
339
|
+
|
|
269
340
|
async function checkoutTree(gentPath, cwd, previousEntries, nextEntries) {
|
|
270
341
|
const previous = new Map(previousEntries.map(entry => [entry.name || entry.path, entry]));
|
|
271
342
|
const next = new Map(nextEntries.map(entry => [entry.name || entry.path, entry]));
|
package/src/commands/resolve.js
CHANGED
|
@@ -31,6 +31,7 @@ const { generateCommitHash } = require('../utils/helpers');
|
|
|
31
31
|
const authStorage = require('../utils/auth-storage');
|
|
32
32
|
const journal = require('../utils/journal');
|
|
33
33
|
const ai = require('../utils/ai-service');
|
|
34
|
+
const reviewCommand = require('./review');
|
|
34
35
|
|
|
35
36
|
async function resolve(options = {}) {
|
|
36
37
|
try {
|
|
@@ -64,6 +65,19 @@ async function resolve(options = {}) {
|
|
|
64
65
|
return;
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
if (!options.ai && ai.isEnabled() && process.stdin.isTTY && process.stdout.isTTY) {
|
|
69
|
+
const { mode } = await inquirer.prompt([{
|
|
70
|
+
type: 'list',
|
|
71
|
+
name: 'mode',
|
|
72
|
+
message: 'How should Gent resolve this merge?',
|
|
73
|
+
choices: [
|
|
74
|
+
{ name: 'Merge with AI (fast) — resolve, commit, then review', value: 'ai' },
|
|
75
|
+
{ name: 'Resolve manually — choose each conflict', value: 'manual' },
|
|
76
|
+
],
|
|
77
|
+
}]);
|
|
78
|
+
options.ai = mode === 'ai';
|
|
79
|
+
}
|
|
80
|
+
|
|
67
81
|
console.log(chalk.bold.cyan(`\nResolving merge of '${mergeState.sourceBranch}' — ${markerFiles.length} file(s)\n`));
|
|
68
82
|
|
|
69
83
|
// Working copy of merged tree entries (we patch hashes as files resolve).
|
|
@@ -126,23 +140,28 @@ async function resolve(options = {}) {
|
|
|
126
140
|
|
|
127
141
|
if (unresolvedFiles > 0) {
|
|
128
142
|
console.log(chalk.yellow(`\n${unresolvedFiles} file(s) still have conflicts. Re-run "gent resolve" when ready.`));
|
|
143
|
+
process.exitCode = 1;
|
|
129
144
|
return;
|
|
130
145
|
}
|
|
131
146
|
|
|
132
147
|
// All conflicts resolved — offer to finalize the merge commit.
|
|
133
|
-
const
|
|
148
|
+
const finalize = options.ai || (await inquirer.prompt([{
|
|
134
149
|
type: 'confirm',
|
|
135
150
|
name: 'finalize',
|
|
136
151
|
message: 'All conflicts resolved. Create the merge commit now?',
|
|
137
152
|
default: true
|
|
138
|
-
}]);
|
|
153
|
+
}])).finalize;
|
|
139
154
|
|
|
140
155
|
if (!finalize) {
|
|
141
156
|
console.log(chalk.cyan('Resolved files staged. Run "gent commit" when ready.'));
|
|
142
157
|
return;
|
|
143
158
|
}
|
|
144
159
|
|
|
145
|
-
await finalizeMerge(gentPath, staging, mergeState, entriesByName);
|
|
160
|
+
const mergeCommit = await finalizeMerge(gentPath, staging, mergeState, entriesByName);
|
|
161
|
+
if (options.ai) {
|
|
162
|
+
console.log(chalk.bold.cyan('\nAI review of the completed merge'));
|
|
163
|
+
await reviewCommand(mergeCommit.hash, { head: true });
|
|
164
|
+
}
|
|
146
165
|
} catch (error) {
|
|
147
166
|
if (error.code === 'ENOENT' && error.message.includes('.gent')) {
|
|
148
167
|
console.error(chalk.red('Error: Not a gent repository'));
|
|
@@ -177,9 +196,9 @@ async function resolveHunk(seg, file, idx, total, options = {}) {
|
|
|
177
196
|
choices.push({ name: 'Skip the rest of this file', value: 'skip' });
|
|
178
197
|
|
|
179
198
|
if (options.ai) {
|
|
180
|
-
const suggestion = await askAiForHunk(seg, file);
|
|
199
|
+
const suggestion = await askAiForHunk(seg, file, true);
|
|
181
200
|
if (suggestion !== null) return suggestion;
|
|
182
|
-
|
|
201
|
+
return null;
|
|
183
202
|
}
|
|
184
203
|
|
|
185
204
|
const { choice } = await inquirer.prompt([{
|
|
@@ -212,13 +231,17 @@ async function resolveHunk(seg, file, idx, total, options = {}) {
|
|
|
212
231
|
}
|
|
213
232
|
}
|
|
214
233
|
|
|
215
|
-
async function askAiForHunk(seg, file) {
|
|
234
|
+
async function askAiForHunk(seg, file, autoAccept = false) {
|
|
216
235
|
try {
|
|
217
236
|
const suggestion = await ai.resolveConflictHunk({
|
|
218
237
|
ours: seg.ours.join('\n'),
|
|
219
238
|
theirs: seg.theirs.join('\n'),
|
|
220
239
|
fileName: file
|
|
221
240
|
});
|
|
241
|
+
if (autoAccept) {
|
|
242
|
+
console.log(chalk.green(` ✓ AI resolved ${file}`));
|
|
243
|
+
return suggestion.split('\n');
|
|
244
|
+
}
|
|
222
245
|
console.log(chalk.cyan(' AI suggestion (review before accepting):'));
|
|
223
246
|
suggestion.split('\n').forEach(line => console.log(chalk.cyan(` ${line}`)));
|
|
224
247
|
const { accept } = await inquirer.prompt([{
|
|
@@ -297,6 +320,7 @@ async function finalizeMerge(gentPath, staging, mergeState, entriesByName) {
|
|
|
297
320
|
await writeJSON(path.join(gentPath, STAGING_FILE), staging);
|
|
298
321
|
|
|
299
322
|
console.log(chalk.green(`\n✓ Merge committed — ${mergeCommit.hash.substring(0, 7)}`));
|
|
323
|
+
return mergeCommit;
|
|
300
324
|
}
|
|
301
325
|
|
|
302
326
|
module.exports = resolve;
|
package/src/commands/review.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* gent review <ref> → review diff for that commit
|
|
8
8
|
*
|
|
9
9
|
* Output: prioritized bug/risk list followed by smaller polish suggestions.
|
|
10
|
-
* Without
|
|
10
|
+
* Without local AI, prints the raw diff so the command still has value.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
const path = require('path');
|
|
@@ -73,6 +73,7 @@ async function review(refArg, options = {}) {
|
|
|
73
73
|
|
|
74
74
|
console.log(chalk.bold.cyan(`\n${title}\n`));
|
|
75
75
|
|
|
76
|
+
await ai.prime();
|
|
76
77
|
if (!ai.isEnabled()) {
|
|
77
78
|
console.log(trimmed);
|
|
78
79
|
console.log(chalk.gray(`\n${ai.disabledHint()}`));
|
|
@@ -82,6 +83,7 @@ async function review(refArg, options = {}) {
|
|
|
82
83
|
const spinner = ora(`Reviewing with ${ai.getModel()}...`).start();
|
|
83
84
|
try {
|
|
84
85
|
const out = await ai.complete({
|
|
86
|
+
profile: 'review',
|
|
85
87
|
system:
|
|
86
88
|
'You are a senior code reviewer. Given a unified diff, list concrete ' +
|
|
87
89
|
'issues you would block on, then smaller suggestions. Format:\n' +
|
|
@@ -90,8 +92,7 @@ async function review(refArg, options = {}) {
|
|
|
90
92
|
'🟢 Looks good\n - one-line positive note\n' +
|
|
91
93
|
'Be specific. If nothing is wrong, say so plainly.',
|
|
92
94
|
prompt: `Review this diff:\n\n${trimmed}`,
|
|
93
|
-
maxTokens:
|
|
94
|
-
thinking: true,
|
|
95
|
+
maxTokens: 800,
|
|
95
96
|
});
|
|
96
97
|
spinner.stop();
|
|
97
98
|
console.log(out + '\n');
|
package/src/commands/setup.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* gent setup
|
|
5
5
|
*
|
|
6
|
-
* Walks the user through: backend URL → login/register →
|
|
6
|
+
* Walks the user through: backend URL → login/register → identity.
|
|
7
7
|
* Each step is skippable; nothing is required.
|
|
8
8
|
*/
|
|
9
9
|
|
|
@@ -15,7 +15,6 @@ const axios = require('axios');
|
|
|
15
15
|
const userConfig = require('../utils/user-config');
|
|
16
16
|
const authStorage = require('../utils/auth-storage');
|
|
17
17
|
const authService = require('../services/auth-service');
|
|
18
|
-
const ai = require('../utils/ai-service');
|
|
19
18
|
|
|
20
19
|
async function setup() {
|
|
21
20
|
console.log(boxen(
|
|
@@ -26,7 +25,6 @@ async function setup() {
|
|
|
26
25
|
|
|
27
26
|
await stepBackend();
|
|
28
27
|
await stepAuth();
|
|
29
|
-
await stepAiKey();
|
|
30
28
|
await stepIdentity();
|
|
31
29
|
|
|
32
30
|
console.log(chalk.green('\n✓ Setup complete!'));
|
|
@@ -125,56 +123,8 @@ async function stepAuth() {
|
|
|
125
123
|
}
|
|
126
124
|
}
|
|
127
125
|
|
|
128
|
-
async function stepAiKey() {
|
|
129
|
-
console.log(chalk.bold('\n3. AI features (optional)'));
|
|
130
|
-
const existing = await ai.resolveKey();
|
|
131
|
-
if (existing.value) {
|
|
132
|
-
console.log(chalk.gray(` Key already configured [${existing.source}] — skipping.`));
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
console.log(chalk.gray(' Gent uses Anthropic Claude for commit-message suggestions,'));
|
|
137
|
-
console.log(chalk.gray(' diff explanations, AI conflict resolution, code review, and more.'));
|
|
138
|
-
console.log(chalk.gray(' Get a key at: https://console.anthropic.com/settings/keys'));
|
|
139
|
-
|
|
140
|
-
const { provide } = await inquirer.prompt([{
|
|
141
|
-
type: 'confirm',
|
|
142
|
-
name: 'provide',
|
|
143
|
-
message: 'Add an Anthropic API key now?',
|
|
144
|
-
default: true,
|
|
145
|
-
}]);
|
|
146
|
-
if (!provide) return;
|
|
147
|
-
|
|
148
|
-
const { key } = await inquirer.prompt([{
|
|
149
|
-
type: 'password',
|
|
150
|
-
name: 'key',
|
|
151
|
-
message: 'Anthropic API key:',
|
|
152
|
-
mask: '*',
|
|
153
|
-
validate: (v) => v.length > 0 || 'Cannot be empty',
|
|
154
|
-
}]);
|
|
155
|
-
|
|
156
|
-
await userConfig.set('ai.api_key', key);
|
|
157
|
-
|
|
158
|
-
const { testNow } = await inquirer.prompt([{
|
|
159
|
-
type: 'confirm',
|
|
160
|
-
name: 'testNow',
|
|
161
|
-
message: 'Test the key now (1 small request)?',
|
|
162
|
-
default: true,
|
|
163
|
-
}]);
|
|
164
|
-
if (testNow) {
|
|
165
|
-
const spinner = ora('Asking Claude to say hi...').start();
|
|
166
|
-
try {
|
|
167
|
-
await ai.complete({ prompt: 'Reply with the single word: ok', maxTokens: 4 });
|
|
168
|
-
spinner.succeed(chalk.green('AI key works'));
|
|
169
|
-
} catch (err) {
|
|
170
|
-
spinner.fail(chalk.red(err.message));
|
|
171
|
-
console.log(chalk.yellow(' You can fix this with `gent config set ai.api_key <key>`.'));
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
126
|
async function stepIdentity() {
|
|
177
|
-
console.log(chalk.bold('\
|
|
127
|
+
console.log(chalk.bold('\n3. Default identity'));
|
|
178
128
|
const currentName = await userConfig.getResolved('user.name');
|
|
179
129
|
const currentEmail = await userConfig.getResolved('user.email');
|
|
180
130
|
|
package/src/commands/summary.js
CHANGED
|
@@ -150,12 +150,16 @@ async function summary(options = {}) {
|
|
|
150
150
|
}));
|
|
151
151
|
|
|
152
152
|
if (options.ai) {
|
|
153
|
+
await ai.prime();
|
|
153
154
|
if (!ai.isEnabled()) {
|
|
154
155
|
console.log(chalk.gray(ai.disabledHint()));
|
|
155
156
|
} else {
|
|
156
157
|
try {
|
|
157
158
|
const facts = lines.join('\n').replace(/\[[0-9;]*m/g, ''); // strip colors
|
|
158
|
-
const narrative = await ai.explainChanges(
|
|
159
|
+
const narrative = await ai.explainChanges(
|
|
160
|
+
`Repository stats:\n${facts}\n\nGive a 2-3 sentence health assessment.`,
|
|
161
|
+
'summary',
|
|
162
|
+
);
|
|
159
163
|
console.log(chalk.cyan(narrative));
|
|
160
164
|
} catch (err) {
|
|
161
165
|
console.log(chalk.yellow(`AI summary failed: ${err.message}`));
|
package/src/index.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* History: commit, log, show, tag, explain
|
|
14
14
|
* Branching: branch, checkout, merge, resolve, stash
|
|
15
15
|
* Safety: undo, redo
|
|
16
|
-
* Insight: summary, ask, review, docs, changelog
|
|
16
|
+
* Insight: summary, ask, chat, review, docs, changelog
|
|
17
17
|
* Remote: remote, repos, members, push, pull, search, web, share
|
|
18
18
|
* Auth: register, login, logout, whoami, password
|
|
19
19
|
* AI: ai (status|test|models)
|
|
@@ -72,6 +72,7 @@ const doctorCommand = require('./commands/doctor');
|
|
|
72
72
|
const setupCommand = require('./commands/setup');
|
|
73
73
|
const aiCommand = require('./commands/ai');
|
|
74
74
|
const askCommand = route('ask', require('./commands/ask'));
|
|
75
|
+
const chatCommand = require('./commands/chat');
|
|
75
76
|
const reviewCommand = route('review', require('./commands/review'));
|
|
76
77
|
const docsCommand = route('docs', require('./commands/docs'));
|
|
77
78
|
const changelogCommand = route('changelog', require('./commands/changelog'));
|
|
@@ -188,7 +189,7 @@ program
|
|
|
188
189
|
.description('Record changes to the repository')
|
|
189
190
|
.option('-m, --message <message>', 'Commit message')
|
|
190
191
|
.option('-a, --all', 'Automatically stage all modified files')
|
|
191
|
-
.option('--ai', 'Suggest a commit message with
|
|
192
|
+
.option('--ai', 'Suggest a commit message with local Gent AI')
|
|
192
193
|
.action(commitCommand);
|
|
193
194
|
|
|
194
195
|
program
|
|
@@ -222,7 +223,7 @@ program
|
|
|
222
223
|
program
|
|
223
224
|
.command('summary')
|
|
224
225
|
.description('Show a repository health & statistics dashboard')
|
|
225
|
-
.option('--ai', 'Add an AI-written health narrative
|
|
226
|
+
.option('--ai', 'Add an AI-written health narrative')
|
|
226
227
|
.action(summaryCommand);
|
|
227
228
|
|
|
228
229
|
// ─── Branching & Merging ────────────────────────────────
|
|
@@ -258,6 +259,7 @@ program
|
|
|
258
259
|
.command('merge [branch]')
|
|
259
260
|
.description('Merge a branch into the current branch (3-way smart merge)')
|
|
260
261
|
.option('-m, --message <message>', 'Merge commit message')
|
|
262
|
+
.option('--ai', 'Resolve conflicts with AI, commit, then review the merge')
|
|
261
263
|
.option('--continue', 'Finish a resolved canonical merge')
|
|
262
264
|
.option('--abort', 'Abort a canonical merge')
|
|
263
265
|
.action(async (branch, options) => {
|
|
@@ -284,7 +286,7 @@ program
|
|
|
284
286
|
program
|
|
285
287
|
.command('resolve')
|
|
286
288
|
.description('Interactively resolve merge conflicts left by "gent merge"')
|
|
287
|
-
.option('--ai', '
|
|
289
|
+
.option('--ai', 'Resolve every text conflict with AI, commit, then review')
|
|
288
290
|
.action(resolveCommand);
|
|
289
291
|
|
|
290
292
|
program
|
|
@@ -352,18 +354,18 @@ program
|
|
|
352
354
|
|
|
353
355
|
program
|
|
354
356
|
.command('setup')
|
|
355
|
-
.description('Interactive first-run wizard (backend URL, login,
|
|
357
|
+
.description('Interactive first-run wizard (backend URL, login, identity)')
|
|
356
358
|
.action(setupCommand);
|
|
357
359
|
|
|
358
360
|
program
|
|
359
361
|
.command('config [subcommand] [args...]')
|
|
360
|
-
.description('Manage CLI settings (list|get|set|unset|path)
|
|
362
|
+
.description('Manage CLI settings (list|get|set|unset|path)')
|
|
361
363
|
.action(configCommand);
|
|
362
364
|
|
|
363
365
|
program
|
|
364
366
|
.command('doctor')
|
|
365
|
-
.description('Run a health check across node, repo, auth, backend, and AI
|
|
366
|
-
.option('--ai', 'Also live-test
|
|
367
|
+
.description('Run a health check across node, repo, auth, backend, and Gent AI')
|
|
368
|
+
.option('--ai', 'Also live-test Gent AI with a tiny request')
|
|
367
369
|
.action(doctorCommand);
|
|
368
370
|
|
|
369
371
|
program
|
|
@@ -375,7 +377,7 @@ program
|
|
|
375
377
|
|
|
376
378
|
program
|
|
377
379
|
.command('ask [question]')
|
|
378
|
-
.description('Ask
|
|
380
|
+
.description('Ask local Gent AI a question about this repo')
|
|
379
381
|
.action(async (question, options) => {
|
|
380
382
|
if (!question && interactive.isInteractive()) {
|
|
381
383
|
question = await interactive.promptAsk();
|
|
@@ -390,6 +392,11 @@ program
|
|
|
390
392
|
.option('--head', 'Force review of HEAD commit')
|
|
391
393
|
.action(reviewCommand);
|
|
392
394
|
|
|
395
|
+
program
|
|
396
|
+
.command('chat [message]')
|
|
397
|
+
.description('Chat with Gent AI about the current repository')
|
|
398
|
+
.action(chatCommand);
|
|
399
|
+
|
|
393
400
|
program
|
|
394
401
|
.command('docs')
|
|
395
402
|
.description('Generate a README.md draft for this repo using AI')
|
|
@@ -552,7 +559,7 @@ function showQuickstart() {
|
|
|
552
559
|
console.log(chalk.gray('A Git-like VCS with cloud sync + AI superpowers.\n'));
|
|
553
560
|
console.log(chalk.bold('First time? Try:'));
|
|
554
561
|
console.log(` ${chalk.cyan('gent auto')} ${chalk.gray('guided init → commit → push (interactive)')}`);
|
|
555
|
-
console.log(` ${chalk.cyan('gent setup')} ${chalk.gray('configure login +
|
|
562
|
+
console.log(` ${chalk.cyan('gent setup')} ${chalk.gray('configure login + identity + remote')}`);
|
|
556
563
|
console.log(` ${chalk.cyan('gent doctor')} ${chalk.gray('check everything is wired up')}`);
|
|
557
564
|
console.log(` ${chalk.cyan('gent template list')} ${chalk.gray('scaffold a starter project')}`);
|
|
558
565
|
console.log();
|
|
@@ -560,9 +567,11 @@ function showQuickstart() {
|
|
|
560
567
|
console.log(` ${chalk.cyan('gent init && gent add -A && gent commit -m "init"')}`);
|
|
561
568
|
console.log(` ${chalk.cyan('gent push')} / ${chalk.cyan('gent pull')} / ${chalk.cyan('gent merge <branch>')}`);
|
|
562
569
|
console.log();
|
|
563
|
-
console.log(chalk.bold('AI features
|
|
570
|
+
console.log(chalk.bold('Local AI features:'));
|
|
571
|
+
console.log(` ${chalk.cyan('gent chat')} ${chalk.gray('interactive repository chat')}`);
|
|
564
572
|
console.log(` ${chalk.cyan('gent ask "what does this repo do?"')}`);
|
|
565
573
|
console.log(` ${chalk.cyan('gent review')} ${chalk.gray('review staged changes')}`);
|
|
574
|
+
console.log(` ${chalk.cyan('gent merge dev --ai')} ${chalk.gray('resolve, commit, then review')}`);
|
|
566
575
|
console.log(` ${chalk.cyan('gent docs --write')} ${chalk.gray('generate README.md')}`);
|
|
567
576
|
console.log(` ${chalk.cyan('gent changelog')} ${chalk.gray('grouped release notes')}`);
|
|
568
577
|
console.log();
|