@tantawowa/hosanna-tools 3.3.0 → 3.4.0

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.
@@ -1,6 +1,6 @@
1
1
  {
2
- "buildDate": "2026-07-01T07:59:40+01:00",
3
- "buildDateISO": "2026-07-01T06:59:40.421Z",
2
+ "buildDate": "2026-07-03T10:14:50+01:00",
3
+ "buildDateISO": "2026-07-03T09:14:50.144Z",
4
4
  "timeZone": "Europe/London",
5
- "gitHash": "fd78d71"
5
+ "gitHash": "325eacd"
6
6
  }
@@ -4248,23 +4248,27 @@ Shows:
4248
4248
  - nextFocusMap hits/misses with target IDs and failure reasons
4249
4249
  - Bubble-up chain when focus bubbles to parent views
4250
4250
  - Per-key timeline when timeline=true
4251
+ - With capture_origins=true: PROGRAMMATIC setFocus origins — i.e. WHO called setFocus (e.g. an app onInputEvent handler doing setFocusedSubview('x')), with the JS call stack. Directional resolution alone cannot show this; use it when focus "bounces" to a view that no nextFocusMap/group navigation explains (the classic "menu opens unexpectedly after Back" class of bug).
4251
4252
 
4252
4253
  Use when:
4253
4254
  - Focus goes to unexpected view
4254
4255
  - nextFocusMap doesn't work
4255
4256
  - Focus gets trapped
4257
+ - A view opens/activates unexpectedly and you need WHO set focus there (capture_origins)
4256
4258
  - Need to understand focus resolution logic
4257
4259
 
4258
- Note: Focus tracing can also be enabled permanently via mcp.focusTracing=true in debug-flags.dev.json.`, {
4260
+ Note: Focus tracing can also be enabled permanently via mcp.focusTracing=true in debug-flags.dev.json.
4261
+ Note: capture_origins requires the framework to emit origins — FocusManager.setFocus must push { targetHid, targetId, source, callStack: new Error().stack } into the focus-trace buffer when SetFocusTracing was enabled with captureOrigins:true. Until that emit lands, origins will be empty (the tool degrades gracefully).`, {
4259
4262
  keys: zod_1.z.array(zod_1.z.string()).describe('Keys to send (e.g., ["Right", "Down"])'),
4260
4263
  trace_depth: zod_1.z.number().optional().default(5).describe('Max bubble-up levels to trace (default: 5)'),
4261
4264
  show_candidates: zod_1.z.boolean().optional().default(false).describe('Include rejected focus candidates'),
4265
+ capture_origins: zod_1.z.boolean().optional().default(false).describe('Capture PROGRAMMATIC setFocus origins (who called setFocus, with call stack) — surfaces app handlers doing setFocusedSubview(...), which directional resolution does not. Requires the framework focus-origin emit.'),
4262
4266
  timeline: zod_1.z.boolean().optional().default(true).describe('Show per-key breakdown (default: true)'),
4263
4267
  delay_ms: zod_1.z.number().optional().default(300).describe('Delay after each key in ms (default: 300)'),
4264
4268
  format: zod_1.z.enum(['json', 'table', 'markdown']).optional().default('table').describe('Output format'),
4265
4269
  appId: zod_1.z.string().optional().describe('Target app ID'),
4266
- }, async ({ keys, trace_depth = 5, show_candidates = false, timeline = true, delay_ms = 300, format = 'table', appId }) => {
4267
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5;
4270
+ }, async ({ keys, trace_depth = 5, show_candidates = false, capture_origins = false, timeline = true, delay_ms = 300, format = 'table', appId }) => {
4271
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6;
4268
4272
  const targetAppId = appId !== null && appId !== void 0 ? appId : (_a = (await client.getFirstConnectedApp())) === null || _a === void 0 ? void 0 : _a.appId;
4269
4273
  if (!targetAppId) {
4270
4274
  return {
@@ -4283,6 +4287,7 @@ Note: Focus tracing can also be enabled permanently via mcp.focusTracing=true in
4283
4287
  await client.executeCommand(targetAppId, types_js_1.RemoteDebugCommand.SetFocusTracing, {
4284
4288
  enabled: true,
4285
4289
  recordCandidates: show_candidates,
4290
+ captureOrigins: capture_origins,
4286
4291
  });
4287
4292
  try {
4288
4293
  // Process each key
@@ -4319,6 +4324,7 @@ Note: Focus tracing can also be enabled permanently via mcp.focusTracing=true in
4319
4324
  focusMoved,
4320
4325
  trace: (_h = traceData === null || traceData === void 0 ? void 0 : traceData.trace) !== null && _h !== void 0 ? _h : null,
4321
4326
  context: (_j = traceData === null || traceData === void 0 ? void 0 : traceData.context) !== null && _j !== void 0 ? _j : null,
4327
+ programmaticOrigins: (_k = traceData === null || traceData === void 0 ? void 0 : traceData.programmaticOrigins) !== null && _k !== void 0 ? _k : null,
4322
4328
  });
4323
4329
  }
4324
4330
  }
