@thxgg/steward 0.1.24 → 0.1.26

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 (203) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/_nuxt/{Bc2V3wPK.js → B2ow85x_.js} +2 -2
  3. package/.output/public/_nuxt/{U78rMDmo.js → B6CbIr08.js} +1 -1
  4. package/.output/public/_nuxt/{BknRrWsw.js → BLQAF8wp.js} +1 -1
  5. package/.output/public/_nuxt/BXuwtOqb.js +1 -0
  6. package/.output/public/_nuxt/CAMiEhze.js +1 -0
  7. package/.output/public/_nuxt/{BRQ9Cxaw.js → CIBCqZF5.js} +1 -1
  8. package/.output/public/_nuxt/Ce0-nlm9.js +1 -0
  9. package/.output/public/_nuxt/{T11EuTtn.js → ConzneVY.js} +1 -1
  10. package/.output/public/_nuxt/D0qxz_Pn.js +1310 -0
  11. package/.output/public/_nuxt/D3PDtLSF.js +3 -0
  12. package/.output/public/_nuxt/{C73kduX-.js → DdKC0UAK.js} +1 -1
  13. package/.output/public/_nuxt/Detail.BGdvrJGh.css +1 -0
  14. package/.output/public/_nuxt/{C53_p0K1.js → Dkh9ic1y.js} +1 -1
  15. package/.output/public/_nuxt/LEjJTR7-.js +1 -0
  16. package/.output/public/_nuxt/{BTmXUZ_s.js → UqZfMfrZ.js} +1 -1
  17. package/.output/public/_nuxt/builds/latest.json +1 -1
  18. package/.output/public/_nuxt/builds/meta/25438e34-19a2-421d-aede-53fd18f1ccd4.json +1 -0
  19. package/.output/public/_nuxt/dckrK0oj.js +1 -0
  20. package/.output/public/_nuxt/entry.DT4p6_uW.css +1 -0
  21. package/.output/public/_nuxt/pIWeVmPw.js +1 -0
  22. package/.output/public/_nuxt/xrHaPo1U.js +60 -0
  23. package/.output/server/chunks/_/prd-service.mjs.map +1 -1
  24. package/.output/server/chunks/build/{Detail-DMMUwTWr.mjs → Detail-rpcemNXe.mjs} +674 -481
  25. package/.output/server/chunks/build/Detail-rpcemNXe.mjs.map +1 -0
  26. package/.output/server/chunks/build/DiffViewer-styles.B1FB5NJj.mjs +8 -0
  27. package/.output/server/chunks/build/DiffViewer-styles.B1FB5NJj.mjs.map +1 -0
  28. package/.output/server/chunks/build/{_prd_-ByugK4Yi.mjs → _prd_-CeibvZOH.mjs} +67 -233
  29. package/.output/server/chunks/build/_prd_-CeibvZOH.mjs.map +1 -0
  30. package/.output/server/chunks/build/client.precomputed.mjs +1 -1
  31. package/.output/server/chunks/build/{default-BKKgG7HJ.mjs → default-iq8SaDDN.mjs} +3 -3
  32. package/.output/server/chunks/build/default-iq8SaDDN.mjs.map +1 -0
  33. package/.output/server/chunks/build/{error-404-Bf6kdO80.mjs → error-404-DFale9A5.mjs} +2 -2
  34. package/.output/server/chunks/build/error-404-DFale9A5.mjs.map +1 -0
  35. package/.output/server/chunks/build/{index-DE1tjHAd.mjs → index-Po00RvHm.mjs} +2 -2
  36. package/.output/server/chunks/build/index-Po00RvHm.mjs.map +1 -0
  37. package/.output/server/chunks/build/{nuxt-link-SvT1nf8Z.mjs → nuxt-link-B4oWFn7n.mjs} +2 -2
  38. package/.output/server/chunks/build/nuxt-link-B4oWFn7n.mjs.map +1 -0
  39. package/.output/server/chunks/build/{repo-graph-DzT45gSB.mjs → repo-graph-BQVFpA-w.mjs} +5 -4
  40. package/.output/server/chunks/build/repo-graph-BQVFpA-w.mjs.map +1 -0
  41. package/.output/server/chunks/build/server.mjs +7 -7
  42. package/.output/server/chunks/build/styles.mjs +4 -5
  43. package/.output/server/chunks/build/{usePrd-hXZOmvAv.mjs → usePrd-Bb6jlnNZ.mjs} +2 -2
  44. package/.output/server/chunks/build/usePrd-Bb6jlnNZ.mjs.map +1 -0
  45. package/.output/server/chunks/nitro/nitro.mjs +983 -678
  46. package/.output/server/chunks/nitro/nitro.mjs.map +1 -1
  47. package/.output/server/node_modules/@pierre/diffs/dist/components/File.js +324 -0
  48. package/.output/server/node_modules/@pierre/diffs/dist/components/FileDiff.js +395 -0
  49. package/.output/server/node_modules/@pierre/diffs/dist/components/FileStream.js +161 -0
  50. package/.output/server/node_modules/@pierre/diffs/dist/components/web-components.js +25 -0
  51. package/.output/server/node_modules/@pierre/diffs/dist/constants.js +23 -0
  52. package/.output/server/node_modules/@pierre/diffs/dist/highlighter/languages/areLanguagesAttached.js +11 -0
  53. package/.output/server/node_modules/@pierre/diffs/dist/highlighter/languages/attachResolvedLanguages.js +20 -0
  54. package/.output/server/node_modules/@pierre/diffs/dist/highlighter/languages/cleanUpResolvedLanguages.js +11 -0
  55. package/.output/server/node_modules/@pierre/diffs/dist/highlighter/languages/constants.js +8 -0
  56. package/.output/server/node_modules/@pierre/diffs/dist/highlighter/languages/getResolvedLanguages.js +16 -0
  57. package/.output/server/node_modules/@pierre/diffs/dist/highlighter/languages/getResolvedOrResolveLanguage.js +11 -0
  58. package/.output/server/node_modules/@pierre/diffs/dist/highlighter/languages/hasResolvedLanguages.js +11 -0
  59. package/.output/server/node_modules/@pierre/diffs/dist/highlighter/languages/resolveLanguage.js +30 -0
  60. package/.output/server/node_modules/@pierre/diffs/dist/highlighter/languages/resolveLanguages.js +25 -0
  61. package/.output/server/node_modules/@pierre/diffs/dist/highlighter/shared_highlighter.js +71 -0
  62. package/.output/server/node_modules/@pierre/diffs/dist/highlighter/themes/areThemesAttached.js +12 -0
  63. package/.output/server/node_modules/@pierre/diffs/dist/highlighter/themes/attachResolvedThemes.js +24 -0
  64. package/.output/server/node_modules/@pierre/diffs/dist/highlighter/themes/cleanUpResolvedThemes.js +11 -0
  65. package/.output/server/node_modules/@pierre/diffs/dist/highlighter/themes/constants.js +9 -0
  66. package/.output/server/node_modules/@pierre/diffs/dist/highlighter/themes/getResolvedOrResolveTheme.js +11 -0
  67. package/.output/server/node_modules/@pierre/diffs/dist/highlighter/themes/getResolvedThemes.js +16 -0
  68. package/.output/server/node_modules/@pierre/diffs/dist/highlighter/themes/hasResolvedThemes.js +11 -0
  69. package/.output/server/node_modules/@pierre/diffs/dist/highlighter/themes/registerCustomCSSVariableTheme.js +18 -0
  70. package/.output/server/node_modules/@pierre/diffs/dist/highlighter/themes/registerCustomTheme.js +14 -0
  71. package/.output/server/node_modules/@pierre/diffs/dist/highlighter/themes/resolveTheme.js +35 -0
  72. package/.output/server/node_modules/@pierre/diffs/dist/highlighter/themes/resolveThemes.js +21 -0
  73. package/.output/server/node_modules/@pierre/diffs/dist/index.js +84 -0
  74. package/.output/server/node_modules/@pierre/diffs/dist/managers/LineSelectionManager.js +282 -0
  75. package/.output/server/node_modules/@pierre/diffs/dist/managers/MouseEventManager.js +244 -0
  76. package/.output/server/node_modules/@pierre/diffs/dist/managers/ResizeManager.js +132 -0
  77. package/.output/server/node_modules/@pierre/diffs/dist/managers/ScrollSyncManager.js +62 -0
  78. package/.output/server/node_modules/@pierre/diffs/dist/managers/UniversalRenderingManager.js +32 -0
  79. package/.output/server/node_modules/@pierre/diffs/dist/renderers/DiffHunksRenderer.js +655 -0
  80. package/.output/server/node_modules/@pierre/diffs/dist/renderers/FileRenderer.js +243 -0
  81. package/.output/server/node_modules/@pierre/diffs/dist/shiki-stream/stream.js +32 -0
  82. package/.output/server/node_modules/@pierre/diffs/dist/shiki-stream/tokenizer.js +71 -0
  83. package/.output/server/node_modules/@pierre/diffs/dist/sprite.js +55 -0
  84. package/.output/server/node_modules/@pierre/diffs/dist/style.js +6 -0
  85. package/.output/server/node_modules/@pierre/diffs/dist/themes/pierre-dark.js +1328 -0
  86. package/.output/server/node_modules/@pierre/diffs/dist/themes/pierre-light.js +1328 -0
  87. package/.output/server/node_modules/@pierre/diffs/dist/utils/areFilesEqual.js +8 -0
  88. package/.output/server/node_modules/@pierre/diffs/dist/utils/areObjectsEqual.js +18 -0
  89. package/.output/server/node_modules/@pierre/diffs/dist/utils/areOptionsEqual.js +12 -0
  90. package/.output/server/node_modules/@pierre/diffs/dist/utils/areSelectionsEqual.js +8 -0
  91. package/.output/server/node_modules/@pierre/diffs/dist/utils/areThemesEqual.js +9 -0
  92. package/.output/server/node_modules/@pierre/diffs/dist/utils/cleanLastNewline.js +8 -0
  93. package/.output/server/node_modules/@pierre/diffs/dist/utils/createAnnotationElement.js +21 -0
  94. package/.output/server/node_modules/@pierre/diffs/dist/utils/createAnnotationWrapperNode.js +12 -0
  95. package/.output/server/node_modules/@pierre/diffs/dist/utils/createCodeNode.js +12 -0
  96. package/.output/server/node_modules/@pierre/diffs/dist/utils/createEmptyRowBuffer.js +16 -0
  97. package/.output/server/node_modules/@pierre/diffs/dist/utils/createFileHeaderElement.js +84 -0
  98. package/.output/server/node_modules/@pierre/diffs/dist/utils/createHoverContentNode.js +15 -0
  99. package/.output/server/node_modules/@pierre/diffs/dist/utils/createNoNewlineElement.js +24 -0
  100. package/.output/server/node_modules/@pierre/diffs/dist/utils/createPreElement.js +28 -0
  101. package/.output/server/node_modules/@pierre/diffs/dist/utils/createRowNodes.js +20 -0
  102. package/.output/server/node_modules/@pierre/diffs/dist/utils/createSeparator.js +69 -0
  103. package/.output/server/node_modules/@pierre/diffs/dist/utils/createSpanNodeFromToken.js +13 -0
  104. package/.output/server/node_modules/@pierre/diffs/dist/utils/createStyleElement.js +19 -0
  105. package/.output/server/node_modules/@pierre/diffs/dist/utils/createTransformerWithState.js +56 -0
  106. package/.output/server/node_modules/@pierre/diffs/dist/utils/createUnsafeCSSStyleNode.js +12 -0
  107. package/.output/server/node_modules/@pierre/diffs/dist/utils/cssWrappers.js +21 -0
  108. package/.output/server/node_modules/@pierre/diffs/dist/utils/diffAcceptRejectHunk.js +82 -0
  109. package/.output/server/node_modules/@pierre/diffs/dist/utils/formatCSSVariablePrefix.js +8 -0
  110. package/.output/server/node_modules/@pierre/diffs/dist/utils/getFiletypeFromFileName.js +343 -0
  111. package/.output/server/node_modules/@pierre/diffs/dist/utils/getHighlighterOptions.js +13 -0
  112. package/.output/server/node_modules/@pierre/diffs/dist/utils/getHighlighterThemeStyles.js +40 -0
  113. package/.output/server/node_modules/@pierre/diffs/dist/utils/getHunkSeparatorSlotName.js +8 -0
  114. package/.output/server/node_modules/@pierre/diffs/dist/utils/getIconForType.js +15 -0
  115. package/.output/server/node_modules/@pierre/diffs/dist/utils/getLineAnnotationName.js +8 -0
  116. package/.output/server/node_modules/@pierre/diffs/dist/utils/getLineEndingType.js +11 -0
  117. package/.output/server/node_modules/@pierre/diffs/dist/utils/getLineNodes.js +15 -0
  118. package/.output/server/node_modules/@pierre/diffs/dist/utils/getSingularPatch.js +20 -0
  119. package/.output/server/node_modules/@pierre/diffs/dist/utils/getThemes.js +16 -0
  120. package/.output/server/node_modules/@pierre/diffs/dist/utils/getTotalLineCountFromHunks.js +10 -0
  121. package/.output/server/node_modules/@pierre/diffs/dist/utils/hast_utils.js +42 -0
  122. package/.output/server/node_modules/@pierre/diffs/dist/utils/isWorkerContext.js +8 -0
  123. package/.output/server/node_modules/@pierre/diffs/dist/utils/parseDiffDecorations.js +34 -0
  124. package/.output/server/node_modules/@pierre/diffs/dist/utils/parseDiffFromFile.js +23 -0
  125. package/.output/server/node_modules/@pierre/diffs/dist/utils/parseLineType.js +17 -0
  126. package/.output/server/node_modules/@pierre/diffs/dist/utils/parsePatchFiles.js +211 -0
  127. package/.output/server/node_modules/@pierre/diffs/dist/utils/prerenderHTMLIfNecessary.js +10 -0
  128. package/.output/server/node_modules/@pierre/diffs/dist/utils/processLine.js +42 -0
  129. package/.output/server/node_modules/@pierre/diffs/dist/utils/renderDiffWithHighlighter.js +339 -0
  130. package/.output/server/node_modules/@pierre/diffs/dist/utils/renderFileWithHighlighter.js +52 -0
  131. package/.output/server/node_modules/@pierre/diffs/dist/utils/setLanguageOverride.js +11 -0
  132. package/.output/server/node_modules/@pierre/diffs/dist/utils/setWrapperNodeProps.js +29 -0
  133. package/.output/server/node_modules/@pierre/diffs/package.json +89 -0
  134. package/.output/server/node_modules/@shikijs/transformers/dist/index.mjs +831 -0
  135. package/.output/server/node_modules/@shikijs/transformers/package.json +37 -0
  136. package/.output/server/node_modules/diff/libesm/convert/dmp.js +21 -0
  137. package/.output/server/node_modules/diff/libesm/convert/xml.js +31 -0
  138. package/.output/server/node_modules/diff/libesm/diff/array.js +16 -0
  139. package/.output/server/node_modules/diff/libesm/diff/base.js +253 -0
  140. package/.output/server/node_modules/diff/libesm/diff/character.js +7 -0
  141. package/.output/server/node_modules/diff/libesm/diff/css.js +10 -0
  142. package/.output/server/node_modules/diff/libesm/diff/json.js +78 -0
  143. package/.output/server/node_modules/diff/libesm/diff/line.js +65 -0
  144. package/.output/server/node_modules/diff/libesm/diff/sentence.js +43 -0
  145. package/.output/server/node_modules/diff/libesm/diff/word.js +296 -0
  146. package/.output/server/node_modules/diff/libesm/index.js +30 -0
  147. package/.output/server/node_modules/diff/libesm/package.json +1 -0
  148. package/.output/server/node_modules/diff/libesm/patch/apply.js +257 -0
  149. package/.output/server/node_modules/diff/libesm/patch/create.js +228 -0
  150. package/.output/server/node_modules/diff/libesm/patch/line-endings.js +44 -0
  151. package/.output/server/node_modules/diff/libesm/patch/parse.js +147 -0
  152. package/.output/server/node_modules/diff/libesm/patch/reverse.js +23 -0
  153. package/.output/server/node_modules/diff/libesm/util/distance-iterator.js +37 -0
  154. package/.output/server/node_modules/diff/libesm/util/params.js +14 -0
  155. package/.output/server/node_modules/diff/libesm/util/string.js +128 -0
  156. package/.output/server/node_modules/diff/package.json +132 -0
  157. package/.output/server/package.json +4 -1
  158. package/README.md +41 -0
  159. package/bin/prd +1 -1
  160. package/dist/host/src/index.js +10 -0
  161. package/dist/host/src/sync.js +201 -0
  162. package/dist/server/utils/db.js +64 -0
  163. package/dist/server/utils/git.js +8 -6
  164. package/dist/server/utils/prd-state.js +24 -2
  165. package/dist/server/utils/repos.js +12 -2
  166. package/dist/server/utils/state-migration.js +4 -3
  167. package/dist/server/utils/sync-apply.js +380 -0
  168. package/dist/server/utils/sync-export.js +183 -0
  169. package/dist/server/utils/sync-identity.js +231 -0
  170. package/dist/server/utils/sync-inspect.js +103 -0
  171. package/dist/server/utils/sync-merge.js +579 -0
  172. package/dist/server/utils/sync-schema.js +100 -0
  173. package/package.json +2 -1
  174. package/.output/public/_nuxt/6tINjQEd.js +0 -141
  175. package/.output/public/_nuxt/B2mIQf5X.js +0 -3
  176. package/.output/public/_nuxt/C0BBSDJ7.js +0 -1
  177. package/.output/public/_nuxt/CN46Bgts.js +0 -1
  178. package/.output/public/_nuxt/CTJgb0zb.js +0 -1
  179. package/.output/public/_nuxt/Cce168lk.js +0 -30
  180. package/.output/public/_nuxt/CyVSeLw5.js +0 -1
  181. package/.output/public/_nuxt/Detail.CYc96mGf.css +0 -1
  182. package/.output/public/_nuxt/ZNypZshD.js +0 -13
  183. package/.output/public/_nuxt/builds/meta/8c342d49-fe70-4f67-a987-821c16f86125.json +0 -1
  184. package/.output/public/_nuxt/entry.Bw0CE6Iz.css +0 -1
  185. package/.output/public/_nuxt/pYJYAY-W.js +0 -60
  186. package/.output/server/chunks/build/Detail-DMMUwTWr.mjs.map +0 -1
  187. package/.output/server/chunks/build/DiffViewer-styles-1.mjs-d2dQvARr.mjs +0 -4
  188. package/.output/server/chunks/build/DiffViewer-styles-1.mjs-d2dQvARr.mjs.map +0 -1
  189. package/.output/server/chunks/build/DiffViewer-styles-2.mjs-X6QKNjM0.mjs +0 -4
  190. package/.output/server/chunks/build/DiffViewer-styles-2.mjs-X6QKNjM0.mjs.map +0 -1
  191. package/.output/server/chunks/build/DiffViewer-styles.0AbHFl6N.mjs +0 -8
  192. package/.output/server/chunks/build/DiffViewer-styles.0AbHFl6N.mjs.map +0 -1
  193. package/.output/server/chunks/build/DiffViewer-styles.BDwAqkTk.mjs +0 -8
  194. package/.output/server/chunks/build/DiffViewer-styles.BDwAqkTk.mjs.map +0 -1
  195. package/.output/server/chunks/build/DiffViewer-styles.DRJh5Ui4.mjs +0 -10
  196. package/.output/server/chunks/build/DiffViewer-styles.DRJh5Ui4.mjs.map +0 -1
  197. package/.output/server/chunks/build/_prd_-ByugK4Yi.mjs.map +0 -1
  198. package/.output/server/chunks/build/default-BKKgG7HJ.mjs.map +0 -1
  199. package/.output/server/chunks/build/error-404-Bf6kdO80.mjs.map +0 -1
  200. package/.output/server/chunks/build/index-DE1tjHAd.mjs.map +0 -1
  201. package/.output/server/chunks/build/nuxt-link-SvT1nf8Z.mjs.map +0 -1
  202. package/.output/server/chunks/build/repo-graph-DzT45gSB.mjs.map +0 -1
  203. package/.output/server/chunks/build/usePrd-hXZOmvAv.mjs.map +0 -1
