@ngockhoale/ukit 1.6.0 → 1.6.2
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 +14 -0
- package/README.md +1 -0
- package/package.json +1 -1
- package/src/cli/commands/update.js +27 -0
- package/src/cli/index.js +7 -0
- package/src/core/update.js +19 -0
- package/templates/.claude/hooks/skill-router.sh +1 -1
- package/templates/.claude/ukit/index/route-task.mjs +10 -0
- package/templates/.claude/ukit/runtime/compact-threshold.mjs +7 -1
- package/templates/CLAUDE.md +26 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to UKit are documented here.
|
|
4
4
|
|
|
5
|
+
## 1.6.2 - 2026-07-26
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- `ukit update` — new command that upgrades the globally installed UKit CLI to the latest published version (equivalent to `npm install -g @ngockhoale/ukit`).
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- Downgraded `find-cause` and `map-impact` orchestration contracts from `modelTier: smart` (opus) to `modelTier: code` (sonnet); `review-release` stays on `smart`. Reduces unnecessary Opus usage for routine debugging/impact-mapping work.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Auto-compact context-pressure state is now surfaced to the model as a warning (`[ukit-skill-router] Context pressure: soft/hard ...`) once token usage crosses the soft/hard threshold, instead of being silently tracked and only reinjected after Claude Code's native compaction already began. Users previously had to compact manually because nothing ever prompted proactive `/compact` suggestions.
|
|
18
|
+
|
|
5
19
|
## 1.5.24 - 2026-06-10
|
|
6
20
|
|
|
7
21
|
### Fixed
|
package/README.md
CHANGED
|
@@ -162,6 +162,7 @@ These commands are still available, but they are **advanced workflows**, not def
|
|
|
162
162
|
- `ukit uninstall` — remove UKit-managed assets
|
|
163
163
|
- `ukit status` — inspect shared UKit runtime state
|
|
164
164
|
- `ukit memory ...` — inspect/export/forget shared memory items
|
|
165
|
+
- `ukit update` — upgrade the global UKit CLI to the latest published version (same as `npm install -g @ngockhoale/ukit`)
|
|
165
166
|
- `ukit index ...` — run repo indexing/query/triage tools directly
|
|
166
167
|
- `ukit build index` — alias for `ukit index build`
|
|
167
168
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { updateUkit, UKIT_PACKAGE_NAME } from '../../core/update.js';
|
|
2
|
+
|
|
3
|
+
const KNOWN_UPDATE_FLAGS = new Set(['--help', '-h']);
|
|
4
|
+
|
|
5
|
+
export async function runUpdate({ packageVersion, argv = [] }) {
|
|
6
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
7
|
+
console.log('Usage: ukit update');
|
|
8
|
+
console.log('');
|
|
9
|
+
console.log('Upgrade the globally installed UKit CLI to the latest published version.');
|
|
10
|
+
console.log(`Equivalent to: npm install -g ${UKIT_PACKAGE_NAME}`);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const unknownFlags = argv.filter((a) => a.startsWith('-') && !KNOWN_UPDATE_FLAGS.has(a));
|
|
15
|
+
if (unknownFlags.length > 0) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
`Unknown option(s): ${unknownFlags.join(', ')}. Run \`ukit update --help\` for usage.`,
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
console.log(`[UKit] Current version: ${packageVersion}`);
|
|
22
|
+
console.log(`[UKit] Running: npm install -g ${UKIT_PACKAGE_NAME}`);
|
|
23
|
+
|
|
24
|
+
updateUkit();
|
|
25
|
+
|
|
26
|
+
console.log('[UKit] Update complete. Run `ukit --version` to confirm the new version.');
|
|
27
|
+
}
|
package/src/cli/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { runUninstall } from './commands/uninstall.js';
|
|
|
5
5
|
import { runIndexTools } from './commands/indexTools.js';
|
|
6
6
|
import { runStatus } from './commands/status.js';
|
|
7
7
|
import { runMemory } from './commands/memory.js';
|
|
8
|
+
import { runUpdate } from './commands/update.js';
|
|
8
9
|
|
|
9
10
|
const GLOBAL_FLAGS = new Set(['--help', '-h', '--version', '-v']);
|
|
10
11
|
|
|
@@ -58,6 +59,11 @@ export async function runCli({ argv, packageRoot, projectRoot, packageVersion })
|
|
|
58
59
|
return;
|
|
59
60
|
}
|
|
60
61
|
|
|
62
|
+
if (command === 'update') {
|
|
63
|
+
await runUpdate({ packageVersion, argv: commandArgv });
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
61
67
|
if (command === 'build' && (commandArgv[0] ?? '').toLowerCase() === 'index') {
|
|
62
68
|
await runIndexTools({ projectRoot, argv: ['build', ...commandArgv.slice(1)] });
|
|
63
69
|
return;
|
|
@@ -85,6 +91,7 @@ export async function runCli({ argv, packageRoot, projectRoot, packageVersion })
|
|
|
85
91
|
console.log(' index Codebase index tools (build/query/triage)');
|
|
86
92
|
console.log(' status Show UKit runtime status');
|
|
87
93
|
console.log(' memory Inspect shared UKit memory');
|
|
94
|
+
console.log(' update Upgrade the global UKit CLI to the latest version');
|
|
88
95
|
console.log(' version Show UKit version');
|
|
89
96
|
console.log('');
|
|
90
97
|
console.log('Options (install / diff):');
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { spawnSync as defaultSpawnSync } from 'node:child_process';
|
|
2
|
+
|
|
3
|
+
export const UKIT_PACKAGE_NAME = '@ngockhoale/ukit';
|
|
4
|
+
|
|
5
|
+
export function updateUkit({ spawnSync = defaultSpawnSync } = {}) {
|
|
6
|
+
const result = spawnSync('npm', ['install', '-g', UKIT_PACKAGE_NAME], {
|
|
7
|
+
stdio: 'inherit',
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
if (result.error) {
|
|
11
|
+
throw new Error(`Failed to run npm install -g ${UKIT_PACKAGE_NAME}: ${result.error.message}`);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (result.status !== 0) {
|
|
15
|
+
throw new Error(`npm install -g ${UKIT_PACKAGE_NAME} exited with code ${result.status}`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
@@ -2601,7 +2601,7 @@ const { pathToFileURL } = require('url');
|
|
|
2601
2601
|
NODE
|
|
2602
2602
|
|
|
2603
2603
|
if [ -f "$THRESHOLD_SCRIPT" ]; then
|
|
2604
|
-
printf '%s' "$INPUT" | CLAUDE_PROJECT_DIR="$PROJECT_ROOT" node "$THRESHOLD_SCRIPT" prompt >/dev/null
|
|
2604
|
+
printf '%s' "$INPUT" | CLAUDE_PROJECT_DIR="$PROJECT_ROOT" node "$THRESHOLD_SCRIPT" prompt 2>/dev/null || true
|
|
2605
2605
|
fi
|
|
2606
2606
|
|
|
2607
2607
|
exit 0
|
|
@@ -433,6 +433,9 @@ function printRouteState(state) {
|
|
|
433
433
|
console.log(`recent-output: ${recentOutputDisplay}`);
|
|
434
434
|
}
|
|
435
435
|
console.log(`summary: ${compactSummary}`);
|
|
436
|
+
if (state.routeSummary?.executionContract?.modelTier) {
|
|
437
|
+
console.log(`modelTier: ${state.routeSummary.executionContract.modelTier}`);
|
|
438
|
+
}
|
|
436
439
|
if (nextDisplay) {
|
|
437
440
|
console.log(`next: ${nextDisplay}`);
|
|
438
441
|
}
|
|
@@ -1655,6 +1658,7 @@ function buildExecutionContract(executionMode = null) {
|
|
|
1655
1658
|
|
|
1656
1659
|
const contracts = {
|
|
1657
1660
|
'tiny-fix': {
|
|
1661
|
+
modelTier: 'lite',
|
|
1658
1662
|
maxReadPasses: 0,
|
|
1659
1663
|
maxContextPulls: 0,
|
|
1660
1664
|
verificationPolicy: 'minimal-or-targeted',
|
|
@@ -1663,6 +1667,7 @@ function buildExecutionContract(executionMode = null) {
|
|
|
1663
1667
|
completionEvidence: ['write-evidence'],
|
|
1664
1668
|
},
|
|
1665
1669
|
'local-fix': {
|
|
1670
|
+
modelTier: 'code',
|
|
1666
1671
|
maxReadPasses: 1,
|
|
1667
1672
|
maxContextPulls: 1,
|
|
1668
1673
|
verificationPolicy: 'targeted-if-covered',
|
|
@@ -1671,6 +1676,7 @@ function buildExecutionContract(executionMode = null) {
|
|
|
1671
1676
|
completionEvidence: ['write-evidence'],
|
|
1672
1677
|
},
|
|
1673
1678
|
'local-build': {
|
|
1679
|
+
modelTier: 'code',
|
|
1674
1680
|
maxReadPasses: 2,
|
|
1675
1681
|
maxContextPulls: 1,
|
|
1676
1682
|
verificationPolicy: 'targeted-if-covered',
|
|
@@ -1679,6 +1685,7 @@ function buildExecutionContract(executionMode = null) {
|
|
|
1679
1685
|
completionEvidence: ['write-evidence'],
|
|
1680
1686
|
},
|
|
1681
1687
|
'find-cause': {
|
|
1688
|
+
modelTier: 'code',
|
|
1682
1689
|
maxReadPassesBeforeReassess: 3,
|
|
1683
1690
|
verificationPolicy: 'root-cause-then-targeted',
|
|
1684
1691
|
completionRule: 'never-claim-fixed-without-write-and-verification',
|
|
@@ -1686,6 +1693,7 @@ function buildExecutionContract(executionMode = null) {
|
|
|
1686
1693
|
completionEvidence: ['write-evidence', 'verification-evidence'],
|
|
1687
1694
|
},
|
|
1688
1695
|
'shared-edit': {
|
|
1696
|
+
modelTier: 'code',
|
|
1689
1697
|
maxReadPasses: 2,
|
|
1690
1698
|
maxContextPulls: 2,
|
|
1691
1699
|
verificationPolicy: 'targeted-then-widen-on-risk',
|
|
@@ -1695,6 +1703,7 @@ function buildExecutionContract(executionMode = null) {
|
|
|
1695
1703
|
mirrorConsistencyRequired: true,
|
|
1696
1704
|
},
|
|
1697
1705
|
'map-impact': {
|
|
1706
|
+
modelTier: 'code',
|
|
1698
1707
|
maxReadPasses: 3,
|
|
1699
1708
|
maxContextPulls: 3,
|
|
1700
1709
|
verificationPolicy: 'impact-first-then-targeted-then-widen-on-risk',
|
|
@@ -1704,6 +1713,7 @@ function buildExecutionContract(executionMode = null) {
|
|
|
1704
1713
|
mirrorConsistencyRequired: true,
|
|
1705
1714
|
},
|
|
1706
1715
|
'review-release': {
|
|
1716
|
+
modelTier: 'smart',
|
|
1707
1717
|
verificationPolicy: 'evidence-first',
|
|
1708
1718
|
completionRule: 'report-findings-not-implementation',
|
|
1709
1719
|
delegationPolicy: 'allow-review-sidecar',
|
|
@@ -1019,12 +1019,18 @@ async function runCli() {
|
|
|
1019
1019
|
return;
|
|
1020
1020
|
}
|
|
1021
1021
|
|
|
1022
|
-
await updateCompactPressureFromPrompt(projectRoot, {
|
|
1022
|
+
const nextState = await updateCompactPressureFromPrompt(projectRoot, {
|
|
1023
1023
|
promptText,
|
|
1024
1024
|
routingContext: routeState?.routingContext ?? {},
|
|
1025
1025
|
previousContext: routeState?.previousContext ?? null,
|
|
1026
1026
|
routeSummary: routeState?.routeSummary ?? null,
|
|
1027
1027
|
}, config);
|
|
1028
|
+
|
|
1029
|
+
if (nextState.phase === 'soft' || nextState.phase === 'hard') {
|
|
1030
|
+
process.stdout.write(
|
|
1031
|
+
`[ukit-skill-router] Context pressure: ${nextState.phase} (est ${nextState.estimatedTotalTokens} / soft ${nextState.softThreshold} / hard ${nextState.hardThreshold}). Suggest running /compact soon, or I will self-summarize before continuing.\n`,
|
|
1032
|
+
);
|
|
1033
|
+
}
|
|
1028
1034
|
}
|
|
1029
1035
|
}
|
|
1030
1036
|
|
package/templates/CLAUDE.md
CHANGED
|
@@ -136,6 +136,32 @@ Khi Handoff mode: đọc `docs/AI_HANDOFF/RULES.md` để biết 4 phase (Idea+P
|
|
|
136
136
|
- `autonomy.level` in `.ukit/storage/config.json` controls how much UKit acts without asking first: `conservative` (ask more), `balanced` (default), `free-run` (auto-run more).
|
|
137
137
|
- End users should not need to change this; maintainers may tune it per-project.
|
|
138
138
|
|
|
139
|
+
## 3-Tier Model Routing
|
|
140
|
+
|
|
141
|
+
**Internal orchestration only — end users still just use natural language. No new commands.**
|
|
142
|
+
|
|
143
|
+
UKit routes tasks to one of three model tiers based on task complexity:
|
|
144
|
+
|
|
145
|
+
| Tier | Generic alias | Claude model | Typical tasks |
|
|
146
|
+
|------|--------------|--------------|---------------|
|
|
147
|
+
| lite | `unic-lite` | claude-haiku | Reads, git queries, bash summaries, small doc edits |
|
|
148
|
+
| code | `unic-code` | claude-sonnet | Normal coding, local fixes, shared edits, builds, debugging, impact mapping |
|
|
149
|
+
| smart | `unic-smart` | claude-opus | Release review/audit, and escalated deep reasoning after repeated failure |
|
|
150
|
+
|
|
151
|
+
### Contract-to-tier mapping
|
|
152
|
+
|
|
153
|
+
| Contract | Tier |
|
|
154
|
+
|----------|------|
|
|
155
|
+
| `tiny-fix` | lite |
|
|
156
|
+
| `local-fix`, `local-build`, `shared-edit`, `find-cause`, `map-impact` | code |
|
|
157
|
+
| `review-release` | smart |
|
|
158
|
+
|
|
159
|
+
### Escalation rule
|
|
160
|
+
|
|
161
|
+
When the same file or symbol fails `debugLoopThreshold` (default: 2) times in one session, UKit routes the next attempt one tier higher (capped at `smart`). Config: `orchestration.escalation` in `.ukit/storage/config.json`.
|
|
162
|
+
|
|
163
|
+
This is internal orchestration — end users do not need to know about tiers, thresholds, or escalation. The AI handles routing transparently.
|
|
164
|
+
|
|
139
165
|
## Project Snapshot
|
|
140
166
|
|
|
141
167
|
- Project: {{project.name}} | Root: {{project.root}}
|