@astryxdesign/cli 0.6.4-canary.fdc76dc → 0.6.5-canary.00f1ed9
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/CHANGELOG.md +56 -0
- package/README.md +14 -13
- package/api/build/_adapter.d.mts +36 -2
- package/api/build/_adapter.mjs +41 -10
- package/api/build/build.doc.mjs +2 -2
- package/api/build/build.test.mjs +38 -2
- package/api/build/kit/kit.mjs +65 -21
- package/api/build/kit/rank.d.mts +24 -8
- package/api/build/kit/rank.mjs +277 -97
- package/api/build/kit/rank.test.mjs +231 -48
- package/api/docs/docs.test.mjs +279 -0
- package/api/docs/integration-tree.test.mjs +572 -0
- package/api/docs/integrationDocs.test.mjs +336 -0
- package/api/error.d.mts +22 -0
- package/api/error.mjs +42 -0
- package/api/integration/add-contribution.d.mts +2 -1
- package/api/integration/add-contribution.mjs +7 -3
- package/api/integration/add-contribution.test.mjs +3 -3
- package/api/integration/add-theme.mjs +246 -24
- package/api/integration/add-theme.test.mjs +214 -1
- package/api/integration/authoring-checks.mjs +10 -7
- package/api/integration/authoring-checks.type.d.mts +8 -0
- package/api/integration/authoring-checks.type.mjs +7 -3
- package/api/integration/integration-authoring.type.d.mts +4 -1
- package/api/integration/integration-authoring.type.mjs +6 -1
- package/api/integration/integrationAdd.doc.mjs +6 -0
- package/api/integration/integrationAddTheme.doc.mjs +10 -0
- package/api/integration/integrationComponentConflicts.doc.mjs +1 -1
- package/api/integration/integrationDocConflicts.doc.mjs +1 -1
- package/api/integration/integrationTemplateConflicts.doc.mjs +1 -1
- package/api/integration/pack-check.mjs +12 -3
- package/api/integration/pack-check.test.mjs +52 -3
- package/api/integration/validate-integration.d.mts +4 -2
- package/api/integration/validate-integration.mjs +7 -2
- package/api/integration/validate-integration.test.mjs +55 -0
- package/api/integration/validate-integration.type.d.mts +5 -0
- package/api/integration/validate-integration.type.mjs +5 -1
- package/api/integration/validateIntegration.doc.mjs +1 -1
- package/api/layout/expand/expand.mjs +12 -7
- package/api/layout/expand/expand.receipt.test.mjs +74 -0
- package/api/layout/layout.type.d.mts +1 -0
- package/api/layout/layout.type.mjs +1 -0
- package/api/layout/layoutExpand.doc.mjs +1 -1
- package/api/search/search.d.mts +24 -0
- package/api/search/search.mjs +71 -1
- package/api/search/search.test.mjs +710 -0
- package/api/swizzle/copy/copy.mjs +66 -3
- package/api/swizzle/swizzle.doc.mjs +4 -0
- package/api/template/copy/copy.mjs +14 -8
- package/api/template/copy/copy.receipt.test.mjs +77 -0
- package/api/template/show/show.mjs +15 -4
- package/api/template/show/show.test.mjs +76 -0
- package/api/template/template-integration.test.mjs +14 -0
- package/api/template/template.d.mts +1 -1
- package/api/template/template.doc.mjs +6 -2
- package/api/template/template.mjs +1 -0
- package/api/template/template.type.d.mts +2 -0
- package/api/template/template.type.mjs +2 -0
- package/api/theme/build/build.d.mts +24 -0
- package/api/theme/build/build.mjs +235 -8
- package/api/theme/build/build.project-core.test.mjs +165 -0
- package/api/upgrade/run/run.mjs +20 -3
- package/api/upgrade/run/run.test.mjs +45 -1
- package/api/upgrade/upgrade.doc.mjs +1 -1
- package/api/upgrade/upgrade.type.d.mts +1 -0
- package/api/upgrade/upgrade.type.mjs +1 -0
- package/assets/docs/icons.doc.mjs +1 -0
- package/assets/docs/theme.doc.mjs +1 -1
- package/assets/docs/tree/add-a-topic.doc.mjs +1 -1
- package/assets/docs/tree/check-your-docs.doc.mjs +2 -2
- package/assets/docs/tree/checks.doc.mjs +1 -1
- package/assets/docs/tree/define-the-theme.doc.mjs +1 -1
- package/assets/docs/tree/sections-and-placement.doc.mjs +1 -1
- package/assets/docs/tree/template-doc-overview.doc.mjs +13 -1
- package/assets/docs/tree/troubleshooting.doc.mjs +9 -5
- package/assets/docs/tree/versioning.doc.mjs +7 -6
- package/assets/templates/pages/ai-chat/template.doc.mjs +16 -1
- package/assets/templates/pages/ai-chat-landing/template.doc.mjs +9 -1
- package/assets/templates/pages/blank/template.doc.mjs +3 -1
- package/assets/templates/pages/canvas-editor/template.doc.mjs +9 -1
- package/assets/templates/pages/centered-hero/template.doc.mjs +3 -1
- package/assets/templates/pages/checkout-wizard/template.doc.mjs +1 -0
- package/assets/templates/pages/classic-gallery/template.doc.mjs +3 -1
- package/assets/templates/pages/contact-form/template.doc.mjs +16 -1
- package/assets/templates/pages/dashboard/template.doc.mjs +15 -1
- package/assets/templates/pages/dashboard-alert-rail/template.doc.mjs +23 -1
- package/assets/templates/pages/dashboard-cohort-funnel/template.doc.mjs +10 -1
- package/assets/templates/pages/dashboard-comparison/template.doc.mjs +9 -1
- package/assets/templates/pages/dashboard-composition/template.doc.mjs +10 -1
- package/assets/templates/pages/dashboard-progress/template.doc.mjs +17 -1
- package/assets/templates/pages/dashboard-scorecard/template.doc.mjs +2 -1
- package/assets/templates/pages/detail-page/template.doc.mjs +8 -0
- package/assets/templates/pages/documentation/template.doc.mjs +10 -1
- package/assets/templates/pages/documentation-design/template.doc.mjs +10 -1
- package/assets/templates/pages/documentation-technical/template.doc.mjs +10 -1
- package/assets/templates/pages/editor/template.doc.mjs +9 -1
- package/assets/templates/pages/file-explorer/template.doc.mjs +3 -1
- package/assets/templates/pages/form-two-column/template.doc.mjs +17 -1
- package/assets/templates/pages/form-wizard/template.doc.mjs +14 -1
- package/assets/templates/pages/form-wizard-dialog/template.doc.mjs +1 -0
- package/assets/templates/pages/gallery-hero/template.doc.mjs +10 -1
- package/assets/templates/pages/ide/template.doc.mjs +9 -1
- package/assets/templates/pages/incident-console/template.doc.mjs +10 -1
- package/assets/templates/pages/kanban-board/template.doc.mjs +11 -1
- package/assets/templates/pages/library/template.doc.mjs +15 -1
- package/assets/templates/pages/login/template.doc.mjs +10 -1
- package/assets/templates/pages/login-card/template.doc.mjs +11 -1
- package/assets/templates/pages/login-split/template.doc.mjs +10 -1
- package/assets/templates/pages/login-sso/template.doc.mjs +10 -1
- package/assets/templates/pages/messaging-shell/template.doc.mjs +11 -1
- package/assets/templates/pages/mixed-gallery/template.doc.mjs +10 -1
- package/assets/templates/pages/payment-form/template.doc.mjs +3 -1
- package/assets/templates/pages/product-detail/template.doc.mjs +9 -1
- package/assets/templates/pages/product-gallery/template.doc.mjs +10 -1
- package/assets/templates/pages/settings/template.doc.mjs +3 -1
- package/assets/templates/pages/settings-dialog/template.doc.mjs +9 -1
- package/assets/templates/pages/settings-sidebar/template.doc.mjs +9 -1
- package/assets/templates/pages/shell-nav/template.doc.mjs +10 -1
- package/assets/templates/pages/shell-side-nav/template.doc.mjs +14 -1
- package/assets/templates/pages/shell-top-nav/template.doc.mjs +11 -1
- package/assets/templates/pages/side-gallery/template.doc.mjs +3 -1
- package/assets/templates/pages/table/template.doc.mjs +11 -1
- package/assets/templates/pages/table-filter/template.doc.mjs +21 -1
- package/assets/templates/pages/table-grouped/template.doc.mjs +15 -1
- package/assets/templates/pages/table-inbox/template.doc.mjs +18 -6
- package/assets/templates/pages/table-page/template.doc.mjs +20 -1
- package/assets/templates/pages/table-tree/template.doc.mjs +14 -1
- package/assets/templates/pages/theme-showcase/template.doc.mjs +10 -1
- package/assets/templates/pages/work-item-detail/template.doc.mjs +10 -0
- package/assets/templates/themes/butter/icons.tsx +2 -0
- package/assets/templates/themes/chocolate/icons.tsx +2 -0
- package/assets/templates/themes/gothic/icons.tsx +2 -0
- package/assets/templates/themes/matcha/icons.tsx +2 -0
- package/assets/templates/themes/neutral/icons.tsx +2 -0
- package/assets/templates/themes/stone/icons.tsx +2 -0
- package/assets/templates/themes/y2k/icons.tsx +2 -0
- package/authoring/doctypes/template/parse.d.mts +2 -0
- package/authoring/doctypes/template/parse.mjs +1 -0
- package/authoring/doctypes/template/parse.test.mjs +21 -0
- package/authoring/doctypes/template/template.doc.mjs +6 -0
- package/authoring/doctypes/template/type.ts +10 -0
- package/clients/cli/commands/docs.test.mjs +383 -0
- package/clients/cli/commands/doctor.mjs +4 -4
- package/clients/cli/commands/integration-add.doc.mjs +10 -0
- package/clients/cli/commands/integration-authoring.test.mjs +12 -2
- package/clients/cli/commands/integration.mjs +3 -1
- package/clients/cli/commands/layout-expand.doc.mjs +3 -1
- package/clients/cli/commands/layout.expand-receipt.test.mjs +94 -0
- package/clients/cli/commands/layout.mjs +16 -0
- package/clients/cli/commands/template.copy-receipt.test.mjs +60 -0
- package/clients/cli/commands/template.mjs +19 -5
- package/clients/cli/commands/template.show-media.test.mjs +62 -0
- package/clients/cli/commands/upgrade.ascii-output.test.mjs +14 -0
- package/clients/cli/commands/write-failure.test.mjs +175 -0
- package/clients/cli/lib/manifest.mjs +44 -165
- package/clients/cli/lib/manifest.test.mjs +98 -7
- package/foundation/agent-docs/agent-docs.test.mjs +1160 -0
- package/foundation/discovery/template-adapter.d.mts +14 -0
- package/foundation/discovery/template-adapter.fixture-refs.test.mjs +37 -1
- package/foundation/discovery/template-adapter.mjs +21 -1
- package/foundation/doc-compiler/doc-loads.test.mjs +2 -2
- package/foundation/doc-compiler/tree.test.mjs +649 -0
- package/foundation/integrations/cli-requirement.d.mts +57 -13
- package/foundation/integrations/cli-requirement.mjs +84 -22
- package/foundation/integrations/cli-requirement.test.mjs +135 -8
- package/foundation/response/response-types.doc.d.mts +5 -5
- package/foundation/response/response-types.doc.mjs +13 -13
- package/package.json +9 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,61 @@
|
|
|
1
1
|
# @xds/cli
|
|
2
2
|
|
|
3
|
+
# 0.6.5
|
|
4
|
+
|
|
5
|
+
#### New Features
|
|
6
|
+
|
|
7
|
+
- `astryx component` accepts several exact selectors in one call. The JSON response keeps one ordered row per selector, including missing and ambiguous components, and text mode prints every row before exiting nonzero when any lookup fails. The `component()` API accepts selector arrays and always returns `component.batch` for an array, including empty and one-item arrays. Batches accept up to 100 selectors and reject larger arrays before lookup. The public API also exports shared `BatchResponse` and `BatchRow` types for typed receipts.
|
|
8
|
+
- `astryx discover` browses integrations: the ones a project has and, through discover sources, the ones it could add, with every version and what each one adds. It searches every kind of item and filters with `--type`, `--installed`, `--available`, and `--limit`. A project sets a source as `discover` in `astryx.config`, and an integration exports one as a `discover` named export. Discover only reads: it prints the command that adds a package and never runs it. Existing `--json` fields keep their meaning. A free-text query now always lists its matches, even an exact component name, and `astryx discover <package>/<Name>` opens one.
|
|
9
|
+
- `astryx integration verify` is the new name of `astryx integration pack --check`.
|
|
10
|
+
The check you run before publishing an integration now has a name that says what it does. `astryx integration verify` packs the package with npm, installs the tarball into a temporary app, and checks that the app sees the same components, templates, themes, docs, and codemods. It takes no flags. `astryx integration pack --check` still works as a deprecated alias: it runs the same check with the same output, JSON, and exit codes, prints a note that names `integration verify`, and shows as deprecated in help. It will be removed in a later release. The `integrationPackCheck()` API and its `integration.pack-check` JSON response do not change. With `--json`, a command group given an unknown subcommand now reports `ERR_UNKNOWN_SUBCOMMAND` and lists its subcommands, where it used to say JSON output is not supported.
|
|
11
|
+
|
|
12
|
+
#### Fixes
|
|
13
|
+
|
|
14
|
+
- Fix the CLI's topic docs and how they print.
|
|
15
|
+
- `astryx doctor` no longer reports an integration it could not check as absent or complete (#6619)
|
|
16
|
+
- `upgrade`'s `filesChanged` counts files, not (codemod, file) pairs (#6622)
|
|
17
|
+
One source file that four codemods each changed was reported as four files changed, so `filesChanged` matched `transformsApplied` and the documented meaning, "Total files changed", was not true. The human summary said the same thing: "Found 4 changes across 4 files" for one file.
|
|
18
|
+
|
|
19
|
+
`filesChanged` is now the count of distinct files. `transformsApplied` is unchanged: a code or config codemod counts once for each file it changed, and a project codemod counts once. A file that both a core codemod and an integration codemod changed counts once in `filesChanged`.
|
|
20
|
+
- A parse error prints the Astryx error format in text mode.
|
|
21
|
+
`astryx theme list --lang zh-Hans` printed Commander's own line — `error: option '--lang <locale>' argument 'zh-Hans' is invalid…` — while every other CLI error prints `Error: …`. `--json` was already correct (`ERR_INVALID_LANG`), so the two modes agreed only on the exit code.
|
|
22
|
+
|
|
23
|
+
Commander writes that line before any Astryx code runs, so the JSON shim — the one place that already sees every parse failure — now suppresses it and writes the Astryx line itself, from the same message, for both modes. Every parse failure is covered: unknown option, unknown command, missing argument, and an invalid value for a global option. `--help` and `--version` are untouched and still exit 0.
|
|
24
|
+
- The CLI reference now matches what the commands do. Every `--help` ends with the command's examples and a `More:` line that names its full docs page. Function docs show each parameter's default, mark required parameters, list the error codes each function throws, and use examples that run. The response-type list adds `help`, `version`, and `upgrade.registry`, and `astryx manifest` now lists `upgrade.registry` for `upgrade`. The `--zh`, `--dense`, `--lang`, and `--detail` descriptions name the commands they change, and command summaries say when to use each command. When `astryx template` refuses to overwrite a file, it now says to re-run with `--overwrite` (or `-f`). The `upgrade` command page (`astryx docs cli/commands/upgrade`) now explains which files codemods never edit, what happens when one of them needs a change, and how to regenerate it.
|
|
25
|
+
- `astryx integration pack --check` now checks the tarball when a `prepack`, `prepare`, or `postpack` script prints to stdout. Before, any lifecycle output made the check fail with "npm pack produced unparseable JSON output" before it looked at the tarball. A failing lifecycle script still fails the check, and its output stays in the `pack_failed` message.
|
|
26
|
+
- `astryx doctor integration docs` fails when a namespace doc or a placement fails, as its help says.
|
|
27
|
+
Such a failure hides the doc from the docs tree, so it now exits 1 with an `invalid_doc_graph` error instead of a warning. A link that names no doc still only warns, since it prints as written. `doctor integration docs` and `doctor integration components` also no longer print an `[ok]` line after a check that failed.
|
|
28
|
+
|
|
29
|
+
A mistyped subcommand under `doctor` now fails and lists the subcommands the group has: `astryx doctor integrations` used to run the project checks, and `astryx doctor integration bogus` exited 0 in text though it exited 1 with `--json`.
|
|
30
|
+
- A package that ships a theme, or a doc section with an `id`, now declares the CLI that can read it.
|
|
31
|
+
A stable CLI before 0.7.0 rejects both: it cannot read the typed theme descriptors that `astryx integration add theme` writes, and it rejects a section `id`. Either way it hides the package's themes or doc topics with no warning. `astryx integration add theme` now adds `"@astryxdesign/cli": ">=0.7.0"` to `peerDependencies`, marked optional, and `astryx integration verify` fails with `themes_need_cli` or `section_ids_need_cli` when a package needs that peer range and does not declare it.
|
|
32
|
+
- `astryx integration verify` resolves every public import in the packed package, not in your source folder.
|
|
33
|
+
Before, its temporary app resolved your package's own name through the source `package.json`, so an `exports` target left out of the tarball still passed. It now fails with `component_export_missing`, as an app that installs the tarball would.
|
|
34
|
+
- `astryx theme add` and `astryx theme build` now undo a failed write completely. Before, when one file failed to write after others were written, the written files kept their new content. Now every replaced file gets its previous content back, every new file is removed, and the error names any file that could not be restored. Both commands also refuse to replace a destination that is a symbolic link. (#6852)
|
|
35
|
+
|
|
36
|
+
#### Other Changes
|
|
37
|
+
|
|
38
|
+
- A code block's label now prints above the block instead of as a `// label` line inside it, so copied bash, CSS, JSON, and HTML stay valid. Table cells escape `|`, so a union type stays in one column.
|
|
39
|
+
- `astryx search dark mode` searches for both words; it used to drop every word after the first. A result that matches every word of a query, one of them by name or keyword, now outranks one that matches only some, and a section whose title or heading holds the whole query ranks near the top. Topics can declare search `keywords`, now a documented ReferenceDoc field, and a namespace's `keywords` now count too. A query keeps its phrase when common words such as `make`, `build`, or `an` drop out, so `astryx search make an integration` finds the integration guides, a plural of a doc's name matches it, one step below the exact name, and a component's name typed as words, such as `command palette`, finds the component. Outside an app, where `@astryxdesign/core` is not installed, `astryx search` searches the docs instead of failing, and says so; `--type component`, `hook`, or `template` still needs Core.
|
|
40
|
+
- Snippets that failed when copied now work: StyleX token imports, the `fr-FR.json` locale path, Tailwind `rounded-lg`, `--color-background-muted`, icon and color values, and the Cursor rule path.
|
|
41
|
+
- Claims that did not match the code are corrected: the 30 shipped locales and how RTL mirroring works, what `astryx init` writes, `--detail brief` for a shorter read, the Neutral and Matcha fonts, the components that need anchor positioning, `gap` steps, Card's radius, and the Next.js StyleX example. The deprecated bare classes are still emitted and will be removed in a later release.
|
|
42
|
+
- `astryx docs tokens` lists all 258 tokens, adding the data visualization and syntax groups, and shows both halves of every `light-dark()` value.
|
|
43
|
+
- Long sections are split, vague titles renamed, and the `--dense` and Chinese versions no longer drop blocks. Eleven long section keys are shorter, such as `astryx docs styling stylex-setup`, and every old key still resolves. An integration section that extends a Core topic by a section's old title still replaces that section.
|
|
44
|
+
- `astryx integration add codemod --to` help says it takes the Core version whose upgrade runs the codemod.
|
|
45
|
+
- The agent block that `astryx init` writes now says `upgrade --from <old version> --apply`; `upgrade --apply` alone stops with "Missing required --from".
|
|
46
|
+
- The contributor-only sections, on adding a semantic icon and on strings and text direction inside components, moved to CONTRIBUTING.md.
|
|
47
|
+
- An installed dependency whose `astryx.integration.*` manifest cannot be loaded is still kept out of the loaded set, but `implicit-integrations` now names it and says it contributes nothing. Before, doctor said that no installed dependency ships a manifest. The check stays informational, and `astryx doctor integration validate <package>` gives the details.
|
|
48
|
+
- `implicit-integrations` lists only the roots that exist on disk. A package whose declared roots are missing is reported as contributing nothing, with the missing roots named. Before, it listed every root the manifest declared.
|
|
49
|
+
- `provider-identity` says how many loaded integrations it could not read, instead of counting only the readable ones.
|
|
50
|
+
|
|
51
|
+
#### Contributors
|
|
52
|
+
|
|
53
|
+
Thanks to everyone who contributed to this release:
|
|
54
|
+
|
|
55
|
+
- @josephfarina
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
3
59
|
# 0.6.4
|
|
4
60
|
|
|
5
61
|
#### New Features
|
package/README.md
CHANGED
|
@@ -303,11 +303,12 @@ Shape:
|
|
|
303
303
|
```
|
|
304
304
|
|
|
305
305
|
The manifest is **derived from Commander metadata** (commands, arguments, options)
|
|
306
|
-
so it can't drift from the real command definitions. The
|
|
307
|
-
|
|
308
|
-
the
|
|
309
|
-
|
|
310
|
-
|
|
306
|
+
so it can't drift from the real command definitions. The facts Commander doesn't
|
|
307
|
+
track come from each command's docs: examples from its CommandDoc, and emitted
|
|
308
|
+
response types from the returns of the FunctionDoc it wraps (plus the few
|
|
309
|
+
envelopes the CLI layer builds itself). `--json` support comes from the
|
|
310
|
+
`JSON_SUPPORTED` allowlist. Drift tests (`manifest.test.mjs`) fail CI when a
|
|
311
|
+
command is added without describing it.
|
|
311
312
|
|
|
312
313
|
**Backwards-compat:** the bare `astryx --json` envelope keeps `type: "help"` and its
|
|
313
314
|
original shallow fields (`name`, `version`, `commands` as a `string[]` of names,
|
|
@@ -450,9 +451,9 @@ Every response has a `type` discriminant. The full set is below (generated from
|
|
|
450
451
|
| `gap-report.categories` | The fixed gap category values and human-readable labels. |
|
|
451
452
|
| `gap-report.file` | An aggregate receipt: overall status, the selected package, issuesUrl (or null), deliveries in handler order, each {handlerType: project \| integration \| fallback, handler, audience, status, url, message}, and filedCount/routedOnlyCount totals. |
|
|
452
453
|
| `template.list` | The effective discovered TemplateListEntry[] for pages and blocks. A winning replacement entry includes optional `replaces`, naming the Core id omitted from the default list. |
|
|
453
|
-
| `template.show` | The resolved template's
|
|
454
|
+
| `template.show` | The resolved template's source, exactly as a copy writes it, plus its description, kind, the component names it composes, and demoMediaReplaced (how many Astryx demo media references were replaced with placeholders for you to swap for your own media). |
|
|
454
455
|
| `template.skeleton` | A layout skeleton (structural tags with spatial annotations) plus the template's description and the components it composes. |
|
|
455
|
-
| `template.copy` | A scaffold receipt: template id, output directory, written file name, and
|
|
456
|
+
| `template.copy` | A scaffold receipt: template id, output directory, written file name, file count, and demoMediaReplaced (how many Astryx demo media references were replaced with placeholders for you to swap for your own media). |
|
|
456
457
|
| `template.cdn` | A write receipt for the no-build-step CDN starter page: the path (relative to cwd), the Astryx version every CDN URL was pinned to, whether it was written, and the reason it was not. `exists` when a file was already there, which is a success. |
|
|
457
458
|
| `hook.list` | The hook catalog grouped by category: `detail` (the level: names \| compact \| full) and `components`, the grouped map of hook names, brief entries, or a full HookDoc per entry. |
|
|
458
459
|
| `hook.detail` | One hook's full authored HookDoc. |
|
|
@@ -468,18 +469,18 @@ Every response has a `type` discriminant. The full set is below (generated from
|
|
|
468
469
|
| `upgrade.list` | Every available codemod, oldest→newest, as {name, title, version, optional}; returned for --list without running anything. |
|
|
469
470
|
| `upgrade.registry` | The copied-composition receipt for --registry: applied, ok, the counts (found, current, wouldUpdate, updated, wouldMerge, merged, wouldRefreshReceipt, receiptsRefreshed, conflicts, missing, invalid, failed), and items. |
|
|
470
471
|
| `upgrade.status` | A short-circuit outcome with no codemods run (up_to_date, no_codemods, or config_fixable), each carrying the agent-docs summary. |
|
|
471
|
-
| `upgrade.run` | The run receipt: from/to versions, codemod count, integrations processed, the agent-docs summary, and (apply mode) filesChanged, transformsApplied, and per-codemod errors.
|
|
472
|
+
| `upgrade.run` | The run receipt: from/to versions, codemod count, integrations processed, the agent-docs summary, sourcePathFound (false when the resolved source directory does not exist, so no source file was read), and (apply mode) filesChanged, transformsApplied, and per-codemod errors. |
|
|
472
473
|
| `manifest` | The CLI capability manifest: name, version, apiVersion, description, globalOptions, commands (each name, description, arguments, options, json, aliases?, responseTypes?, examples?, exitCodes? as [{code, when}], subcommands?), jsonSupported, and the flat responseTypes index. |
|
|
473
474
|
| `help` | Help, in one of two shapes. A bare `astryx --json` returns the root manifest: name, version, commands (the command names), jsonSupported, and manifest (the full payload `astryx manifest --json` returns). `--help --json` on any command, or `astryx help [command] --json`, returns that command's help: command, description, usage, options (each flags, description, and defaultValue and choices when set), and subcommands (each name and description). data.manifest marks the first shape; data.usage marks the second. |
|
|
474
475
|
| `version` | The CLI version, for `astryx --version --json`: {version}. |
|
|
475
476
|
| `doctor` | The health-check report: `checks` (each with id, label, status: pass \| warn \| fail \| info, a message, and an optional fix, always present on warn and fail) plus a `summary` of counts per status. |
|
|
476
477
|
| `integration.add` | A contribution-writer receipt: kind, name, optional root {path, created}, integration-manifest path, every affected project-relative path, written, and dryRun. |
|
|
477
478
|
| `integration.pack-check` | The packed-package check: name, version, packable, tarball {filename, fileCount, size, unpackedSize} or null, inventory {manifest, roots [{kind, path, expectedFiles, missingFiles, complete}], expectedFiles, packedFiles}, contributions {local, packed}, each null or {themes [{slug, exportName}], components, templates [{id, type, name}], codemods [{version, id}], docs, agentDocsAppend}, and issues [{code, severity, message}]. |
|
|
478
|
-
| `integration.validate` | The validation result: the package name and version (both null when
|
|
479
|
-
| `integration.template-conflicts` |
|
|
480
|
-
| `integration.component-conflicts` |
|
|
481
|
-
| `integration.doc-conflicts` |
|
|
482
|
-
| `layout.expand` | The expansion: parsed form, generated TSX code, componentsUsed, states (count of useState hooks scaffolded), todos, blocksReferenced (each {name, mode}), warnings,
|
|
479
|
+
| `integration.validate` | The validation result: validated (false when no integration manifest was found, so nothing was checked and the empty issues list proves nothing), the package name and version (both null when validated is false) plus issues, an AstryxIntegrationIssue[] of {code, severity: warning \| error, message}. |
|
|
480
|
+
| `integration.template-conflicts` | validated (false when no integration manifest was found, so nothing was inspected), the integration identity, structural issues, and non-blocking Core template-id conflicts as {id, severity: warning, integrationPackage, integrationType, integrationName, coreMatches, message, command}. |
|
|
481
|
+
| `integration.component-conflicts` | validated (false when no integration manifest was found, so nothing was inspected), the integration identity, structural issues, and non-blocking conflicts where an integration component name is also owned by Core; each conflict includes the exact package-qualified command. |
|
|
482
|
+
| `integration.doc-conflicts` | validated (false when no integration manifest was found, so nothing was inspected), the integration identity, structural issues, and Core doc overlaps. Each finding includes `severity` (`info` \| `error`) and `relationship` (`replaces` \| `extends` \| `accidental`). |
|
|
483
|
+
| `layout.expand` | The expansion: parsed form, generated TSX code, componentsUsed, states (count of useState hooks scaffolded), todos, blocksReferenced (each {name, mode}), warnings, written (the output path, or null when nothing was written), and demoMediaReplaced (count of demo media placeholders). |
|
|
483
484
|
| `layout.check` | The validation result: a valid flag, the detected form, errors (each with line/col, message, formatted text, and suggestions), warnings, and the expression re-printed in both canonical surfaces (compact and outline). |
|
|
484
485
|
| `layout.grammar` | The XLE/XLO grammar cheatsheet: a text field with the full reference plus an aliases map (short name → canonical component) generated from this install's registry. |
|
|
485
486
|
|
package/api/build/_adapter.d.mts
CHANGED
|
@@ -7,8 +7,15 @@
|
|
|
7
7
|
* @property {string} name The template's own id, as search reports it.
|
|
8
8
|
* @property {string} command `astryx template <id> --type page`, the command that selects exactly this template: an integration replacement is selected by the Core id it replaces, and `--type page` keeps a block with the same id from making it ambiguous. Search prints template commands the same way.
|
|
9
9
|
* @property {string} displayName Human-facing name.
|
|
10
|
-
* @property {string} description What the page is
|
|
10
|
+
* @property {string} description What the page is and how it is laid out.
|
|
11
11
|
* @property {string} category The template's own `Family - Variant` label; empty when it declares none.
|
|
12
|
+
* @property {string[]} keywords The ideas the page serves, as its own descriptor names them; empty when it declares none.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* A component the project can use, as the ranker reads it.
|
|
16
|
+
* @typedef {object} ComponentWords
|
|
17
|
+
* @property {string} name The component's name, e.g. `DateRangeInput`.
|
|
18
|
+
* @property {string[]} keywords The keywords its own doc declares.
|
|
12
19
|
*/
|
|
13
20
|
/**
|
|
14
21
|
* Every ready page template the project can scaffold, in discovery order. This
|
|
@@ -23,6 +30,16 @@
|
|
|
23
30
|
* @returns {Promise<PageTemplate[]>}
|
|
24
31
|
*/
|
|
25
32
|
export function loadPageTemplates(cwd: string): Promise<PageTemplate[]>;
|
|
33
|
+
/**
|
|
34
|
+
* The components the project can use, Core's and its integrations', each with
|
|
35
|
+
* the keywords its own doc declares: what the ranker reads to tell a part of a
|
|
36
|
+
* page from a page. Search's own discovery, so both agree on what exists; empty
|
|
37
|
+
* when Core cannot be found.
|
|
38
|
+
*
|
|
39
|
+
* @param {string} cwd
|
|
40
|
+
* @returns {Promise<ComponentWords[]>}
|
|
41
|
+
*/
|
|
42
|
+
export function loadComponents(cwd: string): Promise<ComponentWords[]>;
|
|
26
43
|
/**
|
|
27
44
|
* A page template the kit can recommend starting from.
|
|
28
45
|
*/
|
|
@@ -40,11 +57,28 @@ export type PageTemplate = {
|
|
|
40
57
|
*/
|
|
41
58
|
displayName: string;
|
|
42
59
|
/**
|
|
43
|
-
* What the page is
|
|
60
|
+
* What the page is and how it is laid out.
|
|
44
61
|
*/
|
|
45
62
|
description: string;
|
|
46
63
|
/**
|
|
47
64
|
* The template's own `Family - Variant` label; empty when it declares none.
|
|
48
65
|
*/
|
|
49
66
|
category: string;
|
|
67
|
+
/**
|
|
68
|
+
* The ideas the page serves, as its own descriptor names them; empty when it declares none.
|
|
69
|
+
*/
|
|
70
|
+
keywords: string[];
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* A component the project can use, as the ranker reads it.
|
|
74
|
+
*/
|
|
75
|
+
export type ComponentWords = {
|
|
76
|
+
/**
|
|
77
|
+
* The component's name, e.g. `DateRangeInput`.
|
|
78
|
+
*/
|
|
79
|
+
name: string;
|
|
80
|
+
/**
|
|
81
|
+
* The keywords its own doc declares.
|
|
82
|
+
*/
|
|
83
|
+
keywords: string[];
|
|
50
84
|
};
|
package/api/build/_adapter.mjs
CHANGED
|
@@ -2,20 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @file The build subject's environment access: the page templates a project
|
|
5
|
-
* can scaffold.
|
|
5
|
+
* can scaffold, and the components it can use.
|
|
6
6
|
*
|
|
7
|
-
* @input Template discovery for `cwd` — the CLI's own templates
|
|
8
|
-
* the project's configured integrations
|
|
7
|
+
* @input Template and component discovery for `cwd` — the CLI's own templates
|
|
8
|
+
* and Core's components, plus any that the project's configured integrations
|
|
9
|
+
* contribute.
|
|
9
10
|
* @output Ready page templates as `{name, displayName, description, category,
|
|
10
|
-
* command}`, where `command` is the `astryx template` command that
|
|
11
|
-
* exactly that template
|
|
12
|
-
* @position Beside build.mjs (api/build/). The kit leaf reads templates
|
|
13
|
-
* through here, because a subject's `_adapter.mjs` is its
|
|
14
|
-
* access.
|
|
15
|
-
*
|
|
11
|
+
* keywords, command}`, where `command` is the `astryx template` command that
|
|
12
|
+
* selects exactly that template; components as `{name, keywords}`.
|
|
13
|
+
* @position Beside build.mjs (api/build/). The kit leaf reads templates and
|
|
14
|
+
* components only through here, because a subject's `_adapter.mjs` is its
|
|
15
|
+
* only environment access. This adds no discovery of its own: templates come
|
|
16
|
+
* from the template subject's, components from search's.
|
|
16
17
|
*/
|
|
17
18
|
|
|
18
19
|
import {discoverTemplates} from '../template/template.mjs';
|
|
20
|
+
import {componentKeywords} from '../search/search.mjs';
|
|
21
|
+
import {findCoreDir} from '../../foundation/fs/paths.mjs';
|
|
19
22
|
|
|
20
23
|
/**
|
|
21
24
|
* A page template the kit can recommend starting from.
|
|
@@ -23,8 +26,16 @@ import {discoverTemplates} from '../template/template.mjs';
|
|
|
23
26
|
* @property {string} name The template's own id, as search reports it.
|
|
24
27
|
* @property {string} command `astryx template <id> --type page`, the command that selects exactly this template: an integration replacement is selected by the Core id it replaces, and `--type page` keeps a block with the same id from making it ambiguous. Search prints template commands the same way.
|
|
25
28
|
* @property {string} displayName Human-facing name.
|
|
26
|
-
* @property {string} description What the page is
|
|
29
|
+
* @property {string} description What the page is and how it is laid out.
|
|
27
30
|
* @property {string} category The template's own `Family - Variant` label; empty when it declares none.
|
|
31
|
+
* @property {string[]} keywords The ideas the page serves, as its own descriptor names them; empty when it declares none.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A component the project can use, as the ranker reads it.
|
|
36
|
+
* @typedef {object} ComponentWords
|
|
37
|
+
* @property {string} name The component's name, e.g. `DateRangeInput`.
|
|
38
|
+
* @property {string[]} keywords The keywords its own doc declares.
|
|
28
39
|
*/
|
|
29
40
|
|
|
30
41
|
/**
|
|
@@ -53,8 +64,28 @@ export async function loadPageTemplates(cwd) {
|
|
|
53
64
|
displayName: t.displayName || t.name,
|
|
54
65
|
description: t.description || '',
|
|
55
66
|
category: t.category || '',
|
|
67
|
+
keywords: t.keywords ?? [],
|
|
56
68
|
// The id `template()` resolves back to this entry: an active replacement
|
|
57
69
|
// owns the Core id it names, so that id selects it, not its own.
|
|
58
70
|
command: `astryx template ${t.replaces ?? t.dirName} --type page`,
|
|
59
71
|
}));
|
|
60
72
|
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The components the project can use, Core's and its integrations', each with
|
|
76
|
+
* the keywords its own doc declares: what the ranker reads to tell a part of a
|
|
77
|
+
* page from a page. Search's own discovery, so both agree on what exists; empty
|
|
78
|
+
* when Core cannot be found.
|
|
79
|
+
*
|
|
80
|
+
* @param {string} cwd
|
|
81
|
+
* @returns {Promise<ComponentWords[]>}
|
|
82
|
+
*/
|
|
83
|
+
export async function loadComponents(cwd) {
|
|
84
|
+
const coreDir = findCoreDir(cwd);
|
|
85
|
+
if (!coreDir) return [];
|
|
86
|
+
try {
|
|
87
|
+
return await componentKeywords(coreDir, cwd);
|
|
88
|
+
} catch {
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
}
|
package/api/build/build.doc.mjs
CHANGED
|
@@ -19,8 +19,8 @@ export const doc = {
|
|
|
19
19
|
'The "build a page" entry point. Called with no query it returns the ' +
|
|
20
20
|
'how-to-build-a-page playbook as data: the workflow steps with their ' +
|
|
21
21
|
'commands, the on-system rules, and related lookups. Called with a query it names the page template to ' +
|
|
22
|
-
'START from (always one: the page template a ranker built for long descriptions puts first
|
|
23
|
-
'shell) and the next two templates, ' +
|
|
22
|
+
'START from (always one: the page template a ranker built for long descriptions puts first; for a part ' +
|
|
23
|
+
'of a page, the page it names; else the app shell) and the next two templates, ' +
|
|
24
24
|
'and the unified search grouped around it: the other close page templates, drop-in blocks, and ' +
|
|
25
25
|
'idea-specific components/hooks, plus the always-on frame + foundation. A template carries the page ' +
|
|
26
26
|
'frame and spacing, so the kit never recommends composing a page from components.',
|
package/api/build/build.test.mjs
CHANGED
|
@@ -8,7 +8,7 @@ import {describe, it, expect, vi} from 'vitest';
|
|
|
8
8
|
import * as path from 'node:path';
|
|
9
9
|
import {fileURLToPath} from 'node:url';
|
|
10
10
|
import {build} from './build.mjs';
|
|
11
|
-
import {search} from '../search/search.mjs';
|
|
11
|
+
import {search, searchedComponents} from '../search/search.mjs';
|
|
12
12
|
|
|
13
13
|
// api/build/ -> up 3 = packages/cli, up 4 = repo root (has packages/core).
|
|
14
14
|
const REPO = path.resolve(
|
|
@@ -152,6 +152,16 @@ describe('build API', () => {
|
|
|
152
152
|
});
|
|
153
153
|
});
|
|
154
154
|
|
|
155
|
+
describe('build kit — reuses the components its search gathered', () => {
|
|
156
|
+
it('keeps them beside the search response, out of its JSON', async () => {
|
|
157
|
+
const all = await search('date picker', {cwd: REPO});
|
|
158
|
+
expect(searchedComponents(all)?.map(c => c.name)).toContain('DateRangeInput');
|
|
159
|
+
expect(JSON.stringify(all)).not.toContain('"keywords"');
|
|
160
|
+
const pagesOnly = await search('date picker', {cwd: REPO, type: 'template'});
|
|
161
|
+
expect(searchedComponents(pagesOnly)).toBeNull();
|
|
162
|
+
}, 60_000);
|
|
163
|
+
});
|
|
164
|
+
|
|
155
165
|
describe('build kit — coverage gates the pages group', () => {
|
|
156
166
|
it('does not call a one-word coincidence a direct match', async () => {
|
|
157
167
|
// A page's keywords include every component its source renders, so any
|
|
@@ -259,7 +269,7 @@ describe('build kit — a thin kit says what to try next', () => {
|
|
|
259
269
|
// A skeleton is a 35-line excerpt: a reader who studies it and composes
|
|
260
270
|
// the rest loses the spacing the template exists to carry. A loose match
|
|
261
271
|
// is still the best start there is, so `start` scaffolds it.
|
|
262
|
-
const r = await build('
|
|
272
|
+
const r = await build('quarterly business review', {cwd: REPO});
|
|
263
273
|
expect(r.type).toBe('build.kit');
|
|
264
274
|
if (r.type !== 'build.kit') return;
|
|
265
275
|
expect(r.data.directMatch).toBe(false);
|
|
@@ -357,9 +367,35 @@ describe('build kit — every page starts from a template', () => {
|
|
|
357
367
|
const placed = await build('an empty state for a settings page', {cwd: REPO});
|
|
358
368
|
if (placed.type !== 'build.kit') throw new Error(placed.type);
|
|
359
369
|
expect(placed.data.start).toMatchObject({name: 'settings', basis: 'closest'});
|
|
370
|
+
expect(placed.data.start?.reason).toMatch(/part of a page, so it starts from the page it names/);
|
|
360
371
|
const loose = await build('a date range picker', {cwd: REPO});
|
|
361
372
|
if (loose.type !== 'build.kit') throw new Error(loose.type);
|
|
362
373
|
expect(loose.data.start).toMatchObject({name: 'shell-top-nav', basis: 'fallback'});
|
|
374
|
+
expect(loose.data.start?.reason).toMatch(/part of a page and names no page, so it starts from the app shell/);
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
it('starts a change to an existing page from the app shell', async () => {
|
|
378
|
+
// The page is the builder's to keep; no template scaffolds it.
|
|
379
|
+
const r = await build('add a sort toggle to the existing reports dashboard', {cwd: REPO});
|
|
380
|
+
if (r.type !== 'build.kit') throw new Error(r.type);
|
|
381
|
+
expect(r.data.start).toMatchObject({name: 'shell-top-nav', basis: 'fallback'});
|
|
382
|
+
expect(r.data.start?.reason).toMatch(/changes a page you already have, so keep it/);
|
|
383
|
+
expect(r.data.start?.reason).not.toMatch(/too little of the idea fits/);
|
|
384
|
+
// A direct match the response reports is still named.
|
|
385
|
+
if (r.data.directMatch) expect(r.data.start?.reason).toContain(`\`${r.data.pages[0].name}\``);
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
it('does not call a new page that mentions something existing a change', async () => {
|
|
389
|
+
for (const idea of [
|
|
390
|
+
'a new dashboard inspired by the existing one',
|
|
391
|
+
'a new dashboard based on the existing dashboard',
|
|
392
|
+
'clone the existing dashboard as a new page',
|
|
393
|
+
]) {
|
|
394
|
+
const r = await build(idea, {cwd: REPO});
|
|
395
|
+
if (r.type !== 'build.kit') throw new Error(r.type);
|
|
396
|
+
expect(r.data.start?.name).toBe('dashboard');
|
|
397
|
+
expect(r.data.start?.reason).not.toMatch(/already have/);
|
|
398
|
+
}
|
|
363
399
|
});
|
|
364
400
|
|
|
365
401
|
it('names a direct match the ranker outweighed in the reason', async () => {
|
package/api/build/kit/kit.mjs
CHANGED
|
@@ -27,13 +27,13 @@
|
|
|
27
27
|
* the invocation stays the renderer's job.
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
|
-
import {search} from '../../search/search.mjs';
|
|
30
|
+
import {search, searchedComponents} from '../../search/search.mjs';
|
|
31
31
|
import {findCoreDir} from '../../../foundation/fs/paths.mjs';
|
|
32
32
|
import {AstryxError} from '../../error.mjs';
|
|
33
33
|
import {ERROR_CODES} from '../../../foundation/response/error-codes.mjs';
|
|
34
34
|
import {getResultCoverage} from '../../search/coverage.mjs';
|
|
35
|
-
import {loadPageTemplates} from '../_adapter.mjs';
|
|
36
|
-
import {pickAlternatives, pickStart, rankPages} from './rank.mjs';
|
|
35
|
+
import {loadComponents, loadPageTemplates} from '../_adapter.mjs';
|
|
36
|
+
import {ideaKind, pickAlternatives, pickStart, rankPages} from './rank.mjs';
|
|
37
37
|
|
|
38
38
|
/** A page at/above this score is a confident direct match. */
|
|
39
39
|
const PAGE_DIRECT = 95;
|
|
@@ -118,6 +118,24 @@ const asTemplate = t => ({
|
|
|
118
118
|
command: `${t.command} <path>`,
|
|
119
119
|
});
|
|
120
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Why a part of a page, or a change to a page the builder already has, starts
|
|
123
|
+
* where it does (spec:AST-048/FR3, FR9): the rest of the start's reason, or
|
|
124
|
+
* null for a whole page.
|
|
125
|
+
* @param {import('./rank.mjs').IdeaKind} kind
|
|
126
|
+
* @param {boolean} inPage whether the start is the page the idea names
|
|
127
|
+
* @returns {string | null}
|
|
128
|
+
*/
|
|
129
|
+
function placement(kind, inPage) {
|
|
130
|
+
if (kind === 'edit')
|
|
131
|
+
return 'the idea changes a page you already have, so keep it and add blocks to it; a new page starts from the app shell.';
|
|
132
|
+
if (kind === 'part')
|
|
133
|
+
return inPage
|
|
134
|
+
? 'the idea is a part of a page, so it starts from the page it names.'
|
|
135
|
+
: 'the idea is a part of a page and names no page, so it starts from the app shell.';
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
121
139
|
/**
|
|
122
140
|
* The template to start from: the ready page the ranker puts first when it
|
|
123
141
|
* has the evidence to lead, else the first fallback shell the project can
|
|
@@ -129,12 +147,13 @@ const asTemplate = t => ({
|
|
|
129
147
|
* the reason names it, so the reader knows why the kit starts elsewhere.
|
|
130
148
|
*
|
|
131
149
|
* @param {import('./rank.mjs').RankedPage[]} ranked
|
|
150
|
+
* @param {import('./rank.mjs').IdeaKind} kind
|
|
132
151
|
* @param {SearchResultEntry[]} pages
|
|
133
152
|
* @param {boolean} directMatch
|
|
134
153
|
* @param {PageTemplate[]} catalog
|
|
135
154
|
* @returns {Omit<BuildStart, 'alternatives'> | null}
|
|
136
155
|
*/
|
|
137
|
-
function chooseStart(ranked, pages, directMatch, catalog) {
|
|
156
|
+
function chooseStart(ranked, kind, pages, directMatch, catalog) {
|
|
138
157
|
const direct = directMatch ? pages[0].name : null;
|
|
139
158
|
const unready =
|
|
140
159
|
direct && !catalog.some(t => t.name === direct) ? direct : null;
|
|
@@ -142,20 +161,32 @@ function chooseStart(ranked, pages, directMatch, catalog) {
|
|
|
142
161
|
// match the ranker outweighed is named, and so are the loose page matches
|
|
143
162
|
// search listed when the kit falls back to the shell.
|
|
144
163
|
const loose = pages.map(p => `\`${p.name}\``).join(', ');
|
|
145
|
-
|
|
164
|
+
/**
|
|
165
|
+
* A part's or an edit's reason, naming a direct match that is not the start.
|
|
166
|
+
* @param {string} place
|
|
167
|
+
* @param {string} startName
|
|
168
|
+
*/
|
|
169
|
+
const placed = (place, startName) =>
|
|
170
|
+
direct && direct !== startName
|
|
171
|
+
? `Search matched \`${direct}\` by name, but ${place}`
|
|
172
|
+
: place[0].toUpperCase() + place.slice(1);
|
|
173
|
+
const pick = pickStart(ranked, kind);
|
|
146
174
|
const closest = pick && catalog.find(t => t.name === pick.name);
|
|
147
|
-
if (closest) {
|
|
175
|
+
if (pick && closest) {
|
|
148
176
|
const agrees = closest.name === direct;
|
|
177
|
+
const place = placement(kind, pick.base && pick.familyNamed);
|
|
149
178
|
return {
|
|
150
179
|
...asTemplate(closest),
|
|
151
180
|
basis: agrees ? 'direct' : 'closest',
|
|
152
|
-
reason:
|
|
153
|
-
?
|
|
154
|
-
:
|
|
155
|
-
?
|
|
156
|
-
:
|
|
157
|
-
?
|
|
158
|
-
:
|
|
181
|
+
reason: unready
|
|
182
|
+
? `\`${unready}\` matches but is not ready yet; this is the closest ready template.`
|
|
183
|
+
: place
|
|
184
|
+
? placed(place, closest.name)
|
|
185
|
+
: agrees
|
|
186
|
+
? 'Matches the idea.'
|
|
187
|
+
: direct
|
|
188
|
+
? `Search matched \`${direct}\` by name, but this template fits more of the idea.`
|
|
189
|
+
: 'The closest template; none is exactly this page.',
|
|
159
190
|
};
|
|
160
191
|
}
|
|
161
192
|
for (const id of FALLBACK_STARTS) {
|
|
@@ -164,18 +195,21 @@ function chooseStart(ranked, pages, directMatch, catalog) {
|
|
|
164
195
|
// The shell can also be the ranker's best guess without the evidence to
|
|
165
196
|
// lead ("horizontal site navigation"); say so rather than "no match".
|
|
166
197
|
const nearest = ranked[0]?.name === shell.name && ranked[0].hits > 0;
|
|
198
|
+
const place = placement(kind, false);
|
|
167
199
|
return {
|
|
168
200
|
...asTemplate(shell),
|
|
169
201
|
basis: 'fallback',
|
|
170
202
|
reason: unready
|
|
171
203
|
? `\`${unready}\` matches but is not ready yet, so start from the app shell.`
|
|
172
|
-
:
|
|
173
|
-
?
|
|
174
|
-
:
|
|
175
|
-
?
|
|
176
|
-
:
|
|
177
|
-
?
|
|
178
|
-
:
|
|
204
|
+
: place
|
|
205
|
+
? placed(place, shell.name)
|
|
206
|
+
: direct
|
|
207
|
+
? `Search matched \`${direct}\` by name, but too little of the idea fits it, so start from the app shell.`
|
|
208
|
+
: nearest
|
|
209
|
+
? 'No template is a clear match; the app shell is the closest.'
|
|
210
|
+
: loose
|
|
211
|
+
? `Search matched ${loose} only loosely, so start from the app shell.`
|
|
212
|
+
: 'No template matched, so start from the app shell.',
|
|
179
213
|
};
|
|
180
214
|
}
|
|
181
215
|
}
|
|
@@ -301,8 +335,18 @@ export async function buildKit(query, options = {}) {
|
|
|
301
335
|
const wantsPages = !type || type === 'template';
|
|
302
336
|
const catalog = wantsPages ? await loadPageTemplates(cwd) : [];
|
|
303
337
|
const ranked = wantsPages ? rankPages(query, catalog) : [];
|
|
338
|
+
// A part of a page starts where it lives (spec:AST-048/FR3); the project's
|
|
339
|
+
// own components say what a part is. The search above already gathered them
|
|
340
|
+
// unless it was narrowed to templates.
|
|
341
|
+
const kind = wantsPages
|
|
342
|
+
? ideaKind(
|
|
343
|
+
query,
|
|
344
|
+
catalog,
|
|
345
|
+
searchedComponents(result) ?? (await loadComponents(cwd)),
|
|
346
|
+
)
|
|
347
|
+
: 'page';
|
|
304
348
|
const chosen = wantsPages
|
|
305
|
-
? chooseStart(ranked, matchedPages, directMatch, catalog)
|
|
349
|
+
? chooseStart(ranked, kind, matchedPages, directMatch, catalog)
|
|
306
350
|
: null;
|
|
307
351
|
// Name the ranker's next two templates beside the start: the reader judges
|
|
308
352
|
// meaning better than keywords do, and an acceptable template is in these
|
package/api/build/kit/rank.d.mts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
// @generated by scripts/sync-api-types.mjs from the JSDoc in api/**/*.mjs.
|
|
2
2
|
// DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* @typedef {import('../_adapter.mjs').PageTemplate} PageTemplate
|
|
6
|
-
* @typedef {{name: string, score: number, hits: number, familyNamed: boolean, containerMatched: boolean}} RankedPage
|
|
7
|
-
*/
|
|
8
4
|
/**
|
|
9
5
|
* Rank page templates against an idea, best first. Ties go to the template
|
|
10
6
|
* with the shorter id (the family's broader page), then by name.
|
|
@@ -14,6 +10,17 @@
|
|
|
14
10
|
* @returns {RankedPage[]}
|
|
15
11
|
*/
|
|
16
12
|
export function rankPages(query: string, pages: PageTemplate[]): RankedPage[];
|
|
13
|
+
/**
|
|
14
|
+
* What an idea asks for (spec:AST-048/FR3): a whole `page`; a `part` of one,
|
|
15
|
+
* when its head noun names one of the system's components and it lists fewer
|
|
16
|
+
* than PAGE_PIECES pieces; or an `edit` of a page the builder already has.
|
|
17
|
+
*
|
|
18
|
+
* @param {string} query
|
|
19
|
+
* @param {PageTemplate[]} pages
|
|
20
|
+
* @param {ComponentWords[]} components
|
|
21
|
+
* @returns {IdeaKind}
|
|
22
|
+
*/
|
|
23
|
+
export function ideaKind(query: string, pages: PageTemplate[], components: ComponentWords[]): IdeaKind;
|
|
17
24
|
/**
|
|
18
25
|
* The next closest templates after the start, best first: the ones a reader
|
|
19
26
|
* should check the idea against when the start's shape is wrong. Each matched
|
|
@@ -26,19 +33,28 @@ export function rankPages(query: string, pages: PageTemplate[]): RankedPage[];
|
|
|
26
33
|
*/
|
|
27
34
|
export function pickAlternatives(ranked: RankedPage[], startName: string, count?: number): RankedPage[];
|
|
28
35
|
/**
|
|
29
|
-
* The template to start from, or null
|
|
30
|
-
* evidence to lead
|
|
31
|
-
*
|
|
36
|
+
* The template to start from, or null for the kit's neutral app shell. A page
|
|
37
|
+
* needs evidence to lead: two matched terms, the idea naming the template's
|
|
38
|
+
* family, or its container. A part starts from the base template of the family
|
|
39
|
+
* the idea places it in, else from the app shell (spec:AST-048/FR3); an edit of
|
|
40
|
+
* a page the builder already has starts from the app shell (FR9). Either way,
|
|
41
|
+
* the app shell is the shell template the idea describes when one leads.
|
|
32
42
|
*
|
|
33
43
|
* @param {RankedPage[]} ranked
|
|
44
|
+
* @param {IdeaKind} [kind]
|
|
34
45
|
* @returns {RankedPage | null}
|
|
35
46
|
*/
|
|
36
|
-
export function pickStart(ranked: RankedPage[]): RankedPage | null;
|
|
47
|
+
export function pickStart(ranked: RankedPage[], kind?: IdeaKind): RankedPage | null;
|
|
37
48
|
export type PageTemplate = import("../_adapter.mjs").PageTemplate;
|
|
49
|
+
export type ComponentWords = import("../_adapter.mjs").ComponentWords;
|
|
38
50
|
export type RankedPage = {
|
|
39
51
|
name: string;
|
|
40
52
|
score: number;
|
|
41
53
|
hits: number;
|
|
42
54
|
familyNamed: boolean;
|
|
43
55
|
containerMatched: boolean;
|
|
56
|
+
family: string;
|
|
57
|
+
base: boolean;
|
|
58
|
+
matched: Set<string>;
|
|
44
59
|
};
|
|
60
|
+
export type IdeaKind = "page" | "part" | "edit";
|