@orchagent/cli 0.3.31 → 0.3.33

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.
@@ -53,100 +53,135 @@ function registerUpdateCommand(program) {
53
53
  process.stdout.write(`Agent "${agentRef}" is not installed.\n`);
54
54
  return;
55
55
  }
56
- process.stdout.write(`Checking ${toCheck.length} installed agent(s) for updates...\n\n`);
56
+ // Group installed entries by agent name to avoid duplicates
57
+ // (same agent installed to multiple formats shows once)
58
+ const grouped = new Map();
59
+ for (const item of toCheck) {
60
+ const existing = grouped.get(item.agent);
61
+ if (existing) {
62
+ existing.push(item);
63
+ }
64
+ else {
65
+ grouped.set(item.agent, [item]);
66
+ }
67
+ }
68
+ process.stdout.write(`Checking ${grouped.size} installed agent(s) for updates...\n\n`);
57
69
  let updatesAvailable = 0;
58
70
  let updatesApplied = 0;
59
71
  let skippedModified = 0;
60
72
  let skippedMissing = 0;
61
- for (const item of toCheck) {
62
- // Check if file was modified locally or is missing
63
- const fileStatus = await (0, installed_1.checkModified)(item);
64
- // Fetch latest version
65
- const latest = await fetchLatestAgent(resolved, item.agent);
73
+ for (const [agentName, entries] of grouped) {
74
+ // Check file status for all entries in this group
75
+ const fileStatuses = [];
76
+ for (const item of entries) {
77
+ fileStatuses.push({ item, status: await (0, installed_1.checkModified)(item) });
78
+ }
79
+ // Fetch latest version once per agent
80
+ const latest = await fetchLatestAgent(resolved, agentName);
66
81
  if (!latest) {
67
- process.stdout.write(` ${chalk_1.default.yellow('?')} ${item.agent} - could not fetch latest\n`);
82
+ process.stdout.write(` ${chalk_1.default.yellow('?')} ${agentName} - could not fetch latest\n`);
68
83
  continue;
69
84
  }
70
- const hasUpdate = latest.latestVersion !== item.version;
71
- if (!hasUpdate && !fileStatus.modified && !fileStatus.missing) {
72
- process.stdout.write(` ${chalk_1.default.green('✓')} ${item.agent}@${item.version} - up to date\n`);
85
+ // Use the version from the first entry (all entries for the same
86
+ // agent should share the same version after install/update)
87
+ const installedVersion = entries[0].version;
88
+ const hasUpdate = latest.latestVersion !== installedVersion;
89
+ const anyMissing = fileStatuses.some(f => f.status.missing);
90
+ const anyModified = fileStatuses.some(f => f.status.modified);
91
+ if (!hasUpdate && !anyModified && !anyMissing) {
92
+ const formatSuffix = entries.length > 1
93
+ ? ` ${chalk_1.default.dim(`(${entries.map(e => e.format).join(', ')})`)}`
94
+ : '';
95
+ process.stdout.write(` ${chalk_1.default.green('✓')} ${agentName}@${installedVersion} - up to date${formatSuffix}\n`);
73
96
  continue;
74
97
  }
75
- // Handle missing file without --force
76
- if (fileStatus.missing && !options.force) {
77
- process.stdout.write(` ${chalk_1.default.yellow('!')} ${item.agent} - file missing (use --force to reinstall)\n`);
98
+ // Handle missing files without --force
99
+ if (anyMissing && !hasUpdate && !options.force) {
100
+ const missingFormats = fileStatuses.filter(f => f.status.missing).map(f => f.item.format);
101
+ process.stdout.write(` ${chalk_1.default.yellow('!')} ${agentName} - file missing [${missingFormats.join(', ')}] (use --force to reinstall)\n`);
78
102
  skippedMissing++;
79
103
  continue;
80
104
  }
81
- // Handle modified file without --force
82
- if (fileStatus.modified && !options.force) {
83
- process.stdout.write(` ${chalk_1.default.yellow('!')} ${item.agent} - local modifications (use --force to overwrite)\n`);
105
+ // Handle modified files without --force
106
+ if (anyModified && !hasUpdate && !options.force) {
107
+ const modifiedFormats = fileStatuses.filter(f => f.status.modified).map(f => f.item.format);
108
+ process.stdout.write(` ${chalk_1.default.yellow('!')} ${agentName} - local modifications [${modifiedFormats.join(', ')}] (use --force to overwrite)\n`);
84
109
  skippedModified++;
85
110
  continue;
86
111
  }
87
- if (hasUpdate || fileStatus.missing) {
112
+ if (hasUpdate || anyMissing) {
88
113
  if (hasUpdate) {
89
114
  updatesAvailable++;
90
115
  }
91
- process.stdout.write(` ${chalk_1.default.blue('↑')} ${item.agent}@${item.version} → ${latest.latestVersion}`);
92
- if (fileStatus.modified) {
116
+ process.stdout.write(` ${chalk_1.default.blue('↑')} ${agentName}@${installedVersion} → ${latest.latestVersion}`);
117
+ if (anyModified) {
93
118
  process.stdout.write(` ${chalk_1.default.yellow('(modified)')}`);
94
119
  }
95
- if (fileStatus.missing) {
120
+ if (anyMissing) {
96
121
  process.stdout.write(` ${chalk_1.default.yellow('(reinstalling)')}`);
97
122
  }
98
123
  process.stdout.write('\n');
99
124
  if (options.check) {
100
125
  continue;
101
126
  }
102
- // Apply update
103
- const adapter = adapters_1.adapterRegistry.get(item.format);
104
- if (!adapter) {
105
- process.stderr.write(` Skipped: Unknown format "${item.format}"\n`);
106
- continue;
107
- }
108
- const checkResult = adapter.canConvert(latest.agent);
109
- if (!checkResult.canConvert) {
110
- process.stderr.write(` Skipped: ${checkResult.errors.join(', ')}\n`);
111
- continue;
112
- }
113
- const files = adapter.convert(latest.agent);
114
- if (files.length > 1) {
115
- process.stderr.write(` Skipped: Multi-file adapters not supported for update. Reinstall instead.\n`);
116
- continue;
117
- }
118
- for (const file of files) {
119
- // Use the original path from tracking
120
- const fullPath = item.path;
121
- try {
122
- const dir = path_1.default.dirname(fullPath);
123
- await promises_1.default.mkdir(dir, { recursive: true });
124
- // Special handling for AGENTS.md - append/replace instead of overwrite
125
- if (file.filename === 'AGENTS.md') {
126
- let existingContent = '';
127
- try {
128
- existingContent = await promises_1.default.readFile(fullPath, 'utf-8');
129
- }
130
- catch {
131
- // File doesn't exist, will create new
127
+ // Apply update to each format entry
128
+ for (const { item, status: fileStatus } of fileStatuses) {
129
+ // Skip unmodified and non-missing entries if no version update
130
+ if (!hasUpdate && !fileStatus.missing)
131
+ continue;
132
+ // Skip modified entries without --force
133
+ if (fileStatus.modified && !options.force) {
134
+ process.stderr.write(` Skipped ${item.format}: local modifications (use --force)\n`);
135
+ continue;
136
+ }
137
+ const adapter = adapters_1.adapterRegistry.get(item.format);
138
+ if (!adapter) {
139
+ process.stderr.write(` Skipped ${item.format}: unknown format\n`);
140
+ continue;
141
+ }
142
+ const checkResult = adapter.canConvert(latest.agent);
143
+ if (!checkResult.canConvert) {
144
+ process.stderr.write(` Skipped ${item.format}: ${checkResult.errors.join(', ')}\n`);
145
+ continue;
146
+ }
147
+ const files = adapter.convert(latest.agent);
148
+ if (files.length > 1) {
149
+ process.stderr.write(` Skipped ${item.format}: multi-file adapters not supported for update. Reinstall instead.\n`);
150
+ continue;
151
+ }
152
+ for (const file of files) {
153
+ // Use the original path from tracking
154
+ const fullPath = item.path;
155
+ try {
156
+ const dir = path_1.default.dirname(fullPath);
157
+ await promises_1.default.mkdir(dir, { recursive: true });
158
+ // Special handling for AGENTS.md - append/replace instead of overwrite
159
+ if (file.filename === 'AGENTS.md') {
160
+ let existingContent = '';
161
+ try {
162
+ existingContent = await promises_1.default.readFile(fullPath, 'utf-8');
163
+ }
164
+ catch {
165
+ // File doesn't exist, will create new
166
+ }
167
+ file.content = (0, agents_md_utils_1.mergeAgentsMdContent)(existingContent, file.content, item.agent);
132
168
  }
133
- file.content = (0, agents_md_utils_1.mergeAgentsMdContent)(existingContent, file.content, item.agent);
169
+ await promises_1.default.writeFile(fullPath, file.content);
170
+ // Update tracking
171
+ const updatedItem = {
172
+ ...item,
173
+ version: latest.latestVersion,
174
+ installedAt: new Date().toISOString(),
175
+ adapterVersion: adapter.version,
176
+ contentHash: (0, installed_1.computeHash)(file.content),
177
+ };
178
+ await (0, installed_1.trackInstall)(updatedItem);
179
+ process.stdout.write(` Updated: ${fullPath}\n`);
180
+ updatesApplied++;
181
+ }
182
+ catch (err) {
183
+ process.stderr.write(` Error (${item.format}): ${err.message}\n`);
134
184
  }
135
- await promises_1.default.writeFile(fullPath, file.content);
136
- // Update tracking
137
- const updatedItem = {
138
- ...item,
139
- version: latest.latestVersion,
140
- installedAt: new Date().toISOString(),
141
- adapterVersion: adapter.version,
142
- contentHash: (0, installed_1.computeHash)(file.content),
143
- };
144
- await (0, installed_1.trackInstall)(updatedItem);
145
- process.stdout.write(` Updated: ${fullPath}\n`);
146
- updatesApplied++;
147
- }
148
- catch (err) {
149
- process.stderr.write(` Error: ${err.message}\n`);
150
185
  }
151
186
  }
152
187
  }
package/dist/index.js CHANGED
File without changes
package/dist/lib/api.js CHANGED
@@ -48,6 +48,7 @@ exports.starAgent = starAgent;
48
48
  exports.unstarAgent = unstarAgent;
49
49
  exports.forkAgent = forkAgent;
50
50
  exports.searchAgents = searchAgents;
51
+ exports.searchMyAgents = searchMyAgents;
51
52
  exports.fetchLlmKeys = fetchLlmKeys;
52
53
  exports.downloadCodeBundle = downloadCodeBundle;
53
54
  exports.uploadCodeBundle = uploadCodeBundle;
@@ -233,8 +234,14 @@ async function updateOrg(config, payload) {
233
234
  headers: { 'Content-Type': 'application/json' },
234
235
  });
235
236
  }
236
- async function listPublicAgents(config) {
237
- return publicRequest(config, '/public/agents');
237
+ async function listPublicAgents(config, options) {
238
+ const params = new URLSearchParams();
239
+ if (options?.sort)
240
+ params.append('sort', options.sort);
241
+ if (options?.type)
242
+ params.append('type', options.type);
243
+ const queryStr = params.toString();
244
+ return publicRequest(config, `/public/agents${queryStr ? `?${queryStr}` : ''}`);
238
245
  }
239
246
  async function getPublicAgent(config, org, agent, version) {
240
247
  return publicRequest(config, `/public/agents/${org}/${agent}/${version}`);
@@ -266,9 +273,68 @@ async function searchAgents(config, query, options) {
266
273
  params.append('sort', options.sort);
267
274
  if (options?.tags?.length)
268
275
  params.append('tags', options.tags.join(','));
276
+ if (options?.type)
277
+ params.append('type', options.type);
269
278
  const queryStr = params.toString();
270
279
  return publicRequest(config, `/public/agents${queryStr ? `?${queryStr}` : ''}`);
271
280
  }
281
+ /**
282
+ * Search within the authenticated user's own agents (public and private).
283
+ * Uses the authenticated /agents endpoint with client-side filtering.
284
+ */
285
+ async function searchMyAgents(config, query, options) {
286
+ let agents = await listMyAgents(config);
287
+ // Deduplicate: keep only latest version per agent name
288
+ const latestByName = new Map();
289
+ for (const agent of agents) {
290
+ const existing = latestByName.get(agent.name);
291
+ if (!existing || new Date(agent.created_at) > new Date(existing.created_at)) {
292
+ latestByName.set(agent.name, agent);
293
+ }
294
+ }
295
+ agents = Array.from(latestByName.values());
296
+ // Apply type filter
297
+ if (options?.type) {
298
+ const typeFilter = options.type;
299
+ if (typeFilter === 'agents') {
300
+ agents = agents.filter(a => a.type === 'prompt' || a.type === 'code');
301
+ }
302
+ else if (typeFilter === 'skills' || typeFilter === 'skill') {
303
+ agents = agents.filter(a => a.type === 'skill');
304
+ }
305
+ else if (typeFilter === 'code' || typeFilter === 'prompt') {
306
+ agents = agents.filter(a => a.type === typeFilter);
307
+ }
308
+ }
309
+ // Apply search filter (match against name and description)
310
+ if (query) {
311
+ const words = query.toLowerCase().replace(/-/g, ' ').split(/\s+/);
312
+ agents = agents.filter(a => {
313
+ const name = (a.name || '').toLowerCase().replace(/-/g, ' ');
314
+ const desc = (a.description || '').toLowerCase();
315
+ return words.every(w => name.includes(w) || desc.includes(w));
316
+ });
317
+ }
318
+ // Map Agent to PublicAgent-compatible objects
319
+ const org = await getOrg(config);
320
+ return agents.map(a => ({
321
+ id: a.id,
322
+ org_name: org.name || org.slug || 'unknown',
323
+ org_slug: a.org_slug || org.slug || 'unknown',
324
+ name: a.name,
325
+ version: a.version,
326
+ type: a.type,
327
+ description: a.description,
328
+ stars_count: a.stars_count ?? 0,
329
+ tags: a.tags ?? [],
330
+ default_endpoint: a.default_endpoint || 'analyze',
331
+ created_at: a.created_at,
332
+ supported_providers: a.supported_providers ?? ['any'],
333
+ is_public: a.is_public,
334
+ pricing_mode: a.pricing_mode,
335
+ price_per_call_cents: a.price_per_call_cents,
336
+ }));
337
+ }
272
338
  async function fetchLlmKeys(config) {
273
339
  const result = await request(config, 'GET', '/llm-keys/export');
274
340
  return result.keys;
@@ -0,0 +1,230 @@
1
+ "use strict";
2
+ /**
3
+ * Tests for API client functions.
4
+ *
5
+ * These tests cover the core API client that all CLI commands depend on:
6
+ * - Request building and authentication
7
+ * - Error parsing
8
+ * - Public vs authenticated requests
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ const vitest_1 = require("vitest");
12
+ const api_1 = require("./api");
13
+ // Mock fetch globally
14
+ const mockFetch = vitest_1.vi.fn();
15
+ global.fetch = mockFetch;
16
+ (0, vitest_1.describe)('ApiError', () => {
17
+ (0, vitest_1.it)('includes status code and message', () => {
18
+ const error = new api_1.ApiError('Not found', 404);
19
+ (0, vitest_1.expect)(error.message).toBe('Not found');
20
+ (0, vitest_1.expect)(error.status).toBe(404);
21
+ });
22
+ (0, vitest_1.it)('includes optional payload', () => {
23
+ const payload = { error: { code: 'NOT_FOUND' } };
24
+ const error = new api_1.ApiError('Not found', 404, payload);
25
+ (0, vitest_1.expect)(error.payload).toEqual(payload);
26
+ });
27
+ });
28
+ (0, vitest_1.describe)('request', () => {
29
+ const config = {
30
+ apiKey: 'sk_test_123',
31
+ apiUrl: 'https://api.test.com',
32
+ };
33
+ (0, vitest_1.beforeEach)(() => {
34
+ mockFetch.mockReset();
35
+ });
36
+ (0, vitest_1.afterEach)(() => {
37
+ vitest_1.vi.restoreAllMocks();
38
+ });
39
+ (0, vitest_1.it)('adds Authorization header with Bearer token', async () => {
40
+ mockFetch.mockResolvedValueOnce({
41
+ ok: true,
42
+ json: () => Promise.resolve({ data: 'test' }),
43
+ });
44
+ await (0, api_1.request)(config, 'GET', '/test');
45
+ (0, vitest_1.expect)(mockFetch).toHaveBeenCalledWith('https://api.test.com/test', vitest_1.expect.objectContaining({
46
+ method: 'GET',
47
+ headers: vitest_1.expect.objectContaining({
48
+ Authorization: 'Bearer sk_test_123',
49
+ }),
50
+ }));
51
+ });
52
+ (0, vitest_1.it)('returns parsed JSON response', async () => {
53
+ const responseData = { org: 'test', slug: 'test-org' };
54
+ mockFetch.mockResolvedValueOnce({
55
+ ok: true,
56
+ json: () => Promise.resolve(responseData),
57
+ });
58
+ const result = await (0, api_1.request)(config, 'GET', '/org');
59
+ (0, vitest_1.expect)(result).toEqual(responseData);
60
+ });
61
+ (0, vitest_1.it)('throws ApiError when response not ok', async () => {
62
+ mockFetch.mockResolvedValueOnce({
63
+ ok: false,
64
+ status: 404,
65
+ statusText: 'Not Found',
66
+ text: () => Promise.resolve(JSON.stringify({
67
+ error: { message: 'Agent not found' }
68
+ })),
69
+ });
70
+ await (0, vitest_1.expect)((0, api_1.request)(config, 'GET', '/agents/missing'))
71
+ .rejects.toThrow(api_1.ApiError);
72
+ });
73
+ (0, vitest_1.it)('throws ApiError with 401 when no API key', async () => {
74
+ const noKeyConfig = {
75
+ apiKey: undefined,
76
+ apiUrl: 'https://api.test.com',
77
+ };
78
+ await (0, vitest_1.expect)((0, api_1.request)(noKeyConfig, 'GET', '/test'))
79
+ .rejects.toThrow('Missing API key');
80
+ });
81
+ (0, vitest_1.it)('parses error message from JSON response', async () => {
82
+ mockFetch.mockResolvedValueOnce({
83
+ ok: false,
84
+ status: 403,
85
+ statusText: 'Forbidden',
86
+ text: () => Promise.resolve(JSON.stringify({
87
+ error: { message: 'Access denied to private agent' }
88
+ })),
89
+ });
90
+ try {
91
+ await (0, api_1.request)(config, 'GET', '/agents/private');
92
+ vitest_1.expect.fail('Should have thrown');
93
+ }
94
+ catch (error) {
95
+ (0, vitest_1.expect)(error).toBeInstanceOf(api_1.ApiError);
96
+ (0, vitest_1.expect)(error.message).toBe('Access denied to private agent');
97
+ (0, vitest_1.expect)(error.status).toBe(403);
98
+ }
99
+ });
100
+ (0, vitest_1.it)('uses statusText when no JSON message', async () => {
101
+ mockFetch.mockResolvedValueOnce({
102
+ ok: false,
103
+ status: 500,
104
+ statusText: 'Internal Server Error',
105
+ text: () => Promise.resolve('not json'),
106
+ });
107
+ try {
108
+ await (0, api_1.request)(config, 'GET', '/broken');
109
+ vitest_1.expect.fail('Should have thrown');
110
+ }
111
+ catch (error) {
112
+ (0, vitest_1.expect)(error).toBeInstanceOf(api_1.ApiError);
113
+ (0, vitest_1.expect)(error.message).toBe('Internal Server Error');
114
+ }
115
+ });
116
+ (0, vitest_1.it)('includes custom headers', async () => {
117
+ mockFetch.mockResolvedValueOnce({
118
+ ok: true,
119
+ json: () => Promise.resolve({}),
120
+ });
121
+ await (0, api_1.request)(config, 'POST', '/agents', {
122
+ body: JSON.stringify({ name: 'test' }),
123
+ headers: { 'Content-Type': 'application/json' },
124
+ });
125
+ (0, vitest_1.expect)(mockFetch).toHaveBeenCalledWith(vitest_1.expect.any(String), vitest_1.expect.objectContaining({
126
+ headers: vitest_1.expect.objectContaining({
127
+ 'Content-Type': 'application/json',
128
+ Authorization: 'Bearer sk_test_123',
129
+ }),
130
+ }));
131
+ });
132
+ (0, vitest_1.it)('strips trailing slash from API URL', async () => {
133
+ const configWithSlash = {
134
+ apiKey: 'sk_test_123',
135
+ apiUrl: 'https://api.test.com/',
136
+ };
137
+ mockFetch.mockResolvedValueOnce({
138
+ ok: true,
139
+ json: () => Promise.resolve({}),
140
+ });
141
+ await (0, api_1.request)(configWithSlash, 'GET', '/test');
142
+ (0, vitest_1.expect)(mockFetch).toHaveBeenCalledWith('https://api.test.com/test', vitest_1.expect.any(Object));
143
+ });
144
+ });
145
+ (0, vitest_1.describe)('publicRequest', () => {
146
+ const config = {
147
+ apiUrl: 'https://api.test.com',
148
+ };
149
+ (0, vitest_1.beforeEach)(() => {
150
+ mockFetch.mockReset();
151
+ });
152
+ (0, vitest_1.it)('makes unauthenticated request', async () => {
153
+ mockFetch.mockResolvedValueOnce({
154
+ ok: true,
155
+ json: () => Promise.resolve([]),
156
+ });
157
+ await (0, api_1.publicRequest)(config, '/public/agents');
158
+ (0, vitest_1.expect)(mockFetch).toHaveBeenCalledWith('https://api.test.com/public/agents');
159
+ });
160
+ (0, vitest_1.it)('returns parsed JSON', async () => {
161
+ const agents = [{ name: 'agent1' }, { name: 'agent2' }];
162
+ mockFetch.mockResolvedValueOnce({
163
+ ok: true,
164
+ json: () => Promise.resolve(agents),
165
+ });
166
+ const result = await (0, api_1.publicRequest)(config, '/public/agents');
167
+ (0, vitest_1.expect)(result).toEqual(agents);
168
+ });
169
+ (0, vitest_1.it)('throws ApiError on failure', async () => {
170
+ mockFetch.mockResolvedValueOnce({
171
+ ok: false,
172
+ status: 404,
173
+ statusText: 'Not Found',
174
+ text: () => Promise.resolve('{}'),
175
+ });
176
+ await (0, vitest_1.expect)((0, api_1.publicRequest)(config, '/public/agents/missing'))
177
+ .rejects.toThrow(api_1.ApiError);
178
+ });
179
+ });
180
+ (0, vitest_1.describe)('getOrg', () => {
181
+ const config = {
182
+ apiKey: 'sk_test_123',
183
+ apiUrl: 'https://api.test.com',
184
+ };
185
+ (0, vitest_1.beforeEach)(() => {
186
+ mockFetch.mockReset();
187
+ });
188
+ (0, vitest_1.it)('calls GET /org endpoint', async () => {
189
+ const orgData = { id: '123', slug: 'test-org', name: 'Test Org' };
190
+ mockFetch.mockResolvedValueOnce({
191
+ ok: true,
192
+ json: () => Promise.resolve(orgData),
193
+ });
194
+ const result = await (0, api_1.getOrg)(config);
195
+ (0, vitest_1.expect)(mockFetch).toHaveBeenCalledWith('https://api.test.com/org', vitest_1.expect.objectContaining({ method: 'GET' }));
196
+ (0, vitest_1.expect)(result).toEqual(orgData);
197
+ });
198
+ });
199
+ (0, vitest_1.describe)('searchAgents', () => {
200
+ const config = {
201
+ apiUrl: 'https://api.test.com',
202
+ };
203
+ (0, vitest_1.beforeEach)(() => {
204
+ mockFetch.mockReset();
205
+ });
206
+ (0, vitest_1.it)('builds search query params', async () => {
207
+ mockFetch.mockResolvedValueOnce({
208
+ ok: true,
209
+ json: () => Promise.resolve([]),
210
+ });
211
+ await (0, api_1.searchAgents)(config, 'pdf analyzer', { sort: 'stars' });
212
+ (0, vitest_1.expect)(mockFetch).toHaveBeenCalledWith('https://api.test.com/public/agents?search=pdf+analyzer&sort=stars');
213
+ });
214
+ (0, vitest_1.it)('handles empty query', async () => {
215
+ mockFetch.mockResolvedValueOnce({
216
+ ok: true,
217
+ json: () => Promise.resolve([]),
218
+ });
219
+ await (0, api_1.searchAgents)(config, '');
220
+ (0, vitest_1.expect)(mockFetch).toHaveBeenCalledWith('https://api.test.com/public/agents');
221
+ });
222
+ (0, vitest_1.it)('includes tags in query', async () => {
223
+ mockFetch.mockResolvedValueOnce({
224
+ ok: true,
225
+ json: () => Promise.resolve([]),
226
+ });
227
+ await (0, api_1.searchAgents)(config, 'test', { tags: ['ai', 'document'] });
228
+ (0, vitest_1.expect)(mockFetch).toHaveBeenCalledWith(vitest_1.expect.stringContaining('tags=ai%2Cdocument'));
229
+ });
230
+ });
@@ -43,6 +43,8 @@ exports.getResolvedConfig = getResolvedConfig;
43
43
  exports.getConfigPath = getConfigPath;
44
44
  exports.getDefaultFormats = getDefaultFormats;
45
45
  exports.setDefaultFormats = setDefaultFormats;
46
+ exports.getDefaultScope = getDefaultScope;
47
+ exports.setDefaultScope = setDefaultScope;
46
48
  const promises_1 = __importDefault(require("fs/promises"));
47
49
  const path_1 = __importDefault(require("path"));
48
50
  const os_1 = __importDefault(require("os"));
@@ -129,3 +131,12 @@ async function setDefaultFormats(formats) {
129
131
  config.default_formats = formats;
130
132
  await saveConfig(config);
131
133
  }
134
+ async function getDefaultScope() {
135
+ const config = await loadConfig();
136
+ return config.default_scope;
137
+ }
138
+ async function setDefaultScope(scope) {
139
+ const config = await loadConfig();
140
+ config.default_scope = scope;
141
+ await saveConfig(config);
142
+ }