@paradigma-inc/flywheel 0.1.154 → 0.2.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 (32) hide show
  1. package/package.json +1 -1
  2. package/skills/flywheel/references/flywheel-mcp-tool-map.md +28 -0
  3. package/src/runtime/required-runtime-commands.json +3 -1
  4. package/src/runtime/vendor/flywheel-cli-dist/commands/artifact-metadata-schema.js +33 -0
  5. package/src/runtime/vendor/flywheel-cli-dist/commands/artifact-metadata-schema.js.map +1 -0
  6. package/src/runtime/vendor/flywheel-cli-dist/commands/graph-operation-resources.js +66 -9
  7. package/src/runtime/vendor/flywheel-cli-dist/commands/graph-operation-resources.js.map +1 -1
  8. package/src/runtime/vendor/flywheel-cli-dist/commands/help-metadata.js +1 -0
  9. package/src/runtime/vendor/flywheel-cli-dist/commands/help-metadata.js.map +1 -1
  10. package/src/runtime/vendor/flywheel-cli-dist/commands/help.js +10 -0
  11. package/src/runtime/vendor/flywheel-cli-dist/commands/help.js.map +1 -1
  12. package/src/runtime/vendor/flywheel-cli-dist/commands/hosted-graph.js +2 -5
  13. package/src/runtime/vendor/flywheel-cli-dist/commands/hosted-graph.js.map +1 -1
  14. package/src/runtime/vendor/flywheel-cli-dist/commands/hosted-repository-control.js +122 -0
  15. package/src/runtime/vendor/flywheel-cli-dist/commands/hosted-repository-control.js.map +1 -0
  16. package/src/runtime/vendor/flywheel-cli-dist/commands/index.js +2 -0
  17. package/src/runtime/vendor/flywheel-cli-dist/commands/index.js.map +1 -1
  18. package/src/runtime/vendor/flywheel-cli-dist/commands/registry/hosted.js +122 -0
  19. package/src/runtime/vendor/flywheel-cli-dist/commands/registry/hosted.js.map +1 -1
  20. package/src/runtime/vendor/flywheel-cli-dist/commands/registry/repository.js +148 -40
  21. package/src/runtime/vendor/flywheel-cli-dist/commands/registry/repository.js.map +1 -1
  22. package/src/runtime/vendor/flywheel-cli-dist/commands/registry.js +2 -1
  23. package/src/runtime/vendor/flywheel-cli-dist/commands/registry.js.map +1 -1
  24. package/src/runtime/vendor/flywheel-cli-dist/commands/repository-output.js +26 -0
  25. package/src/runtime/vendor/flywheel-cli-dist/commands/repository-output.js.map +1 -1
  26. package/src/runtime/vendor/flywheel-cli-dist/local/store/diagnostics.js +3 -2
  27. package/src/runtime/vendor/flywheel-cli-dist/local/store/diagnostics.js.map +1 -1
  28. package/src/runtime/vendor/flywheel-cli-dist/main.js +79 -11
  29. package/src/runtime/vendor/flywheel-cli-dist/main.js.map +1 -1
  30. package/src/runtime/vendor/manifest.json +5 -1
  31. package/src/setup/modes/cli.mjs +137 -106
  32. package/src/update/cache.mjs +4 -2
@@ -238,7 +238,9 @@ export async function runCliSetupMode({
238
238
  serverUrl,
239
239
  supportedCliHosts,
240
240
  });
