@juspay/yama 2.4.2 → 2.5.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.
@@ -0,0 +1,389 @@
1
+ /**
2
+ * ProviderToolset — the single source of truth for everything that differs
3
+ * between VCS providers (Bitbucket vs GitHub) when Yama drives an AI review.
4
+ *
5
+ * Every provider-specific string the AI sees (the <available_tools> section,
6
+ * the numbered review workflow, the PR-context identifier block, the
7
+ * description-enhancement steps) and every provider-specific signal Yama reads
8
+ * back (which tool call is a comment, a diff fetch, a repo mutation, or an
9
+ * approve/block decision) lives behind this interface.
10
+ *
11
+ * The Bitbucket toolset reproduces the CURRENT prompt wording verbatim, so
12
+ * behaviour for Bitbucket stays byte-identical. The GitHub toolset teaches the
13
+ * official github/github-mcp-server consolidated API and pending-review flow,
14
+ * keeping the same review philosophy and severity rules.
15
+ */
16
+ /**
17
+ * Escape XML special characters. Mirrors PromptBuilder.escapeXML so the
18
+ * identifier blocks emitted here are byte-identical to the current prompt.
19
+ */
20
+ function escapeXML(text) {
21
+ return text
22
+ .replace(/&/g, "&amp;")
23
+ .replace(/</g, "&lt;")
24
+ .replace(/>/g, "&gt;")
25
+ .replace(/"/g, "&quot;")
26
+ .replace(/'/g, "&apos;");
27
+ }
28
+ // ============================================================================
29
+ // Bitbucket toolset — reproduces the CURRENT prompt text VERBATIM
30
+ // ============================================================================
31
+ class BitbucketToolset {
32
+ provider = "bitbucket";
33
+ // get_pull_request reads PR metadata and existing comments today.
34
+ prReadToolName = "get_pull_request";
35
+ // add_comment is the inline/PR comment tool today.
36
+ commentToolNames = ["add_comment"];
37
+ // get_pull_request_diff is the per-file diff retrieval tool today.
38
+ diffToolNames = ["get_pull_request_diff"];
39
+ // Plain Bitbucket mutation names currently blocked in
40
+ // ContextExplorerService.shouldExcludeTool. Copied verbatim from the regex
41
+ // alternation so explore_context cannot mutate PR/repo state.
42
+ mutationToolNames = [
43
+ "add_comment",
44
+ "set_pr_approval",
45
+ "set_review_status",
46
+ "approve_pull_request",
47
+ "unapprove_pull_request",
48
+ "request_changes",
49
+ "remove_requested_changes",
50
+ "update_pull_request",
51
+ "merge_pull_request",
52
+ "delete_branch",
53
+ ];
54
+ /**
55
+ * <pr_context> identifier block — mirrors the <workspace>/<repository>/
56
+ * <pull_request_id>/<branch> fields PromptBuilder emits today (~lines
57
+ * 119-122), with the same escaping and the same "find-by-branch" fallback.
58
+ */
59
+ identifierXml(params) {
60
+ const workspace = escapeXML((params.workspace || "").trim());
61
+ const repository = escapeXML((params.repository || "").trim());
62
+ const pullRequestId = params.pullRequestId || "find-by-branch";
63
+ const branch = escapeXML((params.branch || "N/A").trim());
64
+ return ` <workspace>${workspace}</workspace>
65
+ <repository>${repository}</repository>
66
+ <pull_request_id>${pullRequestId}</pull_request_id>
67
+ <branch>${branch}</branch>`;
68
+ }
69
+ /**
70
+ * The <available_tools>/<tool-usage> section. Reproduced verbatim from
71
+ * ReviewSystemPrompt.ts (the <tool-usage> block, lines 28-69).
72
+ */
73
+ systemPromptToolsSection() {
74
+ return ` <tool-usage>
75
+ <tool name="get_pull_request">
76
+ <use-when>Once at the start, to read PR metadata and existing comments.</use-when>
77
+ </tool>
78
+
79
+ <tool name="get_pull_request_diff">
80
+ <use-when>For ONE file at a time, immediately before reviewing it.</use-when>
81
+ <do-not-use-when>Never call this without a file_path argument. Never request the full PR diff.</do-not-use-when>
82
+ </tool>
83
+
84
+ <tool name="search_code">
85
+ <use-when>A single direct lookup answers your question (function definition, single file).</use-when>
86
+ <do-not-use-when>The investigation needs more than one call or spans multiple files — delegate to explore_context instead.</do-not-use-when>
87
+ </tool>
88
+
89
+ <tool name="get_file_content">
90
+ <use-when>You already know the path and need the file's contents.</use-when>
91
+ </tool>
92
+
93
+ <!-- EXPLORE_BEGIN -->
94
+ <tool name="explore_context">
95
+ <use-when>Multi-step research, multi-file tracing, history lookup, ambiguous behavior, or anything that would otherwise need 3+ tool calls in the main loop.</use-when>
96
+ <do-not-use-when>A single search_code or get_file_content would answer it. Delegating cheap lookups wastes a turn.</do-not-use-when>
97
+ <how>Pass a one-sentence research question as task and optional file paths/PR refs as focus. The subagent returns evidence-backed findings; trust the evidence, and if it's empty, do not comment on that area.</how>
98
+ <example positive>Diff adds a retry guard in PaymentProcessor → explore_context(task="Is this retry guard consistent with how other payment handlers retry, and does it match the convention from PR 842?", focus=["src/payments/", "PR 842"])</example>
99
+ <example negative>Don't: explore_context(task="What does validatePayment do?"). Do: search_code(search_query="function validatePayment").</example>
100
+ </tool>
101
+ <!-- EXPLORE_END -->
102
+
103
+ <tool name="add_comment">
104
+ <fields>file_path, line_number, line_type (ADDED|REMOVED|CONTEXT), comment_text, and suggestion (required for CRITICAL and MAJOR — must be real, executable code).</fields>
105
+ <do-not-use-when>You only have a code_snippet but no line_number/line_type from the diff JSON.</do-not-use-when>
106
+ </tool>
107
+
108
+ <tool name="set_pr_approval">
109
+ <use-when>No blocking issues found. Pass approved=true.</use-when>
110
+ </tool>
111
+
112
+ <tool name="set_review_status">
113
+ <use-when>Blocking criteria met. Pass request_changes=true.</use-when>
114
+ </tool>
115
+ </tool-usage>`;
116
+ }
117
+ /**
118
+ * Numbered review workflow. Reproduced verbatim from
119
+ * PromptBuilder.buildReviewWorkflow (the <instructions> body, lines 142-178),
120
+ * minus the dynamic modeLine/additional tail which the caller still appends.
121
+ */
122
+ reviewWorkflowInstructions(_params) {
123
+ return ` Begin your autonomous review. Follow this order.
124
+
125
+ STEP 1 — Read project standards
126
+ Read the <project-standards> block above carefully. Treat any reviewer-expectation
127
+ entry with severity=BLOCKING as a blocking criterion for this PR. If the block is
128
+ missing or empty, fall back to <focus-areas> and <blocking-criteria>.
129
+
130
+ STEP 2 — Read the PR shell
131
+ Call get_pull_request once to get changed files, branch info, and existing comments.
132
+ Build a mental map of which files exist and which already have comments.
133
+ Do NOT request the full PR diff.
134
+
135
+ STEP 3 — Walk files one at a time
136
+ For each changed file, in order:
137
+ a. Call get_pull_request_diff(file_path=&lt;this file&gt;).
138
+ b. Cross-check the diff against project-standards and existing comments on this file.
139
+ c. If anything is non-trivial — multi-file impact, unfamiliar pattern, unclear intent,
140
+ history-dependent behavior — <!-- EXPLORE_BEGIN -->call explore_context with a precise
141
+ task and wait for its evidence before commenting<!-- EXPLORE_END --><!-- EXPLORE_DISABLED_BEGIN -->use search_code or get_file_content to verify before commenting<!-- EXPLORE_DISABLED_END -->.
142
+ d. For every confirmed issue, call add_comment immediately with line_number and
143
+ line_type from the diff JSON. Include a real-code suggestion for CRITICAL/MAJOR.
144
+ e. Move to the next file. Never request another file's diff before finishing the
145
+ current one. Never request a multi-file diff.
146
+
147
+ STEP 4 — Decision
148
+ After the last file, count issues by severity, apply <blocking-criteria>, and call
149
+ set_pr_approval(approved=true) OR set_review_status(request_changes=true).
150
+
151
+ STEP 5 — Summary comment
152
+ Post one summary comment with file count, issue counts by severity, and next steps.
153
+
154
+ Budget guidance: roughly 10 tool calls per file in the main loop. If you exceed
155
+ that on a single file, <!-- EXPLORE_BEGIN -->delegate the rest to explore_context<!-- EXPLORE_END --><!-- EXPLORE_DISABLED_BEGIN -->stop investigating<!-- EXPLORE_DISABLED_END --> and move on.`;
156
+ }
157
+ /**
158
+ * Description-enhancement workflow. Reproduced verbatim from
159
+ * PromptBuilder.buildDescriptionEnhancementInstructions (the <instructions>
160
+ * body, lines 421-437), minus the dynamic mode/additional tail.
161
+ */
162
+ descriptionEnhancementInstructions(_params) {
163
+ return ` Enhance the PR description now.
164
+
165
+ 1. Call get_pull_request() to read current PR and description
166
+ 2. Call get_pull_request_diff() to analyze code changes
167
+ 3. Use search_code() to find configuration patterns, API changes
168
+ 4. Extract information for each required section
169
+ 5. Build enhanced description following section structure
170
+ 6. Call update_pull_request() with enhanced description
171
+
172
+ CRITICAL: Return ONLY the enhanced description markdown.
173
+ Do NOT include meta-commentary or explanations.
174
+ Start directly with section content.`;
175
+ }
176
+ /**
177
+ * Replicates the orchestrator's extractDecision logic
178
+ * (YamaV2Orchestrator.extractDecision, ~lines 1132-1163) for a single call.
179
+ * set_review_status(request_changes=true) -> BLOCKED;
180
+ * set_pr_approval(approved=true) -> APPROVED. Legacy names handled too.
181
+ */
182
+ interpretDecision(call) {
183
+ const name = call.toolName;
184
+ const args = call.args || {};
185
+ if (name === "set_review_status") {
186
+ if (typeof args.request_changes === "boolean") {
187
+ return args.request_changes ? "BLOCKED" : null;
188
+ }
189
+ return null;
190
+ }
191
+ if (name === "request_changes") {
192
+ return "BLOCKED";
193
+ }
194
+ if (name === "remove_requested_changes") {
195
+ return null;
196
+ }
197
+ if (name === "set_pr_approval") {
198
+ if (typeof args.approved === "boolean") {
199
+ return args.approved ? "APPROVED" : null;
200
+ }
201
+ return null;
202
+ }
203
+ if (name === "approve_pull_request") {
204
+ return "APPROVED";
205
+ }
206
+ if (name === "unapprove_pull_request") {
207
+ return null;
208
+ }
209
+ return null;
210
+ }
211
+ }
212
+ // ============================================================================
213
+ // GitHub toolset — teaches the github/github-mcp-server consolidated API
214
+ // ============================================================================
215
+ class GitHubToolset {
216
+ provider = "github";
217
+ // pull_request_read (method="get") reads PR metadata and existing comments.
218
+ prReadToolName = "pull_request_read";
219
+ // Inline comments go through the pending-review batch; non-line PR comments
220
+ // through add_issue_comment.
221
+ commentToolNames = [
222
+ "add_comment_to_pending_review",
223
+ "add_issue_comment",
224
+ ];
225
+ // pull_request_read serves PR meta, unified diff, and changed files.
226
+ diffToolNames = ["pull_request_read"];
227
+ // Tools that mutate PR/repo state — review-write, comment-write, PR update,
228
+ // and the raw repo-mutation tools. Blocked for explore_context.
229
+ mutationToolNames = [
230
+ "pull_request_review_write",
231
+ "add_comment_to_pending_review",
232
+ "add_issue_comment",
233
+ "update_pull_request",
234
+ "push_files",
235
+ "create_or_update_file",
236
+ "create_branch",
237
+ "delete_file",
238
+ "create_pull_request_with_copilot",
239
+ "assign_copilot_to_issue",
240
+ ];
241
+ /**
242
+ * <pr_context> identifier block for GitHub: owner / repo / pull_number.
243
+ */
244
+ identifierXml(params) {
245
+ const owner = escapeXML((params.owner || "").trim());
246
+ const repo = escapeXML((params.repo || "").trim());
247
+ const pullNumber = params.prNumber !== undefined ? params.prNumber : "find-by-branch";
248
+ return ` <owner>${owner}</owner>
249
+ <repo>${repo}</repo>
250
+ <pull_number>${pullNumber}</pull_number>`;
251
+ }
252
+ /**
253
+ * <tool-usage> section for the GitHub consolidated MCP API.
254
+ */
255
+ systemPromptToolsSection() {
256
+ return ` <tool-usage>
257
+ <tool name="pull_request_read">
258
+ <use-when>Read PR state. method="get" once at the start for PR metadata and existing comments; method="get_files" to list changed files; method="get_diff" for the unified diff. Always pass owner, repo, pullNumber.</use-when>
259
+ </tool>
260
+
261
+ <tool name="search_code">
262
+ <use-when>A single direct lookup answers your question (function definition, single file). Pass a query.</use-when>
263
+ <do-not-use-when>The investigation needs more than one call or spans multiple files — delegate to explore_context instead.</do-not-use-when>
264
+ </tool>
265
+
266
+ <tool name="get_file_contents">
267
+ <use-when>You already know the path and need the file's contents. Pass owner, repo, path, and optionally ref.</use-when>
268
+ </tool>
269
+
270
+ <!-- EXPLORE_BEGIN -->
271
+ <tool name="explore_context">
272
+ <use-when>Multi-step research, multi-file tracing, history lookup, ambiguous behavior, or anything that would otherwise need 3+ tool calls in the main loop.</use-when>
273
+ <do-not-use-when>A single search_code or get_file_contents would answer it. Delegating cheap lookups wastes a turn.</do-not-use-when>
274
+ <how>Pass a one-sentence research question as task and optional file paths/PR refs as focus. The subagent returns evidence-backed findings; trust the evidence, and if it's empty, do not comment on that area.</how>
275
+ <example positive>Diff adds a retry guard in PaymentProcessor → explore_context(task="Is this retry guard consistent with how other payment handlers retry, and does it match the convention from PR 842?", focus=["src/payments/", "PR 842"])</example>
276
+ <example negative>Don't: explore_context(task="What does validatePayment do?"). Do: search_code(query="function validatePayment").</example>
277
+ </tool>
278
+ <!-- EXPLORE_END -->
279
+
280
+ <tool name="pull_request_review_write">
281
+ <use-when>Open and close ONE pending review per PR. method="create" (owner, repo, pullNumber) opens the pending review BEFORE posting any inline comment. method="submit" (owner, repo, pullNumber, event, body) closes it: event="APPROVE" when clean, event="REQUEST_CHANGES" when blocking, event="COMMENT" otherwise. method="delete" discards an unsubmitted pending review.</use-when>
282
+ <do-not-use-when>Never submit before all inline comments are added. Never open more than one pending review per PR.</do-not-use-when>
283
+ </tool>
284
+
285
+ <tool name="add_comment_to_pending_review">
286
+ <fields>owner, repo, pullNumber, path, body, subjectType ("LINE"|"FILE"), and line (with optional side, startLine, startSide) for LINE comments. Each confirmed issue is one call. Include a real, executable code suggestion in body for CRITICAL and MAJOR.</fields>
287
+ <do-not-use-when>No pending review has been created yet, or you have no path/line for a LINE comment.</do-not-use-when>
288
+ </tool>
289
+
290
+ <tool name="add_issue_comment">
291
+ <fields>owner, repo, issue_number, body. Use for the general (non-line) PR summary comment.</fields>
292
+ </tool>
293
+ </tool-usage>`;
294
+ }
295
+ /**
296
+ * Numbered review workflow mirroring the Bitbucket steps but using the
297
+ * GitHub pending-review batch model. Same review philosophy and severity
298
+ * rules — only the tool calls differ.
299
+ */
300
+ reviewWorkflowInstructions(_params) {
301
+ return ` Begin your autonomous review. Follow this order.
302
+
303
+ STEP 1 — Read project standards
304
+ Read the <project-standards> block above carefully. Treat any reviewer-expectation
305
+ entry with severity=BLOCKING as a blocking criterion for this PR. If the block is
306
+ missing or empty, fall back to <focus-areas> and <blocking-criteria>.
307
+
308
+ STEP 2 — Read the PR shell
309
+ Call pull_request_read(method="get", owner, repo, pullNumber) once to get PR
310
+ metadata and existing comments, then pull_request_read(method="get_files", ...)
311
+ to list the changed files. Build a mental map of which files exist and which
312
+ already have comments. Do NOT request the full PR diff yet.
313
+
314
+ STEP 3 — Open ONE pending review
315
+ Call pull_request_review_write(method="create", owner, repo, pullNumber) to open a
316
+ single pending review. Every inline comment in STEP 4 attaches to this review.
317
+
318
+ STEP 4 — Walk files one at a time
319
+ For each changed file, in order:
320
+ a. Call pull_request_read(method="get_diff", owner, repo, pullNumber) and work the
321
+ hunks for &lt;this file&gt; only. Finish this file before touching another.
322
+ b. Cross-check the diff against project-standards and existing comments on this file.
323
+ c. If anything is non-trivial — multi-file impact, unfamiliar pattern, unclear intent,
324
+ history-dependent behavior — <!-- EXPLORE_BEGIN -->call explore_context with a precise
325
+ task and wait for its evidence before commenting<!-- EXPLORE_END --><!-- EXPLORE_DISABLED_BEGIN -->use search_code or get_file_contents to verify before commenting<!-- EXPLORE_DISABLED_END -->.
326
+ d. For every confirmed issue, call add_comment_to_pending_review immediately with
327
+ path, line, subjectType="LINE", and body taken from the diff. Include a real-code
328
+ suggestion in body for CRITICAL/MAJOR.
329
+ e. Move to the next file. Never jump between files mid-review.
330
+
331
+ STEP 5 — Decision (submit the review)
332
+ After the last file, count issues by severity, apply <blocking-criteria>, and call
333
+ pull_request_review_write(method="submit", owner, repo, pullNumber, body=&lt;summary&gt;)
334
+ with event="APPROVE" when clean OR event="REQUEST_CHANGES" when blocking criteria are met.
335
+
336
+ STEP 6 — Summary comment
337
+ Use the submit body above for the summary (file count, issue counts by severity, next
338
+ steps). For an extra non-line note, call add_issue_comment(owner, repo, issue_number, body).
339
+
340
+ Budget guidance: roughly 10 tool calls per file in the main loop. If you exceed
341
+ that on a single file, <!-- EXPLORE_BEGIN -->delegate the rest to explore_context<!-- EXPLORE_END --><!-- EXPLORE_DISABLED_BEGIN -->stop investigating<!-- EXPLORE_DISABLED_END --> and move on.`;
342
+ }
343
+ /**
344
+ * Description-enhancement workflow using the GitHub consolidated API.
345
+ */
346
+ descriptionEnhancementInstructions(_params) {
347
+ return ` Enhance the PR description now.
348
+
349
+ 1. Call pull_request_read(method="get", owner, repo, pullNumber) to read current PR and description
350
+ 2. Call pull_request_read(method="get_diff", owner, repo, pullNumber) to analyze code changes
351
+ 3. Use search_code(query=...) to find configuration patterns, API changes
352
+ 4. Extract information for each required section
353
+ 5. Build enhanced description following section structure
354
+ 6. Call update_pull_request(owner, repo, pullNumber, body=...) with enhanced description
355
+
356
+ CRITICAL: Return ONLY the enhanced description markdown.
357
+ Do NOT include meta-commentary or explanations.
358
+ Start directly with section content.`;
359
+ }
360
+ /**
361
+ * A submitted review carries the decision: APPROVE -> APPROVED,
362
+ * REQUEST_CHANGES -> BLOCKED, anything else (incl. COMMENT) -> null.
363
+ */
364
+ interpretDecision(call) {
365
+ if (call.toolName !== "pull_request_review_write") {
366
+ return null;
367
+ }
368
+ const args = call.args || {};
369
+ if (args.method !== "submit") {
370
+ return null;
371
+ }
372
+ if (args.event === "APPROVE") {
373
+ return "APPROVED";
374
+ }
375
+ if (args.event === "REQUEST_CHANGES") {
376
+ return "BLOCKED";
377
+ }
378
+ return null;
379
+ }
380
+ }
381
+ // ============================================================================
382
+ // Factory
383
+ // ============================================================================
384
+ const BITBUCKET_TOOLSET = new BitbucketToolset();
385
+ const GITHUB_TOOLSET = new GitHubToolset();
386
+ export function getProviderToolset(provider) {
387
+ return provider === "github" ? GITHUB_TOOLSET : BITBUCKET_TOOLSET;
388
+ }
389
+ //# sourceMappingURL=ProviderToolset.js.map
@@ -16,6 +16,12 @@ export interface YamaConfig {
16
16
  projectStandards?: ProjectStandardsConfig;
17
17
  monitoring: MonitoringConfig;
18
18
  performance: PerformanceConfig;
19
+ /**
20
+ * Optional explicit default VCS provider used as the lowest-priority
21
+ * fallback by ProviderDetector when neither the request nor the
22
+ * environment indicate a provider. Defaults to Bitbucket when unset.
23
+ */
24
+ defaultProvider?: "github" | "bitbucket";
19
25
  }
20
26
  export type YamaV2Config = YamaConfig;
21
27
  export interface DisplayConfig {
@@ -70,16 +76,41 @@ export interface RedisConfig {
70
76
  keyPrefix?: string;
71
77
  ttl?: number;
72
78
  }
79
+ export interface BitbucketConfig {
80
+ enabled?: boolean;
81
+ /** List of tool names to block from Bitbucket MCP server */
82
+ blockedTools?: string[];
83
+ }
84
+ export interface GitHubConfig {
85
+ enabled: boolean;
86
+ /** List of tool names to block from GitHub MCP server */
87
+ blockedTools?: string[];
88
+ /**
89
+ * Transport for the GitHub MCP server.
90
+ * Defaults to "http" (GitHub's hosted remote MCP server). Use "stdio" only
91
+ * for a self-hosted / Docker GitHub MCP server (requires `command`/`args`).
92
+ */
93
+ transport?: "http" | "stdio";
94
+ /**
95
+ * Endpoint for the remote HTTP GitHub MCP server.
96
+ * Defaults to "https://api.githubcopilot.com/mcp/". Override to point at a
97
+ * self-hosted / enterprise remote MCP endpoint.
98
+ */
99
+ url?: string;
100
+ /** Command to launch a self-hosted stdio GitHub MCP server (transport: "stdio"). */
101
+ command?: string;
102
+ /** Arguments for the self-hosted stdio GitHub MCP server command. */
103
+ args?: string[];
104
+ }
105
+ export interface JiraConfig {
106
+ enabled: boolean;
107
+ /** List of tool names to block from Jira MCP server */
108
+ blockedTools?: string[];
109
+ }
73
110
  export interface MCPServersConfig {
74
- bitbucket?: {
75
- /** List of tool names to block from Bitbucket MCP server */
76
- blockedTools?: string[];
77
- };
78
- jira: {
79
- enabled: boolean;
80
- /** List of tool names to block from Jira MCP server */
81
- blockedTools?: string[];
82
- };
111
+ bitbucket?: BitbucketConfig;
112
+ github?: GitHubConfig;
113
+ jira: JiraConfig;
83
114
  }
84
115
  export interface ReviewConfig {
85
116
  enabled: boolean;