@meltstudio/meltctl 5.8.0 → 5.9.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 (2) hide show
  1. package/dist/index.js +282 -142
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7450,7 +7450,7 @@ var CLI_VERSION;
7450
7450
  var init_version = __esm({
7451
7451
  "src/utils/version.ts"() {
7452
7452
  "use strict";
7453
- CLI_VERSION = "5.8.0";
7453
+ CLI_VERSION = "5.9.0";
7454
7454
  }
7455
7455
  });
7456
7456
 
@@ -42515,6 +42515,253 @@ async function logoutCommand() {
42515
42515
  // src/index.ts
42516
42516
  init_version_check();
42517
42517
 
42518
+ // src/utils/skills-sync.ts
42519
+ init_source();
42520
+
42521
+ // src/utils/debug.ts
42522
+ init_source();
42523
+ function debugLog(message) {
42524
+ if (process.env["MELTCTL_DEBUG"]) {
42525
+ console.error(source_default.dim(`[debug] ${message}`));
42526
+ }
42527
+ }
42528
+
42529
+ // src/commands/skills.ts
42530
+ init_source();
42531
+ var import_fs_extra3 = __toESM(require_lib(), 1);
42532
+ import { createHash } from "crypto";
42533
+ import os5 from "os";
42534
+ import path5 from "path";
42535
+
42536
+ // src/utils/api.ts
42537
+ init_source();
42538
+ async function getToken() {
42539
+ const envToken = process.env["MELTCTL_TOKEN"];
42540
+ if (envToken) {
42541
+ return envToken;
42542
+ }
42543
+ const auth = await getStoredAuth();
42544
+ if (!auth) {
42545
+ console.error(
42546
+ source_default.red(
42547
+ "Not authenticated. Run `npx @meltstudio/meltctl@latest login` or set MELTCTL_TOKEN for CI."
42548
+ )
42549
+ );
42550
+ process.exit(1);
42551
+ }
42552
+ if (new Date(auth.expiresAt) <= /* @__PURE__ */ new Date()) {
42553
+ console.error(
42554
+ source_default.red(
42555
+ "Session expired. Run `npx @meltstudio/meltctl@latest login` to re-authenticate, or set MELTCTL_TOKEN for CI."
42556
+ )
42557
+ );
42558
+ process.exit(1);
42559
+ }
42560
+ return auth.token;
42561
+ }
42562
+ async function getClient() {
42563
+ const token = await getToken();
42564
+ return createMeltClient({ baseUrl: API_BASE, token });
42565
+ }
42566
+
42567
+ // src/commands/skills.ts
42568
+ var MARKER_FILE = ".melt-skills.json";
42569
+ function getSkillsRoot() {
42570
+ return path5.join(os5.homedir(), ".claude", "skills");
42571
+ }
42572
+ function computeSkillHash(files) {
42573
+ const hash2 = createHash("sha256");
42574
+ for (const filePath of Object.keys(files).sort()) {
42575
+ hash2.update(filePath);
42576
+ hash2.update(files[filePath] ?? "");
42577
+ }
42578
+ return hash2.digest("hex").slice(0, 12);
42579
+ }
42580
+ function readMarker(skillsRoot) {
42581
+ const markerPath = path5.join(skillsRoot, MARKER_FILE);
42582
+ if (!import_fs_extra3.default.existsSync(markerPath)) return null;
42583
+ try {
42584
+ const raw = import_fs_extra3.default.readJsonSync(markerPath);
42585
+ const skills2 = Array.isArray(raw.skills) ? raw.skills.map((entry) => {
42586
+ if (typeof entry === "string") {
42587
+ return { name: entry, hash: "" };
42588
+ }
42589
+ if (entry && typeof entry === "object") {
42590
+ const e = entry;
42591
+ return {
42592
+ name: typeof e.name === "string" ? e.name : "",
42593
+ hash: typeof e.hash === "string" ? e.hash : ""
42594
+ };
42595
+ }
42596
+ return { name: "", hash: "" };
42597
+ }) : [];
42598
+ return {
42599
+ version: typeof raw.version === "string" ? raw.version : "",
42600
+ installedAt: typeof raw.installedAt === "string" ? raw.installedAt : "",
42601
+ lastCheckedAt: typeof raw.lastCheckedAt === "string" ? raw.lastCheckedAt : void 0,
42602
+ skills: skills2
42603
+ };
42604
+ } catch {
42605
+ return null;
42606
+ }
42607
+ }
42608
+ function computeDiff(previous, next) {
42609
+ const prevByName = new Map(previous.map((s) => [s.name, s]));
42610
+ const nextNames = new Set(next.map((s) => s.name));
42611
+ const added = [];
42612
+ const changed = [];
42613
+ for (const skill of next) {
42614
+ const prev = prevByName.get(skill.name);
42615
+ if (!prev) {
42616
+ added.push(skill.name);
42617
+ continue;
42618
+ }
42619
+ if (prev.hash && prev.hash !== skill.hash) {
42620
+ changed.push(skill.name);
42621
+ }
42622
+ }
42623
+ const removed = previous.filter((s) => s.name && !nextNames.has(s.name)).map((s) => s.name);
42624
+ return { added, changed, removed };
42625
+ }
42626
+ function applyDevSkills(manifest, skillsRoot) {
42627
+ const previous = readMarker(skillsRoot)?.skills ?? [];
42628
+ const nextEntries = manifest.skills.map((s) => ({
42629
+ name: s.name,
42630
+ hash: computeSkillHash(s.files)
42631
+ }));
42632
+ const diff = computeDiff(previous, nextEntries);
42633
+ for (const name of diff.removed) {
42634
+ import_fs_extra3.default.removeSync(path5.join(skillsRoot, name));
42635
+ }
42636
+ for (const skill of manifest.skills) {
42637
+ const dir = path5.join(skillsRoot, skill.name);
42638
+ import_fs_extra3.default.removeSync(dir);
42639
+ for (const [relPath, content] of Object.entries(skill.files)) {
42640
+ import_fs_extra3.default.outputFileSync(path5.join(dir, relPath), content);
42641
+ }
42642
+ }
42643
+ const now = (/* @__PURE__ */ new Date()).toISOString();
42644
+ const marker = {
42645
+ version: manifest.version,
42646
+ installedAt: now,
42647
+ lastCheckedAt: now,
42648
+ skills: nextEntries
42649
+ };
42650
+ import_fs_extra3.default.outputFileSync(path5.join(skillsRoot, MARKER_FILE), `${JSON.stringify(marker, null, 2)}
42651
+ `);
42652
+ return diff;
42653
+ }
42654
+ function touchMarkerLastChecked(skillsRoot) {
42655
+ const marker = readMarker(skillsRoot);
42656
+ if (!marker) return;
42657
+ marker.lastCheckedAt = (/* @__PURE__ */ new Date()).toISOString();
42658
+ import_fs_extra3.default.outputFileSync(path5.join(skillsRoot, MARKER_FILE), `${JSON.stringify(marker, null, 2)}
42659
+ `);
42660
+ }
42661
+ async function skillsInstallCommand(opts) {
42662
+ const client = await getClient();
42663
+ let manifest;
42664
+ try {
42665
+ manifest = await client.skills.getDevSkills();
42666
+ } catch (error48) {
42667
+ console.error(
42668
+ source_default.red(
42669
+ `Failed to fetch Melt dev skills: ${error48 instanceof Error ? error48.message : "Unknown error"}`
42670
+ )
42671
+ );
42672
+ process.exit(1);
42673
+ }
42674
+ const skillsRoot = getSkillsRoot();
42675
+ const existing = readMarker(skillsRoot);
42676
+ if (existing && existing.version === manifest.version && !opts.force) {
42677
+ console.log(
42678
+ source_default.dim(
42679
+ `
42680
+ Melt dev skills already up to date (v${manifest.version}). Use --force to reinstall.
42681
+ `
42682
+ )
42683
+ );
42684
+ return;
42685
+ }
42686
+ applyDevSkills(manifest, skillsRoot);
42687
+ console.log(
42688
+ source_default.green(
42689
+ `
42690
+ \u2713 Installed ${manifest.skills.length} Melt dev skills (v${manifest.version}) to ~/.claude/skills/.`
42691
+ )
42692
+ );
42693
+ console.log(
42694
+ source_default.dim(
42695
+ " Restart Claude Code to load them (they appear as /melt-dev-plan, /melt-dev-review, \u2026).\n"
42696
+ )
42697
+ );
42698
+ }
42699
+
42700
+ // src/utils/skills-sync.ts
42701
+ var TWENTY_FOUR_HOURS_MS = 24 * 60 * 60 * 1e3;
42702
+ var SKIP_SYNC_COMMANDS = /* @__PURE__ */ new Set(["skills", "login", "logout", "update", "version"]);
42703
+ function formatSyncNotice(oldVersion, newVersion, diff) {
42704
+ const groups = [];
42705
+ if (diff.changed.length > 0) groups.push(`changed ${diff.changed.join(", ")}`);
42706
+ if (diff.added.length > 0) groups.push(`added ${diff.added.join(", ")}`);
42707
+ if (diff.removed.length > 0) groups.push(`removed ${diff.removed.join(", ")}`);
42708
+ const suffix = groups.length > 0 ? `: ${groups.join("; ")}` : "";
42709
+ return `\u2191 Melt dev skills updated (v${oldVersion} \u2192 v${newVersion})${suffix}`;
42710
+ }
42711
+ function isCheckDue(lastCheckedAt, now) {
42712
+ if (!lastCheckedAt) return true;
42713
+ const last = Date.parse(lastCheckedAt);
42714
+ if (Number.isNaN(last)) return true;
42715
+ return now - last >= TWENTY_FOUR_HOURS_MS;
42716
+ }
42717
+ async function maybeSyncSkills(commandName) {
42718
+ try {
42719
+ if (process.env["MELTCTL_SKIP_SKILLS_SYNC"]) {
42720
+ debugLog("Skills sync disabled via MELTCTL_SKIP_SKILLS_SYNC");
42721
+ return;
42722
+ }
42723
+ if (commandName && SKIP_SYNC_COMMANDS.has(commandName)) {
42724
+ debugLog(`Skills sync skipped for command "${commandName}"`);
42725
+ return;
42726
+ }
42727
+ const auth = await getStoredAuth();
42728
+ if (!auth || new Date(auth.expiresAt) <= /* @__PURE__ */ new Date()) {
42729
+ debugLog("Skills sync skipped: not authenticated or token expired");
42730
+ return;
42731
+ }
42732
+ const skillsRoot = getSkillsRoot();
42733
+ const marker = readMarker(skillsRoot);
42734
+ if (!marker) {
42735
+ debugLog("Skills sync skipped: no skills marker (not installed)");
42736
+ return;
42737
+ }
42738
+ if (!isCheckDue(marker.lastCheckedAt, Date.now())) {
42739
+ debugLog("Skills sync skipped: checked within the last 24h");
42740
+ return;
42741
+ }
42742
+ const client = createMeltClient({ baseUrl: API_BASE, token: auth.token });
42743
+ let manifest;
42744
+ try {
42745
+ manifest = await client.skills.getDevSkills();
42746
+ } catch (e) {
42747
+ touchMarkerLastChecked(skillsRoot);
42748
+ debugLog(`Skills sync fetch error: ${e instanceof Error ? e.message : e}`);
42749
+ return;
42750
+ }
42751
+ if (manifest.version === marker.version) {
42752
+ touchMarkerLastChecked(skillsRoot);
42753
+ debugLog("Skills sync: already up to date");
42754
+ return;
42755
+ }
42756
+ const oldVersion = marker.version;
42757
+ const diff = applyDevSkills(manifest, skillsRoot);
42758
+ console.log(source_default.dim(source_default.green(formatSyncNotice(oldVersion, manifest.version, diff))));
42759
+ debugLog(`Skills sync: updated ${oldVersion} \u2192 ${manifest.version}`);
42760
+ } catch (e) {
42761
+ debugLog(`Skills sync error: ${e instanceof Error ? e.message : e}`);
42762
+ }
42763
+ }
42764
+
42518
42765
  // src/commands/version.ts
42519
42766
  init_source();
42520
42767
  init_version_check();
@@ -42611,39 +42858,6 @@ async function versionCheckCommand() {
42611
42858
  // src/commands/standup.ts
42612
42859
  init_source();
42613
42860
  init_dist11();
42614
-
42615
- // src/utils/api.ts
42616
- init_source();
42617
- async function getToken() {
42618
- const envToken = process.env["MELTCTL_TOKEN"];
42619
- if (envToken) {
42620
- return envToken;
42621
- }
42622
- const auth = await getStoredAuth();
42623
- if (!auth) {
42624
- console.error(
42625
- source_default.red(
42626
- "Not authenticated. Run `npx @meltstudio/meltctl@latest login` or set MELTCTL_TOKEN for CI."
42627
- )
42628
- );
42629
- process.exit(1);
42630
- }
42631
- if (new Date(auth.expiresAt) <= /* @__PURE__ */ new Date()) {
42632
- console.error(
42633
- source_default.red(
42634
- "Session expired. Run `npx @meltstudio/meltctl@latest login` to re-authenticate, or set MELTCTL_TOKEN for CI."
42635
- )
42636
- );
42637
- process.exit(1);
42638
- }
42639
- return auth.token;
42640
- }
42641
- async function getClient() {
42642
- const token = await getToken();
42643
- return createMeltClient({ baseUrl: API_BASE, token });
42644
- }
42645
-
42646
- // src/commands/standup.ts
42647
42861
  var EDITOR_HINT = source_default.dim("(type \\e to open your editor)");
42648
42862
  async function promptField(message, required2) {
42649
42863
  const value = await dist_default7({ message: `${message} ${EDITOR_HINT}` });
@@ -42865,12 +43079,12 @@ async function showLeaderboard() {
42865
43079
 
42866
43080
  // src/commands/audit.ts
42867
43081
  init_source();
42868
- var import_fs_extra4 = __toESM(require_lib(), 1);
42869
- import path6 from "path";
43082
+ var import_fs_extra5 = __toESM(require_lib(), 1);
43083
+ import path7 from "path";
42870
43084
 
42871
43085
  // src/utils/git.ts
42872
- var import_fs_extra3 = __toESM(require_lib(), 1);
42873
- import path5 from "path";
43086
+ var import_fs_extra4 = __toESM(require_lib(), 1);
43087
+ import path6 from "path";
42874
43088
  import { execSync as execSync4 } from "child_process";
42875
43089
  function getGitBranch() {
42876
43090
  try {
@@ -42908,34 +43122,34 @@ function getGitRepository() {
42908
43122
  function getProjectName() {
42909
43123
  const cwd = process.cwd();
42910
43124
  try {
42911
- const pkgPath = path5.join(cwd, "package.json");
42912
- if (import_fs_extra3.default.pathExistsSync(pkgPath)) {
42913
- const pkg = import_fs_extra3.default.readJsonSync(pkgPath);
43125
+ const pkgPath = path6.join(cwd, "package.json");
43126
+ if (import_fs_extra4.default.pathExistsSync(pkgPath)) {
43127
+ const pkg = import_fs_extra4.default.readJsonSync(pkgPath);
42914
43128
  if (pkg.name) {
42915
43129
  return pkg.name;
42916
43130
  }
42917
43131
  }
42918
43132
  } catch {
42919
43133
  }
42920
- return path5.basename(cwd);
43134
+ return path6.basename(cwd);
42921
43135
  }
42922
43136
  function extractTicketId(branch) {
42923
43137
  const match = branch.match(/([A-Z]+-\d+)/i);
42924
43138
  return match ? match[1] : null;
42925
43139
  }
42926
43140
  async function findMdFiles(dir) {
42927
- if (!await import_fs_extra3.default.pathExists(dir)) {
43141
+ if (!await import_fs_extra4.default.pathExists(dir)) {
42928
43142
  return [];
42929
43143
  }
42930
43144
  const results = [];
42931
43145
  async function walk(current) {
42932
- const entries = await import_fs_extra3.default.readdir(current, { withFileTypes: true });
43146
+ const entries = await import_fs_extra4.default.readdir(current, { withFileTypes: true });
42933
43147
  for (const entry of entries) {
42934
- const fullPath = path5.join(current, entry.name);
43148
+ const fullPath = path6.join(current, entry.name);
42935
43149
  if (entry.isDirectory()) {
42936
43150
  await walk(fullPath);
42937
43151
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
42938
- const stat = await import_fs_extra3.default.stat(fullPath);
43152
+ const stat = await import_fs_extra4.default.stat(fullPath);
42939
43153
  results.push({ path: fullPath, mtime: stat.mtimeMs });
42940
43154
  }
42941
43155
  }
@@ -42954,15 +43168,15 @@ function detectAuditType(filename) {
42954
43168
  }
42955
43169
  async function autoDetectAuditFile() {
42956
43170
  const cwd = process.cwd();
42957
- const auditsDir = path6.join(cwd, ".audits");
43171
+ const auditsDir = path7.join(cwd, ".audits");
42958
43172
  const auditFiles = await findMdFiles(auditsDir);
42959
43173
  if (auditFiles.length > 0) {
42960
43174
  return auditFiles[0] ?? null;
42961
43175
  }
42962
43176
  const candidates = ["AUDIT.md", "UX-AUDIT.md", "SECURITY-AUDIT.md"];
42963
43177
  for (const name of candidates) {
42964
- const filePath = path6.join(cwd, name);
42965
- if (await import_fs_extra4.default.pathExists(filePath)) {
43178
+ const filePath = path7.join(cwd, name);
43179
+ if (await import_fs_extra5.default.pathExists(filePath)) {
42966
43180
  return filePath;
42967
43181
  }
42968
43182
  }
@@ -42972,7 +43186,7 @@ async function auditSubmitCommand(file2) {
42972
43186
  const client = await getClient();
42973
43187
  let filePath;
42974
43188
  if (file2) {
42975
- filePath = path6.resolve(file2);
43189
+ filePath = path7.resolve(file2);
42976
43190
  } else {
42977
43191
  const detected = await autoDetectAuditFile();
42978
43192
  if (!detected) {
@@ -42984,14 +43198,14 @@ async function auditSubmitCommand(file2) {
42984
43198
  process.exit(1);
42985
43199
  }
42986
43200
  filePath = detected;
42987
- console.log(source_default.dim(`Auto-detected audit file: ${path6.relative(process.cwd(), filePath)}`));
43201
+ console.log(source_default.dim(`Auto-detected audit file: ${path7.relative(process.cwd(), filePath)}`));
42988
43202
  }
42989
- if (!await import_fs_extra4.default.pathExists(filePath)) {
43203
+ if (!await import_fs_extra5.default.pathExists(filePath)) {
42990
43204
  console.error(source_default.red(`File not found: ${filePath}`));
42991
43205
  process.exit(1);
42992
43206
  }
42993
- const content = await import_fs_extra4.default.readFile(filePath, "utf-8");
42994
- const filename = path6.basename(filePath);
43207
+ const content = await import_fs_extra5.default.readFile(filePath, "utf-8");
43208
+ const filename = path7.basename(filePath);
42995
43209
  const auditType = detectAuditType(filename);
42996
43210
  const project2 = getProjectName();
42997
43211
  const branch = getGitBranch();
@@ -43116,11 +43330,11 @@ async function auditViewCommand(id, options) {
43116
43330
  try {
43117
43331
  const audit2 = await client.audits.get(id);
43118
43332
  if (options.output) {
43119
- const outputPath = path6.resolve(options.output);
43120
- await import_fs_extra4.default.ensureDir(path6.dirname(outputPath));
43121
- await import_fs_extra4.default.writeFile(outputPath, audit2.content, "utf-8");
43333
+ const outputPath = path7.resolve(options.output);
43334
+ await import_fs_extra5.default.ensureDir(path7.dirname(outputPath));
43335
+ await import_fs_extra5.default.writeFile(outputPath, audit2.content, "utf-8");
43122
43336
  console.log(source_default.green(`
43123
- \u2713 Audit saved to ${path6.relative(process.cwd(), outputPath)}
43337
+ \u2713 Audit saved to ${path7.relative(process.cwd(), outputPath)}
43124
43338
  `));
43125
43339
  } else {
43126
43340
  const typeLabels = {
@@ -43152,8 +43366,8 @@ async function auditViewCommand(id, options) {
43152
43366
 
43153
43367
  // src/commands/plan.ts
43154
43368
  init_source();
43155
- var import_fs_extra5 = __toESM(require_lib(), 1);
43156
- import path7 from "path";
43369
+ var import_fs_extra6 = __toESM(require_lib(), 1);
43370
+ import path8 from "path";
43157
43371
  function extractFrontmatterStatus(content) {
43158
43372
  const match = content.match(/^---\n([\s\S]*?)\n---/);
43159
43373
  if (!match) return null;
@@ -43162,7 +43376,7 @@ function extractFrontmatterStatus(content) {
43162
43376
  }
43163
43377
  async function autoDetectPlanFile() {
43164
43378
  const cwd = process.cwd();
43165
- const plansDir = path7.join(cwd, ".plans");
43379
+ const plansDir = path8.join(cwd, ".plans");
43166
43380
  const mdFiles = await findMdFiles(plansDir);
43167
43381
  if (mdFiles.length === 0) {
43168
43382
  return null;
@@ -43171,7 +43385,7 @@ async function autoDetectPlanFile() {
43171
43385
  const ticketId = extractTicketId(branch);
43172
43386
  if (ticketId) {
43173
43387
  const ticketLower = ticketId.toLowerCase();
43174
- const match = mdFiles.find((f) => path7.basename(f).toLowerCase().includes(ticketLower));
43388
+ const match = mdFiles.find((f) => path8.basename(f).toLowerCase().includes(ticketLower));
43175
43389
  if (match) {
43176
43390
  return match;
43177
43391
  }
@@ -43182,7 +43396,7 @@ async function planSubmitCommand(file2) {
43182
43396
  const client = await getClient();
43183
43397
  let filePath;
43184
43398
  if (file2) {
43185
- filePath = path7.resolve(file2);
43399
+ filePath = path8.resolve(file2);
43186
43400
  } else {
43187
43401
  const detected = await autoDetectPlanFile();
43188
43402
  if (!detected) {
@@ -43194,14 +43408,14 @@ async function planSubmitCommand(file2) {
43194
43408
  process.exit(1);
43195
43409
  }
43196
43410
  filePath = detected;
43197
- console.log(source_default.dim(`Auto-detected plan file: ${path7.relative(process.cwd(), filePath)}`));
43411
+ console.log(source_default.dim(`Auto-detected plan file: ${path8.relative(process.cwd(), filePath)}`));
43198
43412
  }
43199
- if (!await import_fs_extra5.default.pathExists(filePath)) {
43413
+ if (!await import_fs_extra6.default.pathExists(filePath)) {
43200
43414
  console.error(source_default.red(`File not found: ${filePath}`));
43201
43415
  process.exit(1);
43202
43416
  }
43203
- const content = await import_fs_extra5.default.readFile(filePath, "utf-8");
43204
- const filename = path7.basename(filePath);
43417
+ const content = await import_fs_extra6.default.readFile(filePath, "utf-8");
43418
+ const filename = path8.basename(filePath);
43205
43419
  const project2 = getProjectName();
43206
43420
  const branch = getGitBranch();
43207
43421
  const commit = getGitCommit();
@@ -43288,84 +43502,9 @@ async function planListCommand(options) {
43288
43502
  }
43289
43503
  }
43290
43504
 
43291
- // src/commands/skills.ts
43292
- init_source();
43293
- var import_fs_extra6 = __toESM(require_lib(), 1);
43294
- import os5 from "os";
43295
- import path8 from "path";
43296
- var MARKER_FILE = ".melt-skills.json";
43297
- function readMarker(skillsRoot) {
43298
- const markerPath = path8.join(skillsRoot, MARKER_FILE);
43299
- if (!import_fs_extra6.default.existsSync(markerPath)) return null;
43300
- try {
43301
- return import_fs_extra6.default.readJsonSync(markerPath);
43302
- } catch {
43303
- return null;
43304
- }
43305
- }
43306
- async function skillsInstallCommand(opts) {
43307
- const client = await getClient();
43308
- let manifest;
43309
- try {
43310
- manifest = await client.skills.getDevSkills();
43311
- } catch (error48) {
43312
- console.error(
43313
- source_default.red(
43314
- `Failed to fetch Melt dev skills: ${error48 instanceof Error ? error48.message : "Unknown error"}`
43315
- )
43316
- );
43317
- process.exit(1);
43318
- }
43319
- const skillsRoot = path8.join(os5.homedir(), ".claude", "skills");
43320
- const existing = readMarker(skillsRoot);
43321
- if (existing && existing.version === manifest.version && !opts.force) {
43322
- console.log(
43323
- source_default.dim(
43324
- `
43325
- Melt dev skills already up to date (v${manifest.version}). Use --force to reinstall.
43326
- `
43327
- )
43328
- );
43329
- return;
43330
- }
43331
- for (const skill of manifest.skills) {
43332
- const dir = path8.join(skillsRoot, skill.name);
43333
- import_fs_extra6.default.removeSync(dir);
43334
- for (const [relPath, content] of Object.entries(skill.files)) {
43335
- import_fs_extra6.default.outputFileSync(path8.join(dir, relPath), content);
43336
- }
43337
- }
43338
- const marker = {
43339
- version: manifest.version,
43340
- installedAt: (/* @__PURE__ */ new Date()).toISOString(),
43341
- skills: manifest.skills.map((s) => s.name)
43342
- };
43343
- import_fs_extra6.default.outputFileSync(path8.join(skillsRoot, MARKER_FILE), `${JSON.stringify(marker, null, 2)}
43344
- `);
43345
- console.log(
43346
- source_default.green(
43347
- `
43348
- \u2713 Installed ${manifest.skills.length} Melt dev skills (v${manifest.version}) to ~/.claude/skills/.`
43349
- )
43350
- );
43351
- console.log(
43352
- source_default.dim(
43353
- " Restart Claude Code to load them (they appear as /melt-dev-plan, /melt-dev-review, \u2026).\n"
43354
- )
43355
- );
43356
- }
43357
-
43358
43505
  // src/index.ts
43359
43506
  init_update();
43360
43507
 
43361
- // src/utils/debug.ts
43362
- init_source();
43363
- function debugLog(message) {
43364
- if (process.env["MELTCTL_DEBUG"]) {
43365
- console.error(source_default.dim(`[debug] ${message}`));
43366
- }
43367
- }
43368
-
43369
43508
  // src/utils/analytics.ts
43370
43509
  init_version();
43371
43510
  async function trackCommand(command, success2, errorMessage) {
@@ -55777,6 +55916,7 @@ ${source_default.dim(" event track --skill Track skill usage (called by skil
55777
55916
  cmd = cmd.parent;
55778
55917
  }
55779
55918
  await checkAndEnforceUpdate(cmd.name());
55919
+ await maybeSyncSkills(cmd.name());
55780
55920
  }).hook("postAction", async (_thisCommand, actionCommand) => {
55781
55921
  try {
55782
55922
  const parts = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meltstudio/meltctl",
3
- "version": "5.8.0",
3
+ "version": "5.9.0",
4
4
  "description": "AI-first development tooling for Melt teams — companion CLI for the org-distributed Claude Code dev skills",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",