@revfleet/hscli 0.8.11 → 0.9.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 (154) hide show
  1. package/CHANGELOG.md +186 -0
  2. package/README.md +223 -10
  3. package/dist/cli.js +29 -3
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/agent/contracts.d.ts +3 -0
  6. package/dist/commands/agent/contracts.js +139 -0
  7. package/dist/commands/agent/contracts.js.map +1 -0
  8. package/dist/commands/agent/index.d.ts +3 -0
  9. package/dist/commands/agent/index.js +188 -0
  10. package/dist/commands/agent/index.js.map +1 -0
  11. package/dist/commands/agent/recipes.d.ts +13 -0
  12. package/dist/commands/agent/recipes.js +108 -0
  13. package/dist/commands/agent/recipes.js.map +1 -0
  14. package/dist/commands/agent/runner.d.ts +16 -0
  15. package/dist/commands/agent/runner.js +165 -0
  16. package/dist/commands/agent/runner.js.map +1 -0
  17. package/dist/commands/auth/index.js +64 -7
  18. package/dist/commands/auth/index.js.map +1 -1
  19. package/dist/commands/automation/index.js +30 -0
  20. package/dist/commands/automation/index.js.map +1 -1
  21. package/dist/commands/backup/index.d.ts +12 -0
  22. package/dist/commands/backup/index.js +459 -0
  23. package/dist/commands/backup/index.js.map +1 -0
  24. package/dist/commands/cms/content-ops.js +1 -1
  25. package/dist/commands/cms/content-ops.js.map +1 -1
  26. package/dist/commands/communication-preferences/index.js +207 -7
  27. package/dist/commands/communication-preferences/index.js.map +1 -1
  28. package/dist/commands/content-permissions/index.d.ts +22 -0
  29. package/dist/commands/content-permissions/index.js +141 -0
  30. package/dist/commands/content-permissions/index.js.map +1 -0
  31. package/dist/commands/context/index.d.ts +3 -0
  32. package/dist/commands/context/index.js +182 -0
  33. package/dist/commands/context/index.js.map +1 -0
  34. package/dist/commands/crm/associations.js +38 -0
  35. package/dist/commands/crm/associations.js.map +1 -1
  36. package/dist/commands/crm/custom-objects.js +39 -2
  37. package/dist/commands/crm/custom-objects.js.map +1 -1
  38. package/dist/commands/crm/dated.js +3 -3
  39. package/dist/commands/crm/dated.js.map +1 -1
  40. package/dist/commands/crm/migration.js +210 -2
  41. package/dist/commands/crm/migration.js.map +1 -1
  42. package/dist/commands/crm/properties.js +2 -1
  43. package/dist/commands/crm/properties.js.map +1 -1
  44. package/dist/commands/crm/property-batch.d.ts +1 -0
  45. package/dist/commands/crm/property-batch.js +44 -5
  46. package/dist/commands/crm/property-batch.js.map +1 -1
  47. package/dist/commands/crm/shared.js +21 -2
  48. package/dist/commands/crm/shared.js.map +1 -1
  49. package/dist/commands/crm/sync.js +1 -1
  50. package/dist/commands/crm/sync.js.map +1 -1
  51. package/dist/commands/doctor/index.js +193 -0
  52. package/dist/commands/doctor/index.js.map +1 -1
  53. package/dist/commands/email-tracking/index.d.ts +16 -0
  54. package/dist/commands/email-tracking/index.js +314 -0
  55. package/dist/commands/email-tracking/index.js.map +1 -0
  56. package/dist/commands/extensions/index.js +1 -1
  57. package/dist/commands/extensions/index.js.map +1 -1
  58. package/dist/commands/forms/index.js +15 -2
  59. package/dist/commands/forms/index.js.map +1 -1
  60. package/dist/commands/forms/legacy-v2.d.ts +7 -2
  61. package/dist/commands/forms/legacy-v2.js +103 -7
  62. package/dist/commands/forms/legacy-v2.js.map +1 -1
  63. package/dist/commands/guide/index.d.ts +3 -0
  64. package/dist/commands/guide/index.js +75 -16
  65. package/dist/commands/guide/index.js.map +1 -1
  66. package/dist/commands/internal/catalog.d.ts +1 -0
  67. package/dist/commands/internal/catalog.js +2 -0
  68. package/dist/commands/internal/catalog.js.map +1 -0
  69. package/dist/commands/internal/index.d.ts +26 -0
  70. package/dist/commands/internal/index.js +823 -0
  71. package/dist/commands/internal/index.js.map +1 -0
  72. package/dist/commands/internal-session.d.ts +15 -0
  73. package/dist/commands/internal-session.js +152 -0
  74. package/dist/commands/internal-session.js.map +1 -0
  75. package/dist/commands/lists/index.js +1 -1
  76. package/dist/commands/lists/index.js.map +1 -1
  77. package/dist/commands/marketing/email-audit.d.ts +32 -0
  78. package/dist/commands/marketing/email-audit.js +121 -0
  79. package/dist/commands/marketing/email-audit.js.map +1 -0
  80. package/dist/commands/marketing/index.js +2 -0
  81. package/dist/commands/marketing/index.js.map +1 -1
  82. package/dist/commands/mcp.js +5 -3
  83. package/dist/commands/mcp.js.map +1 -1
  84. package/dist/commands/playbooks/index.d.ts +3 -0
  85. package/dist/commands/playbooks/index.js +228 -0
  86. package/dist/commands/playbooks/index.js.map +1 -0
  87. package/dist/commands/portals/index.d.ts +3 -0
  88. package/dist/commands/portals/index.js +132 -0
  89. package/dist/commands/portals/index.js.map +1 -0
  90. package/dist/commands/reports/index.d.ts +3 -0
  91. package/dist/commands/reports/index.js +479 -0
  92. package/dist/commands/reports/index.js.map +1 -0
  93. package/dist/commands/sales-content/index.d.ts +13 -0
  94. package/dist/commands/sales-content/index.js +59 -0
  95. package/dist/commands/sales-content/index.js.map +1 -0
  96. package/dist/commands/sales-templates/index.d.ts +3 -0
  97. package/dist/commands/sales-templates/index.js +88 -0
  98. package/dist/commands/sales-templates/index.js.map +1 -0
  99. package/dist/commands/seed/cms.js +28 -45
  100. package/dist/commands/seed/cms.js.map +1 -1
  101. package/dist/commands/sequences/index.d.ts +18 -0
  102. package/dist/commands/sequences/index.js +263 -0
  103. package/dist/commands/sequences/index.js.map +1 -0
  104. package/dist/commands/settings/index.js +450 -102
  105. package/dist/commands/settings/index.js.map +1 -1
  106. package/dist/commands/settings/site-settings.d.ts +3 -0
  107. package/dist/commands/settings/site-settings.js +106 -0
  108. package/dist/commands/settings/site-settings.js.map +1 -0
  109. package/dist/commands/ui/index.d.ts +3 -0
  110. package/dist/commands/ui/index.js +422 -0
  111. package/dist/commands/ui/index.js.map +1 -0
  112. package/dist/commands/workflows/index.js +2 -0
  113. package/dist/commands/workflows/index.js.map +1 -1
  114. package/dist/commands/workflows/preflight.d.ts +23 -0
  115. package/dist/commands/workflows/preflight.js +295 -0
  116. package/dist/commands/workflows/preflight.js.map +1 -0
  117. package/dist/core/action-contract.d.ts +43 -0
  118. package/dist/core/action-contract.js +207 -0
  119. package/dist/core/action-contract.js.map +1 -0
  120. package/dist/core/auth.d.ts +6 -0
  121. package/dist/core/auth.js +85 -0
  122. package/dist/core/auth.js.map +1 -1
  123. package/dist/core/http.d.ts +8 -0
  124. package/dist/core/http.js +37 -5
  125. package/dist/core/http.js.map +1 -1
  126. package/dist/core/id-maps.d.ts +27 -0
  127. package/dist/core/id-maps.js +204 -0
  128. package/dist/core/id-maps.js.map +1 -0
  129. package/dist/core/output.d.ts +2 -1
  130. package/dist/core/output.js +34 -0
  131. package/dist/core/output.js.map +1 -1
  132. package/dist/mcp/profiles.d.ts +14 -0
  133. package/dist/mcp/profiles.js +68 -0
  134. package/dist/mcp/profiles.js.map +1 -0
  135. package/dist/mcp/server.d.ts +5 -3
  136. package/dist/mcp/server.js +22 -5
  137. package/dist/mcp/server.js.map +1 -1
  138. package/docs/CAPABILITY_LIBRARY.md +29 -8
  139. package/docs/COMMAND_TREE.md +98 -17
  140. package/docs/COMPARISON.md +8 -5
  141. package/docs/COOKBOOK.md +127 -5
  142. package/docs/INTERNAL-ENDPOINTS-DISCOVERY.md +813 -0
  143. package/docs/INTERNAL-ENDPOINTS.md +371 -0
  144. package/docs/INTERNAL_ENDPOINTS.md +243 -0
  145. package/docs/MARKETING_SETUP.md +21 -1
  146. package/docs/MCP.md +28 -89
  147. package/docs/MCP_TOOL_CATALOG.md +192 -0
  148. package/docs/MIGRATION_ADAPTERS.md +115 -0
  149. package/docs/PORTAL_BACKUP.md +212 -0
  150. package/docs/PUBLISHING.md +1 -1
  151. package/docs/SALES_CONTENT_INTERNAL_PUNCH_LIST.md +504 -0
  152. package/docs/TUTORIALS/secure-agent-writes.md +49 -0
  153. package/docs/WHY_HOW_WHAT.md +10 -4
  154. package/package.json +4 -2
