@stoneforge/quarry 1.12.0 → 1.14.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 (199) hide show
  1. package/README.md +2 -0
  2. package/dist/api/quarry-api.d.ts +9 -1
  3. package/dist/api/quarry-api.d.ts.map +1 -1
  4. package/dist/api/quarry-api.js +21 -2
  5. package/dist/api/quarry-api.js.map +1 -1
  6. package/dist/api/types.d.ts +8 -1
  7. package/dist/api/types.d.ts.map +1 -1
  8. package/dist/api/types.js.map +1 -1
  9. package/dist/cli/commands/auto-link-helper.d.ts +33 -0
  10. package/dist/cli/commands/auto-link-helper.d.ts.map +1 -0
  11. package/dist/cli/commands/auto-link-helper.js +74 -0
  12. package/dist/cli/commands/auto-link-helper.js.map +1 -0
  13. package/dist/cli/commands/crud.d.ts +3 -0
  14. package/dist/cli/commands/crud.d.ts.map +1 -1
  15. package/dist/cli/commands/crud.js +144 -15
  16. package/dist/cli/commands/crud.js.map +1 -1
  17. package/dist/cli/commands/docs.js +2 -2
  18. package/dist/cli/commands/docs.js.map +1 -1
  19. package/dist/cli/commands/document.js +1 -1
  20. package/dist/cli/commands/document.js.map +1 -1
  21. package/dist/cli/commands/entity.js +1 -1
  22. package/dist/cli/commands/entity.js.map +1 -1
  23. package/dist/cli/commands/external-sync.d.ts +18 -0
  24. package/dist/cli/commands/external-sync.d.ts.map +1 -0
  25. package/dist/cli/commands/external-sync.js +2499 -0
  26. package/dist/cli/commands/external-sync.js.map +1 -0
  27. package/dist/cli/commands/library.js +1 -1
  28. package/dist/cli/commands/library.js.map +1 -1
  29. package/dist/cli/commands/message.js +2 -2
  30. package/dist/cli/commands/message.js.map +1 -1
  31. package/dist/cli/commands/serve.d.ts.map +1 -1
  32. package/dist/cli/commands/serve.js +2 -0
  33. package/dist/cli/commands/serve.js.map +1 -1
  34. package/dist/cli/commands/task.d.ts.map +1 -1
  35. package/dist/cli/commands/task.js +7 -4
  36. package/dist/cli/commands/task.js.map +1 -1
  37. package/dist/cli/commands/team.js +1 -1
  38. package/dist/cli/commands/team.js.map +1 -1
  39. package/dist/cli/commands/workflow.js +1 -1
  40. package/dist/cli/commands/workflow.js.map +1 -1
  41. package/dist/cli/runner.d.ts.map +1 -1
  42. package/dist/cli/runner.js +3 -0
  43. package/dist/cli/runner.js.map +1 -1
  44. package/dist/cli/utils/progress.d.ts +30 -0
  45. package/dist/cli/utils/progress.d.ts.map +1 -0
  46. package/dist/cli/utils/progress.js +47 -0
  47. package/dist/cli/utils/progress.js.map +1 -0
  48. package/dist/config/config.d.ts.map +1 -1
  49. package/dist/config/config.js +34 -0
  50. package/dist/config/config.js.map +1 -1
  51. package/dist/config/defaults.d.ts +13 -1
  52. package/dist/config/defaults.d.ts.map +1 -1
  53. package/dist/config/defaults.js +22 -0
  54. package/dist/config/defaults.js.map +1 -1
  55. package/dist/config/file.d.ts.map +1 -1
  56. package/dist/config/file.js +71 -0
  57. package/dist/config/file.js.map +1 -1
  58. package/dist/config/index.d.ts +3 -3
  59. package/dist/config/index.d.ts.map +1 -1
  60. package/dist/config/index.js +2 -2
  61. package/dist/config/index.js.map +1 -1
  62. package/dist/config/merge.d.ts.map +1 -1
  63. package/dist/config/merge.js +52 -1
  64. package/dist/config/merge.js.map +1 -1
  65. package/dist/config/types.d.ts +68 -1
  66. package/dist/config/types.d.ts.map +1 -1
  67. package/dist/config/types.js +33 -0
  68. package/dist/config/types.js.map +1 -1
  69. package/dist/config/validation.d.ts.map +1 -1
  70. package/dist/config/validation.js +64 -1
  71. package/dist/config/validation.js.map +1 -1
  72. package/dist/external-sync/adapters/document-sync-adapter.d.ts +150 -0
  73. package/dist/external-sync/adapters/document-sync-adapter.d.ts.map +1 -0
  74. package/dist/external-sync/adapters/document-sync-adapter.js +325 -0
  75. package/dist/external-sync/adapters/document-sync-adapter.js.map +1 -0
  76. package/dist/external-sync/adapters/task-sync-adapter.d.ts +177 -0
  77. package/dist/external-sync/adapters/task-sync-adapter.d.ts.map +1 -0
  78. package/dist/external-sync/adapters/task-sync-adapter.js +353 -0
  79. package/dist/external-sync/adapters/task-sync-adapter.js.map +1 -0
  80. package/dist/external-sync/auto-link.d.ts +66 -0
  81. package/dist/external-sync/auto-link.d.ts.map +1 -0
  82. package/dist/external-sync/auto-link.js +98 -0
  83. package/dist/external-sync/auto-link.js.map +1 -0
  84. package/dist/external-sync/conflict-resolver.d.ts +170 -0
  85. package/dist/external-sync/conflict-resolver.d.ts.map +1 -0
  86. package/dist/external-sync/conflict-resolver.js +580 -0
  87. package/dist/external-sync/conflict-resolver.js.map +1 -0
  88. package/dist/external-sync/index.d.ts +23 -0
  89. package/dist/external-sync/index.d.ts.map +1 -0
  90. package/dist/external-sync/index.js +24 -0
  91. package/dist/external-sync/index.js.map +1 -0
  92. package/dist/external-sync/provider-registry.d.ts +113 -0
  93. package/dist/external-sync/provider-registry.d.ts.map +1 -0
  94. package/dist/external-sync/provider-registry.js +205 -0
  95. package/dist/external-sync/provider-registry.js.map +1 -0
  96. package/dist/external-sync/providers/folder/folder-document-adapter.d.ts +97 -0
  97. package/dist/external-sync/providers/folder/folder-document-adapter.d.ts.map +1 -0
  98. package/dist/external-sync/providers/folder/folder-document-adapter.js +261 -0
  99. package/dist/external-sync/providers/folder/folder-document-adapter.js.map +1 -0
  100. package/dist/external-sync/providers/folder/folder-fs.d.ts +146 -0
  101. package/dist/external-sync/providers/folder/folder-fs.d.ts.map +1 -0
  102. package/dist/external-sync/providers/folder/folder-fs.js +300 -0
  103. package/dist/external-sync/providers/folder/folder-fs.js.map +1 -0
  104. package/dist/external-sync/providers/folder/folder-provider.d.ts +28 -0
  105. package/dist/external-sync/providers/folder/folder-provider.d.ts.map +1 -0
  106. package/dist/external-sync/providers/folder/folder-provider.js +87 -0
  107. package/dist/external-sync/providers/folder/folder-provider.js.map +1 -0
  108. package/dist/external-sync/providers/folder/index.d.ts +11 -0
  109. package/dist/external-sync/providers/folder/index.d.ts.map +1 -0
  110. package/dist/external-sync/providers/folder/index.js +13 -0
  111. package/dist/external-sync/providers/folder/index.js.map +1 -0
  112. package/dist/external-sync/providers/github/github-api.d.ts +271 -0
  113. package/dist/external-sync/providers/github/github-api.d.ts.map +1 -0
  114. package/dist/external-sync/providers/github/github-api.js +366 -0
  115. package/dist/external-sync/providers/github/github-api.js.map +1 -0
  116. package/dist/external-sync/providers/github/github-field-map.d.ts +76 -0
  117. package/dist/external-sync/providers/github/github-field-map.d.ts.map +1 -0
  118. package/dist/external-sync/providers/github/github-field-map.js +157 -0
  119. package/dist/external-sync/providers/github/github-field-map.js.map +1 -0
  120. package/dist/external-sync/providers/github/github-provider.d.ts +36 -0
  121. package/dist/external-sync/providers/github/github-provider.d.ts.map +1 -0
  122. package/dist/external-sync/providers/github/github-provider.js +212 -0
  123. package/dist/external-sync/providers/github/github-provider.js.map +1 -0
  124. package/dist/external-sync/providers/github/github-task-adapter.d.ts +135 -0
  125. package/dist/external-sync/providers/github/github-task-adapter.d.ts.map +1 -0
  126. package/dist/external-sync/providers/github/github-task-adapter.js +374 -0
  127. package/dist/external-sync/providers/github/github-task-adapter.js.map +1 -0
  128. package/dist/external-sync/providers/github/index.d.ts +12 -0
  129. package/dist/external-sync/providers/github/index.d.ts.map +1 -0
  130. package/dist/external-sync/providers/github/index.js +15 -0
  131. package/dist/external-sync/providers/github/index.js.map +1 -0
  132. package/dist/external-sync/providers/index.d.ts +13 -0
  133. package/dist/external-sync/providers/index.d.ts.map +1 -0
  134. package/dist/external-sync/providers/index.js +15 -0
  135. package/dist/external-sync/providers/index.js.map +1 -0
  136. package/dist/external-sync/providers/linear/index.d.ts +19 -0
  137. package/dist/external-sync/providers/linear/index.d.ts.map +1 -0
  138. package/dist/external-sync/providers/linear/index.js +19 -0
  139. package/dist/external-sync/providers/linear/index.js.map +1 -0
  140. package/dist/external-sync/providers/linear/linear-api.d.ts +252 -0
  141. package/dist/external-sync/providers/linear/linear-api.d.ts.map +1 -0
  142. package/dist/external-sync/providers/linear/linear-api.js +522 -0
  143. package/dist/external-sync/providers/linear/linear-api.js.map +1 -0
  144. package/dist/external-sync/providers/linear/linear-field-map.d.ts +135 -0
  145. package/dist/external-sync/providers/linear/linear-field-map.d.ts.map +1 -0
  146. package/dist/external-sync/providers/linear/linear-field-map.js +338 -0
  147. package/dist/external-sync/providers/linear/linear-field-map.js.map +1 -0
  148. package/dist/external-sync/providers/linear/linear-provider.d.ts +52 -0
  149. package/dist/external-sync/providers/linear/linear-provider.d.ts.map +1 -0
  150. package/dist/external-sync/providers/linear/linear-provider.js +169 -0
  151. package/dist/external-sync/providers/linear/linear-provider.js.map +1 -0
  152. package/dist/external-sync/providers/linear/linear-task-adapter.d.ts +190 -0
  153. package/dist/external-sync/providers/linear/linear-task-adapter.d.ts.map +1 -0
  154. package/dist/external-sync/providers/linear/linear-task-adapter.js +521 -0
  155. package/dist/external-sync/providers/linear/linear-task-adapter.js.map +1 -0
  156. package/dist/external-sync/providers/linear/linear-types.d.ts +114 -0
  157. package/dist/external-sync/providers/linear/linear-types.d.ts.map +1 -0
  158. package/dist/external-sync/providers/linear/linear-types.js +10 -0
  159. package/dist/external-sync/providers/linear/linear-types.js.map +1 -0
  160. package/dist/external-sync/providers/notion/index.d.ts +19 -0
  161. package/dist/external-sync/providers/notion/index.d.ts.map +1 -0
  162. package/dist/external-sync/providers/notion/index.js +20 -0
  163. package/dist/external-sync/providers/notion/index.js.map +1 -0
  164. package/dist/external-sync/providers/notion/notion-api.d.ts +253 -0
  165. package/dist/external-sync/providers/notion/notion-api.d.ts.map +1 -0
  166. package/dist/external-sync/providers/notion/notion-api.js +492 -0
  167. package/dist/external-sync/providers/notion/notion-api.js.map +1 -0
  168. package/dist/external-sync/providers/notion/notion-blocks.d.ts +93 -0
  169. package/dist/external-sync/providers/notion/notion-blocks.d.ts.map +1 -0
  170. package/dist/external-sync/providers/notion/notion-blocks.js +773 -0
  171. package/dist/external-sync/providers/notion/notion-blocks.js.map +1 -0
  172. package/dist/external-sync/providers/notion/notion-document-adapter.d.ts +176 -0
  173. package/dist/external-sync/providers/notion/notion-document-adapter.d.ts.map +1 -0
  174. package/dist/external-sync/providers/notion/notion-document-adapter.js +413 -0
  175. package/dist/external-sync/providers/notion/notion-document-adapter.js.map +1 -0
  176. package/dist/external-sync/providers/notion/notion-provider.d.ts +57 -0
  177. package/dist/external-sync/providers/notion/notion-provider.d.ts.map +1 -0
  178. package/dist/external-sync/providers/notion/notion-provider.js +159 -0
  179. package/dist/external-sync/providers/notion/notion-provider.js.map +1 -0
  180. package/dist/external-sync/providers/notion/notion-types.d.ts +388 -0
  181. package/dist/external-sync/providers/notion/notion-types.d.ts.map +1 -0
  182. package/dist/external-sync/providers/notion/notion-types.js +47 -0
  183. package/dist/external-sync/providers/notion/notion-types.js.map +1 -0
  184. package/dist/external-sync/sync-engine.d.ts +364 -0
  185. package/dist/external-sync/sync-engine.d.ts.map +1 -0
  186. package/dist/external-sync/sync-engine.js +1154 -0
  187. package/dist/external-sync/sync-engine.js.map +1 -0
  188. package/dist/index.d.ts +1 -0
  189. package/dist/index.d.ts.map +1 -1
  190. package/dist/index.js +2 -0
  191. package/dist/index.js.map +1 -1
  192. package/dist/server/index.js +8 -8
  193. package/dist/server/index.js.map +1 -1
  194. package/dist/services/inbox.js +1 -1
  195. package/dist/sync/hash.d.ts +5 -0
  196. package/dist/sync/hash.d.ts.map +1 -1
  197. package/dist/sync/hash.js +21 -2
  198. package/dist/sync/hash.js.map +1 -1
  199. package/package.json +10 -12
