@senso-ai/cli 0.12.0 → 0.15.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 (3) hide show
  1. package/README.md +176 -423
  2. package/dist/cli.js +4239 -2601
  3. package/package.json +54 -24
package/README.md CHANGED
@@ -1,490 +1,243 @@
1
1
  # Senso CLI
2
2
 
3
- > Infrastructure for the Agentic Web
3
+ [![Testing](https://github.com/AI-Template-SDK/senso-user-cli/actions/workflows/testing.yml/badge.svg)](https://github.com/AI-Template-SDK/senso-user-cli/actions/workflows/testing.yml)
4
+ [![npm version](https://img.shields.io/npm/v/@senso-ai/cli.svg)](https://www.npmjs.com/package/@senso-ai/cli)
5
+ [![npm downloads](https://img.shields.io/npm/dm/@senso-ai/cli.svg)](https://www.npmjs.com/package/@senso-ai/cli)
6
+ [![Node](https://img.shields.io/node/v/@senso-ai/cli.svg)](https://nodejs.org)
7
+ [![License: AGPL v3](https://img.shields.io/badge/license-AGPL--3.0-blue.svg)](LICENSE)
4
8
 
5
- The official command-line interface for [Senso](https://docssenso.ai). Search your knowledge base, manage content, and control your entire Senso organization — directly from the terminal.
9
+ > Infrastructure for the Agentic Web
6
10
 
7
- Built for **AI agents** (Claude Code, Gemini CLI, Codex) and **developers** who want fast, scriptable access to their Senso workspace.
11
+ The command-line interface for [Senso](https://docs.senso.ai). Search your
12
+ knowledge base, manage content, run the generation engine and read your GEO
13
+ analytics — from a terminal, a script, or an AI agent.
8
14
 
9
- ---
15
+ Built for **agents** (Claude Code, Gemini CLI, Codex) as much as for people:
16
+ stdout carries only the payload, `--output json` is available on every command,
17
+ and failures exit with a code that says what went wrong.
10
18
 
11
- ## Quick Install
19
+ ## Install
12
20
 
13
- Requires **Node.js 18+**. Works on **Linux**, **macOS**, and **Windows**.
21
+ Requires **Node.js 20.12 or newer**. Works on Linux, macOS and Windows.
14
22
 
15
23
  ```bash
16
24
  npm install -g @senso-ai/cli
17
- ```
18
-
19
- Verify it works:
20
-
21
- ```bash
22
25
  senso --version
23
26
  ```
24
27
 
25
- > **One-off usage** — If you just want to try it without installing globally:
26
- >
27
- > ```bash
28
- > npx @senso-ai/cli --help
29
- > ```
30
-
31
- ## Quick Start
32
-
33
- ### 1. Authenticate
34
-
35
- Get an API key from [docs.senso.ai](https://docs.senso.ai), then:
28
+ Or without installing:
36
29
 
37
30
  ```bash
38
- senso login
31
+ npx @senso-ai/cli --help
39
32
  ```
40
33
 
41
- The interactive prompt will walk you through pasting your key and verifying it against your organization.
42
-
43
- Alternatively, set an environment variable (useful for CI and AI agents):
44
-
45
- ```bash
46
- export SENSO_API_KEY=tgr_your_key_here
47
- ```
48
-
49
- ### 2. Search your knowledge base
34
+ ## 60 seconds
50
35
 
51
36
  ```bash
52
- senso search "What are the current mortgage rates?"
53
- ```
54
-
55
- ### 3. List your content
56
-
57
- ```bash
58
- senso content list
59
- ```
60
-
61
- That's it. Every endpoint in the Senso Org API is available as a CLI command.
62
-
63
- ---
64
-
65
- ## Platform Support
66
-
67
- | Platform | Supported | Config Location |
68
- |----------|-----------|-----------------|
69
- | **Linux** | Node 18+ | `~/.config/senso/config.json` |
70
- | **macOS** | Node 18+ | `~/Library/Preferences/senso/config.json` |
71
- | **Windows** | Node 18+ | `%APPDATA%\senso\config.json` |
72
-
73
- Config paths are handled automatically via XDG-compatible directories. The config file stores your API key, org info, and update check timestamps with owner-only permissions (`0600`).
74
-
75
- ---
76
-
77
- ## Usage with AI Agents
78
-
79
- The CLI is designed as a first-class tool for AI LLM agents. Pass `--output json` to get structured output and `--quiet` to suppress banners:
80
-
81
- ```bash
82
- # Claude Code / Gemini CLI / Codex can call this directly
83
- senso search "customer refund policy" --output json --quiet
84
-
85
- # Pass API key inline (no config file needed)
86
- senso search "billing FAQ" --api-key tgr_... --output json --quiet
87
- ```
88
-
89
- ### Auth Priority
90
-
91
- The CLI resolves credentials in this order:
92
-
93
- 1. `--api-key` flag (highest priority)
94
- 2. `SENSO_API_KEY` environment variable
95
- 3. `~/.config/senso/config.json` stored key
96
- 4. Interactive prompt (first-run only)
97
-
98
- For non-interactive use (CI, agents), set the env var or pass the flag.
99
-
100
- ---
101
-
102
- ## Commands
103
-
104
- ### Authentication
105
-
106
- ```
107
- senso login Authenticate with Senso (interactive)
108
- senso logout Remove stored credentials
109
- senso whoami Show current org and auth status
110
- ```
111
-
112
- ### Search
113
-
114
- ```
115
- senso search <query> Semantic search with AI-generated answer + source chunks
116
- senso search context <query> Chunks only (no AI answer, faster)
117
- senso search content <query> Content IDs only (deduplicated)
118
- ```
119
-
120
- Options: `--max-results <n>`
121
-
122
- ### Content
123
-
124
- ```
125
- senso content list List all knowledge base items
126
- senso content get <id> Get full content detail by ID
127
- senso content versions <id> List version history for a content item
128
- senso content delete <id> Delete content (knowledge base + external)
129
- senso content unpublish <id> Unpublish and revert to draft
130
- senso content verification List items in verification workflow
131
- senso content verification-counts Counts by status + published-domain summaries
132
- senso content reject <versionId> Reject a content version
133
- senso content restore <versionId> Restore rejected version to draft
134
- senso content owners <id> List owners of a content item
135
- senso content set-owners <id> Replace owners (--user-ids)
136
- senso content remove-owner <id> <userId> Remove a single owner
137
- ```
138
-
139
- Options: `content list` supports `--limit`, `--offset`, `--search`, `--sort`. `content verification` supports `--limit`, `--offset`, `--search`, `--status`, `--substatus`. `content reject` supports `--reason`.
140
-
141
- ### Generated Content (GEO)
142
-
143
- ```
144
- senso generated-content list List published or draft generated content (--status published|drafts)
145
- senso generated-content get <id> Get a generated content item with its rendered body
146
- ```
147
-
148
- Options: `generated-content list` supports `--status`, `--limit`, `--offset`, `--search`.
149
-
150
- ### Content Generation
151
-
152
- ```
153
- senso generate settings Get generation settings
154
- senso generate update-settings Update generation settings (--data)
155
- senso generate sample Generate sample for a prompt; waits for async job by default (--prompt-id, --content-type-id, --no-wait)
156
- senso generate run Trigger a content engine run (--prompt-ids)
157
- ```
158
-
159
- ### Content Engine
160
-
161
- ```
162
- senso engine publish Publish content to external destinations (--data)
163
- senso engine draft Save content as draft for review (--data)
164
- ```
165
-
166
- ### Tracked Competitors & Sources
37
+ # 1. Authenticate. Get a key from https://docs.senso.ai
38
+ export SENSO_API_KEY=tgr_your_key_here # or run: senso login
167
39
 
168
- ```
169
- senso competitors list List tracked competitors
170
- senso competitors add Add a competitor (--name, --url)
171
- senso competitors suggest Get AI-generated competitor suggestions
172
- senso competitors batch-add Add up to 50 competitors (--data)
173
- senso competitors update <id> Update a competitor (--name, --url)
174
- senso competitors delete <id> Remove a competitor
175
-
176
- senso tracked-sources list List citation-classification rules
177
- senso tracked-sources add Add a rule (--pattern, --match-type, --tier)
178
- senso tracked-sources update <id> Replace a rule (--pattern, --match-type, --tier)
179
- senso tracked-sources delete <id> Remove a rule
180
- ```
181
-
182
- Options: `tracked-sources add`/`update` support `--category`, `--label`, `--priority`; `update` also supports `--active`/`--no-active`. `--match-type` is one of `domain | host | path_prefix | exact_url`; `--tier` is one of `primary | tracked | secondary`.
40
+ # 2. Ask your knowledge base a question
41
+ senso search "what is our refund policy?"
183
42
 
184
- ### Ingestion
185
-
186
- ```
187
- senso ingest upload <files...> Upload files to knowledge base (up to 10)
188
- senso ingest reprocess <contentId> <file> Re-ingest content with new file
189
- ```
190
-
191
- ### Brand Kit & Content Types
192
-
193
- ```
194
- senso brand-kit get Get brand kit guidelines
195
- senso brand-kit set Create or replace brand kit (--data)
196
-
197
- senso content-types list List content types
198
- senso content-types create Create a content type (--data)
199
- senso content-types get <id> Get content type by ID
200
- senso content-types update <id> Update a content type (--data)
201
- senso content-types delete <id> Delete a content type
43
+ # 3. The same thing, for a program
44
+ senso search "what is our refund policy?" --output json | jq -r .answer
202
45
  ```
203
46
 
204
- Options: `content-types list` supports `--limit`, `--offset`.
47
+ That third line is the point of this tool. There is no `--quiet` in it because
48
+ none is needed: stdout carries the payload and nothing else.
205
49
 
206
- ### Prompts
50
+ ## Using it from an agent
207
51
 
208
- ```
209
- senso prompts list List prompts (geo questions)
210
- senso prompts create Create a prompt (--data)
211
- senso prompts get <promptId> Get prompt with run history
212
- senso prompts delete <promptId> Delete a prompt
213
- ```
214
-
215
- Options: `prompts list` supports `--limit`, `--offset`, `--search`, `--sort`.
216
-
217
- ### Analytics (GEO)
218
-
219
- Read-only metrics for your own organization: how often the AI models name your brand, your share of every brand mention the models made, and which domains and pages get cited. Works with the organization API key from `senso login`.
220
-
221
- ```
222
- senso analytics summary Headline metrics + previous window + deltas
223
- senso analytics mentions Visibility time series (day or week buckets)
224
- senso analytics citations Citation rates and shares, with the series
225
- senso analytics domains Cited domains, ranked (coverage + share)
226
- senso analytics pages Cited pages, with the prompts driving them
227
- senso analytics prompts Per-prompt performance table
228
- senso analytics prompt <promptId> One prompt: history + latest full answers
229
- senso analytics answers Latest answer per prompt × model × location
230
- senso analytics glossary Canonical definition of every metric
231
- senso analytics filters Filter values that have data for this org
232
- ```
52
+ The whole contract, in one place.
233
53
 
234
- Options:
54
+ **Streams.** stdout is the payload. Everything else — progress, warnings,
55
+ errors, the banner — goes to stderr. Redirecting stdout to a file gives you data
56
+ or an empty file, never a sentence you have to strip.
235
57
 
236
- | Command | Options |
237
- |---------|---------|
238
- | all except `prompt <promptId>`, `glossary`, `filters` | `--from`, `--to`, `--models`, `--location`, `--prompt-type`, `--tag` |
239
- | `mentions`, `citations` | `--group-by <day\|week>` |
240
- | `domains` | `--tier`, `--domain-contains`, `--sort <citations\|coverage>`, `--limit`, `--offset` |
241
- | `pages` | `--tier`, `--domain`, `--domain-contains`, `--url-contains`, `--sort <citations\|coverage>`, `--limit`, `--offset` |
242
- | `prompts` | `--search`, `--sort <mention_rate\|share_of_voice\|citations\|answered\|text>`, `--order <asc\|desc>`, `--limit`, `--offset` |
243
- | `prompt <promptId>` | `--from`, `--to`, `--models`, `--location`, `--no-include-answers` |
244
- | `answers` | `--from`, `--to`, `--models`, `--location`, `--prompt-type`, `--tag`, `--mentioned <bool>`, `--cited <bool>`, `--citation-tier <primary\|tracked\|secondary>`, `--limit`, `--offset` |
58
+ **Formats.** Every command takes `--output`:
245
59
 
246
- `--from`/`--to` are `YYYY-MM-DD` and default to the 30 days ending at the most recent day that has data for your model/location filter (max window: 365 days). `--models` and `--location` are comma-separated; locations are case-sensitive exact codes (`US`, `US/California`) — the API also accepts `locations` as an alias for the `location` query param. Run `senso analytics filters` to see the values that actually have data.
60
+ | Format | For | Notes |
61
+ | ------- | ------------------ | ----------------------------------------------- |
62
+ | `json` | scripts and agents | The API payload, unmodified. Implies `--quiet`. |
63
+ | `table` | reading a list | Aligned columns; long cells truncated. |
64
+ | `plain` | reading one thing | Complete and untruncated. The default. |
247
65
 
248
- `analytics answers` is a snapshot, not a window. It always returns the newest stored answer per prompt × model × location, and `--from`/`--to` filter on `run_at` — when that answer was collected. Narrowing the window therefore **hides** prompt × model × location combinations whose latest answer falls outside it; it does not return older answers in their place. Use `analytics mentions` or `analytics citations` for history.
66
+ **Exit codes.** Branch on these rather than on message text.
249
67
 
250
- Reading the numbers:
251
-
252
- - **Share of Voice** is your mention instances ÷ `brand_mention_total` mentions of *every* brand the models named, not just your tracked competitors. It matches the Share of Voice in the Senso app. `tracked_mention_total` is still returned as a raw count in `totals`, but it is not the denominator.
253
- - **Citation Rate** and **Citation Coverage** divide by `D` — answers with at least one citation. **Citation Share** divides by `S` — total citation instances. They are different metrics on different denominators; every table shows the numerator and denominator next to the percentage so you can check.
254
- - The three tier **rates** are independent and can sum past 100% (one answer can cite an owned page and an external page). The three tier **shares** partition and sum to exactly 100%.
255
- - A metric renders as `—` when its denominator was zero. That is "not measured", not 0%.
256
- - Every response carries `notes[]` — caveats about window truncation, null denominators and tracking-set dependence. They are printed under a **Notes** heading in `plain` and `table` output, and are part of the payload in `--output json`.
257
- - `senso analytics glossary` is the canonical definition, denominator and gotcha for every metric.
258
-
259
- ### Industry Intelligence (partner key required)
260
-
261
- ```
262
- senso industries list List industries visible to the partner
263
- senso industries summary <industry> Industry overview over a time window
264
- senso industries brand <industry> <brandName> One brand within an industry
265
- senso industries domain <industry> <domainOrUrl> Domain/URL citation lookup
266
- senso industries prompt-metrics <industry> Per-prompt industry metrics
267
- senso industries glossary Competitive-intelligence metric glossary
268
- ```
269
-
270
- These commands read partner-scoped endpoints and **require a partner API key**. The organization key stored by `senso login` is rejected with a 401/403 — pass a partner key with `--api-key <key>` or `SENSO_API_KEY`. For metrics about your own organization, use `senso analytics` instead.
271
-
272
- Options: all except `list` and `glossary` support `--from`, `--to`, `--location`, `--models`; `prompt-metrics` also supports `--limit`, `--offset`; `list` supports `--search`. The `<industry>` argument accepts a UUID or a name (e.g. `"Automotive"`).
273
-
274
- ### Organization
275
-
276
- ```
277
- senso org get Get organization details
278
- senso org update Update organization details (--data)
279
-
280
- senso users list List users
281
- senso users add Add a user (--data)
282
- senso users get <userId> Get user details
283
- senso users update <userId> Update a user's role (--data)
284
- senso users remove <userId> Remove a user
285
- senso users set-current <userId> Set org as current for a user
286
-
287
- senso api-keys list List API keys
288
- senso api-keys create Create API key (--data)
289
- senso api-keys get <keyId> Get API key details
290
- senso api-keys update <keyId> Update API key (--data)
291
- senso api-keys delete <keyId> Delete API key
292
- senso api-keys revoke <keyId> Revoke API key
293
-
294
- senso members list List organization members
295
- ```
296
-
297
- Options: `users list` supports `--limit`, `--offset`. `api-keys list` supports `--limit`, `--offset`. `members list` supports `--limit`, `--offset`, `--search`, `--sort`.
298
-
299
- ### Run Configuration
300
-
301
- ```
302
- senso run-config models Get configured AI models
303
- senso run-config set-models Set AI models (--data)
304
- senso run-config schedule Get run schedule (days of week)
305
- senso run-config set-schedule Set run schedule (--data)
306
- ```
68
+ | Code | Meaning | What to do |
69
+ | ---- | ------------------------------ | -------------------- |
70
+ | 0 | Success | |
71
+ | 1 | The API or the runtime refused | Read the message |
72
+ | 2 | Usage error | Fix the command line |
73
+ | 3 | Authentication | Fix the credential |
74
+ | 4 | Not found | Check the ID |
75
+ | 5 | Network failure or timeout | Retry |
307
76
 
308
- ### CLI Management
77
+ **Errors are structured too.** Under `--output json`, a failure writes JSON to
78
+ stderr and leaves stdout empty:
309
79
 
80
+ ```json
81
+ {
82
+ "error": {
83
+ "code": "unauthorized",
84
+ "message": "Authentication failed: the API key was rejected.",
85
+ "status": 401,
86
+ "hint": "Run `senso login` to store a new key, or check SENSO_API_KEY."
87
+ }
88
+ }
310
89
  ```
311
- senso update Update to the latest version
312
- senso --version Show current version
313
- senso --help Show help
314
- ```
315
-
316
- ---
317
-
318
- ## Global Options
319
90
 
320
- | Flag | Description |
321
- |------|-------------|
322
- | `--api-key <key>` | Override API key (or set `SENSO_API_KEY` env var) |
323
- | `--base-url <url>` | Override API base URL (default: `https://apiv2.senso.ai/api/v1`) |
324
- | `--output <format>` | Output format: `json`, `table`, or `plain` (default: `plain`) |
325
- | `--quiet` | Suppress banners and non-essential output |
326
- | `--no-update-check` | Skip version check (or set `SENSO_NO_UPDATE_CHECK=1`) |
327
- | `-v, --version` | Show version |
328
- | `-h, --help` | Show help |
91
+ `error.code` is stable. Messages may be reworded; codes are part of the
92
+ interface.
329
93
 
330
- ---
94
+ **Environment.**
331
95
 
332
- ## Output Formats
96
+ | Variable | Purpose |
97
+ | ------------------------- | ------------------------------------------------------------ |
98
+ | `SENSO_API_KEY` | The API key, if you would rather not run `senso login` |
99
+ | `SENSO_BASE_URL` | Point at a different API |
100
+ | `SENSO_CONFIG_DIR` | Relocate the config file (per-project credentials, or tests) |
101
+ | `SENSO_DEBUG=1` | Log every request and its status to stderr, key redacted |
102
+ | `SENSO_NO_UPDATE_CHECK=1` | Never check npm for a newer version |
103
+ | `NO_COLOR` | Disable color (any value) |
333
104
 
334
- **Plain** (default human-friendly):
105
+ **Teaching an agent to use Senso.** The official agent skills install in one
106
+ command:
335
107
 
108
+ ```bash
109
+ senso skills install --all
336
110
  ```
337
- $ senso search "mortgage rates"
338
111
 
339
- Answer: Based on our knowledge base, current mortgage rates are...
112
+ ## Commands
340
113
 
341
- Found 3 results:
114
+ **[Full command reference →](docs/reference/commands.md)** — every command,
115
+ argument and flag, generated from the CLI itself.
116
+
117
+ | Group | What it does |
118
+ | ------------------------------------------------------------------------ | ------------------------------------------------------------------- |
119
+ | `login` `logout` `whoami` | Authentication and the current organization |
120
+ | `search` | Ask the knowledge base, with an AI answer, raw chunks, or streaming |
121
+ | `kb` `ingest` `content` `website-import` | The knowledge base: upload, browse, organize, verify |
122
+ | `ctas` | Call-to-action cards on published pages |
123
+ | `generate` `engine` `generated-content` `destinations` `publish-records` | Generate content and publish it |
124
+ | `analytics` | GEO metrics for your own organization |
125
+ | `industries` | Competitive intelligence (needs a partner key) |
126
+ | `prompts` `questions` `competitors` `tracked-sources` | What gets monitored |
127
+ | `brand-kit` `content-types` `product-lines` `tags` | How content is shaped |
128
+ | `org` `users` `members` `roles` `permissions` `api-keys` `credits` | Organization administration |
129
+ | `run-config` | Which models run, and on which days |
130
+ | `skills` | Install the Senso agent skills |
131
+ | `update` | Update this CLI |
132
+
133
+ A few worth knowing about:
342
134
 
343
- 1. Mortgage Rate Overview
344
- Content: Fixed-rate mortgages currently average...
345
- ID: cnt_abc123
135
+ ```bash
136
+ senso search "..." --output json # answer + sources, for a program
137
+ senso search stream "..." # tokens as they arrive
138
+ senso ingest upload ./policy.pdf # into the knowledge base
139
+ senso analytics summary # headline GEO metrics
140
+ senso analytics glossary # what every metric means
346
141
  ```
347
142
 
348
- **JSON** (for AI agents and scripting):
143
+ ### Reading the analytics numbers
349
144
 
350
- ```
351
- $ senso search "mortgage rates" --output json
352
- {
353
- "answer": "Based on our knowledge base...",
354
- "results": [
355
- {
356
- "title": "Mortgage Rate Overview",
357
- "chunk_text": "Fixed-rate mortgages currently average...",
358
- "content_id": "cnt_abc123"
359
- }
360
- ]
361
- }
362
- ```
363
-
364
- **Table** (for listing resources):
145
+ `senso analytics` reports how often AI models name your brand and which pages
146
+ they cite. The metrics are easy to misread, so
147
+ **[docs/analytics.md](docs/analytics.md)** states what each one divides by. The
148
+ short version:
365
149
 
366
- ```
367
- $ senso content list --output table
150
+ - **Share of Voice** divides by mentions of _every_ brand the models named, not
151
+ just your tracked competitors.
152
+ - **Citation Rate** and **Coverage** divide by answers that carried at least one
153
+ citation. **Citation Share** divides by total citation instances. They are
154
+ different metrics on different denominators.
155
+ - A metric shown as `—` means _not measured_ — its denominator was zero. That is
156
+ not the same as 0%.
157
+ - `analytics answers` is a snapshot, not a window: narrowing the dates hides
158
+ results rather than returning older ones.
368
159
 
369
- ID Title Status
370
- cnt_abc123 Mortgage Rate Overview published
371
- cnt_def456 Variable Rate Products draft
372
- ```
160
+ `senso analytics glossary` is the canonical definition of each metric, its
161
+ denominator and its gotcha, straight from the API.
373
162
 
374
- ---
163
+ ## Configuration
375
164
 
376
- ## Auto-Update
165
+ `senso login` stores your key in a JSON file with owner-only permissions:
377
166
 
378
- The CLI checks for new versions once every 24 hours (via the npm registry) and shows a notice on stderr if an update is available:
167
+ | Platform | Location |
168
+ | -------- | ----------------------------------------- |
169
+ | Linux | `~/.config/senso/config.json` |
170
+ | macOS | `~/Library/Preferences/senso/config.json` |
171
+ | Windows | `%APPDATA%\senso\Config\config.json` |
379
172
 
380
- ```
381
- ╭──────────────────────────────────────────────╮
382
- │ │
383
- │ Update available! 0.1.0 → 0.2.0 │
384
- │ │
385
- │ Run senso update to update │
386
- │ │
387
- ╰──────────────────────────────────────────────╯
388
- ```
173
+ Set `SENSO_CONFIG_DIR` to put it somewhere else. Credentials resolve in this
174
+ order: `--api-key`, then `SENSO_API_KEY`, then the config file. `senso login`
175
+ needs a terminal; in CI or an agent, set the environment variable.
389
176
 
390
- Update notices are written to `stderr` so they won't interfere with `--output json` piped to other programs.
391
-
392
- To disable: `--no-update-check` or `export SENSO_NO_UPDATE_CHECK=1`.
393
-
394
- ---
177
+ The CLI checks npm for a newer version once a day and prints a notice on stderr.
178
+ `senso update` upgrades it; `SENSO_NO_UPDATE_CHECK=1` turns the check off.
395
179
 
396
180
  ## Development
397
181
 
398
182
  ```bash
399
- # Clone the repo
400
- git clone https://github.com/AI-Template-SDK/senso-user-cli.git
401
- cd senso-user-cli
402
-
403
- # Install dependencies
404
- npm install
405
-
406
- # Run in dev mode (TypeScript directly via tsx)
407
- npm run dev -- search "test query"
408
-
409
- # Build (single ESM bundle via tsup)
410
- npm run build
411
-
412
- # Run the built version
413
- node dist/cli.js --help
414
-
415
- # Run tests
416
- npm test
417
- ```
418
-
419
- ### Project Structure
420
-
421
- ```
422
- src/
423
- ├── cli.ts # Entry point — arg parsing, command dispatch
424
- ├── commands/ # One file per command group
425
- │ ├── auth.ts # login, logout, whoami
426
- │ ├── search.ts # search, search context, search content
427
- │ ├── content.ts # CRUD + versions + verification + owners
428
- │ ├── generated-content.ts # GEO generated content (list, get)
429
- │ ├── generate.ts # content generation settings + triggers
430
- │ ├── engine.ts # publish, draft
431
- │ ├── competitors.ts # tracked competitors CRUD + suggest
432
- │ ├── tracked-sources.ts # citation-classification rules CRUD
433
- │ ├── ingest.ts # upload, reprocess (with S3 upload)
434
- │ ├── brand-kit.ts # get, set
435
- │ ├── content-types.ts # CRUD
436
- │ ├── prompts.ts # CRUD
437
- │ ├── analytics.ts # org GEO analytics (summary, citations, prompts, …)
438
- │ ├── industries.ts # partner-scoped competitive intelligence
439
- │ ├── org.ts # get, update
440
- │ ├── users.ts # CRUD + set-current
441
- │ ├── api-keys.ts # CRUD + revoke
442
- │ ├── members.ts # list
443
- │ ├── run-config.ts # models, schedule
444
- │ └── update.ts # self-update
445
- ├── lib/
446
- │ ├── api-client.ts # HTTP wrapper (native fetch, X-API-Key auth)
447
- │ ├── config.ts # Config read/write (~/.config/senso/)
448
- │ ├── output.ts # json/table/plain formatting
449
- │ └── version.ts # Reads version from package.json
450
- └── utils/
451
- ├── logger.ts # Colored log helpers (picocolors)
452
- ├── branding.ts # ASCII logo, gradient banner, boxed panels
453
- └── updater.ts # npm registry version check
183
+ nvm use # Node 22, per .nvmrc
184
+ npm ci
185
+ make all # everything CI runs
454
186
  ```
455
187
 
456
- ### Tech Stack
188
+ `make all` is the whole pipeline, and CI invokes the same targets in the same
189
+ order — a green `make all` and a green pipeline cannot mean different things.
457
190
 
458
- | Concern | Choice |
459
- |---------|--------|
460
- | Language | TypeScript |
461
- | Runtime | Node.js 18+ |
462
- | Bundler | tsup (esbuild) single 50KB ESM bundle |
463
- | CLI Framework | Commander.js |
464
- | Interactive Prompts | @clack/prompts |
465
- | Colors | picocolors |
466
- | ASCII Branding | figlet + gradient-string + boxen |
467
- | Config | env-paths (XDG-compatible) |
468
- | HTTP | Native `fetch` |
191
+ | Stage | Command | What it checks |
192
+ | ---------- | ---------------- | --------------------------------------------------------- |
193
+ | Lint | `make lint` | ESLint (type-aware) and Prettier |
194
+ | Typecheck | `make typecheck` | `tsc --noEmit` over src, tests and scripts |
195
+ | Security | `make security` | `npm audit`, semgrep, and gitleaks over the full history |
196
+ | Unit | `make unit` | Unit, command and policy tests, behind a coverage gate |
197
+ | End-to-end | `make e2e` | The built bundle, as a subprocess, against a mock API |
198
+ | Build | `make build` | The tsup bundle |
199
+ | Smoke | `make smoke` | Packs the tarball, installs it clean, and runs the binary |
469
200
 
470
- ### Releasing a New Version
201
+ **No test touches the network.** `tests/setup.ts` installs an MSW server with
202
+ `onUnhandledRequest: 'error'`, so a request nobody mocked fails the test rather
203
+ than quietly reaching a real Senso API with whatever key is in your environment.
204
+ That is enforced mechanically, which is why CI needs no secrets and why pull
205
+ requests from forks run the full suite.
471
206
 
472
- This project uses [semantic versioning](https://semver.org/). Publishing to npm is automated: pushing a `v*` tag runs `.github/workflows/publish.yml`, which builds the CLI and publishes it via [npm Trusted Publishing](https://docs.npmjs.com/trusted-publishers) (GitHub Actions OIDC, no token secrets). Every pull request and push to `main` runs `.github/workflows/ci.yml` (typecheck, build, tests, smoke test).
207
+ Run `make help` for every target.
473
208
 
474
- ```bash
475
- # 1. Bump the version in package.json, commit, and create a git tag
476
- npm version patch # 0.1.0 → 0.1.1 (bug fixes)
477
- npm version minor # 0.1.0 → 0.2.0 (new features, backwards-compatible)
478
- npm version major # 0.1.0 → 1.0.0 (breaking changes)
209
+ ## Repository layout
479
210
 
480
- # 2. Push the commit and tag — the tag triggers the publish workflow
481
- git push --follow-tags
482
211
  ```
483
-
484
- The publish workflow refuses to run if the tag does not match the version in `package.json`. Once it finishes, users running the CLI will see the update notice within 24 hours (or immediately via `senso update`).
485
-
486
- ---
212
+ src/
213
+ cli.ts The bin entry the only file that ends the process
214
+ program.ts createProgram(): builds the command tree, no side effects
215
+ commands/ One file per command group
216
+ lib/ api-client, config, errors, output, run-action
217
+ utils/ logger, branding, updater
218
+ tests/
219
+ unit/ Library modules, in isolation
220
+ commands/ Each command group, in-process, against a mock API
221
+ e2e/ The built bundle, as a subprocess
222
+ policy/ This repository's own rules
223
+ docs/ How it works, and the generated command reference
224
+ scripts/ Reference generator, smoke test, pre-commit checks
225
+ ```
226
+
227
+ ## Documentation
228
+
229
+ | Document | Read it when you want to know |
230
+ | -------------------------------------------------------- | -------------------------------------------------------------- |
231
+ | [docs/README.md](docs/README.md) | How this CLI works, in 60 seconds and then in depth |
232
+ | [docs/reference/commands.md](docs/reference/commands.md) | Every command and flag |
233
+ | [docs/configuration.md](docs/configuration.md) | Every environment variable and file it touches |
234
+ | [docs/architecture.md](docs/architecture.md) | What happens between argv and the API |
235
+ | [docs/analytics.md](docs/analytics.md) | What every GEO metric divides by, and how to not misread one |
236
+ | [CONTRIBUTING.md](CONTRIBUTING.md) | How to make a change, and what a review will send back |
237
+ | [SECURITY.md](SECURITY.md) | What this tool holds, and what it does not do |
238
+ | [CHANGELOG.md](CHANGELOG.md) | What changed and when |
239
+ | [CLAUDE.md](CLAUDE.md) | Conventions, and the behaviors that look like bugs but are not |
487
240
 
488
241
  ## License
489
242
 
490
- [AGPL-3.0](LICENSE)
243
+ [AGPL-3.0-or-later](LICENSE)