samagotchi 0.157.0 → 0.158.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.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -1
  3. data/README.md +1 -1
  4. data/bin/chi +3 -1
  5. data/docs/architecture.md +7 -6
  6. data/docs/bundle-authoring.md +5 -5
  7. data/docs/bundles.md +2 -1
  8. data/docs/cli.md +21 -17
  9. data/docs/configuration.md +7 -5
  10. data/docs/context.md +5 -3
  11. data/docs/guardrails.md +8 -7
  12. data/docs/hooks.md +44 -80
  13. data/docs/internals/bridge.md +1 -1
  14. data/docs/internals/bundles.md +47 -34
  15. data/docs/internals/engine.md +7 -5
  16. data/docs/memory.md +39 -9
  17. data/docs/plugins.md +13 -9
  18. data/docs/releasing.md +6 -5
  19. data/docs/sessions.md +5 -5
  20. data/docs/testing.md +4 -4
  21. data/lib/samagotchi/bridge/card_places.rb +5 -13
  22. data/lib/samagotchi/bridge/turn_accumulator.rb +4 -1
  23. data/lib/samagotchi/bridge/turn_drawings.rb +7 -1
  24. data/lib/samagotchi/bundle_command/check.rb +23 -10
  25. data/lib/samagotchi/bundle_command/installed_check.rb +13 -2
  26. data/lib/samagotchi/bundle_command/try/run.rb +4 -1
  27. data/lib/samagotchi/bundle_command.rb +15 -43
  28. data/lib/samagotchi/bundles/system/config_modification_protocol.md +1 -1
  29. data/lib/samagotchi/bundles/system/manifest.yml +4 -4
  30. data/lib/samagotchi/bundles/system/memory_guide.md +6 -6
  31. data/lib/samagotchi/bundles/system/self_map.md +1 -1
  32. data/lib/samagotchi/context_command.rb +7 -5
  33. data/lib/samagotchi/context_sources.rb +4 -2
  34. data/lib/samagotchi/engine.rb +1 -6
  35. data/lib/samagotchi/extension_load.rb +20 -41
  36. data/lib/samagotchi/guardrails/load_failures.rb +3 -2
  37. data/lib/samagotchi/guardrails/path_resolve.rb +11 -0
  38. data/lib/samagotchi/guardrails/review_paths.rb +1 -5
  39. data/lib/samagotchi/hook_routes.rb +2 -2
  40. data/lib/samagotchi/hooks.rb +2 -3
  41. data/lib/samagotchi/memory_bundle/asset_installer.rb +39 -119
  42. data/lib/samagotchi/memory_bundle/builder.rb +10 -34
  43. data/lib/samagotchi/memory_bundle/bundle_hook.rb +6 -31
  44. data/lib/samagotchi/memory_bundle/installed_bundle.rb +1 -1
  45. data/lib/samagotchi/memory_bundle/installed_copy.rb +11 -18
  46. data/lib/samagotchi/memory_bundle/installed_manifest.rb +2 -16
  47. data/lib/samagotchi/memory_bundle/installer.rb +32 -14
  48. data/lib/samagotchi/memory_bundle/manifest.rb +37 -54
  49. data/lib/samagotchi/memory_bundle/provenance.rb +11 -51
  50. data/lib/samagotchi/memory_bundle/remerge.rb +7 -3
  51. data/lib/samagotchi/memory_bundle/shas.rb +5 -8
  52. data/lib/samagotchi/memory_bundle/shipped_update.rb +30 -35
  53. data/lib/samagotchi/memory_bundle/status.rb +12 -10
  54. data/lib/samagotchi/memory_bundle/system_bundle.rb +16 -5
  55. data/lib/samagotchi/memory_bundle/trash.rb +7 -12
  56. data/lib/samagotchi/memory_orphans.rb +4 -4
  57. data/lib/samagotchi/memory_paths.rb +102 -13
  58. data/lib/samagotchi/plugin/api.rb +1 -4
  59. data/lib/samagotchi/plugin/loader.rb +42 -5
  60. data/lib/samagotchi/project_command.rb +167 -0
  61. data/lib/samagotchi/project_init.rb +69 -0
  62. data/lib/samagotchi/project_scope.rb +6 -4
  63. data/lib/samagotchi/prompt_files.rb +10 -7
  64. data/lib/samagotchi/recap_place.rb +3 -4
  65. data/lib/samagotchi/run_options.rb +1 -0
  66. data/lib/samagotchi/session.rb +44 -18
  67. data/lib/samagotchi/session_access/contract.rb +1 -1
  68. data/lib/samagotchi/session_field.rb +5 -4
  69. data/lib/samagotchi/steer.rb +10 -0
  70. data/lib/samagotchi/system_prompt.rb +20 -11
  71. data/lib/samagotchi/terminal_ui/join_replay.rb +6 -1
  72. data/lib/samagotchi/testing/bundle.rb +5 -12
  73. data/lib/samagotchi/testing/install.rb +5 -5
  74. data/lib/samagotchi/testing/recorder.rb +1 -1
  75. data/lib/samagotchi/testing.rb +1 -1
  76. data/lib/samagotchi/tool_declarations.rb +1 -1
  77. data/lib/samagotchi/toolkit_binding.rb +1 -4
  78. data/lib/samagotchi/toolkit_command.rb +1 -4
  79. data/lib/samagotchi/tools/delegate.rb +12 -8
  80. data/lib/samagotchi/version.rb +1 -1
  81. data/lib/samagotchi/web/app.rb +35 -0
  82. data/lib/samagotchi/web/project_list.rb +3 -2
  83. data/lib/samagotchi/web/public/index.html +6 -1
  84. data/lib/samagotchi/web/public/project_picker.js +86 -4
  85. metadata +3 -2
  86. data/lib/samagotchi/hooks/bundle_loader.rb +0 -191
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e6e6add2f9511492180ba39e11ebbcd9da6a703e5325facfc9ddc24bd22bc2eb
4
- data.tar.gz: e426d754c3306c361fe54d1fde2475502e3121b2ab1a5caecad92712165056e1
3
+ metadata.gz: 7be08e98d08c8d5fae428eee5ca7a1439c44cda4354693cc542e712cd4aa9ce2
4
+ data.tar.gz: '09a1d00545460e98a25a4076314b1202822ad7ab71377e17d5917d8a05634fa0'
5
5
  SHA512:
6
- metadata.gz: a0026bf97c1ef84a78f181283c712490f1961972d49c10e2559fe29d2047d599223837c328a96b15aa382208d47f5d78a723842b575a624bfa992b71130a6082
7
- data.tar.gz: 0f6e09c9abd81b9e218677b47373215a91788b735fb7c515d4ea72b563d9875ea836438a4b1b019ddf4d480418954363dcbe0bd01b23a545a59a864484f6f437
6
+ metadata.gz: '0149b416ea8e87e57e95c1cbe32a313a18a9735c50655f74d59531c41ab3b7785c2d8a3a2a41b458fab6308e802496e381d9b2f2ad062105b683608c603c9b67'
7
+ data.tar.gz: d01cee2cdcfafdc1bb0f6f88e9b99ddbd85a72bda7c096b2e23ab67eeac68857836ab4bd557b4dc98397634be7b0495f17230e473ccd4695d1f189fd4da714df
data/CHANGELOG.md CHANGED
@@ -8,6 +8,31 @@ and commands may change between minor versions. How releases are made:
8
8
 
9
9
  ## [Unreleased]
10
10
 
