uniweb 0.87.4 → 0.88.1

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/README.md CHANGED
@@ -268,7 +268,7 @@ _Foundation-first_ — You're building a component system. The site is a test ha
268
268
 
269
269
  ## Growing Your Project
270
270
 
271
- > **Install the CLI globally** with `npm i -g uniweb` for the best experience. You can also use `npx uniweb` or `pnpm uniweb` without a global install.
271
+ > **Install the CLI globally** with `npm i -g uniweb` for the best experience — inside a project it runs the project's own copy. Keep it current: `create` and `clone` start a project on the CLI that runs them, so a global install stops them when a newer release is out. Without a global install, use `npx uniweb@latest create` to start a project and `pnpm uniweb` inside one.
272
272
 
273
273
  Start simple. Add what you need, when you need it:
274
274
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uniweb",
3
- "version": "0.87.4",
3
+ "version": "0.88.1",
4
4
  "description": "Create structured Vite + React sites with content/code separation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -41,17 +41,17 @@
41
41
  "js-yaml": "^4.1.0",
42
42
  "prompts": "^2.4.2",
43
43
  "tar": "^7.0.0",
44
+ "@uniweb/core": "^0.38.0",
44
45
  "@uniweb/content-writer": "^0.3.4",
46
+ "@uniweb/semantic-parser": "^1.5.1",
45
47
  "@uniweb/kit": "^0.20.0",
46
48
  "@uniweb/schemas": "^0.13.0",
47
- "@uniweb/runtime": "^0.29.4",
48
- "@uniweb/core": "^0.38.0",
49
- "@uniweb/semantic-parser": "^1.5.1"
49
+ "@uniweb/runtime": "^0.29.5"
50
50
  },
51
51
  "peerDependencies": {
52
- "@uniweb/content-reader": "^1.2.8",
52
+ "@uniweb/build": "^0.80.3",
53
53
  "@uniweb/semantic-parser": "^1.5.1",
54
- "@uniweb/build": "^0.80.3"
54
+ "@uniweb/content-reader": "^1.2.8"
55
55
  },
