@neocompose/cli 0.84.5 → 0.86.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.84.5",
3
+ "version": "0.86.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.84.5 -->
12
+ <!-- reviewed-through-cli: 0.86.0 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -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.84.5 -->
132
+ <!-- reviewed-through-cli: 0.86.0 -->
133
133
  ```
134
134
 
135
135
  The quoted version above is checked too, so this instruction cannot go stale
@@ -61,6 +61,13 @@ it may extend another class but cannot be extended, cannot also be abstract,
61
61
  and can only be sealed while it has no subclasses. Interfaces can declare type
62
62
  parameters and pass arguments to the interfaces they extend; see Interface
63
63
  members.
64
+ A `closed` class is implicitly abstract and defines an exhaustive subtype
65
+ family within the project version. Do not combine `closed` with `abstract` or
66
+ `sealed`. Descendants remain open unless explicitly sealed or closed. Every
67
+ generic parameter of a direct subclass must appear in its base construction.
68
+ Changing the family requires recompilation of its switch-expression consumers.
69
+ Generated Unity C# uses an abstract class; C# 15 is not required.
70
+
64
71
  A read-only override may narrow a class or interface contract to an assignable
65
72
  class or derived interface, including a non-null value for a nullable contract.
66
73
  Generated Unity C# keeps the inherited property type for Mono compatibility;
@@ -148,16 +148,17 @@ Functions, natives and interface functions can declare their own type
148
148
  parameters after the name, each with an optional constraint and default:
149
149
 
150
150
  ```neo
151
- public static T? FirstOf<T extends Item = Item>(List<Item> items) {
152
- foreach (Item item in items) {
153
- if (item is T match) return match;
154
- }
155
- return null;
156
- }
151
+ public static T? FirstOf<T extends Item = Item>(List<T> items) => items.FirstOrDefault();
157
152
  ```
158
153
 
159
154
  A call writes every type argument or omits the defaulted tail
160
- (`FirstOf<Seed>(items)`, `FirstOf(items)`); nothing is inferred. Function type
155
+ (`FirstOf<Seed>(items)`, `FirstOf(items)`). When `<...>` is omitted, supplied
156
+ arguments infer the function's type arguments. Lambda inputs bind first so their
157
+ returns can infer output parameters, as in `Map(seed, (value) => value.Price)`.
158
+ Defaults fill parameters with no inference evidence. The destination/expected
159
+ return type does not provide evidence. Explicit `<...>` keeps explicit/default
160
+ binding and does not infer omitted trailing arguments. Conflicting types require
161
+ explicit arguments; inference does not invent a shared ancestor for siblings. Function type
161
162
  parameters take no `@id`, and an override declares the same count, constraints
162
163
  and defaults, though it may rename them.
163
164
 
@@ -384,12 +385,38 @@ Inside a generic body, `Reference<T>` takes the category of `T`'s binding.
384
385
 
385
386
  ## Numeric types
386
387
 
387
- `int` is a 32-bit integer and `float` is a 32-bit float, matching C#. Float
388
- literals, stored values, and every arithmetic step round to 32 bits, so
389
- `0.1f + 0.2f` behaves like Unity. `int` division truncates toward zero.
388
+ `int` stores whole numbers and division truncates toward zero. Ordinary integer
389
+ arithmetic retains the precision of the stored number. `float` literals, stored
390
+ values, and every float arithmetic step round to 32 bits, matching Unity.
390
391
  `decimal` is exact base-10 and is the only type to use for currency or other
391
392
  values that must not drift.
392
393
 
394
+ ## Bitwise integer operators
395
+
396
+ Use `~`, `&`, `^`, `|`, `<<`, `>>`, and `>>>` with non-nullable `int` operands.
397
+ The compound forms are `&=`, `^=`, `|=`, `<<=`, `>>=`, and `>>>=`.
398
+ Enums, bools, float literals such as `1.0`, and decimals are not accepted.
399
+ Use `Set<Enum>` for enum membership.
400
+
401
+ ```neo
402
+ int mask = (region << 8) | (tile & 255);
403
+ bool hasEast = (mask & 2) != 0;
404
+ mask &= ~2;
405
+ ```
406
+
407
+ Each operand wraps modulo 2^32 into signed int32 at the bitwise boundary.
408
+ Every result is signed int32. Ordinary integer arithmetic is unchanged.
409
+ Shift counts use their low five bits: 32 means 0, 33 means 1, and -1 means 31.
410
+ `>>` extends the sign bit; `>>>` fills with zero but still returns a signed
411
+ result. Thus `-1 >>> 1` is 2147483647 and `-1 >>> 0` is -1.
412
+ Operands evaluate once, left to right. A compound assignment captures its
413
+ target and reads its old value before evaluating the right operand.
414
+
415
+ Precedence from tightest to loosest: unary, switch expressions, multiplication/division/remainder,
416
+ addition/subtraction, shifts, comparisons, equality and `is`, `&`, `^`, `|`,
417
+ `&&`, `||`, `??`, then `?:`. Binary bitwise operators associate left to right.
418
+ Keep shift characters contiguous: `x >> 1` is valid; `x > > 1` is not.
419
+
393
420
  ## Numeric builtins
394
421
 
395
422
  Use the `Math` namespace for numeric utilities with C# names and C# semantics:
@@ -413,6 +440,33 @@ enum, parameter, or local may shadow it. Qualified members use their owner's
413
440
  namespace and may be named `Math`; access one as `this.Math` or
414
441
  `SomeValue.Math` without colliding with the builtin qualifier.
415
442
 
443
+ ## Vector math
444
+
445
+ `Vector2`, `Vector3`, `Vector2Int`, and `Vector3Int` support `+`, `-`, unary
446
+ `-`, multiplication by a scalar in either order, and division by a scalar.
447
+ Both vector operands must have the same type. Integer vectors accept only
448
+ integer scalars; float vectors accept int or float. Integer division truncates
449
+ toward zero. Division by zero throws. `==` and `!=` keep exact component equality.
450
+
451
+ ```neo
452
+ Vector2 next = position + direction * speed;
453
+ float distance = Vector2.Distance(position, target);
454
+ Vector2 heading = (target - position).normalized;
455
+ Vector2 step = Vector2.MoveTowards(position, target, speed);
456
+ ```
457
+
458
+ All vectors expose read-only `magnitude` and `sqrMagnitude`, static `Distance`,
459
+ `Min`, `Max`, and constants `zero`, `one`, `up`, `down`, `left`, and `right`.
460
+ Three-dimensional vectors also have `forward` and `back`. Float vectors add
461
+ `normalized`, `Dot`, `Lerp`, and `MoveTowards`; `Vector3` adds `Cross`.
462
+ Integer vectors have no implicit conversion to float vectors.
463
+
464
+ `Color.Lerp(a, b, t)` interpolates all four channels, including alpha.
465
+ Both vector and Color `Lerp` clamp t to [0, 1]. `normalized` returns zero when
466
+ the magnitude is at most 0.00001. A negative MoveTowards delta moves away.
467
+ Computed properties are read-only; arithmetic, math results, and constants
468
+ are fresh values. Component writes retain the ordinary vector aliasing rules.
469
+
416
470
  ## String builtins
417
471
 
418
472
  Use `text.Replace(search, replacement)` to return a string with every
@@ -424,6 +478,36 @@ may be null. The receiver stays unchanged.
424
478
  return "banana".Replace("a", "o"); // "bonono"
425
479
  ```
