@uicontract/namer 0.1.0 → 0.2.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.
package/README.md CHANGED
@@ -40,9 +40,36 @@ Examples:
40
40
 
41
41
  The namer is deterministic: the same element always receives the same ID across runs. It does not read source files or write anything to disk.
42
42
 
43
- ## Part of UIC
43
+ ## AI-Assisted Naming
44
44
 
45
- This package is part of [UIC (UI Contracts)](https://github.com/sherifkozman/uicontract) making web app UIs machine-readable.
45
+ For elements lacking strong naming signals (no label or handler), enable AI naming to get context-aware IDs instead of fallback line-number names.
46
+
47
+ ```typescript
48
+ import { assignNames } from '@uicontract/namer';
49
+ import type { RawElement } from '@uicontract/core';
50
+
51
+ const named = await assignNames(rawElements, {
52
+ projectRoot: '/path/to/my-app',
53
+ ai: true, // requires OPENAI_API_KEY, ANTHROPIC_API_KEY, or GOOGLE_API_KEY in env
54
+ });
55
+ ```
56
+
57
+ ### AiNamerOptions
58
+
59
+ | Option | Type | Default | Description |
60
+ |--------|------|---------|-------------|
61
+ | `timeout` | `number` | `10000` | Timeout in milliseconds per AI batch |
62
+ | `provider` | `'openai' \| 'anthropic' \| 'google'` | auto-detected | AI provider to use |
63
+ | `model` | `string` | provider-specific | Model override |
64
+ | `apiKey` | `string` | from env | API key override |
65
+
66
+ AI naming only targets "weak" elements (no label + no handler). All other elements use deterministic naming. If the AI call fails or times out, the element falls back to its deterministic ID.
67
+
68
+ See the [CLI docs](../../docs/CLI.md#ai-assisted-naming) for full setup and troubleshooting.
69
+
70
+ ## Part of UI Contracts
71
+
72
+ This package is part of [UI Contracts](https://github.com/sherifkozman/uicontract) - making web app UIs machine-readable.
46
73
 
47
74
  ## License
48
75
 
@@ -1,30 +1,72 @@
1
1
  /**
2
- * AI-assisted naming engine (stub).
2
+ * AI-assisted naming engine.
3
3
  *
4
- * TODO: Connect to an AI provider (e.g., OpenAI, Anthropic) to generate
5
- * context-aware, human-friendly agent IDs. The AI namer should receive
6
- * the full element context (surrounding code, component hierarchy, route
7
- * structure) and produce a naming suggestion that is validated against
8
- * the agent ID pattern before use. When the AI namer returns null or
9
- * fails, the deterministic namer is used as a fallback.
4
+ * Connects to an AI provider (OpenAI, Anthropic, or Google) to generate
5
+ * context-aware, human-friendly agent ID segments for elements where
6
+ * deterministic naming produces weak fallback IDs (no label and no handler).
7
+ *
8
+ * When the AI namer fails or times out, the deterministic namer is used
9
+ * as a fallback.
10
+ *
11
+ * TODO: Investigate how AI agent skills can detect and reuse the AI
12
+ * environment they run within (e.g., Claude Code's API context, Gemini CLI's
13
+ * credentials) instead of requiring separate API keys. This would enable
14
+ * zero-config AI naming when invoked as a skill from an AI agent.
10
15
  */
11
16
  import type { RawElement } from '@uicontract/core';
17
+ /** Supported AI providers. */
18
+ export type AiProvider = 'openai' | 'anthropic' | 'google';
12
19
  /** Options for the AI naming provider. */
13
20
  export interface AiNamerOptions {
14
21
  /** Timeout in milliseconds for the AI provider call. */
15
22
  timeout?: number;
16
- /** AI provider identifier (e.g., "openai", "anthropic"). */
17
- provider?: string;
23
+ /** AI provider identifier. */
24
+ provider?: AiProvider;
25
+ /** Model override (provider-specific). */
26
+ model?: string;
27
+ /** API key override (defaults to env variable). */
28
+ apiKey?: string;
18
29
  }
19
30
  /**
20
- * Attempt to assign an AI-generated agent ID to a RawElement.
31
+ * Detect AI provider from environment variables.
32
+ * Returns null if no API key is found.
33
+ */
34
+ export declare function detectProvider(): {
35
+ provider: AiProvider;
36
+ apiKey: string;
37
+ } | null;
38
+ /**
39
+ * Check whether an element would receive a weak deterministic name.
40
+ *
41
+ * A "weak" name falls to the deterministic namer's priority 5 fallback
42
+ * (component.type.line or unknown.type.line), which happens when the
43
+ * element has neither a label nor a handler.
44
+ */
45
+ export declare function isWeakName(element: RawElement): boolean;
46
+ /**
47
+ * Validate and sanitize an AI-suggested name.
48
+ * Returns null if the name is invalid or empty after sanitization.
49
+ */
50
+ export declare function validateAiName(suggestion: string): string | null;
51
+ /**
52
+ * Assign AI-generated agent ID segments to weak elements in a batch.
21
53
  *
22
- * Currently a stub that always returns null, causing the caller
23
- * to fall back to deterministic naming.
54
+ * Identifies elements that would receive weak deterministic names (no label,
55
+ * no handler), batches them, sends context to the configured AI provider,
56
+ * and returns validated name suggestions.
57
+ *
58
+ * @param elements - All raw elements to consider for AI naming.
59
+ * @param options - AI provider options.
60
+ * @returns Map from element index to AI-suggested name segment.
61
+ */
62
+ export declare function assignAiNames(elements: ReadonlyArray<RawElement>, options?: AiNamerOptions): Promise<Map<number, string>>;
63
+ /**
64
+ * Attempt to assign an AI-generated agent ID to a single RawElement.
24
65
  *
25
- * @param _element - The raw element to name.
26
- * @param _options - AI provider options.
27
- * @returns null (stub AI naming not yet implemented).
66
+ * @deprecated Use assignAiNames for batch processing.
67
+ * @param element - The raw element to name.
68
+ * @param options - AI provider options.
69
+ * @returns AI-suggested name segment, or null on failure.
28
70
  */
29
- export declare function assignAiName(_element: RawElement, _options?: AiNamerOptions): Promise<string | null>;
71
+ export declare function assignAiName(element: RawElement, options?: AiNamerOptions): Promise<string | null>;
30
72
  //# sourceMappingURL=ai-namer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-namer.d.ts","sourceRoot":"","sources":["../src/ai-namer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,0CAA0C;AAC1C,MAAM,WAAW,cAAc;IAC7B,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,UAAU,EACpB,QAAQ,CAAC,EAAE,cAAc,GACxB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOxB"}
1
+ {"version":3,"file":"ai-namer.d.ts","sourceRoot":"","sources":["../src/ai-namer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGnD,8BAA8B;AAC9B,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,CAAC;AAE3D,0CAA0C;AAC1C,MAAM,WAAW,cAAc;IAC7B,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8BAA8B;IAC9B,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAkBD;;;GAGG;AACH,wBAAgB,cAAc,IAAI;IAAE,QAAQ,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAWhF;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAEvD;AA6DD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMhE;AA6HD;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,aAAa,CAAC,UAAU,CAAC,EACnC,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAkE9B;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,UAAU,EACnB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGxB"}
package/dist/ai-namer.js CHANGED
@@ -1,29 +1,282 @@
1
1
  /**
2
- * AI-assisted naming engine (stub).
2
+ * AI-assisted naming engine.
3
3
  *
4
- * TODO: Connect to an AI provider (e.g., OpenAI, Anthropic) to generate
5
- * context-aware, human-friendly agent IDs. The AI namer should receive
6
- * the full element context (surrounding code, component hierarchy, route
7
- * structure) and produce a naming suggestion that is validated against
8
- * the agent ID pattern before use. When the AI namer returns null or
9
- * fails, the deterministic namer is used as a fallback.
4
+ * Connects to an AI provider (OpenAI, Anthropic, or Google) to generate
5
+ * context-aware, human-friendly agent ID segments for elements where
6
+ * deterministic naming produces weak fallback IDs (no label and no handler).
7
+ *
8
+ * When the AI namer fails or times out, the deterministic namer is used
9
+ * as a fallback.
10
+ *
11
+ * TODO: Investigate how AI agent skills can detect and reuse the AI
12
+ * environment they run within (e.g., Claude Code's API context, Gemini CLI's
13
+ * credentials) instead of requiring separate API keys. This would enable
14
+ * zero-config AI naming when invoked as a skill from an AI agent.
15
+ */
16
+ import { sanitizeSegment } from './naming-rules.js';
17
+ /** Agent ID validation pattern. */
18
+ const AGENT_ID_PATTERN = /^[a-z][a-z0-9.-]*$/;
19
+ /** Default models per provider. */
20
+ const DEFAULT_MODELS = {
21
+ openai: 'gpt-4o-mini',
22
+ anthropic: 'claude-haiku-4-5-20251001',
23
+ google: 'gemini-2.0-flash',
24
+ };
25
+ /** Default timeout in milliseconds. */
26
+ const DEFAULT_TIMEOUT = 10_000;
27
+ /** Maximum elements per API batch. */
28
+ const BATCH_SIZE = 20;
29
+ /**
30
+ * Detect AI provider from environment variables.
31
+ * Returns null if no API key is found.
10
32
  */
33
+ export function detectProvider() {
34
+ const openaiKey = process.env['OPENAI_API_KEY'];
35
+ if (openaiKey)
36
+ return { provider: 'openai', apiKey: openaiKey };
37
+ const anthropicKey = process.env['ANTHROPIC_API_KEY'];
38
+ if (anthropicKey)
39
+ return { provider: 'anthropic', apiKey: anthropicKey };
40
+ const googleKey = process.env['GOOGLE_API_KEY'] ?? process.env['GEMINI_API_KEY'];
41
+ if (googleKey)
42
+ return { provider: 'google', apiKey: googleKey };
43
+ return null;
44
+ }
11
45
  /**
12
- * Attempt to assign an AI-generated agent ID to a RawElement.
46
+ * Check whether an element would receive a weak deterministic name.
13
47
  *
14
- * Currently a stub that always returns null, causing the caller
15
- * to fall back to deterministic naming.
48
+ * A "weak" name falls to the deterministic namer's priority 5 fallback
49
+ * (component.type.line or unknown.type.line), which happens when the
50
+ * element has neither a label nor a handler.
51
+ */
52
+ export function isWeakName(element) {
53
+ return element.label === null && element.handler === null;
54
+ }
55
+ /**
56
+ * Build a naming prompt for a batch of weak elements.
57
+ */
58
+ function buildNamingPrompt(elements) {
59
+ const descriptions = elements
60
+ .map(({ element }, i) => {
61
+ const parts = [`${String(i + 1)}. Type: ${element.type}`];
62
+ if (element.componentName)
63
+ parts.push(`Component: ${element.componentName}`);
64
+ if (element.route)
65
+ parts.push(`Route: ${element.route}`);
66
+ if (element.filePath)
67
+ parts.push(`File: ${element.filePath}`);
68
+ parts.push(`Line: ${String(element.line)}`);
69
+ if (element.conditional)
70
+ parts.push('(conditionally rendered)');
71
+ if (element.dynamic)
72
+ parts.push('(dynamically rendered)');
73
+ const attrKeys = Object.keys(element.attributes);
74
+ if (attrKeys.length > 0)
75
+ parts.push(`Attributes: ${attrKeys.join(', ')}`);
76
+ return parts.join(', ');
77
+ })
78
+ .join('\n');
79
+ return [
80
+ 'You are a naming assistant for UI elements in a web application.',
81
+ 'Each element needs a short, descriptive kebab-case name (1-3 words) that describes its likely purpose.',
82
+ '',
83
+ 'Rules:',
84
+ '- Use lowercase letters, digits, and hyphens only',
85
+ '- Must start with a letter',
86
+ '- Be concise: 1-3 words separated by hyphens',
87
+ '- Describe the element\'s purpose, not its type',
88
+ '',
89
+ 'Elements:',
90
+ descriptions,
91
+ '',
92
+ 'Respond with ONLY a JSON array of strings, one name per element, in the same order.',
93
+ 'Example: ["search-query", "submit-form"]',
94
+ ].join('\n');
95
+ }
96
+ /**
97
+ * Parse the AI response to extract name suggestions.
98
+ */
99
+ function parseAiResponse(text, expectedCount) {
100
+ const match = text.match(/\[[\s\S]*?\]/);
101
+ if (!match)
102
+ return new Array(expectedCount).fill(null);
103
+ try {
104
+ const parsed = JSON.parse(match[0]);
105
+ if (!Array.isArray(parsed))
106
+ return new Array(expectedCount).fill(null);
107
+ return parsed.map((item) => {
108
+ if (typeof item !== 'string')
109
+ return null;
110
+ return item;
111
+ });
112
+ }
113
+ catch {
114
+ return new Array(expectedCount).fill(null);
115
+ }
116
+ }
117
+ /**
118
+ * Validate and sanitize an AI-suggested name.
119
+ * Returns null if the name is invalid or empty after sanitization.
120
+ */
121
+ export function validateAiName(suggestion) {
122
+ const sanitized = sanitizeSegment(suggestion);
123
+ if (sanitized.length === 0)
124
+ return null;
125
+ if (sanitized.length > 40)
126
+ return null;
127
+ if (!AGENT_ID_PATTERN.test(sanitized))
128
+ return null;
129
+ return sanitized;
130
+ }
131
+ async function callOpenAI(prompt, apiKey, model, signal) {
132
+ const response = await fetch('https://api.openai.com/v1/chat/completions', {
133
+ method: 'POST',
134
+ headers: {
135
+ 'Content-Type': 'application/json',
136
+ Authorization: `Bearer ${apiKey}`,
137
+ },
138
+ body: JSON.stringify({
139
+ model,
140
+ messages: [{ role: 'user', content: prompt }],
141
+ temperature: 0.3,
142
+ max_tokens: 512,
143
+ }),
144
+ signal,
145
+ });
146
+ if (!response.ok) {
147
+ throw new Error(`OpenAI API error: ${String(response.status)} ${response.statusText}`);
148
+ }
149
+ const data = (await response.json());
150
+ return data.choices?.[0]?.message?.content ?? '';
151
+ }
152
+ async function callAnthropic(prompt, apiKey, model, signal) {
153
+ const response = await fetch('https://api.anthropic.com/v1/messages', {
154
+ method: 'POST',
155
+ headers: {
156
+ 'Content-Type': 'application/json',
157
+ 'x-api-key': apiKey,
158
+ 'anthropic-version': '2023-06-01',
159
+ },
160
+ body: JSON.stringify({
161
+ model,
162
+ max_tokens: 512,
163
+ messages: [{ role: 'user', content: prompt }],
164
+ }),
165
+ signal,
166
+ });
167
+ if (!response.ok) {
168
+ throw new Error(`Anthropic API error: ${String(response.status)} ${response.statusText}`);
169
+ }
170
+ const data = (await response.json());
171
+ const textBlock = data.content?.find((b) => b.type === 'text');
172
+ return textBlock?.text ?? '';
173
+ }
174
+ async function callGoogle(prompt, apiKey, model, signal) {
175
+ const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`;
176
+ const response = await fetch(url, {
177
+ method: 'POST',
178
+ headers: { 'Content-Type': 'application/json' },
179
+ body: JSON.stringify({
180
+ contents: [{ parts: [{ text: prompt }] }],
181
+ }),
182
+ signal,
183
+ });
184
+ if (!response.ok) {
185
+ throw new Error(`Google API error: ${String(response.status)} ${response.statusText}`);
186
+ }
187
+ const data = (await response.json());
188
+ return data.candidates?.[0]?.content?.parts?.[0]?.text ?? '';
189
+ }
190
+ /**
191
+ * Call the configured AI provider with the given prompt.
192
+ */
193
+ async function callProvider(prompt, provider, apiKey, model, signal) {
194
+ switch (provider) {
195
+ case 'openai':
196
+ return callOpenAI(prompt, apiKey, model, signal);
197
+ case 'anthropic':
198
+ return callAnthropic(prompt, apiKey, model, signal);
199
+ case 'google':
200
+ return callGoogle(prompt, apiKey, model, signal);
201
+ }
202
+ }
203
+ // ---------------------------------------------------------------------------
204
+ // Public API
205
+ // ---------------------------------------------------------------------------
206
+ /**
207
+ * Assign AI-generated agent ID segments to weak elements in a batch.
16
208
  *
17
- * @param _element - The raw element to name.
18
- * @param _options - AI provider options.
19
- * @returns null (stub — AI naming not yet implemented).
20
- */
21
- export async function assignAiName(_element, _options) {
22
- // TODO: Implement AI naming. This function should:
23
- // 1. Serialize the element context into a prompt
24
- // 2. Call the configured AI provider
25
- // 3. Validate the response matches ^[a-z][a-z0-9.-]*$
26
- // 4. Return the validated name, or null on failure/timeout
27
- return null;
209
+ * Identifies elements that would receive weak deterministic names (no label,
210
+ * no handler), batches them, sends context to the configured AI provider,
211
+ * and returns validated name suggestions.
212
+ *
213
+ * @param elements - All raw elements to consider for AI naming.
214
+ * @param options - AI provider options.
215
+ * @returns Map from element index to AI-suggested name segment.
216
+ */
217
+ export async function assignAiNames(elements, options) {
218
+ const result = new Map();
219
+ // Find weak elements that would benefit from AI naming
220
+ const weakElements = [];
221
+ for (let i = 0; i < elements.length; i++) {
222
+ const element = elements[i];
223
+ if (element && isWeakName(element)) {
224
+ weakElements.push({ index: i, element });
225
+ }
226
+ }
227
+ if (weakElements.length === 0)
228
+ return result;
229
+ // Resolve provider config
230
+ const detected = detectProvider();
231
+ const provider = options?.provider ?? detected?.provider;
232
+ const apiKey = options?.apiKey ?? detected?.apiKey;
233
+ if (!provider || !apiKey) {
234
+ process.stderr.write('Warning: AI naming enabled but no API key found. ' +
235
+ 'Set OPENAI_API_KEY, ANTHROPIC_API_KEY, or GOOGLE_API_KEY. ' +
236
+ 'Falling back to deterministic naming.\n');
237
+ return result;
238
+ }
239
+ const model = options?.model ?? DEFAULT_MODELS[provider];
240
+ const timeout = options?.timeout ?? DEFAULT_TIMEOUT;
241
+ // Process in batches to avoid overlong prompts
242
+ for (let start = 0; start < weakElements.length; start += BATCH_SIZE) {
243
+ const batch = weakElements.slice(start, start + BATCH_SIZE);
244
+ const prompt = buildNamingPrompt(batch);
245
+ const controller = new AbortController();
246
+ const timer = setTimeout(() => controller.abort(), timeout);
247
+ try {
248
+ const responseText = await callProvider(prompt, provider, apiKey, model, controller.signal);
249
+ const suggestions = parseAiResponse(responseText, batch.length);
250
+ for (let j = 0; j < batch.length; j++) {
251
+ const suggestion = suggestions[j];
252
+ if (suggestion) {
253
+ const validated = validateAiName(suggestion);
254
+ if (validated) {
255
+ result.set(batch[j].index, validated);
256
+ }
257
+ }
258
+ }
259
+ }
260
+ catch {
261
+ // On error (timeout, network, parse failure), skip this batch.
262
+ // Deterministic fallback will be used for these elements.
263
+ }
264
+ finally {
265
+ clearTimeout(timer);
266
+ }
267
+ }
268
+ return result;
269
+ }
270
+ /**
271
+ * Attempt to assign an AI-generated agent ID to a single RawElement.
272
+ *
273
+ * @deprecated Use assignAiNames for batch processing.
274
+ * @param element - The raw element to name.
275
+ * @param options - AI provider options.
276
+ * @returns AI-suggested name segment, or null on failure.
277
+ */
278
+ export async function assignAiName(element, options) {
279
+ const names = await assignAiNames([element], options);
280
+ return names.get(0) ?? null;
28
281
  }
29
282
  //# sourceMappingURL=ai-namer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-namer.js","sourceRoot":"","sources":["../src/ai-namer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAYH;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,QAAoB,EACpB,QAAyB;IAEzB,mDAAmD;IACnD,iDAAiD;IACjD,qCAAqC;IACrC,sDAAsD;IACtD,2DAA2D;IAC3D,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"ai-namer.js","sourceRoot":"","sources":["../src/ai-namer.ts"],"names":[],"mappings":"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"}
@@ -70,7 +70,7 @@ export function assignDeterministicName(element) {
70
70
  if (hasComponent && hasHandler) {
71
71
  return ensureValid(buildId([componentSeg, handlerSeg], element.type));
72
72
  }
73
- // Priority 5: fallback component (or "unknown") + type + line
73
+ // Priority 5: fallback - component (or "unknown") + type + line
74
74
  const base = hasComponent ? componentSeg : 'unknown';
75
75
  return ensureValid(`${base}.${element.type}.${element.line}`);
76
76
  }
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  /**
2
- * @uicontract/namer naming engine for UIC elements.
2
+ * @uicontract/namer - naming engine for UIC elements.
3
3
  *
4
4
  * Takes RawElements discovered by a parser and assigns stable,
5
- * hierarchical agentIds using deterministic rules (with an optional
6
- * AI-assisted mode for future use).
5
+ * hierarchical agentIds using deterministic rules with an optional
6
+ * AI-assisted mode for elements that lack strong context.
7
7
  */
8
8
  import type { RawElement, NamedElement } from '@uicontract/core';
9
9
  /** Options for the naming engine. */
@@ -12,21 +12,26 @@ export interface NamerOptions {
12
12
  ai?: boolean;
13
13
  /** Timeout in milliseconds for AI naming calls. */
14
14
  aiTimeout?: number;
15
+ /** AI provider identifier (e.g., "openai", "anthropic", "google"). */
16
+ aiProvider?: string;
17
+ /** Model override for the AI provider. */
18
+ aiModel?: string;
15
19
  }
16
20
  /**
17
21
  * Name a list of raw elements, producing NamedElements with unique agentIds.
18
22
  *
19
- * 1. Each element is named via the deterministic namer (AI stub returns null).
20
- * 2. Duplicate IDs are resolved with numeric suffixes.
23
+ * 1. Optionally batch-processes weak elements via AI for better name segments.
24
+ * 2. Each element is named via the deterministic namer (with AI overrides).
25
+ * 3. Duplicate IDs are resolved with numeric suffixes.
21
26
  *
22
27
  * @param elements - Raw elements from a parser's discovery phase.
23
- * @param options - Naming options (AI mode is stubbed for now).
28
+ * @param options - Naming options.
24
29
  * @returns Array of NamedElements with unique agentIds.
25
30
  */
26
- export declare function nameElements(elements: ReadonlyArray<RawElement>, options?: NamerOptions): NamedElement[];
31
+ export declare function nameElements(elements: ReadonlyArray<RawElement>, options?: NamerOptions): Promise<NamedElement[]>;
27
32
  export { sanitizeSegment, camelToKebab, routeToSegments, labelToSegment, handlerToSegment, componentToSegment, } from './naming-rules.js';
28
33
  export { assignDeterministicName } from './deterministic-namer.js';
29
34
  export { deduplicateNames } from './deduplicator.js';
30
- export { assignAiName } from './ai-namer.js';
31
- export type { AiNamerOptions } from './ai-namer.js';
35
+ export { assignAiName, assignAiNames } from './ai-namer.js';
36
+ export type { AiNamerOptions, AiProvider } from './ai-namer.js';
32
37
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAKjE,qCAAqC;AACrC,MAAM,WAAW,YAAY;IAC3B,kFAAkF;IAClF,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,mDAAmD;IACnD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,aAAa,CAAC,UAAU,CAAC,EACnC,OAAO,CAAC,EAAE,YAAY,GACrB,YAAY,EAAE,CA0BhB;AAGD,OAAO,EACL,eAAe,EACf,YAAY,EACZ,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAUjE,qCAAqC;AACrC,MAAM,WAAW,YAAY;IAC3B,kFAAkF;IAClF,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,mDAAmD;IACnD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,aAAa,CAAC,UAAU,CAAC,EACnC,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,YAAY,EAAE,CAAC,CAiDzB;AAGD,OAAO,EACL,eAAe,EACf,YAAY,EACZ,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC5D,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -1,48 +1,73 @@
1
1
  /**
2
- * @uicontract/namer naming engine for UIC elements.
2
+ * @uicontract/namer - naming engine for UIC elements.
3
3
  *
4
4
  * Takes RawElements discovered by a parser and assigns stable,
5
- * hierarchical agentIds using deterministic rules (with an optional
6
- * AI-assisted mode for future use).
5
+ * hierarchical agentIds using deterministic rules with an optional
6
+ * AI-assisted mode for elements that lack strong context.
7
7
  */
8
8
  import { assignDeterministicName } from './deterministic-namer.js';
9
9
  import { deduplicateNames } from './deduplicator.js';
10
- import { assignAiName } from './ai-namer.js';
10
+ import { assignAiNames } from './ai-namer.js';
11
+ import { routeToSegments, componentToSegment, } from './naming-rules.js';
11
12
  /**
12
13
  * Name a list of raw elements, producing NamedElements with unique agentIds.
13
14
  *
14
- * 1. Each element is named via the deterministic namer (AI stub returns null).
15
- * 2. Duplicate IDs are resolved with numeric suffixes.
15
+ * 1. Optionally batch-processes weak elements via AI for better name segments.
16
+ * 2. Each element is named via the deterministic namer (with AI overrides).
17
+ * 3. Duplicate IDs are resolved with numeric suffixes.
16
18
  *
17
19
  * @param elements - Raw elements from a parser's discovery phase.
18
- * @param options - Naming options (AI mode is stubbed for now).
20
+ * @param options - Naming options.
19
21
  * @returns Array of NamedElements with unique agentIds.
20
22
  */
21
- export function nameElements(elements, options) {
23
+ export async function nameElements(elements, options) {
22
24
  const useAi = options?.ai ?? false;
23
- const aiTimeout = options?.aiTimeout;
24
- // Phase 1: assign names
25
- const named = elements.map((element) => {
26
- let agentId = null;
27
- // Attempt AI naming if enabled (currently a stub that returns null)
28
- if (useAi) {
29
- // AI naming is async but we handle it synchronously here since
30
- // the stub always returns null. When real AI is connected, this
31
- // function signature should become async.
32
- void assignAiName(element, { timeout: aiTimeout });
25
+ // Phase 1: try AI naming for weak elements
26
+ let aiNames = new Map();
27
+ if (useAi) {
28
+ aiNames = await assignAiNames(elements, {
29
+ timeout: options?.aiTimeout,
30
+ provider: options?.aiProvider,
31
+ model: options?.aiModel,
32
+ });
33
+ }
34
+ // Phase 2: assign names (deterministic, with AI overrides for weak elements)
35
+ const named = elements.map((element, index) => {
36
+ const aiSuggestion = aiNames.get(index);
37
+ let agentId;
38
+ if (aiSuggestion) {
39
+ // AI provided a name segment — build the full ID using the same
40
+ // structure as the deterministic namer: prefix.suggestion.type
41
+ const routeSegs = element.route !== null ? routeToSegments(element.route) : [];
42
+ const componentSeg = element.componentName !== null ? componentToSegment(element.componentName) : '';
43
+ let prefix;
44
+ if (routeSegs.length > 0) {
45
+ prefix = routeSegs.join('.');
46
+ }
47
+ else if (componentSeg.length > 0) {
48
+ prefix = componentSeg;
49
+ }
50
+ else {
51
+ prefix = aiSuggestion;
52
+ }
53
+ // If prefix is the AI suggestion itself (no route/component), use suggestion.type
54
+ // Otherwise use prefix.suggestion.type
55
+ agentId =
56
+ prefix === aiSuggestion
57
+ ? `${aiSuggestion}.${element.type}`
58
+ : `${prefix}.${aiSuggestion}.${element.type}`;
33
59
  }
34
- // Deterministic fallback (always used until AI is implemented)
35
- if (agentId === null) {
60
+ else {
36
61
  agentId = assignDeterministicName(element);
37
62
  }
38
63
  return { ...element, agentId };
39
64
  });
40
- // Phase 2: deduplicate
65
+ // Phase 3: deduplicate
41
66
  return deduplicateNames(named);
42
67
  }
43
68
  // Re-export individual modules for direct access and testing
44
69
  export { sanitizeSegment, camelToKebab, routeToSegments, labelToSegment, handlerToSegment, componentToSegment, } from './naming-rules.js';
45
70
  export { assignDeterministicName } from './deterministic-namer.js';
46
71
  export { deduplicateNames } from './deduplicator.js';
47
- export { assignAiName } from './ai-namer.js';
72
+ export { assignAiName, assignAiNames } from './ai-namer.js';
48
73
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAU7C;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAC1B,QAAmC,EACnC,OAAsB;IAEtB,MAAM,KAAK,GAAG,OAAO,EAAE,EAAE,IAAI,KAAK,CAAC;IACnC,MAAM,SAAS,GAAG,OAAO,EAAE,SAAS,CAAC;IAErC,wBAAwB;IACxB,MAAM,KAAK,GAAmB,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QACrD,IAAI,OAAO,GAAkB,IAAI,CAAC;QAElC,oEAAoE;QACpE,IAAI,KAAK,EAAE,CAAC;YACV,+DAA+D;YAC/D,gEAAgE;YAChE,0CAA0C;YAC1C,KAAK,YAAY,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;QACrD,CAAC;QAED,+DAA+D;QAC/D,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,OAAO,GAAG,uBAAuB,CAAC,OAAO,CAAC,CAAC;QAC7C,CAAC;QAED,OAAO,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,uBAAuB;IACvB,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC;AACjC,CAAC;AAED,6DAA6D;AAC7D,OAAO,EACL,eAAe,EACf,YAAY,EACZ,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,OAAO,EACL,eAAe,EACf,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAc3B;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,QAAmC,EACnC,OAAsB;IAEtB,MAAM,KAAK,GAAG,OAAO,EAAE,EAAE,IAAI,KAAK,CAAC;IAEnC,2CAA2C;IAC3C,IAAI,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,GAAG,MAAM,aAAa,CAAC,QAAQ,EAAE;YACtC,OAAO,EAAE,OAAO,EAAE,SAAS;YAC3B,QAAQ,EAAE,OAAO,EAAE,UAAoC;YACvD,KAAK,EAAE,OAAO,EAAE,OAAO;SACxB,CAAC,CAAC;IACL,CAAC;IAED,6EAA6E;IAC7E,MAAM,KAAK,GAAmB,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;QAC5D,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACxC,IAAI,OAAe,CAAC;QAEpB,IAAI,YAAY,EAAE,CAAC;YACjB,gEAAgE;YAChE,+DAA+D;YAC/D,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/E,MAAM,YAAY,GAChB,OAAO,CAAC,aAAa,KAAK,IAAI,CAAC,CAAC,CAAC,kBAAkB,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAElF,IAAI,MAAc,CAAC;YACnB,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACzB,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC/B,CAAC;iBAAM,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACnC,MAAM,GAAG,YAAY,CAAC;YACxB,CAAC;iBAAM,CAAC;gBACN,MAAM,GAAG,YAAY,CAAC;YACxB,CAAC;YAED,kFAAkF;YAClF,uCAAuC;YACvC,OAAO;gBACL,MAAM,KAAK,YAAY;oBACrB,CAAC,CAAC,GAAG,YAAY,IAAI,OAAO,CAAC,IAAI,EAAE;oBACnC,CAAC,CAAC,GAAG,MAAM,IAAI,YAAY,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACpD,CAAC;aAAM,CAAC;YACN,OAAO,GAAG,uBAAuB,CAAC,OAAO,CAAC,CAAC;QAC7C,CAAC;QAED,OAAO,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,uBAAuB;IACvB,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC;AACjC,CAAC;AAED,6DAA6D;AAC7D,OAAO,EACL,eAAe,EACf,YAAY,EACZ,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uicontract/namer",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Naming engine that assigns hierarchical agent IDs to UI elements",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -41,7 +41,7 @@
41
41
  "README.md"
42
42
  ],
43
43
  "dependencies": {
44
- "@uicontract/core": "0.1.0"
44
+ "@uicontract/core": "0.2.1"
45
45
  },
46
46
  "scripts": {
47
47
  "build": "tsc -b",