@@ -0,0 +1,504 @@
1
+ # Sales Content Internal Punch List
2
+
3
+ Status: staged backlog with a first migration-capable slice implemented.
4
+ Read surfaces are available for sales content, and narrow write/migration
5
+ helpers are available for sequences, sales templates, and playbooks. Broader
6
+ full-fidelity migration still requires sandbox contract testing before it should
7
+ be used on client production-adjacent portals.
8
+
9
+ This captures internal HubSpot surfaces discovered during migration work for
10
+ sequences, playbooks, sales templates, shared content permissions, and related
11
+ analytics. Existing public commands such as `hscli sales sequences` should stay
12
+ public-API first; this punch list describes the future cookie-auth/internal
13
+ surface needed for full-fidelity backup and migration.
14
+
15
+ Reference endpoint details and payload schemas should live in
16
+ `HSCLI-ENDPOINTS.md` or the migration-specific endpoint catalog.
17
+
18
+ Implemented slice:
19
+
20
+ - Browser-session fetch requests now use `credentials: "include"`.
21
+ - Browser-session commands already accept `--cookie`, `--cookie-file`,
22
+ `HSCLI_HUBSPOT_COOKIE`, `HSCLI_HUBSPOT_COOKIE_FILE`, `--csrf`, and
23
+ `HSCLI_HUBSPOT_CSRF`.
24
+ - `hubspotapi-csrf` is accepted as the modern CSRF cookie.
25
+ - `hscli sales-content search` wraps internal sales content search.
26
+ - `hscli sequences list|get|create|delete|migrate` reads, creates, deletes, and
27
+ migrates internal sequence payloads with destination owner/template remapping.
28
+ - `hscli sales-templates list|get|usage|create` reads template payloads and can
29
+ create a destination template from inline or file-backed HTML.
30
+ - `hscli playbooks list|get|questions|create|update|delete|migrate` reads and
31
+ migrates playbook payloads through the read/write Chirp RPC surface.
32
+ - Shared sales-content permission updates are available through the
33
+ sequence/playbook migration helpers when destination teams/users are supplied.
34
+
35
+ Still backlog:
36
+
37
+ - standalone `permissions get|set`
38
+ - standalone `sequences clone`
39
+ - standalone `sales-templates migrate`
40
+ - playbook/template permission verification reads
41
+ - doctor capability probes for these internal surfaces
42
+ - deeper policy gates for write/delete/permissions
43
+ - expanded sandbox contract tests across real portals
44
+
45
+ ## 1. Cookie / CSRF Transport
46
+
47
+ Why: internal HubSpot endpoints are same-origin browser-session endpoints, not
48
+ private-app bearer-token endpoints.
49
+
50
+ Code area: `src/core/http.ts` or the shared HubSpot HTTP client/session layer.
51
+
52
+ Add / verify:
53
+
54
+ - auth mode: `cookie`
55
+ - cookie input: `--cookie`, `--cookie-file`, or `HSCLI_HUBSPOT_COOKIE`
56
+ - cookie jar support for Netscape-format browser exports
57
+ - CSRF cookie name: `hubspotapi-csrf`
58
+ - CSRF header name: `X-HubSpot-CSRF-hubspotapi`
59
+ - automatic CSRF extraction from cookie jar or cookie header
60
+ - automatic CSRF header injection for internal calls
61
+ - `credentials: "include"` for fetch options where supported
62
+ - trace redaction for the `hubspotapi-csrf` value
63
+
64
+ Smoke test:
65
+
66
+ ```bash
67
+ hscli sequences list --cookie-file ~/.hubspot-cookie.txt
68
+ ```
69
+
70
+ ## 2. Top-Level `hscli sequences`
71
+
72
+ Code area: `src/commands/sequences/index.ts`.
73
+
74
+ ### `hscli sequences list`
75
+
76
+ - Endpoint: `POST /api/salescontentsearch/v2/search?portalId={portalId}`
77
+ - Body: `{ "query": "", "limit": <limit>, "offset": <offset> }`
78
+ - Pagination: use `hasMore`
79
+ - Filtering: client-side `contentType === "SEQUENCE"`
80
+ - Output columns: `id`, `name`, `owner`, `folderId`, `dynamic`,
81
+ `lastUsedAt`, `teams`, `visibleToAll`
82
+
83
+ ### `hscli sequences get <id>`
84
+
85
+ - Endpoint: `GET /api/sequences/v3/sequences/{id}?portalId={portalId}`
86
+ - Output: `{ sequence, userView }`
87
+ - Pretty print: steps, settings, dependencies
88
+ - Optional: `--with-stats` using sequence enrollment analytics below
89
+
90
+ ### `hscli sequences create`
91
+
92
+ - Endpoint: `POST /api/sequences/v3/sequences/create?portalId={portalId}`
93
+ - Body wrapper: `{ "sequenceEgg": <transformed-sequence> }`
94
+ - Inputs: `--from <jsonfile>`, `--remap-templates <mapfile>`
95
+ - Strip server-set fields:
96
+ - `id`
97
+ - `sequenceId`
98
+ - `sequenceStepId`
99
+ - `createdAt`
100
+ - `updatedAt`
101
+ - `deletedAt`
102
+ - Apply template ID remapping before create.
103
+ - If `--permissions <jsonfile>` is provided, call the shared permissions
104
+ helper after create.
105
+ - Log warning: `active: "INACTIVE"` is ignored by the API.
106
+
107
+ ### `hscli sequences delete <id...>`
108
+
109
+ - Endpoint: `DELETE /api/sequences/v3/sequences/batch?portalId={portalId}`
110
+ - Body: `{ "ids": ["..."] }`
111
+ - Require confirmation unless `--force`.
112
+
113
+ ### `hscli sequences clone <src-id> --dest <portalId>`
114
+
115
+ - Pipeline:
116
+ - fetch source sequence
117
+ - transform payload
118
+ - remap owner
119
+ - remap templates
120
+ - create destination sequence
121
+ - set destination permissions
122
+ - Template mapping strategy: match by `name + subject + normalized_body`
123
+ across portals.
124
+
125
+ ## 3. Top-Level `hscli playbooks`
126
+
127
+ Code area: `src/commands/playbooks/index.ts`.
128
+
129
+ Auth: cookie + Chirp RPC through
130
+ `POST /api/chirp-frontend-app/v1/gateway/...`.
131
+
132
+ ### `hscli playbooks list`
133
+
134
+ - Endpoint:
135
+ `POST /api/chirp-frontend-app/v1/gateway/com.hubspot.playbooks.rpc.PlaybooksRpc/listPlaybooks`
136
+ - Body: `{}`
137
+ - Response path: `.data.playbooks[]`
138
+ - Includes inline `body` and `htmlBody`.
139
+
140
+ ### `hscli playbooks get <id>`
141
+
142
+ - Option A: call `listPlaybooks` and filter by `id`
143
+ - Option B:
144
+ `POST .../com.hubspot.playbooks.rpc.PlaybooksRpc/getLatestPlaybookVersion`
145
+ - Body: `{ "playbookId": <id> }`
146
+
147
+ ### `hscli playbooks questions <id>`
148
+
149
+ - Endpoint:
150
+ `POST .../com.hubspot.playbooks.rpc.PlaybookQuestionsRpc/getLatestQuestionsByPlaybook`
151
+ - Body: `{ "playbookId": <id> }`
152
+ - Output: `{ questions: [...] }`
153
+
154
+ ### `hscli playbooks clone <src-id> --dest <portalId>`
155
+
156
+ - Status: read-only for now.
157
+ - Playbook write endpoint is not validated.
158
+ - Document as a known migration gap until sandbox write behavior is proven.
159
+
160
+ ## 4. Shared `hscli permissions`
161
+
162
+ Code area: `src/commands/permissions.ts`.
163
+
164
+ Used by sequences, sales templates, playbooks, snippets, and documents.
165
+
166
+ ### `hscli permissions set <objectType> <id1,id2,...>`
167
+
168
+ Flags:
169
+
170
+ - `--teams <ids>`
171
+ - `--users <ids>`
172
+ - `--visible-to-all`
173
+ - `--private`
174
+
175
+ Endpoint:
176
+
177
+ ```text
178
+ PUT /api/contentpermissions/v1/permissions?portalId={portalId}
179
+ ```
180
+
181
+ Body:
182
+
183
+ ```json
184
+ {
185
+ "objectType": "SEQUENCE",
186
+ "objectIds": ["123"],
187
+ "permissions": {
188
+ "TEAM": [],
189
+ "USER": []
190
+ },
191
+ "visibleToAll": false,
192
+ "private": true
193
+ }
194
+ ```
195
+
196
+ Object type enum:
197
+
198
+ - `SEQUENCE`
199
+ - `TEMPLATE`
200
+ - `PLAYBOOK`
201
+ - `DOCUMENT`
202
+ - `SNIPPET`
203
+
204
+ Behavior:
205
+
206
+ - Replace semantics.
207
+ - Fetch current state first.
208
+ - Show diff before overwrite.
209
+ - Gate writes through policy and `--force`.
210
+
211
+ ### `hscli permissions get <objectType> <id>`
212
+
213
+ - No standalone GET endpoint is confirmed.
214
+ - Use `salescontentsearch` and extract:
215
+ - `permissionedTeamIds`
216
+ - `permissionedUserIds`
217
+ - `visibleToAll`
218
+ - `private`
219
+
220
+ ## 5. Top-Level `hscli sales-templates`
221
+
222
+ Code area: `src/commands/sales-templates/index.ts`.
223
+
224
+ ### `hscli sales-templates list`
225
+
226
+ - Endpoint: `GET /api/templates/v1/templates?portalId={portalId}`
227
+ - Response: flat array
228
+ - Pagination: not required for typical portals unless tests prove otherwise
229
+
230
+ ### `hscli sales-templates get <id>`
231
+
232
+ - Endpoint: `GET /api/sales-templates/v2/templates/{id}?portalId={portalId}`
233
+ - Output fields:
234
+ - `id`
235
+ - `name`
236
+ - `subject`
237
+ - `body`
238
+ - `htmlBody`
239
+ - `userId`
240
+ - `sharedUsers`
241
+ - `active`
242
+ - `folderId`
243
+
244
+ ### `hscli sales-templates usage`
245
+
246
+ - Endpoint:
247
+ `GET /api/sales-templates/v2/templates/usage?portalId={portalId}`
248
+ - Output:
249
+ - `limit`
250
+ - `userLimit`
251
+ - `currentUsage`
252
+
253
+ ### `hscli sales-templates create`
254
+
255
+ - Endpoint: `POST /api/sales-templates/v2/templates?portalId={portalId}`
256
+ - Minimum body:
257
+ - `name`
258
+ - `subject`
259
+ - `body`
260
+ - Input: `--from <jsonfile>`
261
+ - Validate optional fields after sandbox contract tests.
262
+
263
+ ## 6. Top-Level `hscli sales-content search`
264
+
265
+ Code area: `src/commands/sales-content/index.ts`.
266
+
267
+ Command:
268
+
269
+ ```bash
270
+ hscli sales-content search \
271
+ --type SEQUENCE \
272
+ --owner <ownerId> \
273
+ --query "renewal"
274
+ ```
275
+
276
+ Endpoint:
277
+
278
+ ```text
279
+ POST /api/salescontentsearch/v2/search?portalId={portalId}
280
+ ```
281
+
282
+ Body:
283
+
284
+ ```json
285
+ {
286
+ "query": "",
287
+ "limit": 100,
288
+ "offset": 0
289
+ }
290
+ ```
291
+
292
+ Behavior:
293
+
294
+ - Paginate with `hasMore`.
295
+ - Filter `contentType` client-side because the API `filters[]` shape was
296
+ inconsistent in tests.
297
+ - Output: `type`, `id`, `name`, `owner`, `lastUsedAt`, `teams`,
298
+ `visibleToAll`.
299
+
300
+ ## 7. Sequence Enrollment Analytics
301
+
302
+ Used by `sequences list --with-stats` and `sequences get --with-stats`.
303
+
304
+ Object type: `0-68` (`SEQUENCE_ENROLLMENT`).
305
+
306
+ Total count endpoint:
307
+
308
+ ```text
309
+ POST /api/crm-search/search?portalId={portalId}
310
+ ```
311
+
312
+ Body:
313
+
314
+ ```json
315
+ {
316
+ "objectTypeId": "0-68",
317
+ "count": 1,
318
+ "filterGroups": [
319
+ {
320
+ "filters": [
321
+ {
322
+ "property": "hs_sequence_id",
323
+ "value": "<seqId>",
324
+ "operator": "EQ"
325
+ }
326
+ ]
327
+ }
328
+ ],
329
+ "requestOptions": {
330
+ "includeAllValues": false
331
+ },
332
+ "sorts": []
333
+ }
334
+ ```
335
+
336
+ Breakdown endpoint:
337
+
338
+ ```text
339
+ POST /api/crm-search/report?portalId={portalId}
340
+ ```
341
+
342
+ Aggregation:
343
+
344
+ ```json
345
+ {
346
+ "aggregations": [
347
+ {
348
+ "name": "enrollment_status",
349
+ "type": "TERMS",
350
+ "size": 20,
351
+ "property": "hs_enrollment_status"
352
+ }
353
+ ]
354
+ }
355
+ ```
356
+
357
+ Relevant properties:
358
+
359
+ - `hs_sequence_id`
360
+ - `hs_enrolled_at`
361
+ - `hs_ended_at`
362
+ - `hs_enrollment_status`
363
+ - `hs_email_sent_count`
364
+ - `hs_email_open_count`
365
+ - `hs_email_replied_count`
366
+ - `hs_email_bounced_count`
367
+ - `hs_finished_count`
368
+ - `hs_unenrolled_count`
369
+ - `hs_error_count`
370
+ - `hs_ended_no_response_count`
371
+ - `hs_enrolled_by`
372
+ - `hs_recipient_email`
373
+ - `hs_first_name`
374
+ - `hs_last_name`
375
+
376
+ ## 8. Migration Adapter: `hscli migrate sequences`
377
+
378
+ Code area: `src/commands/migrate/sequences.ts`.
379
+
380
+ Command:
381
+
382
+ ```bash
383
+ hscli migrate sequences \
384
+ --src-portal <source> \
385
+ --dst-portal <target> \
386
+ --ids <id1,id2> \
387
+ --owner-map owners.json \
388
+ --team-map teams.json \
389
+ --dry-run
390
+ ```
391
+
392
+ Pipeline:
393
+
394
+ - Discover destination templates with `sales-templates list`.
395
+ - Discover source sequences with `sales-content search --type SEQUENCE`.
396
+ - Fetch each source sequence with `sequences get`.
397
+ - Remap template IDs by `name + subject + normalized_body` similarity.
398
+ - Require similarity ratio `>= 0.95`.
399
+ - Fail if any template is unmappable.
400
+ - Remap owners from `--owner-map`.
401
+ - Default missing owner to running user only when explicitly allowed by policy.
402
+ - Strip server-set fields from sequence, steps, and nested patterns:
403
+ - `id`
404
+ - `sequenceId`
405
+ - `sequenceStepId`
406
+ - `createdAt`
407
+ - `updatedAt`
408
+ - `deletedAt`
409
+ - Preserve dependency fields:
410
+ - `requiredByStepOrder`
411
+ - `reliesOnStepOrder`
412
+ - `branchNumber`
413
+ - Create destination sequence.
414
+ - Set destination permissions with explicit team/user mapping.
415
+ - Never default to `visibleToAll`.
416
+ - Output CSV: `srcId,dstId,status,notes`.
417
+
418
+ Known gotchas:
419
+
420
+ - `active: "INACTIVE"` is overridden by the API to active.
421
+ - New sequences are safe only while they have no enrollments.
422
+ - Permissions in `sequenceEgg` are ignored.
423
+ - Permissions must be set with separate `PUT /api/contentpermissions/v1/permissions`.
424
+ - Dependencies in `sequenceEgg` are respected.
425
+ - The API appends an automatic `FINISH_ENROLLMENT` step.
426
+ - Expected step count: `dst.steps.length = src.steps.length + 1`.
427
+
428
+ ## 9. Existing Command Updates
429
+
430
+ ### `hscli doctor capabilities`
431
+
432
+ Add probes:
433
+
434
+ - `GET /api/sequences/v3/sequences/{id}`
435
+ - `POST /api/sequences/v3/sequences/create`
436
+ - `PUT /api/contentpermissions/v1/permissions`
437
+ - `POST /api/chirp-frontend-app/v1/gateway/com.hubspot.playbooks.rpc.PlaybooksRpc/getPlaybookCount`
438
+ - `GET /api/sales-templates/v2/templates/usage`
439
+
440
+ Each probe should report:
441
+
442
+ - status
443
+ - response time
444
+ - user permission scopes from `GET /api/login-verify/hub-user-info`
445
+
446
+ ### `hscli trace`
447
+
448
+ Add redaction for:
449
+
450
+ - `hubspotapi-csrf` cookie value
451
+ - `X-HubSpot-CSRF-hubspotapi` header value
452
+ - browser cookie values in cookie-mode requests
453
+
454
+ ### `hscli policy`
455
+
456
+ Add capabilities:
457
+
458
+ - `sequences.write`
459
+ - `sequences.delete`
460
+ - `permissions.write`
461
+ - `playbooks.write`
462
+
463
+ Policy defaults:
464
+
465
+ - `sequences.write`: require `--force`, dry-run first, optional time window
466
+ - `sequences.delete`: require `--force` and change ticket
467
+ - `permissions.write`: require `--force` and target-team allowlist
468
+ - `playbooks.write`: disabled by default because write endpoint is not proven
469
+
470
+ ## 10. Contract Tests
471
+
472
+ Gate with `HSCLI_ENABLE_SANDBOX_CONTRACT=1`.
473
+
474
+ Add:
475
+
476
+ - create test sequence
477
+ - fetch test sequence
478
+ - diff source/create output
479
+ - delete test sequence
480
+ - set permissions
481
+ - search and verify permissions
482
+ - reset permissions
483
+ - run full source-to-target sequence migration between two sandbox portals
484
+
485
+ ## Endpoint Catalog
486
+
487
+ | Verb | Path | Command | Auth |
488
+ |---|---|---|---|
489
+ | GET | `/api/sequences/v3/sequences/{id}` | `sequences get` | cookie |
490
+ | POST | `/api/sequences/v3/sequences/create` | `sequences create` | cookie |
491
+ | DELETE | `/api/sequences/v3/sequences/batch` | `sequences delete` | cookie |
492
+ | POST | `/api/salescontentsearch/v2/search` | `sales-content search`, `sequences list` | cookie |
493
+ | POST | `/api/crm-search/search` with `objectTypeId=0-68` | `sequences get --with-stats` | cookie |
494
+ | POST | `/api/crm-search/report` with `objectTypeId=0-68` | `sequences get --with-stats` | cookie |
495
+ | GET | `/api/sales-templates/v2/templates/{id}` | `sales-templates get` | cookie |
496
+ | GET | `/api/sales-templates/v2/templates/usage` | `sales-templates usage` | cookie |
497
+ | POST | `/api/sales-templates/v2/templates` | `sales-templates create` | cookie |
498
+ | GET | `/api/templates/v1/templates` | `sales-templates list` | cookie |
499
+ | POST | `/api/chirp-frontend-app/v1/gateway/com.hubspot.playbooks.rpc.PlaybooksRpc/listPlaybooks` | `playbooks list` | cookie + chirp |
500
+ | POST | `/api/chirp-frontend-app/v1/gateway/com.hubspot.playbooks.rpc.PlaybooksRpc/getPlaybookCount` | `playbooks usage` | cookie + chirp |
501
+ | POST | `/api/chirp-frontend-app/v1/gateway/com.hubspot.playbooks.rpc.PlaybookQuestionsRpc/getLatestQuestionsByPlaybook` | `playbooks questions` | cookie + chirp |
502
+ | PUT | `/api/contentpermissions/v1/permissions` | `permissions set` | cookie |
503
+ | GET | `/api/app-users/v1/teams` | `teams list` | cookie |
504
+ | GET | `/api/login-verify/hub-user-info` | internal doctor whoami | cookie |
@@ -170,6 +170,55 @@ Now every tool call the agent makes is filtered through the same rules.
170
170
  - Change tickets travel with the request and are written to telemetry.
