@eunjae/il 1.0.0 → 1.2.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.
Files changed (2) hide show
  1. package/dist/scripts/il.mjs +110 -27
  2. package/package.json +1 -1
@@ -277,6 +277,21 @@ const resolveGithubToken = async (workspaceRoot) => {
277
277
  //#endregion
278
278
  //#region lib/time.ts
279
279
  const DEFAULT_ZONE = "Europe/Paris";
280
+ const toIsoDate = (value, label) => {
281
+ const date = value.toISODate();
282
+ if (!date) throw new Error(`Failed to generate ${label} date`);
283
+ return date;
284
+ };
285
+ const buildDateRange = (start, end) => {
286
+ const dates = [];
287
+ let cursor = start.startOf("day");
288
+ const final = end.startOf("day");
289
+ while (cursor <= final) {
290
+ dates.push(toIsoDate(cursor, "range"));
291
+ cursor = cursor.plus({ days: 1 });
292
+ }
293
+ return dates;
294
+ };
280
295
  const nowIso = () => {
281
296
  const value = DateTime.now().setZone(DEFAULT_ZONE).toISO();
282
297
  if (!value) throw new Error("Failed to generate timestamp");
@@ -287,6 +302,18 @@ const todayDate = (date) => {
287
302
  if (!value) throw new Error("Failed to generate date");
288
303
  return value;
289
304
  };
305
+ const listDatesForScope = (scope) => {
306
+ const now = DateTime.now().setZone(DEFAULT_ZONE);
307
+ switch (scope) {
308
+ case "today": return [toIsoDate(now, "today")];
309
+ case "yesterday": return [toIsoDate(now.minus({ days: 1 }), "yesterday")];
310
+ case "this_week": return buildDateRange(now.startOf("week"), now.endOf("week"));
311
+ case "last_week": {
312
+ const lastWeek = now.minus({ weeks: 1 });
313
+ return buildDateRange(lastWeek.startOf("week"), lastWeek.endOf("week"));
314
+ }
315
+ }
316
+ };
290
317
 
291
318
  //#endregion
292
319
  //#region lib/pr.ts
@@ -660,18 +687,6 @@ const appendDayAssignment = (task, date, action, msg) => {
660
687
  };
661
688
  };
662
689
 
663
- //#endregion
664
- //#region lib/taskStore.ts
665
- const createTaskInStore = async (workspaceRoot, task) => {
666
- const { alias, aliases } = await allocateAlias(workspaceRoot, task.type === "pr_review" ? "R" : "T", task.id);
667
- await writeAliases(workspaceRoot, aliases);
668
- await saveTask(workspaceRoot, task.status, task);
669
- return {
670
- alias,
671
- task
672
- };
673
- };
674
-
675
690
  //#endregion
676
691
  //#region lib/taskResolver.ts
677
692
  const stableRefPattern = /^[TR]-[A-Z0-9]{6}$/;
@@ -696,6 +711,18 @@ const resolveTask = async (workspaceRoot, identifier) => {
696
711
  };
697
712
  };
698
713
 
714
+ //#endregion
715
+ //#region lib/taskStore.ts
716
+ const createTaskInStore = async (workspaceRoot, task) => {
717
+ const { alias, aliases } = await allocateAlias(workspaceRoot, task.type === "pr_review" ? "R" : "T", task.id);
718
+ await writeAliases(workspaceRoot, aliases);
719
+ await saveTask(workspaceRoot, task.status, task);
720
+ return {
721
+ alias,
722
+ task
723
+ };
724
+ };
725
+
699
726
  //#endregion
700
727
  //#region scripts/il.ts
701
728
  const program = new Command();
@@ -725,9 +752,23 @@ const printLines = (lines) => {
725
752
  }
726
753
  process.stdout.write(`${lines.join("\n")}\n`);
727
754
  };
755
+ const failWithHelp = (command, message) => {
756
+ process.stderr.write(`${message}\n`);
757
+ command.outputHelp();
758
+ process.exitCode = 1;
759
+ };
760
+ const listScopes = [
761
+ "today",
762
+ "yesterday",
763
+ "this_week",
764
+ "last_week"
765
+ ];
728
766
  const isTaskStatus = (value) => {
729
767
  return taskStatuses.includes(value);
730
768
  };
769
+ const isListScope = (value) => {
770
+ return listScopes.includes(value);
771
+ };
731
772
  const isPackageJson = (value) => {
732
773
  return value !== null && typeof value === "object" && "version" in value;
733
774
  };
