@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.
- package/dist/scripts/il.mjs +110 -27
- package/package.json +1 -1
package/dist/scripts/il.mjs
CHANGED
|
@@ -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))
|
|
774
|
-
|
|
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)
|
|
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)
|
|
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))
|
|
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
|
|
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
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
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
|
-
|
|
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)
|
|
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({
|