@oprt/cli 0.0.0-stage → 0.3.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/read.cjs ADDED
@@ -0,0 +1,284 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/read.ts
21
+ var read_exports = {};
22
+ __export(read_exports, {
23
+ END: () => END,
24
+ START: () => START,
25
+ componentDetail: () => componentDetail,
26
+ componentIndex: () => componentIndex,
27
+ renderAgentBlock: () => renderAgentBlock,
28
+ renderComponent: () => renderComponent,
29
+ search: () => search
30
+ });
31
+ module.exports = __toCommonJS(read_exports);
32
+
33
+ // src/types.ts
34
+ var CliError = class extends Error {
35
+ constructor(message, exitCode = 1, details = {}) {
36
+ super(message);
37
+ this.exitCode = exitCode;
38
+ this.name = "CliError";
39
+ this.code = details.code ?? (exitCode === 2 ? "ERR_USAGE" : "ERR_FAILED");
40
+ if (details.suggestions !== void 0) this.suggestions = details.suggestions;
41
+ }
42
+ exitCode;
43
+ code;
44
+ suggestions;
45
+ };
46
+
47
+ // src/lookup.ts
48
+ var SEARCH_LIMIT_DEFAULT = 10;
49
+ var squash = (name) => name.toLowerCase().replace(/[^a-z0-9]/g, "");
50
+ var firstSentence = (text) => /^.*?[.!?](?=\s|$)/.exec(text)?.[0] ?? text;
51
+ function distance(a, b) {
52
+ const row = Array.from({ length: b.length + 1 }, (_, index) => index);
53
+ for (let i = 1; i <= a.length; i++) {
54
+ let diagonal = row[0];
55
+ row[0] = i;
56
+ for (let j = 1; j <= b.length; j++) {
57
+ const above = row[j];
58
+ row[j] = Math.min(above + 1, row[j - 1] + 1, diagonal + (a[i - 1] === b[j - 1] ? 0 : 1));
59
+ diagonal = above;
60
+ }
61
+ }
62
+ return row[b.length];
63
+ }
64
+ function nearest(wanted, names, limit = 5) {
65
+ const target = squash(wanted);
66
+ return names.map((name) => {
67
+ const candidate = squash(name);
68
+ const apart = distance(target, candidate);
69
+ const close = apart <= 2 || target.length >= 3 && (candidate.includes(target) || target.includes(candidate));
70
+ return { name, apart, close };
71
+ }).filter(({ close }) => close).sort((a, b) => a.apart - b.apart || a.name.localeCompare(b.name)).slice(0, limit).map(({ name }) => name);
72
+ }
73
+ function resolveComponent(contract, wanted) {
74
+ const names = Object.keys(contract.components);
75
+ const found = names.find((name) => name === wanted) ?? names.find((name) => squash(name) === squash(wanted));
76
+ if (found !== void 0) return found;
77
+ const suggestions = nearest(wanted, names);
78
+ throw new CliError(
79
+ `Unknown component ${JSON.stringify(wanted)}.` + (suggestions.length > 0 ? ` Did you mean ${suggestions.join(", ")}?` : "") + " Run oprt component --list.",
80
+ 2,
81
+ { code: "ERR_UNKNOWN_COMPONENT", suggestions }
82
+ );
83
+ }
84
+ function allowedChildren(rules, name) {
85
+ for (const [parent, rule] of Object.entries(rules ?? {})) {
86
+ if (parent === name) return rule.allowed_children;
87
+ const nested = allowedChildren(rule.children, name);
88
+ if (nested !== void 0) return nested;
89
+ }
90
+ return void 0;
91
+ }
92
+ var partsOf = (contract, name) => Object.keys(contract.components).filter(
93
+ (part) => contract.guidance.components[part]?.part_of === name
94
+ );
95
+ function componentIndex(contract) {
96
+ return Object.keys(contract.components).filter((name) => (contract.guidance.components[name]?.use_for ?? []).length > 0).sort((a, b) => a.localeCompare(b)).map((name) => ({
97
+ name,
98
+ purpose: contract.guidance.components[name].use_for.join("; "),
99
+ parts: partsOf(contract, name)
100
+ }));
101
+ }
102
+ function componentDetail(contract, wanted) {
103
+ const name = resolveComponent(contract, wanted);
104
+ const component = contract.components[name];
105
+ const guide = contract.guidance.components[name] ?? {};
106
+ const props = Object.entries(component.props).map(([prop, definition]) => {
107
+ const notes = guide.values?.[prop];
108
+ return { name: prop, ...definition, ...notes === void 0 ? {} : { notes } };
109
+ });
110
+ const allowed = allowedChildren(contract.composition_rules, name);
111
+ return {
112
+ name,
113
+ description: component.description,
114
+ use_for: guide.use_for ?? [],
115
+ not_for: guide.not_for ?? [],
116
+ ...guide.part_of === void 0 ? {} : { part_of: guide.part_of },
117
+ parts: partsOf(contract, name),
118
+ props,
119
+ ...component.slots === void 0 ? {} : { slots: component.slots },
120
+ composable_with: component.composable_with ?? [],
121
+ never_inside: component.never_inside ?? [],
122
+ ...allowed === void 0 ? {} : { allowed_children: allowed },
123
+ example: component.example
124
+ };
125
+ }
126
+ function propLine(prop, dense) {
127
+ const kind = prop.type === "enum" ? (prop.values ?? []).map((value) => value === prop.default ? `${value} (default)` : value).join(" | ") : (prop.type === "union" ? (prop.types ?? []).join(" or ") : prop.type) + (prop.default === void 0 ? "" : ` (default ${String(prop.default)})`);
128
+ const description = prop.description === void 0 ? "" : dense ? firstSentence(prop.description) : prop.description;
129
+ const lines = [
130
+ ` ${prop.name}: ${kind}${prop.required ? ", required" : ""}${description ? `. ${description}` : ""}`
131
+ ];
132
+ for (const [value, note] of Object.entries(prop.notes ?? {})) lines.push(` ${value}: ${note}`);
133
+ return lines;
134
+ }
135
+ function renderComponent(detail, { dense }) {
136
+ const lines = [
137
+ detail.part_of === void 0 ? `${detail.name}: ${detail.use_for.join("; ")}` : `${detail.name}: a part of ${detail.part_of}. ${firstSentence(detail.description)}`
138
+ ];
139
+ if (detail.not_for.length > 0)
140
+ lines.push(
141
+ `Not for: ${detail.not_for.map(({ need, use }) => `${need} (use ${use})`).join("; ")}`
142
+ );
143
+ if (!dense) lines.push("", detail.description, "");
144
+ if (detail.props.length > 0) {
145
+ lines.push("Props:");
146
+ for (const prop of detail.props) lines.push(...propLine(prop, dense));
147
+ }
148
+ for (const [slot, { type, required }] of Object.entries(detail.slots ?? {}))
149
+ lines.push(
150
+ `${slot === "children" ? "Children" : `Slot ${slot}`}: ${type}${required ? ", required" : ""}`
151
+ );
152
+ if (detail.parts.length > 0) lines.push(`Parts: ${detail.parts.join(", ")}`);
153
+ if (detail.allowed_children !== void 0)
154
+ lines.push(`Takes directly inside: ${detail.allowed_children.join(", ")}`);
155
+ if (detail.composable_with.length > 0)
156
+ lines.push(`Goes inside: ${detail.composable_with.join(", ")}`);
157
+ if (detail.never_inside.length > 0) lines.push(`Never inside: ${detail.never_inside.join(", ")}`);
158
+ lines.push(
159
+ detail.example.includes("\n") ? `Example:
160
+ ${detail.example.split("\n").map((line) => ` ${line}`).join("\n")}` : `Example: ${detail.example}`
161
+ );
162
+ return `${lines.join("\n")}
163
+ `;
164
+ }
165
+ var STOP = new Set(
166
+ "a an the to of for in on at with and or i we need want that which is are it its my some from by".split(
167
+ " "
168
+ )
169
+ );
170
+ var words = (text) => (text.toLowerCase().match(/[a-z0-9]+/g) ?? []).filter((word) => !STOP.has(word)).map((word) => word.length > 4 ? word.replace(/(?:ing|ed|es|s)$/, "") : word);
171
+ var matched = (query, text) => {
172
+ const present = new Set(words(text));
173
+ return query.filter((word) => present.has(word)).length;
174
+ };
175
+ function search(contract, registry, query, limit = SEARCH_LIMIT_DEFAULT) {
176
+ const wanted = words(query);
177
+ if (wanted.length === 0) return [];
178
+ const whole = squash(query);
179
+ const scores = /* @__PURE__ */ new Map();
180
+ const add = (name, points, why) => {
181
+ const before = scores.get(name) ?? { score: 0 };
182
+ scores.set(name, {
183
+ score: before.score + points,
184
+ ...before.why !== void 0 ? { why: before.why } : why !== void 0 ? { why } : {}
185
+ });
186
+ };
187
+ for (const [name, component] of Object.entries(contract.components)) {
188
+ const guide = contract.guidance.components[name] ?? {};
189
+ const plain = squash(name);
190
+ if (plain === whole) add(name, 100);
191
+ else if (wanted.some((word) => word.length >= 3 && plain.includes(word))) add(name, 15);
192
+ else if (wanted.some((word) => word.length >= 4 && distance(word, plain) <= 1)) add(name, 12);
193
+ for (const phrase of guide.use_for ?? []) {
194
+ const hits = matched(wanted, phrase);
195
+ if (hits > 0) add(name, 12 * hits + (hits === wanted.length ? 40 : 0));
196
+ }
197
+ for (const { need, use } of guide.not_for ?? []) {
198
+ const hits = matched(wanted, need);
199
+ if (hits > 0 && hits >= Math.ceil(wanted.length * 0.6))
200
+ add(use, 10 * hits + (hits === wanted.length ? 15 : 0), `${name} is not for this: ${need}`);
201
+ }
202
+ const inDescription = matched(wanted, component.description);
203
+ if (inDescription > 0) add(name, 2 * inDescription);
204
+ }
205
+ const results = [];
206
+ for (const [name, { score, why }] of scores) {
207
+ const guide = contract.guidance.components[name] ?? {};
208
+ results.push({
209
+ kind: "component",
210
+ name,
211
+ purpose: guide.part_of === void 0 ? (guide.use_for ?? []).join("; ") : `a part of ${guide.part_of}`,
212
+ score,
213
+ ...why === void 0 ? {} : { why },
214
+ next: `oprt component ${name}`
215
+ });
216
+ }
217
+ for (const entry of registry.entries.filter(({ kind }) => kind === "recipe")) {
218
+ const recipe = contract.patterns?.[entry.name];
219
+ const plain = squash(entry.name);
220
+ const inName = plain === whole ? 100 : wanted.some((word) => word.length >= 3 && plain.includes(word)) ? 15 : 0;
221
+ const hits = matched(wanted, entry.description);
222
+ const score = inName + (hits > 0 ? 10 * hits + (hits === wanted.length ? 15 : 0) : 0);
223
+ if (score > 0)
224
+ results.push({
225
+ kind: "recipe",
226
+ name: entry.name,
227
+ purpose: firstSentence(entry.description),
228
+ score,
229
+ next: recipe?.install ?? `oprt add ${entry.slug}`
230
+ });
231
+ }
232
+ results.sort((a, b) => b.score - a.score || a.name.localeCompare(b.name));
233
+ const floor = Math.max(4, (results[0]?.score ?? 0) * 0.15);
234
+ return results.filter(({ score }) => score >= floor).slice(0, limit);
235
+ }
236
+
237
+ // src/agent-block.ts
238
+ var START = "<!-- OPERATOR:START -->";
239
+ var END = "<!-- OPERATOR:END -->";
240
+ var firstSentence2 = (text) => /^.*?[.!?](?=\s|$)/.exec(text)?.[0] ?? text;
241
+ function renderAgentBlock(contract) {
242
+ const lines = [
243
+ START,
244
+ `<!-- Written by oprt ${contract.version}. Do not edit between these markers: run \`oprt init\` to refresh. -->`,
245
+ "## OPERATOR design system",
246
+ "",
247
+ "This project builds its interface with OPERATOR (`@oprt/react`). Use its components, and look them up instead of guessing: these commands print the facts for the version installed here. Run them with the project's package runner (`npx oprt \u2026`, `pnpm exec oprt \u2026`).",
248
+ "",
249
+ "- `oprt search <need>`: which component or recipe answers a need",
250
+ "- `oprt component <Name>`: one component's options, nesting rules and example (`--dense` for the short form)",
251
+ "- `oprt add <recipe>`: copy a working screen into the project to adapt",
252
+ "- `oprt check <paths>`: check written code against the contract",
253
+ "",
254
+ "### Rules",
255
+ ""
256
+ ];
257
+ contract.guidance.rules.forEach(({ rule, wrong, right }, index) => {
258
+ lines.push(`${index + 1}. ${rule}`);
259
+ if (wrong !== void 0) lines.push(` Wrong: \`${wrong}\``);
260
+ if (right !== void 0) lines.push(` Right: \`${right}\``);
261
+ });
262
+ lines.push("", "### Components", "");
263
+ for (const { name, purpose, parts } of componentIndex(contract))
264
+ lines.push(`- ${name}${parts.length > 0 ? ` (${parts.join(", ")})` : ""}: ${purpose}`);
265
+ const recipes = Object.entries(contract.patterns ?? {});
266
+ if (recipes.length > 0) {
267
+ lines.push("", "### Recipes (whole screens to start from)", "");
268
+ for (const [name, { description, install }] of recipes)
269
+ lines.push(`- ${name}: ${firstSentence2(description)} \`${install}\``);
270
+ }
271
+ lines.push(END);
272
+ return `${lines.join("\n")}
273
+ `;
274
+ }
275
+ // Annotate the CommonJS export names for ESM import in node:
276
+ 0 && (module.exports = {
277
+ END,
278
+ START,
279
+ componentDetail,
280
+ componentIndex,
281
+ renderAgentBlock,
282
+ renderComponent,
283
+ search
284
+ });
@@ -0,0 +1 @@
1
+ export { a as ComponentDetail, C as Contract, D as DetailProp, E as END, e as IndexEntry, m as START, S as SearchResult, i as componentDetail, j as componentIndex, k as renderAgentBlock, l as renderComponent, s as search } from './read-C7rE5UlY.cjs';
package/dist/read.d.ts ADDED
@@ -0,0 +1 @@
1
+ export { a as ComponentDetail, C as Contract, D as DetailProp, E as END, e as IndexEntry, m as START, S as SearchResult, i as componentDetail, j as componentIndex, k as renderAgentBlock, l as renderComponent, s as search } from './read-C7rE5UlY.js';
package/dist/read.js ADDED
@@ -0,0 +1,251 @@
1
+ // src/types.ts
2
+ var CliError = class extends Error {
3
+ constructor(message, exitCode = 1, details = {}) {
4
+ super(message);
5
+ this.exitCode = exitCode;
6
+ this.name = "CliError";
7
+ this.code = details.code ?? (exitCode === 2 ? "ERR_USAGE" : "ERR_FAILED");
8
+ if (details.suggestions !== void 0) this.suggestions = details.suggestions;
9
+ }
10
+ exitCode;
11
+ code;
12
+ suggestions;
13
+ };
14
+
15
+ // src/lookup.ts
16
+ var SEARCH_LIMIT_DEFAULT = 10;
17
+ var squash = (name) => name.toLowerCase().replace(/[^a-z0-9]/g, "");
18
+ var firstSentence = (text) => /^.*?[.!?](?=\s|$)/.exec(text)?.[0] ?? text;
19
+ function distance(a, b) {
20
+ const row = Array.from({ length: b.length + 1 }, (_, index) => index);
21
+ for (let i = 1; i <= a.length; i++) {
22
+ let diagonal = row[0];
23
+ row[0] = i;
24
+ for (let j = 1; j <= b.length; j++) {
25
+ const above = row[j];
26
+ row[j] = Math.min(above + 1, row[j - 1] + 1, diagonal + (a[i - 1] === b[j - 1] ? 0 : 1));
27
+ diagonal = above;
28
+ }
29
+ }
30
+ return row[b.length];
31
+ }
32
+ function nearest(wanted, names, limit = 5) {
33
+ const target = squash(wanted);
34
+ return names.map((name) => {
35
+ const candidate = squash(name);
36
+ const apart = distance(target, candidate);
37
+ const close = apart <= 2 || target.length >= 3 && (candidate.includes(target) || target.includes(candidate));
38
+ return { name, apart, close };
39
+ }).filter(({ close }) => close).sort((a, b) => a.apart - b.apart || a.name.localeCompare(b.name)).slice(0, limit).map(({ name }) => name);
40
+ }
41
+ function resolveComponent(contract, wanted) {
42
+ const names = Object.keys(contract.components);
43
+ const found = names.find((name) => name === wanted) ?? names.find((name) => squash(name) === squash(wanted));
44
+ if (found !== void 0) return found;
45
+ const suggestions = nearest(wanted, names);
46
+ throw new CliError(
47
+ `Unknown component ${JSON.stringify(wanted)}.` + (suggestions.length > 0 ? ` Did you mean ${suggestions.join(", ")}?` : "") + " Run oprt component --list.",
48
+ 2,
49
+ { code: "ERR_UNKNOWN_COMPONENT", suggestions }
50
+ );
51
+ }
52
+ function allowedChildren(rules, name) {
53
+ for (const [parent, rule] of Object.entries(rules ?? {})) {
54
+ if (parent === name) return rule.allowed_children;
55
+ const nested = allowedChildren(rule.children, name);
56
+ if (nested !== void 0) return nested;
57
+ }
58
+ return void 0;
59
+ }
60
+ var partsOf = (contract, name) => Object.keys(contract.components).filter(
61
+ (part) => contract.guidance.components[part]?.part_of === name
62
+ );
63
+ function componentIndex(contract) {
64
+ return Object.keys(contract.components).filter((name) => (contract.guidance.components[name]?.use_for ?? []).length > 0).sort((a, b) => a.localeCompare(b)).map((name) => ({
65
+ name,
66
+ purpose: contract.guidance.components[name].use_for.join("; "),
67
+ parts: partsOf(contract, name)
68
+ }));
69
+ }
70
+ function componentDetail(contract, wanted) {
71
+ const name = resolveComponent(contract, wanted);
72
+ const component = contract.components[name];
73
+ const guide = contract.guidance.components[name] ?? {};
74
+ const props = Object.entries(component.props).map(([prop, definition]) => {
75
+ const notes = guide.values?.[prop];
76
+ return { name: prop, ...definition, ...notes === void 0 ? {} : { notes } };
77
+ });
78
+ const allowed = allowedChildren(contract.composition_rules, name);
79
+ return {
80
+ name,
81
+ description: component.description,
82
+ use_for: guide.use_for ?? [],
83
+ not_for: guide.not_for ?? [],
84
+ ...guide.part_of === void 0 ? {} : { part_of: guide.part_of },
85
+ parts: partsOf(contract, name),
86
+ props,
87
+ ...component.slots === void 0 ? {} : { slots: component.slots },
88
+ composable_with: component.composable_with ?? [],
89
+ never_inside: component.never_inside ?? [],
90
+ ...allowed === void 0 ? {} : { allowed_children: allowed },
91
+ example: component.example
92
+ };
93
+ }
94
+ function propLine(prop, dense) {
95
+ const kind = prop.type === "enum" ? (prop.values ?? []).map((value) => value === prop.default ? `${value} (default)` : value).join(" | ") : (prop.type === "union" ? (prop.types ?? []).join(" or ") : prop.type) + (prop.default === void 0 ? "" : ` (default ${String(prop.default)})`);
96
+ const description = prop.description === void 0 ? "" : dense ? firstSentence(prop.description) : prop.description;
97
+ const lines = [
98
+ ` ${prop.name}: ${kind}${prop.required ? ", required" : ""}${description ? `. ${description}` : ""}`
99
+ ];
100
+ for (const [value, note] of Object.entries(prop.notes ?? {})) lines.push(` ${value}: ${note}`);
101
+ return lines;
102
+ }
103
+ function renderComponent(detail, { dense }) {
104
+ const lines = [
105
+ detail.part_of === void 0 ? `${detail.name}: ${detail.use_for.join("; ")}` : `${detail.name}: a part of ${detail.part_of}. ${firstSentence(detail.description)}`
106
+ ];
107
+ if (detail.not_for.length > 0)
108
+ lines.push(
109
+ `Not for: ${detail.not_for.map(({ need, use }) => `${need} (use ${use})`).join("; ")}`
110
+ );
111
+ if (!dense) lines.push("", detail.description, "");
112
+ if (detail.props.length > 0) {
113
+ lines.push("Props:");
114
+ for (const prop of detail.props) lines.push(...propLine(prop, dense));
115
+ }
116
+ for (const [slot, { type, required }] of Object.entries(detail.slots ?? {}))
117
+ lines.push(
118
+ `${slot === "children" ? "Children" : `Slot ${slot}`}: ${type}${required ? ", required" : ""}`
119
+ );
120
+ if (detail.parts.length > 0) lines.push(`Parts: ${detail.parts.join(", ")}`);
121
+ if (detail.allowed_children !== void 0)
122
+ lines.push(`Takes directly inside: ${detail.allowed_children.join(", ")}`);
123
+ if (detail.composable_with.length > 0)
124
+ lines.push(`Goes inside: ${detail.composable_with.join(", ")}`);
125
+ if (detail.never_inside.length > 0) lines.push(`Never inside: ${detail.never_inside.join(", ")}`);
126
+ lines.push(
127
+ detail.example.includes("\n") ? `Example:
128
+ ${detail.example.split("\n").map((line) => ` ${line}`).join("\n")}` : `Example: ${detail.example}`
129
+ );
130
+ return `${lines.join("\n")}
131
+ `;
132
+ }
133
+ var STOP = new Set(
134
+ "a an the to of for in on at with and or i we need want that which is are it its my some from by".split(
135
+ " "
136
+ )
137
+ );
138
+ var words = (text) => (text.toLowerCase().match(/[a-z0-9]+/g) ?? []).filter((word) => !STOP.has(word)).map((word) => word.length > 4 ? word.replace(/(?:ing|ed|es|s)$/, "") : word);
139
+ var matched = (query, text) => {
140
+ const present = new Set(words(text));
141
+ return query.filter((word) => present.has(word)).length;
142
+ };
143
+ function search(contract, registry, query, limit = SEARCH_LIMIT_DEFAULT) {
144
+ const wanted = words(query);
145
+ if (wanted.length === 0) return [];
146
+ const whole = squash(query);
147
+ const scores = /* @__PURE__ */ new Map();
148
+ const add = (name, points, why) => {
149
+ const before = scores.get(name) ?? { score: 0 };
150
+ scores.set(name, {
151
+ score: before.score + points,
152
+ ...before.why !== void 0 ? { why: before.why } : why !== void 0 ? { why } : {}
153
+ });
154
+ };
155
+ for (const [name, component] of Object.entries(contract.components)) {
156
+ const guide = contract.guidance.components[name] ?? {};
157
+ const plain = squash(name);
158
+ if (plain === whole) add(name, 100);
159
+ else if (wanted.some((word) => word.length >= 3 && plain.includes(word))) add(name, 15);
160
+ else if (wanted.some((word) => word.length >= 4 && distance(word, plain) <= 1)) add(name, 12);
161
+ for (const phrase of guide.use_for ?? []) {
162
+ const hits = matched(wanted, phrase);
163
+ if (hits > 0) add(name, 12 * hits + (hits === wanted.length ? 40 : 0));
164
+ }
165
+ for (const { need, use } of guide.not_for ?? []) {
166
+ const hits = matched(wanted, need);
167
+ if (hits > 0 && hits >= Math.ceil(wanted.length * 0.6))
168
+ add(use, 10 * hits + (hits === wanted.length ? 15 : 0), `${name} is not for this: ${need}`);
169
+ }
170
+ const inDescription = matched(wanted, component.description);
171
+ if (inDescription > 0) add(name, 2 * inDescription);
172
+ }
173
+ const results = [];
174
+ for (const [name, { score, why }] of scores) {
175
+ const guide = contract.guidance.components[name] ?? {};
176
+ results.push({
177
+ kind: "component",
178
+ name,
179
+ purpose: guide.part_of === void 0 ? (guide.use_for ?? []).join("; ") : `a part of ${guide.part_of}`,
180
+ score,
181
+ ...why === void 0 ? {} : { why },
182
+ next: `oprt component ${name}`
183
+ });
184
+ }
185
+ for (const entry of registry.entries.filter(({ kind }) => kind === "recipe")) {
186
+ const recipe = contract.patterns?.[entry.name];
187
+ const plain = squash(entry.name);
188
+ const inName = plain === whole ? 100 : wanted.some((word) => word.length >= 3 && plain.includes(word)) ? 15 : 0;
189
+ const hits = matched(wanted, entry.description);
190
+ const score = inName + (hits > 0 ? 10 * hits + (hits === wanted.length ? 15 : 0) : 0);
191
+ if (score > 0)
192
+ results.push({
193
+ kind: "recipe",
194
+ name: entry.name,
195
+ purpose: firstSentence(entry.description),
196
+ score,
197
+ next: recipe?.install ?? `oprt add ${entry.slug}`
198
+ });
199
+ }
200
+ results.sort((a, b) => b.score - a.score || a.name.localeCompare(b.name));
201
+ const floor = Math.max(4, (results[0]?.score ?? 0) * 0.15);
202
+ return results.filter(({ score }) => score >= floor).slice(0, limit);
203
+ }
204
+
205
+ // src/agent-block.ts
206
+ var START = "<!-- OPERATOR:START -->";
207
+ var END = "<!-- OPERATOR:END -->";
208
+ var firstSentence2 = (text) => /^.*?[.!?](?=\s|$)/.exec(text)?.[0] ?? text;
209
+ function renderAgentBlock(contract) {
210
+ const lines = [
211
+ START,
212
+ `<!-- Written by oprt ${contract.version}. Do not edit between these markers: run \`oprt init\` to refresh. -->`,
213
+ "## OPERATOR design system",
214
+ "",
215
+ "This project builds its interface with OPERATOR (`@oprt/react`). Use its components, and look them up instead of guessing: these commands print the facts for the version installed here. Run them with the project's package runner (`npx oprt \u2026`, `pnpm exec oprt \u2026`).",
216
+ "",
217
+ "- `oprt search <need>`: which component or recipe answers a need",
218
+ "- `oprt component <Name>`: one component's options, nesting rules and example (`--dense` for the short form)",
219
+ "- `oprt add <recipe>`: copy a working screen into the project to adapt",
220
+ "- `oprt check <paths>`: check written code against the contract",
221
+ "",
222
+ "### Rules",
223
+ ""
224
+ ];
225
+ contract.guidance.rules.forEach(({ rule, wrong, right }, index) => {
226
+ lines.push(`${index + 1}. ${rule}`);
227
+ if (wrong !== void 0) lines.push(` Wrong: \`${wrong}\``);
228
+ if (right !== void 0) lines.push(` Right: \`${right}\``);
229
+ });
230
+ lines.push("", "### Components", "");
231
+ for (const { name, purpose, parts } of componentIndex(contract))
232
+ lines.push(`- ${name}${parts.length > 0 ? ` (${parts.join(", ")})` : ""}: ${purpose}`);
233
+ const recipes = Object.entries(contract.patterns ?? {});
234
+ if (recipes.length > 0) {
235
+ lines.push("", "### Recipes (whole screens to start from)", "");
236
+ for (const [name, { description, install }] of recipes)
237
+ lines.push(`- ${name}: ${firstSentence2(description)} \`${install}\``);
238
+ }
239
+ lines.push(END);
240
+ return `${lines.join("\n")}
241
+ `;
242
+ }
243
+ export {
244
+ END,
245
+ START,
246
+ componentDetail,
247
+ componentIndex,
248
+ renderAgentBlock,
249
+ renderComponent,
250
+ search
251
+ };