@ngockhoale/ukit 2.0.5 → 2.0.6

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,6 +2,12 @@
2
2
 
3
3
  All notable changes to UKit are documented here.
4
4
 
5
+ ## 2.0.6 - 2026-08-12
6
+
7
+ ### Changed
8
+
9
+ - **`context-window-guard.sh` now issues an actionable directive instead of a passive notice.** At 80%+ of `compact.hardCapTokens` it previously only printed an FYI warning that nothing acted on, so it kept re-firing every prompt with no remedy. It now instructs the agent, in the same turn: persist progress to `docs/STATUS.md` (update-status skill), split remaining multi-phase work (e.g. a `handoff-fullstack` run) into bounded `docs/AI_HANDOFF` tasks instead of continuing in one long thread, or tell the user plainly to `/compact`/start a fresh session. The full directive is debounced (3 min at hard phase, 8 min at soft) so it does not repeat in full every prompt — a short reminder line stands in until the phase changes or the cooldown lapses. `/compact` itself still cannot be invoked from a hook (client-only command), so this closes the gap by making the advisory self-actionable rather than attempting to fake a call the hook has no access to.
10
+
5
11
  ## 2.0.5 - 2026-08-11
6
12
 
7
13
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ngockhoale/ukit",
3
- "version": "2.0.5",
3
+ "version": "2.0.6",
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",
@@ -134,21 +134,55 @@ const ratio = estimatedTokens / hardCap;
134
134
  if (ratio < 0.8) process.exit(0);
135
135
 
136
136
  const pct = Math.round(ratio * 100);
137
+ const phase = ratio >= 1 ? 'hard' : 'soft';
138
+
139
+ // Debounce the directive block so it does not re-print in full every single prompt
140
+ // while the phase is unchanged — that would just be more tokens added to the same
141
+ // oversized context it is warning about. Re-arms on phase change (soft -> hard) or
142
+ // after the cooldown, and resets naturally once a real compaction/new session drops
143
+ // the live transcript back under 0.8, since this whole branch exits early above.
144
+ const GUARD_STATE_PATH = path.join(projectRoot, '.ukit', 'storage', 'cache', 'context-guard-state.json');
145
+ const COOLDOWN_MS = phase === 'hard' ? 3 * 60 * 1000 : 8 * 60 * 1000;
146
+
147
+ function readGuardState() {
148
+ try {
149
+ return JSON.parse(fs.readFileSync(GUARD_STATE_PATH, 'utf8'));
150
+ } catch {
151
+ return { lastPhase: null, lastActionAt: 0 };
152
+ }
153
+ }
154
+
155
+ function writeGuardState(state) {
156
+ try {
157
+ fs.mkdirSync(path.dirname(GUARD_STATE_PATH), { recursive: true });
158
+ fs.writeFileSync(GUARD_STATE_PATH, `${JSON.stringify(state, null, 2)}\n`, 'utf8');
159
+ } catch { /* best-effort only; never block the prompt over this */ }
160
+ }
161
+
162
+ const guardState = readGuardState();
163
+ const now = Date.now();
164
+ const withinCooldown = guardState.lastPhase === phase
165
+ && (now - Number(guardState.lastActionAt || 0)) < COOLDOWN_MS;
166
+
137
167
  const lines_out = [];
138
168
  if (ratio >= 1) {
139
169
  lines_out.push(`UKIT CONTEXT ALERT — live context ~${estimatedTokens.toLocaleString()} tokens, at/over the ${hardCap.toLocaleString()} cap (${pct}%).`);
140
170
  lines_out.push('The next few turns risk "Your input exceeds the context window of this model".');
141
- lines_out.push('Act now: /compact, or finish this thread and start a fresh session.');
142
- lines_out.push('If /compact itself already failed, the context is too large to summarise —');
143
- lines_out.push('a new session is the only way out. Work committed to disk is not lost.');
144
171
  } else {
145
172
  lines_out.push(`UKIT CONTEXT WARNING — live context ~${estimatedTokens.toLocaleString()} tokens (${pct}% of the ${hardCap.toLocaleString()} cap).`);
146
- lines_out.push('Wrap up or /compact soon, while compacting still works.');
147
173
  }
148
- if (sidechainEntries > 0) {
149
- lines_out.push(`Note: ${sidechainEntries} subagent entries in this stretch. Each teammate carries its own`);
150
- lines_out.push('context window, and every finished report is injected back here — running many at');
151
- lines_out.push('once is the fastest way to overflow this session.');
174
+
175
+ if (withinCooldown) {
176
+ lines_out.push('(Directive already issued this phase — act on it now if not already done; this is just a reminder.)');
177
+ } else {
178
+ lines_out.push('ACTION THIS TURN, before starting new investigation/subagents/pipeline phases:');
179
+ lines_out.push('1) Persist current progress now (update docs/STATUS.md, e.g. via the update-status skill) so nothing is lost.');
180
+ lines_out.push('2) If the remaining work still has multiple steps or pipeline phases left (e.g. a handoff-fullstack run with implement/review/deploy still ahead), split what remains into bounded docs/AI_HANDOFF tasks instead of continuing in this one thread — smaller phases keep each turn under the cap.');
181
+ lines_out.push('3) Otherwise, tell the user plainly: run /compact now, or wrap up and start a fresh session — work already committed/persisted to disk is not lost.');
182
+ if (sidechainEntries > 0) {
183
+ lines_out.push(`Note: ${sidechainEntries} subagent entries in this stretch — each teammate carries its own context window, and every finished report is injected back here, so running many at once is the fastest way to overflow this session. Avoid spawning more until context drops back under the cap.`);
184
+ }
185
+ writeGuardState({ lastPhase: phase, lastActionAt: now });
152
186
  }
153
187
 
154
188
  process.stdout.write(`${lines_out.join('\n')}\n`);