tlc-claude-code 2.9.2 → 2.10.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.
@@ -106,14 +106,14 @@ process.stdout.write(JSON.stringify(result));" 2>/dev/null
106
106
  After `resolveRouting` returns, immediately write `.tlc/.build-routing-active` with the active provider name from `models[0]` before doing any other build work. Remove `.tlc/.build-routing-active` on completion, cancellation, or failure cleanup.
107
107
 
108
108
  **Routing decision:**
109
- - If routing says external provider (`models[0] !== 'claude'`), follow **ONLY Orchestrator Mode** below.
110
- - If routing says Claude (`models[0] === 'claude'`), follow **Inline Mode** below.
109
+ - **ALWAYS use Orchestrator Mode.** Claude never writes code inline. The routing config determines which provider the orchestrator dispatches TO (codex, claude session, gemini), but Claude in this conversation is always the PM.
110
+ - If the orchestrator is unreachable, auto-recover it (restart container, deploy latest code). If recovery fails, STOP do not fall back to inline code.
111
111
 
112
- ## ORCHESTRATOR MODE
112
+ ## ORCHESTRATOR MODE (ALWAYS)
113
113
 
114
- Use this mode only when `models[0]` is **NOT** `claude`.
114
+ Claude is the **project manager**. Claude reads the plan, breaks tasks into small prompts, and dispatches each to the tlc-core orchestrator. Claude does NOT write code. This is not conditional on routing — it is always the mode.
115
115
 
116
- Claude is the **project manager**. Claude reads the plan, breaks tasks into small prompts, and dispatches each to the tlc-core orchestrator. Claude does NOT write code.
116
+ The `models[0]` value from routing determines which provider the orchestrator uses (`codex`, `claude`, `gemini`), not whether Claude writes code inline.
117
117
 
118
118
  ### Step O1: Read the Plan
119
119
 
@@ -254,11 +254,9 @@ After PR is created (or phase completes):
254
254
  - Mark completed tasks `[x]` in PLAN.md
255
255
  - Report final summary
256
256
 
257
- ## INLINE MODE
257
+ ## AGENT INSTRUCTIONS (Reference for Dispatched Sessions)
258
258
 
259
- Use this mode only when `models[0]` **IS** `claude`.
260
-
261
- The existing build instructions below are the Inline Mode instructions and should be followed unchanged.
259
+ **These instructions are NOT for Claude in the main conversation.** They are the reference for what the dispatched orchestrator agent (Codex, Claude session, or Gemini) should do inside its tmux session. Claude packages these into the dispatch prompt — Claude does not execute them inline.
262
260
 
263
261
  ## Engineering Standards
264
262
 
@@ -19,9 +19,62 @@ For brand new projects with no code, use `/tlc:new-project` instead.
19
19
 
20
20
  ## Process
21
21
 
22
- ### 0. Upgrade Check (runs first, always)
22
+ ### 0. Orchestrator Enforcement (runs first, always)
23
23
 
