wiggum-cli 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai/agents/codebase-analyzer.d.ts.map +1 -1
- package/dist/ai/agents/codebase-analyzer.js +9 -2
- package/dist/ai/agents/codebase-analyzer.js.map +1 -1
- package/dist/ai/agents/mcp-detector.d.ts +4 -2
- package/dist/ai/agents/mcp-detector.d.ts.map +1 -1
- package/dist/ai/agents/mcp-detector.js +9 -6
- package/dist/ai/agents/mcp-detector.js.map +1 -1
- package/dist/ai/agents/types.d.ts +12 -2
- package/dist/ai/agents/types.d.ts.map +1 -1
- package/dist/ai/enhancer.d.ts +27 -1
- package/dist/ai/enhancer.d.ts.map +1 -1
- package/dist/ai/enhancer.js +52 -7
- package/dist/ai/enhancer.js.map +1 -1
- package/dist/ai/index.d.ts +1 -1
- package/dist/ai/index.d.ts.map +1 -1
- package/dist/ai/index.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +113 -112
- package/dist/commands/init.js.map +1 -1
- package/dist/utils/colors.d.ts +47 -0
- package/dist/utils/colors.d.ts.map +1 -1
- package/dist/utils/colors.js +108 -0
- package/dist/utils/colors.js.map +1 -1
- package/dist/utils/header.d.ts.map +1 -1
- package/dist/utils/header.js +19 -2
- package/dist/utils/header.js.map +1 -1
- package/dist/utils/spinner.d.ts +74 -0
- package/dist/utils/spinner.d.ts.map +1 -0
- package/dist/utils/spinner.js +208 -0
- package/dist/utils/spinner.js.map +1 -0
- package/package.json +1 -1
- package/src/ai/agents/codebase-analyzer.ts +12 -3
- package/src/ai/agents/mcp-detector.test.ts +30 -7
- package/src/ai/agents/mcp-detector.ts +10 -7
- package/src/ai/agents/types.ts +13 -2
- package/src/ai/enhancer.ts +88 -9
- package/src/ai/index.ts +2 -0
- package/src/commands/init.ts +129 -124
- package/src/utils/colors.ts +139 -0
- package/src/utils/header.ts +19 -2
- package/src/utils/spinner.ts +262 -0
package/dist/utils/header.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"header.js","sourceRoot":"","sources":["../../src/utils/header.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"header.js","sourceRoot":"","sources":["../../src/utils/header.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAErC,gCAAgC;AAChC,SAAS,UAAU;IACjB,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;QACtC,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;QAChE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;QAC3D,OAAO,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,4CAA4C;IAC5C,MAAM,WAAW,GAAG,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,OAAO,EAAE,CAAC,GAAG,KAAK,CAAC;IACjL,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,mCAAmC;IACnC,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE;QACvB,IAAI,EAAE,OAAO;QACb,MAAM,EAAE,CAAC,cAAc,CAAC,MAAM,CAAC;QAC/B,aAAa,EAAE,CAAC;QAChB,UAAU,EAAE,CAAC;QACb,KAAK,EAAE,KAAK;QACZ,SAAS,EAAE,CAAC;KACb,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,mCAAmC,CAAC,CAAC;IACvG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom Spinner with Shimmer Effect, Timer, and Token Tracking
|
|
3
|
+
* Inspired by Claude Code's loading experience
|
|
4
|
+
*/
|
|
5
|
+
export interface ShimmerSpinnerOptions {
|
|
6
|
+
/** Enable shimmer effect (default: true) */
|
|
7
|
+
shimmer?: boolean;
|
|
8
|
+
/** Show elapsed time (default: true) */
|
|
9
|
+
showTimer?: boolean;
|
|
10
|
+
/** Show token usage (default: false) */
|
|
11
|
+
showTokens?: boolean;
|
|
12
|
+
/** Spinner style (default: 'shimmer') */
|
|
13
|
+
style?: 'shimmer' | 'braille' | 'dots';
|
|
14
|
+
}
|
|
15
|
+
export interface TokenUsage {
|
|
16
|
+
inputTokens: number;
|
|
17
|
+
outputTokens: number;
|
|
18
|
+
totalTokens: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Custom spinner with shimmer effect, timer, and token tracking
|
|
22
|
+
*/
|
|
23
|
+
export declare class ShimmerSpinner {
|
|
24
|
+
private message;
|
|
25
|
+
private isRunning;
|
|
26
|
+
private intervalId;
|
|
27
|
+
private frameIndex;
|
|
28
|
+
private startTime;
|
|
29
|
+
private tokens;
|
|
30
|
+
private options;
|
|
31
|
+
private frames;
|
|
32
|
+
constructor(options?: ShimmerSpinnerOptions);
|
|
33
|
+
/**
|
|
34
|
+
* Start the spinner with a message
|
|
35
|
+
*/
|
|
36
|
+
start(message: string): void;
|
|
37
|
+
/**
|
|
38
|
+
* Update the spinner message
|
|
39
|
+
*/
|
|
40
|
+
update(message: string): void;
|
|
41
|
+
/**
|
|
42
|
+
* Update token usage (adds to existing)
|
|
43
|
+
*/
|
|
44
|
+
updateTokens(usage: TokenUsage): void;
|
|
45
|
+
/**
|
|
46
|
+
* Set token usage directly
|
|
47
|
+
*/
|
|
48
|
+
setTokens(usage: TokenUsage): void;
|
|
49
|
+
/**
|
|
50
|
+
* Render the spinner
|
|
51
|
+
*/
|
|
52
|
+
private render;
|
|
53
|
+
/**
|
|
54
|
+
* Stop the spinner with a final message
|
|
55
|
+
*/
|
|
56
|
+
stop(finalMessage?: string): void;
|
|
57
|
+
/**
|
|
58
|
+
* Stop with error
|
|
59
|
+
*/
|
|
60
|
+
fail(message?: string): void;
|
|
61
|
+
/**
|
|
62
|
+
* Get elapsed time in milliseconds
|
|
63
|
+
*/
|
|
64
|
+
getElapsed(): number;
|
|
65
|
+
/**
|
|
66
|
+
* Get current token usage
|
|
67
|
+
*/
|
|
68
|
+
getTokens(): TokenUsage;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Create a shimmer spinner instance
|
|
72
|
+
*/
|
|
73
|
+
export declare function createShimmerSpinner(options?: ShimmerSpinnerOptions): ShimmerSpinner;
|
|
74
|
+
//# sourceMappingURL=spinner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spinner.d.ts","sourceRoot":"","sources":["../../src/utils/spinner.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAkDH,MAAM,WAAW,qBAAqB;IACpC,4CAA4C;IAC5C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,wCAAwC;IACxC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yCAAyC;IACzC,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;CACxC;AAED,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,MAAM,CAAmE;IACjF,OAAO,CAAC,OAAO,CAAkC;IACjD,OAAO,CAAC,MAAM,CAAW;gBAEb,OAAO,GAAE,qBAA0B;IAuB/C;;OAEG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAgB5B;;OAEG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI7B;;OAEG;IACH,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAQrC;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAIlC;;OAEG;IACH,OAAO,CAAC,MAAM;IAgCd;;OAEG;IACH,IAAI,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI;IA4BjC;;OAEG;IACH,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI;IAsB5B;;OAEG;IACH,UAAU,IAAI,MAAM;IAIpB;;OAEG;IACH,SAAS,IAAI,UAAU;CAGxB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,cAAc,CAEpF"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom Spinner with Shimmer Effect, Timer, and Token Tracking
|
|
3
|
+
* Inspired by Claude Code's loading experience
|
|
4
|
+
*/
|
|
5
|
+
import { simpson } from './colors.js';
|
|
6
|
+
/**
|
|
7
|
+
* Shimmer frames for the spinner animation
|
|
8
|
+
* Creates a wave-like effect similar to Claude Code
|
|
9
|
+
*/
|
|
10
|
+
const SHIMMER_FRAMES = [
|
|
11
|
+
'░▒▓█▓▒░',
|
|
12
|
+
'▒▓█▓▒░░',
|
|
13
|
+
'▓█▓▒░░▒',
|
|
14
|
+
'█▓▒░░▒▓',
|
|
15
|
+
'▓▒░░▒▓█',
|
|
16
|
+
'▒░░▒▓█▓',
|
|
17
|
+
'░░▒▓█▓▒',
|
|
18
|
+
'░▒▓█▓▒░',
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* Braille spinner frames (alternative style)
|
|
22
|
+
*/
|
|
23
|
+
const BRAILLE_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
24
|
+
/**
|
|
25
|
+
* Dots spinner frames
|
|
26
|
+
*/
|
|
27
|
+
const DOTS_FRAMES = ['⠁', '⠂', '⠄', '⡀', '⢀', '⠠', '⠐', '⠈'];
|
|
28
|
+
/**
|
|
29
|
+
* Format milliseconds to human-readable time
|
|
30
|
+
*/
|
|
31
|
+
function formatTime(ms) {
|
|
32
|
+
const seconds = Math.floor(ms / 1000);
|
|
33
|
+
const minutes = Math.floor(seconds / 60);
|
|
34
|
+
const remainingSeconds = seconds % 60;
|
|
35
|
+
if (minutes > 0) {
|
|
36
|
+
return `${minutes}m ${remainingSeconds}s`;
|
|
37
|
+
}
|
|
38
|
+
return `${seconds}s`;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Format token count with commas
|
|
42
|
+
*/
|
|
43
|
+
function formatTokens(tokens) {
|
|
44
|
+
return tokens.toLocaleString();
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Custom spinner with shimmer effect, timer, and token tracking
|
|
48
|
+
*/
|
|
49
|
+
export class ShimmerSpinner {
|
|
50
|
+
message = '';
|
|
51
|
+
isRunning = false;
|
|
52
|
+
intervalId = null;
|
|
53
|
+
frameIndex = 0;
|
|
54
|
+
startTime = 0;
|
|
55
|
+
tokens = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
56
|
+
options;
|
|
57
|
+
frames;
|
|
58
|
+
constructor(options = {}) {
|
|
59
|
+
this.options = {
|
|
60
|
+
shimmer: options.shimmer ?? true,
|
|
61
|
+
showTimer: options.showTimer ?? true,
|
|
62
|
+
showTokens: options.showTokens ?? false,
|
|
63
|
+
style: options.style ?? 'shimmer',
|
|
64
|
+
};
|
|
65
|
+
// Select frames based on style
|
|
66
|
+
switch (this.options.style) {
|
|
67
|
+
case 'braille':
|
|
68
|
+
this.frames = BRAILLE_FRAMES;
|
|
69
|
+
break;
|
|
70
|
+
case 'dots':
|
|
71
|
+
this.frames = DOTS_FRAMES;
|
|
72
|
+
break;
|
|
73
|
+
case 'shimmer':
|
|
74
|
+
default:
|
|
75
|
+
this.frames = SHIMMER_FRAMES;
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Start the spinner with a message
|
|
81
|
+
*/
|
|
82
|
+
start(message) {
|
|
83
|
+
this.message = message;
|
|
84
|
+
this.isRunning = true;
|
|
85
|
+
this.startTime = Date.now();
|
|
86
|
+
this.frameIndex = 0;
|
|
87
|
+
// Clear line and hide cursor
|
|
88
|
+
process.stdout.write('\x1B[?25l');
|
|
89
|
+
this.render();
|
|
90
|
+
this.intervalId = setInterval(() => {
|
|
91
|
+
this.frameIndex = (this.frameIndex + 1) % this.frames.length;
|
|
92
|
+
this.render();
|
|
93
|
+
}, 80);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Update the spinner message
|
|
97
|
+
*/
|
|
98
|
+
update(message) {
|
|
99
|
+
this.message = message;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Update token usage (adds to existing)
|
|
103
|
+
*/
|
|
104
|
+
updateTokens(usage) {
|
|
105
|
+
this.tokens = {
|
|
106
|
+
inputTokens: this.tokens.inputTokens + usage.inputTokens,
|
|
107
|
+
outputTokens: this.tokens.outputTokens + usage.outputTokens,
|
|
108
|
+
totalTokens: this.tokens.totalTokens + usage.totalTokens,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Set token usage directly
|
|
113
|
+
*/
|
|
114
|
+
setTokens(usage) {
|
|
115
|
+
this.tokens = usage;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Render the spinner
|
|
119
|
+
*/
|
|
120
|
+
render() {
|
|
121
|
+
if (!this.isRunning)
|
|
122
|
+
return;
|
|
123
|
+
const elapsed = Date.now() - this.startTime;
|
|
124
|
+
const frame = this.frames[this.frameIndex];
|
|
125
|
+
let line = '';
|
|
126
|
+
// Shimmer/spinner frame
|
|
127
|
+
if (this.options.shimmer && this.options.style === 'shimmer') {
|
|
128
|
+
line += simpson.yellow(frame) + ' ';
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
line += simpson.yellow(frame) + ' ';
|
|
132
|
+
}
|
|
133
|
+
// Message
|
|
134
|
+
line += this.message;
|
|
135
|
+
// Timer
|
|
136
|
+
if (this.options.showTimer) {
|
|
137
|
+
line += simpson.brown(` [${formatTime(elapsed)}]`);
|
|
138
|
+
}
|
|
139
|
+
// Tokens
|
|
140
|
+
if (this.options.showTokens && this.tokens.totalTokens > 0) {
|
|
141
|
+
line += simpson.pink(` (${formatTokens(this.tokens.totalTokens)} tokens)`);
|
|
142
|
+
}
|
|
143
|
+
// Clear line and write
|
|
144
|
+
process.stdout.write('\r\x1B[K' + line);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Stop the spinner with a final message
|
|
148
|
+
*/
|
|
149
|
+
stop(finalMessage) {
|
|
150
|
+
this.isRunning = false;
|
|
151
|
+
if (this.intervalId) {
|
|
152
|
+
clearInterval(this.intervalId);
|
|
153
|
+
this.intervalId = null;
|
|
154
|
+
}
|
|
155
|
+
const elapsed = Date.now() - this.startTime;
|
|
156
|
+
// Build final line
|
|
157
|
+
let line = '';
|
|
158
|
+
line += simpson.yellow('✓') + ' ';
|
|
159
|
+
line += finalMessage || this.message;
|
|
160
|
+
if (this.options.showTimer) {
|
|
161
|
+
line += simpson.brown(` [${formatTime(elapsed)}]`);
|
|
162
|
+
}
|
|
163
|
+
if (this.options.showTokens && this.tokens.totalTokens > 0) {
|
|
164
|
+
line += simpson.pink(` (${formatTokens(this.tokens.totalTokens)} tokens)`);
|
|
165
|
+
}
|
|
166
|
+
// Clear line, show cursor, print final
|
|
167
|
+
process.stdout.write('\r\x1B[K' + line + '\n');
|
|
168
|
+
process.stdout.write('\x1B[?25h');
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Stop with error
|
|
172
|
+
*/
|
|
173
|
+
fail(message) {
|
|
174
|
+
this.isRunning = false;
|
|
175
|
+
if (this.intervalId) {
|
|
176
|
+
clearInterval(this.intervalId);
|
|
177
|
+
this.intervalId = null;
|
|
178
|
+
}
|
|
179
|
+
const elapsed = Date.now() - this.startTime;
|
|
180
|
+
let line = '';
|
|
181
|
+
line += simpson.pink('✗') + ' ';
|
|
182
|
+
line += message || this.message;
|
|
183
|
+
if (this.options.showTimer) {
|
|
184
|
+
line += simpson.brown(` [${formatTime(elapsed)}]`);
|
|
185
|
+
}
|
|
186
|
+
process.stdout.write('\r\x1B[K' + line + '\n');
|
|
187
|
+
process.stdout.write('\x1B[?25h');
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Get elapsed time in milliseconds
|
|
191
|
+
*/
|
|
192
|
+
getElapsed() {
|
|
193
|
+
return Date.now() - this.startTime;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Get current token usage
|
|
197
|
+
*/
|
|
198
|
+
getTokens() {
|
|
199
|
+
return { ...this.tokens };
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Create a shimmer spinner instance
|
|
204
|
+
*/
|
|
205
|
+
export function createShimmerSpinner(options) {
|
|
206
|
+
return new ShimmerSpinner(options);
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=spinner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spinner.js","sourceRoot":"","sources":["../../src/utils/spinner.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@ import { existsSync, readFileSync } from 'node:fs';
|
|
|
8
8
|
import { join } from 'node:path';
|
|
9
9
|
import { stepCountIs, type LanguageModel } from 'ai';
|
|
10
10
|
import type { ScanResult } from '../../scanner/types.js';
|
|
11
|
-
import type { MultiAgentAnalysis, CodebaseAnalysis, StackResearch, RalphMcpServers } from './types.js';
|
|
11
|
+
import type { MultiAgentAnalysis, CodebaseAnalysis, StackResearch, RalphMcpServers, TokenUsage } from './types.js';
|
|
12
12
|
import { createExplorationTools } from '../tools.js';
|
|
13
13
|
import { isReasoningModel } from '../providers.js';
|
|
14
14
|
import { logger } from '../../utils/logger.js';
|
|
@@ -193,8 +193,15 @@ After exploring, output your complete analysis as JSON.`;
|
|
|
193
193
|
// Detect MCP servers (pure function)
|
|
194
194
|
const mcpServers = detectRalphMcpServers(stack, analyzerOutput.projectType);
|
|
195
195
|
|
|
196
|
+
// Capture token usage from AI response
|
|
197
|
+
const tokenUsage = result.usage ? {
|
|
198
|
+
inputTokens: result.usage.inputTokens ?? 0,
|
|
199
|
+
outputTokens: result.usage.outputTokens ?? 0,
|
|
200
|
+
totalTokens: result.usage.totalTokens ?? 0,
|
|
201
|
+
} : undefined;
|
|
202
|
+
|
|
196
203
|
// Build full MultiAgentAnalysis
|
|
197
|
-
return buildMultiAgentAnalysis(analyzerOutput, mcpServers, input);
|
|
204
|
+
return buildMultiAgentAnalysis(analyzerOutput, mcpServers, input, tokenUsage);
|
|
198
205
|
} catch (error) {
|
|
199
206
|
if (verbose) {
|
|
200
207
|
logger.error(`Codebase Analyzer error: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -270,7 +277,8 @@ function parseAnalyzerOutput(
|
|
|
270
277
|
function buildMultiAgentAnalysis(
|
|
271
278
|
output: AnalyzerOutput,
|
|
272
279
|
mcpServers: RalphMcpServers,
|
|
273
|
-
input: CodebaseAnalyzerInput
|
|
280
|
+
input: CodebaseAnalyzerInput,
|
|
281
|
+
tokenUsage?: TokenUsage
|
|
274
282
|
): MultiAgentAnalysis {
|
|
275
283
|
const derivedCommands = deriveCommandsFromScripts(input.scanResult.projectRoot);
|
|
276
284
|
|
|
@@ -316,6 +324,7 @@ function buildMultiAgentAnalysis(
|
|
|
316
324
|
codebaseAnalysis,
|
|
317
325
|
stackResearch,
|
|
318
326
|
mcpServers: convertToLegacyMcpRecommendations(mcpServers),
|
|
327
|
+
tokenUsage,
|
|
319
328
|
};
|
|
320
329
|
}
|
|
321
330
|
|
|
@@ -33,22 +33,25 @@ describe('detectRalphMcpServers', () => {
|
|
|
33
33
|
expect(result.e2eTesting).toBe('playwright');
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
-
it('returns
|
|
36
|
+
it('returns undefined for MCP projects (use npx @modelcontextprotocol/inspector)', () => {
|
|
37
37
|
const stack = createStack({
|
|
38
38
|
mcp: { isProject: true },
|
|
39
39
|
});
|
|
40
40
|
const result = detectRalphMcpServers(stack);
|
|
41
|
-
|
|
41
|
+
// MCP Inspector is an npx tool, not an MCP server
|
|
42
|
+
expect(result.e2eTesting).toBeUndefined();
|
|
42
43
|
});
|
|
43
44
|
|
|
44
|
-
it('returns
|
|
45
|
+
it('returns undefined for MCP projects via projectType parameter', () => {
|
|
45
46
|
const result = detectRalphMcpServers(createStack(), 'MCP Server');
|
|
46
|
-
|
|
47
|
+
// MCP Inspector is an npx tool, not an MCP server
|
|
48
|
+
expect(result.e2eTesting).toBeUndefined();
|
|
47
49
|
});
|
|
48
50
|
|
|
49
|
-
it('returns
|
|
51
|
+
it('returns undefined when projectType contains "mcp" (case-insensitive)', () => {
|
|
50
52
|
const result = detectRalphMcpServers(createStack(), 'mcp tool');
|
|
51
|
-
|
|
53
|
+
// MCP Inspector is an npx tool, not an MCP server
|
|
54
|
+
expect(result.e2eTesting).toBeUndefined();
|
|
52
55
|
});
|
|
53
56
|
|
|
54
57
|
it('returns playwright for Next.js projects', () => {
|
|
@@ -81,7 +84,8 @@ describe('detectRalphMcpServers', () => {
|
|
|
81
84
|
framework: detection('Next.js'),
|
|
82
85
|
});
|
|
83
86
|
const result = detectRalphMcpServers(stack);
|
|
84
|
-
|
|
87
|
+
// MCP Inspector is an npx tool, not an MCP server - no E2E MCP for MCP projects
|
|
88
|
+
expect(result.e2eTesting).toBeUndefined();
|
|
85
89
|
});
|
|
86
90
|
});
|
|
87
91
|
|
|
@@ -313,6 +317,25 @@ describe('convertToLegacyMcpRecommendations', () => {
|
|
|
313
317
|
expect(result.essential).toContain('playwright');
|
|
314
318
|
});
|
|
315
319
|
|
|
320
|
+
it('returns empty essential when e2eTesting is undefined (MCP projects)', () => {
|
|
321
|
+
const result = convertToLegacyMcpRecommendations({
|
|
322
|
+
e2eTesting: undefined,
|
|
323
|
+
additional: [],
|
|
324
|
+
});
|
|
325
|
+
// MCP projects use npx @modelcontextprotocol/inspector, not an MCP server
|
|
326
|
+
expect(result.essential).toEqual([]);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it('only includes database in essential for MCP projects', () => {
|
|
330
|
+
const result = convertToLegacyMcpRecommendations({
|
|
331
|
+
e2eTesting: undefined,
|
|
332
|
+
database: 'supabase',
|
|
333
|
+
additional: [],
|
|
334
|
+
});
|
|
335
|
+
// MCP projects use npx inspector for testing, but still get database MCP
|
|
336
|
+
expect(result.essential).toEqual(['supabase']);
|
|
337
|
+
});
|
|
338
|
+
|
|
316
339
|
it('includes database in essential when detected', () => {
|
|
317
340
|
const result = convertToLegacyMcpRecommendations({
|
|
318
341
|
e2eTesting: 'playwright',
|
|
@@ -66,13 +66,15 @@ const SERVICE_MCP_MAP: Record<string, string> = {
|
|
|
66
66
|
* Detect ralph-essential MCP servers from the stack
|
|
67
67
|
*
|
|
68
68
|
* Ralph loop essentials:
|
|
69
|
-
* - For
|
|
70
|
-
* - For web apps with E2E: playwright for E2E testing
|
|
69
|
+
* - For web apps with E2E: playwright MCP server for E2E testing
|
|
71
70
|
* - Database MCP: If database is detected
|
|
72
71
|
* - Additional MCPs based on services and deployment
|
|
72
|
+
*
|
|
73
|
+
* Note: MCP Inspector is a debugging TOOL (npx @modelcontextprotocol/inspector),
|
|
74
|
+
* NOT an MCP server. It should be mentioned in implementation guidelines, not here.
|
|
73
75
|
*/
|
|
74
76
|
export function detectRalphMcpServers(stack: DetectedStack, projectType?: string): RalphMcpServers {
|
|
75
|
-
// Determine appropriate E2E testing
|
|
77
|
+
// Determine appropriate E2E testing MCP based on project type
|
|
76
78
|
const isMcpProject = stack.mcp?.isProject || projectType?.toLowerCase().includes('mcp');
|
|
77
79
|
const isWebApp = stack.framework?.name?.toLowerCase().includes('next') ||
|
|
78
80
|
stack.framework?.name?.toLowerCase().includes('react') ||
|
|
@@ -81,12 +83,13 @@ export function detectRalphMcpServers(stack: DetectedStack, projectType?: string
|
|
|
81
83
|
stack.framework?.name?.toLowerCase().includes('nuxt') ||
|
|
82
84
|
stack.framework?.name?.toLowerCase().includes('remix');
|
|
83
85
|
|
|
84
|
-
// Choose E2E testing
|
|
85
|
-
|
|
86
|
+
// Choose E2E testing MCP based on project type
|
|
87
|
+
// Note: MCP projects don't need an E2E testing MCP server - they use npx @modelcontextprotocol/inspector
|
|
88
|
+
let e2eTesting: string | undefined;
|
|
86
89
|
if (isMcpProject) {
|
|
87
|
-
e2eTesting =
|
|
90
|
+
e2eTesting = undefined; // MCP projects use npx inspector (not an MCP server)
|
|
88
91
|
} else if (isWebApp) {
|
|
89
|
-
e2eTesting = 'playwright'; // Web apps use Playwright
|
|
92
|
+
e2eTesting = 'playwright'; // Web apps use Playwright MCP
|
|
90
93
|
} else {
|
|
91
94
|
e2eTesting = 'playwright'; // Default to Playwright for CLI/other projects
|
|
92
95
|
}
|
package/src/ai/agents/types.ts
CHANGED
|
@@ -82,8 +82,8 @@ export interface TechResearchResult {
|
|
|
82
82
|
export interface RalphMcpServers {
|
|
83
83
|
/** Database MCP server if applicable */
|
|
84
84
|
database?: string;
|
|
85
|
-
/** E2E testing MCP (
|
|
86
|
-
e2eTesting
|
|
85
|
+
/** E2E testing MCP (playwright for web apps, undefined for MCP projects) */
|
|
86
|
+
e2eTesting?: string;
|
|
87
87
|
/** Any additional recommended MCPs */
|
|
88
88
|
additional: string[];
|
|
89
89
|
}
|
|
@@ -178,6 +178,15 @@ export interface McpRecommendations {
|
|
|
178
178
|
/**
|
|
179
179
|
* Combined result from all agents
|
|
180
180
|
*/
|
|
181
|
+
/**
|
|
182
|
+
* Token usage from AI calls
|
|
183
|
+
*/
|
|
184
|
+
export interface TokenUsage {
|
|
185
|
+
inputTokens: number;
|
|
186
|
+
outputTokens: number;
|
|
187
|
+
totalTokens: number;
|
|
188
|
+
}
|
|
189
|
+
|
|
181
190
|
export interface MultiAgentAnalysis {
|
|
182
191
|
/** Codebase analysis from the Codebase Analyst */
|
|
183
192
|
codebaseAnalysis: CodebaseAnalysis;
|
|
@@ -185,6 +194,8 @@ export interface MultiAgentAnalysis {
|
|
|
185
194
|
stackResearch: StackResearch;
|
|
186
195
|
/** MCP server recommendations (merged from both agents) */
|
|
187
196
|
mcpServers: McpRecommendations;
|
|
197
|
+
/** Token usage from AI calls */
|
|
198
|
+
tokenUsage?: TokenUsage;
|
|
188
199
|
}
|
|
189
200
|
|
|
190
201
|
/**
|