esedre 0.1.10 → 0.1.12
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/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,17 @@ All notable changes to Esedre are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.1.12] - 2026-09-26
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **UTF-8 En-Dash Mojibake Normalization**: Added `normalizeDashesAndMojibake` and `normalizeTicketFields` to transparently sanitize Windows-1252 mojibake (`–`), double-encoded UTF-8, and non-ASCII Unicode dashes (`\u2013`, `\u2014`, `\u2015`, `\u2012`, `\u2212`) into clean standard ASCII hyphens (`-`) across CLI tables, JSON projections, storage reads, and ticket mutations.
|
|
12
|
+
|
|
13
|
+
## [0.1.11] - 2026-09-26
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- **Prevent Project Query Leak in Embedded Mode**: Guarded `window.history.replaceState` behind `!isEmbedded` in `PlannedWorkView` when switching project filters, added an explicit `isEmbedded` prop to `PlannedWorkViewProps`, and introduced `detectIsEmbedded` and `updateProjectUrlSearchParam` helpers to ensure embedded planners never mutate the host window's URL search parameters.
|
|
17
|
+
- **Published Version Collision Guard**: Added upfront registry version pre-check to `scripts/publish.js` to immediately detect and reject attempts to republish already-published versions before running the full build/test lifecycle.
|
|
18
|
+
|
|
8
19
|
## [0.1.10] - 2026-09-26
|
|
9
20
|
|
|
10
21
|
### Added
|
package/dist/esedre.mjs
CHANGED
|
@@ -9,7 +9,7 @@ import fs3 from "node:fs";
|
|
|
9
9
|
import path3 from "node:path";
|
|
10
10
|
|
|
11
11
|
// src/types.ts
|
|
12
|
-
var CURRENT_ESEDRE_VERSION = "0.1.
|
|
12
|
+
var CURRENT_ESEDRE_VERSION = "0.1.12";
|
|
13
13
|
var EsedreConflictError = class extends Error {
|
|
14
14
|
constructor(ticketId, currentHash, lastHash) {
|
|
15
15
|
super(
|
|
@@ -538,6 +538,162 @@ async function generateProjectSnapshot(storage, projectCode, outputDir) {
|
|
|
538
538
|
return snapshot;
|
|
539
539
|
}
|
|
540
540
|
|
|
541
|
+
// src/utils/formatter.ts
|
|
542
|
+
var isColorSupported = !process.env.NO_COLOR && (process.stdout.isTTY || process.env.FORCE_COLOR);
|
|
543
|
+
var colors = {
|
|
544
|
+
reset: isColorSupported ? "\x1B[0m" : "",
|
|
545
|
+
bold: isColorSupported ? "\x1B[1m" : "",
|
|
546
|
+
dim: isColorSupported ? "\x1B[2m" : "",
|
|
547
|
+
cyan: isColorSupported ? "\x1B[36m" : "",
|
|
548
|
+
green: isColorSupported ? "\x1B[32m" : "",
|
|
549
|
+
yellow: isColorSupported ? "\x1B[33m" : "",
|
|
550
|
+
magenta: isColorSupported ? "\x1B[35m" : "",
|
|
551
|
+
red: isColorSupported ? "\x1B[31m" : "",
|
|
552
|
+
blue: isColorSupported ? "\x1B[34m" : ""
|
|
553
|
+
};
|
|
554
|
+
function normalizeDashesAndMojibake(val) {
|
|
555
|
+
if (typeof val !== "string") return val;
|
|
556
|
+
return val.replace(/–|‗/g, "-").replace(/â€[˜™]/g, "'").replace(/â€[Å“Â]/g, '"').replace(/\u00e2\u20ac\u201c|\u00e2\u0080\u0093|–/g, "-").replace(/\u00e2\u20ac\u201d|\u00e2\u0080\u0094|—/g, "-").replace(/\u00e2\u20ac\u02dc|\u00e2\u20ac\u2122|‘|’/g, "'").replace(/\u00e2\u20ac\u0153|\u00e2\u0080\u009d|“|â€/g, '"').replace(/\u00e2\u20ac\u00a6|…/g, "...").replace(/[\u2012\u2013\u2014\u2015\u2212]/g, "-");
|
|
557
|
+
}
|
|
558
|
+
function normalizeTicketFields(ticket) {
|
|
559
|
+
if (!ticket) return ticket;
|
|
560
|
+
if (ticket.meta) {
|
|
561
|
+
if (ticket.meta.title) ticket.meta.title = normalizeDashesAndMojibake(ticket.meta.title);
|
|
562
|
+
if (ticket.meta.estimatedEffort) ticket.meta.estimatedEffort = normalizeDashesAndMojibake(ticket.meta.estimatedEffort);
|
|
563
|
+
if (ticket.meta.complexity) ticket.meta.complexity = normalizeDashesAndMojibake(ticket.meta.complexity);
|
|
564
|
+
}
|
|
565
|
+
if (ticket.detail) {
|
|
566
|
+
if (ticket.detail.title) ticket.detail.title = normalizeDashesAndMojibake(ticket.detail.title);
|
|
567
|
+
if (ticket.detail.estimatedEffort) ticket.detail.estimatedEffort = normalizeDashesAndMojibake(ticket.detail.estimatedEffort);
|
|
568
|
+
if (ticket.detail.summary) ticket.detail.summary = normalizeDashesAndMojibake(ticket.detail.summary);
|
|
569
|
+
if (ticket.detail.breakdown) ticket.detail.breakdown = ticket.detail.breakdown.map((b) => normalizeDashesAndMojibake(b));
|
|
570
|
+
if (ticket.detail.technicalDetails) ticket.detail.technicalDetails = ticket.detail.technicalDetails.map((t) => normalizeDashesAndMojibake(t));
|
|
571
|
+
if (ticket.detail.openQuestions) ticket.detail.openQuestions = ticket.detail.openQuestions.map((q) => normalizeDashesAndMojibake(q));
|
|
572
|
+
if (ticket.detail.raw) ticket.detail.raw = normalizeDashesAndMojibake(ticket.detail.raw);
|
|
573
|
+
}
|
|
574
|
+
if (ticket.planMarkdown) {
|
|
575
|
+
ticket.planMarkdown = normalizeDashesAndMojibake(ticket.planMarkdown);
|
|
576
|
+
}
|
|
577
|
+
if (ticket.comments) {
|
|
578
|
+
for (const c of ticket.comments) {
|
|
579
|
+
if (c.text) c.text = normalizeDashesAndMojibake(c.text);
|
|
580
|
+
if (c.author) c.author = normalizeDashesAndMojibake(c.author);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
return ticket;
|
|
584
|
+
}
|
|
585
|
+
function formatTicketListTable(tickets) {
|
|
586
|
+
if (tickets.length === 0) {
|
|
587
|
+
return `${colors.dim}No tickets found matching criteria.${colors.reset}`;
|
|
588
|
+
}
|
|
589
|
+
const rows = tickets.map((t) => {
|
|
590
|
+
const id = t.projectDescriptor?.code ? `${t.projectDescriptor.code}-${t.meta.id}` : `#${t.meta.id}`;
|
|
591
|
+
const project = t.projectDescriptor?.code || "CORE";
|
|
592
|
+
const type = t.meta.type || t.meta.category || "Feature";
|
|
593
|
+
const status = t.meta.status;
|
|
594
|
+
const title = normalizeDashesAndMojibake(t.meta.title);
|
|
595
|
+
return { id, project, type, status, title };
|
|
596
|
+
});
|
|
597
|
+
const idWidth = Math.max(4, ...rows.map((r) => r.id.length));
|
|
598
|
+
const projWidth = Math.max(7, ...rows.map((r) => r.project.length));
|
|
599
|
+
const typeWidth = Math.max(8, ...rows.map((r) => r.type.length));
|
|
600
|
+
const statusWidth = Math.max(14, ...rows.map((r) => r.status.length));
|
|
601
|
+
const header = `${colors.bold}${pad("ID", idWidth)} ${pad("Project", projWidth)} ${pad("Type", typeWidth)} ${pad("Status", statusWidth)} Title${colors.reset}`;
|
|
602
|
+
const divider = `${colors.dim}${"-".repeat(idWidth)} ${"-".repeat(projWidth)} ${"-".repeat(typeWidth)} ${"-".repeat(statusWidth)} ${"-".repeat(40)}${colors.reset}`;
|
|
603
|
+
const formattedRows = rows.map((r) => {
|
|
604
|
+
const statusColored = colorStatus(r.status);
|
|
605
|
+
const typeColored = colorType(r.type);
|
|
606
|
+
const projColored = `${colors.magenta}${r.project}${colors.reset}`;
|
|
607
|
+
return `${colors.bold}${pad(r.id, idWidth)}${colors.reset} ${pad(projColored, projWidth + (isColorSupported ? colors.magenta.length + colors.reset.length : 0))} ${pad(typeColored, typeWidth + (isColorSupported ? 9 : 0))} ${pad(statusColored, statusWidth + (isColorSupported ? 9 : 0))} ${r.title}`;
|
|
608
|
+
});
|
|
609
|
+
return [header, divider, ...formattedRows].join("\n");
|
|
610
|
+
}
|
|
611
|
+
function formatTicketDetail(ticket) {
|
|
612
|
+
const { meta, detail, comments, planMarkdown, projectDescriptor } = ticket;
|
|
613
|
+
const lines = [];
|
|
614
|
+
lines.push(`${colors.bold}${colors.cyan}Ticket #${meta.project ? `${meta.project}-${meta.id}` : meta.id}: ${normalizeDashesAndMojibake(meta.title)}${colors.reset}`);
|
|
615
|
+
lines.push(`${colors.dim}${"=".repeat(60)}${colors.reset}`);
|
|
616
|
+
lines.push(`${colors.bold}Project:${colors.reset} ${projectDescriptor ? `${projectDescriptor.code} - ${projectDescriptor.name}` : "Default"}`);
|
|
617
|
+
lines.push(`${colors.bold}Type:${colors.reset} ${colorType(meta.type || meta.category || "Feature")}`);
|
|
618
|
+
lines.push(`${colors.bold}Status:${colors.reset} ${colorStatus(meta.status)}`);
|
|
619
|
+
lines.push(`${colors.bold}Complexity:${colors.reset} ${normalizeDashesAndMojibake(meta.complexity || "Medium")}`);
|
|
620
|
+
lines.push(`${colors.bold}Effort:${colors.reset} ${normalizeDashesAndMojibake(meta.estimatedEffort || "N/A")}`);
|
|
621
|
+
lines.push(`${colors.bold}Submitted By:${colors.reset} ${meta.submittedBy || "Unknown"}`);
|
|
622
|
+
if (meta.featureFlag) {
|
|
623
|
+
lines.push(`${colors.bold}Feature Flag:${colors.reset} ${colors.yellow}${meta.featureFlag}${colors.reset}`);
|
|
624
|
+
}
|
|
625
|
+
if (detail?.summary) {
|
|
626
|
+
lines.push("");
|
|
627
|
+
lines.push(`${colors.bold}Summary:${colors.reset}`);
|
|
628
|
+
lines.push(normalizeDashesAndMojibake(detail.summary));
|
|
629
|
+
}
|
|
630
|
+
if (detail?.breakdown && detail.breakdown.length > 0) {
|
|
631
|
+
lines.push("");
|
|
632
|
+
lines.push(`${colors.bold}Feature Breakdown:${colors.reset}`);
|
|
633
|
+
for (const b of detail.breakdown) {
|
|
634
|
+
lines.push(` \u2022 ${normalizeDashesAndMojibake(b)}`);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
if (detail?.technicalDetails && detail.technicalDetails.length > 0) {
|
|
638
|
+
lines.push("");
|
|
639
|
+
lines.push(`${colors.bold}Technical Details:${colors.reset}`);
|
|
640
|
+
for (const t of detail.technicalDetails) {
|
|
641
|
+
lines.push(` \u2022 ${normalizeDashesAndMojibake(t)}`);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
if (detail?.openQuestions && detail.openQuestions.length > 0) {
|
|
645
|
+
lines.push("");
|
|
646
|
+
lines.push(`${colors.bold}Open Decisions & Questions:${colors.reset}`);
|
|
647
|
+
for (const q of detail.openQuestions) {
|
|
648
|
+
lines.push(` \u2022 ${normalizeDashesAndMojibake(q)}`);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
if (planMarkdown) {
|
|
652
|
+
lines.push("");
|
|
653
|
+
lines.push(`${colors.bold}Implementation Plan:${colors.reset}`);
|
|
654
|
+
lines.push(normalizeDashesAndMojibake(planMarkdown.trim()));
|
|
655
|
+
}
|
|
656
|
+
if (comments && comments.length > 0) {
|
|
657
|
+
lines.push("");
|
|
658
|
+
lines.push(`${colors.bold}Comments (${comments.length}):${colors.reset}`);
|
|
659
|
+
for (const c of comments) {
|
|
660
|
+
lines.push(` ${colors.dim}[${c.timestamp.slice(0, 10)}]${colors.reset} ${colors.bold}${normalizeDashesAndMojibake(c.author)}:${colors.reset} ${normalizeDashesAndMojibake(c.text)}`);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return lines.join("\n");
|
|
664
|
+
}
|
|
665
|
+
function pad(str, width) {
|
|
666
|
+
return str.padEnd(width, " ");
|
|
667
|
+
}
|
|
668
|
+
function colorStatus(status) {
|
|
669
|
+
switch (status) {
|
|
670
|
+
case "Completed":
|
|
671
|
+
return `${colors.green}${status}${colors.reset}`;
|
|
672
|
+
case "In Development":
|
|
673
|
+
return `${colors.green}${status}${colors.reset}`;
|
|
674
|
+
case "Rejected":
|
|
675
|
+
return `${colors.red}${status}${colors.reset}`;
|
|
676
|
+
default:
|
|
677
|
+
return `${colors.cyan}${status}${colors.reset}`;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
function colorType(type) {
|
|
681
|
+
switch (type) {
|
|
682
|
+
case "Feature":
|
|
683
|
+
return `${colors.blue}${type}${colors.reset}`;
|
|
684
|
+
case "Platform":
|
|
685
|
+
return `${colors.magenta}${type}${colors.reset}`;
|
|
686
|
+
case "Tools":
|
|
687
|
+
return `${colors.yellow}${type}${colors.reset}`;
|
|
688
|
+
case "Bug":
|
|
689
|
+
return `${colors.red}${type}${colors.reset}`;
|
|
690
|
+
case "Idea":
|
|
691
|
+
return `${colors.green}${type}${colors.reset}`;
|
|
692
|
+
default:
|
|
693
|
+
return type;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
541
697
|
// src/storage/filesystem.ts
|
|
542
698
|
function isProjectMatch(code, filter) {
|
|
543
699
|
if (!code || !filter) return false;
|
|
@@ -1268,6 +1424,7 @@ ${formattedList}`
|
|
|
1268
1424
|
comments,
|
|
1269
1425
|
projectDescriptor: effectiveLoc.project
|
|
1270
1426
|
};
|
|
1427
|
+
normalizeTicketFields(ticketObj);
|
|
1271
1428
|
ticketObj.sha1 = computeTicketHash(ticketObj);
|
|
1272
1429
|
ticketObj.lastHash = ticketObj.sha1;
|
|
1273
1430
|
tickets.push(ticketObj);
|
|
@@ -1319,6 +1476,7 @@ ${formattedList}`
|
|
|
1319
1476
|
comments,
|
|
1320
1477
|
projectDescriptor: locInfo.loc.project
|
|
1321
1478
|
};
|
|
1479
|
+
normalizeTicketFields(ticket);
|
|
1322
1480
|
ticket.sha1 = computeTicketHash(ticket);
|
|
1323
1481
|
ticket.lastHash = ticket.sha1;
|
|
1324
1482
|
return ticket;
|
|
@@ -1411,13 +1569,18 @@ ${formattedList}`
|
|
|
1411
1569
|
fs3.mkdirSync(ticketDir, { recursive: true });
|
|
1412
1570
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1413
1571
|
const effectiveType = input.type || input.category || "Feature";
|
|
1572
|
+
const cleanTitle = normalizeDashesAndMojibake(input.title).slice(0, 48).trim();
|
|
1573
|
+
const cleanEffort = normalizeDashesAndMojibake(input.estimatedEffort || input.effort || "2.0 - 4.0 hours");
|
|
1574
|
+
const cleanComplexity = normalizeDashesAndMojibake(input.complexity || "Medium");
|
|
1575
|
+
const cleanSummary = input.summary ? normalizeDashesAndMojibake(input.summary) : void 0;
|
|
1576
|
+
const cleanDetailRaw = input.detailMarkdown || input.detail ? normalizeDashesAndMojibake(input.detailMarkdown || input.detail) : void 0;
|
|
1414
1577
|
const meta = {
|
|
1415
1578
|
id: nextId,
|
|
1416
|
-
title:
|
|
1579
|
+
title: cleanTitle,
|
|
1417
1580
|
type: effectiveType,
|
|
1418
1581
|
category: effectiveType,
|
|
1419
|
-
complexity:
|
|
1420
|
-
estimatedEffort:
|
|
1582
|
+
complexity: cleanComplexity,
|
|
1583
|
+
estimatedEffort: cleanEffort,
|
|
1421
1584
|
submittedBy: input.submittedBy || "Developer",
|
|
1422
1585
|
timestamp: now,
|
|
1423
1586
|
createdAt: now,
|
|
@@ -1428,7 +1591,7 @@ ${formattedList}`
|
|
|
1428
1591
|
projectId: targetLoc.project.id,
|
|
1429
1592
|
project: targetLoc.project.code
|
|
1430
1593
|
};
|
|
1431
|
-
const rawDetail =
|
|
1594
|
+
const rawDetail = cleanDetailRaw?.trim();
|
|
1432
1595
|
let detailMd;
|
|
1433
1596
|
if (rawDetail) {
|
|
1434
1597
|
if (/^#\s+[^\n]+/m.test(rawDetail)) {
|
|
@@ -1527,9 +1690,13 @@ ${input.summary || "Summary to be defined."}
|
|
|
1527
1690
|
const isNowCompleted = updates.status === "Completed";
|
|
1528
1691
|
const completedAt = isNowCompleted ? existing.meta.completedAt || now : updates.status && updates.status !== "Completed" ? void 0 : existing.meta.completedAt;
|
|
1529
1692
|
const revision = (existing.meta.revision || 1) + 1;
|
|
1693
|
+
const sanitizedUpdates = { ...updates };
|
|
1694
|
+
if (sanitizedUpdates.title) sanitizedUpdates.title = normalizeDashesAndMojibake(sanitizedUpdates.title);
|
|
1695
|
+
if (sanitizedUpdates.estimatedEffort) sanitizedUpdates.estimatedEffort = normalizeDashesAndMojibake(sanitizedUpdates.estimatedEffort);
|
|
1696
|
+
if (sanitizedUpdates.complexity) sanitizedUpdates.complexity = normalizeDashesAndMojibake(sanitizedUpdates.complexity);
|
|
1530
1697
|
const updatedMeta = {
|
|
1531
1698
|
...existing.meta,
|
|
1532
|
-
...
|
|
1699
|
+
...sanitizedUpdates,
|
|
1533
1700
|
id: locInfo.id,
|
|
1534
1701
|
project: locInfo.loc.project.code,
|
|
1535
1702
|
updatedAt: now,
|
|
@@ -1565,7 +1732,7 @@ ${input.summary || "Summary to be defined."}
|
|
|
1565
1732
|
if (!locInfo) return null;
|
|
1566
1733
|
const planPath = path3.join(locInfo.ticketDir, "implementation_plan.md");
|
|
1567
1734
|
if (!fs3.existsSync(planPath)) return null;
|
|
1568
|
-
return fs3.readFileSync(planPath, "utf-8");
|
|
1735
|
+
return normalizeDashesAndMojibake(fs3.readFileSync(planPath, "utf-8"));
|
|
1569
1736
|
}
|
|
1570
1737
|
async savePlan(id, planMarkdown, lastHash) {
|
|
1571
1738
|
const ticket = await this.getTicket(id);
|
|
@@ -1583,7 +1750,7 @@ ${input.summary || "Summary to be defined."}
|
|
|
1583
1750
|
throw new Error(`Ticket #${id} could not be located on disk`);
|
|
1584
1751
|
}
|
|
1585
1752
|
const planPath = path3.join(locInfo.ticketDir, "implementation_plan.md");
|
|
1586
|
-
writeSafeFile(planPath, planMarkdown);
|
|
1753
|
+
writeSafeFile(planPath, normalizeDashesAndMojibake(planMarkdown));
|
|
1587
1754
|
const metaPath = path3.join(locInfo.ticketDir, "meta.json");
|
|
1588
1755
|
if (fs3.existsSync(metaPath)) {
|
|
1589
1756
|
try {
|
|
@@ -1615,8 +1782,8 @@ ${input.summary || "Summary to be defined."}
|
|
|
1615
1782
|
const newComment = {
|
|
1616
1783
|
id: `${locInfo.id}-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
|
|
1617
1784
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1618
|
-
author: comment.author || "User",
|
|
1619
|
-
text: comment.text.trim()
|
|
1785
|
+
author: normalizeDashesAndMojibake(comment.author || "User"),
|
|
1786
|
+
text: normalizeDashesAndMojibake(comment.text.trim())
|
|
1620
1787
|
};
|
|
1621
1788
|
comments.push(newComment);
|
|
1622
1789
|
writeSafeFile(commentsPath, JSON.stringify(comments, null, 2) + "\n");
|
|
@@ -1727,131 +1894,6 @@ var SecurityFilter = class {
|
|
|
1727
1894
|
}
|
|
1728
1895
|
};
|
|
1729
1896
|
|
|
1730
|
-
// src/utils/formatter.ts
|
|
1731
|
-
var isColorSupported = !process.env.NO_COLOR && (process.stdout.isTTY || process.env.FORCE_COLOR);
|
|
1732
|
-
var colors = {
|
|
1733
|
-
reset: isColorSupported ? "\x1B[0m" : "",
|
|
1734
|
-
bold: isColorSupported ? "\x1B[1m" : "",
|
|
1735
|
-
dim: isColorSupported ? "\x1B[2m" : "",
|
|
1736
|
-
cyan: isColorSupported ? "\x1B[36m" : "",
|
|
1737
|
-
green: isColorSupported ? "\x1B[32m" : "",
|
|
1738
|
-
yellow: isColorSupported ? "\x1B[33m" : "",
|
|
1739
|
-
magenta: isColorSupported ? "\x1B[35m" : "",
|
|
1740
|
-
red: isColorSupported ? "\x1B[31m" : "",
|
|
1741
|
-
blue: isColorSupported ? "\x1B[34m" : ""
|
|
1742
|
-
};
|
|
1743
|
-
function formatTicketListTable(tickets) {
|
|
1744
|
-
if (tickets.length === 0) {
|
|
1745
|
-
return `${colors.dim}No tickets found matching criteria.${colors.reset}`;
|
|
1746
|
-
}
|
|
1747
|
-
const rows = tickets.map((t) => {
|
|
1748
|
-
const id = t.projectDescriptor?.code ? `${t.projectDescriptor.code}-${t.meta.id}` : `#${t.meta.id}`;
|
|
1749
|
-
const project = t.projectDescriptor?.code || "CORE";
|
|
1750
|
-
const type = t.meta.type || t.meta.category || "Feature";
|
|
1751
|
-
const status = t.meta.status;
|
|
1752
|
-
const title = t.meta.title;
|
|
1753
|
-
return { id, project, type, status, title };
|
|
1754
|
-
});
|
|
1755
|
-
const idWidth = Math.max(4, ...rows.map((r) => r.id.length));
|
|
1756
|
-
const projWidth = Math.max(7, ...rows.map((r) => r.project.length));
|
|
1757
|
-
const typeWidth = Math.max(8, ...rows.map((r) => r.type.length));
|
|
1758
|
-
const statusWidth = Math.max(14, ...rows.map((r) => r.status.length));
|
|
1759
|
-
const header = `${colors.bold}${pad("ID", idWidth)} ${pad("Project", projWidth)} ${pad("Type", typeWidth)} ${pad("Status", statusWidth)} Title${colors.reset}`;
|
|
1760
|
-
const divider = `${colors.dim}${"-".repeat(idWidth)} ${"-".repeat(projWidth)} ${"-".repeat(typeWidth)} ${"-".repeat(statusWidth)} ${"-".repeat(40)}${colors.reset}`;
|
|
1761
|
-
const formattedRows = rows.map((r) => {
|
|
1762
|
-
const statusColored = colorStatus(r.status);
|
|
1763
|
-
const typeColored = colorType(r.type);
|
|
1764
|
-
const projColored = `${colors.magenta}${r.project}${colors.reset}`;
|
|
1765
|
-
return `${colors.bold}${pad(r.id, idWidth)}${colors.reset} ${pad(projColored, projWidth + (isColorSupported ? colors.magenta.length + colors.reset.length : 0))} ${pad(typeColored, typeWidth + (isColorSupported ? 9 : 0))} ${pad(statusColored, statusWidth + (isColorSupported ? 9 : 0))} ${r.title}`;
|
|
1766
|
-
});
|
|
1767
|
-
return [header, divider, ...formattedRows].join("\n");
|
|
1768
|
-
}
|
|
1769
|
-
function formatTicketDetail(ticket) {
|
|
1770
|
-
const { meta, detail, comments, planMarkdown, projectDescriptor } = ticket;
|
|
1771
|
-
const lines = [];
|
|
1772
|
-
lines.push(`${colors.bold}${colors.cyan}Ticket #${meta.project ? `${meta.project}-${meta.id}` : meta.id}: ${meta.title}${colors.reset}`);
|
|
1773
|
-
lines.push(`${colors.dim}${"=".repeat(60)}${colors.reset}`);
|
|
1774
|
-
lines.push(`${colors.bold}Project:${colors.reset} ${projectDescriptor ? `${projectDescriptor.code} - ${projectDescriptor.name}` : "Default"}`);
|
|
1775
|
-
lines.push(`${colors.bold}Type:${colors.reset} ${colorType(meta.type || meta.category || "Feature")}`);
|
|
1776
|
-
lines.push(`${colors.bold}Status:${colors.reset} ${colorStatus(meta.status)}`);
|
|
1777
|
-
lines.push(`${colors.bold}Complexity:${colors.reset} ${meta.complexity || "Medium"}`);
|
|
1778
|
-
lines.push(`${colors.bold}Effort:${colors.reset} ${meta.estimatedEffort || "N/A"}`);
|
|
1779
|
-
lines.push(`${colors.bold}Submitted By:${colors.reset} ${meta.submittedBy || "Unknown"}`);
|
|
1780
|
-
if (meta.featureFlag) {
|
|
1781
|
-
lines.push(`${colors.bold}Feature Flag:${colors.reset} ${colors.yellow}${meta.featureFlag}${colors.reset}`);
|
|
1782
|
-
}
|
|
1783
|
-
if (detail?.summary) {
|
|
1784
|
-
lines.push("");
|
|
1785
|
-
lines.push(`${colors.bold}Summary:${colors.reset}`);
|
|
1786
|
-
lines.push(detail.summary);
|
|
1787
|
-
}
|
|
1788
|
-
if (detail?.breakdown && detail.breakdown.length > 0) {
|
|
1789
|
-
lines.push("");
|
|
1790
|
-
lines.push(`${colors.bold}Feature Breakdown:${colors.reset}`);
|
|
1791
|
-
for (const b of detail.breakdown) {
|
|
1792
|
-
lines.push(` \u2022 ${b}`);
|
|
1793
|
-
}
|
|
1794
|
-
}
|
|
1795
|
-
if (detail?.technicalDetails && detail.technicalDetails.length > 0) {
|
|
1796
|
-
lines.push("");
|
|
1797
|
-
lines.push(`${colors.bold}Technical Details:${colors.reset}`);
|
|
1798
|
-
for (const t of detail.technicalDetails) {
|
|
1799
|
-
lines.push(` \u2022 ${t}`);
|
|
1800
|
-
}
|
|
1801
|
-
}
|
|
1802
|
-
if (detail?.openQuestions && detail.openQuestions.length > 0) {
|
|
1803
|
-
lines.push("");
|
|
1804
|
-
lines.push(`${colors.bold}Open Decisions & Questions:${colors.reset}`);
|
|
1805
|
-
for (const q of detail.openQuestions) {
|
|
1806
|
-
lines.push(` \u2022 ${q}`);
|
|
1807
|
-
}
|
|
1808
|
-
}
|
|
1809
|
-
if (planMarkdown) {
|
|
1810
|
-
lines.push("");
|
|
1811
|
-
lines.push(`${colors.bold}Implementation Plan:${colors.reset}`);
|
|
1812
|
-
lines.push(planMarkdown.trim());
|
|
1813
|
-
}
|
|
1814
|
-
if (comments && comments.length > 0) {
|
|
1815
|
-
lines.push("");
|
|
1816
|
-
lines.push(`${colors.bold}Comments (${comments.length}):${colors.reset}`);
|
|
1817
|
-
for (const c of comments) {
|
|
1818
|
-
lines.push(` ${colors.dim}[${c.timestamp.slice(0, 10)}]${colors.reset} ${colors.bold}${c.author}:${colors.reset} ${c.text}`);
|
|
1819
|
-
}
|
|
1820
|
-
}
|
|
1821
|
-
return lines.join("\n");
|
|
1822
|
-
}
|
|
1823
|
-
function pad(str, width) {
|
|
1824
|
-
return str.padEnd(width, " ");
|
|
1825
|
-
}
|
|
1826
|
-
function colorStatus(status) {
|
|
1827
|
-
switch (status) {
|
|
1828
|
-
case "Completed":
|
|
1829
|
-
return `${colors.green}${status}${colors.reset}`;
|
|
1830
|
-
case "In Development":
|
|
1831
|
-
return `${colors.green}${status}${colors.reset}`;
|
|
1832
|
-
case "Rejected":
|
|
1833
|
-
return `${colors.red}${status}${colors.reset}`;
|
|
1834
|
-
default:
|
|
1835
|
-
return `${colors.cyan}${status}${colors.reset}`;
|
|
1836
|
-
}
|
|
1837
|
-
}
|
|
1838
|
-
function colorType(type) {
|
|
1839
|
-
switch (type) {
|
|
1840
|
-
case "Feature":
|
|
1841
|
-
return `${colors.blue}${type}${colors.reset}`;
|
|
1842
|
-
case "Platform":
|
|
1843
|
-
return `${colors.magenta}${type}${colors.reset}`;
|
|
1844
|
-
case "Tools":
|
|
1845
|
-
return `${colors.yellow}${type}${colors.reset}`;
|
|
1846
|
-
case "Bug":
|
|
1847
|
-
return `${colors.red}${type}${colors.reset}`;
|
|
1848
|
-
case "Idea":
|
|
1849
|
-
return `${colors.green}${type}${colors.reset}`;
|
|
1850
|
-
default:
|
|
1851
|
-
return type;
|
|
1852
|
-
}
|
|
1853
|
-
}
|
|
1854
|
-
|
|
1855
1897
|
// src/mcp/server.ts
|
|
1856
1898
|
import readline from "node:readline";
|
|
1857
1899
|
var EsedreMcpServer = class {
|
|
@@ -4129,6 +4171,18 @@ function printDuplicateProjectWarnings(storage, isJson) {
|
|
|
4129
4171
|
}
|
|
4130
4172
|
}
|
|
4131
4173
|
async function main() {
|
|
4174
|
+
if (process.stdout && typeof process.stdout.setDefaultEncoding === "function") {
|
|
4175
|
+
try {
|
|
4176
|
+
process.stdout.setDefaultEncoding("utf-8");
|
|
4177
|
+
} catch {
|
|
4178
|
+
}
|
|
4179
|
+
}
|
|
4180
|
+
if (process.stderr && typeof process.stderr.setDefaultEncoding === "function") {
|
|
4181
|
+
try {
|
|
4182
|
+
process.stderr.setDefaultEncoding("utf-8");
|
|
4183
|
+
} catch {
|
|
4184
|
+
}
|
|
4185
|
+
}
|
|
4132
4186
|
const rawArgs = process.argv.slice(2);
|
|
4133
4187
|
const { command, positionals, flags } = parseArgs(rawArgs);
|
|
4134
4188
|
try {
|
|
@@ -4704,12 +4758,12 @@ ${colors.yellow}\u26A0\uFE0F Warning: Daemon is running v${status.version}, but
|
|
|
4704
4758
|
if (isJson) {
|
|
4705
4759
|
console.log(JSON.stringify(tickets.map((t) => ({
|
|
4706
4760
|
id: t.meta.id,
|
|
4707
|
-
title: t.meta.title,
|
|
4761
|
+
title: normalizeDashesAndMojibake(t.meta.title),
|
|
4708
4762
|
type: t.meta.type || t.meta.category,
|
|
4709
4763
|
category: t.meta.type || t.meta.category,
|
|
4710
4764
|
status: t.meta.status,
|
|
4711
|
-
complexity: t.meta.complexity,
|
|
4712
|
-
effort: t.meta.estimatedEffort,
|
|
4765
|
+
complexity: normalizeDashesAndMojibake(t.meta.complexity),
|
|
4766
|
+
effort: normalizeDashesAndMojibake(t.meta.estimatedEffort),
|
|
4713
4767
|
project: t.projectDescriptor?.code || t.meta.project || "UNASSIGNED",
|
|
4714
4768
|
sha1: t.sha1 || t.meta.sha1
|
|
4715
4769
|
})), null, 2));
|
|
@@ -4806,11 +4860,12 @@ ${colors.dim}Total: ${tickets.length} tickets${colors.reset}`);
|
|
|
4806
4860
|
return;
|
|
4807
4861
|
}
|
|
4808
4862
|
case "create": {
|
|
4809
|
-
const
|
|
4810
|
-
if (!
|
|
4863
|
+
const rawTitle = flags["title"];
|
|
4864
|
+
if (!rawTitle) {
|
|
4811
4865
|
console.error(`${colors.red}Error: --title is required${colors.reset}`);
|
|
4812
4866
|
process.exit(1);
|
|
4813
4867
|
}
|
|
4868
|
+
const title = normalizeDashesAndMojibake(rawTitle);
|
|
4814
4869
|
const type = flags["type"] || flags["category"] || "Feature";
|
|
4815
4870
|
const category = type;
|
|
4816
4871
|
const projectCode = flags["project"] || discovered.config?.projectCode;
|
|
@@ -4819,9 +4874,9 @@ ${colors.dim}Total: ${tickets.length} tickets${colors.reset}`);
|
|
|
4819
4874
|
console.error(`${colors.dim}Tip: Run 'ese init' to initialize this repository.${colors.reset}`);
|
|
4820
4875
|
process.exit(1);
|
|
4821
4876
|
}
|
|
4822
|
-
const complexity = flags["complexity"] || "Medium";
|
|
4823
|
-
const estimatedEffort = flags["effort"] || "2.0 - 4.0 hours";
|
|
4824
|
-
const summary = flags["summary"]
|
|
4877
|
+
const complexity = normalizeDashesAndMojibake(flags["complexity"] || "Medium");
|
|
4878
|
+
const estimatedEffort = normalizeDashesAndMojibake(flags["effort"] || "2.0 - 4.0 hours");
|
|
4879
|
+
const summary = flags["summary"] ? normalizeDashesAndMojibake(flags["summary"]) : void 0;
|
|
4825
4880
|
const submittedBy = flags["author"] || "Developer";
|
|
4826
4881
|
const val = validateProjectCode(projectCode);
|
|
4827
4882
|
if (!val.valid) {
|
|
@@ -4871,9 +4926,9 @@ ${colors.dim}Total: ${tickets.length} tickets${colors.reset}`);
|
|
|
4871
4926
|
const projectFlag = flags["project"];
|
|
4872
4927
|
const lookupKey = projectFlag && /^\d+$/.test(idStr) ? `${projectFlag}-${idStr}` : idStr;
|
|
4873
4928
|
const status = flags["status"];
|
|
4874
|
-
const title = flags["title"];
|
|
4875
|
-
const complexity = flags["complexity"];
|
|
4876
|
-
const effort = flags["effort"];
|
|
4929
|
+
const title = flags["title"] ? normalizeDashesAndMojibake(flags["title"]) : void 0;
|
|
4930
|
+
const complexity = flags["complexity"] ? normalizeDashesAndMojibake(flags["complexity"]) : void 0;
|
|
4931
|
+
const effort = flags["effort"] ? normalizeDashesAndMojibake(flags["effort"]) : void 0;
|
|
4877
4932
|
const inDev = flags["in-dev"] !== void 0 ? Boolean(flags["in-dev"]) : void 0;
|
|
4878
4933
|
const flag = flags["flag"];
|
|
4879
4934
|
const isForce = Boolean(flags["force"]);
|