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.
Files changed (39) hide show
  1. package/README.md +247 -0
  2. package/dist/bin/cckb.d.ts +1 -0
  3. package/dist/bin/cckb.js +89 -0
  4. package/dist/bin/cckb.js.map +1 -0
  5. package/dist/chunk-GUB5D6EN.js +197 -0
  6. package/dist/chunk-GUB5D6EN.js.map +1 -0
  7. package/dist/chunk-K4W3KOBL.js +239 -0
  8. package/dist/chunk-K4W3KOBL.js.map +1 -0
  9. package/dist/chunk-TFFLX3YY.js +131 -0
  10. package/dist/chunk-TFFLX3YY.js.map +1 -0
  11. package/dist/chunk-XAY6TTXB.js +149 -0
  12. package/dist/chunk-XAY6TTXB.js.map +1 -0
  13. package/dist/chunk-Z3CJQKTH.js +547 -0
  14. package/dist/chunk-Z3CJQKTH.js.map +1 -0
  15. package/dist/hooks/notification.d.ts +3 -0
  16. package/dist/hooks/notification.js +132 -0
  17. package/dist/hooks/notification.js.map +1 -0
  18. package/dist/hooks/post-tool-use.d.ts +3 -0
  19. package/dist/hooks/post-tool-use.js +96 -0
  20. package/dist/hooks/post-tool-use.js.map +1 -0
  21. package/dist/hooks/session-start.d.ts +3 -0
  22. package/dist/hooks/session-start.js +57 -0
  23. package/dist/hooks/session-start.js.map +1 -0
  24. package/dist/hooks/stop.d.ts +3 -0
  25. package/dist/hooks/stop.js +80 -0
  26. package/dist/hooks/stop.js.map +1 -0
  27. package/dist/hooks/user-prompt.d.ts +3 -0
  28. package/dist/hooks/user-prompt.js +48 -0
  29. package/dist/hooks/user-prompt.js.map +1 -0
  30. package/dist/index.d.ts +145 -0
  31. package/dist/index.js +24 -0
  32. package/dist/index.js.map +1 -0
  33. package/package.json +47 -0
  34. package/templates/CLAUDE.md.tmpl +29 -0
  35. package/templates/settings.json.tmpl +44 -0
  36. package/templates/vault/INDEX.md +19 -0
  37. package/templates/vault/architecture.md +15 -0
  38. package/templates/vault/entities/INDEX.md +10 -0
  39. 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
@@ -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":[]}