@harborppm/cli 0.87.0 → 0.88.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
@@ -5066,6 +5066,22 @@ function isVoyageProject(projectId) {
5066
5066
  function ownProjects(projects) {
5067
5067
  return projects.filter((p) => !isVoyageProject(p.id));
5068
5068
  }
5069
+ /**
5070
+ * The harbor as anyone outside it may see it: First voyage is practice, so
5071
+ * share links, their embeds and the Monday letter to stakeholders leave it
5072
+ * out, with its tasks, risks and spend, from what they show and count.
5073
+ */
5074
+ function withoutPractice(data) {
5075
+ if (!data.projects.some((p) => isVoyageProject(p.id))) return data;
5076
+ const out = {
5077
+ ...data,
5078
+ projects: ownProjects(data.projects),
5079
+ tasks: data.tasks.filter((t) => !isVoyageProject(t.projectId)),
5080
+ raidItems: data.raidItems.filter((r) => !r.projectId || !isVoyageProject(r.projectId))
5081
+ };
5082
+ if (data.spendEntries) out.spendEntries = data.spendEntries.filter((e) => !isVoyageProject(e.projectId));
5083
+ return out;
5084
+ }
5069
5085
  var VOYAGE_PROJECT_ID, STEPS, voyageTaskId, VOYAGE_NO_FORECAST, SEEDED;
5070
5086
  var init_first_voyage = __esmMin((() => {
5071
5087
  VOYAGE_PROJECT_ID = "voyage";
@@ -8205,6 +8221,7 @@ function list$2(names) {
8205
8221
  * document only; nothing here changes it.
8206
8222
  */
8207
8223
  function shippingForecast(data, opts) {
8224
+ data = withoutPractice(data);
8208
8225
  const { now } = opts;
8209
8226
  const today = todayISO(now);
8210
8227
  const programs = data.programs.filter((p) => !opts.portfolioId || p.portfolioId === opts.portfolioId);
@@ -8291,6 +8308,7 @@ var init_shipping_forecast = __esmMin((() => {
8291
8308
  init_date_text();
8292
8309
  init_format();
8293
8310
  init_indexes();
8311
+ init_first_voyage();
8294
8312
  FORECAST_AREAS = 10;
8295
8313
  WORDS = [
8296
8314
  "no",
@@ -10022,7 +10040,9 @@ const KIND_RANK = {
10022
10040
  sounding: 3,
10023
10041
  assigned: 4,
10024
10042
  idle: 5,
10025
- mention: 6
10043
+ mention: 6,
10044
+ reply: 7,
10045
+ finished: 8
10026
10046
  };
10027
10047
  const PING_KIND_LABEL = {
10028
10048
  harbor: "Your harbor",
@@ -10032,7 +10052,9 @@ const PING_KIND_LABEL = {
10032
10052
  sounding: "Soundings",
10033
10053
  assigned: "Assigned to you",
10034
10054
  idle: "Quiet blockers",
10035
- mention: "Mentions"
10055
+ mention: "Mentions",
10056
+ reply: "Replies",
10057
+ finished: "Finished"
10036
10058
  };
10037
10059
  function firstName$3(name) {
10038
10060
  return name.split(/\s+/)[0] ?? name;
@@ -10240,6 +10262,63 @@ function inboxOf(data, personId, now = clockNow()) {
10240
10262
  href: `/projects/${task.projectId}`
10241
10263
  });
10242
10264
  }
10265
+ const recentByTask = /* @__PURE__ */ new Map();
10266
+ for (const c of data.comments) {
10267
+ const list = recentByTask.get(c.taskId);
10268
+ if (list) list.push(c);
10269
+ else recentByTask.set(c.taskId, [c]);
10270
+ }
10271
+ for (const [taskId, thread] of recentByTask) {
10272
+ const task = byId(data.tasks).get(taskId);
10273
+ if (!task) continue;
10274
+ const ordered = [...thread].sort((a, b) => a.createdAt.localeCompare(b.createdAt));
10275
+ let joined = task.assignedBy === personId && task.assigneeId !== personId;
10276
+ const replies = [];
10277
+ for (const c of ordered) {
10278
+ if (c.authorId === personId) {
10279
+ joined = true;
10280
+ replies.length = 0;
10281
+ continue;
10282
+ }
10283
+ if (!joined || c.createdAt < since) continue;
10284
+ if ((c.mentionedIds ?? []).includes(personId)) continue;
10285
+ replies.push(c);
10286
+ }
10287
+ const latest = replies.at(-1);
10288
+ if (!latest) continue;
10289
+ const authors = [...new Set(replies.map((c) => {
10290
+ const who = data.people.find((p) => p.id === c.authorId);
10291
+ return who ? firstName$3(who.name) : "Someone";
10292
+ }))];
10293
+ const project = projectOf(data, task);
10294
+ const snippet = excerpt(latest.body);
10295
+ pings.push({
10296
+ id: `reply:${latest.id}`,
10297
+ kind: "reply",
10298
+ title: `${listNames(authors)} replied`,
10299
+ detail: snippet ? `on “${task.title}”${project ? ` · ${project.name}` : ""} — ${snippet}` : `on “${task.title}”${project ? ` · ${project.name}` : ""}`,
10300
+ at: latest.createdAt,
10301
+ taskId: task.id,
10302
+ personId: latest.authorId,
10303
+ href: `/projects/${task.projectId}`
10304
+ });
10305
+ }
10306
+ for (const task of data.tasks) {
10307
+ if (task.assignedBy !== personId || !task.assigneeId || task.assigneeId === personId) continue;
10308
+ if (task.status !== "done" || !task.completedAt || task.completedAt < since) continue;
10309
+ const who = data.people.find((p) => p.id === task.assigneeId);
10310
+ const project = projectOf(data, task);
10311
+ pings.push({
10312
+ id: `finished:${task.id}:${task.completedAt}`,
10313
+ kind: "finished",
10314
+ title: `${who ? firstName$3(who.name) : "Someone"} finished “${task.title}”`,
10315
+ detail: project ? `A task you gave them · ${project.name}` : "A task you gave them",
10316
+ at: task.completedAt,
10317
+ taskId: task.id,
10318
+ personId: task.assigneeId,
10319
+ href: `/projects/${task.projectId}`
10320
+ });
10321
+ }
10243
10322
  return pings.sort((a, b) => {
10244
10323
  const rank = KIND_RANK[a.kind] - KIND_RANK[b.kind];
10245
10324
  if (rank !== 0) return rank;
package/lib/format.mjs CHANGED
@@ -769,6 +769,12 @@ export function importText(r) {
769
769
  if (r.alreadyThere) lines.push(`${n(r.alreadyThere, "task")} already there, left alone.`);
770
770
  if (r.leftOut) lines.push(`${n(r.leftOut, "row")} left out (canceled, won't do or duplicate).`);
771
771
  if (r.waitsOnLinks) lines.push(`${n(r.waitsOnLinks, "link")} between tasks became "waits on".`);
772
+ if (r.subtasks)
773
+ lines.push(
774
+ r.subtasks === 1
775
+ ? "1 row came in as a subtask of its task."
776
+ : `${r.subtasks} rows came in as subtasks of their tasks.`,
777
+ );
772
778
  for (const p of r.problems ?? [])
773
779
  lines.push(`${p.card ? `Card “${p.card}”` : `Row ${p.row}`}: ${p.message}`);
774
780
  return lines.join("\n");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@harborppm/cli",
3
- "version": "0.87.0",
3
+ "version": "0.88.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",