@neocompose/cli 0.84.5 → 0.85.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.85.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.85.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.85.0 -->
133
133
  ```
134
134
 
135
135
  The quoted version above is checked too, so this instruction cannot go stale
@@ -413,6 +413,33 @@ enum, parameter, or local may shadow it. Qualified members use their owner's
413
413
  namespace and may be named `Math`; access one as `this.Math` or
414
414
  `SomeValue.Math` without colliding with the builtin qualifier.
415
415
 
416
+ ## Vector math
417
+
418
+ `Vector2`, `Vector3`, `Vector2Int`, and `Vector3Int` support `+`, `-`, unary
419
+ `-`, multiplication by a scalar in either order, and division by a scalar.
420
+ Both vector operands must have the same type. Integer vectors accept only
421
+ integer scalars; float vectors accept int or float. Integer division truncates
422
+ toward zero. Division by zero throws. `==` and `!=` keep exact component equality.
423
+
424
+ ```neo
425
+ Vector2 next = position + direction * speed;
426
+ float distance = Vector2.Distance(position, target);
427
+ Vector2 heading = (target - position).normalized;
428
+ Vector2 step = Vector2.MoveTowards(position, target, speed);
429
+ ```
430
+
431
+ All vectors expose read-only `magnitude` and `sqrMagnitude`, static `Distance`,
432
+ `Min`, `Max`, and constants `zero`, `one`, `up`, `down`, `left`, and `right`.
433
+ Three-dimensional vectors also have `forward` and `back`. Float vectors add
434
+ `normalized`, `Dot`, `Lerp`, and `MoveTowards`; `Vector3` adds `Cross`.
435
+ Integer vectors have no implicit conversion to float vectors.
436
+
437
+ `Color.Lerp(a, b, t)` interpolates all four channels, including alpha.
438
+ Both vector and Color `Lerp` clamp t to [0, 1]. `normalized` returns zero when
439
+ the magnitude is at most 0.00001. A negative MoveTowards delta moves away.
440
+ Computed properties are read-only; arithmetic, math results, and constants
441
+ are fresh values. Component writes retain the ordinary vector aliasing rules.
442
+
416
443
  ## String builtins
417
444
 
418
445
  Use `text.Replace(search, replacement)` to return a string with every
@@ -424,6 +451,36 @@ may be null. The receiver stays unchanged.
424
451
  return "banana".Replace("a", "o"); // "bonono"
425
452
  ```
426
453
 
454
+ Strings also have `Length`, `Substring(startIndex[, length])`, `IndexOf`,
455
+ `LastIndexOf`, `Split(separator)`, `PadLeft`/`PadRight(totalWidth[,
456
+ paddingChar])`, `Trim`, `TrimStart`, and `TrimEnd`, plus the statics
457
+ `string.Join(separator, values)`, `string.IsNullOrEmpty`, and
458
+ `string.IsNullOrWhiteSpace`. Lengths and indices count UTF-16 code units, every
459
+ comparison is ordinal, and whitespace is C#'s `char.IsWhiteSpace` set. `Split`
460
+ keeps empty entries and returns a new list. `Join` takes a `List<string>` or
461
+ `Set<string>`; turn other entries into text first with
462
+ `values.Select(v => $"{v}")`. `s?.Length` is an error; use
463
+ `string.IsNullOrEmpty(s)` or narrow first.
464
+
465
+ ```neo
466
+ List<string> parts = this.Text.Trim().Split(" ");
467
+ int quantity = int.TryParse(parts[1]) ?? 0;
468
+ return $"{slot.ToString("D3")} {price.ToString("N2")}";
469
+ ```
470
+
471
+ `int.Parse`, `float.Parse`, and `decimal.Parse` accept ASCII digits with an
472
+ optional sign (plus a fraction for `float` and `decimal`, and an exponent for
473
+ `float`) after trimming ASCII whitespace, and throw on anything else or on a
474
+ value the type can't hold. `TryParse` returns `null` instead of throwing.
475
+ `decimal.Parse` never rounds: more than 28 significant or fractional digits is
476
+ an error.
477
+
478
+ Numbers format only with an explicit format: `D<n>` (int only, zero-padded),
479
+ `F<n>` (fixed point), or `N<n>` (fixed point with `,` grouping), where `n` is 0
480
+ to 28 and defaults to 2 (1 for `D`). `float` rounds half to even from its exact
481
+ value, `decimal` rounds half away from zero, and a result that is all zeros has
482
+ no `-` sign. For plain text, use interpolation: `$"{value}"`.
483
+
427
484
  Integer inputs assigned to `float` round to float32. This also applies to
428
485
  function arguments and returns, collection entries, and `List.Repeat` with a
429
486
  float entry type. `Contains` and `IndexOf` on float collections also convert
@@ -466,6 +523,44 @@ return this.Beds
466
523
  .Take(10);
467
524
  ```
468
525
 
526
+ `Sum`, `Average`, `Min`, and `Max` accept zero arguments or one selector.
527
+ Sum/Average require int, float, or decimal values; Min/Max also accept strings
528
+ and bools. Null values are skipped. Sum returns zero on empty input; the other
529
+ reductions return null for nullable input and throw for required input.
530
+ Average of ints returns float. Decimal calculations stay exact; decimal Average
531
+ rounds half-even within Neo's 28-digit envelope. Float Min stops at the first
532
+ NaN; Max prefers ordinary values to NaN.
533
+
534
+ `MinBy((item) => item.Score)` and `MaxBy(...)` return the first entry with the
535
+ winning key. They skip null keys unless every key is null, then keep the first
536
+ entry. Empty input throws unless the entry type is nullable.
537
+
538
+ List and Set support `Aggregate((acc, item) => ...)`,
539
+ `Aggregate(seed, (acc, item) => ...)`, `Distinct()`, and
540
+ `Zip(other, (left, right) => ...)`. Aggregate visits entries in order; an empty
541
+ unseeded fold throws. Distinct retains first occurrences using Contains equality
542
+ and returns a read-only List, including nullable entries. Zip stops at the
543
+ shorter input.
544
+
545
+ `GroupBy((item) => item.Category)` returns a read-only dictionary of read-only
546
+ lists. `ToDictionary((item) => item.Name)` optionally accepts a second value
547
+ selector. Keys must be non-null strings or enum values; duplicate dictionary
548
+ keys throw. Dictionary callbacks take `(key, value)`. `.Keys` and `.Values`
549
+ return read-only lists in dictionary traversal order (integer-looking string
550
+ keys precede other strings in numeric order).
551
+
552
+ Chain `ThenBy` or `ThenByDescending` after OrderBy to add stable secondary keys.
553
+ An inferred `var` retains this capability; ordinary List/Set annotations,
554
+ function boundaries, and non-ordering queries erase it. Every ordering call is
555
+ eager: it caches keys and sorts the snapshot, costing O(n log n * k) for k key
556
+ 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.
557
+
558
+ Writable ordered lists support `items.Sort()` for scalar entries or
559
+ `items.Sort((left, right) => left.Score - right.Score)` for an int comparison.
560
+ Sort returns void, preserves entry identity, and is stable. It rejects mutation
561
+ of the target list through aliases while comparisons run; entry fields retain
562
+ their existing write permissions. A failed comparison leaves the order unchanged.
563
+
469
564
  Use `items.IndexOf(value)` to return the zero-based index of the first equal
470
565
  list entry, or `-1` when the value is absent. `IndexOf` is list-only and uses
471
566
  the same equality as `Contains`.