Skip to content

Commit b4fed79

Browse files
author
SqlRush
committed
Accept wrapped string alias fields
1 parent ddaa979 commit b4fed79

5 files changed

Lines changed: 202 additions & 85 deletions

File tree

docs/cc-100-roadmap.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,8 @@ M7 补充:prompt history `LogEntry` 读取现在接受 `sessionID`/`session`/`
588588

589589
本轮补充:interaction script 的 direct key alias 字段现在也接受 wrapped object,`key``keyPress``keyPresses` 等字段可从 `resource.attributes``edge.node.attrs` 中恢复单键和 key sequence,避免官方 fixture 直接字段形态在 string/list decode 阶段失败。
590590

591+
本轮补充:interaction script 的 direct string alias 字段现在也接受 wrapped object,`text``pasteText``setStatus``snapshotName` 等字段可从 `resource.attributes``edge.node.attrs` 中恢复正文、paste、status 和 snapshot 名称,避免官方 fixture 直接字段形态在 scalar decode 阶段失败。
592+
591593
本轮补充:interaction script step 现在接受 `action`/`type`/`kind`/`name`/`operation` 动作判别别名,并可用 `value`/`payload`/`data` 等载荷字段驱动 key press、key sequence、text input、paste、status、resize、mouse/image 和 focus/blur 动作。
592594

593595
本轮补充:interaction script action/type/kind/name/operation 动作判别字段现在接受 compact/camel fixture aliases,例如 `typeText``inputText``insertText``keyPress``pressKey``keySequence``pasteText``pastedText``clipboardText``setStatus``statusLine``terminalSize``screenSize`

docs/claude-code-go-rewrite-plan.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ test/parity/ # golden tests against TS/official behavior
306306
- 本轮补充:interaction script 的 `keys` 字段支持 printable text chunk 和空格分隔 named-key sequence,例如 `ctrl-x ctrl-k`,减少官方脚本把连续输入拆成数组的改写成本。
307307
- 本轮补充:interaction script key input 接受 press-style aliases,包括 `press``keyPress``keypress``shortcutKey``presses``keyPresses``shortcuts`
308308
- 本轮补充:interaction script key/keySequence action payload 现在递归解包 JSON:API/GraphQL-style wrapper,`payload.resource.attributes.key``edge.node.attrs.sequence` 可直接驱动按键与组合键序列。
309+
- 本轮补充:interaction script 的直接字符串 alias 字段现在也接受 wrapped object;`text``pasteText``setStatus``snapshotName` 等字段可从 `resource.attributes``edge.node.attrs` 中恢复正文、paste、status 和 snapshot 名称,避免 direct field fixture 在 scalar decode 阶段失败。
309310
- 本轮补充:interaction script action/type/kind/name/operation 动作判别字段接受 compact/camel fixture aliases,包括 `typeText``inputText``insertText``keyPress``pressKey``keySequence``pasteText``pastedText``clipboardText``setStatus``statusLine``terminalSize``screenSize`
310311
- 本轮补充:interaction script 的 typeText/pasteText/setStatus/snapshot 字符串 action payload 现在递归解包 JSON:API/GraphQL-style wrapper,`payload.resource.attributes.text``edge.node.attrs.content``resource.attributes.message` 和 wrapped snapshot `name` 可直接驱动 prompt、paste、status 与 snapshot capture。
311312
- 本轮补充:interaction script resize/terminalSize/screenSize action payload 现在递归解包 `value`/`payload`/`data`/`resource`/`attributes`/`properties`/`attrs`/`edge.node` 等 wrapper,官方/API fixture 可把 columns/rows 放在 JSON:API 或 GraphQL envelope 内。

docs/first-second-parity-audit.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ M7 progress now includes:
289289
- `internal/tui`: scripted interaction key input now accepts press-style aliases such as `press`, `keyPress`, `keypress`, `shortcutKey`, `presses`, `keyPresses`, and `shortcuts`.
290290
- `internal/tui`: scripted key and key-sequence action payloads now recursively unwrap JSON:API/GraphQL-style wrappers, preserving wrapped single keys and key sequence arrays from API-shaped interaction fixtures.
291291
- `internal/tui`: direct scripted key alias fields such as `key`, `keyPress`, and `keyPresses` now accept JSON:API/GraphQL-style wrapper objects without failing strong string/list decoding.
292+
- `internal/tui`: direct scripted string alias fields such as `text`, `pasteText`, `setStatus`, and `snapshotName` now accept JSON:API/GraphQL-style wrapper objects without failing strong scalar decoding.
292293
- `internal/tui`: terminal key parsing now accepts CSI-u/kitty keyboard protocol sequences for existing ctrl/alt editing keys, shift-enter, shift-tab, and printable shift-only runes.
293294
- `internal/tui`: terminal CSI-u/kitty keyboard parsing now also accepts colon-suffixed alternate codepoint and modifier event-type fields such as `CSI 97:65;5:1u`.
294295
- `internal/tui`: terminal CSI-u/kitty keyboard parsing now also accepts base/unmodified sequences such as `CSI 97u` and `CSI 13;1u`, mapping printable runes plus Enter, Tab, Esc, and Backspace instead of treating ordinary extended-key reports as unknown.

internal/tui/script_aliases.go

Lines changed: 129 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ func (step *ScriptStep) UnmarshalJSON(data []byte) error {
339339
data = normalizeScriptStepJSON(data)
340340
type alias ScriptStep
341341
var raw alias
342-
if err := json.Unmarshal(stripScriptStepRawKeyAliasFields(data), &raw); err != nil {
342+
if err := json.Unmarshal(stripScriptStepRawScalarAliasFields(data), &raw); err != nil {
343343
return err
344344
}
345345
*step = ScriptStep(raw)
@@ -367,30 +367,30 @@ func (step *ScriptStep) UnmarshalJSON(data []byte) error {
367367
KeyPressesCamel *json.RawMessage `json:"keyPresses"`
368368
Keypresses *json.RawMessage `json:"keypresses"`
369369
Shortcuts *json.RawMessage `json:"shortcuts"`
370-
Input *string `json:"input"`
371-
InputText *string `json:"input_text"`
372-
InputTextCamel *string `json:"inputText"`
373-
TextInput *string `json:"text_input"`
374-
TextInputCamel *string `json:"textInput"`
375-
KeysText *string `json:"keys_text"`
376-
KeysTextCamel *string `json:"keysText"`
377-
PasteText *string `json:"paste_text"`
378-
PasteTextCamel *string `json:"pasteText"`
379-
PastedText *string `json:"pasted_text"`
380-
PastedTextCamel *string `json:"pastedText"`
381-
Clipboard *string `json:"clipboard"`
370+
Input *json.RawMessage `json:"input"`
371+
InputText *json.RawMessage `json:"input_text"`
372+
InputTextCamel *json.RawMessage `json:"inputText"`
373+
TextInput *json.RawMessage `json:"text_input"`
374+
TextInputCamel *json.RawMessage `json:"textInput"`
375+
KeysText *json.RawMessage `json:"keys_text"`
376+
KeysTextCamel *json.RawMessage `json:"keysText"`
377+
PasteText *json.RawMessage `json:"paste_text"`
378+
PasteTextCamel *json.RawMessage `json:"pasteText"`
379+
PastedText *json.RawMessage `json:"pasted_text"`
380+
PastedTextCamel *json.RawMessage `json:"pastedText"`
381+
Clipboard *json.RawMessage `json:"clipboard"`
382382
Messages []Message `json:"messages"`
383383
AppendMessages []Message `json:"append_messages"`
384384
AppendMessagesCamel []Message `json:"appendMessages"`
385385
TranscriptMessages []Message `json:"transcript_messages"`
386386
TranscriptMessagesCamel []Message `json:"transcriptMessages"`
387-
Status *string `json:"status"`
388-
SetStatus *string `json:"set_status"`
389-
SetStatusCamel *string `json:"setStatus"`
390-
StatusLine *string `json:"status_line"`
391-
StatusLineCamel *string `json:"statusLine"`
392-
BaseStatus *string `json:"base_status"`
393-
BaseStatusCamel *string `json:"baseStatus"`
387+
Status *json.RawMessage `json:"status"`
388+
SetStatus *json.RawMessage `json:"set_status"`
389+
SetStatusCamel *json.RawMessage `json:"setStatus"`
390+
StatusLine *json.RawMessage `json:"status_line"`
391+
StatusLineCamel *json.RawMessage `json:"statusLine"`
392+
BaseStatus *json.RawMessage `json:"base_status"`
393+
BaseStatusCamel *json.RawMessage `json:"baseStatus"`
394394
Mouse *ScriptMouse `json:"mouse"`
395395
MouseEvent *ScriptMouse `json:"mouse_event"`
396396
MouseEventCamel *ScriptMouse `json:"mouseEvent"`
@@ -443,8 +443,8 @@ func (step *ScriptStep) UnmarshalJSON(data []byte) error {
443443
ResizeWidthCamel *int `json:"resizeWidth"`
444444
ResizeHeight *int `json:"resize_height"`
445445
ResizeHeightCamel *int `json:"resizeHeight"`
446-
SnapshotName *string `json:"snapshot_name"`
447-
SnapshotNameCamel *string `json:"snapshotName"`
446+
SnapshotName *json.RawMessage `json:"snapshot_name"`
447+
SnapshotNameCamel *json.RawMessage `json:"snapshotName"`
448448
Focus *bool `json:"focus"`
449449
Focused *bool `json:"focused"`
450450
FocusIn *bool `json:"focus_in"`
@@ -581,41 +581,46 @@ func (step *ScriptStep) UnmarshalJSON(data []byte) error {
581581
); len(values) > 0 {
582582
step.Keys = append(step.Keys, values...)
583583
}
584-
if fields.Input != nil {
585-
step.Text = *fields.Input
586-
}
587-
if fields.InputText != nil {
588-
step.Text = *fields.InputText
589-
}
590-
if fields.InputTextCamel != nil {
591-
step.Text = *fields.InputTextCamel
592-
}
593-
if fields.TextInput != nil {
594-
step.Text = *fields.TextInput
595-
}
596-
if fields.TextInputCamel != nil {
597-
step.Text = *fields.TextInputCamel
598-
}
599-
if fields.KeysText != nil {
600-
step.Text = *fields.KeysText
601-
}
602-
if fields.KeysTextCamel != nil {
603-
step.Text = *fields.KeysTextCamel
604-
}
605-
if fields.PasteText != nil {
606-
step.Paste = *fields.PasteText
607-
}
608-
if fields.PasteTextCamel != nil {
609-
step.Paste = *fields.PasteTextCamel
610-
}
611-
if fields.PastedText != nil {
612-
step.Paste = *fields.PastedText
613-
}
614-
if fields.PastedTextCamel != nil {
615-
step.Paste = *fields.PastedTextCamel
584+
if step.Text == "" {
585+
step.Text = scriptNamedStringField(rawFieldMap,
586+
[]string{
587+
"Text",
588+
"text",
589+
"input",
590+
"input_text",
591+
"inputText",
592+
"text_input",
593+
"textInput",
594+
"keys_text",
595+
"keysText",
596+
},
597+
"text",
598+
"input",
599+
"content",
600+
"body",
601+
"message",
602+
"value",
603+
)
616604
}
617-
if fields.Clipboard != nil {
618-
step.Paste = *fields.Clipboard
605+
if step.Paste == "" {
606+
step.Paste = scriptNamedStringField(rawFieldMap,
607+
[]string{
608+
"Paste",
609+
"paste",
610+
"paste_text",
611+
"pasteText",
612+
"pasted_text",
613+
"pastedText",
614+
"clipboard",
615+
},
616+
"paste",
617+
"clipboard",
618+
"text",
619+
"content",
620+
"body",
621+
"message",
622+
"value",
623+
)
619624
}
620625
if fields.Messages != nil {
621626
step.Messages = fields.Messages
@@ -632,26 +637,25 @@ func (step *ScriptStep) UnmarshalJSON(data []byte) error {
632637
if fields.TranscriptMessagesCamel != nil {
633638
step.Messages = fields.TranscriptMessagesCamel
634639
}
635-
if fields.Status != nil {
636-
step.Status = *fields.Status
637-
}
638-
if fields.SetStatus != nil {
639-
step.Status = *fields.SetStatus
640-
}
641-
if fields.SetStatusCamel != nil {
642-
step.Status = *fields.SetStatusCamel
643-
}
644-
if fields.StatusLine != nil {
645-
step.Status = *fields.StatusLine
646-
}
647-
if fields.StatusLineCamel != nil {
648-
step.Status = *fields.StatusLineCamel
649-
}
650-
if fields.BaseStatus != nil {
651-
step.Status = *fields.BaseStatus
652-
}
653-
if fields.BaseStatusCamel != nil {
654-
step.Status = *fields.BaseStatusCamel
640+
if step.Status == "" {
641+
step.Status = scriptNamedStringField(rawFieldMap,
642+
[]string{
643+
"Status",
644+
"status",
645+
"set_status",
646+
"setStatus",
647+
"status_line",
648+
"statusLine",
649+
"base_status",
650+
"baseStatus",
651+
},
652+
"status",
653+
"text",
654+
"message",
655+
"content",
656+
"body",
657+
"value",
658+
)
655659
}
656660
if fields.Mouse != nil {
657661
step.Mouse = fields.Mouse
@@ -742,14 +746,28 @@ func (step *ScriptStep) UnmarshalJSON(data []byte) error {
742746
step.ResizeHeight = *height
743747
}
744748
}
745-
if fields.SnapshotName != nil {
746-
step.SnapshotName = *fields.SnapshotName
747-
}
748-
if fields.SnapshotNameCamel != nil {
749-
step.SnapshotName = *fields.SnapshotNameCamel
750-
}
751749
if step.SnapshotName == "" {
752-
step.SnapshotName = stringJSONField(fieldMap, "snapshot", "snapshot_id", "snapshotId", "snapshot_label", "snapshotLabel", "capture_name", "captureName", "baseline_name", "baselineName")
750+
step.SnapshotName = scriptNamedStringField(rawFieldMap,
751+
[]string{
752+
"SnapshotName",
753+
"snapshotName",
754+
"snapshot_name",
755+
"snapshot",
756+
"snapshot_id",
757+
"snapshotId",
758+
"snapshot_label",
759+
"snapshotLabel",
760+
"capture_name",
761+
"captureName",
762+
"baseline_name",
763+
"baselineName",
764+
},
765+
"snapshot",
766+
"name",
767+
"label",
768+
"id",
769+
"value",
770+
)
753771
}
754772
if fields.Focus != nil {
755773
step.Keys = append(step.Keys, scriptFocusKey(*fields.Focus))
@@ -1688,6 +1706,19 @@ func scriptActionStringField(fields map[string]json.RawMessage, objectNames ...s
16881706
return ""
16891707
}
16901708

1709+
func scriptNamedStringField(fields map[string]json.RawMessage, directNames []string, nestedNames ...string) string {
1710+
for _, name := range directNames {
1711+
raw, ok := fields[name]
1712+
if !ok {
1713+
continue
1714+
}
1715+
if value := scriptActionStringFromJSON(raw, nestedNames, 0); value != "" {
1716+
return value
1717+
}
1718+
}
1719+
return ""
1720+
}
1721+
16911722
func scriptActionStringListField(fields map[string]json.RawMessage, names ...string) []string {
16921723
for _, name := range append([]string{"value", "payload", "data", "body"}, names...) {
16931724
raw, ok := fields[name]
@@ -2165,13 +2196,26 @@ func normalizeScriptStepJSON(data []byte) []byte {
21652196
)
21662197
}
21672198

2168-
func stripScriptStepRawKeyAliasFields(data []byte) []byte {
2199+
func stripScriptStepRawScalarAliasFields(data []byte) []byte {
21692200
var fields map[string]json.RawMessage
21702201
if err := json.Unmarshal(data, &fields); err != nil {
21712202
return data
21722203
}
21732204
changed := false
2174-
for _, name := range []string{"Key", "key", "Keys", "keys"} {
2205+
for _, name := range []string{
2206+
"Key",
2207+
"key",
2208+
"Keys",
2209+
"keys",
2210+
"Text",
2211+
"text",
2212+
"Paste",
2213+
"paste",
2214+
"Status",
2215+
"status",
2216+
"SnapshotName",
2217+
"snapshotName",
2218+
} {
21752219
raw, ok := fields[name]
21762220
if !ok {
21772221
continue

internal/tui/tui_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5609,6 +5609,75 @@ func TestRunInteractionScriptAcceptsWrappedStringActionPayloads(t *testing.T) {
56095609
}
56105610
}
56115611

5612+
func TestRunInteractionScriptAcceptsWrappedStringAliasFields(t *testing.T) {
5613+
steps, err := ParseInteractionScript([]byte(`[
5614+
{
5615+
"text": {
5616+
"resource": {
5617+
"attributes": {
5618+
"content": "hi"
5619+
}
5620+
}
5621+
},
5622+
"expectPrompt": {"text": "hi"}
5623+
},
5624+
{
5625+
"key": "enter",
5626+
"expectEvent": {"type": "prompt_submitted", "value": "hi"},
5627+
"expectPrompt": {"empty": true}
5628+
},
5629+
{
5630+
"pasteText": {
5631+
"edge": {
5632+
"node": {
5633+
"attrs": {
5634+
"text": "clip"
5635+
}
5636+
}
5637+
}
5638+
},
5639+
"expectPrompt": {"text": "[Pasted text #1]", "expandedText": "clip", "pastedContentCount": 1}
5640+
},
5641+
{
5642+
"setStatus": {
5643+
"resource": {
5644+
"attributes": {
5645+
"message": "direct status"
5646+
}
5647+
}
5648+
},
5649+
"expectStatusContains": "direct status"
5650+
},
5651+
{"message": {"role": "assistant", "text": "ready"}},
5652+
{
5653+
"snapshotName": {
5654+
"edge": {
5655+
"node": {
5656+
"attrs": {
5657+
"name": "direct-snapshot"
5658+
}
5659+
}
5660+
}
5661+
},
5662+
"expectSnapshotContains": "assistant: ready"
5663+
}
5664+
]`))
5665+
if err != nil {
5666+
t.Fatal(err)
5667+
}
5668+
screen := NewREPLScreen(40, 8, nil)
5669+
result, err := RunInteractionScriptChecked(&screen, steps)
5670+
if err != nil {
5671+
t.Fatal(err)
5672+
}
5673+
if len(result.Events) != 1 || result.Events[0].Type != ScreenEventPromptSubmitted || result.Events[0].Value != "hi" {
5674+
t.Fatalf("events = %#v", result.Events)
5675+
}
5676+
if len(result.Snapshots) != 1 || result.Snapshots[0].Name != "direct-snapshot" {
5677+
t.Fatalf("snapshots = %#v", result.Snapshots)
5678+
}
5679+
}
5680+
56125681
func TestRunInteractionScriptAcceptsWrappedKeyActionPayloads(t *testing.T) {
56135682
steps, err := ParseInteractionScript([]byte(`[
56145683
{

0 commit comments

Comments
 (0)