@meltstudio/meltctl 4.35.0 → 4.36.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.
@@ -1,13 +1,21 @@
1
1
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
2
  vi.mock('../utils/auth.js', () => ({
3
3
  isAuthenticated: vi.fn(),
4
- authenticatedFetch: vi.fn(),
4
+ }));
5
+ const mockClient = vi.hoisted(() => ({
6
+ standups: {
7
+ getStatus: vi.fn(),
8
+ submit: vi.fn(),
9
+ },
10
+ }));
11
+ vi.mock('../utils/api.js', () => ({
12
+ getClient: vi.fn().mockResolvedValue(mockClient),
5
13
  }));
6
14
  vi.mock('@inquirer/prompts', () => ({
7
15
  input: vi.fn(),
8
16
  editor: vi.fn(),
9
17
  }));
10
- import { isAuthenticated, authenticatedFetch } from '../utils/auth.js';
18
+ import { isAuthenticated } from '../utils/auth.js';
11
19
  import { input, editor } from '@inquirer/prompts';
12
20
  import { standupCommand } from './standup.js';
13
21
  beforeEach(() => {
@@ -28,47 +36,33 @@ describe('standupCommand', () => {
28
36
  it('returns early when standup already submitted today', async () => {
29
37
  ;
30
38
  isAuthenticated.mockResolvedValue(true);
31
- const statusResponse = {
32
- ok: true,
33
- json: vi.fn().mockResolvedValue({
34
- yesterday: 'Did stuff',
35
- today: 'More stuff',
36
- blockers: null,
37
- }),
38
- };
39
- authenticatedFetch.mockResolvedValue(statusResponse);
39
+ mockClient.standups.getStatus.mockResolvedValue({
40
+ yesterday: 'Did stuff',
41
+ today: 'More stuff',
42
+ blockers: null,
43
+ });
40
44
  await standupCommand({});
41
- expect(authenticatedFetch).toHaveBeenCalledWith('/standups/status');
42
- // Should not call POST /standups
43
- expect(authenticatedFetch).toHaveBeenCalledTimes(1);
45
+ expect(mockClient.standups.getStatus).toHaveBeenCalled();
46
+ // Should not call submit
47
+ expect(mockClient.standups.submit).not.toHaveBeenCalled();
44
48
  const logCalls = console.log.mock.calls.map((c) => String(c[0]));
45
49
  expect(logCalls.some((msg) => msg.includes('already submitted'))).toBe(true);
46
50
  });
47
51
  it('submits standup with correct payload using option flags', async () => {
48
52
  ;
49
53
  isAuthenticated.mockResolvedValue(true);
50
- // Status check returns not found (no existing standup)
51
- const statusResponse = { ok: false };
52
- const submitResponse = {
53
- ok: true,
54
- json: vi.fn().mockResolvedValue({}),
55
- };
56
- authenticatedFetch
57
- .mockResolvedValueOnce(statusResponse)
58
- .mockResolvedValueOnce(submitResponse);
54
+ // Status check returns null (no existing standup)
55
+ mockClient.standups.getStatus.mockResolvedValue(null);
56
+ mockClient.standups.submit.mockResolvedValue({});
59
57
  await standupCommand({
60
58
  yesterday: 'Fixed bugs',
61
59
  today: 'Write tests',
62
60
  blockers: 'None',
63
61
  });
64
- expect(authenticatedFetch).toHaveBeenCalledWith('/standups', {
65
- method: 'POST',
66
- headers: { 'Content-Type': 'application/json' },
67
- body: JSON.stringify({
68
- yesterday: 'Fixed bugs',
69
- today: 'Write tests',
70
- blockers: 'None',
71
- }),
62
+ expect(mockClient.standups.submit).toHaveBeenCalledWith({
63
+ yesterday: 'Fixed bugs',
64
+ today: 'Write tests',
65
+ blockers: 'None',
72
66
  });
73
67
  const logCalls = console.log.mock.calls.map((c) => String(c[0]));
74
68
  expect(logCalls.some((msg) => msg.includes('Standup submitted'))).toBe(true);
@@ -76,58 +70,45 @@ describe('standupCommand', () => {
76
70
  it('submits standup without blockers when empty string', async () => {
77
71
  ;
78
72
  isAuthenticated.mockResolvedValue(true);
79
- const statusResponse = { ok: false };
80
- const submitResponse = { ok: true, json: vi.fn().mockResolvedValue({}) };
81
- authenticatedFetch
82
- .mockResolvedValueOnce(statusResponse)
83
- .mockResolvedValueOnce(submitResponse);
73
+ mockClient.standups.getStatus.mockResolvedValue(null);
74
+ mockClient.standups.submit.mockResolvedValue({});
84
75
  await standupCommand({
85
76
  yesterday: 'Did things',
86
77
  today: 'More things',
87
78
  });
88
- const call = authenticatedFetch.mock.calls[1];
89
- const body = JSON.parse(call[1].body);
90
- expect(body.blockers).toBeUndefined();
79
+ expect(mockClient.standups.submit).toHaveBeenCalledWith({
80
+ yesterday: 'Did things',
81
+ today: 'More things',
82
+ blockers: undefined,
83
+ });
91
84
  });
92
85
  it('exits with error when API returns failure', async () => {
93
86
  ;
94
87
  isAuthenticated.mockResolvedValue(true);
95
- const statusResponse = { ok: false };
96
- const submitResponse = {
97
- ok: false,
98
- statusText: 'Bad Request',
99
- json: vi.fn().mockResolvedValue({ error: 'Invalid standup' }),
100
- };
101
- authenticatedFetch
102
- .mockResolvedValueOnce(statusResponse)
103
- .mockResolvedValueOnce(submitResponse);
88
+ mockClient.standups.getStatus.mockResolvedValue(null);
89
+ mockClient.standups.submit.mockRejectedValue(new Error('Invalid standup'));
104
90
  await expect(standupCommand({ yesterday: 'x', today: 'y' })).rejects.toThrow('process.exit(1)');
105
91
  expect(console.error).toHaveBeenCalled();
106
92
  });
107
93
  it('continues to submission when status check throws', async () => {
108
94
  ;
109
95
  isAuthenticated.mockResolvedValue(true);
110
- authenticatedFetch
111
- .mockRejectedValueOnce(new Error('Network error'))
112
- .mockResolvedValueOnce({ ok: true, json: vi.fn().mockResolvedValue({}) });
96
+ mockClient.standups.getStatus.mockRejectedValue(new Error('Network error'));
97
+ mockClient.standups.submit.mockResolvedValue({});
113
98
  await standupCommand({ yesterday: 'a', today: 'b' });
114
- expect(authenticatedFetch).toHaveBeenCalledTimes(2);
115
- expect(authenticatedFetch).toHaveBeenCalledWith('/standups', expect.objectContaining({
116
- method: 'POST',
99
+ expect(mockClient.standups.submit).toHaveBeenCalledWith(expect.objectContaining({
100
+ yesterday: 'a',
101
+ today: 'b',
117
102
  }));
118
103
  });
119
104
  it('displays existing standup with blockers', async () => {
120
105
  ;
121
106
  isAuthenticated.mockResolvedValue(true);
122
- const statusResponse = {
123
- ok: true,
124
- json: vi.fn().mockResolvedValue({
125
- yesterday: 'Bug fixes',
126
- today: 'Feature work',
127
- blockers: 'Waiting on review',
128
- }),
129
- };
130
- authenticatedFetch.mockResolvedValue(statusResponse);
107
+ mockClient.standups.getStatus.mockResolvedValue({
108
+ yesterday: 'Bug fixes',
109
+ today: 'Feature work',
110
+ blockers: 'Waiting on review',
111
+ });
131
112
  await standupCommand({});
132
113
  const logCalls = console.log.mock.calls.map((c) => String(c[0]));
133
114
  expect(logCalls.some((msg) => msg.includes('already submitted'))).toBe(true);
@@ -136,44 +117,41 @@ describe('standupCommand', () => {
136
117
  function setupInteractiveAuth() {
137
118
  ;
138
119
  isAuthenticated.mockResolvedValue(true);
139
- authenticatedFetch.mockResolvedValueOnce({ ok: false });
120
+ // Status check returns null (no existing standup)
121
+ mockClient.standups.getStatus.mockResolvedValue(null);
140
122
  }
141
123
  it('prompts for yesterday, today, and blockers in interactive mode', async () => {
142
124
  setupInteractiveAuth();
143
- const submitResponse = { ok: true, json: vi.fn().mockResolvedValue({}) };
144
- authenticatedFetch.mockResolvedValueOnce(submitResponse);
125
+ mockClient.standups.submit.mockResolvedValue({});
145
126
  input
146
127
  .mockResolvedValueOnce('Worked on feature X')
147
128
  .mockResolvedValueOnce('Working on feature Y')
148
129
  .mockResolvedValueOnce('No blockers');
149
130
  await standupCommand({});
150
131
  expect(input).toHaveBeenCalledTimes(3);
151
- expect(authenticatedFetch).toHaveBeenCalledWith('/standups', expect.objectContaining({
152
- method: 'POST',
153
- body: JSON.stringify({
154
- yesterday: 'Worked on feature X',
155
- today: 'Working on feature Y',
156
- blockers: 'No blockers',
157
- }),
158
- }));
132
+ expect(mockClient.standups.submit).toHaveBeenCalledWith({
133
+ yesterday: 'Worked on feature X',
134
+ today: 'Working on feature Y',
135
+ blockers: 'No blockers',
136
+ });
159
137
  });
160
138
  it('submits without blockers when left empty in interactive mode', async () => {
161
139
  setupInteractiveAuth();
162
- const submitResponse = { ok: true, json: vi.fn().mockResolvedValue({}) };
163
- authenticatedFetch.mockResolvedValueOnce(submitResponse);
140
+ mockClient.standups.submit.mockResolvedValue({});
164
141
  input
165
142
  .mockResolvedValueOnce('Did code review')
166
143
  .mockResolvedValueOnce('Deploy to staging')
167
144
  .mockResolvedValueOnce('');
168
145
  await standupCommand({});
169
- const call = authenticatedFetch.mock.calls[1];
170
- const body = JSON.parse(call[1].body);
171
- expect(body.blockers).toBeUndefined();
146
+ expect(mockClient.standups.submit).toHaveBeenCalledWith({
147
+ yesterday: 'Did code review',
148
+ today: 'Deploy to staging',
149
+ blockers: undefined,
150
+ });
172
151
  });
173
152
  it('re-prompts when required field is empty', async () => {
174
153
  setupInteractiveAuth();
175
- const submitResponse = { ok: true, json: vi.fn().mockResolvedValue({}) };
176
- authenticatedFetch.mockResolvedValueOnce(submitResponse);
154
+ mockClient.standups.submit.mockResolvedValue({});
177
155
  input
178
156
  .mockResolvedValueOnce(' ') // empty yesterday -> re-prompt
179
157
  .mockResolvedValueOnce('Fixed a bug') // valid yesterday
@@ -186,8 +164,7 @@ describe('standupCommand', () => {
186
164
  });
187
165
  it('opens editor when user types \\e', async () => {
188
166
  setupInteractiveAuth();
189
- const submitResponse = { ok: true, json: vi.fn().mockResolvedValue({}) };
190
- authenticatedFetch.mockResolvedValueOnce(submitResponse);
167
+ mockClient.standups.submit.mockResolvedValue({});
191
168
  input
192
169
  .mockResolvedValueOnce('\\e') // trigger editor for yesterday
193
170
  .mockResolvedValueOnce('Writing docs') // today
@@ -196,14 +173,13 @@ describe('standupCommand', () => {
196
173
  editor.mockResolvedValueOnce('Detailed yesterday notes from editor');
197
174
  await standupCommand({});
198
175
  expect(editor).toHaveBeenCalledTimes(1);
199
- const call = authenticatedFetch.mock.calls[1];
200
- const body = JSON.parse(call[1].body);
201
- expect(body.yesterday).toBe('Detailed yesterday notes from editor');
176
+ expect(mockClient.standups.submit).toHaveBeenCalledWith(expect.objectContaining({
177
+ yesterday: 'Detailed yesterday notes from editor',
178
+ }));
202
179
  });
203
180
  it('falls back to inline input when editor fails', async () => {
204
181
  setupInteractiveAuth();
205
- const submitResponse = { ok: true, json: vi.fn().mockResolvedValue({}) };
206
- authenticatedFetch.mockResolvedValueOnce(submitResponse);
182
+ mockClient.standups.submit.mockResolvedValue({});
207
183
  input
208
184
  .mockResolvedValueOnce('\\e') // trigger editor for yesterday
209
185
  .mockResolvedValueOnce('Fallback input') // fallback after editor fails
@@ -218,12 +194,7 @@ describe('standupCommand', () => {
218
194
  });
219
195
  it('exits with error when API fails in interactive mode', async () => {
220
196
  setupInteractiveAuth();
221
- const submitResponse = {
222
- ok: false,
223
- statusText: 'Unprocessable Entity',
224
- json: vi.fn().mockResolvedValue({ error: 'Missing fields' }),
225
- };
226
- authenticatedFetch.mockResolvedValueOnce(submitResponse);
197
+ mockClient.standups.submit.mockRejectedValue(new Error('Missing fields'));
227
198
  input
228
199
  .mockResolvedValueOnce('Yesterday work')
229
200
  .mockResolvedValueOnce('Today work')
@@ -234,12 +205,7 @@ describe('standupCommand', () => {
234
205
  });
235
206
  it('falls back to statusText when API error has no error body', async () => {
236
207
  setupInteractiveAuth();
237
- const submitResponse = {
238
- ok: false,
239
- statusText: 'Bad Gateway',
240
- json: vi.fn().mockResolvedValue({}),
241
- };
242
- authenticatedFetch.mockResolvedValueOnce(submitResponse);
208
+ mockClient.standups.submit.mockRejectedValue(new Error('Bad Gateway'));
243
209
  input
244
210
  .mockResolvedValueOnce('Yesterday')
245
211
  .mockResolvedValueOnce('Today')
@@ -1,6 +1,7 @@
1
1
  import { readFileSync } from 'fs';
2
2
  import { join, dirname } from 'path';
3
3
  import { fileURLToPath } from 'url';
4
+ import { createMeltClient } from '@meltstudio/meltctl-sdk';
4
5
  import { getStoredAuth, API_BASE } from './auth.js';
5
6
  import { getGitRepository, getGitBranch, getProjectName } from './git.js';
6
7
  import { debugLog } from './debug.js';
@@ -27,15 +28,11 @@ export async function trackCommand(command, success, errorMessage) {
27
28
  return;
28
29
  }
29
30
  const repo = getGitRepository();
31
+ const client = createMeltClient({ baseUrl: API_BASE, token: auth.token });
30
32
  const controller = new AbortController();
31
33
  const timeout = setTimeout(() => controller.abort(), 3000);
32
- const res = await fetch(`${API_BASE}/events`, {
33
- method: 'POST',
34
- headers: {
35
- Authorization: `Bearer ${auth.token}`,
36
- 'Content-Type': 'application/json',
37
- },
38
- body: JSON.stringify({
34
+ try {
35
+ await client.events.submit({
39
36
  command,
40
37
  project: getProjectName(),
41
38
  repository: repo?.slug ?? null,
@@ -43,20 +40,13 @@ export async function trackCommand(command, success, errorMessage) {
43
40
  version: getVersion(),
44
41
  success,
45
42
  errorMessage: errorMessage?.slice(0, 500) ?? null,
46
- }),
47
- signal: controller.signal,
48
- }).catch(e => {
49
- debugLog(`Analytics fetch failed: ${e instanceof Error ? e.message : e}`);
50
- return null;
51
- });
52
- clearTimeout(timeout);
53
- if (res && !res.ok) {
54
- const body = await res.text().catch(() => '');
55
- debugLog(`Analytics API error ${res.status}: ${body}`);
56
- }
57
- else if (res) {
43
+ });
58
44
  debugLog(`Analytics event sent for "${command}"`);
59
45
  }
46
+ catch (e) {
47
+ debugLog(`Analytics error: ${e instanceof Error ? e.message : e}`);
48
+ }
49
+ clearTimeout(timeout);
60
50
  }
61
51
  catch (e) {
62
52
  debugLog(`Analytics error: ${e instanceof Error ? e.message : e}`);
@@ -1,2 +1,3 @@
1
+ import { type MeltClient } from '@meltstudio/meltctl-sdk';
1
2
  export declare function getToken(): Promise<string>;
2
- export declare function tokenFetch(token: string, urlPath: string, options?: RequestInit): Promise<Response>;
3
+ export declare function getClient(): Promise<MeltClient>;
package/dist/utils/api.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import chalk from 'chalk';
2
+ import { createMeltClient } from '@meltstudio/meltctl-sdk';
2
3
  import { getStoredAuth, API_BASE } from './auth.js';
3
4
  export async function getToken() {
4
5
  const envToken = process.env['MELTCTL_TOKEN'];
@@ -16,16 +17,7 @@ export async function getToken() {
16
17
  }
17
18
  return auth.token;
18
19
  }
19
- export async function tokenFetch(token, urlPath, options = {}) {
20
- const response = await fetch(`${API_BASE}${urlPath}`, {
21
- ...options,
22
- headers: {
23
- Authorization: `Bearer ${token}`,
24
- ...options.headers,
25
- },
26
- });
27
- if (response.status === 401) {
28
- throw new Error('Authentication failed. Run `npx @meltstudio/meltctl@latest login` or check your MELTCTL_TOKEN.');
29
- }
30
- return response;
20
+ export async function getClient() {
21
+ const token = await getToken();
22
+ return createMeltClient({ baseUrl: API_BASE, token });
31
23
  }
@@ -3,8 +3,12 @@ vi.mock('./auth.js', () => ({
3
3
  getStoredAuth: vi.fn(),
4
4
  API_BASE: 'https://test-api.example.com',
5
5
  }));
6
- import { getStoredAuth, API_BASE } from './auth.js';
7
- import { getToken, tokenFetch } from './api.js';
6
+ vi.mock('@meltstudio/meltctl-sdk', () => ({
7
+ createMeltClient: vi.fn().mockReturnValue({ mock: true }),
8
+ }));
9
+ import { getStoredAuth } from './auth.js';
10
+ import { createMeltClient } from '@meltstudio/meltctl-sdk';
11
+ import { getToken, getClient } from './api.js';
8
12
  beforeEach(() => {
9
13
  vi.clearAllMocks();
10
14
  vi.spyOn(console, 'error').mockImplementation(() => { });
@@ -47,50 +51,26 @@ describe('getToken', () => {
47
51
  expect(console.error).toHaveBeenCalled();
48
52
  });
49
53
  });
50
- describe('tokenFetch', () => {
51
- it('makes request with bearer token', async () => {
52
- const mockResponse = { status: 200, ok: true };
53
- const fetchMock = vi.fn().mockResolvedValue(mockResponse);
54
- vi.stubGlobal('fetch', fetchMock);
55
- const result = await tokenFetch('my-token', '/audits');
56
- expect(result).toBe(mockResponse);
57
- expect(fetchMock).toHaveBeenCalledWith(`${API_BASE}/audits`, expect.objectContaining({
58
- headers: expect.objectContaining({
59
- Authorization: 'Bearer my-token',
60
- }),
61
- }));
62
- vi.unstubAllGlobals();
63
- });
64
- it('merges custom options and headers', async () => {
65
- const fetchMock = vi.fn().mockResolvedValue({ status: 200, ok: true });
66
- vi.stubGlobal('fetch', fetchMock);
67
- await tokenFetch('tok', '/test', {
68
- method: 'POST',
69
- headers: { 'Content-Type': 'application/json' },
70
- body: '{}',
54
+ describe('getClient', () => {
55
+ it('creates SDK client with token and base URL', async () => {
56
+ ;
57
+ getStoredAuth.mockResolvedValue({
58
+ token: 'my-token',
59
+ email: 'dev@meltstudio.co',
60
+ expiresAt: '2099-12-31T00:00:00Z',
61
+ });
62
+ await getClient();
63
+ expect(createMeltClient).toHaveBeenCalledWith({
64
+ baseUrl: 'https://test-api.example.com',
65
+ token: 'my-token',
71
66
  });
72
- expect(fetchMock).toHaveBeenCalledWith(`${API_BASE}/test`, expect.objectContaining({
73
- method: 'POST',
74
- body: '{}',
75
- headers: expect.objectContaining({
76
- Authorization: 'Bearer tok',
77
- 'Content-Type': 'application/json',
78
- }),
79
- }));
80
- vi.unstubAllGlobals();
81
- });
82
- it('throws when API returns 401', async () => {
83
- const fetchMock = vi.fn().mockResolvedValue({ status: 401, ok: false });
84
- vi.stubGlobal('fetch', fetchMock);
85
- await expect(tokenFetch('bad-token', '/test')).rejects.toThrow('Authentication failed');
86
- vi.unstubAllGlobals();
87
67
  });
88
- it('returns response for non-401 errors without throwing', async () => {
89
- const mockResponse = { status: 500, ok: false, statusText: 'Internal Server Error' };
90
- const fetchMock = vi.fn().mockResolvedValue(mockResponse);
91
- vi.stubGlobal('fetch', fetchMock);
92
- const result = await tokenFetch('tok', '/test');
93
- expect(result.status).toBe(500);
94
- vi.unstubAllGlobals();
68
+ it('uses env token when MELTCTL_TOKEN is set', async () => {
69
+ process.env['MELTCTL_TOKEN'] = 'env-token';
70
+ await getClient();
71
+ expect(createMeltClient).toHaveBeenCalledWith({
72
+ baseUrl: 'https://test-api.example.com',
73
+ token: 'env-token',
74
+ });
95
75
  });
96
76
  });
@@ -1,4 +1,2 @@
1
- export interface TemplateFiles {
2
- [key: string]: string;
3
- }
4
- export declare function fetchTemplates(): Promise<TemplateFiles>;
1
+ export type { TemplateFiles } from '@meltstudio/meltctl-sdk';
2
+ export declare function fetchTemplates(): Promise<Record<string, string>>;
@@ -1,9 +1,5 @@
1
- import { authenticatedFetch } from './auth.js';
1
+ import { getClient } from './api.js';
2
2
  export async function fetchTemplates() {
3
- const response = await authenticatedFetch('/templates');
4
- if (!response.ok) {
5
- throw new Error(`Failed to fetch templates: ${response.statusText}`);
6
- }
7
- const data = (await response.json());
8
- return data.files;
3
+ const client = await getClient();
4
+ return client.templates.fetch();
9
5
  }
@@ -1,8 +1,12 @@
1
1
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- vi.mock('./auth.js', () => ({
3
- authenticatedFetch: vi.fn(),
2
+ const mockClient = vi.hoisted(() => ({
3
+ templates: {
4
+ fetch: vi.fn(),
5
+ },
6
+ }));
7
+ vi.mock('./api.js', () => ({
8
+ getClient: vi.fn().mockResolvedValue(mockClient),
4
9
  }));
5
- import { authenticatedFetch } from './auth.js';
6
10
  import { fetchTemplates } from './templates.js';
7
11
  beforeEach(() => {
8
12
  vi.clearAllMocks();
@@ -13,38 +17,22 @@ describe('fetchTemplates', () => {
13
17
  'AGENTS.md': '# Agents content',
14
18
  '.claude/skills/setup.md': '# Setup skill',
15
19
  };
16
- authenticatedFetch.mockResolvedValue({
17
- ok: true,
18
- json: vi.fn().mockResolvedValue({ files: mockFiles }),
19
- });
20
+ mockClient.templates.fetch.mockResolvedValue(mockFiles);
20
21
  const result = await fetchTemplates();
21
- expect(authenticatedFetch).toHaveBeenCalledWith('/templates');
22
+ expect(mockClient.templates.fetch).toHaveBeenCalled();
22
23
  expect(result).toEqual(mockFiles);
23
24
  });
24
25
  it('throws error when API returns failure', async () => {
25
- ;
26
- authenticatedFetch.mockResolvedValue({
27
- ok: false,
28
- statusText: 'Unauthorized',
29
- });
26
+ mockClient.templates.fetch.mockRejectedValue(new Error('Failed to fetch templates: Unauthorized'));
30
27
  await expect(fetchTemplates()).rejects.toThrow('Failed to fetch templates: Unauthorized');
31
28
  });
32
29
  it('throws error when API returns 500', async () => {
33
- ;
34
- authenticatedFetch.mockResolvedValue({
35
- ok: false,
36
- statusText: 'Internal Server Error',
37
- });
30
+ mockClient.templates.fetch.mockRejectedValue(new Error('Failed to fetch templates: Internal Server Error'));
38
31
  await expect(fetchTemplates()).rejects.toThrow('Failed to fetch templates: Internal Server Error');
39
32
  });
40
- it('calls authenticatedFetch with /templates path', async () => {
41
- ;
42
- authenticatedFetch.mockResolvedValue({
43
- ok: true,
44
- json: vi.fn().mockResolvedValue({ files: {} }),
45
- });
33
+ it('calls client.templates.fetch', async () => {
34
+ mockClient.templates.fetch.mockResolvedValue({});
46
35
  await fetchTemplates();
47
- expect(authenticatedFetch).toHaveBeenCalledTimes(1);
48
- expect(authenticatedFetch).toHaveBeenCalledWith('/templates');
36
+ expect(mockClient.templates.fetch).toHaveBeenCalledTimes(1);
49
37
  });
50
38
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meltstudio/meltctl",
3
- "version": "4.35.0",
3
+ "version": "4.36.0",
4
4
  "description": "AI-first development tools for teams - set up AGENTS.md, Claude Code, Cursor, and OpenCode standards",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -47,6 +47,7 @@
47
47
  "author": "Melt Studio",
48
48
  "license": "UNLICENSED",
49
49
  "dependencies": {
50
+ "@meltstudio/meltctl-sdk": "*",
50
51
  "@commander-js/extra-typings": "^12.1.0",
51
52
  "@inquirer/prompts": "^8.2.1",
52
53
  "chalk": "^5.4.1",
@@ -60,7 +61,7 @@
60
61
  "@typescript-eslint/eslint-plugin": "^8.19.0",
61
62
  "@typescript-eslint/parser": "^8.19.0",
62
63
  "eslint": "^9.17.0",
63
- "eslint-config-prettier": "^9.1.0",
64
+ "eslint-config-prettier": "^10.1.8",
64
65
  "eslint-plugin-prettier": "^5.2.1",
65
66
  "prettier": "^3.4.2",
66
67
  "tsx": "^4.19.5",