@qqbrowser/openclaw-qbot 0.10.13 → 0.10.15

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 (180) hide show
  1. package/dist/build-info.json +3 -3
  2. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  3. package/dist/canvas-host/a2ui/a2ui.bundle.js +6 -6
  4. package/node_modules/@aws-sdk/client-bedrock-runtime/dist-cjs/index.js +1 -0
  5. package/node_modules/@aws-sdk/client-bedrock-runtime/dist-es/models/enums.js +1 -0
  6. package/node_modules/@aws-sdk/client-bedrock-runtime/package.json +13 -13
  7. package/node_modules/@aws-sdk/core/package.json +6 -6
  8. package/node_modules/@aws-sdk/credential-provider-env/package.json +5 -5
  9. package/node_modules/@aws-sdk/credential-provider-http/package.json +7 -7
  10. package/node_modules/@aws-sdk/credential-provider-ini/package.json +13 -13
  11. package/node_modules/@aws-sdk/credential-provider-login/package.json +6 -6
  12. package/node_modules/@aws-sdk/credential-provider-node/dist-cjs/index.js +12 -1
  13. package/node_modules/@aws-sdk/credential-provider-node/dist-es/runtime/memoize-chain.js +12 -1
  14. package/node_modules/@aws-sdk/credential-provider-node/package.json +11 -11
  15. package/node_modules/@aws-sdk/credential-provider-process/package.json +5 -5
  16. package/node_modules/@aws-sdk/credential-provider-sso/package.json +7 -7
  17. package/node_modules/@aws-sdk/credential-provider-web-identity/package.json +6 -6
  18. package/node_modules/@aws-sdk/eventstream-handler-node/package.json +4 -4
  19. package/node_modules/@aws-sdk/middleware-eventstream/package.json +4 -4
  20. package/node_modules/@aws-sdk/middleware-websocket/package.json +7 -7
  21. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/cognito-identity/index.js +1 -1
  22. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/signin/index.js +1 -1
  23. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso/index.js +1 -1
  24. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso-oidc/index.js +1 -1
  25. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sts/index.js +1 -1
  26. package/node_modules/@aws-sdk/nested-clients/package.json +8 -8
  27. package/node_modules/@aws-sdk/signature-v4-multi-region/package.json +4 -4
  28. package/node_modules/@aws-sdk/token-providers/package.json +6 -6
  29. package/node_modules/@aws-sdk/types/package.json +2 -2
  30. package/node_modules/@aws-sdk/xml-builder/package.json +2 -2
  31. package/node_modules/@clack/core/dist/index.mjs +7 -7
  32. package/node_modules/@clack/core/package.json +2 -1
  33. package/node_modules/@clack/prompts/dist/index.mjs +38 -38
  34. package/node_modules/@clack/prompts/package.json +2 -2
  35. package/node_modules/@nodable/entities/package.json +1 -1
  36. package/node_modules/@nodable/entities/src/EntityDecoder.js +1 -1
  37. package/node_modules/@nodable/entities/src/entities.js +0 -18
  38. package/node_modules/@slack/bolt/dist/App.js +8 -12
  39. package/node_modules/@slack/bolt/dist/context/index.js +7 -7
  40. package/node_modules/@slack/bolt/dist/index.js +16 -16
  41. package/node_modules/@slack/bolt/dist/receivers/AwsLambdaReceiver.js +2 -0
  42. package/node_modules/@slack/bolt/dist/receivers/ExpressReceiver.js +8 -1
  43. package/node_modules/@slack/bolt/dist/receivers/HTTPReceiver.js +4 -2
  44. package/node_modules/@slack/bolt/dist/receivers/SocketModeReceiver.js +1 -1
  45. package/node_modules/@slack/bolt/dist/receivers/verify-request.js +3 -0
  46. package/node_modules/@slack/bolt/dist/receivers/verify-signing-secret.js +12 -0
  47. package/node_modules/@slack/bolt/dist/types/actions/index.js +1 -1
  48. package/node_modules/@slack/bolt/dist/types/index.js +3 -3
  49. package/node_modules/@slack/bolt/package.json +4 -4
  50. package/node_modules/@smithy/core/dist-cjs/index.js +3 -4
  51. package/node_modules/@smithy/core/dist-cjs/submodules/client/index.js +3 -11
  52. package/node_modules/@smithy/core/dist-cjs/submodules/config/index.browser.js +2 -2
  53. package/node_modules/@smithy/core/dist-cjs/submodules/config/index.js +2 -2
  54. package/node_modules/@smithy/core/dist-cjs/submodules/config/index.native.js +2 -2
  55. package/node_modules/@smithy/core/dist-cjs/submodules/endpoints/index.browser.js +9 -40
  56. package/node_modules/@smithy/core/dist-cjs/submodules/endpoints/index.js +9 -40
  57. package/node_modules/@smithy/core/dist-cjs/submodules/protocols/index.js +12 -142
  58. package/node_modules/@smithy/core/dist-cjs/submodules/retry/index.browser.js +39 -22
  59. package/node_modules/@smithy/core/dist-cjs/submodules/retry/index.js +39 -22
  60. package/node_modules/@smithy/core/dist-cjs/submodules/schema/index.js +7 -9
  61. package/node_modules/@smithy/core/dist-cjs/submodules/serde/index.browser.js +2 -2
  62. package/node_modules/@smithy/core/dist-cjs/submodules/serde/index.js +2 -2
  63. package/node_modules/@smithy/core/dist-cjs/submodules/serde/index.native.js +2 -2
  64. package/node_modules/@smithy/core/dist-cjs/submodules/transport/index.js +184 -0
  65. package/node_modules/@smithy/core/dist-es/index.js +6 -6
  66. package/node_modules/@smithy/core/dist-es/submodules/client/index.js +2 -2
  67. package/node_modules/@smithy/core/dist-es/submodules/config/config-resolver/regionConfig/checkRegion.js +1 -1
  68. package/node_modules/@smithy/core/dist-es/submodules/endpoints/index.browser.js +2 -2
  69. package/node_modules/@smithy/core/dist-es/submodules/endpoints/index.js +2 -2
  70. package/node_modules/@smithy/core/dist-es/submodules/endpoints/middleware-endpoint/adaptors/toEndpointV1.js +1 -1
  71. package/node_modules/@smithy/core/dist-es/submodules/endpoints/middleware-endpoint/resolveEndpointConfig.js +1 -1
  72. package/node_modules/@smithy/core/dist-es/submodules/endpoints/util-endpoints/lib/index.js +1 -1
  73. package/node_modules/@smithy/core/dist-es/submodules/protocols/HttpBindingProtocol.js +1 -1
  74. package/node_modules/@smithy/core/dist-es/submodules/protocols/HttpProtocol.js +1 -2
  75. package/node_modules/@smithy/core/dist-es/submodules/protocols/RpcProtocol.js +1 -1
  76. package/node_modules/@smithy/core/dist-es/submodules/protocols/index.js +5 -5
  77. package/node_modules/@smithy/core/dist-es/submodules/protocols/middleware-content-length/contentLengthMiddleware.js +1 -1
  78. package/node_modules/@smithy/core/dist-es/submodules/protocols/requestBuilder.js +1 -1
  79. package/node_modules/@smithy/core/dist-es/submodules/retry/middleware-retry/configurations.js +19 -6
  80. package/node_modules/@smithy/core/dist-es/submodules/retry/middleware-retry/retryMiddleware.js +4 -5
  81. package/node_modules/@smithy/core/dist-es/submodules/retry/service-error-classification/constants.js +1 -1
  82. package/node_modules/@smithy/core/dist-es/submodules/retry/util-retry/ConfiguredRetryStrategy.js +4 -5
  83. package/node_modules/@smithy/core/dist-es/submodules/retry/util-retry/DefaultRetryToken.js +3 -0
  84. package/node_modules/@smithy/core/dist-es/submodules/retry/util-retry/StandardRetryStrategy.js +9 -5
  85. package/node_modules/@smithy/core/dist-es/submodules/schema/middleware/schemaDeserializationMiddleware.js +3 -4
  86. package/node_modules/@smithy/core/dist-es/submodules/schema/middleware/schemaSerializationMiddleware.js +1 -2
  87. package/node_modules/@smithy/core/dist-es/submodules/serde/middleware-serde/deserializerMiddleware.js +1 -1
  88. package/node_modules/@smithy/core/dist-es/submodules/transport/index.js +9 -0
  89. package/node_modules/@smithy/core/dist-es/submodules/{protocols/url-parser → transport}/parseUrl.js +1 -1
  90. package/node_modules/@smithy/core/dist-es/submodules/{endpoints → transport}/toEndpointV1.js +1 -1
  91. package/node_modules/@smithy/core/package.json +20 -11
  92. package/node_modules/@smithy/core/transport.js +5 -0
  93. package/node_modules/@smithy/credential-provider-imds/dist-cjs/index.js +14 -13
  94. package/node_modules/@smithy/credential-provider-imds/dist-es/fromContainerMetadata.js +14 -13
  95. package/node_modules/@smithy/credential-provider-imds/package.json +3 -3
  96. package/node_modules/@smithy/fetch-http-handler/package.json +4 -4
  97. package/node_modules/@smithy/node-http-handler/package.json +4 -4
  98. package/node_modules/@smithy/signature-v4/package.json +3 -3
  99. package/node_modules/@smithy/types/package.json +1 -1
  100. package/node_modules/eventsource-parser/dist/index.cjs +21 -10
  101. package/node_modules/eventsource-parser/dist/index.d.cts +33 -10
  102. package/node_modules/eventsource-parser/dist/index.js +21 -10
  103. package/node_modules/eventsource-parser/dist/stream.cjs +4 -3
  104. package/node_modules/eventsource-parser/dist/stream.d.cts +16 -3
  105. package/node_modules/eventsource-parser/dist/stream.js +4 -3
  106. package/node_modules/eventsource-parser/package.json +8 -8
  107. package/node_modules/hasown/package.json +4 -5
  108. package/node_modules/lru-cache/package.json +1 -1
  109. package/node_modules/protobufjs/dist/light/protobuf.js +7 -5
  110. package/node_modules/protobufjs/dist/light/protobuf.min.js +3 -3
  111. package/node_modules/protobufjs/dist/minimal/protobuf.js +3 -3
  112. package/node_modules/protobufjs/dist/minimal/protobuf.min.js +3 -3
  113. package/node_modules/protobufjs/dist/protobuf.js +7 -5
  114. package/node_modules/protobufjs/dist/protobuf.min.js +3 -3
  115. package/node_modules/protobufjs/package.json +1 -1
  116. package/node_modules/protobufjs/src/converter.js +4 -2
  117. package/node_modules/protobufjs/src/roots.js +1 -1
  118. package/node_modules/thread-stream/.claude/settings.local.json +15 -0
  119. package/node_modules/thread-stream/CLAUDE.md +64 -0
  120. package/node_modules/thread-stream/index.js +41 -13
  121. package/node_modules/thread-stream/lib/indexes.js +3 -1
  122. package/node_modules/thread-stream/lib/worker.js +20 -8
  123. package/node_modules/thread-stream/package.json +1 -1
  124. package/node_modules/undici/lib/global.js +10 -1
  125. package/node_modules/undici/package.json +1 -1
  126. package/package.json +1 -1
  127. package/skills/qqbrowser-playbook/SKILL.md +262 -234
  128. package/skills/qqbrowser-skill/SKILL.md +330 -234
  129. package/node_modules/@smithy/core/dist-cjs/getSmithyContext.js +0 -6
  130. package/node_modules/@smithy/core/dist-cjs/middleware-http-auth-scheme/getHttpAuthSchemeEndpointRuleSetPlugin.js +0 -21
  131. package/node_modules/@smithy/core/dist-cjs/middleware-http-auth-scheme/getHttpAuthSchemePlugin.js +0 -21
  132. package/node_modules/@smithy/core/dist-cjs/middleware-http-auth-scheme/httpAuthSchemeMiddleware.js +0 -46
  133. package/node_modules/@smithy/core/dist-cjs/middleware-http-auth-scheme/index.js +0 -6
  134. package/node_modules/@smithy/core/dist-cjs/middleware-http-auth-scheme/resolveAuthOptions.js +0 -24
  135. package/node_modules/@smithy/core/dist-cjs/middleware-http-signing/getHttpSigningMiddleware.js +0 -19
  136. package/node_modules/@smithy/core/dist-cjs/middleware-http-signing/httpSigningMiddleware.js +0 -27
  137. package/node_modules/@smithy/core/dist-cjs/middleware-http-signing/index.js +0 -5
  138. package/node_modules/@smithy/core/dist-cjs/normalizeProvider.js +0 -10
  139. package/node_modules/@smithy/core/dist-cjs/pagination/createPaginator.js +0 -44
  140. package/node_modules/@smithy/core/dist-cjs/request-builder/requestBuilder.js +0 -5
  141. package/node_modules/@smithy/core/dist-cjs/setFeature.js +0 -14
  142. package/node_modules/@smithy/core/dist-cjs/util-identity-and-auth/DefaultIdentityProviderConfig.js +0 -18
  143. package/node_modules/@smithy/core/dist-cjs/util-identity-and-auth/httpAuthSchemes/httpApiKeyAuth.js +0 -38
  144. package/node_modules/@smithy/core/dist-cjs/util-identity-and-auth/httpAuthSchemes/httpBearerAuth.js +0 -15
  145. package/node_modules/@smithy/core/dist-cjs/util-identity-and-auth/httpAuthSchemes/index.js +0 -6
  146. package/node_modules/@smithy/core/dist-cjs/util-identity-and-auth/httpAuthSchemes/noAuth.js +0 -9
  147. package/node_modules/@smithy/core/dist-cjs/util-identity-and-auth/index.js +0 -6
  148. package/node_modules/@smithy/core/dist-cjs/util-identity-and-auth/memoizeIdentityProvider.js +0 -61
  149. package/node_modules/@smithy/core/dist-es/request-builder/requestBuilder.js +0 -1
  150. package/node_modules/@smithy/core/dist-es/submodules/client/util-middleware/getSmithyContext.js +0 -2
  151. package/node_modules/@smithy/core/dist-es/submodules/event-streams/eventstream-codec/TestVectors.fixture.js +0 -146
  152. package/node_modules/@smithy/core/dist-es/submodules/event-streams/eventstream-codec/vectorTypes.fixture.js +0 -1
  153. package/node_modules/@smithy/credential-provider-imds/dist-es/remoteProvider/index.js +0 -2
  154. package/node_modules/@smithy/node-http-handler/dist-es/readable.mock.js +0 -21
  155. package/node_modules/@smithy/node-http-handler/dist-es/server.mock.js +0 -88
  156. package/node_modules/@smithy/node-http-handler/dist-es/stream-collector/readable.mock.js +0 -21
  157. package/node_modules/@smithy/signature-v4/dist-es/suite.fixture.js +0 -399
  158. /package/node_modules/@smithy/core/dist-es/{middleware-http-auth-scheme → legacy-root-exports/middleware-http-auth-scheme}/getHttpAuthSchemeEndpointRuleSetPlugin.js +0 -0
  159. /package/node_modules/@smithy/core/dist-es/{middleware-http-auth-scheme → legacy-root-exports/middleware-http-auth-scheme}/getHttpAuthSchemePlugin.js +0 -0
  160. /package/node_modules/@smithy/core/dist-es/{middleware-http-auth-scheme → legacy-root-exports/middleware-http-auth-scheme}/httpAuthSchemeMiddleware.js +0 -0
  161. /package/node_modules/@smithy/core/dist-es/{middleware-http-auth-scheme → legacy-root-exports/middleware-http-auth-scheme}/index.js +0 -0
  162. /package/node_modules/@smithy/core/dist-es/{middleware-http-auth-scheme → legacy-root-exports/middleware-http-auth-scheme}/resolveAuthOptions.js +0 -0
  163. /package/node_modules/@smithy/core/dist-es/{middleware-http-signing → legacy-root-exports/middleware-http-signing}/getHttpSigningMiddleware.js +0 -0
  164. /package/node_modules/@smithy/core/dist-es/{middleware-http-signing → legacy-root-exports/middleware-http-signing}/httpSigningMiddleware.js +0 -0
  165. /package/node_modules/@smithy/core/dist-es/{middleware-http-signing → legacy-root-exports/middleware-http-signing}/index.js +0 -0
  166. /package/node_modules/@smithy/core/dist-es/{pagination → legacy-root-exports/pagination}/createPaginator.js +0 -0
  167. /package/node_modules/@smithy/core/dist-es/{util-identity-and-auth → legacy-root-exports/util-identity-and-auth}/DefaultIdentityProviderConfig.js +0 -0
  168. /package/node_modules/@smithy/core/dist-es/{util-identity-and-auth → legacy-root-exports/util-identity-and-auth}/httpAuthSchemes/httpApiKeyAuth.js +0 -0
  169. /package/node_modules/@smithy/core/dist-es/{util-identity-and-auth → legacy-root-exports/util-identity-and-auth}/httpAuthSchemes/httpBearerAuth.js +0 -0
  170. /package/node_modules/@smithy/core/dist-es/{util-identity-and-auth → legacy-root-exports/util-identity-and-auth}/httpAuthSchemes/index.js +0 -0
  171. /package/node_modules/@smithy/core/dist-es/{util-identity-and-auth → legacy-root-exports/util-identity-and-auth}/httpAuthSchemes/noAuth.js +0 -0
  172. /package/node_modules/@smithy/core/dist-es/{util-identity-and-auth → legacy-root-exports/util-identity-and-auth}/index.js +0 -0
  173. /package/node_modules/@smithy/core/dist-es/{util-identity-and-auth → legacy-root-exports/util-identity-and-auth}/memoizeIdentityProvider.js +0 -0
  174. /package/node_modules/@smithy/core/dist-es/{getSmithyContext.js → submodules/transport/getSmithyContext.js} +0 -0
  175. /package/node_modules/@smithy/core/dist-es/submodules/{protocols/protocol-http → transport}/httpRequest.js +0 -0
  176. /package/node_modules/@smithy/core/dist-es/submodules/{protocols/protocol-http → transport}/httpResponse.js +0 -0
  177. /package/node_modules/@smithy/core/dist-es/submodules/{endpoints/util-endpoints/lib → transport}/isValidHostLabel.js +0 -0
  178. /package/node_modules/@smithy/core/dist-es/submodules/{protocols/protocol-http → transport}/isValidHostname.js +0 -0
  179. /package/node_modules/@smithy/core/dist-es/submodules/{client/util-middleware → transport}/normalizeProvider.js +0 -0
  180. /package/node_modules/@smithy/core/dist-es/submodules/{protocols/querystring-parser → transport}/parseQueryString.js +0 -0