241
- if (priorModeMatch.priorMode && priorModeMatch.affectedHosts.length > 0) {
241
+ const hasPriorModeConflict =
242
+ Boolean(priorModeMatch.priorMode) && priorModeMatch.affectedHosts.length > 0;
243
+ if (hasPriorModeConflict) {
242
244
  if (force !== true) {
243
245
  throw new SetupModeUsageError(
244
246
  formatPriorModeConflictError(priorModeMatch),
@@ -250,7 +252,85 @@ export async function runCliSetupMode({
250
252
  "reconcilePriorMode dependency is required when --force is used.",
251
253
  );
252
254
  }
255
+ }
256
+
257
+ const packageRoot = resolvePackageRootFn();
258
+ const skillNames = await listSkillNames(packageRoot);
259
+ if (skillNames.length === 0) {
260
+ throw new Error(`No bundled skills found under ${packageRoot}/skills.`);
261
+ }
262
+
263
+ const agentResults = [];
264
+ let runtimeConfigPath = null;
265
+ let pathHint = null;
266
+ let cleanupState = null;
267
+
268
+ // Usage errors (bad flags, empty --api-key, mode conflicts) are
269
+ // user-input problems, not operational failures on any particular host.
270
+ // Rethrow so they surface with their intended exit code and let the
271
+ // top-level handler print the message once — matching how `--mode mcp`
272
+ // already behaves. Wrapping them as per-host failures would falsely
273
+ // attribute a user-input error to every selected host and force the
274
+ // setup summary to exit 4 (operational) instead of 1/2 (usage). Shared by
275
+ // the prerequisite stage and the skill stage below so both stages classify
276
+ // failures identically and cannot drift apart.
277
+ function recordOperationalFailure(error) {
278
+ if (
279
+ error instanceof SetupInputUsageError ||
280
+ error instanceof SetupModeUsageError
281
+ ) {
282
+ throw error;
283
+ }
284
+
285
+ const message = error instanceof Error ? error.message : String(error);
286
+ for (const hostName of supportedHosts) {
287
+ agentResults.push({
288
+ agent: hostName,
289
+ success: false,
290
+ touchedFiles: [],
291
+ skippedSteps: ["skill-install"],
292
+ warnings: [message],
293
+ error: message,
294
+ });
295
+ }
296
+ }
253
297
 
298
+ // Prerequisites that can fail run before anything destructive: resolving
299
+ // the API key, confirming the server accepts it, and writing the CLI
300
+ // runtime configuration all happen before the forced reconcile below
301
+ // removes an existing --mode mcp host entry. That way an ordinary
302
+ // auth/network/config failure leaves the prior MCP entry intact instead of
303
+ // a machine with neither mode working. A skill-install failure *after* a
304
+ // successful reconcile is a separate, accepted boundary: the runtime
305
+ // config is already written, so the CLI stays usable and rerunning the
306
+ // same command converges — this boundary is intentional, not a gap.
307
+ let prerequisiteFailed = false;
308
+ if (!skillOnlyOnly) {
309
+ try {
310
+ const resolvedAuth = authResult ?? await resolveSetupApiKeyFn({
311
+ options: {
312
+ apiKey,
313
+ authMode,
314
+ },
315
+ baseUrl,
316
+ });
317
+ await validateRemoteReadinessFn({
318
+ baseUrl,
319
+ apiKey: resolvedAuth.apiKey,
320
+ });
321
+ const runtimeConfig = await writeRuntimeConfigFn({
322
+ origin: baseUrl,
323
+ apiKey: resolvedAuth.apiKey,
324
+ credentialProfile: await credentialProfileForSetupBaseUrl(baseUrl),
325
+ });
326
+ runtimeConfigPath = runtimeConfig.configPath;
327
+ } catch (error) {
328
+ recordOperationalFailure(error);
329
+ prerequisiteFailed = true;
330
+ }
331
+ }
332
+
333
+ if (!prerequisiteFailed && hasPriorModeConflict) {
254
334
  const reconcileEntries = Array.isArray(priorModeMatch.entries)
255
335
  ? priorModeMatch.entries
256
336
  : priorModeMatch.affectedHosts.map((hostName) => ({
@@ -291,120 +371,71 @@ export async function runCliSetupMode({
291
371
  );
292
372
  }
293
373
 
294
- const packageRoot = resolvePackageRootFn();
295
- const skillNames = await listSkillNames(packageRoot);
296
- if (skillNames.length === 0) {
297
- throw new Error(`No bundled skills found under ${packageRoot}/skills.`);
298
- }
299
-
300
- const agentResults = [];
301
- let runtimeConfigPath = null;
302
- let pathHint = null;
303
- let cleanupState = null;
304
- try {
305
- cleanupState = await inspectSkillArtifactsForHosts({
306
- projectRoot: cwd,
307
- hostNames: supportedHosts,
308
- scope,
309
- skillNames,
310
- });
311
-
312
- await installSkillForHosts({
313
- projectRoot: cwd,
314
- hostNames: supportedHosts,
315
- scope,
316
- serverName,
317
- serverUrl,
318
- skillNames,
319
- // CLI-mode install: write a per-host ownership marker for every host
320
- // so a later `setup --mode mcp --<host>` can detect this install via
321
- // the marker even when the skills-root is shared with other hosts.
322
- cliMode: true,
323
- });
324
-
325
- // Skill-only hosts (e.g. --universal, --antigravity) do not bind to the
326
- // flywheel CLI binary — they just drop skill directories for the IDE or
327
- // toolchain to read. Skip API-key minting, runtime-config writing, and
328
- // the binary-on-PATH hint when every selected host is skill-only, so a
329
- // `flywheel setup --universal --mode cli --yes` run is entirely hermetic.
330
- if (!skillOnlyOnly) {
331
- const resolvedAuth = authResult ?? await resolveSetupApiKeyFn({
332
- options: {
333
- apiKey,
334
- authMode,
335
- },
336
- baseUrl,
337
- });
338
- await validateRemoteReadinessFn({
339
- baseUrl,
340
- apiKey: resolvedAuth.apiKey,
341
- });
342
- const runtimeConfig = await writeRuntimeConfigFn({
343
- origin: baseUrl,
344
- apiKey: resolvedAuth.apiKey,
345
- credentialProfile: await credentialProfileForSetupBaseUrl(baseUrl),
346
- });
347
- runtimeConfigPath = runtimeConfig.configPath;
348
-
349
- const resolvedBinaryPath = await resolveFlywheelBinaryPath({
350
- env,
351
- platform,
374
+ if (!prerequisiteFailed) {
375
+ try {
376
+ cleanupState = await inspectSkillArtifactsForHosts({
377
+ projectRoot: cwd,
378
+ hostNames: supportedHosts,
379
+ scope,
380
+ skillNames,
352
381
  });
353
- pathHint = resolvedBinaryPath
354
- ? `flywheel binary detected on PATH at ${resolvedBinaryPath}.`
355
- : formatMissingFlywheelPathHint({ platform });
356
- }
357
382
 
358
- if (cleanupState?.backupRoot) {
359
- await removeDir(cleanupState.backupRoot, { force: true, recursive: true });
360
- cleanupState = null;
361
- }
362
-
363
- for (const hostName of supportedHosts) {
364
- agentResults.push({
365
- agent: hostName,
366
- success: true,
367
- touchedFiles: runtimeConfigPath ? [runtimeConfigPath] : [],
368
- skippedSteps: [],
369
- warnings: [],
370
- });
371
- }
372
- } catch (error) {
373
- if (cleanupState) {
374
- await cleanupSkillArtifactsForHosts({
383
+ await installSkillForHosts({
375
384
  projectRoot: cwd,
376
385
  hostNames: supportedHosts,
377
- priorState: cleanupState,
378
386
  scope,
387
+ serverName,
388
+ serverUrl,
379
389
  skillNames,
390
+ // CLI-mode install: write a per-host ownership marker for every host
391
+ // so a later `setup --mode mcp --<host>` can detect this install via
392
+ // the marker even when the skills-root is shared with other hosts.
393
+ cliMode: true,
380
394
  });
381
- cleanupState = null;
382
- }
383
395
 
384
- // Usage errors (bad flags, empty --api-key, mode conflicts) are
385
- // user-input problems, not operational failures on any particular host.
386
- // Rethrow so they surface with their intended exit code and let the
387
- // top-level handler print the message once — matching how `--mode mcp`
388
- // already behaves. Wrapping them as per-host failures would falsely
389
- // attribute a user-input error to every selected host and force the
390
- // setup summary to exit 4 (operational) instead of 1/2 (usage).
391
- if (
392
- error instanceof SetupInputUsageError ||
393
- error instanceof SetupModeUsageError
394
- ) {
395
- throw error;
396
- }
396
+ // Skill-only hosts (e.g. --universal, --antigravity) do not bind to
397
+ // the flywheel CLI binary they just drop skill directories for the
398
+ // IDE or toolchain to read. Skip the binary-on-PATH hint when every
399
+ // selected host is skill-only, so a
400
+ // `flywheel setup --universal --mode cli --yes` run is entirely
401
+ // hermetic.
402
+ if (!skillOnlyOnly) {
403
+ const resolvedBinaryPath = await resolveFlywheelBinaryPath({
404
+ env,
405
+ platform,
406
+ });
407
+ pathHint = resolvedBinaryPath
408
+ ? `flywheel binary detected on PATH at ${resolvedBinaryPath}.`
409
+ : formatMissingFlywheelPathHint({ platform });
410
+ }
397
411
 
398
- const message = error instanceof Error ? error.message : String(error);
399
- for (const hostName of supportedHosts) {
400
- agentResults.push({
401
- agent: hostName,
402
- success: false,
403
- touchedFiles: [],
404
- skippedSteps: ["skill-install"],
405
- warnings: [message],
406
- error: message,
407
- });
412
+ if (cleanupState?.backupRoot) {
413
+ await removeDir(cleanupState.backupRoot, { force: true, recursive: true });
414
+ cleanupState = null;
415
+ }
416
+
417
+ for (const hostName of supportedHosts) {
418
+ agentResults.push({
419
+ agent: hostName,
420
+ success: true,
421
+ touchedFiles: runtimeConfigPath ? [runtimeConfigPath] : [],
422
+ skippedSteps: [],
423
+ warnings: [],
424
+ });
425
+ }
426
+ } catch (error) {
427
+ if (cleanupState) {
428
+ await cleanupSkillArtifactsForHosts({
429
+ projectRoot: cwd,
430
+ hostNames: supportedHosts,
431
+ priorState: cleanupState,
432
+ scope,
433
+ skillNames,
434
+ });
435
+ cleanupState = null;
436
+ }
437
+
438
+ recordOperationalFailure(error);
408
439
  }
409
440
  }
410
441
 
@@ -24,8 +24,10 @@ export function resolveUpdateCheckCachePath({
24
24
  String(env.LOCALAPPDATA || "").trim() ||
25
25
  path.join(homeDir, "AppData", "Local");
26
26
  } else {
27
- cacheRoot =
28
- String(env.XDG_CACHE_HOME || "").trim() || path.join(homeDir, ".cache");
27
+ const xdgCacheHome = String(env.XDG_CACHE_HOME || "").trim();
28
+ cacheRoot = path.isAbsolute(xdgCacheHome)
29
+ ? xdgCacheHome
30
+ : path.join(homeDir, ".cache");
29
31
  }
30
32
  return path.join(cacheRoot, "flywheel", "update-check.json");
31
33
  }