@newlandnpt/pep-cli 0.4.1 → 0.5.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 (3) hide show
  1. package/README.md +17 -3
  2. package/dist/pep.cjs +40 -18
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -137,9 +137,23 @@ pep skills add group/sub/project -p # move it into this project
137
137
  `pep skills list` prints each repository with the directory it lives in, which is what `-p` matches
138
138
  against.
139
139
 
140
- Deleting a skill folder by hand is fine — `pep skills update` notices it is missing and writes it
141
- again. It checks the disk, not just its own ledger, so it cannot report "already up to date" about
142
- files that are no longer there.
140
+ ### Deleting a skill
141
+
142
+ Delete a skill folder by hand and it **stays deleted**. `update` refreshes what is still on disk
143
+ and leaves the rest alone, naming them so the run is not silent:
144
+
145
+ ```
146
+ group/sub/project: updated b
147
+ deleted locally, left alone: a
148
+ (`pep skills add group/sub/project` puts them back)
149
+ ```
150
+
151
+ `add` is the command that installs, so that is what brings a deleted skill back. This split is
152
+ deliberate: `update` means "bring what I have up to date", not "refill everything the repository
153
+ offers".
154
+
155
+ Skills that are **new upstream** are still installed by `update` — the test is "was mine and is now
156
+ gone", not "is absent".
143
157
 
144
158
  `pep auth token` writes only the access token to stdout, so agents can use it without parsing status
145
159
  text. Refresh occurs automatically shortly before expiry and the rotated refresh token replaces the old
package/dist/pep.cjs CHANGED
@@ -1195,14 +1195,17 @@ function hashSkill(files) {
1195
1195
  }
1196
1196
  return hash.digest("hex").slice(0, 32);
1197
1197
  }
