@closedloop-ai/cli 0.3.4 → 0.3.5

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 (3) hide show
  1. package/README.md +32 -28
  2. package/dist/closedloop.js +133 -127
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -37,7 +37,7 @@ The npm package is `@closedloop-ai/cli`; users of the earlier `cl` CLI can updat
37
37
  - A new account gets its own Free workspace unless the email was invited to one. One network can create three such accounts a day.
38
38
  - A new account is asked for its company website right after the code (in a terminal), or takes `--website <url>` (an agent passes it with `--code`). ClosedLoop AI researches the company from it before it analyses anything you send in; without it, imports run but nothing becomes insights. A website that cannot be saved does not undo the sign-in: the result names it and says to run `closedloop company --website <url>`.
39
39
  - Then its email domains: the domains of your colleagues' work addresses, which tell ClosedLoop AI who is a colleague and who is a customer. In a terminal it suggests them (from the website and your own address, never a personal one such as gmail.com) and you confirm or correct them, or type `none` and confirm that your company has none. An agent gets `domains_instructions`: plain sentences telling it what to ask its user and which command to run with their answer. Nothing in a new workspace is analysed until a person has confirmed them.
40
- - Signing in makes the account the active one (see Accounts). The config folder holds `general.json` (the active account, and the email and region last signed in with) and one file per account in `accounts/` (its session, its sign-in in progress, its working goal and numbered lists), each named by a hash of the email with the email inside. Tokens are stored as local JSON, not in an OS keychain; passwords are not saved. Unix directories use mode `0700` and files `0600`; Windows uses the account's local application-data folder and its inherited access permissions.
40
+ - Signing in makes the account the active one (see Accounts). The config folder holds `general.json` (the active account, and the email and region last signed in with) and one file per account in `accounts/` (its session, its sign-in in progress, its working goal), each named by a hash of the email with the email inside. Tokens are stored as local JSON, not in an OS keychain; passwords are not saved. Unix directories use mode `0700` and files `0600`; Windows uses the account's local application-data folder and its inherited access permissions.
41
41
  - The API checks the session on every call. The CLI renews it once it is past half its 30 days, so it stays signed in while you use it; if the API refuses it, the CLI renews once more, and otherwise asks you to sign in again.
42
42
  - `closedloop whoami`: who you are signed in as, your workspace (role and plan), the other workspaces you belong to, the region, when and how this computer signed in (an email code, or Google, Microsoft, Okta or SAML SSO, or a password, in the browser), and when the sign-in ends if the CLI is not used.
43
43
  - `closedloop status`: what is running now (company research, history imports with what is left and when they should finish, your own data still waiting or processing) and the onboarding checklist: company website, company research, email domains, product structure, a source, history imported, and the optional goal and team. Each open step names the command or the place in the app for it; a part the CLI could not read shows as unknown. No insights here: see `closedloop insights`.
@@ -49,51 +49,51 @@ The npm package is `@closedloop-ai/cli`; users of the earlier `cl` CLI can updat
49
49
  - `--watch` checks every 30 seconds until the research is done or fails, for up to an hour.
50
50
  - The research reads your website to learn your company, its products and its market; it is the basis for the suggested product structure and for analysing your insights, and usually takes a few minutes. Once it is done, `closedloop company` says what it learned (its description of your company and the other parts it covers: products and features, use cases, target persona, integrations, pricing) and, while products it suggested wait to be confirmed, how many and how to confirm them. As JSON the summary is `research.summary` and the count `products_suggested` (null when it could not be read).
51
51
  - `--research` shows everything the research found, the whole research text (as JSON, the profile with `research.text`).
