@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.
|
|
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
|
|
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.
|
|
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
|
@@ -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.
|
|
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.
|
|
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.
|