happyskills 0.3.0 → 0.3.1

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,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.1] - 2026-03-04
11
+
12
+ ### Fixed
13
+ - Fix global installs writing `skills-lock.json` to the local project directory instead of `~/.claude/`; global lock state is now read/written from `~/.claude/skills-lock.json` across `install`, `uninstall`, `list`, `update`, and `convert` commands
14
+
10
15
  ## [0.3.0] - 2026-03-04
11
16
 
12
17
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "happyskills",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
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)",
@@ -12,7 +12,7 @@ const { write_manifest } = require('../manifest/writer')
12
12
  const { read_lock } = require('../lock/reader')
13
13
  const { write_lock, update_lock_skills } = require('../lock/writer')
14
14
  const { hash_directory } = require('../lock/integrity')
15
- const { skills_dir, find_project_root } = require('../config/paths')
15
+ const { skills_dir, find_project_root, lock_root } = require('../config/paths')
16
16
  const { file_exists } = require('../utils/fs')
17
17
  const { create_spinner } = require('../ui/spinner')
18
18
  const { print_help, print_success, print_error, print_info, print_label, print_json } = require('../ui/output')
@@ -154,7 +154,8 @@ const run = (args) => catch_errors('Convert failed', async () => {
154
154
  if (push_err) { pub_spinner.fail('Publish failed'); throw e('Push failed', push_err) }
155
155
 
156
156
  pub_spinner.update('Updating lock file...')
157
- const [, lock_data] = await read_lock(project_root)
157
+ const lock_dir = lock_root(is_global, project_root)
158
+ const [, lock_data] = await read_lock(lock_dir)
158
159
  const [, integrity] = await hash_directory(skill_dir)
159
160
 
160
161
  const full_name = `${workspace.slug}/${skill_name}`
@@ -170,7 +171,7 @@ const run = (args) => catch_errors('Convert failed', async () => {
170
171
  }
171
172
 
172
173
  const new_skills = update_lock_skills(lock_data, updates)
173
- const [lock_err] = await write_lock(project_root, new_skills)
174
+ const [lock_err] = await write_lock(lock_dir, new_skills)
174
175
  if (lock_err) { pub_spinner.fail('Failed to write lock file'); throw e('Lock write failed', lock_err) }
175
176
 
176
177
  pub_spinner.succeed(`Converted ${full_name}@${version}`)
@@ -1,6 +1,6 @@
1
1
  const { error: { catch_errors, wrap_errors: e } } = require('puffy-core')
2
2
  const { read_lock, get_all_locked_skills } = require('../lock/reader')
3
- const { skills_dir, skill_install_dir, find_project_root } = require('../config/paths')
3
+ const { skills_dir, skill_install_dir, find_project_root, lock_root } = require('../config/paths')
4
4
  const { file_exists } = require('../utils/fs')
5
5
  const { scan_skills_dir } = require('../utils/skill_scanner')
6
6
  const { print_help, print_table, print_json, print_info } = require('../ui/output')
@@ -32,7 +32,7 @@ const run = (args) => catch_errors('List failed', async () => {
32
32
  const project_root = find_project_root()
33
33
  const base_dir = skills_dir(is_global, project_root)
34
34
 
35
- const [, lock_data] = await read_lock(project_root)
35
+ const [, lock_data] = await read_lock(lock_root(is_global, project_root))
36
36
  const skills = get_all_locked_skills(lock_data)
37
37
  const managed_entries = Object.entries(skills)
38
38
 
@@ -3,7 +3,7 @@ const { install } = require('../engine/installer')
3
3
  const { read_lock, get_all_locked_skills } = require('../lock/reader')
4
4
  const { print_help, print_success, print_info, print_json } = require('../ui/output')
5
5
  const { exit_with_error, UsageError } = require('../utils/errors')
6
- const { find_project_root } = require('../config/paths')
6
+ const { find_project_root, lock_root } = require('../config/paths')
7
7
  const { EXIT_CODES } = require('../constants')
8
8
 
9
9
  const HELP_TEXT = `Usage: happyskills update [owner/skill|--all] [options]
@@ -40,7 +40,8 @@ const run = (args) => catch_errors('Update failed', async () => {
40
40
  throw new UsageError("Specify a skill to update or use --all (e.g., 'happyskills update acme/deploy-aws' or 'happyskills update --all').")
41
41
  }
42
42
 
43
- const [, lock_data] = await read_lock(project_root)
43
+ const is_global = args.flags.global || false
44
+ const [, lock_data] = await read_lock(lock_root(is_global, project_root))
44
45
  const skills = get_all_locked_skills(lock_data)
45
46
 
46
47
  const to_update = target_skill
@@ -57,7 +58,7 @@ const run = (args) => catch_errors('Update failed', async () => {
57
58
  }
58
59
 
59
60
  const options = {
60
- global: args.flags.global || false,
61
+ global: is_global,
61
62
  fresh: true,
62
63
  project_root
63
64
  }
@@ -26,6 +26,10 @@ const tmp_dir = (base_skills_dir) => path.join(base_skills_dir, '.tmp')
26
26
 
27
27
  const install_lock_path = (base_skills_dir) => path.join(base_skills_dir, '.install.lock')
28
28
 
29
+ const lock_root = (is_global, project_root) => {
30
+ return is_global ? path.join(home_dir, '.claude') : project_root
31
+ }
32
+
29
33
  const lock_file_path = (project_root = process.cwd()) => path.join(project_root, 'skills-lock.json')
30
34
 
31
35
  const skill_install_dir = (base_skills_dir, name) => path.join(base_skills_dir, name)
@@ -60,6 +64,7 @@ module.exports = {
60
64
  skills_dir,
61
65
  tmp_dir,
62
66
  install_lock_path,
67
+ lock_root,
63
68
  lock_file_path,
64
69
  skill_install_dir,
65
70
  find_project_root
@@ -8,7 +8,7 @@ const { check_system_dependencies } = require('./system_deps')
8
8
  const { hash_directory, verify_integrity } = require('../lock/integrity')
9
9
  const { read_lock, get_locked_skill } = require('../lock/reader')
10
10
  const { write_lock, update_lock_skills } = require('../lock/writer')
11
- const { skills_dir, tmp_dir, skill_install_dir } = require('../config/paths')
11
+ const { skills_dir, tmp_dir, skill_install_dir, lock_root } = require('../config/paths')
12
12
  const { ensure_dir, remove_dir, file_exists } = require('../utils/fs')
13
13
  const { create_spinner } = require('../ui/spinner')
14
14
  const { print_success, print_warn } = require('../ui/output')
@@ -22,8 +22,9 @@ const install = (skill, options = {}) => catch_errors('Install failed', async ()
22
22
  const { version, global: is_global = false, force = false, fresh = false, project_root } = options
23
23
  const base_dir = skills_dir(is_global, project_root)
24
24
  const temp_dir = tmp_dir(base_dir)
25
+ const lock_dir = lock_root(is_global, project_root)
25
26
 
26
- const [, lock_data] = fresh ? [null, null] : await read_lock(project_root)
27
+ const [, lock_data] = fresh ? [null, null] : await read_lock(lock_dir)
27
28
 
28
29
  if (!fresh && lock_data) {
29
30
  const locked = get_locked_skill(lock_data, skill)
@@ -148,7 +149,7 @@ const install = (skill, options = {}) => catch_errors('Install failed', async ()
148
149
  }
149
150
 
150
151
  const new_skills = update_lock_skills(lock_data, updates)
151
- const [lock_errors] = await write_lock(project_root, new_skills)
152
+ const [lock_errors] = await write_lock(lock_dir, new_skills)
152
153
  if (lock_errors) { spinner.fail('Failed to write lock file'); throw e('Lock write failed', lock_errors) }
153
154
 
154
155
  spinner.succeed(`Installed ${packages_to_install.length} package(s)`)
@@ -1,7 +1,7 @@
1
1
  const { error: { catch_errors, wrap_errors: e } } = require('puffy-core')
2
2
  const { read_lock, get_locked_skill, get_all_locked_skills } = require('../lock/reader')
3
3
  const { write_lock, update_lock_skills } = require('../lock/writer')
4
- const { skills_dir, skill_install_dir } = require('../config/paths')
4
+ const { skills_dir, skill_install_dir, lock_root } = require('../config/paths')
5
5
  const { remove_dir } = require('../utils/fs')
6
6
  const { print_success, print_info } = require('../ui/output')
7
7
 
@@ -24,8 +24,9 @@ const find_orphans = (skills, removed_skill) => {
24
24
  const uninstall = (skill, options = {}) => catch_errors('Uninstall failed', async () => {
25
25
  const { global: is_global = false, project_root } = options
26
26
  const base_dir = skills_dir(is_global, project_root)
27
+ const lock_dir = lock_root(is_global, project_root)
27
28
 
28
- const [lock_errors, lock_data] = await read_lock(project_root)
29
+ const [lock_errors, lock_data] = await read_lock(lock_dir)
29
30
  if (lock_errors || !lock_data) {
30
31
  throw new Error('No skills-lock.json found. Nothing to uninstall.')
31
32
  }
@@ -58,7 +59,7 @@ const uninstall = (skill, options = {}) => catch_errors('Uninstall failed', asyn
58
59
  }
59
60
 
60
61
  const new_skills = update_lock_skills(lock_data, updates)
61
- const [write_errors] = await write_lock(project_root, new_skills)
62
+ const [write_errors] = await write_lock(lock_dir, new_skills)
62
63
  if (write_errors) throw e('Failed to update lock file', write_errors)
63
64
 
64
65
  print_success(`Removed ${skill}`)