@productbrain/cli 0.1.0-beta.29 → 0.1.0-beta.32

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 (106) hide show
  1. package/README.md +62 -178
  2. package/dist/__tests__/constants.test.d.ts +2 -0
  3. package/dist/__tests__/constants.test.d.ts.map +1 -0
  4. package/dist/__tests__/constants.test.js +94 -0
  5. package/dist/__tests__/constants.test.js.map +1 -0
  6. package/dist/__tests__/errors.test.d.ts +2 -0
  7. package/dist/__tests__/errors.test.d.ts.map +1 -0
  8. package/dist/__tests__/errors.test.js +117 -0
  9. package/dist/__tests__/errors.test.js.map +1 -0
  10. package/dist/__tests__/glossary.test.d.ts +2 -0
  11. package/dist/__tests__/glossary.test.d.ts.map +1 -0
  12. package/dist/__tests__/glossary.test.js +32 -0
  13. package/dist/__tests__/glossary.test.js.map +1 -0
  14. package/dist/__tests__/login.test.d.ts +2 -0
  15. package/dist/__tests__/login.test.d.ts.map +1 -0
  16. package/dist/__tests__/login.test.js +168 -0
  17. package/dist/__tests__/login.test.js.map +1 -0
  18. package/dist/__tests__/profiles.test.d.ts +2 -0
  19. package/dist/__tests__/profiles.test.d.ts.map +1 -0
  20. package/dist/__tests__/profiles.test.js +168 -0
  21. package/dist/__tests__/profiles.test.js.map +1 -0
  22. package/dist/__tests__/setup.test.d.ts +2 -0
  23. package/dist/__tests__/setup.test.d.ts.map +1 -0
  24. package/dist/__tests__/setup.test.js +170 -0
  25. package/dist/__tests__/setup.test.js.map +1 -0
  26. package/dist/commands/capture.d.ts.map +1 -1
  27. package/dist/commands/capture.js +23 -2
  28. package/dist/commands/capture.js.map +1 -1
  29. package/dist/commands/doctor.d.ts +18 -0
  30. package/dist/commands/doctor.d.ts.map +1 -0
  31. package/dist/commands/doctor.js +211 -0
  32. package/dist/commands/doctor.js.map +1 -0
  33. package/dist/commands/doctor.test.d.ts +7 -0
  34. package/dist/commands/doctor.test.d.ts.map +1 -0
  35. package/dist/commands/doctor.test.js +265 -0
  36. package/dist/commands/doctor.test.js.map +1 -0
  37. package/dist/commands/login.d.ts +4 -0
  38. package/dist/commands/login.d.ts.map +1 -1
  39. package/dist/commands/login.js +53 -27
  40. package/dist/commands/login.js.map +1 -1
  41. package/dist/commands/profile.d.ts +24 -0
  42. package/dist/commands/profile.d.ts.map +1 -0
  43. package/dist/commands/profile.js +82 -0
  44. package/dist/commands/profile.js.map +1 -0
  45. package/dist/commands/promote.d.ts.map +1 -1
  46. package/dist/commands/promote.js +3 -2
  47. package/dist/commands/promote.js.map +1 -1
  48. package/dist/commands/setup.d.ts +16 -0
  49. package/dist/commands/setup.d.ts.map +1 -0
  50. package/dist/commands/setup.js +213 -0
  51. package/dist/commands/setup.js.map +1 -0
  52. package/dist/formatters/promote.d.ts +1 -0
  53. package/dist/formatters/promote.d.ts.map +1 -1
  54. package/dist/formatters/promote.js +1 -0
  55. package/dist/formatters/promote.js.map +1 -1
  56. package/dist/index.js +251 -284
  57. package/dist/index.js.map +1 -1
  58. package/dist/lib/activation.d.ts +28 -0
  59. package/dist/lib/activation.d.ts.map +1 -0
  60. package/dist/lib/activation.js +57 -0
  61. package/dist/lib/activation.js.map +1 -0
  62. package/dist/lib/activation.test.d.ts +6 -0
  63. package/dist/lib/activation.test.d.ts.map +1 -0
  64. package/dist/lib/activation.test.js +121 -0
  65. package/dist/lib/activation.test.js.map +1 -0
  66. package/dist/lib/client.d.ts +19 -2
  67. package/dist/lib/client.d.ts.map +1 -1
  68. package/dist/lib/client.js +71 -11
  69. package/dist/lib/client.js.map +1 -1
  70. package/dist/lib/config.d.ts +9 -3
  71. package/dist/lib/config.d.ts.map +1 -1
  72. package/dist/lib/config.js +54 -15
  73. package/dist/lib/config.js.map +1 -1
  74. package/dist/lib/constants.d.ts +21 -0
  75. package/dist/lib/constants.d.ts.map +1 -0
  76. package/dist/lib/constants.js +39 -0
  77. package/dist/lib/constants.js.map +1 -0
  78. package/dist/lib/errors.d.ts +57 -0
  79. package/dist/lib/errors.d.ts.map +1 -0
  80. package/dist/lib/errors.js +65 -0
  81. package/dist/lib/errors.js.map +1 -0
  82. package/dist/lib/glossary.d.ts +19 -0
  83. package/dist/lib/glossary.d.ts.map +1 -0
  84. package/dist/lib/glossary.js +53 -0
  85. package/dist/lib/glossary.js.map +1 -0
  86. package/dist/lib/profiles.d.ts +34 -0
  87. package/dist/lib/profiles.d.ts.map +1 -0
  88. package/dist/lib/profiles.js +173 -0
  89. package/dist/lib/profiles.js.map +1 -0
  90. package/dist/lib/runner.d.ts +2 -0
  91. package/dist/lib/runner.d.ts.map +1 -1
  92. package/dist/lib/runner.js +33 -4
  93. package/dist/lib/runner.js.map +1 -1
  94. package/dist/lib/style.d.ts +65 -0
  95. package/dist/lib/style.d.ts.map +1 -0
  96. package/dist/lib/style.js +108 -0
  97. package/dist/lib/style.js.map +1 -0
  98. package/dist/lib/style.test.d.ts +7 -0
  99. package/dist/lib/style.test.d.ts.map +1 -0
  100. package/dist/lib/style.test.js +195 -0
  101. package/dist/lib/style.test.js.map +1 -0
  102. package/dist/lib/telemetry.d.ts +15 -0
  103. package/dist/lib/telemetry.d.ts.map +1 -0
  104. package/dist/lib/telemetry.js +29 -0
  105. package/dist/lib/telemetry.js.map +1 -0
  106. package/package.json +1 -1
