feedbackbasket-cli 0.7.0 → 0.8.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.
package/README.md CHANGED
@@ -22,6 +22,7 @@ feedbackbasket login --manual
22
22
  # Start exploring
23
23
  feedbackbasket projects list
24
24
  feedbackbasket feedback list
25
+ feedbackbasket feedback create "Login button is broken" --project myapp --type bug
25
26
  feedbackbasket bugs list --severity high
26
27
  ```
27
28
 
@@ -35,6 +36,7 @@ Any AI agent with shell access (Claude Code, Codex, Cursor, OpenCode) can use th
35
36
  # Agents should use --agent flag for raw JSON output
36
37
  feedbackbasket projects list --agent
37
38
  feedbackbasket feedback list --category BUG --agent
39
+ feedbackbasket feedback create "Login button is broken" --content "Clicking Log in does nothing in Safari." --project myapp --type bug --agent
38
40
  feedbackbasket feedback update <id> --status PLANNED --agent
39
41
  feedbackbasket widget script myproject --agent
40
42
  ```
@@ -87,6 +89,8 @@ feedbackbasket feedback show <id> # View detail, includ
87
89
  feedbackbasket feedback search "crash on mobile" # Search shortcut
88
90
 
89
91
  # Write
92
+ feedbackbasket feedback create "Title" --content "Body" --project myapp
93
+ feedbackbasket feedback create "Login bug" --content "Clicking Log in does nothing" --project myapp --type bug --page-url https://example.com/login
90
94
  feedbackbasket feedback update <id> --status PLANNED # Update status
91
95
  feedbackbasket feedback update <id> --category BUG # Update category
92
96
  feedbackbasket feedback reply <id> "Thanks for reporting!" # Email the submitter
@@ -1,4 +1,4 @@
1
- import type { ProjectsResponse, FeedbackResponse, BugReportsResponse, FeedbackParams, BugReportParams, UserProfile, Project, Feedback, WidgetSettings } from './types.js';
1
+ import type { ProjectsResponse, FeedbackResponse, BugReportsResponse, FeedbackParams, FeedbackCreateInput, FeedbackCreateResponse, BugReportParams, UserProfile, Project, Feedback, WidgetSettings } from './types.js';
2
2
  export declare class FeedbackBasketClient {
3
3
  private http;
4
4
  constructor(token: string, baseUrl: string);
@@ -34,6 +34,7 @@ export declare class FeedbackBasketClient {
34
34
  category?: string;
35
35
  limit?: number;
36
36
  }): Promise<FeedbackResponse>;