11
+ ## [0.158.0] - 2026-11-08
12
+
13
+ ### Added
14
+
15
+ - A folder in no git repository can be a chi project: `chi project init [DIR]` writes `DIR/.chi/project.yml`, and DIR and every subfolder share one project memories folder, group their sessions (`chi sessions list`, chi web's projects) and take `chi context --project`. `chi project` shows which project a folder is in. A git repository stays the project wherever there is one (init writes nothing there), and `$HOME` or a folder above it can't be marked. A folder chi already ran in at its top keeps its memories. A session in a subfolder of a marked project reads the AGENTS.md chain from the project's top, and `delegate`'s `cwd:` takes any folder of it (not a nested repository or marked project); sessions already running in a folder when it is marked keep the old project until `chi sessions restart`.
16
+ - chi web's project picker has a New project… row: type a folder path, and chi creates the folder if needed and marks it as a project, as `chi project init` does (a folder inside a git repository opens that repository's project).
17
+
18
+ ### Removed
19
+
20
+ - Bundles can no longer ship class-file hooks (`hooks/*.rb` and the manifest's `hooks:`): a bundle hooks events from its `plugin.rb` with `chi.on` (docs/plugins.md). An installed bundle that still has them keeps its memories, rules, plugin and scripts, and chi says at the first turn that its hooks no longer load (`chi bundle status` and `chi update` say so too); installing or upgrading it is refused until they move. An older install's `on_error: fail_closed` class-file hook (chi-nah 0.1.x) stops guarding: move it to `chi.on(…, on_error: :fail_closed)` with `plugin: required: true` (docs/plugins.md). Your config's own `hooks:` are unchanged.
21
+ - The next install or upgrade of such a bundle from a source without hooks removes its old `hooks/` (an edited hook is copied to the trash first; `--dry-run` lists the hooks under "Would remove", and `chi update`'s plan names an edited one), and `chi bundle uninstall` still removes them. An external bundle whose source still ships hooks stays on the first-turn warning until its source moves them or you uninstall it. `chi bundle build` no longer copies an installed bundle's hooks, and `chi bundle check` reports a source's hooks as `hooks-retired`.
22
+ - The test kit's `Testing::Bundle#hooks_namespace` (the module a bundle's hook files were loaded in); `Testing::Install::Result#hook_failures` and `Registered#hooks` stay, filled from the plugin and config hooks.
23
+
24
+ ### Fixed
25
+
26
+ - `chi bundle install --dev` no longer warns of a checksum mismatch for the bundle's memories (stale shas while you edit); `chi bundle check` still flags them.
27
+ - `chi update --dry-run` now shows the same memory checksum warnings as the update itself.
28
+ - A conflicted memory that chi retries at the same version (at start, or in `chi update`) now warns when the bundle's file doesn't match its manifest's sha256, as an install or an upgrade does.
29
+ - A session whose first message, last prompt or title held bytes that aren't valid UTF-8 (pasted binary, a broken terminal) no longer fails to save; the bad bytes are saved as `?`.
30
+ - A damaged turn-drawings file no longer stops a terminal from joining a session or chi web from showing it; the turn is drawn from the saved messages instead.
31
+ - `chi bundle try --json` no longer crashes when the answer or the trace holds non-ASCII text.
32
+ - `chi bundle upgrade` (and `chi update`) list a plugin or script that the new version adds under Installed, not Updated.
33
+
34
+ Update with `chi update`.
35
+
11
36
  ## [0.157.0] - 2026-11-08
12
37
 
13
38
  ### Added
@@ -5840,7 +5865,8 @@ and long-lived sessions.
5840
5865
  - A macOS desktop helper (`chi desktop install`): a "Send to chi" Service and a
5841
5866
  hotkey panel that send selected text or the clipboard to your sessions.
5842
5867
 
5843
- [Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.157.0...HEAD
5868
+ [Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.158.0...HEAD
5869
+ [0.158.0]: https://github.com/dm1try/samagotchi/compare/v0.157.0...v0.158.0
5844
5870
  [0.157.0]: https://github.com/dm1try/samagotchi/compare/v0.156.0...v0.157.0
5845
5871
  [0.156.0]: https://github.com/dm1try/samagotchi/compare/v0.155.0...v0.156.0
5846
5872
  [0.155.0]: https://github.com/dm1try/samagotchi/compare/v0.154.1...v0.155.0
data/README.md CHANGED
@@ -217,7 +217,7 @@ once, for the session, for the repo, or for the whole rule in the repo.
217
217
  - [chi as a sub-agent](docs/sub-agent.md): `chi send --wait --format json`, `chi answer`, instructions for a parent agent
218
218
  - [Desktop helper](docs/desktop.md): `chi desktop`, the macOS "Send to chi" Service and hotkey
219
219
  - [Guardrails](docs/guardrails.md): allow / ask / deny for tool calls, rules, approvals
220
- - [Hooks](docs/hooks.md): plugin hooks and bundle hooks
220
+ - [Hooks](docs/hooks.md): config hooks and a bundle plugin's hooks
221
221
  - [Plugins](docs/plugins.md): bundle plugins (commands, tools, hooks)
222
222
  - [Bundles](docs/bundles.md): what each shipped bundle does (btw, mcp, cost-cap, claude-peer, skills…), its commands and settings
223
223
  - [Writing a bundle](docs/bundle-authoring.md): `chi bundle new`, `test`, `check`, `try`, `install --dev`
data/bin/chi CHANGED
@@ -102,7 +102,9 @@ SUBCOMMANDS = {
102
102
  # other machines' chi web, for chi --remote NAME (add, ls, rm)
103
103
  "remote" => ["samagotchi/remote_command", "RemoteCommand"],
104
104
  # the chi home: the Ruby every chi launch boots (set, doctor)
105
- "home" => ["samagotchi/home_command", "HomeCommand"]
105
+ "home" => ["samagotchi/home_command", "HomeCommand"],
106
+ # this folder's project; init marks a folder in no git repository (.chi/project.yml)
107
+ "project" => ["samagotchi/project_command", "ProjectCommand"]
106
108
  }.freeze
107
109
  # chi --remote NAME <command> (or CHI_REMOTE=NAME): the session commands
108
110
  # against another machine's chi web; what runs here only is refused (exit 2).
data/docs/architecture.md CHANGED
@@ -51,7 +51,7 @@ A compact visual overview of the current architecture, then the core API in pros
51
51
  ▼
52
52
  ┌──────────────────────────────────────────────────────────────────────────────────┐
53
53
  │ Persistence │
54
- │ Project: ~/.config/samagotchi/memories/projects/<repo>_<hash>/ (per git repo) │
54
+ │ Project: ~/.config/samagotchi/memories/projects/<repo>_<hash>/ (per project) │
55
55
  │ System: ~/.config/samagotchi/memories/ │
56
56
  └──────────────────────────────────────────────────────────────────────────────────┘
57
57
  ```
@@ -104,7 +104,7 @@ terminal renders the events it gets over the Bridge.
104
104
  | Turn | `TurnContext`, `TurnState`, `Engine::Turn` | One turn's frozen `TurnContext` (id, session, origin, author, participant mode, controller, folders, git facts, settings, model key/name and the tool surface (`Tools::ToolSurface`) from `generate`): built by `Engine#begin_turn`, published with the running flag by `TurnState` (the only way another thread reads the running turn: `Engine#turn_context` / `#last_turn_context`), handed explicitly down the tool path (`ModelBackend#complete(context:)`, `ToolRunner#run`, `Guardrails::Gate#evaluate`, `KernelLoop#dispatch_tool_call`). `Engine::Turn` keeps only the lifecycle state ([internals/engine.md](internals/engine.md), "Turn and TurnContext"). The turn stages (`TurnStages::ToolkitSuggestion`, `TurnStages::ContextJudge`, `TurnStages::Objectives`, `TurnStages::Warmup`, `TurnStages::VerifyAnchors`, `lib/samagotchi/turn_stages/`): the steps that keep state across the turn, built from the Engine-lifetime objects and called by name from the turn's skeleton with the values of the call (a `SystemOne::Recorder`, the sessions dir), not callbacks into the Engine (internals/engine.md, "Turn stages"). |
105
105
  | Adapters | `Samagotchi::Client`, `LLM::OpenAIChat`, `LLM::AnthropicMessages`, `LLM::HTTP` | Raw-prompt servers, the OpenAI chat API, Anthropic's Messages API, and the HTTP they share. |
106
106
  | Tools | `lib/samagotchi/tools/*` | Execute, read, edit, write, memory, task_*, web_fetch, delegate/delegate_result, ask_user_question, reminders, send_note, list_sessions, context_read, objectives, forget_outputs, find_tools/call_tool (the tool index), plus runtime/output-guardrails. |
107
- | Bundles | `lib/samagotchi/memory_bundle/*`, `BundleCommand` | `chi bundle`: install/upgrade (3-way, with a text merge of edited memories), build, uninstall; the install record under `.bundles/<name>/` that the hook, guardrail and plugin loaders read ([internals/bundles.md](internals/bundles.md)). |
107
+ | Bundles | `lib/samagotchi/memory_bundle/*`, `BundleCommand` | `chi bundle`: install/upgrade (3-way, with a text merge of edited memories), build, uninstall; the install record under `.bundles/<name>/` that the guardrail and plugin loaders read ([internals/bundles.md](internals/bundles.md)). |
108
108
  | Background | `Samagotchi::Worker`, `Samagotchi::SessionManager` | `SessionManager` (`session_manager.rb`, its parts in `session_manager/`: spawn · worker_command · listing · delivery · lifecycle · removal · emptiness) forks/spawns one worker process per session. The worker process's main (owner lock, detach, signals, project env, what it does as it leaves) is `WorkerBoot` (`worker_boot.rb`), which SessionManager loads only as a worker boots (its `run_session_loop` forwarder); its `Worker` builds the `Engine` and `Bridge` (no terminal rendering) and runs queued turns. The spawned process (own process group) forks the worker, which `setsid`s and is reparented to init once its starter exits (it waits until the worker holds `owner.lock`, so the spawner's `Process.detach` waiter tells a starting worker): nothing that walks the spawner's process tree reaches it (`SAMAGOTCHI_WORKER_DETACH=1`, `detach_from_spawner`). The lock's pid is the worker's; `spawn`'s `child_pid` is its starter's. |
109
109
  | Web | `Samagotchi::Web::App`, `Samagotchi::Web::Server`, `Samagotchi::Web::SessionHub` | Rack+WEBrick on one port, `127.0.0.1:4567` (index.html + `/api/*` + SSE); with `web.host: lan` also on this machine's LAN address, behind an access token (`Web::Lan`, `Web::Token`). The hub is chi web's projection of the session list, pushed to every tab over `GET /api/events`. |
110
110
  | Sessions | `Samagotchi::Session`, `SessionManager` | File `sessions/<uuid>.json` + sidecar `input/`/`output/`; retention 14d/500, `updated_at desc`, lazy sweep. |
@@ -420,7 +420,7 @@ project (the list, the events stream and a new chat follow it), `?scope=all` eve
420
420
  `data-scope-dir`, which the page reads when the URL has no `?dir`), else every session. The page never
421
421
  redirects, so the `?token=` trade's 303, a link code's `#code=` and the Home Screen app's `start_url` `/`
422
422
  all land on the start project; a push opens its session's own project (`/?dir=<root>#/s/<id>`, or
423
- `/?scope=all#/s/<id>` outside a repo). `?dir=` beats `?scope=all`. A session created
423
+ `/?scope=all#/s/<id>` outside a project: no repository or marked folder). `?dir=` beats `?scope=all`. A session created
424
424
  without a folder starts in the start folder.
