happyskills 0.27.1 → 0.27.2

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/CHANGELOG.md CHANGED
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.27.2] - 2026-04-02
11
+
12
+ ### Fixed
13
+ - Fix `publish` not syncing dependency declarations to the lock file when publishing an existing skill — the lock entry's `dependencies` field was stale after publish
14
+ - Fix `refresh` not detecting dependency drift — skills whose lock `dependencies` diverged from the installed `skill.json` were incorrectly reported as up-to-date because the staleness check was purely commit-based
15
+
10
16
  ## [0.27.1] - 2026-04-02
11
17
 
12
18
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "happyskills",
3
- "version": "0.27.1",
3
+ "version": "0.27.2",
4
4
  "description": "Package manager for AI agent skills",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "Nicolas Dao <nic@cloudlesslabs.com> (https://cloudlesslabs.com)",
@@ -229,7 +229,8 @@ const run = (args) => catch_errors('Publish failed', async () => {
229
229
  ref: push_data?.ref || `refs/tags/v${manifest.version}`,
230
230
  commit: push_data?.commit || null,
231
231
  base_commit: push_data?.commit || null,
232
- base_integrity: (!hash_err && integrity) ? integrity : null
232
+ base_integrity: (!hash_err && integrity) ? integrity : null,
233
+ dependencies: manifest.dependencies || {}
233
234
  }
234
235
  if (!hash_err && integrity) updated_entry.integrity = integrity
235
236
  delete updated_entry.merge_parents
@@ -1,6 +1,7 @@
1
1
  const { error: { catch_errors, wrap_errors: e } } = require('puffy-core')
2
2
  const { install } = require('../engine/installer')
3
3
  const { read_lock, get_all_locked_skills } = require('../lock/reader')
4
+ const { read_manifest } = require('../manifest/reader')
4
5
  const { detect_status } = require('../merge/detector')
5
6
  const repos_api = require('../api/repos')
6
7
  const { print_help, print_table, print_json, print_info, print_success, print_warn, print_hint, code } = require('../ui/output')
@@ -26,6 +27,16 @@ Examples:
26
27
  happyskills refresh -y
27
28
  happyskills refresh -g -y --json`
28
29
 
30
+ /**
31
+ * Returns true if the lock entry's dependencies differ from the installed skill.json's dependencies.
32
+ */
33
+ const has_dependency_drift = (lock_entry, manifest) => {
34
+ if (!manifest) return false
35
+ const lock_deps = JSON.stringify(lock_entry.dependencies || {})
36
+ const manifest_deps = JSON.stringify(manifest.dependencies || {})
37
+ return lock_deps !== manifest_deps
38
+ }
39
+
29
40
  const confirm_prompt = (question) => new Promise((resolve) => {
30
41
  const readline = require('readline')
31
42
  const rl = readline.createInterface({ input: process.stdin, output: process.stderr })
@@ -72,6 +83,15 @@ const run = (args) => catch_errors('Refresh failed', async () => {
72
83
  }
73
84
  } else {
74
85
  spinner?.succeed('Checked for updates')
86
+ const base_dir = skills_dir(is_global, project_root)
87
+ // Read all installed manifests in parallel to check for dependency drift
88
+ const manifest_map = {}
89
+ await Promise.all(to_check.map(async ([name]) => {
90
+ const short_name = name.split('/')[1] || name
91
+ const dir = skill_install_dir(base_dir, short_name)
92
+ const [, manifest] = await read_manifest(dir)
93
+ if (manifest) manifest_map[name] = manifest
94
+ }))
75
95
  for (const [name, data] of to_check) {
76
96
  const info = batch_data?.results?.[name]
77
97
  if (info?.access_denied) {
@@ -83,6 +103,9 @@ const run = (args) => catch_errors('Refresh failed', async () => {
83
103
  } else if (!data.base_commit && info.latest_version !== data.version) {
84
104
  // Fallback to version comparison for old lock files without base_commit
85
105
  results.push({ skill: name, installed: data.version, latest: info.latest_version, status: 'outdated' })
106
+ } else if (has_dependency_drift(data, manifest_map[name])) {
107
+ // Lock dependencies are stale compared to installed skill.json
108
+ results.push({ skill: name, installed: data.version, latest: info.latest_version, status: 'outdated' })
86
109
  } else {
87
110
  results.push({ skill: name, installed: data.version, latest: info.latest_version, status: 'up-to-date' })
88
111
  }