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 initial README.md with project overview, architecture, and implementation plan

Mark Bennett d859b005

+139
+139
README.md
··· 1 + # **Tangled CLI: Architecture & Implementation Plan** 2 + 3 + ## **1\. Project Overview** 4 + 5 + **Goal:** Create a context-aware CLI for tangled.org that bridges the gap between the AT Protocol (XRPC) and standard Git. **Philosophy:** Follow the **GitHub CLI (gh)** standard: act as a wrapper that creates a seamless experience where the API and local Git repo feel like one unified tool. 6 + 7 + ## **2\. Prior Art Analysis: GitHub CLI (gh) vs. Tangled CLI** 8 + 9 + | Feature | GitHub CLI (gh) Approach | Tangled CLI Strategy | 10 + | :------------- | :--------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------- | 11 + | **Context** | Infers repo from .git/config remote URL. | **Must-Have:** Parse .git/config to resolve did:plc:... from the remote URL. | 12 + | **Auth** | Stores oauth token; acts as a git-credential-helper. | **Plan:** Store AT Proto session; inject auth headers into git operations if possible, or manage SSH keys via API. | 13 + | **Output** | TTY \= Tables. Pipe \= Text. \--json \= Structured. | **Plan:** Use is-interactive check. Default to "Human Mode". Force "Machine Mode" via flags. | 14 + | **Filtering** | \--json name,url (filters fields). | **Plan:** Support basic \--json flag first. Add field filtering (--json "cloneUrl,did") to save LLM context window tokens. | 15 + | **Extensions** | Allows custom subcommands. | _Out of Scope for V1._ | 16 + 17 + ## **3\. High-Level Architecture (Refined)** 18 + 19 + The CLI acts as a "Context Engine" before it even hits the API. 20 + `graph TD` 21 + `User[User / LLM] -->|Command| CLI` 22 + 23 + `subgraph "Context Engine"` 24 + `Git[Local .git/config] -->|Read Remote| Resolver[Context Resolver]` 25 + `Resolver -->|Inferred DID| Payload` 26 + `end` 27 + 28 + `subgraph "Execution"` 29 + `Payload -->|XRPC Request| API[Tangled AppView]` 30 + `Payload -->|Git Command| Shell[Git Shell]` 31 + `end` 32 + 33 + `API --> Output` 34 + `Shell --> Output` 35 + 36 + ## **4\. Tech Stack (TypeScript)** 37 + 38 + | Component | Library | Purpose | 39 + | :---------------- | :-------------------- | :----------------------------------------------------------- | 40 + | **Framework** | **commander** | Routing (tangled repo create). | 41 + | **API Client** | **@atproto/api** | Official XRPC client & session management. | 42 + | **Git Context** | **git-url-parse** | **New:** Parses remote URLs to extract the Tangled DID/NSID. | 43 + | **Git Ops** | **simple-git** | Wraps local git operations safely. | 44 + | **Validation** | **zod** | Validates inputs & generates schemas for LLMs. | 45 + | **Interactivity** | **@inquirer/prompts** | Modern prompts for humans. | 46 + | **Formatting** | **cli-table3** | **New:** For gh-style pretty tables in Human Mode. | 47 + 48 + ## **5\. Agent Integration (The "LLM Friendly" Layer)** 49 + 50 + To make this tool accessible to Claude Code/Gemini, we adopt gh's best patterns: 51 + 52 + ### **Rule 1: Context is King** 53 + 54 + LLMs often hallucinate repo IDs. 55 + 56 + - **Design:** If the user/LLM runs tangled issue list inside a folder, **do not** ask for the repo DID. Infer it. 57 + - **Fallback:** Only error if no git remote is found. 58 + 59 + ### **Rule 2: Precision JSON (--json \<fields\>)** 60 + 61 + LLMs have token limits. Returning a 50KB repo object is wasteful. 62 + 63 + - **Feature:** tangled repo view \--json name,cloneUrl,description 64 + - **Implementation:** Use lodash/pick to filter the API response before printing to stdout. 65 + 66 + ### **Rule 3: The CLAUDE.md Context** 67 + 68 + Include this file in the repo so Claude knows how to drive the CLI. 69 + `# Tangled CLI Cheatsheet` 70 + 71 + `## Principles` 72 + ``1. **Context Aware:** Run commands inside the repo directory. The CLI infers the repo DID from `git remote`.`` 73 + ``2. **Flags:** Always use `--json` and `--no-input`.`` 74 + 75 + `## Common Tasks` 76 + ``- **Setup:** `tangled auth login --username <handle> --password <app-password>` (One time)`` 77 + `` - **Init:** `tangled repo create <name> --json` -> `git remote add tangled <url>` `` 78 + ``- **Issues:** `tangled issue list --json title,number` (Uses current dir context)`` 79 + 80 + ## **6\. Implementation Roadmap** 81 + 82 + ### **Phase 1: Context & Auth (The "Plumbing")** 83 + 84 + 1. **Auth Storage:** Store the AT Proto session in conf. 85 + 2. **Context Resolver:** 86 + - Implement a helper getRepoFromContext(). 87 + - It calls git remote get-url origin. 88 + - Regex matches the Tangled DID from the URL. 89 + - Returns the repo param required by XRPC. 90 + 91 + ### **Phase 2: CRUD Commands (The "Porcelain")** 92 + 93 + 1. **repo create:** 94 + - Creates record via XRPC. 95 + - **Auto-config:** Runs git init and git remote add if local dir is empty. 96 + 2. **issue list/create:** 97 + - Uses getRepoFromContext() so the user just types tangled issue create. 98 + 99 + ### **Phase 3: The "Machine" View** 100 + 101 + 1. Implement a generic Formatter class. 102 + - Formatter.render(data, { json: true, fields: \['id', 'name'\] }) 103 + - If json: pick fields, JSON.stringify. 104 + - If human: use cli-table3. 105 + 106 + ## **7\. Example: The Context Pattern** 107 + 108 + This is how we solve the usability problem (and the LLM hallucination problem). 109 + `// src/lib/context.ts` 110 + `import simpleGit from 'simple-git';` 111 + 112 + `export async function resolveRepoContext(explicitFlag?: string): Promise<string> {` 113 + `// 1. If user passed --repo flag, use it` 114 + `if (explicitFlag) return explicitFlag;` 115 + 116 + `// 2. Try to infer from local git config` 117 + `const git = simpleGit();` 118 + `const isRepo = await git.checkIsRepo();` 119 + 120 + `if (isRepo) {` 121 + `const remotes = await git.getRemotes(true);` 122 + `const tangledRemote = remotes.find(r => r.name === 'tangled' || r.name === 'origin');` 123 + 124 + `if (tangledRemote) {` 125 + `// Logic to extract DID from URL (e.g., [https://tangled.sh/did:plc:123/repo](https://tangled.sh/did:plc:123/repo))` 126 + `const did = extractDidFromUrl(tangledRemote.refs.fetch);` 127 + `if (did) return did;` 128 + `}` 129 + 130 + `}` 131 + 132 + `throw new Error("Could not infer repository context. Please use --repo <did> or run this inside a Tangled repository.");` 133 + `}` 134 + 135 + ### **Summary of Improvements** 136 + 137 + - **Context Inference:** This is the "killer feature" of gh that we are copying. It makes the tool usable for humans and safer for LLMs (less typing \= fewer errors). 138 + - **Filtered JSON:** Saves tokens for LLM context windows. 139 + - **Git Config Integration:** Treats the local .git folder as a database of configuration, reducing the need for environment variables or complex flags.