171
171
  - Off-hours writes are rejected — no more 2am surprises.
172
172
 
173
+ ## Seal an approved action
174
+
175
+ Policies decide whether a request is allowed. Action contracts additionally
176
+ prove exactly which hscli arguments were reviewed. Create a draft without
177
+ credentials or shell commands:
178
+
179
+ ```json
180
+ {
181
+ "profile": "sandbox",
182
+ "portalId": "123456",
183
+ "createdAt": "2026-07-09T09:00:00.000Z",
184
+ "expiresAt": "2026-07-09T10:00:00.000Z",
185
+ "changeTicket": "CHG-12345",
186
+ "operation": {
187
+ "summary": "Update one approved contact property",
188
+ "commands": [
189
+ {
190
+ "argv": ["crm", "contacts", "update", "101", "--data", "{\"properties\":{\"lifecyclestage\":\"lead\"}}"],
191
+ "verifyArgv": ["crm", "contacts", "get", "101", "--properties", "lifecyclestage"]
192
+ }
193
+ ]
194
+ },
195
+ "limits": { "maxCommands": 1, "maxRecords": 1 }
196
+ }
197
+ ```
198
+
199
+ Seal and review it:
200
+
201
+ ```bash
202
+ hscli --json agent contract create --input ./draft.json | jq '.data' > ./contract.json
203
+ hscli --json --profile sandbox --change-ticket CHG-12345 \
204
+ agent contract verify ./contract.json
205
+ hscli --json --profile sandbox --change-ticket CHG-12345 \
206
+ agent contract apply ./contract.json
207
+ ```
208
+
209
+ The last command is still plan-only. Execution requires both approvals:
210
+
211
+ ```bash
212
+ hscli --json --profile sandbox --policy-file ./policy.json \
213
+ --change-ticket CHG-12345 --force \
214
+ agent contract apply ./contract.json --execute
215
+ ```
216
+
217
+ The runner checks the SHA-256 digest, deterministic contract ID, profile,
218
+ known portal ID, expiry, and ticket before starting. It invokes only hscli
219
+ argument arrays with `shell:false`; `agent`, `auth`, `internal`, `mcp`, and
220
+ global routing flags are forbidden inside contracts.
221
+
173
222
  ## Going further