426
480
 
481
+ Strings also have `Length`, `Substring(startIndex[, length])`, `IndexOf`,
482
+ `LastIndexOf`, `Split(separator)`, `PadLeft`/`PadRight(totalWidth[,
483
+ paddingChar])`, `Trim`, `TrimStart`, and `TrimEnd`, plus the statics
484
+ `string.Join(separator, values)`, `string.IsNullOrEmpty`, and
485
+ `string.IsNullOrWhiteSpace`. Lengths and indices count UTF-16 code units, every
486
+ comparison is ordinal, and whitespace is C#'s `char.IsWhiteSpace` set. `Split`
487
+ keeps empty entries and returns a new list. `Join` takes a `List<string>` or
488
+ `Set<string>`; turn other entries into text first with
489
+ `values.Select(v => $"{v}")`. `s?.Length` is an error; use
490
+ `string.IsNullOrEmpty(s)` or narrow first.
491
+
492
+ ```neo
493
+ List<string> parts = this.Text.Trim().Split(" ");
494
+ int quantity = int.TryParse(parts[1]) ?? 0;
495
+ return $"{slot.ToString("D3")} {price.ToString("N2")}";
496
+ ```
497
+
498
+ `int.Parse`, `float.Parse`, and `decimal.Parse` accept ASCII digits with an
499
+ optional sign (plus a fraction for `float` and `decimal`, and an exponent for
500
+ `float`) after trimming ASCII whitespace, and throw on anything else or on a
501
+ value the type can't hold. `TryParse` returns `null` instead of throwing.
502
+ `decimal.Parse` never rounds: more than 28 significant or fractional digits is
503
+ an error.
504
+
505
+ Numbers format only with an explicit format: `D<n>` (int only, zero-padded),
506
+ `F<n>` (fixed point), or `N<n>` (fixed point with `,` grouping), where `n` is 0
507
+ to 28 and defaults to 2 (1 for `D`). `float` rounds half to even from its exact
508
+ value, `decimal` rounds half away from zero, and a result that is all zeros has
509
+ no `-` sign. For plain text, use interpolation: `$"{value}"`.
510
+
427
511
  Integer inputs assigned to `float` round to float32. This also applies to
428
512
  function arguments and returns, collection entries, and `List.Repeat` with a
429
513
  float entry type. `Contains` and `IndexOf` on float collections also convert
@@ -466,6 +550,44 @@ return this.Beds
466
550
  .Take(10);