24
- **Before anything else**, check if this is already a TLC project:
24
+ **Before anything else**, verify the orchestrator is available. All code execution goes through it — no exceptions.
25
+
26
+ ```bash
27
+ # Check orchestrator health
28
+ if curl -sf --max-time 2 http://localhost:3100/health > /dev/null 2>&1; then
29
+ echo "Orchestrator: ✅ healthy"
30
+ else
31
+ echo "Orchestrator: ❌ not running — attempting recovery..."
32
+ # Try to find and restart the orchestrator container
33
+ ORCH_CONTAINER=$(docker ps -a --format '{{.Names}}' 2>/dev/null | grep "orchestrator" | head -1)
34
+ if [ -n "$ORCH_CONTAINER" ]; then
35
+ docker start "$ORCH_CONTAINER" 2>/dev/null
36
+ sleep 3
37
+ fi
38
+ # If still down and tlc-core source exists, deploy and start
39
+ if ! curl -sf --max-time 2 http://localhost:3100/health > /dev/null 2>&1; then
40
+ if command -v tlc-core >/dev/null 2>&1; then
41
+ tlc-core start &
42
+ sleep 5
43
+ fi
44
+ fi
45
+ # Final check
46
+ if curl -sf --max-time 2 http://localhost:3100/health > /dev/null 2>&1; then
47
+ echo "Orchestrator: ✅ recovered"
48
+ else
49
+ echo "Orchestrator: ❌ FAILED — code execution will not work"
50
+ echo "Install tlc-core: git clone git@github.com:TwentyTwoLabs22/tlc-core.git && cd cli && npm i && npm link"
51
+ echo "Then run: tlc-core start"
52
+ fi
53
+ fi
54
+ ```
55
+
56
+ Also verify CodeDB and agent runner:
57
+
58
+ ```bash
59
+ # CodeDB
60
+ if ! curl -sf --max-time 1 http://localhost:7719/health > /dev/null 2>&1; then
61
+ if command -v codedb >/dev/null 2>&1; then
62
+ codedb serve . &
63
+ echo "CodeDB: ✅ started"
64
+ else
65
+ echo "CodeDB: ❌ not installed — run: npx tlc-claude-code"
66
+ fi
67
+ fi
68
+
69
+ # Agent runner container
70
+ if ! docker ps --format '{{.Names}}' 2>/dev/null | grep -q "tlc-agent-runner"; then
71
+ docker start tlc-agent-runner 2>/dev/null && echo "Agent Runner: ✅ started" || echo "Agent Runner: ❌ not available"
72
+ fi
73
+ ```
74
+
75
+ ### 0b. Upgrade Check
76
+
77
+ **After orchestrator is verified**, check if this is already a TLC project:
25
78
 
26
79
  ```bash
27
80
  # Check for existing TLC markers
@@ -100,14 +100,12 @@ process.stdout.write(JSON.stringify(result));" 2>/dev/null
100
100
  After `resolveRouting` returns, immediately write `.tlc/.quick-routing-active` with the active provider name from `models[0]` before doing any other quick work. Remove `.tlc/.quick-routing-active` on completion, cancellation, or failure cleanup.
101
101
 
102
102
  **Routing decision:**
103
- - If routing says external provider (`models[0] !== 'claude'`), follow **ONLY ORCHESTRATOR MODE** below.
104
- - If routing says Claude (`models[0] === 'claude'`), follow **INLINE MODE** below.
103
+ - **ALWAYS use Orchestrator Mode.** Claude never writes code inline. Routing determines which provider the orchestrator dispatches to.
104
+ - If orchestrator is down, auto-recover. If recovery fails, STOP do not fall back to inline.
105
105
 
106
- ## ORCHESTRATOR MODE
106
+ ## ORCHESTRATOR MODE (ALWAYS)
107
107
 
108
- Use this mode only when `models[0]` is **NOT** `claude`.
109
-
110
- Claude does not execute the quick-task instructions inline in this path. Claude acts as the orchestrator for the routed provider run:
108
+ Claude acts as the orchestrator dispatches the quick task to the orchestrator, monitors completion, verifies result. Claude does NOT write code.
111
109
 
112
110
  1. Treat Claude as the orchestrator for a small, test-first routed task rather than executing the work inline.
113
111
  2. Gather the minimal project context needed for the quick task, then package a focused prompt with the task request, expected failing test, implementation scope, and verification target.
@@ -123,12 +121,9 @@ Claude does not execute the quick-task instructions inline in this path. Claude
123
121
  - If the provider returns incomplete code or missing tests, issue a targeted retry for the missing part.
124
122
  7. When finished, display the routed provider result, persist any captured memory, remove `.tlc/.quick-routing-active`, and stop. Do **not** execute Inline Mode afterward.
125
123
 
126
- ## INLINE MODE
127
-
128
- Use this mode only when `models[0]` **IS** `claude`.
129
-
130
- The existing quick-task instructions below are the Inline Mode instructions and should be followed unchanged.
124
+ ## AGENT INSTRUCTIONS (Reference for Dispatched Sessions)
131
125
 
126
+ **These instructions are NOT for Claude in the main conversation.** They are packaged into the dispatch prompt for the orchestrator agent.
132
127
 
133
128
  ## Engineering Standards
134
129
 
@@ -1,6 +1,6 @@
1
1
  # /tlc:recall - Team Memory Search
2
2
 
3
- Search TLC team memory using plain grep. This is intentional: no vector search, no extra services.
3
+ Search TLC team memory through the context engine hybrid search pipeline. Use FTS5 plus vector search when the engine is available, and fall back to grep only when it is not.
4
4
 
5
5
  ## Usage
6
6
 
@@ -10,33 +10,58 @@ Search TLC team memory using plain grep. This is intentional: no vector search,
10
10
 
11
11
  ## What This Does
12
12
 
13
- 1. Searches `.tlc/memory/team/` with `grep -rl "query" .tlc/memory/team/`.
14
- 2. Looks in both `.tlc/memory/team/decisions/` and `.tlc/memory/team/gotchas/`.
15
- 3. Shows the file path, date from the filename, and a matching excerpt.
16
- 4. Uses grep only because it works everywhere.
13
+ 1. Opens `.tlc/context.sqlite` through `createContextEngine()`.
14
+ 2. Builds a search pipeline with `createSearchPipeline` from `@tlc/context-engine/search-pipeline`.
15
+ 3. Runs the context leg with the engine's hybrid FTS5 plus vector search.
16
+ 4. Searches TLC memory under `.tlc/memory/team/`.
17
+ 5. Falls back to recursive grep only when the context engine or database is unavailable.
17
18
 
18
19
  ## Search Rules
19
20
 
20
21
  - Search both `decisions/` and `gotchas/`.
21
- - Do not use vector search.
22
- - Do not depend on embeddings, databases, or external services.
23
- - Prefer simple recursive grep so the command works across projects.
22
+ - Prefer context-engine hybrid search over grep.
23
+ - Use vector ranking when embeddings are available; FTS5-only results are acceptable when vectors are missing.
24
+ - Fall back to grep if `@tlc/context-engine` cannot be loaded or `.tlc/context.sqlite` does not exist yet.
24
25
 
25
26
  ## Process
26
27
 
27
- ### Step 1: Find matching files
28
+ ### Step 1: Try the context engine
29
+
30
+ ```js
31
+ import { createContextEngine } from '@tlc/context-engine';
32
+ import { createSearch } from '@tlc/context-engine/retrieval/search';
33
+ import { createSearchPipeline } from '@tlc/context-engine/search-pipeline';
34
+
35
+ const engine = createContextEngine({ dbPath: '.tlc/context.sqlite' });
36
+ const hybridSearch = createSearch({
37
+ embeddingStore: {
38
+ findSimilar(db, queryVec, options) {
39
+ return engine.embeddings.findSimilar(queryVec, options);
40
+ },
41
+ },
42
+ });
43
+
44
+ const searchAll = createSearchPipeline({
45
+ search: (db, query, options) => hybridSearch(db, query, options),
46
+ });
47
+
48
+ const results = await searchAll(engine.db, query, { limit: 10 });
49
+ ```
50
+
51
+ ### Step 2: If the engine is unavailable, fall back to grep
28
52
 
29
53
  ```bash
