@nameczz/skill-sync 0.1.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 (101) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +246 -0
  3. package/dist/src/autoSync.d.ts +36 -0
  4. package/dist/src/autoSync.js +235 -0
  5. package/dist/src/autoSync.js.map +1 -0
  6. package/dist/src/cli.d.ts +2 -0
  7. package/dist/src/cli.js +211 -0
  8. package/dist/src/cli.js.map +1 -0
  9. package/dist/src/codexArchive.d.ts +38 -0
  10. package/dist/src/codexArchive.js +340 -0
  11. package/dist/src/codexArchive.js.map +1 -0
  12. package/dist/src/config.d.ts +12 -0
  13. package/dist/src/config.js +78 -0
  14. package/dist/src/config.js.map +1 -0
  15. package/dist/src/copy.d.ts +1 -0
  16. package/dist/src/copy.js +42 -0
  17. package/dist/src/copy.js.map +1 -0
  18. package/dist/src/directoryPicker.d.ts +8 -0
  19. package/dist/src/directoryPicker.js +49 -0
  20. package/dist/src/directoryPicker.js.map +1 -0
  21. package/dist/src/format.d.ts +2 -0
  22. package/dist/src/format.js +27 -0
  23. package/dist/src/format.js.map +1 -0
  24. package/dist/src/frontmatter.d.ts +5 -0
  25. package/dist/src/frontmatter.js +36 -0
  26. package/dist/src/frontmatter.js.map +1 -0
  27. package/dist/src/git.d.ts +25 -0
  28. package/dist/src/git.js +227 -0
  29. package/dist/src/git.js.map +1 -0
  30. package/dist/src/hash.d.ts +1 -0
  31. package/dist/src/hash.js +34 -0
  32. package/dist/src/hash.js.map +1 -0
  33. package/dist/src/importSkill.d.ts +6 -0
  34. package/dist/src/importSkill.js +58 -0
  35. package/dist/src/importSkill.js.map +1 -0
  36. package/dist/src/init.d.ts +5 -0
  37. package/dist/src/init.js +13 -0
  38. package/dist/src/init.js.map +1 -0
  39. package/dist/src/installSkill.d.ts +6 -0
  40. package/dist/src/installSkill.js +62 -0
  41. package/dist/src/installSkill.js.map +1 -0
  42. package/dist/src/json.d.ts +2 -0
  43. package/dist/src/json.js +11 -0
  44. package/dist/src/json.js.map +1 -0
  45. package/dist/src/metadata.d.ts +11 -0
  46. package/dist/src/metadata.js +115 -0
  47. package/dist/src/metadata.js.map +1 -0
  48. package/dist/src/paths.d.ts +22 -0
  49. package/dist/src/paths.js +103 -0
  50. package/dist/src/paths.js.map +1 -0
  51. package/dist/src/removeLocalSkill.d.ts +5 -0
  52. package/dist/src/removeLocalSkill.js +79 -0
  53. package/dist/src/removeLocalSkill.js.map +1 -0
  54. package/dist/src/resolveConflict.d.ts +10 -0
  55. package/dist/src/resolveConflict.js +146 -0
  56. package/dist/src/resolveConflict.js.map +1 -0
  57. package/dist/src/scanner.d.ts +5 -0
  58. package/dist/src/scanner.js +57 -0
  59. package/dist/src/scanner.js.map +1 -0
  60. package/dist/src/server.d.ts +10 -0
  61. package/dist/src/server.js +494 -0
  62. package/dist/src/server.js.map +1 -0
  63. package/dist/src/sessionUsage.d.ts +14 -0
  64. package/dist/src/sessionUsage.js +180 -0
  65. package/dist/src/sessionUsage.js.map +1 -0
  66. package/dist/src/skillDependencies.d.ts +2 -0
  67. package/dist/src/skillDependencies.js +56 -0
  68. package/dist/src/skillDependencies.js.map +1 -0
  69. package/dist/src/skillMentions.d.ts +3 -0
  70. package/dist/src/skillMentions.js +111 -0
  71. package/dist/src/skillMentions.js.map +1 -0
  72. package/dist/src/status.d.ts +3 -0
  73. package/dist/src/status.js +134 -0
  74. package/dist/src/status.js.map +1 -0
  75. package/dist/src/stopSyncingSkill.d.ts +2 -0
  76. package/dist/src/stopSyncingSkill.js +31 -0
  77. package/dist/src/stopSyncingSkill.js.map +1 -0
  78. package/dist/src/sync.d.ts +48 -0
  79. package/dist/src/sync.js +741 -0
  80. package/dist/src/sync.js.map +1 -0
  81. package/dist/src/types.d.ts +84 -0
  82. package/dist/src/types.js +2 -0
  83. package/dist/src/types.js.map +1 -0
  84. package/dist/src/updateLocalSkill.d.ts +6 -0
  85. package/dist/src/updateLocalSkill.js +19 -0
  86. package/dist/src/updateLocalSkill.js.map +1 -0
  87. package/dist/src/usage.d.ts +6 -0
  88. package/dist/src/usage.js +84 -0
  89. package/dist/src/usage.js.map +1 -0
  90. package/dist/src/usageMonitor.d.ts +17 -0
  91. package/dist/src/usageMonitor.js +90 -0
  92. package/dist/src/usageMonitor.js.map +1 -0
  93. package/dist/web/assets/index-CPJdd8n0.js +59 -0
  94. package/dist/web/assets/index-T4bm09OX.css +2 -0
  95. package/dist/web/index.html +13 -0
  96. package/dist/web/style-options/common.css +515 -0
  97. package/dist/web/style-options/console.html +143 -0
  98. package/dist/web/style-options/desktop.html +144 -0
  99. package/dist/web/style-options/index.html +36 -0
  100. package/dist/web/style-options/workbench.html +112 -0
  101. package/package.json +84 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 zilliz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,246 @@