56
56
  "peerDependenciesMeta": {
57
57
  "@uniweb/build": {
@@ -109,11 +109,13 @@ For anything not in that table, start at the index. For CLI flags, prefer `uniwe
109
109
  **Always use the CLI to scaffold — never hand-write `package.json`, `vite.config.js`, `entry.js`, or `index.html`.** The CLI resolves correct versions and structure; hand-written config is the most common way to end up with a project that can't build.
110
110
 
111
111
  ```bash
112
- npx uniweb create my-project --template marketing
112
+ npx uniweb@latest create my-project --template marketing
113
113
  cd my-project && pnpm install
114
114
  uniweb dev
115
115
  ```
116
116
 
117
+ `@latest` matters: a project starts on the packages and templates of the CLI that creates it, and a bare `npx uniweb` runs a globally installed `uniweb` when there is one, however old.
118
+
117
119
  **Choosing a template.** `--template <name>` gives you a working site plus a foundation you can study and edit. `--template none` gives you the same two packages with no content — the right choice when you're building a foundation from scratch or porting a design. `--blank` gives you an empty workspace and assumes you'll add packages with `uniweb add`; use it only if you already know the framework.
118
120
 
119
121
  Official templates include `marketing` (tokens, insets, grids, multi-line headings), `docs` (sidebar nav, code highlighting), `blog` (records of the standard `@std/article` schema, a query, a parametric `[slug]` page), `dynamic` (live API data, loading states), `international` (i18n, records, multi-locale routing), `store` (product grids, e-commerce), `academic` (publications, timeline, math), `extensions` (multi-foundation, runtime loading). `uniweb template list` shows them all.
@@ -1066,7 +1066,7 @@ async function applyFromTemplate(
1066
1066
  })
1067
1067
 
1068
1068
  try {
1069
- const metadata = await validateTemplate(resolved.path, {})
1069
+ const metadata = await validateTemplate(resolved.path)
1070
1070
 
1071
1071
  // Look in contentDirs for matching package type
1072
1072
  const match =
@@ -1083,10 +1083,7 @@ async function applyFromTemplate(
1083
1083
  projectName,
1084
1084
  versions: getVersionsForTemplates()
1085
1085
  },
1086
- {
1087
- onProgress: (msg) => info(` ${msg}`),
1088
- renames: match.renames
1089
- }
1086
+ { onProgress: (msg) => info(` ${msg}`) }
1090
1087
  )
1091
1088
 
1092
1089
  // Merge template dependencies
@@ -1,29 +1,25 @@
1
1
  /**
2
- * template — `list` is live; `register` is RESERVED (not on the new backend yet).
3
- *
4
- * `uniweb template publish` submitted a site as a cloud template to the legacy
5
- * registry (the PHP backend + Cloudflare Worker) the CLI no longer talks to. A
6
- * new-backend equivalent isn't built; when it is, a template is REGISTERED (like
7
- * a foundation or a schemas package) — the verb will be `register`, not `publish`
8
- * (`publish` is for SITES only). The command name is kept reserved.
9
- *
10
- * (Unrelated: scaffolding FROM a template — `uniweb create --template <name>` —
11
- * is a separate path and is unaffected.)
2
+ * template — lists the official templates.
12
3
  *
13
4
  * ⭐ `list` exists so a SCRIPT can enumerate the official templates. The
14
- * interactive picker in `create` has always shown this list; `--json` is the
15
- * same data, second rendering, for tooling that scaffolds several templates in
16
- * a row and needs their names first.
5
+ * interactive picker in `create` shows the same list; `--json` is the same
6
+ * data, second rendering, for tooling that scaffolds several templates in a
7
+ * row and needs their names first. A bare `uniweb template` lists too.
8
+ *
9
+ * ⚠️ IT REPORTS THIS CLI'S SNAPSHOT, NOT A LIVE FETCH — the roster is baked into
10
+ * the CLI at publish time (`src/framework-index.json`), and `create` downloads
11
+ * these templates from the release that same snapshot names. So it answers
12
+ * "which official templates does THIS CLI scaffold", which is the right
13
+ * question when the next step is resolving one of those names with the same
14
+ * CLI. `cliVersion` is in the JSON for exactly that reason. A newer template
15
+ * than your CLI will not appear — and an unknown name is not fatal either way,
16
+ * since `create` falls through to npm `@uniweb/template-<name>`, so this is the
17
+ * OFFICIAL roster rather than the set of resolvable names.
17
18
  *
18
- * ⚠️ IT REPORTS A BUNDLED SNAPSHOT, NOT A LIVE FETCH — the roster is baked into
19
- * the CLI at publish time (`src/framework-index.json`, rewritten from
20
- * `templates/manifest.json` by the pre-publish hook). So it answers "which
21
- * official templates does THIS CLI know", which is the right question when the
22
- * next step is resolving one of those names with the same CLI. `cliVersion` is
23
- * in the JSON for exactly that reason. A newer template than your CLI will not
24
- * appear — and an unknown name is not fatal either way, since `create` falls
25
- * through to npm `@uniweb/template-<name>`, so this is the OFFICIAL roster
26
- * rather than the set of resolvable names.
19
+ * ⛔ There is no `template register` [Diego, 2026-10-08]: a site is offered as a
20
+ * template by the backend it is pushed to when its `site.yml` says
21
+ * `template: true`, so there is nothing for a verb to do. It was a reserved
22
+ * refusal until then, and never worked on the current backend.
27
23
  */
28
24
 
29
25
  import { OFFICIAL_TEMPLATE_MAP } from '../templates/resolver.js'
@@ -37,24 +33,6 @@ const colors = {
37
33
  red: '\x1b[31m'
38
34
  }
39
35
 
40
- function reserved() {
41
- console.error(
42
- "\x1b[31m✗\x1b[0m `uniweb template register` isn't available on the new backend yet."
43
- )
44
- console.error(
45
- ' Submitting a site as a cloud template was retired with the PHP backend.'
46
- )
47
- console.error(
48
- ' A template is REGISTERED, like a foundation — `publish` is for sites only.'
49
- )
50
- console.error(
51
- ' (Scaffolding FROM a template still works: `uniweb create --template <name>`.)'
52
- )
53
- console.error('')
54
- console.error(' To list the official templates: `uniweb template list`.')
55
- process.exit(1)
56
- }
57
-
58
36
  function listTemplates(args) {
59
37
  const entries = Object.entries(OFFICIAL_TEMPLATE_MAP).map(([id, info]) => ({
60
38
  id,
@@ -73,19 +51,19 @@ function listTemplates(args) {
73
51
  2
74
52
  ) + '\n'
75
53
  )
76
- return
54
+ return { exitCode: 0 }
77
55
  }
78
56
 
79
57
  if (entries.length === 0) {
80
- // Not a crash: a CLI whose snapshot failed to load falls back to an empty
81
- // map, and `create` then routes every name to npm. Say which happened.
58
+ // Not a crash: a CLI whose snapshot failed to load has an empty map, and
59
+ // `create` then routes every name to npm. Say which happened.
82
60
  console.error(
83
61
  `${colors.red}✗${colors.reset} No official templates found in this CLI's bundled index.`
84
62
  )
85
63
  console.error(
86
64
  ` ${colors.dim}A template name will still resolve through npm as @uniweb/template-<name>.${colors.reset}`
87
65
  )
88
- process.exit(1)
66
+ return { exitCode: 1 }
89
67
  }