@@ -10,293 +10,321 @@ permissions:
10
10
 
11
11
  # qqbrowser-playbook
12
12
 
13
- A skill for managing browser automation playbooks. It works alongside `qqbrowser-skill` to convert raw task recordings into reusable, parameterized replay scripts.
13
+ Convert raw `qqbrowser-skill` task recordings into reusable, parameterized replay scripts.
14
+
15
+ ## Critical Rules
16
+
17
+ 1. A playbook **MUST** be generated from actual task recording (`task_latest`). NEVER fabricate steps.
18
+ 2. Only use actions from `task_latest` — copy action names and index values **verbatim**.
19
+ 3. Only replace **user data** (`text`, `url`, file path) with `{{param_name}}`. NEVER replace `action` or `index`.
20
+ 4. Save to `~/.qqbrowser-skill/playbooks/<kebab-case-name>.json`. Create dir first: `mkdir -p ~/.qqbrowser-skill/playbooks`.
21
+ 5. JSON structure: `version` + `metadata` + `params` + `settings` + `steps`. Key is `params` not `parameters`.
22
+
23
+ ## Valid Actions
24
+
25
+ ### ✅ Allowed (replayable)
26
+
27
+ | Action | Parameterizable Fields |
28
+ |--------|----------------------|
29
+ | `browser_go_to_url` | `url` |
30
+ | `browser_click_element` / `browser_dblclick_element` / `browser_focus_element` | — |
31
+ | `browser_input_text` / `browser_keyboard_op` | `text` |
32
+ | `browser_keypress` / `browser_keydown` / `browser_keyup` | — |
33
+ | `browser_select_dropdown_option` | `text` |
34
+ | `browser_check_op` | — |
35
+ | `browser_scroll_down` / `browser_scroll_up` / `browser_scroll_to_top` / `browser_scroll_to_bottom` / `browser_scroll_by` / `browser_scroll_into_view` | — |
36
+ | `browser_scroll_to_text` | `text` |
37
+ | `browser_wait` | — |
38
+ | `browser_go_back` | — |
39
+ | `browser_dialog` | `text` |
40
+ | `browser_tab_open` | `url`, `as` (alias). Use semantic refs, NOT physical `tabId` |
41
+ | `browser_tab_switch` / `browser_tab_close` | `tab` (`"origin"` / `"current"` / `"latest"` / custom alias) |
42
+ | `browser_download_file` / `browser_download_url` | — |
43
+ | `browser_eval_content_js` | `script` |
44
+ | `browser_find_and_act` | `value`, `actionValue`, `name`, `nth` |
45
+ | `loop` | `count`, nested `steps` |
46
+
47
+ ### ❌ Excluded (filter out from recording)
48
+
49
+ `browser_snapshot`, `browser_screenshot`, `browser_get_info`, `browser_check_state`, `browser_get_dropdown_options`, `browser_tab_list`, `task_begin`/`task_end`/`task_latest`, `browser_done`
50
+
51
+ ## Playbook JSON Format
14
52
 
