@meltstudio/meltctl 4.38.0 → 4.38.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 (81) hide show
  1. package/dist/index.js +2178 -210
  2. package/package.json +4 -3
  3. package/dist/commands/audit.d.ts +0 -10
  4. package/dist/commands/audit.js +0 -191
  5. package/dist/commands/audit.test.d.ts +0 -1
  6. package/dist/commands/audit.test.js +0 -324
  7. package/dist/commands/coins.d.ts +0 -5
  8. package/dist/commands/coins.js +0 -51
  9. package/dist/commands/coins.test.d.ts +0 -1
  10. package/dist/commands/coins.test.js +0 -113
  11. package/dist/commands/feedback.d.ts +0 -7
  12. package/dist/commands/feedback.js +0 -90
  13. package/dist/commands/feedback.test.d.ts +0 -1
  14. package/dist/commands/feedback.test.js +0 -177
  15. package/dist/commands/init.d.ts +0 -8
  16. package/dist/commands/init.js +0 -520
  17. package/dist/commands/init.test.d.ts +0 -1
  18. package/dist/commands/init.test.js +0 -478
  19. package/dist/commands/login.d.ts +0 -1
  20. package/dist/commands/login.js +0 -90
  21. package/dist/commands/login.test.d.ts +0 -1
  22. package/dist/commands/login.test.js +0 -194
  23. package/dist/commands/logout.d.ts +0 -1
  24. package/dist/commands/logout.js +0 -12
  25. package/dist/commands/logout.test.d.ts +0 -1
  26. package/dist/commands/logout.test.js +0 -59
  27. package/dist/commands/plan.d.ts +0 -6
  28. package/dist/commands/plan.js +0 -123
  29. package/dist/commands/plan.test.d.ts +0 -1
  30. package/dist/commands/plan.test.js +0 -246
  31. package/dist/commands/standup.d.ts +0 -7
  32. package/dist/commands/standup.js +0 -74
  33. package/dist/commands/standup.test.d.ts +0 -1
  34. package/dist/commands/standup.test.js +0 -218
  35. package/dist/commands/templates.d.ts +0 -1
  36. package/dist/commands/templates.js +0 -37
  37. package/dist/commands/templates.test.d.ts +0 -1
  38. package/dist/commands/templates.test.js +0 -89
  39. package/dist/commands/update.d.ts +0 -2
  40. package/dist/commands/update.js +0 -74
  41. package/dist/commands/update.test.d.ts +0 -1
  42. package/dist/commands/update.test.js +0 -93
  43. package/dist/commands/version.d.ts +0 -1
  44. package/dist/commands/version.js +0 -43
  45. package/dist/commands/version.test.d.ts +0 -1
  46. package/dist/commands/version.test.js +0 -86
  47. package/dist/index.d.ts +0 -2
  48. package/dist/utils/analytics.d.ts +0 -1
  49. package/dist/utils/analytics.js +0 -54
  50. package/dist/utils/analytics.test.d.ts +0 -1
  51. package/dist/utils/analytics.test.js +0 -91
  52. package/dist/utils/api.d.ts +0 -3
  53. package/dist/utils/api.js +0 -23
  54. package/dist/utils/api.test.d.ts +0 -1
  55. package/dist/utils/api.test.js +0 -76
  56. package/dist/utils/auth.d.ts +0 -12
  57. package/dist/utils/auth.js +0 -54
  58. package/dist/utils/auth.test.d.ts +0 -1
  59. package/dist/utils/auth.test.js +0 -165
  60. package/dist/utils/banner.d.ts +0 -1
  61. package/dist/utils/banner.js +0 -22
  62. package/dist/utils/banner.test.d.ts +0 -1
  63. package/dist/utils/banner.test.js +0 -34
  64. package/dist/utils/debug.d.ts +0 -1
  65. package/dist/utils/debug.js +0 -6
  66. package/dist/utils/git.d.ts +0 -9
  67. package/dist/utils/git.js +0 -76
  68. package/dist/utils/git.test.d.ts +0 -1
  69. package/dist/utils/git.test.js +0 -184
  70. package/dist/utils/package-manager.d.ts +0 -7
  71. package/dist/utils/package-manager.js +0 -55
  72. package/dist/utils/package-manager.test.d.ts +0 -1
  73. package/dist/utils/package-manager.test.js +0 -76
  74. package/dist/utils/templates.d.ts +0 -2
  75. package/dist/utils/templates.js +0 -5
  76. package/dist/utils/templates.test.d.ts +0 -1
  77. package/dist/utils/templates.test.js +0 -38
  78. package/dist/utils/version-check.d.ts +0 -7
  79. package/dist/utils/version-check.js +0 -139
  80. package/dist/utils/version-check.test.d.ts +0 -1
  81. package/dist/utils/version-check.test.js +0 -189
