@formigio/fazemos-cli 0.10.16 → 0.10.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/dist/index.js +119 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5539,14 +5539,57 @@ executions
|
|
|
5539
5539
|
// ── My Work ─────────────────────────────────────────────────
|
|
5540
5540
|
program
|
|
5541
5541
|
.command('my-work')
|
|
5542
|
-
.description('Show pending work in the active project (or all projects with --all-projects)')
|
|
5542
|
+
.description('Show pending work in the active project (or all projects with --all-projects, or across every org with --all-orgs)')
|
|
5543
5543
|
.option('--project <slug>', 'Override active project for this call')
|
|
5544
5544
|
.option('--all-projects', 'Show pending work across every project in the active org', false)
|
|
5545
|
+
.option('-a, --all-orgs', 'Show pending work across every org you are a member of (calls /api/my-work/unified)', false)
|
|
5546
|
+
.option('--json', 'Emit machine-readable JSON', false)
|
|
5545
5547
|
.action(async (opts) => {
|
|
5548
|
+
// ── F19 — cross-Org unified path ───────────────────────────
|
|
5549
|
+
// When --all-orgs is set, we drop X-Org-Id / X-Fazemos-Project-Id
|
|
5550
|
+
// headers and call the unified endpoint. The endpoint is mounted
|
|
5551
|
+
// BEFORE requireOrgMembership and runs across every Org the caller is
|
|
5552
|
+
// an active member of. See rul_cross_org_auth_no_org_middleware.
|
|
5553
|
+
if (opts.allOrgs) {
|
|
5554
|
+
try {
|
|
5555
|
+
const data = await api('GET', '/api/my-work/unified', undefined, { noProjectHeader: true });
|
|
5556
|
+
if (opts.json) {
|
|
5557
|
+
console.log(JSON.stringify(data, null, 2));
|
|
5558
|
+
return;
|
|
5559
|
+
}
|
|
5560
|
+
// Sage edge case #24: single-Org user with --all-orgs gets a soft
|
|
5561
|
+
// notice + their normal output (no flag-stripping, no error).
|
|
5562
|
+
if (data.meta.orgCount === 1) {
|
|
5563
|
+
console.log(chalk.yellow("--all-orgs has no effect — you're a member of 1 org. Showing your current scope.\n"));
|
|
5564
|
+
}
|
|
5565
|
+
renderUnifiedMyWork(data);
|
|
5566
|
+
return;
|
|
5567
|
+
}
|
|
5568
|
+
catch (err) {
|
|
5569
|
+
// Defensive: 403 NO_ACTIVE_MEMBERSHIP gets a friendly nudge.
|
|
5570
|
+
if (err instanceof ApiError && err.code === 'NO_ACTIVE_MEMBERSHIP') {
|
|
5571
|
+
console.error(chalk.red('No active org memberships.'));
|
|
5572
|
+
console.error(chalk.gray('Ask an admin to add you, or accept a pending invite.'));
|
|
5573
|
+
process.exit(1);
|
|
5574
|
+
}
|
|
5575
|
+
if (err instanceof Error) {
|
|
5576
|
+
console.error(chalk.red(err.message));
|
|
5577
|
+
}
|
|
5578
|
+
else {
|
|
5579
|
+
console.error(chalk.red(String(err)));
|
|
5580
|
+
}
|
|
5581
|
+
process.exit(1);
|
|
5582
|
+
}
|
|
5583
|
+
}
|
|
5584
|
+
// ── Default project-scoped path (unchanged) ─────────────────
|
|
5546
5585
|
try {
|
|
5547
5586
|
const data = await api('GET', '/api/my-work', undefined, projectOpts(opts));
|
|
5548
5587
|
const c = data.commitments;
|
|
5549
5588
|
const total = c.overdue.length + c.due_today.length + c.due_this_week.length + c.upcoming.length;
|
|
5589
|
+
if (opts.json) {
|
|
5590
|
+
console.log(JSON.stringify(data, null, 2));
|
|
5591
|
+
return;
|
|
5592
|
+
}
|
|
5550
5593
|
console.log(chalk.cyan(`Commitments: ${total}`));
|
|
5551
5594
|
if (c.overdue.length)
|
|
5552
5595
|
console.log(chalk.red(` Overdue: ${c.overdue.length}`));
|
|
@@ -5564,6 +5607,81 @@ program
|
|
|
5564
5607
|
handleScopedError(err);
|
|
5565
5608
|
}
|
|
5566
5609
|
});
|
|
5610
|
+
function renderUnifiedMyWork(data) {
|
|
5611
|
+
if (data.items.length === 0) {
|
|
5612
|
+
console.log(chalk.green("Looking good — across every project, in every org, you're caught up."));
|
|
5613
|
+
return;
|
|
5614
|
+
}
|
|
5615
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
5616
|
+
const truncate = (s, n) => s.length > n ? s.slice(0, n - 1) + '…' : s;
|
|
5617
|
+
// Header
|
|
5618
|
+
const header = [
|
|
5619
|
+
pad('STATUS', 10),
|
|
5620
|
+
pad('TYPE', 9),
|
|
5621
|
+
pad('TITLE', 36),
|
|
5622
|
+
pad('ORG', 5),
|
|
5623
|
+
pad('PROJ', 5),
|
|
5624
|
+
'DUE',
|
|
5625
|
+
].join(' ');
|
|
5626
|
+
console.log(chalk.gray(header));
|
|
5627
|
+
console.log(chalk.gray('-'.repeat(80)));
|
|
5628
|
+
for (const item of data.items) {
|
|
5629
|
+
// Status color rules per manifest.cli.changes.
|
|
5630
|
+
let statusLabel;
|
|
5631
|
+
if (item.dueDate && item.dueDate < today) {
|
|
5632
|
+
statusLabel = chalk.red('overdue');
|
|
5633
|
+
}
|
|
5634
|
+
else if (item.dueDate === today) {
|
|
5635
|
+
statusLabel = chalk.yellow('today');
|
|
5636
|
+
}
|
|
5637
|
+
else {
|
|
5638
|
+
statusLabel = item.status;
|
|
5639
|
+
}
|
|
5640
|
+
const row = [
|
|
5641
|
+
pad(statusLabel, 10),
|
|
5642
|
+
pad(item.type, 9),
|
|
5643
|
+
pad(truncate(item.title, 36), 36),
|
|
5644
|
+
pad(`[${computeAbbreviation(item.org.name)}]`, 5),
|
|
5645
|
+
pad(`[${computeAbbreviation(item.project.name)}]`, 5),
|
|
5646
|
+
item.dueDate ?? '—',
|
|
5647
|
+
].join(' ');
|
|
5648
|
+
console.log(row);
|
|
5649
|
+
}
|
|
5650
|
+
// Footer
|
|
5651
|
+
const shown = data.items.length;
|
|
5652
|
+
const total = data.meta.typeCounts.commitment +
|
|
5653
|
+
data.meta.typeCounts.action +
|
|
5654
|
+
data.meta.typeCounts.worksheet +
|
|
5655
|
+
data.meta.typeCounts.check_in +
|
|
5656
|
+
data.meta.typeCounts.pipeline_step;
|
|
5657
|
+
console.log('');
|
|
5658
|
+
console.log(chalk.gray(`Showing ${shown} of ${total}.` +
|
|
5659
|
+
(data.nextCursor ? ' More available — re-run with --cursor=<next>.' : '')));
|
|
5660
|
+
console.log(chalk.gray(`Tip: fazemos my-work (no flag) shows just the active project.`));
|
|
5661
|
+
}
|
|
5662
|
+
/**
|
|
5663
|
+
* 2-letter abbreviation: first letter of first word + first letter of next
|
|
5664
|
+
* word, or first 2 letters of the only word if no second word exists.
|
|
5665
|
+
* Duplicates the helper used in fazemos-web (ProjectBadge.tsx) per
|
|
5666
|
+
* manifest.cli.shared_helpers — "don't block on packaging."
|
|
5667
|
+
*/
|
|
5668
|
+
function computeAbbreviation(name) {
|
|
5669
|
+
const cleaned = name.trim();
|
|
5670
|
+
if (!cleaned)
|
|
5671
|
+
return '??';
|
|
5672
|
+
const words = cleaned.split(/\s+/);
|
|
5673
|
+
if (words.length === 1) {
|
|
5674
|
+
return words[0].slice(0, 2).toUpperCase();
|
|
5675
|
+
}
|
|
5676
|
+
return (words[0][0] + words[1][0]).toUpperCase();
|
|
5677
|
+
}
|
|
5678
|
+
function pad(s, width) {
|
|
5679
|
+
// Strip ANSI escape codes for length measurement so colored cells line up.
|
|
5680
|
+
const visible = s.replace(/\u001b\[[0-9;]*m/g, '');
|
|
5681
|
+
if (visible.length >= width)
|
|
5682
|
+
return s;
|
|
5683
|
+
return s + ' '.repeat(width - visible.length);
|
|
5684
|
+
}
|
|
5567
5685
|
// ── Test ────────────────────────────────────────────────────
|
|
5568
5686
|
program
|
|
5569
5687
|
.command('test')
|