@@ -751,6 +792,7 @@ const resolvePackageVersion = async () => {
751
792
  const packageVersion = await resolvePackageVersion();
752
793
  program.name("il").description("Terminal task manager").option("--store <path>", "explicit workspace path").option("--global", "use global workspace").option("--repo", "use repo workspace");
753
794
  if (packageVersion) program.version(packageVersion);
795
+ program.showHelpAfterError();
754
796
  program.command("init").description("initialize repo workspace").action(handleAction(async () => {
755
797
  const repoRoot = await findGitRoot(process.cwd());
756
798
  if (!repoRoot) throw new Error("Not inside a git repository");
@@ -770,22 +812,34 @@ program.command("where").description("show resolved workspace").action(handleAct
770
812
  }));
771
813
  program.command("add").description("add a task").argument("[title]", "task title").option("--type <type>", "regular|pr_review", "regular").option("--url <url>", "attach URL").option("--pr <url>", "attach PR URL").action(handleAction(async (title, options, command) => {
772
814
  const taskType = options.type;
773
- if (!taskTypes.includes(taskType)) throw new Error(`Invalid type: ${taskType}`);
774
- if (taskType === "pr_review" && !options.pr) throw new Error("PR review tasks require --pr");
815
+ if (!taskTypes.includes(taskType)) {
816
+ failWithHelp(command, `Invalid type: ${taskType}`);
817
+ return;
818
+ }
819
+ if (taskType === "pr_review" && !options.pr) {
820
+ failWithHelp(command, "PR review tasks require --pr");
821
+ return;
822
+ }
775
823
  const workspaceRoot = await resolveWorkspaceFor(true);
776
824
  await withWorkspaceLock(workspaceRoot, async () => {
777
825
  let prAttachment;
778
826
  let prTitle;
779
827
  if (options.pr) {
780
828
  const parsed = parseGitHubPrUrl(options.pr);
781
- if (!parsed) throw new Error("Invalid PR URL");
829
+ if (!parsed) {
830
+ failWithHelp(command, "Invalid PR URL");
831
+ return;
832
+ }
782
833
  const fetched = await fetchGitHubPr(parsed, await resolveGithubToken(workspaceRoot));
783
834
  prAttachment = buildPrAttachment(parsed, fetched ?? void 0);
784
835
  prTitle = fetched?.title ?? `PR #${parsed.number} ${parsed.repo.owner}/${parsed.repo.name}`;
785
836
  }
786
837
  const isExplicitTitle = Boolean(title);
787
838
  let finalTitle = title ?? prTitle;
788
- if (!finalTitle) throw new Error("Title is required when no PR URL is provided");
839
+ if (!finalTitle) {
840
+ failWithHelp(command, "Title is required when no PR URL is provided");
841
+ return;
842
+ }
789
843
  if (taskType === "pr_review" && !isExplicitTitle) finalTitle = `Review: ${finalTitle}`;
790
844
  const metadata = {
791
845
  url: options.url,
@@ -801,12 +855,28 @@ program.command("add").description("add a task").argument("[title]", "task title
801
855
  process.stdout.write(`Created ${created.alias} ${created.task.ref} ${created.task.title}\n`);
802
856
  });
803
857
  }));
804
- program.command("list").description("list tasks").argument("[status]", "task status").action(handleAction(async (status, command) => {
858
+ program.command("list").description("list tasks").argument("[status]", "task status or date scope").action(handleAction(async (status, command) => {
805
859
  const workspaceRoot = await resolveWorkspaceFor(true);
806
860
  const aliasLookup = buildAliasLookup(await readAliases(workspaceRoot));
807
- if (status && !isTaskStatus(status)) throw new Error(`Invalid status: ${status}`);
808
- const statuses = status ? [status] : [...taskStatuses];
809
861
  const lines = [];
862
+ if (status && !isTaskStatus(status) && !isListScope(status)) {
863
+ failWithHelp(command, `Invalid status or scope: ${status}`);
864
+ return;
865
+ }
866
+ if (status && isListScope(status)) {
867
+ const dates = listDatesForScope(status);
868
+ const tasks = await listAllTasks(workspaceRoot);
869
+ for (const entry of tasks) {
870
+ if (!dates.some((date) => isAssignedOnDate(entry.task, date))) continue;
871
+ const alias = aliasLookup.get(entry.task.id);
872
+ let line = formatTaskListLine(entry.task, alias);
873
+ if (entry.task.metadata.pr?.fetched?.state) line += ` PR:${entry.task.metadata.pr.fetched.state}`;
874
+ lines.push(line);
875
+ }
876
+ printLines(lines);
877
+ return;
878
+ }
879
+ const statuses = status ? [status] : [...taskStatuses];
810
880
  for (const currentStatus of statuses) {
811
881
  const stored = await listTasksByStatus(workspaceRoot, currentStatus);
812
882
  for (const entry of stored) {
@@ -843,7 +913,10 @@ addTransitionCommand("cancel");
843
913
  program.command("log").description("append a log entry").argument("<id>", "task identifier").argument("<message>", "log message").option("--status <status>", "include status in log entry").action(handleAction(async (identifier, message, options, command) => {
844
914
  const workspaceRoot = await resolveWorkspaceFor(true);
845
915
  const status = options.status;
846
- if (status && !taskStatuses.includes(status)) throw new Error(`Invalid status: ${status}`);
916
+ if (status && !taskStatuses.includes(status)) {
917
+ failWithHelp(command, `Invalid status: ${status}`);
918
+ return;
919
+ }
847
920
  await withWorkspaceLock(workspaceRoot, async () => {
848
921
  const stored = await resolveTask(workspaceRoot, identifier);
849
922
  const updated = appendLog(stored.task, message, status);
@@ -870,12 +943,13 @@ program.command("search").description("search tasks").argument("<query>", "searc
870
943
  return line;
871
944
  }));
872
945
  }));
873
- program.command("today").description("assign or list today tasks").argument("[id]", "task identifier").option("--date <date>", "YYYY-MM-DD").option("-m, --message <message>", "assignment message").action(handleAction(async (identifier, options, command) => {
946
+ program.command("today").description("assign a task for today").argument("<id>", "task identifier").option("--date <date>", "YYYY-MM-DD").option("-m, --message <message>", "assignment message").action(handleAction(async (identifier, options, command) => {
874
947
  const workspaceRoot = await resolveWorkspaceFor(true);
875
- const date = todayDate(options.date);
876
- if (!identifier) {
877
- const aliasLookup = buildAliasLookup(await readAliases(workspaceRoot));
878
- printLines((await listAllTasks(workspaceRoot)).filter((stored) => isAssignedOnDate(stored.task, date)).map((stored) => formatTaskListLine(stored.task, aliasLookup.get(stored.task.id))));
948
+ let date;
949
+ try {
950
+ date = todayDate(options.date);
951
+ } catch (error) {
952
+ failWithHelp(command, error instanceof Error ? error.message : String(error));
879
953
  return;
880
954
  }
881
955
  await withWorkspaceLock(workspaceRoot, async () => {
@@ -891,7 +965,13 @@ program.command("today").description("assign or list today tasks").argument("[id
891
965
  }));
892
966
  program.command("untoday").description("unassign a task from today").argument("<id>", "task identifier").option("--date <date>", "YYYY-MM-DD").action(handleAction(async (identifier, options, command) => {
893
967
  const workspaceRoot = await resolveWorkspaceFor(true);
894
- const date = todayDate(options.date);
968
+ let date;
969
+ try {
970
+ date = todayDate(options.date);
971
+ } catch (error) {
972
+ failWithHelp(command, error instanceof Error ? error.message : String(error));
973
+ return;
974
+ }
895
975
  await withWorkspaceLock(workspaceRoot, async () => {
896
976
  const stored = await resolveTask(workspaceRoot, identifier);
897
977
  if (!isAssignedOnDate(stored.task, date)) {
@@ -907,7 +987,10 @@ program.command("attach-pr").description("attach PR metadata to a task").argumen
907
987
  const workspaceRoot = await resolveWorkspaceFor(true);
908
988
  await withWorkspaceLock(workspaceRoot, async () => {
909
989
  const parsed = parseGitHubPrUrl(url);
910
- if (!parsed) throw new Error("Invalid PR URL");
990
+ if (!parsed) {
991
+ failWithHelp(command, "Invalid PR URL");
992
+ return;
993
+ }
911
994
  const fetched = await fetchGitHubPr(parsed, await resolveGithubToken(workspaceRoot));
912
995
  const stored = await resolveTask(workspaceRoot, identifier);
913
996
  const next = appendLog({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eunjae/il",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "il": "dist/scripts/il.mjs"