backlog.md 0.1.0 → 0.1.1
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/bin/backlog-darwin-arm64/backlog +0 -0
- package/bin/backlog-darwin-x64/backlog +0 -0
- package/bin/backlog-linux-arm64/backlog +0 -0
- package/{cli → bin/backlog-linux-x64}/backlog +0 -0
- package/bin/backlog-win32-x64/backlog.exe +0 -0
- package/cli.js +62 -0
- package/package.json +57 -46
- package/.backlog/archive/drafts/readme.md +0 -3
- package/.backlog/archive/drafts/task-41 - temporary-test-task.md +0 -13
- package/.backlog/archive/readme.md +0 -6
- package/.backlog/archive/tasks/readme.md +0 -3
- package/.backlog/archive/tasks/task-41 - cli-migrate-terminal-ui-to-bblessed.md +0 -14
- package/.backlog/config.yml +0 -7
- package/.backlog/decisions/readme.md +0 -7
- package/.backlog/docs/readme.md +0 -20
- package/.backlog/drafts/readme.md +0 -3
- package/.backlog/drafts/task-26 - docs-add-board-export-step-to-agent-dod.md +0 -21
- package/.backlog/drafts/task-28 - add-code-of-conduct.md +0 -20
- package/.backlog/drafts/task-30 - create-changelog.md +0 -19
- package/.backlog/milestones/m-0 - project-setup.md +0 -8
- package/.backlog/milestones/m-1 - cli.md +0 -8
- package/.backlog/milestones/m-2 - cli-kanban.md +0 -8
- package/.backlog/milestones/m-3 - gui.md +0 -8
- package/.backlog/milestones/m-4 - gui-kanban.md +0 -8
- package/.backlog/milestones/m-5 - gui-advanced.md +0 -12
- package/.backlog/milestones/readme.md +0 -3
- package/.backlog/readme.md +0 -5
- package/.backlog/tasks/readme.md +0 -37
- package/.backlog/tasks/task-1 - cli-setup-core-project.md +0 -23
- package/.backlog/tasks/task-10 - gui-init-packaging.md +0 -23
- package/.backlog/tasks/task-11 - gui-kanban-board.md +0 -26
- package/.backlog/tasks/task-12 - gui-advanced.md +0 -25
- package/.backlog/tasks/task-13 - cli-add-agent-instruction-prompt.md +0 -53
- package/.backlog/tasks/task-13.1 - cli-agent-instruction-file-selection.md +0 -40
- package/.backlog/tasks/task-14 - gui-introduction-screens.md +0 -21
- package/.backlog/tasks/task-15 - improve-tasks-readme-with-generic-example-and-cli-reference.md +0 -20
- package/.backlog/tasks/task-16 - improve-docs-readme-with-generic-example-and-cli-reference.md +0 -20
- package/.backlog/tasks/task-17 - improve-drafts-readme-with-generic-example-and-cli-reference.md +0 -20
- package/.backlog/tasks/task-18 - improve-decisions-readme-with-generic-example-and-cli-reference.md +0 -20
- package/.backlog/tasks/task-19 - cli-fix-default-task-status-and-remove-draft-from-statuses.md +0 -55
- package/.backlog/tasks/task-2 - cli-core-logic-library.md +0 -28
- package/.backlog/tasks/task-20 - add-agent-guideline-to-mark-tasks-in-progress-on-start.md +0 -32
- package/.backlog/tasks/task-21 - kanban-board-vertical-layout.md +0 -31
- package/.backlog/tasks/task-22 - cli-prevent-double-dash-in-task-filenames.md +0 -24
- package/.backlog/tasks/task-23 - cli-kanban-board-order-tasks-by-id-asc.md +0 -30
- package/.backlog/tasks/task-24 - handle-subtasks-in-the-kanban-view.md +0 -38
- package/.backlog/tasks/task-24.1 - cli-kanban-board-milestone-view.md +0 -19
- package/.backlog/tasks/task-25 - cli-export-kanban-board-to-readme.md +0 -28
- package/.backlog/tasks/task-27 - add-contributing-guidelines.md +0 -27
- package/.backlog/tasks/task-29 - add-github-templates.md +0 -28
- package/.backlog/tasks/task-3 - cli-implement-backlog-init.md +0 -63
- package/.backlog/tasks/task-31 - update-readme-for-open-source.md +0 -26
- package/.backlog/tasks/task-32 - cli-hide-empty-'no-status'-column.md +0 -31
- package/.backlog/tasks/task-33 - cli-export-milestones-board-as-roadmap.md +0 -20
- package/.backlog/tasks/task-34 - split-readme.md-for-users-and-contributors.md +0 -26
- package/.backlog/tasks/task-35 - finalize-package.json-metadata-for-publishing.md +0 -24
- package/.backlog/tasks/task-36 - cli-prompt-for-project-name-in-init.md +0 -24
- package/.backlog/tasks/task-37 - cli-board-view-open-tasks-in-ide.md +0 -19
- package/.backlog/tasks/task-38 - cli-improved-agent-selection-for-init.md +0 -25
- package/.backlog/tasks/task-39 - cli-fix-empty-agent-instruction-files-on-init.md +0 -31
- package/.backlog/tasks/task-4 - cli-task-management-commands.md +0 -28
- package/.backlog/tasks/task-4.1 - cli-task-create.md +0 -27
- package/.backlog/tasks/task-4.10 - use-cli-to-mark-tasks-done.md +0 -51
- package/.backlog/tasks/task-4.11 - docs-add-definition-of-done-to-agent-guidelines.md +0 -23
- package/.backlog/tasks/task-4.12 - cli-handle-task-id-conflicts-across-branches.md +0 -53
- package/.backlog/tasks/task-4.13 - cli-fix-config-command-local-global-logic.md +0 -58
- package/.backlog/tasks/task-4.2 - cli-task-list-view.md +0 -25
- package/.backlog/tasks/task-4.3 - cli-task-edit.md +0 -24
- package/.backlog/tasks/task-4.4 - cli-task-archive-transition.md +0 -27
- package/.backlog/tasks/task-4.5 - cli-init-prompts-for-reporter-name-and-global-local-config.md +0 -28
- package/.backlog/tasks/task-4.6 - cli-add-empty-assignee-array-field-for-new-tasks.md +0 -35
- package/.backlog/tasks/task-4.7 - cli-parse-unquoted-created_date.md +0 -40
- package/.backlog/tasks/task-4.8 - cli-enforce-description-header.md +0 -48
- package/.backlog/tasks/task-4.9 - cli-normalize-task-id-inputs.md +0 -66
- package/.backlog/tasks/task-40 - cli-board-command-defaults-to-view.md +0 -38
- package/.backlog/tasks/task-41 - cli-migrate-terminal-ui-to-bblessed.md +0 -93
- package/.backlog/tasks/task-41.1 - cli-bblessed-init-wizard.md +0 -42
- package/.backlog/tasks/task-41.2 - cli-bblessed-task-view.md +0 -44
- package/.backlog/tasks/task-41.3 - cli-bblessed-doc-view.md +0 -45
- package/.backlog/tasks/task-41.4 - cli-bblessed-board-view.md +0 -49
- package/.backlog/tasks/task-41.5 - cli-audit-remaining-ui-for-bblessed.md +0 -55
- package/.backlog/tasks/task-42 - visual-hierarchy.md +0 -54
- package/.backlog/tasks/task-43 - remove-duplicate-acceptance-criteria-and-style-metadata.md +0 -56
- package/.backlog/tasks/task-44 - checklist-alignment.md +0 -24
- package/.backlog/tasks/task-45 - safe-line-wrapping.md +0 -23
- package/.backlog/tasks/task-46 - split-pane-layout.md +0 -24
- package/.backlog/tasks/task-47 - sticky-header-in-detail-view.md +0 -43
- package/.backlog/tasks/task-48 - footer-hint-line.md +0 -21
- package/.backlog/tasks/task-49 - status-styling.md +0 -53
- package/.backlog/tasks/task-5 - cli-docs-decisions.md +0 -57
- package/.backlog/tasks/task-50 - borders-&-padding.md +0 -22
- package/.backlog/tasks/task-51 - code-path-styling.md +0 -23
- package/.backlog/tasks/task-52 - cli-filter-tasks-list-by-status-or-assignee.md +0 -29
- package/.backlog/tasks/task-6 - cli-packaging.md +0 -65
- package/.backlog/tasks/task-6.1 - cli-local-installation-support-for-bunx-npx.md +0 -49
- package/.backlog/tasks/task-6.2 - cli-github-actions-for-build-&-publish.md +0 -64
- package/.backlog/tasks/task-7 - cli-kanban-view.md +0 -60
- package/.backlog/tasks/task-7.1 - cli-kanban-board-detect-remote-task-status.md +0 -62
- package/.backlog/tasks/task-8 - gui-project-setup.md +0 -21
- package/.backlog/tasks/task-9 - gui-task-crud.md +0 -24
- package/.cursorrules +0 -223
- package/.gitattributes +0 -2
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -25
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -15
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -8
- package/.github/workflows/ci.yml +0 -36
- package/.husky/pre-commit +0 -1
- package/AGENTS.md +0 -65
- package/CLAUDE.md +0 -87
- package/CONTRIBUTING.md +0 -19
- package/DEVELOPMENT.md +0 -37
- package/biome.json +0 -31
- package/bun.lock +0 -152
- package/cli/.cursorrules-xh86jabm.md +0 -82
- package/cli/AGENTS-xh86jabm.md +0 -82
- package/cli/CLAUDE-xh86jabm.md +0 -82
- package/cli/cli.js +0 -19622
- package/cli/index.js +0 -2
- package/docs/npm-publishing.md +0 -69
- package/scripts/build.js +0 -73
- package/src/agent-instructions.ts +0 -54
- package/src/board.ts +0 -263
- package/src/cli.ts +0 -806
- package/src/constants/index.ts +0 -48
- package/src/core/backlog.ts +0 -183
- package/src/core/remote-tasks.ts +0 -168
- package/src/file-system/operations.ts +0 -515
- package/src/git/operations.ts +0 -189
- package/src/guidelines/.cursorrules.md +0 -82
- package/src/guidelines/AGENTS.md +0 -82
- package/src/guidelines/CLAUDE.md +0 -82
- package/src/guidelines/index.ts +0 -7
- package/src/index.ts +0 -30
- package/src/markdown/parser.ts +0 -145
- package/src/markdown/serializer.ts +0 -71
- package/src/test/agent-instructions.test.ts +0 -62
- package/src/test/board.test.ts +0 -291
- package/src/test/build.test.ts +0 -28
- package/src/test/checklist.test.ts +0 -273
- package/src/test/cli.test.ts +0 -1300
- package/src/test/code-path.test.ts +0 -204
- package/src/test/core.test.ts +0 -330
- package/src/test/filesystem.test.ts +0 -435
- package/src/test/git.test.ts +0 -26
- package/src/test/heading.test.ts +0 -102
- package/src/test/line-wrapping.test.ts +0 -252
- package/src/test/local-install.test.ts +0 -34
- package/src/test/markdown.test.ts +0 -526
- package/src/test/parallel-loading.test.ts +0 -160
- package/src/test/parent-id-normalization.test.ts +0 -48
- package/src/test/remote-id-conflict.test.ts +0 -60
- package/src/test/status-icon.test.ts +0 -93
- package/src/types/blessed.d.ts +0 -14
- package/src/types/index.ts +0 -55
- package/src/types/raw.d.ts +0 -4
- package/src/ui/board.ts +0 -322
- package/src/ui/checklist.ts +0 -103
- package/src/ui/code-path.ts +0 -113
- package/src/ui/heading.ts +0 -121
- package/src/ui/loading.ts +0 -216
- package/src/ui/status-icon.ts +0 -53
- package/src/ui/task-list.ts +0 -168
- package/src/ui/task-viewer.ts +0 -640
- package/src/ui/tui.ts +0 -301
- package/tsconfig.json +0 -26
package/src/ui/checklist.ts
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
/* Checklist alignment utilities for consistent checkbox display */
|
|
2
|
-
|
|
3
|
-
export interface ChecklistItem {
|
|
4
|
-
text: string;
|
|
5
|
-
checked: boolean;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Regex patterns for detecting checkbox markdown
|
|
10
|
-
*/
|
|
11
|
-
export const CHECKBOX_PATTERNS = {
|
|
12
|
-
// Matches "- [ ] text" or "- [x] text" with optional leading whitespace
|
|
13
|
-
CHECKBOX_LINE: /^\s*-\s*\[([ x])\]\s*(.*)$/,
|
|
14
|
-
// Matches just the checkbox part
|
|
15
|
-
CHECKBOX_PREFIX: /^-\s*\[([ x])\]\s*/,
|
|
16
|
-
} as const;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Parse a line to extract checkbox state and text
|
|
20
|
-
*/
|
|
21
|
-
export function parseCheckboxLine(line: string): ChecklistItem | null {
|
|
22
|
-
const match = line.match(CHECKBOX_PATTERNS.CHECKBOX_LINE);
|
|
23
|
-
if (!match) return null;
|
|
24
|
-
|
|
25
|
-
const [, checkState, text] = match;
|
|
26
|
-
return {
|
|
27
|
-
text: text.trim(),
|
|
28
|
-
checked: checkState === "x",
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Format a checklist item with aligned checkbox display
|
|
34
|
-
*/
|
|
35
|
-
export function formatChecklistItem(
|
|
36
|
-
item: ChecklistItem,
|
|
37
|
-
options: {
|
|
38
|
-
padding?: string;
|
|
39
|
-
checkedSymbol?: string;
|
|
40
|
-
uncheckedSymbol?: string;
|
|
41
|
-
} = {},
|
|
42
|
-
): string {
|
|
43
|
-
const { padding = " ", checkedSymbol = "[x]", uncheckedSymbol = "[ ]" } = options;
|
|
44
|
-
|
|
45
|
-
const checkbox = item.checked ? checkedSymbol : uncheckedSymbol;
|
|
46
|
-
return `${padding}${checkbox} ${item.text}`;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Process acceptance criteria section and align checkboxes
|
|
51
|
-
*/
|
|
52
|
-
export function alignAcceptanceCriteria(criteriaSection: string): string[] {
|
|
53
|
-
if (!criteriaSection) return [];
|
|
54
|
-
|
|
55
|
-
return criteriaSection
|
|
56
|
-
.split("\n")
|
|
57
|
-
.map((line) => line.trim())
|
|
58
|
-
.filter((line) => line.length > 0)
|
|
59
|
-
.map((line) => {
|
|
60
|
-
const item = parseCheckboxLine(line);
|
|
61
|
-
if (item) {
|
|
62
|
-
return formatChecklistItem(item);
|
|
63
|
-
}
|
|
64
|
-
// Return non-checkbox lines as-is with minimal padding
|
|
65
|
-
return ` ${line}`;
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Extract and format acceptance criteria from markdown content
|
|
71
|
-
*/
|
|
72
|
-
export function extractAndFormatAcceptanceCriteria(content: string): string[] {
|
|
73
|
-
const criteriaSection = extractSection(content, "Acceptance Criteria");
|
|
74
|
-
if (!criteriaSection) return [];
|
|
75
|
-
|
|
76
|
-
return alignAcceptanceCriteria(criteriaSection);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Extract a section from markdown content
|
|
81
|
-
*/
|
|
82
|
-
function extractSection(content: string, sectionTitle: string): string | undefined {
|
|
83
|
-
const regex = new RegExp(`## ${sectionTitle}\\s*\\n([\\s\\S]*?)(?=\\n## |$)`, "i");
|
|
84
|
-
const match = content.match(regex);
|
|
85
|
-
return match?.[1]?.trim();
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Format multiple checklist items with consistent alignment
|
|
90
|
-
*/
|
|
91
|
-
export function formatChecklist(items: ChecklistItem[]): string[] {
|
|
92
|
-
return items.map((item) => formatChecklistItem(item));
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Parse multiple checkbox lines from text
|
|
97
|
-
*/
|
|
98
|
-
export function parseCheckboxLines(text: string): ChecklistItem[] {
|
|
99
|
-
return text
|
|
100
|
-
.split("\n")
|
|
101
|
-
.map((line) => parseCheckboxLine(line))
|
|
102
|
-
.filter((item): item is ChecklistItem => item !== null);
|
|
103
|
-
}
|
package/src/ui/code-path.ts
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
/* Code path detection and styling utilities */
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Regex patterns for detecting code paths in backticks
|
|
5
|
-
*/
|
|
6
|
-
export const CODE_PATH_PATTERNS = {
|
|
7
|
-
// Matches `src/cli.ts`, `package.json`, `/full/path/file.ts`
|
|
8
|
-
BACKTICKED_PATH: /`([^`]+)`/g,
|
|
9
|
-
// Matches file extensions
|
|
10
|
-
FILE_EXTENSION: /\.[a-zA-Z0-9]+$/,
|
|
11
|
-
// Matches path separators
|
|
12
|
-
PATH_SEPARATOR: /[\/\\]/,
|
|
13
|
-
} as const;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Detect if a backticked string is likely a file path
|
|
17
|
-
*/
|
|
18
|
-
export function isCodePath(content: string): boolean {
|
|
19
|
-
// Has file extension OR contains path separator
|
|
20
|
-
return CODE_PATH_PATTERNS.FILE_EXTENSION.test(content) || CODE_PATH_PATTERNS.PATH_SEPARATOR.test(content);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Extract all code paths from text
|
|
25
|
-
*/
|
|
26
|
-
export function extractCodePaths(text: string): string[] {
|
|
27
|
-
const matches = text.match(CODE_PATH_PATTERNS.BACKTICKED_PATH);
|
|
28
|
-
if (!matches) return [];
|
|
29
|
-
|
|
30
|
-
return matches
|
|
31
|
-
.map((match) => match.slice(1, -1)) // Remove backticks
|
|
32
|
-
.filter(isCodePath);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Style a code path for blessed display
|
|
37
|
-
*/
|
|
38
|
-
export function styleCodePath(path: string): string {
|
|
39
|
-
return `{gray-fg}\`${path}\`{/gray-fg}`;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Transform text to style code paths and place them on separate lines
|
|
44
|
-
*/
|
|
45
|
-
export function transformCodePaths(text: string): string {
|
|
46
|
-
if (!text) return text;
|
|
47
|
-
|
|
48
|
-
// Split into lines to preserve existing line breaks
|
|
49
|
-
const lines = text.split("\n");
|
|
50
|
-
const result: string[] = [];
|
|
51
|
-
|
|
52
|
-
for (const line of lines) {
|
|
53
|
-
let transformedLine = line;
|
|
54
|
-
const codePaths = extractCodePaths(line);
|
|
55
|
-
|
|
56
|
-
if (codePaths.length === 0) {
|
|
57
|
-
// No code paths, add line as-is
|
|
58
|
-
result.push(transformedLine);
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Check if line contains only a code path (possibly with minimal surrounding text)
|
|
63
|
-
const lineWithoutPaths = line.replace(/`[^`]+`/g, "").trim();
|
|
64
|
-
const isIsolatedPath = codePaths.length === 1 && lineWithoutPaths.length < 10;
|
|
65
|
-
|
|
66
|
-
if (isIsolatedPath) {
|
|
67
|
-
// Style the code path in place
|
|
68
|
-
for (const path of codePaths) {
|
|
69
|
-
transformedLine = transformedLine.replace(`\`${path}\``, styleCodePath(path));
|
|
70
|
-
}
|
|
71
|
-
result.push(transformedLine);
|
|
72
|
-
} else {
|
|
73
|
-
// Extract code paths to separate lines
|
|
74
|
-
let workingLine = transformedLine;
|
|
75
|
-
const pathsToExtract: string[] = [];
|
|
76
|
-
|
|
77
|
-
for (const path of codePaths) {
|
|
78
|
-
const backticked = `\`${path}\``;
|
|
79
|
-
if (workingLine.includes(backticked)) {
|
|
80
|
-
// Remove from line and collect for separate placement, clean up extra spaces
|
|
81
|
-
workingLine = workingLine.replace(backticked, " ").replace(/\s+/g, " ").trim();
|
|
82
|
-
pathsToExtract.push(path);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Add the line without code paths (if not empty)
|
|
87
|
-
if (workingLine.length > 0) {
|
|
88
|
-
result.push(workingLine);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Add each code path on its own line
|
|
92
|
-
for (const path of pathsToExtract) {
|
|
93
|
-
result.push(styleCodePath(path));
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return result.join("\n");
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Simple styling for plain text (without blessed tags)
|
|
103
|
-
*/
|
|
104
|
-
export function transformCodePathsPlain(text: string): string {
|
|
105
|
-
if (!text) return text;
|
|
106
|
-
|
|
107
|
-
return text.replace(CODE_PATH_PATTERNS.BACKTICKED_PATH, (match, path) => {
|
|
108
|
-
if (isCodePath(path)) {
|
|
109
|
-
return `\`${path}\``;
|
|
110
|
-
}
|
|
111
|
-
return match;
|
|
112
|
-
});
|
|
113
|
-
}
|
package/src/ui/heading.ts
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
/* Heading helper component for consistent terminal UI styling */
|
|
2
|
-
|
|
3
|
-
import { createRequire } from "node:module";
|
|
4
|
-
|
|
5
|
-
// Load blessed dynamically
|
|
6
|
-
// biome-ignore lint/suspicious/noExplicitAny: blessed is dynamically loaded
|
|
7
|
-
async function loadBlessed(): Promise<any | null> {
|
|
8
|
-
try {
|
|
9
|
-
const require = createRequire(import.meta.url);
|
|
10
|
-
const blessed = require("blessed");
|
|
11
|
-
return blessed;
|
|
12
|
-
} catch {
|
|
13
|
-
try {
|
|
14
|
-
// biome-ignore lint/suspicious/noExplicitAny: dynamic import
|
|
15
|
-
const mod = (await import("blessed")) as any;
|
|
16
|
-
return mod.default ?? mod;
|
|
17
|
-
} catch {
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export type HeadingLevel = 1 | 2 | 3;
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Get the styling for a heading level
|
|
27
|
-
*/
|
|
28
|
-
export function getHeadingStyle(level: HeadingLevel): { color: string; bold: boolean } {
|
|
29
|
-
switch (level) {
|
|
30
|
-
case 1:
|
|
31
|
-
return { color: "bright-white", bold: true };
|
|
32
|
-
case 2:
|
|
33
|
-
return { color: "cyan", bold: false };
|
|
34
|
-
case 3:
|
|
35
|
-
return { color: "white", bold: false };
|
|
36
|
-
default:
|
|
37
|
-
return { color: "white", bold: false };
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Format heading text with appropriate blessed tags
|
|
43
|
-
*/
|
|
44
|
-
export function formatHeading(text: string, level: HeadingLevel): string {
|
|
45
|
-
const style = getHeadingStyle(level);
|
|
46
|
-
const colorTag = style.color.replace("-", "");
|
|
47
|
-
|
|
48
|
-
if (style.bold) {
|
|
49
|
-
return `{bold}{${colorTag}-fg}${text}{/${colorTag}-fg}{/bold}`;
|
|
50
|
-
}
|
|
51
|
-
return `{${colorTag}-fg}${text}{/${colorTag}-fg}`;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Create a heading box with proper styling and spacing
|
|
56
|
-
*/
|
|
57
|
-
export async function createHeading(
|
|
58
|
-
// biome-ignore lint/suspicious/noExplicitAny: blessed types
|
|
59
|
-
parent: any,
|
|
60
|
-
text: string,
|
|
61
|
-
level: HeadingLevel,
|
|
62
|
-
options: {
|
|
63
|
-
top?: number | string;
|
|
64
|
-
left?: number | string;
|
|
65
|
-
width?: number | string;
|
|
66
|
-
} = {},
|
|
67
|
-
// biome-ignore lint/suspicious/noExplicitAny: blessed element type
|
|
68
|
-
): Promise<any | null> {
|
|
69
|
-
const blessed = await loadBlessed();
|
|
70
|
-
if (!blessed) return null;
|
|
71
|
-
|
|
72
|
-
const style = getHeadingStyle(level);
|
|
73
|
-
|
|
74
|
-
return blessed.box({
|
|
75
|
-
parent,
|
|
76
|
-
content: formatHeading(text, level),
|
|
77
|
-
top: options.top || 0,
|
|
78
|
-
left: options.left || 0,
|
|
79
|
-
width: options.width || "100%",
|
|
80
|
-
height: 1,
|
|
81
|
-
tags: true,
|
|
82
|
-
style: {
|
|
83
|
-
fg: style.color,
|
|
84
|
-
bold: style.bold,
|
|
85
|
-
},
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Add a heading with automatic spacing (blank line before)
|
|
91
|
-
*/
|
|
92
|
-
export async function addHeadingWithSpacing(
|
|
93
|
-
// biome-ignore lint/suspicious/noExplicitAny: blessed types
|
|
94
|
-
parent: any,
|
|
95
|
-
text: string,
|
|
96
|
-
level: HeadingLevel,
|
|
97
|
-
currentTop: number,
|
|
98
|
-
options: {
|
|
99
|
-
left?: number | string;
|
|
100
|
-
width?: number | string;
|
|
101
|
-
} = {},
|
|
102
|
-
// biome-ignore lint/suspicious/noExplicitAny: blessed element type
|
|
103
|
-
): Promise<{ element: any; nextTop: number }> {
|
|
104
|
-
const blessed = await loadBlessed();
|
|
105
|
-
if (!blessed) return { element: null, nextTop: currentTop };
|
|
106
|
-
|
|
107
|
-
// Add blank line before heading (except if it's the very first element)
|
|
108
|
-
const actualTop = currentTop === 0 ? 0 : currentTop + 1;
|
|
109
|
-
|
|
110
|
-
const heading = await createHeading(parent, text, level, {
|
|
111
|
-
top: actualTop,
|
|
112
|
-
left: options.left,
|
|
113
|
-
width: options.width,
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
// Return next available position (heading + 1 line for spacing after)
|
|
117
|
-
return {
|
|
118
|
-
element: heading,
|
|
119
|
-
nextTop: actualTop + 1,
|
|
120
|
-
};
|
|
121
|
-
}
|
package/src/ui/loading.ts
DELETED
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Loading screen utilities for blessed-based terminal UI
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { createRequire } from "node:module";
|
|
6
|
-
|
|
7
|
-
// biome-ignore lint/suspicious/noExplicitAny: blessed is dynamically loaded
|
|
8
|
-
async function loadBlessed(): Promise<any | null> {
|
|
9
|
-
try {
|
|
10
|
-
const require = createRequire(import.meta.url);
|
|
11
|
-
return require("blessed");
|
|
12
|
-
} catch {
|
|
13
|
-
try {
|
|
14
|
-
// biome-ignore lint/suspicious/noExplicitAny: dynamic import
|
|
15
|
-
const mod = await import("blessed" as any);
|
|
16
|
-
return mod.default ?? mod;
|
|
17
|
-
} catch {
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface LoadingScreen {
|
|
24
|
-
update: (message: string) => void;
|
|
25
|
-
close: () => void;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Show a loading screen while an async operation runs.
|
|
30
|
-
* Falls back to console.log if blessed is not available.
|
|
31
|
-
*/
|
|
32
|
-
export async function withLoadingScreen<T>(message: string, operation: () => Promise<T>): Promise<T> {
|
|
33
|
-
const blessed = await loadBlessed();
|
|
34
|
-
|
|
35
|
-
if (!blessed) {
|
|
36
|
-
// Fallback: just log the message
|
|
37
|
-
console.log(`${message}...`);
|
|
38
|
-
return operation();
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const screen = blessed.screen({
|
|
42
|
-
smartCSR: true,
|
|
43
|
-
title: "Loading...",
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
// Create loading box
|
|
47
|
-
const loadingBox = blessed.box({
|
|
48
|
-
parent: screen,
|
|
49
|
-
top: "center",
|
|
50
|
-
left: "center",
|
|
51
|
-
width: "50%",
|
|
52
|
-
height: 7,
|
|
53
|
-
border: "line",
|
|
54
|
-
label: " Loading ",
|
|
55
|
-
padding: 1,
|
|
56
|
-
style: {
|
|
57
|
-
border: { fg: "cyan" },
|
|
58
|
-
},
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
// Loading message
|
|
62
|
-
const messageText = blessed.text({
|
|
63
|
-
parent: loadingBox,
|
|
64
|
-
top: 0,
|
|
65
|
-
left: "center",
|
|
66
|
-
content: message,
|
|
67
|
-
style: { fg: "white" },
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
// Spinner
|
|
71
|
-
const spinnerChars = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
72
|
-
let spinnerIndex = 0;
|
|
73
|
-
|
|
74
|
-
const spinner = blessed.text({
|
|
75
|
-
parent: loadingBox,
|
|
76
|
-
top: 2,
|
|
77
|
-
left: "center",
|
|
78
|
-
content: spinnerChars[0],
|
|
79
|
-
style: { fg: "cyan" },
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
// Start spinner animation
|
|
83
|
-
const spinnerInterval = setInterval(() => {
|
|
84
|
-
spinnerIndex = (spinnerIndex + 1) % spinnerChars.length;
|
|
85
|
-
spinner.setContent(spinnerChars[spinnerIndex]);
|
|
86
|
-
screen.render();
|
|
87
|
-
}, 100);
|
|
88
|
-
|
|
89
|
-
// Allow escape to cancel (though operation continues)
|
|
90
|
-
let cancelled = false;
|
|
91
|
-
screen.key(["escape", "C-c"], () => {
|
|
92
|
-
cancelled = true;
|
|
93
|
-
clearInterval(spinnerInterval);
|
|
94
|
-
screen.destroy();
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
screen.render();
|
|
98
|
-
|
|
99
|
-
try {
|
|
100
|
-
const result = await operation();
|
|
101
|
-
|
|
102
|
-
// Clean up
|
|
103
|
-
clearInterval(spinnerInterval);
|
|
104
|
-
if (!cancelled) {
|
|
105
|
-
screen.destroy();
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return result;
|
|
109
|
-
} catch (error) {
|
|
110
|
-
// Clean up on error
|
|
111
|
-
clearInterval(spinnerInterval);
|
|
112
|
-
if (!cancelled) {
|
|
113
|
-
screen.destroy();
|
|
114
|
-
}
|
|
115
|
-
throw error;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Create a loading screen that can be updated with progress messages.
|
|
121
|
-
* Useful for multi-step operations.
|
|
122
|
-
*/
|
|
123
|
-
export async function createLoadingScreen(initialMessage: string): Promise<LoadingScreen | null> {
|
|
124
|
-
const blessed = await loadBlessed();
|
|
125
|
-
|
|
126
|
-
if (!blessed) {
|
|
127
|
-
// Fallback: return a simple console logger
|
|
128
|
-
console.log(`${initialMessage}...`);
|
|
129
|
-
return {
|
|
130
|
-
update: (msg) => console.log(` ${msg}...`),
|
|
131
|
-
close: () => {},
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const screen = blessed.screen({
|
|
136
|
-
smartCSR: true,
|
|
137
|
-
title: "Loading...",
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
const loadingBox = blessed.box({
|
|
141
|
-
parent: screen,
|
|
142
|
-
top: "center",
|
|
143
|
-
left: "center",
|
|
144
|
-
width: "60%",
|
|
145
|
-
height: 10,
|
|
146
|
-
border: "line",
|
|
147
|
-
label: " Loading ",
|
|
148
|
-
padding: 1,
|
|
149
|
-
scrollable: true,
|
|
150
|
-
alwaysScroll: true,
|
|
151
|
-
style: {
|
|
152
|
-
border: { fg: "cyan" },
|
|
153
|
-
},
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
// Progress messages area
|
|
157
|
-
const messages = blessed.log({
|
|
158
|
-
parent: loadingBox,
|
|
159
|
-
top: 0,
|
|
160
|
-
left: 0,
|
|
161
|
-
width: "100%-2",
|
|
162
|
-
height: "100%-2",
|
|
163
|
-
tags: true,
|
|
164
|
-
style: { fg: "white" },
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
// Add initial message
|
|
168
|
-
messages.log(initialMessage);
|
|
169
|
-
|
|
170
|
-
// Spinner at bottom
|
|
171
|
-
const spinnerChars = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
172
|
-
let spinnerIndex = 0;
|
|
173
|
-
|
|
174
|
-
const spinner = blessed.text({
|
|
175
|
-
parent: screen,
|
|
176
|
-
bottom: 1,
|
|
177
|
-
left: "center",
|
|
178
|
-
content: spinnerChars[0],
|
|
179
|
-
style: { fg: "cyan" },
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
// Start spinner animation
|
|
183
|
-
const spinnerInterval = setInterval(() => {
|
|
184
|
-
spinnerIndex = (spinnerIndex + 1) % spinnerChars.length;
|
|
185
|
-
spinner.setContent(spinnerChars[spinnerIndex]);
|
|
186
|
-
screen.render();
|
|
187
|
-
}, 100);
|
|
188
|
-
|
|
189
|
-
// Allow escape to close
|
|
190
|
-
let closed = false;
|
|
191
|
-
screen.key(["escape", "C-c"], () => {
|
|
192
|
-
if (!closed) {
|
|
193
|
-
closed = true;
|
|
194
|
-
clearInterval(spinnerInterval);
|
|
195
|
-
screen.destroy();
|
|
196
|
-
}
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
screen.render();
|
|
200
|
-
|
|
201
|
-
return {
|
|
202
|
-
update: (message: string) => {
|
|
203
|
-
if (!closed) {
|
|
204
|
-
messages.log(message);
|
|
205
|
-
screen.render();
|
|
206
|
-
}
|
|
207
|
-
},
|
|
208
|
-
close: () => {
|
|
209
|
-
if (!closed) {
|
|
210
|
-
closed = true;
|
|
211
|
-
clearInterval(spinnerInterval);
|
|
212
|
-
screen.destroy();
|
|
213
|
-
}
|
|
214
|
-
},
|
|
215
|
-
};
|
|
216
|
-
}
|
package/src/ui/status-icon.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/* Status icon and color mappings for consistent UI display */
|
|
2
|
-
|
|
3
|
-
export interface StatusStyle {
|
|
4
|
-
icon: string;
|
|
5
|
-
color: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Get the icon and color for a given status
|
|
10
|
-
* @param status - The task status
|
|
11
|
-
* @returns The icon and color for the status
|
|
12
|
-
*/
|
|
13
|
-
export function getStatusStyle(status: string): StatusStyle {
|
|
14
|
-
const statusMap: Record<string, StatusStyle> = {
|
|
15
|
-
Done: { icon: "✔", color: "green" },
|
|
16
|
-
"In Progress": { icon: "◒", color: "yellow" },
|
|
17
|
-
Blocked: { icon: "●", color: "red" },
|
|
18
|
-
"To Do": { icon: "○", color: "white" },
|
|
19
|
-
Review: { icon: "◆", color: "blue" },
|
|
20
|
-
Testing: { icon: "▣", color: "cyan" },
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
// Return the mapped style or default for unknown statuses
|
|
24
|
-
return statusMap[status] || { icon: "○", color: "white" };
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Get just the color for a status (for backward compatibility)
|
|
29
|
-
* @param status - The task status
|
|
30
|
-
* @returns The color for the status
|
|
31
|
-
*/
|
|
32
|
-
export function getStatusColor(status: string): string {
|
|
33
|
-
return getStatusStyle(status).color;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Get just the icon for a status
|
|
38
|
-
* @param status - The task status
|
|
39
|
-
* @returns The icon for the status
|
|
40
|
-
*/
|
|
41
|
-
export function getStatusIcon(status: string): string {
|
|
42
|
-
return getStatusStyle(status).icon;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Format a status with its icon
|
|
47
|
-
* @param status - The task status
|
|
48
|
-
* @returns The formatted status string with icon
|
|
49
|
-
*/
|
|
50
|
-
export function formatStatusWithIcon(status: string): string {
|
|
51
|
-
const style = getStatusStyle(status);
|
|
52
|
-
return `${style.icon} ${status}`;
|
|
53
|
-
}
|