@@ -4330,7 +4336,7 @@ Note: Focus tracing can also be enabled permanently via mcp.focusTracing=true in
4330
4336
  // Format output
4331
4337
  // ─────────────────────────────────────────────────────────────────────────
4332
4338
  const lastStep = steps[steps.length - 1];
4333
- const overallMoved = (initialFocusData === null || initialFocusData === void 0 ? void 0 : initialFocusData._hid) !== ((_k = lastStep === null || lastStep === void 0 ? void 0 : lastStep.focusAfter) === null || _k === void 0 ? void 0 : _k._hid);
4339
+ const overallMoved = (initialFocusData === null || initialFocusData === void 0 ? void 0 : initialFocusData._hid) !== ((_l = lastStep === null || lastStep === void 0 ? void 0 : lastStep.focusAfter) === null || _l === void 0 ? void 0 : _l._hid);
4334
4340
  // Table format
4335
4341
  if (format === 'table') {
4336
4342
  const lines = [];
@@ -4351,10 +4357,10 @@ Note: Focus tracing can also be enabled permanently via mcp.focusTracing=true in
4351
4357
  lines.push(`╚${'═'.repeat(74)}╝`);
4352
4358
  // Summary
4353
4359
  if (overallMoved) {
4354
- lines.push(`${C.green}✓${C.reset} Focus changed: ${(_m = (_l = initialFocusData === null || initialFocusData === void 0 ? void 0 : initialFocusData.id) !== null && _l !== void 0 ? _l : initialFocusData === null || initialFocusData === void 0 ? void 0 : initialFocusData._hid) !== null && _m !== void 0 ? _m : 'none'} → ${(_r = (_p = (_o = lastStep === null || lastStep === void 0 ? void 0 : lastStep.focusAfter) === null || _o === void 0 ? void 0 : _o.id) !== null && _p !== void 0 ? _p : (_q = lastStep === null || lastStep === void 0 ? void 0 : lastStep.focusAfter) === null || _q === void 0 ? void 0 : _q._hid) !== null && _r !== void 0 ? _r : 'none'}`);
4360
+ lines.push(`${C.green}✓${C.reset} Focus changed: ${(_o = (_m = initialFocusData === null || initialFocusData === void 0 ? void 0 : initialFocusData.id) !== null && _m !== void 0 ? _m : initialFocusData === null || initialFocusData === void 0 ? void 0 : initialFocusData._hid) !== null && _o !== void 0 ? _o : 'none'} → ${(_s = (_q = (_p = lastStep === null || lastStep === void 0 ? void 0 : lastStep.focusAfter) === null || _p === void 0 ? void 0 : _p.id) !== null && _q !== void 0 ? _q : (_r = lastStep === null || lastStep === void 0 ? void 0 : lastStep.focusAfter) === null || _r === void 0 ? void 0 : _r._hid) !== null && _s !== void 0 ? _s : 'none'}`);
4355
4361
  }
4356
4362
  else {
4357
- lines.push(`${C.yellow}○${C.reset} Focus unchanged: ${(_t = (_s = initialFocusData === null || initialFocusData === void 0 ? void 0 : initialFocusData.id) !== null && _s !== void 0 ? _s : initialFocusData === null || initialFocusData === void 0 ? void 0 : initialFocusData._hid) !== null && _t !== void 0 ? _t : 'none'}`);
4363
+ lines.push(`${C.yellow}○${C.reset} Focus unchanged: ${(_u = (_t = initialFocusData === null || initialFocusData === void 0 ? void 0 : initialFocusData.id) !== null && _t !== void 0 ? _t : initialFocusData === null || initialFocusData === void 0 ? void 0 : initialFocusData._hid) !== null && _u !== void 0 ? _u : 'none'}`);
4358
4364
  }
4359
4365
  lines.push('');
4360
4366
  // Per-key breakdown
@@ -4368,8 +4374,8 @@ Note: Focus tracing can also be enabled permanently via mcp.focusTracing=true in
4368
4374
  const moveMark = step.focusMoved ? `${C.green}✓${C.reset}` : `${C.dim}○${C.reset}`;
4369
4375
  lines.push(`${moveMark} [${step.step}] ${C.bold}${step.key}${C.reset} ${C.cyan}${methodTag}${C.reset} +${step.timestamp}ms`);
4370
4376
  // Focus transition
4371
- const fromId = ((_u = step.focusBefore) === null || _u === void 0 ? void 0 : _u.id) || ((_v = step.focusBefore) === null || _v === void 0 ? void 0 : _v._hid) || 'none';
4372
- const toId = ((_w = step.focusAfter) === null || _w === void 0 ? void 0 : _w.id) || ((_x = step.focusAfter) === null || _x === void 0 ? void 0 : _x._hid) || 'none';
4377
+ const fromId = ((_v = step.focusBefore) === null || _v === void 0 ? void 0 : _v.id) || ((_w = step.focusBefore) === null || _w === void 0 ? void 0 : _w._hid) || 'none';
4378
+ const toId = ((_x = step.focusAfter) === null || _x === void 0 ? void 0 : _x.id) || ((_y = step.focusAfter) === null || _y === void 0 ? void 0 : _y._hid) || 'none';
4373
4379
  if (step.focusMoved) {
4374
4380
  lines.push(` ${fromId} → ${C.bold}${toId}${C.reset}`);
4375
4381
  }
@@ -4398,6 +4404,18 @@ Note: Focus tracing can also be enabled permanently via mcp.focusTracing=true in
4398
4404
  lines.push(` ${C.dim}bubble: ${chainStr}${C.reset}`);
4399
4405
  }
