@ngockhoale/ukit 1.4.2 → 1.4.4

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 CHANGED
@@ -2,7 +2,24 @@
2
2
 
3
3
  All notable changes to UKit are documented here.
4
4
 
5
- ## Unreleased
5
+ ## 1.4.4 - 2026-05-11
6
+
7
+ ### Fixed
8
+
9
+ - Removed shipped `.gitignore` negations that accidentally unignored installed `.claude/` and `.codex/` files in fresh repos, preventing large post-install `git status` noise.
10
+ - Kept the intended local-only ignores for `AGENTS.md`, `CLAUDE.md`, `docs/STATUS.md`, `docs/TASKS.md`, runtime cache, and adapter state intact.
11
+
12
+ ### Tests
13
+
14
+ - Added install-pipeline regression coverage that initializes a fresh git repo, runs `ukit install`, and asserts managed UKit files stay ignored.
15
+
16
+ ## 1.4.3 - 2026-05-10
17
+
18
+ ### Changed
19
+
20
+ - Tightened execute-first orchestration guidance so one clear safe path runs directly, while real user-choice or risk-confirmation moments still ask.
21
+ - Added compact route-audit coverage and repeated write-lane rescue bias to reduce stop-and-explain loops.
22
+ - Published patch `1.4.3` after fresh full-suite, release-verify, and registry-smoke validation.
6
23
 
7
24
  ## 1.4.2 - 2026-05-10
8
25
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ngockhoale/ukit",
3
- "version": "1.4.2",
3
+ "version": "1.4.4",
4
4
  "description": "Install/update an index-first AI workspace for Claude Code, Antigravity, OpenAI Codex, and OpenCode.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -49,7 +49,7 @@ export function buildDefaultRuntimeConfig(overrides = {}) {
49
49
  const safeOverrides = isPlainObject(overrides) ? overrides : {};
50
50
 
