@industry-theme/backlogmd-kanban-panel 1.0.14 → 1.0.15
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/panels.bundle.js +326 -209
- package/dist/panels.bundle.js.map +1 -1
- package/package.json +2 -2
package/dist/panels.bundle.js
CHANGED
|
@@ -4419,9 +4419,9 @@ function parseArrayValue(value) {
|
|
|
4419
4419
|
function parseAcceptanceCriteria(content2) {
|
|
4420
4420
|
const criteria = [];
|
|
4421
4421
|
const checkboxPattern = /^-\s*\[([ xX])\]\s*(.+)$/gm;
|
|
4422
|
-
|
|
4422
|
+
const matches = content2.matchAll(checkboxPattern);
|
|
4423
4423
|
let index2 = 1;
|
|
4424
|
-
|
|
4424
|
+
for (const match of matches) {
|
|
4425
4425
|
criteria.push({
|
|
4426
4426
|
index: index2++,
|
|
4427
4427
|
checked: match[1].toLowerCase() === "x",
|
|
@@ -4435,9 +4435,9 @@ function extractDescription(content2, title) {
|
|
|
4435
4435
|
if (title) {
|
|
4436
4436
|
body = body.replace(new RegExp(`^#\\s+${escapeRegex(title)}\\s*$`, "m"), "");
|
|
4437
4437
|
}
|
|
4438
|
-
body = body.replace(/^##\s+Acceptance Criteria[\s\S]*?(
|
|
4439
|
-
body = body.replace(/^##\s+Implementation Plan[\s\S]*?(
|
|
4440
|
-
body = body.replace(/^##\s+Implementation Notes[\s\S]*?(
|
|
4438
|
+
body = body.replace(/^##\s+Acceptance Criteria[\s\S]*?(?=^##|$)/m, "");
|
|
4439
|
+
body = body.replace(/^##\s+Implementation Plan[\s\S]*?(?=^##|$)/m, "");
|
|
4440
|
+
body = body.replace(/^##\s+Implementation Notes[\s\S]*?(?=^##|$)/m, "");
|
|
4441
4441
|
return body.trim();
|
|
4442
4442
|
}
|
|
4443
4443
|
function extractIdFromPath(filePath) {
|
|
@@ -4546,6 +4546,184 @@ function extractMilestoneIdFromFilename(filename) {
|
|
|
4546
4546
|
const match = filename.match(/^(m-\d+)/);
|
|
4547
4547
|
return match ? match[1] : null;
|
|
4548
4548
|
}
|
|
4549
|
+
const NO_MILESTONE_KEY = "__none";
|
|
4550
|
+
function normalizeMilestoneName(name2) {
|
|
4551
|
+
return name2.trim();
|
|
4552
|
+
}
|
|
4553
|
+
function milestoneKey(name2) {
|
|
4554
|
+
return normalizeMilestoneName(name2 ?? "").toLowerCase();
|
|
4555
|
+
}
|
|
4556
|
+
function isDoneStatus(status) {
|
|
4557
|
+
const normalized = (status ?? "").toLowerCase();
|
|
4558
|
+
return normalized.includes("done") || normalized.includes("complete");
|
|
4559
|
+
}
|
|
4560
|
+
function getMilestoneLabel(milestoneId, milestoneEntities) {
|
|
4561
|
+
if (!milestoneId) {
|
|
4562
|
+
return "Tasks without milestone";
|
|
4563
|
+
}
|
|
4564
|
+
const entity = milestoneEntities.find((m) => milestoneKey(m.id) === milestoneKey(milestoneId));
|
|
4565
|
+
return (entity == null ? void 0 : entity.title) || milestoneId;
|
|
4566
|
+
}
|
|
4567
|
+
function collectMilestoneIds(tasks, milestoneEntities) {
|
|
4568
|
+
const merged = [];
|
|
4569
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4570
|
+
const addMilestone = (value) => {
|
|
4571
|
+
const normalized = normalizeMilestoneName(value);
|
|
4572
|
+
if (!normalized)
|
|
4573
|
+
return;
|
|
4574
|
+
const key2 = milestoneKey(normalized);
|
|
4575
|
+
if (seen.has(key2))
|
|
4576
|
+
return;
|
|
4577
|
+
seen.add(key2);
|
|
4578
|
+
merged.push(normalized);
|
|
4579
|
+
};
|
|
4580
|
+
for (const entity of milestoneEntities) {
|
|
4581
|
+
addMilestone(entity.id);
|
|
4582
|
+
}
|
|
4583
|
+
for (const task of tasks) {
|
|
4584
|
+
addMilestone(task.milestone ?? "");
|
|
4585
|
+
}
|
|
4586
|
+
return merged;
|
|
4587
|
+
}
|
|
4588
|
+
function collectMilestones(tasks, configMilestones) {
|
|
4589
|
+
const merged = [];
|
|
4590
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4591
|
+
const addMilestone = (value) => {
|
|
4592
|
+
const normalized = normalizeMilestoneName(value);
|
|
4593
|
+
if (!normalized)
|
|
4594
|
+
return;
|
|
4595
|
+
const key2 = milestoneKey(normalized);
|
|
4596
|
+
if (seen.has(key2))
|
|
4597
|
+
return;
|
|
4598
|
+
seen.add(key2);
|
|
4599
|
+
merged.push(normalized);
|
|
4600
|
+
};
|
|
4601
|
+
for (const m of configMilestones) {
|
|
4602
|
+
addMilestone(m);
|
|
4603
|
+
}
|
|
4604
|
+
for (const task of tasks) {
|
|
4605
|
+
addMilestone(task.milestone ?? "");
|
|
4606
|
+
}
|
|
4607
|
+
return merged;
|
|
4608
|
+
}
|
|
4609
|
+
function createBucket(milestoneId, tasks, statuses, milestoneEntities, isNoMilestone) {
|
|
4610
|
+
const bucketMilestoneKey = milestoneKey(milestoneId);
|
|
4611
|
+
const bucketTasks = tasks.filter((task) => {
|
|
4612
|
+
const taskMilestoneKey = milestoneKey(task.milestone);
|
|
4613
|
+
return bucketMilestoneKey ? taskMilestoneKey === bucketMilestoneKey : !taskMilestoneKey;
|
|
4614
|
+
});
|
|
4615
|
+
const counts = {};
|
|
4616
|
+
for (const status of statuses) {
|
|
4617
|
+
counts[status] = 0;
|
|
4618
|
+
}
|
|
4619
|
+
for (const task of bucketTasks) {
|
|
4620
|
+
const status = task.status ?? "";
|
|
4621
|
+
counts[status] = (counts[status] ?? 0) + 1;
|
|
4622
|
+
}
|
|
4623
|
+
const doneCount = bucketTasks.filter((t) => isDoneStatus(t.status)).length;
|
|
4624
|
+
const progress = bucketTasks.length > 0 ? Math.round(doneCount / bucketTasks.length * 100) : 0;
|
|
4625
|
+
const key2 = bucketMilestoneKey ? bucketMilestoneKey : NO_MILESTONE_KEY;
|
|
4626
|
+
const label = getMilestoneLabel(milestoneId, milestoneEntities);
|
|
4627
|
+
return {
|
|
4628
|
+
key: key2,
|
|
4629
|
+
label,
|
|
4630
|
+
milestone: milestoneId,
|
|
4631
|
+
isNoMilestone,
|
|
4632
|
+
tasks: bucketTasks,
|
|
4633
|
+
statusCounts: counts,
|
|
4634
|
+
total: bucketTasks.length,
|
|
4635
|
+
doneCount,
|
|
4636
|
+
progress
|
|
4637
|
+
};
|
|
4638
|
+
}
|
|
4639
|
+
function buildMilestoneBuckets(tasks, milestoneEntities, statuses) {
|
|
4640
|
+
const allMilestoneIds = collectMilestoneIds(tasks, milestoneEntities);
|
|
4641
|
+
const buckets = [
|
|
4642
|
+
// "No milestone" bucket first
|
|
4643
|
+
createBucket(void 0, tasks, statuses, milestoneEntities, true),
|
|
4644
|
+
// Then each milestone bucket
|
|
4645
|
+
...allMilestoneIds.map((m) => createBucket(m, tasks, statuses, milestoneEntities, false))
|
|
4646
|
+
];
|
|
4647
|
+
return buckets;
|
|
4648
|
+
}
|
|
4649
|
+
function buildMilestoneBucketsFromConfig(tasks, configMilestones, statuses) {
|
|
4650
|
+
const milestoneEntities = configMilestones.map((id) => ({
|
|
4651
|
+
id,
|
|
4652
|
+
title: id,
|
|
4653
|
+
description: "",
|
|
4654
|
+
rawContent: "",
|
|
4655
|
+
tasks: []
|
|
4656
|
+
}));
|
|
4657
|
+
return buildMilestoneBuckets(tasks, milestoneEntities, statuses);
|
|
4658
|
+
}
|
|
4659
|
+
function groupTasksByMilestone(tasks, configMilestones, statuses) {
|
|
4660
|
+
const milestones = collectMilestones(tasks, configMilestones);
|
|
4661
|
+
const buckets = buildMilestoneBucketsFromConfig(tasks, configMilestones, statuses);
|
|
4662
|
+
return {
|
|
4663
|
+
milestones,
|
|
4664
|
+
buckets
|
|
4665
|
+
};
|
|
4666
|
+
}
|
|
4667
|
+
const PRIORITY_ORDER = {
|
|
4668
|
+
high: 0,
|
|
4669
|
+
medium: 1,
|
|
4670
|
+
low: 2
|
|
4671
|
+
};
|
|
4672
|
+
function sortTasksByTitle(tasks, direction = "asc") {
|
|
4673
|
+
return [...tasks].sort((a, b) => {
|
|
4674
|
+
const cmp = a.title.localeCompare(b.title);
|
|
4675
|
+
return direction === "asc" ? cmp : -cmp;
|
|
4676
|
+
});
|
|
4677
|
+
}
|
|
4678
|
+
function sortTasksBy(tasks, sortBy = "title", direction = "asc") {
|
|
4679
|
+
switch (sortBy) {
|
|
4680
|
+
case "title":
|
|
4681
|
+
return sortTasksByTitle(tasks, direction);
|
|
4682
|
+
case "createdDate":
|
|
4683
|
+
return [...tasks].sort((a, b) => {
|
|
4684
|
+
const cmp = a.createdDate.localeCompare(b.createdDate);
|
|
4685
|
+
return direction === "asc" ? cmp : -cmp;
|
|
4686
|
+
});
|
|
4687
|
+
default: {
|
|
4688
|
+
const sorted = sortTasks(tasks);
|
|
4689
|
+
return direction === "desc" ? sorted.reverse() : sorted;
|
|
4690
|
+
}
|
|
4691
|
+
}
|
|
4692
|
+
}
|
|
4693
|
+
function sortTasks(tasks) {
|
|
4694
|
+
return [...tasks].sort((a, b) => {
|
|
4695
|
+
if (a.ordinal !== void 0 && b.ordinal !== void 0) {
|
|
4696
|
+
return a.ordinal - b.ordinal;
|
|
4697
|
+
}
|
|
4698
|
+
if (a.ordinal !== void 0)
|
|
4699
|
+
return -1;
|
|
4700
|
+
if (b.ordinal !== void 0)
|
|
4701
|
+
return 1;
|
|
4702
|
+
const aPri = a.priority ? PRIORITY_ORDER[a.priority] : 3;
|
|
4703
|
+
const bPri = b.priority ? PRIORITY_ORDER[b.priority] : 3;
|
|
4704
|
+
if (aPri !== bPri)
|
|
4705
|
+
return aPri - bPri;
|
|
4706
|
+
return b.createdDate.localeCompare(a.createdDate);
|
|
4707
|
+
});
|
|
4708
|
+
}
|
|
4709
|
+
function groupTasksByStatus(tasks, statuses) {
|
|
4710
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
4711
|
+
for (const status of statuses) {
|
|
4712
|
+
grouped.set(status, []);
|
|
4713
|
+
}
|
|
4714
|
+
for (const task of tasks) {
|
|
4715
|
+
const list2 = grouped.get(task.status);
|
|
4716
|
+
if (list2) {
|
|
4717
|
+
list2.push(task);
|
|
4718
|
+
} else {
|
|
4719
|
+
grouped.set(task.status, [task]);
|
|
4720
|
+
}
|
|
4721
|
+
}
|
|
4722
|
+
for (const [status, statusTasks] of grouped) {
|
|
4723
|
+
grouped.set(status, sortTasks(statusTasks));
|
|
4724
|
+
}
|
|
4725
|
+
return grouped;
|
|
4726
|
+
}
|
|
4549
4727
|
const DEFAULT_STATUSES = ["To Do", "In Progress", "Done"];
|
|
4550
4728
|
function parseBacklogConfig(content2) {
|
|
4551
4729
|
const config = {};
|
|
@@ -4696,185 +4874,6 @@ function serializeBacklogConfig(config) {
|
|
|
4696
4874
|
return `${lines.join("\n")}
|
|
4697
4875
|
`;
|
|
4698
4876
|
}
|
|
4699
|
-
const PRIORITY_ORDER = {
|
|
4700
|
-
high: 0,
|
|
4701
|
-
medium: 1,
|
|
4702
|
-
low: 2
|
|
4703
|
-
};
|
|
4704
|
-
function sortTasksByTitle(tasks, direction = "asc") {
|
|
4705
|
-
return [...tasks].sort((a, b) => {
|
|
4706
|
-
const cmp = a.title.localeCompare(b.title);
|
|
4707
|
-
return direction === "asc" ? cmp : -cmp;
|
|
4708
|
-
});
|
|
4709
|
-
}
|
|
4710
|
-
function sortTasksBy(tasks, sortBy = "title", direction = "asc") {
|
|
4711
|
-
switch (sortBy) {
|
|
4712
|
-
case "title":
|
|
4713
|
-
return sortTasksByTitle(tasks, direction);
|
|
4714
|
-
case "createdDate":
|
|
4715
|
-
return [...tasks].sort((a, b) => {
|
|
4716
|
-
const cmp = a.createdDate.localeCompare(b.createdDate);
|
|
4717
|
-
return direction === "asc" ? cmp : -cmp;
|
|
4718
|
-
});
|
|
4719
|
-
case "priority":
|
|
4720
|
-
case "ordinal":
|
|
4721
|
-
default:
|
|
4722
|
-
const sorted = sortTasks(tasks);
|
|
4723
|
-
return direction === "desc" ? sorted.reverse() : sorted;
|
|
4724
|
-
}
|
|
4725
|
-
}
|
|
4726
|
-
function sortTasks(tasks) {
|
|
4727
|
-
return [...tasks].sort((a, b) => {
|
|
4728
|
-
if (a.ordinal !== void 0 && b.ordinal !== void 0) {
|
|
4729
|
-
return a.ordinal - b.ordinal;
|
|
4730
|
-
}
|
|
4731
|
-
if (a.ordinal !== void 0)
|
|
4732
|
-
return -1;
|
|
4733
|
-
if (b.ordinal !== void 0)
|
|
4734
|
-
return 1;
|
|
4735
|
-
const aPri = a.priority ? PRIORITY_ORDER[a.priority] : 3;
|
|
4736
|
-
const bPri = b.priority ? PRIORITY_ORDER[b.priority] : 3;
|
|
4737
|
-
if (aPri !== bPri)
|
|
4738
|
-
return aPri - bPri;
|
|
4739
|
-
return b.createdDate.localeCompare(a.createdDate);
|
|
4740
|
-
});
|
|
4741
|
-
}
|
|
4742
|
-
function groupTasksByStatus(tasks, statuses) {
|
|
4743
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
4744
|
-
for (const status of statuses) {
|
|
4745
|
-
grouped.set(status, []);
|
|
4746
|
-
}
|
|
4747
|
-
for (const task of tasks) {
|
|
4748
|
-
const list2 = grouped.get(task.status);
|
|
4749
|
-
if (list2) {
|
|
4750
|
-
list2.push(task);
|
|
4751
|
-
} else {
|
|
4752
|
-
grouped.set(task.status, [task]);
|
|
4753
|
-
}
|
|
4754
|
-
}
|
|
4755
|
-
for (const [status, statusTasks] of grouped) {
|
|
4756
|
-
grouped.set(status, sortTasks(statusTasks));
|
|
4757
|
-
}
|
|
4758
|
-
return grouped;
|
|
4759
|
-
}
|
|
4760
|
-
const NO_MILESTONE_KEY = "__none";
|
|
4761
|
-
function normalizeMilestoneName(name2) {
|
|
4762
|
-
return name2.trim();
|
|
4763
|
-
}
|
|
4764
|
-
function milestoneKey(name2) {
|
|
4765
|
-
return normalizeMilestoneName(name2 ?? "").toLowerCase();
|
|
4766
|
-
}
|
|
4767
|
-
function isDoneStatus(status) {
|
|
4768
|
-
const normalized = (status ?? "").toLowerCase();
|
|
4769
|
-
return normalized.includes("done") || normalized.includes("complete");
|
|
4770
|
-
}
|
|
4771
|
-
function getMilestoneLabel(milestoneId, milestoneEntities) {
|
|
4772
|
-
if (!milestoneId) {
|
|
4773
|
-
return "Tasks without milestone";
|
|
4774
|
-
}
|
|
4775
|
-
const entity = milestoneEntities.find((m) => milestoneKey(m.id) === milestoneKey(milestoneId));
|
|
4776
|
-
return (entity == null ? void 0 : entity.title) || milestoneId;
|
|
4777
|
-
}
|
|
4778
|
-
function collectMilestoneIds(tasks, milestoneEntities) {
|
|
4779
|
-
const merged = [];
|
|
4780
|
-
const seen = /* @__PURE__ */ new Set();
|
|
4781
|
-
const addMilestone = (value) => {
|
|
4782
|
-
const normalized = normalizeMilestoneName(value);
|
|
4783
|
-
if (!normalized)
|
|
4784
|
-
return;
|
|
4785
|
-
const key2 = milestoneKey(normalized);
|
|
4786
|
-
if (seen.has(key2))
|
|
4787
|
-
return;
|
|
4788
|
-
seen.add(key2);
|
|
4789
|
-
merged.push(normalized);
|
|
4790
|
-
};
|
|
4791
|
-
for (const entity of milestoneEntities) {
|
|
4792
|
-
addMilestone(entity.id);
|
|
4793
|
-
}
|
|
4794
|
-
for (const task of tasks) {
|
|
4795
|
-
addMilestone(task.milestone ?? "");
|
|
4796
|
-
}
|
|
4797
|
-
return merged;
|
|
4798
|
-
}
|
|
4799
|
-
function collectMilestones(tasks, configMilestones) {
|
|
4800
|
-
const merged = [];
|
|
4801
|
-
const seen = /* @__PURE__ */ new Set();
|
|
4802
|
-
const addMilestone = (value) => {
|
|
4803
|
-
const normalized = normalizeMilestoneName(value);
|
|
4804
|
-
if (!normalized)
|
|
4805
|
-
return;
|
|
4806
|
-
const key2 = milestoneKey(normalized);
|
|
4807
|
-
if (seen.has(key2))
|
|
4808
|
-
return;
|
|
4809
|
-
seen.add(key2);
|
|
4810
|
-
merged.push(normalized);
|
|
4811
|
-
};
|
|
4812
|
-
for (const m of configMilestones) {
|
|
4813
|
-
addMilestone(m);
|
|
4814
|
-
}
|
|
4815
|
-
for (const task of tasks) {
|
|
4816
|
-
addMilestone(task.milestone ?? "");
|
|
4817
|
-
}
|
|
4818
|
-
return merged;
|
|
4819
|
-
}
|
|
4820
|
-
function createBucket(milestoneId, tasks, statuses, milestoneEntities, isNoMilestone) {
|
|
4821
|
-
const bucketMilestoneKey = milestoneKey(milestoneId);
|
|
4822
|
-
const bucketTasks = tasks.filter((task) => {
|
|
4823
|
-
const taskMilestoneKey = milestoneKey(task.milestone);
|
|
4824
|
-
return bucketMilestoneKey ? taskMilestoneKey === bucketMilestoneKey : !taskMilestoneKey;
|
|
4825
|
-
});
|
|
4826
|
-
const counts = {};
|
|
4827
|
-
for (const status of statuses) {
|
|
4828
|
-
counts[status] = 0;
|
|
4829
|
-
}
|
|
4830
|
-
for (const task of bucketTasks) {
|
|
4831
|
-
const status = task.status ?? "";
|
|
4832
|
-
counts[status] = (counts[status] ?? 0) + 1;
|
|
4833
|
-
}
|
|
4834
|
-
const doneCount = bucketTasks.filter((t) => isDoneStatus(t.status)).length;
|
|
4835
|
-
const progress = bucketTasks.length > 0 ? Math.round(doneCount / bucketTasks.length * 100) : 0;
|
|
4836
|
-
const key2 = bucketMilestoneKey ? bucketMilestoneKey : NO_MILESTONE_KEY;
|
|
4837
|
-
const label = getMilestoneLabel(milestoneId, milestoneEntities);
|
|
4838
|
-
return {
|
|
4839
|
-
key: key2,
|
|
4840
|
-
label,
|
|
4841
|
-
milestone: milestoneId,
|
|
4842
|
-
isNoMilestone,
|
|
4843
|
-
tasks: bucketTasks,
|
|
4844
|
-
statusCounts: counts,
|
|
4845
|
-
total: bucketTasks.length,
|
|
4846
|
-
doneCount,
|
|
4847
|
-
progress
|
|
4848
|
-
};
|
|
4849
|
-
}
|
|
4850
|
-
function buildMilestoneBuckets(tasks, milestoneEntities, statuses) {
|
|
4851
|
-
const allMilestoneIds = collectMilestoneIds(tasks, milestoneEntities);
|
|
4852
|
-
const buckets = [
|
|
4853
|
-
// "No milestone" bucket first
|
|
4854
|
-
createBucket(void 0, tasks, statuses, milestoneEntities, true),
|
|
4855
|
-
// Then each milestone bucket
|
|
4856
|
-
...allMilestoneIds.map((m) => createBucket(m, tasks, statuses, milestoneEntities, false))
|
|
4857
|
-
];
|
|
4858
|
-
return buckets;
|
|
4859
|
-
}
|
|
4860
|
-
function buildMilestoneBucketsFromConfig(tasks, configMilestones, statuses) {
|
|
4861
|
-
const milestoneEntities = configMilestones.map((id) => ({
|
|
4862
|
-
id,
|
|
4863
|
-
title: id,
|
|
4864
|
-
description: "",
|
|
4865
|
-
rawContent: "",
|
|
4866
|
-
tasks: []
|
|
4867
|
-
}));
|
|
4868
|
-
return buildMilestoneBuckets(tasks, milestoneEntities, statuses);
|
|
4869
|
-
}
|
|
4870
|
-
function groupTasksByMilestone(tasks, configMilestones, statuses) {
|
|
4871
|
-
const milestones = collectMilestones(tasks, configMilestones);
|
|
4872
|
-
const buckets = buildMilestoneBucketsFromConfig(tasks, configMilestones, statuses);
|
|
4873
|
-
return {
|
|
4874
|
-
milestones,
|
|
4875
|
-
buckets
|
|
4876
|
-
};
|
|
4877
|
-
}
|
|
4878
4877
|
class Core {
|
|
4879
4878
|
constructor(options) {
|
|
4880
4879
|
__publicField(this, "projectRoot");
|
|
@@ -5067,7 +5066,7 @@ class Core {
|
|
|
5067
5066
|
const tasksLimit = (options == null ? void 0 : options.tasksLimit) ?? 10;
|
|
5068
5067
|
const completedLimit = (options == null ? void 0 : options.completedLimit) ?? 10;
|
|
5069
5068
|
const offset = (options == null ? void 0 : options.offset) ?? 0;
|
|
5070
|
-
(options == null ? void 0 : options.tasksSortDirection) ?? "asc";
|
|
5069
|
+
const tasksSortDirection = (options == null ? void 0 : options.tasksSortDirection) ?? "asc";
|
|
5071
5070
|
const completedSortByIdDesc = (options == null ? void 0 : options.completedSortByIdDesc) ?? true;
|
|
5072
5071
|
const sources = ["tasks", "completed"];
|
|
5073
5072
|
const bySource = /* @__PURE__ */ new Map();
|
|
@@ -5077,7 +5076,11 @@ class Core {
|
|
|
5077
5076
|
entries = entries.sort((a, b) => {
|
|
5078
5077
|
const aNum = parseInt(a.id.replace(/\D/g, ""), 10) || 0;
|
|
5079
5078
|
const bNum = parseInt(b.id.replace(/\D/g, ""), 10) || 0;
|
|
5080
|
-
|
|
5079
|
+
if (source2 === "completed") {
|
|
5080
|
+
return completedSortByIdDesc ? bNum - aNum : aNum - bNum;
|
|
5081
|
+
} else {
|
|
5082
|
+
return tasksSortDirection === "desc" ? bNum - aNum : aNum - bNum;
|
|
5083
|
+
}
|
|
5081
5084
|
});
|
|
5082
5085
|
const total = entries.length;
|
|
5083
5086
|
const pageEntries = entries.slice(offset, offset + limit);
|
|
@@ -5108,13 +5111,17 @@ class Core {
|
|
|
5108
5111
|
throw new Error("Core not lazy initialized. Call initializeLazy() first.");
|
|
5109
5112
|
}
|
|
5110
5113
|
const limit = (options == null ? void 0 : options.limit) ?? 10;
|
|
5111
|
-
(options == null ? void 0 : options.sortDirection) ?? "asc";
|
|
5114
|
+
const sortDirection = (options == null ? void 0 : options.sortDirection) ?? "asc";
|
|
5112
5115
|
const completedSortByIdDesc = (options == null ? void 0 : options.completedSortByIdDesc) ?? true;
|
|
5113
5116
|
let entries = Array.from(this.taskIndex.values()).filter((e) => e.source === source2);
|
|
5114
5117
|
entries = entries.sort((a, b) => {
|
|
5115
5118
|
const aNum = parseInt(a.id.replace(/\D/g, ""), 10) || 0;
|
|
5116
5119
|
const bNum = parseInt(b.id.replace(/\D/g, ""), 10) || 0;
|
|
5117
|
-
|
|
5120
|
+
if (source2 === "completed") {
|
|
5121
|
+
return completedSortByIdDesc ? bNum - aNum : aNum - bNum;
|
|
5122
|
+
} else {
|
|
5123
|
+
return sortDirection === "desc" ? bNum - aNum : aNum - bNum;
|
|
5124
|
+
}
|
|
5118
5125
|
});
|
|
5119
5126
|
const total = entries.length;
|
|
5120
5127
|
const pageEntries = entries.slice(currentOffset, currentOffset + limit);
|
|
@@ -5134,7 +5141,7 @@ class Core {
|
|
|
5134
5141
|
*/
|
|
5135
5142
|
getConfig() {
|
|
5136
5143
|
this.ensureInitialized();
|
|
5137
|
-
return this.
|
|
5144
|
+
return this.safeConfig;
|
|
5138
5145
|
}
|
|
5139
5146
|
/**
|
|
5140
5147
|
* List all tasks, optionally filtered
|
|
@@ -5149,7 +5156,8 @@ class Core {
|
|
|
5149
5156
|
tasks = tasks.filter((t) => t.status === filter.status);
|
|
5150
5157
|
}
|
|
5151
5158
|
if (filter == null ? void 0 : filter.assignee) {
|
|
5152
|
-
|
|
5159
|
+
const assignee = filter.assignee;
|
|
5160
|
+
tasks = tasks.filter((t) => t.assignee.includes(assignee));
|
|
5153
5161
|
}
|
|
5154
5162
|
if (filter == null ? void 0 : filter.priority) {
|
|
5155
5163
|
tasks = tasks.filter((t) => t.priority === filter.priority);
|
|
@@ -5159,7 +5167,10 @@ class Core {
|
|
|
5159
5167
|
tasks = tasks.filter((t) => milestoneKey(t.milestone) === filterKey);
|
|
5160
5168
|
}
|
|
5161
5169
|
if ((filter == null ? void 0 : filter.labels) && filter.labels.length > 0) {
|
|
5162
|
-
tasks = tasks.filter((t) =>
|
|
5170
|
+
tasks = tasks.filter((t) => {
|
|
5171
|
+
var _a;
|
|
5172
|
+
return (_a = filter.labels) == null ? void 0 : _a.some((label) => t.labels.includes(label));
|
|
5173
|
+
});
|
|
5163
5174
|
}
|
|
5164
5175
|
if (filter == null ? void 0 : filter.parentTaskId) {
|
|
5165
5176
|
tasks = tasks.filter((t) => t.parentTaskId === filter.parentTaskId);
|
|
@@ -5176,7 +5187,7 @@ class Core {
|
|
|
5176
5187
|
getTasksByStatus() {
|
|
5177
5188
|
this.ensureInitialized();
|
|
5178
5189
|
const tasks = Array.from(this.tasks.values());
|
|
5179
|
-
return groupTasksByStatus(tasks, this.
|
|
5190
|
+
return groupTasksByStatus(tasks, this.safeConfig.statuses);
|
|
5180
5191
|
}
|
|
5181
5192
|
/**
|
|
5182
5193
|
* Get tasks grouped by milestone
|
|
@@ -5200,7 +5211,7 @@ class Core {
|
|
|
5200
5211
|
getTasksByMilestone() {
|
|
5201
5212
|
this.ensureInitialized();
|
|
5202
5213
|
const tasks = Array.from(this.tasks.values());
|
|
5203
|
-
return groupTasksByMilestone(tasks, this.
|
|
5214
|
+
return groupTasksByMilestone(tasks, this.safeConfig.milestones, this.safeConfig.statuses);
|
|
5204
5215
|
}
|
|
5205
5216
|
// =========================================================================
|
|
5206
5217
|
// Milestone CRUD Operations
|
|
@@ -5426,7 +5437,7 @@ class Core {
|
|
|
5426
5437
|
const sortDirection = (pagination == null ? void 0 : pagination.sortDirection) ?? "asc";
|
|
5427
5438
|
const byStatus = /* @__PURE__ */ new Map();
|
|
5428
5439
|
const allGrouped = /* @__PURE__ */ new Map();
|
|
5429
|
-
for (const status of this.
|
|
5440
|
+
for (const status of this.safeConfig.statuses) {
|
|
5430
5441
|
allGrouped.set(status, []);
|
|
5431
5442
|
}
|
|
5432
5443
|
for (const task of this.tasks.values()) {
|
|
@@ -5437,7 +5448,7 @@ class Core {
|
|
|
5437
5448
|
allGrouped.set(task.status, [task]);
|
|
5438
5449
|
}
|
|
5439
5450
|
}
|
|
5440
|
-
for (const status of this.
|
|
5451
|
+
for (const status of this.safeConfig.statuses) {
|
|
5441
5452
|
let tasks = allGrouped.get(status) ?? [];
|
|
5442
5453
|
tasks = sortTasksBy(tasks, sortBy, sortDirection);
|
|
5443
5454
|
const total = tasks.length;
|
|
@@ -5452,7 +5463,7 @@ class Core {
|
|
|
5452
5463
|
}
|
|
5453
5464
|
return {
|
|
5454
5465
|
byStatus,
|
|
5455
|
-
statuses: this.
|
|
5466
|
+
statuses: this.safeConfig.statuses
|
|
5456
5467
|
};
|
|
5457
5468
|
}
|
|
5458
5469
|
/**
|
|
@@ -5492,10 +5503,16 @@ class Core {
|
|
|
5492
5503
|
}
|
|
5493
5504
|
// --- Private methods ---
|
|
5494
5505
|
ensureInitialized() {
|
|
5495
|
-
if (!this.initialized) {
|
|
5496
|
-
throw new Error("Core not initialized. Call initialize() first.");
|
|
5506
|
+
if (!this.initialized && !this.lazyInitialized || !this.config) {
|
|
5507
|
+
throw new Error("Core not initialized. Call initialize() or initializeLazy() first.");
|
|
5497
5508
|
}
|
|
5498
5509
|
}
|
|
5510
|
+
/**
|
|
5511
|
+
* Get config with type safety (use after ensureInitialized)
|
|
5512
|
+
*/
|
|
5513
|
+
get safeConfig() {
|
|
5514
|
+
return this.config;
|
|
5515
|
+
}
|
|
5499
5516
|
applyFilters(tasks, filter) {
|
|
5500
5517
|
if (!filter)
|
|
5501
5518
|
return tasks;
|
|
@@ -5504,7 +5521,8 @@ class Core {
|
|
|
5504
5521
|
result = result.filter((t) => t.status === filter.status);
|
|
5505
5522
|
}
|
|
5506
5523
|
if (filter.assignee) {
|
|
5507
|
-
|
|
5524
|
+
const assignee = filter.assignee;
|
|
5525
|
+
result = result.filter((t) => t.assignee.includes(assignee));
|
|
5508
5526
|
}
|
|
5509
5527
|
if (filter.priority) {
|
|
5510
5528
|
result = result.filter((t) => t.priority === filter.priority);
|
|
@@ -5514,7 +5532,10 @@ class Core {
|
|
|
5514
5532
|
result = result.filter((t) => milestoneKey(t.milestone) === filterKey);
|
|
5515
5533
|
}
|
|
5516
5534
|
if (filter.labels && filter.labels.length > 0) {
|
|
5517
|
-
result = result.filter((t) =>
|
|
5535
|
+
result = result.filter((t) => {
|
|
5536
|
+
var _a;
|
|
5537
|
+
return (_a = filter.labels) == null ? void 0 : _a.some((label) => t.labels.includes(label));
|
|
5538
|
+
});
|
|
5518
5539
|
}
|
|
5519
5540
|
if (filter.parentTaskId) {
|
|
5520
5541
|
result = result.filter((t) => t.parentTaskId === filter.parentTaskId);
|
|
@@ -5611,6 +5632,12 @@ class Core {
|
|
|
5611
5632
|
getTasksDir() {
|
|
5612
5633
|
return this.fs.join(this.projectRoot, "backlog", "tasks");
|
|
5613
5634
|
}
|
|
5635
|
+
/**
|
|
5636
|
+
* Get the completed directory path
|
|
5637
|
+
*/
|
|
5638
|
+
getCompletedDir() {
|
|
5639
|
+
return this.fs.join(this.projectRoot, "backlog", "completed");
|
|
5640
|
+
}
|
|
5614
5641
|
// =========================================================================
|
|
5615
5642
|
// Task CRUD Operations
|
|
5616
5643
|
// =========================================================================
|
|
@@ -5621,18 +5648,18 @@ class Core {
|
|
|
5621
5648
|
* @returns Created task
|
|
5622
5649
|
*/
|
|
5623
5650
|
async createTask(input) {
|
|
5624
|
-
var _a;
|
|
5651
|
+
var _a, _b;
|
|
5625
5652
|
this.ensureInitialized();
|
|
5626
5653
|
const tasksDir = this.getTasksDir();
|
|
5627
5654
|
await this.fs.createDir(tasksDir, { recursive: true });
|
|
5628
|
-
const existingIds = Array.from(this.tasks.keys()).map((id) => parseInt(id.replace(/\D/g, ""), 10)).filter((n) => !isNaN(n));
|
|
5655
|
+
const existingIds = Array.from(this.tasks.keys()).map((id) => parseInt(id.replace(/\D/g, ""), 10)).filter((n) => !Number.isNaN(n));
|
|
5629
5656
|
const nextId = existingIds.length > 0 ? Math.max(...existingIds) + 1 : 1;
|
|
5630
5657
|
const taskId = String(nextId);
|
|
5631
5658
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
5632
5659
|
const task = {
|
|
5633
5660
|
id: taskId,
|
|
5634
5661
|
title: input.title,
|
|
5635
|
-
status: input.status || this.config.defaultStatus || "To Do",
|
|
5662
|
+
status: input.status || ((_a = this.config) == null ? void 0 : _a.defaultStatus) || "To Do",
|
|
5636
5663
|
priority: input.priority,
|
|
5637
5664
|
assignee: input.assignee || [],
|
|
5638
5665
|
createdDate: now,
|
|
@@ -5643,7 +5670,7 @@ class Core {
|
|
|
5643
5670
|
description: input.description,
|
|
5644
5671
|
implementationPlan: input.implementationPlan,
|
|
5645
5672
|
implementationNotes: input.implementationNotes,
|
|
5646
|
-
acceptanceCriteriaItems: (
|
|
5673
|
+
acceptanceCriteriaItems: (_b = input.acceptanceCriteria) == null ? void 0 : _b.map((ac, i) => ({
|
|
5647
5674
|
index: i + 1,
|
|
5648
5675
|
text: ac.text,
|
|
5649
5676
|
checked: ac.checked || false
|
|
@@ -5699,19 +5726,23 @@ class Core {
|
|
|
5699
5726
|
updated.labels = [.../* @__PURE__ */ new Set([...updated.labels, ...input.addLabels])];
|
|
5700
5727
|
}
|
|
5701
5728
|
if (input.removeLabels) {
|
|
5702
|
-
updated.labels = updated.labels.filter((l) =>
|
|
5729
|
+
updated.labels = updated.labels.filter((l) => {
|
|
5730
|
+
var _a;
|
|
5731
|
+
return !((_a = input.removeLabels) == null ? void 0 : _a.includes(l));
|
|
5732
|
+
});
|
|
5703
5733
|
}
|
|
5704
5734
|
}
|
|
5705
5735
|
if (input.assignee) {
|
|
5706
5736
|
updated.assignee = input.assignee;
|
|
5707
5737
|
}
|
|
5708
5738
|
if (input.addDependencies) {
|
|
5709
|
-
updated.dependencies = [
|
|
5710
|
-
.../* @__PURE__ */ new Set([...updated.dependencies, ...input.addDependencies])
|
|
5711
|
-
];
|
|
5739
|
+
updated.dependencies = [.../* @__PURE__ */ new Set([...updated.dependencies, ...input.addDependencies])];
|
|
5712
5740
|
}
|
|
5713
5741
|
if (input.removeDependencies) {
|
|
5714
|
-
updated.dependencies = updated.dependencies.filter((d) =>
|
|
5742
|
+
updated.dependencies = updated.dependencies.filter((d) => {
|
|
5743
|
+
var _a;
|
|
5744
|
+
return !((_a = input.removeDependencies) == null ? void 0 : _a.includes(d));
|
|
5745
|
+
});
|
|
5715
5746
|
}
|
|
5716
5747
|
if (input.acceptanceCriteria) {
|
|
5717
5748
|
updated.acceptanceCriteriaItems = input.acceptanceCriteria.map((ac, i) => ({
|
|
@@ -5767,6 +5798,92 @@ class Core {
|
|
|
5767
5798
|
this.tasks.delete(id);
|
|
5768
5799
|
return true;
|
|
5769
5800
|
}
|
|
5801
|
+
/**
|
|
5802
|
+
* Archive a task (move from tasks/ to completed/)
|
|
5803
|
+
*
|
|
5804
|
+
* @param id - Task ID to archive
|
|
5805
|
+
* @returns Archived task or null if not found or already archived
|
|
5806
|
+
*/
|
|
5807
|
+
async archiveTask(id) {
|
|
5808
|
+
this.ensureInitialized();
|
|
5809
|
+
const task = this.tasks.get(id);
|
|
5810
|
+
if (!task) {
|
|
5811
|
+
return null;
|
|
5812
|
+
}
|
|
5813
|
+
if (task.source === "completed") {
|
|
5814
|
+
return null;
|
|
5815
|
+
}
|
|
5816
|
+
const completedDir = this.getCompletedDir();
|
|
5817
|
+
await this.fs.createDir(completedDir, { recursive: true });
|
|
5818
|
+
const safeTitle = task.title.replace(/[<>:"/\\|?*]/g, "").replace(/\s+/g, " ").slice(0, 50);
|
|
5819
|
+
const filename = `${id} - ${safeTitle}.md`;
|
|
5820
|
+
const newFilepath = this.fs.join(completedDir, filename);
|
|
5821
|
+
if (task.filePath) {
|
|
5822
|
+
try {
|
|
5823
|
+
await this.fs.deleteFile(task.filePath);
|
|
5824
|
+
} catch {
|
|
5825
|
+
}
|
|
5826
|
+
}
|
|
5827
|
+
const archived = {
|
|
5828
|
+
...task,
|
|
5829
|
+
source: "completed",
|
|
5830
|
+
filePath: newFilepath
|
|
5831
|
+
};
|
|
5832
|
+
const content2 = serializeTaskMarkdown(archived);
|
|
5833
|
+
await this.fs.writeFile(newFilepath, content2);
|
|
5834
|
+
this.tasks.set(id, archived);
|
|
5835
|
+
if (this.lazyInitialized) {
|
|
5836
|
+
const entry = this.taskIndex.get(id);
|
|
5837
|
+
if (entry) {
|
|
5838
|
+
entry.source = "completed";
|
|
5839
|
+
entry.filePath = newFilepath;
|
|
5840
|
+
}
|
|
5841
|
+
}
|
|
5842
|
+
return archived;
|
|
5843
|
+
}
|
|
5844
|
+
/**
|
|
5845
|
+
* Restore a task (move from completed/ to tasks/)
|
|
5846
|
+
*
|
|
5847
|
+
* @param id - Task ID to restore
|
|
5848
|
+
* @returns Restored task or null if not found or not archived
|
|
5849
|
+
*/
|
|
5850
|
+
async restoreTask(id) {
|
|
5851
|
+
this.ensureInitialized();
|
|
5852
|
+
const task = this.tasks.get(id);
|
|
5853
|
+
if (!task) {
|
|
5854
|
+
return null;
|
|
5855
|
+
}
|
|
5856
|
+
if (task.source !== "completed") {
|
|
5857
|
+
return null;
|
|
5858
|
+
}
|
|
5859
|
+
const tasksDir = this.getTasksDir();
|
|
5860
|
+
await this.fs.createDir(tasksDir, { recursive: true });
|
|
5861
|
+
const safeTitle = task.title.replace(/[<>:"/\\|?*]/g, "").replace(/\s+/g, " ").slice(0, 50);
|
|
5862
|
+
const filename = `${id} - ${safeTitle}.md`;
|
|
5863
|
+
const newFilepath = this.fs.join(tasksDir, filename);
|
|
5864
|
+
if (task.filePath) {
|
|
5865
|
+
try {
|
|
5866
|
+
await this.fs.deleteFile(task.filePath);
|
|
5867
|
+
} catch {
|
|
5868
|
+
}
|
|
5869
|
+
}
|
|
5870
|
+
const restored = {
|
|
5871
|
+
...task,
|
|
5872
|
+
source: "local",
|
|
5873
|
+
filePath: newFilepath
|
|
5874
|
+
};
|
|
5875
|
+
const content2 = serializeTaskMarkdown(restored);
|
|
5876
|
+
await this.fs.writeFile(newFilepath, content2);
|
|
5877
|
+
this.tasks.set(id, restored);
|
|
5878
|
+
if (this.lazyInitialized) {
|
|
5879
|
+
const entry = this.taskIndex.get(id);
|
|
5880
|
+
if (entry) {
|
|
5881
|
+
entry.source = "tasks";
|
|
5882
|
+
entry.filePath = newFilepath;
|
|
5883
|
+
}
|
|
5884
|
+
}
|
|
5885
|
+
return restored;
|
|
5886
|
+
}
|
|
5770
5887
|
/**
|
|
5771
5888
|
* Load specific tasks by their IDs (for lazy loading milestone tasks)
|
|
5772
5889
|
*
|