@opum-ai/lore 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -22
- package/bin/lore.cjs +35 -7
- package/package.json +17 -17
- package/src/adapters/backlog.ts +0 -1084
- package/src/adapters/git.ts +0 -221
- package/src/cli.ts +0 -667
- package/src/commands/agent.ts +0 -301
- package/src/commands/agents.ts +0 -302
- package/src/commands/args.ts +0 -209
- package/src/commands/changed.ts +0 -70
- package/src/commands/check.ts +0 -1031
- package/src/commands/codex-bridge.ts +0 -49
- package/src/commands/concurrency.ts +0 -48
- package/src/commands/context.ts +0 -292
- package/src/commands/discover.ts +0 -89
- package/src/commands/explorer.ts +0 -253
- package/src/commands/export.ts +0 -93
- package/src/commands/fswrite.ts +0 -928
- package/src/commands/graph.ts +0 -291
- package/src/commands/help.ts +0 -151
- package/src/commands/impact.ts +0 -59
- package/src/commands/init.ts +0 -583
- package/src/commands/instructions.ts +0 -91
- package/src/commands/link.ts +0 -929
- package/src/commands/new.ts +0 -476
- package/src/commands/orphans.ts +0 -457
- package/src/commands/path.ts +0 -67
- package/src/commands/provenance.ts +0 -68
- package/src/commands/query.ts +0 -312
- package/src/commands/reconcile-shared.ts +0 -280
- package/src/commands/rename.ts +0 -585
- package/src/commands/replace.ts +0 -320
- package/src/commands/scaffold.ts +0 -346
- package/src/commands/schema.ts +0 -293
- package/src/commands/snapshot.ts +0 -130
- package/src/commands/supersede.ts +0 -400
- package/src/commands/sync.ts +0 -371
- package/src/commands/tasks.ts +0 -271
- package/src/commands/traversal.ts +0 -151
- package/src/commands/validate.ts +0 -226
- package/src/config.ts +0 -598
- package/src/core/agent-bridge.ts +0 -287
- package/src/core/agent-context.ts +0 -498
- package/src/core/agent-profile.ts +0 -447
- package/src/core/bundle.ts +0 -893
- package/src/core/check.ts +0 -853
- package/src/core/codex-bridge.ts +0 -100
- package/src/core/concept.ts +0 -597
- package/src/core/consumer-scaffold.ts +0 -433
- package/src/core/context.ts +0 -271
- package/src/core/explorer-contract.ts +0 -441
- package/src/core/explorer-qualification.ts +0 -58
- package/src/core/explorer.ts +0 -518
- package/src/core/finding.ts +0 -31
- package/src/core/graph.ts +0 -201
- package/src/core/indexes.ts +0 -436
- package/src/core/instructions.ts +0 -209
- package/src/core/ladybug-driver.ts +0 -1795
- package/src/core/ladybug-lifecycle.ts +0 -1178
- package/src/core/ladybug-native.ts +0 -95
- package/src/core/ladybug-source.ts +0 -667
- package/src/core/links.ts +0 -681
- package/src/core/log.ts +0 -253
- package/src/core/managed-block.ts +0 -540
- package/src/core/manifest.ts +0 -718
- package/src/core/order.ts +0 -13
- package/src/core/profile.ts +0 -1007
- package/src/core/projection.ts +0 -195
- package/src/core/query.ts +0 -542
- package/src/core/reconcile.ts +0 -236
- package/src/core/replace.ts +0 -419
- package/src/core/retrieval.ts +0 -213
- package/src/core/rewrite.ts +0 -940
- package/src/core/scaffold.ts +0 -255
- package/src/core/schema.ts +0 -366
- package/src/core/snapshot-runtime.ts +0 -52
- package/src/core/snapshot-store.ts +0 -287
- package/src/core/snapshot.ts +0 -711
- package/src/core/template.ts +0 -429
- package/src/core/traversal.ts +0 -487
- package/src/core/validate.ts +0 -517
- package/src/core/workspace-contract.ts +0 -473
- package/src/core/workspace-projection.ts +0 -365
- package/src/core/workspace-retrieval.ts +0 -196
- package/src/core/workspace-source.ts +0 -174
- package/src/errors.ts +0 -697
- package/src/meta.ts +0 -7
- package/src/output.ts +0 -589
- package/src/scripts/upstream-backlog-watch.ts +0 -288
- package/src/state.ts +0 -390
package/src/core/context.ts
DELETED
|
@@ -1,271 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* context.ts — assemble a concept and its neighborhood into a token-budgeted
|
|
3
|
-
* **context pack** for an agent (cli-surface §context, LORE-34).
|
|
4
|
-
*
|
|
5
|
-
* `lore context <id>` answers "give me this concept plus enough of what surrounds
|
|
6
|
-
* it to reason about it, within a token budget." This module is the pure shaping
|
|
7
|
-
* layer behind it: it takes an already-loaded {@link BundleGraph}, gathers the
|
|
8
|
-
* target's neighborhood via the shared {@link subgraph} traversal `lore graph`
|
|
9
|
-
* also uses, and produces a {@link ContextExport} — the target concept's **full
|
|
10
|
-
* body** plus a **one-line `summary` compaction** of each neighbor — trimmed to a
|
|
11
|
-
* `--max-tokens` budget.
|
|
12
|
-
*
|
|
13
|
-
* It is deliberately **structural, not ranked** (design §2.1, ADR-0015): there are
|
|
14
|
-
* no relevance heuristics here. Neighbors are taken in the traversal's
|
|
15
|
-
* **nearest-first** discovery order (depth-1 before depth-2, deterministic within
|
|
16
|
-
* a level), and the budget fill keeps a prefix of that order — "the closest
|
|
17
|
-
* neighbors that fit." Scoring/relevance is `lore query`'s job (BM25); this command
|
|
18
|
-
* is the predictable, reproducible expansion an agent can rely on.
|
|
19
|
-
*
|
|
20
|
-
* ## Token model (all figures are the chars/4 heuristic, never a real tokenizer)
|
|
21
|
-
*
|
|
22
|
-
* Every figure is {@link estimateTokens} — the project's shared chars/4 rule. The
|
|
23
|
-
* two roles in a pack are charged over the bytes each actually contributes:
|
|
24
|
-
*
|
|
25
|
-
* - The **target** is emitted in full, so it is charged
|
|
26
|
-
* {@link BundleGraph.tokenEstimate}`(root)` — the whole-concept estimate, which is
|
|
27
|
-
* the *same* `~tokens` `lore graph` reports for that concept (cross-command
|
|
28
|
-
* consistency, and a reuse of the one memoized estimator).
|
|
29
|
-
* - A **neighbor** is compacted to a one-line entry, so it is charged the chars/4 of
|
|
30
|
-
* that entry's content — its `id`, `type`, `title` (when present), and `summary`
|
|
31
|
-
* (when present) — not the whole concept (which would defeat the compaction) and
|
|
32
|
-
* not the summary alone (which would under-count the always-present id/type, or a
|
|
33
|
-
* long `title` sitting behind a short/absent `summary`, letting a wide
|
|
34
|
-
* neighborhood overrun the budget).
|
|
35
|
-
*
|
|
36
|
-
* The export's `tokenEstimate` is the target's estimate plus every **included**
|
|
37
|
-
* neighbor's — the size of the pack actually emitted. Neighbors are added in
|
|
38
|
-
* nearest-first order while the running total stays within `--max-tokens`; the fill
|
|
39
|
-
* **stops at the first neighbor that would exceed the budget** (a predictable
|
|
40
|
-
* nearest-first prefix, not a greedy "skip the big one and keep filling"), and any
|
|
41
|
-
* remaining neighbors are dropped with `truncated` set. The **target is always
|
|
42
|
-
* present** — a context without its subject is meaningless — so a `--max-tokens`
|
|
43
|
-
* smaller than the target still returns the target with zero neighbors; that pack is
|
|
44
|
-
* **over budget**, and `truncated` is set in that case too (so a `truncated: false`
|
|
45
|
-
* is an honest "everything fit", never a silent overrun the target alone caused).
|
|
46
|
-
*
|
|
47
|
-
* Like the rest of `core/` (design §2.1) this module is pure: it reads the graph
|
|
48
|
-
* and returns plain data or throws a {@link LoreError}; it never touches the
|
|
49
|
-
* filesystem, prints, or reads flags.
|
|
50
|
-
*/
|
|
51
|
-
|
|
52
|
-
import { singleLine } from "../errors";
|
|
53
|
-
import { type BundleGraph, estimateTokens, frontmatterScalar } from "./bundle";
|
|
54
|
-
import type { Concept } from "./concept";
|
|
55
|
-
import { subgraph } from "./query";
|
|
56
|
-
import type { WorkspaceRecordProvenance, WorkspaceResultScope } from "./workspace-contract";
|
|
57
|
-
|
|
58
|
-
/** The target concept at the center of a {@link ContextExport} — emitted in full. */
|
|
59
|
-
export interface ContextTarget {
|
|
60
|
-
/** The concept id (bundle-root-relative, e.g. `stories/bulk-archive`). */
|
|
61
|
-
readonly id: string;
|
|
62
|
-
/** The concept's resolved `type` (mirrors `frontmatter.type`). */
|
|
63
|
-
readonly type: string;
|
|
64
|
-
/** The concept's `title` frontmatter, when present and a non-empty scalar; omitted otherwise. */
|
|
65
|
-
readonly title?: string;
|
|
66
|
-
/** The concept's full markdown body (verbatim, the pack's primary content). */
|
|
67
|
-
readonly body: string;
|
|
68
|
-
/** The chars/4 estimate over the target's full serialized bytes (== `lore graph`'s node estimate). */
|
|
69
|
-
readonly tokenEstimate: number;
|
|
70
|
-
/** Complete locator-free provenance in explicit workspace mode. */
|
|
71
|
-
readonly provenance?: WorkspaceRecordProvenance;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/** One neighbor in a {@link ContextExport} — compacted to its one-line `summary`. */
|
|
75
|
-
export interface ContextNeighbor {
|
|
76
|
-
/** The neighbor concept id. */
|
|
77
|
-
readonly id: string;
|
|
78
|
-
/** The neighbor's resolved `type`. */
|
|
79
|
-
readonly type: string;
|
|
80
|
-
/** The neighbor's `title` frontmatter, when present and a non-empty scalar; omitted otherwise. */
|
|
81
|
-
readonly title?: string;
|
|
82
|
-
/**
|
|
83
|
-
* The neighbor's one-line compaction: its `summary` frontmatter, else its `title`,
|
|
84
|
-
* collapsed to a single line. Omitted when the concept carries neither — the
|
|
85
|
-
* neighbor still appears (its id/type are the structural signal), it just has no
|
|
86
|
-
* sentence to show.
|
|
87
|
-
*/
|
|
88
|
-
readonly summary?: string;
|
|
89
|
-
/**
|
|
90
|
-
* The chars/4 estimate of the emitted entry — `id` + `type` + `title` (when
|
|
91
|
-
* present) + `summary` (when present) — what this neighbor costs the budget.
|
|
92
|
-
* `title` and `summary` are charged independently even when `summary` is the
|
|
93
|
-
* `title` fallback (the neighbor object emits both fields under `--json`), so a
|
|
94
|
-
* long `title` behind a short/absent `summary` is never undercounted.
|
|
95
|
-
*/
|
|
96
|
-
readonly tokenEstimate: number;
|
|
97
|
-
/** Complete locator-free provenance in explicit workspace mode. */
|
|
98
|
-
readonly provenance?: WorkspaceRecordProvenance;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/** The `context.export` payload: the target's full body, the neighbor compaction, and the budget accounting. */
|
|
102
|
-
export interface ContextExport {
|
|
103
|
-
/** The target concept id the pack is centered on. */
|
|
104
|
-
readonly root: string;
|
|
105
|
-
/** The neighbor radius used (hops from the target; `0` = target only). */
|
|
106
|
-
readonly depth: number;
|
|
107
|
-
/** The token budget when one was given (`--max-tokens`); omitted when the pack was bounded only by `--depth`. */
|
|
108
|
-
readonly maxTokens?: number;
|
|
109
|
-
/** The target concept, emitted in full. */
|
|
110
|
-
readonly target: ContextTarget;
|
|
111
|
-
/** The included neighbors, in nearest-first order. */
|
|
112
|
-
readonly neighbors: readonly ContextNeighbor[];
|
|
113
|
-
/** The pack's chars/4 token estimate: the target's estimate plus every included neighbor's. */
|
|
114
|
-
readonly tokenEstimate: number;
|
|
115
|
-
/** The total number of neighbors within `--depth` before any budget trim. */
|
|
116
|
-
readonly total: number;
|
|
117
|
-
/** The number of neighbors actually included (`shown <= total`). */
|
|
118
|
-
readonly shown: number;
|
|
119
|
-
/**
|
|
120
|
-
* `true` when the pack does not fully fit the budget: either the budget dropped
|
|
121
|
-
* one or more neighbors (`shown < total`), or the always-present target alone
|
|
122
|
-
* pushes `tokenEstimate` past `--max-tokens` (an over-budget pack with no neighbor
|
|
123
|
-
* to drop). `false` is therefore an honest "the whole neighborhood is here and
|
|
124
|
-
* within budget", never a silent overrun.
|
|
125
|
-
*/
|
|
126
|
-
readonly truncated: boolean;
|
|
127
|
-
/** Explicit selected workspace scope; absent for repository-local output. */
|
|
128
|
-
readonly workspace?: WorkspaceResultScope;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/** Options for {@link buildContext}. */
|
|
132
|
-
export interface BuildContextOptions {
|
|
133
|
-
/** The neighbor radius in hops from the target. Defaults to `1`. */
|
|
134
|
-
readonly depth?: number;
|
|
135
|
-
/**
|
|
136
|
-
* The token budget for the whole pack. When omitted, no neighbor is dropped for
|
|
137
|
-
* size — the pack is bounded only by `depth`.
|
|
138
|
-
*/
|
|
139
|
-
readonly maxTokens?: number;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/** The default neighbor radius when `--depth` is not given (cli-surface §context). */
|
|
143
|
-
export const DEFAULT_DEPTH = 1;
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Build a {@link ContextExport} for `root`: the target concept's full body plus a
|
|
147
|
-
* one-line compaction of its neighbors out to `depth` hops, trimmed to `maxTokens`.
|
|
148
|
-
*
|
|
149
|
-
* The neighborhood comes from the shared {@link subgraph} traversal (undirected,
|
|
150
|
-
* cycle-tolerant, depth-bounded) — the same one `lore graph` roots its export at —
|
|
151
|
-
* so the two commands agree on what "around this concept" means. The traversal's
|
|
152
|
-
* discovery order is **nearest-first**; the target is dropped from it (it is the
|
|
153
|
-
* subject, carried separately) and the remaining neighbors are taken in that order.
|
|
154
|
-
*
|
|
155
|
-
* Budgeting and the token model are documented in the module header. The **target
|
|
156
|
-
* is always included**; only neighbors are trimmed, and the fill stops at the first
|
|
157
|
-
* neighbor that would push the running total past `maxTokens`.
|
|
158
|
-
*
|
|
159
|
-
* @throws LoreError `not_found` (exit 3) via {@link subgraph} when `root` names no
|
|
160
|
-
* concept in the bundle.
|
|
161
|
-
*/
|
|
162
|
-
export function buildContext(graph: BundleGraph, root: string, options: BuildContextOptions = {}): ContextExport {
|
|
163
|
-
const depth = options.depth ?? DEFAULT_DEPTH;
|
|
164
|
-
const { maxTokens } = options;
|
|
165
|
-
|
|
166
|
-
// subgraph throws not_found for an unknown root, so this is also the id guard.
|
|
167
|
-
const reached = subgraph(graph, root, depth);
|
|
168
|
-
const targetConcept = conceptAt(graph, root);
|
|
169
|
-
const target: ContextTarget = {
|
|
170
|
-
id: root,
|
|
171
|
-
type: targetConcept.type,
|
|
172
|
-
...titleField(targetConcept.frontmatter.title),
|
|
173
|
-
body: targetConcept.body,
|
|
174
|
-
tokenEstimate: graph.tokenEstimate(root),
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
// Every reached id but the root is a neighbor (subgraph yields only real concepts),
|
|
178
|
-
// so the candidate count is known without materializing the dropped ones.
|
|
179
|
-
const total = reached.size - 1;
|
|
180
|
-
// Single nearest-first fill: subgraph iterates root-first then by level, so dropping
|
|
181
|
-
// the root visits depth-1 before depth-2 — exactly the order the budget should keep.
|
|
182
|
-
// Include each neighbor while it still fits and STOP at the first that would exceed
|
|
183
|
-
// the budget (a predictable prefix), so a dropped neighbor's summary is never even
|
|
184
|
-
// computed. An omitted budget keeps every neighbor.
|
|
185
|
-
const neighbors: ContextNeighbor[] = [];
|
|
186
|
-
let tokenEstimate = target.tokenEstimate;
|
|
187
|
-
for (const id of reached) {
|
|
188
|
-
if (id === root) {
|
|
189
|
-
continue;
|
|
190
|
-
}
|
|
191
|
-
const neighbor = neighborOf(conceptAt(graph, id), id);
|
|
192
|
-
if (maxTokens !== undefined && tokenEstimate + neighbor.tokenEstimate > maxTokens) {
|
|
193
|
-
break;
|
|
194
|
-
}
|
|
195
|
-
neighbors.push(neighbor);
|
|
196
|
-
tokenEstimate += neighbor.tokenEstimate;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const overBudget = maxTokens !== undefined && tokenEstimate > maxTokens;
|
|
200
|
-
return {
|
|
201
|
-
root,
|
|
202
|
-
depth,
|
|
203
|
-
...(maxTokens !== undefined ? { maxTokens } : {}),
|
|
204
|
-
target,
|
|
205
|
-
neighbors,
|
|
206
|
-
tokenEstimate,
|
|
207
|
-
total,
|
|
208
|
-
shown: neighbors.length,
|
|
209
|
-
truncated: neighbors.length < total || overBudget,
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Shape one neighbor concept into its compacted {@link ContextNeighbor}: its `type`,
|
|
215
|
-
* optional `title`, the one-line summary (its `summary`, falling back to its
|
|
216
|
-
* `title`), and the chars/4 cost of the emitted entry (`id` + `type` + `title` +
|
|
217
|
-
* `summary`). The `title` scalar is coerced once and reused for both the `title`
|
|
218
|
-
* field and the summary fallback; it is charged in the cost even when `summary`
|
|
219
|
-
* duplicates it via that fallback, so a long `title` behind a short/absent
|
|
220
|
-
* `summary` is never undercounted.
|
|
221
|
-
*/
|
|
222
|
-
function neighborOf(concept: Concept, id: string): ContextNeighbor {
|
|
223
|
-
const title = frontmatterScalar(concept.frontmatter.title);
|
|
224
|
-
const summary = oneLine(frontmatterScalar(concept.frontmatter.summary) ?? title);
|
|
225
|
-
const titlePart = title !== undefined ? ` ${title}` : "";
|
|
226
|
-
const summaryPart = summary !== undefined ? ` ${summary}` : "";
|
|
227
|
-
return {
|
|
228
|
-
id,
|
|
229
|
-
type: concept.type,
|
|
230
|
-
...(title !== undefined ? { title } : {}),
|
|
231
|
-
...(summary !== undefined ? { summary } : {}),
|
|
232
|
-
tokenEstimate: estimateTokens(`${id} ${concept.type}${titlePart}${summaryPart}`),
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* The concept at `id`, which the caller already knows is in the bundle — every id
|
|
238
|
-
* {@link subgraph} returns names a real concept (the root is checked, and every
|
|
239
|
-
* other reached id came from an {@link Edge} whose endpoints are bundle concepts).
|
|
240
|
-
* Asserting that here keeps the build straight-line, with no dead "missing concept"
|
|
241
|
-
* branch the traversal contract makes unreachable.
|
|
242
|
-
*/
|
|
243
|
-
function conceptAt(graph: BundleGraph, id: string): Concept {
|
|
244
|
-
return graph.concepts.get(id) as Concept;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* A `title` frontmatter value as a `{ title }` field to spread, or `{}` when it has
|
|
249
|
-
* no display form. Uses the shared {@link frontmatterScalar} so the `title` a target
|
|
250
|
-
* or neighbor reports is **byte-identical** to what `lore graph` reports for the same
|
|
251
|
-
* concept (verbatim string, finite number/boolean coerced, else absent).
|
|
252
|
-
*/
|
|
253
|
-
function titleField(value: unknown): { title?: string } {
|
|
254
|
-
const title = frontmatterScalar(value);
|
|
255
|
-
return title !== undefined ? { title } : {};
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Collapse an already-coerced scalar ({@link frontmatterScalar}) to a single trimmed,
|
|
260
|
-
* non-empty line for the neighbor compaction, or `undefined`. Unlike the `title`
|
|
261
|
-
* field — which is kept verbatim to match `lore graph` — the summary is a one-line
|
|
262
|
-
* display, so {@link singleLine} + trim ensures a multi-line YAML scalar cannot
|
|
263
|
-
* smuggle extra lines into the pack.
|
|
264
|
-
*/
|
|
265
|
-
function oneLine(value: string | undefined): string | undefined {
|
|
266
|
-
if (value === undefined) {
|
|
267
|
-
return undefined;
|
|
268
|
-
}
|
|
269
|
-
const line = singleLine(value).trim();
|
|
270
|
-
return line === "" ? undefined : line;
|
|
271
|
-
}
|