happyskills 0.18.0 → 0.18.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 +5 -0
- package/package.json +1 -1
- package/src/commands/diff.js +12 -6
- package/src/commands/pull.js +10 -9
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.18.1] - 2026-03-29
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Fix `pull` and `diff` recording stale commit SHA from CloudFront-cached clone responses — now clones at the specific head commit from the compare endpoint, ensuring the lock file matches the remote after pull
|
|
14
|
+
|
|
10
15
|
## [0.18.0] - 2026-03-29
|
|
11
16
|
|
|
12
17
|
### Added
|
package/package.json
CHANGED
package/src/commands/diff.js
CHANGED
|
@@ -136,15 +136,18 @@ const run = (args) => catch_errors('Diff failed', async () => {
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
if (mode === 'remote') {
|
|
139
|
-
// Base vs remote —
|
|
140
|
-
const [
|
|
139
|
+
// Base vs remote — get head commit via compare, clone at that commit
|
|
140
|
+
const [cmp_err, cmp_data] = await repos_api.compare(owner, repo, lock_entry.base_commit)
|
|
141
|
+
if (cmp_err) throw e('Compare failed', cmp_err)
|
|
142
|
+
|
|
143
|
+
const [remote_err, remote_clone] = await repos_api.clone(owner, repo, null, { commit: cmp_data.head_commit })
|
|
141
144
|
if (remote_err) throw e('Failed to fetch remote files', remote_err)
|
|
142
145
|
const remote_files = (remote_clone.files || []).map(f => ({ path: f.path, sha: f.sha }))
|
|
143
146
|
|
|
144
147
|
const classified = classify_changes(base_files, base_files, remote_files)
|
|
145
148
|
|
|
146
149
|
if (args.flags.json) {
|
|
147
|
-
const report = build_report(skill_name, lock_entry.version,
|
|
150
|
+
const report = build_report(skill_name, lock_entry.version, cmp_data.head_version, classified)
|
|
148
151
|
print_json({ data: { mode, report } })
|
|
149
152
|
} else {
|
|
150
153
|
print_file_table(classified)
|
|
@@ -152,16 +155,19 @@ const run = (args) => catch_errors('Diff failed', async () => {
|
|
|
152
155
|
return
|
|
153
156
|
}
|
|
154
157
|
|
|
155
|
-
// Full three-way diff
|
|
158
|
+
// Full three-way diff — get head commit via compare
|
|
159
|
+
const [cmp_err, cmp_data] = await repos_api.compare(owner, repo, lock_entry.base_commit)
|
|
160
|
+
if (cmp_err) throw e('Compare failed', cmp_err)
|
|
161
|
+
|
|
156
162
|
const [local_err, local_files] = await build_local_entries(skill_dir)
|
|
157
163
|
if (local_err) throw e('Failed to read local files', local_err)
|
|
158
164
|
|
|
159
|
-
const [remote_err, remote_clone] = await repos_api.clone(owner, repo, null)
|
|
165
|
+
const [remote_err, remote_clone] = await repos_api.clone(owner, repo, null, { commit: cmp_data.head_commit })
|
|
160
166
|
if (remote_err) throw e('Failed to fetch remote files', remote_err)
|
|
161
167
|
const remote_files = (remote_clone.files || []).map(f => ({ path: f.path, sha: f.sha }))
|
|
162
168
|
|
|
163
169
|
const classified = classify_changes(base_files, local_files, remote_files)
|
|
164
|
-
const report = build_report(skill_name, lock_entry.version,
|
|
170
|
+
const report = build_report(skill_name, lock_entry.version, cmp_data.head_version, classified)
|
|
165
171
|
|
|
166
172
|
if (args.flags.json) {
|
|
167
173
|
print_json({ data: { mode, report } })
|
package/src/commands/pull.js
CHANGED
|
@@ -142,7 +142,8 @@ const run = (args) => catch_errors('Pull failed', async () => {
|
|
|
142
142
|
// 5. No local mods → fast-forward
|
|
143
143
|
if (!det.local_modified || strategy === 'force') {
|
|
144
144
|
spinner.update('Fast-forwarding...')
|
|
145
|
-
|
|
145
|
+
// Clone at the specific head commit from compare (bypasses CDN cache)
|
|
146
|
+
const [clone_err, clone_data] = await repos_api.clone(owner, repo, null, { commit: cmp_data.head_commit })
|
|
146
147
|
if (clone_err) { spinner.fail('Clone failed'); throw clone_err[0] }
|
|
147
148
|
|
|
148
149
|
// Remove existing files and write fresh
|
|
@@ -159,12 +160,12 @@ const run = (args) => catch_errors('Pull failed', async () => {
|
|
|
159
160
|
const [wf_err] = await write_files_from_clone(clone_data, skill_dir)
|
|
160
161
|
if (wf_err) { spinner.fail('Failed to write files'); throw wf_err[0] }
|
|
161
162
|
|
|
162
|
-
// Update lock
|
|
163
|
+
// Update lock — use head_commit from compare (authoritative, not cached)
|
|
163
164
|
const [hash_err, new_integrity] = await hash_directory(skill_dir)
|
|
164
165
|
const updated_entry = {
|
|
165
166
|
...lock_entry,
|
|
166
|
-
commit:
|
|
167
|
-
base_commit:
|
|
167
|
+
commit: cmp_data.head_commit,
|
|
168
|
+
base_commit: cmp_data.head_commit,
|
|
168
169
|
integrity: new_integrity || lock_entry.integrity,
|
|
169
170
|
base_integrity: new_integrity || lock_entry.base_integrity,
|
|
170
171
|
version: cmp_data.head_version || lock_entry.version,
|
|
@@ -196,8 +197,8 @@ const run = (args) => catch_errors('Pull failed', async () => {
|
|
|
196
197
|
const [local_err, local_files] = await build_local_entries(skill_dir)
|
|
197
198
|
if (local_err) { spinner.fail('Failed to read local files'); throw local_err[0] }
|
|
198
199
|
|
|
199
|
-
// Get remote files (clone
|
|
200
|
-
const [remote_err, remote_clone] = await repos_api.clone(owner, repo, null)
|
|
200
|
+
// Get remote files (clone at head commit — bypasses CDN cache)
|
|
201
|
+
const [remote_err, remote_clone] = await repos_api.clone(owner, repo, null, { commit: cmp_data.head_commit })
|
|
201
202
|
if (remote_err) { spinner.fail('Failed to fetch remote files'); throw remote_err[0] }
|
|
202
203
|
const remote_files = (remote_clone.files || []).map(f => ({ path: f.path, sha: f.sha }))
|
|
203
204
|
|
|
@@ -262,12 +263,12 @@ const run = (args) => catch_errors('Pull failed', async () => {
|
|
|
262
263
|
}
|
|
263
264
|
// strategy === 'ours' → keep local files as-is (no action needed)
|
|
264
265
|
|
|
265
|
-
// Update lock
|
|
266
|
+
// Update lock — use head_commit from compare (authoritative, not cached)
|
|
266
267
|
const [hash_err, new_integrity] = await hash_directory(skill_dir)
|
|
267
268
|
const updated_entry = {
|
|
268
269
|
...lock_entry,
|
|
269
|
-
commit:
|
|
270
|
-
base_commit:
|
|
270
|
+
commit: cmp_data.head_commit,
|
|
271
|
+
base_commit: cmp_data.head_commit,
|
|
271
272
|
integrity: new_integrity || lock_entry.integrity,
|
|
272
273
|
base_integrity: new_integrity || lock_entry.base_integrity,
|
|
273
274
|
version: cmp_data.head_version || lock_entry.version,
|