@astryxdesign/cli 0.7.12-canary.f543ec7 → 0.7.12

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 (113) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/api/build/blocks/blocks.d.mts +41 -0
  3. package/api/build/blocks/blocks.mjs +118 -68
  4. package/api/component/__fixtures__/resolutions/modules.txt +3 -3
  5. package/api/docs/_adapter.d.mts +22 -0
  6. package/api/docs/_adapter.mjs +81 -3
  7. package/api/docs/docs.doc.mjs +5 -5
  8. package/api/docs/docs.type.d.mts +21 -0
  9. package/api/docs/docs.type.mjs +11 -0
  10. package/api/docs/list/list.d.mts +5 -1
  11. package/api/docs/list/list.mjs +72 -6
  12. package/api/docs/node/node.mjs +2 -40
  13. package/api/doctor/doctor.d.mts +4 -4
  14. package/api/doctor/doctor.mjs +248 -78
  15. package/api/icon/_authoring.mjs +8 -1
  16. package/api/icon/add/add.mjs +45 -5
  17. package/api/icon/icon.type.d.mts +24 -0
  18. package/api/icon/icon.type.mjs +6 -0
  19. package/api/icon/iconAdd.doc.mjs +1 -1
  20. package/api/icon/map/map.mjs +11 -4
  21. package/api/index.d.mts +1 -0
  22. package/api/index.mjs +1 -0
  23. package/api/integration/add-helpers.d.mts +5 -1
  24. package/api/integration/add-helpers.mjs +1 -1
  25. package/api/render/_adapter.mjs +9 -0
  26. package/api/render/render.doc.mjs +3 -3
  27. package/api/render/render.type.d.mts +1 -1
  28. package/api/render/render.type.mjs +1 -1
  29. package/api/search/ORDERING.md +3 -0
  30. package/api/search/_adapter.d.mts +32 -3
  31. package/api/search/_adapter.mjs +50 -22
  32. package/api/search/_fit-order.d.mts +47 -0
  33. package/api/search/_fit-order.mjs +257 -0
  34. package/api/search/_main-searcher.mjs +15 -5
  35. package/api/search/exportSearchIndex.doc.d.mts +10 -0
  36. package/api/search/exportSearchIndex.doc.mjs +58 -0
  37. package/api/search/rankSearchIndex.doc.d.mts +10 -0
  38. package/api/search/rankSearchIndex.doc.mjs +79 -0
  39. package/api/search/search-index.d.mts +44 -0
  40. package/api/search/search-index.mjs +270 -0
  41. package/api/search/search.type.d.mts +28 -0
  42. package/api/search/search.type.mjs +17 -0
  43. package/api/swizzle/_moduleGraph.d.mts +7 -0
  44. package/api/swizzle/_moduleGraph.mjs +191 -83
  45. package/api/swizzle/copy/copy.mjs +28 -6
  46. package/api/template/skeleton/skeleton.mjs +1 -1
  47. package/api/template/template.mjs +1 -0
  48. package/assets/codemods/transforms/v0.1.0/drop-xds-prefix-imports.mjs +1 -1
  49. package/assets/codemods/transforms/v0.1.0/index.mjs +1 -1
  50. package/assets/discover/official-packages.json +16 -0
  51. package/assets/docs/styling-overview.doc.mjs +3 -3
  52. package/assets/docs/tree/icon-sets.doc.mjs +4 -4
  53. package/assets/docs/tree/render-pages.doc.mjs +3 -3
  54. package/assets/docs/tree/tailwind.doc.mjs +41 -1
  55. package/assets/docs/tree/type-scale.doc.mjs +19 -2
  56. package/assets/templates/blocks/components/Hooks/useInputStatusIconHookUsage.tsx +1 -0
  57. package/assets/templates/blocks/components/Hooks/useInteractiveRoleHookUsage.doc.mjs +14 -0
  58. package/assets/templates/blocks/components/Hooks/useInteractiveRoleHookUsage.tsx +56 -0
  59. package/assets/templates/blocks/components/Hooks/useStreamingTextHookUsage.doc.mjs +2 -2
  60. package/assets/templates/blocks/components/Hooks/useStreamingTextHookUsage.tsx +66 -10
  61. package/assets/templates/blocks/components/Link/useLinkifyHookUsage.doc.mjs +14 -0
  62. package/assets/templates/blocks/components/Link/useLinkifyHookUsage.tsx +39 -0
  63. package/assets/templates/blocks/components/PowerSearch/usePowerSearchConfigHookUsage.doc.mjs +14 -0
  64. package/assets/templates/blocks/components/PowerSearch/usePowerSearchConfigHookUsage.tsx +83 -0
  65. package/assets/templates/blocks/components/Table/RowsVirtualizationHookUsage.doc.mjs +14 -0
  66. package/assets/templates/blocks/components/Table/RowsVirtualizationHookUsage.tsx +41 -0
  67. package/assets/templates/blocks/components/Table/useTableColumnSettingsStateHookUsage.doc.mjs +14 -0
  68. package/assets/templates/blocks/components/Table/useTableColumnSettingsStateHookUsage.tsx +97 -0
  69. package/assets/templates/pages/table-filter/page.tsx +15 -23
  70. package/assets/templates/pages/table-grouped/page.tsx +30 -30
  71. package/assets/templates/pages/table-inbox/page.tsx +3 -6
  72. package/assets/templates/themes/butter/butterTheme.ts +1 -1
  73. package/assets/templates/themes/gothic/gothicTheme.ts +16 -0
  74. package/assets/templates/themes/matcha/matchaTheme.ts +1 -1
  75. package/assets/templates/themes/neutral/neutralTheme.ts +6 -0
  76. package/assets/templates/themes/y2k/y2kPaletteRefs.generated.ts +1 -1
  77. package/assets/templates/themes/y2k/y2kTheme.ts +1 -1
  78. package/clients/cli/commands/docs.doc.mjs +5 -1
  79. package/clients/cli/commands/docs.mjs +30 -2
  80. package/clients/cli/commands/experimental/icon.mjs +25 -4
  81. package/clients/cli/commands/experimental-render.doc.mjs +1 -1
  82. package/clients/cli/commands/template.mjs +204 -181
  83. package/clients/cli/lib/resolve-theme.mjs +16 -7
  84. package/foundation/discovery/component-discovery.mjs +1 -1
  85. package/foundation/discovery/hook-discovery.mjs +31 -8
  86. package/foundation/discovery/icon-set-writer.d.mts +21 -5
  87. package/foundation/discovery/icon-set-writer.mjs +114 -13
  88. package/foundation/discovery/template-adapter.mjs +2 -2
  89. package/foundation/doc-compiler/import.d.mts +5 -1
  90. package/foundation/doc-compiler/import.mjs +40 -6
  91. package/foundation/fs/import-url.d.mts +20 -0
  92. package/foundation/fs/import-url.mjs +22 -0
  93. package/foundation/fs/module-loader.d.mts +24 -3
  94. package/foundation/fs/module-loader.mjs +69 -47
  95. package/foundation/render/charts.d.mts +59 -0
  96. package/foundation/render/charts.mjs +216 -0
  97. package/foundation/render/embedded-imports.d.mts +7 -7
  98. package/foundation/render/embedded-imports.mjs +63 -54
  99. package/foundation/render/page-host.d.mts +4 -0
  100. package/foundation/render/page-host.mjs +45 -0
  101. package/foundation/render/page-worker.mjs +350 -57
  102. package/foundation/render/render-page.d.mts +11 -6
  103. package/foundation/render/render-page.mjs +164 -54
  104. package/foundation/render/ts-paths.d.mts +63 -0
  105. package/foundation/render/ts-paths.mjs +138 -0
  106. package/foundation/response/error-codes.doc.mjs +3 -3
  107. package/foundation/response/error-codes.mjs +3 -2
  108. package/foundation/text/levenshtein.d.mts +6 -1
  109. package/foundation/text/levenshtein.mjs +25 -9
  110. package/foundation/text/suggest-order.d.mts +8 -1
  111. package/foundation/text/suggest-order.mjs +72 -6
  112. package/foundation/xle/registry-core.mjs +1 -1
  113. package/package.json +7 -6
