mobbdev 1.0.204 → 1.0.206
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/dist/args/commands/upload_ai_blame.d.mts +176 -2
- package/dist/args/commands/upload_ai_blame.mjs +82 -20
- package/dist/index.mjs +127 -151
- package/package.json +2 -1
|
@@ -1,5 +1,172 @@
|
|
|
1
1
|
import * as Yargs from 'yargs';
|
|
2
|
+
import z from 'zod';
|
|
2
3
|
|
|
4
|
+
declare const PromptItemZ: z.ZodObject<{
|
|
5
|
+
type: z.ZodEnum<["USER_PROMPT", "AI_RESPONSE", "TOOL_EXECUTION", "AI_THINKING"]>;
|
|
6
|
+
attachedFiles: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
7
|
+
relativePath: z.ZodString;
|
|
8
|
+
startLine: z.ZodOptional<z.ZodNumber>;
|
|
9
|
+
}, "strip", z.ZodTypeAny, {
|
|
10
|
+
relativePath: string;
|
|
11
|
+
startLine?: number | undefined;
|
|
12
|
+
}, {
|
|
13
|
+
relativePath: string;
|
|
14
|
+
startLine?: number | undefined;
|
|
15
|
+
}>, "many">>;
|
|
16
|
+
tokens: z.ZodOptional<z.ZodObject<{
|
|
17
|
+
inputCount: z.ZodNumber;
|
|
18
|
+
outputCount: z.ZodNumber;
|
|
19
|
+
}, "strip", z.ZodTypeAny, {
|
|
20
|
+
inputCount: number;
|
|
21
|
+
outputCount: number;
|
|
22
|
+
}, {
|
|
23
|
+
inputCount: number;
|
|
24
|
+
outputCount: number;
|
|
25
|
+
}>>;
|
|
26
|
+
text: z.ZodOptional<z.ZodString>;
|
|
27
|
+
date: z.ZodOptional<z.ZodDate>;
|
|
28
|
+
tool: z.ZodOptional<z.ZodObject<{
|
|
29
|
+
name: z.ZodString;
|
|
30
|
+
parameters: z.ZodString;
|
|
31
|
+
result: z.ZodString;
|
|
32
|
+
rawArguments: z.ZodOptional<z.ZodString>;
|
|
33
|
+
accepted: z.ZodOptional<z.ZodBoolean>;
|
|
34
|
+
}, "strip", z.ZodTypeAny, {
|
|
35
|
+
name: string;
|
|
36
|
+
parameters: string;
|
|
37
|
+
result: string;
|
|
38
|
+
accepted?: boolean | undefined;
|
|
39
|
+
rawArguments?: string | undefined;
|
|
40
|
+
}, {
|
|
41
|
+
name: string;
|
|
42
|
+
parameters: string;
|
|
43
|
+
result: string;
|
|
44
|
+
accepted?: boolean | undefined;
|
|
45
|
+
rawArguments?: string | undefined;
|
|
46
|
+
}>>;
|
|
47
|
+
}, "strip", z.ZodTypeAny, {
|
|
48
|
+
type: "USER_PROMPT" | "AI_RESPONSE" | "TOOL_EXECUTION" | "AI_THINKING";
|
|
49
|
+
tool?: {
|
|
50
|
+
name: string;
|
|
51
|
+
parameters: string;
|
|
52
|
+
result: string;
|
|
53
|
+
accepted?: boolean | undefined;
|
|
54
|
+
rawArguments?: string | undefined;
|
|
55
|
+
} | undefined;
|
|
56
|
+
date?: Date | undefined;
|
|
57
|
+
text?: string | undefined;
|
|
58
|
+
attachedFiles?: {
|
|
59
|
+
relativePath: string;
|
|
60
|
+
startLine?: number | undefined;
|
|
61
|
+
}[] | undefined;
|
|
62
|
+
tokens?: {
|
|
63
|
+
inputCount: number;
|
|
64
|
+
outputCount: number;
|
|
65
|
+
} | undefined;
|
|
66
|
+
}, {
|
|
67
|
+
type: "USER_PROMPT" | "AI_RESPONSE" | "TOOL_EXECUTION" | "AI_THINKING";
|
|
68
|
+
tool?: {
|
|
69
|
+
name: string;
|
|
70
|
+
parameters: string;
|
|
71
|
+
result: string;
|
|
72
|
+
accepted?: boolean | undefined;
|
|
73
|
+
rawArguments?: string | undefined;
|
|
74
|
+
} | undefined;
|
|
75
|
+
date?: Date | undefined;
|
|
76
|
+
text?: string | undefined;
|
|
77
|
+
attachedFiles?: {
|
|
78
|
+
relativePath: string;
|
|
79
|
+
startLine?: number | undefined;
|
|
80
|
+
}[] | undefined;
|
|
81
|
+
tokens?: {
|
|
82
|
+
inputCount: number;
|
|
83
|
+
outputCount: number;
|
|
84
|
+
} | undefined;
|
|
85
|
+
}>;
|
|
86
|
+
type PromptItem = z.infer<typeof PromptItemZ>;
|
|
87
|
+
declare const PromptItemArrayZ: z.ZodArray<z.ZodObject<{
|
|
88
|
+
type: z.ZodEnum<["USER_PROMPT", "AI_RESPONSE", "TOOL_EXECUTION", "AI_THINKING"]>;
|
|
89
|
+
attachedFiles: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
90
|
+
relativePath: z.ZodString;
|
|
91
|
+
startLine: z.ZodOptional<z.ZodNumber>;
|
|
92
|
+
}, "strip", z.ZodTypeAny, {
|
|
93
|
+
relativePath: string;
|
|
94
|
+
startLine?: number | undefined;
|
|
95
|
+
}, {
|
|
96
|
+
relativePath: string;
|
|
97
|
+
startLine?: number | undefined;
|
|
98
|
+
}>, "many">>;
|
|
99
|
+
tokens: z.ZodOptional<z.ZodObject<{
|
|
100
|
+
inputCount: z.ZodNumber;
|
|
101
|
+
outputCount: z.ZodNumber;
|
|
102
|
+
}, "strip", z.ZodTypeAny, {
|
|
103
|
+
inputCount: number;
|
|
104
|
+
outputCount: number;
|
|
105
|
+
}, {
|
|
106
|
+
inputCount: number;
|
|
107
|
+
outputCount: number;
|
|
108
|
+
}>>;
|
|
109
|
+
text: z.ZodOptional<z.ZodString>;
|
|
110
|
+
date: z.ZodOptional<z.ZodDate>;
|
|
111
|
+
tool: z.ZodOptional<z.ZodObject<{
|
|
112
|
+
name: z.ZodString;
|
|
113
|
+
parameters: z.ZodString;
|
|
114
|
+
result: z.ZodString;
|
|
115
|
+
rawArguments: z.ZodOptional<z.ZodString>;
|
|
116
|
+
accepted: z.ZodOptional<z.ZodBoolean>;
|
|
117
|
+
}, "strip", z.ZodTypeAny, {
|
|
118
|
+
name: string;
|
|
119
|
+
parameters: string;
|
|
120
|
+
result: string;
|
|
121
|
+
accepted?: boolean | undefined;
|
|
122
|
+
rawArguments?: string | undefined;
|
|
123
|
+
}, {
|
|
124
|
+
name: string;
|
|
125
|
+
parameters: string;
|
|
126
|
+
result: string;
|
|
127
|
+
accepted?: boolean | undefined;
|
|
128
|
+
rawArguments?: string | undefined;
|
|
129
|
+
}>>;
|
|
130
|
+
}, "strip", z.ZodTypeAny, {
|
|
131
|
+
type: "USER_PROMPT" | "AI_RESPONSE" | "TOOL_EXECUTION" | "AI_THINKING";
|
|
132
|
+
tool?: {
|
|
133
|
+
name: string;
|
|
134
|
+
parameters: string;
|
|
135
|
+
result: string;
|
|
136
|
+
accepted?: boolean | undefined;
|
|
137
|
+
rawArguments?: string | undefined;
|
|
138
|
+
} | undefined;
|
|
139
|
+
date?: Date | undefined;
|
|
140
|
+
text?: string | undefined;
|
|
141
|
+
attachedFiles?: {
|
|
142
|
+
relativePath: string;
|
|
143
|
+
startLine?: number | undefined;
|
|
144
|
+
}[] | undefined;
|
|
145
|
+
tokens?: {
|
|
146
|
+
inputCount: number;
|
|
147
|
+
outputCount: number;
|
|
148
|
+
} | undefined;
|
|
149
|
+
}, {
|
|
150
|
+
type: "USER_PROMPT" | "AI_RESPONSE" | "TOOL_EXECUTION" | "AI_THINKING";
|
|
151
|
+
tool?: {
|
|
152
|
+
name: string;
|
|
153
|
+
parameters: string;
|
|
154
|
+
result: string;
|
|
155
|
+
accepted?: boolean | undefined;
|
|
156
|
+
rawArguments?: string | undefined;
|
|
157
|
+
} | undefined;
|
|
158
|
+
date?: Date | undefined;
|
|
159
|
+
text?: string | undefined;
|
|
160
|
+
attachedFiles?: {
|
|
161
|
+
relativePath: string;
|
|
162
|
+
startLine?: number | undefined;
|
|
163
|
+
}[] | undefined;
|
|
164
|
+
tokens?: {
|
|
165
|
+
inputCount: number;
|
|
166
|
+
outputCount: number;
|
|
167
|
+
} | undefined;
|
|
168
|
+
}>, "many">;
|
|
169
|
+
type PromptItemArray = z.infer<typeof PromptItemArrayZ>;
|
|
3
170
|
type UploadAiBlameOptions = {
|
|
4
171
|
prompt: string[];
|
|
5
172
|
inference: string[];
|
|
@@ -10,6 +177,13 @@ type UploadAiBlameOptions = {
|
|
|
10
177
|
'tool-name'?: string[];
|
|
11
178
|
};
|
|
12
179
|
declare function uploadAiBlameBuilder(args: Yargs.Argv<unknown>): Yargs.Argv<UploadAiBlameOptions>;
|
|
13
|
-
declare function
|
|
180
|
+
declare function uploadAiBlameHandlerFromExtension(args: {
|
|
181
|
+
prompts: PromptItemArray;
|
|
182
|
+
inference: string;
|
|
183
|
+
model: string;
|
|
184
|
+
tool: string;
|
|
185
|
+
responseTime: string;
|
|
186
|
+
}): Promise<void>;
|
|
187
|
+
declare function uploadAiBlameHandler(args: UploadAiBlameOptions, exitOnError?: boolean): Promise<void>;
|
|
14
188
|
|
|
15
|
-
export { type UploadAiBlameOptions, uploadAiBlameBuilder, uploadAiBlameHandler };
|
|
189
|
+
export { type PromptItem, type PromptItemArray, type UploadAiBlameOptions, uploadAiBlameBuilder, uploadAiBlameHandler, uploadAiBlameHandlerFromExtension };
|
|
@@ -92,9 +92,11 @@ var init_GitService = __esm({
|
|
|
92
92
|
});
|
|
93
93
|
|
|
94
94
|
// src/args/commands/upload_ai_blame.ts
|
|
95
|
-
import
|
|
95
|
+
import fsPromises2 from "fs/promises";
|
|
96
96
|
import path6 from "path";
|
|
97
97
|
import chalk2 from "chalk";
|
|
98
|
+
import { withFile } from "tmp-promise";
|
|
99
|
+
import z27 from "zod";
|
|
98
100
|
|
|
99
101
|
// src/features/analysis/upload-file.ts
|
|
100
102
|
import Debug7 from "debug";
|
|
@@ -5560,6 +5562,29 @@ async function createAuthenticatedMcpGQLClient({
|
|
|
5560
5562
|
}
|
|
5561
5563
|
|
|
5562
5564
|
// src/args/commands/upload_ai_blame.ts
|
|
5565
|
+
var PromptItemZ = z27.object({
|
|
5566
|
+
type: z27.enum(["USER_PROMPT", "AI_RESPONSE", "TOOL_EXECUTION", "AI_THINKING"]),
|
|
5567
|
+
attachedFiles: z27.array(
|
|
5568
|
+
z27.object({
|
|
5569
|
+
relativePath: z27.string(),
|
|
5570
|
+
startLine: z27.number().optional()
|
|
5571
|
+
})
|
|
5572
|
+
).optional(),
|
|
5573
|
+
tokens: z27.object({
|
|
5574
|
+
inputCount: z27.number(),
|
|
5575
|
+
outputCount: z27.number()
|
|
5576
|
+
}).optional(),
|
|
5577
|
+
text: z27.string().optional(),
|
|
5578
|
+
date: z27.date().optional(),
|
|
5579
|
+
tool: z27.object({
|
|
5580
|
+
name: z27.string(),
|
|
5581
|
+
parameters: z27.string(),
|
|
5582
|
+
result: z27.string(),
|
|
5583
|
+
rawArguments: z27.string().optional(),
|
|
5584
|
+
accepted: z27.boolean().optional()
|
|
5585
|
+
}).optional()
|
|
5586
|
+
});
|
|
5587
|
+
var PromptItemArrayZ = z27.array(PromptItemZ);
|
|
5563
5588
|
function uploadAiBlameBuilder(args) {
|
|
5564
5589
|
return args.option("prompt", {
|
|
5565
5590
|
type: "string",
|
|
@@ -5589,17 +5614,44 @@ function uploadAiBlameBuilder(args) {
|
|
|
5589
5614
|
describe: chalk2.bold("Tool/IDE name(s) (optional, one per session)")
|
|
5590
5615
|
}).strict();
|
|
5591
5616
|
}
|
|
5592
|
-
async function
|
|
5617
|
+
async function uploadAiBlameHandlerFromExtension(args) {
|
|
5618
|
+
const uploadArgs = {
|
|
5619
|
+
prompt: [],
|
|
5620
|
+
inference: [],
|
|
5621
|
+
model: [],
|
|
5622
|
+
toolName: [],
|
|
5623
|
+
aiResponseAt: []
|
|
5624
|
+
};
|
|
5625
|
+
await withFile(async (promptFile) => {
|
|
5626
|
+
await fsPromises2.writeFile(
|
|
5627
|
+
promptFile.path,
|
|
5628
|
+
JSON.stringify(args.prompts, null, 2),
|
|
5629
|
+
"utf-8"
|
|
5630
|
+
);
|
|
5631
|
+
uploadArgs.prompt.push(promptFile.path);
|
|
5632
|
+
await withFile(async (inferenceFile) => {
|
|
5633
|
+
await fsPromises2.writeFile(inferenceFile.path, args.inference, "utf-8");
|
|
5634
|
+
uploadArgs.inference.push(inferenceFile.path);
|
|
5635
|
+
uploadArgs.model.push(args.model);
|
|
5636
|
+
uploadArgs.toolName.push(args.tool);
|
|
5637
|
+
uploadArgs.aiResponseAt.push(args.responseTime);
|
|
5638
|
+
await uploadAiBlameHandler(uploadArgs, false);
|
|
5639
|
+
});
|
|
5640
|
+
});
|
|
5641
|
+
}
|
|
5642
|
+
async function uploadAiBlameHandler(args, exitOnError = true) {
|
|
5593
5643
|
const prompts = args.prompt || [];
|
|
5594
5644
|
const inferences = args.inference || [];
|
|
5595
5645
|
const models = args.model || [];
|
|
5596
5646
|
const tools = args.toolName || args["tool-name"] || [];
|
|
5597
5647
|
const responseTimes = args.aiResponseAt || args["ai-response-at"] || [];
|
|
5598
5648
|
if (prompts.length !== inferences.length) {
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
)
|
|
5602
|
-
|
|
5649
|
+
const errorMsg = "prompt and inference must have the same number of entries";
|
|
5650
|
+
console.error(chalk2.red(errorMsg));
|
|
5651
|
+
if (exitOnError) {
|
|
5652
|
+
process.exit(1);
|
|
5653
|
+
}
|
|
5654
|
+
throw new Error(errorMsg);
|
|
5603
5655
|
}
|
|
5604
5656
|
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
5605
5657
|
const sessions = [];
|
|
@@ -5607,10 +5659,17 @@ async function uploadAiBlameHandler(args) {
|
|
|
5607
5659
|
const promptPath = String(prompts[i]);
|
|
5608
5660
|
const inferencePath = String(inferences[i]);
|
|
5609
5661
|
try {
|
|
5610
|
-
await Promise.all([
|
|
5662
|
+
await Promise.all([
|
|
5663
|
+
fsPromises2.access(promptPath),
|
|
5664
|
+
fsPromises2.access(inferencePath)
|
|
5665
|
+
]);
|
|
5611
5666
|
} catch {
|
|
5612
|
-
|
|
5613
|
-
|
|
5667
|
+
const errorMsg = `File not found for session ${i + 1}`;
|
|
5668
|
+
console.error(chalk2.red(errorMsg));
|
|
5669
|
+
if (exitOnError) {
|
|
5670
|
+
process.exit(1);
|
|
5671
|
+
}
|
|
5672
|
+
throw new Error(errorMsg);
|
|
5614
5673
|
}
|
|
5615
5674
|
sessions.push({
|
|
5616
5675
|
promptFileName: path6.basename(promptPath),
|
|
@@ -5624,10 +5683,12 @@ async function uploadAiBlameHandler(args) {
|
|
|
5624
5683
|
const initRes = await gqlClient.uploadAIBlameInferencesInitRaw({ sessions });
|
|
5625
5684
|
const uploadSessions = initRes.uploadAIBlameInferencesInit?.uploadSessions ?? [];
|
|
5626
5685
|
if (uploadSessions.length !== sessions.length) {
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
)
|
|
5630
|
-
|
|
5686
|
+
const errorMsg = "Init failed to return expected number of sessions";
|
|
5687
|
+
console.error(chalk2.red(errorMsg));
|
|
5688
|
+
if (exitOnError) {
|
|
5689
|
+
process.exit(1);
|
|
5690
|
+
}
|
|
5691
|
+
throw new Error(errorMsg);
|
|
5631
5692
|
}
|
|
5632
5693
|
for (let i = 0; i < uploadSessions.length; i++) {
|
|
5633
5694
|
const us = uploadSessions[i];
|
|
@@ -5662,16 +5723,17 @@ async function uploadAiBlameHandler(args) {
|
|
|
5662
5723
|
});
|
|
5663
5724
|
const status = finRes?.finalizeAIBlameInferencesUpload?.status;
|
|
5664
5725
|
if (status !== "OK") {
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
)
|
|
5669
|
-
|
|
5670
|
-
|
|
5726
|
+
const errorMsg = finRes?.finalizeAIBlameInferencesUpload?.error || "unknown error";
|
|
5727
|
+
console.error(chalk2.red(errorMsg));
|
|
5728
|
+
if (exitOnError) {
|
|
5729
|
+
process.exit(1);
|
|
5730
|
+
}
|
|
5731
|
+
throw new Error(errorMsg);
|
|
5671
5732
|
}
|
|
5672
5733
|
console.log(chalk2.green("AI Blame uploads finalized successfully"));
|
|
5673
5734
|
}
|
|
5674
5735
|
export {
|
|
5675
5736
|
uploadAiBlameBuilder,
|
|
5676
|
-
uploadAiBlameHandler
|
|
5737
|
+
uploadAiBlameHandler,
|
|
5738
|
+
uploadAiBlameHandlerFromExtension
|
|
5677
5739
|
};
|
package/dist/index.mjs
CHANGED
|
@@ -7354,6 +7354,7 @@ var GET_BLAME_DOCUMENT = `
|
|
|
7354
7354
|
blame(path: $path) {
|
|
7355
7355
|
ranges {
|
|
7356
7356
|
commit {
|
|
7357
|
+
oid
|
|
7357
7358
|
author {
|
|
7358
7359
|
user {
|
|
7359
7360
|
name
|
|
@@ -7368,7 +7369,7 @@ var GET_BLAME_DOCUMENT = `
|
|
|
7368
7369
|
}
|
|
7369
7370
|
}
|
|
7370
7371
|
}
|
|
7371
|
-
|
|
7372
|
+
|
|
7372
7373
|
}
|
|
7373
7374
|
}
|
|
7374
7375
|
}
|
|
@@ -7776,6 +7777,7 @@ function getGithubSdk(params = {}) {
|
|
|
7776
7777
|
(range) => ({
|
|
7777
7778
|
startingLine: range.startingLine,
|
|
7778
7779
|
endingLine: range.endingLine,
|
|
7780
|
+
commitSha: range.commit.oid,
|
|
7779
7781
|
email: range.commit.author.user?.email || "",
|
|
7780
7782
|
name: range.commit.author.user?.name || "",
|
|
7781
7783
|
login: range.commit.author.user?.login || ""
|
|
@@ -7925,6 +7927,14 @@ function getGithubSdk(params = {}) {
|
|
|
7925
7927
|
direction: "desc",
|
|
7926
7928
|
per_page: 100
|
|
7927
7929
|
});
|
|
7930
|
+
},
|
|
7931
|
+
async listPRFiles(params2) {
|
|
7932
|
+
return octokit.rest.pulls.listFiles({
|
|
7933
|
+
owner: params2.owner,
|
|
7934
|
+
repo: params2.repo,
|
|
7935
|
+
pull_number: params2.pull_number,
|
|
7936
|
+
per_page: 100
|
|
7937
|
+
});
|
|
7928
7938
|
}
|
|
7929
7939
|
};
|
|
7930
7940
|
}
|
|
@@ -8207,24 +8217,21 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
8207
8217
|
this._validateAccessTokenAndUrl();
|
|
8208
8218
|
const { owner, repo } = parseGithubOwnerAndRepo(this.url);
|
|
8209
8219
|
const prNumber = Number(submitRequestId);
|
|
8210
|
-
const prRes = await
|
|
8211
|
-
owner,
|
|
8212
|
-
repo,
|
|
8213
|
-
pull_number: prNumber
|
|
8214
|
-
|
|
8215
|
-
const prDiff = await this.getPrDiff({ pull_number: prNumber });
|
|
8216
|
-
const commitsRes = await this.githubSdk.getPrCommits({
|
|
8217
|
-
owner,
|
|
8218
|
-
repo,
|
|
8219
|
-
pull_number: prNumber
|
|
8220
|
-
});
|
|
8221
|
-
const commits = [];
|
|
8222
|
-
for (const commit of commitsRes.data) {
|
|
8223
|
-
const commitDiff = await this.getCommitDiff(commit.sha);
|
|
8224
|
-
commits.push(commitDiff);
|
|
8225
|
-
}
|
|
8220
|
+
const [prRes, commitsRes, filesRes] = await Promise.all([
|
|
8221
|
+
this.githubSdk.getPr({ owner, repo, pull_number: prNumber }),
|
|
8222
|
+
this.githubSdk.getPrCommits({ owner, repo, pull_number: prNumber }),
|
|
8223
|
+
this.githubSdk.listPRFiles({ owner, repo, pull_number: prNumber })
|
|
8224
|
+
]);
|
|
8226
8225
|
const pr = prRes.data;
|
|
8227
|
-
const
|
|
8226
|
+
const prDiff = await this.getPrDiff({ pull_number: prNumber });
|
|
8227
|
+
const commits = await Promise.all(
|
|
8228
|
+
commitsRes.data.map((commit) => this.getCommitDiff(commit.sha))
|
|
8229
|
+
);
|
|
8230
|
+
const diffLines = await this._attributeLinesViaBlame(
|
|
8231
|
+
pr.head.ref,
|
|
8232
|
+
filesRes.data,
|
|
8233
|
+
commits
|
|
8234
|
+
);
|
|
8228
8235
|
return {
|
|
8229
8236
|
diff: prDiff,
|
|
8230
8237
|
createdAt: new Date(pr.created_at),
|
|
@@ -8344,18 +8351,15 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
8344
8351
|
return { added: 0, removed: 0 };
|
|
8345
8352
|
}
|
|
8346
8353
|
}
|
|
8347
|
-
|
|
8348
|
-
|
|
8349
|
-
|
|
8350
|
-
|
|
8354
|
+
/**
|
|
8355
|
+
* Optimized helper to parse added line numbers from a unified diff patch
|
|
8356
|
+
* Single-pass parsing for minimal CPU usage
|
|
8357
|
+
*/
|
|
8358
|
+
_parseAddedLinesFromPatch(patch) {
|
|
8359
|
+
const addedLines = [];
|
|
8360
|
+
const lines = patch.split("\n");
|
|
8351
8361
|
let currentLineNumber = 0;
|
|
8352
|
-
for (const line of
|
|
8353
|
-
if (line.startsWith("+++")) {
|
|
8354
|
-
const match = line.match(/^\+\+\+ b\/(.+)$/);
|
|
8355
|
-
currentFile = match?.[1] || "";
|
|
8356
|
-
currentLineNumber = 0;
|
|
8357
|
-
continue;
|
|
8358
|
-
}
|
|
8362
|
+
for (const line of lines) {
|
|
8359
8363
|
if (line.startsWith("@@")) {
|
|
8360
8364
|
const match = line.match(/^@@ -\d+(?:,\d+)? \+(\d+)/);
|
|
8361
8365
|
if (match?.[1]) {
|
|
@@ -8364,122 +8368,57 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
8364
8368
|
continue;
|
|
8365
8369
|
}
|
|
8366
8370
|
if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
8367
|
-
|
|
8368
|
-
currentFile,
|
|
8369
|
-
currentLineNumber,
|
|
8370
|
-
line.substring(1),
|
|
8371
|
-
// Remove the '+' prefix
|
|
8372
|
-
commits
|
|
8373
|
-
);
|
|
8374
|
-
if (commitSha && currentFile) {
|
|
8375
|
-
attributions.push({
|
|
8376
|
-
file: currentFile,
|
|
8377
|
-
line: currentLineNumber,
|
|
8378
|
-
commitSha
|
|
8379
|
-
});
|
|
8380
|
-
}
|
|
8371
|
+
addedLines.push(currentLineNumber);
|
|
8381
8372
|
currentLineNumber++;
|
|
8382
8373
|
} else if (!line.startsWith("-")) {
|
|
8383
8374
|
currentLineNumber++;
|
|
8384
8375
|
}
|
|
8385
8376
|
}
|
|
8386
|
-
return
|
|
8377
|
+
return addedLines;
|
|
8387
8378
|
}
|
|
8388
|
-
|
|
8389
|
-
|
|
8390
|
-
|
|
8391
|
-
|
|
8392
|
-
|
|
8393
|
-
|
|
8394
|
-
|
|
8395
|
-
const
|
|
8396
|
-
|
|
8397
|
-
for (const
|
|
8398
|
-
if (
|
|
8399
|
-
const match = commitLine.match(/^\+\+\+ b\/(.+)$/);
|
|
8400
|
-
commitCurrentFile = match?.[1] || "";
|
|
8401
|
-
continue;
|
|
8402
|
-
}
|
|
8403
|
-
if (commitLine.startsWith("@@")) {
|
|
8379
|
+
/**
|
|
8380
|
+
* Attribute lines in a single file to their commits using blame
|
|
8381
|
+
*/
|
|
8382
|
+
async _attributeFileLines(file, headRef, prCommitShas) {
|
|
8383
|
+
try {
|
|
8384
|
+
const blame = await this.getRepoBlameRanges(headRef, file.filename);
|
|
8385
|
+
const addedLines = this._parseAddedLinesFromPatch(file.patch);
|
|
8386
|
+
const addedLinesSet = new Set(addedLines);
|
|
8387
|
+
const fileAttributions = [];
|
|
8388
|
+
for (const blameRange of blame) {
|
|
8389
|
+
if (!prCommitShas.has(blameRange.commitSha)) {
|
|
8404
8390
|
continue;
|
|
8405
8391
|
}
|
|
8406
|
-
|
|
8407
|
-
|
|
8408
|
-
|
|
8409
|
-
|
|
8392
|
+
for (let lineNum = blameRange.startingLine; lineNum <= blameRange.endingLine; lineNum++) {
|
|
8393
|
+
if (addedLinesSet.has(lineNum)) {
|
|
8394
|
+
fileAttributions.push({
|
|
8395
|
+
file: file.filename,
|
|
8396
|
+
line: lineNum,
|
|
8397
|
+
commitSha: blameRange.commitSha
|
|
8398
|
+
});
|
|
8410
8399
|
}
|
|
8411
8400
|
}
|
|
8412
8401
|
}
|
|
8402
|
+
return fileAttributions;
|
|
8403
|
+
} catch (error) {
|
|
8404
|
+
return [];
|
|
8413
8405
|
}
|
|
8414
|
-
for (let i = commits.length - 1; i >= 0; i--) {
|
|
8415
|
-
const commit = commits[i];
|
|
8416
|
-
if (!commit) {
|
|
8417
|
-
continue;
|
|
8418
|
-
}
|
|
8419
|
-
const addedLinesInFile = this._getAddedLinesFromCommit(commit, file);
|
|
8420
|
-
for (const { lineNum, content } of addedLinesInFile) {
|
|
8421
|
-
if (Math.abs(lineNum - lineNumber) <= 10 && this._contentSimilarity(content, normalizedContent) > 0.9) {
|
|
8422
|
-
return commit.commitSha;
|
|
8423
|
-
}
|
|
8424
|
-
}
|
|
8425
|
-
}
|
|
8426
|
-
const lastCommit = commits[commits.length - 1];
|
|
8427
|
-
return lastCommit ? lastCommit.commitSha : null;
|
|
8428
|
-
}
|
|
8429
|
-
_getAddedLinesFromCommit(commit, targetFile) {
|
|
8430
|
-
const addedLines = [];
|
|
8431
|
-
const commitLines = commit.diff.split("\n");
|
|
8432
|
-
let currentFile = "";
|
|
8433
|
-
let currentLineNumber = 0;
|
|
8434
|
-
for (const line of commitLines) {
|
|
8435
|
-
if (line.startsWith("+++")) {
|
|
8436
|
-
const match = line.match(/^\+\+\+ b\/(.+)$/);
|
|
8437
|
-
currentFile = match?.[1] || "";
|
|
8438
|
-
currentLineNumber = 0;
|
|
8439
|
-
continue;
|
|
8440
|
-
}
|
|
8441
|
-
if (line.startsWith("@@")) {
|
|
8442
|
-
const match = line.match(/^@@ -\d+(?:,\d+)? \+(\d+)/);
|
|
8443
|
-
if (match?.[1]) {
|
|
8444
|
-
currentLineNumber = parseInt(match[1], 10);
|
|
8445
|
-
}
|
|
8446
|
-
continue;
|
|
8447
|
-
}
|
|
8448
|
-
if (currentFile === targetFile && line.startsWith("+") && !line.startsWith("+++")) {
|
|
8449
|
-
addedLines.push({
|
|
8450
|
-
lineNum: currentLineNumber,
|
|
8451
|
-
content: line.substring(1).trim()
|
|
8452
|
-
});
|
|
8453
|
-
currentLineNumber++;
|
|
8454
|
-
} else if (!line.startsWith("-")) {
|
|
8455
|
-
if (currentFile === targetFile) {
|
|
8456
|
-
currentLineNumber++;
|
|
8457
|
-
}
|
|
8458
|
-
}
|
|
8459
|
-
}
|
|
8460
|
-
return addedLines;
|
|
8461
8406
|
}
|
|
8462
|
-
|
|
8463
|
-
|
|
8464
|
-
|
|
8465
|
-
|
|
8466
|
-
|
|
8467
|
-
const
|
|
8468
|
-
|
|
8469
|
-
|
|
8470
|
-
|
|
8471
|
-
const
|
|
8472
|
-
|
|
8473
|
-
|
|
8474
|
-
|
|
8475
|
-
|
|
8476
|
-
|
|
8477
|
-
return shorter.length / longer.length;
|
|
8478
|
-
}
|
|
8479
|
-
const set1 = new Set(normalized1.split(""));
|
|
8480
|
-
const set2 = new Set(normalized2.split(""));
|
|
8481
|
-
const intersection = new Set([...set1].filter((x) => set2.has(x)));
|
|
8482
|
-
return intersection.size / Math.max(set1.size, set2.size);
|
|
8407
|
+
/**
|
|
8408
|
+
* Optimized helper to attribute PR lines to commits using blame API
|
|
8409
|
+
* Parallel blame queries for minimal API call time
|
|
8410
|
+
*/
|
|
8411
|
+
async _attributeLinesViaBlame(headRef, changedFiles, prCommits) {
|
|
8412
|
+
const prCommitShas = new Set(prCommits.map((c) => c.commitSha));
|
|
8413
|
+
const filesWithAdditions = changedFiles.filter(
|
|
8414
|
+
(file) => file.patch?.includes("\n+")
|
|
8415
|
+
);
|
|
8416
|
+
const attributions = await Promise.all(
|
|
8417
|
+
filesWithAdditions.map(
|
|
8418
|
+
(file) => this._attributeFileLines(file, headRef, prCommitShas)
|
|
8419
|
+
)
|
|
8420
|
+
);
|
|
8421
|
+
return attributions.flat();
|
|
8483
8422
|
}
|
|
8484
8423
|
};
|
|
8485
8424
|
|
|
@@ -8828,6 +8767,7 @@ async function getGitlabBlameRanges({ ref, gitlabUrl, path: path17 }, options) {
|
|
|
8828
8767
|
return {
|
|
8829
8768
|
startingLine: oldLineNumber,
|
|
8830
8769
|
endingLine: lineNumber - 1,
|
|
8770
|
+
commitSha: range.commit.id,
|
|
8831
8771
|
login: range.commit.author_email,
|
|
8832
8772
|
email: range.commit.author_email,
|
|
8833
8773
|
name: range.commit.author_name
|
|
@@ -19226,9 +19166,34 @@ async function addScmTokenHandler(args) {
|
|
|
19226
19166
|
}
|
|
19227
19167
|
|
|
19228
19168
|
// src/args/commands/upload_ai_blame.ts
|
|
19229
|
-
import
|
|
19169
|
+
import fsPromises3 from "fs/promises";
|
|
19230
19170
|
import path16 from "path";
|
|
19231
19171
|
import chalk10 from "chalk";
|
|
19172
|
+
import { withFile } from "tmp-promise";
|
|
19173
|
+
import z38 from "zod";
|
|
19174
|
+
var PromptItemZ = z38.object({
|
|
19175
|
+
type: z38.enum(["USER_PROMPT", "AI_RESPONSE", "TOOL_EXECUTION", "AI_THINKING"]),
|
|
19176
|
+
attachedFiles: z38.array(
|
|
19177
|
+
z38.object({
|
|
19178
|
+
relativePath: z38.string(),
|
|
19179
|
+
startLine: z38.number().optional()
|
|
19180
|
+
})
|
|
19181
|
+
).optional(),
|
|
19182
|
+
tokens: z38.object({
|
|
19183
|
+
inputCount: z38.number(),
|
|
19184
|
+
outputCount: z38.number()
|
|
19185
|
+
}).optional(),
|
|
19186
|
+
text: z38.string().optional(),
|
|
19187
|
+
date: z38.date().optional(),
|
|
19188
|
+
tool: z38.object({
|
|
19189
|
+
name: z38.string(),
|
|
19190
|
+
parameters: z38.string(),
|
|
19191
|
+
result: z38.string(),
|
|
19192
|
+
rawArguments: z38.string().optional(),
|
|
19193
|
+
accepted: z38.boolean().optional()
|
|
19194
|
+
}).optional()
|
|
19195
|
+
});
|
|
19196
|
+
var PromptItemArrayZ = z38.array(PromptItemZ);
|
|
19232
19197
|
function uploadAiBlameBuilder(args) {
|
|
19233
19198
|
return args.option("prompt", {
|
|
19234
19199
|
type: "string",
|
|
@@ -19258,17 +19223,19 @@ function uploadAiBlameBuilder(args) {
|
|
|
19258
19223
|
describe: chalk10.bold("Tool/IDE name(s) (optional, one per session)")
|
|
19259
19224
|
}).strict();
|
|
19260
19225
|
}
|
|
19261
|
-
async function uploadAiBlameHandler(args) {
|
|
19226
|
+
async function uploadAiBlameHandler(args, exitOnError = true) {
|
|
19262
19227
|
const prompts = args.prompt || [];
|
|
19263
19228
|
const inferences = args.inference || [];
|
|
19264
19229
|
const models = args.model || [];
|
|
19265
19230
|
const tools = args.toolName || args["tool-name"] || [];
|
|
19266
19231
|
const responseTimes = args.aiResponseAt || args["ai-response-at"] || [];
|
|
19267
19232
|
if (prompts.length !== inferences.length) {
|
|
19268
|
-
|
|
19269
|
-
|
|
19270
|
-
)
|
|
19271
|
-
|
|
19233
|
+
const errorMsg = "prompt and inference must have the same number of entries";
|
|
19234
|
+
console.error(chalk10.red(errorMsg));
|
|
19235
|
+
if (exitOnError) {
|
|
19236
|
+
process.exit(1);
|
|
19237
|
+
}
|
|
19238
|
+
throw new Error(errorMsg);
|
|
19272
19239
|
}
|
|
19273
19240
|
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
19274
19241
|
const sessions = [];
|
|
@@ -19276,10 +19243,17 @@ async function uploadAiBlameHandler(args) {
|
|
|
19276
19243
|
const promptPath = String(prompts[i]);
|
|
19277
19244
|
const inferencePath = String(inferences[i]);
|
|
19278
19245
|
try {
|
|
19279
|
-
await Promise.all([
|
|
19246
|
+
await Promise.all([
|
|
19247
|
+
fsPromises3.access(promptPath),
|
|
19248
|
+
fsPromises3.access(inferencePath)
|
|
19249
|
+
]);
|
|
19280
19250
|
} catch {
|
|
19281
|
-
|
|
19282
|
-
|
|
19251
|
+
const errorMsg = `File not found for session ${i + 1}`;
|
|
19252
|
+
console.error(chalk10.red(errorMsg));
|
|
19253
|
+
if (exitOnError) {
|
|
19254
|
+
process.exit(1);
|
|
19255
|
+
}
|
|
19256
|
+
throw new Error(errorMsg);
|
|
19283
19257
|
}
|
|
19284
19258
|
sessions.push({
|
|
19285
19259
|
promptFileName: path16.basename(promptPath),
|
|
@@ -19293,10 +19267,12 @@ async function uploadAiBlameHandler(args) {
|
|
|
19293
19267
|
const initRes = await gqlClient.uploadAIBlameInferencesInitRaw({ sessions });
|
|
19294
19268
|
const uploadSessions = initRes.uploadAIBlameInferencesInit?.uploadSessions ?? [];
|
|
19295
19269
|
if (uploadSessions.length !== sessions.length) {
|
|
19296
|
-
|
|
19297
|
-
|
|
19298
|
-
)
|
|
19299
|
-
|
|
19270
|
+
const errorMsg = "Init failed to return expected number of sessions";
|
|
19271
|
+
console.error(chalk10.red(errorMsg));
|
|
19272
|
+
if (exitOnError) {
|
|
19273
|
+
process.exit(1);
|
|
19274
|
+
}
|
|
19275
|
+
throw new Error(errorMsg);
|
|
19300
19276
|
}
|
|
19301
19277
|
for (let i = 0; i < uploadSessions.length; i++) {
|
|
19302
19278
|
const us = uploadSessions[i];
|
|
@@ -19331,12 +19307,12 @@ async function uploadAiBlameHandler(args) {
|
|
|
19331
19307
|
});
|
|
19332
19308
|
const status = finRes?.finalizeAIBlameInferencesUpload?.status;
|
|
19333
19309
|
if (status !== "OK") {
|
|
19334
|
-
|
|
19335
|
-
|
|
19336
|
-
|
|
19337
|
-
)
|
|
19338
|
-
|
|
19339
|
-
|
|
19310
|
+
const errorMsg = finRes?.finalizeAIBlameInferencesUpload?.error || "unknown error";
|
|
19311
|
+
console.error(chalk10.red(errorMsg));
|
|
19312
|
+
if (exitOnError) {
|
|
19313
|
+
process.exit(1);
|
|
19314
|
+
}
|
|
19315
|
+
throw new Error(errorMsg);
|
|
19340
19316
|
}
|
|
19341
19317
|
console.log(chalk10.green("AI Blame uploads finalized successfully"));
|
|
19342
19318
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mobbdev",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.206",
|
|
4
4
|
"description": "Automated secure code remediation tool",
|
|
5
5
|
"repository": "git+https://github.com/mobb-dev/bugsy.git",
|
|
6
6
|
"main": "dist/index.mjs",
|
|
@@ -92,6 +92,7 @@
|
|
|
92
92
|
"snyk": "1.1300.0",
|
|
93
93
|
"tar": "6.2.1",
|
|
94
94
|
"tmp": "0.2.5",
|
|
95
|
+
"tmp-promise": "3.0.3",
|
|
95
96
|
"undici": "6.21.3",
|
|
96
97
|
"uuid": "11.1.0",
|
|
97
98
|
"ws": "8.18.3",
|