@pi-stef/catalog 0.3.1 → 0.3.3

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-stef/catalog",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Pi extension for managing skill/package catalogs.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -111,7 +111,13 @@ export async function syncCommand(
111
111
  errors: [],
112
112
  };
113
113
 
114
- // --- 1. Pull remote catalog (into memory only) ---------------------------
114
+ // --- 1. Read local state BEFORE pulling ---------------------------------
115
+ // We need this to detect local-only packages and version changes
116
+ // (e.g., from `pi update`) that haven't been pushed yet.
117
+ const localCatalogBeforePull = readCatalog(ctx.home);
118
+ const localLockBeforePull = readLock(ctx.home);
119
+
120
+ // --- 2. Pull remote catalog (into memory only) ---------------------------
115
121
  let remoteCatalog = false;
116
122
  let pulledData: { catalog: CatalogYaml; lock: LockFile } | undefined;
117
123
  try {
@@ -124,9 +130,48 @@ export async function syncCommand(
124
130
  summary.errors.push(message);
125
131
  }
126
132
 
127
- // --- 2. Reconcile --------------------------------------------------------
133
+ // --- 3. Reconcile --------------------------------------------------------
128
134
  // Use pulled catalog if available, otherwise read from disk
129
- const catalog = pulledData ? pulledData.catalog : readCatalog(ctx.home);
135
+ let catalog = pulledData ? pulledData.catalog : readCatalog(ctx.home);
136
+
137
+ // Merge local-only packages into the catalog.
138
+ // When a user adds a package locally (ct add) and then syncs, the pull
139
+ // would overwrite their addition. Detect local packages not in the remote
140
+ // and merge them back so they get pushed.
141
+ let hasLocalOnlyPackages = false;
142
+ if (pulledData) {
143
+ for (const [key, pkg] of Object.entries(localCatalogBeforePull.packages)) {
144
+ if (!(key in catalog.packages)) {
145
+ catalog.packages[key] = pkg;
146
+ hasLocalOnlyPackages = true;
147
+ }
148
+ }
149
+ }
150
+
151
+ // Detect local lock changes (e.g., from `pi update` bumping versions).
152
+ // If the local lock has different versions than the remote lock, we need
153
+ // to push so the remote gist reflects the actual installed state.
154
+ let hasLocalLockChanges = false;
155
+ if (pulledData) {
156
+ const remoteLock = pulledData.lock;
157
+ for (const [key, localEntry] of Object.entries(localLockBeforePull.packages)) {
158
+ const remoteEntry = remoteLock.packages[key];
159
+ if (!remoteEntry || remoteEntry.version !== localEntry.version) {
160
+ hasLocalLockChanges = true;
161
+ break;
162
+ }
163
+ }
164
+ // Also check for packages in remote but removed locally
165
+ if (!hasLocalLockChanges) {
166
+ for (const key of Object.keys(remoteLock.packages)) {
167
+ if (!(key in localLockBeforePull.packages)) {
168
+ hasLocalLockChanges = true;
169
+ break;
170
+ }
171
+ }
172
+ }
173
+ }
174
+
130
175
  const installed = scanInstalled(ctx.home);
131
176
 
132
177
  // Build catalog entries for reconcile
@@ -206,7 +251,7 @@ export async function syncCommand(
206
251
  const hasGist = readCachedGistId(ctx.home) !== undefined;
207
252
  const localHasPackages = Object.keys(catalog.packages).length > 0;
208
253
 
209
- if (force || summary.actionCount > 0 || (!hasGist && localHasPackages)) {
254
+ if (force || summary.actionCount > 0 || hasLocalOnlyPackages || hasLocalLockChanges || (!hasGist && localHasPackages)) {
210
255
  try {
211
256
  const updatedCatalog = readCatalog(ctx.home);
212
257
  const updatedLock = readLock(ctx.home);
@@ -237,7 +282,7 @@ export async function syncCommand(
237
282
  }
238
283
  }
239
284
 
240
- if (summary.actionCount === 0 && summary.errors.length === 0 && !force) {
285
+ if (summary.actionCount === 0 && summary.errors.length === 0 && !force && !hasLocalOnlyPackages && !hasLocalLockChanges) {
241
286
  ctx.ui.notify("Catalog already up to date.", "info");
242
287
  return;
243
288
  }
@@ -247,6 +292,12 @@ export async function syncCommand(
247
292
  if (summary.pulled) {
248
293
  parts.push("Pulled remote catalog.");
249
294
  }
295
+ if (hasLocalOnlyPackages) {
296
+ parts.push("Merged local-only packages.");
297
+ }
298
+ if (hasLocalLockChanges) {
299
+ parts.push("Pushed local version updates.");
300
+ }
250
301
  if (plan.installs.length > 0) {
251
302
  parts.push(`${plan.installs.length} install(s): ${plan.installs.map((a) => a.key).join(", ")}`);
252
303
  }