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
package/CLAUDE.md
CHANGED
|
@@ -1,201 +1,96 @@
|
|
|
1
|
-
# CLAUDE.md
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
| "checklist", "full check" | `/tlc:checklist` | Ad-hoc project review |
|
|
98
|
-
| "quick task", "small fix" | `/tlc:quick` | Coding without tests |
|
|
99
|
-
| "dashboard", "start dashboard", "dashboard container" | `/tlc:dashboard` | Running docker-compose manually |
|
|
100
|
-
|
|
101
|
-
### Before ANY work — run `/tlc`
|
|
102
|
-
|
|
103
|
-
**MANDATORY.** This detects state, syncs if needed, shows what's next.
|
|
104
|
-
|
|
105
|
-
### How to invoke TLC commands
|
|
106
|
-
|
|
107
|
-
Use the **Skill tool**: `Skill(skill="tlc:plan")`, `Skill(skill="tlc:build")`, etc.
|
|
108
|
-
|
|
109
|
-
When a command is invoked, its `.md` file gets loaded as instructions. **Follow those instructions step by step.** Do not skip steps. Do not improvise your own version.
|
|
110
|
-
|
|
111
|
-
### TLC file-based system
|
|
112
|
-
|
|
113
|
-
| Purpose | TLC Location |
|
|
114
|
-
|---------|--------------|
|
|
115
|
-
| Project overview | `PROJECT.md` |
|
|
116
|
-
| Roadmap & phases | `.planning/ROADMAP.md` |
|
|
117
|
-
| Phase plans | `.planning/phases/{N}-PLAN.md` |
|
|
118
|
-
| Task status | Markers in PLAN.md: `[ ]`, `[>@user]`, `[x@user]` |
|
|
119
|
-
| Bugs/feedback | `.planning/BUGS.md` |
|
|
120
|
-
| Test status | `.planning/phases/{N}-TESTS.md` |
|
|
121
|
-
| Config | `.tlc.json` |
|
|
122
|
-
|
|
123
|
-
## Test-First Development (Non-Negotiable)
|
|
124
|
-
|
|
125
|
-
All implementation follows **Red → Green → Refactor**:
|
|
126
|
-
|
|
127
|
-
1. **Red**: Write failing tests that define expected behavior
|
|
128
|
-
2. **Green**: Write minimum code to make tests pass
|
|
129
|
-
3. **Refactor**: Clean up while keeping tests green
|
|
130
|
-
|
|
131
|
-
Tests are written BEFORE implementation, not after.
|
|
132
|
-
|
|
133
|
-
**This means:**
|
|
134
|
-
- Do NOT write a function and then add tests after
|
|
135
|
-
- Do NOT "implement first and test later"
|
|
136
|
-
- Do NOT skip tests for "simple" code
|
|
137
|
-
- The `/tlc:build` command enforces this — use it instead of coding directly
|
|
138
|
-
|
|
139
|
-
**If the user says "implement X" or "build X" or "add X":**
|
|
140
|
-
1. Check `/tlc:progress` for current state
|
|
141
|
-
2. If no plan exists → run `/tlc:plan` first
|
|
142
|
-
3. If plan exists → run `/tlc:build` which writes tests first
|
|
143
|
-
4. Never jump straight to implementation
|
|
144
|
-
|
|
145
|
-
## Context Management
|
|
146
|
-
|
|
147
|
-
**Use multiple sessions/agents for large tasks.** When working in overdrive mode or across workspaces:
|
|
148
|
-
- Use the `Task` tool to spawn sub-agents for independent work (research, testing, building)
|
|
149
|
-
- Keep the main conversation focused on orchestration and decisions
|
|
150
|
-
- Delegate file-heavy operations (reading many files, running test suites) to sub-agents
|
|
151
|
-
- This prevents context window overflow, which causes crashes and lost work
|
|
152
|
-
- Especially critical in workspace mode where multiple repos are involved
|
|
153
|
-
|
|
154
|
-
**Signs you need to delegate:** If you've read 15+ files, run 10+ commands, or the conversation is getting long — spawn a sub-agent for the next chunk of work.
|
|
155
|
-
|
|
156
|
-
## After TLC Updates
|
|
157
|
-
|
|
158
|
-
If TLC command files are updated, re-read them before executing. Check version in `package.json`.
|
|
159
|
-
|
|
160
|
-
## Multi-User Collaboration
|
|
161
|
-
|
|
162
|
-
When working with teammates:
|
|
163
|
-
- Claim tasks before starting: `/tlc:claim`
|
|
164
|
-
- Release if blocked: `/tlc:release`
|
|
165
|
-
- Check team status: `/tlc:who`
|
|
166
|
-
- Pull before claiming: `git pull`
|
|
167
|
-
- Push after claiming: `git push`
|
|
168
|
-
|
|
169
|
-
## Git Commits
|
|
170
|
-
|
|
171
|
-
**⛔ NEVER ADD CO-AUTHORED-BY LINES TO COMMITS ⛔**
|
|
172
|
-
|
|
173
|
-
- NO `Co-Authored-By: Claude`
|
|
174
|
-
- NO `Co-Authored-By: Anthropic`
|
|
175
|
-
- NO co-authoring of any kind
|
|
176
|
-
- The USER is the author. Claude is a tool, not an author.
|
|
177
|
-
|
|
178
|
-
**ALWAYS ask before `git push`.** Never push to remote without explicit user approval.
|
|
179
|
-
|
|
180
|
-
---
|
|
181
|
-
|
|
182
|
-
<!-- TLC-STANDARDS -->
|
|
183
|
-
|
|
184
|
-
## Code Quality (TLC)
|
|
185
|
-
|
|
186
|
-
This project follows TLC (Test-Led Coding) code quality standards. See [CODING-STANDARDS.md](./CODING-STANDARDS.md) for detailed guidelines.
|
|
187
|
-
|
|
188
|
-
### Quick Reference
|
|
189
|
-
|
|
190
|
-
**Module Structure:** Code lives in `server/lib/` - each module is a self-contained `.js` file with corresponding `.test.js` test file.
|
|
191
|
-
|
|
192
|
-
### Key Rules
|
|
193
|
-
|
|
194
|
-
1. **Test-first development** - Tests are written BEFORE implementation
|
|
195
|
-
2. **No hardcoded URLs or config** - Use environment variables
|
|
196
|
-
3. **JSDoc required** - Document all exported functions
|
|
197
|
-
4. **Paired test files** - Every `module.js` has a `module.test.js`
|
|
198
|
-
|
|
199
|
-
### Standards Reference
|
|
200
|
-
|
|
201
|
-
For complete standards including file naming, import rules, error handling patterns, and service design guidelines, see [CODING-STANDARDS.md](./CODING-STANDARDS.md).
|
|
1
|
+
# CLAUDE.md — TLC Project
|
|
2
|
+
|
|
3
|
+
> **This is a TLC project. All work goes through `/tlc` commands. Run `/tlc` first.**
|
|
4
|
+
|
|
5
|
+
## Rules (Enforced by hooks — violations are blocked)
|
|
6
|
+
|
|
7
|
+
1. **Tests before code.** Always. Red → Green → Refactor. Use `/tlc:build`.
|
|
8
|
+
2. **Plans go in files.** Use `/tlc:plan` → writes to `.planning/phases/`. Never plan in chat.
|
|
9
|
+
3. **No direct implementation.** User says "build X" → run `/tlc:progress` then `/tlc:build`.
|
|
10
|
+
4. **No Co-Authored-By in commits.** The user is the author. Claude is a tool.
|
|
11
|
+
5. **Ask before `git push`.** Never push without explicit approval.
|
|
12
|
+
|
|
13
|
+
## Command Dispatch
|
|
14
|
+
|
|
15
|
+
When the user says X → invoke `Skill(skill="tlc:...")`:
|
|
16
|
+
|
|
17
|
+
| User Says | Run This |
|
|
18
|
+
|-----------|----------|
|
|
19
|
+
| "plan", "break this down" | `/tlc:plan` |
|
|
20
|
+
| "build", "implement", "add feature" | `/tlc:build` |
|
|
21
|
+
| "review", "check code" | `/tlc:review` |
|
|
22
|
+
| "status", "what's next", "where are we" | `/tlc:progress` |
|
|
23
|
+
| "discuss", "talk about approach" | `/tlc:discuss` |
|
|
24
|
+
| "test", "run tests" | `/tlc:status` |
|
|
25
|
+
| "fix tests", "tests failing" | `/tlc:autofix` |
|
|
26
|
+
| "refactor", "clean up" | `/tlc:refactor` |
|
|
27
|
+
| "deploy", "set up server" | `/tlc:deploy` |
|
|
28
|
+
| "coverage", "what's untested" | `/tlc:coverage` |
|
|
29
|
+
| "edge cases", "more tests" | `/tlc:edge-cases` |
|
|
30
|
+
| "security", "audit" | `/tlc:security` |
|
|
31
|
+
| "docs", "documentation" | `/tlc:docs` |
|
|
32
|
+
| "new project" | `/tlc:new-project` |
|
|
33
|
+
| "init", "add tlc" | `/tlc:init` |
|
|
34
|
+
| "configure", "setup" | `/tlc:config` |
|
|
35
|
+
| "bug", "found a bug" | `/tlc:bug` |
|
|
36
|
+
| "claim", "I'll take this" | `/tlc:claim` |
|
|
37
|
+
| "release", "can't finish" | `/tlc:release` |
|
|
38
|
+
| "who's working", "team" | `/tlc:who` |
|
|
39
|
+
| "verify", "check my work" | `/tlc:verify` |
|
|
40
|
+
| "complete", "milestone done" | `/tlc:complete` |
|
|
41
|
+
| "quality", "test quality" | `/tlc:quality` |
|
|
42
|
+
| "outdated", "dependencies" | `/tlc:outdated` |
|
|
43
|
+
| "cleanup", "fix standards" | `/tlc:cleanup` |
|
|
44
|
+
| "ci", "github actions" | `/tlc:ci` |
|
|
45
|
+
| "export", "cursor rules" | `/tlc:export` |
|
|
46
|
+
| "models", "llm", "providers" | `/tlc:llm` |
|
|
47
|
+
| "issues", "import issues" | `/tlc:issues` |
|
|
48
|
+
| "checklist" | `/tlc:checklist` |
|
|
49
|
+
| "quick task", "small fix" | `/tlc:quick` |
|
|
50
|
+
| "dashboard" | `/tlc:dashboard` |
|
|
51
|
+
| "review PR" | `/tlc:review-pr` |
|
|
52
|
+
| "watch ci", "fix ci", "ci failing" | `/tlc:watchci` |
|
|
53
|
+
| "e2e", "screenshot", "visual check" | `/tlc:e2e-verify` |
|
|
54
|
+
| "guard", "check process", "validate" | `/tlc:guard` |
|
|
55
|
+
| "preflight", "am I done", "check gaps" | `/tlc:preflight` |
|
|
56
|
+
|
|
57
|
+
## TLC File System
|
|
58
|
+
|
|
59
|
+
| Purpose | Location |
|
|
60
|
+
|---------|----------|
|
|
61
|
+
| Project overview | `PROJECT.md` |
|
|
62
|
+
| Roadmap & phases | `.planning/ROADMAP.md` |
|
|
63
|
+
| Phase plans | `.planning/phases/{N}-PLAN.md` |
|
|
64
|
+
| Task status | `[ ]` / `[>@user]` / `[x@user]` markers in plan files |
|
|
65
|
+
| Bugs/feedback | `.planning/BUGS.md` |
|
|
66
|
+
| Config | `.tlc.json` |
|
|
67
|
+
|
|
68
|
+
## LLM Router
|
|
69
|
+
|
|
70
|
+
Check `.tlc.json` for `router.capabilities` before routing work. If no config exists, Claude handles everything. Commands: `/tlc:llm status`, `/tlc:llm config`, `/tlc:llm test`.
|
|
71
|
+
|
|
72
|
+
## Context Management
|
|
73
|
+
|
|
74
|
+
Use `Task` tool to spawn sub-agents for independent work. Keep main conversation for orchestration. Delegate when you've read 15+ files or run 10+ commands.
|
|
75
|
+
|
|
76
|
+
## Multi-User Collaboration
|
|
77
|
+
|
|
78
|
+
Claim tasks before starting: `/tlc:claim`. Release if blocked: `/tlc:release`. Check team: `/tlc:who`. Pull before claiming, push after.
|
|
79
|
+
|
|
80
|
+
## Memory Auto-Capture
|
|
81
|
+
|
|
82
|
+
Conversations are automatically captured via the Claude Code `Stop` hook. After each response, the hook POSTs the exchange to the TLC server's capture endpoint. The pattern detector classifies decisions, gotchas, and preferences into team memory files under `.tlc/memory/team/`.
|
|
83
|
+
|
|
84
|
+
- **Resilience:** If the server is unreachable, exchanges spool to `.tlc/memory/.spool.jsonl` and drain on the next successful capture.
|
|
85
|
+
- **Endpoint hardening:** Payloads are capped at 100KB, deduplicated within a 60s window, and rate-limited to 100 captures/minute per project.
|
|
86
|
+
- **Disable:** Remove the `Stop` hook entry from `.claude/settings.json`.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
<!-- TLC-STANDARDS -->
|
|
91
|
+
|
|
92
|
+
## Code Quality (TLC)
|
|
93
|
+
|
|
94
|
+
See [CODING-STANDARDS.md](./CODING-STANDARDS.md) for full standards.
|
|
95
|
+
|
|
96
|
+
**Quick reference:** Modules in `server/lib/`. Each `module.js` has `module.test.js`. JSDoc required on exports. No hardcoded URLs — use env vars.
|
package/bin/install.js
CHANGED
|
@@ -33,6 +33,7 @@ const COMMANDS = [
|
|
|
33
33
|
'sync.md',
|
|
34
34
|
'new-project.md',
|
|
35
35
|
'init.md',
|
|
36
|
+
'bootstrap.md',
|
|
36
37
|
'import-project.md',
|
|
37
38
|
'discuss.md',
|
|
38
39
|
'plan.md',
|
|
@@ -78,10 +79,30 @@ const COMMANDS = [
|
|
|
78
79
|
'deploy.md',
|
|
79
80
|
// Multi-Model
|
|
80
81
|
'llm.md',
|
|
82
|
+
// Plugins (auto-run via hooks)
|
|
83
|
+
'watchci.md',
|
|
84
|
+
'e2e-verify.md',
|
|
85
|
+
'guard.md',
|
|
86
|
+
'preflight.md',
|
|
87
|
+
// Memory
|
|
88
|
+
'remember.md',
|
|
89
|
+
'recall.md',
|
|
90
|
+
// Dashboard
|
|
91
|
+
'dashboard.md',
|
|
81
92
|
// Help
|
|
82
93
|
'help.md'
|
|
83
94
|
];
|
|
84
95
|
|
|
96
|
+
// Hook scripts that power the plugin system
|
|
97
|
+
const HOOKS = [
|
|
98
|
+
'tlc-block-tools.sh',
|
|
99
|
+
'tlc-prompt-guard.sh',
|
|
100
|
+
'tlc-session-init.sh',
|
|
101
|
+
'tlc-post-push.sh',
|
|
102
|
+
'tlc-post-build.sh',
|
|
103
|
+
'tlc-capture-exchange.sh'
|
|
104
|
+
];
|
|
105
|
+
|
|
85
106
|
function getGlobalDir() {
|
|
86
107
|
const claudeConfig = process.env.CLAUDE_CONFIG_DIR || path.join(require('os').homedir(), '.claude');
|
|
87
108
|
return path.join(claudeConfig, 'commands');
|
|
@@ -115,15 +136,20 @@ function info(msg) {
|
|
|
115
136
|
|
|
116
137
|
function install(targetDir, installType) {
|
|
117
138
|
const commandsDir = path.join(targetDir, 'tlc');
|
|
139
|
+
const packageRoot = path.join(__dirname, '..');
|
|
118
140
|
|
|
119
141
|
// Create directory
|
|
120
142
|
fs.mkdirSync(commandsDir, { recursive: true });
|
|
121
143
|
|
|
122
144
|
// Copy command files with version injection
|
|
123
|
-
|
|
145
|
+
// Try .claude/commands/tlc/ first (npm package structure), fall back to root (dev)
|
|
146
|
+
const commandsSrcDir = fs.existsSync(path.join(packageRoot, '.claude', 'commands', 'tlc'))
|
|
147
|
+
? path.join(packageRoot, '.claude', 'commands', 'tlc')
|
|
148
|
+
: packageRoot;
|
|
149
|
+
|
|
124
150
|
let installed = 0;
|
|
125
151
|
for (const file of COMMANDS) {
|
|
126
|
-
const src = path.join(
|
|
152
|
+
const src = path.join(commandsSrcDir, file);
|
|
127
153
|
const dest = path.join(commandsDir, file);
|
|
128
154
|
if (fs.existsSync(src)) {
|
|
129
155
|
// Read, replace {{VERSION}}, write
|
|
@@ -135,6 +161,19 @@ function install(targetDir, installType) {
|
|
|
135
161
|
}
|
|
136
162
|
|
|
137
163
|
success(`Installed ${installed} commands to ${c.cyan}${commandsDir}${c.reset}`);
|
|
164
|
+
|
|
165
|
+
// Install hooks (plugin system)
|
|
166
|
+
const hooksInstalled = installHooks(targetDir, packageRoot);
|
|
167
|
+
if (hooksInstalled > 0) {
|
|
168
|
+
success(`Installed ${hooksInstalled} hooks to ${c.cyan}${path.dirname(targetDir)}/.claude/hooks/${c.reset}`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Install settings template (with hooks wiring)
|
|
172
|
+
const settingsInstalled = installSettings(targetDir);
|
|
173
|
+
if (settingsInstalled) {
|
|
174
|
+
success(`Installed settings template with hook wiring`);
|
|
175
|
+
}
|
|
176
|
+
|
|
138
177
|
log('');
|
|
139
178
|
log(`${c.green}Done!${c.reset} Restart Claude Code to load commands.`);
|
|
140
179
|
log('');
|
|
@@ -150,6 +189,136 @@ function install(targetDir, installType) {
|
|
|
150
189
|
log('');
|
|
151
190
|
}
|
|
152
191
|
|
|
192
|
+
function installHooks(targetDir, packageRoot) {
|
|
193
|
+
// For local install: .claude/commands -> go up to .claude/hooks
|
|
194
|
+
// For global install: ~/.claude/commands -> go up to ~/.claude/hooks
|
|
195
|
+
const claudeDir = path.dirname(targetDir);
|
|
196
|
+
const hooksDestDir = path.join(claudeDir, 'hooks');
|
|
197
|
+
fs.mkdirSync(hooksDestDir, { recursive: true });
|
|
198
|
+
|
|
199
|
+
// Try .claude/hooks/ first (npm package), fall back to root .claude/hooks/ (dev)
|
|
200
|
+
const hooksSrcDir = fs.existsSync(path.join(packageRoot, '.claude', 'hooks'))
|
|
201
|
+
? path.join(packageRoot, '.claude', 'hooks')
|
|
202
|
+
: null;
|
|
203
|
+
|
|
204
|
+
if (!hooksSrcDir) return 0;
|
|
205
|
+
|
|
206
|
+
let copied = 0;
|
|
207
|
+
for (const file of HOOKS) {
|
|
208
|
+
const src = path.join(hooksSrcDir, file);
|
|
209
|
+
const dest = path.join(hooksDestDir, file);
|
|
210
|
+
if (fs.existsSync(src)) {
|
|
211
|
+
fs.copyFileSync(src, dest);
|
|
212
|
+
// Make executable
|
|
213
|
+
fs.chmodSync(dest, 0o755);
|
|
214
|
+
copied++;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return copied;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function installSettings(targetDir) {
|
|
221
|
+
// For local install: .claude/commands -> go up to .claude/settings.json
|
|
222
|
+
// For global install: ~/.claude/commands -> go up to ~/.claude/settings.json
|
|
223
|
+
const claudeDir = path.dirname(targetDir);
|
|
224
|
+
const settingsPath = path.join(claudeDir, 'settings.json');
|
|
225
|
+
|
|
226
|
+
// The settings template with full hook wiring
|
|
227
|
+
const settingsTemplate = {
|
|
228
|
+
permissions: {
|
|
229
|
+
allow: [
|
|
230
|
+
"Bash(npm *)", "Bash(npx *)", "Bash(node *)", "Bash(git *)",
|
|
231
|
+
"Bash(gh *)", "Bash(ssh *)", "Bash(scp *)", "Bash(rsync *)",
|
|
232
|
+
"Bash(curl *)", "Bash(wget *)", "Bash(docker *)", "Bash(docker-compose *)",
|
|
233
|
+
"Bash(pytest*)", "Bash(python *)", "Bash(pip *)", "Bash(go *)",
|
|
234
|
+
"Bash(cargo *)", "Bash(make *)", "Bash(cat *)", "Bash(ls *)",
|
|
235
|
+
"Bash(pwd*)", "Bash(cd *)", "Bash(mkdir *)", "Bash(cp *)",
|
|
236
|
+
"Bash(mv *)", "Bash(which *)", "Bash(echo *)", "Bash(jq *)",
|
|
237
|
+
"Bash(wc *)", "Bash(head *)", "Bash(tail *)", "Bash(sort *)",
|
|
238
|
+
"Bash(uniq *)", "Bash(xargs *)"
|
|
239
|
+
]
|
|
240
|
+
},
|
|
241
|
+
hooks: {
|
|
242
|
+
PreToolUse: [{
|
|
243
|
+
matcher: "EnterPlanMode|TaskCreate|TaskUpdate|TaskList|TaskGet|ExitPlanMode",
|
|
244
|
+
hooks: [{
|
|
245
|
+
type: "command",
|
|
246
|
+
command: "bash $CLAUDE_PROJECT_DIR/.claude/hooks/tlc-block-tools.sh",
|
|
247
|
+
timeout: 5
|
|
248
|
+
}]
|
|
249
|
+
}],
|
|
250
|
+
UserPromptSubmit: [{
|
|
251
|
+
hooks: [{
|
|
252
|
+
type: "command",
|
|
253
|
+
command: "bash $CLAUDE_PROJECT_DIR/.claude/hooks/tlc-prompt-guard.sh",
|
|
254
|
+
timeout: 5
|
|
255
|
+
}]
|
|
256
|
+
}],
|
|
257
|
+
SessionStart: [{
|
|
258
|
+
hooks: [{
|
|
259
|
+
type: "command",
|
|
260
|
+
command: "bash $CLAUDE_PROJECT_DIR/.claude/hooks/tlc-session-init.sh",
|
|
261
|
+
timeout: 5
|
|
262
|
+
}]
|
|
263
|
+
}],
|
|
264
|
+
PostToolUse: [
|
|
265
|
+
{
|
|
266
|
+
matcher: "Bash",
|
|
267
|
+
hooks: [{
|
|
268
|
+
type: "command",
|
|
269
|
+
command: "bash $CLAUDE_PROJECT_DIR/.claude/hooks/tlc-post-push.sh",
|
|
270
|
+
timeout: 5
|
|
271
|
+
}]
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
matcher: "Skill",
|
|
275
|
+
hooks: [{
|
|
276
|
+
type: "command",
|
|
277
|
+
command: "bash $CLAUDE_PROJECT_DIR/.claude/hooks/tlc-post-build.sh",
|
|
278
|
+
timeout: 5
|
|
279
|
+
}]
|
|
280
|
+
}
|
|
281
|
+
],
|
|
282
|
+
Stop: [{
|
|
283
|
+
hooks: [{
|
|
284
|
+
type: "command",
|
|
285
|
+
command: "bash \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/tlc-capture-exchange.sh",
|
|
286
|
+
timeout: 30
|
|
287
|
+
}]
|
|
288
|
+
}]
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
if (fs.existsSync(settingsPath)) {
|
|
293
|
+
// Merge: preserve existing permissions, add missing hooks
|
|
294
|
+
try {
|
|
295
|
+
const existing = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
296
|
+
// Merge permissions (union)
|
|
297
|
+
if (existing.permissions && existing.permissions.allow) {
|
|
298
|
+
const existingSet = new Set(existing.permissions.allow);
|
|
299
|
+
for (const perm of settingsTemplate.permissions.allow) {
|
|
300
|
+
existingSet.add(perm);
|
|
301
|
+
}
|
|
302
|
+
existing.permissions.allow = [...existingSet];
|
|
303
|
+
} else {
|
|
304
|
+
existing.permissions = settingsTemplate.permissions;
|
|
305
|
+
}
|
|
306
|
+
// Add hooks if not present
|
|
307
|
+
if (!existing.hooks) {
|
|
308
|
+
existing.hooks = settingsTemplate.hooks;
|
|
309
|
+
}
|
|
310
|
+
fs.writeFileSync(settingsPath, JSON.stringify(existing, null, 2) + '\n');
|
|
311
|
+
return true;
|
|
312
|
+
} catch (err) {
|
|
313
|
+
// If we can't parse existing, don't overwrite
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
} else {
|
|
317
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settingsTemplate, null, 2) + '\n');
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
153
322
|
async function main() {
|
|
154
323
|
const args = process.argv.slice(2);
|
|
155
324
|
|
package/bin/postinstall.js
CHANGED
|
@@ -4,44 +4,63 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const os = require('os');
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
7
|
+
const packageRoot = path.join(__dirname, '..');
|
|
8
|
+
const claudeHome = path.join(os.homedir(), '.claude');
|
|
9
|
+
|
|
10
|
+
// Source directories (inside npm package)
|
|
11
|
+
const commandsSrcDir = path.join(packageRoot, '.claude', 'commands', 'tlc');
|
|
12
|
+
const hooksSrcDir = path.join(packageRoot, '.claude', 'hooks');
|
|
13
|
+
|
|
14
|
+
// Destination directories (user's home)
|
|
15
|
+
const commandsDestDir = path.join(claudeHome, 'commands', 'tlc');
|
|
16
|
+
const hooksDestDir = path.join(claudeHome, 'hooks');
|
|
10
17
|
|
|
11
|
-
// Create destination directory if it doesn't exist
|
|
12
18
|
function ensureDir(dir) {
|
|
13
19
|
if (!fs.existsSync(dir)) {
|
|
14
20
|
fs.mkdirSync(dir, { recursive: true });
|
|
15
21
|
}
|
|
16
22
|
}
|
|
17
23
|
|
|
18
|
-
// Copy all .md files
|
|
24
|
+
// Copy all .md command files
|
|
19
25
|
function copyCommands() {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
ensureDir(destDir);
|
|
26
|
+
if (!fs.existsSync(commandsSrcDir)) return 0;
|
|
27
|
+
ensureDir(commandsDestDir);
|
|
23
28
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
const files = fs.readdirSync(commandsSrcDir).filter(f => f.endsWith('.md'));
|
|
30
|
+
let copied = 0;
|
|
31
|
+
for (const file of files) {
|
|
32
|
+
fs.copyFileSync(path.join(commandsSrcDir, file), path.join(commandsDestDir, file));
|
|
33
|
+
copied++;
|
|
34
|
+
}
|
|
35
|
+
return copied;
|
|
36
|
+
}
|
|
29
37
|
|
|
30
|
-
|
|
31
|
-
|
|
38
|
+
// Copy all .sh hook files
|
|
39
|
+
function copyHooks() {
|
|
40
|
+
if (!fs.existsSync(hooksSrcDir)) return 0;
|
|
41
|
+
ensureDir(hooksDestDir);
|
|
32
42
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
43
|
+
const files = fs.readdirSync(hooksSrcDir).filter(f => f.endsWith('.sh'));
|
|
44
|
+
let copied = 0;
|
|
45
|
+
for (const file of files) {
|
|
46
|
+
const dest = path.join(hooksDestDir, file);
|
|
47
|
+
fs.copyFileSync(path.join(hooksSrcDir, file), dest);
|
|
48
|
+
fs.chmodSync(dest, 0o755);
|
|
49
|
+
copied++;
|
|
50
|
+
}
|
|
51
|
+
return copied;
|
|
52
|
+
}
|
|
37
53
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
54
|
+
function postinstall() {
|
|
55
|
+
try {
|
|
56
|
+
const commands = copyCommands();
|
|
57
|
+
const hooks = copyHooks();
|
|
42
58
|
|
|
43
|
-
if (
|
|
44
|
-
console.log(`\x1b[32m✓\x1b[0m TLC: Installed ${
|
|
59
|
+
if (commands > 0) {
|
|
60
|
+
console.log(`\x1b[32m✓\x1b[0m TLC: Installed ${commands} commands to ~/.claude/commands/tlc/`);
|
|
61
|
+
}
|
|
62
|
+
if (hooks > 0) {
|
|
63
|
+
console.log(`\x1b[32m✓\x1b[0m TLC: Installed ${hooks} hooks to ~/.claude/hooks/`);
|
|
45
64
|
}
|
|
46
65
|
} catch (err) {
|
|
47
66
|
// Silent fail - don't break npm install
|
|
@@ -51,4 +70,4 @@ function copyCommands() {
|
|
|
51
70
|
}
|
|
52
71
|
}
|
|
53
72
|
|
|
54
|
-
|
|
73
|
+
postinstall();
|