@neocompose/cli 0.77.1 → 0.79.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.77.1",
3
+ "version": "0.79.0",
4
4
  "description": "Neo Compose native project-source CLI with bidirectional sync.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -9,7 +9,7 @@ description: >-
9
9
  `@neocompose/cli` or `node cli/bin/neo.mjs` in the neo-compose repository.
10
10
  ---
11
11
 
12
- <!-- reviewed-through-cli: 0.77.1 -->
12
+ <!-- reviewed-through-cli: 0.79.0 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -38,10 +38,28 @@ NeoCellPattern neighbors = NeoCellPattern.Box(1, excluding: .Center);
38
38
  NeoCellPattern custom = new(offsets: [new(-1, 0), new(2, 1)]);
39
39
  ```
40
40
 
41
+ `NeoCellPattern` is an ordinary stored class, and the presets are stored
42
+ static values. A field that owns its pattern can't default to a preset or any
43
+ other stored static, because the static already owns that row. `neo status`
44
+ and push refuse `= NeoCellPattern.EightNeighbors` there. Call `.Clone()` to
45
+ give each instance its own copy, or declare `Reference<NeoCellPattern>` to
46
+ share the preset or any stored pattern:
47
+
48
+ ```neo
49
+ class Sprinkler : NeoObject {
50
+ public NeoCellPattern Reach = NeoCellPattern.EightNeighbors.Clone();
51
+ public Reference<NeoCellPattern> Splash = NeoCellPattern.Center;
52
+ }
53
+ ```
54
+
41
55
  On a placed `NeoObject`, `this.Cell` returns its grid cell.
42
56
  `this.GetObjects<Plant>(neighbors)` returns matching placed objects in the
43
57
  same grid, filtered to `Plant` and subclasses. `this.GetTile(neighbors)`
44
- returns the first tile found in pattern order, or null. These queries track
58
+ returns the first tile found in pattern order, or null.
59
+ `this.GetTiles(neighbors)` returns the tile at each occupied cell in pattern
60
+ order and skips empty cells. Each query takes an optional `offset` that shifts
61
+ the whole pattern, so `this.GetTiles(neighbors, new(0, 2))` queries around the
62
+ cell two rows up without building a translated pattern. These queries track
45
63
  empty cells as well as hits, so a getter updates when a new neighbor appears.
46
64
  Calling them on an unplaced object is an error.
47
65
 
@@ -51,10 +69,11 @@ Box, Cross, and Line include the origin by default. Use `excluding: .Center`
51
69
  to omit it. Line's `length` is the number of steps beyond the origin, so
52
70
  `Line(new(1, 0), 3)` contains four cells.
53
71
 
54
- The SDK uses the existing `NeoCellPattern` type and `GetCells(origin)` API.
55
- Replace old boolean factory arguments with `NeoCellPatternExcluding.None`
56
- or `.Center`. To retain Line's old default, pass
57
- `excluding: NeoCellPatternExcluding.Center` explicitly.
72
+ In C#, the generated `NeoCellPattern` implements `INeoCellPattern`, an
73
+ `IReadOnlyList<Vector2Int>` of offsets, and SDK grid helpers accept any
74
+ `INeoCellPattern`. Replace old boolean factory arguments with
75
+ `NeoCellPatternExcluding.None` or `.Center`. To retain Line's old default,
76
+ pass `excluding: NeoCellPatternExcluding.Center` explicitly.
58
77
 
59
78
  ## Tile placements
60
79
 
@@ -129,7 +129,7 @@ wrappers.
129
129
  The marker near the top of `SKILL.md` must exactly match the package version:
130
130
 
131
131
  ```html