37
+ createFeedback(data: FeedbackCreateInput): Promise<FeedbackCreateResponse>;
37
38
  getWidgetSettings(projectId: string): Promise<{
38
39
  projectId: string;
39
40
  projectName: string;
@@ -50,6 +50,9 @@ export class FeedbackBasketClient {
50
50
  async searchFeedback(query, opts = {}) {
51
51
  return this.getFeedback({ search: query, ...opts });
52
52
  }
53
+ async createFeedback(data) {
54
+ return this.request('POST', '/feedback', data);
55
+ }
53
56
  // Widget
54
57
  async getWidgetSettings(projectId) {
55
58
  return this.request('GET', `/projects/${encodeURIComponent(projectId)}/widget`);
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ import type { OutputWriter } from '../output/writer.js';
3
+ export declare function createFeedbackCreateCommand(getWriter: () => OutputWriter): Command;
@@ -0,0 +1,120 @@
1
+ import { Command } from 'commander';
2
+ import { FeedbackBasketClient } from '../client.js';
3
+ import { AuthManager } from '../auth/manager.js';
4
+ import { loadConfig } from '../config/config.js';
5
+ import { errAuth, errUsage } from '../output/errors.js';
6
+ import { brand } from '../output/theme.js';
7
+ import { resolveProject } from '../resolve.js';
8
+ const validTypes = new Set(['bug', 'feature', 'general']);
9
+ const validCategories = new Set(['BUG', 'FEATURE_REQUEST', 'IMPROVEMENT', 'QUESTION']);
10
+ const validStatuses = new Set(['OPEN', 'UNDER_REVIEW', 'PLANNED', 'IN_PROGRESS', 'COMPLETE', 'CLOSED']);
11
+ export function createFeedbackCreateCommand(getWriter) {
12
+ return new Command('create')
13
+ .argument('<title>', 'Short title or summary for the feedback')
14
+ .description('Create a new feedback item')
15
+ .requiredOption('--project <id-or-name>', 'Project ID or name')
16
+ .option('--content <body>', 'Longer feedback body')
17
+ .option('--type <type>', 'Feedback type (bug, feature, general)')
18
+ .option('--category <category>', 'Category (BUG, FEATURE_REQUEST, IMPROVEMENT, QUESTION)')
19
+ .option('--status <status>', 'Initial status (OPEN, UNDER_REVIEW, PLANNED, IN_PROGRESS, COMPLETE, CLOSED)')
20
+ .option('--email <email>', 'Submitter email')
21
+ .option('--page-url <url>', 'Page URL where the feedback applies')
22
+ .option('--metadata <key=value>', 'Metadata key/value pair (repeatable)', collectMetadata, [])
23
+ .action(async (title, opts) => {
24
+ const writer = getWriter();
25
+ const client = requireClient();
26
+ if (opts.type && !validTypes.has(opts.type)) {
27
+ throw errUsage('Invalid type. Must be one of: bug, feature, general', 'Example: feedbackbasket feedback create "Login is broken" --project myapp --type bug');
28
+ }
29
+ if (opts.category && !validCategories.has(opts.category)) {
30
+ throw errUsage('Invalid category. Must be one of: BUG, FEATURE_REQUEST, IMPROVEMENT, QUESTION');
31
+ }
32
+ if (opts.status && !validStatuses.has(opts.status)) {
33
+ throw errUsage('Invalid status. Must be one of: OPEN, UNDER_REVIEW, PLANNED, IN_PROGRESS, COMPLETE, CLOSED');
34
+ }
35
+ const project = await resolveProject(client, opts.project);
36
+ const content = composeContent(title, opts.content);
37
+ const metadata = parseMetadata(opts.metadata ?? []);
38
+ const result = await client.createFeedback({
39
+ projectId: project.id,
40
+ content,
41
+ type: opts.type,
42
+ category: opts.category,
43
+ status: opts.status,
44
+ email: opts.email,
45
+ pageUrl: opts.pageUrl,
46
+ metadata,
47
+ });
48
+ if (!writer.isMachineOutput()) {
49
+ console.log(` ${brand.success('[OK]')} Feedback created: ${brand.bold(result.id)}`);
50
+ console.log(` ${brand.muted('Project:')} ${project.name}`);
51
+ console.log(` ${brand.muted('URL:')} ${result.url}`);
52
+ console.log();
53
+ }
54
+ writer.ok(result, {
55
+ summary: `Created feedback ${result.id}`,
56
+ breadcrumbs: [
57
+ { action: 'View feedback', cmd: `feedbackbasket feedback show ${result.id}` },
58
+ { action: 'Update status', cmd: `feedbackbasket feedback update ${result.id} --status UNDER_REVIEW` },
59
+ { action: 'List project feedback', cmd: `feedbackbasket feedback list --project ${project.id}` },
60
+ ],
61
+ });
62
+ });
63
+ }
64
+ function requireClient() {
65
+ const manager = new AuthManager();
66
+ const token = manager.resolveToken();
67
+ if (!token)
68
+ throw errAuth();
69
+ const config = loadConfig();
70
+ return new FeedbackBasketClient(token, config.baseUrl);
71
+ }
72
+ function composeContent(title, body) {
73
+ const cleanTitle = title.trim();
74
+ const cleanBody = body?.trim();
75
+ const content = cleanBody ? `${cleanTitle}\n\n${cleanBody}` : cleanTitle;
76
+ if (!content) {
77
+ throw errUsage('Feedback title is required');
78
+ }
79
+ if (content.length > 2000) {
80
+ throw errUsage('Feedback content must be 2000 characters or less');
81
+ }
82
+ return content;
83
+ }
84
+ function collectMetadata(value, previous) {
85
+ return previous.concat(value);
86
+ }
87
+ function parseMetadata(entries) {
88
+ if (entries.length === 0)
89
+ return undefined;
90
+ const metadata = {};
91
+ for (const entry of entries) {
92
+ const separator = entry.indexOf('=');
93
+ if (separator <= 0) {
94
+ throw errUsage(`Invalid metadata "${entry}"`, 'Use --metadata key=value, for example --metadata source=codex');
95
+ }
96
+ const key = entry.slice(0, separator).trim();
97
+ const rawValue = entry.slice(separator + 1).trim();
98
+ if (!key) {
99
+ throw errUsage('Metadata keys cannot be empty');
100
+ }
101
+ metadata[key] = parseMetadataValue(rawValue);
102
+ }
103
+ return metadata;
104
+ }
105
+ function parseMetadataValue(value) {
106
+ if (value === 'true')
107
+ return true;
108
+ if (value === 'false')
109
+ return false;
110
+ if (value === 'null')
111
+ return null;
112
+ if (value !== '' && Number.isFinite(Number(value)))
113
+ return Number(value);
114
+ try {
115
+ return JSON.parse(value);
116
+ }
117
+ catch {
118
+ return value;
119
+ }
120
+ }
@@ -17,6 +17,7 @@ async function resolveProjectId(client, optProject, all) {
17
17
  }
18
18
  import { createFeedbackUpdateCommand } from './feedback-update.js';
19
19
  import { createFeedbackNoteCommand } from './feedback-note.js';
20
+ import { createFeedbackCreateCommand } from './feedback-create.js';
20
21
  import { createFeedbackDeleteCommand } from './feedback-delete.js';
21
22
  import { createFeedbackBulkUpdateCommand } from './feedback-bulk-update.js';
22
23
  import { createFeedbackExportCommand } from './feedback-export.js';
@@ -25,6 +26,7 @@ export function createFeedbackCommand(getWriter) {
25
26
  const feedback = new Command('feedback')
26
27
  .description('View and manage feedback');
27
28
  // Write subcommands
29
+ feedback.addCommand(createFeedbackCreateCommand(getWriter));
28
30
  feedback.addCommand(createFeedbackUpdateCommand(getWriter));
29
31
  feedback.addCommand(createFeedbackNoteCommand(getWriter));
30
32
  feedback.addCommand(createFeedbackReplyCommand(getWriter));
package/dist/src/help.js CHANGED
@@ -57,6 +57,7 @@ export function renderRootHelp() {
57
57
  // Examples
58
58
  lines.push(section(' EXAMPLES'));
59
59
  lines.push(`${INDENT}${brand.muted('$')} feedbackbasket projects list`);
60
+ lines.push(`${INDENT}${brand.muted('$')} feedbackbasket feedback create "Login bug" --project myapp --type bug`);
60
61
  lines.push(`${INDENT}${brand.muted('$')} feedbackbasket feedback list --category BUG --status OPEN`);
61
62
  lines.push(`${INDENT}${brand.muted('$')} feedbackbasket bugs list --severity high`);
62
63
  lines.push(`${INDENT}${brand.muted('$')} feedbackbasket widget script myapp`);
@@ -126,6 +126,21 @@ export interface FeedbackResponse {
126
126
  feedback: Feedback[];
127
127
  pagination: Pagination;
128
128
  }
129
+ export interface FeedbackCreateInput {
130
+ projectId: string;
131
+ content: string;
132
+ type?: 'bug' | 'feature' | 'general';
133
+ category?: FeedbackCategory;
134
+ status?: FeedbackStatus;
135
+ email?: string;
136
+ pageUrl?: string;
137
+ metadata?: Record<string, unknown>;
138
+ }
139
+ export interface FeedbackCreateResponse {
140
+ id: string;
141
+ url: string;
142
+ feedback: Feedback;
143
+ }
129
144
  export interface BugReportsResponse {
130
145
  bugReports: BugReport[];
131
146
  stats: {
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.7.0";
2
- export declare const USER_AGENT = "FeedbackBasket-CLI/0.7.0";
1
+ export declare const VERSION = "0.8.0";
2
+ export declare const USER_AGENT = "FeedbackBasket-CLI/0.8.0";
@@ -1,2 +1,2 @@
1
- export const VERSION = '0.7.0';
1
+ export const VERSION = '0.8.0';
2
2
  export const USER_AGENT = `FeedbackBasket-CLI/${VERSION}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "feedbackbasket-cli",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Command-line interface for FeedbackBasket — manage feedback from your terminal",
5
5
  "type": "module",
6
6
  "main": "dist/src/cli.js",
@@ -62,6 +62,8 @@ feedbackbasket feedback show <id>
62
62
  feedbackbasket feedback search "crash on mobile" --project <id> --limit 10
63
63
 
64
64
  # Write
65
+ feedbackbasket feedback create "Login button is broken" --content "Clicking Log in does nothing in Safari." --project <id> --type bug
66
+ feedbackbasket feedback create "Feature idea" --content "Let users export saved views." --project <id> --type feature --metadata source=agent
65
67
  feedbackbasket feedback update <id> --status PLANNED --category BUG --sentiment NEGATIVE
66
68
  feedbackbasket feedback note <id> "Investigating — appears related to auth flow"
67
69
  feedbackbasket feedback delete <id> --yes
@@ -143,6 +145,18 @@ feedbackbasket feedback update <id> --status UNDER_REVIEW --agent
143
145
  feedbackbasket feedback note <id> "Reviewing — appears related to auth flow" --agent
144
146
  ```
145
147
 
148
+ ### Capture new feedback without leaving the terminal
149
+ ```bash
150
+ feedbackbasket feedback create "Login button is broken" \
151
+ --content "Clicking Log in does nothing in Safari." \
152
+ --project myapp \
153
+ --type bug \
154
+ --page-url https://example.com/login \
155
+ --metadata source=agent \
156
+ --agent
157
+ ```
158
+ Agent mode returns the created feedback ID, dashboard URL, and feedback object. Created feedback is analyzed by AI and follows the project's notification settings.
159
+
146
160
  ### Investigate high-priority bugs
147
161
  ```bash
148
162
  feedbackbasket bugs list --severity high --agent