codingbuddy 5.4.1 → 5.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.
package/README.md CHANGED
@@ -20,6 +20,7 @@ npx codingbuddy init
20
20
  | Command | Description |
21
21
  |---------|-------------|
22
22
  | `codingbuddy init` | Analyze project and generate configuration |
23
+ | `codingbuddy init --team` | Auto-detect installed AI tools and generate adapter configs |
23
24
  | `codingbuddy mcp` | Start MCP server (stdio mode by default) |
24
25
  | `codingbuddy install <source>` | Install plugin from git URL or registry |
25
26
  | `codingbuddy search <query>` | Search plugins in registry |
@@ -42,6 +43,8 @@ npx codingbuddy init
42
43
 
43
44
  | Tool | Description |
44
45
  |------|-------------|
46
+ | `activate` | One-shot entry point returning rules, primary agent, specialists, and discussion format for a prompt. Preferred over `parse_mode` in Claude Code |
47
+ | `suggest_rules` | Analyze execution history for repeated failure patterns and generate draft rule suggestions. Proposed for human review — never auto-applied |
45
48
  | `get_project_config` | Get project configuration settings |
46
49
  | `search_rules` | Search through rules and guidelines |
47
50
  | `get_agent_details` | Get detailed profile of a specialist agent |
@@ -6,6 +6,7 @@ export declare class TeamsCapabilityService {
6
6
  constructor(configService: ConfigService);
7
7
  getStatus(): Promise<TeamsCapabilityStatus>;
8
8
  isAvailable(): Promise<boolean>;
9
+ private isClaudeCodeEnvironment;
9
10
  readEnvFlag(): boolean | undefined;
10
11
  private readConfigFlag;
11
12
  }
@@ -43,9 +43,18 @@ let TeamsCapabilityService = TeamsCapabilityService_1 = class TeamsCapabilitySer
43
43
  this.logger.debug(`Teams capability: ${status.reason}`);
44
44
  return status;
45
45
  }
46
+ if (this.isClaudeCodeEnvironment()) {
47
+ const status = {
48
+ available: true,
49
+ reason: 'Auto-enabled: Claude Code environment detected with native Teams support',
50
+ source: 'claude-native',
51
+ };
52
+ this.logger.debug(`Teams capability: ${status.reason}`);
53
+ return status;
54
+ }
46
55
  const status = {
47
56
  available: false,
48
- reason: 'Teams coordination is experimental and disabled by default',
57
+ reason: 'Teams coordination disabled by default for non-Claude Code hosts',
49
58
  source: 'default',
50
59
  };
51
60
  this.logger.debug(`Teams capability: ${status.reason}`);
@@ -55,6 +64,11 @@ let TeamsCapabilityService = TeamsCapabilityService_1 = class TeamsCapabilitySer
55
64
  const status = await this.getStatus();
56
65
  return status.available;
57
66
  }
