linkedin-automation-cli 1.0.0

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 (314) hide show
  1. package/.env.example +12 -0
  2. package/.github/workflows/ci.yml +66 -0
  3. package/.github/workflows/publish.yml +48 -0
  4. package/.husky/pre-commit +6 -0
  5. package/.prettierignore +4 -0
  6. package/.prettierrc +10 -0
  7. package/AGENTS.md +294 -0
  8. package/CHANGELOG.md +40 -0
  9. package/GIT_RELEASE.md +167 -0
  10. package/LICENSE +21 -0
  11. package/Makefile +30 -0
  12. package/NPM_PUBLISHING.md +230 -0
  13. package/PYEOF +0 -0
  14. package/README.md +295 -0
  15. package/TESTING-GUIDE.md +151 -0
  16. package/cmd/linkedin/main.go +9 -0
  17. package/dist/agent/action-executor.d.ts +81 -0
  18. package/dist/agent/action-executor.d.ts.map +1 -0
  19. package/dist/agent/action-executor.js +170 -0
  20. package/dist/agent/action-executor.js.map +1 -0
  21. package/dist/agent/action-executor.test.d.ts +2 -0
  22. package/dist/agent/action-executor.test.d.ts.map +1 -0
  23. package/dist/agent/action-executor.test.js +366 -0
  24. package/dist/agent/action-executor.test.js.map +1 -0
  25. package/dist/agent/claude-client.d.ts +74 -0
  26. package/dist/agent/claude-client.d.ts.map +1 -0
  27. package/dist/agent/claude-client.js +314 -0
  28. package/dist/agent/claude-client.js.map +1 -0
  29. package/dist/agent/claude-client.test.d.ts +2 -0
  30. package/dist/agent/claude-client.test.d.ts.map +1 -0
  31. package/dist/agent/claude-client.test.js +590 -0
  32. package/dist/agent/claude-client.test.js.map +1 -0
  33. package/dist/agent/dom-extractor.d.ts +50 -0
  34. package/dist/agent/dom-extractor.d.ts.map +1 -0
  35. package/dist/agent/dom-extractor.js +374 -0
  36. package/dist/agent/dom-extractor.js.map +1 -0
  37. package/dist/agent/dom-extractor.test.d.ts +7 -0
  38. package/dist/agent/dom-extractor.test.d.ts.map +1 -0
  39. package/dist/agent/dom-extractor.test.js +504 -0
  40. package/dist/agent/dom-extractor.test.js.map +1 -0
  41. package/dist/agent/extension-client.d.ts +75 -0
  42. package/dist/agent/extension-client.d.ts.map +1 -0
  43. package/dist/agent/extension-client.js +245 -0
  44. package/dist/agent/extension-client.js.map +1 -0
  45. package/dist/agent/index.d.ts +8 -0
  46. package/dist/agent/index.d.ts.map +1 -0
  47. package/dist/agent/index.js +16 -0
  48. package/dist/agent/index.js.map +1 -0
  49. package/dist/agent/page-agent.d.ts +76 -0
  50. package/dist/agent/page-agent.d.ts.map +1 -0
  51. package/dist/agent/page-agent.js +236 -0
  52. package/dist/agent/page-agent.js.map +1 -0
  53. package/dist/agent/types.d.ts +236 -0
  54. package/dist/agent/types.d.ts.map +1 -0
  55. package/dist/agent/types.js +37 -0
  56. package/dist/agent/types.js.map +1 -0
  57. package/dist/cli/agent-commands.d.ts +3 -0
  58. package/dist/cli/agent-commands.d.ts.map +1 -0
  59. package/dist/cli/agent-commands.js +250 -0
  60. package/dist/cli/agent-commands.js.map +1 -0
  61. package/dist/cli/auth.d.ts +3 -0
  62. package/dist/cli/auth.d.ts.map +1 -0
  63. package/dist/cli/auth.js +288 -0
  64. package/dist/cli/auth.js.map +1 -0
  65. package/dist/cli/company.d.ts +3 -0
  66. package/dist/cli/company.d.ts.map +1 -0
  67. package/dist/cli/company.js +55 -0
  68. package/dist/cli/company.js.map +1 -0
  69. package/dist/cli/connection.d.ts +3 -0
  70. package/dist/cli/connection.d.ts.map +1 -0
  71. package/dist/cli/connection.js +79 -0
  72. package/dist/cli/connection.js.map +1 -0
  73. package/dist/cli/index.d.ts +7 -0
  74. package/dist/cli/index.d.ts.map +1 -0
  75. package/dist/cli/index.js +17 -0
  76. package/dist/cli/index.js.map +1 -0
  77. package/dist/cli/messages.d.ts +3 -0
  78. package/dist/cli/messages.d.ts.map +1 -0
  79. package/dist/cli/messages.js +268 -0
  80. package/dist/cli/messages.js.map +1 -0
  81. package/dist/cli/profile.d.ts +3 -0
  82. package/dist/cli/profile.d.ts.map +1 -0
  83. package/dist/cli/profile.js +81 -0
  84. package/dist/cli/profile.js.map +1 -0
  85. package/dist/cli/profile.test.d.ts +2 -0
  86. package/dist/cli/profile.test.d.ts.map +1 -0
  87. package/dist/cli/profile.test.js +15 -0
  88. package/dist/cli/profile.test.js.map +1 -0
  89. package/dist/cli/reply.d.ts +3 -0
  90. package/dist/cli/reply.d.ts.map +1 -0
  91. package/dist/cli/reply.js +129 -0
  92. package/dist/cli/reply.js.map +1 -0
  93. package/dist/core/audit.d.ts +17 -0
  94. package/dist/core/audit.d.ts.map +1 -0
  95. package/dist/core/audit.js +121 -0
  96. package/dist/core/audit.js.map +1 -0
  97. package/dist/core/audit.test.d.ts +2 -0
  98. package/dist/core/audit.test.d.ts.map +1 -0
  99. package/dist/core/audit.test.js +142 -0
  100. package/dist/core/audit.test.js.map +1 -0
  101. package/dist/core/browser-cookies.d.ts +19 -0
  102. package/dist/core/browser-cookies.d.ts.map +1 -0
  103. package/dist/core/browser-cookies.js +181 -0
  104. package/dist/core/browser-cookies.js.map +1 -0
  105. package/dist/core/browser.d.ts +50 -0
  106. package/dist/core/browser.d.ts.map +1 -0
  107. package/dist/core/browser.js +318 -0
  108. package/dist/core/browser.js.map +1 -0
  109. package/dist/core/config.d.ts +20 -0
  110. package/dist/core/config.d.ts.map +1 -0
  111. package/dist/core/config.js +103 -0
  112. package/dist/core/config.js.map +1 -0
  113. package/dist/core/config.test.d.ts +2 -0
  114. package/dist/core/config.test.d.ts.map +1 -0
  115. package/dist/core/config.test.js +111 -0
  116. package/dist/core/config.test.js.map +1 -0
  117. package/dist/core/storage.d.ts +19 -0
  118. package/dist/core/storage.d.ts.map +1 -0
  119. package/dist/core/storage.js +124 -0
  120. package/dist/core/storage.js.map +1 -0
  121. package/dist/core/storage.test.d.ts +2 -0
  122. package/dist/core/storage.test.d.ts.map +1 -0
  123. package/dist/core/storage.test.js +142 -0
  124. package/dist/core/storage.test.js.map +1 -0
  125. package/dist/index.d.ts +3 -0
  126. package/dist/index.d.ts.map +1 -0
  127. package/dist/index.js +63 -0
  128. package/dist/index.js.map +1 -0
  129. package/dist/linkedin/auth.d.ts +22 -0
  130. package/dist/linkedin/auth.d.ts.map +1 -0
  131. package/dist/linkedin/auth.js +167 -0
  132. package/dist/linkedin/auth.js.map +1 -0
  133. package/dist/linkedin/company-extractor.d.ts +36 -0
  134. package/dist/linkedin/company-extractor.d.ts.map +1 -0
  135. package/dist/linkedin/company-extractor.js +211 -0
  136. package/dist/linkedin/company-extractor.js.map +1 -0
  137. package/dist/linkedin/company-extractor.test.d.ts +2 -0
  138. package/dist/linkedin/company-extractor.test.d.ts.map +1 -0
  139. package/dist/linkedin/company-extractor.test.js +52 -0
  140. package/dist/linkedin/company-extractor.test.js.map +1 -0
  141. package/dist/linkedin/connector.d.ts +45 -0
  142. package/dist/linkedin/connector.d.ts.map +1 -0
  143. package/dist/linkedin/connector.js +245 -0
  144. package/dist/linkedin/connector.js.map +1 -0
  145. package/dist/linkedin/message-sender.d.ts +32 -0
  146. package/dist/linkedin/message-sender.d.ts.map +1 -0
  147. package/dist/linkedin/message-sender.js +112 -0
  148. package/dist/linkedin/message-sender.js.map +1 -0
  149. package/dist/linkedin/messages.d.ts +78 -0
  150. package/dist/linkedin/messages.d.ts.map +1 -0
  151. package/dist/linkedin/messages.js +745 -0
  152. package/dist/linkedin/messages.js.map +1 -0
  153. package/dist/linkedin/profile.d.ts +37 -0
  154. package/dist/linkedin/profile.d.ts.map +1 -0
  155. package/dist/linkedin/profile.js +268 -0
  156. package/dist/linkedin/profile.js.map +1 -0
  157. package/dist/linkedin/profile.test.d.ts +2 -0
  158. package/dist/linkedin/profile.test.d.ts.map +1 -0
  159. package/dist/linkedin/profile.test.js +68 -0
  160. package/dist/linkedin/profile.test.js.map +1 -0
  161. package/dist/linkedin/reply.d.ts +21 -0
  162. package/dist/linkedin/reply.d.ts.map +1 -0
  163. package/dist/linkedin/reply.js +76 -0
  164. package/dist/linkedin/reply.js.map +1 -0
  165. package/dist/linkedin/selector-engine.d.ts +69 -0
  166. package/dist/linkedin/selector-engine.d.ts.map +1 -0
  167. package/dist/linkedin/selector-engine.js +339 -0
  168. package/dist/linkedin/selector-engine.js.map +1 -0
  169. package/dist/linkedin/selector-engine.test.d.ts +2 -0
  170. package/dist/linkedin/selector-engine.test.d.ts.map +1 -0
  171. package/dist/linkedin/selector-engine.test.js +135 -0
  172. package/dist/linkedin/selector-engine.test.js.map +1 -0
  173. package/dist/linkedin/selectors.d.ts +65 -0
  174. package/dist/linkedin/selectors.d.ts.map +1 -0
  175. package/dist/linkedin/selectors.js +261 -0
  176. package/dist/linkedin/selectors.js.map +1 -0
  177. package/dist/templates/engine.d.ts +37 -0
  178. package/dist/templates/engine.d.ts.map +1 -0
  179. package/dist/templates/engine.js +215 -0
  180. package/dist/templates/engine.js.map +1 -0
  181. package/dist/templates/engine.test.d.ts +2 -0
  182. package/dist/templates/engine.test.d.ts.map +1 -0
  183. package/dist/templates/engine.test.js +212 -0
  184. package/dist/templates/engine.test.js.map +1 -0
  185. package/dist/templates/index.d.ts +2 -0
  186. package/dist/templates/index.d.ts.map +1 -0
  187. package/dist/templates/index.js +7 -0
  188. package/dist/templates/index.js.map +1 -0
  189. package/dist/types/index.d.ts +113 -0
  190. package/dist/types/index.d.ts.map +1 -0
  191. package/dist/types/index.js +3 -0
  192. package/dist/types/index.js.map +1 -0
  193. package/dist/types/index.test.d.ts +2 -0
  194. package/dist/types/index.test.d.ts.map +1 -0
  195. package/dist/types/index.test.js +90 -0
  196. package/dist/types/index.test.js.map +1 -0
  197. package/dist/utils/paths.d.ts +8 -0
  198. package/dist/utils/paths.d.ts.map +1 -0
  199. package/dist/utils/paths.js +68 -0
  200. package/dist/utils/paths.js.map +1 -0
  201. package/dist/utils/rate-limiter.d.ts +22 -0
  202. package/dist/utils/rate-limiter.d.ts.map +1 -0
  203. package/dist/utils/rate-limiter.js +57 -0
  204. package/dist/utils/rate-limiter.js.map +1 -0
  205. package/dist/utils/retry.d.ts +18 -0
  206. package/dist/utils/retry.d.ts.map +1 -0
  207. package/dist/utils/retry.js +49 -0
  208. package/dist/utils/retry.js.map +1 -0
  209. package/docs/connection-command.md +52 -0
  210. package/docs/plans/2025-03-03-linkedin-cli-design.md +280 -0
  211. package/docs/plans/2025-03-03-linkedin-cli-implementation-plan.md +2087 -0
  212. package/docs/plans/2025-03-03-linkedin-cli-implementation.md +2420 -0
  213. package/docs/plans/2026-02-19-linkedin-connection-feature.md +596 -0
  214. package/docs/plans/2026-02-28-messages-send-feature.md +480 -0
  215. package/docs/plans/2026-02-28-messages-show-design.md +243 -0
  216. package/docs/plans/2026-03-03-linkedin-cli-oss-publishing-design.md +394 -0
  217. package/docs/plans/2026-03-03-linkedin-cli-oss-publishing-plan.md +1592 -0
  218. package/docs/superpowers/plans/2026-03-13-linkedin-automation-resilience-migration.md +425 -0
  219. package/docs/superpowers/plans/2026-03-13-playwright-fara-migration.md +1112 -0
  220. package/docs/superpowers/plans/2026-03-14-page-agent-plan.md +1598 -0
  221. package/docs/superpowers/plans/2026-03-15-company-profile-extraction.md +591 -0
  222. package/docs/superpowers/plans/2026-03-15-profile-extraction-plan.md +943 -0
  223. package/docs/superpowers/specs/2026-03-14-company-profile-extraction-design.md +371 -0
  224. package/docs/superpowers/specs/2026-03-14-page-agent-design.md +385 -0
  225. package/docs/superpowers/specs/2026-03-15-profile-extraction-design.md +409 -0
  226. package/eslint.config.mjs +58 -0
  227. package/go.mod +9 -0
  228. package/go.sum +10 -0
  229. package/import-cookies.js +376 -0
  230. package/internal/cmd/actions.go +123 -0
  231. package/internal/cmd/auth.go +108 -0
  232. package/internal/cmd/connect.go +42 -0
  233. package/internal/cmd/message.go +44 -0
  234. package/internal/cmd/people.go +454 -0
  235. package/internal/cmd/profiles.go +121 -0
  236. package/internal/cmd/root.go +89 -0
  237. package/internal/cmd/sequence.go +192 -0
  238. package/internal/config/config.go +187 -0
  239. package/internal/config/config_test.go +121 -0
  240. package/internal/config/profile.go +65 -0
  241. package/internal/linkedin/navigator.go +195 -0
  242. package/internal/linkedin/selectors.go +39 -0
  243. package/internal/linkedin/validator.go +69 -0
  244. package/internal/pinchtab/client.go +183 -0
  245. package/internal/pinchtab/client_test.go +67 -0
  246. package/internal/pinchtab/types.go +50 -0
  247. package/internal/ratelimit/limiter.go +115 -0
  248. package/internal/ratelimit/limits.go +32 -0
  249. package/package.json +67 -0
  250. package/release.sh +66 -0
  251. package/scripts/debug-linkedin.js +156 -0
  252. package/scripts/debug-login.js +193 -0
  253. package/scripts/extract-from-edge.js +96 -0
  254. package/scripts/import-cookies.js +101 -0
  255. package/scripts/poc-show-data.js +205 -0
  256. package/scripts/proof-of-access.js +87 -0
  257. package/scripts/prove-connection.js +110 -0
  258. package/scripts/show-linkedin-data.js +173 -0
  259. package/src/agent/action-executor.test.ts +464 -0
  260. package/src/agent/action-executor.ts +203 -0
  261. package/src/agent/claude-client.test.ts +707 -0
  262. package/src/agent/claude-client.ts +422 -0
  263. package/src/agent/dom-extractor.test.ts +574 -0
  264. package/src/agent/dom-extractor.ts +437 -0
  265. package/src/agent/extension-client.ts +306 -0
  266. package/src/agent/index.ts +28 -0
  267. package/src/agent/page-agent.ts +292 -0
  268. package/src/agent/types.ts +288 -0
  269. package/src/cli/agent-commands.ts +274 -0
  270. package/src/cli/auth.ts +343 -0
  271. package/src/cli/company.ts +66 -0
  272. package/src/cli/connection.ts +89 -0
  273. package/src/cli/index.ts +7 -0
  274. package/src/cli/messages.ts +338 -0
  275. package/src/cli/profile.test.ts +14 -0
  276. package/src/cli/profile.ts +95 -0
  277. package/src/cli/reply.ts +110 -0
  278. package/src/core/audit.test.ts +134 -0
  279. package/src/core/audit.ts +98 -0
  280. package/src/core/browser-cookies.ts +203 -0
  281. package/src/core/browser.ts +304 -0
  282. package/src/core/config.test.ts +90 -0
  283. package/src/core/config.ts +81 -0
  284. package/src/core/storage.test.ts +129 -0
  285. package/src/core/storage.ts +100 -0
  286. package/src/index.ts +70 -0
  287. package/src/linkedin/auth.ts +218 -0
  288. package/src/linkedin/company-extractor.test.ts +58 -0
  289. package/src/linkedin/company-extractor.ts +222 -0
  290. package/src/linkedin/connector.ts +336 -0
  291. package/src/linkedin/message-sender.ts +141 -0
  292. package/src/linkedin/messages.ts +894 -0
  293. package/src/linkedin/profile.test.ts +79 -0
  294. package/src/linkedin/profile.ts +314 -0
  295. package/src/linkedin/reply.ts +96 -0
  296. package/src/linkedin/selector-engine.test.ts +167 -0
  297. package/src/linkedin/selector-engine.ts +393 -0
  298. package/src/linkedin/selectors.ts +268 -0
  299. package/src/templates/defaults/followup.txt +14 -0
  300. package/src/templates/defaults/meeting.txt +16 -0
  301. package/src/templates/defaults/welcome.txt +14 -0
  302. package/src/templates/engine.test.ts +228 -0
  303. package/src/templates/engine.ts +208 -0
  304. package/src/templates/index.ts +1 -0
  305. package/src/types/index.test.ts +94 -0
  306. package/src/types/index.ts +143 -0
  307. package/src/types/sql.js.d.ts +23 -0
  308. package/src/utils/paths.ts +33 -0
  309. package/src/utils/rate-limiter.ts +75 -0
  310. package/src/utils/retry.ts +78 -0
  311. package/test-cli.sh +85 -0
  312. package/test-real-data.sh +97 -0
  313. package/tsconfig.json +23 -0
  314. package/vitest.config.ts +35 -0
