dual-brain 0.1.9 → 0.1.10
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/package.json +1 -1
- package/src/detect.mjs +132 -1
package/package.json
CHANGED
package/src/detect.mjs
CHANGED
|
@@ -157,6 +157,129 @@ function buildExplanation({ intent, risk, complexity, fileCount, priorFailures }
|
|
|
157
157
|
return parts.join(' ') + '.';
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
// ─── Reasoning depth classification ───────────────────────────────────────────
|
|
161
|
+
|
|
162
|
+
const ULTRA_UNCERTAINTY = /\b(not sure|maybe|should we|architect|design|trade-?off|approach)\b/i;
|
|
163
|
+
const ULTRA_DEEP_ANALYSIS = /\b(think about|analyze|analyse|evaluate|compare options)\b/i;
|
|
164
|
+
const HIGH_CROSS_CUTTING = /\b(refactor|rename across|update all|migration)\b/i;
|
|
165
|
+
const LOW_SIMPLE = /\b(grep|find|search|list|show|what is|where is)\b/i;
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Classify the reasoning depth needed for a task.
|
|
169
|
+
* Returns { depth: 'low'|'medium'|'high'|'ultra', signals: string[] }
|
|
170
|
+
*/
|
|
171
|
+
function classifyReasoningDepth(prompt, files = [], priorOutcomes = []) {
|
|
172
|
+
const signals = [];
|
|
173
|
+
|
|
174
|
+
// Gather prior failure count from priorOutcomes array
|
|
175
|
+
const failures = priorOutcomes.filter(o => o && (o.failed || o.status === 'failed' || o.outcome === 'failed' || o.success === false)).length;
|
|
176
|
+
|
|
177
|
+
// File-based risk (reuse classifyRisk)
|
|
178
|
+
const { level: fileRisk } = classifyRisk(files);
|
|
179
|
+
|
|
180
|
+
// Keyword risk from prompt (reuse RISK_KEYWORDS)
|
|
181
|
+
let keywordRisk = 'low';
|
|
182
|
+
for (const { level, regex } of RISK_KEYWORDS) {
|
|
183
|
+
if (regex.test(prompt)) { keywordRisk = level; break; }
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const risk = higherRisk(fileRisk, keywordRisk);
|
|
187
|
+
|
|
188
|
+
// Directory spread from files
|
|
189
|
+
const dirs = new Set(files.map(f => {
|
|
190
|
+
const parts = f.replace(/^\//, '').split('/');
|
|
191
|
+
return parts.length > 1 ? parts[0] : '.';
|
|
192
|
+
}));
|
|
193
|
+
const dirCount = dirs.size;
|
|
194
|
+
|
|
195
|
+
// ── Ultra signals ──────────────────────────────────────────────────────────
|
|
196
|
+
const ultraSignals = [];
|
|
197
|
+
|
|
198
|
+
if (ULTRA_UNCERTAINTY.test(prompt)) {
|
|
199
|
+
const match = prompt.match(ULTRA_UNCERTAINTY);
|
|
200
|
+
ultraSignals.push(`prompt contains '${match[0]}'`);
|
|
201
|
+
}
|
|
202
|
+
if (ULTRA_DEEP_ANALYSIS.test(prompt)) {
|
|
203
|
+
const match = prompt.match(ULTRA_DEEP_ANALYSIS);
|
|
204
|
+
ultraSignals.push(`prompt requests deep analysis ('${match[0]}')`);
|
|
205
|
+
}
|
|
206
|
+
if (risk === 'critical') {
|
|
207
|
+
ultraSignals.push('risk classified as critical');
|
|
208
|
+
}
|
|
209
|
+
if (failures >= 2) {
|
|
210
|
+
ultraSignals.push(`${failures} prior failures on similar task`);
|
|
211
|
+
}
|
|
212
|
+
if (fileRisk === 'critical') {
|
|
213
|
+
ultraSignals.push('files include auth/security/billing/migration patterns');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (ultraSignals.length > 0) {
|
|
217
|
+
return { depth: 'ultra', signals: ultraSignals };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ── High signals ───────────────────────────────────────────────────────────
|
|
221
|
+
const highSignals = [];
|
|
222
|
+
|
|
223
|
+
if (risk === 'high') {
|
|
224
|
+
highSignals.push('risk classified as high');
|
|
225
|
+
}
|
|
226
|
+
if (files.length > 5) {
|
|
227
|
+
highSignals.push(`${files.length} files provided`);
|
|
228
|
+
}
|
|
229
|
+
if (failures === 1) {
|
|
230
|
+
highSignals.push('1 prior failure on similar task');
|
|
231
|
+
}
|
|
232
|
+
if (HIGH_CROSS_CUTTING.test(prompt)) {
|
|
233
|
+
const match = prompt.match(HIGH_CROSS_CUTTING);
|
|
234
|
+
highSignals.push(`prompt mentions cross-cutting concern ('${match[0]}')`);
|
|
235
|
+
}
|
|
236
|
+
if (dirCount >= 3) {
|
|
237
|
+
highSignals.push(`files span ${dirCount} directories`);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (highSignals.length > 0) {
|
|
241
|
+
return { depth: 'high', signals: highSignals };
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ── Medium signals ─────────────────────────────────────────────────────────
|
|
245
|
+
const MEDIUM_IMPL = /\b(add|implement|build|create|fix|update)\b/i;
|
|
246
|
+
const mediumSignals = [];
|
|
247
|
+
|
|
248
|
+
if (risk === 'medium') {
|
|
249
|
+
mediumSignals.push('risk classified as medium');
|
|
250
|
+
}
|
|
251
|
+
if (files.length >= 2 && files.length <= 5) {
|
|
252
|
+
mediumSignals.push(`${files.length} files provided`);
|
|
253
|
+
}
|
|
254
|
+
if (MEDIUM_IMPL.test(prompt)) {
|
|
255
|
+
const match = prompt.match(MEDIUM_IMPL);
|
|
256
|
+
mediumSignals.push(`prompt contains implementation keyword ('${match[0]}')`);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (mediumSignals.length > 0) {
|
|
260
|
+
return { depth: 'medium', signals: mediumSignals };
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// ── Low signals ────────────────────────────────────────────────────────────
|
|
264
|
+
const lowSignals = [];
|
|
265
|
+
|
|
266
|
+
if (risk === 'low') {
|
|
267
|
+
lowSignals.push('risk classified as low');
|
|
268
|
+
}
|
|
269
|
+
if (files.length <= 1) {
|
|
270
|
+
lowSignals.push(files.length === 0 ? 'no files provided' : '1 file provided');
|
|
271
|
+
}
|
|
272
|
+
if (LOW_SIMPLE.test(prompt)) {
|
|
273
|
+
const match = prompt.match(LOW_SIMPLE);
|
|
274
|
+
lowSignals.push(`prompt is a simple lookup ('${match[0]}')`);
|
|
275
|
+
}
|
|
276
|
+
if (failures === 0) {
|
|
277
|
+
lowSignals.push('no prior failures');
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return { depth: 'low', signals: lowSignals.length > 0 ? lowSignals : ['no elevated signals detected'] };
|
|
281
|
+
}
|
|
282
|
+
|
|
160
283
|
/** Main detection function. Input: { prompt, files?, priorFailures? } */
|
|
161
284
|
function detectTask(input) {
|
|
162
285
|
const { prompt = '', files = [], priorFailures = 0 } = input;
|
|
@@ -213,6 +336,12 @@ function detectTask(input) {
|
|
|
213
336
|
// 8. Explanation
|
|
214
337
|
const explanation = buildExplanation({ intent, risk, complexity, fileCount, priorFailures });
|
|
215
338
|
|
|
339
|
+
// 9. Reasoning depth
|
|
340
|
+
const priorOutcomes = priorFailures > 0
|
|
341
|
+
? Array.from({ length: priorFailures }, () => ({ failed: true }))
|
|
342
|
+
: [];
|
|
343
|
+
const { depth: reasoningDepth, signals: reasoningSignals } = classifyReasoningDepth(prompt, files, priorOutcomes);
|
|
344
|
+
|
|
216
345
|
return {
|
|
217
346
|
intent,
|
|
218
347
|
risk,
|
|
@@ -225,6 +354,8 @@ function detectTask(input) {
|
|
|
225
354
|
requiresWrite: requiresWrite(intent),
|
|
226
355
|
explanation,
|
|
227
356
|
specialist: specialistResult,
|
|
357
|
+
reasoningDepth,
|
|
358
|
+
reasoningSignals,
|
|
228
359
|
};
|
|
229
360
|
}
|
|
230
361
|
|
|
@@ -342,4 +473,4 @@ if (process.argv[1] && new URL(import.meta.url).pathname === process.argv[1]) {
|
|
|
342
473
|
console.log(JSON.stringify(result, null, 2));
|
|
343
474
|
}
|
|
344
475
|
|
|
345
|
-
export { detectTask, classifyIntent, classifyRisk, estimateComplexity, inferTier, extractPaths, classifySpecialist };
|
|
476
|
+
export { detectTask, classifyIntent, classifyRisk, estimateComplexity, inferTier, extractPaths, classifySpecialist, classifyReasoningDepth };
|