@closedloop-ai/cli 0.2.4 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +106 -272
  2. package/dist/THIRD_PARTY_NOTICES.txt +24 -0
  3. package/dist/closedloop.js +156 -0
  4. package/package.json +31 -62
  5. package/CONTRIBUTING.md +0 -437
  6. package/LICENSE +0 -21
  7. package/docs/CLI_COMMANDS.md +0 -522
  8. package/packages/cli/dist/__tests__/api.test.js +0 -151
  9. package/packages/cli/dist/__tests__/api.test.js.map +0 -1
  10. package/packages/cli/dist/__tests__/cli.test.js +0 -37
  11. package/packages/cli/dist/__tests__/cli.test.js.map +0 -1
  12. package/packages/cli/dist/__tests__/config.test.js +0 -33
  13. package/packages/cli/dist/__tests__/config.test.js.map +0 -1
  14. package/packages/cli/dist/__tests__/errors.test.js +0 -109
  15. package/packages/cli/dist/__tests__/errors.test.js.map +0 -1
  16. package/packages/cli/dist/__tests__/performance.test.js +0 -221
  17. package/packages/cli/dist/__tests__/performance.test.js.map +0 -1
  18. package/packages/cli/dist/__tests__/setup.js +0 -50
  19. package/packages/cli/dist/__tests__/setup.js.map +0 -1
  20. package/packages/cli/dist/__tests__/team-simple.test.js +0 -38
  21. package/packages/cli/dist/__tests__/team-simple.test.js.map +0 -1
  22. package/packages/cli/dist/__tests__/test-factories.js +0 -148
  23. package/packages/cli/dist/__tests__/test-factories.js.map +0 -1
  24. package/packages/cli/dist/__tests__/validation.test.js +0 -124
  25. package/packages/cli/dist/__tests__/validation.test.js.map +0 -1
  26. package/packages/cli/dist/api/auth.js +0 -27
  27. package/packages/cli/dist/api/auth.js.map +0 -1
  28. package/packages/cli/dist/api/feedback.js +0 -70
  29. package/packages/cli/dist/api/feedback.js.map +0 -1
  30. package/packages/cli/dist/api/ingest.js +0 -60
  31. package/packages/cli/dist/api/ingest.js.map +0 -1
  32. package/packages/cli/dist/api/input.js +0 -60
  33. package/packages/cli/dist/api/input.js.map +0 -1
  34. package/packages/cli/dist/api/team.js +0 -59
  35. package/packages/cli/dist/api/team.js.map +0 -1
  36. package/packages/cli/dist/api/usage.js +0 -49
  37. package/packages/cli/dist/api/usage.js.map +0 -1
  38. package/packages/cli/dist/cli.js +0 -201
  39. package/packages/cli/dist/cli.js.map +0 -1
  40. package/packages/cli/dist/commands/config.js +0 -186
  41. package/packages/cli/dist/commands/config.js.map +0 -1
  42. package/packages/cli/dist/commands/feedback.js +0 -234
  43. package/packages/cli/dist/commands/feedback.js.map +0 -1
  44. package/packages/cli/dist/commands/ingest.js +0 -391
  45. package/packages/cli/dist/commands/ingest.js.map +0 -1
  46. package/packages/cli/dist/commands/input.js +0 -370
  47. package/packages/cli/dist/commands/input.js.map +0 -1
  48. package/packages/cli/dist/commands/insight.js +0 -234
  49. package/packages/cli/dist/commands/insight.js.map +0 -1
  50. package/packages/cli/dist/commands/list.js +0 -81
  51. package/packages/cli/dist/commands/list.js.map +0 -1
  52. package/packages/cli/dist/commands/status.js +0 -93
  53. package/packages/cli/dist/commands/status.js.map +0 -1
  54. package/packages/cli/dist/commands/submit.js +0 -225
  55. package/packages/cli/dist/commands/submit.js.map +0 -1
  56. package/packages/cli/dist/commands/team.js +0 -82
  57. package/packages/cli/dist/commands/team.js.map +0 -1
  58. package/packages/cli/dist/commands/usage.js +0 -168
  59. package/packages/cli/dist/commands/usage.js.map +0 -1
  60. package/packages/cli/dist/config/constants.js +0 -38
  61. package/packages/cli/dist/config/constants.js.map +0 -1
  62. package/packages/cli/dist/config/index.js +0 -84
  63. package/packages/cli/dist/config/index.js.map +0 -1
  64. package/packages/cli/dist/types/index.js +0 -3
  65. package/packages/cli/dist/types/index.js.map +0 -1
  66. package/packages/cli/dist/utils/config.js +0 -55
  67. package/packages/cli/dist/utils/config.js.map +0 -1
  68. package/packages/cli/dist/utils/errors.js +0 -70
  69. package/packages/cli/dist/utils/errors.js.map +0 -1
  70. package/packages/cli/dist/utils/validation.js +0 -133
  71. package/packages/cli/dist/utils/validation.js.map +0 -1
