@fragments-sdk/cli 0.15.10 → 0.17.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.
Files changed (88) hide show
  1. package/dist/bin.js +901 -789
  2. package/dist/bin.js.map +1 -1
  3. package/dist/{chunk-6SQPP47U.js → chunk-ANTWP3UG.js} +532 -31
  4. package/dist/chunk-ANTWP3UG.js.map +1 -0
  5. package/dist/{chunk-ONUP6Z4W.js → chunk-B4A4ZEGS.js} +9 -9
  6. package/dist/{chunk-32LIWN2P.js → chunk-FFCI6OVZ.js} +584 -261
  7. package/dist/chunk-FFCI6OVZ.js.map +1 -0
  8. package/dist/{chunk-HQ6A6DTV.js → chunk-HNHE64CR.js} +315 -1089
  9. package/dist/chunk-HNHE64CR.js.map +1 -0
  10. package/dist/{chunk-BJE3425I.js → chunk-MN3B2EE6.js} +2 -2
  11. package/dist/{chunk-QCN35LJU.js → chunk-SAQW37L5.js} +3 -2
  12. package/dist/chunk-SAQW37L5.js.map +1 -0
  13. package/dist/{chunk-2WXKALIG.js → chunk-SNZXGHL2.js} +2 -2
  14. package/dist/{chunk-5JF26E55.js → chunk-VT2J62ND.js} +11 -11
  15. package/dist/{codebase-scanner-MQHUZC2G.js → codebase-scanner-2T5QIDBA.js} +2 -2
  16. package/dist/core/index.js +53 -1
  17. package/dist/{create-EXURTBKK.js → create-D44QD7MV.js} +2 -2
  18. package/dist/{doctor-BDPMYYE6.js → doctor-7B5N4JYU.js} +2 -2
  19. package/dist/{generate-PVOLUAAC.js → generate-T47JZRVU.js} +4 -4
  20. package/dist/govern-scan-X6UEIOSV.js +632 -0
  21. package/dist/govern-scan-X6UEIOSV.js.map +1 -0
  22. package/dist/index.js +7 -8
  23. package/dist/index.js.map +1 -1
  24. package/dist/{init-SSGUSP7Z.js → init-2RGAY4W6.js} +5 -5
  25. package/dist/mcp-bin.js +2 -2
  26. package/dist/scan-A2WJM54L.js +14 -0
  27. package/dist/{scan-generate-VY27PIOX.js → scan-generate-LUSOHT36.js} +4 -4
  28. package/dist/{service-QJGWUIVL.js → service-ROCP7TKG.js} +13 -15
  29. package/dist/{snapshot-WIJMEIFT.js → snapshot-B3SAW74Y.js} +2 -2
  30. package/dist/{static-viewer-7QIBQZRC.js → static-viewer-7L6UEYTJ.js} +3 -3
  31. package/dist/{test-64Z5BKBA.js → test-PQDVDURE.js} +3 -3
  32. package/dist/{token-normalizer-TEPOVBPV.js → token-normalizer-7TFCVDZL.js} +2 -2
  33. package/dist/{tokens-NZWFQIAB.js → tokens-64FG5FDP.js} +8 -9
  34. package/dist/{tokens-NZWFQIAB.js.map → tokens-64FG5FDP.js.map} +1 -1
  35. package/dist/{tokens-generate-5JQSJ27E.js → tokens-generate-CL4LBBQA.js} +2 -2
  36. package/package.json +9 -8
  37. package/src/bin.ts +55 -88
  38. package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/label.contract.json +1 -1
  39. package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/primitive.contract.json +1 -1
  40. package/src/commands/__tests__/context-cloud.test.ts +291 -0
  41. package/src/commands/__tests__/govern-scan.test.ts +185 -0
  42. package/src/commands/__tests__/govern.test.ts +1 -0
  43. package/src/commands/context-cloud.ts +355 -0
  44. package/src/commands/govern-scan-report.ts +170 -0
  45. package/src/commands/govern-scan.ts +282 -135
  46. package/src/commands/govern.ts +0 -157
  47. package/src/mcp/__tests__/server.integration.test.ts +9 -20
  48. package/src/service/enhance/codebase-scanner.ts +3 -2
  49. package/src/service/enhance/types.ts +3 -0
  50. package/dist/chunk-32LIWN2P.js.map +0 -1
  51. package/dist/chunk-6SQPP47U.js.map +0 -1
  52. package/dist/chunk-HQ6A6DTV.js.map +0 -1
  53. package/dist/chunk-MHIBEEW4.js +0 -511
  54. package/dist/chunk-MHIBEEW4.js.map +0 -1
  55. package/dist/chunk-QCN35LJU.js.map +0 -1
  56. package/dist/govern-scan-DW4QUAYD.js +0 -414
  57. package/dist/govern-scan-DW4QUAYD.js.map +0 -1
  58. package/dist/init-cloud-3DNKPWFB.js +0 -304
  59. package/dist/init-cloud-3DNKPWFB.js.map +0 -1
  60. package/dist/node-37AUE74M.js +0 -65
  61. package/dist/push-contracts-WY32TFP6.js +0 -84
  62. package/dist/push-contracts-WY32TFP6.js.map +0 -1
  63. package/dist/scan-PKSYSTRR.js +0 -15
  64. package/dist/static-viewer-7QIBQZRC.js.map +0 -1
  65. package/dist/token-parser-32KOIOFN.js +0 -22
  66. package/dist/token-parser-32KOIOFN.js.map +0 -1
  67. package/dist/tokens-push-HY3KO36V.js +0 -148
  68. package/dist/tokens-push-HY3KO36V.js.map +0 -1
  69. package/src/commands/init-cloud.ts +0 -382
  70. package/src/commands/push-contracts.ts +0 -112
  71. package/src/commands/tokens-push.ts +0 -199
  72. /package/dist/{chunk-ONUP6Z4W.js.map → chunk-B4A4ZEGS.js.map} +0 -0
  73. /package/dist/{chunk-BJE3425I.js.map → chunk-MN3B2EE6.js.map} +0 -0
  74. /package/dist/{chunk-2WXKALIG.js.map → chunk-SNZXGHL2.js.map} +0 -0
  75. /package/dist/{chunk-5JF26E55.js.map → chunk-VT2J62ND.js.map} +0 -0
  76. /package/dist/{codebase-scanner-MQHUZC2G.js.map → codebase-scanner-2T5QIDBA.js.map} +0 -0
  77. /package/dist/{create-EXURTBKK.js.map → create-D44QD7MV.js.map} +0 -0
  78. /package/dist/{doctor-BDPMYYE6.js.map → doctor-7B5N4JYU.js.map} +0 -0
  79. /package/dist/{generate-PVOLUAAC.js.map → generate-T47JZRVU.js.map} +0 -0
  80. /package/dist/{init-SSGUSP7Z.js.map → init-2RGAY4W6.js.map} +0 -0
  81. /package/dist/{node-37AUE74M.js.map → scan-A2WJM54L.js.map} +0 -0
  82. /package/dist/{scan-generate-VY27PIOX.js.map → scan-generate-LUSOHT36.js.map} +0 -0
  83. /package/dist/{scan-PKSYSTRR.js.map → service-ROCP7TKG.js.map} +0 -0
  84. /package/dist/{snapshot-WIJMEIFT.js.map → snapshot-B3SAW74Y.js.map} +0 -0
  85. /package/dist/{service-QJGWUIVL.js.map → static-viewer-7L6UEYTJ.js.map} +0 -0
  86. /package/dist/{test-64Z5BKBA.js.map → test-PQDVDURE.js.map} +0 -0
  87. /package/dist/{token-normalizer-TEPOVBPV.js.map → token-normalizer-7TFCVDZL.js.map} +0 -0
  88. /package/dist/{tokens-generate-5JQSJ27E.js.map → tokens-generate-CL4LBBQA.js.map} +0 -0
