chasm-cli 1.5.4

Universal chat session manager - harvest, merge, and analyze AI chat history from VS Code, Cursor, and other editors
Documentation
"""Check response part kinds and content for all non-empty sessions."""
import json
from pathlib import Path

WS = Path(r"C:\Users\adamm\AppData\Roaming\Code\User\workspaceStorage")

ALL = {
    "chasm (WORKING)": ("82cdabb21413f2ff42168423e82c8bdf", [
        "44f0cf62-331e-43c9-b32c-25d91ebab0b8",  # 1 req, loads fine
        "837247f1-89d8-4b41-8646-600154cdb4a4",   # 117 req
    ]),
    "Agentic": ("5ec71800c69c79b96b06a37e38537907", [
        "6be29cba-331e-4aa4-bc58-659cc20f4800",
    ]),
    "AgenticFortress": ("724ab159cbc91cdd8242d9b5aa690c3b", [
        "4e5dd6b4-ea53-475b-8f9e-8fad3bf59388",
    ]),
    "AgentQ": ("0e71f7221cc2ffe28e938bc38efeb8c5", [
        "b5f4d4d3-ec2f-40f4-b941-ef6573dec711",
    ]),
    "AIModelVault": ("56cb9246fe0ba2d5debb96e9135e5c95", [
        "5229f61a-4960-4c27-bec0-a6efc12dece3",
    ]),
    "Cyborg": ("cc60bfebb242bac1578d7b49a44033db", [
        "527646cd-d49e-4b0c-b51b-83abd51f9080",
    ]),
    "Hyperlight": ("1c25bd214fe52001bcec2ffa40836c82", [
        "88671d20-ea1a-4324-bbf8-be2ada7229b0",
    ]),
    "Framewerx": ("c8f466664b769ee6a567242b576fb955", [
        "53a6df5c-02ef-462f-8046-e06b6738d0d3",
    ]),
    "OverwatchGCS": ("c7aca0a33bd40b6a718d04f88c333669", [
        "d5005e19-1bf3-44c7-8ef8-163f125b57f6",
    ]),
    "Rodeo": ("9190fefe3d0b856449a6bdacdca0c1ef", [
        "a076b63c-f863-433c-a943-af699ce256d9",
    ]),
    "SentryWall": ("6c88aa026ece73be4109fe5008915801", [
        "200a387d-a1f4-4e70-beeb-afa5fe268bbf",
    ]),
    "XWERX_B001": ("05db3446971342e7f6a633a732955575", [
        "6ecda819-b49a-45bd-9b46-dc140c2b30e2",
    ]),
}

def describe_part(part, depth=0):
    """Describe a response part."""
    if not isinstance(part, dict):
        return f"non-dict: {type(part).__name__}"
    
    kind = part.get("kind", "NO_KIND")
    
    # Different kinds have different structures
    if kind == "markdownContent":
        content = part.get("content", {})
        if isinstance(content, dict):
            value = content.get("value", "")
            return f"markdownContent({len(value)}chars)"
        return f"markdownContent(raw)"
    elif kind == "markdownVuln":
        content = part.get("content", {})
        return f"markdownVuln"
    elif kind == "inlineReference":
        return f"inlineReference"
    elif kind == "treeData":
        return f"treeData"
    elif kind == "toolInvocation":
        name = part.get("toolName", part.get("name", "?"))
        return f"toolInvocation({name})"
    elif kind == "toolInvocationSerialized":
        name = part.get("toolName", part.get("name", "?"))
        return f"toolInvocationSerialized({name})"
    elif kind == "confirmation":
        return f"confirmation"
    elif kind == "progressMessage":
        return f"progressMessage"
    elif kind == "mcpServersStarting":
        return f"mcpServersStarting"
    elif kind == "codeblockUri":
        return f"codeblockUri"
    elif kind == "codeCitation":
        return f"codeCitation"
    else:
        return f"{kind}"

print("=" * 70)
print("RESPONSE PARTS ANALYSIS FOR ALL SESSIONS")
print("=" * 70)

for name, (h, sids) in ALL.items():
    for sid in sids:
        jsonl = WS / h / "chatSessions" / f"{sid}.jsonl"
        if not jsonl.exists():
            print(f"\n{name} {sid[:8]}: FILE NOT FOUND")
            continue
        
        with open(jsonl, "r", encoding="utf-8") as f:
            first_line = f.readline().strip()
        
        try:
            snap = json.loads(first_line)
        except Exception as e:
            print(f"\n{name} {sid[:8]}: PARSE ERROR: {e}")
            continue
        
        v = snap.get("v", snap)
        reqs = v.get("requests", [])
        
        print(f"\n{name} {sid[:8]}: {len(reqs)} requests")
        
        for i, req in enumerate(reqs[:5]):  # Show first 5 requests
            response_parts = req.get("response", [])
            ms_val = req.get("modelState", {}).get("value", "?")
            msg_text = req.get("message", {}).get("text", "")[:60]
            
            part_descs = [describe_part(p) for p in response_parts] if isinstance(response_parts, list) else [f"not-list: {type(response_parts)}"]
            
            has_markdown = any("markdownContent" in d for d in part_descs)
            has_tool = any("toolInvocation" in d for d in part_descs)
            
            print(f"  req[{i}] ms={ms_val} msg=\"{msg_text}\"")
            print(f"    {len(response_parts)} parts: {part_descs}")
            if not has_markdown:
                print(f"    *** NO MARKDOWN CONTENT IN RESPONSE ***")
        
        if len(reqs) > 5:
            # Summarize rest
            all_has_markdown = True
            for req in reqs[5:]:
                resp = req.get("response", [])
                if isinstance(resp, list):
                    kinds = [p.get("kind") for p in resp if isinstance(p, dict)]
                    if "markdownContent" not in kinds:
                        all_has_markdown = False
            if not all_has_markdown:
                print(f"  ... {len(reqs)-5} more requests, SOME MISSING MARKDOWN")
            else:
                print(f"  ... {len(reqs)-5} more requests, all have markdown")

print("\n=== DONE ===")