15
- ## Overview
16
-
17
- After you complete a browser automation task using `qqbrowser-skill` (wrapped in `task_begin`/`task_end`), use this skill to:
18
-
19
- 1. **Retrieve** the latest task recording
20
- 2. **Analyze** the steps and identify values that should be parameterized
21
- 3. **Generate** a playbook file (parameterized replay script)
22
- 4. **List** all available playbooks
23
- 5. **Run** a playbook with specified parameters
24
-
25
- **CRITICAL RULES:**
26
- - A playbook MUST be generated from an actual task recording (`task_latest`). You MUST NOT invent or fabricate steps.
27
- - You MUST only use actions that appear in the `task_latest` output. These are the only valid actions for the playbook.
28
- - The generated playbook steps must be a direct transformation of the recorded steps — only replacing literal user data with `{{param_name}}` placeholders.
29
- - Element identification MUST use the `index` field from the recording (e.g., `"index": "154_17q6_1w8c"`). Do NOT substitute indices with arbitrary values.
30
- - If no recording exists (`task_latest` returns empty), you MUST first complete the task using `qqbrowser-skill` with `task_begin`/`task_end`, then generate the playbook from the recording.
31
- - **FILE PATH**: Playbook files MUST be saved to `~/.qqbrowser-skill/playbooks/<name>.json` on macOS/Linux. Do NOT save to any other directory such as `~/.qbotclaw/`, project directories, or temp directories.
32
- - **FILE FORMAT**: The JSON file MUST follow the exact nested structure defined below (with `version`, `metadata`, `params`, `settings`, `steps` as top-level keys). Do NOT use a flat/simplified format. Do NOT use `parameters` instead of `params`. Do NOT omit `metadata` or `settings`.
33
- - **USE write_to_file**: When saving the playbook JSON, use the `write_to_file` tool with the full absolute path (e.g., `/Users/<username>/.qqbrowser-skill/playbooks/xiaohongshu-publish-note.json`). Ensure the directory exists first (create with `mkdir -p`).
34
-
35
- ## Playbook Storage
36
-
37
- Playbooks are stored in the `playbooks` subdirectory under the qqbrowser-skill data directory (`DEFAULT_DATA_DIR/playbooks/`). The data directory follows the same convention as `qqbrowser-skill`:
53
+ ```json
54
+ {
55
+ "version": "1.0",
56
+ "metadata": {
57
+ "name": "小红书发布笔记",
58
+ "description": "自动打开小红书并发布一篇笔记",
59
+ "created_at": "2026-05-21T16:59:00Z",
60
+ "url": "https://www.xiaohongshu.com/explore",
61
+ "requires_ai": false
62
+ },
63
+ "params": {
64
+ "notes_title": { "description": "笔记标题", "required": true },
65
+ "notes_content": { "description": "笔记正文内容", "required": true }
66
+ },
67
+ "settings": { "default_delay_ms": 1000, "default_retry_count": 1, "step_timeout_ms": 30000 },
68
+ "steps": [
69
+ { "action": "browser_go_to_url", "params": { "url": "https://www.xiaohongshu.com/explore" }, "description": "打开小红书探索页" },
70
+ { "action": "browser_input_text", "params": { "index": "6_2vl4_txwg", "text": "{{notes_title}}" }, "description": "输入笔记标题" },
71
+ { "action": "browser_click_element", "params": { "index": "19_oxgq_ttmr" }, "description": "点击发布" }
72
+ ]
73
+ }
74
+ ```
38
75
 
