codingbuddy 5.3.0 → 5.4.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.
Files changed (47) hide show
  1. package/dist/src/agent/index.d.ts +1 -0
  2. package/dist/src/agent/index.js +1 -0
  3. package/dist/src/agent/stack-matcher.d.ts +13 -0
  4. package/dist/src/agent/stack-matcher.js +81 -0
  5. package/dist/src/collaboration/index.d.ts +0 -2
  6. package/dist/src/collaboration/index.js +1 -3
  7. package/dist/src/keyword/keyword.module.js +39 -2
  8. package/dist/src/keyword/keyword.service.d.ts +5 -0
  9. package/dist/src/keyword/keyword.service.js +90 -6
  10. package/dist/src/keyword/keyword.types.d.ts +24 -2
  11. package/dist/src/keyword/permission-forecast.d.ts +2 -0
  12. package/dist/src/keyword/permission-forecast.js +94 -0
  13. package/dist/src/mcp/handlers/agent.handler.js +1 -1
  14. package/dist/src/mcp/handlers/clarification-gate.d.ts +22 -0
  15. package/dist/src/mcp/handlers/clarification-gate.js +129 -0
  16. package/dist/src/mcp/handlers/council-scene.builder.d.ts +9 -0
  17. package/dist/src/mcp/handlers/council-scene.builder.js +115 -0
  18. package/dist/src/mcp/handlers/council-scene.types.d.ts +11 -0
  19. package/dist/src/{collaboration/council-summary.types.js → mcp/handlers/council-scene.types.js} +1 -1
  20. package/dist/src/mcp/handlers/discussion.handler.d.ts +1 -0
  21. package/dist/src/mcp/handlers/discussion.handler.js +23 -4
  22. package/dist/src/mcp/handlers/discussion.types.d.ts +12 -0
  23. package/dist/src/mcp/handlers/discussion.types.js +4 -1
  24. package/dist/src/mcp/handlers/execution-gate.d.ts +29 -0
  25. package/dist/src/mcp/handlers/execution-gate.js +49 -0
  26. package/dist/src/mcp/handlers/index.d.ts +1 -0
  27. package/dist/src/mcp/handlers/index.js +3 -1
  28. package/dist/src/mcp/handlers/mode.handler.d.ts +5 -0
  29. package/dist/src/mcp/handlers/mode.handler.js +127 -2
  30. package/dist/src/mcp/handlers/planning-contract.d.ts +2 -0
  31. package/dist/src/mcp/handlers/planning-contract.js +28 -0
  32. package/dist/src/mcp/handlers/planning-stage.d.ts +20 -0
  33. package/dist/src/mcp/handlers/planning-stage.js +58 -0
  34. package/dist/src/mcp/handlers/review-pr.handler.d.ts +12 -0
  35. package/dist/src/mcp/handlers/review-pr.handler.js +81 -0
  36. package/dist/src/mcp/mcp.module.js +1 -0
  37. package/dist/src/shared/version.d.ts +1 -1
  38. package/dist/src/shared/version.js +1 -1
  39. package/dist/src/ship/review-pr.service.d.ts +15 -0
  40. package/dist/src/ship/review-pr.service.js +136 -0
  41. package/dist/src/ship/review-pr.types.d.ts +21 -0
  42. package/dist/src/ship/review-pr.types.js +3 -0
  43. package/dist/src/ship/ship.module.js +5 -2
  44. package/package.json +2 -2
  45. package/dist/src/collaboration/council-summary.service.d.ts +0 -2
  46. package/dist/src/collaboration/council-summary.service.js +0 -114
  47. package/dist/src/collaboration/council-summary.types.d.ts +0 -24
