@msareen/knowledge-hub-builder 0.1.7 → 0.2.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/.bundle_template/sources.yaml +1 -0
- package/AGENTS.md +25 -5
- package/README.md +104 -5
- package/SPEC.md +227 -4
- package/document/faq.md +1 -0
- package/package.json +1 -1
- package/scripts/cli.ts +111 -17
- package/scripts/export.ts +6 -1
- package/scripts/hubs.ts +691 -0
- package/scripts/ingest/acquire.ts +56 -13
- package/scripts/ingest/exclude.ts +37 -0
- package/scripts/ingest/exts.ts +82 -1
- package/scripts/ingest/files.ts +15 -4
- package/scripts/ingest/folder.ts +20 -5
- package/scripts/ingest/index.ts +16 -11
- package/scripts/init.ts +40 -13
- package/scripts/lib/args.ts +45 -4
- package/scripts/lib/create.ts +39 -0
- package/scripts/lib/extract.ts +269 -28
- package/scripts/lib/log.ts +40 -0
- package/scripts/lib/registry.ts +306 -0
- package/scripts/lib/relocate.ts +165 -0
- package/scripts/lib/schema.ts +75 -0
- package/scripts/lib/upgrade.ts +181 -12
- package/scripts/lib/util.ts +7 -0
- package/scripts/lint.ts +3 -0
- package/scripts/new-bundle.ts +4 -1
- package/scripts/visualize.ts +15 -10
- package/skills/ingest/SKILL.md +76 -4
- package/templates/hub/gitignore +4 -3
package/scripts/cli.ts
CHANGED
|
@@ -3,16 +3,47 @@
|
|
|
3
3
|
// so nothing that resolves a hub may be imported at module scope.
|
|
4
4
|
import { version, findHub, markerIn, MARKER } from "./lib/paths";
|
|
5
5
|
|
|
6
|
-
const COMMANDS: Record<string, { load: () => Promise<unknown>;
|
|
7
|
-
init: { load: () => import("./init"),
|
|
8
|
-
upgrade: { load: () => import("./init"),
|
|
9
|
-
"new-bundle": { load: () => import("./new-bundle"),
|
|
10
|
-
ingest: {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
const COMMANDS: Record<string, { load: () => Promise<unknown>; usage: string; desc: string }> = {
|
|
7
|
+
init: { load: () => import("./init"), usage: 'khb init [dir] [--name N] [--description "…"]', desc: "create a hub here (or in dir)" },
|
|
8
|
+
upgrade: { load: () => import("./init"), usage: "khb upgrade", desc: "refresh this hub's contract docs" },
|
|
9
|
+
"new-bundle": { load: () => import("./new-bundle"), usage: 'khb new-bundle <name> ["scope"]', desc: "scaffold a bundle + register it" },
|
|
10
|
+
ingest: {
|
|
11
|
+
load: () => import("./ingest/index"),
|
|
12
|
+
usage: "khb ingest [bundle] [--force] [--skip-ocr] [--skip-audio]",
|
|
13
|
+
desc: "acquire + extract declared sources → raw/",
|
|
14
|
+
},
|
|
15
|
+
lint: { load: () => import("./lint"), usage: "khb lint", desc: "validate the hub against skills/lint/SKILL.md" },
|
|
16
|
+
visualize: {
|
|
17
|
+
load: () => import("./visualize"),
|
|
18
|
+
usage: "khb visualize [--port N] [--no-open]",
|
|
19
|
+
desc: "serve the live bundle graph in your browser; aliases: vis, viz",
|
|
20
|
+
},
|
|
21
|
+
export: { load: () => import("./export"), usage: "khb export <bundle> [dest]", desc: "standalone copy of one bundle" },
|
|
22
|
+
list: { load: () => import("./hubs"), usage: "khb list [--json]", desc: "every hub on this machine" },
|
|
23
|
+
go: {
|
|
24
|
+
load: () => import("./hubs"),
|
|
25
|
+
usage: "khb go [name|N] [--path] [--no-agent] [--agent X]",
|
|
26
|
+
desc: "open a hub with your agent (bare 'khb' picks one)",
|
|
27
|
+
},
|
|
28
|
+
agent: {
|
|
29
|
+
load: () => import("./hubs"),
|
|
30
|
+
usage: 'khb agent [name|none] [--command X] [--args "…"]',
|
|
31
|
+
desc: "which agent 'khb go' launches",
|
|
32
|
+
},
|
|
33
|
+
update: {
|
|
34
|
+
load: () => import("./hubs"),
|
|
35
|
+
usage: "khb update [new-path] [--path|-p] [--schema|-s] [--from <old>] [--dry-run]",
|
|
36
|
+
desc: "repair a moved hub's paths, and/or backfill sources.yaml",
|
|
37
|
+
},
|
|
38
|
+
forget: { load: () => import("./hubs"), usage: "khb forget <name|path>", desc: "drop a hub from the list (folder untouched)" },
|
|
14
39
|
};
|
|
15
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Commands that work *outside* a hub, against ~/.khb/hubs-config.json. They must not
|
|
43
|
+
* resolve or upgrade a hub — their whole job is running before you are in one.
|
|
44
|
+
*/
|
|
45
|
+
const REGISTRY_COMMANDS = new Set(["list", "go", "agent", "forget", "update"]);
|
|
46
|
+
|
|
16
47
|
// Short forms that just resolve to a canonical command above — kept out of COMMANDS
|
|
17
48
|
// itself so help text lists each command once. `-v` is taken by --version, so
|
|
18
49
|
// `visualize` gets word-shaped aliases instead of a letter one.
|
|
@@ -34,15 +65,56 @@ if (hubAt >= 0) {
|
|
|
34
65
|
}
|
|
35
66
|
|
|
36
67
|
const cmd0 = argv.shift();
|
|
37
|
-
|
|
68
|
+
// Bare `khb` is the way in from a cold terminal: pick a hub and open it. Help stays one
|
|
69
|
+
// word away, and is what you get when no hub has ever been registered.
|
|
70
|
+
const cmd = !cmd0 ? "go" : (ALIASES[cmd0] ?? cmd0);
|
|
71
|
+
|
|
72
|
+
if (cmd === "help" || cmd === "--help" || cmd === "-h") {
|
|
73
|
+
// Descriptions align to the longest usage — but only among those that fit. One long
|
|
74
|
+
// entry should not push every other description off an 80-column terminal: past the
|
|
75
|
+
// cap, that entry gets its own line for the usage and an indented line for the
|
|
76
|
+
// description, instead of dragging the whole column wide.
|
|
77
|
+
const CAP = 60;
|
|
78
|
+
const usages = Object.values(COMMANDS).map((c) => c.usage.length);
|
|
79
|
+
const width = Math.max(...usages.filter((n) => n <= CAP));
|
|
80
|
+
const printSection = (title: string, names: string[]) => {
|
|
81
|
+
console.log(title);
|
|
82
|
+
for (const name of names) {
|
|
83
|
+
const c = COMMANDS[name];
|
|
84
|
+
if (c.usage.length > width) {
|
|
85
|
+
console.log(` ${c.usage}`);
|
|
86
|
+
console.log(` ${" ".repeat(width)} ${c.desc}`);
|
|
87
|
+
} else {
|
|
88
|
+
console.log(` ${c.usage.padEnd(width)} ${c.desc}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
};
|
|
38
92
|
|
|
39
|
-
|
|
40
|
-
console.log(
|
|
93
|
+
console.log(`khb ${version()} — Knowledge Hub Builder`);
|
|
94
|
+
console.log();
|
|
41
95
|
console.log(`khb is the supporting tool: it handles deterministic extraction, file plumbing,`);
|
|
42
96
|
console.log(`validation, and export. Your AI agent — Claude, Codex, Gemini, or another`);
|
|
43
|
-
console.log(`compatible agent — follows the workflow skills and orchestrates the knowledge work
|
|
44
|
-
|
|
45
|
-
|
|
97
|
+
console.log(`compatible agent — follows the workflow skills and orchestrates the knowledge work.`);
|
|
98
|
+
console.log();
|
|
99
|
+
|
|
100
|
+
printSection(
|
|
101
|
+
"In a hub:",
|
|
102
|
+
Object.keys(COMMANDS).filter((name) => !REGISTRY_COMMANDS.has(name)),
|
|
103
|
+
);
|
|
104
|
+
console.log();
|
|
105
|
+
printSection(
|
|
106
|
+
"Anywhere on this machine (no hub needed) — config and maintenance:",
|
|
107
|
+
Object.keys(COMMANDS).filter((name) => REGISTRY_COMMANDS.has(name)),
|
|
108
|
+
);
|
|
109
|
+
console.log();
|
|
110
|
+
|
|
111
|
+
console.log(`Global: --hub <dir> operate on that hub instead of searching upward from cwd`);
|
|
112
|
+
console.log(` help | --help | -h this help --version | -v version`);
|
|
113
|
+
console.log(`Env: KHB_HUB same as --hub`);
|
|
114
|
+
console.log(` KHB_HOME where the hub list lives (default ~/.khb)`);
|
|
115
|
+
console.log(` KHB_NO_AUTO_UPGRADE don't refresh a hub's contract docs on version drift`);
|
|
116
|
+
console.log(`Exit: 0 on success, 1 on a usage error or a failure. An unknown option is an error;`);
|
|
117
|
+
console.log(` a source khb cannot extract is not — it becomes a pending row in log.md.`);
|
|
46
118
|
console.log(`Docs: https://github.com/msareen/knowledge-hub-builder`);
|
|
47
119
|
process.exit(0);
|
|
48
120
|
}
|
|
@@ -63,11 +135,31 @@ if (!entry) {
|
|
|
63
135
|
// stamped at an older version than the installed khb is stating an older contract than
|
|
64
136
|
// the one the CLI now implements. Rather than let the two disagree, refresh the hub in
|
|
65
137
|
// place before running the command — `khb upgrade` touches nothing the user wrote.
|
|
66
|
-
// `init` has no hub yet, `upgrade` does this itself,
|
|
67
|
-
|
|
138
|
+
// `init` has no hub yet, `upgrade` does this itself, the registry commands run outside
|
|
139
|
+
// any hub, and $KHB_NO_AUTO_UPGRADE opts out.
|
|
140
|
+
if (cmd !== "init" && cmd !== "upgrade" && !REGISTRY_COMMANDS.has(cmd)) {
|
|
68
141
|
const hub = findHub();
|
|
69
142
|
if (hub) {
|
|
70
|
-
|
|
143
|
+
// Any command run in a hub is proof the hub exists and is in use — record it, so the
|
|
144
|
+
// machine registry fills itself in without a migration or a `khb register` to recall.
|
|
145
|
+
const { registerHub, touchHub } = await import("./lib/registry");
|
|
146
|
+
registerHub(hub);
|
|
147
|
+
touchHub(hub);
|
|
148
|
+
|
|
149
|
+
// …and proof of where it is. The hub records its own location in its marker, so a move
|
|
150
|
+
// is noticed by the hub itself rather than inferred from a registry that may have been
|
|
151
|
+
// deleted or never have seen this machine. Must run before the drift check below:
|
|
152
|
+
// that restamps the marker, and would overwrite the old location before anyone read it.
|
|
153
|
+
const { recordLocation } = await import("./lib/upgrade");
|
|
154
|
+
const { moved } = recordLocation(hub);
|
|
155
|
+
if (moved) {
|
|
156
|
+
console.error(`khb: this hub was at ${moved} and is now at ${hub}.`);
|
|
157
|
+
console.error(`khb: absolute paths recorded inside it still name the old location.`);
|
|
158
|
+
console.error(`khb: repair them: khb update --path (--dry-run to preview)`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (hub && !process.env.KHB_NO_AUTO_UPGRADE) {
|
|
162
|
+
const { hubVersion, upgradeHub, updateHint } = await import("./lib/upgrade");
|
|
71
163
|
// A marker under a pre-rename name is drift too, even at a matching version.
|
|
72
164
|
if (hubVersion(hub) !== version() || markerIn(hub) !== MARKER) {
|
|
73
165
|
const { from, to, pruned, renamed } = upgradeHub(hub);
|
|
@@ -77,6 +169,8 @@ if (cmd !== "init" && cmd !== "upgrade" && !process.env.KHB_NO_AUTO_UPGRADE) {
|
|
|
77
169
|
);
|
|
78
170
|
if (renamed) console.error(`khb: renamed ${renamed} -> khb.json`);
|
|
79
171
|
if (pruned.length) console.error(`khb: removed (no longer part of the contract): ${pruned.join(", ")}`);
|
|
172
|
+
const hint = updateHint(hub);
|
|
173
|
+
if (hint) console.error(hint);
|
|
80
174
|
}
|
|
81
175
|
}
|
|
82
176
|
}
|
package/scripts/export.ts
CHANGED
|
@@ -4,8 +4,13 @@
|
|
|
4
4
|
import { cpSync, writeFileSync, mkdirSync, existsSync, readFileSync } from "node:fs";
|
|
5
5
|
import { HUB, bundleDir, join } from "./lib/util";
|
|
6
6
|
import { detail, totalElapsed } from "./lib/log";
|
|
7
|
+
import { rejectUnknownFlags } from "./lib/args";
|
|
7
8
|
|
|
8
|
-
const
|
|
9
|
+
const argv = process.argv.slice(2);
|
|
10
|
+
// Before reading positionals: an unrecognized flag would otherwise become the destination,
|
|
11
|
+
// and `khb export mybundle --force` would export into a folder named `--force`.
|
|
12
|
+
rejectUnknownFlags(argv, "khb export <bundle> [dest]");
|
|
13
|
+
const [name, destArg] = argv;
|
|
9
14
|
if (!name) { console.error("Usage: khb export <bundle> [dest]"); process.exit(1); }
|
|
10
15
|
|
|
11
16
|
const src = bundleDir(name);
|