@opum-ai/lore 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.
Files changed (90) hide show
  1. package/README.md +33 -22
  2. package/bin/lore.cjs +35 -7
  3. package/package.json +17 -17
  4. package/src/adapters/backlog.ts +0 -1084
  5. package/src/adapters/git.ts +0 -221
  6. package/src/cli.ts +0 -667
  7. package/src/commands/agent.ts +0 -301
  8. package/src/commands/agents.ts +0 -302
  9. package/src/commands/args.ts +0 -209
  10. package/src/commands/changed.ts +0 -70
  11. package/src/commands/check.ts +0 -1031
  12. package/src/commands/codex-bridge.ts +0 -49
  13. package/src/commands/concurrency.ts +0 -48
  14. package/src/commands/context.ts +0 -292
  15. package/src/commands/discover.ts +0 -89
  16. package/src/commands/explorer.ts +0 -253
  17. package/src/commands/export.ts +0 -93
  18. package/src/commands/fswrite.ts +0 -928
  19. package/src/commands/graph.ts +0 -291
  20. package/src/commands/help.ts +0 -151
  21. package/src/commands/impact.ts +0 -59
  22. package/src/commands/init.ts +0 -583
  23. package/src/commands/instructions.ts +0 -91
  24. package/src/commands/link.ts +0 -929
  25. package/src/commands/new.ts +0 -476
  26. package/src/commands/orphans.ts +0 -457
  27. package/src/commands/path.ts +0 -67
  28. package/src/commands/provenance.ts +0 -68
  29. package/src/commands/query.ts +0 -312
  30. package/src/commands/reconcile-shared.ts +0 -280
  31. package/src/commands/rename.ts +0 -585
  32. package/src/commands/replace.ts +0 -320
  33. package/src/commands/scaffold.ts +0 -346
  34. package/src/commands/schema.ts +0 -293
  35. package/src/commands/snapshot.ts +0 -130
  36. package/src/commands/supersede.ts +0 -400
  37. package/src/commands/sync.ts +0 -371
  38. package/src/commands/tasks.ts +0 -271
  39. package/src/commands/traversal.ts +0 -151
  40. package/src/commands/validate.ts +0 -226
  41. package/src/config.ts +0 -598
  42. package/src/core/agent-bridge.ts +0 -287
  43. package/src/core/agent-context.ts +0 -498
  44. package/src/core/agent-profile.ts +0 -447
  45. package/src/core/bundle.ts +0 -893
  46. package/src/core/check.ts +0 -853
  47. package/src/core/codex-bridge.ts +0 -100
  48. package/src/core/concept.ts +0 -597
  49. package/src/core/consumer-scaffold.ts +0 -433
  50. package/src/core/context.ts +0 -271
  51. package/src/core/explorer-contract.ts +0 -441
  52. package/src/core/explorer-qualification.ts +0 -58
  53. package/src/core/explorer.ts +0 -518
  54. package/src/core/finding.ts +0 -31
  55. package/src/core/graph.ts +0 -201
  56. package/src/core/indexes.ts +0 -436
  57. package/src/core/instructions.ts +0 -209
  58. package/src/core/ladybug-driver.ts +0 -1795
  59. package/src/core/ladybug-lifecycle.ts +0 -1178
  60. package/src/core/ladybug-native.ts +0 -95
  61. package/src/core/ladybug-source.ts +0 -667
  62. package/src/core/links.ts +0 -681
  63. package/src/core/log.ts +0 -253
  64. package/src/core/managed-block.ts +0 -540
  65. package/src/core/manifest.ts +0 -718
  66. package/src/core/order.ts +0 -13
  67. package/src/core/profile.ts +0 -1007
  68. package/src/core/projection.ts +0 -195
  69. package/src/core/query.ts +0 -542
  70. package/src/core/reconcile.ts +0 -236
  71. package/src/core/replace.ts +0 -419
  72. package/src/core/retrieval.ts +0 -213
  73. package/src/core/rewrite.ts +0 -940
  74. package/src/core/scaffold.ts +0 -255
  75. package/src/core/schema.ts +0 -366
  76. package/src/core/snapshot-runtime.ts +0 -52
  77. package/src/core/snapshot-store.ts +0 -287
  78. package/src/core/snapshot.ts +0 -711
  79. package/src/core/template.ts +0 -429
  80. package/src/core/traversal.ts +0 -487
  81. package/src/core/validate.ts +0 -517
  82. package/src/core/workspace-contract.ts +0 -473
  83. package/src/core/workspace-projection.ts +0 -365
  84. package/src/core/workspace-retrieval.ts +0 -196
  85. package/src/core/workspace-source.ts +0 -174
  86. package/src/errors.ts +0 -697
  87. package/src/meta.ts +0 -7
  88. package/src/output.ts +0 -589
  89. package/src/scripts/upstream-backlog-watch.ts +0 -288
  90. package/src/state.ts +0 -390