425
425
  Files stay the source of truth and workers don't know the hub. Its watcher is a 1 s tick that
426
426
  stats the sessions dir (every session writer goes tmp + rename, which bumps the dir's mtime) and
@@ -459,7 +459,8 @@ model: [internals/remote-sessions.md](internals/remote-sessions.md).
459
459
  |---|---|---|---|---|
460
460
  | `GET /api/sessions/resolve?id=` | owner | an id or a unique prefix | 200 `{id}` | 404 `not_found`; 400 `ambiguous_id` with `ids` |
461
461
  | `GET /api/sessions/summaries` | owner | `dir` (a folder: its project; none: every project), `cwd`, `live=1`, `archived=1`, `tests=0`, `sort`, `order`, `limit` | 200: the rows `chi sessions list` prints (`SessionRow#to_summary_h`, 28 keys in a fixed order: `status`, `live`, `busy`, `owner`, `recap`, `ctx_pct`, `waiting`, `waiting_id`, `title`, `preview`, …) | 400 `invalid_dir`, `invalid_cwd` |
462
- | `GET /api/projects?dir=` | owner | optional page-scope folder | 200: project rows (`Web::ProjectList`: the hub's projects plus the start project) with counts, roots, default/current marks; no `dir`: no current project | 400 `invalid_dir`; 503 `no_hub` |
462
+ | `GET /api/projects?dir=` | owner | optional page-scope folder | 200: project rows (`Web::ProjectList`: the hub's projects plus the start project and current project, even without sessions) with counts, roots, default/current marks; no `dir`: no current project | 400 `invalid_dir`; 503 `no_hub` |
463
+ | `POST /api/projects` | owner | `{dir}` (absolute folder; `~` and `~/` expand against the server's home) | 200 `{outcome, root, dir}` (`created`, `already_marked`, or `git`) | 400 `invalid_json`, `invalid_dir`; 422 `above_home`, `write_failed` |
463
464
  | `POST /api/sessions` (more fields) | owner | `toolkit`, `tools`, `no_tools`, `parent_answers`; `client_id` (`cli:send`/`cli:answer`: the first line is checked as chi send checks it); `check_vision: true` (images follow) | 201 adds `model_warning`, `parent_answers_warning` (a level looser than config.yml's, capped) and `first_turn_id` (the id the prompt it starts with gets for its turn; null for an `idle` create) | 400 `invalid_toolkit`, `invalid_parent_answers`, `invalid_continues` (beside `continues`); 403 `parent_answers_refused`, `parent_continue_refused`; 422 `no_vision` |
464
465
  | `POST /api/sessions/:id/turn` (more fields) | owner | `client_id` as above; `check_vision: true` with `images` | as before | 403 `parent_answers_refused`, `parent_continue_refused`; 422 `no_vision` |
465
466
  | `POST /api/sessions/:id/wait/prepare` | owner | `question_id` (absent: the pending one; `null`: none), `turn`, `created`, `busy` (+ `queue`) | 200 `{session_id, cursor, baseline: {messages, question_id, last_turn}, own_turn, known_turn, running, live, settled, last_turn_id}` (`WaitStart`) | 409 `busy`, `waiting` (`SendCommand::Busy`'s words, and its fields: `question`, `parent_strategy`) |
@@ -618,9 +619,9 @@ A map of the parts the overview above doesn't name (paths under `lib/samagotchi/
618
619
  | Worker | `worker.rb` (`worker_boot.rb`, `worker_idle_exit.rb`, `worker_inbound.rb`, `worker_signals.rb`, `worker_wakes.rb`, `worker_task_wakes.rb`, `task_watch.rb`) | A session's background process: builds the `Engine` and `Bridge`, runs queued turns and the wake turns nobody typed (delegate reports, a background task that finished unseen: [Finish wake](internals/background-tasks.md#finish-wake), attached context), exits when idle. A spawned one traps TERM, HUP and INT (`WorkerSignals`, installed by `WorkerBoot.run_session_loop` when `SAMAGOTCHI_WORKER_PROCESS=1`): the running turn stops as a Stop with reason `signal`, is saved, and the worker leaves (`:signal`, exit 0; a new worker only for input already queued). A running turn is checkpointed at its iteration boundaries (`TurnCheckpoint`, the `interrupted_turn` mark), and the next worker settles a turn its predecessor died in (`TurnCheckpoint.settle`). The session is saved from the turn's thread, Bridge threads and a signal's thread; `Session#save` takes a per-session lock around its snapshot and write, so an older snapshot never lands over a newer one. |
619
620
  | Config | `config.rb`, `config/restart_watch.rb` | Every setting as an `Entry`: env, config.yml and CLI layers ([configuration.md](configuration.md)); the notice a worker or chi web gets when config.yml changes a setting it reads only at its start. With `log.rb` it is the bottom layer: nothing it references reaches back to it, so `hosts:` and `models:` entries parse next to their types (`HostConfig.all`, `ModelSettings.all`). |
620
621
  | Paths | `paths.rb` (`Paths`) | The XDG base dirs chi's files live under, read from the env on every call: `Paths.state_home` (`$XDG_STATE_HOME`, else `~/.local/state`) and `Paths.config_home` (`$XDG_CONFIG_HOME`, else `~/.config`), both defaults under `Dir.home`; a blank variable counts as unset, a relative one is made absolute from the current directory. `Paths.tilde` shows a path with `~` for the home folder. `SessionManager` hands a spawned worker the spawner's `XDG_STATE_HOME` / `XDG_CONFIG_HOME`, when set, made absolute, so a relative one isn't read from the session's folder. |
621
- | MemoryBundle | `memory_bundle.rb`, `memory_bundle/{installer,builder,installed_bundle,plugin_ref,bundle_hook}.rb` (`Installer`, `Builder`, `InstalledBundle`, `PluginRef`, `BundleHook`) | Installing, building and reading bundles of memories, hooks and plugins. |
622
+ | MemoryBundle | `memory_bundle.rb`, `memory_bundle/{installer,builder,installed_bundle,plugin_ref,bundle_hook}.rb` (`Installer`, `Builder`, `InstalledBundle`, `PluginRef`, `BundleHook`) | Installing, building and reading bundles of memories, rules, plugins and scripts (`BundleHook`: an older record's retired class-file hook). |
622
623
  | Bundle authoring | `bundle_command.rb` (`BundleCommand`), `bundle_command/{scaffold,check,test_run}.rb`, `bundle_command/try/{run,sandbox,trace}.rb`, `testing.rb` + `testing/` (the test kit) | `chi bundle new`, `check`, `test` and `try`, and the kit a bundle's tests load it with ([bundle-authoring.md](bundle-authoring.md), [internals/bundles.md](internals/bundles.md)). |
623
- | Session file | `session.rb` (`Session`, `Session::FIELDS`), `session_field.rb` (`SessionField`), `session_paths.rb` (`SessionPaths`), `session_setup.rb` (`SessionSetup`) | A session's `<uuid>.json`: one `SessionField` row per key (default, cast, what the file gets; the codec on its type), from which `Session.new`, `.from_h` and `#to_h` are built; `SessionPaths` is where the file and the session's dir are and the id rule, a leaf `Session` extends (so `ArchiveStore`, `ContextSources` and `ParticipantMode` name no `Session`); `SessionSetup` the settings a fork and a continue carry, applied by member name. See [sessions.md](sessions.md#session-file). |
624
+ | Session file | `session.rb` (`Session`, `Session::FIELDS`), `session_field.rb` (`SessionField`), `session_paths.rb` (`SessionPaths`), `session_setup.rb` (`SessionSetup`) | A session's `<uuid>.json`: one `SessionField` row per key (default, cast, what the file gets; the codec on its type), from which `Session.new`, `.from_h` and `#to_h` are built; `SessionPaths` is where the file and the session's dir are and the id rule, a leaf `Session` extends (so `ArchiveStore`, `ContextSources` and `ParticipantMode` name no `Session` constant; the first two still require session.rb at load, `ParticipantMode` lazily); `SessionSetup` the settings a fork and a continue carry, applied by member name. See [sessions.md](sessions.md#session-file). |
624
625
  | Session config (`--set`) | `session_config.rb` (`SessionConfig`, `SessionConfig::Settings`) | A session's own settings as one value (`Session#settings`, `SessionSetup#settings`): `core`, the `--set KEY=VALUE` values of the core keys marked `session: true` (`Config::Entry`), typed by `Config.coerce!` and read first through `Config.get(key, session:)` (`RecapSetup`; `Engine#session=` rebuilds the recap job, `IdleScheduler#replace_job`), file key `config_settings`; `bundles`, the `--set BUNDLE.KEY=VALUE` values, checked against the manifest's `session_settings:`, laid over `bundles: <name>:` in `ctx.settings` ([plugins.md](plugins.md)), file key `bundle_settings`. `SessionConfig.clash` refuses a bundle setting named like a core one (install, `chi bundle check`). See [configuration.md](configuration.md#per-session-settings). |
625
626
  | Plugin API | `plugin/api.rb` (`Plugin::Api`), `plugin/tool_spec.rb` (`Plugin::ToolSpec`: a checked tool declaration, how it is registered), `plugin/loader.rb`, `plugin/context.rb` (`ctx`), `plugin/http.rb` + `plugin/http_transport.rb` (`ctx.http`), `plugin/card.rb` (`Plugin::Card`) | What a bundle's plugin registers: tools, commands, annotation actions, hooks, services, toolkits, peer sources; what its handlers get as `ctx` ([plugins.md](plugins.md)). |
626
627
  | Peer bundles' base | `plugin/peer.rb` (`Plugin::Peer`) | What claude-peer and codex-peer share (the send tool, the command, the cards, payloads); each bundle's subclass sets its strings and transport ([bundles.md](bundles.md#the-claude-peer-bundle)). |
@@ -24,7 +24,7 @@ Dev-linked scripts aren't chmodded: chi warns with a `chmod +x PATH/scripts/x` h
24
24
 
25
25
  `check` prints one finding per line (`error|warn|info CODE file[:line]: message (fix: hint)`), exits 1 on any error
26
26
  and installs into a temp sandbox, not the user's config or state; run it after every edit. `check` and `test` run
27
- the bundle's plugin and hooks in their own process, as written: run them on code you'd install.
27
+ the bundle's plugin in their own process, as written: run them on code you'd install.
28
28
 
29
29
  ## Trying a bundle
30
30
 
@@ -51,8 +51,6 @@ plugin:
51
51
  sha256: sha256:<hex> # `chi bundle check --fix` fills it; never compute it by hand
52
52
  # files: # memories it installs (top-level *.md); without files: every *.md but README & co.
53
53
  # my_notes.md: sha256:<hex>
54
- # hooks: # hooks/*.rb files (a plugin's chi.on is usually simpler), each with event: NAME
55
- # # or events: [a, b] (one instance for both; chi newer than 0.97.0)
56
54
  # needs: [gh] # outside commands it relies on
57
55
  ```
58
56
 
@@ -90,7 +88,8 @@ empty), then `exit: N` (`exit: 0 (no output)` when both were empty); a non-zero
90
88
  Act through `ctx`: `ctx.notify(text, level: :warn)`, `ctx.steer(text)` (a line the model reads before its next
91
89
  step), `ctx.card(…)`, `ctx.stop_turn(reason)`, `ctx.ask_user(…)`, `ctx.data_dir` (the bundle's state folder),
92
90
  `ctx.session_dir` (this session's, removed with it), `ctx.cwd`, `ctx.log`. In a test, each recorded notice, steer or stop names its hook as `"plugin.rb (bundle
93
- my-bundle)"`, the plugin's label (a `hooks/x.rb` file's is `"x.rb (bundle my-bundle)"`).
91
+ my-bundle)"`, the plugin's label. A bundle hooks events from its plugin only: class-file hooks (`hooks/*.rb`, a
92
+ manifest `hooks:`) are retired, and chi refuses a source that has them ([hooks.md](hooks.md#class-file-hooks-are-retired)).
94
93
 
95
94
  ## Testing it
96
95
 
@@ -126,8 +125,9 @@ class, a hand-built `ctx`), that is a gap in the kit: say so to the user rather
126
125
 
127
126
  ## Pitfalls
128
127
 
128
+ - **Sandbox and ENV.** A standalone `Testing::Bundle` snapshots `ENV` when built and restores it at shutdown; see [Testing a bundle](plugins.md#testing-a-bundle-experimental).
129
129
  - **Shas.** Every file the manifest names has a sha256 line, and an edit makes it stale: the install then warns
130
- of a checksum mismatch, and a line with no sha installs unchecked. `chi bundle check --fix` rewrites them; run it
130
+ of a checksum mismatch (not for `install --dev`; `chi bundle check` does), and a line with no sha installs unchecked. `chi bundle check --fix` rewrites them; run it
131
131
  after the last edit.
132
132
  - **requires_chi.** Use the chi the user runs (`chi --version`): a bundle whose `requires_chi` that chi doesn't
133
133
  meet is now **refused at install** (`--force` is the only way past it), not just unloaded. `check` warns when the plugin calls something newer than the floor (`ctx.http` 0.80.0, `ctx.cost` 0.83.0, `ctx.usage` 0.100.0, `ctx.sessions.children` 0.34.0),
data/docs/bundles.md CHANGED
@@ -6,7 +6,8 @@ the plugin API ([plugins.md](plugins.md)); install, upgrade and the `chi bundle`
6
6
  feature they belong to: **source-links** in [hooks.md](hooks.md#the-source-links-bundle) and **known-names** in
7
7
  [guardrails.md](guardrails.md#the-known-names-bundle). Both hook events from their `plugin.rb` with `chi.on`
8
8
  (known-names on `before_tool_call`, priority 50; source-links on `after_turn` and `question_asked`, priority 90), as
9
- every shipped bundle with code does; before known-names 0.2.0 and source-links 0.7.0 they shipped a `hooks/*.rb` file.
9
+ every shipped bundle with code does; before known-names 0.2.0 and source-links 0.7.0 they shipped a `hooks/*.rb` file
10
+ (chi no longer loads one: [hooks.md](hooks.md#class-file-hooks-are-retired); `chi update` removes the old file).
10
11
 
11
12
  ## The btw bundle
12
13
 
data/docs/cli.md CHANGED
@@ -10,9 +10,9 @@
10
10
  - `chi scratch [options]` — a one-time session in this folder, attached like plain `chi`, that leaves nothing behind (see [Scratch sessions](#scratch-sessions))
11
11
  - `chi --attach <session-id>` — attach the terminal to a session's worker (e.g. one started from the Web UI), waking one if it has exited
12
12
  - A session id can be shortened to any unique prefix (like git): `chi --attach 2ea8`. `--resume`, `--attach`, `sessions stop`, `sessions archive` and `sessions delete` take one; an ambiguous prefix lists the sessions it matches.
13
- - `chi web [--port 4567] [--open] [--scope=all] [--start-dir DIR]` — start the Web UI (single localhost port session control plane) on this git project's sessions (`--scope=all`, or a folder in no repo: `/?scope=all`, every session); if a chi web already runs on the port, print (with `--open`, open) its page for this folder and exit. Something else on the port (an older chi web too) exits 1 with "port N is in use"
13
+ - `chi web [--port 4567] [--open] [--scope=all] [--start-dir DIR]` — start the Web UI (single localhost port session control plane) on this project's sessions (a git repository or a `chi project init` folder; `--scope=all`, or a folder in neither: `/?scope=all`, every session); if a chi web already runs on the port, print (with `--open`, open) its page for this folder and exit. Something else on the port (an older chi web too) exits 1 with "port N is in use"
14
14
  - `chi web --web-host lan` — the Web UI on your home network too, for your phone: a link with an access token and its QR code (see [chi web on your phone](#chi-web-on-your-phone)); `chi web --new-token` replaces the token
15
- - `chi web --start-dir DIR` (config `web.start_dir`) — the folder chi web starts in, in place of the one you run it in (`~` and a relative path work; chi web never changes its own folder): its page opens on that folder's project (the printed link, a bare `/`, the LAN link and its QR code, the Home Screen app), and a new session from the all view starts there; the header's project chip switches projects. One that isn't a folder exits 1; `--start-dir` without `web` exits 2. A chi web already running in another folder keeps it (the second one says so: restart it to change it)
15
+ - `chi web --start-dir DIR` (config `web.start_dir`) — the folder chi web starts in, in place of the one you run it in (`~` and a relative path work; chi web never changes its own folder): its page opens on that folder's project (the printed link, a bare `/`, the LAN link and its QR code, the Home Screen app), and a new session from the all view starts there; the header's project chip switches projects, and its picker has a `New project…` row for creating a project folder (a path inside a git repository opens that repository's project and the folder is left as it is, nothing marked). One that isn't a folder exits 1; `--start-dir` without `web` exits 2. A chi web already running in another folder keeps it (the second one says so: restart it to change it)
16
16
  - `chi web --tunnel tailscale` — the Web UI over https on your tailnet, for your phone anywhere: Tailscale Serve (never Funnel) on this machine's `<machine>.<tailnet>.ts.net` name for this run, the owner your Tailscale login, turned off on exit (see [Tailscale Serve](configuration.md#tailscale-serve-your-tailnet)); `--tunnel` without `web` exits 2
17
17
  - `chi web push ls [--format json]`, `chi web push rm ID`, `chi web push test [ID]` — the phones chi web pushes to (see [Phone notifications](configuration.md#phone-notifications-web-push)): list them (short id, label, origin, added, last delivery, done turns, last error; `stale` for one made on another public URL than chi web's now: never sent to), remove one, or send a test notification to ID or to every device not stale. chi web need not run; nothing printed holds a device's push address or keys. A usage error exits 2; a store others can read, a corrupt one, an unknown device or a failed test push exit 1. This machine only: `chi --remote X web push` exits 2
18
18
  - `chi web --web-markdown` / `--no-web-markdown` — opt in to, or opt out of, sanitized Markdown rendering for completed assistant messages, the stage's asking text and a resolved question card's reply (`web.markdown`, off by default; `--no-web-markdown` turns a config's `web.markdown: true` off for this run)
@@ -35,7 +35,8 @@
35
35
  - `chi self` — print version, source dir (checkout or installed gem), config/memory/session paths, model/host and bundles. Run by a session's `execute`, the `model` row (and host, loop, profile, thinking, served model) is **that session's** model, labelled `(this session <id8>; default …)`; elsewhere it is the default, `(default)`. `model key` is the memory overlay key (`<name>.<key>.md`), and `big memories` the memories over `memory.entry_warn_tokens` ([Memory](memory.md#a-memorys-size)), and `model notes` the model notes a prompt of that model loads (`model_notes_deepseek (system, 612 chars)`, `none`; inside a session its `--mute` list is left out). `chi self --model` prints only the model a new session starts on, an alias resolved to its target (the desktop helper's hint)
36
36
  - `chi update [--dry-run] [--to VERSION] [--json] [--no-gem] [--no-bundles] [--no-desktop]` — update an installed chi: the gem, the system bundle, the shipped bundles you installed and the desktop helper, and check the bundles from elsewhere, in one table (see [Updating](#updating))
37
37
  - `chi home`, `chi home set [--ruby PATH]`, `chi home doctor` — the chi home: the one Ruby (and its gem path) every chi launch boots, whoever launches it. `chi home` shows it (its Ruby, the newest chi in it, gem home and path, `mise`, when and by which chi it was set, and the chi you ran when that is another one; exit 1 with the problem when it can't boot chi), `set` records this chi's Ruby and gem path (or `--ruby PATH`'s own; it must have samagotchi), `doctor` checks it and what should follow it, one row each with `ok`, `warn` or `fail` and what to run (exit 1 on a `fail`; see [Checking it](configuration.md#checking-it-chi-home-doctor)). At a terminal, a chi on another Ruby than the home's says so in one dim line a day. This machine only (see [The chi home](configuration.md#the-chi-home))
38
- - `chi bundle install|upgrade|uninstall|status|diff|list|build|trash|new|test|check` — manage memory bundles (see [Bundle hooks](hooks.md#bundle-hooks-unified-workflow-bundle)); `list` shows the installed ones and the ones shipped with chi, which `install <name>` installs; `upgrade <name>` upgrades an installed bundle by name from the source it came from (its folder, archive or git URL), `upgrade <source>` from a new one; `core` and `dev` are profiles that install a set of them (see [Bundle profiles](memory.md#bundle-profiles-core-and-dev), [Guardrails](guardrails.md), [Bundles](bundles.md#the-btw-bundle), [the mcp bundle](bundles.md#the-mcp-bundle) [the loop-guard bundle](bundles.md#the-loop-guard-bundle), [the check-in bundle](bundles.md#the-check-in-bundle) and [the skills bundle](bundles.md#the-skills-bundle)). `trash` lists and empties the bundle trash (moved files from uninstalls/upgrades). `install`/`upgrade` `--force` overwrites existing entries and also installs a bundle whose `requires_chi` this chi doesn't meet; without it such a bundle is refused (exit 1), nothing written. A plain `install` of a bundle that is already installed from the same source rewrites its hooks, rules, plugin, scripts and record: the summary lists a file that already holds the bundle's bytes as `Up to date: …`, one you wrote as `Skipped: …`, and ends with `NAME was already installed from SOURCE: its hooks, rules, plugin and scripts now match it and its record was rewritten` (plus, when a memory was kept, `your own copies of its memories were kept (chi bundle upgrade NAME merges the bundle's changes into them)`). A usage error (an unknown subcommand or flag, a missing argument, a bad `build --scope`) exits 2, as for the other subcommands. Plain `chi` and `chi web` exit 1 on an unknown flag, a flag missing its value or a stray argument. `new`, `test` and `check` are for writing one: see [Writing a bundle](#writing-a-bundle-new-test-check)
38
+ - `chi project`, `chi project init [DIR]` — this folder's project, and making a folder in no git repository one. `chi project` prints how its project was found (`kind` `git`, `marker` or `none`), its `root` (what memories are keyed on), its `top` (where the AGENTS.md chain starts) and its project `memories` folder, with a `note` for a marker a repository makes ignored, or a `$HOME` that is a git repository. `init` writes `DIR/.chi/project.yml` (DIR defaults to this folder and is created when missing), so DIR and every subfolder share one memories folder, group their sessions and take `chi context --project`; it writes nothing in a git repository (the repository already is the project, exit 0), says so for a folder already marked, refuses `$HOME`, a folder above it and `/` (exit 1), and marks a folder inside another marked project as its own, nested one. It prints the root and the memories folder (with the memories already there: a folder chi ran in at its top keeps them), the running sessions in DIR to restart (`chi sessions restart ID`: their prompt keeps the old project) and, when DIR has saved sessions, that a running chi web groups them after its restart. This machine only. See [Projects without git](memory.md#projects-without-git)
39
+ - `chi bundle install|upgrade|uninstall|status|diff|list|build|trash|new|test|check` — manage memory bundles (see [Plugins](plugins.md) and [Bundle hooks](hooks.md#bundle-hooks)); `list` shows the installed ones and the ones shipped with chi, which `install <name>` installs; `upgrade <name>` upgrades an installed bundle by name from the source it came from (its folder, archive or git URL), `upgrade <source>` from a new one; `core` and `dev` are profiles that install a set of them (see [Bundle profiles](memory.md#bundle-profiles-core-and-dev), [Guardrails](guardrails.md), [Bundles](bundles.md#the-btw-bundle), [the mcp bundle](bundles.md#the-mcp-bundle) [the loop-guard bundle](bundles.md#the-loop-guard-bundle), [the check-in bundle](bundles.md#the-check-in-bundle) and [the skills bundle](bundles.md#the-skills-bundle)). `trash` lists and empties the bundle trash (moved files from uninstalls/upgrades). `install`/`upgrade` `--force` overwrites existing entries and also installs a bundle whose `requires_chi` this chi doesn't meet; without it such a bundle is refused (exit 1), nothing written. A plain `install` of a bundle that is already installed from the same source rewrites its rules, plugin, scripts and record: the summary lists a file that already holds the bundle's bytes as `Up to date: …`, one you wrote as `Skipped: …`, and ends with `NAME was already installed from SOURCE: its rules, plugin and scripts now match it and its record was rewritten` (plus, when a memory was kept, `your own copies of its memories were kept (chi bundle upgrade NAME merges the bundle's changes into them)`). A source with class-file hooks (`hooks/*.rb` or a manifest `hooks:` entry) is refused, `--dry-run` too; an older install's hooks are removed by the next install or upgrade (`Removed (chi no longer loads class-file bundle hooks): …`, an edited one copied to the trash first; see [Class-file hooks are retired](hooks.md#class-file-hooks-are-retired)). A usage error (an unknown subcommand or flag, a missing argument, a bad `build --scope`) exits 2, as for the other subcommands. Plain `chi` and `chi web` exit 1 on an unknown flag, a flag missing its value or a stray argument. `new`, `test` and `check` are for writing one: see [Writing a bundle](#writing-a-bundle-new-test-check)
39
40
 
40
41
  - `chi guardrails replay [--dry-run] [--threshold T] [--model M]` — seed System 1's record from the approvals this machine's sessions already answered: each ask is judged by the configured model and written to `guardrails/judgments.jsonl` with the answer the user gave, then a per-rule table prints (asks, allowed, denied, would-be auto-allows, false allows, asks with no judgment). A parent agent's answer is not the user's: only its deny is replayed, and only where the parent could have allowed. `--dry-run` judges without writing, `--threshold` overrides `guardrails.system_one.threshold` for the table, `--model` the configured model for this run. Exit 2 when `guardrails.system_one.mode` is off or no `host` is set; a usage error exits 2 (see [System 1](guardrails.md#system-1-experimental))
41
42
  - `chi guardrails rule <id>` — print exactly `asks` when that rule is loaded, not disabled and votes `ask` in this `guardrails.mode` for every model; any other line says why not (no such rule, disabled, another mode, it denies, for some models only, rules that failed to load, `guardrails.enabled: false`). Exit 0 either way, 2 for a usage error. It reads config.yml and the installed bundles as a worker does (`GuardrailWiring#rules`) and takes the rule's id, never a command; the [coordinator skill](bundles.md#the-coordinator-bundle) reads it before it merges without asking
@@ -132,11 +133,11 @@ The loop for a bundle you're writing (the whole path: [bundle-authoring.md](bund
132
133
  chi's Ruby (minitest is one of chi's gems; rspec is optional). The tests run the bundle's code as written, with
133
134
  your environment (the kit sandboxes each test's HOME and XDG dirs, not the process).
134
135
  - `chi bundle install --dev PATH` installs the bundle in the local directory `PATH` (not a git URL, archive or name:
135
- exit 2) as a link to it: its plugin, hooks, rules, scripts and `manifest.yml` are `PATH`'s own files, so an edit applies at the
136
+ exit 2) as a link to it: its plugin, rules, scripts and `manifest.yml` are `PATH`'s own files, so an edit applies at the
136
137
  next worker or `chi web` restart with no re-install (a manifest edit to `block_renderers:` shows in chi web's next
137
- request; a new `scripts:`, `hooks:` or `plugin:` entry needs `chi bundle install --dev PATH` again, since the record
138
- has no entry for it), and their sha256 isn't checked, for this bundle only (the
139
- record says `dev`; every other bundle is checked as before). Its memories are copied at install. `chi bundle
138
+ request; a new `scripts:` or `plugin:` entry needs `chi bundle install --dev PATH` again, since the record
139
+ has no entry for it), and no sha256 is checked for any of its files, including its memories, for this bundle only
140
+ (the record says `dev`; every other bundle is checked as before). Its memories are copied at install. `chi bundle
140
141
  status` shows `NAME vX dev → /abs/path`. `chi bundle upgrade NAME` refuses it (it follows its source already; to
141
142
  leave dev mode: `chi bundle uninstall NAME`, then `chi bundle install` without `--dev`), as does a plain install
142
143
  over it, and `--dev` over a normal install. `uninstall` removes the links and the record, never `PATH`'s files.
@@ -144,10 +145,11 @@ The loop for a bundle you're writing (the whole path: [bundle-authoring.md](bund
144
145
  - `chi bundle check [PATH] [--fix] [--set KEY=VALUE]` (PATH defaults to `.`) checks it as an install takes it, one
145
146
  finding per line, `error|warn|info CODE file[:line]: message (fix: hint)`, then `ok|failed: N errors, N
146
147
  warnings`. Exit 1 on any error, 0 otherwise, 2 for usage. It installs and loads the bundle in a temp sandbox
147
- (HOME, config and state), not in yours, but in chi's own process: the plugin's and hooks' code runs as written,
148
+ (HOME, config and state), not in yours, but in chi's own process: the plugin's code runs as written,
148
149
  so run it (and `test`) on code you'd install. The codes, in order: `manifest` (a parse error, verbatim; the run
149
- stops), `missing-file`, `unlisted` (warn: a file an install leaves out, by `MemoryFiles`' rules, so a README
150
- isn't one), `sha` (a stale or placeholder sha, or a plugin / hook / script entry with no sha line; `--fix`
150
+ stops), `hooks-retired` (error: a manifest `hooks:` entry, or `hooks/*.rb` files: class-file hooks are retired,
151
+ hook events from `plugin.rb` with `chi.on`; the run stops), `missing-file`, `unlisted` (warn: a file an install leaves out, by `MemoryFiles`' rules, so a README
152
+ isn't one), `sha` (a stale or placeholder sha, or a plugin / script entry with no sha line; `--fix`
151
153
  rewrites the first two and prints `fixed sha FILE → sha256:…`), `unknown-key` (warn: a manifest key this chi's
152
154
  parser never reads, so it ignores it, one row per key at its line; the install keeps the manifest, so a newer
153
155
  chi reads the key from it with no reinstall, and `chi update`'s re-check names it too), `requires-chi` (this chi doesn't satisfy it:
@@ -156,7 +158,7 @@ The loop for a bundle you're writing (the whole path: [bundle-authoring.md](bund
156
158
  `Manifest::SINCE`; an installed bundle gets the unknown-key row but never the floor, as it's the author's fix),
157
159
  `trust` (warn: `trust_level: reviewed|trusted` on a
158
160
  bundle chi doesn't ship), `clash` (warn: a name chi ships, or one installed from elsewhere; the sandboxed install
159
- then forces), `install` (each Installer warning, a `needs` one as warn, and each plugin or hook load failure at
161
+ then forces), `install` (each Installer warning, a `needs` one as warn, and each plugin load failure at
160
162
  the source `file:line`; skipped while a placeholder sha is unfilled), `registered` (info: each command, tool,
161
163
  hook with its priority, init, toolkit, tool source, peer source and prompt section), `hook-event` (warn: a `chi.on` event chi
162
164
  never fires), `fail-closed` (warn: a `chi.on(..., on_error: :fail_closed)` in a plugin whose manifest `plugin:` lacks `required: true`, so a load failure would not deny tool calls; not on an installed bundle's re-check), `settings` (warn: a `--set` the manifest's `settings:` rules out, as config.yml's `bundles:` is told), `api` (warn: a `ctx.*` / `chi.*` call or a keyword this chi's API lacks, read from the source with
@@ -339,7 +341,8 @@ Also shipped, not installed: dev (mcp, skills, source-links) (chi bundle install
339
341
  the merge itself, and `--force` replacing the file. A dry run and an automatic update
340
342
  (`update.auto`) show it and leave it, and so does a failed row or one with no place
341
343
  for it (a skipped system bundle). A memory that can't be read at a
342
- newer version fails that bundle's row with the file's name. Hooks, rules, the plugin and scripts are replaced. One
344
+ newer version fails that bundle's row with the file's name. Rules, the plugin and scripts are replaced, and an
345
+ older install's class-file `hooks/` removed. One
343
346
  whose bytes are neither what the install recorded (nothing recorded counts as edited) nor the new version's is
344
347
  edited: the row says `replaced edited hooks/x (a copy goes to the trash first)` (`would replace` in the plan), and
345
348
  a copy goes to the bundle trash first (`chi bundle trash`; `hooks/x`,
@@ -348,15 +351,16 @@ Also shipped, not installed: dev (mcp, skills, source-links) (chi bundle install
348
351
  chi"), a newer installed one is left, one whose new version needs a newer
349
352
  chi is skipped, and bundles you didn't install stay uninstalled.
350
353
  - **Bundles not from chi** are never changed, but the chi the update leaves
351
- checks what is installed of them (the plugin, hooks and rules as they are,
354
+ checks what is installed of them (the plugin and rules as they are,
352
355
  not their source, which may be gone) with `chi bundle check`'s own checks,
353
356
  and each warning or error is a row under the bundle's name: `requires_chi`
354
- ruling this chi out, a plugin or hook that no longer loads, a hook on an event
357
+ ruling this chi out, a plugin that no longer loads, a hook on an event
355
358
  chi no longer fires, a `ctx`/`chi` call the API lacks, a reference to chi's
356
359
  internals that this chi no longer has (`Samagotchi::OldInternal::Thing is gone from this chi, so this bundle may
357
360
  fail` — a name that still resolves gets no row, so a bundle reaching `Samagotchi::Log` isn't warned about on every
358
- update), a name chi now ships, or a hook or plugin edited since it was installed (it
359
- doesn't load). What only an author can act on (shas, unlisted files, tests, a
361
+ update), a name chi now ships, a rule or plugin edited since it was installed (it
362
+ doesn't load), or class-file hooks an older chi installed (`hooks-retired`: they no longer load; `chi bundle upgrade
363
+ NAME` once its source hooks from `plugin.rb`, or `chi bundle uninstall NAME`). What only an author can act on (shas, unlisted files, tests, a
360
364
  missing `requires_chi`, its floor, `trust_level`) is left out, and so are
361
365
  profiles. A clean bundle gets no row beyond its `skipped (not from chi)`.
362
366
  - **They fail nothing:** an error row too has `failed: false` and leaves the exit
@@ -1427,7 +1431,7 @@ Scan the QR code with the phone's camera. The page trades the token in the
1427
1431
  link for a cookie (kept 400 days) and drops it from the address, so a
1428
1432
  bookmark or a home-screen icon keeps working across restarts. It opens on
1429
1433
  the project chi web starts in (`--start-dir DIR` or `web.start_dir` picks
1430
- another; outside a repo it shows every session); the header's `all` shows
1434
+ another; outside a project (no repository or marked folder) it shows every session); the header's `all` shows
1431
1435
  every project's. A home-screen
1432
1436
  web app on iOS has cookies of its own: if it opens on "needs chi web's
1433
1437
  access token", paste the token there (the part of the link after
@@ -1471,10 +1471,12 @@ test run (`SAMAGOTCHI_ENV`, `RACK_ENV`, `CI`). The day is stamped in `$XDG_STATE
1471
1471
  If an AGENTS.md file is present in the project root, samagotchi injects its
1472
1472
  contents into the system prompt under a "Project specific description:" section.
1473
1473
  The project root is the top of the git work tree chi runs in (a linked
1474
- worktree's own checkout), or the current directory outside a repository.
1474
+ worktree's own checkout), or of the folder marked by `chi project init` it runs
1475
+ in (see [Projects without git](memory.md#projects-without-git)); outside both,
1476
+ the current directory.
1475
1477
 
1476
- Started in a subdirectory, chi loads the chain: every AGENTS.md from the work
1477
- tree top down to the current directory, outermost first. The first keeps the
1478
+ Started in a subdirectory, chi loads the chain: every AGENTS.md from the
1479
+ project's top down to the current directory, outermost first. The first keeps the
1478
1480
  plain header; each further one is headed with its directory, e.g. "Project
1479
1481
  specific description for pkg/:". Outside a repository only the current
1480
1482
  directory's AGENTS.md is read. Edits are picked up by a new process or a
@@ -2125,7 +2127,7 @@ are never sent to; `chi web push rm ID` removes them. From the Mac: `chi web pus
2125
2127
  shows a recent delivery (Apple keeps accepting pushes for it): if one stops getting notifications, switch it off and on
2126
2128
  there; the new subscription replaces the dead one.
2127
2129
 
2128
- - **What a tap opens**: the session, in its own project's view (the all view for a session in no repo), whichever
2130
+ - **What a tap opens**: the session, in its own project's view (the all view for a session in no project: no repository or marked folder), whichever
2129
2131
  project the app was showing; an open app window moves there.
2130
2132
  - **What is sent**: the session's name and the reason ("needs approval", "needs an answer", "hit its step limit",
2131
2133
  "needs you", "turn failed", "done in N s"), never the question or the command, as the tab's notification on the
@@ -2370,7 +2372,7 @@ column says so, a `--kebab-name` flag. The maps (`hosts:`, `models:`,
2370
2372
  | `web.reload_check` | `false` | yes | Opt-in: once each finished turn has moved into the history, the page reads the session again (one full read per turn end), draws that turn as a reload would and compares the two, strictly: a known divergence counts as a mismatch too. `chi web` logs one `reload_checked` line per check and a `reload_mismatch` line naming the kinds that differed, `excused` (the known divergences, `history_shape.js` `ACCEPTED`, that would explain them) and `explained` (all of them had one), with `turn_id`, `drawn` (the turn came back from its drawing, so a mismatch with `drawn: true` is a drawing bug) and the worker's version: counts only, never content. The count is `grep -c reload_mismatch` over `grep -c reload_checked` in chi web's log, split by `excused`. Cards are left out: a reload places them after the render, which the check doesn't run (the e2e reload checks compare them). Every open owner tab runs the check; `chi web` logs a turn's first report only, and starts over when it restarts. |
2371
2373
  | `web.annotate_presets` | `Agreed\|Could you please elaborate?` | yes | Quick replies next to Annotate in `chi web`, `\|`-separated (a YAML list works too); `""` in the file or on the CLI leaves only Annotate (an empty env value means the default). See [CLI](cli.md#web-annotate-presets). |
2372
2374
  | `web.editor` | `vscode` | yes | What a file ref in `chi web` (a file tool row's title, a diff line) opens, for a viewer on this machine: a preset (`vscode`, `vscode-insiders`, `cursor`, `zed`) or a URL template with `{path}` and an optional `{line}`; `none` turns refs off. See [Opening files from chi web](#opening-files-from-chi-web). |
2373
- | `web.start_dir` | the folder `chi web` runs in | | The folder `chi web` starts in (`chi web --start-dir DIR` for one run): its page (the printed link, a bare `/`, the LAN link and QR code, the Home Screen app) opens on that folder's project, outside a repo on every session, and a new session from the all view starts there; the header's project picker switches projects. `~` and a relative path (resolved from your home folder) work; one that isn't a folder stops `chi web` from starting. `chi web` never changes its own folder. See [CLI](cli.md). |
2375
+ | `web.start_dir` | the folder `chi web` runs in | | The folder `chi web` starts in (`chi web --start-dir DIR` for one run): its page (the printed link, a bare `/`, the LAN link and QR code, the Home Screen app) opens on that folder's project, outside a project (no repository or marked folder) on every session, and a new session from the all view starts there; the header's project picker switches projects. `~` and a relative path (resolved from your home folder) work; one that isn't a folder stops `chi web` from starting. `chi web` never changes its own folder. See [CLI](cli.md). |
2374
2376
  | `web.participants.expires` | `7d` | | How long a participant's link lasts unless `chi share --expires` says otherwise: `Nh` or `Nd`, 1h to 30d. See [participants](internals/participants.md). |
2375
2377
  | `web.participants.cap` | `2.0` | | What a participant's turns, and the side calls they cause, may cost you in dollars unless `chi share --cap` says otherwise ($0 to $1000); at the cap their next comment is refused (429 `cap_reached`) and you get a notice. `chi share cap ID P DOLLARS` changes one grant's. |
2376
2378
  | `web.participants.queue` | `3` | | How many of a participant's turns may wait behind their running one (0 to 20); one more is refused (429 `too_many_turns`). |
data/docs/context.md CHANGED
@@ -71,10 +71,12 @@ A source belongs to one session, or to a project:
71
71
 
72
72
  - A **session** source (`chi context add NAME … <id>`) is that session's
73
73
  alone and runs in the session's folder (the worktree it started in).
74
- - A **project** source (`--project`, inside a git repository) has **one
75
- snapshot shared by every session of the project**: all worktrees of a repo
74
+ - A **project** source (`--project`, inside a git repository or a folder
75
+ marked by `chi project init`) has **one snapshot shared by every session of
76
+ the project**: all worktrees of a repo (or subfolders of a marked folder)
76
77
  share the project, and the command runs in the project root (the main
77
- checkout; the session's folder when the root is a bare git dir). Whichever
78
+ checkout, or the marked folder; the session's folder when the root is a bare
79
+ git dir). Whichever
78
80
  worker is due runs it; the others read the snapshot.
79
81
 
80
82
  So anything that depends on the branch or the checkout (a PR, `git status`,
data/docs/guardrails.md CHANGED
@@ -1393,7 +1393,7 @@ chi bundle install known-names # or chi bundle install dev
1393
1393
  ```
1394
1394
 
1395
1395
  installs a plugin with one `before_tool_call` hook (`chi.on`, priority 50; a
1396
- `hooks/known_names.rb` file before known-names 0.2.0) and a short memory. A local model that
1396
+ `hooks/known_names.rb` file before known-names 0.2.0, which chi no longer loads) and a short memory. A local model that
1397
1397
  once misspells a name inside a path (`jonathandoe` → `jonathndoe`)
1398
1398
  keeps copying the wrong spelling from its context, and every call after
1399
1399
  that fails. The hook knows the right names and compares strings: the
@@ -1433,7 +1433,7 @@ run*, *Run as is*, *Deny*; with no one to ask (`--non-interactive`) or a
1433
1433
  dismissed question it rejects. A real near name (a folder `jondoe` next to
1434
1434
  user `johndoe`, a login one letter from another) is caught too: list it under
1435
1435
  `ignore:`. The hook is `on_error: log`: a bug in it warns and lets the call
1436
- through. As with every bundle hook, a running worker picks it up after its
1436
+ through. As with every plugin, a running worker picks it up after its
1437
1437
  next start.
1438
1438
 
1439
1439
  The system prompt names the home directory once, with the advice to write
@@ -1442,7 +1442,8 @@ it as `~` or `$HOME`, so the model rarely has to spell it.
1442
1442
  ## An external guard: chi-nah
1443
1443
 
1444
1444
  [chi-nah](https://github.com/dm1try/chi-nah) is a bundle kept in its own repo (`chi bundle install
1445
- https://github.com/dm1try/chi-nah`). Its `before_tool_call` hook asks
1445
+ https://github.com/dm1try/chi-nah`). Its plugin's `before_tool_call` hook (`chi.on`, chi-nah 0.2.0;
1446
+ 0.1.x shipped it as a class-file hook, which chi no longer loads: `chi bundle upgrade chi-nah`) asks
1446
1447
  [nah](https://github.com/manuelschipper/nah)'s deterministic guards about every `execute`, `task_create`,
1447
1448
  `read`, `write` and `edit`: a call nah blocks (secret exfiltration, `rm -rf ~`, `git reset --hard`,
1448
1449
  `curl | sh`…) is denied with the guard's name and how the user pauses it (`nah nap <guard>`); everything
@@ -1450,13 +1451,13 @@ else goes to chi's own rules. It needs nah installed; see its README for the set
1450
1451
 
1451
1452
  ## Failing closed
1452
1453
 
1453
- - A config hook with `required: true`, a bundle `before_tool_call` hook with
1454
- `on_error: fail_closed`, or a bundle plugin its manifest marks `required: true`
1454
+ - A config hook with `required: true`, or a bundle plugin its manifest marks `required: true`
1455
1455
  (`plugin:` `required: true`, for a `chi.on(:before_tool_call, on_error:
1456
1456
  :fail_closed)` guard, chi 0.157.0 or newer, [plugins.md](plugins.md#a-guard-that-fails-closed-on_error-fail_closed)),
1457
1457
  that fails to load (missing, syntax error, an unmet `requires_chi`, or for a
1458
- bundle's file a sha256 that differs from the installed one) makes chi deny
1459
- every tool call. A fail-closed hook that raises when called denies that call.
1458
+ plugin a sha256 that differs from the installed one) makes chi deny
1459
+ every tool call. (An older chi's class-file bundle hooks don't load at all,
1460
+ `fail_closed` or not: a not-required failure, [hooks.md](hooks.md#class-file-hooks-are-retired).) A fail-closed hook that raises when called denies that call.
1460
1461
  - Other hooks stay fail-open; a load failure is a warning.
1461
1462
  - Every load failure is shown once, at the start of the first turn
1462
1463
  (`guardrails> …` in the terminal, a red line on the web).