@chemx/create-chemx 26.9.16-1249 → 26.9.16-1408

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
@@ -1,5 +1,9 @@
1
1
  # Chemical X Protocol: Starter Kit
2
2
 
3
+ <p align="center">
4
+ <img src="./assets/chemx-starter-kit-logo.gif" alt="Chemical X Starter Kit Comic Logo" width="380" />
5
+ </p>
6
+
3
7
  [![Live App](https://img.shields.io/badge/Web%20App-chemicalx.xophz.com-06b6d4?style=for-the-badge&logo=cloudflare)](https://chemicalx.xophz.com)
4
8
  [![Parent Repo](https://img.shields.io/badge/Repository-awesome--secret--sauce-8b5cf6?style=for-the-badge)](https://github.com/Chemical-X-Protocol/awesome-secret-sauce)
5
9
  [![Benchmarks](https://img.shields.io/badge/Benchmarks-Agent%20Evaluations-10b981?style=for-the-badge)](https://github.com/Chemical-X-Protocol/benchmarks)
@@ -49,9 +49,10 @@ export const createPatternRegistry = () => {
49
49
  suggestedCapsule = 'types/state.d.ts';
50
50
  recommendation = 'Extract shared discriminated union into domain types to eliminate duplicate definitions';
51
51
  } else if (bucket.type === 'UI_STRUCTURE') {
52
- label = `Shared UI Layout Structure (${bucket.detail})`;
53
- suggestedCapsule = 'm-feature-card';
54
- recommendation = 'Extract canonical molecule capsule before slicing consumer monoliths';
52
+ const categorization = categorizeUiStructure(bucket.detail);
53
+ label = categorization.label;
54
+ suggestedCapsule = categorization.suggestedCapsule;
55
+ recommendation = categorization.recommendation;
55
56
  } else if (bucket.type === 'PREDICATE_LOGIC') {
56
57
  label = `Duplicated Boolean Predicate Topology (${bucket.detail})`;
57
58
  suggestedCapsule = 'usePredicateFilter.ts';
@@ -84,6 +85,92 @@ export const createPatternRegistry = () => {
84
85
  return { record, resolveHarmonizationCandidates };
85
86
  };
86
87
 
88
+ export const categorizeUiStructure = (signature = '') => {
89
+ const normalized = signature.toLowerCase();
90
+
91
+ const isSvg =
92
+ normalized.startsWith('svg>') ||
93
+ normalized.includes('path+path') ||
94
+ normalized.includes('polygon') ||
95
+ normalized.includes('circle');
96
+ if (isSvg) {
97
+ const isMultiLayer = (normalized.match(/path|circle|rect|polygon|g/g) || []).length > 3;
98
+ return {
99
+ label: `Shared Vector Glyph (${signature})`,
100
+ suggestedCapsule: isMultiLayer ? 'm-vector-glyph' : 'a-icon',
101
+ recommendation: 'Extract foundational icon atom or vector glyph capsule'
102
+ };
103
+ }
104
+
105
+ const hasButton =
106
+ normalized.includes('btn') ||
107
+ normalized.includes('button');
108
+ if (hasButton) {
109
+ const hasTextOrTitle =
110
+ normalized.includes('span') ||
111
+ normalized.includes('title') ||
112
+ normalized.includes('h1') ||
113
+ normalized.includes('h2') ||
114
+ normalized.includes('h3') ||
115
+ normalized.includes('h4') ||
116
+ normalized.includes('p');
117
+
118
+ if (hasTextOrTitle) {
119
+ return {
120
+ label: `Shared Action Header (${signature})`,
121
+ suggestedCapsule: 'm-action-header',
122
+ recommendation: 'Extract action header molecule capsule to standardize button actions'
123
+ };
124
+ }
125
+
126
+ return {
127
+ label: `Shared Button Row (${signature})`,
128
+ suggestedCapsule: 'm-button-row',
129
+ recommendation: 'Extract button row molecule capsule to consolidate action bar'
130
+ };
131
+ }
132
+
133
+ const hasChipOrBadge =
134
+ normalized.includes('chip') ||
135
+ normalized.includes('badge') ||
136
+ normalized.includes('pill') ||
137
+ normalized.includes('tag');
138
+ if (hasChipOrBadge) {
139
+ const chipCount = (normalized.match(/chip|badge|pill|tag/g) || []).length;
140
+ if (chipCount > 1) {
141
+ return {
142
+ label: `Shared Pill Row (${signature})`,
143
+ suggestedCapsule: 'm-pill-row',
144
+ recommendation: 'Extract pill row molecule capsule to group status indicators'
145
+ };
146
+ }
147
+
148
+ return {
149
+ label: `Shared Status Badge (${signature})`,
150
+ suggestedCapsule: 'm-status-badge',
151
+ recommendation: 'Extract status badge atom or capsule to unify state tags'
152
+ };
153
+ }
154
+
155
+ const hasFormInput =
156
+ normalized.includes('input') ||
157
+ normalized.includes('textarea') ||
158
+ normalized.includes('select');
159
+ if (hasFormInput) {
160
+ return {
161
+ label: `Shared Form Field (${signature})`,
162
+ suggestedCapsule: 'm-form-field',
163
+ recommendation: 'Extract form field molecule encapsulating label and input atom'
164
+ };
165
+ }
166
+
167
+ return {
168
+ label: `Shared UI Layout Structure (${signature})`,
169
+ suggestedCapsule: 'm-feature-card',
170
+ recommendation: 'Extract canonical molecule capsule before slicing consumer monoliths'
171
+ };
172
+ };
173
+
87
174
  const VOID_ELEMENTS = new Set([
88
175
  'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr'
89
176
  ]);
@@ -4,7 +4,8 @@ import {
4
4
  createPatternRegistry,
5
5
  extractTemplateTokens,
6
6
  buildTagTree,
7
- recordTemplatePatterns
7
+ recordTemplatePatterns,
8
+ categorizeUiStructure
8
9
  } from './pattern-detector.js';
9
10
 
10
11
  test('extractTemplateTokens: extracts tags, ignoring comments and handling quotes', () => {
@@ -56,6 +57,40 @@ test('buildTagTree and recordTemplatePatterns: captures UI_STRUCTURE from Vue te
56
57
 
57
58
  assert.ok(uiCandidate, 'Expected UI_STRUCTURE candidate to be found');
58
59
  assert.equal(uiCandidate.detail, 'v-sheet>(x-btn+x-btn)');
60
+ assert.equal(uiCandidate.suggestedCapsule, 'm-button-row');
59
61
  assert.equal(uiCandidate.fileCount, 2);
60
62
  assert.deepEqual(uiCandidate.uniqueFiles.sort(), ['src/components/A.vue', 'src/components/B.vue']);
61
63
  });
64
+
65
+ test('categorizeUiStructure: maps semantic AST topologies to specialized capsules', () => {
66
+ // SVG dual path -> a-icon
67
+ const svgRes = categorizeUiStructure('svg>(path+path)');
68
+ assert.equal(svgRes.suggestedCapsule, 'a-icon');
69
+ assert.ok(svgRes.label.includes('Vector Glyph'));
70
+
71
+ // Action header -> m-action-header
72
+ const actionRes = categorizeUiStructure('div>(span+v-btn)');
73
+ assert.equal(actionRes.suggestedCapsule, 'm-action-header');
74
+ assert.ok(actionRes.label.includes('Action Header'));
75
+
76
+ // Button row -> m-button-row
77
+ const btnRowRes = categorizeUiStructure('div>(v-btn+v-btn)');
78
+ assert.equal(btnRowRes.suggestedCapsule, 'm-button-row');
79
+
80
+ // Status badge -> m-status-badge
81
+ const badgeRes = categorizeUiStructure('div>(span+v-chip)');
82
+ assert.equal(badgeRes.suggestedCapsule, 'm-status-badge');
83
+
84
+ // Pill row -> m-pill-row
85
+ const pillRowRes = categorizeUiStructure('div>(v-chip+v-chip)');
86
+ assert.equal(pillRowRes.suggestedCapsule, 'm-pill-row');
87
+
88
+ // Form field -> m-form-field
89
+ const formRes = categorizeUiStructure('div>(label+input)');
90
+ assert.equal(formRes.suggestedCapsule, 'm-form-field');
91
+
92
+ // Fallback -> m-feature-card
93
+ const fallbackRes = categorizeUiStructure('div>(div+div)');
94
+ assert.equal(fallbackRes.suggestedCapsule, 'm-feature-card');
95
+ });
96
+
package/cli/mcp/tools.js CHANGED
@@ -184,13 +184,23 @@ const handleQueryPatterns = (args = {}, cwd = process.cwd()) => {
184
184
  };
185
185
 
186
186
  if (isCompact) {
187
+ const sampleOccurrences = [];
188
+ const seenSampleFiles = new Set();
189
+ for (const occ of (p.occurrences || [])) {
190
+ if (!seenSampleFiles.has(occ.filePath)) {
191
+ seenSampleFiles.add(occ.filePath);
192
+ sampleOccurrences.push({
193
+ file: occ.filePath,
194
+ line: occ.line
195
+ });
196
+ if (sampleOccurrences.length >= 3) break;
197
+ }
198
+ }
199
+
187
200
  return {
188
201
  ...base,
189
202
  files: p.uniqueFiles || Array.from(new Set((p.occurrences || []).map((o) => o.filePath))),
190
- sampleOccurrences: (p.occurrences || []).slice(0, 3).map((o) => ({
191
- file: o.filePath,
192
- line: o.line
193
- }))
203
+ sampleOccurrences
194
204
  };
195
205
  }
196
206
 
@@ -51,9 +51,15 @@ export const detectInstalledFamily = (cwd = process.cwd()) => {
51
51
  const atomsCandidate = ATOMS_PACKAGES.find((name) => Boolean(deps[name]));
52
52
  const paletteCandidate = PALETTE_PACKAGES.find((name) => Boolean(deps[name]));
53
53
 
54
+ const localAtomsCandidate =
55
+ fs.existsSync(path.resolve(cwd, 'src/components/atoms')) ? '@/components/atoms' :
56
+ fs.existsSync(path.resolve(cwd, 'src/atoms')) ? '@/atoms' : null;
57
+
58
+ const resolvedAtomsPackage = atomsCandidate || localAtomsCandidate || null;
59
+
54
60
  return {
55
- hasAtoms: Boolean(atomsCandidate),
56
- atomsPackage: atomsCandidate || null,
61
+ hasAtoms: Boolean(resolvedAtomsPackage),
62
+ atomsPackage: resolvedAtomsPackage,
57
63
  hasPalette: Boolean(paletteCandidate),
58
64
  palettePackage: paletteCandidate || null
59
65
  };
@@ -102,3 +102,22 @@ test('project-detector: detectInstalledFamily discovers @chemx/x-atoms and @chem
102
102
 
103
103
  fs.rmSync(tmpDir, { recursive: true, force: true });
104
104
  });
105
+
106
+ test('project-detector: detectInstalledFamily falls back to local components/atoms facade', () => {
107
+ const tmpDir = path.resolve(process.cwd(), 'scratch/test-detector-local-atoms');
108
+ if (fs.existsSync(tmpDir)) fs.rmSync(tmpDir, { recursive: true, force: true });
109
+ fs.mkdirSync(path.join(tmpDir, 'src/components/atoms'), { recursive: true });
110
+
111
+ fs.writeFileSync(
112
+ path.join(tmpDir, 'package.json'),
113
+ JSON.stringify({ dependencies: {} }),
114
+ 'utf-8'
115
+ );
116
+
117
+ const family = detectInstalledFamily(tmpDir);
118
+ assert.strictEqual(family.hasAtoms, true);
119
+ assert.strictEqual(family.atomsPackage, '@/components/atoms');
120
+
121
+ fs.rmSync(tmpDir, { recursive: true, force: true });
122
+ });
123
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chemx/create-chemx",
3
- "version": "26.9.16-1249",
3
+ "version": "26.9.16-1408",
4
4
  "description": "Chemical X Protocol: Private drop-in architecture starter kit and capsule generator",
5
5
  "type": "module",
6
6
  "bin": {
@@ -25,7 +25,8 @@
25
25
  "scripts",
26
26
  "README.md",
27
27
  "LICENSE",
28
- "docs"
28
+ "docs",
29
+ "assets"
29
30
  ],
30
31
  "exports": {
31
32
  ".": "./cli/index.js",