@neocompose/cli 0.19.2 → 0.19.4
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 +125 -0
- package/README.md +17 -12
- package/dist/neo.mjs +10420 -195
- package/package.json +2 -1
- package/skills/neocompose-cli/SKILL.md +115 -524
- package/skills/neocompose-cli/agents/openai.yaml +4 -0
- package/skills/neocompose-cli/references/animation-and-world-authoring.md +218 -0
- package/skills/neocompose-cli/references/cli-development.md +110 -0
- package/skills/neocompose-cli/references/commands-and-sync.md +130 -0
- package/skills/neocompose-cli/references/declarations-and-construction.md +217 -0
- package/skills/neocompose-cli/references/neoscript.md +150 -0
- package/skills/neocompose-cli/references/values-identities-and-references.md +158 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Animation and world authoring
|
|
2
|
+
|
|
3
|
+
Use this reference for world objects, structured leaves, animation, and
|
|
4
|
+
NeoFlow dialogue graphs.
|
|
5
|
+
|
|
6
|
+
## Contents
|
|
7
|
+
|
|
8
|
+
- World object contracts
|
|
9
|
+
- Sorting and optional children
|
|
10
|
+
- Structured leaves
|
|
11
|
+
- Animation overrides
|
|
12
|
+
- Segments and tracks
|
|
13
|
+
- NeoFlow dialogues
|
|
14
|
+
|
|
15
|
+
## World object contracts
|
|
16
|
+
|
|
17
|
+
Extend visible system world classes with ordinary project-authored classes and
|
|
18
|
+
overrides. Never copy the system schema's `@system` provenance annotation into
|
|
19
|
+
project source.
|
|
20
|
+
|
|
21
|
+
For world layer links, derive a concrete project class from
|
|
22
|
+
`NeoTileLayerLink` or `NeoObjectLayerLink` and resolve exactly one
|
|
23
|
+
`targetLayer` relation. Do not add value-level target metadata or ask painting
|
|
24
|
+
operations to create/repair relation targets.
|
|
25
|
+
|
|
26
|
+
## Sorting and optional children
|
|
27
|
+
|
|
28
|
+
Attach `NeoSortingGroup` as an optional nested member on the project object
|
|
29
|
+
classes that need it:
|
|
30
|
+
|
|
31
|
+
```neo
|
|
32
|
+
class Tree : NeoObject {
|
|
33
|
+
public virtual NeoSortingGroup? SortingGroup = null;
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Presence of a non-null group makes the object and descendants sort as one unit.
|
|
38
|
+
`SortAtRoot` hoists a nested group against the scene root.
|
|
39
|
+
|
|
40
|
+
`NeoSpriteObject` exposes `FlipX`, `FlipY`, `MaskInteraction`, and optional
|
|
41
|
+
`SortingOrder`. Treat `SortingOrder` as an offset added to the order derived
|
|
42
|
+
from layer, placement, and composition; it is not an absolute replacement.
|
|
43
|
+
|
|
44
|
+
Use `NeoObjectBase.Enabled` to hide an object and its entire subtree without
|
|
45
|
+
removing its authored `Children` row. A disabled object is not rendered or
|
|
46
|
+
collided with, but its live values and running animation continue. Prefer this
|
|
47
|
+
for fixed optional equipment/content slots so their provenance and animation
|
|
48
|
+
targets remain stable.
|
|
49
|
+
|
|
50
|
+
Do not treat P45 runtime-child provenance as available. Runtime structural
|
|
51
|
+
children remain deferred; author known slots and toggle `Enabled`.
|
|
52
|
+
|
|
53
|
+
## Structured leaves
|
|
54
|
+
|
|
55
|
+
Address fields of these leaf values:
|
|
56
|
+
|
|
57
|
+
- `SpriteInfo`: `FileId`, `SliceIndex`
|
|
58
|
+
- Vector values: lowercase components such as `x`, `y`, `z`, `w` where
|
|
59
|
+
supported by the concrete vector type
|
|
60
|
+
- Color: lowercase `r`, `g`, `b`, `a`
|
|
61
|
+
|
|
62
|
+
Keep the casing exact. `FileId` and `SliceIndex` are PascalCase; vector and
|
|
63
|
+
color components are lowercase.
|
|
64
|
+
|
|
65
|
+
Use complete values in normal member initialization:
|
|
66
|
+
|
|
67
|
+
```neo
|
|
68
|
+
Sprite = Images.Hero.Slice(0);
|
|
69
|
+
Position = new(0, 0, 0);
|
|
70
|
+
Tint = Color(1, 1, 1, 1);
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
A bare `Images.Hero` yields a file ID only in a file-valued field context.
|
|
74
|
+
Use `.Slice(index)` for a complete `SpriteInfo`.
|
|
75
|
+
|
|
76
|
+
NeoScript may read or write structured fields when the receiver's effective
|
|
77
|
+
storage permits it:
|
|
78
|
+
|
|
79
|
+
```neo
|
|
80
|
+
root.Session.Debug.Slice = Leg.Sprite.SliceIndex;
|
|
81
|
+
Leg.Sprite.SliceIndex = 2;
|
|
82
|
+
Leg.Position.y = 0.25;
|
|
83
|
+
Leg.Tint.a = 0.5;
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Treat writes as read-modify-write against the current whole leaf. Reject
|
|
87
|
+
unknown fields, vector components unsupported by the concrete type, non-integer
|
|
88
|
+
integer-vector values, and color channels outside `[0, 1]`.
|
|
89
|
+
|
|
90
|
+
## Animation overrides
|
|
91
|
+
|
|
92
|
+
Use partial structured literals only inside animation override graphs:
|
|
93
|
+
|
|
94
|
+
```neo
|
|
95
|
+
Overrides = new {
|
|
96
|
+
Sprite = new { SliceIndex = 1 },
|
|
97
|
+
Position = new { y = 0.25 },
|
|
98
|
+
Tint = new { a = 0.5 },
|
|
99
|
+
};
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
An empty partial `new { }` means no change. A partial override changes only
|
|
103
|
+
the named fields and preserves the leaf's other current fields. A whole-leaf
|
|
104
|
+
override replaces the whole leaf.
|
|
105
|
+
|
|
106
|
+
Frame overrides may change `Enabled`, `FlipX`, and `SortingOrder`; they may
|
|
107
|
+
not add, remove, or reorder `Children`.
|
|
108
|
+
|
|
109
|
+
Address direct children with stable row IDs. Missing optional child slots are
|
|
110
|
+
skipped with diagnostics when the authored slot is absent, while stale
|
|
111
|
+
pre-provenance placements may still fail closed.
|
|
112
|
+
|
|
113
|
+
A placement row carries its `assetClassId` binding and optional `assetValueId`
|
|
114
|
+
override beside its schema keys. They are row provenance, not declared schema:
|
|
115
|
+
never declare them as members on a project class, and never strip them from a
|
|
116
|
+
row you rewrite.
|
|
117
|
+
|
|
118
|
+
## Segments and tracks
|
|
119
|
+
|
|
120
|
+
Use `NeoAnimationSegment<T>` for a frame-indexed value lane. The shipped
|
|
121
|
+
sprite specialization is `NeoSpriteAnimationSegment`, whose
|
|
122
|
+
`NeoAnimationSegmentFrame<SpriteInfo>` rows each provide `Index` and
|
|
123
|
+
`Value`. Sparse frame values hold until the next row or `Duration`.
|
|
124
|
+
|
|
125
|
+
Use one polymorphic `NeoAnimationClip.Tracks` list:
|
|
126
|
+
|
|
127
|
+
- `NeoAnimationChildTrack` schedules a child clip selected by `Child` and
|
|
128
|
+
`ClipKey`.
|
|
129
|
+
- A project subclass of
|
|
130
|
+
`NeoAnimationSegmentTrack<TChild, TValue>` schedules a segment onto one
|
|
131
|
+
target member. Implement its abstract `Segment` member as a stored value,
|
|
132
|
+
lookup, or computed getter.
|
|
133
|
+
- The shipped
|
|
134
|
+
`NeoSpriteAnimationSegmentTrack<TChild extends NeoSpriteObject>` pairing
|
|
135
|
+
targets `NeoSpriteObject.Sprite`.
|
|
136
|
+
|
|
137
|
+
Declare a concrete project track by implementing `Segment`, then construct its
|
|
138
|
+
row with the target child ID:
|
|
139
|
+
|
|
140
|
+
```neo
|
|
141
|
+
class StoredPantsTrack : NeoSpriteAnimationSegmentTrack<PantsSprite> {
|
|
142
|
+
public override NeoSpriteAnimationSegment Segment = new();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
Tracks = [
|
|
146
|
+
@id("pants-track-row-id")
|
|
147
|
+
new StoredPantsTrack(id: "pants-child-row-id") {
|
|
148
|
+
StartFrame = 0,
|
|
149
|
+
Direction = .Reverse,
|
|
150
|
+
OffsetEndIndex = 3,
|
|
151
|
+
},
|
|
152
|
+
];
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Each track has `StartFrame`, `Direction` (`.Forward` or `.Reverse`), and a
|
|
156
|
+
crop window `OffsetStartIndex`/`OffsetEndIndex`. Crop before reversing, then
|
|
157
|
+
schedule on the owning clip. Content beyond the owning clip truncates. Reject
|
|
158
|
+
empty/inverted crop windows and tracks that can never enter the clip.
|
|
159
|
+
|
|
160
|
+
Treat track target selection as class metadata, not row data. A target must be
|
|
161
|
+
compatible with `TChild` and `TValue`. Track reads and segment getters are
|
|
162
|
+
re-resolved for the next applied frame after a watched dependency changes.
|
|
163
|
+
|
|
164
|
+
When multiple tracks write the same member in one frame, apply list order and
|
|
165
|
+
let the last write win.
|
|
166
|
+
|
|
167
|
+
## NeoFlow dialogues
|
|
168
|
+
|
|
169
|
+
Put exactly one top-level `sealed class Name : Dialogue` in each `.neoflow`
|
|
170
|
+
file. Make it non-generic, derive directly from compiler-owned `Dialogue`, and
|
|
171
|
+
override `Trigger Trigger` exactly once. Do not add any other type declaration.
|
|
172
|
+
|
|
173
|
+
```neoflow
|
|
174
|
+
@id("capitol-dialogue-id")
|
|
175
|
+
@settings(name: "Capitol: cold boot", saveOptionChoices: true)
|
|
176
|
+
sealed class CapitolColdBoot : Dialogue {
|
|
177
|
+
@primary Outpost capitol = Assets.Capitol;
|
|
178
|
+
Player player = Player.Current;
|
|
179
|
+
|
|
180
|
+
@id("trigger-id")
|
|
181
|
+
override Trigger Trigger = new(group: CapitolDialogues.High) => Welcome;
|
|
182
|
+
|
|
183
|
+
@id("welcome-node-id")
|
|
184
|
+
Text Welcome = new(name: "Welcome!") {
|
|
185
|
+
"""
|
|
186
|
+
Hello, {player.Name}.
|
|
187
|
+
"""
|
|
188
|
+
|
|
189
|
+
@id("continue-option-id")
|
|
190
|
+
Option Continue = new() {
|
|
191
|
+
"""
|
|
192
|
+
Continue.
|
|
193
|
+
"""
|
|
194
|
+
return Finish;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
@id("finish-node-id")
|
|
199
|
+
Text Finish = new() {
|
|
200
|
+
"""
|
|
201
|
+
Until next time.
|
|
202
|
+
"""
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Use `@primary` explicitly; never infer it. Keep dialogue bindings flow-wide and
|
|
208
|
+
node bindings scoped to their node/children. Give each persisted condition use,
|
|
209
|
+
action invocation, mutation, pause, option, and node its own owner-scoped ID.
|
|
210
|
+
|
|
211
|
+
Use `=> Next` or a trailing block with `return Next;`; do not author a `to:`
|
|
212
|
+
argument. Require destinations for triggers, options, and outcomes. Permit
|
|
213
|
+
terminal text/actions to fall through. Keep all statements for
|
|
214
|
+
`Actions Empty = new();` inside its body.
|
|
215
|
+
|
|
216
|
+
After editing, run `neo dialogue dryrun <dialogue-ref>`. Exit 1 means the graph
|
|
217
|
+
would fail on device. Use `neo dialogue compile '<logic-block-json>'` only for
|
|
218
|
+
low-level inspection or repair of one logic block.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# CLI development
|
|
2
|
+
|
|
3
|
+
Use this reference only when changing the CLI, language service, package, or
|
|
4
|
+
the skill itself. Project authors usually do not need it.
|
|
5
|
+
|
|
6
|
+
## Contents
|
|
7
|
+
|
|
8
|
+
- Source of truth
|
|
9
|
+
- Feature-spec status
|
|
10
|
+
- Shared implementation contract
|
|
11
|
+
- Package and release maintenance
|
|
12
|
+
- Verification
|
|
13
|
+
|
|
14
|
+
## Source of truth
|
|
15
|
+
|
|
16
|
+
Treat the current CLI compiler, shared-language implementation, contract
|
|
17
|
+
registry, tests, and generated corpus as the executable source of truth. Use
|
|
18
|
+
the feature specs to understand intent and edge cases, but do not call the old
|
|
19
|
+
umbrella project-source document complete: it predates P38–P52 and contains
|
|
20
|
+
obsolete authoring examples.
|
|
21
|
+
|
|
22
|
+
When a spec status header disagrees with released implementation and tests,
|
|
23
|
+
verify the shipped package and implementation PR before teaching the feature.
|
|
24
|
+
|
|
25
|
+
## Feature-spec status
|
|
26
|
+
|
|
27
|
+
Read the relevant specs in full:
|
|
28
|
+
|
|
29
|
+
- [P38 complete schema declarations](https://github.com/ryanbliss/neo-compose-specs/blob/main/new-features/complete/p38-neo-source-complete-schema-declarations.md)
|
|
30
|
+
- [P39 authored system schema](https://github.com/ryanbliss/neo-compose-specs/blob/main/new-features/complete/p39-neo-authored-system-schema.md)
|
|
31
|
+
- [P40 sorting and sprite metadata](https://github.com/ryanbliss/neo-compose-specs/blob/main/new-features/complete/p40-sorting-groups-and-sprite-renderer-metadata.md)
|
|
32
|
+
- [P41 optional object children](https://github.com/ryanbliss/neo-compose-specs/blob/main/new-features/complete/p41-optional-object-children.md)
|
|
33
|
+
- [P42 structured leaf fields](https://github.com/ryanbliss/neo-compose-specs/blob/main/new-features/complete/p42-addressable-structured-leaf-fields.md)
|
|
34
|
+
- [P43 initializers and constructors](https://github.com/ryanbliss/neo-compose-specs/blob/main/new-features/complete/p43-neoscript-initializers-and-constructors.md)
|
|
35
|
+
- [P44 nested-row provenance](https://github.com/ryanbliss/neo-compose-specs/blob/main/new-features/complete/p44-authored-provenance-for-nested-class-rows.md)
|
|
36
|
+
- [P45 deferred runtime-child provenance](https://github.com/ryanbliss/neo-compose-specs/blob/main/new-features/p45-provenance-for-runtime-children.md)
|
|
37
|
+
- [P46 proposed provenance hardening](https://github.com/ryanbliss/neo-compose-specs/blob/main/new-features/p46-provenance-hardening.md)
|
|
38
|
+
- [P47 same-push references](https://github.com/ryanbliss/neo-compose-specs/blob/main/new-features/p47-same-push-value-references.md)
|
|
39
|
+
- [P48 animation segments](https://github.com/ryanbliss/neo-compose-specs/blob/main/new-features/p48-sprite-animation-segments.md)
|
|
40
|
+
- [P49 required construction](https://github.com/ryanbliss/neo-compose-specs/blob/main/new-features/p49-required-constructors-and-settled-members.md)
|
|
41
|
+
- [P50 loops](https://github.com/ryanbliss/neo-compose-specs/blob/main/new-features/p50-neoscript-for-and-foreach-loops.md)
|
|
42
|
+
- [P51 switch](https://github.com/ryanbliss/neo-compose-specs/blob/main/new-features/p51-neoscript-switch-statements.md)
|
|
43
|
+
- [P52 try/catch](https://github.com/ryanbliss/neo-compose-specs/blob/main/new-features/p52-neoscript-try-catch-blocks.md)
|
|
44
|
+
|
|
45
|
+
P38–P44 and P47–P51 are implemented. P45 remains deliberately deferred; do not
|
|
46
|
+
build or teach runtime-child provenance. P46 is a hardening proposal, not an
|
|
47
|
+
authoring capability. P52's document header still says proposed, but try/catch
|
|
48
|
+
shipped in CLI 0.18; implementation and parity tests take precedence over that
|
|
49
|
+
stale header.
|
|
50
|
+
|
|
51
|
+
## Shared implementation contract
|
|
52
|
+
|
|
53
|
+
Keep parsing, strict resolution, formatting, diagnostics, symbols, and
|
|
54
|
+
NeoScript compilation in the browser-safe `@neocompose/neoscript-language`
|
|
55
|
+
service. Monaco, VS Code, CLI, trusted server, and corpus tooling must consume
|
|
56
|
+
that shared implementation. Do not add language intelligence to one adapter.
|
|
57
|
+
|
|
58
|
+
Keep project context refreshable without accumulating stale Monaco providers or
|
|
59
|
+
LSP state. Preserve diagnostics, completion, hover, signatures, definition,
|
|
60
|
+
references, rename, symbols, semantic tokens, code actions, and formatting
|
|
61
|
+
parity for `.neo` and `.neoflow`.
|
|
62
|
+
|
|
63
|
+
Keep trusted server recompilation and verification independent of client
|
|
64
|
+
outputs. The CLI emits a complete source bundle; client-generated IR, structural
|
|
65
|
+
rows, hashes, storage stamps, and pending IDs are not trusted.
|
|
66
|
+
|
|
67
|
+
## Package and release maintenance
|
|
68
|
+
|
|
69
|
+
Keep the npm artifact native JavaScript. Do not publish `.csproj`, `.cs`,
|
|
70
|
+
`.dll`, Roslyn host metadata, generated C# SDK output, or trusted server-only
|
|
71
|
+
wrappers.
|
|
72
|
+
|
|
73
|
+
The marker near the top of `SKILL.md` must exactly match the package version:
|
|
74
|
+
|
|
75
|
+
```html
|
|
76
|
+
<!-- reviewed-through-cli: 0.19.4 -->
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The quoted version above is checked too, so this instruction cannot go stale
|
|
80
|
+
while the skill advances.
|
|
81
|
+
|
|
82
|
+
For every CLI version bump, audit all behavior and public syntax added since the
|
|
83
|
+
marker, update the skill/references where needed, then advance the marker. Never
|
|
84
|
+
bump it mechanically. `npm run doctor` and package verification both fail when
|
|
85
|
+
it drifts, so the audit lands in the same change as the bump rather than
|
|
86
|
+
blocking a publish after the release has merged.
|
|
87
|
+
|
|
88
|
+
Every version bump also gets a `CHANGELOG.md` entry, newest first, under a
|
|
89
|
+
`## [version] - date` heading. The same check grades it, so an undocumented
|
|
90
|
+
release fails alongside a stale marker.
|
|
91
|
+
|
|
92
|
+
Keep `agents/openai.yaml` and every directly linked reference inside the
|
|
93
|
+
published `skills/neocompose-cli/` tree. Keep the frontmatter limited to
|
|
94
|
+
`name` and `description`.
|
|
95
|
+
|
|
96
|
+
## Verification
|
|
97
|
+
|
|
98
|
+
Run focused language/compiler tests for changed behavior, then:
|
|
99
|
+
|
|
100
|
+
```sh
|
|
101
|
+
python3 <skill-creator>/scripts/quick_validate.py cli/skills/neocompose-cli
|
|
102
|
+
npm --prefix cli run test:package
|
|
103
|
+
npm run typecheck
|
|
104
|
+
npm run doctor
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Also run contract/corpus checks, Monaco/browser parity, VSIX packaging, and
|
|
108
|
+
interactive sample no-op verification when the corresponding implementation
|
|
109
|
+
surface changes. Run `npm run doctor` last; do not rerun successful suites only
|
|
110
|
+
because doctor formatted files.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Commands and synchronization
|
|
2
|
+
|
|
3
|
+
Use this reference for workspace synchronization, conflicts, repair commands,
|
|
4
|
+
branches, releases, history, and authentication.
|
|
5
|
+
|
|
6
|
+
## Contents
|
|
7
|
+
|
|
8
|
+
- Pull, inspect, and push
|
|
9
|
+
- Conflicts and files
|
|
10
|
+
- Low-level operations
|
|
11
|
+
- Branches, releases, and history
|
|
12
|
+
- Authentication and automation
|
|
13
|
+
|
|
14
|
+
## Pull, inspect, and push
|
|
15
|
+
|
|
16
|
+
Use the high-level source workflow:
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
neo pull
|
|
20
|
+
neo status
|
|
21
|
+
neo diff
|
|
22
|
+
neo dialogue dryrun <dialogue-ref>
|
|
23
|
+
neo push --dry-run
|
|
24
|
+
neo push
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Use `neo pull --regenerate-source-names` to refresh readable naming while
|
|
28
|
+
retaining normal merge behavior. Use `--reset` to regenerate canonical source
|
|
29
|
+
and managed binaries. Use `--force` only to discard local edits for the server
|
|
30
|
+
version.
|
|
31
|
+
|
|
32
|
+
Treat `neo push --dry-run` as a full local commit rehearsal: bundle emission,
|
|
33
|
+
pending-ID assignment on a clone, transport shapes, hashes, and repeat
|
|
34
|
+
verification all run. It also runs the server's preparation phase locally —
|
|
35
|
+
schema-commit materialization, seeded structural rows, and whole-graph
|
|
36
|
+
validation — so a rejection that used to cost a real push and return an opaque
|
|
37
|
+
`preparation-failed` now reports before anything is sent. It does not prompt,
|
|
38
|
+
upload, commit, or rewrite source.
|
|
39
|
+
|
|
40
|
+
A clean dry-run does not promise a clean push. The workspace holds its
|
|
41
|
+
last-pulled state, so everything that grades the live head is still server-only:
|
|
42
|
+
re-verification of the uploaded bundle bytes, project-file staging, the
|
|
43
|
+
snapshot-id and content-head cross-check, admission classification, chunk
|
|
44
|
+
planning, and finalization. Pull again before pushing if time has passed.
|
|
45
|
+
|
|
46
|
+
One accepted push atomically commits records, main-locale text, stored bindings,
|
|
47
|
+
and staged file metadata under CAS. Pending durable IDs and source rewrites
|
|
48
|
+
become real only after acceptance.
|
|
49
|
+
|
|
50
|
+
## Conflicts and files
|
|
51
|
+
|
|
52
|
+
Resolve source conflicts by editing the final intended source. Use
|
|
53
|
+
`neo resolve --mine` or `neo resolve --theirs` only for a deliberate whole-side
|
|
54
|
+
choice. Pull, inspect, dry-run, and retry; there is no force-CAS path.
|
|
55
|
+
|
|
56
|
+
For `base-hash-conflict`, pull and merge. For `version-bump-required`, inspect
|
|
57
|
+
the classification and use `neo push --accept-bump` only when the bump is
|
|
58
|
+
intended.
|
|
59
|
+
|
|
60
|
+
Inspect and stage files with:
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
neo files list
|
|
64
|
+
neo files add <path> [--template <Name>]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Keep local bytes during binary conflict resolution. Inspect the server copy
|
|
68
|
+
under `.neo/conflicts/files/<file-id>/`.
|
|
69
|
+
|
|
70
|
+
## Low-level operations
|
|
71
|
+
|
|
72
|
+
Prefer checked-in source for normal authoring. Use low-level commands only for
|
|
73
|
+
automation, repair, or inspection:
|
|
74
|
+
|
|
75
|
+
```sh
|
|
76
|
+
neo records query [--kind <recordKind>]
|
|
77
|
+
neo records get <kind> <id>
|
|
78
|
+
neo values list [memberId]
|
|
79
|
+
neo values get <valueId>
|
|
80
|
+
neo values set <valueId> '<raw-json-value>'
|
|
81
|
+
neo values bind <staticMemberId> <valueId>
|
|
82
|
+
neo values unbind <staticMemberId>
|
|
83
|
+
neo values create '<raw-json-value>' [--class <classId>] --bind <staticMemberId>
|
|
84
|
+
neo loc locales
|
|
85
|
+
neo loc list
|
|
86
|
+
neo loc set <textId> <locale> "text"
|
|
87
|
+
neo dialogue list
|
|
88
|
+
neo dialogue show <ref>
|
|
89
|
+
neo dialogue compile '<logic-block-json>'
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Pass a JSON-array batch on stdin or `--file` where supported so related writes
|
|
93
|
+
remain atomic. A `values set` payload is the raw value, not
|
|
94
|
+
`{ "value": ... }`. Static bind/unbind changes the live binding.
|
|
95
|
+
|
|
96
|
+
Low-level commands do not rewrite local project source. Run `neo pull` after
|
|
97
|
+
any low-level or web mutation before further source edits or push.
|
|
98
|
+
|
|
99
|
+
## Branches, releases, and history
|
|
100
|
+
|
|
101
|
+
```sh
|
|
102
|
+
neo branch list
|
|
103
|
+
neo branch create <name> [--from <ref>]
|
|
104
|
+
neo branch switch <nameOrId>
|
|
105
|
+
neo branch refresh [--dry-run]
|
|
106
|
+
neo merge <branch> [--dry-run] [--migrate]
|
|
107
|
+
neo release cut [--bump major|minor|patch] [--dry-run]
|
|
108
|
+
neo history inspect
|
|
109
|
+
neo history log
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Treat branches as copy-on-write forks and releases as immutable snapshots. The
|
|
113
|
+
server derives the minimum compatibility bump; a caller may only raise it.
|
|
114
|
+
Require the release login profile for release operations.
|
|
115
|
+
|
|
116
|
+
## Authentication and automation
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
neo login [--api <url>] [--profile editor|release] [--save-project <id>]
|
|
120
|
+
neo doctor
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
The Node CLI talks directly to authenticated Convex APIs through the
|
|
124
|
+
session-gated CAS boundary. Tokens use the OS credential store when available
|
|
125
|
+
and a protected file only as fallback. Use `NEO_COMPOSE_TOKEN` or
|
|
126
|
+
`--token-stdin` in CI. The editor profile cannot publish releases; server
|
|
127
|
+
scopes remain the security boundary.
|
|
128
|
+
|
|
129
|
+
Pass explicit project/version IDs and flags in automation. Prefer `--json` for
|
|
130
|
+
machine-readable output and do not depend on interactive pickers or confirms.
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# Declarations and construction
|
|
2
|
+
|
|
3
|
+
Use this reference for schema declarations, annotations, member contracts,
|
|
4
|
+
constructors, and construction-time settlement.
|
|
5
|
+
|
|
6
|
+
## Contents
|
|
7
|
+
|
|
8
|
+
- Declaration rules
|
|
9
|
+
- Annotations and settings
|
|
10
|
+
- Member and relation rules
|
|
11
|
+
- Computed initializers and declared constructors
|
|
12
|
+
- Required constructors and settlement
|
|
13
|
+
- Inheritance construction
|
|
14
|
+
|
|
15
|
+
## Declaration rules
|
|
16
|
+
|
|
17
|
+
Declare classes, interfaces, and enums only at the top level of definition
|
|
18
|
+
`.neo` files. Do not nest them in a type, body, accessor, lambda, initializer,
|
|
19
|
+
migration, or `.neoflow` file. NeoFlow has its own single-dialogue exception.
|
|
20
|
+
|
|
21
|
+
Persist the declared identifier as the schema name; there is no separate
|
|
22
|
+
technical/display name. Keep existing IDs stable.
|
|
23
|
+
|
|
24
|
+
```neo
|
|
25
|
+
@id("interface-named-id")
|
|
26
|
+
interface INamed {
|
|
27
|
+
@id("named-name-id")
|
|
28
|
+
string Name { get; }
|
|
29
|
+
|
|
30
|
+
@id("named-use-id")
|
|
31
|
+
void Use(SomeClass context);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@id("class-item-id")
|
|
35
|
+
@storage(allowed: .Immutable)
|
|
36
|
+
abstract class Item<TContext extends SomeClass> : INamed {
|
|
37
|
+
@id("item-name-id")
|
|
38
|
+
@settings(localizable: true, searchKey: true)
|
|
39
|
+
public virtual string Name = "";
|
|
40
|
+
|
|
41
|
+
@id("item-use-id")
|
|
42
|
+
public abstract void Use(TContext context);
|
|
43
|
+
|
|
44
|
+
@id("item-load-id")
|
|
45
|
+
public native async bool Load(TContext context);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Use ordinary `public`, `protected`, `private`, `static`, `virtual`, `abstract`,
|
|
50
|
+
and `override` semantics. Interfaces are non-generic. Do not put `@id` on
|
|
51
|
+
inferred list/dictionary type entries or on derived structural descriptors.
|
|
52
|
+
|
|
53
|
+
A bodyless function must be `abstract`, `native`, or an interface contract.
|
|
54
|
+
`async` alone does not make it valid.
|
|
55
|
+
|
|
56
|
+
## Annotations and settings
|
|
57
|
+
|
|
58
|
+
Never author `@system`. It marks platform-owned system records and is rejected
|
|
59
|
+
on project-authored declarations, members, overrides, values, and files. The
|
|
60
|
+
system types visible in project source may themselves have system provenance;
|
|
61
|
+
that does not authorize copying the annotation onto authored records.
|
|
62
|
+
|
|
63
|
+
Use context-aware typed annotations:
|
|
64
|
+
|
|
65
|
+
- `@settings(...)` for localizable/search/index/editor constraints.
|
|
66
|
+
- `@storage(allowed: ...)` on classes and storage keys on members.
|
|
67
|
+
- `@locked` for author-protected declarations supported by the contract.
|
|
68
|
+
- `@hidden` only on a class; absence means visible.
|
|
69
|
+
- `@relations(...)` only on the source class of a specialized direct relation.
|
|
70
|
+
|
|
71
|
+
Use numeric literals for numeric limits. Keep annotations separate instead of
|
|
72
|
+
inventing a generic metadata bag.
|
|
73
|
+
|
|
74
|
+
`@index(member: Field, unique: ...)` and
|
|
75
|
+
`@column(member: Field, width: ..., hidden: ..., frozen: ..., wrapContent: ...)`
|
|
76
|
+
are repeatable List annotations. `member:` is a bare field name on the entry
|
|
77
|
+
class. An index accepts a non-localizable string or single-select enum. A column
|
|
78
|
+
may target `__other__` for the default layout of subclass-added fields.
|
|
79
|
+
|
|
80
|
+
Use `.EnumCase` only when context fixes one enum type; otherwise write
|
|
81
|
+
`EnumType.Case`. Defaults and settings must be statically analyzable.
|
|
82
|
+
|
|
83
|
+
## Member and relation rules
|
|
84
|
+
|
|
85
|
+
Infer member kind and optionality from its type and nullability. Treat the
|
|
86
|
+
initializer as its default. Omitted fields materialize their current defaults
|
|
87
|
+
once when an instance is created; later default edits do not mutate existing
|
|
88
|
+
instances.
|
|
89
|
+
|
|
90
|
+
A lookup member's arity is its declared type. `public Config Config` selects one
|
|
91
|
+
entry and its declared type is the entry type; `public Set<Config> Configs`
|
|
92
|
+
selects several. An initializer body for a single-select lookup returns one
|
|
93
|
+
entry, never a set.
|
|
94
|
+
|
|
95
|
+
Keep generic project relations as typed top-level declarations in
|
|
96
|
+
`Relations.neo`. Let the compiler derive identities for owned descriptors and
|
|
97
|
+
concrete generic bindings.
|
|
98
|
+
|
|
99
|
+
For world layer links, instantiate a project-authored descendant of
|
|
100
|
+
`NeoTileLayerLink` or `NeoObjectLayerLink` that resolves exactly one
|
|
101
|
+
`targetLayer` relation. The relation is the complete binding; do not add
|
|
102
|
+
value-level target metadata.
|
|
103
|
+
|
|
104
|
+
## Computed initializers and declared constructors
|
|
105
|
+
|
|
106
|
+
Use an expression initializer when a member default must be evaluated during
|
|
107
|
+
construction rather than stored as a closed literal. It runs for every newly
|
|
108
|
+
constructed instance in push, web, and SDK contexts; it does not rewrite
|
|
109
|
+
existing saved instances.
|
|
110
|
+
|
|
111
|
+
Declare overloadable constructors as members when each constructor body owns
|
|
112
|
+
its parameter scope:
|
|
113
|
+
|
|
114
|
+
```neo
|
|
115
|
+
class Foo {
|
|
116
|
+
public string Bar;
|
|
117
|
+
public bool IsFun;
|
|
118
|
+
|
|
119
|
+
public Foo(string bar, bool isFun) {
|
|
120
|
+
this.Bar = bar;
|
|
121
|
+
this.IsFun = isFun;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
Foo value = new(bar: "BAR", isFun: true) { IsFun = false };
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Constructor parameters are visible only in that constructor body. Do not read
|
|
129
|
+
them from member initializers. Each declared constructor must settle every
|
|
130
|
+
required member on its own; a call-site block cannot repair an invalid
|
|
131
|
+
constructor declaration.
|
|
132
|
+
|
|
133
|
+
## Required constructors and settlement
|
|
134
|
+
|
|
135
|
+
Put one required constructor on the class header when the entire class body
|
|
136
|
+
needs the parameters:
|
|
137
|
+
|
|
138
|
+
```neo
|
|
139
|
+
class Foo(string bar, bool isFun) {
|
|
140
|
+
public string Bar = bar;
|
|
141
|
+
public bool IsFun;
|
|
142
|
+
|
|
143
|
+
init {
|
|
144
|
+
if (isFun) {
|
|
145
|
+
IsFun = true;
|
|
146
|
+
} else {
|
|
147
|
+
IsFun = false;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
Foo value = new(bar: "BAR", isFun: true) { IsFun = false };
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Header parameters are in scope throughout the class. Permit at most one `init`
|
|
156
|
+
block. A header constructor disables implicit parameterless `new` and forbids
|
|
157
|
+
additional constructor overloads. Its derived constructor identity is not
|
|
158
|
+
authored with `@id`.
|
|
159
|
+
|
|
160
|
+
Treat a non-null member with no default as required. Treat a nullable member
|
|
161
|
+
with no default as implicitly `null` and therefore settled:
|
|
162
|
+
|
|
163
|
+
```neo
|
|
164
|
+
class Result {
|
|
165
|
+
public string Value; // required
|
|
166
|
+
public bool Ok = true; // settled by default
|
|
167
|
+
public string? Note; // settled by implicit null
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Accept settlement only from:
|
|
172
|
+
|
|
173
|
+
1. The member's own initializer expression.
|
|
174
|
+
2. Assignment on every reachable path in `init` or a constructor body.
|
|
175
|
+
3. A call-site initializer entry when using an otherwise valid construction
|
|
176
|
+
path.
|
|
177
|
+
|
|
178
|
+
Do not infer settlement from a side effect inside another function. Require
|
|
179
|
+
full `if`/`else` coverage when an assignment is conditional.
|
|
180
|
+
|
|
181
|
+
Apply construction in this order: base construction, member initializers,
|
|
182
|
+
`init` or declared-constructor body, then the call-site initializer block. The
|
|
183
|
+
call-site block wins when it refines an earlier value.
|
|
184
|
+
|
|
185
|
+
## Inheritance construction
|
|
186
|
+
|
|
187
|
+
Treat the base clause as a construction expression:
|
|
188
|
+
|
|
189
|
+
```neo
|
|
190
|
+
class Base(string name) {
|
|
191
|
+
public string Name = name;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
class Child(string name, int rank) : Base(name) {
|
|
195
|
+
public int Rank = rank;
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Call a declared base constructor with `: Base(args)`. When a base has no
|
|
200
|
+
constructor, use a base initializer block to settle required inherited members:
|
|
201
|
+
|
|
202
|
+
```neo
|
|
203
|
+
class Base {
|
|
204
|
+
public string Name;
|
|
205
|
+
public bool Enabled = true;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
class Child(string name) : Base {
|
|
209
|
+
Name = name,
|
|
210
|
+
Enabled = false
|
|
211
|
+
} {
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Bind generic base arguments explicitly when needed. Reject a subclass that
|
|
216
|
+
omits required base arguments or leaves any inherited required member
|
|
217
|
+
unsettled.
|