@neocompose/cli 0.87.2 → 0.89.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/CHANGELOG.md +23 -0
- package/README.md +14 -0
- package/dist/neo-runtime.mjs +11153 -3060
- package/dist/test-worker.mjs +15214 -9707
- package/package.json +1 -1
- package/skills/neocompose-cli/SKILL.md +95 -279
- package/skills/neocompose-cli/references/cli-development.md +101 -117
- package/skills/neocompose-cli/references/declarations/annotations-and-storage.md +112 -0
- package/skills/neocompose-cli/references/declarations/constructors.md +164 -0
- package/skills/neocompose-cli/references/declarations/declarations.md +103 -0
- package/skills/neocompose-cli/references/declarations/extensions.md +46 -0
- package/skills/neocompose-cli/references/declarations/initializers.md +100 -0
- package/skills/neocompose-cli/references/declarations/member-kinds.md +107 -0
- package/skills/neocompose-cli/references/declarations/overloads.md +50 -0
- package/skills/neocompose-cli/references/dialogues.md +145 -0
- package/skills/neocompose-cli/references/neoscript/collections.md +138 -0
- package/skills/neocompose-cli/references/neoscript/control-flow.md +123 -0
- package/skills/neocompose-cli/references/neoscript/delegates-and-listeners.md +107 -0
- package/skills/neocompose-cli/references/neoscript/math-and-random.md +114 -0
- package/skills/neocompose-cli/references/neoscript/neoscript.md +113 -0
- package/skills/neocompose-cli/references/neoscript/strings.md +47 -0
- package/skills/neocompose-cli/references/neoscript/testing-and-migrations.md +73 -0
- package/skills/neocompose-cli/references/neoscript/types-and-narrowing.md +111 -0
- package/skills/neocompose-cli/references/sync/branches-and-history.md +69 -0
- package/skills/neocompose-cli/references/sync/conflicts-and-recovery.md +68 -0
- package/skills/neocompose-cli/references/sync/sync.md +96 -0
- package/skills/neocompose-cli/references/sync/unity-export.md +63 -0
- package/skills/neocompose-cli/references/values/defaults-and-localization.md +86 -0
- package/skills/neocompose-cli/references/values/files.md +71 -0
- package/skills/neocompose-cli/references/values/identities.md +97 -0
- package/skills/neocompose-cli/references/values/references.md +151 -0
- package/skills/neocompose-cli/references/values/values.md +110 -0
- package/skills/neocompose-cli/references/world/animation.md +133 -0
- package/skills/neocompose-cli/references/world/grids-and-cell-patterns.md +55 -0
- package/skills/neocompose-cli/references/world/variants.md +112 -0
- package/skills/neocompose-cli/references/world/world.md +105 -0
- package/skills/neocompose-cli/references/animation-and-world-authoring.md +0 -582
- package/skills/neocompose-cli/references/commands-and-sync.md +0 -408
- package/skills/neocompose-cli/references/declarations-and-construction.md +0 -773
- package/skills/neocompose-cli/references/neoscript.md +0 -990
- package/skills/neocompose-cli/references/values-identities-and-references.md +0 -421
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Types, narrowing, and tuples
|
|
2
|
+
|
|
3
|
+
Null and type narrowing, patterns, equality, generic functions, and tuples.
|
|
4
|
+
|
|
5
|
+
## Null narrowing
|
|
6
|
+
|
|
7
|
+
- Null propagation continues through later required accesses in the same
|
|
8
|
+
chain: `config.Hat?.Head.Idle.Down.Frames` is null when `Hat` is null. A
|
|
9
|
+
nullable descendant still needs its own `?.`, `??`, `!`, or guard.
|
|
10
|
+
- `Member` and `this.Member` share narrowing. A local or parameter named
|
|
11
|
+
`Member` shadows the member; `this.Member` still names the field.
|
|
12
|
+
- Reassigning a narrowed path to a nullable value clears its facts and its
|
|
13
|
+
descendants' facts. Writes to unrelated members keep guards, also in loops.
|
|
14
|
+
A false earlier condition narrows later `else if` conditions.
|
|
15
|
+
- `a ?? b` is non-null when `b` is non-null, including in constructor
|
|
16
|
+
arguments such as `new Sprite(item.Sprite ?? SpriteInfo.Empty)`.
|
|
17
|
+
- Where a reference is expected, `first ?? fallback` picks the reference; a
|
|
18
|
+
present reference whose target is null does not select the fallback. In a
|
|
19
|
+
scalar context the reference reads its target first.
|
|
20
|
+
- Lookup results follow the same rules.
|
|
21
|
+
|
|
22
|
+
## Type patterns
|
|
23
|
+
|
|
24
|
+
```neo
|
|
25
|
+
if (item is not BedItem bedItem) {
|
|
26
|
+
throw "Expected a bed item";
|
|
27
|
+
}
|
|
28
|
+
return bedItem.Name;
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
- A binding stays in scope after a guard when every path that falls through
|
|
32
|
+
matched, including when other paths return from a nested block.
|
|
33
|
+
- `value is not T name` binds only where the test is known false: the `else`
|
|
34
|
+
branch, the right of `||`, or after a rejecting guard. The operand evaluates
|
|
35
|
+
once. `not` is an ordinary identifier elsewhere.
|
|
36
|
+
- Patterns follow the possible runtime type. An open class can match an
|
|
37
|
+
unrelated interface through a subclass that implements it; a sealed class
|
|
38
|
+
without it is a compile-time impossible pattern. A class implements each
|
|
39
|
+
interface once, so a value known to be `IBox<int>` never passes
|
|
40
|
+
`is IBox<string>`.
|
|
41
|
+
- An interface guard such as `if (item is not IReceiver) continue;` narrows
|
|
42
|
+
`item` itself: it keeps its declared class or generic `T` members and gains
|
|
43
|
+
`IReceiver` members. Later class guards accumulate (`IReceiver` then
|
|
44
|
+
`Furnace` exposes both). Guards that return, throw, or break work the same.
|
|
45
|
+
- A declaration pattern keeps the original receiver's type too: after
|
|
46
|
+
`if (item is not IReceiver receiver) return;`, `item` is unchanged and
|
|
47
|
+
`receiver` exposes `IReceiver`.
|
|
48
|
+
- Generic `is` tests check whether the bound can overlap the tested type.
|
|
49
|
+
- Guards also narrow named globals and static member paths, qualified or not,
|
|
50
|
+
inherited included. Assignment clears the proof. Closures must guard again
|
|
51
|
+
because the stored value can change before they run.
|
|
52
|
+
- A generic receiver may call members of its class or interface constraint;
|
|
53
|
+
nullable receivers still need narrowing.
|
|
54
|
+
|
|
55
|
+
## Equality
|
|
56
|
+
|
|
57
|
+
`==`, `!=`, `Contains`, `IndexOf`, and `Remove` compare class, interface,
|
|
58
|
+
List, Set, and Dictionary values by identity. Primitives, enums, vectors, and
|
|
59
|
+
media references compare by value. The static type decides, so an
|
|
60
|
+
unconstrained `T` compares by value.
|
|
61
|
+
|
|
62
|
+
`Equals(other)` on a non-null generic value calls the runtime class's
|
|
63
|
+
one-argument `bool Equals` (including overrides) when it has one, otherwise
|
|
64
|
+
falls back to value equality. Local sets fall back to comparing current
|
|
65
|
+
entries in insertion order.
|
|
66
|
+
|
|
67
|
+
## Generic functions
|
|
68
|
+
|
|
69
|
+
Functions, natives, and interface functions may declare type parameters with
|
|
70
|
+
optional constraints and defaults:
|
|
71
|
+
|
|
72
|
+
```neo
|
|
73
|
+
public static T? FirstOf<T extends Item = Item>(List<T> items) => items.FirstOrDefault();
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
- Call with every type argument, omit a defaulted tail
|
|
77
|
+
(`FirstOf<Seed>(items)`), or omit `<...>` to infer from arguments.
|
|
78
|
+
- Lambda inputs bind first so their returns can infer outputs, as in
|
|
79
|
+
`Map(seed, (value) => value.Price)`. Defaults fill parameters with no
|
|
80
|
+
evidence. The expected return type is not evidence. Explicit `<...>`
|
|
81
|
+
never infers the omitted tail. Conflicting types need explicit arguments;
|
|
82
|
+
inference does not invent a common ancestor.
|
|
83
|
+
- Function type parameters take no `@id`. Overrides declare the same count,
|
|
84
|
+
constraints, and defaults but may rename them.
|
|
85
|
+
|
|
86
|
+
Generic class arguments are invariant through inheritance: `IntBox : Box<int>`
|
|
87
|
+
assigns to `Box<int>`, never `Box<string>`. Typed locals accept generic
|
|
88
|
+
classes (`Box<int> box = new(1);`). Explicit lambda parameter types accept
|
|
89
|
+
the full declaration grammar, including `decimal`, generics, collections, and
|
|
90
|
+
nullables.
|
|
91
|
+
|
|
92
|
+
## Tuples
|
|
93
|
+
|
|
94
|
+
```neo
|
|
95
|
+
(int Row, int Col) position = (Row: 2, Col: 3);
|
|
96
|
+
var (row, col) = position;
|
|
97
|
+
(int first, (bool enabled, int _)) = (1, (true, 9));
|
|
98
|
+
var score = positions.Select(((r, c)) => r + c);
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
- Two or more elements. Labels are optional and don't affect type identity;
|
|
102
|
+
`Item1`, `Item2`, ... always work. Slots are immutable: replace the tuple.
|
|
103
|
+
- `((r, c)) =>` takes one tuple; `(r, c) =>` takes two parameters. Recursive
|
|
104
|
+
patterns work in declarations, loop bindings, and lambda parameters. `_`
|
|
105
|
+
discards. Assignment deconstruction is unsupported.
|
|
106
|
+
- Equality is recursive by element; decimal scale is ignored, classes and
|
|
107
|
+
collections compare by identity.
|
|
108
|
+
- `(int, int)?` is a nullable tuple; `(int?, int)` has a nullable element.
|
|
109
|
+
|
|
110
|
+
Stored tuple members are covered in
|
|
111
|
+
[member kinds](../declarations/member-kinds.md#stored-tuples).
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Branches, releases, and access
|
|
2
|
+
|
|
3
|
+
## Branches and releases
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
neo branch list
|
|
7
|
+
neo branch create <name> [--from <ref>]
|
|
8
|
+
neo branch switch <nameOrId>
|
|
9
|
+
neo branch refresh [--dry-run]
|
|
10
|
+
neo merge <branch> [--dry-run] [--migrate]
|
|
11
|
+
neo release cut [--bump major|minor|patch] [--dry-run]
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
- Branches are copy-on-write forks; releases are immutable snapshots.
|
|
15
|
+
- Switching needs a clean checkout. Unpushed source or binaries, conflicts,
|
|
16
|
+
source errors, and pending push recovery stop it before anything changes.
|
|
17
|
+
Push or back up first; `neo pull --force` only when discarding is intended.
|
|
18
|
+
- Creation waits until the durable copy publishes. The source stays readable
|
|
19
|
+
while copying.
|
|
20
|
+
- The server derives the minimum compatibility bump; a caller may only raise
|
|
21
|
+
it. Releases need the `release` login profile.
|
|
22
|
+
- In Unity projects using `unityConfigPath`, branch and version switches
|
|
23
|
+
update `NeoComposeConfig.asset`.
|
|
24
|
+
|
|
25
|
+
## History
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
neo history inspect
|
|
29
|
+
neo history log
|
|
30
|
+
neo history prune [--project <id|@latest|@current>]
|
|
31
|
+
neo history flatten [--project <id|@latest|@current>] --version <id|@latest|@current>
|
|
32
|
+
neo history apply --plan <plan-id> [--confirm-scope <project-id>[/<version-id>]]
|
|
33
|
+
neo history status --run <run-id>
|
|
34
|
+
neo history resume --run <run-id>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Rewrites need project owner or admin. `prune` and `flatten` only create
|
|
38
|
+
immutable plans. `apply` starts the destructive run once the plan seals;
|
|
39
|
+
production also needs the exact scope confirmation the plan shows.
|
|
40
|
+
|
|
41
|
+
## Login and credentials
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
neo login [--api <url>] [--profile editor|release] [--save-project <id>]
|
|
45
|
+
neo whoami
|
|
46
|
+
neo logout [--api <url>]
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
- Inside a workspace these use its `apiBaseUrl`; `--api` wins; outside, the
|
|
50
|
+
build's default origin. Login prints the origin before storing anything.
|
|
51
|
+
- Tokens use the OS credential store, falling back to a protected file. In CI
|
|
52
|
+
use `NEO_COMPOSE_TOKEN` or `--token-stdin`. The CLI refreshes its
|
|
53
|
+
short-lived JWT during long commands.
|
|
54
|
+
- The editor profile can't publish releases; server scopes are the security
|
|
55
|
+
boundary.
|
|
56
|
+
- `logout` deletes the credential for exactly one origin and revokes the
|
|
57
|
+
session best-effort.
|
|
58
|
+
- `NEO_COMPOSE_CONFIG_HOME` (absolute path replacing
|
|
59
|
+
`$XDG_CONFIG_HOME/neo-compose`) and `NEO_COMPOSE_CREDENTIAL_NAMESPACE`
|
|
60
|
+
(keychain account scope) isolate concurrent environments on one OS user.
|
|
61
|
+
|
|
62
|
+
## Automation
|
|
63
|
+
|
|
64
|
+
- Pass explicit project/version IDs and flags, prefer `--json`, and never
|
|
65
|
+
depend on pickers or confirms. Non-TTY runs never wait for them.
|
|
66
|
+
- Boolean flags are bare switches; `--dry-run=true` is rejected.
|
|
67
|
+
- `--dry-run` exists on push, merge, refresh, branch refresh, release cut, and
|
|
68
|
+
migrate run; elsewhere it is rejected before any action. `branch create`
|
|
69
|
+
has none.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Conflicts and recovery
|
|
2
|
+
|
|
3
|
+
## Conflicts
|
|
4
|
+
|
|
5
|
+
Pull merges disjoint fields and children, and merges collection and graph
|
|
6
|
+
children by ID, never by index. Conflicting scalars, edges, order,
|
|
7
|
+
delete/edit pairs, and binaries become explicit conflicts whose source fails
|
|
8
|
+
compilation on purpose.
|
|
9
|
+
|
|
10
|
+
- Edit the source to the intended final state, or run `neo resolve --mine` or
|
|
11
|
+
`--theirs` for a deliberate whole-side choice. Then pull, review, dry-run,
|
|
12
|
+
and push. There is no force-CAS path.
|
|
13
|
+
- Either side adopts the server as the new base for the conflicts it
|
|
14
|
+
settles: `--theirs` then reads clean, `--mine` reads as an ordinary local
|
|
15
|
+
update.
|
|
16
|
+
- One record is one conflict however many files declare it. `neo resolve`
|
|
17
|
+
settles all of its files together and refuses a partly resolved record.
|
|
18
|
+
- Resolution keeps the chosen lines and their line endings (LF, CRLF, or
|
|
19
|
+
mixed). Choosing a deleted side removes the file; deletion placeholders are
|
|
20
|
+
display text. Empty and comment-only files stay files.
|
|
21
|
+
- Against a server-side deletion, keeping the record restores it as a create
|
|
22
|
+
guarded by an absent-record check; removing it accepts the deletion.
|
|
23
|
+
- Pull warns when a conflicted record's local base strands newly downloaded
|
|
24
|
+
descendant rows. Resolve it with `--mine` or `--theirs` before pushing.
|
|
25
|
+
- Compiled initializer IR and other server-derived metadata never cause a
|
|
26
|
+
conflict by themselves.
|
|
27
|
+
- Binary conflicts keep local bytes; the server copy is under
|
|
28
|
+
`.neo/conflicts/files/<file-id>/`.
|
|
29
|
+
|
|
30
|
+
`value-base-desync` means the workspace base no longer names value rows its
|
|
31
|
+
own files project, so a plan would delete live rows. Nothing is planned; run
|
|
32
|
+
`neo pull` and retry. As a warning, it reports without blocking.
|
|
33
|
+
|
|
34
|
+
## Interrupted pushes
|
|
35
|
+
|
|
36
|
+
If a push committed but rewriting source failed, `neo pull` resumes the saved
|
|
37
|
+
result before checking the remote head. Status and push require that
|
|
38
|
+
recovery first. Interrupted file writes resume without parsing a half-written
|
|
39
|
+
checkout. If a file changed again after the plan was saved, recovery stops and
|
|
40
|
+
names it; save that edit separately, restore the file to its pre-rewrite or
|
|
41
|
+
generated content, then run `neo pull`.
|
|
42
|
+
|
|
43
|
+
## .neo/state.json
|
|
44
|
+
|
|
45
|
+
It holds the merge baseline, source placements, and the recovery journal.
|
|
46
|
+
Keep it. Normal pull refuses existing source without a baseline. To replace a
|
|
47
|
+
checkout whose state is gone, back it up and run `neo pull --force`, which
|
|
48
|
+
recovers custom paths from the remaining declarations and replaces local
|
|
49
|
+
source with the server version.
|
|
50
|
+
|
|
51
|
+
## neo doctor
|
|
52
|
+
|
|
53
|
+
`neo doctor` checks format, compiler, extension cache, source, files, and the
|
|
54
|
+
last pull's document integrity, and fails on malformed records. Pull first to
|
|
55
|
+
audit the current head.
|
|
56
|
+
|
|
57
|
+
- The partition audit follows ownership: a lookup may reference a value in a
|
|
58
|
+
different partition.
|
|
59
|
+
- It also reports base rows that no file projects and no record names. These
|
|
60
|
+
are invisible to `neo status`.
|
|
61
|
+
- `--json` includes a `repair` entry per finding for targeted repair.
|
|
62
|
+
- `--fix` repairs only `unprojected-value-row` findings: it lists each orphan
|
|
63
|
+
root with its row count, confirms, and deletes them in batches. Each row
|
|
64
|
+
carries the last pull's CAS base and each transaction expects the previous
|
|
65
|
+
head, so a stale audit fails instead of deleting a row that gained an
|
|
66
|
+
owner; a workspace with no recorded head is refused. `--yes` skips the
|
|
67
|
+
prompt. `--fix` has no `--json` form.
|
|
68
|
+
- After a reviewed `--fix`, pull and run doctor again.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Sync commands
|
|
2
|
+
|
|
3
|
+
What `pull`, `status`, `diff`, and `push` do beyond the workflow in
|
|
4
|
+
[SKILL.md](../../SKILL.md). Open a leaf from [References](#references) for
|
|
5
|
+
conflicts and recovery, branches and releases, or Unity export.
|
|
6
|
+
|
|
7
|
+
## Pull
|
|
8
|
+
|
|
9
|
+
- Normal pull fetches only changed records and merges them three-way by
|
|
10
|
+
record identity. If the remote head is unchanged it leaves source alone
|
|
11
|
+
without compiling; use `neo status` for local diagnostics.
|
|
12
|
+
- It skips records whose hashes match the saved baseline, including echoes of
|
|
13
|
+
this checkout's last push, and refreshes records whose remote change touches
|
|
14
|
+
only compiler-owned bodies or timestamps without rewriting authored files.
|
|
15
|
+
- It keeps declaration placement, spelling, comments, and file grouping by
|
|
16
|
+
stable ID. New server declarations use the default layout; system
|
|
17
|
+
declarations default to a `System` subfolder under their category.
|
|
18
|
+
Ownership comes from the reserved system identity, not the name.
|
|
19
|
+
- `--reset` regenerates canonical source and managed binaries, keeping
|
|
20
|
+
recorded declaration paths. `--force` discards local edits for the server
|
|
21
|
+
version. Both bypass push recovery. Use them only when that loss is
|
|
22
|
+
intended.
|
|
23
|
+
- `--regenerate-source-names` replaces generated dialogue symbols with
|
|
24
|
+
readable names and rewrites references.
|
|
25
|
+
- Pull refuses a baseline holding a compiled body this CLI rejects and names
|
|
26
|
+
the record. If pull can't refresh it, back up local edits and run
|
|
27
|
+
`neo pull --force`.
|
|
28
|
+
|
|
29
|
+
## Status and diff
|
|
30
|
+
|
|
31
|
+
`neo status` and `neo diff` show every pending change, grouped by owning
|
|
32
|
+
source, including newly authored value seeds. Diagnostics carry
|
|
33
|
+
file:line:column, such as invalid constructor parameter types. A failed class
|
|
34
|
+
or constructor declaration blocks candidate preparation.
|
|
35
|
+
|
|
36
|
+
Deleting a class or collection declaration also deletes its hidden entry
|
|
37
|
+
members, nested lists and dictionaries included; replacing a collection with
|
|
38
|
+
a scalar deletes abandoned entries. Entries used by surviving declarations
|
|
39
|
+
stay. If you deleted a member before pulling remote instance values, pull
|
|
40
|
+
keeps them in the deletion plan. Review these derived deletions before
|
|
41
|
+
pushing.
|
|
42
|
+
|
|
43
|
+
## Dry-run
|
|
44
|
+
|
|
45
|
+
`neo push --dry-run` rehearses the whole commit locally: source emission,
|
|
46
|
+
pending-ID assignment, transport shapes, hashes, repeat verification,
|
|
47
|
+
compilation, schema-commit materialization, seeded structural rows, and
|
|
48
|
+
whole-graph validation. It never prompts, uploads, commits, or rewrites
|
|
49
|
+
source. Pending getters and functions compile before initializers evaluate,
|
|
50
|
+
even on a first push, and initializers resolve pending dependencies first;
|
|
51
|
+
cycles report the participating values. It also checks derived-edge extractor compatibility against metadata
|
|
52
|
+
from the last pull; run `neo pull` if that metadata is missing.
|
|
53
|
+
|
|
54
|
+
A clean dry-run doesn't guarantee a clean push. The server still checks the
|
|
55
|
+
live head, per-change CAS bases, authorization, row shapes, file staging, and
|
|
56
|
+
admission.
|
|
57
|
+
|
|
58
|
+
## Push
|
|
59
|
+
|
|
60
|
+
- Push recompiles only bodies affected by changed declarations, prepares the
|
|
61
|
+
transaction once before confirmation, and reuses it after.
|
|
62
|
+
`--force-recompile` (with or without `--dry-run`) sweeps every body; use it
|
|
63
|
+
only to diagnose stale derived IR.
|
|
64
|
+
- One accepted push atomically commits records, main-locale text, bindings,
|
|
65
|
+
and staged file metadata. Pending IDs and source rewrites become real only
|
|
66
|
+
after acceptance.
|
|
67
|
+
- Normal push hashes the authored source it read and does not re-lower the
|
|
68
|
+
whole tree. A lowering mismatch therefore surfaces on the next canonical
|
|
69
|
+
pull; dry-run is the full source-to-record proof.
|
|
70
|
+
- `non-fast-forward`: someone committed after your pull. Pull, then push.
|
|
71
|
+
- `base-hash-conflict`: pull and resolve.
|
|
72
|
+
- `version-bump-required`: inspect the classification and pass
|
|
73
|
+
`--accept-bump` only when intended.
|
|
74
|
+
- Completion arrives through a live subscription, with progress every five
|
|
75
|
+
seconds. A disconnect doesn't cancel the durable job.
|
|
76
|
+
|
|
77
|
+
## Output and diagnostics
|
|
78
|
+
|
|
79
|
+
Interactive commands show phase spinners; `NO_COLOR` removes color only. JSON
|
|
80
|
+
and piped output contain no animation. Set `NEO_COMPOSE_TRACE_PERFORMANCE=1`
|
|
81
|
+
to write `neo-performance` JSON spans to stderr (`startedMs` relative to the
|
|
82
|
+
process, `durationMs` per operation; nested spans overlap, so don't sum
|
|
83
|
+
them).
|
|
84
|
+
|
|
85
|
+
## References
|
|
86
|
+
|
|
87
|
+
- [Conflicts and recovery](conflicts-and-recovery.md): merge conflicts,
|
|
88
|
+
`neo resolve`, `value-base-desync`, interrupted pushes, `.neo/state.json`,
|
|
89
|
+
`neo doctor` and `--fix`.
|
|
90
|
+
- [Branches, releases, and access](branches-and-history.md): branches, merge,
|
|
91
|
+
releases, history rewrites, login profiles, tokens, flags.
|
|
92
|
+
- [Unity export](unity-export.md): `neo export`, `neo dev`, local exports,
|
|
93
|
+
workspace lock.
|
|
94
|
+
- [Testing](../neoscript/testing-and-migrations.md): `neo test`,
|
|
95
|
+
`neo script`, `neo migrate`.
|
|
96
|
+
- [Localization](../values/defaults-and-localization.md#neo-loc): `neo loc`.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Unity export
|
|
2
|
+
|
|
3
|
+
`neo export` builds the Unity export from the working copy, offline, into the
|
|
4
|
+
directories the Unity project's `NeoComposeConfig` asset names: `project.json`
|
|
5
|
+
with a partition index, `Partitions/*.json` value rows, localization files,
|
|
6
|
+
and generated C# under `<generatedTypesDirectory>/Generated/`.
|
|
7
|
+
`Library/NeoCompose/export.json` is written last for Unity to ingest.
|
|
8
|
+
|
|
9
|
+
- Only changed files are written. Codegen is skipped when schema inputs are
|
|
10
|
+
unchanged, but content is still validated. Repeated exports, undos, and
|
|
11
|
+
reverts reuse cached results after verifying output bytes.
|
|
12
|
+
- The Unity project comes from `unityConfigPath`. Without it, an interactive
|
|
13
|
+
export offers to link the one config asset naming this project; scripts get
|
|
14
|
+
an error with the line to add. `--unity-project <dir>` exports elsewhere
|
|
15
|
+
without linking (agent rigs use it).
|
|
16
|
+
- Export stops before writing on a source error, a file conflict, a
|
|
17
|
+
`download` file action, an error diagnostic, or an unchanged project file
|
|
18
|
+
whose bytes differ from the pulled file.
|
|
19
|
+
- Keep `NeoGeneratedFiles.json` with the generated files; it preserves
|
|
20
|
+
`.meta` GUIDs across type renames.
|
|
21
|
+
- Export reports an error when an animation clip target doesn't match its
|
|
22
|
+
owner or one of its constructed base types.
|
|
23
|
+
|
|
24
|
+
Status and export stay clean when animation defaults use initializers or
|
|
25
|
+
constructor arguments select collection rows through Lookups.
|
|
26
|
+
|
|
27
|
+
## Local exports
|
|
28
|
+
|
|
29
|
+
A clean working copy exports the pulled head; diagnostic location shifts
|
|
30
|
+
alone don't count as local changes. Local edits, `Files/` bytes included, make
|
|
31
|
+
the export local: `project.json` gets `metadata.localExport`, Unity fails
|
|
32
|
+
player builds and keeps saves under `NeoCompose/LocalExport`, and a push or a
|
|
33
|
+
clean export ends it.
|
|
34
|
+
|
|
35
|
+
IDs minted for local declarations, including getters used in NeoFlow
|
|
36
|
+
interpolation, are kept in `.neo/export.json`'s `identityLedger`, so the next
|
|
37
|
+
push commits the IDs Unity already saw. A push re-exports into a Unity project
|
|
38
|
+
that received a local export.
|
|
39
|
+
|
|
40
|
+
## neo dev
|
|
41
|
+
|
|
42
|
+
`neo dev` watches source, binaries, ignore rules, workspace state, and config,
|
|
43
|
+
then exports in batches (20 ms quiet period, capped at 250 ms). Edits during
|
|
44
|
+
an export go into the next batch. It never pulls or pushes.
|
|
45
|
+
|
|
46
|
+
Pull, push, branch, merge, resolve, init, and export take turns through
|
|
47
|
+
`.neo/workspace.lock`; a lock whose process died is taken over.
|
|
48
|
+
|
|
49
|
+
## C# target and extension wrappers
|
|
50
|
+
|
|
51
|
+
Set `codegen.csharpVersion` in `neo.json` to a numeric language version such as
|
|
52
|
+
`"14"`. `latest` and `preview` are rejected. Without an override, export uses
|
|
53
|
+
the supported Unity compiler mapping or the SDK's C# 9 baseline. It reads
|
|
54
|
+
`codegen.unityVersion` first, then the linked Unity project's
|
|
55
|
+
`ProjectSettings/ProjectVersion.txt`. `neo doctor` reports the effective target
|
|
56
|
+
and its source. An unrecognized Unity version produces a baseline warning.
|
|
57
|
+
|
|
58
|
+
Closed instance extension functions can generate classic C# extension methods.
|
|
59
|
+
Interface receivers, open receiver types, static extensions, and computed
|
|
60
|
+
extension properties require C# 14. Export omits an unavailable wrapper and
|
|
61
|
+
reports its member ID, receiver, and missing capability. It keeps the member
|
|
62
|
+
available to the NeoScript evaluators and SDK direct invocation API. Changing
|
|
63
|
+
the target regenerates wrappers and removes obsolete owned wrapper files.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Defaults, overrides, and localization
|
|
2
|
+
|
|
3
|
+
## Virtual members and sparse overrides
|
|
4
|
+
|
|
5
|
+
Omitted members are virtual projections of the instance's recorded
|
|
6
|
+
constructor or variant and follow later edits to that source.
|
|
7
|
+
|
|
8
|
+
- Writing a member creates a sparse override with a deterministic projected
|
|
9
|
+
ID. Removing it from an existing initializer resets the override and
|
|
10
|
+
resumes tracking. Both transitions must show in `neo diff`.
|
|
11
|
+
- Spelling a declared default verbatim is the same as omitting it: a leaf
|
|
12
|
+
equal to its declaration, or a bare construction the declaration wholly
|
|
13
|
+
settles, mints no override. Spelling an unchanged child default such as
|
|
14
|
+
`FPS = 8` is a no-op even when siblings own rows. A stored row that already
|
|
15
|
+
spells its default becomes sparse on its next authored edit.
|
|
16
|
+
- Adding a different child default keeps sibling row identities.
|
|
17
|
+
- A stored class with no construction record still inherits literal
|
|
18
|
+
declaration defaults (`FoodInfo.Quality = .OneStar` fills an existing row
|
|
19
|
+
that stores only its price). Don't add an override to repair that. Required
|
|
20
|
+
fields with no applicable default remain errors.
|
|
21
|
+
- Defaulted members can be added without recreating existing instances.
|
|
22
|
+
- Editing an inline child keeps the outer construction's call-site values for
|
|
23
|
+
its other fields while its variant selection matches; a different variant
|
|
24
|
+
gives it an independent recipe.
|
|
25
|
+
|
|
26
|
+
## Emitted defaults
|
|
27
|
+
|
|
28
|
+
A member whose default constructs its declared type emits `= new()`; a
|
|
29
|
+
subclass default keeps its type (`Base Value = new Derived();`). Edit the
|
|
30
|
+
declaration to change the shared default, or assign inside an instance
|
|
31
|
+
initializer to override one instance. A nullable member with no default
|
|
32
|
+
stays bare; an explicit null default emits `= null`.
|
|
33
|
+
|
|
34
|
+
Generic arguments are types, not defaults: a required generic member needs a
|
|
35
|
+
value at construction unless a member declaration supplies a default. Pull
|
|
36
|
+
keeps incomplete stored objects for repair; add the initializer or default
|
|
37
|
+
before testing or pushing. An override without a local initializer inherits
|
|
38
|
+
the base default; an explicit `= null` is a literal nullable default.
|
|
39
|
+
|
|
40
|
+
## Construction rebuilds
|
|
41
|
+
|
|
42
|
+
Source is authoritative for a construction. Editing its constructor
|
|
43
|
+
arguments, a static's included, rebuilds it in place on push:
|
|
44
|
+
|
|
45
|
+
- the value keeps its ID and rows at unchanged positions keep theirs,
|
|
46
|
+
- added entries create rows and removed entries delete them,
|
|
47
|
+
- overrides the new construction no longer produces are deleted, so keep
|
|
48
|
+
intended overrides in the construction's `{ ... }` block.
|
|
49
|
+
|
|
50
|
+
Switching to another overload or shared argument rebuilds even when argument
|
|
51
|
+
values compare equal; owned inline arguments still compare by contents. A
|
|
52
|
+
construction whose class changed fails with `construction-arguments-conflict`;
|
|
53
|
+
delete and recreate the value instead.
|
|
54
|
+
|
|
55
|
+
## Localized text
|
|
56
|
+
|
|
57
|
+
- Write main-locale text directly in localizable string initializers. Never
|
|
58
|
+
author localized-text IDs in place of prose.
|
|
59
|
+
- Equal main-locale text on the same declared member shares one localization
|
|
60
|
+
record across overrides and instances. Different members keep separate
|
|
61
|
+
contexts.
|
|
62
|
+
- Editing a class default updates inheriting instances; editing one instance
|
|
63
|
+
doesn't change the default. Reverting an instance to its default restores
|
|
64
|
+
the shared record and archives an unreferenced unique one.
|
|
65
|
+
- An empty string stores no localization ID. Nullability, not text length,
|
|
66
|
+
decides whether the member is required.
|
|
67
|
+
|
|
68
|
+
## neo loc
|
|
69
|
+
|
|
70
|
+
Source files hold only main-locale text. Use `neo loc` for other locales,
|
|
71
|
+
statuses, comments, archives, and repair:
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
neo loc locales
|
|
75
|
+
neo loc list
|
|
76
|
+
neo loc set <textId> <locale> "text"
|
|
77
|
+
neo loc status-set <textId> <locale> <statusId>
|
|
78
|
+
neo loc comment-set <textId> <locale> "comment"
|
|
79
|
+
neo loc archive <textId>
|
|
80
|
+
neo loc restore <textId>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Batches go on stdin or `--file` as a JSON array where supported. `neo loc`
|
|
84
|
+
doesn't rewrite source; run `neo pull` after it (or any web edit) before
|
|
85
|
+
further edits or a push. A write refused because another commit landed is
|
|
86
|
+
run again.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Managed files
|
|
2
|
+
|
|
3
|
+
Images (PNG, JPEG, GIF, SVG) and audio clips are declared in typed registries
|
|
4
|
+
and stored as tracked binaries. SVG keeps its original bytes everywhere; the
|
|
5
|
+
CLI never rasterizes it.
|
|
6
|
+
|
|
7
|
+
## Registries
|
|
8
|
+
|
|
9
|
+
```neo
|
|
10
|
+
ImageRegistry Images = new() {
|
|
11
|
+
@id("sword-image-file-id")
|
|
12
|
+
@settings(template: PixelArt)
|
|
13
|
+
NeoImage Sword = new("Files/Images/Sword.png");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
AudioClipRegistry AudioClips = new() {
|
|
17
|
+
@id("sword-hit-audio-file-id")
|
|
18
|
+
@settings(template: SoundEffect)
|
|
19
|
+
NeoAudioClip SwordHit = new("Files/AudioClips/SwordHit.wav");
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
- `Images.Sword.Slice(0)` is a sprite; `AudioClips.SwordHit` is a clip. A bare
|
|
24
|
+
`Images.Sword` is only a file ID, valid in file-valued contexts such as
|
|
25
|
+
`SpriteInfo.FileId`.
|
|
26
|
+
- Registries and binaries may sit in any non-ignored folder. Update the path
|
|
27
|
+
when moving a binary.
|
|
28
|
+
- Renaming the symbol or replacing bytes keeps the file ID when `@id` stays.
|
|
29
|
+
- Symbols derive from the persisted file name (`tree.cherryblossom.png`
|
|
30
|
+
becomes `TreeCherryblossom`). Keep the symbol aligned or set
|
|
31
|
+
`@settings(name: "TreeCherryBlossom.png")`. The name must end with the
|
|
32
|
+
binary's extension because pull rebuilds the path from it.
|
|
33
|
+
- Without `@settings(template: ...)`, the CLI stores the complete built-in
|
|
34
|
+
Unity import defaults for the file kind. Template-backed settings may hold
|
|
35
|
+
sparse leaf paths such as `platformSettings.standalone.maxTextureSize` or
|
|
36
|
+
`spriteSettings.pivot.x`; keep them so untouched settings inherit.
|
|
37
|
+
|
|
38
|
+
A declaration can live in its own file, listed by name in the registry. The
|
|
39
|
+
declaration carries `@id`, `@settings`, and the path, and must be listed
|
|
40
|
+
exactly once:
|
|
41
|
+
|
|
42
|
+
```neo
|
|
43
|
+
// Values/Audio/SWORD_HIT.neo
|
|
44
|
+
@id("sword-hit-audio-file-id")
|
|
45
|
+
@settings(template: SoundEffect)
|
|
46
|
+
NeoAudioClip SWORD_HIT = new("Files/AudioClips/SwordHit.wav");
|
|
47
|
+
|
|
48
|
+
// Files/AudioClips.neo
|
|
49
|
+
AudioClipRegistry AudioClips = new() {
|
|
50
|
+
NeoAudioClip SwordHit = SWORD_HIT;
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Adding, replacing, removing
|
|
55
|
+
|
|
56
|
+
- Drop a supported binary under `Files/Images/` or `Files/AudioClips/` to
|
|
57
|
+
create a pending file with a provisional symbol. Status, diff, and dry-run
|
|
58
|
+
don't rewrite the registry; a successful push creates the record, uploads
|
|
59
|
+
verified bytes, and writes its declaration and ID.
|
|
60
|
+
- Remove the declaration to delete a file. A declaration whose binary is
|
|
61
|
+
missing is an error.
|
|
62
|
+
- Pull keeps unchanged entries, comments, and order; new remote entries
|
|
63
|
+
append, deleted ones are removed in place.
|
|
64
|
+
|
|
65
|
+
## Transfers and conflicts
|
|
66
|
+
|
|
67
|
+
Pull and push compare server-verified SHA-256, byte length, and MIME type.
|
|
68
|
+
Pull downloads up to eight files at once, reuses matching local bytes, and
|
|
69
|
+
verifies every download before replacing anything; a failed transfer leaves
|
|
70
|
+
files intact. A diverged binary keeps local bytes and writes the server copy
|
|
71
|
+
under `.neo/conflicts/files/<file-id>/`.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Identities
|
|
2
|
+
|
|
3
|
+
## Keep, omit, or author
|
|
4
|
+
|
|
5
|
+
- Keep every existing `@id` through rename, reorder, and file moves. Renaming
|
|
6
|
+
a member with its ID keeps its value graph; update initializer keys to the
|
|
7
|
+
new name and keep child IDs.
|
|
8
|
+
- A copy gets a new ID.
|
|
9
|
+
- New records omit `@id`. A successful real push assigns and inserts it;
|
|
10
|
+
dry-run and failed pushes leave source unchanged.
|
|
11
|
+
- Never match an existing ordered row by index or payload. A bare row is a
|
|
12
|
+
pending create.
|
|
13
|
+
- Never invent IDs for rows whose identity derives from an owner role: root
|
|
14
|
+
and static bindings, projected values, and constructor arguments (named or
|
|
15
|
+
positional), whose identity comes from the schema key the parameter
|
|
16
|
+
settles.
|
|
17
|
+
|
|
18
|
+
Nested class and list rows keep inline IDs:
|
|
19
|
+
|
|
20
|
+
```neo
|
|
21
|
+
Tags = [
|
|
22
|
+
@id("story-tag-item-id")
|
|
23
|
+
"story",
|
|
24
|
+
];
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Authored UUIDs
|
|
28
|
+
|
|
29
|
+
When a same-push structural reference has no symbol, path, or unique-key
|
|
30
|
+
form, give the new row a never-used UUID-v4 `@id` and reference it in the same
|
|
31
|
+
edit. The server checks shape and uniqueness. Reusing an existing ID means
|
|
32
|
+
update, not create. Dry-run keeps a valid authored UUID-v4 on a new nested
|
|
33
|
+
member or dictionary value. A malformed unknown ID is an error at its
|
|
34
|
+
annotation: remove it or supply an unused UUID-v4. Prefer generated IDs.
|
|
35
|
+
|
|
36
|
+
## Moving a nested value
|
|
37
|
+
|
|
38
|
+
A stored nested class value may keep its UUID-v4 when moved to an empty,
|
|
39
|
+
compatible class field on another existing parent, if all of these hold:
|
|
40
|
+
|
|
41
|
+
- the old owning declaration is removed in the same push,
|
|
42
|
+
- both fields declare the same non-generic class and storage rules,
|
|
43
|
+
- exactly one ownership path remains to the same root or static binding
|
|
44
|
+
through ordinary class fields, and
|
|
45
|
+
- both paths use inherited storage and automatic distribution, with no member
|
|
46
|
+
overrides.
|
|
47
|
+
|
|
48
|
+
Moves of projected values, constructor-owned slots, partitioned values,
|
|
49
|
+
generic instances, unordered-list entries, or into newly constructed parents
|
|
50
|
+
are refused. Omit the ID to create a new value there instead.
|
|
51
|
+
|
|
52
|
+
## Local initializer IDs
|
|
53
|
+
|
|
54
|
+
An existing row ID may precede a local declaration. It names the initializer
|
|
55
|
+
row, not a stored global:
|
|
56
|
+
|
|
57
|
+
```neo
|
|
58
|
+
@id("existing-quest-row-id")
|
|
59
|
+
Quest HardAsStone = new Quest() { Name = "Hard as stone" };
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`Quest HardAsStone = @id("...") new Quest();` is accepted and formatted to the
|
|
63
|
+
form above. Never put an ID in both places. A local keeps its concrete type
|
|
64
|
+
when a base-typed member consumes it.
|
|
65
|
+
|
|
66
|
+
## Locals as constructor arguments
|
|
67
|
+
|
|
68
|
+
In constructor arguments and executable operands, a class local names its one
|
|
69
|
+
stored instance rather than building a copy. Its owning placement keeps it,
|
|
70
|
+
so a constructor that stores it needs a `Reference` field:
|
|
71
|
+
|
|
72
|
+
```neo
|
|
73
|
+
@id("86ac7c7d-8d3b-4642-9def-cdf32643bdc5")
|
|
74
|
+
DeliveryQuest HARD_AS_STONE = new();
|
|
75
|
+
// The core quest list owns HARD_AS_STONE.
|
|
76
|
+
WorkflowStep UNLOCK = new QuestUnlockStep(HARD_AS_STONE);
|
|
77
|
+
|
|
78
|
+
class QuestUnlockStep(DeliveryQuest quest) : WorkflowStep {
|
|
79
|
+
public Reference<DeliveryQuest> Quest = quest;
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
- The local needs exactly one owning placement. Unowned locals, ambiguous
|
|
84
|
+
placements, and cycles are source errors.
|
|
85
|
+
- A nested target needs its existing `@id`. A structurally owned list entry
|
|
86
|
+
may use an unused UUID-v4 in the same push.
|
|
87
|
+
- A local nested inside an evaluated constructor stays idless on first push;
|
|
88
|
+
push assigns its identity before it can be referenced. Don't give it a fresh
|
|
89
|
+
UUID.
|
|
90
|
+
- A direct static owner supplies a pending identity for an idless local, and
|
|
91
|
+
aliases of that static local work too.
|
|
92
|
+
- A member default is a construction template, so its local is not a single
|
|
93
|
+
stored instance.
|
|
94
|
+
- Owned locals inside evaluated initializers need directly addressable fields,
|
|
95
|
+
list entries, or literal dictionary keys. Inline the construction when a
|
|
96
|
+
computed key hides the owning path.
|
|
97
|
+
- Canonical construction arguments may use `Reference<Type>(id: "...")`.
|