zenarc-mcp 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.
Files changed (54) hide show
  1. package/README.md +142 -0
  2. package/dist/core/bin/migrate.d.ts +3 -0
  3. package/dist/core/bin/migrate.d.ts.map +1 -0
  4. package/dist/core/bin/migrate.js +24 -0
  5. package/dist/core/bin/migrate.js.map +1 -0
  6. package/dist/core/index.d.ts +6 -0
  7. package/dist/core/index.d.ts.map +1 -0
  8. package/dist/core/index.js +4 -0
  9. package/dist/core/index.js.map +1 -0
  10. package/dist/core/migrate.d.ts +13 -0
  11. package/dist/core/migrate.d.ts.map +1 -0
  12. package/dist/core/migrate.js +154 -0
  13. package/dist/core/migrate.js.map +1 -0
  14. package/dist/core/schema.d.ts +89 -0
  15. package/dist/core/schema.d.ts.map +1 -0
  16. package/dist/core/schema.js +49 -0
  17. package/dist/core/schema.js.map +1 -0
  18. package/dist/core/store/index.d.ts +68 -0
  19. package/dist/core/store/index.d.ts.map +1 -0
  20. package/dist/core/store/index.js +23 -0
  21. package/dist/core/store/index.js.map +1 -0
  22. package/dist/core/store/types.d.ts +30 -0
  23. package/dist/core/store/types.d.ts.map +1 -0
  24. package/dist/core/store/types.js +2 -0
  25. package/dist/core/store/types.js.map +1 -0
  26. package/dist/core/store/yaml-store.d.ts +26 -0
  27. package/dist/core/store/yaml-store.d.ts.map +1 -0
  28. package/dist/core/store/yaml-store.js +482 -0
  29. package/dist/core/store/yaml-store.js.map +1 -0
  30. package/dist/server.d.ts +3 -0
  31. package/dist/server.d.ts.map +1 -0
  32. package/dist/server.js +456 -0
  33. package/dist/server.js.map +1 -0
  34. package/dist/store-init.d.ts +2 -0
  35. package/dist/store-init.d.ts.map +1 -0
  36. package/dist/store-init.js +39 -0
  37. package/dist/store-init.js.map +1 -0
  38. package/dist/sync/firestore-store.d.ts +28 -0
  39. package/dist/sync/firestore-store.d.ts.map +1 -0
  40. package/dist/sync/firestore-store.js +251 -0
  41. package/dist/sync/firestore-store.js.map +1 -0
  42. package/dist/sync/hybrid-store.d.ts +33 -0
  43. package/dist/sync/hybrid-store.d.ts.map +1 -0
  44. package/dist/sync/hybrid-store.js +86 -0
  45. package/dist/sync/hybrid-store.js.map +1 -0
  46. package/dist/sync/index.d.ts +4 -0
  47. package/dist/sync/index.d.ts.map +1 -0
  48. package/dist/sync/index.js +4 -0
  49. package/dist/sync/index.js.map +1 -0
  50. package/dist/sync/sync-bridge.d.ts +21 -0
  51. package/dist/sync/sync-bridge.d.ts.map +1 -0
  52. package/dist/sync/sync-bridge.js +157 -0
  53. package/dist/sync/sync-bridge.js.map +1 -0
  54. package/package.json +52 -0