90
68
 
91
69
  const width = Math.max(...entries.map((e) => e.id.length))
@@ -107,12 +85,23 @@ function listTemplates(args) {
107
85
  ` ${colors.dim}--json for scripts. This is the roster THIS CLI ships with, not a live fetch.${colors.reset}`
108
86
  )
109
87
  console.log('')
88
+ return { exitCode: 0 }
110
89
  }
111
90
 
91
+ /**
92
+ * @param {string[]} args - what follows `uniweb template`
93
+ * @returns {{ exitCode: number }}
94
+ */
112
95
  export async function template(args = []) {
113
96
  const sub = args.find((a) => !a.startsWith('-'))
114
- if (sub === 'list') return listTemplates(args)
115
- return reserved()
97
+ if (sub === undefined || sub === 'list') return listTemplates(args)
98
+ console.error(
99
+ `${colors.red}✗${colors.reset} Unknown subcommand: uniweb template ${sub}`
100
+ )
101
+ console.error(
102
+ ` ${colors.dim}uniweb template list lists the official templates${colors.reset}`
103
+ )
104
+ return { exitCode: 2 }
116
105
  }
117
106
 
118
107
  export default template
@@ -1,141 +1,66 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-10-08T17:12:48.725Z",
3
+ "generatedAt": "2026-10-09T02:55:55.375Z",
4
4
  "packages": {
5
5
  "@uniweb/api": {
6
- "version": "0.7.0",
7
- "path": "framework/api",
8
- "deps": [
9
- "@uniweb/core"
10
- ]
6
+ "version": "0.7.0"
11
7
  },
12
8
  "@uniweb/build": {
13
- "version": "0.80.3",
14
- "path": "framework/build",
15
- "deps": [
16
- "@uniweb/content-reader",
17
- "@uniweb/content-writer",
18
- "@uniweb/core",
19
- "@uniweb/projections",
20
- "@uniweb/runtime",
21
- "@uniweb/runtime",
22
- "@uniweb/schemas",
23
- "@uniweb/semantic-parser",
24
- "@uniweb/theming"
25
- ]
9
+ "version": "0.80.3"
26
10
  },
27
11
  "@uniweb/content-reader": {
28
- "version": "1.2.8",
29
- "path": "framework/content-reader",
30
- "deps": []
12
+ "version": "1.2.8"
31
13
  },
32
14
  "@uniweb/content-writer": {
33
- "version": "0.3.4",
34
- "path": "framework/content-writer",
35
- "deps": []
15
+ "version": "0.3.4"
36
16
  },
37
17
  "@uniweb/core": {
38
- "version": "0.38.0",
39
- "path": "framework/core",
40
- "deps": [
41
- "@uniweb/semantic-parser",
42
- "@uniweb/theming"
43
- ]
18
+ "version": "0.38.0"
44
19
  },
45
20
  "@uniweb/frame-bridge": {
46
- "version": "0.3.2",
47
- "path": "framework/frame-bridge",
48
- "deps": []
21
+ "version": "0.3.2"
49
22
  },
50
23
  "@uniweb/icons": {
51
- "version": "0.4.29",
52
- "path": "framework/icons",
53
- "deps": [
54
- "@uniweb/core"
55
- ]
24
+ "version": "0.4.29"
56
25
  },
57
26
  "@uniweb/kit": {
58
- "version": "0.20.0",
59
- "path": "framework/kit",
60
- "deps": [
61
- "@uniweb/core",
62
- "@uniweb/projections",
63
- "@uniweb/scene",
64
- "@uniweb/schemas",
65
- "@uniweb/semantic-parser"
66
- ]
27
+ "version": "0.20.0"
67
28
  },
68
29
  "@uniweb/loom": {
69
- "version": "0.2.5",
70
- "path": "framework/loom",
71
- "deps": []
30
+ "version": "0.2.5"
72
31
  },
73
32
  "@uniweb/press": {
74
- "version": "0.4.18",
75
- "path": "framework/press",
76
- "deps": []
33
+ "version": "0.4.18"
77
34
  },
78
35
  "@uniweb/projections": {
79
- "version": "0.7.15",
80
- "path": "framework/projections",
81
- "deps": [
82
- "@uniweb/content-writer",
83
- "@uniweb/core"
84
- ]
36
+ "version": "0.7.15"
85
37
  },
86
38
  "@uniweb/runtime": {
87
- "version": "0.29.4",
88
- "path": "framework/runtime",
89
- "deps": [
90
- "@uniweb/core",
91
- "@uniweb/theming"
92
- ]
39
+ "version": "0.29.5"
93
40
  },
94
41
  "@uniweb/scene": {
95
- "version": "0.1.3",
96
- "path": "framework/scene",
97
- "deps": []
42
+ "version": "0.1.3"
98
43
  },
99
44
  "@uniweb/schemas": {
100
- "version": "0.13.0",
101
- "path": "framework/schemas",
102
- "deps": []
45
+ "version": "0.13.0"
103
46
  },
104
47
  "@uniweb/scholar": {
105
- "version": "0.2.3",
106
- "path": "framework/scholar",
107
- "deps": []
48
+ "version": "0.2.3"
108
49
  },
109
50
  "@uniweb/semantic-parser": {
110
- "version": "1.5.1",
111
- "path": "framework/semantic-parser",
112
- "deps": []
51
+ "version": "1.5.1"
113
52
  },
114
53
  "@uniweb/snapshot": {
115
- "version": "0.1.2",
116
- "path": "framework/snapshot",
117
- "deps": []
54
+ "version": "0.1.2"
118
55
  },
119
56
  "@uniweb/templates": {
120
- "version": "0.18.4",
121
- "path": "framework/templates",
122
- "deps": []
57
+ "version": "0.18.6"
123
58
  },
124
59
  "@uniweb/theming": {
125
- "version": "0.2.0",
126
- "path": "framework/theming",
127
- "deps": []
60
+ "version": "0.2.0"
128
61
  },
129
62
  "@uniweb/unipress": {
130
- "version": "0.10.24",
131
- "path": "framework/unipress",
132
- "deps": [
133
- "@uniweb/build",
134
- "@uniweb/content-reader",
135
- "@uniweb/core",
136
- "@uniweb/runtime",
137
- "@uniweb/semantic-parser"
138
- ]
63
+ "version": "0.10.24"
139
64
  }
140
65
  },