39
- | Platform | Playbooks Directory |
40
- |----------|-------------------|
41
- | Linux / macOS | `~/.qqbrowser-skill/playbooks/` |
42
- | Windows | `%LOCALAPPDATA%/qqbrowser-skill/playbooks/` |
76
+ **Fields**: `version`(always "1.0"), `metadata.name`, `metadata.description`, `metadata.created_at`(ISO8601) are required. `metadata.url`, `metadata.requires_ai`(default false) are optional. Each param needs `description`(string) and `required`(bool), optional `default`. Steps need `action`, `params`, `description`; optional `condition: {"param":"xx"}` for conditional execution.
43
77
 
44
- **To determine the playbooks directory at runtime:**
78
+ ## Parameter Extraction
45
79
 
46
- ```bash
47
- # Linux / macOS
48
- PLAYBOOKS_DIR="$HOME/.qqbrowser-skill/playbooks"
80
+ **Parameterize**: user text, emails, usernames, quantities, file paths, user-specific URL segments.
81
+ **Never parameterize**: `index`, `action`, fixed workflow URLs, fixed UI buttons, `settings`.
82
+ **Naming**: `snake_case`, descriptive (`notes_title` not `title`).
49
83
 
50
- # Windows (PowerShell)
51
- $PLAYBOOKS_DIR = "$env:LOCALAPPDATA/qqbrowser-skill/playbooks"
52
- ```
84
+ ## Conditional Steps
53
85
 