@@ -1,113 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- vi.mock('../utils/auth.js', () => ({
3
- isAuthenticated: vi.fn(),
4
- }));
5
- const mockClient = vi.hoisted(() => ({
6
- coins: {
7
- getBalance: vi.fn(),
8
- getLeaderboard: vi.fn(),
9
- },
10
- }));
11
- vi.mock('../utils/api.js', () => ({
12
- getClient: vi.fn().mockResolvedValue(mockClient),
13
- }));
14
- import { isAuthenticated } from '../utils/auth.js';
15
- import { coinsCommand } from './coins.js';
16
- beforeEach(() => {
17
- vi.clearAllMocks();
18
- vi.spyOn(console, 'log').mockImplementation(() => { });
19
- vi.spyOn(console, 'error').mockImplementation(() => { });
20
- vi.spyOn(process, 'exit').mockImplementation((code) => {
21
- throw new Error(`process.exit(${code})`);
22
- });
23
- });
24
- describe('coinsCommand', () => {
25
- describe('balance (default)', () => {
26
- it('exits with error when not authenticated', async () => {
27
- ;
28
- isAuthenticated.mockResolvedValue(false);
29
- await expect(coinsCommand({})).rejects.toThrow('process.exit(1)');
30
- expect(console.error).toHaveBeenCalled();
31
- });
32
- it('displays coin balance on success', async () => {
33
- ;
34
- isAuthenticated.mockResolvedValue(true);
35
- mockClient.coins.getBalance.mockResolvedValue({ coins: 5, period: '28d' });
36
- await coinsCommand({});
37
- expect(mockClient.coins.getBalance).toHaveBeenCalled();
38
- const logCalls = console.log.mock.calls.map((c) => String(c[0]));
39
- expect(logCalls.some((msg) => msg.includes('5'))).toBe(true);
40
- });
41
- it('displays singular coin text for 1 coin', async () => {
42
- ;
43
- isAuthenticated.mockResolvedValue(true);
44
- mockClient.coins.getBalance.mockResolvedValue({ coins: 1, period: '28d' });
45
- await coinsCommand({});
46
- const logCalls = console.log.mock.calls.map((c) => String(c[0]));
47
- expect(logCalls.some((msg) => msg.includes('coin') && !msg.includes('coins'))).toBe(true);
48
- });
49
- it('displays plural coin text for multiple coins', async () => {
50
- ;
51
- isAuthenticated.mockResolvedValue(true);
52
- mockClient.coins.getBalance.mockResolvedValue({ coins: 3, period: '28d' });
53
- await coinsCommand({});
54
- const logCalls = console.log.mock.calls.map((c) => String(c[0]));
55
- expect(logCalls.some((msg) => msg.includes('coins'))).toBe(true);
56
- });
57
- it('exits with error when API returns failure', async () => {
58
- ;
59
- isAuthenticated.mockResolvedValue(true);
60
- mockClient.coins.getBalance.mockRejectedValue(new Error('Something went wrong'));
61
- await expect(coinsCommand({})).rejects.toThrow('process.exit(1)');
62
- const errorCalls = console.error.mock.calls.map((c) => String(c[0]));
63
- expect(errorCalls.some((msg) => msg.includes('Something went wrong'))).toBe(true);
64
- });
65
- });
66
- describe('leaderboard', () => {
67
- it('exits with error when not authenticated', async () => {
68
- ;
69
- isAuthenticated.mockResolvedValue(false);
70
- await expect(coinsCommand({ leaderboard: true })).rejects.toThrow('process.exit(1)');
71
- });
72
- it('displays leaderboard entries on success', async () => {
73
- ;
74
- isAuthenticated.mockResolvedValue(true);
75
- const entries = [
76
- { name: 'Alice', coins: 10 },
77
- { name: 'Bob', coins: 7 },
78
- { name: 'Charlie', coins: 3 },
79
- ];
80
- mockClient.coins.getLeaderboard.mockResolvedValue(entries);
81
- await coinsCommand({ leaderboard: true });
82
- expect(mockClient.coins.getLeaderboard).toHaveBeenCalled();
83
- const logCalls = console.log.mock.calls.map((c) => String(c[0]));
84
- expect(logCalls.some((msg) => msg.includes('Leaderboard'))).toBe(true);
85
- expect(logCalls.some((msg) => msg.includes('Alice'))).toBe(true);
86
- expect(logCalls.some((msg) => msg.includes('Bob'))).toBe(true);
87
- });
88
- it('shows message when no coins have been sent', async () => {
89
- ;
90
- isAuthenticated.mockResolvedValue(true);
91
- mockClient.coins.getLeaderboard.mockResolvedValue([]);
92
- await coinsCommand({ leaderboard: true });
93
- const logCalls = console.log.mock.calls.map((c) => String(c[0]));
94
- expect(logCalls.some((msg) => msg.includes('No coins'))).toBe(true);
95
- });
96
- it('exits with error when leaderboard API fails', async () => {
97
- ;
98
- isAuthenticated.mockResolvedValue(true);
99
- mockClient.coins.getLeaderboard.mockRejectedValue(new Error('Access denied'));
100
- await expect(coinsCommand({ leaderboard: true })).rejects.toThrow('process.exit(1)');
101
- const errorCalls = console.error.mock.calls.map((c) => String(c[0]));
102
- expect(errorCalls.some((msg) => msg.includes('Access denied'))).toBe(true);
103
- });
104
- it('calls getLeaderboard not getBalance', async () => {
105
- ;
106
- isAuthenticated.mockResolvedValue(true);
107
- mockClient.coins.getLeaderboard.mockResolvedValue([{ name: 'Test', coins: 1 }]);
108
- await coinsCommand({ leaderboard: true });
109
- expect(mockClient.coins.getLeaderboard).toHaveBeenCalled();
110
- expect(mockClient.coins.getBalance).not.toHaveBeenCalled();
111
- });
112
- });
113
- });
@@ -1,7 +0,0 @@
1
- interface FeedbackOptions {
2
- to?: string;
3
- coins?: string;
4
- description?: string;
5
- }
6
- export declare function feedbackCommand(options: FeedbackOptions): Promise<void>;
7
- export {};
@@ -1,90 +0,0 @@
1
- import chalk from 'chalk';
2
- import { input, select, editor } from '@inquirer/prompts';
3
- import { isAuthenticated } from '../utils/auth.js';
4
- import { getClient } from '../utils/api.js';
5
- const EDITOR_HINT = chalk.dim('(type \\\\e to open your editor)');
6
- async function promptDescription() {
7
- const value = await input({
8
- message: `Write your feedback (50–500 chars): ${EDITOR_HINT}`,
9
- });
10
- if (value === '\\e') {
11
- try {
12
- const editorValue = await editor({ message: 'Write your feedback:' });
13
- return validateDescription(editorValue);
14
- }
15
- catch {
16
- console.log(chalk.yellow('Editor failed to open. Falling back to inline input.'));
17
- return promptDescription();
18
- }
19
- }
20
- return validateDescription(value);
21
- }
22
- function validateDescription(value) {
23
- const trimmed = value.trim();
24
- if (trimmed.length < 50) {
25
- console.log(chalk.yellow(`Too short (${trimmed.length}/50 min). Please write more.`));
26
- return promptDescription();
27
- }
28
- if (trimmed.length > 500) {
29
- console.log(chalk.yellow(`Too long (${trimmed.length}/500 max). Please shorten it.`));
30
- return promptDescription();
31
- }
32
- return trimmed;
33
- }
34
- export async function feedbackCommand(options) {
35
- if (!(await isAuthenticated())) {
36
- console.error(chalk.red('Not authenticated. Run `npx @meltstudio/meltctl@latest login` first.'));
37
- process.exit(1);
38
- }
39
- const client = await getClient();
40
- let toUserId;
41
- let coins;
42
- let description;
43
- if (options.to && options.coins && options.description) {
44
- // Hidden flags for E2E testing
45
- toUserId = parseInt(options.to, 10);
46
- coins = parseInt(options.coins, 10);
47
- description = options.description;
48
- }
49
- else {
50
- console.log(chalk.bold.cyan('\n Send Feedback\n'));
51
- // Fetch recipients
52
- let recipients;
53
- try {
54
- recipients = await client.feedback.getRecipients();
55
- }
56
- catch {
57
- console.error(chalk.red('Failed to connect to the server.'));
58
- process.exit(1);
59
- }
60
- if (recipients.length === 0) {
61
- console.log(chalk.yellow('No recipients available.'));
62
- return;
63
- }
64
- const selectedRecipient = await select({
65
- message: 'Who would you like to recognize?',
66
- choices: recipients.map(r => ({
67
- name: `${r.firstName} ${r.lastName} (${r.email})`,
68
- value: r.id,
69
- })),
70
- });
71
- toUserId = selectedRecipient;
72
- coins = await select({
73
- message: 'How many coins?',
74
- choices: [
75
- { name: '1 coin', value: 1 },
76
- { name: '2 coins', value: 2 },
77
- { name: '3 coins', value: 3 },
78
- ],
79
- });
80
- description = await promptDescription();
81
- }
82
- try {
83
- await client.feedback.submit({ toUserId, coins, description });
84
- console.log(chalk.green(`\n ✓ Feedback sent! You gave ${coins} coin${coins > 1 ? 's' : ''}.\n`));
85
- }
86
- catch (error) {
87
- console.error(chalk.red(`\nFailed to send feedback: ${error instanceof Error ? error.message : 'Unknown error'}`));
88
- process.exit(1);
89
- }
90
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,177 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- vi.mock('../utils/auth.js', () => ({
3
- isAuthenticated: vi.fn(),
4
- }));
5
- const mockClient = vi.hoisted(() => ({
6
- feedback: {
7
- submit: vi.fn(),
8
- getRecipients: vi.fn(),
9
- },
10
- }));
11
- vi.mock('../utils/api.js', () => ({
12
- getClient: vi.fn().mockResolvedValue(mockClient),
13
- }));
14
- vi.mock('@inquirer/prompts', () => ({
15
- input: vi.fn(),
16
- select: vi.fn(),
17
- editor: vi.fn(),
18
- }));
19
- import { isAuthenticated } from '../utils/auth.js';
20
- import { input, select } from '@inquirer/prompts';
21
- import { feedbackCommand } from './feedback.js';
22
- beforeEach(() => {
23
- vi.clearAllMocks();
24
- vi.spyOn(console, 'log').mockImplementation(() => { });
25
- vi.spyOn(console, 'error').mockImplementation(() => { });
26
- vi.spyOn(process, 'exit').mockImplementation((code) => {
27
- throw new Error(`process.exit(${code})`);
28
- });
29
- });
30
- describe('feedbackCommand', () => {
31
- it('exits with error when not authenticated', async () => {
32
- ;
33
- isAuthenticated.mockResolvedValue(false);
34
- await expect(feedbackCommand({})).rejects.toThrow('process.exit(1)');
35
- expect(console.error).toHaveBeenCalled();
36
- });
37
- it('submits feedback with correct payload using option flags', async () => {
38
- ;
39
- isAuthenticated.mockResolvedValue(true);
40
- mockClient.feedback.submit.mockResolvedValue({});
41
- await feedbackCommand({
42
- to: '42',
43
- coins: '2',
44
- description: 'Great job on the feature implementation!',
45
- });
46
- expect(mockClient.feedback.submit).toHaveBeenCalledWith({
47
- toUserId: 42,
48
- coins: 2,
49
- description: 'Great job on the feature implementation!',
50
- });
51
- const logCalls = console.log.mock.calls.map((c) => String(c[0]));
52
- expect(logCalls.some((msg) => msg.includes('Feedback sent'))).toBe(true);
53
- });
54
- it('displays singular coin text when sending 1 coin', async () => {
55
- ;
56
- isAuthenticated.mockResolvedValue(true);
57
- mockClient.feedback.submit.mockResolvedValue({});
58
- await feedbackCommand({ to: '1', coins: '1', description: 'Nice work' });
59
- const logCalls = console.log.mock.calls.map((c) => String(c[0]));
60
- expect(logCalls.some((msg) => msg.includes('1 coin') && !msg.includes('1 coins'))).toBe(true);
61
- });
62
- it('displays plural coin text when sending multiple coins', async () => {
63
- ;
64
- isAuthenticated.mockResolvedValue(true);
65
- mockClient.feedback.submit.mockResolvedValue({});
66
- await feedbackCommand({ to: '1', coins: '3', description: 'Excellent work' });
67
- const logCalls = console.log.mock.calls.map((c) => String(c[0]));
68
- expect(logCalls.some((msg) => msg.includes('3 coins'))).toBe(true);
69
- });
70
- it('exits with error when API returns failure', async () => {
71
- ;
72
- isAuthenticated.mockResolvedValue(true);
73
- mockClient.feedback.submit.mockRejectedValue(new Error('Insufficient coins'));
74
- await expect(feedbackCommand({ to: '42', coins: '2', description: 'Good work' })).rejects.toThrow('process.exit(1)');
75
- const errorCalls = console.error.mock.calls.map((c) => String(c[0]));
76
- expect(errorCalls.some((msg) => msg.includes('Insufficient coins'))).toBe(true);
77
- });
78
- it('exits with error when API returns failure without error body', async () => {
79
- ;
80
- isAuthenticated.mockResolvedValue(true);
81
- mockClient.feedback.submit.mockRejectedValue(new Error('Internal Server Error'));
82
- await expect(feedbackCommand({ to: '42', coins: '2', description: 'Good work' })).rejects.toThrow('process.exit(1)');
83
- const errorCalls = console.error.mock.calls.map((c) => String(c[0]));
84
- expect(errorCalls.some((msg) => msg.includes('Internal Server Error'))).toBe(true);
85
- });
86
- it('sends correct numeric types in payload', async () => {
87
- ;
88
- isAuthenticated.mockResolvedValue(true);
89
- mockClient.feedback.submit.mockResolvedValue({});
90
- await feedbackCommand({ to: '7', coins: '3', description: 'Awesome' });
91
- expect(mockClient.feedback.submit).toHaveBeenCalledWith({
92
- toUserId: 7,
93
- coins: 3,
94
- description: 'Awesome',
95
- });
96
- });
97
- describe('interactive flow', () => {
98
- const mockRecipients = [
99
- { id: 1, firstName: 'Alice', lastName: 'Smith', email: 'alice@meltstudio.co' },
100
- { id: 2, firstName: 'Bob', lastName: 'Jones', email: 'bob@meltstudio.co' },
101
- ];
102
- function setupInteractiveMocks() {
103
- ;
104
- isAuthenticated.mockResolvedValue(true);
105
- }
106
- it('prompts for recipient, coins, and description in interactive mode', async () => {
107
- setupInteractiveMocks();
108
- mockClient.feedback.getRecipients.mockResolvedValue(mockRecipients);
109
- mockClient.feedback.submit.mockResolvedValue({});
110
- select.mockResolvedValueOnce(1).mockResolvedValueOnce(2);
111
- input.mockResolvedValueOnce('This is a great piece of feedback that is long enough to pass validation easily.');
112
- await feedbackCommand({});
113
- expect(mockClient.feedback.getRecipients).toHaveBeenCalled();
114
- expect(select).toHaveBeenCalledTimes(2);
115
- expect(input).toHaveBeenCalledTimes(1);
116
- expect(mockClient.feedback.submit).toHaveBeenCalledWith(expect.objectContaining({
117
- toUserId: 1,
118
- }));
119
- });
120
- it('shows no recipients message when list is empty', async () => {
121
- setupInteractiveMocks();
122
- mockClient.feedback.getRecipients.mockResolvedValue([]);
123
- await feedbackCommand({});
124
- const logCalls = console.log.mock.calls.map((c) => String(c[0]));
125
- expect(logCalls.some((msg) => msg.includes('No recipients'))).toBe(true);
126
- // Should not call submit
127
- expect(mockClient.feedback.submit).not.toHaveBeenCalled();
128
- });
129
- it('exits with error when recipients API fails', async () => {
130
- setupInteractiveMocks();
131
- mockClient.feedback.getRecipients.mockRejectedValue(new Error('Not allowed'));
132
- await expect(feedbackCommand({})).rejects.toThrow('process.exit(1)');
133
- const errorCalls = console.error.mock.calls.map((c) => String(c[0]));
134
- expect(errorCalls.some((msg) => msg.includes('Failed to connect'))).toBe(true);
135
- });
136
- it('exits with error when recipients fetch throws network error', async () => {
137
- setupInteractiveMocks();
138
- mockClient.feedback.getRecipients.mockRejectedValue(new Error('Network error'));
139
- await expect(feedbackCommand({})).rejects.toThrow('process.exit(1)');
140
- const errorCalls = console.error.mock.calls.map((c) => String(c[0]));
141
- expect(errorCalls.some((msg) => msg.includes('Failed to connect'))).toBe(true);
142
- });
143
- it('re-prompts when description is too short', async () => {
144
- setupInteractiveMocks();
145
- mockClient.feedback.getRecipients.mockResolvedValue(mockRecipients);
146
- mockClient.feedback.submit.mockResolvedValue({});
147
- select.mockResolvedValueOnce(2).mockResolvedValueOnce(3);
148
- input
149
- .mockResolvedValueOnce('Too short')
150
- .mockResolvedValueOnce('This description is now long enough to pass the fifty character minimum validation check.');
151
- await feedbackCommand({});
152
- expect(input).toHaveBeenCalledTimes(2);
153
- const logCalls = console.log.mock.calls.map((c) => String(c[0]));
154
- expect(logCalls.some((msg) => msg.includes('Too short'))).toBe(true);
155
- });
156
- it('re-prompts when description is too long', async () => {
157
- setupInteractiveMocks();
158
- mockClient.feedback.getRecipients.mockResolvedValue(mockRecipients);
159
- mockClient.feedback.submit.mockResolvedValue({});
160
- select.mockResolvedValueOnce(1).mockResolvedValueOnce(1);
161
- input
162
- .mockResolvedValueOnce('x'.repeat(501))
163
- .mockResolvedValueOnce('This description is now exactly the right length to pass all validation checks successfully.');
164
- await feedbackCommand({});
165
- expect(input).toHaveBeenCalledTimes(2);
166
- const logCalls = console.log.mock.calls.map((c) => String(c[0]));
167
- expect(logCalls.some((msg) => msg.includes('Too long'))).toBe(true);
168
- });
169
- it('falls back to recipients error statusText when no error body', async () => {
170
- setupInteractiveMocks();
171
- mockClient.feedback.getRecipients.mockRejectedValue(new Error('Service Unavailable'));
172
- await expect(feedbackCommand({})).rejects.toThrow('process.exit(1)');
173
- const errorCalls = console.error.mock.calls.map((c) => String(c[0]));
174
- expect(errorCalls.some((msg) => msg.includes('Failed to connect'))).toBe(true);
175
- });
176
- });
177
- });
@@ -1,8 +0,0 @@
1
- interface InitOptions {
2
- force?: boolean;
3
- claude?: boolean;
4
- cursor?: boolean;
5
- opencode?: boolean;
6
- }
7
- export declare function initCommand(options: InitOptions): Promise<void>;
8
- export {};