@@ -38,6 +38,10 @@ const teams_capability_service_1 = require("../../agent/teams-capability.service
38
38
  const execution_plan_1 = require("../../agent/execution-plan");
39
39
  const impact_1 = require("../../impact");
40
40
  const rule_event_collector_1 = require("../../rules/rule-event-collector");
41
+ const clarification_gate_1 = require("./clarification-gate");
42
+ const planning_stage_1 = require("./planning-stage");
43
+ const execution_gate_1 = require("./execution-gate");
44
+ const council_scene_builder_1 = require("./council-scene.builder");
41
45
  const CONTEXT_TITLE_MAX_LENGTH = 50;
42
46
  let ModeHandler = ModeHandler_1 = class ModeHandler extends abstract_handler_1.AbstractHandler {
43
47
  constructor(keywordService, configService, languageService, modelResolverService, stateService, contextDocService, diagnosticLogService, agentService, councilPresetService, teamsCapabilityService, impactEventService, ruleEventCollector) {
@@ -80,6 +84,15 @@ let ModeHandler = ModeHandler_1 = class ModeHandler extends abstract_handler_1.A
80
84
  enum: ['minimal', 'standard', 'full'],
81
85
  description: 'Control response detail level. minimal: metadata only; standard: truncated content (default); full: complete content',
82
86
  },
87
+ question_budget: {
88
+ type: 'number',
89
+ description: 'Clarification Gate budget (#1371). Maximum clarification questions remaining in this session. Defaults to 3 on the first call; pass the `questionBudget` returned from the previous PLAN response to decrement across rounds. When 0, the gate proceeds to planning with explicit assumptions.',
90
+ },
91
+ planning_stage: {
92
+ type: 'string',
93
+ enum: ['discover', 'design', 'plan'],
94
+ description: 'Staged planning hint (#1372). Overrides automatic stage routing when the caller knows which stage to enter. Use "design" after the user confirms direction from Discover, or "plan" after the user confirms the approach from Design.',
95
+ },
83
96
  },
84
97
  required: ['prompt'],
85
98
  },
@@ -94,6 +107,15 @@ let ModeHandler = ModeHandler_1 = class ModeHandler extends abstract_handler_1.A
94
107
  const recommendedAgent = (0, validation_constants_1.extractRequiredString)(args, 'recommended_agent') ?? undefined;
95
108
  const rawVerbosity = (0, validation_constants_1.extractRequiredString)(args, 'verbosity') ?? 'standard';
96
109
  const verbosity = (0, verbosity_types_1.isValidVerbosity)(rawVerbosity) ? rawVerbosity : 'standard';
