unoverse 0.1.169 → 0.1.171

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
@@ -8,7 +8,7 @@ npm install -g unoverse
8
8
  unoverse create # asks what you're building, then sets it up
9
9
  ```
10
10
 
11
- - **Studio** — author components, templates, nodes, and agent skills. Most
11
+ - **Studio** — author components, apps, nodes, and agent skills. Most
12
12
  people start (and stay) here.
13
13
  - **Universe** — run the platform yourself, on your own infrastructure. Needs a
14
14
  registry access token from your Unoverse admin.
@@ -29,7 +29,7 @@ unoverse stop
29
29
 
30
30
  Creating a universe registers the `canvas` MCP (`.mcp.json`): open the folder in
31
31
  Claude Code and describe the workflow you want, built live on your Canvas. The
32
- authoring skills for components, templates, themes, and custom nodes come with Studio
32
+ authoring skills for components, apps, themes, and custom nodes come with Studio
33
33
  projects (`unoverse studio`). `unoverse update` keeps the CLI, a universe's images,
34
34
  and any installed skills current; to add the skills to your own agent by hand:
35
35
  `npx skills add unoverse-platform/skills`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unoverse",
3
- "version": "0.1.169",
3
+ "version": "0.1.171",
4
4
  "description": "The Unoverse front door — create a Studio project, a universe, or a client app, and launch Studio.",
5
5
  "license": "SEE LICENSE IN README.md",
6
6
  "type": "module",
@@ -112,7 +112,7 @@ export function collectProject(designRoot, project) {
112
112
  //
113
113
  // Both become the same shape, a map of relative path to contents, so nothing downstream
114
114
  // has to care which it was. A single file is simply a map with one entry.
115
- for (const [dir, kind] of [["components", "component"], ["templates", "template"], ["atoms", "atom"]]) {
115
+ for (const [dir, kind] of [["components", "component"], ["apps", "template"], ["atoms", "atom"]]) {
116
116
  const home = join(root, dir);
117
117
  if (!existsSync(home))
118
118
  continue;
@@ -18,7 +18,7 @@ import { RAW_VALUE, PARTIAL_DIRS } from "./vocabulary.mjs";
18
18
  import { isDefFile, defName, defPath, readDef, parseDef } from "./defs.mjs";
19
19
 
20
20
  export function makeLintFile(ctx) {
21
- const { report, walkNode, isFixture, isHook, isManifest, isTemplatePath, defRoot, componentNamesForFile, DS, orgDirs, RX, readText } = ctx;
21
+ const { report, walkNode, isFixture, isHook, isManifest, isAppPath, defRoot, componentNamesForFile, DS, orgDirs, RX, readText } = ctx;
22
22
 
23
23
  function checkStateOrder(order, rootFolder, file, includeLayouts = false) {
24
24
  if (!Array.isArray(order)) return;
@@ -90,7 +90,7 @@ function lintFile(file) {
90
90
 
91
91
  if (isManifest(file)) {
92
92
  const root = dirname(file);
93
- if (isTemplatePath(file)) {
93
+ if (isAppPath(file)) {
94
94
  // TEMPLATE manifest = the envelope. Requires binding + a resolvable root.
95
95
  for (const req of ["name", "whenToUse"])
96
96
  if (!json[req]) report("warn", file, `template manifest missing "${req}": ${req === "whenToUse" ? "the AI selects the app by it" : "the display name"} (docs/design/05)`);
@@ -302,9 +302,9 @@ function lintFile(file) {
302
302
  if (json[k] !== undefined)
303
303
  report("error", file, `envelope duplicates manifest meta "${k}". The discovery manifest is the single home (docs/design/03a)`);
304
304
 
305
- // deprecated bridge: a top-level `defaultState` triggers the component node TEMPLATE_DATA emit
305
+ // deprecated bridge: a top-level `defaultState` triggers the component node APP_DATA emit
306
306
  if (json.defaultState !== undefined)
307
- report("warn", file, `top-level "defaultState" is the deprecated bridge (component-node TEMPLATE_DATA emit). The master state lives in the \`state\` block; templates react via ComponentSlot.select.where (STATE_MODEL §5b)`);
307
+ report("warn", file, `top-level "defaultState" is the deprecated bridge (component-node APP_DATA emit). The master state lives in the \`state\` block; templates react via ComponentSlot.select.where (STATE_MODEL §5b)`);
308
308
 