30
- grep -rl "query" .tlc/memory/team/
54
+ grep -Ril "query" .tlc/memory/team/
31
55
  ```
32
56
 
33
- ### Step 2: Display matches
57
+ ### Step 3: Display matches
34
58
 
35
59
  For each matching file, show:
36
60
 
37
61
  - File path
38
62
  - Date parsed from the filename prefix
39
63
  - A short matching excerpt from the file body
64
+ - Search source: `context-engine` or `grep-fallback`
40
65
 
41
66
  ## Example Output
42
67
 
@@ -57,3 +82,4 @@ Excerpt: Local timezone conversion caused reporting drift in scheduled jobs.
57
82
  - If there are no matches, say so directly.
58
83
  - Keep excerpts short and relevant.
59
84
  - Search TLC memory under `.tlc/memory/team/`, not Claude-managed memory.
85
+ - Close the context engine after the search when it was opened successfully.
@@ -17,31 +17,32 @@ Or without arguments:
17
17
  ## What This Does
18
18
 
19
19
  1. Captures the provided decision text, or extracts decisions from the recent conversation context if no argument is given.
20
- 2. Writes a markdown file to `.tlc/memory/team/decisions/{date}-{slug}.md`.
21
- 3. Stores TLC-owned frontmatter for later grep-based recall.
22
- 4. Never writes to `~/.claude/projects/.../memory/` because that is Claude memory, not TLC memory.
20
+ 2. Appends a JSONL event to `.tlc/events/memory.jsonl` through the context engine event log.
21
+ 3. Replays the event into `.tlc/context.sqlite`.
22
+ 4. Exports visible `.md` memory files for git visibility under `.tlc/memory/team/decisions/`.
23
+ 5. Never writes to `~/.claude/projects/.../memory/` because that is Claude memory, not TLC memory.
23
24
 
24
25
  ## Write Format
25
26
 
26
- Write the decision as a markdown file with YAML frontmatter:
27
+ Write the canonical record as a JSONL event first:
28
+
29
+ ```json
30
+ {"type":"decision","category":"decision","scope":"team","subject":"use-utc-timestamps","body":"Always use UTC timestamps in the database and in exported logs."}
31
+ ```
32
+
33
+ Then export the visible markdown file with YAML frontmatter:
27
34
 
28
35
  ```yaml
