buddy-workbench 0.1.74 → 0.1.76
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/package.json +1 -1
- package/server/repositories/settings.js +22 -2
- package/server/routes/branch-sync.js +1 -1
- package/server/routes/pr-conflicts.js +54 -5
- package/server/routes/pr-review.js +466 -27
- package/server/routes/settings.js +15 -1
- package/ui/dist/assets/index-CIElY9EY.css +1 -0
- package/ui/dist/assets/{index-BqVFfTXR.js → index-Ci4SjAkd.js} +181 -168
- package/ui/dist/index.html +2 -2
- package/ui/dist/sw.js +2 -2
- package/ui/dist/assets/index-CAdLVTJk.css +0 -1
package/package.json
CHANGED
|
@@ -11,6 +11,7 @@ export function settingsStatus() {
|
|
|
11
11
|
const settings = readSettings();
|
|
12
12
|
return {
|
|
13
13
|
domain: typeof settings.domain === 'string' ? settings.domain : '',
|
|
14
|
+
aiApiHost: typeof settings.aiApiHost === 'string' ? settings.aiApiHost : '',
|
|
14
15
|
jiraIssuePrefix: typeof settings.jiraIssuePrefix === 'string' ? settings.jiraIssuePrefix : '',
|
|
15
16
|
defaultEditor: typeof settings.defaultEditor === 'string' ? settings.defaultEditor : 'vscode',
|
|
16
17
|
defaultBrowser: typeof settings.defaultBrowser === 'string' ? settings.defaultBrowser : 'chrome',
|
|
@@ -26,6 +27,15 @@ export function settingsStatus() {
|
|
|
26
27
|
};
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
export function saveAiApiHost(aiApiHost) {
|
|
31
|
+
const settings = readSettings();
|
|
32
|
+
if (aiApiHost) settings.aiApiHost = aiApiHost;
|
|
33
|
+
else delete settings.aiApiHost;
|
|
34
|
+
mkdirSync(dirname(paths.settings), { recursive: true });
|
|
35
|
+
writeFileSync(paths.settings, JSON.stringify(settings, null, 2), { mode: 0o600 });
|
|
36
|
+
return settingsStatus();
|
|
37
|
+
}
|
|
38
|
+
|
|
29
39
|
export function saveDomain(domain) {
|
|
30
40
|
const settings = readSettings();
|
|
31
41
|
if (domain) settings.domain = domain;
|
|
@@ -112,7 +122,14 @@ export function readPrReviewCustomRules() {
|
|
|
112
122
|
return Array.isArray(settings.prReviewCustomRules) ? settings.prReviewCustomRules : [];
|
|
113
123
|
}
|
|
114
124
|
|
|
115
|
-
export function
|
|
125
|
+
export function readPrReviewFileFilters() {
|
|
126
|
+
const settings = readSettings();
|
|
127
|
+
return Array.isArray(settings.prReviewFileFilters)
|
|
128
|
+
? settings.prReviewFileFilters.filter((pattern) => typeof pattern === 'string' && pattern.trim()).map((pattern) => pattern.trim())
|
|
129
|
+
: [];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function savePrReviewRules(rules, customRules, fileFilters, defaultRules) {
|
|
116
133
|
const settings = readSettings();
|
|
117
134
|
const allowed = new Map(defaultRules.map((rule) => [rule.id, rule]));
|
|
118
135
|
settings.prReviewRules = {};
|
|
@@ -135,9 +152,12 @@ export function savePrReviewRules(rules, customRules, defaultRules) {
|
|
|
135
152
|
enabled: rule.enabled !== false,
|
|
136
153
|
severity: ['critical', 'warning', 'info'].includes(rule.severity) ? rule.severity : 'warning'
|
|
137
154
|
}));
|
|
155
|
+
settings.prReviewFileFilters = [...new Set((Array.isArray(fileFilters) ? fileFilters : [])
|
|
156
|
+
.filter((pattern) => typeof pattern === 'string' && pattern.trim())
|
|
157
|
+
.map((pattern) => pattern.trim().slice(0, 300)))].slice(0, 100);
|
|
138
158
|
mkdirSync(dirname(paths.settings), { recursive: true });
|
|
139
159
|
writeFileSync(paths.settings, JSON.stringify(settings, null, 2), { mode: 0o600 });
|
|
140
|
-
return { rules: readPrReviewRules(defaultRules), customRules: readPrReviewCustomRules() };
|
|
160
|
+
return { rules: readPrReviewRules(defaultRules), customRules: readPrReviewCustomRules(), fileFilters: readPrReviewFileFilters() };
|
|
141
161
|
}
|
|
142
162
|
|
|
143
163
|
const accessTokenFields = {
|
|
@@ -355,7 +355,7 @@ router.post('/create-pr', async (req, res) => {
|
|
|
355
355
|
|
|
356
356
|
const prPayload = {
|
|
357
357
|
title: title || `Merge ${sourceBranch} to ${targetBranch}`,
|
|
358
|
-
description: `Automated Pull Request created
|
|
358
|
+
description: `Automated Pull Request created.\n\nMerging \`${sourceBranch}\` into \`${targetBranch}\`.`,
|
|
359
359
|
fromRef: {
|
|
360
360
|
id: `refs/heads/${sourceBranch}`,
|
|
361
361
|
repository: {
|
|
@@ -118,13 +118,62 @@ function pathOf(change) {
|
|
|
118
118
|
return change?.path?.toString || change?.path?.name || change?.path || change?.src?.toString || '';
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
function extractTextFromContent(data) {
|
|
122
|
+
if (data === null || data === undefined) return '';
|
|
123
|
+
if (typeof data === 'object') {
|
|
124
|
+
if (Array.isArray(data.lines)) {
|
|
125
|
+
return data.lines.map((line) => {
|
|
126
|
+
if (typeof line === 'string') return line;
|
|
127
|
+
if (line && typeof line.text === 'string') return line.text;
|
|
128
|
+
return typeof line === 'object' && line !== null ? (line.text ?? JSON.stringify(line)) : String(line ?? '');
|
|
129
|
+
}).join('\n');
|
|
130
|
+
}
|
|
131
|
+
if (typeof data.text === 'string') return data.text;
|
|
132
|
+
return JSON.stringify(data);
|
|
133
|
+
}
|
|
134
|
+
if (typeof data === 'string') {
|
|
135
|
+
const trimmed = data.trim();
|
|
136
|
+
if (trimmed.startsWith('{') && (trimmed.includes('"lines"') || trimmed.includes('"text"'))) {
|
|
137
|
+
try {
|
|
138
|
+
const parsed = JSON.parse(trimmed);
|
|
139
|
+
if (Array.isArray(parsed?.lines)) {
|
|
140
|
+
return parsed.lines.map((line) => {
|
|
141
|
+
if (typeof line === 'string') return line;
|
|
142
|
+
if (line && typeof line.text === 'string') return line.text;
|
|
143
|
+
return typeof line === 'object' && line !== null ? (line.text ?? JSON.stringify(line)) : String(line ?? '');
|
|
144
|
+
}).join('\n');
|
|
145
|
+
}
|
|
146
|
+
if (typeof parsed?.text === 'string') return parsed.text;
|
|
147
|
+
} catch {}
|
|
148
|
+
}
|
|
149
|
+
return data;
|
|
150
|
+
}
|
|
151
|
+
return String(data);
|
|
152
|
+
}
|
|
153
|
+
|
|
121
154
|
async function readFile({ host, projectKey, repositorySlug, commit, path }) {
|
|
122
155
|
if (!commit || !path) return '';
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
156
|
+
const encodedPath = path.split('/').map(encodeURIComponent).join('/');
|
|
157
|
+
|
|
158
|
+
// 1. Try raw endpoint first (returns pure plaintext)
|
|
159
|
+
const rawUrl = `https://${host}/rest/api/latest/projects/${encodeURIComponent(projectKey)}/repos/${encodeURIComponent(repositorySlug)}/raw/${encodedPath}?at=${encodeURIComponent(commit)}`;
|
|
160
|
+
try {
|
|
161
|
+
const rawRes = await client.get(rawUrl, { headers: authHeaders(), responseType: 'text' });
|
|
162
|
+
if (rawRes.status >= 200 && rawRes.status < 300 && rawRes.data !== undefined) {
|
|
163
|
+
return extractTextFromContent(rawRes.data);
|
|
164
|
+
}
|
|
165
|
+
} catch {}
|
|
166
|
+
|
|
167
|
+
// 2. Fallback to browse endpoint with limit=10000
|
|
168
|
+
const browseUrl = `https://${host}/rest/api/latest/projects/${encodeURIComponent(projectKey)}/repos/${encodeURIComponent(repositorySlug)}/browse/${encodedPath}?at=${encodeURIComponent(commit)}&limit=10000`;
|
|
169
|
+
try {
|
|
170
|
+
const response = await client.get(browseUrl, { headers: authHeaders(), responseType: 'text' });
|
|
171
|
+
if (response.status >= 200 && response.status < 300 && response.data !== undefined) {
|
|
172
|
+
return extractTextFromContent(response.data);
|
|
173
|
+
}
|
|
174
|
+
} catch {}
|
|
175
|
+
|
|
176
|
+
return '';
|
|
128
177
|
}
|
|
129
178
|
|
|
130
179
|
router.post('/load', async (req, res) => {
|