@contentful/experience-design-system-cli 2.6.2-dev-build-3f849e3.0 → 2.6.2-dev-build-ea30bad.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/package.json +1 -1
- package/dist/src/analyze/command.js +1 -10
- package/dist/src/analyze/select/tui/App.js +2 -13
- package/dist/src/analyze/select/tui/components/Sidebar.js +2 -25
- package/dist/src/analyze/select/types.d.ts +0 -2
- package/dist/src/analyze/select/types.js +0 -2
- package/dist/src/analyze/tui/AnalyzeView.d.ts +0 -2
- package/dist/src/analyze/tui/AnalyzeView.js +1 -6
- package/dist/src/generate/agent-runner.d.ts +0 -2
- package/dist/src/generate/agent-runner.js +0 -3
- package/dist/src/import/tui/steps/GenerateReviewStep.js +0 -2
- package/dist/src/session/db.js +13 -38
- package/dist/src/types.d.ts +0 -3
- package/package.json +2 -2
- package/skills/select-components.md +2 -3
- package/dist/src/analyze/extract/scoring.d.ts +0 -7
- package/dist/src/analyze/extract/scoring.js +0 -83
package/dist/package.json
CHANGED
|
@@ -9,7 +9,6 @@ import { registerAnalyzeSelectAgentCommand } from './select-agent/command.js';
|
|
|
9
9
|
import { openPipelineDb, getOrCreateSession, createStep, updateStep, storeRawComponents } from '../session/db.js';
|
|
10
10
|
import { preClassifyComponent } from './pre-classify.js';
|
|
11
11
|
import { isNonAuthorableComponent } from './extract/non-authorable-filter.js';
|
|
12
|
-
import { computeExtractionScore, deriveNeedsReview } from './extract/scoring.js';
|
|
13
12
|
const SCANNED_FILE_EXTENSIONS = new Set(['.astro', '.js', '.jsx', '.ts', '.tsx', '.vue']);
|
|
14
13
|
const IGNORED_DIRECTORY_NAMES = new Set([
|
|
15
14
|
'.git',
|
|
@@ -131,13 +130,7 @@ export function registerAnalyzeCommand(program) {
|
|
|
131
130
|
filterWarnings.push(`Skipped non-authorable component: ${component.name} (${verdict.reason})`);
|
|
132
131
|
continue;
|
|
133
132
|
}
|
|
134
|
-
|
|
135
|
-
filteredComponents.push({
|
|
136
|
-
...component,
|
|
137
|
-
extractionConfidence: confidence,
|
|
138
|
-
reviewReasons: reasons,
|
|
139
|
-
needsReview: deriveNeedsReview(confidence),
|
|
140
|
-
});
|
|
133
|
+
filteredComponents.push(component);
|
|
141
134
|
}
|
|
142
135
|
storeRawComponents(db, sessionId, filteredComponents);
|
|
143
136
|
updateStep(db, stepId, 'complete', { sessionId });
|
|
@@ -153,8 +146,6 @@ export function registerAnalyzeCommand(program) {
|
|
|
153
146
|
propCount: c.props.length,
|
|
154
147
|
slotCount: c.slots.length,
|
|
155
148
|
warnings: allWarnings.filter((w) => w.startsWith(c.name + ':')),
|
|
156
|
-
extractionConfidence: c.extractionConfidence ?? 100,
|
|
157
|
-
needsReview: c.needsReview ?? false,
|
|
158
149
|
})),
|
|
159
150
|
totalWarnings: allWarnings.length,
|
|
160
151
|
};
|
|
@@ -327,23 +327,12 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
|
|
|
327
327
|
if (finalizedResult) {
|
|
328
328
|
return _jsx(FinalizedScreen, { result: finalizedResult });
|
|
329
329
|
}
|
|
330
|
-
const sessionSummary = session.components
|
|
331
|
-
.map((c) => ({
|
|
330
|
+
const sessionSummary = session.components.map((c) => ({
|
|
332
331
|
id: c.id,
|
|
333
332
|
name: c.name,
|
|
334
333
|
status: c.status,
|
|
335
334
|
previewAnnotation: previewAnnotations[c.name],
|
|
336
|
-
|
|
337
|
-
needsReview: c.originalProposal.needsReview ?? false,
|
|
338
|
-
}))
|
|
339
|
-
.sort((a, b) => {
|
|
340
|
-
// Unresolved flagged items first, then by ascending confidence
|
|
341
|
-
const aFlagged = a.needsReview && a.status === 'needs-review' ? 0 : 1;
|
|
342
|
-
const bFlagged = b.needsReview && b.status === 'needs-review' ? 0 : 1;
|
|
343
|
-
if (aFlagged !== bFlagged)
|
|
344
|
-
return aFlagged - bFlagged;
|
|
345
|
-
return a.extractionConfidence - b.extractionConfidence;
|
|
346
|
-
});
|
|
335
|
+
}));
|
|
347
336
|
const selectedRecord = session.components.find((c) => c.id === selectedId) ?? null;
|
|
348
337
|
const sessionDetail = selectedRecord ? createReviewSessionDetail({ ...session, components: [selectedRecord] }) : null;
|
|
349
338
|
const selectedDetail = sessionDetail?.components[0] ?? null;
|
|
@@ -29,24 +29,6 @@ function truncateName(name, maxLen) {
|
|
|
29
29
|
return name;
|
|
30
30
|
return name.slice(0, maxLen) + '…';
|
|
31
31
|
}
|
|
32
|
-
function confidenceColor(confidence, needsReview) {
|
|
33
|
-
if (needsReview)
|
|
34
|
-
return 'red';
|
|
35
|
-
if (confidence >= 80)
|
|
36
|
-
return 'green';
|
|
37
|
-
if (confidence >= 50)
|
|
38
|
-
return 'yellow';
|
|
39
|
-
return 'red';
|
|
40
|
-
}
|
|
41
|
-
function confidenceDot(confidence, needsReview) {
|
|
42
|
-
if (needsReview)
|
|
43
|
-
return '⚑';
|
|
44
|
-
if (confidence >= 80)
|
|
45
|
-
return '●';
|
|
46
|
-
if (confidence >= 50)
|
|
47
|
-
return '◐';
|
|
48
|
-
return '○';
|
|
49
|
-
}
|
|
50
32
|
export function Sidebar({ components, selectedId, focused, scrollOffset, visibleCount, collapsed = false, width: widthProp, }) {
|
|
51
33
|
const visible = components.slice(scrollOffset, scrollOffset + visibleCount);
|
|
52
34
|
const showScrollUp = scrollOffset > 0;
|
|
@@ -56,16 +38,11 @@ export function Sidebar({ components, selectedId, focused, scrollOffset, visible
|
|
|
56
38
|
const isSelected = component.id === selectedId;
|
|
57
39
|
const icon = statusIcon(component.status);
|
|
58
40
|
const color = statusColor(component.status);
|
|
59
|
-
const
|
|
60
|
-
const nr = component.needsReview ?? false;
|
|
61
|
-
const dot = confidenceDot(conf, nr);
|
|
62
|
-
const dotColor = confidenceColor(conf, nr);
|
|
63
|
-
// reserve 2 chars for dot + space on top of existing icon + space
|
|
64
|
-
const maxNameLen = Math.max(1, width - 7);
|
|
41
|
+
const maxNameLen = Math.max(1, width - 5);
|
|
65
42
|
const name = truncateName(component.name, maxNameLen);
|
|
66
43
|
if (collapsed) {
|
|
67
44
|
return (_jsx(Box, { children: _jsx(Text, { color: color, inverse: isSelected && focused, underline: isSelected && !focused, children: icon }) }, component.id));
|
|
68
45
|
}
|
|
69
|
-
return (
|
|
46
|
+
return (_jsx(Box, { children: _jsx(Text, { color: color, inverse: isSelected && focused, underline: isSelected && !focused, wrap: "truncate", children: icon + ' ' + name }) }, component.id));
|
|
70
47
|
}), showScrollDown && !collapsed && _jsx(Text, { dimColor: true, children: "\u25BC" })] }));
|
|
71
48
|
}
|
|
@@ -22,8 +22,6 @@ export type ReviewComponentSummary = {
|
|
|
22
22
|
name: string;
|
|
23
23
|
status: ReviewComponentStatus;
|
|
24
24
|
previewAnnotation?: PreviewAnnotation;
|
|
25
|
-
extractionConfidence: number;
|
|
26
|
-
needsReview: boolean;
|
|
27
25
|
};
|
|
28
26
|
export type ReviewSessionSnapshot = {
|
|
29
27
|
components: ReviewComponentRecord[];
|
|
@@ -4,8 +4,6 @@ export function createReviewSessionSummary(session) {
|
|
|
4
4
|
id: component.id,
|
|
5
5
|
name: component.name,
|
|
6
6
|
status: component.status,
|
|
7
|
-
extractionConfidence: component.originalProposal.extractionConfidence ?? 100,
|
|
8
|
-
needsReview: component.originalProposal.needsReview ?? false,
|
|
9
7
|
})),
|
|
10
8
|
};
|
|
11
9
|
}
|
|
@@ -31,12 +31,7 @@ export function AnalyzeView({ result, onExit }) {
|
|
|
31
31
|
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(TopBar, { subcommand: "analyze", hints: [
|
|
32
32
|
{ key: '?', label: 'help' },
|
|
33
33
|
{ key: 'q', label: 'quit' },
|
|
34
|
-
] }), _jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsx(Text, { children: 'Scanned ' + result.fileCount + ' source files in ' + result.sourceDirectory }), _jsx(Text, { children: 'Extracted ' + result.components.length + ' components' }), _jsx(Text, { dimColor: true, children: 'Session: ' + result.sessionId }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: '─'.repeat(70) }), _jsx(Text, { bold: true, children: "Components" }), _jsx(Text, { dimColor: true, children: '─'.repeat(70) }), _jsx(Text, { children: " " }), result.components.slice(scrollOffset).map((component) => {
|
|
35
|
-
const conf = component.extractionConfidence ?? 100;
|
|
36
|
-
const confColor = component.needsReview ? 'red' : conf >= 80 ? 'green' : conf >= 50 ? 'yellow' : 'red';
|
|
37
|
-
const confDot = component.needsReview ? '⚑' : conf >= 80 ? '●' : conf >= 50 ? '◐' : '○';
|
|
38
|
-
return (_jsxs(Box, { children: [component.warnings.length > 0 && _jsx(Text, { color: "yellow", children: "\u26A0 " }), component.warnings.length === 0 && _jsx(Text, { children: " " }), _jsx(Text, { children: truncateName(component.name).padEnd(20) }), _jsx(Text, { dimColor: true, children: component.framework.padEnd(10) }), _jsx(Text, { children: (component.propCount + ' props').padEnd(10) }), _jsx(Text, { children: (component.slotCount + ' ' + (component.slotCount === 1 ? 'slot' : 'slots')).padEnd(8) }), _jsx(Text, { color: confColor, children: confDot + ' ' + String(conf).padStart(3) }), component.warnings.length > 0 && (_jsx(Text, { color: "yellow", children: ' ⚠ ' + component.warnings.length + ' warning' + (component.warnings.length === 1 ? '' : 's') }))] }, component.name));
|
|
39
|
-
}), result.totalWarnings > 0 && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: '─'.repeat(70) }), _jsx(Text, { bold: true, color: "yellow", children: 'Warnings (' + result.totalWarnings + ')' }), _jsx(Text, { dimColor: true, children: '─'.repeat(70) }), _jsx(Text, { children: " " }), result.components
|
|
34
|
+
] }), _jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsx(Text, { children: 'Scanned ' + result.fileCount + ' source files in ' + result.sourceDirectory }), _jsx(Text, { children: 'Extracted ' + result.components.length + ' components' }), _jsx(Text, { dimColor: true, children: 'Session: ' + result.sessionId }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: '─'.repeat(70) }), _jsx(Text, { bold: true, children: "Components" }), _jsx(Text, { dimColor: true, children: '─'.repeat(70) }), _jsx(Text, { children: " " }), result.components.slice(scrollOffset).map((component) => (_jsxs(Box, { children: [component.warnings.length > 0 && _jsx(Text, { color: "yellow", children: "\u26A0 " }), component.warnings.length === 0 && _jsx(Text, { children: " " }), _jsx(Text, { children: truncateName(component.name).padEnd(20) }), _jsx(Text, { dimColor: true, children: component.framework.padEnd(10) }), _jsx(Text, { children: (component.propCount + ' props').padEnd(10) }), _jsx(Text, { children: component.slotCount + ' ' + (component.slotCount === 1 ? 'slot' : 'slots') }), component.warnings.length > 0 && (_jsx(Text, { color: "yellow", children: ' ' + component.warnings.length + ' warning' + (component.warnings.length === 1 ? '' : 's') }))] }, component.name))), result.totalWarnings > 0 && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: '─'.repeat(70) }), _jsx(Text, { bold: true, color: "yellow", children: 'Warnings (' + result.totalWarnings + ')' }), _jsx(Text, { dimColor: true, children: '─'.repeat(70) }), _jsx(Text, { children: " " }), result.components
|
|
40
35
|
.filter((c) => c.warnings.length > 0)
|
|
41
36
|
.flatMap((c) => c.warnings.map((w) => ({ component: c.name, warning: w })))
|
|
42
37
|
.map((w, i) => (_jsx(Text, { color: "yellow", children: ' ⚠ ' + w.component + ': ' + w.warning }, i)))] })), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: 'Run: analyze select --session ' + result.sessionId }), _jsx(Text, { children: " " })] }), _jsx(Box, { borderStyle: "single", paddingX: 1, children: _jsx(Text, { dimColor: true, children: "Press Enter or q to exit" }) })] }));
|
|
@@ -37,13 +37,11 @@ export interface SelectComponentCall {
|
|
|
37
37
|
tool: 'select_component';
|
|
38
38
|
name: string;
|
|
39
39
|
reason?: string;
|
|
40
|
-
confidence?: number;
|
|
41
40
|
}
|
|
42
41
|
export interface RejectComponentCall {
|
|
43
42
|
tool: 'reject_component';
|
|
44
43
|
name: string;
|
|
45
44
|
reason?: string;
|
|
46
|
-
confidence?: number;
|
|
47
45
|
}
|
|
48
46
|
export type SelectToolCall = SelectComponentCall | RejectComponentCall;
|
|
49
47
|
export interface ParsedSelectToolCalls {
|
|
@@ -30,9 +30,6 @@ export function parseSelectToolCallLines(stdout) {
|
|
|
30
30
|
};
|
|
31
31
|
if (typeof rec.reason === 'string')
|
|
32
32
|
call.reason = rec.reason;
|
|
33
|
-
if (typeof rec.confidence === 'number' && rec.confidence >= 0 && rec.confidence <= 100) {
|
|
34
|
-
call.confidence = rec.confidence;
|
|
35
|
-
}
|
|
36
33
|
calls.push(call);
|
|
37
34
|
}
|
|
38
35
|
return { calls, warnings };
|
|
@@ -185,8 +185,6 @@ export function GenerateReviewStep({ extractSessionId, onFinalize, onQuit, }) {
|
|
|
185
185
|
id: c.key,
|
|
186
186
|
name: c.key,
|
|
187
187
|
status: c.status,
|
|
188
|
-
extractionConfidence: 100,
|
|
189
|
-
needsReview: false,
|
|
190
188
|
}));
|
|
191
189
|
const longestName = components.reduce((m, c) => Math.max(m, c.key.length), 0);
|
|
192
190
|
const sidebarWidth = Math.min(Math.max(longestName + 4, 14), 22);
|
package/dist/src/session/db.js
CHANGED
|
@@ -34,18 +34,15 @@ CREATE TABLE IF NOT EXISTS steps (
|
|
|
34
34
|
);
|
|
35
35
|
|
|
36
36
|
CREATE TABLE IF NOT EXISTS raw_components (
|
|
37
|
-
session_id
|
|
38
|
-
component_id
|
|
39
|
-
name
|
|
40
|
-
source
|
|
41
|
-
framework
|
|
42
|
-
extracted_at
|
|
43
|
-
status
|
|
44
|
-
cdf_schema
|
|
45
|
-
description
|
|
46
|
-
extraction_confidence INTEGER NOT NULL DEFAULT 100,
|
|
47
|
-
review_reasons TEXT NOT NULL DEFAULT '[]',
|
|
48
|
-
needs_review INTEGER NOT NULL DEFAULT 0,
|
|
37
|
+
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
38
|
+
component_id TEXT NOT NULL,
|
|
39
|
+
name TEXT NOT NULL,
|
|
40
|
+
source TEXT NOT NULL,
|
|
41
|
+
framework TEXT NOT NULL,
|
|
42
|
+
extracted_at TEXT NOT NULL,
|
|
43
|
+
status TEXT NOT NULL DEFAULT 'extracted',
|
|
44
|
+
cdf_schema TEXT,
|
|
45
|
+
description TEXT,
|
|
49
46
|
PRIMARY KEY (session_id, component_id)
|
|
50
47
|
);
|
|
51
48
|
|
|
@@ -167,18 +164,6 @@ function applyDbMigrations(db) {
|
|
|
167
164
|
if (!cols.some((c) => c.name === 'required')) {
|
|
168
165
|
db.exec('ALTER TABLE raw_slots ADD COLUMN required INTEGER NOT NULL DEFAULT 1 CHECK (required IN (0, 1))');
|
|
169
166
|
}
|
|
170
|
-
// Add extraction confidence scoring columns if they don't exist yet (added in v0.6.0).
|
|
171
|
-
const rawCompCols = db.prepare('PRAGMA table_info(raw_components)').all();
|
|
172
|
-
const rawCompColNames = new Set(rawCompCols.map((c) => c.name));
|
|
173
|
-
if (!rawCompColNames.has('extraction_confidence')) {
|
|
174
|
-
db.exec('ALTER TABLE raw_components ADD COLUMN extraction_confidence INTEGER NOT NULL DEFAULT 100');
|
|
175
|
-
}
|
|
176
|
-
if (!rawCompColNames.has('review_reasons')) {
|
|
177
|
-
db.exec("ALTER TABLE raw_components ADD COLUMN review_reasons TEXT NOT NULL DEFAULT '[]'");
|
|
178
|
-
}
|
|
179
|
-
if (!rawCompColNames.has('needs_review')) {
|
|
180
|
-
db.exec('ALTER TABLE raw_components ADD COLUMN needs_review INTEGER NOT NULL DEFAULT 0');
|
|
181
|
-
}
|
|
182
167
|
// Add generation_cache table if it doesn't exist yet.
|
|
183
168
|
const tables = db
|
|
184
169
|
.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='generation_cache'`)
|
|
@@ -367,8 +352,8 @@ export function updateStep(db, stepId, status, outputs, error) {
|
|
|
367
352
|
}
|
|
368
353
|
export function storeRawComponents(db, sessionId, components, options) {
|
|
369
354
|
const now = new Date().toISOString();
|
|
370
|
-
const insertComp = db.prepare(`INSERT INTO raw_components (session_id, component_id, name, source, framework, extracted_at
|
|
371
|
-
VALUES (?, ?, ?, ?, ?,
|
|
355
|
+
const insertComp = db.prepare(`INSERT INTO raw_components (session_id, component_id, name, source, framework, extracted_at)
|
|
356
|
+
VALUES (?, ?, ?, ?, ?, ?)`);
|
|
372
357
|
const insertProp = db.prepare(`INSERT INTO raw_props
|
|
373
358
|
(session_id, component_id, name, type, required, category, default_value, description, token_reference, position)
|
|
374
359
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
@@ -402,7 +387,7 @@ export function storeRawComponents(db, sessionId, components, options) {
|
|
|
402
387
|
db.prepare('DELETE FROM raw_components WHERE session_id = ?').run(sessionId);
|
|
403
388
|
for (const comp of components) {
|
|
404
389
|
const componentId = deriveComponentId(comp.name, comp.source);
|
|
405
|
-
insertComp.run(sessionId, componentId, comp.name, comp.source, comp.framework, now
|
|
390
|
+
insertComp.run(sessionId, componentId, comp.name, comp.source, comp.framework, now);
|
|
406
391
|
for (let i = 0; i < comp.props.length; i++) {
|
|
407
392
|
const prop = comp.props[i];
|
|
408
393
|
insertProp.run(sessionId, componentId, prop.name, prop.type, prop.required ? 1 : 0, prop.category ?? null, prop.defaultValue ?? null, prop.description ?? null, prop.tokenReference ?? null, i);
|
|
@@ -482,7 +467,7 @@ export function storeRawComponents(db, sessionId, components, options) {
|
|
|
482
467
|
}
|
|
483
468
|
export function loadRawComponents(db, sessionId, allowedNames) {
|
|
484
469
|
const all = db
|
|
485
|
-
.prepare('SELECT component_id, name, source, framework
|
|
470
|
+
.prepare('SELECT component_id, name, source, framework FROM raw_components WHERE session_id = ? ORDER BY rowid')
|
|
486
471
|
.all(sessionId);
|
|
487
472
|
const components = allowedNames ? all.filter((c) => allowedNames.has(c.name)) : all;
|
|
488
473
|
if (components.length === 0)
|
|
@@ -513,16 +498,6 @@ export function loadRawComponents(db, sessionId, allowedNames) {
|
|
|
513
498
|
name: c.name,
|
|
514
499
|
source: c.source,
|
|
515
500
|
framework: c.framework,
|
|
516
|
-
extractionConfidence: c.extraction_confidence ?? 100,
|
|
517
|
-
reviewReasons: (() => {
|
|
518
|
-
try {
|
|
519
|
-
return JSON.parse(c.review_reasons ?? '[]');
|
|
520
|
-
}
|
|
521
|
-
catch {
|
|
522
|
-
return [];
|
|
523
|
-
}
|
|
524
|
-
})(),
|
|
525
|
-
needsReview: Boolean(c.needs_review),
|
|
526
501
|
props: (propsByComponent.get(c.component_id) ?? []).map((p) => {
|
|
527
502
|
const av = allowedValuesByProp.get(`${c.component_id}::${p.name}`);
|
|
528
503
|
const prop = {
|
package/dist/src/types.d.ts
CHANGED
|
@@ -34,9 +34,6 @@ export interface RawComponentDefinition {
|
|
|
34
34
|
* non-authorable context-provider components.
|
|
35
35
|
*/
|
|
36
36
|
usesCreateContext?: boolean;
|
|
37
|
-
extractionConfidence?: number;
|
|
38
|
-
reviewReasons?: string[];
|
|
39
|
-
needsReview?: boolean;
|
|
40
37
|
}
|
|
41
38
|
export interface ComponentExtractionResult {
|
|
42
39
|
components: RawComponentDefinition[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentful/experience-design-system-cli",
|
|
3
|
-
"version": "2.6.2-dev-build-
|
|
3
|
+
"version": "2.6.2-dev-build-ea30bad.0",
|
|
4
4
|
"description": "Contentful Experiences design system import CLI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"react-dom": "^18.3.1",
|
|
37
37
|
"ts-morph": "^27.0.2",
|
|
38
38
|
"typescript": "^5.9.3",
|
|
39
|
-
"@contentful/experience-design-system-types": "2.6.2-dev-build-
|
|
39
|
+
"@contentful/experience-design-system-types": "2.6.2-dev-build-ea30bad.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@tsconfig/node24": "^24.0.3",
|
|
@@ -70,16 +70,15 @@ Emit one JSON object on a single line. Lines not starting with `{` are ignored b
|
|
|
70
70
|
**Two tool calls — emit exactly one:**
|
|
71
71
|
|
|
72
72
|
```
|
|
73
|
-
{"tool":"select_component","name":"<ComponentName>","reason":"<brief reason>"
|
|
73
|
+
{"tool":"select_component","name":"<ComponentName>","reason":"<brief reason>"}
|
|
74
74
|
|
|
75
|
-
{"tool":"reject_component","name":"<ComponentName>","reason":"<brief reason>"
|
|
75
|
+
{"tool":"reject_component","name":"<ComponentName>","reason":"<brief reason>"}
|
|
76
76
|
```
|
|
77
77
|
|
|
78
78
|
**Rules:**
|
|
79
79
|
- Emit exactly one JSON object, on one line. No multi-line JSON. No markdown fences.
|
|
80
80
|
- The `name` must match the component name in the input.
|
|
81
81
|
- `reason` is a brief phrase documenting your decision.
|
|
82
|
-
- `confidence` is your certainty (0–100) that the decision is correct. Use 90–100 for obvious cases (clear UI atom or obvious infrastructure), 60–80 for borderline cases (few props, ambiguous purpose), and below 60 when you're genuinely unsure.
|
|
83
82
|
- Emit prose lines (not starting with `{`) to log your reasoning before the final tool call.
|
|
84
83
|
|
|
85
84
|
---
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { RawComponentDefinition } from '../../types.js';
|
|
2
|
-
export type ExtractionScore = {
|
|
3
|
-
confidence: number;
|
|
4
|
-
reasons: string[];
|
|
5
|
-
};
|
|
6
|
-
export declare function computeExtractionScore(component: RawComponentDefinition): ExtractionScore;
|
|
7
|
-
export declare function deriveNeedsReview(confidence: number): boolean;
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
// Prop type strings that indicate the extractor couldn't resolve a concrete type
|
|
2
|
-
const OPAQUE_TYPES = new Set(['any', 'unknown', 'object', 'Record<string, unknown>', 'Record<string, any>']);
|
|
3
|
-
// Prop names that are non-obvious and benefit from a description
|
|
4
|
-
const OBVIOUS_PROP_NAMES = new Set([
|
|
5
|
-
'className',
|
|
6
|
-
'style',
|
|
7
|
-
'id',
|
|
8
|
-
'children',
|
|
9
|
-
'disabled',
|
|
10
|
-
'hidden',
|
|
11
|
-
'onClick',
|
|
12
|
-
'onChange',
|
|
13
|
-
'onSubmit',
|
|
14
|
-
'onBlur',
|
|
15
|
-
'onFocus',
|
|
16
|
-
'href',
|
|
17
|
-
'src',
|
|
18
|
-
'alt',
|
|
19
|
-
'type',
|
|
20
|
-
'value',
|
|
21
|
-
'checked',
|
|
22
|
-
'placeholder',
|
|
23
|
-
'label',
|
|
24
|
-
'title',
|
|
25
|
-
'name',
|
|
26
|
-
'required',
|
|
27
|
-
'readOnly',
|
|
28
|
-
'autoFocus',
|
|
29
|
-
'tabIndex',
|
|
30
|
-
'role',
|
|
31
|
-
'aria-label',
|
|
32
|
-
'aria-describedby',
|
|
33
|
-
]);
|
|
34
|
-
// A union is "wide" if it mixes primitive base types with no additional narrowing
|
|
35
|
-
function isWidePrimitiveUnion(type) {
|
|
36
|
-
// e.g. "string | number | boolean" — three or more base primitives
|
|
37
|
-
const parts = type.split('|').map((p) => p.trim());
|
|
38
|
-
if (parts.length < 3)
|
|
39
|
-
return false;
|
|
40
|
-
const primitives = new Set(['string', 'number', 'boolean', 'null', 'undefined']);
|
|
41
|
-
return parts.filter((p) => primitives.has(p)).length >= 3;
|
|
42
|
-
}
|
|
43
|
-
export function computeExtractionScore(component) {
|
|
44
|
-
let confidence = 100;
|
|
45
|
-
const reasons = [];
|
|
46
|
-
// No props and no slots — extractor likely missed something or component is a wrapper
|
|
47
|
-
if (component.props.length === 0 && component.slots.length === 0) {
|
|
48
|
-
confidence -= 15;
|
|
49
|
-
reasons.push('no-props-or-slots');
|
|
50
|
-
}
|
|
51
|
-
for (const prop of component.props) {
|
|
52
|
-
// Opaque types — extractor couldn't resolve the real type
|
|
53
|
-
if (OPAQUE_TYPES.has(prop.type.trim())) {
|
|
54
|
-
confidence -= 20;
|
|
55
|
-
reasons.push(`opaque-type:${prop.name}`);
|
|
56
|
-
break; // only penalise once per component
|
|
57
|
-
}
|
|
58
|
-
// Wide primitive union — hard for the AI agent to classify meaningfully
|
|
59
|
-
if (isWidePrimitiveUnion(prop.type)) {
|
|
60
|
-
confidence -= 10;
|
|
61
|
-
reasons.push(`wide-union:${prop.name}`);
|
|
62
|
-
break; // only penalise once
|
|
63
|
-
}
|
|
64
|
-
// Non-obvious prop name with no description
|
|
65
|
-
if (!prop.description && !OBVIOUS_PROP_NAMES.has(prop.name)) {
|
|
66
|
-
confidence -= 10;
|
|
67
|
-
reasons.push('props-missing-description');
|
|
68
|
-
break; // only penalise once
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
// High prop count — possible DOM inflation near-miss or overly broad extraction
|
|
72
|
-
if (component.props.length > 50) {
|
|
73
|
-
confidence -= 20;
|
|
74
|
-
reasons.push(`high-prop-count:${component.props.length}`);
|
|
75
|
-
}
|
|
76
|
-
return {
|
|
77
|
-
confidence: Math.max(0, Math.min(100, confidence)),
|
|
78
|
-
reasons: [...new Set(reasons)], // deduplicate
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
export function deriveNeedsReview(confidence) {
|
|
82
|
-
return confidence < 70;
|
|
83
|
-
}
|