ConversationAdvisor (gnosis)
A CLI for ingesting and analyzing AI agent conversation logs (Claude Code, Codex) into a local SQLite store. Tracks every conversation through a three-stage lifecycle — discovered → imported → analyzed — so the expensive LLM steps (intent classification, pain-point detection) run once and their results are persisted for later inspection.
Features
- Stateful ingestion —
gnosis discoverwalks the default Claude (~/.claude/projects/) and Codex (~/.codex/sessions/) roots, parses every session file, and stores conversations + messages into a SQLite DB. Incremental: re-running only appends new turns. - Intent classification —
gnosis conversation classifysegments a conversation into discrete tasks by classifying each user message along two axes (Type + Lifecycle). New-Task lifecycle entries become task rows. - Pain-point analysis —
gnosis task analyzeruns a LangGraph DAG (scanner → classifier → resolver) against each task and persists problems with severity, suggested fix, and message-range evidence. - Friction-derived effectiveness — the headline
mean_effectivenessmetric (and the THS effectiveness weight, shown as the Friction bar) is computed byhla.models.friction_score: the deterministic per-tool effectiveness mean × a confirmed-friction-episode penalty. The deterministic tool scorer is an input to friction, not a separate metric. Per-messageeffectiveness(the tool component) is still stamped on Tool messages. See docs/effectiveness-metric.md and the friction-analysis section of CLAUDE.md. - Idempotent re-runs — every stage skips work that's already cached in the store;
--rebuildflags force re-computation when needed. - Two output formats — most read-only commands accept
--format table|json. JSON output is unconstrained (no truncation) for machine consumption. - Two providers — Anthropic (claude-haiku) or Ollama (qwen2.5:3b) for local inference.
Requirements
- Python 3.12+
- One of:
- An Anthropic API key
- Ollama running locally with
qwen2.5:3bpulled
Installation
uv sync # primary path; reads uv.lock
uv sync --extra dev # adds pytest, black, mypy, ruff, torch
uv sync --extra lint # just black/ruff/mypy (matches the CI lane)
pip install -e . works too — same [project] section in pyproject.toml. One caveat: the optional
embark-code-methods wheel (tree-sitter structure triage for gnosis labs review-current-branch; everything
else works without it) lives on the JetBrains package index, which uv resolves automatically via
[tool.uv.sources] but plain pip does not:
pip install -e ".[embark]" --extra-index-url https://packages.jetbrains.team/pypi/p/grazi/jetbrains-ai-platform-public/simple
To install the published package from PyPI (base feature set, no repo checkout):
pip install gnosis-air # or: uv tool install gnosis-air
For the Anthropic provider, drop your key in .env at the repo root or pass it via --key:
echo 'ANTHROPIC_API_KEY=sk-ant-…' > .env
For Ollama:
ollama pull qwen2.5:3b
ollama serve
The SQLite store lives at ~/.local/share/gnosis/gnosis.sqlite3 by default. Override per-invocation via --db-path (on discover/stats) or via the GNOSIS_DB_PATH env var (every command).
Embedding in another application
If you're calling gnosis as a subprocess from another application (an IDE plugin, a desktop app, a CI step), the parent doesn't need to assume uv, pip, or even Python is set up the way the user's interactive shell expects. Use scripts/bootstrap.py to install uv (if missing) and then install gnosis into an isolated uv tool venv. Cross-platform — works on Linux, macOS, and Windows.
import subprocess, sys
# One-time bootstrap. Idempotent — safe to call on every launch.
result = subprocess.run(
[sys.executable, "scripts/bootstrap.py", "--gnosis-ref", "v0.3.0"],
capture_output=True, text=True, check=True,
)
gnosis_binary = result.stdout.strip().splitlines()[-1]
# Every subsequent invocation: subprocess the binary directly.
subprocess.run(
[gnosis_binary, "conversation", "messages", "3", "--task", "22", "--format", "json"],
env={**os.environ, "GNOSIS_DB_PATH": "/path/parent/controls",
"ANTHROPIC_API_KEY": "sk-ant-…"},
capture_output=True, check=True,
)
The bootstrap script:
- Looks for
uvon PATH and at astral's standard install locations (~/.local/bin/uv, etc.). - If missing, runs astral's official installer (
curl … | shon Unix;irm … | iexon Windows) — no third-party hosts. - Runs
uv tool install --python 3.12 git+<gnosis-url>@<ref>to create an isolated venv. - Verifies the install by importing gnosis's LLM entry chain in the tool venv, so an incomplete env (e.g. a compiled transitive dep dropped by a partial/proxy-blocked download) fails at bootstrap time with a clear message rather than as a mystery
ModuleNotFoundErrorat runtime. - Mirrors everything — uv's output, exit code, resolved path, verification result — to a timestamped log at
<tempdir>/gnosis/gnosis-bootstrap__<ts>.log, so a failed install stays inspectable after the subprocess exits. - Prints the resolved gnosis binary path to stdout's last line; progress goes to stderr.
Pass --reinstall (alias --force) to force a full rebuild of gnosis and all its dependencies — the repair path when a user's env is left incomplete (uv tool install is otherwise a no-op if gnosis is already present):
subprocess.run([sys.executable, "scripts/bootstrap.py", "--gnosis-ref", "v0.3.0", "--reinstall"], check=True)
The parent should always invoke gnosis with --format json (machine-parseable output) and pass --key / --db-path (or the ANTHROPIC_API_KEY / GNOSIS_DB_PATH env vars) so the embedded gnosis never touches the user's .env or default state path. See python scripts/bootstrap.py --help for the available flags.
CLI Reference
Every command supports -h / --help. The CLI is a Click group; sub-groups (conversation, task) have their own subcommands.
Lifecycle in 30 seconds
gnosis discover # 1. ingest all local sessions into the store
gnosis conversation classify 7 # 2. classify intents + extract tasks for conversation #7
gnosis task analyze 7 # 3. run pain-point analysis on every task in #7
gnosis task show 7 # 4. browse the results
Each step is incremental and persists its output, so step 4 doesn't re-run the LLM. The conversation's status field (visible in gnosis conversation list) reflects how far it's progressed:
Discovered → Imported → Analyzed
(1) (2) (3)
discover — ingest trajectories into the store
gnosis discover [--extra-folder PATH]... [--db-path PATH]
[--rebuild | --rebuild-conversation PATH...]
[--benchmark] [-v]
Scans ~/.claude/projects/ and ~/.codex/sessions/ (plus any --extra-folder paths), parses every session, and upserts conversations + messages into the store. Idempotent — re-running only appends new turns.
| Flag | Purpose |
|---|---|
--extra-folder PATH |
Additional folder to scan recursively for *.jsonl. Repeatable. |
--db-path PATH |
Override the default DB location. Beats $GNOSIS_DB_PATH. |
--rebuild |
Drop every conversation row first, then re-import. FK cascade clears messages, intents, tasks, problems too. |
--rebuild-conversation PATH |
Scoped rebuild — drop one conversation's row + cascade, then re-ingest it. Repeatable. Mutually exclusive with --rebuild. |
--benchmark |
Print a stage time / tokens / cost report at the end. (Discover doesn't call any LLMs, so tokens are zero — useful for seeing how long the discovery walk vs ingest pass took.) |
-v / --verbose |
Per-call token usage, parser skips, progress bars. |
gnosis discover # default roots only
gnosis discover --extra-folder ~/old-sessions/ # plus an archive folder
gnosis discover --rebuild # wipe + re-import everything
gnosis discover --rebuild-conversation ~/.claude/projects/.../foo.jsonl
gnosis discover --benchmark # include the per-stage report
conversation list — browse the store
gnosis conversation list [--format table|json]
Compact table sorted by stable # identifier. Columns: #, Provider, Title, Path, Project, Status. Read-only — no LLM.
gnosis conversation list
gnosis conversation list --format json
conversation show — full record for one conversation
gnosis conversation show <id-or-path> [--format table|json]
Identifier is the # from list (integer) or the absolute path on disk. Prints every stored field plus the tasks summary.
gnosis conversation show 7
gnosis conversation show /Users/me/.claude/projects/.../uuid.jsonl
gnosis conversation show 7 --format json
conversation classify — segment one or more conversations into tasks
gnosis conversation classify [IDENTIFIER]
[--project NAME] [--folder PATH] [--classify-all]
[--benchmark]
[-p ollama|anthropic] [--key KEY] [-v]
Runs the two-axis intent classifier on every human message in each matched conversation, in chunks of ≤ 4 humans per LLM call. Incremental: humans with a stored intent are skipped. New-Task lifecycle classifications become task rows; the conversation flips to Imported.
Exactly one selector must be set:
| Selector | Effect |
|---|---|
<IDENTIFIER> |
One conversation, by # (digits) or absolute path. |
--project NAME |
Every conversation whose project column equals NAME. |
--folder PATH |
Every conversation whose path starts with PATH/. |
--classify-all |
Every conversation in the store. |
Conditional re-classification wipe. If at least one new intent is saved this run, prior analysis state is invalidated: tasks rows replaced (their analyzed_at/token columns dropped), all problems deleted, last_analysis cleared. On a true no-op re-run (every human already classified), nothing changes — analysis state is preserved.
--benchmark prints a per-conversation time / tokens / cost report at the end.
gnosis conversation classify 7 # single by #
gnosis conversation classify /Users/me/.claude/.../uuid.jsonl # single by path
gnosis conversation classify --project /Users/me/projects/foo # by project
gnosis conversation classify --folder ~/.claude/projects/ # by folder prefix
gnosis conversation classify --classify-all # everything
gnosis conversation classify --classify-all --benchmark
conversation friction — multi-tier inefficiency analysis
gnosis conversation friction <id-or-path>
[--rebuild] [--benchmark] [--format table|json]
[-p ollama|anthropic] [--key KEY] [-v]
Finds the friction a per-tool-call scan misses — defects the user had to paste back, dissatisfaction loops, blocked stalls, wrong-context requests, output-visibility misses. A deterministic pass (no LLM) surfaces candidate episodes; a single batched LLM call then adjudicates each (real? root cause? wasted turns? one-line remedy). ~$0.02 per conversation on Haiku.
Results persist to the store. Re-running renders the cached verdicts without an LLM call; pass --rebuild to re-detect and re-adjudicate. --format json emits the full payload (episodes + verdicts + a wasted-turn rollup).
gnosis conversation friction 2 # detect + adjudicate (or render cached)
gnosis conversation friction 2 --benchmark # + time / tokens / cost
gnosis conversation friction 2 --format json # structured payload
gnosis conversation friction 2 --rebuild # force re-analysis
task list — browse one conversation's tasks
gnosis task list <id-or-path> [--format table|json]
Compact table: #, Status (not analyzed / analyzed), Type, Position, Title. Read-only.
gnosis task list 7
gnosis task list 7 --format json
task show — detailed task view
gnosis task show <id-or-path> [--task N] [--format table|json]
Without --task: every task is printed in full detail. With --task N: just that one (errors if out of range).
Each task includes: intent segments (text + Type + Lifecycle), message-range start → end, start_time / end_time from the source JSONL, per-type step counts (user, agent, tool), analyzed_at and tokens spent (when analyzed), and any persisted problems with their severity / fix / analysis. Read-only.
gnosis task show 7
gnosis task show 7 --task 2
gnosis task show 7 --format json
task analyze — find pain points in a conversation
gnosis task analyze <id-or-path> [--task N] [--rebuild] [--benchmark]
[-p ollama|anthropic] [--key KEY] [-v]
Runs the LangGraph pain-point pipeline against each task in a conversation. Per-task idempotent — tasks already analyzed (analyzed_at stamped) are rendered from the store without re-running the LLM. With --task N it scopes to one task.
--rebuild clears problems + analyzed_at for the scope (whole conversation or single task) up front, then re-analyzes everything. Use after fixing prompt/scoring code or when you want a fresh read.
--benchmark prints a per-task time / tokens / cost report at the end.
On success: problems persisted, tasks.analyzed_at / input_tokens / output_tokens stamped per task, conversations.last_analysis updated → status flips to Analyzed.
gnosis task analyze 7 # analyze unanalyzed tasks in #7
gnosis task analyze 7 --task 2 # only task #2 (cached if already done)
gnosis task analyze 7 --rebuild # force re-analysis of every task
gnosis task analyze 7 --task 2 --rebuild # force re-analysis of just task #2
gnosis task analyze 7 --benchmark # add the per-stage report
stats — store summary
gnosis stats [--db-path PATH]
Prints: total conversations, distinct projects, median human/agent/tool messages per conversation.
gnosis stats
GNOSIS_DB_PATH=/tmp/scratch.sqlite3 gnosis stats
score and intents (legacy, file-based)
These two haven't been migrated to the store. They take a session file path (or directory) directly.
gnosis score <session.jsonl> [-p anthropic|ollama] [--key KEY] [-v]
gnosis intents <session.jsonl> [-p anthropic|ollama] [--key KEY] [-v]
score returns one of Bad / Ok / Good for the whole conversation. intents prints the full per-message intent table.
Architecture
High-level data flow
The pipeline is session file → parsed messages → SQLite store → on-demand LLM stages. The store is the single source of truth for everything except the freshly-parsed BaseMessage list during discover.
flowchart TB
classDef store fill:#dde,stroke:#666
classDef agent fill:#fdd,stroke:#666
subgraph Files["~/.claude/projects/, ~/.codex/sessions/, --extra-folder"]
SF[".jsonl files"]
end
SF --> Disc
subgraph Layers["src/"]
direction TB
Disc["discovery/<br/>find trajectory files<br/>per provider"]
Pars["parsers/<br/>JSONL → list[BaseMessage]<br/>(Claude / Codex)"]
HLA["hla/<br/>conversation_outline<br/>(skeleton, chunks, IDE-strip)"]
Agents["agents/<br/>intent_analyzer<br/>conversation_optimizer<br/>user_satisfaction"]
Comp["analyzers/<br/>comprehensive_scan/<br/>(LangGraph DAG)"]
Cmds["commands/<br/>one file per CLI verb"]
Store[("storage/<br/>SQLite store<br/>~/.local/share/gnosis/<br/>gnosis.sqlite3")]
end
Disc --> Pars
Pars --> HLA
HLA --> Agents
Agents --> Comp
Cmds <--> Store
Cmds --> Agents
Pars --> Store
Store:::store
Agents:::agent
Comp:::agent
Store -. read .-> CLI["gnosis CLI<br/>(Click)"]
CLI --> Cmds
Layer rules (load-bearing — see CLAUDE.md for the full list):
commands/is the only layer the CLI talks to.agents/is where every LLM call lives. Never imports fromparsers/ordiscovery/— agents work on already-parsedBaseMessagelists.hla/(no-LLM data shaping) andagents/(LLM) are siblings.storage/is provider-agnostic; everything incommands/,agents/, andhla/reads/writes through its helpers.
SQLite schema
Five tables, all in SQLite STRICT mode. The lifecycle is encoded in the FK cascades.
erDiagram
conversations ||--o{ messages : "cascades on delete"
messages ||--o| intents : "(path, position) cascades"
intents ||--o| tasks : "(path, position) cascades"
conversations ||--o{ problems : "cascades on delete"
conversations {
INTEGER id "stable # identifier"
TEXT path PK "absolute trajectory path"
TEXT provider "claude | codex"
TEXT title
TEXT project
TEXT created_at
TEXT last_updated_at
TEXT first_ingested_at
TEXT last_ingested_at
TEXT last_task_analysis "stamped by conversations classify"
TEXT last_analysis "stamped by analyze"
}
messages {
TEXT conversation_path PK,FK
INTEGER position PK "0-based parser ordinal"
TEXT type "human | agent | tool"
TEXT uuid "stable for claude"
TEXT timestamp "from raw JSONL entry"
TEXT payload "pydantic model_dump_json"
}
intents {
TEXT conversation_path PK,FK
INTEGER position PK,FK "→ messages.position"
TEXT title
TEXT segments_json "JSON list[{text, type, lifecycle}]"
TEXT classified_at
}
tasks {
TEXT conversation_path PK,FK
INTEGER number PK "1-indexed per conversation"
INTEGER position UK,FK "→ intents.position"
TEXT type
TEXT title
TEXT analyzed_at "NULL = classified, NOT NULL = analyzed"
INTEGER input_tokens
INTEGER output_tokens
}
problems {
TEXT conversation_path FK
INTEGER task_number "FK by convention; NULL for fallback runs"
TEXT type
INTEGER relevant_message_range_start
INTEGER relevant_message_range_end
REAL severity
TEXT description
TEXT suggested_fix
TEXT analysis
TEXT created_at
}
The cascade chain: deleting a conversation row → cascades to its messages and problems rows → which cascades to intents → which cascades to tasks. This is what makes discover --rebuild a clean wipe and what keeps --rebuild-conversation <path> precisely scoped.
Per-conversation status derivation
Computed in storage/conversations.py:compute_status from two signals:
stateDiagram-v2
[*] --> Discovered : gnosis discover
Discovered --> Imported : gnosis conversation classify
Imported --> Analyzed : gnosis task analyze
Analyzed --> Imported : conversation classify (invalidates analysis)
Imported --> Imported : conversation classify (incremental)
Analyzed --> Analyzed : analyze (cached re-render)
Analyzed --> Analyzed : analyze --rebuild
Discovered—taskstable is empty for this conversation.Imported— at least one task row,last_analysis IS NULL.Analyzed—last_analysis IS NOT NULL.
Per-task status is independent and shown in task list / task show:
not analyzed— task row exists,analyzed_at IS NULL.analyzed—analyzed_at IS NOT NULL(problems persisted, tokens recorded).
discover flow
flowchart TD
cli["gnosis discover [--extra-folder PATH]... [--rebuild | --rebuild-conversation PATH]..."]
cli --> resolveDB["resolve DB path<br/>(--db-path > $GNOSIS_DB_PATH > default)"]
resolveDB --> openDB["open + migrate schema<br/>(CREATE TABLE IF NOT EXISTS + idempotent ALTER TABLE)"]
openDB --> rebuild{--rebuild?}
rebuild -- yes --> wipeAll["DELETE FROM conversations<br/>(cascades to all tables)"]
rebuild -- no --> rebuildOne{--rebuild-conversation?}
rebuildOne -- yes --> wipeOne["DELETE FROM conversations<br/>WHERE path IN (...) (cascades)"]
rebuildOne -- no --> walk
wipeAll --> walk
wipeOne --> walk
walk["discover_trajectories(project=None)<br/>+ glob each --extra-folder"]
walk --> perTraj[for each trajectory]
perTraj --> parse["read + parsers.get_messages(content)<br/>list[BaseMessage] with timestamps"]
parse --> fill["hla.fill_tool_success(msgs[existing_count:])<br/>(sentence-transformer; cached centroid)"]
fill --> upsert["upsert conversation + append messages<br/>(BEGIN IMMEDIATE; idempotent)"]
upsert --> uuidCheck{first N stored uuids<br/>match parsed prefix?<br/>(claude only)}
uuidCheck -- yes --> next[next trajectory]
uuidCheck -- no --> skip["skip with warning<br/>(tree branch divergence)"]
skip --> next
next --> done(["summary line:<br/>N scanned, M new conversations,<br/>K new messages"])
conversation classify flow
flowchart TD
cli["gnosis conversation classify (one selector required)"]
cli --> resolve["find_conversation_by_identifier<br/>(digits → id, else path)"]
resolve --> miss{found?}
miss -- no --> err1["Error → stderr"]
miss -- yes --> load["load_messages + load_intents"]
load --> diff["new_positions = {all humans} − {classified}"]
diff --> any{new_positions empty?}
any -- yes --> stamp
any -- no --> chunk["create_messages_skeleton_outline<br/>chunk_messages(max_humans=4)"]
chunk --> filter["keep chunks containing ≥1 new human"]
filter --> llm["per chunk:<br/>analyze_intents_from_outline<br/>(retry once on missing indexes)"]
llm --> persist["save_intents (INSERT OR IGNORE)<br/>only new positions"]
persist --> stamp
stamp["compute task boundaries<br/>save_tasks (DELETE + INSERT)<br/>set_last_task_analysis(now)<br/>delete_problems(path)<br/>UPDATE last_analysis = NULL"]
stamp --> summary["summary:<br/>N tasks identified<br/>+ note if prior analysis was cleared"]
task analyze flow
flowchart TD
cli["gnosis task analyze <id-or-path> [--task N] [--rebuild] [--benchmark]"]
cli --> guard["check: conversation exists,<br/>last_task_analysis IS NOT NULL"]
guard --> scope{--task N?}
scope -- yes --> single["scope = [task N]"]
scope -- no --> all["scope = every task"]
single --> rebuildQ
all --> rebuildQ
rebuildQ{--rebuild?}
rebuildQ -- yes --> wipe["upfront wipe in scope:<br/>delete_problems(...) +<br/>UPDATE tasks SET analyzed_at=NULL"]
rebuildQ -- no --> part["partition scope<br/>by analyzed_at IS NULL/NOT NULL"]
wipe --> part2["to_analyze = scope<br/>cached = []"]
part --> loop
part2 --> loop
loop[for each task in to_analyze]
loop --> benchmark["with benchmark(task #N) as section:<br/>find_problems(msgs[start:end])<br/>(comprehensive_scan LangGraph:<br/>Scanner → Classifier×N → Resolver)"]
benchmark --> save["BEGIN IMMEDIATE:<br/>delete_problems(path, task=N)<br/>save_problems(...)<br/>mark_task_analyzed(N, tokens, now)"]
save --> loop
loop -- done --> render["load_problems(path)<br/>reconstruct full TaskAnalysis from cached + new<br/>+ stored task ranges<br/>compute IQC / per-task TQC<br/>print report + benchmark"]
find_problems invokes the LangGraph DAG in analyzers/comprehensive_scan/ — a pre-scanner node first stamps anchor-driven effectiveness scores on each Tool / Agent message (see docs/effectiveness-metric.md); a cost-scanner stamps cost_effectiveness on each Human; then scanner finds suspect ranges, classifier verifies each via a sub-agent with get_messages_by_range / get_detailed_message tools, and resolver assigns severity + fix. See CLAUDE.md for the full walkthrough.
Project structure
.
├── src/
│ ├── gnosis.py # Click CLI entry point
│ ├── main.py # legacy argparse entry (discover/show)
│ ├── llm.py # provider abstraction + llm_invoke
│ ├── utils.py
│ ├── tool_output_classifier.py # sentence-transformer for tool success/failure
│ ├── pattern_scanner.py # heuristic helpers (unused; pending cleanup)
│ ├── commands/ # one file per CLI verb
│ │ ├── analyze.py, conversations.py,
│ │ │ discover.py, intents.py, score.py,
│ │ │ show.py, stats.py, tasks.py
│ │ └── runner.py # iter_session_files + run_over_path
│ ├── parsers/ # JSONL → list[BaseMessage]
│ │ ├── base.py, claude.py, codex.py, messages.py
│ ├── discovery/ # find session files per provider
│ ├── storage/ # SQLite store
│ │ ├── db.py # connection, schema bootstrap, migrations
│ │ └── conversations.py # CRUD + StoredTask/StoredIntent/StoredProblem
│ ├── hla/ # non-LLM data shaping
│ │ ├── conversation_outline.py # outline, skeleton, chunking, ide-block strip
│ │ ├── benchmark.py # BenchmarkSection + UsageTracker
│ │ └── models.py # Problem, TaskAnalysis, ByTaskAnalysisResult
│ ├── agents/ # LLM-driven analyzers
│ │ ├── intent_analyzer.py # two-axis classification
│ │ ├── conversation_optimizer.py # find_problems (entry to comprehensive_scan)
│ │ └── user_satisfaction.py
│ ├── analyzers/comprehensive_scan/ # LangGraph DAG: scanner → classifier → resolver
│ ├── analysis_tools/ # LangChain tools used by sub-agents
│ └── contracts/ # shared types (IntentType, TaskBoundary, etc.)
├── data/sessions/ # bundled sample conversations
├── experiments/ # exploratory notebooks (not tests)
└── .github/workflows/lint.yml # black + ruff + mypy on every PR
LLM providers
LLMConfig in src/llm.py carries provider, model, and per-million-token pricing. Two presets:
| Preset | Provider | Model |
|---|---|---|
LLMConfig.claude_haiku_4_5_20251001() |
Anthropic | claude-haiku-4-5-20251001 |
LLMConfig.ollama_qwen_2_5__3b() |
Ollama | qwen2.5:3b |
llm_invoke() is the single entry point — it builds the langchain client, runs structured-output inference, wires the UsageTracker callback so per-task tokens flow into the active BenchmarkSection, and prints per-call cost.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gnosis_air-0.0.3.tar.gz.
File metadata
- Download URL: gnosis_air-0.0.3.tar.gz
- Upload date:
- Size: 841.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ff0a4af42472b9012bb300cdeacbc432e3c7e7c3ea71bf16f50350e5c1ff4e92
|
|
| MD5 |
f0b945c1c485829503d6e9049ea67f94
|
|
| BLAKE2b-256 |
4757bd7285b6dfc13b71e28e92bfad7497e1f57f83efbd51d87e6e96dd8b119d
|
Provenance
The following attestation bundles were made for gnosis_air-0.0.3.tar.gz:
Publisher:
publish-pypi.yml on JetBrains/air-trajectory-analysis
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gnosis_air-0.0.3.tar.gz -
Subject digest:
ff0a4af42472b9012bb300cdeacbc432e3c7e7c3ea71bf16f50350e5c1ff4e92 - Sigstore transparency entry: 2477768139
- Sigstore integration time:
-
Permalink:
JetBrains/air-trajectory-analysis@4ff5311e72979cadd2e418fd028572b34ad61691 -
Branch / Tag:
refs/tags/v0.0.3 - Owner: https://github.com/JetBrains
-
Access:
internal
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@4ff5311e72979cadd2e418fd028572b34ad61691 -
Trigger Event:
release
-
Statement type:
File details
Details for the file gnosis_air-0.0.3-py3-none-any.whl.
File metadata
- Download URL: gnosis_air-0.0.3-py3-none-any.whl
- Upload date:
- Size: 842.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5d8e0c99edd4428931cc0d3c4039e98cd931dcb94adda52d831a7a56556aed4e
|
|
| MD5 |
24af1465d30691de7c7426efaa6b67ab
|
|
| BLAKE2b-256 |
a014de8f8756f001f27ff0c2bcbbdc3cad93f252f2fdde2a582eed359f2e0149
|
Provenance
The following attestation bundles were made for gnosis_air-0.0.3-py3-none-any.whl:
Publisher:
publish-pypi.yml on JetBrains/air-trajectory-analysis
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gnosis_air-0.0.3-py3-none-any.whl -
Subject digest:
5d8e0c99edd4428931cc0d3c4039e98cd931dcb94adda52d831a7a56556aed4e - Sigstore transparency entry: 2477768196
- Sigstore integration time:
-
Permalink:
JetBrains/air-trajectory-analysis@4ff5311e72979cadd2e418fd028572b34ad61691 -
Branch / Tag:
refs/tags/v0.0.3 - Owner: https://github.com/JetBrains
-
Access:
internal
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@4ff5311e72979cadd2e418fd028572b34ad61691 -
Trigger Event:
release
-
Statement type: