kingdom-cli 0.2__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/PKG-INFO +60 -23
  2. kingdom_cli-0.3.0/README.md +137 -0
  3. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/pyproject.toml +2 -1
  4. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom/agent.py +91 -2
  5. kingdom_cli-0.3.0/src/kingdom/cli/__init__.py +466 -0
  6. kingdom_cli-0.3.0/src/kingdom/cli/config.py +138 -0
  7. kingdom_cli-0.3.0/src/kingdom/cli/council.py +1220 -0
  8. kingdom_cli-0.3.0/src/kingdom/cli/design.py +94 -0
  9. kingdom_cli-0.3.0/src/kingdom/cli/display.py +42 -0
  10. kingdom_cli-0.3.0/src/kingdom/cli/helpers.py +148 -0
  11. kingdom_cli-0.3.0/src/kingdom/cli/peasant.py +1370 -0
  12. kingdom_cli-0.3.0/src/kingdom/cli/ticket.py +1229 -0
  13. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom/config.py +106 -39
  14. kingdom_cli-0.3.0/src/kingdom/council/__init__.py +48 -0
  15. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom/council/base.py +14 -7
  16. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom/council/council.py +19 -5
  17. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom/council/worker.py +2 -11
  18. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom/design.py +1 -1
  19. kingdom_cli-0.3.0/src/kingdom/harness.py +1144 -0
  20. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom/parsing.py +30 -1
  21. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom/skill/SKILL.md +0 -6
  22. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom/skill/references/peasants.md +1 -5
  23. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom/state.py +118 -96
  24. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom/thread.py +49 -26
  25. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom/ticket.py +270 -77
  26. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom/tui/app.py +424 -151
  27. kingdom_cli-0.3.0/src/kingdom/tui/chat.tcss +116 -0
  28. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom/tui/poll.py +23 -9
  29. kingdom_cli-0.3.0/src/kingdom/tui/terminal.py +43 -0
  30. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom/tui/widgets.py +63 -83
  31. kingdom_cli-0.3.0/src/kingdom/worker.py +54 -0
  32. kingdom_cli-0.3.0/src/kingdom/worktree.py +133 -0
  33. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom_cli.egg-info/PKG-INFO +60 -23
  34. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom_cli.egg-info/SOURCES.txt +22 -4
  35. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/tests/test_agent.py +67 -0
  36. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/tests/test_cli.py +195 -163
  37. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/tests/test_cli_council.py +235 -99
  38. kingdom_cli-0.3.0/tests/test_cli_design_breakdown.py +78 -0
  39. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/tests/test_cli_peasant.py +781 -89
  40. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/tests/test_cli_status.py +19 -23
  41. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/tests/test_cli_ticket_current.py +12 -12
  42. kingdom_cli-0.3.0/tests/test_cli_ticket_deps.py +382 -0
  43. kingdom_cli-0.3.0/tests/test_cli_ticket_lifecycle.py +904 -0
  44. kingdom_cli-0.3.0/tests/test_cli_ticket_links.py +83 -0
  45. kingdom_cli-0.3.0/tests/test_cli_ticket_list.py +953 -0
  46. kingdom_cli-0.3.0/tests/test_cli_ticket_show.py +340 -0
  47. kingdom_cli-0.3.0/tests/test_cli_ticket_worklog.py +102 -0
  48. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/tests/test_config.py +91 -32
  49. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/tests/test_council.py +66 -7
  50. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/tests/test_done.py +71 -37
  51. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/tests/test_harness.py +746 -177
  52. kingdom_cli-0.3.0/tests/test_init.py +273 -0
  53. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/tests/test_parsing.py +66 -1
  54. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/tests/test_session.py +0 -7
  55. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/tests/test_state.py +212 -6
  56. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/tests/test_thread.py +173 -12
  57. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/tests/test_ticket.py +380 -15
  58. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/tests/test_tui.py +775 -82
  59. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/tests/test_tui_clipboard.py +66 -1
  60. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/tests/test_tui_integration.py +239 -30
  61. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/tests/test_tui_poll.py +85 -14
  62. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/tests/test_tui_reply.py +58 -26
  63. kingdom_cli-0.3.0/tests/test_tui_terminal.py +82 -0
  64. kingdom_cli-0.3.0/tests/test_tui_thinking_persistence.py +191 -0
  65. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/tests/test_tui_widgets.py +8 -0
  66. kingdom_cli-0.3.0/tests/test_worker.py +106 -0
  67. kingdom_cli-0.3.0/tests/test_worktree.py +25 -0
  68. kingdom_cli-0.2/README.md +0 -100
  69. kingdom_cli-0.2/src/kingdom/cli.py +0 -4176
  70. kingdom_cli-0.2/src/kingdom/council/__init__.py +0 -13
  71. kingdom_cli-0.2/src/kingdom/harness.py +0 -727
  72. kingdom_cli-0.2/tests/test_cli_design_breakdown.py +0 -83
  73. kingdom_cli-0.2/tests/test_cli_ticket.py +0 -2440
  74. kingdom_cli-0.2/tests/test_cli_whoami.py +0 -46
  75. kingdom_cli-0.2/tests/test_init.py +0 -360
  76. kingdom_cli-0.2/tests/test_tui_thinking_persistence.py +0 -159
  77. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/LICENSE +0 -0
  78. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/setup.cfg +0 -0
  79. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom/__init__.py +0 -0
  80. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom/__main__.py +0 -0
  81. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom/breakdown.py +0 -0
  82. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom/council/bundle.py +0 -0
  83. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom/session.py +0 -0
  84. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom/skill/__init__.py +0 -0
  85. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom/skill/references/__init__.py +0 -0
  86. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom/skill/references/council.md +0 -0
  87. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom/skill/references/tickets.md +0 -0
  88. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom/synthesis.py +0 -0
  89. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom/tui/__init__.py +0 -0
  90. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom/tui/clipboard.py +0 -0
  91. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom_cli.egg-info/dependency_links.txt +0 -0
  92. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom_cli.egg-info/entry_points.txt +0 -0
  93. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom_cli.egg-info/requires.txt +0 -0
  94. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/src/kingdom_cli.egg-info/top_level.txt +0 -0
  95. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/tests/test_breakdown.py +0 -0
  96. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/tests/test_design.py +0 -0
  97. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/tests/test_skill.py +0 -0
  98. {kingdom_cli-0.2 → kingdom_cli-0.3.0}/tests/test_synthesis.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kingdom-cli
