WIP: A simple cli for daily tangled use cases and AI integration. This is for my personal use right now, but happy if others get mileage from it! :)
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add context command for debugging repository context

Implement context debug command to help users understand repository
context resolution:
- Shows if current directory is a Tangled repository
- Displays owner (DID or handle), repository name, protocol
- Shows which Git remote is being used
- Provides helpful error messages when not in a Tangled repo

This is a debug/diagnostic tool that helps users verify their
context resolution is working correctly.

Includes comprehensive test coverage (4 tests) for success and
error cases.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

+130
+38
src/commands/context.ts
··· 1 + import { Command } from 'commander'; 2 + import { getCurrentRepoContext } from '../lib/context.js'; 3 + 4 + /** 5 + * Create the context command for debugging repository context resolution 6 + */ 7 + export function createContextCommand(): Command { 8 + const context = new Command('context'); 9 + context.description('Show current repository context (debug)'); 10 + 11 + context.action(async () => { 12 + try { 13 + const repoContext = await getCurrentRepoContext(); 14 + 15 + if (!repoContext) { 16 + console.log('✗ Not in a Tangled repository'); 17 + console.log('\nTo use this repository with Tangled, add a tangled.org remote:'); 18 + console.log(' git remote add origin git@tangled.org:<did>/<repo>.git'); 19 + console.log('\nOr clone from tangled.org:'); 20 + console.log(' git clone git@tangled.org:<did>/<repo>.git'); 21 + process.exit(1); 22 + } 23 + 24 + console.log('✓ Repository context resolved:\n'); 25 + console.log(` Owner: ${repoContext.owner} (${repoContext.ownerType})`); 26 + console.log(` Repository: ${repoContext.name}`); 27 + console.log(` Protocol: ${repoContext.protocol}`); 28 + console.log(` Remote: ${repoContext.remoteName} (${repoContext.remoteUrl})`); 29 + } catch (error) { 30 + console.error( 31 + `Failed to resolve context: ${error instanceof Error ? error.message : 'Unknown error'}` 32 + ); 33 + process.exit(1); 34 + } 35 + }); 36 + 37 + return context; 38 + }
+92
tests/commands/context.test.ts
··· 1 + import { beforeEach, describe, expect, it, vi } from 'vitest'; 2 + import { createContextCommand } from '../../src/commands/context.js'; 3 + 4 + // Mock modules 5 + vi.mock('../../src/lib/context.js'); 6 + 7 + // Import mocked modules 8 + import * as contextModule from '../../src/lib/context.js'; 9 + 10 + describe('Context Command', () => { 11 + let consoleLogSpy: ReturnType<typeof vi.fn>; 12 + let consoleErrorSpy: ReturnType<typeof vi.fn>; 13 + 14 + beforeEach(() => { 15 + vi.clearAllMocks(); 16 + 17 + // Mock console methods 18 + consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {}) as never; 19 + consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}) as never; 20 + 21 + // Mock process.exit to throw so tests don't actually exit 22 + vi.spyOn(process, 'exit').mockImplementation((code) => { 23 + throw new Error(`process.exit(${code})`); 24 + }) as never; 25 + }); 26 + 27 + it('should display repository context when in tangled repo', async () => { 28 + vi.mocked(contextModule.getCurrentRepoContext).mockResolvedValue({ 29 + owner: 'did:plc:b2mcbcamkwyznc5fkplwlxbf', 30 + ownerType: 'did', 31 + name: 'tangled-cli', 32 + remoteName: 'origin', 33 + remoteUrl: 'git@tangled.org:did:plc:b2mcbcamkwyznc5fkplwlxbf/tangled-cli.git', 34 + protocol: 'ssh', 35 + }); 36 + 37 + const context = createContextCommand(); 38 + await context.parseAsync(['node', 'test']); 39 + 40 + expect(consoleLogSpy).toHaveBeenCalledWith('✓ Repository context resolved:\n'); 41 + expect(consoleLogSpy).toHaveBeenCalledWith(' Owner: did:plc:b2mcbcamkwyznc5fkplwlxbf (did)'); 42 + expect(consoleLogSpy).toHaveBeenCalledWith(' Repository: tangled-cli'); 43 + expect(consoleLogSpy).toHaveBeenCalledWith(' Protocol: ssh'); 44 + expect(consoleLogSpy).toHaveBeenCalledWith( 45 + ' Remote: origin (git@tangled.org:did:plc:b2mcbcamkwyznc5fkplwlxbf/tangled-cli.git)' 46 + ); 47 + }); 48 + 49 + it('should display context for HTTPS remote with handle', async () => { 50 + vi.mocked(contextModule.getCurrentRepoContext).mockResolvedValue({ 51 + owner: 'markbennett.ca', 52 + ownerType: 'handle', 53 + name: 'tangled-cli', 54 + remoteName: 'origin', 55 + remoteUrl: 'https://tangled.org/markbennett.ca/tangled-cli', 56 + protocol: 'https', 57 + }); 58 + 59 + const context = createContextCommand(); 60 + await context.parseAsync(['node', 'test']); 61 + 62 + expect(consoleLogSpy).toHaveBeenCalledWith(' Owner: markbennett.ca (handle)'); 63 + expect(consoleLogSpy).toHaveBeenCalledWith(' Protocol: https'); 64 + }); 65 + 66 + it('should show error when not in tangled repository', async () => { 67 + vi.mocked(contextModule.getCurrentRepoContext).mockResolvedValue(null); 68 + 69 + const context = createContextCommand(); 70 + await expect(context.parseAsync(['node', 'test'])).rejects.toThrow('process.exit(1)'); 71 + 72 + expect(consoleLogSpy).toHaveBeenCalledWith('✗ Not in a Tangled repository'); 73 + expect(consoleLogSpy).toHaveBeenCalledWith( 74 + expect.stringContaining('To use this repository with Tangled') 75 + ); 76 + expect(consoleLogSpy).toHaveBeenCalledWith( 77 + expect.stringContaining('git remote add origin git@tangled.org:') 78 + ); 79 + }); 80 + 81 + it('should handle errors gracefully', async () => { 82 + vi.mocked(contextModule.getCurrentRepoContext).mockRejectedValue(new Error('Git error')); 83 + 84 + const context = createContextCommand(); 85 + await expect(context.parseAsync(['node', 'test'])).rejects.toThrow('process.exit(1)'); 86 + 87 + expect(consoleErrorSpy).toHaveBeenCalledWith( 88 + expect.stringContaining('Failed to resolve context') 89 + ); 90 + expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('Git error')); 91 + }); 92 + });