110
+ const rawQuestionBudget = args?.['question_budget'];
111
+ const questionBudget = typeof rawQuestionBudget === 'number' && Number.isFinite(rawQuestionBudget)
112
+ ? rawQuestionBudget
113
+ : undefined;
114
+ const VALID_STAGES = new Set(['discover', 'design', 'plan']);
115
+ const rawPlanningStage = (0, validation_constants_1.extractRequiredString)(args, 'planning_stage') ?? undefined;
116
+ const planningStageHint = rawPlanningStage && VALID_STAGES.has(rawPlanningStage)
117
+ ? rawPlanningStage
118
+ : undefined;
97
119
  try {
98
120
  await this.configService.reload();
99
121
  let resolvedRecommendedAgent = recommendedAgent;
@@ -150,23 +172,53 @@ let ModeHandler = ModeHandler_1 = class ModeHandler extends abstract_handler_1.A
150
172
  const deepThinkingInstructions = this.buildDeepThinkingInstructions(result.mode);
151
173
  const planReviewGate = this.buildPlanReviewGate(result.mode, settings?.ai?.planReviewGate);
152
174
  const agentDiscussion = this.buildAgentDiscussion(result.mode, settings?.ai?.agentDiscussion);
175
+ const reviewContext = this.buildReviewContext(result.mode, result.originalPrompt);
153
176
  const councilPreset = this.councilPresetService.resolvePreset(result.mode) ?? undefined;
154
177
  const teamsCapability = await this.resolveTeamsCapability();
155
178
  const executionPlan = this.buildExecutionPlan(dispatchReady, teamsCapability);
156
179
  const visual = await this.buildVisual(result.mode, result.delegates_to, result.parallelAgentsRecommendation?.specialists, settings?.eco);
180
+ const councilScene = (0, council_scene_builder_1.buildCouncilScene)(result.mode, councilPreset ?? undefined, visual, {
181
+ delegatesTo: result.delegates_to,
182
+ specialists: result.parallelAgentsRecommendation?.specialists,
183
+ });
184
+ const councilRenderInstructions = (0, council_scene_builder_1.buildCouncilSceneInstructions)(councilScene);
185
+ if (councilRenderInstructions) {
186
+ result.instructions += councilRenderInstructions;
187
+ }
188
+ const clarification = this.buildClarificationMetadata(result.mode, result.originalPrompt, questionBudget);
189
+ const clarificationInstructions = this.buildClarificationFirstInstructions(clarification);
190
+ const planningStage = this.buildPlanningStageMetadata(result.mode, clarification, planningStageHint);
191
+ const executionGate = this.buildExecutionGate(result.mode, clarification, planningStage, result.parallelAgentsRecommendation?.specialists);
192
+ const serializedExecutionPlan = executionPlan
193
+ ? (0, execution_plan_1.serializeExecutionPlan)(executionPlan)
194
+ : undefined;
195
+ const gatedFields = (0, execution_gate_1.suppressDispatchWhileGated)(executionGate, {
196
+ dispatchReady,
197
+ parallelAgentsRecommendation: result.parallelAgentsRecommendation,
198
+ executionPlan: serializedExecutionPlan,
199
+ });
157
200
  const response = (0, response_utils_1.createJsonResponse)({
158
201
  ...result,
202
+ ...(clarificationInstructions !== undefined && { instructions: clarificationInstructions }),
159
203
  language,
160
204
  languageInstruction: languageInstructionResult.instruction,
161
205
  resolvedModel,
162
- ...(dispatchReady && { dispatchReady }),
206
+ ...(gatedFields.parallelAgentsRecommendation !== undefined && {
207
+ parallelAgentsRecommendation: gatedFields.parallelAgentsRecommendation,
208
+ }),
209
+ ...(gatedFields.dispatchReady && { dispatchReady: gatedFields.dispatchReady }),
163
210
  ...(deepThinkingInstructions && { deepThinkingInstructions }),
164
211
  ...(planReviewGate && { planReviewGate }),
165
212
  ...(agentDiscussion && { agentDiscussion }),
213
+ ...(reviewContext && { reviewContext }),
166
214
  ...(visual && { visual }),
167
215
  ...(councilPreset && { councilPreset }),
168
- ...(executionPlan && { executionPlan: (0, execution_plan_1.serializeExecutionPlan)(executionPlan) }),
216
+ ...(councilScene && { councilScene }),
217
+ ...(gatedFields.executionPlan && { executionPlan: gatedFields.executionPlan }),
169
218
  ...(teamsCapability && { teamsCapability }),
219
+ ...(clarification && clarification),
220
+ ...(planningStage && { planningStage }),
221
+ ...(executionGate && { executionGate }),
170
222
  ...contextResult,
171
223
  ...(projectRootWarning && { projectRootWarning }),
172
224
  });
@@ -332,6 +384,55 @@ let ModeHandler = ModeHandler_1 = class ModeHandler extends abstract_handler_1.A
332
384
  dispatch: 'recommend',
333
385
  };
334
386
  }
387
+ buildClarificationMetadata(mode, originalPrompt, questionBudget) {
388
+ if (mode !== 'PLAN' && mode !== 'AUTO') {
389
+ return undefined;
390
+ }
391
+ return (0, clarification_gate_1.evaluateClarification)(originalPrompt, {
392
+ ...(questionBudget !== undefined && { questionBudget }),
393
+ });
394
+ }
395
+ buildExecutionGate(mode, clarification, planningStage, specialists) {
396
+ if (mode !== 'PLAN' && mode !== 'AUTO') {
397
+ return undefined;
398
+ }
399
+ if (!clarification) {
400
+ return undefined;
401
+ }
402
+ return (0, execution_gate_1.evaluateExecutionGate)({
403
+ clarification,
404
+ planningStage,
405
+ specialists,
406
+ });
407
+ }
408
+ buildClarificationFirstInstructions(clarification) {
409
+ if (!clarification?.clarificationNeeded) {
410
+ return undefined;
411
+ }
412
+ const question = clarification.nextQuestion ??
413
+ 'Can you clarify the scope and success criteria of this request?';
414
+ const budget = clarification.questionBudget ?? 0;
415
+ return ('🔴 CLARIFICATION REQUIRED — DO NOT PLAN.\n\n' +
416
+ 'The request is ambiguous. You MUST:\n' +
417
+ '1. Ask EXACTLY the question below and STOP.\n' +
418
+ '2. Do NOT output any implementation plan, architecture, or code.\n' +
419
+ "3. Wait for the user's response before continuing.\n\n" +
420
+ `❓ Ask this: "${question}"\n\n` +
421
+ `Remaining question budget: ${budget}\n\n` +
422
+ 'After the user answers, call parse_mode again with the clarified prompt ' +
423
+ `and question_budget=${budget} to continue.`);
424
+ }
425
+ buildPlanningStageMetadata(mode, clarification, stageHint) {
426
+ if (mode !== 'PLAN' && mode !== 'AUTO') {
427
+ return undefined;
428
+ }
429
+ if (!clarification) {
430
+ return undefined;
431
+ }
432
+ return (0, planning_stage_1.resolvePlanningStage)(clarification, {
433
+ ...(stageHint && { stageHint }),
434
+ });
435
+ }
335
436
  buildAgentDiscussion(mode, configValue) {
336
437
  if (mode !== 'EVAL') {
337
438
  return undefined;
@@ -343,6 +444,30 @@ let ModeHandler = ModeHandler_1 = class ModeHandler extends abstract_handler_1.A
343
444
  includeConsensus: true,
344
445
  };
345
446
  }