309
309
  // only components that ADOPTED the structure are held to the full discipline
310
310
  if (hasLayouts || stateFiles.length || hasStateBlock) {
@@ -503,7 +503,7 @@ function lintFile(file) {
503
503
  // bare partial (layouts/ states/ components/ blocks/, or an atom). A template
504
504
  // layout's TOP-LEVEL node is the app's layout root — the one non-slot home for
505
505
  // `appWidth` (state-owned sizing, docs/design/05).
506
- walkNode(json, file, root, null, /[\\/]layouts[\\/][^\\/]+\.(json|yaml)$/.test(file) && isTemplatePath(file));
506
+ walkNode(json, file, root, null, /[\\/]layouts[\\/][^\\/]+\.(json|yaml)$/.test(file) && isAppPath(file));
507
507
  }
508
508
  }
509
509
 
@@ -67,7 +67,7 @@ export function lintDefinitions(designRoot, options = {}) {
67
67
  try {
68
68
  return (
69
69
  statSync(d).isDirectory() &&
70
- ["components", "atoms", "styles", "templates"].some((s) => existsSync(join(d, s)))
70
+ ["components", "atoms", "styles", "apps"].some((s) => existsSync(join(d, s)))
71
71
  );
72
72
  } catch {
73
73
  return false;
@@ -164,7 +164,7 @@ const isHook = (f) => {
164
164
  return life.some((e) => typeof e === "object" && e && typeof e.handler === "string" && e.handler.toLowerCase() === stem);
165
165
  };
166
166
  const isManifest = (f) => /^manifest\.(json|yaml)$/.test(basename(f));
167
- const isTemplatePath = (f) => f.includes(`${sep}templates${sep}`);
167
+ const isAppPath = (f) => f.includes(`${sep}apps${sep}`);
168
168
  // The DEFINITION ROOT folder for a file: a bare partial lives one level under it
169
169
  // (layouts/ states/ components/ blocks/); every `$include` resolves against it.
170
170
  const defRoot = (file) => {
@@ -535,7 +535,7 @@ const keyHomes = [
535
535
  [join(DS, "components"), true],
536
536
  ];
537
537
  for (const orgDir of orgDirs) {
538
- keyHomes.push([join(orgDir, "components"), true], [join(orgDir, "templates"), true]);
538
+ keyHomes.push([join(orgDir, "components"), true], [join(orgDir, "apps"), true]);
539
539
  }
540
540
  for (const [dir, isDir] of keyHomes) {
541
541
  if (!existsSync(dir)) continue;
@@ -568,7 +568,7 @@ for (const [dir, isDir] of keyHomes) {
568
568
  // components (same law everywhere — an org component is a component, just org-private)
569
569
  const homes = [{ dir: join(DS, "components") }, { dir: join(DS, "atoms") }];
570
570
  for (const orgDir of orgDirs) {
571
- for (const sub of ["templates", "components"]) {
571
+ for (const sub of ["apps", "components"]) {
572
572
  const d = join(orgDir, sub);
573
573
  if (existsSync(d) && statSync(d).isDirectory()) homes.push({ dir: d });
574
574
  }
@@ -610,7 +610,7 @@ for (const orgDir of orgDirs) {
610
610
  // Exactly one app is an org's home; a host opens the app whose manifest sets `default: true`
611
611
  // as the conversation's entry point. Two defaults = ambiguous front door → error.
612
612
  for (const orgDir of orgDirs) {
613
- const tdir = join(orgDir, "templates");
613
+ const tdir = join(orgDir, "apps");
614
614
  if (!(existsSync(tdir) && statSync(tdir).isDirectory())) continue;
615
615
  const defaults = [];
616
616
  for (const e of readdirSync(tdir)) {
@@ -627,7 +627,7 @@ for (const orgDir of orgDirs) {
627
627
  report(
628
628
  "error",
629
629
  mf,
630
- `org "${org}" has ${defaults.length} apps with "default": true. An org has exactly ONE default app (its /mcp/${org} front door). Keep it on one manifest, remove it from the others (docs/unoverse/UNOVERSE_MCP_TEMPLATE_PROTOCOL.md §4b)`,
630
+ `org "${org}" has ${defaults.length} apps with "default": true. An org has exactly ONE default app (its /mcp/${org} front door). Keep it on one manifest, remove it from the others (docs/unoverse/UNOVERSE_MCP_APP_PROTOCOL.md §4b)`,
631
631
  );
632
632
  }
633
633
 
@@ -875,7 +875,7 @@ function componentNamesForFile(file) {
875
875
  const ctx = {
876
876
  RX, DS, orgDirs, report, spaceSteps, stepList, checkDimension, checkToken, checkCondition,
877
877
  appSizesForFile, componentNamesForFile, refResolves, canonicalRef, declaredProps, atomsDirExists,
878
- isFixture, isHook, isManifest, isTemplatePath, defRoot, readText,
878
+ isFixture, isHook, isManifest, isAppPath, defRoot, readText,
879
879
  };
880
880
  const walkNode = makeWalkNode(ctx);
881
881
  const lintFile = makeLintFile({ ...ctx, walkNode });
@@ -18,7 +18,7 @@ import { PRIMITIVES, CONDITION_KEYS, STYLE_KEYS, RAW_VALUE, CHILD_NODE_KEYS, PAR
18
18
  import { isDefFile, defName, defPath, readDef } from "./defs.mjs";
19
19
 
20
20
  export function makeWalkNode(ctx) {
21
- const { report, checkCondition, checkDimension, checkToken, appSizesForFile, componentNamesForFile, refResolves, canonicalRef, declaredProps, atomsDirExists, stepList, spaceSteps, isTemplatePath, defRoot } = ctx;
21
+ const { report, checkCondition, checkDimension, checkToken, appSizesForFile, componentNamesForFile, refResolves, canonicalRef, declaredProps, atomsDirExists, stepList, spaceSteps, isAppPath, defRoot } = ctx;
22
22
 
23
23
  function walkNode(node, file, root, widthCap = null, isLayoutRoot = false) {
24
24
  if (Array.isArray(node)) return node.forEach((n) => walkNode(n, file, root, widthCap));
@@ -197,11 +197,11 @@ function walkNode(node, file, root, widthCap = null, isLayoutRoot = false) {
197
197
 
198
198
  // deprecated FOCUS BRIDGE (STATE_MODEL §5b): nothing writes a template `defaultState`
199
199
  // key anymore — a component writes only its own slice; templates react via select.where.
200
- // (Legit panel/draft setTemplateValue writes a DIFFERENT key and is untouched.)
200
+ // (Legit panel/draft setAppValue writes a DIFFERENT key and is untouched.)
201
201
  //
202
202
  const scanAction = (a) => {
203
203
  if (!a || typeof a !== "object") return;
204
- if (a.type === "setTemplateValue") {
204
+ if (a.type === "setAppValue") {
205
205
  // WHAT IS DEPRECATED IS THE FOCUS BRIDGE, not the action (2026-08-23, owner
206
206
  // ruling). Writing `defaultState` to move the template is the bridge: a component
207
207
  // reaching up to change which surface is shown, which the tree reacts to by name
@@ -209,7 +209,7 @@ function walkNode(node, file, root, widthCap = null, isLayoutRoot = false) {
209
209
  // anywhere — a `Ref`-embedded component has no slice of its own, so template state
210
210
  // is the only state in scope, and the shared chat chrome depends on it.
211
211
  if (Array.isArray(a.values) && a.values.some((v) => v && v.key === "defaultState"))
212
- report(isTemplatePath(file) ? "warn" : "error", file, `setTemplateValue writing "defaultState" is the deprecated focus bridge. Change the view with setValue and let the template react via ComponentSlot.select.where (STATE_MODEL §5b)`);
212
+ report(isAppPath(file) ? "warn" : "error", file, `setAppValue writing "defaultState" is the deprecated focus bridge. Change the view with setValue and let the template react via ComponentSlot.select.where (STATE_MODEL §5b)`);
213
213
  }
214
214
  if (a.then) scanAction(a.then);
215
215
  };