54
- File naming convention: `<name>.json` (name derived from metadata, kebab-case, e.g. `xiaohongshu-publish-note.json`)
86
+ Add `condition: {"param": "cc"}` to ALL steps dependent on an optional param. If param absent, step is skipped.
55
87
 
56
- ## Workflow
88
+ ## Loop Steps
57
89
 
58
- ### Step 1: Get the Latest Task Recording
90
+ Use `loop` when recording shows **2+ repetitions** of the same action sequence differing only in target element, and count is user-controlled.
59
91
 
60
- ```bash
61
- qqbrowser-skill task_latest
62
- ```
92
+ **Do NOT use loop** for fixed form fields or when a single `browser_eval_content_js` can extract all data at once.
63
93
 
64
- This returns a JSON object with the raw recorded steps. Example output:
94
+ ### Loop JSON
65
95
 
66
96
  ```json
67
97
  {
68
- "task_id": "task_20260521_165900",
69
- "description": "自动打开小红书并发布一篇笔记",
70
- "commands": [
71
- {"skill_name": "browser_go_to_url", "params": {"url": "https://www.xiaohongshu.com/explore"}},
72
- {"skill_name": "browser_click_element", "params": {"index": "154_17q6_1w8c"}},
73
- {"skill_name": "browser_click_element", "params": {"index": "7_ek3h_2b3r"}},
74
- {"skill_name": "browser_click_element", "params": {"index": "10_4bq5_u01g"}},
75
- {"skill_name": "browser_input_text", "params": {"index": "6_2vl4_txwg", "text": "我的笔记标题"}},
76
- {"skill_name": "browser_input_text", "params": {"index": "4_ku44_rrpt", "text": "这是笔记的正文内容..."}},
77
- {"skill_name": "browser_click_element", "params": {"index": "19_oxgq_ttmr"}}
78
- ],
79
- "started_at": "2026-05-21T16:59:00Z",
80
- "ended_at": "2026-05-21T17:01:30Z"
98
+ "action": "loop",
99
+ "params": { "count": "{{count}}", "variable": "i", "start": 1 },
100
+ "description": "逐个访问前N篇文章",
101
+ "steps": [ ... ]
81
102
  }
82
103
  ```
83
104
 
84
- ### Step 2: AI Analyzes and Generates the Playbook
105
+ Fields: `count`(string/number, supports `{{param}}`), `variable`(loop var name), `start`(default 1, 1-based for `nth`).
85
106
 
86
- Based on the task recording, you (the AI) must:
107
+ ### Key Rules for Loop Steps
87
108
 
88
- 1. **Keep all actions and indices exactly as recorded**do NOT change `action` names or `index` values
89
- 2. **Identify parameterizable values** — values that a user would likely want to change on each run:
90
- - Text inputs (`browser_input_text` the `text` field)
91
- - URLs (`browser_go_to_url` the `url` field, only if the URL varies per use)
92
- - File paths in upload actions
93
- - Any user-specific data (names, emails, content, etc.)
94
- 3. **Define parameters** with descriptive names, descriptions, and whether they are required
95
- 4. **Replace ONLY literal values** with `{{param_name}}` placeholders in the steps (only the `text`, `url`, or file path fields NEVER replace `action` or `index`)
96
- 5. **Add meaningful descriptions** to each step
97
- 6. **Set appropriate metadata** (name, description, url)
109
+ 1. **Use `browser_find_and_act` with `by: "css"` + `nth: "{{i}}"`** inside loops — NOT `browser_click_element` with hardcoded index.
110
+ 2. **`by` MUST be `"css"` when `nth` is used** — NEVER `by: "text"` or `by: "label"` with `nth`. Text/label is for single unique elements, not list iteration.
111
+ 3. **`value` must be a list-level CSS selector** matching ALL items (e.g., `.ContentItem h2 a`), NOT specific text content.
112
+ 4. **CSS selector MUST come from actual DOM**, with this priority:
113
+ - **Best**: Extract from `__list_probe__` in recording (see Step 2b)
114
+ - **Good**: Use `by: "css"` selector already in recording's `find_and_act`
115
+ - **Fallback**: Use `browser_get_info --type list_selector --index <id>` to auto-detect, or run `browser_eval_content_js` probe to discover (before generating playbook)
116
+ - **NEVER**: Fabricate or guess. If all above fail, set `metadata.requires_ai: true`.
117
+ 5. **`{{i}}` is 1-based** (start=1), aligning with `nth` (nth=1 = first element).
118
+ 6. **Check recording for tab operations in each iteration** — if recording has `tab_switch`/`tab_close`, loop body MUST use `tab_switch("latest")` ... → `tab_close("current")` → `tab_switch("origin")`. **NEVER silently convert tab-based recording to `go_back`.**
119
+ 7. **Use `browser_go_back` ONLY** if iteration stays in the **same tab** (no tab ops in recording).
120
+ 8. **Include `browser_wait`** between iterations for stability.
98
121
 
99
- **IMPORTANT**: The transformation from recording to playbook is purely:
100
- - Recording `"text": "我的搜索词"` → Playbook `"text": "{{search_keyword}}"`
101
- - Everything else (action names, index values, key names) stays **exactly** the same.
122
+ ### Decision Tables
102
123
 
103
- ### Step 3: Write the Playbook File
124
+ **Loop vs Single JS Extraction:**
104
125
 
105
- Generate a JSON file conforming to this exact format and save it to the playbooks directory:
126
+ | Scenario | Method |
127
+ |----------|--------|
128
+ | List page data only | Single `browser_eval_content_js` |
129
+ | Need detail page content | `loop` (navigate in/out per item) |
130
+ | Pagination + extract | `loop` |
131
+ | Fixed different form fields | Linear steps |
106
132
 
107
- ```json
108
- {
109
- "version": "1.0",
110
- "metadata": {
111
- "name": "小红书发布长文笔记",
112
- "description": "自动打开小红书,发布一篇长文笔记并暂存离开",
113
- "created_at": "2026-05-21T16:59:00Z",
114
- "url": "https://www.xiaohongshu.com/explore"
115
- },
116
- "params": {
117
- "notes_title": {
118
- "description": "笔记标题",
119
- "required": true
120
- },
121
- "notes_content": {
122
- "description": "笔记正文内容",
123
- "required": true
124
- }
125
- },
126
- "settings": {
127
- "default_delay_ms": 1000,
128
- "default_retry_count": 1,
129
- "step_timeout_ms": 30000
130
- },
131
- "steps": [
132
- {
133
- "action": "browser_go_to_url",
134
- "params": { "url": "https://www.xiaohongshu.com/explore" },
135
- "description": "打开小红书探索页"
136
- },
137
- {
138
- "action": "browser_click_element",
139
- "params": { "index": "154_17q6_1w8c" },
140
- "description": "点击发布按钮"
141
- },
142
- {
143
- "action": "browser_click_element",
144
- "params": { "index": "7_ek3h_2b3r" },
145
- "description": "选择写长文"
146
- },
147
- {
148
- "action": "browser_click_element",
149
- "params": { "index": "10_4bq5_u01g" },
150
- "description": "点击新的创作"
151
- },
152
- {
153
- "action": "browser_input_text",
154
- "params": { "index": "6_2vl4_txwg", "text": "{{notes_title}}" },
155
- "description": "输入笔记标题"
156
- },
157
- {
158
- "action": "browser_input_text",
159
- "params": { "index": "4_ku44_rrpt", "text": "{{notes_content}}" },
160
- "description": "输入笔记正文"
161
- },
162
- {
163
- "action": "browser_click_element",
164
- "params": { "index": "19_oxgq_ttmr" },
165
- "description": "点击暂存离开"
166
- }
167
- ]
168
- }
169
- ```
133
+ **`go_back` vs `tab_close` + `tab_switch`:**
170
134
 
