@task-mcp/cli 1.0.4 → 1.0.6

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.
@@ -0,0 +1,267 @@
1
+ /**
2
+ * CLI Inbox Commands
3
+ * Quick capture and management of ideas
4
+ */
5
+
6
+ import { c } from "../ansi.js";
7
+ import { InboxStore, TaskStore, ProjectStore } from "@task-mcp/mcp-server/storage";
8
+ import { parseInboxInput } from "@task-mcp/shared";
9
+
10
+ const inboxStore = new InboxStore();
11
+ const taskStore = new TaskStore();
12
+ const projectStore = new ProjectStore();
13
+
14
+ export interface InboxAddOptions {
15
+ content: string;
16
+ source?: string;
17
+ }
18
+
19
+ export interface InboxListOptions {
20
+ all?: boolean;
21
+ status?: "pending" | "promoted" | "discarded";
22
+ }
23
+
24
+ export interface InboxPromoteOptions {
25
+ itemId: string;
26
+ projectId?: string;
27
+ title?: string;
28
+ priority?: "critical" | "high" | "medium" | "low";
29
+ }
30
+
31
+ /**
32
+ * Add item to inbox with natural language parsing
33
+ */
34
+ export async function inboxAddCmd(options: InboxAddOptions): Promise<void> {
35
+ const parsed = parseInboxInput(options.content);
36
+
37
+ const item = await inboxStore.add({
38
+ content: parsed.content,
39
+ source: options.source ?? "cli",
40
+ tags: parsed.tags,
41
+ });
42
+
43
+ console.log();
44
+ console.log(c.green("✓") + " Captured to inbox");
45
+ console.log();
46
+ console.log(" " + c.dim("ID:") + " " + c.cyan(item.id));
47
+ console.log(" " + c.dim("Content:") + " " + item.content);
48
+ if (parsed.tags?.length) {
49
+ console.log(" " + c.dim("Tags:") + " " + parsed.tags.map((t) => c.yellow("#" + t)).join(" "));
50
+ }
51
+ console.log();
52
+ console.log(c.dim("Use 'task inbox promote " + item.id + "' to convert to task"));
53
+ console.log();
54
+ }
55
+
56
+ /**
57
+ * List inbox items
58
+ */
59
+ export async function inboxListCmd(options: InboxListOptions): Promise<void> {
60
+ const status = options.all ? undefined : (options.status ?? "pending");
61
+ const items = await inboxStore.list({ status });
62
+
63
+ console.log();
64
+
65
+ if (items.length === 0) {
66
+ console.log(c.dim(status ? `No ${status} items in inbox.` : "Inbox is empty."));
67
+ console.log();
68
+ return;
69
+ }
70
+
71
+ // Header
72
+ const title = status ? `Inbox (${status})` : "Inbox (all)";
73
+ console.log(c.bold(c.cyan(title)) + c.dim(` - ${items.length} items`));
74
+ console.log(c.dim("─".repeat(50)));
75
+ console.log();
76
+
77
+ for (const item of items) {
78
+ const statusIcon =
79
+ item.status === "pending" ? c.yellow("○") :
80
+ item.status === "promoted" ? c.green("✓") :
81
+ c.dim("×");
82
+
83
+ const date = new Date(item.capturedAt).toLocaleDateString();
84
+ const tags = item.tags?.length
85
+ ? " " + item.tags.map((t: string) => c.dim("#" + t)).join(" ")
86
+ : "";
87
+
88
+ console.log(`${statusIcon} ${c.cyan(item.id)} ${c.dim("(" + date + ")")}`);
89
+ console.log(` ${item.content}${tags}`);
90
+
91
+ if (item.promotedToTaskId) {
92
+ console.log(` ${c.dim("→ Task:")} ${c.green(item.promotedToTaskId)}`);
93
+ }
94
+ console.log();
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Show inbox item details
100
+ */
101
+ export async function inboxGetCmd(itemId: string): Promise<void> {
102
+ const item = await inboxStore.get(itemId);
103
+
104
+ if (!item) {
105
+ console.log();
106
+ console.log(c.error(`Inbox item not found: ${itemId}`));
107
+ console.log();
108
+ return;
109
+ }
110
+
111
+ console.log();
112
+ console.log(c.bold(c.cyan("Inbox Item")));
113
+ console.log(c.dim("─".repeat(40)));
114
+ console.log();
115
+ console.log(" " + c.dim("ID:") + " " + item.id);
116
+ console.log(" " + c.dim("Status:") + " " + formatStatus(item.status));
117
+ console.log(" " + c.dim("Captured:") + " " + new Date(item.capturedAt).toLocaleString());
118
+ if (item.source) {
119
+ console.log(" " + c.dim("Source:") + " " + item.source);
120
+ }
121
+ if (item.tags?.length) {
122
+ console.log(" " + c.dim("Tags:") + " " + item.tags.map((t: string) => c.yellow("#" + t)).join(" "));
123
+ }
124
+ console.log();
125
+ console.log(c.bold("Content:"));
126
+ console.log(" " + item.content);
127
+
128
+ if (item.promotedToTaskId) {
129
+ console.log();
130
+ console.log(c.green("→ Promoted to: " + item.promotedToTaskId));
131
+ }
132
+ console.log();
133
+ }
134
+
135
+ /**
136
+ * Promote inbox item to task
137
+ */
138
+ export async function inboxPromoteCmd(options: InboxPromoteOptions): Promise<void> {
139
+ const item = await inboxStore.get(options.itemId);
140
+
141
+ if (!item) {
142
+ console.log();
143
+ console.log(c.error(`Inbox item not found: ${options.itemId}`));
144
+ console.log();
145
+ return;
146
+ }
147
+
148
+ if (item.status === "promoted") {
149
+ console.log();
150
+ console.log(c.warning(`Item already promoted to: ${item.promotedToTaskId}`));
151
+ console.log();
152
+ return;
153
+ }
154
+
155
+ // Get project
156
+ let projectId = options.projectId;
157
+ if (!projectId) {
158
+ const projects = await projectStore.list();
159
+ if (projects.length === 0) {
160
+ console.log();
161
+ console.log(c.error("No projects found. Create a project first."));
162
+ console.log();
163
+ return;
164
+ }
165
+ // Use first project if not specified
166
+ projectId = projects[0]!.id;
167
+ }
168
+
169
+ const project = await projectStore.get(projectId);
170
+ if (!project) {
171
+ console.log();
172
+ console.log(c.error(`Project not found: ${projectId}`));
173
+ console.log();
174
+ return;
175
+ }
176
+
177
+ // Create task
178
+ const task = await taskStore.create(projectId, {
179
+ title: options.title ?? item.content.slice(0, 100),
180
+ description: item.content,
181
+ priority: options.priority ?? "medium",
182
+ tags: item.tags,
183
+ });
184
+
185
+ // Mark as promoted
186
+ await inboxStore.promote(options.itemId, task.id);
187
+
188
+ console.log();
189
+ console.log(c.green("✓") + " Promoted to task");
190
+ console.log();
191
+ console.log(" " + c.dim("Task ID:") + " " + c.cyan(task.id));
192
+ console.log(" " + c.dim("Title:") + " " + task.title);
193
+ console.log(" " + c.dim("Project:") + " " + project.name);
194
+ console.log(" " + c.dim("Priority:") + " " + formatPriority(task.priority));
195
+ console.log();
196
+ }
197
+
198
+ /**
199
+ * Discard inbox item
200
+ */
201
+ export async function inboxDiscardCmd(itemId: string): Promise<void> {
202
+ const item = await inboxStore.discard(itemId);
203
+
204
+ if (!item) {
205
+ console.log();
206
+ console.log(c.error(`Inbox item not found: ${itemId}`));
207
+ console.log();
208
+ return;
209
+ }
210
+
211
+ console.log();
212
+ console.log(c.dim("×") + " Discarded: " + item.content.slice(0, 50) + "...");
213
+ console.log();
214
+ }
215
+
216
+ /**
217
+ * Delete inbox item permanently
218
+ */
219
+ export async function inboxDeleteCmd(itemId: string): Promise<void> {
220
+ const deleted = await inboxStore.delete(itemId);
221
+
222
+ if (!deleted) {
223
+ console.log();
224
+ console.log(c.error(`Inbox item not found: ${itemId}`));
225
+ console.log();
226
+ return;
227
+ }
228
+
229
+ console.log();
230
+ console.log(c.green("✓") + " Deleted: " + itemId);
231
+ console.log();
232
+ }
233
+
234
+ /**
235
+ * Show inbox count
236
+ */
237
+ export async function inboxCountCmd(): Promise<void> {
238
+ const count = await inboxStore.getPendingCount();
239
+
240
+ console.log();
241
+ if (count > 0) {
242
+ console.log(c.yellow("📥") + ` ${count} pending item${count === 1 ? "" : "s"} in inbox`);
243
+ } else {
244
+ console.log(c.dim("Inbox is empty"));
245
+ }
246
+ console.log();
247
+ }
248
+
249
+ // Helper functions
250
+ function formatStatus(status: string): string {
251
+ switch (status) {
252
+ case "pending": return c.yellow("pending");
253
+ case "promoted": return c.green("promoted");
254
+ case "discarded": return c.dim("discarded");
255
+ default: return status;
256
+ }
257
+ }
258
+
259
+ function formatPriority(priority: string): string {
260
+ switch (priority) {
261
+ case "critical": return c.red("critical");
262
+ case "high": return c.yellow("high");
263
+ case "medium": return c.blue("medium");
264
+ case "low": return c.dim("low");
265
+ default: return priority;
266
+ }
267
+ }
@@ -85,7 +85,7 @@ export async function listTasksCmd(options: {
85
85
  { header: "Due", key: "dueDate", width: 12, format: (v) => formatDate(v as string) },
86
86
  ];
87
87
 
88
- console.log(table(tasks, columns));
88
+ console.log(table(tasks as unknown as Record<string, unknown>[], columns));
89
89
  console.log();
90
90
  }
91
91
 
package/src/index.ts CHANGED
@@ -16,16 +16,27 @@
16
16
  import { c, banner } from "./ansi.js";
17
17
  import { dashboard } from "./commands/dashboard.js";
18
18
  import { listTasksCmd, listProjectsCmd } from "./commands/list.js";
19
+ import { startInteractive } from "./interactive.js";
20
+ import {
21
+ inboxAddCmd,
22
+ inboxListCmd,
23
+ inboxGetCmd,
24
+ inboxPromoteCmd,
25
+ inboxDiscardCmd,
26
+ inboxDeleteCmd,
27
+ inboxCountCmd,
28
+ } from "./commands/inbox.js";
19
29
 
20
- const VERSION = "1.0.4";
30
+ import pkg from "../package.json" with { type: "json" };
31
+ export const VERSION = pkg.version;
21
32
 
22
- interface ParsedArgs {
33
+ export interface ParsedArgs {
23
34
  command: string;
24
35
  args: string[];
25
36
  flags: Record<string, string | boolean>;
26
37
  }
27
38
 
28
- function parseArgs(argv: string[]): ParsedArgs {
39
+ export function parseArgs(argv: string[]): ParsedArgs {
29
40
  const args = argv.slice(2); // Skip bun and script path
30
41
  const flags: Record<string, string | boolean> = {};
31
42
  const positional: string[] = [];
@@ -75,10 +86,23 @@ ${c.yellow("COMMANDS")}
75
86
  ${c.cyan("ls")} Alias for list
76
87
  ${c.cyan("projects")} List all projects
77
88
  ${c.cyan("p")} Alias for projects
89
+ ${c.cyan("inbox")} <subcommand> Manage inbox (add/list/get/promote/discard)
90
+ ${c.cyan("interactive")} Start interactive mode
91
+ ${c.cyan("i")} Alias for interactive
78
92
  ${c.cyan("help")} Show this help message
79
93
  ${c.cyan("version")} Show version
80
94
 
95
+ ${c.yellow("INBOX SUBCOMMANDS")}
96
+ ${c.cyan("inbox")} "content" Quick capture to inbox
97
+ ${c.cyan("inbox add")} "content" Add item with natural language
98
+ ${c.cyan("inbox list")} List pending items
99
+ ${c.cyan("inbox get")} <id> Show item details
100
+ ${c.cyan("inbox promote")} <id> Promote to task
101
+ ${c.cyan("inbox discard")} <id> Discard item
102
+ ${c.cyan("inbox count")} Show pending count
103
+
81
104
  ${c.yellow("OPTIONS")}
105
+ ${c.cyan("-i, --interactive")} Start interactive mode
82
106
  ${c.cyan("--status")} <status> Filter tasks by status (pending,in_progress,blocked,completed,cancelled)
83
107
  ${c.cyan("--priority")} <priority> Filter tasks by priority (critical,high,medium,low)
84
108
  ${c.cyan("--all")} Include completed/cancelled tasks
@@ -102,9 +126,94 @@ function showVersion(): void {
102
126
  console.log(`task-mcp-cli v${VERSION}`);
103
127
  }
104
128
 
129
+ async function handleInboxCommand(
130
+ args: string[],
131
+ flags: Record<string, string | boolean>
132
+ ): Promise<void> {
133
+ const subcommand = args[0];
134
+
135
+ // If first arg is not a subcommand, treat it as content for quick capture
136
+ const subcommands = ["add", "list", "ls", "get", "promote", "discard", "delete", "count"];
137
+ if (subcommand && !subcommands.includes(subcommand)) {
138
+ // Quick capture: task inbox "content here"
139
+ await inboxAddCmd({ content: args.join(" ") });
140
+ return;
141
+ }
142
+
143
+ switch (subcommand) {
144
+ case "add":
145
+ if (!args[1]) {
146
+ console.log(c.error("Usage: task inbox add \"content\""));
147
+ return;
148
+ }
149
+ await inboxAddCmd({ content: args.slice(1).join(" ") });
150
+ break;
151
+
152
+ case "list":
153
+ case "ls":
154
+ await inboxListCmd({
155
+ all: flags.all === true,
156
+ status: flags.status as "pending" | "promoted" | "discarded" | undefined,
157
+ });
158
+ break;
159
+
160
+ case "get":
161
+ if (!args[1]) {
162
+ console.log(c.error("Usage: task inbox get <item-id>"));
163
+ return;
164
+ }
165
+ await inboxGetCmd(args[1]);
166
+ break;
167
+
168
+ case "promote":
169
+ if (!args[1]) {
170
+ console.log(c.error("Usage: task inbox promote <item-id> [--project <id>] [--priority <p>]"));
171
+ return;
172
+ }
173
+ await inboxPromoteCmd({
174
+ itemId: args[1],
175
+ projectId: flags.project as string | undefined,
176
+ title: flags.title as string | undefined,
177
+ priority: flags.priority as "critical" | "high" | "medium" | "low" | undefined,
178
+ });
179
+ break;
180
+
181
+ case "discard":
182
+ if (!args[1]) {
183
+ console.log(c.error("Usage: task inbox discard <item-id>"));
184
+ return;
185
+ }
186
+ await inboxDiscardCmd(args[1]);
187
+ break;
188
+
189
+ case "delete":
190
+ if (!args[1]) {
191
+ console.log(c.error("Usage: task inbox delete <item-id>"));
192
+ return;
193
+ }
194
+ await inboxDeleteCmd(args[1]);
195
+ break;
196
+
197
+ case "count":
198
+ await inboxCountCmd();
199
+ break;
200
+
201
+ default:
202
+ // No subcommand = list pending
203
+ await inboxListCmd({ all: false });
204
+ break;
205
+ }
206
+ }
207
+
105
208
  async function main(): Promise<void> {
106
209
  const { command, args, flags } = parseArgs(process.argv);
107
210
 
211
+ // Handle -i flag for interactive mode
212
+ if (flags.i === true || flags.interactive === true) {
213
+ await startInteractive();
214
+ return;
215
+ }
216
+
108
217
  try {
109
218
  switch (command) {
110
219
  case "dashboard":
@@ -129,6 +238,15 @@ async function main(): Promise<void> {
129
238
  });
130
239
  break;
131
240
 
241
+ case "interactive":
242
+ case "i":
243
+ await startInteractive();
244
+ break;
245
+
246
+ case "inbox":
247
+ await handleInboxCommand(args, flags);
248
+ break;
249
+
132
250
  case "help":
133
251
  case "-h":
134
252
  case "--help":
@@ -156,4 +274,7 @@ async function main(): Promise<void> {
156
274
  }
157
275
  }
158
276
 
159
- main();
277
+ // Only run main when directly executed, not when imported
278
+ if (import.meta.main) {
279
+ main();
280
+ }