···11+# **Tangled CLI: Architecture & Implementation Plan**
22+33+## **1\. Project Overview**
44+55+**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.
66+77+## **2\. Prior Art Analysis: GitHub CLI (gh) vs. Tangled CLI**
88+99+| Feature | GitHub CLI (gh) Approach | Tangled CLI Strategy |
1010+| :------------- | :--------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------- |
1111+| **Context** | Infers repo from .git/config remote URL. | **Must-Have:** Parse .git/config to resolve did:plc:... from the remote URL. |
1212+| **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. |
1313+| **Output** | TTY \= Tables. Pipe \= Text. \--json \= Structured. | **Plan:** Use is-interactive check. Default to "Human Mode". Force "Machine Mode" via flags. |
1414+| **Filtering** | \--json name,url (filters fields). | **Plan:** Support basic \--json flag first. Add field filtering (--json "cloneUrl,did") to save LLM context window tokens. |
1515+| **Extensions** | Allows custom subcommands. | _Out of Scope for V1._ |
1616+1717+## **3\. High-Level Architecture (Refined)**
1818+1919+The CLI acts as a "Context Engine" before it even hits the API.
2020+`graph TD`
2121+`User[User / LLM] -->|Command| CLI`
2222+2323+ `subgraph "Context Engine"`
2424+ `Git[Local .git/config] -->|Read Remote| Resolver[Context Resolver]`
2525+ `Resolver -->|Inferred DID| Payload`
2626+ `end`
2727+2828+ `subgraph "Execution"`
2929+ `Payload -->|XRPC Request| API[Tangled AppView]`
3030+ `Payload -->|Git Command| Shell[Git Shell]`
3131+ `end`
3232+3333+ `API --> Output`
3434+ `Shell --> Output`
3535+3636+## **4\. Tech Stack (TypeScript)**
3737+3838+| Component | Library | Purpose |
3939+| :---------------- | :-------------------- | :----------------------------------------------------------- |
4040+| **Framework** | **commander** | Routing (tangled repo create). |
4141+| **API Client** | **@atproto/api** | Official XRPC client & session management. |
4242+| **Git Context** | **git-url-parse** | **New:** Parses remote URLs to extract the Tangled DID/NSID. |
4343+| **Git Ops** | **simple-git** | Wraps local git operations safely. |
4444+| **Validation** | **zod** | Validates inputs & generates schemas for LLMs. |
4545+| **Interactivity** | **@inquirer/prompts** | Modern prompts for humans. |
4646+| **Formatting** | **cli-table3** | **New:** For gh-style pretty tables in Human Mode. |
4747+4848+## **5\. Agent Integration (The "LLM Friendly" Layer)**
4949+5050+To make this tool accessible to Claude Code/Gemini, we adopt gh's best patterns:
5151+5252+### **Rule 1: Context is King**
5353+5454+LLMs often hallucinate repo IDs.
5555+5656+- **Design:** If the user/LLM runs tangled issue list inside a folder, **do not** ask for the repo DID. Infer it.
5757+- **Fallback:** Only error if no git remote is found.
5858+5959+### **Rule 2: Precision JSON (--json \<fields\>)**
6060+6161+LLMs have token limits. Returning a 50KB repo object is wasteful.
6262+6363+- **Feature:** tangled repo view \--json name,cloneUrl,description
6464+- **Implementation:** Use lodash/pick to filter the API response before printing to stdout.
6565+6666+### **Rule 3: The CLAUDE.md Context**
6767+6868+Include this file in the repo so Claude knows how to drive the CLI.
6969+`# Tangled CLI Cheatsheet`
7070+7171+`## Principles`
7272+``1. **Context Aware:** Run commands inside the repo directory. The CLI infers the repo DID from `git remote`.``
7373+``2. **Flags:** Always use `--json` and `--no-input`.``
7474+7575+`## Common Tasks`
7676+``- **Setup:** `tangled auth login --username <handle> --password <app-password>` (One time)``
7777+`` - **Init:** `tangled repo create <name> --json` -> `git remote add tangled <url>` ``
7878+``- **Issues:** `tangled issue list --json title,number` (Uses current dir context)``
7979+8080+## **6\. Implementation Roadmap**
8181+8282+### **Phase 1: Context & Auth (The "Plumbing")**
8383+8484+1. **Auth Storage:** Store the AT Proto session in conf.
8585+2. **Context Resolver:**
8686+ - Implement a helper getRepoFromContext().
8787+ - It calls git remote get-url origin.
8888+ - Regex matches the Tangled DID from the URL.
8989+ - Returns the repo param required by XRPC.
9090+9191+### **Phase 2: CRUD Commands (The "Porcelain")**
9292+9393+1. **repo create:**
9494+ - Creates record via XRPC.
9595+ - **Auto-config:** Runs git init and git remote add if local dir is empty.
9696+2. **issue list/create:**
9797+ - Uses getRepoFromContext() so the user just types tangled issue create.
9898+9999+### **Phase 3: The "Machine" View**
100100+101101+1. Implement a generic Formatter class.
102102+ - Formatter.render(data, { json: true, fields: \['id', 'name'\] })
103103+ - If json: pick fields, JSON.stringify.
104104+ - If human: use cli-table3.
105105+106106+## **7\. Example: The Context Pattern**
107107+108108+This is how we solve the usability problem (and the LLM hallucination problem).
109109+`// src/lib/context.ts`
110110+`import simpleGit from 'simple-git';`
111111+112112+`export async function resolveRepoContext(explicitFlag?: string): Promise<string> {`
113113+`// 1. If user passed --repo flag, use it`
114114+`if (explicitFlag) return explicitFlag;`
115115+116116+`// 2. Try to infer from local git config`
117117+`const git = simpleGit();`
118118+`const isRepo = await git.checkIsRepo();`
119119+120120+`if (isRepo) {`
121121+`const remotes = await git.getRemotes(true);`
122122+`const tangledRemote = remotes.find(r => r.name === 'tangled' || r.name === 'origin');`
123123+124124+ `if (tangledRemote) {`
125125+ `// Logic to extract DID from URL (e.g., [https://tangled.sh/did:plc:123/repo](https://tangled.sh/did:plc:123/repo))`
126126+ `const did = extractDidFromUrl(tangledRemote.refs.fetch);`
127127+ `if (did) return did;`
128128+ `}`
129129+130130+`}`
131131+132132+`throw new Error("Could not infer repository context. Please use --repo <did> or run this inside a Tangled repository.");`
133133+`}`
134134+135135+### **Summary of Improvements**
136136+137137+- **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).
138138+- **Filtered JSON:** Saves tokens for LLM context windows.
139139+- **Git Config Integration:** Treats the local .git folder as a database of configuration, reducing the need for environment variables or complex flags.