deepline 0.1.261 → 0.1.262

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.
@@ -123,7 +123,7 @@ export const SDK_RELEASE = {
123
123
  // Deepline-native radars. Older clients must update before discovering,
124
124
  // checking, or deploying an unlaunched monitor integration.
125
125
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
126
- version: '0.1.261',
126
+ version: '0.1.262',
127
127
  apiContract: '2026-07-native-monitor-launch-hard-cutover',
128
128
  supportPolicy: {
129
129
  minimumSupported: '0.1.53',
@@ -0,0 +1,51 @@
1
+ // One-line definitions for the tool categories surfaced by `deepline tools list`.
2
+ //
3
+ // Tool categories are free-form strings that each provider tool declares in the
4
+ // generated catalog (see `src/lib/integrations/**`), not a single closed enum.
5
+ // The canonical set of well-known category slugs lives next to this file in
6
+ // `DEEPLINE_TOOL_CATEGORIES` (bootstrap-routes.ts) and in the richer
7
+ // `IntegrationCatalogTag` enum (src/lib/integrations/catalog-tags.ts). This map
8
+ // MUST track the categories that actually appear on live tools. The CLI derives
9
+ // the category enumeration from the live catalog, so a new category slug shows
10
+ // up automatically; only its human-readable definition comes from here. When a
11
+ // category has no entry the CLI renders it without a definition (never invents
12
+ // one), which is the loud signal to add it here.
13
+ //
14
+ // Keep each definition to a single, plain sentence fragment.
15
+
16
+ export const TOOL_CATEGORY_DESCRIPTIONS: Readonly<Record<string, string>> = {
17
+ company_search: 'Find companies matching firmographic, ad, or web criteria.',
18
+ people_search: 'Find people matching role, company, or persona criteria.',
19
+ people_enrich:
20
+ 'Enrich a known person with contact, social, and profile data.',
21
+ company_enrich: 'Enrich a known company with firmographic and account data.',
22
+ email_finder: 'Find work or personal email addresses for a person.',
23
+ email_verify: 'Validate email deliverability and catch-all status.',
24
+ phone_finder: 'Find mobile or direct phone numbers for a person.',
25
+ phone_verify: 'Validate phone-number reachability and line type.',
26
+ research:
27
+ 'Research accounts, people, news, and signals for context and scoring.',
28
+ autocomplete: 'Resolve names and entities to canonical catalog records.',
29
+ automation:
30
+ 'Kick off provider-side jobs, exports, and asynchronous workflows.',
31
+ outbound_tools:
32
+ 'Run outbound sequencing, deliverability, and campaign operations.',
33
+ admin:
34
+ 'Provider-side status, metadata, and account operations behind other tools.',
35
+ smb: 'Look up small-business listings, local records, and public filings.',
36
+ identity_resolution:
37
+ 'Match a partial identity to a single resolved person or company.',
38
+ reverse_lookup: 'Resolve an email, phone, or profile back to a person.',
39
+ enrichment: 'General-purpose record enrichment across people and companies.',
40
+ batch: 'Run an enrichment or search operation over many rows at once.',
41
+ premium: 'Higher-cost tools with premium provider coverage.',
42
+ free: 'Free tools that do not spend Deepline credits.',
43
+ };
44
+
45
+ /**
46
+ * One-line definition for a tool category slug, or null when none is declared.
47
+ * Never fabricates a definition for unknown slugs.
48
+ */
49
+ export function describeToolCategory(category: string): string | null {
50
+ return TOOL_CATEGORY_DESCRIPTIONS[category] ?? null;
51
+ }
package/dist/cli/index.js CHANGED
@@ -716,7 +716,7 @@ var SDK_RELEASE = {
716
716
  // Deepline-native radars. Older clients must update before discovering,
717
717
  // checking, or deploying an unlaunched monitor integration.
718
718
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
719
- version: "0.1.261",
719
+ version: "0.1.262",
720
720
  apiContract: "2026-07-native-monitor-launch-hard-cutover",
721
721
  supportPolicy: {
722
722
  minimumSupported: "0.1.53",
@@ -26616,6 +26616,33 @@ function extractSummaryFields(payload) {
26616
26616
  return {};
26617
26617
  }
26618
26618
 
26619
+ // ../shared_libs/plays/tool-category-descriptions.ts
26620
+ var TOOL_CATEGORY_DESCRIPTIONS = {
26621
+ company_search: "Find companies matching firmographic, ad, or web criteria.",
26622
+ people_search: "Find people matching role, company, or persona criteria.",
26623
+ people_enrich: "Enrich a known person with contact, social, and profile data.",
26624
+ company_enrich: "Enrich a known company with firmographic and account data.",
26625
+ email_finder: "Find work or personal email addresses for a person.",
26626
+ email_verify: "Validate email deliverability and catch-all status.",
26627
+ phone_finder: "Find mobile or direct phone numbers for a person.",
26628
+ phone_verify: "Validate phone-number reachability and line type.",
26629
+ research: "Research accounts, people, news, and signals for context and scoring.",
26630
+ autocomplete: "Resolve names and entities to canonical catalog records.",
26631
+ automation: "Kick off provider-side jobs, exports, and asynchronous workflows.",
26632
+ outbound_tools: "Run outbound sequencing, deliverability, and campaign operations.",
26633
+ admin: "Provider-side status, metadata, and account operations behind other tools.",
26634
+ smb: "Look up small-business listings, local records, and public filings.",
26635
+ identity_resolution: "Match a partial identity to a single resolved person or company.",
26636
+ reverse_lookup: "Resolve an email, phone, or profile back to a person.",
26637
+ enrichment: "General-purpose record enrichment across people and companies.",
26638
+ batch: "Run an enrichment or search operation over many rows at once.",
26639
+ premium: "Higher-cost tools with premium provider coverage.",
26640
+ free: "Free tools that do not spend Deepline credits."
26641
+ };
26642
+ function describeToolCategory(category) {
26643
+ return TOOL_CATEGORY_DESCRIPTIONS[category] ?? null;
26644
+ }
26645
+
26619
26646
  // src/cli/commands/model-description.ts
26620
26647
  function formatModelDescription(description) {
26621
26648
  const lines = [];
@@ -26782,21 +26809,181 @@ function zeroMatchRender(emptyResult) {
26782
26809
  ]
26783
26810
  };
26784
26811
  }
26785
- async function listTools(args) {
26812
+ function firstSentence(text) {
26813
+ const clean = (text ?? "").replace(/\s+/g, " ").trim();
26814
+ if (!clean) return "";
26815
+ const match = clean.match(/^.*?[.!?](?=\s|$)/);
26816
+ return (match ? match[0] : clean).trim();
26817
+ }
26818
+ function requiredInputIds(tool) {
26819
+ const inputSchema = recordField2(
26820
+ tool,
26821
+ "inputSchema",
26822
+ "input_schema"
26823
+ );
26824
+ return toolInputFieldsForDisplay(inputSchema).filter((field) => field.required === true).map((field) => String(field.name ?? "")).filter(Boolean);
26825
+ }
26826
+ function shortPricingHint(tool) {
26827
+ const pricing = recordField2(
26828
+ tool,
26829
+ "pricing"
26830
+ );
26831
+ const credits = numberField2(pricing, "creditsPerUnit", "credits_per_unit");
26832
+ const unit = stringField2(pricing, "unit");
26833
+ if (credits !== null) {
26834
+ return `${formatDecimal(credits)} cr/${unit || "unit"}`;
26835
+ }
26836
+ const summary = stringField2(pricing, "summary");
26837
+ if (summary) return summary;
26838
+ const displayText = stringField2(pricing, "displayText", "display_text");
26839
+ return displayText || null;
26840
+ }
26841
+ function aggregateCategorySummaries(tools) {
26842
+ const counts = /* @__PURE__ */ new Map();
26843
+ for (const tool of tools) {
26844
+ for (const category of tool.categories ?? []) {
26845
+ counts.set(category, (counts.get(category) ?? 0) + 1);
26846
+ }
26847
+ }
26848
+ return [...counts.entries()].map(([name, count]) => ({
26849
+ name,
26850
+ count,
26851
+ description: describeToolCategory(name)
26852
+ })).sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));
26853
+ }
26854
+ function categoryEnumerationRender(summaries) {
26855
+ const nameWidth = summaries.reduce(
26856
+ (max, item) => Math.max(max, item.name.length),
26857
+ 0
26858
+ );
26859
+ const countWidth = summaries.reduce(
26860
+ (max, item) => Math.max(max, String(item.count).length),
26861
+ 0
26862
+ );
26863
+ return {
26864
+ sections: [
26865
+ {
26866
+ title: `${summaries.length} tool categories:`,
26867
+ lines: summaries.map((item) => {
26868
+ const name = item.name.padEnd(nameWidth);
26869
+ const count = String(item.count).padStart(countWidth);
26870
+ const definition = item.description ? ` - ${item.description}` : "";
26871
+ return `${name} ${count}${definition}`;
26872
+ })
26873
+ },
26874
+ {
26875
+ title: "next",
26876
+ lines: ["Pass a category: deepline tools list email_finder"]
26877
+ }
26878
+ ]
26879
+ };
26880
+ }
26881
+ async function listToolCategories(emitJson, compact) {
26882
+ const client2 = new DeeplineClient();
26883
+ const rawTools = await client2.listTools({ compact });
26884
+ const items = rawTools.map(toListedTool);
26885
+ const summaries = aggregateCategorySummaries(rawTools);
26886
+ const outputItems = compact ? items.map(compactTool) : items;
26887
+ printCommandEnvelope(
26888
+ {
26889
+ tools: outputItems,
26890
+ count: outputItems.length,
26891
+ categories: summaries,
26892
+ filters: {
26893
+ categories: []
26894
+ },
26895
+ commandTemplates: TOOL_COMMAND_TEMPLATES,
26896
+ render: categoryEnumerationRender(summaries)
26897
+ },
26898
+ { json: emitJson }
26899
+ );
26900
+ return 0;
26901
+ }
26902
+ function unknownCategoryError(category, summaries, emitJson) {
26903
+ const known = summaries.map((item) => item.name);
26904
+ const message = `Unknown tool category "${category}". Run \`deepline tools list\` to browse categories.`;
26905
+ if (emitJson) {
26906
+ printJson({
26907
+ ok: false,
26908
+ exitCode: 4,
26909
+ code: "TOOL_CATEGORY_NOT_FOUND",
26910
+ message,
26911
+ category,
26912
+ categories: summaries,
26913
+ next: "deepline tools list"
26914
+ });
26915
+ return 4;
26916
+ }
26917
+ const render = categoryEnumerationRender(summaries);
26918
+ console.error(message);
26919
+ console.error("");
26920
+ console.error(renderCommandEnvelopeText({ render }).trimEnd());
26921
+ console.error("");
26922
+ console.error(`Valid categories: ${known.join(", ")}`);
26923
+ return 4;
26924
+ }
26925
+ async function listToolsInCategory(category, emitJson) {
26926
+ const client2 = new DeeplineClient();
26927
+ const rawTools = await client2.listTools({
26928
+ categories: category,
26929
+ compact: false
26930
+ });
26931
+ const allTools = await client2.listTools({ compact: true });
26932
+ const summaries = aggregateCategorySummaries(allTools);
26933
+ if (!summaries.some((item) => item.name === category)) {
26934
+ return unknownCategoryError(category, summaries, emitJson);
26935
+ }
26936
+ const rows = rawTools.filter((tool) => tool.categories?.includes(category)).map((tool) => ({
26937
+ id: tool.toolId,
26938
+ provider: tool.provider,
26939
+ required: requiredInputIds(tool),
26940
+ pricing: shortPricingHint(tool),
26941
+ description: firstSentence(tool.description)
26942
+ })).sort((a, b) => a.id.localeCompare(b.id));
26943
+ const idWidth = rows.reduce((max, row) => Math.max(max, row.id.length), 0);
26944
+ const render = {
26945
+ sections: [
26946
+ {
26947
+ title: `${rows.length} tools in ${category}:`,
26948
+ lines: rows.map((row) => {
26949
+ const id = row.id.padEnd(idWidth);
26950
+ const required = row.required.length ? `(${row.required.join(", ")})` : "(no required inputs)";
26951
+ const pricing = row.pricing ? ` [${row.pricing}]` : "";
26952
+ const description = row.description ? ` - ${row.description}` : "";
26953
+ return `${id} ${required}${pricing}${description}`;
26954
+ })
26955
+ },
26956
+ {
26957
+ title: "next",
26958
+ lines: [
26959
+ "Run `deepline tools describe <toolId>` for the full contract."
26960
+ ]
26961
+ }
26962
+ ]
26963
+ };
26964
+ printCommandEnvelope(
26965
+ {
26966
+ category,
26967
+ total: rows.length,
26968
+ tools: rows,
26969
+ commandTemplates: TOOL_COMMAND_TEMPLATES,
26970
+ render
26971
+ },
26972
+ { json: emitJson }
26973
+ );
26974
+ return 0;
26975
+ }
26976
+ async function listToolsForCategoryFilter(requestedCategories, emitJson, compact) {
26786
26977
  const client2 = new DeeplineClient();
26787
- const categoryArgIndex = args.findIndex((arg) => arg === "--categories");
26788
- const categoryFilter = categoryArgIndex >= 0 ? args[categoryArgIndex + 1] : "";
26789
- const compact = !args.includes("--full");
26790
- const requestedCategories = categoryFilter ? categoryFilter.split(",").map((item) => item.trim()).filter(Boolean) : [];
26791
26978
  const items = (await client2.listTools({
26792
- ...categoryFilter ? { categories: categoryFilter } : {},
26979
+ categories: requestedCategories.join(","),
26793
26980
  compact
26794
26981
  })).map(toListedTool).filter(
26795
- (item) => requestedCategories.length === 0 || requestedCategories.some(
26982
+ (item) => requestedCategories.some(
26796
26983
  (category) => item.categories.includes(category)
26797
26984
  )
26798
26985
  );
26799
- const emptyResult = items.length === 0 && requestedCategories.length > 0 ? zeroMatchToolGuidance({ categories: requestedCategories }) : null;
26986
+ const emptyResult = items.length === 0 ? zeroMatchToolGuidance({ categories: requestedCategories }) : null;
26800
26987
  const render = {
26801
26988
  sections: emptyResult ? zeroMatchRender(emptyResult).sections : [
26802
26989
  {
@@ -26827,10 +27014,41 @@ async function listTools(args) {
26827
27014
  commandTemplates: TOOL_COMMAND_TEMPLATES,
26828
27015
  render
26829
27016
  },
26830
- { json: argsWantJson(args) }
27017
+ { json: emitJson }
26831
27018
  );
26832
27019
  return 0;
26833
27020
  }
27021
+ async function listTools(category, options = {}) {
27022
+ const positional = category?.trim() ?? "";
27023
+ const flagCategories = (options.categories ?? "").split(",").map((item) => item.trim()).filter(Boolean);
27024
+ const compact = options.compact !== false;
27025
+ const emitJson = options.json === true || shouldEmitJson();
27026
+ if (positional && flagCategories.length > 0) {
27027
+ const flagIsSameSingle = flagCategories.length === 1 && flagCategories[0] === positional;
27028
+ if (!flagIsSameSingle) {
27029
+ const message = `Category "${positional}" and --categories ${flagCategories.join(",")} disagree. Pass one: deepline tools list ${positional}`;
27030
+ if (emitJson) {
27031
+ printJson({
27032
+ ok: false,
27033
+ exitCode: 2,
27034
+ code: "TOOL_LIST_CATEGORY_CONFLICT",
27035
+ message,
27036
+ next: `deepline tools list ${positional}`
27037
+ });
27038
+ } else {
27039
+ console.error(message);
27040
+ }
27041
+ return 2;
27042
+ }
27043
+ }
27044
+ if (positional) {
27045
+ return listToolsInCategory(positional, emitJson);
27046
+ }
27047
+ if (flagCategories.length > 0) {
27048
+ return listToolsForCategoryFilter(flagCategories, emitJson, compact);
27049
+ }
27050
+ return listToolCategories(emitJson, compact);
27051
+ }
26834
27052
  async function searchTools(queryInput, options = {}) {
26835
27053
  const query = queryInput?.trim() ?? "";
26836
27054
  const hasStructuredSearch = Boolean(
@@ -27029,27 +27247,37 @@ Output:
27029
27247
  Use execute to run a tool.
27030
27248
  `
27031
27249
  );
27032
- tools.command("list").description("List available tools.").addHelpText(
27250
+ tools.command("list [category]").description("Browse tool categories, or list every tool in a category.").addHelpText(
27033
27251
  "after",
27034
27252
  `
27035
27253
  Notes:
27036
- Inventory command for known tool ids. Use search for ranked discovery by
27037
- intent, aliases, descriptions, and schema fields.
27254
+ Bare \`tools list\` prints the category enumeration: one line per category with
27255
+ its tool count and definition. Pass a category to list every tool in it, one
27256
+ succinct row each (required inputs, Deepline pricing hint, first sentence).
27257
+ The listing is exhaustive and never truncated. Use search for ranked discovery
27258
+ by intent, aliases, descriptions, and schema fields.
27259
+
27260
+ --categories is a back-compat inventory filter. Passing it alone returns the
27261
+ legacy flat inventory envelope (one row per tool). The positional category is
27262
+ the newer exhaustive per-category view. If both are given they must name the
27263
+ same category (the positional view wins); otherwise it is a usage error.
27038
27264
 
27039
27265
  Examples:
27040
27266
  deepline tools list
27267
+ deepline tools list email_finder
27268
+ deepline tools list email_finder --json
27041
27269
  deepline tools list --categories email_finder --json
27042
27270
  deepline tools search email --json
27043
27271
  `
27044
27272
  ).option(
27045
27273
  "--categories <categories>",
27046
- "Comma-separated categories to filter inventory"
27047
- ).option("--compact", "Drop verbose fields from JSON output").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
27048
- process.exitCode = await listTools([
27049
- ...options.categories ? ["--categories", options.categories] : [],
27050
- ...options.compact ? ["--compact"] : [],
27051
- ...options.json ? ["--json"] : []
27052
- ]);
27274
+ "Comma-separated categories filter (back-compat alias for the positional)"
27275
+ ).option("--compact", "Drop verbose fields from JSON output").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (category, options) => {
27276
+ process.exitCode = await listTools(category, {
27277
+ categories: typeof options.categories === "string" ? options.categories : void 0,
27278
+ compact: options.compact !== false,
27279
+ json: Boolean(options.json)
27280
+ });
27053
27281
  });
27054
27282
  const addToolSearchCommand = (command) => command.description(
27055
27283
  "Search available tools by intent query or structured filters."
@@ -701,7 +701,7 @@ var SDK_RELEASE = {
701
701
  // Deepline-native radars. Older clients must update before discovering,
702
702
  // checking, or deploying an unlaunched monitor integration.
703
703
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
704
- version: "0.1.261",
704
+ version: "0.1.262",
705
705
  apiContract: "2026-07-native-monitor-launch-hard-cutover",
706
706
  supportPolicy: {
707
707
  minimumSupported: "0.1.53",
@@ -26664,6 +26664,33 @@ function extractSummaryFields(payload) {
26664
26664
  return {};
26665
26665
  }
26666
26666
 
26667
+ // ../shared_libs/plays/tool-category-descriptions.ts
26668
+ var TOOL_CATEGORY_DESCRIPTIONS = {
26669
+ company_search: "Find companies matching firmographic, ad, or web criteria.",
26670
+ people_search: "Find people matching role, company, or persona criteria.",
26671
+ people_enrich: "Enrich a known person with contact, social, and profile data.",
26672
+ company_enrich: "Enrich a known company with firmographic and account data.",
26673
+ email_finder: "Find work or personal email addresses for a person.",
26674
+ email_verify: "Validate email deliverability and catch-all status.",
26675
+ phone_finder: "Find mobile or direct phone numbers for a person.",
26676
+ phone_verify: "Validate phone-number reachability and line type.",
26677
+ research: "Research accounts, people, news, and signals for context and scoring.",
26678
+ autocomplete: "Resolve names and entities to canonical catalog records.",
26679
+ automation: "Kick off provider-side jobs, exports, and asynchronous workflows.",
26680
+ outbound_tools: "Run outbound sequencing, deliverability, and campaign operations.",
26681
+ admin: "Provider-side status, metadata, and account operations behind other tools.",
26682
+ smb: "Look up small-business listings, local records, and public filings.",
26683
+ identity_resolution: "Match a partial identity to a single resolved person or company.",
26684
+ reverse_lookup: "Resolve an email, phone, or profile back to a person.",
26685
+ enrichment: "General-purpose record enrichment across people and companies.",
26686
+ batch: "Run an enrichment or search operation over many rows at once.",
26687
+ premium: "Higher-cost tools with premium provider coverage.",
26688
+ free: "Free tools that do not spend Deepline credits."
26689
+ };
26690
+ function describeToolCategory(category) {
26691
+ return TOOL_CATEGORY_DESCRIPTIONS[category] ?? null;
26692
+ }
26693
+
26667
26694
  // src/cli/commands/model-description.ts
26668
26695
  function formatModelDescription(description) {
26669
26696
  const lines = [];
@@ -26830,21 +26857,181 @@ function zeroMatchRender(emptyResult) {
26830
26857
  ]
26831
26858
  };
26832
26859
  }
26833
- async function listTools(args) {
26860
+ function firstSentence(text) {
26861
+ const clean = (text ?? "").replace(/\s+/g, " ").trim();
26862
+ if (!clean) return "";
26863
+ const match = clean.match(/^.*?[.!?](?=\s|$)/);
26864
+ return (match ? match[0] : clean).trim();
26865
+ }
26866
+ function requiredInputIds(tool) {
26867
+ const inputSchema = recordField2(
26868
+ tool,
26869
+ "inputSchema",
26870
+ "input_schema"
26871
+ );
26872
+ return toolInputFieldsForDisplay(inputSchema).filter((field) => field.required === true).map((field) => String(field.name ?? "")).filter(Boolean);
26873
+ }
26874
+ function shortPricingHint(tool) {
26875
+ const pricing = recordField2(
26876
+ tool,
26877
+ "pricing"
26878
+ );
26879
+ const credits = numberField2(pricing, "creditsPerUnit", "credits_per_unit");
26880
+ const unit = stringField2(pricing, "unit");
26881
+ if (credits !== null) {
26882
+ return `${formatDecimal(credits)} cr/${unit || "unit"}`;
26883
+ }
26884
+ const summary = stringField2(pricing, "summary");
26885
+ if (summary) return summary;
26886
+ const displayText = stringField2(pricing, "displayText", "display_text");
26887
+ return displayText || null;
26888
+ }
26889
+ function aggregateCategorySummaries(tools) {
26890
+ const counts = /* @__PURE__ */ new Map();
26891
+ for (const tool of tools) {
26892
+ for (const category of tool.categories ?? []) {
26893
+ counts.set(category, (counts.get(category) ?? 0) + 1);
26894
+ }
26895
+ }
26896
+ return [...counts.entries()].map(([name, count]) => ({
26897
+ name,
26898
+ count,
26899
+ description: describeToolCategory(name)
26900
+ })).sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));
26901
+ }
26902
+ function categoryEnumerationRender(summaries) {
26903
+ const nameWidth = summaries.reduce(
26904
+ (max, item) => Math.max(max, item.name.length),
26905
+ 0
26906
+ );
26907
+ const countWidth = summaries.reduce(
26908
+ (max, item) => Math.max(max, String(item.count).length),
26909
+ 0
26910
+ );
26911
+ return {
26912
+ sections: [
26913
+ {
26914
+ title: `${summaries.length} tool categories:`,
26915
+ lines: summaries.map((item) => {
26916
+ const name = item.name.padEnd(nameWidth);
26917
+ const count = String(item.count).padStart(countWidth);
26918
+ const definition = item.description ? ` - ${item.description}` : "";
26919
+ return `${name} ${count}${definition}`;
26920
+ })
26921
+ },
26922
+ {
26923
+ title: "next",
26924
+ lines: ["Pass a category: deepline tools list email_finder"]
26925
+ }
26926
+ ]
26927
+ };
26928
+ }
26929
+ async function listToolCategories(emitJson, compact) {
26930
+ const client2 = new DeeplineClient();
26931
+ const rawTools = await client2.listTools({ compact });
26932
+ const items = rawTools.map(toListedTool);
26933
+ const summaries = aggregateCategorySummaries(rawTools);
26934
+ const outputItems = compact ? items.map(compactTool) : items;
26935
+ printCommandEnvelope(
26936
+ {
26937
+ tools: outputItems,
26938
+ count: outputItems.length,
26939
+ categories: summaries,
26940
+ filters: {
26941
+ categories: []
26942
+ },
26943
+ commandTemplates: TOOL_COMMAND_TEMPLATES,
26944
+ render: categoryEnumerationRender(summaries)
26945
+ },
26946
+ { json: emitJson }
26947
+ );
26948
+ return 0;
26949
+ }
26950
+ function unknownCategoryError(category, summaries, emitJson) {
26951
+ const known = summaries.map((item) => item.name);
26952
+ const message = `Unknown tool category "${category}". Run \`deepline tools list\` to browse categories.`;
26953
+ if (emitJson) {
26954
+ printJson({
26955
+ ok: false,
26956
+ exitCode: 4,
26957
+ code: "TOOL_CATEGORY_NOT_FOUND",
26958
+ message,
26959
+ category,
26960
+ categories: summaries,
26961
+ next: "deepline tools list"
26962
+ });
26963
+ return 4;
26964
+ }
26965
+ const render = categoryEnumerationRender(summaries);
26966
+ console.error(message);
26967
+ console.error("");
26968
+ console.error(renderCommandEnvelopeText({ render }).trimEnd());
26969
+ console.error("");
26970
+ console.error(`Valid categories: ${known.join(", ")}`);
26971
+ return 4;
26972
+ }
26973
+ async function listToolsInCategory(category, emitJson) {
26974
+ const client2 = new DeeplineClient();
26975
+ const rawTools = await client2.listTools({
26976
+ categories: category,
26977
+ compact: false
26978
+ });
26979
+ const allTools = await client2.listTools({ compact: true });
26980
+ const summaries = aggregateCategorySummaries(allTools);
26981
+ if (!summaries.some((item) => item.name === category)) {
26982
+ return unknownCategoryError(category, summaries, emitJson);
26983
+ }
26984
+ const rows = rawTools.filter((tool) => tool.categories?.includes(category)).map((tool) => ({
26985
+ id: tool.toolId,
26986
+ provider: tool.provider,
26987
+ required: requiredInputIds(tool),
26988
+ pricing: shortPricingHint(tool),
26989
+ description: firstSentence(tool.description)
26990
+ })).sort((a, b) => a.id.localeCompare(b.id));
26991
+ const idWidth = rows.reduce((max, row) => Math.max(max, row.id.length), 0);
26992
+ const render = {
26993
+ sections: [
26994
+ {
26995
+ title: `${rows.length} tools in ${category}:`,
26996
+ lines: rows.map((row) => {
26997
+ const id = row.id.padEnd(idWidth);
26998
+ const required = row.required.length ? `(${row.required.join(", ")})` : "(no required inputs)";
26999
+ const pricing = row.pricing ? ` [${row.pricing}]` : "";
27000
+ const description = row.description ? ` - ${row.description}` : "";
27001
+ return `${id} ${required}${pricing}${description}`;
27002
+ })
27003
+ },
27004
+ {
27005
+ title: "next",
27006
+ lines: [
27007
+ "Run `deepline tools describe <toolId>` for the full contract."
27008
+ ]
27009
+ }
27010
+ ]
27011
+ };
27012
+ printCommandEnvelope(
27013
+ {
27014
+ category,
27015
+ total: rows.length,
27016
+ tools: rows,
27017
+ commandTemplates: TOOL_COMMAND_TEMPLATES,
27018
+ render
27019
+ },
27020
+ { json: emitJson }
27021
+ );
27022
+ return 0;
27023
+ }
27024
+ async function listToolsForCategoryFilter(requestedCategories, emitJson, compact) {
26834
27025
  const client2 = new DeeplineClient();
26835
- const categoryArgIndex = args.findIndex((arg) => arg === "--categories");
26836
- const categoryFilter = categoryArgIndex >= 0 ? args[categoryArgIndex + 1] : "";
26837
- const compact = !args.includes("--full");
26838
- const requestedCategories = categoryFilter ? categoryFilter.split(",").map((item) => item.trim()).filter(Boolean) : [];
26839
27026
  const items = (await client2.listTools({
26840
- ...categoryFilter ? { categories: categoryFilter } : {},
27027
+ categories: requestedCategories.join(","),
26841
27028
  compact
26842
27029
  })).map(toListedTool).filter(
26843
- (item) => requestedCategories.length === 0 || requestedCategories.some(
27030
+ (item) => requestedCategories.some(
26844
27031
  (category) => item.categories.includes(category)
26845
27032
  )
26846
27033
  );
26847
- const emptyResult = items.length === 0 && requestedCategories.length > 0 ? zeroMatchToolGuidance({ categories: requestedCategories }) : null;
27034
+ const emptyResult = items.length === 0 ? zeroMatchToolGuidance({ categories: requestedCategories }) : null;
26848
27035
  const render = {
26849
27036
  sections: emptyResult ? zeroMatchRender(emptyResult).sections : [
26850
27037
  {
@@ -26875,10 +27062,41 @@ async function listTools(args) {
26875
27062
  commandTemplates: TOOL_COMMAND_TEMPLATES,
26876
27063
  render
26877
27064
  },
26878
- { json: argsWantJson(args) }
27065
+ { json: emitJson }
26879
27066
  );
26880
27067
  return 0;
26881
27068
  }
27069
+ async function listTools(category, options = {}) {
27070
+ const positional = category?.trim() ?? "";
27071
+ const flagCategories = (options.categories ?? "").split(",").map((item) => item.trim()).filter(Boolean);
27072
+ const compact = options.compact !== false;
27073
+ const emitJson = options.json === true || shouldEmitJson();
27074
+ if (positional && flagCategories.length > 0) {
27075
+ const flagIsSameSingle = flagCategories.length === 1 && flagCategories[0] === positional;
27076
+ if (!flagIsSameSingle) {
27077
+ const message = `Category "${positional}" and --categories ${flagCategories.join(",")} disagree. Pass one: deepline tools list ${positional}`;
27078
+ if (emitJson) {
27079
+ printJson({
27080
+ ok: false,
27081
+ exitCode: 2,
27082
+ code: "TOOL_LIST_CATEGORY_CONFLICT",
27083
+ message,
27084
+ next: `deepline tools list ${positional}`
27085
+ });
27086
+ } else {
27087
+ console.error(message);
27088
+ }
27089
+ return 2;
27090
+ }
27091
+ }
27092
+ if (positional) {
27093
+ return listToolsInCategory(positional, emitJson);
27094
+ }
27095
+ if (flagCategories.length > 0) {
27096
+ return listToolsForCategoryFilter(flagCategories, emitJson, compact);
27097
+ }
27098
+ return listToolCategories(emitJson, compact);
27099
+ }
26882
27100
  async function searchTools(queryInput, options = {}) {
26883
27101
  const query = queryInput?.trim() ?? "";
26884
27102
  const hasStructuredSearch = Boolean(
@@ -27077,27 +27295,37 @@ Output:
27077
27295
  Use execute to run a tool.
27078
27296
  `
27079
27297
  );
27080
- tools.command("list").description("List available tools.").addHelpText(
27298
+ tools.command("list [category]").description("Browse tool categories, or list every tool in a category.").addHelpText(
27081
27299
  "after",
27082
27300
  `
27083
27301
  Notes:
27084
- Inventory command for known tool ids. Use search for ranked discovery by
27085
- intent, aliases, descriptions, and schema fields.
27302
+ Bare \`tools list\` prints the category enumeration: one line per category with
27303
+ its tool count and definition. Pass a category to list every tool in it, one
27304
+ succinct row each (required inputs, Deepline pricing hint, first sentence).
27305
+ The listing is exhaustive and never truncated. Use search for ranked discovery
27306
+ by intent, aliases, descriptions, and schema fields.
27307
+
27308
+ --categories is a back-compat inventory filter. Passing it alone returns the
27309
+ legacy flat inventory envelope (one row per tool). The positional category is
27310
+ the newer exhaustive per-category view. If both are given they must name the
27311
+ same category (the positional view wins); otherwise it is a usage error.
27086
27312
 
27087
27313
  Examples:
27088
27314
  deepline tools list
27315
+ deepline tools list email_finder
27316
+ deepline tools list email_finder --json
27089
27317
  deepline tools list --categories email_finder --json
27090
27318
  deepline tools search email --json
27091
27319
  `
27092
27320
  ).option(
27093
27321
  "--categories <categories>",
27094
- "Comma-separated categories to filter inventory"
27095
- ).option("--compact", "Drop verbose fields from JSON output").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
27096
- process.exitCode = await listTools([
27097
- ...options.categories ? ["--categories", options.categories] : [],
27098
- ...options.compact ? ["--compact"] : [],
27099
- ...options.json ? ["--json"] : []
27100
- ]);
27322
+ "Comma-separated categories filter (back-compat alias for the positional)"
27323
+ ).option("--compact", "Drop verbose fields from JSON output").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (category, options) => {
27324
+ process.exitCode = await listTools(category, {
27325
+ categories: typeof options.categories === "string" ? options.categories : void 0,
27326
+ compact: options.compact !== false,
27327
+ json: Boolean(options.json)
27328
+ });
27101
27329
  });
27102
27330
  const addToolSearchCommand = (command) => command.description(
27103
27331
  "Search available tools by intent query or structured filters."
package/dist/index.js CHANGED
@@ -435,7 +435,7 @@ var SDK_RELEASE = {
435
435
  // Deepline-native radars. Older clients must update before discovering,
436
436
  // checking, or deploying an unlaunched monitor integration.
437
437
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
438
- version: "0.1.261",
438
+ version: "0.1.262",
439
439
  apiContract: "2026-07-native-monitor-launch-hard-cutover",
440
440
  supportPolicy: {
441
441
  minimumSupported: "0.1.53",
package/dist/index.mjs CHANGED
@@ -365,7 +365,7 @@ var SDK_RELEASE = {
365
365
  // Deepline-native radars. Older clients must update before discovering,
366
366
  // checking, or deploying an unlaunched monitor integration.
367
367
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
368
- version: "0.1.261",
368
+ version: "0.1.262",
369
369
  apiContract: "2026-07-native-monitor-launch-hard-cutover",
370
370
  supportPolicy: {
371
371
  minimumSupported: "0.1.53",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.1.261",
3
+ "version": "0.1.262",
4
4
  "description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
5
5
  "license": "MIT",
6
6
  "repository": {