typesafeai-cli 0.3.1__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.
Files changed (50) hide show
  1. typesafeai_cli-0.3.1/.gitignore +14 -0
  2. typesafeai_cli-0.3.1/LICENSE +21 -0
  3. typesafeai_cli-0.3.1/PKG-INFO +137 -0
  4. typesafeai_cli-0.3.1/README.md +113 -0
  5. typesafeai_cli-0.3.1/examples/README.md +15 -0
  6. typesafeai_cli-0.3.1/examples/code-change/questions.json +25 -0
  7. typesafeai_cli-0.3.1/examples/code-change/state.json +10 -0
  8. typesafeai_cli-0.3.1/examples/ticket/questions.json +24 -0
  9. typesafeai_cli-0.3.1/examples/ticket/state.json +4 -0
  10. typesafeai_cli-0.3.1/install.sh +125 -0
  11. typesafeai_cli-0.3.1/pyproject.toml +55 -0
  12. typesafeai_cli-0.3.1/src/typesafe_cli/__init__.py +1 -0
  13. typesafeai_cli-0.3.1/src/typesafe_cli/cli.py +93 -0
  14. typesafeai_cli-0.3.1/src/typesafe_cli/client.py +106 -0
  15. typesafeai_cli-0.3.1/src/typesafe_cli/commands/__init__.py +0 -0
  16. typesafeai_cli-0.3.1/src/typesafe_cli/commands/agent.py +12 -0
  17. typesafeai_cli-0.3.1/src/typesafe_cli/commands/ask.py +90 -0
  18. typesafeai_cli-0.3.1/src/typesafe_cli/commands/auth.py +28 -0
  19. typesafeai_cli-0.3.1/src/typesafe_cli/commands/decide.py +43 -0
  20. typesafeai_cli-0.3.1/src/typesafe_cli/commands/eval.py +37 -0
  21. typesafeai_cli-0.3.1/src/typesafe_cli/commands/extract.py +83 -0
  22. typesafeai_cli-0.3.1/src/typesafe_cli/commands/find.py +53 -0
  23. typesafeai_cli-0.3.1/src/typesafe_cli/commands/models.py +30 -0
  24. typesafeai_cli-0.3.1/src/typesafe_cli/commands/oneshot.py +91 -0
  25. typesafeai_cli-0.3.1/src/typesafe_cli/commands/rank.py +49 -0
  26. typesafeai_cli-0.3.1/src/typesafe_cli/commands/screen.py +38 -0
  27. typesafeai_cli-0.3.1/src/typesafe_cli/commands/skills.py +138 -0
  28. typesafeai_cli-0.3.1/src/typesafe_cli/commands/smoke.py +35 -0
  29. typesafeai_cli-0.3.1/src/typesafe_cli/commands/suggest_skill.py +48 -0
  30. typesafeai_cli-0.3.1/src/typesafe_cli/commands/verify.py +53 -0
  31. typesafeai_cli-0.3.1/src/typesafe_cli/config.py +37 -0
  32. typesafeai_cli-0.3.1/src/typesafe_cli/data/__init__.py +0 -0
  33. typesafeai_cli-0.3.1/src/typesafe_cli/data/typesafe-ai/CLI.md +7 -0
  34. typesafeai_cli-0.3.1/src/typesafe_cli/data/typesafe-ai/SKILL.md +149 -0
  35. typesafeai_cli-0.3.1/src/typesafe_cli/data/typesafe-cli/SKILL.md +137 -0
  36. typesafeai_cli-0.3.1/src/typesafe_cli/detect.py +83 -0
  37. typesafeai_cli-0.3.1/src/typesafe_cli/format.py +22 -0
  38. typesafeai_cli-0.3.1/src/typesafe_cli/help.py +36 -0
  39. typesafeai_cli-0.3.1/src/typesafe_cli/io.py +17 -0
  40. typesafeai_cli-0.3.1/src/typesafe_cli/questions.py +96 -0
  41. typesafeai_cli-0.3.1/src/typesafe_cli/recipes/__init__.py +3 -0
  42. typesafeai_cli-0.3.1/src/typesafe_cli/recipes/decide.py +64 -0
  43. typesafeai_cli-0.3.1/src/typesafe_cli/recipes/extract.py +42 -0
  44. typesafeai_cli-0.3.1/src/typesafe_cli/recipes/find.py +90 -0
  45. typesafeai_cli-0.3.1/src/typesafe_cli/recipes/rank.py +97 -0
  46. typesafeai_cli-0.3.1/src/typesafe_cli/recipes/screen.py +68 -0
  47. typesafeai_cli-0.3.1/src/typesafe_cli/recipes/skills.py +148 -0
  48. typesafeai_cli-0.3.1/src/typesafe_cli/recipes/thresholds.py +24 -0
  49. typesafeai_cli-0.3.1/src/typesafe_cli/recipes/verify.py +48 -0
  50. typesafeai_cli-0.3.1/src/typesafe_cli/schema.py +150 -0
