@zalom/plastic 1.0.0-alpha.1 → 1.0.0-alpha.3
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/package.json +1 -1
- package/scripts/install.rb +84 -28
package/package.json
CHANGED
package/scripts/install.rb
CHANGED
|
@@ -141,13 +141,18 @@ def distribute(mode)
|
|
|
141
141
|
puts " \u{1f4e6} #{mode == :update ? "Updating" : "Installing"} core files to #{PLASTIC_HOME}"
|
|
142
142
|
|
|
143
143
|
FileUtils.mkdir_p(PLASTIC_HOME)
|
|
144
|
-
FileUtils.mkdir_p(File.join(PLASTIC_HOME, "scripts"))
|
|
144
|
+
FileUtils.mkdir_p(File.join(PLASTIC_HOME, "scripts", "lib"))
|
|
145
145
|
|
|
146
146
|
core_files = {
|
|
147
147
|
"PLASTIC.md" => "PLASTIC.md",
|
|
148
148
|
"deprecations.yml" => "deprecations.yml",
|
|
149
149
|
"scripts/folgezettel-id" => "scripts/folgezettel-id",
|
|
150
150
|
"scripts/read-config" => "scripts/read-config",
|
|
151
|
+
"scripts/hook-session-start" => "scripts/hook-session-start",
|
|
152
|
+
"scripts/hook-continue" => "scripts/hook-continue",
|
|
153
|
+
"scripts/hook-future-intent-check" => "scripts/hook-future-intent-check",
|
|
154
|
+
"scripts/hook-gate-check" => "scripts/hook-gate-check",
|
|
155
|
+
"scripts/lib/bridge.rb" => "scripts/lib/bridge.rb",
|
|
151
156
|
}
|
|
152
157
|
|
|
153
158
|
core_files.each do |src, dest|
|
|
@@ -239,7 +244,7 @@ def install_claude(config, force)
|
|
|
239
244
|
|
|
240
245
|
installed = []
|
|
241
246
|
|
|
242
|
-
# Copy hooks
|
|
247
|
+
# Copy hooks, rewriting script paths for the installed location
|
|
243
248
|
hook_source = File.join(PACKAGE_ROOT, "hooks")
|
|
244
249
|
Dir.glob(File.join(hook_source, "*")).each do |f|
|
|
245
250
|
next unless File.file?(f)
|
|
@@ -247,7 +252,9 @@ def install_claude(config, force)
|
|
|
247
252
|
next if %w[hooks.json run-hook].include?(basename)
|
|
248
253
|
dest_name = basename.start_with?("plastic-") ? basename : "plastic-#{basename}"
|
|
249
254
|
dest = File.join(hooks_dir, dest_name)
|
|
250
|
-
|
|
255
|
+
content = File.read(f)
|
|
256
|
+
content = content.gsub('$SCRIPT_DIR/../scripts/', '$HOME/.plastic/scripts/')
|
|
257
|
+
File.write(dest, content)
|
|
251
258
|
FileUtils.chmod(0o755, dest)
|
|
252
259
|
installed << dest
|
|
253
260
|
end
|
|
@@ -304,42 +311,78 @@ end
|
|
|
304
311
|
|
|
305
312
|
def merge_claude_hooks(settings_path)
|
|
306
313
|
settings = read_json_safe(settings_path) || {}
|
|
307
|
-
return if settings.nil?
|
|
314
|
+
return if settings.nil?
|
|
308
315
|
|
|
309
316
|
hooks = settings["hooks"] ||= {}
|
|
310
317
|
hook_dir = File.join(Dir.home, ".claude", "hooks")
|
|
311
318
|
|
|
319
|
+
purge_stale_plastic_hooks(hooks)
|
|
320
|
+
|
|
312
321
|
plastic_hooks = {
|
|
313
|
-
"SessionStart" =>
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
"
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
322
|
+
"SessionStart" => {
|
|
323
|
+
"matcher" => "",
|
|
324
|
+
"hooks" => [
|
|
325
|
+
{ "type" => "command", "command" => "#{hook_dir}/plastic-session-start", "statusMessage" => "Loading Plastic context..." },
|
|
326
|
+
{ "type" => "command", "command" => "#{hook_dir}/plastic-check-update", "statusMessage" => "" },
|
|
327
|
+
],
|
|
328
|
+
},
|
|
329
|
+
"PreCompact" => {
|
|
330
|
+
"matcher" => "",
|
|
331
|
+
"hooks" => [
|
|
332
|
+
{ "type" => "command", "command" => "#{hook_dir}/plastic-savepoint", "statusMessage" => "Saving Plastic intent state..." },
|
|
333
|
+
],
|
|
334
|
+
},
|
|
335
|
+
"PostToolUse" => {
|
|
336
|
+
"matcher" => "Write|Edit",
|
|
337
|
+
"hooks" => [
|
|
338
|
+
{ "type" => "command", "command" => "#{hook_dir}/plastic-gate-check", "statusMessage" => "Checking lifecycle gates..." },
|
|
339
|
+
],
|
|
340
|
+
},
|
|
341
|
+
"UserPromptSubmit" => {
|
|
342
|
+
"matcher" => "",
|
|
343
|
+
"hooks" => [
|
|
344
|
+
{ "type" => "command", "command" => "#{hook_dir}/plastic-continue", "statusMessage" => "Checking for continue..." },
|
|
345
|
+
{ "type" => "command", "command" => "#{hook_dir}/plastic-future-intent-check", "statusMessage" => "Checking future intents..." },
|
|
346
|
+
],
|
|
347
|
+
},
|
|
330
348
|
}
|
|
331
349
|
|
|
332
|
-
plastic_hooks.each do |event,
|
|
350
|
+
plastic_hooks.each do |event, group|
|
|
333
351
|
hooks[event] ||= []
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
352
|
+
existing = hooks[event].find { |g| g.is_a?(Hash) && g["hooks"].is_a?(Array) && g["hooks"].any? { |h| h["command"].to_s.include?("plastic-") } }
|
|
353
|
+
|
|
354
|
+
if existing
|
|
355
|
+
existing["matcher"] = group["matcher"]
|
|
356
|
+
existing["hooks"] = group["hooks"]
|
|
357
|
+
else
|
|
358
|
+
hooks[event] << group
|
|
337
359
|
end
|
|
338
360
|
end
|
|
339
361
|
|
|
362
|
+
settings["statusLine"] = { "type" => "command", "command" => "#{hook_dir}/plastic-statusline" }
|
|
363
|
+
|
|
340
364
|
write_json_atomic(settings_path, settings)
|
|
341
365
|
end
|
|
342
366
|
|
|
367
|
+
def purge_stale_plastic_hooks(hooks)
|
|
368
|
+
stale = ->(cmd) { cmd.to_s.match?(/ruby\s+.*plastic-/) || cmd.to_s.match?(/plastic-[a-z-]+\.rb/) }
|
|
369
|
+
|
|
370
|
+
hooks.each do |event, groups|
|
|
371
|
+
next unless groups.is_a?(Array)
|
|
372
|
+
|
|
373
|
+
hooks[event] = groups.map do |group|
|
|
374
|
+
if group.is_a?(Hash) && group["hooks"].is_a?(Array)
|
|
375
|
+
group["hooks"].reject! { |h| stale.call(h["command"]) }
|
|
376
|
+
group unless group["hooks"].empty?
|
|
377
|
+
elsif group.is_a?(Hash) && group["command"]
|
|
378
|
+
stale.call(group["command"]) ? nil : group
|
|
379
|
+
else
|
|
380
|
+
group
|
|
381
|
+
end
|
|
382
|
+
end.compact
|
|
383
|
+
end
|
|
384
|
+
end
|
|
385
|
+
|
|
343
386
|
# --- Uninstall ---
|
|
344
387
|
|
|
345
388
|
def handle_uninstall(agents)
|
|
@@ -403,11 +446,24 @@ def remove_claude_hooks(settings_path)
|
|
|
403
446
|
settings = read_json_safe(settings_path)
|
|
404
447
|
return unless settings && settings["hooks"]
|
|
405
448
|
|
|
406
|
-
settings["hooks"].each do |event,
|
|
407
|
-
|
|
449
|
+
settings["hooks"].each do |event, groups|
|
|
450
|
+
next unless groups.is_a?(Array)
|
|
451
|
+
|
|
452
|
+
settings["hooks"][event] = groups.map do |group|
|
|
453
|
+
if group.is_a?(Hash) && group["hooks"].is_a?(Array)
|
|
454
|
+
group["hooks"].reject! { |h| h["command"].to_s.include?("plastic-") }
|
|
455
|
+
group unless group["hooks"].empty?
|
|
456
|
+
elsif group.is_a?(Hash) && group["command"]
|
|
457
|
+
group["command"].to_s.include?("plastic-") ? nil : group
|
|
458
|
+
else
|
|
459
|
+
group
|
|
460
|
+
end
|
|
461
|
+
end.compact
|
|
408
462
|
end
|
|
409
|
-
|
|
463
|
+
|
|
464
|
+
settings["hooks"].delete_if { |_, v| v.is_a?(Array) && v.empty? }
|
|
410
465
|
settings.delete("hooks") if settings["hooks"]&.empty?
|
|
466
|
+
settings.delete("statusLine") if settings.dig("statusLine", "command").to_s.include?("plastic-")
|
|
411
467
|
|
|
412
468
|
write_json_atomic(settings_path, settings)
|
|
413
469
|
end
|