@harborppm/cli 0.78.0 → 0.79.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/dist/app.mjs CHANGED
@@ -9834,6 +9834,7 @@ init_work();
9834
9834
  init_clock();
9835
9835
  init_soundings();
9836
9836
  const KIND_RANK = {
9837
+ harbor: -1,
9837
9838
  waiting: 0,
9838
9839
  approval: 1,
9839
9840
  review: 2,
@@ -9843,6 +9844,7 @@ const KIND_RANK = {
9843
9844
  mention: 6
9844
9845
  };
9845
9846
  const PING_KIND_LABEL = {
9847
+ harbor: "Your harbor",
9846
9848
  waiting: "Waiting on you",
9847
9849
  approval: "Sign-offs",
9848
9850
  review: "Reviews due",
@@ -9882,6 +9884,19 @@ function inboxOf(data, personId, now = clockNow()) {
9882
9884
  const pings = [];
9883
9885
  const mine = data.tasks.filter((t) => t.assigneeId === personId && t.status !== "done");
9884
9886
  const since = subDays(now, ASSIGNED_DAYS).toISOString();
9887
+ const me = data.people.find((p) => p.id === personId);
9888
+ if (me?.harborHandedAt && me.harborHandedAt >= since && me.harborHandedById !== personId) {
9889
+ const by = data.people.find((p) => p.id === me.harborHandedById);
9890
+ pings.push({
9891
+ id: `harbor:${me.harborHandedAt}`,
9892
+ kind: "harbor",
9893
+ title: `${by ? firstName$3(by.name) : "The owner"} handed this harbor to you`,
9894
+ detail: "You own it now: invites, roles, billing, webhooks and the whole-harbor download. Settings has them all.",
9895
+ at: me.harborHandedAt,
9896
+ personId: by?.id ?? null,
9897
+ href: "/settings"
9898
+ });
9899
+ }
9885
9900
  for (const task of mine) {
9886
9901
  if (!task.assignedBy || task.assignedBy === personId || !task.assignedAt) continue;
9887
9902
  if (task.assignedAt < since) continue;
package/lib/cli.mjs CHANGED
@@ -557,17 +557,30 @@ async function readTitles(client, harbor, titles, values, today = todayLocal())
557
557
  });
558
558
  }
559
559
 
560
- /** What a title's words said, in a line: "Quinn (@Quinn), due 2026-11-06 (by Friday)". */
560
+ /** "Fri 2026-11-13": a day with its weekday, so "next friday" shows which Friday it became. */
561
+ export function dayWithWeekday(iso) {
562
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(iso)) return iso;
563
+ const day = WEEKDAYS[new Date(`${iso}T12:00:00`).getDay()];
564
+ return day ? `${day.charAt(0).toUpperCase()}${day.slice(1, 3)} ${iso}` : iso;
565
+ }
566
+
567
+ /** What a title's words said, in a line: "Quinn (@Quinn), due Fri 2026-11-06 (by Friday)". */
561
568
  function wordsLine(r) {
562
569
  const bits = [
563
570
  r.owner && `${r.owner.value} (${r.owner.text})`,
564
- r.due && `due ${r.due.value} (${r.due.text})`,
571
+ r.due && `due ${dayWithWeekday(r.due.value)} (${r.due.text})`,
565
572
  r.priority && `${r.priority.value} priority (${r.priority.text})`,
566
573
  r.project && `in ${r.project.value} (${r.project.text})`,
567
574
  ].filter(Boolean);
568
575
  return bits.length ? `Read from your words: ${bits.join(", ")}.` : null;
569
576
  }
570
577
 
578
+ /** --due given in words rather than as a date or +N, when it reads as a day. */
579
+ function dueWords(v) {
580
+ if (typeof v !== "string" || /^(\d{4}-\d{2}-\d{2}|\+\d{1,3})$/.test(v) || PILOT.has(v.toLowerCase())) return false;
581
+ return /^\d{4}-\d{2}-\d{2}$/.test(dueDate(v));
582
+ }
583
+
571
584
  /** --due pilot: the date the owner's queue suggests, set by Harbor. */
572
585
  const PILOT = new Set(["pilot", "suggested"]);
573
586
  const duePilot = (v) => (v !== undefined && PILOT.has(v.toLowerCase()) ? true : undefined);
@@ -2537,7 +2550,7 @@ export async function run(argv, io = {}) {
2537
2550
  [
2538
2551
  `Would add ${n} to ${where.join(" and ")}:`,
2539
2552
  ...reads.flatMap((r, i) => [
2540
- ` ${r.title}${pilot ? ` (due ${pilot[i].earliest})` : values.due ? ` (due ${dueDate(values.due)})` : ""}`,
2553
+ ` ${r.title}${pilot ? ` (due ${pilot[i].earliest})` : values.due ? ` (due ${dueWords(values.due) ? dayWithWeekday(dueDate(values.due)) : dueDate(values.due)})` : ""}`,
2541
2554
  ...(wordsLine(r) ? [` ${wordsLine(r)}`] : []),
2542
2555
  ]),
2543
2556
  ...(pilot
@@ -2598,6 +2611,8 @@ export async function run(argv, io = {}) {
2598
2611
  `Added: ${taskLine(r.created, opts)}${r.comment ? ", with your comment" : ""}`,
2599
2612
  // What the title's words said, so nothing is read without being seen.
2600
2613
  ...(r.read ? [`${r.read} --literal keeps the words in the title.`] : []),
2614
+ // --due in words ("next friday"): the day it became, weekday and all.
2615
+ ...(dueWords(values.due) ? [`--due ${values.due}: ${dayWithWeekday(dueDate(values.due))}.`] : []),
2601
2616
  // Pilot speaks up only when a date was given that the queue says won't hold.
2602
2617
  ...(values.due ? pilotLines(r, r.created) : []),
2603
2618
  ].join("\n"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@harborppm/cli",
3
- "version": "0.78.0",
3
+ "version": "0.79.0",
4
4
  "description": "Harbor from the terminal: a full-screen app with everything, plus commands for scripts and agents.",
5
5
  "keywords": [
6
6
  "harbor",