67
+ isClaudeCodeEnvironment() {
68
+ return (process.env.CLAUDE_CODE === '1' ||
69
+ process.env.CLAUDE_CODE_ENTRYPOINT !== undefined ||
70
+ this.configService.getClientName() === 'claude-code');
71
+ }
58
72
  readEnvFlag() {
59
73
  const raw = process.env.CODINGBUDDY_TEAMS_ENABLED;
60
74
  if (raw === undefined || raw === '')
@@ -3,4 +3,4 @@ export interface TeamsCapabilityStatus {
3
3
  readonly reason: string;
4
4
  readonly source: TeamsCapabilitySource;
5
5
  }
6
- export type TeamsCapabilitySource = 'config' | 'environment' | 'default';
6
+ export type TeamsCapabilitySource = 'config' | 'environment' | 'claude-native' | 'default';
@@ -0,0 +1,22 @@
1
+ import type { ToolDefinition } from './base.handler';
2
+ import type { ToolResponse } from '../response.utils';
3
+ import { AbstractHandler } from './abstract-handler';
4
+ import { AgentService } from '../../agent/agent.service';
5
+ import { TeamsCapabilityService } from '../../agent/teams-capability.service';
6
+ import type { KeywordService } from '../../keyword/keyword.service';
7
+ import { RulesService } from '../../rules/rules.service';
8
+ export declare class ActivateHandler extends AbstractHandler {
9
+ private readonly keywordService;
10
+ private readonly agentService;
11
+ private readonly rulesService;
12
+ private readonly teamsCapability;
13
+ private readonly logger;
14
+ constructor(keywordService: KeywordService, agentService: AgentService, rulesService: RulesService, teamsCapability: TeamsCapabilityService);
15
+ protected getHandledTools(): string[];
16
+ protected handleTool(toolName: string, args: Record<string, unknown> | undefined): Promise<ToolResponse>;
17
+ getToolDefinitions(): ToolDefinition[];
18
+ private handleActivate;
19
+ private resolveMode;
20
+ private resolveSpecialists;
21
+ private getContextAwareSpecialists;
22
+ }
@@ -0,0 +1,260 @@
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 __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ var ActivateHandler_1;
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.ActivateHandler = void 0;
17
+ const common_1 = require("@nestjs/common");
18
+ const abstract_handler_1 = require("./abstract-handler");
19
+ const response_utils_1 = require("../response.utils");
20
+ const agent_service_1 = require("../../agent/agent.service");
21
+ const teams_capability_service_1 = require("../../agent/teams-capability.service");
22
+ const keyword_module_1 = require("../../keyword/keyword.module");
23
+ const keyword_types_1 = require("../../keyword/keyword.types");
24
+ const rules_service_1 = require("../../rules/rules.service");
25
+ const validation_constants_1 = require("../../shared/validation.constants");
26
+ const MODE_DEFAULT_AGENTS = {
27
+ PLAN: 'plan-mode-agent',
28
+ ACT: 'act-mode-agent',
29
+ EVAL: 'eval-mode-agent',
30
+ AUTO: 'auto-mode-agent',
31
+ };
32
+ const CONTEXT_SPECIALIST_PATTERNS = [
33
+ {
34
+ pattern: /보안|security|auth|인증|JWT|OAuth|XSS|CSRF|취약점|vulnerability/i,
35
+ specialist: 'security-specialist',
36
+ },
37
+ {
38
+ pattern: /접근성|accessibility|a11y|WCAG|aria|스크린\s*리더|screen\s*reader/i,
39
+ specialist: 'accessibility-specialist',
40
+ },
41
+ {
42
+ pattern: /성능|performance|최적화|optimiz|빠르게|느린|slow|fast|bundle\s*size|로딩/i,
43
+ specialist: 'performance-specialist',
44
+ },
45
+ {
46
+ pattern: /다국어|i18n|internationalization|번역|locale|translation|localization/i,
47
+ specialist: 'i18n-specialist',
48
+ },
49
+ {
50
+ pattern: /SEO|검색\s*엔진|search\s*engine|메타|meta\s*tag|sitemap|구조화\s*데이터/i,
51
+ specialist: 'seo-specialist',
52
+ },
53
+ {
54
+ pattern: /문서화|document|README|API\s*문서|JSDoc|주석|comment/i,
55
+ specialist: 'documentation-specialist',
56
+ },
57
+ {
58
+ pattern: /UI|UX|디자인|design\s*system|사용자\s*경험|user\s*experience|인터랙션/i,
59
+ specialist: 'ui-ux-designer',
60
+ },
61
+ {
62
+ pattern: /외부\s*서비스|external\s*(api|service)|webhook|웹훅|third-?party|circuit\s*breaker|retry\s*pattern|API\s*integration|서드파티|연동|SDK\s*wrapper/i,
63
+ specialist: 'integration-specialist',
64
+ },
65
+ {
66
+ pattern: /observability|관측성|distributed\s*trac|분산\s*추적|SLI|SLO|error\s*budget|OpenTelemetry|otel|Prometheus|Grafana|Jaeger|Zipkin|log\s*aggregat|로그\s*수집|alerting\s*strateg|알림\s*전략|메트릭\s*수집|metric\s*collect|tracing\s*infra|monitoring|대시보드|dashboard|logs?\s*manag/i,
67
+ specialist: 'observability-specialist',
68
+ },
69
+ {
70
+ pattern: /event[- ]?driven|이벤트\s*기반|message\s*queue|메시지\s*큐|Kafka|RabbitMQ|SQS|Azure\s*Service\s*Bus|event\s*sourc|CQRS|saga\s*pattern|분산\s*트랜잭션|distributed\s*transaction|pub\/?sub|dead\s*letter|DLQ|websocket|SSE|server[- ]?sent|real[- ]?time|실시간|async\s*messag|비동기\s*통신/i,
71
+ specialist: 'event-architecture-specialist',
72
+ },
73
+ {
74
+ pattern: /migration|마이그레이션|migrate|이전|legacy\s*(system|code|moderniz)|레거시|upgrade\s*(framework|version|library)|업그레이드|strangler\s*fig|branch\s*by\s*abstraction|blue[- ]?green|canary\s*(deploy|release)|rollback|롤백|api\s*version|deprecat|dual[- ]?write|backward\s*compatib|호환성|zero[- ]?downtime|data\s*migration|데이터\s*마이그레이션|schema\s*migration|스키마\s*변경|cutover|전환/i,
75
+ specialist: 'migration-specialist',
76
+ },
77
+ ];
78
+ const SPECIALIST_DOMAIN_MAP = {
79
+ 'security-specialist': 'security',
80
+ 'accessibility-specialist': 'accessibility',
81
+ 'performance-specialist': 'performance',
82
+ 'i18n-specialist': 'i18n',
83
+ 'seo-specialist': 'seo',
84
+ 'documentation-specialist': 'documentation',
85
+ 'ui-ux-designer': 'ui-ux',
86
+ 'integration-specialist': 'integration',
87
+ 'observability-specialist': 'observability',
88
+ 'event-architecture-specialist': 'event-architecture',
89
+ 'migration-specialist': 'migration',
90
+ 'architecture-specialist': 'architecture',
91
+ 'test-strategy-specialist': 'testing',
92
+ 'code-quality-specialist': 'code-quality',
93
+ };
94
+ let ActivateHandler = ActivateHandler_1 = class ActivateHandler extends abstract_handler_1.AbstractHandler {
95
+ constructor(keywordService, agentService, rulesService, teamsCapability) {
96
+ super();
97
+ this.keywordService = keywordService;
98
+ this.agentService = agentService;
99
+ this.rulesService = rulesService;
100
+ this.teamsCapability = teamsCapability;
101
+ this.logger = new common_1.Logger(ActivateHandler_1.name);
102
+ }
103
+ getHandledTools() {
104
+ return ['activate'];
105
+ }
106
+ async handleTool(toolName, args) {
107
+ switch (toolName) {
108
+ case 'activate':
109
+ return this.handleActivate(args);
110
+ default:
111
+ return (0, response_utils_1.createErrorResponse)(`Unknown tool: ${toolName}`);
112
+ }
113
+ }
114
+ getToolDefinitions() {
115
+ return [
116
+ {
117
+ name: 'activate',
118
+ description: 'One-shot entry point for collective intelligence workflow. ' +
119
+ 'Combines rule loading, primary agent resolution, specialist recommendation, ' +
120
+ 'and prompt generation in a single call — replacing the multi-step ' +
121
+ 'parse_mode + dispatch_agents ceremony. Returns everything needed to ' +
122
+ 'run a specialist council via Claude native Teams.',
123
+ inputSchema: {
124
+ type: 'object',
125
+ properties: {
126
+ prompt: {
127
+ type: 'string',
128
+ description: 'Task description. May start with a mode keyword (PLAN/ACT/EVAL/AUTO) ' +
129
+ 'which will be auto-detected, or use the explicit mode parameter.',
130
+ },
131
+ mode: {
132
+ type: 'string',
133
+ enum: ['PLAN', 'ACT', 'EVAL', 'AUTO'],
134
+ description: 'Explicit workflow mode. If omitted, auto-detected from prompt keywords.',
135
+ },
136
+ primaryAgent: {
137
+ type: 'string',
138
+ description: 'Explicit primary agent name. If omitted, uses mode default.',
139
+ },
140
+ },
141
+ required: ['prompt'],
142
+ },
143
+ },
144
+ ];
145
+ }
146
+ async handleActivate(args) {
147
+ const prompt = (0, validation_constants_1.extractRequiredString)(args, 'prompt');
148
+ if (prompt === null) {
149
+ return (0, response_utils_1.createErrorResponse)('Missing required parameter: prompt');
150
+ }
151
+ const explicitMode = (0, validation_constants_1.extractOptionalString)(args, 'mode');
152
+ const { mode, taskPrompt } = this.resolveMode(prompt, explicitMode);
153
+ const rules = await this.keywordService.getRulesForMode(mode, 'standard');
154
+ const primaryAgentName = (0, validation_constants_1.extractOptionalString)(args, 'primaryAgent') ?? MODE_DEFAULT_AGENTS[mode];
155
+ let primaryAgent = null;
156
+ try {
157
+ const agentPrompt = await this.agentService.getAgentSystemPrompt(primaryAgentName, {
158
+ mode,
159
+ taskDescription: taskPrompt,
160
+ });
161
+ primaryAgent = {
162
+ name: primaryAgentName,
163
+ displayName: agentPrompt.displayName,
164
+ systemPrompt: agentPrompt.systemPrompt,
165
+ };
166
+ }
167
+ catch (error) {
168
+ this.logger.warn(`Failed to resolve primary agent '${primaryAgentName}': ${error instanceof Error ? error.message : 'Unknown'}`);
169
+ }
170
+ const specialists = await this.resolveSpecialists(mode, taskPrompt);
171
+ const specialistResults = [];
172
+ if (specialists.length > 0) {
173
+ try {
174
+ const prepared = await this.agentService.prepareParallelAgents(mode, specialists, undefined, taskPrompt, 'full');
175
+ for (const agent of prepared.agents) {
176
+ specialistResults.push({
177
+ name: agent.id,
178
+ displayName: agent.displayName,
179
+ prompt: agent.taskPrompt ?? agent.description ?? '',
180
+ domain: SPECIALIST_DOMAIN_MAP[agent.id] ?? agent.id,
181
+ });
182
+ }
183
+ }
184
+ catch (error) {
185
+ this.logger.warn(`Failed to prepare specialists: ${error instanceof Error ? error.message : 'Unknown'}`);
186
+ }
187
+ }
188
+ const teamsStatus = await this.teamsCapability.getStatus();
189
+ return (0, response_utils_1.createJsonResponse)({
190
+ mode,
191
+ rules: rules.map(r => ({ name: r.name, content: r.content })),
192
+ primaryAgent,
193
+ specialists: specialistResults,
194
+ discussion: {
195
+ format: 'Each specialist: approve|concern|reject + reasoning + suggestedChanges',
196
+ consensus: 'No rejections = consensus reached',
197
+ crossReview: "Specialists review each other's opinions",
198
+ },
199
+ nativeIntegration: {
200
+ teams: teamsStatus.available
201
+ ? 'Use Claude native Teams to run specialists as teammates for real-time debate'
202
+ : 'Enable Teams via CODINGBUDDY_TEAMS_ENABLED=true or experimental.teamsCoordination config',
203
+ memory: 'Use Claude Code Memory for context persistence across sessions',
204
+ orchestration: 'Host manages mode transitions, clarification, permissions natively',
205
+ },
206
+ });
207
+ }
208
+ resolveMode(prompt, explicitMode) {
209
+ if (explicitMode && (0, validation_constants_1.isValidMode)(explicitMode)) {
210
+ return { mode: explicitMode, taskPrompt: prompt };
211
+ }
212
+ const trimmed = prompt.trim();
213
+ const keywordRegex = /^([^\s::]+)\s*[::]?\s*(.*)$/s;
214
+ const match = trimmed.match(keywordRegex);
215
+ if (match?.[1]) {
216
+ const candidate = match[1];
217
+ const upper = candidate.toUpperCase();
218
+ const rest = (match[2] ?? '').trim();
219
+ if (keyword_types_1.KEYWORDS.includes(upper)) {
220
+ return { mode: upper, taskPrompt: rest || trimmed };
221
+ }
222
+ const localized = keyword_types_1.LOCALIZED_KEYWORD_MAP[candidate] ?? keyword_types_1.LOCALIZED_KEYWORD_MAP[upper];
223
+ if (localized) {
224
+ return { mode: localized, taskPrompt: rest || trimmed };
225
+ }
226
+ }
227
+ return { mode: 'PLAN', taskPrompt: trimmed };
228
+ }
229
+ async resolveSpecialists(mode, prompt) {
230
+ let defaultSpecialists = [];
231
+ try {
232
+ const configContent = await this.rulesService.getRuleContent('keyword-modes.json');
233
+ const config = JSON.parse(configContent);
234
+ defaultSpecialists = config.modes[mode]?.defaultSpecialists ?? [];
235
+ }
236
+ catch {
237
+ this.logger.debug('Failed to load keyword-modes.json for default specialists');
238
+ }
239
+ const contextSpecialists = this.getContextAwareSpecialists(prompt);
240
+ return [...new Set([...defaultSpecialists, ...contextSpecialists])];
241
+ }
242
+ getContextAwareSpecialists(prompt) {
243
+ const specialists = [];
244
+ for (const { pattern, specialist } of CONTEXT_SPECIALIST_PATTERNS) {
245
+ if (pattern.test(prompt)) {
246
+ specialists.push(specialist);
247
+ }
248
+ }
249
+ return specialists;
250
+ }
251
+ };
252
+ exports.ActivateHandler = ActivateHandler;
253
+ exports.ActivateHandler = ActivateHandler = ActivateHandler_1 = __decorate([
254
+ (0, common_1.Injectable)(),
255
+ __param(0, (0, common_1.Inject)(keyword_module_1.KEYWORD_SERVICE)),
256
+ __metadata("design:paramtypes", [Function, agent_service_1.AgentService,
257
+ rules_service_1.RulesService,
258
+ teams_capability_service_1.TeamsCapabilityService])
259
+ ], ActivateHandler);
260
+ //# sourceMappingURL=activate.handler.js.map
@@ -44,7 +44,7 @@ let BriefingHandler = BriefingHandler_1 = class BriefingHandler extends abstract
44
44
  return [
45
45
  {
46
46
  name: 'create_briefing',
47
- description: 'Capture current session state into a briefing document for cross-session recovery',
47
+ description: '[DEPRECATED — Use Claude Code Memory for cross-session context] Capture current session state into a briefing document for cross-session recovery.',
48
48
  inputSchema: {
49
49
  type: 'object',
50
50
  properties: {
@@ -81,7 +81,7 @@ let ChecklistContextHandler = class ChecklistContextHandler extends abstract_han
81
81
  },
82
82
  {
83
83
  name: 'analyze_task',
84
- description: 'Analyze a task to provide contextual recommendations including risk assessment, relevant checklists, specialist recommendations, and workflow suggestions. Use this at the start of PLAN mode to get comprehensive task analysis.',
84
+ description: '[DEPRECATED — Use activate + /dream instead] Analyze a task to provide contextual recommendations including risk assessment, relevant checklists, specialist recommendations, and workflow suggestions.',
85
85
  inputSchema: {
86
86
  type: 'object',
87
87
  properties: {
@@ -43,7 +43,7 @@ let ContextDocumentHandler = class ContextDocumentHandler extends abstract_handl
43
43
  return [
44
44
  {
45
45
  name: 'read_context',
46
- description: `Read the current context document from ${context_document_types_1.CONTEXT_FILE_PATH}. Returns all accumulated context from PLAN/ACT/EVAL modes including decisions, notes, and recommended agents. Supports verbosity levels for controlling returned data size.`,
46
+ description: `[DEPRECATED — Use Claude Code Memory instead for cross-session context persistence] Read the current context document from ${context_document_types_1.CONTEXT_FILE_PATH}. Returns all accumulated context from PLAN/ACT/EVAL modes including decisions, notes, and recommended agents. Supports verbosity levels for controlling returned data size.`,
47
47
  inputSchema: {
48
48
  type: 'object',
49
49
  properties: {
@@ -62,7 +62,7 @@ let ContextDocumentHandler = class ContextDocumentHandler extends abstract_handl
62
62
  },
63
63
  {
64
64
  name: 'update_context',
65
- description: `MANDATORY: Update the context document at ${context_document_types_1.CONTEXT_FILE_PATH}.
65
+ description: `[DEPRECATED — Use Claude Code Memory instead for cross-session context persistence] Update the context document at ${context_document_types_1.CONTEXT_FILE_PATH}.
66
66
  - PLAN mode: Resets (clears) existing content and starts fresh.
67
67
  - ACT/EVAL modes: Appends new section to existing content (requires PLAN first).
68
68
  - Automatic cleanup: If document exceeds size threshold, older sections are automatically summarized.
@@ -47,12 +47,11 @@ let DiscussionHandler = class DiscussionHandler extends abstract_handler_1.Abstr
47
47
  return [
48
48
  {
49
49
  name: 'agent_discussion',
50
- description: '[EXPERIMENTAL disabled by default] Produces templated synthesis of ' +
51
- 'specialist opinions, not real specialist execution. The output does ' +
52
- 'not reflect collective intelligence from live agents; treat it as a ' +
53
- 'placeholder until the rebuild lands. Enable by setting ' +
54
- `${discussion_types_1.EXPERIMENTAL_DISCUSSION_ENV}=1. When disabled, the tool returns ` +
55
- '{disabled: true} without invoking synthesis.',
50
+ description: 'Structured specialist discussion for collective intelligence workflows. ' +
51
+ 'Collects and synthesizes opinions from multiple specialist agents on a topic. ' +
52
+ 'For real-time specialist debate, use the `activate` tool with Claude native Teams. ' +
53
+ `Set ${discussion_types_1.EXPERIMENTAL_DISCUSSION_ENV}=1 to enable built-in synthesis. ` +
54
+ 'When unset, returns guidance on using activate + Teams instead.',
56
55
  inputSchema: {
57
56
  type: 'object',
58
57
  properties: {
@@ -17,8 +17,8 @@ export interface DiscussionResult {
17
17
  }
18
18
  export declare const VALID_SEVERITIES: readonly OpinionSeverity[];
19
19
  export declare const EXPERIMENTAL_DISCUSSION_ENV = "CODINGBUDDY_EXPERIMENTAL_DISCUSSION";
20
- export declare const EXPERIMENTAL_DISCUSSION_WARNING = "\u26A0\uFE0F experimental \u2014 templated synthesis, not real specialist execution";
21
- export declare const DISABLED_DISCUSSION_REASON = "templated synthesis not aligned with collective intelligence promise";
20
+ export declare const EXPERIMENTAL_DISCUSSION_WARNING = "Built-in synthesis \u2014 for real specialist debate, use activate + Claude native Teams";
21
+ export declare const DISABLED_DISCUSSION_REASON: string;
22
22
  export interface DisabledDiscussionResult {
23
23
  disabled: true;
24
24
  reason: string;
@@ -9,6 +9,7 @@ exports.VALID_SEVERITIES = [
9
9
  'critical',
10
10
  ];
11
11
  exports.EXPERIMENTAL_DISCUSSION_ENV = 'CODINGBUDDY_EXPERIMENTAL_DISCUSSION';
12
- exports.EXPERIMENTAL_DISCUSSION_WARNING = '\u26a0\ufe0f experimentaltemplated synthesis, not real specialist execution';
13
- exports.DISABLED_DISCUSSION_REASON = 'templated synthesis not aligned with collective intelligence promise';
12
+ exports.EXPERIMENTAL_DISCUSSION_WARNING = 'Built-in synthesisfor real specialist debate, use activate + Claude native Teams';
13
+ exports.DISABLED_DISCUSSION_REASON = 'Use the `activate` tool with Claude native Teams for real specialist debate. ' +
14
+ 'Set CODINGBUDDY_EXPERIMENTAL_DISCUSSION=1 to enable built-in synthesis as an alternative.';
14
15
  //# sourceMappingURL=discussion.types.js.map
@@ -21,4 +21,6 @@ export { BriefingHandler } from './briefing.handler';
21
21
  export { ResumeHandler } from './resume.handler';
22
22
  export { RuleImpactHandler } from './rule-impact.handler';
23
23
  export { ReviewPrHandler } from './review-pr.handler';
24
+ export { ActivateHandler } from './activate.handler';
25
+ export { SuggestRulesHandler } from './suggest-rules.handler';
24
26
  export declare const TOOL_HANDLERS = "TOOL_HANDLERS";
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TOOL_HANDLERS = exports.ReviewPrHandler = exports.RuleImpactHandler = exports.ResumeHandler = exports.BriefingHandler = exports.QualityReportHandler = exports.ReleaseCheckHandler = exports.PluginValidationHandler = exports.ImpactHandler = exports.RuleInsightsHandler = exports.ContextArchiveHandler = exports.PipelineHandler = exports.ParallelValidationHandler = exports.DiscussionHandler = exports.TuiHandler = exports.ContextDocumentHandler = exports.ConventionsHandler = exports.ChecklistContextHandler = exports.ModeHandler = exports.AgentHandler = exports.SkillHandler = exports.ConfigHandler = exports.RulesHandler = void 0;
3
+ exports.TOOL_HANDLERS = exports.SuggestRulesHandler = exports.ActivateHandler = exports.ReviewPrHandler = exports.RuleImpactHandler = exports.ResumeHandler = exports.BriefingHandler = exports.QualityReportHandler = exports.ReleaseCheckHandler = exports.PluginValidationHandler = exports.ImpactHandler = exports.RuleInsightsHandler = exports.ContextArchiveHandler = exports.PipelineHandler = exports.ParallelValidationHandler = exports.DiscussionHandler = exports.TuiHandler = exports.ContextDocumentHandler = exports.ConventionsHandler = exports.ChecklistContextHandler = exports.ModeHandler = exports.AgentHandler = exports.SkillHandler = exports.ConfigHandler = exports.RulesHandler = void 0;
4
4
  var rules_handler_1 = require("./rules.handler");
5
5
  Object.defineProperty(exports, "RulesHandler", { enumerable: true, get: function () { return rules_handler_1.RulesHandler; } });
6
6
  var config_handler_1 = require("./config.handler");
@@ -45,5 +45,9 @@ var rule_impact_handler_1 = require("./rule-impact.handler");
45
45
  Object.defineProperty(exports, "RuleImpactHandler", { enumerable: true, get: function () { return rule_impact_handler_1.RuleImpactHandler; } });
46
46
  var review_pr_handler_1 = require("./review-pr.handler");
47
47
  Object.defineProperty(exports, "ReviewPrHandler", { enumerable: true, get: function () { return review_pr_handler_1.ReviewPrHandler; } });
48
+ var activate_handler_1 = require("./activate.handler");
49
+ Object.defineProperty(exports, "ActivateHandler", { enumerable: true, get: function () { return activate_handler_1.ActivateHandler; } });
50
+ var suggest_rules_handler_1 = require("./suggest-rules.handler");
51
+ Object.defineProperty(exports, "SuggestRulesHandler", { enumerable: true, get: function () { return suggest_rules_handler_1.SuggestRulesHandler; } });
48
52
  exports.TOOL_HANDLERS = 'TOOL_HANDLERS';
49
53
  //# sourceMappingURL=index.js.map
@@ -40,7 +40,7 @@ let ResumeHandler = ResumeHandler_1 = class ResumeHandler extends abstract_handl
40
40
  return [
41
41
  {
42
42
  name: 'resume_session',
43
- description: 'Load a previous session briefing to restore context and continue work',
43
+ description: '[DEPRECATED — Use Claude Code Memory for session resume] Load a previous session briefing to restore context and continue work.',
44
44
  inputSchema: {
45
45
  type: 'object',
46
46
  properties: {
@@ -16,6 +16,7 @@ export declare class RuleImpactHandler extends AbstractHandler {
16
16
  private appendSummary;
17
17
  private appendTopRules;
18
18
  private appendDomainCoverage;
19
+ private appendEffectivenessScores;
19
20
  private appendUnusedRules;
20
21
  private appendTrends;
21
22
  private appendSuggestions;
@@ -104,6 +104,7 @@ let RuleImpactHandler = class RuleImpactHandler extends abstract_handler_1.Abstr
104
104
  this.appendSummary(lines, insights);
105
105
  this.appendTopRules(lines, insights);
106
106
  this.appendDomainCoverage(lines, insights);
107
+ this.appendEffectivenessScores(lines, insights);
107
108
  this.appendUnusedRules(lines, insights);
108
109
  this.appendTrends(lines, insights);
109
110
  this.appendSuggestions(lines, insights);
@@ -169,6 +170,24 @@ let RuleImpactHandler = class RuleImpactHandler extends abstract_handler_1.Abstr
169
170
  }
170
171
  lines.push('');
171
172
  }
173
+ appendEffectivenessScores(lines, insights) {
174
+ const scores = insights.effectivenessScores;
175
+ if (!scores || scores.length === 0)
176
+ return;
177
+ lines.push('## Auto-Generated Rule Effectiveness');
178
+ lines.push('');
179
+ lines.push('| Rule | Baseline | Current | Reduction | Verdict |');
180
+ lines.push('| --- | ---: | ---: | ---: | --- |');
181
+ for (const score of scores) {
182
+ const verdictBadge = score.verdict === 'effective'
183
+ ? '**EFFECTIVE**'
184
+ : score.verdict === 'needs-review'
185
+ ? 'needs-review'
186
+ : '_ineffective_';
187
+ lines.push(`| ${score.ruleName} | ${(score.baselineFailureRate * 100).toFixed(1)}% | ${(score.currentFailureRate * 100).toFixed(1)}% | ${score.reductionPercent}% | ${verdictBadge} |`);
188
+ }
189
+ lines.push('');
190
+ }
172
191
  appendUnusedRules(lines, insights) {
173
192
  lines.push('## Unused Rules');
174
193
  lines.push('');
@@ -0,0 +1,10 @@
1
+ import { AbstractHandler } from './abstract-handler';
2
+ import type { ToolDefinition } from './base.handler';
3
+ import type { ToolResponse } from '../response.utils';
4
+ export declare class SuggestRulesHandler extends AbstractHandler {
5
+ protected getHandledTools(): string[];
6
+ protected handleTool(_toolName: string, args: Record<string, unknown> | undefined): Promise<ToolResponse>;
7
+ getToolDefinitions(): ToolDefinition[];
8
+ private buildPipelineArgs;
9
+ private runPipeline;
10
+ }
@@ -0,0 +1,105 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.SuggestRulesHandler = void 0;
10
+ const common_1 = require("@nestjs/common");
11
+ const child_process_1 = require("child_process");
12
+ const path_1 = require("path");
13
+ const abstract_handler_1 = require("./abstract-handler");
14
+ const response_utils_1 = require("../response.utils");
15
+ const validation_constants_1 = require("../../shared/validation.constants");
16
+ const PIPELINE_SCRIPT = (0, path_1.join)(__dirname, '..', '..', '..', '..', 'packages', 'claude-code-plugin', 'hooks', 'lib', 'suggest_pipeline.py');
17
+ const PIPELINE_TIMEOUT_MS = 30_000;
18
+ let SuggestRulesHandler = class SuggestRulesHandler extends abstract_handler_1.AbstractHandler {
19
+ getHandledTools() {
20
+ return ['suggest_rules'];
21
+ }
22
+ async handleTool(_toolName, args) {
23
+ const pipelineArgs = this.buildPipelineArgs(args);
24
+ let stdout;
25
+ try {
26
+ stdout = await this.runPipeline(pipelineArgs);
27
+ }
28
+ catch (err) {
29
+ const message = err instanceof Error ? err.message : String(err);
30
+ return (0, response_utils_1.createErrorResponse)(`Pipeline execution failed: ${message}`);
31
+ }
32
+ let suggestions;
33
+ try {
34
+ suggestions = JSON.parse(stdout);
35
+ }
36
+ catch {
37
+ return (0, response_utils_1.createErrorResponse)(`Failed to parse pipeline output: ${stdout.slice(0, 200)}`);
38
+ }
39
+ return (0, response_utils_1.createJsonResponse)({
40
+ generatedAt: Date.now(),
41
+ count: suggestions.length,
42
+ suggestions,
43
+ });
44
+ }
45
+ getToolDefinitions() {
46
+ return [
47
+ {
48
+ name: 'suggest_rules',
49
+ description: 'Analyze execution history for repeated failure patterns and generate draft rule suggestions. ' +
50
+ 'Rules are proposed for human review — never auto-applied. ' +
51
+ 'Powered by PatternDetector → RuleSuggester pipeline.',
52
+ inputSchema: {
53
+ type: 'object',
54
+ properties: {
55
+ minOccurrences: {
56
+ type: 'number',
57
+ description: 'Minimum number of failures to count as a pattern (default: 3)',
58
+ },
59
+ days: {
60
+ type: 'number',
61
+ description: 'How many days back to search (default: 30)',
62
+ },
63
+ dbPath: {
64
+ type: 'string',
65
+ description: 'Path to history.db file. Defaults to ~/.codingbuddy/history.db',
66
+ },
67
+ },
68
+ required: [],
69
+ },
70
+ },
71
+ ];
72
+ }
73
+ buildPipelineArgs(args) {
74
+ const pipelineArgs = [];
75
+ const dbPath = (0, validation_constants_1.extractOptionalString)(args, 'dbPath');
76
+ if (dbPath) {
77
+ pipelineArgs.push('--db-path', dbPath);
78
+ }
79
+ const minOccurrences = args?.minOccurrences;
80
+ if (typeof minOccurrences === 'number' && Number.isInteger(minOccurrences)) {
81
+ pipelineArgs.push('--min-occurrences', String(minOccurrences));
82
+ }
83
+ const days = args?.days;
84
+ if (typeof days === 'number' && Number.isInteger(days)) {
85
+ pipelineArgs.push('--days', String(days));
86
+ }
87
+ return pipelineArgs;
88
+ }
89
+ runPipeline(pipelineArgs) {
90
+ return new Promise((resolve, reject) => {
91
+ (0, child_process_1.execFile)('python3', [PIPELINE_SCRIPT, ...pipelineArgs], { timeout: PIPELINE_TIMEOUT_MS }, (err, stdout, stderr) => {
92
+ if (err) {
93
+ reject(new Error(`${err.message}${stderr ? `: ${stderr}` : ''}`));
94
+ return;
95
+ }
96
+ resolve(stdout.trim());
97
+ });
98
+ });
99
+ }
100
+ };
101
+ exports.SuggestRulesHandler = SuggestRulesHandler;
102
+ exports.SuggestRulesHandler = SuggestRulesHandler = __decorate([
103
+ (0, common_1.Injectable)()
104
+ ], SuggestRulesHandler);
105
+ //# sourceMappingURL=suggest-rules.handler.js.map
@@ -50,6 +50,8 @@ const handlers = [
50
50
  handlers_1.BriefingHandler,
51
51
  handlers_1.ResumeHandler,
52
52
  handlers_1.RuleImpactHandler,
53
+ handlers_1.ActivateHandler,
54
+ handlers_1.SuggestRulesHandler,
53
55
  ];
54
56
  let McpModule = class McpModule {
55
57
  };
@@ -1,4 +1,11 @@
1
1
  import type { RuleStats } from './rule-tracker';
2
+ export interface EffectivenessScore {
3
+ ruleName: string;
4
+ baselineFailureRate: number;
5
+ currentFailureRate: number;
6
+ reductionPercent: number;
7
+ verdict: 'effective' | 'needs-review' | 'ineffective';
8
+ }
2
9
  export interface RuleInsight {
3
10
  generatedAt: number;
4
11
  summary: {
@@ -20,6 +27,7 @@ export interface RuleInsight {
20
27
  emerging: string[];
21
28
  };
22
29
  suggestions: string[];
30
+ effectivenessScores: EffectivenessScore[];
23
31
  }
24
32
  export declare class RuleInsightsService {
25
33
  generateInsights(stats: Record<string, RuleStats>, allRuleNames: string[], now?: number): RuleInsight;
@@ -27,5 +35,6 @@ export declare class RuleInsightsService {
27
35
  private classifyFrequency;
28
36
  private findUnusedRules;
29
37
  private analyzeTrends;
38
+ private computeEffectivenessScores;
30
39
  private generateSuggestions;
31
40
  }
@@ -13,6 +13,8 @@ const MONTH_MS = 30 * 24 * 60 * 60 * 1000;
13
13
  const TOP_RULES_LIMIT = 10;
14
14
  const EMERGING_THRESHOLD = 3;
15
15
  const SUGGESTION_UNUSED_PREVIEW = 5;
16
+ const EFFECTIVE_THRESHOLD = 50;
17
+ const NEEDS_REVIEW_THRESHOLD = 10;
16
18
  let RuleInsightsService = class RuleInsightsService {
17
19
  generateInsights(stats, allRuleNames, now = Date.now()) {
18
20
  const entries = Object.entries(stats);
@@ -24,6 +26,7 @@ let RuleInsightsService = class RuleInsightsService {
24
26
  const staleRules = entries.filter(([, s]) => now - s.lastUsed > MONTH_MS).length;
25
27
  const trends = this.analyzeTrends(entries, avgCount, now);
26
28
  const suggestions = this.generateSuggestions(topRules, unusedRules, trends.declining, entries.length);
29
+ const effectivenessScores = this.computeEffectivenessScores(entries);
27
30
  return {
28
31
  generatedAt: now,
29
32
  summary: {
@@ -36,6 +39,7 @@ let RuleInsightsService = class RuleInsightsService {
36
39
  unusedRules,
37
40
  trends,
38
41
  suggestions,
42
+ effectivenessScores,
39
43
  };
40
44
  }
41
45
  buildTopRules(entries, avgCount) {
@@ -75,6 +79,37 @@ let RuleInsightsService = class RuleInsightsService {
75
79
  .map(([name]) => name);
76
80
  return { recentlyActive, declining, emerging };
77
81
  }
82
+ computeEffectivenessScores(entries) {
83
+ const scores = [];
84
+ for (const [name, stat] of entries) {
85
+ if (!stat.generatedRule)
86
+ continue;
87
+ if (typeof stat.baselineFailureRate !== 'number' ||
88
+ typeof stat.currentFailureRate !== 'number')
89
+ continue;
90
+ const baseline = stat.baselineFailureRate;
91
+ const current = stat.currentFailureRate;
92
+ const reductionPercent = baseline > 0 ? ((baseline - current) / baseline) * 100 : 0;
93
+ let verdict;
94
+ if (reductionPercent >= EFFECTIVE_THRESHOLD) {
95
+ verdict = 'effective';
96
+ }
97
+ else if (reductionPercent >= NEEDS_REVIEW_THRESHOLD) {
98
+ verdict = 'needs-review';
99
+ }
100
+ else {
101
+ verdict = 'ineffective';
102
+ }
103
+ scores.push({
104
+ ruleName: name,
105
+ baselineFailureRate: baseline,
106
+ currentFailureRate: current,
107
+ reductionPercent: Math.round(reductionPercent * 10) / 10,
108
+ verdict,
109
+ });
110
+ }
111
+ return scores;
112
+ }
78
113
  generateSuggestions(topRules, unusedRules, declining, totalTracked) {
79
114
  const suggestions = [];
80
115
  if (totalTracked === 0) {
@@ -1,6 +1,9 @@
1
1
  export interface RuleStats {
2
2
  count: number;
3
3
  lastUsed: number;
4
+ generatedRule?: boolean;
5
+ baselineFailureRate?: number;
6
+ currentFailureRate?: number;
4
7
  }
5
8
  export interface RuleEffectivenessReport {
6
9
  totalRulesTracked: number;
@@ -17,6 +20,8 @@ export declare class RuleTracker {
17
20
  constructor(statsPath: string);
18
21
  static fromFile(statsPath: string): Promise<RuleTracker>;
19
22
  trackRuleUsage(ruleNames: string[]): void;
23
+ markRuleAsGenerated(ruleName: string): void;
24
+ recordFailureRate(ruleName: string, rate: number, kind: 'baseline' | 'current'): void;
20
25
  getStats(): Record<string, RuleStats>;
21
26
  detectUnusedRules(allRuleNames: string[], thresholdDays: number): string[];
22
27
  generateReport(): RuleEffectivenessReport;
@@ -33,6 +33,27 @@ class RuleTracker {
33
33
  }
34
34
  }
35
35
  }
36
+ markRuleAsGenerated(ruleName) {
37
+ if (!ruleName)
38
+ return;
39
+ if (!this.stats[ruleName]) {
40
+ this.stats[ruleName] = { count: 0, lastUsed: Date.now() };
41
+ }
42
+ this.stats[ruleName].generatedRule = true;
43
+ }
44
+ recordFailureRate(ruleName, rate, kind) {
45
+ if (!ruleName)
46
+ return;
47
+ const stat = this.stats[ruleName];
48
+ if (!stat || !stat.generatedRule)
49
+ return;
50
+ if (kind === 'baseline') {
51
+ stat.baselineFailureRate = rate;
52
+ }
53
+ else {
54
+ stat.currentFailureRate = rate;
55
+ }
56
+ }
36
57
  getStats() {
37
58
  return JSON.parse(JSON.stringify(this.stats));
38
59
  }
@@ -1 +1 @@
1
- export declare const VERSION = "5.4.1";
1
+ export declare const VERSION = "5.5.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.4.1';
4
+ exports.VERSION = '5.5.0';
5
5
  //# sourceMappingURL=version.js.map
@@ -2718,6 +2718,8 @@ var MONTH_MS = 30 * 24 * 60 * 60 * 1e3;
2718
2718
  var TOP_RULES_LIMIT = 10;
2719
2719
  var EMERGING_THRESHOLD = 3;
2720
2720
  var SUGGESTION_UNUSED_PREVIEW = 5;
2721
+ var EFFECTIVE_THRESHOLD = 50;
2722
+ var NEEDS_REVIEW_THRESHOLD = 10;
2721
2723
  var RuleInsightsService = class {
2722
2724
  generateInsights(stats, allRuleNames, now = Date.now()) {
2723
2725
  const entries = Object.entries(stats);
@@ -2734,6 +2736,7 @@ var RuleInsightsService = class {
2734
2736
  trends.declining,
2735
2737
  entries.length
2736
2738
  );
2739
+ const effectivenessScores = this.computeEffectivenessScores(entries);
2737
2740
  return {
2738
2741
  generatedAt: now,
2739
2742
  summary: {
@@ -2745,7 +2748,8 @@ var RuleInsightsService = class {
2745
2748
  topRules: topRules.slice(0, TOP_RULES_LIMIT),
2746
2749
  unusedRules,
2747
2750
  trends,
2748
- suggestions
2751
+ suggestions,
2752
+ effectivenessScores
2749
2753
  };
2750
2754
  }
2751
2755
  buildTopRules(entries, avgCount) {
@@ -2774,6 +2778,33 @@ var RuleInsightsService = class {
2774
2778
  const emerging = entries.filter(([, s]) => now - s.lastUsed <= WEEK_MS && s.count <= EMERGING_THRESHOLD).map(([name]) => name);
2775
2779
  return { recentlyActive, declining, emerging };
2776
2780
  }
2781
+ computeEffectivenessScores(entries) {
2782
+ const scores = [];
2783
+ for (const [name, stat2] of entries) {
2784
+ if (!stat2.generatedRule) continue;
2785
+ if (typeof stat2.baselineFailureRate !== "number" || typeof stat2.currentFailureRate !== "number")
2786
+ continue;
2787
+ const baseline = stat2.baselineFailureRate;
2788
+ const current = stat2.currentFailureRate;
2789
+ const reductionPercent = baseline > 0 ? (baseline - current) / baseline * 100 : 0;
2790
+ let verdict;
2791
+ if (reductionPercent >= EFFECTIVE_THRESHOLD) {
2792
+ verdict = "effective";
2793
+ } else if (reductionPercent >= NEEDS_REVIEW_THRESHOLD) {
2794
+ verdict = "needs-review";
2795
+ } else {
2796
+ verdict = "ineffective";
2797
+ }
2798
+ scores.push({
2799
+ ruleName: name,
2800
+ baselineFailureRate: baseline,
2801
+ currentFailureRate: current,
2802
+ reductionPercent: Math.round(reductionPercent * 10) / 10,
2803
+ verdict
2804
+ });
2805
+ }
2806
+ return scores;
2807
+ }
2777
2808
  generateSuggestions(topRules, unusedRules, declining, totalTracked) {
2778
2809
  const suggestions = [];
2779
2810
  if (totalTracked === 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codingbuddy",
3
- "version": "5.4.1",
3
+ "version": "5.5.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.4.1",
80
+ "codingbuddy-rules": "5.5.0",
81
81
  "eventemitter2": "6.4.9",
82
82
  "ink": "6.7.0",
83
83
  "minimatch": "10.2.3",