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
@@ -10,3 +10,4 @@ export * from './agent-stack.schema';
10
10
  export * from './agent-stack.loader';
11
11
  export * from './execution-plan';
12
12
  export * from './execution-plan.types';
13
+ export * from './stack-matcher';
@@ -26,4 +26,5 @@ __exportStar(require("./agent-stack.schema"), exports);
26
26
  __exportStar(require("./agent-stack.loader"), exports);
27
27
  __exportStar(require("./execution-plan"), exports);
28
28
  __exportStar(require("./execution-plan.types"), exports);
29
+ __exportStar(require("./stack-matcher"), exports);
29
30
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,13 @@
1
+ export interface StackMatchInput {
2
+ name: string;
3
+ category: string;
4
+ tags: string[];
5
+ specialist_agents?: string[];
6
+ }
7
+ export interface StackMatchResult {
8
+ stackName: string;
9
+ confidence: number;
10
+ matchedTags: string[];
11
+ stackBased: true;
12
+ }
13
+ export declare function matchStack(prompt: string, stacks: readonly StackMatchInput[]): StackMatchResult | null;
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.matchStack = matchStack;
4
+ const DOMAIN_KEYWORDS = [
5
+ { pattern: /\bREST\b/i, tags: ['rest', 'api'] },
6
+ { pattern: /\bGraphQL\b/i, tags: ['graphql', 'api'] },
7
+ { pattern: /\bAPI\b/i, tags: ['api'] },
8
+ { pattern: /\bbackend\b/i, tags: ['backend'] },
9
+ { pattern: /\bendpoint/i, tags: ['api', 'backend'] },
10
+ { pattern: /\bcontroller/i, tags: ['api', 'backend'] },
11
+ { pattern: /\bservice\s+layer/i, tags: ['backend'] },
12
+ { pattern: /\broute/i, tags: ['api', 'backend'] },
13
+ { pattern: /\bsecurity\b/i, tags: ['security'] },
14
+ { pattern: /\baudit\b/i, tags: ['audit', 'security'] },
15
+ { pattern: /\b(?:vulnerability|vulnerabilities)\b/i, tags: ['vulnerability', 'security'] },
16
+ { pattern: /\bcompliance\b/i, tags: ['compliance', 'security'] },
17
+ { pattern: /\bOAuth\b/i, tags: ['security'] },
18
+ { pattern: /\bJWT\b/i, tags: ['security'] },
19
+ { pattern: /\b(?:XSS|CSRF)\b/i, tags: ['security', 'vulnerability'] },
20
+ { pattern: /\bauthenticat/i, tags: ['security'] },
21
+ { pattern: /\bUI\b/i, tags: ['ui', 'frontend'] },
22
+ { pattern: /\bUX\b/i, tags: ['ux', 'frontend'] },
23
+ { pattern: /\baccessibility\b/i, tags: ['accessibility', 'frontend'] },
24
+ { pattern: /\bSEO\b/i, tags: ['seo', 'frontend'] },
25
+ { pattern: /\bfrontend\b/i, tags: ['frontend'] },
26
+ { pattern: /\bETL\b/i, tags: ['etl', 'data'] },
27
+ { pattern: /\bpipeline\b/i, tags: ['pipeline', 'data'] },
28
+ { pattern: /\banalytics\b/i, tags: ['analytics', 'data'] },
29
+ { pattern: /\bdata\s+(warehouse|lake|pipeline|ingestion)/i, tags: ['data', 'pipeline'] },
30
+ { pattern: /\bmodel\s+(training|inference|deploy)/i, tags: ['model', 'ml'] },
31
+ { pattern: /\bML\b/i, tags: ['ml'] },
32
+ { pattern: /\bmachine\s*learning/i, tags: ['ml', 'ai'] },
33
+ { pattern: /\binference\b/i, tags: ['inference', 'ml'] },
34
+ { pattern: /\btraining\b/i, tags: ['training', 'ml'] },
35
+ { pattern: /\bfull[- ]?stack\b/i, tags: ['fullstack'] },
36
+ { pattern: /보안/i, tags: ['security'] },
37
+ { pattern: /취약점/i, tags: ['vulnerability', 'security'] },
38
+ { pattern: /감사/i, tags: ['audit', 'security'] },
39
+ { pattern: /접근성/i, tags: ['accessibility', 'frontend'] },
40
+ { pattern: /데이터\s*파이프라인/i, tags: ['data', 'pipeline'] },
41
+ { pattern: /머신\s*러닝|기계\s*학습/i, tags: ['ml', 'ai'] },
42
+ ];
43
+ const MIN_CONFIDENCE = 0.3;
44
+ function matchStack(prompt, stacks) {
45
+ if (!prompt || stacks.length === 0)
46
+ return null;
47
+ const detectedTags = new Set();
48
+ for (const { pattern, tags } of DOMAIN_KEYWORDS) {
49
+ if (pattern.test(prompt)) {
50
+ for (const tag of tags)
51
+ detectedTags.add(tag);
52
+ }
53
+ }
54
+ if (detectedTags.size === 0)
55
+ return null;
56
+ let bestResult = null;
57
+ for (const stack of stacks) {
58
+ const matched = [];
59
+ for (const tag of stack.tags) {
60
+ if (detectedTags.has(tag))
61
+ matched.push(tag);
62
+ }
63
+ if (matched.length === 0)
64
+ continue;
65
+ const tagCoverage = matched.length / stack.tags.length;
66
+ const detectedCoverage = matched.length / detectedTags.size;
67
+ const confidence = Math.min((tagCoverage + detectedCoverage) / 2, 1.0);
68
+ if (confidence < MIN_CONFIDENCE)
69
+ continue;
70
+ if (!bestResult || confidence > bestResult.confidence) {
71
+ bestResult = {
72
+ stackName: stack.name,
73
+ confidence,
74
+ matchedTags: matched,
75
+ stackBased: true,
76
+ };
77
+ }
78
+ }
79
+ return bestResult;
80
+ }
81
+ //# sourceMappingURL=stack-matcher.js.map
@@ -3,5 +3,3 @@ export { STANCES, STANCE_ICONS, createAgentOpinion, createCrossReview, createDis
3
3
  export { DiscussionEngine } from './discussion-engine';
4
4
  export { formatOpinion, formatCrossReview, formatConsensus, formatDiscussionRound, } from './terminal-formatter';
5
5
  export { mapSeverityToStance, convertSpecialistResult, convertSpecialistResults, type FindingSeverity, type SpecialistFinding, type SpecialistResult, } from './opinion-adapter';
6
- export type { CouncilInput, CouncilSummary, Disagreement, DisagreementPosition, } from './council-summary.types';
7
- export { generateCouncilSummary } from './council-summary.service';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.generateCouncilSummary = exports.convertSpecialistResults = exports.convertSpecialistResult = exports.mapSeverityToStance = exports.formatDiscussionRound = exports.formatConsensus = exports.formatCrossReview = exports.formatOpinion = exports.DiscussionEngine = exports.calculateConsensus = exports.createDiscussionRound = exports.createCrossReview = exports.createAgentOpinion = exports.STANCE_ICONS = exports.STANCES = void 0;
3
+ exports.convertSpecialistResults = exports.convertSpecialistResult = exports.mapSeverityToStance = exports.formatDiscussionRound = exports.formatConsensus = exports.formatCrossReview = exports.formatOpinion = exports.DiscussionEngine = exports.calculateConsensus = exports.createDiscussionRound = exports.createCrossReview = exports.createAgentOpinion = exports.STANCE_ICONS = exports.STANCES = void 0;
4
4
  var types_1 = require("./types");
5
5
  Object.defineProperty(exports, "STANCES", { enumerable: true, get: function () { return types_1.STANCES; } });
6
6
  Object.defineProperty(exports, "STANCE_ICONS", { enumerable: true, get: function () { return types_1.STANCE_ICONS; } });
@@ -19,6 +19,4 @@ var opinion_adapter_1 = require("./opinion-adapter");
19
19
  Object.defineProperty(exports, "mapSeverityToStance", { enumerable: true, get: function () { return opinion_adapter_1.mapSeverityToStance; } });
20
20
  Object.defineProperty(exports, "convertSpecialistResult", { enumerable: true, get: function () { return opinion_adapter_1.convertSpecialistResult; } });
21
21
  Object.defineProperty(exports, "convertSpecialistResults", { enumerable: true, get: function () { return opinion_adapter_1.convertSpecialistResults; } });
22
- var council_summary_service_1 = require("./council-summary.service");
23
- Object.defineProperty(exports, "generateCouncilSummary", { enumerable: true, get: function () { return council_summary_service_1.generateCouncilSummary; } });
24
22
  //# sourceMappingURL=index.js.map
@@ -16,6 +16,7 @@ const skill_module_1 = require("../skill/skill.module");
16
16
  const skill_recommendation_service_1 = require("../skill/skill-recommendation.service");
17
17
  const agent_module_1 = require("../agent/agent.module");
18
18
  const agent_service_1 = require("../agent/agent.service");
19
+ const agent_stack_service_1 = require("../agent/agent-stack.service");
19
20
  const agent_utils_1 = require("../shared/agent.utils");
20
21
  const client_type_1 = require("../shared/client-type");
21
22
  const keyword_service_1 = require("./keyword.service");
@@ -30,7 +31,7 @@ exports.KeywordModule = KeywordModule = __decorate([
30
31
  providers: [
31
32
  {
32
33
  provide: exports.KEYWORD_SERVICE,
33
- useFactory: (rulesService, configService, skillRecommendationService, agentService) => {
34
+ useFactory: (rulesService, configService, skillRecommendationService, agentService, agentStackService) => {
34
35
  const logger = new common_1.Logger('KeywordModule');
35
36
  const loadConfig = async () => {
36
37
  const content = await rulesService.getRuleContent('keyword-modes.json');
@@ -157,6 +158,35 @@ exports.KeywordModule = KeywordModule = __decorate([
157
158
  return null;
158
159
  }
159
160
  };
161
+ const loadStacks = async () => {
162
+ try {
163
+ const summaries = await agentStackService.listStacks();
164
+ const results = [];
165
+ for (const summary of summaries) {
166
+ try {
167
+ const full = await agentStackService.resolveStack(summary.name);
168
+ results.push({
169
+ name: full.name,
170
+ category: full.category,
171
+ tags: full.tags,
172
+ specialist_agents: full.specialist_agents,
173
+ });
174
+ }
175
+ catch {
176
+ results.push({
177
+ name: summary.name,
178
+ category: summary.category,
179
+ tags: summary.tags ?? [],
180
+ });
181
+ }
182
+ }
183
+ return results;
184
+ }
185
+ catch (error) {
186
+ logger.debug(`Failed to load agent stacks: ${error instanceof Error ? error.message : 'Unknown error'}`);
187
+ return [];
188
+ }
189
+ };
160
190
  const options = {
161
191
  primaryAgentResolver,
162
192
  loadAutoConfigFn: loadAutoConfig,
@@ -165,10 +195,17 @@ exports.KeywordModule = KeywordModule = __decorate([
165
195
  loadAgentSystemPromptFn: loadAgentSystemPrompt,
166
196
  getMaxIncludedSkillsFn: getMaxIncludedSkills,
167
197
  getClientTypeFn: () => (0, client_type_1.resolveClientType)(configService.getClientName()),
198
+ loadStacksFn: loadStacks,
168
199
  };
169
200
  return new keyword_service_1.KeywordService(loadConfig, loadRule, loadAgent, options);
170
201
  },
171
- inject: [rules_service_1.RulesService, config_service_1.ConfigService, skill_recommendation_service_1.SkillRecommendationService, agent_service_1.AgentService],
202
+ inject: [
203
+ rules_service_1.RulesService,
204
+ config_service_1.ConfigService,
205
+ skill_recommendation_service_1.SkillRecommendationService,
206
+ agent_service_1.AgentService,
207
+ agent_stack_service_1.AgentStackService,
208
+ ],
172
209
  },
173
210
  ],
174
211
  exports: [exports.KEYWORD_SERVICE],
@@ -2,6 +2,7 @@ import { type Mode, type RuleContent, type ParseModeResult, type KeywordModesCon
2
2
  import { type VerbosityLevel } from '../shared/verbosity.types';
3
3
  import { PrimaryAgentResolver } from './primary-agent-resolver';
4
4
  import { type ClientType } from '../shared/client-type';
5
+ import { type StackMatchInput } from '../agent/stack-matcher';
5
6
  export interface ParseModeOptions {
6
7
  recommendedActAgent?: string;
7
8
  context?: ResolutionContext;
@@ -19,6 +20,7 @@ export interface KeywordServiceOptions {
19
20
  getMaxIncludedSkillsFn?: () => Promise<number | null>;
20
21
  getClientTypeFn?: () => ClientType;
21
22
  trackRuleUsageFn?: (ruleNames: string[]) => void;
23
+ loadStacksFn?: () => Promise<StackMatchInput[]>;
22
24
  }
23
25
  export interface SkillRecommendationInfo {
24
26
  skillName: string;
@@ -54,6 +56,7 @@ export declare class KeywordService {
54
56
  private readonly getMaxIncludedSkillsFn?;
55
57
  private readonly getClientTypeFn?;
56
58
  private readonly trackRuleUsageFn?;
59
+ private readonly loadStacksFn?;
57
60
  private static readonly CONTEXT_SPECIALIST_PATTERNS;
58
61
  constructor(loadConfigFn: () => Promise<KeywordModesConfig>, loadRuleFn: (path: string) => Promise<string>, loadAgentInfoFn?: ((agentName: string) => Promise<unknown>) | undefined, options?: KeywordServiceOptions);
59
62
  parseMode(prompt: string, options?: ParseModeOptions): Promise<ParseModeResult>;
@@ -73,6 +76,8 @@ export declare class KeywordService {
73
76
  private addAutoConfigToResult;
74
77
  private addComplexityToResult;
75
78
  private addIncludedSkillsToResult;
79
+ private enforceRequiredAgentSkills;
80
+ private extractRequiredSkillNames;
76
81
  private getMaxIncludedSkills;
77
82
  private loadSkillsInParallel;
78
83
  private addIncludedAgentToResult;
@@ -12,9 +12,12 @@ const rule_filter_1 = require("./rule-filter");
12
12
  const skill_content_utils_1 = require("../skill/skill-content.utils");
13
13
  const agent_summary_utils_1 = require("../agent/agent-summary.utils");
14
14
  const rules_content_utils_1 = require("../rules/rules-content.utils");
15
+ const permission_forecast_1 = require("./permission-forecast");
15
16
  const keyword_core_1 = require("../shared/keyword-core");
16
17
  const taskmaestro_detector_1 = require("./taskmaestro-detector");
17
18
  const diff_analyzer_1 = require("./diff-analyzer");
19
+ const stack_matcher_1 = require("../agent/stack-matcher");
20
+ const planning_contract_1 = require("../mcp/handlers/planning-contract");
18
21
  const BASE_CONFIG = (0, keyword_core_1.getDefaultModeConfig)();
19
22
  const DEFAULT_CONFIG = {
20
23
  modes: {
@@ -109,6 +112,7 @@ class KeywordService {
109
112
  this.getMaxIncludedSkillsFn = options?.getMaxIncludedSkillsFn;
110
113
  this.getClientTypeFn = options?.getClientTypeFn;
111
114
  this.trackRuleUsageFn = options?.trackRuleUsageFn;
115
+ this.loadStacksFn = options?.loadStacksFn;
112
116
  this.cacheTTL = process.env.NODE_ENV === 'production' ? 3600000 : 300000;
113
117
  }
114
118
  async parseMode(prompt, options) {
@@ -187,13 +191,16 @@ class KeywordService {
187
191
  }
188
192
  const resolvedAgent = await this.resolvePrimaryAgent(mode, originalPrompt, modeConfig.delegates_to, options?.context, recommendedActAgent, diffAnalysis ?? undefined);
189
193
  await this.addAgentInfoToResult(result, resolvedAgent);
190
- this.addParallelAgentsToResult(result, mode, config, originalPrompt);
194
+ await this.addParallelAgentsToResult(result, mode, config, originalPrompt);
191
195
  await this.addActRecommendationToResult(result, mode, originalPrompt);
192
196
  this.addActivationMessageToResult(result, resolvedAgent);
193
197
  this.addDisplayInstructionToResult(result, mode);
194
198
  await this.addAutoConfigToResult(result, mode);
195
199
  this.addComplexityToResult(result, mode, originalPrompt);
196
200
  await this.addIncludedSkillsToResult(result, originalPrompt, options);
201
+ if (mode === 'PLAN' || mode === 'AUTO') {
202
+ await this.enforceRequiredAgentSkills(result, options);
203
+ }
197
204
  await this.addIncludedAgentToResult(result, mode, options);
198
205
  const taskmaestroInstalled = (0, taskmaestro_detector_1.isTaskmaestroAvailable)();
199
206
  result.availableStrategies = taskmaestroInstalled ? ['subagent', 'taskmaestro'] : ['subagent'];
@@ -202,6 +209,7 @@ class KeywordService {
202
209
  'TaskMaestro skill not found at ~/.claude/skills/taskmaestro/SKILL.md. To enable tmux-based parallel specialist execution, install the taskmaestro skill.';
203
210
  }
204
211
  this.addReleaseChecklistIfNeeded(result, mode, originalPrompt);
212
+ result.permissionForecast = (0, permission_forecast_1.generatePermissionForecast)(mode, originalPrompt);
205
213
  return result;
206
214
  }
207
215
  addReleaseChecklistIfNeeded(result, mode, originalPrompt) {
@@ -241,8 +249,8 @@ class KeywordService {
241
249
  result.delegate_agent_info = delegateAgentInfo;
242
250
  }
243
251
  }
244
- addParallelAgentsToResult(result, mode, config, originalPrompt) {
245
- const recommendation = this.getParallelAgentsRecommendation(mode, config, originalPrompt);
252
+ async addParallelAgentsToResult(result, mode, config, originalPrompt) {
253
+ const recommendation = await this.getParallelAgentsRecommendation(mode, config, originalPrompt);
246
254
  if (recommendation) {
247
255
  result.parallelAgentsRecommendation = recommendation;
248
256
  }
@@ -335,6 +343,58 @@ class KeywordService {
335
343
  this.logger.warn(`Failed to auto-include skills: ${error instanceof Error ? error.message : 'Unknown error'}`);
336
344
  }
337
345
  }
346
+ async enforceRequiredAgentSkills(result, options) {
347
+ if (!this.loadAgentInfoFn || !this.loadSkillContentFn || !result.delegates_to)
348
+ return;
349
+ try {
350
+ const agentData = await this.loadAgentInfoFn(result.delegates_to);
351
+ const requiredSkillNames = this.extractRequiredSkillNames(agentData);
352
+ if (requiredSkillNames.length === 0)
353
+ return;
354
+ const verbosity = options?.verbosity ?? 'standard';
355
+ if (!result.included_skills) {
356
+ result.included_skills = [];
357
+ }
358
+ for (const skillName of requiredSkillNames) {
359
+ const existingIndex = result.included_skills.findIndex(s => s.name === skillName);
360
+ const content = await this.loadSkillContentFn(skillName);
361
+ if (!content)
362
+ continue;
363
+ const enforcedSkill = {
364
+ name: content.name,
365
+ description: content.description,
366
+ content: verbosity === 'minimal' ? '' : content.content,
367
+ reason: 'Required by planning agent',
368
+ };
369
+ if (existingIndex >= 0) {
370
+ result.included_skills[existingIndex] = enforcedSkill;
371
+ }
372
+ else {
373
+ result.included_skills.push(enforcedSkill);
374
+ }
375
+ }
376
+ result.requiredSkillsEnforced = true;
377
+ result.requiredSkillNames = requiredSkillNames;
378
+ }
379
+ catch (error) {
380
+ this.logger.warn(`Failed to enforce required agent skills: ${error instanceof Error ? error.message : 'Unknown error'}`);
381
+ }
382
+ }
383
+ extractRequiredSkillNames(agentData) {
384
+ if (!agentData || typeof agentData !== 'object')
385
+ return [];
386
+ const agent = agentData;
387
+ const skills = agent.skills;
388
+ if (!skills || typeof skills !== 'object')
389
+ return [];
390
+ const required = skills.required;
391
+ if (!Array.isArray(required))
392
+ return [];
393
+ return required
394
+ .filter((s) => typeof s === 'object' && s !== null)
395
+ .map(s => s.name)
396
+ .filter((n) => typeof n === 'string');
397
+ }
338
398
  async getMaxIncludedSkills() {
339
399
  if (!this.getMaxIncludedSkillsFn)
340
400
  return keyword_types_1.DEFAULT_MAX_INCLUDED_SKILLS;
@@ -407,15 +467,34 @@ class KeywordService {
407
467
  expertise: summary.expertise,
408
468
  };
409
469
  }
470
+ const planningContract = (0, planning_contract_1.resolvePlanningContract)(mode, result.delegates_to);
471
+ if (planningContract) {
472
+ result.included_agent.planningContract = [...planningContract];
473
+ }
410
474
  this.logger.log(`Auto-included agent: ${agentPrompt.displayName} (verbosity: ${verbosity})`);
411
475
  }
412
476
  }
413
477
  getPrimaryAgentTier(agentName) {
414
478
  return keyword_types_1.ALL_PRIMARY_AGENTS.includes(agentName) ? 'primary' : 'specialist';
415
479
  }
416
- getParallelAgentsRecommendation(mode, config, prompt) {
480
+ async getParallelAgentsRecommendation(mode, config, prompt) {
417
481
  const modeConfig = config.modes[mode];
418
- const baseSpecialists = modeConfig?.defaultSpecialists ?? [];
482
+ const defaultSpecialists = modeConfig?.defaultSpecialists ?? [];
483
+ let stackResult = null;
484
+ let stackSpecialists;
485
+ if (prompt && this.loadStacksFn) {
486
+ try {
487
+ const stacks = await this.loadStacksFn();
488
+ stackResult = (0, stack_matcher_1.matchStack)(prompt, stacks);
489
+ if (stackResult) {
490
+ const matched = stacks.find(s => s.name === stackResult.stackName);
491
+ stackSpecialists = matched?.specialist_agents;
492
+ }
493
+ }
494
+ catch {
495
+ }
496
+ }
497
+ const baseSpecialists = stackSpecialists?.length ? stackSpecialists : defaultSpecialists;
419
498
  const contextSpecialists = prompt ? this.getContextAwareSpecialists(prompt) : [];
420
499
  const allSpecialists = [...new Set([...baseSpecialists, ...contextSpecialists])];
421
500
  if (allSpecialists.length === 0) {
@@ -432,10 +511,15 @@ class KeywordService {
432
511
  else {
433
512
  hint = `Use Task tool with subagent_type="general-purpose" and run_in_background=true for each specialist. Call prepare_parallel_agents MCP tool to get ready-to-use prompts.`;
434
513
  }
435
- return {
514
+ const result = {
436
515
  specialists: allSpecialists,
437
516
  hint,
438
517
  };
518
+ if (stackResult) {
519
+ result.suggestedStack = stackResult.stackName;
520
+ result.stackBased = true;
521
+ }
522
+ return result;
439
523
  }
440
524
  getContextAwareSpecialists(prompt) {
441
525
  const specialists = [];
@@ -1,10 +1,21 @@
1
1
  import type { DiffAnalysisResult } from './diff-analyzer';
2
2
  import type { CouncilPreset } from '../agent/council-preset.types';
3
- import type { CouncilSummary } from '../collaboration/council-summary.types';
4
3
  import type { ExecutionPlan } from '../agent/execution-plan.types';
5
4
  import type { TeamsCapabilityStatus } from '../agent/teams-capability.types';
6
5
  export declare const KEYWORDS: readonly ["PLAN", "ACT", "EVAL", "AUTO"];
7
6
  export type Mode = (typeof KEYWORDS)[number];
7
+ export type PermissionClass = 'read-only' | 'repo-write' | 'network' | 'destructive' | 'external';
8
+ export interface ApprovalBundle {
9
+ name: string;
10
+ actions: string[];
11
+ permissionClass: PermissionClass;
12
+ reason: string;
13
+ }
14
+ export interface PermissionForecast {
15
+ permissionClasses: PermissionClass[];
16
+ approvalBundles: ApprovalBundle[];
17
+ permissionSummary: string;
18
+ }
8
19
  export declare const MODE_AGENTS: readonly ["plan-mode", "act-mode", "eval-mode", "auto-mode"];
9
20
  export declare const PLAN_PRIMARY_AGENTS: readonly ["solution-architect", "technical-planner"];
10
21
  export declare const ACT_PRIMARY_AGENTS: readonly ["tooling-engineer", "platform-engineer", "data-engineer", "data-scientist", "ai-ml-engineer", "mobile-developer", "frontend-developer", "backend-developer", "devops-engineer", "agent-architect", "test-engineer", "security-engineer", "systems-developer", "software-engineer"];
@@ -45,6 +56,8 @@ export interface ParallelAgentRecommendation {
45
56
  specialists: string[];
46
57
  hint: string;
47
58
  dispatch?: DispatchStrength;
59
+ suggestedStack?: string;
60
+ stackBased?: boolean;
48
61
  }
49
62
  export interface IncludedSkill {
50
63
  name: string;
@@ -56,6 +69,7 @@ export interface IncludedAgent {
56
69
  name: string;
57
70
  systemPrompt: string;
58
71
  expertise: string[];
72
+ planningContract?: string[];
59
73
  }
60
74
  export type PrimaryAgentSource = 'explicit' | 'explicit_patterns' | 'config' | 'intent' | 'context' | 'default';
61
75
  export interface PrimaryAgentResolutionResult {
@@ -123,9 +137,11 @@ export interface ParseModeResult {
123
137
  visual?: VisualData;
124
138
  diffAnalysis?: DiffAnalysisResult;
125
139
  councilPreset?: CouncilPreset;
126
- councilSummary?: CouncilSummary;
140
+ requiredSkillsEnforced?: boolean;
141
+ requiredSkillNames?: string[];
127
142
  executionPlan?: ExecutionPlan;
128
143
  teamsCapability?: TeamsCapabilityStatus;
144
+ permissionForecast?: PermissionForecast;
129
145
  }
130
146
  export interface DispatchReadyParams {
131
147
  subagent_type: 'general-purpose';
@@ -165,6 +181,12 @@ export interface VisualData {
165
181
  collaboration: CollaborationConfig;
166
182
  }
167
183
  export type { DiffAnalysisResult, DiffAgentScore } from './diff-analyzer';
184
+ export interface ReviewContext {
185
+ detected: boolean;
186
+ pr_number: number;
187
+ issue_number?: number;
188
+ hint: string;
189
+ }
168
190
  export interface ModeConfig {
169
191
  description: string;
170
192
  instructions: string;
@@ -0,0 +1,2 @@
1
+ import type { Mode, PermissionForecast } from './keyword.types';
2
+ export declare function generatePermissionForecast(mode: Mode, prompt: string): PermissionForecast;
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generatePermissionForecast = generatePermissionForecast;
4
+ const SHIP_PATTERNS = /\b(ship|deploy|push|pr\b|pull\s*request|merge|release)\b/i;
5
+ const TEST_PATTERNS = /\b(tests?|lint|typecheck|format|check|verify|coverage)\b/i;
6
+ const INSTALL_PATTERNS = /\b(install|add\s+package|add\s+dependency|yarn\s+add|npm\s+install)\b/i;
7
+ const DELETE_PATTERNS = /\b(delete|remove|drop|reset|clean|destroy)\b/i;
8
+ const REVIEW_PATTERNS = /\b(review|comment|approve|feedback|pr\b|pull\s*request)\b/i;
9
+ const SHIP_BUNDLE = {
10
+ name: 'Ship changes',
11
+ actions: ['git add', 'git commit', 'git push', 'gh pr create'],
12
+ permissionClass: 'external',
13
+ reason: 'Committing, pushing, and creating a PR requires repo-write and GitHub API access',
14
+ };
15
+ const TEST_BUNDLE = {
16
+ name: 'Run checks',
17
+ actions: ['yarn test', 'yarn lint', 'yarn typecheck'],
18
+ permissionClass: 'read-only',
19
+ reason: 'Running tests and linters reads the codebase but does not modify it',
20
+ };
21
+ const INSTALL_BUNDLE = {
22
+ name: 'Install dependencies',
23
+ actions: ['yarn install', 'yarn add'],
24
+ permissionClass: 'network',
25
+ reason: 'Package installation fetches from external registries',
26
+ };
27
+ const REVIEW_BUNDLE = {
28
+ name: 'Review PR',
29
+ actions: ['gh pr review', 'gh pr comment'],
30
+ permissionClass: 'external',
31
+ reason: 'Reviewing or commenting on a PR uses the GitHub API',
32
+ };
33
+ const MODE_BASE_CLASSES = {
34
+ PLAN: ['read-only'],
35
+ ACT: ['read-only', 'repo-write'],
36
+ EVAL: ['read-only'],
37
+ AUTO: ['read-only', 'repo-write', 'external'],
38
+ };
39
+ function generatePermissionForecast(mode, prompt) {
40
+ const classes = new Set(MODE_BASE_CLASSES[mode]);
41
+ const bundles = [];
42
+ if (SHIP_PATTERNS.test(prompt)) {
43
+ classes.add('repo-write');
44
+ classes.add('external');
45
+ bundles.push(SHIP_BUNDLE);
46
+ }
47
+ if (TEST_PATTERNS.test(prompt)) {
48
+ bundles.push(TEST_BUNDLE);
49
+ }
50
+ if (INSTALL_PATTERNS.test(prompt)) {
51
+ classes.add('network');
52
+ bundles.push(INSTALL_BUNDLE);
53
+ }
54
+ if (DELETE_PATTERNS.test(prompt)) {
55
+ classes.add('destructive');
56
+ }
57
+ if (REVIEW_PATTERNS.test(prompt) && mode === 'EVAL') {
58
+ classes.add('external');
59
+ bundles.push(REVIEW_BUNDLE);
60
+ }
61
+ if (mode === 'ACT' && bundles.length === 0) {
62
+ bundles.push({
63
+ name: 'Code changes',
64
+ actions: ['file edit', 'git add', 'git commit'],
65
+ permissionClass: 'repo-write',
66
+ reason: 'Implementation involves editing files and creating commits',
67
+ });
68
+ }
69
+ const permissionClasses = sortPermissionClasses([...classes]);
70
+ return {
71
+ permissionClasses,
72
+ approvalBundles: bundles,
73
+ permissionSummary: buildSummary(mode, permissionClasses, bundles),
74
+ };
75
+ }
76
+ const CLASS_ORDER = [
77
+ 'read-only',
78
+ 'repo-write',
79
+ 'network',
80
+ 'destructive',
81
+ 'external',
82
+ ];
83
+ function sortPermissionClasses(classes) {
84
+ return [...classes].sort((a, b) => CLASS_ORDER.indexOf(a) - CLASS_ORDER.indexOf(b));
85
+ }
86
+ function buildSummary(mode, classes, bundles) {
87
+ const highest = classes[classes.length - 1];
88
+ const bundleNames = bundles.map(b => b.name).join(', ');
89
+ if (bundles.length === 0) {
90
+ return `${mode} mode requires ${highest} permissions`;
91
+ }
92
+ return `${mode} mode requires ${highest} permissions — expected actions: ${bundleNames}`;
93
+ }
94
+ //# sourceMappingURL=permission-forecast.js.map
@@ -160,7 +160,7 @@ let AgentHandler = class AgentHandler extends abstract_handler_1.AbstractHandler
160
160
  },
161
161
  agentStack: {
162
162
  type: 'string',
163
- description: 'Agent stack name to resolve primary + specialists from a preset (e.g., "api-development"). Overrides primaryAgent and specialists when provided.',
163
+ description: 'Agent stack name to resolve primary + specialists from a preset (e.g., "api-development"). Overrides primaryAgent and specialists when provided. Tip: use the suggestedStack value from parse_mode response to auto-select the best stack for the current context.',
164
164
  },
165
165
  inlineAgents: {
166
166
  type: 'object',
@@ -0,0 +1,22 @@
1
+ export declare const DEFAULT_QUESTION_BUDGET = 3;
2
+ export declare const MIN_PROMPT_LENGTH = 20;
3
+ export interface ClarificationOptions {
4
+ questionBudget?: number;
5
+ }
6
+ export interface ClarificationMetadata {
7
+ clarificationNeeded: boolean;
8
+ planReady: boolean;
9
+ questionBudget?: number;
10
+ nextQuestion?: string;
11
+ clarificationTopics?: string[];
12
+ assumptionNote?: string;
13
+ }
14
+ export declare function hasOverridePhrase(prompt: string): boolean;
15
+ export declare function hasVagueIntent(prompt: string): boolean;
16
+ export declare function hasTechnicalReference(prompt: string): boolean;
17
+ export declare const CLARIFICATION_TOPICS: {
18
+ readonly VAGUE_INTENT: "vague-intent";
19
+ readonly TARGET_ARTIFACT: "target-artifact";
20
+ readonly REQUEST_SCOPE: "request-scope";
21
+ };
22
+ export declare function evaluateClarification(prompt: string, options?: ClarificationOptions): ClarificationMetadata;