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,139 @@
1
+ /**
2
+ * Agents Cancel Command
3
+ * Cancel running agents
4
+ */
5
+
6
+ /**
7
+ * Check if confirmation is required
8
+ * @param {object} options - Command options
9
+ * @returns {boolean} True if confirmation needed
10
+ */
11
+ function requiresConfirmation(options) {
12
+ return !options.force;
13
+ }
14
+
15
+ /**
16
+ * Cancel a single agent
17
+ * @param {object} agent - Agent to cancel
18
+ * @param {Function} updateAgent - Function to update agent
19
+ * @returns {object} Cancelled agent
20
+ */
21
+ function cancelAgent(agent, updateAgent) {
22
+ return updateAgent(agent.id, {
23
+ status: 'cancelled',
24
+ endTime: new Date(),
25
+ });
26
+ }
27
+
28
+ /**
29
+ * Cancel all running agents
30
+ * @param {Array} agents - All agents
31
+ * @param {Function} updateAgent - Function to update agent
32
+ * @returns {number} Count of cancelled agents
33
+ */
34
+ function cancelAllRunning(agents, updateAgent) {
35
+ const running = agents.filter(a => a.status === 'running' || a.status === 'queued');
36
+ for (const agent of running) {
37
+ cancelAgent(agent, updateAgent);
38
+ }
39
+ return running.length;
40
+ }
41
+
42
+ /**
43
+ * Check if agent can be cancelled
44
+ * @param {object} agent - Agent to check
45
+ * @returns {object} Result with canCancel and reason
46
+ */
47
+ function canCancel(agent) {
48
+ if (!agent) {
49
+ return { canCancel: false, reason: 'Agent not found' };
50
+ }
51
+
52
+ if (agent.status === 'cancelled') {
53
+ return { canCancel: false, reason: 'Agent is already cancelled' };
54
+ }
55
+
56
+ if (agent.status === 'completed') {
57
+ return { canCancel: false, reason: 'Agent is already completed' };
58
+ }
59
+
60
+ if (agent.status === 'failed') {
61
+ return { canCancel: false, reason: 'Agent has already failed' };
62
+ }
63
+
64
+ return { canCancel: true };
65
+ }
66
+
67
+ /**
68
+ * Execute agents cancel command
69
+ * @param {object} context - Execution context
70
+ * @returns {Promise<object>} Command result
71
+ */
72
+ async function execute(context) {
73
+ const { registry, agentId, options = {}, onCleanup } = context;
74
+
75
+ // Handle --all flag
76
+ if (options.all) {
77
+ if (requiresConfirmation(options)) {
78
+ const running = registry.listAgents().filter(a =>
79
+ a.status === 'running' || a.status === 'queued'
80
+ );
81
+ return {
82
+ success: false,
83
+ needsConfirmation: true,
84
+ message: `Cancel ${running.length} running agents?`,
85
+ count: running.length,
86
+ };
87
+ }
88
+
89
+ const agents = registry.listAgents();
90
+ const count = cancelAllRunning(agents, registry.updateAgent.bind(registry));
91
+
92
+ if (onCleanup) onCleanup();
93
+
94
+ return {
95
+ success: true,
96
+ cancelled: count,
97
+ message: `Cancelled ${count} agents`,
98
+ };
99
+ }
100
+
101
+ // Single agent cancel
102
+ const agent = registry.getAgent(agentId);
103
+ const { canCancel: allowed, reason } = canCancel(agent);
104
+
105
+ if (!allowed) {
106
+ return {
107
+ success: false,
108
+ error: reason,
109
+ };
110
+ }
111
+
112
+ if (requiresConfirmation(options)) {
113
+ return {
114
+ success: false,
115
+ needsConfirmation: true,
116
+ message: `Cancel agent ${agentId}?`,
117
+ agent,
118
+ };
119
+ }
120
+
121
+ cancelAgent(agent, registry.updateAgent.bind(registry));
122
+
123
+ if (onCleanup) onCleanup();
124
+
125
+ return {
126
+ success: true,
127
+ cancelled: 1,
128
+ message: `Cancelled agent ${agentId}`,
129
+ agent,
130
+ };
131
+ }
132
+
133
+ module.exports = {
134
+ execute,
135
+ cancelAgent,
136
+ cancelAllRunning,
137
+ requiresConfirmation,
138
+ canCancel,
139
+ };
@@ -0,0 +1,180 @@
1
+ const { describe, it, beforeEach, mock } = require('node:test');
2
+ const assert = require('node:assert');
3
+ const {
4
+ execute,
5
+ cancelAgent,
6
+ cancelAllRunning,
7
+ requiresConfirmation,
8
+ } = require('./agents-cancel-command.js');
9
+
10
+ describe('agents-cancel-command', () => {
11
+ describe('execute', () => {
12
+ const createMockRegistry = (agents = []) => ({
13
+ getAgent: (id) => agents.find(a => a.id === id),
14
+ listAgents: () => agents,
15
+ updateAgent: (id, updates) => {
16
+ const agent = agents.find(a => a.id === id);
17
+ if (agent) Object.assign(agent, updates);
18
+ return agent;
19
+ },
20
+ });
21
+
22
+ it('cancels agent', async () => {
23
+ const agents = [
24
+ { id: 'agent-1', status: 'running' },
25
+ ];
26
+ const result = await execute({
27
+ registry: createMockRegistry(agents),
28
+ agentId: 'agent-1',
29
+ options: { force: true },
30
+ });
31
+ assert.ok(result.success);
32
+ assert.strictEqual(agents[0].status, 'cancelled');
33
+ });
34
+
35
+ it('shows confirmation prompt', async () => {
36
+ const agents = [{ id: 'agent-1', status: 'running' }];
37
+ const result = await execute({
38
+ registry: createMockRegistry(agents),
39
+ agentId: 'agent-1',
40
+ options: {},
41
+ });
42
+ assert.ok(result.needsConfirmation);
43
+ });
44
+
45
+ it('with --force skips prompt', async () => {
46
+ const agents = [{ id: 'agent-1', status: 'running' }];
47
+ const result = await execute({
48
+ registry: createMockRegistry(agents),
49
+ agentId: 'agent-1',
50
+ options: { force: true },
51
+ });
52
+ assert.ok(result.success);
53
+ assert.ok(!result.needsConfirmation);
54
+ });
55
+
56
+ it('--all cancels all running', async () => {
57
+ const agents = [
58
+ { id: 'agent-1', status: 'running' },
59
+ { id: 'agent-2', status: 'running' },
60
+ { id: 'agent-3', status: 'completed' },
61
+ ];
62
+ const result = await execute({
63
+ registry: createMockRegistry(agents),
64
+ options: { all: true, force: true },
65
+ });
66
+ assert.ok(result.success);
67
+ assert.strictEqual(result.cancelled, 2);
68
+ });
69
+
70
+ it('handles unknown agent ID', async () => {
71
+ const result = await execute({
72
+ registry: createMockRegistry([]),
73
+ agentId: 'unknown',
74
+ options: { force: true },
75
+ });
76
+ assert.strictEqual(result.success, false);
77
+ assert.ok(result.error.includes('not found'));
78
+ });
79
+
80
+ it('handles already cancelled', async () => {
81
+ const agents = [{ id: 'agent-1', status: 'cancelled' }];
82
+ const result = await execute({
83
+ registry: createMockRegistry(agents),
84
+ agentId: 'agent-1',
85
+ options: { force: true },
86
+ });
87
+ assert.strictEqual(result.success, false);
88
+ assert.ok(result.error.includes('already'));
89
+ });
90
+
91
+ it('handles already completed', async () => {
92
+ const agents = [{ id: 'agent-1', status: 'completed' }];
93
+ const result = await execute({
94
+ registry: createMockRegistry(agents),
95
+ agentId: 'agent-1',
96
+ options: { force: true },
97
+ });
98
+ assert.strictEqual(result.success, false);
99
+ assert.ok(result.error.includes('already') || result.error.includes('completed'));
100
+ });
101
+
102
+ it('shows result message', async () => {
103
+ const agents = [{ id: 'agent-1', status: 'running' }];
104
+ const result = await execute({
105
+ registry: createMockRegistry(agents),
106
+ agentId: 'agent-1',
107
+ options: { force: true },
108
+ });
109
+ assert.ok(result.message);
110
+ assert.ok(result.message.includes('cancelled') || result.message.includes('Cancelled'));
111
+ });
112
+
113
+ it('updates agent state', async () => {
114
+ const agents = [{ id: 'agent-1', status: 'running' }];
115
+ await execute({
116
+ registry: createMockRegistry(agents),
117
+ agentId: 'agent-1',
118
+ options: { force: true },
119
+ });
120
+ assert.strictEqual(agents[0].status, 'cancelled');
121
+ });
122
+
123
+ it('triggers cleanup', async () => {
124
+ let cleanupCalled = false;
125
+ const agents = [{ id: 'agent-1', status: 'running' }];
126
+ await execute({
127
+ registry: createMockRegistry(agents),
128
+ agentId: 'agent-1',
129
+ options: { force: true },
130
+ onCleanup: () => { cleanupCalled = true; },
131
+ });
132
+ assert.ok(cleanupCalled);
133
+ });
134
+ });
135
+
136
+ describe('cancelAgent', () => {
137
+ it('sets status to cancelled', () => {
138
+ const agent = { id: 'agent-1', status: 'running' };
139
+ const updateAgent = (id, updates) => Object.assign(agent, updates);
140
+ cancelAgent(agent, updateAgent);
141
+ assert.strictEqual(agent.status, 'cancelled');
142
+ });
143
+
144
+ it('sets end time', () => {
145
+ const agent = { id: 'agent-1', status: 'running' };
146
+ const updateAgent = (id, updates) => Object.assign(agent, updates);
147
+ cancelAgent(agent, updateAgent);
148
+ assert.ok(agent.endTime);
149
+ });
150
+ });
151
+
152
+ describe('cancelAllRunning', () => {
153
+ it('cancels only running agents', () => {
154
+ const agents = [
155
+ { id: '1', status: 'running' },
156
+ { id: '2', status: 'completed' },
157
+ { id: '3', status: 'running' },
158
+ ];
159
+ const updateAgent = (id, updates) => {
160
+ const agent = agents.find(a => a.id === id);
161
+ Object.assign(agent, updates);
162
+ };
163
+ const count = cancelAllRunning(agents, updateAgent);
164
+ assert.strictEqual(count, 2);
165
+ assert.strictEqual(agents[0].status, 'cancelled');
166
+ assert.strictEqual(agents[1].status, 'completed');
167
+ assert.strictEqual(agents[2].status, 'cancelled');
168
+ });
169
+ });
170
+
171
+ describe('requiresConfirmation', () => {
172
+ it('returns true without force', () => {
173
+ assert.strictEqual(requiresConfirmation({}), true);
174
+ });
175
+
176
+ it('returns false with force', () => {
177
+ assert.strictEqual(requiresConfirmation({ force: true }), false);
178
+ });
179
+ });
180
+ });
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Agents Get Command
3
+ * Show detailed agent information
4
+ */
5
+
6
+ /**
7
+ * Format timestamp
8
+ * @param {Date} date - Date to format
9
+ * @returns {string} Formatted timestamp
10
+ */
11
+ function formatTimestamp(date) {
12
+ if (!date) return '-';
13
+ return new Date(date).toLocaleString();
14
+ }
15
+
16
+ /**
17
+ * Format state history
18
+ * @param {Array} history - State history entries
19
+ * @returns {string} Formatted history
20
+ */
21
+ function formatStateHistory(history) {
22
+ if (!history || history.length === 0) {
23
+ return 'No state history';
24
+ }
25
+
26
+ return history.map(entry =>
27
+ ` ${formatTimestamp(entry.timestamp)} → ${entry.state}`
28
+ ).join('\n');
29
+ }
30
+
31
+ /**
32
+ * Format token breakdown
33
+ * @param {object} tokens - Token counts
34
+ * @returns {string} Formatted breakdown
35
+ */
36
+ function formatTokenBreakdown(tokens) {
37
+ if (!tokens) return 'No token data';
38
+
39
+ const input = tokens.input || 0;
40
+ const output = tokens.output || 0;
41
+ const total = input + output;
42
+
43
+ return [
44
+ ` Input: ${input.toLocaleString()}`,
45
+ ` Output: ${output.toLocaleString()}`,
46
+ ` Total: ${total.toLocaleString()}`,
47
+ ].join('\n');
48
+ }
49
+
50
+ /**
51
+ * Format cost breakdown
52
+ * @param {number} cost - Total cost
53
+ * @returns {string} Formatted cost
54
+ */
55
+ function formatCostBreakdown(cost) {
56
+ if (cost === undefined || cost === null) return 'No cost data';
57
+ return ` Total: $${cost.toFixed(4)}`;
58
+ }
59
+
60
+ /**
61
+ * Format quality scores
62
+ * @param {object} quality - Quality data
63
+ * @returns {string} Formatted quality
64
+ */
65
+ function formatQualityScores(quality) {
66
+ if (!quality) return 'No quality data';
67
+
68
+ const lines = [` Score: ${quality.score}%`];
69
+
70
+ if (quality.dimensions) {
71
+ for (const [dim, score] of Object.entries(quality.dimensions)) {
72
+ lines.push(` ${dim}: ${score}%`);
73
+ }
74
+ }
75
+
76
+ return lines.join('\n');
77
+ }
78
+
79
+ /**
80
+ * Format full agent details
81
+ * @param {object} agent - Agent to format
82
+ * @returns {string} Formatted details
83
+ */
84
+ function formatDetails(agent) {
85
+ const sections = [];
86
+
87
+ // Header
88
+ sections.push(`Agent: ${agent.id}`);
89
+ sections.push('='.repeat(50));
90
+
91
+ // Metadata
92
+ sections.push('\n📋 Metadata');
93
+ sections.push(` ID: ${agent.id}`);
94
+ sections.push(` Name: ${agent.name || '-'}`);
95
+ sections.push(` Model: ${agent.model}`);
96
+ sections.push(` Status: ${agent.status}`);
97
+ sections.push(` Start: ${formatTimestamp(agent.startTime)}`);
98
+ if (agent.endTime) {
99
+ sections.push(` End: ${formatTimestamp(agent.endTime)}`);
100
+ }
101
+
102
+ // State History
103
+ sections.push('\n📜 State History');
104
+ sections.push(formatStateHistory(agent.stateHistory));
105
+
106
+ // Tokens
107
+ sections.push('\n🔢 Tokens');
108
+ sections.push(formatTokenBreakdown(agent.tokens));
109
+
110
+ // Cost
111
+ sections.push('\n💰 Cost');
112
+ sections.push(formatCostBreakdown(agent.cost));
113
+
114
+ // Quality
115
+ sections.push('\n📊 Quality');
116
+ sections.push(formatQualityScores(agent.quality));
117
+
118
+ return sections.join('\n');
119
+ }
120
+
121
+ /**
122
+ * Execute agents get command
123
+ * @param {object} context - Execution context
124
+ * @returns {Promise<object>} Command result
125
+ */
126
+ async function execute(context) {
127
+ const { registry, agentId, options = {} } = context;
128
+
129
+ const agent = registry.getAgent(agentId);
130
+
131
+ if (!agent) {
132
+ return {
133
+ success: false,
134
+ error: `Agent not found: ${agentId}`,
135
+ };
136
+ }
137
+
138
+ let output;
139
+ if (options.json) {
140
+ output = JSON.stringify(agent, null, 2);
141
+ } else {
142
+ output = formatDetails(agent);
143
+ }
144
+
145
+ return {
146
+ success: true,
147
+ agent,
148
+ output,
149
+ };
150
+ }
151
+
152
+ module.exports = {
153
+ execute,
154
+ formatDetails,
155
+ formatStateHistory,
156
+ formatTokenBreakdown,
157
+ formatCostBreakdown,
158
+ formatQualityScores,
159
+ };
@@ -0,0 +1,167 @@
1
+ const { describe, it, beforeEach, mock } = require('node:test');
2
+ const assert = require('node:assert');
3
+ const {
4
+ execute,
5
+ formatDetails,
6
+ formatStateHistory,
7
+ formatTokenBreakdown,
8
+ formatCostBreakdown,
9
+ formatQualityScores,
10
+ } = require('./agents-get-command.js');
11
+
12
+ describe('agents-get-command', () => {
13
+ const mockAgent = {
14
+ id: 'agent-123',
15
+ name: 'Code Builder',
16
+ model: 'claude-3-opus',
17
+ status: 'completed',
18
+ startTime: new Date(Date.now() - 120000),
19
+ endTime: new Date(),
20
+ tokens: { input: 5000, output: 2500 },
21
+ cost: 0.25,
22
+ quality: { score: 85, dimensions: { correctness: 90, completeness: 80 } },
23
+ stateHistory: [
24
+ { state: 'queued', timestamp: new Date(Date.now() - 180000) },
25
+ { state: 'running', timestamp: new Date(Date.now() - 120000) },
26
+ { state: 'completed', timestamp: new Date() },
27
+ ],
28
+ };
29
+
30
+ describe('execute', () => {
31
+ it('shows agent details', async () => {
32
+ const result = await execute({
33
+ registry: { getAgent: () => mockAgent },
34
+ agentId: 'agent-123',
35
+ });
36
+ assert.ok(result.output);
37
+ assert.ok(result.output.includes('agent-123'));
38
+ });
39
+
40
+ it('shows metadata section', async () => {
41
+ const result = await execute({
42
+ registry: { getAgent: () => mockAgent },
43
+ agentId: 'agent-123',
44
+ });
45
+ assert.ok(result.output.includes('claude-3-opus'));
46
+ assert.ok(result.output.includes('completed'));
47
+ });
48
+
49
+ it('shows state history', async () => {
50
+ const result = await execute({
51
+ registry: { getAgent: () => mockAgent },
52
+ agentId: 'agent-123',
53
+ });
54
+ assert.ok(result.output.includes('queued') || result.output.includes('running'));
55
+ });
56
+
57
+ it('shows token breakdown', async () => {
58
+ const result = await execute({
59
+ registry: { getAgent: () => mockAgent },
60
+ agentId: 'agent-123',
61
+ });
62
+ assert.ok(result.output.includes('5000') || result.output.includes('5,000') || result.output.includes('Input'));
63
+ });
64
+
65
+ it('shows cost breakdown', async () => {
66
+ const result = await execute({
67
+ registry: { getAgent: () => mockAgent },
68
+ agentId: 'agent-123',
69
+ });
70
+ assert.ok(result.output.includes('0.25') || result.output.includes('$'));
71
+ });
72
+
73
+ it('shows quality scores', async () => {
74
+ const result = await execute({
75
+ registry: { getAgent: () => mockAgent },
76
+ agentId: 'agent-123',
77
+ });
78
+ assert.ok(result.output.includes('85') || result.output.includes('Quality'));
79
+ });
80
+
81
+ it('handles unknown agent ID', async () => {
82
+ const result = await execute({
83
+ registry: { getAgent: () => null },
84
+ agentId: 'unknown',
85
+ });
86
+ assert.strictEqual(result.success, false);
87
+ assert.ok(result.error.includes('not found') || result.error.includes('Unknown'));
88
+ });
89
+
90
+ it('includes timestamps', async () => {
91
+ const result = await execute({
92
+ registry: { getAgent: () => mockAgent },
93
+ agentId: 'agent-123',
94
+ });
95
+ assert.ok(result.output.includes(':') || result.output.includes('Time'));
96
+ });
97
+
98
+ it('JSON output option works', async () => {
99
+ const result = await execute({
100
+ registry: { getAgent: () => mockAgent },
101
+ agentId: 'agent-123',
102
+ options: { json: true },
103
+ });
104
+ const parsed = JSON.parse(result.output);
105
+ assert.strictEqual(parsed.id, 'agent-123');
106
+ });
107
+ });
108
+
109
+ describe('formatDetails', () => {
110
+ it('creates sections', () => {
111
+ const result = formatDetails(mockAgent);
112
+ assert.ok(result.includes('Agent:') || result.includes('ID:'));
113
+ assert.ok(result.includes('Model:') || result.includes('claude'));
114
+ });
115
+ });
116
+
117
+ describe('formatStateHistory', () => {
118
+ it('shows state transitions', () => {
119
+ const result = formatStateHistory(mockAgent.stateHistory);
120
+ assert.ok(result.includes('queued'));
121
+ assert.ok(result.includes('running'));
122
+ assert.ok(result.includes('completed'));
123
+ });
124
+
125
+ it('handles empty history', () => {
126
+ const result = formatStateHistory([]);
127
+ assert.ok(result.includes('No') || result === '');
128
+ });
129
+ });
130
+
131
+ describe('formatTokenBreakdown', () => {
132
+ it('shows input and output', () => {
133
+ const result = formatTokenBreakdown({ input: 5000, output: 2500 });
134
+ assert.ok(result.includes('5000') || result.includes('5,000'));
135
+ assert.ok(result.includes('2500') || result.includes('2,500'));
136
+ });
137
+
138
+ it('shows total', () => {
139
+ const result = formatTokenBreakdown({ input: 5000, output: 2500 });
140
+ assert.ok(result.includes('7500') || result.includes('7,500') || result.includes('Total'));
141
+ });
142
+ });
143
+
144
+ describe('formatCostBreakdown', () => {
145
+ it('shows cost', () => {
146
+ const result = formatCostBreakdown(0.25);
147
+ assert.ok(result.includes('0.25') || result.includes('$'));
148
+ });
149
+ });
150
+
151
+ describe('formatQualityScores', () => {
152
+ it('shows main score', () => {
153
+ const result = formatQualityScores({ score: 85, dimensions: {} });
154
+ assert.ok(result.includes('85'));
155
+ });
156
+
157
+ it('shows dimensions', () => {
158
+ const result = formatQualityScores({ score: 85, dimensions: { correctness: 90 } });
159
+ assert.ok(result.includes('90') || result.includes('correctness'));
160
+ });
161
+
162
+ it('handles no quality data', () => {
163
+ const result = formatQualityScores(null);
164
+ assert.ok(result.includes('No') || result === '');
165
+ });
166
+ });
167
+ });