autotouch-cli 0.2.85__tar.gz → 0.2.87__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/PKG-INFO +10 -6
  2. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/README.md +9 -5
  3. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/cli.py +1 -0
  4. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/commands/columns.py +76 -0
  5. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/commands/search.py +13 -7
  6. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/data/CLI_REFERENCE.md +16 -8
  7. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/data/cli-manifest.json +70 -10
  8. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/templates.py +97 -3
  9. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli.egg-info/PKG-INFO +10 -6
  10. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_shared/linkedin_contract.py +2 -0
  11. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_shared/provider_registry.py +34 -1
  12. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_shared/search_contract.py +25 -22
  13. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/pyproject.toml +1 -1
  14. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/MANIFEST.in +0 -0
  15. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/__init__.py +0 -0
  16. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/cli_contracts.py +0 -0
  17. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/commands/__init__.py +0 -0
  18. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/commands/agents.py +0 -0
  19. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/commands/auth.py +0 -0
  20. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/commands/cells.py +0 -0
  21. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/commands/jobs.py +0 -0
  22. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/commands/leads.py +0 -0
  23. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/commands/linkedin.py +0 -0
  24. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/commands/list_build.py +0 -0
  25. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/commands/prompts.py +0 -0
  26. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/commands/rows.py +0 -0
  27. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/commands/sequences.py +0 -0
  28. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/commands/tables.py +0 -0
  29. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/commands/tasks.py +0 -0
  30. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/commands/webhooks.py +0 -0
  31. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/commands/workspace_secrets.py +0 -0
  32. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/core/__init__.py +0 -0
  33. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/core/auth.py +0 -0
  34. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/core/config.py +0 -0
  35. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/core/csv_import.py +0 -0
  36. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/core/http.py +0 -0
  37. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/core/io.py +0 -0
  38. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/core/output.py +0 -0
  39. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/core/polling.py +0 -0
  40. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/core/run.py +0 -0
  41. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/core/validation.py +0 -0
  42. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/exceptions.py +0 -0
  43. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/mongo_status.py +0 -0
  44. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/parser.py +0 -0
  45. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/parser_groups.py +0 -0
  46. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli/sequence_support.py +0 -0
  47. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli.egg-info/SOURCES.txt +0 -0
  48. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli.egg-info/dependency_links.txt +0 -0
  49. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli.egg-info/entry_points.txt +0 -0
  50. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli.egg-info/requires.txt +0 -0
  51. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_cli.egg-info/top_level.txt +0 -0
  52. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_shared/__init__.py +0 -0
  53. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_shared/linkedin_filters.py +0 -0
  54. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/autotouch_shared/list_build_contract.py +0 -0
  55. {autotouch_cli-0.2.85 → autotouch_cli-0.2.87}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: autotouch-cli
3
- Version: 0.2.85
3
+ Version: 0.2.87
4
4
  Summary: Autotouch Smart Table CLI
5
5
  Project-URL: Homepage, https://app.autotouch.ai
6
6
  Project-URL: Documentation, https://github.com/nicolonic/autotouch_main/tree/main/docs/research-table/reference
@@ -132,7 +132,7 @@ autotouch rows get --table-id "$TABLE_ID" --row-id "$ROW_ID" --output json
132
132
  - Inspect rows: `autotouch rows list`, `autotouch rows get`
133
133
  - Inspect one cell: `autotouch cells get`
134
134
  - Create a workflow column: `autotouch columns recipe`, `autotouch columns create`
135
- - Run neural search for niche/specific company or people discovery: `autotouch search companies`, `autotouch search people`
135
+ - Run Exa Company Search for basic company discovery, competitors, and lookalikes: `autotouch search companies`, `autotouch search similar-companies`
136
136
  - Inspect list-build inputs: `autotouch list-build inputs`
137
137
  - Build durable company and lead lists on Smart Table workers: `autotouch list-build companies`, `autotouch list-build leads`
138
138
  - Check connected LinkedIn account status/limits: `autotouch linkedin status`, `autotouch linkedin limits`
@@ -146,13 +146,17 @@ autotouch rows get --table-id "$TABLE_ID" --row-id "$ROW_ID" --output json
146
146
 
147
147
  Autotouch has two different company/people discovery paths.
148
148
 
149
- Use `autotouch search companies` and `autotouch search people` for neural search when the target is niche, semantic, or hard to express with structured filters. Examples: "AI workflow platforms for healthcare", "AI workflow startups selling to law firms", or "RevOps operators at PLG SaaS companies". Neural company/people search returns at most 10 results per API call and costs 1 credit per API call.
149
+ Use `autotouch search companies` for Exa Company Search when the target is basic company discovery, competitors, lookalikes, or a semantic company search. Put company constraints directly in natural language: industry, geography, funding, headcount, technology, founded date, and similarity. Examples: "AI workflow platforms for healthcare", "US education software startups focused on AI math tutoring with 1-50 employees", or "companies like Rippling for HR and workforce management". Exa company search returns up to 100 results per API call and costs 1 credit per 10 returned results.
150
150
 
151
- Use `autotouch list-build companies` and `autotouch list-build leads` for structured, repeatable list construction. This is the only CLI path for building LinkedIn-sourced company and lead lists. Treat list builds as the sourcing step for the Smart Table research workspace: build company/account records first by default, add the records to a research table, then enrich, score, segment, find related leads, attach signals/notes, and continue downstream workflows from that table. Use lead builds directly when the request is explicitly person/contact focused. This path uses Autotouch-managed provider access, so users do not need to connect their own LinkedIn account. Use filters like geography IDs, company size, industry IDs, title/persona, and current company IDs; call `autotouch list-build inputs` for supported values. Durable list builds run as background jobs with status/results endpoints and cost 1 credit per successful non-empty result page.
151
+ Use `autotouch search similar-companies` as a convenience wrapper for Exa Company Search similarity. Prefer `--query` for Exa's documented semantic patterns, such as "companies like Notion", "competitors of Stripe", or "companies working on making space travel cheaper". The older seed fields `company_name`, `company_domain`, and `company_description` remain available when you want the CLI/API to compose that query; it does not use Exa `/findSimilar`.
152
+
153
+ Use `autotouch search people` for provider-hidden people search when the target is niche, semantic, or hard to express with structured filters. People search currently returns at most 10 results per API call and costs 1 credit per API call.
154
+
155
+ Use `autotouch list-build companies` and `autotouch list-build leads` for structured, repeatable LinkedIn/Sales Navigator list construction. This is the only CLI path for building LinkedIn-sourced company and lead lists. Treat list builds as the sourcing step for the Smart Table research workspace: build company/account records first by default, add the records to a research table, then enrich, score, segment, find related leads, attach signals/notes, and continue downstream workflows from that table. Use lead builds directly when the request is explicitly person/contact focused. This path uses Autotouch-managed provider access, so users do not need to connect their own LinkedIn account. Use filters like geography IDs, company size, industry IDs, title/persona, and current company IDs; call `autotouch list-build inputs` for supported values. Durable list builds run as background jobs with status/results endpoints and cost 1 credit per successful non-empty result page.
152
156
 
153
157
  For structured company builds, start with industry IDs plus geo/company-size filters. User-supplied `keywords` are optional when structured filters are present; use them only to refine, disambiguate, or recover hard-to-classify targets instead of carrying the whole target definition.
154
158
 
155
- For account-first prospecting, build companies/accounts first, inspect the returned company IDs, then pass those IDs to `autotouch list-build leads --current-company-id ...`. Do not use neural search as a default pre-step for every list build; use it when the user's target is genuinely semantic or niche.
159
+ For account-first prospecting from LinkedIn/Sales Navigator, build companies/accounts first, inspect the returned company IDs, then pass those IDs to `autotouch list-build leads --current-company-id ...`. Do not use Exa company search as a default pre-step for every LinkedIn list build; use it when the user's target is genuinely semantic, niche, competitor-based, or not meant to be LinkedIn-sourced.
156
160
 
157
161
  ## More
158
162
 
@@ -160,7 +164,7 @@ For automation or agent-driven setup, use:
160
164
  - `autotouch cli-manifest --output json` for the local machine-readable command contract
161
165
  - `autotouch cli-reference` for the shipped parser-generated reference
162
166
  - `autotouch capabilities --output json` for provider/workflow contracts
163
- - `autotouch --version` should be `0.2.85` or newer for structured company list builds without user-supplied keywords, stored list-build input lookup, research-workspace list-build guidance, and the cleaned single LinkedIn-sourced list-build path
167
+ - `autotouch --version` should be `0.2.87` or newer for structured company list builds without user-supplied keywords, stored list-build input lookup, research-workspace list-build guidance, the cleaned single LinkedIn-sourced list-build path, and Exa Company Search up to 100 results
164
168
  - `autotouch capabilities --output json --select list_builds` for documented list-build inputs such as geography IDs, company size buckets, profile language, and company IDs
