@ngockhoale/ukit 3.4.4 → 3.4.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,16 @@
2
2
 
3
3
  All notable changes to UKit are documented here.
4
4
 
5
+
6
+ ## 3.4.4 - 2026-09-30
7
+
8
+ **Stop-hook blocks are now user-visible.** When the Stop gate refuses a
9
+ premature stop (missing completion evidence, mid-flight handoff run, watchdog
10
+ budget), Claude Code previously received only the model-facing `reason` — the
11
+ turn looked like a silent stall and users had to type "continue" blindly.
12
+ Every blocked stop now also emits a `systemMessage` shown in the transcript:
13
+ `[UKit] Stop blocked — <policyOwner>: <reason first line>`.
14
+
5
15
  ## 3.4.3 - 2026-09-29
6
16
 
7
17
  Republish of 3.4.2 — npm staged-version E409 limbo (same content, version bump only).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ngockhoale/ukit",
3
- "version": "3.4.4",
3
+ "version": "3.4.6",
4
4
  "description": "Install/update an index-first AI workspace for Claude Code, OpenAI Codex and omp (Oh My Pi).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -316,9 +316,10 @@ function buildRawOutputText({ command = '', stdout = '', stderr = '', exitCode =
316
316
 
317
317
  function buildRecoveryHintSummary(summary, rawPath, { tokensBefore = 0 } = {}) {
318
318
  // TASK-003 fix round 1: a failed tee persist returns rawPath='' — never emit a
319
- // dangling `- Full output: ` hint that points at nothing.
319
+ // dangling `- Tee log: ` hint that points at nothing. Label avoids "Full output:"
320
+ // because agents misread a path-shaped hint as an artifact:// pointer (omp).
320
321
  if (!rawPath) return null;
321
- const recoveryLine = `- Full output: ${rawPath}`;
322
+ const recoveryLine = `- Tee log: ${rawPath}`;
322
323
  const summaryLines = String(summary ?? '')
323
324
  .split(/\r?\n/)
324
325
  .map((line) => line.trim())
@@ -1143,7 +1144,7 @@ function normalizeOutputSummaryForDedupe(summary) {
1143
1144
  return String(summary ?? '')
1144
1145
  .split(/\r?\n/)
1145
1146
  .map((line) => String(line ?? '').trim())
1146
- .filter((line) => line && !/^- Full output:\s+/i.test(line))
1147
+ .filter((line) => line && !/^- (?:Full output|Tee log):\s+/i.test(line))
1147
1148
  .join('\n');
1148
1149
  }
1149
1150
 
@@ -379,9 +379,10 @@ function buildRawOutputText({ command = '', stdout = '', stderr = '', exitCode =
379
379
 
380
380
  function buildRecoveryHintSummary(summary, rawPath, { tokensBefore = 0 } = {}) {
381
381
  // TASK-003 fix round 1: a failed tee persist returns rawPath='' — never emit a
382
- // dangling `- Full output: ` hint that points at nothing.
382
+ // dangling `- Tee log: ` hint that points at nothing. Label avoids "Full output:"
383
+ // because agents misread a path-shaped hint as an artifact:// pointer (omp).
383
384
  if (!rawPath) return null;
384
- const recoveryLine = `- Full output: ${rawPath}`;
385
+ const recoveryLine = `- Tee log: ${rawPath}`;
385
386
  const summaryLines = String(summary ?? '')
386
387
  .split(/\r?\n/)
387
388
  .map((line) => line.trim())
@@ -1252,7 +1253,7 @@ function normalizeOutputSummaryForDedupe(summary) {
1252
1253
  .split(/\r?\n/)
1253
1254
  .map((line) => String(line ?? '').trim())
1254
1255
  .filter((line) => line
1255
- && !/^- Full output:\s+/i.test(line)
1256
+ && !/^- (?:Full output|Tee log):\s+/i.test(line)
1256
1257
  && !/^-\s*FAIL(?:ED)?\b/i.test(line)
1257
1258
  && !/^-\s*PASS\b/i.test(line)
1258
1259
  && !/^-\s*(?:Test Files|Tests|Duration|Start at)\b/i.test(line))
@@ -1170,10 +1170,18 @@ async function main() {
1170
1170
  if (merged.decision === 'block') {
1171
1171
  // One JSON line: the single decision, its provenance, and — when an advisory
1172
1172
  // lane failed or has news — the verbatim note. undefined fields are omitted.
1173
+ //
1174
+ // `reason` is fed to the MODEL as the continuation prompt — Claude Code does
1175
+ // NOT display it to the user. Only `systemMessage` is user-visible, so a
1176
+ // blocked stop must surface its reason there too: without it the turn just
1177
+ // "stops and restarts" with no explanation, which reads as the agent
1178
+ // stalling and forces the user to type "continue".
1179
+ const firstLine = String(merged.reason || '').split('\n', 1)[0] || 'reason unavailable';
1180
+ const blockNote = `[UKit] Stop blocked — ${merged.policyOwner}: ${firstLine}`;
1173
1181
  writeDecisionLine({
1174
1182
  decision: 'block',
1175
1183
  reason: merged.reason,
1176
- systemMessage: merged.systemMessage,
1184
+ systemMessage: merged.systemMessage ? `${blockNote}\n${merged.systemMessage}` : blockNote,
1177
1185
  policyOwner: merged.policyOwner,
1178
1186
  });
1179
1187
  return;