cc-transcript 14.0.0__tar.gz → 14.1.1__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.
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/PKG-INFO +1 -1
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/_native.pyi +6 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/pyproject.toml +1 -1
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/src/command.rs +166 -7
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/src/parse.rs +2 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/src/types.rs +2 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/data/command_prefix_pins.tsv +1 -1
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/views/command.rs +1 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/views/print.rs +15 -1
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/views/store.rs +7 -2
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/Cargo.lock +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/Cargo.toml +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/LICENSE +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/README.md +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/__init__.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/activity.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/activity_probe.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/builders.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/command.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/context.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/corrections.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/cost.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/decisions.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/discovery.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/disktruth.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/evidence.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/extract/__init__.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/extract/correct.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/facts.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/filterspec.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/heartbeats.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/ids.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/judge/__init__.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/judge/llm.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/judge/similar.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/judge/verdicts.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/ledger.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/literals.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/mining/__init__.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/mining/candidates.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/mining/confidence.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/mining/engine.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/mining/filterspec.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/mining/formats.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/mining/sampling.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/mining/signals.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/mining/sourcekind.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/mining/spec.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/mining/store.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/models.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/nlp.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/notifications.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/parser.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/py.typed +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/query.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/render.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/sentiment/__init__.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/sentiment/buckets.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/sentiment/data/afinn-en-165.tsv +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/sentiment/data/domain_overrides.tsv +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/sentiment/data/en-ewt.udpipe +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/sentiment/engine.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/sentiment/lexicon.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/sentiment/scorespec.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/store.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/tools.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/watch.py +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/cli/Cargo.toml +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/cli/src/commands/corrections.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/cli/src/commands/digest.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/cli/src/commands/facts.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/cli/src/commands/grep.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/cli/src/commands/list.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/cli/src/commands/mod.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/cli/src/commands/scratchpad.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/cli/src/commands/show.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/cli/src/commands/slice.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/cli/src/commands/stats.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/cli/src/commands/watch.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/cli/src/lib.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/cli/src/main.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/cli/src/output.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/cli/src/target.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/Cargo.toml +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/benches/parse.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/benches/render.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/benches/toolcall.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/src/activity.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/src/buckets.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/src/context.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/src/corrections.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/src/cost.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/src/discovery.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/src/facts.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/src/filter.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/src/ids.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/src/lib.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/src/literals/command.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/src/literals/corrections.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/src/literals/mining.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/src/literals/mod.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/src/literals/protocol.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/src/notifications.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/src/protocol.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/src/pystr.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/src/query.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/src/render.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/src/toolcall.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/src/value.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/core/src/watch.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/Cargo.toml +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/build.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/bin/stub_gen.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/command.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/context.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/corrections.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/discovery.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/lexicon.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/lib.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/mining.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/nlp.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/python.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/render.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/score.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/toolcall.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/views/attachment.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/views/blocks.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/views/convert.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/views/dunder.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/views/events.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/views/meta.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/views/mod.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/views/system.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/views/toolcall.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/views/toolresult.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/views/transcript.rs +0 -0
- {cc_transcript-14.0.0 → cc_transcript-14.1.1}/rust/crates/py/src/watch.rs +0 -0
|
@@ -1259,6 +1259,8 @@ class PrintMessage:
|
|
|
1259
1259
|
blocks: The structured content blocks of the message.
|
|
1260
1260
|
uuid: The message's event uuid, when present.
|
|
1261
1261
|
session_id: The session the message belongs to.
|
|
1262
|
+
id: The API message id, when present (assistant messages carry one).
|
|
1263
|
+
usage: The message's token usage, when present (assistant messages carry one).
|
|
1262
1264
|
"""
|
|
1263
1265
|
__match_args__: typing.ClassVar[tuple[str, ...]]
|
|
1264
1266
|
@property
|
|
@@ -1273,6 +1275,10 @@ class PrintMessage:
|
|
|
1273
1275
|
def uuid(self) -> ids.EventUuid | None: ...
|
|
1274
1276
|
@property
|
|
1275
1277
|
def session_id(self) -> ids.SessionId: ...
|
|
1278
|
+
@property
|
|
1279
|
+
def id(self) -> typing.Optional[builtins.str]: ...
|
|
1280
|
+
@property
|
|
1281
|
+
def usage(self) -> typing.Optional[Usage]: ...
|
|
1276
1282
|
|
|
1277
1283
|
@typing.final
|
|
1278
1284
|
class PrintResult:
|
|
@@ -46,6 +46,10 @@ pub struct Command {
|
|
|
46
46
|
// Parity: command.py Command.span — byte offsets (start, end), None when a redirect absorbs a
|
|
47
47
|
// trailing word; carries compare=False, repr=False, so it is excluded from PartialEq and Debug.
|
|
48
48
|
pub span: Option<(usize, usize)>,
|
|
49
|
+
// Native-only provenance for top-level selectors. Substitution commands remain visible in
|
|
50
|
+
// occurrence APIs; this flag is excluded from PartialEq, Debug, and the PyO3 command surface.
|
|
51
|
+
#[doc(hidden)]
|
|
52
|
+
pub substitution: bool,
|
|
49
53
|
}
|
|
50
54
|
|
|
51
55
|
impl PartialEq for Command {
|
|
@@ -145,6 +149,7 @@ impl Command {
|
|
|
145
149
|
env: self.env.clone(),
|
|
146
150
|
redirects: self.redirects.clone(),
|
|
147
151
|
span: self.span,
|
|
152
|
+
substitution: self.substitution,
|
|
148
153
|
}
|
|
149
154
|
}
|
|
150
155
|
|
|
@@ -183,10 +188,23 @@ pub struct CommandLine {
|
|
|
183
188
|
impl CommandLine {
|
|
184
189
|
// Parity: command.py CommandLine.parse — blank/comment-only input yields empty parts.
|
|
185
190
|
pub fn parse(raw: &str) -> CommandLine {
|
|
186
|
-
let parts = BASH_PARSER.with(|parser| match parser.borrow_mut().parse(raw, None) {
|
|
191
|
+
let mut parts = BASH_PARSER.with(|parser| match parser.borrow_mut().parse(raw, None) {
|
|
187
192
|
Some(tree) => walk_node(tree.root_node(), raw.as_bytes()),
|
|
188
193
|
None => Vec::new(),
|
|
189
194
|
});
|
|
195
|
+
// Nested inside an enumerated host's span: visible but span-less, like an absorbed word.
|
|
196
|
+
let spans: Vec<Option<(usize, usize)>> = parts.iter().map(|(cmd, _)| cmd.span).collect();
|
|
197
|
+
for (i, (cmd, _)) in parts.iter_mut().enumerate() {
|
|
198
|
+
let Some((start, end)) = cmd.span else {
|
|
199
|
+
continue;
|
|
200
|
+
};
|
|
201
|
+
let enclosed = spans.iter().enumerate().any(|(j, other)| {
|
|
202
|
+
j != i && matches!(other, Some((os, oe)) if *os <= start && end <= *oe && (*os, *oe) != (start, end))
|
|
203
|
+
});
|
|
204
|
+
if enclosed {
|
|
205
|
+
cmd.span = None;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
190
208
|
CommandLine {
|
|
191
209
|
raw: raw.to_string(),
|
|
192
210
|
parts,
|
|
@@ -198,14 +216,19 @@ impl CommandLine {
|
|
|
198
216
|
self.parts.iter().map(|(cmd, _)| cmd).collect()
|
|
199
217
|
}
|
|
200
218
|
|
|
201
|
-
// Parity: command.py CommandLine.primary — the final command, or None.
|
|
219
|
+
// Parity: command.py CommandLine.primary — the final top-level command, or None.
|
|
202
220
|
pub fn primary(&self) -> Option<&Command> {
|
|
203
|
-
self.parts
|
|
221
|
+
self.parts
|
|
222
|
+
.iter()
|
|
223
|
+
.rev()
|
|
224
|
+
.find_map(|(cmd, _)| (!cmd.substitution).then_some(cmd))
|
|
204
225
|
}
|
|
205
226
|
|
|
206
|
-
// Parity: command.py CommandLine.head — the first command, or None.
|
|
227
|
+
// Parity: command.py CommandLine.head — the first top-level command, or None.
|
|
207
228
|
pub fn head(&self) -> Option<&Command> {
|
|
208
|
-
self.parts
|
|
229
|
+
self.parts
|
|
230
|
+
.iter()
|
|
231
|
+
.find_map(|(cmd, _)| (!cmd.substitution).then_some(cmd))
|
|
209
232
|
}
|
|
210
233
|
|
|
211
234
|
// Parity: command.py CommandLine.prefixes — each command's prefix, None dropped.
|
|
@@ -501,6 +524,7 @@ fn extract_command(node: Node, src: &[u8]) -> Command {
|
|
|
501
524
|
env,
|
|
502
525
|
redirects,
|
|
503
526
|
span: Some(span),
|
|
527
|
+
substitution: false,
|
|
504
528
|
}
|
|
505
529
|
}
|
|
506
530
|
|
|
@@ -562,13 +586,19 @@ fn walk_redirected(node: Node, src: &[u8]) -> Vec<(Command, Option<String>)> {
|
|
|
562
586
|
}
|
|
563
587
|
|
|
564
588
|
// Parity: command.py CommandLine.walk_node — program/list/pipeline split at their ops,
|
|
565
|
-
// command extracts,
|
|
589
|
+
// command extracts, command substitutions mark their recursive commands, redirected_statement
|
|
590
|
+
// unwraps, and everything else recurses in order.
|
|
566
591
|
fn walk_node(node: Node, src: &[u8]) -> Vec<(Command, Option<String>)> {
|
|
567
592
|
match node.kind() {
|
|
568
593
|
"program" => walk_program(node, src),
|
|
569
594
|
"list" => collect_parts(node, src, COMPOUND_OPS),
|
|
570
595
|
"pipeline" => collect_parts(node, src, &["|"]),
|
|
571
|
-
"command" =>
|
|
596
|
+
"command" => {
|
|
597
|
+
let mut parts = vec![(extract_command(node, src), None)];
|
|
598
|
+
parts.extend(substitution_parts(node, src));
|
|
599
|
+
parts
|
|
600
|
+
}
|
|
601
|
+
"command_substitution" => walk_substitution(node, src),
|
|
572
602
|
"redirected_statement" => walk_redirected(node, src),
|
|
573
603
|
_ => {
|
|
574
604
|
let mut parts = Vec::new();
|
|
@@ -581,6 +611,33 @@ fn walk_node(node: Node, src: &[u8]) -> Vec<(Command, Option<String>)> {
|
|
|
581
611
|
}
|
|
582
612
|
}
|
|
583
613
|
|
|
614
|
+
fn walk_substitution(node: Node, src: &[u8]) -> Vec<(Command, Option<String>)> {
|
|
615
|
+
let mut parts = Vec::new();
|
|
616
|
+
let mut cursor = node.walk();
|
|
617
|
+
for child in node.children(&mut cursor) {
|
|
618
|
+
parts.extend(walk_node(child, src));
|
|
619
|
+
}
|
|
620
|
+
for (cmd, _) in &mut parts {
|
|
621
|
+
cmd.substitution = true;
|
|
622
|
+
}
|
|
623
|
+
parts
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// Word/argument-position `$(…)`/backtick substitutions under a command node, enumerated as parts
|
|
627
|
+
// mirroring assignment position: document order (host first), redirect targets excluded.
|
|
628
|
+
fn substitution_parts(node: Node, src: &[u8]) -> Vec<(Command, Option<String>)> {
|
|
629
|
+
let mut parts = Vec::new();
|
|
630
|
+
let mut cursor = node.walk();
|
|
631
|
+
for child in node.children(&mut cursor) {
|
|
632
|
+
match child.kind() {
|
|
633
|
+
"command_substitution" => parts.extend(walk_node(child, src)),
|
|
634
|
+
"file_redirect" => {}
|
|
635
|
+
_ => parts.extend(substitution_parts(child, src)),
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
parts
|
|
639
|
+
}
|
|
640
|
+
|
|
584
641
|
// Parity: command.py CommandLine.walk_program — collect_parts over `;`, dropping the heredoc body
|
|
585
642
|
// and delimiter lines that tree-sitter's multi-heredoc ERROR recovery re-parses as sibling commands.
|
|
586
643
|
fn walk_program(node: Node, src: &[u8]) -> Vec<(Command, Option<String>)> {
|
|
@@ -1017,6 +1074,108 @@ mod tests {
|
|
|
1017
1074
|
assert_eq!(line.parts[0].0.redirects[0].fd, None);
|
|
1018
1075
|
}
|
|
1019
1076
|
|
|
1077
|
+
fn execs(line: &CommandLine) -> Vec<&str> {
|
|
1078
|
+
line.parts
|
|
1079
|
+
.iter()
|
|
1080
|
+
.map(|(cmd, _)| cmd.executable.as_str())
|
|
1081
|
+
.collect()
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
// Both substitution positions get one treatment: the nested command is a first-class part.
|
|
1085
|
+
#[test]
|
|
1086
|
+
fn substitutions_enumerate_in_word_position_like_assignment_position() {
|
|
1087
|
+
assert_eq!(
|
|
1088
|
+
execs(&CommandLine::parse("x=$(ccx repo overview)")),
|
|
1089
|
+
["ccx"]
|
|
1090
|
+
);
|
|
1091
|
+
let word = CommandLine::parse("echo $(ccx repo overview)");
|
|
1092
|
+
assert_eq!(execs(&word), ["echo", "ccx"]);
|
|
1093
|
+
assert_eq!(word.parts[1].0.args, ["repo", "overview"]);
|
|
1094
|
+
assert_eq!(
|
|
1095
|
+
execs(&CommandLine::parse("echo `ccx repo overview`")),
|
|
1096
|
+
["echo", "ccx"]
|
|
1097
|
+
);
|
|
1098
|
+
// command_name, quoted, concatenated, and env-prefix-value positions all enumerate.
|
|
1099
|
+
assert_eq!(
|
|
1100
|
+
execs(&CommandLine::parse("$(which python) --version")),
|
|
1101
|
+
["$(which python)", "which"]
|
|
1102
|
+
);
|
|
1103
|
+
assert_eq!(execs(&CommandLine::parse("echo \"$(a)\"")), ["echo", "a"]);
|
|
1104
|
+
assert_eq!(
|
|
1105
|
+
execs(&CommandLine::parse("tag=v$(git rev-parse HEAD) make")),
|
|
1106
|
+
["make", "git"]
|
|
1107
|
+
);
|
|
1108
|
+
// Redirect targets stay out, matching statement-level redirects.
|
|
1109
|
+
assert_eq!(execs(&CommandLine::parse("echo hi > $(target)")), ["echo"]);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
#[test]
|
|
1113
|
+
fn primary_skips_argument_position_substitution() {
|
|
1114
|
+
let line = CommandLine::parse(r#"gh pr list --json number --search "$(cat q.txt)""#);
|
|
1115
|
+
assert_eq!(execs(&line), ["gh", "cat"]);
|
|
1116
|
+
assert_eq!(line.primary().unwrap().executable, "gh");
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
#[test]
|
|
1120
|
+
fn primary_keeps_word_position_host_command() {
|
|
1121
|
+
let line = CommandLine::parse(r#""$(get-cmd)" --flag"#);
|
|
1122
|
+
assert_eq!(execs(&line), ["\"$(get-cmd)\"", "get-cmd"]);
|
|
1123
|
+
assert_eq!(line.primary().unwrap().executable, "\"$(get-cmd)\"");
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
#[test]
|
|
1127
|
+
fn head_skips_leading_assignment_position_substitution() {
|
|
1128
|
+
let line = CommandLine::parse("x=$(get-cmd); host --flag");
|
|
1129
|
+
assert_eq!(execs(&line), ["get-cmd", "host"]);
|
|
1130
|
+
assert_eq!(line.head().unwrap().executable, "host");
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
// Document order: the host command first, then each outermost substitution left to right,
|
|
1134
|
+
// recursing so nested substitutions follow their host.
|
|
1135
|
+
#[test]
|
|
1136
|
+
fn substitutions_enumerate_nested_in_document_order() {
|
|
1137
|
+
assert_eq!(
|
|
1138
|
+
execs(&CommandLine::parse("diff $(sort a) $(b $(c))")),
|
|
1139
|
+
["diff", "sort", "b", "c"]
|
|
1140
|
+
);
|
|
1141
|
+
assert_eq!(
|
|
1142
|
+
execs(&CommandLine::parse("echo $(a | b; c)")),
|
|
1143
|
+
["echo", "a", "b", "c"]
|
|
1144
|
+
);
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
// Operator attachment mirrors assignment position: the op hangs off the last enumerated part.
|
|
1148
|
+
#[test]
|
|
1149
|
+
fn substitution_parts_carry_operators_and_spans_like_assignment_position() {
|
|
1150
|
+
let assign = CommandLine::parse("x=$(a) && foo");
|
|
1151
|
+
assert_eq!(execs(&assign), ["a", "foo"]);
|
|
1152
|
+
assert_eq!(assign.parts[0].1.as_deref(), Some("&&"));
|
|
1153
|
+
let word = CommandLine::parse("echo $(a) && foo");
|
|
1154
|
+
assert_eq!(execs(&word), ["echo", "a", "foo"]);
|
|
1155
|
+
assert_eq!(word.parts[0].1, None);
|
|
1156
|
+
assert_eq!(word.parts[1].1.as_deref(), Some("&&"));
|
|
1157
|
+
assert_eq!(word.next_op(1), Some("&&"));
|
|
1158
|
+
assert_eq!(word.prev_op(2), Some("&&"));
|
|
1159
|
+
assert!(!word.piped(0) && !word.piped(1));
|
|
1160
|
+
// Nested inside the host's span: visible but span-less (splice's non-overlap invariant);
|
|
1161
|
+
// assignment position has no host, so its span survives and splices in place.
|
|
1162
|
+
let line = CommandLine::parse("echo $(ccx repo overview)");
|
|
1163
|
+
assert_eq!(line.parts[0].0.span, Some((0, 25)));
|
|
1164
|
+
assert_eq!(line.parts[1].0.span, None);
|
|
1165
|
+
assert!(matches!(
|
|
1166
|
+
line.splice(&BTreeMap::from([(1, "ls".to_string())])),
|
|
1167
|
+
Err(SpliceError::NoSpan { index: 1 })
|
|
1168
|
+
));
|
|
1169
|
+
let assign_only = CommandLine::parse("x=$(ccx repo overview)");
|
|
1170
|
+
assert_eq!(assign_only.parts[0].0.span, Some((4, 21)));
|
|
1171
|
+
assert_eq!(
|
|
1172
|
+
assign_only
|
|
1173
|
+
.splice(&BTreeMap::from([(0, "ls".to_string())]))
|
|
1174
|
+
.unwrap(),
|
|
1175
|
+
"x=$(ls)"
|
|
1176
|
+
);
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1020
1179
|
#[test]
|
|
1021
1180
|
fn rewrite_occurrences_maps_or_none() {
|
|
1022
1181
|
let line = CommandLine::parse("git push; ls; git pull");
|
|
@@ -699,6 +699,8 @@ fn parse_print_message(element: &Value) -> Result<PrintMessage, ParseError> {
|
|
|
699
699
|
};
|
|
700
700
|
Ok(PrintMessage {
|
|
701
701
|
body,
|
|
702
|
+
id: field_str(message, "id").map(str::to_string),
|
|
703
|
+
usage: parse_usage(message)?,
|
|
702
704
|
uuid: field_str(element, "uuid").map(str::to_string),
|
|
703
705
|
session_id: require_str(element, "session_id")?.to_string(),
|
|
704
706
|
})
|
|
@@ -34,7 +34,7 @@ process-substitution diff <(sort a.txt) <(sort b.txt) diff
|
|
|
34
34
|
process-substitution-in-pipeline cat <(git log) | head -3 cat|head
|
|
35
35
|
arithmetic-expansion echo $((1 + 2)) echo
|
|
36
36
|
arithmetic-assignment-prefix x=$((COUNT + 1)) make build make
|
|
37
|
-
backtick-substitution echo `git rev-parse HEAD` echo
|
|
37
|
+
backtick-substitution echo `git rev-parse HEAD` echo|git rev-parse
|
|
38
38
|
function-definition-body-and-call foo() { git status; }; foo git status|foo
|
|
39
39
|
multiline-quoted-string git commit -m "line1\nline2" git commit
|
|
40
40
|
newline-separated-commands git add .\ngit commit -m x git add|git commit
|
|
@@ -264,6 +264,8 @@ view_dunders!(
|
|
|
264
264
|
/// blocks: The structured content blocks of the message.
|
|
265
265
|
/// uuid: The message's event uuid, when present.
|
|
266
266
|
/// session_id: The session the message belongs to.
|
|
267
|
+
/// id: The API message id, when present (assistant messages carry one).
|
|
268
|
+
/// usage: The message's token usage, when present (assistant messages carry one).
|
|
267
269
|
#[pyo3_stub_gen::derive::gen_stub_pyclass]
|
|
268
270
|
#[pyclass(name = "PrintMessage", module = "cc_transcript.models", frozen)]
|
|
269
271
|
pub(crate) struct PrintMessageView {
|
|
@@ -326,12 +328,24 @@ impl PrintMessageView {
|
|
|
326
328
|
fn session_id(&self, _py: Python<'_>) -> PyResult<String> {
|
|
327
329
|
Ok(self.message().session_id.clone())
|
|
328
330
|
}
|
|
331
|
+
|
|
332
|
+
#[getter]
|
|
333
|
+
fn id(&self, _py: Python<'_>) -> PyResult<Option<String>> {
|
|
334
|
+
Ok(self.message().id.clone())
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
#[getter]
|
|
338
|
+
fn usage(&self, _py: Python<'_>) -> PyResult<Option<UsageView>> {
|
|
339
|
+
Ok(self.message().usage.as_ref().map(|_| UsageView {
|
|
340
|
+
host: UsageHost::PrintMessage(Arc::clone(&self.p.0), self.msg),
|
|
341
|
+
}))
|
|
342
|
+
}
|
|
329
343
|
}
|
|
330
344
|
|
|
331
345
|
view_dunders!(
|
|
332
346
|
PrintMessageView,
|
|
333
347
|
"PrintMessage",
|
|
334
|
-
fields = [role, model, text, blocks, uuid, session_id]
|
|
348
|
+
fields = [role, model, text, blocks, uuid, session_id, id, usage]
|
|
335
349
|
);
|
|
336
350
|
|
|
337
351
|
/// A parsed 'claude -p --output-format json' result.
|
|
@@ -100,12 +100,13 @@ impl BlockRef {
|
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
/// Where a Usage lives: an assistant entry's optional usage,
|
|
104
|
-
/// usage of a ``--print`` result.
|
|
103
|
+
/// Where a Usage lives: an assistant entry's optional usage, the aggregate
|
|
104
|
+
/// usage of a ``--print`` result, or one ``--print`` message's usage.
|
|
105
105
|
#[derive(Clone)]
|
|
106
106
|
pub(crate) enum UsageHost {
|
|
107
107
|
Entry(EventRef),
|
|
108
108
|
Print(Arc<PrintResult>),
|
|
109
|
+
PrintMessage(Arc<PrintResult>, usize),
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
impl UsageHost {
|
|
@@ -117,6 +118,10 @@ impl UsageHost {
|
|
|
117
118
|
.as_ref()
|
|
118
119
|
.expect("usage view over an assistant entry with usage"),
|
|
119
120
|
UsageHost::Print(pr) => &pr.usage,
|
|
121
|
+
UsageHost::PrintMessage(pr, msg) => pr.messages[*msg]
|
|
122
|
+
.usage
|
|
123
|
+
.as_ref()
|
|
124
|
+
.expect("usage view over a print message with usage"),
|
|
120
125
|
}
|
|
121
126
|
}
|
|
122
127
|
}
|
|
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
|
|
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
|
{cc_transcript-14.0.0 → cc_transcript-14.1.1}/cc_transcript/sentiment/data/domain_overrides.tsv
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
|
|
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
|
|
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
|
|
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
|