├── .gitignore ├── .npmignore ├── assets ├── mit-license.png ├── tasklist-cli.gif └── tasklist-cli.png ├── src ├── actions │ ├── config.d.ts │ ├── index.ts │ ├── fileCheck.ts │ ├── addProj.ts │ ├── remove.ts │ ├── removeProj.ts │ ├── listProj.ts │ ├── listAll.ts │ ├── list.ts │ ├── help.ts │ ├── done.ts │ ├── add.ts │ └── listDate.ts └── tasklist.ts ├── tsconfig.json ├── package.json ├── LICENSE ├── USAGE.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /lib -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | tsconfig.json -------------------------------------------------------------------------------- /assets/mit-license.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manojuppala/tasklist-cli/HEAD/assets/mit-license.png -------------------------------------------------------------------------------- /assets/tasklist-cli.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manojuppala/tasklist-cli/HEAD/assets/tasklist-cli.gif -------------------------------------------------------------------------------- /assets/tasklist-cli.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manojuppala/tasklist-cli/HEAD/assets/tasklist-cli.png -------------------------------------------------------------------------------- /src/actions/config.d.ts: -------------------------------------------------------------------------------- 1 | // this file contains types for config object. 2 | 3 | interface configType { 4 | default: string; 5 | emoji: boolean; 6 | } 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["es5", "dom", "es2015.promise"], 5 | "module": "commonjs", 6 | "declaration": true, 7 | "outDir": "./lib", 8 | "strict": true 9 | }, 10 | "include": ["src"], 11 | "exclude": ["node_modules", "**/__tests__/*"] 12 | } 13 | -------------------------------------------------------------------------------- /src/actions/index.ts: -------------------------------------------------------------------------------- 1 | export { default as add } from "./add"; 2 | export { default as addProj } from "./addProj"; 3 | export { default as done } from "./done"; 4 | export { default as fileCheck } from "./fileCheck"; 5 | export { default as help } from "./help"; 6 | export { default as list } from "./list"; 7 | export { default as listAll } from "./listAll"; 8 | export { default as listDate } from "./listDate"; 9 | export { default as listProj } from "./listProj"; 10 | export { default as remove } from "./remove"; 11 | export { default as removeProj } from "./removeProj"; 12 | -------------------------------------------------------------------------------- /src/actions/fileCheck.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | import { homedir } from "os"; 3 | 4 | // check if tasklist.json exists 5 | export default async function fileCheck() { 6 | const STORAGE_PATH = homedir() + "/.tasklist/tasklist.json"; 7 | const default_data = { default: [] }; 8 | 9 | //create /.tasklist/tasklist.json if dosent exist already 10 | if (!fs.existsSync(STORAGE_PATH)) { 11 | if (!fs.existsSync(homedir() + "/.tasklist")) 12 | fs.mkdirSync(homedir() + "/.tasklist"); 13 | fs.writeFileSync(STORAGE_PATH, JSON.stringify(default_data)); 14 | } 15 | 16 | //if tasklist.json is empty 17 | if (fs.readFileSync(STORAGE_PATH).length === 0) { 18 | fs.writeFileSync(STORAGE_PATH, JSON.stringify(default_data)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tasklist-cli", 3 | "version": "1.3.0", 4 | "description": "Tasklist-cli is a simple and elegant command line application to manage tasks and todo lists", 5 | "main": "lib/tasklist.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/manojuppala/tasklist-cli" 9 | }, 10 | "scripts": { 11 | "start": "node .", 12 | "build": "tsc" 13 | }, 14 | "keywords": [ 15 | "todo", 16 | "cli", 17 | "todo-list", 18 | "tasklist-cli" 19 | ], 20 | "author": "manoj uppala ", 21 | "license": "MIT", 22 | "dependencies": { 23 | "chalk": "^4.1.2", 24 | "inquirer": "^8.2.0", 25 | "inquirer-date-prompt": "^2.0.1", 26 | "nanospinner": "^1.0.0", 27 | "yargs": "^17.3.1" 28 | }, 29 | "bin": { 30 | "task": "./lib/tasklist.js" 31 | }, 32 | "devDependencies": { 33 | "@types/inquirer": "^8.2.1", 34 | "@types/yargs": "^17.0.8", 35 | "typescript": "^4.5.5" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/actions/addProj.ts: -------------------------------------------------------------------------------- 1 | import * as chalk from "chalk"; 2 | import * as inquirer from "inquirer"; 3 | import * as fs from "fs"; 4 | import { homedir } from "os"; 5 | 6 | const STORAGE_PATH = homedir() + "/.tasklist/tasklist.json"; 7 | 8 | let tasks = JSON.parse(fs.readFileSync(STORAGE_PATH, "utf-8")); 9 | 10 | export default async function addProj(config: configType) { 11 | const newProj = await inquirer.prompt({ 12 | name: "projName", 13 | type: "input", 14 | prefix: config?.emoji ?? true ? "📁" : undefined, 15 | message: "Project name", 16 | default() { 17 | return "New project"; 18 | }, 19 | }); 20 | console.clear(); 21 | if (Object.keys(tasks).includes(newProj.projName)) { 22 | console.log( 23 | `${chalk.red("Project with name")} ${chalk.yellow( 24 | newProj.projName 25 | )} ${chalk.red("already exists!")}` 26 | ); 27 | } else { 28 | tasks[newProj.projName] = []; 29 | fs.writeFileSync(STORAGE_PATH, JSON.stringify(tasks)); 30 | console.log(`New project ${chalk.yellow(newProj.projName)} created.`); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/actions/remove.ts: -------------------------------------------------------------------------------- 1 | import * as chalk from "chalk"; 2 | import * as fs from "fs"; 3 | import { homedir } from "os"; 4 | 5 | const STORAGE_PATH = homedir() + "/.tasklist/tasklist.json"; 6 | 7 | let tasks = JSON.parse(fs.readFileSync(STORAGE_PATH, "utf-8")); 8 | 9 | type taskType = { 10 | id: number; 11 | name: string; 12 | status: boolean; 13 | date: string; 14 | }; 15 | 16 | // function to revoke or mark done a task. 17 | export default async function removeTask( 18 | config: configType, 19 | removedTaskId: number, 20 | proj: string = "default" 21 | ) { 22 | tasks[proj].forEach((task: taskType) => { 23 | if (task.id === removedTaskId) { 24 | console.clear(); 25 | task.status 26 | ? console.log(`Task ${chalk.yellow(task.name)} has been revoked.`) 27 | : console.log( 28 | `Task ${chalk.yellow(task.name)} is marked ${ 29 | config?.emoji ?? true ? "✅" : "" 30 | } done.` 31 | ); 32 | task.status = !task.status; 33 | } 34 | }); 35 | fs.writeFileSync(STORAGE_PATH, JSON.stringify(tasks)); 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 manoj 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/actions/removeProj.ts: -------------------------------------------------------------------------------- 1 | import * as chalk from "chalk"; 2 | import * as inquirer from "inquirer"; 3 | import * as fs from "fs"; 4 | import { homedir } from "os"; 5 | 6 | const STORAGE_PATH = homedir() + "/.tasklist/tasklist.json"; 7 | 8 | let tasks = JSON.parse(fs.readFileSync(STORAGE_PATH, "utf-8")); 9 | 10 | // function to remove a project 11 | export default async function removeProj(config: configType, proj: string) { 12 | if (Object.keys(tasks).includes(proj)) { 13 | if ((config?.default ?? "default") !== proj) { 14 | delete tasks[proj]; 15 | console.clear(); 16 | const confirmDelete = await inquirer.prompt({ 17 | name: "choice", 18 | type: "confirm", 19 | prefix: config?.emoji ?? true ? "❓" : undefined, 20 | message: `Are you sure to delete "${proj}"?`, 21 | default() { 22 | return false; 23 | }, 24 | }); 25 | if (confirmDelete.choice) { 26 | console.log( 27 | `Project ${chalk.yellow(proj)} has been ${chalk.red("deleted.")}` 28 | ); 29 | fs.writeFileSync(STORAGE_PATH, JSON.stringify(tasks)); 30 | } 31 | } else { 32 | console.log( 33 | `You cannot delete the default project ${chalk.yellow(proj)}.` 34 | ); 35 | } 36 | } else { 37 | console.log(`${chalk.red(proj)}: No such project.`); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/actions/listProj.ts: -------------------------------------------------------------------------------- 1 | import * as chalk from "chalk"; 2 | import * as inquirer from "inquirer"; 3 | import * as fs from "fs"; 4 | import { homedir } from "os"; 5 | import { default as list } from "./list.js"; 6 | 7 | const STORAGE_PATH = homedir() + "/.tasklist/tasklist.json"; 8 | 9 | let tasks = JSON.parse(fs.readFileSync(STORAGE_PATH, "utf-8")); 10 | 11 | // function to list available projects. 12 | export default async function listProj(config: configType) { 13 | const cancel = `${config?.emoji ?? true ? "❌ " : ""}cancel`; 14 | 15 | let projList = []; 16 | Object.keys(tasks).forEach((task: string) => { 17 | projList.push(task); 18 | }); 19 | 20 | if (projList.length) { 21 | projList.push(new inquirer.Separator()); 22 | projList.push(`${chalk.red(cancel)}`); 23 | 24 | const listProjs = await inquirer.prompt({ 25 | name: "selectProj", 26 | type: "list", 27 | prefix: config?.emoji ?? true ? "📁" : undefined, 28 | message: "Choose a project to list tasks.", 29 | choices: projList, 30 | pageSize: projList.length, 31 | }); 32 | 33 | if (listProjs.selectProj === `${chalk.red(cancel)}`) { 34 | console.clear(); 35 | console.log(`No Project Choosen.`); 36 | } else { 37 | console.clear(); 38 | await list(config, listProjs.selectProj); 39 | } 40 | } else { 41 | console.clear(); 42 | console.log(`There are currently no projects.`); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/actions/listAll.ts: -------------------------------------------------------------------------------- 1 | import * as chalk from "chalk"; 2 | import * as inquirer from "inquirer"; 3 | import * as fs from "fs"; 4 | import { homedir } from "os"; 5 | import { default as remove } from "./remove.js"; 6 | 7 | const STORAGE_PATH = homedir() + "/.tasklist/tasklist.json"; 8 | 9 | let tasks = JSON.parse(fs.readFileSync(STORAGE_PATH, "utf-8")); 10 | 11 | type taskType = { 12 | id: number; 13 | name: string; 14 | status: boolean; 15 | date: string; 16 | }; 17 | 18 | // function to list pending tasks. 19 | export default async function listAll(config: configType) { 20 | const cancel = `${config?.emoji ?? true ? "❌ " : ""}cancel`; 21 | 22 | let taskList = []; 23 | Object.keys(tasks).forEach((proj: string) => { 24 | tasks[proj].forEach((task: taskType) => { 25 | if (!task.status) { 26 | taskList.push({ 27 | value: task.id + "&@^$%" + proj, 28 | name: task.name + ` (${chalk.blue(proj)})`, 29 | }); 30 | } 31 | }); 32 | }); 33 | 34 | if (taskList.length) { 35 | taskList.push(new inquirer.Separator()); 36 | taskList.push(`${chalk.red(cancel)}`); 37 | 38 | const listTasks = await inquirer.prompt({ 39 | name: "selectTask", 40 | type: "list", 41 | prefix: config?.emoji ?? true ? "📝" : undefined, 42 | message: "Choose a task to mark done", 43 | choices: taskList, 44 | pageSize: taskList.length, 45 | }); 46 | 47 | if (listTasks.selectTask === `${chalk.red(cancel)}`) { 48 | console.clear(); 49 | console.log(`No task Choosen.`); 50 | } else { 51 | await remove( 52 | config, 53 | parseInt(listTasks.selectTask.split("&@^$%")[0]), 54 | listTasks.selectTask.split("&@^$%")[1] 55 | ); 56 | } 57 | } else { 58 | console.clear(); 59 | console.log( 60 | `No tasks are marked ${config?.emoji ?? true ? "📝" : ""}todo.` 61 | ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/actions/list.ts: -------------------------------------------------------------------------------- 1 | import * as chalk from "chalk"; 2 | import * as inquirer from "inquirer"; 3 | import * as fs from "fs"; 4 | import { homedir } from "os"; 5 | import { default as remove } from "./remove.js"; 6 | 7 | const STORAGE_PATH = homedir() + "/.tasklist/tasklist.json"; 8 | 9 | let tasks = JSON.parse(fs.readFileSync(STORAGE_PATH, "utf-8")); 10 | 11 | type taskType = { 12 | id: number; 13 | name: string; 14 | status: boolean; 15 | date: string; 16 | }; 17 | 18 | // function to list pending tasks. 19 | export default async function list( 20 | config: configType, 21 | proj: string = config?.default ?? "default" 22 | ) { 23 | const cancel = `${config?.emoji ?? true ? "❌ " : ""}cancel`; 24 | 25 | if (Object.keys(tasks).includes(proj)) { 26 | let taskList = []; 27 | 28 | tasks[proj].forEach((task: taskType) => { 29 | if (!task.status) { 30 | taskList.push({ 31 | value: task.id, 32 | name: task.name, 33 | }); 34 | } 35 | }); 36 | if (taskList.length) { 37 | taskList.push(new inquirer.Separator()); 38 | taskList.push(`${chalk.red(cancel)}`); 39 | 40 | const listTasks = await inquirer.prompt({ 41 | name: "selectTask", 42 | type: "list", 43 | prefix: config?.emoji ?? true ? "📝" : undefined, 44 | message: `Choose a task to mark done (${chalk.yellow(proj)})`, 45 | choices: taskList, 46 | pageSize: taskList.length, 47 | }); 48 | 49 | if (listTasks.selectTask === `${chalk.red(cancel)}`) { 50 | console.clear(); 51 | console.log(`No task Choosen.`); 52 | } else { 53 | await remove(config, listTasks.selectTask, proj); 54 | } 55 | } else { 56 | console.clear(); 57 | console.log( 58 | `No tasks are marked ${ 59 | config?.emoji ?? true ? "📝" : "" 60 | }todo in ${chalk.yellow(proj)}.` 61 | ); 62 | } 63 | } else { 64 | console.log(`${chalk.red(proj)}: No such project.`); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /USAGE.md: -------------------------------------------------------------------------------- 1 | # [tasklist-cli](https://github.com/manojuppala/tasklist-cli) Usage 2 | 3 | you can perform any of the actions listed below. 4 | 5 | ```shell 6 | task [action] [project_name] 7 | ``` 8 | 9 | ## Actions 10 | 11 | ### `add` 12 | 13 | - Adds a new task to default list (or project). 14 | 15 | ```shell 16 | task add 17 | task a 18 | ``` 19 | 20 | ### `addproj` 21 | 22 | - Creates a new project with the name specified. 23 | 24 | ```shell 25 | task addproj 26 | task ap 27 | ``` 28 | 29 | ### `addto` 30 | 31 | - Adds a new task to specified project. 32 | 33 | ```shell 34 | task addto [project_name] 35 | task at [project_name] 36 | ``` 37 | 38 | ### `done` 39 | 40 | - Lists all the tasks that are marked done. 41 | 42 | ```shell 43 | task done [project_name] 44 | task d [project_name] 45 | ``` 46 | 47 | _Note: [project_name] is optional._ 48 | 49 | ### `list` 50 | 51 | - Lists tasks that are marked undone from specified project. 52 | 53 | ```shell 54 | task list [project_name] 55 | task ls [project_name] 56 | ``` 57 | 58 | _Note: [project_name] is optional._ 59 | 60 | ### `listall` 61 | 62 | - Lists tasks that are marked undone from all projects. 63 | 64 | ```shell 65 | task listall 66 | task la 67 | ``` 68 | 69 | ### `listdate` 70 | 71 | - Lists tasks marked undone along with due date. 72 | 73 | ```shell 74 | task listdate [project_name] 75 | task ld [project_name] 76 | ``` 77 | 78 | _Note: [project_name] is optional._ 79 | 80 | ### `listproj` 81 | 82 | - Lists all available projects. 83 | 84 | ```shell 85 | task listproj 86 | task lp 87 | ``` 88 | 89 | ### `remove` 90 | 91 | - Deletes a project permanently. 92 | 93 | ```shell 94 | task remove [project_name] 95 | task rm [project_name] 96 | ``` 97 | 98 | ### `--help` 99 | 100 | - Show help. 101 | 102 | ```shell 103 | task [action] --help 104 | task [action] -h 105 | ``` 106 | 107 | ### `--version` 108 | 109 | - Show version number. 110 | 111 | ```shell 112 | task --version 113 | task -v 114 | ``` 115 | -------------------------------------------------------------------------------- /src/actions/help.ts: -------------------------------------------------------------------------------- 1 | import * as yargs from "yargs"; 2 | 3 | // function to return help documentation. 4 | export default async function help() { 5 | return yargs 6 | .usage("Usage: task [command] [project_name]") 7 | .command( 8 | "add", 9 | "- Adds a new task to default list (or project).", 10 | (yargs) => { 11 | yargs.usage(`Usage: task add (or) task a`); 12 | } 13 | ) 14 | .command( 15 | "addproj", 16 | "- Creates a new project with the name specified.", 17 | (yargs) => { 18 | yargs.usage( 19 | `Usage: task addproj [new_project_name] (or) task ap [new_project_name]` 20 | ); 21 | } 22 | ) 23 | .command("addto", "- Adds a new task to specified project.", (yargs) => { 24 | yargs.usage( 25 | `Usage: task addto [project_name] (or) task at [project_name]` 26 | ); 27 | }) 28 | .command("done", "- Lists all the tasks that are marked done.", (yargs) => { 29 | yargs.usage(`Usage: task done (or) task d`); 30 | }) 31 | .command( 32 | "list", 33 | "- Lists tasks that are marked undone from specified project.", 34 | (yargs) => { 35 | yargs.usage( 36 | `Usage: task list [project_name] (or) task ls [project_name]` 37 | ); 38 | } 39 | ) 40 | .command( 41 | "listall", 42 | "- Lists tasks that are marked undone from all projects.", 43 | (yargs) => { 44 | yargs.usage(`Usage: task listall (or) task la`); 45 | } 46 | ) 47 | .command( 48 | "listdate", 49 | "- Lists tasks marked undone along with due date.", 50 | (yargs) => { 51 | yargs.usage( 52 | `Usage: task listdate [project_name] (or) task ld [project_name]` 53 | ); 54 | } 55 | ) 56 | .command("listproj", "- Lists all available projects.", (yargs) => { 57 | yargs.usage(`Usage: task listproj (or) task lp`); 58 | }) 59 | .command("remove", "- Deletes a project permanently.", (yargs) => { 60 | yargs.usage( 61 | `Usage: task remove [project_name] (or) task rm [project_name]` 62 | ); 63 | }) 64 | .alias("h", "help") 65 | .help("help") 66 | .alias("v", "version").argv; 67 | } 68 | -------------------------------------------------------------------------------- /src/actions/done.ts: -------------------------------------------------------------------------------- 1 | import * as chalk from "chalk"; 2 | import * as inquirer from "inquirer"; 3 | import * as fs from "fs"; 4 | import { createSpinner } from "nanospinner"; 5 | import { homedir } from "os"; 6 | import { default as remove } from "./remove.js"; 7 | 8 | const STORAGE_PATH = homedir() + "/.tasklist/tasklist.json"; 9 | 10 | let tasks = JSON.parse(fs.readFileSync(STORAGE_PATH, "utf-8")); 11 | 12 | const sleep = (ms = 100) => new Promise((r) => setTimeout(r, ms)); 13 | 14 | type taskType = { 15 | id: number; 16 | name: string; 17 | status: boolean; 18 | date: string; 19 | }; 20 | 21 | // function to add tasks to donetask list. 22 | export default async function done( 23 | config: configType, 24 | proj: string = config?.default ?? "default" 25 | ) { 26 | const cancel = `${config?.emoji ?? true ? "❌ " : ""}cancel`; 27 | const clearAll = `${config?.emoji ?? true ? "🗑️ " : ""}clear all`; 28 | 29 | if (Object.keys(tasks).includes(proj)) { 30 | let taskList = []; 31 | tasks[proj].forEach((task: taskType) => { 32 | if (task.status) { 33 | taskList.push({ value: task.id, name: task.name }); 34 | } 35 | }); 36 | if (taskList.length) { 37 | taskList.push(new inquirer.Separator()); 38 | taskList.push(`${chalk.yellow(clearAll)}`); 39 | taskList.push(`${chalk.red(cancel)}`); 40 | 41 | const doneTasks = await inquirer.prompt({ 42 | name: "selectTask", 43 | type: "list", 44 | prefix: config?.emoji ?? true ? "✅" : undefined, 45 | message: "Done tasks list", 46 | choices: taskList, 47 | pageSize: taskList.length, 48 | }); 49 | 50 | if (doneTasks.selectTask === `${chalk.yellow(clearAll)}`) { 51 | console.clear(); 52 | const spinner = createSpinner("Clearing tasks...").start(); 53 | await sleep(1000); 54 | for (let i = 0; i < tasks[proj].length; i++) { 55 | if (tasks[proj][i].status) { 56 | tasks[proj].splice(i, 1); 57 | i--; 58 | } 59 | } 60 | fs.writeFileSync(STORAGE_PATH, JSON.stringify(tasks)); 61 | spinner.success({ text: `All tasks have been cleared` }); 62 | } else if (doneTasks.selectTask === `${chalk.red(cancel)}`) { 63 | console.clear(); 64 | console.log(`No task Selected.`); 65 | } else { 66 | await remove(config, doneTasks.selectTask, proj); 67 | } 68 | } else { 69 | console.clear(); 70 | console.log( 71 | `No tasks are marked ${config?.emoji ?? true ? "✅" : ""}done.` 72 | ); 73 | } 74 | } else { 75 | console.log(`${chalk.red(proj)}: No such project.`); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/actions/add.ts: -------------------------------------------------------------------------------- 1 | import * as chalk from "chalk"; 2 | import * as inquirer from "inquirer"; 3 | import * as inquirerDate from "inquirer-date-prompt"; 4 | import * as fs from "fs"; 5 | import { homedir } from "os"; 6 | import fileCheck from "./fileCheck"; 7 | 8 | const STORAGE_PATH = homedir() + "/.tasklist/tasklist.json"; 9 | 10 | fileCheck(); 11 | 12 | let tasks = JSON.parse(fs.readFileSync(STORAGE_PATH, "utf-8")); 13 | 14 | inquirer.registerPrompt("date", inquirerDate as any); 15 | 16 | // function to add new tasks to a project. 17 | export default async function add( 18 | config: configType, 19 | proj: string = config?.default ?? "default" 20 | ) { 21 | if (Object.keys(tasks).includes(proj)) { 22 | const newTask = await inquirer.prompt({ 23 | name: "taskName", 24 | type: "input", 25 | prefix: config?.emoji ?? true ? "❓" : undefined, 26 | message: "Task name", 27 | default() { 28 | return "New task"; 29 | }, 30 | }); 31 | const confirmDueDate = await inquirer.prompt({ 32 | name: "dueDate", 33 | type: "confirm", 34 | prefix: config?.emoji ?? true ? "❓" : undefined, 35 | message: "Add due date", 36 | default() { 37 | return false; 38 | }, 39 | }); 40 | const taskDueDate = confirmDueDate.dueDate 41 | ? await inquirer.prompt({ 42 | name: "dueDate", 43 | type: "date", 44 | prefix: config?.emoji ?? true ? "📅" : undefined, 45 | message: "Due date", 46 | default() { 47 | return new Date(new Date().getTime() + 24 * 60 * 60 * 1000); 48 | }, 49 | }) 50 | : null; 51 | 52 | tasks[proj].push({ 53 | id: tasks[proj].length === 0 ? 0 : tasks[proj].slice(-1)[0].id + 1, 54 | name: newTask.taskName, 55 | status: false, 56 | date: confirmDueDate.dueDate 57 | ? (taskDueDate as any).dueDate 58 | : "no due date", 59 | priority: null, 60 | }); 61 | 62 | fs.writeFileSync(STORAGE_PATH, JSON.stringify(tasks)); 63 | console.clear(); 64 | console.log( 65 | `New task ${chalk.yellow(newTask.taskName)} created in ${chalk.blue( 66 | proj 67 | )}.` + 68 | (confirmDueDate.dueDate 69 | ? ` Due date: ${chalk.green( 70 | (taskDueDate as any).dueDate.toLocaleTimeString([], { 71 | year: "numeric", 72 | month: "numeric", 73 | day: "numeric", 74 | hour: "2-digit", 75 | minute: "2-digit", 76 | }) 77 | )}` 78 | : "") 79 | ); 80 | } else { 81 | console.log(`${chalk.red(proj)}: No such project.`); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/tasklist.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import * as yargs from "yargs"; 4 | import * as fs from "fs"; 5 | import { homedir } from "os"; 6 | import { 7 | add, 8 | addProj, 9 | done, 10 | help, 11 | list, 12 | listAll, 13 | listDate, 14 | listProj, 15 | removeProj, 16 | } from "./actions/index.js"; 17 | 18 | // function to initialize tasklist-cli 19 | async function taskList() { 20 | // check for config.json 21 | const CONFIG = homedir() + "/.tasklist/config.json"; 22 | let config; 23 | if (fs.existsSync(CONFIG)) { 24 | if (fs.readFileSync(CONFIG).length === 0) { 25 | fs.writeFileSync(CONFIG, JSON.stringify({})); 26 | } 27 | config = JSON.parse(fs.readFileSync(CONFIG, "utf-8")); 28 | } 29 | 30 | // task --help 31 | help(); 32 | 33 | if ( 34 | (yargs.argv as any)._[0] === "list" || 35 | (yargs.argv as any)._[0] === "ls" 36 | ) { 37 | console.clear(); 38 | (yargs.argv as any)._[1] 39 | ? list(config, (yargs.argv as any)._[1]) 40 | : list(config); 41 | } else if ( 42 | (yargs.argv as any)._[0] === "listall" || 43 | (yargs.argv as any)._[0] === "la" 44 | ) { 45 | console.clear(); 46 | listAll(config); 47 | } else if ( 48 | (yargs.argv as any)._[0] === "listdate" || 49 | (yargs.argv as any)._[0] === "ld" 50 | ) { 51 | console.clear(); 52 | (yargs.argv as any)._[1] 53 | ? listDate(config, (yargs.argv as any)._[1]) 54 | : listDate(config); 55 | } else if ( 56 | (yargs.argv as any)._[0] === "add" || 57 | (yargs.argv as any)._[0] === "a" 58 | ) { 59 | console.clear(); 60 | add(config); 61 | } else if ( 62 | (yargs.argv as any)._[0] === "addto" || 63 | (yargs.argv as any)._[0] === "at" 64 | ) { 65 | console.clear(); 66 | (yargs.argv as any)._[1] 67 | ? add(config, (yargs.argv as any)._[1]) 68 | : yargs.showHelp(); 69 | } else if ( 70 | (yargs.argv as any)._[0] === "done" || 71 | (yargs.argv as any)._[0] === "d" 72 | ) { 73 | console.clear(); 74 | (yargs.argv as any)._[1] 75 | ? done(config, (yargs.argv as any)._[1]) 76 | : done(config); 77 | } else if ( 78 | (yargs.argv as any)._[0] === "addproj" || 79 | (yargs.argv as any)._[0] === "ap" 80 | ) { 81 | console.clear(); 82 | addProj(config); 83 | } else if ( 84 | (yargs.argv as any)._[0] === "listproj" || 85 | (yargs.argv as any)._[0] === "lp" 86 | ) { 87 | console.clear(); 88 | listProj(config); 89 | } else if ( 90 | (yargs.argv as any)._[0] === "remove" || 91 | (yargs.argv as any)._[0] === "rm" 92 | ) { 93 | console.clear(); 94 | (yargs.argv as any)._[1] 95 | ? removeProj(config, (yargs.argv as any)._[1]) 96 | : yargs.showHelp(); 97 | } else { 98 | yargs.showHelp(); 99 | } 100 | } 101 | 102 | (async () => { 103 | console.clear(); 104 | await taskList(); 105 | })(); 106 | -------------------------------------------------------------------------------- /src/actions/listDate.ts: -------------------------------------------------------------------------------- 1 | import * as chalk from "chalk"; 2 | import * as inquirer from "inquirer"; 3 | import * as fs from "fs"; 4 | import { homedir } from "os"; 5 | import { default as remove } from "./remove.js"; 6 | 7 | const STORAGE_PATH = homedir() + "/.tasklist/tasklist.json"; 8 | 9 | let tasks = JSON.parse(fs.readFileSync(STORAGE_PATH, "utf-8")); 10 | 11 | type taskType = { 12 | id: number; 13 | name: string; 14 | status: boolean; 15 | date: string; 16 | }; 17 | 18 | // function to list pending tasks with date. 19 | export default async function listDate( 20 | config: configType, 21 | proj: string = "default" 22 | ) { 23 | const cancel = `${config?.emoji ?? true ? "❌ " : ""}cancel`; 24 | 25 | if (Object.keys(tasks).includes(proj)) { 26 | let taskList = []; 27 | let sortDateArray = tasks[proj]; 28 | sortDateArray.sort( 29 | (a: taskType, b: taskType) => +new Date(a.date) - +new Date(b.date) 30 | ); 31 | sortDateArray.forEach((element: taskType) => { 32 | if (!element.status) { 33 | let dueDate = new Date(element.date); 34 | let today = new Date(); 35 | taskList.push({ 36 | value: element.id, 37 | name: 38 | element.name + 39 | " " + 40 | (element.date !== "no due date" 41 | ? today <= dueDate 42 | ? chalk.green( 43 | dueDate.toLocaleTimeString([], { 44 | year: "numeric", 45 | month: "numeric", 46 | day: "numeric", 47 | hour: "2-digit", 48 | minute: "2-digit", 49 | }) 50 | ) 51 | : chalk.red( 52 | dueDate.toLocaleTimeString([], { 53 | year: "numeric", 54 | month: "numeric", 55 | day: "numeric", 56 | hour: "2-digit", 57 | minute: "2-digit", 58 | }) 59 | ) 60 | : `${chalk.yellow(element.date)}`), 61 | }); 62 | } 63 | }); 64 | if (taskList.length) { 65 | taskList.push(new inquirer.Separator()); 66 | taskList.push(`${chalk.red(cancel)}`); 67 | 68 | const listTasks = await inquirer.prompt({ 69 | name: "selectTask", 70 | type: "list", 71 | prefix: config?.emoji ?? true ? "📅" : undefined, 72 | message: `Choose a task to mark done (${chalk.yellow(proj)})`, 73 | choices: taskList, 74 | pageSize: taskList.length, 75 | }); 76 | 77 | if (listTasks.selectTask === `${chalk.red(cancel)}`) { 78 | console.clear(); 79 | console.log(`No task Choosen.`); 80 | } else { 81 | await remove(config, listTasks.selectTask, proj); 82 | } 83 | } else { 84 | console.clear(); 85 | console.log( 86 | `No tasks are marked ${ 87 | config?.emoji ?? true ? "📝" : "" 88 | }todo in ${chalk.yellow(proj)}.` 89 | ); 90 | } 91 | } else { 92 | console.log(`${chalk.red(proj)}: No such project.`); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | tasklist-cli 3 |