51
51
  return mergeObjects({
52
- version: '1.4.2',
52
+ version: '1.4.4',
53
53
  agent: 'claude-code',
54
54
  autonomy: {
55
55
  level: 'balanced',
@@ -178,7 +178,7 @@ export const ROUTE_CATALOG = [
178
178
  order: 20,
179
179
  contextMode: 'standalone',
180
180
  signals: [
181
- { type: 'prompt', regex: /\b(pdf|pdf form|fill (?:this )?pdf|merge pdf|split pdf|extract tables? from .*pdf|extract text from .*pdf)\b/i, score: 8 },
181
+ { type: 'prompt', regex: /\b(pdf form|fillable pdf|fill (?:this )?pdf|merge pdfs?|split pdfs?|extract tables? from .*pdf|extract text from .*pdf|read (?:this |the |a )?pdf|open (?:this |the |a )?pdf|analy(?:s|z)e (?:this |the |a )?pdf|annotate (?:this |the |a )?pdf|create pdf|convert .*pdf|pdf extraction|pdf parser|pdf parsing)\b/i, score: 8 },
182
182
  { type: 'command', regex: /\b(pdftotext|qpdf|pypdf|pdfplumber|reportlab)\b/i, score: 5 },
183
183
  { type: 'file', regex: /\.pdf$/i, score: 5 },
184
184
  ],
@@ -59,13 +59,20 @@ export async function deriveTaskRoute({
59
59
  intentMode,
60
60
  taskType: inferredTaskType,
61
61
  });
62
+ const executionCandidates = buildExecutionModeCandidates({
63
+ promptText: normalizedPrompt,
64
+ commandText: normalizedCommand,
65
+ targetFile: normalizedTarget,
66
+ intentMode,
67
+ executionScores,
68
+ });
62
69
  const executionMode = deriveExecutionMode({
63
70
  promptText: normalizedPrompt,
64
71
  commandText: normalizedCommand,
65
72
  targetFile: normalizedTarget,
66
73
  intentMode,
67
- taskType: inferredTaskType,
68
74
  executionScores,
75
+ executionCandidates,
69
76
  });
70
77
  const preservedPrompt = normalizedPrompt || String(lastExplicitUserPromptText || '').trim();
71
78
  const degradedWarnings = [];
@@ -144,6 +151,7 @@ export async function deriveTaskRoute({
144
151
  intentMode,
145
152
  autonomyLevel,
146
153
  executionScores,
154
+ executionCandidates,
147
155
  executionMode,
148
156
  },
149
157
  contextRecommendation,
@@ -164,6 +172,7 @@ export async function deriveTaskRoute({
164
172
  intentMode,
165
173
  autonomyLevel,
166
174
  executionScores,
175
+ executionCandidates,
167
176
  executionMode,
168
177
  },
169
178
  approachSelector,
@@ -209,9 +218,11 @@ export function buildRouteSummary({
209
218
  && contextRecommendation.command.trim();
210
219
  const executionMode = routingContext.executionMode ?? null;
211
220
  const executionScores = routingContext.executionScores ?? null;
221
+ const executionCandidates = routingContext.executionCandidates ?? null;
212
222
  const approachSelector = buildApproachSelectorResult({
213
223
  executionMode,
214
224
  executionScores,
225
+ executionCandidates,
215
226
  });
216
227
  const executionContract = buildExecutionContract(executionMode);
217
228
  const completionState = buildCompletionState({
@@ -250,6 +261,7 @@ export function buildRouteSummary({
250
261
  editGuardHint,
251
262
  executionMode,
252
263
  executionScores,
264
+ executionCandidates,
253
265
  approachSelector,
254
266
  executionContract,
255
267
  completionState,
@@ -284,6 +296,12 @@ function deriveExecutionScores({
284
296
  const buildSignal = /\b(build|create|add|implement|feature)\b/.test(raw);
285
297
  const debugSignal = intentMode === 'debug-specific' || /\b(root cause|debug|triage|flaky|investigate|why)\b/.test(raw);
286
298
  const reviewSignal = intentMode === 'review-specific' || /\b(review|audit|verify|release readiness)\b/.test(raw);
299
+ const failureSignal = /\b(failing|failed|broken|error|crash|timeout|undefined|exception|eacces|trace)\b/.test(raw);
300
+ 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);
301
+ const boundedEditSignal = /\b(one[- ]line|small|tiny|single|local|guard|log line|loading message|label)\b/.test(raw);
302
+ const implementSignal = /\b(implement|apply|update|modify|add|create|ship|deliver|fix)\b/.test(raw)
303
+ || /\bchange\s+.+\s+to\s+.+\b/.test(raw);
304
+ const explicitTarget = Boolean(targetFile);
287
305
 
288
306
  let editCertainty = 0;
289
307
  if (directTransformSignal) {
@@ -297,7 +315,7 @@ function deriveExecutionScores({
297
315
  let investigationNeed = 0;
298
316
  if (debugSignal) {
299
317
  investigationNeed = 3;
300
- } else if (/\b(failing|broken|error|crash|timeout)\b/.test(raw)) {
318
+ } else if (failureSignal) {
301
319
  investigationNeed = 2;
302
320
  }
303
321
 
@@ -330,6 +348,17 @@ function deriveExecutionScores({
330
348
  blastRadius,
331
349
  verificationBurden,
332
350
  ambiguity,
351
+ sharedRisk,
352
+ directTransformSignal,
353
+ smallFixSignal,
354
+ buildSignal,
355
+ debugSignal,
356
+ reviewSignal,
357
+ failureSignal,
358
+ impactSignal,
359
+ boundedEditSignal,
360
+ implementSignal,
361
+ explicitTarget,
333
362
  };
334
363
  }
335
364
 
@@ -339,40 +368,64 @@ function deriveExecutionMode({
339
368
  targetFile = null,
340
369
  intentMode = null,
341
370
  executionScores = {},
371
+ executionCandidates = null,
342
372
  } = {}) {
343
373
  const raw = `${promptText ?? ''}\n${commandText ?? ''}`.toLowerCase();
344
374
  const scores = executionScores;
375
+ const candidates = executionCandidates ?? buildExecutionModeCandidates({
376
+ promptText,
377
+ commandText,
378
+ targetFile,
379
+ intentMode,
380
+ executionScores,
381
+ });
345
382
  const explicitReviewLead = /\b(review this|audit this|review\b.*\brelease readiness|verify\b.*\brelease readiness)\b/.test(raw);
383
+ const strongImpactLead = scores.sharedRisk
384
+ && (scores.impactSignal || /\b(check all affected|map all affected|across all affected)\b/.test(raw));
385
+ const boundedLocalBuildCandidate = scores.buildSignal && scores.boundedEditSignal && scores.explicitTarget && !scores.sharedRisk;
346
386
 
347
- if (intentMode === 'review-specific' || explicitReviewLead) {
387
+ if ((intentMode === 'review-specific' || explicitReviewLead) && !scores.implementSignal) {
348
388
  return 'review-release';
349
389
  }
350
390
 
351
- if (scores.blastRadius >= 3 && (scores.ambiguity >= 2 || scores.investigationNeed >= 2)) {
391
+ if (strongImpactLead || (scores.blastRadius >= 3 && (scores.ambiguity >= 2 || scores.investigationNeed >= 2))) {
352
392
  return 'map-impact';
353
393
  }
354
394
 
355
- if (scores.blastRadius >= 3 || isSharedImpactFile(targetFile)) {
395
+ if (scores.sharedRisk && scores.failureSignal && scores.investigationNeed >= 2 && !scores.impactSignal) {
356
396
  return 'shared-edit';
357
397
  }
358
398
 
359
- if (scores.investigationNeed >= 3) {
399
+ if (scores.blastRadius >= 3 || scores.sharedRisk) {
400
+ return 'shared-edit';
401
+ }
402
+
403
+ if (scores.investigationNeed >= 3 || (scores.failureSignal && scores.investigationNeed >= 2)) {
360
404
  return 'find-cause';
361
405
  }
362
406
 
407
+ if (scores.directTransformSignal && !scores.explicitTarget && scores.investigationNeed === 0 && !scores.sharedRisk) {
408
+ return 'local-build';
409
+ }
410
+
363
411
  if (
364
412
  scores.editCertainty >= 3
365
- && scores.ambiguity === 0
413
+ && scores.ambiguity <= 1
366
414
  && scores.blastRadius === 0
367
415
  && scores.verificationBurden <= 1
368
416
  ) {
369
417
  return 'tiny-fix';
370
418
  }
371
419
 
420
+ if (boundedLocalBuildCandidate && scores.investigationNeed === 0) {
421
+ return 'local-fix';
422
+ }
423
+
372
424
  if (
373
425
  /\b(build|create|add|implement|feature|summary card)\b/.test(raw)
374
426
  && scores.investigationNeed === 0
375
427
  && scores.blastRadius < 3
428
+ && !scores.boundedEditSignal
376
429
  ) {
377
430
  return 'local-build';
378
431
  }
@@ -381,7 +434,107 @@ function deriveExecutionMode({
381
434
  return 'local-fix';
382
435
  }
383
436
 
384
- return 'local-build';
437
+ return applySafeUpwardBias(candidates, 'local-build');
438
+ }
439
+
440
+ function buildExecutionModeCandidates({
441
+ promptText = '',
442
+ commandText = '',
443
+ targetFile = null,
444
+ intentMode = null,
445
+ executionScores = {},
446
+ } = {}) {
447
+ const scores = executionScores;
448
+ const raw = `${promptText ?? ''}\n${commandText ?? ''}`.toLowerCase();
449
+ const explicitReviewLead = (intentMode === 'review-specific' || /\b(review this|audit this|review\b.*\brelease readiness|verify\b.*\brelease readiness)\b/.test(raw))
450
+ && !scores.implementSignal;
451
+
452
+ const modeScores = {
453
+ 'tiny-fix': (
454
+ (scores.editCertainty * 4)
455
+ + (scores.directTransformSignal ? 4 : 0)
456
+ + (scores.explicitTarget ? 1 : 0)
457
+ - (scores.failureSignal ? 4 : 0)
458
+ - (scores.blastRadius * 4)
459
+ - (scores.ambiguity * 2)
460
+ ),
461
+ 'local-fix': (
462
+ (scores.editCertainty * 3)
463
+ + (scores.boundedEditSignal ? 3 : 0)
464
+ + (scores.explicitTarget ? 1 : 0)
465
+ - (scores.failureSignal ? 2 : 0)
466
+ - (scores.blastRadius * 4)
467
+ ),
468
+ 'local-build': (
469
+ (scores.buildSignal ? 6 : 0)
470
+ + (scores.editCertainty * 1)
471
+ + (scores.explicitTarget ? 1 : 0)
472
+ - (scores.boundedEditSignal ? 3 : 0)
473
+ - (scores.failureSignal ? 3 : 0)
474
+ - (scores.blastRadius * 4)
475
+ ),
476
+ 'find-cause': (
477
+ (scores.investigationNeed * 4)
478
+ + (scores.failureSignal ? 3 : 0)
479
+ + (scores.debugSignal ? 2 : 0)
480
+ - (scores.blastRadius >= 3 ? 1 : 0)
481
+ ),
482
+ 'shared-edit': (
483
+ (scores.sharedRisk ? 8 : 0)
484
+ + (scores.editCertainty * 1)
485
+ + (scores.buildSignal ? 1 : 0)
486
+ - (scores.impactSignal ? 2 : 0)
487
+ ),
488
+ 'map-impact': (
489
+ (scores.sharedRisk ? 7 : 0)
490
+ + (scores.impactSignal ? 5 : 0)
491
+ + (scores.ambiguity * 2)
492
+ + scores.investigationNeed
493
+ ),
494
+ 'review-release': (
495
+ (explicitReviewLead ? 10 : 0)
496
+ + (scores.reviewSignal ? 3 : 0)
497
+ - (scores.implementSignal ? 5 : 0)
498
+ ),
499
+ };
500
+
501
+ return Object.entries(modeScores)
502
+ .map(([mode, score]) => ({ mode, score }))
503
+ .sort((a, b) => b.score - a.score || executionModeRank(a.mode) - executionModeRank(b.mode));
504
+ }
505
+
506
+ function applySafeUpwardBias(candidates = [], fallbackMode = 'local-build') {
507
+ const [topCandidate, competingCandidate] = candidates;
508
+ if (!topCandidate) {
509
+ return fallbackMode;
510
+ }
511
+
512
+ if (!competingCandidate) {
513
+ return topCandidate.mode;
514
+ }
515
+
516
+ const scoreGap = Number(topCandidate.score ?? 0) - Number(competingCandidate.score ?? 0);
517
+ const rankGap = executionModeRank(competingCandidate.mode) - executionModeRank(topCandidate.mode);
518
+ if (scoreGap <= 1 && rankGap === 1) {
519
+ return competingCandidate.mode;
520
+ }
521
+
522
+ return topCandidate.mode;
523
+ }
524
+
525
+ function executionModeRank(mode = '') {
526
+ const orderedModes = [
527
+ 'tiny-fix',
528
+ 'local-fix',
529
+ 'local-build',
530
+ 'find-cause',
531
+ 'shared-edit',
532
+ 'map-impact',
533
+ 'review-release',
534
+ ];
535
+
536
+ const index = orderedModes.indexOf(mode);
537
+ return index >= 0 ? index : orderedModes.length;
385
538
  }
386
539
 
387
540
  function buildExecutionContract(executionMode = null) {
@@ -453,6 +606,7 @@ function buildExecutionContract(executionMode = null) {
453
606
  function buildApproachSelectorResult({
454
607
  executionMode = null,
455
608
  executionScores = null,
609
+ executionCandidates = null,
456
610
  } = {}) {
457
611
  if (!executionMode) {
458
612
  return null;
@@ -501,6 +655,14 @@ function buildApproachSelectorResult({
501
655
  contextPolicy: selectedPolicy.contextPolicy,
502
656
  verificationPolicy: executionContract?.verificationPolicy ?? null,
503
657
  completionRule: executionContract?.completionRule ?? null,
658
+ competingMode: executionCandidates?.[1]?.mode ?? null,
659
+ competingScoreGap: executionCandidates?.length >= 2
660
+ ? Number(executionCandidates[0]?.score ?? 0) - Number(executionCandidates[1]?.score ?? 0)
661
+ : null,
662
+ candidateModes: (executionCandidates ?? []).slice(0, 3).map((entry) => ({
663
+ mode: entry.mode,
664
+ score: entry.score,
665
+ })),
504
666
  };
505
667
  }
506
668
 
@@ -588,6 +750,8 @@ function buildContinuationState({
588
750
  repeatCount: reasons.length > 0 ? 1 : 0,
589
751
  stuckRisk: null,
590
752
  rescueMode: null,
753
+ wideningBlocked: false,
754
+ milestonePriority: reasons.length > 0 ? 'normal' : null,
591
755
  };
592
756
  }
593
757
 
@@ -749,7 +913,7 @@ function deriveIntentMode({ promptText = '', commandText = '', targetFile = null
749
913
  }
750
914
 
751
915
  if (concreteTask) {
752
- if (/\b(bug|debug|error|crash|broken|failing|stack trace|triage|fix|login|timeout)\b/.test(lower)) {
916
+ if (/\b(bug|debug|error|crash|broken|failing|stack trace|triage|fix|timeout)\b/.test(lower)) {
753
917
  return 'debug-specific';
754
918
  }
755
919
  if (/\b(review|audit|diff|pr feedback|code review|kiem tra|soat)\b/.test(raw)) {