ai-project-manage-cli 5.0.18 → 5.0.20

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/dist/index.js CHANGED
@@ -231,10 +231,12 @@ var requestConfig = {
231
231
  method: "GET",
232
232
  path: "/cli/documents"
233
233
  }),
234
- listAttachments: defineEndpoint({
235
- method: "GET",
236
- path: "/cli/attachments"
237
- }),
234
+ listAttachments: defineEndpoint(
235
+ {
236
+ method: "GET",
237
+ path: "/cli/attachments"
238
+ }
239
+ ),
238
240
  upsertDocument: defineEndpoint({
239
241
  method: "PUT",
240
242
  path: "/cli/documents/upsert"
@@ -243,6 +245,10 @@ var requestConfig = {
243
245
  method: "PUT",
244
246
  path: "/cli/messages/content"
245
247
  }),
248
+ upsertCursorMessageLog: defineEndpoint({
249
+ method: "PUT",
250
+ path: "/cli/cursor-message-logs"
251
+ }),
246
252
  updateMessageStatus: defineEndpoint({
247
253
  method: "PUT",
248
254
  path: "/cli/messages/status"
@@ -477,10 +483,9 @@ async function runBranch(sessionId, options = {}) {
477
483
  workdirPath
478
484
  });
479
485
  if (!baseline.repositoryId) {
480
- throw new Error(
481
- `[apm] \u672A\u5728\u5E73\u53F0\u627E\u5230\u4E0E\u5F53\u524D\u76EE\u5F55\u5339\u914D\u7684\u5DE5\u4F5C\u76EE\u5F55\u767B\u8BB0\uFF1A${workdirPath}
482
- \u8BF7\u5148\u5728\u5E73\u53F0\u767B\u8BB0\u8BE5\u8DEF\u5F84\u5BF9\u5E94\u7684\u5DE5\u4F5C\u76EE\u5F55\u4E0E\u4ED3\u5E93\u3002`
483
- );
486
+ const detail = baseline.diagnostic?.message ?? `\u672A\u5728\u5E73\u53F0\u627E\u5230\u4E0E\u5F53\u524D\u76EE\u5F55\u5339\u914D\u7684\u5DE5\u4F5C\u76EE\u5F55\u767B\u8BB0\uFF1A${workdirPath}\uFF08\u89C4\u8303\u5316\uFF1A${baseline.workdirPath}\uFF09
487
+ \u8BF7\u5148\u5728\u5E73\u53F0\u767B\u8BB0\u8BE5\u8DEF\u5F84\u5BF9\u5E94\u7684\u5DE5\u4F5C\u76EE\u5F55\u4E0E\u4ED3\u5E93\u3002`;
488
+ throw new Error(`[apm] ${detail}`);
484
489
  }
485
490
  const baselineBranch = (baseline.defaultBranch ?? "").trim();
486
491
  if (!baselineBranch) {
@@ -1042,23 +1047,225 @@ function validateAgentWsMessage(value, kind) {
1042
1047
  // src/commands/connect/cursor-agent.ts
1043
1048
  import { Agent, CursorAgentError } from "@cursor/sdk";
1044
1049
  import { resolve as resolve3 } from "path";
1045
- function extractAssistantTextFromStream(event) {
1046
- if (event.type !== "assistant") {
1047
- return "";
1050
+
1051
+ // src/session-utils.ts
1052
+ var EventSession = class {
1053
+ events = [];
1054
+ constructor(prompt) {
1055
+ this.events.push({
1056
+ type: "input",
1057
+ content: prompt
1058
+ });
1059
+ }
1060
+ addEvent(event) {
1061
+ const latestEvent = this.events[this.events.length - 1];
1062
+ const formatedEvent = this.formatEvent(event);
1063
+ if (!formatedEvent) {
1064
+ return;
1065
+ }
1066
+ if (formatedEvent.type === "tool_call") {
1067
+ const existingToolCall = this.events.find(
1068
+ (e) => e.type === "tool_call" && e.call_id === formatedEvent.call_id
1069
+ );
1070
+ if (existingToolCall) {
1071
+ existingToolCall.args = formatedEvent.args;
1072
+ existingToolCall.result = formatedEvent.result;
1073
+ existingToolCall.status = formatedEvent.status;
1074
+ return;
1075
+ }
1076
+ this.events.push(formatedEvent);
1077
+ return;
1078
+ }
1079
+ if (formatedEvent.type === "status") {
1080
+ return;
1081
+ }
1082
+ if (formatedEvent.type === "request") {
1083
+ this.events.push(formatedEvent);
1084
+ return;
1085
+ }
1086
+ if (latestEvent?.type === formatedEvent.type) {
1087
+ switch (formatedEvent.type) {
1088
+ case "assistant":
1089
+ latestEvent.content += formatedEvent.content;
1090
+ break;
1091
+ case "thinking":
1092
+ latestEvent.content += formatedEvent.content;
1093
+ break;
1094
+ case "task":
1095
+ latestEvent.status = formatedEvent.status;
1096
+ latestEvent.text = formatedEvent.text;
1097
+ break;
1098
+ }
1099
+ return;
1100
+ }
1101
+ this.events.push(formatedEvent);
1102
+ }
1103
+ formatEvent(event) {
1104
+ switch (event.type) {
1105
+ case "assistant": {
1106
+ let content = "";
1107
+ for (const block of event.message.content) {
1108
+ if (block.type === "text" && block.text) {
1109
+ content += block.text;
1110
+ }
1111
+ }
1112
+ return {
1113
+ type: "assistant",
1114
+ content: content || event.content || ""
1115
+ };
1116
+ }
1117
+ case "thinking":
1118
+ return {
1119
+ type: "thinking",
1120
+ content: event.text || event.content || ""
1121
+ };
1122
+ case "tool_call":
1123
+ return {
1124
+ type: "tool_call",
1125
+ args: event.args,
1126
+ result: event.result,
1127
+ status: event.status,
1128
+ call_id: event.call_id,
1129
+ name: event.name
1130
+ };
1131
+ case "task":
1132
+ return {
1133
+ type: "task",
1134
+ status: event.status,
1135
+ text: event.text
1136
+ };
1137
+ case "request":
1138
+ return {
1139
+ ...event,
1140
+ type: "request"
1141
+ };
1142
+ case "status":
1143
+ return { type: "status", status: event.status, message: event.message };
1144
+ }
1145
+ }
1146
+ /** 合并所有 assistant 片段,供剧场成员回传等场景使用 */
1147
+ getAssistantText() {
1148
+ return this.events.filter((e) => e.type === "assistant").map((e) => String(e.content ?? "")).join("\n").trim();
1149
+ }
1150
+ resolveLogContent() {
1151
+ return this.events.map((event) => {
1152
+ const type = event.type;
1153
+ const content = (function(type2) {
1154
+ if (type2 === "input") {
1155
+ return `## \u7528\u6237\u8F93\u5165
1156
+
1157
+ ${event.content}
1158
+ `;
1159
+ }
1160
+ if (type2 === "assistant") {
1161
+ return `## \u6A21\u578B\u8F93\u51FA
1162
+
1163
+ ${event.content}
1164
+ `;
1165
+ }
1166
+ if (type2 === "thinking") {
1167
+ return `## \u6A21\u578B\u601D\u8003
1168
+
1169
+ ${event.content}
1170
+ `;
1171
+ }
1172
+ if (type2 === "tool_call") {
1173
+ return "````toolcall\n" + JSON.stringify(event, null, 2) + "\n````\n";
1174
+ }
1175
+ return `## \u672A\u77E5\u4E8B\u4EF6\uFF1A${type2}
1176
+
1177
+ \`\`\`json
1178
+ ${JSON.stringify(event, null, 2)}
1179
+ \`\`\``;
1180
+ })(type);
1181
+ return content;
1182
+ }).join("\n");
1048
1183
  }
1049
- const msg = event.message;
1050
- if (!msg?.content) {
1184
+ };
1185
+
1186
+ // src/commands/connect/cursor-message-log.ts
1187
+ function assistantStreamChunk(event) {
1188
+ if (event.type !== "assistant") {
1051
1189
  return "";
1052
1190
  }
1053
1191
  let text = "";
1054
- for (const block of msg.content) {
1192
+ for (const block of event.message.content) {
1055
1193
  if (block.type === "text" && block.text) {
1056
1194
  text += block.text;
1057
1195
  }
1058
1196
  }
1059
1197
  return text;
1060
1198
  }
1061
- async function runCursorAgent(_cfg, ctx) {
1199
+ var CURSOR_MESSAGE_LOG_SYNC_INTERVAL_MS = 2e3;
1200
+ function createThrottledCursorMessageLogSync(cfg, ctx, onError) {
1201
+ let lastRunAt = 0;
1202
+ let timer;
1203
+ let latestSession;
1204
+ const execute = async () => {
1205
+ const session = latestSession;
1206
+ if (!session) {
1207
+ return;
1208
+ }
1209
+ lastRunAt = Date.now();
1210
+ try {
1211
+ await syncCursorMessageLog(cfg, ctx, session);
1212
+ } catch (err) {
1213
+ onError(err);
1214
+ }
1215
+ };
1216
+ return {
1217
+ schedule(session) {
1218
+ latestSession = session;
1219
+ const now = Date.now();
1220
+ const elapsed = now - lastRunAt;
1221
+ if (elapsed >= CURSOR_MESSAGE_LOG_SYNC_INTERVAL_MS) {
1222
+ if (timer) {
1223
+ clearTimeout(timer);
1224
+ timer = void 0;
1225
+ }
1226
+ void execute();
1227
+ return;
1228
+ }
1229
+ if (timer) {
1230
+ return;
1231
+ }
1232
+ timer = setTimeout(() => {
1233
+ timer = void 0;
1234
+ void execute();
1235
+ }, CURSOR_MESSAGE_LOG_SYNC_INTERVAL_MS - elapsed);
1236
+ },
1237
+ async flush(session) {
1238
+ latestSession = session;
1239
+ if (timer) {
1240
+ clearTimeout(timer);
1241
+ timer = void 0;
1242
+ }
1243
+ await execute();
1244
+ }
1245
+ };
1246
+ }
1247
+ async function syncCursorMessageLog(cfg, ctx, session) {
1248
+ const agentId = ctx.agentId.trim();
1249
+ const data = session.resolveLogContent().trim();
1250
+ if (!agentId || !data) {
1251
+ return;
1252
+ }
1253
+ const api = createApmApiClient(cfg);
1254
+ await api.cli.upsertCursorMessageLog({
1255
+ sessionId: ctx.sessionId,
1256
+ messageId: ctx.messageId,
1257
+ agentId,
1258
+ data
1259
+ });
1260
+ }
1261
+
1262
+ // src/commands/connect/cursor-agent.ts
1263
+ var logCtx = (ctx, agentId) => ({
1264
+ sessionId: ctx.sessionId,
1265
+ messageId: ctx.messageId,
1266
+ agentId
1267
+ });
1268
+ async function runCursorAgent(cfg, ctx) {
1062
1269
  const apiKey = ctx.apiKey.trim();
1063
1270
  if (!apiKey) {
1064
1271
  throw new Error("\u7F3A\u5C11 apiKey\uFF0C\u65E0\u6CD5\u8C03\u7528 Cursor SDK");
@@ -1076,15 +1283,27 @@ async function runCursorAgent(_cfg, ctx) {
1076
1283
  settingSources: []
1077
1284
  }
1078
1285
  });
1286
+ const eventSession = new EventSession(prompt);
1287
+ const remoteLogCtx = logCtx(ctx, agent.agentId);
1288
+ const syncRemoteLog = createThrottledCursorMessageLogSync(
1289
+ cfg,
1290
+ remoteLogCtx,
1291
+ (err) => {
1292
+ console.warn(
1293
+ "[apm] \u540C\u6B65 Cursor \u6D88\u606F\u65E5\u5FD7\u5931\u8D25:",
1294
+ err instanceof Error ? err.message : err
1295
+ );
1296
+ }
1297
+ );
1079
1298
  try {
1080
1299
  const run = await agent.send(prompt);
1081
1300
  console.log(`[apm] Cursor run id=${run.id} agentId=${agent.agentId}`);
1082
1301
  for await (const event of run.stream()) {
1083
- const chunk = extractAssistantTextFromStream(event);
1084
- if (chunk) {
1085
- process.stdout.write(chunk);
1086
- }
1302
+ eventSession.addEvent(event);
1303
+ const chunk = assistantStreamChunk(event);
1304
+ syncRemoteLog.schedule(eventSession);
1087
1305
  }
1306
+ await syncRemoteLog.flush(eventSession);
1088
1307
  const result = await run.wait();
1089
1308
  if (result.status === "error") {
1090
1309
  throw new Error(`Cursor run \u5931\u8D25: ${result.id}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-project-manage-cli",
3
- "version": "5.0.18",
3
+ "version": "5.0.20",
4
4
  "description": "命令行工具:后续用于调用平台后端 API 完成运维与自动化操作",
5
5
  "type": "module",
6
6
  "private": false,
@@ -3,6 +3,7 @@
3
3
  1. 如果回复的内容包含文档地址,不要把前缀也写出来,直接用文档名称即可。例如: 我已经把待处理的内容更新到了 `PRD.md` 中,请查阅。
4
4
  2. 回复的内容并非对你工作的总结,要尽可能简洁,不要长篇大论,如果同步的内容确实较多,你可以先写文档,然后指引用户去阅读你写的文档。
5
5
  3. 只要你有任何进展都想都可以调用下面的命令进行回复,你可以一直补充内容。
6
+ 4. 如果有需要指定相关人,必须在消息内容中@指定出来
6
7
 
7
8
  ## 执行回复的方法
8
9