tlc-claude-code 1.8.5 → 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/bootstrap.md +77 -0
- package/.claude/commands/tlc/build.md +20 -6
- 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/recall.md +87 -0
- package/.claude/commands/tlc/remember.md +71 -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 +96 -201
- 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 +240 -1
- package/server/lib/bug-writer.js +204 -0
- package/server/lib/bug-writer.test.js +279 -0
- 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/claude-cascade.js +247 -0
- package/server/lib/claude-cascade.test.js +245 -0
- package/server/lib/command-runner.js +159 -0
- package/server/lib/command-runner.test.js +92 -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/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/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/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 +182 -0
- package/server/lib/memory-api.test.js +320 -0
- 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 +415 -0
- package/server/lib/memory-hooks-integration.test.js +98 -0
- package/server/lib/memory-hooks.js +139 -0
- package/server/lib/memory-inheritance.js +179 -0
- package/server/lib/memory-inheritance.test.js +360 -0
- 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/plan-writer.js +196 -0
- package/server/lib/plan-writer.test.js +298 -0
- package/server/lib/port-guard.js +44 -0
- package/server/lib/port-guard.test.js +65 -0
- package/server/lib/project-scanner.js +302 -0
- package/server/lib/project-scanner.test.js +541 -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 +98 -0
- package/server/lib/remember-command.test.js +288 -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/security/crypto-utils.test.js +2 -2
- package/server/lib/semantic-recall.js +242 -0
- package/server/lib/semantic-recall.test.js +463 -0
- package/server/lib/setup-generator.js +315 -0
- package/server/lib/setup-generator.test.js +303 -0
- package/server/lib/ssh-client.js +184 -0
- package/server/lib/ssh-client.test.js +127 -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/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 +992 -0
- package/server/lib/workspace-api.test.js +1217 -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 +1306 -17
- package/server/package.json +7 -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
|
@@ -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
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# TLC Enforcement Layer 2: Smart intent detection + routing
|
|
3
|
+
# Fires on every UserPromptSubmit. Parses user message for intent and
|
|
4
|
+
# injects specific TLC command routing — not a generic reminder.
|
|
5
|
+
#
|
|
6
|
+
# Output appears as <user-prompt-submit-hook> and Claude treats it as user instruction.
|
|
7
|
+
# Survives context compaction because it is re-injected every turn.
|
|
8
|
+
|
|
9
|
+
# Only active in TLC projects
|
|
10
|
+
[ ! -f ".tlc.json" ] && exit 0
|
|
11
|
+
|
|
12
|
+
# Read user's message from stdin
|
|
13
|
+
INPUT=$(cat)
|
|
14
|
+
MSG=$(echo "$INPUT" | jq -r '.user_prompt // empty' 2>/dev/null)
|
|
15
|
+
|
|
16
|
+
# If we can't parse the message, fall back to generic reminder
|
|
17
|
+
if [ -z "$MSG" ]; then
|
|
18
|
+
echo "[TLC PROJECT] All work uses /tlc commands. Never write code without tests. Run /tlc if unsure."
|
|
19
|
+
exit 0
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
# Lowercase for matching
|
|
23
|
+
MSG_LOWER=$(echo "$MSG" | tr '[:upper:]' '[:lower:]')
|
|
24
|
+
|
|
25
|
+
# Detect intent and inject specific routing
|
|
26
|
+
# Priority order: most specific first
|
|
27
|
+
|
|
28
|
+
# Plan intent
|
|
29
|
+
if echo "$MSG_LOWER" | grep -qE '\b(plan|break.*(down|into)|design|architect|roadmap)\b'; then
|
|
30
|
+
if ! echo "$MSG_LOWER" | grep -qE '/tlc'; then
|
|
31
|
+
echo "[TLC-ENFORCE] Planning detected. You MUST use /tlc:plan for this. Plans go in .planning/phases/ files, not in chat. Invoke Skill(skill=\"tlc:plan\") now."
|
|
32
|
+
exit 0
|
|
33
|
+
fi
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# Build/implement intent
|
|
37
|
+
if echo "$MSG_LOWER" | grep -qE '\b(build|implement|create|add|code|write|develop|make)\b.*(feature|function|module|component|endpoint|api|page|service|handler|route|model)'; then
|
|
38
|
+
if ! echo "$MSG_LOWER" | grep -qE '/tlc'; then
|
|
39
|
+
echo "[TLC-ENFORCE] Implementation detected. You MUST use /tlc:build for this. Tests before code — Red, Green, Refactor. Run /tlc:progress first, then invoke Skill(skill=\"tlc:build\"). Do NOT write code directly."
|
|
40
|
+
exit 0
|
|
41
|
+
fi
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
# Fix/bug intent
|
|
45
|
+
if echo "$MSG_LOWER" | grep -qE '\b(fix|bug|broken|not working|failing|crash|error)\b'; then
|
|
46
|
+
if ! echo "$MSG_LOWER" | grep -qE '/tlc'; then
|
|
47
|
+
echo "[TLC-ENFORCE] Bug/fix detected. Use /tlc:quick for small fixes (still test-first) or /tlc:autofix if tests are failing. Do NOT write code directly without tests."
|
|
48
|
+
exit 0
|
|
49
|
+
fi
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# Refactor intent
|
|
53
|
+
if echo "$MSG_LOWER" | grep -qE '\b(refactor|clean.?up|restructure|reorganize|simplify)\b'; then
|
|
54
|
+
if ! echo "$MSG_LOWER" | grep -qE '/tlc'; then
|
|
55
|
+
echo "[TLC-ENFORCE] Refactoring detected. Use /tlc:refactor for step-by-step standards refactoring with tests. Invoke Skill(skill=\"tlc:refactor\")."
|
|
56
|
+
exit 0
|
|
57
|
+
fi
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# Deploy intent
|
|
61
|
+
if echo "$MSG_LOWER" | grep -qE '\b(deploy|ship|release|publish|push to prod|staging)\b'; then
|
|
62
|
+
if ! echo "$MSG_LOWER" | grep -qE '/tlc'; then
|
|
63
|
+
echo "[TLC-ENFORCE] Deployment detected. Use /tlc:deploy for deployment. Secrets must come from HashiCorp Vault or environment — never hardcode. Invoke Skill(skill=\"tlc:deploy\")."
|
|
64
|
+
exit 0
|
|
65
|
+
fi
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# Generic fallback for TLC projects
|
|
69
|
+
echo "[TLC PROJECT] All work uses /tlc commands. Never write code without tests. Run /tlc if unsure."
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# TLC Enforcement Layer 2b: Session initialization
|
|
3
|
+
# 1. Inject TLC awareness
|
|
4
|
+
# 2. Ensure TLC server is running
|
|
5
|
+
# 3. Probe LLM providers and write persistent router state
|
|
6
|
+
|
|
7
|
+
if [ ! -f ".tlc.json" ]; then
|
|
8
|
+
exit 0
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
echo "TLC project detected. All work goes through /tlc commands. Run /tlc for current status and next action."
|
|
12
|
+
|
|
13
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
14
|
+
|
|
15
|
+
# ─── TLC Server ───────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
TLC_PORT="${TLC_PORT:-3147}"
|
|
18
|
+
if curl -sf --max-time 1 "http://localhost:${TLC_PORT}/api/health" > /dev/null 2>&1; then
|
|
19
|
+
: # Server is running
|
|
20
|
+
else
|
|
21
|
+
PLIST="$HOME/Library/LaunchAgents/com.tlc.server.plist"
|
|
22
|
+
|
|
23
|
+
if [ -f "$PLIST" ]; then
|
|
24
|
+
launchctl kickstart -k "gui/$(id -u)/com.tlc.server" 2>/dev/null
|
|
25
|
+
elif [ -f "$PROJECT_DIR/server/index.js" ]; then
|
|
26
|
+
nohup node "$PROJECT_DIR/server/index.js" > "$HOME/.tlc/logs/server.log" 2>&1 &
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
for i in 1 2 3; do
|
|
30
|
+
sleep 1
|
|
31
|
+
curl -sf --max-time 1 "http://localhost:${TLC_PORT}/api/health" > /dev/null 2>&1 && break
|
|
32
|
+
done
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# ─── LLM Router: Probe Providers ─────────────────────────
|
|
36
|
+
#
|
|
37
|
+
# Writes .tlc/.router-state.json with provider availability.
|
|
38
|
+
# Skills read this file instead of probing from scratch.
|
|
39
|
+
# State has a TTL — re-probed if older than 1 hour.
|
|
40
|
+
|
|
41
|
+
STATE_DIR="$PROJECT_DIR/.tlc"
|
|
42
|
+
STATE_FILE="$STATE_DIR/.router-state.json"
|
|
43
|
+
mkdir -p "$STATE_DIR"
|
|
44
|
+
|
|
45
|
+
# Check if state is fresh (less than 1 hour old)
|
|
46
|
+
STALE=true
|
|
47
|
+
if [ -f "$STATE_FILE" ]; then
|
|
48
|
+
STATE_AGE=$(( $(date +%s) - $(stat -f %m "$STATE_FILE" 2>/dev/null || stat -c %Y "$STATE_FILE" 2>/dev/null || echo 0) ))
|
|
49
|
+
if [ "$STATE_AGE" -lt 3600 ]; then
|
|
50
|
+
STALE=false
|
|
51
|
+
fi
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
if [ "$STALE" = true ]; then
|
|
55
|
+
# Probe each provider
|
|
56
|
+
CLAUDE_PATH=$(which claude 2>/dev/null || echo "")
|
|
57
|
+
CODEX_PATH=$(which codex 2>/dev/null || echo "")
|
|
58
|
+
GEMINI_PATH=$(which gemini 2>/dev/null || echo "")
|
|
59
|
+
|
|
60
|
+
CLAUDE_OK="false"
|
|
61
|
+
CODEX_OK="false"
|
|
62
|
+
GEMINI_OK="false"
|
|
63
|
+
|
|
64
|
+
[ -n "$CLAUDE_PATH" ] && CLAUDE_OK="true"
|
|
65
|
+
[ -n "$CODEX_PATH" ] && CODEX_OK="true"
|
|
66
|
+
[ -n "$GEMINI_PATH" ] && GEMINI_OK="true"
|
|
67
|
+
|
|
68
|
+
# Count available
|
|
69
|
+
AVAILABLE=0
|
|
70
|
+
[ "$CLAUDE_OK" = "true" ] && AVAILABLE=$((AVAILABLE + 1))
|
|
71
|
+
[ "$CODEX_OK" = "true" ] && AVAILABLE=$((AVAILABLE + 1))
|
|
72
|
+
[ "$GEMINI_OK" = "true" ] && AVAILABLE=$((AVAILABLE + 1))
|
|
73
|
+
|
|
74
|
+
# Read configured providers from .tlc.json
|
|
75
|
+
CONFIGURED_PROVIDERS=""
|
|
76
|
+
if command -v jq >/dev/null 2>&1; then
|
|
77
|
+
CONFIGURED_PROVIDERS=$(jq -r '.router.providers // {} | keys[]' .tlc.json 2>/dev/null | tr '\n' ',' | sed 's/,$//')
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
# Write state file
|
|
81
|
+
cat > "$STATE_FILE" <<STATEEOF
|
|
82
|
+
{
|
|
83
|
+
"probed_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
84
|
+
"ttl_seconds": 3600,
|
|
85
|
+
"providers": {
|
|
86
|
+
"claude": {
|
|
87
|
+
"available": $CLAUDE_OK,
|
|
88
|
+
"path": "$CLAUDE_PATH"
|
|
89
|
+
},
|
|
90
|
+
"codex": {
|
|
91
|
+
"available": $CODEX_OK,
|
|
92
|
+
"path": "$CODEX_PATH"
|
|
93
|
+
},
|
|
94
|
+
"gemini": {
|
|
95
|
+
"available": $GEMINI_OK,
|
|
96
|
+
"path": "$GEMINI_PATH"
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
"summary": {
|
|
100
|
+
"available_count": $AVAILABLE,
|
|
101
|
+
"configured": "$CONFIGURED_PROVIDERS"
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
STATEEOF
|
|
105
|
+
|
|
106
|
+
# Report to Claude
|
|
107
|
+
if [ "$AVAILABLE" -gt 0 ]; then
|
|
108
|
+
PROVIDERS_LIST=""
|
|
109
|
+
[ "$CLAUDE_OK" = "true" ] && PROVIDERS_LIST="${PROVIDERS_LIST}claude, "
|
|
110
|
+
[ "$CODEX_OK" = "true" ] && PROVIDERS_LIST="${PROVIDERS_LIST}codex, "
|
|
111
|
+
[ "$GEMINI_OK" = "true" ] && PROVIDERS_LIST="${PROVIDERS_LIST}gemini, "
|
|
112
|
+
PROVIDERS_LIST=$(echo "$PROVIDERS_LIST" | sed 's/, $//')
|
|
113
|
+
echo "LLM Router: ${AVAILABLE} providers available (${PROVIDERS_LIST}). State written to .tlc/.router-state.json. All routing skills MUST read this file for provider availability — do not probe manually."
|
|
114
|
+
else
|
|
115
|
+
echo "LLM Router: No external providers detected. Running Claude-only mode. Install codex or gemini for multi-LLM reviews."
|
|
116
|
+
fi
|
|
117
|
+
else
|
|
118
|
+
# State is fresh — just report it
|
|
119
|
+
if command -v jq >/dev/null 2>&1 && [ -f "$STATE_FILE" ]; then
|
|
120
|
+
COUNT=$(jq -r '.summary.available_count' "$STATE_FILE" 2>/dev/null)
|
|
121
|
+
echo "LLM Router: ${COUNT} providers available (cached). State at .tlc/.router-state.json."
|
|
122
|
+
fi
|
|
123
|
+
fi
|