pisama-claude-code 0.3.4__tar.gz → 0.3.6__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.
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/CHANGELOG.md +9 -1
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/PKG-INFO +5 -1
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/README.md +4 -0
- pisama_claude_code-0.3.6/assets/demo.gif +0 -0
- pisama_claude_code-0.3.6/demo/demo.cast +153 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/pyproject.toml +1 -1
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/src/pisama_claude_code/__init__.py +1 -1
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/src/pisama_claude_code/cli.py +16 -2
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/src/pisama_claude_code/hooks/capture_hook.py +91 -36
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/.github/workflows/ci.yml +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/.github/workflows/publish.yml +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/.gitignore +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/CODE_OF_CONDUCT.md +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/CONTRIBUTING.md +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/LICENSE +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/SECURITY.md +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/demo/README.md +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/demo/demo-simulated.sh +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/demo/record-demo.sh +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/src/pisama_claude_code/adapter.py +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/src/pisama_claude_code/guardian.py +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/src/pisama_claude_code/hooks/__init__.py +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/src/pisama_claude_code/hooks/guardian_hook.py +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/src/pisama_claude_code/install.py +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/src/pisama_claude_code/py.typed +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/src/pisama_claude_code/storage.py +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/src/pisama_claude_code/trace_converter.py +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/src/pisama_claude_code/trace_types.py +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/tests/__init__.py +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/tests/conftest.py +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/tests/test_adapter.py +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/tests/test_cli.py +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/tests/test_guardian.py +0 -0
- {pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/tests/test_storage.py +0 -0
|
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.3.5] - 2025-01-05
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Correctly extract user input from transcript (skip tool_result blocks)
|
|
14
|
+
- Collect reasoning/output from multiple assistant messages (Claude Code splits them)
|
|
15
|
+
- Look back 200 lines to find user prompts
|
|
16
|
+
|
|
10
17
|
## [0.3.4] - 2025-01-05
|
|
11
18
|
|
|
12
19
|
### Fixed
|
|
@@ -84,7 +91,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
84
91
|
- Support for Bash, Read, Write, Edit, Grep, Glob tools
|
|
85
92
|
- Local storage in `~/.claude/pisama/traces/`
|
|
86
93
|
|
|
87
|
-
[Unreleased]: https://github.com/tn-pisama/pisama-claude-code/compare/v0.3.
|
|
94
|
+
[Unreleased]: https://github.com/tn-pisama/pisama-claude-code/compare/v0.3.5...HEAD
|
|
95
|
+
[0.3.5]: https://github.com/tn-pisama/pisama-claude-code/compare/v0.3.4...v0.3.5
|
|
88
96
|
[0.3.4]: https://github.com/tn-pisama/pisama-claude-code/compare/v0.3.3...v0.3.4
|
|
89
97
|
[0.3.3]: https://github.com/tn-pisama/pisama-claude-code/compare/v0.3.2...v0.3.3
|
|
90
98
|
[0.3.2]: https://github.com/tn-pisama/pisama-claude-code/compare/v0.3.1...v0.3.2
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pisama-claude-code
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.6
|
|
4
4
|
Summary: Trace capture for Claude Code sessions - sync to PISAMA platform for analysis
|
|
5
5
|
Project-URL: Homepage, https://pisama.dev
|
|
6
6
|
Project-URL: Documentation, https://pisama.dev/docs/claude-code
|
|
@@ -57,6 +57,10 @@ Description-Content-Type: text/markdown
|
|
|
57
57
|
[](https://pypistats.org/packages/pisama-claude-code)
|
|
58
58
|
[](https://github.com/astral-sh/ruff)
|
|
59
59
|
|
|
60
|
+
## Demo
|
|
61
|
+
|
|
62
|
+

|
|
63
|
+
|
|
60
64
|
## Why PISAMA?
|
|
61
65
|
|
|
62
66
|
When working with Claude Code, have you ever wondered:
|
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
[](https://pypistats.org/packages/pisama-claude-code)
|
|
11
11
|
[](https://github.com/astral-sh/ruff)
|
|
12
12
|
|
|
13
|
+
## Demo
|
|
14
|
+
|
|
15
|
+

|
|
16
|
+
|
|
13
17
|
## Why PISAMA?
|
|
14
18
|
|
|
15
19
|
When working with Claude Code, have you ever wondered:
|
|
Binary file
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
{"version":3,"term":{"cols":80,"rows":24,"type":"xterm-256color"},"timestamp":1767597795,"command":"./demo/demo-simulated.sh","env":{"SHELL":"/bin/zsh"}}
|
|
2
|
+
[0.011, "o", "\u001b[3J\u001b[H\u001b[2J"]
|
|
3
|
+
[0.000, "o", "\u001b[1;36m"]
|
|
4
|
+
[0.003, "o", "╔═══════════════════════════════════════════════════╗\r\n║ pisama-claude-code ║\r\n║ Track token costs for Claude Code sessions ║\r\n╚═══════════════════════════════════════════════════╝\r\n"]
|
|
5
|
+
[0.000, "o", "\u001b[0m\r\n"]
|
|
6
|
+
[2.004, "o", "\u001b[1;33m# Install\u001b[0m\r\n"]
|
|
7
|
+
[0.000, "o", "\u001b[0;32m$\u001b[0m "]
|
|
8
|
+
[0.000, "o", "p"]
|
|
9
|
+
[0.048, "o", "i"]
|
|
10
|
+
[0.046, "o", "p"]
|
|
11
|
+
[0.046, "o", " "]
|
|
12
|
+
[0.043, "o", "i"]
|
|
13
|
+
[0.043, "o", "n"]
|
|
14
|
+
[0.048, "o", "s"]
|
|
15
|
+
[0.043, "o", "t"]
|
|
16
|
+
[0.045, "o", "a"]
|
|
17
|
+
[0.047, "o", "l"]
|
|
18
|
+
[0.047, "o", "l"]
|
|
19
|
+
[0.046, "o", " "]
|
|
20
|
+
[0.048, "o", "p"]
|
|
21
|
+
[0.046, "o", "i"]
|
|
22
|
+
[0.047, "o", "s"]
|
|
23
|
+
[0.047, "o", "a"]
|
|
24
|
+
[0.043, "o", "m"]
|
|
25
|
+
[0.044, "o", "a"]
|
|
26
|
+
[0.046, "o", "-"]
|
|
27
|
+
[0.049, "o", "c"]
|
|
28
|
+
[0.045, "o", "l"]
|
|
29
|
+
[0.047, "o", "a"]
|
|
30
|
+
[0.048, "o", "u"]
|
|
31
|
+
[0.043, "o", "d"]
|
|
32
|
+
[0.048, "o", "e"]
|
|
33
|
+
[0.047, "o", "-"]
|
|
34
|
+
[0.045, "o", "c"]
|
|
35
|
+
[0.047, "o", "o"]
|
|
36
|
+
[0.048, "o", "d"]
|
|
37
|
+
[0.044, "o", "e"]
|
|
38
|
+
[0.044, "o", "\r\n"]
|
|
39
|
+
[0.207, "o", "Successfully installed pisama-claude-code-0.3.2\r\n"]
|
|
40
|
+
[1.508, "o", "\r\n\u001b[1;33m# Set up hooks\u001b[0m\r\n\u001b[0;32m$\u001b[0m "]
|
|
41
|
+
[0.000, "o", "p"]
|
|
42
|
+
[0.047, "o", "i"]
|
|
43
|
+
[0.045, "o", "s"]
|
|
44
|
+
[0.046, "o", "a"]
|
|
45
|
+
[0.046, "o", "m"]
|
|
46
|
+
[0.045, "o", "a"]
|
|
47
|
+
[0.047, "o", "-"]
|
|
48
|
+
[0.043, "o", "c"]
|
|
49
|
+
[0.047, "o", "c"]
|
|
50
|
+
[0.047, "o", " "]
|
|
51
|
+
[0.048, "o", "i"]
|
|
52
|
+
[0.043, "o", "n"]
|
|
53
|
+
[0.046, "o", "s"]
|
|
54
|
+
[0.044, "o", "t"]
|
|
55
|
+
[0.046, "o", "a"]
|
|
56
|
+
[0.047, "o", "l"]
|
|
57
|
+
[0.045, "o", "l"]
|
|
58
|
+
[0.047, "o", "\r\n"]
|
|
59
|
+
[0.209, "o", "✅ Installed pisama-capture.py\r\n✅ Installed pisama-pre.sh\r\n✅ Installed pisama-post.sh\r\n✅ Updated ~/.claude/settings.local.json\r\n\r\nHooks installed! Traces will be captured automatically.\r\n"]
|
|
60
|
+
[2.005, "o", "\r\n\u001b[1;33m# After a coding session, check status\u001b[0m\r\n\u001b[0;32m$\u001b[0m "]
|
|
61
|
+
[0.000, "o", "p"]
|
|
62
|
+
[0.045, "o", "i"]
|
|
63
|
+
[0.047, "o", "s"]
|
|
64
|
+
[0.046, "o", "a"]
|
|
65
|
+
[0.048, "o", "m"]
|
|
66
|
+
[0.045, "o", "a"]
|
|
67
|
+
[0.045, "o", "-"]
|
|
68
|
+
[0.045, "o", "c"]
|
|
69
|
+
[0.048, "o", "c"]
|
|
70
|
+
[0.048, "o", " "]
|
|
71
|
+
[0.048, "o", "s"]
|
|
72
|
+
[0.043, "o", "t"]
|
|
73
|
+
[0.044, "o", "a"]
|
|
74
|
+
[0.049, "o", "t"]
|
|
75
|
+
[0.047, "o", "u"]
|
|
76
|
+
[0.045, "o", "s"]
|
|
77
|
+
[0.043, "o", "\r\n"]
|
|
78
|
+
[0.211, "o", "📊 PISAMA Status\r\n========================================\r\n\r\n🔧 Hook Installation:\r\n ✅ pisama-capture.py\r\n ✅ pisama-pre.sh\r\n ✅ pisama-post.sh\r\n All hooks installed\r\n\r\n🔗 Platform Connection:\r\n ❌ Not connected (local-only mode)\r\n\r\n📁 Local Traces: 1,847\r\n Input tokens: 142,580\r\n Output tokens: 891,234\r\n Total cost: $52.34\r\n Models: claude-opus-4-5-20251101\r\n\r\n📝 Claude Code Settings:\r\n ✅ PISAMA hooks configured in settings\r\n"]
|
|
79
|
+
[2.509, "o", "\r\n\u001b[1;33m# View recent tool calls\u001b[0m\r\n\u001b[0;32m$\u001b[0m "]
|
|
80
|
+
[0.000, "o", "p"]
|
|
81
|
+
[0.046, "o", "i"]
|
|
82
|
+
[0.043, "o", "s"]
|
|
83
|
+
[0.043, "o", "a"]
|
|
84
|
+
[0.043, "o", "m"]
|
|
85
|
+
[0.043, "o", "a"]
|
|
86
|
+
[0.044, "o", "-"]
|
|
87
|
+
[0.047, "o", "c"]
|
|
88
|
+
[0.046, "o", "c"]
|
|
89
|
+
[0.046, "o", " "]
|
|
90
|
+
[0.042, "o", "t"]
|
|
91
|
+
[0.043, "o", "r"]
|
|
92
|
+
[0.046, "o", "a"]
|
|
93
|
+
[0.047, "o", "c"]
|
|
94
|
+
[0.048, "o", "e"]
|
|
95
|
+
[0.045, "o", "s"]
|
|
96
|
+
[0.045, "o", " "]
|
|
97
|
+
[0.047, "o", "-"]
|
|
98
|
+
[0.047, "o", "v"]
|
|
99
|
+
[0.047, "o", " "]
|
|
100
|
+
[0.043, "o", "-"]
|
|
101
|
+
[0.045, "o", "-"]
|
|
102
|
+
[0.048, "o", "l"]
|
|
103
|
+
[0.047, "o", "a"]
|
|
104
|
+
[0.046, "o", "s"]
|
|
105
|
+
[0.046, "o", "t"]
|
|
106
|
+
[0.048, "o", " "]
|
|
107
|
+
[0.043, "o", "5"]
|
|
108
|
+
[0.045, "o", "\r\n"]
|
|
109
|
+
[0.207, "o", "📋 Recent Traces (5 shown)\r\n======================================================================\r\n2025-01-04T14:23:45 | Bash | a3f8c2d1 | post | 2341i 8923o | $0.7012\r\n2025-01-04T14:23:38 | Read | a3f8c2d1 | post | 1823i 2341o | $0.1892\r\n2025-01-04T14:23:31 | Edit | a3f8c2d1 | post | 3421i 12893o | $1.0234\r\n2025-01-04T14:23:22 | Grep | a3f8c2d1 | post | 892i 1234o | $0.1023\r\n2025-01-04T14:23:15 | Glob | a3f8c2d1 | post | 423i 891o | $0.0712\r\n──────────────────────────────────────────────────────────────────────\r\nTotals: 8,900 input + 26,282 output tokens = $2.0873\r\n"]
|
|
110
|
+
[2.504, "o", "\r\n\u001b[1;33m# Detailed breakdown\u001b[0m\r\n"]
|
|
111
|
+
[0.000, "o", "\u001b[0;32m$\u001b[0m "]
|
|
112
|
+
[0.000, "o", "p"]
|
|
113
|
+
[0.045, "o", "i"]
|
|
114
|
+
[0.043, "o", "s"]
|
|
115
|
+
[0.045, "o", "a"]
|
|
116
|
+
[0.044, "o", "m"]
|
|
117
|
+
[0.045, "o", "a"]
|
|
118
|
+
[0.048, "o", "-"]
|
|
119
|
+
[0.044, "o", "c"]
|
|
120
|
+
[0.047, "o", "c"]
|
|
121
|
+
[0.043, "o", " "]
|
|
122
|
+
[0.043, "o", "u"]
|
|
123
|
+
[0.043, "o", "s"]
|
|
124
|
+
[0.047, "o", "a"]
|
|
125
|
+
[0.048, "o", "g"]
|
|
126
|
+
[0.046, "o", "e"]
|
|
127
|
+
[0.042, "o", " "]
|
|
128
|
+
[0.044, "o", "-"]
|
|
129
|
+
[0.043, "o", "-"]
|
|
130
|
+
[0.046, "o", "b"]
|
|
131
|
+
[0.046, "o", "y"]
|
|
132
|
+
[0.045, "o", "-"]
|
|
133
|
+
[0.046, "o", "m"]
|
|
134
|
+
[0.046, "o", "o"]
|
|
135
|
+
[0.046, "o", "d"]
|
|
136
|
+
[0.047, "o", "e"]
|
|
137
|
+
[0.044, "o", "l"]
|
|
138
|
+
[0.048, "o", " "]
|
|
139
|
+
[0.044, "o", "-"]
|
|
140
|
+
[0.048, "o", "-"]
|
|
141
|
+
[0.045, "o", "b"]
|
|
142
|
+
[0.045, "o", "y"]
|
|
143
|
+
[0.044, "o", "-"]
|
|
144
|
+
[0.048, "o", "t"]
|
|
145
|
+
[0.042, "o", "o"]
|
|
146
|
+
[0.047, "o", "o"]
|
|
147
|
+
[0.046, "o", "l"]
|
|
148
|
+
[0.046, "o", "\r\n"]
|
|
149
|
+
[0.210, "o", "📊 Token Usage Summary (last 100 traces)\r\n==================================================\r\nInput tokens: 142,580\r\nOutput tokens: 891,234\r\nCache read tokens: 1,234,567\r\nTotal tokens: 1,033,814\r\nTotal cost: $ 52.34\r\n\r\n📈 By Model:\r\n--------------------------------------------------\r\n claude-opus-4-5-20251101 $52.34\r\n\r\n🔧 By Tool:\r\n--------------------------------------------------\r\n Bash 145 calls $23.45\r\n Read 89 calls $12.34\r\n Edit 67 calls $8.92\r\n Write 34 calls $4.21\r\n Grep 23 calls $2.12\r\n Glob 12 calls $1.30\r\n"]
|
|
150
|
+
[2.002, "o", "\r\n\u001b[1;36m"]
|
|
151
|
+
[0.003, "o", "════════════════════════════════════════════════════\r\n ✓ Track every token. Know your costs.\r\n\r\n pip install pisama-claude-code\r\n════════════════════════════════════════════════════\r\n"]
|
|
152
|
+
[0.000, "o", "\u001b[0m"]
|
|
153
|
+
[3.008, "x", "0"]
|
|
@@ -53,12 +53,15 @@ def save_config(config: dict):
|
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
@click.group()
|
|
56
|
-
@click.version_option(version="0.3.
|
|
56
|
+
@click.version_option(version="0.3.6")
|
|
57
57
|
def main():
|
|
58
58
|
"""PISAMA Claude Code - Trace capture and sync."""
|
|
59
59
|
pass
|
|
60
60
|
|
|
61
61
|
|
|
62
|
+
GITHUB_URL = "https://github.com/tn-pisama/pisama-claude-code"
|
|
63
|
+
|
|
64
|
+
|
|
62
65
|
@main.command()
|
|
63
66
|
@click.option("--force", "-f", is_flag=True, help="Overwrite existing hooks")
|
|
64
67
|
def install(force: bool):
|
|
@@ -66,6 +69,13 @@ def install(force: bool):
|
|
|
66
69
|
from pisama_claude_code.install import install as do_install
|
|
67
70
|
do_install(force=force)
|
|
68
71
|
|
|
72
|
+
# Star reminder after install
|
|
73
|
+
click.echo("")
|
|
74
|
+
click.echo("=" * 50)
|
|
75
|
+
click.echo("If pisama-claude-code is useful, please star us!")
|
|
76
|
+
click.echo(f" {GITHUB_URL}")
|
|
77
|
+
click.echo("=" * 50)
|
|
78
|
+
|
|
69
79
|
|
|
70
80
|
@main.command()
|
|
71
81
|
def uninstall():
|
|
@@ -439,6 +449,10 @@ def status():
|
|
|
439
449
|
else:
|
|
440
450
|
click.echo(" ❌ No settings file found")
|
|
441
451
|
|
|
452
|
+
# Star reminder
|
|
453
|
+
click.echo("")
|
|
454
|
+
click.echo(f"Star us on GitHub: {GITHUB_URL}")
|
|
455
|
+
|
|
442
456
|
|
|
443
457
|
@main.command()
|
|
444
458
|
@click.option("--last", default=100, help="Number of traces to analyze")
|
|
@@ -675,7 +689,7 @@ def prepare_sync_payload(traces: list, include_outputs: bool) -> dict:
|
|
|
675
689
|
|
|
676
690
|
return {
|
|
677
691
|
"source": "claude-code",
|
|
678
|
-
"version": "0.3.
|
|
692
|
+
"version": "0.3.5",
|
|
679
693
|
"uploaded_at": datetime.now(timezone.utc).isoformat(),
|
|
680
694
|
"trace_count": len(clean_traces),
|
|
681
695
|
"traces": clean_traces,
|
{pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/src/pisama_claude_code/hooks/capture_hook.py
RENAMED
|
@@ -185,7 +185,14 @@ def extract_content_parts(content_blocks: list) -> dict:
|
|
|
185
185
|
|
|
186
186
|
|
|
187
187
|
def get_last_user_message(transcript_path: str) -> dict:
|
|
188
|
-
"""Read transcript and get the last user message (input).
|
|
188
|
+
"""Read transcript and get the last user text message (input).
|
|
189
|
+
|
|
190
|
+
Note: Claude Code "user" messages can be either:
|
|
191
|
+
1. Actual user text input (content is a string or has type="text" blocks)
|
|
192
|
+
2. Tool results (content has type="tool_result" blocks)
|
|
193
|
+
|
|
194
|
+
We want the actual user input, not tool results.
|
|
195
|
+
"""
|
|
189
196
|
try:
|
|
190
197
|
from pathlib import Path
|
|
191
198
|
transcript = Path(transcript_path)
|
|
@@ -193,29 +200,47 @@ def get_last_user_message(transcript_path: str) -> dict:
|
|
|
193
200
|
return {}
|
|
194
201
|
|
|
195
202
|
with open(transcript) as f:
|
|
196
|
-
lines = f.readlines()[-
|
|
203
|
+
lines = f.readlines()[-200:] # Look back further for user message
|
|
197
204
|
|
|
198
205
|
for line in reversed(lines):
|
|
199
206
|
try:
|
|
200
207
|
entry = json.loads(line)
|
|
201
|
-
|
|
208
|
+
# Claude Code uses "user" type
|
|
209
|
+
if entry.get("type") in ("user", "human") and "message" in entry:
|
|
202
210
|
msg = entry["message"]
|
|
203
211
|
content = msg.get("content", [])
|
|
204
212
|
|
|
205
|
-
#
|
|
206
|
-
if isinstance(content,
|
|
213
|
+
# Content can be a direct string (actual user input)
|
|
214
|
+
if isinstance(content, str):
|
|
215
|
+
# Skip system messages and interrupts
|
|
216
|
+
if not content.startswith("[Request") and not content.startswith("[System"):
|
|
217
|
+
return {"content": content, "role": "user"}
|
|
218
|
+
|
|
219
|
+
# Content can be a list of blocks
|
|
220
|
+
elif isinstance(content, list):
|
|
207
221
|
text_parts = []
|
|
222
|
+
is_tool_result = False
|
|
223
|
+
|
|
208
224
|
for block in content:
|
|
209
|
-
if isinstance(block, dict)
|
|
210
|
-
|
|
225
|
+
if isinstance(block, dict):
|
|
226
|
+
block_type = block.get("type", "")
|
|
227
|
+
if block_type == "tool_result":
|
|
228
|
+
is_tool_result = True
|
|
229
|
+
break # This is a tool result, not user input
|
|
230
|
+
elif block_type == "text":
|
|
231
|
+
text = block.get("text", "")
|
|
232
|
+
if text and not text.startswith("[Request"):
|
|
233
|
+
text_parts.append(text)
|
|
211
234
|
elif isinstance(block, str):
|
|
212
235
|
text_parts.append(block)
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
236
|
+
|
|
237
|
+
# Only return if this is actual user text, not tool results
|
|
238
|
+
if text_parts and not is_tool_result:
|
|
239
|
+
return {
|
|
240
|
+
"content": "\n".join(text_parts),
|
|
241
|
+
"role": "user",
|
|
242
|
+
}
|
|
243
|
+
|
|
219
244
|
except json.JSONDecodeError:
|
|
220
245
|
continue
|
|
221
246
|
return {}
|
|
@@ -224,14 +249,17 @@ def get_last_user_message(transcript_path: str) -> dict:
|
|
|
224
249
|
|
|
225
250
|
|
|
226
251
|
def get_last_assistant_message(transcript_path: str) -> dict:
|
|
227
|
-
"""Read transcript and get
|
|
252
|
+
"""Read transcript and get recent assistant messages with usage.
|
|
253
|
+
|
|
254
|
+
Claude Code separates thinking, text, and tool_use into different messages,
|
|
255
|
+
so we need to collect content from multiple recent assistant messages.
|
|
228
256
|
|
|
229
|
-
|
|
257
|
+
Extracts:
|
|
230
258
|
- model: The Claude model used
|
|
231
259
|
- usage: Token counts (input, output, cache)
|
|
232
260
|
- input: User's input message
|
|
233
|
-
- reasoning: Extended thinking content
|
|
234
|
-
- output: Text response content
|
|
261
|
+
- reasoning: Extended thinking content (collected from recent messages)
|
|
262
|
+
- output: Text response content (collected from recent messages)
|
|
235
263
|
- tool_calls: Any tool calls made
|
|
236
264
|
"""
|
|
237
265
|
try:
|
|
@@ -240,45 +268,72 @@ def get_last_assistant_message(transcript_path: str) -> dict:
|
|
|
240
268
|
if not transcript.exists():
|
|
241
269
|
return {}
|
|
242
270
|
|
|
243
|
-
# Read last
|
|
271
|
+
# Read last 100 lines to find messages
|
|
244
272
|
with open(transcript) as f:
|
|
245
|
-
lines = f.readlines()[-
|
|
273
|
+
lines = f.readlines()[-100:]
|
|
274
|
+
|
|
275
|
+
# Collect content from recent assistant messages (up to 10)
|
|
276
|
+
# Claude Code sends thinking, text, tool_use as separate messages
|
|
277
|
+
all_reasoning = []
|
|
278
|
+
all_output = []
|
|
279
|
+
all_tool_calls = []
|
|
280
|
+
model = None
|
|
281
|
+
usage = {}
|
|
282
|
+
stop_reason = None
|
|
283
|
+
messages_checked = 0
|
|
284
|
+
max_messages = 10
|
|
246
285
|
|
|
247
|
-
assistant_msg = None
|
|
248
286
|
for line in reversed(lines):
|
|
287
|
+
if messages_checked >= max_messages:
|
|
288
|
+
break
|
|
249
289
|
try:
|
|
250
290
|
entry = json.loads(line)
|
|
251
291
|
if entry.get("type") == "assistant" and "message" in entry:
|
|
292
|
+
messages_checked += 1
|
|
252
293
|
msg = entry["message"]
|
|
253
|
-
|
|
294
|
+
|
|
295
|
+
# Get model and usage from first (most recent) message
|
|
296
|
+
if model is None:
|
|
297
|
+
model = msg.get("model")
|
|
298
|
+
usage = msg.get("usage", {})
|
|
299
|
+
stop_reason = msg.get("stop_reason")
|
|
300
|
+
|
|
301
|
+
# Extract content from this message
|
|
302
|
+
content = msg.get("content", [])
|
|
303
|
+
if isinstance(content, list):
|
|
304
|
+
parts = extract_content_parts(content)
|
|
305
|
+
if parts.get("reasoning", {}).get("content"):
|
|
306
|
+
all_reasoning.append(parts["reasoning"]["content"])
|
|
307
|
+
if parts.get("output", {}).get("content"):
|
|
308
|
+
all_output.append(parts["output"]["content"])
|
|
309
|
+
if parts.get("tool_calls"):
|
|
310
|
+
all_tool_calls.extend(parts["tool_calls"])
|
|
311
|
+
|
|
312
|
+
# Stop if we hit a user message (different turn)
|
|
313
|
+
elif entry.get("type") == "human":
|
|
254
314
|
break
|
|
315
|
+
|
|
255
316
|
except json.JSONDecodeError:
|
|
256
317
|
continue
|
|
257
318
|
|
|
258
|
-
if not assistant_msg:
|
|
259
|
-
return {}
|
|
260
|
-
|
|
261
|
-
# Extract content parts (reasoning, output, tool_calls)
|
|
262
|
-
content = assistant_msg.get("content", [])
|
|
263
|
-
content_parts = extract_content_parts(content) if isinstance(content, list) else {}
|
|
264
|
-
|
|
265
319
|
# Get user input
|
|
266
320
|
user_msg = get_last_user_message(transcript_path)
|
|
267
321
|
|
|
268
|
-
#
|
|
269
|
-
|
|
322
|
+
# Combine collected content (reverse to get chronological order)
|
|
323
|
+
reasoning_text = "\n\n".join(reversed(all_reasoning)) if all_reasoning else None
|
|
324
|
+
output_text = "\n\n".join(reversed(all_output)) if all_output else None
|
|
270
325
|
|
|
271
326
|
return {
|
|
272
|
-
"model":
|
|
327
|
+
"model": model,
|
|
273
328
|
"usage": usage,
|
|
274
|
-
"stop_reason":
|
|
329
|
+
"stop_reason": stop_reason,
|
|
275
330
|
# Structured content
|
|
276
331
|
"input": user_msg.get("content"),
|
|
277
|
-
"reasoning":
|
|
278
|
-
"output":
|
|
279
|
-
"tool_calls":
|
|
332
|
+
"reasoning": reasoning_text,
|
|
333
|
+
"output": output_text,
|
|
334
|
+
"tool_calls": all_tool_calls if all_tool_calls else None,
|
|
280
335
|
# Legacy field for backward compatibility
|
|
281
|
-
"content":
|
|
336
|
+
"content": [],
|
|
282
337
|
}
|
|
283
338
|
except Exception:
|
|
284
339
|
return {}
|
|
File without changes
|
|
File without changes
|
{pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/.github/ISSUE_TEMPLATE/feature_request.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/src/pisama_claude_code/hooks/__init__.py
RENAMED
|
File without changes
|
{pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/src/pisama_claude_code/hooks/guardian_hook.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pisama_claude_code-0.3.4 → pisama_claude_code-0.3.6}/src/pisama_claude_code/trace_converter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|