3
- Version: 0.2
3
+ Version: 0.3.0
4
4
  Summary: A markdown file-based CLI for software development with multi-agent council design, markdown tickets, and background worker loops.
5
5
  Author: James Robinson-Bohnslav
6
6
  License-Expression: Apache-2.0
@@ -48,49 +48,72 @@ Gastown minus the polecats.
48
48
  uv tool install kingdom-cli
49
49
  ```
50
50
 
51
- ## Getting Started
51
+ ## Workflow Scales
52
+
53
+ `kd` scales down gracefully. You pick the parts of the workflow that fit the size of the work.
54
+
55
+ ### Full workflow (new features)
52
56
 
53
- 1. Initialize the project on your current branch:
57
+ Design with the council, break into tickets, dispatch peasants, review, and merge.
54
58
 
55
59
  ```bash
56
- kd start
60
+ kd start # initialize branch session
61
+ kd council chat --new # discuss design with the council TUI
62
+ kd design approve # lock in the design
63
+ # create tickets from the design (via the kingdom skill or manually)
64
+ kd peasant start <id> # dispatch parallel workers
65
+ kd peasant review <id> # review completed work
66
+ kd peasant accept <id> # accept and close, or reject with feedback
67
+ kd done # archive and clean up
57
68
  ```
58
69
 
59
- 2. Configure council agent CLIs in `.kd/config.json` (check effective config with `kd config show`).
70
+ ### Medium workflow (refactors, smaller features)
60
71
 
61
- 3. Design with the council in the TUI:
72
+ Pull tickets into a branch, work them directly or with peasants. No design phase needed.
62
73
 
63
74
  ```bash
