@nzpr/kb 0.1.2 → 0.1.3
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 +14 -5
- package/lib/cli.js +6 -3
- package/lib/init-repo-interactive.js +31 -0
- package/lib/repo-init.js +34 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,6 +24,7 @@ The public surface of `@nzpr/kb` is the `kb` CLI.
|
|
|
24
24
|
- `kb init-repo`
|
|
25
25
|
Initialize a knowledge base workspace.
|
|
26
26
|
Use `kb init-repo --interactive` for a guided setup flow.
|
|
27
|
+
Use `--layout repo-root` when this repository is itself the KB.
|
|
27
28
|
|
|
28
29
|
- `kb create --title TEXT --text TEXT`
|
|
29
30
|
Propose a new knowledge document or a substantial update.
|
|
@@ -102,6 +103,13 @@ If you want the CLI to walk you through setup and tell you what to do next:
|
|
|
102
103
|
kb init-repo --interactive
|
|
103
104
|
```
|
|
104
105
|
|
|
106
|
+
Layout options:
|
|
107
|
+
|
|
108
|
+
- `--layout repo-root` puts documents in `docs/`
|
|
109
|
+
- `--layout nested-kb` puts documents in `kb/docs/`
|
|
110
|
+
|
|
111
|
+
For a dedicated knowledge repository, prefer `--layout repo-root`.
|
|
112
|
+
|
|
105
113
|
Or bootstrap and configure it in one step:
|
|
106
114
|
|
|
107
115
|
```bash
|
|
@@ -120,12 +128,13 @@ Inside that knowledge repo, the normal flow is:
|
|
|
120
128
|
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.
|
|
121
129
|
2. Open or update a knowledge proposal issue.
|
|
122
130
|
3. Review and approve it there.
|
|
123
|
-
4. Materialize it into
|
|
124
|
-
5. After merge, that repo's CI runs `kb publish
|
|
131
|
+
4. Materialize it into the configured docs directory.
|
|
132
|
+
5. After merge, that repo's CI runs `kb publish` against that docs directory.
|
|
125
133
|
|
|
126
134
|
`kb init-repo` is safe to rerun. It reports bootstrap status for:
|
|
127
135
|
|
|
128
136
|
- local scaffold creation
|
|
137
|
+
- knowledge document layout selection
|
|
129
138
|
- database preflight and schema initialization
|
|
130
139
|
- GitHub repo labels, variables, and secrets
|
|
131
140
|
|
|
@@ -185,7 +194,7 @@ export KB_DATABASE_URL=postgresql://kb:kb@localhost:5432/kb
|
|
|
185
194
|
export KB_GITHUB_REPO=owner/repo
|
|
186
195
|
export GITHUB_TOKEN=...
|
|
187
196
|
docker compose -f docker-compose.pgvector.yml up -d
|
|
188
|
-
kb publish --docs-root ./
|
|
197
|
+
kb publish --docs-root ./docs
|
|
189
198
|
kb catalog --json
|
|
190
199
|
kb search "deployment rule"
|
|
191
200
|
```
|
|
@@ -215,13 +224,13 @@ The package is published as `@nzpr/kb`.
|
|
|
215
224
|
|
|
216
225
|
## Using From A Knowledge Repo
|
|
217
226
|
|
|
218
|
-
The knowledge-authority repo should install this package in CI and use it to sync
|
|
227
|
+
The knowledge-authority repo should install this package in CI and use it to sync its configured docs directory into the vector database. For a dedicated KB repo with `--layout repo-root`, that directory is `docs/`:
|
|
219
228
|
|
|
220
229
|
```bash
|
|
221
230
|
npm install -g @nzpr/kb
|
|
222
231
|
export KB_GITHUB_REPO=owner/repo
|
|
223
232
|
export GITHUB_TOKEN=...
|
|
224
|
-
kb publish --docs-root ./
|
|
233
|
+
kb publish --docs-root ./docs
|
|
225
234
|
```
|
|
226
235
|
|
|
227
236
|
Or scaffold that repo first:
|
package/lib/cli.js
CHANGED
|
@@ -65,6 +65,7 @@ export async function main(argv) {
|
|
|
65
65
|
? await collectInitRepoInteractiveOptions({ flags })
|
|
66
66
|
: {
|
|
67
67
|
targetDir: flags.dir ?? process.cwd(),
|
|
68
|
+
layout: flags.layout ?? "nested-kb",
|
|
68
69
|
repo: flags.repo ?? resolveGitHubRepository(),
|
|
69
70
|
databaseUrl: flags["database-url"] ?? process.env.KB_DATABASE_URL ?? null,
|
|
70
71
|
embeddingMode: flags["embedding-mode"] ?? process.env.KB_EMBEDDING_MODE ?? null,
|
|
@@ -78,6 +79,7 @@ export async function main(argv) {
|
|
|
78
79
|
};
|
|
79
80
|
const result = await bootstrapKnowledgeRepo({
|
|
80
81
|
targetDir: initRepoOptions.targetDir,
|
|
82
|
+
layout: initRepoOptions.layout,
|
|
81
83
|
repo: initRepoOptions.repo,
|
|
82
84
|
githubToken: process.env.GITHUB_TOKEN ?? null,
|
|
83
85
|
databaseUrl: initRepoOptions.databaseUrl,
|
|
@@ -89,6 +91,7 @@ export async function main(argv) {
|
|
|
89
91
|
repoAutomationToken: initRepoOptions.repoAutomationToken
|
|
90
92
|
});
|
|
91
93
|
console.log(`initialized knowledge repo scaffold in ${result.root}`);
|
|
94
|
+
console.log(`knowledge documents will live in ${result.docsRootRelative}/`);
|
|
92
95
|
for (const relativePath of result.created) {
|
|
93
96
|
console.log(`created ${relativePath}`);
|
|
94
97
|
}
|
|
@@ -246,7 +249,7 @@ function printHelp() {
|
|
|
246
249
|
console.log(`usage: kb <command> [options]
|
|
247
250
|
|
|
248
251
|
commands:
|
|
249
|
-
init-repo [--interactive] [--dir PATH] [--repo OWNER/REPO]
|
|
252
|
+
init-repo [--interactive] [--layout repo-root|nested-kb] [--dir PATH] [--repo OWNER/REPO]
|
|
250
253
|
create --title TEXT --text TEXT [--path RELATIVE_PATH]
|
|
251
254
|
search <query>
|
|
252
255
|
ask <question>
|
|
@@ -271,7 +274,7 @@ search options:
|
|
|
271
274
|
function printCommandHelp(command) {
|
|
272
275
|
const commandHelp = {
|
|
273
276
|
"init-repo":
|
|
274
|
-
"usage: kb init-repo [--interactive] [--dir PATH] [--repo OWNER/REPO] [--database-url URL] [--embedding-mode MODE] [--embedding-api-url URL] [--embedding-model NAME] [--embedding-api-key KEY] [--db-connect-timeout-ms N] [--repo-automation-token TOKEN]\n\nScaffold a knowledge-authority repository, optionally configure its GitHub settings, and preflight the target database. Use --interactive for a guided terminal walkthrough.",
|
|
277
|
+
"usage: kb init-repo [--interactive] [--layout repo-root|nested-kb] [--dir PATH] [--repo OWNER/REPO] [--database-url URL] [--embedding-mode MODE] [--embedding-api-url URL] [--embedding-model NAME] [--embedding-api-key KEY] [--db-connect-timeout-ms N] [--repo-automation-token TOKEN]\n\nScaffold a knowledge-authority repository, optionally configure its GitHub settings, and preflight the target database. Use --interactive for a guided terminal walkthrough.",
|
|
275
278
|
create: `usage: kb create --title TEXT --text TEXT [--path RELATIVE_PATH] [--repo OWNER/REPO]\n\n${githubCreationHelp()}`,
|
|
276
279
|
search: `usage: kb search <query> [options]\n\n${databaseHelp()}\n --limit N`,
|
|
277
280
|
ask: `usage: kb ask <question> [options]\n\n${databaseHelp()}\n --limit N`,
|
|
@@ -409,7 +412,7 @@ function printInitRepoChecklist(result) {
|
|
|
409
412
|
console.log("");
|
|
410
413
|
console.log("what to do next:");
|
|
411
414
|
if (result.created.length || result.skipped.length) {
|
|
412
|
-
console.log(
|
|
415
|
+
console.log(` 1. commit and push the scaffolded knowledge repo files, including ${result.docsRootRelative}/`);
|
|
413
416
|
}
|
|
414
417
|
if (result.github.status !== "configured") {
|
|
415
418
|
console.log(" 2. make sure the target repo exists and rerun with --repo once GitHub setup is ready");
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import fs from "node:fs";
|
|
2
3
|
import readline from "node:readline/promises";
|
|
3
4
|
import { stdin as defaultStdin, stdout as defaultStdout } from "node:process";
|
|
4
5
|
import { resolveGitHubRepository } from "./config.js";
|
|
@@ -29,6 +30,7 @@ export async function collectInitRepoInteractiveOptions({
|
|
|
29
30
|
validate: requireValue
|
|
30
31
|
})
|
|
31
32
|
);
|
|
33
|
+
const layout = await askKnowledgeLayout(prompt, targetDir, defaults.layout);
|
|
32
34
|
|
|
33
35
|
const configureRepo = await prompt.askConfirm(
|
|
34
36
|
"configure the GitHub repo now",
|
|
@@ -106,6 +108,7 @@ export async function collectInitRepoInteractiveOptions({
|
|
|
106
108
|
|
|
107
109
|
stdout.write("\nplan:\n");
|
|
108
110
|
stdout.write(` local scaffold: ${targetDir}\n`);
|
|
111
|
+
stdout.write(` document layout: ${layout === "repo-root" ? "docs/" : "kb/docs/"}\n`);
|
|
109
112
|
stdout.write(` github repo setup: ${repo ? repo : "skip for now"}\n`);
|
|
110
113
|
stdout.write(` database preflight: ${databaseUrl ? "yes" : "skip for now"}\n`);
|
|
111
114
|
stdout.write(
|
|
@@ -119,6 +122,7 @@ export async function collectInitRepoInteractiveOptions({
|
|
|
119
122
|
|
|
120
123
|
return {
|
|
121
124
|
targetDir,
|
|
125
|
+
layout,
|
|
122
126
|
repo,
|
|
123
127
|
databaseUrl,
|
|
124
128
|
embeddingMode,
|
|
@@ -136,6 +140,8 @@ export async function collectInitRepoInteractiveOptions({
|
|
|
136
140
|
export function buildInitRepoDefaults({ flags = {}, env = process.env, cwd = process.cwd() }) {
|
|
137
141
|
return {
|
|
138
142
|
targetDir: flags.dir ?? cwd,
|
|
143
|
+
layout:
|
|
144
|
+
flags.layout ?? inferDefaultKnowledgeLayout(flags.dir ? path.resolve(cwd, flags.dir) : cwd),
|
|
139
145
|
repo: flags.repo ?? resolveGitHubRepository(env) ?? "",
|
|
140
146
|
databaseUrl: flags["database-url"] ?? env.KB_DATABASE_URL ?? "",
|
|
141
147
|
embeddingMode: flags["embedding-mode"] ?? env.KB_EMBEDDING_MODE ?? "",
|
|
@@ -149,6 +155,31 @@ export function buildInitRepoDefaults({ flags = {}, env = process.env, cwd = pro
|
|
|
149
155
|
};
|
|
150
156
|
}
|
|
151
157
|
|
|
158
|
+
async function askKnowledgeLayout(prompt, targetDir, defaultLayout) {
|
|
159
|
+
const useRepoRoot = await prompt.askConfirm(
|
|
160
|
+
"store documents at repo root docs/ (recommended when this repo is the knowledge base)",
|
|
161
|
+
defaultLayout === "repo-root"
|
|
162
|
+
);
|
|
163
|
+
if (useRepoRoot) {
|
|
164
|
+
return "repo-root";
|
|
165
|
+
}
|
|
166
|
+
if (!fs.existsSync(targetDir) || fs.readdirSync(targetDir).length === 0) {
|
|
167
|
+
return "nested-kb";
|
|
168
|
+
}
|
|
169
|
+
return "nested-kb";
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function inferDefaultKnowledgeLayout(targetDir) {
|
|
173
|
+
const root = path.resolve(targetDir);
|
|
174
|
+
if (fs.existsSync(path.join(root, "docs"))) {
|
|
175
|
+
return "repo-root";
|
|
176
|
+
}
|
|
177
|
+
if (fs.existsSync(path.join(root, "kb", "docs"))) {
|
|
178
|
+
return "nested-kb";
|
|
179
|
+
}
|
|
180
|
+
return "repo-root";
|
|
181
|
+
}
|
|
182
|
+
|
|
152
183
|
function createReadlinePrompter({ stdin, stdout }) {
|
|
153
184
|
const rl = readline.createInterface({
|
|
154
185
|
input: stdin,
|
package/lib/repo-init.js
CHANGED
|
@@ -5,6 +5,8 @@ import { connect, initDb } from "./db.js";
|
|
|
5
5
|
import { maskConnection } from "./cli-common.js";
|
|
6
6
|
|
|
7
7
|
const PACKAGE_NAME = "@nzpr/kb";
|
|
8
|
+
const REPO_ROOT_LAYOUT = "repo-root";
|
|
9
|
+
const NESTED_KB_LAYOUT = "nested-kb";
|
|
8
10
|
const LABELS = Object.freeze([
|
|
9
11
|
{
|
|
10
12
|
name: "kb-entry",
|
|
@@ -18,14 +20,19 @@ const LABELS = Object.freeze([
|
|
|
18
20
|
}
|
|
19
21
|
]);
|
|
20
22
|
|
|
21
|
-
export function initializeKnowledgeRepo({
|
|
23
|
+
export function initializeKnowledgeRepo({
|
|
24
|
+
targetDir = process.cwd(),
|
|
25
|
+
layout = NESTED_KB_LAYOUT
|
|
26
|
+
} = {}) {
|
|
22
27
|
const root = path.resolve(targetDir);
|
|
28
|
+
const normalizedLayout = normalizeKnowledgeLayout(layout);
|
|
29
|
+
const docsRootRelative = docsRootForLayout(normalizedLayout);
|
|
23
30
|
const files = new Map([
|
|
24
31
|
[".github/ISSUE_TEMPLATE/config.yml", renderIssueConfig()],
|
|
25
|
-
[".github/ISSUE_TEMPLATE/knowledge-document.md", renderIssueTemplate()],
|
|
26
|
-
[".github/workflows/kb-issue-to-pr.yml", renderIssueToPrWorkflow()],
|
|
27
|
-
[".github/workflows/kb-publish.yml", renderPublishWorkflow()],
|
|
28
|
-
[
|
|
32
|
+
[".github/ISSUE_TEMPLATE/knowledge-document.md", renderIssueTemplate({ docsRootRelative })],
|
|
33
|
+
[".github/workflows/kb-issue-to-pr.yml", renderIssueToPrWorkflow({ docsRootRelative })],
|
|
34
|
+
[".github/workflows/kb-publish.yml", renderPublishWorkflow({ docsRootRelative })],
|
|
35
|
+
[`${docsRootRelative}/.gitkeep`, ""]
|
|
29
36
|
]);
|
|
30
37
|
|
|
31
38
|
const created = [];
|
|
@@ -44,6 +51,8 @@ export function initializeKnowledgeRepo({ targetDir = process.cwd() } = {}) {
|
|
|
44
51
|
|
|
45
52
|
return {
|
|
46
53
|
root,
|
|
54
|
+
layout: normalizedLayout,
|
|
55
|
+
docsRootRelative,
|
|
47
56
|
created,
|
|
48
57
|
skipped,
|
|
49
58
|
configuration: buildConfigurationGuide()
|
|
@@ -52,6 +61,7 @@ export function initializeKnowledgeRepo({ targetDir = process.cwd() } = {}) {
|
|
|
52
61
|
|
|
53
62
|
export async function bootstrapKnowledgeRepo({
|
|
54
63
|
targetDir = process.cwd(),
|
|
64
|
+
layout = NESTED_KB_LAYOUT,
|
|
55
65
|
repo = null,
|
|
56
66
|
githubToken = null,
|
|
57
67
|
databaseUrl = null,
|
|
@@ -64,7 +74,7 @@ export async function bootstrapKnowledgeRepo({
|
|
|
64
74
|
runGitHubCommand = defaultRunGitHubCommand,
|
|
65
75
|
verifyDatabaseReady = defaultVerifyDatabaseReady
|
|
66
76
|
} = {}) {
|
|
67
|
-
const scaffold = initializeKnowledgeRepo({ targetDir });
|
|
77
|
+
const scaffold = initializeKnowledgeRepo({ targetDir, layout });
|
|
68
78
|
const result = {
|
|
69
79
|
...scaffold,
|
|
70
80
|
ok: true,
|
|
@@ -284,7 +294,7 @@ function renderIssueConfig() {
|
|
|
284
294
|
return ["blank_issues_enabled: false", ""].join("\n");
|
|
285
295
|
}
|
|
286
296
|
|
|
287
|
-
function renderIssueTemplate() {
|
|
297
|
+
function renderIssueTemplate({ docsRootRelative }) {
|
|
288
298
|
return [
|
|
289
299
|
"---",
|
|
290
300
|
"name: Knowledge Base Document",
|
|
@@ -313,12 +323,12 @@ function renderIssueTemplate() {
|
|
|
313
323
|
"",
|
|
314
324
|
"1. Open the issue with this template.",
|
|
315
325
|
"2. Review and edit the issue until the title and text are ready.",
|
|
316
|
-
|
|
326
|
+
`3. Add the \`kb-approved\` label to generate a PR that writes the Markdown file into \`${docsRootRelative}/\`.`,
|
|
317
327
|
""
|
|
318
328
|
].join("\n");
|
|
319
329
|
}
|
|
320
330
|
|
|
321
|
-
function renderIssueToPrWorkflow() {
|
|
331
|
+
function renderIssueToPrWorkflow({ docsRootRelative }) {
|
|
322
332
|
return [
|
|
323
333
|
"name: kb-issue-to-pr",
|
|
324
334
|
"",
|
|
@@ -353,7 +363,7 @@ function renderIssueToPrWorkflow() {
|
|
|
353
363
|
"",
|
|
354
364
|
" - name: Materialize approved issue",
|
|
355
365
|
" id: materialize",
|
|
356
|
-
|
|
366
|
+
` run: kb-admin issue-to-doc --issue-event "$GITHUB_EVENT_PATH" --docs-root ./${docsRootRelative}`,
|
|
357
367
|
"",
|
|
358
368
|
" - name: Create pull request",
|
|
359
369
|
" id: cpr",
|
|
@@ -382,7 +392,7 @@ function renderIssueToPrWorkflow() {
|
|
|
382
392
|
].join("\n");
|
|
383
393
|
}
|
|
384
394
|
|
|
385
|
-
function renderPublishWorkflow() {
|
|
395
|
+
function renderPublishWorkflow({ docsRootRelative }) {
|
|
386
396
|
return [
|
|
387
397
|
"name: kb-publish",
|
|
388
398
|
"",
|
|
@@ -391,7 +401,7 @@ function renderPublishWorkflow() {
|
|
|
391
401
|
" branches:",
|
|
392
402
|
" - main",
|
|
393
403
|
" paths:",
|
|
394
|
-
|
|
404
|
+
` - "${docsRootRelative}/**"`,
|
|
395
405
|
' - ".github/workflows/**"',
|
|
396
406
|
" workflow_dispatch:",
|
|
397
407
|
"",
|
|
@@ -432,7 +442,18 @@ function renderPublishWorkflow() {
|
|
|
432
442
|
` run: npm install -g ${PACKAGE_NAME}`,
|
|
433
443
|
"",
|
|
434
444
|
" - name: Publish knowledge",
|
|
435
|
-
|
|
445
|
+
` run: kb publish --docs-root ./${docsRootRelative}`,
|
|
436
446
|
""
|
|
437
447
|
].join("\n");
|
|
438
448
|
}
|
|
449
|
+
|
|
450
|
+
function normalizeKnowledgeLayout(layout) {
|
|
451
|
+
if (layout === REPO_ROOT_LAYOUT || layout === NESTED_KB_LAYOUT) {
|
|
452
|
+
return layout;
|
|
453
|
+
}
|
|
454
|
+
throw new Error(`unsupported init-repo layout: ${layout}`);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function docsRootForLayout(layout) {
|
|
458
|
+
return layout === REPO_ROOT_LAYOUT ? "docs" : "kb/docs";
|
|
459
|
+
}
|