@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.
@@ -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
- return 'trivial';
573
+ inferred = 'trivial';
402
574
  }
403
575
 
404
- return 'simple';
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 (reusedState.routeSummary?.helperHint && !reusedState.routeSummary?.nextActionCommand) {
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
- const routeSummary = buildRouteSummary({
1622
- activeSkills: selected,
1623
- routingContext,
1624
- contextRecommendation,
1625
- verificationRecommendation,
1626
- nextAction,
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
- if (routeSummary.helperHint && !routeSummary.nextActionCommand) {
1673
- process.stdout.write(`[ukit-skill-router] Helper: ${routeSummary.helperHint}\n`);
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
  });