64
- kd chat --new
75
+ kd start
76
+ kd tk pull <id> <id> # pull backlog tickets onto this branch
77
+ kd tk start <id> # work tickets one at a time
78
+ kd tk close <id>
79
+ kd done
65
80
  ```
66
81
 
67
- 4. Break your design into markdown tickets:
82
+ ### Lightweight workflow (bug fixes)
83
+
84
+ Work a single ticket, close it, make a PR. Or batch several bug-fix tickets on one branch.
68
85
 
69
86
  ```bash
70
- kd breakdown
87
+ kd start
88
+ kd tk create "fix: login redirect loop"
89
+ kd tk start <id>
90
+ # ... fix the bug ...
91
+ kd tk close <id>
92
+ kd done
71
93
  ```
72
94
 
73
- 5. Review and refine tickets:
95
+ Design docs, council sessions, and peasant workers are all optional. A branch with one ticket and no design doc is a perfectly valid `kd` workflow.
96
+
97
+ ## Getting Started
74
98
 
75
99
  ```bash
76
- kd ticket list
77
- kd ticket show <id>
100
+ kd start # start a session on the current branch
78
101
  ```
79
102
 
80
- 6. Execute tickets:
81
- - Serial: tell Claude Code or Codex to work tickets directly, or run `kd work <id>`.
82
- - Parallel: dispatch peasants in worktrees.
103
+ Configure council agent CLIs in `.kd/config.json` (check effective config with `kd config show`).
83
104
 
84
- ```bash
85
- kd peasant start <id>
86
- kd peasant status
87
- ```
105
+ ## Chat Modes
88
106
 
89
- 7. Close out the session:
107
+ The council chat TUI (`kd council chat`) supports four modes, configured via `council.chat.mode`:
90
108
 
91
- ```bash
92
- kd done
93
- ```
109
+ | Mode | First turn | Auto-turns | Default |
110
+ |------|-----------|------------|---------|
111
+ | `natural` | parallel broadcast | shuffled round-robin | yes |
112
+ | `round_robin` | sequential fixed-order | sequential fixed-order | |
113
+ | `manual` | only @mentioned | only @mentioned | |
114
+ | `broadcast` | parallel to all | parallel to all | |
115
+
116
+ LLM-to-LLM @mentions in responses automatically bump the mentioned member to the front of the auto-turn queue.
94
117
 
95
118
  ## How It Works
96
119
 
@@ -114,6 +137,20 @@ All state lives in `.kd/` as plain Markdown and JSON files, tracked in git along
114
137
 
115
138
  No database. No server. Just files on disk.
116
139
 
140
+ ## Commands
141
+
142
+ | Group | Description |
143
+ |-------|-------------|
144
+ | `kd start` / `kd done` / `kd status` | Branch lifecycle — initialize, finish, and inspect sessions |
145
+ | `kd design` | Manage design documents (`show`, `approve`) |
146
+ | `kd council` | Multi-model council — `ask`, `chat`, `review`, `show`, `list`, `watch`, `reset`, `retry` |
147
+ | `kd ticket` (alias `kd tk`) | Ticket management — `create`, `list`, `show`, `start`, `close`, `deps`, and more |
148
+ | `kd peasant` | Worker agents — `start`, `stop`, `review`, `accept`, `reject`, `msg`, `read`, `watch` |
149
+ | `kd config` | View and manage configuration |
150
+ | `kd doctor` | Check config and agent CLIs |
151
+
152
+ Run `kd <command> --help` for full flags and options.
153
+
117
154
  ## Development
118
155
 
119
156
  ```bash
