mobbdev 1.1.27 → 1.1.29
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 +16 -2
- package/dist/args/commands/upload_ai_blame.mjs +119 -18
- package/dist/index.mjs +210 -20
- package/package.json +1 -1
|
@@ -16,6 +16,10 @@ type SanitizationCounts = {
|
|
|
16
16
|
};
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
+
type Logger = {
|
|
20
|
+
info: (msg: string, data?: unknown) => void;
|
|
21
|
+
error: (msg: string, data?: unknown) => void;
|
|
22
|
+
};
|
|
19
23
|
declare const PromptItemZ: z.ZodObject<{
|
|
20
24
|
type: z.ZodEnum<["USER_PROMPT", "AI_RESPONSE", "TOOL_EXECUTION", "AI_THINKING"]>;
|
|
21
25
|
attachedFiles: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
@@ -210,7 +214,17 @@ declare function uploadAiBlameHandlerFromExtension(args: {
|
|
|
210
214
|
responseTime: string;
|
|
211
215
|
blameType?: AiBlameInferenceType;
|
|
212
216
|
sessionId?: string;
|
|
217
|
+
apiUrl?: string;
|
|
218
|
+
webAppUrl?: string;
|
|
213
219
|
}): Promise<UploadAiBlameResult>;
|
|
214
|
-
|
|
220
|
+
type UploadAiBlameHandlerOptions = {
|
|
221
|
+
args: UploadAiBlameOptions;
|
|
222
|
+
exitOnError?: boolean;
|
|
223
|
+
apiUrl?: string;
|
|
224
|
+
webAppUrl?: string;
|
|
225
|
+
logger?: Logger;
|
|
226
|
+
};
|
|
227
|
+
declare function uploadAiBlameHandler(options: UploadAiBlameHandlerOptions): Promise<void>;
|
|
228
|
+
declare function uploadAiBlameCommandHandler(args: UploadAiBlameOptions): Promise<void>;
|
|
215
229
|
|
|
216
|
-
export { type PromptItem, type PromptItemArray, type UploadAiBlameOptions, type UploadAiBlameResult, uploadAiBlameBuilder, uploadAiBlameHandler, uploadAiBlameHandlerFromExtension };
|
|
230
|
+
export { type PromptItem, type PromptItemArray, type UploadAiBlameOptions, type UploadAiBlameResult, uploadAiBlameBuilder, uploadAiBlameCommandHandler, uploadAiBlameHandler, uploadAiBlameHandlerFromExtension };
|
|
@@ -80,11 +80,13 @@ import fs4 from "fs";
|
|
|
80
80
|
import ignore from "ignore";
|
|
81
81
|
import * as path5 from "path";
|
|
82
82
|
import { simpleGit as simpleGit2 } from "simple-git";
|
|
83
|
+
var MAX_COMMIT_DIFF_SIZE_BYTES;
|
|
83
84
|
var init_GitService = __esm({
|
|
84
85
|
"src/features/analysis/scm/services/GitService.ts"() {
|
|
85
86
|
"use strict";
|
|
86
87
|
init_configs();
|
|
87
88
|
init_FileUtils();
|
|
89
|
+
MAX_COMMIT_DIFF_SIZE_BYTES = 3 * 1024 * 1024;
|
|
88
90
|
}
|
|
89
91
|
});
|
|
90
92
|
|
|
@@ -817,11 +819,14 @@ var UploadS3BucketInfoDocument = `
|
|
|
817
819
|
}
|
|
818
820
|
`;
|
|
819
821
|
var AnalyzeCommitForExtensionAiBlameDocument = `
|
|
820
|
-
mutation AnalyzeCommitForExtensionAIBlame($repositoryURL: String!, $commitSha: String!, $organizationId: String!) {
|
|
822
|
+
mutation AnalyzeCommitForExtensionAIBlame($repositoryURL: String!, $commitSha: String!, $organizationId: String!, $commitDiff: String, $commitTimestamp: Timestamp, $parentCommits: [ParentCommitInput!]) {
|
|
821
823
|
analyzeCommitForAIBlame(
|
|
822
824
|
repositoryURL: $repositoryURL
|
|
823
825
|
commitSha: $commitSha
|
|
824
826
|
organizationId: $organizationId
|
|
827
|
+
commitDiff: $commitDiff
|
|
828
|
+
commitTimestamp: $commitTimestamp
|
|
829
|
+
parentCommits: $parentCommits
|
|
825
830
|
) {
|
|
826
831
|
__typename
|
|
827
832
|
... on ProcessAIBlameFinalResult {
|
|
@@ -867,6 +872,30 @@ var GetAiBlameAttributionPromptDocument = `
|
|
|
867
872
|
}
|
|
868
873
|
}
|
|
869
874
|
`;
|
|
875
|
+
var GetPromptSummaryDocument = `
|
|
876
|
+
query GetPromptSummary($aiBlameAttributionId: String!) {
|
|
877
|
+
getPromptSummary(aiBlameAttributionId: $aiBlameAttributionId) {
|
|
878
|
+
__typename
|
|
879
|
+
... on PromptSummarySuccess {
|
|
880
|
+
status
|
|
881
|
+
summary {
|
|
882
|
+
goal
|
|
883
|
+
developersPlan
|
|
884
|
+
aiImplementationDetails
|
|
885
|
+
importantInstructionsAndPushbacks
|
|
886
|
+
frictionScore {
|
|
887
|
+
score
|
|
888
|
+
justification
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
... on PromptSummaryError {
|
|
893
|
+
status
|
|
894
|
+
error
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
`;
|
|
870
899
|
var UploadAiBlameInferencesInitDocument = `
|
|
871
900
|
mutation UploadAIBlameInferencesInit($sessions: [AIBlameInferenceInitInput!]!) {
|
|
872
901
|
uploadAIBlameInferencesInit(sessions: $sessions) {
|
|
@@ -1194,6 +1223,9 @@ function getSdk(client, withWrapper = defaultWrapper) {
|
|
|
1194
1223
|
GetAIBlameAttributionPrompt(variables, requestHeaders, signal) {
|
|
1195
1224
|
return withWrapper((wrappedRequestHeaders) => client.request({ document: GetAiBlameAttributionPromptDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "GetAIBlameAttributionPrompt", "query", variables);
|
|
1196
1225
|
},
|
|
1226
|
+
GetPromptSummary(variables, requestHeaders, signal) {
|
|
1227
|
+
return withWrapper((wrappedRequestHeaders) => client.request({ document: GetPromptSummaryDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "GetPromptSummary", "query", variables);
|
|
1228
|
+
},
|
|
1197
1229
|
UploadAIBlameInferencesInit(variables, requestHeaders, signal) {
|
|
1198
1230
|
return withWrapper((wrappedRequestHeaders) => client.request({ document: UploadAiBlameInferencesInitDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "UploadAIBlameInferencesInit", "mutation", variables);
|
|
1199
1231
|
},
|
|
@@ -4734,6 +4766,11 @@ var GQLClient = class {
|
|
|
4734
4766
|
debug6(`init with ${args}`);
|
|
4735
4767
|
this._auth = args;
|
|
4736
4768
|
this._apiUrl = args.apiUrl || API_URL;
|
|
4769
|
+
debug6(
|
|
4770
|
+
"GQLClient constructor: resolved apiUrl=%s (from param: %s)",
|
|
4771
|
+
this._apiUrl,
|
|
4772
|
+
args.apiUrl || "fallback to API_URL constant"
|
|
4773
|
+
);
|
|
4737
4774
|
this._client = new GraphQLClient(this._apiUrl, {
|
|
4738
4775
|
headers: args.type === "apiKey" ? { [API_KEY_HEADER_NAME]: args.apiKey || "" } : {
|
|
4739
4776
|
Authorization: `Bearer ${args.token}`
|
|
@@ -5098,6 +5135,9 @@ var GQLClient = class {
|
|
|
5098
5135
|
async getAIBlameAttributionPrompt(variables) {
|
|
5099
5136
|
return await this._clientSdk.GetAIBlameAttributionPrompt(variables);
|
|
5100
5137
|
}
|
|
5138
|
+
async getAIBlameAttributionPromptSummary(variables) {
|
|
5139
|
+
return await this._clientSdk.GetPromptSummary(variables);
|
|
5140
|
+
}
|
|
5101
5141
|
};
|
|
5102
5142
|
|
|
5103
5143
|
// src/mcp/services/types.ts
|
|
@@ -5144,6 +5184,11 @@ async function getAuthenticatedGQLClient({
|
|
|
5144
5184
|
apiUrl,
|
|
5145
5185
|
webAppUrl
|
|
5146
5186
|
}) {
|
|
5187
|
+
debug7(
|
|
5188
|
+
"getAuthenticatedGQLClient called with: apiUrl=%s, webAppUrl=%s",
|
|
5189
|
+
apiUrl || "undefined",
|
|
5190
|
+
webAppUrl || "undefined"
|
|
5191
|
+
);
|
|
5147
5192
|
let gqlClient = new GQLClient({
|
|
5148
5193
|
apiKey: inputApiKey || configStore.get("apiToken") || "",
|
|
5149
5194
|
type: "apiKey",
|
|
@@ -5167,6 +5212,13 @@ async function handleMobbLogin({
|
|
|
5167
5212
|
}) {
|
|
5168
5213
|
const resolvedWebAppUrl = webAppUrl || WEB_APP_URL;
|
|
5169
5214
|
const resolvedApiUrl = apiUrl || API_URL;
|
|
5215
|
+
debug7(
|
|
5216
|
+
"handleMobbLogin: resolved URLs - apiUrl=%s (from param: %s), webAppUrl=%s (from param: %s)",
|
|
5217
|
+
resolvedApiUrl,
|
|
5218
|
+
apiUrl || "fallback",
|
|
5219
|
+
resolvedWebAppUrl,
|
|
5220
|
+
webAppUrl || "fallback"
|
|
5221
|
+
);
|
|
5170
5222
|
const { createSpinner } = Spinner({ ci: skipPrompts });
|
|
5171
5223
|
const isConnected = await inGqlClient.verifyApiConnection();
|
|
5172
5224
|
if (!isConnected) {
|
|
@@ -5447,6 +5499,22 @@ async function sanitizeDataWithCounts(obj) {
|
|
|
5447
5499
|
}
|
|
5448
5500
|
|
|
5449
5501
|
// src/args/commands/upload_ai_blame.ts
|
|
5502
|
+
var defaultLogger = {
|
|
5503
|
+
info: (msg, data) => {
|
|
5504
|
+
if (data !== void 0) {
|
|
5505
|
+
console.log(msg, data);
|
|
5506
|
+
} else {
|
|
5507
|
+
console.log(msg);
|
|
5508
|
+
}
|
|
5509
|
+
},
|
|
5510
|
+
error: (msg, data) => {
|
|
5511
|
+
if (data !== void 0) {
|
|
5512
|
+
console.error(msg, data);
|
|
5513
|
+
} else {
|
|
5514
|
+
console.error(msg);
|
|
5515
|
+
}
|
|
5516
|
+
}
|
|
5517
|
+
};
|
|
5450
5518
|
var PromptItemZ = z26.object({
|
|
5451
5519
|
type: z26.enum(["USER_PROMPT", "AI_RESPONSE", "TOOL_EXECUTION", "AI_THINKING"]),
|
|
5452
5520
|
attachedFiles: z26.array(
|
|
@@ -5562,7 +5630,12 @@ async function uploadAiBlameHandlerFromExtension(args) {
|
|
|
5562
5630
|
if (args.sessionId) {
|
|
5563
5631
|
uploadArgs.sessionId.push(args.sessionId);
|
|
5564
5632
|
}
|
|
5565
|
-
await uploadAiBlameHandler(
|
|
5633
|
+
await uploadAiBlameHandler({
|
|
5634
|
+
args: uploadArgs,
|
|
5635
|
+
exitOnError: false,
|
|
5636
|
+
apiUrl: args.apiUrl,
|
|
5637
|
+
webAppUrl: args.webAppUrl
|
|
5638
|
+
});
|
|
5566
5639
|
});
|
|
5567
5640
|
});
|
|
5568
5641
|
return {
|
|
@@ -5572,7 +5645,14 @@ async function uploadAiBlameHandlerFromExtension(args) {
|
|
|
5572
5645
|
inferenceUUID
|
|
5573
5646
|
};
|
|
5574
5647
|
}
|
|
5575
|
-
async function uploadAiBlameHandler(
|
|
5648
|
+
async function uploadAiBlameHandler(options) {
|
|
5649
|
+
const {
|
|
5650
|
+
args,
|
|
5651
|
+
exitOnError = true,
|
|
5652
|
+
apiUrl,
|
|
5653
|
+
webAppUrl,
|
|
5654
|
+
logger = defaultLogger
|
|
5655
|
+
} = options;
|
|
5576
5656
|
const prompts = args.prompt || [];
|
|
5577
5657
|
const inferences = args.inference || [];
|
|
5578
5658
|
const models = args.model || [];
|
|
@@ -5582,7 +5662,7 @@ async function uploadAiBlameHandler(args, exitOnError = true) {
|
|
|
5582
5662
|
const sessionIds = args.sessionId || args["session-id"] || [];
|
|
5583
5663
|
if (prompts.length !== inferences.length) {
|
|
5584
5664
|
const errorMsg = "prompt and inference must have the same number of entries";
|
|
5585
|
-
|
|
5665
|
+
logger.error(chalk3.red(errorMsg));
|
|
5586
5666
|
if (exitOnError) {
|
|
5587
5667
|
process.exit(1);
|
|
5588
5668
|
}
|
|
@@ -5601,7 +5681,7 @@ async function uploadAiBlameHandler(args, exitOnError = true) {
|
|
|
5601
5681
|
]);
|
|
5602
5682
|
} catch {
|
|
5603
5683
|
const errorMsg = `File not found for session ${i + 1}`;
|
|
5604
|
-
|
|
5684
|
+
logger.error(chalk3.red(errorMsg));
|
|
5605
5685
|
if (exitOnError) {
|
|
5606
5686
|
process.exit(1);
|
|
5607
5687
|
}
|
|
@@ -5620,7 +5700,9 @@ async function uploadAiBlameHandler(args, exitOnError = true) {
|
|
|
5620
5700
|
});
|
|
5621
5701
|
}
|
|
5622
5702
|
const authenticatedClient = await getAuthenticatedGQLClient({
|
|
5623
|
-
isSkipPrompts: true
|
|
5703
|
+
isSkipPrompts: true,
|
|
5704
|
+
apiUrl,
|
|
5705
|
+
webAppUrl
|
|
5624
5706
|
});
|
|
5625
5707
|
const initSessions = sessions.map(
|
|
5626
5708
|
({ sessionId: _sessionId, ...rest }) => rest
|
|
@@ -5631,7 +5713,7 @@ async function uploadAiBlameHandler(args, exitOnError = true) {
|
|
|
5631
5713
|
const uploadSessions = initRes.uploadAIBlameInferencesInit?.uploadSessions ?? [];
|
|
5632
5714
|
if (uploadSessions.length !== sessions.length) {
|
|
5633
5715
|
const errorMsg = "Init failed to return expected number of sessions";
|
|
5634
|
-
|
|
5716
|
+
logger.error(chalk3.red(errorMsg));
|
|
5635
5717
|
if (exitOnError) {
|
|
5636
5718
|
process.exit(1);
|
|
5637
5719
|
}
|
|
@@ -5673,22 +5755,41 @@ async function uploadAiBlameHandler(args, exitOnError = true) {
|
|
|
5673
5755
|
sessionId: s.sessionId
|
|
5674
5756
|
};
|
|
5675
5757
|
});
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5758
|
+
try {
|
|
5759
|
+
logger.info(
|
|
5760
|
+
`[UPLOAD] Calling finalizeAIBlameInferencesUploadRaw with ${finalizeSessions.length} sessions`
|
|
5761
|
+
);
|
|
5762
|
+
const finRes = await authenticatedClient.finalizeAIBlameInferencesUploadRaw(
|
|
5763
|
+
{
|
|
5764
|
+
sessions: finalizeSessions
|
|
5765
|
+
}
|
|
5766
|
+
);
|
|
5767
|
+
logger.info("[UPLOAD] Finalize response:", JSON.stringify(finRes, null, 2));
|
|
5768
|
+
const status = finRes?.finalizeAIBlameInferencesUpload?.status;
|
|
5769
|
+
if (status !== "OK") {
|
|
5770
|
+
const errorMsg = finRes?.finalizeAIBlameInferencesUpload?.error || "unknown error";
|
|
5771
|
+
logger.error(
|
|
5772
|
+
chalk3.red(
|
|
5773
|
+
`[UPLOAD] Finalize failed with status: ${status}, error: ${errorMsg}`
|
|
5774
|
+
)
|
|
5775
|
+
);
|
|
5776
|
+
if (exitOnError) {
|
|
5777
|
+
process.exit(1);
|
|
5778
|
+
}
|
|
5779
|
+
throw new Error(errorMsg);
|
|
5685
5780
|
}
|
|
5686
|
-
|
|
5781
|
+
logger.info(chalk3.green("[UPLOAD] AI Blame uploads finalized successfully"));
|
|
5782
|
+
} catch (error) {
|
|
5783
|
+
logger.error("[UPLOAD] Finalize threw error:", error);
|
|
5784
|
+
throw error;
|
|
5687
5785
|
}
|
|
5688
|
-
|
|
5786
|
+
}
|
|
5787
|
+
async function uploadAiBlameCommandHandler(args) {
|
|
5788
|
+
await uploadAiBlameHandler({ args });
|
|
5689
5789
|
}
|
|
5690
5790
|
export {
|
|
5691
5791
|
uploadAiBlameBuilder,
|
|
5792
|
+
uploadAiBlameCommandHandler,
|
|
5692
5793
|
uploadAiBlameHandler,
|
|
5693
5794
|
uploadAiBlameHandlerFromExtension
|
|
5694
5795
|
};
|
package/dist/index.mjs
CHANGED
|
@@ -723,12 +723,13 @@ import fs2 from "fs";
|
|
|
723
723
|
import ignore from "ignore";
|
|
724
724
|
import * as path2 from "path";
|
|
725
725
|
import { simpleGit } from "simple-git";
|
|
726
|
-
var GitService;
|
|
726
|
+
var MAX_COMMIT_DIFF_SIZE_BYTES, GitService;
|
|
727
727
|
var init_GitService = __esm({
|
|
728
728
|
"src/features/analysis/scm/services/GitService.ts"() {
|
|
729
729
|
"use strict";
|
|
730
730
|
init_configs();
|
|
731
731
|
init_FileUtils();
|
|
732
|
+
MAX_COMMIT_DIFF_SIZE_BYTES = 3 * 1024 * 1024;
|
|
732
733
|
GitService = class {
|
|
733
734
|
constructor(repositoryPath, log2) {
|
|
734
735
|
__publicField(this, "git");
|
|
@@ -1307,6 +1308,97 @@ ${rootContent}`;
|
|
|
1307
1308
|
throw new Error(errorMessage);
|
|
1308
1309
|
}
|
|
1309
1310
|
}
|
|
1311
|
+
/**
|
|
1312
|
+
* Gets timestamps for parent commits in a single git call.
|
|
1313
|
+
* @param parentShas Array of parent commit SHAs
|
|
1314
|
+
* @returns Array of parent commits with timestamps, or undefined if unavailable
|
|
1315
|
+
*/
|
|
1316
|
+
async getParentCommitTimestamps(parentShas) {
|
|
1317
|
+
if (parentShas.length === 0) {
|
|
1318
|
+
return void 0;
|
|
1319
|
+
}
|
|
1320
|
+
try {
|
|
1321
|
+
const output = await this.git.raw([
|
|
1322
|
+
"log",
|
|
1323
|
+
"--format=%H %cI",
|
|
1324
|
+
"--no-walk",
|
|
1325
|
+
...parentShas
|
|
1326
|
+
]);
|
|
1327
|
+
const parentCommits = output.trim().split("\n").filter(Boolean).map((line) => {
|
|
1328
|
+
const [sha, ts] = line.split(" ");
|
|
1329
|
+
return { sha: sha ?? "", timestamp: new Date(ts ?? "") };
|
|
1330
|
+
}).filter((p) => p.sha !== "");
|
|
1331
|
+
return parentCommits.length > 0 ? parentCommits : void 0;
|
|
1332
|
+
} catch {
|
|
1333
|
+
this.log("[GitService] Could not get parent commit timestamps", "debug", {
|
|
1334
|
+
parentShas
|
|
1335
|
+
});
|
|
1336
|
+
return void 0;
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
/**
|
|
1340
|
+
* Gets local commit data including diff, timestamp, and parent commits.
|
|
1341
|
+
* Used by Tracy extension to send commit data directly without requiring SCM token.
|
|
1342
|
+
* @param commitSha The commit SHA to get data for
|
|
1343
|
+
* @param maxDiffSizeBytes Maximum diff size in bytes (default 3MB). Returns null if exceeded.
|
|
1344
|
+
* @returns Commit data or null if unavailable/too large
|
|
1345
|
+
*/
|
|
1346
|
+
async getLocalCommitData(commitSha, maxDiffSizeBytes = MAX_COMMIT_DIFF_SIZE_BYTES) {
|
|
1347
|
+
this.log("[GitService] Getting local commit data", "debug", { commitSha });
|
|
1348
|
+
try {
|
|
1349
|
+
const DIFF_DELIMITER = "---MOBB_DIFF_START---";
|
|
1350
|
+
const output = await this.git.show([
|
|
1351
|
+
commitSha,
|
|
1352
|
+
`--format=%cI%n%P%n${DIFF_DELIMITER}`,
|
|
1353
|
+
"--patch"
|
|
1354
|
+
]);
|
|
1355
|
+
const delimiterIndex = output.indexOf(DIFF_DELIMITER);
|
|
1356
|
+
if (delimiterIndex === -1) {
|
|
1357
|
+
this.log("[GitService] Could not parse git show output", "warning", {
|
|
1358
|
+
commitSha
|
|
1359
|
+
});
|
|
1360
|
+
return null;
|
|
1361
|
+
}
|
|
1362
|
+
const metadataOutput = output.substring(0, delimiterIndex);
|
|
1363
|
+
const diff = output.substring(delimiterIndex + DIFF_DELIMITER.length + 1);
|
|
1364
|
+
const diffSizeBytes = Buffer.byteLength(diff, "utf8");
|
|
1365
|
+
if (diffSizeBytes > maxDiffSizeBytes) {
|
|
1366
|
+
this.log("[GitService] Commit diff exceeds size limit", "warning", {
|
|
1367
|
+
commitSha,
|
|
1368
|
+
diffSizeBytes,
|
|
1369
|
+
maxDiffSizeBytes
|
|
1370
|
+
});
|
|
1371
|
+
return null;
|
|
1372
|
+
}
|
|
1373
|
+
const metadataLines = metadataOutput.trim().split("\n");
|
|
1374
|
+
if (metadataLines.length < 1 || !metadataLines[0]) {
|
|
1375
|
+
this.log("[GitService] Unexpected metadata format", "warning", {
|
|
1376
|
+
commitSha,
|
|
1377
|
+
metadataLines
|
|
1378
|
+
});
|
|
1379
|
+
return null;
|
|
1380
|
+
}
|
|
1381
|
+
const timestampStr = metadataLines[0];
|
|
1382
|
+
const timestamp = new Date(timestampStr);
|
|
1383
|
+
const parentShas = (metadataLines[1] ?? "").trim().split(/\s+/).filter(Boolean);
|
|
1384
|
+
const parentCommits = await this.getParentCommitTimestamps(parentShas);
|
|
1385
|
+
this.log("[GitService] Local commit data retrieved", "debug", {
|
|
1386
|
+
commitSha,
|
|
1387
|
+
diffSizeBytes,
|
|
1388
|
+
timestamp: timestamp.toISOString(),
|
|
1389
|
+
parentCommitCount: parentCommits?.length ?? 0
|
|
1390
|
+
});
|
|
1391
|
+
return {
|
|
1392
|
+
diff,
|
|
1393
|
+
timestamp,
|
|
1394
|
+
parentCommits
|
|
1395
|
+
};
|
|
1396
|
+
} catch (error) {
|
|
1397
|
+
const errorMessage = `Failed to get local commit data: ${error.message}`;
|
|
1398
|
+
this.log(`[GitService] ${errorMessage}`, "debug", { error, commitSha });
|
|
1399
|
+
return null;
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1310
1402
|
};
|
|
1311
1403
|
}
|
|
1312
1404
|
});
|
|
@@ -2070,11 +2162,14 @@ var UploadS3BucketInfoDocument = `
|
|
|
2070
2162
|
}
|
|
2071
2163
|
`;
|
|
2072
2164
|
var AnalyzeCommitForExtensionAiBlameDocument = `
|
|
2073
|
-
mutation AnalyzeCommitForExtensionAIBlame($repositoryURL: String!, $commitSha: String!, $organizationId: String!) {
|
|
2165
|
+
mutation AnalyzeCommitForExtensionAIBlame($repositoryURL: String!, $commitSha: String!, $organizationId: String!, $commitDiff: String, $commitTimestamp: Timestamp, $parentCommits: [ParentCommitInput!]) {
|
|
2074
2166
|
analyzeCommitForAIBlame(
|
|
2075
2167
|
repositoryURL: $repositoryURL
|
|
2076
2168
|
commitSha: $commitSha
|
|
2077
2169
|
organizationId: $organizationId
|
|
2170
|
+
commitDiff: $commitDiff
|
|
2171
|
+
commitTimestamp: $commitTimestamp
|
|
2172
|
+
parentCommits: $parentCommits
|
|
2078
2173
|
) {
|
|
2079
2174
|
__typename
|
|
2080
2175
|
... on ProcessAIBlameFinalResult {
|
|
@@ -2120,6 +2215,30 @@ var GetAiBlameAttributionPromptDocument = `
|
|
|
2120
2215
|
}
|
|
2121
2216
|
}
|
|
2122
2217
|
`;
|
|
2218
|
+
var GetPromptSummaryDocument = `
|
|
2219
|
+
query GetPromptSummary($aiBlameAttributionId: String!) {
|
|
2220
|
+
getPromptSummary(aiBlameAttributionId: $aiBlameAttributionId) {
|
|
2221
|
+
__typename
|
|
2222
|
+
... on PromptSummarySuccess {
|
|
2223
|
+
status
|
|
2224
|
+
summary {
|
|
2225
|
+
goal
|
|
2226
|
+
developersPlan
|
|
2227
|
+
aiImplementationDetails
|
|
2228
|
+
importantInstructionsAndPushbacks
|
|
2229
|
+
frictionScore {
|
|
2230
|
+
score
|
|
2231
|
+
justification
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
... on PromptSummaryError {
|
|
2236
|
+
status
|
|
2237
|
+
error
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
`;
|
|
2123
2242
|
var UploadAiBlameInferencesInitDocument = `
|
|
2124
2243
|
mutation UploadAIBlameInferencesInit($sessions: [AIBlameInferenceInitInput!]!) {
|
|
2125
2244
|
uploadAIBlameInferencesInit(sessions: $sessions) {
|
|
@@ -2447,6 +2566,9 @@ function getSdk(client, withWrapper = defaultWrapper) {
|
|
|
2447
2566
|
GetAIBlameAttributionPrompt(variables, requestHeaders, signal) {
|
|
2448
2567
|
return withWrapper((wrappedRequestHeaders) => client.request({ document: GetAiBlameAttributionPromptDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "GetAIBlameAttributionPrompt", "query", variables);
|
|
2449
2568
|
},
|
|
2569
|
+
GetPromptSummary(variables, requestHeaders, signal) {
|
|
2570
|
+
return withWrapper((wrappedRequestHeaders) => client.request({ document: GetPromptSummaryDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "GetPromptSummary", "query", variables);
|
|
2571
|
+
},
|
|
2450
2572
|
UploadAIBlameInferencesInit(variables, requestHeaders, signal) {
|
|
2451
2573
|
return withWrapper((wrappedRequestHeaders) => client.request({ document: UploadAiBlameInferencesInitDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "UploadAIBlameInferencesInit", "mutation", variables);
|
|
2452
2574
|
},
|
|
@@ -11034,6 +11156,11 @@ var GQLClient = class {
|
|
|
11034
11156
|
debug6(`init with ${args}`);
|
|
11035
11157
|
this._auth = args;
|
|
11036
11158
|
this._apiUrl = args.apiUrl || API_URL;
|
|
11159
|
+
debug6(
|
|
11160
|
+
"GQLClient constructor: resolved apiUrl=%s (from param: %s)",
|
|
11161
|
+
this._apiUrl,
|
|
11162
|
+
args.apiUrl || "fallback to API_URL constant"
|
|
11163
|
+
);
|
|
11037
11164
|
this._client = new GraphQLClient(this._apiUrl, {
|
|
11038
11165
|
headers: args.type === "apiKey" ? { [API_KEY_HEADER_NAME]: args.apiKey || "" } : {
|
|
11039
11166
|
Authorization: `Bearer ${args.token}`
|
|
@@ -11398,6 +11525,9 @@ var GQLClient = class {
|
|
|
11398
11525
|
async getAIBlameAttributionPrompt(variables) {
|
|
11399
11526
|
return await this._clientSdk.GetAIBlameAttributionPrompt(variables);
|
|
11400
11527
|
}
|
|
11528
|
+
async getAIBlameAttributionPromptSummary(variables) {
|
|
11529
|
+
return await this._clientSdk.GetPromptSummary(variables);
|
|
11530
|
+
}
|
|
11401
11531
|
};
|
|
11402
11532
|
|
|
11403
11533
|
// src/mcp/services/types.ts
|
|
@@ -11464,6 +11594,11 @@ async function getAuthenticatedGQLClient({
|
|
|
11464
11594
|
apiUrl,
|
|
11465
11595
|
webAppUrl
|
|
11466
11596
|
}) {
|
|
11597
|
+
debug7(
|
|
11598
|
+
"getAuthenticatedGQLClient called with: apiUrl=%s, webAppUrl=%s",
|
|
11599
|
+
apiUrl || "undefined",
|
|
11600
|
+
webAppUrl || "undefined"
|
|
11601
|
+
);
|
|
11467
11602
|
let gqlClient = new GQLClient({
|
|
11468
11603
|
apiKey: inputApiKey || configStore.get("apiToken") || "",
|
|
11469
11604
|
type: "apiKey",
|
|
@@ -11487,6 +11622,13 @@ async function handleMobbLogin({
|
|
|
11487
11622
|
}) {
|
|
11488
11623
|
const resolvedWebAppUrl = webAppUrl || WEB_APP_URL;
|
|
11489
11624
|
const resolvedApiUrl = apiUrl || API_URL;
|
|
11625
|
+
debug7(
|
|
11626
|
+
"handleMobbLogin: resolved URLs - apiUrl=%s (from param: %s), webAppUrl=%s (from param: %s)",
|
|
11627
|
+
resolvedApiUrl,
|
|
11628
|
+
apiUrl || "fallback",
|
|
11629
|
+
resolvedWebAppUrl,
|
|
11630
|
+
webAppUrl || "fallback"
|
|
11631
|
+
);
|
|
11490
11632
|
const { createSpinner: createSpinner5 } = Spinner({ ci: skipPrompts });
|
|
11491
11633
|
const isConnected = await inGqlClient.verifyApiConnection();
|
|
11492
11634
|
if (!isConnected) {
|
|
@@ -13801,6 +13943,22 @@ async function sanitizeDataWithCounts(obj) {
|
|
|
13801
13943
|
}
|
|
13802
13944
|
|
|
13803
13945
|
// src/args/commands/upload_ai_blame.ts
|
|
13946
|
+
var defaultLogger2 = {
|
|
13947
|
+
info: (msg, data) => {
|
|
13948
|
+
if (data !== void 0) {
|
|
13949
|
+
console.log(msg, data);
|
|
13950
|
+
} else {
|
|
13951
|
+
console.log(msg);
|
|
13952
|
+
}
|
|
13953
|
+
},
|
|
13954
|
+
error: (msg, data) => {
|
|
13955
|
+
if (data !== void 0) {
|
|
13956
|
+
console.error(msg, data);
|
|
13957
|
+
} else {
|
|
13958
|
+
console.error(msg);
|
|
13959
|
+
}
|
|
13960
|
+
}
|
|
13961
|
+
};
|
|
13804
13962
|
var PromptItemZ = z31.object({
|
|
13805
13963
|
type: z31.enum(["USER_PROMPT", "AI_RESPONSE", "TOOL_EXECUTION", "AI_THINKING"]),
|
|
13806
13964
|
attachedFiles: z31.array(
|
|
@@ -13916,7 +14074,12 @@ async function uploadAiBlameHandlerFromExtension(args) {
|
|
|
13916
14074
|
if (args.sessionId) {
|
|
13917
14075
|
uploadArgs.sessionId.push(args.sessionId);
|
|
13918
14076
|
}
|
|
13919
|
-
await uploadAiBlameHandler(
|
|
14077
|
+
await uploadAiBlameHandler({
|
|
14078
|
+
args: uploadArgs,
|
|
14079
|
+
exitOnError: false,
|
|
14080
|
+
apiUrl: args.apiUrl,
|
|
14081
|
+
webAppUrl: args.webAppUrl
|
|
14082
|
+
});
|
|
13920
14083
|
});
|
|
13921
14084
|
});
|
|
13922
14085
|
return {
|
|
@@ -13926,7 +14089,14 @@ async function uploadAiBlameHandlerFromExtension(args) {
|
|
|
13926
14089
|
inferenceUUID
|
|
13927
14090
|
};
|
|
13928
14091
|
}
|
|
13929
|
-
async function uploadAiBlameHandler(
|
|
14092
|
+
async function uploadAiBlameHandler(options) {
|
|
14093
|
+
const {
|
|
14094
|
+
args,
|
|
14095
|
+
exitOnError = true,
|
|
14096
|
+
apiUrl,
|
|
14097
|
+
webAppUrl,
|
|
14098
|
+
logger: logger2 = defaultLogger2
|
|
14099
|
+
} = options;
|
|
13930
14100
|
const prompts = args.prompt || [];
|
|
13931
14101
|
const inferences = args.inference || [];
|
|
13932
14102
|
const models = args.model || [];
|
|
@@ -13936,7 +14106,7 @@ async function uploadAiBlameHandler(args, exitOnError = true) {
|
|
|
13936
14106
|
const sessionIds = args.sessionId || args["session-id"] || [];
|
|
13937
14107
|
if (prompts.length !== inferences.length) {
|
|
13938
14108
|
const errorMsg = "prompt and inference must have the same number of entries";
|
|
13939
|
-
|
|
14109
|
+
logger2.error(chalk9.red(errorMsg));
|
|
13940
14110
|
if (exitOnError) {
|
|
13941
14111
|
process.exit(1);
|
|
13942
14112
|
}
|
|
@@ -13955,7 +14125,7 @@ async function uploadAiBlameHandler(args, exitOnError = true) {
|
|
|
13955
14125
|
]);
|
|
13956
14126
|
} catch {
|
|
13957
14127
|
const errorMsg = `File not found for session ${i + 1}`;
|
|
13958
|
-
|
|
14128
|
+
logger2.error(chalk9.red(errorMsg));
|
|
13959
14129
|
if (exitOnError) {
|
|
13960
14130
|
process.exit(1);
|
|
13961
14131
|
}
|
|
@@ -13974,7 +14144,9 @@ async function uploadAiBlameHandler(args, exitOnError = true) {
|
|
|
13974
14144
|
});
|
|
13975
14145
|
}
|
|
13976
14146
|
const authenticatedClient = await getAuthenticatedGQLClient({
|
|
13977
|
-
isSkipPrompts: true
|
|
14147
|
+
isSkipPrompts: true,
|
|
14148
|
+
apiUrl,
|
|
14149
|
+
webAppUrl
|
|
13978
14150
|
});
|
|
13979
14151
|
const initSessions = sessions.map(
|
|
13980
14152
|
({ sessionId: _sessionId, ...rest }) => rest
|
|
@@ -13985,7 +14157,7 @@ async function uploadAiBlameHandler(args, exitOnError = true) {
|
|
|
13985
14157
|
const uploadSessions = initRes.uploadAIBlameInferencesInit?.uploadSessions ?? [];
|
|
13986
14158
|
if (uploadSessions.length !== sessions.length) {
|
|
13987
14159
|
const errorMsg = "Init failed to return expected number of sessions";
|
|
13988
|
-
|
|
14160
|
+
logger2.error(chalk9.red(errorMsg));
|
|
13989
14161
|
if (exitOnError) {
|
|
13990
14162
|
process.exit(1);
|
|
13991
14163
|
}
|
|
@@ -14027,19 +14199,37 @@ async function uploadAiBlameHandler(args, exitOnError = true) {
|
|
|
14027
14199
|
sessionId: s.sessionId
|
|
14028
14200
|
};
|
|
14029
14201
|
});
|
|
14030
|
-
|
|
14031
|
-
|
|
14032
|
-
|
|
14033
|
-
|
|
14034
|
-
|
|
14035
|
-
|
|
14036
|
-
|
|
14037
|
-
|
|
14038
|
-
|
|
14202
|
+
try {
|
|
14203
|
+
logger2.info(
|
|
14204
|
+
`[UPLOAD] Calling finalizeAIBlameInferencesUploadRaw with ${finalizeSessions.length} sessions`
|
|
14205
|
+
);
|
|
14206
|
+
const finRes = await authenticatedClient.finalizeAIBlameInferencesUploadRaw(
|
|
14207
|
+
{
|
|
14208
|
+
sessions: finalizeSessions
|
|
14209
|
+
}
|
|
14210
|
+
);
|
|
14211
|
+
logger2.info("[UPLOAD] Finalize response:", JSON.stringify(finRes, null, 2));
|
|
14212
|
+
const status = finRes?.finalizeAIBlameInferencesUpload?.status;
|
|
14213
|
+
if (status !== "OK") {
|
|
14214
|
+
const errorMsg = finRes?.finalizeAIBlameInferencesUpload?.error || "unknown error";
|
|
14215
|
+
logger2.error(
|
|
14216
|
+
chalk9.red(
|
|
14217
|
+
`[UPLOAD] Finalize failed with status: ${status}, error: ${errorMsg}`
|
|
14218
|
+
)
|
|
14219
|
+
);
|
|
14220
|
+
if (exitOnError) {
|
|
14221
|
+
process.exit(1);
|
|
14222
|
+
}
|
|
14223
|
+
throw new Error(errorMsg);
|
|
14039
14224
|
}
|
|
14040
|
-
|
|
14225
|
+
logger2.info(chalk9.green("[UPLOAD] AI Blame uploads finalized successfully"));
|
|
14226
|
+
} catch (error) {
|
|
14227
|
+
logger2.error("[UPLOAD] Finalize threw error:", error);
|
|
14228
|
+
throw error;
|
|
14041
14229
|
}
|
|
14042
|
-
|
|
14230
|
+
}
|
|
14231
|
+
async function uploadAiBlameCommandHandler(args) {
|
|
14232
|
+
await uploadAiBlameHandler({ args });
|
|
14043
14233
|
}
|
|
14044
14234
|
|
|
14045
14235
|
// src/features/claude_code/transcript_parser.ts
|
|
@@ -22792,7 +22982,7 @@ var parseArgs = async (args) => {
|
|
|
22792
22982
|
"Upload AI Blame inference artifacts (prompt + inference) and finalize them."
|
|
22793
22983
|
),
|
|
22794
22984
|
uploadAiBlameBuilder,
|
|
22795
|
-
|
|
22985
|
+
uploadAiBlameCommandHandler
|
|
22796
22986
|
).command(
|
|
22797
22987
|
mobbCliCommand.claudeCodeInstallHook,
|
|
22798
22988
|
chalk12.bold("Install Claude Code hooks for data collection."),
|