@neocompose/cli 0.36.7 → 0.36.8

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,16 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.36.8] - 2026-08-20
4
+
5
+ ### Fixed
6
+
7
+ - Ignore server-compiled initializer IR when deciding whether both sides of a
8
+ pull changed authored value source, preventing compiler-only refreshes from
9
+ producing false conflicts.
10
+ - Let `neo resolve --mine` and `neo resolve --theirs` clear metadata-only
11
+ record conflicts retained by older pulls even when no textual marker was
12
+ written.
13
+
3
14
  ## [0.36.7] - 2026-08-20
4
15
 
5
16
  ### Fixed
package/dist/neo.mjs CHANGED
@@ -76389,6 +76389,23 @@ var init_workspace = __esm({
76389
76389
  }
76390
76390
  });
76391
76391
 
76392
+ // src/project-sync/value-record-comparison.ts
76393
+ function valueRecordComparisonBody(value) {
76394
+ if (!isObjectRecord2(value)) return value;
76395
+ const init = value.init;
76396
+ const body = value.value;
76397
+ const normalizedInit = isObjectRecord2(init) && init.compiled !== void 0 ? { ...init, compiled: void 0 } : init;
76398
+ const normalizedBody = isObjectRecord2(body) && typeof body.code === "string" && body.action !== void 0 ? { ...body, action: void 0 } : body;
76399
+ if (normalizedInit === init && normalizedBody === body) return value;
76400
+ return { ...value, init: normalizedInit, value: normalizedBody };
76401
+ }
76402
+ var init_value_record_comparison = __esm({
76403
+ "src/project-sync/value-record-comparison.ts"() {
76404
+ "use strict";
76405
+ init_projection();
76406
+ }
76407
+ });
76408
+
76392
76409
  // src/project-source/lower-configuration.ts
76393
76410
  function lowerConfiguration(base, analysis) {
76394
76411
  const globals = analysis.configuration.globals;
@@ -96308,15 +96325,6 @@ function recordsSemanticallyEqual(recordKind, left, right) {
96308
96325
  }
96309
96326
  return canonicallyEqual(left, right);
96310
96327
  }
