Skip to content

Commit 40d039f

Browse files
committed
feat(cli): add back navigation and countdown to menus
1 parent ede434f commit 40d039f

3 files changed

Lines changed: 270 additions & 104 deletions

File tree

src/core/TaskMaster.ts

Lines changed: 127 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
REPORT_PATH,
2222
README_PATH,
2323
TASKS_FILES,
24+
NOTE_LANGS,
2425
} from "@/constants";
2526

2627
/* extras */
@@ -122,7 +123,7 @@ export class TaskMaster {
122123

123124
// TODO: done
124125
/**
125-
* @description Retrieves all dependencies for a given task or subtask.
126+
* @description Extracts all dependencies for a given task or subtask.
126127
* @param tasks The tasks data structure
127128
* @param taskId The task ID (either a simple number as string or hierarchical like "1.2")
128129
*/
@@ -162,6 +163,122 @@ export class TaskMaster {
162163
return subtask.dependencies;
163164
}
164165

166+
// TODO: done
167+
/**
168+
* @description Fixes the format of the tasks.json file if necessary
169+
* by encapsulating the 'tasks' and 'metadata' keys under a 'master' key
170+
* @param mode Operation mode: 'init' for initialization or 'repair' for fixing existing file
171+
*/
172+
public async _fixTasksFileFormatAsync(
173+
mode: "init" | "repair" = "repair",
174+
): Promise<void> {
175+
const isInitMode = mode === "init";
176+
const oraOptions = {
177+
text: isInitMode
178+
? "Initializing tasks.json file..."
179+
: "Verifying tasks.json file format...",
180+
successText: isInitMode
181+
? chalk.bgGreen(
182+
"tasks.json initialized successfully with default structure.",
183+
)
184+
: chalk.bgGreen("tasks.json format validated successfully!"),
185+
failText: isInitMode
186+
? chalk.bgRed("Failed to initialize tasks.json file")
187+
: chalk.bgRed("Failed to validate tasks.json format"),
188+
};
189+
190+
await oraPromise(async () => {
191+
// Handle init mode - create default structure
192+
if (isInitMode) {
193+
const defaultContent = {
194+
master: {
195+
tasks: [],
196+
metadata: {},
197+
},
198+
};
199+
200+
await writeFile(
201+
this._tasksFilePath,
202+
JSON.stringify(defaultContent, null, 2),
203+
);
204+
return;
205+
}
206+
207+
// Handle repair mode - fix existing file format
208+
const currentContent = await readJsonFileAsync<Record<string, unknown>>(
209+
this._tasksFilePath,
210+
);
211+
212+
interface MasterStructure {
213+
tasks?: unknown;
214+
metadata?: unknown;
215+
}
216+
217+
let tasksToSave: unknown[] | null = null;
218+
let metadataToSave: object | null = null;
219+
let masterKey: string | null = null;
220+
221+
if (currentContent.master) {
222+
const masterData = currentContent.master as MasterStructure;
223+
if (
224+
Array.isArray(masterData.tasks) &&
225+
typeof masterData.metadata === "object"
226+
) {
227+
masterKey = "master";
228+
tasksToSave = masterData.tasks;
229+
metadataToSave = masterData.metadata;
230+
}
231+
}
232+
233+
if (!masterKey) {
234+
for (const key of Object.keys(currentContent)) {
235+
const value = currentContent[key];
236+
if (typeof value === "object" && !Array.isArray(value)) {
237+
const data = value as MasterStructure;
238+
if (
239+
Array.isArray(data.tasks) &&
240+
typeof data.metadata === "object"
241+
) {
242+
masterKey = key;
243+
tasksToSave = data.tasks;
244+
metadataToSave = data.metadata;
245+
break;
246+
}
247+
}
248+
}
249+
}
250+
251+
if (!masterKey) {
252+
if (
253+
Array.isArray(currentContent?.tasks) &&
254+
typeof currentContent?.metadata === "object"
255+
) {
256+
tasksToSave = currentContent.tasks;
257+
metadataToSave = currentContent.metadata;
258+
}
259+
}
260+
261+
if (tasksToSave !== null && metadataToSave !== null) {
262+
const correctedContent = {
263+
master: {
264+
tasks: tasksToSave,
265+
metadata: metadataToSave,
266+
},
267+
};
268+
269+
await writeFile(
270+
this._tasksFilePath,
271+
JSON.stringify(correctedContent, null, 2),
272+
);
273+
return;
274+
}
275+
276+
throw new Error(
277+
"Invalid tasks.json format. Could not find valid tasks and metadata to correct the file.",
278+
);
279+
}, oraOptions);
280+
}
281+
165282
// ==============================================
166283
// Helpers
167284
// ==============================================
@@ -249,93 +366,6 @@ export class TaskMaster {
249366
}, oraOptions);
250367
}
251368

