cckb 0.1.3 → 0.1.5
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 +40 -6
- package/dist/bin/cckb.js +34 -12
- package/dist/bin/cckb.js.map +1 -1
- package/dist/{chunk-NOCXYKIE.js → chunk-4IQV2TQE.js} +2 -2
- package/dist/chunk-C6NR36YP.js +276 -0
- package/dist/chunk-C6NR36YP.js.map +1 -0
- package/dist/{chunk-E2IJSPVB.js → chunk-FRETJBP5.js} +23 -1
- package/dist/chunk-FRETJBP5.js.map +1 -0
- package/dist/{chunk-XQXIDTUW.js → chunk-HP5YVMZ6.js} +2 -2
- package/dist/chunk-TB2GPCQP.js +358 -0
- package/dist/chunk-TB2GPCQP.js.map +1 -0
- package/dist/chunk-XOV27B2L.js +969 -0
- package/dist/chunk-XOV27B2L.js.map +1 -0
- package/dist/hooks/notification.js +2 -2
- package/dist/hooks/post-tool-use.js +2 -2
- package/dist/hooks/session-start.js +3 -3
- package/dist/hooks/stop.js +7 -5
- package/dist/hooks/stop.js.map +1 -1
- package/dist/hooks/user-prompt.js +2 -2
- package/dist/index.d.ts +106 -1
- package/dist/index.js +16 -6
- package/package.json +1 -1
- package/dist/chunk-3KGJEBKK.js +0 -207
- package/dist/chunk-3KGJEBKK.js.map +0 -1
- package/dist/chunk-E2IJSPVB.js.map +0 -1
- package/dist/chunk-GLYS4OA4.js +0 -602
- package/dist/chunk-GLYS4OA4.js.map +0 -1
- /package/dist/{chunk-NOCXYKIE.js.map → chunk-4IQV2TQE.js.map} +0 -0
- /package/dist/{chunk-XQXIDTUW.js.map → chunk-HP5YVMZ6.js.map} +0 -0
package/README.md
CHANGED
|
@@ -62,6 +62,9 @@ CCKB runs silently in the background, capturing conversations and building a str
|
|
|
62
62
|
```bash
|
|
63
63
|
# From your project directory
|
|
64
64
|
npx cckb init
|
|
65
|
+
|
|
66
|
+
# Install and analyze existing codebase
|
|
67
|
+
npx cckb init --discover
|
|
65
68
|
```
|
|
66
69
|
|
|
67
70
|
### Option 2: Global Install
|
|
@@ -78,6 +81,24 @@ cckb init
|
|
|
78
81
|
pnpm dlx cckb init
|
|
79
82
|
```
|
|
80
83
|
|
|
84
|
+
### Bootstrapping Existing Projects
|
|
85
|
+
|
|
86
|
+
For existing codebases, use the `discover` command to analyze and populate the vault:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# After init, or anytime
|
|
90
|
+
cckb discover
|
|
91
|
+
|
|
92
|
+
# Or during install
|
|
93
|
+
cckb init --discover
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The discover command uses Claude to analyze your codebase and automatically populate the vault with:
|
|
97
|
+
- **Entities** — Data models, types, interfaces
|
|
98
|
+
- **Architecture** — Design patterns, structural decisions
|
|
99
|
+
- **Services** — Controllers, handlers, business logic
|
|
100
|
+
- **Knowledge** — Conventions, configuration, project context
|
|
101
|
+
|
|
81
102
|
---
|
|
82
103
|
|
|
83
104
|
## What Gets Installed
|
|
@@ -109,7 +130,8 @@ Edit `cc-knowledge-base/.cckb-config.json`:
|
|
|
109
130
|
{
|
|
110
131
|
"compaction": {
|
|
111
132
|
"trigger": "session_end",
|
|
112
|
-
"sizeThresholdKB": 50
|
|
133
|
+
"sizeThresholdKB": 50,
|
|
134
|
+
"cleanupAfterSummary": "keep"
|
|
113
135
|
},
|
|
114
136
|
"capture": {
|
|
115
137
|
"tools": ["Write", "Edit", "MultiEdit", "Bash", "Task"],
|
|
@@ -122,6 +144,11 @@ Edit `cc-knowledge-base/.cckb-config.json`:
|
|
|
122
144
|
"feedback": {
|
|
123
145
|
"enabled": true,
|
|
124
146
|
"contextDepth": 2
|
|
147
|
+
},
|
|
148
|
+
"discover": {
|
|
149
|
+
"maxFiles": 100,
|
|
150
|
+
"maxChunkSize": 50000,
|
|
151
|
+
"supportedLanguages": ["typescript", "javascript", "python", "go", "rust"]
|
|
125
152
|
}
|
|
126
153
|
}
|
|
127
154
|
```
|
|
@@ -130,12 +157,16 @@ Edit `cc-knowledge-base/.cckb-config.json`:
|
|
|
130
157
|
|--------|-------------|
|
|
131
158
|
| `compaction.trigger` | When to summarize: `session_end`, `size`, `messages`, or `manual` |
|
|
132
159
|
| `compaction.sizeThresholdKB` | File size threshold for rotation |
|
|
160
|
+
| `compaction.cleanupAfterSummary` | After compaction: `keep`, `archive`, or `delete` original files |
|
|
133
161
|
| `capture.tools` | Which tool outputs to capture |
|
|
134
162
|
| `capture.maxContentLength` | Max characters per tool output |
|
|
135
163
|
| `vault.autoIntegrate` | Auto-update vault after compaction |
|
|
136
164
|
| `vault.maxDepth` | Maximum folder nesting depth |
|
|
137
165
|
| `feedback.enabled` | Inject vault context into sessions |
|
|
138
166
|
| `feedback.contextDepth` | How many INDEX levels to load |
|
|
167
|
+
| `discover.maxFiles` | Maximum files to analyze |
|
|
168
|
+
| `discover.maxChunkSize` | Characters per Claude analysis chunk |
|
|
169
|
+
| `discover.supportedLanguages` | Languages to analyze |
|
|
139
170
|
|
|
140
171
|
---
|
|
141
172
|
|
|
@@ -189,11 +220,14 @@ All hooks run silently in the background.
|
|
|
189
220
|
## Commands
|
|
190
221
|
|
|
191
222
|
```bash
|
|
192
|
-
cckb init [path]
|
|
193
|
-
cckb init --force
|
|
194
|
-
cckb
|
|
195
|
-
cckb
|
|
196
|
-
cckb
|
|
223
|
+
cckb init [path] # Install CCKB in a project
|
|
224
|
+
cckb init --force # Reinstall, overwriting existing config
|
|
225
|
+
cckb init --discover # Install and analyze existing codebase
|
|
226
|
+
cckb discover [path] # Analyze codebase and populate vault
|
|
227
|
+
cckb discover --verbose # Show detailed progress
|
|
228
|
+
cckb hook <name> # Run a hook (internal use)
|
|
229
|
+
cckb version # Show version
|
|
230
|
+
cckb help # Show help
|
|
197
231
|
```
|
|
198
232
|
|
|
199
233
|
---
|
package/dist/bin/cckb.js
CHANGED
|
@@ -1,19 +1,34 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
discover,
|
|
3
4
|
install
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
5
|
+
} from "../chunk-XOV27B2L.js";
|
|
6
|
+
import "../chunk-TB2GPCQP.js";
|
|
7
|
+
import "../chunk-4IQV2TQE.js";
|
|
8
|
+
import "../chunk-FRETJBP5.js";
|
|
6
9
|
|
|
7
10
|
// src/bin/cckb.ts
|
|
8
11
|
var args = process.argv.slice(2);
|
|
9
12
|
var command = args[0];
|
|
10
13
|
async function main() {
|
|
11
14
|
switch (command) {
|
|
12
|
-
case "init":
|
|
13
|
-
const targetPath = args
|
|
15
|
+
case "init": {
|
|
16
|
+
const targetPath = args.slice(1).find((a) => !a.startsWith("-")) || process.cwd();
|
|
14
17
|
const force = args.includes("--force");
|
|
18
|
+
const shouldDiscover = args.includes("--discover");
|
|
15
19
|
await install(targetPath, { force });
|
|
20
|
+
if (shouldDiscover) {
|
|
21
|
+
console.log("\nRunning codebase discovery...\n");
|
|
22
|
+
await discover({ targetPath, verbose: true });
|
|
23
|
+
}
|
|
16
24
|
break;
|
|
25
|
+
}
|
|
26
|
+
case "discover": {
|
|
27
|
+
const targetPath = args.slice(1).find((a) => !a.startsWith("-")) || process.cwd();
|
|
28
|
+
const verbose = args.includes("--verbose") || args.includes("-v");
|
|
29
|
+
await discover({ targetPath, verbose: verbose || true });
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
17
32
|
case "hook":
|
|
18
33
|
const hookName = args[1];
|
|
19
34
|
await runHook(hookName);
|
|
@@ -68,18 +83,25 @@ function printHelp() {
|
|
|
68
83
|
CCKB - Claude Code Knowledge Base
|
|
69
84
|
|
|
70
85
|
Usage:
|
|
71
|
-
cckb init [path]
|
|
72
|
-
cckb
|
|
73
|
-
cckb
|
|
74
|
-
cckb
|
|
86
|
+
cckb init [path] Install CCKB into a project
|
|
87
|
+
cckb init --discover Install and analyze existing codebase
|
|
88
|
+
cckb discover [path] Analyze codebase and populate vault
|
|
89
|
+
cckb hook <name> Run a specific hook (internal use)
|
|
90
|
+
cckb version Show version
|
|
91
|
+
cckb help Show this help message
|
|
75
92
|
|
|
76
93
|
Options:
|
|
77
|
-
--force
|
|
94
|
+
--force Overwrite existing installation
|
|
95
|
+
--discover Run codebase analysis after init
|
|
96
|
+
--verbose, -v Show detailed progress during discovery
|
|
78
97
|
|
|
79
98
|
Examples:
|
|
80
|
-
cckb init
|
|
81
|
-
cckb init ./myapp
|
|
82
|
-
cckb init --force
|
|
99
|
+
cckb init Install in current directory
|
|
100
|
+
cckb init ./myapp Install in ./myapp directory
|
|
101
|
+
cckb init --force Reinstall, overwriting existing config
|
|
102
|
+
cckb init --discover Install and run initial discovery
|
|
103
|
+
cckb discover Analyze current project's codebase
|
|
104
|
+
cckb discover ./myapp Analyze ./myapp codebase
|
|
83
105
|
`);
|
|
84
106
|
}
|
|
85
107
|
main().catch((error) => {
|
package/dist/bin/cckb.js.map
CHANGED
|
@@ -1 +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\"
|
|
1
|
+
{"version":3,"sources":["../../src/bin/cckb.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { install } from \"../cli/install.js\";\nimport { discover } from \"../cli/discover.js\";\n\nconst args = process.argv.slice(2);\nconst command = args[0];\n\nasync function main() {\n switch (command) {\n case \"init\": {\n // Find target path (first arg that's not a flag)\n const targetPath = args.slice(1).find((a) => !a.startsWith(\"-\")) || process.cwd();\n const force = args.includes(\"--force\");\n const shouldDiscover = args.includes(\"--discover\");\n\n await install(targetPath, { force });\n\n if (shouldDiscover) {\n console.log(\"\\nRunning codebase discovery...\\n\");\n await discover({ targetPath, verbose: true });\n }\n break;\n }\n\n case \"discover\": {\n const targetPath = args.slice(1).find((a) => !a.startsWith(\"-\")) || process.cwd();\n const verbose = args.includes(\"--verbose\") || args.includes(\"-v\");\n await discover({ targetPath, verbose: verbose || true });\n break;\n }\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 init --discover Install and analyze existing codebase\n cckb discover [path] Analyze codebase and populate vault\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 --discover Run codebase analysis after init\n --verbose, -v Show detailed progress during discovery\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 cckb init --discover Install and run initial discovery\n cckb discover Analyze current project's codebase\n cckb discover ./myapp Analyze ./myapp codebase\n`);\n}\n\nmain().catch((error) => {\n console.error(\"Error:\", error.message);\n process.exit(1);\n});\n"],"mappings":";;;;;;;;;;AAKA,IAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,IAAM,UAAU,KAAK,CAAC;AAEtB,eAAe,OAAO;AACpB,UAAQ,SAAS;AAAA,IACf,KAAK,QAAQ;AAEX,YAAM,aAAa,KAAK,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC,KAAK,QAAQ,IAAI;AAChF,YAAM,QAAQ,KAAK,SAAS,SAAS;AACrC,YAAM,iBAAiB,KAAK,SAAS,YAAY;AAEjD,YAAM,QAAQ,YAAY,EAAE,MAAM,CAAC;AAEnC,UAAI,gBAAgB;AAClB,gBAAQ,IAAI,mCAAmC;AAC/C,cAAM,SAAS,EAAE,YAAY,SAAS,KAAK,CAAC;AAAA,MAC9C;AACA;AAAA,IACF;AAAA,IAEA,KAAK,YAAY;AACf,YAAM,aAAa,KAAK,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,CAAC,KAAK,QAAQ,IAAI;AAChF,YAAM,UAAU,KAAK,SAAS,WAAW,KAAK,KAAK,SAAS,IAAI;AAChE,YAAM,SAAS,EAAE,YAAY,SAAS,WAAW,KAAK,CAAC;AACvD;AAAA,IACF;AAAA,IAEA,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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAuBb;AACD;AAEA,KAAK,EAAE,MAAM,CAAC,UAAU;AACtB,UAAQ,MAAM,UAAU,MAAM,OAAO;AACrC,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
listDir,
|
|
5
5
|
readTextFile,
|
|
6
6
|
writeTextFile
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-FRETJBP5.js";
|
|
8
8
|
|
|
9
9
|
// src/core/index-manager.ts
|
|
10
10
|
import * as path from "path";
|
|
@@ -146,4 +146,4 @@ _Last updated: ${(/* @__PURE__ */ new Date()).toISOString()}_
|
|
|
146
146
|
export {
|
|
147
147
|
IndexManager
|
|
148
148
|
};
|
|
149
|
-
//# sourceMappingURL=chunk-
|
|
149
|
+
//# sourceMappingURL=chunk-4IQV2TQE.js.map
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import {
|
|
2
|
+
spawnClaudeAgent
|
|
3
|
+
} from "./chunk-TB2GPCQP.js";
|
|
4
|
+
import {
|
|
5
|
+
getConversationsPath,
|
|
6
|
+
loadConfig,
|
|
7
|
+
readTextFile,
|
|
8
|
+
writeTextFile
|
|
9
|
+
} from "./chunk-FRETJBP5.js";
|
|
10
|
+
|
|
11
|
+
// src/core/compaction-engine.ts
|
|
12
|
+
import * as path from "path";
|
|
13
|
+
import * as fs from "fs/promises";
|
|
14
|
+
import * as zlib from "zlib";
|
|
15
|
+
import { promisify } from "util";
|
|
16
|
+
var gzip2 = promisify(zlib.gzip);
|
|
17
|
+
var SUMMARIZATION_PROMPT = `You are a technical knowledge extractor. Analyze this conversation log and extract key information for a project knowledge base.
|
|
18
|
+
|
|
19
|
+
Extract and format the following:
|
|
20
|
+
|
|
21
|
+
## Entities
|
|
22
|
+
For each domain entity (data model, type, class) created or modified:
|
|
23
|
+
- **Name**: Entity name
|
|
24
|
+
- **Location**: File path
|
|
25
|
+
- **Attributes**: Key fields/properties
|
|
26
|
+
- **Relations**: Related entities
|
|
27
|
+
|
|
28
|
+
## Architecture
|
|
29
|
+
For each architectural pattern or design decision:
|
|
30
|
+
- **Pattern**: Name of pattern
|
|
31
|
+
- **Description**: Brief explanation
|
|
32
|
+
- **Affected Files**: Relevant file paths
|
|
33
|
+
|
|
34
|
+
## Services
|
|
35
|
+
For each service or component created:
|
|
36
|
+
- **Name**: Service name
|
|
37
|
+
- **Location**: File path
|
|
38
|
+
- **Purpose**: Brief description
|
|
39
|
+
- **Methods**: Key methods/functions
|
|
40
|
+
|
|
41
|
+
## Knowledge
|
|
42
|
+
For each convention, rule, or important context:
|
|
43
|
+
- **Topic**: What it's about
|
|
44
|
+
- **Details**: The actual information
|
|
45
|
+
|
|
46
|
+
Only include sections that have content. Be concise but complete.
|
|
47
|
+
Use file paths exactly as shown in the conversation.
|
|
48
|
+
|
|
49
|
+
CONVERSATION LOG:
|
|
50
|
+
`;
|
|
51
|
+
var CompactionEngine = class {
|
|
52
|
+
projectPath;
|
|
53
|
+
constructor(projectPath) {
|
|
54
|
+
this.projectPath = projectPath;
|
|
55
|
+
}
|
|
56
|
+
async compact(sessionId, conversation) {
|
|
57
|
+
try {
|
|
58
|
+
const summaryContent = await this.generateSummary(conversation);
|
|
59
|
+
if (!summaryContent) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
const summary = this.parseSummary(sessionId, summaryContent);
|
|
63
|
+
await this.writeSummary(sessionId, summaryContent);
|
|
64
|
+
await this.cleanupConversationFiles(sessionId);
|
|
65
|
+
return summary;
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error("Compaction failed:", error);
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async generateSummary(conversation) {
|
|
72
|
+
const prompt = SUMMARIZATION_PROMPT + conversation;
|
|
73
|
+
try {
|
|
74
|
+
const result = await spawnClaudeAgent(prompt);
|
|
75
|
+
return result;
|
|
76
|
+
} catch (error) {
|
|
77
|
+
return this.fallbackExtraction(conversation);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
fallbackExtraction(conversation) {
|
|
81
|
+
const lines = conversation.split("\n");
|
|
82
|
+
const files = [];
|
|
83
|
+
const actions = [];
|
|
84
|
+
for (const line of lines) {
|
|
85
|
+
const fileMatch = line.match(/(?:Created|Modified|Edited).*?:\s*(.+\.(?:ts|js|tsx|jsx|md))/i);
|
|
86
|
+
if (fileMatch) {
|
|
87
|
+
files.push(fileMatch[1]);
|
|
88
|
+
}
|
|
89
|
+
if (line.includes("[TOOL:")) {
|
|
90
|
+
actions.push(line);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
let summary = "# Session Summary\n\n";
|
|
94
|
+
if (files.length > 0) {
|
|
95
|
+
summary += "## Files Modified\n";
|
|
96
|
+
for (const file of [...new Set(files)]) {
|
|
97
|
+
summary += `- ${file}
|
|
98
|
+
`;
|
|
99
|
+
}
|
|
100
|
+
summary += "\n";
|
|
101
|
+
}
|
|
102
|
+
if (actions.length > 0) {
|
|
103
|
+
summary += "## Actions\n";
|
|
104
|
+
for (const action of actions.slice(0, 20)) {
|
|
105
|
+
summary += `- ${action}
|
|
106
|
+
`;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return summary;
|
|
110
|
+
}
|
|
111
|
+
parseSummary(sessionId, content) {
|
|
112
|
+
const summary = {
|
|
113
|
+
sessionId,
|
|
114
|
+
content,
|
|
115
|
+
entities: [],
|
|
116
|
+
architecture: [],
|
|
117
|
+
services: [],
|
|
118
|
+
knowledge: []
|
|
119
|
+
};
|
|
120
|
+
const entitiesMatch = content.match(/## Entities\n([\s\S]*?)(?=\n## |$)/);
|
|
121
|
+
if (entitiesMatch) {
|
|
122
|
+
summary.entities = this.parseEntities(entitiesMatch[1]);
|
|
123
|
+
}
|
|
124
|
+
const archMatch = content.match(/## Architecture\n([\s\S]*?)(?=\n## |$)/);
|
|
125
|
+
if (archMatch) {
|
|
126
|
+
summary.architecture = this.parseArchitecture(archMatch[1]);
|
|
127
|
+
}
|
|
128
|
+
const servicesMatch = content.match(/## Services\n([\s\S]*?)(?=\n## |$)/);
|
|
129
|
+
if (servicesMatch) {
|
|
130
|
+
summary.services = this.parseServices(servicesMatch[1]);
|
|
131
|
+
}
|
|
132
|
+
const knowledgeMatch = content.match(/## Knowledge\n([\s\S]*?)(?=\n## |$)/);
|
|
133
|
+
if (knowledgeMatch) {
|
|
134
|
+
summary.knowledge = this.parseKnowledge(knowledgeMatch[1]);
|
|
135
|
+
}
|
|
136
|
+
return summary;
|
|
137
|
+
}
|
|
138
|
+
parseEntities(section) {
|
|
139
|
+
const entities = [];
|
|
140
|
+
const blocks = section.split(/\n(?=- \*\*Name\*\*:)/);
|
|
141
|
+
for (const block of blocks) {
|
|
142
|
+
if (!block.trim()) continue;
|
|
143
|
+
const nameMatch = block.match(/\*\*Name\*\*:\s*(.+)/);
|
|
144
|
+
const locationMatch = block.match(/\*\*Location\*\*:\s*(.+)/);
|
|
145
|
+
const attributesMatch = block.match(/\*\*Attributes\*\*:\s*(.+)/);
|
|
146
|
+
const relationsMatch = block.match(/\*\*Relations\*\*:\s*(.+)/);
|
|
147
|
+
if (nameMatch) {
|
|
148
|
+
entities.push({
|
|
149
|
+
name: nameMatch[1].trim(),
|
|
150
|
+
location: locationMatch?.[1].trim(),
|
|
151
|
+
attributes: attributesMatch?.[1].split(",").map((a) => a.trim()) || [],
|
|
152
|
+
relations: relationsMatch?.[1].split(",").map((r) => r.trim()) || []
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return entities;
|
|
157
|
+
}
|
|
158
|
+
parseArchitecture(section) {
|
|
159
|
+
const items = [];
|
|
160
|
+
const blocks = section.split(/\n(?=- \*\*Pattern\*\*:)/);
|
|
161
|
+
for (const block of blocks) {
|
|
162
|
+
if (!block.trim()) continue;
|
|
163
|
+
const patternMatch = block.match(/\*\*Pattern\*\*:\s*(.+)/);
|
|
164
|
+
const descMatch = block.match(/\*\*Description\*\*:\s*(.+)/);
|
|
165
|
+
const filesMatch = block.match(/\*\*Affected Files\*\*:\s*(.+)/);
|
|
166
|
+
if (patternMatch) {
|
|
167
|
+
items.push({
|
|
168
|
+
pattern: patternMatch[1].trim(),
|
|
169
|
+
description: descMatch?.[1].trim() || "",
|
|
170
|
+
affectedFiles: filesMatch?.[1].split(",").map((f) => f.trim()) || []
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return items;
|
|
175
|
+
}
|
|
176
|
+
parseServices(section) {
|
|
177
|
+
const items = [];
|
|
178
|
+
const blocks = section.split(/\n(?=- \*\*Name\*\*:)/);
|
|
179
|
+
for (const block of blocks) {
|
|
180
|
+
if (!block.trim()) continue;
|
|
181
|
+
const nameMatch = block.match(/\*\*Name\*\*:\s*(.+)/);
|
|
182
|
+
const locationMatch = block.match(/\*\*Location\*\*:\s*(.+)/);
|
|
183
|
+
const purposeMatch = block.match(/\*\*Purpose\*\*:\s*(.+)/);
|
|
184
|
+
const methodsMatch = block.match(/\*\*Methods\*\*:\s*(.+)/);
|
|
185
|
+
if (nameMatch) {
|
|
186
|
+
items.push({
|
|
187
|
+
name: nameMatch[1].trim(),
|
|
188
|
+
location: locationMatch?.[1].trim(),
|
|
189
|
+
purpose: purposeMatch?.[1].trim() || "",
|
|
190
|
+
methods: methodsMatch?.[1].split(",").map((m) => m.trim()) || []
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return items;
|
|
195
|
+
}
|
|
196
|
+
parseKnowledge(section) {
|
|
197
|
+
const items = [];
|
|
198
|
+
const blocks = section.split(/\n(?=- \*\*Topic\*\*:)/);
|
|
199
|
+
for (const block of blocks) {
|
|
200
|
+
if (!block.trim()) continue;
|
|
201
|
+
const topicMatch = block.match(/\*\*Topic\*\*:\s*(.+)/);
|
|
202
|
+
const detailsMatch = block.match(/\*\*Details\*\*:\s*(.+)/);
|
|
203
|
+
if (topicMatch) {
|
|
204
|
+
items.push({
|
|
205
|
+
topic: topicMatch[1].trim(),
|
|
206
|
+
details: detailsMatch?.[1].trim() || ""
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return items;
|
|
211
|
+
}
|
|
212
|
+
async writeSummary(sessionId, content) {
|
|
213
|
+
const conversationsPath = getConversationsPath(this.projectPath);
|
|
214
|
+
const summaryPath = path.join(conversationsPath, sessionId, "summary.md");
|
|
215
|
+
const fullContent = `# Session Summary: ${sessionId}
|
|
216
|
+
Generated: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
217
|
+
|
|
218
|
+
${content}
|
|
219
|
+
`;
|
|
220
|
+
await writeTextFile(summaryPath, fullContent);
|
|
221
|
+
}
|
|
222
|
+
async cleanupConversationFiles(sessionId) {
|
|
223
|
+
const config = await loadConfig(this.projectPath);
|
|
224
|
+
const cleanupMode = config.compaction.cleanupAfterSummary;
|
|
225
|
+
if (cleanupMode === "keep") {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
const conversationsPath = getConversationsPath(this.projectPath);
|
|
229
|
+
const sessionPath = path.join(conversationsPath, sessionId);
|
|
230
|
+
const files = await fs.readdir(sessionPath);
|
|
231
|
+
const conversationFiles = files.filter(
|
|
232
|
+
(f) => /^\d+\.txt$/.test(f)
|
|
233
|
+
);
|
|
234
|
+
if (conversationFiles.length === 0) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (cleanupMode === "archive") {
|
|
238
|
+
await this.archiveConversationFiles(sessionPath, conversationFiles);
|
|
239
|
+
} else if (cleanupMode === "delete") {
|
|
240
|
+
await this.deleteConversationFiles(sessionPath, conversationFiles);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
async archiveConversationFiles(sessionPath, files) {
|
|
244
|
+
try {
|
|
245
|
+
const contents = [];
|
|
246
|
+
for (const file of files.sort()) {
|
|
247
|
+
const filePath = path.join(sessionPath, file);
|
|
248
|
+
const content = await readTextFile(filePath);
|
|
249
|
+
if (content) {
|
|
250
|
+
contents.push(`=== ${file} ===
|
|
251
|
+
${content}`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
const combined = contents.join("\n\n");
|
|
255
|
+
const compressed = await gzip2(Buffer.from(combined, "utf-8"));
|
|
256
|
+
const archivePath = path.join(sessionPath, "raw.txt.gz");
|
|
257
|
+
await fs.writeFile(archivePath, compressed);
|
|
258
|
+
await this.deleteConversationFiles(sessionPath, files);
|
|
259
|
+
} catch (error) {
|
|
260
|
+
console.error("Failed to archive conversation files:", error);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
async deleteConversationFiles(sessionPath, files) {
|
|
264
|
+
for (const file of files) {
|
|
265
|
+
try {
|
|
266
|
+
await fs.unlink(path.join(sessionPath, file));
|
|
267
|
+
} catch {
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
export {
|
|
274
|
+
CompactionEngine
|
|
275
|
+
};
|
|
276
|
+
//# sourceMappingURL=chunk-C6NR36YP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/compaction-engine.ts"],"sourcesContent":["import * as path from \"node:path\";\nimport * as fs from \"node:fs/promises\";\nimport * as zlib from \"node:zlib\";\nimport { promisify } from \"node:util\";\nimport { writeTextFile, readTextFile } from \"../utils/file-utils.js\";\nimport { getConversationsPath, loadConfig } from \"../utils/config.js\";\nimport { spawnClaudeAgent } from \"../utils/claude-sdk.js\";\n\nconst gzip = promisify(zlib.gzip);\n\nconst SUMMARIZATION_PROMPT = `You are a technical knowledge extractor. Analyze this conversation log and extract key information for a project knowledge base.\n\nExtract and format the following:\n\n## Entities\nFor each domain entity (data model, type, class) created or modified:\n- **Name**: Entity name\n- **Location**: File path\n- **Attributes**: Key fields/properties\n- **Relations**: Related entities\n\n## Architecture\nFor each architectural pattern or design decision:\n- **Pattern**: Name of pattern\n- **Description**: Brief explanation\n- **Affected Files**: Relevant file paths\n\n## Services\nFor each service or component created:\n- **Name**: Service name\n- **Location**: File path\n- **Purpose**: Brief description\n- **Methods**: Key methods/functions\n\n## Knowledge\nFor each convention, rule, or important context:\n- **Topic**: What it's about\n- **Details**: The actual information\n\nOnly include sections that have content. Be concise but complete.\nUse file paths exactly as shown in the conversation.\n\nCONVERSATION LOG:\n`;\n\nexport interface Summary {\n sessionId: string;\n content: string;\n entities: ExtractedEntity[];\n architecture: ArchitectureItem[];\n services: ServiceItem[];\n knowledge: KnowledgeItem[];\n}\n\nexport interface ExtractedEntity {\n name: string;\n location?: string;\n attributes: string[];\n relations: string[];\n}\n\nexport interface ArchitectureItem {\n pattern: string;\n description: string;\n affectedFiles: string[];\n}\n\nexport interface ServiceItem {\n name: string;\n location?: string;\n purpose: string;\n methods: string[];\n}\n\nexport interface KnowledgeItem {\n topic: string;\n details: string;\n}\n\nexport class CompactionEngine {\n private projectPath: string;\n\n constructor(projectPath: string) {\n this.projectPath = projectPath;\n }\n\n async compact(sessionId: string, conversation: string): Promise<Summary | null> {\n try {\n // Use Claude SDK to generate summary\n const summaryContent = await this.generateSummary(conversation);\n\n if (!summaryContent) {\n return null;\n }\n\n // Parse the summary to extract structured data\n const summary = this.parseSummary(sessionId, summaryContent);\n\n // Write summary to conversation folder\n await this.writeSummary(sessionId, summaryContent);\n\n // Cleanup original conversation files based on config\n await this.cleanupConversationFiles(sessionId);\n\n return summary;\n } catch (error) {\n console.error(\"Compaction failed:\", error);\n return null;\n }\n }\n\n private async generateSummary(conversation: string): Promise<string | null> {\n const prompt = SUMMARIZATION_PROMPT + conversation;\n\n try {\n const result = await spawnClaudeAgent(prompt);\n return result;\n } catch (error) {\n // Fallback to basic extraction if Claude SDK fails\n return this.fallbackExtraction(conversation);\n }\n }\n\n private fallbackExtraction(conversation: string): string {\n // Basic extraction without AI - just capture file paths and actions\n const lines = conversation.split(\"\\n\");\n const files: string[] = [];\n const actions: string[] = [];\n\n for (const line of lines) {\n // Extract file paths\n const fileMatch = line.match(/(?:Created|Modified|Edited).*?:\\s*(.+\\.(?:ts|js|tsx|jsx|md))/i);\n if (fileMatch) {\n files.push(fileMatch[1]);\n }\n\n // Extract tool actions\n if (line.includes(\"[TOOL:\")) {\n actions.push(line);\n }\n }\n\n let summary = \"# Session Summary\\n\\n\";\n\n if (files.length > 0) {\n summary += \"## Files Modified\\n\";\n for (const file of [...new Set(files)]) {\n summary += `- ${file}\\n`;\n }\n summary += \"\\n\";\n }\n\n if (actions.length > 0) {\n summary += \"## Actions\\n\";\n for (const action of actions.slice(0, 20)) {\n summary += `- ${action}\\n`;\n }\n }\n\n return summary;\n }\n\n private parseSummary(sessionId: string, content: string): Summary {\n const summary: Summary = {\n sessionId,\n content,\n entities: [],\n architecture: [],\n services: [],\n knowledge: [],\n };\n\n // Parse entities section\n const entitiesMatch = content.match(/## Entities\\n([\\s\\S]*?)(?=\\n## |$)/);\n if (entitiesMatch) {\n summary.entities = this.parseEntities(entitiesMatch[1]);\n }\n\n // Parse architecture section\n const archMatch = content.match(/## Architecture\\n([\\s\\S]*?)(?=\\n## |$)/);\n if (archMatch) {\n summary.architecture = this.parseArchitecture(archMatch[1]);\n }\n\n // Parse services section\n const servicesMatch = content.match(/## Services\\n([\\s\\S]*?)(?=\\n## |$)/);\n if (servicesMatch) {\n summary.services = this.parseServices(servicesMatch[1]);\n }\n\n // Parse knowledge section\n const knowledgeMatch = content.match(/## Knowledge\\n([\\s\\S]*?)(?=\\n## |$)/);\n if (knowledgeMatch) {\n summary.knowledge = this.parseKnowledge(knowledgeMatch[1]);\n }\n\n return summary;\n }\n\n private parseEntities(section: string): ExtractedEntity[] {\n const entities: ExtractedEntity[] = [];\n const blocks = section.split(/\\n(?=- \\*\\*Name\\*\\*:)/);\n\n for (const block of blocks) {\n if (!block.trim()) continue;\n\n const nameMatch = block.match(/\\*\\*Name\\*\\*:\\s*(.+)/);\n const locationMatch = block.match(/\\*\\*Location\\*\\*:\\s*(.+)/);\n const attributesMatch = block.match(/\\*\\*Attributes\\*\\*:\\s*(.+)/);\n const relationsMatch = block.match(/\\*\\*Relations\\*\\*:\\s*(.+)/);\n\n if (nameMatch) {\n entities.push({\n name: nameMatch[1].trim(),\n location: locationMatch?.[1].trim(),\n attributes: attributesMatch?.[1].split(\",\").map((a) => a.trim()) || [],\n relations: relationsMatch?.[1].split(\",\").map((r) => r.trim()) || [],\n });\n }\n }\n\n return entities;\n }\n\n private parseArchitecture(section: string): ArchitectureItem[] {\n const items: ArchitectureItem[] = [];\n const blocks = section.split(/\\n(?=- \\*\\*Pattern\\*\\*:)/);\n\n for (const block of blocks) {\n if (!block.trim()) continue;\n\n const patternMatch = block.match(/\\*\\*Pattern\\*\\*:\\s*(.+)/);\n const descMatch = block.match(/\\*\\*Description\\*\\*:\\s*(.+)/);\n const filesMatch = block.match(/\\*\\*Affected Files\\*\\*:\\s*(.+)/);\n\n if (patternMatch) {\n items.push({\n pattern: patternMatch[1].trim(),\n description: descMatch?.[1].trim() || \"\",\n affectedFiles: filesMatch?.[1].split(\",\").map((f) => f.trim()) || [],\n });\n }\n }\n\n return items;\n }\n\n private parseServices(section: string): ServiceItem[] {\n const items: ServiceItem[] = [];\n const blocks = section.split(/\\n(?=- \\*\\*Name\\*\\*:)/);\n\n for (const block of blocks) {\n if (!block.trim()) continue;\n\n const nameMatch = block.match(/\\*\\*Name\\*\\*:\\s*(.+)/);\n const locationMatch = block.match(/\\*\\*Location\\*\\*:\\s*(.+)/);\n const purposeMatch = block.match(/\\*\\*Purpose\\*\\*:\\s*(.+)/);\n const methodsMatch = block.match(/\\*\\*Methods\\*\\*:\\s*(.+)/);\n\n if (nameMatch) {\n items.push({\n name: nameMatch[1].trim(),\n location: locationMatch?.[1].trim(),\n purpose: purposeMatch?.[1].trim() || \"\",\n methods: methodsMatch?.[1].split(\",\").map((m) => m.trim()) || [],\n });\n }\n }\n\n return items;\n }\n\n private parseKnowledge(section: string): KnowledgeItem[] {\n const items: KnowledgeItem[] = [];\n const blocks = section.split(/\\n(?=- \\*\\*Topic\\*\\*:)/);\n\n for (const block of blocks) {\n if (!block.trim()) continue;\n\n const topicMatch = block.match(/\\*\\*Topic\\*\\*:\\s*(.+)/);\n const detailsMatch = block.match(/\\*\\*Details\\*\\*:\\s*(.+)/);\n\n if (topicMatch) {\n items.push({\n topic: topicMatch[1].trim(),\n details: detailsMatch?.[1].trim() || \"\",\n });\n }\n }\n\n return items;\n }\n\n private async writeSummary(sessionId: string, content: string): Promise<void> {\n const conversationsPath = getConversationsPath(this.projectPath);\n const summaryPath = path.join(conversationsPath, sessionId, \"summary.md\");\n\n const fullContent = `# Session Summary: ${sessionId}\nGenerated: ${new Date().toISOString()}\n\n${content}\n`;\n\n await writeTextFile(summaryPath, fullContent);\n }\n\n private async cleanupConversationFiles(sessionId: string): Promise<void> {\n const config = await loadConfig(this.projectPath);\n const cleanupMode = config.compaction.cleanupAfterSummary;\n\n if (cleanupMode === \"keep\") {\n return; // Nothing to do\n }\n\n const conversationsPath = getConversationsPath(this.projectPath);\n const sessionPath = path.join(conversationsPath, sessionId);\n\n // Get all conversation files (numbered .txt files)\n const files = await fs.readdir(sessionPath);\n const conversationFiles = files.filter(\n (f) => /^\\d+\\.txt$/.test(f)\n );\n\n if (conversationFiles.length === 0) {\n return;\n }\n\n if (cleanupMode === \"archive\") {\n await this.archiveConversationFiles(sessionPath, conversationFiles);\n } else if (cleanupMode === \"delete\") {\n await this.deleteConversationFiles(sessionPath, conversationFiles);\n }\n }\n\n private async archiveConversationFiles(\n sessionPath: string,\n files: string[]\n ): Promise<void> {\n try {\n // Combine all conversation files into one archive\n const contents: string[] = [];\n\n for (const file of files.sort()) {\n const filePath = path.join(sessionPath, file);\n const content = await readTextFile(filePath);\n if (content) {\n contents.push(`=== ${file} ===\\n${content}`);\n }\n }\n\n const combined = contents.join(\"\\n\\n\");\n const compressed = await gzip(Buffer.from(combined, \"utf-8\"));\n\n // Write archive\n const archivePath = path.join(sessionPath, \"raw.txt.gz\");\n await fs.writeFile(archivePath, compressed);\n\n // Delete original files\n await this.deleteConversationFiles(sessionPath, files);\n } catch (error) {\n console.error(\"Failed to archive conversation files:\", error);\n // Don't delete originals if archive fails\n }\n }\n\n private async deleteConversationFiles(\n sessionPath: string,\n files: string[]\n ): Promise<void> {\n for (const file of files) {\n try {\n await fs.unlink(path.join(sessionPath, file));\n } catch {\n // Ignore deletion errors\n }\n }\n }\n}\n"],"mappings":"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","names":["gzip"]}
|
|
@@ -85,6 +85,28 @@ var DEFAULT_CONFIG = {
|
|
|
85
85
|
feedback: {
|
|
86
86
|
enabled: true,
|
|
87
87
|
contextDepth: 2
|
|
88
|
+
},
|
|
89
|
+
discover: {
|
|
90
|
+
maxFiles: 100,
|
|
91
|
+
maxChunkSize: 5e4,
|
|
92
|
+
excludePatterns: [
|
|
93
|
+
"*.min.js",
|
|
94
|
+
"*.bundle.js",
|
|
95
|
+
"*.map",
|
|
96
|
+
"coverage/**",
|
|
97
|
+
".next/**",
|
|
98
|
+
"build/**",
|
|
99
|
+
"dist/**"
|
|
100
|
+
],
|
|
101
|
+
priorityPatterns: [
|
|
102
|
+
"**/index.{ts,js,tsx,jsx}",
|
|
103
|
+
"**/main.{ts,js,py,go,rs}",
|
|
104
|
+
"**/app.{ts,js,py}",
|
|
105
|
+
"**/models/**",
|
|
106
|
+
"**/entities/**",
|
|
107
|
+
"**/services/**"
|
|
108
|
+
],
|
|
109
|
+
supportedLanguages: ["typescript", "javascript", "python", "go", "rust"]
|
|
88
110
|
}
|
|
89
111
|
};
|
|
90
112
|
async function loadConfig(projectPath) {
|
|
@@ -129,4 +151,4 @@ export {
|
|
|
129
151
|
getVaultPath,
|
|
130
152
|
getStatePath
|
|
131
153
|
};
|
|
132
|
-
//# sourceMappingURL=chunk-
|
|
154
|
+
//# sourceMappingURL=chunk-FRETJBP5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/file-utils.ts","../src/utils/config.ts"],"sourcesContent":["import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nexport async function ensureDir(dirPath: string): Promise<void> {\n await fs.mkdir(dirPath, { recursive: true });\n}\n\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function readJSON<T>(filePath: string): Promise<T | null> {\n try {\n const content = await fs.readFile(filePath, \"utf-8\");\n return JSON.parse(content) as T;\n } catch {\n return null;\n }\n}\n\nexport async function writeJSON(\n filePath: string,\n data: unknown,\n pretty = true\n): Promise<void> {\n await ensureDir(path.dirname(filePath));\n const content = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);\n await fs.writeFile(filePath, content);\n}\n\nexport async function appendToFile(\n filePath: string,\n content: string\n): Promise<void> {\n await ensureDir(path.dirname(filePath));\n await fs.appendFile(filePath, content);\n}\n\nexport async function readTextFile(filePath: string): Promise<string | null> {\n try {\n return await fs.readFile(filePath, \"utf-8\");\n } catch {\n return null;\n }\n}\n\nexport async function writeTextFile(\n filePath: string,\n content: string\n): Promise<void> {\n await ensureDir(path.dirname(filePath));\n await fs.writeFile(filePath, content);\n}\n\nexport async function copyFile(src: string, dest: string): Promise<void> {\n await ensureDir(path.dirname(dest));\n await fs.copyFile(src, dest);\n}\n\nexport async function listDir(dirPath: string): Promise<string[]> {\n try {\n return await fs.readdir(dirPath);\n } catch {\n return [];\n }\n}\n\nexport async function getFileSize(filePath: string): Promise<number> {\n try {\n const stats = await fs.stat(filePath);\n return stats.size;\n } catch {\n return 0;\n }\n}\n\nexport function generateSessionId(): string {\n const timestamp = Date.now().toString(36);\n const random = Math.random().toString(36).substring(2, 8);\n return `${timestamp}-${random}`;\n}\n\nexport function getCurrentTimestamp(): string {\n return new Date().toISOString();\n}\n","import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nexport interface CCKBConfig {\n compaction: {\n trigger: \"session_end\" | \"size\" | \"messages\" | \"manual\";\n sizeThresholdKB: number;\n messageThreshold: number;\n cleanupAfterSummary: \"keep\" | \"archive\" | \"delete\";\n };\n capture: {\n tools: string[];\n maxContentLength: number;\n };\n vault: {\n autoIntegrate: boolean;\n maxDepth: number;\n };\n feedback: {\n enabled: boolean;\n contextDepth: number;\n };\n discover: {\n maxFiles: number;\n maxChunkSize: number;\n excludePatterns: string[];\n priorityPatterns: string[];\n supportedLanguages: string[];\n };\n}\n\nexport const DEFAULT_CONFIG: CCKBConfig = {\n compaction: {\n trigger: \"session_end\",\n sizeThresholdKB: 50,\n messageThreshold: 100,\n cleanupAfterSummary: \"keep\",\n },\n capture: {\n tools: [\"Write\", \"Edit\", \"MultiEdit\", \"Bash\", \"Task\"],\n maxContentLength: 500,\n },\n vault: {\n autoIntegrate: true,\n maxDepth: 5,\n },\n feedback: {\n enabled: true,\n contextDepth: 2,\n },\n discover: {\n maxFiles: 100,\n maxChunkSize: 50000,\n excludePatterns: [\n \"*.min.js\",\n \"*.bundle.js\",\n \"*.map\",\n \"coverage/**\",\n \".next/**\",\n \"build/**\",\n \"dist/**\",\n ],\n priorityPatterns: [\n \"**/index.{ts,js,tsx,jsx}\",\n \"**/main.{ts,js,py,go,rs}\",\n \"**/app.{ts,js,py}\",\n \"**/models/**\",\n \"**/entities/**\",\n \"**/services/**\",\n ],\n supportedLanguages: [\"typescript\", \"javascript\", \"python\", \"go\", \"rust\"],\n },\n};\n\nexport async function loadConfig(projectPath: string): Promise<CCKBConfig> {\n const configPath = path.join(\n projectPath,\n \"cc-knowledge-base\",\n \".cckb-config.json\"\n );\n\n try {\n const content = await fs.readFile(configPath, \"utf-8\");\n const userConfig = JSON.parse(content);\n return { ...DEFAULT_CONFIG, ...userConfig };\n } catch {\n return DEFAULT_CONFIG;\n }\n}\n\nexport async function saveConfig(\n projectPath: string,\n config: CCKBConfig\n): Promise<void> {\n const configPath = path.join(\n projectPath,\n \"cc-knowledge-base\",\n \".cckb-config.json\"\n );\n\n await fs.writeFile(configPath, JSON.stringify(config, null, 2));\n}\n\nexport function getKnowledgeBasePath(projectPath: string): string {\n return path.join(projectPath, \"cc-knowledge-base\");\n}\n\nexport function getConversationsPath(projectPath: string): string {\n return path.join(projectPath, \"cc-knowledge-base\", \"conversations\");\n}\n\nexport function getVaultPath(projectPath: string): string {\n return path.join(projectPath, \"cc-knowledge-base\", \"vault\");\n}\n\nexport function getStatePath(projectPath: string): string {\n return path.join(projectPath, \"cc-knowledge-base\", \".cckb-state\");\n}\n"],"mappings":"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","names":["fs","path"]}
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
readTextFile,
|
|
14
14
|
writeJSON,
|
|
15
15
|
writeTextFile
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-FRETJBP5.js";
|
|
17
17
|
|
|
18
18
|
// src/core/conversation-manager.ts
|
|
19
19
|
import * as path from "path";
|
|
@@ -236,4 +236,4 @@ ${entry.content}
|
|
|
236
236
|
export {
|
|
237
237
|
ConversationManager
|
|
238
238
|
};
|
|
239
|
-
//# sourceMappingURL=chunk-
|
|
239
|
+
//# sourceMappingURL=chunk-HP5YVMZ6.js.map
|