4400
4406
  }
4407
+ // Programmatic setFocus origins — "who called setFocus" (the gap directional resolution can't show)
4408
+ if (step.programmaticOrigins && step.programmaticOrigins.length > 0) {
4409
+ for (const origin of step.programmaticOrigins) {
4410
+ lines.push(` ${C.yellow}setFocus→${origin.targetId || origin.targetHid} (source=${origin.source})${C.reset}`);
4411
+ if (origin.callStack) {
4412
+ const frames = origin.callStack.split('\n').map(s => s.trim()).filter(s => s.startsWith('at ')).slice(0, trace_depth);
4413
+ for (const f of frames) {
4414
+ lines.push(` ${C.dim}${f}${C.reset}`);
4415
+ }
4416
+ }
4417
+ }
4418
+ }
4401
4419
  lines.push('');
4402
4420
  }
4403
4421
  // Siblings at final focus
@@ -4421,10 +4439,10 @@ Note: Focus tracing can also be enabled permanently via mcp.focusTracing=true in
4421
4439
  lines.push(`## diff_focus: ${keys.join(' → ')}`);
4422
4440
  lines.push('');
4423
4441
  if (overallMoved) {
4424
- lines.push(`✅ **Focus changed:** ${(_y = initialFocusData === null || initialFocusData === void 0 ? void 0 : initialFocusData.id) !== null && _y !== void 0 ? _y : 'none'} → ${(_0 = (_z = lastStep === null || lastStep === void 0 ? void 0 : lastStep.focusAfter) === null || _z === void 0 ? void 0 : _z.id) !== null && _0 !== void 0 ? _0 : 'none'}`);
4442
+ lines.push(`✅ **Focus changed:** ${(_z = initialFocusData === null || initialFocusData === void 0 ? void 0 : initialFocusData.id) !== null && _z !== void 0 ? _z : 'none'} → ${(_1 = (_0 = lastStep === null || lastStep === void 0 ? void 0 : lastStep.focusAfter) === null || _0 === void 0 ? void 0 : _0.id) !== null && _1 !== void 0 ? _1 : 'none'}`);
4425
4443
  }