447
+ buildReviewContext(mode, originalPrompt) {
448
+ if (mode !== 'EVAL') {
449
+ return undefined;
450
+ }
451
+ const prMatch = originalPrompt.match(/(?:PR\s*#?(\d+)|pull\s*request\s*#?(\d+))/i);
452
+ if (!prMatch) {
453
+ return undefined;
454
+ }
455
+ const prNumber = parseInt(prMatch[1] ?? prMatch[2], 10);
456
+ if (isNaN(prNumber)) {
457
+ return undefined;
458
+ }
459
+ const issueMatch = originalPrompt.match(/issue\s*#?(\d+)/i);
460
+ const issueNumber = issueMatch ? parseInt(issueMatch[1], 10) : undefined;
461
+ const hintParams = issueNumber
462
+ ? `pr_number: ${prNumber}, issue_number: ${issueNumber}`
463
+ : `pr_number: ${prNumber}`;
464
+ return {
465
+ detected: true,
466
+ pr_number: prNumber,
467
+ ...(issueNumber && { issue_number: issueNumber }),
468
+ hint: `Call review_pr({ ${hintParams} }) to get structured review data including diff, checklists, and specialist recommendations.`,
469
+ };
470
+ }
346
471
  async buildVisual(mode, delegatesTo, specialists, eco) {
347
472
  try {
348
473
  const modeAgentName = `${mode.toLowerCase()}-mode`;
@@ -0,0 +1,2 @@
1
+ export declare const PLANNING_CONTRACT: readonly string[];
2
+ export declare function resolvePlanningContract(mode: string, agentId?: string): readonly string[] | undefined;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PLANNING_CONTRACT = void 0;
4
+ exports.resolvePlanningContract = resolvePlanningContract;
5
+ exports.PLANNING_CONTRACT = [
6
+ 'Ask one clarifying question at a time — do not batch questions.',
7
+ 'Wait for user confirmation before advancing to the next planning stage.',
8
+ 'Use the recommended skill for the current stage (brainstorming for discover, writing-plans for plan).',
9
+ 'Present 2-3 alternative approaches with trade-offs before settling on a direction.',
10
+ 'Break implementation into bite-sized tasks (2-5 minutes each).',
11
+ ];
12
+ const PLANNING_AGENT_IDS = new Set([
13
+ 'technical-planner',
14
+ 'solution-architect',
15
+ 'plan-mode',
16
+ 'auto-mode',
17
+ ]);
18
+ const PLANNING_MODES = new Set(['PLAN', 'AUTO']);
19
+ function resolvePlanningContract(mode, agentId) {
20
+ if (!PLANNING_MODES.has(mode.toUpperCase())) {
21
+ return undefined;
22
+ }
23
+ if (agentId && PLANNING_AGENT_IDS.has(agentId)) {
24
+ return exports.PLANNING_CONTRACT;
25
+ }
26
+ return undefined;
27
+ }
28
+ //# sourceMappingURL=planning-contract.js.map
@@ -0,0 +1,20 @@
1
+ import type { ClarificationMetadata } from './clarification-gate';
2
+ export type PlanningStage = 'discover' | 'design' | 'plan';
3
+ export interface StageProgression {
4
+ completedStages: PlanningStage[];
5
+ currentStage: PlanningStage;
6
+ remainingStages: PlanningStage[];
7
+ }
8
+ export interface PlanningStageMetadata {
9
+ currentStage: PlanningStage;
10
+ stageDescription: string;
11
+ nextStage?: 'design' | 'plan';
12
+ stageTransitionHint?: string;
13
+ recommendedAgent?: string;
14
+ recommendedSkill?: string;
15
+ stageProgression?: StageProgression;
16
+ }
17
+ export interface PlanningStageOptions {
18
+ stageHint?: PlanningStage;
19
+ }
20
+ export declare function resolvePlanningStage(clarification: ClarificationMetadata, options?: PlanningStageOptions): PlanningStageMetadata;
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolvePlanningStage = resolvePlanningStage;
4
+ const STAGE_DESCRIPTIONS = {
5
+ discover: 'Discover: Surface questions, constraints, and the option space before committing to an approach.',
6
+ design: 'Design: Synthesize candidate approaches, compare trade-offs, and identify open risks.',
7
+ plan: 'Plan: Produce a concrete, step-by-step implementation plan.',
8
+ };
9
+ const STAGE_TRANSITION_HINTS = {
10
+ discover: 'Answer the clarification question(s) or confirm the direction to proceed to Design.',
11
+ design: 'Confirm the preferred approach to proceed to the concrete implementation Plan.',
12
+ };
13
+ const STAGE_AGENTS = {
14
+ discover: 'solution-architect',
15
+ design: 'solution-architect',
16
+ plan: 'technical-planner',
17
+ };
18
+ const STAGE_SKILLS = {
19
+ discover: 'brainstorming',
20
+ design: undefined,
21
+ plan: 'writing-plans',
22
+ };
23
+ function resolvePlanningStage(clarification, options = {}) {
24
+ const stage = resolveStage(clarification, options);
25
+ return {
26
+ currentStage: stage,
27
+ stageDescription: STAGE_DESCRIPTIONS[stage],
28
+ ...(stage !== 'plan' && { nextStage: getNextStage(stage) }),
29
+ ...(stage !== 'plan' && {
30
+ stageTransitionHint: STAGE_TRANSITION_HINTS[stage],
31
+ }),
32
+ recommendedAgent: STAGE_AGENTS[stage],
33
+ ...(STAGE_SKILLS[stage] && { recommendedSkill: STAGE_SKILLS[stage] }),
34
+ stageProgression: buildStageProgression(stage),
35
+ };
36
+ }
37
+ function resolveStage(clarification, options) {
38
+ if (options.stageHint) {
39
+ return options.stageHint;
40
+ }
41
+ if (clarification.clarificationNeeded) {
42
+ return 'discover';
43
+ }
44
+ return 'discover';
45
+ }
46
+ function getNextStage(stage) {
47
+ return stage === 'discover' ? 'design' : 'plan';
48
+ }
49
+ const ALL_STAGES = ['discover', 'design', 'plan'];
50
+ function buildStageProgression(currentStage) {
51
+ const currentIndex = ALL_STAGES.indexOf(currentStage);
52
+ return {
53
+ completedStages: ALL_STAGES.slice(0, currentIndex),
54
+ currentStage,
55
+ remainingStages: ALL_STAGES.slice(currentIndex + 1),
56
+ };
57
+ }
58
+ //# sourceMappingURL=planning-stage.js.map
@@ -0,0 +1,12 @@
1
+ import type { ToolDefinition } from './base.handler';
2
+ import type { ToolResponse } from '../response.utils';
3
+ import { AbstractHandler } from './abstract-handler';
4
+ import { ReviewPrService } from '../../ship/review-pr.service';
5
+ export declare class ReviewPrHandler extends AbstractHandler {
6
+ private readonly reviewPrService;
7
+ private readonly logger;
8
+ constructor(reviewPrService: ReviewPrService);
9
+ protected getHandledTools(): string[];
10
+ protected handleTool(_toolName: string, args: Record<string, unknown> | undefined): Promise<ToolResponse>;
11
+ getToolDefinitions(): ToolDefinition[];
12
+ }
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var ReviewPrHandler_1;
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.ReviewPrHandler = void 0;
14
+ const common_1 = require("@nestjs/common");
15
+ const abstract_handler_1 = require("./abstract-handler");
16
+ const review_pr_service_1 = require("../../ship/review-pr.service");
17
+ const response_utils_1 = require("../response.utils");
18
+ let ReviewPrHandler = ReviewPrHandler_1 = class ReviewPrHandler extends abstract_handler_1.AbstractHandler {
19
+ constructor(reviewPrService) {
20
+ super();
21
+ this.reviewPrService = reviewPrService;
22
+ this.logger = new common_1.Logger(ReviewPrHandler_1.name);
23
+ }
24
+ getHandledTools() {
25
+ return ['review_pr'];
26
+ }
27
+ async handleTool(_toolName, args) {
28
+ try {
29
+ const prNumber = args?.pr_number;
30
+ if (typeof prNumber !== 'number' || !Number.isFinite(prNumber) || prNumber <= 0) {
31
+ return (0, response_utils_1.createErrorResponse)('pr_number is required and must be a positive number');
32
+ }
33
+ const issueNumber = typeof args?.issue_number === 'number' && args.issue_number > 0
34
+ ? args.issue_number
35
+ : undefined;
36
+ const timeout = typeof args?.timeout === 'number' && args.timeout > 0 ? args.timeout : 30000;
37
+ const result = await this.reviewPrService.reviewPr({
38
+ prNumber,
39
+ issueNumber,
40
+ timeout,
41
+ });
42
+ return (0, response_utils_1.createJsonResponse)(result);
43
+ }
44
+ catch (error) {
45
+ this.logger.error(`review_pr failed: ${error}`);
46
+ return (0, response_utils_1.createErrorResponse)(`review_pr failed: ${error instanceof Error ? error.message : String(error)}`);
47
+ }
48
+ }
49
+ getToolDefinitions() {
50
+ return [
51
+ {
52
+ name: 'review_pr',
53
+ description: 'Fetch PR metadata, diff, checklists, and specialist recommendations for EVAL-mode PR review. Returns structured data for comprehensive code review.',
54
+ inputSchema: {
55
+ type: 'object',
56
+ properties: {
57
+ pr_number: {
58
+ type: 'number',
59
+ description: 'PR number to review',
60
+ },
61
+ issue_number: {
62
+ type: 'number',
63
+ description: 'Optional linked issue number for spec compliance check',
64
+ },
65
+ timeout: {
66
+ type: 'number',
67
+ description: 'Timeout in milliseconds (default: 30000)',
68
+ },
69
+ },
70
+ required: ['pr_number'],
71
+ },
72
+ },
73
+ ];
74
+ }
75
+ };
76
+ exports.ReviewPrHandler = ReviewPrHandler;
77
+ exports.ReviewPrHandler = ReviewPrHandler = ReviewPrHandler_1 = __decorate([
78
+ (0, common_1.Injectable)(),
79
+ __metadata("design:paramtypes", [review_pr_service_1.ReviewPrService])
80
+ ], ReviewPrHandler);
81
+ //# sourceMappingURL=review-pr.handler.js.map
@@ -46,6 +46,7 @@ const handlers = [
46
46
  handlers_1.PluginValidationHandler,
47
47
  handlers_1.ReleaseCheckHandler,
48
48
  handlers_1.QualityReportHandler,
49
+ handlers_1.ReviewPrHandler,
49
50
  handlers_1.BriefingHandler,
50
51
  handlers_1.ResumeHandler,
51
52
  handlers_1.RuleImpactHandler,
@@ -1 +1 @@
1
- export declare const VERSION = "5.3.0";
1
+ export declare const VERSION = "5.4.0";
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VERSION = void 0;
4
- exports.VERSION = '5.3.0';
4
+ exports.VERSION = '5.4.0';
5
5
  //# sourceMappingURL=version.js.map
@@ -0,0 +1,15 @@
1
+ import { ChecklistService } from '../checklist/checklist.service';
2
+ import type { ReviewPrInput, ReviewPrResult } from './review-pr.types';
3
+ export declare class ReviewPrService {
4
+ private readonly checklistService;
5
+ private readonly logger;
6
+ private readonly mapper;
7
+ constructor(checklistService: ChecklistService);
8
+ reviewPr(input: ReviewPrInput): Promise<ReviewPrResult>;
9
+ private fetchPrMeta;
10
+ private fetchPrDiff;
11
+ private generateChecklists;
12
+ private mapSpecialists;
13
+ private fetchIssueCriteria;
14
+ private buildReviewProtocol;
15
+ }
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var ReviewPrService_1;
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.ReviewPrService = void 0;
14
+ const common_1 = require("@nestjs/common");
15
+ const child_process_1 = require("child_process");
16
+ const file_specialist_mapper_1 = require("./file-specialist-mapper");
17
+ const checklist_service_1 = require("../checklist/checklist.service");
18
+ let ReviewPrService = ReviewPrService_1 = class ReviewPrService {
19
+ constructor(checklistService) {
20
+ this.checklistService = checklistService;
21
+ this.logger = new common_1.Logger(ReviewPrService_1.name);
22
+ this.mapper = new file_specialist_mapper_1.FileSpecialistMapper();
23
+ }
24
+ async reviewPr(input) {
25
+ const start = Date.now();
26
+ const { prNumber, issueNumber } = input;
27
+ const prMeta = this.fetchPrMeta(prNumber);
28
+ const diff = this.fetchPrDiff(prNumber);
29
+ const checklists = await this.generateChecklists(prMeta.changedFiles);
30
+ const recommendedSpecialists = this.mapSpecialists(prMeta.changedFiles);
31
+ const acceptanceCriteria = issueNumber ? this.fetchIssueCriteria(issueNumber) : null;
32
+ const reviewProtocol = this.buildReviewProtocol(prNumber, issueNumber);
33
+ return {
34
+ prMeta,
35
+ diff,
36
+ checklists,
37
+ recommendedSpecialists,
38
+ acceptanceCriteria,
39
+ reviewProtocol,
40
+ duration: Date.now() - start,
41
+ };
42
+ }
43
+ fetchPrMeta(prNumber) {
44
+ try {
45
+ const raw = (0, child_process_1.execSync)(`gh pr view ${prNumber} --json title,headRefName,files,additions,deletions`, { encoding: 'utf-8', timeout: 15000 });
46
+ const data = JSON.parse(raw);
47
+ return {
48
+ title: data.title ?? '',
49
+ branch: data.headRefName ?? '',
50
+ changedFiles: (data.files ?? []).map((f) => f.path),
51
+ additions: data.additions ?? 0,
52
+ deletions: data.deletions ?? 0,
53
+ };
54
+ }
55
+ catch (error) {
56
+ throw new Error(`Failed to fetch PR #${prNumber}: ${error instanceof Error ? error.message : String(error)}`);
57
+ }
58
+ }
59
+ fetchPrDiff(prNumber) {
60
+ try {
61
+ return (0, child_process_1.execSync)(`gh pr diff ${prNumber}`, {
62
+ encoding: 'utf-8',
63
+ timeout: 15000,
64
+ maxBuffer: 10 * 1024 * 1024,
65
+ });
66
+ }
67
+ catch (error) {
68
+ this.logger.warn(`Failed to fetch diff for PR #${prNumber}: ${error}`);
69
+ return '';
70
+ }
71
+ }
72
+ async generateChecklists(changedFiles) {
73
+ if (changedFiles.length === 0)
74
+ return [];
75
+ try {
76
+ const result = await this.checklistService.generateChecklist({
77
+ files: changedFiles,
78
+ });
79
+ return result.checklists;
80
+ }
81
+ catch (error) {
82
+ this.logger.warn(`Checklist generation failed: ${error}`);
83
+ return [];
84
+ }
85
+ }
86
+ mapSpecialists(changedFiles) {
87
+ const mapped = this.mapper.mapFiles(changedFiles);
88
+ const specialists = [...new Set(mapped.map(m => m.specialist))];
89
+ return specialists;
90
+ }
91
+ fetchIssueCriteria(issueNumber) {
92
+ try {
93
+ const raw = (0, child_process_1.execSync)(`gh issue view ${issueNumber} --json body`, {
94
+ encoding: 'utf-8',
95
+ timeout: 10000,
96
+ });
97
+ const data = JSON.parse(raw);
98
+ const body = data.body ?? '';
99
+ const criteriaMatch = body.match(/## Acceptance criteria\s*\n([\s\S]*?)(?=\n## |\n---|$)/i);
100
+ return criteriaMatch ? criteriaMatch[1].trim() : body.slice(0, 2000);
101
+ }
102
+ catch {
103
+ return null;
104
+ }
105
+ }
106
+ buildReviewProtocol(prNumber, issueNumber) {
107
+ const issueCmd = issueNumber ? `\ngh issue view ${issueNumber}` : '';
108
+ return [
109
+ 'Follow the canonical PR review cycle (pr-review-cycle.md):',
110
+ '',
111
+ '1. CI GATE (BLOCKING): gh pr checks ' + prNumber,
112
+ '2. LOCAL VERIFICATION: yarn lint && yarn type-check',
113
+ '3. READ THE DIFF: gh pr diff ' + prNumber,
114
+ '4. CODE QUALITY SCAN: unused imports, any types, missing error handling, dead code',
115
+ '5. SPEC COMPLIANCE: ' + (issueNumber ? `gh issue view ${issueNumber}` : 'N/A'),
116
+ '6. TEST COVERAGE: new logic tested? edge cases covered?',
117
+ '7. WRITE REVIEW: gh pr review ' + prNumber + ' --comment --body "<structured review>"',
118
+ '',
119
+ 'Severity scale: critical / high / medium / low (see severity-classification.md)',
120
+ 'Approval gate: Critical=0 AND High=0',
121
+ '',
122
+ 'Commands:',
123
+ `gh pr checks ${prNumber}`,
124
+ `gh pr diff ${prNumber}`,
125
+ issueCmd,
126
+ ]
127
+ .filter(Boolean)
128
+ .join('\n');
129
+ }
130
+ };
131
+ exports.ReviewPrService = ReviewPrService;
132
+ exports.ReviewPrService = ReviewPrService = ReviewPrService_1 = __decorate([
133
+ (0, common_1.Injectable)(),
134
+ __metadata("design:paramtypes", [checklist_service_1.ChecklistService])
135
+ ], ReviewPrService);
136
+ //# sourceMappingURL=review-pr.service.js.map
@@ -0,0 +1,21 @@
1
+ export interface ReviewPrInput {
2
+ prNumber: number;
3
+ issueNumber?: number;
4
+ timeout?: number;
5
+ }
6
+ export interface PrMeta {
7
+ title: string;
8
+ branch: string;
9
+ changedFiles: string[];
10
+ additions: number;
11
+ deletions: number;
12
+ }
13
+ export interface ReviewPrResult {
14
+ prMeta: PrMeta;
15
+ diff: string;
16
+ checklists: unknown[];
17
+ recommendedSpecialists: string[];
18
+ acceptanceCriteria: string | null;
19
+ reviewProtocol: string;
20
+ duration: number;
21
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=review-pr.types.js.map
@@ -8,14 +8,17 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.ShipModule = void 0;
10
10
  const common_1 = require("@nestjs/common");
11
+ const checklist_module_1 = require("../checklist/checklist.module");
11
12
  const quality_report_service_1 = require("./quality-report.service");
13
+ const review_pr_service_1 = require("./review-pr.service");
12
14
  let ShipModule = class ShipModule {
13
15
  };
14
16
  exports.ShipModule = ShipModule;
15
17
  exports.ShipModule = ShipModule = __decorate([
16
18
  (0, common_1.Module)({
17
- providers: [quality_report_service_1.QualityReportService],
18
- exports: [quality_report_service_1.QualityReportService],
19
+ imports: [checklist_module_1.ChecklistModule],
20
+ providers: [quality_report_service_1.QualityReportService, review_pr_service_1.ReviewPrService],
21
+ exports: [quality_report_service_1.QualityReportService, review_pr_service_1.ReviewPrService],
19
22
  })
20
23
  ], ShipModule);
21
24
  //# sourceMappingURL=ship.module.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codingbuddy",
3
- "version": "5.3.0",
3
+ "version": "5.4.0",
4
4
  "description": "Multi-AI Rules MCP Server - One source of truth for AI coding rules across all AI assistants",
5
5
  "author": "JeremyDev87",
6
6
  "license": "MIT",
@@ -77,7 +77,7 @@
77
77
  "@nestjs/core": "11.1.9",
78
78
  "@nestjs/platform-express": "11.1.9",
79
79
  "chalk": "5.4.1",
80
- "codingbuddy-rules": "5.3.0",
80
+ "codingbuddy-rules": "5.4.0",
81
81
  "eventemitter2": "6.4.9",
82
82
  "ink": "6.7.0",
83
83
  "minimatch": "10.2.3",
@@ -1,2 +0,0 @@
1
- import type { CouncilInput, CouncilSummary } from './council-summary.types';
2
- export declare function generateCouncilSummary(inputs: readonly CouncilInput[]): CouncilSummary;