@neocompose/cli 0.67.0 → 0.67.1

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.67.1] - 2026-10-04
4
+
5
+ - Pull adopts records the server only recompiled into `.neo/state.json` before it reads local sources, even when the same pull also merges authored changes. Previously one authored change anywhere in the pull sent every recompiled record through the merge, which read the stale baseline and failed.
6
+ - Status, push, and pull refuse a baseline holding a compiled body this CLI cannot read, name the record, and say to run `neo pull`, or `neo pull --force` after backing up local edits when pull cannot refresh it. Previously they failed with "init.compiled carries compiler revision 16" and suggested `npm run compiler-revision:mint`, which cannot fix a local file.
7
+ - A baseline pulled before 0.67.0 also holds 64-bit floats, which only the server's copy rounded. Plain `neo pull` cannot refresh it. Commit or back up local edits, run `neo pull --force` once, then restore them.
8
+
3
9
  ## [0.67.0] - 2026-10-04
4
10
 
5
11
  - `float` is a 32-bit float everywhere, as in C#. Float literals, defaults, constructor arguments, and stored values round to the nearest 32-bit float, and pull prints the shortest decimal that reads back as that float, so `0.7` stays `0.7`. Previously push stored the 64-bit double, and Unity read a value that its own `float` could not hold.
package/dist/neo.mjs CHANGED
@@ -30114,7 +30114,7 @@ var init_registry = __esm({
30114
30114
  "../packages/manifest/schema-contract/registry.mjs"() {
30115
30115
  "use strict";
30116
30116
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
30117
- cliVersion: "0.67.0",
30117
+ cliVersion: "0.67.1",
30118
30118
  projectFileUploadBatchSize: 32,
30119
30119
  documentRecords: {
30120
30120
  member: {
@@ -40491,6 +40491,31 @@ function collectNeoScriptCompiledBodyStamps(record6) {
40491
40491
  visit(record6, "");
40492
40492
  return stamps;
40493
40493
  }
40494
+ function firstRejectedNeoScriptCompiledBody(node, rollout = ACTIVE_NEOSCRIPT_COMPILER_REVISION_ROLLOUT) {
40495
+ if (Array.isArray(node)) {
40496
+ for (let index = 0; index < node.length; index += 1) {
40497
+ const found = firstRejectedNeoScriptCompiledBody(node[index], rollout);
40498
+ if (found !== null) {
40499
+ return { ...found, path: prefixPath(`[${String(index)}]`, found.path) };
40500
+ }
40501
+ }
40502
+ return null;
40503
+ }
40504
+ if (typeof node !== "object" || node === null) return null;
40505
+ const object2 = node;
40506
+ if (isCompiledBodyShape(object2) && !isAcceptedNeoScriptCompilerRevision(object2.compilerRevision, rollout)) {
40507
+ return { path: "", stamp: object2.compilerRevision };
40508
+ }
40509
+ for (const key of Object.keys(object2)) {
40510
+ const found = firstRejectedNeoScriptCompiledBody(object2[key], rollout);
40511
+ if (found === null) continue;
40512
+ return { ...found, path: prefixPath(key, found.path) };
40513
+ }
40514
+ return null;
40515
+ }
40516
+ function prefixPath(head, rest) {
40517
+ return rest === "" || rest.startsWith("[") ? head + rest : `${head}.${rest}`;
40518
+ }
40494
40519
  function isCompiledBodyShape(object2) {
40495
40520
  if (!Array.isArray(object2.parameters)) return false;
40496
40521
  if (!Array.isArray(object2.instructions)) return false;
@@ -112239,6 +112264,62 @@ var init_schema_source_validation = __esm({
112239
112264
  }
112240
112265
  });
112241
112266
 
112267
+ // src/project-sync/compiler-metadata.ts
112268
+ function compilerOnlyChanges(previous, document, changedKeys) {
112269
+ const keys = changedKeys ?? /* @__PURE__ */ new Set([...Object.keys(previous), ...document.records.keys()]);
112270
+ const refreshed = /* @__PURE__ */ new Set();
112271
+ let complete2 = true;
112272
+ for (const key of keys) {
112273
+ const before = previous[key];
112274
+ const after = document.records.get(key);
112275
+ if (before !== void 0 && after !== void 0 && !after.deleted && before.contentHash === after.contentHash)
112276
+ continue;
112277
+ if (before === void 0 || after === void 0 || after.deleted || before.recordId !== after.recordId || before.recordKind !== after.recordKind || !isProjectRecordKind(after.recordKind) || !isObjectRecord2(before.data) || !isObjectRecord2(after.data) || !canonicallyEqual(
112278
+ authoredCompilerInput(after.recordKind, before.data),
112279
+ authoredCompilerInput(after.recordKind, after.data)
112280
+ )) {
112281
+ complete2 = false;
112282
+ continue;
112283
+ }
112284
+ refreshed.add(key);
112285
+ }
112286
+ return { keys: refreshed, complete: complete2 };
112287
+ }
112288
+ function authoredCompilerInput(kind, data) {
112289
+ const result = stripClientDerivedBodies(kind, data);
112290
+ stampUILogicBodies(kind, result);
112291
+ if (isObjectRecord2(result)) delete result.updatedAt;
112292
+ return result;
112293
+ }
112294
+ function assertCurrentBaseline(records2) {
112295
+ for (const [key, record6] of Object.entries(records2)) {
112296
+ if (!isProjectRecordKind(record6.recordKind)) continue;
112297
+ const rejected = firstRejectedNeoScriptCompiledBody(record6.data);
112298
+ if (rejected === null) continue;
112299
+ try {
112300
+ validateProjectRecordByKind(record6.recordKind, record6.data, {
112301
+ members: "authored-or-compiled"
112302
+ });
112303
+ continue;
112304
+ } catch {
112305
+ }
112306
+ const carried = rejected.stamp === void 0 ? "has no compiler revision stamp" : `carries compiler revision ${JSON.stringify(rejected.stamp)}`;
112307
+ throw new Error(
112308
+ `The pull baseline in .neo/state.json predates this CLI: ${key} ${rejected.path} ${carried}. Run \`neo pull\` to refresh it. If pull reports this again, commit or back up your local edits, run \`neo pull --force\`, then restore them.`
112309
+ );
112310
+ }
112311
+ }
112312
+ var init_compiler_metadata = __esm({
112313
+ "src/project-sync/compiler-metadata.ts"() {
112314
+ "use strict";
112315
+ init_project2();
112316
+ init_neo_script_derived_bodies();
112317
+ init_neoscript_compiler_revision();
112318
+ init_project_record_validation();
112319
+ init_value_comparison();
112320
+ }
112321
+ });
112322
+
112242
112323
  // src/project-sync/value-record-comparison.ts
112243
112324
  function valueRecordComparisonBody(value) {
112244
112325
  if (!isObjectRecord2(value)) return value;
@@ -118422,6 +118503,7 @@ function materializeWorkspaceStatusInitializers(workspace, status, forceRecompil
118422
118503
  });
118423
118504
  }
118424
118505
  function computeWorkspaceStatus(workspace, options) {
118506
+ assertCurrentBaseline(workspace.state.records);
118425
118507
  let phaseStarted = performance.now();
118426
118508
  options.onPhase?.("Reading project schema\u2026");
118427
118509
  const reportPhase = (phase, nextLabel) => {
@@ -120396,6 +120478,7 @@ var init_workspace_status_core = __esm({
120396
120478
  init_key_reference_spelling();
120397
120479
  init_workspace();
120398
120480
  init_source_diagnostics();
120481
+ init_compiler_metadata();
120399
120482
  init_migration_files();
120400
120483
  init_value_comparison();
120401
120484
  init_projection();
@@ -137892,40 +137975,6 @@ var init_static_value_seed_records = __esm({
137892
137975
  }
137893
137976
  });
137894
137977
 
137895
- // src/project-sync/compiler-metadata.ts
137896
- function compilerMetadataChangedKeys(previous, document, changedKeys) {
137897
- const keys = changedKeys ?? /* @__PURE__ */ new Set([...Object.keys(previous), ...document.records.keys()]);
137898
- const refreshed = /* @__PURE__ */ new Set();
137899
- for (const key of keys) {
137900
- const before = previous[key];
137901
- const after = document.records.get(key);
137902
- if (before === void 0 || after === void 0 || after.deleted || before.recordId !== after.recordId || before.recordKind !== after.recordKind || !isProjectRecordKind(after.recordKind))
137903
- return null;
137904
- if (before.contentHash === after.contentHash) continue;
137905
- if (!isObjectRecord2(before.data) || !isObjectRecord2(after.data) || !canonicallyEqual(
137906
- authoredCompilerInput(after.recordKind, before.data),
137907
- authoredCompilerInput(after.recordKind, after.data)
137908
- ))
137909
- return null;
137910
- refreshed.add(key);
137911
- }
137912
- return refreshed.size === 0 ? null : refreshed;
137913
- }
137914
- function authoredCompilerInput(kind, data) {
137915
- const result = stripClientDerivedBodies(kind, data);
137916
- stampUILogicBodies(kind, result);
137917
- if (isObjectRecord2(result)) delete result.updatedAt;
137918
- return result;
137919
- }
137920
- var init_compiler_metadata = __esm({
137921
- "src/project-sync/compiler-metadata.ts"() {
137922
- "use strict";
137923
- init_project2();
137924
- init_neo_script_derived_bodies();
137925
- init_value_comparison();
137926
- }
137927
- });
137928
-
137929
137978
  // src/commands/pull.ts
137930
137979
  var pull_exports = {};
137931
137980
  __export(pull_exports, {
@@ -138014,20 +138063,23 @@ async function runNormalPull(workspace, options, progress) {
138014
138063
  document = await fetchProjectDocument(workspace);
138015
138064
  nextCursor = document.revision === void 0 ? null : cursorFromRevision(document.revision);
138016
138065
  }
138017
- const compilerRefreshKeys = !destructive && hasBaseline && !hadConflicts && !options.regenerateSourceNames ? compilerMetadataChangedKeys(
138018
- workspace.state.records,
138019
- document,
138020
- changedRecordKeys
138021
- ) : null;
138022
- if (compilerRefreshKeys !== null) {
138023
- finishRecordOnlyDeltaPull({
138024
- workspace,
138066
+ if (!destructive && hasBaseline && !hadConflicts) {
138067
+ const compilerOnly = compilerOnlyChanges(
138068
+ workspace.state.records,
138025
138069
  document,
138026
- changedRecordKeys: compilerRefreshKeys,
138027
- nextCursor,
138028
- progress
138029
- });
138030
- return;
138070
+ changedRecordKeys
138071
+ );
138072
+ if (compilerOnly.complete && compilerOnly.keys.size > 0 && !options.regenerateSourceNames) {
138073
+ finishRecordOnlyDeltaPull({
138074
+ workspace,
138075
+ document,
138076
+ changedRecordKeys: compilerOnly.keys,
138077
+ nextCursor,
138078
+ progress
138079
+ });
138080
+ return;
138081
+ }
138082
+ adoptServerRecords(workspace, document, compilerOnly.keys);
138031
138083
  }
138032
138084
  let localByKey = /* @__PURE__ */ new Map();
138033
138085
  let localStatus = null;
@@ -138616,9 +138668,25 @@ function deltaRequiresSourceProjectionV4(args) {
138616
138668
  return false;
138617
138669
  }
138618
138670
  function finishRecordOnlyDeltaPull(args) {
138619
- const records2 = { ...args.workspace.state.records };
138620
- for (const key of args.changedRecordKeys) {
138621
- const server = args.document.records.get(key);
138671
+ adoptServerRecords(args.workspace, args.document, args.changedRecordKeys);
138672
+ if (args.nextCursor !== null) {
138673
+ args.workspace.state.documentRevisionCursor = mutableCursor(
138674
+ args.nextCursor
138675
+ );
138676
+ }
138677
+ args.workspace.state.headTransactionHash = pulledHeadTransactionHash(
138678
+ args.document
138679
+ );
138680
+ writeWorkspaceState(args.workspace.root, args.workspace.state);
138681
+ args.progress.succeed(
138682
+ `Pulled ${args.changedRecordKeys.size} changed record(s) \u2014 0 file(s) updated \u2014 0 removed.`
138683
+ );
138684
+ }
138685
+ function adoptServerRecords(workspace, document, keys) {
138686
+ if (keys.size === 0) return;
138687
+ const records2 = { ...workspace.state.records };
138688
+ for (const key of keys) {
138689
+ const server = document.records.get(key);
138622
138690
  if (server === void 0) {
138623
138691
  delete records2[key];
138624
138692
  continue;
@@ -138633,19 +138701,7 @@ function finishRecordOnlyDeltaPull(args) {
138633
138701
  ...previous?.projectBinary === void 0 ? {} : { projectBinary: previous.projectBinary }
138634
138702
  };
138635
138703
  }
138636
- args.workspace.state.records = records2;
138637
- if (args.nextCursor !== null) {
138638
- args.workspace.state.documentRevisionCursor = mutableCursor(
138639
- args.nextCursor
138640
- );
138641
- }
138642
- args.workspace.state.headTransactionHash = pulledHeadTransactionHash(
138643
- args.document
138644
- );
138645
- writeWorkspaceState(args.workspace.root, args.workspace.state);
138646
- args.progress.succeed(
138647
- `Pulled ${args.changedRecordKeys.size} changed record(s) \u2014 0 file(s) updated \u2014 0 removed.`
138648
- );
138704
+ workspace.state.records = records2;
138649
138705
  }
138650
138706
  function pulledHeadTransactionHash(document) {
138651
138707
  return document.revision?.headTransactionHash ?? null;
@@ -156777,7 +156833,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
156777
156833
  async function main() {
156778
156834
  const args = parseArgs(process.argv.slice(2));
156779
156835
  if (args.command === "--version") {
156780
- console.log("0.67.0");
156836
+ console.log("0.67.1");
156781
156837
  return;
156782
156838
  }
156783
156839
  if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.67.0",
3
+ "version": "0.67.1",
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.67.0 -->
12
+ <!-- reviewed-through-cli: 0.67.1 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -110,7 +110,7 @@ wrappers.
110
110
  The marker near the top of `SKILL.md` must exactly match the package version:
111
111
 
112
112
  ```html
113
- <!-- reviewed-through-cli: 0.67.0 -->
113
+ <!-- reviewed-through-cli: 0.67.1 -->
114
114
  ```
115
115
 
116
116
  The quoted version above is checked too, so this instruction cannot go stale
@@ -40,12 +40,14 @@ without compiling it; use `neo status` to inspect local edits and diagnostics.
40
40
  An undo/reset metadata change invalidates the cursor and causes a safe
41
41
  full-snapshot fallback.
42
42
 
43
- After a compiler upgrade, run `neo pull` with `.neo/state.json` intact. When
44
- remote changes affect only compiler-owned bodies or timestamps, pull refreshes
45
- the cached records before local compilation and leaves authored files and
46
- managed binaries untouched. It handles both changed-record pulls and full
47
- snapshot refreshes. Added or deleted records, authored changes, and unresolved
48
- conflicts still require the normal merge path.
43
+ After a compiler upgrade, run `neo pull` with `.neo/state.json` intact. Pull
44
+ refreshes every cached record whose remote change touches only compiler-owned
45
+ bodies or timestamps before local compilation, even when the same pull merges
46
+ other changes. When nothing else changed, it leaves authored files and managed
47
+ binaries untouched. Added or deleted records, authored changes, and unresolved
48
+ conflicts take the normal merge path. Status, push, and pull refuse a baseline
49
+ holding a compiled body this CLI rejects and name the record. If `neo pull`
50
+ cannot refresh it, back up local edits and run `neo pull --force`.
49
51
 
50
52
  If a push commits but source rewriting fails, `neo pull` resumes its saved
51
53
  result before checking the remote head. The recovery journal in `.neo/state.json`