agkan 1.0.0
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.ja.md +1127 -0
- package/README.md +1140 -0
- package/bin/agkan +2 -0
- package/dist/cli/commands/block/add.d.ts +6 -0
- package/dist/cli/commands/block/add.d.ts.map +1 -0
- package/dist/cli/commands/block/add.js +135 -0
- package/dist/cli/commands/block/add.js.map +1 -0
- package/dist/cli/commands/block/list.d.ts +6 -0
- package/dist/cli/commands/block/list.d.ts.map +1 -0
- package/dist/cli/commands/block/list.js +120 -0
- package/dist/cli/commands/block/list.js.map +1 -0
- package/dist/cli/commands/block/remove.d.ts +6 -0
- package/dist/cli/commands/block/remove.d.ts.map +1 -0
- package/dist/cli/commands/block/remove.js +117 -0
- package/dist/cli/commands/block/remove.js.map +1 -0
- package/dist/cli/commands/meta/delete.d.ts +6 -0
- package/dist/cli/commands/meta/delete.d.ts.map +1 -0
- package/dist/cli/commands/meta/delete.js +67 -0
- package/dist/cli/commands/meta/delete.js.map +1 -0
- package/dist/cli/commands/meta/get.d.ts +6 -0
- package/dist/cli/commands/meta/get.d.ts.map +1 -0
- package/dist/cli/commands/meta/get.js +71 -0
- package/dist/cli/commands/meta/get.js.map +1 -0
- package/dist/cli/commands/meta/list.d.ts +6 -0
- package/dist/cli/commands/meta/list.d.ts.map +1 -0
- package/dist/cli/commands/meta/list.js +69 -0
- package/dist/cli/commands/meta/list.js.map +1 -0
- package/dist/cli/commands/meta/set.d.ts +6 -0
- package/dist/cli/commands/meta/set.d.ts.map +1 -0
- package/dist/cli/commands/meta/set.js +85 -0
- package/dist/cli/commands/meta/set.js.map +1 -0
- package/dist/cli/commands/tag/add.d.ts +6 -0
- package/dist/cli/commands/tag/add.d.ts.map +1 -0
- package/dist/cli/commands/tag/add.js +99 -0
- package/dist/cli/commands/tag/add.js.map +1 -0
- package/dist/cli/commands/tag/attach.d.ts +6 -0
- package/dist/cli/commands/tag/attach.d.ts.map +1 -0
- package/dist/cli/commands/tag/attach.js +119 -0
- package/dist/cli/commands/tag/attach.js.map +1 -0
- package/dist/cli/commands/tag/delete.d.ts +6 -0
- package/dist/cli/commands/tag/delete.d.ts.map +1 -0
- package/dist/cli/commands/tag/delete.js +93 -0
- package/dist/cli/commands/tag/delete.js.map +1 -0
- package/dist/cli/commands/tag/detach.d.ts +6 -0
- package/dist/cli/commands/tag/detach.d.ts.map +1 -0
- package/dist/cli/commands/tag/detach.js +118 -0
- package/dist/cli/commands/tag/detach.js.map +1 -0
- package/dist/cli/commands/tag/list.d.ts +6 -0
- package/dist/cli/commands/tag/list.d.ts.map +1 -0
- package/dist/cli/commands/tag/list.js +80 -0
- package/dist/cli/commands/tag/list.js.map +1 -0
- package/dist/cli/commands/tag/show.d.ts +6 -0
- package/dist/cli/commands/tag/show.d.ts.map +1 -0
- package/dist/cli/commands/tag/show.js +88 -0
- package/dist/cli/commands/tag/show.js.map +1 -0
- package/dist/cli/commands/task/add-helpers.d.ts +17 -0
- package/dist/cli/commands/task/add-helpers.d.ts.map +1 -0
- package/dist/cli/commands/task/add-helpers.js +122 -0
- package/dist/cli/commands/task/add-helpers.js.map +1 -0
- package/dist/cli/commands/task/add.d.ts +6 -0
- package/dist/cli/commands/task/add.d.ts.map +1 -0
- package/dist/cli/commands/task/add.js +140 -0
- package/dist/cli/commands/task/add.js.map +1 -0
- package/dist/cli/commands/task/count.d.ts +6 -0
- package/dist/cli/commands/task/count.d.ts.map +1 -0
- package/dist/cli/commands/task/count.js +97 -0
- package/dist/cli/commands/task/count.js.map +1 -0
- package/dist/cli/commands/task/delete.d.ts +6 -0
- package/dist/cli/commands/task/delete.d.ts.map +1 -0
- package/dist/cli/commands/task/delete.js +59 -0
- package/dist/cli/commands/task/delete.js.map +1 -0
- package/dist/cli/commands/task/find.d.ts +7 -0
- package/dist/cli/commands/task/find.d.ts.map +1 -0
- package/dist/cli/commands/task/find.js +118 -0
- package/dist/cli/commands/task/find.js.map +1 -0
- package/dist/cli/commands/task/get.d.ts +6 -0
- package/dist/cli/commands/task/get.d.ts.map +1 -0
- package/dist/cli/commands/task/get.js +196 -0
- package/dist/cli/commands/task/get.js.map +1 -0
- package/dist/cli/commands/task/list.d.ts +6 -0
- package/dist/cli/commands/task/list.d.ts.map +1 -0
- package/dist/cli/commands/task/list.js +301 -0
- package/dist/cli/commands/task/list.js.map +1 -0
- package/dist/cli/commands/task/update-parent.d.ts +6 -0
- package/dist/cli/commands/task/update-parent.d.ts.map +1 -0
- package/dist/cli/commands/task/update-parent.js +123 -0
- package/dist/cli/commands/task/update-parent.js.map +1 -0
- package/dist/cli/commands/task/update.d.ts +6 -0
- package/dist/cli/commands/task/update.d.ts.map +1 -0
- package/dist/cli/commands/task/update.js +96 -0
- package/dist/cli/commands/task/update.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +68 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/utils/array-utils.d.ts +15 -0
- package/dist/cli/utils/array-utils.d.ts.map +1 -0
- package/dist/cli/utils/array-utils.js +20 -0
- package/dist/cli/utils/array-utils.js.map +1 -0
- package/dist/cli/utils/error-handler.d.ts +35 -0
- package/dist/cli/utils/error-handler.d.ts.map +1 -0
- package/dist/cli/utils/error-handler.js +84 -0
- package/dist/cli/utils/error-handler.js.map +1 -0
- package/dist/cli/utils/output-formatter.d.ts +34 -0
- package/dist/cli/utils/output-formatter.d.ts.map +1 -0
- package/dist/cli/utils/output-formatter.js +44 -0
- package/dist/cli/utils/output-formatter.js.map +1 -0
- package/dist/cli/utils/response-formatter.d.ts +19 -0
- package/dist/cli/utils/response-formatter.d.ts.map +1 -0
- package/dist/cli/utils/response-formatter.js +43 -0
- package/dist/cli/utils/response-formatter.js.map +1 -0
- package/dist/cli/utils/validators.d.ts +23 -0
- package/dist/cli/utils/validators.d.ts.map +1 -0
- package/dist/cli/utils/validators.js +47 -0
- package/dist/cli/utils/validators.js.map +1 -0
- package/dist/db/config.d.ts +27 -0
- package/dist/db/config.d.ts.map +1 -0
- package/dist/db/config.js +116 -0
- package/dist/db/config.js.map +1 -0
- package/dist/db/connection.d.ts +24 -0
- package/dist/db/connection.d.ts.map +1 -0
- package/dist/db/connection.js +62 -0
- package/dist/db/connection.js.map +1 -0
- package/dist/db/reset.d.ts +8 -0
- package/dist/db/reset.d.ts.map +1 -0
- package/dist/db/reset.js +33 -0
- package/dist/db/reset.js.map +1 -0
- package/dist/db/schema.d.ts +6 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +134 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/models/Attachment.d.ts +25 -0
- package/dist/models/Attachment.d.ts.map +1 -0
- package/dist/models/Attachment.js +7 -0
- package/dist/models/Attachment.js.map +1 -0
- package/dist/models/Tag.d.ts +24 -0
- package/dist/models/Tag.d.ts.map +1 -0
- package/dist/models/Tag.js +3 -0
- package/dist/models/Tag.js.map +1 -0
- package/dist/models/Task.d.ts +55 -0
- package/dist/models/Task.d.ts.map +1 -0
- package/dist/models/Task.js +7 -0
- package/dist/models/Task.js.map +1 -0
- package/dist/models/TaskBlock.d.ts +11 -0
- package/dist/models/TaskBlock.d.ts.map +1 -0
- package/dist/models/TaskBlock.js +3 -0
- package/dist/models/TaskBlock.js.map +1 -0
- package/dist/models/TaskMetadata.d.ts +30 -0
- package/dist/models/TaskMetadata.d.ts.map +1 -0
- package/dist/models/TaskMetadata.js +3 -0
- package/dist/models/TaskMetadata.js.map +1 -0
- package/dist/models/TaskTag.d.ts +11 -0
- package/dist/models/TaskTag.d.ts.map +1 -0
- package/dist/models/TaskTag.js +3 -0
- package/dist/models/TaskTag.js.map +1 -0
- package/dist/models/index.d.ts +10 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/index.js +7 -0
- package/dist/models/index.js.map +1 -0
- package/dist/services/AttachmentService.d.ts +62 -0
- package/dist/services/AttachmentService.d.ts.map +1 -0
- package/dist/services/AttachmentService.js +95 -0
- package/dist/services/AttachmentService.js.map +1 -0
- package/dist/services/FileService.d.ts +31 -0
- package/dist/services/FileService.d.ts.map +1 -0
- package/dist/services/FileService.js +77 -0
- package/dist/services/FileService.js.map +1 -0
- package/dist/services/MetadataService.d.ts +49 -0
- package/dist/services/MetadataService.d.ts.map +1 -0
- package/dist/services/MetadataService.js +126 -0
- package/dist/services/MetadataService.js.map +1 -0
- package/dist/services/TagService.d.ts +49 -0
- package/dist/services/TagService.d.ts.map +1 -0
- package/dist/services/TagService.js +127 -0
- package/dist/services/TagService.js.map +1 -0
- package/dist/services/TaskBlockService.d.ts +49 -0
- package/dist/services/TaskBlockService.d.ts.map +1 -0
- package/dist/services/TaskBlockService.js +118 -0
- package/dist/services/TaskBlockService.js.map +1 -0
- package/dist/services/TaskService.d.ts +89 -0
- package/dist/services/TaskService.d.ts.map +1 -0
- package/dist/services/TaskService.js +285 -0
- package/dist/services/TaskService.js.map +1 -0
- package/dist/services/TaskTagService.d.ts +66 -0
- package/dist/services/TaskTagService.d.ts.map +1 -0
- package/dist/services/TaskTagService.js +171 -0
- package/dist/services/TaskTagService.js.map +1 -0
- package/dist/services/index.d.ts +11 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +20 -0
- package/dist/services/index.js.map +1 -0
- package/dist/utils/cycle-detector.d.ts +14 -0
- package/dist/utils/cycle-detector.d.ts.map +1 -0
- package/dist/utils/cycle-detector.js +32 -0
- package/dist/utils/cycle-detector.js.map +1 -0
- package/dist/utils/format.d.ts +13 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/format.js +29 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/input-validators.d.ts +32 -0
- package/dist/utils/input-validators.d.ts.map +1 -0
- package/dist/utils/input-validators.js +108 -0
- package/dist/utils/input-validators.js.map +1 -0
- package/dist/utils/security.d.ts +11 -0
- package/dist/utils/security.d.ts.map +1 -0
- package/dist/utils/security.js +29 -0
- package/dist/utils/security.js.map +1 -0
- package/package.json +70 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TaskTagService = void 0;
|
|
4
|
+
const connection_1 = require("../db/connection");
|
|
5
|
+
const TaskService_1 = require("./TaskService");
|
|
6
|
+
const TagService_1 = require("./TagService");
|
|
7
|
+
/**
|
|
8
|
+
* Task Tag Service
|
|
9
|
+
* Manages associations between tasks and tags
|
|
10
|
+
*/
|
|
11
|
+
class TaskTagService {
|
|
12
|
+
constructor(db, taskService, tagService) {
|
|
13
|
+
this.db = db || (0, connection_1.getDatabase)();
|
|
14
|
+
this.taskService = taskService || new TaskService_1.TaskService(this.db);
|
|
15
|
+
this.tagService = tagService || new TagService_1.TagService(this.db);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Add tag to task
|
|
19
|
+
* @param input - Task tag creation input
|
|
20
|
+
* @returns Created task tag association
|
|
21
|
+
* @throws Error if task or tag does not exist, or if the association already exists
|
|
22
|
+
*/
|
|
23
|
+
addTagToTask(input) {
|
|
24
|
+
const db = this.db;
|
|
25
|
+
// Check if task exists
|
|
26
|
+
const task = this.taskService.getTask(input.task_id);
|
|
27
|
+
if (!task) {
|
|
28
|
+
throw new Error(`Task with id ${input.task_id} does not exist`);
|
|
29
|
+
}
|
|
30
|
+
// Check if tag exists
|
|
31
|
+
const tag = this.tagService.getTag(input.tag_id);
|
|
32
|
+
if (!tag) {
|
|
33
|
+
throw new Error(`Tag with id ${input.tag_id} does not exist`);
|
|
34
|
+
}
|
|
35
|
+
// Check if the association already exists
|
|
36
|
+
if (this.hasTag(input.task_id, input.tag_id)) {
|
|
37
|
+
throw new Error(`Task ${input.task_id} already has tag ${input.tag_id}`);
|
|
38
|
+
}
|
|
39
|
+
const now = new Date().toISOString();
|
|
40
|
+
const stmt = db.prepare(`
|
|
41
|
+
INSERT INTO task_tags (task_id, tag_id, created_at)
|
|
42
|
+
VALUES (?, ?, ?)
|
|
43
|
+
`);
|
|
44
|
+
const result = stmt.run(input.task_id, input.tag_id, now);
|
|
45
|
+
const getStmt = db.prepare('SELECT * FROM task_tags WHERE id = ?');
|
|
46
|
+
return getStmt.get(result.lastInsertRowid);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Remove tag from task
|
|
50
|
+
* @param taskId - Task ID
|
|
51
|
+
* @param tagId - Tag ID
|
|
52
|
+
* @returns True if removal was successful, false if association not found
|
|
53
|
+
*/
|
|
54
|
+
removeTagFromTask(taskId, tagId) {
|
|
55
|
+
const db = this.db;
|
|
56
|
+
const stmt = db.prepare(`
|
|
57
|
+
DELETE FROM task_tags
|
|
58
|
+
WHERE task_id = ? AND tag_id = ?
|
|
59
|
+
`);
|
|
60
|
+
const result = stmt.run(taskId, tagId);
|
|
61
|
+
return result.changes > 0;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Check if task has a tag
|
|
65
|
+
* @param taskId - Task ID
|
|
66
|
+
* @param tagId - Tag ID
|
|
67
|
+
* @returns True if task has the tag, false otherwise
|
|
68
|
+
*/
|
|
69
|
+
hasTag(taskId, tagId) {
|
|
70
|
+
const db = this.db;
|
|
71
|
+
const stmt = db.prepare(`
|
|
72
|
+
SELECT COUNT(*) as count FROM task_tags
|
|
73
|
+
WHERE task_id = ? AND tag_id = ?
|
|
74
|
+
`);
|
|
75
|
+
const result = stmt.get(taskId, tagId);
|
|
76
|
+
return result.count > 0;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Get tag IDs for a task
|
|
80
|
+
* @param taskId - Task ID
|
|
81
|
+
* @returns Array of tag IDs in order of assignment
|
|
82
|
+
*/
|
|
83
|
+
getTagIdsForTask(taskId) {
|
|
84
|
+
const db = this.db;
|
|
85
|
+
const stmt = db.prepare(`
|
|
86
|
+
SELECT tag_id FROM task_tags
|
|
87
|
+
WHERE task_id = ?
|
|
88
|
+
ORDER BY created_at ASC
|
|
89
|
+
`);
|
|
90
|
+
const results = stmt.all(taskId);
|
|
91
|
+
return results.map((row) => row.tag_id);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Get tag objects for a task
|
|
95
|
+
* @param taskId - Task ID
|
|
96
|
+
* @returns Array of tag objects in order of assignment
|
|
97
|
+
*/
|
|
98
|
+
getTagsForTask(taskId) {
|
|
99
|
+
const db = this.db;
|
|
100
|
+
const stmt = db.prepare(`
|
|
101
|
+
SELECT t.*
|
|
102
|
+
FROM tags t
|
|
103
|
+
INNER JOIN task_tags tt ON t.id = tt.tag_id
|
|
104
|
+
WHERE tt.task_id = ?
|
|
105
|
+
ORDER BY tt.created_at ASC
|
|
106
|
+
`);
|
|
107
|
+
return stmt.all(taskId);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Get task IDs for a tag
|
|
111
|
+
* @param tagId - Tag ID
|
|
112
|
+
* @returns Array of task IDs
|
|
113
|
+
*/
|
|
114
|
+
getTaskIdsForTag(tagId) {
|
|
115
|
+
const db = this.db;
|
|
116
|
+
const stmt = db.prepare(`
|
|
117
|
+
SELECT task_id FROM task_tags
|
|
118
|
+
WHERE tag_id = ?
|
|
119
|
+
ORDER BY created_at ASC
|
|
120
|
+
`);
|
|
121
|
+
const results = stmt.all(tagId);
|
|
122
|
+
return results.map((row) => row.task_id);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Get task objects for a tag
|
|
126
|
+
* @param tagId - Tag ID
|
|
127
|
+
* @returns Array of task objects
|
|
128
|
+
*/
|
|
129
|
+
getTasksForTag(tagId) {
|
|
130
|
+
const db = this.db;
|
|
131
|
+
const stmt = db.prepare(`
|
|
132
|
+
SELECT t.*
|
|
133
|
+
FROM tasks t
|
|
134
|
+
INNER JOIN task_tags tt ON t.id = tt.task_id
|
|
135
|
+
WHERE tt.tag_id = ?
|
|
136
|
+
ORDER BY tt.created_at ASC
|
|
137
|
+
`);
|
|
138
|
+
return stmt.all(tagId);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get all task tags at once
|
|
142
|
+
* Avoids the N+1 problem by fetching all task tags in a single query
|
|
143
|
+
* @returns Map<task_id, Tag[]>
|
|
144
|
+
*/
|
|
145
|
+
getAllTaskTags() {
|
|
146
|
+
const db = this.db;
|
|
147
|
+
const stmt = db.prepare(`
|
|
148
|
+
SELECT tt.task_id, t.*
|
|
149
|
+
FROM tags t
|
|
150
|
+
INNER JOIN task_tags tt ON t.id = tt.tag_id
|
|
151
|
+
ORDER BY tt.task_id, tt.created_at ASC
|
|
152
|
+
`);
|
|
153
|
+
const results = stmt.all();
|
|
154
|
+
const taskTagsMap = new Map();
|
|
155
|
+
for (const row of results) {
|
|
156
|
+
const taskId = row.task_id;
|
|
157
|
+
const tag = {
|
|
158
|
+
id: row.id,
|
|
159
|
+
name: row.name,
|
|
160
|
+
created_at: row.created_at,
|
|
161
|
+
};
|
|
162
|
+
if (!taskTagsMap.has(taskId)) {
|
|
163
|
+
taskTagsMap.set(taskId, []);
|
|
164
|
+
}
|
|
165
|
+
taskTagsMap.get(taskId).push(tag);
|
|
166
|
+
}
|
|
167
|
+
return taskTagsMap;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
exports.TaskTagService = TaskTagService;
|
|
171
|
+
//# sourceMappingURL=TaskTagService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TaskTagService.js","sourceRoot":"","sources":["../../src/services/TaskTagService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service layer exports
|
|
3
|
+
* Centrally manages all service classes
|
|
4
|
+
*/
|
|
5
|
+
export { TaskService } from './TaskService';
|
|
6
|
+
export { FileService } from './FileService';
|
|
7
|
+
export { TaskBlockService } from './TaskBlockService';
|
|
8
|
+
export { TagService } from './TagService';
|
|
9
|
+
export { TaskTagService } from './TaskTagService';
|
|
10
|
+
export { MetadataService } from './MetadataService';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Service layer exports
|
|
4
|
+
* Centrally manages all service classes
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.MetadataService = exports.TaskTagService = exports.TagService = exports.TaskBlockService = exports.FileService = exports.TaskService = void 0;
|
|
8
|
+
var TaskService_1 = require("./TaskService");
|
|
9
|
+
Object.defineProperty(exports, "TaskService", { enumerable: true, get: function () { return TaskService_1.TaskService; } });
|
|
10
|
+
var FileService_1 = require("./FileService");
|
|
11
|
+
Object.defineProperty(exports, "FileService", { enumerable: true, get: function () { return FileService_1.FileService; } });
|
|
12
|
+
var TaskBlockService_1 = require("./TaskBlockService");
|
|
13
|
+
Object.defineProperty(exports, "TaskBlockService", { enumerable: true, get: function () { return TaskBlockService_1.TaskBlockService; } });
|
|
14
|
+
var TagService_1 = require("./TagService");
|
|
15
|
+
Object.defineProperty(exports, "TagService", { enumerable: true, get: function () { return TagService_1.TagService; } });
|
|
16
|
+
var TaskTagService_1 = require("./TaskTagService");
|
|
17
|
+
Object.defineProperty(exports, "TaskTagService", { enumerable: true, get: function () { return TaskTagService_1.TaskTagService; } });
|
|
18
|
+
var MetadataService_1 = require("./MetadataService");
|
|
19
|
+
Object.defineProperty(exports, "MetadataService", { enumerable: true, get: function () { return MetadataService_1.MetadataService; } });
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,6CAA4C;AAAnC,0GAAA,WAAW,OAAA;AACpB,6CAA4C;AAAnC,0GAAA,WAAW,OAAA;AACpB,uDAAsD;AAA7C,oHAAA,gBAAgB,OAAA;AACzB,2CAA0C;AAAjC,wGAAA,UAAU,OAAA;AACnB,mDAAkD;AAAzC,gHAAA,cAAc,OAAA;AACvB,qDAAoD;AAA3C,kHAAA,eAAe,OAAA"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cycle detection utility for task parent-child relationships
|
|
3
|
+
* Decoupled from TaskService so it can be tested without a database.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Check whether setting a new parent would create a circular reference.
|
|
7
|
+
*
|
|
8
|
+
* @param taskId - The task that would become a child
|
|
9
|
+
* @param proposedParentId - The proposed new parent (null = remove parent)
|
|
10
|
+
* @param getParentId - Callback that returns the parent_id of a task (null if no parent / not found)
|
|
11
|
+
* @returns true if assigning proposedParentId as parent of taskId would create a cycle
|
|
12
|
+
*/
|
|
13
|
+
export declare function wouldCreateCycle(taskId: number, proposedParentId: number | null, getParentId: (id: number) => number | null): boolean;
|
|
14
|
+
//# sourceMappingURL=cycle-detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cycle-detector.d.ts","sourceRoot":"","sources":["../../src/utils/cycle-detector.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,MAAM,GAAG,IAAI,EAC/B,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,GACzC,OAAO,CAgBT"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Cycle detection utility for task parent-child relationships
|
|
4
|
+
* Decoupled from TaskService so it can be tested without a database.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.wouldCreateCycle = wouldCreateCycle;
|
|
8
|
+
/**
|
|
9
|
+
* Check whether setting a new parent would create a circular reference.
|
|
10
|
+
*
|
|
11
|
+
* @param taskId - The task that would become a child
|
|
12
|
+
* @param proposedParentId - The proposed new parent (null = remove parent)
|
|
13
|
+
* @param getParentId - Callback that returns the parent_id of a task (null if no parent / not found)
|
|
14
|
+
* @returns true if assigning proposedParentId as parent of taskId would create a cycle
|
|
15
|
+
*/
|
|
16
|
+
function wouldCreateCycle(taskId, proposedParentId, getParentId) {
|
|
17
|
+
if (!proposedParentId)
|
|
18
|
+
return false;
|
|
19
|
+
if (taskId === proposedParentId)
|
|
20
|
+
return true;
|
|
21
|
+
let currentId = proposedParentId;
|
|
22
|
+
const visited = new Set();
|
|
23
|
+
while (currentId !== null) {
|
|
24
|
+
if (visited.has(currentId) || currentId === taskId) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
visited.add(currentId);
|
|
28
|
+
currentId = getParentId(currentId);
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=cycle-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cycle-detector.js","sourceRoot":"","sources":["../../src/utils/cycle-detector.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAUH,4CAoBC;AA5BD;;;;;;;GAOG;AACH,SAAgB,gBAAgB,CAC9B,MAAc,EACd,gBAA+B,EAC/B,WAA0C;IAE1C,IAAI,CAAC,gBAAgB;QAAE,OAAO,KAAK,CAAC;IACpC,IAAI,MAAM,KAAK,gBAAgB;QAAE,OAAO,IAAI,CAAC;IAE7C,IAAI,SAAS,GAAkB,gBAAgB,CAAC;IAChD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAElC,OAAO,SAAS,KAAK,IAAI,EAAE,CAAC;QAC1B,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;YACnD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACvB,SAAS,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;IACrC,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { TaskStatus } from '../models';
|
|
2
|
+
/**
|
|
3
|
+
* Get the color corresponding to a status
|
|
4
|
+
* Returns the color name to be used by chalk
|
|
5
|
+
*/
|
|
6
|
+
export declare function getStatusColor(status: TaskStatus): 'gray' | 'blue' | 'yellow' | 'cyan' | 'green' | 'magenta';
|
|
7
|
+
/**
|
|
8
|
+
* Format a date from ISO 8601 format to Japanese locale format
|
|
9
|
+
* @param isoDate - ISO 8601 format date string
|
|
10
|
+
* @returns Formatted date string
|
|
11
|
+
*/
|
|
12
|
+
export declare function formatDate(isoDate: string): string;
|
|
13
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAU5G;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGlD"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getStatusColor = getStatusColor;
|
|
4
|
+
exports.formatDate = formatDate;
|
|
5
|
+
/**
|
|
6
|
+
* Get the color corresponding to a status
|
|
7
|
+
* Returns the color name to be used by chalk
|
|
8
|
+
*/
|
|
9
|
+
function getStatusColor(status) {
|
|
10
|
+
const colorMap = {
|
|
11
|
+
backlog: 'gray',
|
|
12
|
+
ready: 'blue',
|
|
13
|
+
in_progress: 'yellow',
|
|
14
|
+
review: 'cyan',
|
|
15
|
+
done: 'green',
|
|
16
|
+
closed: 'magenta',
|
|
17
|
+
};
|
|
18
|
+
return colorMap[status];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Format a date from ISO 8601 format to Japanese locale format
|
|
22
|
+
* @param isoDate - ISO 8601 format date string
|
|
23
|
+
* @returns Formatted date string
|
|
24
|
+
*/
|
|
25
|
+
function formatDate(isoDate) {
|
|
26
|
+
const date = new Date(isoDate);
|
|
27
|
+
return date.toLocaleString('ja-JP');
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":";;AAMA,wCAUC;AAOD,gCAGC;AAxBD;;;GAGG;AACH,SAAgB,cAAc,CAAC,MAAkB;IAC/C,MAAM,QAAQ,GAAkF;QAC9F,OAAO,EAAE,MAAM;QACf,KAAK,EAAE,MAAM;QACb,WAAW,EAAE,QAAQ;QACrB,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,OAAO;QACb,MAAM,EAAE,SAAS;KAClB,CAAC;IACF,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC1B,CAAC;AAED;;;;GAIG;AACH,SAAgB,UAAU,CAAC,OAAe;IACxC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/B,OAAO,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;AACtC,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { CreateTaskInput, UpdateTaskInput } from '../models/Task';
|
|
2
|
+
import type { CreateTagInput } from '../models/Tag';
|
|
3
|
+
/**
|
|
4
|
+
* Input validation utility functions
|
|
5
|
+
* Provides validation for user input with length limits
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Validation error interface
|
|
9
|
+
*/
|
|
10
|
+
export interface ValidationError {
|
|
11
|
+
field: string;
|
|
12
|
+
message: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Validate task input for security and length constraints
|
|
16
|
+
* @param input - Task creation input to validate
|
|
17
|
+
* @returns Array of validation errors (empty if valid)
|
|
18
|
+
*/
|
|
19
|
+
export declare function validateTaskInput(input: CreateTaskInput): ValidationError[];
|
|
20
|
+
/**
|
|
21
|
+
* Validate task update input for security and length constraints
|
|
22
|
+
* @param input - Task update input to validate (all fields optional)
|
|
23
|
+
* @returns Array of validation errors (empty if valid)
|
|
24
|
+
*/
|
|
25
|
+
export declare function validateTaskUpdateInput(input: UpdateTaskInput): ValidationError[];
|
|
26
|
+
/**
|
|
27
|
+
* Validate tag input for security and length constraints
|
|
28
|
+
* @param input - Tag creation input to validate
|
|
29
|
+
* @returns Array of validation errors (empty if valid)
|
|
30
|
+
*/
|
|
31
|
+
export declare function validateTagInput(input: CreateTagInput): ValidationError[];
|
|
32
|
+
//# sourceMappingURL=input-validators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-validators.d.ts","sourceRoot":"","sources":["../../src/utils/input-validators.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD;;;GAGG;AAEH;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,eAAe,GAAG,eAAe,EAAE,CAiC3E;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,eAAe,GAAG,eAAe,EAAE,CAmCjF;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,GAAG,eAAe,EAAE,CAsBzE"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateTaskInput = validateTaskInput;
|
|
4
|
+
exports.validateTaskUpdateInput = validateTaskUpdateInput;
|
|
5
|
+
exports.validateTagInput = validateTagInput;
|
|
6
|
+
/**
|
|
7
|
+
* Validate task input for security and length constraints
|
|
8
|
+
* @param input - Task creation input to validate
|
|
9
|
+
* @returns Array of validation errors (empty if valid)
|
|
10
|
+
*/
|
|
11
|
+
function validateTaskInput(input) {
|
|
12
|
+
const errors = [];
|
|
13
|
+
// Validate title (required, max 200 chars)
|
|
14
|
+
if (!input.title || input.title.trim().length === 0) {
|
|
15
|
+
errors.push({
|
|
16
|
+
field: 'title',
|
|
17
|
+
message: 'Title is required',
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
else if (input.title.length > 200) {
|
|
21
|
+
errors.push({
|
|
22
|
+
field: 'title',
|
|
23
|
+
message: 'Title must not exceed 200 characters',
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
// Validate body (optional, max 10000 chars)
|
|
27
|
+
if (input.body && input.body.length > 10000) {
|
|
28
|
+
errors.push({
|
|
29
|
+
field: 'body',
|
|
30
|
+
message: 'Body must not exceed 10000 characters',
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
// Validate author (optional, max 100 chars)
|
|
34
|
+
if (input.author && input.author.length > 100) {
|
|
35
|
+
errors.push({
|
|
36
|
+
field: 'author',
|
|
37
|
+
message: 'Author must not exceed 100 characters',
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return errors;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Validate task update input for security and length constraints
|
|
44
|
+
* @param input - Task update input to validate (all fields optional)
|
|
45
|
+
* @returns Array of validation errors (empty if valid)
|
|
46
|
+
*/
|
|
47
|
+
function validateTaskUpdateInput(input) {
|
|
48
|
+
const errors = [];
|
|
49
|
+
// Validate title if being updated (required when provided, max 200 chars)
|
|
50
|
+
if (input.title !== undefined) {
|
|
51
|
+
if (!input.title || input.title.trim().length === 0) {
|
|
52
|
+
errors.push({
|
|
53
|
+
field: 'title',
|
|
54
|
+
message: 'Title is required',
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
else if (input.title.length > 200) {
|
|
58
|
+
errors.push({
|
|
59
|
+
field: 'title',
|
|
60
|
+
message: 'Title must not exceed 200 characters',
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Validate body if being updated (optional, max 10000 chars)
|
|
65
|
+
if (input.body !== undefined && input.body !== null && input.body.length > 10000) {
|
|
66
|
+
errors.push({
|
|
67
|
+
field: 'body',
|
|
68
|
+
message: 'Body must not exceed 10000 characters',
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
// Validate author if being updated (optional, max 100 chars)
|
|
72
|
+
if (input.author !== undefined && input.author !== null && input.author.length > 100) {
|
|
73
|
+
errors.push({
|
|
74
|
+
field: 'author',
|
|
75
|
+
message: 'Author must not exceed 100 characters',
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return errors;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Validate tag input for security and length constraints
|
|
82
|
+
* @param input - Tag creation input to validate
|
|
83
|
+
* @returns Array of validation errors (empty if valid)
|
|
84
|
+
*/
|
|
85
|
+
function validateTagInput(input) {
|
|
86
|
+
const errors = [];
|
|
87
|
+
// Validate name (required, max 50 chars)
|
|
88
|
+
if (!input.name || input.name.trim().length === 0) {
|
|
89
|
+
errors.push({
|
|
90
|
+
field: 'name',
|
|
91
|
+
message: 'Name is required',
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
else if (input.name.length > 50) {
|
|
95
|
+
errors.push({
|
|
96
|
+
field: 'name',
|
|
97
|
+
message: 'Name must not exceed 50 characters',
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
else if (/^\d+$/.test(input.name.trim())) {
|
|
101
|
+
errors.push({
|
|
102
|
+
field: 'name',
|
|
103
|
+
message: 'Tag name cannot be purely numeric',
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return errors;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=input-validators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-validators.js","sourceRoot":"","sources":["../../src/utils/input-validators.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security utility functions
|
|
3
|
+
* Provides path validation and security checks
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Check if a file path is safe from path traversal attacks
|
|
7
|
+
* @param filePath - Path to validate
|
|
8
|
+
* @returns True if path is safe, false otherwise
|
|
9
|
+
*/
|
|
10
|
+
export declare function isPathSafe(filePath: string): boolean;
|
|
11
|
+
//# sourceMappingURL=security.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../../src/utils/security.ts"],"names":[],"mappings":"AAEA;;;GAGG;AAEH;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAapD"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.isPathSafe = isPathSafe;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
/**
|
|
9
|
+
* Security utility functions
|
|
10
|
+
* Provides path validation and security checks
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Check if a file path is safe from path traversal attacks
|
|
14
|
+
* @param filePath - Path to validate
|
|
15
|
+
* @returns True if path is safe, false otherwise
|
|
16
|
+
*/
|
|
17
|
+
function isPathSafe(filePath) {
|
|
18
|
+
// Check for path traversal (..)
|
|
19
|
+
if (filePath.includes('..')) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
// For additional safety, check the normalized path doesn't contain '..'
|
|
23
|
+
const normalizedPath = path_1.default.normalize(filePath);
|
|
24
|
+
if (normalizedPath.includes('..')) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=security.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["../../src/utils/security.ts"],"names":[],"mappings":";;;;;AAYA,gCAaC;AAzBD,gDAAwB;AAExB;;;GAGG;AAEH;;;;GAIG;AACH,SAAgB,UAAU,CAAC,QAAgB;IACzC,gCAAgC;IAChC,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,wEAAwE;IACxE,MAAM,cAAc,GAAG,cAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAChD,IAAI,cAAc,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAClC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agkan",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "TypeScript-based CLI task management tool with SQLite storage",
|
|
5
|
+
"main": "dist/cli/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"agkan": "./bin/agkan"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"bin"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"dev": "tsc --watch",
|
|
16
|
+
"start": "node dist/cli/index.js",
|
|
17
|
+
"test": "vitest",
|
|
18
|
+
"test:errors": "vitest run --reporter=./tests/reporters/error-only.ts",
|
|
19
|
+
"test:e2e": "./test-e2e.sh",
|
|
20
|
+
"lint": "eslint src tests --ext .ts",
|
|
21
|
+
"lint:fix": "eslint src tests --ext .ts --fix",
|
|
22
|
+
"format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"",
|
|
23
|
+
"format:check": "prettier --check \"src/**/*.ts\" \"tests/**/*.ts\"",
|
|
24
|
+
"type-check": "tsc --noEmit",
|
|
25
|
+
"check": "npm run type-check && npm run lint && npm run format:check",
|
|
26
|
+
"prepublishOnly": "npm run check && npm run build && vitest run",
|
|
27
|
+
"prepare": "husky || true"
|
|
28
|
+
},
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/gendosu/agkan.git"
|
|
32
|
+
},
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/gendosu/agkan/issues"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/gendosu/agkan#readme",
|
|
37
|
+
"keywords": [
|
|
38
|
+
"cli",
|
|
39
|
+
"task-management",
|
|
40
|
+
"kanban",
|
|
41
|
+
"typescript",
|
|
42
|
+
"sqlite"
|
|
43
|
+
],
|
|
44
|
+
"author": "gendosu <gendosu@gmail.com>",
|
|
45
|
+
"license": "ISC",
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=18.0.0"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"better-sqlite3": "^11.7.0",
|
|
51
|
+
"chalk": "^4.1.2",
|
|
52
|
+
"commander": "^12.1.0",
|
|
53
|
+
"js-yaml": "^4.1.1"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/better-sqlite3": "^7.6.12",
|
|
57
|
+
"@types/js-yaml": "^4.0.9",
|
|
58
|
+
"@types/node": "^22.10.5",
|
|
59
|
+
"@typescript-eslint/eslint-plugin": "^8.55.0",
|
|
60
|
+
"@typescript-eslint/parser": "^8.55.0",
|
|
61
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
62
|
+
"eslint": "^9.39.2",
|
|
63
|
+
"eslint-config-prettier": "^10.1.8",
|
|
64
|
+
"husky": "^9.1.7",
|
|
65
|
+
"lint-staged": "^15.5.2",
|
|
66
|
+
"prettier": "^3.8.1",
|
|
67
|
+
"typescript": "^5.7.3",
|
|
68
|
+
"vitest": "^4.0.18"
|
|
69
|
+
}
|
|
70
|
+
}
|