132
- <!-- reviewed-through-cli: 0.77.1 -->
132
+ <!-- reviewed-through-cli: 0.79.0 -->
133
133
  ```
134
134
 
135
135
  The quoted version above is checked too, so this instruction cannot go stale
@@ -35,7 +35,8 @@ names with readable ones. It keeps chosen names and rewrites references. Use
35
35
  declaration file grouping already recorded by the workspace, while
36
36
  declarations with no prior placement use the canonical per-declaration layout. Use `--force` only to discard local edits for the server
37
37
  version. Normal pulls persist a transaction cursor and fetch only changed
38
- records. If the remote head is unchanged, pull preserves the local source
38
+ records. Dictionary string keys, including Unicode, round-trip through full and
39
+ incremental reads. If the remote head is unchanged, pull preserves the local source
39
40
  without compiling it; use `neo status` to inspect local edits and diagnostics.
40
41
  Pull ignores records whose hashes already match the saved server baseline,
41
42
  including echoes of this checkout's last push, and advances its cursor without
@@ -86,7 +87,9 @@ does not prompt, upload, commit, or rewrite source.
86
87
  Removing a class or collection declaration also deletes its hidden collection
87
88
  entry members, including nested lists and dictionaries. Replacing a collection
88
89
  with a scalar deletes its abandoned entries too. Entries used by surviving
89
- declarations are retained. Review these derived deletions in `neo status` or
90
+ declarations are retained. If a member is deleted locally before pulling remote
91
+ instance values, pull retains those values in the deletion plan; the next push
92
+ removes them with the member. Review these derived deletions in `neo status` or
90
93
  `neo diff` before pushing.
91
94
 
92
95
  Dry-run also compares the CLI's derived-edge extractor with the server version
@@ -91,7 +91,7 @@ Use context-aware typed annotations:
91
91
  - `@visibility(.Hidden)` only on a class; absence means visible.
92
92
  - Stateful settings use contextual enum cases, such as
93
93
  `@settings(format: .Plain)` and `@settings(kind: .Unordered)`.
94
- - `@settings(...)` before a `List` element or `Dictionary` value type
94
+ - `@settings(...)` before a `List` or `Set` element or `Dictionary` value type
95
95
  configures the collection's entries: `List<@settings(format: .Plain) string>`,
96
96
  `Dictionary<string, @settings(min: 0) int>`. Only the member that declares
97
97
  the collection spells them; overrides share its entries.
@@ -271,6 +271,36 @@ A `Reference<T>` member satisfies an interface `T Foo { get; }` or
271
271
 
272
272
  Constructor and function parameters use `Set<T>` for several selections.
273
273
 
274
+ ### Set members
275
+
276
+ `Set<T>` is an insertion-ordered collection that can't hold duplicates. The
277
+ element picks the storage:
278
+
279
+ - An enum `Set<Trait>` is a multi-select Enum.
280
+ - `Set<Reference<T>>` is a multi-select lookup.
281
+ - `Set<NeoDialogueReference>` is a multi-select dialogue lookup.
282
+ - Any other element makes an owned Set whose entries are rows, like a List.
283
+
284
+ An owned Set holds `bool`, `int`, `float`, `decimal`,
285
+ `@settings(format: .Plain) string`, vectors, `Color`, sprites, audio clips,
286
+ or a class. Elements can't be nullable, collections, variants, delegates,
287
+ actions, or interfaces. A class entry is unique by row, a value entry by its
288
+ value. `@index` and `@column` work as on a List; `kind:` is an error, since a
289
+ Set is always insertion-ordered.
290
+
291
+ ```neo
292
+ @index(member: Slug, kind: .Unique)
293
+ public Set<Quest> ActiveQuests = [];
294
+ public Set<int> LuckyNumbers = [3, 7];
295
+ public Set<Trait> Traits = [.Brave];
296
+ ```
297
+
298
+ `selection:` is an error everywhere: `Set<T>` selects many and `T` selects
299
+ one. The language server's quick fix rewrites `@settings(selection: .Multi)`
300
+ to a `Set<…>` type. A default literal that repeats a value fails `push`, and a
301
+ `List<Trait>` stays an ordered List of enum values. Queries over a Set, such
302
+ as `Where` and `OrderBy`, return a Set; `ToSet()` turns a List into one.
303
+
274
304
  Declare a multicast event as `NeoAction<…>`. Its value is an ordered listener
275
305
  set, so give it a bracketed list of method groups — never a lambda, and never
276
306
  a bare method group — and never mark it `required`: the rest state is the
@@ -535,6 +565,9 @@ Populated unordered member defaults, such as `@settings(kind: .Unordered)` with
535
565
  `List<Entry> Entries = [new Entry()]`, are constructed by an initializer for
536
566
  each instance. A declaration has no container value row to own unordered entries.
537
567
  Nested unordered lists already have container rows and retain their stored values.
568
+ A field, collection entry, or dictionary value can't place a value that
569
+ something else already owns, such as `Bag = [Shared.Pattern]` or a constructor
570
+ parameter stored in an ordinary field. Call `Clone()` or use `Reference`.
538
571
 
539
572
  A lookup's declared class must be assignable to the entry class of its
540
573
  `@settings(collection: Owner.Member)` collection. A subclass may narrow the
@@ -143,8 +143,8 @@ static function may be named as `Read` or qualified as `Holder.Read`/`Other.Read
143
143
  A qualified method group must name a static function. Method groups use the same
144
144
  signature rule as in a body, and a mismatch is reported at the declaration.
145
145
  A static field that names another static copies a value, as in
146
- `public static int B = Holder.A;`; a class, List, or Dictionary static shares
147
- the same object. A static delegate cannot copy another Class's delegate field,
146
+ `public static int B = Holder.A;`. A class, List, Set, or Dictionary static
147
+ already owns its value, so call `Clone()` or use `Reference`. A static delegate cannot copy another Class's delegate field,
148
148
  because its closure compiles in its own Class; wrap it as `() => Other.Call()`.
