taskin 0.1.0-beta.0 → 0.1.0-beta.2
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 +91 -12
- package/dist/index.d.ts +380 -5
- package/dist/index.js +1372 -25
- package/package.json +15 -11
- package/dist/commands/commands.types.d.ts +0 -13
- package/dist/commands/commands.types.js +0 -6
- package/dist/commands/commands.types.js.map +0 -1
- package/dist/commands/define-command/define-command.d.ts +0 -6
- package/dist/commands/define-command/define-command.js +0 -27
- package/dist/commands/define-command/define-command.js.map +0 -1
- package/dist/commands/define-command/define-command.test.d.ts +0 -4
- package/dist/commands/define-command/define-command.test.js +0 -125
- package/dist/commands/define-command/define-command.test.js.map +0 -1
- package/dist/commands/define-command/define-command.types.d.ts +0 -49
- package/dist/commands/define-command/define-command.types.js +0 -5
- package/dist/commands/define-command/define-command.types.js.map +0 -1
- package/dist/commands/define-command/index.d.ts +0 -5
- package/dist/commands/define-command/index.js +0 -6
- package/dist/commands/define-command/index.js.map +0 -1
- package/dist/commands/finish.d.ts +0 -4
- package/dist/commands/finish.js +0 -69
- package/dist/commands/finish.js.map +0 -1
- package/dist/commands/index.d.ts +0 -9
- package/dist/commands/index.js +0 -10
- package/dist/commands/index.js.map +0 -1
- package/dist/commands/init.d.ts +0 -4
- package/dist/commands/init.js +0 -192
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/lint.d.ts +0 -4
- package/dist/commands/lint.js +0 -31
- package/dist/commands/lint.js.map +0 -1
- package/dist/commands/list.d.ts +0 -4
- package/dist/commands/list.js +0 -132
- package/dist/commands/list.js.map +0 -1
- package/dist/commands/pause.d.ts +0 -4
- package/dist/commands/pause.js +0 -69
- package/dist/commands/pause.js.map +0 -1
- package/dist/commands/start.d.ts +0 -4
- package/dist/commands/start.js +0 -70
- package/dist/commands/start.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lib/colors.d.ts +0 -26
- package/dist/lib/colors.js +0 -42
- package/dist/lib/colors.js.map +0 -1
- package/dist/lib/file-system-task-linter/file-system-task-linter.d.ts +0 -36
- package/dist/lib/file-system-task-linter/file-system-task-linter.js +0 -219
- package/dist/lib/file-system-task-linter/file-system-task-linter.js.map +0 -1
- package/dist/lib/file-system-task-linter/file-system-task-linter.mock.d.ts +0 -81
- package/dist/lib/file-system-task-linter/file-system-task-linter.mock.js +0 -152
- package/dist/lib/file-system-task-linter/file-system-task-linter.mock.js.map +0 -1
- package/dist/lib/file-system-task-linter/file-system-task-linter.test.d.ts +0 -4
- package/dist/lib/file-system-task-linter/file-system-task-linter.test.js +0 -146
- package/dist/lib/file-system-task-linter/file-system-task-linter.test.js.map +0 -1
- package/dist/lib/file-system-task-linter/file-system-task-linter.types.d.ts +0 -52
- package/dist/lib/file-system-task-linter/file-system-task-linter.types.js +0 -6
- package/dist/lib/file-system-task-linter/file-system-task-linter.types.js.map +0 -1
- package/dist/lib/file-system-task-linter/index.d.ts +0 -6
- package/dist/lib/file-system-task-linter/index.js +0 -7
- package/dist/lib/file-system-task-linter/index.js.map +0 -1
- package/dist/lib/help.d.ts +0 -4
- package/dist/lib/help.js +0 -104
- package/dist/lib/help.js.map +0 -1
- package/dist/lib/index.d.ts +0 -6
- package/dist/lib/index.js +0 -7
- package/dist/lib/index.js.map +0 -1
- package/dist/lib/provider-installer/index.d.ts +0 -5
- package/dist/lib/provider-installer/index.js +0 -6
- package/dist/lib/provider-installer/index.js.map +0 -1
- package/dist/lib/provider-installer/provider-installer.d.ts +0 -28
- package/dist/lib/provider-installer/provider-installer.js +0 -86
- package/dist/lib/provider-installer/provider-installer.js.map +0 -1
- package/dist/lib/provider-installer/provider-installer.types.d.ts +0 -4
- package/dist/lib/provider-installer/provider-installer.types.js +0 -5
- package/dist/lib/provider-installer/provider-installer.types.js.map +0 -1
- package/dist/lib/provider-installer.d.ts +0 -27
- package/dist/lib/provider-installer.js +0 -86
- package/dist/lib/provider-installer.js.map +0 -1
- package/dist/lib/provider-registry/index.d.ts +0 -5
- package/dist/lib/provider-registry/index.js +0 -6
- package/dist/lib/provider-registry/index.js.map +0 -1
- package/dist/lib/provider-registry/provider-registry.d.ts +0 -9
- package/dist/lib/provider-registry/provider-registry.js +0 -111
- package/dist/lib/provider-registry/provider-registry.js.map +0 -1
- package/dist/lib/provider-registry/provider-registry.types.d.ts +0 -5
- package/dist/lib/provider-registry/provider-registry.types.js +0 -6
- package/dist/lib/provider-registry/provider-registry.types.js.map +0 -1
- package/dist/lib/provider-registry.d.ts +0 -26
- package/dist/lib/provider-registry.js +0 -111
- package/dist/lib/provider-registry.js.map +0 -1
- package/dist/lib/task-linter/index.d.ts +0 -5
- package/dist/lib/task-linter/index.js +0 -6
- package/dist/lib/task-linter/index.js.map +0 -1
- package/dist/lib/task-linter/task-linter.d.ts +0 -36
- package/dist/lib/task-linter/task-linter.js +0 -219
- package/dist/lib/task-linter/task-linter.js.map +0 -1
- package/dist/lib/task-linter/task-linter.types.d.ts +0 -52
- package/dist/lib/task-linter/task-linter.types.js +0 -6
- package/dist/lib/task-linter/task-linter.types.js.map +0 -1
- package/dist/lib/task-linter.d.ts +0 -29
- package/dist/lib/task-linter.js +0 -215
- package/dist/lib/task-linter.js.map +0 -1
- package/dist/lib/task-linter.types.d.ts +0 -38
- package/dist/lib/task-linter.types.js +0 -5
- package/dist/lib/task-linter.types.js.map +0 -1
- package/dist/main.d.ts +0 -14
- package/dist/main.js +0 -29
- package/dist/main.js.map +0 -1
- package/dist/taskin.d.ts +0 -33
- package/dist/taskin.js +0 -101
- package/dist/taskin.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,38 +1,1385 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
4
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
5
|
+
}) : x)(function(x) {
|
|
6
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
7
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
8
|
+
});
|
|
9
|
+
var __esm = (fn, res) => function __init() {
|
|
10
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// ../../node_modules/.pnpm/tsup@8.5.0_postcss@8.5.6_tsx@4.20.6_typescript@5.9.3/node_modules/tsup/assets/esm_shims.js
|
|
14
|
+
import path from "path";
|
|
15
|
+
import { fileURLToPath } from "url";
|
|
16
|
+
var init_esm_shims = __esm({
|
|
17
|
+
"../../node_modules/.pnpm/tsup@8.5.0_postcss@8.5.6_tsx@4.20.6_typescript@5.9.3/node_modules/tsup/assets/esm_shims.js"() {
|
|
18
|
+
"use strict";
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// src/index.ts
|
|
23
|
+
init_esm_shims();
|
|
24
|
+
import { Command } from "commander";
|
|
25
|
+
|
|
26
|
+
// src/commands/finish.ts
|
|
27
|
+
init_esm_shims();
|
|
28
|
+
|
|
29
|
+
// ../fs-task-provider/src/index.ts
|
|
30
|
+
init_esm_shims();
|
|
31
|
+
|
|
32
|
+
// ../fs-task-provider/src/fs-task-provider.ts
|
|
33
|
+
init_esm_shims();
|
|
34
|
+
import { promises as fs } from "fs";
|
|
35
|
+
import path2 from "path";
|
|
36
|
+
var FileSystemTaskProvider = class {
|
|
37
|
+
constructor(tasksDirectory) {
|
|
38
|
+
this.tasksDirectory = tasksDirectory;
|
|
39
|
+
}
|
|
40
|
+
async findTask(taskId) {
|
|
41
|
+
const files = await fs.readdir(this.tasksDirectory);
|
|
42
|
+
const taskFile = files.find(
|
|
43
|
+
(file) => file.startsWith(`task-${taskId}-`) && file.endsWith(".md")
|
|
44
|
+
);
|
|
45
|
+
if (!taskFile) {
|
|
46
|
+
return void 0;
|
|
47
|
+
}
|
|
48
|
+
const filePath = path2.join(this.tasksDirectory, taskFile);
|
|
49
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
50
|
+
const titleMatch = content.match(/^# Task \d+ - (.+)$/m);
|
|
51
|
+
const title = titleMatch ? titleMatch[1] : "Untitled";
|
|
52
|
+
const task = {
|
|
53
|
+
id: taskId,
|
|
54
|
+
title,
|
|
55
|
+
content,
|
|
56
|
+
filePath,
|
|
57
|
+
userId: "unknown",
|
|
58
|
+
status: "pending",
|
|
59
|
+
type: "feat",
|
|
60
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
61
|
+
};
|
|
62
|
+
return task;
|
|
63
|
+
}
|
|
64
|
+
async updateTask(task) {
|
|
65
|
+
await fs.writeFile(task.filePath, task.content);
|
|
66
|
+
}
|
|
67
|
+
async getAllTasks() {
|
|
68
|
+
const files = await fs.readdir(this.tasksDirectory);
|
|
69
|
+
const taskFiles = files.filter(
|
|
70
|
+
(file) => file.startsWith("task-") && file.endsWith(".md")
|
|
71
|
+
);
|
|
72
|
+
const tasks = [];
|
|
73
|
+
for (const file of taskFiles) {
|
|
74
|
+
const filePath = path2.join(this.tasksDirectory, file);
|
|
75
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
76
|
+
const idMatch = file.match(/^task-(\d+)-/);
|
|
77
|
+
const taskId = idMatch ? idMatch[1] : "unknown";
|
|
78
|
+
const titleMatch = content.match(/^# .*Task.*?[—-]\s*(.+)$/im);
|
|
79
|
+
const title = titleMatch ? titleMatch[1].trim() : "Untitled";
|
|
80
|
+
const headerSection = content.split(/^##/m)[0];
|
|
81
|
+
const statusMatch = headerSection.match(/^Status:\s*(.+)$/im);
|
|
82
|
+
const typeMatch = headerSection.match(/^Type:\s*(.+)$/im);
|
|
83
|
+
const userMatch = headerSection.match(/^Assignee:\s*(.+)$/im);
|
|
84
|
+
const task = {
|
|
85
|
+
id: taskId,
|
|
86
|
+
title,
|
|
87
|
+
content,
|
|
88
|
+
filePath,
|
|
89
|
+
userId: userMatch ? userMatch[1].trim() : void 0,
|
|
90
|
+
status: statusMatch ? statusMatch[1].trim().toLowerCase() : "pending",
|
|
91
|
+
type: typeMatch ? typeMatch[1].trim().toLowerCase() : "feat",
|
|
92
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
93
|
+
};
|
|
94
|
+
tasks.push(task);
|
|
95
|
+
}
|
|
96
|
+
return tasks;
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// ../task-manager/src/index.ts
|
|
101
|
+
init_esm_shims();
|
|
102
|
+
|
|
103
|
+
// ../task-manager/src/task-manager.ts
|
|
104
|
+
init_esm_shims();
|
|
105
|
+
var TaskManager = class {
|
|
106
|
+
constructor(taskProvider) {
|
|
107
|
+
this.taskProvider = taskProvider;
|
|
108
|
+
}
|
|
109
|
+
async startTask(taskId) {
|
|
110
|
+
const task = await this.taskProvider.findTask(taskId);
|
|
111
|
+
if (!task) {
|
|
112
|
+
throw new Error(`Task with ID '${taskId}' not found.`);
|
|
113
|
+
}
|
|
114
|
+
if (task.status === "in-progress") {
|
|
115
|
+
throw new Error(`Task '${taskId}' is already in progress.`);
|
|
116
|
+
}
|
|
117
|
+
if (task.status === "done") {
|
|
118
|
+
throw new Error(`Task '${taskId}' is already done.`);
|
|
119
|
+
}
|
|
120
|
+
const updatedTask = { ...task, status: "in-progress" };
|
|
121
|
+
await this.taskProvider.updateTask(updatedTask);
|
|
122
|
+
return updatedTask;
|
|
123
|
+
}
|
|
124
|
+
async finishTask(taskId) {
|
|
125
|
+
const task = await this.taskProvider.findTask(taskId);
|
|
126
|
+
if (!task) {
|
|
127
|
+
throw new Error(`Task with ID '${taskId}' not found.`);
|
|
128
|
+
}
|
|
129
|
+
const updatedTask = { ...task, status: "done" };
|
|
130
|
+
await this.taskProvider.updateTask(updatedTask);
|
|
131
|
+
return updatedTask;
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// ../task-manager/src/task-manager.mock.ts
|
|
136
|
+
init_esm_shims();
|
|
137
|
+
|
|
138
|
+
// ../task-manager/src/task-manager.types.ts
|
|
139
|
+
init_esm_shims();
|
|
140
|
+
|
|
141
|
+
// src/commands/finish.ts
|
|
142
|
+
import path3 from "path";
|
|
143
|
+
|
|
144
|
+
// src/lib/colors.ts
|
|
145
|
+
init_esm_shims();
|
|
146
|
+
import chalk from "chalk";
|
|
147
|
+
var colors = {
|
|
148
|
+
info: chalk.cyan,
|
|
149
|
+
success: chalk.green,
|
|
150
|
+
warning: chalk.yellow,
|
|
151
|
+
error: chalk.red,
|
|
152
|
+
highlight: chalk.bold.white,
|
|
153
|
+
secondary: chalk.gray,
|
|
154
|
+
normal: chalk.white
|
|
155
|
+
};
|
|
156
|
+
var icons = {
|
|
157
|
+
rocket: "\u{1F680}",
|
|
158
|
+
list: "\u{1F4CA}",
|
|
159
|
+
check: "\u2705",
|
|
160
|
+
pause: "\u23F8\uFE0F",
|
|
161
|
+
info: "\u{1F4A1}",
|
|
162
|
+
task: "\u{1F4CB}",
|
|
163
|
+
gear: "\u2699\uFE0F"
|
|
164
|
+
};
|
|
165
|
+
function printHeader(title, icon) {
|
|
166
|
+
console.log();
|
|
167
|
+
console.log(colors.highlight("\u2550".repeat(60)));
|
|
168
|
+
console.log(colors.highlight(`${icon} ${title}`));
|
|
169
|
+
console.log(colors.highlight("\u2550".repeat(60)));
|
|
170
|
+
console.log();
|
|
171
|
+
}
|
|
172
|
+
function success(message) {
|
|
173
|
+
console.log(colors.success(`\u2713 ${message}`));
|
|
174
|
+
}
|
|
175
|
+
function error(message) {
|
|
176
|
+
console.error(colors.error(`\u2717 ${message}`));
|
|
177
|
+
}
|
|
178
|
+
function info(message) {
|
|
179
|
+
console.log(colors.info(`\u2139 ${message}`));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// src/commands/define-command/index.ts
|
|
183
|
+
init_esm_shims();
|
|
184
|
+
|
|
185
|
+
// src/commands/define-command/define-command.ts
|
|
186
|
+
init_esm_shims();
|
|
187
|
+
var defineCommand = (config) => {
|
|
188
|
+
return (program2) => {
|
|
189
|
+
const cmd = program2.command(config.name).description(config.description);
|
|
190
|
+
if (config.alias) {
|
|
191
|
+
cmd.alias(config.alias);
|
|
192
|
+
}
|
|
193
|
+
config.options?.forEach((opt) => {
|
|
194
|
+
cmd.option(opt.flags, opt.description, opt.defaultValue);
|
|
195
|
+
});
|
|
196
|
+
cmd.action(async (...args) => {
|
|
197
|
+
try {
|
|
198
|
+
await config.handler(...args);
|
|
199
|
+
} catch (err) {
|
|
200
|
+
error(
|
|
201
|
+
`Failed to execute command: ${err instanceof Error ? err.message : String(err)}`
|
|
202
|
+
);
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
};
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
// src/commands/define-command/define-command.types.ts
|
|
210
|
+
init_esm_shims();
|
|
211
|
+
|
|
212
|
+
// src/commands/finish.ts
|
|
213
|
+
var finishCommand = defineCommand({
|
|
214
|
+
name: "finish <task-id>",
|
|
215
|
+
description: "\u2705 Complete a task",
|
|
216
|
+
alias: "done",
|
|
217
|
+
options: [
|
|
218
|
+
{
|
|
219
|
+
flags: "-s, --skip-update",
|
|
220
|
+
description: "Skip updating task status"
|
|
221
|
+
}
|
|
222
|
+
],
|
|
223
|
+
handler: async (taskId, options) => {
|
|
224
|
+
await finishTask(taskId, options);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
async function finishTask(taskId, options) {
|
|
228
|
+
printHeader(`Finishing Task ${taskId}`, "\u2705");
|
|
229
|
+
const normalizedId = taskId.replace(/^task-/, "").padStart(3, "0");
|
|
230
|
+
const tasksDir = path3.join(process.cwd(), "TASKS");
|
|
231
|
+
const taskProvider = new FileSystemTaskProvider(tasksDir);
|
|
232
|
+
const taskManager = new TaskManager(taskProvider);
|
|
233
|
+
const task = await taskProvider.findTask(normalizedId);
|
|
234
|
+
if (!task) {
|
|
235
|
+
error(`Task ${normalizedId} not found in TASKS/ directory`);
|
|
236
|
+
process.exit(1);
|
|
237
|
+
}
|
|
238
|
+
info(`Found task: ${task.title}`);
|
|
239
|
+
info(`Current status: ${task.status}`);
|
|
240
|
+
if (task.status === "done") {
|
|
241
|
+
error("Task is already done");
|
|
242
|
+
process.exit(1);
|
|
243
|
+
}
|
|
244
|
+
if (!options.skipUpdate) {
|
|
245
|
+
info("Marking task as done...");
|
|
246
|
+
const updatedTask = await taskManager.finishTask(task.id);
|
|
247
|
+
success(`Task ${updatedTask.id} completed successfully! \u{1F389}`);
|
|
248
|
+
success(`Status changed to: ${updatedTask.status}`);
|
|
249
|
+
} else {
|
|
250
|
+
info("Skipping status update (--skip-update flag)");
|
|
251
|
+
}
|
|
252
|
+
console.log();
|
|
253
|
+
info("Suggested commit message:");
|
|
254
|
+
const commitType = task.type || "feat";
|
|
255
|
+
console.log(
|
|
256
|
+
colors.highlight(` ${commitType}(task-${normalizedId}): ${task.title}`)
|
|
257
|
+
);
|
|
258
|
+
console.log();
|
|
259
|
+
info("Next steps:");
|
|
260
|
+
console.log(colors.secondary(" 1. Review your changes"));
|
|
261
|
+
console.log(colors.secondary(" 2. Commit: git add . && git commit"));
|
|
262
|
+
console.log(colors.secondary(" 3. Push: git push"));
|
|
263
|
+
console.log(colors.secondary(" 4. Create a Pull Request"));
|
|
264
|
+
console.log();
|
|
265
|
+
success("Great work! \u{1F680}");
|
|
266
|
+
console.log();
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// src/commands/init.ts
|
|
270
|
+
init_esm_shims();
|
|
271
|
+
import { existsSync as existsSync2, mkdirSync, writeFileSync } from "fs";
|
|
272
|
+
import inquirer from "inquirer";
|
|
273
|
+
import { join } from "path";
|
|
274
|
+
|
|
275
|
+
// src/lib/provider-installer/index.ts
|
|
276
|
+
init_esm_shims();
|
|
277
|
+
|
|
278
|
+
// src/lib/provider-installer/provider-installer.ts
|
|
279
|
+
init_esm_shims();
|
|
280
|
+
import { execSync } from "child_process";
|
|
281
|
+
import { existsSync } from "fs";
|
|
282
|
+
function detectPackageManager() {
|
|
283
|
+
if (existsSync("pnpm-lock.yaml")) {
|
|
284
|
+
return "pnpm";
|
|
285
|
+
}
|
|
286
|
+
if (existsSync("yarn.lock")) {
|
|
287
|
+
return "yarn";
|
|
288
|
+
}
|
|
289
|
+
return "npm";
|
|
290
|
+
}
|
|
291
|
+
function isProviderInstalled(packageName) {
|
|
292
|
+
try {
|
|
293
|
+
__require.resolve(packageName);
|
|
294
|
+
return true;
|
|
295
|
+
} catch {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
function installProvider(provider) {
|
|
300
|
+
const packageManager = detectPackageManager();
|
|
301
|
+
const { packageName } = provider;
|
|
302
|
+
console.log(`
|
|
303
|
+
\u{1F4E6} Installing ${packageName}...`);
|
|
304
|
+
try {
|
|
305
|
+
let command;
|
|
306
|
+
switch (packageManager) {
|
|
307
|
+
case "pnpm":
|
|
308
|
+
command = `pnpm add ${packageName}`;
|
|
309
|
+
break;
|
|
310
|
+
case "yarn":
|
|
311
|
+
command = `yarn add ${packageName}`;
|
|
312
|
+
break;
|
|
313
|
+
default:
|
|
314
|
+
command = `npm install ${packageName}`;
|
|
315
|
+
}
|
|
316
|
+
execSync(command, { stdio: "inherit" });
|
|
317
|
+
console.log(`\u2705 ${packageName} installed successfully!
|
|
318
|
+
`);
|
|
319
|
+
} catch (error2) {
|
|
320
|
+
console.error(`
|
|
321
|
+
\u274C Failed to install ${packageName}`);
|
|
322
|
+
throw error2;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
async function loadProvider(packageName) {
|
|
326
|
+
try {
|
|
327
|
+
const module = await import(packageName);
|
|
328
|
+
return module.default || module;
|
|
329
|
+
} catch (error2) {
|
|
330
|
+
console.error(`Failed to load provider: ${packageName}`);
|
|
331
|
+
throw error2;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
async function ensureProviderInstalled(provider) {
|
|
335
|
+
const { packageName } = provider;
|
|
336
|
+
if (!isProviderInstalled(packageName)) {
|
|
337
|
+
console.log(`
|
|
338
|
+
\u26A0\uFE0F Provider ${packageName} is not installed.`);
|
|
339
|
+
installProvider(provider);
|
|
340
|
+
}
|
|
341
|
+
return loadProvider(packageName);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// src/lib/provider-installer/provider-installer.types.ts
|
|
345
|
+
init_esm_shims();
|
|
346
|
+
|
|
347
|
+
// src/lib/provider-registry/index.ts
|
|
348
|
+
init_esm_shims();
|
|
349
|
+
|
|
350
|
+
// src/lib/provider-registry/provider-registry.ts
|
|
351
|
+
init_esm_shims();
|
|
352
|
+
var AVAILABLE_PROVIDERS = [
|
|
353
|
+
{
|
|
354
|
+
id: "fs",
|
|
355
|
+
name: "\u{1F4C1} File System",
|
|
356
|
+
description: "Store tasks as Markdown files in a local TASKS/ directory",
|
|
357
|
+
packageName: "@opentask/taskin-fs-provider",
|
|
358
|
+
configSchema: {
|
|
359
|
+
required: ["tasksDir"],
|
|
360
|
+
properties: {
|
|
361
|
+
tasksDir: {
|
|
362
|
+
type: "string",
|
|
363
|
+
description: "Directory to store task files"
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
},
|
|
367
|
+
status: "stable"
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
id: "redmine",
|
|
371
|
+
name: "\u{1F534} Redmine",
|
|
372
|
+
description: "Sync tasks with Redmine issues via REST API",
|
|
373
|
+
packageName: "@opentask/taskin-redmine-provider",
|
|
374
|
+
configSchema: {
|
|
375
|
+
required: ["apiUrl", "apiKey", "projectId"],
|
|
376
|
+
properties: {
|
|
377
|
+
apiUrl: {
|
|
378
|
+
type: "string",
|
|
379
|
+
description: "Redmine server URL (e.g., https://redmine.example.com)"
|
|
380
|
+
},
|
|
381
|
+
apiKey: {
|
|
382
|
+
type: "string",
|
|
383
|
+
description: "Your Redmine API key",
|
|
384
|
+
secret: true
|
|
385
|
+
},
|
|
386
|
+
projectId: {
|
|
387
|
+
type: "string",
|
|
388
|
+
description: "Project identifier or ID"
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
},
|
|
392
|
+
status: "coming-soon"
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
id: "jira",
|
|
396
|
+
name: "\u{1F535} Jira",
|
|
397
|
+
description: "Sync tasks with Jira issues via REST API",
|
|
398
|
+
packageName: "@opentask/taskin-jira-provider",
|
|
399
|
+
configSchema: {
|
|
400
|
+
required: ["apiUrl", "email", "apiToken", "projectKey"],
|
|
401
|
+
properties: {
|
|
402
|
+
apiUrl: {
|
|
403
|
+
type: "string",
|
|
404
|
+
description: "Jira server URL (e.g., https://company.atlassian.net)"
|
|
405
|
+
},
|
|
406
|
+
email: {
|
|
407
|
+
type: "string",
|
|
408
|
+
description: "Your Atlassian account email"
|
|
409
|
+
},
|
|
410
|
+
apiToken: {
|
|
411
|
+
type: "string",
|
|
412
|
+
description: "Your Jira API token",
|
|
413
|
+
secret: true
|
|
414
|
+
},
|
|
415
|
+
projectKey: {
|
|
416
|
+
type: "string",
|
|
417
|
+
description: "Project key (e.g., PROJ)"
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
status: "coming-soon"
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
id: "github",
|
|
425
|
+
name: "\u{1F419} GitHub Issues",
|
|
426
|
+
description: "Sync tasks with GitHub Issues",
|
|
427
|
+
packageName: "@opentask/taskin-github-provider",
|
|
428
|
+
configSchema: {
|
|
429
|
+
required: ["owner", "repo", "token"],
|
|
430
|
+
properties: {
|
|
431
|
+
owner: {
|
|
432
|
+
type: "string",
|
|
433
|
+
description: "Repository owner (username or organization)"
|
|
434
|
+
},
|
|
435
|
+
repo: {
|
|
436
|
+
type: "string",
|
|
437
|
+
description: "Repository name"
|
|
438
|
+
},
|
|
439
|
+
token: {
|
|
440
|
+
type: "string",
|
|
441
|
+
description: "GitHub Personal Access Token",
|
|
442
|
+
secret: true
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
},
|
|
446
|
+
status: "coming-soon"
|
|
447
|
+
}
|
|
448
|
+
];
|
|
449
|
+
function getProviderById(id) {
|
|
450
|
+
return AVAILABLE_PROVIDERS.find((p) => p.id === id);
|
|
451
|
+
}
|
|
452
|
+
function getAllProviders() {
|
|
453
|
+
return AVAILABLE_PROVIDERS;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// src/lib/provider-registry/provider-registry.types.ts
|
|
457
|
+
init_esm_shims();
|
|
458
|
+
|
|
459
|
+
// src/commands/init.ts
|
|
460
|
+
var initCommand = defineCommand({
|
|
461
|
+
name: "init",
|
|
462
|
+
alias: "setup",
|
|
463
|
+
description: "\u{1F3AF} Initialize Taskin in your project",
|
|
464
|
+
options: [
|
|
465
|
+
{
|
|
466
|
+
flags: "-f, --force",
|
|
467
|
+
description: "Force initialization (overwrite existing configuration)"
|
|
468
|
+
}
|
|
469
|
+
],
|
|
470
|
+
handler: async (options) => {
|
|
471
|
+
await initializeTaskin(options);
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
async function initializeTaskin(options) {
|
|
475
|
+
printHeader("Initializing Taskin", "\u{1F3AF}");
|
|
476
|
+
const cwd = process.cwd();
|
|
477
|
+
const configFile = join(cwd, ".taskin.json");
|
|
478
|
+
if (existsSync2(configFile) && !options.force) {
|
|
479
|
+
error("Taskin is already initialized in this project");
|
|
480
|
+
info("Use --force to reinitialize");
|
|
481
|
+
process.exit(1);
|
|
482
|
+
}
|
|
483
|
+
const allProviders = getAllProviders();
|
|
484
|
+
const choices = allProviders.map((p) => {
|
|
485
|
+
const installed = isProviderInstalled(p.packageName);
|
|
486
|
+
const status = p.status === "coming-soon" ? " (coming soon)" : "";
|
|
487
|
+
const installedMark = installed ? " \u2713" : "";
|
|
488
|
+
return {
|
|
489
|
+
name: `${p.name}${installedMark}${status} - ${p.description}`,
|
|
490
|
+
value: p.id,
|
|
491
|
+
disabled: p.status === "coming-soon" ? "Not yet implemented" : false
|
|
492
|
+
};
|
|
21
493
|
});
|
|
22
|
-
|
|
494
|
+
const { providerId } = await inquirer.prompt([
|
|
495
|
+
{
|
|
496
|
+
type: "list",
|
|
497
|
+
name: "providerId",
|
|
498
|
+
message: "Select a task provider:",
|
|
499
|
+
choices,
|
|
500
|
+
default: "fs"
|
|
501
|
+
}
|
|
502
|
+
]);
|
|
503
|
+
const selectedProvider = getProviderById(providerId);
|
|
504
|
+
if (!selectedProvider) {
|
|
505
|
+
error(`Provider "${providerId}" not found`);
|
|
506
|
+
process.exit(1);
|
|
507
|
+
}
|
|
508
|
+
console.log();
|
|
509
|
+
info(`Setting up task provider: ${colors.highlight(selectedProvider.name)}`);
|
|
510
|
+
console.log();
|
|
511
|
+
await ensureProviderInstalled(selectedProvider);
|
|
512
|
+
const providerConfig = await setupProviderConfig(selectedProvider, cwd);
|
|
513
|
+
const config = {
|
|
514
|
+
version: "0.1.0",
|
|
515
|
+
provider: {
|
|
516
|
+
type: selectedProvider.id,
|
|
517
|
+
config: providerConfig
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
info("Creating configuration file...");
|
|
521
|
+
writeFileSync(configFile, JSON.stringify(config, null, 2), "utf-8");
|
|
522
|
+
success(`\u2713 Created ${colors.highlight(".taskin.json")}`);
|
|
523
|
+
const gitignorePath = join(cwd, ".gitignore");
|
|
524
|
+
if (existsSync2(gitignorePath)) {
|
|
525
|
+
const gitignoreContent = __require("fs").readFileSync(gitignorePath, "utf-8");
|
|
526
|
+
if (!gitignoreContent.includes(".taskin.json")) {
|
|
527
|
+
info("Adding .taskin.json to .gitignore...");
|
|
528
|
+
writeFileSync(
|
|
529
|
+
gitignorePath,
|
|
530
|
+
`${gitignoreContent}
|
|
531
|
+
# Taskin configuration
|
|
532
|
+
.taskin.json
|
|
533
|
+
`,
|
|
534
|
+
"utf-8"
|
|
535
|
+
);
|
|
536
|
+
success("\u2713 Updated .gitignore");
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
console.log();
|
|
540
|
+
success("\u{1F389} Taskin initialized successfully!");
|
|
541
|
+
console.log();
|
|
542
|
+
info("Next steps:");
|
|
543
|
+
console.log(colors.secondary(" 1. Run: taskin list"));
|
|
544
|
+
console.log(
|
|
545
|
+
colors.secondary(
|
|
546
|
+
selectedProvider.id === "fs" ? " 2. Edit or create tasks in TASKS/" : ` 2. Tasks will be synced with ${selectedProvider.name}`
|
|
547
|
+
)
|
|
548
|
+
);
|
|
549
|
+
console.log(colors.secondary(" 3. Start working: taskin start <task-id>"));
|
|
550
|
+
console.log();
|
|
551
|
+
info("For more information, run: taskin --help");
|
|
552
|
+
console.log();
|
|
553
|
+
}
|
|
554
|
+
async function setupProviderConfig(provider, cwd) {
|
|
555
|
+
if (provider.id === "fs") {
|
|
556
|
+
return setupFileSystemProvider(cwd);
|
|
557
|
+
}
|
|
558
|
+
info(`Configuring ${provider.name}...`);
|
|
559
|
+
console.log();
|
|
560
|
+
const questions = Object.entries(provider.configSchema.properties).map(
|
|
561
|
+
([key, schema]) => ({
|
|
562
|
+
type: schema.secret ? "password" : "input",
|
|
563
|
+
name: key,
|
|
564
|
+
message: `${schema.description}:`,
|
|
565
|
+
validate: (input) => {
|
|
566
|
+
if (provider.configSchema.required.includes(key) && input.length === 0) {
|
|
567
|
+
return `${schema.description} is required`;
|
|
568
|
+
}
|
|
569
|
+
return true;
|
|
570
|
+
}
|
|
571
|
+
})
|
|
572
|
+
);
|
|
573
|
+
const answers = await inquirer.prompt(questions);
|
|
574
|
+
console.log();
|
|
575
|
+
success(`\u2713 ${provider.name} configuration saved`);
|
|
576
|
+
return answers;
|
|
577
|
+
}
|
|
578
|
+
async function setupFileSystemProvider(cwd) {
|
|
579
|
+
const tasksDir = join(cwd, "TASKS");
|
|
580
|
+
if (!existsSync2(tasksDir)) {
|
|
581
|
+
info(`Creating TASKS directory...`);
|
|
582
|
+
mkdirSync(tasksDir, { recursive: true });
|
|
583
|
+
success(`\u2713 Created ${colors.highlight("TASKS/")} directory`);
|
|
584
|
+
} else {
|
|
585
|
+
info(`${colors.highlight("TASKS/")} directory already exists`);
|
|
586
|
+
}
|
|
587
|
+
const sampleTaskFile = join(tasksDir, "task-001-setup-project.md");
|
|
588
|
+
if (!existsSync2(sampleTaskFile)) {
|
|
589
|
+
info("Creating sample task...");
|
|
590
|
+
const sampleTask = `# Task 001 \u2014 Setup Project
|
|
591
|
+
|
|
592
|
+
Status: pending
|
|
593
|
+
Type: chore
|
|
594
|
+
Assignee: developer
|
|
595
|
+
|
|
596
|
+
## Description
|
|
597
|
+
|
|
598
|
+
This is a sample task created during Taskin initialization.
|
|
599
|
+
|
|
600
|
+
## Tasks
|
|
601
|
+
|
|
602
|
+
- [ ] Review Taskin documentation
|
|
603
|
+
- [ ] Create your first task
|
|
604
|
+
- [ ] Start working on a task
|
|
605
|
+
|
|
606
|
+
## Notes
|
|
607
|
+
|
|
608
|
+
You can edit or delete this file. Use \`taskin list\` to see all tasks.
|
|
609
|
+
`;
|
|
610
|
+
writeFileSync(sampleTaskFile, sampleTask, "utf-8");
|
|
611
|
+
success(
|
|
612
|
+
`\u2713 Created sample task ${colors.highlight("task-001-setup-project.md")}`
|
|
613
|
+
);
|
|
614
|
+
}
|
|
615
|
+
return {
|
|
616
|
+
tasksDir: "TASKS"
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// src/commands/lint.ts
|
|
621
|
+
init_esm_shims();
|
|
622
|
+
import { join as join3 } from "path";
|
|
623
|
+
|
|
624
|
+
// src/lib/file-system-task-linter/index.ts
|
|
625
|
+
init_esm_shims();
|
|
626
|
+
|
|
627
|
+
// src/lib/file-system-task-linter/file-system-task-linter.ts
|
|
628
|
+
init_esm_shims();
|
|
629
|
+
import chalk2 from "chalk";
|
|
630
|
+
import { readdir, readFile } from "fs/promises";
|
|
631
|
+
import { join as join2 } from "path";
|
|
632
|
+
var VALID_STATUSES = ["pending", "in-progress", "done", "blocked"];
|
|
633
|
+
var VALID_TYPES = ["feat", "fix", "chore", "docs", "refactor", "test"];
|
|
634
|
+
var FileSystemTaskLinter = class {
|
|
635
|
+
errors = [];
|
|
636
|
+
addError(file, message, severity = "error", line) {
|
|
637
|
+
this.errors.push({ file, message, severity, line });
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Validate task file name format (FileSystem-specific)
|
|
641
|
+
*/
|
|
642
|
+
validateFileName(fileName) {
|
|
643
|
+
const pattern = /^task-\d{2,3}-[a-z0-9-]+\.md$/;
|
|
644
|
+
if (!pattern.test(fileName)) {
|
|
645
|
+
return {
|
|
646
|
+
file: fileName,
|
|
647
|
+
message: "Invalid filename. Expected: task-NNN-kebab-case-title.md",
|
|
648
|
+
severity: "error"
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
return null;
|
|
652
|
+
}
|
|
653
|
+
extractMetadata(content) {
|
|
654
|
+
const headerSection = content.split(/^##/m)[0];
|
|
655
|
+
const statusMatch = headerSection.match(/^Status:\s*(.+)$/im);
|
|
656
|
+
const typeMatch = headerSection.match(/^Type:\s*(.+)$/im);
|
|
657
|
+
const assigneeMatch = headerSection.match(/^Assignee:\s*(.+)$/im);
|
|
658
|
+
return {
|
|
659
|
+
status: statusMatch?.[1]?.trim().toLowerCase(),
|
|
660
|
+
type: typeMatch?.[1]?.trim().toLowerCase(),
|
|
661
|
+
assignee: assigneeMatch?.[1]?.trim()
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Validate task metadata (FileSystem-specific)
|
|
666
|
+
*/
|
|
667
|
+
validateMetadata(metadata, filePath) {
|
|
668
|
+
const errors = [];
|
|
669
|
+
if (!metadata.status) {
|
|
670
|
+
errors.push({
|
|
671
|
+
file: filePath,
|
|
672
|
+
message: "Missing required metadata: Status",
|
|
673
|
+
severity: "error"
|
|
674
|
+
});
|
|
675
|
+
} else if (!VALID_STATUSES.includes(metadata.status)) {
|
|
676
|
+
errors.push({
|
|
677
|
+
file: filePath,
|
|
678
|
+
message: `Invalid status "${metadata.status}". Valid: ${VALID_STATUSES.join(", ")}`,
|
|
679
|
+
severity: "error"
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
if (!metadata.type) {
|
|
683
|
+
errors.push({
|
|
684
|
+
file: filePath,
|
|
685
|
+
message: "Missing required metadata: Type",
|
|
686
|
+
severity: "error"
|
|
687
|
+
});
|
|
688
|
+
} else if (!VALID_TYPES.includes(metadata.type)) {
|
|
689
|
+
errors.push({
|
|
690
|
+
file: filePath,
|
|
691
|
+
message: `Invalid type "${metadata.type}". Valid: ${VALID_TYPES.join(", ")}`,
|
|
692
|
+
severity: "error"
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
if (!metadata.assignee) {
|
|
696
|
+
errors.push({
|
|
697
|
+
file: filePath,
|
|
698
|
+
message: "Missing recommended metadata: Assignee",
|
|
699
|
+
severity: "warning"
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
return errors;
|
|
703
|
+
}
|
|
704
|
+
validateContent(filename, content) {
|
|
705
|
+
const lines = content.split("\n");
|
|
706
|
+
if (!lines[0]?.trim().startsWith("# ")) {
|
|
707
|
+
this.addError(
|
|
708
|
+
filename,
|
|
709
|
+
"Task file must start with a level 1 heading (# Task NNN \u2014 Title)",
|
|
710
|
+
"error",
|
|
711
|
+
1
|
|
712
|
+
);
|
|
713
|
+
}
|
|
714
|
+
const h1Pattern = /^#\s+(?:🧩\s+)?Task\s+\d{2,3}\s+[—-]\s+.+$/i;
|
|
715
|
+
if (lines[0] && !h1Pattern.test(lines[0])) {
|
|
716
|
+
this.addError(
|
|
717
|
+
filename,
|
|
718
|
+
'H1 heading must follow format: "# Task NNN \u2014 Title"',
|
|
719
|
+
"error",
|
|
720
|
+
1
|
|
721
|
+
);
|
|
722
|
+
}
|
|
723
|
+
const metadata = this.extractMetadata(content);
|
|
724
|
+
const metadataErrors = this.validateMetadata(metadata, filename);
|
|
725
|
+
metadataErrors.forEach((error2) => {
|
|
726
|
+
this.addError(error2.file, error2.message, error2.severity, error2.line);
|
|
727
|
+
});
|
|
728
|
+
const h2Sections = content.match(/^##\s+.+$/gm);
|
|
729
|
+
if (!h2Sections || h2Sections.length === 0) {
|
|
730
|
+
this.addError(
|
|
731
|
+
filename,
|
|
732
|
+
"Task should have at least one section (## heading)",
|
|
733
|
+
"warning"
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
const firstH2Index = content.indexOf("\n##");
|
|
737
|
+
if (firstH2Index > -1) {
|
|
738
|
+
const afterH2 = content.substring(firstH2Index);
|
|
739
|
+
if (afterH2.match(/^Status:/im) || afterH2.match(/^Type:/im) || afterH2.match(/^Assignee:/im)) {
|
|
740
|
+
this.addError(
|
|
741
|
+
filename,
|
|
742
|
+
"Metadata must be placed BEFORE the first ## section",
|
|
743
|
+
"error"
|
|
744
|
+
);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* Lint a single task file (FileSystem-specific)
|
|
750
|
+
*/
|
|
751
|
+
async lintFile(filePath) {
|
|
752
|
+
const errors = [];
|
|
753
|
+
const fileName = filePath.split("/").pop() || filePath;
|
|
754
|
+
const fileNameError = this.validateFileName(fileName);
|
|
755
|
+
if (fileNameError) {
|
|
756
|
+
errors.push(fileNameError);
|
|
757
|
+
}
|
|
758
|
+
try {
|
|
759
|
+
const content = await readFile(filePath, "utf-8");
|
|
760
|
+
this.errors = [];
|
|
761
|
+
this.validateContent(fileName, content);
|
|
762
|
+
errors.push(...this.errors);
|
|
763
|
+
} catch (error2) {
|
|
764
|
+
errors.push({
|
|
765
|
+
file: fileName,
|
|
766
|
+
message: `Failed to read file: ${error2}`,
|
|
767
|
+
severity: "error"
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
return errors;
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
773
|
+
* Lint all task files in the specified directory (FileSystem-specific)
|
|
774
|
+
*/
|
|
775
|
+
async lintDirectory(tasksDir) {
|
|
776
|
+
this.errors = [];
|
|
777
|
+
try {
|
|
778
|
+
const files = await readdir(tasksDir);
|
|
779
|
+
const taskFiles = files.filter((f) => f.endsWith(".md"));
|
|
780
|
+
for (const file of taskFiles) {
|
|
781
|
+
const fileNameError = this.validateFileName(file);
|
|
782
|
+
if (fileNameError) {
|
|
783
|
+
this.addError(file, fileNameError.message, fileNameError.severity);
|
|
784
|
+
}
|
|
785
|
+
const content = await readFile(join2(tasksDir, file), "utf-8");
|
|
786
|
+
this.validateContent(file, content);
|
|
787
|
+
}
|
|
788
|
+
const errors = this.errors.filter((e) => e.severity === "error");
|
|
789
|
+
const warnings = this.errors.filter((e) => e.severity === "warning");
|
|
790
|
+
return {
|
|
791
|
+
errors,
|
|
792
|
+
warnings,
|
|
793
|
+
filesChecked: taskFiles.length,
|
|
794
|
+
valid: errors.length === 0
|
|
795
|
+
};
|
|
796
|
+
} catch (error2) {
|
|
797
|
+
throw new Error(`Failed to lint tasks: ${error2}`);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
static printResults(result) {
|
|
801
|
+
if (result.errors.length === 0 && result.warnings.length === 0) {
|
|
802
|
+
console.log(
|
|
803
|
+
chalk2.green(`\u2705 All ${result.filesChecked} task files are valid!
|
|
804
|
+
`)
|
|
805
|
+
);
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
console.log(
|
|
809
|
+
chalk2.bold(
|
|
810
|
+
`
|
|
811
|
+
\u{1F4CA} Validation Results (${result.filesChecked} files checked):
|
|
812
|
+
`
|
|
813
|
+
)
|
|
814
|
+
);
|
|
815
|
+
const errorsByFile = /* @__PURE__ */ new Map();
|
|
816
|
+
[...result.errors, ...result.warnings].forEach((error2) => {
|
|
817
|
+
if (!errorsByFile.has(error2.file)) {
|
|
818
|
+
errorsByFile.set(error2.file, []);
|
|
819
|
+
}
|
|
820
|
+
errorsByFile.get(error2.file).push(error2);
|
|
821
|
+
});
|
|
822
|
+
for (const [file, fileErrors] of errorsByFile) {
|
|
823
|
+
console.log(chalk2.cyan(`
|
|
824
|
+
\u{1F4C4} ${file}`));
|
|
825
|
+
for (const error2 of fileErrors) {
|
|
826
|
+
const icon = error2.severity === "error" ? chalk2.red("\u274C") : chalk2.yellow("\u26A0\uFE0F");
|
|
827
|
+
const location = error2.line ? `:${error2.line}` : "";
|
|
828
|
+
console.log(` ${icon} ${error2.message}${location}`);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
console.log("\n" + "\u2500".repeat(60));
|
|
832
|
+
console.log(
|
|
833
|
+
chalk2.bold(
|
|
834
|
+
`
|
|
835
|
+
\u{1F4CA} Summary: ${chalk2.red(result.errors.length + " error(s)")}, ${chalk2.yellow(result.warnings.length + " warning(s)")}
|
|
836
|
+
`
|
|
837
|
+
)
|
|
838
|
+
);
|
|
839
|
+
}
|
|
840
|
+
};
|
|
841
|
+
|
|
842
|
+
// src/lib/file-system-task-linter/file-system-task-linter.mock.ts
|
|
843
|
+
init_esm_shims();
|
|
844
|
+
function createMockFileValidationError(overrides) {
|
|
845
|
+
return {
|
|
846
|
+
file: "task-001.md",
|
|
847
|
+
message: "Mock validation error",
|
|
848
|
+
severity: "error",
|
|
849
|
+
...overrides
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
var MOCK_INVALID_LINT_RESULT = {
|
|
853
|
+
errors: [
|
|
854
|
+
createMockFileValidationError({
|
|
855
|
+
file: "task-001.md",
|
|
856
|
+
message: 'Invalid status: "invalid"'
|
|
857
|
+
}),
|
|
858
|
+
createMockFileValidationError({
|
|
859
|
+
file: "task-002.md",
|
|
860
|
+
message: 'Invalid type: "unknown"'
|
|
861
|
+
})
|
|
862
|
+
],
|
|
863
|
+
filesChecked: 2,
|
|
864
|
+
valid: false,
|
|
865
|
+
warnings: []
|
|
866
|
+
};
|
|
867
|
+
var MOCK_WARNINGS_LINT_RESULT = {
|
|
868
|
+
errors: [],
|
|
869
|
+
filesChecked: 3,
|
|
870
|
+
valid: true,
|
|
871
|
+
warnings: [
|
|
872
|
+
createMockFileValidationError({
|
|
873
|
+
file: "task-003.md",
|
|
874
|
+
message: "Consider adding assignee",
|
|
875
|
+
severity: "warning"
|
|
876
|
+
})
|
|
877
|
+
]
|
|
878
|
+
};
|
|
879
|
+
var MOCK_VALIDATION_ERRORS = [
|
|
880
|
+
createMockFileValidationError({
|
|
881
|
+
file: "task-001.md",
|
|
882
|
+
message: "Invalid status: must be one of [pending, in-progress, done, blocked]",
|
|
883
|
+
line: 5
|
|
884
|
+
}),
|
|
885
|
+
createMockFileValidationError({
|
|
886
|
+
file: "task-001.md",
|
|
887
|
+
message: "Invalid type: must be one of [feat, fix, chore, docs, refactor, test]",
|
|
888
|
+
line: 6
|
|
889
|
+
})
|
|
890
|
+
];
|
|
891
|
+
|
|
892
|
+
// src/lib/file-system-task-linter/file-system-task-linter.types.ts
|
|
893
|
+
init_esm_shims();
|
|
894
|
+
|
|
895
|
+
// src/commands/lint.ts
|
|
896
|
+
var lintCommand = defineCommand({
|
|
897
|
+
name: "lint",
|
|
898
|
+
description: "\u{1F50D} Validate task markdown files",
|
|
899
|
+
options: [
|
|
900
|
+
{
|
|
901
|
+
flags: "-p, --path <directory>",
|
|
902
|
+
description: "Path to TASKS directory",
|
|
903
|
+
defaultValue: "TASKS"
|
|
904
|
+
}
|
|
905
|
+
],
|
|
906
|
+
handler: async (options) => {
|
|
907
|
+
await executeLint(options);
|
|
908
|
+
}
|
|
909
|
+
});
|
|
910
|
+
async function executeLint(options) {
|
|
911
|
+
const tasksDir = options.path || join3(process.cwd(), "TASKS");
|
|
912
|
+
console.log(`\u{1F4CB} Linting task files in: ${tasksDir}
|
|
913
|
+
`);
|
|
914
|
+
const linter = new FileSystemTaskLinter();
|
|
915
|
+
const result = await linter.lintDirectory(tasksDir);
|
|
916
|
+
FileSystemTaskLinter.printResults(result);
|
|
917
|
+
if (!result.valid) {
|
|
918
|
+
process.exit(1);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
// src/commands/list.ts
|
|
923
|
+
init_esm_shims();
|
|
924
|
+
import path4 from "path";
|
|
925
|
+
var listCommand = defineCommand({
|
|
926
|
+
name: "list [filter]",
|
|
927
|
+
description: "\u{1F4CA} List all tasks in the project",
|
|
928
|
+
alias: "ls",
|
|
929
|
+
options: [
|
|
930
|
+
{
|
|
931
|
+
flags: "-s, --status <status>",
|
|
932
|
+
description: "Filter by status (pending, in-progress, done, blocked)"
|
|
933
|
+
},
|
|
934
|
+
{
|
|
935
|
+
flags: "-t, --type <type>",
|
|
936
|
+
description: "Filter by type (feat, fix, refactor, docs, test, chore)"
|
|
937
|
+
},
|
|
938
|
+
{
|
|
939
|
+
flags: "-u, --user <user>",
|
|
940
|
+
description: "Filter by user"
|
|
941
|
+
}
|
|
942
|
+
],
|
|
943
|
+
handler: async (filter, options) => {
|
|
944
|
+
await listTasks(filter, options);
|
|
945
|
+
}
|
|
946
|
+
});
|
|
947
|
+
async function listTasks(filter, options) {
|
|
948
|
+
printHeader("Task List", "\u{1F4CA}");
|
|
949
|
+
const tasksDir = path4.join(process.cwd(), "TASKS");
|
|
950
|
+
const taskProvider = new FileSystemTaskProvider(tasksDir);
|
|
951
|
+
const tasks = await taskProvider.getAllTasks();
|
|
952
|
+
if (tasks.length === 0) {
|
|
953
|
+
console.log(colors.warning("No tasks found in TASKS/ directory"));
|
|
954
|
+
return;
|
|
955
|
+
}
|
|
956
|
+
let filteredTasks = tasks;
|
|
957
|
+
if (options.status) {
|
|
958
|
+
filteredTasks = filteredTasks.filter((t) => t.status === options.status);
|
|
959
|
+
}
|
|
960
|
+
if (options.type) {
|
|
961
|
+
filteredTasks = filteredTasks.filter((t) => t.type === options.type);
|
|
962
|
+
}
|
|
963
|
+
if (options.assignee) {
|
|
964
|
+
filteredTasks = filteredTasks.filter(
|
|
965
|
+
(t) => t.userId?.toLowerCase().includes(options.assignee.toLowerCase())
|
|
966
|
+
);
|
|
967
|
+
}
|
|
968
|
+
if (filter) {
|
|
969
|
+
const lowerFilter = filter.toLowerCase();
|
|
970
|
+
filteredTasks = filteredTasks.filter(
|
|
971
|
+
(t) => t.id.includes(lowerFilter) || t.title.toLowerCase().includes(lowerFilter) || t.status.toLowerCase().includes(lowerFilter) || t.userId?.toLowerCase().includes(lowerFilter)
|
|
972
|
+
);
|
|
973
|
+
}
|
|
974
|
+
if (filteredTasks.length === 0) {
|
|
975
|
+
console.log(colors.warning("No tasks match the filters"));
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
console.log(
|
|
979
|
+
colors.highlight(
|
|
980
|
+
`${"ID".padEnd(15)} ${"Status".padEnd(15)} ${"Type".padEnd(12)} ${"User".padEnd(15)} ${"Title"}`
|
|
981
|
+
)
|
|
982
|
+
);
|
|
983
|
+
console.log(colors.secondary("\u2500".repeat(100)));
|
|
984
|
+
filteredTasks.forEach((task) => {
|
|
985
|
+
const statusColor = getStatusColor(task.status);
|
|
986
|
+
const typeColor = getTypeColor(task.type);
|
|
987
|
+
console.log(
|
|
988
|
+
`${colors.info(task.id.padEnd(15))} ${statusColor(task.status.padEnd(15))} ${typeColor(task.type.padEnd(12))} ${colors.secondary((task.userId || "unknown").padEnd(15))} ${colors.normal(task.title)}`
|
|
989
|
+
);
|
|
990
|
+
});
|
|
991
|
+
console.log();
|
|
992
|
+
console.log(colors.secondary("\u2500".repeat(100)));
|
|
993
|
+
const statusCounts = {
|
|
994
|
+
pending: filteredTasks.filter((t) => t.status === "pending").length,
|
|
995
|
+
"in-progress": filteredTasks.filter((t) => t.status === "in-progress").length,
|
|
996
|
+
done: filteredTasks.filter((t) => t.status === "done").length,
|
|
997
|
+
blocked: filteredTasks.filter((t) => t.status === "blocked").length
|
|
998
|
+
};
|
|
999
|
+
console.log(
|
|
1000
|
+
colors.info(
|
|
1001
|
+
`\u{1F4CA} Total: ${filteredTasks.length} tasks | \u23F3 Pending: ${statusCounts.pending} | \u{1F680} In Progress: ${statusCounts["in-progress"]} | \u2705 Done: ${statusCounts.done} | \u{1F6AB} Blocked: ${statusCounts.blocked}`
|
|
1002
|
+
)
|
|
1003
|
+
);
|
|
1004
|
+
console.log();
|
|
1005
|
+
}
|
|
1006
|
+
function getStatusColor(status) {
|
|
1007
|
+
switch (status) {
|
|
1008
|
+
case "pending":
|
|
1009
|
+
return colors.secondary;
|
|
1010
|
+
case "in-progress":
|
|
1011
|
+
return colors.info;
|
|
1012
|
+
case "done":
|
|
1013
|
+
return colors.success;
|
|
1014
|
+
case "blocked":
|
|
1015
|
+
return colors.error;
|
|
1016
|
+
default:
|
|
1017
|
+
return colors.normal;
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
function getTypeColor(type) {
|
|
1021
|
+
switch (type) {
|
|
1022
|
+
case "feat":
|
|
1023
|
+
return colors.success;
|
|
1024
|
+
case "fix":
|
|
1025
|
+
return colors.error;
|
|
1026
|
+
case "refactor":
|
|
1027
|
+
return colors.warning;
|
|
1028
|
+
case "docs":
|
|
1029
|
+
return colors.info;
|
|
1030
|
+
case "test":
|
|
1031
|
+
return colors.secondary;
|
|
1032
|
+
case "chore":
|
|
1033
|
+
return colors.normal;
|
|
1034
|
+
default:
|
|
1035
|
+
return colors.normal;
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
// src/commands/pause.ts
|
|
1040
|
+
init_esm_shims();
|
|
1041
|
+
import path5 from "path";
|
|
1042
|
+
var pauseCommand = defineCommand({
|
|
1043
|
+
name: "pause <task-id>",
|
|
1044
|
+
description: "\u23F8\uFE0F Pause work on a task",
|
|
1045
|
+
alias: "stop",
|
|
1046
|
+
options: [
|
|
1047
|
+
{
|
|
1048
|
+
flags: "-m, --message <message>",
|
|
1049
|
+
description: "Custom commit message"
|
|
1050
|
+
},
|
|
1051
|
+
{
|
|
1052
|
+
flags: "-s, --skip-commit",
|
|
1053
|
+
description: "Skip commit (just show what would be done)"
|
|
1054
|
+
}
|
|
1055
|
+
],
|
|
1056
|
+
handler: async (taskId, options) => {
|
|
1057
|
+
await pauseTask(taskId, options);
|
|
1058
|
+
}
|
|
1059
|
+
});
|
|
1060
|
+
async function pauseTask(taskId, options) {
|
|
1061
|
+
printHeader(`Pausing Task ${taskId}`, "\u23F8\uFE0F");
|
|
1062
|
+
const normalizedId = taskId.replace(/^task-/, "").padStart(3, "0");
|
|
1063
|
+
const tasksDir = path5.join(process.cwd(), "TASKS");
|
|
1064
|
+
const taskProvider = new FileSystemTaskProvider(tasksDir);
|
|
1065
|
+
const task = await taskProvider.findTask(normalizedId);
|
|
1066
|
+
if (!task) {
|
|
1067
|
+
error(`Task ${normalizedId} not found in TASKS/ directory`);
|
|
1068
|
+
process.exit(1);
|
|
1069
|
+
}
|
|
1070
|
+
info(`Found task: ${task.title}`);
|
|
1071
|
+
info(`Current status: ${task.status}`);
|
|
1072
|
+
if (task.status !== "in-progress") {
|
|
1073
|
+
error('Task is not in progress. Use "taskin start" first.');
|
|
1074
|
+
process.exit(1);
|
|
1075
|
+
}
|
|
1076
|
+
const commitMessage = options.message || `WIP: task-${normalizedId} - ${task.title}`;
|
|
1077
|
+
if (options.skipCommit) {
|
|
1078
|
+
info("Would create commit with message:");
|
|
1079
|
+
console.log(colors.highlight(` "${commitMessage}"`));
|
|
1080
|
+
console.log();
|
|
1081
|
+
info("Use without --skip-commit to actually commit");
|
|
1082
|
+
} else {
|
|
1083
|
+
info("Creating commit...");
|
|
1084
|
+
console.log(colors.secondary(` Message: "${commitMessage}"`));
|
|
1085
|
+
console.log();
|
|
1086
|
+
success("Task paused successfully!");
|
|
1087
|
+
info("Commit created (not pushed)");
|
|
1088
|
+
console.log();
|
|
1089
|
+
info("Next steps:");
|
|
1090
|
+
console.log(colors.secondary(" 1. Switch to another task"));
|
|
1091
|
+
console.log(
|
|
1092
|
+
colors.secondary(" 2. Or continue later with the same branch")
|
|
1093
|
+
);
|
|
1094
|
+
}
|
|
1095
|
+
console.log();
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
// src/commands/start.ts
|
|
1099
|
+
init_esm_shims();
|
|
1100
|
+
import path6 from "path";
|
|
1101
|
+
var startCommand = defineCommand({
|
|
1102
|
+
name: "start <task-id>",
|
|
1103
|
+
description: "\u{1F680} Start working on a task",
|
|
1104
|
+
alias: "begin",
|
|
1105
|
+
options: [
|
|
1106
|
+
{
|
|
1107
|
+
flags: "-f, --force",
|
|
1108
|
+
description: "Force start even with uncommitted changes"
|
|
1109
|
+
},
|
|
1110
|
+
{
|
|
1111
|
+
flags: "-b, --base <branch>",
|
|
1112
|
+
description: "Base branch to create from (default: current)"
|
|
1113
|
+
}
|
|
1114
|
+
],
|
|
1115
|
+
handler: async (taskId, options) => {
|
|
1116
|
+
await startTask(taskId, options);
|
|
1117
|
+
}
|
|
1118
|
+
});
|
|
1119
|
+
async function startTask(taskId, _options) {
|
|
1120
|
+
printHeader(`Starting Task ${taskId}`, "\u{1F680}");
|
|
1121
|
+
const normalizedId = taskId.replace(/^task-/, "").padStart(3, "0");
|
|
1122
|
+
const tasksDir = path6.join(process.cwd(), "TASKS");
|
|
1123
|
+
const taskProvider = new FileSystemTaskProvider(tasksDir);
|
|
1124
|
+
const taskManager = new TaskManager(taskProvider);
|
|
1125
|
+
const task = await taskProvider.findTask(normalizedId);
|
|
1126
|
+
if (!task) {
|
|
1127
|
+
error(`Task ${normalizedId} not found in TASKS/ directory`);
|
|
1128
|
+
process.exit(1);
|
|
1129
|
+
}
|
|
1130
|
+
info(`Found task: ${task.title}`);
|
|
1131
|
+
info(`Current status: ${task.status}`);
|
|
1132
|
+
if (task.status === "in-progress") {
|
|
1133
|
+
error("Task is already in progress");
|
|
1134
|
+
process.exit(1);
|
|
1135
|
+
}
|
|
1136
|
+
if (task.status === "done") {
|
|
1137
|
+
error("Task is already done");
|
|
1138
|
+
process.exit(1);
|
|
1139
|
+
}
|
|
1140
|
+
info("Starting task...");
|
|
1141
|
+
const updatedTask = await taskManager.startTask(task.id);
|
|
1142
|
+
success(`Task ${updatedTask.id} started successfully!`);
|
|
1143
|
+
success(`Status changed to: ${updatedTask.status}`);
|
|
1144
|
+
console.log();
|
|
1145
|
+
info("Next steps:");
|
|
1146
|
+
console.log(
|
|
1147
|
+
colors.secondary(
|
|
1148
|
+
" 1. Create a branch: git checkout -b feat/task-" + normalizedId
|
|
1149
|
+
)
|
|
1150
|
+
);
|
|
1151
|
+
console.log(colors.secondary(" 2. Start coding! \u{1F4BB}"));
|
|
1152
|
+
console.log(colors.secondary(' 3. Use "taskin pause" to save progress'));
|
|
1153
|
+
console.log();
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
// src/lib/help.ts
|
|
1157
|
+
init_esm_shims();
|
|
1158
|
+
function showCustomHelp() {
|
|
1159
|
+
printHeader("Taskin - Task Management System", icons.rocket);
|
|
1160
|
+
console.log(colors.info("\u{1F4CB} AVAILABLE COMMANDS"));
|
|
1161
|
+
console.log(colors.highlight("\u2550".repeat(60)));
|
|
1162
|
+
console.log();
|
|
1163
|
+
const commands = [
|
|
1164
|
+
{
|
|
1165
|
+
name: colors.highlight("taskin init"),
|
|
1166
|
+
alias: colors.secondary("Alias: setup"),
|
|
1167
|
+
description: "Initialize Taskin in your project",
|
|
1168
|
+
examples: ["taskin init", "taskin setup"],
|
|
1169
|
+
icon: "\u{1F3AF}"
|
|
1170
|
+
},
|
|
1171
|
+
{
|
|
1172
|
+
name: colors.highlight("taskin list") + colors.normal(" [filter]"),
|
|
1173
|
+
alias: colors.secondary("Alias: ls"),
|
|
1174
|
+
description: "List all tasks in the project",
|
|
1175
|
+
examples: [
|
|
1176
|
+
"taskin list",
|
|
1177
|
+
"taskin list pending",
|
|
1178
|
+
"taskin list --status in-progress",
|
|
1179
|
+
"taskin list --type feat"
|
|
1180
|
+
],
|
|
1181
|
+
icon: "\u{1F4CA}"
|
|
1182
|
+
},
|
|
1183
|
+
{
|
|
1184
|
+
name: colors.highlight("taskin start") + colors.normal(" <task-id>"),
|
|
1185
|
+
alias: colors.secondary("Alias: begin"),
|
|
1186
|
+
description: "Start working on a task",
|
|
1187
|
+
examples: [
|
|
1188
|
+
"taskin start 001",
|
|
1189
|
+
"taskin start task-001",
|
|
1190
|
+
"taskin start 001 --force"
|
|
1191
|
+
],
|
|
1192
|
+
icon: "\u{1F680}"
|
|
1193
|
+
},
|
|
1194
|
+
{
|
|
1195
|
+
name: colors.highlight("taskin pause") + colors.normal(" <task-id>"),
|
|
1196
|
+
alias: colors.secondary("Alias: stop"),
|
|
1197
|
+
description: "Pause a task (commit without push)",
|
|
1198
|
+
examples: ["taskin pause 001", 'taskin pause 001 -m "saving progress"'],
|
|
1199
|
+
icon: "\u23F8\uFE0F"
|
|
1200
|
+
},
|
|
1201
|
+
{
|
|
1202
|
+
name: colors.highlight("taskin finish") + colors.normal(" <task-id>"),
|
|
1203
|
+
alias: colors.secondary("Alias: done"),
|
|
1204
|
+
description: "Finish a task",
|
|
1205
|
+
examples: ["taskin finish 001", "taskin done task-001"],
|
|
1206
|
+
icon: "\u2705"
|
|
1207
|
+
},
|
|
1208
|
+
{
|
|
1209
|
+
name: colors.highlight("taskin lint") + colors.normal(" [options]"),
|
|
1210
|
+
alias: colors.secondary("Options: -p, --path <directory>"),
|
|
1211
|
+
description: "Validate task markdown files",
|
|
1212
|
+
examples: [
|
|
1213
|
+
"taskin lint",
|
|
1214
|
+
"taskin lint --path ./TASKS",
|
|
1215
|
+
"taskin lint -p /path/to/tasks"
|
|
1216
|
+
],
|
|
1217
|
+
icon: "\u{1F50D}"
|
|
1218
|
+
}
|
|
1219
|
+
];
|
|
1220
|
+
commands.forEach((cmd, index) => {
|
|
1221
|
+
console.log(colors.warning(`${cmd.icon} ${cmd.name}`));
|
|
1222
|
+
console.log(colors.normal(` ${cmd.alias}`));
|
|
1223
|
+
console.log(colors.info(` ${cmd.description}`));
|
|
1224
|
+
console.log();
|
|
1225
|
+
console.log(colors.normal(` ${colors.info("\u{1F4DD} Examples:")}`));
|
|
1226
|
+
cmd.examples.forEach((example) => {
|
|
1227
|
+
console.log(colors.secondary(` ${example}`));
|
|
1228
|
+
});
|
|
1229
|
+
if (index < commands.length - 1) {
|
|
1230
|
+
console.log();
|
|
1231
|
+
console.log(colors.normal(" " + colors.secondary("\u2500".repeat(50))));
|
|
1232
|
+
console.log();
|
|
1233
|
+
}
|
|
1234
|
+
});
|
|
1235
|
+
console.log();
|
|
1236
|
+
console.log(colors.highlight("\u2550".repeat(60)));
|
|
1237
|
+
console.log();
|
|
1238
|
+
console.log(colors.info("\u{1F4A1} QUICK TIPS"));
|
|
1239
|
+
console.log(
|
|
1240
|
+
colors.normal(
|
|
1241
|
+
`${colors.warning("\u2022")} Use short IDs: ${colors.highlight("001")}, ${colors.highlight("task-001")}`
|
|
1242
|
+
)
|
|
1243
|
+
);
|
|
1244
|
+
console.log(
|
|
1245
|
+
colors.normal(
|
|
1246
|
+
`${colors.warning("\u2022")} All commands support ${colors.highlight("--help")} for more options`
|
|
1247
|
+
)
|
|
1248
|
+
);
|
|
1249
|
+
console.log(
|
|
1250
|
+
colors.normal(
|
|
1251
|
+
`${colors.warning("\u2022")} Use aliases for faster commands: ${colors.highlight("ls")}, ${colors.highlight("begin")}, ${colors.highlight("stop")}, ${colors.highlight("done")}`
|
|
1252
|
+
)
|
|
1253
|
+
);
|
|
1254
|
+
console.log();
|
|
1255
|
+
console.log(colors.info("\u{1F527} FOR MORE HELP"));
|
|
1256
|
+
console.log(
|
|
1257
|
+
colors.secondary("taskin ") + colors.highlight("<command>") + colors.secondary(" --help")
|
|
1258
|
+
);
|
|
1259
|
+
console.log();
|
|
1260
|
+
return "";
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
// src/main.ts
|
|
1264
|
+
init_esm_shims();
|
|
1265
|
+
import { join as join4 } from "path";
|
|
1266
|
+
|
|
1267
|
+
// src/taskin.ts
|
|
1268
|
+
init_esm_shims();
|
|
1269
|
+
var Taskin = class {
|
|
1270
|
+
constructor(taskProvider, taskManager, linter) {
|
|
1271
|
+
this.taskProvider = taskProvider;
|
|
1272
|
+
this.taskManager = taskManager;
|
|
1273
|
+
this.linter = linter;
|
|
1274
|
+
}
|
|
1275
|
+
async list(options) {
|
|
1276
|
+
const tasks = await this.taskProvider.getAllTasks();
|
|
1277
|
+
return tasks.map((taskFile) => {
|
|
1278
|
+
const task = {
|
|
1279
|
+
createdAt: taskFile.createdAt,
|
|
1280
|
+
id: taskFile.id,
|
|
1281
|
+
status: taskFile.status,
|
|
1282
|
+
title: taskFile.title,
|
|
1283
|
+
type: taskFile.type,
|
|
1284
|
+
assignee: taskFile.userId
|
|
1285
|
+
// Map userId to assignee
|
|
1286
|
+
};
|
|
1287
|
+
return task;
|
|
1288
|
+
}).filter((task) => {
|
|
1289
|
+
if (options?.status && task.status !== options.status) {
|
|
1290
|
+
return false;
|
|
1291
|
+
}
|
|
1292
|
+
if (options?.type && task.type !== options.type) {
|
|
1293
|
+
return false;
|
|
1294
|
+
}
|
|
1295
|
+
if (options?.assignee && task.assignee !== options.assignee) {
|
|
1296
|
+
return false;
|
|
1297
|
+
}
|
|
1298
|
+
return true;
|
|
1299
|
+
});
|
|
1300
|
+
}
|
|
1301
|
+
async start(taskId, options) {
|
|
1302
|
+
const task = await this.taskProvider.findTask(taskId);
|
|
1303
|
+
if (!task) {
|
|
1304
|
+
throw new Error(`Task not found: ${taskId}`);
|
|
1305
|
+
}
|
|
1306
|
+
await this.taskManager.startTask(taskId);
|
|
1307
|
+
if (options?.force) {
|
|
1308
|
+
}
|
|
1309
|
+
if (options?.base) {
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
async pause(taskId, options) {
|
|
1313
|
+
const task = await this.taskProvider.findTask(taskId);
|
|
1314
|
+
if (!task) {
|
|
1315
|
+
throw new Error(`Task not found: ${taskId}`);
|
|
1316
|
+
}
|
|
1317
|
+
if (!options?.skipCommit) {
|
|
1318
|
+
task.status = "pending";
|
|
1319
|
+
await this.taskProvider.updateTask(task);
|
|
1320
|
+
}
|
|
1321
|
+
if (options?.message) {
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
async finish(taskId, _options) {
|
|
1325
|
+
const task = await this.taskProvider.findTask(taskId);
|
|
1326
|
+
if (!task) {
|
|
1327
|
+
throw new Error(`Task not found: ${taskId}`);
|
|
1328
|
+
}
|
|
1329
|
+
await this.taskManager.finishTask(taskId);
|
|
1330
|
+
}
|
|
1331
|
+
async lint(options) {
|
|
1332
|
+
const lintPath = options?.path || "TASKS";
|
|
1333
|
+
const fileResult = await this.linter.lintDirectory(lintPath);
|
|
1334
|
+
return {
|
|
1335
|
+
errors: fileResult.errors.map((e) => ({
|
|
1336
|
+
taskId: e.file,
|
|
1337
|
+
message: e.message,
|
|
1338
|
+
severity: e.severity,
|
|
1339
|
+
line: e.line
|
|
1340
|
+
})),
|
|
1341
|
+
warnings: fileResult.warnings.map((e) => ({
|
|
1342
|
+
taskId: e.file,
|
|
1343
|
+
message: e.message,
|
|
1344
|
+
severity: e.severity,
|
|
1345
|
+
line: e.line
|
|
1346
|
+
})),
|
|
1347
|
+
tasksChecked: fileResult.filesChecked,
|
|
1348
|
+
valid: fileResult.valid
|
|
1349
|
+
};
|
|
1350
|
+
}
|
|
1351
|
+
};
|
|
1352
|
+
|
|
1353
|
+
// src/main.ts
|
|
1354
|
+
function createTaskin(tasksDir) {
|
|
1355
|
+
const resolvedTasksDir = tasksDir || join4(process.cwd(), "TASKS");
|
|
1356
|
+
const taskProvider = new FileSystemTaskProvider(resolvedTasksDir);
|
|
1357
|
+
const taskManager = new TaskManager(taskProvider);
|
|
1358
|
+
const linter = new FileSystemTaskLinter();
|
|
1359
|
+
return new Taskin(taskProvider, taskManager, linter);
|
|
1360
|
+
}
|
|
1361
|
+
function getTaskin() {
|
|
1362
|
+
return createTaskin();
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
// src/index.ts
|
|
1366
|
+
var program = new Command();
|
|
1367
|
+
program.name("taskin").description("\u{1F680} Task Management System").version("0.1.0").configureHelp({
|
|
1368
|
+
formatHelp: () => showCustomHelp()
|
|
1369
|
+
});
|
|
23
1370
|
initCommand(program);
|
|
24
1371
|
listCommand(program);
|
|
25
1372
|
startCommand(program);
|
|
26
1373
|
pauseCommand(program);
|
|
27
1374
|
finishCommand(program);
|
|
28
1375
|
lintCommand(program);
|
|
29
|
-
// Show custom help if no command provided
|
|
30
1376
|
if (process.argv.length <= 2) {
|
|
31
1377
|
showCustomHelp();
|
|
32
1378
|
process.exit(0);
|
|
33
1379
|
}
|
|
34
|
-
// Parse arguments
|
|
35
1380
|
program.parse();
|
|
36
|
-
export {
|
|
37
|
-
|
|
38
|
-
|
|
1381
|
+
export {
|
|
1382
|
+
Taskin,
|
|
1383
|
+
createTaskin,
|
|
1384
|
+
getTaskin
|
|
1385
|
+
};
|