171
- ## Playbook File Format Specification
135
+ | Scenario | Method |
136
+ |----------|--------|
137
+ | Link opens in **same tab** | `browser_go_back` |
138
+ | Link opens in **new tab** | `browser_tab_close` + `browser_tab_switch` |
139
+ | Unsure | Prefer `tab_close` + `tab_switch` (safer) |
172
140
 
173
- | Field | Type | Required | Description |
174
- |-------|------|----------|-------------|
175
- | `version` | string | Yes | Always `"1.0"` |
176
- | `metadata.name` | string | Yes | Human-readable name of the playbook |
177
- | `metadata.description` | string | Yes | What this playbook does |
178
- | `metadata.created_at` | string | Yes | ISO 8601 timestamp of creation |
179
- | `metadata.url` | string | No | The starting URL of the automation |
180
- | `params` | object | No | Parameter definitions (omit if no parameters needed) |
181
- | `params.<key>.description` | string | Yes | What this parameter is for |
182
- | `params.<key>.required` | boolean | Yes | Whether the parameter must be provided |
183
- | `params.<key>.default` | string | No | Default value if not provided |
184
- | `settings.default_delay_ms` | number | Yes | Delay between steps in milliseconds |
185
- | `settings.default_retry_count` | number | Yes | Number of retries on step failure |
186
- | `settings.step_timeout_ms` | number | Yes | Timeout for each step |
187
- | `steps[].action` | string | Yes | The browser action (must have `browser_` prefix) |
188
- | `steps[].params` | object | Yes | Parameters for the action |
189
- | `steps[].description` | string | Yes | Human-readable step description |
141
+ ## Tab Management
190
142
 
191
- ### Parameter Placeholder Syntax
143
+ Playbooks **MUST use semantic tab references** — physical `tabId` changes every session.
192
144
 
193
- Use `{{param_name}}` in step params to reference a defined parameter. The param_name must match a key in the top-level `params` object.
145
+ | Reference | Meaning |
146
+ |-----------|---------|
147
+ | `"origin"` | Tab active when replay started (always available) |
148
+ | `"current"` | Currently active tab (dynamic) |
149
+ | `"latest"` | Most recently opened tab (after any `tab_open`) |
150
+ | Custom alias | Named via `"as"` param in `tab_open` |
194
151
 
195
- ## Commands
152
+ **Tab action examples:**
153
+ ```json
154
+ { "action": "browser_tab_open", "params": { "url": "https://example.com", "as": "detail" } }
155
+ { "action": "browser_tab_switch", "params": { "tab": "origin" } }
156
+ { "action": "browser_tab_close", "params": { "tab": "current" } }
157
+ ```
196
158
 
197
- ### Generate a Playbook
159
+ **Rules:**
160
+ 1. NEVER use physical `tabId` — always semantic references.
161
+ 2. `browser_go_back` does NOT work across tabs — use `tab_close` + `tab_switch`.
162
+ 3. After `tab_close`, explicitly `tab_switch` — don't assume Chrome's active tab.
198
163
 
199
- After retrieving task_latest output, write the generated playbook JSON to the playbooks directory:
164
+ ## Workflow
200
165
 
201
166
  ```bash
202
- # 1. Get the latest recording
167
+ # 1. Get recording
203
168
  qqbrowser-skill task_latest
204
169
 
205
- # 2. AI analyzes the output and generates the playbook content (this is done by you, the AI)
170
+ # 2. AI analyzes generates playbook:
171
+ # 2a: Filter excluded actions (snapshot, screenshot, tab_list, etc.)
172
+ # 2b: Extract CSS selector from __list_probe__ (see below)
173
+ # 2c: Detect loop patterns (repeated sequences → loop, using selector from 2b)
174
+ # 2d: Detect tab patterns (tabId → semantic refs). NEVER silently drop tab ops.
175
+ # 2e: Cross-check loop+tab — if loop iteration has tab ops, use tab_switch/close NOT go_back
176
+ # 2f: Identify parameterizable values → {{param}}
177
+ # 2g: Add descriptions, metadata, settings
178
+
179
+ # 3. Save
180
+ mkdir -p ~/.qqbrowser-skill/playbooks
181
+ # write_to_file tool
182
+
183
+ # 4. Run
184
+ # ⚠️ browser_replay can take UP TO 10 MINUTES for complex playbooks with loops.
185
+ # You MUST wait patiently for the command to finish. Do NOT assume it has timed out.
186
+ qqbrowser-skill browser_replay --script ~/.qqbrowser-skill/playbooks/<name>.json \
187
+ --variables '{"param1": "value1"}'
188
+ ```
189
+
190
+ > **⚠️ IMPORTANT**: `browser_replay` executes all playbook steps sequentially in the browser. Depending on step count, delays, and network conditions, **execution can take up to 10 minutes**. Wait for the command to return a result before proceeding — do NOT interrupt or retry prematurely.
206
191
 
207
- # 3. Ensure the playbooks directory exists and write the file
208
- # Linux / macOS:
209
- mkdir -p "$HOME/.qqbrowser-skill/playbooks"
210
- # Windows (PowerShell):
211
- New-Item -ItemType Directory -Force -Path "$env:LOCALAPPDATA/qqbrowser-skill/playbooks"
192
+ ### Step 2b: Extract CSS Selector from Recording
212
193
 
213
- # 4. Save the JSON file as <kebab-case-name>.json in the playbooks directory
194
+ **Primary source**: Look for `browser_eval_content_js` calls whose output contains `__list_probe__: true`.
195
+ ```
196
+ Recording data example:
197
+ browser_eval_content_js → output: {"__list_probe__": true, "selector": ".SearchResult-Card .ContentItem h2 a", "count": 10, "samples": ["文章A", "文章B", "文章C"]}
214
198
  ```