29
36
  ---
30
- provider: claude
31
- source: manual
32
37
  timestamp: 2026-03-28T12:34:56Z
33
38
  type: decision
34
39
  scope: team
40
+ category: decision
41
+ subject: "use-utc-timestamps"
35
42
  ---
36
43
  ```
37
44
 
38
- The filename must be:
39
-
40
- ```text
41
- .tlc/memory/team/decisions/{date}-{slug}.md
42
- ```
43
-
44
- Example:
45
+ The markdown filename must be:
45
46
 
46
47
  ```text
47
48
  .tlc/memory/team/decisions/2026-03-28-use-utc-timestamps.md
@@ -53,19 +54,43 @@ Example:
53
54
 
54
55
  - Use the argument as the decision content.
55
56
  - Generate a short slug from the decision.
56
- - Write the file under `.tlc/memory/team/decisions/`.
57
+ - Append the event with `engine.events.appendEvent('.tlc/events/memory.jsonl', event)`.
58
+ - Replay the log into the database.
59
+ - Export markdown back to `.tlc/memory/team/decisions/`.
57
60
 
58
61
  ### Without an argument
59
62
 
60
63
  - Review the recent conversation context.
61
64
  - Extract concrete decisions, conventions, or constraints worth preserving.
62
65
  - Summarize them clearly.
63
- - Write one decision file under `.tlc/memory/team/decisions/`.
66
+ - Append one JSONL event.
67
+ - Export one visible markdown file under `.tlc/memory/team/decisions/`.
68
+
69
+ ## Implementation Sketch
70
+
71
+ ```js
72
+ import { createContextEngine } from '@tlc/context-engine';
73
+ import { exportFactsToMarkdown } from '@tlc/context-engine/import/markdown-exporter';
74
+
75
+ const engine = createContextEngine({ dbPath: '.tlc/context.sqlite' });
76
+ const event = engine.events.appendEvent('.tlc/events/memory.jsonl', {
77
+ type: 'decision',
78
+ category: 'decision',
79
+ scope: 'team',
80
+ subject: slug,
81
+ body: decisionText,
82
+ });
83
+
84
+ engine.events.replayEvents([event]);
85
+ exportFactsToMarkdown(engine.db, '.tlc/memory/team/decisions');
86
+ engine.close();
87
+ ```
64
88
 
65
89
  ## Confirmation
66
90
 
67
91
  ```text
68
92
  Remembered: {summary}
93
+ Event: .tlc/events/memory.jsonl
69
94
  File: .tlc/memory/team/decisions/2026-03-28-use-utc-timestamps.md
70
95
  ```
71
96
 
@@ -74,3 +99,4 @@ File: .tlc/memory/team/decisions/2026-03-28-use-utc-timestamps.md
74
99
  - Prefer concise, durable statements over raw transcript dumps.
75
100
  - Record decisions, not general chatter.
76
101
  - Use `.tlc/memory/team/gotchas/` for gotchas, not this command.
102
+ - JSONL is the source of truth; markdown is the human-visible export.
@@ -181,6 +181,31 @@ Only mention upgrade details in `--verbose` mode.
181
181
 
182
182
  ## One-Pass Detection
183
183
 
