1- import aiConfig from '../config.mjs' ;
2- import Base from '../../../../../src/core/Base.mjs' ;
3- import fs from 'fs/promises' ;
4- import logger from '../logger.mjs' ;
5- import path from 'path' ;
6- import IssueSyncer from './sync/IssueSyncer.mjs' ;
7- import ReleaseSyncer from './sync/ReleaseSyncer.mjs' ;
8-
9- const issueSyncConfig = aiConfig . issueSync ;
1+ import aiConfig from '../config.mjs' ;
2+ import Base from '../../../../../src/core/Base.mjs' ;
3+ import logger from '../logger.mjs' ;
4+ import IssueSyncer from './sync/IssueSyncer.mjs' ;
5+ import MetadataManager from './sync/MetadataManager.mjs' ;
6+ import ReleaseSyncer from './sync/ReleaseSyncer.mjs' ;
107
118/**
129 * Orchestrates the bi-directional synchronization of GitHub issues and releases with local Markdown files.
1310 *
1411 * This service is the core engine for the GitHub sync workflow. Its primary responsibilities include:
15- * - **State Management:** It maintains a persistent state via a `.sync-metadata.json` file to track
16- * the last sync time and the status of each item, enabling efficient delta-based updates.
1712 * - **Orchestration:** It calls specialized syncer modules (`IssueSyncer`, `ReleaseSyncer`) in the
1813 * correct order to ensure data integrity and minimize conflicts (e.g., push-then-pull).
19- * - **Metadata Management:** It loads the metadata at the start of a sync and saves the updated
20- * metadata, including new cache objects from the syncers, at the end.
14+ * - **Metadata Management:** It uses the `MetadataManager` to load metadata at the start of a sync
15+ * and save the updated metadata at the end.
2116 *
2217 * The main entry point is the `runFullSync` method, which executes the entire orchestration sequence.
2318 * @class Neo.ai.mcp.server.github-workflow.SyncService
@@ -43,20 +38,20 @@ class SyncService extends Base {
4338 *
4439 * This method orchestrates the entire bi-directional sync workflow in a specific order
4540 * to ensure data integrity and minimize conflicts:
46- * 1. Loads the persistent metadata from the last sync.
41+ * 1. Loads the persistent metadata from the last sync via `MetadataManager` .
4742 * 2. Fetches and caches GitHub release data via `ReleaseSyncer`.
4843 * 3. **Pushes** any local issue changes to GitHub via `IssueSyncer`.
4944 * 4. **Pulls** the latest issue changes from GitHub via `IssueSyncer`.
5045 * 5. Syncs release notes into local Markdown files via `ReleaseSyncer`.
51- * 6. Saves the updated metadata (including new release and issue data) to disk.
46+ * 6. Saves the updated, pruned metadata to disk via `MetadataManager` .
5247 *
5348 * @returns {Promise<object> } A comprehensive object containing detailed statistics and timing
5449 * information about all operations performed during the sync.
5550 */
5651 async runFullSync ( ) {
5752 const startTime = new Date ( ) ;
5853
59- const metadata = await this . #loadMetadata ( ) ;
54+ const metadata = await MetadataManager . load ( ) ;
6055
6156 // Fetch releases first, as they are needed for issue archiving
6257 await ReleaseSyncer . fetchAndCacheReleases ( metadata ) ;
@@ -89,7 +84,7 @@ class SyncService extends Base {
8984 newMetadata . releasesLastFetched = new Date ( ) . toISOString ( ) ;
9085
9186 // 6. Save metadata
92- await this . #saveMetadata ( newMetadata ) ;
87+ await MetadataManager . save ( newMetadata ) ;
9388
9489 const endTime = new Date ( ) ;
9590 const durationMs = endTime - startTime ;
@@ -121,41 +116,6 @@ class SyncService extends Base {
121116 timing
122117 } ;
123118 }
124-
125- /**
126- * Loads the synchronization metadata file from disk. If the file doesn't exist,
127- * it returns a default empty metadata object.
128- * @returns {Promise<object> } The parsed metadata object.
129- * @throws {Error } If reading the file fails for reasons other than not existing.
130- * @private
131- */
132- async #loadMetadata( ) {
133- try {
134- const data = await fs . readFile ( issueSyncConfig . metadataFile , 'utf-8' ) ;
135- return JSON . parse ( data ) ;
136- } catch ( error ) {
137- if ( error . code === 'ENOENT' ) {
138- return {
139- lastSync : null ,
140- issues : { }
141- } ;
142- }
143- throw error ;
144- }
145- }
146-
147- /**
148- * Saves the provided metadata object to the configured metadata file on disk,
149- * ensuring the directory exists.
150- * @param {object } metadata - The metadata object to serialize and save.
151- * @returns {Promise<void> }
152- * @private
153- */
154- async #saveMetadata( metadata ) {
155- const dir = path . dirname ( issueSyncConfig . metadataFile ) ;
156- await fs . mkdir ( dir , { recursive : true } ) ;
157- await fs . writeFile ( issueSyncConfig . metadataFile , JSON . stringify ( metadata , null , 2 ) , 'utf-8' ) ;
158- }
159119}
160120
161121export default Neo . setupClass ( SyncService ) ;
0 commit comments