@vibetasks/core 0.2.0 → 0.3.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/dist/index.d.ts +125 -5
- package/dist/index.js +257 -4
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -125,12 +125,13 @@ interface Tag {
|
|
|
125
125
|
created_at?: string;
|
|
126
126
|
}
|
|
127
127
|
/**
|
|
128
|
-
* Task status - simple 3-state flow for VibeTasks
|
|
128
|
+
* Task status - simple 3-state flow + archive for VibeTasks
|
|
129
129
|
* - todo: Not started yet, waiting in backlog
|
|
130
130
|
* - vibing: Currently in progress (actively working on it)
|
|
131
131
|
* - done: Completed
|
|
132
|
+
* - archived: Archived (hidden from default views)
|
|
132
133
|
*/
|
|
133
|
-
type TaskStatus = 'todo' | 'vibing' | 'done';
|
|
134
|
+
type TaskStatus = 'todo' | 'vibing' | 'done' | 'archived';
|
|
134
135
|
interface Task {
|
|
135
136
|
id: string;
|
|
136
137
|
user_id?: string;
|
|
@@ -139,6 +140,7 @@ interface Task {
|
|
|
139
140
|
notes_format?: 'html' | 'markdown';
|
|
140
141
|
completed?: boolean;
|
|
141
142
|
completed_at?: string;
|
|
143
|
+
archived_at?: string;
|
|
142
144
|
due_date?: string;
|
|
143
145
|
start_date?: string;
|
|
144
146
|
duration_minutes?: number;
|
|
@@ -170,22 +172,140 @@ declare class TaskOperations {
|
|
|
170
172
|
* Create TaskOperations from AuthManager
|
|
171
173
|
*/
|
|
172
174
|
static fromAuthManager(authManager: AuthManager): Promise<TaskOperations>;
|
|
173
|
-
getTasks(filter?: TaskFilter): Promise<Task[]>;
|
|
175
|
+
getTasks(filter?: TaskFilter, includeArchived?: boolean): Promise<Task[]>;
|
|
174
176
|
getTask(taskId: string): Promise<Task>;
|
|
175
177
|
createTask(task: Partial<Task>): Promise<Task>;
|
|
176
178
|
updateTask(taskId: string, updates: Partial<Task>): Promise<Task>;
|
|
177
179
|
deleteTask(taskId: string): Promise<void>;
|
|
178
180
|
completeTask(taskId: string): Promise<Task>;
|
|
179
181
|
uncompleteTask(taskId: string): Promise<Task>;
|
|
182
|
+
/**
|
|
183
|
+
* Archive a task - moves it to archived status
|
|
184
|
+
* Typically used for completed tasks you want to hide from views
|
|
185
|
+
*/
|
|
186
|
+
archiveTask(taskId: string): Promise<Task>;
|
|
187
|
+
/**
|
|
188
|
+
* Unarchive a task - restores it to its previous state (done by default)
|
|
189
|
+
*/
|
|
190
|
+
unarchiveTask(taskId: string, restoreStatus?: TaskStatus): Promise<Task>;
|
|
191
|
+
/**
|
|
192
|
+
* Get all archived tasks
|
|
193
|
+
*/
|
|
194
|
+
getArchivedTasks(limit?: number): Promise<Task[]>;
|
|
180
195
|
getTags(): Promise<Tag[]>;
|
|
181
196
|
createTag(name: string, color?: string): Promise<Tag>;
|
|
182
197
|
linkTaskTags(taskId: string, tagIds: string[]): Promise<void>;
|
|
183
198
|
unlinkAllTaskTags(taskId: string): Promise<void>;
|
|
184
|
-
searchTasks(query: string, limit?: number): Promise<Task[]>;
|
|
199
|
+
searchTasks(query: string, limit?: number, includeArchived?: boolean): Promise<Task[]>;
|
|
185
200
|
/**
|
|
186
201
|
* Find tag by name (case-insensitive), create if doesn't exist
|
|
187
202
|
*/
|
|
188
203
|
findOrCreateTag(name: string, color?: string): Promise<Tag>;
|
|
189
204
|
}
|
|
190
205
|
|
|
191
|
-
|
|
206
|
+
/**
|
|
207
|
+
* Claude Code TodoWrite Sync
|
|
208
|
+
*
|
|
209
|
+
* This module provides synchronization between Claude's TodoWrite state
|
|
210
|
+
* and VibeTasks task statuses.
|
|
211
|
+
*
|
|
212
|
+
* Status mapping:
|
|
213
|
+
* Claude "pending" -> VibeTasks "todo"
|
|
214
|
+
* Claude "in_progress" -> VibeTasks "vibing"
|
|
215
|
+
* Claude "completed" -> VibeTasks "done"
|
|
216
|
+
*/
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Claude Code TodoWrite item structure
|
|
220
|
+
*/
|
|
221
|
+
interface ClaudeTodoItem {
|
|
222
|
+
/** The task description/content */
|
|
223
|
+
content: string;
|
|
224
|
+
/** Current status: pending, in_progress, or completed */
|
|
225
|
+
status: 'pending' | 'in_progress' | 'completed';
|
|
226
|
+
/** Active form description (used during in_progress) */
|
|
227
|
+
activeForm?: string;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Result of syncing a single todo item
|
|
231
|
+
*/
|
|
232
|
+
interface SyncResult {
|
|
233
|
+
/** The Claude todo content */
|
|
234
|
+
content: string;
|
|
235
|
+
/** Whether a matching VibeTasks task was found */
|
|
236
|
+
matched: boolean;
|
|
237
|
+
/** The VibeTasks task ID if matched */
|
|
238
|
+
taskId?: string;
|
|
239
|
+
/** The action taken */
|
|
240
|
+
action: 'created' | 'updated' | 'skipped' | 'not_found';
|
|
241
|
+
/** Previous status (if updated) */
|
|
242
|
+
previousStatus?: TaskStatus;
|
|
243
|
+
/** New status (if updated) */
|
|
244
|
+
newStatus?: TaskStatus;
|
|
245
|
+
/** Error message if any */
|
|
246
|
+
error?: string;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Result of syncing all todos
|
|
250
|
+
*/
|
|
251
|
+
interface SyncAllResult {
|
|
252
|
+
/** Whether the overall sync was successful */
|
|
253
|
+
success: boolean;
|
|
254
|
+
/** Number of todos processed */
|
|
255
|
+
processed: number;
|
|
256
|
+
/** Number of todos synced successfully */
|
|
257
|
+
synced: number;
|
|
258
|
+
/** Number of todos that failed to sync */
|
|
259
|
+
failed: number;
|
|
260
|
+
/** Individual sync results */
|
|
261
|
+
results: SyncResult[];
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Options for the sync operation
|
|
265
|
+
*/
|
|
266
|
+
interface SyncOptions {
|
|
267
|
+
/** Create new tasks for unmatched todos (default: false) */
|
|
268
|
+
createMissing?: boolean;
|
|
269
|
+
/** Project tag to use for created tasks */
|
|
270
|
+
projectTag?: string;
|
|
271
|
+
/** Minimum similarity score for matching (0-1, default: 0.6) */
|
|
272
|
+
matchThreshold?: number;
|
|
273
|
+
/** Whether to sync 'pending' todos as 'todo' status (default: true) */
|
|
274
|
+
syncPending?: boolean;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Claude Sync class for managing TodoWrite synchronization
|
|
278
|
+
*/
|
|
279
|
+
declare class ClaudeSync {
|
|
280
|
+
private taskOps;
|
|
281
|
+
constructor(taskOps: TaskOperations);
|
|
282
|
+
/**
|
|
283
|
+
* Sync a single Claude todo item with VibeTasks
|
|
284
|
+
*/
|
|
285
|
+
syncTodo(todo: ClaudeTodoItem, existingTasks?: Task[], options?: SyncOptions): Promise<SyncResult>;
|
|
286
|
+
/**
|
|
287
|
+
* Sync all Claude todos with VibeTasks
|
|
288
|
+
*/
|
|
289
|
+
syncAllTodos(todos: ClaudeTodoItem[], options?: SyncOptions): Promise<SyncAllResult>;
|
|
290
|
+
/**
|
|
291
|
+
* Get current vibing tasks (for comparison with Claude's in_progress)
|
|
292
|
+
*/
|
|
293
|
+
getVibingTasks(): Promise<Task[]>;
|
|
294
|
+
/**
|
|
295
|
+
* Auto-start vibing on a task when Claude marks it as in_progress
|
|
296
|
+
*/
|
|
297
|
+
startVibing(taskId: string): Promise<Task>;
|
|
298
|
+
/**
|
|
299
|
+
* Complete a task when Claude marks it as completed
|
|
300
|
+
*/
|
|
301
|
+
completeTask(taskId: string): Promise<Task>;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* MCP Tool: sync_claude_todos
|
|
305
|
+
*
|
|
306
|
+
* This function is designed to be called as an MCP tool.
|
|
307
|
+
* It accepts Claude's current todo list and syncs it with VibeTasks.
|
|
308
|
+
*/
|
|
309
|
+
declare function syncClaudeTodos(taskOps: TaskOperations, todos: ClaudeTodoItem[], options?: SyncOptions): Promise<SyncAllResult>;
|
|
310
|
+
|
|
311
|
+
export { AuthManager, ClaudeSync, type ClaudeTodoItem, ConfigManager, type SupabaseConfig, type SyncAllResult, type SyncOptions, type SyncResult, type Tag, type Task, type TaskFilter, type TaskFlowConfig, TaskOperations, type TaskPriority, type TaskStatus, createSupabaseClient, createSupabaseClientFromEnv, syncClaudeTodos };
|
package/dist/index.js
CHANGED
|
@@ -287,11 +287,14 @@ var TaskOperations = class _TaskOperations {
|
|
|
287
287
|
// ============================================
|
|
288
288
|
// TASK CRUD OPERATIONS
|
|
289
289
|
// ============================================
|
|
290
|
-
async getTasks(filter = "all") {
|
|
290
|
+
async getTasks(filter = "all", includeArchived = false) {
|
|
291
291
|
let query = this.supabase.from("tasks").select(`
|
|
292
292
|
*,
|
|
293
293
|
tags:task_tags(tag:tags(*))
|
|
294
294
|
`).is("parent_task_id", null).order("position", { ascending: true });
|
|
295
|
+
if (!includeArchived) {
|
|
296
|
+
query = query.neq("status", "archived");
|
|
297
|
+
}
|
|
295
298
|
if (filter === "today") {
|
|
296
299
|
const today = /* @__PURE__ */ new Date();
|
|
297
300
|
today.setHours(0, 0, 0, 0);
|
|
@@ -409,6 +412,46 @@ var TaskOperations = class _TaskOperations {
|
|
|
409
412
|
return data;
|
|
410
413
|
}
|
|
411
414
|
// ============================================
|
|
415
|
+
// ARCHIVE OPERATIONS
|
|
416
|
+
// ============================================
|
|
417
|
+
/**
|
|
418
|
+
* Archive a task - moves it to archived status
|
|
419
|
+
* Typically used for completed tasks you want to hide from views
|
|
420
|
+
*/
|
|
421
|
+
async archiveTask(taskId) {
|
|
422
|
+
const { data, error } = await this.supabase.from("tasks").update({
|
|
423
|
+
status: "archived",
|
|
424
|
+
archived_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
425
|
+
}).eq("id", taskId).select().single();
|
|
426
|
+
if (error) throw error;
|
|
427
|
+
return data;
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Unarchive a task - restores it to its previous state (done by default)
|
|
431
|
+
*/
|
|
432
|
+
async unarchiveTask(taskId, restoreStatus = "done") {
|
|
433
|
+
const { data, error } = await this.supabase.from("tasks").update({
|
|
434
|
+
status: restoreStatus,
|
|
435
|
+
archived_at: null
|
|
436
|
+
}).eq("id", taskId).select().single();
|
|
437
|
+
if (error) throw error;
|
|
438
|
+
return data;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Get all archived tasks
|
|
442
|
+
*/
|
|
443
|
+
async getArchivedTasks(limit = 50) {
|
|
444
|
+
const { data, error } = await this.supabase.from("tasks").select(`
|
|
445
|
+
*,
|
|
446
|
+
tags:task_tags(tag:tags(*))
|
|
447
|
+
`).eq("status", "archived").is("parent_task_id", null).order("archived_at", { ascending: false }).limit(limit);
|
|
448
|
+
if (error) throw error;
|
|
449
|
+
return (data || []).map((task) => ({
|
|
450
|
+
...task,
|
|
451
|
+
tags: task.tags?.map((t) => t.tag).filter(Boolean) || []
|
|
452
|
+
}));
|
|
453
|
+
}
|
|
454
|
+
// ============================================
|
|
412
455
|
// TAG OPERATIONS
|
|
413
456
|
// ============================================
|
|
414
457
|
async getTags() {
|
|
@@ -435,11 +478,15 @@ var TaskOperations = class _TaskOperations {
|
|
|
435
478
|
// ============================================
|
|
436
479
|
// SEARCH & UTILITY
|
|
437
480
|
// ============================================
|
|
438
|
-
async searchTasks(query, limit = 20) {
|
|
439
|
-
|
|
481
|
+
async searchTasks(query, limit = 20, includeArchived = false) {
|
|
482
|
+
let dbQuery = this.supabase.from("tasks").select(`
|
|
440
483
|
*,
|
|
441
484
|
tags:task_tags(tag:tags(*))
|
|
442
485
|
`).ilike("title", `%${query}%`).is("parent_task_id", null).eq("completed", false).order("position", { ascending: true }).limit(limit);
|
|
486
|
+
if (!includeArchived) {
|
|
487
|
+
dbQuery = dbQuery.neq("status", "archived");
|
|
488
|
+
}
|
|
489
|
+
const { data, error } = await dbQuery;
|
|
443
490
|
if (error) throw error;
|
|
444
491
|
return (data || []).map((task) => ({
|
|
445
492
|
...task,
|
|
@@ -458,10 +505,216 @@ var TaskOperations = class _TaskOperations {
|
|
|
458
505
|
return await this.createTag(name, color);
|
|
459
506
|
}
|
|
460
507
|
};
|
|
508
|
+
|
|
509
|
+
// src/claude-sync.ts
|
|
510
|
+
function mapStatus(claudeStatus) {
|
|
511
|
+
switch (claudeStatus) {
|
|
512
|
+
case "pending":
|
|
513
|
+
return "todo";
|
|
514
|
+
case "in_progress":
|
|
515
|
+
return "vibing";
|
|
516
|
+
case "completed":
|
|
517
|
+
return "done";
|
|
518
|
+
default:
|
|
519
|
+
return "todo";
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
function calculateSimilarity(str1, str2) {
|
|
523
|
+
const s1 = str1.toLowerCase().trim();
|
|
524
|
+
const s2 = str2.toLowerCase().trim();
|
|
525
|
+
if (s1 === s2) return 1;
|
|
526
|
+
if (s1.length === 0 || s2.length === 0) return 0;
|
|
527
|
+
if (s1.includes(s2) || s2.includes(s1)) {
|
|
528
|
+
return 0.8;
|
|
529
|
+
}
|
|
530
|
+
const words1 = new Set(s1.split(/\s+/));
|
|
531
|
+
const words2 = new Set(s2.split(/\s+/));
|
|
532
|
+
let commonWords = 0;
|
|
533
|
+
for (const word of words1) {
|
|
534
|
+
if (words2.has(word)) {
|
|
535
|
+
commonWords++;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
const totalWords = Math.max(words1.size, words2.size);
|
|
539
|
+
if (totalWords === 0) return 0;
|
|
540
|
+
return commonWords / totalWords;
|
|
541
|
+
}
|
|
542
|
+
function findBestMatch(todoContent, tasks, threshold = 0.6) {
|
|
543
|
+
let bestMatch = null;
|
|
544
|
+
for (const task of tasks) {
|
|
545
|
+
const similarity = calculateSimilarity(todoContent, task.title);
|
|
546
|
+
if (similarity >= threshold) {
|
|
547
|
+
if (!bestMatch || similarity > bestMatch.similarity) {
|
|
548
|
+
bestMatch = { task, similarity };
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
if (task.context_notes) {
|
|
552
|
+
const noteSimilarity = calculateSimilarity(todoContent, task.context_notes);
|
|
553
|
+
if (noteSimilarity >= threshold && noteSimilarity > (bestMatch?.similarity || 0)) {
|
|
554
|
+
bestMatch = { task, similarity: noteSimilarity };
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
return bestMatch;
|
|
559
|
+
}
|
|
560
|
+
var ClaudeSync = class {
|
|
561
|
+
taskOps;
|
|
562
|
+
constructor(taskOps) {
|
|
563
|
+
this.taskOps = taskOps;
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Sync a single Claude todo item with VibeTasks
|
|
567
|
+
*/
|
|
568
|
+
async syncTodo(todo, existingTasks, options = {}) {
|
|
569
|
+
const {
|
|
570
|
+
createMissing = false,
|
|
571
|
+
projectTag,
|
|
572
|
+
matchThreshold = 0.6,
|
|
573
|
+
syncPending = true
|
|
574
|
+
} = options;
|
|
575
|
+
try {
|
|
576
|
+
if (todo.status === "pending" && !syncPending) {
|
|
577
|
+
return {
|
|
578
|
+
content: todo.content,
|
|
579
|
+
matched: false,
|
|
580
|
+
action: "skipped"
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
const tasks = existingTasks || await this.taskOps.getTasks("all");
|
|
584
|
+
const match = findBestMatch(todo.content, tasks, matchThreshold);
|
|
585
|
+
if (match) {
|
|
586
|
+
const vibeStatus = mapStatus(todo.status);
|
|
587
|
+
const currentStatus = match.task.status || "todo";
|
|
588
|
+
if (currentStatus !== vibeStatus) {
|
|
589
|
+
if (vibeStatus === "done" && !match.task.completed) {
|
|
590
|
+
await this.taskOps.completeTask(match.task.id);
|
|
591
|
+
} else {
|
|
592
|
+
await this.taskOps.updateTask(match.task.id, {
|
|
593
|
+
status: vibeStatus,
|
|
594
|
+
completed: vibeStatus === "done"
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
return {
|
|
598
|
+
content: todo.content,
|
|
599
|
+
matched: true,
|
|
600
|
+
taskId: match.task.id,
|
|
601
|
+
action: "updated",
|
|
602
|
+
previousStatus: currentStatus,
|
|
603
|
+
newStatus: vibeStatus
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
return {
|
|
607
|
+
content: todo.content,
|
|
608
|
+
matched: true,
|
|
609
|
+
taskId: match.task.id,
|
|
610
|
+
action: "skipped"
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
if (createMissing) {
|
|
614
|
+
const vibeStatus = mapStatus(todo.status);
|
|
615
|
+
const newTask = await this.taskOps.createTask({
|
|
616
|
+
title: todo.content,
|
|
617
|
+
status: vibeStatus,
|
|
618
|
+
project_tag: projectTag,
|
|
619
|
+
created_by: "ai",
|
|
620
|
+
context_notes: todo.activeForm ? `Started: ${todo.activeForm}` : void 0,
|
|
621
|
+
completed: vibeStatus === "done"
|
|
622
|
+
});
|
|
623
|
+
return {
|
|
624
|
+
content: todo.content,
|
|
625
|
+
matched: false,
|
|
626
|
+
taskId: newTask.id,
|
|
627
|
+
action: "created",
|
|
628
|
+
newStatus: vibeStatus
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
return {
|
|
632
|
+
content: todo.content,
|
|
633
|
+
matched: false,
|
|
634
|
+
action: "not_found"
|
|
635
|
+
};
|
|
636
|
+
} catch (error) {
|
|
637
|
+
return {
|
|
638
|
+
content: todo.content,
|
|
639
|
+
matched: false,
|
|
640
|
+
action: "skipped",
|
|
641
|
+
error: error.message
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Sync all Claude todos with VibeTasks
|
|
647
|
+
*/
|
|
648
|
+
async syncAllTodos(todos, options = {}) {
|
|
649
|
+
const results = [];
|
|
650
|
+
let synced = 0;
|
|
651
|
+
let failed = 0;
|
|
652
|
+
try {
|
|
653
|
+
const tasks = await this.taskOps.getTasks("all");
|
|
654
|
+
for (const todo of todos) {
|
|
655
|
+
const result = await this.syncTodo(todo, tasks, options);
|
|
656
|
+
results.push(result);
|
|
657
|
+
if (result.action === "updated" || result.action === "created") {
|
|
658
|
+
synced++;
|
|
659
|
+
} else if (result.error) {
|
|
660
|
+
failed++;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return {
|
|
664
|
+
success: failed === 0,
|
|
665
|
+
processed: todos.length,
|
|
666
|
+
synced,
|
|
667
|
+
failed,
|
|
668
|
+
results
|
|
669
|
+
};
|
|
670
|
+
} catch (error) {
|
|
671
|
+
return {
|
|
672
|
+
success: false,
|
|
673
|
+
processed: 0,
|
|
674
|
+
synced: 0,
|
|
675
|
+
failed: todos.length,
|
|
676
|
+
results: [{
|
|
677
|
+
content: "All todos",
|
|
678
|
+
matched: false,
|
|
679
|
+
action: "skipped",
|
|
680
|
+
error: error.message
|
|
681
|
+
}]
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Get current vibing tasks (for comparison with Claude's in_progress)
|
|
687
|
+
*/
|
|
688
|
+
async getVibingTasks() {
|
|
689
|
+
const allTasks = await this.taskOps.getTasks("all");
|
|
690
|
+
return allTasks.filter((t) => t.status === "vibing" && !t.completed);
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Auto-start vibing on a task when Claude marks it as in_progress
|
|
694
|
+
*/
|
|
695
|
+
async startVibing(taskId) {
|
|
696
|
+
return await this.taskOps.updateTask(taskId, {
|
|
697
|
+
status: "vibing",
|
|
698
|
+
completed: false
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* Complete a task when Claude marks it as completed
|
|
703
|
+
*/
|
|
704
|
+
async completeTask(taskId) {
|
|
705
|
+
return await this.taskOps.completeTask(taskId);
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
async function syncClaudeTodos(taskOps, todos, options = {}) {
|
|
709
|
+
const sync = new ClaudeSync(taskOps);
|
|
710
|
+
return await sync.syncAllTodos(todos, options);
|
|
711
|
+
}
|
|
461
712
|
export {
|
|
462
713
|
AuthManager,
|
|
714
|
+
ClaudeSync,
|
|
463
715
|
ConfigManager,
|
|
464
716
|
TaskOperations,
|
|
465
717
|
createSupabaseClient,
|
|
466
|
-
createSupabaseClientFromEnv
|
|
718
|
+
createSupabaseClientFromEnv,
|
|
719
|
+
syncClaudeTodos
|
|
467
720
|
};
|
package/package.json
CHANGED