@unoverse-platform/studio 0.1.72 → 0.1.74
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/bin/studio.mjs
CHANGED
|
@@ -167,6 +167,11 @@ process.env.UNOVERSE_HOME = projectRoot;
|
|
|
167
167
|
// topUpSamples explains the record that makes that distinction safe.
|
|
168
168
|
{
|
|
169
169
|
const { topUpSamples } = await import("../local/scaffold.mjs");
|
|
170
|
+
// NO PROJECT ARGUMENT ON PURPOSE. Since skills and blocks became org-scoped
|
|
171
|
+
// (2026-08-24) their samples need a project to land in, and a workspace may hold
|
|
172
|
+
// several — picking one here would drop a sample into an arbitrary org. Those
|
|
173
|
+
// samples arrive with `createFirstProject`, which is the only moment a destination
|
|
174
|
+
// is unambiguous; workspace-level samples (nodes) still top up on every launch.
|
|
170
175
|
try { topUpSamples(projectRoot); } catch { /* a read-only folder must not block launch */ }
|
|
171
176
|
}
|
|
172
177
|
console.log(`\n Studio reading: ${designHome(projectRoot)}\n`);
|
package/local/scaffold.mjs
CHANGED
|
@@ -73,7 +73,11 @@ export function createProject(home, name) {
|
|
|
73
73
|
const root = join(designHome(home), name);
|
|
74
74
|
if (existsSync(root)) throw new Error(`${name} already exists in this workspace`);
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
// EVERY AUTHORED KIND LIVES UNDER THE ORG (2026-08-24). skills/ and blocks/ moved in
|
|
77
|
+
// beside components/apps/styles so a skill is org-scoped exactly like a component —
|
|
78
|
+
// two orgs may each ship an `hr-education-coach` without colliding on one flat name
|
|
79
|
+
// (items/collect.ts qualifies a skill's row `<org>/<name>`).
|
|
80
|
+
for (const d of ["components/welcome", "apps", "styles", "skills", "blocks"]) mkdirSync(join(root, d), { recursive: true });
|
|
77
81
|
writeFileSync(join(root, "components/welcome/welcome.yaml"), WELCOME);
|
|
78
82
|
|
|
79
83
|
// Worked example styles (sample-styles: brand ingredients + the override rules), so
|
|
@@ -111,9 +115,14 @@ export function createProject(home, name) {
|
|
|
111
115
|
* makes that safe.
|
|
112
116
|
*/
|
|
113
117
|
const SAMPLES = [
|
|
114
|
-
{ id: "nodes/samples", dir: "sample-nodes/samples", to: "nodes/samples" },
|
|
115
|
-
|
|
116
|
-
|
|
118
|
+
{ id: "nodes/samples", dir: "sample-nodes/samples", to: () => "nodes/samples" },
|
|
119
|
+
// ORG-SCOPED since 2026-08-24: a sample skill/block lands in the FIRST project's own
|
|
120
|
+
// folder, because that is where authored skills and blocks now live. `to` is a
|
|
121
|
+
// function of the project for exactly that reason — the id stays the old flat string
|
|
122
|
+
// so a workspace that already installed these is not offered them a second time in a
|
|
123
|
+
// new location (the record is keyed by id, and re-copying is what it exists to stop).
|
|
124
|
+
{ id: "prompts/skills/sample-complaint-handling", dir: "sample-prompts/skills/sample-complaint-handling", to: (p) => `design/${p}/skills/sample-complaint-handling` },
|
|
125
|
+
{ id: "prompts/blocks/sample-concise-answers", dir: "sample-prompts/blocks/formatting/sample-concise-answers.md", to: (p) => `design/${p}/blocks/formatting/sample-concise-answers.md` },
|
|
117
126
|
];
|
|
118
127
|
|
|
119
128
|
/**
|
|
@@ -127,25 +136,39 @@ const SAMPLES = [
|
|
|
127
136
|
* That is what lets `unoverse studio` top up new samples on every launch without ever
|
|
128
137
|
* resurrecting deleted work or overwriting changed files.
|
|
129
138
|
*/
|
|
130
|
-
export function topUpSamples(home) {
|
|
139
|
+
export function topUpSamples(home, project) {
|
|
131
140
|
const record = join(home, ".unoverse", "samples.json");
|
|
141
|
+
// An org-scoped sample has nowhere to go without a project. Called at launch (where
|
|
142
|
+
// the project is not known) the org samples are simply skipped — they arrive with the
|
|
143
|
+
// next `createFirstProject`, which is the only moment their destination exists.
|
|
144
|
+
const destOf = (s) => (typeof s.to === "function" ? s.to(project) : s.to);
|
|
145
|
+
const placeable = (s) => typeof s.to !== "function" || !!project;
|
|
132
146
|
let seen = [];
|
|
133
147
|
try {
|
|
134
148
|
seen = JSON.parse(readFileSync(record, "utf8")).installed ?? [];
|
|
135
149
|
} catch {
|
|
136
150
|
// No record: a workspace from before records existed. Treat every sample already
|
|
137
151
|
// on disk as seen (it was installed by an old scaffold), so nothing is re-copied.
|
|
138
|
-
|
|
152
|
+
// The OLD flat paths count too: a workspace that installed these before the org
|
|
153
|
+
// move still holds them at prompts/…, and must not be handed a second copy.
|
|
154
|
+
const legacy = {
|
|
155
|
+
"prompts/skills/sample-complaint-handling": "prompts/skills/sample-complaint-handling",
|
|
156
|
+
"prompts/blocks/sample-concise-answers": "prompts/blocks/formatting/sample-concise-answers.md",
|
|
157
|
+
};
|
|
158
|
+
seen = SAMPLES.filter(
|
|
159
|
+
(s) => (placeable(s) && existsSync(join(home, destOf(s)))) || (legacy[s.id] && existsSync(join(home, legacy[s.id]))),
|
|
160
|
+
).map((s) => s.id);
|
|
139
161
|
}
|
|
140
162
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
141
163
|
let changed = false;
|
|
142
164
|
for (const s of SAMPLES) {
|
|
143
|
-
if (seen.includes(s.id)) continue;
|
|
165
|
+
if (seen.includes(s.id) || !placeable(s)) continue;
|
|
144
166
|
const src = join(here, s.dir);
|
|
145
167
|
if (!existsSync(src)) continue;
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
168
|
+
const dest = join(home, destOf(s));
|
|
169
|
+
if (!existsSync(dest)) {
|
|
170
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
171
|
+
cpSync(src, dest, { recursive: true });
|
|
149
172
|
}
|
|
150
173
|
seen.push(s.id);
|
|
151
174
|
changed = true;
|
|
@@ -164,9 +187,11 @@ export function topUpSamples(home) {
|
|
|
164
187
|
*/
|
|
165
188
|
export function createFirstProject(home, name) {
|
|
166
189
|
const root = createProject(home, name);
|
|
167
|
-
|
|
190
|
+
// Only nodes/ remains beside the design tree; skills/ and blocks/ moved INTO the org
|
|
191
|
+
// (createProject makes them) so every authored kind sits in one place.
|
|
192
|
+
for (const d of ["nodes"]) mkdirSync(join(home, d), { recursive: true });
|
|
168
193
|
|
|
169
|
-
topUpSamples(home);
|
|
194
|
+
topUpSamples(home, name);
|
|
170
195
|
|
|
171
196
|
// SCAFFOLD WRITES NO `.claude/skills` (2026-08-21). It used to copy the Claude Code
|
|
172
197
|
// authoring skills out of `vendor/skills` and into `<home>/.claude/skills`, which meant
|
package/package.json
CHANGED
|
@@ -43,8 +43,8 @@ states:
|
|
|
43
43
|
- **Top-level order IS the priority ladder**, base first. The app can be in ONE
|
|
44
44
|
state at a time: it walks the list top-down and enters the first of its states that
|
|
45
45
|
ANY hosted component currently matches. The ladder is DERIVED from the tree; never
|
|
46
|
-
author `stateOrder
|
|
47
|
-
|
|
46
|
+
author `stateOrder`. It is now a LINT ERROR beside a tree (`file.mjs`), after two
|
|
47
|
+
client components ran on one for months because nothing enforced the rule.
|
|
48
48
|
- **Nesting IS containment, enforced by compilation.** A declared base substate
|
|
49
49
|
(`welcome`) is stripped from every non-base arrangement; no hand-written guard ever
|
|
50
50
|
polices it. The substate's file keeps its own condition (`visibleWhen: isEmpty`).
|
|
@@ -76,8 +76,9 @@ design/<project>/apps/<name>/
|
|
|
76
76
|
products.yaml # state "products" → core + cards rail
|
|
77
77
|
detail.yaml # state "detail" → core + full record page
|
|
78
78
|
components/ # app-local partials (core, header, composer-bar, turns…)
|
|
79
|
-
|
|
80
|
-
#
|
|
79
|
+
# NO states/ folder: every drawing lives in layouts/. A substate's
|
|
80
|
+
# drawing is axis-prefixed (step-register, callState-idle) and named
|
|
81
|
+
# by its `layout:` key, so layouts/ reads at a glance.
|
|
81
82
|
```
|
|
82
83
|
|
|
83
84
|
Each layout = a complete arrangement: `$include: components/core` + that state's
|