tlc-claude-code 1.8.5 → 2.0.1
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.
- package/.claude/commands/tlc/bootstrap.md +77 -0
- package/.claude/commands/tlc/build.md +20 -6
- package/.claude/commands/tlc/recall.md +87 -0
- package/.claude/commands/tlc/remember.md +71 -0
- package/CLAUDE.md +84 -201
- package/dashboard-web/dist/assets/index-Uhc49PE-.css +1 -0
- package/dashboard-web/dist/assets/index-W36XHPC5.js +431 -0
- package/dashboard-web/dist/assets/index-W36XHPC5.js.map +1 -0
- package/dashboard-web/dist/index.html +2 -2
- package/package.json +1 -1
- package/server/index.js +13 -0
- package/server/lib/bug-writer.js +204 -0
- package/server/lib/bug-writer.test.js +279 -0
- package/server/lib/claude-cascade.js +247 -0
- package/server/lib/claude-cascade.test.js +245 -0
- package/server/lib/context-injection.js +121 -0
- package/server/lib/context-injection.test.js +340 -0
- package/server/lib/conversation-chunker.js +320 -0
- package/server/lib/conversation-chunker.test.js +573 -0
- package/server/lib/embedding-client.js +160 -0
- package/server/lib/embedding-client.test.js +243 -0
- package/server/lib/global-config.js +198 -0
- package/server/lib/global-config.test.js +288 -0
- package/server/lib/inherited-search.js +184 -0
- package/server/lib/inherited-search.test.js +343 -0
- package/server/lib/memory-api.js +180 -0
- package/server/lib/memory-api.test.js +322 -0
- package/server/lib/memory-hooks-capture.test.js +350 -0
- package/server/lib/memory-hooks.js +101 -0
- package/server/lib/memory-inheritance.js +179 -0
- package/server/lib/memory-inheritance.test.js +360 -0
- package/server/lib/plan-writer.js +196 -0
- package/server/lib/plan-writer.test.js +298 -0
- package/server/lib/project-scanner.js +267 -0
- package/server/lib/project-scanner.test.js +389 -0
- package/server/lib/project-status.js +302 -0
- package/server/lib/project-status.test.js +470 -0
- package/server/lib/projects-registry.js +237 -0
- package/server/lib/projects-registry.test.js +275 -0
- package/server/lib/recall-command.js +207 -0
- package/server/lib/recall-command.test.js +306 -0
- package/server/lib/remember-command.js +96 -0
- package/server/lib/remember-command.test.js +265 -0
- package/server/lib/rich-capture.js +221 -0
- package/server/lib/rich-capture.test.js +312 -0
- package/server/lib/roadmap-api.js +200 -0
- package/server/lib/roadmap-api.test.js +318 -0
- package/server/lib/semantic-recall.js +242 -0
- package/server/lib/semantic-recall.test.js +446 -0
- package/server/lib/setup-generator.js +315 -0
- package/server/lib/setup-generator.test.js +303 -0
- package/server/lib/test-inventory.js +112 -0
- package/server/lib/test-inventory.test.js +360 -0
- package/server/lib/vector-indexer.js +246 -0
- package/server/lib/vector-indexer.test.js +459 -0
- package/server/lib/vector-store.js +260 -0
- package/server/lib/vector-store.test.js +706 -0
- package/server/lib/workspace-api.js +811 -0
- package/server/lib/workspace-api.test.js +743 -0
- package/server/lib/workspace-bootstrap.js +164 -0
- package/server/lib/workspace-bootstrap.test.js +503 -0
- package/server/lib/workspace-context.js +129 -0
- package/server/lib/workspace-context.test.js +214 -0
- package/server/lib/workspace-detector.js +162 -0
- package/server/lib/workspace-detector.test.js +193 -0
- package/server/lib/workspace-init.js +307 -0
- package/server/lib/workspace-init.test.js +244 -0
- package/server/lib/workspace-snapshot.js +236 -0
- package/server/lib/workspace-snapshot.test.js +444 -0
- package/server/lib/workspace-watcher.js +162 -0
- package/server/lib/workspace-watcher.test.js +257 -0
- package/server/package-lock.json +552 -0
- package/server/package.json +4 -0
- package/dashboard-web/dist/assets/index-B1I_joSL.js +0 -393
- package/dashboard-web/dist/assets/index-B1I_joSL.js.map +0 -1
- package/dashboard-web/dist/assets/index-Trhg1C1Y.css +0 -1
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# /tlc:bootstrap - Workspace Bootstrap
|
|
2
|
+
|
|
3
|
+
Clone all repos and set up a workspace on a new machine.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
/tlc:bootstrap
|
|
9
|
+
/tlc:bootstrap --dry-run
|
|
10
|
+
/tlc:bootstrap --skip-install
|
|
11
|
+
/tlc:bootstrap --parallel 5
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## What This Does
|
|
15
|
+
|
|
16
|
+
1. Reads `projects.json` from the workspace root
|
|
17
|
+
2. Clones all repos that aren't already present
|
|
18
|
+
3. Checks out the correct branch per repo
|
|
19
|
+
4. Installs dependencies (npm install, pip install, etc.)
|
|
20
|
+
5. Rebuilds vector indexes from memory text files
|
|
21
|
+
6. Reports summary
|
|
22
|
+
|
|
23
|
+
## Options
|
|
24
|
+
|
|
25
|
+
| Flag | Default | Description |
|
|
26
|
+
|------|---------|-------------|
|
|
27
|
+
| `--dry-run` | false | Show what would be cloned without doing it |
|
|
28
|
+
| `--skip-install` | false | Skip dependency installation |
|
|
29
|
+
| `--parallel N` | 3 | Number of concurrent clones |
|
|
30
|
+
|
|
31
|
+
## Process
|
|
32
|
+
|
|
33
|
+
### Step 1: Read Registry
|
|
34
|
+
|
|
35
|
+
Load `projects.json` from workspace root. If it doesn't exist:
|
|
36
|
+
```
|
|
37
|
+
No projects.json found.
|
|
38
|
+
|
|
39
|
+
Run /tlc:init to initialize this workspace, or create projects.json manually.
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Step 2: Clone Repos
|
|
43
|
+
|
|
44
|
+
For each project in the registry:
|
|
45
|
+
- If directory already exists with `.git/` → skip
|
|
46
|
+
- Otherwise → `git clone <url> <path>`
|
|
47
|
+
- Checkout the `defaultBranch`
|
|
48
|
+
|
|
49
|
+
### Step 3: Install Dependencies
|
|
50
|
+
|
|
51
|
+
For each cloned repo (unless `--skip-install`):
|
|
52
|
+
- Node.js (`package.json`) → `npm install`
|
|
53
|
+
- Python (`requirements.txt`) → `pip install -r requirements.txt`
|
|
54
|
+
- Go (`go.mod`) → `go mod download`
|
|
55
|
+
|
|
56
|
+
### Step 4: Rebuild Vectors
|
|
57
|
+
|
|
58
|
+
If Ollama is available:
|
|
59
|
+
- Pull embedding model: `ollama pull mxbai-embed-large`
|
|
60
|
+
- Rebuild vector indexes from memory text files
|
|
61
|
+
|
|
62
|
+
### Step 5: Summary
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
Workspace bootstrap complete:
|
|
66
|
+
Cloned: 3 repos
|
|
67
|
+
Skipped: 1 (already present)
|
|
68
|
+
Failed: 0
|
|
69
|
+
|
|
70
|
+
All repos ready. Run /tlc:progress to see project status.
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Related Commands
|
|
74
|
+
|
|
75
|
+
- `/tlc:init` — Initialize TLC in an existing project
|
|
76
|
+
- `/tlc:recall` — Search workspace memory
|
|
77
|
+
- `/tlc:progress` — Check project status
|
|
@@ -636,21 +636,35 @@ git add src/auth/login.ts tests/auth/login.test.ts
|
|
|
636
636
|
git commit -m "feat: {task-title} - phase {N}"
|
|
637
637
|
```
|
|
638
638
|
|
|
639
|
-
#### 7e. Mark Task Complete (
|
|
639
|
+
#### 7e. Mark Task Complete in PLAN.md (MANDATORY)
|
|
640
640
|
|
|
641
|
-
|
|
641
|
+
**Always update the task marker in PLAN.md after tests pass.** This is not optional.
|
|
642
642
|
|
|
643
|
-
1.
|
|
644
|
-
2.
|
|
645
|
-
3.
|
|
643
|
+
1. Open `.planning/phases/{phase}-PLAN.md`
|
|
644
|
+
2. Find the task heading: `### Task {N}: {title} [ ]` (or `[>@{user}]`)
|
|
645
|
+
3. Update the marker:
|
|
646
|
+
- Single-user: `[ ]` → `[x]`
|
|
647
|
+
- Multi-user: `[>@{user}]` → `[x@{user}]`
|
|
648
|
+
4. Include the plan update in the same commit (step 7d) or commit separately
|
|
649
|
+
|
|
650
|
+
**Example:**
|
|
651
|
+
```
|
|
652
|
+
### Task 3: Tabbed Project Detail Page [ ] ← before
|
|
653
|
+
### Task 3: Tabbed Project Detail Page [x] ← after
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
This keeps the plan file as the single source of truth for task status. Do NOT wait until the end of the phase — mark each task done immediately after its tests pass.
|
|
657
|
+
|
|
658
|
+
If in multi-user mode, also push to share progress with team.
|
|
646
659
|
|
|
647
660
|
#### 7f. Move to next task
|
|
648
|
-
Repeat 7a-
|
|
661
|
+
Repeat 7a-7e for each task in the phase.
|
|
649
662
|
|
|
650
663
|
**Critical Rules:**
|
|
651
664
|
- Implement **one task at a time**
|
|
652
665
|
- Run tests **after each task**
|
|
653
666
|
- Commit **after each passing task**
|
|
667
|
+
- **Mark task `[x]` in PLAN.md after each passing task** — never defer this
|
|
654
668
|
- Do NOT batch — sequential execution catches issues early
|
|
655
669
|
|
|
656
670
|
### Step 8: Verify All Tests Pass (Green)
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# /tlc:recall - Semantic Memory Search
|
|
2
|
+
|
|
3
|
+
Search your project's memory by meaning. "What did we decide about X?"
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
/tlc:recall <query>
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## What This Does
|
|
12
|
+
|
|
13
|
+
1. Searches the vector memory store using semantic similarity
|
|
14
|
+
2. Ranks results by: similarity (50%) + recency (25%) + project relevance (25%)
|
|
15
|
+
3. Returns top-5 results with scores, types, and excerpts
|
|
16
|
+
4. Permanent memories get a 1.2x boost in scoring
|
|
17
|
+
|
|
18
|
+
## Options
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
/tlc:recall database architecture
|
|
22
|
+
/tlc:recall --scope workspace authentication approach
|
|
23
|
+
/tlc:recall --type decision deployment strategy
|
|
24
|
+
/tlc:recall --limit 10 API design
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
| Flag | Values | Default | Description |
|
|
28
|
+
|------|--------|---------|-------------|
|
|
29
|
+
| `--scope` | `project`, `workspace`, `global` | `project` | Search scope (auto-widens if few results) |
|
|
30
|
+
| `--type` | `decision`, `gotcha`, `conversation`, `all` | `all` | Filter by memory type |
|
|
31
|
+
| `--limit` | 1-20 | 5 | Maximum results |
|
|
32
|
+
|
|
33
|
+
## Process
|
|
34
|
+
|
|
35
|
+
### Step 1: Parse Query
|
|
36
|
+
|
|
37
|
+
Extract the search query and any flags from the arguments.
|
|
38
|
+
|
|
39
|
+
### Step 2: Semantic Search
|
|
40
|
+
|
|
41
|
+
1. Generate embedding for the query
|
|
42
|
+
2. Search vector store for nearest neighbors
|
|
43
|
+
3. Score results: `similarity * 0.5 + recency * 0.25 + projectRelevance * 0.25`
|
|
44
|
+
4. Boost permanent memories by 1.2x
|
|
45
|
+
5. Apply scope, type, and limit filters
|
|
46
|
+
|
|
47
|
+
### Step 3: Display Results
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
## Memory Recall: "database architecture"
|
|
51
|
+
|
|
52
|
+
1. **Use SQLite for vector storage** (92% match) [decision]
|
|
53
|
+
Date: 2026-02-09
|
|
54
|
+
Zero infrastructure, single file, ships with TLC
|
|
55
|
+
Source: memory/decisions/2026-02-09-use-sqlite-for-vector-storage.md
|
|
56
|
+
|
|
57
|
+
2. **[PERMANENT] Always use WAL mode** (87% match) [gotcha]
|
|
58
|
+
Date: 2026-02-08
|
|
59
|
+
WAL mode required for concurrent reads during indexing
|
|
60
|
+
Source: memory/gotchas/2026-02-08-always-use-wal-mode.md
|
|
61
|
+
|
|
62
|
+
3. **Database migration strategy** (81% match) [conversation]
|
|
63
|
+
Date: 2026-02-07
|
|
64
|
+
Discussed migration approach, decided on schema-first
|
|
65
|
+
Source: memory/conversations/2026-02-07-database-migration.md
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Step 4: Fallback
|
|
69
|
+
|
|
70
|
+
If the vector store is unavailable (Ollama not running, no embeddings):
|
|
71
|
+
- Falls back to keyword-based text search
|
|
72
|
+
- Shows a notice that semantic search is unavailable
|
|
73
|
+
|
|
74
|
+
## Examples
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
/tlc:recall what database should we use
|
|
78
|
+
/tlc:recall deployment strategy --scope global
|
|
79
|
+
/tlc:recall --type decision authentication
|
|
80
|
+
/tlc:recall testing approach --limit 10
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Graceful Degradation
|
|
84
|
+
|
|
85
|
+
- **No Ollama**: Falls back to text search with keyword matching
|
|
86
|
+
- **Empty store**: Shows "No memories indexed yet" with guidance to run `/tlc:remember`
|
|
87
|
+
- **No results**: Shows "No matching memories" with search suggestions
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# /tlc:remember - Permanent Memory Capture
|
|
2
|
+
|
|
3
|
+
Save important context permanently so it survives context compaction.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
/tlc:remember <text>
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or without arguments to capture recent exchanges:
|
|
12
|
+
```
|
|
13
|
+
/tlc:remember
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## What This Does
|
|
17
|
+
|
|
18
|
+
1. Captures the provided text (or recent conversation) as a **permanent memory**
|
|
19
|
+
2. Writes it to `memory/conversations/` with `[PERMANENT]` prefix and `permanent: true` frontmatter
|
|
20
|
+
3. Indexes it in the vector store for semantic recall
|
|
21
|
+
4. Permanent memories are **never pruned** and get a **1.2x boost** in recall scoring
|
|
22
|
+
|
|
23
|
+
## Process
|
|
24
|
+
|
|
25
|
+
### Step 1: Determine What to Remember
|
|
26
|
+
|
|
27
|
+
**With text argument:**
|
|
28
|
+
- Use the provided text as the memory content
|
|
29
|
+
|
|
30
|
+
**Without arguments:**
|
|
31
|
+
- Capture the recent conversation exchanges from the current session
|
|
32
|
+
- Generate a summary from the exchanges
|
|
33
|
+
|
|
34
|
+
### Step 2: Write Permanent Memory
|
|
35
|
+
|
|
36
|
+
1. Create a conversation chunk with `permanent: true`
|
|
37
|
+
2. Title prefixed with `[PERMANENT]`
|
|
38
|
+
3. Write to `memory/conversations/YYYY-MM-DD-{slug}.md`
|
|
39
|
+
4. YAML frontmatter includes `permanent: true`
|
|
40
|
+
|
|
41
|
+
### Step 3: Index in Vector Store
|
|
42
|
+
|
|
43
|
+
1. Generate embedding for the memory content
|
|
44
|
+
2. Store in vector database with `permanent = 1` flag
|
|
45
|
+
3. This memory will be boosted 1.2x in future recall searches
|
|
46
|
+
|
|
47
|
+
### Step 4: Confirm
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
Remembered permanently: {summary}
|
|
51
|
+
File: memory/conversations/2026-02-09-always-use-utc.md
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Examples
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
/tlc:remember Always use UTC timestamps in the database, never local time
|
|
58
|
+
|
|
59
|
+
/tlc:remember The API rate limit is 100 requests per minute per user
|
|
60
|
+
|
|
61
|
+
/tlc:remember
|
|
62
|
+
(captures recent conversation context)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## When to Use
|
|
66
|
+
|
|
67
|
+
- **Architecture decisions** that should never be forgotten
|
|
68
|
+
- **Environment gotchas** that are hard to rediscover
|
|
69
|
+
- **Team conventions** that must be consistent
|
|
70
|
+
- **Critical constraints** from stakeholders
|
|
71
|
+
- Any knowledge you want to survive across sessions and context compaction
|
package/CLAUDE.md
CHANGED
|
@@ -1,201 +1,84 @@
|
|
|
1
|
-
# CLAUDE.md
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
| "claim", "I'll take this" | `/tlc:claim` | Editing plan markers manually |
|
|
86
|
-
| "release", "can't finish this" | `/tlc:release` | Editing plan markers manually |
|
|
87
|
-
| "who's working", "team" | `/tlc:who` | Guessing team state |
|
|
88
|
-
| "verify", "check my work" | `/tlc:verify` | Ad-hoc verification |
|
|
89
|
-
| "complete", "milestone done" | `/tlc:complete` | Tagging without TLC |
|
|
90
|
-
| "quality", "test quality" | `/tlc:quality` | Guessing quality metrics |
|
|
91
|
-
| "outdated", "dependencies" | `/tlc:outdated` | Running npm outdated manually |
|
|
92
|
-
| "cleanup", "fix standards" | `/tlc:cleanup` | Fixing issues without tracking |
|
|
93
|
-
| "ci", "github actions", "pipeline" | `/tlc:ci` | Writing CI config from scratch |
|
|
94
|
-
| "export", "cursor rules", "agents.md" | `/tlc:export` | Writing tool configs manually |
|
|
95
|
-
| "models", "llm", "providers" | `/tlc:llm` | Ignoring router config |
|
|
96
|
-
| "issues", "import issues" | `/tlc:issues` | Manual issue tracking |
|
|
97
|
-
| "checklist", "full check" | `/tlc:checklist` | Ad-hoc project review |
|
|
98
|
-
| "quick task", "small fix" | `/tlc:quick` | Coding without tests |
|
|
99
|
-
| "dashboard", "start dashboard", "dashboard container" | `/tlc:dashboard` | Running docker-compose manually |
|
|
100
|
-
|
|
101
|
-
### Before ANY work — run `/tlc`
|
|
102
|
-
|
|
103
|
-
**MANDATORY.** This detects state, syncs if needed, shows what's next.
|
|
104
|
-
|
|
105
|
-
### How to invoke TLC commands
|
|
106
|
-
|
|
107
|
-
Use the **Skill tool**: `Skill(skill="tlc:plan")`, `Skill(skill="tlc:build")`, etc.
|
|
108
|
-
|
|
109
|
-
When a command is invoked, its `.md` file gets loaded as instructions. **Follow those instructions step by step.** Do not skip steps. Do not improvise your own version.
|
|
110
|
-
|
|
111
|
-
### TLC file-based system
|
|
112
|
-
|
|
113
|
-
| Purpose | TLC Location |
|
|
114
|
-
|---------|--------------|
|
|
115
|
-
| Project overview | `PROJECT.md` |
|
|
116
|
-
| Roadmap & phases | `.planning/ROADMAP.md` |
|
|
117
|
-
| Phase plans | `.planning/phases/{N}-PLAN.md` |
|
|
118
|
-
| Task status | Markers in PLAN.md: `[ ]`, `[>@user]`, `[x@user]` |
|
|
119
|
-
| Bugs/feedback | `.planning/BUGS.md` |
|
|
120
|
-
| Test status | `.planning/phases/{N}-TESTS.md` |
|
|
121
|
-
| Config | `.tlc.json` |
|
|
122
|
-
|
|
123
|
-
## Test-First Development (Non-Negotiable)
|
|
124
|
-
|
|
125
|
-
All implementation follows **Red → Green → Refactor**:
|
|
126
|
-
|
|
127
|
-
1. **Red**: Write failing tests that define expected behavior
|
|
128
|
-
2. **Green**: Write minimum code to make tests pass
|
|
129
|
-
3. **Refactor**: Clean up while keeping tests green
|
|
130
|
-
|
|
131
|
-
Tests are written BEFORE implementation, not after.
|
|
132
|
-
|
|
133
|
-
**This means:**
|
|
134
|
-
- Do NOT write a function and then add tests after
|
|
135
|
-
- Do NOT "implement first and test later"
|
|
136
|
-
- Do NOT skip tests for "simple" code
|
|
137
|
-
- The `/tlc:build` command enforces this — use it instead of coding directly
|
|
138
|
-
|
|
139
|
-
**If the user says "implement X" or "build X" or "add X":**
|
|
140
|
-
1. Check `/tlc:progress` for current state
|
|
141
|
-
2. If no plan exists → run `/tlc:plan` first
|
|
142
|
-
3. If plan exists → run `/tlc:build` which writes tests first
|
|
143
|
-
4. Never jump straight to implementation
|
|
144
|
-
|
|
145
|
-
## Context Management
|
|
146
|
-
|
|
147
|
-
**Use multiple sessions/agents for large tasks.** When working in overdrive mode or across workspaces:
|
|
148
|
-
- Use the `Task` tool to spawn sub-agents for independent work (research, testing, building)
|
|
149
|
-
- Keep the main conversation focused on orchestration and decisions
|
|
150
|
-
- Delegate file-heavy operations (reading many files, running test suites) to sub-agents
|
|
151
|
-
- This prevents context window overflow, which causes crashes and lost work
|
|
152
|
-
- Especially critical in workspace mode where multiple repos are involved
|
|
153
|
-
|
|
154
|
-
**Signs you need to delegate:** If you've read 15+ files, run 10+ commands, or the conversation is getting long — spawn a sub-agent for the next chunk of work.
|
|
155
|
-
|
|
156
|
-
## After TLC Updates
|
|
157
|
-
|
|
158
|
-
If TLC command files are updated, re-read them before executing. Check version in `package.json`.
|
|
159
|
-
|
|
160
|
-
## Multi-User Collaboration
|
|
161
|
-
|
|
162
|
-
When working with teammates:
|
|
163
|
-
- Claim tasks before starting: `/tlc:claim`
|
|
164
|
-
- Release if blocked: `/tlc:release`
|
|
165
|
-
- Check team status: `/tlc:who`
|
|
166
|
-
- Pull before claiming: `git pull`
|
|
167
|
-
- Push after claiming: `git push`
|
|
168
|
-
|
|
169
|
-
## Git Commits
|
|
170
|
-
|
|
171
|
-
**⛔ NEVER ADD CO-AUTHORED-BY LINES TO COMMITS ⛔**
|
|
172
|
-
|
|
173
|
-
- NO `Co-Authored-By: Claude`
|
|
174
|
-
- NO `Co-Authored-By: Anthropic`
|
|
175
|
-
- NO co-authoring of any kind
|
|
176
|
-
- The USER is the author. Claude is a tool, not an author.
|
|
177
|
-
|
|
178
|
-
**ALWAYS ask before `git push`.** Never push to remote without explicit user approval.
|
|
179
|
-
|
|
180
|
-
---
|
|
181
|
-
|
|
182
|
-
<!-- TLC-STANDARDS -->
|
|
183
|
-
|
|
184
|
-
## Code Quality (TLC)
|
|
185
|
-
|
|
186
|
-
This project follows TLC (Test-Led Coding) code quality standards. See [CODING-STANDARDS.md](./CODING-STANDARDS.md) for detailed guidelines.
|
|
187
|
-
|
|
188
|
-
### Quick Reference
|
|
189
|
-
|
|
190
|
-
**Module Structure:** Code lives in `server/lib/` - each module is a self-contained `.js` file with corresponding `.test.js` test file.
|
|
191
|
-
|
|
192
|
-
### Key Rules
|
|
193
|
-
|
|
194
|
-
1. **Test-first development** - Tests are written BEFORE implementation
|
|
195
|
-
2. **No hardcoded URLs or config** - Use environment variables
|
|
196
|
-
3. **JSDoc required** - Document all exported functions
|
|
197
|
-
4. **Paired test files** - Every `module.js` has a `module.test.js`
|
|
198
|
-
|
|
199
|
-
### Standards Reference
|
|
200
|
-
|
|
201
|
-
For complete standards including file naming, import rules, error handling patterns, and service design guidelines, see [CODING-STANDARDS.md](./CODING-STANDARDS.md).
|
|
1
|
+
# CLAUDE.md — TLC Project
|
|
2
|
+
|
|
3
|
+
> **This is a TLC project. All work goes through `/tlc` commands. Run `/tlc` first.**
|
|
4
|
+
|
|
5
|
+
## Rules (Enforced by hooks — violations are blocked)
|
|
6
|
+
|
|
7
|
+
1. **Tests before code.** Always. Red → Green → Refactor. Use `/tlc:build`.
|
|
8
|
+
2. **Plans go in files.** Use `/tlc:plan` → writes to `.planning/phases/`. Never plan in chat.
|
|
9
|
+
3. **No direct implementation.** User says "build X" → run `/tlc:progress` then `/tlc:build`.
|
|
10
|
+
4. **No Co-Authored-By in commits.** The user is the author. Claude is a tool.
|
|
11
|
+
5. **Ask before `git push`.** Never push without explicit approval.
|
|
12
|
+
|
|
13
|
+
## Command Dispatch
|
|
14
|
+
|
|
15
|
+
When the user says X → invoke `Skill(skill="tlc:...")`:
|
|
16
|
+
|
|
17
|
+
| User Says | Run This |
|
|
18
|
+
|-----------|----------|
|
|
19
|
+
| "plan", "break this down" | `/tlc:plan` |
|
|
20
|
+
| "build", "implement", "add feature" | `/tlc:build` |
|
|
21
|
+
| "review", "check code" | `/tlc:review` |
|
|
22
|
+
| "status", "what's next", "where are we" | `/tlc:progress` |
|
|
23
|
+
| "discuss", "talk about approach" | `/tlc:discuss` |
|
|
24
|
+
| "test", "run tests" | `/tlc:status` |
|
|
25
|
+
| "fix tests", "tests failing" | `/tlc:autofix` |
|
|
26
|
+
| "refactor", "clean up" | `/tlc:refactor` |
|
|
27
|
+
| "deploy", "set up server" | `/tlc:deploy` |
|
|
28
|
+
| "coverage", "what's untested" | `/tlc:coverage` |
|
|
29
|
+
| "edge cases", "more tests" | `/tlc:edge-cases` |
|
|
30
|
+
| "security", "audit" | `/tlc:security` |
|
|
31
|
+
| "docs", "documentation" | `/tlc:docs` |
|
|
32
|
+
| "new project" | `/tlc:new-project` |
|
|
33
|
+
| "init", "add tlc" | `/tlc:init` |
|
|
34
|
+
| "configure", "setup" | `/tlc:config` |
|
|
35
|
+
| "bug", "found a bug" | `/tlc:bug` |
|
|
36
|
+
| "claim", "I'll take this" | `/tlc:claim` |
|
|
37
|
+
| "release", "can't finish" | `/tlc:release` |
|
|
38
|
+
| "who's working", "team" | `/tlc:who` |
|
|
39
|
+
| "verify", "check my work" | `/tlc:verify` |
|
|
40
|
+
| "complete", "milestone done" | `/tlc:complete` |
|
|
41
|
+
| "quality", "test quality" | `/tlc:quality` |
|
|
42
|
+
| "outdated", "dependencies" | `/tlc:outdated` |
|
|
43
|
+
| "cleanup", "fix standards" | `/tlc:cleanup` |
|
|
44
|
+
| "ci", "github actions" | `/tlc:ci` |
|
|
45
|
+
| "export", "cursor rules" | `/tlc:export` |
|
|
46
|
+
| "models", "llm", "providers" | `/tlc:llm` |
|
|
47
|
+
| "issues", "import issues" | `/tlc:issues` |
|
|
48
|
+
| "checklist" | `/tlc:checklist` |
|
|
49
|
+
| "quick task", "small fix" | `/tlc:quick` |
|
|
50
|
+
| "dashboard" | `/tlc:dashboard` |
|
|
51
|
+
| "review PR" | `/tlc:review-pr` |
|
|
52
|
+
|
|
53
|
+
## TLC File System
|
|
54
|
+
|
|
55
|
+
| Purpose | Location |
|
|
56
|
+
|---------|----------|
|
|
57
|
+
| Project overview | `PROJECT.md` |
|
|
58
|
+
| Roadmap & phases | `.planning/ROADMAP.md` |
|
|
59
|
+
| Phase plans | `.planning/phases/{N}-PLAN.md` |
|
|
60
|
+
| Task status | `[ ]` / `[>@user]` / `[x@user]` markers in plan files |
|
|
61
|
+
| Bugs/feedback | `.planning/BUGS.md` |
|
|
62
|
+
| Config | `.tlc.json` |
|
|
63
|
+
|
|
64
|
+
## LLM Router
|
|
65
|
+
|
|
66
|
+
Check `.tlc.json` for `router.capabilities` before routing work. If no config exists, Claude handles everything. Commands: `/tlc:llm status`, `/tlc:llm config`, `/tlc:llm test`.
|
|
67
|
+
|
|
68
|
+
## Context Management
|
|
69
|
+
|
|
70
|
+
Use `Task` tool to spawn sub-agents for independent work. Keep main conversation for orchestration. Delegate when you've read 15+ files or run 10+ commands.
|
|
71
|
+
|
|
72
|
+
## Multi-User Collaboration
|
|
73
|
+
|
|
74
|
+
Claim tasks before starting: `/tlc:claim`. Release if blocked: `/tlc:release`. Check team: `/tlc:who`. Pull before claiming, push after.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
<!-- TLC-STANDARDS -->
|
|
79
|
+
|
|
80
|
+
## Code Quality (TLC)
|
|
81
|
+
|
|
82
|
+
See [CODING-STANDARDS.md](./CODING-STANDARDS.md) for full standards.
|
|
83
|
+
|
|
84
|
+
**Quick reference:** Modules in `server/lib/`. Each `module.js` has `module.test.js`. JSDoc required on exports. No hardcoded URLs — use env vars.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--color-bg-primary: #0a0a0b;--color-bg-secondary: #141416;--color-bg-tertiary: #1e1e21;--color-bg-elevated: #252529;--color-text-primary: #fafafa;--color-text-secondary: #a1a1aa;--color-text-muted: #71717a;--color-border: #27272a;--color-border-hover: #3f3f46;--color-accent: #3b82f6;--color-accent-hover: #2563eb;--color-success: #22c55e;--color-warning: #eab308;--color-error: #ef4444;--color-info: #06b6d4;--space-0: 0;--space-1: .25rem;--space-2: .5rem;--space-3: .75rem;--space-4: 1rem;--space-5: 1.25rem;--space-6: 1.5rem;--space-8: 2rem;--space-10: 2.5rem;--space-12: 3rem;--space-16: 4rem;--font-sans: "Inter", system-ui, -apple-system, sans-serif;--font-mono: "JetBrains Mono", ui-monospace, monospace;--text-xs: .75rem;--text-sm: .875rem;--text-base: 1rem;--text-lg: 1.125rem;--text-xl: 1.25rem;--text-2xl: 1.5rem;--text-3xl: 1.875rem;--font-normal: 400;--font-medium: 500;--font-semibold: 600;--font-bold: 700;--leading-tight: 1.25;--leading-normal: 1.5;--leading-relaxed: 1.625;--radius-sm: .25rem;--radius-md: .375rem;--radius-lg: .5rem;--radius-xl: .75rem;--radius-2xl: 1rem;--radius-full: 9999px;--shadow-sm: 0 1px 2px rgba(0, 0, 0, .3);--shadow-md: 0 4px 6px rgba(0, 0, 0, .3);--shadow-lg: 0 10px 15px rgba(0, 0, 0, .3);--shadow-xl: 0 20px 25px rgba(0, 0, 0, .3);--transition-fast: .1s ease;--transition-base: .2s ease;--transition-slow: .3s ease;--z-dropdown: 1000;--z-sticky: 1020;--z-modal: 1030;--z-popover: 1040;--z-tooltip: 1050;--z-toast: 1060;--sidebar-width: 240px;--sidebar-collapsed-width: 64px;--header-height: 56px;--mobile-nav-height: 64px}[data-theme=light]{--color-bg-primary: #ffffff;--color-bg-secondary: #f4f4f5;--color-bg-tertiary: #e4e4e7;--color-bg-elevated: #ffffff;--color-text-primary: #09090b;--color-text-secondary: #52525b;--color-text-muted: #a1a1aa;--color-border: #e4e4e7;--color-border-hover: #d4d4d8;--shadow-sm: 0 1px 2px rgba(0, 0, 0, .05);--shadow-md: 0 4px 6px rgba(0, 0, 0, .07);--shadow-lg: 0 10px 15px rgba(0, 0, 0, .1);--shadow-xl: 0 20px 25px rgba(0, 0, 0, .1)}@media(prefers-contrast:high){:root{--color-border: #52525b;--color-text-secondary: #d4d4d8}[data-theme=light]{--color-border: #71717a;--color-text-secondary: #3f3f46}}@media(prefers-reduced-motion:reduce){:root{--transition-fast: 0ms;--transition-base: 0ms;--transition-slow: 0ms}}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Inter,system-ui,sans-serif;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:JetBrains Mono,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}*,*:before,*:after{box-sizing:border-box}html{font-family:var(--font-sans);font-size:16px;line-height:var(--leading-normal);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body{margin:0;padding:0;background-color:var(--color-bg-primary);color:var(--color-text-primary)}:focus-visible{outline:2px solid var(--color-accent);outline-offset:2px}.skip-link{position:absolute;top:-40px;left:0;padding:var(--space-2) var(--space-4);background:var(--color-accent);color:#fff;z-index:var(--z-tooltip);transition:top var(--transition-fast)}.skip-link:focus{top:0}::-moz-selection{background-color:var(--color-accent);color:#fff}::selection{background-color:var(--color-accent);color:#fff}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:var(--color-bg-secondary)}::-webkit-scrollbar-thumb{background:var(--color-border-hover);border-radius:var(--radius-full)}::-webkit-scrollbar-thumb:hover{background:var(--color-text-muted)}.container{width:100%}@media(min-width:640px){.container{max-width:640px}}@media(min-width:768px){.container{max-width:768px}}@media(min-width:1024px){.container{max-width:1024px}}@media(min-width:1280px){.container{max-width:1280px}}@media(min-width:1536px){.container{max-width:1536px}}.btn{display:inline-flex;align-items:center;justify-content:center;gap:.5rem;border-radius:var(--radius-md);padding:.5rem 1rem;font-weight:500;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.btn:focus-visible{outline:2px solid transparent;outline-offset:2px;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000);--tw-ring-color: var(--color-accent);--tw-ring-offset-width: 2px}.btn:disabled{cursor:not-allowed;opacity:.5}.btn-primary{background-color:var(--color-accent);--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.btn-primary:hover{background-color:var(--color-accent-hover)}.btn-secondary{border-width:1px;border-color:var(--color-border);background-color:var(--color-bg-tertiary);color:var(--color-text-primary)}.btn-secondary:hover{background-color:var(--color-bg-elevated)}.btn-ghost{background-color:transparent;color:var(--color-text-secondary)}.btn-ghost:hover{background-color:var(--color-bg-tertiary);color:var(--color-text-primary)}.btn-danger{background-color:var(--color-error);--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.btn-danger:hover{opacity:.9}.btn-sm{padding:.375rem .75rem;font-size:.875rem;line-height:1.25rem}.btn-lg{padding:.75rem 1.5rem;font-size:1.125rem;line-height:1.75rem}.card{border-radius:var(--radius-lg);border-width:1px;border-color:var(--color-border);background-color:var(--color-bg-secondary)}.card-hover{cursor:pointer;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.card-hover:hover{border-color:var(--color-border-hover);background-color:var(--color-bg-tertiary)}.input{width:100%;padding:.5rem .75rem;border-radius:var(--radius-md);border-width:1px;border-color:var(--color-border);background-color:var(--color-bg-tertiary);color:var(--color-text-primary)}.input::-moz-placeholder{color:var(--color-text-muted)}.input::placeholder{color:var(--color-text-muted)}.input:focus{border-color:transparent;outline:2px solid transparent;outline-offset:2px;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000);--tw-ring-color: var(--color-accent)}.input:disabled{cursor:not-allowed;opacity:.5}.badge{display:inline-flex;align-items:center;gap:.25rem;border-radius:9999px;padding:.125rem .5rem;font-size:.75rem;line-height:1rem;font-weight:500}.badge-success{color:var(--color-success);background-color:color-mix(in srgb,var(--color-success) 20%,transparent)}.badge-warning{color:var(--color-warning);background-color:color-mix(in srgb,var(--color-warning) 20%,transparent)}.badge-error{color:var(--color-error);background-color:color-mix(in srgb,var(--color-error) 20%,transparent)}.badge-info{color:var(--color-info);background-color:color-mix(in srgb,var(--color-info) 20%,transparent)}.badge-neutral{background-color:var(--color-bg-tertiary);color:var(--color-text-secondary)}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.inset-x-0{left:0;right:0}.inset-y-0{top:0;bottom:0}.-right-2{right:-.5rem}.-top-1{top:-.25rem}.-top-10{top:-2.5rem}.bottom-0{bottom:0}.bottom-4{bottom:1rem}.left-0{left:0}.left-1\.5{left:.375rem}.left-2{left:.5rem}.left-3{left:.75rem}.left-4{left:1rem}.left-full{left:100%}.right-0{right:0}.right-2{right:.5rem}.right-3{right:.75rem}.right-4{right:1rem}.top-0{top:0}.top-1\/2{top:50%}.top-4{top:1rem}.z-10{z-index:10}.z-40{z-index:40}.z-50{z-index:50}.mx-4{margin-left:1rem;margin-right:1rem}.mx-auto{margin-left:auto;margin-right:auto}.-mb-px{margin-bottom:-1px}.-ml-2{margin-left:-.5rem}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.ml-1{margin-left:.25rem}.ml-10{margin-left:2.5rem}.ml-2{margin-left:.5rem}.ml-6{margin-left:1.5rem}.ml-9{margin-left:2.25rem}.ml-auto{margin-left:auto}.mr-2{margin-right:.5rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-6{margin-top:1.5rem}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.aspect-video{aspect-ratio:16 / 9}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-12{height:3rem}.h-14{height:3.5rem}.h-16{height:4rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-24{height:6rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-32{height:8rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-64{height:16rem}.h-8{height:2rem}.h-96{height:24rem}.h-\[18px\]{height:18px}.h-full{height:100%}.h-screen{height:100vh}.max-h-48{max-height:12rem}.max-h-60{max-height:15rem}.max-h-80{max-height:20rem}.max-h-96{max-height:24rem}.max-h-\[400px\]{max-height:400px}.max-h-\[80vh\]{max-height:80vh}.max-h-\[90vh\]{max-height:90vh}.min-h-\[100px\]{min-height:100px}.min-h-\[200px\]{min-height:200px}.min-h-\[400px\]{min-height:400px}.min-h-\[60vh\]{min-height:60vh}.w-1{width:.25rem}.w-1\.5{width:.375rem}.w-1\/3{width:33.333333%}.w-1\/4{width:25%}.w-10{width:2.5rem}.w-12{width:3rem}.w-16{width:4rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-24{width:6rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-3\/4{width:75%}.w-32{width:8rem}.w-4{width:1rem}.w-48{width:12rem}.w-5{width:1.25rem}.w-56{width:14rem}.w-6{width:1.5rem}.w-60{width:15rem}.w-64{width:16rem}.w-8{width:2rem}.w-96{width:24rem}.w-full{width:100%}.w-px{width:1px}.min-w-0{min-width:0px}.min-w-\[180px\]{min-width:180px}.min-w-\[18px\]{min-width:18px}.min-w-\[300px\]{min-width:300px}.max-w-2xl{max-width:42rem}.max-w-4xl{max-width:56rem}.max-w-\[400px\]{max-width:400px}.max-w-full{max-width:100%}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.max-w-xl{max-width:36rem}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.-translate-x-full{--tw-translate-x: -100%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-180{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-95{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize-none{resize:none}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-around{justify-content:space-around}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.divide-border>:not([hidden])~:not([hidden]){border-color:var(--color-border)}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-line{white-space:pre-line}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-none{border-radius:0}.rounded-l-lg{border-top-left-radius:var(--radius-lg);border-bottom-left-radius:var(--radius-lg)}.border{border-width:1px}.border-0{border-width:0px}.border-2{border-width:2px}.border-x{border-left-width:1px;border-right-width:1px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l{border-left-width:1px}.border-l-4{border-left-width:4px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-none{border-style:none}.border-border{border-color:var(--color-border)}.border-error{border-color:var(--color-error)}.border-info{border-color:var(--color-info)}.border-transparent{border-color:transparent}.border-warning{border-color:var(--color-warning)}.border-l-error{border-left-color:var(--color-error)}.border-l-info{border-left-color:var(--color-info)}.border-l-success{border-left-color:var(--color-success)}.border-l-warning{border-left-color:var(--color-warning)}.bg-accent{background-color:var(--color-accent)}.bg-bg-elevated{background-color:var(--color-bg-elevated)}.bg-bg-primary{background-color:var(--color-bg-primary)}.bg-bg-secondary{background-color:var(--color-bg-secondary)}.bg-bg-tertiary{background-color:var(--color-bg-tertiary)}.bg-black\/50{background-color:#00000080}.bg-black\/60{background-color:#0009}.bg-black\/80{background-color:#000c}.bg-border{background-color:var(--color-border)}.bg-error{background-color:var(--color-error)}.bg-info{background-color:var(--color-info)}.bg-success{background-color:var(--color-success)}.bg-text-muted{background-color:var(--color-text-muted)}.bg-transparent{background-color:transparent}.bg-warning{background-color:var(--color-warning)}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-16{padding-top:4rem;padding-bottom:4rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-0{padding-bottom:0}.pb-2{padding-bottom:.5rem}.pb-3{padding-bottom:.75rem}.pl-10{padding-left:2.5rem}.pl-2{padding-left:.5rem}.pl-3{padding-left:.75rem}.pl-8{padding-left:2rem}.pl-9{padding-left:2.25rem}.pr-10{padding-right:2.5rem}.pr-3{padding-right:.75rem}.pr-4{padding-right:1rem}.pr-8{padding-right:2rem}.pt-0{padding-top:0}.pt-2{padding-top:.5rem}.pt-3{padding-top:.75rem}.pt-4{padding-top:1rem}.pt-\[20vh\]{padding-top:20vh}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:JetBrains Mono,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.leading-6{line-height:1.5rem}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.text-accent{color:var(--color-accent)}.text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity, 1))}.text-error{color:var(--color-error)}.text-info{color:var(--color-info)}.text-success{color:var(--color-success)}.text-text-muted{color:var(--color-text-muted)}.text-text-primary{color:var(--color-text-primary)}.text-text-secondary{color:var(--color-text-secondary)}.text-warning{color:var(--color-warning)}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.line-through{text-decoration-line:line-through}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: var(--shadow-lg);--tw-shadow-colored: var(--shadow-lg);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.ring-2{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-sm{--tw-backdrop-blur: blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-300{transition-duration:.3s}.placeholder\:text-text-muted::-moz-placeholder{color:var(--color-text-muted)}.placeholder\:text-text-muted::placeholder{color:var(--color-text-muted)}.hover\:bg-accent-hover:hover{background-color:var(--color-accent-hover)}.hover\:bg-bg-tertiary:hover{background-color:var(--color-bg-tertiary)}.hover\:text-error:hover{color:var(--color-error)}.hover\:text-gray-300:hover{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.hover\:text-text-primary:hover{color:var(--color-text-primary)}.hover\:text-text-secondary:hover{color:var(--color-text-secondary)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-90:hover{opacity:.9}.hover\:shadow-md:hover{--tw-shadow: var(--shadow-md);--tw-shadow-colored: var(--shadow-md);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.hover\:shadow-sm:hover{--tw-shadow: var(--shadow-sm);--tw-shadow-colored: var(--shadow-sm);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.hover\:ring-2:hover{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:border-accent:focus{border-color:var(--color-accent)}.focus\:border-transparent:focus{border-color:transparent}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-1:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-accent:focus{--tw-ring-color: var(--color-accent)}.focus\:ring-error:focus{--tw-ring-color: var(--color-error)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}@media(min-width:640px){.sm\:block{display:block}.sm\:flex-row{flex-direction:row}}@media(min-width:768px){.md\:block{display:block}.md\:hidden{display:none}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media(min-width:1024px){.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}
|