@@ -1,253 +0,0 @@
1
- /** `lore explorer [--out <file>] [--force]` — deterministic offline graph artifact generation. */
2
-
3
- import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
4
- import type { BacklogAdapter } from "../adapters/backlog";
5
- import {
6
- buildExplorerChangeSnapshot,
7
- buildExplorerSnapshot,
8
- EXPLORER_ARTIFACT_VERSION,
9
- explorerArtifactDigest,
10
- renderExplorerArtifact,
11
- renderExplorerChangeArtifact,
12
- } from "../core/explorer";
13
- import type { ExplorerSnapshot } from "../core/explorer-contract";
14
- import { EXPECTED_LADYBUG_STORAGE_VERSION, EXPECTED_LADYBUG_VERSION } from "../core/ladybug-native";
15
- import { loadLadybugProjectionSource } from "../core/ladybug-source";
16
- import type { RetainedSnapshot } from "../core/snapshot";
17
- import { resolveSnapshotScope } from "../core/snapshot-runtime";
18
- import { loadSnapshot as loadStoredSnapshot } from "../core/snapshot-store";
19
- import { EXIT_OK, LoreError, WarningCollector, type Writer } from "../errors";
20
- import { emit, type OutputContext, type Renderable } from "../output";
21
- import { assertFlagAtMostOnce, parseCommandArgs, singleOptionValue, usage, workspaceSelection } from "./args";
22
- import { assertNoSymlinkInPath, classifyExistingFile, ensureDir, writeFileAtomic } from "./fswrite";
23
-
24
- export const DEFAULT_EXPLORER_ARTIFACT_PATH = ".lore/explorer/index.html";
25
-
26
- export interface ExplorerCommandOptions {
27
- readonly root: string;
28
- readonly output: OutputContext;
29
- readonly args: readonly string[];
30
- readonly stdout?: Writer;
31
- readonly stderr?: Writer;
32
- readonly adapter?: BacklogAdapter;
33
- readonly loadSnapshot?: ExplorerSnapshotLoader;
34
- readonly loadRetainedSnapshot?: (selector: string) => RetainedSnapshot | Promise<RetainedSnapshot>;
35
- }
36
-
37
- export interface ExplorerSnapshotLoaderOptions {
38
- readonly root: string;
39
- readonly adapter?: BacklogAdapter;
40
- readonly warnings: WarningCollector;
41
- }
42
-
43
- export type ExplorerSnapshotLoader = (options: ExplorerSnapshotLoaderOptions) => Promise<ExplorerSnapshot>;
44
-
45
- export interface ExplorerArtifactResult {
46
- readonly artifactVersion: typeof EXPLORER_ARTIFACT_VERSION;
47
- readonly snapshotSchemaVersion: string;
48
- readonly snapshotKey: string;
49
- readonly path: string;
50
- readonly action: "created" | "updated" | "unchanged";
51
- readonly byteLength: number;
52
- readonly digest: string;
53
- readonly counts: ExplorerSnapshot["health"]["counts"];
54
- }
55
-
56
- interface ExplorerArgs {
57
- readonly out: string;
58
- readonly customOut: boolean;
59
- readonly force: boolean;
60
- readonly snapshot?: string;
61
- readonly from?: string;
62
- readonly to?: string;
63
- readonly workspace?: { readonly manifestPath: string; readonly memberIds: readonly string[] };
64
- }
65
-
66
- export async function runExplorer(options: ExplorerCommandOptions): Promise<number> {
67
- const parsed = parseExplorerArgs(options.args);
68
- const target = confineArtifactPath(parsed.out, options.root);
69
- const warnings = new WarningCollector();
70
- let html: string;
71
- let schemaVersion: string;
72
- let snapshotKey: string;
73
- let counts: ExplorerSnapshot["health"]["counts"];
74
- if (parsed.snapshot !== undefined || parsed.from !== undefined) {
75
- let scope: Awaited<ReturnType<typeof resolveSnapshotScope>> | undefined;
76
- const loadRetained = async (selector: string): Promise<RetainedSnapshot> => {
77
- if (options.loadRetainedSnapshot !== undefined) return options.loadRetainedSnapshot(selector);
78
- scope ??= await resolveSnapshotScope({
79
- root: options.root,
80
- selection: parsed.workspace === undefined ? {} : { workspace: parsed.workspace.manifestPath },
81
- warnings,
82
- adapter: options.adapter,
83
- });
84
- return loadStoredSnapshot(options.root, scope, selector);
85
- };
86
- const from = await loadRetained(parsed.snapshot ?? (parsed.from as string));
87
- const to = parsed.snapshot !== undefined ? from : await loadRetained(parsed.to as string);
88
- const retained = buildExplorerChangeSnapshot(from, to, {
89
- mode: parsed.snapshot !== undefined ? "snapshot" : "comparison",
90
- repositories: parsed.workspace?.memberIds ?? [],
91
- });
92
- html = renderExplorerChangeArtifact(retained);
93
- schemaVersion = retained.schemaVersion;
94
- snapshotKey = retained.to.snapshotKey;
95
- counts = retainedCounts(retained.to, parsed.workspace?.memberIds ?? []);
96
- } else {
97
- const snapshot = await (options.loadSnapshot ?? loadProductionSnapshot)({
98
- root: options.root,
99
- adapter: options.adapter,
100
- warnings,
101
- });
102
- html = renderExplorerArtifact(snapshot);
103
- schemaVersion = snapshot.schemaVersion;
104
- snapshotKey = snapshot.source.snapshotKey;
105
- counts = snapshot.health.counts;
106
- }
107
- warnings.flush({ color: options.output.color, stderr: options.stderr });
108
- assertNoSymlinkInPath(options.root, target.relPath);
109
- const existing = classifyExistingFile(target.absPath, html);
110
- if (parsed.customOut && existing === "differs" && !parsed.force) {
111
- throw new LoreError(
112
- "conflict",
113
- `cannot overwrite differing explorer artifact ${target.relPath}`,
114
- "pass --force to replace it, choose another --out path, or remove the existing file",
115
- { path: target.relPath },
116
- );
117
- }
118
- const action = existing === "missing" ? "created" : existing === "unchanged" ? "unchanged" : "updated";
119
- if (action !== "unchanged") {
120
- const parent = dirname(target.relPath);
121
- if (parent !== ".") ensureDir(options.root, parent);
122
- writeFileAtomic(target.absPath, html, target.relPath);
123
- }
124
- const data: ExplorerArtifactResult = {
125
- artifactVersion: EXPLORER_ARTIFACT_VERSION,
126
- snapshotSchemaVersion: schemaVersion,
127
- snapshotKey,
128
- path: target.relPath,
129
- action,
130
- byteLength: Buffer.byteLength(html),
131
- digest: explorerArtifactDigest(html),
132
- counts,
133
- };
134
- emit(explorerRenderable(data), options.output, options.stdout);
135
- return EXIT_OK;
136
- }
137
-
138
- async function loadProductionSnapshot(options: ExplorerSnapshotLoaderOptions): Promise<ExplorerSnapshot> {
139
- const source = await loadLadybugProjectionSource({
140
- root: options.root,
141
- ladybugVersion: EXPECTED_LADYBUG_VERSION,
142
- ladybugStorageVersion: EXPECTED_LADYBUG_STORAGE_VERSION,
143
- adapter: options.adapter,
144
- warnings: options.warnings,
145
- });
146
- return buildExplorerSnapshot(source);
147
- }
148
-
149
- function parseExplorerArgs(args: readonly string[]): ExplorerArgs {
150
- const parsed = parseCommandArgs(args, "explorer");
151
- assertFlagAtMostOnce(parsed, "out");
152
- assertFlagAtMostOnce(parsed, "force");
153
- const workspace = workspaceSelection(parsed);
154
- if (parsed.positionals.length > 0) {
155
- throw usage(`unexpected argument "${parsed.positionals[0]}"`, "run `lore explorer [--out <file>] [--force]`");
156
- }
157
- const out = singleOptionValue(parsed, "out");
158
- const snapshot = nonEmptySelector(singleOptionValue(parsed, "snapshot"), "snapshot");
159
- const from = nonEmptySelector(singleOptionValue(parsed, "from"), "from");
160
- const to = nonEmptySelector(singleOptionValue(parsed, "to"), "to");
161
- if (out === "") throw usage("--out needs a value", "pass a repository-relative .html file path");
162
- if (snapshot !== undefined && (from !== undefined || to !== undefined)) {
163
- throw usage(
164
- "--snapshot is mutually exclusive with --from and --to",
165
- "choose a retained snapshot view or a comparison",
166
- );
167
- }
168
- if ((from === undefined) !== (to === undefined)) {
169
- throw usage("--from and --to must be supplied together", "pass both retained snapshot selectors");
170
- }
171
- if (workspace !== undefined && snapshot === undefined && from === undefined) {
172
- throw usage("--workspace requires retained snapshot selectors", "pass --snapshot or both --from and --to");
173
- }
174
- return {
175
- out: out ?? DEFAULT_EXPLORER_ARTIFACT_PATH,
176
- customOut: out !== undefined,
177
- force: parsed.flags.has("force"),
178
- ...(snapshot !== undefined ? { snapshot } : {}),
179
- ...(from !== undefined ? { from, to: to as string } : {}),
180
- ...(workspace !== undefined ? { workspace } : {}),
181
- };
182
- }
183
-
184
- function nonEmptySelector(value: string | undefined, flag: string): string | undefined {
185
- if (value === undefined) return undefined;
186
- if (value.trim() === "")
187
- throw usage(`--${flag} needs a value`, "pass an exact retained snapshot key or unambiguous commit");
188
- return value.trim();
189
- }
190
-
191
- function retainedCounts(
192
- snapshot: RetainedSnapshot,
193
- repositories: readonly string[],
194
- ): ExplorerSnapshot["health"]["counts"] {
195
- const selected = new Set(repositories);
196
- const facts = snapshot.facts.filter(
197
- (fact) => selected.size === 0 || (fact.provenance.memberId !== null && selected.has(fact.provenance.memberId)),
198
- );
199
- const edgeFingerprints = new Set<string>();
200
- let danglingEdges = 0;
201
- let duplicateEdges = 0;
202
- for (const fact of facts) {
203
- if (fact.kind !== "edge") continue;
204
- if (fact.value.dangling === true) danglingEdges += 1;
205
- const fingerprint = [fact.value.from, fact.value.kind, fact.value.target].join("\0");
206
- if (edgeFingerprints.has(fingerprint)) duplicateEdges += 1;
207
- edgeFingerprints.add(fingerprint);
208
- }
209
- return {
210
- repositories:
211
- selected.size === 0
212
- ? snapshot.repositories.length
213
- : snapshot.repositories.filter(
214
- (repository) => repository.memberId !== null && selected.has(repository.memberId),
215
- ).length,
216
- concepts: facts.filter((fact) => fact.kind === "concept").length,
217
- tasks: facts.filter((fact) => fact.kind === "task").length,
218
- authoredEdges: facts.filter((fact) => fact.kind === "edge").length,
219
- danglingEdges,
220
- duplicateEdges,
221
- };
222
- }
223
-
224
- function confineArtifactPath(path: string, root: string): { readonly absPath: string; readonly relPath: string } {
225
- const absPath = resolve(root, path);
226
- const rel = relative(root, absPath);
227
- if (isAbsolute(path) || rel === "" || rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {
228
- throw usage(`--out path "${path}" must name a file inside the repo`, "give --out a repo-relative .html file path");
229
- }
230
- const relPath = rel.split(sep).join("/");
231
- const first = relPath.split("/")[0]?.toLowerCase();
232
- if (first === "docs" || first === "backlog" || first === ".git") {
233
- throw usage(
234
- `--out path "${path}" targets protected repository source`,
235
- "write the derived artifact outside docs/, backlog/, and .git/, e.g. .lore/explorer/index.html",
236
- );
237
- }
238
- if (!relPath.toLowerCase().endsWith(".html")) {
239
- throw usage(`--out path "${path}" must end in .html`, "pass a self-contained HTML artifact path");
240
- }
241
- return { absPath, relPath };
242
- }
243
-
244
- function explorerRenderable(data: ExplorerArtifactResult): Renderable<ExplorerArtifactResult> {
245
- return { kind: "explorer.artifact", data, pretty: renderText, plain: renderText };
246
- }
247
-
248
- function renderText(data: ExplorerArtifactResult): string {
249
- return [
250
- `${data.action} ${data.path} (${data.byteLength} bytes, ${data.artifactVersion})`,
251
- `snapshot ${data.snapshotKey}: ${data.counts.concepts} concepts, ${data.counts.tasks} tasks, ${data.counts.authoredEdges} edges`,
252
- ].join("\n");
253
- }
@@ -1,93 +0,0 @@
1
- /** `lore export --schema-version 1.0` — deterministic JSONL OKF projection. */
2
-
3
- import { join } from "node:path";
4
- import { type BacklogAdapter, bunBacklogSpawn, createBacklogAdapter } from "../adapters/backlog";
5
- import { resolveHeadSha } from "../adapters/git";
6
- import { loadBundle } from "../core/bundle";
7
- import { loadProfile } from "../core/profile";
8
- import { buildProjection, PROJECTION_SCHEMA_VERSION } from "../core/projection";
9
- import { DOCS_DIR } from "../core/scaffold";
10
- import { EXIT_OK, LoreError, WarningCollector, type Writer } from "../errors";
11
- import { VERSION } from "../meta";
12
- import { emit, type OutputContext, type Renderable } from "../output";
13
- import { parseCommandArgs, singleOptionValue } from "./args";
14
-
15
- export interface ExportOptions {
16
- readonly root: string;
17
- readonly output: OutputContext;
18
- readonly args: readonly string[];
19
- readonly stdout?: Writer;
20
- readonly stderr?: Writer;
21
- readonly adapter?: BacklogAdapter;
22
- readonly resolveGitCommit?: (root: string) => string | null;
23
- readonly generatedAt?: string | null;
24
- }
25
-
26
- export async function runExport(options: ExportOptions): Promise<number> {
27
- const schemaVersion = parseExportArgs(options.args, options.output);
28
- // Everything below this line may touch bundle, Backlog, or Git state. Keeping
29
- // version parsing above it makes unsupported breaking versions fail first.
30
- const profile = loadProfile({ root: options.root });
31
- const warnings = new WarningCollector();
32
- const graph = loadBundle(join(options.root, DOCS_DIR), { warnings, profile });
33
- warnings.flush({ color: options.output.color, stderr: options.stderr });
34
- const adapter = options.adapter ?? createBacklogAdapter(bunBacklogSpawn(undefined, options.root));
35
- const tasks = await adapter.listTasks();
36
- const gitCommit = (options.resolveGitCommit ?? resolveHeadSha)(options.root);
37
- const projection = buildProjection({
38
- graph,
39
- tasks,
40
- docsRoot: DOCS_DIR,
41
- okfVersion: profile.okfVersion,
42
- exporterVersion: VERSION,
43
- gitCommit,
44
- generatedAt:
45
- options.generatedAt !== undefined ? options.generatedAt : sourceDateEpoch(process.env.SOURCE_DATE_EPOCH),
46
- });
47
- if (options.output.mode === "json") {
48
- const data = { projectionSchemaVersion: schemaVersion, records: projection.records };
49
- const renderable: Renderable<typeof data> = {
50
- kind: "projection.export",
51
- data,
52
- pretty: () => projection.jsonl,
53
- plain: () => projection.jsonl,
54
- };
55
- emit(renderable, options.output, options.stdout);
56
- } else {
57
- (options.stdout ?? process.stdout).write(projection.jsonl);
58
- }
59
- return EXIT_OK;
60
- }
61
-
62
- function parseExportArgs(args: readonly string[], output: OutputContext): string {
63
- void output;
64
- const parsed = parseCommandArgs(args, "export");
65
- if (parsed.positionals.length > 0) {
66
- throw usage(`unexpected argument "${parsed.positionals[0]}"`, "run `lore export --help` to list options");
67
- }
68
- const rawVersion = singleOptionValue(parsed, "schema-version");
69
- if (rawVersion === "") {
70
- throw usage("--schema-version needs a value", `pass --schema-version ${PROJECTION_SCHEMA_VERSION}`);
71
- }
72
- const version = rawVersion ?? PROJECTION_SCHEMA_VERSION;
73
- if (version !== PROJECTION_SCHEMA_VERSION) {
74
- throw usage(
75
- `unsupported projection schema version "${version}"`,
76
- `this lore supports ${PROJECTION_SCHEMA_VERSION}`,
77
- );
78
- }
79
- return version;
80
- }
81
-
82
- function sourceDateEpoch(raw: string | undefined): string | null {
83
- if (raw === undefined || raw.trim() === "") return null;
84
- const seconds = Number(raw);
85
- if (!Number.isFinite(seconds) || seconds < 0) {
86
- throw usage("SOURCE_DATE_EPOCH must be a non-negative number", "unset it or provide Unix epoch seconds");
87
- }
88
- return new Date(seconds * 1000).toISOString();
89
- }
90
-
91
- function usage(message: string, hint: string): LoreError {
92
- return new LoreError("usage", message, hint);
93
- }