claude-sessions 0.1.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.
Files changed (48) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +140 -0
  3. package/data/model-pricing.json +113 -0
  4. package/dist/cost-calculator.d.ts +49 -0
  5. package/dist/cost-calculator.d.ts.map +1 -0
  6. package/dist/cost-calculator.js +429 -0
  7. package/dist/cost-calculator.js.map +1 -0
  8. package/dist/index.d.ts +28 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.js +65 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/projects-service.d.ts +106 -0
  13. package/dist/projects-service.d.ts.map +1 -0
  14. package/dist/projects-service.js +317 -0
  15. package/dist/projects-service.js.map +1 -0
  16. package/dist/session-cache-store.d.ts +48 -0
  17. package/dist/session-cache-store.d.ts.map +1 -0
  18. package/dist/session-cache-store.js +231 -0
  19. package/dist/session-cache-store.js.map +1 -0
  20. package/dist/session-cache.d.ts +266 -0
  21. package/dist/session-cache.d.ts.map +1 -0
  22. package/dist/session-cache.js +1294 -0
  23. package/dist/session-cache.js.map +1 -0
  24. package/dist/session-parser.d.ts +265 -0
  25. package/dist/session-parser.d.ts.map +1 -0
  26. package/dist/session-parser.js +555 -0
  27. package/dist/session-parser.js.map +1 -0
  28. package/dist/session-reader.d.ts +87 -0
  29. package/dist/session-reader.d.ts.map +1 -0
  30. package/dist/session-reader.js +279 -0
  31. package/dist/session-reader.js.map +1 -0
  32. package/dist/tasks-service.d.ts +100 -0
  33. package/dist/tasks-service.d.ts.map +1 -0
  34. package/dist/tasks-service.js +290 -0
  35. package/dist/tasks-service.js.map +1 -0
  36. package/dist/teams-service.d.ts +30 -0
  37. package/dist/teams-service.d.ts.map +1 -0
  38. package/dist/teams-service.js +85 -0
  39. package/dist/teams-service.js.map +1 -0
  40. package/dist/types.d.ts +87 -0
  41. package/dist/types.d.ts.map +1 -0
  42. package/dist/types.js +7 -0
  43. package/dist/types.js.map +1 -0
  44. package/dist/utils/path-utils.d.ts +80 -0
  45. package/dist/utils/path-utils.d.ts.map +1 -0
  46. package/dist/utils/path-utils.js +355 -0
  47. package/dist/utils/path-utils.js.map +1 -0
  48. package/package.json +42 -0
