@svgicons-com/cli 0.1.0-alpha.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.
package/README.md ADDED
@@ -0,0 +1,281 @@
1
+ # Svg/icons CLI Alpha
2
+
3
+ The Svg/icons CLI alpha is a developer workflow client for Pro Plan API tokens. It can search icons, recommend icons for a UI brief, create Icon Collections, add icons to collections, queue exports, and scan a local codebase for UI concepts.
4
+
5
+ The scanner is read-only by default. It never edits project files unless a future command explicitly adds that behavior.
6
+
7
+ Current package version: `0.1.0-alpha.1`.
8
+
9
+ ## Requirements
10
+
11
+ - Node.js 18.18 or newer
12
+ - A Svg/icons Pro API token for collection and export commands
13
+
14
+ ## Login
15
+
16
+ ```bash
17
+ svgicons login --token YOUR_PRO_API_TOKEN
18
+ ```
19
+
20
+ The explicit auth namespace is also supported:
21
+
22
+ ```bash
23
+ svgicons auth login --token YOUR_PRO_API_TOKEN
24
+ svgicons auth status
25
+ svgicons doctor
26
+ ```
27
+
28
+ Use `--base-url http://127.0.0.1:8000` when testing against a local Laravel server.
29
+
30
+ You can also avoid writing a config file by setting `SVGICONS_TOKEN` or `SVGICONS_API_TOKEN`.
31
+
32
+ Inspect local config without exposing the stored token:
33
+
34
+ ```bash
35
+ svgicons config list
36
+ svgicons config get baseUrl
37
+ svgicons config set baseUrl https://svgicons.com
38
+ ```
39
+
40
+ ## Commands
41
+
42
+ ```bash
43
+ svgicons version
44
+ svgicons auth login --token YOUR_PRO_API_TOKEN
45
+ svgicons auth status --json
46
+ svgicons config list --json
47
+ svgicons doctor --json
48
+ svgicons search "arrow left" --limit 10
49
+ svgicons search "arrow left" --anonymous
50
+ svgicons recommend "billing settings dashboard" --limit 12
51
+ svgicons recommend "billing settings dashboard" --create-collection --collection-name "Billing icons"
52
+ svgicons pick "settings gear" --download --output ./icons
53
+ svgicons pick "credit card" --add "Billing icons"
54
+ svgicons pick "settings gear" --interactive
55
+ svgicons icon show 33716
56
+ svgicons icon url 33716-arrow-circle-up-fill
57
+ svgicons icon raw 33716-arrow-circle-up-fill
58
+ svgicons icon download 33716-arrow-circle-up-fill --output ./icons
59
+ svgicons collection list
60
+ svgicons collection create --name "Dashboard icons" --description "Navigation and status icons"
61
+ svgicons collection show "Dashboard icons" --icons
62
+ svgicons collection rename "Dashboard icons" --name "Billing icons"
63
+ svgicons collection update "Billing icons" --framework vue --color-policy preserve
64
+ svgicons collection add "Dashboard icons" 33716 240297
65
+ svgicons collection remove "Dashboard icons" 33716-arrow-circle-up-fill
66
+ svgicons collection delete "Dashboard icons" --yes
67
+ svgicons collection export "Dashboard icons" --formats react-ts,vue --color-policy currentColor --output ./exports
68
+ svgicons collection export "Dashboard icons" --formats react-ts --no-size-props --no-typescript --output ./exports
69
+ svgicons export status 55 --collection "Dashboard icons"
70
+ svgicons export download 55 --collection "Dashboard icons" --output ./exports
71
+ svgicons init --collection "Dashboard icons" --output ./src/icons
72
+ svgicons sync
73
+ svgicons build --ci
74
+ svgicons update --check
75
+ svgicons license check --allow MIT,Apache-2.0,ISC --fail
76
+ svgicons license export --format markdown --output THIRD_PARTY_ICONS.md
77
+ svgicons scan .
78
+ svgicons scan . --write-manifest
79
+ ```
80
+
81
+ Use `--json` on commands when integrating with scripts.
82
+
83
+ ## Release status
84
+
85
+ This package is an alpha release. It is safe to test in real projects, but command names and JSON response shapes may still change before a stable `1.0.0`.
86
+
87
+ See `RELEASE_NOTES.md` for included workflows, safety notes, and known limitations.
88
+
89
+ Maintainers should use `../docs/svgicons-cli-npm-publishing.md` before publishing a new npm version.
90
+
91
+ ## Download one icon
92
+
93
+ Download a single SVG file to the current folder:
94
+
95
+ ```bash
96
+ svgicons icon download 33716-arrow-circle-up-fill
97
+ ```
98
+
99
+ Download to a folder:
100
+
101
+ ```bash
102
+ svgicons icon download 33716-arrow-circle-up-fill --output ./icons
103
+ ```
104
+
105
+ Download to a specific file:
106
+
107
+ ```bash
108
+ svgicons icon download 33716-arrow-circle-up-fill --output ./icons/arrow-up.svg
109
+ ```
110
+
111
+ The icon reference must include both the numeric ID and the icon name, such as `33716-arrow-circle-up-fill`. The CLI checks the returned icon metadata before writing the file. This makes sequential ID-only download scripts less useful.
112
+
113
+ This command uses the MCP `get_icon` tool with raw SVG output, so the token needs `mcp:use` and `icons:read`. Existing files are not overwritten unless you add `--force`.
114
+
115
+ Read-only icon commands accept a numeric ID, an `id-name` reference, or a full svgicons.com icon URL:
116
+
117
+ ```bash
118
+ svgicons icon show 33716
119
+ svgicons icon url 33716-arrow-circle-up-fill
120
+ svgicons icon raw https://svgicons.com/icon/33716/arrow-circle-up-fill
121
+ ```
122
+
123
+ ## Recommend and pick icons
124
+
125
+ Ask Svg/icons for icon candidates based on a UI brief:
126
+
127
+ ```bash
128
+ svgicons recommend "billing settings dashboard with invoices, payment methods, and security"
129
+ ```
130
+
131
+ `recommend` uses the hosted MCP `recommend_icons_for_ui` tool. It returns metadata by default, so it can work without writing files. Add `--json` for automation.
132
+
133
+ Create a Pro Icon Collection directly from a recommendation:
134
+
135
+ ```bash
136
+ svgicons recommend "billing settings dashboard" --create-collection --collection-name "Billing icons"
137
+ ```
138
+
139
+ This uses the MCP `generate_icon_kit_for_project` tool and requires a Pro token with `mcp:use` and `collections:write`.
140
+
141
+ For deterministic scripts, `pick` selects the first search result:
142
+
143
+ ```bash
144
+ svgicons pick "settings gear"
145
+ svgicons pick "settings gear" --download --output ./icons
146
+ svgicons pick "credit card" --add "Billing icons"
147
+ ```
148
+
149
+ Use `--download` when you want the selected SVG written locally. Use `--add <collection>` when you want the selected icon added to an Icon Collection.
150
+
151
+ For manual terminal selection, opt in to the interactive picker:
152
+
153
+ ```bash
154
+ svgicons pick "settings gear" --interactive
155
+ svgicons pick "settings gear" --interactive --download --output ./icons
156
+ ```
157
+
158
+ `--interactive` requires a real terminal and is rejected in CI/non-interactive shells. Omit it for deterministic scripts.
159
+
160
+ ## Export and download a collection ZIP
161
+
162
+ Queue an export, wait for the background job to finish, and download the ZIP:
163
+
164
+ ```bash
165
+ svgicons collection export "Dashboard icons" --formats react-ts,vue --output ./exports
166
+ ```
167
+
168
+ The command polls every 2 seconds for up to 180 seconds by default. Use `--timeout 300` for larger collections, or `--no-wait` if you only want to queue the export and manually download it later.
169
+
170
+ Supported export flags:
171
+
172
+ ```bash
173
+ --formats react-ts,vue
174
+ --color-policy currentColor|preserve|strip
175
+ --naming-policy kebab|pascal|camel
176
+ --size-props / --no-size-props
177
+ --typescript / --no-typescript
178
+ ```
179
+
180
+ Collection commands accept a numeric ID, exact slug, or exact case-insensitive collection name. The legacy `kit` alias remains available for old scripts.
181
+
182
+ Lifecycle commands are available for collection maintenance:
183
+
184
+ ```bash
185
+ svgicons collection show "Dashboard icons" --icons
186
+ svgicons collection update "Dashboard icons" --description "Core product UI icons"
187
+ svgicons collection rename "Dashboard icons" --name "Billing icons"
188
+ svgicons collection remove "Billing icons" 33716-arrow-circle-up-fill
189
+ svgicons collection delete "Billing icons" --yes
190
+ ```
191
+
192
+ `collection delete` prompts for confirmation in an interactive shell. Use `--yes` in CI or scripts.
193
+
194
+ If you queued an export with `--no-wait`, check and download it later:
195
+
196
+ ```bash
197
+ svgicons export status 55 --collection "Dashboard icons"
198
+ svgicons export download 55 --collection "Dashboard icons" --output ./exports
199
+ ```
200
+
201
+ ## Local project manifest and lockfile
202
+
203
+ Initialize a local project:
204
+
205
+ ```bash
206
+ svgicons init --collection "Dashboard icons" --output ./src/icons
207
+ ```
208
+
209
+ This creates `svgicons.json`. The manifest can reference individual icon IDs or Icon Collections:
210
+
211
+ ```json
212
+ {
213
+ "version": 1,
214
+ "format": "svg",
215
+ "output": "./src/icons",
216
+ "icons": [{ "ref": "33716-arrow-circle-up-fill" }],
217
+ "collections": [{ "ref": "Dashboard icons" }]
218
+ }
219
+ ```
220
+
221
+ Sync the manifest into a deterministic lockfile:
222
+
223
+ ```bash
224
+ svgicons sync
225
+ ```
226
+
227
+ This writes `svgicons.lock` with icon SVG markup, source icon set metadata, license data, and SVG hashes. Build local SVG files from the lockfile:
228
+
229
+ ```bash
230
+ svgicons build --ci
231
+ ```
232
+
233
+ `build` uses only `svgicons.lock`, so CI builds do not depend on live API responses. Re-running `build` writes stable SVG files with stable names.
234
+
235
+ Check whether locked icons changed upstream:
236
+
237
+ ```bash
238
+ svgicons update --check
239
+ ```
240
+
241
+ ## License audit
242
+
243
+ Check local icon licenses from `svgicons.lock`:
244
+
245
+ ```bash
246
+ svgicons license check --allow MIT,Apache-2.0,ISC,CC0-1.0 --fail
247
+ ```
248
+
249
+ Use `--deny` for forbidden licenses:
250
+
251
+ ```bash
252
+ svgicons license check --deny GPL,CC-BY-NC-SA-4.0 --fail
253
+ ```
254
+
255
+ Export a license summary for your repository:
256
+
257
+ ```bash
258
+ svgicons license export --format markdown --output THIRD_PARTY_ICONS.md
259
+ svgicons license export --format json --output third-party-icons.json
260
+ svgicons license export --format csv --output third-party-icons.csv
261
+ ```
262
+
263
+ ## Scanner
264
+
265
+ The scanner reads common frontend files, detects UI concepts such as dashboard, search, billing, upload, auth, and settings, and reports a proposed Icon Collection. It also detects existing `svgicons.com/icon/{id}/{name}` links, generated SVG imports, and inline SVG blocks.
266
+
267
+ ```bash
268
+ svgicons scan ./src --max-files 300
269
+ ```
270
+
271
+ To update a local `svgicons.json` manifest from detected icon refs, opt in explicitly:
272
+
273
+ ```bash
274
+ svgicons scan . --write-manifest
275
+ ```
276
+
277
+ To create a collection from existing icon URLs found in the scan, opt in explicitly:
278
+
279
+ ```bash
280
+ svgicons scan . --create-collection
281
+ ```
@@ -0,0 +1,42 @@
1
+ # Svg/icons CLI 0.1.0-alpha.1
2
+
3
+ This alpha release prepares the CLI for real Pro Plan workflows against the svgicons.com Laravel backend.
4
+
5
+ ## Included
6
+
7
+ - Token login/logout plus `auth status`, known scopes, config inspection, and `doctor`.
8
+ - Public icon search through the hosted MCP endpoint, with authenticated search when a token is configured.
9
+ - Icon metadata, URL, raw SVG, browser open, and strict `id-name` SVG download.
10
+ - Icon Collection list, create, show, rename, update, add, remove, delete, export, status, and ZIP download.
11
+ - Backward-compatible `kit` alias for collection commands.
12
+ - Local `svgicons.json` manifest, deterministic `svgicons.lock`, SVG build output, update checks, and license audits.
13
+ - Project scanner that detects UI concepts, svgicons.com URLs, generated SVG imports, and inline SVG blocks.
14
+ - `recommend` for UI-brief icon recommendations and Pro generated collections.
15
+ - `pick` for deterministic first-result selection, plus opt-in interactive terminal selection.
16
+
17
+ ## Safety Notes
18
+
19
+ - The scanner is read-only by default. It writes only when `--write-manifest` or `--create-collection` is used.
20
+ - Destructive collection deletion requires confirmation or `--yes`.
21
+ - `--interactive` is rejected in CI/non-interactive shells.
22
+ - Tokens and authorization headers are redacted from config and diagnostic output.
23
+ - SVG download remains strict: the reference must include both icon ID and name.
24
+
25
+ ## Known Limitations
26
+
27
+ - The package is still alpha. Command names and JSON response shapes may change before a stable `1.0.0`.
28
+ - The interactive picker is intentionally simple and terminal-only.
29
+ - Search and recommendation quality depend on the current MCP metadata search implementation.
30
+ - Local `build` currently writes SVG files. Framework component ZIPs are produced through collection exports.
31
+ - Live Pro workflows require an active Pro account, verified email, and API token scopes matching the command.
32
+
33
+ ## Verification Checklist
34
+
35
+ Before publishing, run:
36
+
37
+ ```bash
38
+ npm --prefix cli test
39
+ npm pack --dry-run
40
+ ```
41
+
42
+ When backend changes are also included, run the relevant Laravel tests for Pro API tokens, MCP, Icon Collections, and collection exports.
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { run } from '../src/cli.js';
4
+ import { serializeError } from '../src/errors.js';
5
+
6
+ run(process.argv.slice(2)).catch((error) => {
7
+ if (process.argv.includes('--json')) {
8
+ console.error(JSON.stringify(serializeError(error), null, 2));
9
+ } else {
10
+ console.error(error?.message || String(error));
11
+ }
12
+ process.exitCode = 1;
13
+ });
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@svgicons-com/cli",
3
+ "version": "0.1.0-alpha.1",
4
+ "description": "Svg/icons CLI alpha for icon search, Pro collections, exports, project scanning, and license workflows.",
5
+ "type": "module",
6
+ "bin": {
7
+ "svgicons": "./bin/svgicons.js"
8
+ },
9
+ "files": [
10
+ "bin/",
11
+ "src/",
12
+ "README.md",
13
+ "RELEASE_NOTES.md"
14
+ ],
15
+ "scripts": {
16
+ "test": "node --test tests"
17
+ },
18
+ "engines": {
19
+ "node": ">=18.18"
20
+ },
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "license": "UNLICENSED",
25
+ "private": false
26
+ }
package/src/api.js ADDED
@@ -0,0 +1,194 @@
1
+ import { credentials } from './config.js';
2
+ import { httpError, invalidJsonError, mcpError, missingTokenError, networkError } from './errors.js';
3
+
4
+ let rpcId = 1;
5
+
6
+ export async function callMcpTool(name, args = {}, options = {}) {
7
+ const response = await mcpRequest('tools/call', {
8
+ name,
9
+ arguments: args,
10
+ }, options);
11
+
12
+ const result = response.result || {};
13
+ const content = result.structuredContent || parseMcpText(result.content);
14
+
15
+ if (result.isError || content?.error) {
16
+ throw mcpError(content?.error?.message || 'MCP tool returned an error', {
17
+ tool: name,
18
+ error: content?.error,
19
+ });
20
+ }
21
+
22
+ return content;
23
+ }
24
+
25
+ export async function mcpRequest(method, params = {}, options = {}) {
26
+ const creds = await credentials();
27
+ const token = options.token ?? creds.token;
28
+ const baseUrl = options.baseUrl ?? creds.baseUrl;
29
+
30
+ const response = await request(`${baseUrl}/mcp`, {
31
+ method: 'POST',
32
+ token,
33
+ body: {
34
+ jsonrpc: '2.0',
35
+ id: rpcId++,
36
+ method,
37
+ params,
38
+ },
39
+ });
40
+
41
+ if (response.error) {
42
+ throw mcpError(response.error.message || 'MCP request failed', {
43
+ code: response.error.code,
44
+ data: response.error.data,
45
+ });
46
+ }
47
+
48
+ return response;
49
+ }
50
+
51
+ export async function proApi(path, options = {}) {
52
+ const creds = await credentials();
53
+ const token = options.token ?? creds.token;
54
+ const baseUrl = options.baseUrl ?? creds.baseUrl;
55
+
56
+ if (!token) {
57
+ throw missingTokenError();
58
+ }
59
+
60
+ return request(`${baseUrl}${path}`, {
61
+ ...options,
62
+ token,
63
+ });
64
+ }
65
+
66
+ export async function proApiDownload(pathOrUrl, options = {}) {
67
+ const creds = await credentials();
68
+ const token = options.token ?? creds.token;
69
+ const baseUrl = options.baseUrl ?? creds.baseUrl;
70
+
71
+ if (!token) {
72
+ throw missingTokenError();
73
+ }
74
+
75
+ const url = /^https?:\/\//i.test(pathOrUrl)
76
+ ? pathOrUrl
77
+ : `${baseUrl}${pathOrUrl}`;
78
+
79
+ let response;
80
+
81
+ try {
82
+ response = await fetch(url, {
83
+ method: options.method || 'GET',
84
+ headers: {
85
+ Accept: 'application/zip, application/octet-stream',
86
+ Authorization: `Bearer ${token}`,
87
+ 'User-Agent': 'svgicons-cli/0.1.0-alpha',
88
+ ...options.headers,
89
+ },
90
+ });
91
+ } catch (error) {
92
+ throw networkError(error, url);
93
+ }
94
+
95
+ if (!response.ok) {
96
+ const text = await response.text();
97
+ let data = {};
98
+
99
+ try {
100
+ data = text ? JSON.parse(text) : {};
101
+ } catch {
102
+ data = {};
103
+ }
104
+
105
+ throw httpError(response.status, url, data);
106
+ }
107
+
108
+ return {
109
+ bytes: Buffer.from(await response.arrayBuffer()),
110
+ filename: filenameFromContentDisposition(response.headers.get('content-disposition')),
111
+ contentType: response.headers.get('content-type'),
112
+ };
113
+ }
114
+
115
+ async function request(url, options = {}) {
116
+ const headers = {
117
+ Accept: 'application/json',
118
+ 'User-Agent': 'svgicons-cli/0.1.0-alpha',
119
+ ...options.headers,
120
+ };
121
+
122
+ const init = {
123
+ method: options.method || 'GET',
124
+ headers,
125
+ };
126
+
127
+ if (options.token) {
128
+ headers.Authorization = `Bearer ${options.token}`;
129
+ }
130
+
131
+ if (options.body !== undefined) {
132
+ headers['Content-Type'] = 'application/json';
133
+ init.body = JSON.stringify(options.body);
134
+ }
135
+
136
+ let response;
137
+
138
+ try {
139
+ response = await fetch(url, init);
140
+ } catch (error) {
141
+ throw networkError(error, url);
142
+ }
143
+
144
+ const text = await response.text();
145
+ const data = text ? safeJson(text, url) : {};
146
+
147
+ if (!response.ok) {
148
+ throw httpError(response.status, url, data);
149
+ }
150
+
151
+ return data;
152
+ }
153
+
154
+ function filenameFromContentDisposition(header) {
155
+ if (!header) {
156
+ return null;
157
+ }
158
+
159
+ const utf8Match = header.match(/filename\*=UTF-8''([^;]+)/i);
160
+ if (utf8Match) {
161
+ return decodeURIComponent(utf8Match[1].trim().replace(/^"|"$/g, ''));
162
+ }
163
+
164
+ const asciiMatch = header.match(/filename="?([^";]+)"?/i);
165
+ if (asciiMatch) {
166
+ return asciiMatch[1].trim();
167
+ }
168
+
169
+ return null;
170
+ }
171
+
172
+ function safeJson(text, url) {
173
+ try {
174
+ return JSON.parse(text);
175
+ } catch {
176
+ throw invalidJsonError(url);
177
+ }
178
+ }
179
+
180
+ function parseMcpText(content) {
181
+ const firstText = Array.isArray(content)
182
+ ? content.find((item) => item?.type === 'text')?.text
183
+ : '';
184
+
185
+ if (!firstText) {
186
+ return {};
187
+ }
188
+
189
+ try {
190
+ return JSON.parse(firstText);
191
+ } catch {
192
+ return {};
193
+ }
194
+ }