@nzpr/kb 0.1.1 → 0.1.2

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
@@ -23,6 +23,7 @@ The public surface of `@nzpr/kb` is the `kb` CLI.
23
23
 
24
24
  - `kb init-repo`
25
25
  Initialize a knowledge base workspace.
26
+ Use `kb init-repo --interactive` for a guided setup flow.
26
27
 
27
28
  - `kb create --title TEXT --text TEXT`
28
29
  Propose a new knowledge document or a substantial update.
@@ -95,6 +96,12 @@ Bootstrap that repo with:
95
96
  kb init-repo --dir /path/to/knowledge-repo
96
97
  ```
97
98
 
99
+ If you want the CLI to walk you through setup and tell you what to do next:
100
+
101
+ ```bash
102
+ kb init-repo --interactive
103
+ ```
104
+
98
105
  Or bootstrap and configure it in one step:
99
106
 
100
107
  ```bash
@@ -122,6 +129,14 @@ Inside that knowledge repo, the normal flow is:
122
129
  - database preflight and schema initialization
123
130
  - GitHub repo labels, variables, and secrets
124
131
 
132
+ In interactive mode it also:
133
+
134
+ - asks which repo and directory you want to initialize
135
+ - asks whether to wire GitHub setup now
136
+ - asks whether to verify the database now
137
+ - collects embedding settings if you want remote embeddings
138
+ - prints the next actions after initialization
139
+
125
140
  If one remote step fails, the scaffold still stays in place and the command tells you what to rerun.
126
141
 
127
142
  ## Runtime
package/lib/cli-common.js CHANGED
@@ -8,7 +8,7 @@ export function parseFlags(args) {
8
8
  continue;
9
9
  }
10
10
  const key = arg.slice(2);
11
- if (key === "json" || key === "include-drafts") {
11
+ if (key === "json" || key === "include-drafts" || key === "interactive") {
12
12
  flags[key] = true;
13
13
  continue;
14
14
  }
package/lib/cli.js CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  } from "./config.js";
10
10
  import { connect, initDb } from "./db.js";
11
11
  import { ingestDocuments } from "./index.js";
12
+ import { collectInitRepoInteractiveOptions } from "./init-repo-interactive.js";
12
13
  import { createGitHubIssueFromText } from "./kb-proposals.js";
13
14
  import { bootstrapKnowledgeRepo } from "./repo-init.js";
14
15
  import { askIndex, doctor, knowledgeCatalog, listDocuments, searchIndex, snippet } from "./search.js";
@@ -60,19 +61,32 @@ export async function main(argv) {
60
61
  return 0;
61
62
  }
