ticketlens 0.1.16 → 0.1.18
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,56 @@ 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=.+|--branch=.+|--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
|
+
const branchDashM = flag.match(/^--branch-(.+)$/);
|
|
505
|
+
if (branchDashM) {
|
|
506
|
+
process.stderr.write(`${sErr.red('✖')} Unknown flag: ${sErr.bold(flag)}\n Did you mean ${sErr.cyan(`--branch=${branchDashM[1]}`)}?\n`);
|
|
507
|
+
process.exitCode = 1;
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
process.stderr.write(`${sErr.red('✖')} Unknown flag: ${sErr.bold(flag)}\n Usage: ${sErr.cyan('ticketlens review [--base=BRANCH] [--branch=BRANCH] [--profile=NAME]')}\n`);
|
|
511
|
+
process.exitCode = 1;
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
|
|
473
515
|
const spinner = makeSpinner(sErr);
|
|
516
|
+
const stderrNotes = [];
|
|
474
517
|
|
|
475
518
|
// Resolve base branch: --base=BRANCH or auto-detect main/master/develop
|
|
476
|
-
const baseArg =
|
|
477
|
-
let baseBranch = baseArg ? baseArg.
|
|
519
|
+
const baseArg = reviewFlags.find(a => a.startsWith('--base=') || a.startsWith('--branch='));
|
|
520
|
+
let baseBranch = baseArg ? baseArg.replace(/^--(base|branch)=/, '') : null;
|
|
478
521
|
|
|
479
522
|
spinner.update('Scanning branch…');
|
|
480
523
|
|
|
481
|
-
if (
|
|
524
|
+
if (baseBranch) {
|
|
525
|
+
// Explicit --base: verify the branch exists
|
|
526
|
+
const verifyR = execFn('git', ['rev-parse', '--verify', baseBranch], { encoding: 'utf8', cwd, timeout: 5_000 });
|
|
527
|
+
if (verifyR.status !== 0) {
|
|
528
|
+
spinner.done();
|
|
529
|
+
process.stderr.write(`${sErr.red('✖')} Branch "${baseBranch}" not found in this repository.\n`);
|
|
530
|
+
process.exitCode = 1;
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
} else {
|
|
482
534
|
for (const candidate of ['main', 'master', 'develop']) {
|
|
483
535
|
const r = execFn('git', ['rev-parse', '--verify', candidate], { encoding: 'utf8', cwd, timeout: 5_000 });
|
|
484
536
|
if (r.status === 0) { baseBranch = candidate; break; }
|
|
@@ -489,6 +541,12 @@ export async function run(args, envOrOpts = process.env, fetcher = globalThis.fe
|
|
|
489
541
|
const headResult = execFn('git', ['branch', '--show-current'], { encoding: 'utf8', cwd, timeout: 5_000 });
|
|
490
542
|
const headBranch = headResult.status === 0 ? (headResult.stdout.trim() || null) : null;
|
|
491
543
|
|
|
544
|
+
if (headBranch && headBranch === baseBranch) {
|
|
545
|
+
stderrNotes.push(
|
|
546
|
+
` ${sErr.yellow('⚠')} You are on ${sErr.cyan(headBranch)} — switch to a feature branch for meaningful output.`
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
|
|
492
550
|
const logResult = execFn('git', ['log', '--oneline', `${baseBranch}..HEAD`], { encoding: 'utf8', cwd, timeout: 10_000 });
|
|
493
551
|
const commitMessages = logResult.status === 0 ? (logResult.stdout ?? '') : '';
|
|
494
552
|
|
|
@@ -498,7 +556,7 @@ export async function run(args, envOrOpts = process.env, fetcher = globalThis.fe
|
|
|
498
556
|
const allText = [headBranch ?? '', commitMessages].join('\n');
|
|
499
557
|
const ticketKeys = extractTicketKeys(allText);
|
|
500
558
|
|
|
501
|
-
const profileArgR =
|
|
559
|
+
const profileArgR = reviewFlags.find(a => a.startsWith('--profile='));
|
|
502
560
|
const profileNameR = profileArgR ? profileArgR.split('=')[1] : undefined;
|
|
503
561
|
|
|
504
562
|
// Validate explicit --profile exists before attempting any fetch
|
|
@@ -515,12 +573,12 @@ export async function run(args, envOrOpts = process.env, fetcher = globalThis.fe
|
|
|
515
573
|
}
|
|
516
574
|
}
|
|
517
575
|
|
|
518
|
-
// Fetch tickets (best-effort —
|
|
576
|
+
// Fetch tickets (best-effort) — start animation here since fetch is async
|
|
519
577
|
const tickets = [];
|
|
520
|
-
const stderrNotes = [];
|
|
521
578
|
|
|
522
579
|
if (ticketKeys.length > 0) {
|
|
523
580
|
spinner.update(`Fetching ticket context (${ticketKeys.length} key${ticketKeys.length > 1 ? 's' : ''})…`);
|
|
581
|
+
spinner.startAnim();
|
|
524
582
|
const connR = resolveConnection(ticketKeys[0], {
|
|
525
583
|
env,
|
|
526
584
|
configDir: resolvedConfigDir,
|
|
@@ -552,6 +610,7 @@ export async function run(args, envOrOpts = process.env, fetcher = globalThis.fe
|
|
|
552
610
|
|
|
553
611
|
spinner.done();
|
|
554
612
|
for (const note of stderrNotes) process.stderr.write(note + '\n');
|
|
613
|
+
if (stderrNotes.length > 0) process.stderr.write('\n');
|
|
555
614
|
|
|
556
615
|
const isLic = opts.isLicensedFn ?? ((tier) => isLicensed(tier, resolvedConfigDir));
|
|
557
616
|
const assembleFn = opts.assemblePrReviewFn ?? assemblePrReview;
|
|
@@ -38,7 +38,7 @@ export function printHelp({ stream = process.stdout } = {}) {
|
|
|
38
38
|
` ${s.brand('ticketlens')} ${s.dim('<TICKET-KEY>')} ${s.dim('[options]')} Fetch a ticket brief`,
|
|
39
39
|
` ${s.brand('ticketlens')} get ${s.dim('<TICKET-KEY>')} Same as above ${s.dim('(explicit alias)')}`,
|
|
40
40
|
` ${s.brand('ticketlens')} triage ${s.dim('[options]')} Scan your assigned tickets`,
|
|
41
|
-
` ${s.brand('ticketlens')} review ${s.dim('[--
|
|
41
|
+
` ${s.brand('ticketlens')} review ${s.dim('[--branch=BRANCH]')} Code-review context brief from current branch`,
|
|
42
42
|
` ${s.brand('ticketlens')} compliance ${s.dim('<TICKET-KEY>')} Check requirements coverage ${s.dim('[Pro/Free 3/mo]')}`,
|
|
43
43
|
'',
|
|
44
44
|
` ${s.brand('ticketlens')} delete ${s.dim('<PROFILE-NAME>')} Remove a profile`,
|
|
@@ -545,7 +545,7 @@ export function printReviewHelp({ stream = process.stdout } = {}) {
|
|
|
545
545
|
const s = createStyler({ isTTY: stream.isTTY });
|
|
546
546
|
const lines = [
|
|
547
547
|
'',
|
|
548
|
-
` ${s.bold(s.brand('ticketlens'))} ${s.bold('review')} ${s.dim('[--
|
|
548
|
+
` ${s.bold(s.brand('ticketlens'))} ${s.bold('review')} ${s.dim('[--branch=BRANCH] [--profile=NAME]')}`,
|
|
549
549
|
'',
|
|
550
550
|
` Assemble a code-review context brief from your current branch.`,
|
|
551
551
|
` Extracts linked ticket keys from the branch name and commit messages,`,
|
|
@@ -555,16 +555,17 @@ export function printReviewHelp({ stream = process.stdout } = {}) {
|
|
|
555
555
|
'',
|
|
556
556
|
` ${s.bold('OPTIONS')}`,
|
|
557
557
|
'',
|
|
558
|
-
` ${s.brand('--
|
|
558
|
+
` ${s.brand('--branch')}=${s.dim('BRANCH')} Compare against this branch ${s.dim('(default: auto-detect main/master/develop)')}`,
|
|
559
|
+
` ${s.brand('--base')}=${s.dim('BRANCH')} Alias for ${s.dim('--branch')}`,
|
|
559
560
|
` ${s.brand('--profile')}=${s.dim('NAME')} Use a specific tracker profile for ticket fetching`,
|
|
560
561
|
` ${s.brand('-h')}, ${s.brand('--help')} Show this help`,
|
|
561
562
|
'',
|
|
562
563
|
` ${s.bold('EXAMPLES')}`,
|
|
563
564
|
'',
|
|
564
565
|
` ${s.dim('$')} ticketlens review`,
|
|
565
|
-
` ${s.dim('$')} ticketlens review --
|
|
566
|
-
` ${s.dim('$')} ticketlens review --
|
|
567
|
-
` ${s.dim('$')} ticketlens review --
|
|
566
|
+
` ${s.dim('$')} ticketlens review --branch=main`,
|
|
567
|
+
` ${s.dim('$')} ticketlens review --branch=main --profile=myteam`,
|
|
568
|
+
` ${s.dim('$')} ticketlens review --branch=main | pbcopy ${s.dim('# copy brief to clipboard')}`,
|
|
568
569
|
'',
|
|
569
570
|
` ${s.bold('OUTPUT SECTIONS')}`,
|
|
570
571
|
'',
|