@@ -0,0 +1,132 @@
1
+ {
2
+ "name": "diff",
3
+ "version": "8.0.3",
4
+ "description": "A JavaScript text diff implementation.",
5
+ "keywords": [
6
+ "diff",
7
+ "jsdiff",
8
+ "compare",
9
+ "patch",
10
+ "text",
11
+ "json",
12
+ "css",
13
+ "javascript"
14
+ ],
15
+ "maintainers": [
16
+ "Kevin Decker <kpdecker@gmail.com> (http://incaseofstairs.com)",
17
+ "Mark Amery <markrobertamery+jsdiff@gmail.com>"
18
+ ],
19
+ "bugs": {
20
+ "email": "kpdecker@gmail.com",
21
+ "url": "http://github.com/kpdecker/jsdiff/issues"
22
+ },
23
+ "license": "BSD-3-Clause",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/kpdecker/jsdiff.git"
27
+ },
28
+ "engines": {
29
+ "node": ">=0.3.1"
30
+ },
31
+ "main": "./libcjs/index.js",
32
+ "module": "./libesm/index.js",
33
+ "browser": "./dist/diff.js",
34
+ "unpkg": "./dist/diff.js",
35
+ "exports": {
36
+ ".": {
37
+ "import": {
38
+ "types": "./libesm/index.d.ts",
39
+ "default": "./libesm/index.js"
40
+ },
41
+ "require": {
42
+ "types": "./libcjs/index.d.ts",
43
+ "default": "./libcjs/index.js"
44
+ }
45
+ },
46
+ "./package.json": "./package.json",
47
+ "./lib/*.js": {
48
+ "import": {
49
+ "types": "./libesm/*.d.ts",
50
+ "default": "./libesm/*.js"
51
+ },
52
+ "require": {
53
+ "types": "./libcjs/*.d.ts",
54
+ "default": "./libcjs/*.js"
55
+ }
56
+ },
57
+ "./lib/": {
58
+ "import": {
59
+ "types": "./libesm/",
60
+ "default": "./libesm/"
61
+ },
62
+ "require": {
63
+ "types": "./libcjs/",
64
+ "default": "./libcjs/"
65
+ }
66
+ }
67
+ },
68
+ "type": "module",
69
+ "types": "libcjs/index.d.ts",
70
+ "scripts": {
71
+ "clean": "rm -rf libcjs/ libesm/ dist/ coverage/ .nyc_output/",
72
+ "lint": "yarn eslint",
73
+ "build": "yarn lint && yarn generate-esm && yarn generate-cjs && yarn check-types && yarn run-rollup && yarn run-uglify",
74
+ "generate-cjs": "yarn tsc --module commonjs --outDir libcjs && node --eval \"fs.writeFileSync('libcjs/package.json', JSON.stringify({type:'commonjs',sideEffects:false}))\"",
75
+ "generate-esm": "yarn tsc --module nodenext --outDir libesm --target es6 && node --eval \"fs.writeFileSync('libesm/package.json', JSON.stringify({type:'module',sideEffects:false}))\"",
76
+ "check-types": "yarn run-tsd && yarn run-attw",
77
+ "test": "nyc yarn _test",
78
+ "_test": "yarn build && cross-env NODE_ENV=test yarn run-mocha",
79
+ "run-attw": "yarn attw --pack --entrypoints . && yarn attw --pack --entrypoints lib/diff/word.js --profile node16",
80
+ "run-tsd": "yarn tsd --typings libesm/ && yarn tsd --files test-d/",
81
+ "run-rollup": "rollup -c rollup.config.mjs",
82
+ "run-uglify": "uglifyjs dist/diff.js -c -o dist/diff.min.js",
83
+ "run-mocha": "mocha --require ./runtime 'test/**/*.js'"
84
+ },
85
+ "devDependencies": {
86
+ "@arethetypeswrong/cli": "^0.17.4",
87
+ "@babel/core": "^7.26.9",
88
+ "@babel/preset-env": "^7.26.9",
89
+ "@babel/register": "^7.25.9",
90
+ "@colors/colors": "^1.6.0",
91
+ "@eslint/js": "^9.25.1",
92
+ "babel-loader": "^10.0.0",
93
+ "babel-plugin-istanbul": "^7.0.0",
94
+ "chai": "^5.2.0",
95
+ "cross-env": "^7.0.3",
96
+ "eslint": "^9.25.1",
97
+ "globals": "^16.0.0",
98
+ "karma": "^6.4.4",
99
+ "karma-mocha": "^2.0.1",
100
+ "karma-mocha-reporter": "^2.2.5",
101
+ "karma-sourcemap-loader": "^0.4.0",
102
+ "karma-webpack": "^5.0.1",
103
+ "mocha": "^11.1.0",
104
+ "nyc": "^17.1.0",
105
+ "rollup": "^4.40.1",
106
+ "tsd": "^0.32.0",
107
+ "typescript": "^5.8.3",
108
+ "typescript-eslint": "^8.31.0",
109
+ "uglify-js": "^3.19.3",
110
+ "webpack": "^5.99.7",
111
+ "webpack-dev-server": "^5.2.1"
112
+ },
113
+ "optionalDependencies": {},
114
+ "dependencies": {},
115
+ "nyc": {
116
+ "require": [
117
+ "@babel/register"
118
+ ],
119
+ "reporter": [
120
+ "lcov",
121
+ "text"
122
+ ],
123
+ "sourceMap": false,
124
+ "instrument": false,
125
+ "check-coverage": true,
126
+ "branches": 100,
127
+ "lines": 100,
128
+ "functions": 100,
129
+ "statements": 100
130
+ },
131
+ "packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610"
132
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thxgg/steward-prod",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "dependencies": {
@@ -11,11 +11,13 @@
11
11
  "@floating-ui/vue": "1.1.10",
