tlc-claude-code 1.4.9 → 1.5.2

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 (122) hide show
  1. package/CLAUDE.md +23 -0
  2. package/CODING-STANDARDS.md +408 -0
  3. package/bin/install.js +2 -0
  4. package/dashboard/dist/components/QualityGatePane.d.ts +38 -0
  5. package/dashboard/dist/components/QualityGatePane.js +31 -0
  6. package/dashboard/dist/components/QualityGatePane.test.d.ts +1 -0
  7. package/dashboard/dist/components/QualityGatePane.test.js +147 -0
  8. package/dashboard/dist/components/orchestration/AgentCard.d.ts +26 -0
  9. package/dashboard/dist/components/orchestration/AgentCard.js +60 -0
  10. package/dashboard/dist/components/orchestration/AgentCard.test.d.ts +1 -0
  11. package/dashboard/dist/components/orchestration/AgentCard.test.js +63 -0
  12. package/dashboard/dist/components/orchestration/AgentControls.d.ts +11 -0
  13. package/dashboard/dist/components/orchestration/AgentControls.js +20 -0
  14. package/dashboard/dist/components/orchestration/AgentControls.test.d.ts +1 -0
  15. package/dashboard/dist/components/orchestration/AgentControls.test.js +52 -0
  16. package/dashboard/dist/components/orchestration/AgentDetail.d.ts +35 -0
  17. package/dashboard/dist/components/orchestration/AgentDetail.js +37 -0
  18. package/dashboard/dist/components/orchestration/AgentDetail.test.d.ts +1 -0
  19. package/dashboard/dist/components/orchestration/AgentDetail.test.js +79 -0
  20. package/dashboard/dist/components/orchestration/AgentList.d.ts +31 -0
  21. package/dashboard/dist/components/orchestration/AgentList.js +47 -0
  22. package/dashboard/dist/components/orchestration/AgentList.test.d.ts +1 -0
  23. package/dashboard/dist/components/orchestration/AgentList.test.js +64 -0
  24. package/dashboard/dist/components/orchestration/CostMeter.d.ts +11 -0
  25. package/dashboard/dist/components/orchestration/CostMeter.js +28 -0
  26. package/dashboard/dist/components/orchestration/CostMeter.test.d.ts +1 -0
  27. package/dashboard/dist/components/orchestration/CostMeter.test.js +50 -0
  28. package/dashboard/dist/components/orchestration/ModelSelector.d.ts +20 -0
  29. package/dashboard/dist/components/orchestration/ModelSelector.js +12 -0
  30. package/dashboard/dist/components/orchestration/ModelSelector.test.d.ts +1 -0
  31. package/dashboard/dist/components/orchestration/ModelSelector.test.js +56 -0
  32. package/dashboard/dist/components/orchestration/OrchestrationDashboard.d.ts +28 -0
  33. package/dashboard/dist/components/orchestration/OrchestrationDashboard.js +28 -0
  34. package/dashboard/dist/components/orchestration/OrchestrationDashboard.test.d.ts +1 -0
  35. package/dashboard/dist/components/orchestration/OrchestrationDashboard.test.js +56 -0
  36. package/dashboard/dist/components/orchestration/QualityIndicator.d.ts +11 -0
  37. package/dashboard/dist/components/orchestration/QualityIndicator.js +37 -0
  38. package/dashboard/dist/components/orchestration/QualityIndicator.test.d.ts +1 -0
  39. package/dashboard/dist/components/orchestration/QualityIndicator.test.js +52 -0
  40. package/dashboard/dist/components/orchestration/index.d.ts +8 -0
  41. package/dashboard/dist/components/orchestration/index.js +8 -0
  42. package/package.json +1 -1
  43. package/server/lib/access-control.js +352 -0
  44. package/server/lib/access-control.test.js +322 -0
  45. package/server/lib/agents-cancel-command.js +139 -0
  46. package/server/lib/agents-cancel-command.test.js +180 -0
  47. package/server/lib/agents-get-command.js +159 -0
  48. package/server/lib/agents-get-command.test.js +167 -0
  49. package/server/lib/agents-list-command.js +150 -0
  50. package/server/lib/agents-list-command.test.js +149 -0
  51. package/server/lib/agents-logs-command.js +126 -0
  52. package/server/lib/agents-logs-command.test.js +198 -0
  53. package/server/lib/agents-retry-command.js +117 -0
  54. package/server/lib/agents-retry-command.test.js +192 -0
  55. package/server/lib/budget-limits.js +222 -0
  56. package/server/lib/budget-limits.test.js +214 -0
  57. package/server/lib/code-generator.js +291 -0
  58. package/server/lib/code-generator.test.js +307 -0
  59. package/server/lib/cost-command.js +290 -0
  60. package/server/lib/cost-command.test.js +202 -0
  61. package/server/lib/cost-optimizer.js +404 -0
  62. package/server/lib/cost-optimizer.test.js +232 -0
  63. package/server/lib/cost-projections.js +302 -0
  64. package/server/lib/cost-projections.test.js +217 -0
  65. package/server/lib/cost-reports.js +277 -0
  66. package/server/lib/cost-reports.test.js +254 -0
  67. package/server/lib/cost-tracker.js +216 -0
  68. package/server/lib/cost-tracker.test.js +302 -0
  69. package/server/lib/crypto-patterns.js +433 -0
  70. package/server/lib/crypto-patterns.test.js +346 -0
  71. package/server/lib/design-command.js +385 -0
  72. package/server/lib/design-command.test.js +249 -0
  73. package/server/lib/design-parser.js +237 -0
  74. package/server/lib/design-parser.test.js +290 -0
  75. package/server/lib/gemini-vision.js +377 -0
  76. package/server/lib/gemini-vision.test.js +282 -0
  77. package/server/lib/input-validator.js +360 -0
  78. package/server/lib/input-validator.test.js +295 -0
  79. package/server/lib/litellm-client.js +232 -0
  80. package/server/lib/litellm-client.test.js +267 -0
  81. package/server/lib/litellm-command.js +291 -0
  82. package/server/lib/litellm-command.test.js +260 -0
  83. package/server/lib/litellm-config.js +273 -0
  84. package/server/lib/litellm-config.test.js +212 -0
  85. package/server/lib/model-pricing.js +189 -0
  86. package/server/lib/model-pricing.test.js +178 -0
  87. package/server/lib/models-command.js +223 -0
  88. package/server/lib/models-command.test.js +193 -0
  89. package/server/lib/optimize-command.js +197 -0
  90. package/server/lib/optimize-command.test.js +193 -0
  91. package/server/lib/orchestration-integration.js +206 -0
  92. package/server/lib/orchestration-integration.test.js +235 -0
  93. package/server/lib/output-encoder.js +308 -0
  94. package/server/lib/output-encoder.test.js +312 -0
  95. package/server/lib/quality-evaluator.js +396 -0
  96. package/server/lib/quality-evaluator.test.js +337 -0
  97. package/server/lib/quality-gate-command.js +340 -0
  98. package/server/lib/quality-gate-command.test.js +321 -0
  99. package/server/lib/quality-gate-scorer.js +378 -0
  100. package/server/lib/quality-gate-scorer.test.js +376 -0
  101. package/server/lib/quality-history.js +265 -0
  102. package/server/lib/quality-history.test.js +359 -0
  103. package/server/lib/quality-presets.js +288 -0
  104. package/server/lib/quality-presets.test.js +269 -0
  105. package/server/lib/quality-retry.js +323 -0
  106. package/server/lib/quality-retry.test.js +325 -0
  107. package/server/lib/quality-thresholds.js +255 -0
  108. package/server/lib/quality-thresholds.test.js +237 -0
  109. package/server/lib/secure-auth.js +333 -0
  110. package/server/lib/secure-auth.test.js +288 -0
  111. package/server/lib/secure-code-command.js +540 -0
  112. package/server/lib/secure-code-command.test.js +309 -0
  113. package/server/lib/secure-errors.js +521 -0
  114. package/server/lib/secure-errors.test.js +298 -0
  115. package/server/lib/vision-command.js +372 -0
  116. package/server/lib/vision-command.test.js +255 -0
  117. package/server/lib/visual-command.js +350 -0
  118. package/server/lib/visual-command.test.js +256 -0
  119. package/server/lib/visual-testing.js +315 -0
  120. package/server/lib/visual-testing.test.js +357 -0
  121. package/server/package-lock.json +2 -2
  122. package/server/package.json +1 -1