@@ -0,0 +1,385 @@
1
+ # Page Agent Integration Design
2
+
3
+ **Date:** 2026-03-14
4
+ **Status:** Draft
5
+ **Scope:** Replace selector-based automation with DOM-text + LLM agent approach
6
+
7
+ ---
8
+
9
+ ## Overview
10
+
11
+ Replace the current CSS selector-based LinkedIn automation with a DOM-text extraction and LLM-powered agent system, inspired by Alibaba's Page Agent. This approach extracts semantic text representations of the page and uses Claude API (via Dashscope) to make decisions about actions.
12
+
13
+ ---
14
+
15
+ ## Current Architecture (v0.3.0)
16
+
17
+ ```
18
+ User Command → CLI → SelectorEngine → Playwright → CSS Selectors → LinkedIn
19
+
20
+ (Multi-layer: a11y → text → pattern → legacy)
21
+ ```
22
+
23
+ **Problems:**
24
+ - Brittle CSS selectors break when LinkedIn updates UI
25
+ - Selector learning system adds complexity
26
+ - Hard-coded selector chains are maintenance burden
27
+ - No natural language understanding
28
+
29
+ ---
30
+
31
+ ## New Architecture
32
+
33
+ ```
34
+ User Command → CLI → PageAgent → DOM Extractor → Claude API → Action Plan → Playwright → LinkedIn
35
+ ↓ ↓
36
+ (Natural language) (Structured actions)
37
+ ```
38
+
39
+ ---
40
+
41
+ ## Components
42
+
43
+ ### 1. DOM Extractor (`src/agent/dom-extractor.ts`)
44
+
45
+ Extracts semantic text representation of the LinkedIn page using Playwright.
46
+
47
+ **Output format:**
48
+ ```typescript
49
+ interface DOMRepresentation {
50
+ url: string;
51
+ title: string;
52
+ elements: DOMElement[];
53
+ textContent: string;
54
+ interactiveElements: InteractiveElement[];
55
+ }
56
+
57
+ interface DOMElement {
58
+ id: string;
59
+ tag: string;
60
+ role?: string;
61
+ ariaLabel?: string;
62
+ text: string;
63
+ bbox?: { x: number; y: number; width: number; height: number };
64
+ }
65
+
66
+ interface InteractiveElement {
67
+ id: string;
68
+ type: 'button' | 'link' | 'input' | 'textarea' | 'select';
69
+ text: string;
70
+ purpose: string; // inferred from context
71
+ }
72
+ ```
73
+
74
+ **Extraction strategy:**
75
+ - Use `page.evaluate()` to extract clean DOM structure
76
+ - Focus on interactive elements (buttons, links, inputs)
77
+ - Include ARIA labels, roles, and visible text
78
+ - Remove script/style content, keep semantic structure
79
+
80
+ ### 2. Claude API Client (`src/agent/claude-client.ts`)
81
+
82
+ Client for Dashscope Claude API endpoint.
83
+
84
+ **Configuration:**
85
+ - Base URL: `https://coding.dashscope.aliyuncs.com/apps/anthropic/v1`
86
+ - Model: `qwen3.5-plus`
87
+ - API Key: From `.env` file (`DASHSCOPE_API_KEY`)
88
+
89
+ **Interface:**
90
+ ```typescript
91
+ interface ClaudeClient {
92
+ generateActionPlan(
93
+ dom: DOMRepresentation,
94
+ goal: string,
95
+ context?: ActionContext
96
+ ): Promise<ActionPlan>;
97
+ }
98
+
99
+ interface ActionPlan {
100
+ actions: Action[];
101
+ reasoning: string;
102
+ expectedOutcome: string;
103
+ }
104
+
105
+ type Action =
106
+ | { type: 'click'; elementId: string; description: string }
107
+ | { type: 'type'; elementId: string; text: string; description: string }
108
+ | { type: 'wait'; durationMs: number; description: string }
109
+ | { type: 'navigate'; url: string; description: string }
110
+ | { type: 'verify'; condition: string; description: string };
111
+ ```
112
+
113
+ ### 3. Action Executor (`src/agent/action-executor.ts`)
114
+
115
+ Executes action plans using Playwright.
116
+
117
+ **Responsibilities:**
118
+ - Map element IDs to actual DOM elements
119
+ - Execute clicks, typing, navigation
120
+ - Handle errors and retries
121
+ - Verify outcomes
122
+
123
+ ### 4. Page Agent (`src/agent/page-agent.ts`)
124
+
125
+ Main orchestrator combining all components.
126
+
127
+ **Interface:**
128
+ ```typescript
129
+ interface PageAgent {
130
+ // High-level task execution
131
+ execute(task: Task): Promise<TaskResult>;
132
+
133
+ // Specific tasks
134
+ connect(profileUrl: string, note?: string): Promise<ConnectionResult>;
135
+ sendMessage(profileUrl: string, message: string): Promise<MessageResult>;
136
+ }
137
+ ```
138
+
139
+ ---
140
+
141
+ ## Prompt Design
142
+
143
+ ### System Prompt
144
+
145
+ ```
146
+ You are a web automation agent that controls LinkedIn through a browser.
147
+ You receive a DOM representation of the current page and must decide the next action(s) to achieve the user's goal.
148
+
149
+ Rules:
150
+ 1. Respond ONLY with a JSON action plan
151
+ 2. Each action must reference an element by its ID from the DOM
152
+ 3. Include brief reasoning for your decision
153
+ 4. If the goal is already achieved, return empty actions with success status
154
+ 5. If stuck or error, explain why and suggest recovery
155
+
156
+ Available actions:
157
+ - click: Click an element
158
+ - type: Type text into an input
159
+ - wait: Wait for page to stabilize
160
+ - navigate: Go to a URL
161
+ - verify: Check if a condition is met
162
+
163
+ Response format:
164
+ {
165
+ "reasoning": "string",
166
+ "expectedOutcome": "string",
167
+ "actions": [
168
+ { "type": "click", "elementId": "elem-123", "description": "Click Connect button" }
169
+ ],
170
+ "status": "in_progress" | "completed" | "error"
171
+ }
172
+ ```
173
+
174
+ ### User Prompt Template
175
+
176
+ ```
177
+ Goal: {{userGoal}}
178
+
179
+ Current page DOM:
180
+ ```json
181
+ {{domRepresentation}}
182
+ ```
183
+
184
+ {{#if previousActions}}
185
+ Previous actions taken:
186
+ {{#each previousActions}}
187
+ - {{type}}: {{description}} ({{status}})
188
+ {{/each}}
189
+ {{/if}}
190
+
191
+ What action(s) should be taken next?
192
+ ```
193
+
194
+ ---
195
+
196
+ ## DOM Extraction Strategy
197
+
198
+ ### Approach: Accessibility Tree + Interactive Elements
199
+
200
+ Extract elements in priority order:
201
+
202
+ 1. **Buttons** - `button` elements, `[role="button"]`, clickables
203
+ 2. **Links** - `a[href]` elements
204
+ 3. **Form inputs** - `input`, `textarea`, `select`
205
+ 4. **Text content** - Headings, labels, status messages
206
+
207
+ ### Element ID Assignment
208
+
209
+ Assign stable IDs based on element characteristics:
210
+ - Use `data-testid` if available
211
+ - Fallback to `[tag]-[aria-label]-[index]` hash
212
+ - Include bounding box for click coordinates
213
+
214
+ ### Example Extracted DOM
215
+
216
+ ```json
217
+ {
218
+ "url": "https://www.linkedin.com/in/alice",
219
+ "title": "Alice Smith | LinkedIn",
220
+ "elements": [
221
+ {
222
+ "id": "btn-connect-1",
223
+ "tag": "button",
224
+ "role": "button",
225
+ "ariaLabel": "Connect",
226
+ "text": "Connect",
227
+ "bbox": { "x": 800, "y": 200, "width": 100, "height": 40 }
228
+ },
229
+ {
230
+ "id": "btn-message-1",
231
+ "tag": "button",
232
+ "role": "button",
233
+ "ariaLabel": "Message",
234
+ "text": "Message",
235
+ "bbox": { "x": 920, "y": 200, "width": 100, "height": 40 }
236
+ },
237
+ {
238
+ "id": "input-note-1",
239
+ "tag": "textarea",
240
+ "role": "textbox",
241
+ "ariaLabel": "Add a note",
242
+ "text": "",
243
+ "bbox": { "x": 500, "y": 400, "width": 400, "height": 100 }
244
+ }
245
+ ],
246
+ "status": {
247
+ "connectionState": "not_connected",
248
+ "profileName": "Alice Smith",
249
+ "profileTitle": "Software Engineer at TechCorp"
250
+ }
251
+ }
252
+ ```
253
+
254
+ ---
255
+
256
+ ## Action Execution Flow
257
+
258
+ ### Connect Flow
259
+
260
+ ```
261
+ 1. Navigate to profile URL
262
+ 2. Extract DOM
263
+ 3. Send to Claude: "Send connection request to this profile"
264
+ 4. Receive action plan:
265
+ - Click Connect button
266
+ - Wait for modal
267
+ - Type note (if provided)
268
+ - Click Send
269
+ 5. Execute actions one by one
270
+ 6. Verify outcome (check for Pending status)
271
+ ```
272
+
273
+ ### Message Flow
274
+
275
+ ```
276
+ 1. Navigate to profile URL
277
+ 2. Extract DOM
278
+ 3. Send to Claude: "Send message to this person"
279
+ 4. Receive action plan:
280
+ - Click Message button
281
+ - Wait for composer
282
+ - Type message
283
+ - Click Send
284
+ 5. Execute actions
285
+ 6. Verify outcome
286
+ ```
287
+
288
+ ---
289
+
290
+ ## Error Handling
291
+
292
+ ### Recovery Strategies
293
+
294
+ 1. **Element not found** - Re-extract DOM, retry with updated IDs
295
+ 2. **Action timeout** - Wait longer, check if page changed
296
+ 3. **Unexpected state** - Re-plan with new DOM
297
+ 4. **LLM error** - Retry with exponential backoff
298
+
299
+ ### Safety Checks
300
+
301
+ - Validate URLs are LinkedIn profiles before navigation
302
+ - Rate limiting via existing limiter
303
+ - Confirmation prompts for destructive actions
304
+
305
+ ---
306
+
307
+ ## File Structure
308
+
309
+ ```
310
+ src/
311
+ ├── agent/
312
+ │ ├── index.ts # Public API exports
313
+ │ ├── page-agent.ts # Main orchestrator
314
+ │ ├── dom-extractor.ts # DOM to text extraction
315
+ │ ├── claude-client.ts # Claude API client
316
+ │ ├── action-executor.ts # Playwright action execution
317
+ │ ├── prompts.ts # LLM prompts
318
+ │ └── types.ts # Agent types
319
+ ├── cli/
320
+ │ └── agent-command.ts # New CLI command: `linkedin agent`
321
+ └── linkedin/
322
+ └── (existing files - keep for fallback)
323
+ ```
324
+
325
+ ---
326
+
327
+ ## Migration Plan
328
+
329
+ ### Phase 1: New Agent Commands (This PR)
330
+ - Create `src/agent/` module
331
+ - Implement DOM extractor
332
+ - Implement Claude client
333
+ - Add `linkedin agent connect <url>` command
334
+ - Add `linkedin agent message <url> <text>` command
335
+ - Keep existing commands unchanged
336
+
337
+ ### Phase 2: Replace Existing Commands (Future)
338
+ - Migrate `linkedin connect` to use agent
339
+ - Migrate `linkedin message` to use agent
340
+ - Deprecate selector engine
341
+
342
+ ---
343
+
344
+ ## Environment Configuration
345
+
346
+ Add to `.env`:
347
+
348
+ ```bash
349
+ # Dashscope Claude API
350
+ DASHSCOPE_API_KEY=your_key_here
351
+ DASHSCOPE_BASE_URL=https://coding.dashscope.aliyuncs.com/apps/anthropic/v1
352
+ DASHSCOPE_MODEL=qwen3.5-plus
353
+ ```
354
+
355
+ ---
356
+
357
+ ## Testing Strategy
358
+
359
+ 1. **Unit tests** - DOM extractor with mock pages
360
+ 2. **Integration tests** - Agent with mock Claude responses
361
+ 3. **E2E tests** - Full flow with test LinkedIn accounts
362
+
363
+ ---
364
+
365
+ ## Trade-offs
366
+
367
+ ### Advantages
368
+ - Resilient to UI changes (text-based, not selectors)
369
+ - Natural language understanding
370
+ - Easier maintenance
371
+ - Self-documenting actions (LLM reasoning)
372
+
373
+ ### Disadvantages
374
+ - Requires API calls (latency, cost)
375
+ - Depends on LLM availability
376
+ - More complex debugging
377
+
378
+ ---
379
+
380
+ ## Success Criteria
381
+
382
+ 1. Connect and message commands work reliably on standard LinkedIn profiles
383
+ 2. No hard-coded selectors in new agent code
384
+ 3. Clear action logging for debugging
385
+ 4. Graceful fallback if LLM is unavailable