149
149
  Collection type arguments in constructions, such as `new Box<List<int>>([1, 2])`,
150
150
  work even when no field declares that collection type. Preparation includes the
@@ -356,9 +356,6 @@ count fails at runtime with `List.Repeat count must be non-negative; got
356
356
  still known. The value is evaluated exactly once, then the count exactly once,
357
357
  so side effects happen once regardless of `count`, and every entry is that one
358
358
  value: for a reference-typed entry the same reference repeats `count` times.
359
- Materializing the list charges `count` to the shared collection-entries
360
- execution budget, so a runaway count fails with the existing budget error
361
- before allocating.
362
359
 
363
360
  ## Conditional and logical operators
364
361
 
@@ -422,7 +419,6 @@ and `Select((key, value) => key)`. String-keyed dictionaries yield strings.
422
419
  Use `break` for the nearest loop or switch and `continue` for the nearest
423
420
  loop. A `continue` inside a switch nested in a loop continues the loop.
424
421
 
425
- All nested loops and called NeoScript bodies share a 10,000-iteration budget.
426
422
  A `while` loop checks its boolean condition before each iteration. A
427
423
  `do / while` loop runs its body once before checking the condition. Both
428
424
  require braces; the `do / while` condition ends with a semicolon.
@@ -437,8 +433,7 @@ do {
437
433
  } while (this.HasWork());
438
434
  ```
439
435
 
440
- `continue` checks the condition again in both loops. Each executed body counts
441
- against the shared iteration budget.
436
+ `continue` checks the condition again in both loops.
442
437
 
443
438
  ## Switch
444
439
 
@@ -21,7 +21,7 @@ Root root = new() {
21
21
  @id("root-assets-member-id")
22
22
  @storage(allowed: .Immutable)
23
23
  Assets Assets = new() {
24
- Outposts = [Assets.Capitol],
24
+ Outposts = [Assets.Capitol.Clone()],
25
25
  };
26
26
  }
27
27
  ```
@@ -44,6 +44,22 @@ class Assets {
44
44
 
45
45
  A computed static getter is an alias; it creates neither a binding nor a row.
46
46
 
47
+ A static used in another initializer copies value kinds, including scalars and
48
+ delegates. Class, list, set, and dictionary values have exactly one owner.
49
+ Placing a value that something else already owns is refused: a static, a
50
+ static's field, a collection entry, a local placed elsewhere, or a constructor
51
+ parameter stored in an ordinary field. Status, push, and `neo test` report the
52
+ placement at its authored position. Call `Clone()` to own a copy, or use
53
+ `Reference`:
54
+
55
+ ```neo
56
+ static Outpost Annex = Assets.Capitol.Clone();
57
+ static Reference<Outpost> Seat = Reference(Assets.Capitol);
58
+ ```
59
+
60
+ Statics created together may refer to each other regardless of declaration
61
+ order; no preliminary push is needed.
62
+
47
63
  Enum-keyed dictionaries accept contextual or qualified option keys in defaults
48
64
  and stored values:
49
65
 
@@ -102,13 +118,18 @@ AudioSettings AUDIO_SETTINGS = new() { Main = SFX_FOLDER };
102
118
  ```
103
119
 
104
120
  In constructor arguments and executable operands, a class local names its one
105
- stored instance instead of constructing a copy:
121
+ stored instance instead of constructing a copy. Its owning placement keeps it,
122
+ so a constructor that stores it needs a `Reference` field:
106
123
 
107
124
  ```neo
108
125
  @id("86ac7c7d-8d3b-4642-9def-cdf32643bdc5")
109
126
  DeliveryQuest HARD_AS_STONE = new();
110
127
  // The core quest list owns HARD_AS_STONE.
111
128
  WorkflowStep UNLOCK = new QuestUnlockStep(HARD_AS_STONE);
129
+
130
+ class QuestUnlockStep(DeliveryQuest quest) : WorkflowStep {
131
+ public Reference<DeliveryQuest> Quest = quest;
132
+ }
112
133
  ```
113
134
 
114
135
  The referenced local must have exactly one owning placement. A nested target
@@ -312,6 +333,11 @@ different. The name must end with the binary's extension, because pull
312
333
  rebuilds the file's path from it. With no template annotation, the CLI persists the complete built-in
313
334
  Unity defaults for the file kind.
314
335
 
336
+ Template-backed Unity settings may contain sparse nested override values and leaf
337
+ paths such as `platformSettings.standalone.maxTextureSize` or
338
+ `spriteSettings.pivot.x`. Preserve these paths during synchronization so untouched
339
+ settings continue inheriting from the template.
340
+
315
341
  A top-level `NeoImage` or `NeoAudioClip` declaration in any definition file
316
342
  can stand in for an inline entry. The registry lists it by name
317
343
  (`NeoAudioClip SwordHit = SWORD_HIT;`), and the declaration carries the `@id`,