tlc-claude-code 2.0.1 → 2.1.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.
- package/.claude/commands/tlc/deploy.md +194 -2
- package/.claude/commands/tlc/e2e-verify.md +214 -0
- package/.claude/commands/tlc/guard.md +191 -0
- package/.claude/commands/tlc/help.md +32 -0
- package/.claude/commands/tlc/init.md +73 -37
- package/.claude/commands/tlc/llm.md +19 -4
- package/.claude/commands/tlc/preflight.md +134 -0
- package/.claude/commands/tlc/review.md +17 -4
- package/.claude/commands/tlc/watchci.md +159 -0
- package/.claude/hooks/tlc-block-tools.sh +41 -0
- package/.claude/hooks/tlc-capture-exchange.sh +50 -0
- package/.claude/hooks/tlc-post-build.sh +38 -0
- package/.claude/hooks/tlc-post-push.sh +22 -0
- package/.claude/hooks/tlc-prompt-guard.sh +69 -0
- package/.claude/hooks/tlc-session-init.sh +123 -0
- package/CLAUDE.md +12 -0
- package/bin/install.js +171 -2
- package/bin/postinstall.js +45 -26
- package/dashboard-web/dist/assets/index-CdS5CHqu.css +1 -0
- package/dashboard-web/dist/assets/index-CwNPPVpg.js +483 -0
- package/dashboard-web/dist/assets/index-CwNPPVpg.js.map +1 -0
- package/dashboard-web/dist/index.html +2 -2
- package/docker-compose.dev.yml +18 -12
- package/package.json +3 -1
- package/server/index.js +228 -2
- package/server/lib/capture-bridge.js +242 -0
- package/server/lib/capture-bridge.test.js +363 -0
- package/server/lib/capture-guard.js +140 -0
- package/server/lib/capture-guard.test.js +182 -0
- package/server/lib/command-runner.js +159 -0
- package/server/lib/command-runner.test.js +92 -0
- package/server/lib/deploy/runners/dependency-runner.js +106 -0
- package/server/lib/deploy/runners/dependency-runner.test.js +148 -0
- package/server/lib/deploy/runners/secrets-runner.js +174 -0
- package/server/lib/deploy/runners/secrets-runner.test.js +127 -0
- package/server/lib/deploy/security-gates.js +11 -24
- package/server/lib/deploy/security-gates.test.js +9 -2
- package/server/lib/deploy-engine.js +182 -0
- package/server/lib/deploy-engine.test.js +147 -0
- package/server/lib/docker-api.js +137 -0
- package/server/lib/docker-api.test.js +202 -0
- package/server/lib/docker-client.js +297 -0
- package/server/lib/docker-client.test.js +308 -0
- package/server/lib/input-sanitizer.js +86 -0
- package/server/lib/input-sanitizer.test.js +117 -0
- package/server/lib/launchd-agent.js +225 -0
- package/server/lib/launchd-agent.test.js +185 -0
- package/server/lib/memory-api.js +3 -1
- package/server/lib/memory-api.test.js +3 -5
- package/server/lib/memory-bridge-e2e.test.js +160 -0
- package/server/lib/memory-committer.js +18 -4
- package/server/lib/memory-committer.test.js +21 -0
- package/server/lib/memory-hooks-capture.test.js +69 -4
- package/server/lib/memory-hooks-integration.test.js +98 -0
- package/server/lib/memory-hooks.js +42 -4
- package/server/lib/memory-store-adapter.js +105 -0
- package/server/lib/memory-store-adapter.test.js +141 -0
- package/server/lib/memory-wiring-e2e.test.js +93 -0
- package/server/lib/nginx-config.js +114 -0
- package/server/lib/nginx-config.test.js +82 -0
- package/server/lib/ollama-health.js +91 -0
- package/server/lib/ollama-health.test.js +74 -0
- package/server/lib/port-guard.js +44 -0
- package/server/lib/port-guard.test.js +65 -0
- package/server/lib/project-scanner.js +37 -2
- package/server/lib/project-scanner.test.js +152 -0
- package/server/lib/remember-command.js +2 -0
- package/server/lib/remember-command.test.js +23 -0
- package/server/lib/security/crypto-utils.test.js +2 -2
- package/server/lib/semantic-recall.js +1 -1
- package/server/lib/semantic-recall.test.js +17 -0
- package/server/lib/ssh-client.js +184 -0
- package/server/lib/ssh-client.test.js +127 -0
- package/server/lib/vps-api.js +184 -0
- package/server/lib/vps-api.test.js +208 -0
- package/server/lib/vps-bootstrap.js +124 -0
- package/server/lib/vps-bootstrap.test.js +79 -0
- package/server/lib/vps-monitor.js +126 -0
- package/server/lib/vps-monitor.test.js +98 -0
- package/server/lib/workspace-api.js +182 -1
- package/server/lib/workspace-api.test.js +474 -0
- package/server/package-lock.json +737 -0
- package/server/package.json +3 -0
- package/dashboard-web/dist/assets/index-Uhc49PE-.css +0 -1
- package/dashboard-web/dist/assets/index-W36XHPC5.js +0 -431
- package/dashboard-web/dist/assets/index-W36XHPC5.js.map +0 -1
|
@@ -262,9 +262,21 @@ All implementation follows **Red → Green → Refactor**:
|
|
|
262
262
|
3. **Refactor**: Clean up while keeping tests green
|
|
263
263
|
```
|
|
264
264
|
|
|
265
|
-
### 9b. Create Claude Settings
|
|
265
|
+
### 9b. Create Claude Settings with Permissions AND Hooks (Automatic)
|
|
266
266
|
|
|
267
|
-
**Always create `.claude/settings.json` during init.** This is not optional — TLC requires
|
|
267
|
+
**Always create `.claude/settings.json` during init.** This is not optional — TLC requires permissions for uninterrupted work AND hooks for the plugin system (enforcement, auto-verification, memory capture).
|
|
268
|
+
|
|
269
|
+
**Also create `.claude/hooks/` directory** and copy all hook scripts from the TLC package. The hooks power the plugin system — without them, TLC is commands-only with no automation.
|
|
270
|
+
|
|
271
|
+
Create `.claude/hooks/` with these scripts (copy from the installed TLC package at `~/.claude/hooks/` or from the npm package):
|
|
272
|
+
- `tlc-block-tools.sh` — Blocks non-TLC planning tools (EnterPlanMode, TaskCreate, etc.)
|
|
273
|
+
- `tlc-prompt-guard.sh` — Smart intent detection: parses user message for plan/build/fix/deploy intent and routes to the correct TLC command
|
|
274
|
+
- `tlc-session-init.sh` — Initializes TLC state and ensures server is running
|
|
275
|
+
- `tlc-post-push.sh` — Auto-triggers CI monitoring after `git push`
|
|
276
|
+
- `tlc-post-build.sh` — Auto-triggers guard + e2e verification after build/plan
|
|
277
|
+
- `tlc-capture-exchange.sh` — Captures conversations to team memory
|
|
278
|
+
|
|
279
|
+
Make all hook scripts executable: `chmod +x .claude/hooks/*.sh`
|
|
268
280
|
|
|
269
281
|
Create `.claude/settings.json`:
|
|
270
282
|
|
|
@@ -272,46 +284,70 @@ Create `.claude/settings.json`:
|
|
|
272
284
|
{
|
|
273
285
|
"permissions": {
|
|
274
286
|
"allow": [
|
|
275
|
-
"Bash(npm *)",
|
|
276
|
-
"Bash(
|
|
277
|
-
"Bash(
|
|
278
|
-
"Bash(
|
|
279
|
-
"Bash(
|
|
280
|
-
"Bash(
|
|
281
|
-
"Bash(
|
|
282
|
-
"Bash(
|
|
283
|
-
"Bash(
|
|
284
|
-
"Bash(wget *)",
|
|
285
|
-
"Bash(docker *)",
|
|
286
|
-
"Bash(docker-compose *)",
|
|
287
|
-
"Bash(pytest*)",
|
|
288
|
-
"Bash(python *)",
|
|
289
|
-
"Bash(pip *)",
|
|
290
|
-
"Bash(go *)",
|
|
291
|
-
"Bash(cargo *)",
|
|
292
|
-
"Bash(make *)",
|
|
293
|
-
"Bash(cat *)",
|
|
294
|
-
"Bash(ls *)",
|
|
295
|
-
"Bash(pwd*)",
|
|
296
|
-
"Bash(cd *)",
|
|
297
|
-
"Bash(mkdir *)",
|
|
298
|
-
"Bash(cp *)",
|
|
299
|
-
"Bash(mv *)",
|
|
300
|
-
"Bash(which *)",
|
|
301
|
-
"Bash(echo *)",
|
|
302
|
-
"Bash(jq *)",
|
|
303
|
-
"Bash(wc *)",
|
|
304
|
-
"Bash(head *)",
|
|
305
|
-
"Bash(tail *)",
|
|
306
|
-
"Bash(sort *)",
|
|
307
|
-
"Bash(uniq *)",
|
|
308
|
-
"Bash(xargs *)"
|
|
287
|
+
"Bash(npm *)", "Bash(npx *)", "Bash(node *)", "Bash(git *)",
|
|
288
|
+
"Bash(gh *)", "Bash(ssh *)", "Bash(scp *)", "Bash(rsync *)",
|
|
289
|
+
"Bash(curl *)", "Bash(wget *)", "Bash(docker *)", "Bash(docker-compose *)",
|
|
290
|
+
"Bash(pytest*)", "Bash(python *)", "Bash(pip *)", "Bash(go *)",
|
|
291
|
+
"Bash(cargo *)", "Bash(make *)", "Bash(cat *)", "Bash(ls *)",
|
|
292
|
+
"Bash(pwd*)", "Bash(cd *)", "Bash(mkdir *)", "Bash(cp *)",
|
|
293
|
+
"Bash(mv *)", "Bash(which *)", "Bash(echo *)", "Bash(jq *)",
|
|
294
|
+
"Bash(wc *)", "Bash(head *)", "Bash(tail *)", "Bash(sort *)",
|
|
295
|
+
"Bash(uniq *)", "Bash(xargs *)"
|
|
309
296
|
]
|
|
297
|
+
},
|
|
298
|
+
"hooks": {
|
|
299
|
+
"PreToolUse": [{
|
|
300
|
+
"matcher": "EnterPlanMode|TaskCreate|TaskUpdate|TaskList|TaskGet|ExitPlanMode",
|
|
301
|
+
"hooks": [{
|
|
302
|
+
"type": "command",
|
|
303
|
+
"command": "bash $CLAUDE_PROJECT_DIR/.claude/hooks/tlc-block-tools.sh",
|
|
304
|
+
"timeout": 5
|
|
305
|
+
}]
|
|
306
|
+
}],
|
|
307
|
+
"UserPromptSubmit": [{
|
|
308
|
+
"hooks": [{
|
|
309
|
+
"type": "command",
|
|
310
|
+
"command": "bash $CLAUDE_PROJECT_DIR/.claude/hooks/tlc-prompt-guard.sh",
|
|
311
|
+
"timeout": 5
|
|
312
|
+
}]
|
|
313
|
+
}],
|
|
314
|
+
"SessionStart": [{
|
|
315
|
+
"hooks": [{
|
|
316
|
+
"type": "command",
|
|
317
|
+
"command": "bash $CLAUDE_PROJECT_DIR/.claude/hooks/tlc-session-init.sh",
|
|
318
|
+
"timeout": 5
|
|
319
|
+
}]
|
|
320
|
+
}],
|
|
321
|
+
"PostToolUse": [
|
|
322
|
+
{
|
|
323
|
+
"matcher": "Bash",
|
|
324
|
+
"hooks": [{
|
|
325
|
+
"type": "command",
|
|
326
|
+
"command": "bash $CLAUDE_PROJECT_DIR/.claude/hooks/tlc-post-push.sh",
|
|
327
|
+
"timeout": 5
|
|
328
|
+
}]
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
"matcher": "Skill",
|
|
332
|
+
"hooks": [{
|
|
333
|
+
"type": "command",
|
|
334
|
+
"command": "bash $CLAUDE_PROJECT_DIR/.claude/hooks/tlc-post-build.sh",
|
|
335
|
+
"timeout": 5
|
|
336
|
+
}]
|
|
337
|
+
}
|
|
338
|
+
],
|
|
339
|
+
"Stop": [{
|
|
340
|
+
"hooks": [{
|
|
341
|
+
"type": "command",
|
|
342
|
+
"command": "bash \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/tlc-capture-exchange.sh",
|
|
343
|
+
"timeout": 30
|
|
344
|
+
}]
|
|
345
|
+
}]
|
|
310
346
|
}
|
|
311
347
|
}
|
|
312
348
|
```
|
|
313
349
|
|
|
314
|
-
If `.claude/settings.json` already exists, merge
|
|
350
|
+
If `.claude/settings.json` already exists, merge: preserve existing permissions (union), add hooks section if missing (don't overwrite existing hooks).
|
|
315
351
|
|
|
316
352
|
### 10. Create or Update PROJECT.md
|
|
317
353
|
|
|
@@ -28,20 +28,35 @@ Interactive setup to configure providers in `.tlc.json`:
|
|
|
28
28
|
|
|
29
29
|
### /tlc:llm status
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
Read from **`.tlc/.router-state.json`** (persistent state) and `.tlc.json` (config):
|
|
32
32
|
|
|
33
33
|
```
|
|
34
|
+
LLM Router Status
|
|
35
|
+
══════════════════════════════════════════════
|
|
36
|
+
|
|
37
|
+
State: .tlc/.router-state.json
|
|
38
|
+
Probed: 2026-03-20T02:15:00Z (12 min ago)
|
|
39
|
+
TTL: 3600s (fresh)
|
|
40
|
+
|
|
34
41
|
Providers:
|
|
35
42
|
claude ✓ /usr/local/bin/claude [review, code-gen, refactor]
|
|
36
43
|
codex ✓ /usr/local/bin/codex [review, code-gen]
|
|
37
44
|
gemini ✗ not found [design, vision]
|
|
38
45
|
|
|
39
46
|
Capabilities:
|
|
40
|
-
review → claude, codex (fallback: claude)
|
|
41
|
-
code-gen → claude
|
|
42
|
-
vision → gemini (unavailable)
|
|
47
|
+
review → claude, codex (fallback: claude) 2/2 available
|
|
48
|
+
code-gen → claude 1/1 available
|
|
49
|
+
vision → gemini (unavailable) 0/1 available
|
|
50
|
+
|
|
51
|
+
Pipeline Integration:
|
|
52
|
+
Post-build review: claude + codex (consensus required)
|
|
53
|
+
Post-push e2e: claude (in-session)
|
|
43
54
|
```
|
|
44
55
|
|
|
56
|
+
The state file is written by the `SessionStart` hook and re-probed every hour. All skills read from this file — no ad-hoc `which` calls.
|
|
57
|
+
|
|
58
|
+
**Force re-probe:** `/tlc:llm probe` (deletes state file, next skill invocation re-probes)
|
|
59
|
+
|
|
45
60
|
### /tlc:llm models
|
|
46
61
|
|
|
47
62
|
List models available from each provider.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# /tlc:preflight - Completeness Check Before Done
|
|
2
|
+
|
|
3
|
+
Never declare a task complete without running this. Catches gaps, loose ends, and half-solutions before they ship.
|
|
4
|
+
|
|
5
|
+
## Philosophy
|
|
6
|
+
|
|
7
|
+
**Assume nothing is complete.** Every task has edges you didn't think about. This skill forces you to actively look for what's missing rather than confirming what's present.
|
|
8
|
+
|
|
9
|
+
## When This Runs
|
|
10
|
+
|
|
11
|
+
This is a **plugin** — it fires automatically via PostToolUse hook whenever Claude is about to declare work "done." You don't invoke it manually.
|
|
12
|
+
|
|
13
|
+
It also runs:
|
|
14
|
+
- After `/tlc:build` (via guard + preflight chain)
|
|
15
|
+
- After any multi-file change
|
|
16
|
+
- Before recommending a commit or push
|
|
17
|
+
|
|
18
|
+
## Process
|
|
19
|
+
|
|
20
|
+
### Step 1: Inventory What Was Changed
|
|
21
|
+
|
|
22
|
+
List every file that was created, modified, or deleted in this session. For each one:
|
|
23
|
+
- What was the intent?
|
|
24
|
+
- Was the change completed or left partial?
|
|
25
|
+
|
|
26
|
+
### Step 2: Trace All References
|
|
27
|
+
|
|
28
|
+
For every file changed, check:
|
|
29
|
+
- **Is it registered/imported where needed?** (e.g., new module added to an index, new command added to a commands array, new route added to a router)
|
|
30
|
+
- **Does anything else reference it?** (e.g., config files, package.json, install scripts, documentation)
|
|
31
|
+
- **Are there parallel systems that need the same update?** (e.g., install.js AND postinstall.js, package.json `files` AND `bin`, CLAUDE.md AND help.md)
|
|
32
|
+
|
|
33
|
+
This is the #1 source of half-solutions: changing the thing but not updating everything that points to the thing.
|
|
34
|
+
|
|
35
|
+
### Step 3: Check Consistency Across Layers
|
|
36
|
+
|
|
37
|
+
For each change, verify consistency across ALL layers:
|
|
38
|
+
|
|
39
|
+
| Layer | Check |
|
|
40
|
+
|-------|-------|
|
|
41
|
+
| **Source files** | Does the code do what was intended? |
|
|
42
|
+
| **Tests** | Do tests exist and pass? |
|
|
43
|
+
| **Config** | Are config files updated (package.json, .tlc.json, settings.json)? |
|
|
44
|
+
| **Distribution** | Will this ship to end users? (files array, install scripts, postinstall) |
|
|
45
|
+
| **Documentation** | Is the change reflected in docs, help text, command tables? |
|
|
46
|
+
| **Integration** | Do other systems that depend on this still work? |
|
|
47
|
+
|
|
48
|
+
### Step 4: Ask the Hard Questions
|
|
49
|
+
|
|
50
|
+
Before declaring done, explicitly answer each:
|
|
51
|
+
|
|
52
|
+
1. **"If someone installs this fresh right now, will it work?"**
|
|
53
|
+
- Not "does it work on my machine" — does it work from a clean install?
|
|
54
|
+
|
|
55
|
+
2. **"What did I assume exists that might not?"**
|
|
56
|
+
- Files, directories, env vars, running services, permissions
|
|
57
|
+
|
|
58
|
+
3. **"What's the blast radius?"**
|
|
59
|
+
- What else touches the same files/systems I changed?
|
|
60
|
+
- Did I update ALL of those touchpoints?
|
|
61
|
+
|
|
62
|
+
4. **"Is there a parallel path I forgot?"**
|
|
63
|
+
- Two install scripts? Two config files? Multiple places the same list appears?
|
|
64
|
+
|
|
65
|
+
5. **"What would a user try that I didn't test?"**
|
|
66
|
+
- Edge cases in usage, not just happy path
|
|
67
|
+
|
|
68
|
+
### Step 5: Report
|
|
69
|
+
|
|
70
|
+
**If gaps found:**
|
|
71
|
+
```
|
|
72
|
+
PREFLIGHT CHECK — GAPS FOUND
|
|
73
|
+
|
|
74
|
+
Changes made: 3 files created, 2 files modified
|
|
75
|
+
|
|
76
|
+
Gaps:
|
|
77
|
+
1. Created new-feature.md but not added to COMMANDS array in install.js
|
|
78
|
+
2. Modified settings.json but init.md template not updated to match
|
|
79
|
+
3. No test file for new utility function
|
|
80
|
+
|
|
81
|
+
Fix these before declaring done.
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**If clean:**
|
|
85
|
+
```
|
|
86
|
+
PREFLIGHT CHECK — CLEAR
|
|
87
|
+
|
|
88
|
+
Changes made: 3 files created, 2 files modified
|
|
89
|
+
All references updated: ✅
|
|
90
|
+
Distribution verified: ✅
|
|
91
|
+
Tests exist: ✅
|
|
92
|
+
Config consistent: ✅
|
|
93
|
+
|
|
94
|
+
Task is complete.
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Common Gap Patterns
|
|
98
|
+
|
|
99
|
+
These are the most frequent gaps this skill catches:
|
|
100
|
+
|
|
101
|
+
| Pattern | Example |
|
|
102
|
+
|---------|---------|
|
|
103
|
+
| **Registry miss** | New file created but not added to install array, export list, or index |
|
|
104
|
+
| **Parallel system miss** | Updated install.js but not postinstall.js |
|
|
105
|
+
| **Config drift** | Changed settings.json but init.md still generates the old version |
|
|
106
|
+
| **Distribution miss** | File exists in repo but won't ship (not in package.json files) |
|
|
107
|
+
| **Doc drift** | New command added but not in CLAUDE.md dispatch table or help.md |
|
|
108
|
+
| **Test miss** | New code without corresponding test |
|
|
109
|
+
| **Cross-reference miss** | Renamed something but didn't grep for all references |
|
|
110
|
+
|
|
111
|
+
## Example
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
PREFLIGHT CHECK — GAPS FOUND
|
|
115
|
+
|
|
116
|
+
Session changes:
|
|
117
|
+
+ .claude/commands/tlc/watchci.md (new skill)
|
|
118
|
+
+ .claude/commands/tlc/e2e-verify.md (new skill)
|
|
119
|
+
+ .claude/commands/tlc/guard.md (new skill)
|
|
120
|
+
+ .claude/hooks/tlc-post-push.sh (new hook)
|
|
121
|
+
+ .claude/hooks/tlc-post-build.sh (new hook)
|
|
122
|
+
~ .claude/settings.json (added PostToolUse hooks)
|
|
123
|
+
~ CLAUDE.md (added dispatch entries)
|
|
124
|
+
|
|
125
|
+
Gaps:
|
|
126
|
+
❌ watchci.md, e2e-verify.md, guard.md not in COMMANDS array (bin/install.js)
|
|
127
|
+
❌ .claude/hooks/ not in package.json files array — won't ship
|
|
128
|
+
❌ postinstall.js doesn't copy hooks — fresh install broken
|
|
129
|
+
❌ init.md settings template doesn't include new hooks
|
|
130
|
+
❌ help.md doesn't list new commands
|
|
131
|
+
❌ bootstrap.md was already missing from COMMANDS array (pre-existing gap)
|
|
132
|
+
|
|
133
|
+
6 gaps found. Fix before declaring done.
|
|
134
|
+
```
|
|
@@ -41,19 +41,32 @@ TLC automatically uses ALL providers configured for the `review` capability in `
|
|
|
41
41
|
|
|
42
42
|
## Process
|
|
43
43
|
|
|
44
|
-
### Step 1: Load Router
|
|
44
|
+
### Step 1: Load Router State (Persistent)
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
**Always read from the router state file first**, then fall back to config:
|
|
47
47
|
|
|
48
48
|
```javascript
|
|
49
|
+
// 1. Read persistent router state (written by session-init hook)
|
|
50
|
+
const routerState = JSON.parse(fs.readFileSync('.tlc/.router-state.json', 'utf-8'));
|
|
51
|
+
|
|
52
|
+
// 2. Read config for capability mappings
|
|
49
53
|
const config = JSON.parse(fs.readFileSync('.tlc.json', 'utf-8'));
|
|
50
54
|
const reviewProviders = config.router?.capabilities?.review?.providers || ['claude'];
|
|
51
55
|
const providers = config.router?.providers || {};
|
|
56
|
+
|
|
57
|
+
// 3. Filter to only AVAILABLE providers (from state file)
|
|
58
|
+
const availableReviewers = reviewProviders.filter(p =>
|
|
59
|
+
routerState.providers[p]?.available === true
|
|
60
|
+
);
|
|
52
61
|
```
|
|
53
62
|
|
|
54
|
-
**
|
|
63
|
+
**The state file (`.tlc/.router-state.json`) is authoritative for availability.** It's written by the `SessionStart` hook, re-probed every hour, and persists across skill invocations. Never run `which codex` yourself — read the state file.
|
|
64
|
+
|
|
65
|
+
If the state file is missing or stale (>1 hour), probe manually and write a fresh one.
|
|
66
|
+
|
|
67
|
+
**Default providers for review:** `['claude', 'codex']` — but only invoked if the state file confirms they're available.
|
|
55
68
|
|
|
56
|
-
If Codex is configured
|
|
69
|
+
If Codex is configured AND available in state, it WILL be invoked automatically.
|
|
57
70
|
|
|
58
71
|
### Step 2: Identify Changes
|
|
59
72
|
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# /tlc:watchci - Watch CI and Fix Until Green
|
|
2
|
+
|
|
3
|
+
After pushing code, monitor the GitHub Actions run and fix failures in a loop until CI passes.
|
|
4
|
+
|
|
5
|
+
## What This Does
|
|
6
|
+
|
|
7
|
+
1. **Identifies the GH Actions run** triggered by the latest push
|
|
8
|
+
2. **Polls until complete** (or fails)
|
|
9
|
+
3. **On failure**: reads the logs, identifies the issue, fixes it, commits, pushes again
|
|
10
|
+
4. **Repeats** until green or max attempts reached
|
|
11
|
+
5. **Reports** final status
|
|
12
|
+
|
|
13
|
+
This is the "I pushed, now babysit CI for me" command.
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
/tlc:watchci
|
|
19
|
+
/tlc:watchci 3 # max 3 fix attempts (default: 5)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Process
|
|
23
|
+
|
|
24
|
+
### Step 1: Find the Active Run
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
gh run list --limit 5 --json databaseId,status,conclusion,headBranch,event,name,createdAt
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
- Find the most recent run on the current branch
|
|
31
|
+
- If no run is in progress or recently completed, tell the user and exit
|
|
32
|
+
- Show: workflow name, run ID, status
|
|
33
|
+
|
|
34
|
+
### Step 2: Wait for Completion
|
|
35
|
+
|
|
36
|
+
Poll the run status every 30 seconds:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
gh run view <run_id> --json status,conclusion
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
While status is `in_progress` or `queued`:
|
|
43
|
+
- Print a brief status update every 60 seconds (not every poll)
|
|
44
|
+
- Continue waiting
|
|
45
|
+
|
|
46
|
+
### Step 3: Check Result
|
|
47
|
+
|
|
48
|
+
If `conclusion` is `success`:
|
|
49
|
+
- Report green and exit
|
|
50
|
+
|
|
51
|
+
If `conclusion` is `failure`:
|
|
52
|
+
- Move to Step 4
|
|
53
|
+
|
|
54
|
+
### Step 4: Read Failure Logs
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
gh run view <run_id> --log-failed
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
- Parse the failed step(s) and their log output
|
|
61
|
+
- Identify the root cause:
|
|
62
|
+
- **Test failure**: Which test, what assertion, what file
|
|
63
|
+
- **Build failure**: Which compilation error, what file/line
|
|
64
|
+
- **Lint failure**: Which rule, what file/line
|
|
65
|
+
- **Dependency issue**: Which package, what version conflict
|
|
66
|
+
- **Other**: Extract the relevant error message
|
|
67
|
+
|
|
68
|
+
### Step 5: Fix the Issue
|
|
69
|
+
|
|
70
|
+
Based on the failure type:
|
|
71
|
+
|
|
72
|
+
**Test failure:**
|
|
73
|
+
- Read the failing test file and the source file it tests
|
|
74
|
+
- Understand what broke
|
|
75
|
+
- Fix the source code (not the test, unless the test itself is wrong)
|
|
76
|
+
- Run the test locally first to verify: `npm test` or `npx vitest run <file>`
|
|
77
|
+
|
|
78
|
+
**Build failure:**
|
|
79
|
+
- Read the file with the compilation error
|
|
80
|
+
- Fix the type error, missing import, etc.
|
|
81
|
+
- Verify locally: `npm run build` or `npx tsc --noEmit`
|
|
82
|
+
|
|
83
|
+
**Lint failure:**
|
|
84
|
+
- Read the file and fix the lint issue
|
|
85
|
+
- Verify locally: `npm run lint`
|
|
86
|
+
|
|
87
|
+
**Dependency issue:**
|
|
88
|
+
- Fix package.json or lock file as needed
|
|
89
|
+
- Verify locally: `npm ci`
|
|
90
|
+
|
|
91
|
+
### Step 6: Commit and Push
|
|
92
|
+
|
|
93
|
+
- Stage only the files you changed
|
|
94
|
+
- Commit with message: `fix: resolve CI failure - <brief description>`
|
|
95
|
+
- Push to the same branch
|
|
96
|
+
- **Ask user before pushing** (per TLC rules)
|
|
97
|
+
|
|
98
|
+
### Step 7: Loop Back
|
|
99
|
+
|
|
100
|
+
- Return to Step 1 to watch the new run
|
|
101
|
+
- Track attempt count
|
|
102
|
+
- If max attempts (default 5) reached without green, stop and report:
|
|
103
|
+
- What was tried
|
|
104
|
+
- What's still failing
|
|
105
|
+
- Suggestion for manual investigation
|
|
106
|
+
|
|
107
|
+
## Guard Rails
|
|
108
|
+
|
|
109
|
+
- **Never push without asking.** Even in a fix loop, confirm before each push.
|
|
110
|
+
- **Never modify tests to make CI pass** unless the test itself has a genuine bug (not a "make the assertion match the wrong output" fix).
|
|
111
|
+
- **Local verification first.** Always run the fix locally before pushing.
|
|
112
|
+
- **Max attempts.** Default 5. Don't loop forever.
|
|
113
|
+
- **Don't mask failures.** If a test is legitimately catching a bug, fix the bug. Don't skip the test.
|
|
114
|
+
|
|
115
|
+
## Example
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
> /tlc:watchci
|
|
119
|
+
|
|
120
|
+
Watching CI for branch: feature/auth-module
|
|
121
|
+
Run #4521 (CI) — in progress...
|
|
122
|
+
|
|
123
|
+
⏳ Waiting... (2m elapsed)
|
|
124
|
+
⏳ Waiting... (3m elapsed)
|
|
125
|
+
|
|
126
|
+
❌ Run #4521 failed
|
|
127
|
+
|
|
128
|
+
Failed step: "Run tests"
|
|
129
|
+
Error: FAIL server/lib/auth/auth.test.js
|
|
130
|
+
● validateToken › should reject expired tokens
|
|
131
|
+
Expected: "TOKEN_EXPIRED"
|
|
132
|
+
Received: "INVALID_TOKEN"
|
|
133
|
+
|
|
134
|
+
Reading auth.test.js and auth.service.js...
|
|
135
|
+
|
|
136
|
+
Found: validateToken returns generic "INVALID_TOKEN" for expired tokens
|
|
137
|
+
instead of the specific "TOKEN_EXPIRED" error code.
|
|
138
|
+
|
|
139
|
+
Fix: Update the expiry check in auth.service.js to return "TOKEN_EXPIRED"
|
|
140
|
+
|
|
141
|
+
Running locally... ✅ Test passes
|
|
142
|
+
|
|
143
|
+
Ready to commit and push fix? (Y/n)
|
|
144
|
+
> y
|
|
145
|
+
|
|
146
|
+
Pushed. Watching new run...
|
|
147
|
+
|
|
148
|
+
Run #4522 (CI) — in progress...
|
|
149
|
+
⏳ Waiting... (2m elapsed)
|
|
150
|
+
|
|
151
|
+
✅ Run #4522 passed! CI is green.
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## When to Use
|
|
155
|
+
|
|
156
|
+
- After pushing and you want CI monitored automatically
|
|
157
|
+
- After `/tlc:build` when you've pushed your work
|
|
158
|
+
- When CI keeps failing and you want the fix loop automated
|
|
159
|
+
- Pair with `/tlc:e2e-verify` for full verification after CI is green
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# TLC Enforcement Layer 1: Hard-block non-TLC planning tools
|
|
3
|
+
# Fires on PreToolUse for banned tools and DENIES them.
|
|
4
|
+
# Claude cannot bypass this - the tool call never executes.
|
|
5
|
+
|
|
6
|
+
INPUT=$(cat)
|
|
7
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty')
|
|
8
|
+
|
|
9
|
+
case "$TOOL" in
|
|
10
|
+
EnterPlanMode)
|
|
11
|
+
REASON="BLOCKED by TLC. Use /tlc:plan instead. Plans go in .planning/phases/ files."
|
|
12
|
+
;;
|
|
13
|
+
TaskCreate)
|
|
14
|
+
REASON="BLOCKED by TLC. Tasks live in .planning/phases/{N}-PLAN.md with [ ] markers."
|
|
15
|
+
;;
|
|
16
|
+
TaskUpdate)
|
|
17
|
+
REASON="BLOCKED by TLC. Update task markers in .planning/phases/{N}-PLAN.md files."
|
|
18
|
+
;;
|
|
19
|
+
TaskGet)
|
|
20
|
+
REASON="BLOCKED by TLC. Read tasks from .planning/phases/{N}-PLAN.md files."
|
|
21
|
+
;;
|
|
22
|
+
TaskList)
|
|
23
|
+
REASON="BLOCKED by TLC. Use /tlc:progress to check task status."
|
|
24
|
+
;;
|
|
25
|
+
ExitPlanMode)
|
|
26
|
+
REASON="BLOCKED by TLC. Plans are approved via /tlc:build, not ExitPlanMode."
|
|
27
|
+
;;
|
|
28
|
+
*)
|
|
29
|
+
exit 0
|
|
30
|
+
;;
|
|
31
|
+
esac
|
|
32
|
+
|
|
33
|
+
cat <<EOF
|
|
34
|
+
{
|
|
35
|
+
"hookSpecificOutput": {
|
|
36
|
+
"hookEventName": "PreToolUse",
|
|
37
|
+
"permissionDecision": "deny",
|
|
38
|
+
"permissionDecisionReason": "${REASON}"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
EOF
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# TLC Memory Capture - Claude Code Stop Hook
|
|
3
|
+
#
|
|
4
|
+
# Reads Stop hook JSON from stdin, extracts the assistant response,
|
|
5
|
+
# and sends it to the TLC server for memory processing.
|
|
6
|
+
# Falls back to local spool when server is unreachable.
|
|
7
|
+
#
|
|
8
|
+
# This script MUST exit 0 always - capture failures never block Claude.
|
|
9
|
+
|
|
10
|
+
set -o pipefail
|
|
11
|
+
|
|
12
|
+
# Read stdin (Stop hook provides JSON)
|
|
13
|
+
INPUT=$(cat)
|
|
14
|
+
|
|
15
|
+
# Quick exit if no input
|
|
16
|
+
[ -z "$INPUT" ] && exit 0
|
|
17
|
+
|
|
18
|
+
# Use the capture-bridge Node.js module for reliable processing
|
|
19
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
20
|
+
BRIDGE_SCRIPT="$PROJECT_DIR/server/lib/capture-bridge.js"
|
|
21
|
+
|
|
22
|
+
if [ -f "$BRIDGE_SCRIPT" ]; then
|
|
23
|
+
echo "$INPUT" | node -e "
|
|
24
|
+
const bridge = require('$BRIDGE_SCRIPT');
|
|
25
|
+
let input = '';
|
|
26
|
+
process.stdin.on('data', d => input += d);
|
|
27
|
+
process.stdin.on('end', async () => {
|
|
28
|
+
const parsed = bridge.parseStopHookInput(input);
|
|
29
|
+
if (!parsed || !parsed.assistantMessage) process.exit(0);
|
|
30
|
+
|
|
31
|
+
const userMessage = parsed.transcriptPath
|
|
32
|
+
? bridge.extractLastUserMessage(parsed.transcriptPath)
|
|
33
|
+
: null;
|
|
34
|
+
|
|
35
|
+
await bridge.captureExchange({
|
|
36
|
+
cwd: parsed.cwd || '$PROJECT_DIR',
|
|
37
|
+
assistantMessage: parsed.assistantMessage,
|
|
38
|
+
userMessage,
|
|
39
|
+
sessionId: parsed.sessionId,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const path = require('path');
|
|
43
|
+
const spoolDir = path.join(parsed.cwd || '$PROJECT_DIR', '.tlc', 'memory');
|
|
44
|
+
await bridge.drainSpool(spoolDir);
|
|
45
|
+
});
|
|
46
|
+
" 2>/dev/null
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# Always exit 0 - never block Claude
|
|
50
|
+
exit 0
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# TLC Plugin: Post-build and post-plan automation chain
|
|
3
|
+
# Fires on PostToolUse for Skill.
|
|
4
|
+
#
|
|
5
|
+
# After /tlc:build (code written):
|
|
6
|
+
# guard → preflight → review (Claude + Codex) → if APPROVED → offer push + PR
|
|
7
|
+
#
|
|
8
|
+
# After /tlc:plan:
|
|
9
|
+
# guard plan → validate structure
|
|
10
|
+
#
|
|
11
|
+
# After /tlc:quick:
|
|
12
|
+
# preflight → review → offer push
|
|
13
|
+
|
|
14
|
+
INPUT=$(cat)
|
|
15
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty')
|
|
16
|
+
|
|
17
|
+
# Only care about Skill tool
|
|
18
|
+
[ "$TOOL" != "Skill" ] && exit 0
|
|
19
|
+
|
|
20
|
+
SKILL=$(echo "$INPUT" | jq -r '.tool_input.skill // empty')
|
|
21
|
+
|
|
22
|
+
case "$SKILL" in
|
|
23
|
+
tlc:build)
|
|
24
|
+
echo "[TLC-PLUGIN] Build phase complete. Mandatory post-build chain:
|
|
25
|
+
(1) Run /tlc:guard to validate TLC process compliance (test-first, coverage, no skipped tests).
|
|
26
|
+
(2) Run /tlc:preflight to check for gaps (references, registries, configs, distribution).
|
|
27
|
+
(3) Run /tlc:review to invoke multi-LLM review — Claude reviews in-session, THEN invoke Codex CLI for second opinion. BOTH must APPROVE.
|
|
28
|
+
(4) If review APPROVED: ask user for permission to push to remote and create a PR for main.
|
|
29
|
+
(5) If review CHANGES_REQUESTED: fix the issues and re-run the review. Do NOT push.
|
|
30
|
+
Do NOT skip any step. Do NOT push without review approval."
|
|
31
|
+
;;
|
|
32
|
+
tlc:plan)
|
|
33
|
+
echo "[TLC-PLUGIN] Plan created. Now automatically run /tlc:guard plan to validate the plan has proper task breakdown, acceptance criteria, and dependency ordering. Do NOT skip."
|
|
34
|
+
;;
|
|
35
|
+
tlc:quick)
|
|
36
|
+
echo "[TLC-PLUGIN] Quick task complete. Run /tlc:preflight then /tlc:review (Claude + Codex). If APPROVED, ask user for permission to push and create PR."
|
|
37
|
+
;;
|
|
38
|
+
esac
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# TLC Plugin: Post-push automation chain
|
|
3
|
+
# Fires on PostToolUse for Bash. Detects git push.
|
|
4
|
+
#
|
|
5
|
+
# After git push:
|
|
6
|
+
# watchci (monitor GH Actions) → if green → e2e-verify (screenshots + logs)
|
|
7
|
+
|
|
8
|
+
INPUT=$(cat)
|
|
9
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty')
|
|
10
|
+
|
|
11
|
+
# Only care about Bash tool
|
|
12
|
+
[ "$TOOL" != "Bash" ] && exit 0
|
|
13
|
+
|
|
14
|
+
# Check if the command was a git push
|
|
15
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
|
|
16
|
+
if echo "$COMMAND" | grep -qE '^git push'; then
|
|
17
|
+
echo "[TLC-PLUGIN] git push detected. Mandatory post-push chain:
|
|
18
|
+
(1) Run /tlc:watchci — monitor the GitHub Actions run, read failure logs, fix issues, and push again until CI is green.
|
|
19
|
+
(2) Once CI is green, run /tlc:e2e-verify — start the app, take screenshots, read them visually, check server and console logs for errors.
|
|
20
|
+
(3) If e2e-verify finds issues, fix them and push again (which restarts this chain).
|
|
21
|
+
Do NOT skip any step."
|
|
22
|
+
fi
|