ticketlens 0.1.16 → 0.1.17
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
|
@@ -146,18 +146,29 @@ async function applySummarize(brief, args, opts, configDir, conn, licensedFn, up
|
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
function makeSpinner(s) {
|
|
149
|
-
|
|
149
|
+
// setInterval won't fire while spawnSync blocks the event loop, so we draw
|
|
150
|
+
// synchronously on update() and only use setInterval during async fetch phases.
|
|
151
|
+
if (!process.stderr.isTTY) return { update: () => {}, startAnim: () => {}, done: () => {} };
|
|
150
152
|
const frames = ['⠋','⠙','⠹','⠸','⠼','⠴','⠦','⠧','⠇','⠏'];
|
|
151
|
-
let
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
153
|
+
let fi = 0, msg = '', lastW = 0, timerId = null, finished = false;
|
|
154
|
+
|
|
155
|
+
const draw = () => {
|
|
156
|
+
const raw = ` ${s.brand(frames[fi % frames.length])} ${s.dim(msg)}`;
|
|
157
|
+
const vis = raw.replace(/\x1b\[[0-9;]*m/g, '');
|
|
158
|
+
process.stderr.write(`\r${raw}${' '.repeat(Math.max(0, lastW - vis.length))}`);
|
|
159
|
+
lastW = vis.length;
|
|
160
|
+
};
|
|
161
|
+
|
|
155
162
|
return {
|
|
156
|
-
update(m) { if (!finished) msg = m; },
|
|
163
|
+
update(m) { if (!finished) { fi++; msg = m; draw(); } },
|
|
164
|
+
startAnim() {
|
|
165
|
+
if (finished || timerId) return;
|
|
166
|
+
timerId = setInterval(() => { if (!finished) { fi++; draw(); } }, 80);
|
|
167
|
+
},
|
|
157
168
|
done() {
|
|
158
169
|
if (finished) return;
|
|
159
170
|
finished = true;
|
|
160
|
-
clearInterval(
|
|
171
|
+
if (timerId) { clearInterval(timerId); timerId = null; }
|
|
161
172
|
process.stderr.write('\r\x1b[2K');
|
|
162
173
|
},
|
|
163
174
|
};
|
|
@@ -470,15 +481,50 @@ export async function run(args, envOrOpts = process.env, fetcher = globalThis.fe
|
|
|
470
481
|
const cwd = process.cwd();
|
|
471
482
|
|
|
472
483
|
const sErr = createStyler({ isTTY: process.stderr.isTTY });
|
|
484
|
+
|
|
485
|
+
// Validate flags before any git work
|
|
486
|
+
const reviewFlags = args.slice(1);
|
|
487
|
+
const VALID_REVIEW_FLAG = /^(--base=.+|--profile=.+)$/;
|
|
488
|
+
for (const flag of reviewFlags) {
|
|
489
|
+
if (!flag.startsWith('-')) continue;
|
|
490
|
+
if (VALID_REVIEW_FLAG.test(flag)) continue;
|
|
491
|
+
// Detect --profile-NAME typo (dash instead of =)
|
|
492
|
+
const profileDashM = flag.match(/^--profile-(.+)$/);
|
|
493
|
+
if (profileDashM) {
|
|
494
|
+
process.stderr.write(`${sErr.red('✖')} Unknown flag: ${sErr.bold(flag)}\n Did you mean ${sErr.cyan(`--profile=${profileDashM[1]}`)}?\n`);
|
|
495
|
+
process.exitCode = 1;
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
const baseDashM = flag.match(/^--base-(.+)$/);
|
|
499
|
+
if (baseDashM) {
|
|
500
|
+
process.stderr.write(`${sErr.red('✖')} Unknown flag: ${sErr.bold(flag)}\n Did you mean ${sErr.cyan(`--base=${baseDashM[1]}`)}?\n`);
|
|
501
|
+
process.exitCode = 1;
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
process.stderr.write(`${sErr.red('✖')} Unknown flag: ${sErr.bold(flag)}\n Usage: ${sErr.cyan('ticketlens review [--base=BRANCH] [--profile=NAME]')}\n`);
|
|
505
|
+
process.exitCode = 1;
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
|
|
473
509
|
const spinner = makeSpinner(sErr);
|
|
510
|
+
const stderrNotes = [];
|
|
474
511
|
|
|
475
512
|
// Resolve base branch: --base=BRANCH or auto-detect main/master/develop
|
|
476
|
-
const baseArg =
|
|
513
|
+
const baseArg = reviewFlags.find(a => a.startsWith('--base='));
|
|
477
514
|
let baseBranch = baseArg ? baseArg.split('=')[1] : null;
|
|
478
515
|
|
|
479
516
|
spinner.update('Scanning branch…');
|
|
480
517
|
|
|
481
|
-
if (
|
|
518
|
+
if (baseBranch) {
|
|
519
|
+
// Explicit --base: verify the branch exists
|
|
520
|
+
const verifyR = execFn('git', ['rev-parse', '--verify', baseBranch], { encoding: 'utf8', cwd, timeout: 5_000 });
|
|
521
|
+
if (verifyR.status !== 0) {
|
|
522
|
+
spinner.done();
|
|
523
|
+
process.stderr.write(`${sErr.red('✖')} Branch "${baseBranch}" not found in this repository.\n`);
|
|
524
|
+
process.exitCode = 1;
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
} else {
|
|
482
528
|
for (const candidate of ['main', 'master', 'develop']) {
|
|
483
529
|
const r = execFn('git', ['rev-parse', '--verify', candidate], { encoding: 'utf8', cwd, timeout: 5_000 });
|
|
484
530
|
if (r.status === 0) { baseBranch = candidate; break; }
|
|
@@ -489,6 +535,12 @@ export async function run(args, envOrOpts = process.env, fetcher = globalThis.fe
|
|
|
489
535
|
const headResult = execFn('git', ['branch', '--show-current'], { encoding: 'utf8', cwd, timeout: 5_000 });
|
|
490
536
|
const headBranch = headResult.status === 0 ? (headResult.stdout.trim() || null) : null;
|
|
491
537
|
|
|
538
|
+
if (headBranch && headBranch === baseBranch) {
|
|
539
|
+
stderrNotes.push(
|
|
540
|
+
` ${sErr.yellow('⚠')} You are on ${sErr.cyan(headBranch)} — switch to a feature branch for meaningful output.`
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
|
|
492
544
|
const logResult = execFn('git', ['log', '--oneline', `${baseBranch}..HEAD`], { encoding: 'utf8', cwd, timeout: 10_000 });
|
|
493
545
|
const commitMessages = logResult.status === 0 ? (logResult.stdout ?? '') : '';
|
|
494
546
|
|
|
@@ -498,7 +550,7 @@ export async function run(args, envOrOpts = process.env, fetcher = globalThis.fe
|
|
|
498
550
|
const allText = [headBranch ?? '', commitMessages].join('\n');
|
|
499
551
|
const ticketKeys = extractTicketKeys(allText);
|
|
500
552
|
|
|
501
|
-
const profileArgR =
|
|
553
|
+
const profileArgR = reviewFlags.find(a => a.startsWith('--profile='));
|
|
502
554
|
const profileNameR = profileArgR ? profileArgR.split('=')[1] : undefined;
|
|
503
555
|
|
|
504
556
|
// Validate explicit --profile exists before attempting any fetch
|
|
@@ -515,12 +567,12 @@ export async function run(args, envOrOpts = process.env, fetcher = globalThis.fe
|
|
|
515
567
|
}
|
|
516
568
|
}
|
|
517
569
|
|
|
518
|
-
// Fetch tickets (best-effort —
|
|
570
|
+
// Fetch tickets (best-effort) — start animation here since fetch is async
|
|
519
571
|
const tickets = [];
|
|
520
|
-
const stderrNotes = [];
|
|
521
572
|
|
|
522
573
|
if (ticketKeys.length > 0) {
|
|
523
574
|
spinner.update(`Fetching ticket context (${ticketKeys.length} key${ticketKeys.length > 1 ? 's' : ''})…`);
|
|
575
|
+
spinner.startAnim();
|
|
524
576
|
const connR = resolveConnection(ticketKeys[0], {
|
|
525
577
|
env,
|
|
526
578
|
configDir: resolvedConfigDir,
|