174
223
 
175
224
  - Pair this with `hscli trace start` (see the [trace tutorial](trace-replay-repro.md)) to record every request + matched rule to a JSONL file.
@@ -3,10 +3,11 @@
3
3
  > See also: [ARCHITECTURE.md](ARCHITECTURE.md) · [SAFETY_MODEL.md](SAFETY_MODEL.md) · [ROADMAP_PHASE1_TO_3.md](ROADMAP_PHASE1_TO_3.md)
4
4
 
5
5
  ## WHY this CLI exists
6
- `hscli` exists to give operators a safe, automatable, production-grade command interface to HubSpot APIs without needing to write code each time. It solves three operational problems:
7
- - **Speed:** repetitive CRM operations become scriptable and repeatable.
8
- - **Safety:** write controls prevent accidental destructive mutations.
9
- - **Reliability:** consistent output + retry behavior reduce brittle ad-hoc scripts.
6
+ `hscli` exists to give operators and agents a safe, automatable, production-grade command interface to HubSpot without needing to write one-off scripts each time. HubSpot is moving toward an agent-ready platform where APIs, MCP, CLI, and connectors let agents operate the CRM; `hscli` focuses on the operator layer around that movement:
7
+ - **Speed:** repetitive CRM, migration, backup, and reporting work becomes scriptable and repeatable.
8
+ - **Safety:** write controls, profile boundaries, policy files, and change tickets prevent accidental destructive mutations.
9
+ - **Reliability:** consistent output, retry behavior, rate-limit handling, and trace/audit evidence reduce brittle ad-hoc scripts.
10
+ - **Context:** deterministic portal context, parity checks, and multi-portal plans give agents useful structure before they summarize or act.
10
11
 
