@easycustomerfeedback/cli 0.1.0 → 0.2.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 +82 -21
  2. package/dist/index.js +340 -5
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @easycustomerfeedback/cli
2
2
 
3
- `ecf` is the command-line interface for [EasyCustomerFeedback](https://easycustomerfeedback.com). Triage feedback submissions, update statuses, and leave internal comments from your terminal.
3
+ `ecf` is the command-line interface for [EasyCustomerFeedback](https://easycustomerfeedback.com). Triage feedback submissions, manage knowledge base articles, and leave internal comments from your terminal.
4
4
 
5
5
  ## Install
6
6
 
@@ -21,41 +21,42 @@ Requires Node.js 18 or newer.
21
21
  1. In the EasyCustomerFeedback dashboard, open **Integrations → Personal API tokens** and create a token. Copy it — tokens are only shown once.
22
22
  2. Log in:
23
23
 
24
- ```sh
25
- ecf login
26
- ```
24
+ ```sh
25
+ ecf login
26
+ ```
27
+
28
+ You'll be prompted for the server URL (defaults to `https://easycustomerfeedback.com`) and the token. Credentials are saved to `~/.config/ecf/config.json`.
27
29
 
28
- You'll be prompted for the server URL (defaults to `https://easycustomerfeedback.com`) and the token. Credentials are saved to `~/.config/ecf/config.json`.
29
30
  3. If you belong to multiple workspaces, pick a default:
30
31
 
31
- ```sh
32
- ecf workspace list
33
- ecf workspace use <workspaceId>
34
- ```
32
+ ```sh
33
+ ecf workspace list
34
+ ecf workspace use <workspaceId>
35
+ ```
35
36
 
36
37
  ## Commands
37
38
 
38
39
  ### Authentication
39
40
 
40
- | Command | What it does |
41
- | --- | --- |
41
+ | Command | What it does |
42
+ | ----------- | ------------------------------------------------------------------------------------ |
42
43
  | `ecf login` | Configure server URL and API token. Auto-selects the workspace if you only have one. |
43
44
 
44
45
  ### Workspaces
45
46
 
46
- | Command | What it does |
47
- | --- | --- |
48
- | `ecf workspace list` | Show the workspaces you belong to (marks the active one). |
49
- | `ecf workspace use <id>` | Set the default workspace for future commands. |
47
+ | Command | What it does |
48
+ | ------------------------ | --------------------------------------------------------- |
49
+ | `ecf workspace list` | Show the workspaces you belong to (marks the active one). |
50
+ | `ecf workspace use <id>` | Set the default workspace for future commands. |
50
51
 
51
52
  ### Submissions
52
53
 
53
- | Command | What it does |
54
- | --- | --- |
55
- | `ecf submissions list [options]` | List submissions in the active workspace. |
56
- | `ecf submissions get <id> [--json]` | Show details for a submission. |
57
- | `ecf submissions status <id> <status>` | Update a submission's status. |
58
- | `ecf submissions comment <id> [body]` | Add an internal comment (body may come from stdin). |
54
+ | Command | What it does |
55
+ | -------------------------------------- | --------------------------------------------------- |
56
+ | `ecf submissions list [options]` | List submissions in the active workspace. |
57
+ | `ecf submissions get <id> [--json]` | Show details for a submission. |
58
+ | `ecf submissions status <id> <status>` | Update a submission's status. |
59
+ | `ecf submissions comment <id> [body]` | Add an internal comment (body may come from stdin). |
59
60
 
60
61
  `ecf submissions list` options:
61
62
 
@@ -68,6 +69,48 @@ Requires Node.js 18 or newer.
68
69
 
69
70
  Valid status values for `ecf submissions status`: `untriaged`, `open`, `in_progress`, `resolved`, `closed`.
70
71
 
72
+ ### Knowledge base
73
+
74
+ Manage articles and categories scoped to a project. Pass the project's slug as the first positional argument.
75
+
76
+ #### Categories
77
+
78
+ | Command | What it does |
79
+ | --------------------------------------------------------------- | ------------------------------------- |
80
+ | `ecf kb categories list <projectSlug> [--json]` | List categories in the project. |
81
+ | `ecf kb categories create <projectSlug> --name <n> [--description <d>]` | Create a category. |
82
+ | `ecf kb categories delete <projectSlug> <categoryId>` | Delete a category. |
83
+
84
+ #### Articles
85
+
86
+ | Command | What it does |
87
+ | -------------------------------------------------------- | ----------------------------------------- |
88
+ | `ecf kb articles list <projectSlug> [options]` | List articles (optionally filter by status/category). |
89
+ | `ecf kb articles get <projectSlug> <articleId> [--json]` | Show an article (title, metadata, plain-text body). |
90
+ | `ecf kb articles create <projectSlug> --title <t> [options]` | Create a draft article. |
91
+ | `ecf kb articles update <projectSlug> <articleId> [options]` | Update an article. Unspecified fields keep their current values. |
92
+ | `ecf kb articles publish <projectSlug> <articleId>` | Publish (or republish) the article. |
93
+ | `ecf kb articles unpublish <projectSlug> <articleId>` | Move the article back to draft. |
94
+ | `ecf kb articles archive <projectSlug> <articleId>` | Archive the article. |
95
+ | `ecf kb articles delete <projectSlug> <articleId>` | Delete the article. |
96
+
97
+ `ecf kb articles list` options:
98
+
99
+ - `--status <s>` — filter by status (`draft`, `published`, `archived`)
100
+ - `--category <id>` — filter by category id
101
+ - `--json` — output raw JSON
102
+
103
+ `ecf kb articles create` / `update` options:
104
+
105
+ - `--title <t>` — required for `create`; optional for `update`
106
+ - `--summary <s>` — short blurb shown in lists and SEO meta
107
+ - `--category <id>` — assign to a category (use `--no-category` on `update` to detach)
108
+ - `--visibility public|internal` — defaults to `public`
109
+ - `--body-md <path|->` — read a Markdown file (or `-` for stdin) and convert to TipTap JSON
110
+ - `--body-json <path|->` — read a TipTap JSON document directly (file path or `-` for stdin)
111
+
112
+ Pass either `--body-md` or `--body-json`, not both. The Markdown importer supports headings, paragraphs, bullet/ordered lists, blockquotes, fenced code blocks, horizontal rules, hard breaks, and inline marks (`**bold**`, `*italic*`, `` `code` ``, `~~strike~~`, `[link](url)`).
113
+
71
114
  ## Examples
72
115
 
73
116
  List open bugs, piped to your pager:
@@ -88,6 +131,24 @@ Resolve a submission:
88
131
  ecf submissions status sub_abc123 resolved
89
132
  ```
90
133
 
134
+ Draft a knowledge base article from a Markdown file and publish it:
135
+
136
+ ```sh
137
+ ecf kb articles create my-project \
138
+ --title "Resetting your password" \
139
+ --summary "Step-by-step instructions for password resets." \
140
+ --body-md ./docs/password-reset.md
141
+ # → Created kba_abc123 (resetting-your-password)
142
+
143
+ ecf kb articles publish my-project kba_abc123
144
+ ```
145
+
146
+ Pipe Markdown straight from your editor:
147
+
148
+ ```sh
149
+ pbpaste | ecf kb articles update my-project kba_abc123 --body-md -
150
+ ```
151
+
91
152
  ## Configuration
92
153
 
93
154
  Configuration lives at `~/.config/ecf/config.json` and is created by `ecf login`. Delete the file to reset.
package/dist/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // src/commands/login.ts
4
- import { createInterface } from "node:readline/promises";
3
+ // src/commands/kb.ts
4
+ import { readFile } from "node:fs/promises";
5
+ import { parseArgs } from "node:util";
5
6
 
6
7
  // src/api.ts
7
8
  class ApiError extends Error {
@@ -68,7 +69,324 @@ function requireConfig() {
68
69
  return config;
69
70
  }
70
71
 
72
+ // src/commands/kb.ts
73
+ var VALID_STATUSES = ["draft", "published", "archived"];
74
+ var VALID_VISIBILITIES = ["public", "internal"];
75
+ async function kbCommand(args) {
76
+ const [resource, ...rest] = args;
77
+ if (resource === "categories")
78
+ return categoriesCommand(rest);
79
+ if (resource === "articles")
80
+ return articlesCommand(rest);
81
+ console.error("Usage: ecf kb <categories|articles> ...");
82
+ process.exit(1);
83
+ }
84
+ async function categoriesCommand(args) {
85
+ const [sub, ...rest] = args;
86
+ if (sub === "list")
87
+ return listCategories(rest);
88
+ if (sub === "create")
89
+ return createCategory(rest);
90
+ if (sub === "delete")
91
+ return deleteCategory(rest);
92
+ console.error("Usage: ecf kb categories <list|create|delete> ...");
93
+ process.exit(1);
94
+ }
95
+ async function listCategories(args) {
96
+ const { values, positionals } = parseArgs({
97
+ args,
98
+ options: { json: { type: "boolean" } },
99
+ allowPositionals: true
100
+ });
101
+ const [projectSlug] = positionals;
102
+ if (!projectSlug) {
103
+ console.error("Usage: ecf kb categories list <projectSlug> [--json]");
104
+ process.exit(1);
105
+ }
106
+ const config = requireConfig();
107
+ const { categories } = await apiFetch(config, `/api/v1/projects/${encodeURIComponent(projectSlug)}/kb/categories`);
108
+ if (values.json) {
109
+ console.log(JSON.stringify(categories, null, 2));
110
+ return;
111
+ }
112
+ if (categories.length === 0) {
113
+ console.log("No categories.");
114
+ return;
115
+ }
116
+ for (const c of categories) {
117
+ console.log(`${c.id} ${c.slug.padEnd(24)} ${c.name}`);
118
+ }
119
+ }
120
+ async function createCategory(args) {
121
+ const { values, positionals } = parseArgs({
122
+ args,
123
+ options: {
124
+ name: { type: "string" },
125
+ description: { type: "string" }
126
+ },
127
+ allowPositionals: true
128
+ });
129
+ const [projectSlug] = positionals;
130
+ if (!projectSlug || !values.name) {
131
+ console.error("Usage: ecf kb categories create <projectSlug> --name <name> [--description <desc>]");
132
+ process.exit(1);
133
+ }
134
+ const config = requireConfig();
135
+ const { category } = await apiFetch(config, `/api/v1/projects/${encodeURIComponent(projectSlug)}/kb/categories`, {
136
+ method: "POST",
137
+ body: JSON.stringify({ name: values.name, description: values.description })
138
+ });
139
+ console.log(`Created ${category.id} (${category.slug})`);
140
+ }
141
+ async function deleteCategory(args) {
142
+ const [projectSlug, categoryId] = args;
143
+ if (!projectSlug || !categoryId) {
144
+ console.error("Usage: ecf kb categories delete <projectSlug> <categoryId>");
145
+ process.exit(1);
146
+ }
147
+ const config = requireConfig();
148
+ await apiFetch(config, `/api/v1/projects/${encodeURIComponent(projectSlug)}/kb/categories/${encodeURIComponent(categoryId)}`, { method: "DELETE" });
149
+ console.log(`Deleted ${categoryId}`);
150
+ }
151
+ async function articlesCommand(args) {
152
+ const [sub, ...rest] = args;
153
+ if (sub === "list")
154
+ return listArticles(rest);
155
+ if (sub === "get")
156
+ return getArticle(rest);
157
+ if (sub === "create")
158
+ return createArticle(rest);
159
+ if (sub === "update")
160
+ return updateArticle(rest);
161
+ if (sub === "publish")
162
+ return setStatus(rest, "published");
163
+ if (sub === "unpublish")
164
+ return setStatus(rest, "draft");
165
+ if (sub === "archive")
166
+ return setStatus(rest, "archived");
167
+ if (sub === "delete")
168
+ return deleteArticle(rest);
169
+ console.error("Usage: ecf kb articles <list|get|create|update|publish|unpublish|archive|delete> ...");
170
+ process.exit(1);
171
+ }
172
+ async function listArticles(args) {
173
+ const { values, positionals } = parseArgs({
174
+ args,
175
+ options: {
176
+ status: { type: "string" },
177
+ category: { type: "string" },
178
+ json: { type: "boolean" }
179
+ },
180
+ allowPositionals: true
181
+ });
182
+ const [projectSlug] = positionals;
183
+ if (!projectSlug) {
184
+ console.error("Usage: ecf kb articles list <projectSlug> [--status draft|published|archived] [--category <id>] [--json]");
185
+ process.exit(1);
186
+ }
187
+ if (values.status && !VALID_STATUSES.includes(values.status)) {
188
+ console.error(`Invalid --status. Must be one of: ${VALID_STATUSES.join(", ")}`);
189
+ process.exit(1);
190
+ }
191
+ const params = new URLSearchParams;
192
+ if (values.status)
193
+ params.set("status", values.status);
194
+ if (values.category)
195
+ params.set("categoryId", values.category);
196
+ const config = requireConfig();
197
+ const query = params.toString();
198
+ const { articles } = await apiFetch(config, `/api/v1/projects/${encodeURIComponent(projectSlug)}/kb/articles${query ? `?${query}` : ""}`);
199
+ if (values.json) {
200
+ console.log(JSON.stringify(articles, null, 2));
201
+ return;
202
+ }
203
+ if (articles.length === 0) {
204
+ console.log("No articles.");
205
+ return;
206
+ }
207
+ for (const a of articles) {
208
+ const cat = a.categoryName ?? "—";
209
+ console.log(`${a.id} [${a.status.padEnd(9)}] ${a.visibility.padEnd(8)} ${cat.padEnd(20)} ${a.title}`);
210
+ }
211
+ }
212
+ async function getArticle(args) {
213
+ const { values, positionals } = parseArgs({
214
+ args,
215
+ options: { json: { type: "boolean" } },
216
+ allowPositionals: true
217
+ });
218
+ const [projectSlug, articleId] = positionals;
219
+ if (!projectSlug || !articleId) {
220
+ console.error("Usage: ecf kb articles get <projectSlug> <articleId> [--json]");
221
+ process.exit(1);
222
+ }
223
+ const config = requireConfig();
224
+ const { article } = await fetchArticle(config, projectSlug, articleId);
225
+ if (values.json) {
226
+ console.log(JSON.stringify(article, null, 2));
227
+ return;
228
+ }
229
+ console.log(`# ${article.title}`);
230
+ console.log(`id: ${article.id} slug: ${article.slug} status: ${article.status} visibility: ${article.visibility}`);
231
+ if (article.categoryName)
232
+ console.log(`category: ${article.categoryName} (${article.categoryId})`);
233
+ if (article.summary)
234
+ console.log(`
235
+ ${article.summary}`);
236
+ if (article.bodyText)
237
+ console.log(`
238
+ ${article.bodyText}`);
239
+ }
240
+ async function createArticle(args) {
241
+ const { values, positionals } = parseArgs({
242
+ args,
243
+ options: {
244
+ title: { type: "string" },
245
+ summary: { type: "string" },
246
+ category: { type: "string" },
247
+ visibility: { type: "string" },
248
+ "body-md": { type: "string" },
249
+ "body-json": { type: "string" }
250
+ },
251
+ allowPositionals: true
252
+ });
253
+ const [projectSlug] = positionals;
254
+ if (!projectSlug || !values.title) {
255
+ console.error("Usage: ecf kb articles create <projectSlug> --title <t> [--summary <s>] [--category <id>] [--visibility public|internal] [--body-md <path|-> | --body-json <path|->]");
256
+ process.exit(1);
257
+ }
258
+ if (values.visibility && !VALID_VISIBILITIES.includes(values.visibility)) {
259
+ console.error(`Invalid --visibility. Must be one of: ${VALID_VISIBILITIES.join(", ")}`);
260
+ process.exit(1);
261
+ }
262
+ const bodyFields = await readBodyFields({
263
+ bodyMd: values["body-md"],
264
+ bodyJson: values["body-json"]
265
+ });
266
+ if (bodyFields instanceof Error) {
267
+ console.error(bodyFields.message);
268
+ process.exit(1);
269
+ }
270
+ const payload = {
271
+ title: values.title,
272
+ summary: values.summary,
273
+ categoryId: values.category ?? null,
274
+ visibility: values.visibility,
275
+ ...bodyFields
276
+ };
277
+ const config = requireConfig();
278
+ const { article } = await apiFetch(config, `/api/v1/projects/${encodeURIComponent(projectSlug)}/kb/articles`, { method: "POST", body: JSON.stringify(payload) });
279
+ console.log(`Created ${article.id} (${article.slug})`);
280
+ }
281
+ async function updateArticle(args) {
282
+ const { values, positionals } = parseArgs({
283
+ args,
284
+ options: {
285
+ title: { type: "string" },
286
+ summary: { type: "string" },
287
+ category: { type: "string" },
288
+ "no-category": { type: "boolean" },
289
+ visibility: { type: "string" },
290
+ "body-md": { type: "string" },
291
+ "body-json": { type: "string" }
292
+ },
293
+ allowPositionals: true
294
+ });
295
+ const [projectSlug, articleId] = positionals;
296
+ if (!projectSlug || !articleId) {
297
+ console.error("Usage: ecf kb articles update <projectSlug> <articleId> [--title ...] [--summary ...] [--category <id> | --no-category] [--visibility ...] [--body-md <path|-> | --body-json <path|->]");
298
+ process.exit(1);
299
+ }
300
+ if (values.visibility && !VALID_VISIBILITIES.includes(values.visibility)) {
301
+ console.error(`Invalid --visibility. Must be one of: ${VALID_VISIBILITIES.join(", ")}`);
302
+ process.exit(1);
303
+ }
304
+ const bodyFields = await readBodyFields({
305
+ bodyMd: values["body-md"],
306
+ bodyJson: values["body-json"]
307
+ });
308
+ if (bodyFields instanceof Error) {
309
+ console.error(bodyFields.message);
310
+ process.exit(1);
311
+ }
312
+ const config = requireConfig();
313
+ const { article: current } = await fetchArticle(config, projectSlug, articleId);
314
+ const nextTitle = values.title ?? current.title;
315
+ const nextSummary = values.summary !== undefined ? values.summary : current.summary ?? undefined;
316
+ const nextVisibility = values.visibility ?? current.visibility;
317
+ let nextCategoryId;
318
+ if (values["no-category"]) {
319
+ nextCategoryId = null;
320
+ } else if (typeof values.category === "string") {
321
+ nextCategoryId = values.category;
322
+ } else {
323
+ nextCategoryId = current.categoryId;
324
+ }
325
+ const payload = {
326
+ title: nextTitle,
327
+ summary: nextSummary,
328
+ categoryId: nextCategoryId,
329
+ visibility: nextVisibility,
330
+ ...Object.keys(bodyFields).length > 0 ? bodyFields : { bodyJson: current.bodyJson ?? { type: "doc", content: [] } }
331
+ };
332
+ const { article } = await apiFetch(config, `/api/v1/projects/${encodeURIComponent(projectSlug)}/kb/articles/${encodeURIComponent(articleId)}`, { method: "PATCH", body: JSON.stringify(payload) });
333
+ console.log(`Updated ${article.id} (${article.slug})`);
334
+ }
335
+ async function setStatus(args, status) {
336
+ const [projectSlug, articleId] = args;
337
+ if (!projectSlug || !articleId) {
338
+ console.error(`Usage: ecf kb articles <publish|unpublish|archive> <projectSlug> <articleId>`);
339
+ process.exit(1);
340
+ }
341
+ const config = requireConfig();
342
+ await apiFetch(config, `/api/v1/projects/${encodeURIComponent(projectSlug)}/kb/articles/${encodeURIComponent(articleId)}/status`, { method: "POST", body: JSON.stringify({ status }) });
343
+ console.log(`${articleId} → ${status}`);
344
+ }
345
+ async function deleteArticle(args) {
346
+ const [projectSlug, articleId] = args;
347
+ if (!projectSlug || !articleId) {
348
+ console.error("Usage: ecf kb articles delete <projectSlug> <articleId>");
349
+ process.exit(1);
350
+ }
351
+ const config = requireConfig();
352
+ await apiFetch(config, `/api/v1/projects/${encodeURIComponent(projectSlug)}/kb/articles/${encodeURIComponent(articleId)}`, { method: "DELETE" });
353
+ console.log(`Deleted ${articleId}`);
354
+ }
355
+ async function fetchArticle(config, projectSlug, articleId) {
356
+ return apiFetch(config, `/api/v1/projects/${encodeURIComponent(projectSlug)}/kb/articles/${encodeURIComponent(articleId)}`);
357
+ }
358
+ async function readSource(source) {
359
+ if (source === "-") {
360
+ const chunks = [];
361
+ for await (const chunk of process.stdin) {
362
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
363
+ }
364
+ return Buffer.concat(chunks).toString("utf8");
365
+ }
366
+ return readFile(source, "utf8");
367
+ }
368
+ async function readBodyFields(input) {
369
+ if (input.bodyMd && input.bodyJson) {
370
+ return new Error("Pass either --body-md or --body-json, not both");
371
+ }
372
+ if (input.bodyMd) {
373
+ const text = await readSource(input.bodyMd);
374
+ return { bodyMarkdown: text };
375
+ }
376
+ if (input.bodyJson) {
377
+ const text = await readSource(input.bodyJson);
378
+ try {
379
+ const parsed = JSON.parse(text);
380
+ return { bodyJson: parsed };
381
+ } catch {
382
+ return new Error(`--body-json: not valid JSON (${input.bodyJson})`);
383
+ }
384
+ }
385
+ return {};
386
+ }
387
+
71
388
  // src/commands/login.ts
389
+ import { createInterface } from "node:readline/promises";
72
390
  async function prompt(rl, question, def) {
73
391
  const suffix = def ? ` [${def}]` : "";
74
392
  const answer = (await rl.question(`${question}${suffix}: `)).trim();
@@ -109,7 +427,7 @@ Active workspace set to ${workspaces[0].name}.`);
109
427
  }
110
428
 
111
429
  // src/commands/submissions.ts
112
- import { parseArgs } from "node:util";
430
+ import { parseArgs as parseArgs2 } from "node:util";
113
431
  function resolveWorkspace(config, override) {
114
432
  const id = override ?? config.workspaceId;
115
433
  if (!id) {
@@ -139,7 +457,7 @@ async function submissionsCommand(args) {
139
457
  process.exit(1);
140
458
  }
141
459
  async function list(args) {
142
- const { values } = parseArgs({
460
+ const { values } = parseArgs2({
143
461
  args,
144
462
  options: {
145
463
  status: { type: "string", multiple: true },
@@ -177,7 +495,7 @@ async function list(args) {
177
495
  }
178
496
  }
179
497
  async function get(args) {
180
- const { values, positionals } = parseArgs({
498
+ const { values, positionals } = parseArgs2({
181
499
  args,
182
500
  options: { json: { type: "boolean" } },
183
501
  allowPositionals: true
@@ -307,6 +625,20 @@ Usage:
307
625
  ecf submissions status <id> <status> Update a submission's status
308
626
  ecf submissions comment <id> [body] Add an internal comment
309
627
  (if body is omitted, reads from stdin)
628
+
629
+ ecf kb categories list <projectSlug> [--json]
630
+ ecf kb categories create <projectSlug> --name <n> [--description <d>]
631
+ ecf kb categories delete <projectSlug> <categoryId>
632
+
633
+ ecf kb articles list <projectSlug> [--status <s>] [--category <id>] [--json]
634
+ ecf kb articles get <projectSlug> <articleId> [--json]
635
+ ecf kb articles create <projectSlug> --title <t> [options]
636
+ --summary <s> --category <id> --visibility public|internal
637
+ --body-md <path|-> Markdown source (file path, or "-" for stdin)
638
+ --body-json <path|-> TipTap JSON source (file path, or "-" for stdin)
639
+ ecf kb articles update <projectSlug> <articleId> [same options + --no-category]
640
+ ecf kb articles publish|unpublish|archive <projectSlug> <articleId>
641
+ ecf kb articles delete <projectSlug> <articleId>
310
642
  `;
311
643
  async function main() {
312
644
  const argv = process.argv.slice(2);
@@ -326,6 +658,9 @@ async function main() {
326
658
  case "submissions":
327
659
  await submissionsCommand(rest);
328
660
  return;
661
+ case "kb":
662
+ await kbCommand(rest);
663
+ return;
329
664
  default:
330
665
  console.error(`Unknown command: ${command}
331
666
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@easycustomerfeedback/cli",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Command-line interface for EasyCustomerFeedback — list, triage, and comment on feedback submissions from your terminal.",
5
5
  "license": "MIT",
6
6
  "type": "module",