myshell-tools 1.0.0 → 2.0.0-alpha.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/CHANGELOG.md +44 -69
- package/LICENSE +21 -21
- package/README.md +178 -318
- package/dist/cli.d.ts +8 -0
- package/dist/cli.js +106 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/cost.d.ts +36 -0
- package/dist/commands/cost.js +103 -0
- package/dist/commands/cost.js.map +1 -0
- package/dist/commands/doctor.d.ts +36 -0
- package/dist/commands/doctor.js +115 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/core/assess.d.ts +25 -0
- package/dist/core/assess.js +142 -0
- package/dist/core/assess.js.map +1 -0
- package/dist/core/classify.d.ts +19 -0
- package/dist/core/classify.js +80 -0
- package/dist/core/classify.js.map +1 -0
- package/dist/core/escalate.d.ts +32 -0
- package/dist/core/escalate.js +57 -0
- package/dist/core/escalate.js.map +1 -0
- package/dist/core/index.d.ts +13 -0
- package/dist/core/index.js +12 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/orchestrate.d.ts +42 -0
- package/dist/core/orchestrate.js +439 -0
- package/dist/core/orchestrate.js.map +1 -0
- package/dist/core/policy.d.ts +9 -0
- package/dist/core/policy.js +27 -0
- package/dist/core/policy.js.map +1 -0
- package/dist/core/prompt.d.ts +26 -0
- package/dist/core/prompt.js +125 -0
- package/dist/core/prompt.js.map +1 -0
- package/dist/core/review.d.ts +46 -0
- package/dist/core/review.js +148 -0
- package/dist/core/review.js.map +1 -0
- package/dist/core/route.d.ts +28 -0
- package/dist/core/route.js +52 -0
- package/dist/core/route.js.map +1 -0
- package/dist/core/types.d.ts +141 -0
- package/dist/core/types.js +14 -0
- package/dist/core/types.js.map +1 -0
- package/dist/infra/atomic.d.ts +53 -0
- package/dist/infra/atomic.js +171 -0
- package/dist/infra/atomic.js.map +1 -0
- package/dist/infra/clock.d.ts +9 -0
- package/dist/infra/clock.js +15 -0
- package/dist/infra/clock.js.map +1 -0
- package/dist/infra/index.d.ts +9 -0
- package/dist/infra/index.js +7 -0
- package/dist/infra/index.js.map +1 -0
- package/dist/infra/ledger.d.ts +49 -0
- package/dist/infra/ledger.js +90 -0
- package/dist/infra/ledger.js.map +1 -0
- package/dist/infra/paths.d.ts +28 -0
- package/dist/infra/paths.js +38 -0
- package/dist/infra/paths.js.map +1 -0
- package/dist/infra/pricing.d.ts +47 -0
- package/dist/infra/pricing.js +151 -0
- package/dist/infra/pricing.js.map +1 -0
- package/dist/infra/session.d.ts +28 -0
- package/dist/infra/session.js +61 -0
- package/dist/infra/session.js.map +1 -0
- package/dist/interface/render.d.ts +27 -0
- package/dist/interface/render.js +134 -0
- package/dist/interface/render.js.map +1 -0
- package/dist/interface/repl.d.ts +23 -0
- package/dist/interface/repl.js +90 -0
- package/dist/interface/repl.js.map +1 -0
- package/dist/interface/run.d.ts +20 -0
- package/dist/interface/run.js +31 -0
- package/dist/interface/run.js.map +1 -0
- package/dist/providers/claude-parse.d.ts +24 -0
- package/dist/providers/claude-parse.js +113 -0
- package/dist/providers/claude-parse.js.map +1 -0
- package/dist/providers/claude.d.ts +45 -0
- package/dist/providers/claude.js +122 -0
- package/dist/providers/claude.js.map +1 -0
- package/dist/providers/codex-parse.d.ts +32 -0
- package/dist/providers/codex-parse.js +145 -0
- package/dist/providers/codex-parse.js.map +1 -0
- package/dist/providers/codex.d.ts +44 -0
- package/dist/providers/codex.js +124 -0
- package/dist/providers/codex.js.map +1 -0
- package/dist/providers/detect.d.ts +49 -0
- package/dist/providers/detect.js +125 -0
- package/dist/providers/detect.js.map +1 -0
- package/dist/providers/errors.d.ts +49 -0
- package/dist/providers/errors.js +189 -0
- package/dist/providers/errors.js.map +1 -0
- package/dist/providers/index.d.ts +9 -0
- package/dist/providers/index.js +7 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/port.d.ts +74 -0
- package/dist/providers/port.js +16 -0
- package/dist/providers/port.js.map +1 -0
- package/dist/providers/registry.d.ts +21 -0
- package/dist/providers/registry.js +34 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/ui/banner.d.ts +19 -0
- package/dist/ui/banner.js +32 -0
- package/dist/ui/banner.js.map +1 -0
- package/dist/ui/spinner.d.ts +27 -0
- package/dist/ui/spinner.js +67 -0
- package/dist/ui/spinner.js.map +1 -0
- package/dist/ui/theme.d.ts +32 -0
- package/dist/ui/theme.js +56 -0
- package/dist/ui/theme.js.map +1 -0
- package/package.json +55 -49
- package/data/orchestrator.json +0 -113
- package/src/auth/recovery.mjs +0 -328
- package/src/auth/refresh.mjs +0 -373
- package/src/chef.mjs +0 -348
- package/src/cli/doctor.mjs +0 -568
- package/src/cli/reset.mjs +0 -447
- package/src/cli/status.mjs +0 -379
- package/src/cli.mjs +0 -429
- package/src/commands/doctor.mjs +0 -375
- package/src/commands/help.mjs +0 -324
- package/src/commands/status.mjs +0 -331
- package/src/monitor/health.mjs +0 -486
- package/src/monitor/performance.mjs +0 -442
- package/src/monitor/report.mjs +0 -535
- package/src/orchestrator/classify.mjs +0 -391
- package/src/orchestrator/confidence.mjs +0 -151
- package/src/orchestrator/handoffs.mjs +0 -231
- package/src/orchestrator/review.mjs +0 -222
- package/src/providers/balance.mjs +0 -201
- package/src/providers/claude.mjs +0 -236
- package/src/providers/codex.mjs +0 -255
- package/src/providers/detect.mjs +0 -185
- package/src/providers/errors.mjs +0 -373
- package/src/providers/select.mjs +0 -162
- package/src/repl-enhanced.mjs +0 -417
- package/src/repl.mjs +0 -321
- package/src/state/archive.mjs +0 -366
- package/src/state/atomic.mjs +0 -116
- package/src/state/cleanup.mjs +0 -440
- package/src/state/recovery.mjs +0 -461
- package/src/state/session.mjs +0 -147
- package/src/ui/errors.mjs +0 -456
- package/src/ui/formatter.mjs +0 -327
- package/src/ui/icons.mjs +0 -318
- package/src/ui/progress.mjs +0 -468
- package/templates/prompts/confidence-format.txt +0 -14
- package/templates/prompts/ic-with-feedback.txt +0 -41
- package/templates/prompts/ic.txt +0 -13
- package/templates/prompts/manager-review.txt +0 -40
- package/templates/prompts/manager.txt +0 -14
- package/templates/prompts/worker.txt +0 -12
package/src/repl.mjs
DELETED
|
@@ -1,321 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* repl.mjs — Enhanced interactive conversation loop with rich hierarchy display
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import * as readline from 'readline';
|
|
6
|
-
import { chef } from './chef.mjs';
|
|
7
|
-
import { addMessage, loadSession, getSessionSummary } from './state/session.mjs';
|
|
8
|
-
import { endPerformanceSession } from './monitor/performance.mjs';
|
|
9
|
-
import { displayQuickSummary } from './monitor/report.mjs';
|
|
10
|
-
import { fmt, separator, formatTier, formatDuration, timeAgo, formatConfidence } from './ui/formatter.mjs';
|
|
11
|
-
import { status, tier, session as sessionIcons, getConfidenceIcon } from './ui/icons.mjs';
|
|
12
|
-
import { createSpinner, HierarchicalProgress } from './ui/progress.mjs';
|
|
13
|
-
import { displayError, handleError } from './ui/errors.mjs';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Create and configure readline interface
|
|
17
|
-
*/
|
|
18
|
-
function createReadlineInterface() {
|
|
19
|
-
const rl = readline.createInterface({
|
|
20
|
-
input: process.stdin,
|
|
21
|
-
output: process.stdout,
|
|
22
|
-
prompt: 'You: ',
|
|
23
|
-
history: [],
|
|
24
|
-
historySize: 100
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
// Handle Ctrl+C gracefully with enhanced output
|
|
28
|
-
rl.on('SIGINT', () => {
|
|
29
|
-
console.log('\n');
|
|
30
|
-
const spinner = createSpinner('Finalizing session...');
|
|
31
|
-
spinner.start();
|
|
32
|
-
|
|
33
|
-
setTimeout(() => {
|
|
34
|
-
spinner.success('Session finalized');
|
|
35
|
-
endPerformanceSession();
|
|
36
|
-
displayQuickSummary();
|
|
37
|
-
console.log(fmt.success('Session saved. Goodbye!'));
|
|
38
|
-
process.exit(0);
|
|
39
|
-
}, 500);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
return rl;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Display enhanced session resume info
|
|
47
|
-
*/
|
|
48
|
-
function displaySessionResume() {
|
|
49
|
-
const summary = getSessionSummary();
|
|
50
|
-
|
|
51
|
-
if (summary.messageCount > 0) {
|
|
52
|
-
const sessionIcon = sessionIcons.active;
|
|
53
|
-
const lastTime = summary.lastMessage
|
|
54
|
-
? timeAgo(summary.lastMessage.timestamp)
|
|
55
|
-
: 'Unknown';
|
|
56
|
-
const duration = summary.duration ? formatDuration(summary.duration) : '';
|
|
57
|
-
|
|
58
|
-
console.log(`\n${sessionIcon} ${fmt.cyan('Resuming active session')}`);
|
|
59
|
-
console.log(` ${fmt.dim('Messages:')} ${fmt.bold(summary.userMessageCount)} exchanges`);
|
|
60
|
-
if (duration) {
|
|
61
|
-
console.log(` ${fmt.dim('Duration:')} ${fmt.bold(duration)}`);
|
|
62
|
-
}
|
|
63
|
-
console.log(` ${fmt.dim('Last activity:')} ${fmt.bold(lastTime)}`);
|
|
64
|
-
|
|
65
|
-
// Show recent context
|
|
66
|
-
if (summary.lastMessage) {
|
|
67
|
-
const preview = summary.lastMessage.content.slice(0, 80);
|
|
68
|
-
const truncated = summary.lastMessage.content.length > 80 ? '...' : '';
|
|
69
|
-
console.log(` ${fmt.dim('Context:')} ${fmt.dim(`"${preview}${truncated}"`)}`);
|
|
70
|
-
}
|
|
71
|
-
} else {
|
|
72
|
-
console.log(`\n${sessionIcons.new} ${fmt.green('Starting new session')}`);
|
|
73
|
-
console.log(` ${fmt.dim('Ready for AI-powered development workflow')}`);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Display enhanced result with rich formatting and visual hierarchy
|
|
79
|
-
*/
|
|
80
|
-
function displayResult(result) {
|
|
81
|
-
if (result.success) {
|
|
82
|
-
console.log('\n' + separator(60, '═', fmt.green('')));
|
|
83
|
-
|
|
84
|
-
// Status line with tier and model info
|
|
85
|
-
const tierIcon = tier[result.tier] || tier.worker;
|
|
86
|
-
const formattedTier = formatTier(result.tier);
|
|
87
|
-
console.log(`${status.success} ${fmt.success('Completed by')} ${tierIcon} ${formattedTier}`);
|
|
88
|
-
|
|
89
|
-
// Model information
|
|
90
|
-
if (result.model) {
|
|
91
|
-
const modelDisplay = `${result.model.provider}/${result.model.model}`;
|
|
92
|
-
console.log(` ${fmt.dim('Model:')} ${fmt.formatModel(result.model.provider, result.model.model)}`);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Confidence display with visual indicator
|
|
96
|
-
if (result.confidence !== null) {
|
|
97
|
-
const confidenceIcon = getConfidenceIcon(result.confidence);
|
|
98
|
-
const confidenceText = formatConfidence(result.confidence);
|
|
99
|
-
console.log(` ${confidenceIcon} ${fmt.dim('Confidence:')} ${confidenceText}`);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Escalation information
|
|
103
|
-
if (result.totalAttempts > 1) {
|
|
104
|
-
const escalations = result.totalAttempts - 1;
|
|
105
|
-
console.log(` ${status.loading} ${fmt.warning(`Escalations: ${escalations}`)}`);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Duration with smart formatting
|
|
109
|
-
if (result.durationMs) {
|
|
110
|
-
const duration = formatDuration(result.durationMs);
|
|
111
|
-
const durationColor = result.durationMs > 30000 ? fmt.yellow : fmt.green;
|
|
112
|
-
console.log(` ${fmt.dim('Duration:')} ${durationColor(duration)}`);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Token efficiency (if available)
|
|
116
|
-
if (result.tokensUsed) {
|
|
117
|
-
console.log(` ${fmt.dim('Tokens:')} ${fmt.cyan(result.tokensUsed.toLocaleString())}`);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
console.log(separator(60, '═', fmt.green('')));
|
|
121
|
-
console.log('\n' + result.output);
|
|
122
|
-
|
|
123
|
-
} else {
|
|
124
|
-
console.log('\n' + separator(60, '═', fmt.red('')));
|
|
125
|
-
|
|
126
|
-
// Error status line
|
|
127
|
-
const tierIcon = result.tier ? tier[result.tier] || tier.worker : status.error;
|
|
128
|
-
console.log(`${status.error} ${fmt.error('Task Failed')}`);
|
|
129
|
-
|
|
130
|
-
// Final tier information
|
|
131
|
-
if (result.tier) {
|
|
132
|
-
const formattedTier = formatTier(result.tier);
|
|
133
|
-
console.log(` ${fmt.dim('Final tier:')} ${tierIcon} ${formattedTier}`);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Attempt information
|
|
137
|
-
if (result.totalAttempts) {
|
|
138
|
-
console.log(` ${fmt.dim('Attempts:')} ${fmt.yellow(result.totalAttempts)}`);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Duration
|
|
142
|
-
if (result.durationMs) {
|
|
143
|
-
const duration = formatDuration(result.durationMs);
|
|
144
|
-
console.log(` ${fmt.dim('Duration:')} ${fmt.dim(duration)}`);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
console.log(separator(60, '═', fmt.red('')));
|
|
148
|
-
|
|
149
|
-
// Error message with proper formatting
|
|
150
|
-
const errorMsg = result.output || result.error || 'Unknown error';
|
|
151
|
-
console.log('\n' + fmt.error(errorMsg));
|
|
152
|
-
}
|
|
153
|
-
console.log('\n');
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Main REPL loop
|
|
158
|
-
*/
|
|
159
|
-
export async function startREPL(context) {
|
|
160
|
-
const rl = createReadlineInterface();
|
|
161
|
-
|
|
162
|
-
displaySessionResume();
|
|
163
|
-
|
|
164
|
-
console.log('\nType your request and press Enter. Use Ctrl+C to exit.\n');
|
|
165
|
-
|
|
166
|
-
const askQuestion = () => {
|
|
167
|
-
rl.prompt();
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
rl.on('line', async (input) => {
|
|
171
|
-
const userInput = input.trim();
|
|
172
|
-
|
|
173
|
-
if (!userInput) {
|
|
174
|
-
askQuestion();
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Handle special commands
|
|
179
|
-
if (userInput === '/quit' || userInput === '/exit') {
|
|
180
|
-
console.log('\nGoodbye! Session saved.');
|
|
181
|
-
rl.close();
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (userInput === '/clear') {
|
|
186
|
-
console.clear();
|
|
187
|
-
displaySessionResume();
|
|
188
|
-
askQuestion();
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
if (userInput === '/help') {
|
|
193
|
-
console.log(fmt.bold('\n📋 Available Commands:'));
|
|
194
|
-
console.log(separator(50, '─'));
|
|
195
|
-
|
|
196
|
-
const helpSections = [
|
|
197
|
-
{
|
|
198
|
-
title: 'Chat Commands:',
|
|
199
|
-
commands: [
|
|
200
|
-
{ cmd: '/help', desc: 'Show this help' },
|
|
201
|
-
{ cmd: '/clear', desc: 'Clear screen and show session info' },
|
|
202
|
-
{ cmd: '/status', desc: 'Show detailed session status' },
|
|
203
|
-
{ cmd: '/quit or /exit', desc: 'Exit Cortex gracefully' }
|
|
204
|
-
]
|
|
205
|
-
},
|
|
206
|
-
{
|
|
207
|
-
title: 'AI Org Chart Hierarchy:',
|
|
208
|
-
commands: [
|
|
209
|
-
{ cmd: `${tier.worker} WORKER`, desc: 'Simple tasks, file search, documentation' },
|
|
210
|
-
{ cmd: `${tier.ic} IC`, desc: 'Implementation, coding, main development work' },
|
|
211
|
-
{ cmd: `${tier.manager} MANAGER`, desc: 'Architecture, reviews, complex decisions' }
|
|
212
|
-
]
|
|
213
|
-
}
|
|
214
|
-
];
|
|
215
|
-
|
|
216
|
-
for (const section of helpSections) {
|
|
217
|
-
console.log(`\n ${fmt.bold(section.title)}`);
|
|
218
|
-
for (const item of section.commands) {
|
|
219
|
-
const cmdPadded = item.cmd.padEnd(20);
|
|
220
|
-
console.log(` ${fmt.cyan(cmdPadded)} ${fmt.dim('—')} ${item.desc}`);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
console.log(`\n ${fmt.dim('Cortex automatically routes tasks to the right tier and escalates when needed.')}`);
|
|
225
|
-
console.log('');
|
|
226
|
-
askQuestion();
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
if (userInput === '/status') {
|
|
231
|
-
const summary = getSessionSummary();
|
|
232
|
-
|
|
233
|
-
console.log('\n' + fmt.bold('📊 Session Status:'));
|
|
234
|
-
console.log(separator(40, '─'));
|
|
235
|
-
|
|
236
|
-
console.log(` ${sessionIcons.active} ${fmt.bold('Active Session')}`);
|
|
237
|
-
console.log(` Messages: ${fmt.cyan(summary.messageCount)} (${summary.userMessageCount} user, ${summary.assistantMessageCount} assistant)`);
|
|
238
|
-
|
|
239
|
-
if (summary.duration) {
|
|
240
|
-
const duration = formatDuration(summary.duration);
|
|
241
|
-
console.log(` Duration: ${fmt.cyan(duration)}`);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
if (summary.lastMessage) {
|
|
245
|
-
const lastTime = timeAgo(summary.lastMessage.timestamp);
|
|
246
|
-
console.log(` Last activity: ${fmt.cyan(lastTime)}`);
|
|
247
|
-
|
|
248
|
-
// Show performance stats if available
|
|
249
|
-
const avgResponseTime = '1.3s'; // Mock - replace with real data
|
|
250
|
-
const successRate = '96%'; // Mock - replace with real data
|
|
251
|
-
console.log(` Performance: ${fmt.green(avgResponseTime)} avg, ${fmt.green(successRate)} success`);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
console.log('');
|
|
255
|
-
askQuestion();
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// Save user message
|
|
260
|
-
addMessage('user', userInput);
|
|
261
|
-
|
|
262
|
-
// Process with the chef with enhanced progress indication
|
|
263
|
-
try {
|
|
264
|
-
// Show processing indicator
|
|
265
|
-
const spinner = createSpinner('Processing request...');
|
|
266
|
-
spinner.start();
|
|
267
|
-
|
|
268
|
-
const result = await chef(userInput, context);
|
|
269
|
-
|
|
270
|
-
// Stop spinner and show result
|
|
271
|
-
spinner.stop();
|
|
272
|
-
|
|
273
|
-
// Save assistant response
|
|
274
|
-
addMessage('assistant', result.output, {
|
|
275
|
-
tier: result.tier,
|
|
276
|
-
confidence: result.confidence,
|
|
277
|
-
success: result.success,
|
|
278
|
-
model: result.model ? `${result.model.provider}/${result.model.model}` : null,
|
|
279
|
-
duration: result.durationMs,
|
|
280
|
-
tokensUsed: result.tokensUsed
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
// Display enhanced result
|
|
284
|
-
displayResult(result);
|
|
285
|
-
|
|
286
|
-
} catch (error) {
|
|
287
|
-
// Enhanced error display
|
|
288
|
-
console.log('\n' + separator(60, '═', fmt.red('')));
|
|
289
|
-
console.log(`${status.error} ${fmt.error('Orchestration Error')}`);
|
|
290
|
-
console.log(` ${fmt.dim('Details:')} ${error.message}`);
|
|
291
|
-
console.log(` ${status.info} Try rephrasing your request or check --doctor`);
|
|
292
|
-
console.log(separator(60, '═', fmt.red('')) + '\n');
|
|
293
|
-
|
|
294
|
-
// Save error message
|
|
295
|
-
addMessage('assistant', `Error: ${error.message}`, {
|
|
296
|
-
success: false,
|
|
297
|
-
error: true,
|
|
298
|
-
timestamp: Date.now()
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
askQuestion();
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
rl.on('close', () => {
|
|
306
|
-
console.log('\n');
|
|
307
|
-
const spinner = createSpinner('Finalizing session...');
|
|
308
|
-
spinner.start();
|
|
309
|
-
|
|
310
|
-
setTimeout(() => {
|
|
311
|
-
spinner.success('Session finalized');
|
|
312
|
-
endPerformanceSession();
|
|
313
|
-
displayQuickSummary();
|
|
314
|
-
console.log(fmt.success('Session saved. Goodbye!'));
|
|
315
|
-
process.exit(0);
|
|
316
|
-
}, 500);
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
// Start the conversation
|
|
320
|
-
askQuestion();
|
|
321
|
-
}
|
package/src/state/archive.mjs
DELETED
|
@@ -1,366 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* archive.mjs — Session archiving and long-term state management
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { existsSync, readFileSync, writeFileSync, readdirSync, statSync, mkdirSync } from 'fs';
|
|
6
|
-
import { join } from 'path';
|
|
7
|
-
import { atomicWriteJSON } from './atomic.mjs';
|
|
8
|
-
import { loadSession, getSessionSummary } from './session.mjs';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Get archive directory
|
|
12
|
-
*/
|
|
13
|
-
function getArchiveDir(workspace = process.cwd()) {
|
|
14
|
-
const archiveDir = join(workspace, '.cortex', 'archive');
|
|
15
|
-
if (!existsSync(archiveDir)) {
|
|
16
|
-
mkdirSync(archiveDir, { recursive: true });
|
|
17
|
-
}
|
|
18
|
-
return archiveDir;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Create a comprehensive session archive
|
|
23
|
-
*/
|
|
24
|
-
export function createSessionArchive(reason = 'manual', metadata = {}, workspace = process.cwd()) {
|
|
25
|
-
const summary = getSessionSummary(workspace);
|
|
26
|
-
|
|
27
|
-
if (summary.messageCount === 0) {
|
|
28
|
-
return { archived: false, reason: 'No session data to archive' };
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const archiveId = `session-${Date.now()}-${Math.random().toString(36).substr(2, 8)}`;
|
|
32
|
-
const archiveDir = getArchiveDir(workspace);
|
|
33
|
-
|
|
34
|
-
const archive = {
|
|
35
|
-
id: archiveId,
|
|
36
|
-
timestamp: new Date().toISOString(),
|
|
37
|
-
reason,
|
|
38
|
-
summary,
|
|
39
|
-
messages: loadSession(workspace),
|
|
40
|
-
workspace: workspace,
|
|
41
|
-
metadata: {
|
|
42
|
-
...metadata,
|
|
43
|
-
cortexVersion: '1.0.0',
|
|
44
|
-
nodeVersion: process.version,
|
|
45
|
-
platform: process.platform
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
// Add performance metrics if available
|
|
50
|
-
if (summary.duration) {
|
|
51
|
-
archive.performance = {
|
|
52
|
-
totalDuration: summary.duration,
|
|
53
|
-
avgResponseTime: summary.duration / Math.max(summary.assistantMessageCount, 1),
|
|
54
|
-
messagesPerMinute: (summary.messageCount / (summary.duration / 60000)).toFixed(2)
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const archivePath = join(archiveDir, `${archiveId}.json`);
|
|
59
|
-
atomicWriteJSON(archivePath, archive);
|
|
60
|
-
|
|
61
|
-
// Create human-readable summary
|
|
62
|
-
const summaryPath = join(archiveDir, `${archiveId}-summary.md`);
|
|
63
|
-
const summaryContent = createMarkdownSummary(archive);
|
|
64
|
-
writeFileSync(summaryPath, summaryContent);
|
|
65
|
-
|
|
66
|
-
return {
|
|
67
|
-
archived: true,
|
|
68
|
-
archiveId,
|
|
69
|
-
archivePath,
|
|
70
|
-
summaryPath,
|
|
71
|
-
messageCount: summary.messageCount,
|
|
72
|
-
duration: summary.duration
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Create markdown summary of the session
|
|
78
|
-
*/
|
|
79
|
-
function createMarkdownSummary(archive) {
|
|
80
|
-
const { summary, messages, performance, reason, timestamp } = archive;
|
|
81
|
-
|
|
82
|
-
let content = `# Cortex Session Archive\n\n`;
|
|
83
|
-
content += `**Archive ID:** ${archive.id}\n`;
|
|
84
|
-
content += `**Created:** ${new Date(timestamp).toLocaleString()}\n`;
|
|
85
|
-
content += `**Reason:** ${reason}\n`;
|
|
86
|
-
content += `**Workspace:** ${archive.workspace}\n\n`;
|
|
87
|
-
|
|
88
|
-
content += `## Summary\n\n`;
|
|
89
|
-
content += `- **Total Messages:** ${summary.messageCount}\n`;
|
|
90
|
-
content += `- **User Messages:** ${summary.userMessageCount}\n`;
|
|
91
|
-
content += `- **Assistant Messages:** ${summary.assistantMessageCount}\n`;
|
|
92
|
-
|
|
93
|
-
if (summary.duration) {
|
|
94
|
-
const durationMin = Math.round(summary.duration / 60000);
|
|
95
|
-
content += `- **Duration:** ${durationMin} minutes\n`;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (performance) {
|
|
99
|
-
content += `\n## Performance\n\n`;
|
|
100
|
-
content += `- **Average Response Time:** ${Math.round(performance.avgResponseTime / 1000)}s\n`;
|
|
101
|
-
content += `- **Messages Per Minute:** ${performance.messagesPerMinute}\n`;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Add sample of conversations
|
|
105
|
-
const userMessages = messages.filter(m => m.role === 'user');
|
|
106
|
-
if (userMessages.length > 0) {
|
|
107
|
-
content += `\n## Conversation Topics\n\n`;
|
|
108
|
-
const sampleSize = Math.min(5, userMessages.length);
|
|
109
|
-
const samples = userMessages.slice(0, sampleSize);
|
|
110
|
-
|
|
111
|
-
for (const [index, msg] of samples.entries()) {
|
|
112
|
-
const preview = msg.content.length > 80 ?
|
|
113
|
-
msg.content.substring(0, 80) + '...' :
|
|
114
|
-
msg.content;
|
|
115
|
-
content += `${index + 1}. ${preview}\n`;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (userMessages.length > sampleSize) {
|
|
119
|
-
content += `\n_(${userMessages.length - sampleSize} more messages not shown)_\n`;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Add handoff statistics if available
|
|
124
|
-
const handoffMessages = messages.filter(m => m.type === 'handoff');
|
|
125
|
-
if (handoffMessages.length > 0) {
|
|
126
|
-
content += `\n## Hierarchy Activity\n\n`;
|
|
127
|
-
content += `- **Total Handoffs:** ${handoffMessages.length}\n`;
|
|
128
|
-
|
|
129
|
-
const escalations = handoffMessages.filter(m => m.operation === 'escalate').length;
|
|
130
|
-
const delegations = handoffMessages.filter(m => m.operation === 'delegate').length;
|
|
131
|
-
const bounces = handoffMessages.filter(m => m.operation === 'bounce').length;
|
|
132
|
-
|
|
133
|
-
if (escalations > 0) content += `- **Escalations:** ${escalations}\n`;
|
|
134
|
-
if (delegations > 0) content += `- **Delegations:** ${delegations}\n`;
|
|
135
|
-
if (bounces > 0) content += `- **Bounces:** ${bounces}\n`;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
content += `\n## Archive Details\n\n`;
|
|
139
|
-
content += `Full conversation data is available in: \`${archive.id}.json\`\n`;
|
|
140
|
-
|
|
141
|
-
return content;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* List available archives with metadata
|
|
146
|
-
*/
|
|
147
|
-
export function listArchives(workspace = process.cwd()) {
|
|
148
|
-
const archiveDir = getArchiveDir(workspace);
|
|
149
|
-
|
|
150
|
-
if (!existsSync(archiveDir)) {
|
|
151
|
-
return [];
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
try {
|
|
155
|
-
const archiveFiles = readdirSync(archiveDir)
|
|
156
|
-
.filter(f => f.endsWith('.json') && f.startsWith('session-'))
|
|
157
|
-
.map(f => join(archiveDir, f))
|
|
158
|
-
.map(filePath => {
|
|
159
|
-
try {
|
|
160
|
-
const archive = JSON.parse(readFileSync(filePath, 'utf8'));
|
|
161
|
-
const stat = statSync(filePath);
|
|
162
|
-
|
|
163
|
-
return {
|
|
164
|
-
id: archive.id,
|
|
165
|
-
timestamp: archive.timestamp,
|
|
166
|
-
reason: archive.reason,
|
|
167
|
-
messageCount: archive.summary.messageCount,
|
|
168
|
-
duration: archive.summary.duration,
|
|
169
|
-
fileSize: stat.size,
|
|
170
|
-
filePath
|
|
171
|
-
};
|
|
172
|
-
} catch {
|
|
173
|
-
return null;
|
|
174
|
-
}
|
|
175
|
-
})
|
|
176
|
-
.filter(Boolean)
|
|
177
|
-
.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
|
|
178
|
-
|
|
179
|
-
return archiveFiles;
|
|
180
|
-
} catch {
|
|
181
|
-
return [];
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Get archive by ID
|
|
187
|
-
*/
|
|
188
|
-
export function getArchive(archiveId, workspace = process.cwd()) {
|
|
189
|
-
const archiveDir = getArchiveDir(workspace);
|
|
190
|
-
const archivePath = join(archiveDir, `${archiveId}.json`);
|
|
191
|
-
|
|
192
|
-
if (!existsSync(archivePath)) {
|
|
193
|
-
throw new Error(`Archive ${archiveId} not found`);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
try {
|
|
197
|
-
return JSON.parse(readFileSync(archivePath, 'utf8'));
|
|
198
|
-
} catch (error) {
|
|
199
|
-
throw new Error(`Failed to load archive ${archiveId}: ${error.message}`);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Delete old archives to manage storage
|
|
205
|
-
*/
|
|
206
|
-
export function cleanupOldArchives(maxAge = 30, workspace = process.cwd()) {
|
|
207
|
-
const archiveDir = getArchiveDir(workspace);
|
|
208
|
-
const maxAgeMs = maxAge * 24 * 60 * 60 * 1000; // Convert days to ms
|
|
209
|
-
const cutoff = Date.now() - maxAgeMs;
|
|
210
|
-
|
|
211
|
-
if (!existsSync(archiveDir)) {
|
|
212
|
-
return { deleted: 0, preserved: 0, errors: [] };
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const archives = listArchives(workspace);
|
|
216
|
-
let deleted = 0;
|
|
217
|
-
let preserved = 0;
|
|
218
|
-
const errors = [];
|
|
219
|
-
|
|
220
|
-
for (const archive of archives) {
|
|
221
|
-
const archiveDate = new Date(archive.timestamp).getTime();
|
|
222
|
-
|
|
223
|
-
if (archiveDate < cutoff) {
|
|
224
|
-
try {
|
|
225
|
-
const archivePath = archive.filePath;
|
|
226
|
-
const summaryPath = archivePath.replace('.json', '-summary.md');
|
|
227
|
-
|
|
228
|
-
// Delete both files
|
|
229
|
-
if (existsSync(archivePath)) {
|
|
230
|
-
require('fs').unlinkSync(archivePath);
|
|
231
|
-
}
|
|
232
|
-
if (existsSync(summaryPath)) {
|
|
233
|
-
require('fs').unlinkSync(summaryPath);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
deleted++;
|
|
237
|
-
} catch (error) {
|
|
238
|
-
errors.push(`Failed to delete ${archive.id}: ${error.message}`);
|
|
239
|
-
}
|
|
240
|
-
} else {
|
|
241
|
-
preserved++;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return { deleted, preserved, errors };
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Export archives to external format
|
|
250
|
-
*/
|
|
251
|
-
export function exportArchives(format = 'json', outputPath = null, workspace = process.cwd()) {
|
|
252
|
-
const archives = listArchives(workspace);
|
|
253
|
-
|
|
254
|
-
if (archives.length === 0) {
|
|
255
|
-
return { exported: false, reason: 'No archives to export' };
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
const exportData = {
|
|
259
|
-
exported: new Date().toISOString(),
|
|
260
|
-
cortexVersion: '1.0.0',
|
|
261
|
-
workspace: workspace,
|
|
262
|
-
archiveCount: archives.length,
|
|
263
|
-
archives: archives.map(meta => {
|
|
264
|
-
try {
|
|
265
|
-
return getArchive(meta.id, workspace);
|
|
266
|
-
} catch (error) {
|
|
267
|
-
return { error: error.message, id: meta.id };
|
|
268
|
-
}
|
|
269
|
-
})
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
273
|
-
const filename = outputPath || join(workspace, `.cortex-export-${timestamp}.${format}`);
|
|
274
|
-
|
|
275
|
-
try {
|
|
276
|
-
if (format === 'json') {
|
|
277
|
-
writeFileSync(filename, JSON.stringify(exportData, null, 2));
|
|
278
|
-
} else {
|
|
279
|
-
throw new Error(`Unsupported export format: ${format}`);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
return {
|
|
283
|
-
exported: true,
|
|
284
|
-
filename,
|
|
285
|
-
archiveCount: archives.length,
|
|
286
|
-
fileSize: require('fs').statSync(filename).size
|
|
287
|
-
};
|
|
288
|
-
} catch (error) {
|
|
289
|
-
return {
|
|
290
|
-
exported: false,
|
|
291
|
-
error: error.message
|
|
292
|
-
};
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
/**
|
|
297
|
-
* Get storage usage statistics
|
|
298
|
-
*/
|
|
299
|
-
export function getStorageStats(workspace = process.cwd()) {
|
|
300
|
-
const cortexDir = join(workspace, '.cortex');
|
|
301
|
-
|
|
302
|
-
if (!existsSync(cortexDir)) {
|
|
303
|
-
return { totalSize: 0, breakdown: {} };
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
const stats = {
|
|
307
|
-
totalSize: 0,
|
|
308
|
-
breakdown: {
|
|
309
|
-
sessions: 0,
|
|
310
|
-
archives: 0,
|
|
311
|
-
plans: 0,
|
|
312
|
-
auth: 0,
|
|
313
|
-
other: 0
|
|
314
|
-
}
|
|
315
|
-
};
|
|
316
|
-
|
|
317
|
-
function calculateDirSize(dir, category = 'other') {
|
|
318
|
-
if (!existsSync(dir)) return 0;
|
|
319
|
-
|
|
320
|
-
let size = 0;
|
|
321
|
-
try {
|
|
322
|
-
const entries = readdirSync(dir);
|
|
323
|
-
|
|
324
|
-
for (const entry of entries) {
|
|
325
|
-
const fullPath = join(dir, entry);
|
|
326
|
-
const stat = statSync(fullPath);
|
|
327
|
-
|
|
328
|
-
if (stat.isDirectory()) {
|
|
329
|
-
size += calculateDirSize(fullPath, category);
|
|
330
|
-
} else {
|
|
331
|
-
size += stat.size;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
} catch {
|
|
335
|
-
// Permission or access error
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
stats.breakdown[category] += size;
|
|
339
|
-
return size;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// Calculate sizes by category
|
|
343
|
-
calculateDirSize(join(cortexDir, 'sessions'), 'sessions');
|
|
344
|
-
calculateDirSize(join(cortexDir, 'archive'), 'archives');
|
|
345
|
-
calculateDirSize(join(cortexDir, 'plans'), 'plans');
|
|
346
|
-
calculateDirSize(join(cortexDir, 'auth'), 'auth');
|
|
347
|
-
|
|
348
|
-
// Calculate other files
|
|
349
|
-
try {
|
|
350
|
-
const cortexEntries = readdirSync(cortexDir);
|
|
351
|
-
for (const entry of cortexEntries) {
|
|
352
|
-
const fullPath = join(cortexDir, entry);
|
|
353
|
-
const stat = statSync(fullPath);
|
|
354
|
-
|
|
355
|
-
if (stat.isFile()) {
|
|
356
|
-
stats.breakdown.other += stat.size;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
} catch {
|
|
360
|
-
// Access error
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
stats.totalSize = Object.values(stats.breakdown).reduce((sum, size) => sum + size, 0);
|
|
364
|
-
|
|
365
|
-
return stats;
|
|
366
|
-
}
|