librian 0.1.0 → 0.1.1
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/CHANGELOG.md +35 -0
- package/README.md +28 -5
- package/dist/cli.js +401 -262
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,41 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## [0.1.1] - 2026-11-08
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **`librian add [<folder>]`.** Adds a docs folder without opening the menu:
|
|
12
|
+
run it inside the docs folder, or pass the path. It counts the markdown
|
|
13
|
+
files, asks `Add it as "akms_docs"? (Y/n)` and adds the entry. It refuses
|
|
14
|
+
your home folder and a drive root (exit code 2), says `Already added as …`
|
|
15
|
+
(exit code 0) for a folder that is already there, and adds nothing when the
|
|
16
|
+
input ends.
|
|
17
|
+
- **The folder question starts with the current folder.** Start `librian`
|
|
18
|
+
inside a docs folder and Enter is enough to add it. In your home folder or a
|
|
19
|
+
drive root the question starts empty.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- **Add asks for the folder only.** The Add form with its Name, Exclude and
|
|
24
|
+
Max bytes rows is gone: the folder is checked (a missing folder is explained
|
|
25
|
+
and asked again; a folder already added takes you to its entry; a folder
|
|
26
|
+
without markdown files defaults to No), then one question adds it with the
|
|
27
|
+
default name. Name, Exclude and Max bytes are changed with **Edit** on the
|
|
28
|
+
entry, which still opens the form.
|
|
29
|
+
- **Counting files is bounded.** Add, `librian add`, Check and the file counts
|
|
30
|
+
in the main menu stop at 5,000 files or after 2 seconds and show
|
|
31
|
+
`5000+ files`, so choosing a huge folder does not hang.
|
|
32
|
+
- **The menu's AI services screen is now called Register.** Its rows and
|
|
33
|
+
actions are the same: switch Claude Code or Codex on or off, then Apply.
|
|
34
|
+
|
|
35
|
+
### Removed
|
|
36
|
+
|
|
37
|
+
- **Runtime → Use a local checkout.** It was for running an unpublished build.
|
|
38
|
+
Install a built checkout with
|
|
39
|
+
`npm install --prefix ~/.librian/runtime <path>` instead; the menu still
|
|
40
|
+
shows it as `linked to <path>` and **Install from npm** switches back.
|
|
41
|
+
|
|
7
42
|
## [0.1.0] - 2026-11-08
|
|
8
43
|
|
|
9
44
|
First release.
|
package/README.md
CHANGED
|
@@ -22,11 +22,11 @@ npx -y librian@latest # or: npm i -g librian, then run `librian`
|
|
|
22
22
|
This opens the librian menu. Then:
|
|
23
23
|
|
|
24
24
|
1. **Runtime → Install**: installs librian into `~/.librian/runtime`.
|
|
25
|
-
2. **Add docs folder**:
|
|
26
|
-
3. **
|
|
25
|
+
2. **Add docs folder**: type the path of a docs folder. If you started `librian` inside it, the question is already filled in with the current folder, so Enter is enough. Repeat for more folders. Or skip the menu: run `librian add` inside the docs folder (see below).
|
|
26
|
+
3. **Register**: switch on Claude Code and/or Codex, then **Apply**. librian shows the commands it will run and asks before running them.
|
|
27
27
|
4. Open a new session in Claude Code or Codex. Sessions that are already open keep what they started with.
|
|
28
28
|
|
|
29
|
-
To update, open `librian` → **Runtime → Check for updates**. To undo a registration, switch the service off in **
|
|
29
|
+
To update, open `librian` → **Runtime → Check for updates**. To undo a registration, switch the service off in **Register** and **Apply**.
|
|
30
30
|
|
|
31
31
|
## The menu
|
|
32
32
|
|
|
@@ -37,6 +37,18 @@ Run `librian` in a terminal. Without a terminal (a pipe or a script) it prints t
|
|
|
37
37
|
The main screen lists your docs folders; each one is an entry with a name and a path, like a connection in a database tool.
|
|
38
38
|
Choose an entry to **Edit**, **Check** (file count, total size, largest files) or **Remove** it. Removing an entry never touches the folder itself.
|
|
39
39
|
|
|
40
|
+
**Add docs folder** asks for one thing, the folder, and then adds it:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
? Docs folder › D:\workspace\projects\akms\akms.docs
|
|
44
|
+
107 markdown files · 4.1M
|
|
45
|
+
? Add it as "akms_docs"? (Y/n)
|
|
46
|
+
✔ Added akms_docs. Open a new session to use akms_docs__read and the other tools.
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
A folder that does not exist is refused with the reason and asked again with what you typed. A folder that is already added says `Already added as "akms".` and takes you to that entry. A folder without markdown files gets a warning and the question defaults to No. Esc saves nothing. The entry gets the default name, no Exclude and the default Max bytes; change them with **Edit** on the entry.
|
|
50
|
+
Counting stops at 5,000 files or after 2 seconds and shows `5000+ files`, so picking a huge folder by mistake does not hang the menu.
|
|
51
|
+
|
|
40
52
|
An entry has:
|
|
41
53
|
|
|
42
54
|
| Field | Meaning |
|
|
@@ -50,7 +62,18 @@ Symbolic links and junctions are not followed. librian only reads your docs; it
|
|
|
50
62
|
|
|
51
63
|
Adding, editing or removing an entry takes effect in new sessions only.
|
|
52
64
|
|
|
53
|
-
|
|
65
|
+
#### `librian add`
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
cd path/to/your/docs
|
|
69
|
+
librian add # adds the current folder
|
|
70
|
+
librian add path/to/docs # or a folder you name
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The same flow without the menu: it prints the file count, asks `Add it as "…"? (Y/n)` and adds the entry. Without a terminal it reads the answer from a line of input, and if the input ends it adds nothing and exits 0.
|
|
74
|
+
It refuses your home folder and a drive root (exit code 2): run it inside the docs folder or pass the path. A folder that is already added exits 0. When no AI service is registered yet, the last line points you to **Register**.
|
|
75
|
+
|
|
76
|
+
### Register
|
|
54
77
|
|
|
55
78
|
Registers librian as one MCP server named `librian` in **Claude Code** and **Codex**, for all your projects (user scope).
|
|
56
79
|
It runs `claude mcp add --scope user …` and `codex mcp add …` for you, and `… mcp remove …` to unregister.
|
|
@@ -148,7 +171,7 @@ librian --help | --version
|
|
|
148
171
|
|
|
149
172
|
## Contributing
|
|
150
173
|
|
|
151
|
-
To run a checkout of this repository instead of the published package, build it (`npm run build`) and
|
|
174
|
+
To run a checkout of this repository instead of the published package, build it (`npm run build`) and install it into the runtime: `npm install --prefix ~/.librian/runtime <path to the checkout>`. The menu then shows the runtime as `linked to <path>`, and **Runtime → Install from npm** switches back.
|
|
152
175
|
|
|
153
176
|
## License
|
|
154
177
|
|
package/dist/cli.js
CHANGED
|
@@ -2985,7 +2985,7 @@ var require_compile = __commonJS({
|
|
|
2985
2985
|
const schOrFunc = root.refs[ref];
|
|
2986
2986
|
if (schOrFunc)
|
|
2987
2987
|
return schOrFunc;
|
|
2988
|
-
let _sch =
|
|
2988
|
+
let _sch = resolve6.call(this, root, ref);
|
|
2989
2989
|
if (_sch === void 0) {
|
|
2990
2990
|
const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
2991
2991
|
const { schemaId } = this.opts;
|
|
@@ -3012,7 +3012,7 @@ var require_compile = __commonJS({
|
|
|
3012
3012
|
function sameSchemaEnv(s1, s2) {
|
|
3013
3013
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
3014
3014
|
}
|
|
3015
|
-
function
|
|
3015
|
+
function resolve6(root, ref) {
|
|
3016
3016
|
let sch;
|
|
3017
3017
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
3018
3018
|
ref = sch;
|
|
@@ -3872,11 +3872,11 @@ var require_fast_uri = __commonJS({
|
|
|
3872
3872
|
normalizeString(uri, options);
|
|
3873
3873
|
} else if (typeof uri === "object") {
|
|
3874
3874
|
uri = /** @type {T} */
|
|
3875
|
-
|
|
3875
|
+
parse4(serialize(uri, options), options);
|
|
3876
3876
|
}
|
|
3877
3877
|
return uri;
|
|
3878
3878
|
}
|
|
3879
|
-
function
|
|
3879
|
+
function resolve6(baseURI, relativeURI, options) {
|
|
3880
3880
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3881
3881
|
const {
|
|
3882
3882
|
parsed: baseParsed,
|
|
@@ -3915,8 +3915,8 @@ var require_fast_uri = __commonJS({
|
|
|
3915
3915
|
function resolveComponent(base, relative3, options, skipNormalization) {
|
|
3916
3916
|
const target = {};
|
|
3917
3917
|
if (!skipNormalization) {
|
|
3918
|
-
base =
|
|
3919
|
-
relative3 =
|
|
3918
|
+
base = parse4(serialize(base, options), options);
|
|
3919
|
+
relative3 = parse4(serialize(relative3, options), options);
|
|
3920
3920
|
}
|
|
3921
3921
|
options = options || {};
|
|
3922
3922
|
if (!options.tolerant && relative3.scheme) {
|
|
@@ -4234,7 +4234,7 @@ var require_fast_uri = __commonJS({
|
|
|
4234
4234
|
}
|
|
4235
4235
|
return { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme };
|
|
4236
4236
|
}
|
|
4237
|
-
function
|
|
4237
|
+
function parse4(uri, opts) {
|
|
4238
4238
|
return parseWithStatus(uri, opts).parsed;
|
|
4239
4239
|
}
|
|
4240
4240
|
function normalizeString(uri, opts) {
|
|
@@ -4267,11 +4267,11 @@ var require_fast_uri = __commonJS({
|
|
|
4267
4267
|
var fastUri = {
|
|
4268
4268
|
SCHEMES,
|
|
4269
4269
|
normalize: normalize2,
|
|
4270
|
-
resolve:
|
|
4270
|
+
resolve: resolve6,
|
|
4271
4271
|
resolveComponent,
|
|
4272
4272
|
equal,
|
|
4273
4273
|
serialize,
|
|
4274
|
-
parse:
|
|
4274
|
+
parse: parse4
|
|
4275
4275
|
};
|
|
4276
4276
|
module.exports = fastUri;
|
|
4277
4277
|
module.exports.default = fastUri;
|
|
@@ -7258,12 +7258,12 @@ var require_dist = __commonJS({
|
|
|
7258
7258
|
|
|
7259
7259
|
// src/cli.ts
|
|
7260
7260
|
import { statSync as statSync5 } from "node:fs";
|
|
7261
|
-
import { resolve as
|
|
7261
|
+
import { resolve as resolve5 } from "node:path";
|
|
7262
7262
|
|
|
7263
7263
|
// package.json
|
|
7264
7264
|
var package_default = {
|
|
7265
7265
|
name: "librian",
|
|
7266
|
-
version: "0.1.
|
|
7266
|
+
version: "0.1.1",
|
|
7267
7267
|
description: "Find and read sections of a markdown docs folder over MCP, with outlines and line ranges that stay current as the docs change",
|
|
7268
7268
|
repository: {
|
|
7269
7269
|
type: "git",
|
|
@@ -7331,6 +7331,8 @@ var TITLE_MAX_CHARS = 80;
|
|
|
7331
7331
|
var EXCERPT_BYTES = 160;
|
|
7332
7332
|
var SEARCH_LIMIT_DEFAULT = 20;
|
|
7333
7333
|
var SEARCH_LIMIT_MAX = 50;
|
|
7334
|
+
var COUNT_MAX_FILES = 5e3;
|
|
7335
|
+
var COUNT_MAX_MS = 2e3;
|
|
7334
7336
|
|
|
7335
7337
|
// src/modules/catalog/catalog.ts
|
|
7336
7338
|
var CATALOG_FILE_NAME = "catalog.json";
|
|
@@ -7456,9 +7458,10 @@ function defaultEntryName(root, taken) {
|
|
|
7456
7458
|
}
|
|
7457
7459
|
}
|
|
7458
7460
|
|
|
7459
|
-
// src/modules/
|
|
7460
|
-
import {
|
|
7461
|
-
import {
|
|
7461
|
+
// src/modules/cli/folder-check.ts
|
|
7462
|
+
import { statSync as statSync2 } from "node:fs";
|
|
7463
|
+
import { homedir as homedir2 } from "node:os";
|
|
7464
|
+
import { join as join3, parse, resolve as resolve2 } from "node:path";
|
|
7462
7465
|
|
|
7463
7466
|
// src/_libs/compare-text.ts
|
|
7464
7467
|
function compareText(left, right) {
|
|
@@ -7491,6 +7494,10 @@ function compareText(left, right) {
|
|
|
7491
7494
|
return 0;
|
|
7492
7495
|
}
|
|
7493
7496
|
|
|
7497
|
+
// src/modules/docs/library.ts
|
|
7498
|
+
import { readFileSync as readFileSync2, readdirSync, realpathSync, statSync } from "node:fs";
|
|
7499
|
+
import { basename, join as join2, resolve } from "node:path";
|
|
7500
|
+
|
|
7494
7501
|
// src/_libs/is-inside.ts
|
|
7495
7502
|
import { isAbsolute, relative, sep } from "node:path";
|
|
7496
7503
|
function isInside(root, target) {
|
|
@@ -7682,18 +7689,10 @@ var ALWAYS_EXCLUDED_DIRECTORY = "node_modules";
|
|
|
7682
7689
|
function normalizeExclude(entry) {
|
|
7683
7690
|
return entry.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+$/, "");
|
|
7684
7691
|
}
|
|
7685
|
-
function
|
|
7686
|
-
const root = realpathSync.native(resolve(options.root));
|
|
7687
|
-
if (statSync(root).isDirectory() == false) {
|
|
7688
|
-
throw new Error("root is not a folder: " + root);
|
|
7689
|
-
}
|
|
7690
|
-
let name = basename(root);
|
|
7691
|
-
if (name == "") {
|
|
7692
|
-
name = root;
|
|
7693
|
-
}
|
|
7692
|
+
function listMarkdownPaths(root, exclude, limits) {
|
|
7694
7693
|
const excludedNames = /* @__PURE__ */ new Set();
|
|
7695
7694
|
const excludedPaths = /* @__PURE__ */ new Set();
|
|
7696
|
-
for (const entry of
|
|
7695
|
+
for (const entry of exclude) {
|
|
7697
7696
|
const normalized = normalizeExclude(entry);
|
|
7698
7697
|
if (normalized == "") {
|
|
7699
7698
|
continue;
|
|
@@ -7704,43 +7703,65 @@ function openLibrary(options) {
|
|
|
7704
7703
|
excludedNames.add(normalized);
|
|
7705
7704
|
}
|
|
7706
7705
|
}
|
|
7707
|
-
const
|
|
7708
|
-
|
|
7709
|
-
|
|
7710
|
-
|
|
7711
|
-
|
|
7712
|
-
|
|
7713
|
-
|
|
7714
|
-
|
|
7706
|
+
const deadline = Date.now() + (limits?.maxMs ?? Number.POSITIVE_INFINITY);
|
|
7707
|
+
const found = [];
|
|
7708
|
+
let truncated = false;
|
|
7709
|
+
const isOverLimit = () => limits != null && (found.length >= limits.maxFiles || Date.now() > deadline);
|
|
7710
|
+
const walk = (absoluteDirectory, relativePrefix) => {
|
|
7711
|
+
if (isOverLimit() == true) {
|
|
7712
|
+
truncated = true;
|
|
7713
|
+
return;
|
|
7714
|
+
}
|
|
7715
|
+
let entries;
|
|
7716
|
+
try {
|
|
7717
|
+
entries = readdirSync(absoluteDirectory, { withFileTypes: true });
|
|
7718
|
+
} catch {
|
|
7719
|
+
return;
|
|
7720
|
+
}
|
|
7721
|
+
for (const entry of entries) {
|
|
7722
|
+
if (truncated == true) {
|
|
7715
7723
|
return;
|
|
7716
7724
|
}
|
|
7717
|
-
|
|
7718
|
-
|
|
7719
|
-
|
|
7720
|
-
|
|
7721
|
-
|
|
7722
|
-
|
|
7723
|
-
|
|
7724
|
-
|
|
7725
|
-
if (entry.
|
|
7726
|
-
if (entry.name.startsWith(".") == true || entry.name == ALWAYS_EXCLUDED_DIRECTORY) {
|
|
7727
|
-
continue;
|
|
7728
|
-
}
|
|
7729
|
-
walk(join2(absoluteDirectory, entry.name), relativePath + "/");
|
|
7725
|
+
if (entry.isSymbolicLink() == true) {
|
|
7726
|
+
continue;
|
|
7727
|
+
}
|
|
7728
|
+
const relativePath = relativePrefix + entry.name;
|
|
7729
|
+
if (excludedNames.has(entry.name) == true || excludedPaths.has(relativePath) == true) {
|
|
7730
|
+
continue;
|
|
7731
|
+
}
|
|
7732
|
+
if (entry.isDirectory() == true) {
|
|
7733
|
+
if (entry.name.startsWith(".") == true || entry.name == ALWAYS_EXCLUDED_DIRECTORY) {
|
|
7730
7734
|
continue;
|
|
7731
7735
|
}
|
|
7732
|
-
|
|
7733
|
-
|
|
7736
|
+
walk(join2(absoluteDirectory, entry.name), relativePath + "/");
|
|
7737
|
+
continue;
|
|
7738
|
+
}
|
|
7739
|
+
if (entry.isFile() == true && /\.md$/i.test(entry.name) == true) {
|
|
7740
|
+
found.push(relativePath);
|
|
7741
|
+
if (isOverLimit() == true) {
|
|
7742
|
+
truncated = true;
|
|
7743
|
+
return;
|
|
7734
7744
|
}
|
|
7735
7745
|
}
|
|
7736
|
-
}
|
|
7737
|
-
|
|
7738
|
-
|
|
7746
|
+
}
|
|
7747
|
+
};
|
|
7748
|
+
walk(root, "");
|
|
7749
|
+
return { paths: found, truncated };
|
|
7750
|
+
}
|
|
7751
|
+
function openLibrary(options) {
|
|
7752
|
+
const root = realpathSync.native(resolve(options.root));
|
|
7753
|
+
if (statSync(root).isDirectory() == false) {
|
|
7754
|
+
throw new Error("root is not a folder: " + root);
|
|
7755
|
+
}
|
|
7756
|
+
let name = basename(root);
|
|
7757
|
+
if (name == "") {
|
|
7758
|
+
name = root;
|
|
7739
7759
|
}
|
|
7760
|
+
const cache = /* @__PURE__ */ new Map();
|
|
7740
7761
|
function refresh() {
|
|
7741
7762
|
const documents = [];
|
|
7742
7763
|
const seen = /* @__PURE__ */ new Set();
|
|
7743
|
-
for (const path of listMarkdownPaths().sort(compareText)) {
|
|
7764
|
+
for (const path of listMarkdownPaths(root, options.exclude).paths.sort(compareText)) {
|
|
7744
7765
|
const absolutePath = join2(root, path);
|
|
7745
7766
|
let modifiedMs;
|
|
7746
7767
|
let size;
|
|
@@ -7885,6 +7906,113 @@ function cutPage(document, start, end, budgetBytes) {
|
|
|
7885
7906
|
return { text: joinLines2(lastLine), lastLine };
|
|
7886
7907
|
}
|
|
7887
7908
|
|
|
7909
|
+
// src/modules/cli/folder-check.ts
|
|
7910
|
+
var LARGEST_FILE_COUNT = 3;
|
|
7911
|
+
var COUNT_LIMITS = { maxFiles: COUNT_MAX_FILES, maxMs: COUNT_MAX_MS };
|
|
7912
|
+
function isFolder(path) {
|
|
7913
|
+
try {
|
|
7914
|
+
return statSync2(path).isDirectory();
|
|
7915
|
+
} catch {
|
|
7916
|
+
return false;
|
|
7917
|
+
}
|
|
7918
|
+
}
|
|
7919
|
+
function comparable(path) {
|
|
7920
|
+
const resolved = resolve2(path);
|
|
7921
|
+
if (process.platform == "win32") {
|
|
7922
|
+
return resolved.toLowerCase();
|
|
7923
|
+
}
|
|
7924
|
+
return resolved;
|
|
7925
|
+
}
|
|
7926
|
+
function isSameFolder(left, right) {
|
|
7927
|
+
return comparable(left) == comparable(right);
|
|
7928
|
+
}
|
|
7929
|
+
function isHomeOrDriveRoot(path) {
|
|
7930
|
+
const resolved = resolve2(path);
|
|
7931
|
+
return isSameFolder(resolved, homedir2()) || isSameFolder(resolved, parse(resolved).root);
|
|
7932
|
+
}
|
|
7933
|
+
function normalizeFolderInput(input2, base = process.cwd()) {
|
|
7934
|
+
let folder = input2.trim();
|
|
7935
|
+
const quoted = /^(["'])(.*)\1$/.exec(folder);
|
|
7936
|
+
if (quoted != null) {
|
|
7937
|
+
folder = quoted[2].trim();
|
|
7938
|
+
}
|
|
7939
|
+
if (folder == "") {
|
|
7940
|
+
return "";
|
|
7941
|
+
}
|
|
7942
|
+
if (folder == "~" || folder.startsWith("~/") || folder.startsWith("~\\")) {
|
|
7943
|
+
folder = homedir2() + folder.slice(1);
|
|
7944
|
+
}
|
|
7945
|
+
return resolve2(base, folder);
|
|
7946
|
+
}
|
|
7947
|
+
function formatFileCount(files, truncated) {
|
|
7948
|
+
let suffix = "";
|
|
7949
|
+
if (truncated == true) {
|
|
7950
|
+
suffix = "+";
|
|
7951
|
+
}
|
|
7952
|
+
if (files == 1 && truncated == false) {
|
|
7953
|
+
return "1 file";
|
|
7954
|
+
}
|
|
7955
|
+
return `${files}${suffix} files`;
|
|
7956
|
+
}
|
|
7957
|
+
function countMarkdownFiles(root, exclude) {
|
|
7958
|
+
if (isFolder(root) == false) {
|
|
7959
|
+
return null;
|
|
7960
|
+
}
|
|
7961
|
+
const scan = listMarkdownPaths(root, exclude, COUNT_LIMITS);
|
|
7962
|
+
return { files: scan.paths.length, truncated: scan.truncated };
|
|
7963
|
+
}
|
|
7964
|
+
function checkFolder(root, exclude) {
|
|
7965
|
+
if (isFolder(root) == false) {
|
|
7966
|
+
return { ok: false, message: `The docs folder does not exist or is not a folder: ${root}` };
|
|
7967
|
+
}
|
|
7968
|
+
const scan = listMarkdownPaths(root, exclude, COUNT_LIMITS);
|
|
7969
|
+
const sized = scan.paths.map((path) => {
|
|
7970
|
+
let bytes = 0;
|
|
7971
|
+
try {
|
|
7972
|
+
bytes = statSync2(join3(root, path)).size;
|
|
7973
|
+
} catch {
|
|
7974
|
+
bytes = 0;
|
|
7975
|
+
}
|
|
7976
|
+
return { path, bytes };
|
|
7977
|
+
});
|
|
7978
|
+
sized.sort((left, right) => right.bytes - left.bytes || compareText(left.path, right.path));
|
|
7979
|
+
return {
|
|
7980
|
+
ok: true,
|
|
7981
|
+
files: sized.length,
|
|
7982
|
+
truncated: scan.truncated,
|
|
7983
|
+
totalBytes: sized.reduce((sum, file2) => sum + file2.bytes, 0),
|
|
7984
|
+
largest: sized.slice(0, LARGEST_FILE_COUNT)
|
|
7985
|
+
};
|
|
7986
|
+
}
|
|
7987
|
+
function summarizeCheck(check2) {
|
|
7988
|
+
if (check2.ok == false) {
|
|
7989
|
+
return "folder not found";
|
|
7990
|
+
}
|
|
7991
|
+
if (check2.truncated == true) {
|
|
7992
|
+
return formatFileCount(check2.files, true);
|
|
7993
|
+
}
|
|
7994
|
+
return `${formatFileCount(check2.files, false)} \xB7 ${formatSize(check2.totalBytes)}`;
|
|
7995
|
+
}
|
|
7996
|
+
function describeCheck(check2) {
|
|
7997
|
+
if (check2.ok == false) {
|
|
7998
|
+
return [check2.message];
|
|
7999
|
+
}
|
|
8000
|
+
const lines = [summarizeCheck(check2)];
|
|
8001
|
+
if (check2.truncated == true) {
|
|
8002
|
+
lines.push(`Stopped counting after ${COUNT_MAX_FILES} files or ${COUNT_MAX_MS / 1e3} seconds. Is this the docs folder?`);
|
|
8003
|
+
return lines;
|
|
8004
|
+
}
|
|
8005
|
+
if (check2.files == 0) {
|
|
8006
|
+
lines.push("No .md files found. Check the folder and the Exclude list.");
|
|
8007
|
+
return lines;
|
|
8008
|
+
}
|
|
8009
|
+
lines.push("Largest:");
|
|
8010
|
+
for (const file2 of check2.largest) {
|
|
8011
|
+
lines.push(` ${file2.path} ${formatSize(file2.bytes)}`);
|
|
8012
|
+
}
|
|
8013
|
+
return lines;
|
|
8014
|
+
}
|
|
8015
|
+
|
|
7888
8016
|
// src/modules/cli/display.ts
|
|
7889
8017
|
var STYLE_CODES = {
|
|
7890
8018
|
dim: "\x1B[2m",
|
|
@@ -8022,67 +8150,6 @@ function renderParts(parts, maxWidth, color) {
|
|
|
8022
8150
|
return line;
|
|
8023
8151
|
}
|
|
8024
8152
|
|
|
8025
|
-
// src/modules/cli/folder-check.ts
|
|
8026
|
-
import { statSync as statSync2 } from "node:fs";
|
|
8027
|
-
import { homedir as homedir2 } from "node:os";
|
|
8028
|
-
import { resolve as resolve2 } from "node:path";
|
|
8029
|
-
var LARGEST_FILE_COUNT = 3;
|
|
8030
|
-
function isFolder(path) {
|
|
8031
|
-
try {
|
|
8032
|
-
return statSync2(path).isDirectory();
|
|
8033
|
-
} catch {
|
|
8034
|
-
return false;
|
|
8035
|
-
}
|
|
8036
|
-
}
|
|
8037
|
-
function normalizeFolderInput(input2) {
|
|
8038
|
-
let folder = input2.trim();
|
|
8039
|
-
const quoted = /^(["'])(.*)\1$/.exec(folder);
|
|
8040
|
-
if (quoted != null) {
|
|
8041
|
-
folder = quoted[2].trim();
|
|
8042
|
-
}
|
|
8043
|
-
if (folder == "") {
|
|
8044
|
-
return "";
|
|
8045
|
-
}
|
|
8046
|
-
if (folder == "~" || folder.startsWith("~/") || folder.startsWith("~\\")) {
|
|
8047
|
-
folder = homedir2() + folder.slice(1);
|
|
8048
|
-
}
|
|
8049
|
-
return resolve2(folder);
|
|
8050
|
-
}
|
|
8051
|
-
function checkFolder(root, exclude) {
|
|
8052
|
-
if (isFolder(root) == false) {
|
|
8053
|
-
return { ok: false, message: `The docs folder does not exist or is not a folder: ${root}` };
|
|
8054
|
-
}
|
|
8055
|
-
const documents = openLibrary({ root, exclude }).refresh();
|
|
8056
|
-
const bySize = [...documents].sort((left, right) => right.totalBytes - left.totalBytes || compareText(left.path, right.path));
|
|
8057
|
-
return {
|
|
8058
|
-
ok: true,
|
|
8059
|
-
files: documents.length,
|
|
8060
|
-
totalBytes: documents.reduce((sum, document) => sum + document.totalBytes, 0),
|
|
8061
|
-
largest: bySize.slice(0, LARGEST_FILE_COUNT).map((document) => ({ path: document.path, bytes: document.totalBytes }))
|
|
8062
|
-
};
|
|
8063
|
-
}
|
|
8064
|
-
function summarizeCheck(check2) {
|
|
8065
|
-
if (check2.ok == false) {
|
|
8066
|
-
return "folder not found";
|
|
8067
|
-
}
|
|
8068
|
-
return `${pluralize(check2.files, "file")} \xB7 ${formatSize(check2.totalBytes)}`;
|
|
8069
|
-
}
|
|
8070
|
-
function describeCheck(check2) {
|
|
8071
|
-
if (check2.ok == false) {
|
|
8072
|
-
return [check2.message];
|
|
8073
|
-
}
|
|
8074
|
-
const lines = [summarizeCheck(check2)];
|
|
8075
|
-
if (check2.files == 0) {
|
|
8076
|
-
lines.push("No .md files found. Check the folder and the Exclude list.");
|
|
8077
|
-
return lines;
|
|
8078
|
-
}
|
|
8079
|
-
lines.push("Largest:");
|
|
8080
|
-
for (const file2 of check2.largest) {
|
|
8081
|
-
lines.push(` ${file2.path} ${formatSize(file2.bytes)}`);
|
|
8082
|
-
}
|
|
8083
|
-
return lines;
|
|
8084
|
-
}
|
|
8085
|
-
|
|
8086
8153
|
// src/modules/cli/input-owner.ts
|
|
8087
8154
|
var InputOwner = class {
|
|
8088
8155
|
constructor(input2) {
|
|
@@ -8138,8 +8205,8 @@ var InputOwner = class {
|
|
|
8138
8205
|
this.pendingText = "";
|
|
8139
8206
|
return rest;
|
|
8140
8207
|
}
|
|
8141
|
-
await new Promise((
|
|
8142
|
-
this.lineWaiter =
|
|
8208
|
+
await new Promise((resolve6) => {
|
|
8209
|
+
this.lineWaiter = resolve6;
|
|
8143
8210
|
this.input.resume();
|
|
8144
8211
|
});
|
|
8145
8212
|
this.lineWaiter = null;
|
|
@@ -8304,6 +8371,9 @@ function writeLine(text2 = "") {
|
|
|
8304
8371
|
function styled(text2, style) {
|
|
8305
8372
|
return styleText(text2, style, getTerminal().color);
|
|
8306
8373
|
}
|
|
8374
|
+
function tickMark() {
|
|
8375
|
+
return styleText(symbolsOf(getTerminal()).tick, "green", getTerminal().color);
|
|
8376
|
+
}
|
|
8307
8377
|
function columnsOf(terminal) {
|
|
8308
8378
|
return terminal.output.columns ?? DEFAULT_COLUMNS;
|
|
8309
8379
|
}
|
|
@@ -8366,7 +8436,7 @@ async function withRawMode(terminal, hideCursor, frame, body) {
|
|
|
8366
8436
|
}
|
|
8367
8437
|
}
|
|
8368
8438
|
function readKeys(terminal, onKey) {
|
|
8369
|
-
return new Promise((
|
|
8439
|
+
return new Promise((resolve6, reject) => {
|
|
8370
8440
|
let finished = false;
|
|
8371
8441
|
const stop = terminal.owner.beginKeys(
|
|
8372
8442
|
(chunk) => {
|
|
@@ -8379,7 +8449,7 @@ function readKeys(terminal, onKey) {
|
|
|
8379
8449
|
if (outcome != void 0) {
|
|
8380
8450
|
finished = true;
|
|
8381
8451
|
stop();
|
|
8382
|
-
|
|
8452
|
+
resolve6(outcome.result);
|
|
8383
8453
|
return;
|
|
8384
8454
|
}
|
|
8385
8455
|
} catch (error62) {
|
|
@@ -8847,6 +8917,122 @@ async function confirm(question, defaultYes, options = {}) {
|
|
|
8847
8917
|
return confirmWithLines(terminal, question, defaultYes);
|
|
8848
8918
|
}
|
|
8849
8919
|
|
|
8920
|
+
// src/modules/cli/quick-add.ts
|
|
8921
|
+
function initialFolderFor(currentFolder) {
|
|
8922
|
+
if (isHomeOrDriveRoot(currentFolder) == true) {
|
|
8923
|
+
return "";
|
|
8924
|
+
}
|
|
8925
|
+
return currentFolder;
|
|
8926
|
+
}
|
|
8927
|
+
async function addFolderAsEntry(home, root) {
|
|
8928
|
+
const catalog = readCatalog(home);
|
|
8929
|
+
const existing = catalog.entries.find((entry) => isSameFolder(entry.root, root));
|
|
8930
|
+
if (existing != null) {
|
|
8931
|
+
writeLine(`Already added as "${existing.name}".`);
|
|
8932
|
+
return { kind: "exists", name: existing.name };
|
|
8933
|
+
}
|
|
8934
|
+
const check2 = checkFolder(root, []);
|
|
8935
|
+
if (check2.ok == false) {
|
|
8936
|
+
writeLine(styled(check2.message, "red"));
|
|
8937
|
+
return { kind: "declined" };
|
|
8938
|
+
}
|
|
8939
|
+
let summary = formatFileCount(check2.files, check2.truncated).replace(/ files?$/, (unit) => ` markdown${unit}`);
|
|
8940
|
+
if (check2.truncated == false) {
|
|
8941
|
+
summary += ` \xB7 ${formatSize(check2.totalBytes)}`;
|
|
8942
|
+
}
|
|
8943
|
+
writeLine(` ${summary}`);
|
|
8944
|
+
if (check2.files == 0) {
|
|
8945
|
+
writeLine(styled("No markdown files were found in this folder.", "yellow"));
|
|
8946
|
+
}
|
|
8947
|
+
const name = defaultEntryName(root, catalog.entries.map((entry) => entry.name));
|
|
8948
|
+
const accepted = await confirm(`Add it as "${name}"?`, check2.files > 0, { transient: true });
|
|
8949
|
+
if (accepted == false) {
|
|
8950
|
+
return { kind: "declined" };
|
|
8951
|
+
}
|
|
8952
|
+
const latest = readCatalog(home);
|
|
8953
|
+
const finalName = defaultEntryName(root, latest.entries.map((entry) => entry.name));
|
|
8954
|
+
latest.entries.push({ name: finalName, root, exclude: [], maxBytes: DEFAULT_MAX_BYTES });
|
|
8955
|
+
writeCatalog(home, latest);
|
|
8956
|
+
writeLine(`${tickMark()} Added ${finalName}. Open a new session to use ${finalName}__read and the other tools.`);
|
|
8957
|
+
return { kind: "added", name: finalName };
|
|
8958
|
+
}
|
|
8959
|
+
async function quickAddFromMenu(home, initialFolder, base) {
|
|
8960
|
+
let candidate = initialFolder;
|
|
8961
|
+
for (; ; ) {
|
|
8962
|
+
let typed;
|
|
8963
|
+
try {
|
|
8964
|
+
typed = await text("Docs folder", { initial: candidate, hint: "the folder that holds your .md files", transient: true });
|
|
8965
|
+
} catch (error62) {
|
|
8966
|
+
if (error62 instanceof PromptBackError) {
|
|
8967
|
+
return { kind: "declined" };
|
|
8968
|
+
}
|
|
8969
|
+
throw error62;
|
|
8970
|
+
}
|
|
8971
|
+
const root = normalizeFolderInput(typed, base);
|
|
8972
|
+
if (root == "") {
|
|
8973
|
+
writeLine(styled("Enter the path of a docs folder.", "red"));
|
|
8974
|
+
candidate = typed;
|
|
8975
|
+
continue;
|
|
8976
|
+
}
|
|
8977
|
+
if (isFolder(root) == false) {
|
|
8978
|
+
writeLine(styled(`The folder does not exist or is not a folder: ${root}`, "red"));
|
|
8979
|
+
candidate = typed;
|
|
8980
|
+
continue;
|
|
8981
|
+
}
|
|
8982
|
+
writeLine(`${tickMark()} Docs folder \xB7 ${root}`);
|
|
8983
|
+
try {
|
|
8984
|
+
return await addFolderAsEntry(home, root);
|
|
8985
|
+
} catch (error62) {
|
|
8986
|
+
if (error62 instanceof PromptBackError) {
|
|
8987
|
+
return { kind: "declined" };
|
|
8988
|
+
}
|
|
8989
|
+
throw error62;
|
|
8990
|
+
}
|
|
8991
|
+
}
|
|
8992
|
+
}
|
|
8993
|
+
|
|
8994
|
+
// src/modules/cli/add-command.ts
|
|
8995
|
+
var EXIT_USAGE = 2;
|
|
8996
|
+
async function runAddCommand(options) {
|
|
8997
|
+
let root = normalizeFolderInput(options.cwd, options.cwd);
|
|
8998
|
+
if (options.folder != null) {
|
|
8999
|
+
root = normalizeFolderInput(options.folder, options.cwd);
|
|
9000
|
+
}
|
|
9001
|
+
if (isHomeOrDriveRoot(root) == true) {
|
|
9002
|
+
options.writeError(`"${root}" is your home folder or a drive root, not a docs folder. Run librian add inside a docs folder, or pass its path: librian add <folder>`);
|
|
9003
|
+
return EXIT_USAGE;
|
|
9004
|
+
}
|
|
9005
|
+
if (isFolder(root) == false) {
|
|
9006
|
+
options.writeError(`The folder does not exist or is not a folder: ${root}`);
|
|
9007
|
+
return EXIT_USAGE;
|
|
9008
|
+
}
|
|
9009
|
+
try {
|
|
9010
|
+
writeLine(`Docs folder \xB7 ${root}`);
|
|
9011
|
+
const outcome = await addFolderAsEntry(options.home, root);
|
|
9012
|
+
if (outcome.kind == "declined") {
|
|
9013
|
+
writeLine("Nothing added.");
|
|
9014
|
+
}
|
|
9015
|
+
if (outcome.kind == "added" && readCatalog(options.home).services.length == 0) {
|
|
9016
|
+
writeLine("Run librian and choose Register to use it in Claude Code or Codex.");
|
|
9017
|
+
}
|
|
9018
|
+
return 0;
|
|
9019
|
+
} catch (error62) {
|
|
9020
|
+
if (error62 instanceof PromptClosedError) {
|
|
9021
|
+
return 0;
|
|
9022
|
+
}
|
|
9023
|
+
if (error62 instanceof PromptBackError) {
|
|
9024
|
+
writeLine("Nothing added.");
|
|
9025
|
+
return 0;
|
|
9026
|
+
}
|
|
9027
|
+
let reason = String(error62);
|
|
9028
|
+
if (error62 instanceof Error) {
|
|
9029
|
+
reason = error62.message;
|
|
9030
|
+
}
|
|
9031
|
+
options.writeError(reason);
|
|
9032
|
+
return 1;
|
|
9033
|
+
}
|
|
9034
|
+
}
|
|
9035
|
+
|
|
8850
9036
|
// src/modules/cli/entry-form.ts
|
|
8851
9037
|
var FORM_ORDER = ["name", "folder", "exclude", "max", "check", "save", "cancel"];
|
|
8852
9038
|
function snapshotOf(draft) {
|
|
@@ -8860,14 +9046,6 @@ function nextRow(current) {
|
|
|
8860
9046
|
return FORM_ORDER[Math.min(position + 1, FORM_ORDER.length - 1)];
|
|
8861
9047
|
}
|
|
8862
9048
|
function buildRows(draft, home, lastCheck) {
|
|
8863
|
-
let nameDetail = draft.name;
|
|
8864
|
-
if (nameDetail == "") {
|
|
8865
|
-
nameDetail = "(from the folder)";
|
|
8866
|
-
}
|
|
8867
|
-
let nameHint = "";
|
|
8868
|
-
if (draft.nameTouched == false) {
|
|
8869
|
-
nameHint = "follows the folder until you change it";
|
|
8870
|
-
}
|
|
8871
9049
|
let folderDetail = draft.root;
|
|
8872
9050
|
let folderKind = "path";
|
|
8873
9051
|
let folderHint = "";
|
|
@@ -8886,7 +9064,7 @@ function buildRows(draft, home, lastCheck) {
|
|
|
8886
9064
|
checkDetail = lastCheck.summary;
|
|
8887
9065
|
}
|
|
8888
9066
|
return [
|
|
8889
|
-
{ value: "name", key: "name", label: "Name", detail:
|
|
9067
|
+
{ value: "name", key: "name", label: "Name", detail: draft.name, hint: "prefixes the tool names" },
|
|
8890
9068
|
{ value: "folder", key: "folder", label: "Docs folder", detail: folderDetail, detailKind: folderKind, hint: folderHint },
|
|
8891
9069
|
{ value: "exclude", key: "exclude", label: "Exclude", detail: excludeDetail, hint: "folder names or paths to skip" },
|
|
8892
9070
|
{ value: "max", key: "max", label: "Max bytes", detail: String(draft.maxBytes), hint: "response size limit" },
|
|
@@ -8899,33 +9077,24 @@ function buildRows(draft, home, lastCheck) {
|
|
|
8899
9077
|
async function openEntryForm(home, editing) {
|
|
8900
9078
|
const catalog = readCatalog(home);
|
|
8901
9079
|
const existing = catalog.entries.find((entry) => entry.name == editing);
|
|
8902
|
-
|
|
8903
|
-
|
|
8904
|
-
|
|
8905
|
-
if (existing != null) {
|
|
8906
|
-
draft = { name: existing.name, nameTouched: true, root: existing.root, exclude: [...existing.exclude], maxBytes: existing.maxBytes };
|
|
8907
|
-
cursor = "name";
|
|
9080
|
+
if (existing == null) {
|
|
9081
|
+
writeLine(styled(`There is no docs folder named "${editing}" any more.`, "red"));
|
|
9082
|
+
return "cancelled";
|
|
8908
9083
|
}
|
|
9084
|
+
const otherNames = () => readCatalog(home).entries.filter((entry) => entry.name != editing).map((entry) => entry.name);
|
|
9085
|
+
let draft = { name: existing.name, root: existing.root, exclude: [...existing.exclude], maxBytes: existing.maxBytes };
|
|
9086
|
+
let cursor = "name";
|
|
8909
9087
|
const openingSnapshot = snapshotOf(draft);
|
|
8910
9088
|
let lastCheck = null;
|
|
8911
|
-
let verifiedKey =
|
|
8912
|
-
|
|
8913
|
-
verifiedKey = checkKeyOf(draft);
|
|
8914
|
-
}
|
|
8915
|
-
let title = "Add docs folder";
|
|
8916
|
-
if (existing != null) {
|
|
8917
|
-
title = `Edit "${existing.name}"`;
|
|
8918
|
-
}
|
|
9089
|
+
let verifiedKey = checkKeyOf(draft);
|
|
9090
|
+
const title = `Edit "${existing.name}"`;
|
|
8919
9091
|
const askName = async () => {
|
|
8920
9092
|
let candidate = draft.name;
|
|
8921
9093
|
for (; ; ) {
|
|
8922
9094
|
const typed = (await text("Name", { initial: candidate, hint: "lowercase letters, digits and _, up to 24", transient: true })).trim();
|
|
8923
9095
|
const problem = checkEntryName(typed, otherNames());
|
|
8924
9096
|
if (problem == null) {
|
|
8925
|
-
|
|
8926
|
-
draft.name = typed;
|
|
8927
|
-
draft.nameTouched = true;
|
|
8928
|
-
}
|
|
9097
|
+
draft.name = typed;
|
|
8929
9098
|
return;
|
|
8930
9099
|
}
|
|
8931
9100
|
writeLine(styled(problem, "red"));
|
|
@@ -8935,12 +9104,6 @@ async function openEntryForm(home, editing) {
|
|
|
8935
9104
|
const askFolder = async () => {
|
|
8936
9105
|
const typed = await text("Docs folder", { initial: draft.root, hint: "the folder that holds your .md files", transient: true });
|
|
8937
9106
|
draft.root = normalizeFolderInput(typed);
|
|
8938
|
-
if (draft.nameTouched == false) {
|
|
8939
|
-
draft.name = "";
|
|
8940
|
-
if (draft.root != "") {
|
|
8941
|
-
draft.name = defaultEntryName(draft.root, otherNames());
|
|
8942
|
-
}
|
|
8943
|
-
}
|
|
8944
9107
|
};
|
|
8945
9108
|
const askExclude = async () => {
|
|
8946
9109
|
const typed = await text("Exclude", { initial: draft.exclude.join(", "), hint: "comma separated, empty for none", transient: true });
|
|
@@ -9004,7 +9167,7 @@ async function openEntryForm(home, editing) {
|
|
|
9004
9167
|
const entry = { name: draft.name, root: draft.root, exclude: draft.exclude, maxBytes: draft.maxBytes };
|
|
9005
9168
|
try {
|
|
9006
9169
|
const latest = readCatalog(home);
|
|
9007
|
-
const position = latest.entries.findIndex((item) => item.name ==
|
|
9170
|
+
const position = latest.entries.findIndex((item) => item.name == existing.name);
|
|
9008
9171
|
if (position >= 0) {
|
|
9009
9172
|
latest.entries[position] = entry;
|
|
9010
9173
|
} else {
|
|
@@ -9098,21 +9261,6 @@ var SERVICE_NAMES = { claude: "Claude Code", codex: "Codex" };
|
|
|
9098
9261
|
var ITEM_PREFIX = "item:";
|
|
9099
9262
|
var ENTRY_PATH_WIDTH = 50;
|
|
9100
9263
|
var HOME_PATH_WIDTH = 40;
|
|
9101
|
-
var LibraryCache = class {
|
|
9102
|
-
libraries = /* @__PURE__ */ new Map();
|
|
9103
|
-
countFiles(entry) {
|
|
9104
|
-
if (isFolder(entry.root) == false) {
|
|
9105
|
-
return null;
|
|
9106
|
-
}
|
|
9107
|
-
const key = JSON.stringify([entry.root, entry.exclude]);
|
|
9108
|
-
let library = this.libraries.get(key);
|
|
9109
|
-
if (library == null) {
|
|
9110
|
-
library = openLibrary({ root: entry.root, exclude: entry.exclude });
|
|
9111
|
-
this.libraries.set(key, library);
|
|
9112
|
-
}
|
|
9113
|
-
return library.refresh().length;
|
|
9114
|
-
}
|
|
9115
|
-
};
|
|
9116
9264
|
function describeServices(catalog) {
|
|
9117
9265
|
if (catalog.services.length == 0) {
|
|
9118
9266
|
return "none registered";
|
|
@@ -9133,13 +9281,13 @@ function describeRuntime(home, context) {
|
|
|
9133
9281
|
return "status unknown";
|
|
9134
9282
|
}
|
|
9135
9283
|
}
|
|
9136
|
-
function buildMainRows(home, context, catalog
|
|
9284
|
+
function buildMainRows(home, context, catalog) {
|
|
9137
9285
|
const rows = [];
|
|
9138
9286
|
for (const entry of catalog.entries) {
|
|
9139
|
-
const
|
|
9287
|
+
const counted = countMarkdownFiles(entry.root, entry.exclude);
|
|
9140
9288
|
let hint = "folder not found";
|
|
9141
|
-
if (
|
|
9142
|
-
hint =
|
|
9289
|
+
if (counted != null) {
|
|
9290
|
+
hint = formatFileCount(counted.files, counted.truncated);
|
|
9143
9291
|
}
|
|
9144
9292
|
rows.push({ value: ITEM_PREFIX + entry.name, label: entry.name, detail: entry.root, detailKind: "path", hint });
|
|
9145
9293
|
}
|
|
@@ -9147,7 +9295,7 @@ function buildMainRows(home, context, catalog, cache) {
|
|
|
9147
9295
|
rows.push("separator");
|
|
9148
9296
|
}
|
|
9149
9297
|
rows.push({ value: "add", key: "add", label: "Add docs folder" });
|
|
9150
|
-
rows.push({ value: "services", key: "services", label: "
|
|
9298
|
+
rows.push({ value: "services", key: "services", label: "Register", hint: describeServices(catalog) });
|
|
9151
9299
|
rows.push({ value: "runtime", key: "runtime", label: "Runtime", hint: describeRuntime(home, context) });
|
|
9152
9300
|
rows.push({ value: "quit", key: "quit", label: "Quit" });
|
|
9153
9301
|
return rows;
|
|
@@ -9225,7 +9373,6 @@ async function runScreen(open2) {
|
|
|
9225
9373
|
}
|
|
9226
9374
|
}
|
|
9227
9375
|
async function runMainMenu(home, context) {
|
|
9228
|
-
const cache = new LibraryCache();
|
|
9229
9376
|
let cursor;
|
|
9230
9377
|
for (; ; ) {
|
|
9231
9378
|
let catalog;
|
|
@@ -9241,7 +9388,7 @@ async function runMainMenu(home, context) {
|
|
|
9241
9388
|
}
|
|
9242
9389
|
let choice;
|
|
9243
9390
|
try {
|
|
9244
|
-
choice = await select(`librian ${context.version} \xB7 ${shortenPath(home, HOME_PATH_WIDTH)}`, buildMainRows(home, context, catalog
|
|
9391
|
+
choice = await select(`librian ${context.version} \xB7 ${shortenPath(home, HOME_PATH_WIDTH)}`, buildMainRows(home, context, catalog), { initial: cursor, transient: true });
|
|
9245
9392
|
} catch (error62) {
|
|
9246
9393
|
if (error62 instanceof PromptBackError) {
|
|
9247
9394
|
return 0;
|
|
@@ -9253,11 +9400,9 @@ async function runMainMenu(home, context) {
|
|
|
9253
9400
|
return 0;
|
|
9254
9401
|
}
|
|
9255
9402
|
if (choice == "add") {
|
|
9256
|
-
|
|
9257
|
-
|
|
9258
|
-
|
|
9259
|
-
cursor = ITEM_PREFIX + newest.name;
|
|
9260
|
-
}
|
|
9403
|
+
const outcome = await quickAddFromMenu(home, initialFolderFor(context.cwd ?? process.cwd()), context.cwd ?? process.cwd());
|
|
9404
|
+
if (outcome.kind != "declined") {
|
|
9405
|
+
cursor = ITEM_PREFIX + outcome.name;
|
|
9261
9406
|
}
|
|
9262
9407
|
} else if (choice == "services") {
|
|
9263
9408
|
await runScreen(() => context.openServicesScreen(home));
|
|
@@ -9281,7 +9426,8 @@ async function openMainMenu(home, context) {
|
|
|
9281
9426
|
|
|
9282
9427
|
// src/modules/mcp/arguments.ts
|
|
9283
9428
|
var USAGE = [
|
|
9284
|
-
"Usage: librian Open the menu (docs folders,
|
|
9429
|
+
"Usage: librian Open the menu (docs folders, register, runtime)",
|
|
9430
|
+
" librian add [<folder>] Add a folder (default: the current folder) as a docs folder, without the menu",
|
|
9285
9431
|
" librian serve --catalog Serve every docs folder of the catalog (what AI services run)",
|
|
9286
9432
|
" librian serve --root <folder> [--exclude <name>]... [--max-bytes <n>]",
|
|
9287
9433
|
" Serve one folder with unprefixed tools",
|
|
@@ -9357,6 +9503,28 @@ function parseServe(argv) {
|
|
|
9357
9503
|
}
|
|
9358
9504
|
return { kind: "serve-root", root, exclude, maxBytes: maxBytes ?? DEFAULT_MAX_BYTES };
|
|
9359
9505
|
}
|
|
9506
|
+
function parseAdd(argv) {
|
|
9507
|
+
let folder = null;
|
|
9508
|
+
for (const token of argv) {
|
|
9509
|
+
if (token == "--help") {
|
|
9510
|
+
return { kind: "help" };
|
|
9511
|
+
}
|
|
9512
|
+
if (token == "--version") {
|
|
9513
|
+
return { kind: "version" };
|
|
9514
|
+
}
|
|
9515
|
+
if (token.startsWith("-") == true) {
|
|
9516
|
+
return { kind: "error", message: `Unknown argument "${token}".` };
|
|
9517
|
+
}
|
|
9518
|
+
if (token == "") {
|
|
9519
|
+
return { kind: "error", message: "The folder must not be empty." };
|
|
9520
|
+
}
|
|
9521
|
+
if (folder != null) {
|
|
9522
|
+
return { kind: "error", message: "librian add takes one folder." };
|
|
9523
|
+
}
|
|
9524
|
+
folder = token;
|
|
9525
|
+
}
|
|
9526
|
+
return { kind: "add", folder };
|
|
9527
|
+
}
|
|
9360
9528
|
function parseArguments(argv) {
|
|
9361
9529
|
if (argv.length == 0) {
|
|
9362
9530
|
return { kind: "menu" };
|
|
@@ -9368,6 +9536,9 @@ function parseArguments(argv) {
|
|
|
9368
9536
|
if (command == "--version") {
|
|
9369
9537
|
return { kind: "version" };
|
|
9370
9538
|
}
|
|
9539
|
+
if (command == "add") {
|
|
9540
|
+
return parseAdd(argv.slice(1));
|
|
9541
|
+
}
|
|
9371
9542
|
if (command == "serve") {
|
|
9372
9543
|
return parseServe(argv.slice(1));
|
|
9373
9544
|
}
|
|
@@ -13562,7 +13733,7 @@ __export(core_exports2, {
|
|
|
13562
13733
|
memoizer: () => memoizer,
|
|
13563
13734
|
mergeValues: () => mergeValues2,
|
|
13564
13735
|
meta: () => meta,
|
|
13565
|
-
parse: () =>
|
|
13736
|
+
parse: () => parse2,
|
|
13566
13737
|
parseAsync: () => parseAsync,
|
|
13567
13738
|
parseURLObject: () => parseURLObject,
|
|
13568
13739
|
prettifyError: () => prettifyError,
|
|
@@ -14782,7 +14953,7 @@ var _parse = (_Err) => {
|
|
|
14782
14953
|
};
|
|
14783
14954
|
return fn;
|
|
14784
14955
|
};
|
|
14785
|
-
var
|
|
14956
|
+
var parse2 = /* @__PURE__ */ _parse($ZodRealError);
|
|
14786
14957
|
var _parseAsync = (_Err) => {
|
|
14787
14958
|
const fn = async (schema, value, _ctx, params) => {
|
|
14788
14959
|
const ctx = _ctx ? { ..._ctx, async: true } : { async: true };
|
|
@@ -14870,18 +15041,18 @@ var validateAsync = async (schema, value, _ctx) => {
|
|
|
14870
15041
|
return result.issues.length === 0;
|
|
14871
15042
|
};
|
|
14872
15043
|
var _encode = (_Err) => {
|
|
14873
|
-
const
|
|
15044
|
+
const parse4 = _parse(_Err);
|
|
14874
15045
|
const fn = (schema, value, _ctx, _params) => {
|
|
14875
15046
|
const ctx = _ctx ? { ..._ctx, direction: "backward" } : { direction: "backward" };
|
|
14876
|
-
return
|
|
15047
|
+
return parse4(schema, value, ctx, finalizeParams(fn, _params));
|
|
14877
15048
|
};
|
|
14878
15049
|
return fn;
|
|
14879
15050
|
};
|
|
14880
15051
|
var encode = /* @__PURE__ */ _encode($ZodRealError);
|
|
14881
15052
|
var _decode = (_Err) => {
|
|
14882
|
-
const
|
|
15053
|
+
const parse4 = _parse(_Err);
|
|
14883
15054
|
const fn = (schema, value, _ctx, _params) => {
|
|
14884
|
-
return
|
|
15055
|
+
return parse4(schema, value, _ctx, finalizeParams(fn, _params));
|
|
14885
15056
|
};
|
|
14886
15057
|
return fn;
|
|
14887
15058
|
};
|
|
@@ -16376,7 +16547,7 @@ function capSize(inst, size) {
|
|
|
16376
16547
|
if (!caps.length)
|
|
16377
16548
|
return;
|
|
16378
16549
|
const cap = Math.min(...caps.map((c) => c[0]));
|
|
16379
|
-
const
|
|
16550
|
+
const parse4 = inst._zod.parse;
|
|
16380
16551
|
inst._zod.parse = (payload, ctx) => {
|
|
16381
16552
|
const n = size(payload.value);
|
|
16382
16553
|
if (n > cap && !ctx?.skipChecks && ctx?.direction !== "backward") {
|
|
@@ -16390,7 +16561,7 @@ function capSize(inst, size) {
|
|
|
16390
16561
|
}
|
|
16391
16562
|
}
|
|
16392
16563
|
}
|
|
16393
|
-
return
|
|
16564
|
+
return parse4(payload, ctx);
|
|
16394
16565
|
};
|
|
16395
16566
|
}
|
|
16396
16567
|
var arraySize = (input2) => Array.isArray(input2) ? input2.length : -1;
|
|
@@ -17935,10 +18106,10 @@ var $ZodFunction = /* @__PURE__ */ $constructor("$ZodFunction", (inst, def) => {
|
|
|
17935
18106
|
throw new Error("implement() must be called with a function");
|
|
17936
18107
|
}
|
|
17937
18108
|
return Object.defineProperty(function(...args) {
|
|
17938
|
-
const parsedArgs = inst._def.input ?
|
|
18109
|
+
const parsedArgs = inst._def.input ? parse2(inst._def.input, args) : args;
|
|
17939
18110
|
const result = Reflect.apply(func, this, parsedArgs);
|
|
17940
18111
|
if (inst._def.output) {
|
|
17941
|
-
return
|
|
18112
|
+
return parse2(inst._def.output, result);
|
|
17942
18113
|
}
|
|
17943
18114
|
return result;
|
|
17944
18115
|
}, "_zod", { value: inst._zod, enumerable: false });
|
|
@@ -18079,7 +18250,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
|
|
|
18079
18250
|
var NONE = 0;
|
|
18080
18251
|
var ASSUMED = 1;
|
|
18081
18252
|
var PROVEN = 2;
|
|
18082
|
-
function isRecursive(inst, stack,
|
|
18253
|
+
function isRecursive(inst, stack, resolve6) {
|
|
18083
18254
|
const cached2 = recursive.get(inst);
|
|
18084
18255
|
if (cached2 !== void 0)
|
|
18085
18256
|
return cached2 ? PROVEN : NONE;
|
|
@@ -18089,7 +18260,7 @@ function isRecursive(inst, stack, resolve7) {
|
|
|
18089
18260
|
let result = NONE;
|
|
18090
18261
|
const check2 = (child) => {
|
|
18091
18262
|
if (result !== PROVEN && child?._zod) {
|
|
18092
|
-
const answer = isRecursive(child, stack,
|
|
18263
|
+
const answer = isRecursive(child, stack, resolve6);
|
|
18093
18264
|
if (answer > result)
|
|
18094
18265
|
result = answer;
|
|
18095
18266
|
}
|
|
@@ -18100,7 +18271,7 @@ function isRecursive(inst, stack, resolve7) {
|
|
|
18100
18271
|
const desc = Object.getOwnPropertyDescriptor(sh, key);
|
|
18101
18272
|
if (spread && !desc.enumerable)
|
|
18102
18273
|
continue;
|
|
18103
|
-
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack,
|
|
18274
|
+
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve6) : NONE;
|
|
18104
18275
|
if (child > answer)
|
|
18105
18276
|
answer = child;
|
|
18106
18277
|
}
|
|
@@ -18164,7 +18335,7 @@ function isRecursive(inst, stack, resolve7) {
|
|
|
18164
18335
|
break;
|
|
18165
18336
|
// `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
|
|
18166
18337
|
case "lazy": {
|
|
18167
|
-
const inner = def._cachedInner ?? (
|
|
18338
|
+
const inner = def._cachedInner ?? (resolve6 ? inst._zod.innerType : void 0);
|
|
18168
18339
|
merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
|
|
18169
18340
|
break;
|
|
18170
18341
|
}
|
|
@@ -29998,7 +30169,7 @@ var ZodMiniType = /* @__PURE__ */ $constructor("ZodMiniType", (inst, def) => {
|
|
|
29998
30169
|
own(this, "with", value);
|
|
29999
30170
|
},
|
|
30000
30171
|
parse(data, params) {
|
|
30001
|
-
return
|
|
30172
|
+
return parse2(this, data, params, { callee: this.parse });
|
|
30002
30173
|
},
|
|
30003
30174
|
parseAsync(data, params) {
|
|
30004
30175
|
return parseAsync(this, data, params, { callee: this.parseAsync });
|
|
@@ -30561,7 +30732,7 @@ __export(external_exports, {
|
|
|
30561
30732
|
optional: () => optional,
|
|
30562
30733
|
output: () => output,
|
|
30563
30734
|
overwrite: () => _overwrite,
|
|
30564
|
-
parse: () =>
|
|
30735
|
+
parse: () => parse3,
|
|
30565
30736
|
parseAsync: () => parseAsync2,
|
|
30566
30737
|
partialRecord: () => partialRecord,
|
|
30567
30738
|
pipe: () => pipe,
|
|
@@ -30889,7 +31060,7 @@ var ZodRealError = /* @__PURE__ */ $constructor("ZodError", initializer2, void 0
|
|
|
30889
31060
|
});
|
|
30890
31061
|
|
|
30891
31062
|
// node_modules/zod/v4/classic/parse.js
|
|
30892
|
-
var
|
|
31063
|
+
var parse3 = /* @__PURE__ */ _parse(ZodRealError);
|
|
30893
31064
|
var parseAsync2 = /* @__PURE__ */ _parseAsync(ZodRealError);
|
|
30894
31065
|
var safeParse3 = /* @__PURE__ */ _safeParse(ZodRealError);
|
|
30895
31066
|
var safeParseAsync3 = /* @__PURE__ */ _safeParseAsync(ZodRealError);
|
|
@@ -31026,7 +31197,7 @@ var ZodType2 = /* @__PURE__ */ $constructor("ZodType", (inst, def) => {
|
|
|
31026
31197
|
util_exports.own(this, "~standard", value);
|
|
31027
31198
|
},
|
|
31028
31199
|
parse: function _parse2(data, params) {
|
|
31029
|
-
return
|
|
31200
|
+
return parse3(this, data, params, { callee: _parse2 });
|
|
31030
31201
|
},
|
|
31031
31202
|
parseAsync: async function _parseAsync2(data, params) {
|
|
31032
31203
|
return await parseAsync2(this, data, params, { callee: _parseAsync2 });
|
|
@@ -36602,7 +36773,7 @@ var Protocol = class {
|
|
|
36602
36773
|
return;
|
|
36603
36774
|
}
|
|
36604
36775
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
36605
|
-
await new Promise((
|
|
36776
|
+
await new Promise((resolve6) => setTimeout(resolve6, pollInterval));
|
|
36606
36777
|
options?.signal?.throwIfAborted();
|
|
36607
36778
|
}
|
|
36608
36779
|
} catch (error62) {
|
|
@@ -36619,7 +36790,7 @@ var Protocol = class {
|
|
|
36619
36790
|
*/
|
|
36620
36791
|
request(request, resultSchema, options) {
|
|
36621
36792
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
36622
|
-
return new Promise((
|
|
36793
|
+
return new Promise((resolve6, reject) => {
|
|
36623
36794
|
const earlyReject = (error62) => {
|
|
36624
36795
|
reject(error62);
|
|
36625
36796
|
};
|
|
@@ -36697,7 +36868,7 @@ var Protocol = class {
|
|
|
36697
36868
|
if (!parseResult.success) {
|
|
36698
36869
|
reject(parseResult.error);
|
|
36699
36870
|
} else {
|
|
36700
|
-
|
|
36871
|
+
resolve6(parseResult.data);
|
|
36701
36872
|
}
|
|
36702
36873
|
} catch (error62) {
|
|
36703
36874
|
reject(error62);
|
|
@@ -36959,12 +37130,12 @@ var Protocol = class {
|
|
|
36959
37130
|
}
|
|
36960
37131
|
} catch {
|
|
36961
37132
|
}
|
|
36962
|
-
return new Promise((
|
|
37133
|
+
return new Promise((resolve6, reject) => {
|
|
36963
37134
|
if (signal.aborted) {
|
|
36964
37135
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
36965
37136
|
return;
|
|
36966
37137
|
}
|
|
36967
|
-
const timeoutId = setTimeout(
|
|
37138
|
+
const timeoutId = setTimeout(resolve6, interval);
|
|
36968
37139
|
signal.addEventListener("abort", () => {
|
|
36969
37140
|
clearTimeout(timeoutId);
|
|
36970
37141
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -38104,7 +38275,7 @@ var McpServer = class {
|
|
|
38104
38275
|
let task = createTaskResult.task;
|
|
38105
38276
|
const pollInterval = task.pollInterval ?? 5e3;
|
|
38106
38277
|
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
38107
|
-
await new Promise((
|
|
38278
|
+
await new Promise((resolve6) => setTimeout(resolve6, pollInterval));
|
|
38108
38279
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
38109
38280
|
if (!updatedTask) {
|
|
38110
38281
|
throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -38768,12 +38939,12 @@ var StdioServerTransport = class {
|
|
|
38768
38939
|
this.onclose?.();
|
|
38769
38940
|
}
|
|
38770
38941
|
send(message) {
|
|
38771
|
-
return new Promise((
|
|
38942
|
+
return new Promise((resolve6) => {
|
|
38772
38943
|
const json2 = serializeMessage(message);
|
|
38773
38944
|
if (this._stdout.write(json2)) {
|
|
38774
|
-
|
|
38945
|
+
resolve6();
|
|
38775
38946
|
} else {
|
|
38776
|
-
this._stdout.once("drain",
|
|
38947
|
+
this._stdout.once("drain", resolve6);
|
|
38777
38948
|
}
|
|
38778
38949
|
});
|
|
38779
38950
|
}
|
|
@@ -39807,12 +39978,12 @@ function catalogReadyLine(version2, home, catalog, opened) {
|
|
|
39807
39978
|
|
|
39808
39979
|
// src/modules/setup/runtime.ts
|
|
39809
39980
|
import { existsSync as existsSync2, lstatSync, mkdirSync as mkdirSync2, readFileSync as readFileSync3, realpathSync as realpathSync3, rmSync as rmSync2 } from "node:fs";
|
|
39810
|
-
import { dirname, join as
|
|
39981
|
+
import { dirname, join as join5, resolve as resolve4 } from "node:path";
|
|
39811
39982
|
|
|
39812
39983
|
// src/modules/setup/commands.ts
|
|
39813
39984
|
import { spawn } from "node:child_process";
|
|
39814
39985
|
import { accessSync, constants, statSync as statSync4 } from "node:fs";
|
|
39815
|
-
import { delimiter, join as
|
|
39986
|
+
import { delimiter, join as join4 } from "node:path";
|
|
39816
39987
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
39817
39988
|
var WINDOWS_SCRIPT_PATTERN = /\.(cmd|bat)$/i;
|
|
39818
39989
|
function quotePart(part) {
|
|
@@ -39841,7 +40012,7 @@ function findOnPath(name, env = process.env, platform = process.platform) {
|
|
|
39841
40012
|
}
|
|
39842
40013
|
for (const directory of directories) {
|
|
39843
40014
|
for (const candidate of candidates) {
|
|
39844
|
-
const path =
|
|
40015
|
+
const path = join4(directory, candidate);
|
|
39845
40016
|
try {
|
|
39846
40017
|
if (statSync4(path).isFile() == false) {
|
|
39847
40018
|
continue;
|
|
@@ -39950,17 +40121,17 @@ var PACKAGE_NAME = "librian";
|
|
|
39950
40121
|
var INSTALL_TIMEOUT_MS = 18e4;
|
|
39951
40122
|
var VIEW_TIMEOUT_MS = 3e4;
|
|
39952
40123
|
function runtimeDirectory(home) {
|
|
39953
|
-
return
|
|
40124
|
+
return join5(home, "runtime");
|
|
39954
40125
|
}
|
|
39955
40126
|
function packageDirectory(home) {
|
|
39956
|
-
return
|
|
40127
|
+
return join5(runtimeDirectory(home), "node_modules", PACKAGE_NAME);
|
|
39957
40128
|
}
|
|
39958
40129
|
function runtimeCliPath(home) {
|
|
39959
|
-
return
|
|
40130
|
+
return join5(packageDirectory(home), "dist", "cli.js");
|
|
39960
40131
|
}
|
|
39961
40132
|
function readPackageVersion(directory) {
|
|
39962
40133
|
try {
|
|
39963
|
-
const parsed = JSON.parse(readFileSync3(
|
|
40134
|
+
const parsed = JSON.parse(readFileSync3(join5(directory, "package.json"), "utf8"));
|
|
39964
40135
|
return typeof parsed.version == "string" ? parsed.version : null;
|
|
39965
40136
|
} catch {
|
|
39966
40137
|
return null;
|
|
@@ -39992,8 +40163,8 @@ function compareVersions(left, right) {
|
|
|
39992
40163
|
function findNpmCli(nodePath) {
|
|
39993
40164
|
const nodeDirectory = dirname(nodePath);
|
|
39994
40165
|
const candidates = [
|
|
39995
|
-
|
|
39996
|
-
|
|
40166
|
+
join5(nodeDirectory, "node_modules", "npm", "bin", "npm-cli.js"),
|
|
40167
|
+
join5(nodeDirectory, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
|
|
39997
40168
|
];
|
|
39998
40169
|
for (const candidate of candidates) {
|
|
39999
40170
|
if (existsSync2(candidate) == true) {
|
|
@@ -40039,24 +40210,6 @@ async function installRuntime(home, spec, toolchain) {
|
|
|
40039
40210
|
}
|
|
40040
40211
|
return { ok: true, message: status.state == "linked" ? `linked to ${status.linkedFrom}` : `${status.version ?? "?"} installed` };
|
|
40041
40212
|
}
|
|
40042
|
-
function checkCheckout(path) {
|
|
40043
|
-
if (existsSync2(path) == false) {
|
|
40044
|
-
return `${path} does not exist.`;
|
|
40045
|
-
}
|
|
40046
|
-
let name;
|
|
40047
|
-
try {
|
|
40048
|
-
name = JSON.parse(readFileSync3(join4(path, "package.json"), "utf8")).name;
|
|
40049
|
-
} catch {
|
|
40050
|
-
return `${path} has no readable package.json.`;
|
|
40051
|
-
}
|
|
40052
|
-
if (name != PACKAGE_NAME) {
|
|
40053
|
-
return `${path} is not the ${PACKAGE_NAME} package.`;
|
|
40054
|
-
}
|
|
40055
|
-
if (existsSync2(join4(path, "dist", "cli.js")) == false) {
|
|
40056
|
-
return `${path} has no dist/cli.js; run \`npm run build\` there first.`;
|
|
40057
|
-
}
|
|
40058
|
-
return null;
|
|
40059
|
-
}
|
|
40060
40213
|
async function latestPublishedVersion(toolchain) {
|
|
40061
40214
|
const viewed = await runNpm(toolchain, ["view", PACKAGE_NAME, "version"], VIEW_TIMEOUT_MS);
|
|
40062
40215
|
if (viewed.ok == false) {
|
|
@@ -40076,12 +40229,9 @@ function uninstallRuntime(home) {
|
|
|
40076
40229
|
rmSync2(directory, { recursive: true, force: true });
|
|
40077
40230
|
}
|
|
40078
40231
|
|
|
40079
|
-
// src/modules/setup/runtime-screen.ts
|
|
40080
|
-
import { resolve as resolve5 } from "node:path";
|
|
40081
|
-
|
|
40082
40232
|
// src/modules/setup/services.ts
|
|
40083
40233
|
import { homedir as homedir3 } from "node:os";
|
|
40084
|
-
import { join as
|
|
40234
|
+
import { join as join6 } from "node:path";
|
|
40085
40235
|
|
|
40086
40236
|
// src/modules/setup/probe.ts
|
|
40087
40237
|
import { spawn as spawn2 } from "node:child_process";
|
|
@@ -40188,7 +40338,7 @@ function defaultServiceDependencies() {
|
|
|
40188
40338
|
return { ...defaultToolchain(), probe: probeServer };
|
|
40189
40339
|
}
|
|
40190
40340
|
function isDefaultHome(home) {
|
|
40191
|
-
return normalizeForCompare(home) == normalizeForCompare(
|
|
40341
|
+
return normalizeForCompare(home) == normalizeForCompare(join6(homedir3(), ".librian"));
|
|
40192
40342
|
}
|
|
40193
40343
|
function expectedRegistration(home, toolchain) {
|
|
40194
40344
|
const environment = {};
|
|
@@ -40403,7 +40553,6 @@ function buildRows2(status, latest, checked) {
|
|
|
40403
40553
|
rows.push({ value: "check", key: "check", label: "Check for updates", hint: checked == true ? "up to date" : void 0 });
|
|
40404
40554
|
}
|
|
40405
40555
|
}
|
|
40406
|
-
rows.push({ value: "link", key: "link", label: "Use a local checkout", hint: "link a built librian folder" });
|
|
40407
40556
|
if (status.state != "missing") {
|
|
40408
40557
|
rows.push({ value: "uninstall", key: "uninstall", label: "Uninstall" });
|
|
40409
40558
|
}
|
|
@@ -40497,25 +40646,6 @@ async function openRuntimeScreen(home, dependencies = defaultServiceDependencies
|
|
|
40497
40646
|
checked = false;
|
|
40498
40647
|
latest = null;
|
|
40499
40648
|
await checkRegisteredServer(home, dependencies);
|
|
40500
|
-
} else if (choice == "link") {
|
|
40501
|
-
let answer;
|
|
40502
|
-
try {
|
|
40503
|
-
answer = await text("Folder of the built librian checkout", { initial: status.linkedFrom, transient: true });
|
|
40504
|
-
} catch (error62) {
|
|
40505
|
-
if (error62 instanceof PromptBackError == false) {
|
|
40506
|
-
throw error62;
|
|
40507
|
-
}
|
|
40508
|
-
continue;
|
|
40509
|
-
}
|
|
40510
|
-
const checkout = resolve5(answer.trim().replace(/^"(.*)"$/, "$1"));
|
|
40511
|
-
const problem = checkCheckout(checkout);
|
|
40512
|
-
if (problem != null) {
|
|
40513
|
-
writeLine(styled(problem, "red"));
|
|
40514
|
-
continue;
|
|
40515
|
-
}
|
|
40516
|
-
writeLine(styled("Linking the checkout\u2026", "dim"));
|
|
40517
|
-
printResult(await installRuntime(home, checkout, dependencies), "link");
|
|
40518
|
-
await checkRegisteredServer(home, dependencies);
|
|
40519
40649
|
} else if (choice == "uninstall") {
|
|
40520
40650
|
await uninstall(home, dependencies);
|
|
40521
40651
|
}
|
|
@@ -40592,7 +40722,7 @@ async function openServicesScreen(home, dependencies = defaultServiceDependencie
|
|
|
40592
40722
|
const needsRuntime = runtimeStatus(home).state == "missing";
|
|
40593
40723
|
const registersSomething = planServiceChanges(statuses, wanted, home, dependencies).some((change) => change.action == "register");
|
|
40594
40724
|
const blockedApply = needsRuntime == true && registersSomething == true ? "install the runtime first" : null;
|
|
40595
|
-
const choice = await select("
|
|
40725
|
+
const choice = await select("Register", buildRows3(statuses, wanted, blockedApply), { initial: cursor, transient: true });
|
|
40596
40726
|
cursor = choice;
|
|
40597
40727
|
if (choice == "back") {
|
|
40598
40728
|
return;
|
|
@@ -40641,7 +40771,7 @@ async function openServicesScreen(home, dependencies = defaultServiceDependencie
|
|
|
40641
40771
|
}
|
|
40642
40772
|
|
|
40643
40773
|
// src/cli.ts
|
|
40644
|
-
var
|
|
40774
|
+
var EXIT_USAGE2 = 2;
|
|
40645
40775
|
async function main() {
|
|
40646
40776
|
const parsed = parseArguments(process.argv.slice(2));
|
|
40647
40777
|
if (parsed.kind == "help") {
|
|
@@ -40657,7 +40787,16 @@ async function main() {
|
|
|
40657
40787
|
if (parsed.kind == "error") {
|
|
40658
40788
|
writeStderrLine(`librian: ${parsed.message}`);
|
|
40659
40789
|
writeStderrLine(USAGE);
|
|
40660
|
-
process.exitCode =
|
|
40790
|
+
process.exitCode = EXIT_USAGE2;
|
|
40791
|
+
return;
|
|
40792
|
+
}
|
|
40793
|
+
if (parsed.kind == "add") {
|
|
40794
|
+
process.exitCode = await runAddCommand({
|
|
40795
|
+
home: librianHome(),
|
|
40796
|
+
folder: parsed.folder,
|
|
40797
|
+
cwd: process.cwd(),
|
|
40798
|
+
writeError: (line) => writeStderrLine(`librian: ${line}`)
|
|
40799
|
+
});
|
|
40661
40800
|
return;
|
|
40662
40801
|
}
|
|
40663
40802
|
if (parsed.kind == "menu") {
|
|
@@ -40693,7 +40832,7 @@ async function main() {
|
|
|
40693
40832
|
writeStderrLine(catalogReadyLine(package_default.version, home, catalog, opened));
|
|
40694
40833
|
return;
|
|
40695
40834
|
}
|
|
40696
|
-
const root =
|
|
40835
|
+
const root = resolve5(parsed.root);
|
|
40697
40836
|
let isFolder3 = false;
|
|
40698
40837
|
try {
|
|
40699
40838
|
isFolder3 = statSync5(root).isDirectory();
|
|
@@ -40702,7 +40841,7 @@ async function main() {
|
|
|
40702
40841
|
}
|
|
40703
40842
|
if (isFolder3 == false) {
|
|
40704
40843
|
writeStderrLine(`librian: root "${root}" does not exist or is not a folder.`);
|
|
40705
|
-
process.exitCode =
|
|
40844
|
+
process.exitCode = EXIT_USAGE2;
|
|
40706
40845
|
return;
|
|
40707
40846
|
}
|
|
40708
40847
|
const library = openLibrary({ root, exclude: parsed.exclude });
|
package/package.json
CHANGED