@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.
- package/dist/bin.js +901 -789
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-6SQPP47U.js → chunk-ANTWP3UG.js} +532 -31
- package/dist/chunk-ANTWP3UG.js.map +1 -0
- package/dist/{chunk-ONUP6Z4W.js → chunk-B4A4ZEGS.js} +9 -9
- package/dist/{chunk-32LIWN2P.js → chunk-FFCI6OVZ.js} +584 -261
- package/dist/chunk-FFCI6OVZ.js.map +1 -0
- package/dist/{chunk-HQ6A6DTV.js → chunk-HNHE64CR.js} +315 -1089
- package/dist/chunk-HNHE64CR.js.map +1 -0
- package/dist/{chunk-BJE3425I.js → chunk-MN3B2EE6.js} +2 -2
- package/dist/{chunk-QCN35LJU.js → chunk-SAQW37L5.js} +3 -2
- package/dist/chunk-SAQW37L5.js.map +1 -0
- package/dist/{chunk-2WXKALIG.js → chunk-SNZXGHL2.js} +2 -2
- package/dist/{chunk-5JF26E55.js → chunk-VT2J62ND.js} +11 -11
- package/dist/{codebase-scanner-MQHUZC2G.js → codebase-scanner-2T5QIDBA.js} +2 -2
- package/dist/core/index.js +53 -1
- package/dist/{create-EXURTBKK.js → create-D44QD7MV.js} +2 -2
- package/dist/{doctor-BDPMYYE6.js → doctor-7B5N4JYU.js} +2 -2
- package/dist/{generate-PVOLUAAC.js → generate-T47JZRVU.js} +4 -4
- package/dist/govern-scan-X6UEIOSV.js +632 -0
- package/dist/govern-scan-X6UEIOSV.js.map +1 -0
- package/dist/index.js +7 -8
- package/dist/index.js.map +1 -1
- package/dist/{init-SSGUSP7Z.js → init-2RGAY4W6.js} +5 -5
- package/dist/mcp-bin.js +2 -2
- package/dist/scan-A2WJM54L.js +14 -0
- package/dist/{scan-generate-VY27PIOX.js → scan-generate-LUSOHT36.js} +4 -4
- package/dist/{service-QJGWUIVL.js → service-ROCP7TKG.js} +13 -15
- package/dist/{snapshot-WIJMEIFT.js → snapshot-B3SAW74Y.js} +2 -2
- package/dist/{static-viewer-7QIBQZRC.js → static-viewer-7L6UEYTJ.js} +3 -3
- package/dist/{test-64Z5BKBA.js → test-PQDVDURE.js} +3 -3
- package/dist/{token-normalizer-TEPOVBPV.js → token-normalizer-7TFCVDZL.js} +2 -2
- package/dist/{tokens-NZWFQIAB.js → tokens-64FG5FDP.js} +8 -9
- package/dist/{tokens-NZWFQIAB.js.map → tokens-64FG5FDP.js.map} +1 -1
- package/dist/{tokens-generate-5JQSJ27E.js → tokens-generate-CL4LBBQA.js} +2 -2
- package/package.json +9 -8
- package/src/bin.ts +55 -88
- package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/label.contract.json +1 -1
- package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/primitive.contract.json +1 -1
- package/src/commands/__tests__/context-cloud.test.ts +291 -0
- package/src/commands/__tests__/govern-scan.test.ts +185 -0
- package/src/commands/__tests__/govern.test.ts +1 -0
- package/src/commands/context-cloud.ts +355 -0
- package/src/commands/govern-scan-report.ts +170 -0
- package/src/commands/govern-scan.ts +282 -135
- package/src/commands/govern.ts +0 -157
- package/src/mcp/__tests__/server.integration.test.ts +9 -20
- package/src/service/enhance/codebase-scanner.ts +3 -2
- package/src/service/enhance/types.ts +3 -0
- package/dist/chunk-32LIWN2P.js.map +0 -1
- package/dist/chunk-6SQPP47U.js.map +0 -1
- package/dist/chunk-HQ6A6DTV.js.map +0 -1
- package/dist/chunk-MHIBEEW4.js +0 -511
- package/dist/chunk-MHIBEEW4.js.map +0 -1
- package/dist/chunk-QCN35LJU.js.map +0 -1
- package/dist/govern-scan-DW4QUAYD.js +0 -414
- package/dist/govern-scan-DW4QUAYD.js.map +0 -1
- package/dist/init-cloud-3DNKPWFB.js +0 -304
- package/dist/init-cloud-3DNKPWFB.js.map +0 -1
- package/dist/node-37AUE74M.js +0 -65
- package/dist/push-contracts-WY32TFP6.js +0 -84
- package/dist/push-contracts-WY32TFP6.js.map +0 -1
- package/dist/scan-PKSYSTRR.js +0 -15
- package/dist/static-viewer-7QIBQZRC.js.map +0 -1
- package/dist/token-parser-32KOIOFN.js +0 -22
- package/dist/token-parser-32KOIOFN.js.map +0 -1
- package/dist/tokens-push-HY3KO36V.js +0 -148
- package/dist/tokens-push-HY3KO36V.js.map +0 -1
- package/src/commands/init-cloud.ts +0 -382
- package/src/commands/push-contracts.ts +0 -112
- package/src/commands/tokens-push.ts +0 -199
- /package/dist/{chunk-ONUP6Z4W.js.map → chunk-B4A4ZEGS.js.map} +0 -0
- /package/dist/{chunk-BJE3425I.js.map → chunk-MN3B2EE6.js.map} +0 -0
- /package/dist/{chunk-2WXKALIG.js.map → chunk-SNZXGHL2.js.map} +0 -0
- /package/dist/{chunk-5JF26E55.js.map → chunk-VT2J62ND.js.map} +0 -0
- /package/dist/{codebase-scanner-MQHUZC2G.js.map → codebase-scanner-2T5QIDBA.js.map} +0 -0
- /package/dist/{create-EXURTBKK.js.map → create-D44QD7MV.js.map} +0 -0
- /package/dist/{doctor-BDPMYYE6.js.map → doctor-7B5N4JYU.js.map} +0 -0
- /package/dist/{generate-PVOLUAAC.js.map → generate-T47JZRVU.js.map} +0 -0
- /package/dist/{init-SSGUSP7Z.js.map → init-2RGAY4W6.js.map} +0 -0
- /package/dist/{node-37AUE74M.js.map → scan-A2WJM54L.js.map} +0 -0
- /package/dist/{scan-generate-VY27PIOX.js.map → scan-generate-LUSOHT36.js.map} +0 -0
- /package/dist/{scan-PKSYSTRR.js.map → service-ROCP7TKG.js.map} +0 -0
- /package/dist/{snapshot-WIJMEIFT.js.map → snapshot-B3SAW74Y.js.map} +0 -0
- /package/dist/{service-QJGWUIVL.js.map → static-viewer-7L6UEYTJ.js.map} +0 -0
- /package/dist/{test-64Z5BKBA.js.map → test-PQDVDURE.js.map} +0 -0
- /package/dist/{token-normalizer-TEPOVBPV.js.map → token-normalizer-7TFCVDZL.js.map} +0 -0
- /package/dist/{tokens-generate-5JQSJ27E.js.map → tokens-generate-CL4LBBQA.js.map} +0 -0
package/src/commands/govern.ts
CHANGED
|
@@ -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('
|
|
316
|
+
it('accepts discover depth:full without error', async () => {
|
|
318
317
|
await withClient(async (client) => {
|
|
319
|
-
const
|
|
320
|
-
name: '
|
|
321
|
-
arguments: { useCase: '
|
|
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
|
|
336
|
-
|
|
322
|
+
const payload = getTextPayload(result) as {
|
|
323
|
+
useCase: string;
|
|
324
|
+
suggestions: Array<{ component: string }>;
|
|
337
325
|
};
|
|
338
326
|
|
|
339
|
-
expect(
|
|
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
|
|
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
|
|