@@ -0,0 +1,251 @@
1
+ import { initializeApp, cert } from "firebase-admin/app";
2
+ import { getFirestore } from "firebase-admin/firestore";
3
+ import { validateTask, } from "../core/index.js";
4
+ import { join } from "node:path";
5
+ import { homedir } from "node:os";
6
+ let firebaseApp = null;
7
+ export async function initializeFirebase(credentialPath) {
8
+ if (firebaseApp)
9
+ return firebaseApp;
10
+ if (credentialPath) {
11
+ const serviceAccount = await import(credentialPath, {
12
+ assert: { type: "json" },
13
+ });
14
+ firebaseApp = initializeApp({
15
+ credential: cert(serviceAccount.default),
16
+ });
17
+ }
18
+ else {
19
+ // Use application default credentials (e.g., GOOGLE_APPLICATION_CREDENTIALS env var)
20
+ firebaseApp = initializeApp();
21
+ }
22
+ return firebaseApp;
23
+ }
24
+ export function getFirebaseApp() {
25
+ if (!firebaseApp) {
26
+ firebaseApp = initializeApp();
27
+ }
28
+ return firebaseApp;
29
+ }
30
+ function taskToDoc(task) {
31
+ return {
32
+ ...task,
33
+ context: {
34
+ files: task.context.files,
35
+ urls: task.context.urls,
36
+ notes: task.context.notes,
37
+ },
38
+ };
39
+ }
40
+ function docToTask(data) {
41
+ return validateTask({
42
+ id: data.id,
43
+ title: data.title,
44
+ status: data.status,
45
+ priority: data.priority,
46
+ project: data.project,
47
+ tags: data.tags || [],
48
+ created_at: data.created_at,
49
+ updated_at: data.updated_at,
50
+ created_by: data.created_by,
51
+ assigned_to: data.assigned_to,
52
+ context: {
53
+ files: data.context?.files || [],
54
+ urls: data.context?.urls || [],
55
+ notes: data.context?.notes || "",
56
+ },
57
+ dependencies: data.dependencies || [],
58
+ });
59
+ }
60
+ export class FirestoreTaskStore {
61
+ db;
62
+ constructor(app) {
63
+ this.db = getFirestore(app || getFirebaseApp());
64
+ }
65
+ // Registry is still stored locally as JSON
66
+ async getRegistry() {
67
+ const { readFile } = await import("node:fs/promises");
68
+ const registryPath = join(homedir(), ".zenarc", "projects.json");
69
+ try {
70
+ const raw = await readFile(registryPath, "utf-8");
71
+ return JSON.parse(raw);
72
+ }
73
+ catch {
74
+ return [];
75
+ }
76
+ }
77
+ async saveRegistry(projects) {
78
+ const { writeFile, mkdir } = await import("node:fs/promises");
79
+ const registryPath = join(homedir(), ".zenarc", "projects.json");
80
+ await mkdir(join(homedir(), ".zenarc"), { recursive: true });
81
+ await writeFile(registryPath, JSON.stringify(projects, null, 2), "utf-8");
82
+ }
83
+ async addProject(config) {
84
+ const registry = await this.getRegistry();
85
+ const filtered = registry.filter((p) => p.name !== config.name);
86
+ filtered.push(config);
87
+ await this.saveRegistry(filtered);
88
+ }
89
+ async removeProject(name) {
90
+ const registry = await this.getRegistry();
91
+ await this.saveRegistry(registry.filter((p) => p.name !== name));
92
+ }
93
+ getProjectTasksDir(projectPath) {
94
+ return join(projectPath, ".zenarc", "tasks");
95
+ }
96
+ async listProjectTasks(projectPath) {
97
+ const projectName = await this.getProjectNameFromPath(projectPath);
98
+ if (!projectName)
99
+ return [];
100
+ const snapshot = await this.db
101
+ .collection("projects")
102
+ .doc(projectName)
103
+ .collection("tasks")
104
+ .orderBy("updated_at", "desc")
105
+ .get();
106
+ return snapshot.docs.map((doc) => docToTask(doc.data()));
107
+ }
108
+ async readTask(projectPath, taskId) {
109
+ const projectName = await this.getProjectNameFromPath(projectPath);
110
+ if (!projectName)
111
+ return null;
112
+ const doc = await this.db
113
+ .collection("projects")
114
+ .doc(projectName)
115
+ .collection("tasks")
116
+ .doc(taskId)
117
+ .get();
118
+ if (!doc.exists)
119
+ return null;
120
+ return docToTask(doc.data());
121
+ }
122
+ async writeTask(projectPath, task) {
123
+ const projectName = await this.getProjectNameFromPath(projectPath);
124
+ if (!projectName)
125
+ throw new Error(`Project not found for path: ${projectPath}`);
126
+ await this.db
127
+ .collection("projects")
128
+ .doc(projectName)
129
+ .collection("tasks")
130
+ .doc(task.id)
131
+ .set(taskToDoc(task), { merge: true });
132
+ }
133
+ async deleteTask(projectPath, taskId) {
134
+ const projectName = await this.getProjectNameFromPath(projectPath);
135
+ if (!projectName)
136
+ return false;
137
+ const docRef = this.db
138
+ .collection("projects")
139
+ .doc(projectName)
140
+ .collection("tasks")
141
+ .doc(taskId);
142
+ const doc = await docRef.get();
143
+ if (!doc.exists)
144
+ return false;
145
+ await docRef.delete();
146
+ return true;
147
+ }
148
+ async searchTasks(query, options) {
149
+ const registry = await this.getRegistry();
150
+ let projectNames = registry.map((p) => p.name);
151
+ if (options?.project) {
152
+ projectNames = projectNames.filter((n) => n === options.project);
153
+ }
154
+ const allTasks = [];
155
+ for (const name of projectNames) {
156
+ const snapshot = await this.db
157
+ .collection("projects")
158
+ .doc(name)
159
+ .collection("tasks")
160
+ .get();
161
+ for (const doc of snapshot.docs) {
162
+ allTasks.push(docToTask(doc.data()));
163
+ }
164
+ }
165
+ const q = query.toLowerCase();
166
+ return allTasks.filter((task) => {
167
+ if (options?.status && task.status !== options.status)
168
+ return false;
169
+ if (options?.priority && task.priority !== options.priority)
170
+ return false;
171
+ if (options?.tag && !task.tags.includes(options.tag))
172
+ return false;
173
+ if (options?.assigned_to && task.assigned_to !== options.assigned_to)
174
+ return false;
175
+ if (!q)
176
+ return true;
177
+ const text = [
178
+ task.title,
179
+ task.context.notes,
180
+ ...task.tags,
181
+ task.project,
182
+ task.assigned_to || "",
183
+ ]
184
+ .join(" ")
185
+ .toLowerCase();
186
+ return text.includes(q);
187
+ });
188
+ }
189
+ async scanForProjects(rootPaths) {
190
+ const { readdir, access } = await import("node:fs/promises");
191
+ const { join } = await import("node:path");
192
+ const found = [];
193
+ for (const root of rootPaths) {
194
+ try {
195
+ const entries = await readdir(root, { withFileTypes: true });
196
+ for (const entry of entries) {
197
+ if (!entry.isDirectory())
198
+ continue;
199
+ const projectPath = join(root, entry.name);
200
+ // Check for .zenarc/tasks/ directory
201
+ try {
202
+ const tasksDir = join(projectPath, ".zenarc", "tasks");
203
+ await access(tasksDir);
204
+ found.push({ name: entry.name, path: projectPath, format: "yaml" });
205
+ continue;
206
+ }
207
+ catch {
208
+ // No .zenarc/tasks/
209
+ }
210
+ // Check for .zenarc/overview.yml
211
+ try {
212
+ await access(join(projectPath, ".zenarc", "overview.yml"));
213
+ found.push({ name: entry.name, path: projectPath, format: "yaml" });
214
+ continue;
215
+ }
216
+ catch {
217
+ // No .zenarc/overview.yml
218
+ }
219
+ }
220
+ }
221
+ catch {
222
+ // Root path doesn't exist
223
+ }
224
+ }
225
+ return found;
226
+ }
227
+ async getProjectNameFromPath(projectPath) {
228
+ const registry = await this.getRegistry();
229
+ const project = registry.find((p) => p.path === projectPath);
230
+ return project?.name || null;
231
+ }
232
+ // Real-time listeners for sync bridge
233
+ onTaskChanged(projectName, callback) {
234
+ const unsubscribe = this.db
235
+ .collection("projects")
236
+ .doc(projectName)
237
+ .collection("tasks")
238
+ .onSnapshot((snapshot) => {
239
+ snapshot.docChanges().forEach((change) => {
240
+ if (change.type === "removed") {
241
+ callback(null, change.doc.id);
242
+ }
243
+ else {
244
+ callback(docToTask(change.doc.data()), change.doc.id);
245
+ }
246
+ });
247
+ });
248
+ return () => unsubscribe();
249
+ }
250
+ }
251
+ //# sourceMappingURL=firestore-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"firestore-store.js","sourceRoot":"","sources":["../../src/sync/firestore-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import { YamlTaskStore, type Task, type TaskStore, type ProjectConfig } from "../core/index.js";
2
+ import { FirestoreTaskStore } from "./firestore-store.js";
3
+ export declare class HybridTaskStore implements TaskStore {
4
+ private yamlStore;
5
+ private firestoreStore;
6
+ private syncEnabled;
7
+ constructor(options?: {
8
+ syncEnabled?: boolean;
9
+ firestoreStore?: FirestoreTaskStore;
10
+ });
11
+ getRegistry(): Promise<ProjectConfig[]>;
12
+ saveRegistry(projects: ProjectConfig[]): Promise<void>;
13
+ addProject(config: ProjectConfig): Promise<void>;
14
+ removeProject(name: string): Promise<void>;
15
+ getProjectTasksDir(projectPath: string): string;
16
+ listProjectTasks(projectPath: string): Promise<Task[]>;
17
+ readTask(projectPath: string, taskId: string): Promise<Task | null>;
18
+ writeTask(projectPath: string, task: Task): Promise<void>;
19
+ deleteTask(projectPath: string, taskId: string): Promise<boolean>;
20
+ searchTasks(query: string, options?: {
21
+ project?: string;
22
+ status?: string;
23
+ priority?: string;
24
+ tag?: string;
25
+ assigned_to?: string;
26
+ }): Promise<Task[]>;
27
+ scanForProjects(rootPaths: string[]): Promise<ProjectConfig[]>;
28
+ getYamlStore(): YamlTaskStore;
29
+ getFirestoreStore(): FirestoreTaskStore;
30
+ isSyncEnabled(): boolean;
31
+ setSyncEnabled(enabled: boolean): void;
32
+ }
33
+ //# sourceMappingURL=hybrid-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hybrid-store.d.ts","sourceRoot":"","sources":["../../src/sync/hybrid-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,KAAK,IAAI,EAAE,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAChG,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,qBAAa,eAAgB,YAAW,SAAS;IAC/C,OAAO,CAAC,SAAS,CAAgB;IACjC,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,WAAW,CAAU;gBAEjB,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,OAAO,CAAC;QAAC,cAAc,CAAC,EAAE,kBAAkB,CAAA;KAAE;IAM9E,WAAW,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;IAIvC,YAAY,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAItD,UAAU,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhD,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhD,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM;IAIzC,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAKtD,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAYnE,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAczD,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAcjE,WAAW,CACf,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE;QACR,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,GACA,OAAO,CAAC,IAAI,EAAE,CAAC;IAIZ,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAIpE,YAAY,IAAI,aAAa;IAI7B,iBAAiB,IAAI,kBAAkB;IAIvC,aAAa,IAAI,OAAO;IAIxB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;CAGvC"}
@@ -0,0 +1,86 @@
1
+ import { YamlTaskStore } from "../core/index.js";
2
+ import { FirestoreTaskStore } from "./firestore-store.js";
3
+ export class HybridTaskStore {
4
+ yamlStore;
5
+ firestoreStore;
6
+ syncEnabled;
7
+ constructor(options) {
8
+ this.yamlStore = new YamlTaskStore();
9
+ this.firestoreStore = options?.firestoreStore || new FirestoreTaskStore();
10
+ this.syncEnabled = options?.syncEnabled ?? true;
11
+ }
12
+ async getRegistry() {
13
+ return this.yamlStore.getRegistry();
14
+ }
15
+ async saveRegistry(projects) {
16
+ await this.yamlStore.saveRegistry(projects);
17
+ }
18
+ async addProject(config) {
19
+ await this.yamlStore.addProject(config);
20
+ }
21
+ async removeProject(name) {
22
+ await this.yamlStore.removeProject(name);
23
+ }
24
+ getProjectTasksDir(projectPath) {
25
+ return this.yamlStore.getProjectTasksDir(projectPath);
26
+ }
27
+ async listProjectTasks(projectPath) {
28
+ // Read from YAML (fast, local). Firestore sync happens via bridge.
29
+ return this.yamlStore.listProjectTasks(projectPath);
30
+ }
31
+ async readTask(projectPath, taskId) {
32
+ // Try YAML first
33
+ const yamlTask = await this.yamlStore.readTask(projectPath, taskId);
34
+ if (yamlTask)
35
+ return yamlTask;
36
+ // Fallback to Firestore if enabled
37
+ if (this.syncEnabled) {
38
+ return this.firestoreStore.readTask(projectPath, taskId);
39
+ }
40
+ return null;
41
+ }
42
+ async writeTask(projectPath, task) {
43
+ // Always write to YAML (source of truth on desktop)
44
+ await this.yamlStore.writeTask(projectPath, task);
45
+ // Async push to Firestore (non-blocking)
46
+ if (this.syncEnabled) {
47
+ try {
48
+ await this.firestoreStore.writeTask(projectPath, task);
49
+ }
50
+ catch (err) {
51
+ console.error("[HybridStore] Firestore write failed:", err);
52
+ }
53
+ }
54
+ }
55
+ async deleteTask(projectPath, taskId) {
56
+ const deleted = await this.yamlStore.deleteTask(projectPath, taskId);
57
+ if (deleted && this.syncEnabled) {
58
+ try {
59
+ await this.firestoreStore.deleteTask(projectPath, taskId);
60
+ }
61
+ catch (err) {
62
+ console.error("[HybridStore] Firestore delete failed:", err);
63
+ }
64
+ }
65
+ return deleted;
66
+ }
67
+ async searchTasks(query, options) {
68
+ return this.yamlStore.searchTasks(query, options);
69
+ }
70
+ async scanForProjects(rootPaths) {
71
+ return this.yamlStore.scanForProjects(rootPaths);
72
+ }
73
+ getYamlStore() {
74
+ return this.yamlStore;
75
+ }
76
+ getFirestoreStore() {
77
+ return this.firestoreStore;
78
+ }
79
+ isSyncEnabled() {
80
+ return this.syncEnabled;
81
+ }
82
+ setSyncEnabled(enabled) {
83
+ this.syncEnabled = enabled;
84
+ }
85
+ }
86
+ //# sourceMappingURL=hybrid-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hybrid-store.js","sourceRoot":"","sources":["../../src/sync/hybrid-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export { FirestoreTaskStore, initializeFirebase, getFirebaseApp } from "./firestore-store.js";
2
+ export { HybridTaskStore } from "./hybrid-store.js";
3
+ export { SyncBridge } from "./sync-bridge.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sync/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC9F,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { FirestoreTaskStore, initializeFirebase, getFirebaseApp } from "./firestore-store.js";
2
+ export { HybridTaskStore } from "./hybrid-store.js";
3
+ export { SyncBridge } from "./sync-bridge.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sync/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC9F,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { FirestoreTaskStore } from "./firestore-store.js";
2
+ export type SyncBridgeOptions = {
3
+ pollIntervalMs?: number;
4
+ verbose?: boolean;
5
+ };
6
+ export declare class SyncBridge {
7
+ private yamlStore;
8
+ private firestoreStore;
9
+ private watchers;
10
+ private firestoreUnsubscribers;
11
+ private options;
12
+ private syncInProgress;
13
+ constructor(firestoreStore?: FirestoreTaskStore, options?: SyncBridgeOptions);
14
+ private log;
15
+ start(projectName: string, projectPath: string): Promise<void>;
16
+ stop(projectName: string): void;
17
+ stopAll(): void;
18
+ initialSync(projectName: string, projectPath: string): Promise<void>;
19
+ startAllFromRegistry(): Promise<void>;
20
+ }
21
+ //# sourceMappingURL=sync-bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-bridge.d.ts","sourceRoot":"","sources":["../../src/sync/sync-bridge.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,qBAAa,UAAU;IACrB,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,QAAQ,CAAqC;IACrD,OAAO,CAAC,sBAAsB,CAAsC;IACpE,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,cAAc,CAAqB;gBAE/B,cAAc,CAAC,EAAE,kBAAkB,EAAE,OAAO,GAAE,iBAAsB;IAShF,OAAO,CAAC,GAAG;IAML,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA+EpE,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAgB/B,OAAO,IAAI,IAAI;IAMT,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA4BpE,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC;CAS5C"}
@@ -0,0 +1,157 @@
1
+ import { watch } from "node:fs";
2
+ import { readFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { parse } from "yaml";
5
+ import { validateTask } from "../core/index.js";
6
+ import { YamlTaskStore } from "../core/index.js";
7
+ import { FirestoreTaskStore } from "./firestore-store.js";
8
+ export class SyncBridge {
9
+ yamlStore = new YamlTaskStore();
10
+ firestoreStore;
11
+ watchers = new Map();
12
+ firestoreUnsubscribers = new Map();
13
+ options;
14
+ syncInProgress = new Set();
15
+ constructor(firestoreStore, options = {}) {
16
+ this.firestoreStore = firestoreStore || new FirestoreTaskStore();
17
+ this.options = {
18
+ pollIntervalMs: 5000,
19
+ verbose: false,
20
+ ...options,
21
+ };
22
+ }
23
+ log(...args) {
24
+ if (this.options.verbose) {
25
+ console.log("[SyncBridge]", ...args);
26
+ }
27
+ }
28
+ async start(projectName, projectPath) {
29
+ const tasksDir = this.yamlStore.getProjectTasksDir(projectPath);
30
+ // Watch YAML files and push to Firestore
31
+ const watcher = watch(tasksDir, { recursive: true }, async (eventType, filename) => {
32
+ if (!filename || !(filename.endsWith(".yaml") || filename.endsWith(".yml")))
33
+ return;
34
+ const filepath = join(tasksDir, filename);
35
+ try {
36
+ if (eventType === "rename") {
37
+ // File was deleted
38
+ const exists = await readFile(filepath, "utf-8").then(() => true).catch(() => false);
39
+ if (!exists) {
40
+ this.log("File deleted:", filename);
41
+ // We don't know the taskId from filename alone, so we skip Firestore delete here.
42
+ // The Firestore listener will handle mobile-initiated deletes back to YAML.
43
+ }
44
+ }
45
+ else {
46
+ // File changed or created
47
+ const raw = await readFile(filepath, "utf-8");
48
+ const data = parse(raw);
49
+ const task = validateTask(data);
50
+ if (this.syncInProgress.has(task.id))
51
+ return;
52
+ this.syncInProgress.add(task.id);
53
+ this.log("YAML → Firestore:", task.id, task.title);
54
+ await this.firestoreStore.writeTask(projectPath, task);
55
+ this.syncInProgress.delete(task.id);
56
+ }
57
+ }
58
+ catch (err) {
59
+ if (eventType !== "rename") {
60
+ // Try to read the file to get task id for cleanup
61
+ try {
62
+ const raw = await readFile(filepath, "utf-8");
63
+ const data = parse(raw);
64
+ if (data?.id)
65
+ this.syncInProgress.delete(data.id);
66
+ }
67
+ catch {
68
+ // ignore
69
+ }
70
+ }
71
+ console.error("[SyncBridge] Error syncing YAML to Firestore:", err);
72
+ }
73
+ });
74
+ this.watchers.set(projectName, watcher);
75
+ // Listen to Firestore changes and write back to YAML
76
+ const unsubscribe = this.firestoreStore.onTaskChanged(projectName, async (task) => {
77
+ if (!task) {
78
+ // Task deleted in Firestore — we would need to know which file to delete.
79
+ // In practice, mobile-initiated deletes are rare; mobile usually marks as "done".
80
+ return;
81
+ }
82
+ if (this.syncInProgress.has(task.id))
83
+ return;
84
+ try {
85
+ const existing = await this.yamlStore.readTask(projectPath, task.id);
86
+ if (existing && existing.updated_at >= task.updated_at) {
87
+ this.log("Skipping Firestore → YAML (YAML is newer):", task.id);
88
+ return;
89
+ }
90
+ this.log("Firestore → YAML:", task.id, task.title);
91
+ this.syncInProgress.add(task.id);
92
+ await this.yamlStore.writeTask(projectPath, task);
93
+ this.syncInProgress.delete(task.id);
94
+ }
95
+ catch (err) {
96
+ this.syncInProgress.delete(task.id);
97
+ console.error("[SyncBridge] Error syncing Firestore to YAML:", err);
98
+ }
99
+ });
100
+ this.firestoreUnsubscribers.set(projectName, unsubscribe);
101
+ this.log("Started sync for project:", projectName);
102
+ }
103
+ stop(projectName) {
104
+ const watcher = this.watchers.get(projectName);
105
+ if (watcher) {
106
+ watcher.close();
107
+ this.watchers.delete(projectName);
108
+ }
109
+ const unsub = this.firestoreUnsubscribers.get(projectName);
110
+ if (unsub) {
111
+ unsub();
112
+ this.firestoreUnsubscribers.delete(projectName);
113
+ }
114
+ this.log("Stopped sync for project:", projectName);
115
+ }
116
+ stopAll() {
117
+ for (const [name] of this.watchers) {
118
+ this.stop(name);
119
+ }
120
+ }
121
+ async initialSync(projectName, projectPath) {
122
+ this.log("Running initial sync for:", projectName);
123
+ // Push all local YAML tasks to Firestore
124
+ const yamlTasks = await this.yamlStore.listProjectTasks(projectPath);
125
+ for (const task of yamlTasks) {
126
+ try {
127
+ const firestoreTask = await this.firestoreStore.readTask(projectPath, task.id);
128
+ if (!firestoreTask || task.updated_at > firestoreTask.updated_at) {
129
+ await this.firestoreStore.writeTask(projectPath, task);
130
+ this.log("Initial sync pushed:", task.id);
131
+ }
132
+ }
133
+ catch (err) {
134
+ console.error("[SyncBridge] Initial sync error for task:", task.id, err);
135
+ }
136
+ }
137
+ // Pull any Firestore tasks that don't exist locally
138
+ const firestoreTasks = await this.firestoreStore.listProjectTasks(projectPath);
139
+ for (const task of firestoreTasks) {
140
+ const yamlTask = await this.yamlStore.readTask(projectPath, task.id);
141
+ if (!yamlTask || task.updated_at > yamlTask.updated_at) {
142
+ await this.yamlStore.writeTask(projectPath, task);
143
+ this.log("Initial sync pulled:", task.id);
144
+ }
145
+ }
146
+ }
147
+ async startAllFromRegistry() {
148
+ const registry = await this.yamlStore.getRegistry();
149
+ for (const project of registry) {
150
+ if (project.sync?.enabled) {
151
+ await this.initialSync(project.name, project.path);
152
+ await this.start(project.name, project.path);
153
+ }
154
+ }
155
+ }
156
+ }
157
+ //# sourceMappingURL=sync-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-bridge.js","sourceRoot":"","sources":["../../src/sync/sync-bridge.ts"],"names":[],"mappings":"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"}