12
12
  "@internationalized/date": "3.11.0",
13
13
  "@internationalized/number": "3.6.5",
14
+ "@pierre/diffs": "1.0.11",
14
15
  "@shikijs/core": "3.22.0",
15
16
  "@shikijs/engine-javascript": "3.22.0",
16
17
  "@shikijs/engine-oniguruma": "3.22.0",
17
18
  "@shikijs/langs": "3.22.0",
18
19
  "@shikijs/themes": "3.22.0",
20
+ "@shikijs/transformers": "3.23.0",
19
21
  "@shikijs/types": "3.22.0",
20
22
  "@shikijs/vscode-textmate": "10.0.2",
21
23
  "@swc/helpers": "0.5.18",
@@ -45,6 +47,7 @@
45
47
  "comma-separated-tokens": "2.0.3",
46
48
  "defu": "6.1.4",
47
49
  "devalue": "5.6.3",
50
+ "diff": "8.0.3",
48
51
  "dompurify": "3.3.1",
49
52
  "entities": "7.0.1",
50
53
  "estree-walker": "2.0.2",
package/README.md CHANGED
@@ -58,14 +58,55 @@ Note: `execute` runs in a VM sandbox by design, so globals like `process` are in
58
58
  prd ui
59
59
  prd ui --port 3100 --host 127.0.0.1
