@neocompose/cli 0.19.3 → 0.19.4
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 +125 -0
- package/README.md +17 -12
- package/dist/neo.mjs +10271 -190
- package/package.json +2 -1
- package/skills/neocompose-cli/SKILL.md +115 -524
- package/skills/neocompose-cli/agents/openai.yaml +4 -0
- package/skills/neocompose-cli/references/animation-and-world-authoring.md +218 -0
- package/skills/neocompose-cli/references/cli-development.md +110 -0
- package/skills/neocompose-cli/references/commands-and-sync.md +130 -0
- package/skills/neocompose-cli/references/declarations-and-construction.md +217 -0
- package/skills/neocompose-cli/references/neoscript.md +150 -0
- package/skills/neocompose-cli/references/values-identities-and-references.md +158 -0
|
@@ -1,46 +1,60 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: neocompose-cli
|
|
3
3
|
description: >-
|
|
4
|
-
Edit and synchronize
|
|
5
|
-
.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
the
|
|
4
|
+
Edit, validate, and synchronize Neo Compose format-4 projects with the `neo`
|
|
5
|
+
CLI. Use when working in a `neo/` checkout or with `.neo`/`.neoflow` source:
|
|
6
|
+
schema declarations, constructors, values and references, world objects and
|
|
7
|
+
animation, dialogue, NeoScript, localization, migrations, managed image or
|
|
8
|
+
audio files, branches and releases, or batch repair. Run `neo` from
|
|
9
|
+
`@neocompose/cli` or `node cli/bin/neo.mjs` in the neo-compose repository.
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
<!-- reviewed-through-cli: 0.19.4 -->
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
project version. Native `.neo`/`.neoflow` source and the web UI are peer
|
|
16
|
-
authoring surfaces over the same granular records; the server remains the
|
|
17
|
-
shared source of truth. The complete contract is
|
|
18
|
-
`https://github.com/ryanbliss/neo-compose-specs/tree/main/new-features/complete/neo-project-source-authoring.md`.
|
|
14
|
+
# Neo Compose CLI
|
|
19
15
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
Treat a format-4 `neo/` directory as a Git-like checkout of one Neo Compose
|
|
17
|
+
project version. Native source and the web UI are peer authoring surfaces over
|
|
18
|
+
the same granular records; the server remains the shared source of truth.
|
|
23
19
|
|
|
24
|
-
|
|
20
|
+
Do not look for a C# schema project, Roslyn compiler, authoring DLL, generated
|
|
21
|
+
SDK, `ValueRegistry`, per-member script sidecars, or checked-in dialogue JSON.
|
|
22
|
+
Those are not part of format 4.
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
2. Edit tracked `.neo`, `.neoflow`, and managed binary files. Never edit
|
|
28
|
-
`.neo/state.json`, analysis caches, or `.neo/conflicts` as source.
|
|
29
|
-
3. Run `neo status` and `neo diff`; inspect every change, including changes
|
|
30
|
-
already present when the task began.
|
|
31
|
-
4. Run `neo dialogue dryrun <ref>` after dialogue changes and
|
|
32
|
-
`neo push --dry-run` before every real push.
|
|
33
|
-
5. Push one reviewed atomic change with `neo push`.
|
|
24
|
+
## Follow the safe workflow
|
|
34
25
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
26
|
+
1. Run `neo pull` before editing. Pull again before pushing if time has passed.
|
|
27
|
+
2. Edit tracked `.neo`, `.neoflow`, and managed binary files only. Never edit
|
|
28
|
+
`.neo/state.json`, analysis caches, or `.neo/conflicts` as source.
|
|
29
|
+
3. Run `neo status` and `neo diff`. Review every change, including changes that
|
|
30
|
+
existed before the task began.
|
|
31
|
+
4. Run `neo dialogue dryrun <ref>` after dialogue changes.
|
|
32
|
+
5. Run `neo push --dry-run` before every real push. It performs the full local
|
|
33
|
+
bundle, ID-assignment, transport-shape, hash, and repeat-verification path,
|
|
34
|
+
then rehearses the server's preparation phase — schema-commit
|
|
35
|
+
materialization and whole-graph validation — against the pulled document,
|
|
36
|
+
without prompting, uploading, committing, or rewriting the checkout.
|
|
37
|
+
6. Push one reviewed atomic change with `neo push`.
|
|
38
|
+
|
|
39
|
+
Treat pull followed immediately by status or dry-run as a semantic no-op. If it
|
|
40
|
+
reports project changes, stop and report a round-trip bug instead of pushing.
|
|
38
41
|
|
|
39
42
|
The compiler reports file:line:column diagnostics and fails closed. Unknown
|
|
40
|
-
authorable server fields require a CLI contract
|
|
41
|
-
|
|
43
|
+
authorable server fields require a CLI contract update; never preserve them as
|
|
44
|
+
opaque JSON or guessed syntax.
|
|
45
|
+
|
|
46
|
+
## Honor hard authoring invariants
|
|
42
47
|
|
|
43
|
-
|
|
48
|
+
- Never add `@system` to project-authored declarations, members, overrides,
|
|
49
|
+
values, files, or other records. `@system` is reserved for platform-owned
|
|
50
|
+
system records; authoring it can make the record uneditable.
|
|
51
|
+
- Preserve existing `@id` annotations through rename, reorder, and file moves.
|
|
52
|
+
- Do not add a second `@id` to a root or static member initializer. The member
|
|
53
|
+
ID owns that stored binding. Nested class/list rows still carry their own IDs.
|
|
54
|
+
- Treat `.neo/` as private state and the source bundle as the authorable input.
|
|
55
|
+
- Never seek a force-CAS bypass. Pull, resolve, review, and retry.
|
|
56
|
+
|
|
57
|
+
## Set up a format-4 workspace
|
|
44
58
|
|
|
45
59
|
```sh
|
|
46
60
|
npm i -g @neocompose/cli
|
|
@@ -49,35 +63,26 @@ neo init --project <id> [--version <id>] [--dir neo]
|
|
|
49
63
|
neo doctor
|
|
50
64
|
```
|
|
51
65
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
non-TTY/CI operation must never wait for input.
|
|
66
|
+
Use `node cli/bin/neo.mjs` while developing the repository. Pass explicit flags
|
|
67
|
+
and IDs in automation. Non-TTY/CI commands never wait for pickers or confirms.
|
|
55
68
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
any local source needed for reference, then reconstruct authoritative records:
|
|
69
|
+
Require `formatVersion: 4` in `neo.json`. There is no format-3 reader or
|
|
70
|
+
upgrader. Reconstruct an authoritative format-4 checkout with `neo init`.
|
|
59
71
|
|
|
60
|
-
|
|
61
|
-
neo init --project <id> [--version <id>] [--dir neo]
|
|
62
|
-
```
|
|
72
|
+
For an existing checkout:
|
|
63
73
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
`neo pull --regenerate-source-names` refreshes only source naming as local
|
|
69
|
-
authored changes while retaining normal merge behavior.
|
|
74
|
+
- `neo pull --reset` regenerates canonical source, names, and managed binaries.
|
|
75
|
+
- `neo pull --force` discards local edits in favor of the server.
|
|
76
|
+
- `neo pull --regenerate-source-names` refreshes readable source names while
|
|
77
|
+
retaining normal merge behavior.
|
|
70
78
|
|
|
71
|
-
|
|
72
|
-
format-4 browser compiler, VS Code analysis contract/cache, source bundle, and
|
|
73
|
-
managed-file capabilities.
|
|
79
|
+
Use reset and force only when their destructive effect is intended.
|
|
74
80
|
|
|
75
81
|
In Unity projects, `neo.json` may use `unityConfigPath` instead of
|
|
76
|
-
`projectId`/`versionId`. The referenced `NeoComposeConfig.asset`
|
|
77
|
-
|
|
78
|
-
the IDs in `neo.json` in this mode.
|
|
82
|
+
`projectId`/`versionId`. The referenced `NeoComposeConfig.asset` then owns those
|
|
83
|
+
IDs and is updated by branch/version switches; do not duplicate them.
|
|
79
84
|
|
|
80
|
-
##
|
|
85
|
+
## Recognize the workspace
|
|
81
86
|
|
|
82
87
|
```text
|
|
83
88
|
neo/
|
|
@@ -91,33 +96,42 @@ neo/
|
|
|
91
96
|
Templates/
|
|
92
97
|
Localization.neo
|
|
93
98
|
LocalizationStatuses/
|
|
94
|
-
Files/
|
|
95
|
-
Images.neo
|
|
96
|
-
AudioClips.neo
|
|
97
|
-
Images/
|
|
98
|
-
AudioClips/
|
|
99
|
+
Files/{Images.neo,AudioClips.neo,Images/,AudioClips/}
|
|
99
100
|
DialogueGroups/
|
|
100
101
|
Dialogues/*.neoflow
|
|
101
102
|
Migrations/*.neo
|
|
102
|
-
.neo/
|
|
103
|
-
state.json
|
|
104
|
-
conflicts/
|
|
103
|
+
.neo/{state.json,conflicts/}
|
|
105
104
|
```
|
|
106
105
|
|
|
107
106
|
All non-hidden `.neo`, `.neoflow`, and supported managed binaries are tracked.
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
the
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
107
|
+
Normal pull preserves declaration placement and spelling by stable ID when it
|
|
108
|
+
can. Force/reset may regroup source into the canonical layout.
|
|
109
|
+
|
|
110
|
+
## Load the focused contract you need
|
|
111
|
+
|
|
112
|
+
Read only the references relevant to the task, but read each selected reference
|
|
113
|
+
completely before editing:
|
|
114
|
+
|
|
115
|
+
- [Declarations and construction](references/declarations-and-construction.md):
|
|
116
|
+
classes, interfaces, enums, annotations, storage, required/header
|
|
117
|
+
constructors, declared constructors, settlement, and inheritance.
|
|
118
|
+
- [Values, identities, and references](references/values-identities-and-references.md):
|
|
119
|
+
root/static bindings, nested/list identities, same-push references, unique
|
|
120
|
+
keys, files, localization, and default materialization.
|
|
121
|
+
- [Animation and world authoring](references/animation-and-world-authoring.md):
|
|
122
|
+
objects, sorting, optional children, structured leaves, partial animation
|
|
123
|
+
overrides, segments, tracks, and NeoFlow dialogues.
|
|
124
|
+
- [NeoScript](references/neoscript.md): inline bodies, snippets, nullability,
|
|
125
|
+
loops, switch, try/catch, ownership, evaluation, and migrations.
|
|
126
|
+
- [Commands and synchronization](references/commands-and-sync.md): pull/push,
|
|
127
|
+
conflicts, low-level repair, branches, releases, and history.
|
|
128
|
+
- [CLI development](references/cli-development.md): implementation source of
|
|
129
|
+
truth, shipped/proposed spec status, package boundaries, release maintenance,
|
|
130
|
+
and repository verification.
|
|
131
|
+
|
|
132
|
+
## Use native typed source
|
|
133
|
+
|
|
134
|
+
Declare top-level project objects with typed declarations:
|
|
121
135
|
|
|
122
136
|
```neo
|
|
123
137
|
Type Name = expression;
|
|
@@ -126,225 +140,28 @@ Type Name = new(arguments) {
|
|
|
126
140
|
}
|
|
127
141
|
```
|
|
128
142
|
|
|
129
|
-
|
|
130
|
-
`values`, `files`, or `relations`.
|
|
131
|
-
|
|
132
|
-
Schema identifiers are persisted names; there is no separate technical/display
|
|
133
|
-
name. `@id` preserves stable persisted identity. Keep assigned IDs through
|
|
134
|
-
rename, reorder, and file moves. Omit an ID only to create a record: a
|
|
135
|
-
successful real push assigns it and canonically inserts the annotation.
|
|
136
|
-
Dry-run, validation failure, upload failure, and CAS failure leave tracked
|
|
137
|
-
source unchanged.
|
|
138
|
-
|
|
139
|
-
```neo
|
|
140
|
-
@id("enum-item-rarity-id")
|
|
141
|
-
enum ItemRarity {
|
|
142
|
-
@id("rarity-common-id")
|
|
143
|
-
Common = "Common text",
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
@id("interface-named-id")
|
|
147
|
-
interface INamed {
|
|
148
|
-
@id("named-name-id")
|
|
149
|
-
string Name { get; }
|
|
150
|
-
|
|
151
|
-
@id("named-use-id")
|
|
152
|
-
void Use(SomeClass context);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
@id("class-inventory-item-id")
|
|
156
|
-
@storage(allowed: .Immutable)
|
|
157
|
-
abstract class InventoryItem<
|
|
158
|
-
@id("inventory-context-generic-id")
|
|
159
|
-
TContext extends SomeClass
|
|
160
|
-
> : INamed {
|
|
161
|
-
@id("item-name-id")
|
|
162
|
-
@settings(localizable: true, searchKey: true)
|
|
163
|
-
virtual string Name = "";
|
|
164
|
-
|
|
165
|
-
@id("item-rarity-id")
|
|
166
|
-
abstract ItemRarity Rarity;
|
|
167
|
-
|
|
168
|
-
@id("item-stack-size-id")
|
|
169
|
-
@settings(min: 1, max: 999)
|
|
170
|
-
int StackSize = 1;
|
|
171
|
-
|
|
172
|
-
@id("item-display-name-id")
|
|
173
|
-
string DisplayName {
|
|
174
|
-
get {
|
|
175
|
-
return $"{Name} ({Rarity})";
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
@id("item-use-id")
|
|
180
|
-
abstract void Use(TContext context);
|
|
181
|
-
|
|
182
|
-
@id("item-load-id")
|
|
183
|
-
native async bool Load(TContext context);
|
|
184
|
-
}
|
|
185
|
-
```
|
|
143
|
+
Do not invent lowercase persistence-kind declarations such as `project`,
|
|
144
|
+
`values`, `files`, or `relations`. Named arguments and contextual enum literals
|
|
145
|
+
come from generated contracts; do not invent raw fields or property bags.
|
|
186
146
|
|
|
187
|
-
|
|
188
|
-
meaning.
|
|
189
|
-
- Interfaces are non-generic. Class generics and constraints use ordinary Neo
|
|
190
|
-
syntax; do not put `@id` on inferred list/dictionary type entries.
|
|
191
|
-
- Field type and nullability determine member kind and optionality; the
|
|
192
|
-
initializer is the default.
|
|
193
|
-
- Inline getters, setters, and implemented functions are NeoScript. A bodyless
|
|
194
|
-
function must be `abstract`, `native`, or an interface contract. `async`
|
|
195
|
-
alone does not make a bodyless function valid.
|
|
196
|
-
- `@settings(...)` and `@storage(...)` are context-aware typed contracts.
|
|
197
|
-
Numeric limits are numeric literals. A class supports only
|
|
198
|
-
`@storage(allowed: ...)`; storage keys are member-only.
|
|
199
|
-
- Keep focused annotations such as `@locked`, `@hidden`, `@system`, and
|
|
200
|
-
`@relations` separate from contextual settings. `@hidden` is class-only and
|
|
201
|
-
hides the class from the member selector and classes tree; absent is visible.
|
|
202
|
-
- `@index(member: Field, unique: ...)` and
|
|
203
|
-
`@column(member: Field, width: ..., hidden: ..., frozen: ..., wrapContent: ...)`
|
|
204
|
-
are repeatable List annotations. `member:` is a bare field name on the entry
|
|
205
|
-
class — an index needs a non-localizable `string` or a single-select enum,
|
|
206
|
-
and a column may also target the reserved `__other__` key.
|
|
207
|
-
- `.EnumCase` works only when one expected enum type is known. Use explicit
|
|
208
|
-
`EnumType.Case` when context is ambiguous.
|
|
209
|
-
- Defaults and settings must be statically analyzable. Project source is parsed
|
|
210
|
-
and compiled, never executed to discover declarations.
|
|
211
|
-
|
|
212
|
-
`@relations(...)` belongs on the source class for specialized direct
|
|
213
|
-
relations. `Relations.neo` holds generic project relations as typed top-level
|
|
214
|
-
declarations. Structurally owned descriptors and concrete generic bindings
|
|
215
|
-
derive their identities from stable owner roles; do not invent `@id`
|
|
216
|
-
annotations for them.
|
|
217
|
-
|
|
218
|
-
For world layer links, `NeoTileLayerLink` and `NeoObjectLayerLink` are abstract,
|
|
219
|
-
relation-free system bases. Instantiate a concrete project-authored descendant
|
|
220
|
-
that resolves exactly one `targetLayer` relation, declared directly or inherited
|
|
221
|
-
from a project-authored ancestor. The class relation is the complete binding:
|
|
222
|
-
native source emits no value-level target metadata, and link values carry no
|
|
223
|
-
second persistence representation. Painting and other world-content writes
|
|
224
|
-
never create or repair link targets.
|
|
225
|
-
|
|
226
|
-
## Root and authored values
|
|
227
|
-
|
|
228
|
-
`Root.neo` contains a compiler-owned envelope of this form:
|
|
229
|
-
|
|
230
|
-
```neo
|
|
231
|
-
// Root member declarations are read only. Their values are editable.
|
|
232
|
-
|
|
233
|
-
Root root = new() {
|
|
234
|
-
@id("root-assets-member-id")
|
|
235
|
-
@locked
|
|
236
|
-
@storage(allowed: .Immutable)
|
|
237
|
-
Assets Assets =
|
|
238
|
-
@id("assets-root-value-id")
|
|
239
|
-
new() {
|
|
240
|
-
Outposts = [Assets.Capitol],
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
@id("root-save-member-id")
|
|
244
|
-
@locked
|
|
245
|
-
@storage(allowed: .Save)
|
|
246
|
-
Save Save =
|
|
247
|
-
@id("save-root-value-id")
|
|
248
|
-
new();
|
|
249
|
-
|
|
250
|
-
@id("root-session-member-id")
|
|
251
|
-
@locked
|
|
252
|
-
@storage(allowed: .Session)
|
|
253
|
-
Session Session =
|
|
254
|
-
@id("session-root-value-id")
|
|
255
|
-
new();
|
|
256
|
-
}
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
The language service protects the root member name, type, member ID, lock, and
|
|
260
|
-
storage metadata. Edit only the value initializer after `=`. The three root
|
|
261
|
-
values use the same nested construction syntax as any other authored value.
|
|
262
|
-
|
|
263
|
-
Reusable values are stored static members on real classes; there is no
|
|
264
|
-
`ValueRegistry` or compiler-defined `Values` global:
|
|
147
|
+
Reusable values are static members on ordinary project classes:
|
|
265
148
|
|
|
266
149
|
```neo
|
|
267
150
|
@id("assets-class-id")
|
|
268
151
|
@storage(allowed: .Immutable)
|
|
269
152
|
class Assets {
|
|
270
153
|
@id("capitol-member-id")
|
|
271
|
-
static Outpost Capitol =
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
Image = Images.Capitol.Slice(0),
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
@id("home-getter-id")
|
|
279
|
-
static Outpost Home {
|
|
280
|
-
get {
|
|
281
|
-
return Assets.Capitol;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
154
|
+
static Outpost Capitol = new {
|
|
155
|
+
Name = "Capitol",
|
|
156
|
+
Image = Images.Capitol.Slice(0),
|
|
157
|
+
};
|
|
284
158
|
}
|
|
285
159
|
```
|
|
286
160
|
|
|
287
|
-
The member ID anchors the
|
|
288
|
-
|
|
289
|
-
binding or value row. Values may also live directly on their domain class.
|
|
290
|
-
|
|
291
|
-
Omitted members materialize the current defaults once during creation. Later
|
|
292
|
-
default changes do not mutate existing values. Removing an explicitly
|
|
293
|
-
materialized field from an existing initializer requests reset through the
|
|
294
|
-
current default, which must appear in `neo diff`.
|
|
295
|
-
|
|
296
|
-
List items use native inline identity, never a wrapper:
|
|
297
|
-
|
|
298
|
-
```neo
|
|
299
|
-
Tags = [
|
|
300
|
-
@id("story-tag-item-id")
|
|
301
|
-
"story",
|
|
302
|
-
|
|
303
|
-
@id("key-tag-item-id")
|
|
304
|
-
"key",
|
|
305
|
-
];
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
A bare new element is pending-create shorthand. The compiler never matches an
|
|
309
|
-
existing ordered item by index or payload.
|
|
161
|
+
The member ID anchors the stored binding. A computed static getter is an alias
|
|
162
|
+
and creates no stored binding or value row.
|
|
310
163
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
```neo
|
|
314
|
-
Reference(Assets.Capitol)
|
|
315
|
-
Reference<Outpost>(id: "capitol-value-id")
|
|
316
|
-
Reference<CapitolColdBoot>(CapitolColdBoot)
|
|
317
|
-
Reference<Dialogue>(id: "capitol-dialogue-id")
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
Use symbols when available. Use the generic ID overload only when the ID is
|
|
321
|
-
the target information. The expected lookup/dialogue member contract still
|
|
322
|
-
validates collection membership, multiplicity, group, and assignability.
|
|
323
|
-
|
|
324
|
-
Localizable string initializers contain the main-locale text, not a
|
|
325
|
-
localized-text ID. Pull/lower preserves other locales, comments, statuses,
|
|
326
|
-
archive state, and unrelated links.
|
|
327
|
-
|
|
328
|
-
## Project configuration
|
|
329
|
-
|
|
330
|
-
Use typed top-level globals for configuration:
|
|
331
|
-
|
|
332
|
-
- `Project Project = new(...)` for project defaults;
|
|
333
|
-
- `TextureTemplate` and `AudioTemplate` declarations under `Templates/`;
|
|
334
|
-
- `LocalizationStatus` and `Localization` declarations;
|
|
335
|
-
- `PriorityGroup Name = new() { PriorityOption ... }`;
|
|
336
|
-
- `DialogueGroup Name = new(...)`, with inline functions/conditions when
|
|
337
|
-
supported;
|
|
338
|
-
- typed generic relation globals in `Relations.neo`.
|
|
339
|
-
|
|
340
|
-
Named arguments and contextual enum literals come from generated contracts.
|
|
341
|
-
Do not invent raw persistence fields or generic property bags. Translations
|
|
342
|
-
other than the main locale, layout, compiled IR, hashes, storage stamps, and
|
|
343
|
-
upload metadata are not project source.
|
|
344
|
-
|
|
345
|
-
## Project files
|
|
346
|
-
|
|
347
|
-
Typed editable registries own project files:
|
|
164
|
+
Declare managed files in typed registries:
|
|
348
165
|
|
|
349
166
|
```neo
|
|
350
167
|
ImageRegistry Images = new() {
|
|
@@ -360,254 +177,28 @@ AudioClipRegistry AudioClips = new() {
|
|
|
360
177
|
}
|
|
361
178
|
```
|
|
362
179
|
|
|
363
|
-
Use `Images.Sword.Slice(0)` for sprite
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
A supported binary dropped under `Files/Images/` or `Files/AudioClips/` is a
|
|
368
|
-
pending file. The language service injects a provisional deterministic symbol.
|
|
369
|
-
Status/diff/dry-run do not change registry source; a successful push creates
|
|
370
|
-
the record, uploads verified bytes, and materializes the declaration and ID.
|
|
371
|
-
`neo files add <path> [--template <Name>]` may scaffold that declaration
|
|
372
|
-
explicitly before push.
|
|
373
|
-
|
|
374
|
-
Pull and push use server-verified SHA-256, not storage ETags. Divergent
|
|
375
|
-
local/remote changes keep local bytes and write the verified remote side under
|
|
376
|
-
`.neo/conflicts/files/<file-id>/`. A missing binary with a retained declaration
|
|
377
|
-
is an error; remove the declaration to request deletion.
|
|
378
|
-
|
|
379
|
-
## NeoFlow dialogues
|
|
380
|
-
|
|
381
|
-
Each `.neoflow` contains exactly one top-level `sealed class Name : Dialogue`.
|
|
382
|
-
It must be non-generic, directly derive from the compiler-owned system
|
|
383
|
-
`Dialogue`, and override the required `Trigger Trigger` exactly once. No other
|
|
384
|
-
class, interface, enum, nested type, or local type is legal in NeoFlow.
|
|
385
|
-
|
|
386
|
-
```neoflow
|
|
387
|
-
@id("capitol-dialogue-id")
|
|
388
|
-
@settings(
|
|
389
|
-
name: "Capitol: cold boot",
|
|
390
|
-
description: "Capitol recognizes a returning player.",
|
|
391
|
-
saveOptionChoices: true
|
|
392
|
-
)
|
|
393
|
-
sealed class CapitolColdBoot : Dialogue {
|
|
394
|
-
@primary Outpost capitol = Assets.Capitol;
|
|
395
|
-
Player player = Player.Current;
|
|
396
|
-
|
|
397
|
-
@id("can-start-function-id")
|
|
398
|
-
bool CanStart() {
|
|
399
|
-
return !player.HasSeenCapitol;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
@id("trigger-id")
|
|
403
|
-
override Trigger Trigger = new(
|
|
404
|
-
group: CapitolDialogues.High,
|
|
405
|
-
when: [
|
|
406
|
-
@id("can-start-use-id")
|
|
407
|
-
CanStart,
|
|
408
|
-
]
|
|
409
|
-
) => Welcome;
|
|
410
|
-
|
|
411
|
-
@id("welcome-node-id")
|
|
412
|
-
Text Welcome = new(name: "Welcome!") {
|
|
413
|
-
"""
|
|
414
|
-
Hello, {player.Name}. Welcome to {capitol.Name}.
|
|
415
|
-
"""
|
|
416
|
-
|
|
417
|
-
@id("continue-option-id")
|
|
418
|
-
Option Continue = new() {
|
|
419
|
-
"""
|
|
420
|
-
Tell me more.
|
|
421
|
-
"""
|
|
422
|
-
return Remember;
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
@id("remember-node-id")
|
|
427
|
-
Actions Remember = new() {
|
|
428
|
-
@id("seen-mutation-id")
|
|
429
|
-
player.HasSeenCapitol = true;
|
|
430
|
-
|
|
431
|
-
@id("pause-id")
|
|
432
|
-
Pause(reason: "remember", duration: 0.5);
|
|
433
|
-
|
|
434
|
-
return Finish;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
@id("finish-node-id")
|
|
438
|
-
Text Finish = new() {
|
|
439
|
-
"""
|
|
440
|
-
Until next time.
|
|
441
|
-
"""
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
```
|
|
445
|
-
|
|
446
|
-
- `@primary` explicitly selects the primary value. Ordinary typed bindings are
|
|
447
|
-
manual linked values. Primary is never inferred.
|
|
448
|
-
- Dialogue bindings are visible throughout the flow; node bindings are scoped
|
|
449
|
-
to that node and its owned children.
|
|
450
|
-
- Direct and transitive function references derive Logic linked values by
|
|
451
|
-
stable dependency graph. Do not add redundant manual links.
|
|
452
|
-
- Inline functions are reusable. A function definition has one ID; every
|
|
453
|
-
persisted condition use, action invocation, mutation, and pause has its own
|
|
454
|
-
owner-scoped ID.
|
|
455
|
-
- Triple-quoted prose is main-locale localized text with typed interpolation.
|
|
456
|
-
- Graph constructor bodies are NeoFlow-only optional escaping trailing bodies
|
|
457
|
-
with contextual `void | Node`. `=> Next` is the concise form; a block may
|
|
458
|
-
finish with `return Next;`. There is no authorable `to:` argument.
|
|
459
|
-
- Triggers, options, and outcomes require a node destination. Terminal text and
|
|
460
|
-
actions may fall through. Text with options and conditions with outcomes do
|
|
461
|
-
not also return a direct destination.
|
|
462
|
-
- `Actions Empty = new();` is valid. Put all action statements inside its body.
|
|
463
|
-
- Web layout and compiled IR are derived state and do not dirty source.
|
|
464
|
-
|
|
465
|
-
After dialogue edits:
|
|
466
|
-
|
|
467
|
-
```sh
|
|
468
|
-
neo dialogue dryrun <dialogue-ref>
|
|
469
|
-
```
|
|
470
|
-
|
|
471
|
-
The dry run traverses option paths from a fresh save and applies runtime
|
|
472
|
-
mutation-ownership rules. Exit 1 means the graph would fail on device. Whole
|
|
473
|
-
dialogue JSON export/apply is not a supported source workflow. Low-level
|
|
474
|
-
`neo dialogue compile` remains available for repair/inspection of a logic
|
|
475
|
-
block.
|
|
476
|
-
|
|
477
|
-
## Inline NeoScript and snippets
|
|
478
|
-
|
|
479
|
-
Computed getters, setters, functions, dialogue logic, and group logic are
|
|
480
|
-
inline in their owning `.neo`/`.neoflow` declaration. The browser-safe shared
|
|
481
|
-
language service compiles them with the same project symbols used by Monaco,
|
|
482
|
-
VS Code, CLI, and the trusted server. There is no `Scripts/<Class>/<Member>.neo`
|
|
483
|
-
sidecar path.
|
|
484
|
-
|
|
485
|
-
Use snippet commands for focused checking and evaluation:
|
|
486
|
-
|
|
487
|
-
```sh
|
|
488
|
-
neo script check --all
|
|
489
|
-
neo script check --this Outpost --returns string 'return $"{this.Name}!";'
|
|
490
|
-
neo script check --mode setter --member ComputedName 'root.Session.Name = value;'
|
|
491
|
-
neo script check --mode nsfunction --member Outpost.RefreshUnlock 'return this.Level > 0;'
|
|
492
|
-
neo script compile --mode nsfunction --member Outpost.RefreshUnlock 'return this.Level > 0;'
|
|
493
|
-
neo script eval --returns string 'return root.Assets.Outposts[0].FullDisplayText;'
|
|
494
|
-
neo script eval --function Outpost.RefreshUnlock --this-value <id> --args '[3]'
|
|
495
|
-
neo script apply --mode action '...'
|
|
496
|
-
```
|
|
497
|
-
|
|
498
|
-
`eval` uses authored values and the same evaluator as the web app. `apply`
|
|
499
|
-
previews ordered write intents unless its explicit command contract supports a
|
|
500
|
-
commit. Prefer `--json` for automation. Runtime ownership—not body kind—decides
|
|
501
|
-
whether a write target is Immutable, Save, Session, or otherwise writable.
|
|
502
|
-
|
|
503
|
-
Migrations remain tracked NeoScript action files under `Migrations/`:
|
|
504
|
-
|
|
505
|
-
```sh
|
|
506
|
-
neo migrate new <name> --target <ClassName|project>
|
|
507
|
-
neo migrate list
|
|
508
|
-
neo migrate check
|
|
509
|
-
neo migrate run [--dry-run] [--skip-invalid]
|
|
510
|
-
neo migrate prune
|
|
511
|
-
```
|
|
512
|
-
|
|
513
|
-
## Conflicts, identity, and push
|
|
180
|
+
Use `Images.Sword.Slice(0)` for a sprite and `AudioClips.SwordHit` for audio.
|
|
181
|
+
Renaming the registry symbol, moving the local presentation, or replacing bytes
|
|
182
|
+
retains the file ID.
|
|
514
183
|
|
|
515
|
-
|
|
184
|
+
## Resolve conflicts by identity
|
|
516
185
|
|
|
517
|
-
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
- collection and graph children merge by ID, never index;
|
|
521
|
-
- conflicting scalar, edge, order, delete/edit, or binary changes are explicit.
|
|
186
|
+
Pull performs a record-aware three-way merge. Disjoint fields and children
|
|
187
|
+
merge; collection and graph children merge by ID, never by index. Conflicting
|
|
188
|
+
scalars, edges, order, delete/edit pairs, and binaries become explicit.
|
|
522
189
|
|
|
523
|
-
Conflict source
|
|
190
|
+
Conflict source deliberately fails compilation. Edit the desired final source,
|
|
524
191
|
or use `neo resolve --mine|--theirs` as a whole-side convenience. Then pull,
|
|
525
|
-
review, dry-run, and push again.
|
|
192
|
+
review, dry-run, and push again.
|
|
526
193
|
|
|
527
194
|
`neo push` sends the complete hashed source bundle for trusted server
|
|
528
|
-
recompilation but
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
placements, structural rows, and generic/storage stamps; client products are
|
|
532
|
-
not trusted.
|
|
533
|
-
|
|
534
|
-
Pending IDs are assigned only inside a successful real commit. Source and
|
|
535
|
-
`.neo/state.json` are rewritten only after acceptance. Dry-run never uploads,
|
|
536
|
-
allocates a durable ID, or changes tracked bytes.
|
|
537
|
-
|
|
538
|
-
For `base-hash-conflict`, pull, resolve, and retry. For
|
|
539
|
-
`version-bump-required`, inspect the classification and pass
|
|
540
|
-
`neo push --accept-bump` only when intended.
|
|
541
|
-
|
|
542
|
-
## Low-level content operations
|
|
543
|
-
|
|
544
|
-
Checked-in source is the normal way to author project values, files, and
|
|
545
|
-
dialogues. Low-level commands remain peer mutation surfaces for automation and
|
|
546
|
-
repair:
|
|
547
|
-
|
|
548
|
-
```sh
|
|
549
|
-
neo records query [--kind <recordKind>]
|
|
550
|
-
neo records get <kind> <id>
|
|
551
|
-
neo values list [memberId]
|
|
552
|
-
neo values get <valueId>
|
|
553
|
-
neo values set <valueId> '<raw-json-value>'
|
|
554
|
-
neo values bind <staticMemberId> <valueId>
|
|
555
|
-
neo values unbind <staticMemberId>
|
|
556
|
-
neo values create '<raw-json-value>' [--class <classId>] --bind <staticMemberId>
|
|
557
|
-
neo loc locales
|
|
558
|
-
neo loc list
|
|
559
|
-
neo loc set <textId> <locale> "text"
|
|
560
|
-
neo dialogue list
|
|
561
|
-
neo dialogue show <ref>
|
|
562
|
-
neo files list
|
|
563
|
-
```
|
|
564
|
-
|
|
565
|
-
Write verbs accept a JSON-array batch on stdin or `--file` where supported;
|
|
566
|
-
use one batch for atomic related changes. A `values set` payload is the raw
|
|
567
|
-
value, not `{ "value": ... }`. Static bind/unbind changes the live stored
|
|
568
|
-
binding. These commands do not rewrite local project source. After any
|
|
569
|
-
low-level or web write, run `neo pull` before further source edits or push.
|
|
570
|
-
|
|
571
|
-
## Branches, releases, and history
|
|
572
|
-
|
|
573
|
-
```sh
|
|
574
|
-
neo branch list
|
|
575
|
-
neo branch create <name> [--from <ref>]
|
|
576
|
-
neo branch switch <nameOrId>
|
|
577
|
-
neo branch refresh [--dry-run]
|
|
578
|
-
neo merge <branch> [--dry-run] [--migrate]
|
|
579
|
-
neo release cut [--bump major|minor|patch] [--dry-run]
|
|
580
|
-
neo history inspect
|
|
581
|
-
neo history log
|
|
582
|
-
```
|
|
583
|
-
|
|
584
|
-
Branches are copy-on-write forks. Releases are immutable snapshots; the
|
|
585
|
-
server derives a minimum compatibility bump and it may only be raised. Release
|
|
586
|
-
operations require the release login profile.
|
|
587
|
-
|
|
588
|
-
## Editor and implementation contract
|
|
589
|
-
|
|
590
|
-
The browser-safe `@neocompose/neoscript-language` service is shared by the
|
|
591
|
-
compiler, CLI, trusted server, web Monaco editor, and VS Code LSP. Do not add
|
|
592
|
-
language intelligence directly to an environment adapter.
|
|
593
|
-
|
|
594
|
-
Monaco and VS Code must share project-aware diagnostics, recovery completion,
|
|
595
|
-
hover, signature help, definitions, references, rename, symbols, semantic
|
|
596
|
-
tokens, code actions, and formatting for `.neo` and `.neoflow`. Project updates
|
|
597
|
-
must refresh context without accumulating stale providers. The VSIX is built
|
|
598
|
-
in-repo; marketplace publication is separate.
|
|
599
|
-
|
|
600
|
-
The Node CLI talks directly to authenticated Convex APIs and commits through
|
|
601
|
-
the session-gated CAS boundary. Tokens use the macOS Keychain/OS credential
|
|
602
|
-
store when available and a protected file only as fallback. Use
|
|
603
|
-
`NEO_COMPOSE_TOKEN` or `--token-stdin` in CI. The editor profile cannot publish
|
|
604
|
-
releases; server-enforced scopes are the security boundary.
|
|
195
|
+
recompilation but commits only the semantic diff under CAS. On
|
|
196
|
+
`base-hash-conflict`, pull and resolve. On `version-bump-required`, inspect the
|
|
197
|
+
classification and pass `neo push --accept-bump` only when intended.
|
|
605
198
|
|
|
606
|
-
|
|
607
|
-
compiler/editor assets. It must not contain a `.csproj`, `.cs`, `.dll`, Roslyn
|
|
608
|
-
application, .NET host metadata, or generated C# SDK. Package-content tests
|
|
609
|
-
enforce that boundary.
|
|
199
|
+
## Finish with evidence
|
|
610
200
|
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
201
|
+
Before a real push, report the files changed, the relevant `neo diff`, dialogue
|
|
202
|
+
dry-run results when applicable, and `neo push --dry-run`. Do not claim success
|
|
203
|
+
from parsing alone when construction, reference membership, runtime ownership,
|
|
204
|
+
or server verification is involved.
|