@@ -27,7 +27,10 @@
27
27
  import * as fs from 'node:fs';
28
28
  import * as path from 'node:path';
29
29
 
30
- import {MIN_NODE_VERSION, isNodeVersionSupported} from '../../foundation/env/node-version.mjs';
30
+ import {
31
+ MIN_NODE_VERSION,
32
+ isNodeVersionSupported,
33
+ } from '../../foundation/env/node-version.mjs';
31
34
  import {
32
35
  AGENT_DOC_PATHS,
33
36
  holdsAstryxBlock,
@@ -37,9 +40,21 @@ import {
37
40
  assertAgentDocsTarget,
38
41
  resolveAgentDocsRoot,
39
42
  } from '../../foundation/agent-docs/agent-docs.mjs';
40
- import {CLI_ROOT, findProjectCoreDir, findInstalledPackage} from '../../foundation/fs/paths.mjs';
41
- import {explainPackageManager, formatCliCommand, getCliInvocation, guessCliInvocation} from '../../foundation/env/package-manager.mjs';
42
- import {chooseDeclaredCliCommand, declaredCliCommandsOf} from '../../foundation/env/cli-command.mjs';
43
+ import {
44
+ CLI_ROOT,
45
+ findProjectCoreDir,
46
+ findInstalledPackage,
47
+ } from '../../foundation/fs/paths.mjs';
48
+ import {
49
+ explainPackageManager,
50
+ formatCliCommand,
51
+ getCliInvocation,
52
+ guessCliInvocation,
53
+ } from '../../foundation/env/package-manager.mjs';
54
+ import {
55
+ chooseDeclaredCliCommand,
56
+ declaredCliCommandsOf,
57
+ } from '../../foundation/env/cli-command.mjs';
43
58
  import {
44
59
  findConfigPath,
45
60
  Project,
@@ -64,15 +79,40 @@ import {
64
79
  import {typedEdges} from '../docs/node/node.mjs';
65
80
  import {detailView, indexView} from '../../foundation/doc-compiler/lenses.mjs';
66
81
  import {isExperimentalTreeNode} from '../../foundation/doc-compiler/tree.mjs';
67
- import {semverCompare, isValidSemver, satisfiesRange} from '../../foundation/env/semver.mjs';
82
+ import {
83
+ semverCompare,
84
+ isValidSemver,
85
+ satisfiesRange,
86
+ } from '../../foundation/env/semver.mjs';
68
87
  import {
69
88
  checkAppThemes,
70
89
  checkThemes,
71
90
  themeImportWiring,
72
91
  } from './theme-checks.mjs';
73
- import {availableLine, RUN_FROM_PROJECT, upgradeLine} from '../../foundation/release/notices.mjs';
74
- import {COMPUTER_COMMAND, COMPUTER_INSTALL_STEP} from '../../foundation/agent-docs/computer-agent-text.mjs';
75
- import {alignCommand, addCliCommandFor, computerAgentTextState, installCommandFor, insideProjectElsewhere, projectCoreVersion, projectHasOwnCli, computerIntegrationsOutside, computerIntegrationsToAdd, displayHomePath, outsideProject, releaseFacts, builtIntegrationPackages} from './_adapter.mjs';
92
+ import {
93
+ availableLine,
94
+ RUN_FROM_PROJECT,
95
+ upgradeLine,
96
+ } from '../../foundation/release/notices.mjs';
97
+ import {
98
+ COMPUTER_COMMAND,
99
+ COMPUTER_INSTALL_STEP,
100
+ } from '../../foundation/agent-docs/computer-agent-text.mjs';
101
+ import {
102
+ alignCommand,
103
+ addCliCommandFor,
104
+ computerAgentTextState,
105
+ installCommandFor,
106
+ insideProjectElsewhere,
107
+ projectCoreVersion,
108
+ projectHasOwnCli,
109
+ computerIntegrationsOutside,
110
+ computerIntegrationsToAdd,
111
+ displayHomePath,
112
+ outsideProject,
113
+ releaseFacts,
114
+ builtIntegrationPackages,
115
+ } from './_adapter.mjs';
76
116
  import {checkBuilt} from './built-checks.mjs';
77
117
 
78
118
  /**
@@ -248,7 +288,9 @@ export function checkCoreInstalled(ctx) {
248
288
  : '@astryxdesign/core could not be resolved from this project.',
249
289
  ...(found
250
290
  ? {}
251
- : {fix: 'Install the design system: `npm install @astryxdesign/core` (or yarn/pnpm/bun).'}),
291
+ : {
292
+ fix: 'Install the design system: `npm install @astryxdesign/core` (or yarn/pnpm/bun).',
293
+ }),
252
294
  };
253
295
  }
254
296
 
@@ -272,7 +314,8 @@ export function checkVersionAlignment(ctx) {
272
314
  id: 'version-alignment',
273
315
  label: '@astryxdesign/core <-> @astryxdesign/cli alignment',
274
316
  status: 'info',
275
- message: 'Skipped — could not read both @astryxdesign/core and @astryxdesign/cli versions.',
317
+ message:
318
+ 'Skipped — could not read both @astryxdesign/core and @astryxdesign/cli versions.',
276
319
  };
277
320
  }
278
321
 
@@ -386,7 +429,10 @@ export async function checkConfig(ctx) {
386
429
  const {pathToFileURL} = await import('node:url');
387
430
  const mod = await import(pathToFileURL(ctx.configPath).href);
388
431
  const config = mod.default;
389
- if (config !== undefined && (typeof config !== 'object' || config === null)) {
432
+ if (
433
+ config !== undefined &&
434
+ (typeof config !== 'object' || config === null)
435
+ ) {
390
436
  return {
391
437
  id: 'config',
392
438
  label: 'astryx.config.mjs',
@@ -438,9 +484,13 @@ export async function checkConfig(ctx) {
438
484
  function describeNoImplicit(integrations) {
439
485
  const own = integrations.filter(integration => integration.__local);
440
486
  const configured = integrations.length - own.length;
441
- const ownNames = own.map(integration => integration.name ?? 'this package').join(', ');
442
- if (own.length === 0) return 'None — every loaded integration is named in astryx.config.';
443
- if (configured === 0) return `None — the only loaded integration is this package's own manifest (${ownNames}).`;
487
+ const ownNames = own
488
+ .map(integration => integration.name ?? 'this package')
489
+ .join(', ');
490
+ if (own.length === 0)
491
+ return 'None — every loaded integration is named in astryx.config.';
492
+ if (configured === 0)
493
+ return `None — the only loaded integration is this package's own manifest (${ownNames}).`;
444
494
  return `None — every loaded integration is named in astryx.config or is this package's own manifest (${ownNames}).`;
445
495
  }
446
496
 
@@ -476,7 +526,10 @@ export function checkImplicitIntegrations(ctx) {
476
526
  id,
477
527
  label,
478
528
  status: 'info',
479
- message: skippedBecause(ctx, 'the project configuration could not be read'),
529
+ message: skippedBecause(
530
+ ctx,
531
+ 'the project configuration could not be read',
532
+ ),
480
533
  };
481
534
  }
482
535
 
@@ -491,7 +544,9 @@ export function checkImplicitIntegrations(ctx) {
491
544
  // A record that carries `__loadError` (the package's own manifest, or a
492
545
  // configured one, that failed to load) loaded nothing, so it is not named
493
546
  // as loaded beside the line reporting that it could not be loaded.
494
- const loaded = ctx.integrations.filter(integration => !integration.__loadError);
547
+ const loaded = ctx.integrations.filter(
548
+ integration => !integration.__loadError,
549
+ );
495
550
  return {
496
551
  id,
497
552
  label,
@@ -533,12 +588,16 @@ export function checkImplicitIntegrations(ctx) {
533
588
  typeof integration.__packageDir === 'string'
534
589
  ? integration.__packageDir
535
590
  : (ctx.cwd ?? process.cwd());
536
- return !fs.existsSync(path.isAbsolute(dir) ? dir : path.resolve(base, dir));
591
+ return !fs.existsSync(
592
+ path.isAbsolute(dir) ? dir : path.resolve(base, dir),
593
+ );
537
594
  });
538
595
  const roots = declared.filter(root => !missing.includes(root));
539
596
  const contributes = roots.length > 0 ? roots.join(', ') : 'nothing';
540
597
  const absent =
541
- missing.length > 0 ? ` (declared ${missing.join(', ')} missing on disk)` : '';
598
+ missing.length > 0
599
+ ? ` (declared ${missing.join(', ')} missing on disk)`
600
+ : '';
542
601
  return `${integration.name}${version}${alias} from ${integration.__dependencyField}, contributing ${contributes}${absent}`;
543
602
  });
544
603
 
@@ -610,7 +669,10 @@ export function checkConfiguredIntegrations(ctx) {
610
669
  id,
611
670
  label,
612
671
  status: 'info',
613
- message: skippedBecause(ctx, 'the project configuration could not be read'),
672
+ message: skippedBecause(
673
+ ctx,
674
+ 'the project configuration could not be read',
675
+ ),
614
676
  };
615
677
  }
616
678
  // What counts as configured is what astryx.config names, read from the
@@ -676,7 +738,9 @@ function agentDocsRefusal(cwd) {
676
738
  assertAgentDocsTarget(agentDocsTargetDir(cwd));
677
739
  return null;
678
740
  } catch (err) {
679
- return err instanceof Error && err.name === 'AgentDocsHomeError' ? err.message : null;
741
+ return err instanceof Error && err.name === 'AgentDocsHomeError'
742
+ ? err.message
743
+ : null;
680
744
  }
681
745
  }
682
746
 
@@ -691,9 +755,11 @@ export function checkAgentDocs(ctx) {
691
755
  // src/ or a nested package, the docs sit at the root.
692
756
  const dirs = [
693
757
  ...new Set(
694
- [ctx.cwd, projectRootOf(ctx), resolveAgentDocsRoot(ctx.cwd) ?? ctx.cwd].map(dir =>
695
- path.resolve(dir),
696
- ),
758
+ [
759
+ ctx.cwd,
760
+ projectRootOf(ctx),
761
+ resolveAgentDocsRoot(ctx.cwd) ?? ctx.cwd,
762
+ ].map(dir => path.resolve(dir)),
697
763
  ),
698
764
  ];
699
765
  const present = dirs.flatMap(dir =>
@@ -736,8 +802,10 @@ export function checkAgentDocs(ctx) {
736
802
  const withMarkers = present.filter(abs => {
737
803
  const content = read(abs);
738
804
  return (
739
- (content.includes('<!-- ASTRYX:START -->') || content.includes('<!-- XDS:START -->')) &&
740
- (content.includes('<!-- ASTRYX:END -->') || content.includes('<!-- XDS:END -->')) &&
805
+ (content.includes('<!-- ASTRYX:START -->') ||
806
+ content.includes('<!-- XDS:START -->')) &&
807
+ (content.includes('<!-- ASTRYX:END -->') ||
808
+ content.includes('<!-- XDS:END -->')) &&
741
809
  holdsAstryxBlock(content)
742
810
  );
743
811
  });
@@ -746,7 +814,9 @@ export function checkAgentDocs(ctx) {
746
814
  // of AGENTS.md) is broken when that file holds no block: the agents that
747
815
  // read either file get no Astryx docs.
748
816
  const dangling = present.flatMap(abs => {
749
- const match = /<!-- ASTRYX:START -->\s*@(\S+)\s*<!-- ASTRYX:END -->/.exec(read(abs));
817
+ const match = /<!-- ASTRYX:START -->\s*@(\S+)\s*<!-- ASTRYX:END -->/.exec(
818
+ read(abs),
819
+ );
750
820
  if (match == null) return [];
751
821
  const target = path.resolve(path.dirname(abs), match[1]);
752
822
  return holdsAstryxBlock(read(target)) ? [] : [{abs, target}];
@@ -848,8 +918,12 @@ export function checkPeerDeps(ctx) {
848
918
  // Pin the required range for anything wrong so the hint fixes it even when a
849
919
  // stale consumer range would otherwise resolve an incompatible version.
850
920
  // Quote targets containing shell metacharacters (e.g. `react@>=19.0.0`).
851
- const quote = (/** @type {string} */ s) => (/[<>|() ]/.test(s) ? `'${s}'` : s);
852
- const targets = [...missing, ...mismatched.map(m => `${m.name}@${m.want}`)].map(quote);
921
+ const quote = (/** @type {string} */ s) =>
922
+ /[<>|() ]/.test(s) ? `'${s}'` : s;
923
+ const targets = [
924
+ ...missing,
925
+ ...mismatched.map(m => `${m.name}@${m.want}`),
926
+ ].map(quote);
853
927
  return {
854
928
  id: 'peer-deps',
855
929
  label: '@astryxdesign/core peer dependencies',
@@ -1060,7 +1134,10 @@ export function checkProviderIdentity(ctx) {
1060
1134
  id,
1061
1135
  label,
1062
1136
  status: 'info',
1063
- message: skippedBecause(ctx, 'the project configuration could not be read'),
1137
+ message: skippedBecause(
1138
+ ctx,
1139
+ 'the project configuration could not be read',
1140
+ ),
1064
1141
  };
1065
1142
  }
1066
1143
 
@@ -1241,7 +1318,8 @@ export async function checkAuthoringDocs(_ctx, options = {}) {
1241
1318
  const audit = await auditAuthoringSelfDocs({root, sources});
1242
1319
  const problems = [
1243
1320
  ...audit.unreachable.map(
1244
- source => `${source} is not in \`${formatCliCommand('docs authoring/field-reference')}\``,
1321
+ source =>
1322
+ `${source} is not in \`${formatCliCommand('docs authoring/field-reference')}\``,
1245
1323
  ),
1246
1324
  ...audit.failed.map(
1247
1325
  ({source, error}) => `${source} failed to load: ${error}`,
@@ -1257,7 +1335,9 @@ export async function checkAuthoringDocs(_ctx, options = {}) {
1257
1335
  : {keys: options.topicKeys};
1258
1336
  const surface = [
1259
1337
  ...('error' in topic
1260
- ? [`\`${formatCliCommand('docs authoring/field-reference')}\` could not be read: ${topic.error}`]
1338
+ ? [
1339
+ `\`${formatCliCommand('docs authoring/field-reference')}\` could not be read: ${topic.error}`,
1340
+ ]
1261
1341
  : []),
1262
1342
  ...surfaceProblems(
1263
1343
  await auditAuthoringSurface({root, sources, topicKeys: topic.keys}),
@@ -1299,7 +1379,7 @@ export async function checkAuthoringDocs(_ctx, options = {}) {
1299
1379
 
1300
1380
  /** How the CLI-docs audit's problems are fixed. */
1301
1381
  const CLI_DOCS_FIX =
1302
- "Set `namespace` on each CLI doc to the one that reads it: cli/commands for a command, cli/api for an API function or the output schema, error codes, and response types, and authoring for a file an author writes (and list it in AUTHORING_SELF_DOCS).";
1382
+ 'Set `namespace` on each CLI doc to the one that reads it: cli/commands for a command, cli/api for an API function or the output schema, error codes, and response types, and authoring for a file an author writes (and list it in AUTHORING_SELF_DOCS).';
1303
1383
 
1304
1384
  /** How the docs-tree check's problems are fixed. */
1305
1385
  const DOCS_TREE_FIX =
@@ -1381,8 +1461,7 @@ export async function checkDocsTree(_ctx, options = {}) {
1381
1461
  const id = 'docs-tree';
1382
1462
  const label = 'Docs tree';
1383
1463
  try {
1384
- const catalog =
1385
- /** @type {any} */ (_ctx)?.docsCatalog ?? builtinCatalog();
1464
+ const catalog = /** @type {any} */ (_ctx)?.docsCatalog ?? builtinCatalog();
1386
1465
  const tree = options.tree ?? (await projectTree(catalog, {fresh: true}));
1387
1466
  const problems = tree.diagnostics
1388
1467
  .filter(d => d.severity === 'error')
@@ -1397,7 +1476,9 @@ export async function checkDocsTree(_ctx, options = {}) {
1397
1476
  );
1398
1477
  for (const {source, doc} of (await loadCliSelfDocs()).loaded) {
1399
1478
  if (CLI_DOC_NAMESPACES[doc.namespace]?.reader !== 'tree') continue;
1400
- if (!placed.has(`@astryxdesign/cli\u0000${doc.type}\u0000${doc.name}`)) {
1479
+ if (
1480
+ !placed.has(`@astryxdesign/cli\u0000${doc.type}\u0000${doc.name}`)
1481
+ ) {
1401
1482
  problems.push(
1402
1483
  `${source} has namespace "${doc.namespace}", but no docs-tree namespace adopts it`,
1403
1484
  );
@@ -1450,7 +1531,6 @@ export async function checkDocsTree(_ctx, options = {}) {
1450
1531
  }
1451
1532
  }
1452
1533
 
1453
-
1454
1534
  /**
1455
1535
  * Every topic reads progressively, in every language it ships: it loads, its
1456
1536
  * section index and each of its sections fit in one read, and no contributed
@@ -1465,7 +1545,9 @@ export async function checkDocsProgressiveDisclosure(ctx) {
1465
1545
  /** @type {string[]} */
1466
1546
  const problems = [];
1467
1547
  if (ctx.docsCatalogError) {
1468
- problems.push(`The docs catalog could not be built: ${ctx.docsCatalogError}`);
1548
+ problems.push(
1549
+ `The docs catalog could not be built: ${ctx.docsCatalogError}`,
1550
+ );
1469
1551
  }
1470
1552
  for (const issue of ctx.docsCatalogIssues ?? []) {
1471
1553
  problems.push(`${issue.package ?? 'a contributed doc'}: ${issue.message}`);
@@ -1581,14 +1663,14 @@ export function checkCliCommand(ctx) {
1581
1663
  * @property {{cli: string, core: string | null, copy: string}} answeredBy
1582
1664
  * @property {string | null} latest the newest release known, or null
1583
1665
  * @property {Array<{path: string, state: import('../../foundation/agent-docs/computer-agent-text.mjs').ComputerAgentTextState}>} [agentText]
1584
- * each file of the computer-level agent text against this release's rendering
1666
+ * each file of the computer-level agent text against this release's rendering
1585
1667
  * @property {import('../../foundation/env/user-settings.mjs').ComputerAgentTextSettings} [agentTextSettings]
1586
1668
  * the person's choices about the computer-level agent text
1587
1669
  * @property {string | null} [agentTextCommand] the command the skill file
1588
1670
  * tells agents to run, or null
1589
1671
  * @property {string | null} [agentTextInstall] the install step the skill
1590
1672
  * file records for when that command is not found, or null
1591
- * @property {string} [cwd] the folder doctor runs in, for the printed fix
1673
+ * @property {string} [cwd] the folder doctor runs in, for the printed fix
1592
1674
  * @property {import('../../foundation/release/notices.mjs').GlobalBehind | null} [globalBehind]
1593
1675
  * the CLI installed for the computer, when it is older than the newest
1594
1676
  * release (outside a project)
@@ -1600,7 +1682,7 @@ export function checkCliCommand(ctx) {
1600
1682
  * the newest update notice that did not print, and why (diagnostic only)
1601
1683
  * @property {{pid: number | null, host: string | null, since: number} | null} [stateLock]
1602
1684
  * who holds the state file's lock now (diagnostic only)
1603
- * @property {Array<import('../../foundation/env/search-path.mjs').SearchPathCommand & {upgrade?: string | null, runner?: boolean}>} [searchPathCommands]
1685
+ * @property {Array<import('../../foundation/env/search-path.mjs').SearchPathCommand & {upgrade?: string | null, runner?: boolean}>} [searchPathCommands]
1604
1686
  * every `astryx` on the search path, with the command that upgrades it
1605
1687
  * the `astryx` commands on the search path, in order
1606
1688
  * @property {import('../../foundation/integrations/computer-integrations.mjs').ComputerIntegrationCandidate[]} [computerIntegrations]
@@ -1649,7 +1731,9 @@ export function checkNewestRelease(ctx) {
1649
1731
  skipNote(ctx.lastNoticeSkip),
1650
1732
  lockNote(ctx.stateLock),
1651
1733
  ].filter(Boolean);
1652
- return notes.length > 0 ? {...check, message: [check.message, ...notes].join(' ')} : check;
1734
+ return notes.length > 0
1735
+ ? {...check, message: [check.message, ...notes].join(' ')}
1736
+ : check;
1653
1737
  }
1654
1738
 
1655
1739
  /**
@@ -1660,7 +1744,10 @@ export function checkNewestRelease(ctx) {
1660
1744
  */
1661
1745
  function lockNote(lock) {
1662
1746
  if (!lock) return null;
1663
- const by = lock.pid == null ? '' : ` by process ${lock.pid}${lock.host ? ` on ${lock.host}` : ''}`;
1747
+ const by =
1748
+ lock.pid == null
1749
+ ? ''
1750
+ : ` by process ${lock.pid}${lock.host ? ` on ${lock.host}` : ''}`;
1664
1751
  return `The state file's lock is held${by} since ${new Date(lock.since).toISOString()}.`;
1665
1752
  }
1666
1753
 
@@ -1687,7 +1774,8 @@ function checkFailureNote(failed) {
1687
1774
  const SKIP_REASONS = {
1688
1775
  'already-shown': 'it had already printed that day',
1689
1776
  'no-newer': 'no newer release was known',
1690
- 'no-project': 'the folder belongs to no Astryx project, so no install command fits',
1777
+ 'no-project':
1778
+ 'the folder belongs to no Astryx project, so no install command fits',
1691
1779
  };
1692
1780
 
1693
1781
  /**
@@ -1698,7 +1786,9 @@ const SKIP_REASONS = {
1698
1786
  */
1699
1787
  function skipNote(skip) {
1700
1788
  if (!skip) return null;
1701
- const why = /** @type {Record<string, string>} */ (SKIP_REASONS)[skip.reason] ?? skip.reason;
1789
+ const why =
1790
+ /** @type {Record<string, string>} */ (SKIP_REASONS)[skip.reason] ??
1791
+ skip.reason;
1702
1792
  return `Last update notice skipped ${new Date(skip.at).toISOString()} (${skip.key}): ${why}.`;
1703
1793
  }
1704
1794
 
@@ -1710,7 +1800,11 @@ function skipNote(skip) {
1710
1800
  function newestReleaseCheck(ctx) {
1711
1801
  const {latest, globalBehind: behind, projectLag: lag} = ctx;
1712
1802
  const {cli} = ctx.answeredBy;
1713
- const base = {id: 'newest-release', label: 'Newest release', status: /** @type {const} */ ('info')};
1803
+ const base = {
1804
+ id: 'newest-release',
1805
+ label: 'Newest release',
1806
+ status: /** @type {const} */ ('info'),
1807
+ };
1714
1808
  if (behind) {
1715
1809
  return {
1716
1810
  ...base,
@@ -1728,16 +1822,24 @@ function newestReleaseCheck(ctx) {
1728
1822
  if (!latest) {
1729
1823
  return {
1730
1824
  ...base,
1731
- message: 'Update check: not run yet, so the newest release is not known here; it runs in the background once a day, after a command.',
1825
+ message:
1826
+ 'Update check: not run yet, so the newest release is not known here; it runs in the background once a day, after a command.',
1732
1827
  };
1733
1828
  }
1734
- if (isValidSemver(latest) && isValidSemver(cli) && semverCompare(latest, cli) > 0) {
1829
+ if (
1830
+ isValidSemver(latest) &&
1831
+ isValidSemver(cli) &&
1832
+ semverCompare(latest, cli) > 0
1833
+ ) {
1735
1834
  return {
1736
1835
  ...base,
1737
1836
  message: `Release ${latest} is newer than ${cli}, the release that answered.`,
1738
1837
  };
1739
1838
  }
1740
- return {...base, message: `No newer release is known: the newest is ${latest}.`};
1839
+ return {
1840
+ ...base,
1841
+ message: `No newer release is known: the newest is ${latest}.`,
1842
+ };
1741
1843
  }
1742
1844
 
1743
1845
  /**
@@ -1750,16 +1852,24 @@ function newestReleaseCheck(ctx) {
1750
1852
  export function checkCliOnPath(ctx) {
1751
1853
  const rows = ctx.searchPathCommands ?? [];
1752
1854
  const base = {id: 'cli-on-path', label: '`astryx` on the search path'};
1753
- const describe = (/** @type {import('../../foundation/env/search-path.mjs').SearchPathCommand} */ row) =>
1754
- row.astryx ? `${row.file} (CLI ${row.version ?? 'of unknown version'})` : `${row.file} (not the Astryx CLI)`;
1855
+ const describe = (
1856
+ /** @type {import('../../foundation/env/search-path.mjs').SearchPathCommand} */ row,
1857
+ ) =>
1858
+ row.astryx
1859
+ ? `${row.file} (CLI ${row.version ?? 'of unknown version'})`
1860
+ : `${row.file} (not the Astryx CLI)`;
1755
1861
  const others = rows.slice(1);
1756
- const othersNote = others.length > 0 ? ` Later on the search path: ${others.map(describe).join(', ')}.` : '';
1862
+ const othersNote =
1863
+ others.length > 0
1864
+ ? ` Later on the search path: ${others.map(describe).join(', ')}.`
1865
+ : '';
1757
1866
  const [first] = rows;
1758
1867
  if (!first) {
1759
1868
  return {
1760
1869
  ...base,
1761
1870
  status: 'info',
1762
- message: 'No `astryx` command on the search path, so `astryx` runs only through a package manager.',
1871
+ message:
1872
+ 'No `astryx` command on the search path, so `astryx` runs only through a package manager.',
1763
1873
  fix: 'To run the CLI in any folder, install it for the computer: `npm i -g @astryxdesign/cli`.',
1764
1874
  };
1765
1875
  }
@@ -1796,8 +1906,11 @@ export function checkCliOnPath(ctx) {
1796
1906
  * @returns {DoctorCheck}
1797
1907
  */
1798
1908
  export function checkComputerAgentText(ctx) {
1799
- const state = ctx.agentTextSettings && ctx.agentText ? null : computerAgentTextState();
1800
- const settings = ctx.agentTextSettings ?? /** @type {NonNullable<typeof state>} */ (state).settings;
1909
+ const state =
1910
+ ctx.agentTextSettings && ctx.agentText ? null : computerAgentTextState();
1911
+ const settings =
1912
+ ctx.agentTextSettings ??
1913
+ /** @type {NonNullable<typeof state>} */ (state).settings;
1801
1914
  const base = {
1802
1915
  id: 'computer-agent-text',
1803
1916
  label: 'Computer-level agent text',
@@ -1806,26 +1919,41 @@ export function checkComputerAgentText(ctx) {
1806
1919
  if (!settings.install) {
1807
1920
  return {
1808
1921
  ...base,
1809
- message: 'The computer-level agent text is turned off (`astryx init --user --remove-agents`), so the CLI installed for the computer does not write it.',
1922
+ message:
1923
+ 'The computer-level agent text is turned off (`astryx init --user --remove-agents`), so the CLI installed for the computer does not write it.',
1810
1924
  fix: `To turn it back on, run \`${formatCliCommand('astryx init --user', ctx.cwd)}\`.`,
1811
1925
  };
1812
1926
  }
1813
- const entries = ctx.agentText ?? /** @type {NonNullable<typeof state>} */ (state).entries;
1814
- const command = ctx.agentText ? (ctx.agentTextCommand ?? null) : /** @type {NonNullable<typeof state>} */ (state).command;
1815
- const recorded = (ctx.agentText ? ctx.agentTextInstall : /** @type {NonNullable<typeof state>} */ (state).install) ?? null;
1927
+ const entries =
1928
+ ctx.agentText ?? /** @type {NonNullable<typeof state>} */ (state).entries;
1929
+ const command = ctx.agentText
1930
+ ? (ctx.agentTextCommand ?? null)
1931
+ : /** @type {NonNullable<typeof state>} */ (state).command;
1932
+ const recorded =
1933
+ (ctx.agentText
1934
+ ? ctx.agentTextInstall
1935
+ : /** @type {NonNullable<typeof state>} */ (state).install) ?? null;
1816
1936
  const fix = `Run \`${formatCliCommand('astryx init --user', ctx.cwd)}\`.`;
1817
1937
  const [file, ...links] = entries;
1818
1938
  const home = (/** @type {{path: string}} */ e) => displayHomePath(e.path);
1819
1939
  const linked = links.filter(l => l.state === 'linked').map(home);
1820
1940
  const copied = links.filter(l => l.state === 'current').map(home);
1821
- const skipped = links.filter(l => l.state === 'foreign' || l.state === 'edited').map(home);
1941
+ const skipped = links
1942
+ .filter(l => l.state === 'foreign' || l.state === 'edited')
1943
+ .map(home);
1822
1944
  const dangling = links.filter(l => l.state === 'dangling').map(home);
1823
- const missing = links.filter(l => l.state === 'absent' || l.state === 'differs').map(home);
1945
+ const missing = links
1946
+ .filter(l => l.state === 'absent' || l.state === 'differs')
1947
+ .map(home);
1824
1948
  const linkNote =
1825
1949
  (linked.length > 0 ? ` Linked into: ${linked.join(', ')}.` : '') +
1826
1950
  (copied.length > 0 ? ` Copied into: ${copied.join(', ')}.` : '') +
1827
- (skipped.length > 0 ? ` Not linked, because something Astryx did not create is there: ${skipped.join(', ')}.` : '') +
1828
- (dangling.length > 0 ? ` Linked, but the skill folder the link points to is not there, so agents reading it see nothing (as in a container that mounts only that folder): ${dangling.join(', ')}.` : '') +
1951
+ (skipped.length > 0
1952
+ ? ` Not linked, because something Astryx did not create is there: ${skipped.join(', ')}.`
1953
+ : '') +
1954
+ (dangling.length > 0
1955
+ ? ` Linked, but the skill folder the link points to is not there, so agents reading it see nothing (as in a container that mounts only that folder): ${dangling.join(', ')}.`
1956
+ : '') +
1829
1957
  (missing.length > 0 ? ` Not linked yet: ${missing.join(', ')}.` : '');
1830
1958
  if (file.state === 'foreign' || file.state === 'edited') {
1831
1959
  return {
@@ -1878,15 +2006,20 @@ export function checkComputerAgentText(ctx) {
1878
2006
  export function checkComputerIntegrations(ctx) {
1879
2007
  const found = ctx.computerIntegrations ?? [];
1880
2008
  const loaded = new Set(ctx.loadedComputerIntegrations ?? []);
1881
- const base = {id: 'computer-integrations', label: 'Integrations installed for this computer'};
2009
+ const base = {
2010
+ id: 'computer-integrations',
2011
+ label: 'Integrations installed for this computer',
2012
+ };
1882
2013
  if (found.length === 0) {
1883
2014
  return {
1884
2015
  ...base,
1885
2016
  status: 'info',
1886
- message: 'No integration is installed beside the CLI installed for this computer.',
2017
+ message:
2018
+ 'No integration is installed beside the CLI installed for this computer.',
1887
2019
  };
1888
2020
  }
1889
- const label = (/** @type {{name: string, version?: string}} */ c) => (c.version ? `${c.name}@${c.version}` : c.name);
2021
+ const label = (/** @type {{name: string, version?: string}} */ c) =>
2022
+ c.version ? `${c.name}@${c.version}` : c.name;
1890
2023
  const own = new Set(ctx.folderIntegrations ?? []);
1891
2024
  const shadowed = found.filter(c => own.has(c.name));
1892
2025
  const rest = found.filter(c => !own.has(c.name));
@@ -1894,14 +2027,24 @@ export function checkComputerIntegrations(ctx) {
1894
2027
  const skipped = rest.filter(c => c.incompatible);
1895
2028
  const failed = rest.filter(c => !c.incompatible && !loaded.has(c.name));
1896
2029
  const notes = [
1897
- used.length > 0 ? `Used outside a project: ${used.map(label).join(', ')}.` : '',
1898
- ...shadowed.map(c => `${label(c)} is not used here: this folder loads its own copy of ${c.name}.`),
2030
+ used.length > 0
2031
+ ? `Used outside a project: ${used.map(label).join(', ')}.`
2032
+ : '',
2033
+ ...shadowed.map(
2034
+ c =>
2035
+ `${label(c)} is not used here: this folder loads its own copy of ${c.name}.`,
2036
+ ),
1899
2037
  ...skipped.map(c => `Skipped ${label(c)}: ${c.incompatible}.`),
1900
2038
  ...failed.map(c => `${label(c)} could not be loaded.`),
1901
2039
  ].filter(Boolean);
1902
2040
  const fixes = [
1903
- ...skipped.map(c => `install a release of ${c.name} that supports this CLI`),
1904
- ...failed.map(c => `run \`${formatCliCommand(`astryx doctor integration validate ${c.name}`, ctx.cwd)}\``),
2041
+ ...skipped.map(
2042
+ c => `install a release of ${c.name} that supports this CLI`,
2043
+ ),
2044
+ ...failed.map(
2045
+ c =>
2046
+ `run \`${formatCliCommand(`astryx doctor integration validate ${c.name}`, ctx.cwd)}\``,
2047
+ ),
1905
2048
  ];
1906
2049
  return {
1907
2050
  ...base,
@@ -1922,18 +2065,33 @@ export function checkComputerIntegrations(ctx) {
1922
2065
  * fix points at the project's folder
1923
2066
  * @returns {DoctorCheck}
1924
2067
  */
1925
- export function checkComputerIntegrationsAvailable({installed, candidates, install, elsewhere = false}) {
2068
+ export function checkComputerIntegrationsAvailable({
2069
+ installed,
2070
+ candidates,
2071
+ install,
2072
+ elsewhere = false,
2073
+ }) {
1926
2074
  const available = candidates.filter(c => !installed.has(c.name));
1927
- const base = {id: 'computer-integrations', label: 'Integrations installed for this computer', status: /** @type {const} */ ('info')};
2075
+ const base = {
2076
+ id: 'computer-integrations',
2077
+ label: 'Integrations installed for this computer',
2078
+ status: /** @type {const} */ ('info'),
2079
+ };
1928
2080
  if (available.length === 0) {
1929
- return {...base, message: 'No integration installed for this computer is missing from this project.'};
2081
+ return {
2082
+ ...base,
2083
+ message:
2084
+ 'No integration installed for this computer is missing from this project.',
2085
+ };
1930
2086
  }
1931
2087
  const add = install;
1932
2088
  return {
1933
2089
  ...base,
1934
2090
  message: `Installed for this computer but not in this project, so not used here: ${available.map(c => c.name).join(', ')}.`,
1935
2091
  ...(add
1936
- ? {fix: `To use one in this project, run ${available.map(c => `\`${add(c.name)}\``).join(' or ')}.`}
2092
+ ? {
2093
+ fix: `To use one in this project, run ${available.map(c => `\`${add(c.name)}\``).join(' or ')}.`,
2094
+ }
1937
2095
  : elsewhere
1938
2096
  ? {fix: NO_PROJECT_FIX}
1939
2097
  : {}),
@@ -1952,9 +2110,9 @@ export const COMPUTER_CHECKS = [
1952
2110
  coreDir: null,
1953
2111
  configPath: null,
1954
2112
  }),
1955
- checkAnsweringRelease,
2113
+ checkAnsweringRelease,
1956
2114
  checkNewestRelease,
1957
- checkCliOnPath,
2115
+ checkCliOnPath,
1958
2116
  checkComputerIntegrations,
1959
2117
  checkComputerAgentText,
1960
2118
  ];
@@ -2016,7 +2174,10 @@ export async function runChecks(options = {}) {
2016
2174
  const cwd = rest.cwd ?? process.cwd();
2017
2175
  const checks = [
2018
2176
  ...report.checks,
2019
- ...checkBuilt(built, {cwd, packages: builtIntegrationPackages(built, cwd)}),
2177
+ ...checkBuilt(built, {
2178
+ cwd,
2179
+ packages: builtIntegrationPackages(built, cwd),
2180
+ }),
2020
2181
  ];
2021
2182
  const summary = {pass: 0, warn: 0, fail: 0, info: 0};
2022
2183
  for (const c of checks) summary[c.status] += 1;
@@ -2222,9 +2383,18 @@ export async function runChecks(options = {}) {
2222
2383
  }),
2223
2384
  );
2224
2385
  {
2225
- const loadedNames = (project?.loadedIntegrations ?? []).map((/** @type {{name: string}} */ i) => i.name);
2386
+ const loadedNames = (project?.loadedIntegrations ?? []).map(
2387
+ (/** @type {{name: string}} */ i) => i.name,
2388
+ );
2226
2389
  const candidates = await computerIntegrationsToAdd(cwd, loadedNames);
2227
- checks.push(checkComputerIntegrationsAvailable({installed: new Set(loadedNames), candidates, install: installCommandFor(cwd), elsewhere: insideProjectElsewhere(cwd)}));
2390
+ checks.push(
2391
+ checkComputerIntegrationsAvailable({
2392
+ installed: new Set(loadedNames),
2393
+ candidates,
2394
+ install: installCommandFor(cwd),
2395
+ elsewhere: insideProjectElsewhere(cwd),
2396
+ }),
2397
+ );
2228
2398
  }
2229
2399
  checks.push(await checkAuthoringDocs(ctx));
2230
2400
  checks.push(await checkCliDocs(ctx));