60
60
  prd mcp
61
+ prd sync export ./steward-sync.json
62
+ prd sync inspect ./steward-sync.json
63
+ prd sync merge ./steward-sync.json
64
+ prd sync merge ./steward-sync.json --apply
65
+ prd sync merge ./steward-sync.json --apply --map rsk_source=/Users/you/Projects/repo
61
66
  ```
62
67
 
68
+ ### Sync Bundles (Cross-Device)
69
+
70
+ Steward supports local-first state sharing across devices using portable JSON bundles.
71
+
72
+ - `prd sync export <bundle-path>` writes a versioned bundle of repos/state/archives.
73
+ - `prd sync inspect <bundle-path>` validates and summarizes bundle contents.
74
+ - `prd sync merge <bundle-path>` plans a merge in dry-run mode by default.
75
+ - `prd sync merge <bundle-path> --apply` applies the planned merge transactionally.
76
+
77
+ Path hint privacy defaults:
78
+
79
+ - Export defaults to `--path-hints basename` to avoid leaking absolute filesystem paths.
80
+ - Use `--path-hints none` to omit path hints entirely.
81
+ - Use `--path-hints absolute` only when you explicitly want full paths in the bundle.
82
+
83
+ Representative command output summaries:
84
+
85
+ - `sync export` prints bundle path, bundle id, and row totals (`repos/states/archives`).
86
+ - `sync inspect` prints bundle metadata (`bundleId`, `sourceDeviceId`, format version), totals, and unknown references.
87
+ - `sync merge` prints mapping totals, state/archive action counts, and conflict counts.
88
+ - `sync merge --apply` also prints backup path and retention cleanup counts.
89
+
90
+ Safety defaults and retention:
91
+
92
+ - Merge defaults to dry-run; no writes happen unless `--apply` is provided.
93
+ - Apply creates a SQLite backup before any write and runs an integrity check before commit.
94
+ - Re-applying the same bundle id is idempotent and returns a no-op result.
95
+ - Default retention keeps backups for 30 days (max 20 files) and sync apply logs for 180 days (max 10,000 rows).
96
+
97
+ Troubleshooting sync:
98
+
99
+ - Unresolved mapping on apply: run `prd sync inspect <bundle-path>` and pass one or more `--map <incomingRepoSyncKey>=<localPathOrRepoRef>` values.
100
+ - Expected no-op reapply: if bundle id was already applied, merge returns "already applied" and leaves state unchanged.
101
+ - Restore from backup: stop Steward processes, then replace your DB file with a backup in the same directory (files match `state.db.sync-backup.*.db`).
102
+
63
103
  ## Architecture
64
104
 
65
105
  ```
