135 lines
3.8 KiB
TypeScript
135 lines
3.8 KiB
TypeScript
import type { Task } from "../types/index.ts";
|
|
import type { ChecklistItem } from "../ui/checklist.ts";
|
|
import { transformCodePathsPlain } from "../ui/code-path.ts";
|
|
import { formatStatusWithIcon } from "../ui/status-icon.ts";
|
|
|
|
export type TaskPlainTextOptions = {
|
|
filePathOverride?: string;
|
|
};
|
|
|
|
export function formatDateForDisplay(dateStr: string): string {
|
|
if (!dateStr) return "";
|
|
const hasTime = dateStr.includes(" ") || dateStr.includes("T");
|
|
return hasTime ? dateStr : dateStr;
|
|
}
|
|
|
|
export function buildAcceptanceCriteriaItems(task: Task): ChecklistItem[] {
|
|
const items = task.acceptanceCriteriaItems ?? [];
|
|
return items
|
|
.slice()
|
|
.sort((a, b) => a.index - b.index)
|
|
.map((criterion, index) => ({
|
|
text: `#${index + 1} ${criterion.text}`,
|
|
checked: criterion.checked,
|
|
}));
|
|
}
|
|
|
|
export function formatAcceptanceCriteriaLines(items: ChecklistItem[]): string[] {
|
|
if (items.length === 0) return [];
|
|
return items.map((item) => {
|
|
const prefix = item.checked ? "- [x]" : "- [ ]";
|
|
return `${prefix} ${transformCodePathsPlain(item.text)}`;
|
|
});
|
|
}
|
|
|
|
function formatPriority(priority?: "high" | "medium" | "low"): string | null {
|
|
if (!priority) return null;
|
|
const label = priority.charAt(0).toUpperCase() + priority.slice(1);
|
|
return label;
|
|
}
|
|
|
|
function formatAssignees(assignee?: string[]): string | null {
|
|
if (!assignee || assignee.length === 0) return null;
|
|
return assignee.map((a) => (a.startsWith("@") ? a : `@${a}`)).join(", ");
|
|
}
|
|
|
|
export function formatTaskPlainText(task: Task, options: TaskPlainTextOptions = {}): string {
|
|
const lines: string[] = [];
|
|
const filePath = options.filePathOverride ?? task.filePath;
|
|
|
|
if (filePath) {
|
|
lines.push(`File: ${filePath}`);
|
|
lines.push("");
|
|
}
|
|
|
|
lines.push(`Task ${task.id} - ${task.title}`);
|
|
lines.push("=".repeat(50));
|
|
lines.push("");
|
|
lines.push(`Status: ${formatStatusWithIcon(task.status)}`);
|
|
|
|
const priorityLabel = formatPriority(task.priority);
|
|
if (priorityLabel) {
|
|
lines.push(`Priority: ${priorityLabel}`);
|
|
}
|
|
|
|
const assigneeText = formatAssignees(task.assignee);
|
|
if (assigneeText) {
|
|
lines.push(`Assignee: ${assigneeText}`);
|
|
}
|
|
|
|
if (task.reporter) {
|
|
const reporter = task.reporter.startsWith("@") ? task.reporter : `@${task.reporter}`;
|
|
lines.push(`Reporter: ${reporter}`);
|
|
}
|
|
|
|
lines.push(`Created: ${formatDateForDisplay(task.createdDate)}`);
|
|
if (task.updatedDate) {
|
|
lines.push(`Updated: ${formatDateForDisplay(task.updatedDate)}`);
|
|
}
|
|
|
|
if (task.labels?.length) {
|
|
lines.push(`Labels: ${task.labels.join(", ")}`);
|
|
}
|
|
|
|
if (task.milestone) {
|
|
lines.push(`Milestone: ${task.milestone}`);
|
|
}
|
|
|
|
if (task.parentTaskId) {
|
|
lines.push(`Parent: ${task.parentTaskId}`);
|
|
}
|
|
|
|
if (task.subtasks?.length) {
|
|
lines.push(`Subtasks: ${task.subtasks.length}`);
|
|
}
|
|
|
|
if (task.dependencies?.length) {
|
|
lines.push(`Dependencies: ${task.dependencies.join(", ")}`);
|
|
}
|
|
|
|
lines.push("");
|
|
lines.push("Description:");
|
|
lines.push("-".repeat(50));
|
|
const description = task.description?.trim();
|
|
lines.push(transformCodePathsPlain(description && description.length > 0 ? description : "No description provided"));
|
|
lines.push("");
|
|
|
|
lines.push("Acceptance Criteria:");
|
|
lines.push("-".repeat(50));
|
|
const criteriaItems = buildAcceptanceCriteriaItems(task);
|
|
if (criteriaItems.length > 0) {
|
|
lines.push(...formatAcceptanceCriteriaLines(criteriaItems));
|
|
} else {
|
|
lines.push("No acceptance criteria defined");
|
|
}
|
|
lines.push("");
|
|
|
|
const implementationPlan = task.implementationPlan?.trim();
|
|
if (implementationPlan) {
|
|
lines.push("Implementation Plan:");
|
|
lines.push("-".repeat(50));
|
|
lines.push(transformCodePathsPlain(implementationPlan));
|
|
lines.push("");
|
|
}
|
|
|
|
const implementationNotes = task.implementationNotes?.trim();
|
|
if (implementationNotes) {
|
|
lines.push("Implementation Notes:");
|
|
lines.push("-".repeat(50));
|
|
lines.push(transformCodePathsPlain(implementationNotes));
|
|
lines.push("");
|
|
}
|
|
|
|
return lines.join("\n");
|
|
}
|