184
+ ### Step 0: Quick Health Check
185
+
186
+ Before phase detection, run a fast health check (no auto-recovery — that's the SessionStart hook's job). Just report status in one line:
187
+
188
+ ```bash
189
+ # Check key systems in parallel (each with 1s timeout)
190
+ CODEDB=$(curl -sf --max-time 1 http://localhost:7719/health > /dev/null 2>&1 && echo "✅" || echo "❌")
191
+ ORCH=$(curl -sf --max-time 1 http://localhost:3100/health > /dev/null 2>&1 && echo "✅" || echo "❌")
192
+ RUNNER=$(docker ps --format '{{.Names}}' 2>/dev/null | grep -q "tlc-agent-runner" && echo "✅" || echo "❌")
193
+ ```
194
+
195
+ Print one compact line:
196
+
197
+ ```
198
+ Systems: CodeDB ${CODEDB} | Orchestrator ${ORCH} | Agent Runner ${RUNNER}
199
+ ```
200
+
201
+ If any system is ❌, **auto-recover immediately** before continuing:
202
+
203
+ 1. CodeDB ❌ → run `codedb serve . &` (or `npx tlc-claude-code` if binary missing), wait 2s, re-check
204
+ 2. Orchestrator ❌ → find orchestrator container (`docker ps | grep orchestrator`), deploy latest code from `../tlc-core/orchestrator/`, restart container, wait 3s, re-check
205
+ 3. Agent Runner ❌ → `docker start tlc-agent-runner` if stopped, report if container doesn't exist
206
+
207
+ After recovery attempts, print updated status. Only continue to phase detection after all recoverable systems are up. If recovery fails, print the failure and continue anyway — don't block indefinitely.
208
+
184
209
  ### Step 1: Load Core Context
185
210
 
186
211
  Read, in this order:
@@ -256,11 +281,22 @@ Backward compatibility:
256
281
 
257
282
  ## Output Format
258
283
 
259
- Normal output must stay within 3-5 lines.
284
+ Normal output must stay compact health line + phase status + action.
260
285
 
261
286
  Use this pattern:
262
287
 
263
288
  ```text
289
+ Systems: CodeDB ✅ | Orchestrator ✅ | Agent Runner ✅
290
+ TLC v{version} | Phase {PHASE_ID}: {Name} | {done}/{total} tasks done
291
+ Next: {action summary}
292
+ Running /tlc:{command} {PHASE_ID}...
293
+ ```
294
+
295
+ If any system is down:
296
+
297
+ ```text
298
+ Systems: CodeDB ✅ | Orchestrator ❌ | Agent Runner ✅
299
+ ⚠️ Orchestrator down — run /tlc:init to recover
264
300
  TLC v{version} | Phase {PHASE_ID}: {Name} | {done}/{total} tasks done
265
301
  Next: {action summary}
266
302
  Running /tlc:{command} {PHASE_ID}...
@@ -269,6 +305,7 @@ Running /tlc:{command} {PHASE_ID}...
269
305
  If there is nothing safe to auto-run:
270
306
 
271
307
  ```text
308
+ Systems: CodeDB ✅ | Orchestrator ✅ | Agent Runner ✅
272
309
  TLC v{version} | All phases complete
273
310
  Next: release or start the next milestone
274
311
  Run /tlc:complete or /tlc:new-milestone
@@ -338,7 +375,8 @@ Run /tlc:complete or /tlc:new-milestone
338
375
  ## Example
339
376
 
340
377
  ```text
341
- TLC v2.4.2 | Phase TLC-8: Auth System | 3/5 tasks done
378
+ Systems: CodeDB | Orchestrator | Agent Runner
379
+ TLC v2.9.2 | Phase TLC-8: Auth System | 3/5 tasks done
342
380
  Next: build task 4 (JWT middleware)
343
381
  Running /tlc:build TLC-8...
344
382
  ```
@@ -26,8 +26,15 @@ if [ "$INSTALLED_VER" = "not-installed" ]; then
26
26
  echo " TLC Package: ❌ not installed (run: npm i -g tlc-claude-code)"
27
27
  ERRORS=$((ERRORS + 1))
28
28
  elif [ "$INSTALLED_VER" != "$LATEST_VER" ] && [ "$LATEST_VER" != "unknown" ]; then