@@ -0,0 +1,14 @@
1
+ .env.local
2
+ .env
3
+ PLAN.md
4
+ plan.md
5
+ docs/
6
+ .venv/
7
+ __pycache__/
8
+ *.py[cod]
9
+ *.egg-info/
10
+ dist/
11
+ .pytest_cache/
12
+ .ruff_cache/
13
+ .coverage
14
+ uv.lock
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 typesafeai-cli contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,137 @@
1
+ Metadata-Version: 2.5
2
+ Name: typesafeai-cli
3
+ Version: 0.3.1
4
+ Summary: Agent CLI for TypeSafe System One (Jev)
5
+ Project-URL: Homepage, https://github.com/maddygoround/typesafeai-cli
6
+ Project-URL: Repository, https://github.com/maddygoround/typesafeai-cli
7
+ Project-URL: Issues, https://github.com/maddygoround/typesafeai-cli/issues
8
+ Project-URL: Releases, https://github.com/maddygoround/typesafeai-cli/releases
9
+ Author: safeaiforeveryone
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: cli,jev,typesafe
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Utilities
20
+ Requires-Python: >=3.10
21
+ Requires-Dist: typer>=0.12
22
+ Requires-Dist: typesafe-sdk>=0.6.0
23
+ Description-Content-Type: text/markdown
24
+
25
+ # TypeSafe agent CLI
26
+
27
+ `typesafe` is an **agent CLI** for [TypeSafe](https://typesafe.ai) **Jev**.
28
+
29
+ It is meant to be invoked by coding agents (Grok, Codex, Claude Code, Cursor, and anything else that can run a shell command). You can run it yourself while you debug; the usual caller is an agent.
30
+
31
+ Jev does not chat. The agent gathers a small JSON document (`state`) and typed questions (`noul`, `choice`, `score`). This CLI sends that to `POST /v1/systemone` and prints probabilities on stdout. Jev never sees the repo unless the agent copies the relevant slices into `state`.
32
+
33
+ You do not hand-write production payloads every day. Install the **typesafe-cli** skill, and the agent writes the JSON (in a temp directory, not in git). The files under [`examples/`](examples/) exist so you can see the shape, and so you can run a call without inventing one.
34
+
35
+ ## JSON shape
36
+
37
+ Two files. Names are yours; types are not.
38
+
39
+ **`state.json`** — named fields the questions will point at:
40
+
41
+ ```json
42
+ {
43
+ "message": "I was charged twice for order A-104. Please refund the duplicate.",
44
+ "policy": "Duplicate charges are eligible for an immediate refund."
45
+ }
46
+ ```
47
+
48
+ **`questions.json`** — one judgment per id. `instructions` should backtick those fields (`` `message` ``):
49
+
50
+ ```json
51
+ {
52
+ "refund_requested": {
53
+ "type": "noul",
54
+ "instructions": "Does `message` request a refund?"
55
+ },
56
+ "intent": {
57
+ "type": "choice",
58
+ "instructions": "What does `message` ask for?",
59
+ "criteria": {
60
+ "refund": "The customer wants money returned",
61
+ "information": "Explanation only",
62
+ "other": "Something else"
63
+ }
64
+ }
65
+ }
66
+ ```
67
+
68
+ Full copies, including a **code-change** example with `files[].hunk`:
69
+
70
+ | Sample | Files |
71
+ | --- | --- |
72
+ | Support ticket | [`examples/ticket/state.json`](examples/ticket/state.json), [`questions.json`](examples/ticket/questions.json) |
73
+ | Skill / code slice | [`examples/code-change/state.json`](examples/code-change/state.json), [`questions.json`](examples/code-change/questions.json) |
74
+ | How to run them | [`examples/README.md`](examples/README.md) |
75
+
76
+ ```bash
77
+ WORKDIR="${TMPDIR:-/tmp}/codex/typesafeai-cli"
78
+ mkdir -p "$WORKDIR"
79
+ cp examples/ticket/*.json "$WORKDIR/"
80
+ typesafe ask --state-file "$WORKDIR/state.json" --questions-file "$WORKDIR/questions.json"
81
+ ```
82
+
83
+ One-liners skip files: `typesafe noul "Does this request a refund?" --state "I was charged twice."`
84
+
85
+ If `data.model` looks like `jev-1.13.0`, the request reached TypeSafe. `auth status` does not.
86
+
87
+ ## What the agent is supposed to do
88
+
89
+ 1. `typesafe skills install` (once) so **typesafe-cli** and the official TypeSafe skill are on disk.
90
+ 2. `typesafe auth status` — read `has_key` only. Agents must not print or open `TYPESAFE_*` or key files.
91
+ 3. Write questions first. Each instruction backticks the state paths it needs. That list is what to collect.
92
+ 4. Fill only those paths. If a path is code, put the hunk body in, not a filename.
93
+ 5. Write JSON under `${TMPDIR:-/tmp}/codex/<project>/`, never in the git tree.
94
+ 6. `typesafe ask`. Apply thresholds in the agent (a noul near `0.5` is unsure). Jev does not pick the next tool.
95
+
96
+ `typesafe agent schema` is the machine-readable command list. `--agent` makes `--help` JSON.
97
+
98
+ ## Install
99
+
100
+ Python 3.10+ and a key from the [TypeSafe console](https://console.typesafe.ai/settings/keys).
101
+
102
+ ```bash
103
+ export TYPESAFE_API_KEY=apikey_…
104
+
105
+ pip install typesafeai-cli
106
+ # or: pipx install typesafeai-cli
107
+ # or clone, then: uv sync && uv run typesafe --help
108
+ ```
109
+
110
+ `install.sh` on the repo and on each release can also install the GitHub wheel. Optional: `TYPESAFE_BASE_URL`, `TYPESAFE_DEFAULT_MODEL`. `--key` / `--creds` are for scripts, not for agents scraping secrets.
111
+
112
+ ## Commands
113
+
114
+ | | |
115
+ | --- | --- |
116
+ | `typesafe ask` | State file + questions file |
117
+ | `typesafe noul` / `choice` / `score` | One question |
118
+ | `typesafe find` | Search a local file by plain-language query |
119
+ | `typesafe rank` | Rerank a JSON shortlist |
120
+ | `typesafe extract` | Pick a verbatim span (email / phone / money / `--candidates`) |
121
+ | `typesafe verify` | Check a claim against a source (missing quote → fabricated) |
122
+ | `typesafe screen` | Jailbreak / injection / sensitive-data / harm gate |
123
+ | `typesafe suggest-skill` | At most one skill name, or none |
124
+ | `typesafe decide` | Map nouls/choices to no / uncertain / yes (no HTTP) |
125
+ | `typesafe models` | Aliases (`jev-latest`, …) |
126
+ | `typesafe smoke` | Live docs quickstart |
127
+ | `typesafe auth status` | `has_key`; never prints the key |
128
+ | `typesafe agent schema` | JSON command tree |
129
+ | `typesafe skills install` | Official TypeSafe skill + **typesafe-cli** |
130
+
131
+ Exit `0` answered, `1` request failed (including missing key on `ask`), `2` bad flags or questions (no HTTP).
132
+
133
+ ## Releases
134
+
135
+ A merge to `main` that passes CI ships automatically: patch bump, tag `vX.Y.Z`, GitHub release, PyPI (`release.yml`, environment `pypi`). Start the merge commit with `[skip release]` to skip. For a minor or major, run **Prepare Release** and pick the bump.
136
+
137
+ Match `version` in `pyproject.toml` to `__version__` in `src/typesafe_cli/__init__.py` if you tag by hand.
@@ -0,0 +1,113 @@
1
+ # TypeSafe agent CLI
2
+
3
+ `typesafe` is an **agent CLI** for [TypeSafe](https://typesafe.ai) **Jev**.
4
+
5
+ It is meant to be invoked by coding agents (Grok, Codex, Claude Code, Cursor, and anything else that can run a shell command). You can run it yourself while you debug; the usual caller is an agent.
6
+
7
+ Jev does not chat. The agent gathers a small JSON document (`state`) and typed questions (`noul`, `choice`, `score`). This CLI sends that to `POST /v1/systemone` and prints probabilities on stdout. Jev never sees the repo unless the agent copies the relevant slices into `state`.
8
+
9
+ You do not hand-write production payloads every day. Install the **typesafe-cli** skill, and the agent writes the JSON (in a temp directory, not in git). The files under [`examples/`](examples/) exist so you can see the shape, and so you can run a call without inventing one.
10
+
11
+ ## JSON shape
12
+
13
+ Two files. Names are yours; types are not.
14
+
15
+ **`state.json`** — named fields the questions will point at:
16
+
17
+ ```json
18
+ {
19
+ "message": "I was charged twice for order A-104. Please refund the duplicate.",
20
+ "policy": "Duplicate charges are eligible for an immediate refund."
21
+ }
22
+ ```
23
+
24
+ **`questions.json`** — one judgment per id. `instructions` should backtick those fields (`` `message` ``):
25
+
26
+ ```json
27
+ {
28
+ "refund_requested": {
29
+ "type": "noul",
30
+ "instructions": "Does `message` request a refund?"
31
+ },
32
+ "intent": {
33
+ "type": "choice",
34
+ "instructions": "What does `message` ask for?",
35
+ "criteria": {
36
+ "refund": "The customer wants money returned",
37
+ "information": "Explanation only",
38
+ "other": "Something else"
39
+ }
40
+ }
41
+ }
42
+ ```
43
+
44
+ Full copies, including a **code-change** example with `files[].hunk`:
45
+
46
+ | Sample | Files |
47
+ | --- | --- |
48
+ | Support ticket | [`examples/ticket/state.json`](examples/ticket/state.json), [`questions.json`](examples/ticket/questions.json) |
49
+ | Skill / code slice | [`examples/code-change/state.json`](examples/code-change/state.json), [`questions.json`](examples/code-change/questions.json) |
50
+ | How to run them | [`examples/README.md`](examples/README.md) |
51
+
52
+ ```bash
53
+ WORKDIR="${TMPDIR:-/tmp}/codex/typesafeai-cli"
54
+ mkdir -p "$WORKDIR"
55
+ cp examples/ticket/*.json "$WORKDIR/"
56
+ typesafe ask --state-file "$WORKDIR/state.json" --questions-file "$WORKDIR/questions.json"
57
+ ```
58
+
59
+ One-liners skip files: `typesafe noul "Does this request a refund?" --state "I was charged twice."`
60
+
61
+ If `data.model` looks like `jev-1.13.0`, the request reached TypeSafe. `auth status` does not.
62
+
63
+ ## What the agent is supposed to do
64
+
65
+ 1. `typesafe skills install` (once) so **typesafe-cli** and the official TypeSafe skill are on disk.
66
+ 2. `typesafe auth status` — read `has_key` only. Agents must not print or open `TYPESAFE_*` or key files.
67
+ 3. Write questions first. Each instruction backticks the state paths it needs. That list is what to collect.
68
+ 4. Fill only those paths. If a path is code, put the hunk body in, not a filename.
69
+ 5. Write JSON under `${TMPDIR:-/tmp}/codex/<project>/`, never in the git tree.
70
+ 6. `typesafe ask`. Apply thresholds in the agent (a noul near `0.5` is unsure). Jev does not pick the next tool.
71
+
72
+ `typesafe agent schema` is the machine-readable command list. `--agent` makes `--help` JSON.
73
+
74
+ ## Install
75
+
76
+ Python 3.10+ and a key from the [TypeSafe console](https://console.typesafe.ai/settings/keys).
77
+
78
+ ```bash
79
+ export TYPESAFE_API_KEY=apikey_…
80
+
81
+ pip install typesafeai-cli
82
+ # or: pipx install typesafeai-cli
83
+ # or clone, then: uv sync && uv run typesafe --help
84
+ ```
85
+
86
+ `install.sh` on the repo and on each release can also install the GitHub wheel. Optional: `TYPESAFE_BASE_URL`, `TYPESAFE_DEFAULT_MODEL`. `--key` / `--creds` are for scripts, not for agents scraping secrets.
87
+
88
+ ## Commands
89
+
90
+ | | |
91
+ | --- | --- |
92
+ | `typesafe ask` | State file + questions file |
93
+ | `typesafe noul` / `choice` / `score` | One question |
94
+ | `typesafe find` | Search a local file by plain-language query |
95
+ | `typesafe rank` | Rerank a JSON shortlist |
96
+ | `typesafe extract` | Pick a verbatim span (email / phone / money / `--candidates`) |
97
+ | `typesafe verify` | Check a claim against a source (missing quote → fabricated) |
98
+ | `typesafe screen` | Jailbreak / injection / sensitive-data / harm gate |
99
+ | `typesafe suggest-skill` | At most one skill name, or none |
100
+ | `typesafe decide` | Map nouls/choices to no / uncertain / yes (no HTTP) |
101
+ | `typesafe models` | Aliases (`jev-latest`, …) |
102
+ | `typesafe smoke` | Live docs quickstart |
103
+ | `typesafe auth status` | `has_key`; never prints the key |
104
+ | `typesafe agent schema` | JSON command tree |
105
+ | `typesafe skills install` | Official TypeSafe skill + **typesafe-cli** |
106
+
107
+ Exit `0` answered, `1` request failed (including missing key on `ask`), `2` bad flags or questions (no HTTP).
108
+
109
+ ## Releases
110
+
111
+ A merge to `main` that passes CI ships automatically: patch bump, tag `vX.Y.Z`, GitHub release, PyPI (`release.yml`, environment `pypi`). Start the merge commit with `[skip release]` to skip. For a minor or major, run **Prepare Release** and pick the bump.
112
+
113
+ Match `version` in `pyproject.toml` to `__version__` in `src/typesafe_cli/__init__.py` if you tag by hand.
@@ -0,0 +1,15 @@
1
+ Samples for the TypeSafe **agent CLI**. How to run them, install, and what agents should do lives in the [project README](../README.md).
2
+
3
+ | Directory | What it is |
4
+ | --- | --- |
5
+ | [ticket/](ticket/) | Support message → refund / intent / frustration |
6
+ | [code-change/](code-change/) | A skill hunk → whether agents must attach real code |
7
+
8
+ Copy these into a temp dir before a live call (agents use `${TMPDIR:-/tmp}/codex/<project>/`, not the git tree):
9
+
10
+ ```bash
11
+ WORKDIR="${TMPDIR:-/tmp}/codex/typesafeai-cli"
12
+ mkdir -p "$WORKDIR"
13
+ cp examples/ticket/*.json "$WORKDIR/"
14
+ typesafe ask --state-file "$WORKDIR/state.json" --questions-file "$WORKDIR/questions.json"
15
+ ```
@@ -0,0 +1,25 @@
1
+ {
2
+ "enough_code_in_state": {
3
+ "type": "noul",
4
+ "instructions": "Does `files[0].hunk` require the agent to put the actual code slice into state when a question path is code, rather than a path or 'see the PR'?"
5
+ },
6
+ "what_to_do": {
7
+ "type": "choice",
8
+ "instructions": "Given `task` and `files[0].hunk`, what should we do with this skill?",
9
+ "criteria": {
10
+ "keep": "Keep questions-first collection; do not add a job catalog",
11
+ "thin": "Cut the skill down to command examples only",
12
+ "expand": "Add per-job recipes (diff, CI, routing, …)",
13
+ "abstain": "Not enough in state to decide"
14
+ }
15
+ },
16
+ "clarity": {
17
+ "type": "score",
18
+ "instructions": "How clearly does `files[0].hunk` tell an agent to attach real code when the judgment is about a change?",
19
+ "criteria": [
20
+ "Silent; agents would send only a path",
21
+ "Mentions code but easy to skip the hunk",
22
+ "Clear: a code path must contain the slice"
23
+ ]
24
+ }
25
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "task": "An agent is about to call Jev on a pull request. Decide whether the typesafe-cli skill tells that agent to attach real code, or only a file path.",
3
+ "files": [
4
+ {
5
+ "path": "src/typesafe_cli/data/typesafe-cli/SKILL.md",
6
+ "line_range": "50-74",
7
+ "hunk": "### 2. Name the decision, then write the questions\n\nDo not start by dumping context. TypeSafe's contract is the questions: they name the fields Jev is allowed to look at.\n\nThen write `$WORKDIR/questions.json`. Each question's `instructions` must point at named paths with backticks (`task`, `files[0].hunk`, `tools`). Those paths **are** the collection list.\n\n### 3. Fill only those fields\n\nIf a path is code (`files[0].hunk`), the value must be the slice (path + line range + body), not a filename and not \"see the PR.\" Prefer under ~8k tokens of code. Do not send the tree.\n"
8
+ }
9
+ ]
10
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "refund_requested": {
3
+ "type": "noul",
4
+ "instructions": "Does `message` request a refund?"
5
+ },
6
+ "intent": {
7
+ "type": "choice",
8
+ "instructions": "What does `message` ask for?",
9
+ "criteria": {
10
+ "refund": "The customer wants money returned",
11
+ "information": "The customer wants an explanation only",
12
+ "other": "Something else"
13
+ }
14
+ },
15
+ "frustration": {
16
+ "type": "score",
17
+ "instructions": "How frustrated does the customer sound in `message`?",
18
+ "criteria": [
19
+ "Calm and factual",
20
+ "Annoyed but civil",
21
+ "Very angry"
22
+ ]
23
+ }
24
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "message": "I was charged twice for order A-104. Please refund the duplicate. This is the third time.",
3
+ "policy": "Duplicate charges are eligible for an immediate refund. One-off goodwill refunds need a human."
4
+ }
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env bash
2
+ # Optional installer for the typesafe CLI. Prefer pipx if you have it:
3
+ # pipx install typesafeai-cli
4
+ #
5
+ # This script installs the wheel from the GitHub release (Pup-style
6
+ # "download the artifact") or from PyPI.
7
+ #
8
+ # ./install.sh
9
+ # TYPESAFE_CLI_VERSION=v0.2.0 ./install.sh
10
+ # TYPESAFE_CLI_FROM=pypi ./install.sh
11
+ #
12
+ # Env:
13
+ # TYPESAFE_CLI_VERSION tag like v0.2.0, or "latest" (default)
14
+ # TYPESAFE_CLI_FROM github (default) or pypi
15
+ set -euo pipefail
16
+
17
+ REPO="${TYPESAFE_CLI_REPO:-maddygoround/typesafeai-cli}"
18
+ PKG="${TYPESAFE_CLI_PKG:-typesafeai-cli}"
19
+ BIN="${TYPESAFE_CLI_BIN:-typesafe}"
20
+ VERSION="${TYPESAFE_CLI_VERSION:-latest}"
21
+ FROM="${TYPESAFE_CLI_FROM:-github}"
22
+ API="https://api.github.com/repos/${REPO}"
23
+
24
+ say() { printf '%s\n' "$*" >&2; }
25
+ die() { say "install.sh: $*"; exit 1; }
26
+
27
+ need_python() {
28
+ if ! command -v python3 >/dev/null 2>&1; then
29
+ die "python3 is required (3.10+)"
30
+ fi
31
+ python3 - <<'PY'
32
+ import sys
33
+ if sys.version_info < (3, 10):
34
+ raise SystemExit(f"python 3.10+ required, found {sys.version.split()[0]}")
35
+ PY
36
+ }
37
+
38
+ resolve_tag() {
39
+ if [ "$VERSION" != "latest" ]; then
40
+ case "$VERSION" in
41
+ v*) printf '%s\n' "$VERSION" ;;
42
+ *) printf 'v%s\n' "$VERSION" ;;
43
+ esac
44
+ return
45
+ fi
46
+ python3 - <<PY
47
+ import json, urllib.request
48
+ url = "${API}/releases/latest"
49
+ req = urllib.request.Request(url, headers={"Accept": "application/vnd.github+json", "User-Agent": "typesafe-install"})
50
+ with urllib.request.urlopen(req, timeout=30) as resp:
51
+ data = json.load(resp)
52
+ tag = data.get("tag_name") or ""
53
+ if not tag:
54
+ raise SystemExit("could not resolve latest GitHub release")
55
+ print(tag)
56
+ PY
57
+ }
58
+
59
+ wheel_url_for_tag() {
60
+ local tag="$1"
61
+ python3 - <<PY
62
+ import json, urllib.request
63
+ url = "${API}/releases/tags/${tag}"
64
+ req = urllib.request.Request(url, headers={"Accept": "application/vnd.github+json", "User-Agent": "typesafe-install"})
65
+ with urllib.request.urlopen(req, timeout=30) as resp:
66
+ data = json.load(resp)
67
+ for asset in data.get("assets") or []:
68
+ name = asset.get("name") or ""
69
+ if name.endswith(".whl"):
70
+ print(asset["browser_download_url"])
71
+ break
72
+ else:
73
+ raise SystemExit("no wheel on GitHub release ${tag}")
74
+ PY
75
+ }
76
+
77
+ have() { command -v "$1" >/dev/null 2>&1; }
78
+
79
+ install_spec() {
80
+ local spec="$1"
81
+ if have pipx; then
82
+ pipx install --force "$spec"
83
+ elif have uv; then
84
+ uv tool install --force "$spec"
85
+ else
86
+ say "pipx/uv not found; using python3 -m pip --user"
87
+ python3 -m pip install --user --upgrade "$spec"
88
+ fi
89
+ }
90
+
91
+ main() {
92
+ need_python
93
+ if [ "$FROM" = "pypi" ]; then
94
+ local spec
95
+ if [ "$VERSION" = "latest" ]; then
96
+ spec="$PKG"
97
+ else
98
+ spec="${PKG}==${VERSION#v}"
99
+ fi
100
+ say "installing ${spec} from PyPI"
101
+ install_spec "$spec"
102
+ else
103
+ local tag url tmp
104
+ tag="$(resolve_tag)"
105
+ say "using GitHub release ${tag}"
106
+ url="$(wheel_url_for_tag "$tag")"
107
+ say "wheel ${url}"
108
+ tmp="$(mktemp -d)"
109
+ trap 'rm -rf "$tmp"' EXIT
110
+ python3 - <<PY
111
+ import urllib.request
112
+ urllib.request.urlretrieve("${url}", "${tmp}/pkg.whl")
113
+ PY
114
+ install_spec "${tmp}/pkg.whl"
115
+ fi
116
+
117
+ if have "$BIN"; then
118
+ say "ok: $(command -v "$BIN")"
119
+ else
120
+ say "installed the package, but '${BIN}' is not on PATH."
121
+ say "add ~/.local/bin to PATH, then run: ${BIN} --help"
122
+ fi
123
+ }
124
+
125
+ main "$@"
@@ -0,0 +1,55 @@
1
+ [project]
2
+ name = "typesafeai-cli"
3
+ version = "0.3.1"
4
+ description = "Agent CLI for TypeSafe System One (Jev)"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ requires-python = ">=3.10"
8
+ authors = [{ name = "safeaiforeveryone" }]
9
+ keywords = ["typesafe", "jev", "cli"]
10
+ classifiers = [
11
+ "Environment :: Console",
12
+ "Intended Audience :: Developers",
13
+ "Programming Language :: Python :: 3",
14
+ "Programming Language :: Python :: 3.10",
15
+ "Programming Language :: Python :: 3.11",
16
+ "Programming Language :: Python :: 3.12",
17
+ "Topic :: Utilities",
18
+ ]
19
+ dependencies = [
20
+ "typesafe-sdk>=0.6.0",
21
+ "typer>=0.12",
22
+ ]
23
+
24
+ [project.urls]
25
+ Homepage = "https://github.com/maddygoround/typesafeai-cli"
26
+ Repository = "https://github.com/maddygoround/typesafeai-cli"
27
+ Issues = "https://github.com/maddygoround/typesafeai-cli/issues"
28
+ Releases = "https://github.com/maddygoround/typesafeai-cli/releases"
29
+
30
+ [project.scripts]
31
+ typesafe = "typesafe_cli.cli:app"
32
+
33
+ [build-system]
34
+ requires = ["hatchling"]
35
+ build-backend = "hatchling.build"
36
+
37
+ [tool.hatch.build.targets.wheel]
38
+ packages = ["src/typesafe_cli"]
39
+
40
+ [tool.hatch.build.targets.sdist]
41
+ include = [
42
+ "/src",
43
+ "/examples",
44
+ "/install.sh",
45
+ "/README.md",
46
+ "/LICENSE",
47
+ ]
48
+
49
+ [dependency-groups]
50
+ dev = ["pytest>=8"]
51
+
52
+ [tool.pytest.ini_options]
53
+ pythonpath = ["src"]
54
+ testpaths = ["tests"]
55
+ addopts = "-q"
@@ -0,0 +1 @@
1
+ __version__ = "0.3.1"
@@ -0,0 +1,93 @@
1
+ from __future__ import annotations
2
+
3
+ import typer
4
+
5
+ from typesafe_cli.commands.agent import schema as agent_schema
6
+ from typesafe_cli.commands.ask import ask
7
+ from typesafe_cli.commands.auth import status as auth_status
8
+ from typesafe_cli.commands.decide import decide
9
+ from typesafe_cli.commands.extract import extract
10
+ from typesafe_cli.commands.find import find
11
+ from typesafe_cli.commands.models import models
12
+ from typesafe_cli.commands.oneshot import choice, noul, score
13
+ from typesafe_cli.commands.rank import rank
14
+ from typesafe_cli.commands.screen import screen
15
+ from typesafe_cli.commands.skills import install as skills_install
16
+ from typesafe_cli.commands.skills import list_skills
17
+ from typesafe_cli.commands.smoke import smoke
18
+ from typesafe_cli.commands.suggest_skill import suggest_skill
19
+ from typesafe_cli.commands.verify import verify
20
+ from typesafe_cli.help import AgentCommand, AgentGroup
21
+
22
+ app = typer.Typer(
23
+ name="typesafe",
24
+ cls=AgentGroup,
25
+ no_args_is_help=True,
26
+ add_completion=False,
27
+ pretty_exceptions_enable=False,
28
+ help="Agent CLI for TypeSafe System One (Jev). Typed judgments, not chat. Agents must not read TYPESAFE_* env vars or key files; use typesafe auth status.",
29
+ )
30
+
31
+
32
+ def _on_agent(args: bool = False) -> bool:
33
+ if args:
34
+ from typesafe_cli.detect import set_force_agent
35
+
36
+ set_force_agent(True)
37
+ return args
38
+
39
+
40
+ def _on_no_agent(args: bool = False) -> bool:
41
+ if args:
42
+ from typesafe_cli.detect import set_force_agent
43
+
44
+ set_force_agent(False)
45
+ return args
46
+
47
+
48
+ @app.callback()
49
+ def _global_options(
50
+ agent: bool = typer.Option(
51
+ False,
52
+ "--agent",
53
+ help="Force agent mode (JSON help/schema)",
54
+ is_eager=True,
55
+ callback=_on_agent,
56
+ ),
57
+ no_agent: bool = typer.Option(
58
+ False,
59
+ "--no-agent",
60
+ help="Disable agent mode",
61
+ is_eager=True,
62
+ callback=_on_no_agent,
63
+ ),
64
+ ) -> None:
65
+ del agent, no_agent
66
+
67
+
68
+ auth_app = typer.Typer(cls=AgentGroup, no_args_is_help=True, help="Authentication helpers")
69
+ auth_app.command("status", cls=AgentCommand)(auth_status)
70
+ app.add_typer(auth_app, name="auth")
71
+
72
+ agent_app = typer.Typer(cls=AgentGroup, no_args_is_help=True, help="Agent discovery")
73
+ agent_app.command("schema", cls=AgentCommand)(agent_schema)
74
+ app.add_typer(agent_app, name="agent")
75
+
76
+ skills_app = typer.Typer(cls=AgentGroup, no_args_is_help=True, help="Install the official TypeSafe skill")
77
+ skills_app.command("install", cls=AgentCommand)(skills_install)
78
+ skills_app.command("list", cls=AgentCommand)(list_skills)
79
+ app.add_typer(skills_app, name="skills")
80
+
81
+ app.command("ask", cls=AgentCommand)(ask)
82
+ app.command("noul", cls=AgentCommand)(noul)
83
+ app.command("choice", cls=AgentCommand)(choice)
84
+ app.command("score", cls=AgentCommand)(score)
85
+ app.command("find", cls=AgentCommand)(find)
86
+ app.command("rank", cls=AgentCommand)(rank)
87
+ app.command("extract", cls=AgentCommand)(extract)
88
+ app.command("verify", cls=AgentCommand)(verify)
89
+ app.command("screen", cls=AgentCommand)(screen)
90
+ app.command("suggest-skill", cls=AgentCommand)(suggest_skill)
91
+ app.command("decide", cls=AgentCommand)(decide)
92
+ app.command("models", cls=AgentCommand)(models)
93
+ app.command("smoke", cls=AgentCommand)(smoke)