myshell-tools 1.0.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.
Files changed (45) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/LICENSE +21 -0
  3. package/README.md +318 -0
  4. package/data/orchestrator.json +113 -0
  5. package/package.json +49 -0
  6. package/src/auth/recovery.mjs +328 -0
  7. package/src/auth/refresh.mjs +373 -0
  8. package/src/chef.mjs +348 -0
  9. package/src/cli/doctor.mjs +568 -0
  10. package/src/cli/reset.mjs +447 -0
  11. package/src/cli/status.mjs +379 -0
  12. package/src/cli.mjs +429 -0
  13. package/src/commands/doctor.mjs +375 -0
  14. package/src/commands/help.mjs +324 -0
  15. package/src/commands/status.mjs +331 -0
  16. package/src/monitor/health.mjs +486 -0
  17. package/src/monitor/performance.mjs +442 -0
  18. package/src/monitor/report.mjs +535 -0
  19. package/src/orchestrator/classify.mjs +391 -0
  20. package/src/orchestrator/confidence.mjs +151 -0
  21. package/src/orchestrator/handoffs.mjs +231 -0
  22. package/src/orchestrator/review.mjs +222 -0
  23. package/src/providers/balance.mjs +201 -0
  24. package/src/providers/claude.mjs +236 -0
  25. package/src/providers/codex.mjs +255 -0
  26. package/src/providers/detect.mjs +185 -0
  27. package/src/providers/errors.mjs +373 -0
  28. package/src/providers/select.mjs +162 -0
  29. package/src/repl-enhanced.mjs +417 -0
  30. package/src/repl.mjs +321 -0
  31. package/src/state/archive.mjs +366 -0
  32. package/src/state/atomic.mjs +116 -0
  33. package/src/state/cleanup.mjs +440 -0
  34. package/src/state/recovery.mjs +461 -0
  35. package/src/state/session.mjs +147 -0
  36. package/src/ui/errors.mjs +456 -0
  37. package/src/ui/formatter.mjs +327 -0
  38. package/src/ui/icons.mjs +318 -0
  39. package/src/ui/progress.mjs +468 -0
  40. package/templates/prompts/confidence-format.txt +14 -0
  41. package/templates/prompts/ic-with-feedback.txt +41 -0
  42. package/templates/prompts/ic.txt +13 -0
  43. package/templates/prompts/manager-review.txt +40 -0
  44. package/templates/prompts/manager.txt +14 -0
  45. package/templates/prompts/worker.txt +12 -0