4 | 5 | # Tasklist-cli 6 | 7 | [![npm (custom registry)](https://img.shields.io/npm/v/tasklist-cli/latest?logo=npm&logoColor=%23ffffff®istry_uri=https%3A%2F%2Fregistry.npmjs.com)](https://www.npmjs.com/package/tasklist-cli) 8 | [![GitHub issues](https://img.shields.io/github/issues-raw/manojuppala/tasklist-cli?logo=GITHUB)](https://github.com/manojuppala/tasklist-cli/issues) 9 | [![npm](https://img.shields.io/npm/dw/tasklist-cli?color=yellow&logo=npm)](https://www.npmjs.com/package/tasklist-cli) 10 | [![NPM](https://img.shields.io/npm/l/tasklist-cli?color=%239e1818&label=License®istry_uri=https%3A%2F%2Fregistry.npmjs.com)](https://github.com/manojuppala/tasklist-cli/blob/main/LICENSE) 11 | 12 | Tasklist-cli is a simple and elegant command line application to manage tasks and todos. Tasklist-cli exists to bring all the needed functionalities for simple project management to the terminal. No graphical interface is needed; this tool is easy enough to use to improve your workflow. 13 | 14 | ## Features 15 | 16 | - add todo tasks. 17 | - mark tasks as done. 18 | - revoke done tasks. 19 | - delete done tasks. 20 | - add due date to a task. 21 | - add subtasks to projects. 22 | 23 | ## Preview 24 | 25 |