141
66
  "templates": {
@@ -233,8 +158,8 @@
233
158
  ]
234
159
  },
235
160
  "cv-loom": {
236
- "name": "CV (Loom)",
237
- "description": "A single-page narrative CV whose prose is written with {placeholder} expressions and instantiated at render time via the foundation content handler and @uniweb/loom. Reference implementation of the handler hook.",
161
+ "name": "CV (Loom + Press)",
162
+ "description": "An academic CV whose sections are written with Loom {placeholder} expressions, filled in from a single profile at render time by the foundation's content handler, and downloadable as a branded Word document through Press. Ships with Charles Darwin's CV as sample data.",
238
163
  "tags": [
239
164
  "cv",
240
165
  "loom",
package/src/index.js CHANGED
@@ -41,7 +41,8 @@ import { template } from './commands/template.js'
41
41
  import {
42
42
  resolveTemplate,
43
43
  parseTemplateId,
44
- buildTemplateChoices
44
+ buildTemplateChoices,
45
+ templatePickerPageSize
45
46
  } from './templates/index.js'
46
47
  import { validateTemplate } from './templates/validator.js'
47
48
  import {
@@ -86,9 +87,8 @@ const colors = {
86
87
  }
87
88
 
88
89
  // Template choices for the interactive prompt — built-ins first, then every
89
- // official template from the shared manifest (so the list never drifts from
90
- // framework/templates/manifest.json), with "Blank" last. Composed by
91
- // buildTemplateChoices() in templates/resolver.js.
90
+ // official template in this CLI's snapshot (the list `create` can download),
91
+ // with "Blank" last. Composed by buildTemplateChoices() in templates/resolver.js.
92
92
  const TEMPLATE_CHOICES = buildTemplateChoices()
93
93
 
94
94
  // Files that may pre-exist in the target dir during `uniweb create .` and
@@ -555,11 +555,7 @@ async function createFromContentTemplate(
555
555
  contentDir.dir,
556
556
  fullPath,
557
557
  { projectName },
558
- {
559
- onProgress,
560
- onWarning,
561
- renames: contentDir.renames
562
- }
558
+ { onProgress, onWarning }
563
559
  )
564
560
  ensureFoundationName(fullPath, registryName)
565
561
  }
@@ -677,9 +673,9 @@ async function main() {
677
673
  const { fetchAndNotifyIfNewer, maybeNotifyFromCache } =
678
674
  await import('./utils/update-check.js')
679
675
  if (process.stdout.isTTY) {
680
- await fetchAndNotifyIfNewer(getCliVersion(), { tone: 'soft' })
676
+ await fetchAndNotifyIfNewer(getCliVersion())
681
677
  } else {
682
- maybeNotifyFromCache(getCliVersion(), 'soft')
678
+ maybeNotifyFromCache(getCliVersion())
683
679
  }
684
680
  } catch {
685
681
  /* ignore */
@@ -700,30 +696,36 @@ async function main() {
700
696
  // Commands that need @uniweb/build will get a helpful error via importProjectCommand().
701
697
  }
702
698
 
703
- // Start non-blocking update check for global installs.
699
+ // Update check for global installs.
704
700
  //
705
- // Two surfaces:
706
- // - showUpdateNotification (soft, trailing): printed at command end for
707
- // any verb. Doesn't interrupt the user's workflow.
708
- // - eager (loud, leading): printed BEFORE staleness-sensitive verbs do
709
- // their work. Today: only `create` (templates ship with the CLI, so
710
- // a stale CLI scaffolds stale starter content; the user needs to know
711
- // before files hit disk). Other verbs are insensitive — `deploy` etc.
712
- // are project-bound (delegated to local node_modules), and the
713
- // local-vs-global mismatch warning in delegateToLocal already covers
714
- // that case.
701
+ // - `create` / `clone` STOP when a newer release exists, before anything is
702
+ // written: a project starts on the packages and templates of the CLI that
703
+ // creates it, and a global install runs these two itself — there is no
704
+ // project CLI to delegate to yet (refuseOutdatedProjectStart). Their
705
+ // `--help` still prints.
706
+ // - every other verb gets a trailing notice (showUpdateNotification): they
707
+ // are project-bound (delegated to local node_modules) or do not depend on
708
+ // the version, and the local-vs-global mismatch warning in
709
+ // delegateToLocal covers the first case.
715
710
  let showUpdateNotification = () => {}
716
711
  if (global) {
712
+ let updateCheck = null
717
713
  try {
718
- const { startUpdateCheck, maybeEagerNotification } =
719
- await import('./utils/update-check.js')
720
- showUpdateNotification = startUpdateCheck(getCliVersion())
721
- if (command === 'create') {
722
- maybeEagerNotification(getCliVersion())
723
- }
714
+ updateCheck = await import('./utils/update-check.js')
724
715
  } catch {
725
716
  // Update check is optional — don't fail if the module is missing
726
717
  }
718
+ const startsProject = command === 'create' || command === 'clone'
719
+ const asksForHelp = args.slice(1).some((a) => a === '--help' || a === '-h')
720
+ if (
721
+ updateCheck &&
722
+ startsProject &&
723
+ !asksForHelp &&
724
+ (await updateCheck.refuseOutdatedProjectStart(getCliVersion(), args))
725
+ ) {
726
+ await exitWhenDrained(1)
727
+ }
728
+ if (updateCheck) showUpdateNotification = updateCheck.startUpdateCheck(getCliVersion())
727
729
  }
728
730
 
729
731
  // Show help
@@ -1028,8 +1030,8 @@ async function main() {
1028
1030
 
1029
1031
  // Handle template command
1030
1032
  if (command === 'template') {
1031
- await template(args.slice(1))
1032
- return
1033
+ const result = await template(args.slice(1))
1034
+ await exitWhenDrained(result?.exitCode ?? 0)
1033
1035
  }
1034
1036
 
1035
1037
  // Handle create command
@@ -1190,7 +1192,8 @@ async function main() {
1190
1192
  name: 'template',
1191
1193
  message: 'Template:',
1192
1194
  choices: TEMPLATE_CHOICES,
1193
- initial: 1
1195
+ initial: 1,
1196
+ optionsPerPage: templatePickerPageSize(TEMPLATE_CHOICES.length)
1194
1197
  },
1195
1198
  {
1196
1199
  onCancel: () => {
@@ -1290,7 +1293,7 @@ async function main() {
1290
1293
  )
1291
1294
 
1292
1295
  // Validate and apply as format 2 content template
1293
- const metadata = await validateTemplate(resolved.path, {})
1296
+ const metadata = await validateTemplate(resolved.path)
1294
1297
 
1295
1298
  try {
1296
1299
  await createFromContentTemplate(
@@ -1504,7 +1507,8 @@ ${colors.bright}Usage:${colors.reset}
1504
1507
 
1505
1508
  ${colors.bright}Options:${colors.reset}
1506
1509
  --template <type> Project template (default: starter)
1507
- Built-in: starter, none, marketing
1510
+ Built-in: starter, none, blank
1511
+ Official: marketing, docs, … (\`uniweb template list\`)
1508
1512
  Local: ./path/to/template
1509
1513
  npm: @scope/template-name
1510
1514
  GitHub: github:user/repo or https://github.com/user/repo
@@ -1664,12 +1668,13 @@ ${colors.dim}Hosts that support PR previews: cloudflare-pages, netlify, vercel.
1664
1668
  GitHub Pages has no preview environment, so it scaffolds a deploy workflow only.${colors.reset}
1665
1669
 
1666
1670
  ${colors.bright}Examples:${colors.reset}
1667
- uniweb add project docs # Create docs/foundation/ + docs/site/
1671
+ uniweb add project docs # Create docs/src/ + docs/site/
1668
1672
  uniweb add project docs --from academic # Co-located pair + academic content
1669
- uniweb add foundation # Create ./foundation/ at root
1670
- uniweb add foundation ui # Create ./foundations/ui/
1673
+ uniweb add foundation # Create ./src/ at root
1674
+ uniweb add foundation ui # Create ./ui/ — the folder you name
1675
+ uniweb add foundation foundations/ui # Create ./foundations/ui/
1671
1676
  uniweb add site # Create ./site/ at root
1672
- uniweb add site blog --foundation marketing # Create ./sites/blog/ wired to marketing
1677
+ uniweb add site blog --foundation marketing # Create ./blog/ wired to marketing
1673
1678
  uniweb add extension effects --site site # Create ./extensions/effects/
1674
1679
  uniweb add section Hero # Create Hero section type
1675
1680
  uniweb add section Hero --foundation ui # Target specific foundation
@@ -1677,7 +1682,7 @@ ${colors.bright}Examples:${colors.reset}
1677
1682
  uniweb add section Pricing --starter # Scaffold Pricing + content that fills it
1678
1683
  uniweb add section Hero --starter --preset split # Frontmatter it with the 'split' preset
1679
1684
  uniweb add section Hero --starter --json # The structure + ProseMirror, for a script
1680
- uniweb add foundation --project docs # Create ./docs/foundation/ (co-located)
1685
+ uniweb add foundation --project docs # Create ./docs/src/ (co-located)
1681
1686
  uniweb add site --project docs # Create ./docs/site/ (co-located)
1682
1687
  uniweb add ci # Pick a host, add a deploy workflow
1683
1688
  uniweb add ci --host github-pages --site marketing # Pick host + site explicitly
@@ -1969,18 +1974,12 @@ ${colors.bright}Usage:${colors.reset}
1969
1974
 
1970
1975
  ${colors.bright}What \`list\` reports:${colors.reset}
1971
1976
  The roster ${colors.bright}this CLI ships with${colors.reset} — a snapshot baked in at publish time,
1972
- not a live fetch. That is the right question when the next step is resolving
1973
- one of those names with the same CLI, which is why \`--json\` stamps
1974
- \`cliVersion\`. A template newer than your CLI will not be listed.
1977
+ not a live fetch. \`create\` downloads these templates from the release the same
1978
+ snapshot names, which is why \`--json\` stamps \`cliVersion\`. A template newer than
1979
+ your CLI is not listed: \`npx uniweb@latest template list\` shows the current roster.
1975
1980
 
1976
1981
  It is the ${colors.bright}official${colors.reset} roster, not the set of resolvable names: an unknown
1977
1982
  name is not fatal, since \`create\` falls through to npm \`@uniweb/template-<name>\`.
1978
-
1979
- ${colors.bright}Reserved:${colors.reset}
1980
- \`uniweb template register\` is not available on the new backend yet. Submitting
1981
- a site as a cloud template was retired with the legacy backend; when rebuilt, a
1982
- template is REGISTERED (like a foundation) — \`publish\` is for sites only.
1983
- Scaffolding FROM a template is unaffected: \`uniweb create --template <name>\`.
1984
1983
  `,
1985
1984
  docs: `
1986
1985
  ${colors.cyan}${colors.bright}uniweb docs${colors.reset} ${colors.dim}— Generate component documentation${colors.reset}
@@ -2093,6 +2092,7 @@ ${colors.bright}Commands:${colors.reset}
2093
2092
  inspect <path> Inspect parsed content shape of a markdown file or folder
2094
2093
  docs Generate component documentation
2095
2094
  families List the standard section families (for meta.js family:)
2095
+ template list List the official templates (for create --template)
2096
2096
  doctor Diagnose project configuration issues
2097
2097
  validate Check your content against your foundation's data schemas
2098
2098
  update Align workspace deps + AGENTS.md to the running CLI
@@ -2214,9 +2214,9 @@ ${colors.bright}Examples:${colors.reset}
2214
2214
  cd my-project
2215
2215
  uniweb add project docs # Add docs/src/ + docs/site/
2216
2216
  uniweb add project docs --from academic # Co-located pair + academic content
2217
- uniweb add marketing # Add marketing/ at root
2218
- uniweb add site blog --foundation marketing # Add site/ wired to marketing
2219
- uniweb add extension effects --site site # Add effects/ at root
2217
+ uniweb add foundation marketing # Add marketing/ at root
2218
+ uniweb add site blog --foundation marketing # Add blog/ wired to marketing
2219
+ uniweb add extension effects --site site # Add extensions/effects/
2220
2220
 
2221
2221
  uniweb build
2222
2222
  uniweb build --target src # Build src/ package