tlc-claude-code 2.4.9 → 2.5.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/audit.md +5 -0
- package/.claude/commands/tlc/build.md +145 -44
- package/.claude/commands/tlc/issues.md +46 -0
- package/.claude/commands/tlc/plan.md +43 -0
- package/.claude/commands/tlc/review.md +596 -571
- package/package.json +1 -1
- package/server/lib/github/config.js +458 -0
- package/server/lib/github/config.test.js +385 -0
- package/server/lib/github/gh-client.js +303 -0
- package/server/lib/github/gh-client.test.js +499 -0
- package/server/lib/github/gh-projects.js +594 -0
- package/server/lib/github/gh-projects.test.js +583 -0
- package/server/lib/github/index.js +19 -0
- package/server/lib/github/plan-sync.js +456 -0
- package/server/lib/github/plan-sync.test.js +805 -0
|
@@ -57,6 +57,11 @@ Run `auditProject(projectPath)` which executes:
|
|
|
57
57
|
| **Secrets in Responses** | Response objects containing fields named `apiKey`, `secret`, `token`, `password` | error |
|
|
58
58
|
| **Manual Instantiation** | `new .*Provider(` or `new .*Service(` in application code | warning |
|
|
59
59
|
| **Missing Ownership Check** | Controller methods with `@Param('id')` but no ownership/authorization guard | warning |
|
|
60
|
+
| **Empty Catch Blocks** | `catch (e) {}` or `catch { }` with no logging or rethrow | error |
|
|
61
|
+
| **Silent Error Returns** | `catch (e) { return null/undefined/false }` without logging | error |
|
|
62
|
+
| **Missing Error Handling** | External calls (HTTP, DB, Docker, file I/O, spawn) without try/catch | error |
|
|
63
|
+
| **No Connection Logging** | DB/Redis/WebSocket/Docker connections without connect/disconnect/error logging | warning |
|
|
64
|
+
| **Bare console.log Errors** | `console.log(error)` instead of structured logging with context | warning |
|
|
60
65
|
|
|
61
66
|
### Step 3: Generate Report
|
|
62
67
|
|
|
@@ -96,6 +96,10 @@ process.stdout.write(JSON.stringify(result));" 2>/dev/null
|
|
|
96
96
|
- Execute inline (Claude) AND dispatch each external provider via `dispatch` from `orchestration/cli-dispatch`
|
|
97
97
|
- After each provider completes, capture its output with `captureFromProvider` from `capture`
|
|
98
98
|
- Collect and merge results
|
|
99
|
+
- **CRITICAL: If a provider fails (wrong model, auth error, CLI missing), do NOT silently fall back to single-provider.** Instead:
|
|
100
|
+
1. Check `.tlc/.router-state.json` for the provider's actual available model and retry
|
|
101
|
+
2. If retry fails, ask the user: "[Provider] failed: [reason]. Run with [other provider] only, or fix and retry?"
|
|
102
|
+
3. Never say "proceeding with X-only" without user consent
|
|
99
103
|
|
|
100
104
|
**Override:** Pass `--model <name>` to route this specific run to a different model.
|
|
101
105
|
|
|
@@ -210,19 +214,25 @@ function processOrder(order) {
|
|
|
210
214
|
- **JSDoc for public API**: Parameters, returns, throws, examples.
|
|
211
215
|
- **No obvious comments**: `// increment i` before `i++` is noise.
|
|
212
216
|
|
|
213
|
-
### Error Handling
|
|
217
|
+
### Error Handling (Zero Silent Failures)
|
|
218
|
+
- **Every catch block must be visible**: Log with context OR rethrow. No exceptions.
|
|
214
219
|
- **Specific error types**: `UserNotFoundError` not generic `Error`.
|
|
215
|
-
- **Actionable messages**: "User 'abc123' not found" not "Not found".
|
|
216
|
-
- **Don't swallow errors**: Log or rethrow, never empty catch blocks.
|
|
220
|
+
- **Actionable messages**: "User 'abc123' not found in database 'users'" not "Not found".
|
|
221
|
+
- **Don't swallow errors**: Log or rethrow, never empty catch blocks. This is the #1 cause of production bugs.
|
|
217
222
|
- **Error boundaries**: Catch at appropriate level, not everywhere.
|
|
218
223
|
- **User vs developer errors**: Different messages for each audience.
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
- **
|
|
223
|
-
- **
|
|
224
|
+
- **External calls MUST have error handling**: HTTP, DB, Docker, file I/O, CLI spawn — every one gets try/catch with logging.
|
|
225
|
+
|
|
226
|
+
### Observability (CRITICAL — silent failures are unacceptable)
|
|
227
|
+
- **No empty catch blocks**: Every `catch` must log with context OR rethrow. `catch (e) {}` is never acceptable.
|
|
228
|
+
- **No silent returns**: `catch (e) { return null }` without logging is a bug. Log first, then return.
|
|
229
|
+
- **Structured logging**: JSON format: `{ level, message, context, error, timestamp }`.
|
|
230
|
+
- **Log levels**: ERROR (failures needing attention), WARN (degraded but working), INFO (business events), DEBUG (troubleshooting).
|
|
231
|
+
- **Context in every log**: Include what operation failed, what inputs caused it, what the caller should know.
|
|
232
|
+
- **Connection state logging**: Every external connection (DB, Redis, Docker, WebSocket, HTTP client, queue) must log: connect, disconnect, reconnect, and failure. Connection state changes are INFO level, not DEBUG.
|
|
233
|
+
- **Startup health**: Log the state of every dependency on startup. "Connected to Postgres", "Docker socket accessible", "Redis: connection refused". Never start silently.
|
|
224
234
|
- **No sensitive data**: Never log passwords, tokens, PII.
|
|
225
|
-
- **Performance**: Don't log in tight loops.
|
|
235
|
+
- **Performance**: Don't log in tight loops. Do log every error, even in loops.
|
|
226
236
|
|
|
227
237
|
### Performance Awareness
|
|
228
238
|
- **O(n) thinking**: Know the complexity of your algorithms. Avoid nested loops on large datasets.
|
|
@@ -270,8 +280,7 @@ This is the core TLC command. Tests before code, one task at a time.
|
|
|
270
280
|
```
|
|
271
281
|
/tlc:build <phase_number>
|
|
272
282
|
/tlc:build <phase_number> --sequential # Force sequential mode
|
|
273
|
-
/tlc:build <phase_number> --model
|
|
274
|
-
/tlc:build <phase_number> --model haiku # Force all agents to use haiku (fast/cheap)
|
|
283
|
+
/tlc:build <phase_number> --model opus # All agents use opus (this is the default)
|
|
275
284
|
/tlc:build <phase_number> --max-turns 30 # Limit agent execution length
|
|
276
285
|
/tlc:build <phase_number> --agents 5 # Limit parallel agents to 5
|
|
277
286
|
```
|
|
@@ -327,7 +336,7 @@ After loading plans, analyze task dependencies and available providers to pick t
|
|
|
327
336
|
|-----------|----------|--------------|
|
|
328
337
|
| 2+ independent tasks, Claude + Codex available, tmux available | **Worktree + Tmux** | Each task gets own git worktree + tmux pane. Claude and Codex work simultaneously. |
|
|
329
338
|
| 2+ independent tasks, Claude + Codex available, no tmux | **Worktree + Background** | Same but without tmux visibility. |
|
|
330
|
-
| 2+ independent tasks, single provider only | **In-process agents** | Agent tool spawns parallel sub-agents (
|
|
339
|
+
| 2+ independent tasks, single provider only | **In-process agents** | Agent tool spawns parallel sub-agents (all opus). |
|
|
331
340
|
| All tasks have dependencies | **Sequential** | One task at a time, in dependency order. |
|
|
332
341
|
| 1 task only | **Sequential** | Just build it. |
|
|
333
342
|
|
|
@@ -353,9 +362,9 @@ Providers: claude (1 available)
|
|
|
353
362
|
Strategy: in-process agents (parallel)
|
|
354
363
|
|
|
355
364
|
Task 1: Create user schema → opus (heavy)
|
|
356
|
-
Task 2: Add validation helpers →
|
|
357
|
-
Task 3: Write migration scripts →
|
|
358
|
-
Task 4: Create seed data →
|
|
365
|
+
Task 2: Add validation helpers → opus (standard)
|
|
366
|
+
Task 3: Write migration scripts → opus (standard)
|
|
367
|
+
Task 4: Create seed data → opus (light)
|
|
359
368
|
|
|
360
369
|
Launching...
|
|
361
370
|
```
|
|
@@ -371,12 +380,13 @@ Strategy: sequential
|
|
|
371
380
|
Starting Task 1...
|
|
372
381
|
```
|
|
373
382
|
|
|
374
|
-
**Model
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
|
379
|
-
|
|
383
|
+
**Model selection (in-process agent mode):**
|
|
384
|
+
|
|
385
|
+
All agents use **opus**. No opus, no opus. Cost is not a concern — quality is.
|
|
386
|
+
|
|
387
|
+
| Task | Model |
|
|
388
|
+
|------|-------|
|
|
389
|
+
| Any | opus |
|
|
380
390
|
|
|
381
391
|
**Provider round-robin (worktree mode):**
|
|
382
392
|
Tasks alternate between available providers. If Claude + Codex are both available, odd tasks go to Claude, even to Codex. Respects router state — never dispatches to unavailable providers.
|
|
@@ -391,9 +401,9 @@ Tasks alternate between available providers. If Claude + Codex are both availabl
|
|
|
391
401
|
Spawning 4 agents in parallel...
|
|
392
402
|
|
|
393
403
|
Agent 1: Task 1 - Create user schema [opus]
|
|
394
|
-
Agent 2: Task 2 - Add validation helpers [
|
|
395
|
-
Agent 3: Task 3 - Write migration scripts [
|
|
396
|
-
Agent 4: Task 4 - Create seed data [
|
|
404
|
+
Agent 2: Task 2 - Add validation helpers [opus]
|
|
405
|
+
Agent 3: Task 3 - Write migration scripts [opus]
|
|
406
|
+
Agent 4: Task 4 - Create seed data [opus]
|
|
397
407
|
|
|
398
408
|
[All agents spawned - working in background]
|
|
399
409
|
```
|
|
@@ -410,9 +420,9 @@ Agent 4: Task 4 - Create seed data [haiku]
|
|
|
410
420
|
|
|
411
421
|
```
|
|
412
422
|
Task(description="Agent 1: Task 1", prompt="...", subagent_type="general-purpose", model="opus", max_turns=50, run_in_background=true)
|
|
413
|
-
Task(description="Agent 2: Task 2", prompt="...", subagent_type="general-purpose", model="
|
|
414
|
-
Task(description="Agent 3: Task 3", prompt="...", subagent_type="general-purpose", model="
|
|
415
|
-
Task(description="Agent 4: Task 4", prompt="...", subagent_type="general-purpose", model="
|
|
423
|
+
Task(description="Agent 2: Task 2", prompt="...", subagent_type="general-purpose", model="opus", max_turns=50, run_in_background=true)
|
|
424
|
+
Task(description="Agent 3: Task 3", prompt="...", subagent_type="general-purpose", model="opus", max_turns=50, run_in_background=true)
|
|
425
|
+
Task(description="Agent 4: Task 4", prompt="...", subagent_type="general-purpose", model="opus", max_turns=50, run_in_background=true)
|
|
416
426
|
```
|
|
417
427
|
|
|
418
428
|
**Live Progress Monitoring (TaskOutput):**
|
|
@@ -443,9 +453,9 @@ Display format:
|
|
|
443
453
|
| Agent | Task | Model | Tests | Phase |
|
|
444
454
|
|-------|------|-------|-------|-------|
|
|
445
455
|
| a1b2c3 | User Schema | opus | 29 ✓ | committed |
|
|
446
|
-
| d4e5f6 | Validation |
|
|
447
|
-
| g7h8i9 | Migrations |
|
|
448
|
-
| j0k1l2 | Seed Data |
|
|
456
|
+
| d4e5f6 | Validation | opus | 18 ✓ | implementing |
|
|
457
|
+
| g7h8i9 | Migrations | opus | - | writing-tests |
|
|
458
|
+
| j0k1l2 | Seed Data | opus | 5 ✓ | committed |
|
|
449
459
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
450
460
|
```
|
|
451
461
|
|
|
@@ -781,6 +791,33 @@ Review the task's:
|
|
|
781
791
|
- Acceptance criteria
|
|
782
792
|
- Test cases (now written and failing)
|
|
783
793
|
|
|
794
|
+
#### 7a-gh. GitHub: Mark In Progress
|
|
795
|
+
|
|
796
|
+
If GitHub sync is enabled, update the linked GitHub issue for this task:
|
|
797
|
+
|
|
798
|
+
```bash
|
|
799
|
+
node -e "
|
|
800
|
+
const path = require('path');
|
|
801
|
+
const fs = require('fs');
|
|
802
|
+
const { execSync } = require('child_process');
|
|
803
|
+
let gh;
|
|
804
|
+
try { gh = require('tlc-claude-code/server/lib/github'); }
|
|
805
|
+
catch { gh = require(path.join(process.cwd(), 'server/lib/github')); }
|
|
806
|
+
try {
|
|
807
|
+
const cfg = gh.loadGitHubConfig(process.cwd(), { fs }).config;
|
|
808
|
+
if (!cfg) process.exit(0);
|
|
809
|
+
const exec = (cmd) => execSync(cmd, { encoding: 'utf-8', stdio: ['pipe','pipe','pipe'] });
|
|
810
|
+
const user = execSync('git config user.name', { encoding: 'utf-8' }).trim();
|
|
811
|
+
const issueNumber = process.argv[1];
|
|
812
|
+
if (issueNumber) {
|
|
813
|
+
gh.assignIssue({ owner: cfg.owner, repo: cfg.repo, number: Number(issueNumber), assignees: [user], exec });
|
|
814
|
+
}
|
|
815
|
+
} catch (e) { console.error('[TLC] GitHub status update failed:', e.message); }
|
|
816
|
+
" "{issue_number}" 2>/dev/null
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
If no issue is linked to this task, skip silently. Failures warn but never block.
|
|
820
|
+
|
|
784
821
|
#### 7b. Implement the code
|
|
785
822
|
Write the minimum code needed to pass the tests:
|
|
786
823
|
- Create files specified in the task
|
|
@@ -822,6 +859,35 @@ This keeps the plan file as the single source of truth for task status. Do NOT w
|
|
|
822
859
|
|
|
823
860
|
If in multi-user mode, also push to share progress with team.
|
|
824
861
|
|
|
862
|
+
#### 7e-gh. GitHub: Mark Done
|
|
863
|
+
|
|
864
|
+
If GitHub sync is enabled, update the linked issue status after marking the task `[x]`:
|
|
865
|
+
|
|
866
|
+
```bash
|
|
867
|
+
node -e "
|
|
868
|
+
const path = require('path');
|
|
869
|
+
const fs = require('fs');
|
|
870
|
+
const { execSync } = require('child_process');
|
|
871
|
+
let gh;
|
|
872
|
+
try { gh = require('tlc-claude-code/server/lib/github'); }
|
|
873
|
+
catch { gh = require(path.join(process.cwd(), 'server/lib/github')); }
|
|
874
|
+
try {
|
|
875
|
+
const cfg = gh.loadGitHubConfig(process.cwd(), { fs }).config;
|
|
876
|
+
if (!cfg) process.exit(0);
|
|
877
|
+
const planPath = process.argv[1];
|
|
878
|
+
const r = gh.updateTaskStatuses({
|
|
879
|
+
planPath,
|
|
880
|
+
config: cfg,
|
|
881
|
+
ghClient: { exec: (cmd) => execSync(cmd, { encoding: 'utf-8', stdio: ['pipe','pipe','pipe'] }) },
|
|
882
|
+
fs
|
|
883
|
+
});
|
|
884
|
+
console.log(JSON.stringify(r));
|
|
885
|
+
} catch (e) { console.error('[TLC] GitHub status sync failed:', e.message); }
|
|
886
|
+
" ".planning/phases/{N}-PLAN.md" 2>/dev/null
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
Failures warn but never block the build.
|
|
890
|
+
|
|
825
891
|
#### 7f. Move to next task
|
|
826
892
|
Repeat 7a-7e for each task in the phase.
|
|
827
893
|
|
|
@@ -868,20 +934,22 @@ git diff --name-status main...HEAD
|
|
|
868
934
|
|
|
869
935
|
**Checks performed:**
|
|
870
936
|
|
|
871
|
-
1. **
|
|
872
|
-
2. **
|
|
873
|
-
3. **
|
|
874
|
-
4. **
|
|
875
|
-
5. **
|
|
876
|
-
6. **
|
|
877
|
-
7. **
|
|
878
|
-
8. **
|
|
879
|
-
9. **
|
|
880
|
-
10. **
|
|
881
|
-
11. **
|
|
882
|
-
12. **
|
|
883
|
-
13. **
|
|
884
|
-
14. **
|
|
937
|
+
1. **Silent Failure Scan (CRITICAL)** - No empty catch blocks. Every catch must log with context OR rethrow. `catch (e) {}` and `catch (e) { return null }` without logging are **auto-rejected**. Every external call (HTTP, DB, Docker, CLI, file I/O) must have error handling that produces an observable signal (log, metric, or rethrow).
|
|
938
|
+
2. **Error Logging** - Every error path must use structured logging: `{ level, message, context, error }`. No bare `console.log` for errors — use a logger with level + context. No `console.error` without identifying WHAT failed and WHERE.
|
|
939
|
+
3. **Test Coverage** - Every implementation file has a test file
|
|
940
|
+
4. **TDD Compliance** - Commits show test-first pattern (score ≥ 50%)
|
|
941
|
+
5. **Security Scan** - No hardcoded secrets, eval(), innerHTML, etc.
|
|
942
|
+
6. **Authorization** - Every data-access endpoint has ownership checks, not just auth guards
|
|
943
|
+
7. **Secrets Exposure** - No API keys, tokens, or passwords returned in responses/HTML
|
|
944
|
+
8. **Config Hygiene** - No `process.env` outside config module; config validated at startup
|
|
945
|
+
9. **Output Encoding** - No unescaped `${...}` interpolation in HTML template strings
|
|
946
|
+
10. **Sensitive Data** - OTPs, reset tokens, session secrets are hashed before storage
|
|
947
|
+
11. **DI Compliance** - No manual `new Service()` / `new Provider()` in application code
|
|
948
|
+
12. **File Size** - No file exceeds 1000 lines (warning at 500+)
|
|
949
|
+
13. **Folder Size** - No folder exceeds 15 files (warning at 8+)
|
|
950
|
+
14. **Strict Typing** - No `any` types in new/changed files
|
|
951
|
+
15. **Return Types** - All exported functions have explicit return types
|
|
952
|
+
16. **Module Structure** - Files grouped by domain entity, not by type
|
|
885
953
|
|
|
886
954
|
**Review output:**
|
|
887
955
|
|
|
@@ -1000,6 +1068,39 @@ gh pr create \
|
|
|
1000
1068
|
{test runner summary}"
|
|
1001
1069
|
```
|
|
1002
1070
|
|
|
1071
|
+
#### Step 11-gh: GitHub: Link PR to Project
|
|
1072
|
+
|
|
1073
|
+
If GitHub sync is enabled, after the PR is created:
|
|
1074
|
+
|
|
1075
|
+
1. Add `Closes #N` references to the PR body for all completed task issues (parse `<!-- gh:N -->` markers from PLAN.md)
|
|
1076
|
+
2. Add the PR to the GitHub project board if configured
|
|
1077
|
+
3. Set the phase issue Status to "In review"
|
|
1078
|
+
|
|
1079
|
+
```bash
|
|
1080
|
+
node -e "
|
|
1081
|
+
const path = require('path');
|
|
1082
|
+
const fs = require('fs');
|
|
1083
|
+
const { execSync } = require('child_process');
|
|
1084
|
+
let gh;
|
|
1085
|
+
try { gh = require('tlc-claude-code/server/lib/github'); }
|
|
1086
|
+
catch { gh = require(path.join(process.cwd(), 'server/lib/github')); }
|
|
1087
|
+
try {
|
|
1088
|
+
const cfg = gh.loadGitHubConfig(process.cwd(), { fs }).config;
|
|
1089
|
+
if (!cfg) process.exit(0);
|
|
1090
|
+
const exec = (cmd) => execSync(cmd, { encoding: 'utf-8', stdio: ['pipe','pipe','pipe'] });
|
|
1091
|
+
const prNumber = Number(process.argv[1]);
|
|
1092
|
+
const planPath = process.argv[2];
|
|
1093
|
+
const content = fs.readFileSync(planPath, 'utf-8');
|
|
1094
|
+
const issueRefs = [...content.matchAll(/<!-- gh:(\d+) -->/g)].map(m => Number(m[1]));
|
|
1095
|
+
if (issueRefs.length > 0) {
|
|
1096
|
+
gh.linkPrToIssue({ ...cfg, prNumber, issueNumbers: issueRefs, exec });
|
|
1097
|
+
}
|
|
1098
|
+
} catch (e) { console.error('[TLC] GitHub PR link failed:', e.message); }
|
|
1099
|
+
" "{pr_number}" ".planning/phases/{N}-PLAN.md" 2>/dev/null
|
|
1100
|
+
```
|
|
1101
|
+
|
|
1102
|
+
Failures warn but never block.
|
|
1103
|
+
|
|
1003
1104
|
**The PR is the deliverable, not the commits.** CI runs on the PR. Merge when green.
|
|
1004
1105
|
|
|
1005
1106
|
**If the user has already been working on main** (e.g., first time using branching): push main directly but note that future phases MUST use branches.
|
|
@@ -28,6 +28,26 @@ Commands:
|
|
|
28
28
|
|
|
29
29
|
## Setup
|
|
30
30
|
|
|
31
|
+
### Implementation
|
|
32
|
+
|
|
33
|
+
Setup uses `server/lib/github/config.js` to auto-detect the repo and configure sync:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
node -e "
|
|
37
|
+
const path = require('path');
|
|
38
|
+
const fs = require('fs');
|
|
39
|
+
const { execSync } = require('child_process');
|
|
40
|
+
let gh;
|
|
41
|
+
try { gh = require('tlc-claude-code/server/lib/github'); }
|
|
42
|
+
catch { gh = require(path.join(process.cwd(), 'server/lib/github')); }
|
|
43
|
+
const exec = (cmd) => execSync(cmd, { encoding: 'utf-8', stdio: ['pipe','pipe','pipe'] });
|
|
44
|
+
const suggestion = gh.detectAndSuggestConfig({ ghClient: { detectRepo: () => gh.detectRepo({ exec }), checkAuth: () => gh.checkAuth({ exec }) }, ghProjects: null, projectDir: process.cwd(), fs });
|
|
45
|
+
console.log(JSON.stringify(suggestion, null, 2));
|
|
46
|
+
// If user approves, write config:
|
|
47
|
+
// gh.writeGitHubConfig(process.cwd(), suggestion.config, { fs });
|
|
48
|
+
" 2>/dev/null
|
|
49
|
+
```
|
|
50
|
+
|
|
31
51
|
### GitHub Issues
|
|
32
52
|
|
|
33
53
|
```
|
|
@@ -129,6 +149,32 @@ In `.tlc.json`:
|
|
|
129
149
|
|
|
130
150
|
## Sync Tasks
|
|
131
151
|
|
|
152
|
+
### Implementation
|
|
153
|
+
|
|
154
|
+
The sync is powered by `server/lib/github/plan-sync.js`:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
node -e "
|
|
158
|
+
const path = require('path');
|
|
159
|
+
const fs = require('fs');
|
|
160
|
+
const { execSync } = require('child_process');
|
|
161
|
+
let gh;
|
|
162
|
+
try { gh = require('tlc-claude-code/server/lib/github'); }
|
|
163
|
+
catch { gh = require(path.join(process.cwd(), 'server/lib/github')); }
|
|
164
|
+
const cfg = gh.loadGitHubConfig(process.cwd(), { fs }).config;
|
|
165
|
+
if (!cfg) { console.error('No GitHub config. Run /tlc:issues setup first.'); process.exit(1); }
|
|
166
|
+
const planPath = process.argv[1];
|
|
167
|
+
const r = gh.syncPlan({
|
|
168
|
+
planPath,
|
|
169
|
+
config: cfg,
|
|
170
|
+
ghClient: { exec: (cmd) => execSync(cmd, { encoding: 'utf-8', stdio: ['pipe','pipe','pipe'] }) },
|
|
171
|
+
ghProjects: null,
|
|
172
|
+
fs
|
|
173
|
+
});
|
|
174
|
+
console.log(JSON.stringify(r, null, 2));
|
|
175
|
+
" ".planning/phases/{N}-PLAN.md" 2>/dev/null
|
|
176
|
+
```
|
|
177
|
+
|
|
132
178
|
### Export to Issue Tracker
|
|
133
179
|
|
|
134
180
|
```
|
|
@@ -296,6 +296,49 @@ Create `.planning/phases/{N}-PLAN.md`:
|
|
|
296
296
|
- File size warnings: {None / list any files projected to exceed 1000 lines and planned split}
|
|
297
297
|
```
|
|
298
298
|
|
|
299
|
+
### Step 4b: GitHub Sync (automatic)
|
|
300
|
+
|
|
301
|
+
After writing the plan file, check if GitHub sync is enabled and sync tasks to GitHub Issues:
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
node -e "
|
|
305
|
+
const path = require('path');
|
|
306
|
+
let gh;
|
|
307
|
+
try { gh = require('tlc-claude-code/server/lib/github'); }
|
|
308
|
+
catch { gh = require(path.join(process.cwd(), 'server/lib/github')); }
|
|
309
|
+
console.log(gh.isGitHubEnabled(process.cwd()));
|
|
310
|
+
" 2>/dev/null
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
If the result is `true`:
|
|
314
|
+
|
|
315
|
+
1. Run the sync:
|
|
316
|
+
```bash
|
|
317
|
+
node -e "
|
|
318
|
+
const path = require('path');
|
|
319
|
+
const fs = require('fs');
|
|
320
|
+
const { execSync } = require('child_process');
|
|
321
|
+
let gh;
|
|
322
|
+
try { gh = require('tlc-claude-code/server/lib/github'); }
|
|
323
|
+
catch { gh = require(path.join(process.cwd(), 'server/lib/github')); }
|
|
324
|
+
const config = gh.loadGitHubConfig(process.cwd(), { fs }).config;
|
|
325
|
+
if (!config) { console.log('GitHub sync: no config'); process.exit(0); }
|
|
326
|
+
const planPath = process.argv[1];
|
|
327
|
+
const r = gh.syncPlan({
|
|
328
|
+
planPath,
|
|
329
|
+
config,
|
|
330
|
+
ghClient: { exec: (cmd) => execSync(cmd, { encoding: 'utf-8', stdio: ['pipe','pipe','pipe'] }) },
|
|
331
|
+
ghProjects: null,
|
|
332
|
+
fs
|
|
333
|
+
});
|
|
334
|
+
console.log(JSON.stringify(r));
|
|
335
|
+
" ".planning/phases/{N}-PLAN.md" 2>/dev/null
|
|
336
|
+
```
|
|
337
|
+
2. Report results: "GitHub: Created N issues, updated M, linked to project board"
|
|
338
|
+
3. If sync fails, warn but do not block: "GitHub sync failed: [reason]. Plan saved locally. Run `/tlc:issues sync` to retry."
|
|
339
|
+
|
|
340
|
+
If not enabled, skip silently (no message).
|
|
341
|
+
|
|
299
342
|
### Step 5: Review Plan
|
|
300
343
|
|
|
301
344
|
Present plan summary:
|