29
- echo " TLC Package: ⚠️ ${INSTALLED_VER} (latest: ${LATEST_VER})run: npm i -g tlc-claude-code"
30
- WARNINGS=$((WARNINGS + 1))
29
+ echo " TLC Package: ${INSTALLED_VER} ${LATEST_VER} — auto-updating..."
30
+ npm i -g tlc-claude-code@latest > /dev/null 2>&1
31
+ NEW_VER=$(node -e "try{console.log(require('tlc-claude-code/package.json').version)}catch{console.log('?')}" 2>/dev/null)
32
+ if [ "$NEW_VER" = "$LATEST_VER" ]; then
33
+ echo " TLC Package: ✅ updated to v${NEW_VER}"
34
+ else
35
+ echo " TLC Package: ⚠️ update failed — still on v${INSTALLED_VER}"
36
+ WARNINGS=$((WARNINGS + 1))
37
+ fi
31
38
  else
32
39
  echo " TLC Package: ✅ v${INSTALLED_VER} (latest)"
33
40
  fi
@@ -37,6 +44,15 @@ mkdir -p "$PROJECT_DIR/.tlc/memory/team/decisions" \
37
44
  "$PROJECT_DIR/.tlc/memory/team/gotchas" \
38
45
  "$PROJECT_DIR/.tlc/memory/.local/sessions" 2>/dev/null
39
46
 
47
+ # Bootstrap context engine on first run by importing visible markdown memory.
48
+ CONTEXT_BOOTSTRAP=""
49
+ if node -e "import('@tlc/context-engine/bootstrap').then(()=>process.exit(0)).catch(()=>process.exit(1))" >/dev/null 2>&1; then
50
+ CONTEXT_BOOTSTRAP="import('@tlc/context-engine').then(async ({ createContextEngine }) => { const { createBootstrap } = await import('@tlc/context-engine/bootstrap'); const { importMarkdownMemories } = await import('@tlc/context-engine/import/markdown-importer'); const { indexProject } = await import('@tlc/context-engine/retrieval/indexer'); const bootstrap = createBootstrap({ contextEngine: createContextEngine, markdownImporter: { importMarkdownMemories }, indexer: { indexProject } }); const result = await bootstrap(process.argv[1]); if ((result.imported || 0) > 0 || (result.indexed || 0) > 0) { console.log(' Context Engine: ✅ bootstrapped (' + result.imported + ' imported, ' + result.indexed + ' indexed)'); } }).catch(() => {})"
51
+ elif [ -d "$PROJECT_DIR/server/node_modules/@tlc/context-engine" ]; then
52
+ CONTEXT_BOOTSTRAP="(async () => { const [{ createContextEngine }, { createBootstrap }, { importMarkdownMemories }, { indexProject }] = await Promise.all([import('$PROJECT_DIR/server/node_modules/@tlc/context-engine/src/index.js'), import('$PROJECT_DIR/server/node_modules/@tlc/context-engine/src/bootstrap.js'), import('$PROJECT_DIR/server/node_modules/@tlc/context-engine/src/import/markdown-importer.js'), import('$PROJECT_DIR/server/node_modules/@tlc/context-engine/src/retrieval/indexer.js')]); const bootstrap = createBootstrap({ contextEngine: createContextEngine, markdownImporter: { importMarkdownMemories }, indexer: { indexProject } }); const result = await bootstrap(process.argv[1]); if ((result.imported || 0) > 0 || (result.indexed || 0) > 0) { console.log(' Context Engine: ✅ bootstrapped (' + result.imported + ' imported, ' + result.indexed + ' indexed)'); } })().catch(() => {})"
53
+ fi
54
+ [ -n "$CONTEXT_BOOTSTRAP" ] && node -e "$CONTEXT_BOOTSTRAP" "$PROJECT_DIR" 2>/dev/null
55
+
40
56
  if curl -sf --max-time 1 "http://localhost:${TLC_PORT}/api/health" > /dev/null 2>&1; then
41
57
  echo " TLC Server: ✅ running (:${TLC_PORT})"
42
58
  # Drain spool
@@ -100,8 +116,20 @@ MCPEOF
100
116
  echo " CodeDB MCP: ✅ created .mcp.json"
101
117
  fi
102
118
  else
103
- echo " CodeDB: ❌ not installed (run: npx tlc-claude-code)"
104
- ERRORS=$((ERRORS + 1))
119
+ # Auto-install CodeDB binary
120
+ echo " CodeDB: ⏳ not installed — auto-installing..."
121
+ if npx -y tlc-claude-code 2>/dev/null | tail -1; then
122
+ if command -v codedb >/dev/null 2>&1; then
123
+ codedb serve "$PROJECT_DIR" > /dev/null 2>&1 &
124
+ echo " CodeDB: ✅ installed and started"
125
+ else
126
+ echo " CodeDB: ❌ install failed"
127
+ ERRORS=$((ERRORS + 1))
128
+ fi
129
+ else
130
+ echo " CodeDB: ❌ install failed"
131
+ ERRORS=$((ERRORS + 1))
132
+ fi
105
133
  fi
