knowledge-mcp-server 1.0.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/CHANGELOG.md +11 -0
- package/LICENSE +21 -0
- package/README.md +205 -0
- package/dist/analytics.d.ts +24 -0
- package/dist/analytics.js +102 -0
- package/dist/analytics.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +98 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +39 -0
- package/dist/config.js +80 -0
- package/dist/config.js.map +1 -0
- package/dist/embeddings.d.ts +26 -0
- package/dist/embeddings.js +534 -0
- package/dist/embeddings.js.map +1 -0
- package/dist/formatter.d.ts +51 -0
- package/dist/formatter.js +273 -0
- package/dist/formatter.js.map +1 -0
- package/dist/generate-embeddings.d.ts +8 -0
- package/dist/generate-embeddings.js +146 -0
- package/dist/generate-embeddings.js.map +1 -0
- package/dist/graph.d.ts +20 -0
- package/dist/graph.js +133 -0
- package/dist/graph.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +481 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +1 -0
- package/dist/init.js +65 -0
- package/dist/init.js.map +1 -0
- package/dist/loader.d.ts +26 -0
- package/dist/loader.js +151 -0
- package/dist/loader.js.map +1 -0
- package/dist/logger.d.ts +6 -0
- package/dist/logger.js +15 -0
- package/dist/logger.js.map +1 -0
- package/dist/query-classifier.d.ts +23 -0
- package/dist/query-classifier.js +111 -0
- package/dist/query-classifier.js.map +1 -0
- package/dist/reranker.d.ts +7 -0
- package/dist/reranker.js +38 -0
- package/dist/reranker.js.map +1 -0
- package/dist/search.d.ts +17 -0
- package/dist/search.js +299 -0
- package/dist/search.js.map +1 -0
- package/dist/validator.d.ts +23 -0
- package/dist/validator.js +97 -0
- package/dist/validator.js.map +1 -0
- package/dist/writer.d.ts +36 -0
- package/dist/writer.js +360 -0
- package/dist/writer.js.map +1 -0
- package/package.json +58 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
Initial release. Extracted from guitar-app project.
|
|
6
|
+
|
|
7
|
+
- MCP server with 8 tools: search, lookup, graph, list, write, delete, validate, stats
|
|
8
|
+
- BM25 full-text search with optional Voyage AI vector embeddings (hybrid RRF fusion)
|
|
9
|
+
- CLI with subcommands: serve, embeddings, init, validate
|
|
10
|
+
- Zero-config mode (works without knowledge.config.yaml)
|
|
11
|
+
- Programmatic API via `createKnowledgeServer()`
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ross Sheridan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# knowledge-mcp-server
|
|
2
|
+
|
|
3
|
+
MCP server for semantic search, CRUD, and graph operations over hierarchical knowledge bases stored as Markdown with YAML frontmatter.
|
|
4
|
+
|
|
5
|
+
Provides 8 tools via [Model Context Protocol](https://modelcontextprotocol.io/): `knowledge_search`, `knowledge_lookup`, `knowledge_graph`, `knowledge_list`, `knowledge_write`, `knowledge_delete`, `knowledge_validate`, and `knowledge_stats`.
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Initialize a new knowledge directory
|
|
11
|
+
npx knowledge-mcp-server init
|
|
12
|
+
|
|
13
|
+
# Start the MCP server
|
|
14
|
+
npx knowledge-mcp-server
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install knowledge-mcp-server
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Claude Code Integration
|
|
24
|
+
|
|
25
|
+
Add to your `.mcp.json`:
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"mcpServers": {
|
|
30
|
+
"knowledge": {
|
|
31
|
+
"type": "stdio",
|
|
32
|
+
"command": "npx",
|
|
33
|
+
"args": ["knowledge-mcp-server", "--knowledge-dir", "./knowledge"],
|
|
34
|
+
"env": {
|
|
35
|
+
"VOYAGE_API_KEY": "${VOYAGE_API_KEY}"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The `VOYAGE_API_KEY` is optional — without it, the server uses BM25 full-text search only. With it, search results are improved via hybrid BM25 + vector scoring with Reciprocal Rank Fusion.
|
|
43
|
+
|
|
44
|
+
## CLI Reference
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
knowledge-mcp-server [command] [options]
|
|
48
|
+
|
|
49
|
+
Commands:
|
|
50
|
+
serve Start the MCP server over stdio (default)
|
|
51
|
+
embeddings Generate embeddings for all documents via Voyage AI
|
|
52
|
+
init Scaffold a new knowledge/ directory with config template
|
|
53
|
+
validate Run graph integrity checks and report issues
|
|
54
|
+
|
|
55
|
+
Options:
|
|
56
|
+
--knowledge-dir <path> Path to knowledge directory (default: ./knowledge)
|
|
57
|
+
--help, -h Show help
|
|
58
|
+
--version Show version
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Generate Embeddings
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
VOYAGE_API_KEY=your-key npx knowledge-mcp-server embeddings
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Uses incremental hashing — only re-embeds documents whose content has changed.
|
|
68
|
+
|
|
69
|
+
### Validate Graph
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npx knowledge-mcp-server validate
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Checks for: orphaned documents, broken references, circular parents, missing tags, empty summaries, stale documents (>6 months), and embedding coverage. Exits with code 1 if integrity issues are found.
|
|
76
|
+
|
|
77
|
+
## Programmatic API
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
import { createKnowledgeServer } from "knowledge-mcp-server";
|
|
81
|
+
|
|
82
|
+
const { server, graph, tfidfIndex, config } = createKnowledgeServer("./knowledge");
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Exported Types
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import type {
|
|
89
|
+
KnowledgeServerResult,
|
|
90
|
+
KnowledgeGraph,
|
|
91
|
+
KnowledgeDocument,
|
|
92
|
+
KnowledgeConfig,
|
|
93
|
+
} from "knowledge-mcp-server";
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Document Format
|
|
97
|
+
|
|
98
|
+
Knowledge documents are Markdown files with YAML frontmatter:
|
|
99
|
+
|
|
100
|
+
```markdown
|
|
101
|
+
---
|
|
102
|
+
id: technology/audio-detection/pitch-detection
|
|
103
|
+
title: Pitch Detection Pipeline
|
|
104
|
+
type: detail
|
|
105
|
+
domain: technology
|
|
106
|
+
subdomain: audio-detection
|
|
107
|
+
tags: [audio, ml, crepe, yin]
|
|
108
|
+
phase: [1]
|
|
109
|
+
related: [technology/audio-detection/chord-recognition]
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
Your document content here in Markdown.
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Frontmatter Fields
|
|
116
|
+
|
|
117
|
+
| Field | Required | Description |
|
|
118
|
+
|-------|----------|-------------|
|
|
119
|
+
| `id` | yes | Unique document ID (lowercase, hyphens, slashes) |
|
|
120
|
+
| `title` | yes | Human-readable title |
|
|
121
|
+
| `type` | yes | `summary`, `detail`, `decision`, or `reference` |
|
|
122
|
+
| `domain` | yes | Top-level domain |
|
|
123
|
+
| `subdomain` | no | Subdomain within the domain |
|
|
124
|
+
| `tags` | yes | Array of searchable tags |
|
|
125
|
+
| `phase` | yes | Array of applicable phase numbers |
|
|
126
|
+
| `related` | no | Array of related document IDs |
|
|
127
|
+
| `status` | no | `active` (default), `draft`, or `deprecated` |
|
|
128
|
+
|
|
129
|
+
### Directory Structure
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
knowledge/
|
|
133
|
+
├── knowledge.config.yaml # Configuration (optional)
|
|
134
|
+
├── _summary.md # Root node
|
|
135
|
+
├── .embeddings.json # Generated embeddings (optional)
|
|
136
|
+
├── .embeddings-hashes.json # Content hashes for change detection
|
|
137
|
+
├── .tags.json # Tag taxonomy (optional)
|
|
138
|
+
├── technology/
|
|
139
|
+
│ ├── _summary.md
|
|
140
|
+
│ └── audio-detection/
|
|
141
|
+
│ ├── _summary.md
|
|
142
|
+
│ └── pitch-detection.md
|
|
143
|
+
└── business/
|
|
144
|
+
├── _summary.md
|
|
145
|
+
└── pricing-tiers.md
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Configuration
|
|
149
|
+
|
|
150
|
+
`knowledge.config.yaml` is optional. Without it, the server runs in zero-config mode (permissive validation, auto-discovered domains).
|
|
151
|
+
|
|
152
|
+
```yaml
|
|
153
|
+
name: "my-project"
|
|
154
|
+
|
|
155
|
+
# Strict domain validation (only these domains are accepted)
|
|
156
|
+
domains:
|
|
157
|
+
- technology
|
|
158
|
+
- architecture
|
|
159
|
+
- business
|
|
160
|
+
|
|
161
|
+
# Phase definitions with optional aliases
|
|
162
|
+
phases:
|
|
163
|
+
- id: 1
|
|
164
|
+
name: "Foundation"
|
|
165
|
+
aliases: ["launch", "mvp"]
|
|
166
|
+
- id: 2
|
|
167
|
+
name: "Growth"
|
|
168
|
+
|
|
169
|
+
# Query hints for domain classification
|
|
170
|
+
query_hints:
|
|
171
|
+
technology: ["api", "database", "framework", "library"]
|
|
172
|
+
business: ["pricing", "revenue", "market"]
|
|
173
|
+
|
|
174
|
+
# Synonym expansion for search
|
|
175
|
+
synonyms:
|
|
176
|
+
ml: ["machine learning"]
|
|
177
|
+
ai: ["artificial intelligence"]
|
|
178
|
+
dkt: ["deep knowledge tracing"]
|
|
179
|
+
|
|
180
|
+
# Embedding configuration
|
|
181
|
+
embeddings:
|
|
182
|
+
provider: "voyage"
|
|
183
|
+
model: "voyage-3-lite"
|
|
184
|
+
api_key_env: "VOYAGE_API_KEY"
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Search Architecture
|
|
188
|
+
|
|
189
|
+
The search pipeline uses a 4-stage hybrid approach:
|
|
190
|
+
|
|
191
|
+
1. **Query Classification** — extracts domains, phases, and query type from natural language
|
|
192
|
+
2. **Metadata Pre-filter** — O(1) lookups via in-memory indices (domain, phase, tag, type)
|
|
193
|
+
3. **Hybrid Scoring** — BM25 full-text search (k1=1.2, b=0.75, title 3x boost) + optional Voyage AI vector embeddings, merged via Reciprocal Rank Fusion (RRF, k=60)
|
|
194
|
+
4. **Hierarchical Expansion** — includes ancestor documents and cross-references within a word budget
|
|
195
|
+
|
|
196
|
+
## Environment Variables
|
|
197
|
+
|
|
198
|
+
| Variable | Required | Description |
|
|
199
|
+
|----------|----------|-------------|
|
|
200
|
+
| `VOYAGE_API_KEY` | No | Voyage AI API key for semantic embeddings |
|
|
201
|
+
| `LOG_LEVEL` | No | Logging verbosity: `debug`, `info` (default), `warn`, `error` |
|
|
202
|
+
|
|
203
|
+
## License
|
|
204
|
+
|
|
205
|
+
MIT
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { KnowledgeGraph } from "./graph.js";
|
|
2
|
+
export interface GraphStats {
|
|
3
|
+
totalDocs: number;
|
|
4
|
+
byType: Map<string, number>;
|
|
5
|
+
byDomain: Map<string, number>;
|
|
6
|
+
byPhase: Map<string, number>;
|
|
7
|
+
topTags: Array<{
|
|
8
|
+
tag: string;
|
|
9
|
+
count: number;
|
|
10
|
+
}>;
|
|
11
|
+
crossLinkDensity: number;
|
|
12
|
+
mostConnected: Array<{
|
|
13
|
+
id: string;
|
|
14
|
+
connections: number;
|
|
15
|
+
}>;
|
|
16
|
+
orphanCount: number;
|
|
17
|
+
embeddingCoverage: {
|
|
18
|
+
total: number;
|
|
19
|
+
covered: number;
|
|
20
|
+
percent: number;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export declare function computeStats(graph: KnowledgeGraph): GraphStats;
|
|
24
|
+
export declare function formatStats(stats: GraphStats): string;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
export function computeStats(graph) {
|
|
2
|
+
const byType = new Map();
|
|
3
|
+
const byDomain = new Map();
|
|
4
|
+
const byPhase = new Map();
|
|
5
|
+
const tagCounts = new Map();
|
|
6
|
+
const connectionCounts = new Map();
|
|
7
|
+
let totalRelated = 0;
|
|
8
|
+
let orphanCount = 0;
|
|
9
|
+
for (const doc of graph.documents.values()) {
|
|
10
|
+
// By type
|
|
11
|
+
byType.set(doc.type, (byType.get(doc.type) || 0) + 1);
|
|
12
|
+
// By domain
|
|
13
|
+
byDomain.set(doc.domain, (byDomain.get(doc.domain) || 0) + 1);
|
|
14
|
+
// By phase
|
|
15
|
+
for (const p of doc.phase) {
|
|
16
|
+
const key = `phase-${p}`;
|
|
17
|
+
byPhase.set(key, (byPhase.get(key) || 0) + 1);
|
|
18
|
+
}
|
|
19
|
+
// Tag counts
|
|
20
|
+
for (const tag of doc.tags) {
|
|
21
|
+
const lower = tag.toLowerCase();
|
|
22
|
+
tagCounts.set(lower, (tagCounts.get(lower) || 0) + 1);
|
|
23
|
+
}
|
|
24
|
+
// Connection counts (forward related + backlinks)
|
|
25
|
+
const forwardCount = doc.related.length;
|
|
26
|
+
const backlinks = graph.backlinkIndex.get(doc.id);
|
|
27
|
+
const backlinkCount = backlinks ? backlinks.size : 0;
|
|
28
|
+
const total = forwardCount + backlinkCount;
|
|
29
|
+
connectionCounts.set(doc.id, total);
|
|
30
|
+
totalRelated += forwardCount;
|
|
31
|
+
// Orphan: no related links in or out
|
|
32
|
+
if (forwardCount === 0 && backlinkCount === 0) {
|
|
33
|
+
orphanCount++;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Top 20 tags by frequency
|
|
37
|
+
const topTags = [...tagCounts.entries()]
|
|
38
|
+
.sort((a, b) => b[1] - a[1])
|
|
39
|
+
.slice(0, 20)
|
|
40
|
+
.map(([tag, count]) => ({ tag, count }));
|
|
41
|
+
// Most connected docs (top 5)
|
|
42
|
+
const mostConnected = [...connectionCounts.entries()]
|
|
43
|
+
.sort((a, b) => b[1] - a[1])
|
|
44
|
+
.slice(0, 5)
|
|
45
|
+
.map(([id, connections]) => ({ id, connections }));
|
|
46
|
+
// Cross-link density
|
|
47
|
+
const totalDocs = graph.documents.size;
|
|
48
|
+
const crossLinkDensity = totalDocs > 0 ? Math.round((totalRelated / totalDocs) * 100) / 100 : 0;
|
|
49
|
+
// Embedding coverage
|
|
50
|
+
const covered = [...graph.documents.keys()].filter((id) => graph.embeddings.vectors.has(id)).length;
|
|
51
|
+
return {
|
|
52
|
+
totalDocs,
|
|
53
|
+
byType,
|
|
54
|
+
byDomain,
|
|
55
|
+
byPhase,
|
|
56
|
+
topTags,
|
|
57
|
+
crossLinkDensity,
|
|
58
|
+
mostConnected,
|
|
59
|
+
orphanCount,
|
|
60
|
+
embeddingCoverage: {
|
|
61
|
+
total: totalDocs,
|
|
62
|
+
covered,
|
|
63
|
+
percent: totalDocs > 0 ? Math.round((covered / totalDocs) * 100) : 0,
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export function formatStats(stats) {
|
|
68
|
+
const lines = ["Knowledge Graph Statistics", "=".repeat(26), ""];
|
|
69
|
+
lines.push(`Total documents: ${stats.totalDocs}`);
|
|
70
|
+
lines.push("");
|
|
71
|
+
lines.push("By type:");
|
|
72
|
+
for (const [type, count] of [...stats.byType].sort((a, b) => b[1] - a[1])) {
|
|
73
|
+
lines.push(` ${type}: ${count}`);
|
|
74
|
+
}
|
|
75
|
+
lines.push("");
|
|
76
|
+
lines.push("By domain:");
|
|
77
|
+
for (const [domain, count] of [...stats.byDomain].sort((a, b) => b[1] - a[1])) {
|
|
78
|
+
lines.push(` ${domain}: ${count}`);
|
|
79
|
+
}
|
|
80
|
+
lines.push("");
|
|
81
|
+
lines.push("By phase:");
|
|
82
|
+
for (const [phase, count] of [...stats.byPhase].sort()) {
|
|
83
|
+
lines.push(` ${phase}: ${count}`);
|
|
84
|
+
}
|
|
85
|
+
lines.push("");
|
|
86
|
+
lines.push("Top tags:");
|
|
87
|
+
for (const { tag, count } of stats.topTags) {
|
|
88
|
+
lines.push(` ${tag}: ${count}`);
|
|
89
|
+
}
|
|
90
|
+
lines.push("");
|
|
91
|
+
lines.push(`Cross-link density: ${stats.crossLinkDensity} related links/doc (avg)`);
|
|
92
|
+
lines.push(`Unlinked documents: ${stats.orphanCount}`);
|
|
93
|
+
lines.push("");
|
|
94
|
+
lines.push("Most connected documents:");
|
|
95
|
+
for (const { id, connections } of stats.mostConnected) {
|
|
96
|
+
lines.push(` ${id}: ${connections} connections`);
|
|
97
|
+
}
|
|
98
|
+
lines.push("");
|
|
99
|
+
lines.push(`Embedding coverage: ${stats.embeddingCoverage.covered}/${stats.embeddingCoverage.total} (${stats.embeddingCoverage.percent}%)`);
|
|
100
|
+
return lines.join("\n");
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=analytics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics.js","sourceRoot":"","sources":["../src/analytics.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { createKnowledgeServer } from "./index.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import { buildGraph } from "./graph.js";
|
|
7
|
+
import { validateGraph, formatValidationReport } from "./validator.js";
|
|
8
|
+
import { loadConfig, getEffectiveDomains } from "./config.js";
|
|
9
|
+
import { generateEmbeddings } from "./generate-embeddings.js";
|
|
10
|
+
import { initKnowledgeDir } from "./init.js";
|
|
11
|
+
import { log } from "./logger.js";
|
|
12
|
+
const VERSION = "1.0.0";
|
|
13
|
+
const COMMANDS = ["serve", "embeddings", "init", "validate"];
|
|
14
|
+
function printUsage() {
|
|
15
|
+
console.log(`knowledge-mcp-server v${VERSION}
|
|
16
|
+
|
|
17
|
+
Usage: knowledge-mcp-server [command] [options]
|
|
18
|
+
|
|
19
|
+
Commands:
|
|
20
|
+
serve Start the MCP server over stdio (default)
|
|
21
|
+
embeddings Generate embeddings for all documents via Voyage AI
|
|
22
|
+
init Scaffold a new knowledge/ directory with config template
|
|
23
|
+
validate Run graph integrity checks and report issues
|
|
24
|
+
|
|
25
|
+
Options:
|
|
26
|
+
--knowledge-dir <path> Path to knowledge directory (default: ./knowledge)
|
|
27
|
+
--help, -h Show this help message
|
|
28
|
+
--version Show version number`);
|
|
29
|
+
}
|
|
30
|
+
function parseArgs(argv) {
|
|
31
|
+
const args = argv.slice(2);
|
|
32
|
+
let command = "serve";
|
|
33
|
+
let knowledgeDir = resolve(process.cwd(), "knowledge");
|
|
34
|
+
// First non-flag argument is the command
|
|
35
|
+
if (args[0] && !args[0].startsWith("-") && COMMANDS.includes(args[0])) {
|
|
36
|
+
command = args.shift();
|
|
37
|
+
}
|
|
38
|
+
for (let i = 0; i < args.length; i++) {
|
|
39
|
+
if (args[i] === "--knowledge-dir" && args[i + 1]) {
|
|
40
|
+
knowledgeDir = resolve(args[++i]);
|
|
41
|
+
}
|
|
42
|
+
else if (args[i] === "--help" || args[i] === "-h") {
|
|
43
|
+
printUsage();
|
|
44
|
+
process.exit(0);
|
|
45
|
+
}
|
|
46
|
+
else if (args[i] === "--version") {
|
|
47
|
+
console.log(VERSION);
|
|
48
|
+
process.exit(0);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return { command, knowledgeDir };
|
|
52
|
+
}
|
|
53
|
+
async function serve(knowledgeDir) {
|
|
54
|
+
const { server } = createKnowledgeServer(knowledgeDir);
|
|
55
|
+
const transport = new StdioServerTransport();
|
|
56
|
+
await server.connect(transport);
|
|
57
|
+
log.info("server_started", { transport: "stdio" });
|
|
58
|
+
}
|
|
59
|
+
function validate(knowledgeDir) {
|
|
60
|
+
if (!existsSync(knowledgeDir)) {
|
|
61
|
+
console.error(`Error: Knowledge directory not found: ${knowledgeDir}`);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
const config = loadConfig(knowledgeDir);
|
|
65
|
+
const validDomains = getEffectiveDomains(config, knowledgeDir);
|
|
66
|
+
const graph = buildGraph(knowledgeDir, validDomains);
|
|
67
|
+
const report = validateGraph(graph);
|
|
68
|
+
console.log(formatValidationReport(report));
|
|
69
|
+
const hasIssues = report.orphans.length > 0 ||
|
|
70
|
+
report.brokenRelated.length > 0 ||
|
|
71
|
+
report.brokenChildren.length > 0 ||
|
|
72
|
+
report.circularParents.length > 0;
|
|
73
|
+
if (hasIssues) {
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function main() {
|
|
78
|
+
const { command, knowledgeDir } = parseArgs(process.argv);
|
|
79
|
+
switch (command) {
|
|
80
|
+
case "serve":
|
|
81
|
+
await serve(knowledgeDir);
|
|
82
|
+
break;
|
|
83
|
+
case "embeddings":
|
|
84
|
+
await generateEmbeddings(knowledgeDir);
|
|
85
|
+
break;
|
|
86
|
+
case "init":
|
|
87
|
+
initKnowledgeDir(knowledgeDir);
|
|
88
|
+
break;
|
|
89
|
+
case "validate":
|
|
90
|
+
validate(knowledgeDir);
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
main().catch((err) => {
|
|
95
|
+
log.error("fatal", { error: String(err) });
|
|
96
|
+
process.exit(1);
|
|
97
|
+
});
|
|
98
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export interface PhaseConfig {
|
|
2
|
+
id: number;
|
|
3
|
+
name: string;
|
|
4
|
+
aliases?: string[];
|
|
5
|
+
}
|
|
6
|
+
export interface KnowledgeConfig {
|
|
7
|
+
name?: string;
|
|
8
|
+
domains?: string[];
|
|
9
|
+
phases?: PhaseConfig[];
|
|
10
|
+
query_hints?: Record<string, string[]>;
|
|
11
|
+
synonyms?: Record<string, string[]>;
|
|
12
|
+
embeddings?: {
|
|
13
|
+
provider?: string;
|
|
14
|
+
model?: string;
|
|
15
|
+
api_key_env?: string;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Load knowledge.config.yaml from the knowledge directory root.
|
|
20
|
+
* Returns null if the file is missing or invalid.
|
|
21
|
+
*/
|
|
22
|
+
export declare function loadConfig(knowledgeDir: string): KnowledgeConfig | null;
|
|
23
|
+
/**
|
|
24
|
+
* Discover domains by scanning top-level directories in the knowledge directory.
|
|
25
|
+
* Excludes directories starting with ".".
|
|
26
|
+
*/
|
|
27
|
+
export declare function discoverDomains(knowledgeDir: string): string[];
|
|
28
|
+
/**
|
|
29
|
+
* Return the effective domain list for validation.
|
|
30
|
+
* - If config specifies domains, return them (strict validation).
|
|
31
|
+
* - Otherwise return null (permissive — accept any domain).
|
|
32
|
+
*/
|
|
33
|
+
export declare function getEffectiveDomains(config: KnowledgeConfig | null, _knowledgeDir: string): string[] | null;
|
|
34
|
+
/**
|
|
35
|
+
* Return the effective phase IDs for validation.
|
|
36
|
+
* - If config specifies phases, return their IDs (strict validation).
|
|
37
|
+
* - Otherwise return null (permissive — accept any positive integer).
|
|
38
|
+
*/
|
|
39
|
+
export declare function getEffectivePhaseIds(config: KnowledgeConfig | null): number[] | null;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { parse as parseYaml } from "yaml";
|
|
4
|
+
import { log } from "./logger.js";
|
|
5
|
+
const CONFIG_FILENAME = "knowledge.config.yaml";
|
|
6
|
+
/**
|
|
7
|
+
* Load knowledge.config.yaml from the knowledge directory root.
|
|
8
|
+
* Returns null if the file is missing or invalid.
|
|
9
|
+
*/
|
|
10
|
+
export function loadConfig(knowledgeDir) {
|
|
11
|
+
const configPath = join(knowledgeDir, CONFIG_FILENAME);
|
|
12
|
+
let raw;
|
|
13
|
+
try {
|
|
14
|
+
raw = readFileSync(configPath, "utf-8");
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
// File doesn't exist — zero-config mode
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
const parsed = parseYaml(raw);
|
|
22
|
+
if (!parsed || typeof parsed !== "object") {
|
|
23
|
+
log.warn("config", { error: "Config file is empty or not an object" });
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
return parsed;
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
log.warn("config", {
|
|
30
|
+
error: `Failed to parse ${CONFIG_FILENAME}: ${err instanceof Error ? err.message : String(err)}`,
|
|
31
|
+
});
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Discover domains by scanning top-level directories in the knowledge directory.
|
|
37
|
+
* Excludes directories starting with ".".
|
|
38
|
+
*/
|
|
39
|
+
export function discoverDomains(knowledgeDir) {
|
|
40
|
+
try {
|
|
41
|
+
return readdirSync(knowledgeDir)
|
|
42
|
+
.filter((entry) => {
|
|
43
|
+
if (entry.startsWith("."))
|
|
44
|
+
return false;
|
|
45
|
+
try {
|
|
46
|
+
return statSync(join(knowledgeDir, entry)).isDirectory();
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
.sort();
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Return the effective domain list for validation.
|
|
60
|
+
* - If config specifies domains, return them (strict validation).
|
|
61
|
+
* - Otherwise return null (permissive — accept any domain).
|
|
62
|
+
*/
|
|
63
|
+
export function getEffectiveDomains(config, _knowledgeDir) {
|
|
64
|
+
if (config?.domains && config.domains.length > 0) {
|
|
65
|
+
return config.domains;
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Return the effective phase IDs for validation.
|
|
71
|
+
* - If config specifies phases, return their IDs (strict validation).
|
|
72
|
+
* - Otherwise return null (permissive — accept any positive integer).
|
|
73
|
+
*/
|
|
74
|
+
export function getEffectivePhaseIds(config) {
|
|
75
|
+
if (config?.phases && config.phases.length > 0) {
|
|
76
|
+
return config.phases.map((p) => p.id);
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAqBlC,MAAM,eAAe,GAAG,uBAAuB,CAAC;AAEhD;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,YAAoB;IAC7C,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,EAAE,eAAe,CAAC,CAAC;IACvD,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,wCAAwC;QACxC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAY,CAAC;QACzC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC1C,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,uCAAuC,EAAE,CAAC,CAAC;YACvE,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,MAAyB,CAAC;IACnC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE;YACjB,KAAK,EAAE,mBAAmB,eAAe,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;SACjG,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,YAAoB;IAClD,IAAI,CAAC;QACH,OAAO,WAAW,CAAC,YAAY,CAAC;aAC7B,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YAChB,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,OAAO,KAAK,CAAC;YACxC,IAAI,CAAC;gBACH,OAAO,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;YAC3D,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC,CAAC;aACD,IAAI,EAAE,CAAC;IACZ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,MAA8B,EAC9B,aAAqB;IAErB,IAAI,MAAM,EAAE,OAAO,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACjD,OAAO,MAAM,CAAC,OAAO,CAAC;IACxB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAA8B;IACjE,IAAI,MAAM,EAAE,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/C,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { KnowledgeDocument } from "./loader.js";
|
|
2
|
+
export interface EmbeddingsStore {
|
|
3
|
+
vectors: Map<string, number[]>;
|
|
4
|
+
available: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function loadEmbeddings(knowledgeDir: string): EmbeddingsStore;
|
|
7
|
+
export declare function cosineSimilarity(a: number[], b: number[]): number;
|
|
8
|
+
export declare function embedQuery(query: string): Promise<number[] | null>;
|
|
9
|
+
export declare function tokenize(text: string): string[];
|
|
10
|
+
export interface Bm25Index {
|
|
11
|
+
docTermFreqs: Map<string, Map<string, number>>;
|
|
12
|
+
docFreq: Map<string, number>;
|
|
13
|
+
idf: Map<string, number>;
|
|
14
|
+
docLengths: Map<string, number>;
|
|
15
|
+
avgDocLength: number;
|
|
16
|
+
docCount: number;
|
|
17
|
+
}
|
|
18
|
+
/** @deprecated Use Bm25Index — kept for backward compatibility */
|
|
19
|
+
export type TfIdfIndex = Bm25Index;
|
|
20
|
+
export declare function buildTfIdfIndex(docs: Map<string, KnowledgeDocument>): Bm25Index;
|
|
21
|
+
export declare function bm25Score(query: string, docId: string, index: Bm25Index): number;
|
|
22
|
+
/** @deprecated Use bm25Score — kept for backward compatibility */
|
|
23
|
+
export declare const tfidfScore: typeof bm25Score;
|
|
24
|
+
export declare function updateBm25Index(index: Bm25Index, docId: string, doc: KnowledgeDocument | null): void;
|
|
25
|
+
export declare function embedSingleDocument(embeddingsStore: EmbeddingsStore, knowledgeDir: string, doc: KnowledgeDocument): Promise<void>;
|
|
26
|
+
export declare function removeEmbedding(embeddingsStore: EmbeddingsStore, knowledgeDir: string, docId: string): void;
|