@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 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 `kb/docs/`.
124
- 5. After merge, that repo's CI runs `kb publish --docs-root ./kb/docs`.
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 ./kb/docs
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 `kb/docs/` into the vector database:
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 ./kb/docs
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(" 1. commit and push the scaffolded knowledge repo files");
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({ targetDir = process.cwd() } = {}) {
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
- ["kb/docs/.gitkeep", ""]
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
- "3. Add the `kb-approved` label to generate a PR that writes the Markdown file into `kb/docs/`.",
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
- ' run: kb-admin issue-to-doc --issue-event "$GITHUB_EVENT_PATH" --docs-root ./kb/docs',
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
- ' - "kb/docs/**"',
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
- " run: kb publish --docs-root ./kb/docs",
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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nzpr/kb",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Knowledge base CLI for proposing, publishing, and querying curated agent knowledge.",
5
5
  "repository": {
6
6
  "type": "git",