106
134
 
107
135
  # ─── 4. tlc-core Orchestrator + Containers ────────────────
@@ -125,17 +153,49 @@ elif curl -sf --max-time 2 "http://localhost:${TLC_CORE_PORT}/health" > /dev/nul
125
153
 
126
154
  # Check session monitor + watchdog
127
155
  ORCH_LOGS=$(docker logs tlc-core-orchestrator-1 --tail 50 2>&1)
156
+ MONITOR_OK=false
157
+ WATCHDOG_OK=false
128
158
  if echo "$ORCH_LOGS" | grep -q "Session monitor started"; then
159
+ MONITOR_OK=true
129
160
  echo " Session Monitor: ✅ active (10s interval)"
130
- else
131
- echo " Session Monitor: ❌ not running — orchestrator needs restart"
132
- ERRORS=$((ERRORS + 1))
133
161
  fi
134
162
  if echo "$ORCH_LOGS" | grep -q "Watchdog started\|watchdog.*Started"; then
163
+ WATCHDOG_OK=true
135
164
  echo " Watchdog: ✅ active (60s interval)"
136
- else
137
- echo " Watchdog: ⚠️ not detected in logs"
138
- WARNINGS=$((WARNINGS + 1))
165
+ fi
166
+
167
+ # Auto-recover: if monitor or watchdog missing, deploy latest code and restart
168
+ if [ "$MONITOR_OK" = false ] || [ "$WATCHDOG_OK" = false ]; then
169
+ echo " Monitor/Watchdog: ⏳ missing — deploying latest code and restarting..."
170
+ # Find orchestrator source
171
+ ORCH_SRC=""
172
+ if [ -d "$PROJECT_DIR/../tlc-core/orchestrator" ]; then
173
+ ORCH_SRC="$PROJECT_DIR/../tlc-core/orchestrator"
174
+ elif [ -d "$(dirname "$PROJECT_DIR")/tlc-core/orchestrator" ]; then
175
+ ORCH_SRC="$(dirname "$PROJECT_DIR")/tlc-core/orchestrator"
176
+ fi
177
+ if [ -n "$ORCH_SRC" ]; then
178
+ ORCH_CONTAINER=$(docker ps --format '{{.Names}}' 2>/dev/null | grep "orchestrator" | head -1)
179
+ if [ -n "$ORCH_CONTAINER" ]; then
180
+ docker cp "$ORCH_SRC/lib/." "$ORCH_CONTAINER:/app/lib/" 2>/dev/null
181
+ docker cp "$ORCH_SRC/index.js" "$ORCH_CONTAINER:/app/index.js" 2>/dev/null
182
+ docker cp "$ORCH_SRC/migrations/." "$ORCH_CONTAINER:/app/migrations/" 2>/dev/null
183
+ docker restart "$ORCH_CONTAINER" > /dev/null 2>&1
184
+ sleep 3
185
+ if curl -sf --max-time 2 "http://localhost:${TLC_CORE_PORT}/health" > /dev/null 2>&1; then
186
+ echo " Monitor/Watchdog: ✅ restarted with latest code"
187
+ else
188
+ echo " Monitor/Watchdog: ❌ restart failed"
189
+ ERRORS=$((ERRORS + 1))
190
+ fi
191
+ else
192
+ echo " Monitor/Watchdog: ❌ orchestrator container not found"
193
+ ERRORS=$((ERRORS + 1))
194
+ fi
195
+ else
196
+ [ "$MONITOR_OK" = false ] && echo " Session Monitor: ❌ not running (tlc-core source not found)" && ERRORS=$((ERRORS + 1))
197
+ [ "$WATCHDOG_OK" = false ] && echo " Watchdog: ⚠️ not detected" && WARNINGS=$((WARNINGS + 1))
198
+ fi
139
199
  fi
140
200
  else
141
201
  if command -v tlc-core >/dev/null 2>&1; then