96311
- function valueRecordComparisonBody(value) {
96312
- if (!isObjectRecord2(value)) return value;
96313
- const init = value.init;
96314
- const body = value.value;
96315
- const normalizedInit = isObjectRecord2(init) && init.compiled !== void 0 ? { ...init, compiled: void 0 } : init;
96316
- const normalizedBody = isObjectRecord2(body) && typeof body.code === "string" && body.action !== void 0 ? { ...body, action: void 0 } : body;
96317
- if (normalizedInit === init && normalizedBody === body) return value;
96318
- return { ...value, init: normalizedInit, value: normalizedBody };
96319
- }
96320
96328
  function describeChange(change) {
96321
96329
  const name = isObjectRecord2(change.nextData) && typeof change.nextData.name === "string" ? change.nextData.name : isObjectRecord2(change.baseData) && typeof change.baseData.name === "string" ? change.baseData.name : change.recordId;
96322
96330
  const location3 = change.file === null ? "" : ` (${change.file})`;
@@ -96347,6 +96355,7 @@ var init_workspace_status_core = __esm({
96347
96355
  init_source_diagnostics();
96348
96356
  init_migration_files();
96349
96357
  init_projection();
96358
+ init_value_record_comparison();
96350
96359
  init_project_manifest();
96351
96360
  init_src();
96352
96361
  init_lower_schema();
@@ -105997,6 +106006,104 @@ var init_conflict_markers = __esm({
105997
106006
  }
105998
106007
  });
105999
106008
 
106009
+ // src/project-sync/source-record-comparison.ts
106010
+ function sourceAuthoredRecordsSemanticallyEqual(recordKind, left, right, mainLocale) {
106011
+ if (isSchemaRecordKindV4(recordKind)) {
106012
+ return schemaDocumentAuthoredSemanticallyEqual(recordKind, left, right);
106013
+ }
106014
+ return canonicallyEqual(
106015
+ sourceComparableRecord(recordKind, left, mainLocale),
106016
+ sourceComparableRecord(recordKind, right, mainLocale)
106017
+ );
106018
+ }
106019
+ function sourceComparableObjectRecord(recordKind, value, mainLocale) {
106020
+ const comparable = sourceComparableRecord(recordKind, value, mainLocale);
106021
+ if (!isObjectRecord2(comparable)) {
106022
+ throw new Error(
106023
+ `Cannot merge ${recordKind} record because its source-comparable payload is not an object.`
106024
+ );
106025
+ }
106026
+ return comparable;
106027
+ }
106028
+ function workspaceMainLocale(records2) {
106029
+ const config = Object.values(records2).find(
106030
+ (record3) => record3.recordKind === "localization-config"
106031
+ );
106032
+ const data = config?.data;
106033
+ return isObjectRecord2(data) && typeof data.mainLocale === "string" ? data.mainLocale : "en-US";
106034
+ }
106035
+ function acceptSourceEquivalentConflictBases(workspace, mainLocale = workspaceMainLocale(workspace.state.records)) {
106036
+ let accepted = 0;
106037
+ for (const [key, record3] of Object.entries(workspace.state.records)) {
106038
+ if (typeof record3.conflictServerHash !== "string") continue;
106039
+ if (!sourceAuthoredRecordsSemanticallyEqual(
106040
+ record3.recordKind,
106041
+ record3.data,
106042
+ record3.conflictServerData,
106043
+ mainLocale
106044
+ )) {
106045
+ continue;
106046
+ }
106047
+ const next = {
106048
+ ...record3,
106049
+ contentHash: record3.conflictServerHash,
106050
+ data: record3.conflictServerData
106051
+ };
106052
+ delete next.conflictServerHash;
106053
+ delete next.conflictServerData;
106054
+ workspace.state.records[key] = next;
106055
+ accepted += 1;
106056
+ }
106057
+ return accepted;
106058
+ }
106059
+ function sourceComparableRecord(recordKind, value, mainLocale) {
106060
+ return sourceComparableValue(
106061
+ recordKind,
106062
+ recordKind === "value" ? valueRecordComparisonBody(value) : value,
106063
+ mainLocale
106064
+ );
106065
+ }
106066
+ function sourceComparableValue(recordKind, value, mainLocale) {
106067
+ if (Array.isArray(value)) {
106068
+ return value.map(
106069
+ (entry) => sourceComparableValue(recordKind, entry, mainLocale)
106070
+ );
106071
+ }
106072
+ if (!isObjectRecord2(value)) return value;
106073
+ const result = {};
106074
+ const hasAuthoredCode = typeof value.code === "string" || typeof value.setterCode === "string";
106075
+ for (const [key, entry] of Object.entries(value)) {
106076
+ if (key === "projectId" || key === "createdAt" || key === "updatedAt") {
106077
+ continue;
106078
+ }
106079
+ if (recordKind === "dialogue-node" && key === "layout") continue;
106080
+ if (hasAuthoredCode && (key === "getter" || key === "setter" || key === "action")) {
106081
+ continue;
106082
+ }
106083
+ if (recordKind === "localized-text" && key === "localeValues" && isObjectRecord2(entry)) {
106084
+ result[key] = Object.hasOwn(entry, mainLocale) ? {
106085
+ [mainLocale]: sourceComparableValue(
106086
+ recordKind,
106087
+ entry[mainLocale],
106088
+ mainLocale
106089
+ )
106090
+ } : {};
106091
+ continue;
106092
+ }
106093
+ result[key] = sourceComparableValue(recordKind, entry, mainLocale);
106094
+ }
106095
+ return result;
106096
+ }
106097
+ var init_source_record_comparison = __esm({
106098
+ "src/project-sync/source-record-comparison.ts"() {
106099
+ "use strict";
106100
+ init_project_manifest();
106101
+ init_project_documents();
106102
+ init_projection();
106103
+ init_value_record_comparison();
106104
+ }
106105
+ });
106106
+
106000
106107
  // src/project-source/reset.ts
106001
106108
  import {
106002
106109
  existsSync as existsSync5,
@@ -107162,77 +107269,6 @@ function projectSourcePathsRequiringRewriteV4(args) {
107162
107269
  }
107163
107270
  return result;
107164
107271
  }
107165
- function sourceAuthoredRecordsSemanticallyEqual(recordKind, left, right, mainLocale) {
107166
- if (isSchemaRecordKindV4(recordKind)) {
107167
- return schemaDocumentAuthoredSemanticallyEqual(recordKind, left, right);
107168
- }
107169
- return canonicallyEqual(
107170
- sourceComparableRecord(recordKind, left, mainLocale),
107171
- sourceComparableRecord(recordKind, right, mainLocale)
107172
- );
107173
- }
107174
- function sourceComparableRecord(recordKind, value, mainLocale) {
107175
- if (Array.isArray(value)) {
107176
- return value.map(
107177
- (entry) => sourceComparableRecord(recordKind, entry, mainLocale)
107178
- );
107179
- }
107180
- if (!isObjectRecord2(value)) return value;
107181
- const result = {};
107182
- const hasAuthoredCode = typeof value.code === "string" || typeof value.setterCode === "string";
107183
- for (const [key, entry] of Object.entries(value)) {
107184
- if (key === "projectId" || key === "createdAt" || key === "updatedAt") {
107185
- continue;
107186
- }
107187
- if (recordKind === "dialogue-node" && key === "layout") continue;
107188
- if (hasAuthoredCode && (key === "getter" || key === "setter" || key === "action")) {
107189
- continue;
107190
- }
107191
- if (recordKind === "localized-text" && key === "localeValues" && isObjectRecord2(entry)) {
107192
- result[key] = Object.hasOwn(entry, mainLocale) ? {
107193
- [mainLocale]: sourceComparableRecord(
107194
- recordKind,
107195
- entry[mainLocale],
107196
- mainLocale
107197
- )
107198
- } : {};
107199
- continue;
107200
- }
107201
- result[key] = sourceComparableRecord(recordKind, entry, mainLocale);
107202
- }
107203
- return result;
107204
- }
107205
- function acceptSourceEquivalentConflictBases(workspace, mainLocale) {
107206
- let accepted = 0;
107207
- for (const [key, record3] of Object.entries(workspace.state.records)) {
107208
- if (typeof record3.conflictServerHash !== "string") continue;
107209
- if (!sourceAuthoredRecordsSemanticallyEqual(
107210
- record3.recordKind,
107211
- record3.data,
107212
- record3.conflictServerData,
107213
- mainLocale
107214
- )) {
107215
- continue;
107216
- }
107217
- const next = {
107218
- ...record3,
107219
- contentHash: record3.conflictServerHash,
107220
- data: record3.conflictServerData
107221
- };
107222
- delete next.conflictServerHash;
107223
- delete next.conflictServerData;
107224
- workspace.state.records[key] = next;
107225
- accepted += 1;
107226
- }
107227
- return accepted;
107228
- }
107229
- function workspaceMainLocale(records2) {
107230
- const config = Object.values(records2).find(
107231
- (record3) => record3.recordKind === "localization-config"
107232
- );
107233
- const data = config?.data;
107234
- return isObjectRecord2(data) && typeof data.mainLocale === "string" ? data.mainLocale : "en-US";
107235
- }
107236
107272
  function mergeProjectDocumentRecord(recordKind, base, server, local, mainLocale) {
107237
107273
  if (isSchemaRecordKindV4(recordKind)) {
107238
107274
  return mergeSchemaDocumentRecord(recordKind, base, server, local);
@@ -107246,15 +107282,6 @@ function mergeProjectDocumentRecord(recordKind, base, server, local, mainLocale)
107246
107282
  }
107247
107283
  });
107248
107284
  }