@@ -0,0 +1,391 @@
1
+ /**
2
+ * classify.mjs — Simple task classification for hierarchical routing
3
+ * Adapted from archive/dual-brain/hooks/vibe-router.mjs
4
+ */
5
+
6
+ /**
7
+ * Risk keyword patterns for escalation detection
8
+ */
9
+ const RISK_KEYWORDS = [
10
+ {
11
+ level: 'critical',
12
+ regex: /\b(auth|credential|secret|\.env|key[s]?|token[s]?|password|encrypt|certificate)\b/i,
13
+ label: 'security-sensitive'
14
+ },
15
+ {
16
+ level: 'high',
17
+ regex: /\b(login|payment|billing|deploy|migration|ci[-/]?cd|permission|policy|schema|api[-_]?contract)\b/i,
18
+ label: 'high-impact'
19
+ },
20
+ {
21
+ level: 'medium',
22
+ regex: /\b(test|spec|config|integration|shared|util|lib)\b/i,
23
+ label: 'shared/tested code'
24
+ },
25
+ {
26
+ level: 'low',
27
+ regex: /\b(readme|docs?|comment|format|lint|style|typo|changelog|nav|ui|css|color|font|margin|padding)\b/i,
28
+ label: 'docs/UI'
29
+ }
30
+ ];
31
+
32
+ const LEVEL_ORDER = { critical: 3, high: 2, medium: 1, low: 0 };
33
+
34
+ /**
35
+ * Tier detection patterns
36
+ */
37
+ const WORKER_WORDS = /\b(explore|search|find|grep|locate|where\s+is|list\s+files|read[-\s]?only|lookup|scan|check|look|where|what)\b/i;
38
+ const MANAGER_WORDS = /\b(review|plan|design|architect|decide|analyze|audit|security|code[-\s]?review|threat[-\s]?model|complex[-\s]?debug|evaluate|compare|assess)\b/i;
39
+
40
+ /**
41
+ * Classify task risk level based on keywords
42
+ */
43
+ function classifyKeywordRisk(text) {
44
+ let highest = { level: 'low', reason: 'general task' };
45
+
46
+ for (const pattern of RISK_KEYWORDS) {
47
+ const match = text.match(pattern.regex);
48
+ if (match && LEVEL_ORDER[pattern.level] > LEVEL_ORDER[highest.level]) {
49
+ highest = { level: pattern.level, reason: `${pattern.label} (${match[0]})` };
50
+ if (pattern.level === 'critical') return highest;
51
+ }
52
+ }
53
+
54
+ return highest;
55
+ }
56
+
57
+ /**
58
+ * Detect file paths in text and classify their risk
59
+ */
60
+ function extractPaths(text) {
61
+ const pathRegex = /(?:^|\s)([\w./\-_]+\/[\w./\-_]*\.[\w]+)(?:\s|$)/g;
62
+ const paths = [];
63
+ let match;
64
+
65
+ while ((match = pathRegex.exec(text)) !== null) {
66
+ paths.push(match[1]);
67
+ }
68
+
69
+ return paths;
70
+ }
71
+
72
+ function classifyPathRisk(paths) {
73
+ if (paths.length === 0) return { level: 'low', reason: 'no files mentioned' };
74
+
75
+ let highest = { level: 'low', reason: 'general files' };
76
+
77
+ for (const path of paths) {
78
+ const lowerPath = path.toLowerCase();
79
+
80
+ if (lowerPath.includes('.env') ||
81
+ lowerPath.includes('secret') ||
82
+ lowerPath.includes('credential')) {
83
+ return { level: 'critical', reason: `sensitive file (${path})` };
84
+ }
85
+
86
+ if (lowerPath.includes('auth') ||
87
+ lowerPath.includes('security') ||
88
+ lowerPath.includes('payment') ||
89
+ lowerPath.includes('billing')) {
90
+ highest = { level: 'high', reason: `sensitive directory (${path})` };
91
+ } else if (lowerPath.includes('test') ||
92
+ lowerPath.includes('spec') ||
93
+ lowerPath.includes('config')) {
94
+ if (LEVEL_ORDER['medium'] > LEVEL_ORDER[highest.level]) {
95
+ highest = { level: 'medium', reason: `shared/test file (${path})` };
96
+ }
97
+ }
98
+ }
99
+
100
+ return highest;
101
+ }
102
+
103
+ /**
104
+ * Determine the appropriate starting tier for a task
105
+ */
106
+ function classifyTier(text) {
107
+ if (MANAGER_WORDS.test(text)) return 'manager';
108
+ if (WORKER_WORDS.test(text)) return 'worker';
109
+ return 'ic'; // Default to IC tier
110
+ }
111
+
112
+ /**
113
+ * Enhanced multi-signal classification function
114
+ */
115
+ export function classifyTask(userMsg, fileContext) {
116
+ const tier = classifyTier(userMsg);
117
+
118
+ // Extract signals for classification
119
+ const keywords = extractKeywords(userMsg);
120
+ const paths = extractMentionedFiles(userMsg, fileContext);
121
+ const complexity = assessComplexity(userMsg);
122
+
123
+ // Multi-signal risk assessment
124
+ const keywordRisk = classifyKeywordRisk(userMsg);
125
+ const fileRisk = classifyFileRisk(paths);
126
+ const complexityRisk = classifyComplexityRisk(complexity);
127
+
128
+ // Take the highest risk level
129
+ const risks = [keywordRisk, fileRisk, complexityRisk];
130
+ const highestRisk = risks.reduce((max, current) =>
131
+ LEVEL_ORDER[current.level] > LEVEL_ORDER[max.level] ? current : max
132
+ );
133
+
134
+ // Estimate confidence based on multiple signals
135
+ const confidence = estimateTaskConfidence(keywords, complexity, paths, tier);
136
+
137
+ return {
138
+ task: userMsg.trim(),
139
+ tier,
140
+ risk: highestRisk.level,
141
+ reason: highestRisk.reason,
142
+ paths,
143
+ keywords,
144
+ complexity,
145
+ confidence,
146
+ signals: {
147
+ keywordRisk,
148
+ fileRisk,
149
+ complexityRisk
150
+ }
151
+ };
152
+ }
153
+
154
+ /**
155
+ * Legacy single-parameter version for backward compatibility
156
+ */
157
+ export function classifyTaskLegacy(task) {
158
+ return classifyTask(task, null);
159
+ }
160
+
161
+ /**
162
+ * Determine if a task result should escalate based on confidence and risk
163
+ */
164
+ export function shouldEscalate(result, classification) {
165
+ // Always escalate if the model explicitly requests it
166
+ if (result.escalate === true) {
167
+ return true;
168
+ }
169
+
170
+ // Escalate low confidence results
171
+ if (result.confidence !== null && result.confidence < 0.5) {
172
+ return true;
173
+ }
174
+
175
+ // Escalate if task failed with errors
176
+ if (!result.success) {
177
+ return true;
178
+ }
179
+
180
+ // Escalate critical/high risk tasks with medium confidence
181
+ if ((classification.risk === 'critical' || classification.risk === 'high') &&
182
+ result.confidence !== null && result.confidence < 0.7) {
183
+ return true;
184
+ }
185
+
186
+ // Look for uncertainty indicators in the output
187
+ const uncertaintyWords = /\b(not sure|uncertain|don't know|unclear|confused|unsure|might|maybe|possibly)\b/i;
188
+ if (uncertaintyWords.test(result.output)) {
189
+ return true;
190
+ }
191
+
192
+ return false;
193
+ }
194
+
195
+ /**
196
+ * Extract relevant keywords from task text
197
+ */
198
+ function extractKeywords(text) {
199
+ const words = text.toLowerCase().split(/\s+/);
200
+ const relevantKeywords = [];
201
+
202
+ for (const pattern of RISK_KEYWORDS) {
203
+ const matches = text.match(pattern.regex);
204
+ if (matches) {
205
+ relevantKeywords.push(...matches.map(m => m.toLowerCase()));
206
+ }
207
+ }
208
+
209
+ return [...new Set(relevantKeywords)]; // Remove duplicates
210
+ }
211
+
212
+ /**
213
+ * Extract mentioned files, enhanced with context
214
+ */
215
+ function extractMentionedFiles(text, fileContext) {
216
+ const paths = extractPaths(text);
217
+
218
+ // If fileContext is provided, add related files
219
+ if (fileContext && fileContext.currentFiles) {
220
+ // Add files from current git status or file list
221
+ for (const file of fileContext.currentFiles) {
222
+ if (text.toLowerCase().includes(file.toLowerCase())) {
223
+ paths.push(file);
224
+ }
225
+ }
226
+ }
227
+
228
+ return [...new Set(paths)]; // Remove duplicates
229
+ }
230
+
231
+ /**
232
+ * Assess task complexity based on multiple indicators
233
+ */
234
+ function assessComplexity(text) {
235
+ const lower = text.toLowerCase();
236
+ let complexity = 0;
237
+
238
+ // Length-based complexity
239
+ const wordCount = text.split(/\s+/).length;
240
+ if (wordCount > 50) complexity += 2;
241
+ else if (wordCount > 20) complexity += 1;
242
+
243
+ // Multi-action complexity
244
+ const actionWords = ['fix', 'add', 'remove', 'update', 'refactor', 'test', 'deploy'];
245
+ const actionCount = actionWords.filter(action => lower.includes(action)).length;
246
+ complexity += Math.min(actionCount, 3); // Cap at 3
247
+
248
+ // Technical complexity indicators
249
+ const complexPatterns = [
250
+ /\b(integration|migration|refactor|architecture|database|performance|optimization)\b/i,
251
+ /\b(concurrent|async|parallel|distributed|scalability)\b/i,
252
+ /\b(security|encryption|authentication|authorization)\b/i
253
+ ];
254
+
255
+ complexity += complexPatterns.filter(pattern => pattern.test(text)).length;
256
+
257
+ return Math.min(complexity, 10); // Cap at 10
258
+ }
259
+
260
+ /**
261
+ * Classify risk based on task complexity
262
+ */
263
+ function classifyComplexityRisk(complexity) {
264
+ if (complexity >= 7) {
265
+ return { level: 'high', reason: 'high complexity task' };
266
+ } else if (complexity >= 4) {
267
+ return { level: 'medium', reason: 'moderate complexity' };
268
+ } else {
269
+ return { level: 'low', reason: 'simple task' };
270
+ }
271
+ }
272
+
273
+ /**
274
+ * Estimate task confidence based on multiple signals
275
+ */
276
+ function estimateTaskConfidence(keywords, complexity, paths, tier) {
277
+ let confidence = 0.7; // Base confidence
278
+
279
+ // Complexity penalty
280
+ if (complexity > 6) confidence -= 0.3;
281
+ else if (complexity > 3) confidence -= 0.1;
282
+
283
+ // Risk keyword penalty
284
+ const riskKeywords = keywords.filter(keyword =>
285
+ RISK_KEYWORDS.some(pattern =>
286
+ pattern.regex.test(keyword) && pattern.level !== 'low'
287
+ )
288
+ );
289
+ confidence -= riskKeywords.length * 0.1;
290
+
291
+ // Path-based adjustment
292
+ const sensitivePaths = paths.filter(path =>
293
+ path.includes('auth') ||
294
+ path.includes('secret') ||
295
+ path.includes('credential') ||
296
+ path.includes('billing')
297
+ );
298
+ confidence -= sensitivePaths.length * 0.15;
299
+
300
+ // Tier mismatch penalty
301
+ if (tier === 'worker' && complexity > 3) confidence -= 0.2;
302
+ if (tier === 'manager' && complexity < 2) confidence += 0.1;
303
+
304
+ return Math.max(0.1, Math.min(1.0, confidence));
305
+ }
306
+
307
+ /**
308
+ * Enhanced file risk classification
309
+ */
310
+ function classifyFileRisk(paths) {
311
+ if (paths.length === 0) return { level: 'low', reason: 'no files mentioned' };
312
+
313
+ let highest = { level: 'low', reason: 'general files' };
314
+
315
+ for (const path of paths) {
316
+ const lowerPath = path.toLowerCase();
317
+
318
+ // Critical patterns
319
+ if (lowerPath.includes('.env') ||
320
+ lowerPath.includes('secret') ||
321
+ lowerPath.includes('credential') ||
322
+ lowerPath.includes('private') ||
323
+ lowerPath.includes('key')) {
324
+ return { level: 'critical', reason: `sensitive file (${path})` };
325
+ }
326
+
327
+ // High risk patterns
328
+ if (lowerPath.includes('auth') ||
329
+ lowerPath.includes('security') ||
330
+ lowerPath.includes('payment') ||
331
+ lowerPath.includes('billing') ||
332
+ lowerPath.includes('migration') ||
333
+ lowerPath.includes('schema')) {
334
+ highest = { level: 'high', reason: `sensitive directory (${path})` };
335
+ }
336
+ // Medium risk patterns
337
+ else if (lowerPath.includes('test') ||
338
+ lowerPath.includes('spec') ||
339
+ lowerPath.includes('config') ||
340
+ lowerPath.includes('api') ||
341
+ lowerPath.includes('server')) {
342
+ if (LEVEL_ORDER['medium'] > LEVEL_ORDER[highest.level]) {
343
+ highest = { level: 'medium', reason: `shared/infrastructure file (${path})` };
344
+ }
345
+ }
346
+ }
347
+
348
+ return highest;
349
+ }
350
+
351
+ /**
352
+ * Select the best available model for a given tier
353
+ * Now with enhanced provider selection
354
+ */
355
+ export function selectModel(tier, availableModels) {
356
+ const tierModels = availableModels[tier] || [];
357
+
358
+ if (tierModels.length === 0) {
359
+ // Fallback: try to find any available model
360
+ const allModels = [...(availableModels.worker || []),
361
+ ...(availableModels.ic || []),
362
+ ...(availableModels.manager || [])];
363
+ return allModels[0] || null;
364
+ }
365
+
366
+ // Use intelligent provider selection if available
367
+ if (typeof selectProvider !== 'undefined') {
368
+ try {
369
+ const selected = selectProvider(tier, { availableModels });
370
+ if (selected) return selected;
371
+ } catch (error) {
372
+ console.warn('Provider selection failed, falling back to simple selection');
373
+ }
374
+ }
375
+
376
+ // Fallback: prefer models with better tier alignment
377
+ const tierPreferences = {
378
+ worker: ['claude', 'codex'],
379
+ ic: ['codex', 'claude'],
380
+ manager: ['claude', 'codex']
381
+ };
382
+
383
+ const preferences = tierPreferences[tier] || [];
384
+ for (const preferredProvider of preferences) {
385
+ const model = tierModels.find(m => m.provider === preferredProvider);
386
+ if (model) return model;
387
+ }
388
+
389
+ // Final fallback
390
+ return tierModels[0];
391
+ }
@@ -0,0 +1,151 @@
1
+ /**
2
+ * confidence.mjs — Parse model confidence output and handle structured responses
3
+ */
4
+
5
+ /**
6
+ * Extract confidence data from model response
7
+ * Handles both structured JSON at the end and inline patterns
8
+ */
9
+ export function parseConfidence(output) {
10
+ if (!output || typeof output !== 'string') {
11
+ return {
12
+ confidence: null,
13
+ escalate: false,
14
+ reason: 'no output',
15
+ needsReview: false,
16
+ structured: false
17
+ };
18
+ }
19
+
20
+ // Try to extract structured JSON from end of response
21
+ const jsonMatch = output.match(/\{[^{}]*"confidence"[^{}]*\}(?:\s*$)/);
22
+
23
+ if (jsonMatch) {
24
+ try {
25
+ const data = JSON.parse(jsonMatch[0]);
26
+ return {
27
+ confidence: parseFloat(data.confidence) || 0,
28
+ escalate: data.escalate === true || data.escalate === 'true',
29
+ reason: data.reason || 'structured response',
30
+ needsReview: data.needs_review === true || data.needs_review === 'true',
31
+ structured: true,
32
+ rawData: data
33
+ };
34
+ } catch (error) {
35
+ // Fall through to pattern-based parsing
36
+ }
37
+ }
38
+
39
+ // Fallback: estimate confidence from response patterns
40
+ return estimateConfidenceFromText(output);
41
+ }
42
+
43
+ /**
44
+ * Estimate confidence from text patterns when structured output is missing
45
+ */
46
+ function estimateConfidenceFromText(text) {
47
+ const lower = text.toLowerCase();
48
+
49
+ // High confidence indicators
50
+ const highConfidence = [
51
+ 'definitely', 'certain', 'sure', 'confident', 'obviously',
52
+ 'clearly', 'exactly', 'precisely', 'completed successfully'
53
+ ];
54
+
55
+ // Medium confidence indicators
56
+ const mediumConfidence = [
57
+ 'likely', 'probably', 'should work', 'appears to', 'seems like',
58
+ 'generally', 'typically', 'usually'
59
+ ];
60
+
61
+ // Low confidence / uncertainty indicators
62
+ const lowConfidence = [
63
+ 'not sure', 'uncertain', "don't know", 'unclear', 'confused',
64
+ 'unsure', 'might', 'maybe', 'possibly', 'perhaps', 'could be',
65
+ 'need to check', 'requires investigation', 'hard to tell'
66
+ ];
67
+
68
+ // Escalation indicators
69
+ const escalationWords = [
70
+ 'complex', 'complicated', 'difficult', 'needs review',
71
+ 'architecture decision', 'security concern', 'not my area',
72
+ 'beyond my scope', 'manager should', 'escalate'
73
+ ];
74
+
75
+ let confidence = 0.6; // default neutral
76
+ let escalate = false;
77
+ let reason = 'text analysis';
78
+
79
+ // Check for explicit escalation requests
80
+ if (escalationWords.some(word => lower.includes(word))) {
81
+ escalate = true;
82
+ confidence = Math.min(confidence, 0.4);
83
+ reason = 'explicit escalation request';
84
+ }
85
+
86
+ // Adjust based on confidence indicators
87
+ if (lowConfidence.some(word => lower.includes(word))) {
88
+ confidence = Math.min(confidence, 0.3);
89
+ escalate = true;
90
+ reason = 'uncertainty indicators';
91
+ } else if (highConfidence.some(word => lower.includes(word))) {
92
+ confidence = Math.max(confidence, 0.8);
93
+ reason = 'high confidence indicators';
94
+ } else if (mediumConfidence.some(word => lower.includes(word))) {
95
+ confidence = 0.6;
96
+ reason = 'medium confidence indicators';
97
+ }
98
+
99
+ // Check for error patterns
100
+ const errorPatterns = [
101
+ /error/i, /failed/i, /couldn't/i, /unable to/i, /permission denied/i
102
+ ];
103
+
104
+ if (errorPatterns.some(pattern => pattern.test(text))) {
105
+ confidence = 0.2;
106
+ escalate = true;
107
+ reason = 'error detected';
108
+ }
109
+
110
+ return {
111
+ confidence,
112
+ escalate,
113
+ reason,
114
+ needsReview: confidence < 0.5,
115
+ structured: false
116
+ };
117
+ }
118
+
119
+ /**
120
+ * Validate confidence score is within expected range
121
+ */
122
+ export function validateConfidence(confidence) {
123
+ if (confidence === null || confidence === undefined) return null;
124
+
125
+ const parsed = parseFloat(confidence);
126
+ if (isNaN(parsed)) return null;
127
+
128
+ // Clamp to valid range
129
+ return Math.max(0, Math.min(1, parsed));
130
+ }
131
+
132
+ /**
133
+ * Determine if a task result should escalate based on confidence and context
134
+ */
135
+ export function shouldEscalateOnConfidence(confidence, riskLevel, tier) {
136
+ if (confidence === null) return false;
137
+
138
+ // Always escalate very low confidence
139
+ if (confidence < 0.3) return true;
140
+
141
+ // Risk-based thresholds
142
+ const thresholds = {
143
+ critical: 0.8,
144
+ high: 0.7,
145
+ medium: 0.5,
146
+ low: 0.4
147
+ };
148
+
149
+ const threshold = thresholds[riskLevel] || thresholds.medium;
150
+ return confidence < threshold;
151
+ }