tycho-cli 0.0.5__tar.gz → 0.0.6__tar.gz
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.
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/.gitignore +1 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/PKG-INFO +1 -1
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/test_update.py +93 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tycho/__init__.py +1 -1
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tycho/cli.py +40 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tycho/hook.py +33 -5
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tycho-target.ps1.example +4 -2
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/.github/workflows/ci.yml +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/.github/workflows/release.yml +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/.tycho.toml +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/CONTRIBUTING.md +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/LICENSE +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/README.md +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/docs/bundler-spike.md +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/docs/harness-support.md +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/docs/hooks.md +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/docs/transcript-structures.md +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/docs/tycho-web.md +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/packaging/homebrew/tycho.rb +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/packaging/npm/bin/tycho.js +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/packaging/npm/package.json +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/pyproject.toml +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/conftest.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/fixtures/codex_transcript_sample.jsonl +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/fixtures/cursor_stop_payload.json +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/fixtures/cursor_transcript_sample.jsonl +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/fixtures/eval/claude_masked_red_suite.jsonl +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/fixtures/eval/codex_masked_red_suite.jsonl +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/fixtures/eval/cursor_masked_red_suite.jsonl +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/fixtures/eval/opencode_masked_red_suite.json +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/fixtures/opencode_transcript_sample.json +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/fixtures/transcript_multiturn.jsonl +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/fixtures/transcript_sample.jsonl +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/test_count.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/test_doctor.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/test_e2e_install.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/test_eval.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/test_init_safety.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/test_m1.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/test_m2.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/test_m3.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/test_m4.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/test_m5.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/test_m6.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/test_relay.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/test_scope.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/test_smoke.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/test_status.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tests/test_turn_scope.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tycho/__main__.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tycho/astdiff.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tycho/checks.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tycho/config.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tycho/doctor.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tycho/events.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tycho/fsstate.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tycho/gitstate.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tycho/harness.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tycho/init.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tycho/model.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tycho/opencode.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tycho/report.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tycho/runlog.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tycho/state.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tycho/status.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tycho/verify.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/tycho/version.py +0 -0
- {tycho_cli-0.0.5 → tycho_cli-0.0.6}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tycho-cli
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.6
|
|
4
4
|
Summary: Tycho — a free, offline, open-source local verifier that proves an AI agent actually did what it claims, from git, the filesystem, process exit codes, and the harness event stream. No account, no LLM in the trust path.
|
|
5
5
|
Project-URL: Homepage, https://swail.dev/tycho
|
|
6
6
|
Project-URL: Repository, https://github.com/swail-labs/tycho
|
|
@@ -151,6 +151,7 @@ def test_update_offline_is_reported_not_fatal(_online, monkeypatch, capsys):
|
|
|
151
151
|
def test_update_prints_the_upgrade_command_then_runs_it(_online, monkeypatch, capsys):
|
|
152
152
|
import subprocess
|
|
153
153
|
|
|
154
|
+
monkeypatch.setattr(cli.sys, "platform", "linux") # POSIX runs the upgrade in place
|
|
154
155
|
_fetches(monkeypatch, "9.9.9")
|
|
155
156
|
monkeypatch.setattr(cli, "_upgrade_command", lambda: ["echo", "upgrading"])
|
|
156
157
|
ran = {}
|
|
@@ -165,6 +166,23 @@ def test_update_prints_the_upgrade_command_then_runs_it(_online, monkeypatch, ca
|
|
|
165
166
|
assert ran["cmd"] == ["echo", "upgrading"]
|
|
166
167
|
|
|
167
168
|
|
|
169
|
+
def test_update_on_windows_defers_the_upgrade_past_process_exit(_online, monkeypatch, capsys):
|
|
170
|
+
# A running .exe can't have its own shim replaced on Windows, so the upgrade must be deferred
|
|
171
|
+
# to a detached child that waits for us to exit — never run synchronously (TYCHO-108 follow-up).
|
|
172
|
+
import subprocess
|
|
173
|
+
|
|
174
|
+
monkeypatch.setattr(cli.sys, "platform", "win32")
|
|
175
|
+
_fetches(monkeypatch, "9.9.9")
|
|
176
|
+
monkeypatch.setattr(cli, "_upgrade_command", lambda: ["uv", "tool", "upgrade", "tycho-cli"])
|
|
177
|
+
monkeypatch.setattr(subprocess, "run", lambda *a, **k: pytest.fail("must not upgrade in-process on Windows"))
|
|
178
|
+
spawned = {}
|
|
179
|
+
monkeypatch.setattr(cli, "_spawn_deferred_upgrade", lambda cmd: spawned.setdefault("cmd", list(cmd)))
|
|
180
|
+
assert cli.main(["update"]) == cli.ExitCode.OK
|
|
181
|
+
out = capsys.readouterr().out
|
|
182
|
+
assert "9.9.9" in out and "once this process exits" in out
|
|
183
|
+
assert spawned["cmd"] == ["uv", "tool", "upgrade", "tycho-cli"]
|
|
184
|
+
|
|
185
|
+
|
|
168
186
|
@pytest.mark.parametrize("prefix", ["/home/u/.local/pipx/venvs/tycho-cli",
|
|
169
187
|
"/home/u/.local/share/uv/tools/tycho-cli",
|
|
170
188
|
"/usr"]) # pipx, uv, plain-pip branches
|
|
@@ -248,3 +266,78 @@ def test_session_start_never_raises_and_prints_nothing_on_error(monkeypatch, cap
|
|
|
248
266
|
_stdin(monkeypatch)
|
|
249
267
|
assert hook.session_start() == 0 # a bootup hook must never break the session
|
|
250
268
|
assert capsys.readouterr().out == ""
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
# --- Stop-hook update notice (TYCHO-116) -------------------------------------
|
|
272
|
+
#
|
|
273
|
+
# The Stop hook appends a human-only "newer Tycho available" line to the verdict the user is
|
|
274
|
+
# already reading — cache-only (no network on the hot path), never in the model-facing
|
|
275
|
+
# additionalContext, and suppressed where there's no human-only channel (Cursor).
|
|
276
|
+
|
|
277
|
+
from pathlib import Path as _Path # noqa: E402
|
|
278
|
+
|
|
279
|
+
_CLAUDE_FIXTURE = _Path(__file__).parent / "fixtures" / "transcript_sample.jsonl"
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def _claude_stop(repo) -> str:
|
|
283
|
+
import json
|
|
284
|
+
return json.dumps({"cwd": str(repo), "transcript_path": str(_CLAUDE_FIXTURE.resolve())})
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def test_stop_hook_appends_update_line_to_human_output(_online, tmp_path):
|
|
288
|
+
from tycho import hook
|
|
289
|
+
state.write_update_cache(latest="9.9.9", checked_at=time.time())
|
|
290
|
+
out = hook.run(_claude_stop(tmp_path))
|
|
291
|
+
assert out is not None and "9.9.9" in out["systemMessage"] # verdict + update line, together
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def test_stop_hook_update_line_is_never_model_facing(_online, tmp_path):
|
|
295
|
+
# With the relay on there IS an additionalContext (model-facing) copy — the update line must
|
|
296
|
+
# ride only the human systemMessage, never that (TYCHO-35: don't tell the model to self-update).
|
|
297
|
+
from tycho import hook
|
|
298
|
+
state.set_relay_enabled(tmp_path, True)
|
|
299
|
+
state.write_update_cache(latest="9.9.9", checked_at=time.time())
|
|
300
|
+
out = hook.run(_claude_stop(tmp_path))
|
|
301
|
+
assert "9.9.9" in out["systemMessage"]
|
|
302
|
+
assert "9.9.9" not in out["hookSpecificOutput"]["additionalContext"]
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def test_stop_hook_silent_when_up_to_date(_online, tmp_path):
|
|
306
|
+
from tycho import hook
|
|
307
|
+
state.write_update_cache(latest=tycho.__version__, checked_at=time.time())
|
|
308
|
+
out = hook.run(_claude_stop(tmp_path))
|
|
309
|
+
assert "newer Tycho" not in out["systemMessage"]
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def test_stop_hook_respects_dismissal(_online, tmp_path):
|
|
313
|
+
from tycho import hook
|
|
314
|
+
state.write_update_cache(latest="9.9.9", checked_at=time.time())
|
|
315
|
+
state.dismiss_update("9.9.9") # `tycho update --skip` waved this version off
|
|
316
|
+
out = hook.run(_claude_stop(tmp_path))
|
|
317
|
+
assert "9.9.9" not in out["systemMessage"]
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def test_stop_hook_silent_when_opted_out(tmp_path):
|
|
321
|
+
# conftest sets TYCHO_NO_UPDATE_CHECK=1; even a fresh cache with a newer version stays silent.
|
|
322
|
+
from tycho import hook
|
|
323
|
+
state.write_update_cache(latest="9.9.9", checked_at=time.time())
|
|
324
|
+
out = hook.run(_claude_stop(tmp_path))
|
|
325
|
+
assert out is not None and "9.9.9" not in out["systemMessage"]
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def test_stop_hook_update_notice_never_hits_the_network(_online, monkeypatch, tmp_path):
|
|
329
|
+
from tycho import hook
|
|
330
|
+
monkeypatch.setattr(version_mod, "_fetch", lambda: pytest.fail("Stop path must not hit the network"))
|
|
331
|
+
state.write_update_cache(latest="9.9.9", checked_at=time.time())
|
|
332
|
+
out = hook.run(_claude_stop(tmp_path))
|
|
333
|
+
assert "9.9.9" in out["systemMessage"] # served from the cache only
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def test_stop_hook_suffix_suppressed_without_a_human_channel(_online):
|
|
337
|
+
# Cursor: format_output is model-facing and notice_output is None, so a notice would reach the
|
|
338
|
+
# model — suppress it, exactly as the bootup notice does (TYCHO-35/72).
|
|
339
|
+
from types import SimpleNamespace
|
|
340
|
+
|
|
341
|
+
from tycho import hook
|
|
342
|
+
state.write_update_cache(latest="9.9.9", checked_at=time.time())
|
|
343
|
+
assert hook._update_suffix(SimpleNamespace(notice_output=None)) == ""
|
|
@@ -407,6 +407,20 @@ def _update(skip: bool) -> int:
|
|
|
407
407
|
print(f"tycho {__version__} is up to date.")
|
|
408
408
|
return ExitCode.OK
|
|
409
409
|
cmd = _upgrade_command()
|
|
410
|
+
if sys.platform == "win32":
|
|
411
|
+
# A running .exe can't have its own launcher shim replaced on Windows — an in-process
|
|
412
|
+
# `uv tool upgrade` fails to copy `…\.local\bin\tycho.exe` with os error 32 ("being used
|
|
413
|
+
# by another process"). So defer: a detached child waits for THIS process to exit (which
|
|
414
|
+
# releases the lock), then upgrades against a free shim. POSIX replaces a running exe fine.
|
|
415
|
+
try:
|
|
416
|
+
_spawn_deferred_upgrade(cmd)
|
|
417
|
+
print(f"Updating tycho {__version__} → {newest}. The upgrade runs once this process "
|
|
418
|
+
f"exits; re-open your shell and run `tycho --version` to confirm.")
|
|
419
|
+
return ExitCode.OK
|
|
420
|
+
except Exception as exc:
|
|
421
|
+
print(f"tycho: couldn't schedule the upgrade ({type(exc).__name__}). Run it yourself:\n {' '.join(cmd)}",
|
|
422
|
+
file=sys.stderr)
|
|
423
|
+
return ExitCode.OK
|
|
410
424
|
print(f"Updating tycho {__version__} → {newest}: {' '.join(cmd)}")
|
|
411
425
|
try:
|
|
412
426
|
import subprocess
|
|
@@ -418,6 +432,32 @@ def _update(skip: bool) -> int:
|
|
|
418
432
|
return ExitCode.OK
|
|
419
433
|
|
|
420
434
|
|
|
435
|
+
def _spawn_deferred_upgrade(cmd: Sequence[str]) -> None:
|
|
436
|
+
"""Windows only: run `cmd` in a detached process that first waits for us to exit, so the
|
|
437
|
+
running tycho.exe's shim is unlocked before the upgrade copies over it (TYCHO-108 follow-up).
|
|
438
|
+
|
|
439
|
+
Waits on THIS PID (deterministic, not a fixed sleep) with a 30s ceiling, then upgrades. Fully
|
|
440
|
+
detached — no console window, survives our exit — so `tycho update` returns immediately and the
|
|
441
|
+
swap happens a beat later against a released lock."""
|
|
442
|
+
import os
|
|
443
|
+
import subprocess
|
|
444
|
+
|
|
445
|
+
ppid = os.getpid()
|
|
446
|
+
call = "& " + " ".join("'" + str(a).replace("'", "''") + "'" for a in cmd) # PS call-operator
|
|
447
|
+
script = (
|
|
448
|
+
"$ErrorActionPreference='SilentlyContinue';"
|
|
449
|
+
f"Wait-Process -Id {ppid} -Timeout 30;"
|
|
450
|
+
f"{call}"
|
|
451
|
+
)
|
|
452
|
+
# CREATE_NO_WINDOW (no console flash) + CREATE_NEW_PROCESS_GROUP (survives our exit, ignores
|
|
453
|
+
# our console signals). NOT DETACHED_PROCESS — Windows rejects it alongside CREATE_NO_WINDOW.
|
|
454
|
+
flags = getattr(subprocess, "CREATE_NO_WINDOW", 0) | getattr(subprocess, "CREATE_NEW_PROCESS_GROUP", 0)
|
|
455
|
+
subprocess.Popen(
|
|
456
|
+
["powershell", "-NoProfile", "-NonInteractive", "-Command", script],
|
|
457
|
+
creationflags=flags, close_fds=True,
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
|
|
421
461
|
def _install(lines: Sequence[str]) -> int:
|
|
422
462
|
"""Print init/uninstall status lines; exit non-zero if we refused to touch a file.
|
|
423
463
|
|
|
@@ -76,8 +76,33 @@ def run(stdin_text: str) -> dict | None:
|
|
|
76
76
|
if harness.name == "opencode":
|
|
77
77
|
Path(transcript).unlink(missing_ok=True)
|
|
78
78
|
report = render(verdict, results)
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
# A human-only "newer Tycho available" line, appended to the verdict the user is already
|
|
80
|
+
# reading (TYCHO-116). Cache-only — never a network call on the hot Stop path.
|
|
81
|
+
update = _update_suffix(harness)
|
|
82
|
+
relayed = _relay_output(repo, harness, verdict, report, update)
|
|
83
|
+
return relayed if relayed is not None else harness.format_output(report + update)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _update_suffix(harness) -> str:
|
|
87
|
+
"""The Stop-hook update notice as a human-only suffix (leading blank line), or "" (TYCHO-116).
|
|
88
|
+
|
|
89
|
+
Only on harnesses with a human-only Stop channel: there, `format_output` writes the same field
|
|
90
|
+
`notice_output` does (systemMessage / message), so appending here stays human-facing. On Cursor
|
|
91
|
+
`format_output` is model-facing (`followup_message`) and `notice_output is None`, so a notice
|
|
92
|
+
would reach the model (TYCHO-35's rule) — suppress it, exactly as the bootup notice does.
|
|
93
|
+
|
|
94
|
+
Cache-only (`refresh_first=False`): the Stop path must never hit the network — the once-a-day
|
|
95
|
+
fetch is the doctor/init/bootup job. Respects opt-out + dismissal via `version.notice`. Never
|
|
96
|
+
raises: an update line is never worth breaking a Stop over."""
|
|
97
|
+
if harness.notice_output is None:
|
|
98
|
+
return ""
|
|
99
|
+
try:
|
|
100
|
+
from . import version as version_mod
|
|
101
|
+
|
|
102
|
+
note = version_mod.notice(refresh_first=False)
|
|
103
|
+
return f"\n\n{note}" if note else ""
|
|
104
|
+
except Exception:
|
|
105
|
+
return ""
|
|
81
106
|
|
|
82
107
|
|
|
83
108
|
# --- verdict relay (TYCHO-35, opt-in, default OFF) --------------------------
|
|
@@ -95,12 +120,15 @@ def run(stdin_text: str) -> dict | None:
|
|
|
95
120
|
# exactly the human-only output they always did (no agent context used, no extra generations).
|
|
96
121
|
|
|
97
122
|
|
|
98
|
-
def _relay_output(repo: Path, harness, verdict, report: str) -> dict | None:
|
|
123
|
+
def _relay_output(repo: Path, harness, verdict, report: str, update: str = "") -> dict | None:
|
|
99
124
|
"""The relay output dict, or None to fall through to the normal human-only output.
|
|
100
125
|
|
|
101
126
|
Returns None (unchanged behaviour) unless the relay is enabled here, the harness is Claude,
|
|
102
127
|
and the verdict is worth acting on. VERIFIED clears the streak and ends the turn (nothing to
|
|
103
128
|
fix); once the streak reaches relay_max() the relay goes quiet and hands control back.
|
|
129
|
+
|
|
130
|
+
`update` is the human-only update line (TYCHO-116): it rides the human-facing `systemMessage`
|
|
131
|
+
only, never `additionalContext` — the model-facing copy must not be told to go update Tycho.
|
|
104
132
|
"""
|
|
105
133
|
if harness.name != "claude" or not state.relay_enabled(repo):
|
|
106
134
|
return None
|
|
@@ -119,10 +147,10 @@ def _relay_output(repo: Path, harness, verdict, report: str) -> dict | None:
|
|
|
119
147
|
# relay (TYCHO-114); additionalContext is the model-facing copy that also drives the continuation.
|
|
120
148
|
manage = "[TYCHO] Relay is on — the agent keeps working until VERIFIED. Manage or turn it off: `tycho relay` (/tycho-relay)."
|
|
121
149
|
return {
|
|
122
|
-
"systemMessage": f"{report}\n\n{manage}",
|
|
150
|
+
"systemMessage": f"{report}\n\n{manage}{update}", # update line is human-only
|
|
123
151
|
"hookSpecificOutput": {
|
|
124
152
|
"hookEventName": "Stop",
|
|
125
|
-
"additionalContext": f"{report}\n\n{guard}",
|
|
153
|
+
"additionalContext": f"{report}\n\n{guard}", # model-facing — never the update line
|
|
126
154
|
},
|
|
127
155
|
}
|
|
128
156
|
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
# Toggle tycho hooks/statusline between local editable venv
|
|
1
|
+
# Toggle tycho hooks/statusline between the local editable venv (.venv, tracks live repo source)
|
|
2
|
+
# and the pinned release venv (.venv-release, a fixed non-editable install). Both live in the repo
|
|
3
|
+
# and are fully isolated — editing source or `tycho update`-ing one never touches the other.
|
|
2
4
|
# Copy to tycho-target.ps1 (gitignored) and run from the repo root.
|
|
3
5
|
# Usage: .\tycho-target.ps1 (flips) | .\tycho-target.ps1 local | .\tycho-target.ps1 release
|
|
4
6
|
$settings = Join-Path $PSScriptRoot ".claude\settings.json"
|
|
5
7
|
$local = (Join-Path $PSScriptRoot ".venv\Scripts\tycho.EXE").Replace("\", "/")
|
|
6
|
-
$release = (Join-Path $
|
|
8
|
+
$release = (Join-Path $PSScriptRoot ".venv-release\Scripts\tycho.EXE").Replace("\", "/")
|
|
7
9
|
|
|
8
10
|
$text = Get-Content $settings -Raw
|
|
9
11
|
$isLocal = $text.Contains($local)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|