@llm4ts/flow 0.13.5 → 0.14.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/dist/BoardSync.d.ts +81 -0
- package/dist/BoardSync.d.ts.map +1 -0
- package/dist/BoardSync.js +259 -0
- package/dist/BoardSync.js.map +1 -0
- package/dist/EstimatedUsage.d.ts +28 -0
- package/dist/EstimatedUsage.d.ts.map +1 -0
- package/dist/EstimatedUsage.js +91 -0
- package/dist/EstimatedUsage.js.map +1 -0
- package/dist/Pack.d.ts +1 -0
- package/dist/Pack.d.ts.map +1 -1
- package/dist/Pack.js +7 -0
- package/dist/Pack.js.map +1 -1
- package/dist/PageSpec.d.ts +104 -0
- package/dist/PageSpec.d.ts.map +1 -0
- package/dist/PageSpec.js +272 -0
- package/dist/PageSpec.js.map +1 -0
- package/dist/SpecChecks.d.ts +6 -1
- package/dist/SpecChecks.d.ts.map +1 -1
- package/dist/SpecChecks.js +11 -4
- package/dist/SpecChecks.js.map +1 -1
- package/dist/Survey.d.ts +38 -1
- package/dist/Survey.d.ts.map +1 -1
- package/dist/Survey.js +101 -5
- package/dist/Survey.js.map +1 -1
- package/dist/Workspace.d.ts +44 -5
- package/dist/Workspace.d.ts.map +1 -1
- package/dist/Workspace.js +84 -14
- package/dist/Workspace.js.map +1 -1
- package/package.json +5 -2
- package/src/BoardSync.ts +358 -0
- package/src/EstimatedUsage.ts +180 -0
- package/src/Pack.ts +11 -0
- package/src/PageSpec.ts +354 -0
- package/src/SpecChecks.ts +12 -6
- package/src/Survey.ts +137 -5
- package/src/Workspace.ts +120 -15
package/src/Workspace.ts
CHANGED
|
@@ -17,13 +17,34 @@ export interface WorkspaceLimits {
|
|
|
17
17
|
readonly maxWriteBytes: number
|
|
18
18
|
readonly maxResults: number
|
|
19
19
|
readonly maxDepth: number
|
|
20
|
+
/**
|
|
21
|
+
* Directory names discovery never descends into, at any depth. Version
|
|
22
|
+
* control internals and dependency/build output are never estate sources,
|
|
23
|
+
* yet on a real repository they hold the overwhelming majority of files —
|
|
24
|
+
* walking them is slow and used to spend the result cap before the first
|
|
25
|
+
* source was seen. Absent means no pruning (the previous behaviour).
|
|
26
|
+
*/
|
|
27
|
+
readonly excludeDirs?: ReadonlyArray<string>
|
|
20
28
|
}
|
|
21
29
|
|
|
30
|
+
/** The directories `defaultWorkspaceLimits` prunes from discovery. */
|
|
31
|
+
export const defaultExcludedDirectories: ReadonlyArray<string> = Object.freeze([
|
|
32
|
+
".git",
|
|
33
|
+
".hg",
|
|
34
|
+
".svn",
|
|
35
|
+
"node_modules",
|
|
36
|
+
"target",
|
|
37
|
+
"build",
|
|
38
|
+
"dist",
|
|
39
|
+
"out"
|
|
40
|
+
])
|
|
41
|
+
|
|
22
42
|
export const defaultWorkspaceLimits: WorkspaceLimits = Object.freeze({
|
|
23
43
|
maxReadBytes: 1_048_576,
|
|
24
44
|
maxWriteBytes: 1_048_576,
|
|
25
45
|
maxResults: 1_000,
|
|
26
|
-
maxDepth: 32
|
|
46
|
+
maxDepth: 32,
|
|
47
|
+
excludeDirs: defaultExcludedDirectories
|
|
27
48
|
})
|
|
28
49
|
|
|
29
50
|
/**
|
|
@@ -32,30 +53,105 @@ export const defaultWorkspaceLimits: WorkspaceLimits = Object.freeze({
|
|
|
32
53
|
* and generated exports, so the default 1 MiB read cap — sized for
|
|
33
54
|
* spec-and-plan repositories — would fail an inventory on its first big
|
|
34
55
|
* file. 8 MiB accommodates real estates while still refusing runaway blobs.
|
|
56
|
+
* The same reasoning sizes the discovery cap: an estate is by nature large,
|
|
57
|
+
* and 1 000 results — sized for spec-and-plan repositories — is fewer files
|
|
58
|
+
* than a mid-sized J2EE application ships in `src/` alone.
|
|
35
59
|
*/
|
|
36
60
|
export const legacySourceWorkspaceLimits: WorkspaceLimits = Object.freeze({
|
|
37
61
|
...defaultWorkspaceLimits,
|
|
38
|
-
maxReadBytes: 8_388_608
|
|
62
|
+
maxReadBytes: 8_388_608,
|
|
63
|
+
maxResults: 20_000
|
|
39
64
|
})
|
|
40
65
|
|
|
66
|
+
const positiveInteger = (raw: string | undefined): number | undefined => {
|
|
67
|
+
const text = raw?.trim()
|
|
68
|
+
if (text === undefined || text.length === 0 || !/^\d+$/.test(text)) {
|
|
69
|
+
return undefined
|
|
70
|
+
}
|
|
71
|
+
const parsed = Number.parseInt(text, 10)
|
|
72
|
+
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : undefined
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const directoryList = (raw: string | undefined): ReadonlyArray<string> | undefined => {
|
|
76
|
+
const text = raw?.trim()
|
|
77
|
+
if (text === undefined || text.length === 0) {
|
|
78
|
+
return undefined
|
|
79
|
+
}
|
|
80
|
+
const names = text
|
|
81
|
+
.split(",")
|
|
82
|
+
.map((name) => name.trim())
|
|
83
|
+
.filter((name) => name.length > 0 && !name.includes("/") && !name.includes("\\"))
|
|
84
|
+
return names.length === 0 ? undefined : names
|
|
85
|
+
}
|
|
86
|
+
|
|
41
87
|
/**
|
|
42
|
-
* `defaults` with the
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
88
|
+
* `defaults` with the caps overridden from the environment when the values
|
|
89
|
+
* are well-formed; anything else leaves that default untouched:
|
|
90
|
+
*
|
|
91
|
+
* - `LLM4TS_MAX_READ_BYTES` (positive integer): the per-file read cap, the
|
|
92
|
+
* escape hatch for estates whose sources exceed even the legacy-source cap.
|
|
93
|
+
* - `LLM4TS_MAX_DISCOVER_RESULTS` (positive integer): the discovery result
|
|
94
|
+
* cap, for estates larger than `legacySourceWorkspaceLimits` allows for.
|
|
95
|
+
* - `LLM4TS_EXCLUDE_DIRS` (comma-separated directory names): replaces the
|
|
96
|
+
* pruned-directory list — `.git,node_modules,generated` — for estates
|
|
97
|
+
* whose vendored or generated trees sit under names the default list
|
|
98
|
+
* does not know.
|
|
46
99
|
*/
|
|
47
100
|
export const workspaceLimitsFromEnv = (
|
|
48
101
|
environment: Readonly<Record<string, string | undefined>>,
|
|
49
102
|
defaults: WorkspaceLimits = defaultWorkspaceLimits
|
|
50
103
|
): WorkspaceLimits => {
|
|
51
|
-
const
|
|
52
|
-
|
|
104
|
+
const maxReadBytes = positiveInteger(environment.LLM4TS_MAX_READ_BYTES)
|
|
105
|
+
const maxResults = positiveInteger(environment.LLM4TS_MAX_DISCOVER_RESULTS)
|
|
106
|
+
const excludeDirs = directoryList(environment.LLM4TS_EXCLUDE_DIRS)
|
|
107
|
+
if (maxReadBytes === undefined && maxResults === undefined && excludeDirs === undefined) {
|
|
53
108
|
return defaults
|
|
54
109
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
? {
|
|
58
|
-
:
|
|
110
|
+
return {
|
|
111
|
+
...defaults,
|
|
112
|
+
...(maxReadBytes === undefined ? {} : { maxReadBytes }),
|
|
113
|
+
...(maxResults === undefined ? {} : { maxResults }),
|
|
114
|
+
...(excludeDirs === undefined ? {} : { excludeDirs })
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Narrows a discovery beyond its glob: only paths `matching` the first regex
|
|
120
|
+
* and not `excluding` the second are returned AND counted against
|
|
121
|
+
* `maxResults`, so a cap sized for source units is not spent on the jars,
|
|
122
|
+
* images, and generated files that share the tree. Pass regexes without the
|
|
123
|
+
* `g` flag — a global regex carries `lastIndex` state across `test` calls.
|
|
124
|
+
*/
|
|
125
|
+
export interface DiscoverOptions {
|
|
126
|
+
readonly matching?: RegExp
|
|
127
|
+
readonly excluding?: RegExp
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export const discoverAccepts = (path: string, options: DiscoverOptions): boolean =>
|
|
131
|
+
(options.matching?.test(path) ?? true) && !(options.excluding?.test(path) ?? false)
|
|
132
|
+
|
|
133
|
+
/** Whether a repo-relative path lies under a pruned directory. */
|
|
134
|
+
export const isExcludedPath = (path: string, limits: WorkspaceLimits): boolean => {
|
|
135
|
+
const excluded = limits.excludeDirs
|
|
136
|
+
if (excluded === undefined || excluded.length === 0) {
|
|
137
|
+
return false
|
|
138
|
+
}
|
|
139
|
+
const directories = path.split("/").slice(0, -1)
|
|
140
|
+
return directories.some((segment) => excluded.includes(segment))
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* The advice a flow gives when discovery overflows `limits.maxResults`: the
|
|
145
|
+
* three knobs that narrow or raise it, in the order a user should try them.
|
|
146
|
+
*/
|
|
147
|
+
export const discoveryOverflowAdvice = (limits: WorkspaceLimits): string => {
|
|
148
|
+
const pruned = limits.excludeDirs ?? []
|
|
149
|
+
return (
|
|
150
|
+
`discovery stopped at ${limits.maxResults} matching files; ` +
|
|
151
|
+
"narrow the pack's `sources:` regex or add an `exclude:` regex, prune more directories " +
|
|
152
|
+
`with LLM4TS_EXCLUDE_DIRS=<names> (pruned now: ${pruned.length === 0 ? "none" : pruned.join(",")}), ` +
|
|
153
|
+
"or raise the cap with LLM4TS_MAX_DISCOVER_RESULTS=<count>"
|
|
154
|
+
)
|
|
59
155
|
}
|
|
60
156
|
|
|
61
157
|
export interface WorkspaceShape {
|
|
@@ -64,7 +160,10 @@ export interface WorkspaceShape {
|
|
|
64
160
|
readonly read: (path: string) => Effect.Effect<string, WorkspaceError>
|
|
65
161
|
readonly write: (path: string, contents: string) => Effect.Effect<void, WorkspaceError>
|
|
66
162
|
readonly append: (path: string, contents: string) => Effect.Effect<void, WorkspaceError>
|
|
67
|
-
readonly discover: (
|
|
163
|
+
readonly discover: (
|
|
164
|
+
pattern?: string,
|
|
165
|
+
options?: DiscoverOptions
|
|
166
|
+
) => Effect.Effect<ReadonlyArray<string>, WorkspaceError>
|
|
68
167
|
readonly search: (
|
|
69
168
|
query: string,
|
|
70
169
|
pattern?: string
|
|
@@ -210,13 +309,19 @@ export const makeMemoryWorkspace = (
|
|
|
210
309
|
)
|
|
211
310
|
}
|
|
212
311
|
|
|
213
|
-
const discover = (
|
|
312
|
+
const discover = (
|
|
313
|
+
pattern = "**/*",
|
|
314
|
+
options: DiscoverOptions = {}
|
|
315
|
+
): Effect.Effect<ReadonlyArray<string>, WorkspaceError> =>
|
|
214
316
|
Effect.gen(function* () {
|
|
215
317
|
const normalizedPattern = pattern.replaceAll("\\", "/")
|
|
216
318
|
const matcher = normalizedPattern === "**/*" ? /.*/ : globRegex(normalizedPattern)
|
|
217
319
|
const files = yield* Ref.get(state)
|
|
218
320
|
const results: Array<string> = []
|
|
219
321
|
for (const path of Object.keys(files).sort(comparePaths)) {
|
|
322
|
+
if (isExcludedPath(path, limits)) {
|
|
323
|
+
continue
|
|
324
|
+
}
|
|
220
325
|
const depth = path.split("/").length - 1
|
|
221
326
|
if (depth > limits.maxDepth) {
|
|
222
327
|
return yield* WorkspaceLimitError.make({
|
|
@@ -225,7 +330,7 @@ export const makeMemoryWorkspace = (
|
|
|
225
330
|
actual: depth
|
|
226
331
|
})
|
|
227
332
|
}
|
|
228
|
-
if (matcher.test(path)) {
|
|
333
|
+
if (matcher.test(path) && discoverAccepts(path, options)) {
|
|
229
334
|
results.push(path)
|
|
230
335
|
if (results.length > limits.maxResults) {
|
|
231
336
|
return yield* WorkspaceLimitError.make({
|