bamboohr-cli 1.0.21 → 1.0.22-g942e3c7.1

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.
Files changed (2) hide show
  1. package/dist/index.js +111 -63
  2. package/package.json +5 -4
package/dist/index.js CHANGED
@@ -60,33 +60,77 @@ function readPackageVersion(importMetaUrl) {
60
60
 
61
61
  // ../../cli-utils/dist/validators.js
62
62
  import { InvalidArgumentError } from "commander";
63
+ function nonNegativeInt(raw) {
64
+ const n = parseInt(raw, 10);
65
+ if (Number.isNaN(n) || n < 0) {
66
+ throw new InvalidArgumentError("Must be a non-negative integer.");
67
+ }
68
+ return n;
69
+ }
63
70
  function positiveInt(raw) {
64
71
  const n = parseInt(raw, 10);
65
72
  if (Number.isNaN(n) || n < 1) {
66
- throw new InvalidArgumentError(`Must be a positive integer (got "${raw}")`);
73
+ throw new InvalidArgumentError("Must be a positive integer.");
67
74
  }
68
75
  return n;
69
76
  }
77
+ function text(raw) {
78
+ return raw;
79
+ }
80
+ function date(raw) {
81
+ const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw);
82
+ if (!m) {
83
+ throw new InvalidArgumentError("Must be a date in YYYY-MM-DD format.");
84
+ }
85
+ const [, y, mo, d] = m;
86
+ const year = Number(y);
87
+ const month = Number(mo);
88
+ const day = Number(d);
89
+ const dt = new Date(Date.UTC(year, month - 1, day));
90
+ if (dt.getUTCFullYear() !== year || dt.getUTCMonth() !== month - 1 || dt.getUTCDate() !== day) {
91
+ throw new InvalidArgumentError("Not a real calendar date.");
92
+ }
93
+ return raw;
94
+ }
95
+
96
+ // ../../cli-utils/dist/json.js
97
+ import { InvalidArgumentError as InvalidArgumentError2 } from "commander";
98
+ function jsonShape(schema) {
99
+ return (raw) => {
100
+ let parsed;
101
+ try {
102
+ parsed = JSON.parse(raw);
103
+ } catch {
104
+ throw new InvalidArgumentError2("Must be valid JSON.");
105
+ }
106
+ const result = schema.safeParse(parsed);
107
+ if (!result.success) {
108
+ const issues = result.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
109
+ throw new InvalidArgumentError2(`Invalid JSON shape \u2014 ${issues}`);
110
+ }
111
+ return result.data;
112
+ };
113
+ }
70
114
 
71
115
  // ../../cli-utils/dist/text-or-file.js
72
116
  import { readFileSync as readFileSync3 } from "fs";
73
- import { InvalidArgumentError as InvalidArgumentError2, Option } from "commander";
117
+ import { InvalidArgumentError as InvalidArgumentError3, Option } from "commander";
74
118
  var STDIN_REF = "-";
75
119
  function rejectStdinSentinel(value) {
76
120
  if (value === "@-" || value === STDIN_REF) {
77
- throw new InvalidArgumentError2(`"${value}" looks like a stdin redirect, which is not supported here. Pass the text directly, or read it from a file/stdin with the matching --\u2026-file <path|-> option.`);
121
+ throw new InvalidArgumentError3(`"${value}" looks like a stdin redirect, which is not supported here. Pass the text directly, or read it from a file/stdin with the matching --\u2026-file <path|-> option.`);
78
122
  }
79
123
  return value;
80
124
  }