@@ -135,163 +135,6 @@ export async function governInit(options: GovernInitOptions = {}): Promise<void>
135
135
  );
136
136
  }
137
137
 
138
- // ---------------------------------------------------------------------------
139
- // connect
140
- // ---------------------------------------------------------------------------
141
-
142
- export async function governConnect(): Promise<void> {
143
- const { readFile, writeFile, appendFile } = await import('node:fs/promises');
144
- const { existsSync } = await import('node:fs');
145
- const { resolve } = await import('node:path');
146
- const { platform } = await import('node:os');
147
- const { exec } = await import('node:child_process');
148
- const { password, confirm } = await import('@inquirer/prompts');
149
-
150
- const cloudUrl = process.env.FRAGMENTS_URL ?? 'https://app.usefragments.com';
151
-
152
- console.log(pc.cyan(`\n ${BRAND.name} — Connect to Cloud\n`));
153
- console.log(
154
- pc.dim(' This will connect your project to the Fragments dashboard\n') +
155
- pc.dim(' for centralized audit tracking and team visibility.\n'),
156
- );
157
-
158
- // ── Step 1: Get API key ──────────────────────────────────────────────────
159
- console.log(pc.bold(' Step 1 of 3: Get your API key\n'));
160
-
161
- const dashboardUrl = `${cloudUrl}/api-keys`;
162
- console.log(pc.dim(` → Opening the dashboard in your browser...`));
163
- console.log(pc.dim(` Copy your API key from Settings → API Keys\n`));
164
-
165
- // Open browser (best-effort)
166
- const os = platform();
167
- const openCmd = os === 'darwin'
168
- ? `open "${dashboardUrl}"`
169
- : os === 'win32'
170
- ? `start "" "${dashboardUrl}"`
171
- : `xdg-open "${dashboardUrl}"`;
172
- exec(openCmd);
173
-
174
- let apiKey: string;
175
- let orgName: string;
176
-
177
- // eslint-disable-next-line no-constant-condition
178
- while (true) {
179
- apiKey = await password({
180
- message: 'Paste your API key:',
181
- mask: '*',
182
- });
183
-
184
- if (!apiKey.trim()) {
185
- console.log(pc.yellow('\n API key cannot be empty. Please try again.\n'));
186
- continue;
187
- }
188
-
189
- // Verify key against cloud
190
- console.log(pc.dim('\n Verifying...'));
191
- try {
192
- const response = await fetch(`${cloudUrl}/api/verify`, {
193
- headers: { Authorization: `Bearer ${apiKey.trim()}` },
194
- });
195
-
196
- if (!response.ok) {
197
- console.log(pc.red(`\n ✗ Invalid API key (HTTP ${response.status}). Please try again.\n`));
198
- continue;
199
- }
200
-
201
- const data = (await response.json()) as { valid: boolean; orgName?: string };
202
- if (!data.valid) {
203
- console.log(pc.red('\n ✗ API key not recognized. Please try again.\n'));
204
- continue;
205
- }
206
-
207
- orgName = data.orgName ?? 'your organization';
208
- console.log(pc.green(`\n ✓ Connected to "${orgName}" (verified)\n`));
209
- break;
210
- } catch (error) {
211
- console.log(
212
- pc.red('\n ✗ Could not reach the dashboard.'),
213
- );
214
- console.log(
215
- pc.dim(` ${error instanceof Error ? error.message : 'Network error'}\n`),
216
- );
217
- continue;
218
- }
219
- }
220
-
221
- // ── Step 2: Save configuration ──────────────────────────────────────────
222
- console.log(pc.bold(' Step 2 of 3: Save configuration\n'));
223
-
224
- const saveToEnv = await confirm({
225
- message: 'Save API key to .env file?',
226
- default: true,
227
- });
228
-
229
- if (saveToEnv) {
230
- const envPath = resolve('.env');
231
- const envEntry = `FRAGMENTS_API_KEY=${apiKey.trim()}`;
232
-
233
- if (existsSync(envPath)) {
234
- const envContent = await readFile(envPath, 'utf-8');
235
- if (envContent.includes('FRAGMENTS_API_KEY=')) {
236
- // Replace existing entry
237
- const updated = envContent.replace(
238
- /^FRAGMENTS_API_KEY=.*$/m,
239
- envEntry,
240
- );
241
- await writeFile(envPath, updated, 'utf-8');
242
- console.log(pc.green(' ✓ Updated FRAGMENTS_API_KEY in .env'));
243
- } else {
244
- await appendFile(envPath, `\n${envEntry}\n`, 'utf-8');
245
- console.log(pc.green(' ✓ Added FRAGMENTS_API_KEY to .env'));
246
- }
247
- } else {
248
- await writeFile(envPath, `${envEntry}\n`, 'utf-8');
249
- console.log(pc.green(' ✓ Created .env with FRAGMENTS_API_KEY'));
250
- }
251
-
252
- // Write FRAGMENTS_URL only if non-default
253
- if (cloudUrl !== 'https://app.usefragments.com') {
254
- const envContent = await readFile(envPath, 'utf-8');
255
- if (!envContent.includes('FRAGMENTS_URL=')) {
256
- await appendFile(envPath, `FRAGMENTS_URL=${cloudUrl}\n`, 'utf-8');
257
- console.log(pc.green(` ✓ Added FRAGMENTS_URL to .env`));
258
- }
259
- }
260
-
261
- // Ensure .env is in .gitignore
262
- const gitignorePath = resolve('.gitignore');
263
- if (existsSync(gitignorePath)) {
264
- const gitignore = await readFile(gitignorePath, 'utf-8');
265
- if (!gitignore.split('\n').some((line) => line.trim() === '.env')) {
266
- await appendFile(gitignorePath, '\n.env\n', 'utf-8');
267
- console.log(pc.green(' ✓ Added .env to .gitignore'));
268
- }
269
- } else {
270
- await writeFile(gitignorePath, '.env\n', 'utf-8');
271
- console.log(pc.green(' ✓ Created .gitignore with .env entry'));
272
- }
273
- }
274
-
275
- // ── Step 3: Config check ────────────────────────────────────────────────
276
- console.log(pc.bold('\n Step 3 of 3: Config check\n'));
277
-
278
- const { findGovernConfig } = await import('@fragments-sdk/govern');
279
- const configPath = findGovernConfig();
280
-
281
- if (configPath) {
282
- console.log(pc.green(` ✓ Found govern config: ${configPath}`));
283
- } else {
284
- console.log(
285
- pc.yellow(' No govern config found — run `fragments govern init` to create one'),
286
- );
287
- }
288
-
289
- // ── Done ────────────────────────────────────────────────────────────────
290
- console.log(pc.dim('\n ─────────────────────────────────────\n'));
291
- console.log(pc.green(' ✓ All set!') + ' Run `fragments govern check` to send your first audit.\n');
292
- console.log(pc.dim(` Dashboard: ${cloudUrl}/overview\n`));
293
- }
294
-
295
138
  // ---------------------------------------------------------------------------