1198
- async function allPresent(directory, skills, linkedInto) {
1198
+ function pickSkipped(previous, skipped) {
1199
+ return Object.fromEntries(
1200
+ skipped.map((skill) => [skill, previous?.skills[skill] ?? ""])
1201
+ );
1202
+ }
1203
+ async function missingSkills(directory, skills) {
1204
+ const gone = /* @__PURE__ */ new Set();
1199
1205
  for (const skill of skills) {
1200
- if (!await (0, import_promises4.stat)((0, import_node_path4.join)(directory, skill)).catch(() => null)) return false;
1201
- if (linkedInto !== void 0 && !await (0, import_promises4.stat)((0, import_node_path4.join)(linkedInto, skill)).catch(() => null)) {
1202
- return false;
1203
- }
1206
+ if (!await (0, import_promises4.stat)((0, import_node_path4.join)(directory, skill)).catch(() => null)) gone.add(skill);
1204
1207
  }
1205
- return true;
1208
+ return gone;
1206
1209
  }
1207
1210
  async function updateSkills(dependencies) {
1208
1211
  const doFetch = dependencies.fetch ?? globalThis.fetch;
@@ -1220,8 +1223,9 @@ async function updateSkills(dependencies) {
1220
1223
  const state = await dependencies.stateStore.read();
1221
1224
  const previous = state?.packages[name];
1222
1225
  const sameTarget = previous?.directory === dependencies.directory && previous?.linkedInto === dependencies.linkInto;
1223
- const intact = sameTarget && previous !== void 0 && await allPresent(previous.directory, Object.keys(previous.skills), previous.linkedInto);
1224
- if (commit !== void 0 && commit === previous?.commit && intact) {
1226
+ const gone = previous !== void 0 && sameTarget ? await missingSkills(previous.directory, Object.keys(previous.skills)) : /* @__PURE__ */ new Set();
1227
+ const nothingToRestore = !dependencies.restoreMissing || gone.size === 0;
1228
+ if (commit !== void 0 && commit === previous?.commit && sameTarget && nothingToRestore) {
1225
1229
  await response.body?.cancel();
1226
1230
  return { status: "unchanged", name, commit };
1227
1231
  }
@@ -1233,7 +1237,9 @@ async function updateSkills(dependencies) {
1233
1237
  if (group) group.push(file);
1234
1238
  else bySkill.set(file.skill, [file]);
1235
1239
  }
1236
- const skills = [...bySkill.keys()].sort();
1240
+ const inArchive = [...bySkill.keys()].sort();
1241
+ const skipped = dependencies.restoreMissing ? [] : inArchive.filter((skill) => gone.has(skill));
1242
+ const skills = inArchive.filter((skill) => !skipped.includes(skill));
1237
1243
  const hashes = {};
1238
1244
  const updated = [];
1239
1245
  const unchanged = [];
@@ -1241,10 +1247,11 @@ async function updateSkills(dependencies) {
1241
1247
  const hash = hashSkill(bySkill.get(skill));
1242
1248
  hashes[skill] = hash;
1243
1249
  const before = previous?.skills[skill];
1244
- if (before !== void 0 && before !== "" && before === hash) unchanged.push(skill);
1250
+ if (gone.has(skill)) updated.push(skill);
1251
+ else if (before !== void 0 && before !== "" && before === hash) unchanged.push(skill);
1245
1252
  else updated.push(skill);
1246
1253
  }
1247
- const removed = Object.keys(previous?.skills ?? {}).filter((skill) => !skills.includes(skill));
1254
+ const removed = Object.keys(previous?.skills ?? {}).filter((skill) => !inArchive.includes(skill));
1248
1255
  if (previous !== void 0) {
1249
1256
  const stale = sameTarget ? removed : Object.keys(previous.skills);
1250
1257
  for (const skill of stale) {
@@ -1258,6 +1265,7 @@ async function updateSkills(dependencies) {
1258
1265
  await (0, import_promises4.rm)((0, import_node_path4.join)(dependencies.directory, skill), { recursive: true, force: true });
1259
1266
  }
1260
1267
  for (const file of files) {
1268
+ if (skipped.includes(file.skill)) continue;
1261
1269
  const target = (0, import_node_path4.join)(dependencies.directory, file.skill, ...file.path.split("/"));
1262
1270
  await (0, import_promises4.mkdir)((0, import_node_path4.dirname)(target), { recursive: true });
1263
1271
  await (0, import_promises4.writeFile)(target, file.data);
@@ -1282,7 +1290,9 @@ async function updateSkills(dependencies) {
1282
1290
  ...commit ? { commit } : {},
1283
1291
  directory: dependencies.directory,
1284
1292
  ...dependencies.linkInto !== void 0 ? { linkedInto: dependencies.linkInto } : {},
1285
- skills: hashes
1293
+ // 跳过的那些**原样留着上次的哈希** —— 账上仍然认它是这个包的(将来 add 能补回来,
1294
+ // 上游删掉时也还清得动),只是这次没去碰它。
1295
+ skills: { ...pickSkipped(previous, skipped), ...hashes }
1286
1296
  }
1287
1297
  }
1288
1298
  });
@@ -1292,8 +1302,9 @@ async function updateSkills(dependencies) {
1292
1302
  updated,
1293
1303
  unchanged,
1294
1304
  skills,
1295
- fileCount: files.length,
1305
+ fileCount: files.filter((one) => !skipped.includes(one.skill)).length,
1296
1306
  removed,
1307
+ skipped,
1297
1308
  directory: dependencies.directory,
1298
1309
  ...dependencies.linkInto !== void 0 ? { linkedInto: dependencies.linkInto } : {},
1299
1310
  ...copiedCount > 0 ? { copiedCount } : {},
@@ -1302,7 +1313,7 @@ async function updateSkills(dependencies) {
1302
1313
  }
1303
1314
 
1304
1315
  // src/cli.ts
1305
- var VERSION = false ? "0.0.0-dev" : "0.4.1";
1316
+ var VERSION = false ? "0.0.0-dev" : "0.5.0";
1306
1317
  function usage() {
1307
1318
  return `PEP CLI ${VERSION}
1308
1319
 
@@ -1358,8 +1369,10 @@ it already lives and never moves anything. On update, -p and --dir instead NARRO
1358
1369
  repositories installed there: \`pep skills update -p\` refreshes only this project's, and reports
1359
1370
  nothing to do when the project has none. To move a repository, add it again with the new flag \u2014
1360
1371
  add is where the location is decided, and the copy in the old location is then removed.
1361
- Deleting a skill folder by hand is fine: update notices it is gone and writes it again.
1362
- Either way update only touches skills it wrote itself; anything you put there by hand is left alone.
1372
+ Delete a skill folder by hand and update LEAVES IT DELETED \u2014 it refreshes what is still there and
1373
+ reports the ones it left alone. Run \`pep skills add <repo>\` to put them back; add is the command
1374
+ that installs. Either way update only touches skills it wrote itself; anything you put there by
1375
+ hand is left alone.
1363
1376
 
1364
1377
  \`pep docs list\` prints the documents this account can read (path + description); feed a path
1365
1378
  straight to \`pep docs get\` to print that document as markdown on stdout. The documentation
@@ -1386,6 +1399,10 @@ function reportUpdate(result) {
1386
1399
  if (result.unchanged.length > 0) {
1387
1400
  console.log(` unchanged: ${result.unchanged.join(" ")}`);
1388
1401
  }
1402
+ if (result.skipped.length > 0) {
1403
+ console.log(` deleted locally, left alone: ${result.skipped.join(" ")}`);
1404
+ console.log(` (\`pep skills add ${result.name}\` puts them back)`);
1405
+ }
1389
1406
  console.log(` ${result.fileCount} file(s) in ${result.directory}`);
1390
1407
  if (result.linkedInto !== void 0) {
1391
1408
  console.log(
@@ -1576,17 +1593,22 @@ ${usage()}`);
1576
1593
  const stateStore = fileSkillsStateStore();
1577
1594
  const state = await stateStore.read();
1578
1595
  const installed = Object.keys(state?.packages ?? {}).sort();
1579
- const run2 = async (source, target) => reportUpdate(
1596
+ const run2 = async (source, target, restoreMissing = false) => reportUpdate(
1580
1597
  await updateSkills({
1581
1598
  ...remote,
1582
1599
  source,
1583
1600
  directory: target.directory,
1584
1601
  ...target.linkInto !== void 0 ? { linkInto: target.linkInto } : {},
1602
+ ...restoreMissing ? { restoreMissing: true } : {},
1585
1603
  stateStore
1586
1604
  })
1587
1605
  );
1588
1606
  if (command === "add") {
1589
- await run2(requested, targetForSource(chosenTarget, state?.packages[requested]));
1607
+ await run2(
1608
+ requested,
1609
+ targetForSource(chosenTarget, state?.packages[requested]),
1610
+ true
1611
+ );
1590
1612
  return;
1591
1613
  }
1592
1614
  if (installed.length === 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newlandnpt/pep-cli",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "PEP CLI —— 凭 PEP 身份读取 Newland 开发者文档与 agent skills",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",