patch-cc 0.1.1__tar.gz → 0.1.2__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.
- {patch_cc-0.1.1 → patch_cc-0.1.2}/PKG-INFO +1 -1
- {patch_cc-0.1.1 → patch_cc-0.1.2}/pyproject.toml +1 -1
- {patch_cc-0.1.1 → patch_cc-0.1.2}/src/patch_cc/__init__.py +1 -1
- patch_cc-0.1.2/src/patch_cc/patches/thinking.py +142 -0
- {patch_cc-0.1.1 → patch_cc-0.1.2}/uv.lock +1 -1
- patch_cc-0.1.1/src/patch_cc/patches/thinking.py +0 -88
- {patch_cc-0.1.1 → patch_cc-0.1.2}/.github/workflows/ci.yml +0 -0
- {patch_cc-0.1.1 → patch_cc-0.1.2}/.github/workflows/release.yml +0 -0
- {patch_cc-0.1.1 → patch_cc-0.1.2}/.gitignore +0 -0
- {patch_cc-0.1.1 → patch_cc-0.1.2}/LICENSE +0 -0
- {patch_cc-0.1.1 → patch_cc-0.1.2}/README.md +0 -0
- {patch_cc-0.1.1 → patch_cc-0.1.2}/docs/CONDUCT.md +0 -0
- {patch_cc-0.1.1 → patch_cc-0.1.2}/docs/INTERNALS.md +0 -0
- {patch_cc-0.1.1 → patch_cc-0.1.2}/docs/PLAYBOOK.md +0 -0
- {patch_cc-0.1.1 → patch_cc-0.1.2}/src/patch_cc/bun/__init__.py +0 -0
- {patch_cc-0.1.1 → patch_cc-0.1.2}/src/patch_cc/bun/blob.py +0 -0
- {patch_cc-0.1.1 → patch_cc-0.1.2}/src/patch_cc/bun/container.py +0 -0
- {patch_cc-0.1.1 → patch_cc-0.1.2}/src/patch_cc/bun/elf.py +0 -0
- {patch_cc-0.1.1 → patch_cc-0.1.2}/src/patch_cc/bun/errors.py +0 -0
- {patch_cc-0.1.1 → patch_cc-0.1.2}/src/patch_cc/bun/macho.py +0 -0
- {patch_cc-0.1.1 → patch_cc-0.1.2}/src/patch_cc/cache.py +0 -0
- {patch_cc-0.1.1 → patch_cc-0.1.2}/src/patch_cc/cli.py +0 -0
- {patch_cc-0.1.1 → patch_cc-0.1.2}/src/patch_cc/doctor.py +0 -0
- {patch_cc-0.1.1 → patch_cc-0.1.2}/src/patch_cc/locate.py +0 -0
- {patch_cc-0.1.1 → patch_cc-0.1.2}/src/patch_cc/menu.py +0 -0
- {patch_cc-0.1.1 → patch_cc-0.1.2}/src/patch_cc/patcher.py +0 -0
- {patch_cc-0.1.1 → patch_cc-0.1.2}/src/patch_cc/patches/__init__.py +0 -0
- {patch_cc-0.1.1 → patch_cc-0.1.2}/src/patch_cc/patches/agents.py +0 -0
- {patch_cc-0.1.1 → patch_cc-0.1.2}/src/patch_cc/patches/base.py +0 -0
- {patch_cc-0.1.1 → patch_cc-0.1.2}/src/patch_cc/patches/chrome.py +0 -0
- {patch_cc-0.1.1 → patch_cc-0.1.2}/src/patch_cc/patches/output.py +0 -0
- {patch_cc-0.1.1 → patch_cc-0.1.2}/src/patch_cc/patches/streaming.py +0 -0
- {patch_cc-0.1.1 → patch_cc-0.1.2}/src/patch_cc/ui.py +0 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""Make thinking blocks visible in the normal UI instead of transcript-only.
|
|
2
|
+
|
|
3
|
+
Three gates hide a finished thinking block in the normal view, and all three
|
|
4
|
+
must go or the patch silently does nothing (which is exactly how it shipped
|
|
5
|
+
broken in 0.1.0):
|
|
6
|
+
|
|
7
|
+
1. **Grouping.** The activity-group builder swallows every thinking-first
|
|
8
|
+
assistant message into the collapsed "✻ ... (thought for Ns)" group, so the
|
|
9
|
+
message never reaches the renderer at all. This was the missed gate: the
|
|
10
|
+
two rewrites below applied cleanly to a switch arm that thinking text never
|
|
11
|
+
flows through.
|
|
12
|
+
2. **The null guard.** The ``case"thinking":`` arm returns null outside
|
|
13
|
+
transcript/verbose mode.
|
|
14
|
+
3. **Presentation.** The renderer collapses the text to one italic line unless
|
|
15
|
+
``isTranscriptMode`` is set.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import re
|
|
21
|
+
|
|
22
|
+
from .base import (
|
|
23
|
+
GROUP_THINKING,
|
|
24
|
+
IDENT,
|
|
25
|
+
Options,
|
|
26
|
+
Outcome,
|
|
27
|
+
Patch,
|
|
28
|
+
compile_js,
|
|
29
|
+
splice,
|
|
30
|
+
switch_case_end,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# The group-builder branch pair that decides where a finished thinking message
|
|
34
|
+
# goes. Branch one (queued prompts) is matched only to *discover* the flush
|
|
35
|
+
# function, visible list, and loop variable; it is reconstructed verbatim.
|
|
36
|
+
# Branch two is the swallow: stash the text as the group's summary line and
|
|
37
|
+
# hide the message inside the group. Rewritten to flush the open group and
|
|
38
|
+
# push the message into the visible flow instead, keeping the think-time
|
|
39
|
+
# accounting.
|
|
40
|
+
_GROUP_ROUTING = compile_js(
|
|
41
|
+
rf"else if\(({IDENT})\(({IDENT})\)\)({IDENT})\(\),({IDENT})\.push\(\2\);"
|
|
42
|
+
rf"else if\(({IDENT})!==void 0\)\{{"
|
|
43
|
+
rf'if\(({IDENT})\.latestThinkingSummary=\5\.text\.trim\(\)\.replace\(/\\s\+/g," "\),'
|
|
44
|
+
rf"({IDENT})!==void 0\)\{{"
|
|
45
|
+
rf"let ({IDENT})=Date\.parse\(\2\.timestamp\)-Date\.parse\(\7\);"
|
|
46
|
+
rf"if\(Number\.isFinite\(\8\)&&\8>0\)\6\.thoughtForMs\+=Math\.min\(\8,({IDENT}|\d+)\)\}}"
|
|
47
|
+
rf"\6\.messages\.push\(\5\.message\)\}}"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# The early return that hides thinking outside transcript/verbose mode.
|
|
51
|
+
# 2.1.216 wrapped the body in braces (`{return null}`); older builds used the
|
|
52
|
+
# bare statement. Accept both -- missing the block form is exactly how this
|
|
53
|
+
# patch silently broke once.
|
|
54
|
+
_NULL_GUARD = compile_js(
|
|
55
|
+
rf"if\(!{IDENT}(?:&&!{IDENT}){{1,2}}\)(?:\{{return null\}}|return null;?)"
|
|
56
|
+
)
|
|
57
|
+
_RENDERER_PROPS = compile_js(rf"((?:createElement|jsx|jsxs)\({IDENT},\{{)([^}}]*)\}}")
|
|
58
|
+
_IS_TRANSCRIPT = compile_js(r"isTranscriptMode:[^,}]+")
|
|
59
|
+
_HIDE_IN_TRANSCRIPT = compile_js(r"hideInTranscript:[^,}]+")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _reroute_grouping(content: str, outcome: Outcome) -> str:
|
|
63
|
+
"""Send finished thinking messages to the visible list, not the group."""
|
|
64
|
+
step = outcome.step("group-routing")
|
|
65
|
+
|
|
66
|
+
def rewrite(match: re.Match[str]) -> str:
|
|
67
|
+
gate, loop, flush, visible, summary, group, stamp, delta, cap = match.groups()
|
|
68
|
+
step.candidates += 1
|
|
69
|
+
step.applied += 1
|
|
70
|
+
return (
|
|
71
|
+
f"else if({gate}({loop})){flush}(),{visible}.push({loop});"
|
|
72
|
+
f"else if({summary}!==void 0){{"
|
|
73
|
+
f"if({stamp}!==void 0){{"
|
|
74
|
+
f"let {delta}=Date.parse({loop}.timestamp)-Date.parse({stamp});"
|
|
75
|
+
f"if(Number.isFinite({delta})&&{delta}>0)"
|
|
76
|
+
f"{group}.thoughtForMs+=Math.min({delta},{cap})}}"
|
|
77
|
+
f"{flush}(),{visible}.push({loop})}}"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
return _GROUP_ROUTING.sub(rewrite, content)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _thinking_inline(content: str, _options: Options, outcome: Outcome) -> str:
|
|
84
|
+
"""Route thinking past the collapse machinery and render it expanded.
|
|
85
|
+
|
|
86
|
+
One rewrite in the activity-group builder (see :data:`_GROUP_ROUTING`),
|
|
87
|
+
then two inside every ``case"thinking":`` arm that renders with an
|
|
88
|
+
``isTranscriptMode:`` prop: remove the early null-return, and force the
|
|
89
|
+
renderer into transcript presentation (full markdown instead of the
|
|
90
|
+
one-line collapsed form).
|
|
91
|
+
"""
|
|
92
|
+
guard = outcome.step("null-guard")
|
|
93
|
+
props = outcome.step("renderer-props")
|
|
94
|
+
output = _reroute_grouping(content, outcome)
|
|
95
|
+
needle = 'case"thinking":'
|
|
96
|
+
index = 0
|
|
97
|
+
|
|
98
|
+
while True:
|
|
99
|
+
start = output.find(needle, index)
|
|
100
|
+
if start == -1:
|
|
101
|
+
break
|
|
102
|
+
end = switch_case_end(output, start + len(needle))
|
|
103
|
+
segment = output[start:end]
|
|
104
|
+
index = start + len(needle)
|
|
105
|
+
|
|
106
|
+
if "isTranscriptMode:" not in segment:
|
|
107
|
+
continue
|
|
108
|
+
|
|
109
|
+
next_segment, dropped = _NULL_GUARD.subn("", segment, count=1)
|
|
110
|
+
if dropped:
|
|
111
|
+
guard.candidates += 1
|
|
112
|
+
guard.applied += 1
|
|
113
|
+
|
|
114
|
+
def rewrite_props(match: re.Match[str]) -> str:
|
|
115
|
+
prefix, body = match.group(1), match.group(2)
|
|
116
|
+
updated = _IS_TRANSCRIPT.sub("isTranscriptMode:!0", body)
|
|
117
|
+
updated = _HIDE_IN_TRANSCRIPT.sub("hideInTranscript:!1", updated)
|
|
118
|
+
if updated == body:
|
|
119
|
+
return match.group(0)
|
|
120
|
+
props.candidates += 1
|
|
121
|
+
props.applied += 1
|
|
122
|
+
return f"{prefix}{updated}}}"
|
|
123
|
+
|
|
124
|
+
next_segment = _RENDERER_PROPS.sub(rewrite_props, next_segment)
|
|
125
|
+
|
|
126
|
+
if next_segment != segment:
|
|
127
|
+
output = splice(output, start, end, next_segment)
|
|
128
|
+
index = start + len(next_segment)
|
|
129
|
+
|
|
130
|
+
return output
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
PATCHES = [
|
|
134
|
+
Patch(
|
|
135
|
+
id="thinking-inline",
|
|
136
|
+
title="Always show thinking",
|
|
137
|
+
summary="Render thinking blocks inline instead of hiding them behind ctrl+o.",
|
|
138
|
+
group=GROUP_THINKING,
|
|
139
|
+
fn=_thinking_inline,
|
|
140
|
+
anchors=('case"thinking":', "isTranscriptMode:", "latestThinkingSummary"),
|
|
141
|
+
),
|
|
142
|
+
]
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
"""Make thinking blocks visible in the normal UI instead of transcript-only."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import re
|
|
6
|
-
|
|
7
|
-
from .base import (
|
|
8
|
-
GROUP_THINKING,
|
|
9
|
-
IDENT,
|
|
10
|
-
Options,
|
|
11
|
-
Outcome,
|
|
12
|
-
Patch,
|
|
13
|
-
compile_js,
|
|
14
|
-
splice,
|
|
15
|
-
switch_case_end,
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
# The early return that hides thinking outside transcript/verbose mode.
|
|
19
|
-
# 2.1.216 wrapped the body in braces (`{return null}`); older builds used the
|
|
20
|
-
# bare statement. Accept both -- missing the block form is exactly how this
|
|
21
|
-
# patch silently broke once.
|
|
22
|
-
_NULL_GUARD = compile_js(
|
|
23
|
-
rf"if\(!{IDENT}(?:&&!{IDENT}){{1,2}}\)(?:\{{return null\}}|return null;?)"
|
|
24
|
-
)
|
|
25
|
-
_RENDERER_PROPS = compile_js(rf"((?:createElement|jsx|jsxs)\({IDENT},\{{)([^}}]*)\}}")
|
|
26
|
-
_IS_TRANSCRIPT = compile_js(r"isTranscriptMode:[^,}]+")
|
|
27
|
-
_HIDE_IN_TRANSCRIPT = compile_js(r"hideInTranscript:[^,}]+")
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def _thinking_inline(content: str, _options: Options, outcome: Outcome) -> str:
|
|
31
|
-
"""Drop the transcript-only gate around thinking blocks.
|
|
32
|
-
|
|
33
|
-
Two rewrites inside every ``case"thinking":`` arm that renders with an
|
|
34
|
-
``isTranscriptMode:`` prop: remove the early null-return, and force the
|
|
35
|
-
renderer into transcript presentation (full markdown instead of the
|
|
36
|
-
one-line collapsed form).
|
|
37
|
-
"""
|
|
38
|
-
guard = outcome.step("null-guard")
|
|
39
|
-
props = outcome.step("renderer-props")
|
|
40
|
-
output = content
|
|
41
|
-
needle = 'case"thinking":'
|
|
42
|
-
index = 0
|
|
43
|
-
|
|
44
|
-
while True:
|
|
45
|
-
start = output.find(needle, index)
|
|
46
|
-
if start == -1:
|
|
47
|
-
break
|
|
48
|
-
end = switch_case_end(output, start + len(needle))
|
|
49
|
-
segment = output[start:end]
|
|
50
|
-
index = start + len(needle)
|
|
51
|
-
|
|
52
|
-
if "isTranscriptMode:" not in segment:
|
|
53
|
-
continue
|
|
54
|
-
|
|
55
|
-
next_segment, dropped = _NULL_GUARD.subn("", segment, count=1)
|
|
56
|
-
if dropped:
|
|
57
|
-
guard.candidates += 1
|
|
58
|
-
guard.applied += 1
|
|
59
|
-
|
|
60
|
-
def rewrite_props(match: re.Match[str]) -> str:
|
|
61
|
-
prefix, body = match.group(1), match.group(2)
|
|
62
|
-
updated = _IS_TRANSCRIPT.sub("isTranscriptMode:!0", body)
|
|
63
|
-
updated = _HIDE_IN_TRANSCRIPT.sub("hideInTranscript:!1", updated)
|
|
64
|
-
if updated == body:
|
|
65
|
-
return match.group(0)
|
|
66
|
-
props.candidates += 1
|
|
67
|
-
props.applied += 1
|
|
68
|
-
return f"{prefix}{updated}}}"
|
|
69
|
-
|
|
70
|
-
next_segment = _RENDERER_PROPS.sub(rewrite_props, next_segment)
|
|
71
|
-
|
|
72
|
-
if next_segment != segment:
|
|
73
|
-
output = splice(output, start, end, next_segment)
|
|
74
|
-
index = start + len(next_segment)
|
|
75
|
-
|
|
76
|
-
return output
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
PATCHES = [
|
|
80
|
-
Patch(
|
|
81
|
-
id="thinking-inline",
|
|
82
|
-
title="Always show thinking",
|
|
83
|
-
summary="Render thinking blocks inline instead of hiding them behind ctrl+o.",
|
|
84
|
-
group=GROUP_THINKING,
|
|
85
|
-
fn=_thinking_inline,
|
|
86
|
-
anchors=('case"thinking":', "isTranscriptMode:"),
|
|
87
|
-
),
|
|
88
|
-
]
|
|
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
|
|
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
|