@newlandnpt/pep-cli 0.4.0 → 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 +18 -0
  2. package/dist/pep.cjs +43 -10
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -137,6 +137,24 @@ 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
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".
157
+
140
158
  `pep auth token` writes only the access token to stdout, so agents can use it without parsing status
141
159
  text. Refresh occurs automatically shortly before expiry and the rotated refresh token replaces the old
142
160
  credential atomically.
package/dist/pep.cjs CHANGED
@@ -1195,6 +1195,18 @@ function hashSkill(files) {
1195
1195
  }
1196
1196
  return hash.digest("hex").slice(0, 32);
1197
1197
  }
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();
1205
+ for (const skill of skills) {
1206
+ if (!await (0, import_promises4.stat)((0, import_node_path4.join)(directory, skill)).catch(() => null)) gone.add(skill);
1207
+ }
1208
+ return gone;
1209
+ }
1198
1210
  async function updateSkills(dependencies) {
1199
1211
  const doFetch = dependencies.fetch ?? globalThis.fetch;
1200
1212
  const name = dependencies.source;
@@ -1211,7 +1223,9 @@ async function updateSkills(dependencies) {
1211
1223
  const state = await dependencies.stateStore.read();
1212
1224
  const previous = state?.packages[name];
1213
1225
  const sameTarget = previous?.directory === dependencies.directory && previous?.linkedInto === dependencies.linkInto;
1214
- if (commit !== void 0 && commit === previous?.commit && sameTarget) {
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) {
1215
1229
  await response.body?.cancel();
1216
1230
  return { status: "unchanged", name, commit };
1217
1231
  }
@@ -1223,7 +1237,9 @@ async function updateSkills(dependencies) {
1223
1237
  if (group) group.push(file);
1224
1238
  else bySkill.set(file.skill, [file]);
1225
1239
  }
1226
- 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));
1227
1243
  const hashes = {};
1228
1244
  const updated = [];
1229
1245
  const unchanged = [];
@@ -1231,10 +1247,11 @@ async function updateSkills(dependencies) {
1231
1247
  const hash = hashSkill(bySkill.get(skill));
1232
1248
  hashes[skill] = hash;
1233
1249
  const before = previous?.skills[skill];
1234
- 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);
1235
1252
  else updated.push(skill);
1236
1253
  }
1237
- const removed = Object.keys(previous?.skills ?? {}).filter((skill) => !skills.includes(skill));
1254
+ const removed = Object.keys(previous?.skills ?? {}).filter((skill) => !inArchive.includes(skill));
1238
1255
  if (previous !== void 0) {
1239
1256
  const stale = sameTarget ? removed : Object.keys(previous.skills);
1240
1257
  for (const skill of stale) {
@@ -1248,6 +1265,7 @@ async function updateSkills(dependencies) {
1248
1265
  await (0, import_promises4.rm)((0, import_node_path4.join)(dependencies.directory, skill), { recursive: true, force: true });
1249
1266
  }
1250
1267
  for (const file of files) {
1268
+ if (skipped.includes(file.skill)) continue;
1251
1269
  const target = (0, import_node_path4.join)(dependencies.directory, file.skill, ...file.path.split("/"));
1252
1270
  await (0, import_promises4.mkdir)((0, import_node_path4.dirname)(target), { recursive: true });
1253
1271
  await (0, import_promises4.writeFile)(target, file.data);
@@ -1272,7 +1290,9 @@ async function updateSkills(dependencies) {
1272
1290
  ...commit ? { commit } : {},
1273
1291
  directory: dependencies.directory,
1274
1292
  ...dependencies.linkInto !== void 0 ? { linkedInto: dependencies.linkInto } : {},
1275
- skills: hashes
1293
+ // 跳过的那些**原样留着上次的哈希** —— 账上仍然认它是这个包的(将来 add 能补回来,
1294
+ // 上游删掉时也还清得动),只是这次没去碰它。
1295
+ skills: { ...pickSkipped(previous, skipped), ...hashes }
1276
1296
  }
1277
1297
  }
1278
1298
  });
@@ -1282,8 +1302,9 @@ async function updateSkills(dependencies) {
1282
1302
  updated,
1283
1303
  unchanged,
1284
1304
  skills,
1285
- fileCount: files.length,
1305
+ fileCount: files.filter((one) => !skipped.includes(one.skill)).length,
1286
1306
  removed,
1307
+ skipped,
1287
1308
  directory: dependencies.directory,
1288
1309
  ...dependencies.linkInto !== void 0 ? { linkedInto: dependencies.linkInto } : {},
1289
1310
  ...copiedCount > 0 ? { copiedCount } : {},
@@ -1292,7 +1313,7 @@ async function updateSkills(dependencies) {
1292
1313
  }
1293
1314
 
1294
1315
  // src/cli.ts
1295
- var VERSION = false ? "0.0.0-dev" : "0.4.0";
1316
+ var VERSION = false ? "0.0.0-dev" : "0.5.0";
1296
1317
  function usage() {
1297
1318
  return `PEP CLI ${VERSION}
1298
1319
 
@@ -1348,7 +1369,10 @@ it already lives and never moves anything. On update, -p and --dir instead NARRO
1348
1369
  repositories installed there: \`pep skills update -p\` refreshes only this project's, and reports
1349
1370
  nothing to do when the project has none. To move a repository, add it again with the new flag \u2014
1350
1371
  add is where the location is decided, and the copy in the old location is then removed.
1351
- 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.
1352
1376
 
1353
1377
  \`pep docs list\` prints the documents this account can read (path + description); feed a path
1354
1378
  straight to \`pep docs get\` to print that document as markdown on stdout. The documentation
@@ -1375,6 +1399,10 @@ function reportUpdate(result) {
1375
1399
  if (result.unchanged.length > 0) {
1376
1400
  console.log(` unchanged: ${result.unchanged.join(" ")}`);
1377
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
+ }
1378
1406
  console.log(` ${result.fileCount} file(s) in ${result.directory}`);
1379
1407
  if (result.linkedInto !== void 0) {
1380
1408
  console.log(
@@ -1565,17 +1593,22 @@ ${usage()}`);
1565
1593
  const stateStore = fileSkillsStateStore();
1566
1594
  const state = await stateStore.read();
1567
1595
  const installed = Object.keys(state?.packages ?? {}).sort();
1568
- const run2 = async (source, target) => reportUpdate(
1596
+ const run2 = async (source, target, restoreMissing = false) => reportUpdate(
1569
1597
  await updateSkills({
1570
1598
  ...remote,
1571
1599
  source,
1572
1600
  directory: target.directory,
1573
1601
  ...target.linkInto !== void 0 ? { linkInto: target.linkInto } : {},
1602
+ ...restoreMissing ? { restoreMissing: true } : {},
1574
1603
  stateStore
1575
1604
  })
1576
1605
  );
1577
1606
  if (command === "add") {
1578
- await run2(requested, targetForSource(chosenTarget, state?.packages[requested]));
1607
+ await run2(
1608
+ requested,
1609
+ targetForSource(chosenTarget, state?.packages[requested]),
1610
+ true
1611
+ );
1579
1612
  return;
1580
1613
  }
1581
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.0",
3
+ "version": "0.5.0",
4
4
  "description": "PEP CLI —— 凭 PEP 身份读取 Newland 开发者文档与 agent skills",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",