26 | 27 |

28 | 29 | ## Installation 30 | 31 | using Node package manager 32 | 33 | ```shell 34 | npm i tasklist-cli -g 35 | ``` 36 | 37 | you should now be able to use the command `task` 38 | 39 | ## Usage 40 | 41 | you can perform any of the actions listed below. 42 | 43 | ```shell 44 | task [action] [project_name] 45 | ``` 46 | 47 | ## Actions 48 | 49 | ### `add` 50 | 51 | - Adds a new task to default list (or project). 52 | 53 | ```shell 54 | task add 55 | task a 56 | ``` 57 | 58 | ### `list` 59 | 60 | - Lists tasks that are marked undone from specified project. 61 | 62 | ```shell 63 | task list [project_name] 64 | task ls [project_name] 65 | ``` 66 | 67 | ### `listdate` 68 | 69 | - Lists tasks marked undone along with due date. 70 | 71 | ```shell 72 | task listdate [project_name] 73 | task ld [project_name] 74 | ``` 75 | 76 | _Note: [project_name] is optional._ 77 | 78 | ### `listproj` 79 | 80 | - Lists all available projects. 81 | 82 | ```shell 83 | task listproj 84 | task lp 85 | ``` 86 | 87 | ### `done` 88 | 89 | - Lists all the tasks that are marked done. 90 | 91 | ```shell 92 | task done [project_name] 93 | task d [project_name] 94 | ``` 95 | 96 | Read about all the possible commands in [USAGE](https://github.com/manojuppala/tasklist-cli/blob/main/USAGE.md) file. 97 | 98 | ## Dependencies 99 | 100 | - [chalk](https://www.npmjs.com/package/chalk) 101 | - [inquirer](https://www.npmjs.com/package/inquirer) 102 | - [inquirer-date-prompt](https://www.npmjs.com/package/inquirer-date-prompt) 103 | - [nanospinner](https://www.npmjs.com/package/nanospinner) 104 | - [yargs](https://www.npmjs.com/package/yargs) 105 | 106 | ## License 107 | 108 | ``` 109 | The files and scripts in this repository are licensed under the MIT License, 110 | which is a very permissive license allowing you to use, modify, copy, 111 | distribute, sell, give away, etc. the software. In other words, 112 | do what you want with it. The only requirement with the MIT License is 113 | that the license and copyright notice must be provided with the software. 114 | ``` 115 | 116 | 117 | tasklist-cli 118 | 119 | --------------------------------------------------------------------------------