199
+ Extract the `selector` value — this is the **verified CSS selector** from actual DOM, use it directly in the loop's `find_and_act`.
215
200
 
216
- ### List Available Playbooks
201
+ **Fallback** (if no `__list_probe__` in recording):
202
+ 1. Check if recording's `find_and_act` already uses `by: "css"` — use that selector directly
203
+ 2. If recording only has `by: "text"` or `click_element(index)`, the selector is unknown
204
+ 3. Use `browser_get_info --type list_selector --index <any_list_item_index>` to auto-detect the selector
205
+ 4. If `list_selector` also fails, run `browser_eval_content_js` with a probe script to discover the selector
206
+ 5. **NEVER** guess or fabricate selectors
217
207
 
218
- ```bash
219
- # Linux / macOS
220
- ls ~/.qqbrowser-skill/playbooks/
208
+ ### Step 2c: Loop Pattern Detection
221
209
 
222
- # Windows (PowerShell)
223
- Get-ChildItem "$env:LOCALAPPDATA/qqbrowser-skill/playbooks"
210
+ ```
211
+ 1. Group consecutive steps into sequences
212
+ 2. If 2+ adjacent sequences share the SAME action pattern → convert to `loop`
213
+ 3. What varies between iterations:
214
+ - `nth` increments → use `{{i}}`
215
+ - Different index values → replace with `find_and_act` + `nth: "{{i}}"`
216
+ - Different text/title in `find_and_act(by="text")` → convert to `by: "css"` + `nth: "{{i}}"`
217
+ 4. Use the CSS selector from Step 2b (from __list_probe__ or fallback probe)
218
+ 5. Convert `browser_click_element(index=...)` → `find_and_act(by="css", nth="{{i}}")`
219
+ 6. Count source: user-mentioned quantity → `{{count}}` param; fixed → literal
224
220
  ```
225
221
 
226
- To show details of a specific playbook:
222
+ **Recording patterns playbook conversion:**
227
223
 
228
- ```bash
229
- cat "<playbooks_dir>/<name>.json"
224
+ | Recording Pattern | Conversion |
225
+ |---|---|
226
+ | Repeated sequences with different text targets | `loop` + `by: "css"` + `nth: "{{i}}"` |
227
+ | Repeated sequences with different `index` values | `loop` + `by: "css"` + `nth: "{{i}}"` |
228
+ | `find_and_act(by="text", value="标题A")` → ... → `find_and_act(by="text", value="标题B")` | **MUST** convert `by: "text"` → `by: "css"` + `nth: "{{i}}"` |
229
+
230
+ ### Step 2d: Tab Pattern Detection
231
+
232
+ ```
233
+ 1. `browser_tab_open` → discard physical tabId, add semantic `"as"` alias
234
+ 2. `browser_tab_switch(tabId=N)` → replace with "origin"/"latest"/custom alias
235
+ 3. `browser_tab_close(tabId=N)` → replace with semantic ref
236
+ 4. `browser_go_back` after new tab → replace with tab_close("current") + tab_switch("origin")
230
237
  ```
231
238
 
232
- ### Run a Playbook
239
+ ## Complete Example: Loop + New Tab
233
240
 
234
- Use `qqbrowser-skill browser_replay` to execute a playbook:
241
+ This example demonstrates ALL key conversions (text→css, tabId→semantic, linear→loop):
235
242
 
236
- ```bash
237
- # Run without parameters (all params must have defaults or be optional)
238
- qqbrowser-skill browser_replay --script "<playbooks_dir>/<name>.json"
243
+ Recording (raw):
244
+ ```
245
+ eval_content_js {"__list_probe__": true, "selector": ".ContentItem h2 a", "count": 10, "samples": ["文章标题A","文章标题B","文章标题C"]}
246
+ find_and_act(by="text", value="文章标题A", action="click")
247
+ click_element(index="197_3i4c_uv7n") ← opened new tab
248
+ tab_switch(tabId=1412776911)
249
+ eval_content_js(script="...")
250
+ tab_close(tabId=1412776911)
251
+ find_and_act(by="text", value="文章标题B", action="click")
252
+ click_element(index="206_90r2_rddo")
253
+ tab_switch(tabId=1412776913)
254
+ eval_content_js(script="...")
255
+ tab_close(tabId=1412776913)
256
+ ```
239
257
 
240
- # Run with parameters
241
- qqbrowser-skill browser_replay --script "<playbooks_dir>/<name>.json" --variables '{"notes_title": "今日分享", "notes_content": "这是内容..."}'
258
+ Playbook generator extracts selector `.ContentItem h2 a` from the `__list_probe__` line.
259
+
260
+ Playbook (generated):
261
+ ```json
262
+ {
263
+ "version": "1.0",
264
+ "metadata": {
265
+ "name": "搜索并提取文章(新Tab场景)",
266
+ "description": "搜索主题,逐个在新Tab打开文章提取内容",
267
+ "created_at": "2026-06-02T15:00:00Z",
268
+ "requires_ai": false
269
+ },
270
+ "params": {
271
+ "topic": { "description": "搜索关键词", "required": true },
272
+ "count": { "description": "提取数量", "required": false, "default": "3" }
273
+ },
274
+ "settings": { "default_delay_ms": 2000, "default_retry_count": 1, "step_timeout_ms": 30000 },
275
+ "steps": [
276
+ { "action": "browser_go_to_url", "params": { "url": "https://example.com/search?q={{topic}}" }, "description": "搜索主题" },
277
+ { "action": "browser_wait", "params": { "seconds": 3 }, "description": "等待搜索结果" },
278
+ {
279
+ "action": "loop",
280
+ "params": { "count": "{{count}}", "variable": "i", "start": 1 },
281
+ "description": "逐个在新Tab打开文章并提取",
282
+ "steps": [
283
+ { "action": "browser_find_and_act", "params": { "by": "css", "value": ".ContentItem h2 a", "nth": "{{i}}", "action": "click" }, "description": "点击第{{i}}篇文章" },
284
+ { "action": "browser_tab_switch", "params": { "tab": "latest" }, "description": "切到文章Tab" },
285
+ { "action": "browser_wait", "params": { "seconds": 2 }, "description": "等待加载" },
286
+ { "action": "browser_eval_content_js", "params": { "script": "JSON.stringify({title:document.querySelector('h1')?.textContent?.trim(),content:document.querySelector('.RichText')?.textContent?.substring(0,2000)})" }, "description": "提取内容" },
287
+ { "action": "browser_tab_close", "params": { "tab": "current" }, "description": "关闭文章Tab" },
288
+ { "action": "browser_tab_switch", "params": { "tab": "origin" }, "description": "切回列表页" },
289
+ { "action": "browser_wait", "params": { "seconds": 1 }, "description": "等待恢复" }
290
+ ]
291
+ }
292
+ ]
293
+ }
242
294
  ```
243
295
 
244
- ## Parameter Extraction Guidelines
296
+ **Key conversions:** `by:"text"`+title → `by:"css"`+`nth:"{{i}}"` | `click_element(index)` → merged into `find_and_act` | `tabId:N` → `"latest"`/`"current"`/`"origin"` | 2× linear → `loop`
245
297
 
246
- When analyzing task recordings to identify parameters, follow these rules:
298
+ ## `requires_ai` Tasks
247
299
 
248
- ### SHOULD be parameterized:
249
- - **User-generated text content**: titles, messages, descriptions, comments, posts
250
- - **User-specific data**: usernames, email addresses, phone numbers, passwords
251
- - **Search queries**: any text entered into search boxes
252
- - **File paths**: paths to files being uploaded
253
- - **Variable URLs**: URLs that contain user-specific identifiers or change per use
300
+ Set `metadata.requires_ai: true` when recording contains AI-dependent steps (summarization, comparison, conditional decisions). Include only replayable portions as steps.
254
301
 
255
- ### Should NOT be parameterized:
256
- - **Fixed navigation URLs**: the starting URL of the workflow (e.g., `https://www.xiaohongshu.com/explore`)
257
- - **Element indices**: the `index` field in click/input actions (these are structural, not data)
258
- - **UI interaction steps**: clicks on fixed buttons like "Submit", "Next", "Save"
259
- - **Fixed option selections**: dropdown choices that are always the same for this workflow
302
+ ## `browser_find_and_act` Usage
260
303
 
