ticketlens 0.36.0 → 0.37.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ticketlens",
3
- "version": "0.36.0",
3
+ "version": "0.37.0",
4
4
  "description": "Jira CLI for developers — fetch ticket context, triage your queue, and stop tab-switching. Zero dependencies, all local.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -45,10 +45,10 @@ function refuseGithubAttachments(adapter, attachPaths, stream) {
45
45
  return true;
46
46
  }
47
47
 
48
- function formatAttachSummary(attachResult) {
48
+ function formatAttachSummary(attachResult, s) {
49
49
  if (!attachResult) return '';
50
50
  const lines = [];
51
- for (const u of attachResult.uploaded) lines.push(` Attached ${u.filename}${u.url ? ` (${u.url})` : ''}\n`);
51
+ for (const u of attachResult.uploaded) lines.push(` ${s.green('✔')} Attached ${s.bold(u.filename)}${u.url ? ` (${u.url})` : ''}\n`);
52
52
  for (const e of attachResult.errors) lines.push(` Failed to attach ${e.path}: ${e.message}\n`);
53
53
  if (attachResult.droppedCount > 0) lines.push(` ${attachResult.droppedCount} attachment(s) dropped — exceeds the ${MAX_ATTACHMENTS}-file limit per call.\n`);
54
54
  return lines.join('');
@@ -190,11 +190,14 @@ function describeAppliedFields(applied) {
190
190
  return parts.join(', ');
191
191
  }
192
192
 
193
- function formatUpdateResult(ticketKey, { applied, errors }) {
193
+ function formatUpdateResult(ticketKey, { applied, errors }, s) {
194
194
  const appliedText = describeAppliedFields(applied);
195
195
  const errorText = Object.entries(errors).map(([field, info]) => formatFieldError(field, info)).join('; ');
196
- if (appliedText && !errorText) return ` ${ticketKey} updated: ${appliedText}.\n`;
197
- if (appliedText && errorText) return ` ${ticketKey} partially updated: ${appliedText}. Failed: ${errorText}.\n`;
196
+ // Success/partial get the styled brand+bold key; total failure stays plain,
197
+ // consistent with every other refusal/error message in this file (e.g. the
198
+ // GitHub-priority-refusal line just above in runTicketUpdate).
199
+ if (appliedText && !errorText) return ` ${s.green('✔')} ${s.brand(s.bold(ticketKey))} updated: ${s.bold(appliedText)}.\n`;
200
+ if (appliedText && errorText) return ` ${s.yellow('~')} ${s.brand(s.bold(ticketKey))} partially updated: ${s.bold(appliedText)}. Failed: ${errorText}.\n`;
198
201
  return ` Nothing updated on ${ticketKey}. Failed: ${errorText}.\n`;
199
202
  }
200
203
 
@@ -267,6 +270,7 @@ export async function runTicketComment(cmdArgs, {
267
270
 
268
271
  const adapter = resolveTicketAdapter(ticketKey, cmdArgs, { configDir, resolveConnectionFn, resolveAdapterFn, stream });
269
272
  if (!adapter) return { ok: false };
273
+ const s = createStyler({ isTTY: stream.isTTY });
270
274
 
271
275
  // Uploaded BEFORE the comment write so a tracker capable of inline
272
276
  // rendering (Jira Server/DC via wiki markup, Jira Cloud via a real ADF
@@ -287,13 +291,13 @@ export async function runTicketComment(cmdArgs, {
287
291
  // actually landed) — a partial attach failure is reconstructable from
288
292
  // the difference between the two, not just silently absent from audit.
289
293
  logActionFn({ ticketKey, action: 'comment', actor, tracker: adapter.type, detail: { id: result.id, attachPaths, attachedFilenames: (attachResult?.uploaded ?? []).map(a => a.filename) } }, { configDir });
290
- stream.write(` Comment posted to ${ticketKey}${result.url ? ` (${result.url})` : ''}\n` + formatAttachSummary(attachResult));
294
+ stream.write(` ${s.green('✔')} Comment posted to ${s.brand(s.bold(ticketKey))}${result.url ? ` (${result.url})` : ''}\n` + formatAttachSummary(attachResult, s));
291
295
  return { ok: true };
292
296
  } catch (err) {
293
297
  // Attachments (if any) genuinely landed on the tracker before this
294
298
  // write was attempted — formatAttachSummary is still shown here so a
295
299
  // caller retrying the whole command doesn't blindly re-upload them.
296
- stream.write(formatWriteFailure(ticketKey, err) + formatAttachSummary(attachResult));
300
+ stream.write(formatWriteFailure(ticketKey, err) + formatAttachSummary(attachResult, s));
297
301
  return { ok: false };
298
302
  }
299
303
  }
@@ -323,13 +327,14 @@ export async function runTicketTransitionList(cmdArgs, {
323
327
 
324
328
  try {
325
329
  const options = await adapter.getTransitions(ticketKey);
330
+ const s = createStyler({ isTTY: stream.isTTY });
326
331
  if (options.length === 0) {
327
- stream.write(` No valid transitions available for ${ticketKey}.\n`);
332
+ stream.write(` No valid transitions available for ${s.brand(s.bold(ticketKey))}.\n`);
328
333
  return { ok: true, options: [] };
329
334
  }
330
- stream.write(` Valid transitions for ${ticketKey}:\n`);
331
- for (const o of options) stream.write(` - ${o.name}\n`);
332
- stream.write(` Run again with --target="<name>" --confirm to execute.\n`);
335
+ stream.write(` Valid transitions for ${s.brand(s.bold(ticketKey))}:\n\n`);
336
+ for (const o of options) stream.write(` ${s.brand('●')} ${o.name}\n`);
337
+ stream.write(`\n Run again with --target="<name>" --confirm to execute.\n`);
333
338
  return { ok: true, options };
334
339
  } catch (err) {
335
340
  stream.write(formatWriteFailure(ticketKey, err));
@@ -389,7 +394,8 @@ export async function runTicketTransition(cmdArgs, {
389
394
  }
390
395
  recordActionFn(ticketKey, 'transition', { configDir });
391
396
  logActionFn({ ticketKey, action: 'transition', actor, tracker: adapter.type, detail: { to: result.to } }, { configDir });
392
- stream.write(` ${ticketKey} transitioned to "${result.to}".\n`);
397
+ const s = createStyler({ isTTY: stream.isTTY });
398
+ stream.write(` ${s.green('✔')} ${s.brand(s.bold(ticketKey))} transitioned to ${s.bold(`"${result.to}"`)}.\n`);
393
399
  return { ok: true };
394
400
  } catch (err) {
395
401
  stream.write(formatWriteFailure(ticketKey, err));
@@ -443,7 +449,8 @@ export async function runTicketAssign(cmdArgs, {
443
449
  const result = await adapter.assignToSelf(ticketKey);
444
450
  recordActionFn(ticketKey, 'assign', { configDir });
445
451
  logActionFn({ ticketKey, action: 'assign', actor, tracker: adapter.type, detail: { assignee: result.assignee } }, { configDir });
446
- stream.write(` ${ticketKey} assigned to ${result.assignee}.\n`);
452
+ const s = createStyler({ isTTY: stream.isTTY });
453
+ stream.write(` ${s.green('✔')} ${s.brand(s.bold(ticketKey))} assigned to ${s.bold(result.assignee)}.\n`);
447
454
  return { ok: true };
448
455
  } catch (err) {
449
456
  stream.write(formatWriteFailure(ticketKey, err));
@@ -563,12 +570,14 @@ export async function runTicketLinkList(cmdArgs, {
563
570
 
564
571
  try {
565
572
  const types = await adapter.getLinkTypes();
573
+ const s = createStyler({ isTTY: stream.isTTY });
566
574
  if (types.length === 0) {
567
- stream.write(` No link types available for ${sourceKey} → ${targetKey} on ${adapter.type}.\n`);
575
+ stream.write(` No link types available for ${s.brand(s.bold(sourceKey))} → ${s.brand(s.bold(targetKey))} on ${adapter.type}.\n`);
568
576
  return { ok: true, types: [] };
569
577
  }
570
- stream.write(` Available link types for ${sourceKey} → ${targetKey} (${adapter.type}):\n`);
571
- for (const t of types) stream.write(` - ${t}\n`);
578
+ stream.write(` Available link types for ${s.brand(s.bold(sourceKey))} → ${s.brand(s.bold(targetKey))} (${adapter.type}):\n\n`);
579
+ for (const t of types) stream.write(` ${s.brand('●')} ${t}\n`);
580
+ stream.write('\n');
572
581
  if (adapter.type === 'github') {
573
582
  stream.write(` Note: GitHub has no generic link relationship — linking will CLOSE ${sourceKey} as a duplicate of ${targetKey}.\n`);
574
583
  }
@@ -647,10 +656,11 @@ export async function runTicketLink(cmdArgs, {
647
656
  }
648
657
  recordActionFn(cooldownKey, 'link', { configDir });
649
658
  logActionFn({ ticketKey: sourceKey, action: 'link', actor, tracker: adapter.type, detail: { targetKey, type } }, { configDir });
659
+ const s = createStyler({ isTTY: stream.isTTY });
650
660
  stream.write(
651
661
  adapter.type === 'github'
652
- ? ` ${sourceKey} closed as a duplicate of ${targetKey}.\n`
653
- : ` ${sourceKey} linked to ${targetKey} as "${type}".\n`,
662
+ ? ` ${s.green('✔')} ${s.brand(s.bold(sourceKey))} closed as a duplicate of ${s.brand(s.bold(targetKey))}.\n`
663
+ : ` ${s.green('✔')} ${s.brand(s.bold(sourceKey))} linked to ${s.brand(s.bold(targetKey))} as ${s.bold(`"${type}"`)}.\n`,
654
664
  );
655
665
  return { ok: true };
656
666
  } catch (err) {
@@ -733,7 +743,7 @@ export async function runTicketUpdate(cmdArgs, {
733
743
  recordActionFn(ticketKey, 'update', { configDir });
734
744
  logActionFn({ ticketKey, action: 'update', actor, tracker: adapter.type, detail: { ...result.applied, failed: Object.keys(result.errors) } }, { configDir });
735
745
  }
736
- stream.write(formatUpdateResult(ticketKey, result));
746
+ stream.write(formatUpdateResult(ticketKey, result, createStyler({ isTTY: stream.isTTY })));
737
747
  return hasErrors ? { ok: false, applied: result.applied, errors: result.errors } : { ok: true, applied: result.applied };
738
748
  } catch (err) {
739
749
  stream.write(formatWriteFailure(ticketKey, err));
@@ -794,6 +804,7 @@ export async function runTicketCreate(cmdArgs, {
794
804
 
795
805
  const adapter = resolveTicketAdapter(undefined, cmdArgs, { configDir, resolveConnectionFn, resolveAdapterFn, stream });
796
806
  if (!adapter) return { ok: false };
807
+ const s = createStyler({ isTTY: stream.isTTY });
797
808
  const attachRefused = refuseGithubAttachments(adapter, attachPaths, stream);
798
809
 
799
810
  if (adapter.type !== 'github' && !project) {
@@ -883,6 +894,6 @@ export async function runTicketCreate(cmdArgs, {
883
894
  }
884
895
  }
885
896
 
886
- stream.write(` Created ${result.key}${result.url ? ` (${result.url})` : ''}\n` + formatAttachSummary(attachResult));
897
+ stream.write(` ${s.green('✔')} Created ${s.brand(s.bold(result.key))}${result.url ? ` (${result.url})` : ''}\n` + formatAttachSummary(attachResult, s));
887
898
  return { ok: true, key: result.key };
888
899
  }