cckb 0.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/README.md +247 -0
- package/dist/bin/cckb.d.ts +1 -0
- package/dist/bin/cckb.js +89 -0
- package/dist/bin/cckb.js.map +1 -0
- package/dist/chunk-GUB5D6EN.js +197 -0
- package/dist/chunk-GUB5D6EN.js.map +1 -0
- package/dist/chunk-K4W3KOBL.js +239 -0
- package/dist/chunk-K4W3KOBL.js.map +1 -0
- package/dist/chunk-TFFLX3YY.js +131 -0
- package/dist/chunk-TFFLX3YY.js.map +1 -0
- package/dist/chunk-XAY6TTXB.js +149 -0
- package/dist/chunk-XAY6TTXB.js.map +1 -0
- package/dist/chunk-Z3CJQKTH.js +547 -0
- package/dist/chunk-Z3CJQKTH.js.map +1 -0
- package/dist/hooks/notification.d.ts +3 -0
- package/dist/hooks/notification.js +132 -0
- package/dist/hooks/notification.js.map +1 -0
- package/dist/hooks/post-tool-use.d.ts +3 -0
- package/dist/hooks/post-tool-use.js +96 -0
- package/dist/hooks/post-tool-use.js.map +1 -0
- package/dist/hooks/session-start.d.ts +3 -0
- package/dist/hooks/session-start.js +57 -0
- package/dist/hooks/session-start.js.map +1 -0
- package/dist/hooks/stop.d.ts +3 -0
- package/dist/hooks/stop.js +80 -0
- package/dist/hooks/stop.js.map +1 -0
- package/dist/hooks/user-prompt.d.ts +3 -0
- package/dist/hooks/user-prompt.js +48 -0
- package/dist/hooks/user-prompt.js.map +1 -0
- package/dist/index.d.ts +145 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -0
- package/templates/CLAUDE.md.tmpl +29 -0
- package/templates/settings.json.tmpl +44 -0
- package/templates/vault/INDEX.md +19 -0
- package/templates/vault/architecture.md +15 -0
- package/templates/vault/entities/INDEX.md +10 -0
- package/templates/vault/general-knowledge.md +15 -0
package/README.md
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# CCKB - Claude Code Knowledge Base
|
|
2
|
+
|
|
3
|
+
**Automatic project knowledge capture for Claude Code**
|
|
4
|
+
|
|
5
|
+
CCKB runs silently in the background, capturing conversations and building a structured knowledge base that Claude can reference in future sessions. No manual documentation needed — your project learns as you work.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## How It Works
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
13
|
+
│ Claude Code Session │
|
|
14
|
+
│ │
|
|
15
|
+
│ You: "Create an Order entity with id, items, total" │
|
|
16
|
+
│ │ │
|
|
17
|
+
│ ▼ │
|
|
18
|
+
│ ┌──────────────────────────────────────────────────────────────────┐ │
|
|
19
|
+
│ │ CCKB Hooks (Background) │ │
|
|
20
|
+
│ │ │ │
|
|
21
|
+
│ │ 📝 Captures conversation 🔍 Provides vault context │ │
|
|
22
|
+
│ │ 📁 Logs to conversations/ 💡 Injects relevant info │ │
|
|
23
|
+
│ │ 🗜️ Summarizes on session end 📚 Updates knowledge base │ │
|
|
24
|
+
│ └──────────────────────────────────────────────────────────────────┘ │
|
|
25
|
+
│ │ │
|
|
26
|
+
└────────────────────────────────────┼─────────────────────────────────────┘
|
|
27
|
+
▼
|
|
28
|
+
┌─────────────────────────────────────┐
|
|
29
|
+
│ cc-knowledge-base/ │
|
|
30
|
+
│ │
|
|
31
|
+
│ vault/ │
|
|
32
|
+
│ ├── INDEX.md │
|
|
33
|
+
│ ├── architecture.md │
|
|
34
|
+
│ ├── general-knowledge.md │
|
|
35
|
+
│ └── entities/ │
|
|
36
|
+
│ ├── INDEX.md │
|
|
37
|
+
│ └── order/ │
|
|
38
|
+
│ ├── INDEX.md │
|
|
39
|
+
│ ├── attributes.md │
|
|
40
|
+
│ └── services.md │
|
|
41
|
+
│ │
|
|
42
|
+
│ conversations/ │
|
|
43
|
+
│ └── {session-id}/ │
|
|
44
|
+
│ ├── 0.txt │
|
|
45
|
+
│ └── summary.md │
|
|
46
|
+
└─────────────────────────────────────┘
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### The Cycle
|
|
50
|
+
|
|
51
|
+
1. **Capture** — Hooks silently log user prompts and Claude's actions
|
|
52
|
+
2. **Compact** — On session end, conversations are summarized
|
|
53
|
+
3. **Integrate** — Entities, patterns, and knowledge are extracted and organized
|
|
54
|
+
4. **Feedback** — Future sessions receive relevant context from the vault
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Installation
|
|
59
|
+
|
|
60
|
+
### Option 1: npx (Recommended)
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# From your project directory
|
|
64
|
+
npx cckb init
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Option 2: Global Install
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npm install -g cckb
|
|
71
|
+
# Then from any project:
|
|
72
|
+
cckb init
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Option 3: pnpm
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
pnpm dlx cckb init
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## What Gets Installed
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
your-project/
|
|
87
|
+
├── .claude/
|
|
88
|
+
│ └── settings.json # Hook configuration added
|
|
89
|
+
├── cc-knowledge-base/
|
|
90
|
+
│ ├── .cckb-config.json # CCKB settings
|
|
91
|
+
│ ├── conversations/ # Session logs
|
|
92
|
+
│ └── vault/ # Knowledge base
|
|
93
|
+
│ ├── INDEX.md
|
|
94
|
+
│ ├── architecture.md
|
|
95
|
+
│ ├── general-knowledge.md
|
|
96
|
+
│ └── entities/
|
|
97
|
+
│ └── INDEX.md
|
|
98
|
+
├── CLAUDE.md # Vault directives added
|
|
99
|
+
└── .gitignore # State files excluded
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Configuration
|
|
105
|
+
|
|
106
|
+
Edit `cc-knowledge-base/.cckb-config.json`:
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"compaction": {
|
|
111
|
+
"trigger": "session_end",
|
|
112
|
+
"sizeThresholdKB": 50
|
|
113
|
+
},
|
|
114
|
+
"capture": {
|
|
115
|
+
"tools": ["Write", "Edit", "MultiEdit", "Bash", "Task"],
|
|
116
|
+
"maxContentLength": 500
|
|
117
|
+
},
|
|
118
|
+
"vault": {
|
|
119
|
+
"autoIntegrate": true,
|
|
120
|
+
"maxDepth": 5
|
|
121
|
+
},
|
|
122
|
+
"feedback": {
|
|
123
|
+
"enabled": true,
|
|
124
|
+
"contextDepth": 2
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
| Option | Description |
|
|
130
|
+
|--------|-------------|
|
|
131
|
+
| `compaction.trigger` | When to summarize: `session_end`, `size`, `messages`, or `manual` |
|
|
132
|
+
| `compaction.sizeThresholdKB` | File size threshold for rotation |
|
|
133
|
+
| `capture.tools` | Which tool outputs to capture |
|
|
134
|
+
| `capture.maxContentLength` | Max characters per tool output |
|
|
135
|
+
| `vault.autoIntegrate` | Auto-update vault after compaction |
|
|
136
|
+
| `vault.maxDepth` | Maximum folder nesting depth |
|
|
137
|
+
| `feedback.enabled` | Inject vault context into sessions |
|
|
138
|
+
| `feedback.contextDepth` | How many INDEX levels to load |
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Vault Structure
|
|
143
|
+
|
|
144
|
+
The vault uses **sparse loading** — Claude only reads INDEX.md files for navigation, loading specific files only when needed.
|
|
145
|
+
|
|
146
|
+
### INDEX.md Format
|
|
147
|
+
|
|
148
|
+
```markdown
|
|
149
|
+
# Entities
|
|
150
|
+
|
|
151
|
+
## Contents
|
|
152
|
+
|
|
153
|
+
| Item | Description |
|
|
154
|
+
|------|-------------|
|
|
155
|
+
| [user/](./user/INDEX.md) | User authentication and profiles |
|
|
156
|
+
| [order/](./order/INDEX.md) | Order processing and tracking |
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Entity Documentation
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
entities/order/
|
|
163
|
+
├── INDEX.md # Overview and links
|
|
164
|
+
├── attributes.md # Fields: id, items, total, status
|
|
165
|
+
└── services/
|
|
166
|
+
├── INDEX.md
|
|
167
|
+
├── repository.md # Data access methods
|
|
168
|
+
└── usecase.md # Business logic
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Hooks
|
|
174
|
+
|
|
175
|
+
CCKB installs these Claude Code hooks:
|
|
176
|
+
|
|
177
|
+
| Hook | Purpose |
|
|
178
|
+
|------|---------|
|
|
179
|
+
| `SessionStart` | Creates conversation folder, loads vault cache |
|
|
180
|
+
| `UserPromptSubmit` | Captures user messages |
|
|
181
|
+
| `PostToolUse` | Captures Claude's actions (filtered) |
|
|
182
|
+
| `Stop` | Triggers compaction and vault integration |
|
|
183
|
+
| `PreToolUse` | Injects relevant vault context |
|
|
184
|
+
|
|
185
|
+
All hooks run silently in the background.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Commands
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
cckb init [path] # Install CCKB in a project
|
|
193
|
+
cckb init --force # Reinstall, overwriting existing config
|
|
194
|
+
cckb hook <name> # Run a hook (internal use)
|
|
195
|
+
cckb version # Show version
|
|
196
|
+
cckb help # Show help
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## How Claude Uses the Vault
|
|
202
|
+
|
|
203
|
+
CCKB adds directives to your `CLAUDE.md`:
|
|
204
|
+
|
|
205
|
+
```markdown
|
|
206
|
+
## Project Knowledge Base (CCKB)
|
|
207
|
+
|
|
208
|
+
### Usage Guidelines
|
|
209
|
+
|
|
210
|
+
1. Before creating new entities/services:
|
|
211
|
+
- Check vault INDEX.md for existing patterns
|
|
212
|
+
- Review related entity structures
|
|
213
|
+
- Follow established architecture
|
|
214
|
+
|
|
215
|
+
2. When uncertain about project conventions:
|
|
216
|
+
- Consult vault/general-knowledge.md
|
|
217
|
+
- Check entity-specific INDEX.md files
|
|
218
|
+
|
|
219
|
+
3. Sparse Loading:
|
|
220
|
+
- Start with INDEX.md files only
|
|
221
|
+
- Deep-load specific files only when needed
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Development
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
# Clone the repo
|
|
230
|
+
git clone https://github.com/n3m/cckb.git
|
|
231
|
+
cd cckb
|
|
232
|
+
|
|
233
|
+
# Install dependencies
|
|
234
|
+
npm install
|
|
235
|
+
|
|
236
|
+
# Build
|
|
237
|
+
npm run build
|
|
238
|
+
|
|
239
|
+
# Test locally
|
|
240
|
+
node dist/bin/cckb.js init /tmp/test-project
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## License
|
|
246
|
+
|
|
247
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/bin/cckb.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
install
|
|
4
|
+
} from "../chunk-GUB5D6EN.js";
|
|
5
|
+
import "../chunk-TFFLX3YY.js";
|
|
6
|
+
|
|
7
|
+
// src/bin/cckb.ts
|
|
8
|
+
var args = process.argv.slice(2);
|
|
9
|
+
var command = args[0];
|
|
10
|
+
async function main() {
|
|
11
|
+
switch (command) {
|
|
12
|
+
case "init":
|
|
13
|
+
const targetPath = args[1] || process.cwd();
|
|
14
|
+
const force = args.includes("--force");
|
|
15
|
+
await install(targetPath, { force });
|
|
16
|
+
break;
|
|
17
|
+
case "hook":
|
|
18
|
+
const hookName = args[1];
|
|
19
|
+
await runHook(hookName);
|
|
20
|
+
break;
|
|
21
|
+
case "version":
|
|
22
|
+
case "-v":
|
|
23
|
+
case "--version":
|
|
24
|
+
console.log("cckb v0.1.0");
|
|
25
|
+
break;
|
|
26
|
+
case "help":
|
|
27
|
+
case "-h":
|
|
28
|
+
case "--help":
|
|
29
|
+
default:
|
|
30
|
+
printHelp();
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function runHook(hookName) {
|
|
35
|
+
switch (hookName) {
|
|
36
|
+
case "session-start": {
|
|
37
|
+
const { handleSessionStart } = await import("../hooks/session-start.js");
|
|
38
|
+
await handleSessionStart();
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
case "user-prompt": {
|
|
42
|
+
const { handleUserPrompt } = await import("../hooks/user-prompt.js");
|
|
43
|
+
await handleUserPrompt();
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
case "post-tool-use": {
|
|
47
|
+
const { handlePostToolUse } = await import("../hooks/post-tool-use.js");
|
|
48
|
+
await handlePostToolUse();
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
case "stop": {
|
|
52
|
+
const { handleStop } = await import("../hooks/stop.js");
|
|
53
|
+
await handleStop();
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
case "notification": {
|
|
57
|
+
const { handleNotification } = await import("../hooks/notification.js");
|
|
58
|
+
await handleNotification();
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
default:
|
|
62
|
+
console.error(`Unknown hook: ${hookName}`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function printHelp() {
|
|
67
|
+
console.log(`
|
|
68
|
+
CCKB - Claude Code Knowledge Base
|
|
69
|
+
|
|
70
|
+
Usage:
|
|
71
|
+
cckb init [path] Install CCKB into a project
|
|
72
|
+
cckb hook <name> Run a specific hook (internal use)
|
|
73
|
+
cckb version Show version
|
|
74
|
+
cckb help Show this help message
|
|
75
|
+
|
|
76
|
+
Options:
|
|
77
|
+
--force Overwrite existing installation
|
|
78
|
+
|
|
79
|
+
Examples:
|
|
80
|
+
cckb init Install in current directory
|
|
81
|
+
cckb init ./myapp Install in ./myapp directory
|
|
82
|
+
cckb init --force Reinstall, overwriting existing config
|
|
83
|
+
`);
|
|
84
|
+
}
|
|
85
|
+
main().catch((error) => {
|
|
86
|
+
console.error("Error:", error.message);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
});
|
|
89
|
+
//# sourceMappingURL=cckb.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/bin/cckb.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { install } from \"../cli/install.js\";\n\nconst args = process.argv.slice(2);\nconst command = args[0];\n\nasync function main() {\n switch (command) {\n case \"init\":\n const targetPath = args[1] || process.cwd();\n const force = args.includes(\"--force\");\n await install(targetPath, { force });\n break;\n\n case \"hook\":\n const hookName = args[1];\n await runHook(hookName);\n break;\n\n case \"version\":\n case \"-v\":\n case \"--version\":\n console.log(\"cckb v0.1.0\");\n break;\n\n case \"help\":\n case \"-h\":\n case \"--help\":\n default:\n printHelp();\n break;\n }\n}\n\nasync function runHook(hookName: string) {\n switch (hookName) {\n case \"session-start\": {\n const { handleSessionStart } = await import(\n \"../hooks/session-start.js\"\n );\n await handleSessionStart();\n break;\n }\n case \"user-prompt\": {\n const { handleUserPrompt } = await import(\"../hooks/user-prompt.js\");\n await handleUserPrompt();\n break;\n }\n case \"post-tool-use\": {\n const { handlePostToolUse } = await import(\"../hooks/post-tool-use.js\");\n await handlePostToolUse();\n break;\n }\n case \"stop\": {\n const { handleStop } = await import(\"../hooks/stop.js\");\n await handleStop();\n break;\n }\n case \"notification\": {\n const { handleNotification } = await import(\"../hooks/notification.js\");\n await handleNotification();\n break;\n }\n default:\n console.error(`Unknown hook: ${hookName}`);\n process.exit(1);\n }\n}\n\nfunction printHelp() {\n console.log(`\nCCKB - Claude Code Knowledge Base\n\nUsage:\n cckb init [path] Install CCKB into a project\n cckb hook <name> Run a specific hook (internal use)\n cckb version Show version\n cckb help Show this help message\n\nOptions:\n --force Overwrite existing installation\n\nExamples:\n cckb init Install in current directory\n cckb init ./myapp Install in ./myapp directory\n cckb init --force Reinstall, overwriting existing config\n`);\n}\n\nmain().catch((error) => {\n console.error(\"Error:\", error.message);\n process.exit(1);\n});\n"],"mappings":";;;;;;;AAIA,IAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,IAAM,UAAU,KAAK,CAAC;AAEtB,eAAe,OAAO;AACpB,UAAQ,SAAS;AAAA,IACf,KAAK;AACH,YAAM,aAAa,KAAK,CAAC,KAAK,QAAQ,IAAI;AAC1C,YAAM,QAAQ,KAAK,SAAS,SAAS;AACrC,YAAM,QAAQ,YAAY,EAAE,MAAM,CAAC;AACnC;AAAA,IAEF,KAAK;AACH,YAAM,WAAW,KAAK,CAAC;AACvB,YAAM,QAAQ,QAAQ;AACtB;AAAA,IAEF,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,cAAQ,IAAI,aAAa;AACzB;AAAA,IAEF,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL;AACE,gBAAU;AACV;AAAA,EACJ;AACF;AAEA,eAAe,QAAQ,UAAkB;AACvC,UAAQ,UAAU;AAAA,IAChB,KAAK,iBAAiB;AACpB,YAAM,EAAE,mBAAmB,IAAI,MAAM,OACnC,2BACF;AACA,YAAM,mBAAmB;AACzB;AAAA,IACF;AAAA,IACA,KAAK,eAAe;AAClB,YAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,yBAAyB;AACnE,YAAM,iBAAiB;AACvB;AAAA,IACF;AAAA,IACA,KAAK,iBAAiB;AACpB,YAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,2BAA2B;AACtE,YAAM,kBAAkB;AACxB;AAAA,IACF;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,EAAE,WAAW,IAAI,MAAM,OAAO,kBAAkB;AACtD,YAAM,WAAW;AACjB;AAAA,IACF;AAAA,IACA,KAAK,gBAAgB;AACnB,YAAM,EAAE,mBAAmB,IAAI,MAAM,OAAO,0BAA0B;AACtE,YAAM,mBAAmB;AACzB;AAAA,IACF;AAAA,IACA;AACE,cAAQ,MAAM,iBAAiB,QAAQ,EAAE;AACzC,cAAQ,KAAK,CAAC;AAAA,EAClB;AACF;AAEA,SAAS,YAAY;AACnB,UAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAgBb;AACD;AAEA,KAAK,EAAE,MAAM,CAAC,UAAU;AACtB,UAAQ,MAAM,UAAU,MAAM,OAAO;AACrC,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_CONFIG,
|
|
3
|
+
ensureDir,
|
|
4
|
+
fileExists,
|
|
5
|
+
readJSON,
|
|
6
|
+
readTextFile,
|
|
7
|
+
writeJSON,
|
|
8
|
+
writeTextFile
|
|
9
|
+
} from "./chunk-TFFLX3YY.js";
|
|
10
|
+
|
|
11
|
+
// src/cli/install.ts
|
|
12
|
+
import * as fs from "fs/promises";
|
|
13
|
+
import * as fsSync from "fs";
|
|
14
|
+
import * as path from "path";
|
|
15
|
+
import { fileURLToPath } from "url";
|
|
16
|
+
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
function findPackageRoot() {
|
|
18
|
+
let dir = __dirname;
|
|
19
|
+
for (let i = 0; i < 5; i++) {
|
|
20
|
+
const packageJson = path.join(dir, "package.json");
|
|
21
|
+
if (fsSync.existsSync(packageJson)) {
|
|
22
|
+
return dir;
|
|
23
|
+
}
|
|
24
|
+
dir = path.dirname(dir);
|
|
25
|
+
}
|
|
26
|
+
return path.resolve(__dirname, "../..");
|
|
27
|
+
}
|
|
28
|
+
var PACKAGE_ROOT = findPackageRoot();
|
|
29
|
+
var TEMPLATES_DIR = path.join(PACKAGE_ROOT, "templates");
|
|
30
|
+
async function install(targetPath, options = {}) {
|
|
31
|
+
const resolvedPath = path.resolve(targetPath);
|
|
32
|
+
console.log(`Installing CCKB to: ${resolvedPath}`);
|
|
33
|
+
await validateTargetPath(resolvedPath);
|
|
34
|
+
await checkExistingInstallation(resolvedPath, options.force);
|
|
35
|
+
await createDirectoryStructure(resolvedPath);
|
|
36
|
+
await copyTemplateFiles(resolvedPath);
|
|
37
|
+
await createConfigFile(resolvedPath);
|
|
38
|
+
await installHooks(resolvedPath);
|
|
39
|
+
await updateClaudeMd(resolvedPath);
|
|
40
|
+
await updateGitignore(resolvedPath);
|
|
41
|
+
console.log("\nCCKB installed successfully!");
|
|
42
|
+
console.log("\nNext steps:");
|
|
43
|
+
console.log(" 1. Review cc-knowledge-base/vault/ structure");
|
|
44
|
+
console.log(" 2. Check .claude/settings.json for hook configuration");
|
|
45
|
+
console.log(" 3. Start a new Claude Code session to begin capturing knowledge");
|
|
46
|
+
}
|
|
47
|
+
async function validateTargetPath(targetPath) {
|
|
48
|
+
const exists = await fileExists(targetPath);
|
|
49
|
+
if (!exists) {
|
|
50
|
+
throw new Error(`Target path does not exist: ${targetPath}`);
|
|
51
|
+
}
|
|
52
|
+
const stats = await fs.stat(targetPath);
|
|
53
|
+
if (!stats.isDirectory()) {
|
|
54
|
+
throw new Error(`Target path is not a directory: ${targetPath}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async function checkExistingInstallation(targetPath, force) {
|
|
58
|
+
const kbPath = path.join(targetPath, "cc-knowledge-base");
|
|
59
|
+
const exists = await fileExists(kbPath);
|
|
60
|
+
if (exists && !force) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
"CCKB is already installed in this project. Use --force to reinstall."
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async function createDirectoryStructure(targetPath) {
|
|
67
|
+
const directories = [
|
|
68
|
+
"cc-knowledge-base",
|
|
69
|
+
"cc-knowledge-base/conversations",
|
|
70
|
+
"cc-knowledge-base/vault",
|
|
71
|
+
"cc-knowledge-base/vault/entities",
|
|
72
|
+
"cc-knowledge-base/.cckb-state"
|
|
73
|
+
];
|
|
74
|
+
for (const dir of directories) {
|
|
75
|
+
await ensureDir(path.join(targetPath, dir));
|
|
76
|
+
}
|
|
77
|
+
console.log(" Created directory structure");
|
|
78
|
+
}
|
|
79
|
+
async function copyTemplateFiles(targetPath) {
|
|
80
|
+
const vaultFiles = [
|
|
81
|
+
{ src: "vault/INDEX.md", dest: "cc-knowledge-base/vault/INDEX.md" },
|
|
82
|
+
{
|
|
83
|
+
src: "vault/architecture.md",
|
|
84
|
+
dest: "cc-knowledge-base/vault/architecture.md"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
src: "vault/general-knowledge.md",
|
|
88
|
+
dest: "cc-knowledge-base/vault/general-knowledge.md"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
src: "vault/entities/INDEX.md",
|
|
92
|
+
dest: "cc-knowledge-base/vault/entities/INDEX.md"
|
|
93
|
+
}
|
|
94
|
+
];
|
|
95
|
+
for (const file of vaultFiles) {
|
|
96
|
+
const srcPath = path.join(TEMPLATES_DIR, file.src);
|
|
97
|
+
const destPath = path.join(targetPath, file.dest);
|
|
98
|
+
const content = await readTextFile(srcPath);
|
|
99
|
+
if (content) {
|
|
100
|
+
await writeTextFile(destPath, content);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
await writeTextFile(
|
|
104
|
+
path.join(targetPath, "cc-knowledge-base/conversations/.gitkeep"),
|
|
105
|
+
""
|
|
106
|
+
);
|
|
107
|
+
console.log(" Copied vault template files");
|
|
108
|
+
}
|
|
109
|
+
async function createConfigFile(targetPath) {
|
|
110
|
+
const configPath = path.join(
|
|
111
|
+
targetPath,
|
|
112
|
+
"cc-knowledge-base",
|
|
113
|
+
".cckb-config.json"
|
|
114
|
+
);
|
|
115
|
+
await writeJSON(configPath, DEFAULT_CONFIG);
|
|
116
|
+
console.log(" Created configuration file");
|
|
117
|
+
}
|
|
118
|
+
async function installHooks(targetPath) {
|
|
119
|
+
const claudeDir = path.join(targetPath, ".claude");
|
|
120
|
+
const settingsPath = path.join(claudeDir, "settings.json");
|
|
121
|
+
await ensureDir(claudeDir);
|
|
122
|
+
let settings = await readJSON(settingsPath) || {};
|
|
123
|
+
const templatePath = path.join(TEMPLATES_DIR, "settings.json.tmpl");
|
|
124
|
+
const hookSettings = await readJSON(
|
|
125
|
+
templatePath
|
|
126
|
+
);
|
|
127
|
+
if (!hookSettings) {
|
|
128
|
+
throw new Error("Failed to load hook template");
|
|
129
|
+
}
|
|
130
|
+
const existingHooks = settings.hooks || {};
|
|
131
|
+
const newHooks = hookSettings.hooks;
|
|
132
|
+
settings.hooks = mergeHooks(existingHooks, newHooks);
|
|
133
|
+
await writeJSON(settingsPath, settings);
|
|
134
|
+
console.log(" Installed hook configuration");
|
|
135
|
+
}
|
|
136
|
+
function mergeHooks(existing, incoming) {
|
|
137
|
+
const merged = { ...existing };
|
|
138
|
+
for (const [hookType, hooks] of Object.entries(incoming)) {
|
|
139
|
+
if (!merged[hookType]) {
|
|
140
|
+
merged[hookType] = [];
|
|
141
|
+
}
|
|
142
|
+
for (const hook of hooks) {
|
|
143
|
+
const hookObj = hook;
|
|
144
|
+
const command = hookObj.command;
|
|
145
|
+
if (command?.includes("cckb")) {
|
|
146
|
+
merged[hookType] = merged[hookType].filter((h) => {
|
|
147
|
+
const existingCmd = h.command;
|
|
148
|
+
return !existingCmd?.includes("cckb");
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
merged[hookType].push(hook);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return merged;
|
|
155
|
+
}
|
|
156
|
+
async function updateClaudeMd(targetPath) {
|
|
157
|
+
const claudeMdPath = path.join(targetPath, "CLAUDE.md");
|
|
158
|
+
const templatePath = path.join(TEMPLATES_DIR, "CLAUDE.md.tmpl");
|
|
159
|
+
const template = await readTextFile(templatePath);
|
|
160
|
+
if (!template) {
|
|
161
|
+
throw new Error("Failed to load CLAUDE.md template");
|
|
162
|
+
}
|
|
163
|
+
const marker = "## Project Knowledge Base (CCKB)";
|
|
164
|
+
let existing = await readTextFile(claudeMdPath);
|
|
165
|
+
if (existing) {
|
|
166
|
+
if (existing.includes(marker)) {
|
|
167
|
+
const regex = /## Project Knowledge Base \(CCKB\)[\s\S]*?(?=\n## |$)/;
|
|
168
|
+
existing = existing.replace(regex, template.trim());
|
|
169
|
+
} else {
|
|
170
|
+
existing = existing.trimEnd() + "\n\n" + template;
|
|
171
|
+
}
|
|
172
|
+
await writeTextFile(claudeMdPath, existing);
|
|
173
|
+
} else {
|
|
174
|
+
await writeTextFile(claudeMdPath, template);
|
|
175
|
+
}
|
|
176
|
+
console.log(" Updated CLAUDE.md with vault directives");
|
|
177
|
+
}
|
|
178
|
+
async function updateGitignore(targetPath) {
|
|
179
|
+
const gitignorePath = path.join(targetPath, ".gitignore");
|
|
180
|
+
const entries = [
|
|
181
|
+
"",
|
|
182
|
+
"# CCKB state files",
|
|
183
|
+
"cc-knowledge-base/.cckb-state/"
|
|
184
|
+
];
|
|
185
|
+
let existing = await readTextFile(gitignorePath) || "";
|
|
186
|
+
if (existing.includes("cc-knowledge-base/.cckb-state/")) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
existing = existing.trimEnd() + "\n" + entries.join("\n") + "\n";
|
|
190
|
+
await writeTextFile(gitignorePath, existing);
|
|
191
|
+
console.log(" Updated .gitignore");
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export {
|
|
195
|
+
install
|
|
196
|
+
};
|
|
197
|
+
//# sourceMappingURL=chunk-GUB5D6EN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/install.ts"],"sourcesContent":["import * as fs from \"node:fs/promises\";\nimport * as fsSync from \"node:fs\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n ensureDir,\n fileExists,\n readJSON,\n writeJSON,\n readTextFile,\n writeTextFile,\n} from \"../utils/file-utils.js\";\nimport { DEFAULT_CONFIG } from \"../utils/config.js\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n// Find package root by looking for package.json\nfunction findPackageRoot(): string {\n let dir = __dirname;\n for (let i = 0; i < 5; i++) {\n const packageJson = path.join(dir, \"package.json\");\n if (fsSync.existsSync(packageJson)) {\n return dir;\n }\n dir = path.dirname(dir);\n }\n // Fallback to relative from __dirname\n return path.resolve(__dirname, \"../..\");\n}\n\nconst PACKAGE_ROOT = findPackageRoot();\nconst TEMPLATES_DIR = path.join(PACKAGE_ROOT, \"templates\");\n\nexport interface InstallOptions {\n force?: boolean;\n}\n\nexport async function install(\n targetPath: string,\n options: InstallOptions = {}\n): Promise<void> {\n const resolvedPath = path.resolve(targetPath);\n\n console.log(`Installing CCKB to: ${resolvedPath}`);\n\n // Pre-flight checks\n await validateTargetPath(resolvedPath);\n await checkExistingInstallation(resolvedPath, options.force);\n\n // Create directory structure\n await createDirectoryStructure(resolvedPath);\n\n // Copy template files\n await copyTemplateFiles(resolvedPath);\n\n // Create config file\n await createConfigFile(resolvedPath);\n\n // Install hooks configuration\n await installHooks(resolvedPath);\n\n // Update CLAUDE.md\n await updateClaudeMd(resolvedPath);\n\n // Update .gitignore\n await updateGitignore(resolvedPath);\n\n console.log(\"\\nCCKB installed successfully!\");\n console.log(\"\\nNext steps:\");\n console.log(\" 1. Review cc-knowledge-base/vault/ structure\");\n console.log(\" 2. Check .claude/settings.json for hook configuration\");\n console.log(\" 3. Start a new Claude Code session to begin capturing knowledge\");\n}\n\nasync function validateTargetPath(targetPath: string): Promise<void> {\n const exists = await fileExists(targetPath);\n if (!exists) {\n throw new Error(`Target path does not exist: ${targetPath}`);\n }\n\n const stats = await fs.stat(targetPath);\n if (!stats.isDirectory()) {\n throw new Error(`Target path is not a directory: ${targetPath}`);\n }\n}\n\nasync function checkExistingInstallation(\n targetPath: string,\n force?: boolean\n): Promise<void> {\n const kbPath = path.join(targetPath, \"cc-knowledge-base\");\n const exists = await fileExists(kbPath);\n\n if (exists && !force) {\n throw new Error(\n \"CCKB is already installed in this project. Use --force to reinstall.\"\n );\n }\n}\n\nasync function createDirectoryStructure(targetPath: string): Promise<void> {\n const directories = [\n \"cc-knowledge-base\",\n \"cc-knowledge-base/conversations\",\n \"cc-knowledge-base/vault\",\n \"cc-knowledge-base/vault/entities\",\n \"cc-knowledge-base/.cckb-state\",\n ];\n\n for (const dir of directories) {\n await ensureDir(path.join(targetPath, dir));\n }\n\n console.log(\" Created directory structure\");\n}\n\nasync function copyTemplateFiles(targetPath: string): Promise<void> {\n const vaultFiles = [\n { src: \"vault/INDEX.md\", dest: \"cc-knowledge-base/vault/INDEX.md\" },\n {\n src: \"vault/architecture.md\",\n dest: \"cc-knowledge-base/vault/architecture.md\",\n },\n {\n src: \"vault/general-knowledge.md\",\n dest: \"cc-knowledge-base/vault/general-knowledge.md\",\n },\n {\n src: \"vault/entities/INDEX.md\",\n dest: \"cc-knowledge-base/vault/entities/INDEX.md\",\n },\n ];\n\n for (const file of vaultFiles) {\n const srcPath = path.join(TEMPLATES_DIR, file.src);\n const destPath = path.join(targetPath, file.dest);\n\n const content = await readTextFile(srcPath);\n if (content) {\n await writeTextFile(destPath, content);\n }\n }\n\n // Create .gitkeep for conversations\n await writeTextFile(\n path.join(targetPath, \"cc-knowledge-base/conversations/.gitkeep\"),\n \"\"\n );\n\n console.log(\" Copied vault template files\");\n}\n\nasync function createConfigFile(targetPath: string): Promise<void> {\n const configPath = path.join(\n targetPath,\n \"cc-knowledge-base\",\n \".cckb-config.json\"\n );\n\n await writeJSON(configPath, DEFAULT_CONFIG);\n\n console.log(\" Created configuration file\");\n}\n\nasync function installHooks(targetPath: string): Promise<void> {\n const claudeDir = path.join(targetPath, \".claude\");\n const settingsPath = path.join(claudeDir, \"settings.json\");\n\n await ensureDir(claudeDir);\n\n // Load existing settings or create new\n let settings: Record<string, unknown> =\n (await readJSON<Record<string, unknown>>(settingsPath)) || {};\n\n // Load hook template\n const templatePath = path.join(TEMPLATES_DIR, \"settings.json.tmpl\");\n const hookSettings = await readJSON<{ hooks: Record<string, unknown> }>(\n templatePath\n );\n\n if (!hookSettings) {\n throw new Error(\"Failed to load hook template\");\n }\n\n // Merge hooks (CCKB hooks take precedence for its own hook types)\n const existingHooks = (settings.hooks as Record<string, unknown[]>) || {};\n const newHooks = hookSettings.hooks as Record<string, unknown[]>;\n\n settings.hooks = mergeHooks(existingHooks, newHooks);\n\n await writeJSON(settingsPath, settings);\n\n console.log(\" Installed hook configuration\");\n}\n\nfunction mergeHooks(\n existing: Record<string, unknown[]>,\n incoming: Record<string, unknown[]>\n): Record<string, unknown[]> {\n const merged = { ...existing };\n\n for (const [hookType, hooks] of Object.entries(incoming)) {\n if (!merged[hookType]) {\n merged[hookType] = [];\n }\n\n // Add incoming hooks, avoiding duplicates based on command\n for (const hook of hooks) {\n const hookObj = hook as Record<string, unknown>;\n const command = hookObj.command as string | undefined;\n\n if (command?.includes(\"cckb\")) {\n // Remove any existing CCKB hooks for this type\n merged[hookType] = merged[hookType].filter((h) => {\n const existingCmd = (h as Record<string, unknown>).command as\n | string\n | undefined;\n return !existingCmd?.includes(\"cckb\");\n });\n }\n\n merged[hookType].push(hook);\n }\n }\n\n return merged;\n}\n\nasync function updateClaudeMd(targetPath: string): Promise<void> {\n const claudeMdPath = path.join(targetPath, \"CLAUDE.md\");\n const templatePath = path.join(TEMPLATES_DIR, \"CLAUDE.md.tmpl\");\n\n const template = await readTextFile(templatePath);\n if (!template) {\n throw new Error(\"Failed to load CLAUDE.md template\");\n }\n\n const marker = \"## Project Knowledge Base (CCKB)\";\n\n let existing = await readTextFile(claudeMdPath);\n\n if (existing) {\n // Check if CCKB section already exists\n if (existing.includes(marker)) {\n // Replace existing CCKB section\n const regex = /## Project Knowledge Base \\(CCKB\\)[\\s\\S]*?(?=\\n## |$)/;\n existing = existing.replace(regex, template.trim());\n } else {\n // Append CCKB section\n existing = existing.trimEnd() + \"\\n\\n\" + template;\n }\n await writeTextFile(claudeMdPath, existing);\n } else {\n // Create new CLAUDE.md with CCKB section\n await writeTextFile(claudeMdPath, template);\n }\n\n console.log(\" Updated CLAUDE.md with vault directives\");\n}\n\nasync function updateGitignore(targetPath: string): Promise<void> {\n const gitignorePath = path.join(targetPath, \".gitignore\");\n\n const entries = [\n \"\",\n \"# CCKB state files\",\n \"cc-knowledge-base/.cckb-state/\",\n ];\n\n let existing = (await readTextFile(gitignorePath)) || \"\";\n\n // Check if already added\n if (existing.includes(\"cc-knowledge-base/.cckb-state/\")) {\n return;\n }\n\n existing = existing.trimEnd() + \"\\n\" + entries.join(\"\\n\") + \"\\n\";\n await writeTextFile(gitignorePath, existing);\n\n console.log(\" Updated .gitignore\");\n}\n"],"mappings":"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","names":[]}
|