@ngockhoale/ukit 1.4.1 → 1.4.3
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 +26 -0
- package/package.json +1 -1
- package/src/core/runtimeConfig.js +65 -1
- package/src/index/taskRouting.js +547 -5
- package/templates/.claude/hooks/reinject-context.sh +2 -0
- package/templates/.claude/hooks/session-start.md +2 -0
- package/templates/.claude/hooks/skill-router.sh +938 -15
- package/templates/.claude/ukit/index/route-task.mjs +744 -6
- package/templates/.claude/ukit/runtime/reinject-context.mjs +136 -8
- package/templates/.codex/README.md +8 -1
- package/templates/.codex/settings.json +53 -0
- package/templates/AGENTS.md +3 -0
- package/templates/CLAUDE.md +5 -1
- package/templates/ukit/README.md +1 -1
- package/templates/ukit/storage/config.json +61 -2
|
@@ -176,6 +176,26 @@ const { pathToFileURL } = require('url');
|
|
|
176
176
|
};
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
function shouldKeepRouteEntryForIntent(entry, intentMode) {
|
|
180
|
+
if (entry.id === 'next-step' && ['scoped-advice', 'docs-specific'].includes(intentMode)) {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (entry.id === 'update-status' && intentMode === 'docs-specific') {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (entry.id === 'docs-quality' && ['open-ended-status', 'status-update'].includes(intentMode)) {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (entry.id === 'code-review' && intentMode === 'implement-specific') {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
|
|
179
199
|
function deriveSkillPriorityBoost(skillId, routeSignals = {}) {
|
|
180
200
|
const combinedPromptSignals = [
|
|
181
201
|
routeSignals.promptRawText || '',
|
|
@@ -183,6 +203,10 @@ const { pathToFileURL } = require('url');
|
|
|
183
203
|
routeSignals.commandRawText || '',
|
|
184
204
|
routeSignals.commandNormalizedText || '',
|
|
185
205
|
].join('\n');
|
|
206
|
+
const rawPromptSignals = [
|
|
207
|
+
routeSignals.promptRawText || '',
|
|
208
|
+
routeSignals.commandRawText || '',
|
|
209
|
+
].join('\n');
|
|
186
210
|
|
|
187
211
|
if (
|
|
188
212
|
skillId === 'testing-quality'
|
|
@@ -191,6 +215,14 @@ const { pathToFileURL } = require('url');
|
|
|
191
215
|
return 1;
|
|
192
216
|
}
|
|
193
217
|
|
|
218
|
+
if (
|
|
219
|
+
skillId === 'code-review'
|
|
220
|
+
&& /\b(implement|build|create|add|ship|deliver|refactor|integrate|integration|scaffold|feature|update|change|modify|inject|apply)\b/.test(rawPromptSignals)
|
|
221
|
+
&& !/\b(review|audit|diff|pr feedback|code review|kiem tra|soat)\b/.test(rawPromptSignals)
|
|
222
|
+
) {
|
|
223
|
+
return -4;
|
|
224
|
+
}
|
|
225
|
+
|
|
194
226
|
return 0;
|
|
195
227
|
}
|
|
196
228
|
|
|
@@ -215,6 +247,131 @@ const { pathToFileURL } = require('url');
|
|
|
215
247
|
};
|
|
216
248
|
}
|
|
217
249
|
|
|
250
|
+
function deriveIntentMode({ promptText = '', commandText = '', targetFile = null } = {}) {
|
|
251
|
+
const lower = buildRouteSignalText(promptText, commandText);
|
|
252
|
+
const raw = `${promptText || ''}\n${commandText || ''}`.toLowerCase();
|
|
253
|
+
const taskQueueNext = hasTaskQueueNextSignal(lower, raw);
|
|
254
|
+
const docsSpecific = hasDocsSpecificTaskSignal(lower, raw, targetFile, { taskQueueNext });
|
|
255
|
+
const statusUpdate = hasStatusUpdateSignal(lower, raw);
|
|
256
|
+
const openEndedStatus = hasOpenEndedStatusSignal(lower, raw) || taskQueueNext;
|
|
257
|
+
const concreteTask = hasConcreteTaskSignal(lower, raw, targetFile, { taskQueueNext });
|
|
258
|
+
|
|
259
|
+
if (docsSpecific) {
|
|
260
|
+
return 'docs-specific';
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (statusUpdate) {
|
|
264
|
+
return 'status-update';
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (concreteTask && openEndedStatus) {
|
|
268
|
+
return 'scoped-advice';
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (openEndedStatus) {
|
|
272
|
+
return 'open-ended-status';
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (concreteTask) {
|
|
276
|
+
if (/\b(bug|debug|error|crash|broken|failing|stack trace|triage|fix|timeout)\b/.test(lower)) {
|
|
277
|
+
return 'debug-specific';
|
|
278
|
+
}
|
|
279
|
+
if (/\b(review|audit|diff|pr feedback|code review|kiem tra|soat)\b/.test(raw)) {
|
|
280
|
+
return 'review-specific';
|
|
281
|
+
}
|
|
282
|
+
if (/\b(implement|build|create|add|ship|deliver|refactor|integrate|integration|scaffold|feature|update|change|modify|inject|apply)\b/.test(raw)) {
|
|
283
|
+
return 'implement-specific';
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function hasStatusUpdateSignal(lower, raw) {
|
|
291
|
+
return /\b(update|refresh|write|sync|record|capture|summarize|summarise).{0,64}\b(status\.md|project status|current state|next candidates|session state)\b/.test(lower)
|
|
292
|
+
|| /\b(status\.md|project status).{0,64}\b(update|refresh|write|sync|record|capture|summarize|summarise)\b/.test(lower)
|
|
293
|
+
|| /\b(wrap up|handoff|end this session|ending this session|session summary|before final)\b/.test(lower)
|
|
294
|
+
|| /\b(cap nhat|ghi lai|tong ket|chot session|ban giao).{0,64}\b(status|trang thai|viec tiep theo)\b/.test(lower)
|
|
295
|
+
|| /\b(cập nhật|ghi lại|tổng kết|chốt session|bàn giao).{0,64}\b(status|trạng thái|việc tiếp theo)\b/.test(raw);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function hasOpenEndedStatusSignal(lower, raw) {
|
|
299
|
+
return /\b(what next|what is next|what's next|next step|next steps|project status|current status|where are we|continue|continue from last session|roadmap|status\.md|task queue|tasks\.md|next queued task|pick next task|work from tasks)\b/.test(lower)
|
|
300
|
+
|| /\b(lam gi tiep|buoc tiep theo|tiep theo lam gi|lam tiep|dang o dau|trang thai project|tinh trang project|task tiep theo|viec tiep theo trong tasks)\b/.test(lower)
|
|
301
|
+
|| /\b(làm gì tiếp|bước tiếp theo|tiếp theo làm gì|làm tiếp|đang ở đâu|trạng thái project|tình trạng project|task tiếp theo|việc tiếp theo trong tasks)\b/.test(raw);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function hasTaskQueueNextSignal(lower, raw) {
|
|
305
|
+
return /\b(task queue|tasks\.md|next queued task|pick next task|work from tasks|next task from tasks|ready for ai)\b/.test(lower)
|
|
306
|
+
|| /\b(task tiếp theo|việc tiếp theo trong tasks|làm task trong tasks|lấy task tiếp theo)\b/.test(raw);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function hasConcreteTaskSignal(lower, raw, targetFile, { taskQueueNext = false } = {}) {
|
|
310
|
+
if (targetFile && !isStatusFileTarget(targetFile) && !(taskQueueNext && isTasksFileTarget(targetFile))) {
|
|
311
|
+
return true;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return /\b(bug|debug|error|crash|broken|failing|stack trace|triage|fix|implement|build|create|add|ship|deliver|refactor|integrate|integration|scaffold|feature|update|change|modify|inject|apply|review|audit|diff|pr feedback|code review|auth|login|api|endpoint|test|spec)\b/.test(lower)
|
|
315
|
+
|| /\b(sửa|fix|lỗi|bug|debug|implement|cài|thêm|review|kiểm tra|soát|đăng nhập)\b/.test(raw);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function hasDocsSpecificTaskSignal(lower, raw, targetFile, { taskQueueNext = false } = {}) {
|
|
319
|
+
if (!targetFile || !isDocsTarget(targetFile)) {
|
|
320
|
+
return /\b(clean tasks|cleanup tasks|prune tasks|dedupe tasks|clear completed tasks|dọn tasks|dọn task)\b/.test(lower)
|
|
321
|
+
|| /\b(dọn tasks|dọn task|dọn danh sách task|xóa task đã xong)\b/.test(raw);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (taskQueueNext && isTasksFileTarget(targetFile) && !/\b(clean|cleanup|prune|dedupe|edit|template|wording|format|structure)\b/.test(lower)) {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const targetName = path.posix.basename(String(targetFile || '').replaceAll('\\', '/')).toLowerCase();
|
|
329
|
+
const explicitStatusUpdate = hasExplicitStatusUpdateSignal(lower, raw);
|
|
330
|
+
|
|
331
|
+
if (!isStatusFileTarget(targetFile)) {
|
|
332
|
+
if (explicitStatusUpdate && !mentionsTargetDoc(lower, targetName)) {
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return mentionsTargetDoc(lower, targetName)
|
|
337
|
+
|| /\b(edit|write|improve|document|docs?|readme|changelog|worklog|memory|code map|template|wording|copy|grammar|format|structure|heading|section|handoff notes?)\b/.test(lower)
|
|
338
|
+
|| /\b(câu chữ|chỉnh|sửa chữ|ngữ pháp|định dạng|cấu trúc|tài liệu|ghi chú bàn giao)\b/.test(raw);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return /\b(template|wording|edit|copy|grammar|format|structure|heading|section|docs?)\b/.test(lower)
|
|
342
|
+
|| /\b(mẫu|câu chữ|chỉnh|sửa chữ|ngữ pháp|định dạng|cấu trúc|tài liệu)\b/.test(raw);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function hasExplicitStatusUpdateSignal(lower, raw) {
|
|
346
|
+
return /\b(update|refresh|write|sync|record|capture|summarize|summarise).{0,64}\b(status\.md|project status|current state|next candidates|session state)\b/.test(lower)
|
|
347
|
+
|| /\b(status\.md|project status).{0,64}\b(update|refresh|write|sync|record|capture|summarize|summarise)\b/.test(lower)
|
|
348
|
+
|| /\b(cap nhat|ghi lai|tong ket|chot session|ban giao).{0,64}\b(status|trang thai|viec tiep theo)\b/.test(lower)
|
|
349
|
+
|| /\b(cập nhật|ghi lại|tổng kết|chốt session|bàn giao).{0,64}\b(status|trạng thái|việc tiếp theo)\b/.test(raw);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function mentionsTargetDoc(lower, targetName) {
|
|
353
|
+
if (!targetName) {
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const escaped = targetName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
358
|
+
const withoutExt = escaped.replace(/\\\.md$/i, '');
|
|
359
|
+
return new RegExp(`\\b(?:${escaped}|${withoutExt})\\b`, 'i').test(lower);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function isStatusFileTarget(targetFile) {
|
|
363
|
+
return /(?:^|\/)docs\/STATUS\.md$|(?:^|\/)STATUS\.md$/i.test(String(targetFile || ''));
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function isTasksFileTarget(targetFile) {
|
|
367
|
+
return /(?:^|\/)docs\/TASKS\.md$|(?:^|\/)TASKS\.md$/i.test(String(targetFile || ''));
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function isDocsTarget(targetFile) {
|
|
371
|
+
const normalized = String(targetFile || '').replaceAll('\\', '/');
|
|
372
|
+
return /(?:^|\/)docs\/.+\.md$|(?:^|\/)(?:README|CHANGELOG|AGENTS|CLAUDE|STATUS)\.md$/i.test(normalized);
|
|
373
|
+
}
|
|
374
|
+
|
|
218
375
|
function shouldUseIndexedContext({ activeSkills = [], targetFile = null } = {}) {
|
|
219
376
|
if (activeSkills.length === 0) {
|
|
220
377
|
return Boolean(targetFile);
|
|
@@ -312,6 +469,7 @@ const { pathToFileURL } = require('url');
|
|
|
312
469
|
targetFile: routingContext.targetFile || '',
|
|
313
470
|
contextIntent: routingContext.contextIntent || '',
|
|
314
471
|
taskType: routingContext.taskType || '',
|
|
472
|
+
executionMode: routingContext.executionMode || '',
|
|
315
473
|
previousContextFingerprint: previousContextFingerprint || null,
|
|
316
474
|
});
|
|
317
475
|
}
|
|
@@ -325,6 +483,17 @@ const { pathToFileURL } = require('url');
|
|
|
325
483
|
return buildCompactMachineKey('routefp-v3', {
|
|
326
484
|
skills: activeSkills.map((item) => item.id),
|
|
327
485
|
taskType: routingContext?.taskType || null,
|
|
486
|
+
executionMode: routeSummary?.executionMode || null,
|
|
487
|
+
approachRiskLevel: routeSummary?.approachSelector?.riskLevel || null,
|
|
488
|
+
approachContextPolicy: routeSummary?.approachSelector?.contextPolicy || null,
|
|
489
|
+
approachVerificationPolicy: routeSummary?.approachSelector?.verificationPolicy || null,
|
|
490
|
+
approachCompletionRule: routeSummary?.approachSelector?.completionRule || null,
|
|
491
|
+
continuationRequired: routeSummary?.continuationState?.required || null,
|
|
492
|
+
continuationReasons: unique(routeSummary?.continuationState?.reasons ?? []),
|
|
493
|
+
continuationMilestone: routeSummary?.continuationState?.nextMilestone || null,
|
|
494
|
+
continuationRepeatCount: routeSummary?.continuationState?.repeatCount || null,
|
|
495
|
+
continuationStuckRisk: routeSummary?.continuationState?.stuckRisk || null,
|
|
496
|
+
continuationRescueMode: routeSummary?.continuationState?.rescueMode || null,
|
|
328
497
|
previousContextLine: previousContext?.line || null,
|
|
329
498
|
previousContextIds: unique(previousContext?.selectedIds ?? []),
|
|
330
499
|
policyMode: routeSummary?.policyMode || null,
|
|
@@ -332,6 +501,8 @@ const { pathToFileURL } = require('url');
|
|
|
332
501
|
nextActionType: routeSummary?.nextActionType || null,
|
|
333
502
|
nextActionCommand: routeSummary?.nextActionCommand || null,
|
|
334
503
|
helperHint: routeSummary?.helperHint || null,
|
|
504
|
+
completionRule: routeSummary?.executionContract?.completionRule || null,
|
|
505
|
+
completionMissingEvidence: unique(routeSummary?.completionState?.missingEvidence ?? []),
|
|
335
506
|
primaryCommands: [...new Set((routeSummary?.primaryCommands || []).filter(Boolean))],
|
|
336
507
|
fallbackCommands: [...new Set((routeSummary?.fallbackCommands || []).filter(Boolean))],
|
|
337
508
|
preferredOrder: [...new Set((routeSummary?.preferredOrder || []).filter(Boolean))],
|
|
@@ -397,11 +568,16 @@ const { pathToFileURL } = require('url');
|
|
|
397
568
|
return 'non-trivial';
|
|
398
569
|
}
|
|
399
570
|
|
|
571
|
+
let inferred = 'simple';
|
|
400
572
|
if (/\b(typo|label|text|rename|color|spacing|toggle|comment)\b/.test(lower)) {
|
|
401
|
-
|
|
573
|
+
inferred = 'trivial';
|
|
402
574
|
}
|
|
403
575
|
|
|
404
|
-
|
|
576
|
+
if ((inferred === 'simple' || inferred === 'trivial') && isSharedImpactFile(targetFile)) {
|
|
577
|
+
return 'shared-simple';
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
return inferred;
|
|
405
581
|
}
|
|
406
582
|
|
|
407
583
|
function shellEscape(value) {
|
|
@@ -1129,6 +1305,23 @@ const { pathToFileURL } = require('url');
|
|
|
1129
1305
|
const compactHelperLane = nextAction?.type === 'pull-indexed-context'
|
|
1130
1306
|
&& typeof contextRecommendation?.command === 'string'
|
|
1131
1307
|
&& contextRecommendation.command.trim();
|
|
1308
|
+
const executionMode = routingContext.executionMode || null;
|
|
1309
|
+
const executionScores = routingContext.executionScores || null;
|
|
1310
|
+
const executionCandidates = routingContext.executionCandidates || null;
|
|
1311
|
+
const approachSelector = buildApproachSelectorResult({
|
|
1312
|
+
executionMode,
|
|
1313
|
+
executionScores,
|
|
1314
|
+
executionCandidates,
|
|
1315
|
+
});
|
|
1316
|
+
const executionContract = buildExecutionContract(executionMode);
|
|
1317
|
+
const completionState = buildCompletionState({
|
|
1318
|
+
executionMode,
|
|
1319
|
+
verificationRecommendation,
|
|
1320
|
+
});
|
|
1321
|
+
const continuationState = buildContinuationState({
|
|
1322
|
+
nextActionType: nextAction?.type || null,
|
|
1323
|
+
completionState,
|
|
1324
|
+
});
|
|
1132
1325
|
const helperHint = compactHelperHint(
|
|
1133
1326
|
compactHelperLane
|
|
1134
1327
|
? contextRecommendation?.command
|
|
@@ -1152,6 +1345,13 @@ const { pathToFileURL } = require('url');
|
|
|
1152
1345
|
fallbackCommands,
|
|
1153
1346
|
preferredOrder,
|
|
1154
1347
|
policyMode,
|
|
1348
|
+
executionMode,
|
|
1349
|
+
executionScores,
|
|
1350
|
+
executionCandidates,
|
|
1351
|
+
approachSelector,
|
|
1352
|
+
executionContract,
|
|
1353
|
+
completionState,
|
|
1354
|
+
continuationState,
|
|
1155
1355
|
delegateHint: delegationRecommendation?.hint || null,
|
|
1156
1356
|
nextActionType: nextAction?.type || null,
|
|
1157
1357
|
nextActionCommand,
|
|
@@ -1160,6 +1360,580 @@ const { pathToFileURL } = require('url');
|
|
|
1160
1360
|
};
|
|
1161
1361
|
}
|
|
1162
1362
|
|
|
1363
|
+
function deriveExecutionScores({
|
|
1364
|
+
promptText = '',
|
|
1365
|
+
commandText = '',
|
|
1366
|
+
targetFile = null,
|
|
1367
|
+
intentMode = null,
|
|
1368
|
+
taskType = null,
|
|
1369
|
+
} = {}) {
|
|
1370
|
+
const raw = `${promptText || ''}\n${commandText || ''}`.toLowerCase();
|
|
1371
|
+
const sharedRisk = isSharedImpactFile(targetFile);
|
|
1372
|
+
const directTransformSignal = /\b(change|replace|set|rename|convert|swap)\b/.test(raw) || /\bchange\s+.+\s+to\s+.+\b/.test(raw);
|
|
1373
|
+
const smallFixSignal = /\b(adjust|tweak|patch|fix|modify|update|apply)\b/.test(raw);
|
|
1374
|
+
const buildSignal = /\b(build|create|add|implement|feature)\b/.test(raw);
|
|
1375
|
+
const debugSignal = intentMode === 'debug-specific' || /\b(root cause|debug|triage|flaky|investigate|why)\b/.test(raw);
|
|
1376
|
+
const reviewSignal = intentMode === 'review-specific' || /\b(review|audit|verify|release readiness)\b/.test(raw);
|
|
1377
|
+
const failureSignal = /\b(failing|failed|broken|error|crash|timeout|undefined|exception|eacces|trace)\b/.test(raw);
|
|
1378
|
+
const impactSignal = /\b(map impact|impact|blast radius|all affected|across (?:runtime|templates|helpers|adapters|mirrors)|affected adapters|affected mirrors|inspect impact)\b/.test(raw);
|
|
1379
|
+
const boundedEditSignal = /\b(one[- ]line|small|tiny|single|local|guard|log line|loading message|label)\b/.test(raw);
|
|
1380
|
+
const implementSignal = /\b(implement|apply|update|modify|add|create|ship|deliver|fix)\b/.test(raw)
|
|
1381
|
+
|| /\bchange\s+.+\s+to\s+.+\b/.test(raw);
|
|
1382
|
+
const explicitTarget = Boolean(targetFile);
|
|
1383
|
+
|
|
1384
|
+
let editCertainty = 0;
|
|
1385
|
+
if (directTransformSignal) {
|
|
1386
|
+
editCertainty = 3;
|
|
1387
|
+
} else if (smallFixSignal) {
|
|
1388
|
+
editCertainty = 2;
|
|
1389
|
+
} else if (buildSignal) {
|
|
1390
|
+
editCertainty = 1;
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
let investigationNeed = 0;
|
|
1394
|
+
if (debugSignal) {
|
|
1395
|
+
investigationNeed = 3;
|
|
1396
|
+
} else if (failureSignal) {
|
|
1397
|
+
investigationNeed = 2;
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
let blastRadius = 0;
|
|
1401
|
+
if (sharedRisk) {
|
|
1402
|
+
blastRadius = 3;
|
|
1403
|
+
} else if (taskType === 'non-trivial' || taskType === 'shared-simple') {
|
|
1404
|
+
blastRadius = 2;
|
|
1405
|
+
} else if (!targetFile) {
|
|
1406
|
+
blastRadius = 1;
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
let verificationBurden = 1;
|
|
1410
|
+
if (reviewSignal || sharedRisk) {
|
|
1411
|
+
verificationBurden = 3;
|
|
1412
|
+
} else if (debugSignal || taskType === 'non-trivial') {
|
|
1413
|
+
verificationBurden = 2;
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
let ambiguity = 0;
|
|
1417
|
+
if (!targetFile) {
|
|
1418
|
+
ambiguity = 2;
|
|
1419
|
+
} else if (buildSignal && !directTransformSignal && !smallFixSignal) {
|
|
1420
|
+
ambiguity = 1;
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
return {
|
|
1424
|
+
editCertainty,
|
|
1425
|
+
investigationNeed,
|
|
1426
|
+
blastRadius,
|
|
1427
|
+
verificationBurden,
|
|
1428
|
+
ambiguity,
|
|
1429
|
+
sharedRisk,
|
|
1430
|
+
directTransformSignal,
|
|
1431
|
+
smallFixSignal,
|
|
1432
|
+
buildSignal,
|
|
1433
|
+
debugSignal,
|
|
1434
|
+
reviewSignal,
|
|
1435
|
+
failureSignal,
|
|
1436
|
+
impactSignal,
|
|
1437
|
+
boundedEditSignal,
|
|
1438
|
+
implementSignal,
|
|
1439
|
+
explicitTarget,
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
function deriveExecutionMode({
|
|
1444
|
+
promptText = '',
|
|
1445
|
+
commandText = '',
|
|
1446
|
+
targetFile = null,
|
|
1447
|
+
intentMode = null,
|
|
1448
|
+
executionScores = {},
|
|
1449
|
+
executionCandidates = null,
|
|
1450
|
+
} = {}) {
|
|
1451
|
+
const raw = `${promptText || ''}\n${commandText || ''}`.toLowerCase();
|
|
1452
|
+
const scores = executionScores;
|
|
1453
|
+
const candidates = executionCandidates ?? buildExecutionModeCandidates({
|
|
1454
|
+
promptText,
|
|
1455
|
+
commandText,
|
|
1456
|
+
targetFile,
|
|
1457
|
+
intentMode,
|
|
1458
|
+
executionScores,
|
|
1459
|
+
});
|
|
1460
|
+
const explicitReviewLead = /\b(review this|audit this|review\b.*\brelease readiness|verify\b.*\brelease readiness)\b/.test(raw);
|
|
1461
|
+
const strongImpactLead = scores.sharedRisk
|
|
1462
|
+
&& (scores.impactSignal || /\b(check all affected|map all affected|across all affected)\b/.test(raw));
|
|
1463
|
+
const boundedLocalBuildCandidate = scores.buildSignal && scores.boundedEditSignal && scores.explicitTarget && !scores.sharedRisk;
|
|
1464
|
+
|
|
1465
|
+
if ((intentMode === 'review-specific' || explicitReviewLead) && !scores.implementSignal) {
|
|
1466
|
+
return 'review-release';
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
if (strongImpactLead || (scores.blastRadius >= 3 && (scores.ambiguity >= 2 || scores.investigationNeed >= 2))) {
|
|
1470
|
+
return 'map-impact';
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
if (scores.sharedRisk && scores.failureSignal && scores.investigationNeed >= 2 && !scores.impactSignal) {
|
|
1474
|
+
return 'shared-edit';
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
if (scores.blastRadius >= 3 || scores.sharedRisk) {
|
|
1478
|
+
return 'shared-edit';
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
if (scores.investigationNeed >= 3 || (scores.failureSignal && scores.investigationNeed >= 2)) {
|
|
1482
|
+
return 'find-cause';
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
if (scores.directTransformSignal && !scores.explicitTarget && scores.investigationNeed === 0 && !scores.sharedRisk) {
|
|
1486
|
+
return 'local-build';
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
if (
|
|
1490
|
+
scores.editCertainty >= 3
|
|
1491
|
+
&& scores.ambiguity <= 1
|
|
1492
|
+
&& scores.blastRadius === 0
|
|
1493
|
+
&& scores.verificationBurden <= 1
|
|
1494
|
+
) {
|
|
1495
|
+
return 'tiny-fix';
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
if (boundedLocalBuildCandidate && scores.investigationNeed === 0) {
|
|
1499
|
+
return 'local-fix';
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
if (
|
|
1503
|
+
/\b(build|create|add|implement|feature|summary card)\b/.test(raw)
|
|
1504
|
+
&& scores.investigationNeed === 0
|
|
1505
|
+
&& scores.blastRadius < 3
|
|
1506
|
+
&& !scores.boundedEditSignal
|
|
1507
|
+
) {
|
|
1508
|
+
return 'local-build';
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
if (scores.editCertainty >= 2 && scores.investigationNeed === 0 && scores.blastRadius === 0) {
|
|
1512
|
+
return 'local-fix';
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
return applySafeUpwardBias(candidates, 'local-build');
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
function buildExecutionModeCandidates({
|
|
1519
|
+
promptText = '',
|
|
1520
|
+
commandText = '',
|
|
1521
|
+
targetFile = null,
|
|
1522
|
+
intentMode = null,
|
|
1523
|
+
executionScores = {},
|
|
1524
|
+
} = {}) {
|
|
1525
|
+
const scores = executionScores;
|
|
1526
|
+
const raw = `${promptText || ''}\n${commandText || ''}`.toLowerCase();
|
|
1527
|
+
const explicitReviewLead = (intentMode === 'review-specific' || /\b(review this|audit this|review\b.*\brelease readiness|verify\b.*\brelease readiness)\b/.test(raw))
|
|
1528
|
+
&& !scores.implementSignal;
|
|
1529
|
+
|
|
1530
|
+
const modeScores = {
|
|
1531
|
+
'tiny-fix': (
|
|
1532
|
+
(scores.editCertainty * 4)
|
|
1533
|
+
+ (scores.directTransformSignal ? 4 : 0)
|
|
1534
|
+
+ (scores.explicitTarget ? 1 : 0)
|
|
1535
|
+
- (scores.failureSignal ? 4 : 0)
|
|
1536
|
+
- (scores.blastRadius * 4)
|
|
1537
|
+
- (scores.ambiguity * 2)
|
|
1538
|
+
),
|
|
1539
|
+
'local-fix': (
|
|
1540
|
+
(scores.editCertainty * 3)
|
|
1541
|
+
+ (scores.boundedEditSignal ? 3 : 0)
|
|
1542
|
+
+ (scores.explicitTarget ? 1 : 0)
|
|
1543
|
+
- (scores.failureSignal ? 2 : 0)
|
|
1544
|
+
- (scores.blastRadius * 4)
|
|
1545
|
+
),
|
|
1546
|
+
'local-build': (
|
|
1547
|
+
(scores.buildSignal ? 6 : 0)
|
|
1548
|
+
+ (scores.editCertainty * 1)
|
|
1549
|
+
+ (scores.explicitTarget ? 1 : 0)
|
|
1550
|
+
- (scores.boundedEditSignal ? 3 : 0)
|
|
1551
|
+
- (scores.failureSignal ? 3 : 0)
|
|
1552
|
+
- (scores.blastRadius * 4)
|
|
1553
|
+
),
|
|
1554
|
+
'find-cause': (
|
|
1555
|
+
(scores.investigationNeed * 4)
|
|
1556
|
+
+ (scores.failureSignal ? 3 : 0)
|
|
1557
|
+
+ (scores.debugSignal ? 2 : 0)
|
|
1558
|
+
- (scores.blastRadius >= 3 ? 1 : 0)
|
|
1559
|
+
),
|
|
1560
|
+
'shared-edit': (
|
|
1561
|
+
(scores.sharedRisk ? 8 : 0)
|
|
1562
|
+
+ (scores.editCertainty * 1)
|
|
1563
|
+
+ (scores.buildSignal ? 1 : 0)
|
|
1564
|
+
- (scores.impactSignal ? 2 : 0)
|
|
1565
|
+
),
|
|
1566
|
+
'map-impact': (
|
|
1567
|
+
(scores.sharedRisk ? 7 : 0)
|
|
1568
|
+
+ (scores.impactSignal ? 5 : 0)
|
|
1569
|
+
+ (scores.ambiguity * 2)
|
|
1570
|
+
+ scores.investigationNeed
|
|
1571
|
+
),
|
|
1572
|
+
'review-release': (
|
|
1573
|
+
(explicitReviewLead ? 10 : 0)
|
|
1574
|
+
+ (scores.reviewSignal ? 3 : 0)
|
|
1575
|
+
- (scores.implementSignal ? 5 : 0)
|
|
1576
|
+
),
|
|
1577
|
+
};
|
|
1578
|
+
|
|
1579
|
+
return Object.entries(modeScores)
|
|
1580
|
+
.map(([mode, score]) => ({ mode, score }))
|
|
1581
|
+
.sort((a, b) => b.score - a.score || executionModeRank(a.mode) - executionModeRank(b.mode));
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
function applySafeUpwardBias(candidates = [], fallbackMode = 'local-build') {
|
|
1585
|
+
const [topCandidate, competingCandidate] = candidates;
|
|
1586
|
+
if (!topCandidate) {
|
|
1587
|
+
return fallbackMode;
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
if (!competingCandidate) {
|
|
1591
|
+
return topCandidate.mode;
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
const scoreGap = Number(topCandidate.score ?? 0) - Number(competingCandidate.score ?? 0);
|
|
1595
|
+
const rankGap = executionModeRank(competingCandidate.mode) - executionModeRank(topCandidate.mode);
|
|
1596
|
+
if (scoreGap <= 1 && rankGap === 1) {
|
|
1597
|
+
return competingCandidate.mode;
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
return topCandidate.mode;
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
function executionModeRank(mode = '') {
|
|
1604
|
+
const orderedModes = [
|
|
1605
|
+
'tiny-fix',
|
|
1606
|
+
'local-fix',
|
|
1607
|
+
'local-build',
|
|
1608
|
+
'find-cause',
|
|
1609
|
+
'shared-edit',
|
|
1610
|
+
'map-impact',
|
|
1611
|
+
'review-release',
|
|
1612
|
+
];
|
|
1613
|
+
|
|
1614
|
+
const index = orderedModes.indexOf(mode);
|
|
1615
|
+
return index >= 0 ? index : orderedModes.length;
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
function buildExecutionContract(executionMode = null) {
|
|
1619
|
+
if (!executionMode) {
|
|
1620
|
+
return null;
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
const contracts = {
|
|
1624
|
+
'tiny-fix': {
|
|
1625
|
+
maxReadPasses: 0,
|
|
1626
|
+
maxContextPulls: 0,
|
|
1627
|
+
verificationPolicy: 'minimal-or-targeted',
|
|
1628
|
+
completionRule: 'never-claim-done-without-write',
|
|
1629
|
+
delegationPolicy: 'disallow',
|
|
1630
|
+
completionEvidence: ['write-evidence'],
|
|
1631
|
+
},
|
|
1632
|
+
'local-fix': {
|
|
1633
|
+
maxReadPasses: 1,
|
|
1634
|
+
maxContextPulls: 1,
|
|
1635
|
+
verificationPolicy: 'targeted-if-covered',
|
|
1636
|
+
completionRule: 'require-write',
|
|
1637
|
+
delegationPolicy: 'disallow',
|
|
1638
|
+
completionEvidence: ['write-evidence'],
|
|
1639
|
+
},
|
|
1640
|
+
'local-build': {
|
|
1641
|
+
maxReadPasses: 2,
|
|
1642
|
+
maxContextPulls: 1,
|
|
1643
|
+
verificationPolicy: 'targeted-if-covered',
|
|
1644
|
+
completionRule: 'require-write',
|
|
1645
|
+
delegationPolicy: 'disallow-by-default',
|
|
1646
|
+
completionEvidence: ['write-evidence'],
|
|
1647
|
+
},
|
|
1648
|
+
'find-cause': {
|
|
1649
|
+
maxReadPassesBeforeReassess: 3,
|
|
1650
|
+
verificationPolicy: 'root-cause-then-targeted',
|
|
1651
|
+
completionRule: 'never-claim-fixed-without-write-and-verification',
|
|
1652
|
+
delegationPolicy: 'allow-specialized-debug-lane',
|
|
1653
|
+
completionEvidence: ['write-evidence', 'verification-evidence'],
|
|
1654
|
+
},
|
|
1655
|
+
'shared-edit': {
|
|
1656
|
+
maxReadPasses: 2,
|
|
1657
|
+
maxContextPulls: 2,
|
|
1658
|
+
verificationPolicy: 'targeted-then-widen-on-risk',
|
|
1659
|
+
completionRule: 'require-write-and-verification',
|
|
1660
|
+
delegationPolicy: 'allow-qualified-sidecar',
|
|
1661
|
+
completionEvidence: ['write-evidence', 'verification-evidence'],
|
|
1662
|
+
mirrorConsistencyRequired: true,
|
|
1663
|
+
},
|
|
1664
|
+
'map-impact': {
|
|
1665
|
+
maxReadPasses: 3,
|
|
1666
|
+
maxContextPulls: 3,
|
|
1667
|
+
verificationPolicy: 'impact-first-then-targeted-then-widen-on-risk',
|
|
1668
|
+
completionRule: 'require-impact-evidence-before-edit-claim',
|
|
1669
|
+
delegationPolicy: 'allow-impact-sidecar',
|
|
1670
|
+
completionEvidence: ['impact-evidence', 'write-evidence', 'verification-evidence'],
|
|
1671
|
+
mirrorConsistencyRequired: true,
|
|
1672
|
+
},
|
|
1673
|
+
'review-release': {
|
|
1674
|
+
verificationPolicy: 'evidence-first',
|
|
1675
|
+
completionRule: 'report-findings-not-implementation',
|
|
1676
|
+
delegationPolicy: 'allow-review-sidecar',
|
|
1677
|
+
completionEvidence: ['verification-evidence'],
|
|
1678
|
+
},
|
|
1679
|
+
};
|
|
1680
|
+
|
|
1681
|
+
return contracts[executionMode] ? { ...contracts[executionMode] } : null;
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
function buildApproachSelectorResult({
|
|
1685
|
+
executionMode = null,
|
|
1686
|
+
executionScores = null,
|
|
1687
|
+
executionCandidates = null,
|
|
1688
|
+
} = {}) {
|
|
1689
|
+
if (!executionMode) {
|
|
1690
|
+
return null;
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
const executionContract = buildExecutionContract(executionMode);
|
|
1694
|
+
const policyByMode = {
|
|
1695
|
+
'tiny-fix': {
|
|
1696
|
+
riskLevel: 'minimal',
|
|
1697
|
+
contextPolicy: 'confirm-target-only',
|
|
1698
|
+
},
|
|
1699
|
+
'local-fix': {
|
|
1700
|
+
riskLevel: 'local',
|
|
1701
|
+
contextPolicy: 'bounded-local',
|
|
1702
|
+
},
|
|
1703
|
+
'local-build': {
|
|
1704
|
+
riskLevel: 'local',
|
|
1705
|
+
contextPolicy: 'bounded-with-related-files',
|
|
1706
|
+
},
|
|
1707
|
+
'find-cause': {
|
|
1708
|
+
riskLevel: 'investigation',
|
|
1709
|
+
contextPolicy: 'trace-then-bounded-context',
|
|
1710
|
+
},
|
|
1711
|
+
'shared-edit': {
|
|
1712
|
+
riskLevel: 'shared',
|
|
1713
|
+
contextPolicy: 'bounded-with-related-tests',
|
|
1714
|
+
},
|
|
1715
|
+
'map-impact': {
|
|
1716
|
+
riskLevel: 'wide',
|
|
1717
|
+
contextPolicy: 'impact-map-first',
|
|
1718
|
+
},
|
|
1719
|
+
'review-release': {
|
|
1720
|
+
riskLevel: 'release',
|
|
1721
|
+
contextPolicy: 'evidence-review-only',
|
|
1722
|
+
},
|
|
1723
|
+
};
|
|
1724
|
+
const selectedPolicy = policyByMode[executionMode] ?? {
|
|
1725
|
+
riskLevel: 'local',
|
|
1726
|
+
contextPolicy: 'bounded-local',
|
|
1727
|
+
};
|
|
1728
|
+
|
|
1729
|
+
return {
|
|
1730
|
+
executionMode,
|
|
1731
|
+
executionScores: executionScores ? { ...executionScores } : null,
|
|
1732
|
+
riskLevel: selectedPolicy.riskLevel,
|
|
1733
|
+
contextPolicy: selectedPolicy.contextPolicy,
|
|
1734
|
+
verificationPolicy: executionContract?.verificationPolicy ?? null,
|
|
1735
|
+
completionRule: executionContract?.completionRule ?? null,
|
|
1736
|
+
competingMode: executionCandidates?.[1]?.mode ?? null,
|
|
1737
|
+
competingScoreGap: executionCandidates?.length >= 2
|
|
1738
|
+
? Number(executionCandidates[0]?.score ?? 0) - Number(executionCandidates[1]?.score ?? 0)
|
|
1739
|
+
: null,
|
|
1740
|
+
candidateModes: (executionCandidates ?? []).slice(0, 3).map((entry) => ({
|
|
1741
|
+
mode: entry.mode,
|
|
1742
|
+
score: entry.score,
|
|
1743
|
+
})),
|
|
1744
|
+
};
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
function buildCompletionState({ executionMode = null, verificationRecommendation = null } = {}) {
|
|
1748
|
+
if (!executionMode) {
|
|
1749
|
+
return null;
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
const contract = buildExecutionContract(executionMode);
|
|
1753
|
+
const missingEvidence = [...(contract?.completionEvidence ?? [])];
|
|
1754
|
+
const requiresVerification = missingEvidence.includes('verification-evidence');
|
|
1755
|
+
if (
|
|
1756
|
+
requiresVerification
|
|
1757
|
+
&& verificationRecommendation
|
|
1758
|
+
&& !(verificationRecommendation.commands?.length || verificationRecommendation.fallbackCommands?.length)
|
|
1759
|
+
) {
|
|
1760
|
+
missingEvidence.push('verification-plan');
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
let reason = 'completion evidence is still required';
|
|
1764
|
+
if (['tiny-fix', 'local-fix', 'local-build', 'shared-edit'].includes(executionMode)) {
|
|
1765
|
+
reason = 'implement request has not produced an edit yet';
|
|
1766
|
+
} else if (executionMode === 'review-release') {
|
|
1767
|
+
reason = 'review/release evidence is still required before final claim';
|
|
1768
|
+
} else if (executionMode === 'map-impact') {
|
|
1769
|
+
reason = 'impact evidence is still required before safe completion claim';
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
return {
|
|
1773
|
+
claimAllowed: false,
|
|
1774
|
+
missingEvidence,
|
|
1775
|
+
reason,
|
|
1776
|
+
};
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
function buildContinuationState({
|
|
1780
|
+
nextActionType = null,
|
|
1781
|
+
completionState = null,
|
|
1782
|
+
} = {}) {
|
|
1783
|
+
const reasons = [];
|
|
1784
|
+
const missingEvidence = unique(completionState?.missingEvidence ?? []);
|
|
1785
|
+
|
|
1786
|
+
if (nextActionType === 'pull-indexed-context') {
|
|
1787
|
+
reasons.push('pending-bounded-context');
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
if (nextActionType === 'read-skill-instructions') {
|
|
1791
|
+
reasons.push('pending-skill-read');
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
if (missingEvidence.includes('write-evidence')) {
|
|
1795
|
+
reasons.push('missing-write-evidence');
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
if (missingEvidence.includes('verification-evidence') || missingEvidence.includes('verification-plan')) {
|
|
1799
|
+
reasons.push('missing-verification-evidence');
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
if (missingEvidence.includes('impact-evidence')) {
|
|
1803
|
+
reasons.push('missing-impact-evidence');
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
const nextMilestone = reasons.includes('pending-bounded-context')
|
|
1807
|
+
? 'complete-bounded-context-then-continue'
|
|
1808
|
+
: (
|
|
1809
|
+
reasons.includes('missing-write-evidence')
|
|
1810
|
+
? 'produce-write-evidence'
|
|
1811
|
+
: (
|
|
1812
|
+
reasons.includes('missing-verification-evidence')
|
|
1813
|
+
? 'produce-verification-evidence'
|
|
1814
|
+
: (
|
|
1815
|
+
reasons.includes('missing-impact-evidence')
|
|
1816
|
+
? 'produce-impact-evidence'
|
|
1817
|
+
: null
|
|
1818
|
+
)
|
|
1819
|
+
)
|
|
1820
|
+
);
|
|
1821
|
+
|
|
1822
|
+
return {
|
|
1823
|
+
required: reasons.length > 0,
|
|
1824
|
+
reasons,
|
|
1825
|
+
doneLanguageBlocked: reasons.length > 0,
|
|
1826
|
+
stopAllowedOnlyFor: reasons.length > 0 ? ['real-blocker'] : [],
|
|
1827
|
+
nextMilestone,
|
|
1828
|
+
repeatCount: reasons.length > 0 ? 1 : 0,
|
|
1829
|
+
stuckRisk: null,
|
|
1830
|
+
rescueMode: null,
|
|
1831
|
+
wideningBlocked: false,
|
|
1832
|
+
milestonePriority: reasons.length > 0 ? 'normal' : null,
|
|
1833
|
+
};
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
function advanceContinuationState(continuationState = null, previousContinuationState = null) {
|
|
1837
|
+
if (!continuationState || typeof continuationState !== 'object') {
|
|
1838
|
+
return continuationState;
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
if (!continuationState.required) {
|
|
1842
|
+
return {
|
|
1843
|
+
...continuationState,
|
|
1844
|
+
repeatCount: 0,
|
|
1845
|
+
stuckRisk: null,
|
|
1846
|
+
rescueMode: null,
|
|
1847
|
+
wideningBlocked: false,
|
|
1848
|
+
milestonePriority: null,
|
|
1849
|
+
};
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
const currentReasons = unique(continuationState.reasons ?? []);
|
|
1853
|
+
const previousReasons = unique(previousContinuationState?.reasons ?? []);
|
|
1854
|
+
const sameMilestone = Boolean(
|
|
1855
|
+
previousContinuationState?.required
|
|
1856
|
+
&& continuationState.nextMilestone
|
|
1857
|
+
&& previousContinuationState.nextMilestone === continuationState.nextMilestone
|
|
1858
|
+
&& JSON.stringify(previousReasons) === JSON.stringify(currentReasons)
|
|
1859
|
+
);
|
|
1860
|
+
const repeatCount = sameMilestone
|
|
1861
|
+
? Math.max(1, Number(previousContinuationState?.repeatCount ?? 1) + 1)
|
|
1862
|
+
: 1;
|
|
1863
|
+
const rescueMode = repeatCount >= 2
|
|
1864
|
+
? (
|
|
1865
|
+
currentReasons.includes('pending-bounded-context')
|
|
1866
|
+
? 'finish-current-milestone-before-more-reading'
|
|
1867
|
+
: (
|
|
1868
|
+
currentReasons.includes('missing-write-evidence')
|
|
1869
|
+
? 'finish-write-before-more-analysis'
|
|
1870
|
+
: 'finish-required-milestone-before-widening'
|
|
1871
|
+
)
|
|
1872
|
+
)
|
|
1873
|
+
: null;
|
|
1874
|
+
const stuckRisk = repeatCount >= 3
|
|
1875
|
+
? 'high'
|
|
1876
|
+
: (repeatCount === 2 ? 'elevated' : null);
|
|
1877
|
+
const wideningBlocked = repeatCount >= 2 && Boolean(rescueMode);
|
|
1878
|
+
const nextMilestone = repeatCount >= 2 && currentReasons.includes('missing-write-evidence')
|
|
1879
|
+
? 'finish-write-before-more-analysis'
|
|
1880
|
+
: (
|
|
1881
|
+
repeatCount >= 2 && currentReasons.includes('pending-bounded-context')
|
|
1882
|
+
? 'finish-current-milestone-before-more-reading'
|
|
1883
|
+
: continuationState.nextMilestone
|
|
1884
|
+
);
|
|
1885
|
+
|
|
1886
|
+
return {
|
|
1887
|
+
...continuationState,
|
|
1888
|
+
nextMilestone,
|
|
1889
|
+
repeatCount,
|
|
1890
|
+
stuckRisk,
|
|
1891
|
+
rescueMode,
|
|
1892
|
+
wideningBlocked,
|
|
1893
|
+
milestonePriority: wideningBlocked ? 'execute-current-milestone' : 'normal',
|
|
1894
|
+
};
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
function applyRescueBiasToRouteSummary(routeSummary = null) {
|
|
1898
|
+
if (!routeSummary || typeof routeSummary !== 'object') {
|
|
1899
|
+
return routeSummary;
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
const continuationState = routeSummary.continuationState;
|
|
1903
|
+
if (
|
|
1904
|
+
continuationState?.repeatCount >= 2
|
|
1905
|
+
&& continuationState?.wideningBlocked
|
|
1906
|
+
&& (continuationState.reasons ?? []).includes('missing-write-evidence')
|
|
1907
|
+
) {
|
|
1908
|
+
routeSummary.nextActionType = 'execute-current-milestone';
|
|
1909
|
+
routeSummary.nextActionCommand = null;
|
|
1910
|
+
routeSummary.helperHint = null;
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
return routeSummary;
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
const SHARED_IMPACT_PATTERNS = [
|
|
1917
|
+
/^\.claude\/hooks\//,
|
|
1918
|
+
/^\.claude\/ukit\//,
|
|
1919
|
+
/^\.codex\//,
|
|
1920
|
+
/^src\/index\//,
|
|
1921
|
+
/^src\/core\/(runInstallPipeline|applyPlan|buildPlan|diffPlan|metadata|migrateLegacy|uninstall|runtimeConfig|runtimePaths)\.js$/,
|
|
1922
|
+
/^src\/core\/(output|token|compact)\//,
|
|
1923
|
+
/^templates\/\.claude\/hooks\//,
|
|
1924
|
+
/^templates\/\.claude\/ukit\//,
|
|
1925
|
+
/^manifests\/platform\.full\.yaml$/,
|
|
1926
|
+
/^templates\//,
|
|
1927
|
+
];
|
|
1928
|
+
|
|
1929
|
+
function isSharedImpactFile(filePath) {
|
|
1930
|
+
const normalized = String(filePath ?? '').trim().replace(/\\/g, '/').replace(/^\.\//, '');
|
|
1931
|
+
if (!normalized) {
|
|
1932
|
+
return false;
|
|
1933
|
+
}
|
|
1934
|
+
return SHARED_IMPACT_PATTERNS.some((pattern) => pattern.test(normalized));
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1163
1937
|
function summarizeCompactList(values, limit = 2) {
|
|
1164
1938
|
const normalized = [...new Set((values || []).filter(Boolean))];
|
|
1165
1939
|
return {
|
|
@@ -1268,6 +2042,8 @@ const { pathToFileURL } = require('url');
|
|
|
1268
2042
|
return {
|
|
1269
2043
|
lastExplicitUserPromptText: routingContext.lastExplicitUserPromptText || '',
|
|
1270
2044
|
taskType: routingContext.taskType || null,
|
|
2045
|
+
intentMode: routingContext.intentMode || null,
|
|
2046
|
+
executionMode: routingContext.executionMode || null,
|
|
1271
2047
|
};
|
|
1272
2048
|
}
|
|
1273
2049
|
|
|
@@ -1299,6 +2075,12 @@ const { pathToFileURL } = require('url');
|
|
|
1299
2075
|
fallbackCommands: [...new Set((routeSummary.fallbackCommands || []).filter(Boolean))].slice(0, 3),
|
|
1300
2076
|
preferredOrder: [...new Set((routeSummary.preferredOrder || []).filter(Boolean))].slice(0, 5),
|
|
1301
2077
|
policyMode: routeSummary.policyMode || null,
|
|
2078
|
+
executionMode: routeSummary.executionMode || null,
|
|
2079
|
+
executionScores: routeSummary.executionScores || null,
|
|
2080
|
+
approachSelector: routeSummary.approachSelector || null,
|
|
2081
|
+
executionContract: routeSummary.executionContract || null,
|
|
2082
|
+
completionState: routeSummary.completionState || null,
|
|
2083
|
+
continuationState: routeSummary.continuationState || null,
|
|
1302
2084
|
delegateHint: routeSummary.delegateHint || null,
|
|
1303
2085
|
nextActionType: routeSummary.nextActionType || null,
|
|
1304
2086
|
nextActionCommand: routeSummary.nextActionCommand || null,
|
|
@@ -1306,6 +2088,79 @@ const { pathToFileURL } = require('url');
|
|
|
1306
2088
|
};
|
|
1307
2089
|
}
|
|
1308
2090
|
|
|
2091
|
+
function buildRouteAuditEntry({
|
|
2092
|
+
routingContext = {},
|
|
2093
|
+
routeSummary = {},
|
|
2094
|
+
state = null,
|
|
2095
|
+
} = {}) {
|
|
2096
|
+
const candidateModes = Array.isArray(routeSummary?.approachSelector?.candidateModes)
|
|
2097
|
+
? routeSummary.approachSelector.candidateModes
|
|
2098
|
+
: [];
|
|
2099
|
+
|
|
2100
|
+
return {
|
|
2101
|
+
ts: Date.now(),
|
|
2102
|
+
source: state?.source || 'skill-router',
|
|
2103
|
+
requestKey: state?.requestKey || null,
|
|
2104
|
+
adapter: routingContext.adapter || 'claude',
|
|
2105
|
+
promptFingerprint: stableMachineDigest({
|
|
2106
|
+
prompt: routingContext.lastExplicitUserPromptText || '',
|
|
2107
|
+
adapter: routingContext.adapter || 'claude',
|
|
2108
|
+
}),
|
|
2109
|
+
targetFile: routingContext.targetFile || null,
|
|
2110
|
+
taskType: routingContext.taskType || null,
|
|
2111
|
+
executionMode: routeSummary.executionMode || null,
|
|
2112
|
+
competingMode: routeSummary?.approachSelector?.competingMode || null,
|
|
2113
|
+
competingScoreGap: routeSummary?.approachSelector?.competingScoreGap || null,
|
|
2114
|
+
candidateModes: candidateModes.slice(0, 3),
|
|
2115
|
+
nextActionType: routeSummary.nextActionType || null,
|
|
2116
|
+
nextMilestone: routeSummary?.continuationState?.nextMilestone || null,
|
|
2117
|
+
repeatCount: routeSummary?.continuationState?.repeatCount || null,
|
|
2118
|
+
rescueMode: routeSummary?.continuationState?.rescueMode || null,
|
|
2119
|
+
wideningBlocked: routeSummary?.continuationState?.wideningBlocked || null,
|
|
2120
|
+
};
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
function appendRouteAuditEntry(filePath, entry) {
|
|
2124
|
+
if (!entry || typeof entry !== 'object') {
|
|
2125
|
+
return;
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2128
|
+
let parsed = { entries: [] };
|
|
2129
|
+
if (fs.existsSync(filePath)) {
|
|
2130
|
+
try {
|
|
2131
|
+
parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
2132
|
+
} catch {
|
|
2133
|
+
parsed = { entries: [] };
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
|
|
2137
|
+
const entries = Array.isArray(parsed?.entries) ? parsed.entries : [];
|
|
2138
|
+
const dedupeKey = stableMachineDigest({
|
|
2139
|
+
requestKey: entry.requestKey,
|
|
2140
|
+
executionMode: entry.executionMode,
|
|
2141
|
+
nextActionType: entry.nextActionType,
|
|
2142
|
+
nextMilestone: entry.nextMilestone,
|
|
2143
|
+
repeatCount: entry.repeatCount,
|
|
2144
|
+
rescueMode: entry.rescueMode,
|
|
2145
|
+
});
|
|
2146
|
+
const filtered = entries.filter((item) => {
|
|
2147
|
+
const itemKey = stableMachineDigest({
|
|
2148
|
+
requestKey: item?.requestKey || null,
|
|
2149
|
+
executionMode: item?.executionMode || null,
|
|
2150
|
+
nextActionType: item?.nextActionType || null,
|
|
2151
|
+
nextMilestone: item?.nextMilestone || null,
|
|
2152
|
+
repeatCount: item?.repeatCount || null,
|
|
2153
|
+
rescueMode: item?.rescueMode || null,
|
|
2154
|
+
});
|
|
2155
|
+
return itemKey !== dedupeKey;
|
|
2156
|
+
});
|
|
2157
|
+
|
|
2158
|
+
ensureDir(filePath);
|
|
2159
|
+
fs.writeFileSync(filePath, JSON.stringify({
|
|
2160
|
+
entries: [entry, ...filtered].slice(0, 40),
|
|
2161
|
+
}));
|
|
2162
|
+
}
|
|
2163
|
+
|
|
1309
2164
|
function shouldIncludePreviousContext({ routingContext = {}, useIndexedContext = true } = {}) {
|
|
1310
2165
|
const taskType = String(routingContext?.taskType || '').trim();
|
|
1311
2166
|
const hasExplicitTarget = Boolean(String(routingContext?.targetFile || '').trim());
|
|
@@ -1373,7 +2228,7 @@ const { pathToFileURL } = require('url');
|
|
|
1373
2228
|
}
|
|
1374
2229
|
|
|
1375
2230
|
const nextActionType = String(routeSummary.nextActionType ?? '').trim();
|
|
1376
|
-
if (!nextActionType || nextActionType === 'read-skill-instructions') {
|
|
2231
|
+
if (!nextActionType || nextActionType === 'read-skill-instructions' || nextActionType === 'pull-indexed-context') {
|
|
1377
2232
|
return '';
|
|
1378
2233
|
}
|
|
1379
2234
|
|
|
@@ -1404,6 +2259,7 @@ const { pathToFileURL } = require('url');
|
|
|
1404
2259
|
const projectRoot = process.env.PROJECT_ROOT || process.cwd();
|
|
1405
2260
|
const statePath = process.env.STATE_FILE || path.join(projectRoot, '.claude', 'ukit', 'skill-router-state.json');
|
|
1406
2261
|
const routeCachePath = path.join(projectRoot, '.claude', 'ukit', 'route-cache.json');
|
|
2262
|
+
const routeAuditPath = path.join(projectRoot, '.ukit', 'storage', 'cache', 'route-audit.json');
|
|
1407
2263
|
const cacheUtilsPath = path.join(projectRoot, '.claude', 'ukit', 'index', 'cache-utils.mjs');
|
|
1408
2264
|
const previous = readJson(statePath, {});
|
|
1409
2265
|
const runtimeConfig = loadRuntimeConfig(projectRoot);
|
|
@@ -1426,12 +2282,18 @@ const { pathToFileURL } = require('url');
|
|
|
1426
2282
|
commandNormalizedText: buildRouteSignalText(commandText),
|
|
1427
2283
|
fileText: String(filePath || '').toLowerCase(),
|
|
1428
2284
|
};
|
|
2285
|
+
const intentMode = deriveIntentMode({
|
|
2286
|
+
promptText,
|
|
2287
|
+
commandText,
|
|
2288
|
+
targetFile: filePath,
|
|
2289
|
+
});
|
|
1429
2290
|
|
|
1430
2291
|
const catalog = await loadRouteCatalog(projectRoot);
|
|
1431
2292
|
|
|
1432
2293
|
const active = catalog
|
|
1433
2294
|
.map((entry) => scoreSkillRouteEntry(entry, routeSignals))
|
|
1434
|
-
.filter((entry) => entry.score > 0 && existsSkill(projectRoot, entry.path))
|
|
2295
|
+
.filter((entry) => entry.score > 0 && existsSkill(projectRoot, entry.path))
|
|
2296
|
+
.filter((entry) => shouldKeepRouteEntryForIntent(entry, intentMode));
|
|
1435
2297
|
|
|
1436
2298
|
const now = Date.now();
|
|
1437
2299
|
const debounceMs = 10 * 60 * 1000;
|
|
@@ -1498,6 +2360,28 @@ const { pathToFileURL } = require('url');
|
|
|
1498
2360
|
selectedIds,
|
|
1499
2361
|
buildRouteSignalText,
|
|
1500
2362
|
});
|
|
2363
|
+
const executionScores = deriveExecutionScores({
|
|
2364
|
+
promptText,
|
|
2365
|
+
commandText,
|
|
2366
|
+
targetFile: filePath,
|
|
2367
|
+
intentMode,
|
|
2368
|
+
taskType,
|
|
2369
|
+
});
|
|
2370
|
+
const executionCandidates = buildExecutionModeCandidates({
|
|
2371
|
+
promptText,
|
|
2372
|
+
commandText,
|
|
2373
|
+
targetFile: filePath,
|
|
2374
|
+
intentMode,
|
|
2375
|
+
executionScores,
|
|
2376
|
+
});
|
|
2377
|
+
const executionMode = deriveExecutionMode({
|
|
2378
|
+
promptText,
|
|
2379
|
+
commandText,
|
|
2380
|
+
targetFile: filePath,
|
|
2381
|
+
intentMode,
|
|
2382
|
+
executionScores,
|
|
2383
|
+
executionCandidates,
|
|
2384
|
+
});
|
|
1501
2385
|
const lastExplicitUserPromptText = promptText.trim()
|
|
1502
2386
|
|| previous?.routingContext?.lastExplicitUserPromptText
|
|
1503
2387
|
|| '';
|
|
@@ -1508,6 +2392,10 @@ const { pathToFileURL } = require('url');
|
|
|
1508
2392
|
targetFile: filePath,
|
|
1509
2393
|
contextIntent,
|
|
1510
2394
|
taskType,
|
|
2395
|
+
intentMode,
|
|
2396
|
+
executionScores,
|
|
2397
|
+
executionCandidates,
|
|
2398
|
+
executionMode,
|
|
1511
2399
|
};
|
|
1512
2400
|
const useIndexedContext = shouldUseIndexedContext({
|
|
1513
2401
|
activeSkills: selected,
|
|
@@ -1565,12 +2453,26 @@ const { pathToFileURL } = require('url');
|
|
|
1565
2453
|
&& cachedRouteState?.routeSummary
|
|
1566
2454
|
&& Array.isArray(cachedRouteState?.activeSkills)
|
|
1567
2455
|
) {
|
|
2456
|
+
const rescuedRouteSummary = cachedRouteState.routeSummary
|
|
2457
|
+
? {
|
|
2458
|
+
...cachedRouteState.routeSummary,
|
|
2459
|
+
continuationState: advanceContinuationState(
|
|
2460
|
+
cachedRouteState.routeSummary.continuationState || null,
|
|
2461
|
+
cachedRouteState.routeSummary.continuationState || null,
|
|
2462
|
+
),
|
|
2463
|
+
}
|
|
2464
|
+
: null;
|
|
2465
|
+
if (rescuedRouteSummary) {
|
|
2466
|
+
applyRescueBiasToRouteSummary(rescuedRouteSummary);
|
|
2467
|
+
}
|
|
1568
2468
|
const reusedState = {
|
|
1569
2469
|
...cachedRouteState,
|
|
1570
2470
|
source: 'skill-router',
|
|
1571
2471
|
ts: now,
|
|
1572
2472
|
requestKey,
|
|
2473
|
+
routeSummary: rescuedRouteSummary,
|
|
1573
2474
|
};
|
|
2475
|
+
reusedState.fingerprint = buildRouteStateFingerprint(reusedState);
|
|
1574
2476
|
|
|
1575
2477
|
if (previous?.fingerprint === reusedState.fingerprint && now - previousTs < debounceMs) {
|
|
1576
2478
|
process.exit(0);
|
|
@@ -1578,6 +2480,9 @@ const { pathToFileURL } = require('url');
|
|
|
1578
2480
|
|
|
1579
2481
|
ensureDir(statePath);
|
|
1580
2482
|
fs.writeFileSync(statePath, JSON.stringify(reusedState));
|
|
2483
|
+
appendRouteAuditEntry(routeAuditPath, buildRouteAuditEntry({
|
|
2484
|
+
state: reusedState,
|
|
2485
|
+
}));
|
|
1581
2486
|
|
|
1582
2487
|
process.stdout.write(`[ukit-skill-router] Read skills: ${getActiveSkillPaths(reusedState.activeSkills || []).join(', ')}\n`);
|
|
1583
2488
|
process.stdout.write(`[ukit-skill-router] Route: ${formatDisplayRouteSummary(reusedState.routeSummary, reusedState.routingContext)}\n`);
|
|
@@ -1588,7 +2493,11 @@ const { pathToFileURL } = require('url');
|
|
|
1588
2493
|
if (reusedState.routeSummary?.delegateHint) {
|
|
1589
2494
|
process.stdout.write(`[ukit-skill-router] Delegate: ${reusedState.routeSummary.delegateHint}\n`);
|
|
1590
2495
|
}
|
|
1591
|
-
if (
|
|
2496
|
+
if (
|
|
2497
|
+
reusedState.routeSummary?.helperHint
|
|
2498
|
+
&& !reusedState.routeSummary?.nextActionCommand
|
|
2499
|
+
&& reusedState.routeSummary?.nextActionType !== 'pull-indexed-context'
|
|
2500
|
+
) {
|
|
1592
2501
|
process.stdout.write(`[ukit-skill-router] Helper: ${reusedState.routeSummary.helperHint}\n`);
|
|
1593
2502
|
}
|
|
1594
2503
|
process.exit(0);
|
|
@@ -1618,13 +2527,18 @@ const { pathToFileURL } = require('url');
|
|
|
1618
2527
|
contextRecommendation,
|
|
1619
2528
|
verificationRecommendation,
|
|
1620
2529
|
});
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
2530
|
+
const routeSummary = buildRouteSummary({
|
|
2531
|
+
activeSkills: selected,
|
|
2532
|
+
routingContext,
|
|
2533
|
+
contextRecommendation,
|
|
2534
|
+
verificationRecommendation,
|
|
2535
|
+
nextAction,
|
|
2536
|
+
});
|
|
2537
|
+
routeSummary.continuationState = advanceContinuationState(
|
|
2538
|
+
routeSummary.continuationState,
|
|
2539
|
+
previous?.routeSummary?.continuationState || null,
|
|
2540
|
+
);
|
|
2541
|
+
applyRescueBiasToRouteSummary(routeSummary);
|
|
1628
2542
|
|
|
1629
2543
|
const fingerprint = buildRouteStateFingerprint({
|
|
1630
2544
|
activeSkills: selected,
|
|
@@ -1654,6 +2568,11 @@ const { pathToFileURL } = require('url');
|
|
|
1654
2568
|
routeSummary: compactRouteSummary(routeSummary),
|
|
1655
2569
|
};
|
|
1656
2570
|
fs.writeFileSync(statePath, JSON.stringify(sharedState));
|
|
2571
|
+
appendRouteAuditEntry(routeAuditPath, buildRouteAuditEntry({
|
|
2572
|
+
routingContext,
|
|
2573
|
+
routeSummary,
|
|
2574
|
+
state: sharedState,
|
|
2575
|
+
}));
|
|
1657
2576
|
if (typeof cacheUtils?.writeRecentCacheEntry === 'function') {
|
|
1658
2577
|
await cacheUtils.writeRecentCacheEntry(routeCachePath, compactRouteCacheState(sharedState), {
|
|
1659
2578
|
maxEntries: cacheUtils.DEFAULT_RECENT_CACHE_MAX_ENTRIES,
|
|
@@ -1669,9 +2588,13 @@ const { pathToFileURL } = require('url');
|
|
|
1669
2588
|
if (routeSummary.delegateHint) {
|
|
1670
2589
|
process.stdout.write(`[ukit-skill-router] Delegate: ${routeSummary.delegateHint}\n`);
|
|
1671
2590
|
}
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
2591
|
+
if (
|
|
2592
|
+
routeSummary.helperHint
|
|
2593
|
+
&& !routeSummary.nextActionCommand
|
|
2594
|
+
&& routeSummary.nextActionType !== 'pull-indexed-context'
|
|
2595
|
+
) {
|
|
2596
|
+
process.stdout.write(`[ukit-skill-router] Helper: ${routeSummary.helperHint}\n`);
|
|
2597
|
+
}
|
|
1675
2598
|
})().catch(() => {
|
|
1676
2599
|
process.exit(0);
|
|
1677
2600
|
});
|