lunel-cli 0.1.51 → 0.1.53

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.
@@ -96,8 +96,13 @@ export declare class CodexProvider implements AIProvider {
96
96
  private extractIncomingItem;
97
97
  private describeToolPart;
98
98
  private normalizeStructuredType;
99
+ private isFileChangeStructuredItem;
99
100
  private extractStructuredOutput;
100
101
  private extractDiffLikePayload;
102
+ private renderFileChangeEntriesBody;
103
+ private renderUnifiedDiffBody;
104
+ private extractCanonicalPatch;
105
+ private normalizedFileChangeStatus;
101
106
  private extractToolInput;
102
107
  private describeCompletedItemOutput;
103
108
  }
package/dist/ai/codex.js CHANGED
@@ -374,10 +374,12 @@ export class CodexProvider {
374
374
  case "codex/event/read":
375
375
  case "codex/event/search":
376
376
  case "codex/event/list_files":
377
+ this.emitStructuredToolPart(session, method, params, false);
378
+ return;
377
379
  case "turn/diff/updated":
378
380
  case "codex/event/turn_diff_updated":
379
381
  case "codex/event/turn_diff":
380
- this.emitStructuredToolPart(session, method, params, false);
382
+ this.emitStructuredToolPart(session, method, params, true);
381
383
  return;
382
384
  case "item/completed":
383
385
  case "codex/event/item_completed":
@@ -504,7 +506,11 @@ export class CodexProvider {
504
506
  const item = this.extractIncomingItem(params);
505
507
  const normalizedType = this.normalizeStructuredType(this.readString(item.type) ?? method);
506
508
  const itemId = this.extractItemId(params) ?? this.readString(item.id) ?? normalizedType ?? "tool";
507
- const partId = `${messageId}:tool:${itemId}`;
509
+ const fileChangeLike = this.isFileChangeStructuredItem(normalizedType, item, params);
510
+ const emittedPartType = normalizedType === "plan"
511
+ ? "reasoning"
512
+ : (fileChangeLike ? "file-change" : "tool");
513
+ const partId = `${messageId}:${emittedPartType}:${itemId}`;
508
514
  const nextText = this.extractStructuredOutput(params, item, normalizedType);
509
515
  const prevOutput = this.partTextById.get(partId) ?? "";
510
516
  const output = completed
@@ -517,14 +523,17 @@ export class CodexProvider {
517
523
  const name = this.describeToolPart(normalizedType, method, item);
518
524
  const input = this.extractToolInput(item, params);
519
525
  const outputValue = output || this.describeCompletedItemOutput(item, params, normalizedType) || undefined;
526
+ const patch = emittedPartType === "file-change"
527
+ ? this.extractCanonicalPatch(params, item)
528
+ : undefined;
520
529
  const part = {
521
530
  id: partId,
522
531
  sessionID: session.id,
523
532
  messageID: messageId,
524
- type: normalizedType === "plan" ? "reasoning" : "tool",
525
- ...(normalizedType === "plan"
533
+ type: emittedPartType,
534
+ ...(emittedPartType === "reasoning"
526
535
  ? { text: outputValue ?? "Planning..." }
527
- : { name, toolName: name, input, output: outputValue, state }),
536
+ : { name, toolName: name, input, output: outputValue, state, ...(patch ? { patch } : {}) }),
528
537
  };
529
538
  this.upsertLocalMessagePart(session, messageId, part);
530
539
  this.emitMessagePartEvent(session.id, messageId, "assistant", part);
@@ -814,16 +823,23 @@ export class CodexProvider {
814
823
  const output = this.decodeFileLikeItemText(itemObject);
815
824
  if (!output)
816
825
  continue;
826
+ const emittedPartType = this.isFileChangeStructuredItem(type, itemObject)
827
+ ? "file-change"
828
+ : "tool";
829
+ const patch = emittedPartType === "file-change"
830
+ ? this.extractCanonicalPatch({}, itemObject)
831
+ : undefined;
817
832
  messages.push({
818
833
  id: itemId,
819
834
  role: "assistant",
820
835
  parts: [{
821
- id: `${itemId}:tool`,
822
- type: "tool",
836
+ id: `${itemId}:${emittedPartType}`,
837
+ type: emittedPartType,
823
838
  name: type,
824
839
  toolName: type,
825
840
  output,
826
841
  state: "completed",
842
+ ...(patch ? { patch } : {}),
827
843
  sessionID: threadId,
828
844
  messageID: itemId,
829
845
  }],
@@ -874,11 +890,16 @@ export class CodexProvider {
874
890
  return `${this.normalizedCommandPhase(status)} ${this.shortCommand(command)}`;
875
891
  }
876
892
  decodeFileLikeItemText(itemObject) {
893
+ const status = this.normalizedFileChangeStatus(itemObject, true);
894
+ const changesBody = this.renderFileChangeEntriesBody(itemObject.changes);
895
+ if (changesBody) {
896
+ return `Status: ${status}\n\n${changesBody}`;
897
+ }
898
+ const diff = this.firstString(itemObject, ["diff", "unified_diff", "unifiedDiff", "patch"]);
899
+ if (diff?.trim()) {
900
+ return this.renderUnifiedDiffBody(diff, status);
901
+ }
877
902
  const direct = this.firstString(itemObject, [
878
- "diff",
879
- "unified_diff",
880
- "unifiedDiff",
881
- "patch",
882
903
  "text",
883
904
  "message",
884
905
  "summary",
@@ -887,20 +908,10 @@ export class CodexProvider {
887
908
  "output_text",
888
909
  "outputText",
889
910
  ]);
890
- if (direct?.trim())
891
- return direct.trim();
892
- const changes = this.readArray(itemObject.changes);
893
- if (changes.length === 0)
894
- return undefined;
895
- return changes
896
- .map((change) => {
897
- const obj = this.asRecord(change);
898
- const path = this.readString(obj.path) ?? this.readString(obj.filePath) ?? this.readString(obj.file_path) ?? "file";
899
- const kind = this.readString(obj.kind) ?? "change";
900
- const diff = this.readString(obj.diff) ?? this.readString(obj.patch) ?? "";
901
- return diff ? `${kind}: ${path}\n${diff}` : `${kind}: ${path}`;
902
- })
903
- .join("\n\n");
911
+ if (direct?.trim()) {
912
+ return `Status: ${status}\n\n${direct.trim()}`;
913
+ }
914
+ return undefined;
904
915
  }
905
916
  normalizedCommandPhase(rawStatus) {
906
917
  const normalized = rawStatus.trim().toLowerCase();
@@ -1104,52 +1115,125 @@ export class CodexProvider {
1104
1115
  return "commandexecution";
1105
1116
  return normalized;
1106
1117
  }
1118
+ isFileChangeStructuredItem(normalizedType, item, params) {
1119
+ if (normalizedType === "filechange" || normalizedType === "diff") {
1120
+ return true;
1121
+ }
1122
+ if (normalizedType !== "toolcall") {
1123
+ return false;
1124
+ }
1125
+ const event = this.asRecord(params?.event);
1126
+ const nestedItem = this.asRecord(event.item);
1127
+ const sources = [item, params ?? {}, event, nestedItem];
1128
+ for (const source of sources) {
1129
+ if (this.firstString(source, ["diff", "unified_diff", "unifiedDiff", "patch"])) {
1130
+ return true;
1131
+ }
1132
+ if (this.readArray(source.changes).length > 0) {
1133
+ return true;
1134
+ }
1135
+ }
1136
+ const toolName = this.firstString(item, ["name", "toolName", "tool_name", "title"])?.toLowerCase() ?? "";
1137
+ return toolName.includes("patch") || toolName.includes("edit") || toolName.includes("write");
1138
+ }
1107
1139
  extractStructuredOutput(params, item, normalizedType) {
1108
1140
  if (normalizedType === "filechange" || normalizedType === "toolcall" || normalizedType === "diff") {
1109
- return this.extractDiffLikePayload(params, item) ?? this.extractTextPayload(params);
1141
+ return this.extractDiffLikePayload(params, item, normalizedType) ?? this.extractTextPayload(params);
1110
1142
  }
1111
1143
  return this.extractTextPayload(params);
1112
1144
  }
1113
- extractDiffLikePayload(params, item) {
1145
+ extractDiffLikePayload(params, item, normalizedType) {
1114
1146
  const event = this.asRecord(params.event);
1115
1147
  const nestedItem = this.asRecord(event.item);
1116
1148
  const sources = [params, item, event, nestedItem];
1117
- for (const source of sources) {
1118
- const direct = this.firstString(source, [
1119
- "diff",
1120
- "unified_diff",
1121
- "unifiedDiff",
1122
- "patch",
1123
- ]);
1124
- if (direct)
1125
- return direct;
1149
+ const status = this.normalizedFileChangeStatus(item, false);
1150
+ const changesBody = this.renderFileChangeEntriesBody(item.changes ?? params.changes ?? event.changes ?? nestedItem.changes);
1151
+ if (changesBody) {
1152
+ return `Status: ${status}\n\n${changesBody}`;
1126
1153
  }
1127
- const changes = this.readArray(item.changes ?? params.changes ?? event.changes ?? nestedItem.changes);
1128
- if (changes.length > 0) {
1129
- return changes
1130
- .map((change) => {
1131
- const obj = this.asRecord(change);
1132
- const path = this.readString(obj.path) ?? this.readString(obj.filePath) ?? this.readString(obj.file_path) ?? "file";
1133
- const kind = this.readString(obj.kind) ?? "change";
1134
- const diff = this.firstString(obj, ["diff", "unified_diff", "unifiedDiff", "patch"]) ?? "";
1135
- return diff ? `Path: ${path}\nKind: ${kind}\n\n\`\`\`diff\n${diff}\n\`\`\`` : `Path: ${path}\nKind: ${kind}`;
1136
- })
1137
- .join("\n\n---\n\n");
1154
+ for (const source of sources) {
1155
+ const diff = this.firstString(source, ["diff", "unified_diff", "unifiedDiff", "patch"]);
1156
+ if (diff) {
1157
+ return this.renderUnifiedDiffBody(diff, status);
1158
+ }
1138
1159
  }
1139
1160
  for (const source of sources) {
1140
- const direct = this.firstString(source, [
1141
- "text",
1142
- "message",
1143
- "summary",
1144
- "output",
1145
- "output_text",
1146
- "outputText",
1147
- ]);
1148
- if (direct)
1149
- return direct;
1161
+ const direct = this.firstString(source, ["text", "message", "summary", "output", "output_text", "outputText"]);
1162
+ if (direct) {
1163
+ if (normalizedType === "toolcall" && !this.isFileChangeStructuredItem("toolcall", item, params)) {
1164
+ return direct;
1165
+ }
1166
+ return `Status: ${status}\n\n${direct}`;
1167
+ }
1150
1168
  }
1151
1169
  return undefined;
1152
1170
  }
1171
+ renderFileChangeEntriesBody(rawChanges) {
1172
+ const changes = this.readArray(rawChanges);
1173
+ if (changes.length === 0)
1174
+ return undefined;
1175
+ const rendered = changes
1176
+ .map((change) => {
1177
+ const obj = this.asRecord(change);
1178
+ const path = this.firstString(obj, [
1179
+ "path",
1180
+ "file",
1181
+ "file_path",
1182
+ "filePath",
1183
+ "relative_path",
1184
+ "relativePath",
1185
+ "new_path",
1186
+ "newPath",
1187
+ "to",
1188
+ "target",
1189
+ "name",
1190
+ "old_path",
1191
+ "oldPath",
1192
+ "from",
1193
+ ]) ?? "file";
1194
+ const kind = this.firstString(obj, ["kind", "type", "action", "status"]) ?? "change";
1195
+ const diff = this.firstString(obj, ["diff", "unified_diff", "unifiedDiff", "patch"]) ?? "";
1196
+ return diff
1197
+ ? `Path: ${path}\nKind: ${kind}\n\n\`\`\`diff\n${diff}\n\`\`\``
1198
+ : `Path: ${path}\nKind: ${kind}`;
1199
+ })
1200
+ .filter(Boolean);
1201
+ return rendered.length > 0 ? rendered.join("\n\n---\n\n") : undefined;
1202
+ }
1203
+ renderUnifiedDiffBody(diff, status) {
1204
+ return `Status: ${status}\n\n\`\`\`diff\n${diff.trim()}\n\`\`\``;
1205
+ }
1206
+ extractCanonicalPatch(params, item) {
1207
+ const event = this.asRecord(params.event);
1208
+ const nestedItem = this.asRecord(event.item);
1209
+ const sources = [item, params, event, nestedItem];
1210
+ for (const source of sources) {
1211
+ const diff = this.firstString(source, ["diff", "unified_diff", "unifiedDiff", "patch"]);
1212
+ if (diff?.trim()) {
1213
+ return diff.trim();
1214
+ }
1215
+ }
1216
+ const changes = this.readArray(item.changes ?? params.changes ?? event.changes ?? nestedItem.changes);
1217
+ if (changes.length === 0)
1218
+ return undefined;
1219
+ const patch = changes
1220
+ .map((change) => {
1221
+ const obj = this.asRecord(change);
1222
+ return this.firstString(obj, ["diff", "unified_diff", "unifiedDiff", "patch"]) ?? "";
1223
+ })
1224
+ .filter((value) => value.trim().length > 0)
1225
+ .join("\n");
1226
+ return patch.trim() || undefined;
1227
+ }
1228
+ normalizedFileChangeStatus(itemObject, isCompleted) {
1229
+ const status = this.readString(itemObject.status)
1230
+ ?? this.readString(this.asRecord(itemObject.status).type)
1231
+ ?? this.readString(itemObject.state)
1232
+ ?? this.readString(this.asRecord(itemObject.state).type);
1233
+ if (status)
1234
+ return status;
1235
+ return isCompleted ? "completed" : "inProgress";
1236
+ }
1153
1237
  extractToolInput(item, params) {
1154
1238
  return item.input ?? item.command ?? item.path ?? item.args ?? params.command ?? params.path ?? undefined;
1155
1239
  }
@@ -1161,7 +1245,7 @@ export class CodexProvider {
1161
1245
  return this.decodePlanItemText(item);
1162
1246
  }
1163
1247
  if (normalizedType === "filechange" || normalizedType === "toolcall" || normalizedType === "diff") {
1164
- return this.extractDiffLikePayload(params, item) ?? this.decodeFileLikeItemText(item);
1248
+ return this.extractDiffLikePayload(params, item, normalizedType) ?? this.decodeFileLikeItemText(item);
1165
1249
  }
1166
1250
  if (normalizedType === "enteredreviewmode") {
1167
1251
  return `Reviewing ${this.readString(item.review) ?? "changes"}...`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lunel-cli",
3
- "version": "0.1.51",
3
+ "version": "0.1.53",
4
4
  "author": [
5
5
  {
6
6
  "name": "Soham Bharambe",