Skip to content

Commit c19b9c3

Browse files
committed
fix: clean up bundle references when prompt hash changes
rm did not remove deleted prompt hashes from bundles/index.json, leaving dangling references that caused "Failed to copy" errors on bundle import. set -f did not update the meta_prompt field in bundles when replacing prompt content, leaving bundles pointing to a hash that no longer exists. Remove dead bundle-rewrite code from renameGroupFromRef — group renames do not affect bundles since bundles store hashes not groups.
1 parent bcae3a6 commit c19b9c3

File tree

3 files changed

+79
-62
lines changed

3 files changed

+79
-62
lines changed

build.zig.zon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
.{
22
.fingerprint = 0xa21731978d142f78,
33
.name = .clumsies,
4-
.version = "0.16.1",
4+
.version = "0.16.2",
55
.paths = .{
66
"build.zig",
77
"build.zig.zon",

src/commands/rm_cmd.zig

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,82 @@ fn rmPrompts(stdout: *std.io.Writer, stderr: *std.io.Writer, allocator: std.mem.
144144
return;
145145
};
146146

147+
// Clean up bundle references to removed prompts
148+
const bundles_index_path = try std.fs.path.join(allocator, &.{ registry_path, "bundles/index.json" });
149+
defer allocator.free(bundles_index_path);
150+
151+
if (fs.openFileAbsolute(bundles_index_path, .{})) |bfile| {
152+
const bcontent = bfile.readToEndAlloc(allocator, MAX_FILE_SIZE) catch {
153+
bfile.close();
154+
return;
155+
};
156+
bfile.close();
157+
defer allocator.free(bcontent);
158+
159+
if (std.json.parseFromSlice(std.json.Value, allocator, bcontent, .{})) |bparsed| {
160+
defer bparsed.deinit();
161+
162+
if (bparsed.value.object.get("bundles")) |bundles| {
163+
var new_bidx: std.ArrayListUnmanaged(u8) = .{};
164+
defer new_bidx.deinit(allocator);
165+
try new_bidx.appendSlice(allocator, "{\n \"bundles\": [");
166+
167+
var bfirst = true;
168+
for (bundles.array.items) |bitem| {
169+
const bname = if (bitem.object.get("name")) |n| n.string else continue;
170+
const btask = if (bitem.object.get("task")) |t| t.string else "-";
171+
const bdesc = if (bitem.object.get("description")) |d| d.string else "-";
172+
const bcreated = if (bitem.object.get("created_at")) |c| c.string else "0";
173+
var bmeta = if (bitem.object.get("meta_prompt")) |m| m.string else "";
174+
175+
for (removed_hashes.items) |rh| {
176+
if (std.mem.eql(u8, bmeta, rh)) {
177+
bmeta = "";
178+
break;
179+
}
180+
}
181+
182+
if (!bfirst) try new_bidx.appendSlice(allocator, ",");
183+
bfirst = false;
184+
185+
const bentry_start = try std.fmt.allocPrint(allocator, "\n {{\n \"name\": \"{s}\",\n \"task\": \"{s}\",\n \"description\": \"{s}\",\n \"created_at\": \"{s}\",\n \"meta_prompt\": \"{s}\",\n \"prompts\": [", .{ bname, btask, bdesc, bcreated, bmeta });
186+
defer allocator.free(bentry_start);
187+
try new_bidx.appendSlice(allocator, bentry_start);
188+
189+
if (bitem.object.get("prompts")) |bprompts| {
190+
var pfirst = true;
191+
for (bprompts.array.items) |ref| {
192+
const ref_hash = if (ref.object.get("hash")) |h| h.string else continue;
193+
194+
var skip = false;
195+
for (removed_hashes.items) |rh| {
196+
if (std.mem.eql(u8, ref_hash, rh)) {
197+
skip = true;
198+
break;
199+
}
200+
}
201+
if (skip) continue;
202+
203+
const ref_entry = try std.fmt.allocPrint(allocator, "{s}\n {{ \"hash\": \"{s}\" }}", .{
204+
if (pfirst) "" else ",",
205+
ref_hash,
206+
});
207+
defer allocator.free(ref_entry);
208+
try new_bidx.appendSlice(allocator, ref_entry);
209+
pfirst = false;
210+
}
211+
}
212+
try new_bidx.appendSlice(allocator, "]\n }");
213+
}
214+
try new_bidx.appendSlice(allocator, "\n ]\n}\n");
215+
216+
const bidx_out = fs.createFileAbsolute(bundles_index_path, .{}) catch return;
217+
defer bidx_out.close();
218+
bidx_out.writeAll(new_bidx.items) catch return;
219+
}
220+
} else |_| {}
221+
} else |_| {}
222+
147223
// Commit and push
148224
var sp = spinner.init(stdout, "Removing from registry");
149225
sp.start();

src/commands/set_cmd.zig

Lines changed: 2 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,8 @@ fn replacePrompt(stdout: *std.io.Writer, stderr: *std.io.Writer, allocator: std.
424424
const btask = if (bitem.object.get("task")) |t| t.string else "-";
425425
const bdesc = if (bitem.object.get("description")) |d| d.string else "-";
426426
const bcreated = if (bitem.object.get("created_at")) |c| c.string else "0";
427-
const bmeta = if (bitem.object.get("meta_prompt")) |m| m.string else "";
427+
const raw_meta = if (bitem.object.get("meta_prompt")) |m| m.string else "";
428+
const bmeta = if (std.mem.eql(u8, raw_meta, old_full_hash.?)) @as([]const u8, &new_hash_hex) else raw_meta;
428429

429430
if (!bfirst) try new_bidx.appendSlice(allocator, ",");
430431
bfirst = false;
@@ -591,66 +592,6 @@ fn renameGroupFromRef(stdout: *std.io.Writer, stderr: *std.io.Writer, allocator:
591592
return;
592593
};
593594

594-
// Update bundles/index.json
595-
const bundles_index_path = try std.fs.path.join(allocator, &.{ registry_path, "bundles/index.json" });
596-
defer allocator.free(bundles_index_path);
597-
598-
if (fs.openFileAbsolute(bundles_index_path, .{})) |bfile| {
599-
const bcontent = bfile.readToEndAlloc(allocator, MAX_FILE_SIZE) catch {
600-
bfile.close();
601-
return;
602-
};
603-
bfile.close();
604-
defer allocator.free(bcontent);
605-
606-
if (std.json.parseFromSlice(std.json.Value, allocator, bcontent, .{})) |bparsed| {
607-
defer bparsed.deinit();
608-
609-
if (bparsed.value.object.get("bundles")) |bundles| {
610-
var new_bidx: std.ArrayListUnmanaged(u8) = .{};
611-
defer new_bidx.deinit(allocator);
612-
try new_bidx.appendSlice(allocator, "{\n \"bundles\": [");
613-
614-
var bfirst = true;
615-
for (bundles.array.items) |bitem| {
616-
const bname = if (bitem.object.get("name")) |n| n.string else continue;
617-
const btask = if (bitem.object.get("task")) |t| t.string else "-";
618-
const bdesc = if (bitem.object.get("description")) |d| d.string else "-";
619-
const bcreated = if (bitem.object.get("created_at")) |c| c.string else "0";
620-
const bmeta = if (bitem.object.get("meta_prompt")) |m| m.string else "";
621-
622-
if (!bfirst) try new_bidx.appendSlice(allocator, ",");
623-
bfirst = false;
624-
625-
const bentry_start = try std.fmt.allocPrint(allocator, "\n {{\n \"name\": \"{s}\",\n \"task\": \"{s}\",\n \"description\": \"{s}\",\n \"created_at\": \"{s}\",\n \"meta_prompt\": \"{s}\",\n \"prompts\": [", .{ bname, btask, bdesc, bcreated, bmeta });
626-
defer allocator.free(bentry_start);
627-
try new_bidx.appendSlice(allocator, bentry_start);
628-
629-
if (bitem.object.get("prompts")) |bprompts| {
630-
for (bprompts.array.items, 0..) |ref, ridx| {
631-
const ref_hash = if (ref.object.get("hash")) |h| h.string else continue;
632-
const ref_entry = try std.fmt.allocPrint(allocator, "{s}\n {{ \"hash\": \"{s}\" }}", .{
633-
if (ridx > 0) "," else "",
634-
ref_hash,
635-
});
636-
defer allocator.free(ref_entry);
637-
try new_bidx.appendSlice(allocator, ref_entry);
638-
}
639-
}
640-
try new_bidx.appendSlice(allocator, "]\n }");
641-
}
642-
try new_bidx.appendSlice(allocator, "\n ]\n}\n");
643-
644-
const bidx_out = fs.createFileAbsolute(bundles_index_path, .{}) catch return;
645-
defer bidx_out.close();
646-
bidx_out.writeAll(new_bidx.items) catch {
647-
try stderr.print("{s}{s}{s}Error:{s} Failed to write bundles index\n", .{ P, Color.bold, Color.red, Color.reset });
648-
return;
649-
};
650-
}
651-
} else |_| {}
652-
} else |_| {}
653-
654595
// Commit and push
655596
var sp = spinner.init(stdout, "Renaming group");
656597
sp.start();

0 commit comments

Comments
 (0)