@@ -0,0 +1,325 @@
1
+ /**
2
+ * Document Sync Adapter Utilities
3
+ *
4
+ * Shared field mapping logic for converting between Stoneforge documents and
5
+ * external document representations (e.g., Notion pages, Obsidian notes).
6
+ *
7
+ * These utilities are provider-agnostic — they handle the common conversion
8
+ * logic that all document sync providers need.
9
+ *
10
+ * Key functions:
11
+ * - documentToExternalDocumentInput: Convert a Stoneforge Document → ExternalDocumentInput for push
12
+ * - externalDocumentToDocumentUpdates: Convert an ExternalDocument → Partial<Document> for pull
13
+ * - diffDocumentUpdates: Return only changed fields between existing and updated document
14
+ * - computeExternalDocumentHash: Deterministic hash for change detection
15
+ */
16
+ import { createHash } from 'crypto';
17
+ // ============================================================================
18
+ // System Categories
19
+ // ============================================================================
20
+ /**
21
+ * Document categories that are system-managed and should be excluded from
22
+ * external sync. These documents are structural (task descriptions, message
23
+ * content) and are synced through their parent element's sync adapter.
24
+ */
25
+ export const SYSTEM_CATEGORIES = new Set([
26
+ 'task-description',
27
+ 'message-content',
28
+ ]);
29
+ /**
30
+ * Checks whether a document category is a system category that should
31
+ * be excluded from document sync.
32
+ */
33
+ export function isSystemCategory(category) {
34
+ return SYSTEM_CATEGORIES.has(category);
35
+ }
36
+ /**
37
+ * Checks whether a document should be included in external sync operations
38
+ * (link-all, push, pull). A document is syncable if:
39
+ * - It does not have a system category (task-description, message-content)
40
+ * - It has a non-empty title (null, undefined, or whitespace-only titles are excluded)
41
+ *
42
+ * Documents without titles are typically system-generated (messages, task descriptions)
43
+ * that happen to not have the system category set, or scratch documents. They all
44
+ * slugify to "untitled.md" and overwrite each other, so they must be excluded.
45
+ */
46
+ export function isSyncableDocument(doc) {
47
+ if (isSystemCategory(doc.category))
48
+ return false;
49
+ if (!doc.title || doc.title.trim().length === 0)
50
+ return false;
51
+ return true;
52
+ }
53
+ // ============================================================================
54
+ // Content Type Mapping
55
+ // ============================================================================
56
+ /**
57
+ * Maps Stoneforge ContentType to external document content type.
58
+ *
59
+ * Stoneforge uses 'markdown', 'text', and 'json'.
60
+ * External systems use 'markdown', 'text', and 'html'.
61
+ * JSON content is mapped to 'text' for external systems since most
62
+ * document providers don't have a native JSON content type.
63
+ */
64
+ export function mapContentTypeToExternal(contentType) {
65
+ switch (contentType) {
66
+ case 'markdown':
67
+ return 'markdown';
68
+ case 'text':
69
+ return 'text';
70
+ case 'json':
71
+ // JSON doesn't have a direct external equivalent; map to text
72
+ return 'text';
73
+ default:
74
+ return 'text';
75
+ }
76
+ }
77
+ /**
78
+ * Maps external document content type to Stoneforge ContentType.
79
+ *
80
+ * External systems use 'markdown', 'text', and 'html'.
81
+ * HTML content is mapped to 'text' since Stoneforge doesn't have an HTML
82
+ * content type — the content is stored as-is, just categorized as text.
83
+ */
84
+ export function mapContentTypeFromExternal(contentType) {
85
+ switch (contentType) {
86
+ case 'markdown':
87
+ return 'markdown';
88
+ case 'text':
89
+ return 'text';
90
+ case 'html':
91
+ // HTML doesn't have a direct Stoneforge equivalent; map to text
92
+ return 'text';
93
+ default:
94
+ return 'text';
95
+ }
96
+ }
97
+ /**
98
+ * Slugifies a library name for use as a directory name.
99
+ * Uses the same algorithm as the folder adapter's slugify function:
100
+ * lowercase, replace non-alphanumeric chars with hyphens, collapse
101
+ * consecutive hyphens, trim leading/trailing hyphens.
102
+ *
103
+ * @param name - Library name to slugify
104
+ * @returns Slugified directory name
105
+ */
106
+ function slugifyLibraryName(name) {
107
+ const slug = name
108
+ .toLowerCase()
109
+ .replace(/[^a-z0-9-]/g, '-')
110
+ .replace(/-+/g, '-')
111
+ .replace(/^-|-$/g, '');
112
+ return slug || 'untitled';
113
+ }
114
+ /**
115
+ * Resolves the library path for a document by querying its parent-child
116
+ * dependencies. Builds a hierarchical path from the root library down
117
+ * to the document's immediate parent library.
118
+ *
119
+ * For example, if a document belongs to library "API Reference" which
120
+ * is a child of library "Documentation", the returned path would be
121
+ * "documentation/api-reference".
122
+ *
123
+ * Design decisions:
124
+ * - If a document belongs to multiple libraries, the first one found is used
125
+ * - Library names are slugified for directory names
126
+ * - If the document has no library parent, returns undefined
127
+ *
128
+ * @param api - API for querying dependencies and elements
129
+ * @param documentId - The document's element ID
130
+ * @returns The slugified library path (e.g., 'documentation/api-reference'), or undefined if not in a library
131
+ */
132
+ export async function resolveDocumentLibraryPath(api, documentId) {
133
+ // Find parent-child dependencies where document is the child (blockedId)
134
+ const deps = await api.getDependencies(documentId, ['parent-child']);
135
+ if (deps.length === 0) {
136
+ return undefined;
137
+ }
138
+ // Find the first library parent
139
+ let libraryId;
140
+ for (const dep of deps) {
141
+ const parent = await api.get(dep.blockerId);
142
+ if (parent && parent.type === 'library') {
143
+ libraryId = dep.blockerId;
144
+ break;
145
+ }
146
+ }
147
+ if (!libraryId) {
148
+ return undefined;
149
+ }
150
+ // Walk up the library hierarchy to build the full path
151
+ const pathSegments = [];
152
+ let currentId = libraryId;
153
+ const visited = new Set(); // Prevent infinite loops from cycles
154
+ while (currentId && !visited.has(currentId)) {
155
+ visited.add(currentId);
156
+ const library = await api.get(currentId);
157
+ if (!library || library.type !== 'library') {
158
+ break;
159
+ }
160
+ pathSegments.unshift(slugifyLibraryName(library.name));
161
+ // Find the parent library of this library
162
+ const parentDeps = await api.getDependencies(currentId, ['parent-child']);
163
+ let foundParentLibrary = false;
164
+ for (const dep of parentDeps) {
165
+ const parent = await api.get(dep.blockerId);
166
+ if (parent && parent.type === 'library') {
167
+ currentId = dep.blockerId;
168
+ foundParentLibrary = true;
169
+ break;
170
+ }
171
+ }
172
+ if (!foundParentLibrary) {
173
+ break;
174
+ }
175
+ }
176
+ return pathSegments.length > 0 ? pathSegments.join('/') : undefined;
177
+ }
178
+ /**
179
+ * Batch-resolves library paths for multiple documents.
180
+ * More efficient than calling resolveDocumentLibraryPath individually
181
+ * since it can reuse cached library lookups.
182
+ *
183
+ * @param api - API for querying dependencies and elements
184
+ * @param documentIds - Array of document element IDs
185
+ * @returns Map from document ID to library path (undefined entries omitted)
186
+ */
187
+ export async function resolveDocumentLibraryPaths(api, documentIds) {
188
+ const result = new Map();
189
+ for (const docId of documentIds) {
190
+ const libraryPath = await resolveDocumentLibraryPath(api, docId);
191
+ if (libraryPath) {
192
+ result.set(docId, libraryPath);
193
+ }
194
+ }
195
+ return result;
196
+ }
197
+ // ============================================================================
198
+ // Push: Stoneforge Document → External Document
199
+ // ============================================================================
200
+ /**
201
+ * Converts a Stoneforge Document into an ExternalDocumentInput for
202
+ * creating/updating a page in an external system.
203
+ *
204
+ * Handles:
205
+ * - Title mapping (1:1, falls back to empty string if undefined)
206
+ * - Content mapping (1:1)
207
+ * - ContentType mapping (markdown/text → markdown/text, json → text)
208
+ * - Optional library path for directory-based organization
209
+ *
210
+ * @param doc - The Stoneforge document to convert
211
+ * @param libraryPath - Optional pre-computed library path for the document
212
+ * @returns ExternalDocumentInput ready for the provider adapter
213
+ */
214
+ export function documentToExternalDocumentInput(doc, libraryPath) {
215
+ return {
216
+ title: doc.title ?? '',
217
+ content: doc.content,
218
+ contentType: mapContentTypeToExternal(doc.contentType),
219
+ ...(doc.category !== undefined && { category: doc.category }),
220
+ ...(doc.tags !== undefined && doc.tags.length > 0 && { tags: doc.tags }),
221
+ ...(libraryPath !== undefined && { libraryPath }),
222
+ };
223
+ }
224
+ // ============================================================================
225
+ // Pull: External Document → Stoneforge Document Updates
226
+ // ============================================================================
227
+ /**
228
+ * Converts an ExternalDocument into a partial Document update object for
229
+ * applying external changes to a local Stoneforge document.
230
+ *
231
+ * Handles:
232
+ * - Title mapping (1:1)
233
+ * - Content mapping (1:1)
234
+ * - ContentType mapping (markdown/text/html → markdown/text/text)
235
+ *
236
+ * If existingDoc is provided, only changed fields are returned (diff mode).
237
+ * If existingDoc is undefined, all mappable fields are returned (create mode).
238
+ *
239
+ * @param externalDoc - The external document to convert
240
+ * @param existingDoc - The existing local document (undefined for new documents)
241
+ * @returns Partial<Document> with only the changed fields (or all fields if no existingDoc)
242
+ */
243
+ export function externalDocumentToDocumentUpdates(externalDoc, existingDoc) {
244
+ const contentType = mapContentTypeFromExternal(externalDoc.contentType);
245
+ const fullUpdate = {
246
+ title: externalDoc.title,
247
+ content: externalDoc.content,
248
+ contentType,
249
+ };
250
+ // If no existing document, return full create input
251
+ if (!existingDoc) {
252
+ return fullUpdate;
253
+ }
254
+ // Diff mode: only return changed fields
255
+ return diffDocumentUpdates(existingDoc, fullUpdate);
256
+ }
257
+ // ============================================================================
258
+ // Diff Utilities
259
+ // ============================================================================
260
+ /**
261
+ * Compares a full update against an existing document and returns only
262
+ * the fields that actually changed.
263
+ *
264
+ * This prevents unnecessary updates when pulling changes from external
265
+ * systems where the data hasn't actually changed.
266
+ *
267
+ * Compared fields: title, content, contentType, category, tags
268
+ */
269
+ export function diffDocumentUpdates(existing, updates) {
270
+ const diff = {};
271
+ if (updates.title !== undefined && updates.title !== existing.title) {
272
+ diff.title = updates.title;
273
+ }
274
+ if (updates.content !== undefined && updates.content !== existing.content) {
275
+ diff.content = updates.content;
276
+ }
277
+ if (updates.contentType !== undefined &&
278
+ updates.contentType !== existing.contentType) {
279
+ diff.contentType = updates.contentType;
280
+ }
281
+ if (updates.category !== undefined &&
282
+ updates.category !== existing.category) {
283
+ diff.category = updates.category;
284
+ }
285
+ if (updates.tags !== undefined && !arraysEqual(updates.tags, existing.tags)) {
286
+ diff.tags = updates.tags;
287
+ }
288
+ return diff;
289
+ }
290
+ // ============================================================================
291
+ // Hash Computation
292
+ // ============================================================================
293
+ /**
294
+ * Computes a deterministic hash of an ExternalDocument for change detection.
295
+ *
296
+ * The hash is based on the document's title, content, and contentType.
297
+ * Used by the sync engine to detect whether an external document has
298
+ * changed since the last sync, avoiding unnecessary pull operations.
299
+ *
300
+ * @param doc - The external document to hash
301
+ * @returns A hex-encoded SHA-256 hash string
302
+ */
303
+ export function computeExternalDocumentHash(doc) {
304
+ const hash = createHash('sha256');
305
+ hash.update(doc.title);
306
+ hash.update('\0'); // null byte separator to avoid collisions
307
+ hash.update(doc.content);
308
+ hash.update('\0');
309
+ hash.update(doc.contentType);
310
+ return hash.digest('hex');
311
+ }
312
+ // ============================================================================
313
+ // Internal Helpers
314
+ // ============================================================================
315
+ /**
316
+ * Compares two arrays for equality (order-independent).
317
+ */
318
+ function arraysEqual(a, b) {
319
+ if (a.length !== b.length)
320
+ return false;
321
+ const sortedA = [...a].sort();
322
+ const sortedB = [...b].sort();
323
+ return sortedA.every((val, idx) => val === sortedB[idx]);
324
+ }
325
+ //# sourceMappingURL=document-sync-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-sync-adapter.js","sourceRoot":"","sources":["../../../src/external-sync/adapters/document-sync-adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Task Sync Adapter Utilities
3
+ *
4
+ * Shared field mapping logic for converting between Stoneforge tasks and
5
+ * external task representations (e.g., GitHub Issues, Linear issues).
6
+ *
7
+ * These utilities are provider-agnostic — each provider supplies its own
8
+ * TaskSyncFieldMapConfig that tells the adapter how to map statuses,
9
+ * priorities, task types, and labels to/from the external system.
10
+ *
11
+ * Key functions:
12
+ * - taskToExternalTask: Convert a Stoneforge Task → ExternalTaskInput for push
13
+ * - externalTaskToTaskUpdates: Convert an ExternalTask → Partial<Task> for pull
14
+ */
15
+ import type { Task, TaskStatus, Priority, TaskTypeValue, DocumentId, Element, ElementId } from '@stoneforge/core';
16
+ import type { ExternalTask, ExternalTaskInput } from '@stoneforge/core';
17
+ /**
18
+ * Minimal API interface used by task sync adapter utilities.
19
+ * Only requires `get()` for description hydration.
20
+ * Both QuarryAPI and SyncEngineAPI satisfy this interface structurally.
21
+ */
22
+ export interface TaskSyncAPI {
23
+ get<T extends Element>(id: ElementId): Promise<T | null>;
24
+ }
25
+ /**
26
+ * Provider-specific configuration for mapping task fields between
27
+ * Stoneforge and an external system.
28
+ *
29
+ * Each provider (GitHub, Linear, etc.) supplies its own config that
30
+ * describes how Stoneforge concepts map to the provider's label/state model.
31
+ */
32
+ export interface TaskSyncFieldMapConfig {
33
+ /**
34
+ * Maps Stoneforge priority values to external label strings.
35
+ * e.g., { 1: 'priority:critical', 2: 'priority:high', 3: 'priority:medium', ... }
36
+ */
37
+ readonly priorityLabels: Record<Priority, string>;
38
+ /**
39
+ * Maps Stoneforge task type values to external label strings.
40
+ * e.g., { bug: 'type:bug', feature: 'type:feature', task: 'type:task', chore: 'type:chore' }
41
+ */
42
+ readonly taskTypeLabels: Record<TaskTypeValue, string>;
43
+ /**
44
+ * Optional: Maps Stoneforge TaskStatus values to external label strings.
45
+ * When present, status labels are pushed alongside priority and type labels,
46
+ * providing granular status visibility on external systems that only have
47
+ * binary open/closed states (e.g., GitHub).
48
+ *
49
+ * Optional because some providers (e.g., Linear) map status natively
50
+ * via workflow states and don't need label-based status mapping.
51
+ *
52
+ * e.g., { open: 'status:open', in_progress: 'status:in-progress', ... }
53
+ */
54
+ readonly statusLabels?: Record<string, string>;
55
+ /**
56
+ * Prefix for sync-managed labels in the external system.
57
+ * Labels with this prefix are managed by the sync system and will be
58
+ * added/removed automatically. User labels without this prefix are preserved.
59
+ * e.g., 'sf:' would produce labels like 'sf:priority:critical', 'sf:type:bug'
60
+ */
61
+ readonly syncLabelPrefix: string;
62
+ /**
63
+ * Converts a Stoneforge TaskStatus to the external system's state.
64
+ * Most external systems only have 'open' and 'closed' states.
65
+ * e.g., 'in_progress' → 'open', 'closed' → 'closed'
66
+ */
67
+ readonly statusToState: (status: TaskStatus) => 'open' | 'closed';
68
+ /**
69
+ * Converts an external state + labels back to a Stoneforge TaskStatus.
70
+ * Labels are provided to allow more granular status inference
71
+ * (e.g., external 'open' + label 'status:in-progress' → 'in_progress').
72
+ */
73
+ readonly stateToStatus: (state: 'open' | 'closed', labels: string[]) => TaskStatus;
74
+ }
75
+ /**
76
+ * Converts a Stoneforge Task into an ExternalTaskInput for creating/updating
77
+ * an issue in an external system.
78
+ *
79
+ * Handles:
80
+ * - Title mapping (1:1)
81
+ * - Status → state mapping via config.statusToState
82
+ * - Tags → labels (user tags preserved, sync-managed labels added)
83
+ * - Priority → label via config.priorityLabels
84
+ * - Task type → label via config.taskTypeLabels
85
+ * - Description hydration from descriptionRef document via api.get()
86
+ *
87
+ * Note: Assignees are intentionally NOT written to external systems.
88
+ * Stoneforge assignees are ephemeral agents (e.g., el-xxxx) that don't
89
+ * correspond to valid users on external platforms like GitHub.
90
+ *
91
+ * @param task - The Stoneforge task to convert
92
+ * @param config - Provider-specific field mapping configuration
93
+ * @param api - API with get() for hydrating description documents
94
+ * @returns ExternalTaskInput ready for the provider adapter
95
+ */
96
+ export declare function taskToExternalTask(task: Task, config: TaskSyncFieldMapConfig, api: TaskSyncAPI): Promise<ExternalTaskInput>;
97
+ /**
98
+ * Converts an ExternalTask into a partial Task update object for applying
99
+ * external changes to a local Stoneforge task.
100
+ *
101
+ * Handles:
102
+ * - Title mapping (1:1)
103
+ * - State + labels → status mapping via config.stateToStatus
104
+ * - Labels → priority, taskType, tags (separating sync-managed from user labels)
105
+ * - Assignees (first assignee mapped if present)
106
+ *
107
+ * If existingTask is provided, only changed fields are returned (diff mode).
108
+ * If existingTask is undefined, all fields are returned (create mode).
109
+ *
110
+ * Note: Description (body) handling is NOT included in the returned partial.
111
+ * The caller (sync engine) is responsible for creating/updating the description
112
+ * Document and linking via descriptionRef, since that requires document creation
113
+ * which is a separate API operation.
114
+ *
115
+ * @param externalTask - The external task to convert
116
+ * @param existingTask - The existing local task (undefined for new tasks)
117
+ * @param config - Provider-specific field mapping configuration
118
+ * @returns Partial<Task> with only the changed fields (or all fields if no existingTask)
119
+ */
120
+ export declare function externalTaskToTaskUpdates(externalTask: ExternalTask, existingTask: Task | undefined, config: TaskSyncFieldMapConfig): Partial<Task>;
121
+ /**
122
+ * Builds the complete set of external labels for a task.
123
+ *
124
+ * Combines:
125
+ * 1. Sync-managed priority label (prefixed)
126
+ * 2. Sync-managed task type label (prefixed)
127
+ * 3. Sync-managed status label (prefixed, when config.statusLabels is present)
128
+ * 4. User tags from the task (not prefixed — these are user-owned)
129
+ */
130
+ export declare function buildExternalLabels(task: Task, config: TaskSyncFieldMapConfig): string[];
131
+ /**
132
+ * Result of parsing external labels into structured task fields
133
+ */
134
+ export interface ParsedExternalLabels {
135
+ /** Extracted priority value, or undefined if no priority label found */
136
+ priority: Priority | undefined;
137
+ /** Extracted task type value, or undefined if no task type label found */
138
+ taskType: TaskTypeValue | undefined;
139
+ /** Extracted status value, or undefined if no status label found */
140
+ status: TaskStatus | undefined;
141
+ /** Labels that are not sync-managed (user tags) */
142
+ userTags: string[];
143
+ }
144
+ /**
145
+ * Parses external labels into structured task field values.
146
+ *
147
+ * Separates sync-managed labels (prefixed) from user labels,
148
+ * and extracts priority, task type, and status values from the managed labels.
149
+ */
150
+ export declare function parseExternalLabels(labels: readonly string[], config: TaskSyncFieldMapConfig): ParsedExternalLabels;
151
+ /**
152
+ * Hydrates a task's description from its descriptionRef document.
153
+ *
154
+ * If the task has a descriptionRef, fetches the document via the API
155
+ * and returns its content as the body string. Returns undefined if
156
+ * no descriptionRef is set or the document is not found.
157
+ */
158
+ export declare function hydrateDescription(descriptionRef: DocumentId | undefined, api: TaskSyncAPI): Promise<string | undefined>;
159
+ /**
160
+ * Compares a full update against an existing task and returns only
161
+ * the fields that actually changed.
162
+ *
163
+ * This prevents unnecessary updates when pulling changes from external
164
+ * systems where the data hasn't actually changed.
165
+ */
166
+ export declare function diffTaskUpdates(existingTask: Task, fullUpdate: Partial<Task>): Partial<Task>;
167
+ /**
168
+ * Returns the TaskSyncFieldMapConfig for a given provider name.
169
+ *
170
+ * Shared utility used by both the CLI link-all command and the sync engine
171
+ * push path to get the correct field mapping for a provider.
172
+ *
173
+ * @param providerName - The provider name (e.g., 'github', 'linear')
174
+ * @returns The provider-specific TaskSyncFieldMapConfig
175
+ */
176
+ export declare function getFieldMapConfigForProvider(providerName: string): TaskSyncFieldMapConfig;
177
+ //# sourceMappingURL=task-sync-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-sync-adapter.d.ts","sourceRoot":"","sources":["../../../src/external-sync/adapters/task-sync-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EACV,IAAI,EACJ,UAAU,EACV,QAAQ,EACR,aAAa,EACb,UAAU,EAEV,OAAO,EAEP,SAAS,EACV,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AASxE;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,CAAC,SAAS,OAAO,EAAE,EAAE,EAAE,SAAS,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;CAC1D;AAMD;;;;;;GAMG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAElD;;;OAGG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;IAEvD;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE/C;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IAEjC;;;;OAIG;IACH,QAAQ,CAAC,aAAa,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,MAAM,GAAG,QAAQ,CAAC;IAElE;;;;OAIG;IACH,QAAQ,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,UAAU,CAAC;CACpF;AAMD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE,WAAW,GACf,OAAO,CAAC,iBAAiB,CAAC,CAuB5B;AAMD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,YAAY,EAC1B,YAAY,EAAE,IAAI,GAAG,SAAS,EAC9B,MAAM,EAAE,sBAAsB,GAC7B,OAAO,CAAC,IAAI,CAAC,CA8Cf;AAMD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,sBAAsB,GAC7B,MAAM,EAAE,CAwCV;AAMD;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,wEAAwE;IACxE,QAAQ,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC/B,0EAA0E;IAC1E,QAAQ,EAAE,aAAa,GAAG,SAAS,CAAC;IACpC,oEAAoE;IACpE,MAAM,EAAE,UAAU,GAAG,SAAS,CAAC;IAC/B,mDAAmD;IACnD,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,MAAM,EAAE,sBAAsB,GAC7B,oBAAoB,CAiDtB;AAMD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,cAAc,EAAE,UAAU,GAAG,SAAS,EACtC,GAAG,EAAE,WAAW,GACf,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAW7B;AAqCD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,YAAY,EAAE,IAAI,EAClB,UAAU,EAAE,OAAO,CAAC,IAAI,CAAC,GACxB,OAAO,CAAC,IAAI,CAAC,CAoCf;AAsCD;;;;;;;;GAQG;AACH,wBAAgB,4BAA4B,CAAC,YAAY,EAAE,MAAM,GAAG,sBAAsB,CAUzF"}