252-
// TODO: done
253-
/**
254-
* @description Fixes the format of the tasks.json file if necessary
255-
* by encapsulating the 'tasks' and 'metadata' keys under a 'master' key
256-
*/
257-
public async _fixTasksFileFormatAsync(): Promise<void> {
258-
const oraOptions = {
259-
text: "Verifying tasks.json file format...",
260-
successText: chalk.bgGreen("tasks.json format validated successfully!"),
261-
failText: chalk.bgRed("Failed to validate tasks.json format"),
262-
};
263-
264-
await oraPromise(async () => {
265-
const currentContent = await readJsonFileAsync<Record<string, unknown>>(
266-
this._tasksFilePath,
267-
);
268-
269-
interface MasterStructure {
270-
tasks?: unknown;
271-
metadata?: unknown;
272-
}
273-
274-
let tasksToSave: unknown[] | null = null;
275-
let metadataToSave: object | null = null;
276-
let masterKey: string | null = null;
277-
278-
if (currentContent.master) {
279-
const masterData = currentContent.master as MasterStructure;
280-
if (
281-
Array.isArray(masterData.tasks) &&
282-
typeof masterData.metadata === "object"
283-
) {
284-
masterKey = "master";
285-
tasksToSave = masterData.tasks;
286-
metadataToSave = masterData.metadata;
287-
}
288-
}
289-
290-
if (!masterKey) {
291-
for (const key of Object.keys(currentContent)) {
292-
const value = currentContent[key];
293-
if (typeof value === "object" && !Array.isArray(value)) {
294-
const data = value as MasterStructure;
295-
if (
296-
Array.isArray(data.tasks) &&
297-
typeof data.metadata === "object"
298-
) {
299-
masterKey = key;
300-
tasksToSave = data.tasks;
301-
metadataToSave = data.metadata;
302-
break;
303-
}
304-
}
305-
}
306-
}
307-
308-
if (!masterKey) {
309-
if (
310-
Array.isArray(currentContent?.tasks) &&
311-
typeof currentContent?.metadata === "object"
312-
) {
313-
tasksToSave = currentContent.tasks;
314-
metadataToSave = currentContent.metadata;
315-
}
316-
}
317-
318-
if (tasksToSave !== null && metadataToSave !== null) {
319-
const correctedContent = {
320-
master: {
321-
tasks: tasksToSave,
322-
metadata: metadataToSave,
323-
},
324-
};
325-
326-
await writeFile(
327-
this._tasksFilePath,
328-
JSON.stringify(correctedContent, null, 2),
329-
);
330-
return;
331-
}
332-
333-
throw new Error(
334-
"Invalid tasks.json format. Could not find valid tasks and metadata to correct the file.",
335-
);
336-
}, oraOptions);
337-
}
338-
339369
// TODO: done
340370
/**
341371
* @description Validates conversion rules for task/subtask conversion
@@ -519,8 +549,12 @@ export class TaskMaster {
519549
}
520550

521551
// TODO: done
522-
private _countdown(seconds: number) {
523-
return new Promise((resolve) => {
552+
/**
553+
* @description Starts a countdown timer
554+
* @param seconds The number of seconds to count down
555+
*/
556+
public async countdownAsync(seconds: number) {
557+
return await new Promise((resolve) => {
524558
let remaining = seconds;
525559
const rl = readline.createInterface({
526560
input: process.stdin,
@@ -689,11 +723,7 @@ export class TaskMaster {
689723
* @param lang Language to set for TMAI responses
690724
*/
691725
public async setLangAsync(lang: string): Promise<void> {
692-
console.log(
693-
chalk.yellow(
694-
"Note: Make sure the LLM used by TMAI supports the language you choose!",
695-
),
696-
);
726+
console.log(chalk.yellow(NOTE_LANGS));
697727
await this._executeCommandAsync(
698728
`Setting TMAI response language to ${chalk.bold(lang)}...`,
699729
`Language set to ${lang} successfully!`,
@@ -1224,7 +1254,7 @@ export class TaskMaster {
12241254
"You can force conversion by manually removing dependencies, but this is not recommended unless absolutely necessary. Consider if the conversion is truly important for your workflow.",
12251255
),
12261256
);
1227-
await this._countdown(20);
1257+
await this.countdownAsync(20);
12281258
return;
12291259
}
12301260

@@ -1283,7 +1313,7 @@ export class TaskMaster {
12831313
"You can force conversion by manually removing dependencies, but this is not recommended unless absolutely necessary. Consider if the conversion is truly important for your workflow.",
12841314
),
12851315
);
1286-
await this._countdown(20);
1316+
await this.countdownAsync(20);
12871317
return;
12881318
}
12891319

0 commit comments

Comments
 (0)