467
551
  ```
468
552
 
553
+ `Sum`, `Average`, `Min`, and `Max` accept zero arguments or one selector.
554
+ Sum/Average require int, float, or decimal values; Min/Max also accept strings
555
+ and bools. Null values are skipped. Sum returns zero on empty input; the other
556
+ reductions return null for nullable input and throw for required input.
557
+ Average of ints returns float. Decimal calculations stay exact; decimal Average
558
+ rounds half-even within Neo's 28-digit envelope. Float Min stops at the first
559
+ NaN; Max prefers ordinary values to NaN.
560
+
561
+ `MinBy((item) => item.Score)` and `MaxBy(...)` return the first entry with the
562
+ winning key. They skip null keys unless every key is null, then keep the first
563
+ entry. Empty input throws unless the entry type is nullable.
564
+
565
+ List and Set support `Aggregate((acc, item) => ...)`,
566
+ `Aggregate(seed, (acc, item) => ...)`, `Distinct()`, and
567
+ `Zip(other, (left, right) => ...)`. Aggregate visits entries in order; an empty
568
+ unseeded fold throws. Distinct retains first occurrences using Contains equality
569
+ and returns a read-only List, including nullable entries. Zip stops at the
570
+ shorter input.
571
+
572
+ `GroupBy((item) => item.Category)` returns a read-only dictionary of read-only
573
+ lists. `ToDictionary((item) => item.Name)` optionally accepts a second value
574
+ selector. Keys must be non-null strings or enum values; duplicate dictionary
575
+ keys throw. Dictionary callbacks take `(key, value)`. `.Keys` and `.Values`
576
+ return read-only lists in dictionary traversal order (integer-looking string
577
+ keys precede other strings in numeric order).
578
+
579
+ Chain `ThenBy` or `ThenByDescending` after OrderBy to add stable secondary keys.
580
+ An inferred `var` retains this capability; ordinary List/Set annotations,
581
+ function boundaries, and non-ordering queries erase it. Every ordering call is
582
+ eager: it caches keys and sorts the snapshot, costing O(n log n * k) for k key
583
+ levels. Each selector runs once per entry per level. Mutating a snapshot through an explicitly writable local alias invalidates its cached keys; ThenBy then throws until a new OrderBy establishes an ordering.
584
+
585
+ Writable ordered lists support `items.Sort()` for scalar entries or
586
+ `items.Sort((left, right) => left.Score - right.Score)` for an int comparison.
587
+ Sort returns void, preserves entry identity, and is stable. It rejects mutation
588
+ of the target list through aliases while comparisons run; entry fields retain
589
+ their existing write permissions. A failed comparison leaves the order unchanged.
590
+
469
591
  Use `items.IndexOf(value)` to return the zero-based index of the first equal
470
592
  list entry, or `-1` when the value is absent. `IndexOf` is list-only and uses
471
593
  the same equality as `Contains`.
@@ -597,8 +719,39 @@ switch (direction) {
597
719
  Permit `int`, `string`, `bool`, enum, and nullable forms. Stack labels when
598
720
  they share one body. End every reachable non-empty section with `break`,
599
721
  `return`, `throw`, or `continue` when a surrounding loop makes it valid.
600
- Do not use implicit fallthrough, switch expressions, pattern cases, guards, or
601
- `goto`.
722
+ Do not use implicit fallthrough, pattern cases, guards, or `goto` in statement switches.
723
+
724
+ ### Switch expressions
725
+
726
+ Use `value switch { pattern => result, ... }` to produce one value. Arms may
727
+ use scalar/enum constants, `null`, type patterns (`Damage`), declaration
728
+ patterns (`Damage damage`), or `_`. A declaration binding is scoped to its
729
+ arm's optional `when` guard and result. Patterns and guards run in order; the
730
+ selector runs once and only the chosen result runs.
731
+
732
+ ```neo
733
+ closed class HitResult {}
734
+ sealed class Miss : HitResult {}
735
+ sealed class Damage : HitResult { public int Amount = 0; }
736
+ class Combat {
737
+ public string Describe(HitResult result) => result switch {
738
+ Miss => "miss",
739
+ Damage damage when damage.Amount > 10 => "strong hit",
740
+ Damage damage => $"{damage.Amount}",
741
+ };
742
+ }
743
+ ```
744
+
745
+ Every switch expression must be exhaustive. Handle every bool/enum option or
746
+ closed-class branch, plus null for nullable input. Other types require a
747
+ covering type or `_` fallback. A guard contributes no exhaustive coverage
748
+ unless it is literally `true`. Missing and unreachable cases are compile
749
+ errors. Adding a new subtype recompiles affected consumers.
750
+
751
+ Result arms must fit the expected type or have a common assignable type.
752
+ Property, relational, list, and positional patterns are unsupported. Pattern
753
+ cases in statement switches remain unsupported. Install the matching Unity
754
+ SDK before exporting the new switch-expression IR; older SDKs reject it.
602
755
 
603
756
  ## Try/catch
604
757