107249
- function sourceComparableObjectRecord(recordKind, value, mainLocale) {
107250
- const comparable = sourceComparableRecord(recordKind, value, mainLocale);
107251
- if (!isObjectRecord2(comparable)) {
107252
- throw new Error(
107253
- `Cannot merge ${recordKind} record because its source-comparable payload is not an object.`
107254
- );
107255
- }
107256
- return comparable;
107257
- }
107258
107285
  function buildEmitRecordSet(document, plans, side) {
107259
107286
  const records2 = /* @__PURE__ */ new Map();
107260
107287
  for (const [key, plan] of plans) {
@@ -107313,6 +107340,7 @@ var init_pull = __esm({
107313
107340
  init_conflict_markers();
107314
107341
  init_merge();
107315
107342
  init_projection();
107343
+ init_source_record_comparison();
107316
107344
  init_reset();
107317
107345
  init_project_documents();
107318
107346
  init_project_document_cache();
@@ -114157,7 +114185,7 @@ var init_registry2 = __esm({
114157
114185
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
114158
114186
  formatVersion: 3,
114159
114187
  contractVersion: "3.14",
114160
- cliVersion: "0.36.7",
114188
+ cliVersion: "0.36.8",
114161
114189
  projectFileUploadBatchSize: 32,
114162
114190
  documentRecords: {
114163
114191
  member: {
@@ -120473,6 +120501,7 @@ __export(resolve_exports, {
120473
120501
  import { readFileSync as readFileSync19, rmSync as rmSync8, writeFileSync as writeFileSync14 } from "node:fs";
120474
120502
  import { join as join21 } from "node:path";
120475
120503
  function runResolve(workspace, side) {
120504
+ const resolvedRecords = acceptSourceEquivalentConflictBases(workspace);
120476
120505
  let resolvedFiles = 0;
120477
120506
  for (const filePath of listProjectSourceFilesV4(workspace.root)) {
120478
120507
  const source = readFileSync19(filePath, "utf8");
@@ -120506,15 +120535,15 @@ function runResolve(workspace, side) {
120506
120535
  delete binary.conflict;
120507
120536
  resolvedBinaries += 1;
120508
120537
  }
120509
- if (resolvedBinaries > 0) {
120538
+ if (resolvedRecords > 0 || resolvedBinaries > 0) {
120510
120539
  writeWorkspaceState(workspace.root, workspace.state);
120511
120540
  }
120512
- if (resolvedFiles === 0 && resolvedBinaries === 0) {
120513
- console.log("No conflict markers found.");
120541
+ if (resolvedFiles === 0 && resolvedRecords === 0 && resolvedBinaries === 0) {
120542
+ console.log("No conflicts found.");
120514
120543
  return;
120515
120544
  }
120516
120545
  console.log(
120517
- `Resolved ${resolvedFiles} source file(s) and ${resolvedBinaries} binary file(s) keeping the "${side}" side. Review with "neo diff", then "neo push".`
120546
+ `Resolved ${resolvedFiles} source file(s), ${resolvedRecords} record conflict(s), and ${resolvedBinaries} binary file(s) keeping the "${side}" side. Review with "neo diff", then "neo push".`
120518
120547
  );
120519
120548
  }
120520
120549
  function resolveMarkers(source, side) {
@@ -120563,6 +120592,7 @@ var init_resolve = __esm({
120563
120592
  init_workspace_status();
120564
120593
  init_source_diagnostics();
120565
120594
  init_project_files();
120595
+ init_source_record_comparison();
120566
120596
  }
120567
120597
  });
120568
120598
 
@@ -120759,7 +120789,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
120759
120789
  async function main() {
120760
120790
  const args = parseArgs(process.argv.slice(2));
120761
120791
  if (args.command === "--version") {
120762
- console.log("0.36.7");
120792
+ console.log("0.36.8");
120763
120793
  return;
120764
120794
  }
120765
120795
  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.36.7",
3
+ "version": "0.36.8",
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.36.7 -->
12
+ <!-- reviewed-through-cli: 0.36.8 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -210,6 +210,11 @@ Conflict source deliberately fails compilation. Edit the desired final source,
210
210
  or use `neo resolve --mine|--theirs` as a whole-side convenience. Then pull,
211
211
  review, dry-run, and push again.
212
212
 
213
+ Server-derived compilation metadata is not authored source and does not create
214
+ a pull conflict by itself. If an older CLI retained such a metadata-only
215
+ conflict without writing source markers, either `neo resolve` side accepts the
216
+ newer server metadata as the base while preserving any real local source edit.
217
+
213
218
  `neo push` sends the authenticated semantic diff plus a deterministic source
214
219
  hash, while the server recompiles the changed NeoScript bodies and
215
220
  schema-affected callers and commits only that diff under CAS. Use
@@ -83,7 +83,7 @@ wrappers.
83
83
  The marker near the top of `SKILL.md` must exactly match the package version:
84
84
 
85
85
  ```html
86
- <!-- reviewed-through-cli: 0.36.7 -->
86
+ <!-- reviewed-through-cli: 0.36.8 -->
87
87
  ```
88
88
 
89
89
  The quoted version above is checked too, so this instruction cannot go stale
@@ -79,6 +79,11 @@ Resolve source conflicts by editing the final intended source. Use
79
79
  `neo resolve --mine` or `neo resolve --theirs` only for a deliberate whole-side
80
80
  choice. Pull, inspect, dry-run, and retry; there is no force-CAS path.
81
81
 
82
+ Compiled initializer IR is server-owned and does not participate in authored
83
+ three-way merge decisions. `neo resolve` also repairs metadata-only conflict
84
+ bookkeeping left by an older pull when no source marker exists; accepting the
85
+ newer server metadata does not overwrite the tracked source.
86
+
82
87
  For `base-hash-conflict`, pull and merge. For `version-bump-required`, inspect
83
88
  the classification and use `neo push --accept-bump` only when the bump is
84
89
  intended.