165
169
  - `autotouch list-build inputs` and `autotouch list-build pricing` before creating durable list-build jobs
166
170
  - `autotouch list-build companies` and `autotouch list-build leads` for durable LinkedIn-sourced company and lead list builds with Smart Table-owned background workers, visible progress, and no user-owned LinkedIn connection requirement
@@ -107,7 +107,7 @@ autotouch rows get --table-id "$TABLE_ID" --row-id "$ROW_ID" --output json
107
107
  - Inspect rows: `autotouch rows list`, `autotouch rows get`
108
108
  - Inspect one cell: `autotouch cells get`
109
109
  - Create a workflow column: `autotouch columns recipe`, `autotouch columns create`
110
- - Run neural search for niche/specific company or people discovery: `autotouch search companies`, `autotouch search people`
110
+ - Run Exa Company Search for basic company discovery, competitors, and lookalikes: `autotouch search companies`, `autotouch search similar-companies`
111
111
  - Inspect list-build inputs: `autotouch list-build inputs`
112
112
  - Build durable company and lead lists on Smart Table workers: `autotouch list-build companies`, `autotouch list-build leads`
113
113
  - Check connected LinkedIn account status/limits: `autotouch linkedin status`, `autotouch linkedin limits`
@@ -121,13 +121,17 @@ autotouch rows get --table-id "$TABLE_ID" --row-id "$ROW_ID" --output json
121
121
 
122
122
  Autotouch has two different company/people discovery paths.
123
123
 
124
- Use `autotouch search companies` and `autotouch search people` for neural search when the target is niche, semantic, or hard to express with structured filters. Examples: "AI workflow platforms for healthcare", "AI workflow startups selling to law firms", or "RevOps operators at PLG SaaS companies". Neural company/people search returns at most 10 results per API call and costs 1 credit per API call.
124
+ Use `autotouch search companies` for Exa Company Search when the target is basic company discovery, competitors, lookalikes, or a semantic company search. Put company constraints directly in natural language: industry, geography, funding, headcount, technology, founded date, and similarity. Examples: "AI workflow platforms for healthcare", "US education software startups focused on AI math tutoring with 1-50 employees", or "companies like Rippling for HR and workforce management". Exa company search returns up to 100 results per API call and costs 1 credit per 10 returned results.
125
125
 
126
- Use `autotouch list-build companies` and `autotouch list-build leads` for structured, repeatable list construction. This is the only CLI path for building LinkedIn-sourced company and lead lists. Treat list builds as the sourcing step for the Smart Table research workspace: build company/account records first by default, add the records to a research table, then enrich, score, segment, find related leads, attach signals/notes, and continue downstream workflows from that table. Use lead builds directly when the request is explicitly person/contact focused. This path uses Autotouch-managed provider access, so users do not need to connect their own LinkedIn account. Use filters like geography IDs, company size, industry IDs, title/persona, and current company IDs; call `autotouch list-build inputs` for supported values. Durable list builds run as background jobs with status/results endpoints and cost 1 credit per successful non-empty result page.
126
+ Use `autotouch search similar-companies` as a convenience wrapper for Exa Company Search similarity. Prefer `--query` for Exa's documented semantic patterns, such as "companies like Notion", "competitors of Stripe", or "companies working on making space travel cheaper". The older seed fields `company_name`, `company_domain`, and `company_description` remain available when you want the CLI/API to compose that query; it does not use Exa `/findSimilar`.
127
+
128
+ Use `autotouch search people` for provider-hidden people search when the target is niche, semantic, or hard to express with structured filters. People search currently returns at most 10 results per API call and costs 1 credit per API call.
129
+
130
+ Use `autotouch list-build companies` and `autotouch list-build leads` for structured, repeatable LinkedIn/Sales Navigator list construction. This is the only CLI path for building LinkedIn-sourced company and lead lists. Treat list builds as the sourcing step for the Smart Table research workspace: build company/account records first by default, add the records to a research table, then enrich, score, segment, find related leads, attach signals/notes, and continue downstream workflows from that table. Use lead builds directly when the request is explicitly person/contact focused. This path uses Autotouch-managed provider access, so users do not need to connect their own LinkedIn account. Use filters like geography IDs, company size, industry IDs, title/persona, and current company IDs; call `autotouch list-build inputs` for supported values. Durable list builds run as background jobs with status/results endpoints and cost 1 credit per successful non-empty result page.
127
131
 
128
132
  For structured company builds, start with industry IDs plus geo/company-size filters. User-supplied `keywords` are optional when structured filters are present; use them only to refine, disambiguate, or recover hard-to-classify targets instead of carrying the whole target definition.
129
133
 
130
- For account-first prospecting, build companies/accounts first, inspect the returned company IDs, then pass those IDs to `autotouch list-build leads --current-company-id ...`. Do not use neural search as a default pre-step for every list build; use it when the user's target is genuinely semantic or niche.
134
+ For account-first prospecting from LinkedIn/Sales Navigator, build companies/accounts first, inspect the returned company IDs, then pass those IDs to `autotouch list-build leads --current-company-id ...`. Do not use Exa company search as a default pre-step for every LinkedIn list build; use it when the user's target is genuinely semantic, niche, competitor-based, or not meant to be LinkedIn-sourced.
131
135
 
132
136
  ## More
133
137
 
@@ -135,7 +139,7 @@ For automation or agent-driven setup, use:
135
139
  - `autotouch cli-manifest --output json` for the local machine-readable command contract
136
140
  - `autotouch cli-reference` for the shipped parser-generated reference
137
141
  - `autotouch capabilities --output json` for provider/workflow contracts
138
- - `autotouch --version` should be `0.2.85` or newer for structured company list builds without user-supplied keywords, stored list-build input lookup, research-workspace list-build guidance, and the cleaned single LinkedIn-sourced list-build path
142
+ - `autotouch --version` should be `0.2.87` or newer for structured company list builds without user-supplied keywords, stored list-build input lookup, research-workspace list-build guidance, the cleaned single LinkedIn-sourced list-build path, and Exa Company Search up to 100 results
139
143
  - `autotouch capabilities --output json --select list_builds` for documented list-build inputs such as geography IDs, company size buckets, profile language, and company IDs
140
144
  - `autotouch list-build inputs` and `autotouch list-build pricing` before creating durable list-build jobs
141
145
  - `autotouch list-build companies` and `autotouch list-build leads` for durable LinkedIn-sourced company and lead list builds with Smart Table-owned background workers, visible progress, and no user-owned LinkedIn connection requirement
@@ -2398,6 +2398,7 @@ cmd_auth_schema = _bound("auth_schema")
2398
2398
  cmd_auth_whoami = _bound("auth_whoami")
2399
2399
  cmd_cells_get = _bound("cells_get")
2400
2400
  cmd_columns_create = _bound("columns_create")
2401
+ cmd_columns_update = _bound("columns_update")
2401
2402
  cmd_columns_move = _bound("columns_move")
2402
2403
  cmd_columns_projections = _bound("columns_projections")
2403
2404
  cmd_columns_run_next = _bound("columns_run_next")
@@ -7,6 +7,16 @@ from typing import Any, Callable, Dict, Sequence
7
7
  from autotouch_cli.exceptions import AutotouchCreditError, AutotouchInputError
8
8
 
9
9
 
10
+ _RESEARCH_MODE_ALIASES = {
11
+ "lite": "lite",
12
+ "light": "lite",
13
+ "quick": "lite",
14
+ "heavy": "heavy",
15
+ "deep": "heavy",
16
+ "thorough": "heavy",
17
+ }
18
+
19
+
10
20
  @dataclass(frozen=True)
11
21
  class ColumnCommandRuntime:
12
22
  resolve_token: Callable[[str | None, bool], str | None]
