@ichintansoni/skills-master 0.2.4 → 0.3.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 +3 -1
  2. package/dist/bin.js +54 -6
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -23,6 +23,8 @@ npx @ichintansoni/skills-master doctor # same detection,
23
23
 
24
24
  `status` and `doctor` share their drift detection but answer different questions. `status` is an inventory — it lists every installed skill with its version, targets, and whether the output has been edited or deleted, works entirely offline, and always exits 0, so it is safe to pipe (`status --json`, `status --problems`, `status <name>…`). `doctor` is the gate: same findings, but it exits non-zero, which is what you want in CI.
25
25
 
26
+ Both answer "is what I installed intact?" — neither answers "is what I installed **current?**", because both compare emitted files against the lockfile and never the lockfile against the source. An install pinned to a release from six months ago is perfectly healthy by that measure. `update --check` is the second gate: it resolves the content, reports which skills are behind, and exits non-zero. It catches the case a version comparison misses — a skill edited upstream *without* a version bump, which is what resource-file and description changes usually are.
27
+
26
28
  `add` writes, per detected tool:
27
29
 
28
30
  | Tool | Output |
@@ -59,7 +61,7 @@ Run `skills-master <command> --help` for the full flag list on any of these.
59
61
  |---|---|
60
62
  | `init` | Detect which tools the project uses and write `skills-master.json`. `--target` · `--commit` · `--ref` · `--force` |
61
63
  | `add <names…>` | Install skills by name, category, or class |
62
- | `update [names…]` | Re-install skills whose **content** changed upstream. Only touches targets a skill is already installed to |
64
+ | `update [names…]` | Re-install skills whose **content** changed upstream. Only touches targets a skill is already installed to. `--check` reports what is behind and exits non-zero |
63
65
  | `sync [names…]` | Re-emit to match the **current config** — new targets, moved `paths`, deleted files. `--overwrite` · `--prune` · `--dry-run` |
64
66
  | `remove <names…>` | Remove installed skills. `--target` removes from one tool only |
65
67
  | `status [names…]` | Inventory: versions, targets, and local-edit state. Offline, always exits 0. `--problems` · `--json` |
package/dist/bin.js CHANGED
@@ -4,7 +4,7 @@
4
4
  import { Command } from "commander";
5
5
 
6
6
  // package.json
7
- var version = "0.2.4";
7
+ var version = "0.3.0";
8
8
 
9
9
  // src/schema/projectConfig.ts
10
10
  import { z } from "zod";
@@ -1216,6 +1216,52 @@ async function updateCommand(opts) {
1216
1216
  const content = await resolveContent({ content: opts.content, ref, cwd: opts.cwd });
1217
1217
  const paths = resolvePaths(cfg);
1218
1218
  const prefix = opts.dryRun ? "[dry-run] " : "";
1219
+ if (opts.check) {
1220
+ const behind = [];
1221
+ for (const name of names.sort()) {
1222
+ const locked = lock.skills[name];
1223
+ if (!locked) {
1224
+ log.warn(`"${name}" is not installed.`);
1225
+ skipped.push(name);
1226
+ continue;
1227
+ }
1228
+ let skill;
1229
+ try {
1230
+ skill = content.loadSkill(name);
1231
+ } catch (err) {
1232
+ if (err instanceof SkillNotFoundError) {
1233
+ log.warn(`"${name}" no longer exists in the content library.`);
1234
+ } else {
1235
+ log.error(
1236
+ `Failed to load "${name}": ${err instanceof Error ? err.message : String(err)}`
1237
+ );
1238
+ }
1239
+ skipped.push(name);
1240
+ continue;
1241
+ }
1242
+ const available = skill.frontmatter["x-skills-master"].version;
1243
+ const sourceChanged = locked.sourceHash !== sourceHashOf(skill);
1244
+ const refChanged = lock.contentRef !== ref;
1245
+ if (!sourceChanged && !refChanged) {
1246
+ upToDate.push(name);
1247
+ continue;
1248
+ }
1249
+ const reason = !sourceChanged ? "ref" : available !== locked.version ? "version" : "content";
1250
+ behind.push({ name, installed: locked.version, available, reason });
1251
+ }
1252
+ for (const b of behind) {
1253
+ const detail = b.reason === "version" ? `${b.installed} \u2192 ${b.available}` : b.reason === "content" ? `${b.installed} (content changed without a version bump)` : `${b.installed} (content ref moved to ${ref})`;
1254
+ log.info(`\u2191 ${b.name.padEnd(32)} ${detail}`);
1255
+ }
1256
+ if (behind.length === 0) {
1257
+ log.success(`All ${upToDate.length} installed skill(s) are current.`);
1258
+ } else {
1259
+ log.warn(
1260
+ `${behind.length} of ${behind.length + upToDate.length} installed skill(s) are behind \u2014 run \`update\`.`
1261
+ );
1262
+ }
1263
+ return { updated, upToDate, skipped, behind };
1264
+ }
1219
1265
  for (const name of names.sort()) {
1220
1266
  const locked = lock.skills[name];
1221
1267
  if (!locked) {
@@ -2297,17 +2343,19 @@ program.command("add <names...>").description("Install skills (by name, category
2297
2343
  })
2298
2344
  )
2299
2345
  );
2300
- program.command("update [names...]").description("Re-install skills whose content changed").option("--dry-run", "preview without writing").option("--overwrite", "force re-install, replacing local edits").option("--content <dir>", "local skills directory").option("--ref <ref>", "content git ref (tag/branch/sha)").action(
2301
- (names, opts) => run(
2302
- () => updateCommand({
2346
+ program.command("update [names...]").description("Re-install skills whose content changed").option("--check", "report which installed skills are behind; exit 1 if any are").option("--dry-run", "preview without writing").option("--overwrite", "force re-install, replacing local edits").option("--content <dir>", "local skills directory").option("--ref <ref>", "content git ref (tag/branch/sha)").action(
2347
+ (names, opts) => run(async () => {
2348
+ const res = await updateCommand({
2303
2349
  cwd: process.cwd(),
2304
2350
  names,
2351
+ check: opts.check,
2305
2352
  dryRun: opts.dryRun,
2306
2353
  overwrite: opts.overwrite,
2307
2354
  content: opts.content,
2308
2355
  ref: opts.ref
2309
- })
2310
- )
2356
+ });
2357
+ return opts.check ? (res.behind?.length ?? 0) === 0 : void 0;
2358
+ }, true)
2311
2359
  );
2312
2360
  program.command("remove <names...>").description("Remove installed skills").option("--target <list>", "comma list or 'all'").option("--dry-run", "preview without writing").action(
2313
2361
  (names, opts) => run(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ichintansoni/skills-master",
3
- "version": "0.2.4",
3
+ "version": "0.3.0",
4
4
  "description": "Install tool-agnostic Apple & Android development skills into any AI coding tool (Claude Code, Cursor, Copilot, AGENTS.md).",
5
5
  "keywords": [
6
6
  "skills",