11
12
  ## WHAT has been built (and what remains)
12
13
 
@@ -38,6 +39,11 @@
38
39
  - secret redaction in output/error serialization
39
40
  - profile environment isolation via `HSCLI_HOME`
40
41
  - request scope guard, timeout, retries, and correlation id
42
+ - Agent/operator workflows:
43
+ - deterministic `agent plan` runbooks
44
+ - read-only `context build` portal profiles
45
+ - `doctor parity` public/internal/hybrid/UI-only coverage map
46
+ - `portals list|plan` for explicit multi-portal management
41
47
  - Auth model expansion:
42
48
  - profile listing/inspection
43
49
  - OAuth authorize URL + code exchange
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@revfleet/hscli",
3
- "version": "0.8.11",
4
- "description": "Agentic HubSpot CLI + MCP server. Covers ~all documented public endpoints (1180 across 55+ command domains). Reachability is tier/scope/auth-model gated — UI-only surfaces and deprecated APIs excluded. Enterprise safety rails, self-hosted.",
3
+ "version": "0.9.0",
4
+ "description": "HubSpot operations and migration control plane for humans and agents, with deterministic runbooks, policy guardrails, and auditable evidence.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "hscli": "dist/cli.js"
@@ -15,6 +15,8 @@
15
15
  "test:contract": "HSCLI_ENABLE_SANDBOX_CONTRACT=1 vitest run tests/contract.sandbox.test.ts",
16
16
  "lint": "eslint src/ tests/",
17
17
  "lint:fix": "eslint src/ tests/ --fix",
18
+ "docs:mcp": "npm run build && node scripts/generate-mcp-catalog.mjs",
19
+ "docs:mcp:check": "npm run build && node scripts/generate-mcp-catalog.mjs --check",
18
20
  "release:checksums": "node scripts/release-checksums.mjs",
19
21
  "audit": "npm audit --audit-level=high",
20
22
  "release:verify": "npm run typecheck && npm run lint && npm test && npm run audit && npm run build && npm run release:checksums && shasum -a 256 -c release/checksums.sha256",