@@ -52,6 +62,54 @@ def _apply_column_position_overrides(
52
62
  return merged
53
63
 
54
64
 
65
+ def _normalize_research_mode_option(value: Any) -> str:
66
+ normalized = str(value or "").strip().lower()
67
+ if normalized in _RESEARCH_MODE_ALIASES:
68
+ return _RESEARCH_MODE_ALIASES[normalized]
69
+ raise AutotouchInputError("--research-mode must be one of: lite, heavy")
70
+
71
+
72
+ def _apply_research_mode_overrides(
73
+ payload: Dict[str, Any],
74
+ args: argparse.Namespace,
75
+ *,
76
+ force_agent_on_explicit: bool,
77
+ ) -> Dict[str, Any]:
78
+ explicit_value = getattr(args, "research_mode", None)
79
+ config = payload.get("config")
80
+ if not isinstance(config, dict):
81
+ if explicit_value is None:
82
+ return payload
83
+ config = {}
84
+
85
+ current_mode = str(config.get("mode") or "").strip().lower()
86
+ has_research_mode = (
87
+ config.get("researchMode") is not None
88
+ or config.get("research_mode") is not None
89
+ )
90
+ should_default_agent_lite = current_mode == "agent" and not has_research_mode
91
+ should_clean_agent_config = current_mode == "agent" and has_research_mode
92
+ if explicit_value is None and not should_default_agent_lite and not should_clean_agent_config:
93
+ return payload
94
+
95
+ merged = dict(payload)
96
+ next_config = dict(config)
97
+ if explicit_value is not None:
98
+ if force_agent_on_explicit:
99
+ next_config["mode"] = "agent"
100
+ next_config["researchMode"] = _normalize_research_mode_option(explicit_value)
101
+ elif config.get("researchMode") is not None:
102
+ next_config["researchMode"] = _normalize_research_mode_option(config.get("researchMode"))
103
+ elif config.get("research_mode") is not None:
104
+ next_config["researchMode"] = _normalize_research_mode_option(config.get("research_mode"))
105
+ else:
106
+ next_config["researchMode"] = "lite"
107
+ next_config.pop("research_mode", None)
108
+ next_config.pop("lite", None)
109
+ merged["config"] = next_config
110
+ return merged
111
+
112
+
55
113
  def _normalize_http_request_test_payload(payload: Dict[str, Any]) -> Dict[str, Any]:
56
114
  candidate = payload
57
115
  config = payload.get("config")
@@ -111,6 +169,7 @@ def cmd_columns_create(args: argparse.Namespace, *, runtime: ColumnCommandRuntim
111
169
  allow_position=True,
112
170
  allow_neighbors=True,
113
171
  )
172
+ payload = _apply_research_mode_overrides(payload, args, force_agent_on_explicit=True)
114
173
  runtime.validate_column_contract_payload_or_exit(payload)
115
174
  runtime.validate_add_to_crm_create_payload(payload)
116
175
  data = runtime.request_api(
@@ -156,6 +215,7 @@ def cmd_columns_update(args: argparse.Namespace, *, runtime: ColumnCommandRuntim
156
215
  allow_position=True,
157
216
  allow_neighbors=False,
158
217
  )
218
+ payload = _apply_research_mode_overrides(payload, args, force_agent_on_explicit=False)
159
219
  runtime.validate_column_contract_payload_or_exit(payload)
160
220
  data = runtime.request_api(
161
221
  "PATCH",
@@ -401,6 +461,11 @@ def register_columns_subcommands(
401
461
  pcc.add_argument("--position", type=float, help="Explicit numeric position override")
402
462
  pcc.add_argument("--after-column-id", help="Place the new column after this existing column id")
403
463
  pcc.add_argument("--before-column-id", help="Place the new column before this existing column id")
464
+ pcc.add_argument(
465
+ "--research-mode",
466
+ choices=["lite", "heavy"],
467
+ help="For LLM Research Agent columns, set config.researchMode. Omitted agent columns default to lite.",
468
+ )
404
469
  add_api_common_arguments(pcc)
405
470
  pcc.set_defaults(func=handlers["create"])
406
471
 
@@ -417,6 +482,11 @@ def register_columns_subcommands(
417
482
  pcu.add_argument("--data-json", help="ColumnUpdate payload JSON")
418
483
  pcu.add_argument("--data-file", help="ColumnUpdate payload file path")
419
484
  pcu.add_argument("--position", type=float, help="Explicit numeric position override")
485
+ pcu.add_argument(
486
+ "--research-mode",
487
+ choices=["lite", "heavy"],
488
+ help="Set config.researchMode. Include config.mode=agent when switching a Quick LLM column to Research Agent.",
489
+ )
420
490
  add_api_common_arguments(pcu)
421
491
  pcu.set_defaults(func=handlers["update"])
422
492
 
@@ -545,6 +615,12 @@ def register_columns_subcommands(
545
615
 
546
616
  pcre = col_sub.add_parser("recipe", help="Print copy-ready ColumnCreate payload templates")
547
617
  pcre.add_argument("--type", choices=["all", *column_recipe_types], default="all")
618
+ pcre.add_argument(
619
+ "--research-mode",
620
+ choices=["lite", "heavy"],
621
+ default="lite",
622
+ help="Research Agent depth for LLM recipes (default: lite; heavy costs more per row)",
623
+ )
548
624
  pcre.add_argument("--out-file", help="Write template JSON to file (single type writes payload only)")
549
625
  add_output_formatting_arguments(pcre)
550
626
  pcre.set_defaults(func=handlers["recipe"])
@@ -67,7 +67,7 @@ def cmd_search_companies(args: argparse.Namespace, *, runtime: SearchCommandRunt
67
67
  runtime=runtime,
68
68
  default_payload={
69
69
  "query": str(getattr(args, "query", "") or "").strip(),
70
- "limit": max(1, min(int(getattr(args, "limit", 10) or 10), 10)),
70
+ "limit": max(1, min(int(getattr(args, "limit", 10) or 10), 100)),
71
71
  },
72
72
  )
73
73
  data = runtime.request_api(
@@ -120,11 +120,16 @@ def cmd_search_people(args: argparse.Namespace, *, runtime: SearchCommandRuntime
120
120
  def cmd_search_similar_companies(args: argparse.Namespace, *, runtime: SearchCommandRuntime) -> None:
121
121
  token = runtime.resolve_token(args.token, required=True)
122
122
  payload: Dict[str, Any] = {
123
- "company_name": str(getattr(args, "company_name", "") or "").strip(),
124
- "limit": max(1, min(int(getattr(args, "limit", 10) or 10), 10)),
123
+ "limit": max(1, min(int(getattr(args, "limit", 10) or 10), 100)),
125
124
  }
125
+ query = str(getattr(args, "query", "") or "").strip()
126
+ company_name = str(getattr(args, "company_name", "") or "").strip()
126
127
  company_domain = str(getattr(args, "company_domain", "") or "").strip()
127
128
  company_description = str(getattr(args, "company_description", "") or "").strip()
129
+ if query:
130
+ payload["query"] = query
131
+ if company_name:
132
+ payload["company_name"] = company_name
128
133
  if company_domain:
129
134
  payload["company_domain"] = company_domain
130
135
  if company_description:
@@ -264,9 +269,9 @@ def register_search_subcommands(
264
269
  add_api_common_arguments(recipe_parser)
265
270
  recipe_parser.set_defaults(func=handlers["recipe"])
266
271
 
267
- companies_parser = search_sub.add_parser("companies", help="Find companies with provider-hidden neural search")
268
- companies_parser.add_argument("--query", help="Free-text company query")
269
- companies_parser.add_argument("--limit", type=int, default=10, help="Max results, 1-10 (default: 10)")
272
+ companies_parser = search_sub.add_parser("companies", help="Find companies with Exa Company Search")
273
+ companies_parser.add_argument("--query", help="Natural-language company query")
274
+ companies_parser.add_argument("--limit", type=int, default=10, help="Max results, 1-100 (default: 10)")
270
275
  companies_parser.add_argument("--data-json", help="Explicit request payload JSON")
271
276
  companies_parser.add_argument("--data-file", help="Path to request payload JSON file")
272
277
  add_api_common_arguments(companies_parser)
@@ -284,10 +289,11 @@ def register_search_subcommands(
284
289
  people_parser.set_defaults(func=handlers["people"])
285
290
 
286
291
  similar_parser = search_sub.add_parser("similar-companies", help="Find companies similar to a seed company")
292
+ similar_parser.add_argument("--query", help="Exa Company Search semantic similarity query, e.g. 'companies like Notion for enterprise knowledge management'")
287
293
  similar_parser.add_argument("--company-name", help="Seed company name")
288
294
  similar_parser.add_argument("--company-domain", help="Official company domain to ground similarity search")
289
295
  similar_parser.add_argument("--company-description", help="Short semantic company description to improve peer matching")
290
- similar_parser.add_argument("--limit", type=int, default=10, help="Max results, 1-10 (default: 10)")
296
+ similar_parser.add_argument("--limit", type=int, default=10, help="Max results, 1-100 (default: 10)")
291
297
  similar_parser.add_argument("--data-json", help="Explicit request payload JSON")
292
298
  similar_parser.add_argument("--data-file", help="Path to request payload JSON file")
293
299
  add_api_common_arguments(similar_parser)
@@ -1,6 +1,6 @@
1
1
  # Autotouch CLI Reference
2
2
 
3
- Generated from the installed parser for `autotouch-cli` `0.2.85`.
3
+ Generated from the installed parser for `autotouch-cli` `0.2.87`.
4
4
  Manifest schema version: `2`.
5
5
 
6
6
  ## Output Modes
@@ -1079,6 +1079,7 @@ For llm_enrichment, there are two valid authoring paths:
1079
1079
  [--data-file DATA_FILE] [--position POSITION]
1080
1080
  [--after-column-id AFTER_COLUMN_ID]
1081
1081
  [--before-column-id BEFORE_COLUMN_ID]
1082
+ [--research-mode {lite,heavy}]
1082
1083
  [--base-url BASE_URL] [--token TOKEN]
1083
1084
  [--use-x-api-key] [--timeout TIMEOUT]
1084
1085
  [--output {json,ndjson,human}] [--compact]
@@ -1100,6 +1101,7 @@ The runnable prompt is always config.advancedPrompt before execution.
1100
1101
  - `--position` (kind=number): Explicit numeric position override
1101
1102
  - `--after-column-id` (kind=string): Place the new column after this existing column id
1102
1103
  - `--before-column-id` (kind=string): Place the new column before this existing column id
1104
+ - `--research-mode` (kind=string; choices=lite,heavy): For LLM Research Agent columns, set config.researchMode. Omitted agent columns default to lite.
1103
1105
  - `--base-url` (kind=string; default=https://app.autotouch.ai): API base URL (default: https://app.autotouch.ai)
1104
1106
  - `--token` (kind=string; sensitive): Developer API key / JWT token
1105
1107
  - `--use-x-api-key` (kind=boolean; when omitted=False; when present=True): Send token via X-API-Key header
@@ -1320,13 +1322,15 @@ Print copy-ready ColumnCreate payload templates
1320
1322
  - Output modes: `json, ndjson, human`
1321
1323
  - Example:
1322
1324
  - `autotouch columns recipe [-h]
1323
- [--type {all,formatter,llm_enrichment,email_finder,phone_finder,lead_finder,add_to_crm,sync_to_table,add_to_sequence,http_request,add_to_leads,sync_to_leads}]
1325
+ [--type {all,formatter,llm_enrichment,email_finder,phone_finder,lead_finder,add_to_crm,sync_to_table,add_to_sequence,http_request,add_to_leads,sync_to_leads,sync_to_leads_array,add_to_leads_array}]
1326
+ [--research-mode {lite,heavy}]
1324
1327
  [--out-file OUT_FILE]
1325
1328
  [--output {json,ndjson,human}] [--compact]
1326
1329
  [--select SELECT |
1327
1330
  --json-pointer JSON_POINTER]`
1328
1331
  - Options:
1329
- - `--type` (kind=string; choices=all,formatter,llm_enrichment,email_finder,phone_finder,lead_finder,add_to_crm,sync_to_table,add_to_sequence,http_request,add_to_leads,sync_to_leads; default=all)
1332
+ - `--type` (kind=string; choices=all,formatter,llm_enrichment,email_finder,phone_finder,lead_finder,add_to_crm,sync_to_table,add_to_sequence,http_request,add_to_leads,sync_to_leads,sync_to_leads_array,add_to_leads_array; default=all)
1333
+ - `--research-mode` (kind=string; choices=lite,heavy; default=lite): Research Agent depth for LLM recipes (default: lite; heavy costs more per row)
1330
1334
  - `--out-file` (kind=file; input=file): Write template JSON to file (single type writes payload only)
1331
1335
  - `--output` (kind=string; choices=json,ndjson,human; default=json): Output mode
1332
1336
  - `--compact` (kind=boolean; when omitted=False; when present=True): Print compact JSON
@@ -1538,6 +1542,7 @@ Update a column definition. Nested config keys merge into the existing config, s
1538
1542
  - `autotouch columns update [-h] --table-id TABLE_ID --column-id COLUMN_ID
1539
1543
  [--data-json DATA_JSON]
1540
1544
  [--data-file DATA_FILE] [--position POSITION]
1545
+ [--research-mode {lite,heavy}]
1541
1546
  [--base-url BASE_URL] [--token TOKEN]
1542
1547
  [--use-x-api-key] [--timeout TIMEOUT]
1543
1548
  [--output {json,ndjson,human}] [--compact]
@@ -1549,6 +1554,7 @@ Update a column definition. Nested config keys merge into the existing config, s
1549
1554
  - `--data-json` (kind=json; input=json): ColumnUpdate payload JSON
1550
1555
  - `--data-file` (kind=file; input=file): ColumnUpdate payload file path
1551
1556
  - `--position` (kind=number): Explicit numeric position override
1557
+ - `--research-mode` (kind=string; choices=lite,heavy): Set config.researchMode. Include config.mode=agent when switching a Quick LLM column to Research Agent.
1552
1558
  - `--base-url` (kind=string; default=https://app.autotouch.ai): API base URL (default: https://app.autotouch.ai)
1553
1559
  - `--token` (kind=string; sensitive): Developer API key / JWT token
1554
1560
  - `--use-x-api-key` (kind=boolean; when omitted=False; when present=True): Send token via X-API-Key header
@@ -3553,7 +3559,7 @@ Provider-hidden search operations
3553
3559
 
3554
3560
  #### `autotouch search companies`
3555
3561
 
3556
- Find companies with provider-hidden neural search
3562
+ Find companies with Exa Company Search
3557
3563
 
3558
3564
  - Auth: `developer_key_or_user_session`
3559
3565
  - Stability: `stable`
@@ -3569,8 +3575,8 @@ Find companies with provider-hidden neural search
3569
3575
  [--select SELECT |
3570
3576
  --json-pointer JSON_POINTER] [--verbose]`
3571
3577
  - Options:
3572
- - `--query` (kind=string): Free-text company query
3573
- - `--limit` (kind=integer; default=10): Max results, 1-10 (default: 10)
3578
+ - `--query` (kind=string): Natural-language company query
3579
+ - `--limit` (kind=integer; default=10): Max results, 1-100 (default: 10)
3574
3580
  - `--data-json` (kind=json; input=json): Explicit request payload JSON
3575
3581
  - `--data-file` (kind=file; input=file): Path to request payload JSON file
3576
3582
  - `--base-url` (kind=string; default=https://app.autotouch.ai): API base URL (default: https://app.autotouch.ai)
@@ -3805,7 +3811,8 @@ Find companies similar to a seed company
3805
3811
  - Destructive: `no`
3806
3812
  - Output modes: `json, ndjson, human`
3807
3813
  - Example:
3808
- - `autotouch search similar-companies [-h] [--company-name COMPANY_NAME]
3814
+ - `autotouch search similar-companies [-h] [--query QUERY]
3815
+ [--company-name COMPANY_NAME]
3809
3816
  [--company-domain COMPANY_DOMAIN]
3810
3817
  [--company-description COMPANY_DESCRIPTION]
3811
3818
  [--limit LIMIT]
@@ -3819,10 +3826,11 @@ Find companies similar to a seed company
3819
3826
  --json-pointer JSON_POINTER]
3820
3827
  [--verbose]`
3821
3828
  - Options:
3829
+ - `--query` (kind=string): Exa Company Search semantic similarity query, e.g. 'companies like Notion for enterprise knowledge management'
3822
3830
  - `--company-name` (kind=string): Seed company name
3823
3831
  - `--company-domain` (kind=string): Official company domain to ground similarity search
3824
3832
  - `--company-description` (kind=string): Short semantic company description to improve peer matching
3825
- - `--limit` (kind=integer; default=10): Max results, 1-10 (default: 10)
3833
+ - `--limit` (kind=integer; default=10): Max results, 1-100 (default: 10)
3826
3834
  - `--data-json` (kind=json; input=json): Explicit request payload JSON
3827
3835
  - `--data-file` (kind=file; input=file): Path to request payload JSON file
3828
3836
  - `--base-url` (kind=string; default=https://app.autotouch.ai): API base URL (default: https://app.autotouch.ai)
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.2.85",
2
+ "version": "0.2.87",
3
3
  "manifest_schema_version": 2,
4
4
  "entry_points": {
5
5
  "autotouch": "autotouch_cli.cli:main",
@@ -6304,7 +6304,7 @@
6304
6304
  ],
6305
6305
  "aliases": [],
6306
6306
  "group": "search",
6307
- "help": "Find companies with provider-hidden neural search",
6307
+ "help": "Find companies with Exa Company Search",
6308
6308
  "description": null,
6309
6309
  "notes": null,
6310
6310
  "required_flags": [],
@@ -6313,7 +6313,7 @@
6313
6313
  {
6314
6314
  "dest": "query",
6315
6315
  "required": false,
6316
- "help": "Free-text company query",
6316
+ "help": "Natural-language company query",
6317
6317
  "kind": "string",
6318
6318
  "action": "store",
6319
6319
  "flags": [
@@ -6324,7 +6324,7 @@
6324
6324
  {
6325
6325
  "dest": "limit",
6326
6326
  "required": false,
6327
- "help": "Max results, 1-10 (default: 10)",
6327
+ "help": "Max results, 1-100 (default: 10)",
6328
6328
  "kind": "integer",
6329
6329
  "action": "store",
6330
6330
  "default_when_omitted": 10,
@@ -6785,6 +6785,17 @@
6785
6785
  "required_flags": [],
6786
6786
  "positionals": [],
6787
6787
  "options": [
6788
+ {
6789
+ "dest": "query",
6790
+ "required": false,
6791
+ "help": "Exa Company Search semantic similarity query, e.g. 'companies like Notion for enterprise knowledge management'",
6792
+ "kind": "string",
6793
+ "action": "store",
6794
+ "flags": [
6795
+ "--query"
6796
+ ],
6797
+ "takes_value": true
6798
+ },
6788
6799
  {
6789
6800
  "dest": "company_name",
6790
6801
  "required": false,
@@ -6821,7 +6832,7 @@
6821
6832
  {
6822
6833
  "dest": "limit",
6823
6834
  "required": false,
6824
- "help": "Max results, 1-10 (default: 10)",
6835
+ "help": "Max results, 1-100 (default: 10)",
6825
6836
  "kind": "integer",
6826
6837
  "action": "store",
6827
6838
  "default_when_omitted": 10,
@@ -7008,7 +7019,7 @@
7008
7019
  },
7009
7020
  "availability": null,
7010
7021
  "examples": [
7011
- "autotouch search similar-companies [-h] [--company-name COMPANY_NAME]\n [--company-domain COMPANY_DOMAIN]\n [--company-description COMPANY_DESCRIPTION]\n [--limit LIMIT]\n [--data-json DATA_JSON]\n [--data-file DATA_FILE]\n [--base-url BASE_URL]\n [--token TOKEN] [--use-x-api-key]\n [--timeout TIMEOUT]\n [--output {json,ndjson,human}]\n [--compact] [--select SELECT |\n --json-pointer JSON_POINTER]\n [--verbose]"
7022
+ "autotouch search similar-companies [-h] [--query QUERY]\n [--company-name COMPANY_NAME]\n [--company-domain COMPANY_DOMAIN]\n [--company-description COMPANY_DESCRIPTION]\n [--limit LIMIT]\n [--data-json DATA_JSON]\n [--data-file DATA_FILE]\n [--base-url BASE_URL]\n [--token TOKEN] [--use-x-api-key]\n [--timeout TIMEOUT]\n [--output {json,ndjson,human}]\n [--compact] [--select SELECT |\n --json-pointer JSON_POINTER]\n [--verbose]"
7012
7023
  ],
7013
7024
  "stability": "stable",
7014
7025
  "handler": "_fn"
@@ -14852,6 +14863,21 @@
14852
14863
  ],
14853
14864
  "takes_value": true
14854
14865
  },
14866
+ {
14867
+ "dest": "research_mode",
14868
+ "required": false,
14869
+ "help": "For LLM Research Agent columns, set config.researchMode. Omitted agent columns default to lite.",
14870
+ "kind": "string",
14871
+ "action": "store",
14872
+ "choices": [
14873
+ "lite",
14874
+ "heavy"
14875
+ ],
14876
+ "flags": [
14877
+ "--research-mode"
14878
+ ],
14879
+ "takes_value": true
14880
+ },
14855
14881
  {
14856
14882
  "dest": "base_url",
14857
14883
  "required": false,
@@ -15005,7 +15031,7 @@
15005
15031
  },
15006
15032
  "availability": null,
15007
15033
  "examples": [
15008
- "autotouch columns create [-h] --table-id TABLE_ID\n [--data-json DATA_JSON]\n [--data-file DATA_FILE] [--position POSITION]\n [--after-column-id AFTER_COLUMN_ID]\n [--before-column-id BEFORE_COLUMN_ID]\n [--base-url BASE_URL] [--token TOKEN]\n [--use-x-api-key] [--timeout TIMEOUT]\n [--output {json,ndjson,human}] [--compact]\n [--select SELECT |\n --json-pointer JSON_POINTER] [--verbose]"
15034
+ "autotouch columns create [-h] --table-id TABLE_ID\n [--data-json DATA_JSON]\n [--data-file DATA_FILE] [--position POSITION]\n [--after-column-id AFTER_COLUMN_ID]\n [--before-column-id BEFORE_COLUMN_ID]\n [--research-mode {lite,heavy}]\n [--base-url BASE_URL] [--token TOKEN]\n [--use-x-api-key] [--timeout TIMEOUT]\n [--output {json,ndjson,human}] [--compact]\n [--select SELECT |\n --json-pointer JSON_POINTER] [--verbose]"
15009
15035
  ],
15010
15036
  "stability": "stable",
15011
15037
  "handler": "_fn"
@@ -15084,6 +15110,21 @@
15084
15110
  ],
15085
15111
  "takes_value": true
15086
15112
  },
15113
+ {
15114
+ "dest": "research_mode",
15115
+ "required": false,
15116
+ "help": "Set config.researchMode. Include config.mode=agent when switching a Quick LLM column to Research Agent.",
15117
+ "kind": "string",
15118
+ "action": "store",
15119
+ "choices": [
15120
+ "lite",
15121
+ "heavy"
15122
+ ],
15123
+ "flags": [
15124
+ "--research-mode"
15125
+ ],
15126
+ "takes_value": true
15127
+ },
15087
15128
  {
15088
15129
  "dest": "base_url",
15089
15130
  "required": false,
@@ -15237,7 +15278,7 @@
15237
15278
  },
15238
15279
  "availability": null,
15239
15280
  "examples": [
15240
- "autotouch columns update [-h] --table-id TABLE_ID --column-id COLUMN_ID\n [--data-json DATA_JSON]\n [--data-file DATA_FILE] [--position POSITION]\n [--base-url BASE_URL] [--token TOKEN]\n [--use-x-api-key] [--timeout TIMEOUT]\n [--output {json,ndjson,human}] [--compact]\n [--select SELECT |\n --json-pointer JSON_POINTER] [--verbose]"
15281
+ "autotouch columns update [-h] --table-id TABLE_ID --column-id COLUMN_ID\n [--data-json DATA_JSON]\n [--data-file DATA_FILE] [--position POSITION]\n [--research-mode {lite,heavy}]\n [--base-url BASE_URL] [--token TOKEN]\n [--use-x-api-key] [--timeout TIMEOUT]\n [--output {json,ndjson,human}] [--compact]\n [--select SELECT |\n --json-pointer JSON_POINTER] [--verbose]"
15241
15282
  ],
15242
15283
  "stability": "stable",
15243
15284
  "handler": "_fn"
@@ -17555,7 +17596,9 @@
17555
17596
  "add_to_sequence",
17556
17597
  "http_request",
17557
17598
  "add_to_leads",
17558
- "sync_to_leads"
17599
+ "sync_to_leads",
17600
+ "sync_to_leads_array",
17601
+ "add_to_leads_array"
17559
17602
  ],
17560
17603
  "default_when_omitted": "all",
17561
17604
  "default": "all",
@@ -17564,6 +17607,23 @@
17564
17607
  ],
17565
17608
  "takes_value": true
17566
17609
  },
17610
+ {
17611
+ "dest": "research_mode",
17612
+ "required": false,
17613
+ "help": "Research Agent depth for LLM recipes (default: lite; heavy costs more per row)",
17614
+ "kind": "string",
17615
+ "action": "store",
17616
+ "choices": [
17617
+ "lite",
17618
+ "heavy"
17619
+ ],
17620
+ "default_when_omitted": "lite",
17621
+ "default": "lite",
17622
+ "flags": [
17623
+ "--research-mode"
17624
+ ],
17625
+ "takes_value": true
17626
+ },
17567
17627
  {
17568
17628
  "dest": "out_file",
17569
17629
  "required": false,
@@ -17663,7 +17723,7 @@
17663
17723
  },
17664
17724
  "availability": null,
17665
17725
  "examples": [
17666
- "autotouch columns recipe [-h]\n [--type {all,formatter,llm_enrichment,email_finder,phone_finder,lead_finder,add_to_crm,sync_to_table,add_to_sequence,http_request,add_to_leads,sync_to_leads}]\n [--out-file OUT_FILE]\n [--output {json,ndjson,human}] [--compact]\n [--select SELECT |\n --json-pointer JSON_POINTER]"
17726
+ "autotouch columns recipe [-h]\n [--type {all,formatter,llm_enrichment,email_finder,phone_finder,lead_finder,add_to_crm,sync_to_table,add_to_sequence,http_request,add_to_leads,sync_to_leads,sync_to_leads_array,add_to_leads_array}]\n [--research-mode {lite,heavy}]\n [--out-file OUT_FILE]\n [--output {json,ndjson,human}] [--compact]\n [--select SELECT |\n --json-pointer JSON_POINTER]"
17667
17727
  ],
17668
17728
  "stability": "stable",
17669
17729
  "handler": "_fn"
@@ -51,6 +51,69 @@ COLUMN_RECIPE_ALIASES: Dict[str, Dict[str, Any]] = {
51
51
  "Emits the standard Sync to Leads payload. Internal provider/config names remain add_to_crm.",
52
52
  ],
53
53
  },
54
+ "sync_to_leads_array": {
55
+ "canonical": "add_to_crm",
56
+ "key": "sync_contact_list_to_leads",
57
+ "label": "Sync Contact List to Leads",
58
+ "payload": {
59
+ "key": "sync_contact_list_to_leads",
60
+ "label": "Sync Contact List to Leads",
61
+ "kind": "enrichment",
62
+ "dataType": "json",
63
+ "origin": "manual",
64
+ "autoRun": "onSourceUpdate",
65
+ "config": {
66
+ "provider": "add_to_crm",
67
+ "requireCompanyDomain": True,
68
+ "leadSource": "research_table_export",
69
+ "fieldMappings": {
70
+ "mode": "array",
71
+ "sourceColumn": "<CONTACT_LIST_COLUMN_KEY>",
72
+ "sourceArrayPath": "contacts",
73
+ "common": {
74
+ "companyDomain": "domain",
75
+ "companyName": "company_name",
76
+ },
77
+ "template": {
78
+ "linkedinUrl": "linkedin_url",
79
+ "firstName": "first_name",
80
+ "lastName": "last_name",
81
+ "title": "title",
82
+ "companyName": "company_name",
83
+ "companyDomain": "company_domain",
84
+ "emailAddresses": [
85
+ {"column": "work_email", "type": "work"},
86
+ {"column": "email", "type": "work"},
87
+ ],
88
+ "phoneNumbers": [
89
+ {"column": "mobile_phone", "type": "mobile"},
90
+ {"column": "phone", "type": "mobile"},
91
+ ],
92
+ },
93
+ },
94
+ "sourceColumns": [
95
+ "<CONTACT_LIST_COLUMN_KEY>",
96
+ "domain",
97
+ "company_name",
98
+ ],
99
+ },
100
+ },
101
+ "notes": [
102
+ "Use this when an upstream research column returns a contact/person array such as contacts[], people[], leads[], or items[] and the goal is to write Leads directly.",
103
+ "Set config.fieldMappings.sourceColumn to the upstream column key, for example key_buyer_leads.",
104
+ "Set config.fieldMappings.sourceArrayPath to the array path inside that JSON value; use contacts for key_buyer_leads.contacts[] or items for canonical items[].",
105
+ "Template fields are item-relative: linkedinUrl=linkedin_url reads each array item's linkedin_url.",
106
+ "Common fields are parent-row mappings used for every item, usually domain/company_name.",
107
+ "Prefer this over manual row extraction, local JSON files, and rows add/import when writing discovered contacts into Leads CRM.",
108
+ ],
109
+ },
110
+ "add_to_leads_array": {
111
+ "canonical": "add_to_crm",
112
+ "alias_of": "sync_to_leads_array",
113
+ "notes": [
114
+ "Legacy-friendly alias for sync_to_leads_array.",
115
+ ],
116
+ },
54
117
  }
55
118
  COLUMN_RECIPE_TYPES = tuple(dict.fromkeys([*COLUMN_RECIPE_BASE_TYPES, *COLUMN_RECIPE_ALIASES.keys()]))
56
119
  WORKFLOW_BLUEPRINT_TYPES = tuple(sorted(automation_workflow_blueprints().keys()))
@@ -103,11 +166,16 @@ def _build_column_recipe_catalog() -> Tuple[Dict[str, Dict[str, Any]], Dict[str,
103
166
  recipes[recipe_type] = payload
104
167
  notes[recipe_type] = contract.recipe_notes_list()
105
168
  for alias, metadata in COLUMN_RECIPE_ALIASES.items():
169
+ alias_of = str(metadata.get("alias_of") or "").strip()
170
+ if alias_of and alias_of in recipes:
171
+ recipes[alias] = json.loads(json.dumps(recipes[alias_of]))
172
+ notes[alias] = list(metadata.get("notes") or []) + list(notes.get(alias_of) or [])
173
+ continue
106
174
  canonical = str(metadata.get("canonical") or "").strip()
107
175
  canonical_payload = recipes.get(canonical)
108
176
  if not isinstance(canonical_payload, dict):
109
177
  continue
110
- alias_payload = json.loads(json.dumps(canonical_payload))
178
+ alias_payload = json.loads(json.dumps(metadata.get("payload") or canonical_payload))
111
179
  if metadata.get("key"):
112
180
  alias_payload["key"] = metadata["key"]
113
181
  if metadata.get("label"):
@@ -917,6 +985,32 @@ def _recipe_payload(recipe_type: str) -> Dict[str, Any]:
917
985
  return _json_copy(payload)
918
986
 
919
987
 
988
+ def _apply_recipe_research_mode(payload: Dict[str, Any], research_mode: Any) -> Dict[str, Any]:
989
+ config = payload.get("config")
990
+ if not isinstance(config, dict):
991
+ return payload
992
+ if str(config.get("mode") or "").strip().lower() != "agent":
993
+ return payload
994
+ mode = str(research_mode or "lite").strip().lower()
995
+ if mode not in {"lite", "heavy"}:
996
+ mode = "lite"
997
+ payload = dict(payload)
998
+ payload["config"] = {
999
+ **config,
1000
+ "researchMode": mode,
1001
+ }
1002
+ payload["config"].pop("research_mode", None)
1003
+ payload["config"].pop("lite", None)
1004
+ return payload
1005
+
1006
+
1007
+ def _recipe_payload_for_args(recipe_type: str, args: argparse.Namespace) -> Dict[str, Any]:
1008
+ return _apply_recipe_research_mode(
1009
+ _recipe_payload(recipe_type),
1010
+ getattr(args, "research_mode", "lite"),
1011
+ )
1012
+
1013
+
920
1014
  def _sequence_recipe_payload(recipe_type: str) -> Dict[str, Any]:
921
1015
  payload = SEQUENCE_RECIPES.get(recipe_type)
922
1016
  if not payload:
@@ -1639,7 +1733,7 @@ def emit_workflows_scaffold(args: argparse.Namespace, runtime: TemplateRuntime)
1639
1733
  def emit_columns_recipe(args: argparse.Namespace, runtime: TemplateRuntime) -> None:
1640
1734
  selected_type = str(args.type or "all")
1641
1735
  if selected_type == "all":
1642
- recipes = {name: _recipe_payload(name) for name in COLUMN_RECIPE_TYPES}
1736
+ recipes = {name: _recipe_payload_for_args(name, args) for name in COLUMN_RECIPE_TYPES}
1643
1737
  output = {
1644
1738
  "recipes": recipes,
1645
1739
  "notes": COLUMN_RECIPE_NOTES,
@@ -1662,7 +1756,7 @@ def emit_columns_recipe(args: argparse.Namespace, runtime: TemplateRuntime) -> N
1662
1756
  runtime.print_json(output, getattr(args, "compact", False))
1663
1757
  return
1664
1758
 
1665
- payload = _recipe_payload(selected_type)
1759
+ payload = _recipe_payload_for_args(selected_type, args)
1666
1760
  output_single = {
1667
1761
  "type": selected_type,
1668
1762
  "payload": payload,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: autotouch-cli
3
- Version: 0.2.85
3
+ Version: 0.2.87
4
4
  Summary: Autotouch Smart Table CLI
5
5
  Project-URL: Homepage, https://app.autotouch.ai
6
6
  Project-URL: Documentation, https://github.com/nicolonic/autotouch_main/tree/main/docs/research-table/reference
@@ -132,7 +132,7 @@ autotouch rows get --table-id "$TABLE_ID" --row-id "$ROW_ID" --output json
132
132
  - Inspect rows: `autotouch rows list`, `autotouch rows get`
133
133
  - Inspect one cell: `autotouch cells get`
134
134
  - Create a workflow column: `autotouch columns recipe`, `autotouch columns create`
135
- - Run neural search for niche/specific company or people discovery: `autotouch search companies`, `autotouch search people`
135
+ - Run Exa Company Search for basic company discovery, competitors, and lookalikes: `autotouch search companies`, `autotouch search similar-companies`
136
136
  - Inspect list-build inputs: `autotouch list-build inputs`
137
137
  - Build durable company and lead lists on Smart Table workers: `autotouch list-build companies`, `autotouch list-build leads`
138
138
  - Check connected LinkedIn account status/limits: `autotouch linkedin status`, `autotouch linkedin limits`
@@ -146,13 +146,17 @@ autotouch rows get --table-id "$TABLE_ID" --row-id "$ROW_ID" --output json
146
146
 
147
147
  Autotouch has two different company/people discovery paths.
148
148
 
149
- Use `autotouch search companies` and `autotouch search people` for neural search when the target is niche, semantic, or hard to express with structured filters. Examples: "AI workflow platforms for healthcare", "AI workflow startups selling to law firms", or "RevOps operators at PLG SaaS companies". Neural company/people search returns at most 10 results per API call and costs 1 credit per API call.
149
+ Use `autotouch search companies` for Exa Company Search when the target is basic company discovery, competitors, lookalikes, or a semantic company search. Put company constraints directly in natural language: industry, geography, funding, headcount, technology, founded date, and similarity. Examples: "AI workflow platforms for healthcare", "US education software startups focused on AI math tutoring with 1-50 employees", or "companies like Rippling for HR and workforce management". Exa company search returns up to 100 results per API call and costs 1 credit per 10 returned results.
150
150
 
151
- Use `autotouch list-build companies` and `autotouch list-build leads` for structured, repeatable list construction. This is the only CLI path for building LinkedIn-sourced company and lead lists. Treat list builds as the sourcing step for the Smart Table research workspace: build company/account records first by default, add the records to a research table, then enrich, score, segment, find related leads, attach signals/notes, and continue downstream workflows from that table. Use lead builds directly when the request is explicitly person/contact focused. This path uses Autotouch-managed provider access, so users do not need to connect their own LinkedIn account. Use filters like geography IDs, company size, industry IDs, title/persona, and current company IDs; call `autotouch list-build inputs` for supported values. Durable list builds run as background jobs with status/results endpoints and cost 1 credit per successful non-empty result page.
151
+ Use `autotouch search similar-companies` as a convenience wrapper for Exa Company Search similarity. Prefer `--query` for Exa's documented semantic patterns, such as "companies like Notion", "competitors of Stripe", or "companies working on making space travel cheaper". The older seed fields `company_name`, `company_domain`, and `company_description` remain available when you want the CLI/API to compose that query; it does not use Exa `/findSimilar`.
152
+
153
+ Use `autotouch search people` for provider-hidden people search when the target is niche, semantic, or hard to express with structured filters. People search currently returns at most 10 results per API call and costs 1 credit per API call.
154
+
155
+ Use `autotouch list-build companies` and `autotouch list-build leads` for structured, repeatable LinkedIn/Sales Navigator list construction. This is the only CLI path for building LinkedIn-sourced company and lead lists. Treat list builds as the sourcing step for the Smart Table research workspace: build company/account records first by default, add the records to a research table, then enrich, score, segment, find related leads, attach signals/notes, and continue downstream workflows from that table. Use lead builds directly when the request is explicitly person/contact focused. This path uses Autotouch-managed provider access, so users do not need to connect their own LinkedIn account. Use filters like geography IDs, company size, industry IDs, title/persona, and current company IDs; call `autotouch list-build inputs` for supported values. Durable list builds run as background jobs with status/results endpoints and cost 1 credit per successful non-empty result page.
152
156
 
153
157
  For structured company builds, start with industry IDs plus geo/company-size filters. User-supplied `keywords` are optional when structured filters are present; use them only to refine, disambiguate, or recover hard-to-classify targets instead of carrying the whole target definition.
154
158
 
155
- For account-first prospecting, build companies/accounts first, inspect the returned company IDs, then pass those IDs to `autotouch list-build leads --current-company-id ...`. Do not use neural search as a default pre-step for every list build; use it when the user's target is genuinely semantic or niche.
159
+ For account-first prospecting from LinkedIn/Sales Navigator, build companies/accounts first, inspect the returned company IDs, then pass those IDs to `autotouch list-build leads --current-company-id ...`. Do not use Exa company search as a default pre-step for every LinkedIn list build; use it when the user's target is genuinely semantic, niche, competitor-based, or not meant to be LinkedIn-sourced.
156
160
 
157
161
  ## More
158
162
 
@@ -160,7 +164,7 @@ For automation or agent-driven setup, use:
160
164
  - `autotouch cli-manifest --output json` for the local machine-readable command contract
161
165
  - `autotouch cli-reference` for the shipped parser-generated reference
162
166
  - `autotouch capabilities --output json` for provider/workflow contracts
163
- - `autotouch --version` should be `0.2.85` or newer for structured company list builds without user-supplied keywords, stored list-build input lookup, research-workspace list-build guidance, and the cleaned single LinkedIn-sourced list-build path
167
+ - `autotouch --version` should be `0.2.87` or newer for structured company list builds without user-supplied keywords, stored list-build input lookup, research-workspace list-build guidance, the cleaned single LinkedIn-sourced list-build path, and Exa Company Search up to 100 results
164
168
  - `autotouch capabilities --output json --select list_builds` for documented list-build inputs such as geography IDs, company size buckets, profile language, and company IDs
165
169
  - `autotouch list-build inputs` and `autotouch list-build pricing` before creating durable list-build jobs
166
170
  - `autotouch list-build companies` and `autotouch list-build leads` for durable LinkedIn-sourced company and lead list builds with Smart Table-owned background workers, visible progress, and no user-owned LinkedIn connection requirement
@@ -63,6 +63,8 @@ _LINKEDIN_CAPABILITIES: Dict[str, Any] = {
63
63
  "autotouch linkedin status",
64
64
  "autotouch linkedin limits",
65
65
  "autotouch list-build inputs",
66
+ "autotouch list-build inputs --type geo --query 'United States'",
67
+ "autotouch list-build inputs --type industry --query 'software'",
66
68
  "autotouch list-build companies --geo-id 103644278 --industry-id <industry_id> --company-size 1-10 --num-results 100 --wait",
67
69
  ],
68
70
  }
@@ -378,6 +378,7 @@ _PROVIDER_CONTRACTS: Tuple[ResearchTableProviderContract, ...] = (
378
378
  "config": {
379
379
  "instructions": "Research the company in this row and return JSON with icp_fit, summary, and risks.",
380
380
  "mode": "agent",
381
+ "researchMode": "lite",
381
382
  "temperature": 0.7,
382
383
  "promptSource": "generated",
383
384
  "useCompanyContext": True,
@@ -397,6 +398,7 @@ _PROVIDER_CONTRACTS: Tuple[ResearchTableProviderContract, ...] = (
397
398
  "Runtime only injects values for placeholders the prompt explicitly references.",
398
399
  "Manual/basic prompts should use explicit placeholders like {{company_name}}, {{domain}}, or {{user_value_proposition}} when those values are needed.",
399
400
  "Agent mode is JSON-oriented.",
401
+ "Agent research depth lives in config.researchMode and defaults to lite; set heavy only when the broader 2-credit research pass is needed.",
400
402
  "Basic mode can be text or JSON; JSON requires dataType=json.",
401
403
  ),
402
404
  column_type={
@@ -835,16 +837,47 @@ _PROVIDER_CONTRACTS: Tuple[ResearchTableProviderContract, ...] = (
835
837
  {"linkedinUrl": {"column": "linkedin_lookup", "path": "linkedin_url"}},
836
838
  ],
837
839
  },
840
+ "array_mode": {
841
+ "mode": "config.fieldMappings.mode=array",
842
+ "source_column_field": "config.fieldMappings.sourceColumn",
843
+ "array_path_field": "config.fieldMappings.sourceArrayPath",
844
+ "default_array_path": "items",
845
+ "common_field": "config.fieldMappings.common",
846
+ "template_field": "config.fieldMappings.template",
847
+ "template_scope": "item-relative",
848
+ "recommended_recipe": "autotouch columns recipe --type sync_to_leads_array",
849
+ "example": {
850
+ "sourceColumn": "key_buyer_leads",
851
+ "sourceArrayPath": "contacts",
852
+ "template": {
853
+ "firstName": "first_name",
854
+ "lastName": "last_name",
855
+ "linkedinUrl": "linkedin_url",
856
+ },
857
+ },
858
+ },
838
859
  },
839
860
  composition_contract={
840
861
  "consumes": {
841
862
  "kind": "crm_export_inputs",
842
- "shape": "mapped source row values including company domain plus hard identity or, in single mode, provisional firstName/lastName fallback; structured source columns use explicit {column, path} mappings to identify the exact field",
863
+ "shape": "mapped source row values, multi-slot mappings, or array-mode contact/person items from a structured source column; strict mode requires company domain plus hard identity or, in single mode, provisional firstName/lastName fallback",
843
864
  },
844
865
  "produces": {
845
866
  "kind": "lead_ids",
846
867
  "shape": "lead creation/update result with CRM lead ids stored on the source row",
847
868
  },
869
+ "decision_hints": {
870
+ "prefer_when": [
871
+ "the target output is Leads CRM, not a separate contacts research table",
872
+ "an upstream research column already returns contacts[], people[], leads[], or items[]",
873
+ ],
874
+ "prefer_sync_to_table_when": [
875
+ "you need a destination contacts table for per-contact enrichment, review, export, or sequencing context before CRM sync",
876
+ ],
877
+ "avoid": [
878
+ "manual rows list -> local JSON transform -> rows add/import for contacts produced by an upstream column",
879
+ ],
880
+ },
848
881
  "common_next_steps": [
849
882
  "add_to_sequence using the column key that stores those lead IDs",
850
883
  ],
@@ -1,4 +1,4 @@
1
- """Shared provider-hidden search contract for API capabilities, CLI recipes, and docs."""
1
+ """Shared search contract for Exa company search, people search, and related API capabilities."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
@@ -15,8 +15,8 @@ _SEARCH_ENDPOINTS: Dict[str, Dict[str, Any]] = {
15
15
  "path": "/api/search/companies",
16
16
  "required_scope": SEARCH_SCOPE,
17
17
  "body": {
18
- "query": "required free-text company search query",
19
- "limit": "optional 1-10 (default 10)",
18
+ "query": "required Exa Company Search natural-language query; include industry, geography, funding, headcount, technology, and similarity constraints in the query",
19
+ "limit": "optional 1-100 (default 10)",
20
20
  },
21
21
  "response": {"items_field": "items", "item_type": "company"},
22
22
  },
@@ -38,10 +38,11 @@ _SEARCH_ENDPOINTS: Dict[str, Dict[str, Any]] = {
38
38
  "path": "/api/search/similar-companies",
39
39
  "required_scope": SEARCH_SCOPE,
40
40
  "body": {
41
- "company_name": "required seed company name",
42
- "company_domain": "optional official company domain",
43
- "company_description": "optional short semantic description to improve peer matching",
44
- "limit": "optional 1-10 (default 10)",
41
+ "query": "optional Exa Company Search semantic similarity query; use this for direct Exa query patterns like 'companies like Notion' or 'competitors of Stripe'",
42
+ "company_name": "optional seed company name used when query is omitted",
43
+ "company_domain": "optional official company domain used to disambiguate the seed in the Exa Company Search query",
44
+ "company_description": "optional semantic description appended to the Exa Company Search query",
45
+ "limit": "optional 1-100 (default 10)",
45
46
  },
46
47
  "response": {"items_field": "items", "item_type": "company"},
47
48
  },
@@ -108,13 +109,14 @@ _SEARCH_CAPABILITIES: Dict[str, Any] = {
108
109
  "use_company_search_when": [
109
110
  "you need to discover target accounts before looking for contacts",
110
111
  "the search criteria are market, product, or industry driven",
112
+ "the user wants basic company discovery, competitors, lookalikes, or a semantic company search",
111
113
  ],
112
114
  "use_people_search_when": [
113
115
  "you already know the target companies",
114
116
  "the search criteria are role, persona, or skill driven",
115
117
  ],
116
118
  "use_list_build_instead_when": [
117
- "the user wants a LinkedIn-sourced company or lead list",
119
+ "the user wants a LinkedIn/Sales Navigator-sourced company or lead list",
118
120
  "the search can be expressed with geography, industry, company size, title, or current-company filters",
119
121
  "the desired output is a durable background job with status/results",
120
122
  ],
@@ -122,9 +124,9 @@ _SEARCH_CAPABILITIES: Dict[str, Any] = {
122
124
  "pricing": {
123
125
  "charge_on": "api_call_or_actual_results_returned",
124
126
  "families": {
125
- "companies": {"credits_per_call": 1, "max_results_per_call": 10, "rule": "1 credit per neural search API call"},
127
+ "companies": {"credits_per_bucket": 1, "bucket_size": 10, "max_results_per_call": 100, "rule": "1 credit per 10 returned Exa company results"},
126
128
  "people": {"credits_per_call": 1, "max_results_per_call": 10, "rule": "1 credit per neural search API call"},
127
- "similar_companies": {"credits_per_call": 1, "max_results_per_call": 10, "rule": "1 credit per neural search API call"},
129
+ "similar_companies": {"credits_per_bucket": 1, "bucket_size": 10, "max_results_per_call": 100, "rule": "1 credit per 10 returned Exa company results"},
128
130
  "news": {"credits_per_bucket": 1, "bucket_size": 25, "rule": "1 credit per 25 results"},
129
131
  "local_businesses": {"credits_per_bucket": 1, "bucket_size": 25, "rule": "1 credit per 25 results"},
130
132
  "reviews": {"credits_per_bucket": 1, "bucket_size": 25, "rule": "1 credit per 25 results"},
@@ -132,8 +134,9 @@ _SEARCH_CAPABILITIES: Dict[str, Any] = {
132
134
  },
133
135
  },
134
136
  "recommended_cli": [
135
- "autotouch search companies --query 'remote IT asset management' --limit 25",
136
- "autotouch search people --query 'VP IT' --company 'Rippling' --limit 25",
137
+ "autotouch search companies --query 'remote IT asset management companies in the US with 50-200 employees' --limit 100",
138
+ "autotouch search companies --query 'companies like Rippling in HR software, based in the United States, with 50-500 employees' --limit 100",
139
+ "autotouch search people --query 'VP IT' --company 'Rippling' --limit 10",
137
140
  "autotouch search recipe --type companies --out-file companies.json",
138
141
  ],
139
142
  }
@@ -141,11 +144,12 @@ _SEARCH_CAPABILITIES: Dict[str, Any] = {
141
144
 
142
145
  _SEARCH_RECIPES: Dict[str, Dict[str, Any]] = {
143
146
  "companies": {
144
- "payload": {"query": "remote IT asset management", "limit": 10},
147
+ "payload": {"query": "remote IT asset management companies in the US with 50-200 employees", "limit": 100},
145
148
  "usage": "autotouch search companies --data-file <payload.json>",
146
149
  "notes": [
147
- "Use this when you need to discover target accounts first.",
148
- "Pricing: 1 credit per neural search API call. Max 10 returned companies per call.",
150
+ "Use this for basic Exa Company Search: company discovery, competitors, lookalikes, and semantic market scans.",
151
+ "Put Exa company constraints in natural language: industry, geography, funding, headcount, technology, founded date, and similarity.",
152
+ "Pricing: 1 credit per 10 returned Exa company results. Max 100 returned companies per call.",
149
153
  ],
150
154
  },
151
155
  "people": {
@@ -164,16 +168,15 @@ _SEARCH_RECIPES: Dict[str, Dict[str, Any]] = {
164
168
  },
165
169
  "similar_companies": {
166
170
  "payload": {
167
- "company_name": "Rippling",
168
- "company_domain": "rippling.com",
169
- "company_description": "Workforce management and HR software platform for businesses",
170
- "limit": 10,
171
+ "query": "companies like Rippling for workforce management, HR, payroll, IT, benefits, devices, and app access",
172
+ "limit": 100,
171
173
  },
172
174
  "usage": "autotouch search similar-companies --data-file <payload.json>",
173
175
  "notes": [
174
- "Good for expanding a seed account list.",
175
- "Best results come from providing company_domain and a short company_description when known.",
176
- "Pricing: 1 credit per neural search API call. Max 10 returned companies per call.",
176
+ "Convenience wrapper for Exa Company Search similarity queries, not Exa /findSimilar.",
177
+ "Prefer query for Exa's documented semantic patterns: companies like X, competitors of X, or companies working on Y.",
178
+ "Seed fields company_name, company_domain, and company_description remain available when you want the CLI/API to compose the query.",
179
+ "Pricing: 1 credit per 10 returned Exa company results. Max 100 returned companies per call.",
177
180
  ],
178
181
  },
179
182
  "news": {
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "autotouch-cli"
7
- version = "0.2.85"
7
+ version = "0.2.87"
8
8
  description = "Autotouch Smart Table CLI"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
File without changes