@genspark/cli 1.0.24 → 1.0.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.
@@ -0,0 +1,92 @@
1
+ /**
2
+ * `gsk skills sync` — client-side materializer for the backend `skills_sync`
3
+ * action.
4
+ *
5
+ * Why client-side: `skills_sync` returns a digest-diffed bundle (base64 file
6
+ * bytes for changed skills, a removal list, per-slug errors); this module
7
+ * applies one such response to disk. Deliberately NOT parity with
8
+ * `setup.sh` (which unconditionally `rm -rf`s then `ln -sfn`s over the mount
9
+ * path — the synced skill always wins): here a pre-existing REAL directory
10
+ * under `mountDir` (an inline-tar builtin, or user content) is never
11
+ * clobbered by a same-named synced skill; the real dir wins and the slug is
12
+ * reported failed instead. Chosen because this materializer runs on a
13
+ * user's laptop, not a disposable sandbox — never `rm -rf`ing directories we
14
+ * didn't create is worth a failed sync entry. Index.ts (Task 6) drives the
15
+ * poll-until-`complete` loop and owns `--check`; this module only ever
16
+ * applies (never diffs) a response.
17
+ */
18
+ /** One file row from a `skills_sync` `update` entry. */
19
+ export interface SyncSkillFile {
20
+ path: string;
21
+ content_base64: string;
22
+ }
23
+ /** One per-slug entry in a `skills_sync` response. */
24
+ export interface SyncSkillEntry {
25
+ slug: string;
26
+ sha?: string;
27
+ action: 'update' | 'unchanged' | 'removed' | 'error';
28
+ files?: SyncSkillFile[];
29
+ total_bytes?: number;
30
+ code?: string;
31
+ }
32
+ /** Shape of the `data` field returned by the backend `skills_sync` action. */
33
+ export interface SyncResponseData {
34
+ synced?: boolean;
35
+ complete: boolean;
36
+ skills: SyncSkillEntry[];
37
+ }
38
+ export interface SyncOptions {
39
+ storeDir?: string;
40
+ mountDir?: string;
41
+ }
42
+ export interface ApplySyncResult {
43
+ state: Record<string, string>;
44
+ applied: string[];
45
+ removed: string[];
46
+ failed: string[];
47
+ }
48
+ /**
49
+ * Folds the per-batch results of the multi-batch sync loop into single deduped
50
+ * totals. A slug can recur across batches — a server `error` row carries no
51
+ * recordable sha, so the server re-emits it in EVERY batch — and concatenating
52
+ * per-batch arrays would multi-count it, making the summary counts and the
53
+ * exit-4 gate wrong. Each category is a Set (dedup by slug); a slug's outcome
54
+ * in a later batch supersedes an earlier one. It also aggregates the echoed
55
+ * skill rows so `-o json` reflects every batch, not just the final one.
56
+ *
57
+ * Generic over the echoed-row type so the CLI can pass its own row shape
58
+ * without a cast (only `slug` is required here).
59
+ */
60
+ export declare class SyncAggregator<Row extends {
61
+ slug: string;
62
+ }> {
63
+ private readonly _applied;
64
+ private readonly _removed;
65
+ private readonly _failed;
66
+ private readonly _rows;
67
+ /** Fold one batch's apply result and its echoed skill rows into the totals. */
68
+ add(result: ApplySyncResult, rows: readonly Row[]): void;
69
+ private categorize;
70
+ get appliedCount(): number;
71
+ get removedCount(): number;
72
+ get failedCount(): number;
73
+ /** Aggregated echoed rows (deduped by slug, last-wins). */
74
+ get skills(): Row[];
75
+ }
76
+ export declare const STATE_FILENAME = "skills-sync-state.json";
77
+ export declare function defaultStoreDir(): string;
78
+ export declare function defaultMountDir(): string;
79
+ /** Load the persisted `{slug: sha}` digest map. Both unparseable JSON AND a
80
+ * valid-JSON-but-wrong-shape file self-heal to `{}` — a dirty state file
81
+ * forces a full re-diff on the next sync rather than risk skipping a change
82
+ * against a state we can't trust. The shape check matters because the server
83
+ * verb rejects the WHOLE sync (`bad state digest map`) on a single bad key,
84
+ * so without validating here a corrupt map would wedge every sync forever;
85
+ * resetting to `{}` lets it recover on the next run. */
86
+ export declare function loadState(storeDir: string): Record<string, string>;
87
+ /**
88
+ * Apply one `skills_sync` response to disk. Never called in `--check` mode
89
+ * (Task 6's caller diffs against `loadState` output itself for that path).
90
+ */
91
+ export declare function applySyncResponse(data: SyncResponseData, opts?: SyncOptions): ApplySyncResult;
92
+ //# sourceMappingURL=skills-sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills-sync.d.ts","sourceRoot":"","sources":["../../src/commands/skills-sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AASH,wDAAwD;AACxD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,cAAc,EAAE,MAAM,CAAA;CACvB;AAED,sDAAsD;AACtD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,QAAQ,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAA;IACpD,KAAK,CAAC,EAAE,aAAa,EAAE,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,8EAA8E;AAC9E,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,QAAQ,EAAE,OAAO,CAAA;IACjB,MAAM,EAAE,cAAc,EAAE,CAAA;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,cAAc,CAAC,GAAG,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE;IACtD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAC5C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyB;IAE/C,+EAA+E;IAC/E,GAAG,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,SAAS,GAAG,EAAE,GAAG,IAAI;IAOxD,OAAO,CAAC,UAAU;IAOlB,IAAI,YAAY,IAAI,MAAM,CAEzB;IACD,IAAI,YAAY,IAAI,MAAM,CAEzB;IACD,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,2DAA2D;IAC3D,IAAI,MAAM,IAAI,GAAG,EAAE,CAElB;CACF;AAED,eAAO,MAAM,cAAc,2BAA2B,CAAA;AAMtD,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED;;;;;;wDAMwD;AACxD,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAelE;AAkJD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,gBAAgB,EACtB,IAAI,GAAE,WAAgB,GACrB,eAAe,CAiGjB"}
@@ -0,0 +1,329 @@
1
+ /**
2
+ * `gsk skills sync` — client-side materializer for the backend `skills_sync`
3
+ * action.
4
+ *
5
+ * Why client-side: `skills_sync` returns a digest-diffed bundle (base64 file
6
+ * bytes for changed skills, a removal list, per-slug errors); this module
7
+ * applies one such response to disk. Deliberately NOT parity with
8
+ * `setup.sh` (which unconditionally `rm -rf`s then `ln -sfn`s over the mount
9
+ * path — the synced skill always wins): here a pre-existing REAL directory
10
+ * under `mountDir` (an inline-tar builtin, or user content) is never
11
+ * clobbered by a same-named synced skill; the real dir wins and the slug is
12
+ * reported failed instead. Chosen because this materializer runs on a
13
+ * user's laptop, not a disposable sandbox — never `rm -rf`ing directories we
14
+ * didn't create is worth a failed sync entry. Index.ts (Task 6) drives the
15
+ * poll-until-`complete` loop and owns `--check`; this module only ever
16
+ * applies (never diffs) a response.
17
+ */
18
+ import * as fs from 'fs';
19
+ import * as os from 'os';
20
+ import * as path from 'path';
21
+ import { error as logError } from '../logger.js';
22
+ import { safeJoin, SLUG_RE } from './skills-pull.js';
23
+ /**
24
+ * Folds the per-batch results of the multi-batch sync loop into single deduped
25
+ * totals. A slug can recur across batches — a server `error` row carries no
26
+ * recordable sha, so the server re-emits it in EVERY batch — and concatenating
27
+ * per-batch arrays would multi-count it, making the summary counts and the
28
+ * exit-4 gate wrong. Each category is a Set (dedup by slug); a slug's outcome
29
+ * in a later batch supersedes an earlier one. It also aggregates the echoed
30
+ * skill rows so `-o json` reflects every batch, not just the final one.
31
+ *
32
+ * Generic over the echoed-row type so the CLI can pass its own row shape
33
+ * without a cast (only `slug` is required here).
34
+ */
35
+ export class SyncAggregator {
36
+ _applied = new Set();
37
+ _removed = new Set();
38
+ _failed = new Set();
39
+ _rows = new Map();
40
+ /** Fold one batch's apply result and its echoed skill rows into the totals. */
41
+ add(result, rows) {
42
+ for (const slug of result.applied)
43
+ this.categorize(slug, this._applied);
44
+ for (const slug of result.removed)
45
+ this.categorize(slug, this._removed);
46
+ for (const slug of result.failed)
47
+ this.categorize(slug, this._failed);
48
+ for (const row of rows)
49
+ this._rows.set(row.slug, row);
50
+ }
51
+ categorize(slug, into) {
52
+ this._applied.delete(slug);
53
+ this._removed.delete(slug);
54
+ this._failed.delete(slug);
55
+ into.add(slug);
56
+ }
57
+ get appliedCount() {
58
+ return this._applied.size;
59
+ }
60
+ get removedCount() {
61
+ return this._removed.size;
62
+ }
63
+ get failedCount() {
64
+ return this._failed.size;
65
+ }
66
+ /** Aggregated echoed rows (deduped by slug, last-wins). */
67
+ get skills() {
68
+ return Array.from(this._rows.values());
69
+ }
70
+ }
71
+ export const STATE_FILENAME = 'skills-sync-state.json';
72
+ function expandHome(p) {
73
+ return p.replace(/^~(?=\/|$)/, os.homedir());
74
+ }
75
+ export function defaultStoreDir() {
76
+ return expandHome('~/.gsk/skills-store');
77
+ }
78
+ export function defaultMountDir() {
79
+ return expandHome('~/.opencode/skills');
80
+ }
81
+ /** Load the persisted `{slug: sha}` digest map. Both unparseable JSON AND a
82
+ * valid-JSON-but-wrong-shape file self-heal to `{}` — a dirty state file
83
+ * forces a full re-diff on the next sync rather than risk skipping a change
84
+ * against a state we can't trust. The shape check matters because the server
85
+ * verb rejects the WHOLE sync (`bad state digest map`) on a single bad key,
86
+ * so without validating here a corrupt map would wedge every sync forever;
87
+ * resetting to `{}` lets it recover on the next run. */
88
+ export function loadState(storeDir) {
89
+ try {
90
+ const raw = fs.readFileSync(path.join(storeDir, STATE_FILENAME), 'utf8');
91
+ const parsed = JSON.parse(raw);
92
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
93
+ const obj = parsed;
94
+ const wellShaped = Object.entries(obj).every(([k, v]) => SLUG_RE.test(k) && typeof v === 'string');
95
+ if (wellShaped)
96
+ return obj;
97
+ }
98
+ }
99
+ catch {
100
+ // absent or unparseable → treated as empty state
101
+ }
102
+ return {};
103
+ }
104
+ /** True when `<slug>`'s files are downloaded on disk. The STORE dir is the
105
+ * single source of truth for "materialized" — NOT the mount symlink. The mount
106
+ * is a best-effort, separately-reconciled concern (see `reconcileMount`): on a
107
+ * filesystem that can't hold a symlink (Windows without Developer Mode,
108
+ * exFAT/FAT/SMB — `symlinkSync` throws EPERM/ENOSYS) the mount is never created,
109
+ * yet the store IS. Keying materialization on the mount would then read the
110
+ * slug as "not materialized" on every `unchanged` re-sync, drop its sha, and
111
+ * re-download the whole store forever. `writeSlugAtomic` renames the tree into
112
+ * place in one step, so a present store dir is a complete one. A hand-deleted
113
+ * store dir (store missing) still reads false → the caller drops the sha and
114
+ * the next sync re-fetches (the F3 self-heal). */
115
+ function isSlugMaterialized(storeDir, slug) {
116
+ return fs.existsSync(path.join(storeDir, slug));
117
+ }
118
+ /** Best-effort reconcile of the mount symlink for an already-stored slug, run
119
+ * every sync (never touches the store, so it can't trigger a re-download):
120
+ * - `'mounted'` — the symlink now points at (or already pointed at) the
121
+ * live store dir.
122
+ * - `'blocked'` — a REAL dir/file occupies the mount path; the builtin /
123
+ * user content wins and we never clobber it (the module's core deviation
124
+ * from setup.sh). A benign, terminal state — not retried into a symlink.
125
+ * - `'unsupported'` — `symlinkSync` threw (filesystem can't hold a symlink),
126
+ * or the mount path can't be stat'd (a non-ENOENT fault). The store is
127
+ * intact; the caller keeps the sha but reports the slug not-mounted.
128
+ * The whole body is guarded: the initial `lstatSync` can itself throw a
129
+ * non-ENOENT error (e.g. ENOTDIR when `mountDir` is a file), which must be
130
+ * caught here rather than escape the batch before `saveState`. */
131
+ function reconcileMount(storeDir, mountDir, slug) {
132
+ const linkPath = path.join(mountDir, slug);
133
+ const targetPath = path.join(storeDir, slug);
134
+ try {
135
+ const stat = fs.lstatSync(linkPath, { throwIfNoEntry: false });
136
+ if (stat && !stat.isSymbolicLink())
137
+ return 'blocked';
138
+ if (stat &&
139
+ path.resolve(mountDir, fs.readlinkSync(linkPath)) === targetPath) {
140
+ return 'mounted'; // already our symlink → no churn
141
+ }
142
+ fs.mkdirSync(mountDir, { recursive: true });
143
+ if (stat)
144
+ fs.unlinkSync(linkPath); // stale / foreign symlink → repoint
145
+ fs.symlinkSync(targetPath, linkPath, 'dir');
146
+ return 'mounted';
147
+ }
148
+ catch {
149
+ return 'unsupported';
150
+ }
151
+ }
152
+ /** Human-facing reason a stored slug isn't mounted, per `reconcileMount`. */
153
+ function mountFailureMessage(slug, result) {
154
+ const why = result === 'blocked'
155
+ ? 'a real directory occupies the mount path (builtin/user content wins)'
156
+ : 'the filesystem does not support symlinks (Windows without Developer Mode, exFAT/FAT/SMB)';
157
+ return `skills sync: ${slug} stored but not mounted — ${why}`;
158
+ }
159
+ /** Sweep orphan `.tmp-<slug>-<pid>` staging dirs left by a killed run. Each
160
+ * `writeSlugAtomic` only clears its OWN pid's tmp dir, so a process killed
161
+ * mid-write leaks one; this best-effort sweep runs once per apply. Guarded to
162
+ * the exact tmp-dir shape so it can never touch a real skill dir. */
163
+ function sweepStaleTmpDirs(storeDir) {
164
+ let entries;
165
+ try {
166
+ entries = fs.readdirSync(storeDir);
167
+ }
168
+ catch {
169
+ return;
170
+ }
171
+ const tmpRe = /^\.tmp-[A-Za-z0-9][A-Za-z0-9_-]*-\d+$/;
172
+ for (const name of entries) {
173
+ if (!tmpRe.test(name))
174
+ continue;
175
+ try {
176
+ fs.rmSync(path.join(storeDir, name), { recursive: true, force: true });
177
+ }
178
+ catch {
179
+ // best-effort — a concurrent writer or locked dir just stays
180
+ }
181
+ }
182
+ }
183
+ function saveState(storeDir, state) {
184
+ fs.mkdirSync(storeDir, { recursive: true });
185
+ fs.writeFileSync(path.join(storeDir, STATE_FILENAME), JSON.stringify(state, null, 2) + '\n');
186
+ }
187
+ /**
188
+ * Write one `update` entry's files into `<storeDir>/<slug>` atomically: build
189
+ * the tree in a sibling `.tmp-` dir first, then remove any existing target
190
+ * and `renameSync` the tmp dir over it. A crash or crafted traversal path
191
+ * partway through the write leaves the tmp dir orphaned but the previous
192
+ * (or absent) target untouched — never a half-written skill on disk.
193
+ */
194
+ function writeSlugAtomic(storeDir, slug, files) {
195
+ const tmpDir = path.join(storeDir, `.tmp-${slug}-${process.pid}`);
196
+ fs.rmSync(tmpDir, { recursive: true, force: true });
197
+ fs.mkdirSync(tmpDir, { recursive: true });
198
+ try {
199
+ for (const file of files) {
200
+ const target = safeJoin(tmpDir, file.path);
201
+ fs.mkdirSync(path.dirname(target), { recursive: true });
202
+ fs.writeFileSync(target, Buffer.from(file.content_base64, 'base64'));
203
+ }
204
+ const finalDir = path.join(storeDir, slug);
205
+ fs.rmSync(finalDir, { recursive: true, force: true });
206
+ fs.renameSync(tmpDir, finalDir);
207
+ }
208
+ catch (e) {
209
+ fs.rmSync(tmpDir, { recursive: true, force: true });
210
+ throw e;
211
+ }
212
+ }
213
+ /** Remove a slug's store dir and mount symlink. Only unlinks the symlink
214
+ * when it actually points into `storeDir` — a repointed or foreign symlink
215
+ * (or a real directory) is left alone rather than risk deleting user data. */
216
+ function removeSlug(storeDir, mountDir, slug) {
217
+ const linkPath = path.join(mountDir, slug);
218
+ const stat = fs.lstatSync(linkPath, { throwIfNoEntry: false });
219
+ if (stat?.isSymbolicLink()) {
220
+ const resolved = path.resolve(mountDir, fs.readlinkSync(linkPath));
221
+ const storeSlugDir = path.join(storeDir, slug);
222
+ if (resolved === storeSlugDir) {
223
+ fs.unlinkSync(linkPath);
224
+ }
225
+ }
226
+ fs.rmSync(path.join(storeDir, slug), { recursive: true, force: true });
227
+ }
228
+ /**
229
+ * Apply one `skills_sync` response to disk. Never called in `--check` mode
230
+ * (Task 6's caller diffs against `loadState` output itself for that path).
231
+ */
232
+ export function applySyncResponse(data, opts = {}) {
233
+ // Absolutize (mirrors resolvePullDest in skills-pull.ts): a relative
234
+ // storeDir/mountDir would otherwise reach safeJoin's containment check as
235
+ // a relative `dest` while its computed `target` is always absolute
236
+ // (path.resolve), so `target.startsWith(dest + sep)` could never match
237
+ // and every `update` entry would be rejected as an unsafe path.
238
+ const storeDir = path.resolve(expandHome(opts.storeDir ?? defaultStoreDir()));
239
+ const mountDir = path.resolve(expandHome(opts.mountDir ?? defaultMountDir()));
240
+ fs.mkdirSync(storeDir, { recursive: true });
241
+ sweepStaleTmpDirs(storeDir);
242
+ const state = loadState(storeDir);
243
+ const applied = [];
244
+ const removed = [];
245
+ const failed = [];
246
+ for (const entry of data.skills) {
247
+ if (!SLUG_RE.test(entry.slug)) {
248
+ failed.push(entry.slug);
249
+ continue;
250
+ }
251
+ if (entry.action === 'update') {
252
+ try {
253
+ writeSlugAtomic(storeDir, entry.slug, entry.files ?? []);
254
+ }
255
+ catch (e) {
256
+ logError(`skills sync: ${entry.slug} failed: ${e instanceof Error ? e.message : String(e)}`);
257
+ failed.push(entry.slug);
258
+ continue;
259
+ }
260
+ // Record the synced sha the moment the STORE write succeeds — the content
261
+ // is materialized and sha-addressed regardless of whether the mount step
262
+ // then succeeds. The mount is reconciled separately (best-effort); its
263
+ // failure must never drop the sha, or the server would re-send this slug
264
+ // every sync (on a symlink-unsupported filesystem that re-downloads
265
+ // everything each run, and a large re-sent skill sorting early could
266
+ // starve later slugs out of the byte budget until the batch guard bailed).
267
+ if (entry.sha)
268
+ state[entry.slug] = entry.sha;
269
+ const mount = reconcileMount(storeDir, mountDir, entry.slug);
270
+ if (mount === 'mounted') {
271
+ applied.push(entry.slug);
272
+ }
273
+ else {
274
+ // Store is materialized and the sha recorded (no re-download); only the
275
+ // mount could not be made. Surface it once for this content change.
276
+ logError(mountFailureMessage(entry.slug, mount));
277
+ failed.push(entry.slug);
278
+ }
279
+ }
280
+ else if (entry.action === 'unchanged') {
281
+ // The server said "unchanged" only because our digest matched — but the
282
+ // store may be gone from disk (dir hand-deleted, or a state file restored
283
+ // from backup without its store). Trust the STORE, not the state file:
284
+ // carry the sha forward only when the store dir is present; otherwise DROP
285
+ // it so the next sync sees a digest miss and re-fetches (self-heal). The
286
+ // mount symlink is NOT part of this decision — see isSlugMaterialized.
287
+ if (entry.sha && isSlugMaterialized(storeDir, entry.slug)) {
288
+ state[entry.slug] = entry.sha;
289
+ // Retry the mount every sync (a transient/absent mount can recover),
290
+ // but never let it drop the sha or re-download. A real dir winning the
291
+ // mount ('blocked') is benign and self-heals to exit 0; a
292
+ // symlink-unsupported filesystem ('unsupported') keeps the slug failed
293
+ // (persistent exit 4 — the store is downloaded but can't be mounted).
294
+ if (reconcileMount(storeDir, mountDir, entry.slug) === 'unsupported') {
295
+ logError(mountFailureMessage(entry.slug, 'unsupported'));
296
+ failed.push(entry.slug);
297
+ }
298
+ }
299
+ else {
300
+ delete state[entry.slug];
301
+ }
302
+ }
303
+ else if (entry.action === 'removed') {
304
+ try {
305
+ removeSlug(storeDir, mountDir, entry.slug);
306
+ delete state[entry.slug];
307
+ removed.push(entry.slug);
308
+ }
309
+ catch (e) {
310
+ // Keep the state entry so the next sync retries this removal —
311
+ // a failed delete here (e.g. a locked store dir) must not be
312
+ // reported as done, and the batch's already-applied updates
313
+ // still need saveState() below to land.
314
+ logError(`skills sync: ${entry.slug} failed: ${e instanceof Error ? e.message : String(e)}`);
315
+ failed.push(entry.slug);
316
+ }
317
+ }
318
+ else if (entry.action === 'error') {
319
+ // Surface the server's per-slug code (NOT_FOUND / TOO_LARGE) like the
320
+ // update/removed branches log their failures — otherwise a server-side
321
+ // sync error was silently swallowed into the failed count with no reason.
322
+ logError(`skills sync: ${entry.slug} failed${entry.code ? ` (${entry.code})` : ''}`);
323
+ failed.push(entry.slug);
324
+ }
325
+ }
326
+ saveState(storeDir, state);
327
+ return { state, applied, removed, failed };
328
+ }
329
+ //# sourceMappingURL=skills-sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills-sync.js","sourceRoot":"","sources":["../../src/commands/skills-sync.ts"],"names":[],"mappings":"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"}