@nzpr/kb 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +5 -0
- package/LICENSE +21 -0
- package/README.md +185 -0
- package/bin/kb-admin.js +5 -0
- package/bin/kb.js +5 -0
- package/docker-compose.pgvector.yml +19 -0
- package/lib/admin-cli.js +203 -0
- package/lib/chunking.js +16 -0
- package/lib/cli-common.js +73 -0
- package/lib/cli.js +391 -0
- package/lib/config.js +109 -0
- package/lib/db.js +81 -0
- package/lib/embeddings.js +94 -0
- package/lib/frontmatter.js +66 -0
- package/lib/index.js +140 -0
- package/lib/kb-proposals.js +188 -0
- package/lib/migrations.js +149 -0
- package/lib/repo-init.js +438 -0
- package/lib/search.js +206 -0
- package/migrations/0001_initial.sql +77 -0
- package/migrations/0002_relax_embedding_dimension.sql +9 -0
- package/migrations/0003_simplify_documents_table.sql +64 -0
- package/package.json +58 -0
package/.env.example
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 nzpr
|
|
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,185 @@
|
|
|
1
|
+
# Team Knowledge Base CLI
|
|
2
|
+
|
|
3
|
+
`@nzpr/kb` is the npm package and CLI used by a separate knowledge-authority repository.
|
|
4
|
+
|
|
5
|
+
Each KB instance is defined by:
|
|
6
|
+
|
|
7
|
+
- `KB_DATABASE_URL`
|
|
8
|
+
- `KB_GITHUB_REPO`
|
|
9
|
+
|
|
10
|
+
This repository is for the tool itself.
|
|
11
|
+
|
|
12
|
+
The actual knowledge base should live in a separate repository that owns:
|
|
13
|
+
|
|
14
|
+
- `kb/docs/`
|
|
15
|
+
- issue templates
|
|
16
|
+
- review and approval workflow
|
|
17
|
+
- CI that runs `kb publish`
|
|
18
|
+
|
|
19
|
+
Bootstrap that repo with:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
kb init-repo --dir /path/to/knowledge-repo
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Or bootstrap and configure it in one step:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
export GITHUB_TOKEN=...
|
|
29
|
+
kb init-repo \
|
|
30
|
+
--dir /path/to/knowledge-repo \
|
|
31
|
+
--repo owner/knowledge-repo \
|
|
32
|
+
--database-url postgresql://kb:kb@host:5432/kb \
|
|
33
|
+
--embedding-mode bge-m3-openai \
|
|
34
|
+
--embedding-api-url https://embeddings.example.com/v1/embeddings \
|
|
35
|
+
--embedding-model BAAI/bge-m3
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Commands
|
|
39
|
+
|
|
40
|
+
- `kb init-repo [--dir PATH] [--repo OWNER/REPO]`
|
|
41
|
+
- `kb search "<terms>"`
|
|
42
|
+
- `kb ask "<question>"`
|
|
43
|
+
- `kb list`
|
|
44
|
+
- `kb catalog [--json]`
|
|
45
|
+
- `kb create --title TEXT --text TEXT [--path RELATIVE_PATH]`
|
|
46
|
+
- `kb publish --docs-root PATH`
|
|
47
|
+
|
|
48
|
+
## Install
|
|
49
|
+
|
|
50
|
+
From npm:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm install -g @nzpr/kb
|
|
54
|
+
kb --help
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
From source:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm install
|
|
61
|
+
npm link
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Knowledge Repo Workflow
|
|
65
|
+
|
|
66
|
+
In the separate knowledge-authority repo:
|
|
67
|
+
|
|
68
|
+
1. Run `kb init-repo` once to scaffold the repo. If you provide `--repo`, `--database-url`, and `GITHUB_TOKEN`, it also configures the target repo and preflights the database.
|
|
69
|
+
2. Open or update a knowledge proposal issue.
|
|
70
|
+
3. Review and approve it there.
|
|
71
|
+
4. Materialize it into `kb/docs/`.
|
|
72
|
+
5. After merge, that repo's CI runs `kb publish --docs-root ./kb/docs`.
|
|
73
|
+
|
|
74
|
+
`kb init-repo` is safe to rerun. It reports bootstrap status for:
|
|
75
|
+
|
|
76
|
+
- local scaffold creation
|
|
77
|
+
- database preflight and schema initialization
|
|
78
|
+
- GitHub repo labels, variables, and secrets
|
|
79
|
+
|
|
80
|
+
If one remote step fails, the scaffold still stays in place and the command tells you what to rerun.
|
|
81
|
+
|
|
82
|
+
## Runtime
|
|
83
|
+
|
|
84
|
+
Node.js 20+ is required.
|
|
85
|
+
|
|
86
|
+
Query commands need:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
export KB_DATABASE_URL=postgresql://kb:kb@localhost:5432/kb
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Higher-quality embeddings with a self-hosted OpenAI-compatible `BAAI/bge-m3` server:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
export KB_EMBEDDING_MODE=bge-m3-openai
|
|
96
|
+
export KB_EMBEDDING_API_URL=https://embeddings.example.com/v1/embeddings
|
|
97
|
+
export KB_EMBEDDING_MODEL=BAAI/bge-m3
|
|
98
|
+
export KB_EMBEDDING_API_KEY=...
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
If `KB_EMBEDDING_MODE` is omitted, the CLI uses local hash embeddings for development.
|
|
102
|
+
|
|
103
|
+
Publishing needs one more privileged environment variable:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
export KB_GITHUB_REPO=owner/repo
|
|
107
|
+
export GITHUB_TOKEN=...
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Publishing is allowed when the provided `GITHUB_TOKEN` has write access to `KB_GITHUB_REPO`. Normal readers do not need GitHub credentials.
|
|
111
|
+
|
|
112
|
+
When `kb init-repo` is given `--repo`, it uses the same token to:
|
|
113
|
+
|
|
114
|
+
- enable issues in the target repo
|
|
115
|
+
- create or update the `kb-entry` and `kb-approved` labels
|
|
116
|
+
- write repository secrets and variables
|
|
117
|
+
- verify and initialize the target database schema if `--database-url` is provided
|
|
118
|
+
|
|
119
|
+
If you run `kb init-repo` without the repo or database inputs, it still scaffolds the knowledge repo and prints exactly which remote bootstrap inputs are still pending.
|
|
120
|
+
|
|
121
|
+
## Quick Start
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
export KB_DATABASE_URL=postgresql://kb:kb@localhost:5432/kb
|
|
125
|
+
export KB_GITHUB_REPO=owner/repo
|
|
126
|
+
export GITHUB_TOKEN=...
|
|
127
|
+
docker compose -f docker-compose.pgvector.yml up -d
|
|
128
|
+
kb publish --docs-root ./kb/docs
|
|
129
|
+
kb catalog --json
|
|
130
|
+
kb search "deployment rule"
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Each Markdown file is indexed as one document and one vector row. Keep documents simple: one title and one body.
|
|
134
|
+
|
|
135
|
+
## Optional Split Mode
|
|
136
|
+
|
|
137
|
+
If you later want separate entities, isolated databases, or a dedicated content repo, you can use `kb create` with:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
export KB_GITHUB_REPO=org/knowledge-content
|
|
141
|
+
export GITHUB_TOKEN=...
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
That path is optional. The default assumption is that you work in one repo.
|
|
145
|
+
|
|
146
|
+
## npm Release
|
|
147
|
+
|
|
148
|
+
The package is published as `@nzpr/kb`.
|
|
149
|
+
|
|
150
|
+
- local validation: `npm test`
|
|
151
|
+
- local package preview: `npm pack --dry-run`
|
|
152
|
+
- GitHub Actions publish: push a tag like `v0.1.0` or run the `npm-publish` workflow manually
|
|
153
|
+
- required repository secret: `NPM_TOKEN`
|
|
154
|
+
|
|
155
|
+
## Using From A Knowledge Repo
|
|
156
|
+
|
|
157
|
+
The knowledge-authority repo should install this package in CI and use it to sync `kb/docs/` into the vector database:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
npm install -g @nzpr/kb
|
|
161
|
+
export KB_GITHUB_REPO=owner/repo
|
|
162
|
+
export GITHUB_TOKEN=...
|
|
163
|
+
kb publish --docs-root ./kb/docs
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Or scaffold that repo first:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
kb init-repo --dir .
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
If you want init to configure the target repo too:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
export GITHUB_TOKEN=...
|
|
176
|
+
export KB_REPO_AUTOMATION_TOKEN=...
|
|
177
|
+
kb init-repo \
|
|
178
|
+
--dir . \
|
|
179
|
+
--repo owner/knowledge-repo \
|
|
180
|
+
--database-url postgresql://kb:kb@host:5432/kb \
|
|
181
|
+
--embedding-mode bge-m3-openai \
|
|
182
|
+
--embedding-api-url https://embeddings.example.com/v1/embeddings \
|
|
183
|
+
--embedding-model BAAI/bge-m3 \
|
|
184
|
+
--embedding-api-key YOUR_KEY
|
|
185
|
+
```
|
package/bin/kb-admin.js
ADDED
package/bin/kb.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
services:
|
|
2
|
+
postgres:
|
|
3
|
+
image: pgvector/pgvector:pg16
|
|
4
|
+
environment:
|
|
5
|
+
POSTGRES_DB: kb
|
|
6
|
+
POSTGRES_USER: kb
|
|
7
|
+
POSTGRES_PASSWORD: kb
|
|
8
|
+
ports:
|
|
9
|
+
- "5432:5432"
|
|
10
|
+
volumes:
|
|
11
|
+
- kb_pgdata:/var/lib/postgresql/data
|
|
12
|
+
healthcheck:
|
|
13
|
+
test: ["CMD-SHELL", "pg_isready -U kb -d kb"]
|
|
14
|
+
interval: 10s
|
|
15
|
+
timeout: 5s
|
|
16
|
+
retries: 10
|
|
17
|
+
|
|
18
|
+
volumes:
|
|
19
|
+
kb_pgdata:
|
package/lib/admin-cli.js
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
resolveDatabaseUrl,
|
|
5
|
+
resolveDocsRoot,
|
|
6
|
+
resolveEmbeddingProfile,
|
|
7
|
+
tryResolveKnowledgeRoot
|
|
8
|
+
} from "./config.js";
|
|
9
|
+
import { connect, initDb, schemaStatus } from "./db.js";
|
|
10
|
+
import { ingestDocuments } from "./index.js";
|
|
11
|
+
import { writeProposalDocument } from "./kb-proposals.js";
|
|
12
|
+
import { databaseHelp, formatCliError, maskConnection, parseFlags } from "./cli-common.js";
|
|
13
|
+
|
|
14
|
+
export async function main(argv) {
|
|
15
|
+
try {
|
|
16
|
+
const [command, ...rest] = argv;
|
|
17
|
+
if (!command || command === "--help" || command === "-h") {
|
|
18
|
+
printHelp();
|
|
19
|
+
return 0;
|
|
20
|
+
}
|
|
21
|
+
if (rest.includes("--help") || rest.includes("-h")) {
|
|
22
|
+
printCommandHelp(command);
|
|
23
|
+
return 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const { flags } = parseFlags(rest);
|
|
27
|
+
switch (command) {
|
|
28
|
+
case "migrate": {
|
|
29
|
+
const databaseUrl = requireDatabaseUrl();
|
|
30
|
+
const client = await connect(databaseUrl);
|
|
31
|
+
try {
|
|
32
|
+
const result = await initDb(client);
|
|
33
|
+
console.log(
|
|
34
|
+
`migrated knowledge database: ${maskConnection(databaseUrl)} current=${result.currentVersion} applied=${result.appliedCount}`
|
|
35
|
+
);
|
|
36
|
+
} finally {
|
|
37
|
+
await client.end();
|
|
38
|
+
}
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
41
|
+
case "init-db": {
|
|
42
|
+
const databaseUrl = requireDatabaseUrl();
|
|
43
|
+
const client = await connect(databaseUrl);
|
|
44
|
+
try {
|
|
45
|
+
const result = await initDb(client);
|
|
46
|
+
console.log(
|
|
47
|
+
`initialized knowledge database: ${maskConnection(databaseUrl)} current=${result.currentVersion} applied=${result.appliedCount}`
|
|
48
|
+
);
|
|
49
|
+
} finally {
|
|
50
|
+
await client.end();
|
|
51
|
+
}
|
|
52
|
+
return 0;
|
|
53
|
+
}
|
|
54
|
+
case "status": {
|
|
55
|
+
const databaseUrl = requireDatabaseUrl();
|
|
56
|
+
const client = await connect(databaseUrl);
|
|
57
|
+
try {
|
|
58
|
+
const status = await schemaStatus(client);
|
|
59
|
+
console.log(`database: ${maskConnection(databaseUrl)}`);
|
|
60
|
+
console.log(`schema current: ${status.currentVersion}`);
|
|
61
|
+
console.log(`schema latest: ${status.latestVersion}`);
|
|
62
|
+
console.log(`schema pending: ${status.pendingCount}`);
|
|
63
|
+
} finally {
|
|
64
|
+
await client.end();
|
|
65
|
+
}
|
|
66
|
+
return 0;
|
|
67
|
+
}
|
|
68
|
+
case "ingest": {
|
|
69
|
+
const databaseUrl = requireDatabaseUrl();
|
|
70
|
+
const embeddingProfile = resolveEmbeddingProfile();
|
|
71
|
+
const knowledgeRoot = flags["knowledge-root"]
|
|
72
|
+
? path.resolve(flags["knowledge-root"])
|
|
73
|
+
: tryResolveKnowledgeRoot();
|
|
74
|
+
const docsRoot = resolveDocsRoot({
|
|
75
|
+
docsRoot: flags["docs-root"] ?? null,
|
|
76
|
+
knowledgeRoot
|
|
77
|
+
});
|
|
78
|
+
if (!docsRoot) {
|
|
79
|
+
console.error("--docs-root PATH is required when no local docs directory is present");
|
|
80
|
+
return 2;
|
|
81
|
+
}
|
|
82
|
+
if (!fs.existsSync(docsRoot)) {
|
|
83
|
+
console.error(`docs root does not exist: ${docsRoot}`);
|
|
84
|
+
return 2;
|
|
85
|
+
}
|
|
86
|
+
const result = await ingestDocuments({ databaseUrl, docsRoot, embeddingProfile });
|
|
87
|
+
console.log(
|
|
88
|
+
`indexed ${result.documents} documents, wrote ${result.vectors} vectors using ${result.embeddingMode}${result.embeddingModel ? `/${result.embeddingModel}` : ""} embeddings`
|
|
89
|
+
);
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
case "issue-to-doc": {
|
|
93
|
+
const knowledgeRoot = flags["knowledge-root"]
|
|
94
|
+
? path.resolve(flags["knowledge-root"])
|
|
95
|
+
: tryResolveKnowledgeRoot();
|
|
96
|
+
const docsRoot = resolveDocsRoot({
|
|
97
|
+
docsRoot: flags["docs-root"] ?? null,
|
|
98
|
+
knowledgeRoot
|
|
99
|
+
});
|
|
100
|
+
if (!docsRoot) {
|
|
101
|
+
console.error("--docs-root PATH is required when no local docs directory is present");
|
|
102
|
+
return 2;
|
|
103
|
+
}
|
|
104
|
+
const issueEventPath = flags["issue-event"] ? path.resolve(flags["issue-event"]) : null;
|
|
105
|
+
if (!issueEventPath) {
|
|
106
|
+
console.error("--issue-event PATH is required");
|
|
107
|
+
return 2;
|
|
108
|
+
}
|
|
109
|
+
if (!fs.existsSync(issueEventPath)) {
|
|
110
|
+
console.error(`issue event payload does not exist: ${issueEventPath}`);
|
|
111
|
+
return 2;
|
|
112
|
+
}
|
|
113
|
+
const event = JSON.parse(fs.readFileSync(issueEventPath, "utf8"));
|
|
114
|
+
const issue = event.issue;
|
|
115
|
+
if (!issue?.body) {
|
|
116
|
+
throw new Error("issue event payload did not include an issue body");
|
|
117
|
+
}
|
|
118
|
+
const result = writeProposalDocument({
|
|
119
|
+
issueNumber: issue.number,
|
|
120
|
+
issueTitle: issue.title,
|
|
121
|
+
issueBody: issue.body,
|
|
122
|
+
docsRoot
|
|
123
|
+
});
|
|
124
|
+
writeGitHubOutput({
|
|
125
|
+
doc_id: result.proposal.docId,
|
|
126
|
+
doc_path: path.relative(process.cwd(), result.absolutePath).replace(/\\/g, "/"),
|
|
127
|
+
branch: result.branch,
|
|
128
|
+
commit_message: result.commitMessage,
|
|
129
|
+
pr_title: result.prTitle,
|
|
130
|
+
pr_body: result.prBody
|
|
131
|
+
});
|
|
132
|
+
console.log(`materialized issue #${issue.number} into ${result.relativePath}`);
|
|
133
|
+
return 0;
|
|
134
|
+
}
|
|
135
|
+
default:
|
|
136
|
+
console.error(`unknown command: ${command}`);
|
|
137
|
+
return 2;
|
|
138
|
+
}
|
|
139
|
+
} catch (error) {
|
|
140
|
+
console.error(formatCliError(error));
|
|
141
|
+
return 1;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function printHelp() {
|
|
146
|
+
console.log(`usage: kb-admin <command> [options]
|
|
147
|
+
|
|
148
|
+
internal automation only; normal users should use kb
|
|
149
|
+
|
|
150
|
+
commands:
|
|
151
|
+
migrate
|
|
152
|
+
init-db
|
|
153
|
+
status
|
|
154
|
+
ingest
|
|
155
|
+
issue-to-doc
|
|
156
|
+
|
|
157
|
+
${databaseHelp()}
|
|
158
|
+
|
|
159
|
+
ingest options:
|
|
160
|
+
--docs-root PATH
|
|
161
|
+
|
|
162
|
+
issue-to-doc options:
|
|
163
|
+
--issue-event PATH
|
|
164
|
+
--docs-root PATH
|
|
165
|
+
|
|
166
|
+
optional path hint:
|
|
167
|
+
--knowledge-root PATH
|
|
168
|
+
`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function printCommandHelp(command) {
|
|
172
|
+
const commandHelp = {
|
|
173
|
+
migrate: `usage: kb-admin migrate\n\n${databaseHelp()}`,
|
|
174
|
+
"init-db": `usage: kb-admin init-db\n\n${databaseHelp()}\n\ninit-db is a bootstrap alias for migrate.`,
|
|
175
|
+
status: `usage: kb-admin status\n\n${databaseHelp()}`,
|
|
176
|
+
ingest: `usage: kb-admin ingest [--docs-root PATH] [--knowledge-root PATH]\n\n${databaseHelp()}`,
|
|
177
|
+
"issue-to-doc": `usage: kb-admin issue-to-doc --issue-event PATH [--docs-root PATH]\n\nInternal automation for approved KB issues.`
|
|
178
|
+
};
|
|
179
|
+
console.log(commandHelp[command] ?? `unknown command: ${command}`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function requireDatabaseUrl() {
|
|
183
|
+
const databaseUrl = resolveDatabaseUrl();
|
|
184
|
+
if (!databaseUrl) {
|
|
185
|
+
throw new Error("KB_DATABASE_URL is not set");
|
|
186
|
+
}
|
|
187
|
+
return databaseUrl;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function writeGitHubOutput(entries) {
|
|
191
|
+
const outputPath = process.env.GITHUB_OUTPUT ?? null;
|
|
192
|
+
if (!outputPath) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
196
|
+
const normalized = String(value);
|
|
197
|
+
if (normalized.includes("\n")) {
|
|
198
|
+
fs.appendFileSync(outputPath, `${key}<<__KB_EOF__\n${normalized}\n__KB_EOF__\n`, "utf8");
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
fs.appendFileSync(outputPath, `${key}=${normalized}\n`, "utf8");
|
|
202
|
+
}
|
|
203
|
+
}
|
package/lib/chunking.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function chunkMarkdown(meta, body) {
|
|
2
|
+
const content = String(body ?? "").trim();
|
|
3
|
+
if (!content) {
|
|
4
|
+
return [];
|
|
5
|
+
}
|
|
6
|
+
return [
|
|
7
|
+
{
|
|
8
|
+
chunkId: `${meta.docId}#0`,
|
|
9
|
+
docId: meta.docId,
|
|
10
|
+
title: meta.title,
|
|
11
|
+
heading: meta.title,
|
|
12
|
+
content,
|
|
13
|
+
path: meta.path,
|
|
14
|
+
}
|
|
15
|
+
];
|
|
16
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export function parseFlags(args) {
|
|
2
|
+
const flags = {};
|
|
3
|
+
const positional = [];
|
|
4
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
5
|
+
const arg = args[index];
|
|
6
|
+
if (!arg.startsWith("--")) {
|
|
7
|
+
positional.push(arg);
|
|
8
|
+
continue;
|
|
9
|
+
}
|
|
10
|
+
const key = arg.slice(2);
|
|
11
|
+
if (key === "json" || key === "include-drafts") {
|
|
12
|
+
flags[key] = true;
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
const value = args[index + 1];
|
|
16
|
+
if (value === undefined || value.startsWith("--")) {
|
|
17
|
+
throw new Error(`missing value for --${key}`);
|
|
18
|
+
}
|
|
19
|
+
flags[key] = value;
|
|
20
|
+
index += 1;
|
|
21
|
+
}
|
|
22
|
+
return { flags, positional };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function maskConnection(connectionString) {
|
|
26
|
+
try {
|
|
27
|
+
const url = new URL(connectionString);
|
|
28
|
+
if (url.password) {
|
|
29
|
+
url.password = "****";
|
|
30
|
+
}
|
|
31
|
+
return url.toString();
|
|
32
|
+
} catch {
|
|
33
|
+
return connectionString;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function formatCliError(error) {
|
|
38
|
+
const message = String(error?.message ?? error);
|
|
39
|
+
if (
|
|
40
|
+
/ECONNREFUSED|connect ECONNREFUSED|ENOTFOUND|timeout expired|Connection terminated unexpectedly/i.test(
|
|
41
|
+
message
|
|
42
|
+
)
|
|
43
|
+
) {
|
|
44
|
+
return `database connection failed: ${message}`;
|
|
45
|
+
}
|
|
46
|
+
return message;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function databaseHelp() {
|
|
50
|
+
return `required runtime:
|
|
51
|
+
KB_DATABASE_URL=postgresql://USER:PASSWORD@HOST:5432/DB
|
|
52
|
+
|
|
53
|
+
optional embeddings:
|
|
54
|
+
KB_EMBEDDING_MODE=local-hash|bge-m3-openai
|
|
55
|
+
KB_EMBEDDING_API_URL=http://HOST:8000/v1/embeddings
|
|
56
|
+
KB_EMBEDDING_MODEL=BAAI/bge-m3
|
|
57
|
+
KB_EMBEDDING_API_KEY=...`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function githubCreationHelp() {
|
|
61
|
+
return `required GitHub issue creation runtime:
|
|
62
|
+
KB_GITHUB_REPO=OWNER/REPO
|
|
63
|
+
GITHUB_TOKEN=...
|
|
64
|
+
|
|
65
|
+
optional GitHub API override:
|
|
66
|
+
GITHUB_API_URL=https://api.github.com`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function publishHelp() {
|
|
70
|
+
return `required publish authorization:
|
|
71
|
+
KB_GITHUB_REPO=OWNER/REPO
|
|
72
|
+
GITHUB_TOKEN=...`;
|
|
73
|
+
}
|