@@ -0,0 +1,137 @@
1
+ # Kingdom
2
+
3
+ Kingdom (`kd`) is a markdown file-based CLI for software development: design with a multi-agent council, track work as markdown tickets, and run background RALPH loops with worker peasants.
4
+
5
+ The kingdom metaphor is intentional: you are the King, debate your design documents with a council of frontier coding agent CLIs you already use (Claude Code and Codex), break the design into modular markdown tickets, and then peasants execute those tickets in parallel worktrees.
6
+
7
+ Gastown minus the polecats.
8
+
9
+ ## Why
10
+
11
+ - **Multi-agent council** — get perspectives from multiple frontier coding models, not just one opinion. Different models catch different things.
12
+ - **Ticket-based execution** — breaking work into scoped tickets fights context rot, lets you use cheaper models for already-designed work, or run tickets in parallel.
13
+ - **Multi-agent reviews** — reviews across models consistently catch bugs that single-agent reviews miss.
14
+ - **Plain markdown files** — tickets, designs, and council threads are all markdown. Your coding agents are already good at finding, reading, and updating markdown.
15
+ - **CLI + TUI** — the TUI is for humans; agent CLIs use `kd` directly to ask the council or other agents for opinions.
16
+ - **Worklog audit trail** — peasant worklogs capture decisions, bugs encountered, and test results in the ticket markdown, committed to git. You can always see *why* something was done, not just the diff.
17
+
18
+ ## Install
19
+
20
+ ```bash
21
+ uv tool install kingdom-cli
22
+ ```
23
+
24
+ ## Workflow Scales
25
+
26
+ `kd` scales down gracefully. You pick the parts of the workflow that fit the size of the work.
27
+
28
+ ### Full workflow (new features)
29
+
30
+ Design with the council, break into tickets, dispatch peasants, review, and merge.
31
+
32
+ ```bash
33
+ kd start # initialize branch session
34
+ kd council chat --new # discuss design with the council TUI
35
+ kd design approve # lock in the design
36
+ # create tickets from the design (via the kingdom skill or manually)
37
+ kd peasant start <id> # dispatch parallel workers
38
+ kd peasant review <id> # review completed work
39
+ kd peasant accept <id> # accept and close, or reject with feedback
40
+ kd done # archive and clean up
41
+ ```
42
+
43
+ ### Medium workflow (refactors, smaller features)
44
+
45
+ Pull tickets into a branch, work them directly or with peasants. No design phase needed.
46
+
47
+ ```bash
48
+ kd start
49
+ kd tk pull <id> <id> # pull backlog tickets onto this branch
50
+ kd tk start <id> # work tickets one at a time
51
+ kd tk close <id>
52
+ kd done
53
+ ```
54
+
55
+ ### Lightweight workflow (bug fixes)
56
+
57
+ Work a single ticket, close it, make a PR. Or batch several bug-fix tickets on one branch.
58
+
59
+ ```bash
60
+ kd start
61
+ kd tk create "fix: login redirect loop"
62
+ kd tk start <id>
63
+ # ... fix the bug ...
64
+ kd tk close <id>
65
+ kd done
66
+ ```
67
+
68
+ Design docs, council sessions, and peasant workers are all optional. A branch with one ticket and no design doc is a perfectly valid `kd` workflow.
69
+
70
+ ## Getting Started
71
+
72
+ ```bash
73
+ kd start # start a session on the current branch
74
+ ```
75
+
76
+ Configure council agent CLIs in `.kd/config.json` (check effective config with `kd config show`).
77
+
78
+ ## Chat Modes
79
+
80
+ The council chat TUI (`kd council chat`) supports four modes, configured via `council.chat.mode`:
81
+
82
+ | Mode | First turn | Auto-turns | Default |
83
+ |------|-----------|------------|---------|
84
+ | `natural` | parallel broadcast | shuffled round-robin | yes |
85
+ | `round_robin` | sequential fixed-order | sequential fixed-order | |
86
+ | `manual` | only @mentioned | only @mentioned | |
87
+ | `broadcast` | parallel to all | parallel to all | |
88
+
89
+ LLM-to-LLM @mentions in responses automatically bump the mentioned member to the front of the auto-turn queue.
90
+
91
+ ## How It Works
92
+
93
+ All state lives in `.kd/` as plain Markdown and JSON files, tracked in git alongside your code:
94
+
95
+ ```
96
+ .kd/
97
+ ├── branches/ # Active branch work
98
+ │ └── feature-oauth-refresh/
99
+ │ ├── design.md # Design document
100
+ │ ├── breakdown.md # Ticket breakdown
101
+ │ ├── tickets/ # Branch-specific tickets
102
+ │ │ ├── a1b2.md
103
+ │ │ └── c3d4.md
104
+ │ └── threads/ # Council discussion threads
105
+ ├── backlog/ # Unassigned tickets
106
+ │ └── tickets/
107
+ ├── archive/ # Completed branches
108
+ └── worktrees/ # Git worktrees (gitignored)
109
+ ```
110
+
111
+ No database. No server. Just files on disk.
112
+
113
+ ## Commands
114
+
115
+ | Group | Description |
116
+ |-------|-------------|
117
+ | `kd start` / `kd done` / `kd status` | Branch lifecycle — initialize, finish, and inspect sessions |
118
+ | `kd design` | Manage design documents (`show`, `approve`) |
119
+ | `kd council` | Multi-model council — `ask`, `chat`, `review`, `show`, `list`, `watch`, `reset`, `retry` |
120
+ | `kd ticket` (alias `kd tk`) | Ticket management — `create`, `list`, `show`, `start`, `close`, `deps`, and more |
121
+ | `kd peasant` | Worker agents — `start`, `stop`, `review`, `accept`, `reject`, `msg`, `read`, `watch` |
122
+ | `kd config` | View and manage configuration |
123
+ | `kd doctor` | Check config and agent CLIs |
124
+
125
+ Run `kd <command> --help` for full flags and options.
126
+
127
+ ## Development
128
+
129
+ ```bash
130
+ uv sync
131
+ source .venv/bin/activate
132
+ pytest tests/
133
+ ```
134
+
135
+ ## License
136
+
137
+ Apache-2.0 — see [LICENSE](LICENSE) for details.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "kingdom-cli"
3
- version = "0.2"
3
+ version = "0.3.0"
4
4
  description = "A markdown file-based CLI for software development with multi-agent council design, markdown tickets, and background worker loops."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -42,6 +42,7 @@ where = ["src"]
