sdtk-brain-kit 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/README.md +66 -0
- package/assets/atlas/doc_atlas_viewer_template.html +4868 -0
- package/assets/atlas/vendor/mermaid.min.js +2029 -0
- package/bin/sdtk-brain.js +88 -0
- package/package.json +28 -0
- package/scripts/brain-smoke.test.js +19 -0
- package/scripts/sync-shared-assets.js +24 -0
- package/src/commands/enrich.js +55 -0
- package/src/commands/init.js +108 -0
- package/src/commands/lint.js +50 -0
- package/src/commands/open.js +59 -0
- package/src/commands/operations.js +357 -0
- package/src/commands/search.js +94 -0
- package/src/commands/status.js +56 -0
- package/src/lib/args.js +76 -0
- package/src/lib/browser-open.js +32 -0
- package/src/lib/errors.js +29 -0
- package/src/lib/wiki-build.js +1101 -0
- package/src/lib/wiki-compile.js +2108 -0
- package/src/lib/wiki-discover.js +271 -0
- package/src/lib/wiki-enrich.js +264 -0
- package/src/lib/wiki-extract.js +1313 -0
- package/src/lib/wiki-flags.js +97 -0
- package/src/lib/wiki-ingest.js +198 -0
- package/src/lib/wiki-lint.js +930 -0
- package/src/lib/wiki-paths.js +256 -0
- package/src/lib/wiki-score.js +64 -0
- package/src/lib/wiki-search.js +213 -0
- package/templates/VAULT_CLAUDE.md +39 -0
- package/templates/VAULT_README.md +18 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
// `sdtk-brain` — standalone second-brain vault CLI (BK-319).
|
|
5
|
+
// A vault is a user-chosen folder: raw/ (immutable sources) -> wiki/ (compiled
|
|
6
|
+
// knowledge), .brain/ (machine state), workspace/ (scratch). The agent reading
|
|
7
|
+
// the vault per its CLAUDE.md contract is the knowledge engine — there is
|
|
8
|
+
// deliberately no `ask` verb, no LLM, no network, no SDTK skill dependency.
|
|
9
|
+
|
|
10
|
+
function helpText() {
|
|
11
|
+
return [
|
|
12
|
+
"sdtk-brain — local-first second-brain vault CLI",
|
|
13
|
+
"",
|
|
14
|
+
"Usage:",
|
|
15
|
+
" sdtk-brain init [--vault <path>] Scaffold a vault (raw/ + wiki/ + workspace/ + CLAUDE.md)",
|
|
16
|
+
" sdtk-brain ingest <file|dir> Semantic extraction over raw sources (report-first)",
|
|
17
|
+
" sdtk-brain compile --mode safe [--apply] Compile extraction into wiki/ knowledge pages",
|
|
18
|
+
" sdtk-brain search [--json] \"<query>\" Deterministic search over the vault wiki",
|
|
19
|
+
" sdtk-brain lint Report-first wiki hygiene (orphans, links, stale)",
|
|
20
|
+
" sdtk-brain maintain --mode safe Lint + discover + compile preview in one pass",
|
|
21
|
+
" sdtk-brain discover --plan Gap-analysis plan from wiki evidence",
|
|
22
|
+
" sdtk-brain enrich --source github --mode review Review-only external repo metadata report",
|
|
23
|
+
" sdtk-brain open [--no-open] Build + open the docs/graph viewer over the vault",
|
|
24
|
+
" sdtk-brain status Vault status",
|
|
25
|
+
" sdtk-brain --help | --version",
|
|
26
|
+
"",
|
|
27
|
+
"The vault's own CLAUDE.md is the agent operating contract: open the vault",
|
|
28
|
+
"in your agent (or Obsidian) and ask questions there — the agent reads the",
|
|
29
|
+
"wiki and synthesizes; this CLI only provides the deterministic rails.",
|
|
30
|
+
"",
|
|
31
|
+
"Everything is local: no LLM, no network, no telemetry, no entitlement.",
|
|
32
|
+
].join("\n");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function main(argv) {
|
|
36
|
+
const command = argv[0];
|
|
37
|
+
|
|
38
|
+
if (!command || command === "--help" || command === "-h" || command === "help") {
|
|
39
|
+
console.log(helpText());
|
|
40
|
+
return 0;
|
|
41
|
+
}
|
|
42
|
+
if (command === "--version" || command === "-v" || command === "version") {
|
|
43
|
+
// eslint-disable-next-line global-require
|
|
44
|
+
console.log(`sdtk-brain-kit ${require("../package.json").version}`);
|
|
45
|
+
return 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* eslint-disable global-require */
|
|
49
|
+
try {
|
|
50
|
+
if (command === "init") {
|
|
51
|
+
return require("../src/commands/init").cmdInit(argv.slice(1));
|
|
52
|
+
}
|
|
53
|
+
if (command === "status") {
|
|
54
|
+
return require("../src/commands/status").cmdStatus(argv.slice(1));
|
|
55
|
+
}
|
|
56
|
+
if (command === "open") {
|
|
57
|
+
require("../src/commands/open").cmdOpen(argv.slice(1)).then((code) => {
|
|
58
|
+
process.exitCode = code;
|
|
59
|
+
}).catch((err) => {
|
|
60
|
+
console.error(`sdtk-brain open: ${err.message}`);
|
|
61
|
+
process.exitCode = typeof err.exitCode === "number" ? err.exitCode : 4;
|
|
62
|
+
});
|
|
63
|
+
return 0;
|
|
64
|
+
}
|
|
65
|
+
if (command === "search") {
|
|
66
|
+
return require("../src/commands/search").cmdSearch(argv.slice(1));
|
|
67
|
+
}
|
|
68
|
+
if (command === "enrich") {
|
|
69
|
+
return require("../src/commands/enrich").cmdEnrich(argv.slice(1));
|
|
70
|
+
}
|
|
71
|
+
const ops = require("../src/commands/operations");
|
|
72
|
+
if (command === "ingest") return ops.cmdIngest(argv.slice(1));
|
|
73
|
+
if (command === "compile") return ops.cmdCompile(argv.slice(1));
|
|
74
|
+
if (command === "lint") return require("../src/commands/lint").cmdLint(argv.slice(1));
|
|
75
|
+
if (command === "maintain") return ops.cmdMaintain(argv.slice(1));
|
|
76
|
+
if (command === "discover") return ops.cmdDiscover(argv.slice(1));
|
|
77
|
+
} catch (err) {
|
|
78
|
+
console.error(`sdtk-brain: ${err.message}`);
|
|
79
|
+
return typeof err.exitCode === "number" ? err.exitCode : 4;
|
|
80
|
+
}
|
|
81
|
+
/* eslint-enable global-require */
|
|
82
|
+
|
|
83
|
+
console.error(`sdtk-brain: unknown command '${command}'.`);
|
|
84
|
+
console.error("Run `sdtk-brain --help` for usage.");
|
|
85
|
+
return 2;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
process.exitCode = main(process.argv.slice(2));
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sdtk-brain-kit",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Standalone local-first second-brain vault CLI: immutable raw/ sources compiled into a markdown wiki/ knowledge layer, maintained by rails (ingest, compile, lint, search, graph viewer) while your agent is the knowledge engine.",
|
|
5
|
+
"type": "commonjs",
|
|
6
|
+
"bin": {
|
|
7
|
+
"sdtk-brain": "bin/sdtk-brain.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"src/",
|
|
12
|
+
"assets/",
|
|
13
|
+
"templates/",
|
|
14
|
+
"scripts/",
|
|
15
|
+
"README.md"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"sync:shared": "node scripts/sync-shared-assets.js",
|
|
19
|
+
"test": "node scripts/brain-smoke.test.js"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=18"
|
|
23
|
+
},
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
// Offline smoke: init -> ingest -> compile --apply -> search on a temp vault.
|
|
4
|
+
const { execFileSync } = require("child_process");
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const os = require("os");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const BIN = path.resolve(__dirname, "..", "bin", "sdtk-brain.js");
|
|
9
|
+
const vault = fs.mkdtempSync(path.join(os.tmpdir(), "brain-smoke-"));
|
|
10
|
+
const run = (...args) => execFileSync(process.execPath, [BIN, ...args], { cwd: vault, encoding: "utf8" });
|
|
11
|
+
run("init");
|
|
12
|
+
fs.writeFileSync(path.join(vault, "raw", "inbox", "note.md"),
|
|
13
|
+
"# Note\n\nsmoke-token here.\n\nhttps://github.com/example/smoke-tool\n");
|
|
14
|
+
run("ingest", path.join(vault, "raw", "inbox"));
|
|
15
|
+
run("compile", "--mode", "safe", "--apply");
|
|
16
|
+
const out = run("search", "--json", "smoke-token");
|
|
17
|
+
const data = JSON.parse(out);
|
|
18
|
+
if (!data.matches.length) { console.error("smoke FAIL: no match"); process.exit(1); }
|
|
19
|
+
console.log("brain smoke: PASS");
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
// L-5 shared-infra sync: copy the canonical builder/scorer/viewer from
|
|
4
|
+
// sdtk-wiki (source of truth) into this kit. The repo test
|
|
5
|
+
// tests/test_sdtk_brain_cli.py::test_shared_assets_are_byte_identical_with_wiki
|
|
6
|
+
// is the drift guard — run this script whenever it goes red.
|
|
7
|
+
const fs = require("fs");
|
|
8
|
+
const path = require("path");
|
|
9
|
+
const HERE = path.resolve(__dirname, "..");
|
|
10
|
+
const WIKI = path.resolve(HERE, "..", "..", "..", "sdtk-wiki", "distribution", "sdtk-wiki-kit");
|
|
11
|
+
const SHARED = [
|
|
12
|
+
"src/lib/wiki-build.js",
|
|
13
|
+
"src/lib/wiki-score.js",
|
|
14
|
+
"assets/atlas/doc_atlas_viewer_template.html",
|
|
15
|
+
"assets/atlas/vendor/mermaid.min.js",
|
|
16
|
+
];
|
|
17
|
+
for (const rel of SHARED) {
|
|
18
|
+
const src = path.join(WIKI, rel);
|
|
19
|
+
const dest = path.join(HERE, rel);
|
|
20
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
21
|
+
fs.copyFileSync(src, dest);
|
|
22
|
+
console.log(`[sync] ${rel}`);
|
|
23
|
+
}
|
|
24
|
+
console.log("[sync] shared assets synced from sdtk-wiki (canonical).");
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { parseFlags } = require("../lib/args");
|
|
4
|
+
const { ValidationError } = require("../lib/errors");
|
|
5
|
+
const { runWikiGithubEnrichmentReview } = require("../lib/wiki-enrich");
|
|
6
|
+
|
|
7
|
+
const ENRICH_FLAG_DEFS = {
|
|
8
|
+
help: { type: "boolean", alias: "h" },
|
|
9
|
+
"project-path": { type: "string" },
|
|
10
|
+
source: { type: "string" },
|
|
11
|
+
mode: { type: "string" },
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
function cmdEnrichHelp() {
|
|
15
|
+
console.log(`SDTK-BRAIN Enrich
|
|
16
|
+
|
|
17
|
+
Usage:
|
|
18
|
+
sdtk-brain enrich --source github --mode review [--project-path <path>]
|
|
19
|
+
|
|
20
|
+
Purpose:
|
|
21
|
+
Write an explicit opt-in GitHub enrichment review report from local wiki evidence.
|
|
22
|
+
|
|
23
|
+
Scope:
|
|
24
|
+
Enriches EXTERNAL tool/repo candidates surfaced by the ingest/compile pipeline.
|
|
25
|
+
Not related to your own project docs — those are indexed by "sdtk-brain atlas build".
|
|
26
|
+
|
|
27
|
+
Safety:
|
|
28
|
+
Review mode only in R1.
|
|
29
|
+
No network fetch, page rewrite, source mutation, apply, entitlement change, or .brain-legacy-unused mutation.
|
|
30
|
+
Network-backed metadata verification requires a later controller-approved slice.`);
|
|
31
|
+
return 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function cmdEnrich(args) {
|
|
35
|
+
const { flags } = parseFlags(args || [], ENRICH_FLAG_DEFS);
|
|
36
|
+
if (flags.help) return cmdEnrichHelp();
|
|
37
|
+
if (flags.source !== "github") {
|
|
38
|
+
throw new ValidationError("sdtk-brain enrich requires --source github in R1. No project files were changed.");
|
|
39
|
+
}
|
|
40
|
+
const mode = flags.mode || "review";
|
|
41
|
+
if (mode !== "review") {
|
|
42
|
+
throw new ValidationError("sdtk-brain enrich supports only --mode review in R1. No project files were changed.");
|
|
43
|
+
}
|
|
44
|
+
const result = runWikiGithubEnrichmentReview({ projectPath: flags["project-path"] });
|
|
45
|
+
console.log(`[brain] GitHub enrichment review: ${result.markdownPath}`);
|
|
46
|
+
console.log(`[brain] JSON report: ${result.jsonPath}`);
|
|
47
|
+
console.log(`[brain] Candidates: ${result.records.length}`);
|
|
48
|
+
console.log("[brain] Review-only local metadata. No network fetch, page rewrite, source mutation, or .brain-legacy-unused mutation was performed.");
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = {
|
|
53
|
+
cmdEnrich,
|
|
54
|
+
cmdEnrichHelp,
|
|
55
|
+
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// `sdtk-brain init` — scaffold a vault (BK-319, OQ-BR-1 locked: CWD-as-vault
|
|
4
|
+
// with a fail-closed refusal when the target is non-empty and not already a
|
|
5
|
+
// vault; vaults are standalone user-chosen folders, never product repos).
|
|
6
|
+
|
|
7
|
+
const fs = require("fs");
|
|
8
|
+
const path = require("path");
|
|
9
|
+
const { parseFlags } = require("../lib/args");
|
|
10
|
+
const { ValidationError } = require("../lib/errors");
|
|
11
|
+
|
|
12
|
+
const INIT_FLAG_DEFS = {
|
|
13
|
+
help: { type: "boolean", alias: "h" },
|
|
14
|
+
vault: { type: "string" },
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const RAW_DIRS = ["inbox", "articles", "papers", "repos", "notes", "meetings", "archive"];
|
|
18
|
+
const VAULT_MARKERS = ["CLAUDE.md", "raw", "wiki"];
|
|
19
|
+
|
|
20
|
+
function templatePath(name) {
|
|
21
|
+
return path.join(__dirname, "..", "..", "templates", name);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function looksLikeVault(target) {
|
|
25
|
+
return VAULT_MARKERS.every((marker) => fs.existsSync(path.join(target, marker)));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function printInitHelp() {
|
|
29
|
+
console.log(`sdtk-brain init
|
|
30
|
+
|
|
31
|
+
Usage:
|
|
32
|
+
sdtk-brain init [--vault <path>]
|
|
33
|
+
|
|
34
|
+
Scaffolds a second-brain vault:
|
|
35
|
+
raw/ immutable sources (inbox, articles, papers, repos, notes, meetings, archive)
|
|
36
|
+
wiki/ compiled knowledge layer (pages are created by compile --apply and by your agent)
|
|
37
|
+
workspace/ scratch space and exports
|
|
38
|
+
CLAUDE.md the agent operating contract (how the vault is maintained)
|
|
39
|
+
README.md human orientation
|
|
40
|
+
|
|
41
|
+
Safety:
|
|
42
|
+
Defaults to the current directory as the vault. Refuses a target that is
|
|
43
|
+
not empty unless it already looks like a vault (re-runs are idempotent and
|
|
44
|
+
never overwrite existing files). Never scaffolds into a product repo.`);
|
|
45
|
+
return 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function writeIfAbsent(filePath, content, created) {
|
|
49
|
+
if (fs.existsSync(filePath)) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
53
|
+
fs.writeFileSync(filePath, content, "utf-8");
|
|
54
|
+
created.push(filePath);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function cmdInit(args) {
|
|
58
|
+
const { flags } = parseFlags(args || [], INIT_FLAG_DEFS);
|
|
59
|
+
if (flags.help) {
|
|
60
|
+
return printInitHelp();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const target = path.resolve(flags.vault || process.cwd());
|
|
64
|
+
fs.mkdirSync(target, { recursive: true });
|
|
65
|
+
|
|
66
|
+
const entries = fs.readdirSync(target).filter((name) => !name.startsWith("."));
|
|
67
|
+
if (entries.length > 0 && !looksLikeVault(target)) {
|
|
68
|
+
throw new ValidationError(
|
|
69
|
+
`Target is not empty and does not look like a vault: ${target}. ` +
|
|
70
|
+
"A vault is a standalone folder (never a product repo). Pick an empty " +
|
|
71
|
+
"folder or pass --vault <path>. No files were changed."
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const created = [];
|
|
76
|
+
for (const dir of RAW_DIRS) {
|
|
77
|
+
const p = path.join(target, "raw", dir);
|
|
78
|
+
if (!fs.existsSync(p)) {
|
|
79
|
+
fs.mkdirSync(p, { recursive: true });
|
|
80
|
+
created.push(p);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
for (const dir of ["wiki", "workspace", path.join(".brain", "reports")]) {
|
|
84
|
+
const p = path.join(target, dir);
|
|
85
|
+
if (!fs.existsSync(p)) {
|
|
86
|
+
fs.mkdirSync(p, { recursive: true });
|
|
87
|
+
created.push(p);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
writeIfAbsent(path.join(target, "CLAUDE.md"), fs.readFileSync(templatePath("VAULT_CLAUDE.md"), "utf-8"), created);
|
|
92
|
+
writeIfAbsent(path.join(target, "README.md"), fs.readFileSync(templatePath("VAULT_README.md"), "utf-8"), created);
|
|
93
|
+
writeIfAbsent(
|
|
94
|
+
path.join(target, ".gitignore"),
|
|
95
|
+
".brain/\nworkspace/\n",
|
|
96
|
+
created
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
console.log(`[brain] Vault ready: ${target}`);
|
|
100
|
+
console.log(`[brain] Created: ${created.length} (existing files are never overwritten)`);
|
|
101
|
+
console.log("[brain] Next: drop a source into raw/inbox, then run:");
|
|
102
|
+
console.log("[brain] sdtk-brain ingest raw/inbox");
|
|
103
|
+
console.log("[brain] sdtk-brain compile --mode safe --apply");
|
|
104
|
+
console.log("[brain] sdtk-brain search \"<topic>\" · sdtk-brain open");
|
|
105
|
+
return 0;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
module.exports = { cmdInit, looksLikeVault };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { LINT_FLAG_DEFS, parseWikiFlags } = require("../lib/wiki-flags");
|
|
4
|
+
const { runWikiLint } = require("../lib/wiki-lint");
|
|
5
|
+
|
|
6
|
+
function hasHelp(args) {
|
|
7
|
+
return args.includes("-h") || args.includes("--help");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function cmdLintHelp() {
|
|
11
|
+
console.log(`Usage:
|
|
12
|
+
sdtk-brain lint --help
|
|
13
|
+
sdtk-brain lint [--project-path <path>]
|
|
14
|
+
|
|
15
|
+
Purpose:
|
|
16
|
+
Run report-first, non-destructive lint checks over canonical .brain content and local source-quality evidence.
|
|
17
|
+
|
|
18
|
+
Output:
|
|
19
|
+
.brain/reports/lint-report-YYYY-MM-DD.md
|
|
20
|
+
|
|
21
|
+
Behavior:
|
|
22
|
+
Findings are written to the report and do not auto-modify wiki or source files.
|
|
23
|
+
Source-quality checks report mojibake-like text, missing source URLs, weak titles, duplicate repo/source candidates, low-confidence extraction, and raw/graph/provenance coverage mismatch.
|
|
24
|
+
Local wiki quality metrics report frontmatter coverage, required sections, source refs, internal links, stub ratio, giant pages, density, and source evidence coverage.
|
|
25
|
+
Completed lint runs exit 0 even when findings exist.
|
|
26
|
+
Missing workspace or fatal report-write failures exit non-zero.
|
|
27
|
+
|
|
28
|
+
Options:
|
|
29
|
+
--project-path <path> Project root. Defaults to current working directory.`);
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function cmdLint(args) {
|
|
34
|
+
if (hasHelp(args)) {
|
|
35
|
+
return cmdLintHelp();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const { flags } = parseWikiFlags(args, LINT_FLAG_DEFS);
|
|
39
|
+
const result = runWikiLint({ projectPath: flags["project-path"] });
|
|
40
|
+
|
|
41
|
+
console.log(`[brain] Lint report: ${result.reportPath}`);
|
|
42
|
+
console.log(`[brain] Findings: ${result.totalFindings}`);
|
|
43
|
+
console.log("[brain] No wiki or source content was auto-modified.");
|
|
44
|
+
return 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = {
|
|
48
|
+
cmdLint,
|
|
49
|
+
cmdLintHelp,
|
|
50
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// `sdtk-brain open` — build the vault knowledge graph (shared atlas builder,
|
|
4
|
+
// L-5) and open the self-contained viewer. The viewer is a single HTML file
|
|
5
|
+
// with the graph JSON inlined, so file:// works without any server.
|
|
6
|
+
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const { parseFlags } = require("../lib/args");
|
|
9
|
+
const { buildAtlas, DEFAULT_EXCLUDE_FRAGS } = require("../lib/wiki-build");
|
|
10
|
+
const { openBrowser } = require("../lib/browser-open");
|
|
11
|
+
const { resolveProjectPath, getWikiGraphPath } = require("../lib/wiki-paths");
|
|
12
|
+
|
|
13
|
+
const OPEN_FLAG_DEFS = {
|
|
14
|
+
help: { type: "boolean", alias: "h" },
|
|
15
|
+
vault: { type: "string" },
|
|
16
|
+
"no-open": { type: "boolean" },
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
function printOpenHelp() {
|
|
20
|
+
console.log(`sdtk-brain open
|
|
21
|
+
|
|
22
|
+
Usage:
|
|
23
|
+
sdtk-brain open [--vault <path>] [--no-open]
|
|
24
|
+
|
|
25
|
+
Builds the vault knowledge graph over wiki/ and raw/ (shared SDTK atlas
|
|
26
|
+
builder) into .brain/graph and opens the self-contained viewer.html
|
|
27
|
+
(docs view + graph view). --no-open builds without launching a browser.
|
|
28
|
+
|
|
29
|
+
Local-only: no server is required — the viewer inlines its data.`);
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function cmdOpen(args) {
|
|
34
|
+
const { flags } = parseFlags(args || [], OPEN_FLAG_DEFS);
|
|
35
|
+
if (flags.help) {
|
|
36
|
+
return printOpenHelp();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const vault = resolveProjectPath(flags.vault || process.cwd());
|
|
40
|
+
const outputDir = getWikiGraphPath(vault);
|
|
41
|
+
const excludes = [...DEFAULT_EXCLUDE_FRAGS, ".brain", "workspace"];
|
|
42
|
+
const scanRoots = [path.join(vault, "wiki"), path.join(vault, "raw")]
|
|
43
|
+
.filter((root) => require("fs").existsSync(root));
|
|
44
|
+
if (scanRoots.length === 0) {
|
|
45
|
+
console.error("[brain] Nothing to index: no wiki/ or raw/ under the vault. Run `sdtk-brain init` first.");
|
|
46
|
+
return 1;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const result = await buildAtlas({ projectRoot: vault, outputDir, scanRoots, excludes });
|
|
50
|
+
const viewer = path.join(outputDir, "viewer.html");
|
|
51
|
+
console.log(`[brain] Graph: ${result.node_count} nodes, ${result.edge_count} edges`);
|
|
52
|
+
console.log(`[brain] Viewer: ${viewer}`);
|
|
53
|
+
if (!flags["no-open"]) {
|
|
54
|
+
openBrowser(viewer);
|
|
55
|
+
}
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = { cmdOpen };
|