package/README.md CHANGED
@@ -1,279 +1,113 @@
1
1
  # ClosedLoop AI CLI
2
2
 
3
- > Transform scattered customer product feedback into actionable strategic insights with AI-powered analysis
3
+ `closedloop`: ClosedLoop AI on the command line, for people and for AI coding agents (Claude Code, Codex, Cursor).
4
4
 
5
- [![npm version](https://badge.fury.io/js/%40closedloop-ai%2Fcli.svg)](https://badge.fury.io/js/%40closedloop-ai%2Fcli)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
- [![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/)
8
- [![Node.js](https://img.shields.io/badge/Node.js-16%2B-green.svg)](https://nodejs.org/)
5
+ Requires Node.js 22.12 or newer.
9
6
 
10
- ## 🎯 The Problem You're Solving
11
-
12
- **Product teams are drowning in scattered product feedback** from multiple channels:
13
- - Support tickets scattered across Zendesk, Intercom, and email
14
- - Sales and customer success call insights buried in transcripts
15
- - User surveys and feedback forms in different tools
16
- - Social media mentions and app store reviews
17
- - Internal team discussions in Slack
18
-
19
- **The result?** Critical customer product feedback get lost, patterns go unnoticed, and product decisions are made without data.
20
-
21
- ## ✨ How ClosedLoop AI CLI Solves This
22
-
23
- **Transform chaos into clarity** with autonomous AI that:
24
-
25
- - 🔍 **Discovers Hidden Patterns** - AI finds insights humans would miss across all your feedback sources
26
- - ⚡ **Real-Time Analysis** - Get instant insights as new customer product feedback comes in
27
- - 📊 **Strategic Intelligence** - Convert customer voice into actionable product decisions
28
- - 🎯 **Revenue Impact** - Quantify the business value of each customer feedback
29
- - 🔄 **Source Agnostic** - Works with any data source through flexible APIs
30
-
31
- ## 🚀 Quick Start
32
-
33
- ### Installation
34
-
35
- ```bash
36
- # Install globally for CLI usage
7
+ ```sh
37
8
  npm install -g @closedloop-ai/cli
9
+ closedloop login
10
+ closedloop --help
38
11
  ```
39
12
 
40
- ### Get Your API Key
41
-
42
- 1. Visit [https://closedloop.sh](https://closedloop.sh)
43
- 2. Sign up for a free account (100,000 credits every month included!)
44
- 3. Generate your API key
45
- 4. Configure the CLI:
46
-
47
- ```bash
48
- cl config set --api-key your-api-key-here
49
- ```
50
-
51
- ### Set Your Product Website (Important!)
52
-
53
- Configure your product's website URL to help ClosedLoop understand your product context:
54
-
55
- ```bash
56
- cl team website "https://yourproduct.com/feature"
57
- ```
58
-
59
- **Why this matters:** ClosedLoop AI uses your product URL to learn about your product features, competitors, pricing, and positioning. This enables more accurate feedback attribution and product-specific insights. For example, if you're working on iPhone, use `https://apple.com/iphone` - this helps ClosedLoop AI understand that "camera quality" feedback is about iPhone cameras, not just any camera.
60
-
61
- ### Ingest Your First Customer Feedback
62
-
63
- ```bash
64
- # Ingest raw customer feedback for AI analysis
65
- cl ingest "The new dashboard is confusing and hard to navigate. I can't find the settings menu and the layout is cluttered."
66
-
67
- # Wait for AI to analyze and generate structured insights
68
- cl ingest "Customer complaint about performance issues" --wait
69
-
70
- # View all AI-generated customer product feedback
71
- cl insight
72
-
73
- # Get detailed analysis of a specific insight
74
- cl insight 2ea8f556-052b-4f5c-bf86-833780b3d00d
75
- ```
76
-
77
- ## 🎯 Perfect For
78
-
79
- ### Customer Success Teams
80
- - **Stop losing critical insights** buried in support tickets
81
- - **Identify patterns** across customer conversations
82
- - **Proactive customer success** with early warning signals
83
- - **Data-driven customer health** scoring
84
-
85
- ### Product Managers
86
- - **Prioritize features** based on real customer needs
87
- - **Quantify business impact** of product decisions
88
- - **Track feature adoption** and customer satisfaction
89
- - **Competitive intelligence** from customer feedback
90
-
91
- ### Business Analysts
92
- - **Strategic insights** from customer behavior patterns
93
- - **Revenue impact** analysis of customer feedback
94
- - **Market intelligence** and trend analysis
95
- - **ROI calculations** for product investments
96
-
97
- ## 📖 CLI Commands
98
-
99
- ### Ingest Customer Feedback
100
-
101
- ```bash
102
- # Basic ingestion
103
- cl ingest "The app crashes when I try to upload large files"
104
-
105
- # With metadata for better analysis
106
- cl ingest "Great product, love the new features!" \
107
- --title "Positive Feedback" \
108
- --customer "customer-456" \
109
- --name "Jane Smith" \
110
- --email "jane@example.com" \
111
- --url "https://support.example.com/ticket/789"
112
-
113
- # Wait for AI analysis to complete
114
- cl ingest "Customer complaint about performance" --wait
115
- ```
116
-
117
- ### View AI-Generated Customer Product Feedback
118
-
119
- ```bash
120
- # List all structured insights
121
- cl insight
122
-
123
- # View specific insight details
124
- cl insight <insight-id>
125
-
126
- # Get JSON output for scripting
127
- cl insight --json | jq '.data[0].title'
128
- ```
129
-
130
- ### Manage Your Data
131
-
132
- ```bash
133
- # List all customer feedback ingested
134
- cl ingest
135
-
136
- # View specific feedback details
137
- cl ingest <feedback-id>
138
-
139
- # Check processing status
140
- cl ingest <feedback-id> --status
141
- ```
142
-
143
- ## 🔧 Configuration
144
-
145
- The CLI stores configuration in `~/.closedloop/config.json`:
146
-
147
- ```json
148
- {
149
- "apiKey": "your-api-key-here"
150
- }
151
- ```
152
-
153
- ### Environment Variables (Advanced)
154
-
155
- **For developers and CI/CD systems**, you can also set your API key as an environment variable instead of using the config file:
156
-
157
- ```bash
158
- # Set the API key as an environment variable
159
- export CLOSEDLOOP_API_KEY="your-api-key-here"
160
-
161
- # Now you can use the CLI without running 'cl config set'
162
- cl input "Customer feedback here"
163
- ```
164
-
165
- **When to use this:**
166
- - **CI/CD pipelines**: Set the API key in your deployment environment
167
- - **Docker containers**: Pass the API key as an environment variable
168
- - **Server scripts**: When running automated scripts on servers
169
- - **Multiple users**: When different users need different API keys on the same machine
170
-
171
- **Note:** The CLI will automatically use the environment variable if it's set, otherwise it falls back to the config file.
172
-
173
- ## 💡 Simple Workflow
174
-
175
- Here's how to get started with ClosedLoop AI CLI:
176
-
177
- ```bash
178
- # 1. Set your API key
179
- cl config set --api-key your-api-key-here
180
-
181
- # 2. Set your product website (helps AI understand your product)
182
- cl team website "https://yourproduct.com/feature"
183
-
184
- # 3. Ingest customer feedback
185
- cl ingest "The new dashboard is confusing and hard to navigate"
186
-
187
- # 4. Check what feedback you've ingested
188
- cl ingest
189
-
190
- # 5. View AI analysis of your feedback
191
- cl insight
192
-
193
- # 6. Get details on specific feedback
194
- cl insight <insight-id>
195
- ```
196
-
197
- That's it! ClosedLoop AI will automatically analyze your feedback and provide actionable insights.
198
-
199
- ## 📚 Documentation
200
-
201
- - **[Complete CLI Commands Reference](docs/CLI_COMMANDS.md)** - All commands, parameters, and examples
202
- - **[Contributing Guide](CONTRIBUTING.md)** - Development and contribution guidelines
203
-
204
- ## 🎯 Why This Matters
205
-
206
- ### Before ClosedLoop AI CLI
207
- - ❌ **Scattered feedback** across 5+ different tools
208
- - ❌ **Manual analysis** taking 20+ hours per week
209
- - ❌ **Missed patterns** and critical insights
210
- - ❌ **Reactive decisions** based on incomplete data
211
- - ❌ **Lost revenue** from unaddressed customer needs
212
-
213
- ### After ClosedLoop AI CLI
214
- - ✅ **Unified intelligence** from all feedback sources
215
- - ✅ **Automated analysis** with AI pattern recognition
216
- - ✅ **Proactive insights** delivered in real-time
217
- - ✅ **Data-driven decisions** with quantified business impact
218
- - ✅ **Increased revenue** from better customer understanding
219
-
220
- ## 📊 Business Impact
221
-
222
- - **80% faster** analysis time (from 20 hours to 4 hours per week)
223
- - **40% faster** feature prioritization decisions
224
- - **25% increase** in customer satisfaction scores
225
- - **35% increase** in feature adoption rates
226
- - **50% reduction** in failed feature launches
227
-
228
- ## 🧪 Development
229
-
230
- ### Prerequisites
231
-
232
- - Node.js 16+
233
- - npm or yarn
234
- - TypeScript 5+
235
-
236
- ### Setup
237
-
238
- ```bash
239
- # Clone the repository
240
- git clone https://github.com/closed-loop-ai/mcp-client.git
241
- cd mcp-client
242
-
243
- # Install dependencies
244
- npm install
245
-
246
- # Build the CLI
247
- npm run build:cli
248
-
249
- # Run tests
250
- npm test
251
- ```
252
-
253
- ## 🤝 Contributing
254
-
255
- We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
256
-
257
- ## 📄 License
258
-
259
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
260
-
261
- ## 🔗 Links
262
-
263
- - **Platform**: [https://closedloop.sh](https://closedloop.sh)
264
- - **Documentation**: [https://docs.closedloop.sh](https://docs.closedloop.sh)
265
- - **GitHub Issues**: [https://github.com/closed-loop-ai/closedloop-cli/issues](https://github.com/closed-loop-ai/closedloop-cli/issues)
266
- - **NPM Package**: [https://www.npmjs.com/package/@closedloop-ai/cli](https://www.npmjs.com/package/@closedloop-ai/cli)
267
-
268
- ## 🆘 Support
269
-
270
- - **Documentation**: Check our [docs](https://docs.closedloop.sh)
271
- - **Issues**: Report bugs on [GitHub Issues](https://github.com/closed-loop-ai/closedloop-cli/issues)
272
- - **Discussions**: Join our [GitHub Discussions](https://github.com/closed-loop-ai/closedloop-cli/discussions)
273
- - **Email**: support@closedloop.sh
274
-
275
- ---
276
-
277
- **Made with ❤️ by ClosedLoop AI**
278
-
279
- *Transform customer product feedback chaos into strategic product decisions with AI-powered insights.*
13
+ The command is `closedloop`, with `cl` as a short form. Both use the same minified bundle; the source repository stays private.
14
+
15
+ `npm run build` writes the CLI as one minified file, `dist/closedloop.js`, with its one library (commander) inside; that file is all the package installs.
16
+
17
+ ## Updates
18
+
19
+ - Every call sends the CLI's version (`X-ClosedLoop-CLI-Version`), and the API answers with the newest version and the oldest one it still serves (`X-ClosedLoop-CLI-Latest`, `X-ClosedLoop-CLI-Minimum`).
20
+ - In a terminal, a newer version prints one line on stderr after the answer, at most once a day per version. It stays quiet for JSON output, in CI, and with `NO_UPDATE_NOTIFIER=1` or `CLOSEDLOOP_NO_UPDATE_NOTIFIER=1`. An agent reads `cli` from `closedloop whoami --json` (`version`, `latest_version`, `update_available`). Nothing updates itself.
21
+ - A copy older than the minimum is refused with the error `upgrade_required`, which names the version to update to.
22
+
23
+ The npm package is `@closedloop-ai/cli`; users of the earlier `cl` CLI can update with the same install command.
24
+
25
+ ## Commands
26
+
27
+ - `closedloop login` (also `closedloop signup`): sign in, or create an account on the Free plan, with a code sent to your email.
28
+ - In a terminal, `closedloop login` asks for the email and the code.
29
+ - For an agent, two runs: `closedloop login --email you@example.com` sends the code; `closedloop login --email you@example.com --code BCDFG-HJKLM` finishes. Add `--totp <code>` (or `--backup-code <code>`) when the account uses two-factor authentication.
30
+ - `closedloop login --sso`: sign in through your browser instead, for workspaces that sign in with SSO (Entra, Okta). It prints a link and a short code; open the link, sign in to the app, type the code and approve. In a terminal it also copies the code to your clipboard and opens the link when you press Enter, then waits; an agent runs it again after you approve.
31
+ - An email whose account is in the other region (US or EU) signs in there, and the session stays with that region.
32
+ - A new account gets its own Free workspace unless the email was invited to one. One network can create three such accounts a day.
33
+ - 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>`.
34
+ - 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.
35
+ - The session is saved in `credentials.json` in the config folder, readable by you only.
36
+ - 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.
37
+ - `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.
38
+ - `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`.
39
+ - `--watch` checks every 30 seconds while anything is running, for up to 2 hours.
40
+ - `closedloop company`: your company website and how far the research of it has got (waiting to start, in progress, trying again, failed, done), and its email domains. Every source still imports before both are done, but nothing is analysed into insights until they are (the feedback portal is the one exception).
41
+ - `--website <url>` sets it, with the same checks as the app (the site must be readable). Setting it needs the right to change the workspace's settings (an owner or admin).
42
+ - `--domains acme.example,acme.example.org` confirms your colleagues' email domains and `--no-domains` confirms your company has none (every colleague then counts as a customer, so results are wrong). Only a person confirms them: in a terminal the CLI asks you to type `yes`; anyone else adds `--confirmed-by-user`, which an agent does only after its user said yes. Without it nothing is saved and the result's `instructions` say what to ask. A personal email domain (gmail.com and the like) is refused. Needs the right to change the workspace's settings.
43
+ - As JSON, a workspace whose domains still wait carries `instructions`: what an agent should ask its user.
44
+ - `--watch` checks every 30 seconds until the research is done or fails, for up to an hour.
45
+ - `closedloop ingest [text]`: sends your own data in (feedback, notes, survey answers, transcripts) for the pipeline to turn into insights.
46
+ - 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.
47
+ - 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.
48
+ - `--wait` follows up to 100 records until they are processed and says how many insights they made.
49
+ - `ingest list` shows the latest records and their state (waiting, processing, done with N insights, failed); `ingest status <id>` one record.
50
+ - 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.
51
+ - `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.
52
+ - `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.
53
+ - `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).
54
+ - `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.
55
+ - `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.
56
+ - `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.
57
+ - `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.
58
+ - `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.
59
+ - `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).
60
+ - `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.
61
+ - `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.
62
+ - `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.
63
+ - `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.
64
+ - `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.
65
+ - `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.
66
+ - `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.
67
+ - `closedloop metrics`: one calculation over one segment, or a formula over saved metrics, measured every night as one number.
68
+ - `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).
69
+ - `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.
70
+ - `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`.
71
+ - With `--json` every answer is the API's, with the rule and calculation that scoped it and a `next` command.
72
+ - `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.
73
+ - `closedloop integrations show <name>`: one integration's status, and what connecting it asks for.
74
+ - `closedloop integrations connect <name>`: connects it the way it connects.
75
+ - 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.
76
+ - 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.
77
+ - 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).
78
+ - 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.
79
+ - Integrations set up only in the app get a link to it.
80
+ - `--reconnect` connects again, for example with a new key.
81
+ - `closedloop integrations pause <name>` / `resume <name>`: stop syncing without disconnecting, and start again.
82
+ - `closedloop integrations disconnect <name>`: stops syncing after confirmation (`--yes` for agents); what was already imported stays.
83
+ - `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.
84
+ - `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.
85
+ - `usage history [--months 12]`: credits per month, newest first.
86
+ - `usage by-source [--month 2026-10]`: a month's credits per integration or feature, largest first.
87
+ - `usage export [--month 2026-10] [--output file] [--force]`: saves the month's usage per day and integration as a CSV, never over an existing file unless `--force`.
88
+ - `closedloop plan`: your plan and what it includes; `plan upgrade` opens Plan & billing (payment happens in the browser), or gives an agent the link.
89
+ - `closedloop logout`: forgets the saved sign-in on this computer. Your other sessions, the web app included, stay signed in.
90
+
91
+ ## How it looks
92
+
93
+ 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.
94
+
95
+ ## Rights
96
+
97
+ Every command does only what the person may do in the app, and the API decides: connecting, pausing, resuming and disconnecting integrations, and sending data in, need the right to manage integrations; reading insights, integrations, usage and backfills is open to every member. The CLI reads the person's rights from the API first, so someone without one is told before anything is read, sent or opened, and whom to ask; `plan upgrade` gives them the plan page without opening it. `whoami` lists what the person can do.
98
+
99
+ ## Settings
100
+
101
+ - `CLOSEDLOOP_API_URL`: the API to talk to. Defaults to the production API (`https://api.closedloop.sh`); existing EU accounts sign in through the EU API automatically. Use `http://localhost:3017` for an API running on your machine.
102
+ - `CLOSEDLOOP_CONFIG_DIR`: where the CLI keeps its files. Defaults to `$XDG_CONFIG_HOME/closedloop`, else `~/.config/closedloop`.
103
+
104
+ ## Principles
105
+
106
+ - **The API holds the logic.** A command calls the ClosedLoop AI API and shapes the answer. The CLI itself only does what has to happen on the user's machine: signing in, reading local files, and printing.
107
+ - **Agents are first-class users.** Every command works without prompts, prints JSON when a program reads it, and fails with a stable code and a hint the agent can act on.
108
+
109
+ ## Exit codes
110
+
111
+ `0` success, `1` failure, `2` invalid command or options. Piped output is JSON; add `--json` to request JSON in a terminal.
112
+
113
+ All addresses and company names shown in examples are invented placeholders.
@@ -0,0 +1,24 @@
1
+ The ClosedLoop AI CLI bundles commander (https://github.com/tj/commander.js):
2
+
3
+ (The MIT License)
4
+
5
+ Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca>
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining
8
+ a copy of this software and associated documentation files (the
9
+ 'Software'), to deal in the Software without restriction, including
10
+ without limitation the rights to use, copy, modify, merge, publish,
11
+ distribute, sublicense, and/or sell copies of the Software, and to
12
+ permit persons to whom the Software is furnished to do so, subject to
13
+ the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be
16
+ included in all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
19
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.