52
- - `closedloop products`: your product structure, the products, areas (parts of a product) and features (parts of the product that exist, never planned work) your insights are filed against, with insight counts, then the products your company research suggested and has not had confirmed. As JSON it is the API's answer, whose `structure` can be edited and saved.
53
- - `products confirm [name...]` adds the suggested products (all, or the ones named) to the structure; `products save --file <structure.json>` (or `--file -` for standard input) saves a structure of your own, either the whole `closedloop products --json` answer or only its `structure`. Products, areas and features left out are archived, never deleted. `--check` only checks.
52
+ - `closedloop products`: your product structure, the products, areas (parts of a product) and features (parts of the product that exist, never planned work) your insights are filed against, with insight counts and each one's id, then the products your company research suggested and has not had confirmed, each with its id. As JSON it is the API's answer, whose `structure` can be edited and saved.
53
+ - `products confirm [id...]` adds the suggested products to the structure: all of them, or the ones whose ids you give. `products save --file <structure.json>` (or `--file -` for standard input) saves a structure of your own, either the whole `closedloop products --json` answer or only its `structure`. Keep each `id` from that answer: it says which product, area or feature an entry is (two can share a name), and a new one has none. Products, areas and features left out are archived, never deleted. `--check` only checks.
54
54
  - Both check the structure first and list every change. In a terminal the CLI asks you to type `yes`; anyone else adds `--yes`, which an agent does only after its user agreed. Without it nothing is saved and the result's `instructions` say what to ask. Saving needs an owner or admin; the next nightly recalculation then re-places needs and insights, charged in credits.
55
- - `closedloop competitors`: the competitors ClosedLoop AI watches for in your insights, the same list as Settings, Company, Competitors in the app: each with its website, whether it is a direct competitor, how many times customers mentioned it, and where it came from (AI research, mentioned by customers, or manual). Insights that name one count as competitor mentions, shown on opportunities, features, customer profiles and the weekly brief. As JSON it is the API's `competitors` and `can_change`.
56
- - `competitors add <name> [--website <url>] [--description <text>]` adds one as a direct competitor (adding one that was removed restores it); `competitors edit <name-or-id> [--name <name>] [--website <url>] [--description <text>]` changes only what you give (an empty value clears the website or description); `competitors remove <name-or-id>` marks it as not a competitor and takes it off the list, keeping the customer mentions behind it. A name matches in any case; one that matches none or several is refused with the candidates.
55
+ - `closedloop competitors`: the competitors ClosedLoop AI watches for in your insights, the same list as Settings, Company, Competitors in the app: each with its website, whether it is a direct competitor, how many times customers mentioned it, where it came from (AI research, mentioned by customers, or manual) and its id. Insights that name one count as competitor mentions, shown on opportunities, features, customer profiles and the weekly brief. As JSON it is the API's `competitors` and `can_change`.
56
+ - `competitors add <name> [--website <url>] [--description <text>]` adds one as a direct competitor (adding one that was removed restores it); `competitors edit <id> [--name <name>] [--website <url>] [--description <text>]` changes only what you give (an empty value clears the website or description); `competitors remove <id>` marks it as not a competitor and takes it off the list, keeping the customer mentions behind it. Both take the competitor's id from the list: two competitors can share a name, so a name never picks one.
57
57
  - A removal asks you to type `yes` in a terminal; anyone else adds `--yes`, which an agent does only after its user agreed. Without it nothing is removed and the result's `instructions` say what to ask. Adding, editing and removing need the right to change the workspace's settings (an owner or admin).
58
58
  - `closedloop ingest [text]`: sends your own data in (feedback, notes, survey answers, transcripts) for the pipeline to turn into insights.
59
59
  - One record from the words given, from `--stdin`, or from a text file; `--title`, `--from`, `--email` (ties the insight to their company when it matches a CRM contact), `--customer`, `--date`, `--url`, `--id` (a record with an id already sent is skipped) and `--tag` describe it.
60
60
  - Many records from `--file` with a CSV (a header row; a `content` column, and any of `title, reporter_name, reporter_email, customer, date, url, id, tags` with tags separated by `;`), a JSON object or list, or JSON Lines. Sent in batches of 100 records or 4 MB.
61
61
  - `--wait` follows up to 100 records until they are processed and says how many insights they made.
62
- - `ingest list` shows the latest records and their state (waiting, processing, done with N insights, failed); `ingest status <id>` one record.
62
+ - `ingest list` shows the latest records, their state (waiting, processing, done with N insights, failed) and their ids; `ingest status <id>` one record.
63
63
  - The data arrives through the workspace's CLI source, created on the first send; it shows in `integrations list`, where it can be paused, resumed or disconnected, and `insights search --source cli` finds what it produced. Sending needs the right to manage integrations.
