guito 1.0.7 → 1.0.9
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/CHANGELOG.md +25 -0
- package/README.md +5 -2
- package/bin/ai-review.js +50 -6
- package/bin/commit-message.js +30 -7
- package/bin/guito-server.js +67 -40
- package/bin/ui/browser/index.html +1 -1
- package/bin/ui/browser/main-VNIXKHUJ.js +67 -0
- package/{guito-1.0.7.vsix → guito-1.0.9.vsix} +0 -0
- package/package.json +1 -1
- package/bin/ui/browser/main-2QNMM6BC.js +0 -67
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,31 @@ All notable changes to Guito are documented in this file. The project follows [S
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.0.9] - 2026-10-04
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Every right-click menu now does what it says. The copy actions (commit hash, commit subject, tag, branch, stash and worktree names, worktree paths, file paths) reach the clipboard even where the browser's Async Clipboard API is unavailable — Guito served on a LAN address or inside a webview — and show a message when copying is refused instead of failing silently. Menu items are no longer swallowed by an in-flight background refresh either: while a fetch runs, items that change the repository are visibly disabled until it finishes, and read-only ones stay clickable.
|
|
12
|
+
- **View Details** on a tag whose commit lies outside the loaded history now pages the log to it and selects it, exactly like clicking the tag row, and **Create Archive** on a side-panel row archives that row's ref instead of doing nothing.
|
|
13
|
+
- Checking out a remote branch whose local name already exists no longer fails: the dialog warns before running, and the checkout switches to the existing local branch and fast-forwards it to the remote, reporting a warning when the two have diverged.
|
|
14
|
+
- **Open Source Control View** works inside the VS Code panel (it now asks the extension host) instead of trying to open a `vscode://` link from an iframe.
|
|
15
|
+
|
|
16
|
+
## [1.0.8] - 2026-10-02
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Pull requests are now reviewed with the latest Opus model by default (the **Claude model** setting, `guito.aiReview.model` inside the extension). Pick **Claude Code's default** to go back to whatever Claude Code itself is set to.
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- VS Code extension: a Generate Commit Message button in the Source Control view's title bar drafts the message with Claude Code, using the same server call as the working panel's Generate message button, and fills the Git commit message box.
|
|
25
|
+
- Choose how hard Claude thinks. New **Review effort** and **Commit message effort** settings (`guito.aiReview.effort` and `guito.aiReview.commitMessageEffort` inside the extension) pass Claude Code's `--effort` level, from low to max, to pull request reviews and commit message drafts. Reviews default to high and commit messages to low; pick **Claude Code's default** to use whatever Claude Code itself is set to. An effort picker next to the model picker in a pull request's **Review** tab, preselected from the setting, overrides it for a single run, and the review's summary line shows which effort produced it.
|
|
26
|
+
- Choose how long drafted commit messages are. The new **Commit message style** setting (`guito.aiReview.commitMessageStyle` inside the extension) picks Oneliner, Brief or Descriptive. Oneliner is the default and drafts only the subject line; Brief adds a short description and Descriptive explains what changed and why in full.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- The **Claude model** and **Commit message model** pickers in Settings, and the model picker in a pull request's Review tab, now show the model actually selected instead of the first option.
|
|
31
|
+
|
|
7
32
|
## [1.0.7] - 2026-10-01
|
|
8
33
|
|
|
9
34
|
### Added
|
package/README.md
CHANGED
|
@@ -60,7 +60,7 @@ Click **Uncommitted changes** to open the staged and unstaged file lists. Click
|
|
|
60
60
|
|
|
61
61
|
Enter a commit message and optional description, then choose **Commit staged changes**. Only staged changes are committed; unstaged edits remain on disk. Commit drafts stay available when you close and reopen the panel during the session.
|
|
62
62
|
|
|
63
|
-
**Generate message** drafts a subject and description with Claude Code running on your machine, from the staged diff when anything is staged and from every uncommitted change otherwise, matching the language and style of your recent commits. The draft fills the boxes for you to review and edit; nothing is committed automatically. The **Commit message model** setting picks the model.
|
|
63
|
+
**Generate message** drafts a subject and description with Claude Code running on your machine, from the staged diff when anything is staged and from every uncommitted change otherwise, matching the language and style of your recent commits. The draft fills the boxes for you to review and edit; nothing is committed automatically. The **Commit message model** setting picks the model, and **Commit message style** picks how much it writes: Oneliner (the default) drafts only the subject line, Brief adds a short description, and Descriptive explains the change in full. In the VS Code extension, the same button is also in the Source Control view's title bar, and it fills the Git commit message box.
|
|
64
64
|
|
|
65
65
|
## CLI options
|
|
66
66
|
|
|
@@ -142,8 +142,11 @@ Inside VS Code the reviewer also runs while no Guito panel is open, so pull requ
|
|
|
142
142
|
| `guito.aiReview.pollMinutes` | Minutes between checks. Defaults to 30. |
|
|
143
143
|
| `guito.aiReview.includeDrafts` | Also review draft pull requests. Off by default. |
|
|
144
144
|
| `guito.aiReview.claudePath` | Path to the Claude Code executable. Empty means Guito looks for it (see above). |
|
|
145
|
-
| `guito.aiReview.model` | Which Claude model reviews pull requests.
|
|
145
|
+
| `guito.aiReview.model` | Which Claude model reviews pull requests. Defaults to opus. |
|
|
146
146
|
| `guito.aiReview.commitMessageModel` | Which Claude model drafts commit messages. Defaults to haiku. |
|
|
147
|
+
| `guito.aiReview.effort` | Claude Code effort level for reviews (`low` to `max`). Defaults to high; empty uses Claude Code's default. |
|
|
148
|
+
| `guito.aiReview.commitMessageEffort` | Claude Code effort level for commit messages. Defaults to low; empty uses Claude Code's default. |
|
|
149
|
+
| `guito.aiReview.commitMessageStyle` | `oneliner` (default), `brief`, or `descriptive`. |
|
|
147
150
|
| `guito.aiReview.timeoutSeconds` | How long one review may take. Defaults to 600. |
|
|
148
151
|
| `guito.aiReview.instructions` | Extra reviewing instructions, such as your team's conventions. |
|
|
149
152
|
|
package/bin/ai-review.js
CHANGED
|
@@ -3,18 +3,38 @@ import { createHash } from 'node:crypto';
|
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
const SEVERITIES = ['blocker', 'concern', 'suggestion', 'nit'];
|
|
6
|
+
/** Effort levels Claude Code accepts for --effort; '' leaves it to Claude Code. */
|
|
7
|
+
export const AI_REVIEW_EFFORTS = ['', 'low', 'medium', 'high', 'xhigh', 'max'];
|
|
8
|
+
/** How much a drafted commit message says beyond its subject line. */
|
|
9
|
+
export const COMMIT_MESSAGE_STYLES = ['oneliner', 'brief', 'descriptive'];
|
|
10
|
+
/** The --effort arguments for one run, or none when Claude Code decides. */
|
|
11
|
+
export function effortArgs(effort) {
|
|
12
|
+
return effort ? ['--effort', effort] : [];
|
|
13
|
+
}
|
|
6
14
|
export const DEFAULT_AI_REVIEW_CONFIG = {
|
|
7
15
|
enabled: false,
|
|
8
16
|
scope: 'reviewer',
|
|
9
17
|
pollMinutes: 30,
|
|
10
18
|
includeDrafts: false,
|
|
11
19
|
claudePath: '',
|
|
12
|
-
model: '',
|
|
20
|
+
model: 'opus',
|
|
13
21
|
commitMessageModel: 'haiku',
|
|
22
|
+
effort: 'high',
|
|
23
|
+
commitMessageEffort: 'low',
|
|
24
|
+
commitMessageStyle: 'oneliner',
|
|
14
25
|
timeoutSeconds: 600,
|
|
15
26
|
maxDiffChars: 300000,
|
|
16
27
|
instructions: '',
|
|
17
28
|
};
|
|
29
|
+
/** A known effort level, or undefined so an unknown value keeps the default. */
|
|
30
|
+
export function normalizeEffort(value) {
|
|
31
|
+
if (typeof value !== 'string')
|
|
32
|
+
return undefined;
|
|
33
|
+
const effort = value.trim().toLowerCase();
|
|
34
|
+
return AI_REVIEW_EFFORTS.includes(effort)
|
|
35
|
+
? effort
|
|
36
|
+
: undefined;
|
|
37
|
+
}
|
|
18
38
|
/** Normalizes a partial config (settings file or VS Code) onto the defaults. */
|
|
19
39
|
export function resolveAiReviewConfig(...sources) {
|
|
20
40
|
const config = { ...DEFAULT_AI_REVIEW_CONFIG };
|
|
@@ -40,6 +60,16 @@ export function resolveAiReviewConfig(...sources) {
|
|
|
40
60
|
if (typeof source.commitMessageModel === 'string') {
|
|
41
61
|
config.commitMessageModel = source.commitMessageModel.trim();
|
|
42
62
|
}
|
|
63
|
+
const effort = normalizeEffort(source.effort);
|
|
64
|
+
if (effort !== undefined)
|
|
65
|
+
config.effort = effort;
|
|
66
|
+
const commitMessageEffort = normalizeEffort(source.commitMessageEffort);
|
|
67
|
+
if (commitMessageEffort !== undefined)
|
|
68
|
+
config.commitMessageEffort = commitMessageEffort;
|
|
69
|
+
const commitMessageStyle = String(source.commitMessageStyle ?? '').trim().toLowerCase();
|
|
70
|
+
if (COMMIT_MESSAGE_STYLES.includes(commitMessageStyle)) {
|
|
71
|
+
config.commitMessageStyle = commitMessageStyle;
|
|
72
|
+
}
|
|
43
73
|
if (Number.isFinite(source.timeoutSeconds)) {
|
|
44
74
|
config.timeoutSeconds = Math.min(Math.max(Number(source.timeoutSeconds), 30), 3600);
|
|
45
75
|
}
|
|
@@ -475,9 +505,14 @@ export function createAiReviewer(deps, initialConfig = DEFAULT_AI_REVIEW_CONFIG,
|
|
|
475
505
|
const model = typeof options.model === 'string' && options.model.trim()
|
|
476
506
|
? options.model.trim()
|
|
477
507
|
: config.model;
|
|
478
|
-
// A
|
|
479
|
-
//
|
|
480
|
-
const
|
|
508
|
+
// A per-review effort wins too, '' included: it asks for Claude Code's
|
|
509
|
+
// own default rather than the configured level.
|
|
510
|
+
const effort = normalizeEffort(options.effort) ?? config.effort;
|
|
511
|
+
// A different model or effort earns a fresh look at code already reviewed
|
|
512
|
+
// at this commit; when new commits exist, they alone are reviewed with it.
|
|
513
|
+
// Reviews stored before effort was recorded are not re-run just for that.
|
|
514
|
+
const freshLook = ((previousState.model ?? '') !== model ||
|
|
515
|
+
(previousState.effort !== undefined && previousState.effort !== effort)) &&
|
|
481
516
|
previousState.reviewedCommit === pullRequest.sourceCommit;
|
|
482
517
|
if (!options.force &&
|
|
483
518
|
!freshLook &&
|
|
@@ -522,8 +557,16 @@ export function createAiReviewer(deps, initialConfig = DEFAULT_AI_REVIEW_CONFIG,
|
|
|
522
557
|
instructions: config.instructions,
|
|
523
558
|
});
|
|
524
559
|
const command = await resolveClaudeCommand(config.claudePath);
|
|
525
|
-
const args = [
|
|
526
|
-
|
|
560
|
+
const args = [
|
|
561
|
+
'-p',
|
|
562
|
+
// Background reviews should not show up in the user's Claude Code session history.
|
|
563
|
+
'--no-session-persistence',
|
|
564
|
+
'--output-format',
|
|
565
|
+
'json',
|
|
566
|
+
...(model ? ['--model', model] : []),
|
|
567
|
+
...effortArgs(effort),
|
|
568
|
+
];
|
|
569
|
+
log(`AI review: reviewing pull request ${pullRequestId} with ${command}${model ? ` (${model})` : ''}${effort ? `, ${effort} effort` : ''}`);
|
|
527
570
|
const raw = await runModel(prompt, {
|
|
528
571
|
command,
|
|
529
572
|
args,
|
|
@@ -545,6 +588,7 @@ export function createAiReviewer(deps, initialConfig = DEFAULT_AI_REVIEW_CONFIG,
|
|
|
545
588
|
title: pullRequest.title,
|
|
546
589
|
reviewedCommit: pullRequest.sourceCommit,
|
|
547
590
|
model,
|
|
591
|
+
effort,
|
|
548
592
|
reviewedAt: now().toISOString(),
|
|
549
593
|
passes: current.passes + 1,
|
|
550
594
|
status: 'idle',
|
package/bin/commit-message.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { resolveClaudeCommand, runClaudeCli, } from './ai-review.js';
|
|
1
|
+
import { effortArgs, resolveClaudeCommand, runClaudeCli, } from './ai-review.js';
|
|
2
2
|
/**
|
|
3
3
|
* One-shot commit message drafting.
|
|
4
4
|
*
|
|
@@ -42,9 +42,15 @@ function renderFile(file, budget) {
|
|
|
42
42
|
rows.push('(diff truncated: file too large)');
|
|
43
43
|
return rows.join('\n');
|
|
44
44
|
}
|
|
45
|
-
|
|
45
|
+
/** What the "description" field holds for each commit message style. */
|
|
46
|
+
const DESCRIPTION_BY_STYLE = {
|
|
47
|
+
oneliner: '\\"\\" always; the subject line is the whole message',
|
|
48
|
+
brief: 'one to three short sentences or bullet points on what changed and why, wrapped at 72 characters; \\"\\" when the subject alone is clear',
|
|
49
|
+
descriptive: 'a full body explaining what changed, why, and anything a reviewer should know, wrapped at 72 characters, using bullet points for separate changes',
|
|
50
|
+
};
|
|
51
|
+
const commitMessageContract = (style) => `Reply with one JSON object and nothing else:
|
|
46
52
|
{"subject": "<one line, imperative mood, at most 72 characters, no trailing period>",
|
|
47
|
-
"description": "
|
|
53
|
+
"description": "<${DESCRIPTION_BY_STYLE[style]}>"}
|
|
48
54
|
|
|
49
55
|
Rules:
|
|
50
56
|
- Summarize the change as a whole; do not walk through the diff file by file.
|
|
@@ -67,7 +73,7 @@ export function buildCommitMessagePrompt(input) {
|
|
|
67
73
|
input.recentSubjects.map((subject) => ` - ${subject}`).join('\n')
|
|
68
74
|
: '',
|
|
69
75
|
'',
|
|
70
|
-
|
|
76
|
+
commitMessageContract(input.style),
|
|
71
77
|
'',
|
|
72
78
|
'DIFF',
|
|
73
79
|
'====',
|
|
@@ -130,17 +136,34 @@ export async function generateCommitMessage(options) {
|
|
|
130
136
|
files: options.files,
|
|
131
137
|
recentSubjects: options.recentSubjects,
|
|
132
138
|
maxDiffChars: Math.min(options.config.maxDiffChars, COMMIT_MESSAGE_MAX_DIFF_CHARS),
|
|
139
|
+
style: options.config.commitMessageStyle,
|
|
133
140
|
});
|
|
134
141
|
const command = await resolveClaudeCommand(options.config.claudePath);
|
|
135
|
-
const
|
|
136
|
-
|
|
142
|
+
const effort = options.config.commitMessageEffort;
|
|
143
|
+
const args = [
|
|
144
|
+
'-p',
|
|
145
|
+
// Background drafts should not show up in the user's Claude Code session history.
|
|
146
|
+
'--no-session-persistence',
|
|
147
|
+
'--output-format',
|
|
148
|
+
'json',
|
|
149
|
+
...(model ? ['--model', model] : []),
|
|
150
|
+
...effortArgs(effort),
|
|
151
|
+
];
|
|
152
|
+
options.log?.(`AI commit message: drafting with ${command} (${model}${effort ? `, ${effort} effort` : ''})`);
|
|
137
153
|
const runModel = options.runModel ?? runClaudeCli;
|
|
138
154
|
const raw = await runModel(prompt, {
|
|
139
155
|
command,
|
|
140
156
|
args,
|
|
141
157
|
cwd: options.cwd,
|
|
142
158
|
timeoutMs: Math.min(options.config.timeoutSeconds, COMMIT_MESSAGE_TIMEOUT_SECONDS) * 1000,
|
|
159
|
+
signal: options.signal,
|
|
143
160
|
});
|
|
144
161
|
const { subject, description } = parseCommitMessage(raw);
|
|
145
|
-
|
|
162
|
+
// A one-liner stays one line even when the model adds a body anyway.
|
|
163
|
+
return {
|
|
164
|
+
subject,
|
|
165
|
+
description: options.config.commitMessageStyle === 'oneliner' ? '' : description,
|
|
166
|
+
scope: options.scope,
|
|
167
|
+
model,
|
|
168
|
+
};
|
|
146
169
|
}
|
package/bin/guito-server.js
CHANGED
|
@@ -1297,8 +1297,28 @@ export async function startGuitoServer({ repositoryPath, uiRoot, host, port = 80
|
|
|
1297
1297
|
: '';
|
|
1298
1298
|
if (newBranch) {
|
|
1299
1299
|
await verifyBranchName(newBranch);
|
|
1300
|
-
|
|
1301
|
-
|
|
1300
|
+
const branchExists = track && (await git.branchLocal()).all.includes(newBranch);
|
|
1301
|
+
if (branchExists) {
|
|
1302
|
+
// The name is taken: switch to the existing branch and sync it with
|
|
1303
|
+
// the remote ref instead of failing to create a duplicate.
|
|
1304
|
+
await git.raw(['checkout', newBranch]);
|
|
1305
|
+
try {
|
|
1306
|
+
await git.raw(['branch', '--set-upstream-to', ref, newBranch]);
|
|
1307
|
+
}
|
|
1308
|
+
catch {
|
|
1309
|
+
// Tracking is best effort; keep the branch's existing upstream.
|
|
1310
|
+
}
|
|
1311
|
+
try {
|
|
1312
|
+
await git.raw(['merge', '--ff-only', ref]);
|
|
1313
|
+
}
|
|
1314
|
+
catch (err) {
|
|
1315
|
+
warnings.push(`Branch "${newBranch}" was checked out but could not be synced with "${ref}". It may have diverged: ${err.message}`);
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
else {
|
|
1319
|
+
// --track sets the upstream when checking out a remote-tracking ref.
|
|
1320
|
+
await git.raw(['checkout', '-b', newBranch, ...(track ? ['--track'] : []), ref]);
|
|
1321
|
+
}
|
|
1302
1322
|
}
|
|
1303
1323
|
else {
|
|
1304
1324
|
await git.raw(['checkout', ...(detach ? ['--detach'] : []), ref]);
|
|
@@ -3319,13 +3339,14 @@ export async function startGuitoServer({ repositoryPath, uiRoot, host, port = 80
|
|
|
3319
3339
|
}
|
|
3320
3340
|
});
|
|
3321
3341
|
// Reviews the pull request now; "force" re-reviews the whole diff even when
|
|
3322
|
-
// the merge source commit has not moved, and "model"
|
|
3323
|
-
// configured Claude model for this one run.
|
|
3342
|
+
// the merge source commit has not moved, and "model" and "effort" override
|
|
3343
|
+
// the configured Claude model and effort for this one run.
|
|
3324
3344
|
app.post('/api/azure-devops/pullrequests/:id/ai-review', async (req, resp) => {
|
|
3325
3345
|
try {
|
|
3326
3346
|
const state = await aiReviewer.review(Number(req.params?.id), {
|
|
3327
3347
|
force: req.body?.force === true,
|
|
3328
3348
|
model: typeof req.body?.model === 'string' ? req.body.model : '',
|
|
3349
|
+
effort: typeof req.body?.effort === 'string' ? req.body.effort : undefined,
|
|
3329
3350
|
});
|
|
3330
3351
|
if (state.status === 'error') {
|
|
3331
3352
|
return resp.status(400).type('application/json').send({ error: state.error, state });
|
|
@@ -3371,44 +3392,49 @@ export async function startGuitoServer({ repositoryPath, uiRoot, host, port = 80
|
|
|
3371
3392
|
// ==================== AI commit message ====================
|
|
3372
3393
|
// One Claude Code run over the pending changes; the reply fills the working
|
|
3373
3394
|
// panel's message box. Nothing is committed and no Azure DevOps call is made.
|
|
3395
|
+
// The VS Code Source Control view drafts through the same function.
|
|
3396
|
+
const draftCommitMessage = async (model = '', signal) => {
|
|
3397
|
+
await mutationQueue;
|
|
3398
|
+
const snapshot = await working.snapshot();
|
|
3399
|
+
const staged = snapshot.stagedFiles;
|
|
3400
|
+
const scope = staged.length ? 'staged' : 'working';
|
|
3401
|
+
const files = scope === 'staged' ? staged : [...staged, ...snapshot.unstagedFiles];
|
|
3402
|
+
if (!files.length) {
|
|
3403
|
+
throw new Error('There are no changes to write a commit message about.');
|
|
3404
|
+
}
|
|
3405
|
+
let branch = '';
|
|
3406
|
+
try {
|
|
3407
|
+
branch = await currentBranch();
|
|
3408
|
+
}
|
|
3409
|
+
catch {
|
|
3410
|
+
branch = '';
|
|
3411
|
+
}
|
|
3412
|
+
let recentSubjects = [];
|
|
3413
|
+
try {
|
|
3414
|
+
recentSubjects = (await git.raw(['log', '-n', '12', '--pretty=format:%s']))
|
|
3415
|
+
.split(/\r?\n/)
|
|
3416
|
+
.map((subject) => subject.trim())
|
|
3417
|
+
.filter(Boolean);
|
|
3418
|
+
}
|
|
3419
|
+
catch {
|
|
3420
|
+
// No commits yet (fresh repository); the model manages without style.
|
|
3421
|
+
}
|
|
3422
|
+
return generateCommitMessage({
|
|
3423
|
+
branch,
|
|
3424
|
+
scope,
|
|
3425
|
+
files,
|
|
3426
|
+
recentSubjects,
|
|
3427
|
+
config: await refreshAiReviewConfig(),
|
|
3428
|
+
model,
|
|
3429
|
+
cwd: repositoryPath,
|
|
3430
|
+
signal,
|
|
3431
|
+
runModel: aiReviewModelImpl,
|
|
3432
|
+
log: (line) => onLog?.(line),
|
|
3433
|
+
});
|
|
3434
|
+
};
|
|
3374
3435
|
app.post('/api/commit-message', async (req, resp) => {
|
|
3375
3436
|
try {
|
|
3376
|
-
await
|
|
3377
|
-
const snapshot = await working.snapshot();
|
|
3378
|
-
const staged = snapshot.stagedFiles;
|
|
3379
|
-
const scope = staged.length ? 'staged' : 'working';
|
|
3380
|
-
const files = scope === 'staged' ? staged : [...staged, ...snapshot.unstagedFiles];
|
|
3381
|
-
if (!files.length) {
|
|
3382
|
-
throw new Error('There are no changes to write a commit message about.');
|
|
3383
|
-
}
|
|
3384
|
-
let branch = '';
|
|
3385
|
-
try {
|
|
3386
|
-
branch = await currentBranch();
|
|
3387
|
-
}
|
|
3388
|
-
catch {
|
|
3389
|
-
branch = '';
|
|
3390
|
-
}
|
|
3391
|
-
let recentSubjects = [];
|
|
3392
|
-
try {
|
|
3393
|
-
recentSubjects = (await git.raw(['log', '-n', '12', '--pretty=format:%s']))
|
|
3394
|
-
.split(/\r?\n/)
|
|
3395
|
-
.map((subject) => subject.trim())
|
|
3396
|
-
.filter(Boolean);
|
|
3397
|
-
}
|
|
3398
|
-
catch {
|
|
3399
|
-
// No commits yet (fresh repository); the model manages without style.
|
|
3400
|
-
}
|
|
3401
|
-
const message = await generateCommitMessage({
|
|
3402
|
-
branch,
|
|
3403
|
-
scope,
|
|
3404
|
-
files,
|
|
3405
|
-
recentSubjects,
|
|
3406
|
-
config: await refreshAiReviewConfig(),
|
|
3407
|
-
model: strField(req.body?.model),
|
|
3408
|
-
cwd: repositoryPath,
|
|
3409
|
-
runModel: aiReviewModelImpl,
|
|
3410
|
-
log: (line) => onLog?.(line),
|
|
3411
|
-
});
|
|
3437
|
+
const message = await draftCommitMessage(strField(req.body?.model));
|
|
3412
3438
|
return resp.type('application/json').send(message);
|
|
3413
3439
|
}
|
|
3414
3440
|
catch (err) {
|
|
@@ -3606,6 +3632,7 @@ export async function startGuitoServer({ repositoryPath, uiRoot, host, port = 80
|
|
|
3606
3632
|
return {
|
|
3607
3633
|
address,
|
|
3608
3634
|
aiReview: aiReviewer,
|
|
3635
|
+
generateCommitMessage: draftCommitMessage,
|
|
3609
3636
|
close: async () => {
|
|
3610
3637
|
aiReviewer.stop();
|
|
3611
3638
|
await app.close();
|
|
@@ -9,5 +9,5 @@
|
|
|
9
9
|
<style>:root{--bg:#1e1e1e;--bg-alt:#232323;--bg-hover:#2a2d2e;--border:#3c3c3c;--text:#cccccc;--text-bright:#e8e8e8;--text-dim:#9d9d9d;--accent:#3794ff;--red:#f14c4c;--mono:ui-monospace, "Cascadia Code", "SF Mono", Menlo, Consolas, monospace}*{box-sizing:border-box}html,body{height:100%;margin:0}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;font-size:13px;color:var(--text);background:var(--bg);overflow:hidden}</style><link rel="stylesheet" href="styles-I7K2YZTW.css" media="print" onload="this.media='all'"><noscript><link rel="stylesheet" href="styles-I7K2YZTW.css"></noscript></head>
|
|
10
10
|
<body>
|
|
11
11
|
<app-root></app-root>
|
|
12
|
-
<script src="polyfills-5CFQRCPP.js" type="module"></script><script src="main-
|
|
12
|
+
<script src="polyfills-5CFQRCPP.js" type="module"></script><script src="main-VNIXKHUJ.js" type="module"></script></body>
|
|
13
13
|
</html>
|