42
42
 
43
43
  [tool.setuptools.package-data]
44
44
  "kingdom.skill" = ["*.md", "references/*.md"]
45
+ "kingdom.tui" = ["*.tcss"]
45
46
 
46
47
  [dependency-groups]
47
48
  dev = [
@@ -435,13 +435,19 @@ COMMAND_BUILDERS: dict[str, CommandBuilder] = {
435
435
  }
436
436
 
437
437
 
438
- def clean_agent_env(role: str | None = None, agent_name: str | None = None) -> dict[str, str]:
438
+ def clean_agent_env(
439
+ role: str | None = None,
440
+ agent_name: str | None = None,
441
+ kd_base: str | None = None,
442
+ ) -> dict[str, str]:
439
443
  """Return an env dict safe for spawning agent CLI subprocesses.
440
444
 
441
445
  Strips ``CLAUDECODE`` so child ``claude`` processes don't refuse to start
442
446
  with a "nested session" error when ``kd`` is invoked from inside Claude Code.
443
447
 
444
- Optionally injects ``KD_ROLE`` and ``KD_AGENT_NAME`` for ``kd whoami``.
448
+ Optionally injects ``KD_ROLE``, ``KD_AGENT_NAME``, and ``KD_BASE``.
449
+ ``KD_BASE`` ensures worktree agents resolve the project root against the
450
+ main repo, not the worktree's copy of ``.kd/``.
445
451
  """
446
452
  import os
447
453
 
@@ -450,6 +456,8 @@ def clean_agent_env(role: str | None = None, agent_name: str | None = None) -> d
450
456
  env["KD_ROLE"] = role
451
457
  if agent_name:
452
458
  env["KD_AGENT_NAME"] = agent_name
459
+ if kd_base:
460
+ env["KD_BASE"] = kd_base
453
461
  return env
454
462
 
455
463
 
@@ -646,3 +654,84 @@ def extract_stream_thinking(line: str, backend: str) -> str | None:
646
654
  if extractor is None:
647
655
  return None
648
656
  return extractor(line)
657
+
658
+
659
+ # ---------------------------------------------------------------------------
660
+ # Stream tool-use extractors — extract tool invocation names from NDJSON
661
+ # ---------------------------------------------------------------------------
662
+
663
+
664
+ def extract_claude_stream_tool_use(line: str) -> str | None:
665
+ """Extract tool name from a Claude Code stream-json NDJSON line.
666
+
667
+ Claude Code emits ``content_block_start`` with a ``tool_use`` block
668
+ containing the tool name (e.g. "Read", "Bash", "Write").
669
+ """
670
+ try:
671
+ event = json.loads(line)
672
+ except json.JSONDecodeError:
673
+ return None
674
+ if not isinstance(event, dict):
675
+ return None
676
+ if event.get("type") == "stream_event":
677
+ event = event.get("event", {})
678
+ if event.get("type") == "content_block_start":
679
+ block = event.get("content_block", {})
680
+ if block.get("type") == "tool_use":
681
+ return block.get("name")
682
+ return None
683
+
684
+
685
+ def extract_cursor_stream_tool_use(line: str) -> str | None:
686
+ """Extract tool name from a Cursor stream-json NDJSON line.
687
+
688
+ Cursor emits ``{"type": "tool_call", "subtype": "started", ...}``
689
+ when a tool invocation begins. The ``name`` field may or may not
690
+ be present depending on the Cursor version.
691
+ """
692
+ try:
693
+ event = json.loads(line)
694
+ except json.JSONDecodeError:
695
+ return None
696
+ if not isinstance(event, dict):
697
+ return None
698
+ if event.get("type") == "tool_call" and event.get("subtype") == "started":
699
+ return event.get("name") or "tool"
700
+ return None
701
+
702
+
703
+ def extract_codex_stream_tool_use(line: str) -> str | None:
704
+ """Extract tool name from a Codex NDJSON line.
705
+
706
+ Codex emits ``item.completed`` events for tool calls with
707
+ ``item.type == "function_call"``.
708
+ """
709
+ try:
710
+ event = json.loads(line)
711
+ except json.JSONDecodeError:
712
+ return None
713
+ if not isinstance(event, dict):
714
+ return None
715
+ if event.get("type") == "item.completed":
716
+ item = event.get("item", {})
717
+ if item.get("type") == "function_call":
718
+ return item.get("name", "tool")
719
+ return None
720
+
721
+
722
+ STREAM_TOOL_USE_EXTRACTORS: dict[str, StreamExtractor] = {
723
+ "claude_code": extract_claude_stream_tool_use,
724
+ "codex": extract_codex_stream_tool_use,
725
+ "cursor": extract_cursor_stream_tool_use,
726
+ }
727
+
728
+
729
+ def extract_stream_tool_use(line: str, backend: str) -> str | None:
730
+ """Extract tool name from a single NDJSON line for the given backend.
731
+
732
+ Returns the tool name string, or None for non-tool-use events.
733
+ """
734
+ extractor = STREAM_TOOL_USE_EXTRACTORS.get(backend)
735
+ if extractor is None:
736
+ return None
737
+ return extractor(line)