ai-project-manage-cli 7.1.2 → 7.1.4

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
@@ -457,6 +457,14 @@ var requestConfig = {
457
457
  method: "GET",
458
458
  path: "/cli/webide/assumptions"
459
459
  }),
460
+ webideRecommendPlan: defineEndpoint({
461
+ method: "PUT",
462
+ path: "/cli/webide/plan-recommendation"
463
+ }),
464
+ webideUpsertPlan: defineEndpoint({
465
+ method: "PUT",
466
+ path: "/cli/webide/plan"
467
+ }),
460
468
  branchBaseline: defineEndpoint({
461
469
  method: "GET",
462
470
  path: "/cli/tasks/branch-baseline"
@@ -6304,6 +6312,16 @@ function formatPlanMarkdown(raw) {
6304
6312
  }
6305
6313
 
6306
6314
  // src/session-utils.ts
6315
+ function serializeTokenUsage(usage) {
6316
+ return {
6317
+ inputTokens: usage.inputTokens,
6318
+ outputTokens: usage.outputTokens,
6319
+ cacheReadTokens: usage.cacheReadTokens,
6320
+ cacheWriteTokens: usage.cacheWriteTokens,
6321
+ totalTokens: usage.totalTokens,
6322
+ ...usage.reasoningTokens != null ? { reasoningTokens: usage.reasoningTokens } : {}
6323
+ };
6324
+ }
6307
6325
  var EventSession = class {
6308
6326
  events = [];
6309
6327
  dirtyIndices = /* @__PURE__ */ new Set();
@@ -6342,7 +6360,7 @@ var EventSession = class {
6342
6360
  if (formatedEvent.type === "status") {
6343
6361
  return;
6344
6362
  }
6345
- if (formatedEvent.type === "request") {
6363
+ if (formatedEvent.type === "request" || formatedEvent.type === "usage") {
6346
6364
  this.events.push(formatedEvent);
6347
6365
  this.markDirty(this.events.length - 1);
6348
6366
  return;
@@ -6366,6 +6384,16 @@ var EventSession = class {
6366
6384
  this.events.push(formatedEvent);
6367
6385
  this.markDirty(this.events.length - 1);
6368
6386
  }
6387
+ /** 写入 run.wait() 返回的累计 TokenUsage(整次 run 汇总) */
6388
+ addRunUsage(usage, options) {
6389
+ this.events.push({
6390
+ type: "usage",
6391
+ scope: "run",
6392
+ usage: serializeTokenUsage(usage),
6393
+ ...options?.durationMs != null ? { durationMs: options.durationMs } : {}
6394
+ });
6395
+ this.markDirty(this.events.length - 1);
6396
+ }
6369
6397
  formatEvent(event) {
6370
6398
  switch (event.type) {
6371
6399
  case "assistant": {
@@ -6400,6 +6428,12 @@ var EventSession = class {
6400
6428
  status: event.status,
6401
6429
  text: event.text
6402
6430
  };
6431
+ case "usage":
6432
+ return {
6433
+ type: "usage",
6434
+ scope: "turn",
6435
+ usage: serializeTokenUsage(event.usage)
6436
+ };
6403
6437
  case "request":
6404
6438
  return {
6405
6439
  ...event,
@@ -6474,6 +6508,16 @@ ${String(event.content ?? "")}
6474
6508
  if (type === "tool_call") {
6475
6509
  return "````toolcall\n" + JSON.stringify(event, null, 2) + "\n````\n";
6476
6510
  }
6511
+ if (type === "usage") {
6512
+ const usage = event.usage;
6513
+ const scope = event.scope === "run" ? "\u7D2F\u8BA1" : "\u672C\u8F6E";
6514
+ return `## Token \u4F7F\u7528\u91CF\uFF08${scope}\uFF09
6515
+
6516
+ \`\`\`json
6517
+ ${JSON.stringify(usage ?? event, null, 2)}
6518
+ \`\`\`
6519
+ `;
6520
+ }
6477
6521
  return `## \u672A\u77E5\u4E8B\u4EF6\uFF1A${type}
6478
6522
 
6479
6523
  \`\`\`json
@@ -6701,13 +6745,82 @@ ${payload}`;
6701
6745
  };
6702
6746
  }
6703
6747
 
6748
+ // src/commands/connect/webide-plan-tools.ts
6749
+ function asString(value) {
6750
+ return typeof value === "string" ? value.trim() : "";
6751
+ }
6752
+ function createRecommendPlanTool(options) {
6753
+ const { cfg, taskId } = options;
6754
+ return {
6755
+ description: "Recommend whether the human should write an implementation plan or skip it. Does not change workflow phase; the human decides in the WebIDE UI.",
6756
+ inputSchema: {
6757
+ type: "object",
6758
+ properties: {
6759
+ recommendation: {
6760
+ type: "string",
6761
+ enum: ["write", "skip", "unknown"],
6762
+ description: "write = suggest generating a plan; skip = plan optional/unnecessary; unknown = unsure"
6763
+ },
6764
+ reason: {
6765
+ type: "string",
6766
+ description: "Short Chinese reason for the recommendation"
6767
+ }
6768
+ },
6769
+ required: ["recommendation"]
6770
+ },
6771
+ execute: async (args) => {
6772
+ const raw = asString(args.recommendation);
6773
+ const recommendation = raw === "write" || raw === "skip" || raw === "unknown" ? raw : "unknown";
6774
+ const reason = asString(args.reason) || void 0;
6775
+ const api = createApmApiClient(cfg);
6776
+ await api.cli.webideRecommendPlan({
6777
+ taskId,
6778
+ recommendation,
6779
+ reason
6780
+ });
6781
+ console.log(
6782
+ `[apm] RecommendPlan taskId=${taskId} recommendation=${recommendation}`
6783
+ );
6784
+ return JSON.stringify({ ok: true, recommendation, reason }, null, 2);
6785
+ }
6786
+ };
6787
+ }
6788
+ function createUpsertWebIdePlanTool(options) {
6789
+ const { cfg, taskId } = options;
6790
+ return {
6791
+ description: "Persist the implementation plan markdown for this WebIDE task and mark the plan as ready for human confirmation. Call once with the full plan document.",
6792
+ inputSchema: {
6793
+ type: "object",
6794
+ properties: {
6795
+ content: {
6796
+ type: "string",
6797
+ description: "Full implementation plan in Markdown (Chinese)"
6798
+ }
6799
+ },
6800
+ required: ["content"]
6801
+ },
6802
+ execute: async (args) => {
6803
+ const content = asString(args.content);
6804
+ if (!content) {
6805
+ throw new Error("UpsertWebIdePlan \u7F3A\u5C11 content");
6806
+ }
6807
+ const api = createApmApiClient(cfg);
6808
+ await api.cli.webideUpsertPlan({ taskId, content });
6809
+ console.log(
6810
+ `[apm] UpsertWebIdePlan taskId=${taskId} chars=${content.length}`
6811
+ );
6812
+ return JSON.stringify({ ok: true, chars: content.length }, null, 2);
6813
+ }
6814
+ };
6815
+ }
6816
+
6704
6817
  // src/commands/connect/cursor-custom-tools.ts
6705
6818
  var PLAN_MODE_ASK_QUESTION_HINT = `[SDK \u73AF\u5883\u8BF4\u660E]
6706
6819
  AskQuestion \u5DF2\u901A\u8FC7 MCP \u670D\u52A1\u5668 custom-user-tools \u6CE8\u518C\uFF0C\u5DE5\u5177\u540D\u4E3A AskQuestion\u3002
6707
6820
  \u9700\u8981\u5411\u7528\u6237\u786E\u8BA4\u65F6\uFF0C\u8BF7\u8C03\u7528 AskQuestion\uFF08\u7ECF CallMcpTool / custom-user-tools\uFF09\uFF0C\u4E0D\u8981\u5047\u8BBE IDE \u5185\u7F6E AskQuestion \u4E0D\u53EF\u7528\u3002
6708
6821
  \u975E\u5FC5\u987B\u7684\u95EE\u9898\u53EF\u8DF3\u8FC7\uFF0C\u76F4\u63A5\u5B8C\u6210 createPlan\u3002`;
6709
6822
  function createCursorCustomTools(cfg, messageId, options) {
6710
- return {
6823
+ const tools = {
6711
6824
  ...createAppendMessageCustomTools(
6712
6825
  cfg,
6713
6826
  messageId,
@@ -6718,6 +6831,17 @@ function createCursorCustomTools(cfg, messageId, options) {
6718
6831
  execute: options?.askQuestionExecute
6719
6832
  })
6720
6833
  };
6834
+ if (options?.enableWebIdePlanTools && options.taskId) {
6835
+ tools.RecommendPlan = createRecommendPlanTool({
6836
+ cfg,
6837
+ taskId: options.taskId
6838
+ });
6839
+ tools.UpsertWebIdePlan = createUpsertWebIdePlanTool({
6840
+ cfg,
6841
+ taskId: options.taskId
6842
+ });
6843
+ }
6844
+ return tools;
6721
6845
  }
6722
6846
  function withPlanModeToolHint(prompt, mode) {
6723
6847
  if (mode !== "plan") {
@@ -6728,6 +6852,16 @@ function withPlanModeToolHint(prompt, mode) {
6728
6852
  ${PLAN_MODE_ASK_QUESTION_HINT}`;
6729
6853
  }
6730
6854
 
6855
+ // src/commands/connect/local-agent-store.ts
6856
+ import { mkdirSync as mkdirSync10 } from "node:fs";
6857
+ import { join as join18 } from "node:path";
6858
+ import { JsonlLocalAgentStore } from "@cursor/sdk";
6859
+ function createWorkspaceLocalAgentStore(workdir) {
6860
+ const rootDir = join18(workdir, ".apm", "cursor-agent-store");
6861
+ mkdirSync10(rootDir, { recursive: true });
6862
+ return new JsonlLocalAgentStore(rootDir);
6863
+ }
6864
+
6731
6865
  // src/commands/connect/cursor-agent.ts
6732
6866
  setMaxListeners2(100);
6733
6867
  installAbortSignalDebug();
@@ -6761,6 +6895,7 @@ async function obtainAgent(ctx) {
6761
6895
  model: { id: ctx.model || "default" },
6762
6896
  local: {
6763
6897
  cwd: ctx.cwd,
6898
+ store: createWorkspaceLocalAgentStore(ctx.workdir),
6764
6899
  ...ctx.customTools ? { customTools: ctx.customTools } : {}
6765
6900
  },
6766
6901
  ...ctx.mode ? { mode: ctx.mode } : {}
@@ -6808,7 +6943,9 @@ async function runCursorAgent(cfg, ctx, options) {
6808
6943
  const customTools = createCursorCustomTools(cfg, ctx.messageId, {
6809
6944
  onAskQuestion: options?.onAskQuestion,
6810
6945
  appendMessageContent: options?.appendMessageContent,
6811
- askQuestionExecute: options?.askQuestionExecute
6946
+ askQuestionExecute: options?.askQuestionExecute,
6947
+ enableWebIdePlanTools: options?.enableWebIdePlanTools,
6948
+ taskId: options?.taskId
6812
6949
  });
6813
6950
  const prompt = withPlanModeToolHint(ctx.prompt, ctx.mode);
6814
6951
  console.log(
@@ -6877,6 +7014,15 @@ async function runCursorAgent(cfg, ctx, options) {
6877
7014
  }
6878
7015
  await syncRemoteLog.flush(eventSession);
6879
7016
  const result = await run.wait();
7017
+ if (result.usage) {
7018
+ eventSession.addRunUsage(result.usage, {
7019
+ durationMs: result.durationMs
7020
+ });
7021
+ await syncRemoteLog.flush(eventSession);
7022
+ console.log(
7023
+ `[apm] Cursor usage total=${result.usage.totalTokens} in=${result.usage.inputTokens} out=${result.usage.outputTokens} cache=${result.usage.cacheReadTokens}/${result.usage.cacheWriteTokens}`
7024
+ );
7025
+ }
6880
7026
  if (result.status === "error") {
6881
7027
  const failureMessage = formatCursorRunFailure(result.id, {
6882
7028
  statusError: lastRunErrorStatus,
@@ -6913,6 +7059,7 @@ async function runCursorAgent(cfg, ctx, options) {
6913
7059
  status: result.status,
6914
7060
  result: result.result,
6915
7061
  durationMs: result.durationMs,
7062
+ usage: result.usage,
6916
7063
  assistantText: eventSession.getAssistantText(),
6917
7064
  createPlan: eventSession.getCreatePlanContent(),
6918
7065
  artifacts,
@@ -6974,10 +7121,10 @@ async function ensureMessageHasReply(cfg, sessionId, messageId, fallback) {
6974
7121
 
6975
7122
  // src/commands/connect/cli-version-sync.ts
6976
7123
  import { existsSync as existsSync24, readFileSync as readFileSync17, writeFileSync as writeFileSync15 } from "fs";
6977
- import { join as join18 } from "path";
7124
+ import { join as join19 } from "path";
6978
7125
  var CLI_VERSION_FILE = ".cli-version.json";
6979
7126
  function manifestPath(apmDir) {
6980
- return join18(apmDir, CLI_VERSION_FILE);
7127
+ return join19(apmDir, CLI_VERSION_FILE);
6981
7128
  }
6982
7129
  function loadManifest4(apmDir) {
6983
7130
  const path19 = toFsPath(manifestPath(apmDir));
@@ -7082,8 +7229,8 @@ function createRunSlotPool(maxConcurrent = DEFAULT_MAX_CONCURRENT, options = {})
7082
7229
  // src/commands/connect/webide-worker-pool.ts
7083
7230
  import { Worker } from "node:worker_threads";
7084
7231
  import { fileURLToPath as fileURLToPath3 } from "node:url";
7085
- import { dirname as dirname7, join as join19 } from "node:path";
7086
- var workerFile = join19(
7232
+ import { dirname as dirname7, join as join20 } from "node:path";
7233
+ var workerFile = join20(
7087
7234
  dirname7(fileURLToPath3(import.meta.url)),
7088
7235
  "webide-message-worker.js"
7089
7236
  );
@@ -88,6 +88,14 @@ var requestConfig = {
88
88
  method: "GET",
89
89
  path: "/cli/webide/assumptions"
90
90
  }),
91
+ webideRecommendPlan: defineEndpoint({
92
+ method: "PUT",
93
+ path: "/cli/webide/plan-recommendation"
94
+ }),
95
+ webideUpsertPlan: defineEndpoint({
96
+ method: "PUT",
97
+ path: "/cli/webide/plan"
98
+ }),
91
99
  branchBaseline: defineEndpoint({
92
100
  method: "GET",
93
101
  path: "/cli/tasks/branch-baseline"
@@ -289,6 +297,16 @@ function formatPlanMarkdown(raw) {
289
297
  }
290
298
 
291
299
  // src/session-utils.ts
300
+ function serializeTokenUsage(usage) {
301
+ return {
302
+ inputTokens: usage.inputTokens,
303
+ outputTokens: usage.outputTokens,
304
+ cacheReadTokens: usage.cacheReadTokens,
305
+ cacheWriteTokens: usage.cacheWriteTokens,
306
+ totalTokens: usage.totalTokens,
307
+ ...usage.reasoningTokens != null ? { reasoningTokens: usage.reasoningTokens } : {}
308
+ };
309
+ }
292
310
  var EventSession = class {
293
311
  events = [];
294
312
  dirtyIndices = /* @__PURE__ */ new Set();
@@ -327,7 +345,7 @@ var EventSession = class {
327
345
  if (formatedEvent.type === "status") {
328
346
  return;
329
347
  }
330
- if (formatedEvent.type === "request") {
348
+ if (formatedEvent.type === "request" || formatedEvent.type === "usage") {
331
349
  this.events.push(formatedEvent);
332
350
  this.markDirty(this.events.length - 1);
333
351
  return;
@@ -351,6 +369,16 @@ var EventSession = class {
351
369
  this.events.push(formatedEvent);
352
370
  this.markDirty(this.events.length - 1);
353
371
  }
372
+ /** 写入 run.wait() 返回的累计 TokenUsage(整次 run 汇总) */
373
+ addRunUsage(usage, options) {
374
+ this.events.push({
375
+ type: "usage",
376
+ scope: "run",
377
+ usage: serializeTokenUsage(usage),
378
+ ...options?.durationMs != null ? { durationMs: options.durationMs } : {}
379
+ });
380
+ this.markDirty(this.events.length - 1);
381
+ }
354
382
  formatEvent(event) {
355
383
  switch (event.type) {
356
384
  case "assistant": {
@@ -385,6 +413,12 @@ var EventSession = class {
385
413
  status: event.status,
386
414
  text: event.text
387
415
  };
416
+ case "usage":
417
+ return {
418
+ type: "usage",
419
+ scope: "turn",
420
+ usage: serializeTokenUsage(event.usage)
421
+ };
388
422
  case "request":
389
423
  return {
390
424
  ...event,
@@ -459,6 +493,16 @@ ${String(event.content ?? "")}
459
493
  if (type === "tool_call") {
460
494
  return "````toolcall\n" + JSON.stringify(event, null, 2) + "\n````\n";
461
495
  }
496
+ if (type === "usage") {
497
+ const usage = event.usage;
498
+ const scope = event.scope === "run" ? "\u7D2F\u8BA1" : "\u672C\u8F6E";
499
+ return `## Token \u4F7F\u7528\u91CF\uFF08${scope}\uFF09
500
+
501
+ \`\`\`json
502
+ ${JSON.stringify(usage ?? event, null, 2)}
503
+ \`\`\`
504
+ `;
505
+ }
462
506
  return `## \u672A\u77E5\u4E8B\u4EF6\uFF1A${type}
463
507
 
464
508
  \`\`\`json
@@ -944,13 +988,82 @@ ${payload}`;
944
988
  };
945
989
  }
946
990
 
991
+ // src/commands/connect/webide-plan-tools.ts
992
+ function asString(value) {
993
+ return typeof value === "string" ? value.trim() : "";
994
+ }
995
+ function createRecommendPlanTool(options) {
996
+ const { cfg, taskId } = options;
997
+ return {
998
+ description: "Recommend whether the human should write an implementation plan or skip it. Does not change workflow phase; the human decides in the WebIDE UI.",
999
+ inputSchema: {
1000
+ type: "object",
1001
+ properties: {
1002
+ recommendation: {
1003
+ type: "string",
1004
+ enum: ["write", "skip", "unknown"],
1005
+ description: "write = suggest generating a plan; skip = plan optional/unnecessary; unknown = unsure"
1006
+ },
1007
+ reason: {
1008
+ type: "string",
1009
+ description: "Short Chinese reason for the recommendation"
1010
+ }
1011
+ },
1012
+ required: ["recommendation"]
1013
+ },
1014
+ execute: async (args) => {
1015
+ const raw = asString(args.recommendation);
1016
+ const recommendation = raw === "write" || raw === "skip" || raw === "unknown" ? raw : "unknown";
1017
+ const reason = asString(args.reason) || void 0;
1018
+ const api = createApmApiClient(cfg);
1019
+ await api.cli.webideRecommendPlan({
1020
+ taskId,
1021
+ recommendation,
1022
+ reason
1023
+ });
1024
+ console.log(
1025
+ `[apm] RecommendPlan taskId=${taskId} recommendation=${recommendation}`
1026
+ );
1027
+ return JSON.stringify({ ok: true, recommendation, reason }, null, 2);
1028
+ }
1029
+ };
1030
+ }
1031
+ function createUpsertWebIdePlanTool(options) {
1032
+ const { cfg, taskId } = options;
1033
+ return {
1034
+ description: "Persist the implementation plan markdown for this WebIDE task and mark the plan as ready for human confirmation. Call once with the full plan document.",
1035
+ inputSchema: {
1036
+ type: "object",
1037
+ properties: {
1038
+ content: {
1039
+ type: "string",
1040
+ description: "Full implementation plan in Markdown (Chinese)"
1041
+ }
1042
+ },
1043
+ required: ["content"]
1044
+ },
1045
+ execute: async (args) => {
1046
+ const content = asString(args.content);
1047
+ if (!content) {
1048
+ throw new Error("UpsertWebIdePlan \u7F3A\u5C11 content");
1049
+ }
1050
+ const api = createApmApiClient(cfg);
1051
+ await api.cli.webideUpsertPlan({ taskId, content });
1052
+ console.log(
1053
+ `[apm] UpsertWebIdePlan taskId=${taskId} chars=${content.length}`
1054
+ );
1055
+ return JSON.stringify({ ok: true, chars: content.length }, null, 2);
1056
+ }
1057
+ };
1058
+ }
1059
+
947
1060
  // src/commands/connect/cursor-custom-tools.ts
948
1061
  var PLAN_MODE_ASK_QUESTION_HINT = `[SDK \u73AF\u5883\u8BF4\u660E]
949
1062
  AskQuestion \u5DF2\u901A\u8FC7 MCP \u670D\u52A1\u5668 custom-user-tools \u6CE8\u518C\uFF0C\u5DE5\u5177\u540D\u4E3A AskQuestion\u3002
950
1063
  \u9700\u8981\u5411\u7528\u6237\u786E\u8BA4\u65F6\uFF0C\u8BF7\u8C03\u7528 AskQuestion\uFF08\u7ECF CallMcpTool / custom-user-tools\uFF09\uFF0C\u4E0D\u8981\u5047\u8BBE IDE \u5185\u7F6E AskQuestion \u4E0D\u53EF\u7528\u3002
951
1064
  \u975E\u5FC5\u987B\u7684\u95EE\u9898\u53EF\u8DF3\u8FC7\uFF0C\u76F4\u63A5\u5B8C\u6210 createPlan\u3002`;
952
1065
  function createCursorCustomTools(cfg, messageId, options) {
953
- return {
1066
+ const tools = {
954
1067
  ...createAppendMessageCustomTools(
955
1068
  cfg,
956
1069
  messageId,
@@ -961,6 +1074,17 @@ function createCursorCustomTools(cfg, messageId, options) {
961
1074
  execute: options?.askQuestionExecute
962
1075
  })
963
1076
  };
1077
+ if (options?.enableWebIdePlanTools && options.taskId) {
1078
+ tools.RecommendPlan = createRecommendPlanTool({
1079
+ cfg,
1080
+ taskId: options.taskId
1081
+ });
1082
+ tools.UpsertWebIdePlan = createUpsertWebIdePlanTool({
1083
+ cfg,
1084
+ taskId: options.taskId
1085
+ });
1086
+ }
1087
+ return tools;
964
1088
  }
965
1089
  function withPlanModeToolHint(prompt, mode) {
966
1090
  if (mode !== "plan") {
@@ -971,6 +1095,16 @@ function withPlanModeToolHint(prompt, mode) {
971
1095
  ${PLAN_MODE_ASK_QUESTION_HINT}`;
972
1096
  }
973
1097
 
1098
+ // src/commands/connect/local-agent-store.ts
1099
+ import { mkdirSync as mkdirSync4 } from "node:fs";
1100
+ import { join as join3 } from "node:path";
1101
+ import { JsonlLocalAgentStore } from "@cursor/sdk";
1102
+ function createWorkspaceLocalAgentStore(workdir) {
1103
+ const rootDir = join3(workdir, ".apm", "cursor-agent-store");
1104
+ mkdirSync4(rootDir, { recursive: true });
1105
+ return new JsonlLocalAgentStore(rootDir);
1106
+ }
1107
+
974
1108
  // src/commands/connect/cursor-agent.ts
975
1109
  setMaxListeners2(100);
976
1110
  installAbortSignalDebug();
@@ -1004,6 +1138,7 @@ async function obtainAgent(ctx) {
1004
1138
  model: { id: ctx.model || "default" },
1005
1139
  local: {
1006
1140
  cwd: ctx.cwd,
1141
+ store: createWorkspaceLocalAgentStore(ctx.workdir),
1007
1142
  ...ctx.customTools ? { customTools: ctx.customTools } : {}
1008
1143
  },
1009
1144
  ...ctx.mode ? { mode: ctx.mode } : {}
@@ -1051,7 +1186,9 @@ async function runCursorAgent(cfg, ctx, options) {
1051
1186
  const customTools = createCursorCustomTools(cfg, ctx.messageId, {
1052
1187
  onAskQuestion: options?.onAskQuestion,
1053
1188
  appendMessageContent: options?.appendMessageContent,
1054
- askQuestionExecute: options?.askQuestionExecute
1189
+ askQuestionExecute: options?.askQuestionExecute,
1190
+ enableWebIdePlanTools: options?.enableWebIdePlanTools,
1191
+ taskId: options?.taskId
1055
1192
  });
1056
1193
  const prompt = withPlanModeToolHint(ctx.prompt, ctx.mode);
1057
1194
  console.log(
@@ -1120,6 +1257,15 @@ async function runCursorAgent(cfg, ctx, options) {
1120
1257
  }
1121
1258
  await syncRemoteLog.flush(eventSession);
1122
1259
  const result = await run.wait();
1260
+ if (result.usage) {
1261
+ eventSession.addRunUsage(result.usage, {
1262
+ durationMs: result.durationMs
1263
+ });
1264
+ await syncRemoteLog.flush(eventSession);
1265
+ console.log(
1266
+ `[apm] Cursor usage total=${result.usage.totalTokens} in=${result.usage.inputTokens} out=${result.usage.outputTokens} cache=${result.usage.cacheReadTokens}/${result.usage.cacheWriteTokens}`
1267
+ );
1268
+ }
1123
1269
  if (result.status === "error") {
1124
1270
  const failureMessage = formatCursorRunFailure(result.id, {
1125
1271
  statusError: lastRunErrorStatus,
@@ -1156,6 +1302,7 @@ async function runCursorAgent(cfg, ctx, options) {
1156
1302
  status: result.status,
1157
1303
  result: result.result,
1158
1304
  durationMs: result.durationMs,
1305
+ usage: result.usage,
1159
1306
  assistantText: eventSession.getAssistantText(),
1160
1307
  createPlan: eventSession.getCreatePlanContent(),
1161
1308
  artifacts,
@@ -1180,7 +1327,7 @@ async function runCursorAgent(cfg, ctx, options) {
1180
1327
  }
1181
1328
 
1182
1329
  // src/commands/connect/webide-agent-registry.ts
1183
- import { existsSync as existsSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "node:fs";
1330
+ import { existsSync as existsSync3, mkdirSync as mkdirSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "node:fs";
1184
1331
  import { dirname as dirname3, resolve as resolve4 } from "node:path";
1185
1332
  function registryPath2(workdir, taskId) {
1186
1333
  return resolve4(workdir, ".apm", "webide", taskId, "cursor-agent.json");
@@ -1202,7 +1349,7 @@ function readRegistry2(path) {
1202
1349
  return {};
1203
1350
  }
1204
1351
  function writeRegistry2(path, registry) {
1205
- mkdirSync4(dirname3(path), { recursive: true });
1352
+ mkdirSync5(dirname3(path), { recursive: true });
1206
1353
  writeFileSync4(path, `${JSON.stringify(registry, null, 2)}
1207
1354
  `, "utf8");
1208
1355
  }
@@ -1221,11 +1368,11 @@ function clearWebIdeAgentId(workdir, taskId) {
1221
1368
  // src/commands/connect/webide-ask-question.ts
1222
1369
  import { setTimeout as delay } from "node:timers/promises";
1223
1370
  var POLL_INTERVAL_MS = 2e3;
1224
- function asString(value) {
1371
+ function asString2(value) {
1225
1372
  return typeof value === "string" ? value.trim() : "";
1226
1373
  }
1227
1374
  function parseQuestions(args) {
1228
- const title = asString(args.title) || void 0;
1375
+ const title = asString2(args.title) || void 0;
1229
1376
  const raw = args.questions;
1230
1377
  if (!Array.isArray(raw) || raw.length === 0) {
1231
1378
  throw new Error("AskQuestion \u7F3A\u5C11 questions");
@@ -1234,16 +1381,16 @@ function parseQuestions(args) {
1234
1381
  for (const item of raw) {
1235
1382
  if (!item || typeof item !== "object" || Array.isArray(item)) continue;
1236
1383
  const row = item;
1237
- const id = asString(row.id);
1238
- const prompt = asString(row.prompt);
1384
+ const id = asString2(row.id);
1385
+ const prompt = asString2(row.prompt);
1239
1386
  const optionsRaw = row.options;
1240
1387
  if (!id || !prompt || !Array.isArray(optionsRaw)) continue;
1241
1388
  const options = [];
1242
1389
  for (const opt of optionsRaw) {
1243
1390
  if (!opt || typeof opt !== "object" || Array.isArray(opt)) continue;
1244
1391
  const o = opt;
1245
- const oid = asString(o.id);
1246
- const label = asString(o.label);
1392
+ const oid = asString2(o.id);
1393
+ const label = asString2(o.label);
1247
1394
  if (oid && label) options.push({ id: oid, label });
1248
1395
  }
1249
1396
  if (options.length < 2) {
@@ -1507,11 +1654,11 @@ function resolveMessageReplyFallback(fallback) {
1507
1654
  }
1508
1655
 
1509
1656
  // src/commands/init.ts
1510
- import { join as join5 } from "path";
1657
+ import { join as join6 } from "path";
1511
1658
  import { readFileSync as readFileSync6, writeFileSync as writeFileSync7 } from "fs";
1512
1659
 
1513
1660
  // src/deployment-config-sync.ts
1514
- import { join as join3 } from "path";
1661
+ import { join as join4 } from "path";
1515
1662
  import { writeFileSync as writeFileSync5 } from "fs";
1516
1663
 
1517
1664
  // src/git-remote.ts
@@ -1702,7 +1849,7 @@ ${diagnostic ?? ""}
1702
1849
  return { synced: false, repositoryId };
1703
1850
  }
1704
1851
  const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
1705
- const apmConfigPath = toFsPath(join3(targetApmDir, "apm.config.json"));
1852
+ const apmConfigPath = toFsPath(join4(targetApmDir, "apm.config.json"));
1706
1853
  writeFileSync5(apmConfigPath, `${JSON.stringify(parsed, null, 2)}
1707
1854
  `, "utf8");
1708
1855
  console.log(`[apm] \u5DF2\u540C\u6B65\u5E73\u53F0\u90E8\u7F72\u914D\u7F6E: ${config.name}`);
@@ -1718,14 +1865,14 @@ import {
1718
1865
  rmSync,
1719
1866
  writeFileSync as writeFileSync6
1720
1867
  } from "fs";
1721
- import { dirname as dirname4, join as join4, relative, sep } from "path";
1868
+ import { dirname as dirname4, join as join5, relative, sep } from "path";
1722
1869
  var MANIFEST_FILE = "manifest.json";
1723
1870
  function projectDocumentsDir(apmRoot) {
1724
- return join4(apmRoot ?? workspaceApmDir(), "project");
1871
+ return join5(apmRoot ?? workspaceApmDir(), "project");
1725
1872
  }
1726
1873
  function projectDocumentLocalPath(apmRoot, documentPath) {
1727
1874
  const normalized = normalizeLocalDocumentPath(documentPath);
1728
- return join4(projectDocumentsDir(apmRoot), ...normalized.split("/"));
1875
+ return join5(projectDocumentsDir(apmRoot), ...normalized.split("/"));
1729
1876
  }
1730
1877
  function normalizeLocalDocumentPath(path) {
1731
1878
  const trimmed = path.trim().replace(/\\/g, "/");
@@ -1739,7 +1886,7 @@ function normalizeLocalDocumentPath(path) {
1739
1886
  return segments.join("/");
1740
1887
  }
1741
1888
  function readLocalManifest(apmRoot) {
1742
- const manifestPath = join4(projectDocumentsDir(apmRoot), MANIFEST_FILE);
1889
+ const manifestPath = join5(projectDocumentsDir(apmRoot), MANIFEST_FILE);
1743
1890
  if (!existsSync4(manifestPath)) {
1744
1891
  return null;
1745
1892
  }
@@ -1835,7 +1982,7 @@ ${diagnostic ?? ""}`
1835
1982
  }
1836
1983
  }
1837
1984
  writeFileSync6(
1838
- toFsPath(join4(projectDir, MANIFEST_FILE)),
1985
+ toFsPath(join5(projectDir, MANIFEST_FILE)),
1839
1986
  `${JSON.stringify(remoteManifest, null, 2)}
1840
1987
  `,
1841
1988
  "utf8"
@@ -1870,7 +2017,7 @@ async function ensureWorkspaceInitialized(workdir, options) {
1870
2017
  await syncRepositoryProjectDocumentsPull(workdir, apmDir);
1871
2018
  const trimmedName = options?.name?.trim();
1872
2019
  if (trimmedName) {
1873
- const apmConfigPath = toFsPath(join5(apmDir, "apm.config.json"));
2020
+ const apmConfigPath = toFsPath(join6(apmDir, "apm.config.json"));
1874
2021
  const config = readFileSync6(apmConfigPath, "utf8");
1875
2022
  const configJson = JSON.parse(config);
1876
2023
  configJson.name = trimmedName;
@@ -1934,6 +2081,8 @@ async function handleWebIdeInboundMessage(cfg, msg, signal) {
1934
2081
  taskId,
1935
2082
  signal
1936
2083
  }),
2084
+ enableWebIdePlanTools: true,
2085
+ taskId,
1937
2086
  createRemoteLogSync: (agentId) => {
1938
2087
  saveWebIdeAgentId(workdir, taskId, agentId);
1939
2088
  logSyncRef.current = createThrottledWebIdeMessageLogSync(
@@ -1978,6 +2127,19 @@ async function handleWebIdeInboundMessage(cfg, msg, signal) {
1978
2127
  id: messageId,
1979
2128
  content: fallback
1980
2129
  });
2130
+ if (msg.action === "write-plan" && outcome.createPlan && outcome.createPlan.trim()) {
2131
+ try {
2132
+ await api.cli.webideUpsertPlan({
2133
+ taskId,
2134
+ content: outcome.createPlan.trim()
2135
+ });
2136
+ } catch (err) {
2137
+ console.warn(
2138
+ "[apm] write-plan \u8865\u5199\u8BA1\u5212\u5931\u8D25:",
2139
+ err instanceof Error ? err.message : err
2140
+ );
2141
+ }
2142
+ }
1981
2143
  await logSyncRef.current?.markRun(outcome.runId, "finished");
1982
2144
  await updateStatus(cfg, messageId, "SUCCESS");
1983
2145
  console.log(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-project-manage-cli",
3
- "version": "7.1.2",
3
+ "version": "7.1.4",
4
4
  "description": "命令行工具:后续用于调用平台后端 API 完成运维与自动化操作",
5
5
  "type": "module",
6
6
  "private": false,
@@ -29,13 +29,13 @@
29
29
  "@types/ssh2-sftp-client": "~9.0.6"
30
30
  },
31
31
  "engines": {
32
- "node": ">=22.13.0"
32
+ "node": ">=22.0.0"
33
33
  },
34
34
  "dependencies": {
35
35
  "@bufbuild/protobuf": "1.10.0",
36
36
  "@connectrpc/connect": "~1.7.0",
37
37
  "@connectrpc/connect-node": "~1.7.0",
38
- "@cursor/sdk": "~1.0.18",
38
+ "@cursor/sdk": "^1.0.22",
39
39
  "ws": "~8.18.0",
40
40
  "listpage-http": "~0.0.318",
41
41
  "commander": "~14.0.3",