@@ -0,0 +1,279 @@
1
+ "use strict";
2
+ /**
3
+ * Session Reader
4
+ *
5
+ * Read-only access to session files in ~/.claude/projects/.
6
+ * Provides low-level JSONL file discovery and reading.
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.SessionReader = void 0;
43
+ exports.createSessionReader = createSessionReader;
44
+ const fs = __importStar(require("fs"));
45
+ const path = __importStar(require("path"));
46
+ const os = __importStar(require("os"));
47
+ const readline = __importStar(require("readline"));
48
+ const path_utils_1 = require("./utils/path-utils");
49
+ // ============================================================================
50
+ // SessionReader Implementation
51
+ // ============================================================================
52
+ /**
53
+ * Read-only access to session files.
54
+ *
55
+ * Sessions are stored in ~/.claude/projects/{projectKey}/ as JSONL files.
56
+ * This class provides methods to:
57
+ * - List sessions and projects
58
+ * - Read session data (messages, tool uses, etc.)
59
+ * - Access conversation history
60
+ * - Read subagent session files
61
+ */
62
+ class SessionReader {
63
+ configDir;
64
+ defaultCwd;
65
+ constructor(config) {
66
+ this.configDir = config?.configDir || path.join(os.homedir(), '.claude');
67
+ this.defaultCwd = config?.defaultCwd;
68
+ }
69
+ getProjectsDir() {
70
+ return path.join(this.configDir, 'projects');
71
+ }
72
+ cwdToProjectKey(cwd) {
73
+ return cwd.replace(/[:\\/]/g, '-');
74
+ }
75
+ getProjectDir(cwd) {
76
+ const workingDir = cwd || this.defaultCwd || process.cwd();
77
+ const projectKey = this.cwdToProjectKey(workingDir);
78
+ return path.join(this.getProjectsDir(), projectKey);
79
+ }
80
+ getSessionFilePath(sessionId, cwd) {
81
+ return path.join(this.getProjectDir(cwd), `${sessionId}.jsonl`);
82
+ }
83
+ listSessions(cwd) {
84
+ const projectDir = this.getProjectDir(cwd);
85
+ if (!fs.existsSync(projectDir)) {
86
+ return [];
87
+ }
88
+ const sessions = [];
89
+ const files = fs.readdirSync(projectDir);
90
+ for (const file of files) {
91
+ if (!file.endsWith('.jsonl') || file.startsWith('agent-')) {
92
+ continue;
93
+ }
94
+ const sessionId = file.replace('.jsonl', '');
95
+ const filePath = path.join(projectDir, file);
96
+ const stats = fs.statSync(filePath);
97
+ sessions.push({
98
+ sessionId,
99
+ projectPath: cwd || this.defaultCwd || process.cwd(),
100
+ projectKey: this.cwdToProjectKey(cwd || this.defaultCwd || process.cwd()),
101
+ lastModified: stats.mtime,
102
+ sizeBytes: stats.size,
103
+ });
104
+ }
105
+ sessions.sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime());
106
+ return sessions;
107
+ }
108
+ listSessionsWithDetails(cwd) {
109
+ const summaries = this.listSessions(cwd);
110
+ const detailed = [];
111
+ for (const summary of summaries) {
112
+ const info = { ...summary };
113
+ try {
114
+ const filePath = this.getSessionFilePath(summary.sessionId, cwd);
115
+ const content = fs.readFileSync(filePath, 'utf-8');
116
+ const lines = content.trim().split('\n');
117
+ if (lines.length > 0) {
118
+ try {
119
+ const firstLine = JSON.parse(lines[0]);
120
+ if (firstLine.type === 'system' && firstLine.subtype === 'init') {
121
+ info.model = firstLine.model;
122
+ }
123
+ }
124
+ catch {
125
+ // Ignore parse errors
126
+ }
127
+ try {
128
+ const lastLine = JSON.parse(lines[lines.length - 1]);
129
+ if (lastLine.type === 'result') {
130
+ info.numTurns = lastLine.num_turns;
131
+ info.totalCostUsd = lastLine.total_cost_usd;
132
+ info.durationMs = lastLine.duration_ms;
133
+ info.status = lastLine.is_error ? 'error' : 'completed';
134
+ info.result = lastLine.result;
135
+ }
136
+ else {
137
+ info.status = 'active';
138
+ }
139
+ }
140
+ catch {
141
+ // Ignore parse errors
142
+ }
143
+ }
144
+ }
145
+ catch {
146
+ // Ignore read errors
147
+ }
148
+ detailed.push(info);
149
+ }
150
+ return detailed;
151
+ }
152
+ listProjects() {
153
+ const projectsDir = this.getProjectsDir();
154
+ if (!fs.existsSync(projectsDir)) {
155
+ return [];
156
+ }
157
+ const projects = [];
158
+ const entries = fs.readdirSync(projectsDir, { withFileTypes: true });
159
+ for (const entry of entries) {
160
+ if (!entry.isDirectory())
161
+ continue;
162
+ const projectDir = path.join(projectsDir, entry.name);
163
+ const files = fs.readdirSync(projectDir).filter(f => f.endsWith('.jsonl') && !f.startsWith('agent-'));
164
+ if (files.length === 0)
165
+ continue;
166
+ let lastActivity = new Date(0);
167
+ for (const file of files) {
168
+ const stats = fs.statSync(path.join(projectDir, file));
169
+ if (stats.mtime > lastActivity) {
170
+ lastActivity = stats.mtime;
171
+ }
172
+ }
173
+ projects.push({
174
+ key: entry.name,
175
+ path: (0, path_utils_1.decodePath)(entry.name),
176
+ sessionCount: files.length,
177
+ lastActivity,
178
+ });
179
+ }
180
+ projects.sort((a, b) => b.lastActivity.getTime() - a.lastActivity.getTime());
181
+ return projects;
182
+ }
183
+ sessionExists(sessionId, cwd) {
184
+ const filePath = this.getSessionFilePath(sessionId, cwd);
185
+ return fs.existsSync(filePath);
186
+ }
187
+ listSubagentFiles(sessionId, cwd) {
188
+ const projectDir = this.getProjectDir(cwd);
189
+ if (!fs.existsSync(projectDir)) {
190
+ return [];
191
+ }
192
+ const files = [];
193
+ const addAgentFile = (filePath, parentSessionId) => {
194
+ const fileName = path.basename(filePath);
195
+ const match = fileName.match(/^agent-(.+)\.jsonl$/);
196
+ if (!match)
197
+ return;
198
+ const agentId = match[1];
199
+ try {
200
+ const stats = fs.statSync(filePath);
201
+ files.push({
202
+ agentId,
203
+ sessionId: parentSessionId,
204
+ filePath,
205
+ lastModified: stats.mtime,
206
+ sizeBytes: stats.size,
207
+ });
208
+ }
209
+ catch {
210
+ // Ignore files we can't stat
211
+ }
212
+ };
213
+ const entries = fs.readdirSync(projectDir);
214
+ for (const file of entries) {
215
+ if (file.startsWith('agent-') && file.endsWith('.jsonl')) {
216
+ addAgentFile(path.join(projectDir, file), sessionId || 'unknown');
217
+ }
218
+ }
219
+ for (const entry of entries) {
220
+ const entryPath = path.join(projectDir, entry);
221
+ const subagentsDir = path.join(entryPath, 'subagents');
222
+ if (sessionId && entry !== sessionId)
223
+ continue;
224
+ if (fs.existsSync(subagentsDir) && fs.statSync(subagentsDir).isDirectory()) {
225
+ const subagentFiles = fs.readdirSync(subagentsDir);
226
+ for (const file of subagentFiles) {
227
+ if (file.startsWith('agent-') && file.endsWith('.jsonl')) {
228
+ addAgentFile(path.join(subagentsDir, file), entry);
229
+ }
230
+ }
231
+ }
232
+ }
233
+ files.sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime());
234
+ return files;
235
+ }
236
+ async readSessionLines(sessionId, cwd) {
237
+ const filePath = this.getSessionFilePath(sessionId, cwd);
238
+ if (!fs.existsSync(filePath)) {
239
+ return [];
240
+ }
241
+ const lines = [];
242
+ const fileStream = fs.createReadStream(filePath);
243
+ const rl = readline.createInterface({
244
+ input: fileStream,
245
+ crlfDelay: Infinity,
246
+ });
247
+ for await (const line of rl) {
248
+ if (line.trim()) {
249
+ lines.push(line);
250
+ }
251
+ }
252
+ return lines;
253
+ }
254
+ async readSessionLinesFrom(sessionId, fromLineIndex, cwd, limit) {
255
+ const allLines = await this.readSessionLines(sessionId, cwd);
256
+ const totalLines = allLines.length;
257
+ let lines = allLines.slice(fromLineIndex);
258
+ if (limit && limit > 0) {
259
+ lines = lines.slice(0, limit);
260
+ }
261
+ return { lines, totalLines };
262
+ }
263
+ parseJsonlLine(line) {
264
+ try {
265
+ return JSON.parse(line);
266
+ }
267
+ catch {
268
+ return null;
269
+ }
270
+ }
271
+ }
272
+ exports.SessionReader = SessionReader;
273
+ // ============================================================================
274
+ // Factory Function
275
+ // ============================================================================
276
+ function createSessionReader(config) {
277
+ return new SessionReader(config);
278
+ }
279
+ //# sourceMappingURL=session-reader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-reader.js","sourceRoot":"","sources":["../src/session-reader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Claude Code Tasks Service
3
+ *
4
+ * Reads Claude Code task files from ~/.claude/tasks/
5
+ * Each task list is a directory containing individual task JSON files.
6
+ */
7
+ export interface Task {
8
+ id: string;
9
+ subject: string;
10
+ description: string;
11
+ activeForm?: string;
12
+ status: 'pending' | 'in_progress' | 'completed' | 'deleted';
13
+ blocks: string[];
14
+ blockedBy: string[];
15
+ owner?: string;
16
+ metadata?: Record<string, unknown>;
17
+ }
18
+ export interface SessionInfo {
19
+ sessionId: string;
20
+ projectPath: string;
21
+ projectKey: string;
22
+ filePath: string;
23
+ exists: boolean;
24
+ }
25
+ export interface TaskList {
26
+ listId: string;
27
+ tasks: Task[];
28
+ taskCount: number;
29
+ path: string;
30
+ sessionInfo?: SessionInfo;
31
+ }
32
+ export interface TaskListSummary {
33
+ listId: string;
34
+ taskCount: number;
35
+ pendingCount: number;
36
+ inProgressCount: number;
37
+ completedCount: number;
38
+ lastModified: Date;
39
+ sessionInfo?: SessionInfo;
40
+ }
41
+ export interface CreateTaskInput {
42
+ subject: string;
43
+ description?: string;
44
+ activeForm?: string;
45
+ status?: 'pending' | 'in_progress' | 'completed';
46
+ blocks?: string[];
47
+ blockedBy?: string[];
48
+ owner?: string;
49
+ metadata?: Record<string, unknown>;
50
+ }
51
+ export interface UpdateTaskInput {
52
+ subject?: string;
53
+ description?: string;
54
+ activeForm?: string;
55
+ status?: 'pending' | 'in_progress' | 'completed';
56
+ blocks?: string[];
57
+ blockedBy?: string[];
58
+ addBlocks?: string[];
59
+ addBlockedBy?: string[];
60
+ owner?: string;
61
+ metadata?: Record<string, unknown>;
62
+ }
63
+ export declare class TasksService {
64
+ private tasksDir;
65
+ private _sessionInfoCache;
66
+ private _taskListCache;
67
+ private _taskListCacheDirty;
68
+ constructor(tasksDir?: string);
69
+ invalidateCache(): void;
70
+ invalidateSessionInfoCache(): void;
71
+ getTasksDir(): string;
72
+ isSessionIdFormat(id: string): boolean;
73
+ findSessionById(sessionId: string): SessionInfo | null;
74
+ getSessionInfo(listId: string): SessionInfo | null;
75
+ listTaskLists(): Promise<TaskListSummary[]>;
76
+ encodeProjectPath(projectPath: string): string;
77
+ getTaskListsForProject(projectPath: string): Promise<TaskListSummary[]>;
78
+ getTaskList(listId: string): Promise<TaskList | null>;
79
+ getTask(listId: string, taskId: string): Promise<Task | null>;
80
+ getReadyTasks(listId: string): Promise<Task[]>;
81
+ getAllTasksFlat(): Promise<Array<Task & {
82
+ sessionId: string;
83
+ projectPath?: string;
84
+ projectName?: string;
85
+ }>>;
86
+ getDependencyGraph(listId: string): Promise<{
87
+ nodes: Array<{
88
+ id: string;
89
+ subject: string;
90
+ status: string;
91
+ }>;
92
+ edges: Array<{
93
+ from: string;
94
+ to: string;
95
+ }>;
96
+ }>;
97
+ private readTasksFromDir;
98
+ }
99
+ export declare function createTasksService(tasksDir?: string): TasksService;
100
+ //# sourceMappingURL=tasks-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tasks-service.d.ts","sourceRoot":"","sources":["../src/tasks-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,290 @@
1
+ "use strict";
2
+ /**
3
+ * Claude Code Tasks Service
4
+ *
5
+ * Reads Claude Code task files from ~/.claude/tasks/
6
+ * Each task list is a directory containing individual task JSON files.
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.TasksService = void 0;
43
+ exports.createTasksService = createTasksService;
44
+ const fs = __importStar(require("fs"));
45
+ const path = __importStar(require("path"));
46
+ const os_1 = require("os");
47
+ const path_utils_1 = require("./utils/path-utils");
48
+ // ============================================================================
49
+ // Service Implementation
50
+ // ============================================================================
51
+ class TasksService {
52
+ tasksDir;
53
+ // Cache: sessionId → SessionInfo (avoids scanning 30 project dirs per lookup)
54
+ _sessionInfoCache = new Map();
55
+ // Cache: listTaskLists() result, invalidated on file changes
56
+ _taskListCache = null;
57
+ _taskListCacheDirty = true;
58
+ constructor(tasksDir) {
59
+ this.tasksDir = tasksDir || path.join((0, os_1.homedir)(), '.claude', 'tasks');
60
+ }
61
+ invalidateCache() {
62
+ this._taskListCache = null;
63
+ this._taskListCacheDirty = true;
64
+ }
65
+ invalidateSessionInfoCache() {
66
+ this._sessionInfoCache.clear();
67
+ }
68
+ getTasksDir() {
69
+ return this.tasksDir;
70
+ }
71
+ isSessionIdFormat(id) {
72
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
73
+ return uuidRegex.test(id);
74
+ }
75
+ findSessionById(sessionId) {
76
+ if (!this.isSessionIdFormat(sessionId)) {
77
+ return null;
78
+ }
79
+ if (this._sessionInfoCache.has(sessionId)) {
80
+ return this._sessionInfoCache.get(sessionId) || null;
81
+ }
82
+ const projectsDir = path.join((0, os_1.homedir)(), '.claude', 'projects');
83
+ if (!fs.existsSync(projectsDir)) {
84
+ this._sessionInfoCache.set(sessionId, null);
85
+ return null;
86
+ }
87
+ try {
88
+ const dirs = fs.readdirSync(projectsDir, { withFileTypes: true })
89
+ .filter(d => d.isDirectory());
90
+ for (const dir of dirs) {
91
+ const sessionFile = path.join(projectsDir, dir.name, `${sessionId}.jsonl`);
92
+ if (fs.existsSync(sessionFile)) {
93
+ const projectPath = '/' + dir.name.replace(/^-/, '').replace(/-/g, '/');
94
+ const info = {
95
+ sessionId,
96
+ projectPath,
97
+ projectKey: dir.name,
98
+ filePath: sessionFile,
99
+ exists: true,
100
+ };
101
+ this._sessionInfoCache.set(sessionId, info);
102
+ return info;
103
+ }
104
+ }
105
+ }
106
+ catch {
107
+ // Ignore errors
108
+ }
109
+ this._sessionInfoCache.set(sessionId, null);
110
+ return null;
111
+ }
112
+ getSessionInfo(listId) {
113
+ return this.findSessionById(listId);
114
+ }
115
+ async listTaskLists() {
116
+ if (!this._taskListCacheDirty && this._taskListCache) {
117
+ return this._taskListCache;
118
+ }
119
+ if (!fs.existsSync(this.tasksDir)) {
120
+ return [];
121
+ }
122
+ const entries = fs.readdirSync(this.tasksDir, { withFileTypes: true });
123
+ const summaries = [];
124
+ for (const entry of entries) {
125
+ if (entry.isDirectory()) {
126
+ const listPath = path.join(this.tasksDir, entry.name);
127
+ const tasks = await this.readTasksFromDir(listPath);
128
+ const stat = fs.statSync(listPath);
129
+ const sessionInfo = this.getSessionInfo(entry.name);
130
+ summaries.push({
131
+ listId: entry.name,
132
+ taskCount: tasks.length,
133
+ pendingCount: tasks.filter(t => t.status === 'pending').length,
134
+ inProgressCount: tasks.filter(t => t.status === 'in_progress').length,
135
+ completedCount: tasks.filter(t => t.status === 'completed').length,
136
+ lastModified: stat.mtime,
137
+ sessionInfo: sessionInfo || undefined,
138
+ });
139
+ }
140
+ }
141
+ const result = summaries.sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime());
142
+ this._taskListCache = result;
143
+ this._taskListCacheDirty = false;
144
+ return result;
145
+ }
146
+ encodeProjectPath(projectPath) {
147
+ const normalized = projectPath.replace(/[\\/]+$/, '');
148
+ return (0, path_utils_1.legacyEncodeProjectPath)(normalized);
149
+ }
150
+ async getTaskListsForProject(projectPath) {
151
+ const allLists = await this.listTaskLists();
152
+ const targetProjectKey = this.encodeProjectPath(projectPath);
153
+ return allLists.filter(list => {
154
+ if (!list.sessionInfo)
155
+ return false;
156
+ return list.sessionInfo.projectKey === targetProjectKey;
157
+ });
158
+ }
159
+ async getTaskList(listId) {
160
+ const listPath = path.join(this.tasksDir, listId);
161
+ if (!fs.existsSync(listPath)) {
162
+ return null;
163
+ }
164
+ const tasks = await this.readTasksFromDir(listPath);
165
+ const sessionInfo = this.getSessionInfo(listId);
166
+ return {
167
+ listId,
168
+ tasks,
169
+ taskCount: tasks.length,
170
+ path: listPath,
171
+ sessionInfo: sessionInfo || undefined,
172
+ };
173
+ }
174
+ async getTask(listId, taskId) {
175
+ const taskPath = path.join(this.tasksDir, listId, `${taskId}.json`);
176
+ if (fs.existsSync(taskPath)) {
177
+ try {
178
+ const content = fs.readFileSync(taskPath, 'utf-8');
179
+ return JSON.parse(content);
180
+ }
181
+ catch {
182
+ // Fall through
183
+ }
184
+ }
185
+ const taskList = await this.getTaskList(listId);
186
+ return taskList?.tasks.find(t => t.id === taskId) || null;
187
+ }
188
+ async getReadyTasks(listId) {
189
+ const taskList = await this.getTaskList(listId);
190
+ if (!taskList)
191
+ return [];
192
+ const completedIds = new Set(taskList.tasks
193
+ .filter(t => t.status === 'completed')
194
+ .map(t => t.id));
195
+ return taskList.tasks.filter(task => {
196
+ if (task.status === 'completed')
197
+ return false;
198
+ return task.blockedBy.every(id => completedIds.has(id));
199
+ });
200
+ }
201
+ async getAllTasksFlat() {
202
+ if (!fs.existsSync(this.tasksDir)) {
203
+ return [];
204
+ }
205
+ const entries = fs.readdirSync(this.tasksDir, { withFileTypes: true });
206
+ const allTasks = [];
207
+ for (const entry of entries) {
208
+ if (!entry.isDirectory())
209
+ continue;
210
+ const listId = entry.name;
211
+ const listPath = path.join(this.tasksDir, listId);
212
+ const rawTasks = await this.readTasksFromDir(listPath);
213
+ const tasks = rawTasks.filter(t => t.status !== 'deleted');
214
+ if (tasks.length === 0)
215
+ continue;
216
+ const sessionInfo = this.getSessionInfo(listId);
217
+ const projectPath = sessionInfo?.projectPath;
218
+ const projectName = projectPath ? path.basename(projectPath) : undefined;
219
+ for (const task of tasks) {
220
+ allTasks.push({
221
+ ...task,
222
+ sessionId: listId,
223
+ projectPath,
224
+ projectName,
225
+ });
226
+ }
227
+ }
228
+ return allTasks;
229
+ }
230
+ async getDependencyGraph(listId) {
231
+ const taskList = await this.getTaskList(listId);
232
+ if (!taskList) {
233
+ return { nodes: [], edges: [] };
234
+ }
235
+ const nodes = taskList.tasks.map(t => ({
236
+ id: t.id,
237
+ subject: t.subject,
238
+ status: t.status,
239
+ }));
240
+ const edges = [];
241
+ const edgeSet = new Set();
242
+ for (const task of taskList.tasks) {
243
+ for (const blockedId of task.blocks) {
244
+ const key = `${task.id}->${blockedId}`;
245
+ if (!edgeSet.has(key)) {
246
+ edgeSet.add(key);
247
+ edges.push({ from: task.id, to: blockedId });
248
+ }
249
+ }
250
+ for (const blockerId of task.blockedBy) {
251
+ const key = `${blockerId}->${task.id}`;
252
+ if (!edgeSet.has(key)) {
253
+ edgeSet.add(key);
254
+ edges.push({ from: blockerId, to: task.id });
255
+ }
256
+ }
257
+ }
258
+ return { nodes, edges };
259
+ }
260
+ async readTasksFromDir(dirPath) {
261
+ if (!fs.existsSync(dirPath)) {
262
+ return [];
263
+ }
264
+ const files = fs.readdirSync(dirPath).filter(f => f.endsWith('.json'));
265
+ const tasks = [];
266
+ for (const file of files) {
267
+ try {
268
+ const content = fs.readFileSync(path.join(dirPath, file), 'utf-8');
269
+ const task = JSON.parse(content);
270
+ tasks.push(task);
271
+ }
272
+ catch {
273
+ // Skip invalid files
274
+ }
275
+ }
276
+ return tasks.sort((a, b) => {
277
+ const aNum = parseInt(a.id, 10);
278
+ const bNum = parseInt(b.id, 10);
279
+ if (isNaN(aNum) || isNaN(bNum)) {
280
+ return a.id.localeCompare(b.id);
281
+ }
282
+ return aNum - bNum;
283
+ });
284
+ }
285
+ }
286
+ exports.TasksService = TasksService;
287
+ function createTasksService(tasksDir) {
288
+ return new TasksService(tasksDir);
289
+ }
290
+ //# sourceMappingURL=tasks-service.js.map