samagotchi 0.2.0 → 0.3.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 +116 -1
- data/README.md +40 -4
- data/bin/chi +86 -19
- data/docs/cli.md +108 -5
- data/docs/configuration.md +229 -45
- data/docs/desktop.md +6 -0
- data/docs/hooks.md +126 -5
- data/docs/plugins.md +68 -2
- data/docs/releasing.md +18 -8
- data/docs/sessions.md +30 -4
- data/lib/samagotchi/answer_display.rb +95 -0
- data/lib/samagotchi/archive_store.rb +90 -0
- data/lib/samagotchi/bootstrap/config_writer.rb +342 -0
- data/lib/samagotchi/bootstrap/probe.rb +262 -0
- data/lib/samagotchi/bootstrap_command.rb +347 -0
- data/lib/samagotchi/bridge/pending_card.rb +89 -0
- data/lib/samagotchi/bridge/turn_accumulator.rb +14 -3
- data/lib/samagotchi/bridge.rb +9 -0
- data/lib/samagotchi/bridge_client.rb +6 -2
- data/lib/samagotchi/bundles/check-in/manifest.yml +10 -0
- data/lib/samagotchi/bundles/check-in/plugin.rb +244 -0
- data/lib/samagotchi/bundles/source-links/hooks/source_links.rb +358 -0
- data/lib/samagotchi/bundles/source-links/manifest.yml +14 -0
- data/lib/samagotchi/bundles/source-links/source_links.md +5 -0
- data/lib/samagotchi/bundles/system/config_modification_protocol.md +10 -6
- data/lib/samagotchi/bundles/system/manifest.yml +3 -3
- data/lib/samagotchi/bundles/system/self_map.md +8 -2
- data/lib/samagotchi/client.rb +72 -13
- data/lib/samagotchi/config.rb +196 -36
- data/lib/samagotchi/desktop/macos/ChiRunner.swift +13 -7
- data/lib/samagotchi/desktop/macos/Panel.swift +71 -19
- data/lib/samagotchi/empty_answer_retry.rb +43 -0
- data/lib/samagotchi/engine.rb +233 -36
- data/lib/samagotchi/guardrails/approval.rb +9 -0
- data/lib/samagotchi/guardrails/scratch_writes.rb +40 -0
- data/lib/samagotchi/guardrails.rb +1 -0
- data/lib/samagotchi/hooks/registry.rb +24 -5
- data/lib/samagotchi/host_registry.rb +4 -3
- data/lib/samagotchi/idle_recap.rb +5 -1
- data/lib/samagotchi/kernel_loop.rb +47 -15
- data/lib/samagotchi/llm/chat_loop.rb +59 -20
- data/lib/samagotchi/llm/errors.rb +21 -3
- data/lib/samagotchi/llm/http.rb +42 -13
- data/lib/samagotchi/llm/openai_chat.rb +12 -4
- data/lib/samagotchi/log_subscriber.rb +18 -3
- data/lib/samagotchi/model_profile.rb +1 -1
- data/lib/samagotchi/plugin/context.rb +22 -1
- data/lib/samagotchi/plugin/sessions.rb +3 -1
- data/lib/samagotchi/reply_wait.rb +126 -0
- data/lib/samagotchi/sampling_settings.rb +58 -0
- data/lib/samagotchi/self_report.rb +1 -0
- data/lib/samagotchi/send_command.rb +153 -7
- data/lib/samagotchi/session.rb +52 -11
- data/lib/samagotchi/session_archive_command.rb +107 -0
- data/lib/samagotchi/session_commands.rb +11 -2
- data/lib/samagotchi/session_manager.rb +114 -9
- data/lib/samagotchi/session_metrics.rb +222 -106
- data/lib/samagotchi/steer.rb +72 -0
- data/lib/samagotchi/terminal_ui/attached_loop.rb +39 -6
- data/lib/samagotchi/terminal_ui/event_renderer.rb +13 -8
- data/lib/samagotchi/terminal_ui/formatting.rb +31 -8
- data/lib/samagotchi/terminal_ui/input_support.rb +3 -0
- data/lib/samagotchi/terminal_ui.rb +77 -4
- data/lib/samagotchi/tool_activity.rb +3 -1
- data/lib/samagotchi/tools/builtins.rb +15 -4
- data/lib/samagotchi/tools/delegate_wait.rb +26 -69
- data/lib/samagotchi/tools/execute.rb +52 -14
- data/lib/samagotchi/tools/task_runtime.rb +19 -0
- data/lib/samagotchi/tools/task_wait.rb +27 -3
- data/lib/samagotchi/turn_note.rb +60 -6
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/vision_support.rb +2 -6
- data/lib/samagotchi/web/app.rb +88 -4
- data/lib/samagotchi/web/public/activity.js +10 -1
- data/lib/samagotchi/web/public/annotate_presets.js +26 -0
- data/lib/samagotchi/web/public/annotations.js +13 -0
- data/lib/samagotchi/web/public/app.js +437 -88
- data/lib/samagotchi/web/public/card.js +5 -3
- data/lib/samagotchi/web/public/chat_view.js +10 -1
- data/lib/samagotchi/web/public/copy.js +20 -4
- data/lib/samagotchi/web/public/ctx.js +15 -0
- data/lib/samagotchi/web/public/data.js +21 -6
- data/lib/samagotchi/web/public/format.js +9 -0
- data/lib/samagotchi/web/public/index.html +38 -2
- data/lib/samagotchi/web/public/notify.js +175 -0
- data/lib/samagotchi/web/public/question_card.js +2 -1
- data/lib/samagotchi/web/public/sessions_list.js +7 -0
- data/lib/samagotchi/web/public/timing.js +39 -14
- data/lib/samagotchi/web/public/turn_events.js +46 -0
- data/lib/samagotchi/web/public/turn_view.js +47 -7
- data/lib/samagotchi/web/server.rb +8 -4
- data/lib/samagotchi/web/session_hub.rb +2 -1
- data/lib/samagotchi/web/session_summary.rb +24 -1
- data/lib/samagotchi/worker.rb +11 -0
- metadata +20 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 950fbee37d2110efad37f8d3f8981144f49af9a2ccf75649d5953b1fe7773260
|
|
4
|
+
data.tar.gz: aaad7adb0e9aa3c81b601df48fde4ebab965862ed954454c8b79e2de841f0789
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1b4d092f263192c242dead3ae0c556758a38af2f01285dc590362d698696a31d38801c70e64ff85daa33067ceda92be7687915024d1e81802bd0a2c365cc292d
|
|
7
|
+
data.tar.gz: 7a06a4d47dea9168e40b0ae0ba7bc6cbfe1b377ba9ac29a998d19f0ee17a766de3186786894deea8b6765a78e21b14e3e7ece8238ab401354c43a5eba4d1815b
|
data/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,120 @@ and commands may change between minor versions. How releases are made:
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [0.3.0] - 2026-09-29
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `chi bootstrap [HOST[:PORT]|URL]`: the first setup in one command. It finds
|
|
16
|
+
out whether the server is llama.cpp or OpenAI-compatible (with no target, it
|
|
17
|
+
looks on localhost's usual ports), picks the model, sends a test request and
|
|
18
|
+
writes config.yml; an existing config gets a `hosts:` entry added after a
|
|
19
|
+
backup, its other lines untouched. `--key-env VAR` for a server that wants a
|
|
20
|
+
key, `--model`, `--name`, `--no-test`, `--dry-run`. The first-run "no model
|
|
21
|
+
configured" line points at it.
|
|
22
|
+
- Web notifications: a session that needs you (a question or approval, a
|
|
23
|
+
failed turn, a turn done after 10 s or more) while the tab is in the
|
|
24
|
+
background counts in the page title, `(N) Chi`, and with the new bell in the
|
|
25
|
+
top bar on, shows an OS notification that opens the session.
|
|
26
|
+
- `chi scratch`: a one-time session in the terminal that leaves nothing behind.
|
|
27
|
+
It is deleted however it ends, saves no memories and starts no other
|
|
28
|
+
sessions; one left by a killed process goes at the next sweep or
|
|
29
|
+
`chi sessions clean`.
|
|
30
|
+
- Archiving a session: `chi sessions archive ID`, `/archive` in a terminal or
|
|
31
|
+
`archive` in the web's info bar hides it (and its delegates) from every list
|
|
32
|
+
and keeps it for good; `chi sessions list --archived` and "include archived"
|
|
33
|
+
in the web's all sessions find it, and a message you send to it brings it
|
|
34
|
+
back.
|
|
35
|
+
- `chi send --new` starts a session with the message, the way the web does, so
|
|
36
|
+
it shows in the web at once (`--dir`, `--model`); `chi send --wait` (new or
|
|
37
|
+
one existing session) blocks and prints the answer, exit 3 when the turn
|
|
38
|
+
waits for your answer.
|
|
39
|
+
- The `check-in` bundle (`chi bundle install check-in`): after 50 tool calls
|
|
40
|
+
in one turn with no answer (then every 50 more) a card asks you to nudge
|
|
41
|
+
the model, let it keep going or stop the turn; `mode: nudge` nudges it by
|
|
42
|
+
itself. `/checkin` shows and changes it for the session.
|
|
43
|
+
- Plugins can steer the running turn: `ctx.steer(text)` and a hook's
|
|
44
|
+
`event[:steer]` put text into the turn as its own message at the loop's
|
|
45
|
+
next step, shown as `<bundle>> nudged: …`; `ctx.stop_turn(reason)` stops it.
|
|
46
|
+
- The `source-links` bundle (`chi bundle install source-links`): refs like
|
|
47
|
+
`JIRA-123` or `GH-45` in an answer become links in the web, and a one-line
|
|
48
|
+
`sources:` note follows the turn in the terminal. Sources are patterns in
|
|
49
|
+
`bundles: source-links:`.
|
|
50
|
+
- Plugins can change how an answer is shown without changing what the model
|
|
51
|
+
sees: an after_turn hook's `event[:present]` sets a display version of the
|
|
52
|
+
answer, which the web renders (and keeps after a reload).
|
|
53
|
+
- Annotate presets: selecting text in an answer offers quick replies next to
|
|
54
|
+
Annotate (default "Agreed" and "Could you please elaborate?"); a pill puts
|
|
55
|
+
the quote and the text in the composer without sending.
|
|
56
|
+
`web.annotate_presets` (`|`-separated or a YAML list; `""` turns them off).
|
|
57
|
+
- `sampling:` on a `hosts:` or `models:` entry (temperature, top_p, top_k,
|
|
58
|
+
min_p, penalties, …) is sent with every request to that host or model;
|
|
59
|
+
`temperature: null` sends none, so the provider's default applies.
|
|
60
|
+
- `retry.empty_answer` (default 1): when the model returns an empty answer
|
|
61
|
+
(or runs out of tokens while thinking), chi asks again once in the same
|
|
62
|
+
turn; the terminal and the web show "↻ empty answer, asking again".
|
|
63
|
+
- The context fill is saved with the session: `chi sessions list` and the web
|
|
64
|
+
list show it (`ctx 12%`), and `/stats`, the status line and the web meter
|
|
65
|
+
show it right after a restart or a reload. Token totals now cover the whole
|
|
66
|
+
session across worker restarts.
|
|
67
|
+
- `chi send --wait ID` with no message waits for the session's next reply
|
|
68
|
+
without sending anything (also on a running session), e.g. after `--wait`
|
|
69
|
+
exited 3 for a question.
|
|
70
|
+
- The desktop panel has a "New session in <folder>" row (⏎ starts a session
|
|
71
|
+
with the selection there).
|
|
72
|
+
- Web notifications also cover a check-in card waiting for you, and a chi tab
|
|
73
|
+
in front keeps the tabs behind it from notifying twice.
|
|
74
|
+
|
|
75
|
+
### Changed
|
|
76
|
+
|
|
77
|
+
- Stop ends a turn that is waiting in `task_wait` or running `execute` within
|
|
78
|
+
about a second, and a Stop right after a turn starts ends it at once. A
|
|
79
|
+
background task keeps running: the model is told it's still running and how
|
|
80
|
+
to continue or stop it, and the cancel note lists the session's tasks still
|
|
81
|
+
running. A stopped wait shows as "stopped".
|
|
82
|
+
- config.yml: a nested key now wins over its legacy flat `SAMAGOTCHI_*` key
|
|
83
|
+
(one warning names both); an unknown key warns with a did-you-mean;
|
|
84
|
+
top-level keys like `max_tool_output_chars` no longer warn; `no_interrupt`
|
|
85
|
+
and `no_default_input` work from config and env.
|
|
86
|
+
- A refused connection to a host fails at once with "can't reach host … — is
|
|
87
|
+
the server running?" instead of retrying for half a minute.
|
|
88
|
+
- A host whose `/props` doesn't answer costs one probe per 30 s, not a wait
|
|
89
|
+
at every turn.
|
|
90
|
+
- `chi -p … --non-interactive` with an empty answer says so on stderr and
|
|
91
|
+
exits 1.
|
|
92
|
+
- docs/configuration.md uses nested keys throughout and lists every setting;
|
|
93
|
+
the README starts with `chi bootstrap`.
|
|
94
|
+
|
|
95
|
+
### Removed
|
|
96
|
+
|
|
97
|
+
- The unused config keys `bridge.enable` and `thinking.preview_lines` (they
|
|
98
|
+
now warn as unknown).
|
|
99
|
+
|
|
100
|
+
### Fixed
|
|
101
|
+
|
|
102
|
+
- The web keeps a failed turn's prompt and reason after a reload, and a failed
|
|
103
|
+
turn's prompt comes back to the composer, also for a first message from the
|
|
104
|
+
start page.
|
|
105
|
+
- Web answer links appear with the answer instead of being swapped in after it.
|
|
106
|
+
- The title badge clears when you switch to the tab in Safari, and drops a
|
|
107
|
+
question answered from another tab or client.
|
|
108
|
+
- `/archive` and `/exit` typed in the web composer get a short reply instead
|
|
109
|
+
of an error.
|
|
110
|
+
- An approval card for a tool without a command or path shows its arguments.
|
|
111
|
+
- A hook notice from before the turn stays in its step after a reload.
|
|
112
|
+
- Card actions (check-in's Nudge, …) no longer leave a command bubble or line.
|
|
113
|
+
- A 404 from a native (llama.cpp) host hints at `api: openai`.
|
|
114
|
+
- A config `memories:` entry that can't be loaded is no longer called `--memory`.
|
|
115
|
+
- `chi --resume`/`--attach` refuse a leftover scratch session.
|
|
116
|
+
- `chi sessions list --live/--cwd/--format` show `[scratch]` too.
|
|
117
|
+
- The legacy-key warning names the right nested key.
|
|
118
|
+
|
|
119
|
+
### Bundles
|
|
120
|
+
|
|
121
|
+
- New: `check-in` 0.1.1 (`chi bundle install check-in`, needs chi 0.3.0) and
|
|
122
|
+
`source-links` 0.2.0 (`chi bundle install source-links`). No other bundle
|
|
123
|
+
changed since 0.2.0.
|
|
124
|
+
|
|
11
125
|
## [0.2.0] - 2026-09-27
|
|
12
126
|
|
|
13
127
|
The first public release. chi is an agent harness for local models
|
|
@@ -39,5 +153,6 @@ and long-lived sessions.
|
|
|
39
153
|
- A macOS desktop helper (`chi desktop install`): a "Send to chi" Service and a
|
|
40
154
|
hotkey panel that send selected text or the clipboard to your sessions.
|
|
41
155
|
|
|
42
|
-
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.
|
|
156
|
+
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.3.0...HEAD
|
|
157
|
+
[0.3.0]: https://github.com/dm1try/samagotchi/compare/v0.2.0...v0.3.0
|
|
43
158
|
[0.2.0]: https://github.com/dm1try/samagotchi/releases/tag/v0.2.0
|
data/README.md
CHANGED
|
@@ -32,9 +32,28 @@ bin/chi self
|
|
|
32
32
|
`bin/chi` runs the checkout; `bundle exec rake gem:install` installs it as a
|
|
33
33
|
local gem, which puts `chi` on your PATH.
|
|
34
34
|
|
|
35
|
+
## Set up
|
|
36
|
+
|
|
37
|
+
Point chi at your model server; it works out the rest and writes
|
|
38
|
+
`~/.config/samagotchi/config.yml`:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
chi bootstrap 192.168.1.29:8081 # llama.cpp on another machine
|
|
42
|
+
chi bootstrap localhost:11434 # Ollama (any OpenAI-compatible server)
|
|
43
|
+
chi bootstrap https://openrouter.ai/api/v1 --key-env OPENROUTER_API_KEY
|
|
44
|
+
chi bootstrap # look on this machine's usual ports
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
It finds out whether the server is llama.cpp or OpenAI-compatible, picks the
|
|
48
|
+
model (or asks, when there are several), sends one test request and writes
|
|
49
|
+
the config. With a config already there, it adds a `hosts:` entry and keeps
|
|
50
|
+
the rest of the file. Edit the file later as in [Configure](#configure);
|
|
51
|
+
`chi bootstrap --help` has the options.
|
|
52
|
+
|
|
35
53
|
## Configure
|
|
36
54
|
|
|
37
|
-
|
|
55
|
+
To write the config by hand instead, or to change it later: create
|
|
56
|
+
`~/.config/samagotchi/config.yml` (or `$XDG_CONFIG_HOME/samagotchi/config.yml`):
|
|
38
57
|
|
|
39
58
|
```yaml
|
|
40
59
|
default:
|
|
@@ -44,7 +63,20 @@ server:
|
|
|
44
63
|
port: 8080
|
|
45
64
|
```
|
|
46
65
|
|
|
47
|
-
|
|
66
|
+
`server:` is a llama.cpp `llama-server`. For any other OpenAI-compatible
|
|
67
|
+
server (vLLM, LM Studio, Ollama, a gateway), name it under `hosts:` with
|
|
68
|
+
`api: openai` instead:
|
|
69
|
+
|
|
70
|
+
```yaml
|
|
71
|
+
default:
|
|
72
|
+
model: local:qwen3:8b # host:model; the ids are in GET <url>/models
|
|
73
|
+
hosts:
|
|
74
|
+
local:
|
|
75
|
+
url: http://localhost:11434/v1 # the API base, /v1 included
|
|
76
|
+
api: openai
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`chi self` shows the model and host chi will use. Other settings (multiple hosts, transports, timeouts, the idle recap) are in
|
|
48
80
|
[Configuration](docs/configuration.md).
|
|
49
81
|
|
|
50
82
|
## Use
|
|
@@ -58,11 +90,12 @@ chi web --open # web UI: this project's sessions (--s
|
|
|
58
90
|
chi sessions list # this project's saved sessions (--scope=all: every one)
|
|
59
91
|
pbpaste | chi note --source slack <id> # background context for a session (no turn)
|
|
60
92
|
pbpaste | chi send -m "same bug?" <id> # a message to a session, the clipboard quoted above it
|
|
93
|
+
chi send --new --wait -m "review feat/x" # a new session you can watch in the web; prints the answer
|
|
61
94
|
```
|
|
62
95
|
|
|
63
96
|
`@shot.png` in a prompt (or a pasted/dropped image in the web UI) shows the model an image, when it can see them; see [Images](docs/cli.md#images).
|
|
64
97
|
|
|
65
|
-
`/model` switches models, Ctrl-C cancels a turn, and Ctrl-D or `/detach` detaches (the session keeps running; `chi --attach ID` comes back). `/exit` detaches and stops the session's worker too, unless something still needs it (a running turn, another UI); `chi --resume ID` picks the conversation up again. `/exit --delete` also deletes the session once the worker has gone; `chi sessions delete ID` deletes one from the shell.
|
|
98
|
+
`/model` switches models, Ctrl-C cancels a turn, and Ctrl-D or `/detach` detaches (the session keeps running; `chi --attach ID` comes back). `/exit` detaches and stops the session's worker too, unless something still needs it (a running turn, another UI); `chi --resume ID` picks the conversation up again. `/exit --delete` also deletes the session once the worker has gone; `chi sessions delete ID` deletes one from the shell. `/archive` (or `chi sessions archive ID`) hides a session from every list and keeps it for good; `chi sessions list --archived` finds it again.
|
|
66
99
|
|
|
67
100
|
### Context notes
|
|
68
101
|
|
|
@@ -78,7 +111,9 @@ reply (a normal session: `chi --attach <id>` steers it).
|
|
|
78
111
|
|
|
79
112
|
`chi send` is the other half: the text goes in as your message, the same as
|
|
80
113
|
typing it in the attached terminal or the web composer, and a turn runs. Piped
|
|
81
|
-
stdin plus `-m` puts the stdin above the message as a `>` quote.
|
|
114
|
+
stdin plus `-m` puts the stdin above the message as a `>` quote. `chi send --new`
|
|
115
|
+
starts a session with the message instead (it shows in the web at once), and
|
|
116
|
+
`--wait` blocks and prints the answer.
|
|
82
117
|
|
|
83
118
|
### Send to chi (macOS)
|
|
84
119
|
|
|
@@ -119,6 +154,7 @@ once, for the session, for the repo, or for the whole rule in the repo.
|
|
|
119
154
|
```sh
|
|
120
155
|
bundle exec rspec # Ruby specs
|
|
121
156
|
npm test # web frontend specs
|
|
157
|
+
npm run e2e # web UI happy paths in Chromium, fake model (once: npx playwright install chromium)
|
|
122
158
|
```
|
|
123
159
|
|
|
124
160
|
## License
|
data/bin/chi
CHANGED
|
@@ -38,16 +38,18 @@ if ARGV[0] == "sessions"
|
|
|
38
38
|
sessions_args = ARGV[1..] || []
|
|
39
39
|
sub = sessions_args[0]
|
|
40
40
|
if sub.nil? || %w[-h --help help].include?(sub)
|
|
41
|
-
puts "Usage: chi sessions <list|stop|delete|prune|clean> [options]"
|
|
41
|
+
puts "Usage: chi sessions <list|stop|archive|unarchive|delete|prune|clean> [options]"
|
|
42
42
|
puts " list [--sort updated_at|created_at] [--order desc|asc] [--limit N]"
|
|
43
|
-
puts " [--live] [--cwd PATH] [--format text|json|tsv]"
|
|
43
|
+
puts " [--live] [--cwd PATH] [--format text|json|tsv] [--archived]"
|
|
44
44
|
puts " --live: sessions a worker runs now (the ones chi note reaches), 10 unless --limit"
|
|
45
45
|
puts " --cwd PATH: sessions in PATH or below; json/tsv (id<TAB>description) are for scripts"
|
|
46
46
|
puts " [--scope=all]: every project's sessions; by default only this git project's (all outside a repo)"
|
|
47
|
+
puts " --archived: archived sessions too (marked [archived]; json: archived: true)"
|
|
47
48
|
puts " stop ID... # stop each session's worker (IDs or unique prefixes); chi --resume ID then starts a fresh one"
|
|
49
|
+
puts " archive ID... # hide sessions (and their delegates) from every list and keep them for good; unarchive ID... brings them back"
|
|
48
50
|
puts " delete [--force] ID... # delete sessions for good (IDs or unique prefixes); --force stops a live worker first"
|
|
49
51
|
puts " prune [--dry-run] [--days N] [--keep N] [--keep-status running,...] [--test-only]"
|
|
50
|
-
puts " clean [--dry-run] [--days N] # test sessions (SAMAGOTCHI_ENV=test, CI): all of them, or those older than N days"
|
|
52
|
+
puts " clean [--dry-run] [--days N] # test sessions (SAMAGOTCHI_ENV=test, CI) and leftover chi scratch ones: all of them, or those older than N days"
|
|
51
53
|
puts "Defaults: days=14 keep=500 keep_status=running (env overrides: SAMAGOTCHI_SESSION_RETENTION_DAYS etc.)"
|
|
52
54
|
exit 0
|
|
53
55
|
end
|
|
@@ -60,6 +62,7 @@ if ARGV[0] == "sessions"
|
|
|
60
62
|
order = nil
|
|
61
63
|
limit = nil
|
|
62
64
|
live = sessions_args.include?("--live")
|
|
65
|
+
include_archived = sessions_args.include?("--archived")
|
|
63
66
|
cwd = nil
|
|
64
67
|
format = nil
|
|
65
68
|
scope = nil
|
|
@@ -89,6 +92,9 @@ if ARGV[0] == "sessions"
|
|
|
89
92
|
when "delete"
|
|
90
93
|
require "samagotchi/session_delete_command"
|
|
91
94
|
exit Samagotchi::SessionDeleteCommand.new(sessions_args[1..]).run
|
|
95
|
+
when "archive", "unarchive"
|
|
96
|
+
require "samagotchi/session_archive_command"
|
|
97
|
+
exit Samagotchi::SessionArchiveCommand.new(sub, sessions_args[1..]).run
|
|
92
98
|
when "list"
|
|
93
99
|
unless format.nil? || %w[text json tsv].include?(format)
|
|
94
100
|
warn "Unknown format #{format.inspect}: use --format text|json|tsv"
|
|
@@ -107,7 +113,8 @@ if ARGV[0] == "sessions"
|
|
|
107
113
|
# and test runs stay out.
|
|
108
114
|
if live || cwd || (format && format != "text")
|
|
109
115
|
summaries = Samagotchi::SessionManager.session_summaries(
|
|
110
|
-
live: live, cwd: cwd, limit: limit || (live ? 10 : nil), include_tests: false, project_root: project
|
|
116
|
+
live: live, cwd: cwd, limit: limit || (live ? 10 : nil), include_tests: false, project_root: project,
|
|
117
|
+
include_archived: include_archived
|
|
111
118
|
)
|
|
112
119
|
case format
|
|
113
120
|
when "json"
|
|
@@ -115,7 +122,11 @@ if ARGV[0] == "sessions"
|
|
|
115
122
|
# or nil; recap: the saved recap's first sentence, or nil; project:
|
|
116
123
|
# its git project's root, or nil
|
|
117
124
|
# parent_id: the session that delegated it (the delegate tool), or nil
|
|
118
|
-
|
|
125
|
+
# archived: hidden from the lists (only with --archived can it be true)
|
|
126
|
+
# scratch: a `chi scratch` session (deleted when its REPL ends, a
|
|
127
|
+
# leftover one at the next sweep)
|
|
128
|
+
# ctx_pct: how full the context was after the last turn, or nil
|
|
129
|
+
keys = %i[id short_id desc cwd project updated_at live busy owner recap parent_id archived scratch ctx_pct]
|
|
119
130
|
puts JSON.generate(summaries.map { |summary| summary.slice(*keys) })
|
|
120
131
|
when "tsv"
|
|
121
132
|
summaries.each { |summary| puts "#{summary[:id]}\t#{summary[:desc]}" }
|
|
@@ -131,7 +142,10 @@ if ARGV[0] == "sessions"
|
|
|
131
142
|
end
|
|
132
143
|
# A delegated session points at its parent.
|
|
133
144
|
child = summary[:parent_short_id] ? " ↳ #{summary[:parent_short_id]}" : ""
|
|
134
|
-
|
|
145
|
+
flag = summary[:scratch] ? " [scratch]" : ""
|
|
146
|
+
flag += " [archived]" if summary[:archived]
|
|
147
|
+
ctx = Samagotchi::SessionMetrics.context_label(summary[:ctx_pct])
|
|
148
|
+
puts "#{summary[:id]} #{state.ljust(8)} #{ctx.ljust(8)} #{summary[:updated_at]} #{desc}#{flag}#{child}"
|
|
135
149
|
end
|
|
136
150
|
if project
|
|
137
151
|
puts "#{summaries.empty? ? "" : "\n"}#{scope_note.call(summaries.size)}"
|
|
@@ -143,7 +157,8 @@ if ARGV[0] == "sessions"
|
|
|
143
157
|
end
|
|
144
158
|
sort ||= "updated_at"
|
|
145
159
|
order ||= "desc"
|
|
146
|
-
sessions = Samagotchi::SessionManager.list_sessions(sort: sort, order: order, limit: limit, project_root: project
|
|
160
|
+
sessions = Samagotchi::SessionManager.list_sessions(sort: sort, order: order, limit: limit, project_root: project,
|
|
161
|
+
include_archived: include_archived)
|
|
147
162
|
# The saved recap's first sentence, else the last prompt, cut as before.
|
|
148
163
|
state_dir = Samagotchi::Session.default_state_dir
|
|
149
164
|
list_text = lambda do |s|
|
|
@@ -152,9 +167,14 @@ if ARGV[0] == "sessions"
|
|
|
152
167
|
end
|
|
153
168
|
# A delegated session points at its parent: ↳ <parent's short id>.
|
|
154
169
|
row = lambda do |s|
|
|
155
|
-
flag = s.test_run ? " [test]" : ""
|
|
170
|
+
flag = s.scratch ? " [scratch]" : (s.test_run ? " [test]" : "")
|
|
171
|
+
flag += " [archived]" if s.archived
|
|
156
172
|
child = s.parent_id ? " ↳ #{s.parent_id[0, 8]}" : ""
|
|
157
|
-
"
|
|
173
|
+
# How full the context was after the last turn: "ctx 12%", blank when unknown.
|
|
174
|
+
ctx = Samagotchi::SessionMetrics.context_label(
|
|
175
|
+
Samagotchi::SessionMetrics.saved_context_pct(Samagotchi::Session.session_dir(s.id, state_dir: state_dir))
|
|
176
|
+
)
|
|
177
|
+
"#{s.id} #{s.status.ljust(8)} #{ctx.ljust(8)} #{s.updated_at} #{list_text.call(s)}#{flag}#{child}"
|
|
158
178
|
end
|
|
159
179
|
if project
|
|
160
180
|
sessions.each { |s| puts row.call(s) }
|
|
@@ -214,7 +234,7 @@ if ARGV[0] == "sessions"
|
|
|
214
234
|
end
|
|
215
235
|
exit 0
|
|
216
236
|
else
|
|
217
|
-
warn "Unknown sessions subcommand: #{sub}. Use: list, stop, delete, prune, clean"
|
|
237
|
+
warn "Unknown sessions subcommand: #{sub}. Use: list, stop, archive, unarchive, delete, prune, clean"
|
|
218
238
|
exit 1
|
|
219
239
|
end
|
|
220
240
|
end
|
|
@@ -231,6 +251,12 @@ if ARGV[0] == "send"
|
|
|
231
251
|
exit Samagotchi::SendCommand.new(ARGV[1..] || []).run
|
|
232
252
|
end
|
|
233
253
|
|
|
254
|
+
# `chi bootstrap`: find a model server and write config.yml, before OptionParser (--model, --name are its own)
|
|
255
|
+
if ARGV[0] == "bootstrap"
|
|
256
|
+
require "samagotchi/bootstrap_command"
|
|
257
|
+
exit Samagotchi::BootstrapCommand.new(ARGV[1..] || []).run
|
|
258
|
+
end
|
|
259
|
+
|
|
234
260
|
# `chi desktop`: the native "Send to chi" helper, before OptionParser (its own flags)
|
|
235
261
|
if ARGV[0] == "desktop"
|
|
236
262
|
require "samagotchi/desktop_command"
|
|
@@ -881,7 +907,13 @@ if ARGV[0] == "bundle"
|
|
|
881
907
|
exit 0
|
|
882
908
|
end
|
|
883
909
|
|
|
884
|
-
|
|
910
|
+
# `chi scratch`: the run options below, for a session that leaves nothing
|
|
911
|
+
# behind (TerminalUI scratch:). The word goes before OptionParser sees it.
|
|
912
|
+
scratch = ARGV[0] == "scratch"
|
|
913
|
+
ARGV.shift if scratch
|
|
914
|
+
|
|
915
|
+
# no_interrupt / no_default_input start from config.yml or the env; the flags below turn them on.
|
|
916
|
+
options = { verbose: false, no_interrupt: Samagotchi::Config.get("no_interrupt"), non_interactive: false, no_default_input: Samagotchi::Config.get("no_default_input"), web: false, web_port: nil, web_open: false, web_markdown: nil, web_turn_view: nil }
|
|
885
917
|
cli_overrides = {}
|
|
886
918
|
|
|
887
919
|
if ARGV.any? { |arg| arg == "--backend" || arg.start_with?("--backend=") }
|
|
@@ -908,10 +940,12 @@ end
|
|
|
908
940
|
parser = OptionParser.new do |opts|
|
|
909
941
|
opts.banner = <<~BANNER
|
|
910
942
|
Usage: chi [options]
|
|
943
|
+
chi bootstrap [HOST[:PORT]|URL] first setup: find the model server, write config.yml
|
|
944
|
+
chi scratch [options] a one-time session in this terminal: nothing is kept
|
|
911
945
|
chi web [--port PORT] [--open] [--scope=all] the web UI
|
|
912
|
-
chi sessions <list|stop|delete|prune|clean>
|
|
946
|
+
chi sessions <list|stop|archive|unarchive|delete|prune|clean>
|
|
913
947
|
chi note [--source NAME] [-m TEXT] ID...|--all background context for sessions
|
|
914
|
-
chi send [-m TEXT] ID
|
|
948
|
+
chi send [-m TEXT] ID...|--new a message into sessions, as if typed there
|
|
915
949
|
chi bundle <install|upgrade|uninstall|status|diff|list|build>
|
|
916
950
|
chi desktop <install|upgrade|uninstall|status> macOS "Send to chi" helper
|
|
917
951
|
chi self version, paths, model and bundles
|
|
@@ -938,7 +972,8 @@ parser = OptionParser.new do |opts|
|
|
|
938
972
|
"every session outside a repo) or all") { |v| options[:scope] = v }
|
|
939
973
|
# Accepted values of string entries that the registry doesn't type as enums.
|
|
940
974
|
value_hints = { "thinking.ui" => "spinner|off", "status.line" => "on|off", "status.width_mode" => "terminal_cap|fixed",
|
|
941
|
-
"web.host" => "127.0.0.1|::1|localhost", "recap.sentences" => "N|N-M"
|
|
975
|
+
"web.host" => "127.0.0.1|::1|localhost", "recap.sentences" => "N|N-M",
|
|
976
|
+
"web.annotate_presets" => "text|text" }
|
|
942
977
|
# Universal config flags (implicit convention: ENV SAMAGOTCHI_* ↔ YAML dotted ↔ CLI --kebab)
|
|
943
978
|
# Generated from Samagotchi::Config registry — Option A: snake leaf in YAML, kebab in CLI.
|
|
944
979
|
Samagotchi::Config.cli_entries.each do |entry|
|
|
@@ -981,12 +1016,22 @@ end
|
|
|
981
1016
|
# The subcommands with their own parsers ran above; what parse! leaves is
|
|
982
1017
|
# `web` or nothing. Anything else is a typo, not a prompt: refuse it rather
|
|
983
1018
|
# than start a session.
|
|
984
|
-
|
|
1019
|
+
web = ARGV.first == "web" && !scratch
|
|
1020
|
+
stray = web ? ARGV[1] : ARGV.first
|
|
985
1021
|
if stray
|
|
986
|
-
warn "Error: #{
|
|
1022
|
+
warn "Error: #{web || scratch ? "unexpected argument" : "unknown command"} #{stray} (see chi --help)"
|
|
987
1023
|
exit 1
|
|
988
1024
|
end
|
|
989
1025
|
|
|
1026
|
+
# A scratch session is new, runs here and goes when it ends.
|
|
1027
|
+
if scratch
|
|
1028
|
+
flag = { resume: "--resume", attach: "--attach", shared: "--shared" }.find { |key, _flag| options[key] }&.last
|
|
1029
|
+
if flag
|
|
1030
|
+
warn "Error: chi scratch starts a new session in this terminal and keeps nothing; it can't take #{flag}"
|
|
1031
|
+
exit 1
|
|
1032
|
+
end
|
|
1033
|
+
end
|
|
1034
|
+
|
|
990
1035
|
# Apply CLI precedence: CLI > ENV > file
|
|
991
1036
|
unless cli_overrides.empty?
|
|
992
1037
|
Samagotchi::Config.reload!(cli_overrides: cli_overrides)
|
|
@@ -1039,7 +1084,8 @@ if options[:web]
|
|
|
1039
1084
|
turn_view = options[:web_turn_view]
|
|
1040
1085
|
turn_view = Samagotchi::Config.get("web.turn_view") if turn_view.nil?
|
|
1041
1086
|
exit Samagotchi::Web::Server.launch(port: port, scope: options[:scope] || "project", open_browser: options[:web_open],
|
|
1042
|
-
markdown: markdown, turn_view: turn_view
|
|
1087
|
+
markdown: markdown, turn_view: turn_view,
|
|
1088
|
+
annotate_presets: Samagotchi::Config.get("web.annotate_presets"))
|
|
1043
1089
|
end
|
|
1044
1090
|
|
|
1045
1091
|
# --resume and --attach take a unique id prefix too, like git.
|
|
@@ -1050,6 +1096,22 @@ rescue Samagotchi::Session::AmbiguousId => e
|
|
|
1050
1096
|
exit 1
|
|
1051
1097
|
end
|
|
1052
1098
|
|
|
1099
|
+
# A `chi scratch` session a killed REPL left behind would turn into a kept
|
|
1100
|
+
# one if opened again; the next sweep deletes it.
|
|
1101
|
+
%i[resume attach].each do |key|
|
|
1102
|
+
next unless options[key]
|
|
1103
|
+
|
|
1104
|
+
leftover = begin
|
|
1105
|
+
Samagotchi::Session.load(options[key]).scratch
|
|
1106
|
+
rescue ArgumentError
|
|
1107
|
+
false # no such session: the launch says so
|
|
1108
|
+
end
|
|
1109
|
+
next unless leftover
|
|
1110
|
+
|
|
1111
|
+
warn "Error: that's a leftover scratch session; it is deleted at the next sweep (chi sessions clean)"
|
|
1112
|
+
exit 1
|
|
1113
|
+
end
|
|
1114
|
+
|
|
1053
1115
|
# --memory and --mute names are checked here, before the launch: a worker's
|
|
1054
1116
|
# stderr goes nowhere, so its engine's warnings would not be seen. Warnings
|
|
1055
1117
|
# only; the session starts either way.
|
|
@@ -1082,7 +1144,11 @@ if options[:memories] || options[:muted]
|
|
|
1082
1144
|
end
|
|
1083
1145
|
|
|
1084
1146
|
require "samagotchi/launch_mode"
|
|
1085
|
-
launch, launch_note =
|
|
1147
|
+
launch, launch_note = if scratch
|
|
1148
|
+
[:repl, nil]
|
|
1149
|
+
else
|
|
1150
|
+
Samagotchi::LaunchMode.resolve(options, shared_config: Samagotchi::Config.get("session.shared"))
|
|
1151
|
+
end
|
|
1086
1152
|
warn launch_note if launch_note
|
|
1087
1153
|
|
|
1088
1154
|
if launch == :attached
|
|
@@ -1124,7 +1190,8 @@ begin
|
|
|
1124
1190
|
memories: options[:memories] || [],
|
|
1125
1191
|
muted_memories: options[:muted] || [],
|
|
1126
1192
|
non_interactive: options[:non_interactive],
|
|
1127
|
-
model_name: options[:model]
|
|
1193
|
+
model_name: options[:model],
|
|
1194
|
+
scratch: scratch
|
|
1128
1195
|
).run
|
|
1129
1196
|
rescue Samagotchi::TerminalUI::SessionBusy, Samagotchi::TerminalUI::SessionNotFound, Samagotchi::ModelProfile::MissingModel => e
|
|
1130
1197
|
warn "Error: #{e.message}"
|
data/docs/cli.md
CHANGED
|
@@ -2,22 +2,60 @@
|
|
|
2
2
|
|
|
3
3
|
## Commands
|
|
4
4
|
|
|
5
|
+
- `chi bootstrap [HOST[:PORT]|URL]` — first setup: find the model server (llama.cpp or OpenAI-compatible), pick the model, send a test request and write config.yml, or add a `hosts:` entry to an existing one (see [First setup](#first-setup))
|
|
5
6
|
- `chi` — start a session in a background worker and attach the terminal to it, so the Web UI (or another terminal) can share it (see [Sharing a session](#sharing-a-session))
|
|
6
7
|
- `chi -p "your prompt"` — run a prompt, then stay attached
|
|
7
8
|
- `chi -p "your prompt" --non-interactive` — run a prompt, print the answer, exit
|
|
8
9
|
- `chi --resume <session-id>` — resume a prior session (in its worker)
|
|
9
10
|
- `chi --no-shared [--resume <session-id>]` — the plain in-process REPL instead, for this run
|
|
11
|
+
- `chi scratch [options]` — a one-time session in the plain in-process REPL, in this folder, that leaves nothing behind (see [Scratch sessions](#scratch-sessions))
|
|
10
12
|
- `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
|
|
11
|
-
- A session id can be shortened to any unique prefix (like git): `chi --attach 2ea8`. `--resume`, `--attach`, `sessions stop` and `sessions delete` take one; an ambiguous prefix lists the sessions it matches.
|
|
13
|
+
- 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.
|
|
12
14
|
- `chi web [--port 4567] [--open] [--scope=all]` — start the Web UI (single localhost port session control plane) on this git project's sessions (`--scope=all`, or a folder in no repo: 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
15
|
- `chi web --web-markdown` — opt in to sanitized Markdown rendering for completed assistant messages
|
|
14
16
|
- `chi web --no-web-turn-view` — show turns as the classic row of bubbles instead of the default turn view (each turn as one block of steps, the running one at the bottom); `?view=turn|chat` on the page URL overrides it (see [Web turn view](#web-turn-view))
|
|
15
|
-
- `chi sessions list|stop|delete|prune|clean` — manage persisted sessions; `list` shows this git project's, `list --scope=all` every one, a delegated session with `↳ <parent
|
|
17
|
+
- `chi sessions list|stop|archive|unarchive|delete|prune|clean` — manage persisted sessions; `list` shows this git project's, `list --scope=all` every one, a delegated session with `↳ <parent>`, `list --archived` the archived ones too (see [Sessions](sessions.md))
|
|
16
18
|
- `chi note [--source NAME] [-m TEXT] (ID|PREFIX)... | --all` — add a context note (TEXT or stdin) to sessions: background the model sees on its next turn; it starts no turn (see [Sessions: Context notes](sessions.md#context-notes))
|
|
17
|
-
- `chi send [-m TEXT] (ID|PREFIX)...` — send a message to sessions as if typed there: a turn starts (or a running one picks it up); piped stdin goes above `-m` as quoted context (see [Sessions: Sending a message](sessions.md#sending-a-message))
|
|
19
|
+
- `chi send [-m TEXT] (ID|PREFIX)...` — send a message to sessions as if typed there: a turn starts (or a running one picks it up); piped stdin goes above `-m` as quoted context (see [Sessions: Sending a message](sessions.md#sending-a-message)); `--new` starts a session with it instead, and `--wait` prints the answer (`--wait ID` with no message waits for the next reply without sending; see [Starting a session](sessions.md#starting-a-session))
|
|
18
20
|
- `chi desktop install|upgrade|uninstall|status` — the macOS "Send to chi" helper: a Service and a ⌃⌥⌘N hotkey that send text to live sessions as context notes (see [Desktop helper](desktop.md))
|
|
19
21
|
- `chi self` — print version, source dir (checkout or installed gem), config/memory/session paths, model/host and bundles
|
|
20
|
-
- `chi bundle install|upgrade|uninstall|status|diff|list|build` — 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 (see [Guardrails](guardrails.md), [Plugins](plugins.md#the-btw-bundle), [the mcp bundle](plugins.md#the-mcp-bundle)
|
|
22
|
+
- `chi bundle install|upgrade|uninstall|status|diff|list|build` — 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 (see [Guardrails](guardrails.md), [Plugins](plugins.md#the-btw-bundle), [the mcp bundle](plugins.md#the-mcp-bundle) [the loop-guard bundle](plugins.md#the-loop-guard-bundle) and [the check-in bundle](plugins.md#the-check-in-bundle))
|
|
23
|
+
|
|
24
|
+
### First setup
|
|
25
|
+
|
|
26
|
+
`chi bootstrap TARGET` names the model server and writes the config for it:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
chi bootstrap 192.168.1.29:8081 # host:port (port 8080 when none)
|
|
30
|
+
chi bootstrap https://openrouter.ai/api/v1 --key-env OPENROUTER_API_KEY
|
|
31
|
+
chi bootstrap # try localhost 8080, 11434, 1234, 8000
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
- **What it is.** llama.cpp's `/props` answering means the native API (no
|
|
35
|
+
`api:`); otherwise `GET /v1/models` answering means an OpenAI-compatible
|
|
36
|
+
server (`api: openai`). A URL with a path is the API base as given
|
|
37
|
+
(`…/api/v1`); a domain without a scheme is tried over https, then http.
|
|
38
|
+
Each request is tried once, with 5 s timeouts, so a refused port answers
|
|
39
|
+
at once.
|
|
40
|
+
- **The key.** A server that answers 401/403 wants an API key: `--key-env VAR`
|
|
41
|
+
names the environment variable holding it (on a terminal chi asks for the
|
|
42
|
+
name). Only the variable's name is written, never the key.
|
|
43
|
+
- **The model.** One model is taken; with several, `--model ID` picks one
|
|
44
|
+
(a terminal gets a numbered list, a script the ids and exit 2). A llama.cpp
|
|
45
|
+
server also shows its context size and the prompt profile its chat template
|
|
46
|
+
matches.
|
|
47
|
+
- **The test.** One short chat request ("test: answered in 1.1 s"); `--no-test`
|
|
48
|
+
skips it. A failed test still writes the config, says so and exits 1.
|
|
49
|
+
- **The file.** With no config.yml it writes a small commented one:
|
|
50
|
+
`default.model` as `<host>:<model>` and one `hosts:` entry named `local`
|
|
51
|
+
(localhost), `lan` (an IP) or after the domain (`openrouter`); `--name`
|
|
52
|
+
sets it. An existing file is left as it is apart from the new entry, added
|
|
53
|
+
at the end of its `hosts:` block (it gets a `hosts:` block, with a
|
|
54
|
+
`default` entry for its `server:` first, when it has none), after a backup
|
|
55
|
+
to `config.yml.bak-<time>`. `default.model` is set only when the file has
|
|
56
|
+
none. A server already in the file writes nothing; a file in YAML flow style
|
|
57
|
+
or with anchors gets the lines printed to paste instead. `--dry-run` shows
|
|
58
|
+
what it would write.
|
|
21
59
|
|
|
22
60
|
## Flags
|
|
23
61
|
|
|
@@ -64,6 +102,7 @@ it sees one.
|
|
|
64
102
|
| `chi --resume ID -p "next step" --non-interactive` | Resume `ID`, run the prompt, save, exit. |
|
|
65
103
|
| `chi --resume ID -p "next step"` | Resume `ID`, send the prompt, **stay attached** to that session. |
|
|
66
104
|
| `chi --no-shared [...]` | The same, in the plain in-process REPL. |
|
|
105
|
+
| `chi scratch [-p ...] [--non-interactive]` | A new session in the plain REPL, deleted when it ends. |
|
|
67
106
|
|
|
68
107
|
Notes:
|
|
69
108
|
|
|
@@ -75,6 +114,28 @@ Notes:
|
|
|
75
114
|
- Non-interactive runs (`-p` with `--non-interactive`, or bare `--non-interactive`)
|
|
76
115
|
print only the final result output — no spinner, status line, or REPL.
|
|
77
116
|
|
|
117
|
+
### Scratch sessions
|
|
118
|
+
|
|
119
|
+
`chi scratch` is `chi --no-shared` for a session you won't keep: a quick
|
|
120
|
+
question, a try-out. It takes the run options (`-p`, `--non-interactive`,
|
|
121
|
+
`--model`, `--profile`, `--memory`, `--mute`, `-v`, …); `--resume`, `--attach`
|
|
122
|
+
and `--shared` are refused. Its first line says it is a scratch session.
|
|
123
|
+
|
|
124
|
+
- The session is deleted however it ends: `/exit`, Ctrl-D, Ctrl-C at the
|
|
125
|
+
prompt, an error, SIGTERM or SIGHUP. There is no recap, and the lines typed
|
|
126
|
+
are not added to the prompt history.
|
|
127
|
+
- It never shows in `chi web`. A process killed with `kill -9` leaves its
|
|
128
|
+
session behind, marked `"scratch": true` in its session.json: `chi sessions
|
|
129
|
+
list` shows it as `[scratch]`, and the next sweep or `chi sessions clean`
|
|
130
|
+
deletes it. `chi --resume` and `--attach` refuse it (exit 1), so it never
|
|
131
|
+
turns into a kept session.
|
|
132
|
+
- Memories are read and preloaded as usual, but nothing is saved: `memory_write`
|
|
133
|
+
answers "scratch session: nothing is saved", and `write`/`edit` into the
|
|
134
|
+
memories folder are denied (a guardrail, rule `scratch-session`). `execute`
|
|
135
|
+
can still write files anywhere, memories included.
|
|
136
|
+
- No child sessions: the `delegate` tools are not offered, and a plugin's
|
|
137
|
+
`ctx.sessions.fork` (btw's side session) refuses, since they would outlive it.
|
|
138
|
+
|
|
78
139
|
### Sharing a session
|
|
79
140
|
|
|
80
141
|
Plain `chi` runs the session in a background worker and attaches the terminal
|
|
@@ -122,7 +183,8 @@ A worker nobody uses exits after `session.idle_exit_minutes` (30 by default, `0`
|
|
|
122
183
|
for never): no turn running or queued, no UI attached (an open web tab or an
|
|
123
184
|
attached terminal counts, even an idle one) and no reminder registered. The next
|
|
124
185
|
prompt or `--attach` wakes a new worker with the conversation intact; `/stats`
|
|
125
|
-
|
|
186
|
+
keeps counting from the turns before (they are saved in the session's
|
|
187
|
+
`analytics.json`, one record per turn), and the recap is saved with the session.
|
|
126
188
|
|
|
127
189
|
A session you leave with nothing in it (no prompt sent, no `/model` switch, no
|
|
128
190
|
note or image) is deleted as its worker exits, and `/exit` says so; set
|
|
@@ -134,6 +196,21 @@ a `chi --resume ID` after it starts a fresh one. A worker still running an
|
|
|
134
196
|
older chi (from before an upgrade) takes turns but not commands; the attached
|
|
135
197
|
terminal and the Web UI say so, with that restart line.
|
|
136
198
|
|
|
199
|
+
`chi sessions archive ID...` hides sessions from every list (the terminal's,
|
|
200
|
+
the web's, `list_sessions`) and keeps them for good: the retention sweep never
|
|
201
|
+
deletes an archived session, nor counts it. Its delegates go with it. A live
|
|
202
|
+
worker is stopped first; a session running a turn (or with a delegate running
|
|
203
|
+
one), open in a plain REPL, or a `chi scratch` one is refused. `chi sessions
|
|
204
|
+
list --archived` shows them too, marked `[archived]` (`archived: true` in
|
|
205
|
+
`--format json`); `chi sessions unarchive ID...` brings them back, and so does
|
|
206
|
+
a message you send to one (the web, an attached terminal, `chi send`), but not
|
|
207
|
+
a delegate's follow-up or a reminder. The web archives from the info bar
|
|
208
|
+
(`archive`, before `stop`); "include archived" by the all-sessions search
|
|
209
|
+
finds archived sessions. `/archive` in a terminal leaves the session and
|
|
210
|
+
archives it (an empty session is discarded instead; `chi scratch` refuses
|
|
211
|
+
it). See
|
|
212
|
+
[Sessions](sessions.md#archiving-a-session).
|
|
213
|
+
|
|
137
214
|
`chi sessions delete [--force] ID...` deletes sessions for good: the
|
|
138
215
|
session file and its whole directory (notes, images, queued input). Each id
|
|
139
216
|
(or unique prefix) gets one line: `deleted`, or `refused` with the reason. A
|
|
@@ -316,6 +393,32 @@ and `?view=turn` the turn view, whatever the config says; the parameter is dropp
|
|
|
316
393
|
when you switch between the project and all-sessions views. The terminal
|
|
317
394
|
UIs are not affected.
|
|
318
395
|
|
|
396
|
+
### Web annotate presets
|
|
397
|
+
|
|
398
|
+
Selecting text in an answer, a thinking block, a tool row or one of your
|
|
399
|
+
messages shows **Annotate**, which quotes the selection into the composer
|
|
400
|
+
for a note under it. Next to it sit quick replies, by default `Agreed` and
|
|
401
|
+
`Could you please elaborate?`: a click quotes the selection the same way
|
|
402
|
+
with that text already written as the note. It only fills the composer,
|
|
403
|
+
never sends, so you can collect several quotes and edit before sending.
|
|
404
|
+
|
|
405
|
+
The list is `web.annotate_presets`, `|`-separated (at most five; a preset
|
|
406
|
+
can't contain `|`; a YAML list works too):
|
|
407
|
+
|
|
408
|
+
```sh
|
|
409
|
+
chi web --web-annotate-presets "Yes|No|Why this way?"
|
|
410
|
+
chi web --web-annotate-presets "" # only Annotate
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
```yaml
|
|
414
|
+
web:
|
|
415
|
+
annotate_presets: "Agreed|Could you please elaborate?"
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
`SAMAGOTCHI_WEB_ANNOTATE_PRESETS` overrides the file, but an empty value
|
|
419
|
+
there means the default, not "none": use `""` in the file or on the command
|
|
420
|
+
line. A `chi web` that already runs keeps its list; restart it.
|
|
421
|
+
|
|
319
422
|
## Runtime Model Switch (Assist Mode)
|
|
320
423
|
|
|
321
424
|
In interactive assist mode, you can switch the request model without restarting:
|