samagotchi 0.149.0 → 0.151.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +69 -1
- data/README.md +4 -2
- data/docs/architecture.md +4 -4
- data/docs/bundle-authoring.md +1 -1
- data/docs/cli.md +59 -24
- data/docs/configuration.md +6 -6
- data/docs/internals/background-tasks.md +1 -1
- data/docs/internals/block-renderers.md +96 -0
- data/docs/internals/bridge.md +53 -9
- data/docs/internals/bundles.md +43 -14
- data/docs/internals/objectives.md +7 -2
- data/docs/internals/web-frontend.md +2 -2
- data/docs/internals/web-push.md +2 -2
- data/docs/internals/working-set.md +5 -2
- data/docs/memory.md +48 -10
- data/docs/plugins.md +49 -3
- data/docs/sessions.md +5 -4
- data/docs/testing.md +5 -0
- data/lib/samagotchi/atomic_file.rb +3 -0
- data/lib/samagotchi/block_renderers.rb +115 -0
- data/lib/samagotchi/bootstrap_command.rb +5 -2
- data/lib/samagotchi/bridge/card_places.rb +93 -0
- data/lib/samagotchi/bridge/card_store.rb +224 -82
- data/lib/samagotchi/bridge.rb +12 -4
- data/lib/samagotchi/bundle_command/try/trace.rb +1 -1
- data/lib/samagotchi/bundle_command.rb +91 -68
- data/lib/samagotchi/bundler_env.rb +24 -0
- data/lib/samagotchi/bundles/dev/manifest.yml +2 -2
- data/lib/samagotchi/bundles/github-pr/manifest.yml +3 -3
- data/lib/samagotchi/bundles/github-pr/plugin.rb +2 -1
- data/lib/samagotchi/bundles/known-names/hooks/known_names.rb +3 -1
- data/lib/samagotchi/bundles/known-names/manifest.yml +3 -2
- data/lib/samagotchi/bundles/source-links/hooks/source_links.rb +2 -2
- data/lib/samagotchi/bundles/source-links/manifest.yml +3 -3
- data/lib/samagotchi/bundles/system/manifest.yml +3 -3
- data/lib/samagotchi/bundles/system/memory_guide.md +5 -5
- data/lib/samagotchi/bundles/system/self_map.md +1 -1
- data/lib/samagotchi/chi_home.rb +4 -2
- data/lib/samagotchi/chi_shim.rb +2 -1
- data/lib/samagotchi/child_env.rb +10 -3
- data/lib/samagotchi/engine.rb +19 -1
- data/lib/samagotchi/git_env.rb +43 -0
- data/lib/samagotchi/guardrails/context.rb +2 -0
- data/lib/samagotchi/guardrails/targets.rb +4 -1
- data/lib/samagotchi/memory_bundle/asset_installer.rb +23 -3
- data/lib/samagotchi/memory_bundle/builder.rb +5 -3
- data/lib/samagotchi/memory_bundle/file_standing.rb +167 -0
- data/lib/samagotchi/memory_bundle/installed_bundle.rb +70 -6
- data/lib/samagotchi/memory_bundle/installed_copy.rb +1 -0
- data/lib/samagotchi/memory_bundle/installer.rb +162 -52
- data/lib/samagotchi/memory_bundle/listing.rb +4 -3
- data/lib/samagotchi/memory_bundle/manifest.rb +23 -2
- data/lib/samagotchi/memory_bundle/merger.rb +209 -8
- data/lib/samagotchi/memory_bundle/placeholder.rb +2 -1
- data/lib/samagotchi/memory_bundle/profile.rb +7 -3
- data/lib/samagotchi/memory_bundle/provenance.rb +182 -22
- data/lib/samagotchi/memory_bundle/remerge.rb +167 -0
- data/lib/samagotchi/memory_bundle/shipped_history.rb +79 -0
- data/lib/samagotchi/memory_bundle/shipped_update.rb +89 -24
- data/lib/samagotchi/memory_bundle/source.rb +3 -3
- data/lib/samagotchi/memory_bundle/status.rb +21 -3
- data/lib/samagotchi/memory_bundle/system_bundle.rb +98 -33
- data/lib/samagotchi/memory_bundle/text_merge.rb +399 -0
- data/lib/samagotchi/memory_bundle/trash.rb +58 -2
- data/lib/samagotchi/memory_bundle/uninstaller.rb +26 -6
- data/lib/samagotchi/objectives_check.rb +37 -7
- data/lib/samagotchi/project_env.rb +3 -2
- data/lib/samagotchi/self_report.rb +2 -0
- data/lib/samagotchi/session_chain.rb +2 -7
- data/lib/samagotchi/session_commands.rb +15 -9
- data/lib/samagotchi/session_manager.rb +10 -7
- data/lib/samagotchi/steer.rb +17 -0
- data/lib/samagotchi/terminal_ui.rb +4 -4
- data/lib/samagotchi/testing/boundary.rb +1 -0
- data/lib/samagotchi/testing/minitest.rb +3 -0
- data/lib/samagotchi/testing/rspec.rb +3 -0
- data/lib/samagotchi/testing/sandbox.rb +3 -1
- data/lib/samagotchi/tools/delegate.rb +2 -1
- data/lib/samagotchi/tools/task_runtime.rb +13 -10
- data/lib/samagotchi/turn_checkpoint.rb +11 -5
- data/lib/samagotchi/turn_flow.rb +2 -1
- data/lib/samagotchi/update_command.rb +76 -28
- data/lib/samagotchi/update_hint.rb +4 -1
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +22 -12
- data/lib/samagotchi/web/auto_update.rb +8 -8
- data/lib/samagotchi/web/auto_update_record.rb +6 -4
- data/lib/samagotchi/web/block_renderer_routes.rb +32 -0
- data/lib/samagotchi/web/markdown_renderer.rb +39 -6
- data/lib/samagotchi/web/message_display.rb +3 -3
- data/lib/samagotchi/web/public/app.js +7 -3
- data/lib/samagotchi/web/public/block_frame.js +202 -0
- data/lib/samagotchi/web/public/block_host.js +502 -0
- data/lib/samagotchi/web/public/block_open.js +13 -0
- data/lib/samagotchi/web/public/block_renderers.css +20 -0
- data/lib/samagotchi/web/public/block_source.js +37 -0
- data/lib/samagotchi/web/public/bubbles.js +11 -5
- data/lib/samagotchi/web/public/card.js +17 -10
- data/lib/samagotchi/web/public/cards_place.js +10 -5
- data/lib/samagotchi/web/public/claim_link.js +4 -2
- data/lib/samagotchi/web/public/details_motion.js +2 -1
- data/lib/samagotchi/web/public/history_reveal.js +4 -1
- data/lib/samagotchi/web/public/index.html +20 -14
- data/lib/samagotchi/web/public/markdown_place.js +2 -3
- data/lib/samagotchi/web/public/stage_model.js +5 -6
- data/lib/samagotchi/web/public/stage_view.js +8 -7
- data/lib/samagotchi/web/public/text_roots.js +6 -0
- data/lib/samagotchi/web/public/turn_block.js +34 -19
- data/lib/samagotchi/web/public/turn_html.js +4 -1
- data/lib/samagotchi/web/public/turn_model.js +1 -1
- data/lib/samagotchi/web/public/update_sheet.js +8 -3
- data/lib/samagotchi/web/public/working_set_view.js +10 -7
- data/lib/samagotchi/web/push/notifier.rb +7 -9
- data/lib/samagotchi/web/session_hub.rb +2 -1
- data/lib/samagotchi/web/session_show.rb +1 -1
- data/lib/samagotchi/web/update_ops.rb +2 -1
- data/lib/samagotchi/worker.rb +4 -2
- metadata +17 -1
|
@@ -15,17 +15,19 @@ module Samagotchi
|
|
|
15
15
|
install <source> [--scope system|project] [--force]
|
|
16
16
|
Install a memory bundle from a directory, zip, tar archive, or git URL.
|
|
17
17
|
Source is the path/URL to the bundle (dir/zip/tar.gz/git).
|
|
18
|
-
--force overwrites existing entries; default skips them.
|
|
18
|
+
--force overwrites existing entries (one that differs is copied to the trash first); default skips them.
|
|
19
19
|
|
|
20
20
|
upgrade <source> [--scope system|project] [--force] [--dry-run] [--agent|--no-agent]
|
|
21
|
-
Upgrade a bundle (3-way
|
|
21
|
+
Upgrade a bundle (3-way: a file you didn't edit updates; one you edited takes a text
|
|
22
|
+
merge, the old file in the bundle trash; a real conflict leaves it as it is).
|
|
22
23
|
--dry-run shows what would change. --agent launches interactive session on conflict.
|
|
23
24
|
|
|
24
25
|
uninstall <bundle> [--scope system|project] [--force]
|
|
25
|
-
Remove a bundle and its index entries (--force
|
|
26
|
+
Remove a bundle and its index entries (--force moves a locally edited file to the trash).
|
|
26
27
|
|
|
27
28
|
status [<bundle>]
|
|
28
|
-
Show provenance
|
|
29
|
+
Show provenance and each file's status for bundles. issues= counts what
|
|
30
|
+
needs you: a file that stays behind, is missing or can't be read, not one you edited.
|
|
29
31
|
|
|
30
32
|
diff <bundle> [file]
|
|
31
33
|
Show diffs between base/current/incoming for a bundle.
|
|
@@ -66,7 +68,7 @@ module Samagotchi
|
|
|
66
68
|
install <source> [--scope system|project] [--force]
|
|
67
69
|
Install a memory bundle from a directory, zip, tar archive, or git URL,
|
|
68
70
|
or a bundle shipped with chi by name (see: chi bundle list).
|
|
69
|
-
--force overwrites existing entries; default skips them.
|
|
71
|
+
--force overwrites existing entries (one that differs is copied to the trash first); default skips them.
|
|
70
72
|
install --dev PATH
|
|
71
73
|
Install the bundle in the local directory PATH as a link to it: its plugin, hooks, rules and scripts
|
|
72
74
|
are PATH's own files (an edit applies at the next worker or chi web restart, with no re-install) and
|
|
@@ -231,6 +233,9 @@ module Samagotchi
|
|
|
231
233
|
run_installer(installer, failed: "Upgrade") do |manifest|
|
|
232
234
|
print_hooks_and_plugin(manifest, plugin: !dry_run)
|
|
233
235
|
next dry_run_done if dry_run
|
|
236
|
+
|
|
237
|
+
# Its summary said so, to someone reading.
|
|
238
|
+
Samagotchi::MemoryBundle::Provenance.new(name: bundle_name).clear_merge_notes([], files: installer.merged_files)
|
|
234
239
|
next upgrade_conflicts(installer, bundle_name, expanded_source, agent) if installer.conflicts.any? && !force
|
|
235
240
|
|
|
236
241
|
print_provenance(bundle_name, manifest)
|
|
@@ -321,42 +326,64 @@ module Samagotchi
|
|
|
321
326
|
# (scripts already read it so): not CLI::Exit::USAGE by meaning.
|
|
322
327
|
CONFLICTS_KEPT = 2
|
|
323
328
|
|
|
324
|
-
# @return [Integer] 0 resolved, CONFLICTS_KEPT kept
|
|
329
|
+
# @return [Integer] 0 all resolved, CONFLICTS_KEPT kept some edits
|
|
325
330
|
def upgrade_conflicts(installer, bundle_name, expanded_source, agent)
|
|
326
|
-
|
|
327
|
-
|
|
331
|
+
standing = Samagotchi::MemoryBundle::FileStanding
|
|
332
|
+
@stdout.puts "\n#{standing.stay_behind(installer.behind.size)}: #{installer.behind.map(&:file).join(", ")}"
|
|
328
333
|
launch = if agent.nil? && @stdin.tty?
|
|
329
|
-
@stdout.print "
|
|
334
|
+
@stdout.print "Launch an interactive agent to merge them? [y/N] "
|
|
330
335
|
ans = begin; @stdin.gets; rescue StandardError => _e; nil; end
|
|
331
336
|
ans && ans.strip.downcase.start_with?("y")
|
|
332
337
|
elsif agent.nil?
|
|
333
|
-
@stdout.puts "Non-interactive terminal:
|
|
338
|
+
@stdout.puts "Non-interactive terminal: the rest is upgraded. #{standing::FORCE_TAKES}; --agent in a TTY merges."
|
|
334
339
|
return CONFLICTS_KEPT
|
|
335
340
|
else
|
|
336
341
|
agent
|
|
337
342
|
end
|
|
338
343
|
unless launch
|
|
339
|
-
@stdout.puts "
|
|
344
|
+
@stdout.puts "The rest is upgraded. #{standing.diff_hint(bundle_name, "FILE")} shows the base; #{standing::FORCE_TAKES}."
|
|
340
345
|
return CONFLICTS_KEPT
|
|
341
346
|
end
|
|
342
347
|
|
|
343
348
|
prompt = build_conflict_prompt(bundle_name, installer.conflicts, expanded_source)
|
|
344
|
-
|
|
349
|
+
before = installer.conflicts.to_h { |key, info| [key, Samagotchi::MemoryBundle::Merger.digest(info[:current])] }
|
|
350
|
+
@stdout.puts "Launching an interactive session to merge them… (/exit when done)"
|
|
345
351
|
require "samagotchi/terminal_ui"
|
|
346
352
|
Samagotchi::TerminalUI.new(prompt: prompt).run
|
|
347
|
-
|
|
348
|
-
# their old base); the resolved files now start from the bundle's
|
|
349
|
-
# version.
|
|
350
|
-
Samagotchi::MemoryBundle::Provenance.new(name: bundle_name).resolve_conflicts(installer.conflicts)
|
|
351
|
-
@stdout.puts "Provenance updated after interactive resolution."
|
|
352
|
-
@stdout.puts "Upgrade resolved interactively."
|
|
353
|
-
0
|
|
353
|
+
settle_conflicts(installer.conflicts, before, bundle_name)
|
|
354
354
|
ensure
|
|
355
355
|
# An owned source (git/zip/tar) was kept for the incoming files above;
|
|
356
356
|
# the agent step is done with them now.
|
|
357
357
|
installer.cleanup_source!
|
|
358
358
|
end
|
|
359
359
|
|
|
360
|
+
# After the agent step. The installer already recorded the upgrade
|
|
361
|
+
# (conflicted files kept their old base); a file the step changed, with
|
|
362
|
+
# no conflict markers left, now starts from the bundle's version. Any
|
|
363
|
+
# other stays a conflict: moving its base would turn a visible miss into
|
|
364
|
+
# a kept edit the next upgrade says nothing about.
|
|
365
|
+
# @param before [Hash{String => String, nil}] each file's digest before the step
|
|
366
|
+
# @return [Integer] 0 all resolved, CONFLICTS_KEPT some still behind
|
|
367
|
+
def settle_conflicts(conflicts, before, bundle_name)
|
|
368
|
+
merger = Samagotchi::MemoryBundle::Merger
|
|
369
|
+
untouched, changed = conflicts.partition { |key, info| merger.digest(info[:current]) == before[key] }
|
|
370
|
+
marked, clean = changed.partition { |_, info| merger.markers?(info[:current]) }
|
|
371
|
+
settled = Samagotchi::MemoryBundle::Provenance.new(name: bundle_name).resolve_conflicts(clean.to_h)
|
|
372
|
+
gone = clean.map(&:first) - settled
|
|
373
|
+
standing = Samagotchi::MemoryBundle::FileStanding
|
|
374
|
+
untouched.each { |key, _| @stdout.puts standing.still_behind(:untouched, key) }
|
|
375
|
+
marked.each { |key, _| @stdout.puts standing.still_behind(:markers, key) }
|
|
376
|
+
gone.each { |key| @stdout.puts standing.still_behind(:incoming_gone, key) }
|
|
377
|
+
if untouched.empty? && marked.empty? && gone.empty?
|
|
378
|
+
@stdout.puts "Provenance updated after interactive resolution."
|
|
379
|
+
@stdout.puts "Upgrade resolved interactively."
|
|
380
|
+
return 0
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
@stdout.puts "Resolved: #{settled.join(", ")}" unless settled.empty?
|
|
384
|
+
CONFLICTS_KEPT
|
|
385
|
+
end
|
|
386
|
+
|
|
360
387
|
def uninstall(rest)
|
|
361
388
|
parsed = parse_flags(UNINSTALL_FLAGS, rest, "uninstall", help: UNINSTALL_HELP)
|
|
362
389
|
return parsed if parsed.is_a?(Integer)
|
|
@@ -377,6 +404,7 @@ module Samagotchi
|
|
|
377
404
|
uninstaller.run
|
|
378
405
|
@stdout.puts "Uninstalled bundle '#{bundle_name}'"
|
|
379
406
|
puts_trashed(uninstaller.trashed_files, uninstaller.trash_dir)
|
|
407
|
+
@stdout.puts uninstaller.trash_copy_line if uninstaller.trash_copy_line
|
|
380
408
|
@stdout.puts "Removed: #{uninstaller.removed_files.join(", ")}" unless uninstaller.removed_files.empty?
|
|
381
409
|
hook_removed = uninstaller.removed_files.count { |f| f.start_with?("hooks/") }
|
|
382
410
|
@stdout.puts "Hooks removed: #{hook_removed}" if hook_removed > 0
|
|
@@ -402,6 +430,7 @@ module Samagotchi
|
|
|
402
430
|
result.skipped.each { |member, why| lines << "skipped #{member}: #{why}" }
|
|
403
431
|
result.failed.each { |member, why| lines << "failed #{member}: #{why}" }
|
|
404
432
|
lines.concat(result.kept_own_lines)
|
|
433
|
+
lines.concat(result.trash_copies)
|
|
405
434
|
lines << "nothing new to install" if lines.empty?
|
|
406
435
|
lines.each { |line| @stdout.puts " #{line}" }
|
|
407
436
|
@stdout.puts "(dry-run: no changes written)" if dry_run
|
|
@@ -453,16 +482,21 @@ module Samagotchi
|
|
|
453
482
|
else
|
|
454
483
|
@stdout.puts "Target: #{st.target_dir}"
|
|
455
484
|
end
|
|
485
|
+
standing = Samagotchi::MemoryBundle::FileStanding
|
|
456
486
|
st.files.each do |k, info|
|
|
457
487
|
next @stdout.puts(" #{k}: unchecked") if info[:unchecked]
|
|
458
488
|
|
|
459
489
|
mods = []
|
|
460
|
-
mods << "conflict (kept your edits over v#{st.provenance.version}: chi bundle diff #{name} #{k})" if info[:conflict]
|
|
461
490
|
mods << "modified" if info[:modified]
|
|
462
491
|
mods << "missing" if info[:missing]
|
|
492
|
+
mods << "unreadable" if info[:unreadable]
|
|
463
493
|
mods << "no-index" unless info[:index_present] || info[:overlay]
|
|
464
|
-
label = mods.
|
|
494
|
+
label = mods.join(",")
|
|
495
|
+
label = "ok" if mods.empty? && !info[:behind]
|
|
465
496
|
label += " (model overlay)" if info[:overlay]
|
|
497
|
+
behind = info[:behind] && "#{info[:behind].state} (#{standing.diff_hint(name, k)})"
|
|
498
|
+
label = [label, behind].reject { |part| part.nil? || part.empty? }.join("; ")
|
|
499
|
+
label += "; #{info[:merge_note].line}#{" (old copy in #{info[:merge_note].trash})" if info[:merge_note].trash}" if info[:merge_note]
|
|
466
500
|
@stdout.puts " #{k}: #{label}"
|
|
467
501
|
end
|
|
468
502
|
# Hooks
|
|
@@ -499,7 +533,7 @@ module Samagotchi
|
|
|
499
533
|
next @stdout.puts(" #{bname} (manifest.json unreadable)") if record.error?
|
|
500
534
|
|
|
501
535
|
st = Samagotchi::MemoryBundle::Status.bundle_status(bname)
|
|
502
|
-
mods = st.files.values.count { |v| v[:
|
|
536
|
+
mods = st.files.values.count { |v| v[:behind] || v[:missing] || v[:unreadable] }
|
|
503
537
|
hooks_count = st.provenance.hooks.size
|
|
504
538
|
hook_info = hooks_count > 0 ? " hooks=#{hooks_count}" : ""
|
|
505
539
|
plugin = st.plugin
|
|
@@ -543,77 +577,62 @@ module Samagotchi
|
|
|
543
577
|
rescue ArgumentError => e
|
|
544
578
|
@stderr.puts "Bundle '#{bname}': #{e.message}"; return 1
|
|
545
579
|
end
|
|
546
|
-
files = record.files
|
|
547
580
|
hooks = record.hooks
|
|
548
581
|
plugin_path = prov.plugin_path(record)
|
|
549
582
|
plugin_file = plugin_path && File.basename(plugin_path)
|
|
550
|
-
show_plugin = lambda do
|
|
551
|
-
@stdout.puts "=== plugin/#{plugin_file} ==="
|
|
552
|
-
@stdout.puts "--- base (provenance) ---"
|
|
553
|
-
base = prov.plugin_base_path(plugin_file)
|
|
554
|
-
@stdout.puts File.exist?(base) ? File.read(base) : "(no base)"
|
|
555
|
-
@stdout.puts "--- current (on-disk) ---"
|
|
556
|
-
@stdout.puts File.exist?(plugin_path) ? File.read(plugin_path) : "(missing)"
|
|
557
|
-
@stdout.puts "--- metadata: sha256=#{record.plugin.sha256}#{" requires_chi=#{record.requires_chi}" if record.requires_chi}"
|
|
558
|
-
end
|
|
559
583
|
if file_arg
|
|
560
584
|
# Try file first, then hook
|
|
561
585
|
if record.owns?(file_arg)
|
|
562
|
-
|
|
563
|
-
cur = File.join(target_dir, file_arg)
|
|
564
|
-
@stdout.puts "=== #{file_arg} ==="
|
|
565
|
-
@stdout.puts "--- base (provenance) ---"
|
|
566
|
-
@stdout.puts File.exist?(base) ? File.read(base) : "(no base)"
|
|
567
|
-
@stdout.puts "--- current (on-disk) ---"
|
|
568
|
-
@stdout.puts File.exist?(cur) ? File.read(cur) : "(missing)"
|
|
586
|
+
diff_memory(prov, target_dir, file_arg)
|
|
569
587
|
elsif hooks.key?(file_arg)
|
|
570
|
-
|
|
571
|
-
cur = File.join(prov.hooks_dir, file_arg)
|
|
572
|
-
@stdout.puts "=== hooks/#{file_arg} ==="
|
|
573
|
-
@stdout.puts "--- base (provenance) ---"
|
|
574
|
-
@stdout.puts File.exist?(base) ? File.read(base) : "(no base)"
|
|
575
|
-
@stdout.puts "--- current (on-disk) ---"
|
|
576
|
-
@stdout.puts File.exist?(cur) ? File.read(cur) : "(missing)"
|
|
577
|
-
# Show hook metadata
|
|
578
|
-
@stdout.puts hook_metadata_line(hooks[file_arg])
|
|
588
|
+
diff_hook(prov, file_arg, hooks[file_arg])
|
|
579
589
|
elsif plugin_file && [plugin_file, "plugin/#{plugin_file}"].include?(file_arg)
|
|
580
|
-
|
|
590
|
+
diff_plugin(prov, record, plugin_path)
|
|
581
591
|
else
|
|
582
592
|
@stdout.puts "=== #{file_arg} ==="
|
|
583
593
|
@stdout.puts "(not found in bundle)"
|
|
584
594
|
end
|
|
585
595
|
@stdout.puts ""
|
|
586
596
|
else
|
|
587
|
-
files.each_key do |
|
|
588
|
-
|
|
589
|
-
cur = File.join(target_dir, k)
|
|
590
|
-
@stdout.puts "=== #{k} ==="
|
|
591
|
-
@stdout.puts "--- base (provenance) ---"
|
|
592
|
-
@stdout.puts File.exist?(base) ? File.read(base) : "(no base)"
|
|
593
|
-
@stdout.puts "--- current (on-disk) ---"
|
|
594
|
-
@stdout.puts File.exist?(cur) ? File.read(cur) : "(missing)"
|
|
597
|
+
record.files.each_key do |key|
|
|
598
|
+
diff_memory(prov, target_dir, key)
|
|
595
599
|
@stdout.puts ""
|
|
596
600
|
end
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
base = prov.base_path(k)
|
|
600
|
-
cur = File.join(prov.hooks_dir, k)
|
|
601
|
-
@stdout.puts "=== hooks/#{k} ==="
|
|
602
|
-
@stdout.puts "--- base (provenance) ---"
|
|
603
|
-
@stdout.puts File.exist?(base) ? File.read(base) : "(no base)"
|
|
604
|
-
@stdout.puts "--- current (on-disk) ---"
|
|
605
|
-
@stdout.puts File.exist?(cur) ? File.read(cur) : "(missing)"
|
|
606
|
-
@stdout.puts hook_metadata_line(hook)
|
|
601
|
+
hooks.each do |key, hook|
|
|
602
|
+
diff_hook(prov, key, hook)
|
|
607
603
|
@stdout.puts ""
|
|
608
604
|
end
|
|
609
605
|
if plugin_file
|
|
610
|
-
|
|
606
|
+
diff_plugin(prov, record, plugin_path)
|
|
611
607
|
@stdout.puts ""
|
|
612
608
|
end
|
|
613
609
|
end
|
|
614
610
|
0
|
|
615
611
|
end
|
|
616
612
|
|
|
613
|
+
def diff_memory(prov, target_dir, key)
|
|
614
|
+
print_base_and_current(key, prov.base_path(key), File.join(target_dir, key))
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
def diff_hook(prov, key, hook)
|
|
618
|
+
print_base_and_current("hooks/#{key}", prov.base_path(key), File.join(prov.hooks_dir, key))
|
|
619
|
+
@stdout.puts hook_metadata_line(hook)
|
|
620
|
+
end
|
|
621
|
+
|
|
622
|
+
def diff_plugin(prov, record, plugin_path)
|
|
623
|
+
file = File.basename(plugin_path)
|
|
624
|
+
print_base_and_current("plugin/#{file}", prov.plugin_base_path(file), plugin_path)
|
|
625
|
+
@stdout.puts "--- metadata: sha256=#{record.plugin.sha256}#{" requires_chi=#{record.requires_chi}" if record.requires_chi}"
|
|
626
|
+
end
|
|
627
|
+
|
|
628
|
+
def print_base_and_current(heading, base, current)
|
|
629
|
+
@stdout.puts "=== #{heading} ==="
|
|
630
|
+
@stdout.puts "--- base (provenance) ---"
|
|
631
|
+
@stdout.puts File.exist?(base) ? File.read(base) : "(no base)"
|
|
632
|
+
@stdout.puts "--- current (on-disk) ---"
|
|
633
|
+
@stdout.puts File.exist?(current) ? File.read(current) : "(missing)"
|
|
634
|
+
end
|
|
635
|
+
|
|
617
636
|
def list
|
|
618
637
|
listing = Samagotchi::MemoryBundle::Listing
|
|
619
638
|
shipped = listing.shipped
|
|
@@ -634,6 +653,10 @@ module Samagotchi
|
|
|
634
653
|
line = " #{b.name.ljust(name_w)} #{"v#{b.version || "?"}".ljust(ver_w)} scope=#{b.scope || "?"} " \
|
|
635
654
|
"#{b.includes ? profile_members(b.includes, installed) : "files=#{b.files}"} installed=#{b.installed_at || "?"}"
|
|
636
655
|
line += " (shipped v#{b.upgrade.version}: chi bundle upgrade #{b.upgrade.source})" if b.upgrade
|
|
656
|
+
if b.behind&.any?
|
|
657
|
+
count = Samagotchi::MemoryBundle::FileStanding.behind_count(b.behind.size, unsure: b.behind.any?(&:unsure))
|
|
658
|
+
line += " (#{count}: chi bundle status #{b.name})"
|
|
659
|
+
end
|
|
637
660
|
@stdout.puts line
|
|
638
661
|
end
|
|
639
662
|
@stdout.puts " (or all at once: chi update)" if installed.any?(&:upgrade)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Samagotchi
|
|
4
|
+
# Bundler's variables, in one place. Every chi spawn that must not carry a
|
|
5
|
+
# `bundle exec` around it takes its names from here.
|
|
6
|
+
module BundlerEnv
|
|
7
|
+
# All Bundler's own and its settings (BUNDLE_PATH, BUNDLE_WITHOUT, ...), for
|
|
8
|
+
# what must take none: a pattern's start (ChiShim, ProjectEnv::RESTORE_NEVER).
|
|
9
|
+
PREFIX = "BUNDLE"
|
|
10
|
+
# What `bundle exec` sets for a child (Bundler::SharedHelpers#set_bundle_variables;
|
|
11
|
+
# bundler_env_spec asks it). Without them, and without Bundler's RUBYOPT, a
|
|
12
|
+
# child ruby is free of the bundle; BUNDLER_SETUP on its own isn't: RubyGems
|
|
13
|
+
# requires what it names at boot, and a Gemfile it then finds writes its lock
|
|
14
|
+
# to BUNDLE_LOCKFILE, the bundle's. A project's own settings (BUNDLE_PATH and
|
|
15
|
+
# the like) aren't here: its `bundle` keeps them. Nor is BUNDLER_ORIG_*, which
|
|
16
|
+
# also reaches a tool: a project's `bundle exec` there reads it, for
|
|
17
|
+
# Bundler.original_env and with_unbundled_env, whose children get the PATH and
|
|
18
|
+
# GEM_* chi's own `bundle exec` started from.
|
|
19
|
+
VARS = %w[BUNDLE_BIN_PATH BUNDLE_GEMFILE BUNDLE_LOCKFILE BUNDLER_SETUP BUNDLER_VERSION].freeze
|
|
20
|
+
# With what else `bundle exec` steers a child ruby by: what a command that
|
|
21
|
+
# isn't the bundle's runs without (TaskRuntime, ProjectEnv, ChiHome.probe).
|
|
22
|
+
CHILD_RUBY = (%w[RUBYOPT RUBYLIB] + VARS).freeze
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: github-pr
|
|
3
|
-
version: 0.6.
|
|
3
|
+
version: 0.6.3
|
|
4
4
|
scope: system
|
|
5
5
|
description: Proposes the branch's open GitHub pull request to a session, attached once you mention it (#N, the branch or its URL), and attaches any PR URL with chi context add or the web's "+ URL"; the agent gets a note when reviews, comments, checks or its state change; in the web, a path:line in an answer links to that line of the PR; the auto_attach setting can attach the PR at once instead, or do nothing
|
|
6
6
|
trust_level: reviewed
|
|
7
|
-
requires_chi: ">= 0.
|
|
7
|
+
requires_chi: ">= 0.150.0"
|
|
8
8
|
needs:
|
|
9
9
|
- command: gh
|
|
10
10
|
why: reads the pull request
|
|
11
11
|
hint: brew install gh, then gh auth login
|
|
12
12
|
plugin:
|
|
13
13
|
file: plugin.rb
|
|
14
|
-
sha256: sha256:
|
|
14
|
+
sha256: sha256:82f8c76d38f314c3503eef3a5de0f8c0b20d0bb5bf4a3daebca43b60d00f8c7a
|
|
15
15
|
scripts:
|
|
16
16
|
pr_context.rb: sha256:a883d2780e89c68c0d7b6d5e0fcb22c273f17bb167e79064e0e99db6bd2ee6f0
|
|
17
17
|
context_providers:
|
|
@@ -5,6 +5,7 @@ require "json"
|
|
|
5
5
|
require "open3"
|
|
6
6
|
require "time"
|
|
7
7
|
require "tmpdir"
|
|
8
|
+
require "samagotchi/git_env"
|
|
8
9
|
|
|
9
10
|
# github-pr: a session on a branch with an open GitHub PR gets the PR
|
|
10
11
|
# proposed as a candidate (docs/context.md, Candidates) when its worker
|
|
@@ -334,7 +335,7 @@ class Plugin
|
|
|
334
335
|
|
|
335
336
|
# stdout of a command that succeeded within COMMAND_SECONDS, else nil.
|
|
336
337
|
def run(ctx, *command, chdir: ctx.cwd)
|
|
337
|
-
Open3.popen2(*command, chdir: chdir, err: File::NULL, pgroup: true) do |stdin, stdout, wait|
|
|
338
|
+
Open3.popen2(Samagotchi::GitEnv::UNSET, *command, chdir: chdir, err: File::NULL, pgroup: true) do |stdin, stdout, wait|
|
|
338
339
|
stdin.close
|
|
339
340
|
reader = Thread.new { stdout.read }
|
|
340
341
|
unless wait.join(COMMAND_SECONDS)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "samagotchi/git_env"
|
|
4
|
+
|
|
3
5
|
# A before_tool_call guard against near-misses of protected identifiers in
|
|
4
6
|
# a tool call's command or paths: the user's home folder name, login, git
|
|
5
7
|
# name and email, the repo name, and any names from config. A local model
|
|
@@ -187,7 +189,7 @@ class KnownNames
|
|
|
187
189
|
end
|
|
188
190
|
|
|
189
191
|
def git_config(root, key)
|
|
190
|
-
IO.popen(["git", "-C", root, "config", "--get", key], err: File::NULL, &:read).to_s.strip
|
|
192
|
+
IO.popen(Samagotchi::GitEnv::UNSET, ["git", "-C", root, "config", "--get", key], err: File::NULL, &:read).to_s.strip
|
|
191
193
|
rescue StandardError
|
|
192
194
|
""
|
|
193
195
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: known-names
|
|
3
|
-
version: 0.1.
|
|
3
|
+
version: 0.1.9
|
|
4
|
+
requires_chi: ">= 0.150.0"
|
|
4
5
|
scope: system
|
|
5
6
|
description: Catches a near-miss of a protected name (home folder, login, git name, repo name, names from config) in a tool call and rejects it with the right spelling; mode ask / correct optional
|
|
6
7
|
trust_level: reviewed
|
|
@@ -8,7 +9,7 @@ files:
|
|
|
8
9
|
known_names.md: sha256:d5348b95b6c655017b541910b36e384220da280818160201f6ec9a50e1e4287c
|
|
9
10
|
hooks:
|
|
10
11
|
known_names.rb:
|
|
11
|
-
sha256: sha256:
|
|
12
|
+
sha256: sha256:d7eed0dd0f418d8099a147ed124c6e6062b8c752035dd6b5aa5e926e29941c8b
|
|
12
13
|
event: before_tool_call
|
|
13
14
|
on_error: log
|
|
14
15
|
priority: 50
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Loaded through module_eval: it requires what it uses.
|
|
4
4
|
require "open3"
|
|
5
|
+
require "samagotchi/git_env"
|
|
5
6
|
require "samagotchi/ref"
|
|
6
7
|
|
|
7
8
|
# An after_turn (and question_asked) hook that links the source refs the
|
|
@@ -621,8 +622,7 @@ class SourceLinks
|
|
|
621
622
|
end
|
|
622
623
|
|
|
623
624
|
def read_remote(dir, name)
|
|
624
|
-
out, status = Open3.capture2(
|
|
625
|
-
"git", "-C", dir, "remote", "get-url", name, err: File::NULL)
|
|
625
|
+
out, status = Open3.capture2(Samagotchi::GitEnv::UNSET, "git", "-C", dir, "remote", "get-url", name, err: File::NULL)
|
|
626
626
|
unless status.success?
|
|
627
627
|
Samagotchi::Log.debug(:hooks, "source_links_no_remote", remote: name, exit: status.exitstatus)
|
|
628
628
|
return nil
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: source-links
|
|
3
|
-
version: 0.6.
|
|
3
|
+
version: 0.6.3
|
|
4
4
|
scope: system
|
|
5
5
|
description: Links source refs (JIRA tickets, GitHub issues, …) in the model's answer — inline links in the web, refs in a waiting question's reply, and a one-line note after the turn (note false turns it off); sources are configured patterns
|
|
6
6
|
trust_level: reviewed
|
|
7
|
-
requires_chi: ">= 0.
|
|
7
|
+
requires_chi: ">= 0.150.0"
|
|
8
8
|
files:
|
|
9
9
|
source_links.md: sha256:ace01a0f7c308a0ae54ece714c31c0e6d7a49c372d98111030e6d268320bed3a
|
|
10
10
|
hooks:
|
|
11
11
|
source_links.rb:
|
|
12
|
-
sha256: sha256:
|
|
12
|
+
sha256: sha256:21d7a9f7c381c653b126b625feed0e0b6422eaa84c8dfe716b6c5b41644a33b3
|
|
13
13
|
events: [after_turn, question_asked]
|
|
14
14
|
on_error: log
|
|
15
15
|
priority: 90
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: samagotchi-system
|
|
3
|
-
version: 0.
|
|
3
|
+
version: 0.151.0
|
|
4
4
|
scope: system
|
|
5
5
|
description: Default system memories — identity, self map, config modification protocol, memory guide and the delegated-session rules
|
|
6
6
|
files:
|
|
7
7
|
identity.md: sha256:29c2f7df9f6c5acbc5ec17000a4490741e55e426af15ca0d81c8706edd9bd027
|
|
8
|
-
self_map.md: sha256:
|
|
8
|
+
self_map.md: sha256:e695f78f858c29ccfef2ed6f9d6dd86d0ec067ed704a5a1956c01e53617948a1
|
|
9
9
|
config_modification_protocol.md: sha256:8896d1d364ce835040f69b504518d43dbdf959b93fdd9b10f87a8de61cae84a8
|
|
10
|
-
memory_guide.md: sha256:
|
|
10
|
+
memory_guide.md: sha256:bfe9f13e8a6c929056fe531c4c74c7e05d0f17f025191c1a111c8e7dfd224fd6
|
|
11
11
|
delegated.md: sha256:a0d6ddcd4a8be06526d964898611775fd251cfd9d145a9d16afaccaa503597bd
|
|
@@ -114,9 +114,9 @@ Bundles are versioned directories/zips/tar.gz/git URLs with a `manifest.yml` and
|
|
|
114
114
|
| Command | Purpose |
|
|
115
115
|
|---------|---------|
|
|
116
116
|
| `install <source> [--scope system\|project] [--force]` | Install from dir/zip/tar.gz/git URL. `--force` overwrites existing entries, otherwise skips them; a skipped file stays the user's (not recorded, never upgraded or removed by the bundle). Writes provenance to `.bundles/<name>/` (base snapshots + `manifest.json`) and updates `index.md`. Warns on double-brace placeholders and checksum mismatches (strict mode). |
|
|
117
|
-
| `upgrade <source> [--force] [--dry-run] [--agent]` | 3-way merge upgrade (base vs current vs incoming) per `Merger.classify
|
|
117
|
+
| `upgrade <source> [--force] [--dry-run] [--agent]` | 3-way merge upgrade (base vs current vs incoming) per `Merger::Upgrade#outcome` (`Merger.classify`, then a text merge where both sides changed): `install` (new file), `noop` (current==incoming), `fast_forward` (current==base, not edited → auto-update), `keep` (incoming==base → preserve local edits), `merged` (both edited, a text merge with `git` comes out clean → the file takes it, the old one goes to the bundle trash, and entries both sides added under a `Changelog` heading are kept), `conflict` (both edited, a real conflict, or no `git` or base → the file stays untouched and marked, reported as `X stays behind 0.8.0: …` with why and what it missed, needs a retry, `--force`, which copies it to the trash first, or interactive `memory_write` resolution). A marked file is retried at the same version: the system bundle's at each chi start and on `chi update`, another shipped bundle's on `chi update`, any other bundle's on `chi bundle upgrade`. A file marked by an older chi merges normally when its recorded base is a version chi shipped (an installed samagotchi gem still has it); otherwise it merges only if the bundle takes out or rewords no line it still holds, and if it does, it stays behind until resolved by hand, with `--agent` or with `--force`, and the retry can't fix that. Pruned files (removed from new bundle) are kept if locally edited, otherwise warned. |
|
|
118
118
|
| `uninstall <bundle> [--force]` | Moves the bundle's memory files to `.bundles/.trash/<bundle>-<time>/` (refuses locally edited ones unless `--force`; keeps a file another installed bundle also lists), removes their `index.md` lines, deletes the provenance dir. |
|
|
119
|
-
| `status [<bundle>]` | Provenance + per-file `ok|modified|missing|no-index` vs stored checksum and base snapshot; a model overlay (`<name>.<key>.md`) has no index line and reads `ok (model overlay)`. |
|
|
119
|
+
| `status [<bundle>]` | Provenance + per-file `ok|modified|missing|unreadable|no-index` vs stored checksum and base snapshot, and what stays behind or merged at a chi start; `issues=` counts files behind, missing or unreadable, not edits; a model overlay (`<name>.<key>.md`) has no index line and reads `ok (model overlay)`. |
|
|
120
120
|
| `diff <bundle> [file]` | Prints `base` (provenance snapshot) vs `current` (on-disk) for each file. |
|
|
121
121
|
| `list` | Lists installed bundles (`name v<version> scope files installed_at`, plus the shipped version when newer) and the bundles shipped with chi that are not installed (`install <name>` installs one), grouped under their profile; an installed profile shows `includes=` and `left out=`. |
|
|
122
122
|
| `build [--scope system\|project] [--name NAME] [--version VER] [--description DESC] [--out PATH] [FILES...]` | **Inverse of install** — builds a shareable bundle from local memories and installed hooks. Infers `zip` vs `dir`/`tar.gz` from `--out` extension; default `chi_system_memories.zip` (system) or `chi_<repo>_memories.zip` (project, named after the project root) v`1.0.0` in `Dir.pwd`. `FILES...` is an optional allowlist of memory basenames (`identity` or `identity.md`); if omitted, all `*.md` except `index.md`/hidden/non-md and the memories other installed bundles own (each named in a `Left out` line) are included. Installed hooks are copied to `hooks/` with their manifest metadata. Computes `sha256:` checksums and writes `manifest.yml` via `Manifest.write`. No provenance write. |
|
|
@@ -138,7 +138,7 @@ chi bundle install https://github.com/org/bundle.git#v1.2.0 --scope system --for
|
|
|
138
138
|
|
|
139
139
|
# check what would change and upgrade
|
|
140
140
|
chi bundle upgrade ./my-bundle --dry-run
|
|
141
|
-
chi bundle upgrade ./my-bundle #
|
|
141
|
+
chi bundle upgrade ./my-bundle # merges your edits, warns on a real conflict
|
|
142
142
|
|
|
143
143
|
# share again after editing
|
|
144
144
|
chi bundle build --scope system --out updated.zip
|
|
@@ -155,9 +155,9 @@ chi bundle build --scope system --out updated.zip
|
|
|
155
155
|
2. **Prefer project scope** for repo decisions; `system` for cross-project identity/preferences.
|
|
156
156
|
3. **One concept per file:** small files merge and share better than monoliths.
|
|
157
157
|
4. **Use bundles for sharing:** `build` → zip → share → `install`. Do not copy raw `~/.config` paths in docs — give `chi bundle install <url>` instructions.
|
|
158
|
-
5. **Respect local edits:** installs skip existing files by default; use `--force` only when the user explicitly wants overwrite. Upgrades preserve edits (`keep`) or report `conflict
|
|
158
|
+
5. **Respect local edits:** installs skip existing files by default; use `--force` only when the user explicitly wants overwrite. Upgrades preserve edits (`keep`, `merged`) or report `conflict`, leaving the file behind — guide the user to resolve via `memory_read`/`memory_write` or `chi bundle diff <bundle>` + `--force`.
|
|
159
159
|
6. **Keep secrets out:** never write tokens/keys to memories — they are plain files and go into bundles.
|
|
160
160
|
|
|
161
161
|
## Current system bundle
|
|
162
162
|
|
|
163
|
-
`samagotchi-system` (`lib/samagotchi/bundles/system/manifest.yml`) ships `identity.md` + `self_map.md` + `config_modification_protocol.md` + `delegated.md` + this guide itself. It is auto-installed/upgraded on first `Engine` creation (`SystemBundle.ensure!`) — no manual install needed. Its files are managed by chi: a newer chi upgrades them (a
|
|
163
|
+
`samagotchi-system` (`lib/samagotchi/bundles/system/manifest.yml`) ships `identity.md` + `self_map.md` + `config_modification_protocol.md` + `delegated.md` + this guide itself. It is auto-installed/upgraded on first `Engine` creation (`SystemBundle.ensure!`) — no manual install needed. Its files are managed by chi: a newer chi upgrades them (a text merge keeps local edits, and a real conflict leaves the file behind), so put your own preferences in separate memories rather than editing these.
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
`file:line`. Read the source `chi self` reports, not some other checkout on disk.
|
|
84
84
|
- Installed gem files and bundled memories (`identity`, `memory_guide`,
|
|
85
85
|
`config_modification_protocol`, `self_map`, `delegated`) are managed: don't edit them
|
|
86
|
-
(local edits conflict
|
|
86
|
+
(an upgrade merges local edits when it can, and a real conflict leaves the file behind).
|
|
87
87
|
Put notes in my own memories. Guidance for some models only is a model note
|
|
88
88
|
(`model_notes_<name>`, first line `models: <glob>`; memory_guide); a model-only
|
|
89
89
|
addition to one memory is an overlay on it (`current_model_only: true`), and it
|
data/lib/samagotchi/chi_home.rb
CHANGED
|
@@ -6,6 +6,7 @@ require "open3"
|
|
|
6
6
|
require "rbconfig"
|
|
7
7
|
require "time"
|
|
8
8
|
require_relative "atomic_file"
|
|
9
|
+
require_relative "bundler_env"
|
|
9
10
|
require_relative "installed_gem"
|
|
10
11
|
require_relative "log"
|
|
11
12
|
require_relative "paths"
|
|
@@ -33,8 +34,9 @@ module Samagotchi
|
|
|
33
34
|
# Where `mise` usually is when PATH doesn't have it (launchd's bare PATH).
|
|
34
35
|
MISE_DIRS = ["/opt/homebrew/bin", "/usr/local/bin", File.join(Dir.home, ".local", "bin")].freeze
|
|
35
36
|
# What a probe of another Ruby must not inherit: they would move it off
|
|
36
|
-
# its own gems.
|
|
37
|
-
|
|
37
|
+
# its own gems. BUNDLER_SETUP alone does: RubyGems requires what it names at
|
|
38
|
+
# boot, and a bundle with a path then replaces GEM_HOME and GEM_PATH.
|
|
39
|
+
PROBE_UNSET = (%w[GEM_HOME GEM_PATH] + BundlerEnv::CHILD_RUBY).freeze
|
|
38
40
|
PROBE = "require 'json'; print JSON.generate('ruby' => RbConfig.ruby, 'gem_home' => Gem.dir, " \
|
|
39
41
|
"'gem_path' => Gem.path.join(File::PATH_SEPARATOR))"
|
|
40
42
|
GEMSPEC = /\Asamagotchi-(#{Gem::Version::VERSION_PATTERN})\.gemspec\z/
|
data/lib/samagotchi/chi_shim.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "fileutils"
|
|
4
4
|
require "shellwords"
|
|
5
5
|
require_relative "atomic_file"
|
|
6
|
+
require_relative "bundler_env"
|
|
6
7
|
require_relative "chi_home"
|
|
7
8
|
require_relative "log"
|
|
8
9
|
require_relative "paths"
|
|
@@ -25,7 +26,7 @@ module Samagotchi
|
|
|
25
26
|
# ruby: unset before the exec.
|
|
26
27
|
UNSET = "unset RUBYOPT RUBYLIB RUBYGEMS_GEMDEPS GEMRC LD_PRELOAD LD_LIBRARY_PATH DYLD_INSERT_LIBRARIES " \
|
|
27
28
|
"DYLD_LIBRARY_PATH\n" \
|
|
28
|
-
"for v in $(/usr/bin/env | /usr/bin/sed -n 's/^\\(
|
|
29
|
+
"for v in $(/usr/bin/env | /usr/bin/sed -n 's/^\\(#{BundlerEnv::PREFIX}[A-Za-z0-9_]*\\)=.*/\\1/p'); do unset \"$v\"; done\n"
|
|
29
30
|
|
|
30
31
|
module_function
|
|
31
32
|
|
data/lib/samagotchi/child_env.rb
CHANGED
|
@@ -20,16 +20,23 @@ module Samagotchi
|
|
|
20
20
|
# (Tools::Builtins.parent_env): that session's, not a child chi's.
|
|
21
21
|
SESSION_MARKERS = %w[SAMAGOTCHI_PARENT_SESSION SAMAGOTCHI_SESSION_MODEL SAMAGOTCHI_SESSION_THINKING
|
|
22
22
|
SAMAGOTCHI_SESSION_REASONING_HISTORY].freeze
|
|
23
|
+
# "1" in the chi update an automatic run starts (Web::UpdateOps): no person
|
|
24
|
+
# reads its rows. An env marker, so it reaches the chi a hand-over or the home
|
|
25
|
+
# execs, and an older chi that doesn't know it simply ignores it.
|
|
26
|
+
UNATTENDED_UPDATE = "SAMAGOTCHI_UPDATE_UNATTENDED"
|
|
23
27
|
|
|
24
28
|
module_function
|
|
25
29
|
|
|
26
30
|
# The keys of +env+ a child chi must not inherit: the guardrails
|
|
27
31
|
# settings, the session markers, a smoke's fake "newest installed"
|
|
28
|
-
# (InstalledVersions::ENV_KEY, for the spawner's notices only)
|
|
29
|
-
# worker's project env marker and record (a child worker applies its own)
|
|
32
|
+
# (InstalledVersions::ENV_KEY, for the spawner's notices only), the
|
|
33
|
+
# worker's project env marker and record (a child worker applies its own) and
|
|
34
|
+
# an automatic update's marker (a session someone starts from one isn't it; the
|
|
35
|
+
# update's own hand-over execs, which this doesn't touch).
|
|
30
36
|
# @return [Array<String>]
|
|
31
37
|
def spawner_keys(env = ENV)
|
|
32
|
-
env.each_key.select { |key| key.start_with?(GUARDRAILS_PREFIX) } + SESSION_MARKERS +
|
|
38
|
+
env.each_key.select { |key| key.start_with?(GUARDRAILS_PREFIX) } + SESSION_MARKERS +
|
|
39
|
+
[InstalledVersions::ENV_KEY, ProjectEnv::MARKER_ENV, ProjectEnv::ORIG_ENV, UNATTENDED_UPDATE]
|
|
33
40
|
end
|
|
34
41
|
|
|
35
42
|
# #spawner_keys mapped to nil: Process.spawn unsets a key mapped to nil.
|
data/lib/samagotchi/engine.rb
CHANGED
|
@@ -1107,6 +1107,8 @@ module Samagotchi
|
|
|
1107
1107
|
# @raise [ArgumentError] a card without a title, or a bad action
|
|
1108
1108
|
def show_card(source:, title:, body: "", actions: [], level: :info, id: nil)
|
|
1109
1109
|
card = Plugin::Card.build(source: source, title: title, body: body, actions: actions, level: level, id: id)
|
|
1110
|
+
command = Thread.current[running_command_key]
|
|
1111
|
+
card[:command] = command if command
|
|
1110
1112
|
@event_routes.route(card, **EventRoutes::CARD_MARKS)
|
|
1111
1113
|
card[:id]
|
|
1112
1114
|
end
|
|
@@ -1126,6 +1128,20 @@ module Samagotchi
|
|
|
1126
1128
|
Thread.current[key] = outer
|
|
1127
1129
|
end
|
|
1128
1130
|
|
|
1131
|
+
# Run a session command's dispatch: a card shown on this thread says
|
|
1132
|
+
# it's that command's (:card's command:), for a reload that no longer has
|
|
1133
|
+
# the command's line above it (Bridge::CardStore). A thread of its own
|
|
1134
|
+
# isn't the command, nor a card shown after it.
|
|
1135
|
+
# @param name [String] the command, "/short"
|
|
1136
|
+
# @return the block's value
|
|
1137
|
+
def running_command(name)
|
|
1138
|
+
outer = Thread.current[running_command_key]
|
|
1139
|
+
Thread.current[running_command_key] = name
|
|
1140
|
+
yield
|
|
1141
|
+
ensure
|
|
1142
|
+
Thread.current[running_command_key] = outer
|
|
1143
|
+
end
|
|
1144
|
+
|
|
1129
1145
|
# Whether an anytime command (/btw) still runs on its thread.
|
|
1130
1146
|
def anytime_running?
|
|
1131
1147
|
@lifecycle_mutex.synchronize { @anytime_threads.any?(&:alive?) }
|
|
@@ -2266,7 +2282,7 @@ module Samagotchi
|
|
|
2266
2282
|
|
|
2267
2283
|
messages = @session&.messages || []
|
|
2268
2284
|
covered = state[:covered].to_i
|
|
2269
|
-
turns_since = Array(messages).drop(covered).count { |m| Steer.
|
|
2285
|
+
turns_since = Array(messages).drop(covered).count { |m| Steer.turn_opening?(m) }
|
|
2270
2286
|
{ text: state[:text], covered: covered, turns_since: turns_since, created_at: state[:created_at] }
|
|
2271
2287
|
end
|
|
2272
2288
|
|
|
@@ -2974,6 +2990,8 @@ module Samagotchi
|
|
|
2974
2990
|
|
|
2975
2991
|
private
|
|
2976
2992
|
|
|
2993
|
+
def running_command_key = :"samagotchi_command_#{object_id}"
|
|
2994
|
+
|
|
2977
2995
|
# The one named seam for replacing a session's conversation (the
|
|
2978
2996
|
# array is replaced, never mutated in place). No lock: a reader gets
|
|
2979
2997
|
# the old array or the new one, and both are whole.
|