project-roadmap-tracking 0.1.0 → 0.2.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/README.md +293 -24
- package/dist/commands/add.d.ts +2 -0
- package/dist/commands/add.js +39 -31
- package/dist/commands/complete.d.ts +2 -0
- package/dist/commands/complete.js +35 -12
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +63 -46
- package/dist/commands/list.d.ts +3 -0
- package/dist/commands/list.js +65 -62
- package/dist/commands/pass-test.d.ts +4 -1
- package/dist/commands/pass-test.js +36 -13
- package/dist/commands/show.d.ts +4 -1
- package/dist/commands/show.js +38 -59
- package/dist/commands/update.d.ts +3 -0
- package/dist/commands/update.js +77 -32
- package/dist/commands/validate.d.ts +4 -1
- package/dist/commands/validate.js +74 -32
- package/dist/errors/base.error.d.ts +21 -0
- package/dist/errors/base.error.js +35 -0
- package/dist/errors/circular-dependency.error.d.ts +8 -0
- package/dist/errors/circular-dependency.error.js +13 -0
- package/dist/errors/config-not-found.error.d.ts +7 -0
- package/dist/errors/config-not-found.error.js +12 -0
- package/dist/errors/index.d.ts +16 -0
- package/dist/errors/index.js +26 -0
- package/dist/errors/invalid-task.error.d.ts +7 -0
- package/dist/errors/invalid-task.error.js +12 -0
- package/dist/errors/roadmap-not-found.error.d.ts +7 -0
- package/dist/errors/roadmap-not-found.error.js +12 -0
- package/dist/errors/task-not-found.error.d.ts +7 -0
- package/dist/errors/task-not-found.error.js +12 -0
- package/dist/errors/validation.error.d.ts +16 -0
- package/dist/errors/validation.error.js +16 -0
- package/dist/repositories/config.repository.d.ts +76 -0
- package/dist/repositories/config.repository.js +282 -0
- package/dist/repositories/index.d.ts +2 -0
- package/dist/repositories/index.js +2 -0
- package/dist/repositories/roadmap.repository.d.ts +82 -0
- package/dist/repositories/roadmap.repository.js +201 -0
- package/dist/services/display.service.d.ts +182 -0
- package/dist/services/display.service.js +320 -0
- package/dist/services/error-handler.service.d.ts +114 -0
- package/dist/services/error-handler.service.js +169 -0
- package/dist/services/roadmap.service.d.ts +142 -0
- package/dist/services/roadmap.service.js +269 -0
- package/dist/services/task-dependency.service.d.ts +210 -0
- package/dist/services/task-dependency.service.js +371 -0
- package/dist/services/task-query.service.d.ts +123 -0
- package/dist/services/task-query.service.js +259 -0
- package/dist/services/task.service.d.ts +155 -0
- package/dist/services/task.service.js +233 -0
- package/dist/util/read-config.js +12 -2
- package/dist/util/read-roadmap.js +12 -2
- package/dist/util/types.d.ts +5 -0
- package/dist/util/update-task.js +2 -1
- package/dist/util/validate-task.js +6 -5
- package/oclif.manifest.json +128 -5
- package/package.json +28 -4
package/dist/commands/init.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Args, Command, Flags } from '@oclif/core';
|
|
2
2
|
import { mkdir, readdir, writeFile } from 'node:fs/promises';
|
|
3
|
+
import errorHandlerService from '../services/error-handler.service.js';
|
|
3
4
|
import { PRIORITY, STATUS, TASK_TYPE } from '../util/types.js';
|
|
4
5
|
export default class Init extends Command {
|
|
5
6
|
static args = {
|
|
@@ -13,6 +14,11 @@ export default class Init extends Command {
|
|
|
13
14
|
force: Flags.boolean({ char: 'f', description: 'force initialization even if files already exist' }),
|
|
14
15
|
// flag with a value (-n, --name=VALUE)
|
|
15
16
|
name: Flags.string({ char: 'n', description: 'name to print' }),
|
|
17
|
+
verbose: Flags.boolean({
|
|
18
|
+
char: 'v',
|
|
19
|
+
default: false,
|
|
20
|
+
description: 'show detailed error information including stack traces',
|
|
21
|
+
}),
|
|
16
22
|
withSampleTasks: Flags.boolean({ description: 'include sample tasks in the initialized roadmap' }),
|
|
17
23
|
};
|
|
18
24
|
buildBlankRoadmap({ description, name, withSampleTasks, }) {
|
|
@@ -48,7 +54,12 @@ export default class Init extends Command {
|
|
|
48
54
|
}
|
|
49
55
|
buildConfig({ description, name, path }) {
|
|
50
56
|
return {
|
|
51
|
-
$schema: 'https://raw.githubusercontent.com/ZacharyEggert/project-roadmap-tracking/refs/heads/master/schemas/config/v1.json',
|
|
57
|
+
$schema: 'https://raw.githubusercontent.com/ZacharyEggert/project-roadmap-tracking/refs/heads/master/schemas/config/v1.1.json',
|
|
58
|
+
cache: {
|
|
59
|
+
enabled: true,
|
|
60
|
+
maxSize: 10,
|
|
61
|
+
watchFiles: true,
|
|
62
|
+
},
|
|
52
63
|
metadata: {
|
|
53
64
|
description: `${description}`,
|
|
54
65
|
name: `${name}`,
|
|
@@ -58,54 +69,60 @@ export default class Init extends Command {
|
|
|
58
69
|
}
|
|
59
70
|
async run() {
|
|
60
71
|
const { args, flags } = await this.parse(Init);
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
72
|
+
try {
|
|
73
|
+
const path = args.folder ?? '.';
|
|
74
|
+
this.log(`creating project roadmap in${path === '.' ? ' current directory' : ': ' + args.folder}`);
|
|
75
|
+
const name = flags.name ?? 'My Project Roadmap';
|
|
76
|
+
const description = flags.description ?? 'A project roadmap managed by Project Roadmap Tracking';
|
|
77
|
+
// create prt.json and prt.config.json files here
|
|
78
|
+
const config = this.buildConfig({ description, name, path });
|
|
79
|
+
const roadmap = this.buildBlankRoadmap({ description, name, withSampleTasks: flags.withSampleTasks ?? false });
|
|
80
|
+
if (path !== '.') {
|
|
81
|
+
// check if target directory exists
|
|
82
|
+
// if it does not, create it
|
|
83
|
+
await readdir(path).catch(async (error) => {
|
|
84
|
+
if (error) {
|
|
85
|
+
this.log(`target directory does not exist, creating: ${path}`);
|
|
86
|
+
try {
|
|
87
|
+
// create the directory
|
|
88
|
+
await mkdir(path, { recursive: true });
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
this.error(`failed to create target directory: ${error.message}`);
|
|
92
|
+
}
|
|
80
93
|
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
// check if config already exists in the target directory
|
|
97
|
+
// if it does, and --force is not set, throw an error
|
|
98
|
+
// if it does, and --force is set, overwrite the files
|
|
99
|
+
await readdir('.')
|
|
100
|
+
.then(async (files) => {
|
|
101
|
+
if (!flags.force && (files.includes('prt.json') || files.includes('prt.config.json'))) {
|
|
102
|
+
this.error('prt.config.json already exist in the current directory. Use --force to overwrite.');
|
|
103
|
+
}
|
|
104
|
+
await writeFile(`./.prtrc.json`, JSON.stringify(config, null, 2));
|
|
105
|
+
this.log('project roadmap config initialized');
|
|
106
|
+
})
|
|
107
|
+
.catch((error) => {
|
|
108
|
+
this.error(`failed to read current directory: ${error.message}`);
|
|
109
|
+
});
|
|
110
|
+
// create prt.json and prt.config.json files in specified directory
|
|
111
|
+
await readdir(path)
|
|
112
|
+
.then(async (files) => {
|
|
113
|
+
if (!flags.force && files.includes('prt.json')) {
|
|
114
|
+
this.error('prt.json already exists in the target directory. Use --force to overwrite.');
|
|
81
115
|
}
|
|
116
|
+
await writeFile(`${path}/prt.json`, JSON.stringify(roadmap, null, 2));
|
|
117
|
+
this.log('project roadmap initialized');
|
|
118
|
+
})
|
|
119
|
+
.catch((error) => {
|
|
120
|
+
this.error(`failed to read directory: ${error.message}`);
|
|
82
121
|
});
|
|
83
122
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
.then(async (files) => {
|
|
89
|
-
if (!flags.force && (files.includes('prt.json') || files.includes('prt.config.json'))) {
|
|
90
|
-
this.error('prt.config.json already exist in the current directory. Use --force to overwrite.');
|
|
91
|
-
}
|
|
92
|
-
await writeFile(`./.prtrc.json`, JSON.stringify(config, null, 2));
|
|
93
|
-
this.log('project roadmap config initialized');
|
|
94
|
-
})
|
|
95
|
-
.catch((error) => {
|
|
96
|
-
this.error(`failed to read current directory: ${error.message}`);
|
|
97
|
-
});
|
|
98
|
-
// create prt.json and prt.config.json files in specified directory
|
|
99
|
-
await readdir(path)
|
|
100
|
-
.then(async (files) => {
|
|
101
|
-
if (!flags.force && files.includes('prt.json')) {
|
|
102
|
-
this.error('prt.json already exists in the target directory. Use --force to overwrite.');
|
|
103
|
-
}
|
|
104
|
-
await writeFile(`${path}/prt.json`, JSON.stringify(roadmap, null, 2));
|
|
105
|
-
this.log('project roadmap initialized');
|
|
106
|
-
})
|
|
107
|
-
.catch((error) => {
|
|
108
|
-
this.error(`failed to read directory: ${error.message}`);
|
|
109
|
-
});
|
|
123
|
+
catch (error) {
|
|
124
|
+
const exitCode = errorHandlerService.handleError(error);
|
|
125
|
+
this.error(errorHandlerService.formatErrorMessage(error, flags.verbose), { exit: exitCode });
|
|
126
|
+
}
|
|
110
127
|
}
|
|
111
128
|
}
|
package/dist/commands/list.d.ts
CHANGED
|
@@ -5,9 +5,12 @@ export default class List extends Command {
|
|
|
5
5
|
static examples: string[];
|
|
6
6
|
static flags: {
|
|
7
7
|
incomplete: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
'no-repo': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
9
|
priority: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
10
|
sort: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
11
|
status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
13
|
};
|
|
14
|
+
private static readonly priorityMap;
|
|
12
15
|
run(): Promise<void>;
|
|
13
16
|
}
|
package/dist/commands/list.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { /* Args, */ Command, Flags } from '@oclif/core';
|
|
2
|
+
import { getDefaultConfigRepository } from '../repositories/config.repository.js';
|
|
3
|
+
import { RoadmapRepository } from '../repositories/roadmap.repository.js';
|
|
4
|
+
import displayService from '../services/display.service.js';
|
|
5
|
+
import errorHandlerService from '../services/error-handler.service.js';
|
|
6
|
+
import taskQueryService, { SortOrder } from '../services/task-query.service.js';
|
|
2
7
|
import { readConfigFile } from '../util/read-config.js';
|
|
3
8
|
import { readRoadmapFile } from '../util/read-roadmap.js';
|
|
4
|
-
import {
|
|
9
|
+
import { PRIORITY } from '../util/types.js';
|
|
5
10
|
export default class List extends Command {
|
|
6
11
|
static args = {
|
|
7
12
|
// file: Args.string({description: 'file to read'}),
|
|
@@ -14,6 +19,10 @@ export default class List extends Command {
|
|
|
14
19
|
// flag with a value (-n, --name=VALUE)
|
|
15
20
|
// name: Flags.string({char: 'n', description: 'name to print'}),
|
|
16
21
|
incomplete: Flags.boolean({ char: 'i', description: 'filter tasks to show in-progress and not-started only' }),
|
|
22
|
+
'no-repo': Flags.boolean({
|
|
23
|
+
default: false,
|
|
24
|
+
description: 'use legacy direct file I/O instead of repository pattern',
|
|
25
|
+
}),
|
|
17
26
|
priority: Flags.string({
|
|
18
27
|
char: 'p',
|
|
19
28
|
description: 'filter tasks by priority (high, medium, low)',
|
|
@@ -29,73 +38,67 @@ export default class List extends Command {
|
|
|
29
38
|
description: 'filter tasks by status (completed, in-progress, not-started)',
|
|
30
39
|
options: ['completed', 'in-progress', 'not-started'],
|
|
31
40
|
}),
|
|
41
|
+
verbose: Flags.boolean({
|
|
42
|
+
char: 'v',
|
|
43
|
+
default: false,
|
|
44
|
+
description: 'show detailed error information including stack traces',
|
|
45
|
+
}),
|
|
46
|
+
};
|
|
47
|
+
static priorityMap = {
|
|
48
|
+
h: PRIORITY.High,
|
|
49
|
+
high: PRIORITY.High,
|
|
50
|
+
l: PRIORITY.Low,
|
|
51
|
+
low: PRIORITY.Low,
|
|
52
|
+
m: PRIORITY.Medium,
|
|
53
|
+
medium: PRIORITY.Medium,
|
|
32
54
|
};
|
|
33
55
|
async run() {
|
|
34
56
|
const { flags } = await this.parse(List);
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
.filter((task) => effectiveStatusFilter.includes(task.status))
|
|
61
|
-
.filter((task) => priorityFilter === null || task.priority === priorityFilter)
|
|
62
|
-
.sort((a, b) => {
|
|
63
|
-
if (sortBy === 'dueDate') {
|
|
64
|
-
const dateA = a.dueDate ? new Date(a.dueDate).getTime() : Infinity;
|
|
65
|
-
const dateB = b.dueDate ? new Date(b.dueDate).getTime() : Infinity;
|
|
66
|
-
return dateA - dateB;
|
|
57
|
+
try {
|
|
58
|
+
const incompleteOnly = flags.incomplete ?? false;
|
|
59
|
+
const priority = (flags.priority ?? null);
|
|
60
|
+
const sortBy = (flags.sort ?? null);
|
|
61
|
+
const statusFilter = (flags.status ?? null);
|
|
62
|
+
const priorityFilter = (priority ? List.priorityMap[priority] : null);
|
|
63
|
+
// if statusFilter is set, it overrides incompleteOnly
|
|
64
|
+
const effectiveStatusFilter = statusFilter
|
|
65
|
+
? [statusFilter]
|
|
66
|
+
: incompleteOnly
|
|
67
|
+
? ['in-progress', 'not-started']
|
|
68
|
+
: ['completed', 'in-progress', 'not-started'];
|
|
69
|
+
// Use repository pattern by default, unless --no-repo flag is set
|
|
70
|
+
const config = flags['no-repo'] ? await readConfigFile() : await getDefaultConfigRepository().load();
|
|
71
|
+
const roadmapPath = config.path;
|
|
72
|
+
const roadmap = flags['no-repo']
|
|
73
|
+
? await readRoadmapFile(roadmapPath)
|
|
74
|
+
: await RoadmapRepository.fromConfig(config).load(roadmapPath);
|
|
75
|
+
// Build filter criteria
|
|
76
|
+
const filterCriteria = {};
|
|
77
|
+
if (effectiveStatusFilter.length > 0) {
|
|
78
|
+
filterCriteria.status =
|
|
79
|
+
effectiveStatusFilter.length === 3
|
|
80
|
+
? undefined // All statuses, no filter needed
|
|
81
|
+
: effectiveStatusFilter;
|
|
67
82
|
}
|
|
68
|
-
if (
|
|
69
|
-
|
|
70
|
-
high: 1,
|
|
71
|
-
low: 3,
|
|
72
|
-
medium: 2,
|
|
73
|
-
};
|
|
74
|
-
return priorityOrder[a.priority] - priorityOrder[b.priority];
|
|
83
|
+
if (priorityFilter) {
|
|
84
|
+
filterCriteria.priority = priorityFilter;
|
|
75
85
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
86
|
+
// Apply filtering and sorting using TaskQueryService
|
|
87
|
+
const hasFilters = Object.keys(filterCriteria).length > 0;
|
|
88
|
+
const filtered = hasFilters
|
|
89
|
+
? // eslint-disable-next-line unicorn/no-array-callback-reference, unicorn/no-array-method-this-argument
|
|
90
|
+
taskQueryService.filter(roadmap.tasks, filterCriteria)
|
|
91
|
+
: roadmap.tasks;
|
|
92
|
+
const tasks = sortBy ? taskQueryService.sort(filtered, sortBy, SortOrder.Ascending) : filtered;
|
|
93
|
+
// Display using DisplayService
|
|
94
|
+
const lines = displayService.formatTaskList(tasks);
|
|
95
|
+
for (const line of lines) {
|
|
96
|
+
console.log(line);
|
|
80
97
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
for (const task of tasks) {
|
|
86
|
-
const status = task.status === STATUS.Completed ? '✓' : task.status === STATUS.InProgress ? '~' : '○';
|
|
87
|
-
const tests = task['passes-tests'] ? '✓' : '✗';
|
|
88
|
-
const prioritySymbol = {
|
|
89
|
-
high: 'H',
|
|
90
|
-
low: 'L',
|
|
91
|
-
medium: 'M',
|
|
92
|
-
}[task.priority];
|
|
93
|
-
console.log(`${status} [${prioritySymbol}] [${task.id}] ${task.title}`);
|
|
94
|
-
console.log(` Type: ${task.type} | Tests: ${tests} | Deps: ${task['depends-on'].length}`);
|
|
95
|
-
if (task['depends-on'].length > 0) {
|
|
96
|
-
console.log(` Depends on: ${task['depends-on'].join(', ')}`);
|
|
97
|
-
}
|
|
98
|
-
console.log();
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
const exitCode = errorHandlerService.handleError(error);
|
|
101
|
+
this.error(errorHandlerService.formatErrorMessage(error, flags.verbose), { exit: exitCode });
|
|
99
102
|
}
|
|
100
103
|
}
|
|
101
104
|
}
|
|
@@ -5,6 +5,9 @@ export default class PassTest extends Command {
|
|
|
5
5
|
};
|
|
6
6
|
static description: string;
|
|
7
7
|
static examples: string[];
|
|
8
|
-
static flags: {
|
|
8
|
+
static flags: {
|
|
9
|
+
'no-repo': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
};
|
|
9
12
|
run(): Promise<void>;
|
|
10
13
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import { Args, Command } from '@oclif/core';
|
|
1
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
+
import { getDefaultConfigRepository } from '../repositories/config.repository.js';
|
|
3
|
+
import { RoadmapRepository } from '../repositories/roadmap.repository.js';
|
|
4
|
+
import errorHandlerService from '../services/error-handler.service.js';
|
|
2
5
|
import { readConfigFile } from '../util/read-config.js';
|
|
3
6
|
import { readRoadmapFile } from '../util/read-roadmap.js';
|
|
4
7
|
import { updateTaskInRoadmap } from '../util/update-task.js';
|
|
@@ -10,19 +13,39 @@ export default class PassTest extends Command {
|
|
|
10
13
|
static description = 'Mark a task as passes-tests';
|
|
11
14
|
static examples = ['<%= config.bin %> <%= command.id %> F-001'];
|
|
12
15
|
static flags = {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
'no-repo': Flags.boolean({
|
|
17
|
+
default: false,
|
|
18
|
+
description: 'use legacy direct file I/O instead of repository pattern',
|
|
19
|
+
}),
|
|
20
|
+
// flag with no value (-f, --force)
|
|
21
|
+
// force: Flags.boolean({char: 'f'}),
|
|
22
|
+
// flag with a value (-n, --name=VALUE)
|
|
23
|
+
// name: Flags.string({char: 'n', description: 'name to print'}),
|
|
24
|
+
verbose: Flags.boolean({
|
|
25
|
+
char: 'v',
|
|
26
|
+
default: false,
|
|
27
|
+
description: 'show detailed error information including stack traces',
|
|
28
|
+
}),
|
|
17
29
|
};
|
|
18
30
|
async run() {
|
|
19
|
-
const { args } = await this.parse(PassTest);
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
'
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
31
|
+
const { args, flags } = await this.parse(PassTest);
|
|
32
|
+
try {
|
|
33
|
+
// Use repository pattern by default, unless --no-repo flag is set
|
|
34
|
+
const config = flags['no-repo'] ? await readConfigFile() : await getDefaultConfigRepository().load();
|
|
35
|
+
const roadmap = flags['no-repo']
|
|
36
|
+
? await readRoadmapFile(config.path)
|
|
37
|
+
: await RoadmapRepository.fromConfig(config).load(config.path);
|
|
38
|
+
const updatedRoadmap = await updateTaskInRoadmap(roadmap, args.taskID, {
|
|
39
|
+
'passes-tests': true,
|
|
40
|
+
});
|
|
41
|
+
await (flags['no-repo']
|
|
42
|
+
? writeRoadmapFile(config.path, updatedRoadmap)
|
|
43
|
+
: RoadmapRepository.fromConfig(config).save(config.path, updatedRoadmap));
|
|
44
|
+
this.log(`Task ${args.taskID} marked as passing tests.`);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
const exitCode = errorHandlerService.handleError(error);
|
|
48
|
+
this.error(errorHandlerService.formatErrorMessage(error, flags.verbose), { exit: exitCode });
|
|
49
|
+
}
|
|
27
50
|
}
|
|
28
51
|
}
|
package/dist/commands/show.d.ts
CHANGED
|
@@ -5,6 +5,9 @@ export default class Show extends Command {
|
|
|
5
5
|
};
|
|
6
6
|
static description: string;
|
|
7
7
|
static examples: string[];
|
|
8
|
-
static flags: {
|
|
8
|
+
static flags: {
|
|
9
|
+
'no-repo': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
};
|
|
9
12
|
run(): Promise<void>;
|
|
10
13
|
}
|
package/dist/commands/show.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import { Args, Command
|
|
1
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
+
import { getDefaultConfigRepository } from '../repositories/config.repository.js';
|
|
3
|
+
import { RoadmapRepository } from '../repositories/roadmap.repository.js';
|
|
4
|
+
import displayService from '../services/display.service.js';
|
|
5
|
+
import errorHandlerService from '../services/error-handler.service.js';
|
|
2
6
|
import { readConfigFile } from '../util/read-config.js';
|
|
3
7
|
import { readRoadmapFile } from '../util/read-roadmap.js';
|
|
4
8
|
export default class Show extends Command {
|
|
@@ -8,66 +12,41 @@ export default class Show extends Command {
|
|
|
8
12
|
static description = 'show details of a specific task in the project roadmap';
|
|
9
13
|
static examples = ['<%= config.bin %> <%= command.id %> F-001'];
|
|
10
14
|
static flags = {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
'no-repo': Flags.boolean({
|
|
16
|
+
default: false,
|
|
17
|
+
description: 'use legacy direct file I/O instead of repository pattern',
|
|
18
|
+
}),
|
|
19
|
+
// flag with no value (-f, --force)
|
|
20
|
+
// force: Flags.boolean({char: 'f'}),
|
|
21
|
+
// flag with a value (-n, --name=VALUE)
|
|
22
|
+
// name: Flags.string({char: 'n', description: 'name to print'}),
|
|
23
|
+
verbose: Flags.boolean({
|
|
24
|
+
char: 'v',
|
|
25
|
+
default: false,
|
|
26
|
+
description: 'show detailed error information including stack traces',
|
|
27
|
+
}),
|
|
15
28
|
};
|
|
16
29
|
async run() {
|
|
17
|
-
const { args } = await this.parse(Show);
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
30
|
+
const { args, flags } = await this.parse(Show);
|
|
31
|
+
try {
|
|
32
|
+
// Use repository pattern by default, unless --no-repo flag is set
|
|
33
|
+
const config = flags['no-repo'] ? await readConfigFile() : await getDefaultConfigRepository().load();
|
|
34
|
+
const roadmapPath = config.path;
|
|
35
|
+
const roadmap = flags['no-repo']
|
|
36
|
+
? await readRoadmapFile(roadmapPath)
|
|
37
|
+
: await RoadmapRepository.fromConfig(config).load(roadmapPath);
|
|
38
|
+
const task = roadmap.tasks.find((t) => t.id === args.task);
|
|
39
|
+
if (!task) {
|
|
40
|
+
this.error(`task with ID ${args.task} not found in roadmap \n see list of tasks with: 'prt list'`);
|
|
41
|
+
}
|
|
42
|
+
const lines = displayService.formatTaskDetails(task);
|
|
43
|
+
for (const line of lines) {
|
|
44
|
+
console.log(line);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
const exitCode = errorHandlerService.handleError(error);
|
|
49
|
+
this.error(errorHandlerService.formatErrorMessage(error, flags.verbose), { exit: exitCode });
|
|
26
50
|
}
|
|
27
|
-
const completeStatus = task.status === 'completed' ? '✓' : task.status === 'in-progress' ? '~' : '○';
|
|
28
|
-
const testStatus = task['passes-tests'] ? '✓' : '✗';
|
|
29
|
-
const priorityLabel = {
|
|
30
|
-
high: 'High',
|
|
31
|
-
low: 'Low',
|
|
32
|
-
medium: 'Medium',
|
|
33
|
-
}[task.priority];
|
|
34
|
-
console.log(`\nTask: ${task.id}\n`);
|
|
35
|
-
console.log(`Title: ${task.title}`);
|
|
36
|
-
console.log(`Type: ${task.type}`);
|
|
37
|
-
console.log(`Priority: ${priorityLabel}`);
|
|
38
|
-
console.log(`Status: ${completeStatus} ${task.status
|
|
39
|
-
.replaceAll('-', ' ')
|
|
40
|
-
.split(' ')
|
|
41
|
-
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
42
|
-
.join(' ')} | ${testStatus} Tests Passing`);
|
|
43
|
-
console.log(`\nDetails:\n${task.details}`);
|
|
44
|
-
// Dependencies
|
|
45
|
-
if (task['depends-on'].length > 0) {
|
|
46
|
-
console.log(`\nDepends On: ${task['depends-on'].join(', ')}`);
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
console.log(`\nDepends On: None`);
|
|
50
|
-
}
|
|
51
|
-
// Blocks (optional)
|
|
52
|
-
if (task.blocks && task.blocks.length > 0) {
|
|
53
|
-
console.log(`Blocks: ${task.blocks.join(', ')}`);
|
|
54
|
-
}
|
|
55
|
-
// Timestamps
|
|
56
|
-
console.log(`\nCreated: ${task.createdAt}`);
|
|
57
|
-
console.log(`Updated: ${task.updatedAt}`);
|
|
58
|
-
// Optional fields - only display if present
|
|
59
|
-
if (task.tags && task.tags.length > 0) {
|
|
60
|
-
console.log(`\nTags: ${task.tags.join(', ')}`);
|
|
61
|
-
}
|
|
62
|
-
if (task.effort !== undefined) {
|
|
63
|
-
console.log(`Effort: ${task.effort}`);
|
|
64
|
-
}
|
|
65
|
-
if (task['github-refs'] && task['github-refs'].length > 0) {
|
|
66
|
-
console.log(`GitHub Refs: ${task['github-refs'].join(', ')}`);
|
|
67
|
-
}
|
|
68
|
-
if (task.notes) {
|
|
69
|
-
console.log(`\nNotes:\n${task.notes}`);
|
|
70
|
-
}
|
|
71
|
-
console.log('');
|
|
72
51
|
}
|
|
73
52
|
}
|
|
@@ -8,9 +8,12 @@ export default class Update extends Command {
|
|
|
8
8
|
static flags: {
|
|
9
9
|
'clear-notes': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
10
|
deps: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
'no-repo': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
12
|
notes: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
13
|
status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
14
|
tested: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
type: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
17
|
};
|
|
15
18
|
run(): Promise<void>;
|
|
16
19
|
}
|