Skip to main content

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 ingestiongnosis discover walks 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 classificationgnosis conversation classify segments a conversation into discrete tasks by classifying each user message along two axes (Type + Lifecycle). New-Task lifecycle entries become task rows.
  • Pain-point analysisgnosis task analyze runs 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_effectiveness metric (and the THS effectiveness weight, shown as the Friction bar) is computed by hla.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-message effectiveness (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; --rebuild flags 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.

Getting started

Requires Python 3.12+ and an Anthropic API key (or a local Ollama with qwen2.5:3b pulled).

1. Install gnosis

The package is published on PyPI as gnosis-air. Install it as a global CLI:

uv tool install gnosis-air    # or: pipx install gnosis-air

pip install gnosis-air works too. It installs into the current environment, so gnosis is only on PATH while that environment is active. See Installation for details.

2. Install the companion tools

gnosis install

This self-provisions the three external CLIs gnosis builds on: jbcontext, chatter, and embark-call-graph. No API key needed. Re-running is safe — already-installed components are skipped. Check state anytime:

gnosis install --status

3. Log in

With a JetBrains AI token, store it once as a persistent session:

gnosis login    # prompts for the token; or: gnosis login --token <JWT>

Alternatively, with an Anthropic API key, set the env var (or drop it in .env) — no login command needed:

export ANTHROPIC_API_KEY=sk-ant-…

The session is stored locally and refreshed automatically on use. Clear it with gnosis logout.

4. Instrument a project

Run this inside any git repository you use with Claude Code:

cd ~/projects/my-app
gnosis instrument

This makes the project self-analyzing. It installs the agent-facing skills, adds a knowledge prompt to the project's CLAUDE.md, and starts a background watcher that ingests, classifies, and analyzes new conversations as they land on disk. Undo everything with:

gnosis instrument --remove

How it works

What gnosis install does

gnosis install downloads and verifies three external CLIs — the same way the IDE plugin deploys them:

Component What it is How it installs
jbcontext Semantic code-search index Hosted JetBrains Context installer
chatter Conversation ground truth (transcripts, files touched, line-level blame) CDN zip, pinned by a vendored release manifest, sha256-verified into $GNOSIS_HOME/bin
embark-call-graph Call-graph index over the repo Bundled install script (uv tool install from a public index)

Behavior:

  • Idempotent — components already present are skipped.
  • --update upgrades installed components through their own upgrade channels (chatter deliberately stays pinned to the manifest — no version drift).
  • --force reinstalls from scratch and wins over --update.
  • --only <component> scopes the run to one component. Repeatable.
  • --status installs nothing — prints one read-only JSON snapshot of every component's install, index, and daemon state.

The subsystems

gnosis composes four pieces:

  • chatter — what the conversation did. Reads the conversation DB maintained by the IDE extension. Supplies ground truth: chat listings, per-chat file sets, and line-level blame that maps surviving lines of code back to the exact conversation and tool call that wrote them.
  • jbcontext (context) — where the code is. A semantic, embedding-based code-search index of the repository. Powers gnosis knowledge search-code and the code edges of the knowledge graph.
  • embark-call-graph — how the code connects. A call-graph index. Names the method enclosing any line and builds per-change dependency graphs for the branch reviewer.
  • gnosis itself — the store and the pipeline. A local SQLite store of parsed conversations, the analysis stages below, the gnosis knowledge hypermedia graph linking tasks ↔ code ↔ knowledge items, and the watch daemon that keeps everything fresh.

All external calls are read-only subprocess calls. A missing component or unbuilt index degrades gracefully — gnosis returns thinner results instead of failing.

The conversation analysis pipeline

Every conversation moves through a stateful lifecycle. Each stage persists its result to the store, so the expensive LLM work runs once.

.jsonl session file
      │  discover       parse + ingest into SQLite        (no LLM)
      ▼
 Discovered
      │  classify       intent labels → task boundaries   (LLM)
      ▼
  Imported
      │  analyze        pain-point + friction analysis    (LLM)
      ▼
  Analyzed
  1. Discover — walks the Claude / Codex session roots, parses every .jsonl into typed messages, and upserts them into the store. Incremental: re-running only appends new turns.
  2. Classify — labels every human message on two axes (intent Type + Lifecycle). "New Task" messages become task boundaries.
  3. Analyze — runs pain-point detection per task, plus friction analysis: a deterministic detector proposes candidate friction episodes, and a single batched LLM call adjudicates each one.

gnosis instrument automates all of this: a filesystem watcher debounces session-file events and pushes each change through discover → classify → knowledge extraction → analyze in-process, so the store stays current without manual runs.

Common operations

Review your branch

Run inside a feature branch. The reviewer collects everything the branch changed (merge-base → working tree), recovers prior decisions recorded for those code locations, and writes a self-contained HTML report:

gnosis labs review-current-branch                 # writes review.html + review.json
gnosis labs review-current-branch -o /tmp/r.html --benchmark
gnosis labs review-current-branch --branch feature/foo    # review a branch's committed state
gnosis labs review-current-branch --format json           # structured output for tooling

Two guardrails refuse before any LLM spend: reviewing main/master directly (pass --allow-base-branch to override), and a surface larger than --max-files (default 100 — usually a wrong --base). Re-running on an unchanged branch reuses cached LLM results.

Search code and history

Semantic search over the codebase and over knowledge extracted from past conversations:

gnosis knowledge search-code "where are auth tokens refreshed"   # find code by meaning
gnosis knowledge search "why we pinned the chatter version"      # find past decisions

Both print a JSON envelope whose links[] are runnable follow-up commands — from a hit you can jump to the owning task, its knowledge items, or the code it touched. gnosis knowledge conversations is the graph's entry point. No API key needed; all knowledge commands are read-only.

Inspect analysis results

gnosis conversation list          # every conversation + lifecycle status
gnosis conversation show 7        # one conversation in full
gnosis task show 7 --task 2       # one task: intents, problems, tokens
gnosis conversation friction 7    # inefficiency episodes + verdicts
gnosis stats                      # store totals
gnosis budget report              # LLM spend per command over time

Most read-only commands accept --format table|json.

Check gnosis's own status

gnosis status install     # external CLIs: not-installed / installed / running / error
gnosis status analysis    # watched directories + the conversation pipeline funnel (scoped to live watchers; out-of-scope rows = "Unwatched conversations")
gnosis status analysis --diagnose   # + troubleshooting checks: why isn't work progressing (dead watcher, auth gate, silent ingest failure, stuck/unwatched pending, parked work…); exits 1 on a failing check
gnosis status health      # live probes of each CLI's `status` verb; exits 1 on failure

status health judges each dependency from its response text, not just its exit code (an expired jbcontext token prints Error: … 401 …), and prints the full response for anything unhealthy — the right command to script a preflight around. gnosis install --status remains the machine-readable state dump (always exits 0).

Requirements

Installation

The CLI is distributed on PyPI as gnosis-air (the name gnosis on PyPI belongs to an unrelated package — installing it will NOT give you this tool). For a global CLI, install it with an isolated tool installer so the gnosis executable lands on your PATH:

uv tool install gnosis-air   # or: pipx install gnosis-air
gnosis --help

Plain pip install gnosis-air works too, but pip installs into the current environment: the gnosis executable goes to that environment's bin/ (e.g. .venv/bin/gnosis or the user-site scripts dir), which is only on PATH when that environment is active. If gnosis is "missing" after a pip install, that's why — prefer uv tool/pipx for a machine-wide CLI.

One hard requirement on the interpreter: gnosis's store loads the sqlite-vec extension, so the Python running it must be built with loadable SQLite extension support. uv-managed and python.org builds have it; pyenv-compiled Pythons usually don't (unless built with PYTHON_CONFIGURE_OPTS="--enable-loadable-sqlite-extensions") and fail at first DB access with AttributeError: 'sqlite3.Connection' object has no attribute 'enable_load_extension' — another reason uv tool install is the recommended path.

Structure triage for gnosis labs review-current-branch (mechanical formatting/comment-only files detected without LLM analysis) is built in: the tree-sitter wrapper is vendored and the grammar wheels are ordinary PyPI dependencies. No extras or extra package indexes are needed for any feature.

Development install (repo checkout)

uv sync                     # 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.

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 uv on PATH and at astral's standard install locations (~/.local/bin/uv, etc.).
  • If missing, runs astral's official installer (curl … | sh on Unix; irm … | iex on 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 ModuleNotFoundError at 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 from parsers/ or discovery/ — agents work on already-parsed BaseMessage lists.
  • hla/ (no-LLM data shaping) and agents/ (LLM) are siblings.
  • storage/ is provider-agnostic; everything in commands/, agents/, and hla/ 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
  • Discoveredtasks table is empty for this conversation.
  • Imported — at least one task row, last_analysis IS NULL.
  • Analyzedlast_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.
  • analyzedanalyzed_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 &lt;id-or-path&gt; [--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

gnosis_air-0.0.14.tar.gz (1.1 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gnosis_air-0.0.14-py3-none-any.whl (1.0 MB view details)

Uploaded Python 3

File details

Details for the file gnosis_air-0.0.14.tar.gz.

File metadata

  • Download URL: gnosis_air-0.0.14.tar.gz
  • Upload date:
  • Size: 1.1 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for gnosis_air-0.0.14.tar.gz
Algorithm Hash digest
SHA256 0ee6c92e37f5939db87b52d2159db825ab17e763d0fd1831aea0ce63676fee83
MD5 6a8ec182f099c9fce0d837453a4fb60d
BLAKE2b-256 8318362bb9bcacc5d954f39f8168675cd288e6c907e8ea63c1acb821ae4c9fe7

See more details on using hashes here.

Provenance

The following attestation bundles were made for gnosis_air-0.0.14.tar.gz:

Publisher: publish-pypi.yml on JetBrains/air-trajectory-analysis

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file gnosis_air-0.0.14-py3-none-any.whl.

File metadata

  • Download URL: gnosis_air-0.0.14-py3-none-any.whl
  • Upload date:
  • Size: 1.0 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for gnosis_air-0.0.14-py3-none-any.whl
Algorithm Hash digest
SHA256 d30ea50ec3132bcfd4bdf84eca43df703a3462d7baa31312611072dc856c056f
MD5 b5ce93224f4a824e5fb46f5215a89198
BLAKE2b-256 df30102fa2a2e2b66c7fbed908807ed5561f5fd62fe877a25110b01bd659237d

See more details on using hashes here.

Provenance

The following attestation bundles were made for gnosis_air-0.0.14-py3-none-any.whl:

Publisher: publish-pypi.yml on JetBrains/air-trajectory-analysis

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.0.16

2 files

0.0.15

2 files

This release

0.0.14 This release

2 files

0.0.13

2 files

0.0.11

2 files

0.0.10

2 files

0.0.9

2 files

0.0.8

2 files

0.0.7

2 files

0.0.6

2 files

0.0.5

2 files

0.0.3

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page