4426
4444
  else {
4427
- lines.push(`⚪ **Focus unchanged:** ${(_1 = initialFocusData === null || initialFocusData === void 0 ? void 0 : initialFocusData.id) !== null && _1 !== void 0 ? _1 : 'none'}`);
4445
+ lines.push(`⚪ **Focus unchanged:** ${(_2 = initialFocusData === null || initialFocusData === void 0 ? void 0 : initialFocusData.id) !== null && _2 !== void 0 ? _2 : 'none'}`);
4428
4446
  }
4429
4447
  lines.push('');
4430
4448
  for (const step of steps) {
@@ -4437,8 +4455,8 @@ Note: Focus tracing can also be enabled permanently via mcp.focusTracing=true in
4437
4455
  const moveMark = step.focusMoved ? '✅' : '⚪';
4438
4456
  lines.push(`### ${moveMark} [${step.step}] **${step.key}** ${methodTag}`);
4439
4457
  lines.push('');
4440
- const fromId = ((_2 = step.focusBefore) === null || _2 === void 0 ? void 0 : _2.id) || ((_3 = step.focusBefore) === null || _3 === void 0 ? void 0 : _3._hid) || 'none';
4441
- const toId = ((_4 = step.focusAfter) === null || _4 === void 0 ? void 0 : _4.id) || ((_5 = step.focusAfter) === null || _5 === void 0 ? void 0 : _5._hid) || 'none';
4458
+ const fromId = ((_3 = step.focusBefore) === null || _3 === void 0 ? void 0 : _3.id) || ((_4 = step.focusBefore) === null || _4 === void 0 ? void 0 : _4._hid) || 'none';
4459
+ const toId = ((_5 = step.focusAfter) === null || _5 === void 0 ? void 0 : _5.id) || ((_6 = step.focusAfter) === null || _6 === void 0 ? void 0 : _6._hid) || 'none';
4442
4460
  if (step.focusMoved) {
4443
4461
  lines.push(`${fromId} → **${toId}**`);
4444
4462
  }
@@ -4463,6 +4481,21 @@ Note: Focus tracing can also be enabled permanently via mcp.focusTracing=true in
4463
4481
  const chainStr = chain.map(e => `${e.type}:${e.returned}`).join(' → ');
4464
4482
  lines.push(`*bubble:* ${chainStr}`);
4465
4483
  }
4484
+ // Programmatic setFocus origins — "who called setFocus"
4485
+ if (step.programmaticOrigins && step.programmaticOrigins.length > 0) {
4486
+ for (const origin of step.programmaticOrigins) {
4487
+ lines.push(`**setFocus → ${origin.targetId || origin.targetHid}** *(source=${origin.source})*`);
4488
+ if (origin.callStack) {
4489
+ const frames = origin.callStack.split('\n').map(s => s.trim()).filter(s => s.startsWith('at ')).slice(0, trace_depth);
4490
+ if (frames.length > 0) {
4491
+ lines.push('```');
4492
+ for (const f of frames)
4493
+ lines.push(f);
4494
+ lines.push('```');
4495
+ }
4496
+ }
4497
+ }
4498
+ }
4466
4499
  lines.push('');
4467
4500
  }
4468
4501
  const finalContext = lastStep === null || lastStep === void 0 ? void 0 : lastStep.context;