@@ -180,6 +240,14 @@ PROVIDER_LIST=$(echo "$PROVIDER_LIST" | sed 's/, $//')
180
240
 
181
241
  if [ "$PROVIDER_COUNT" -gt 0 ]; then
182
242
  echo " LLM Providers: ✅ ${PROVIDER_COUNT} available — ${PROVIDER_LIST}"
243
+ # Check for updates (non-blocking, just warn)
244
+ if [ "$CODEX_VER" != "not installed" ]; then
245
+ CODEX_LATEST=$(npm view @openai/codex version 2>/dev/null || echo "")
246
+ if [ -n "$CODEX_LATEST" ] && [ "$CODEX_VER" != *"$CODEX_LATEST"* ] 2>/dev/null; then
247
+ echo " Codex Update: ⚠️ latest: ${CODEX_LATEST} — run: npm i -g @openai/codex"
248
+ WARNINGS=$((WARNINGS + 1))
249
+ fi
250
+ fi
183
251
  else
184
252
  echo " LLM Providers: ❌ none found"
185
253
  ERRORS=$((ERRORS + 1))
@@ -209,6 +277,8 @@ cat > "$STATE_FILE" <<STATEEOF
209
277
  STATEEOF
210
278
 
211
279
  # ─── 6. Git + Remote Sync ────────────────────────────────
280
+ # Fetch remote to get accurate ahead/behind counts
281
+ git fetch --quiet 2>/dev/null
212
282
  CURRENT_BRANCH=$(git branch --show-current 2>/dev/null)
213
283
  BEHIND=$(git rev-list --count HEAD..@{u} 2>/dev/null || echo "?")
214
284
  AHEAD=$(git rev-list --count @{u}..HEAD 2>/dev/null || echo "?")
@@ -254,11 +324,16 @@ if [ -n "$WORKSPACE_FILE" ]; then
254
324
  const rp = path.resolve(wsRoot, repo.path);
255
325
  try {
256
326
  fs.accessSync(rp);
327
+ // Fetch remote for accurate state
328
+ try { execSync('git -C ' + JSON.stringify(rp) + ' fetch --quiet', { stdio: ['pipe','pipe','pipe'], timeout: 10000 }); } catch {}
257
329
  const branch = execSync('git -C ' + JSON.stringify(rp) + ' branch --show-current', { encoding: 'utf8', stdio: ['pipe','pipe','pipe'] }).trim();
258
330
  let status = 'unknown';
259
331
  try {
260
332
  const behind = execSync('git -C ' + JSON.stringify(rp) + ' rev-list --count HEAD..@{u}', { encoding: 'utf8', stdio: ['pipe','pipe','pipe'] }).trim();
261
- status = behind === '0' ? 'in sync' : behind + ' behind';
333
+ const ahead = execSync('git -C ' + JSON.stringify(rp) + ' rev-list --count @{u}..HEAD', { encoding: 'utf8', stdio: ['pipe','pipe','pipe'] }).trim();
334
+ if (behind === '0' && ahead === '0') status = 'in sync';
335
+ else if (behind !== '0') status = behind + ' behind — needs pull';
336
+ else status = ahead + ' ahead — push pending';
262
337
  } catch { status = 'no remote tracking'; }
263
338
  console.log(' [' + repo.prefix + '] ' + branch + ': ' + status);
264
339
  } catch {
package/CLAUDE.md CHANGED
@@ -10,6 +10,7 @@
10
10
  4. **No direct implementation.** User says "build X" → run `/tlc:progress` then `/tlc:build`.
11
11
  5. **No Co-Authored-By in commits.** The user is the author. Claude is a tool.
12
12
  6. **Ask before `git push`.** Never push without explicit approval.
13
+ 7. **Claude NEVER writes code.** All code execution goes through the orchestrator (tlc-core :3100). Claude is PM — read, plan, dispatch, review. Sub-agents writing code is a bug. If the orchestrator is down, recover it first — never fall back to inline code.
13
14
 
14
15
  ## CodeDB
15
16
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tlc-claude-code",
3
- "version": "2.9.2",
3
+ "version": "2.10.1",
4
4
  "description": "TLC - Test Led Coding for Claude Code",
5
5
  "bin": {
6
6
  "tlc-claude-code": "./bin/install.js",