@neocompose/cli 0.19.3 → 0.19.5

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.
@@ -1,46 +1,60 @@
1
1
  ---
2
2
  name: neocompose-cli
3
3
  description: >-
4
- Edit and synchronize a Neo Compose format-4 project as native .neo and
5
- .neoflow source plus tracked image/audio files. Use for classes, interfaces,
6
- enums, members, values, templates, localization, dialogue flows, files,
7
- migrations, NeoScript, or batch content edits. The working copy is a neo/
8
- directory; run `neo` from the published package or `node cli/bin/neo.mjs` in
9
- the web repository.
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
- # Neo Compose native project-source CLI (`neo`)
12
+ <!-- reviewed-through-cli: 0.19.5 -->
13
13
 
14
- A format-4 `neo/` working copy is a Git-like checkout of one Neo Compose
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
- There is no C# schema project, Roslyn compiler, authoring DLL, .NET discovery,
21
- generated SDK, per-member script sidecar tree, `ValueRegistry`, or checked-in
22
- dialogue JSON source format.
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
- ## Non-negotiable workflow
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
- 1. Run `neo pull` before editing and again before pushing after time has passed.
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
- **Pull followed immediately by status or push dry-run must be a semantic
36
- no-op.** If it reports project changes, stop and report a round-trip bug rather
37
- than pushing it.
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 upgrade; never preserve them
41
- through opaque JSON or guessed syntax.
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
- ## Setup and clean-break format
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
- Repository development uses `node cli/bin/neo.mjs`. Agents should pass
53
- explicit flags and IDs. Interactive terminals may show pickers and confirms;
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
- `neo.json` must contain `formatVersion: 4`. The compiler has no format-3 C#
57
- reader or upgrader. Push intended format-3 edits with the old CLI, preserve
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
- ```sh
61
- neo init --project <id> [--version <id>] [--dir neo]
62
- ```
72
+ For an existing checkout:
63
73
 
64
- For an existing format-4 workspace, `neo pull --reset` regenerates canonical
65
- source, readable dialogue names, and managed binaries. `neo pull --force`
66
- discards local edits in favor of the server and also regenerates readable
67
- dialogue names. Use neither casually. A normal
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
- Neo never installs, discovers, or requires .NET. `neo doctor` validates the
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` is then the
77
- single source of truth, and branch/version switches update it. Do not duplicate
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
- ## Working-copy layout
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
- `.neo/` is ignored private state. Normal pull preserves declaration placement
109
- and spelling by stable ID when possible. Force and reset derive readable
110
- dialogue names from authoritative records; reset may also regroup source into
111
- the canonical layout.
112
-
113
- ## Native project declarations
114
-
115
- Classes, interfaces, and enums are legal only at the top level of definition
116
- `.neo` files. Do not declare them inside another type, function, accessor,
117
- lambda, initializer, migration, or `.neoflow`. A `.neoflow` file has the one
118
- deliberate exception described below.
119
-
120
- Use the shared typed declaration grammar for every top-level project object:
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
- There are no lowercase record-kind declaration keywords such as `project`,
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
- - Plain, `virtual`, `abstract`, and `override` declarations carry their native
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
- @id("capitol-value-id")
273
- new {
274
- Name = "Capitol",
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 replaceable stored binding; the initializer ID is
288
- the actual value row. A computed static getter is an alias and creates no
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
- Logical project references use the native `Reference` intrinsic:
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 members and
364
- `AudioClips.SwordHit` for audio members. The annotation is the project-file ID;
365
- renaming/moving local presentation or replacing bytes retains it.
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
- Pull performs a record-aware three-way merge by stable identity:
184
+ ## Resolve conflicts by identity
516
185
 
517
- - unchanged local accepts server authored fields;
518
- - unchanged server retains local edits;
519
- - disjoint fields/children merge;
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 intentionally fails compilation. Edit the desired final 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. Never seek a force-CAS bypass.
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 writes only the semantic diff. One accepted push atomically
529
- commits records, main-locale text, stored bindings, and staged file metadata
530
- under CAS. Server compilation independently derives IR, linked values,
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
- The npm package bundles the Node orchestrator and shared browser-safe
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
- A change is incomplete until focused tests, typecheck, builds, package-content
612
- and contract checks, Monaco/browser parity, VSIX packaging and interactive
613
- testing, sample no-op verification, and final `npm run doctor` succeed.
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.