62
63
  case "init-repo": {
64
+ const initRepoOptions = flags.interactive
65
+ ? await collectInitRepoInteractiveOptions({ flags })
66
+ : {
67
+ targetDir: flags.dir ?? process.cwd(),
68
+ repo: flags.repo ?? resolveGitHubRepository(),
69
+ databaseUrl: flags["database-url"] ?? process.env.KB_DATABASE_URL ?? null,
70
+ embeddingMode: flags["embedding-mode"] ?? process.env.KB_EMBEDDING_MODE ?? null,
71
+ embeddingApiUrl: flags["embedding-api-url"] ?? process.env.KB_EMBEDDING_API_URL ?? null,
72
+ embeddingModel: flags["embedding-model"] ?? process.env.KB_EMBEDDING_MODEL ?? null,
73
+ embeddingApiKey: flags["embedding-api-key"] ?? process.env.KB_EMBEDDING_API_KEY ?? null,
74
+ dbConnectTimeoutMs:
75
+ flags["db-connect-timeout-ms"] ?? process.env.KB_DB_CONNECT_TIMEOUT_MS ?? null,
76
+ repoAutomationToken:
77
+ flags["repo-automation-token"] ?? process.env.KB_REPO_AUTOMATION_TOKEN ?? null
78
+ };
63
79
  const result = await bootstrapKnowledgeRepo({
64
- targetDir: flags.dir ?? process.cwd(),
65
- repo: flags.repo ?? resolveGitHubRepository(),
80
+ targetDir: initRepoOptions.targetDir,
81
+ repo: initRepoOptions.repo,
66
82
  githubToken: process.env.GITHUB_TOKEN ?? null,
67
- databaseUrl: flags["database-url"] ?? process.env.KB_DATABASE_URL ?? null,
68
- embeddingMode: flags["embedding-mode"] ?? process.env.KB_EMBEDDING_MODE ?? null,
69
- embeddingApiUrl: flags["embedding-api-url"] ?? process.env.KB_EMBEDDING_API_URL ?? null,
70
- embeddingModel: flags["embedding-model"] ?? process.env.KB_EMBEDDING_MODEL ?? null,
71
- embeddingApiKey: flags["embedding-api-key"] ?? process.env.KB_EMBEDDING_API_KEY ?? null,
72
- dbConnectTimeoutMs:
73
- flags["db-connect-timeout-ms"] ?? process.env.KB_DB_CONNECT_TIMEOUT_MS ?? null,
74
- repoAutomationToken:
75
- flags["repo-automation-token"] ?? process.env.KB_REPO_AUTOMATION_TOKEN ?? null
83
+ databaseUrl: initRepoOptions.databaseUrl,
84
+ embeddingMode: initRepoOptions.embeddingMode,
85
+ embeddingApiUrl: initRepoOptions.embeddingApiUrl,
86
+ embeddingModel: initRepoOptions.embeddingModel,
87
+ embeddingApiKey: initRepoOptions.embeddingApiKey,
88
+ dbConnectTimeoutMs: initRepoOptions.dbConnectTimeoutMs,
89
+ repoAutomationToken: initRepoOptions.repoAutomationToken
76
90
  });
77
91
  console.log(`initialized knowledge repo scaffold in ${result.root}`);
78
92
  for (const relativePath of result.created) {
@@ -87,6 +101,7 @@ export async function main(argv) {
87
101
  printInitRepoStatus(result);
88
102
  printRepoConfiguration(result.configuration);
89
103
  printInitRepoNextStep(result);
104
+ printInitRepoChecklist(result);
90
105
  return result.ok ? 0 : 1;
91
106
  }
92
107
  case "search": {
@@ -231,7 +246,7 @@ function printHelp() {
231
246
  console.log(`usage: kb <command> [options]
232
247
 
233
248
  commands:
234
- init-repo [--dir PATH] [--repo OWNER/REPO]
249
+ init-repo [--interactive] [--dir PATH] [--repo OWNER/REPO]
235
250
  create --title TEXT --text TEXT [--path RELATIVE_PATH]
236
251
  search <query>
237
252
  ask <question>
@@ -256,7 +271,7 @@ search options:
256
271
  function printCommandHelp(command) {
257
272
  const commandHelp = {
258
273
  "init-repo":
259
- "usage: kb init-repo [--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.",
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.",
260
275
  create: `usage: kb create --title TEXT --text TEXT [--path RELATIVE_PATH] [--repo OWNER/REPO]\n\n${githubCreationHelp()}`,
261
276
  search: `usage: kb search <query> [options]\n\n${databaseHelp()}\n --limit N`,
262
277
  ask: `usage: kb ask <question> [options]\n\n${databaseHelp()}\n --limit N`,
@@ -389,3 +404,19 @@ function printInitRepoNextStep(result) {
389
404
  "next: rerun kb init-repo with the missing repo or database inputs when you are ready, or commit the scaffold now and finish remote setup later"
390
405
  );
391
406
  }
407
+
408
+ function printInitRepoChecklist(result) {
409
+ console.log("");
410
+ console.log("what to do next:");
411
+ if (result.created.length || result.skipped.length) {
412
+ console.log(" 1. commit and push the scaffolded knowledge repo files");
413
+ }
414
+ if (result.github.status !== "configured") {
415
+ console.log(" 2. make sure the target repo exists and rerun with --repo once GitHub setup is ready");
416
+ }
417
+ if (result.database.status !== "verified") {
418
+ console.log(" 3. rerun with --database-url once the target database is ready");
419
+ }
420
+ console.log(" 4. use kb create to open a proposal issue for the first knowledge entry");
421
+ console.log(" 5. review the issue, add kb-approved, merge the generated PR, and let publish sync the live KB");
422
+ }
@@ -0,0 +1,222 @@
1
+ import path from "node:path";
2
+ import readline from "node:readline/promises";
3
+ import { stdin as defaultStdin, stdout as defaultStdout } from "node:process";
4
+ import { resolveGitHubRepository } from "./config.js";
5
+
6
+ export async function collectInitRepoInteractiveOptions({
7
+ flags,
8
+ env = process.env,
9
+ cwd = process.cwd(),
10
+ stdin = defaultStdin,
11
+ stdout = defaultStdout,
12
+ prompter = null
13
+ }) {
14
+ if (!prompter && (!stdin.isTTY || !stdout.isTTY)) {
15
+ throw new Error("interactive init-repo requires a TTY; rerun in a terminal or pass flags directly");
16
+ }
17
+
18
+ const prompt = prompter ?? createReadlinePrompter({ stdin, stdout });
19
+
20
+ try {
21
+ const defaults = buildInitRepoDefaults({ flags, env, cwd });
22
+
23
+ stdout.write("interactive kb init-repo\n");
24
+ stdout.write("this wizard scaffolds the knowledge repo and can also wire remote setup now.\n\n");
25
+
26
+ const targetDir = path.resolve(
27
+ cwd,
28
+ await prompt.askText("knowledge repo directory", defaults.targetDir, {
29
+ validate: requireValue
30
+ })
31
+ );
32
+
33
+ const configureRepo = await prompt.askConfirm(
34
+ "configure the GitHub repo now",
35
+ Boolean(defaults.repo || env.GITHUB_TOKEN)
36
+ );
37
+
38
+ let repo = null;
39
+ if (configureRepo) {
40
+ repo = await prompt.askText("target GitHub repo (OWNER/REPO)", defaults.repo, {
41
+ validate: requireValue
42
+ });
43
+ }
44
+
45
+ const verifyDatabase = await prompt.askConfirm(
46
+ "verify the database and initialize schema now",
47
+ Boolean(defaults.databaseUrl)
48
+ );
49
+
50
+ let databaseUrl = null;
51
+ if (verifyDatabase) {
52
+ databaseUrl = await prompt.askText("database URL", defaults.databaseUrl, {
53
+ validate: requireValue
54
+ });
55
+ }
56
+
57
+ let embeddingMode = null;
58
+ let embeddingApiUrl = null;
59
+ let embeddingModel = null;
60
+ let embeddingApiKey = null;
61
+ let dbConnectTimeoutMs = null;
62
+
63
+ if (verifyDatabase) {
64
+ const useRemoteEmbeddings = await prompt.askConfirm(
65
+ "configure remote embeddings now",
66
+ defaults.embeddingMode === "bge-m3-openai"
67
+ );
68
+
69
+ if (useRemoteEmbeddings) {
70
+ embeddingMode = "bge-m3-openai";
71
+ embeddingApiUrl = await prompt.askText(
72
+ "embedding API URL",
73
+ defaults.embeddingApiUrl || "https://your-embeddings-host/v1/embeddings",
74
+ { validate: requireValue }
75
+ );
76
+ embeddingModel = await prompt.askText(
77
+ "embedding model",
78
+ defaults.embeddingModel || "BAAI/bge-m3",
79
+ { validate: requireValue }
80
+ );
81
+ embeddingApiKey = await prompt.askText(
82
+ "embedding API key (optional)",
83
+ defaults.embeddingApiKey
84
+ );
85
+ dbConnectTimeoutMs = await prompt.askText(
86
+ "database connect timeout ms",
87
+ defaults.dbConnectTimeoutMs || "20000",
88
+ { validate: requirePositiveNumber }
89
+ );
90
+ }
91
+ }
92
+
93
+ let repoAutomationToken = null;
94
+ if (configureRepo) {
95
+ const useAutomationToken = await prompt.askConfirm(
96
+ "set a dedicated repo automation token secret",
97
+ Boolean(defaults.repoAutomationToken)
98
+ );
99
+ if (useAutomationToken) {
100
+ repoAutomationToken = await prompt.askText(
101
+ "repo automation token",
102
+ defaults.repoAutomationToken
103
+ );
104
+ }
105
+ }
106
+
107
+ stdout.write("\nplan:\n");
108
+ stdout.write(` local scaffold: ${targetDir}\n`);
109
+ stdout.write(` github repo setup: ${repo ? repo : "skip for now"}\n`);
110
+ stdout.write(` database preflight: ${databaseUrl ? "yes" : "skip for now"}\n`);
111
+ stdout.write(
112
+ ` embeddings config: ${embeddingMode === "bge-m3-openai" ? `${embeddingMode}/${embeddingModel}` : "skip for now"}\n`
113
+ );
114
+
115
+ const proceed = await prompt.askConfirm("run init-repo with this plan", true);
116
+ if (!proceed) {
117
+ throw new Error("interactive init-repo cancelled");
118
+ }
119
+
120
+ return {
121
+ targetDir,
122
+ repo,
123
+ databaseUrl,
124
+ embeddingMode,
125
+ embeddingApiUrl,
126
+ embeddingModel,
127
+ embeddingApiKey: emptyToNull(embeddingApiKey),
128
+ dbConnectTimeoutMs: emptyToNull(dbConnectTimeoutMs),
129
+ repoAutomationToken: emptyToNull(repoAutomationToken)
130
+ };
131
+ } finally {
132
+ await prompt.close();
133
+ }
134
+ }
135
+
136
+ export function buildInitRepoDefaults({ flags = {}, env = process.env, cwd = process.cwd() }) {
137
+ return {
138
+ targetDir: flags.dir ?? cwd,
139
+ repo: flags.repo ?? resolveGitHubRepository(env) ?? "",
140
+ databaseUrl: flags["database-url"] ?? env.KB_DATABASE_URL ?? "",
141
+ embeddingMode: flags["embedding-mode"] ?? env.KB_EMBEDDING_MODE ?? "",
142
+ embeddingApiUrl: flags["embedding-api-url"] ?? env.KB_EMBEDDING_API_URL ?? "",
143
+ embeddingModel: flags["embedding-model"] ?? env.KB_EMBEDDING_MODEL ?? "",
144
+ embeddingApiKey: flags["embedding-api-key"] ?? env.KB_EMBEDDING_API_KEY ?? "",
145
+ dbConnectTimeoutMs:
146
+ flags["db-connect-timeout-ms"] ?? env.KB_DB_CONNECT_TIMEOUT_MS ?? "",
147
+ repoAutomationToken:
148
+ flags["repo-automation-token"] ?? env.KB_REPO_AUTOMATION_TOKEN ?? ""
149
+ };
150
+ }
151
+
152
+ function createReadlinePrompter({ stdin, stdout }) {
153
+ const rl = readline.createInterface({
154
+ input: stdin,
155
+ output: stdout
156
+ });
157
+ return {
158
+ askText(label, defaultValue = "", options = {}) {
159
+ return askText(rl, label, defaultValue, options);
160
+ },
161
+ askConfirm(label, defaultValue = false) {
162
+ return askConfirm(rl, label, defaultValue);
163
+ },
164
+ async close() {
165
+ rl.close();
166
+ }
167
+ };
168
+ }
169
+
170
+ async function askText(rl, label, defaultValue = "", { validate = null } = {}) {
171
+ while (true) {
172
+ const suffix = defaultValue ? ` [${defaultValue}]` : "";
173
+ const answer = (await rl.question(`${label}${suffix}: `)).trim();
174
+ const value = answer || String(defaultValue ?? "").trim();
175
+
176
+ if (!validate) {
177
+ return value;
178
+ }
179
+ const error = validate(value);
180
+ if (!error) {
181
+ return value;
182
+ }
183
+ rl.output.write(`${error}\n`);
184
+ }
185
+ }
186
+
187
+ async function askConfirm(rl, label, defaultValue = false) {
188
+ const hint = defaultValue ? "Y/n" : "y/N";
189
+ while (true) {
190
+ const answer = (await rl.question(`${label}? [${hint}]: `)).trim().toLowerCase();
191
+ if (!answer) {
192
+ return defaultValue;
193
+ }
194
+ if (["y", "yes"].includes(answer)) {
195
+ return true;
196
+ }
197
+ if (["n", "no"].includes(answer)) {
198
+ return false;
199
+ }
200
+ rl.output.write("please answer yes or no\n");
201
+ }
202
+ }
203
+
204
+ function requireValue(value) {
205
+ return String(value ?? "").trim() ? null : "a value is required";
206
+ }
207
+
208
+ function requirePositiveNumber(value) {
209
+ if (!String(value ?? "").trim()) {
210
+ return "a value is required";
211
+ }
212
+ const number = Number(value);
213
+ if (!Number.isFinite(number) || number <= 0) {
214
+ return "enter a positive number";
215
+ }
216
+ return null;
217
+ }
218
+
219
+ function emptyToNull(value) {
220
+ const normalized = String(value ?? "").trim();
221
+ return normalized ? normalized : null;
222
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nzpr/kb",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Knowledge base CLI for proposing, publishing, and querying curated agent knowledge.",
5
5
  "repository": {
6
6
  "type": "git",