package/README.md CHANGED
@@ -1,230 +1,114 @@
1
1
  # Product Brain CLI (`pb`)
2
2
 
3
- CLI for Chain knowledge: read, search, gather context, and **write back**. Uses your Product Brain API key (same as MCP). Write commands require a readwrite key.
3
+ Chain knowledge + write-back in the terminal. Read, search, capture, and update your product knowledge graph.
4
4
 
5
- **Requirements:** Node 18+, API key from Product Brain Settings → API Keys (`pb_sk_*`).
6
-
7
- ---
5
+ **Requirements:** Node 18+, API key from Product Brain Settings (`pb_sk_*`).
8
6
 
9
7
  ## Install
10
8
 
11
- ### From npm (beta)
12
-
13
9
  ```bash
14
10
  npm install -g @productbrain/cli@beta
15
11
  ```
16
12
 
17
- **IDE MCP (Cursor, Claude Desktop, VS Code):** `npx @productbrain/mcp@beta setup` — [Install — MCP and CLI](../../docs/surfaces/INSTALL.md).
18
-
19
- Updates require a new publish to npm (and, if you use 2FA, a one-time password). Use this when you want a stable beta from the registry.
20
-
21
- ### From main (no publish needed)
22
-
23
- If you have the repo and want **latest from `main`** without publishing to npm:
13
+ From source (latest main):
24
14
 
25
15
  ```bash
26
16
  git clone https://github.com/synergyai-os/Product-OS.git
27
- cd Product-OS
28
- npm ci
17
+ cd Product-OS && npm ci
29
18
  cd packages/cli && npm run build && npm install -g .
30
19
  ```
31
20
 
32
- After that, `pb` runs from your global path. Pull and re-run the last two commands to update:
21
+ ## Login
33
22
 
34
23
  ```bash
35
- cd Product-OS && git pull && cd packages/cli && npm run build && npm install -g .
24
+ pb login
36
25
  ```
37
26
 