66
106
  ┌─────────────────────────────────────────┐
67
107
  │ Steward CLI (Node) │
68
108
  │ - `prd ui` runs prebuilt UI server │
109
+ │ - `prd sync` manages state bundles │
69
110
  │ - `prd mcp` starts MCP over stdio │
70
111
  └─────────────────────────────────────────┘
71
112
 
package/bin/prd CHANGED
@@ -12,7 +12,7 @@ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..')
12
12
  const entryPath = resolve(packageRoot, 'dist', 'host', 'src', 'index.js')
13
13
 
14
14
  function requiresSqliteRuntime(command) {
15
- return command === 'mcp' || command === 'ui'
15
+ return command === 'mcp' || command === 'ui' || command === 'sync'
16
16
  }
17
17
 
18
18
  function isSqliteRuntimeError(error) {
@@ -1,14 +1,17 @@
1
1
  import { runMcpServer } from './mcp.js';
2
+ import { runSync } from './sync.js';
2
3
  import { runUi } from './ui.js';
3
4
  function printUsage() {
4
5
  console.log(`prd - Steward CLI
5
6
 
6
7
  Usage:
7
8
  prd ui [--preview] [--port <port>] [--host <host>]
9
+ prd sync <subcommand> [options]
8
10
  prd mcp
9
11
 
10
12
  Commands:
11
13
  ui Launch the prebuilt PRD web UI server
14
+ sync Export/inspect/merge sync bundles
12
15
  mcp Start MCP server over stdio (codemode)
13
16
 
14
17
  Options:
@@ -84,6 +87,13 @@ export async function main(argv = process.argv.slice(2)) {
84
87
  }
85
88
  return;
86
89
  }
90
+ if (command === 'sync') {
91
+ const exitCode = await runSync(rest);
92
+ if (exitCode !== 0) {
93
+ process.exitCode = exitCode;
94
+ }
95
+ return;
96
+ }
87
97
  throw new Error(`Unknown command: ${command}`);
88
98
  }
89
99
  if (import.meta.main) {
@@ -0,0 +1,201 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import { dirname, resolve } from 'node:path';
3
+ import { buildSyncBundle, serializeSyncBundle } from '../../server/utils/sync-export.js';
4
+ import { executeSyncMergeJson } from '../../server/utils/sync-apply.js';
5
+ import { inspectSyncBundleJson } from '../../server/utils/sync-inspect.js';
6
+ function printSyncUsage() {
7
+ console.log(`Sync Usage:
8
+ prd sync export <bundle-path> [--path-hints basename|none|absolute]
9
+ prd sync inspect <bundle-path>
10
+ prd sync merge <bundle-path> [--map <incomingRepoSyncKey>=<localPathOrRepoRef>] [--dry-run] [--apply]
11
+
12
+ Notes:
13
+ - merge defaults to --dry-run when --apply is omitted
14
+ - --map can be provided multiple times
15
+ `);
16
+ }
17
+ function resolveBundlePath(pathInput) {
18
+ return resolve(process.cwd(), pathInput);
19
+ }
20
+ function parsePathHintsMode(rawValue) {
21
+ if (rawValue === 'basename' || rawValue === 'none' || rawValue === 'absolute') {
22
+ return rawValue;
23
+ }
24
+ throw new Error(`Invalid --path-hints value: ${rawValue}`);
25
+ }
26
+ function parseMapAssignment(rawValue) {
27
+ const separatorIndex = rawValue.indexOf('=');
28
+ if (separatorIndex <= 0 || separatorIndex >= rawValue.length - 1) {
29
+ throw new Error(`Invalid --map value: ${rawValue}. Expected <incomingRepoSyncKey>=<localPathOrRepoRef>`);
30
+ }
31
+ const source = rawValue.slice(0, separatorIndex).trim();
32
+ const target = rawValue.slice(separatorIndex + 1).trim();
33
+ if (!source || !target) {
34
+ throw new Error(`Invalid --map value: ${rawValue}. Expected <incomingRepoSyncKey>=<localPathOrRepoRef>`);
35
+ }
36
+ return { source, target };
37
+ }
38
+ export function parseSyncExportArgs(args) {
39
+ let bundlePath = null;
40
+ let pathHints = 'basename';
41
+ for (let index = 0; index < args.length; index += 1) {
42
+ const arg = args[index];
43
+ if (!arg) {
44
+ continue;
45
+ }
46
+ if (arg === '--path-hints') {
47
+ const next = args[index + 1];
48
+ if (!next) {
49
+ throw new Error('--path-hints requires a value');
50
+ }
51
+ pathHints = parsePathHintsMode(next);
52
+ index += 1;
53
+ continue;
54
+ }
55
+ if (arg.startsWith('--')) {
56
+ throw new Error(`Unknown option for sync export: ${arg}`);
57
+ }
58
+ if (bundlePath !== null) {
59
+ throw new Error(`Unexpected argument for sync export: ${arg}`);
60
+ }
61
+ bundlePath = arg;
62
+ }
63
+ if (!bundlePath) {
64
+ throw new Error('sync export requires <bundle-path>');
65
+ }
66
+ return {
67
+ bundlePath: resolveBundlePath(bundlePath),
68
+ pathHints
69
+ };
70
+ }
71
+ export function parseSyncInspectArgs(args) {
72
+ const [bundlePath] = args;
73
+ if (args.length !== 1 || !bundlePath || bundlePath.startsWith('--')) {
74
+ throw new Error('sync inspect requires exactly one <bundle-path>');
75
+ }
76
+ return {
77
+ bundlePath: resolveBundlePath(bundlePath)
78
+ };
79
+ }
80
+ export function parseSyncMergeArgs(args) {
81
+ let bundlePath = null;
82
+ let apply = false;
83
+ let dryRun = false;
84
+ const repoMap = {};
85
+ for (let index = 0; index < args.length; index += 1) {
86
+ const arg = args[index];
87
+ if (!arg) {
88
+ continue;
89
+ }
90
+ if (arg === '--apply') {
91
+ apply = true;
92
+ continue;
93
+ }
94
+ if (arg === '--dry-run') {
95
+ dryRun = true;
96
+ continue;
97
+ }
98
+ if (arg === '--map') {
99
+ const next = args[index + 1];
100
+ if (!next) {
101
+ throw new Error('--map requires a value');
102
+ }
103
+ const { source, target } = parseMapAssignment(next);
104
+ repoMap[source] = target;
105
+ index += 1;
106
+ continue;
107
+ }
108
+ if (arg.startsWith('--')) {
109
+ throw new Error(`Unknown option for sync merge: ${arg}`);
110
+ }
111
+ if (bundlePath !== null) {
112
+ throw new Error(`Unexpected argument for sync merge: ${arg}`);
113
+ }
114
+ bundlePath = arg;
115
+ }
116
+ if (!bundlePath) {
117
+ throw new Error('sync merge requires <bundle-path>');
118
+ }
119
+ if (apply && dryRun) {
120
+ throw new Error('Cannot use --apply and --dry-run together');
121
+ }
122
+ return {
123
+ bundlePath: resolveBundlePath(bundlePath),
124
+ apply,
125
+ repoMap
126
+ };
127
+ }
128
+ async function runSyncExport(args) {
129
+ const parsed = parseSyncExportArgs(args);
130
+ const bundle = await buildSyncBundle({
131
+ pathHints: parsed.pathHints
132
+ });
133
+ await fs.mkdir(dirname(parsed.bundlePath), { recursive: true });
134
+ await fs.writeFile(parsed.bundlePath, `${serializeSyncBundle(bundle)}\n`, 'utf-8');
135
+ console.log(`[steward] Exported bundle: ${parsed.bundlePath}`);
136
+ console.log(`[steward] Bundle ID: ${bundle.bundleId}`);
137
+ console.log(`[steward] Rows: repos=${bundle.repos.length} states=${bundle.states.length} archives=${bundle.archives.length}`);
138
+ return 0;
139
+ }
140
+ async function runSyncInspect(args) {
141
+ const parsed = parseSyncInspectArgs(args);
142
+ const jsonPayload = await fs.readFile(parsed.bundlePath, 'utf-8');
143
+ const inspection = inspectSyncBundleJson(jsonPayload);
144
+ console.log(`[steward] Bundle: ${parsed.bundlePath}`);
145
+ console.log(`[steward] ID=${inspection.bundleId} sourceDevice=${inspection.sourceDeviceId} format=v${inspection.formatVersion}`);
146
+ console.log(`[steward] Totals: repos=${inspection.totals.repos} states=${inspection.totals.states} archives=${inspection.totals.archives}`);
147
+ console.log(`[steward] Unknown references: states=${inspection.totals.unknownRepoStates} archives=${inspection.totals.unknownRepoArchives}`);
148
+ for (const repo of inspection.repos) {
149
+ const hint = repo.pathHint ? ` pathHint=${repo.pathHint}` : '';
150
+ console.log(`[steward] Repo ${repo.repoSyncKey} (${repo.name})${hint} states=${repo.stateCount} archives=${repo.archiveCount}`);
151
+ }
152
+ return 0;
153
+ }
154
+ async function runSyncMerge(args) {
155
+ const parsed = parseSyncMergeArgs(args);
156
+ const jsonPayload = await fs.readFile(parsed.bundlePath, 'utf-8');
157
+ const result = await executeSyncMergeJson(jsonPayload, {
158
+ apply: parsed.apply,
159
+ repoMap: parsed.repoMap
160
+ });
161
+ const mode = result.mode === 'dry_run' ? 'dry-run' : 'apply';
162
+ console.log(`[steward] Merge mode: ${mode}`);
163
+ console.log(`[steward] Bundle ID: ${result.bundleId}`);
164
+ console.log(`[steward] Repo mapping: mapped=${result.plan.summary.repos.mapped} unresolved=${result.plan.summary.repos.unresolved}`);
165
+ console.log(`[steward] State actions: insert=${result.plan.summary.states.insert} update=${result.plan.summary.states.update} skip=${result.plan.summary.states.skip} unresolved=${result.plan.summary.states.unresolved} conflicts=${result.plan.summary.states.conflicts}`);
166
+ console.log(`[steward] Archive actions: insert=${result.plan.summary.archives.insert} update=${result.plan.summary.archives.update} skip=${result.plan.summary.archives.skip} unresolved=${result.plan.summary.archives.unresolved}`);
167
+ if (result.mode === 'apply') {
168
+ if (result.alreadyApplied) {
169
+ console.log('[steward] Bundle already applied; no changes were written.');
170
+ }
171
+ else {
172
+ console.log(`[steward] Applied: ${result.applied ? 'yes' : 'no'}`);
173
+ if (result.backupPath) {
174
+ console.log(`[steward] Backup: ${result.backupPath}`);
175
+ }
176
+ console.log(`[steward] Retention: backupsDeleted=${result.retention.backupsDeleted} logsDeleted=${result.retention.logsDeleted}`);
177
+ }
178
+ }
179
+ else {
180
+ console.log('[steward] Dry-run only; re-run with --apply to write changes.');
181
+ }
182
+ return 0;
183
+ }
184
+ export async function runSync(args) {
185
+ const [subcommandRaw, ...rest] = args;
186
+ const subcommand = subcommandRaw || '';
187
+ if (subcommand === '' || subcommand === '-h' || subcommand === '--help') {
188
+ printSyncUsage();
189
+ return 0;
190
+ }
191
+ if (subcommand === 'export') {
192
+ return await runSyncExport(rest);
193
+ }
194
+ if (subcommand === 'inspect') {
195
+ return await runSyncInspect(rest);
196
+ }
197
+ if (subcommand === 'merge') {
198
+ return await runSyncMerge(rest);
199
+ }
200
+ throw new Error(`Unknown sync subcommand: ${subcommandRaw}`);
201
+ }
@@ -34,6 +34,47 @@ function resolveDbPath() {
34
34
  }
35
35
  return DEFAULT_DB_PATH;
36
36
  }
37
+ function getTableColumnNames(adapter, tableName) {
38
+ const rows = adapter.all(`PRAGMA table_info(${tableName})`);
39
+ const names = new Set();
40
+ for (const row of rows) {
41
+ if (typeof row.name === 'string' && row.name.length > 0) {
42
+ names.add(row.name);
43
+ }
44
+ }
45
+ return names;
46
+ }
47
+ function ensurePrdStateFieldClockColumns(adapter) {
48
+ const columnNames = getTableColumnNames(adapter, 'prd_states');
49
+ const requiredColumns = ['tasks_updated_at', 'progress_updated_at', 'notes_updated_at'];
50
+ for (const columnName of requiredColumns) {
51
+ if (columnNames.has(columnName)) {
52
+ continue;
53
+ }
54
+ adapter.exec(`ALTER TABLE prd_states ADD COLUMN ${columnName} TEXT;`);
55
+ columnNames.add(columnName);
56
+ }
57
+ }
58
+ function backfillPrdStateFieldClocks(adapter) {
59
+ adapter.run(`
60
+ UPDATE prd_states
61
+ SET tasks_updated_at = updated_at
62
+ WHERE tasks_updated_at IS NULL
63
+ AND tasks_json IS NOT NULL
64
+ `);
65
+ adapter.run(`
66
+ UPDATE prd_states
67
+ SET progress_updated_at = updated_at
68
+ WHERE progress_updated_at IS NULL
69
+ AND progress_json IS NOT NULL
70
+ `);
71
+ adapter.run(`
72
+ UPDATE prd_states
73
+ SET notes_updated_at = updated_at
74
+ WHERE notes_updated_at IS NULL
75
+ AND notes_md IS NOT NULL
76
+ `);
77
+ }
37
78
  function formatNodeRuntimeHint() {
38
79
  const nodeOptions = process.env.NODE_OPTIONS || '';
39
80
  const hasDisableFlag = process.execArgv.includes(SQLITE_DISABLE_FLAG)
@@ -177,6 +218,9 @@ async function initializeDatabase() {
177
218
  tasks_json TEXT,
178
219
  progress_json TEXT,
179
220
  notes_md TEXT,
221
+ tasks_updated_at TEXT,
222
+ progress_updated_at TEXT,
223
+ notes_updated_at TEXT,
180
224
  updated_at TEXT NOT NULL,
181
225
  PRIMARY KEY (repo_id, slug),
182
226
  FOREIGN KEY (repo_id) REFERENCES repos(id) ON DELETE CASCADE
@@ -196,9 +240,29 @@ async function initializeDatabase() {
196
240
  updated_at TEXT NOT NULL
197
241
  );
198
242
 
243
+ CREATE TABLE IF NOT EXISTS repo_sync_meta (
244
+ repo_id TEXT PRIMARY KEY,
245
+ sync_key TEXT NOT NULL UNIQUE,
246
+ fingerprint TEXT,
247
+ fingerprint_kind TEXT,
248
+ updated_at TEXT NOT NULL,
249
+ FOREIGN KEY (repo_id) REFERENCES repos(id) ON DELETE CASCADE
250
+ );
251
+
252
+ CREATE TABLE IF NOT EXISTS sync_bundle_log (
253
+ bundle_id TEXT PRIMARY KEY,
254
+ source_device_id TEXT,
255
+ applied_at TEXT NOT NULL,
256
+ summary_json TEXT NOT NULL
257
+ );
258
+
199
259
  CREATE INDEX IF NOT EXISTS idx_prd_states_repo_id ON prd_states(repo_id);
200
260
  CREATE INDEX IF NOT EXISTS idx_prd_archives_repo_id ON prd_archives(repo_id);
261
+ CREATE INDEX IF NOT EXISTS idx_repo_sync_meta_sync_key ON repo_sync_meta(sync_key);
262
+ CREATE INDEX IF NOT EXISTS idx_sync_bundle_log_applied_at ON sync_bundle_log(applied_at);
201
263
  `);
264
+ ensurePrdStateFieldClockColumns(adapter);
265
+ backfillPrdStateFieldClocks(adapter);
202
266
  return adapter;
203
267
  }
204
268
  async function getAdapter() {
@@ -168,12 +168,13 @@ export async function getCommitInfo(repoPath, sha) {
168
168
  throw new Error(`Invalid commit SHA: ${sha}`);
169
169
  }
170
170
  // Get commit details
171
- const format = '%H%n%h%n%s%n%an%n%aI';
171
+ const format = '%H%n%P%n%h%n%s%n%an%n%aI';
172
172
  const output = await execGit(repoPath, ['show', sha, '--format=' + format, '--no-patch']);
173
173
  const lines = output.trim().split('\n');
174
- if (lines.length < 5) {
174
+ if (lines.length < 6) {
175
175
  throw new Error(`Failed to parse commit info for ${sha}`);
176
176
  }
177
+ const parentSha = lines[1]?.trim().split(' ').filter(Boolean)[0];
177
178
  // Get stats
178
179
  const statsOutput = await execGit(repoPath, ['show', sha, '--format=', '--numstat']);
179
180
  const statsLines = statsOutput.trim().split('\n').filter(l => l.trim());
@@ -192,10 +193,11 @@ export async function getCommitInfo(repoPath, sha) {
192
193
  }
193
194
  return {
194
195
  sha: lines[0],
195
- shortSha: lines[1],
196
- message: lines[2],
197
- author: lines[3],
198
- date: lines[4],
196
+ parentSha: parentSha || undefined,
197
+ shortSha: lines[2],
198
+ message: lines[3],
199
+ author: lines[4],
200
+ date: lines[5],
199
201
  filesChanged,
200
202
  additions,
201
203
  deletions,
@@ -92,13 +92,29 @@ export async function upsertPrdState(repoId, slug, update) {
92
92
  ? null
93
93
  : update.notes;
94
94
  const updatedAt = new Date().toISOString();
95
+ const tasksUpdatedAt = updateTasks ? updatedAt : null;
96
+ const progressUpdatedAt = updateProgress ? updatedAt : null;
97
+ const notesUpdatedAt = updateNotes ? updatedAt : null;
95
98
  await dbRun(`
96
- INSERT INTO prd_states (repo_id, slug, tasks_json, progress_json, notes_md, updated_at)
97
- VALUES (?, ?, ?, ?, ?, ?)
99
+ INSERT INTO prd_states (
100
+ repo_id,
101
+ slug,
102
+ tasks_json,
103
+ progress_json,
104
+ notes_md,
105
+ tasks_updated_at,
106
+ progress_updated_at,
107
+ notes_updated_at,
108
+ updated_at
109
+ )
110
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
98
111
  ON CONFLICT(repo_id, slug) DO UPDATE SET
99
112
  tasks_json = CASE WHEN ? THEN excluded.tasks_json ELSE prd_states.tasks_json END,
100
113
  progress_json = CASE WHEN ? THEN excluded.progress_json ELSE prd_states.progress_json END,
101
114
  notes_md = CASE WHEN ? THEN excluded.notes_md ELSE prd_states.notes_md END,
115
+ tasks_updated_at = CASE WHEN ? THEN excluded.tasks_updated_at ELSE prd_states.tasks_updated_at END,
116
+ progress_updated_at = CASE WHEN ? THEN excluded.progress_updated_at ELSE prd_states.progress_updated_at END,
117
+ notes_updated_at = CASE WHEN ? THEN excluded.notes_updated_at ELSE prd_states.notes_updated_at END,
102
118
  updated_at = excluded.updated_at
103
119
  `, [
104
120
  repoId,
@@ -106,9 +122,15 @@ export async function upsertPrdState(repoId, slug, update) {
106
122
  tasksJson,
107
123
  progressJson,
108
124
  notesMd,
125
+ tasksUpdatedAt,
126
+ progressUpdatedAt,
127
+ notesUpdatedAt,
109
128
  updatedAt,
110
129
  updateTasks ? 1 : 0,
111
130
  updateProgress ? 1 : 0,
131
+ updateNotes ? 1 : 0,
132
+ updateTasks ? 1 : 0,
133
+ updateProgress ? 1 : 0,
112
134
  updateNotes ? 1 : 0
113
135
  ]);
114
136
  if (validatedTasks !== undefined) {
@@ -3,6 +3,7 @@ import { join, basename, dirname, resolve, relative, isAbsolute } from 'node:pat
3
3
  import { randomUUID } from 'node:crypto';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import { dbAll, dbGet, dbRun } from './db.js';
6
+ import { ensureRepoSyncMetaForRepo, ensureRepoSyncMetaForRepos } from './sync-identity.js';
6
7
  function findPackageRoot(startDir) {
7
8
  let currentDir = startDir;
8
9
  while (true) {
@@ -136,7 +137,9 @@ async function importLegacyReposIfNeeded() {
136
137
  export async function getRepos() {
137
138
  await importLegacyReposIfNeeded();
138
139
  const rows = await dbAll('SELECT id, name, path, added_at, git_repos_json FROM repos ORDER BY added_at ASC');
139
- return rows.map(rowToRepo);
140
+ const repos = rows.map(rowToRepo);
141
+ await ensureRepoSyncMetaForRepos(repos);
142
+ return repos;
140
143
  }
141
144
  export async function saveRepos(repos) {
142
145
  await importLegacyReposIfNeeded();
@@ -158,6 +161,7 @@ export async function saveRepos(repos) {
158
161
  const repoIds = repos.map(repo => repo.id);
159
162
  const placeholders = repoIds.map(() => '?').join(', ');
160
163
  await dbRun(`DELETE FROM repos WHERE id NOT IN (${placeholders})`, repoIds);
164
+ await ensureRepoSyncMetaForRepos(repos);
161
165
  }
162
166
  export async function addRepo(path, name) {
163
167
  await importLegacyReposIfNeeded();
@@ -175,6 +179,7 @@ export async function addRepo(path, name) {
175
179
  ...(gitRepos.length > 0 && { gitRepos })
176
180
  };
177
181
  await dbRun('INSERT INTO repos (id, name, path, added_at, git_repos_json) VALUES (?, ?, ?, ?, ?)', [repo.id, repo.name, repo.path, repo.addedAt, serializeGitRepos(repo.gitRepos)]);
182
+ await ensureRepoSyncMetaForRepo(repo);
178
183
  return repo;
179
184
  }
180
185
  /**
@@ -183,7 +188,12 @@ export async function addRepo(path, name) {
183
188
  export async function getRepoById(id) {
184
189
  await importLegacyReposIfNeeded();
185
190
  const row = await dbGet('SELECT id, name, path, added_at, git_repos_json FROM repos WHERE id = ?', [id]);
186
- return row ? rowToRepo(row) : undefined;
191
+ if (!row) {
192
+ return undefined;
193
+ }
194
+ const repo = rowToRepo(row);
195
+ await ensureRepoSyncMetaForRepo(repo);
196
+ return repo;
187
197
  }
188
198
  export async function updateRepoGitRepos(id, gitRepos) {
189
199
  await importLegacyReposIfNeeded();