@bbyqtbean/taco-helper 0.1.2 → 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.
@@ -0,0 +1,477 @@
1
+ /**
2
+ * MCP Shim — lightweight proxy between the AI agent and the daemon.
3
+ *
4
+ * Speaks MCP stdio to the agent, proxies tool calls to the daemon
5
+ * over a Unix socket (~/.taco/daemon.sock).
6
+ *
7
+ * See: taco-helper-package-spec.md §MCP Shim, §D, §E
8
+ * See: daemon-mcp-strategy.md §2 (MCP Shim)
9
+ */
10
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
11
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
12
+ import { z } from 'zod';
13
+ import { Socket } from 'net';
14
+ import { existsSync } from 'fs';
15
+ import { join } from 'path';
16
+ import { homedir } from 'os';
17
+ import { spawn } from 'child_process';
18
+ const TACO_DIR = join(homedir(), '.taco');
19
+ const SOCKET_PATH = join(TACO_DIR, 'daemon.sock');
20
+ const LOCK_PATH = join(TACO_DIR, 'daemon.lock');
21
+ /** The shimId assigned by the daemon after connect handshake */
22
+ let shimId = null;
23
+ /** Active Unix socket connection to daemon */
24
+ let daemonSocket = null;
25
+ /** Pending tool call resolvers, keyed by a correlationId */
26
+ let callId = 0;
27
+ const pendingCalls = new Map();
28
+ /**
29
+ * Connect to the daemon over Unix socket.
30
+ * If the daemon isn't running, attempt to start it.
31
+ */
32
+ async function connectToDaemon() {
33
+ // Try connecting directly
34
+ if (existsSync(SOCKET_PATH)) {
35
+ try {
36
+ await attemptConnect();
37
+ return;
38
+ }
39
+ catch {
40
+ // Socket exists but connection failed — daemon may have died
41
+ }
42
+ }
43
+ // Daemon not running — try to start it
44
+ console.error('🌮 [Shim] Daemon not running. Attempting to start...');
45
+ await startDaemonProcess();
46
+ // Retry connection
47
+ const maxRetries = 5;
48
+ for (let i = 0; i < maxRetries; i++) {
49
+ await sleep(2000);
50
+ try {
51
+ await attemptConnect();
52
+ return;
53
+ }
54
+ catch {
55
+ console.error(`🌮 [Shim] Connection attempt ${i + 1}/${maxRetries} failed, retrying...`);
56
+ }
57
+ }
58
+ console.error('🌮 [Shim] Could not connect to daemon after starting it.');
59
+ }
60
+ /**
61
+ * Attempt a single connection to the daemon socket.
62
+ */
63
+ function attemptConnect() {
64
+ return new Promise((resolve, reject) => {
65
+ const socket = new Socket();
66
+ socket.connect(SOCKET_PATH, () => {
67
+ daemonSocket = socket;
68
+ // Send connect handshake
69
+ const msg = {
70
+ event: 'connect',
71
+ cwd: process.cwd(),
72
+ pid: process.pid,
73
+ agent: 'antigravity', // TODO: detect from config
74
+ };
75
+ socket.write(JSON.stringify(msg) + '\n');
76
+ });
77
+ let buffer = '';
78
+ socket.on('data', (data) => {
79
+ buffer += data.toString();
80
+ let newlineIdx;
81
+ while ((newlineIdx = buffer.indexOf('\n')) !== -1) {
82
+ const line = buffer.slice(0, newlineIdx).trim();
83
+ buffer = buffer.slice(newlineIdx + 1);
84
+ if (line.length > 0) {
85
+ handleDaemonMessage(line);
86
+ }
87
+ }
88
+ });
89
+ // Wait for the connected response
90
+ const onMessage = (line) => {
91
+ try {
92
+ const msg = JSON.parse(line);
93
+ if (msg.event === 'connected') {
94
+ shimId = msg.shimId;
95
+ console.error(`🌮 [Shim] Connected to daemon (shimId: ${shimId})`);
96
+ socket.removeListener('data', onDataForHandshake);
97
+ resolve();
98
+ }
99
+ }
100
+ catch { /* ignore parse errors during handshake */ }
101
+ };
102
+ // Temporary listener for the handshake
103
+ const onDataForHandshake = (data) => {
104
+ const lines = data.toString().split('\n');
105
+ for (const line of lines) {
106
+ if (line.trim())
107
+ onMessage(line.trim());
108
+ }
109
+ };
110
+ // We already set up the main data handler above, so the connected
111
+ // event will be handled there. Just wait with a timeout.
112
+ const timeout = setTimeout(() => {
113
+ socket.destroy();
114
+ reject(new Error('Connection handshake timeout'));
115
+ }, 5000);
116
+ // Listen for the connected event via the main handler
117
+ const origHandler = handleDaemonMessage;
118
+ handleDaemonMessage = (line) => {
119
+ try {
120
+ const msg = JSON.parse(line);
121
+ if (msg.event === 'connected') {
122
+ shimId = msg.shimId;
123
+ console.error(`🌮 [Shim] Connected to daemon (shimId: ${shimId})`);
124
+ clearTimeout(timeout);
125
+ handleDaemonMessage = origHandler;
126
+ resolve();
127
+ return;
128
+ }
129
+ }
130
+ catch { /* ignore */ }
131
+ origHandler(line);
132
+ };
133
+ socket.on('error', (err) => {
134
+ clearTimeout(timeout);
135
+ reject(err);
136
+ });
137
+ socket.on('close', () => {
138
+ daemonSocket = null;
139
+ shimId = null;
140
+ });
141
+ });
142
+ }
143
+ /**
144
+ * Handle incoming messages from the daemon.
145
+ */
146
+ let handleDaemonMessage = (line) => {
147
+ try {
148
+ const msg = JSON.parse(line);
149
+ if (msg.event === 'tool_result') {
150
+ // Resolve a pending tool call — match by shimId only (sequential calls)
151
+ // Find the oldest pending call
152
+ const [id, pending] = [...pendingCalls.entries()][0] ?? [];
153
+ if (id !== undefined && pending) {
154
+ pendingCalls.delete(id);
155
+ if (msg.error) {
156
+ pending.reject(new Error(msg.error));
157
+ }
158
+ else {
159
+ pending.resolve(msg.result);
160
+ }
161
+ }
162
+ }
163
+ else if (msg.type === 'sample') {
164
+ // Daemon requesting us to trigger sampling — Phase 6
165
+ console.error('🌮 [Shim] Received sample request from daemon (not yet implemented)');
166
+ }
167
+ }
168
+ catch {
169
+ console.error('🌮 [Shim] Could not parse daemon message:', line);
170
+ }
171
+ };
172
+ /**
173
+ * Send a tool call to the daemon and wait for the result.
174
+ */
175
+ async function callDaemon(tool, args = {}) {
176
+ if (!daemonSocket || !shimId) {
177
+ // Try reconnecting once
178
+ try {
179
+ await connectToDaemon();
180
+ }
181
+ catch {
182
+ throw new Error('Taco daemon unavailable — it may have restarted.');
183
+ }
184
+ }
185
+ const id = callId++;
186
+ return new Promise((resolve, reject) => {
187
+ pendingCalls.set(id, { resolve, reject });
188
+ const msg = {
189
+ event: 'tool_call',
190
+ shimId,
191
+ tool,
192
+ args,
193
+ };
194
+ daemonSocket.write(JSON.stringify(msg) + '\n');
195
+ // Timeout after 10s
196
+ setTimeout(() => {
197
+ if (pendingCalls.has(id)) {
198
+ pendingCalls.delete(id);
199
+ reject(new Error('Tool call timeout'));
200
+ }
201
+ }, 10_000);
202
+ });
203
+ }
204
+ /**
205
+ * Start the daemon as a detached process.
206
+ */
207
+ async function startDaemonProcess() {
208
+ try {
209
+ const child = spawn('npx', ['-y', '@bbyqtbean/taco-helper@latest', 'daemon'], {
210
+ detached: true,
211
+ stdio: 'ignore',
212
+ cwd: homedir(),
213
+ });
214
+ child.unref();
215
+ console.error('🌮 [Shim] Started daemon process');
216
+ }
217
+ catch (err) {
218
+ console.error('🌮 [Shim] Failed to start daemon:', err.message);
219
+ }
220
+ }
221
+ function sleep(ms) {
222
+ return new Promise(resolve => setTimeout(resolve, ms));
223
+ }
224
+ /**
225
+ * Request the agent to implement pending comments via MCP sampling.
226
+ * If the agent doesn't support sampling, silently does nothing.
227
+ */
228
+ async function requestAgentImplementation(mcpServer) {
229
+ const prompt = `You have new Taco UI comments. Call mcp_taco_get_comments to see them, implement each change, then call mcp_taco_resolve_comment for each one when done.`;
230
+ try {
231
+ console.error('🌮 [Sampling] Requesting agent to implement comments...');
232
+ await mcpServer.server.createMessage({
233
+ messages: [{
234
+ role: 'user',
235
+ content: { type: 'text', text: prompt },
236
+ }],
237
+ maxTokens: 1024,
238
+ });
239
+ console.error('🌮 [Sampling] Agent acknowledged');
240
+ }
241
+ catch (err) {
242
+ if (err?.code === -32601 || err?.message?.includes('not supported') || err?.message?.includes('Method not found')) {
243
+ console.error('🌮 [Sampling] Agent does not support sampling — user must prompt manually');
244
+ }
245
+ else {
246
+ throw err;
247
+ }
248
+ }
249
+ }
250
+ /**
251
+ * Start the MCP shim.
252
+ * This is the main entry point for `npx taco-helper mcp`.
253
+ */
254
+ export async function startMcpShim() {
255
+ console.error('🌮 Starting Taco MCP shim...');
256
+ // Connect to daemon
257
+ try {
258
+ await connectToDaemon();
259
+ }
260
+ catch (err) {
261
+ console.error(`🌮 [Shim] Warning: could not connect to daemon: ${err.message}`);
262
+ console.error('🌮 [Shim] Tools will return errors until daemon is available.');
263
+ }
264
+ // Create MCP server
265
+ const server = new McpServer({
266
+ name: 'taco',
267
+ version: '0.2.0',
268
+ });
269
+ // ── Tool: mcp_taco_get_comments ──────────────────────────────
270
+ server.tool('get_comments', 'Retrieve all pending (unresolved) comments left by the user in the Taco Chrome extension. Each comment includes the text, CSS selector of the annotated element, page URL, and optionally a screenshot.', {}, async () => {
271
+ try {
272
+ const result = await callDaemon('get_comments');
273
+ return {
274
+ content: [{
275
+ type: 'text',
276
+ text: result.comments?.length === 0
277
+ ? (result.note ?? 'No pending comments.')
278
+ : JSON.stringify(result.comments, null, 2),
279
+ }],
280
+ };
281
+ }
282
+ catch (err) {
283
+ return {
284
+ content: [{ type: 'text', text: `Error: ${err.message}` }],
285
+ isError: true,
286
+ };
287
+ }
288
+ });
289
+ // ── Tool: mcp_taco_get_all_comments ──────────────────────────
290
+ server.tool('get_all_comments', 'Retrieve all comments including resolved ones. Use this to see the full history of comments.', {
291
+ include_resolved: z.boolean().optional().describe('Whether to include resolved comments (default: true)'),
292
+ }, async ({ include_resolved }) => {
293
+ try {
294
+ const result = await callDaemon('get_all_comments', { include_resolved: include_resolved ?? true });
295
+ return {
296
+ content: [{
297
+ type: 'text',
298
+ text: result.comments?.length === 0
299
+ ? 'No comments found.'
300
+ : JSON.stringify(result.comments, null, 2),
301
+ }],
302
+ };
303
+ }
304
+ catch (err) {
305
+ return {
306
+ content: [{ type: 'text', text: `Error: ${err.message}` }],
307
+ isError: true,
308
+ };
309
+ }
310
+ });
311
+ // ── Tool: mcp_taco_resolve_comment ───────────────────────────
312
+ server.tool('mark_resolved', 'Mark a comment as resolved by its ID. Use this after you have addressed a comment.', {
313
+ id: z.string().describe('The ID of the comment to mark as resolved'),
314
+ }, async ({ id }) => {
315
+ try {
316
+ const result = await callDaemon('resolve_comment', { id });
317
+ return {
318
+ content: [{
319
+ type: 'text',
320
+ text: result.success
321
+ ? `Comment ${id} marked as resolved.`
322
+ : `Comment ${id} not found.`,
323
+ }],
324
+ };
325
+ }
326
+ catch (err) {
327
+ return {
328
+ content: [{ type: 'text', text: `Error: ${err.message}` }],
329
+ isError: true,
330
+ };
331
+ }
332
+ });
333
+ // ── Tool: mcp_taco_action_comment ────────────────────────────
334
+ server.tool('action_comment', 'Mark a comment as in-progress (actioned) before starting work on it. Prevents duplicate pickup across sessions.', {
335
+ id: z.string().describe('The ID of the comment to mark as actioned'),
336
+ }, async ({ id }) => {
337
+ try {
338
+ const result = await callDaemon('action_comment', { id });
339
+ return {
340
+ content: [{
341
+ type: 'text',
342
+ text: result.success
343
+ ? `Comment ${id} marked as actioned.`
344
+ : `Comment ${id} not found.`,
345
+ }],
346
+ };
347
+ }
348
+ catch (err) {
349
+ return {
350
+ content: [{ type: 'text', text: `Error: ${err.message}` }],
351
+ isError: true,
352
+ };
353
+ }
354
+ });
355
+ // ── Tool: mcp_taco_list_projects ─────────────────────────────
356
+ server.tool('list_projects', 'Debug tool — lists all registered projects and their port mappings.', {}, async () => {
357
+ try {
358
+ const result = await callDaemon('list_projects');
359
+ return {
360
+ content: [{
361
+ type: 'text',
362
+ text: JSON.stringify(result.projects, null, 2),
363
+ }],
364
+ };
365
+ }
366
+ catch (err) {
367
+ return {
368
+ content: [{ type: 'text', text: `Error: ${err.message}` }],
369
+ isError: true,
370
+ };
371
+ }
372
+ });
373
+ // ── MCP Resource: taco://comments/pending ─────────────────────
374
+ server.resource('pending-comments', 'taco://comments/pending', { mimeType: 'application/json' }, async () => {
375
+ try {
376
+ const result = await callDaemon('get_comments');
377
+ return {
378
+ contents: [{
379
+ uri: 'taco://comments/pending',
380
+ mimeType: 'application/json',
381
+ text: JSON.stringify(result.comments ?? [], null, 2),
382
+ }],
383
+ };
384
+ }
385
+ catch (err) {
386
+ return {
387
+ contents: [{
388
+ uri: 'taco://comments/pending',
389
+ mimeType: 'application/json',
390
+ text: JSON.stringify({ error: err.message }),
391
+ }],
392
+ };
393
+ }
394
+ });
395
+ // Connect to stdio transport
396
+ const transport = new StdioServerTransport();
397
+ await server.connect(transport);
398
+ console.error('🌮 Taco MCP shim running on stdio');
399
+ // ── Resource Subscriptions: SSE → notifications/resources/updated ──
400
+ // Open SSE connection to daemon to push resource update notifications
401
+ try {
402
+ const http = await import('http');
403
+ const sseReq = http.request({
404
+ hostname: '127.0.0.1',
405
+ port: 7867,
406
+ path: '/events',
407
+ method: 'GET',
408
+ headers: { 'Accept': 'text/event-stream' },
409
+ }, (res) => {
410
+ let sseBuffer = '';
411
+ res.on('data', (chunk) => {
412
+ sseBuffer += chunk.toString();
413
+ const lines = sseBuffer.split('\n');
414
+ sseBuffer = lines.pop() ?? '';
415
+ for (const line of lines) {
416
+ if (line.startsWith('event: comment-received')) {
417
+ // Notify the agent that comments resource has been updated
418
+ try {
419
+ server.server.notification({
420
+ method: 'notifications/resources/updated',
421
+ params: { uri: 'taco://comments/pending' },
422
+ });
423
+ console.error('🌮 [Shim] Sent resource update notification');
424
+ }
425
+ catch {
426
+ // Agent may not support subscriptions — that's fine
427
+ }
428
+ }
429
+ }
430
+ });
431
+ });
432
+ sseReq.on('error', () => {
433
+ // SSE connection failed — non-critical, resource subscriptions just won't work
434
+ console.error('🌮 [Shim] Could not open SSE connection to daemon');
435
+ });
436
+ sseReq.end();
437
+ console.error('🌮 [Shim] SSE subscription active for resource notifications');
438
+ }
439
+ catch {
440
+ console.error('🌮 [Shim] SSE subscription setup failed (non-critical)');
441
+ }
442
+ // ── Sampling: daemon → shim → agent ─────────────────────────
443
+ // The daemon sends { type: "sample" } over the Unix socket when it wants
444
+ // the agent to start working. We handle that in handleDaemonMessage above,
445
+ // but need a reference to the server to call sampling/createMessage.
446
+ const origHandler = handleDaemonMessage;
447
+ handleDaemonMessage = (line) => {
448
+ try {
449
+ const msg = JSON.parse(line);
450
+ if (msg.type === 'sample') {
451
+ console.error('🌮 [Shim] Received sample request from daemon');
452
+ requestAgentImplementation(server).catch((err) => {
453
+ console.error('🌮 [Sampling] Error:', err.message ?? err);
454
+ });
455
+ return;
456
+ }
457
+ }
458
+ catch { /* ignore */ }
459
+ origHandler(line);
460
+ };
461
+ // Clean up on exit
462
+ process.on('SIGINT', () => {
463
+ if (daemonSocket && shimId) {
464
+ daemonSocket.write(JSON.stringify({ event: 'disconnect', shimId }) + '\n');
465
+ }
466
+ daemonSocket?.destroy();
467
+ process.exit(0);
468
+ });
469
+ process.on('SIGTERM', () => {
470
+ if (daemonSocket && shimId) {
471
+ daemonSocket.write(JSON.stringify({ event: 'disconnect', shimId }) + '\n');
472
+ }
473
+ daemonSocket?.destroy();
474
+ process.exit(0);
475
+ });
476
+ }
477
+ //# sourceMappingURL=mcp-shim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-shim.js","sourceRoot":"","sources":["../src/mcp-shim.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Project registry — maps localhost ports to project workspace paths.
3
+ *
4
+ * Stored as a flat JSON file at ~/.taco/projects.json.
5
+ * The daemon reads/writes this to route comments to the correct workspace.
6
+ *
7
+ * See: daemon-mcp-strategy.md §Port-to-Workspace Mapping
8
+ */
9
+ export interface ProjectRegistry {
10
+ /** Maps localhost origin (e.g. "http://localhost:3000") → project root (e.g. "/Users/rebecca/projects/my-app") */
11
+ [origin: string]: string;
12
+ }
13
+ /**
14
+ * Load the project registry from disk.
15
+ * Returns an empty object if the file doesn't exist.
16
+ */
17
+ export declare function loadRegistry(): ProjectRegistry;
18
+ /**
19
+ * Save the project registry to disk.
20
+ */
21
+ export declare function saveRegistry(registry: ProjectRegistry): void;
22
+ /**
23
+ * Register a project: map a localhost origin to a workspace path.
24
+ */
25
+ export declare function registerProject(origin: string, workspacePath: string): void;
26
+ /**
27
+ * Look up which workspace owns a given port.
28
+ * Returns the workspace path, or undefined if not registered.
29
+ */
30
+ export declare function getWorkspaceForPort(port: number): string | undefined;
31
+ /**
32
+ * Look up the port for a given workspace CWD.
33
+ * Returns the port number, or undefined if not registered.
34
+ */
35
+ export declare function getPortForWorkspace(cwd: string): number | undefined;
36
+ /**
37
+ * List all registered projects.
38
+ */
39
+ export declare function listProjects(): Array<{
40
+ origin: string;
41
+ workspace: string;
42
+ }>;
43
+ //# sourceMappingURL=project-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-registry.d.ts","sourceRoot":"","sources":["../src/project-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,MAAM,WAAW,eAAe;IAC5B,kHAAkH;IAClH,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;CAC5B;AAED;;;GAGG;AACH,wBAAgB,YAAY,IAAI,eAAe,CAQ9C;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAK5D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI,CAI3E;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAWpE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAYnE;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,KAAK,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAG3E"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Project registry — maps localhost ports to project workspace paths.
3
+ *
4
+ * Stored as a flat JSON file at ~/.taco/projects.json.
5
+ * The daemon reads/writes this to route comments to the correct workspace.
6
+ *
7
+ * See: daemon-mcp-strategy.md §Port-to-Workspace Mapping
8
+ */
9
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
10
+ import { join } from 'path';
11
+ import { TACO_DIR } from './sqlite-store.js';
12
+ const REGISTRY_PATH = join(TACO_DIR, 'projects.json');
13
+ /**
14
+ * Load the project registry from disk.
15
+ * Returns an empty object if the file doesn't exist.
16
+ */
17
+ export function loadRegistry() {
18
+ try {
19
+ if (!existsSync(REGISTRY_PATH))
20
+ return {};
21
+ const raw = readFileSync(REGISTRY_PATH, 'utf-8');
22
+ return JSON.parse(raw);
23
+ }
24
+ catch {
25
+ return {};
26
+ }
27
+ }
28
+ /**
29
+ * Save the project registry to disk.
30
+ */
31
+ export function saveRegistry(registry) {
32
+ if (!existsSync(TACO_DIR)) {
33
+ mkdirSync(TACO_DIR, { recursive: true });
34
+ }
35
+ writeFileSync(REGISTRY_PATH, JSON.stringify(registry, null, 2), 'utf-8');
36
+ }
37
+ /**
38
+ * Register a project: map a localhost origin to a workspace path.
39
+ */
40
+ export function registerProject(origin, workspacePath) {
41
+ const registry = loadRegistry();
42
+ registry[origin] = workspacePath;
43
+ saveRegistry(registry);
44
+ }
45
+ /**
46
+ * Look up which workspace owns a given port.
47
+ * Returns the workspace path, or undefined if not registered.
48
+ */
49
+ export function getWorkspaceForPort(port) {
50
+ const registry = loadRegistry();
51
+ // Try common origins for this port
52
+ const origins = [
53
+ `http://localhost:${port}`,
54
+ `http://127.0.0.1:${port}`,
55
+ ];
56
+ for (const origin of origins) {
57
+ if (registry[origin])
58
+ return registry[origin];
59
+ }
60
+ return undefined;
61
+ }
62
+ /**
63
+ * Look up the port for a given workspace CWD.
64
+ * Returns the port number, or undefined if not registered.
65
+ */
66
+ export function getPortForWorkspace(cwd) {
67
+ const registry = loadRegistry();
68
+ for (const [origin, workspace] of Object.entries(registry)) {
69
+ if (workspace === cwd) {
70
+ try {
71
+ return parseInt(new URL(origin).port, 10);
72
+ }
73
+ catch {
74
+ continue;
75
+ }
76
+ }
77
+ }
78
+ return undefined;
79
+ }
80
+ /**
81
+ * List all registered projects.
82
+ */
83
+ export function listProjects() {
84
+ const registry = loadRegistry();
85
+ return Object.entries(registry).map(([origin, workspace]) => ({ origin, workspace }));
86
+ }
87
+ //# sourceMappingURL=project-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-registry.js","sourceRoot":"","sources":["../src/project-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACxE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;AAOtD;;;GAGG;AACH,MAAM,UAAU,YAAY;IACxB,IAAI,CAAC;QACD,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC;YAAE,OAAO,EAAE,CAAC;QAC1C,MAAM,GAAG,GAAG,YAAY,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;QACjD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAoB,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,EAAE,CAAC;IACd,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,QAAyB;IAClD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxB,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7C,CAAC;IACD,aAAa,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AAC7E,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,aAAqB;IACjE,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAC;IAChC,QAAQ,CAAC,MAAM,CAAC,GAAG,aAAa,CAAC;IACjC,YAAY,CAAC,QAAQ,CAAC,CAAC;AAC3B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC5C,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAC;IAChC,mCAAmC;IACnC,MAAM,OAAO,GAAG;QACZ,oBAAoB,IAAI,EAAE;QAC1B,oBAAoB,IAAI,EAAE;KAC7B,CAAC;IACF,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC3B,IAAI,QAAQ,CAAC,MAAM,CAAC;YAAE,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,SAAS,CAAC;AACrB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAW;IAC3C,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAC;IAChC,KAAK,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzD,IAAI,SAAS,KAAK,GAAG,EAAE,CAAC;YACpB,IAAI,CAAC;gBACD,OAAO,QAAQ,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC9C,CAAC;YAAC,MAAM,CAAC;gBACL,SAAS;YACb,CAAC;QACL,CAAC;IACL,CAAC;IACD,OAAO,SAAS,CAAC;AACrB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY;IACxB,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAC;IAChC,OAAO,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;AAC1F,CAAC"}