81
125
  function readFileOrStdin(ref) {
82
126
  if (ref === STDIN_REF) {
83
127
  if (process.stdin.isTTY) {
84
- throw new InvalidArgumentError2('"--\u2026-file -" reads stdin, but stdin is a terminal (nothing piped).');
128
+ throw new InvalidArgumentError3('"--\u2026-file -" reads stdin, but stdin is a terminal (nothing piped).');
85
129
  }
86
130
  try {
87
131
  return readFileSync3(0, "utf8");
88
132
  } catch (err) {
89
- throw new InvalidArgumentError2(`failed to read stdin for "--\u2026-file -": ${err.message}`);
133
+ throw new InvalidArgumentError3(`failed to read stdin for "--\u2026-file -": ${err.message}`);
90
134
  }
91
135
  }
92
136
  try {
@@ -94,28 +138,29 @@ function readFileOrStdin(ref) {
94
138
  } catch (err) {
95
139
  const e = err;
96
140
  if (e.code === "ENOENT") {
97
- throw new InvalidArgumentError2(`file not found: "${ref}".`);
141
+ throw new InvalidArgumentError3(`file not found: "${ref}".`);
98
142
  }
99
- throw new InvalidArgumentError2(`failed to read file "${ref}": ${e.message}`);
143
+ throw new InvalidArgumentError3(`failed to read file "${ref}": ${e.message}`);
100
144
  }
101
145
  }
102
146
  function textOrFileOption(cmd, name, opts = {}) {
103
147
  const label = `${name.charAt(0).toUpperCase()}${name.slice(1)} content`;
104
148
  cmd.option(`--${name} <text>`, opts.description ?? label, rejectStdinSentinel);
105
- cmd.addOption(new Option(`--${name}-file <path>`, `Read --${name} from a file, or "-" for stdin (mutually exclusive with --${name})`).conflicts(name));
149
+ cmd.addOption(new Option(`--${name}-file <path>`, `Read --${name} from a file, or "-" for stdin (mutually exclusive with --${name})`).conflicts(name).argParser(text));
106
150
  return cmd;
107
151
  }
108
152
  function resolveTextOrFile(opts, name, { required = true } = {}) {
109
- const literal = opts[name];
110
- const ref = opts[`${name}File`];
153
+ const record = opts;
154
+ const literal = record[name];
155
+ const ref = record[`${name}File`];
111
156
  if (literal !== void 0 && ref !== void 0) {
112
- throw new InvalidArgumentError2(`--${name} and --${name}-file are mutually exclusive; provide only one.`);
157
+ throw new InvalidArgumentError3(`--${name} and --${name}-file are mutually exclusive; provide only one.`);
113
158
  }
114
159
  if (ref !== void 0) {
115
160
  return readFileOrStdin(ref);
116
161
  }
117
162
  if (literal === void 0 && required) {
118
- throw new InvalidArgumentError2(`a ${name} is required: provide --${name} <text> or --${name}-file <path>.`);
163
+ throw new InvalidArgumentError3(`a ${name} is required: provide --${name} <text> or --${name}-file <path>.`);
119
164
  }
120
165
  return literal;
121
166
  }
@@ -518,7 +563,7 @@ function directory(parent) {
518
563
 
519
564
  // src/commands/employee/get.ts
520
565
  function get(parent) {
521
- const cmd = parent.command("get [employeeId]").description("Get employee data by ID (default: current user)").option("--fields <fields>", "Comma-separated field names", "firstName,lastName,email,jobTitle").option("--include-future", "Include future-dated values from history fields").option("--with-photos", "Include the employee's signed photo URL and photoUploaded flag");
566
+ const cmd = parent.command("get").description("Get employee data by ID (default: current user)").argument("[employeeId]", "Employee ID (0 or omitted = current user)", nonNegativeInt).option("--fields <fields>", "Comma-separated field names", text, "firstName,lastName,email,jobTitle").option("--include-future", "Include future-dated values from history fields").option("--with-photos", "Include the employee's signed photo URL and photoUploaded flag");
522
567
  examples(cmd, [
523
568
  "",
524
569
  "123",
@@ -531,7 +576,7 @@ function get(parent) {
531
576
  const client = getClient();
532
577
  const fields2 = opts.withPhotos ? mergeFields(opts.fields, ["photoUrl", "photoUploaded"]) : opts.fields;
533
578
  const result = await client.employees.get({
534
- id: employeeId ?? "0",
579
+ id: employeeId !== void 0 ? String(employeeId) : "0",
535
580
  fields: fields2,
536
581
  onlyCurrent: !opts.includeFuture
537
582
  });
@@ -551,12 +596,12 @@ function mergeFields(existing, extra) {
551
596
  import { Option as Option2 } from "commander";
552
597
  var GOAL_FILTERS = ["open", "closed", "all"];
553
598
  function goals(parent) {
554
- const cmd = parent.command("goals <employeeId>").description("Get employee performance goals").addOption(new Option2("--filter <filter>", "Filter goals by status").choices(GOAL_FILTERS).default("all"));
599
+ const cmd = parent.command("goals").description("Get employee performance goals").argument("<employeeId>", "Employee ID (0 = current user)", nonNegativeInt).addOption(new Option2("--filter <filter>", "Filter goals by status").choices(GOAL_FILTERS).default("all"));
555
600
  examples(cmd, ["123", ["123 --filter open", "only open goals"], ["123 --filter closed", "only closed goals"]]);
556
601
  cmd.action(async (employeeId, opts) => {
557
602
  const client = getClient();
558
603
  const result = await client.goals.get({
559
- employeeId,
604
+ employeeId: String(employeeId),
560
605
  filter: opts.filter === "all" ? void 0 : opts.filter
561
606
  });
562
607
  output(transformGoals(result));
@@ -568,12 +613,12 @@ import { writeFileSync as writeFileSync2 } from "fs";
568
613
  import { Option as Option3 } from "commander";
569
614
  var PHOTO_SIZES = ["original", "large", "medium", "small", "xs", "tiny"];
570
615
  function photo(parent) {
571
- const cmd = parent.command("photo <employeeId>").description("Download employee photo").requiredOption("--output <path>", "File path to save photo").addOption(new Option3("--size <size>", "Photo size").choices(PHOTO_SIZES).default("medium"));
616
+ const cmd = parent.command("photo").description("Download employee photo").argument("<employeeId>", "Employee ID (0 = current user)", nonNegativeInt).requiredOption("--output <path>", "File path to save photo", text).addOption(new Option3("--size <size>", "Photo size").choices(PHOTO_SIZES).default("medium"));
572
617
  examples(cmd, [["123 --output ./photo.jpg", "medium size (default)"], "123 --output ./photo.jpg --size large"]);
573
618
  cmd.action(async (employeeId, opts) => {
574
619
  const client = getClient();
575
620
  const buffer = await client.employees.getPhoto({
576
- employeeId,
621
+ employeeId: String(employeeId),
577
622
  size: opts.size
578
623
  });
579
624
  writeFileSync2(opts.output, buffer);
@@ -590,7 +635,7 @@ async function resolveEmployeeId(client, employeeId) {
590
635
 
591
636
  // src/commands/employee/search.ts
592
637
  function search(parent) {
593
- const cmd = parent.command("search [query]").description("Search and filter employees").option("--supervisor <name>", 'Filter by supervisor name (use "me" for current user)').option("--department <name>", "Filter by department").option("--location <name>", "Filter by location").option("--division <name>", "Filter by division").option("--with-photos", "Include each result's signed photo URL and photoUploaded flag");
638
+ const cmd = parent.command("search").description("Search and filter employees").argument("[query]", "Name fragment to search for", text).option("--supervisor <name>", 'Filter by supervisor name (use "me" for current user)', text).option("--department <name>", "Filter by department", text).option("--location <name>", "Filter by location", text).option("--division <name>", "Filter by division", text).option("--with-photos", "Include each result's signed photo URL and photoUploaded flag");
594
639
  examples(cmd, [
595
640
  "john",
596
641
  "--supervisor me",
@@ -657,11 +702,11 @@ function registerEmployeeCommands(program) {
657
702
  // src/commands/file/get.ts
658
703
  import { writeFileSync as writeFileSync3 } from "fs";
659
704
  function get2(parent) {
660
- const cmd = parent.command("get <fileId>").description("Download a company file").requiredOption("--output <path>", "File path to save to");
705
+ const cmd = parent.command("get").description("Download a company file").argument("<fileId>", "File ID", positiveInt).requiredOption("--output <path>", "File path to save to", text);
661
706
  examples(cmd, ["42 --output ./handbook.pdf"]);
662
707
  cmd.action(async (fileId, opts) => {
663
708
  const client = getClient();
664
- const buffer = await client.files.get({ fileId });
709
+ const buffer = await client.files.get({ fileId: String(fileId) });
665
710
  writeFileSync3(opts.output, buffer);
666
711
  output({ saved: true, path: opts.output, size: buffer.length });
667
712
  });
@@ -745,59 +790,43 @@ function registerMetaCommands(program) {
745
790
 
746
791
  // src/commands/timeoff/balance.ts
747
792
  function balance(parent) {
748
- const cmd = parent.command("balance [employeeId]").description("Estimate time off balance (defaults to current user)").option("--date <date>", "Future date to estimate balance for (YYYY-MM-DD)");
793
+ const cmd = parent.command("balance").description("Estimate time off balance (defaults to current user)").argument("[employeeId]", "Employee ID (0 or omitted = current user)", nonNegativeInt).option("--date <date>", "Future date to estimate balance for (YYYY-MM-DD)", date);
749
794
  examples(cmd, ["", "123", ["--date 2026-06-30", "balance at a future date"]]);
750
795
  cmd.action(async (employeeId, opts) => {
751
796
  const client = getClient();
752
- const id = await resolveEmployeeId(client, employeeId);
797
+ const id = await resolveEmployeeId(client, employeeId !== void 0 ? String(employeeId) : void 0);
753
798
  const result = await client.timeOff.getBalance({ employeeId: id, date: opts.date });
754
799
  output(transformTimeOffBalances(result));
755
800
  });
756
801
  }
757
802
 
758
803
  // src/commands/timeoff/create.ts
759
- import { InvalidArgumentError as InvalidArgumentError4, Option as Option4 } from "commander";
804
+ import { InvalidArgumentError as InvalidArgumentError5, Option as Option4 } from "commander";
805
+ import { z } from "zod";
760
806
 
761
807
  // src/utils/timeoff-dates.ts
762
- import { InvalidArgumentError as InvalidArgumentError3 } from "commander";
808
+ import { InvalidArgumentError as InvalidArgumentError4 } from "commander";
763
809
  var WEEKDAY_NAMES = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
764
810
  var YMD = /^\d{4}-\d{2}-\d{2}$/;
765
- function parseDatesJson(raw) {
766
- let parsed;
767
- try {
768
- parsed = JSON.parse(raw);
769
- } catch {
770
- throw new InvalidArgumentError3(
771
- `--dates is not valid JSON. Expected an array like '[{"ymd":"2026-03-21","amount":0.5}]'.`
772
- );
773
- }
774
- const isEntry = (d) => typeof d === "object" && d !== null && typeof d.ymd === "string" && YMD.test(d.ymd) && typeof d.amount === "number" && Number.isFinite(d.amount) && d.amount >= 0;
775
- if (!Array.isArray(parsed) || parsed.length === 0 || !parsed.every(isEntry)) {
776
- throw new InvalidArgumentError3(
777
- '--dates must be a non-empty JSON array of {"ymd":"YYYY-MM-DD","amount":<non-negative number>} objects.'
778
- );
779
- }
780
- return parsed.map((d) => ({ ymd: d.ymd, amount: d.amount }));
781
- }
782
811
  function parseYmd(value, label) {
783
812
  if (!YMD.test(value)) {
784
- throw new InvalidArgumentError3(`${label} must be a YYYY-MM-DD date, got "${value}".`);
813
+ throw new InvalidArgumentError4(`${label} must be a YYYY-MM-DD date, got "${value}".`);
785
814
  }
786
- const date = /* @__PURE__ */ new Date(`${value}T00:00:00Z`);
787
- if (Number.isNaN(date.getTime())) {
788
- throw new InvalidArgumentError3(`${label} is not a valid date: "${value}".`);
815
+ const date2 = /* @__PURE__ */ new Date(`${value}T00:00:00Z`);
816
+ if (Number.isNaN(date2.getTime())) {
817
+ throw new InvalidArgumentError4(`${label} is not a valid date: "${value}".`);
789
818
  }
790
- return date;
819
+ return date2;
791
820
  }
792
- function toYmd(date) {
793
- return date.toISOString().slice(0, 10);
821
+ function toYmd(date2) {
822
+ return date2.toISOString().slice(0, 10);
794
823
  }
795
824
  function buildTimeOffDates(params) {
796
825
  const { start, end, units, defaultHours, holidays = [] } = params;
797
826
  const startDate = parseYmd(start, "--start-date");
798
827
  const endDate = parseYmd(end, "--end-date");
799
828
  if (endDate < startDate) {
800
- throw new InvalidArgumentError3(`--end-date ${end} is before --start-date ${start}.`);
829
+ throw new InvalidArgumentError4(`--end-date ${end} is before --start-date ${start}.`);
801
830
  }
802
831
  const hoursByName = new Map(defaultHours.map((entry) => [entry.name, Number(entry.amount)]));
803
832
  const fallbackHours = hoursByName.get("default") ?? 8;
@@ -813,12 +842,31 @@ function buildTimeOffDates(params) {
813
842
  }
814
843
 
815
844
  // src/commands/timeoff/create.ts
845
+ var datesSchema = z.array(
846
+ z.object({
847
+ // Reuse the shared `date` validator so an impossible calendar date (e.g. 2026-02-30)
848
+ // is rejected inside --dates JSON exactly as it is for --start-date/--end-date.
849
+ ymd: z.string().refine(
850
+ (v) => {
851
+ try {
852
+ date(v);
853
+ return true;
854
+ } catch {
855
+ return false;
856
+ }
857
+ },
858
+ { message: "must be a real YYYY-MM-DD date" }
859
+ ),
860
+ amount: z.number().finite().nonnegative()
861
+ })
862
+ ).min(1, "must be a non-empty array");
816
863
  function create(parent) {
817
- const cmd = parent.command("create [employeeId]").description("Create a time off request (defaults to current user)").requiredOption("--start-date <date>", "Start date (YYYY-MM-DD)").requiredOption("--end-date <date>", "End date (YYYY-MM-DD)").requiredOption("--type-id <id>", 'Time off type ID (use "timeoff types" to find IDs)', positiveInt).addOption(
864
+ const cmd = parent.command("create").description("Create a time off request (defaults to current user)").argument("[employeeId]", "Employee ID (0 or omitted = current user)", nonNegativeInt).requiredOption("--start-date <date>", "Start date (YYYY-MM-DD)", date).requiredOption("--end-date <date>", "End date (YYYY-MM-DD)", date).requiredOption("--type-id <id>", 'Time off type ID (use "timeoff types" to find IDs)', positiveInt).addOption(
818
865
  new Option4("--status <status>", "Request status").choices(["requested", "approved"]).default("requested")
819
866
  ).option(
820
867
  "--dates <json>",
821
- `Per-day amounts as JSON array, e.g. '[{"ymd":"2026-03-21","amount":0.5}]' for a half day. When omitted, generated automatically: 1 day (or the weekday's working hours) per working day, 0 on weekends and company holidays`
868
+ `Per-day amounts as JSON array, e.g. '[{"ymd":"2026-03-21","amount":0.5}]' for a half day. When omitted, generated automatically: 1 day (or the weekday's working hours) per working day, 0 on weekends and company holidays`,
869
+ jsonShape(datesSchema)
822
870
  );
823
871
  textOrFileOption(cmd, "note", { description: "Note to include with the request" });
824
872
  examples(cmd, [
@@ -835,11 +883,11 @@ function create(parent) {
835
883
  cmd.action(
836
884
  async (employeeId, opts) => {
837
885
  const client = getClient();
838
- const id = await resolveEmployeeId(client, employeeId);
886
+ const id = await resolveEmployeeId(client, employeeId !== void 0 ? String(employeeId) : void 0);
839
887
  const note = resolveTextOrFile(opts, "note", { required: false });
840
888
  let dates;
841
889
  if (opts.dates) {
842
- dates = parseDatesJson(opts.dates);
890
+ dates = opts.dates;
843
891
  } else {
844
892
  const [types2, whosOut2] = await Promise.all([
845
893
  getTimeOffTypes(client),
@@ -847,7 +895,7 @@ function create(parent) {
847
895
  ]);
848
896
  const type = types2.timeOffTypes.find((t) => Number(t.id) === opts.typeId);
849
897
  if (!type) {
850
- throw new InvalidArgumentError4(`Unknown time off type ID ${opts.typeId}. Run "timeoff types" to list IDs.`);
898
+ throw new InvalidArgumentError5(`Unknown time off type ID ${opts.typeId}. Run "timeoff types" to list IDs.`);
851
899
  }
852
900
  const holidays = whosOut2.filter((e) => e.type === "holiday").map((e) => ({ start: e.start, end: e.end }));
853
901
  dates = buildTimeOffDates({
@@ -860,7 +908,7 @@ function create(parent) {
860
908
  }
861
909
  const total = dates.reduce((sum, d) => sum + d.amount, 0);
862
910
  if (total <= 0) {
863
- throw new InvalidArgumentError4(
911
+ throw new InvalidArgumentError5(
864
912
  opts.dates ? "The per-day amounts in --dates sum to 0; a request must claim more than 0 days/hours." : "The requested range contains no working days (weekends/company holidays only). Adjust the dates or pass --dates explicitly."
865
913
  );
866
914
  }
@@ -884,7 +932,7 @@ function create(parent) {
884
932
  // src/commands/timeoff/requests.ts
885
933
  import { Option as Option5 } from "commander";
886
934
  function requests(parent) {
887
- const cmd = parent.command("requests").description("Get time off requests (defaults to current user)").option("--request-id <id>", "Specific request ID", positiveInt).addOption(new Option5("--action <action>", "Access level filter").choices(["view", "approve"])).option("--employee-id <id>", "Filter by employee ID").option("--all", "Show all visible requests instead of just current user").option("--start-date <date>", "Start date (YYYY-MM-DD)").option("--end-date <date>", "End date (YYYY-MM-DD)").addOption(
935
+ const cmd = parent.command("requests").description("Get time off requests (defaults to current user)").option("--request-id <id>", "Specific request ID", positiveInt).addOption(new Option5("--action <action>", "Access level filter").choices(["view", "approve"])).option("--employee-id <id>", "Filter by employee ID (0 = current user)", nonNegativeInt).option("--all", "Show all visible requests instead of just current user").option("--start-date <date>", "Start date (YYYY-MM-DD)", date).option("--end-date <date>", "End date (YYYY-MM-DD)", date).addOption(
888
936
  new Option5("--status <status>", "Filter by request status").choices([
889
937
  "approved",
890
938
  "denied",
@@ -892,7 +940,7 @@ function requests(parent) {
892
940
  "requested",
893
941
  "canceled"
894
942
  ])
895
- ).option("--type-id <typeId>", "Filter by time off type ID");
943
+ ).option("--type-id <typeId>", "Filter by time off type ID", positiveInt);
896
944
  examples(cmd, [
897
945
  "",
898
946
  "--status requested",
@@ -905,7 +953,7 @@ function requests(parent) {
905
953
  const start = opts.startDate ?? `${year}-01-01`;
906
954
  const end = opts.endDate ?? `${year}-12-31`;
907
955
  const client = getClient();
908
- let employeeId = opts.employeeId;
956
+ let employeeId = opts.employeeId !== void 0 ? String(opts.employeeId) : void 0;
909
957
  if (!employeeId && !opts.all) {
910
958
  employeeId = await resolveEmployeeId(client);
911
959
  }
@@ -916,7 +964,7 @@ function requests(parent) {
916
964
  start,
917
965
  end,
918
966
  status: opts.status,
919
- type: opts.typeId
967
+ type: opts.typeId !== void 0 ? String(opts.typeId) : void 0
920
968
  });
921
969
  output(transformTimeOffRequests(result));
922
970
  }
@@ -937,7 +985,7 @@ function types(parent) {
937
985
  // src/commands/timeoff/update-status.ts
938
986
  import { Option as Option6 } from "commander";
939
987
  function updateStatus(parent) {
940
- const cmd = parent.command("update-status <requestId>").description("Update time off request status (approve, deny, cancel)").addOption(
988
+ const cmd = parent.command("update-status").description("Update time off request status (approve, deny, cancel)").argument("<requestId>", "Time off request ID", positiveInt).addOption(
941
989
  new Option6("--status <status>", "New status").choices(["approved", "denied", "canceled"]).makeOptionMandatory()
942
990
  );
943
991
  textOrFileOption(cmd, "note", { description: "Note to attach to the status change" });
@@ -951,7 +999,7 @@ function updateStatus(parent) {
951
999
  const client = getClient();
952
1000
  const note = resolveTextOrFile(opts, "note", { required: false });
953
1001
  const result = await client.timeOff.updateRequestStatus({
954
- requestId,
1002
+ requestId: String(requestId),
955
1003
  status: opts.status,
956
1004
  note
957
1005
  });
@@ -962,7 +1010,7 @@ function updateStatus(parent) {
962
1010
 
963
1011
  // src/commands/timeoff/whos-out.ts
964
1012
  function whosOut(parent) {
965
- const cmd = parent.command("whos-out").description("View who's out \u2014 upcoming time off and holidays").option("--start-date <date>", "Start date (YYYY-MM-DD, defaults to today)").option("--end-date <date>", "End date (YYYY-MM-DD, defaults to today)");
1013
+ const cmd = parent.command("whos-out").description("View who's out \u2014 upcoming time off and holidays").option("--start-date <date>", "Start date (YYYY-MM-DD, defaults to today)", date).option("--end-date <date>", "End date (YYYY-MM-DD, defaults to today)", date);
966
1014
  examples(cmd, ["", ["--start-date 2026-03-20 --end-date 2026-04-03", "two-week window"]]);
967
1015
  cmd.action(async (opts) => {
968
1016
  const client = getClient();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bamboohr-cli",
3
- "version": "1.0.21",
3
+ "version": "1.0.22-g942e3c7.1",
4
4
  "publish": true,
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -12,7 +12,8 @@
12
12
  ],
13
13
  "dependencies": {
14
14
  "commander": "^13.1.0",
15
- "bamboohr-client": "1.0.24"
15
+ "zod": "^3.25.67",
16
+ "bamboohr-client": "1.0.24-g942e3c7.1"
16
17
  },
17
18
  "devDependencies": {
18
19
  "@types/node": "24.10.4",
@@ -32,8 +33,8 @@
32
33
  "scripts": {
33
34
  "build": "tsup",
34
35
  "dev": "tsx src/index.ts",
35
- "lint": "eslint src",
36
- "lint:fix": "eslint src --fix",
36
+ "lint": "eslint src tests",
37
+ "lint:fix": "eslint src tests --fix",
37
38
  "test": "vitest run",
38
39
  "test:integration": "vitest run --config vitest.integration.config.ts"
39
40
  }