@lahat-group/cli 1.1.8 → 2.0.1

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/dist/index.js +66 -12168
  2. package/package.json +1 -2
  3. package/skill/SKILL.md +57 -17
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lahat-group/cli",
3
- "version": "1.1.8",
3
+ "version": "2.0.1",
4
4
  "description": "CLI + Claude Skill to build, validate, price, and save wardrobe designs.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -30,7 +30,6 @@
30
30
  "test": "vitest run"
31
31
  },
32
32
  "devDependencies": {
33
- "@wardrobe/shared": "*",
34
33
  "commander": "^12.0.0",
35
34
  "ky": "^1.14.3",
36
35
  "tsup": "^8.0.0",
package/skill/SKILL.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: wardrobe-configurator
3
- description: Use when the user wants to build, configure, validate, price, or order a wardrobe/closet design from the terminal — phrases like "configure a wardrobe", "price a design", "build a closet layout", "validate my wardrobe config" — OR wants to look up existing wardrobe data: projects, customers, designers, orders and their PDFs, e.g. "which projects does this designer have", "find the customer with phone 05x", "who is on this project", "what is the status of this order", "download the order PDF". Drives the `wardrobe` CLI (HTTP client over the configurator API); a WardrobeConfig JSON file on disk is the unit of work for design verbs, and every lookup verb is read-only.
3
+ description: Use when the user wants to build, configure, validate, price, or order a wardrobe/closet design from the terminal — phrases like "configure a wardrobe", "price a design", "build a closet layout", "validate my wardrobe config" — OR wants to look up existing wardrobe data: projects, customers, designers, orders, quotations and their PDFs, or (staff only) registered users across the whole system, e.g. "which projects does this designer have", "find the customer with phone 05x", "who is on this project", "what is the status of this order", "download the order PDF", "who signed up recently", "which account owns this phone number", "what's the latest quote we generated". Drives the `wardrobe` CLI (HTTP client over the configurator API); a WardrobeConfig JSON file on disk is the unit of work for design verbs, and every lookup verb is read-only.
4
4
  ---
5
5
 
6
6
  # Wardrobe Configurator CLI
@@ -12,8 +12,13 @@ agent loop is: **init → edit → validate → price → plan → apply**.
12
12
  ## Prerequisites
13
13
 
14
14
  - Install the CLI: `npm install -g @lahat-group/cli` (or run ad-hoc via `npx @lahat-group/cli <verb>`).
15
- It ships as a self-contained binary — no build step, no extra dependencies.
16
- - **Authenticate once**: `wardrobe login` runs the OAuth device flow — it prints a
15
+ It ships as a self-contained binary — no build step, no extra dependencies. It holds
16
+ no domain logic of its own; the rules live in the API.
17
+ - **Authenticate first — every verb needs it.** The CLI is a thin HTTP client: `init`,
18
+ `validate` and `describe` are served by the API just as `price` and `apply` are, so
19
+ none of them work offline. `Not authenticated. Run \`wardrobe login\` first.` means
20
+ exactly that.
21
+ - `wardrobe login` runs the OAuth device flow — it prints a
17
22
  user code, opens `/activate` in the browser, and stores a scoped agent key at
18
23
  `~/.config/wardrobe/config.json` (chmod 600). Confirm with `wardrobe whoami`.
19
24
  - The CLI targets the **production** API by default. Set `WARDROBE_API_URL` to override
@@ -26,7 +31,8 @@ agent loop is: **init → edit → validate → price → plan → apply**.
26
31
  emits a fresh, valid `WardrobeConfig` (full-width, split into optimal columns).
27
32
  2. **edit** — modify `wardrobe.json` directly (see shape below).
28
33
  3. **validate** — `wardrobe validate wardrobe.json` (or `-` for stdin). Runs the
29
- **shared constraint pipeline fully offline** — byte-identical to the web app.
34
+ **live** constraint pipeline server-side — the same rules `wardrobe price` enforces,
35
+ so a config `validate` approves can never be one `price` rejects.
30
36
  Exit 0 = valid; exit 1 + structured errors otherwise. Do this after every edit.
31
37
  4. **price** — `wardrobe price wardrobe.json` → authoritative total in **NIS** from
32
38
  the API's unified area×rate engine.
@@ -65,7 +71,7 @@ or a saved design id.
65
71
  ## Reading what exists
66
72
 
67
73
  Every read the key can reach has a verb. **All of them are GETs** — the CLI can read
68
- customers and orders but never create or change them (`customers:write`,
74
+ customers, orders and quotations but never create or change them (`customers:write`,
69
75
  `orders:write` and `quotations:write` are not on the CLI's key, so those routes
70
76
  answer `403 insufficient_scope` whoever signed in).
71
77
 
@@ -80,32 +86,67 @@ answer `403 insufficient_scope` whoever signed in).
80
86
  | `wardrobe customers get <id>` | One customer |
81
87
  | `wardrobe customers projects <id>` | That customer's projects |
82
88
  | `wardrobe designs list --project <id>` | Designs saved in a project |
89
+ | `wardrobe designs list` | The newest designs across every project you can see (all yours, or the whole tenant with `staff:read`) |
90
+ | `wardrobe designs list --limit <n>` `--since <ISO>` | Same, paged — `--limit` caps rows (default 20, max 100), `--since` filters by `updatedAt >=` |
83
91
  | `wardrobe designs thumbnail <id> --out f.jpg` | The design's rendered thumbnail |
84
92
  | `wardrobe orders list --project <id>` | Every order the project has had |
85
93
  | `wardrobe orders list --project <id> --open` | Only its active (non-cancelled) order |
94
+ | `wardrobe orders list` | The newest-**updated** orders across every project you can see — "what moved", not "what's new": a status change or a revision surfaces an old order here too |
95
+ | `wardrobe orders list --limit <n>` `--since <ISO>` | Same, paged (`--since` filters by `updatedAt >=`) |
86
96
  | `wardrobe orders pdf <id> --out f.pdf` | The order PDF |
97
+ | `wardrobe quotations list --project <id>` | That project's quote number + full revision history |
98
+ | `wardrobe quotations list` | Every quotation you can see, **newest-generated first** — this is "latest quotes generation" |
99
+ | `wardrobe quotations list --limit <n>` `--since <ISO>` | Same, paged (`--since` filters by `updatedAt >=`, i.e. last generated at/after) |
100
+ | `wardrobe users list` | Registered accounts, **newest-registered first** — staff only, see below |
101
+ | `wardrobe users list --limit <n>` `--since <ISO>` | Same, paged (`--since` filters by `createdAt >=`) — `--limit 5` with no `--since` is "the latest registered users"; omit both, paging with `--since` off the last row's timestamp, for "every user in the system" |
102
+ | `wardrobe users by-phone <number>` | The account(s) tied to that phone number: the linked client account plus every designer who owns a project for a customer with it — staff only |
87
103
  | `wardrobe help-texts` | The help-text table |
88
104
 
89
105
  `projects list` already returns each project's **customer and owning designer inline** —
90
106
  so "who is the customer on each of this designer's projects" is one call plus a filter,
91
107
  never one call per project. Prefer `--phone` / `--designer` over fetching and grepping.
92
108
 
109
+ Every cross-project/cross-tenant list (`designs`, `orders`, `quotations`, `users`) takes
110
+ `--limit` (1–100, default 20) and a `--since` cursor — pass the last row's timestamp
111
+ back as `--since` to page further instead of guessing an offset. A malformed `--since` or
112
+ an out-of-range `--limit` is a `400`, not a silently-ignored filter — read the error
113
+ rather than retrying blind.
114
+
93
115
  ### What you can see is not what exists
94
116
 
95
117
  The list is bounded by the key, so an empty result is as often a **visibility** answer as
96
118
  a data one. `wardrobe whoami --json` reports the truth under `agent.scopes` and `role`:
97
119
 
98
- - An ordinary designer or client sees **their own** projects plus ones they were invited
99
- to. A project belonging to someone else answers `403 Forbidden`, not `404`.
120
+ - An ordinary designer or client sees **their own** projects, designs, orders and
121
+ quotations, plus ones they were invited to. A project belonging to someone else
122
+ answers `403 Forbidden`, not `404`. `wardrobe users list` and `users by-phone` answer
123
+ `403 Forbidden` outright — there is no per-row visibility to fall back to; the whole
124
+ surface is staff-only (see below).
100
125
  - A key approved by an **admin or support** user *and* carrying **`staff:read`** sees
101
- **every** project, customer and order in the system. That scope is granted only to
126
+ **every** project, customer, order and quotation in the system — and can use
127
+ `wardrobe users list` / `users by-phone` at all. That scope is granted only to
102
128
  staff, only for GETs, and is listed by name on the consent screen. The same approver
103
129
  also receives **`staff:write`**, which lets `designs create` / `designs update` land in
104
- a project the admin does not own. A key logged in before 2026-09-05 lacks it — a `403
105
- Forbidden` on a write to someone else's project means `wardrobe login` again.
130
+ a project the admin does not own. A key logged in before 2026-09-05 lacks `staff:read`
131
+ and before 2026-09-12 lacks `quotations:read` — a `403 insufficient_scope` on
132
+ `quotations list`, or a `403 Forbidden` on a write to someone else's project, means
133
+ `wardrobe login` again.
106
134
 
107
135
  Never tell a user something does not exist when you only know your key cannot see it.
108
- Say which it is: `403 Forbidden` means the row is someone else's, `404` means it is gone.
136
+ Say which it is: `403 Forbidden` means the row is someone else's (or, for `users`, that
137
+ your key isn't staff at all), `404` means it is gone.
138
+
139
+ ### `wardrobe users` is a directory lookup, not a customer lookup
140
+
141
+ `users list` / `users by-phone` read the `User` **account** table — designers, admins,
142
+ support and self-serve clients who can sign in — not the per-project `Customer` contact
143
+ card `wardrobe customers` reads. A phone number usually resolves to a **designer**
144
+ (whoever owns the project for the customer with that number), occasionally to a
145
+ **client** account too (if that customer accepted a project invitation and has their own
146
+ login). It is normal for `users by-phone` to return more than one account, or to return
147
+ zero when the number belongs to a customer nobody has invited to sign in — that is not
148
+ an error, and it does not mean the phone number is unknown; check `wardrobe projects list
149
+ --phone <number>` for the project/customer side of the same question.
109
150
 
110
151
  ### Not available here
111
152
 
@@ -113,8 +154,6 @@ Don't reach for these; they are not CLI verbs and not agent-reachable:
113
154
 
114
155
  - **Anything under `/admin`**, plus commissions and invitations — barred to every agent
115
156
  key at the mount (`403 agent_not_permitted`). No scope unlocks them.
116
- - **Quotation history.** `POST /quotations/pdf` is open to other clients, but reading a
117
- project's past quotes is JWT-only — a human in the web app.
118
157
  - **Manufacturing-files export.** Staff-only, through the web app.
119
158
 
120
159
  ## WardrobeConfig shape (top-level)
@@ -141,10 +180,11 @@ Prefer generating the skeleton with `wardrobe init` rather than hand-writing it
141
180
 
142
181
  - **The CLI can read orders but never place one.** Its key carries `profile:read`,
143
182
  `projects:read`, `customers:read`, `designs:read`, `designs:write`, `orders:read`,
144
- `price:read` (+ `staff:read` and `staff:write` for staff) — **every write beyond designs is absent**,
145
- and so is every admin scope. `wardrobe orders list` and `orders pdf` work; creating
146
- or changing an order, a customer or a quotation answers `403 insufficient_scope`.
147
- Don't attempt it, and don't attempt admin/catalog operations.
183
+ `price:read`, `quotations:read` (+ `staff:read` and `staff:write` for staff) —
184
+ **every write beyond designs is absent**, and so is every admin scope. `wardrobe
185
+ orders list`, `orders pdf` and `quotations list` work; creating or changing an order,
186
+ a customer or a quotation answers `403 insufficient_scope`. Don't attempt it, and
187
+ don't attempt admin/catalog operations.
148
188
  - To turn a saved design into an order, direct the **human** to the web app: they
149
189
  open the project, review the design, and complete the **verify-details** flow —
150
190
  which computes the final price, freezes the PDF, and advances the order. Only a