@harborppm/cli 0.30.0 → 0.31.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/lib/cli.mjs CHANGED
@@ -22,6 +22,7 @@ import {
22
22
  STATUS,
23
23
  harbors,
24
24
  limitsText,
25
+ hillData,
25
26
  hillList,
26
27
  myWork,
27
28
  premortemText,
@@ -899,7 +900,12 @@ export async function run(argv, io = {}) {
899
900
  if (!project) {
900
901
  if (values.at !== undefined)
901
902
  throw new HarborError("Say which project: harbor hill PROJECT --at N.");
902
- return call("list_projects", { harbor, program: values.program, limit: 100 }, hillList);
903
+ return call(
904
+ "list_projects",
905
+ { harbor, program: values.program, limit: 100 },
906
+ hillList,
907
+ (r) => hillData(r, values.program),
908
+ );
903
909
  }
904
910
  if (values.at === undefined) {
905
911
  throw new HarborError("Say where with --at: 0 to 100, or start, top, doing or done.");
@@ -1299,7 +1305,7 @@ export async function run(argv, io = {}) {
1299
1305
  if (!project) throw new HarborError("Give a project, like harbor limit RR doing 5.");
1300
1306
  if (!column) {
1301
1307
  return call("get_project", { harbor, project }, (r) =>
1302
- limitsText(r.name, r.limits, r.tasks),
1308
+ limitsText(r.name, r.limits, r.tasks, r.hiddenColumns),
1303
1309
  );
1304
1310
  }
1305
1311
  if (value === undefined || extra.length) {
@@ -1310,7 +1316,7 @@ export async function run(argv, io = {}) {
1310
1316
  throw new HarborError("A limit is a whole number from 1 to 20, or none.");
1311
1317
  }
1312
1318
  return call("set_column_limit", { harbor, project, column, limit }, (r) =>
1313
- limitsText(r.set.project, r.limits, r.tasks),
1319
+ limitsText(r.set.project, r.limits, r.tasks, r.hiddenColumns),
1314
1320
  );
1315
1321
  }
1316
1322
  case "voyage": {
package/lib/format.mjs CHANGED
@@ -144,16 +144,26 @@ export function projectList(r, { done = false } = {}) {
144
144
  );
145
145
  }
146
146
 
147
- /** A project's column limits with how full each is: "doing 4 of 5; no limit on backlog, todo, done". */
148
- export function limitsText(name, limits, tasks = {}) {
147
+ /**
148
+ * A project's column limits with how full each is: "doing 4 of 5; no limit on
149
+ * backlog, todo, done". Columns the board hides are named after, with any cap
150
+ * they keep for when they're shown again.
151
+ */
152
+ export function limitsText(name, limits, tasks = {}, hidden = []) {
153
+ const label = (status) => STATUS[status] ?? status;
149
154
  const entries = Object.entries(limits ?? {});
150
- const capped = entries
155
+ const shown = entries.filter(([status]) => !hidden.includes(status));
156
+ const capped = shown
151
157
  .filter(([, n]) => n != null)
152
- .map(([status, n]) => `${STATUS[status] ?? status} ${tasks[status] ?? 0} of ${n}`);
153
- const open = entries.filter(([, n]) => n == null).map(([status]) => STATUS[status] ?? status);
154
- if (!capped.length) return `${name}'s board has no column limits.`;
158
+ .map(([status, n]) => `${label(status)} ${tasks[status] ?? 0} of ${n}`);
159
+ const open = shown.filter(([, n]) => n == null).map(([status]) => label(status));
160
+ const off = entries
161
+ .filter(([status]) => hidden.includes(status))
162
+ .map(([status, n]) => (n == null ? label(status) : `${label(status)} (limit ${n})`));
163
+ const offNote = off.length ? ` Hidden on the board: ${off.join(", ")}.` : "";
164
+ if (!capped.length) return `${name}'s board has no column limits.${offNote}`;
155
165
  const rest = open.length ? `; no limit on ${open.join(", ")}` : "";
156
- return `${name}: ${capped.join(", ")}${rest}.`;
166
+ return `${name}: ${capped.join(", ")}${rest}.${offNote}`;
157
167
  }
158
168
 
159
169
  export function harbors(r) {
@@ -295,10 +305,43 @@ function hillTrack(at) {
295
305
  return cells.join("");
296
306
  }
297
307
 
308
+ /**
309
+ * What harbor hill shows, for the chart and --json alike: open projects with
310
+ * their place on the hill (null when not on it), how many Done ones were left
311
+ * out, and the program asked for.
312
+ */
313
+ export function hillData(r, program) {
314
+ const all = r.projects ?? [];
315
+ const open = all.filter((p) => p.health !== "done");
316
+ // The program's own name when every project is in it, else what was typed.
317
+ const named =
318
+ all.length && all.every((p) => p.program === all[0].program) ? all[0].program : null;
319
+ return {
320
+ ...(program ? { program: named ?? program } : {}),
321
+ projects: open.map((p) => ({
322
+ id: p.id,
323
+ name: p.name,
324
+ ...(p.key ? { key: p.key } : {}),
325
+ program: p.program ?? null,
326
+ health: p.health,
327
+ hill: p.hill ?? null,
328
+ })),
329
+ done: all.length - open.length,
330
+ };
331
+ }
332
+
298
333
  /** Each open project's place on its program's hill, by program, unplaced ones named after. */
299
334
  export function hillList(r) {
300
335
  const projects = (r.projects ?? []).filter((p) => p.health !== "done");
301
- if (!projects.length) return "No open projects match.";
336
+ if (!projects.length) {
337
+ if (r.done) {
338
+ const where = r.program ? `in ${r.program} ` : "";
339
+ return r.done === 1
340
+ ? `The one project ${where}is Done, so nothing is on the hill.`
341
+ : `All ${r.done} projects ${where}are Done, so nothing is on the hill.`;
342
+ }
343
+ return "No open projects match.";
344
+ }
302
345
  const byProgram = new Map();
303
346
  for (const p of projects) {
304
347
  const key = p.program ?? "No program";
@@ -317,6 +360,11 @@ export function hillList(r) {
317
360
  lines.push(` ${name} ${hillTrack(p.hill.at)} ${String(p.hill.at).padStart(3)}${still}`);
318
361
  }
319
362
  if (unplaced.length) lines.push(` Not on the hill: ${unplaced.map((p) => p.name).join(", ")}`);
363
+ if (!placed.length) {
364
+ lines.push(
365
+ ` Place one with harbor hill "${unplaced[0].name}" --at 30 (0 to 100, top is 50).`,
366
+ );
367
+ }
320
368
  blocks.push(lines.join("\n"));
321
369
  }
322
370
  return blocks.join("\n\n");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@harborppm/cli",
3
- "version": "0.30.0",
3
+ "version": "0.31.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",