38
- See [Deployment strategy CLI vs MCP](../../docs/deployment-strategy.md#cli-vs-mcp-distribution) for why this avoids `cli:publish` / OTP.
39
-
40
- ---
41
-
42
- ## First run
27
+ Paste your API key (`pb_sk_...`). Saved to `~/.config/productbrain/.env`.
43
28
 
44
- - **No key yet:** Run any command (e.g. `pb orient -b`). You’ll get a short guided flow: “Do you have an API key? (y/n)” → paste key → saved to `~/.config/productbrain/.env`. Your command then runs.
45
- - **Or:** Run `pb login` to paste and save your key once. Get a key: Product Brain app → Settings → API Keys.
29
+ Or run any command without a key and follow the guided flow.
46
30
 
47
- Config is read from (in order): env vars → `~/.config/productbrain/.env` → `.env.mcp` in the current directory.
31
+ ## Quick Start
48
32
 
49
- ## Terminal-native contract
50
-
51
- Use `pb` with two explicit modes:
52
-
53
- - **Read mode** for `orient`, `get`, `search`, `context`, and repo grounding
54
- - **Write mode** for `capture`, `relate`, `update`, and other Chain writes
33
+ ```bash
34
+ # 1. Check your setup
35
+ pb doctor
55
36
 
56
- The Chain is the live source of truth. Generated files such as `AGENTS.md`, `.productbrain/context.md`, `.productbrain/briefing.md`, and `.codex/skills/*` are local projections for grounding, not the authoring source.
37
+ # 2. Orient see where things stand
38
+ pb orient -b
57
39
 
58
- In non-TTY usage, `pb` defaults to machine-readable JSON. Use `--json` to force JSON and `--pretty` to force human formatting.
40
+ # 3. Read an entry
41
+ pb get BET-123
59
42
 
60
- Recommended default loop:
43
+ # 4. Search the Chain
44
+ pb search "onboarding flow"
61
45
 
62
- ```bash
63
- pb orient -b
64
- pb get <ID>
65
- # read projected repo context as needed
66
- pb session start # only when entering write-heavy work
67
- pb capture "INS: ..."
46
+ # 5. Start a write session and capture knowledge
47
+ pb session start
48
+ pb capture "DEC: chose X because Y"
49
+ pb capture "TEN: blocked by missing API"
68
50
  pb session close
69
51
  ```
70
52
 
71
- Projected files such as `AGENTS.md`, `.productbrain/context.md`, and `.productbrain/briefing.md` are startup aids and mirrors. Use live `pb` reads when you need current truth from the Chain.
72
-
73
- ### Recommended terminal-native startup loop
74
-
75
- 1. `pb orient -b`
76
- 2. `pb get <ID>` before bet-, decision-, or standard-scoped implementation
77
- 3. read projected repo context (`AGENTS.md`, `.productbrain/context.md`, `.productbrain/briefing.md`)
78
- 4. stay in read mode while exploring
79
- 5. `pb session start` only when you are entering write-heavy work
80
- 6. `pb capture ...`
81
- 7. `pb session close`
82
-
83
- ---
53
+ Running `pb` with no arguments shows contextual guidance based on your setup state.
84
54
 
85
55
  ## Commands
86
56
 
87
57
  ### Read (no session required)
88
58
 
89
59
  | Command | Description |
90
- |--------|-------------|
91
- | `pb orient` | Workspace stage, bets, governance, tensions. Use `-b` for a brief view. |
92
- | `pb search <query>` | Full-text search across the Chain. |
93
- | `pb get <entry-id>` | Full entry (data, relations, last 10 history events). |
94
- | `pb context <entry-id>` | Constellation context (pipe-friendly markdown when not a TTY). |
95
- | `pb handshake` | Generate context files for AI developer tools. See below. |
96
- | `pb login` | Save your API key to `~/.config/productbrain/.env`. |
97
-
98
- Read mode is the default for grounding, repo tracing, and spec lookup. Do not start a write session until you actually intend to write back to the Chain.
99
-
100
- **Internal (Product-OS repo only):** `pb brand-pack` forwards to `scripts/brand-pack.mjs` — structured community marketing JSON via **OpenRouter** (`OPENROUTER_API_KEY`, not the Product Brain API key). Use `npm run brand-pack -- --help` from repo root for script options. Not published behavior for global npm installs unless built from this monorepo.
101
-
102
- ### Write (session required — DEC-9)
60
+ |---------|-------------|
61
+ | `pb orient [-b]` | Workspace overview. `-b` for brief, `--task` for task-grounded context. |
62
+ | `pb get <id>` | Full entry: data, relations, history. |
63
+ | `pb search <query>` | Full-text search across all collections. |
64
+ | `pb context <id>` | Constellation context for an entry. |
65
+ | `pb fields <collection>` | Field definitions for a collection. |
66
+ | `pb constellation <id>` | Entry + all related entries grouped by relation type. |
67
+ | `pb collections list` | All collections with field counts. |
68
+ | `pb glossary` | Key Product Brain CLI terms. |
69
+ | `pb doctor` | Check configuration and connectivity. |
70
+
71
+ ### Write (session required)
103
72
 
104
73
  | Command | Description |
105
- |--------|-------------|
106
- | `pb session start` | Open a tracked write session. Refreshes context files. |
107
- | `pb capture "<text>"` | Capture a decision, tension, insight, or any knowledge to the Chain. Auto-classifies. |
108
- | `pb session close` | Close the session. Shows captured entries, runs wrapup, refreshes context. |
109
-
110
- `pb --help` for options.
111
-
112
- ### Mixed-surface switching
113
-
114
- If you use Codex, Cursor, or Claude on the same stretch of work:
115
-
116
- 1. let one surface own writes at a time
117
- 2. let other surfaces read freely
118
- 3. close the current write stretch before a different surface becomes the writer
119
-
120
- Do not treat force-close or overlapping write sessions as normal workflow. The goal is clear attribution, not concurrent write ownership.
121
-
122
- ### Delivery + Chain (when you ship code in Product-OS)
123
-
124
- Progressive delivery and deploy ordering are documented in the monorepo — not inside the `pb` binary:
74
+ |---------|-------------|
75
+ | `pb session start` | Open a tracked write session. |
76
+ | `pb capture "<text>"` | Capture knowledge. Auto-classifies to the right collection. |
77
+ | `pb update <id>` | Update fields on an existing entry. |
78
+ | `pb relate <from> <type> <to>` | Add a typed relation between entries. |
79
+ | `pb promote <id>` | Promote entry from draft to active. |
80
+ | `pb session close` | Close session, show summary. |
125
81
 
126
- | SSOT | Purpose |
127
- |------|---------|
128
- | [`docs/system/design/progressive-delivery-thesis.md`](../../docs/system/design/progressive-delivery-thesis.md) | Ladder (dark launch → GA), rollback (**STA-10**), flags, edge/Cloudflare notes |
129
- | **MAP-13** on the Chain | Map entry pointing at the thesis — `pb get MAP-13` / `pb context MAP-13` |
130
- | [`docs/deployment-strategy.md`](../../docs/deployment-strategy.md) | CI target, Convex vs Railway, CLI vs MCP distribution |
82
+ ### Profile Management
131
83
 
132
- **BR-157:** If your push includes changes under `convex/`, run **`npx convex deploy`** (correct deployment) **before** `git push origin main` so Railway never serves a frontend that calls missing Convex functions.
133
-
134
- **Typical ship loop with `pb`:**
84
+ Config is read from (in order): environment variables, `~/.config/productbrain/.env`, `.env.mcp` in the current directory.
135
85
 
136
86
  ```bash
137
- pb session start
138
- # commit, merge, push per .productbrain/skills/commit-to-main …
139
- pb capture "DEC: Shipped <short>. Rollback: <flag OFF | Railway revert | …>. Convex deploy before push: <yes | N/A>."
140
- pb session close
141
- # If you changed .productbrain rules or skills:
142
- cd ../.. && pb handshake # from repo root
87
+ pb login # Save API key
88
+ pb doctor # Verify configuration
89
+ pb setup # Guided first-time setup
143
90
  ```
144
91
 
145
- ---
146
-
147
- ## `pb handshake` — AI Developer Onboarding
148
-
149
- One command generates context files for every major AI coding tool:
150
-
151
- ```bash
152
- pb handshake
153
- ```
154
-
155
- **What it generates:**
156
-
157
- | File | Purpose |
158
- |------|---------|
159
- | `AGENTS.md` | Adapter for Codex / terminal-native agents |
160
- | `.codex/skills/*.md` | Product Brain skill projections for Codex |
161
- | `.productbrain/context.md` | Workspace briefing — stage, bets, governance, architecture, tensions |
162
- | `.productbrain/briefing.md` | Chain entries matched to this specific repo |
163
- | `CLAUDE.md` | Adapter for Claude Code |
164
- | `.cursor/rules/chain.mdc` | Adapter for Cursor |
165
- | `.github/copilot-instructions.md` | Adapter for GitHub Copilot |
166
-
167
- **Options:**
168
-
169
- - `--force` — Overwrite adapter files even if they have custom content
170
- - `--dry-run` — Preview what would be generated without writing files
171
-
172
- **Safety:** Adapter files are marked with `auto-generated by pb handshake`. If you customize one and remove the marker, future runs skip it (unless `--force`). The `.productbrain/` directory contains workspace-specific context and should be in `.gitignore`.
173
-
174
- **Governance:** Context export surface per DEC-161 and GLO-63. Architecture: ARCH-63 (surfaces are peers consuming the kernel).
175
-
176
- Projected files are adapters and mirrors, not the authoring source of truth. Use live `pb` reads when you need current Chain truth or are verifying governed product behavior.
177
-
178
- ---
179
-
180
- ## `pb session` + `pb capture` — Close the Loop
181
-
182
- The Chain compounds from every AI coding session. Instead of knowledge dying in chat transcripts (TEN-27), capture it as you work.
183
-
184
- ### Flow
92
+ ### AI Developer Onboarding
185
93
 
186
94
  ```bash
187
- # Start of session
188
- pb session start # opens session, refreshes context
189
-
190
- # During work — capture what you learn
191
- pb capture "TEN: topology query returns stale containers when initiatives are archived"
192
- pb capture "DEC: excluded archived initiatives at the query level, not the UI filter"
193
- pb capture "INS: learned that the containerIds array references soft-deleted entries"
194
-
195
- # End of session
196
- pb session close # shows summary, runs wrapup, refreshes context
95
+ pb handshake # Generate context files for Codex, Cursor, Claude, Copilot
197
96
  ```
198
97
 
199
- ### How capture works
200
-
201
- 1. Text is sent to the Chain's classification pipeline — auto-assigns collection
202
- 2. Entry is created as a **draft** (nothing auto-commits)
203
- 3. Receipt shows: entry ID, collection, confidence, reasoning, alternatives
204
- 4. You review and commit drafts in Product Brain Studio
205
-
206
- ### Options
207
-
208
- - `pb capture --collection tensions "text"` — skip auto-classification, use explicit collection
209
- - `pb capture --name "Short title" --description "Full explanation"` — separate name and description
210
- - `pb session close --force` — clear local session state even if server close fails
211
-
212
- ### Governance
213
-
214
- - Write access requires a tracked session (DEC-9, BR-8)
215
- - Session attribution via API key; `clientKind: 'cli'` discriminator per DEC-184 (pending schema extension)
216
- - All captures are drafts — human reviews and commits (TEN-374)
217
- - Solves TEN-264 (CLI write access session attribution) with explicit session token in `.productbrain/session.json`
218
- - Addresses TEN-27 (strategic conversations generate decisions that never reach the Chain)
219
- - Implements INS-80 (proactive capture during coding)
98
+ Generates `AGENTS.md`, `CLAUDE.md`, `.cursor/rules/chain.mdc`, `.productbrain/context.md`, and more.
220
99
 
221
- ## JSON output contract
100
+ ## Output Modes
222
101
 
223
- `pb` treats output mode as part of the terminal-native surface contract:
102
+ | Context | Output |
103
+ |---------|--------|
104
+ | TTY (terminal) | Human-readable formatting |
105
+ | Piped / non-TTY | JSON |
106
+ | `--json` | Force JSON |
107
+ | `--pretty` | Force human-readable |
224
108
 
225
- - non-TTY output defaults to JSON
226
- - TTY output defaults to human-readable formatting
227
- - `--json` forces machine-readable JSON
228
- - `--pretty` forces human-readable output even when piped
109
+ ## Reference
229
110
 
230
- For automation, prefer calling `pb` directly instead of wrappers that add extra stdout noise. Changes to output shape should be treated as compatibility-sensitive for machine consumers.
111
+ - `pb --help` for full command list
112
+ - `pb <command> --help` for command-specific options
113
+ - `pb glossary` for key terms
114
+ - Chain: WP-302 (CLI Maturity)
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=constants.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/constants.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * constants — unit tests.
3
+ * WP-301 Slice 0: Verify DEFAULT_SITE_URL, resolveSiteUrl(), resolveAppUrl(), isDevMode().
4
+ */
5
+ import { describe, expect, it, beforeEach, afterEach } from 'vitest';
6
+ import { DEFAULT_SITE_URL, resolveSiteUrl, resolveAppUrl, isDevMode } from '../lib/constants.js';
7
+ describe('DEFAULT_SITE_URL', () => {
8
+ it('is the production Convex site URL', () => {
9
+ expect(DEFAULT_SITE_URL).toBe('https://gateway.productbrain.io');
10
+ });
11
+ });
12
+ describe('resolveSiteUrl', () => {
13
+ let originalPbEnv;
14
+ beforeEach(() => {
15
+ originalPbEnv = process.env.PB_ENV;
16
+ });
17
+ afterEach(() => {
18
+ if (originalPbEnv === undefined) {
19
+ delete process.env.PB_ENV;
20
+ }
21
+ else {
22
+ process.env.PB_ENV = originalPbEnv;
23
+ }
24
+ });
25
+ it('returns localhost URL when PB_ENV=dev', () => {
26
+ process.env.PB_ENV = 'dev';
27
+ expect(resolveSiteUrl()).toBe('http://localhost:5176');
28
+ });
29
+ it('returns production URL when PB_ENV=production', () => {
30
+ process.env.PB_ENV = 'production';
31
+ expect(resolveSiteUrl()).toBe(DEFAULT_SITE_URL);
32
+ });
33
+ it('returns production URL when PB_ENV is unset', () => {
34
+ delete process.env.PB_ENV;
35
+ expect(resolveSiteUrl()).toBe(DEFAULT_SITE_URL);
36
+ });
37
+ it('is case-insensitive for dev', () => {
38
+ process.env.PB_ENV = 'DEV';
39
+ expect(resolveSiteUrl()).toBe('http://localhost:5176');
40
+ });
41
+ });
42
+ describe('resolveAppUrl', () => {
43
+ let originalPbEnv;
44
+ beforeEach(() => {
45
+ originalPbEnv = process.env.PB_ENV;
46
+ });
47
+ afterEach(() => {
48
+ if (originalPbEnv === undefined) {
49
+ delete process.env.PB_ENV;
50
+ }
51
+ else {
52
+ process.env.PB_ENV = originalPbEnv;
53
+ }
54
+ });
55
+ it('returns localhost URL when PB_ENV=dev', () => {
56
+ process.env.PB_ENV = 'dev';
57
+ expect(resolveAppUrl()).toBe('http://localhost:5173');
58
+ });
59
+ it('returns production URL when PB_ENV=production', () => {
60
+ process.env.PB_ENV = 'production';
61
+ expect(resolveAppUrl()).toBe('https://work.productbrain.io');
62
+ });
63
+ it('returns production URL when PB_ENV is unset', () => {
64
+ delete process.env.PB_ENV;
65
+ expect(resolveAppUrl()).toBe('https://work.productbrain.io');
66
+ });
67
+ });
68
+ describe('isDevMode', () => {
69
+ let originalPbEnv;
70
+ beforeEach(() => {
71
+ originalPbEnv = process.env.PB_ENV;
72
+ });
73
+ afterEach(() => {
74
+ if (originalPbEnv === undefined) {
75
+ delete process.env.PB_ENV;
76
+ }
77
+ else {
78
+ process.env.PB_ENV = originalPbEnv;
79
+ }
80
+ });
81
+ it('returns true when PB_ENV=dev', () => {
82
+ process.env.PB_ENV = 'dev';
83
+ expect(isDevMode()).toBe(true);
84
+ });
85
+ it('returns false when PB_ENV=production', () => {
86
+ process.env.PB_ENV = 'production';
87
+ expect(isDevMode()).toBe(false);
88
+ });
89
+ it('returns false when PB_ENV is unset', () => {
90
+ delete process.env.PB_ENV;
91
+ expect(isDevMode()).toBe(false);
92
+ });
93
+ });
94
+ //# sourceMappingURL=constants.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.test.js","sourceRoot":"","sources":["../../src/__tests__/constants.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=errors.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/errors.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * CLIError + McpError — unit tests.
3
+ * WP-302 Slice 0: Error Taxonomy + Commander Migration.
4
+ *
5
+ * Verifies:
6
+ * - CLIError serializes correctly with code, category, guidance
7
+ * - CLIError works with just a message (optional fields default)
8
+ * - McpError extends CLIError and retains backward compat
9
+ * - McpError maps server codes to categories
10
+ */
11
+ import { describe, expect, it } from 'vitest';
12
+ import { CLIError, ErrorCode } from '../lib/errors.js';
13
+ import { McpError } from '../lib/client.js';
14
+ describe('CLIError', () => {
15
+ it('serializes all fields to JSON', () => {
16
+ const err = new CLIError('No API key configured', {
17
+ code: ErrorCode.AUTH_MISSING,
18
+ category: 'auth',
19
+ guidance: 'Run `pb login` to configure your API key.',
20
+ });
21
+ expect(err.message).toBe('No API key configured');
22
+ expect(err.code).toBe('AUTH_MISSING');
23
+ expect(err.category).toBe('auth');
24
+ expect(err.guidance).toBe('Run `pb login` to configure your API key.');
25
+ const json = err.toJSON();
26
+ expect(json).toEqual({
27
+ error: 'No API key configured',
28
+ code: 'AUTH_MISSING',
29
+ category: 'auth',
30
+ guidance: 'Run `pb login` to configure your API key.',
31
+ });
32
+ });
33
+ it('defaults to INTERNAL code and internal category when no options given', () => {
34
+ const err = new CLIError('Something broke');
35
+ expect(err.code).toBe('INTERNAL');
36
+ expect(err.category).toBe('internal');
37
+ expect(err.guidance).toBeUndefined();
38
+ const json = err.toJSON();
39
+ expect(json).toEqual({
40
+ error: 'Something broke',
41
+ code: 'INTERNAL',
42
+ category: 'internal',
43
+ });
44
+ // guidance should NOT appear in JSON when undefined
45
+ expect(json).not.toHaveProperty('guidance');
46
+ });
47
+ it('is an instance of Error', () => {
48
+ const err = new CLIError('test');
49
+ expect(err).toBeInstanceOf(Error);
50
+ expect(err).toBeInstanceOf(CLIError);
51
+ expect(err.name).toBe('CLIError');
52
+ });
53
+ it('supports all defined error codes', () => {
54
+ // Verify the ErrorCode object has all expected keys
55
+ expect(ErrorCode.AUTH_MISSING).toBe('AUTH_MISSING');
56
+ expect(ErrorCode.AUTH_INVALID).toBe('AUTH_INVALID');
57
+ expect(ErrorCode.AUTH_DENIED).toBe('AUTH_DENIED');
58
+ expect(ErrorCode.NETWORK_UNREACHABLE).toBe('NETWORK_UNREACHABLE');
59
+ expect(ErrorCode.NETWORK_TIMEOUT).toBe('NETWORK_TIMEOUT');
60
+ expect(ErrorCode.CONFIG_MISSING).toBe('CONFIG_MISSING');
61
+ expect(ErrorCode.CONFIG_INVALID).toBe('CONFIG_INVALID');
62
+ expect(ErrorCode.SESSION_REQUIRED).toBe('SESSION_REQUIRED');
63
+ expect(ErrorCode.VALIDATION_FAILED).toBe('VALIDATION_FAILED');
64
+ expect(ErrorCode.INTERNAL).toBe('INTERNAL');
65
+ });
66
+ });
67
+ describe('McpError', () => {
68
+ it('extends CLIError', () => {
69
+ const err = new McpError('Server error');
70
+ expect(err).toBeInstanceOf(Error);
71
+ expect(err).toBeInstanceOf(CLIError);
72
+ expect(err).toBeInstanceOf(McpError);
73
+ expect(err.name).toBe('McpError');
74
+ });
75
+ it('preserves backward-compatible interface (message, code, details)', () => {
76
+ const details = [
77
+ {
78
+ fieldKey: 'status',
79
+ fieldLabel: 'Status',
80
+ invalidValue: 'bogus',
81
+ validOptions: ['draft', 'active'],
82
+ errorType: 'invalid_option',
83
+ message: 'Invalid status value',
84
+ },
85
+ ];
86
+ const err = new McpError('Validation failed', 'VALIDATION_FAILED', details);
87
+ expect(err.message).toBe('Validation failed');
88
+ expect(err.code).toBe('VALIDATION_FAILED');
89
+ expect(err.details).toEqual(details);
90
+ expect(err.category).toBe('validation');
91
+ });
92
+ it('maps AUTH codes to auth category', () => {
93
+ const err = new McpError('Unauthorized', 'AUTH_DENIED');
94
+ expect(err.category).toBe('auth');
95
+ });
96
+ it('maps UNAUTHORIZED to auth category', () => {
97
+ const err = new McpError('Unauthorized', 'UNAUTHORIZED');
98
+ expect(err.category).toBe('auth');
99
+ });
100
+ it('maps SESSION_REQUIRED to session category', () => {
101
+ const err = new McpError('No session', 'SESSION_REQUIRED');
102
+ expect(err.category).toBe('session');
103
+ });
104
+ it('defaults to INTERNAL code and internal category when no code given', () => {
105
+ const err = new McpError('Unknown error');
106
+ expect(err.code).toBe('INTERNAL');
107
+ expect(err.category).toBe('internal');
108
+ });
109
+ it('serializes via toJSON from CLIError', () => {
110
+ const err = new McpError('Bad request', 'VALIDATION_FAILED');
111
+ const json = err.toJSON();
112
+ expect(json.error).toBe('Bad request');
113
+ expect(json.code).toBe('VALIDATION_FAILED');
114
+ expect(json.category).toBe('validation');
115
+ });
116
+ });
117
+ //# sourceMappingURL=errors.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.test.js","sourceRoot":"","sources":["../../src/__tests__/errors.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;IACxB,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,uBAAuB,EAAE;YAChD,IAAI,EAAE,SAAS,CAAC,YAAY;YAC5B,QAAQ,EAAE,MAAM;YAChB,QAAQ,EAAE,2CAA2C;SACtD,CAAC,CAAC;QAEH,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QAClD,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACtC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAClC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAC;QAEvE,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC;YACnB,KAAK,EAAE,uBAAuB;YAC9B,IAAI,EAAE,cAAc;YACpB,QAAQ,EAAE,MAAM;YAChB,QAAQ,EAAE,2CAA2C;SACtD,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uEAAuE,EAAE,GAAG,EAAE;QAC/E,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,iBAAiB,CAAC,CAAC;QAE5C,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAClC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACtC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,aAAa,EAAE,CAAC;QAErC,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC;YACnB,KAAK,EAAE,iBAAiB;YACxB,IAAI,EAAE,UAAU;YAChB,QAAQ,EAAE,UAAU;SACrB,CAAC,CAAC;QACH,oDAAoD;QACpD,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;QACjC,MAAM,CAAC,GAAG,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QAClC,MAAM,CAAC,GAAG,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QACrC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,oDAAoD;QACpD,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACpD,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACpD,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAClD,MAAM,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;QAClE,MAAM,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC1D,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QACxD,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QACxD,MAAM,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QAC5D,MAAM,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAC9D,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;IACxB,EAAE,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAC1B,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,cAAc,CAAC,CAAC;QACzC,MAAM,CAAC,GAAG,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QAClC,MAAM,CAAC,GAAG,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QACrC,MAAM,CAAC,GAAG,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QACrC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,OAAO,GAAG;YACd;gBACE,QAAQ,EAAE,QAAQ;gBAClB,UAAU,EAAE,QAAQ;gBACpB,YAAY,EAAE,OAAO;gBACrB,YAAY,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC;gBACjC,SAAS,EAAE,gBAAyB;gBACpC,OAAO,EAAE,sBAAsB;aAChC;SACF,CAAC;QACF,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,mBAAmB,EAAE,mBAAmB,EAAE,OAAO,CAAC,CAAC;QAE5E,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAC9C,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAC3C,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACrC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;QACxD,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC;QACzD,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,YAAY,EAAE,kBAAkB,CAAC,CAAC;QAC3D,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;QAC5E,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,eAAe,CAAC,CAAC;QAC1C,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAClC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,aAAa,EAAE,mBAAmB,CAAC,CAAC;QAC7D,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACvC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAC5C,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=glossary.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glossary.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/glossary.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,32 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { GLOSSARY, formatGlossary } from '../lib/glossary.js';
3
+ describe('glossary', () => {
4
+ it('has non-empty entries', () => {
5
+ expect(GLOSSARY.length).toBeGreaterThan(0);
6
+ for (const entry of GLOSSARY) {
7
+ expect(entry.term.length).toBeGreaterThan(0);
8
+ expect(entry.definition.length).toBeGreaterThan(0);
9
+ }
10
+ });
11
+ const requiredTerms = ['workspace', 'entry', 'collection', 'relation', 'session', 'capture', 'profile'];
12
+ it.each(requiredTerms)('includes required term: %s', (term) => {
13
+ const found = GLOSSARY.find((g) => g.term === term);
14
+ expect(found).toBeDefined();
15
+ expect(found.definition.length).toBeGreaterThan(0);
16
+ });
17
+ it('formatGlossary produces formatted multi-line output', () => {
18
+ const output = formatGlossary();
19
+ expect(output).toContain('workspace');
20
+ expect(output).toContain('entry');
21
+ expect(output).toContain('collection');
22
+ // Should have multiple lines (one per term)
23
+ const lines = output.split('\n');
24
+ expect(lines.length).toBe(GLOSSARY.length);
25
+ });
26
+ it('all terms are unique', () => {
27
+ const terms = GLOSSARY.map((g) => g.term);
28
+ const unique = new Set(terms);
29
+ expect(unique.size).toBe(terms.length);
30
+ });
31
+ });
32
+ //# sourceMappingURL=glossary.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glossary.test.js","sourceRoot":"","sources":["../../src/__tests__/glossary.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAE9D,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;IACxB,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAC/B,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC3C,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;YAC7B,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;YAC7C,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QACrD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,aAAa,GAAG,CAAC,WAAW,EAAE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;IAExG,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,4BAA4B,EAAE,CAAC,IAAI,EAAE,EAAE;QAC5D,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;QACpD,MAAM,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;QAC5B,MAAM,CAAC,KAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,MAAM,GAAG,cAAc,EAAE,CAAC;QAChC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QACtC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAClC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QACvC,4CAA4C;QAC5C,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACjC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=login.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/login.test.ts"],"names":[],"mappings":""}