sediment-cli 0.3.0__tar.gz → 0.5.0__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.
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/.gitignore +1 -3
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/PKG-INFO +5 -5
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/pyproject.toml +5 -5
- sediment_cli-0.5.0/sediment_cli/__init__.py +72 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/sediment_cli/_pi/README.md +1 -1
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/sediment_cli/_pi/package.json +2 -2
- sediment_cli-0.5.0/sediment_cli/agent_guide.txt +142 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/sediment_cli/attribution.py +38 -57
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/sediment_cli/cli.py +244 -55
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/sediment_cli/client.py +15 -14
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/sediment_cli/local_postgres.py +4 -1
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/sediment_cli/ui.py +5 -0
- sediment_cli-0.5.0/tests/test_agent_guide.py +102 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/test_cli.py +164 -12
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/test_cli_capture_authority.py +76 -30
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/test_cli_db.py +4 -1
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/test_cli_libpq_unavailable.py +13 -1
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/test_cli_remote.py +213 -1
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/test_delivery_transport.py +7 -2
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/test_e2e_quickstart.py +35 -10
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/test_installed_wheel.py +17 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/test_local_postgres.py +3 -2
- sediment_cli-0.5.0/tests/test_server_migration.py +207 -0
- sediment_cli-0.5.0/tests/testdata/help/db-check.txt +9 -0
- sediment_cli-0.5.0/tests/testdata/help/db-provision.txt +14 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/db-upgrade.txt +2 -1
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/db.txt +4 -1
- sediment_cli-0.5.0/tests/testdata/help/guide.txt +7 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/install.txt +2 -8
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/sediment.txt +2 -1
- sediment_cli-0.3.0/sediment_cli/__init__.py +0 -22
- sediment_cli-0.3.0/tests/testdata/help/db-provision.txt +0 -9
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/LICENSE +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/hatch_build.py +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/sediment_cli/_pi/LICENSE +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/sediment_cli/_pi/index.ts +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/sediment_cli/_pi/lib/contract.ts +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/sediment_cli/_pi/lib/process.ts +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/sediment_cli/_pi/lib/provider.ts +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/sediment_cli/_pi/lib/register.ts +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/sediment_cli/_pi/lib/retrieval.ts +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/sediment_cli/delivery.py +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/sediment_cli/evidence.py +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/sediment_cli/transcript.py +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/conftest.py +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/test_cli_evidence.py +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/test_cli_evidence_integration.py +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/test_cli_help.py +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/test_cli_ui.py +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/test_consumer_profile_cli.py +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/test_demo_verb.py +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/commit.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/db-status.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/delivery-enqueue.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/delivery-replay.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/delivery-status.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/delivery.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/demo.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/derive.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/doctor.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/evidence-fetch.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/evidence-inspect.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/evidence-inventory.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/evidence.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/export-diff-sft.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/export-dpo.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/export-recovery.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/export-rlvr.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/export-sft.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/export.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/facts.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/login.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/logout.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/mirror-gc.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/quarantine-inference-calls.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/quarantine-log.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/quarantine.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/release.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/report-abandonment.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/report-attribution-share.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/report-dataset-diagnostics.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/report-label-confidence-inspection.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/report-lifecycle.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/report-merge-retention.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/report-model.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/report-precision.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/report-recovery-yield.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/report.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/server.txt +0 -0
- {sediment_cli-0.3.0 → sediment_cli-0.5.0}/tests/testdata/help/uninstall.txt +0 -0
|
@@ -13,8 +13,7 @@ exports/
|
|
|
13
13
|
!docs/exports/
|
|
14
14
|
.env
|
|
15
15
|
.DS_Store
|
|
16
|
-
.claude/
|
|
17
|
-
.claude/worktrees/
|
|
16
|
+
.claude/
|
|
18
17
|
|
|
19
18
|
# Deployment-local compose overrides (mirror credentials etc.) — never in PRs
|
|
20
19
|
compose.override.yml
|
|
@@ -29,4 +28,3 @@ litellm/config.local.yaml
|
|
|
29
28
|
!.env.example
|
|
30
29
|
.agents/
|
|
31
30
|
skills-lock.json
|
|
32
|
-
.claude/skills/asd-ste100
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sediment-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: The sediment command: login, install, server, facts, exports
|
|
5
5
|
Project-URL: Source, https://github.com/sediment-ai/sediment
|
|
6
6
|
Project-URL: Documentation, https://github.com/sediment-ai/sediment/tree/main/docs
|
|
@@ -10,7 +10,7 @@ License-Expression: AGPL-3.0-or-later
|
|
|
10
10
|
License-File: LICENSE
|
|
11
11
|
Requires-Python: >=3.12
|
|
12
12
|
Requires-Dist: httpx>=0.27
|
|
13
|
-
Requires-Dist: sediment-api==0.
|
|
14
|
-
Requires-Dist: sediment-core==0.
|
|
15
|
-
Requires-Dist: sediment-derive==0.
|
|
16
|
-
Requires-Dist: sediment-export==0.
|
|
13
|
+
Requires-Dist: sediment-api==0.5.0
|
|
14
|
+
Requires-Dist: sediment-core==0.5.0
|
|
15
|
+
Requires-Dist: sediment-derive==0.5.0
|
|
16
|
+
Requires-Dist: sediment-export==0.5.0
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# Depends on sediment-api deliberately: the operator verbs are DB-local by
|
|
6
6
|
# design (ADR 0001) and `server`/reports/mirror-gc forward into it.
|
|
7
7
|
name = "sediment-cli"
|
|
8
|
-
version = "0.
|
|
8
|
+
version = "0.5.0"
|
|
9
9
|
description = "The sediment command: login, install, server, facts, exports"
|
|
10
10
|
requires-python = ">=3.12"
|
|
11
11
|
license = "AGPL-3.0-or-later"
|
|
@@ -14,10 +14,10 @@ license-files = ["LICENSE"]
|
|
|
14
14
|
# so an install never mixes member versions (the release workflow asserts
|
|
15
15
|
# the versions agree before publishing).
|
|
16
16
|
dependencies = [
|
|
17
|
-
"sediment-core==0.
|
|
18
|
-
"sediment-derive==0.
|
|
19
|
-
"sediment-export==0.
|
|
20
|
-
"sediment-api==0.
|
|
17
|
+
"sediment-core==0.5.0",
|
|
18
|
+
"sediment-derive==0.5.0",
|
|
19
|
+
"sediment-export==0.5.0",
|
|
20
|
+
"sediment-api==0.5.0",
|
|
21
21
|
"httpx>=0.27",
|
|
22
22
|
]
|
|
23
23
|
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
"""The sediment CLI — the top-level `cli/` workspace member.
|
|
3
|
+
|
|
4
|
+
Remote verbs speak HTTP through `.client`; operator verbs run where the
|
|
5
|
+
facts live (ADR 0001); `.attribution` is the stdlib-only stamper. The
|
|
6
|
+
version is single-sourced from ``sediment_api`` (the string ``/v1/me``
|
|
7
|
+
and ``/health`` report)."""
|
|
8
|
+
|
|
9
|
+
import re
|
|
10
|
+
|
|
11
|
+
from sediment_api import __version__ as __version__
|
|
12
|
+
|
|
13
|
+
# The release policy's two shapes, X.Y.Z and X.Y.ZrcN, in ASCII digits.
|
|
14
|
+
_RELEASE_VERSION = re.compile(r"[0-9]+\.[0-9]+\.[0-9]+(?:rc[0-9]+)?")
|
|
15
|
+
# A release candidate pins these at the same pre-release; uv admits a
|
|
16
|
+
# pre-release only when a direct requirement names it.
|
|
17
|
+
_FIRST_PARTY_DEPENDENCIES = (
|
|
18
|
+
"sediment-api",
|
|
19
|
+
"sediment-capture",
|
|
20
|
+
"sediment-core",
|
|
21
|
+
"sediment-derive",
|
|
22
|
+
"sediment-export",
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def install_command(version: str) -> str:
|
|
27
|
+
"""The installer's uv command pinned to *version*.
|
|
28
|
+
|
|
29
|
+
A pinned install replaces the tool in either direction. `uv tool upgrade`
|
|
30
|
+
keeps an earlier pin and otherwise moves only to the newest release.
|
|
31
|
+
"""
|
|
32
|
+
command = f"uv tool install --python 3.12 --upgrade 'sediment-cli=={version}'"
|
|
33
|
+
if re.fullmatch(r"[0-9.]+rc[0-9]+", version):
|
|
34
|
+
command += "".join(
|
|
35
|
+
f" --with '{name}=={version}'" for name in _FIRST_PARTY_DEPENDENCIES
|
|
36
|
+
)
|
|
37
|
+
return command
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def version_skew_advice(server_version: object, client_version: str) -> str | None:
|
|
41
|
+
"""How to match the server's version, or None when it matches or is absent.
|
|
42
|
+
|
|
43
|
+
The server controls *server_version*; only a release version reaches the
|
|
44
|
+
copyable command, so a server can't put shell syntax or terminal control
|
|
45
|
+
characters into it.
|
|
46
|
+
"""
|
|
47
|
+
if server_version is None or server_version == client_version:
|
|
48
|
+
return None
|
|
49
|
+
if not isinstance(server_version, str) or not _RELEASE_VERSION.fullmatch(
|
|
50
|
+
server_version
|
|
51
|
+
):
|
|
52
|
+
return (
|
|
53
|
+
f"server reported an unrecognized version; client is {client_version}. "
|
|
54
|
+
f"Install your deployment's release: {install_command('<version>')}"
|
|
55
|
+
)
|
|
56
|
+
return (
|
|
57
|
+
f"server version {server_version} differs from client {client_version}; "
|
|
58
|
+
f"install the server's version: {install_command(server_version)}"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def main(argv: list[str] | None = None) -> int:
|
|
63
|
+
"""Refuse unsupported capture installation before importing POSIX operators."""
|
|
64
|
+
import os
|
|
65
|
+
import sys
|
|
66
|
+
|
|
67
|
+
args = sys.argv[1:] if argv is None else argv
|
|
68
|
+
if os.name == "nt" and args[:1] == ["install"]:
|
|
69
|
+
from .attribution import main as run
|
|
70
|
+
else:
|
|
71
|
+
from .cli import main as run
|
|
72
|
+
return run(args)
|
|
@@ -195,7 +195,7 @@ delivery changes also trigger this workflow. To exercise the same tests locally,
|
|
|
195
195
|
set `SEDIMENT_PI_TEST_PYTHON` to the installed environment's Python executable
|
|
196
196
|
and `SEDIMENT_PI_TEST_INSTALLED_BIN` to its `bin` directory before `npm test`.
|
|
197
197
|
|
|
198
|
-
The lockfile pins pi 0.
|
|
198
|
+
The lockfile pins pi 0.87.1 (`@earendil-works/pi-coding-agent`) as a development
|
|
199
199
|
dependency. The native retrieval test exercises its extension registration,
|
|
200
200
|
argument validation, agent loop, and model-visible tool result with a scripted
|
|
201
201
|
model stream. It makes no paid inference request and downloads no package during
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Help operators and developers use Sediment
|
|
2
|
+
|
|
3
|
+
Use this guide when you help an operator run Sediment or help a developer
|
|
4
|
+
capture work from repositories and coding agents. Both roles install the
|
|
5
|
+
Sediment CLI. Developers connecting to a shared server use the installer's
|
|
6
|
+
`--capture-only` option; they don't need to run a local server.
|
|
7
|
+
|
|
8
|
+
`sediment guide` prints this bundled guide without reading configuration,
|
|
9
|
+
contacting a server, or changing the machine. It converts procedure links to
|
|
10
|
+
raw GitHub URLs pinned to the release it ships in, or to `main` from a source
|
|
11
|
+
checkout. Reading those pages requires network access. `sediment --help` lists
|
|
12
|
+
public commands; hooks invoke others.
|
|
13
|
+
|
|
14
|
+
## Read the state before changing it
|
|
15
|
+
|
|
16
|
+
- Run `sediment --version`. For a shared deployment, compare it with the
|
|
17
|
+
server's `/health` version. Use the guide from the version you operate.
|
|
18
|
+
- Establish the task, server URL, deployment method, server account, and
|
|
19
|
+
repository. A developer's machine might only send capture to a remote server.
|
|
20
|
+
- Use `/health` to check server readiness and `sediment doctor <repo>` to
|
|
21
|
+
inspect capture. Neither proves that the server received agent evidence.
|
|
22
|
+
- Stay within the requested task. Don't start another server, reinstall hooks,
|
|
23
|
+
upgrade, rotate credentials, quarantine Facts, or remove data as a repair
|
|
24
|
+
unless that change is authorized. Follow the linked procedure for changes.
|
|
25
|
+
|
|
26
|
+
## Help an operator
|
|
27
|
+
|
|
28
|
+
Run server commands as the account that owns the deployment. For database
|
|
29
|
+
commands, first set up the operator shell from the deployment procedure.
|
|
30
|
+
An API login doesn't configure database access. Keep operator credentials on
|
|
31
|
+
the operator machine; give developers capture credentials only.
|
|
32
|
+
|
|
33
|
+
| Task | First command | Procedure |
|
|
34
|
+
| --- | --- | --- |
|
|
35
|
+
| Try a local server | `sediment server` | [Quickstart](../quickstart.md) |
|
|
36
|
+
| Deploy on your own host | `sediment --version` | [Deploy Sediment on your own host](deploy.md) |
|
|
37
|
+
| Deploy on Amazon Elastic Compute Cloud (EC2) | `sediment --version` | [Deploy Sediment on EC2](deploy-ec2.md) |
|
|
38
|
+
| Enroll a team and connect repository webhooks | `sediment facts` | [Enroll your team](run-pilot.md) |
|
|
39
|
+
| Check schema and Fact counts | `sediment db status` | [Monitor the deployment](maintain.md#monitor-the-deployment) |
|
|
40
|
+
| Upgrade | `sediment --version` | [Upgrade Sediment](maintain.md#upgrade-sediment) |
|
|
41
|
+
| Back up or restore | `sediment db status` | [Back up and restore](maintain.md#back-up-and-restore) |
|
|
42
|
+
| Rotate credentials | `sediment --version` | [Rotate credentials](maintain.md#rotate-credentials) |
|
|
43
|
+
| Build a Derivation bundle | `sediment derive --help` | [Run derivations](run-derivations.md) |
|
|
44
|
+
| Export training data | `sediment export --help` | [Choose a training export](../exports/training-exports.md) |
|
|
45
|
+
| Quarantine captured data | `sediment quarantine-log` | [Quarantine captured data](maintain.md#quarantine-captured-data) |
|
|
46
|
+
| Tear down a deployment | `sediment --version` | [Tear down the deployment](maintain.md#tear-down-the-deployment) |
|
|
47
|
+
|
|
48
|
+
`sediment server` runs in the foreground. Without an external PostgreSQL
|
|
49
|
+
connection, it downloads PostgreSQL on first use and owns the local database.
|
|
50
|
+
It stores data, credentials, and mirrors under `~/.sediment/server` or `--root`.
|
|
51
|
+
Starting it provisions database roles and applies migrations. Preserve that
|
|
52
|
+
directory across restarts and upgrades. Plan backups before migrations or
|
|
53
|
+
teardown. Use the deployment's supervisor to manage a shared server.
|
|
54
|
+
|
|
55
|
+
## Help a developer
|
|
56
|
+
|
|
57
|
+
Use the server URL and capture credential that the operator supplies.
|
|
58
|
+
Login saves credentials; installation changes Git hooks, agent configuration,
|
|
59
|
+
and shell configuration. Run these steps when the developer requests capture.
|
|
60
|
+
|
|
61
|
+
| Task | First command | Procedure |
|
|
62
|
+
| --- | --- | --- |
|
|
63
|
+
| Enroll with a shared server | `sediment login <url> --capture` | [Configure local capture](../capture/local-capture.md) |
|
|
64
|
+
| Install repository and agent capture | `sediment install <repo>` | [Install capture](../capture/local-capture.md#install-capture) |
|
|
65
|
+
| Select agent setup and verify delivery | `sediment doctor <repo>` | [Choose an agent integration](../capture/agent-integrations.md) |
|
|
66
|
+
| Check buffered delivery | `sediment delivery status` | [Preserve prepared payloads through outages](../capture/local-capture.md#preserve-prepared-payloads-through-outages) |
|
|
67
|
+
| Inspect evidence for a pushed commit | `sediment commit <full commit SHA>` | [Measure agent work](measure-agent-work.md) |
|
|
68
|
+
| Remove capture | `sediment uninstall <repo>` | [Uninstall capture](../capture/local-capture.md#uninstall-capture) |
|
|
69
|
+
|
|
70
|
+
Default `doctor` checks configuration and contacts the server and `origin`.
|
|
71
|
+
It changes nothing unless you pass `--fetch`, which updates a notes tracking
|
|
72
|
+
ref. Only `FAIL` lines make it exit 1. A sandbox network restriction or an
|
|
73
|
+
unloaded capture environment can explain a failure. Use the agent procedure's
|
|
74
|
+
Session checks to verify delivery. `commit` requires an operator credential;
|
|
75
|
+
`no attributions` before the push and notes reach the server isn't a failure.
|
|
76
|
+
|
|
77
|
+
## Preserve capture while doing repository work
|
|
78
|
+
|
|
79
|
+
- Leave the marked `sediment-attribution` Git hook blocks, `notes.rewriteRef`,
|
|
80
|
+
and `refs/notes/sediment` in place. Don't bypass hooks or change
|
|
81
|
+
`core.hooksPath`. If hook files are tracked, leave Sediment's uncommitted
|
|
82
|
+
blocks for the developer; don't commit, stash, or revert them incidentally.
|
|
83
|
+
- Keep the `sediment` executable at its installed path. Hooks use that absolute
|
|
84
|
+
path; source installs also depend on their checkout and virtual environment.
|
|
85
|
+
- Preserve the `sediment env` blocks in shell profiles and Sediment's entries
|
|
86
|
+
in Claude Code settings, Codex hooks and profiles, Cursor hooks, and pi
|
|
87
|
+
settings. User-level agent hooks apply across repositories.
|
|
88
|
+
- Read hook output. Sediment commands end in `|| true`; an appended block can
|
|
89
|
+
also mask a repository check that relies on its last command's exit status.
|
|
90
|
+
A zero status alone doesn't prove that the repository's checks passed.
|
|
91
|
+
|
|
92
|
+
[What the installer changes](../capture/local-capture.md#what-the-installer-changes)
|
|
93
|
+
describes the hooks. [Roll out managed capture](../capture/managed-capture.md)
|
|
94
|
+
covers system settings, gateways, and fleet installation.
|
|
95
|
+
|
|
96
|
+
## Remove capture within its installation scope
|
|
97
|
+
|
|
98
|
+
- `uninstall <repo>` removes that repository's hook blocks and Sediment's
|
|
99
|
+
`notes.rewriteRef` value. Require `removed git hooks from` in its output;
|
|
100
|
+
a silent exit can mean that the path isn't a Git repository.
|
|
101
|
+
- `uninstall <repo> --agents` also removes user-level agent hooks, including
|
|
102
|
+
pi, environment files, shell blocks, and verified Codex telemetry blocks.
|
|
103
|
+
These user-level removals affect capture across repositories. The command
|
|
104
|
+
doesn't remove Claude Code managed settings, fleet Git templates, or
|
|
105
|
+
externally distributed environment settings. Follow
|
|
106
|
+
[Remove managed capture](../capture/managed-capture.md#remove-managed-capture)
|
|
107
|
+
for those installations. Report skipped files; don't claim complete removal.
|
|
108
|
+
- `logout` removes stored CLI credentials, but environment files retain their
|
|
109
|
+
ingest token and hooks keep running. It doesn't stop capture.
|
|
110
|
+
- Neither uninstall form removes existing notes, remote notes, or
|
|
111
|
+
`~/.sediment/`. An `auto_install_remotes` allowlist can reinstall hooks at
|
|
112
|
+
the next Session. Remove that entry as part of an authorized teardown.
|
|
113
|
+
Restart running agents so they stop using the earlier environment.
|
|
114
|
+
|
|
115
|
+
## Keep credentials and captured content private
|
|
116
|
+
|
|
117
|
+
Don't print credentials, paste them into chat, or commit them. Credential
|
|
118
|
+
locations include `~/.sediment/config.json`, `~/.sediment/env.sh`, the Fish
|
|
119
|
+
`sediment.fish` file, Codex telemetry profiles, and server environment files.
|
|
120
|
+
Custom server roots, shell profiles, and service settings can hold them too.
|
|
121
|
+
Don't dump the process environment: it can contain `SEDIMENT_INGEST_TOKEN`,
|
|
122
|
+
`OTEL_EXPORTER_OTLP_HEADERS`, database passwords, and gateway credentials.
|
|
123
|
+
Follow [Secure a deployment](security.md) when handling secrets.
|
|
124
|
+
|
|
125
|
+
Capture can leave the developer machine in these forms:
|
|
126
|
+
|
|
127
|
+
- The pre-push hook sends the whole notes ref to each push remote, including
|
|
128
|
+
forks, even during `git push --dry-run`. Notes contain commit SHAs, agent
|
|
129
|
+
names, Session identifiers, and timestamps, including other branches' notes.
|
|
130
|
+
They don't contain prompts, diffs, file paths, model names, or hostnames.
|
|
131
|
+
- Agent telemetry goes to the Sediment server. Claude Code tool details can
|
|
132
|
+
include paths, shell commands, edit text, and the Claude account email.
|
|
133
|
+
Codex decisions can include patch code and tool arguments; Sediment removes
|
|
134
|
+
the Codex account email and account identifier before storage. Cursor sends
|
|
135
|
+
accept decisions for applied writes and a configured user identifier.
|
|
136
|
+
- Opt-in transcript capture sends edit text and edited-file contents. Claude
|
|
137
|
+
Code transcripts also include refused edit text and non-agent change counts.
|
|
138
|
+
- If the operator routes model calls through a gateway, it captures full
|
|
139
|
+
prompts and responses.
|
|
140
|
+
|
|
141
|
+
[Privacy boundaries and ceilings](../explanation/how-capture-works.md#privacy-boundaries-and-ceilings)
|
|
142
|
+
describes these payloads and their limits.
|
|
@@ -55,8 +55,7 @@ Stdlib only, no daemon. Nine subcommands:
|
|
|
55
55
|
+ ``notes.rewriteRef`` (local config) + the agent
|
|
56
56
|
telemetry env files generated from the ``sediment
|
|
57
57
|
login`` config (``--no-env`` skips; ``--user-id``
|
|
58
|
-
stamps per-developer attribution;
|
|
59
|
-
``--gateway-key`` add the completions routing);
|
|
58
|
+
stamps per-developer attribution);
|
|
60
59
|
``--transcripts`` opts in to the SessionEnd transcript
|
|
61
60
|
extractor and its PreToolUse snapshot hook
|
|
62
61
|
(sediment_transcript.py — ships edit text pairs, a
|
|
@@ -106,9 +105,12 @@ try:
|
|
|
106
105
|
except ImportError: # pragma: no cover — run by path, not as a package
|
|
107
106
|
# The checkout shim (scripts/sediment_attribution.py) and the fleet
|
|
108
107
|
# bundle execute this file standalone, where no sibling ui module
|
|
109
|
-
# exists.
|
|
110
|
-
# output there, so a passthrough stand-in keeps the bytes identical.
|
|
108
|
+
# exists. Keep styling plain and match ui's server-text display filter.
|
|
111
109
|
class ui: # type: ignore[no-redef] # ponytail: plain-text stand-in
|
|
110
|
+
@staticmethod
|
|
111
|
+
def printable_text(value: object) -> str:
|
|
112
|
+
return "".join(char for char in str(value) if char.isprintable())
|
|
113
|
+
|
|
112
114
|
@staticmethod
|
|
113
115
|
def style(text: str, *names: str, stream=None) -> str:
|
|
114
116
|
return text
|
|
@@ -2852,8 +2854,6 @@ def _env_pairs(
|
|
|
2852
2854
|
url: str,
|
|
2853
2855
|
token: str,
|
|
2854
2856
|
user_id: str | None,
|
|
2855
|
-
gateway_url: str | None,
|
|
2856
|
-
gateway_key: str | None,
|
|
2857
2857
|
) -> list[tuple[str, str]]:
|
|
2858
2858
|
"""The §5.1 block, generated. The exporter appends /v1/logs itself, so
|
|
2859
2859
|
the endpoint is the bare server URL."""
|
|
@@ -2875,22 +2875,27 @@ def _env_pairs(
|
|
|
2875
2875
|
_warn("capture helper unavailable; dedicated hook delivery is disabled")
|
|
2876
2876
|
if user_id:
|
|
2877
2877
|
pairs.append(("OTEL_RESOURCE_ATTRIBUTES", f"user.id={user_id}"))
|
|
2878
|
-
if gateway_url:
|
|
2879
|
-
pairs.append(("ANTHROPIC_BASE_URL", gateway_url))
|
|
2880
|
-
if gateway_key:
|
|
2881
|
-
pairs.append(("ANTHROPIC_AUTH_TOKEN", gateway_key))
|
|
2882
|
-
pairs.append(("SEDIMENT_GATEWAY_KEY", gateway_key))
|
|
2883
2878
|
return pairs
|
|
2884
2879
|
|
|
2885
2880
|
|
|
2886
2881
|
def _write_0600(path: Path, content: str) -> None:
|
|
2887
|
-
"""
|
|
2888
|
-
|
|
2882
|
+
"""Publish a complete private file without truncating the previous version."""
|
|
2883
|
+
if path.is_symlink():
|
|
2884
|
+
raise ValueError(
|
|
2885
|
+
"refusing to replace a symbolic link with private configuration"
|
|
2886
|
+
)
|
|
2889
2887
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
2890
|
-
|
|
2891
|
-
os.
|
|
2892
|
-
|
|
2893
|
-
|
|
2888
|
+
temporary = path.with_name(f".{path.name}.{uuid4().hex}.tmp")
|
|
2889
|
+
fd = os.open(temporary, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
|
|
2890
|
+
try:
|
|
2891
|
+
with os.fdopen(fd, "w", encoding="utf-8") as fh:
|
|
2892
|
+
os.fchmod(fh.fileno(), 0o600)
|
|
2893
|
+
fh.write(content)
|
|
2894
|
+
fh.flush()
|
|
2895
|
+
os.fsync(fh.fileno())
|
|
2896
|
+
os.replace(temporary, path)
|
|
2897
|
+
finally:
|
|
2898
|
+
temporary.unlink(missing_ok=True)
|
|
2894
2899
|
|
|
2895
2900
|
|
|
2896
2901
|
def _write_env_files(pairs: list[tuple[str, str]]) -> None:
|
|
@@ -2956,19 +2961,14 @@ def _unwire_env() -> list[str]:
|
|
|
2956
2961
|
return removed
|
|
2957
2962
|
|
|
2958
2963
|
|
|
2959
|
-
def cmd_install_env(
|
|
2960
|
-
user_id: str | None,
|
|
2961
|
-
gateway_url: str | None,
|
|
2962
|
-
gateway_key: str | None,
|
|
2963
|
-
transcripts: bool = False,
|
|
2964
|
-
) -> str:
|
|
2964
|
+
def cmd_install_env(user_id: str | None, transcripts: bool = False) -> str:
|
|
2965
2965
|
"""Wire the agent telemetry env from the login config; the install
|
|
2966
2966
|
summary string says what happened."""
|
|
2967
2967
|
cfg = _cli_config(capture=True)
|
|
2968
2968
|
if cfg is None:
|
|
2969
2969
|
return f"skipped (capture credential is absent; {_CAPTURE_LOGIN})"
|
|
2970
2970
|
url, token = cfg
|
|
2971
|
-
pairs = _env_pairs(url, token, user_id
|
|
2971
|
+
pairs = _env_pairs(url, token, user_id)
|
|
2972
2972
|
try:
|
|
2973
2973
|
previous = _sh_env_file().read_text(encoding="utf-8").splitlines()
|
|
2974
2974
|
except FileNotFoundError:
|
|
@@ -3222,11 +3222,13 @@ def _uninstall_codex_profiles() -> bool:
|
|
|
3222
3222
|
complete = True
|
|
3223
3223
|
for path in profiles:
|
|
3224
3224
|
try:
|
|
3225
|
-
if
|
|
3225
|
+
if not path.is_file():
|
|
3226
3226
|
raise ValueError
|
|
3227
3227
|
original = path.read_text(encoding="utf-8")
|
|
3228
3228
|
if _CODEX_OTEL_BEGIN not in original and _CODEX_OTEL_END not in original:
|
|
3229
3229
|
continue
|
|
3230
|
+
if path.is_symlink():
|
|
3231
|
+
raise ValueError
|
|
3230
3232
|
remaining = _without_codex_telemetry(original)
|
|
3231
3233
|
if remaining.strip():
|
|
3232
3234
|
_write_0600(path, remaining)
|
|
@@ -3436,21 +3438,17 @@ def _doctor_server(findings: list[Finding]) -> None:
|
|
|
3436
3438
|
)
|
|
3437
3439
|
)
|
|
3438
3440
|
return
|
|
3439
|
-
|
|
3441
|
+
org_id = ui.printable_text(body.get("org_id"))
|
|
3442
|
+
detail = f"reachable, {authority} token valid (org {org_id})"
|
|
3440
3443
|
try:
|
|
3441
|
-
from
|
|
3444
|
+
from sediment_cli import __version__ as client_version
|
|
3445
|
+
from sediment_cli import version_skew_advice
|
|
3442
3446
|
except ImportError: # standalone copied-file run — skew unknowable
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
DOCTOR_INFO,
|
|
3449
|
-
check,
|
|
3450
|
-
f"{detail}; version skew: server {server_version}, client "
|
|
3451
|
-
f"{client_version} — uv tool upgrade sediment-cli",
|
|
3452
|
-
)
|
|
3453
|
-
)
|
|
3447
|
+
advice = None
|
|
3448
|
+
else:
|
|
3449
|
+
advice = version_skew_advice(body.get("version"), client_version)
|
|
3450
|
+
if advice:
|
|
3451
|
+
findings.append((DOCTOR_INFO, check, f"{detail}; {advice}"))
|
|
3454
3452
|
else:
|
|
3455
3453
|
findings.append((DOCTOR_OK, check, detail))
|
|
3456
3454
|
|
|
@@ -3461,8 +3459,6 @@ def cmd_install(
|
|
|
3461
3459
|
transcripts: bool = False,
|
|
3462
3460
|
env: bool = True,
|
|
3463
3461
|
user_id: str | None = None,
|
|
3464
|
-
gateway_url: str | None = None,
|
|
3465
|
-
gateway_key: str | None = None,
|
|
3466
3462
|
codex_profile: str | None = None,
|
|
3467
3463
|
) -> int:
|
|
3468
3464
|
repo_path = Path(repo).resolve()
|
|
@@ -3511,7 +3507,7 @@ def cmd_install(
|
|
|
3511
3507
|
# same flag pair: --no-agents implies no env wiring.
|
|
3512
3508
|
if env:
|
|
3513
3509
|
try:
|
|
3514
|
-
detail = cmd_install_env(user_id,
|
|
3510
|
+
detail = cmd_install_env(user_id, transcripts)
|
|
3515
3511
|
except ValueError as exc:
|
|
3516
3512
|
_error(str(exc))
|
|
3517
3513
|
return 1
|
|
@@ -3854,17 +3850,6 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
3854
3850
|
metavar="NAME",
|
|
3855
3851
|
help="write a Codex telemetry profile with resolved login credentials (0600); preserve its model and unrelated settings",
|
|
3856
3852
|
)
|
|
3857
|
-
p_install.add_argument(
|
|
3858
|
-
"--gateway-url",
|
|
3859
|
-
default=None,
|
|
3860
|
-
help="also wire ANTHROPIC_BASE_URL to this LLM gateway",
|
|
3861
|
-
)
|
|
3862
|
-
p_install.add_argument(
|
|
3863
|
-
"--gateway-key",
|
|
3864
|
-
default=None,
|
|
3865
|
-
help="also wire ANTHROPIC_AUTH_TOKEN and SEDIMENT_GATEWAY_KEY "
|
|
3866
|
-
"(the key agents present to the gateway)",
|
|
3867
|
-
)
|
|
3868
3853
|
p_install.add_argument(
|
|
3869
3854
|
"--fleet",
|
|
3870
3855
|
action="store_true",
|
|
@@ -3954,15 +3939,13 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
3954
3939
|
or args.transcripts
|
|
3955
3940
|
or args.no_env
|
|
3956
3941
|
or args.user_id is not None
|
|
3957
|
-
or args.gateway_url is not None
|
|
3958
|
-
or args.gateway_key is not None
|
|
3959
3942
|
or args.codex_profile is not None
|
|
3960
3943
|
)
|
|
3961
3944
|
if args.fleet:
|
|
3962
3945
|
if per_user_flags:
|
|
3963
3946
|
_error(
|
|
3964
3947
|
"REPO, --no-agents, --transcripts, --codex-profile, and the env "
|
|
3965
|
-
"flags (--no-env/--user-id
|
|
3948
|
+
"flags (--no-env/--user-id) do not apply "
|
|
3966
3949
|
"to --fleet"
|
|
3967
3950
|
)
|
|
3968
3951
|
return 2
|
|
@@ -3983,8 +3966,6 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
3983
3966
|
transcripts=args.transcripts,
|
|
3984
3967
|
env=not args.no_env,
|
|
3985
3968
|
user_id=args.user_id,
|
|
3986
|
-
gateway_url=args.gateway_url,
|
|
3987
|
-
gateway_key=args.gateway_key,
|
|
3988
3969
|
codex_profile=args.codex_profile,
|
|
3989
3970
|
)
|
|
3990
3971
|
if args.command == "uninstall":
|