64
- - `closedloop insights search [words]`: searches insights as the Insights page does (every field, or one with `--in title|quote|reporter|account`), with `--source`, `--category`, `--severity`, `--last week|month|3months`, `--customer <email>`, `--account <domain>`, `--sort newest|oldest|severity|relevance`, `--limit` and `--page`. While you work on a goal (`goals use`) it reads only that goal's insights; `--goal <goal>` reads another goal's once and `--all` reads every insight.
65
- - `closedloop insights show <id>`: one insight in full (summary, quote and its source-language original, who, where, feature and product area); `--transcript` adds the conversation.
64
+ - `closedloop insights search [words]`: searches insights as the Insights page does (every field, or one with `--in title|quote|reporter|account`), with `--source`, `--category`, `--severity`, `--last week|month|3months`, `--customer <email>`, `--account <domain>`, `--sort newest|oldest|severity|relevance`, `--limit` and `--page`. Each insight shows its id. While you work on a goal (`goals use`) it reads only that goal's insights; `--goal <goal id>` reads another goal's once and `--all` reads every insight.
65
+ - `closedloop insights show <id>`: one insight in full, by the id the search shows (summary, quote and its source-language original, who, where, feature and product area); `--transcript` adds the conversation.
66
66
  - `closedloop goals`: your own goals, each a number to move by a date (onboarding completion to 60% by December) scoped to the insights it is about (customers in Germany and Austria, the checkout area).
67
- - `goals list` shows yours as a tree (each sub-goal under its parent) with their state (draft, active, closed, archived), each led by its first key result (its target and newest value; for a closed goal the one it ended with) or, for a goal without one, the number typed when it was created with its current value and progress, and how many insights each matches; `--team` adds the ones teammates share, `--archived` the ones you deleted, `--state draft,active` only those states. `goals show <number or id>` gives one with its parent, its recorded values and, for a draft, what it still needs to be activated.
68
- - `goals use <number or id>` picks the goal you work on (`--clear` drops it). `goals context [goal]` shows the deal blockers in its scope when it bears on new sales and the churn risks when it bears on retention, the newest of each and how many there are.
67
+ - `goals list` shows yours as a tree (each sub-goal under its parent) with their state (draft, active, closed, archived), each led by its first key result (its target and newest value; for a closed goal the one it ended with) or, for a goal without one, the number typed when it was created with its current value and progress, and how many insights each matches, and each goal's id; `--team` adds the ones teammates share, `--archived` the ones you deleted, `--state draft,active` only those states. `goals show <goal id>` gives one with its parent, its recorded values and, for a draft, what it still needs to be activated. Every goal command takes the goal's id from the list.
68
+ - `goals use <goal id>` picks the goal you work on (`--clear` drops it). `goals context [goal]` shows the deal blockers in its scope when it bears on new sales and the churn risks when it bears on retention, the newest of each and how many there are.
69
69
  - `goals fields` lists what a goal can be scoped by in this workspace (product, area, feature, category, severity, outcome, source, country, account owner, company size, deal stage, lifecycle stage) with the values in use and how many insights each matches; `--field <name>` lists every value of one.
70
- - `goals create --file goals.json` (or `--stdin`) creates one goal, a list, or `{ "goals": [...] }`, each as a draft; `goals example` prints a file to copy. Every goal is checked against the workspace and nothing is saved unless all pass; the answer names each problem and the allowed values. A goal with a `key` used before replaces that goal (a field left out takes its default), as far as its state allows: an active goal keeps its title, scope, parent and what its number counts, and a new target needs a `reason`. `--parent <goal>` makes each goal a sub-goal of another (or give `parent` in the file). `--dry-run` checks and counts without saving.
70
+ - `goals create --file goals.json` (or `--stdin`) creates one goal, a list, or `{ "goals": [...] }`, each as a draft; `goals example` prints a file to copy. Every goal is checked against the workspace and nothing is saved unless all pass; the answer names each problem and the allowed values. A goal with a `key` used before replaces that goal (a field left out takes its default), as far as its state allows: an active goal keeps its title, scope, parent and what its number counts, and a new target needs a `reason`. `--parent <goal id>` makes each goal a sub-goal of another (or give `parent` in the file). The answer shows each saved goal's id. `--dry-run` checks and counts without saving.
71
71
  - `goals activate <goal> [--reason]` activates one of your drafts once it has a key result with a target (and every metric its target); its metrics' first period starts that day and their baselines are measured then. `goals close <goal> [--reason] [--yes]` closes an active goal: it is finished, keeps its results and history, and cannot be opened again; its sub-goals keep their own state.