261
- ### Naming Conventions for Parameters:
262
- - Use `snake_case` for parameter names
263
- - Be descriptive: `notes_title` not `title`, `search_query` not `q`
264
- - Group related params with common prefix: `login_username`, `login_password`
304
+ For single-element targeting (NOT in loops): `by: "text"` or `by: "label"` with known stable text.
305
+ For list iteration (in loops): `by: "css"` + `nth: "{{i}}"` with list-level selector.
265
306
 
266
- ## Example: Full Workflow
307
+ ## `browser_eval_content_js` Usage
267
308
 
268
- ```
269
- User: "帮我生成一个回放脚本"
270
-
271
- AI steps:
272
- 1. Run: qqbrowser-skill task_latest
273
- 2. Analyze the returned commands
274
- 3. Identify that browser_input_text steps contain user data that should be parameterized
275
- 4. Generate the playbook JSON with params and {{placeholders}}
276
- 5. Determine the correct playbooks directory for the current platform
277
- 6. Ensure the playbooks directory exists (mkdir -p)
278
- 7. Write the JSON file to <playbooks_dir>/<name>.json
279
- 8. Report to the user: playbook name, parameters defined, file path, and how to run it
280
- ```
309
+ Primary data extraction method. Supports `{{param}}` in script for selectors, counts, keywords.
310
+ Selector priority: `id` > `data-*` > ARIA > semantic class > structural path. NEVER use dynamic/hashed classes.
311
+
312
+ ## Common Mistakes
281
313
 
282
- ## Notes
283
-
284
- - This skill does NOT require any additional installationit uses `qqbrowser-skill` commands and file system operations
285
- - The AI is responsible for the analysis and generation logic — no separate binary or service is needed
286
- - Playbook files are plain JSON and can be manually edited if needed
287
- - Always ensure the playbooks directory exists before writing
288
- - The playbooks directory follows the same platform convention as `qqbrowser-skill` data directory (`~/.qqbrowser-skill/` on Linux/macOS, `%LOCALAPPDATA%/qqbrowser-skill/` on Windows)
289
-
290
- ## Common Mistakes to Avoid
291
-
292
- | Mistake | Why it's wrong | Correct approach |
293
- |---------|---------------|-----------------|
294
- | Generating steps without running `task_latest` first | Playbooks must be based on actual recordings | Always run `task_latest` first, then transform |
295
- | Using actions not in `task_latest` output | Only actions from the recording are valid | Copy actions exactly from `task_latest` output |
296
- | Changing the `action` field from what was recorded | Actions must match the recording exactly | Copy action names verbatim |
297
- | Replacing `index` values with something else | Index is a structural reference, not user data | Never parameterize index values |
298
- | Using `{{param}}` in fields other than user data | Only user-provided content should be parameterized | Only replace `text`, `url`, or file path values |
299
- | Saving to wrong directory (e.g. `~/.qbotclaw/workspace/`) | Playbooks have a designated storage path | Always save to `~/.qqbrowser-skill/playbooks/` |
300
- | Using flat JSON format (top-level `name`, `parameters`, `steps`) | The schema requires nested structure | Must use `metadata.name`, `params`, `settings`, `steps` structure |
301
- | Using `parameters` key instead of `params` | The schema key is `params` | Use `params` with `description` and `required` fields |
302
- | Using `write content=` shell command to save | May corrupt JSON formatting | Use `write_to_file` tool with proper path |
314
+ | Mistake | Fix |
315
+ |---------|-----|
316
+ | Including `browser_snapshot` etc. in steps | Filter outAI-decision only |
317
+ | Fabricating steps without `task_latest` | Always base on actual recording |
318
+ | Parameterizing `index` or `action` | Only parameterize value fields (`text`, `url`, `script`) |
319
+ | Using `parameters` instead of `params` | Schema key is `params` |
320
+ | Expanding N repetitions as linear steps | Use `loop` with `{{count}}` |
321
+ | Using `click_element` + hardcoded index in loops | Use `find_and_act` + `nth: "{{i}}"` |
322
+ | Using `by: "text"`/`"label"` with `nth` in loops | `nth` requires `by: "css"` |
323
+ | Keeping `by: "text"` + specific content in loops | Recording's `find_and_act(text, "标题")` MUST convert to `by: "css"` + `nth` in loop |
324
+ | Fabricating CSS selectors | Must come from `__list_probe__` in recording, or existing `find_and_act(css)`, or a live probe — never guess |
325
+ | Using search keyword as `value` in loop | `value` is CSS selector (`.ContentItem h2 a`), not the keyword |
326
+ | `find_and_act(text)` + `click_element(index)` for same target | Merge into single `find_and_act(css, nth)` in loop |
327
+ | Hardcoding physical `tabId` | Use semantic refs: `"origin"`, `"current"`, `"latest"`, custom alias |
328
+ | `browser_go_back` after new tab opened | Use `tab_close` + `tab_switch` new tab has no history |
329
+ | Silently dropping tab ops from recording | If recording has tab ops, playbook MUST include tab management |
330
+ | Loop with `go_back` when recording had tab ops | Check recording if tab_switch/close exists, use tab management not go_back |