@@ -0,0 +1,307 @@
1
+ /**
2
+ * Code Generator Tests
3
+ *
4
+ * Generate code from parsed design data
5
+ */
6
+
7
+ const { describe, it, beforeEach } = require('node:test');
8
+ const assert = require('node:assert');
9
+
10
+ const {
11
+ createGenerator,
12
+ generateReact,
13
+ generateVue,
14
+ generateHTML,
15
+ generateTailwind,
16
+ mapToLibrary,
17
+ addDesignReference,
18
+ formatCode,
19
+ } = require('./code-generator.js');
20
+
21
+ describe('Code Generator', () => {
22
+ let generator;
23
+ let mockLLMClient;
24
+
25
+ beforeEach(() => {
26
+ mockLLMClient = {
27
+ _call: async () => ({
28
+ code: '<div>Generated code</div>',
29
+ }),
30
+ };
31
+
32
+ generator = createGenerator({
33
+ llmClient: mockLLMClient,
34
+ });
35
+ });
36
+
37
+ describe('createGenerator', () => {
38
+ it('creates generator with LLM client', () => {
39
+ assert.ok(generator);
40
+ assert.ok(generator.llmClient);
41
+ });
42
+
43
+ it('accepts framework option', () => {
44
+ const reactGenerator = createGenerator({
45
+ llmClient: mockLLMClient,
46
+ framework: 'react',
47
+ });
48
+
49
+ assert.strictEqual(reactGenerator.framework, 'react');
50
+ });
51
+ });
52
+
53
+ describe('generateReact', () => {
54
+ it('generates React component', async () => {
55
+ mockLLMClient._call = async () => ({
56
+ code: `function LoginForm() {
57
+ return (
58
+ <div className="p-4">
59
+ <input type="email" placeholder="Email" />
60
+ <button>Submit</button>
61
+ </div>
62
+ );
63
+ }`,
64
+ });
65
+
66
+ const design = {
67
+ components: [
68
+ { type: 'input', placeholder: 'Email' },
69
+ { type: 'button', label: 'Submit' },
70
+ ],
71
+ layout: { type: 'column' },
72
+ };
73
+
74
+ const result = await generateReact(generator, { design });
75
+
76
+ assert.ok(result.code);
77
+ assert.ok(result.code.includes('function') || result.code.includes('const'));
78
+ });
79
+
80
+ it('uses TypeScript when specified', async () => {
81
+ mockLLMClient._call = async () => ({
82
+ code: `interface Props { onSubmit: () => void; }
83
+ export const Form: React.FC<Props> = ({ onSubmit }) => { return <form />; }`,
84
+ });
85
+
86
+ const result = await generateReact(generator, {
87
+ design: { components: [] },
88
+ typescript: true,
89
+ });
90
+
91
+ assert.ok(result.code.includes('interface') || result.code.includes(':'));
92
+ });
93
+
94
+ it('includes imports', async () => {
95
+ mockLLMClient._call = async () => ({
96
+ code: `import React from 'react';
97
+
98
+ export function Component() { return <div />; }`,
99
+ imports: ['react'],
100
+ });
101
+
102
+ const result = await generateReact(generator, {
103
+ design: { components: [] },
104
+ });
105
+
106
+ assert.ok(result.code.includes('import'));
107
+ });
108
+ });
109
+
110
+ describe('generateVue', () => {
111
+ it('generates Vue SFC', async () => {
112
+ mockLLMClient._call = async () => ({
113
+ code: `<template>
114
+ <div class="container">
115
+ <input v-model="email" placeholder="Email" />
116
+ <button @click="submit">Submit</button>
117
+ </div>
118
+ </template>
119
+
120
+ <script setup>
121
+ import { ref } from 'vue';
122
+ const email = ref('');
123
+ </script>`,
124
+ });
125
+
126
+ const result = await generateVue(generator, {
127
+ design: { components: [{ type: 'input' }, { type: 'button' }] },
128
+ });
129
+
130
+ assert.ok(result.code);
131
+ assert.ok(result.code.includes('<template>'));
132
+ });
133
+
134
+ it('supports composition API', async () => {
135
+ mockLLMClient._call = async () => ({
136
+ code: `<script setup>
137
+ import { ref } from 'vue';
138
+ </script>`,
139
+ });
140
+
141
+ const result = await generateVue(generator, {
142
+ design: { components: [] },
143
+ composition: true,
144
+ });
145
+
146
+ assert.ok(result.code.includes('setup'));
147
+ });
148
+ });
149
+
150
+ describe('generateHTML', () => {
151
+ it('generates plain HTML', async () => {
152
+ mockLLMClient._call = async () => ({
153
+ code: `<div class="form">
154
+ <input type="email" placeholder="Email">
155
+ <button type="submit">Submit</button>
156
+ </div>`,
157
+ });
158
+
159
+ const result = await generateHTML(generator, {
160
+ design: { components: [{ type: 'input' }, { type: 'button' }] },
161
+ });
162
+
163
+ assert.ok(result.code);
164
+ assert.ok(result.code.includes('<div'));
165
+ });
166
+
167
+ it('includes CSS when requested', async () => {
168
+ mockLLMClient._call = async () => ({
169
+ code: `<div class="form"></div>`,
170
+ css: `.form { padding: 1rem; }`,
171
+ });
172
+
173
+ const result = await generateHTML(generator, {
174
+ design: { components: [] },
175
+ includeCSS: true,
176
+ });
177
+
178
+ assert.ok(result.css || result.code.includes('style'));
179
+ });
180
+ });
181
+
182
+ describe('generateTailwind', () => {
183
+ it('generates Tailwind classes', async () => {
184
+ mockLLMClient._call = async () => ({
185
+ code: `<div class="flex flex-col gap-4 p-6">
186
+ <input class="border rounded px-4 py-2" placeholder="Email" />
187
+ <button class="bg-blue-500 text-white px-4 py-2 rounded">Submit</button>
188
+ </div>`,
189
+ });
190
+
191
+ const result = await generateTailwind(generator, {
192
+ design: { components: [], colors: [{ hex: '#3B82F6' }] },
193
+ });
194
+
195
+ assert.ok(result.code);
196
+ assert.ok(result.code.includes('class="'));
197
+ });
198
+
199
+ it('maps colors to Tailwind', async () => {
200
+ mockLLMClient._call = async () => ({
201
+ code: `<button class="bg-blue-500">Button</button>`,
202
+ });
203
+
204
+ const result = await generateTailwind(generator, {
205
+ design: { colors: [{ hex: '#3B82F6', role: 'primary' }] },
206
+ });
207
+
208
+ assert.ok(result.code.includes('blue') || result.code.includes('primary'));
209
+ });
210
+ });
211
+
212
+ describe('mapToLibrary', () => {
213
+ it('maps to shadcn/ui components', async () => {
214
+ const components = [
215
+ { type: 'button', variant: 'primary' },
216
+ { type: 'input', variant: 'text' },
217
+ ];
218
+
219
+ const mapped = await mapToLibrary(generator, {
220
+ components,
221
+ library: 'shadcn',
222
+ });
223
+
224
+ assert.ok(mapped);
225
+ assert.ok(Array.isArray(mapped));
226
+ mapped.forEach(m => {
227
+ assert.ok(m.original);
228
+ assert.ok(m.mapped);
229
+ });
230
+ });
231
+
232
+ it('maps to MUI components', async () => {
233
+ const components = [
234
+ { type: 'button', label: 'Click' },
235
+ ];
236
+
237
+ const mapped = await mapToLibrary(generator, {
238
+ components,
239
+ library: 'mui',
240
+ });
241
+
242
+ assert.ok(mapped[0].mapped.includes('Button') || mapped[0].mapped.includes('Mui'));
243
+ });
244
+
245
+ it('maps to Chakra components', async () => {
246
+ const components = [
247
+ { type: 'input', placeholder: 'Enter text' },
248
+ ];
249
+
250
+ const mapped = await mapToLibrary(generator, {
251
+ components,
252
+ library: 'chakra',
253
+ });
254
+
255
+ assert.ok(mapped[0].mapped);
256
+ });
257
+ });
258
+
259
+ describe('addDesignReference', () => {
260
+ it('adds source comment', () => {
261
+ const code = `function Component() { return <div />; }`;
262
+ const mockupPath = '/designs/login.png';
263
+
264
+ const result = addDesignReference(code, { mockupPath });
265
+
266
+ assert.ok(result.includes('login.png') || result.includes('design'));
267
+ });
268
+
269
+ it('adds figma link', () => {
270
+ const code = `function Component() { return <div />; }`;
271
+ const figmaUrl = 'https://figma.com/file/abc123';
272
+
273
+ const result = addDesignReference(code, { figmaUrl });
274
+
275
+ assert.ok(result.includes('figma.com'));
276
+ });
277
+ });
278
+
279
+ describe('formatCode', () => {
280
+ it('formats JavaScript', async () => {
281
+ const code = `function Component(){return <div className="test"></div>}`;
282
+
283
+ const formatted = await formatCode(code, { language: 'javascript' });
284
+
285
+ // Should have some formatting applied
286
+ assert.ok(formatted);
287
+ assert.ok(typeof formatted === 'string');
288
+ });
289
+
290
+ it('formats TypeScript', async () => {
291
+ const code = `const x:string="hello"`;
292
+
293
+ const formatted = await formatCode(code, { language: 'typescript' });
294
+
295
+ assert.ok(formatted);
296
+ });
297
+
298
+ it('preserves code if formatting fails', async () => {
299
+ const invalidCode = `function ( { return }`;
300
+
301
+ const formatted = await formatCode(invalidCode, { language: 'javascript' });
302
+
303
+ // Should return original or close to it if parsing fails
304
+ assert.ok(formatted);
305
+ });
306
+ });
307
+ });
@@ -0,0 +1,290 @@
1
+ /**
2
+ * Cost Command Module
3
+ *
4
+ * CLI interface for cost management
5
+ */
6
+
7
+ const { projectCost, compareModels } = require('./cost-projections.js');
8
+ const { generateReport, groupByModel, groupByOperation, formatReport } = require('./cost-reports.js');
9
+ const { analyzeUsage, suggestCheaperModel, createOptimizer, formatSuggestions } = require('./cost-optimizer.js');
10
+
11
+ /**
12
+ * Parse command line arguments
13
+ * @param {string} input - Command input string
14
+ * @returns {Object} Parsed arguments
15
+ */
16
+ function parseArgs(input) {
17
+ const parts = [];
18
+ let current = '';
19
+ let inQuotes = false;
20
+
21
+ // Parse respecting quotes
22
+ for (const char of input) {
23
+ if (char === '"' && !inQuotes) {
24
+ inQuotes = true;
25
+ } else if (char === '"' && inQuotes) {
26
+ inQuotes = false;
27
+ } else if (char === ' ' && !inQuotes) {
28
+ if (current) {
29
+ parts.push(current);
30
+ current = '';
31
+ }
32
+ } else {
33
+ current += char;
34
+ }
35
+ }
36
+ if (current) parts.push(current);
37
+
38
+ const result = {
39
+ command: parts[0] || 'status',
40
+ };
41
+
42
+ // Parse flags
43
+ for (let i = 1; i < parts.length; i++) {
44
+ const part = parts[i];
45
+
46
+ if (part === '--daily' && parts[i + 1]) {
47
+ result.daily = parseFloat(parts[i + 1]);
48
+ i++;
49
+ } else if (part === '--monthly' && parts[i + 1]) {
50
+ result.monthly = parseFloat(parts[i + 1]);
51
+ i++;
52
+ } else if (part === '--from' && parts[i + 1]) {
53
+ result.from = parts[i + 1];
54
+ i++;
55
+ } else if (part === '--to' && parts[i + 1]) {
56
+ result.to = parts[i + 1];
57
+ i++;
58
+ } else if (part === '--compare') {
59
+ result.compare = true;
60
+ } else if (!part.startsWith('--')) {
61
+ result.prompt = part;
62
+ }
63
+ }
64
+
65
+ return result;
66
+ }
67
+
68
+ /**
69
+ * Format status output
70
+ * @param {Object} status - Status data
71
+ * @returns {string} Formatted output
72
+ */
73
+ function formatStatus(status) {
74
+ const lines = [
75
+ 'Cost Status',
76
+ '═'.repeat(40),
77
+ '',
78
+ `Daily: $${status.dailySpend.toFixed(2)} / $${status.dailyBudget.toFixed(2)} ($${status.dailyRemaining.toFixed(2)} remaining)`,
79
+ `Monthly: $${status.monthlySpend.toFixed(2)} / $${status.monthlyBudget.toFixed(2)} ($${status.monthlyRemaining.toFixed(2)} remaining)`,
80
+ ];
81
+
82
+ if (status.byModel && Object.keys(status.byModel).length > 0) {
83
+ lines.push('', 'By Model:');
84
+ for (const [model, cost] of Object.entries(status.byModel)) {
85
+ lines.push(` ${model}: $${cost.toFixed(2)}`);
86
+ }
87
+ }
88
+
89
+ return lines.join('\n');
90
+ }
91
+
92
+ /**
93
+ * Cost Command class
94
+ */
95
+ class CostCommand {
96
+ /**
97
+ * Create a cost command instance
98
+ * @param {Object} options - Dependencies
99
+ * @param {Object} options.tracker - Cost tracker instance
100
+ * @param {Object} options.pricing - Pricing module
101
+ * @param {Object} options.budget - Budget manager
102
+ */
103
+ constructor(options) {
104
+ this.tracker = options.tracker;
105
+ this.pricing = options.pricing;
106
+ this.budget = options.budget;
107
+ this.optimizer = createOptimizer();
108
+ }
109
+
110
+ /**
111
+ * Execute a command
112
+ * @param {string} input - Command input
113
+ * @returns {Object} Execution result
114
+ */
115
+ async execute(input) {
116
+ const args = parseArgs(input);
117
+
118
+ switch (args.command) {
119
+ case 'status':
120
+ return this.executeStatus();
121
+
122
+ case 'budget':
123
+ return this.executeBudget(args);
124
+
125
+ case 'report':
126
+ return this.executeReport(args);
127
+
128
+ case 'estimate':
129
+ return this.executeEstimate(args);
130
+
131
+ case 'optimize':
132
+ return this.executeOptimize();
133
+
134
+ default:
135
+ return {
136
+ success: false,
137
+ output: `Unknown command: ${args.command}`,
138
+ };
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Execute status command
144
+ * @returns {Object} Status result
145
+ */
146
+ executeStatus() {
147
+ const today = new Date().toISOString().split('T')[0];
148
+
149
+ const status = {
150
+ dailySpend: this.tracker.getDailyCost(today),
151
+ monthlySpend: this.tracker.getMonthlyCost(),
152
+ dailyBudget: this.budget.getDailyBudget() || 0,
153
+ monthlyBudget: this.budget.getMonthlyBudget() || 0,
154
+ byModel: this.tracker.getCostByModel(),
155
+ };
156
+
157
+ const remaining = this.budget.budgetRemaining({
158
+ currentSpend: status.dailySpend,
159
+ });
160
+
161
+ status.dailyRemaining = remaining.daily || 0;
162
+ status.monthlyRemaining = remaining.monthly || 0;
163
+
164
+ return {
165
+ success: true,
166
+ output: formatStatus(status),
167
+ status,
168
+ };
169
+ }
170
+
171
+ /**
172
+ * Execute budget command
173
+ * @param {Object} args - Parsed arguments
174
+ * @returns {Object} Budget result
175
+ */
176
+ executeBudget(args) {
177
+ if (args.daily !== undefined) {
178
+ this.budget.setBudget({ type: 'daily', limit: args.daily });
179
+ }
180
+
181
+ if (args.monthly !== undefined) {
182
+ this.budget.setBudget({ type: 'monthly', limit: args.monthly });
183
+ }
184
+
185
+ return {
186
+ success: true,
187
+ output: `Budget updated${args.daily ? ` - Daily: $${args.daily}` : ''}${args.monthly ? ` - Monthly: $${args.monthly}` : ''}`,
188
+ };
189
+ }
190
+
191
+ /**
192
+ * Execute report command
193
+ * @param {Object} args - Parsed arguments
194
+ * @returns {Object} Report result
195
+ */
196
+ executeReport(args) {
197
+ const records = this.tracker.getRecords?.({
198
+ startDate: args.from,
199
+ endDate: args.to,
200
+ }) || [];
201
+
202
+ const report = generateReport(records);
203
+ report.byModel = groupByModel(records);
204
+ report.byOperation = groupByOperation(records);
205
+
206
+ return {
207
+ success: true,
208
+ output: formatReport(report),
209
+ report,
210
+ };
211
+ }
212
+
213
+ /**
214
+ * Execute estimate command
215
+ * @param {Object} args - Parsed arguments
216
+ * @returns {Object} Estimate result
217
+ */
218
+ executeEstimate(args) {
219
+ const prompt = args.prompt || '';
220
+ const model = 'claude-3-sonnet'; // Default model
221
+
222
+ if (args.compare) {
223
+ const models = ['claude-3-opus', 'claude-3-sonnet', 'claude-3-haiku', 'gpt-4', 'gpt-4o'];
224
+ const comparison = compareModels({ prompt, taskType: 'code-generation', models });
225
+
226
+ const lines = ['Cost Comparison:', ''];
227
+ for (const proj of comparison) {
228
+ lines.push(`${proj.model}: $${proj.estimatedCost.toFixed(4)}`);
229
+ }
230
+
231
+ return {
232
+ success: true,
233
+ output: lines.join('\n'),
234
+ comparison,
235
+ };
236
+ }
237
+
238
+ const estimate = projectCost({
239
+ prompt,
240
+ model,
241
+ taskType: 'code-generation',
242
+ });
243
+
244
+ return {
245
+ success: true,
246
+ output: `Estimated cost for "${prompt.substring(0, 30)}...": $${estimate.estimatedCost.toFixed(4)}`,
247
+ estimate,
248
+ };
249
+ }
250
+
251
+ /**
252
+ * Execute optimize command
253
+ * @returns {Object} Optimization result
254
+ */
255
+ executeOptimize() {
256
+ const records = this.tracker.getRecords?.() || [];
257
+ const analysis = analyzeUsage(this.optimizer, records);
258
+
259
+ const suggestions = [];
260
+
261
+ // Check for expensive operations using high-cost models
262
+ for (const op of analysis.expensiveOperations.slice(0, 3)) {
263
+ const suggestion = suggestCheaperModel(this.optimizer, {
264
+ currentModel: op.model,
265
+ taskType: op.operation || 'code-generation',
266
+ });
267
+
268
+ if (suggestion) {
269
+ suggestions.push({
270
+ type: 'model',
271
+ current: op.model,
272
+ suggested: suggestion.alternativeModel,
273
+ savings: suggestion.estimatedSavings,
274
+ });
275
+ }
276
+ }
277
+
278
+ return {
279
+ success: true,
280
+ output: formatSuggestions(suggestions),
281
+ suggestions,
282
+ };
283
+ }
284
+ }
285
+
286
+ module.exports = {
287
+ CostCommand,
288
+ parseArgs,
289
+ formatStatus,
290
+ };