296
139
  // report
297
140
  // ---------------------------------------------------------------------------
@@ -207,7 +207,6 @@ describe('CLI MCP server integration', () => {
207
207
  expect(names).toContain('inspect');
208
208
  expect(names).toContain('blocks');
209
209
  expect(names).toContain('tokens');
210
- expect(names).toContain('implement');
211
210
 
212
211
  const prefixedResult = await client.callTool({
213
212
  name: 'fragments_discover',
@@ -314,29 +313,19 @@ describe('CLI MCP server integration', () => {
314
313
  });
315
314
  });
316
315
 
317
- it('returns login-oriented implement results and respects component limits', async () => {
316
+ it('accepts discover depth:full without error', async () => {
318
317
  await withClient(async (client) => {
319
- const defaultResult = await client.callTool({
320
- name: 'implement',
321
- arguments: { useCase: 'login form' },
322
- });
323
- const defaultPayload = getTextPayload(defaultResult) as {
324
- components: Array<{ name: string }>;
325
- blocks?: Array<{ name: string }>;
326
- };
327
-
328
- expect(defaultPayload.components.length).toBeGreaterThan(0);
329
- expect(defaultPayload.blocks?.[0]?.name).toBe('Login Form');
330
-
331
- const limitedResult = await client.callTool({
332
- name: 'implement',
333
- arguments: { useCase: 'login form', limit: 1 },
318
+ const result = await client.callTool({
319
+ name: 'discover',
320
+ arguments: { useCase: 'button', depth: 'full' },
334
321
  });
335
- const limitedPayload = getTextPayload(limitedResult) as {
336
- components: Array<{ name: string }>;
322
+ const payload = getTextPayload(result) as {
323
+ useCase: string;
324
+ suggestions: Array<{ component: string }>;
337
325
  };
338
326
 
339
- expect(limitedPayload.components).toHaveLength(1);
327
+ expect(payload.useCase).toBe('button');
328
+ expect(payload.suggestions).toBeDefined();
340
329
  });
341
330
  });
342
331
  });
@@ -68,6 +68,7 @@ export async function scanCodebase(
68
68
  rootDir,
69
69
  include = DEFAULT_INCLUDE,
70
70
  exclude = DEFAULT_EXCLUDE,
71
+ files: explicitFiles,
71
72
  componentNames,
72
73
  useCache = true,
73
74
  onProgress,
@@ -84,7 +85,7 @@ export async function scanCodebase(
84
85
  cache = createEmptyCache(absoluteRoot);
85
86
  }
86
87
 
87
- // Discover all files
88
+ // Discover files — use explicit list when provided, otherwise glob
88
89
  onProgress?.({
89
90
  current: 0,
90
91
  total: 0,
@@ -92,7 +93,7 @@ export async function scanCodebase(
92
93
  phase: "discovering",
93
94
  });
94
95
 
95
- const files = await fg(include, {
96
+ const files = explicitFiles ?? await fg(include, {
96
97
  cwd: absoluteRoot,
97
98
  ignore: exclude,
98
99
  absolute: true,
@@ -236,6 +236,9 @@ export interface ScanOptions {
236
236
  /** Glob patterns for files to exclude */
237
237
  exclude?: string[];
238
238
 
239
+ /** Explicit file list — when set, skips glob discovery and scans only these paths (absolute) */
240
+ files?: string[];
241
+
239
242
  /** Component names to specifically track (if not provided, discovers all) */
240
243
  componentNames?: string[];
241
244