@any-nav/core 0.0.0-stage → 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/CHANGELOG.md +21 -0
- package/LICENSE +21 -0
- package/README.md +69 -2
- package/dist/bindings.d.ts +10 -0
- package/dist/bindings.js +51 -0
- package/dist/bindings.js.map +1 -0
- package/dist/config.d.ts +39 -0
- package/dist/config.js +177 -0
- package/dist/config.js.map +1 -0
- package/dist/description.d.ts +31 -0
- package/dist/description.js +273 -0
- package/dist/description.js.map +1 -0
- package/dist/engine.d.ts +33 -0
- package/dist/engine.js +43 -0
- package/dist/engine.js.map +1 -0
- package/dist/expand.d.ts +33 -0
- package/dist/expand.js +61 -0
- package/dist/expand.js.map +1 -0
- package/dist/facade.d.ts +93 -0
- package/dist/facade.js +384 -0
- package/dist/facade.js.map +1 -0
- package/dist/formats/agents-md.d.ts +15 -0
- package/dist/formats/agents-md.js +30 -0
- package/dist/formats/agents-md.js.map +1 -0
- package/dist/formats/generic-markdown.d.ts +9 -0
- package/dist/formats/generic-markdown.js +18 -0
- package/dist/formats/generic-markdown.js.map +1 -0
- package/dist/gitignore.d.ts +9 -0
- package/dist/gitignore.js +110 -0
- package/dist/gitignore.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/json.d.ts +6 -0
- package/dist/json.js +48 -0
- package/dist/json.js.map +1 -0
- package/dist/path-hints.d.ts +14 -0
- package/dist/path-hints.js +60 -0
- package/dist/path-hints.js.map +1 -0
- package/dist/query.d.ts +104 -0
- package/dist/query.js +434 -0
- package/dist/query.js.map +1 -0
- package/dist/render.d.ts +105 -0
- package/dist/render.js +209 -0
- package/dist/render.js.map +1 -0
- package/dist/scan.d.ts +46 -0
- package/dist/scan.js +301 -0
- package/dist/scan.js.map +1 -0
- package/dist/search-engine.d.ts +36 -0
- package/dist/search-engine.js +54 -0
- package/dist/search-engine.js.map +1 -0
- package/dist/types.d.ts +256 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +31 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 — 2026-10-21
|
|
4
|
+
|
|
5
|
+
First registry release.
|
|
6
|
+
|
|
7
|
+
- Public SDK surface: the `createAnyNav` / `listFolder` facade, config
|
|
8
|
+
(`loadConfigFile` / `parseConfig` / `ConfigError`), the budget-guard
|
|
9
|
+
engine, render / `materialize` with built-in `agents-md` and
|
|
10
|
+
`generic-markdown` formats, and the query faces (`routingView`,
|
|
11
|
+
`curatedView`, `candidateFeed`, the search kernel, `readSource`).
|
|
12
|
+
- `routingViewJson` — the routing projection serialized as pure JSON, the
|
|
13
|
+
machine face for CLI `--json` and library consumers.
|
|
14
|
+
- Scanning now composes the split-out zero-dependency `@any-nav/scan`
|
|
15
|
+
package and re-exports its full face; embedders who only need the engine
|
|
16
|
+
can depend on `@any-nav/scan` directly.
|
|
17
|
+
- Listings compose the structure-only walk — depth-bounded views hydrate
|
|
18
|
+
only the rows they render, not every file under the root.
|
|
19
|
+
- Budget cuts surface as lower bounds: the routing face carries
|
|
20
|
+
`countIncompletePaths`; rendered rows show ≥N floors with the
|
|
21
|
+
`count-truncated-at-limit` diagnostic.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CatheadOwl
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,70 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @any-nav/core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> The Any-Nav SDK: scan a Markdown folder, guard it against a context budget, render navigation fragments, and answer list / search / read.
|
|
4
|
+
|
|
5
|
+
`@any-nav/core` is the read-only SDK behind Any-Nav. It composes the
|
|
6
|
+
zero-dependency scan engine (`@any-nav/scan` — its full face is re-exported
|
|
7
|
+
here) with: strict `any-nav.toml` config parsing, curation of default
|
|
8
|
+
expansion via the config's `local` list, the budget-guard engine, rendering
|
|
9
|
+
of the canonical navigation fragment for registered formats, and the query
|
|
10
|
+
faces — `list(path)` with its rows / markdown renderings and JSON
|
|
11
|
+
serialization, the search kernel, `read`, and the candidate feed.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
npm install @any-nav/core
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
The one-call face — list a folder under its nearest governing config (pure
|
|
22
|
+
defaults when there is none):
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { listFolder, routingViewJson } from '@any-nav/core';
|
|
26
|
+
|
|
27
|
+
const listing = await listFolder('docs'); // optional query: { configPath, depth, commandRunner }
|
|
28
|
+
const machine = routingViewJson(listing); // the routing projection as a pure JSON string
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The full facade:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { createAnyNav } from '@any-nav/core';
|
|
35
|
+
|
|
36
|
+
const app = await createAnyNav({ configPath: 'any-nav.toml' }); // or { config, cwd }
|
|
37
|
+
|
|
38
|
+
const sources = app.sources(); // the config root as the single implicit source
|
|
39
|
+
const exposure = app.decideExposure(sources[0]); // budget-guard reading — warns, never collapses
|
|
40
|
+
const fragment = app.render(exposure, 'agents-md');
|
|
41
|
+
const next = app.materialize(current, fragment); // pure splice — the caller writes the file
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Config
|
|
45
|
+
|
|
46
|
+
`any-nav.toml` states only exceptions; every key defaults:
|
|
47
|
+
|
|
48
|
+
- `local` — curation list: paths expanded into the default listing.
|
|
49
|
+
- `budget` — the context-budget guard (over budget warns, never silently
|
|
50
|
+
collapses).
|
|
51
|
+
- `scan` — scan boundary policy (`excludeDirs` / `excludeFiles` /
|
|
52
|
+
`excludeDotEntries` / `respectGitignore` / `maxFiles`), additive over the
|
|
53
|
+
engine's built-in floor.
|
|
54
|
+
|
|
55
|
+
## Boundaries this SDK keeps
|
|
56
|
+
|
|
57
|
+
- Read-only over the filesystem: no writes, no processes, no network, no
|
|
58
|
+
transport or LLM-client imports. `materialize()` returns new text — every
|
|
59
|
+
write belongs to the adapter.
|
|
60
|
+
- The unified list model: `list(path)` is the product face; governance and
|
|
61
|
+
expansion live here, adapters only transport.
|
|
62
|
+
- `NavItem { name, description, locator }` (from `@any-nav/scan`) is the only
|
|
63
|
+
knowledge model.
|
|
64
|
+
- Embedders that only need the scan engine can depend on `@any-nav/scan`
|
|
65
|
+
directly and carry no SDK dependencies (smol-toml, fast-bm25 live on this
|
|
66
|
+
package's config and search paths, not the engine's).
|
|
67
|
+
|
|
68
|
+
## License
|
|
69
|
+
|
|
70
|
+
MIT
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bindings: passive-push declarations (mode ④).
|
|
3
|
+
*
|
|
4
|
+
* Interface only — the harness extracts paths from prompts and tool
|
|
5
|
+
* calls and calls resolve(); core never triggers. Glob support is a
|
|
6
|
+
* small in-house matcher (** across separators, * and ? within one).
|
|
7
|
+
*/
|
|
8
|
+
import type { NavSource, PushResolution } from './types.ts';
|
|
9
|
+
export declare function globToRegExp(pattern: string): RegExp;
|
|
10
|
+
export declare function resolvePush(sources: NavSource[], path: string): PushResolution;
|
package/dist/bindings.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export function globToRegExp(pattern) {
|
|
2
|
+
const segments = pattern.replace(/\\/g, '/').split('/');
|
|
3
|
+
let source = '';
|
|
4
|
+
for (let i = 0; i < segments.length; i++) {
|
|
5
|
+
const segment = segments[i];
|
|
6
|
+
const isLast = i === segments.length - 1;
|
|
7
|
+
if (segment === '**') {
|
|
8
|
+
// Trailing ** matches any tail; mid-pattern ** matches whole directories.
|
|
9
|
+
source += isLast ? '.*' : '(?:.*/)?';
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
source += segment
|
|
13
|
+
.split('')
|
|
14
|
+
.map((ch) => (ch === '*' ? '[^/]*' : ch === '?' ? '[^/]' : escapeRegExp(ch)))
|
|
15
|
+
.join('');
|
|
16
|
+
if (!isLast)
|
|
17
|
+
source += '/';
|
|
18
|
+
}
|
|
19
|
+
return new RegExp(`^${source}$`);
|
|
20
|
+
}
|
|
21
|
+
export function resolvePush(sources, path) {
|
|
22
|
+
const normalized = path.replace(/\\/g, '/').replace(/^\.\//, '').replace(/^\/+/, '');
|
|
23
|
+
const items = [];
|
|
24
|
+
for (const source of sources) {
|
|
25
|
+
for (const binding of source.config.bind ?? []) {
|
|
26
|
+
if (!globToRegExp(binding.match).test(normalized))
|
|
27
|
+
continue;
|
|
28
|
+
for (const itemPath of binding.items) {
|
|
29
|
+
const item = findItem(source.tree, itemPath.replace(/\\/g, '/').replace(/^\.\//, ''));
|
|
30
|
+
if (item !== null)
|
|
31
|
+
items.push(item);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return { matched: items.length > 0, items };
|
|
36
|
+
}
|
|
37
|
+
function findItem(tree, relPath) {
|
|
38
|
+
const segments = relPath.split('/').filter((s) => s !== '');
|
|
39
|
+
let node = tree;
|
|
40
|
+
for (const segment of segments) {
|
|
41
|
+
const next = node.children?.find((c) => c.name === segment || (c.kind === 'file' && `${c.name}.md` === segment.toLowerCase()));
|
|
42
|
+
if (!next)
|
|
43
|
+
return null;
|
|
44
|
+
node = next;
|
|
45
|
+
}
|
|
46
|
+
return node.kind === 'file' ? node.item ?? null : null;
|
|
47
|
+
}
|
|
48
|
+
function escapeRegExp(ch) {
|
|
49
|
+
return /[.*+?^${}()|[\]\\]/.test(ch) ? `\\${ch}` : ch;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=bindings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bindings.js","sourceRoot":"","sources":["../src/bindings.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ScanConfig } from '@any-nav/scan';
|
|
2
|
+
export interface BudgetConfig {
|
|
3
|
+
/** Share of the context budget navigation may occupy, 0 < share ≤ 1. */
|
|
4
|
+
share?: number;
|
|
5
|
+
/** Hard cap in estimated tokens. */
|
|
6
|
+
maxTokens?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface AnyNavConfig {
|
|
9
|
+
/**
|
|
10
|
+
* Curated default-expansion list (the `local` kind family; external /
|
|
11
|
+
* command members are parked in a trigger unit). Entries are
|
|
12
|
+
* config-root-relative folder paths; a listed folder's row expands to
|
|
13
|
+
* its direct children. Membership says expansion, not existence —
|
|
14
|
+
* folders off the list stay addressable by path. Optional; default = no
|
|
15
|
+
* expansion (depth-0 everywhere).
|
|
16
|
+
*/
|
|
17
|
+
local?: string[];
|
|
18
|
+
budget?: BudgetConfig;
|
|
19
|
+
scan?: ScanConfig;
|
|
20
|
+
/**
|
|
21
|
+
* Declared entry commands (2026-10-16): full CLI command lines an agent
|
|
22
|
+
* runs once when starting here, e.g. "coggit instructions". Pure
|
|
23
|
+
* declarations — any-nav never executes them and never reads their
|
|
24
|
+
* output; the list faces render one leading procedure line. Optional;
|
|
25
|
+
* default = none.
|
|
26
|
+
*/
|
|
27
|
+
commands?: string[];
|
|
28
|
+
}
|
|
29
|
+
export declare class ConfigError extends Error {
|
|
30
|
+
constructor(message: string);
|
|
31
|
+
}
|
|
32
|
+
export declare function parseConfig(text: string): AnyNavConfig;
|
|
33
|
+
export declare function loadConfigFile(configPath: string): Promise<AnyNavConfig>;
|
|
34
|
+
/**
|
|
35
|
+
* Normalize one local entry to a clean config-root-relative POSIX path.
|
|
36
|
+
* The unit is the path — no globs, no numeric depths (Any principle: an
|
|
37
|
+
* entry states "expand this folder", containment merges trees for free).
|
|
38
|
+
*/
|
|
39
|
+
export declare function normalizeLocalPath(entry: string, where: string): string;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config: parse and strictly validate any-nav.toml.
|
|
3
|
+
*
|
|
4
|
+
* The file only states exceptions — scan / local / budget / commands are
|
|
5
|
+
* the whole key set (framework decision 9, unified list model; commands
|
|
6
|
+
* joined 2026-10-16, entry-procedures argumentation): scan bounds the
|
|
7
|
+
* subtree, local curates default expansion, budget guards size, commands
|
|
8
|
+
* declares entry procedure. Unknown keys are rejected rather than ignored.
|
|
9
|
+
*/
|
|
10
|
+
import { readFile } from 'node:fs/promises';
|
|
11
|
+
import { parse } from 'smol-toml';
|
|
12
|
+
export class ConfigError extends Error {
|
|
13
|
+
constructor(message) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = 'ConfigError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function parseConfig(text) {
|
|
19
|
+
let raw;
|
|
20
|
+
try {
|
|
21
|
+
raw = parse(text);
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
throw new ConfigError(`TOML syntax error: ${err.message}`);
|
|
25
|
+
}
|
|
26
|
+
if (typeof raw !== 'object' || raw === null) {
|
|
27
|
+
throw new ConfigError('config must be a TOML table');
|
|
28
|
+
}
|
|
29
|
+
const obj = raw;
|
|
30
|
+
const unknownTop = Object.keys(obj).filter((k) => k !== 'local' && k !== 'budget' && k !== 'scan' && k !== 'commands');
|
|
31
|
+
if (unknownTop.length > 0) {
|
|
32
|
+
throw new ConfigError(`unknown top-level keys: ${unknownTop.join(', ')}`);
|
|
33
|
+
}
|
|
34
|
+
const local = obj.local === undefined ? undefined : parseLocal(obj.local);
|
|
35
|
+
const budget = obj.budget === undefined ? undefined : parseBudget(obj.budget);
|
|
36
|
+
const scan = obj.scan === undefined ? undefined : parseScan(obj.scan);
|
|
37
|
+
const commands = obj.commands === undefined ? undefined : parseCommands(obj.commands);
|
|
38
|
+
const config = {};
|
|
39
|
+
if (local !== undefined)
|
|
40
|
+
config.local = local;
|
|
41
|
+
if (budget !== undefined)
|
|
42
|
+
config.budget = budget;
|
|
43
|
+
if (scan !== undefined)
|
|
44
|
+
config.scan = scan;
|
|
45
|
+
if (commands !== undefined)
|
|
46
|
+
config.commands = commands;
|
|
47
|
+
return config;
|
|
48
|
+
}
|
|
49
|
+
export async function loadConfigFile(configPath) {
|
|
50
|
+
const text = await readFile(configPath, 'utf8');
|
|
51
|
+
return parseConfig(text);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Normalize one local entry to a clean config-root-relative POSIX path.
|
|
55
|
+
* The unit is the path — no globs, no numeric depths (Any principle: an
|
|
56
|
+
* entry states "expand this folder", containment merges trees for free).
|
|
57
|
+
*/
|
|
58
|
+
export function normalizeLocalPath(entry, where) {
|
|
59
|
+
if (typeof entry !== 'string' || entry.trim() === '') {
|
|
60
|
+
throw new ConfigError(`${where} must be a non-empty path string`);
|
|
61
|
+
}
|
|
62
|
+
if (/[*?[\]]/.test(entry)) {
|
|
63
|
+
throw new ConfigError(`${where} must be a plain path, not a glob: ${entry}`);
|
|
64
|
+
}
|
|
65
|
+
const normalized = entry.replace(/\\/g, '/').replace(/^\.\/+/, '').replace(/\/+$/, '');
|
|
66
|
+
if (normalized === '' || normalized === '.') {
|
|
67
|
+
throw new ConfigError(`${where} must name a folder under the config root, not the root itself: ${entry}`);
|
|
68
|
+
}
|
|
69
|
+
const segments = normalized.split('/');
|
|
70
|
+
if (segments.some((s) => s === '' || s === '.')) {
|
|
71
|
+
throw new ConfigError(`${where} is not a clean relative path: ${entry}`);
|
|
72
|
+
}
|
|
73
|
+
if (segments.includes('..')) {
|
|
74
|
+
throw new ConfigError(`${where} must stay under the config root: ${entry}`);
|
|
75
|
+
}
|
|
76
|
+
if (/^[a-zA-Z]:/.test(normalized) || normalized.startsWith('/')) {
|
|
77
|
+
throw new ConfigError(`${where} must be config-root-relative, not absolute: ${entry}`);
|
|
78
|
+
}
|
|
79
|
+
return segments.join('/');
|
|
80
|
+
}
|
|
81
|
+
function parseLocal(raw) {
|
|
82
|
+
if (!Array.isArray(raw) || raw.length === 0) {
|
|
83
|
+
throw new ConfigError('local must be a non-empty array of folder paths (omit the key for no expansion)');
|
|
84
|
+
}
|
|
85
|
+
const seen = new Set();
|
|
86
|
+
const out = [];
|
|
87
|
+
raw.forEach((entry, i) => {
|
|
88
|
+
const normalized = normalizeLocalPath(entry, `local[${i}]`);
|
|
89
|
+
if (seen.has(normalized)) {
|
|
90
|
+
throw new ConfigError(`local lists the same path twice: ${normalized}`);
|
|
91
|
+
}
|
|
92
|
+
seen.add(normalized);
|
|
93
|
+
out.push(normalized);
|
|
94
|
+
});
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
97
|
+
function parseScan(raw) {
|
|
98
|
+
if (typeof raw !== 'object' || raw === null) {
|
|
99
|
+
throw new ConfigError('scan must be a table');
|
|
100
|
+
}
|
|
101
|
+
const obj = raw;
|
|
102
|
+
const known = ['excludeDirs', 'excludeFiles', 'excludeDotEntries', 'respectGitignore', 'maxFiles'];
|
|
103
|
+
const unknown = Object.keys(obj).filter((k) => !known.includes(k));
|
|
104
|
+
if (unknown.length > 0) {
|
|
105
|
+
throw new ConfigError(`unknown keys in scan: ${unknown.join(', ')}`);
|
|
106
|
+
}
|
|
107
|
+
const scan = {};
|
|
108
|
+
for (const key of ['excludeDirs', 'excludeFiles']) {
|
|
109
|
+
const value = obj[key];
|
|
110
|
+
if (value === undefined)
|
|
111
|
+
continue;
|
|
112
|
+
if (!Array.isArray(value) || !value.every((s) => typeof s === 'string' && s.trim() !== '')) {
|
|
113
|
+
throw new ConfigError(`scan.${key} must be an array of non-empty strings`);
|
|
114
|
+
}
|
|
115
|
+
if (value.length === 0) {
|
|
116
|
+
throw new ConfigError(`scan.${key} must not be empty — omit the key to keep just the built-in defaults (it appends, never replaces)`);
|
|
117
|
+
}
|
|
118
|
+
scan[key] = value;
|
|
119
|
+
}
|
|
120
|
+
for (const key of ['excludeDotEntries', 'respectGitignore']) {
|
|
121
|
+
const value = obj[key];
|
|
122
|
+
if (value === undefined)
|
|
123
|
+
continue;
|
|
124
|
+
if (typeof value !== 'boolean') {
|
|
125
|
+
throw new ConfigError(`scan.${key} must be a boolean`);
|
|
126
|
+
}
|
|
127
|
+
scan[key] = value;
|
|
128
|
+
}
|
|
129
|
+
if (obj.maxFiles !== undefined) {
|
|
130
|
+
if (typeof obj.maxFiles !== 'number' || !Number.isInteger(obj.maxFiles) || obj.maxFiles <= 0) {
|
|
131
|
+
throw new ConfigError('scan.maxFiles must be a positive integer');
|
|
132
|
+
}
|
|
133
|
+
scan.maxFiles = obj.maxFiles;
|
|
134
|
+
}
|
|
135
|
+
return scan;
|
|
136
|
+
}
|
|
137
|
+
function parseCommands(raw) {
|
|
138
|
+
if (!Array.isArray(raw) || raw.length === 0) {
|
|
139
|
+
throw new ConfigError('commands must be a non-empty array of CLI command lines (omit the key for none)');
|
|
140
|
+
}
|
|
141
|
+
const seen = new Set();
|
|
142
|
+
raw.forEach((entry, i) => {
|
|
143
|
+
if (typeof entry !== 'string' || entry.trim() === '') {
|
|
144
|
+
throw new ConfigError(`commands[${i}] must be a non-empty command line string`);
|
|
145
|
+
}
|
|
146
|
+
if (seen.has(entry)) {
|
|
147
|
+
throw new ConfigError(`commands lists the same command twice: ${entry}`);
|
|
148
|
+
}
|
|
149
|
+
seen.add(entry);
|
|
150
|
+
});
|
|
151
|
+
return raw;
|
|
152
|
+
}
|
|
153
|
+
function parseBudget(raw) {
|
|
154
|
+
if (typeof raw !== 'object' || raw === null) {
|
|
155
|
+
throw new ConfigError('budget must be a table');
|
|
156
|
+
}
|
|
157
|
+
const obj = raw;
|
|
158
|
+
const unknown = Object.keys(obj).filter((k) => k !== 'share' && k !== 'maxTokens');
|
|
159
|
+
if (unknown.length > 0) {
|
|
160
|
+
throw new ConfigError(`unknown keys in budget: ${unknown.join(', ')}`);
|
|
161
|
+
}
|
|
162
|
+
const budget = {};
|
|
163
|
+
if (obj.share !== undefined) {
|
|
164
|
+
if (typeof obj.share !== 'number' || !(obj.share > 0 && obj.share <= 1)) {
|
|
165
|
+
throw new ConfigError('budget.share must be a number in (0, 1]');
|
|
166
|
+
}
|
|
167
|
+
budget.share = obj.share;
|
|
168
|
+
}
|
|
169
|
+
if (obj.maxTokens !== undefined) {
|
|
170
|
+
if (typeof obj.maxTokens !== 'number' || !Number.isInteger(obj.maxTokens) || obj.maxTokens <= 0) {
|
|
171
|
+
throw new ConfigError('budget.maxTokens must be a positive integer');
|
|
172
|
+
}
|
|
173
|
+
budget.maxTokens = obj.maxTokens;
|
|
174
|
+
}
|
|
175
|
+
return budget;
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAiClC,MAAM,OAAO,WAAY,SAAQ,KAAK;IACpC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF;AAED,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,IAAI,GAAY,CAAC;IACjB,IAAI,CAAC;QACH,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,WAAW,CAAC,sBAAuB,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;IACxE,CAAC;IACD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QAC5C,MAAM,IAAI,WAAW,CAAC,6BAA6B,CAAC,CAAC;IACvD,CAAC;IACD,MAAM,GAAG,GAAG,GAA8B,CAAC;IAC3C,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CACxC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,UAAU,CAC3E,CAAC;IACF,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,WAAW,CAAC,2BAA2B,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5E,CAAC;IACD,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC1E,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9E,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACtE,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACtF,MAAM,MAAM,GAAiB,EAAE,CAAC;IAChC,IAAI,KAAK,KAAK,SAAS;QAAE,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;IAC9C,IAAI,MAAM,KAAK,SAAS;QAAE,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC;IACjD,IAAI,IAAI,KAAK,SAAS;QAAE,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;IAC3C,IAAI,QAAQ,KAAK,SAAS;QAAE,MAAM,CAAC,QAAQ,GAAG,QAAQ,CAAC;IACvD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,UAAkB;IACrD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAChD,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC;AAC3B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAa,EAAE,KAAa;IAC7D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACrD,MAAM,IAAI,WAAW,CAAC,GAAG,KAAK,kCAAkC,CAAC,CAAC;IACpE,CAAC;IACD,IAAI,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,WAAW,CAAC,GAAG,KAAK,sCAAsC,KAAK,EAAE,CAAC,CAAC;IAC/E,CAAC;IACD,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACvF,IAAI,UAAU,KAAK,EAAE,IAAI,UAAU,KAAK,GAAG,EAAE,CAAC;QAC5C,MAAM,IAAI,WAAW,CAAC,GAAG,KAAK,mEAAmE,KAAK,EAAE,CAAC,CAAC;IAC5G,CAAC;IACD,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACvC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,WAAW,CAAC,GAAG,KAAK,kCAAkC,KAAK,EAAE,CAAC,CAAC;IAC3E,CAAC;IACD,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,WAAW,CAAC,GAAG,KAAK,qCAAqC,KAAK,EAAE,CAAC,CAAC;IAC9E,CAAC;IACD,IAAI,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,WAAW,CAAC,GAAG,KAAK,gDAAgD,KAAK,EAAE,CAAC,CAAC;IACzF,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,UAAU,CAAC,GAAY;IAC9B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5C,MAAM,IAAI,WAAW,CAAC,iFAAiF,CAAC,CAAC;IAC3G,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,GAAG,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QACvB,MAAM,UAAU,GAAG,kBAAkB,CAAC,KAAe,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC;QACtE,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,WAAW,CAAC,oCAAoC,UAAU,EAAE,CAAC,CAAC;QAC1E,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACrB,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;IACH,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,SAAS,CAAC,GAAY;IAC7B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QAC5C,MAAM,IAAI,WAAW,CAAC,sBAAsB,CAAC,CAAC;IAChD,CAAC;IACD,MAAM,GAAG,GAAG,GAA8B,CAAC;IAC3C,MAAM,KAAK,GAAG,CAAC,aAAa,EAAE,cAAc,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,UAAU,CAAC,CAAC;IACnG,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACnE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,WAAW,CAAC,yBAAyB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvE,CAAC;IACD,MAAM,IAAI,GAAe,EAAE,CAAC;IAC5B,KAAK,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE,cAAc,CAAU,EAAE,CAAC;QAC3D,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;QACvB,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAClC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;YAC3F,MAAM,IAAI,WAAW,CAAC,QAAQ,GAAG,wCAAwC,CAAC,CAAC;QAC7E,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,WAAW,CAAC,QAAQ,GAAG,mGAAmG,CAAC,CAAC;QACxI,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,GAAG,KAAiB,CAAC;IAChC,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,CAAC,mBAAmB,EAAE,kBAAkB,CAAU,EAAE,CAAC;QACrE,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;QACvB,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAClC,IAAI,OAAO,KAAK,KAAK,SAAS,EAAE,CAAC;YAC/B,MAAM,IAAI,WAAW,CAAC,QAAQ,GAAG,oBAAoB,CAAC,CAAC;QACzD,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACpB,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC/B,IAAI,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,QAAQ,IAAI,CAAC,EAAE,CAAC;YAC7F,MAAM,IAAI,WAAW,CAAC,0CAA0C,CAAC,CAAC;QACpE,CAAC;QACD,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;IAC/B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,aAAa,CAAC,GAAY;IACjC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5C,MAAM,IAAI,WAAW,CAAC,iFAAiF,CAAC,CAAC;IAC3G,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,GAAG,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QACvB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YACrD,MAAM,IAAI,WAAW,CAAC,YAAY,CAAC,2CAA2C,CAAC,CAAC;QAClF,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACpB,MAAM,IAAI,WAAW,CAAC,0CAA0C,KAAK,EAAE,CAAC,CAAC;QAC3E,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;IACH,OAAO,GAAe,CAAC;AACzB,CAAC;AAED,SAAS,WAAW,CAAC,GAAY;IAC/B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QAC5C,MAAM,IAAI,WAAW,CAAC,wBAAwB,CAAC,CAAC;IAClD,CAAC;IACD,MAAM,GAAG,GAAG,GAA8B,CAAC;IAC3C,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK,WAAW,CAAC,CAAC;IACnF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,WAAW,CAAC,2BAA2B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACzE,CAAC;IACD,MAAM,MAAM,GAAiB,EAAE,CAAC;IAChC,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAC5B,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC,EAAE,CAAC;YACxE,MAAM,IAAI,WAAW,CAAC,yCAAyC,CAAC,CAAC;QACnE,CAAC;QACD,MAAM,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC;IAC3B,CAAC;IACD,IAAI,GAAG,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QAChC,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,SAAS,IAAI,CAAC,EAAE,CAAC;YAChG,MAAM,IAAI,WAAW,CAAC,6CAA6C,CAAC,CAAC;QACvE,CAAC;QACD,MAAM,CAAC,SAAS,GAAG,GAAG,CAAC,SAAS,CAAC;IACnC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-line description extraction, ported verbatim from the routes module
|
|
3
|
+
* (dsh-plugin-dev/extras/modules/routes/src/description.ts, ADR-0010:
|
|
4
|
+
* absorption is porting — every boundary handling here is tuition already
|
|
5
|
+
* paid on production corpora; do not "simplify" it).
|
|
6
|
+
*
|
|
7
|
+
* Priority: ① frontmatter `description:`, ② an explicit `description:` line
|
|
8
|
+
* in the document head, ③ the first substantive body line as a fallback.
|
|
9
|
+
* Returns null when none match.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Extracts a one-line route description from Markdown, in priority order:
|
|
13
|
+
* ① frontmatter `description:`, ② an explicit `description:` line in the document
|
|
14
|
+
* head, ③ the first substantive body line as a fallback. Returns `null` when none match.
|
|
15
|
+
*/
|
|
16
|
+
export declare function extractDescription(content: string): string | null;
|
|
17
|
+
/**
|
|
18
|
+
* Directory sidecar read (2026-10-07): `_category_.yml/.yaml/.json` carry a
|
|
19
|
+
* folder's description so release-face READMEs stay untouched. YAML reuses
|
|
20
|
+
* the frontmatter ① key reader — the sidecar is frontmatter without the
|
|
21
|
+
* document (an indented `link.description:` therefore reads too, covering
|
|
22
|
+
* Docusaurus-shaped files for free); JSON parses properly and accepts the
|
|
23
|
+
* top-level `description` or the Docusaurus `link.description` shape.
|
|
24
|
+
* `{ description: null }` means the file carries no description key.
|
|
25
|
+
*/
|
|
26
|
+
export type SidecarRead = {
|
|
27
|
+
description: string | null;
|
|
28
|
+
} | {
|
|
29
|
+
error: string;
|
|
30
|
+
};
|
|
31
|
+
export declare function extractSidecarDescription(fileName: string, content: string): SidecarRead;
|