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,357 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const { parseFlags } = require("../lib/args");
|
|
6
|
+
const { CliError, ValidationError } = require("../lib/errors");
|
|
7
|
+
const { runWikiCompileApply, runWikiCompileDryRun } = require("../lib/wiki-compile");
|
|
8
|
+
const { runWikiDiscoverPlan } = require("../lib/wiki-discover");
|
|
9
|
+
const { runWikiExtractDryRun } = require("../lib/wiki-extract");
|
|
10
|
+
const { runWikiLint } = require("../lib/wiki-lint");
|
|
11
|
+
const { runWikiSearch } = require("../lib/wiki-search");
|
|
12
|
+
const { getWikiReportsPath, resolveProjectPath } = require("../lib/wiki-paths");
|
|
13
|
+
const { printHumanResult } = require("./search");
|
|
14
|
+
|
|
15
|
+
const INGEST_FLAG_DEFS = {
|
|
16
|
+
help: { type: "boolean", alias: "h" },
|
|
17
|
+
"project-path": { type: "string" },
|
|
18
|
+
"source-root": { type: "string" },
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const COMPILE_FLAG_DEFS = {
|
|
22
|
+
help: { type: "boolean", alias: "h" },
|
|
23
|
+
"project-path": { type: "string" },
|
|
24
|
+
mode: { type: "string" },
|
|
25
|
+
apply: { type: "boolean" },
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const QUERY_FLAG_DEFS = {
|
|
29
|
+
help: { type: "boolean", alias: "h" },
|
|
30
|
+
"project-path": { type: "string" },
|
|
31
|
+
json: { type: "boolean" },
|
|
32
|
+
limit: { type: "string" },
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const DISCOVER_FLAG_DEFS = {
|
|
36
|
+
help: { type: "boolean", alias: "h" },
|
|
37
|
+
"project-path": { type: "string" },
|
|
38
|
+
plan: { type: "boolean" },
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const MAINTAIN_FLAG_DEFS = {
|
|
42
|
+
help: { type: "boolean", alias: "h" },
|
|
43
|
+
"project-path": { type: "string" },
|
|
44
|
+
mode: { type: "string" },
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
function ensureProjectPath(projectPath) {
|
|
48
|
+
const resolved = resolveProjectPath(projectPath || process.cwd());
|
|
49
|
+
if (!fs.existsSync(resolved) || !fs.statSync(resolved).isDirectory()) {
|
|
50
|
+
throw new ValidationError(`--project-path is not a valid directory: ${resolved}`);
|
|
51
|
+
}
|
|
52
|
+
return resolved;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function ensureSafeMode(mode, commandName) {
|
|
56
|
+
const resolvedMode = mode || "safe";
|
|
57
|
+
if (resolvedMode !== "safe") {
|
|
58
|
+
throw new ValidationError(
|
|
59
|
+
`sdtk-brain ${commandName} supports only --mode safe in R1. Auto mode is deferred. No project files were changed.`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
return resolvedMode;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function findLatestReport(projectPath, pattern) {
|
|
66
|
+
const reportsPath = getWikiReportsPath(projectPath);
|
|
67
|
+
if (!fs.existsSync(reportsPath) || !fs.statSync(reportsPath).isDirectory()) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
const matcher = new RegExp(`^${pattern}$`);
|
|
71
|
+
const files = fs.readdirSync(reportsPath)
|
|
72
|
+
.filter((name) => matcher.test(name))
|
|
73
|
+
.map((name) => {
|
|
74
|
+
const filePath = path.join(reportsPath, name);
|
|
75
|
+
return { filePath, mtimeMs: fs.statSync(filePath).mtimeMs };
|
|
76
|
+
})
|
|
77
|
+
.sort((a, b) => {
|
|
78
|
+
if (b.mtimeMs !== a.mtimeMs) return b.mtimeMs - a.mtimeMs;
|
|
79
|
+
return b.filePath.localeCompare(a.filePath);
|
|
80
|
+
});
|
|
81
|
+
return files.length > 0 ? files[0] : null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function latestExtraction(projectPath) {
|
|
85
|
+
return findLatestReport(projectPath, "semantic-extraction-dry-run-.*\\.json");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function latestApplyPlan(projectPath) {
|
|
89
|
+
return findLatestReport(projectPath, "compile-apply-plan-.*\\.json");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function cmdIngestHelp() {
|
|
93
|
+
console.log(`SDTK-BRAIN Ingest
|
|
94
|
+
|
|
95
|
+
Usage:
|
|
96
|
+
sdtk-brain ingest <source-root> [--project-path <path>]
|
|
97
|
+
sdtk-brain ingest --source-root <path> [--project-path <path>]
|
|
98
|
+
|
|
99
|
+
Purpose:
|
|
100
|
+
Run local semantic extraction over Markdown and supported JSON source records and write a report under .brain/reports.
|
|
101
|
+
|
|
102
|
+
Scope:
|
|
103
|
+
This pipeline researches EXTERNAL tool/repo knowledge referenced in your sources
|
|
104
|
+
(GitHub repos, comparisons, syntheses) — it is not a system-documentation extractor.
|
|
105
|
+
To index and query your own project docs, use "sdtk-brain init" / "sdtk-brain atlas build"
|
|
106
|
+
and then query/search/ask.
|
|
107
|
+
|
|
108
|
+
Safety:
|
|
109
|
+
Local sources only.
|
|
110
|
+
No source mutation, local wiki page writes, graph rebuild, web fetch, Ask, raw/provenance mutation, or .brain-legacy-unused mutation.
|
|
111
|
+
|
|
112
|
+
Next:
|
|
113
|
+
sdtk-brain compile --mode safe`);
|
|
114
|
+
return 0;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function cmdCompileHelp() {
|
|
118
|
+
console.log(`SDTK-BRAIN Compile
|
|
119
|
+
|
|
120
|
+
Usage:
|
|
121
|
+
sdtk-brain compile --mode safe [--project-path <path>]
|
|
122
|
+
sdtk-brain compile --mode safe --apply [--project-path <path>]
|
|
123
|
+
|
|
124
|
+
Purpose:
|
|
125
|
+
Use the latest semantic extraction report to write a compile preview and JSON sidecar, then optionally apply the sidecar.
|
|
126
|
+
|
|
127
|
+
Scope:
|
|
128
|
+
Compiles the external tool/repo knowledge extracted by ingest into wiki/ pages
|
|
129
|
+
(sources, entities, comparisons, syntheses). Your own project docs are indexed by
|
|
130
|
+
"sdtk-brain atlas build" instead — no compile step needed there.
|
|
131
|
+
|
|
132
|
+
Safety:
|
|
133
|
+
Safe mode is the only R1 mode.
|
|
134
|
+
--apply still uses the existing JSON sidecar contract.
|
|
135
|
+
No delete, archive, rewrite, raw/provenance mutation, source mutation, web fetch, Ask, or .brain-legacy-unused mutation.`);
|
|
136
|
+
return 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function cmdQueryHelp() {
|
|
140
|
+
console.log(`SDTK-BRAIN Query
|
|
141
|
+
|
|
142
|
+
Usage:
|
|
143
|
+
sdtk-brain query [--project-path <path>] [--json] [--limit <n>] "<query>"
|
|
144
|
+
|
|
145
|
+
Purpose:
|
|
146
|
+
Deterministically search generated local wiki Markdown pages.
|
|
147
|
+
|
|
148
|
+
Behavior:
|
|
149
|
+
Local search only.
|
|
150
|
+
No wiki.ask entitlement, LLM/RAG runtime, query history, or project mutation.
|
|
151
|
+
Use sdtk-brain ask for premium grounded Q&A when wiki.ask preconditions are available.`);
|
|
152
|
+
return 0;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function cmdDiscoverHelp() {
|
|
156
|
+
console.log(`SDTK-BRAIN Discover
|
|
157
|
+
|
|
158
|
+
Usage:
|
|
159
|
+
sdtk-brain discover --plan [--project-path <path>]
|
|
160
|
+
|
|
161
|
+
Purpose:
|
|
162
|
+
Write a local-only discovery plan from existing wiki gap evidence.
|
|
163
|
+
|
|
164
|
+
Safety:
|
|
165
|
+
Plan-only. No web fetch, source ingest, compile, apply, prune, delete, archive, query history, Ask, or .brain-legacy-unused mutation.`);
|
|
166
|
+
return 0;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function cmdMaintainHelp() {
|
|
170
|
+
console.log(`SDTK-BRAIN Maintain
|
|
171
|
+
|
|
172
|
+
Usage:
|
|
173
|
+
sdtk-brain maintain --mode safe [--project-path <path>]
|
|
174
|
+
|
|
175
|
+
Purpose:
|
|
176
|
+
Run a report-first maintenance cycle: lint, discover plan, and compile preview when an extraction report exists.
|
|
177
|
+
|
|
178
|
+
Safety:
|
|
179
|
+
Safe mode only.
|
|
180
|
+
No apply, delete, archive, source mutation, web fetch, Ask, query history, or .brain-legacy-unused mutation.`);
|
|
181
|
+
return 0;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function resolveSourceRoot(flags, positional) {
|
|
185
|
+
if (flags["source-root"]) return flags["source-root"];
|
|
186
|
+
if (positional.length === 1) return positional[0];
|
|
187
|
+
if (positional.length > 1) {
|
|
188
|
+
throw new ValidationError("sdtk-brain ingest accepts one source root. Quote paths that contain spaces. No project files were changed.");
|
|
189
|
+
}
|
|
190
|
+
throw new ValidationError("sdtk-brain ingest requires <source-root> or --source-root <path>. No project files were changed.");
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function cmdIngest(args) {
|
|
194
|
+
const { flags, positional } = parseFlags(args || [], INGEST_FLAG_DEFS);
|
|
195
|
+
if (flags.help) return cmdIngestHelp();
|
|
196
|
+
const projectPath = ensureProjectPath(flags["project-path"]);
|
|
197
|
+
const result = runWikiExtractDryRun({
|
|
198
|
+
projectPath,
|
|
199
|
+
sourceRootArg: resolveSourceRoot(flags, positional),
|
|
200
|
+
});
|
|
201
|
+
const extraction = result.extraction;
|
|
202
|
+
|
|
203
|
+
console.log(`[brain] Semantic extraction report: ${result.reportPath}`);
|
|
204
|
+
console.log(`[brain] Sources scanned: ${extraction.source_counts.scanned}`);
|
|
205
|
+
console.log(`[brain] Sources indexed: ${extraction.source_counts.indexed}`);
|
|
206
|
+
console.log(`[brain] Tool candidates: ${extraction.tool_entities.length}`);
|
|
207
|
+
console.log(`[brain] Concept candidates: ${extraction.concepts.length}`);
|
|
208
|
+
console.log(`[brain] Quality findings: ${extraction.source_quality_findings.length}`);
|
|
209
|
+
console.log("[brain] No source files, local wiki pages, raw/provenance state, graph outputs, or atlas compatibility files were modified.");
|
|
210
|
+
console.log("[brain] Next: sdtk-brain compile --mode safe");
|
|
211
|
+
return 0;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function runCompileDryRunFromLatestExtraction(projectPath) {
|
|
215
|
+
const extraction = latestExtraction(projectPath);
|
|
216
|
+
if (!extraction) {
|
|
217
|
+
throw new ValidationError(
|
|
218
|
+
"No semantic extraction report found. Run \"sdtk-brain ingest <source-root>\" first. No project files were changed."
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
return runWikiCompileDryRun({ projectPath, planArg: extraction.filePath });
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function ensureApplySidecar(projectPath) {
|
|
225
|
+
const extraction = latestExtraction(projectPath);
|
|
226
|
+
const sidecar = latestApplyPlan(projectPath);
|
|
227
|
+
if (!sidecar && !extraction) {
|
|
228
|
+
throw new ValidationError(
|
|
229
|
+
"No compile apply sidecar or semantic extraction report found. Run \"sdtk-brain ingest <source-root>\" first. No project files were changed."
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
if (!sidecar || (extraction && extraction.mtimeMs > sidecar.mtimeMs)) {
|
|
233
|
+
const dryRun = runCompileDryRunFromLatestExtraction(projectPath);
|
|
234
|
+
return {
|
|
235
|
+
sidecarPath: dryRun.applyPlanPath,
|
|
236
|
+
dryRun,
|
|
237
|
+
createdFreshSidecar: true,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
return {
|
|
241
|
+
sidecarPath: sidecar.filePath,
|
|
242
|
+
dryRun: null,
|
|
243
|
+
createdFreshSidecar: false,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function cmdCompile(args) {
|
|
248
|
+
const { flags } = parseFlags(args || [], COMPILE_FLAG_DEFS);
|
|
249
|
+
if (flags.help) return cmdCompileHelp();
|
|
250
|
+
ensureSafeMode(flags.mode, "compile");
|
|
251
|
+
const projectPath = ensureProjectPath(flags["project-path"]);
|
|
252
|
+
|
|
253
|
+
if (!flags.apply) {
|
|
254
|
+
const result = runCompileDryRunFromLatestExtraction(projectPath);
|
|
255
|
+
console.log(`[brain] Compile dry-run preview: ${result.reportPath}`);
|
|
256
|
+
console.log(`[brain] Compile apply JSON sidecar: ${result.applyPlanPath}`);
|
|
257
|
+
console.log(`[brain] Operations: ${result.operations.length}`);
|
|
258
|
+
console.log(`[brain] Unsupported operations: ${result.unsupportedCount}`);
|
|
259
|
+
console.log("[brain] No wiki pages, raw sources, provenance, source files, or atlas compatibility files were modified.");
|
|
260
|
+
console.log("[brain] Next: sdtk-brain compile --mode safe --apply");
|
|
261
|
+
if (result.unsupportedCount > 0) {
|
|
262
|
+
throw new CliError(
|
|
263
|
+
`unsupported_operation: ${result.unsupportedCount} operation(s) are blocked. Review the compile dry-run preview report.`,
|
|
264
|
+
1
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
return 0;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const sidecar = ensureApplySidecar(projectPath);
|
|
271
|
+
const result = runWikiCompileApply({ projectPath, planArg: sidecar.sidecarPath });
|
|
272
|
+
console.log(`[brain] Compile apply plan: ${result.planPath}`);
|
|
273
|
+
if (sidecar.createdFreshSidecar) {
|
|
274
|
+
console.log(`[brain] Refreshed compile dry-run preview: ${sidecar.dryRun.reportPath}`);
|
|
275
|
+
}
|
|
276
|
+
console.log(`[brain] Created files: ${result.created.length}`);
|
|
277
|
+
console.log(`[brain] Unchanged files: ${result.unchanged.length}`);
|
|
278
|
+
console.log(`[brain] Scaffold created/skipped: ${result.scaffoldCreated.length}/${result.scaffoldSkipped.length}`);
|
|
279
|
+
console.log(`[brain] Source-quality warnings: ${result.sourceQualityWarningCount}`);
|
|
280
|
+
console.log("[brain] Canonical local wiki output is <project>/wiki; .brain remains internal state. No source files, raw sources, provenance descriptors, or atlas compatibility files were modified.");
|
|
281
|
+
console.log("[brain] Next: sdtk-brain query \"multi-agent\"");
|
|
282
|
+
return 0;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function cmdQuery(args) {
|
|
286
|
+
const { flags, positional } = parseFlags(args || [], QUERY_FLAG_DEFS);
|
|
287
|
+
if (flags.help) return cmdQueryHelp();
|
|
288
|
+
const query = positional.join(" ");
|
|
289
|
+
const result = runWikiSearch({
|
|
290
|
+
projectPath: flags["project-path"],
|
|
291
|
+
query,
|
|
292
|
+
limit: flags.limit,
|
|
293
|
+
});
|
|
294
|
+
if (flags.json) {
|
|
295
|
+
console.log(JSON.stringify(result, null, 2));
|
|
296
|
+
} else {
|
|
297
|
+
console.log("[brain] Query mode: local deterministic search. Premium Ask is not used.");
|
|
298
|
+
printHumanResult(result);
|
|
299
|
+
}
|
|
300
|
+
return 0;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function cmdDiscover(args) {
|
|
304
|
+
const { flags } = parseFlags(args || [], DISCOVER_FLAG_DEFS);
|
|
305
|
+
if (flags.help) return cmdDiscoverHelp();
|
|
306
|
+
if (!flags.plan) {
|
|
307
|
+
throw new ValidationError("sdtk-brain discover requires --plan. No web/fetch/apply/auto behavior is enabled. No project files were changed.");
|
|
308
|
+
}
|
|
309
|
+
const projectPath = ensureProjectPath(flags["project-path"]);
|
|
310
|
+
const result = runWikiDiscoverPlan({ projectPath });
|
|
311
|
+
|
|
312
|
+
console.log(`[brain] Discovery plan report: ${result.reportPath}`);
|
|
313
|
+
console.log(`[brain] Pages scanned: ${result.pageCount}`);
|
|
314
|
+
console.log(`[brain] Plan items: ${result.items.length}`);
|
|
315
|
+
console.log("[brain] No sources were fetched, ingested, compiled, applied, pruned, deleted, archived, or persisted.");
|
|
316
|
+
return 0;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function cmdMaintain(args) {
|
|
320
|
+
const { flags } = parseFlags(args || [], MAINTAIN_FLAG_DEFS);
|
|
321
|
+
if (flags.help) return cmdMaintainHelp();
|
|
322
|
+
ensureSafeMode(flags.mode, "maintain");
|
|
323
|
+
const projectPath = ensureProjectPath(flags["project-path"]);
|
|
324
|
+
|
|
325
|
+
const lint = runWikiLint({ projectPath });
|
|
326
|
+
const discover = runWikiDiscoverPlan({ projectPath });
|
|
327
|
+
const extraction = latestExtraction(projectPath);
|
|
328
|
+
let compile = null;
|
|
329
|
+
if (extraction) {
|
|
330
|
+
compile = runWikiCompileDryRun({ projectPath, planArg: extraction.filePath });
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
console.log(`[brain] Maintain mode: safe`);
|
|
334
|
+
console.log(`[brain] Lint report: ${lint.reportPath}`);
|
|
335
|
+
console.log(`[brain] Discovery plan report: ${discover.reportPath}`);
|
|
336
|
+
if (compile) {
|
|
337
|
+
console.log(`[brain] Compile dry-run preview: ${compile.reportPath}`);
|
|
338
|
+
console.log(`[brain] Compile apply JSON sidecar: ${compile.applyPlanPath}`);
|
|
339
|
+
} else {
|
|
340
|
+
console.log("[brain] Compile preview skipped: no semantic extraction report found. Run \"sdtk-brain ingest <source-root>\".");
|
|
341
|
+
}
|
|
342
|
+
console.log("[brain] No apply, source mutation, web fetch, Ask, query history, delete/archive, or atlas compatibility mutation was performed.");
|
|
343
|
+
return 0;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
module.exports = {
|
|
347
|
+
cmdCompile,
|
|
348
|
+
cmdCompileHelp,
|
|
349
|
+
cmdDiscover,
|
|
350
|
+
cmdDiscoverHelp,
|
|
351
|
+
cmdIngest,
|
|
352
|
+
cmdIngestHelp,
|
|
353
|
+
cmdMaintain,
|
|
354
|
+
cmdMaintainHelp,
|
|
355
|
+
cmdQuery,
|
|
356
|
+
cmdQueryHelp,
|
|
357
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { parseFlags } = require("../lib/args");
|
|
4
|
+
const { runWikiSearch } = require("../lib/wiki-search");
|
|
5
|
+
|
|
6
|
+
const SEARCH_FLAG_DEFS = {
|
|
7
|
+
help: { type: "boolean", alias: "h" },
|
|
8
|
+
"project-path": { type: "string" },
|
|
9
|
+
json: { type: "boolean" },
|
|
10
|
+
limit: { type: "string" },
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
function parseSearchFlags(args) {
|
|
14
|
+
return parseFlags(args || [], SEARCH_FLAG_DEFS);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function printSearchHelp() {
|
|
18
|
+
console.log(`SDTK-BRAIN Local Search
|
|
19
|
+
|
|
20
|
+
Usage:
|
|
21
|
+
sdtk-brain search --project-path <path> "multi-agent"
|
|
22
|
+
sdtk-brain search --project-path <path> --json --limit 10 "Claude Code"
|
|
23
|
+
|
|
24
|
+
Purpose:
|
|
25
|
+
Deterministically search generated local wiki Markdown pages.
|
|
26
|
+
|
|
27
|
+
Inputs:
|
|
28
|
+
Union of the atlas pages (.brain/pages/**/*.md, built by "sdtk-brain atlas build")
|
|
29
|
+
and wiki/**/*.md, falling back to .brain/personal-brain/**/*.md for legacy workspaces.
|
|
30
|
+
Duplicate coverage of the same source is de-duplicated; the atlas version wins.
|
|
31
|
+
Each match reports its store (atlas | wiki).
|
|
32
|
+
|
|
33
|
+
Behavior:
|
|
34
|
+
Read-only and non-premium.
|
|
35
|
+
No wiki.ask entitlement is required.
|
|
36
|
+
No LLM, RAG, web search, query history, compile/apply, prune, or project mutation is performed.`);
|
|
37
|
+
return 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function printHumanResult(result) {
|
|
41
|
+
const lines = [
|
|
42
|
+
`Query: ${result.query}`,
|
|
43
|
+
`Search mode: ${result.searchMode}`,
|
|
44
|
+
`Wiki content: ${result.wikiContentPath}`,
|
|
45
|
+
`Wiki content mode: ${result.wikiContentMode}`,
|
|
46
|
+
`Scanned files: ${result.scannedFiles}`,
|
|
47
|
+
`Matches: ${result.totalMatches}`,
|
|
48
|
+
"",
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
if (result.matches.length === 0) {
|
|
52
|
+
lines.push("No local wiki matches found.");
|
|
53
|
+
} else {
|
|
54
|
+
result.matches.forEach((match, index) => {
|
|
55
|
+
lines.push(`${index + 1}. ${match.path}`);
|
|
56
|
+
lines.push(` store: ${match.store}`);
|
|
57
|
+
lines.push(` title: ${match.title}`);
|
|
58
|
+
lines.push(` score: ${match.score}`);
|
|
59
|
+
lines.push(` why: ${match.why}`);
|
|
60
|
+
lines.push(` snippet: ${match.snippet}`);
|
|
61
|
+
lines.push("");
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
lines.push("No entitlement, LLM/RAG runtime, query history, or project mutation was used.");
|
|
66
|
+
console.log(lines.join("\n").trimEnd());
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function cmdSearch(args) {
|
|
70
|
+
const { flags, positional } = parseSearchFlags(args || []);
|
|
71
|
+
if (flags.help) {
|
|
72
|
+
return printSearchHelp();
|
|
73
|
+
}
|
|
74
|
+
const query = positional.join(" ");
|
|
75
|
+
const result = runWikiSearch({
|
|
76
|
+
projectPath: flags["project-path"],
|
|
77
|
+
query,
|
|
78
|
+
limit: flags.limit,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
if (flags.json) {
|
|
82
|
+
console.log(JSON.stringify(result, null, 2));
|
|
83
|
+
} else {
|
|
84
|
+
printHumanResult(result);
|
|
85
|
+
}
|
|
86
|
+
return 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
module.exports = {
|
|
90
|
+
cmdSearch,
|
|
91
|
+
parseSearchFlags,
|
|
92
|
+
printHumanResult,
|
|
93
|
+
printSearchHelp,
|
|
94
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// `sdtk-brain status` — truthful vault status, read-only.
|
|
4
|
+
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const path = require("path");
|
|
7
|
+
const { parseFlags } = require("../lib/args");
|
|
8
|
+
const { resolveProjectPath, getWikiWorkspacePath, getCanonicalWikiPath, getWikiGraphPath } = require("../lib/wiki-paths");
|
|
9
|
+
|
|
10
|
+
const STATUS_FLAG_DEFS = {
|
|
11
|
+
help: { type: "boolean", alias: "h" },
|
|
12
|
+
vault: { type: "string" },
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function countFiles(root, ext) {
|
|
16
|
+
if (!fs.existsSync(root)) {
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
let count = 0;
|
|
20
|
+
const stack = [root];
|
|
21
|
+
while (stack.length) {
|
|
22
|
+
const current = stack.pop();
|
|
23
|
+
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
|
24
|
+
const p = path.join(current, entry.name);
|
|
25
|
+
if (entry.isDirectory()) {
|
|
26
|
+
stack.push(p);
|
|
27
|
+
} else if (entry.isFile() && (!ext || p.endsWith(ext))) {
|
|
28
|
+
count += 1;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return count;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function cmdStatus(args) {
|
|
36
|
+
const { flags } = parseFlags(args || [], STATUS_FLAG_DEFS);
|
|
37
|
+
if (flags.help) {
|
|
38
|
+
console.log("Usage: sdtk-brain status [--vault <path>] — read-only vault status.");
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
41
|
+
const vault = resolveProjectPath(flags.vault || process.cwd());
|
|
42
|
+
const wiki = getCanonicalWikiPath(vault);
|
|
43
|
+
const raw = path.join(vault, "raw");
|
|
44
|
+
const workspace = getWikiWorkspacePath(vault);
|
|
45
|
+
const viewer = path.join(getWikiGraphPath(vault), "viewer.html");
|
|
46
|
+
|
|
47
|
+
console.log(`[brain] Vault: ${vault}`);
|
|
48
|
+
console.log(` Contract: ${fs.existsSync(path.join(vault, "CLAUDE.md")) ? "CLAUDE.md present" : "CLAUDE.md MISSING — run sdtk-brain init"}`);
|
|
49
|
+
console.log(` raw/: ${countFiles(raw)} file(s)`);
|
|
50
|
+
console.log(` wiki/: ${countFiles(wiki, ".md")} page(s)`);
|
|
51
|
+
console.log(` reports: ${countFiles(path.join(workspace, "reports"))} report(s) under .brain/reports`);
|
|
52
|
+
console.log(` viewer: ${fs.existsSync(viewer) ? viewer : "not built yet — run sdtk-brain open"}`);
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = { cmdStatus };
|
package/src/lib/args.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { ValidationError } = require("./errors");
|
|
4
|
+
|
|
5
|
+
function parseFlags(argv, defs) {
|
|
6
|
+
// Brain-wide convention: every command that takes --project-path also
|
|
7
|
+
// accepts --vault as a friendlier alias (a vault IS the project root here).
|
|
8
|
+
if (defs && defs["project-path"] && !defs.vault) {
|
|
9
|
+
defs = { ...defs, vault: { type: "string" } };
|
|
10
|
+
}
|
|
11
|
+
const flags = {};
|
|
12
|
+
const positional = [];
|
|
13
|
+
const aliasMap = {};
|
|
14
|
+
|
|
15
|
+
for (const [name, def] of Object.entries(defs)) {
|
|
16
|
+
if (def.alias) aliasMap[def.alias] = name;
|
|
17
|
+
if (def.type === "boolean") flags[name] = false;
|
|
18
|
+
else flags[name] = undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let i = 0;
|
|
22
|
+
while (i < argv.length) {
|
|
23
|
+
const arg = argv[i];
|
|
24
|
+
|
|
25
|
+
if (!arg.startsWith("-")) {
|
|
26
|
+
positional.push(arg);
|
|
27
|
+
i++;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let key;
|
|
32
|
+
let inlineValue;
|
|
33
|
+
|
|
34
|
+
if (arg.includes("=")) {
|
|
35
|
+
const eqIdx = arg.indexOf("=");
|
|
36
|
+
key = arg.slice(0, eqIdx);
|
|
37
|
+
inlineValue = arg.slice(eqIdx + 1);
|
|
38
|
+
} else {
|
|
39
|
+
key = arg;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const stripped = key.replace(/^-{1,2}/, "");
|
|
43
|
+
const resolved = aliasMap[stripped] || stripped;
|
|
44
|
+
|
|
45
|
+
if (!(resolved in defs)) {
|
|
46
|
+
throw new ValidationError(`Unknown flag: ${key}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const def = defs[resolved];
|
|
50
|
+
if (def.type === "boolean") {
|
|
51
|
+
flags[resolved] = true;
|
|
52
|
+
i++;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let value = inlineValue;
|
|
57
|
+
if (value === undefined) {
|
|
58
|
+
i++;
|
|
59
|
+
if (i >= argv.length) {
|
|
60
|
+
throw new ValidationError(`Flag ${key} requires a value.`);
|
|
61
|
+
}
|
|
62
|
+
value = argv[i];
|
|
63
|
+
}
|
|
64
|
+
flags[resolved] = value;
|
|
65
|
+
i++;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (flags.vault !== undefined && flags["project-path"] === undefined) {
|
|
69
|
+
flags["project-path"] = flags.vault;
|
|
70
|
+
}
|
|
71
|
+
return { flags, positional };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
module.exports = {
|
|
75
|
+
parseFlags,
|
|
76
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { execFile } = require("child_process");
|
|
4
|
+
|
|
5
|
+
function openBrowser(url) {
|
|
6
|
+
return new Promise((resolve) => {
|
|
7
|
+
let cmd;
|
|
8
|
+
let args;
|
|
9
|
+
|
|
10
|
+
if (process.platform === "win32") {
|
|
11
|
+
cmd = "cmd";
|
|
12
|
+
args = ["/c", "start", "", url];
|
|
13
|
+
} else if (process.platform === "darwin") {
|
|
14
|
+
cmd = "open";
|
|
15
|
+
args = [url];
|
|
16
|
+
} else {
|
|
17
|
+
cmd = "xdg-open";
|
|
18
|
+
args = [url];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
execFile(cmd, args, { windowsHide: true }, (err) => {
|
|
22
|
+
if (err) {
|
|
23
|
+
console.error(`[brain] Warning: could not open browser: ${err.message}`);
|
|
24
|
+
}
|
|
25
|
+
resolve();
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = {
|
|
31
|
+
openBrowser,
|
|
32
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
class CliError extends Error {
|
|
4
|
+
constructor(message, exitCode = 1) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "CliError";
|
|
7
|
+
this.exitCode = exitCode;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
class ValidationError extends CliError {
|
|
12
|
+
constructor(message) {
|
|
13
|
+
super(message, 1);
|
|
14
|
+
this.name = "ValidationError";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
class DependencyError extends CliError {
|
|
19
|
+
constructor(message) {
|
|
20
|
+
super(message, 2);
|
|
21
|
+
this.name = "DependencyError";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = {
|
|
26
|
+
CliError,
|
|
27
|
+
DependencyError,
|
|
28
|
+
ValidationError,
|
|
29
|
+
};
|