72
- - `goals set <number or id> <value> [--at <date>]` records the goal's number as measured; earlier values stay as its history, and a second value for the same moment replaces the first (`push` still works).
73
- - `goals delete <number or id>` archives one of your drafts or active goals: its values, metrics and history stay, and it leaves your list (`--yes` skips the question). A closed goal stays as it is.
74
- - `goals metrics list <goal>` shows a goal's metrics: each a saved metric (`metrics list`) at the version it was added with, as a key result or as an indicator of one key result, with its target, the baseline measured when the target was set, its newest value, and every finished period with the value measured the night after it ended and whether it met the target (`--ended` adds archived ones). `goals metrics add <goal> --metric <number|id|name> --period week|month|quarter|days [--days <n>] [--target <target>]` adds one as a key result (`--role indicator --key-result <kpi>` for an indicator, `--reason`, `--dry-run`); a target is one line: `>=1400`, `<=0.3`, `between 40 and 60`, a change from the baseline `+500` or `+10%`, or for a share `+5pts`. `goals metrics target <goal> <kpi> --target <target> --reason <text> [--period ...]` sets a new target and measures a new baseline (a draft's baselines are measured when it is activated, and until then its metrics show their current value as a preview); `goals metrics archive <goal> <kpi>` stops one (a key result takes its indicators) and keeps its history. `goals show` lists the metrics too. The old name `goals kpis` still works.
72
+ - `goals set <goal id> <value> [--at <date>]` records the goal's number as measured; earlier values stay as its history, and a second value for the same moment replaces the first (`push` still works).
73
+ - `goals delete <goal id>` archives one of your drafts or active goals: its values, metrics and history stay, and it leaves your list (`--yes` skips the question). A closed goal stays as it is.
74
+ - `goals metrics list <goal>` shows a goal's metrics: each a saved metric (`metrics list`) at the version it was added with, as a key result or as an indicator of one key result, with its target, the baseline measured when the target was set, its newest value, every finished period with the value measured the night after it ended and whether it met the target, and its id (`--ended` adds archived ones); an indicator names its key result by the key result's id. `goals metrics add <goal> --metric <metric id> --period week|month|quarter|days [--days <n>] [--target <target>]` adds one as a key result (`--role indicator --key-result <kpi id>` for an indicator, `--reason`, `--dry-run`); a target is one line: `>=1400`, `<=0.3`, `between 40 and 60`, a change from the baseline `+500` or `+10%`, or for a share `+5pts`. `goals metrics target <goal> <kpi> --target <target> --reason <text> [--period ...]` sets a new target and measures a new baseline (a draft's baselines are measured when it is activated, and until then its metrics show their current value as a preview); `goals metrics archive <goal> <kpi>` stops one (a key result takes its indicators) and keeps its history. `<kpi>` is the goal metric's id from `goals metrics list`. `goals show` lists the metrics too. The old name `goals kpis` still works.
75
75
  - `goals measure <goal>` measures a goal's metrics now instead of waiting for the night; `goals history <goal>` lists every change to the goal, its metrics and its values, with who made it, through what and why.
76
- - `closedloop fields`: the named fields segments and metrics are written in (`customer.country`, `deal.value`, `insight.area`, a CRM field by its label such as `customer.crm["Property Type"]`). `fields list [--entity customer|deal|person|insight|context|conversation|usage]` shows every field that holds values, one line each: its key, type and label, by the kind of record it describes (a field no record has a value of is left out; the JSON answer carries each field's guide, built every night: its meaning, a preview of this workspace's values, the operators it takes and one example condition); `fields search <words>` finds fields by key, label or description, empty ones included. When two fields fit a request, ask the user which one they mean (the JSON answer carries an `ask` line saying so); `fields show <key>` gives one in full, read from this workspace's records at that moment: every value with how many records have it, the range of numbers or dates, how many records have none, and the fields it is often confused with (for example `deal.value` and `deal.annual_value`). Email and phone fields show only a pattern.
76
+ - `closedloop fields`: the named fields segments and metrics are written in (`customer.country`, `deal.value`, `insight.area`, a CRM field by its label such as `customer.crm["Property Type"]`). `fields list [--entity customer|deal|person|insight|context|conversation|usage]` shows every field that holds values, one line each: its key, type and label, by the kind of record it describes, with the field's id under it when that differs from the key (a field no record has a value of is left out; the JSON answer carries each field's guide, built every night: its meaning, a preview of this workspace's values, the operators it takes and one example condition); `fields search <words>` finds fields by key, label or description, empty ones included. When two fields fit a request, ask the user which one they mean (the JSON answer carries an `ask` line saying so); `fields show <key>` (or the field's id) gives one in full, read from this workspace's records at that moment: every value with how many records have it, the range of numbers or dates, how many records have none, and the fields it is often confused with (for example `deal.value` and `deal.annual_value`). Email and phone fields show only a pattern.
77
77
  - `closedloop segments`: who is in scope, as a rule over named fields (`{"of":"customer","where":{"and":[...]}}`; `has` and `subtotal` for a customer's deals, people, insights or conversations). A date takes `within_last` (days) or `within_period` (`week`, `month`, `quarter` or `year` so far). Changes over time read the customer history: `{"moved":"customer.stage","into":"churned","within_period":"month"}` (optionally `"from"`) for customers who moved into a value, and `{"change":{"field":"customer.arr","within_last":90,"start":{"op":">","value":0}},"op":">","value":0}` for a number's value at the window's end minus its value at the start. A rule includes another saved segment of the same kind by its name, `{"segment":"Groups"}` (or its id): define *Groups* once and use it in every rule about groups; inside `has` it takes a segment of that kind (`{"has":"deal","where":{"segment":"Big deals"}}`). Saving keeps the version the included segment had then, so a number measured with it keeps its meaning; save the rule again to use a newer version. The rule is given with `--rule` as a JSON file, inline JSON, or `-` for standard input.
78
78
  - `segments preview --rule <rule>` counts without saving: how many match, how many are left after each condition, a few examples, the insights in scope, and warnings (a value no record has, with the closest ones; a source that is late). For a field often confused with another it also gives the count with the other field; with `--json` an `ask` line tells an agent to ask which one is meant, showing both. Nothing is guessed: a rule that would take too long stops and says so.
79
- - `segments define <name> --rule <rule> [--description <text>]` saves it as version 1; a segment in use is never changed in place, and other rules include it by its name. `segments list` numbers your segments with each rule in words, the segments it includes (and when one has a newer version) and the segments that include it.
79
+ - `segments define <name> --rule <rule> [--description <text>]` saves it as version 1; a segment in use is never changed in place, and other rules include it by its name. `segments list` shows your segments with each rule in words, the segments it includes (and when one has a newer version), the segments that include it, and each segment's id.
80
80
  - `closedloop metrics`: one calculation over one segment, or a formula over saved metrics, measured every night as one number.
81
- - `metrics preview --segment <name|number|id|rule> --calc <calc>` measures it now: `--calc count`, or JSON such as `{"fn":"sum","field":"customer.open_pipeline"}` or a share `{"fn":"ratio","numerator":{...},"denominator":{...}}` (totals over the whole segment, then divided). It says how many members have no value for the field, when a source it reads is late, and for a field often confused with another the value with the other field (and, with `--json`, an `ask` line).
82
- - `metrics define <name> --segment ... --calc ... [--description <text>]` saves it (a rule given inline is saved as a segment of its own, named after the metric, with a number when a segment already has that name; the metric's later inline rules are new versions of it); `metrics list` shows each with its calculation and its latest measured value, or why it is late (stale, with when the source was last updated); `metrics value <number|id> --as-of <day> [--metric-version <n>]` gives its value on a past day: the number measured that night, else computed now from the CRM history and the dated records (labelled rebuilt, not stored), else why there is none; a goal measures a saved metric with `goals metrics add --metric`, and keeps the version it was added with. `metrics history <number|id|name> [--days <n>] [--every day|week] [--metric-version <n>]` shows it day by day over the last 30 days (or the days given), oldest first: the value measured that night, else worked out from the history (labelled), else why there is none, and the change from the first value to the last; it asks nothing before the day the history starts, and when the API stops answering (the per-person limit, a busy workspace, a value too slow to work out) it still shows every stored day and says how many were not fetched. `metrics delete <number|id|name> [--reason <text>] [--yes]` deletes one: it comes off each of your goals that measures it (a key result takes its indicators) and its history stays; it is refused, with nothing changed, when a teammate's goal measures it, when it is an active goal's only key result, or when a formula uses it.
83
- - `metrics preview --formula <formula>` and `metrics define <name> --formula <formula>` combine saved metrics with `+ - * /` and brackets, with no segment: `"[Accounts churned this quarter] / [Paying accounts]"`, `"#2 * 12"` (a number from the last `metrics list`) or `"([MRR] - [MRR]@90d) / [MRR]@90d"` (`@90d`: its value 90 days earlier); a metric is named by its exact name in brackets, its list number or its id. The preview shows each input's value and how it was obtained. Saving pins each metric at its current version; the formula is measured every night after the metrics it uses, and has no value (with the reason) when one of them has none or a division is by 0. The same formula as JSON (`{"fn":"formula","op":"divide","args":[{"metric":"<id>"},{"metric":"<id>","days_ago":90}]}`) works through `--calc` without `--segment`.
81
+ - `metrics preview --segment <segment id|rule> --calc <calc>` measures it now (a saved segment by its id from `segments list`, or the rule itself as a JSON file, inline JSON or `-`): `--calc count`, or JSON such as `{"fn":"sum","field":"customer.open_pipeline"}` or a share `{"fn":"ratio","numerator":{...},"denominator":{...}}` (totals over the whole segment, then divided). It says how many members have no value for the field, when a source it reads is late, and for a field often confused with another the value with the other field (and, with `--json`, an `ask` line).
82
+ - `metrics define <name> --segment ... --calc ... [--description <text>]` saves it (a rule given inline is saved as a segment of its own, named after the metric, with a number when a segment already has that name; the metric's later inline rules are new versions of it); `metrics list` shows each with its calculation, its latest measured value, or why it is late (stale, with when the source was last updated), and its id; `metrics value <metric id> --as-of <day> [--metric-version <n>]` gives its value on a past day: the number measured that night, else computed now from the CRM history and the dated records (labelled rebuilt, not stored), else why there is none; a goal measures a saved metric with `goals metrics add --metric`, and keeps the version it was added with. `metrics history <metric id> [--days <n>] [--every day|week] [--metric-version <n>]` shows it day by day over the last 30 days (or the days given), oldest first: the value measured that night, else worked out from the history (labelled), else why there is none, and the change from the first value to the last; it asks nothing before the day the history starts, and when the API stops answering (the per-person limit, a busy workspace, a value too slow to work out) it still shows every stored day and says how many were not fetched. `metrics delete <metric id> [--reason <text>] [--yes]` deletes one: it comes off each of your goals that measures it (a key result takes its indicators) and its history stays; it is refused, with nothing changed, when a teammate's goal measures it, when it is an active goal's only key result, or when a formula uses it.
83
+ - `metrics preview --formula <formula>` and `metrics define <name> --formula <formula>` combine saved metrics with `+ - * /` and brackets, with no segment, each metric by its id from `metrics list`, alone or in square brackets: `"<id> / <id>"`, `"[<id>] * 12"` or `"(<id> - <id>@90d) / <id>@90d"` (`@90d`: its value 90 days earlier). A metric's name or a list number is refused. `metrics list` shows a saved formula with its metrics' names. The preview shows each input's value and how it was obtained. Saving pins each metric at its current version; the formula is measured every night after the metrics it uses, and has no value (with the reason) when one of them has none or a division is by 0. The same formula as JSON (`{"fn":"formula","op":"divide","args":[{"metric":"<id>"},{"metric":"<id>","days_ago":90}]}`) works through `--calc` without `--segment`.
84
84
  - With `--json` every answer is the API's, with the rule and calculation that scoped it and a `next` command.
85
- - `closedloop integrations list`: every integration, in three groups: needing attention (with the reason), connected (with the last sync), and available to connect (with how, and the plan it needs). `--connected` or `--available` shows one side only.
86
- - `closedloop integrations show <name>`: one integration's status, and what connecting it asks for.
87
- - `closedloop integrations connect <name>`: connects it the way it connects.
85
+ - `closedloop integrations list`: every integration, in three groups: needing attention (with the reason), connected (with the last sync), and available to connect (with how, and the plan it needs), each with its id. `--connected` or `--available` shows one side only. As JSON each carries `integration_id` (and its `slug`, as data).
86
+ - `closedloop integrations show <id>`: one integration's status, and what connecting it asks for. Every integrations command takes the integration's id from the list.
87
+ - `closedloop integrations connect <id>`: connects it the way it connects.
88
88
  - With an API key, it asks for the key without showing it, plus any settings (site, region). An agent pipes the key in with `--stdin` (a secret on a second line) and passes settings as `--field key=value`; a key is never accepted as an argument.
89
89
  - In the browser (OAuth), it opens the app's connect page and waits until the connection shows; an agent gets the link and runs the command again to check.
90
90
  - Asana, Attio, Discord, Front, Granola, Pipedrive, Plaud, Read.ai, Stripe, Zendesk and Microsoft Teams connect in the browser without signing in to the app. The CLI opens a one-time link (valid for up to 10 minutes) that names the workspace; Continue there sends your browser back to the CLI on this computer, which finishes the connect, so a link someone forwards connects nothing. The command waits up to 15 minutes for the connection: in a terminal it shows the link and opens it when you press Enter; otherwise (an agent, a pipe) it opens the browser itself and prints one answer at the end, so an agent runs it with a long timeout or in the background. Asana needs `--field asana_workspace_gid=<id>` and Zendesk `--field zendesk_subdomain=<subdomain>` (a terminal asks for them).
91
91
  - When the browser cannot come back to this computer, it gives the app's connect page instead (`status: browser_needed`, exit 0), with a `reason`: `remote_browser` (over SSH, in a Codespace, dev container, Gitpod, Cloud Shell, a container or Kubernetes), `no_local_listener` (the CLI could not listen on 127.0.0.1) or `browser_not_opened` (the browser could not be opened). `--in-app` asks for the app's connect page directly, for example when the confirm page's next step does not load. Settings given with `--field` are not used there; the app asks for them.
92
92
  - Integrations set up only in the app get a link to it. Wherever the CLI sends you to the app it says how to sign in there: with your email address, since an account made in the CLI has no password: choose "Email me a code" and the app emails you a sign-in code.
93
93
  - `--reconnect` connects again, for example with a new key.
94
- - `closedloop integrations pause <name>` / `resume <name>`: stop syncing without disconnecting, and start again.
95
- - `closedloop integrations disconnect <name>`: stops syncing after confirmation (`--yes` for agents); what was already imported stays.
96
- - `closedloop backfills`: the workspace's history imports, running ones first: source, window, state (queued, importing, done, failed, cancelled, or stalled when nothing moved for 30 minutes), progress in days or records, and for a running one an estimate of when it finishes, from the pace so far. `backfills show <id or source> [--watch]` gives one in detail, and `--watch` follows it every 30 seconds. Progress covers importing; insights appear as the records are analysed.
94
+ - `closedloop integrations pause <id>` / `resume <id>`: stop syncing without disconnecting, and start again.
95
+ - `closedloop integrations disconnect <id>`: stops syncing after confirmation (`--yes` for agents); what was already imported stays.
96
+ - `closedloop backfills`: the workspace's history imports, running ones first: source, window, state (queued, importing, done, failed, cancelled, or stalled when nothing moved for 30 minutes), progress in days or records, and for a running one an estimate of when it finishes, from the pace so far, each with its id. `backfills show <id> [--watch]` gives one in detail, and `--watch` follows it every 30 seconds. Progress covers importing; insights appear as the records are analysed.
97
97
  - `closedloop usage`: credits used this period, against the limit on the Free plan (when it resets, and when this pace reaches it) or with the cost so far on Pay as you go. The numbers are the Plan & billing page's.
98
98
  - `usage history [--months 12]`: credits per month, newest first.
99
99
  - `usage by-source [--month 2026-10]`: a month's credits per integration or feature, largest first.
@@ -111,11 +111,15 @@ One computer can stay signed in to several ClosedLoop AI accounts. Each keeps it
111
111
  - `closedloop use <email>`: makes that account the active one. An account that is not signed in here is refused with `account_not_signed_in`.
112
112
  - `--as <email>` runs one command as one of the accounts signed in here, before or after the command, without changing the active one; `CLOSEDLOOP_ACCOUNT=<email>` does the same for every command in that environment, and `--as` wins over it. Agents should name the account explicitly. (`--account` stays what it is in `insights search`: a customer's account domain, as in `closedloop insights search --account acme.com --as you@example.com`.) An account that is not signed in here is refused with `account_not_signed_in`.
113
113
  - `closedloop whoami` shows the active account, or the one named with `--as`.
114
- - Updating from 0.3.3 or earlier moves the saved sign-in into this layout on the first command, once: you stay signed in, it becomes the active account, and its working goal and numbered lists come with it.
114
+ - Updating from 0.3.3 or earlier moves the saved sign-in into this layout on the first command, once: you stay signed in, it becomes the active account, and its working goal comes with it.
115
+
116
+ ## Ids
117
+
118
+ Every list shows each item's id: its database id, exactly as ClosedLoop AI stores it, on a line of its own under the item (`id 3f2c9d4e-...`, always in full). As JSON each item carries it as `id` (an integration's as `integration_id`). Every command that shows, changes, confirms or removes one item takes that id, so copy it from the list. A name is only data, since two items can share one, and a position in a list is never used: a name or a number where an id belongs is refused with `invalid_id`, and the hint names the command that shows the ids.
115
119
 
116
120
  ## How it looks
117
121
 
118
- In a terminal the CLI writes for people: insights as short numbered blocks (`insights show 2` opens the second): the title, then who, where and when, then the start of the quote, in one column up to 100 characters wide; bars for usage, backfills and history; colour only where it means something (severity, problems, done), never blue; and a spinner while it waits. Colour and animation are off without a terminal, with `NO_COLOR`, or in a dumb terminal (`FORCE_COLOR=1` turns colour on). A program or an agent always gets plain JSON, so none of this reaches it.
122
+ In a terminal the CLI writes for people: insights as short blocks: the title, then who, where and when, then the start of the quote and the insight's id, in one column up to 100 characters wide; bars for usage, backfills and history; colour only where it means something (severity, problems, done), never blue; and a spinner while it waits. Colour and animation are off without a terminal, with `NO_COLOR`, or in a dumb terminal (`FORCE_COLOR=1` turns colour on). A program or an agent always gets plain JSON, so none of this reaches it.
119
123
 
120
124
  ## Rights
121
125
 
@@ -172,7 +176,7 @@ before assuming the package changed successfully.
172
176
  - `config_busy`: wait for other CLI commands to finish and retry. If a process stopped while holding a lock, inspect the named lock and confirm no CLI process is using it before removing it.
173
177
  - `config_migration_failed`: use `CLOSEDLOOP_CONFIG_DIR` to select the original folder; the original files were kept.
174
178
  - `session_changed`: a logout or newer sign-in took precedence while a request was running; repeat the command with the current sign-in. A delayed response does not restore a cancelled login.
175
- - `unknown_number`: list again in the current account and workspace, then select its number; saved numbers are not portable across accounts or workspaces.
179
+ - `invalid_id`: the command takes an item's id, not its name or a number from a list; run the command the hint names to see the ids, and copy the one you mean.
176
180
  - `api_unreachable` or `api_timeout`: check connectivity and retry; inspect an ingestion failure's accepted-record hint before resending a partially sent batch.
177
181
  - `rate_limited`: wait for the delay in the hint; the CLI does not automatically repeat a write.
178
182