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.
- package/CHANGELOG.md +69 -0
- package/LICENSE +21 -0
- package/README.md +318 -0
- package/data/orchestrator.json +113 -0
- package/package.json +49 -0
- package/src/auth/recovery.mjs +328 -0
- package/src/auth/refresh.mjs +373 -0
- package/src/chef.mjs +348 -0
- package/src/cli/doctor.mjs +568 -0
- package/src/cli/reset.mjs +447 -0
- package/src/cli/status.mjs +379 -0
- package/src/cli.mjs +429 -0
- package/src/commands/doctor.mjs +375 -0
- package/src/commands/help.mjs +324 -0
- package/src/commands/status.mjs +331 -0
- package/src/monitor/health.mjs +486 -0
- package/src/monitor/performance.mjs +442 -0
- package/src/monitor/report.mjs +535 -0
- package/src/orchestrator/classify.mjs +391 -0
- package/src/orchestrator/confidence.mjs +151 -0
- package/src/orchestrator/handoffs.mjs +231 -0
- package/src/orchestrator/review.mjs +222 -0
- package/src/providers/balance.mjs +201 -0
- package/src/providers/claude.mjs +236 -0
- package/src/providers/codex.mjs +255 -0
- package/src/providers/detect.mjs +185 -0
- package/src/providers/errors.mjs +373 -0
- package/src/providers/select.mjs +162 -0
- package/src/repl-enhanced.mjs +417 -0
- package/src/repl.mjs +321 -0
- package/src/state/archive.mjs +366 -0
- package/src/state/atomic.mjs +116 -0
- package/src/state/cleanup.mjs +440 -0
- package/src/state/recovery.mjs +461 -0
- package/src/state/session.mjs +147 -0
- package/src/ui/errors.mjs +456 -0
- package/src/ui/formatter.mjs +327 -0
- package/src/ui/icons.mjs +318 -0
- package/src/ui/progress.mjs +468 -0
- package/templates/prompts/confidence-format.txt +14 -0
- package/templates/prompts/ic-with-feedback.txt +41 -0
- package/templates/prompts/ic.txt +13 -0
- package/templates/prompts/manager-review.txt +40 -0
- package/templates/prompts/manager.txt +14 -0
- package/templates/prompts/worker.txt +12 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* claude.mjs — Claude CLI subprocess wrapper with robust error handling
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { spawnSync } from 'child_process';
|
|
6
|
+
import {
|
|
7
|
+
executeWithRecovery,
|
|
8
|
+
parseCliOutput,
|
|
9
|
+
createFriendlyErrorMessage,
|
|
10
|
+
CliError,
|
|
11
|
+
defaultRetryCallback
|
|
12
|
+
} from './errors.mjs';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Execute a Claude command with the specified model and prompt
|
|
16
|
+
*/
|
|
17
|
+
export async function executeClaude(claudeBin, model, prompt, options = {}) {
|
|
18
|
+
const startTime = Date.now();
|
|
19
|
+
|
|
20
|
+
const args = [
|
|
21
|
+
'-p',
|
|
22
|
+
'--model', model,
|
|
23
|
+
'--output-format', 'stream-json',
|
|
24
|
+
'--verbose'
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
// Add the prompt as the final argument
|
|
28
|
+
args.push(prompt);
|
|
29
|
+
|
|
30
|
+
let proc;
|
|
31
|
+
try {
|
|
32
|
+
proc = await executeWithRecovery(claudeBin, args, {
|
|
33
|
+
provider: 'claude',
|
|
34
|
+
timeoutMs: options.timeoutMs || 120000,
|
|
35
|
+
cwd: options.cwd || process.cwd(),
|
|
36
|
+
onRetry: options.onRetry || defaultRetryCallback
|
|
37
|
+
});
|
|
38
|
+
} catch (error) {
|
|
39
|
+
// Return error result in expected format
|
|
40
|
+
return {
|
|
41
|
+
success: false,
|
|
42
|
+
output: '',
|
|
43
|
+
confidence: null,
|
|
44
|
+
escalate: false,
|
|
45
|
+
reasoning: 'CLI execution failed',
|
|
46
|
+
durationMs: Date.now() - startTime,
|
|
47
|
+
model,
|
|
48
|
+
provider: 'claude',
|
|
49
|
+
exitCode: error.details?.exitCode || -1,
|
|
50
|
+
stderr: error.details?.stderr || error.message,
|
|
51
|
+
error: createFriendlyErrorMessage(error, 'claude')
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const durationMs = Date.now() - startTime;
|
|
56
|
+
|
|
57
|
+
// Parse streaming JSON output
|
|
58
|
+
let output = '';
|
|
59
|
+
let confidence = null;
|
|
60
|
+
let escalate = false;
|
|
61
|
+
let reasoning = '';
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
if (proc.stdout) {
|
|
65
|
+
// Claude streams JSON objects, one per line
|
|
66
|
+
const lines = proc.stdout.trim().split('\n').filter(l => l.trim());
|
|
67
|
+
|
|
68
|
+
for (const line of lines) {
|
|
69
|
+
try {
|
|
70
|
+
const data = JSON.parse(line);
|
|
71
|
+
|
|
72
|
+
// Look for assistant messages with content
|
|
73
|
+
if (data.type === 'assistant' && data.message && data.message.content) {
|
|
74
|
+
for (const content of data.message.content) {
|
|
75
|
+
if (content.type === 'text' && content.text) {
|
|
76
|
+
output += content.text;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
} catch {
|
|
81
|
+
// If not JSON, treat as plain text (fallback)
|
|
82
|
+
output += line + '\n';
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Try to extract confidence and escalation info from output
|
|
88
|
+
const confidenceMatch = output.match(/"confidence":\s*([0-9.]+)/);
|
|
89
|
+
const escalateMatch = output.match(/"escalate":\s*(true|false)/);
|
|
90
|
+
const reasonMatch = output.match(/"reason":\s*"([^"]+)"/);
|
|
91
|
+
|
|
92
|
+
if (confidenceMatch) confidence = parseFloat(confidenceMatch[1]);
|
|
93
|
+
if (escalateMatch) escalate = escalateMatch[1] === 'true';
|
|
94
|
+
if (reasonMatch) reasoning = reasonMatch[1];
|
|
95
|
+
|
|
96
|
+
} catch (err) {
|
|
97
|
+
// Fallback to plain text if JSON parsing fails
|
|
98
|
+
output = proc.stdout || '';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Parse CLI output for better error handling
|
|
102
|
+
const parsedOutput = parseCliOutput(proc.stdout, proc.stderr, proc.status);
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
success: parsedOutput.success,
|
|
106
|
+
output: output.trim(),
|
|
107
|
+
confidence,
|
|
108
|
+
escalate,
|
|
109
|
+
reasoning,
|
|
110
|
+
durationMs,
|
|
111
|
+
model,
|
|
112
|
+
provider: 'claude',
|
|
113
|
+
exitCode: proc.status,
|
|
114
|
+
stderr: proc.stderr || '',
|
|
115
|
+
error: parsedOutput.success ? null : createFriendlyErrorMessage({
|
|
116
|
+
message: parsedOutput.error,
|
|
117
|
+
details: {
|
|
118
|
+
errorType: parsedOutput.errorType,
|
|
119
|
+
suggestions: parsedOutput.suggestions,
|
|
120
|
+
isRecoverable: parsedOutput.isRecoverable
|
|
121
|
+
}
|
|
122
|
+
}, 'claude')
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Build a hierarchical prompt for Claude based on tier and context
|
|
128
|
+
*/
|
|
129
|
+
export function buildClaudePrompt(tier, task, context = {}) {
|
|
130
|
+
let prompt = '';
|
|
131
|
+
|
|
132
|
+
// Enhanced prompts with manager feedback support
|
|
133
|
+
switch (tier) {
|
|
134
|
+
case 'worker':
|
|
135
|
+
prompt = `You are a WORKER in an AI organization hierarchy. Handle this specific, simple task efficiently:
|
|
136
|
+
|
|
137
|
+
TASK: ${task}
|
|
138
|
+
|
|
139
|
+
You are the cheapest, fastest model in the org chart. Focus on:
|
|
140
|
+
- File lookups, grep operations, simple reads
|
|
141
|
+
- Quick information gathering
|
|
142
|
+
- Basic operations that don't require complex reasoning
|
|
143
|
+
|
|
144
|
+
Work efficiently and honestly. If the task is more complex than you can handle confidently, be honest about it.
|
|
145
|
+
|
|
146
|
+
End your response with: {"confidence": 0.0-1.0, "escalate": true|false, "reason": "brief explanation", "needs_review": true|false}`;
|
|
147
|
+
break;
|
|
148
|
+
|
|
149
|
+
case 'ic':
|
|
150
|
+
// Check if this is a retry with manager feedback
|
|
151
|
+
if (context.managerNotes) {
|
|
152
|
+
prompt = `You are an IC (Individual Contributor) in an AI organization. You handle most of the implementation work:
|
|
153
|
+
|
|
154
|
+
TASK: ${task}
|
|
155
|
+
|
|
156
|
+
MANAGER FEEDBACK (from previous attempt):
|
|
157
|
+
═══════════════════════════════════════
|
|
158
|
+
${context.managerNotes}
|
|
159
|
+
═══════════════════════════════════════
|
|
160
|
+
|
|
161
|
+
Your manager has reviewed your previous work and wants you to address the above issues.
|
|
162
|
+
Focus on fixing exactly what they pointed out.
|
|
163
|
+
|
|
164
|
+
${context.attempt ? `This is attempt #${context.attempt}. Apply the manager's feedback carefully.` : ''}
|
|
165
|
+
|
|
166
|
+
You are the primary workhorse - most tasks should be completed at your level. Focus on:
|
|
167
|
+
- Code implementation and editing
|
|
168
|
+
- Refactoring and improvements
|
|
169
|
+
- Running tests and debugging
|
|
170
|
+
- Git operations and file management
|
|
171
|
+
|
|
172
|
+
If you encounter something that needs architectural decisions, security review, or complex debugging, escalate to your manager.
|
|
173
|
+
|
|
174
|
+
End your response with: {"confidence": 0.0-1.0, "escalate": true|false, "reason": "brief explanation", "needs_review": true|false}`;
|
|
175
|
+
} else {
|
|
176
|
+
prompt = `You are an IC (Individual Contributor) in an AI organization. You handle most of the implementation work:
|
|
177
|
+
|
|
178
|
+
TASK: ${task}
|
|
179
|
+
|
|
180
|
+
You are the primary workhorse - most tasks should be completed at your level. Focus on:
|
|
181
|
+
- Code implementation and editing
|
|
182
|
+
- Refactoring and improvements
|
|
183
|
+
- Running tests and debugging
|
|
184
|
+
- Git operations and file management
|
|
185
|
+
|
|
186
|
+
If you encounter something that needs architectural decisions, security review, or complex debugging, escalate to your manager.
|
|
187
|
+
|
|
188
|
+
End your response with: {"confidence": 0.0-1.0, "escalate": true|false, "reason": "brief explanation", "needs_review": true|false}`;
|
|
189
|
+
}
|
|
190
|
+
break;
|
|
191
|
+
|
|
192
|
+
case 'manager':
|
|
193
|
+
// Check if this is a manager review operation
|
|
194
|
+
if (context.operation === 'review') {
|
|
195
|
+
prompt = task; // task already contains the full review prompt
|
|
196
|
+
} else {
|
|
197
|
+
prompt = `You are a MANAGER in an AI organization. Handle high-level decisions and review complex problems:
|
|
198
|
+
|
|
199
|
+
TASK: ${task}
|
|
200
|
+
|
|
201
|
+
You handle:
|
|
202
|
+
- Architecture decisions
|
|
203
|
+
- Security reviews
|
|
204
|
+
- Complex debugging that requires deep reasoning
|
|
205
|
+
- Code reviews and quality decisions
|
|
206
|
+
- Escalated issues from ICs
|
|
207
|
+
|
|
208
|
+
Either solve the problem completely or provide specific guidance for your team to implement.
|
|
209
|
+
|
|
210
|
+
End your response with: {"confidence": 0.0-1.0, "escalate": false, "reason": "solution approach", "needs_review": false}`;
|
|
211
|
+
}
|
|
212
|
+
break;
|
|
213
|
+
|
|
214
|
+
default:
|
|
215
|
+
prompt = `Handle this task: ${task}
|
|
216
|
+
|
|
217
|
+
End your response with: {"confidence": 0.0-1.0, "escalate": true|false, "reason": "brief explanation", "needs_review": true|false}`;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Add context if available (skip for review operations)
|
|
221
|
+
if (context.operation !== 'review') {
|
|
222
|
+
if (context.files && context.files.length > 0) {
|
|
223
|
+
prompt += `\n\nRelevant files:\n${context.files.map(f => `- ${f}`).join('\n')}`;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (context.constraints && context.constraints.length > 0) {
|
|
227
|
+
prompt += `\n\nConstraints:\n${context.constraints.map(c => `- ${c}`).join('\n')}`;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (context.previous && tier === 'manager' && !context.managerNotes) {
|
|
231
|
+
prompt += `\n\nPREVIOUS ATTEMPT (escalated to you):\n${context.previous.output}\n\nConfidence was: ${context.previous.confidence}\nReason for escalation: ${context.previous.reasoning}`;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return prompt;
|
|
236
|
+
}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* codex.mjs — Codex CLI subprocess wrapper with robust error handling
|
|
3
|
+
* Adapted from archive/dual-brain/hooks/gpt-work-dispatcher.mjs
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { spawnSync } from 'child_process';
|
|
7
|
+
import {
|
|
8
|
+
executeWithRecovery,
|
|
9
|
+
parseCliOutput,
|
|
10
|
+
createFriendlyErrorMessage,
|
|
11
|
+
CliError,
|
|
12
|
+
defaultRetryCallback
|
|
13
|
+
} from './errors.mjs';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Execute a Codex command with the specified model and prompt
|
|
17
|
+
*/
|
|
18
|
+
export async function executeCodex(codexBin, model, prompt, options = {}) {
|
|
19
|
+
const startTime = Date.now();
|
|
20
|
+
|
|
21
|
+
const args = [
|
|
22
|
+
'exec', '--json', '--ephemeral',
|
|
23
|
+
'-m', model,
|
|
24
|
+
'-s', 'danger-full-access',
|
|
25
|
+
prompt
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
let proc;
|
|
29
|
+
try {
|
|
30
|
+
proc = await executeWithRecovery(codexBin, args, {
|
|
31
|
+
provider: 'codex',
|
|
32
|
+
timeoutMs: options.timeoutMs || 120000,
|
|
33
|
+
cwd: options.cwd || process.cwd(),
|
|
34
|
+
onRetry: options.onRetry || defaultRetryCallback
|
|
35
|
+
});
|
|
36
|
+
} catch (error) {
|
|
37
|
+
// Return error result in expected format
|
|
38
|
+
return {
|
|
39
|
+
success: false,
|
|
40
|
+
output: '',
|
|
41
|
+
confidence: null,
|
|
42
|
+
escalate: false,
|
|
43
|
+
reasoning: 'CLI execution failed',
|
|
44
|
+
durationMs: Date.now() - startTime,
|
|
45
|
+
model,
|
|
46
|
+
provider: 'codex',
|
|
47
|
+
usage: null,
|
|
48
|
+
errors: [error.message],
|
|
49
|
+
exitCode: error.details?.exitCode || -1,
|
|
50
|
+
stderr: error.details?.stderr || error.message,
|
|
51
|
+
error: createFriendlyErrorMessage(error, 'codex')
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const durationMs = Date.now() - startTime;
|
|
56
|
+
|
|
57
|
+
// Parse JSONL output (each line is a JSON object)
|
|
58
|
+
let output = '';
|
|
59
|
+
let confidence = null;
|
|
60
|
+
let escalate = false;
|
|
61
|
+
let reasoning = '';
|
|
62
|
+
let usage = null;
|
|
63
|
+
const errors = [];
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const messages = (proc.stdout || '')
|
|
67
|
+
.split('\n')
|
|
68
|
+
.filter(l => l.trim())
|
|
69
|
+
.map(l => {
|
|
70
|
+
try {
|
|
71
|
+
return JSON.parse(l);
|
|
72
|
+
} catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
.filter(Boolean);
|
|
77
|
+
|
|
78
|
+
// Extract agent messages (the actual AI response)
|
|
79
|
+
const agentMessages = messages
|
|
80
|
+
.filter(m => m.type === 'item.completed' && m.item?.type === 'agent_message')
|
|
81
|
+
.map(m => m.item.text);
|
|
82
|
+
|
|
83
|
+
output = agentMessages.join('\n\n');
|
|
84
|
+
|
|
85
|
+
// Get usage statistics
|
|
86
|
+
const turnCompleted = messages.find(m => m.type === 'turn.completed');
|
|
87
|
+
if (turnCompleted) {
|
|
88
|
+
usage = turnCompleted.usage;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Collect errors
|
|
92
|
+
messages
|
|
93
|
+
.filter(m => m.type === 'error' || m.type === 'turn.failed')
|
|
94
|
+
.forEach(m => errors.push(m.message || m.error?.message || 'unknown error'));
|
|
95
|
+
|
|
96
|
+
// Try to extract confidence and escalation info from the last agent message
|
|
97
|
+
if (output) {
|
|
98
|
+
const confidenceMatch = output.match(/"confidence":\s*([0-9.]+)/);
|
|
99
|
+
const escalateMatch = output.match(/"escalate":\s*(true|false)/);
|
|
100
|
+
const reasonMatch = output.match(/"reason":\s*"([^"]+)"/);
|
|
101
|
+
|
|
102
|
+
if (confidenceMatch) confidence = parseFloat(confidenceMatch[1]);
|
|
103
|
+
if (escalateMatch) escalate = escalateMatch[1] === 'true';
|
|
104
|
+
if (reasonMatch) reasoning = reasonMatch[1];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
} catch (err) {
|
|
108
|
+
errors.push(`Parse error: ${err.message}`);
|
|
109
|
+
output = proc.stdout || '';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Parse CLI output for better error handling
|
|
113
|
+
const parsedOutput = parseCliOutput(proc.stdout, proc.stderr, proc.status);
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
success: parsedOutput.success && errors.length === 0,
|
|
117
|
+
output: output.trim(),
|
|
118
|
+
confidence,
|
|
119
|
+
escalate,
|
|
120
|
+
reasoning,
|
|
121
|
+
durationMs,
|
|
122
|
+
model,
|
|
123
|
+
provider: 'codex',
|
|
124
|
+
usage,
|
|
125
|
+
errors,
|
|
126
|
+
exitCode: proc.status,
|
|
127
|
+
stderr: proc.stderr || '',
|
|
128
|
+
error: (parsedOutput.success && errors.length === 0) ? null :
|
|
129
|
+
createFriendlyErrorMessage({
|
|
130
|
+
message: errors.length > 0 ? errors.join('; ') : parsedOutput.error,
|
|
131
|
+
details: {
|
|
132
|
+
errorType: parsedOutput.errorType,
|
|
133
|
+
suggestions: parsedOutput.suggestions,
|
|
134
|
+
isRecoverable: parsedOutput.isRecoverable
|
|
135
|
+
}
|
|
136
|
+
}, 'codex')
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Build a hierarchical prompt for Codex based on tier and context
|
|
142
|
+
*/
|
|
143
|
+
export function buildCodexPrompt(tier, task, context = {}) {
|
|
144
|
+
let prompt = '';
|
|
145
|
+
|
|
146
|
+
// Enhanced prompts with manager feedback support
|
|
147
|
+
switch (tier) {
|
|
148
|
+
case 'worker':
|
|
149
|
+
prompt = `You are a WORKER in an AI organization hierarchy. Handle this specific, simple task efficiently:
|
|
150
|
+
|
|
151
|
+
TASK: ${task}
|
|
152
|
+
|
|
153
|
+
You are the cheapest, fastest model in the org chart. Focus on:
|
|
154
|
+
- File lookups, grep operations, simple reads
|
|
155
|
+
- Quick information gathering
|
|
156
|
+
- Basic operations that don't require complex reasoning
|
|
157
|
+
|
|
158
|
+
Work efficiently and honestly. If the task is more complex than you can handle confidently, be honest about it.
|
|
159
|
+
|
|
160
|
+
When complete, output a summary and end with: {"confidence": 0.0-1.0, "escalate": true|false, "reason": "brief explanation", "needs_review": true|false}`;
|
|
161
|
+
break;
|
|
162
|
+
|
|
163
|
+
case 'ic':
|
|
164
|
+
// Check if this is a retry with manager feedback
|
|
165
|
+
if (context.managerNotes) {
|
|
166
|
+
prompt = `You are an IC (Individual Contributor) in an AI organization. You handle most implementation work:
|
|
167
|
+
|
|
168
|
+
TASK: ${task}
|
|
169
|
+
|
|
170
|
+
MANAGER FEEDBACK (from previous attempt):
|
|
171
|
+
═══════════════════════════════════════
|
|
172
|
+
${context.managerNotes}
|
|
173
|
+
═══════════════════════════════════════
|
|
174
|
+
|
|
175
|
+
Your manager has reviewed your previous work and wants you to address the above issues.
|
|
176
|
+
Focus on fixing exactly what they pointed out.
|
|
177
|
+
|
|
178
|
+
${context.attempt ? `This is attempt #${context.attempt}. Apply the manager's feedback carefully.` : ''}
|
|
179
|
+
|
|
180
|
+
Own this task completely. Edit files directly. Run tests to verify your changes.
|
|
181
|
+
|
|
182
|
+
When complete, output:
|
|
183
|
+
1. What you changed (files and behavior)
|
|
184
|
+
2. How you addressed the manager's feedback
|
|
185
|
+
3. Tests run and results (if applicable)
|
|
186
|
+
4. Remaining risks or edge cases
|
|
187
|
+
5. End with: {"confidence": 0.0-1.0, "escalate": true|false, "reason": "brief explanation", "needs_review": true|false}`;
|
|
188
|
+
} else {
|
|
189
|
+
prompt = `You are an IC (Individual Contributor) in an AI organization. You handle most implementation work:
|
|
190
|
+
|
|
191
|
+
TASK: ${task}
|
|
192
|
+
|
|
193
|
+
You are the primary workhorse - most tasks should be completed at your level. Focus on:
|
|
194
|
+
- Code implementation and editing
|
|
195
|
+
- Refactoring and improvements
|
|
196
|
+
- Running tests and debugging
|
|
197
|
+
- Git operations and file management
|
|
198
|
+
|
|
199
|
+
Own this task completely. Edit files directly. Run tests to verify your changes.
|
|
200
|
+
|
|
201
|
+
If you encounter something requiring architectural decisions, security review, or complex debugging, escalate to your manager.
|
|
202
|
+
|
|
203
|
+
When complete, output:
|
|
204
|
+
1. What you changed (files and behavior)
|
|
205
|
+
2. Tests run and results (if applicable)
|
|
206
|
+
3. Remaining risks or edge cases
|
|
207
|
+
4. End with: {"confidence": 0.0-1.0, "escalate": true|false, "reason": "brief explanation", "needs_review": true|false}`;
|
|
208
|
+
}
|
|
209
|
+
break;
|
|
210
|
+
|
|
211
|
+
case 'manager':
|
|
212
|
+
// Check if this is a manager review operation
|
|
213
|
+
if (context.operation === 'review') {
|
|
214
|
+
prompt = task; // task already contains the full review prompt
|
|
215
|
+
} else {
|
|
216
|
+
prompt = `You are a MANAGER in an AI organization. Handle high-level decisions and review complex problems:
|
|
217
|
+
|
|
218
|
+
TASK: ${task}
|
|
219
|
+
|
|
220
|
+
You handle:
|
|
221
|
+
- Architecture decisions
|
|
222
|
+
- Security reviews
|
|
223
|
+
- Complex debugging that requires deep reasoning
|
|
224
|
+
- Code reviews and quality decisions
|
|
225
|
+
- Escalated issues from ICs
|
|
226
|
+
|
|
227
|
+
Either solve the problem completely or provide specific guidance for your team to implement.
|
|
228
|
+
|
|
229
|
+
When complete, output your decision/solution and end with: {"confidence": 0.0-1.0, "escalate": false, "reason": "solution approach", "needs_review": false}`;
|
|
230
|
+
}
|
|
231
|
+
break;
|
|
232
|
+
|
|
233
|
+
default:
|
|
234
|
+
prompt = `Handle this task: ${task}
|
|
235
|
+
|
|
236
|
+
End with: {"confidence": 0.0-1.0, "escalate": true|false, "reason": "brief explanation", "needs_review": true|false}`;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Add context if available (skip for review operations)
|
|
240
|
+
if (context.operation !== 'review') {
|
|
241
|
+
if (context.files && context.files.length > 0) {
|
|
242
|
+
prompt += `\n\nRelevant files:\n${context.files.map(f => `- ${f}`).join('\n')}`;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (context.constraints && context.constraints.length > 0) {
|
|
246
|
+
prompt += `\n\nConstraints:\n${context.constraints.map(c => `- ${c}`).join('\n')}`;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (context.previous && tier === 'manager' && !context.managerNotes) {
|
|
250
|
+
prompt += `\n\nPREVIOUS ATTEMPT (escalated to you):\nOutput: ${context.previous.output}\nConfidence: ${context.previous.confidence}\nEscalation reason: ${context.previous.reasoning}`;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return prompt;
|
|
255
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* detect.mjs — CLI detection and auth status checking for Claude and Codex
|
|
3
|
+
* Adapted from archive/dual-brain/install.mjs
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { spawnSync } from 'child_process';
|
|
7
|
+
import { existsSync, readFileSync } from 'fs';
|
|
8
|
+
import { join, resolve } from 'path';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Run a command and capture output safely
|
|
12
|
+
*/
|
|
13
|
+
function run(cmd, args = [], options = {}) {
|
|
14
|
+
try {
|
|
15
|
+
return spawnSync(cmd, args, {
|
|
16
|
+
encoding: 'utf8',
|
|
17
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
18
|
+
timeout: 10000,
|
|
19
|
+
...options
|
|
20
|
+
});
|
|
21
|
+
} catch (err) {
|
|
22
|
+
return { status: -1, stdout: '', stderr: err.message };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Detect Claude CLI installation and authentication status
|
|
28
|
+
*/
|
|
29
|
+
export function detectClaude() {
|
|
30
|
+
const result = {
|
|
31
|
+
installed: false,
|
|
32
|
+
version: null,
|
|
33
|
+
authed: false,
|
|
34
|
+
models: [],
|
|
35
|
+
bin: 'claude'
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Try to get version
|
|
39
|
+
const ver = run('claude', ['--version']);
|
|
40
|
+
if (ver.status === 0 && ver.stdout.trim()) {
|
|
41
|
+
result.installed = true;
|
|
42
|
+
result.version = ver.stdout.trim().split('\n')[0];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Fallback: check if claude exists in PATH
|
|
46
|
+
if (!result.installed) {
|
|
47
|
+
const which = run('which', ['claude']);
|
|
48
|
+
if (which.status === 0 && which.stdout.trim()) {
|
|
49
|
+
result.installed = true;
|
|
50
|
+
result.bin = which.stdout.trim();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Check authentication via credential files
|
|
55
|
+
const credPaths = [
|
|
56
|
+
join(process.env.HOME || '', '.claude', '.credentials.json'),
|
|
57
|
+
join(process.env.HOME || '', '.claude', 'credentials.json'),
|
|
58
|
+
resolve(process.cwd(), '.replit-tools', '.claude-persistent', '.credentials.json'),
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
for (const p of credPaths) {
|
|
62
|
+
try {
|
|
63
|
+
const cred = JSON.parse(readFileSync(p, 'utf8'));
|
|
64
|
+
if (cred.claudeAiOauth || cred.apiKey || cred.oauth_token) {
|
|
65
|
+
result.authed = true;
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
} catch {}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Fallback: check auth status command
|
|
72
|
+
if (!result.authed && result.installed) {
|
|
73
|
+
const auth = run('claude', ['auth', 'status']);
|
|
74
|
+
const out = ((auth.stdout || '') + (auth.stderr || '')).toLowerCase();
|
|
75
|
+
if (out.includes('logged in') || out.includes('authenticated') || out.includes('valid')) {
|
|
76
|
+
result.authed = true;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// If installed and authed, assume standard models are available
|
|
81
|
+
if (result.installed && result.authed) {
|
|
82
|
+
result.models = ['opus', 'sonnet', 'haiku'];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Detect Codex CLI installation and authentication status
|
|
90
|
+
*/
|
|
91
|
+
export function detectCodex() {
|
|
92
|
+
const result = {
|
|
93
|
+
installed: false,
|
|
94
|
+
version: null,
|
|
95
|
+
authed: false,
|
|
96
|
+
path: null,
|
|
97
|
+
models: []
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// Try which first
|
|
101
|
+
const which = run('which', ['codex']);
|
|
102
|
+
if (which.status === 0 && which.stdout.trim()) {
|
|
103
|
+
result.path = which.stdout.trim();
|
|
104
|
+
result.installed = true;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Try common fallback locations
|
|
108
|
+
if (!result.installed) {
|
|
109
|
+
const home = process.env.HOME || '';
|
|
110
|
+
const fallbacks = [
|
|
111
|
+
join(home, '.local', 'bin', 'codex'),
|
|
112
|
+
join(home, 'bin', 'codex'),
|
|
113
|
+
'/usr/local/bin/codex',
|
|
114
|
+
];
|
|
115
|
+
for (const p of fallbacks) {
|
|
116
|
+
if (existsSync(p)) {
|
|
117
|
+
result.path = p;
|
|
118
|
+
result.installed = true;
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (result.installed && result.path) {
|
|
125
|
+
// Get version
|
|
126
|
+
const ver = run(result.path, ['--version']);
|
|
127
|
+
if (ver.status === 0) {
|
|
128
|
+
result.version = ver.stdout.trim().split('\n')[0];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Check login status
|
|
132
|
+
const login = run(result.path, ['login', 'status']);
|
|
133
|
+
const out = ((login.stdout || '') + (login.stderr || '')).toLowerCase();
|
|
134
|
+
if (login.status === 0 || out.includes('logged in') || out.includes('authenticated')) {
|
|
135
|
+
result.authed = true;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// If installed and authed, assume standard models
|
|
139
|
+
if (result.authed) {
|
|
140
|
+
result.models = ['gpt-5.5', 'gpt-5.4', 'gpt-4.1-mini'];
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Detect all available providers and their capabilities
|
|
149
|
+
*/
|
|
150
|
+
export function detectEnvironment() {
|
|
151
|
+
const claude = detectClaude();
|
|
152
|
+
const codex = detectCodex();
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
claude,
|
|
156
|
+
codex,
|
|
157
|
+
hasProviders: (claude.installed && claude.authed) || (codex.installed && codex.authed),
|
|
158
|
+
workspace: resolve(process.cwd())
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Get available models organized by tier
|
|
164
|
+
*/
|
|
165
|
+
export function getAvailableModels(env) {
|
|
166
|
+
const models = {
|
|
167
|
+
worker: [],
|
|
168
|
+
ic: [],
|
|
169
|
+
manager: []
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
if (env.claude.installed && env.claude.authed) {
|
|
173
|
+
models.worker.push({ provider: 'claude', model: 'haiku', bin: env.claude.bin });
|
|
174
|
+
models.ic.push({ provider: 'claude', model: 'sonnet', bin: env.claude.bin });
|
|
175
|
+
models.manager.push({ provider: 'claude', model: 'opus', bin: env.claude.bin });
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (env.codex.installed && env.codex.authed) {
|
|
179
|
+
models.worker.push({ provider: 'codex', model: 'gpt-4.1-mini', bin: env.codex.path });
|
|
180
|
+
models.ic.push({ provider: 'codex', model: 'gpt-5.4', bin: env.codex.path });
|
|
181
|
+
models.manager.push({ provider: 'codex', model: 'gpt-5.5', bin: env.codex.path });
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return models;
|
|
185
|
+
}
|