@opentask/taskin-task-manager 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +5 -0
- package/.turbo/turbo-format.log +31 -0
- package/.turbo/turbo-test.log +15 -0
- package/CHANGELOG.md +7 -0
- package/README.md +84 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/metrics.types.d.ts +92 -0
- package/dist/metrics.types.js +2 -0
- package/dist/metrics.types.js.map +1 -0
- package/dist/provider-stats.test.d.ts +1 -0
- package/dist/provider-stats.test.js +17 -0
- package/dist/provider-stats.test.js.map +1 -0
- package/dist/task-manager.d.ts +16 -0
- package/dist/task-manager.js +37 -0
- package/dist/task-manager.js.map +1 -0
- package/dist/task-manager.mock.d.ts +5 -0
- package/dist/task-manager.mock.js +21 -0
- package/dist/task-manager.mock.js.map +1 -0
- package/dist/task-manager.test.d.ts +1 -0
- package/dist/task-manager.test.js +104 -0
- package/dist/task-manager.test.js.map +1 -0
- package/dist/task-manager.types.d.ts +152 -0
- package/dist/task-manager.types.js +2 -0
- package/dist/task-manager.types.js.map +1 -0
- package/eslint.config.js +8 -0
- package/package.json +52 -0
- package/src/index.ts +5 -0
- package/src/metrics.types.ts +96 -0
- package/src/provider-stats.test.ts +20 -0
- package/src/task-manager.mock.ts +24 -0
- package/src/task-manager.test.ts +133 -0
- package/src/task-manager.ts +54 -0
- package/src/task-manager.types.ts +167 -0
- package/tsconfig.json +11 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +7 -0
package/eslint.config.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@opentask/taskin-task-manager",
|
|
3
|
+
"version": "1.0.6",
|
|
4
|
+
"description": "Task lifecycle management for Taskin",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"motivation": "To manage the lifecycle of tasks, including creation, status updates, and retrieval.",
|
|
7
|
+
"solve": "Provides a structured API for interacting with task data, abstracting the underlying storage mechanism.",
|
|
8
|
+
"scope": "core",
|
|
9
|
+
"status": "active",
|
|
10
|
+
"since": "2025-11-05",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"taskin",
|
|
13
|
+
"task-manager",
|
|
14
|
+
"task-management",
|
|
15
|
+
"lifecycle"
|
|
16
|
+
],
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/sidartaveloso/taskin.git",
|
|
21
|
+
"directory": "packages/task-manager"
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"main": "./dist/index.js",
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"import": "./dist/index.js"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@opentask/taskin-types": "1.0.5"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"typescript": "^5.9.3"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsc",
|
|
42
|
+
"clean": "rm -rf dist coverage .turbo",
|
|
43
|
+
"dev": "tsc --watch",
|
|
44
|
+
"format": "prettier --write .",
|
|
45
|
+
"format:check": "prettier --check .",
|
|
46
|
+
"lint": "eslint .",
|
|
47
|
+
"lint:fix": "eslint . --fix",
|
|
48
|
+
"test": "vitest run",
|
|
49
|
+
"test:coverage": "vitest run --coverage",
|
|
50
|
+
"typecheck": "tsc --noEmit"
|
|
51
|
+
}
|
|
52
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
StatsQuery,
|
|
3
|
+
TaskStats,
|
|
4
|
+
TeamStats,
|
|
5
|
+
UserStats,
|
|
6
|
+
} from '@opentask/taskin-types';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Manages metrics and statistics for users, teams, and tasks.
|
|
10
|
+
* Aggregates data from Git history and task files to provide productivity insights.
|
|
11
|
+
*
|
|
12
|
+
* @remarks
|
|
13
|
+
* This interface separates analytics/reporting responsibilities from ITaskProvider,
|
|
14
|
+
* following the Single Responsibility Principle.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const metrics = new FileSystemMetricsAdapter(tasksDir, userRegistry, gitAnalyzer);
|
|
19
|
+
* const stats = await metrics.getUserMetrics('john-doe', { period: 'week' });
|
|
20
|
+
* console.log(`Commits: ${stats.codeMetrics.commits}`);
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @public
|
|
24
|
+
*/
|
|
25
|
+
export interface IMetricsManager {
|
|
26
|
+
/**
|
|
27
|
+
* Get productivity metrics for a specific user.
|
|
28
|
+
*
|
|
29
|
+
* Includes code metrics (commits, lines of code), temporal patterns
|
|
30
|
+
* (day of week, time of day), and engagement statistics (completion rate, streaks).
|
|
31
|
+
*
|
|
32
|
+
* @param userId - User identifier (username or registry ID)
|
|
33
|
+
* @param query - Optional query parameters to filter results
|
|
34
|
+
* @param query.period - Time period to analyze ('day' | 'week' | 'month' | 'year')
|
|
35
|
+
* @returns Promise resolving to user statistics
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* const stats = await metrics.getUserMetrics('alice', { period: 'month' });
|
|
40
|
+
* console.log(`Tasks completed: ${stats.contributionMetrics.tasksCompleted}`);
|
|
41
|
+
* console.log(`Completion rate: ${(stats.engagementMetrics.completionRate * 100).toFixed(1)}%`);
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
getUserMetrics(userId: string, query?: StatsQuery): Promise<UserStats>;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Get aggregated metrics for an entire team.
|
|
48
|
+
*
|
|
49
|
+
* Provides overview of team productivity with breakdown by individual contributors.
|
|
50
|
+
* Useful for team retrospectives, capacity planning, and identifying top contributors.
|
|
51
|
+
*
|
|
52
|
+
* @param teamId - Team identifier
|
|
53
|
+
* @param query - Optional query parameters to filter results
|
|
54
|
+
* @returns Promise resolving to team statistics with per-contributor breakdown
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* const stats = await metrics.getTeamMetrics('frontend', { period: 'week' });
|
|
59
|
+
* console.log(`Total commits: ${stats.totalCommits}`);
|
|
60
|
+
* console.log(`Contributors: ${stats.totalContributors}`);
|
|
61
|
+
* stats.contributors.forEach(c => {
|
|
62
|
+
* console.log(`${c.username}: ${c.commits} commits, ${c.tasksCompleted} tasks`);
|
|
63
|
+
* });
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
getTeamMetrics(teamId: string, query?: StatsQuery): Promise<TeamStats>;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get detailed metrics for a specific task.
|
|
70
|
+
*
|
|
71
|
+
* Includes timeline analysis, contributor activity, code impact metrics,
|
|
72
|
+
* and work patterns. Useful for task retrospectives and understanding
|
|
73
|
+
* effort distribution.
|
|
74
|
+
*
|
|
75
|
+
* @param taskId - Task identifier (task number like '015' or full ID like 'task-015')
|
|
76
|
+
* @param query - Optional query parameters
|
|
77
|
+
* @returns Promise resolving to task-specific statistics
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```ts
|
|
81
|
+
* const stats = await metrics.getTaskMetrics('task-015', {});
|
|
82
|
+
* console.log(`Duration: ${stats.duration} days`);
|
|
83
|
+
* console.log(`Contributors: ${stats.contributors.map(c => c.name).join(', ')}`);
|
|
84
|
+
* if (stats.refactoringMetrics) {
|
|
85
|
+
* console.log(`Simplification ratio: ${stats.refactoringMetrics.simplificationRatio.toFixed(2)}x`);
|
|
86
|
+
* }
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
getTaskMetrics(taskId: string, query?: StatsQuery): Promise<TaskStats>;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export type {
|
|
93
|
+
TaskStats as TaskMetrics,
|
|
94
|
+
TeamStats as TeamMetrics,
|
|
95
|
+
UserStats as UserMetrics,
|
|
96
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import type { IMetricsManager } from './metrics.types';
|
|
3
|
+
|
|
4
|
+
describe('IMetricsManager contract', () => {
|
|
5
|
+
it('accepts an object implementing IMetricsManager', async () => {
|
|
6
|
+
const metrics: IMetricsManager = {
|
|
7
|
+
getUserMetrics: async (userId: string) => ({ userId }) as any,
|
|
8
|
+
getTeamMetrics: async (teamId: string) => ({ teamId }) as any,
|
|
9
|
+
getTaskMetrics: async (taskId: string) => ({ taskId }) as any,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
expect(typeof metrics.getUserMetrics).toBe('function');
|
|
13
|
+
expect(typeof metrics.getTeamMetrics).toBe('function');
|
|
14
|
+
expect(typeof metrics.getTaskMetrics).toBe('function');
|
|
15
|
+
|
|
16
|
+
await expect(metrics.getUserMetrics('u1')).resolves.toBeDefined();
|
|
17
|
+
await expect(metrics.getTeamMetrics('t1')).resolves.toBeDefined();
|
|
18
|
+
await expect(metrics.getTaskMetrics('task1')).resolves.toBeDefined();
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { TaskId } from '@opentask/taskin-types';
|
|
2
|
+
import { vi } from 'vitest';
|
|
3
|
+
import type { ITaskProvider, TaskFile } from './task-manager.types';
|
|
4
|
+
|
|
5
|
+
export const createMockTask = (overrides?: Partial<TaskFile>): TaskFile => ({
|
|
6
|
+
content: '# Task 001 - Implement feature',
|
|
7
|
+
createdAt: new Date().toISOString(),
|
|
8
|
+
description: 'A test feature',
|
|
9
|
+
filePath: '/tasks/task-001.md',
|
|
10
|
+
id: 'task-001' satisfies string as TaskId,
|
|
11
|
+
status: 'pending',
|
|
12
|
+
title: 'Implement feature',
|
|
13
|
+
type: 'feat',
|
|
14
|
+
userId: 'user-123',
|
|
15
|
+
...overrides,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export const createMockTaskProvider = (): ITaskProvider => ({
|
|
19
|
+
findTask: vi.fn(),
|
|
20
|
+
getAllTasks: vi.fn(),
|
|
21
|
+
updateTask: vi.fn(),
|
|
22
|
+
createTask: vi.fn(),
|
|
23
|
+
lint: vi.fn(),
|
|
24
|
+
});
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type { Mock } from 'vitest';
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { TaskManager } from './task-manager';
|
|
4
|
+
import { createMockTask, createMockTaskProvider } from './task-manager.mock';
|
|
5
|
+
import type { ITaskProvider, TaskFile } from './task-manager.types';
|
|
6
|
+
|
|
7
|
+
describe('TaskManager', () => {
|
|
8
|
+
let taskManager: TaskManager;
|
|
9
|
+
let mockTaskProvider: ITaskProvider;
|
|
10
|
+
let mockTask: TaskFile;
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
vi.clearAllMocks();
|
|
14
|
+
mockTaskProvider = createMockTaskProvider();
|
|
15
|
+
mockTask = createMockTask();
|
|
16
|
+
taskManager = new TaskManager(mockTaskProvider);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe('startTask', () => {
|
|
20
|
+
it('should start a pending task', async () => {
|
|
21
|
+
(mockTaskProvider.findTask as Mock).mockResolvedValue(mockTask);
|
|
22
|
+
|
|
23
|
+
const updatedTask = await taskManager.startTask('task-001');
|
|
24
|
+
|
|
25
|
+
expect(mockTaskProvider.findTask).toHaveBeenCalledWith('task-001');
|
|
26
|
+
expect(mockTaskProvider.updateTask).toHaveBeenCalledWith(
|
|
27
|
+
expect.objectContaining({ status: 'in-progress' }),
|
|
28
|
+
);
|
|
29
|
+
expect(updatedTask.status).toBe('in-progress');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should throw an error if task is not found', async () => {
|
|
33
|
+
(mockTaskProvider.findTask as Mock).mockResolvedValue(undefined);
|
|
34
|
+
await expect(taskManager.startTask('not-found')).rejects.toThrow(
|
|
35
|
+
"Task with ID 'not-found' not found.",
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should throw an error if task is already in progress', async () => {
|
|
40
|
+
const inProgressTask = { ...mockTask, status: 'in-progress' as const };
|
|
41
|
+
(mockTaskProvider.findTask as Mock).mockResolvedValue(inProgressTask);
|
|
42
|
+
|
|
43
|
+
await expect(taskManager.startTask('task-001')).rejects.toThrow(
|
|
44
|
+
"Task 'task-001' is already in progress.",
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('finishTask', () => {
|
|
50
|
+
it('should finish an in-progress task', async () => {
|
|
51
|
+
const inProgressTask = { ...mockTask, status: 'in-progress' as const };
|
|
52
|
+
(mockTaskProvider.findTask as Mock).mockResolvedValue(inProgressTask);
|
|
53
|
+
|
|
54
|
+
const updatedTask = await taskManager.finishTask('task-001');
|
|
55
|
+
|
|
56
|
+
expect(mockTaskProvider.updateTask).toHaveBeenCalledWith(
|
|
57
|
+
expect.objectContaining({ status: 'done' }),
|
|
58
|
+
);
|
|
59
|
+
expect(updatedTask.status).toBe('done');
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('createTask', () => {
|
|
64
|
+
it('should delegate task creation to provider', async () => {
|
|
65
|
+
const createOptions = { title: 'New Task', type: 'feat' as const };
|
|
66
|
+
const createResult = {
|
|
67
|
+
task: mockTask,
|
|
68
|
+
taskId: '001',
|
|
69
|
+
filePath: '/tasks/task-001.md',
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
(mockTaskProvider.createTask as Mock).mockResolvedValue(createResult);
|
|
73
|
+
|
|
74
|
+
const result = await taskManager.createTask(createOptions);
|
|
75
|
+
|
|
76
|
+
expect(mockTaskProvider.createTask).toHaveBeenCalledWith(createOptions);
|
|
77
|
+
expect(result).toEqual(createResult);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should pass through description and assignee to provider', async () => {
|
|
81
|
+
const createOptions = {
|
|
82
|
+
title: 'New Task',
|
|
83
|
+
type: 'fix' as const,
|
|
84
|
+
description: 'Fix something',
|
|
85
|
+
assignee: 'john-doe',
|
|
86
|
+
};
|
|
87
|
+
const createResult = {
|
|
88
|
+
task: mockTask,
|
|
89
|
+
taskId: '002',
|
|
90
|
+
filePath: '/tasks/task-002.md',
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
(mockTaskProvider.createTask as Mock).mockResolvedValue(createResult);
|
|
94
|
+
|
|
95
|
+
const result = await taskManager.createTask(createOptions);
|
|
96
|
+
|
|
97
|
+
expect(mockTaskProvider.createTask).toHaveBeenCalledWith(createOptions);
|
|
98
|
+
expect(result.taskId).toBe('002');
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe('lint', () => {
|
|
103
|
+
it('should delegate lint without fix to provider', async () => {
|
|
104
|
+
const lintResult = { errors: 0, warnings: 2, details: [] };
|
|
105
|
+
(mockTaskProvider.lint as Mock).mockResolvedValue(lintResult);
|
|
106
|
+
|
|
107
|
+
const result = await taskManager.lint();
|
|
108
|
+
|
|
109
|
+
expect(mockTaskProvider.lint).toHaveBeenCalledWith(undefined);
|
|
110
|
+
expect(result).toEqual(lintResult);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should delegate lint with fix=true to provider', async () => {
|
|
114
|
+
const lintResult = { errors: 0, warnings: 0, details: [] };
|
|
115
|
+
(mockTaskProvider.lint as Mock).mockResolvedValue(lintResult);
|
|
116
|
+
|
|
117
|
+
const result = await taskManager.lint(true);
|
|
118
|
+
|
|
119
|
+
expect(mockTaskProvider.lint).toHaveBeenCalledWith(true);
|
|
120
|
+
expect(result).toEqual(lintResult);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should delegate lint with fix=false to provider', async () => {
|
|
124
|
+
const lintResult = { errors: 1, warnings: 2, details: [] };
|
|
125
|
+
(mockTaskProvider.lint as Mock).mockResolvedValue(lintResult);
|
|
126
|
+
|
|
127
|
+
const result = await taskManager.lint(false);
|
|
128
|
+
|
|
129
|
+
expect(mockTaskProvider.lint).toHaveBeenCalledWith(false);
|
|
130
|
+
expect(result).toEqual(lintResult);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
CreateTaskOptions,
|
|
3
|
+
CreateTaskResult,
|
|
4
|
+
ITaskManager,
|
|
5
|
+
ITaskProvider,
|
|
6
|
+
LintResult,
|
|
7
|
+
TaskFile,
|
|
8
|
+
} from './task-manager.types';
|
|
9
|
+
|
|
10
|
+
export class TaskManager implements ITaskManager {
|
|
11
|
+
constructor(private taskProvider: ITaskProvider) {}
|
|
12
|
+
|
|
13
|
+
async startTask(taskId: string): Promise<TaskFile> {
|
|
14
|
+
const task = await this.taskProvider.findTask(taskId);
|
|
15
|
+
|
|
16
|
+
if (!task) {
|
|
17
|
+
throw new Error(`Task with ID '${taskId}' not found.`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (task.status === 'in-progress') {
|
|
21
|
+
throw new Error(`Task '${taskId}' is already in progress.`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (task.status === 'done') {
|
|
25
|
+
throw new Error(`Task '${taskId}' is already done.`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const updatedTask: TaskFile = { ...task, status: 'in-progress' };
|
|
29
|
+
await this.taskProvider.updateTask(updatedTask);
|
|
30
|
+
|
|
31
|
+
return updatedTask;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async finishTask(taskId: string): Promise<TaskFile> {
|
|
35
|
+
const task = await this.taskProvider.findTask(taskId);
|
|
36
|
+
|
|
37
|
+
if (!task) {
|
|
38
|
+
throw new Error(`Task with ID '${taskId}' not found.`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const updatedTask: TaskFile = { ...task, status: 'done' };
|
|
42
|
+
await this.taskProvider.updateTask(updatedTask);
|
|
43
|
+
|
|
44
|
+
return updatedTask;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async createTask(options: CreateTaskOptions): Promise<CreateTaskResult> {
|
|
48
|
+
return this.taskProvider.createTask(options);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async lint(fix?: boolean): Promise<LintResult> {
|
|
52
|
+
return this.taskProvider.lint(fix);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import type { Task, TaskType, User } from '@opentask/taskin-types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Options for creating a new task
|
|
5
|
+
* @public
|
|
6
|
+
*/
|
|
7
|
+
export interface CreateTaskOptions {
|
|
8
|
+
/** Task title */
|
|
9
|
+
title: string;
|
|
10
|
+
/** Task type (feat, fix, chore, etc.) */
|
|
11
|
+
type: TaskType;
|
|
12
|
+
/** Optional task description */
|
|
13
|
+
description?: string;
|
|
14
|
+
/** Optional assignee user ID or name */
|
|
15
|
+
assignee?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Result of creating a new task
|
|
20
|
+
* @public
|
|
21
|
+
*/
|
|
22
|
+
export interface CreateTaskResult {
|
|
23
|
+
/** The created task */
|
|
24
|
+
task: TaskFile;
|
|
25
|
+
/** The generated task ID */
|
|
26
|
+
taskId: string;
|
|
27
|
+
/** The file path where the task was created */
|
|
28
|
+
filePath: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Severity level for validation issues
|
|
33
|
+
* @public
|
|
34
|
+
*/
|
|
35
|
+
export type ValidationSeverity = 'error' | 'warning' | 'info';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A validation error or warning found during linting
|
|
39
|
+
* @public
|
|
40
|
+
*/
|
|
41
|
+
export interface ValidationIssue {
|
|
42
|
+
/** The file or task that has the issue */
|
|
43
|
+
file: string;
|
|
44
|
+
/** Optional line number where the issue occurs */
|
|
45
|
+
line?: number;
|
|
46
|
+
/** Human-readable description of the issue */
|
|
47
|
+
message: string;
|
|
48
|
+
/** Severity level of the issue */
|
|
49
|
+
severity: ValidationSeverity;
|
|
50
|
+
/** Optional suggestion for fixing the issue */
|
|
51
|
+
suggestion?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Result of a lint operation
|
|
56
|
+
* @public
|
|
57
|
+
*/
|
|
58
|
+
export interface LintResult {
|
|
59
|
+
/** Whether the lint passed without errors */
|
|
60
|
+
valid: boolean;
|
|
61
|
+
/** List of validation issues found */
|
|
62
|
+
issues: ValidationIssue[];
|
|
63
|
+
/** Number of errors found */
|
|
64
|
+
errorCount: number;
|
|
65
|
+
/** Number of warnings found */
|
|
66
|
+
warningCount: number;
|
|
67
|
+
/** Number of info messages */
|
|
68
|
+
infoCount: number;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* A task with additional file system metadata.
|
|
73
|
+
* Extends the base Task type with content and path information.
|
|
74
|
+
* @public
|
|
75
|
+
*/
|
|
76
|
+
export type TaskFile = Task & {
|
|
77
|
+
/** The raw markdown content of the task file */
|
|
78
|
+
content: string;
|
|
79
|
+
/** Absolute or relative path to the task file */
|
|
80
|
+
filePath: string;
|
|
81
|
+
/** The type of work this task represents */
|
|
82
|
+
type: TaskType;
|
|
83
|
+
/** Optional user assigned to this task */
|
|
84
|
+
assignee?: User;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Interface for task storage providers.
|
|
89
|
+
* Implementations handle reading and writing tasks from different sources
|
|
90
|
+
* (e.g., file system, database, API).
|
|
91
|
+
* @public
|
|
92
|
+
*/
|
|
93
|
+
export interface ITaskProvider {
|
|
94
|
+
/**
|
|
95
|
+
* Find a specific task by its ID.
|
|
96
|
+
* @param taskId - The unique identifier of the task
|
|
97
|
+
* @returns The task if found, undefined otherwise
|
|
98
|
+
*/
|
|
99
|
+
findTask(taskId: string): Promise<TaskFile | undefined>;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Retrieve all tasks from the provider.
|
|
103
|
+
* @returns Array of all tasks
|
|
104
|
+
*/
|
|
105
|
+
getAllTasks(): Promise<TaskFile[]>;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Update an existing task.
|
|
109
|
+
* @param task - The task with updated information
|
|
110
|
+
*/
|
|
111
|
+
updateTask(task: TaskFile): Promise<void>;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Create a new task.
|
|
115
|
+
* @param options - Options for creating the task
|
|
116
|
+
* @returns The created task information
|
|
117
|
+
*/
|
|
118
|
+
createTask(options: CreateTaskOptions): Promise<CreateTaskResult>;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Validate all tasks managed by this provider.
|
|
122
|
+
* Each provider knows its own format and validation rules.
|
|
123
|
+
* @param fix - If true, attempt to automatically fix validation issues
|
|
124
|
+
* @returns The lint result with any validation issues found
|
|
125
|
+
*/
|
|
126
|
+
lint(fix?: boolean): Promise<LintResult>;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Interface for task management operations.
|
|
131
|
+
* Provides high-level methods for managing task workflow and state transitions.
|
|
132
|
+
* @public
|
|
133
|
+
*/
|
|
134
|
+
export interface ITaskManager {
|
|
135
|
+
/**
|
|
136
|
+
* Mark a task as finished.
|
|
137
|
+
* Transitions the task to 'done' status.
|
|
138
|
+
* @param taskId - The unique identifier of the task
|
|
139
|
+
* @returns The updated task
|
|
140
|
+
* @throws Error if task is not found
|
|
141
|
+
*/
|
|
142
|
+
finishTask(taskId: string): Promise<TaskFile>;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Start working on a task.
|
|
146
|
+
* Transitions the task to 'in-progress' status.
|
|
147
|
+
* @param taskId - The unique identifier of the task
|
|
148
|
+
* @returns The updated task
|
|
149
|
+
* @throws Error if task is not found, already in progress, or already done
|
|
150
|
+
*/
|
|
151
|
+
startTask(taskId: string): Promise<TaskFile>;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Create a new task.
|
|
155
|
+
* @param options - Options for creating the task
|
|
156
|
+
* @returns The created task information
|
|
157
|
+
*/
|
|
158
|
+
createTask(options: CreateTaskOptions): Promise<CreateTaskResult>;
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Validate all tasks in the system.
|
|
162
|
+
* Delegates to the underlying provider's lint implementation.
|
|
163
|
+
* @param fix - If true, attempt to automatically fix validation issues
|
|
164
|
+
* @returns The lint result with any validation issues found
|
|
165
|
+
*/
|
|
166
|
+
lint(fix?: boolean): Promise<LintResult>;
|
|
167
|
+
}
|
package/tsconfig.json
ADDED