1
+ # Skill Sync
2
+
3
+ [![CI](https://img.shields.io/github/actions/workflow/status/nameczz/skill-sync/ci.yml?branch=main&label=CI)](./.github/workflows/ci.yml)
4
+ [![npm](https://img.shields.io/npm/v/@nameczz/skill-sync?label=npm)](https://www.npmjs.com/package/@nameczz/skill-sync)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE)
6
+ [![Node >= 20](https://img.shields.io/badge/node-%3E%3D20-339933)](https://nodejs.org/)
7
+
8
+ A local-first web and CLI workbench for syncing AI agent skills across machines with Git. It currently supports Codex and Agents skill folders, with room for more runtimes later.
9
+
10
+ Skills are just folders on your machine. That is great for hacking, but awkward once you have more than one computer, a growing skill library, or a mix of Codex and Agents skill directories. Skill Sync gives those folders a small control plane: choose what to track, keep local copies in sync, inspect conflicts, and push changes through your own Git repository.
11
+
12
+ 中文简介:Skill Sync 是一个本地优先的 skill 同步工具。你可以用自己的 Git 仓库在多台电脑之间同步 Codex / Agents skills,可视化查看本地/仓库差异、最近使用时间、Codex 归档会话,并支持自动同步。
13
+
14
+ ## Screenshots
15
+
16
+ These are placeholders. Replace them with real screenshots before sharing a launch post or article.
17
+
18
+ | Skills dashboard | Conflict / compare flow |
19
+ | --- | --- |
20
+ | ![Skills dashboard placeholder](docs/screenshots/dashboard-placeholder.svg) | ![Conflict resolution placeholder](docs/screenshots/conflict-placeholder.svg) |
21
+
22
+ Suggested real captures:
23
+ - `docs/screenshots/skills-dashboard.png`
24
+ - `docs/screenshots/codex-archive.png`
25
+ - `docs/screenshots/compare-conflict.png`
26
+ - `docs/screenshots/dark-mode.png`
27
+
28
+ ## What It Does
29
+
30
+ - Tracks selected skills from `~/.codex/skills` and `~/.agents/skills`.
31
+ - Syncs skill folders through a Git repository that you control.
32
+ - Supports one-click add, install, update, stop syncing, and local delete flows.
33
+ - Auto-syncs managed local skill changes and commits/pushes them to the sync repo.
34
+ - Pulls remote changes from another machine and applies missing or updated skills locally.
35
+ - Shows skill states such as in sync, local only, repo only, local changed, repo changed, and conflict.
36
+ - Lets you compare versions and choose which copy should win when a skill conflicts.
37
+ - Records skill usage from local Codex session traces and shows last-used time.
38
+ - Includes a Codex Archive browser for archived sessions, with soft delete, restore, and unarchive.
39
+ - Keeps machine-specific config and cache out of Git.
40
+
41
+ ## What It Does Not Do
42
+
43
+ - It does not host your skills on a SaaS service.
44
+ - It does not require a central backend beyond your own Git remote.
45
+ - It does not delete local installed skill copies when you stop syncing a skill.
46
+ - It does not change how Codex loads skills; Codex still reads local skill directories.
47
+
48
+ ## Quick Start
49
+
50
+ Prerequisites:
51
+
52
+ - Node.js `>=20`
53
+ - Git
54
+ - macOS for the native directory picker
55
+ - A local folder or Git clone to use as the sync repository
56
+
57
+ Install from npm:
58
+
59
+ ```bash
60
+ npm install -g @nameczz/skill-sync
61
+ skill-sync serve
62
+ ```
63
+
64
+ Or run from source:
65
+
66
+ ```bash
67
+ yarn install
68
+ npm run dev -- serve
69
+ ```
70
+
71
+ Open [http://127.0.0.1:3017](http://127.0.0.1:3017).
72
+
73
+ On first launch:
74
+
75
+ 1. Choose the Git sync repository directory.
76
+ 2. Initialize the manager.
77
+ 3. Add local-only skills to sync.
78
+ 4. Let auto-sync watch managed skills, or use manual actions when you want explicit control.
79
+
80
+ ## Common CLI Commands
81
+
82
+ ```bash
83
+ skill-sync status
84
+ skill-sync pull
85
+ skill-sync sync <skill-id>
86
+ skill-sync update-local <skill-id>
87
+ skill-sync stop-syncing <skill-id>
88
+ skill-sync serve --port 4100
89
+ ```
90
+
91
+ ## Sync Model
92
+
93
+ The sync repository stores shared state:
94
+
95
+ ```text
96
+ skills/ # tracked skill folders
97
+ metadata/skills.json # tracked skill metadata
98
+ metadata/usage-events.jsonl
99
+ .gitignore
100
+ ```
101
+
102
+ Your machine keeps local-only state outside Git:
103
+
104
+ ```text
105
+ ~/.skill-sync/config.json
106
+ ~/.skill-sync/cache/
107
+ ```
108
+
109
+ At a high level:
110
+
111
+ ```mermaid
112
+ flowchart LR
113
+ Codex["~/.codex/skills"] --> Manager["Skill Sync"]
114
+ Agents["~/.agents/skills"] --> Manager
115
+ Manager --> Repo["Git sync repo"]
116
+ Repo --> GitHub["Your Git remote"]
117
+ Manager --> Archive["~/.codex/archived_sessions"]
118
+ Manager --> Cache["Local config/cache"]
119
+ ```
120
+
121
+ ## Typical Workflows
122
+
123
+ **Add a local skill to sync**
124
+
125
+ 1. Open the Skills page.
126
+ 2. Find a `Local only` skill.
127
+ 3. Click `Add to sync`.
128
+ 4. The app copies it into the sync repo, updates metadata, commits, and pushes.
129
+
130
+ **Use another machine**
131
+
132
+ 1. Clone or choose the same sync repository.
133
+ 2. Initialize Skill Sync with that repo path.
134
+ 3. Click `Pull`.
135
+ 4. Apply repo changes to install missing local copies.
136
+
137
+ **Resolve a conflict**
138
+
139
+ 1. Open the compare dialog for the skill.
140
+ 2. Review the repo, Codex, and Agents versions.
141
+ 3. Accept the version you want to keep.
142
+ 4. The app updates metadata, commits, and pushes the resolution.
143
+
144
+ ## Codex Archive
145
+
146
+ Codex App can archive sessions into `~/.codex/archived_sessions`. Skill Sync exposes those sessions in the web UI so you can:
147
+
148
+ - Search archived sessions.
149
+ - Preview metadata without loading the full file by default.
150
+ - Move an archived session to Trash.
151
+ - Restore from Trash.
152
+ - Unarchive a session back into Codex sessions.
153
+
154
+ ## Documentation
155
+
156
+ Local docs site:
157
+
158
+ ```bash
159
+ npm run docs:dev
160
+ npm run docs:build
161
+ ```
162
+
163
+ Useful docs:
164
+
165
+ - [Getting Started](./docs/guide/getting-started.md)
166
+ - [Sync Model](./docs/guide/sync-model.md)
167
+ - [API Reference](./docs/reference/api.md)
168
+
169
+ ## Development
170
+
171
+ Run checks:
172
+
173
+ ```bash
174
+ npm run typecheck
175
+ npm test
176
+ npm run build
177
+ ```
178
+
179
+ Run with isolated test paths:
180
+
181
+ ```bash
182
+ SKILL_SYNC_REPO=/tmp/skill-sync-repo \
183
+ SKILL_SYNC_CODEX_SKILLS_DIR=/tmp/skill-sync-codex \
184
+ SKILL_SYNC_AGENTS_SKILLS_DIR=/tmp/skill-sync-agents \
185
+ SKILL_SYNC_CONFIG_DIR=/tmp/skill-sync-config \
186
+ SKILL_SYNC_CACHE_DIR=/tmp/skill-sync-cache \
187
+ npm run dev -- serve
188
+ ```
189
+
190
+ Legacy `CSM_*` environment variables and `~/.codex-skill-manager` config are still read for compatibility.
191
+
192
+ ## Publishing
193
+
194
+ The npm package is published by `.github/workflows/publish-npm.yml` using npm Trusted Publishing, so no long-lived npm publish token is needed after the package exists on npm.
195
+
196
+ 1. Rename or create the GitHub repository that matches `package.json`'s `repository.url`.
197
+ 2. Publish `@nameczz/skill-sync@0.1.0` once manually if the package does not exist yet:
198
+ ```bash
199
+ npm login
200
+ npm publish --access=public
201
+ ```
202
+ 3. On npmjs.com, open the `@nameczz/skill-sync` package settings and add a Trusted Publisher:
203
+ - Provider: GitHub Actions
204
+ - Organization/user: `nameczz`
205
+ - Repository: `skill-sync`
206
+ - Workflow filename: `publish-npm.yml`
207
+ - Allowed action: `npm publish`
208
+ 4. Publish future versions with a GitHub Release, or run the `Publish npm` workflow manually with `dry-run` set to `false`.
209
+
210
+ You can also configure the trusted publisher from the CLI after the first publish:
211
+
212
+ ```bash
213
+ npm trust github @nameczz/skill-sync --repo nameczz/skill-sync --file publish-npm.yml --allow-publish
214
+ ```
215
+
216
+ The workflow installs dependencies, runs typecheck, tests, build, `npm pack --dry-run`, then publishes with OIDC-backed npm provenance.
217
+
218
+ ## Project Structure
219
+
220
+ ```text
221
+ skill-sync/
222
+ ├── src/ # CLI, sync, git, archive, usage scanner, server
223
+ ├── web/ # local management UI
224
+ ├── tests/ # Vitest coverage
225
+ ├── docs/ # docs and screenshot placeholders
226
+ ├── .github/ # CI workflows and GitHub metadata
227
+ └── README.md
228
+ ```
229
+
230
+ ## Contributing
231
+
232
+ Issues and pull requests are welcome. No special template is required; a clear description, reproduction steps for bugs, and screenshots or logs when relevant are enough.
233
+
234
+ Before opening a PR, please run:
235
+
236
+ ```bash
237
+ npm run typecheck
238
+ npm test
239
+ npm run build
240
+ ```
241
+
242
+ See [CONTRIBUTING.md](./CONTRIBUTING.md), [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md), and [SECURITY.md](./SECURITY.md) for project expectations.
243
+
244
+ ## License
245
+
246
+ MIT. See [LICENSE](./LICENSE).
@@ -0,0 +1,36 @@
1
+ import { type FSWatcher } from "node:fs";
2
+ import type { LocalConfig, SkillRecord } from "./types.js";
3
+ import { type SyncSelection } from "./sync.js";
4
+ import type { SyncResult } from "./sync.js";
5
+ type AutoSyncMode = "disabled" | "watching" | "polling";
6
+ export type AutoSyncStatus = {
7
+ enabled: boolean;
8
+ mode: AutoSyncMode;
9
+ running: boolean;
10
+ pending: boolean;
11
+ lastRunStartedAt: string | null;
12
+ lastRunCompletedAt: string | null;
13
+ lastSyncedSkillIds: string[];
14
+ lastError: string | null;
15
+ watchersSupported: boolean;
16
+ };
17
+ type AutoSyncController = {
18
+ start: (config: LocalConfig) => Promise<void>;
19
+ stop: () => Promise<void>;
20
+ trigger: () => void;
21
+ getStatus: () => AutoSyncStatus;
22
+ };
23
+ type AutoSyncOptions = {
24
+ debounceMs?: number;
25
+ pollingIntervalMs?: number;
26
+ statusBuilder?: (config: LocalConfig) => Promise<ManagedStatusReport>;
27
+ sync?: (config: LocalConfig, selections: SyncSelection[]) => Promise<SyncResult>;
28
+ pollSignatureBuilder?: (config: LocalConfig) => Promise<string>;
29
+ watchProvider?: (path: string, onChange: () => void, onError: (error: unknown) => void) => FSWatcher;
30
+ now?: () => string;
31
+ };
32
+ type ManagedStatusReport = {
33
+ managed: Array<Pick<SkillRecord, "id" | "syncState" | "localSource">>;
34
+ };
35
+ export declare function createAutoSyncController(options?: AutoSyncOptions): AutoSyncController;
36
+ export {};
@@ -0,0 +1,235 @@
1
+ import { existsSync, watch } from "node:fs";
2
+ import { scanSkills } from "./scanner.js";
3
+ import { buildStatusReport } from "./status.js";
4
+ import { syncSelectedSkills } from "./sync.js";
5
+ export function createAutoSyncController(options = {}) {
6
+ const debounceMs = options.debounceMs ?? 2000;
7
+ const pollingIntervalMs = options.pollingIntervalMs ?? 4000;
8
+ const sync = options.sync ?? syncSelectedSkills;
9
+ const statusBuilder = options.statusBuilder ?? buildStatusReport;
10
+ const pollSignatureBuilder = options.pollSignatureBuilder ?? defaultPollSignatureBuilder;
11
+ const watchProvider = options.watchProvider ?? ((targetPath, onChange, onError) => {
12
+ const watcher = watch(targetPath, { recursive: true }, () => onChange());
13
+ watcher.on("error", onError);
14
+ return watcher;
15
+ });
16
+ const now = options.now ?? (() => new Date().toISOString());
17
+ let currentConfig = null;
18
+ let state = {
19
+ enabled: false,
20
+ mode: "disabled",
21
+ running: false,
22
+ pending: false,
23
+ lastRunStartedAt: null,
24
+ lastRunCompletedAt: null,
25
+ lastSyncedSkillIds: [],
26
+ lastError: null,
27
+ watchersSupported: true
28
+ };
29
+ const watchers = [];
30
+ let pollingTimer = null;
31
+ let debounceTimer = null;
32
+ let pollingPreviousSignature = "";
33
+ let running = false;
34
+ async function start(config) {
35
+ if (currentConfig !== null &&
36
+ currentConfig.syncRepo === config.syncRepo &&
37
+ currentConfig.codexSkillsDir === config.codexSkillsDir &&
38
+ currentConfig.agentsSkillsDir === config.agentsSkillsDir) {
39
+ state.enabled = true;
40
+ state.mode = state.mode === "disabled" ? (state.watchersSupported ? "watching" : "polling") : state.mode;
41
+ return;
42
+ }
43
+ await stop();
44
+ currentConfig = config;
45
+ state.enabled = true;
46
+ state.lastError = null;
47
+ state.pending = false;
48
+ state.lastSyncedSkillIds = [];
49
+ state.mode = "watching";
50
+ const watchersStarted = await startWatchers(config);
51
+ state.watchersSupported = watchersStarted;
52
+ state.mode = watchersStarted ? "watching" : "polling";
53
+ try {
54
+ pollingPreviousSignature = await pollSignatureBuilder(config);
55
+ }
56
+ catch (error) {
57
+ state.lastError = normalizeError(error);
58
+ state.watchersSupported = false;
59
+ state.mode = "polling";
60
+ startPolling(config);
61
+ return;
62
+ }
63
+ if (!watchersStarted) {
64
+ startPolling(config);
65
+ }
66
+ queueSync("startup");
67
+ }
68
+ async function stop() {
69
+ state.enabled = false;
70
+ state.mode = "disabled";
71
+ currentConfig = null;
72
+ for (const watcher of watchers) {
73
+ watcher.close();
74
+ }
75
+ watchers.length = 0;
76
+ if (pollingTimer !== null) {
77
+ clearInterval(pollingTimer);
78
+ pollingTimer = null;
79
+ }
80
+ if (debounceTimer !== null) {
81
+ clearTimeout(debounceTimer);
82
+ debounceTimer = null;
83
+ }
84
+ state.pending = false;
85
+ state.running = false;
86
+ state.lastRunStartedAt = null;
87
+ }
88
+ async function startWatchers(config) {
89
+ const roots = [config.codexSkillsDir, config.agentsSkillsDir];
90
+ let startedAny = false;
91
+ try {
92
+ for (const root of roots) {
93
+ if (!existsSync(root)) {
94
+ continue;
95
+ }
96
+ const watcher = watchProvider(root, () => {
97
+ queueSync("local-change");
98
+ }, (error) => {
99
+ void fallbackToPolling(config, error);
100
+ });
101
+ watchers.push(watcher);
102
+ startedAny = true;
103
+ }
104
+ }
105
+ catch (error) {
106
+ await cleanupWatchers();
107
+ state.lastError = normalizeError(error);
108
+ return false;
109
+ }
110
+ if (startedAny) {
111
+ return true;
112
+ }
113
+ return false;
114
+ }
115
+ async function cleanupWatchers() {
116
+ for (const watcher of watchers) {
117
+ watcher.close();
118
+ }
119
+ watchers.length = 0;
120
+ }
121
+ function startPolling(config) {
122
+ state.mode = "polling";
123
+ if (pollingTimer !== null) {
124
+ clearInterval(pollingTimer);
125
+ }
126
+ pollingTimer = setInterval(() => {
127
+ void runPoll(config);
128
+ }, pollingIntervalMs);
129
+ }
130
+ async function runPoll(config) {
131
+ if (!state.enabled || running) {
132
+ return;
133
+ }
134
+ try {
135
+ const signature = await pollSignatureBuilder(config);
136
+ if (signature !== pollingPreviousSignature) {
137
+ pollingPreviousSignature = signature;
138
+ queueSync("poll-change");
139
+ }
140
+ }
141
+ catch (error) {
142
+ state.lastError = normalizeError(error);
143
+ }
144
+ }
145
+ async function fallbackToPolling(config, error) {
146
+ await cleanupWatchers();
147
+ pollingPreviousSignature = "";
148
+ state.watchersSupported = false;
149
+ state.mode = "polling";
150
+ state.lastError = normalizeError(error);
151
+ startPolling(config);
152
+ }
153
+ function queueSync(_reason) {
154
+ if (!currentConfig || !state.enabled) {
155
+ return;
156
+ }
157
+ state.pending = true;
158
+ if (debounceTimer !== null) {
159
+ clearTimeout(debounceTimer);
160
+ }
161
+ debounceTimer = setTimeout(() => {
162
+ void runAutoSync();
163
+ }, debounceMs);
164
+ }
165
+ async function runAutoSync() {
166
+ if (!state.enabled || running || !currentConfig) {
167
+ return;
168
+ }
169
+ running = true;
170
+ state.running = true;
171
+ state.pending = false;
172
+ state.lastError = null;
173
+ state.lastRunStartedAt = now();
174
+ try {
175
+ const report = await statusBuilder(currentConfig);
176
+ const managed = report.managed
177
+ .map((skill) => ({
178
+ id: skill.id,
179
+ syncState: skill.syncState,
180
+ localSource: skill.localSource ?? null
181
+ }))
182
+ .filter((skill) => skill.syncState === "local_modified")
183
+ .map((skill) => ({
184
+ skillId: skill.id,
185
+ source: normalizeSource(skill.localSource)
186
+ }));
187
+ if (managed.length === 0) {
188
+ state.lastSyncedSkillIds = [];
189
+ return;
190
+ }
191
+ const result = await sync(currentConfig, managed);
192
+ state.lastSyncedSkillIds = result.skillIds;
193
+ }
194
+ catch (error) {
195
+ state.lastError = normalizeError(error);
196
+ }
197
+ finally {
198
+ state.running = false;
199
+ state.lastRunCompletedAt = now();
200
+ running = false;
201
+ if (state.pending) {
202
+ queueSync("coalesced");
203
+ }
204
+ }
205
+ }
206
+ function normalizeSource(source) {
207
+ if (source === "codex" || source === "agents") {
208
+ return source;
209
+ }
210
+ return undefined;
211
+ }
212
+ function getStatus() {
213
+ return { ...state };
214
+ }
215
+ return {
216
+ start,
217
+ stop,
218
+ trigger: () => queueSync("manual"),
219
+ getStatus
220
+ };
221
+ }
222
+ async function defaultPollSignatureBuilder(config) {
223
+ const [codex, agents] = await Promise.all([scanSkills(config.codexSkillsDir, "codex"), scanSkills(config.agentsSkillsDir, "agents")]);
224
+ const signature = [...codex, ...agents]
225
+ .map((skill) => `${skill.source}:${skill.id}:${skill.hash}:${skill.modifiedAt}`)
226
+ .sort();
227
+ return signature.join("\u0000");
228
+ }
229
+ function normalizeError(error) {
230
+ if (error instanceof Error) {
231
+ return error.message;
232
+ }
233
+ return String(error);
234
+ }
235
+ //# sourceMappingURL=autoSync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autoSync.js","sourceRoot":"","sources":["../../src/autoSync.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};