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 +154 -7
- package/dist/webide-message-worker.js +182 -20
- package/package.json +3 -3
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
|
-
|
|
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
|
|
7124
|
+
import { join as join19 } from "path";
|
|
6978
7125
|
var CLI_VERSION_FILE = ".cli-version.json";
|
|
6979
7126
|
function manifestPath(apmDir) {
|
|
6980
|
-
return
|
|
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
|
|
7086
|
-
var workerFile =
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
1371
|
+
function asString2(value) {
|
|
1225
1372
|
return typeof value === "string" ? value.trim() : "";
|
|
1226
1373
|
}
|
|
1227
1374
|
function parseQuestions(args) {
|
|
1228
|
-
const title =
|
|
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 =
|
|
1238
|
-
const 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 =
|
|
1246
|
-
const 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
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
1871
|
+
return join5(apmRoot ?? workspaceApmDir(), "project");
|
|
1725
1872
|
}
|
|
1726
1873
|
function projectDocumentLocalPath(apmRoot, documentPath) {
|
|
1727
1874
|
const normalized = normalizeLocalDocumentPath(documentPath);
|
|
1728
|
-
return
|
|
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 =
|
|
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(
|
|
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(
|
|
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.
|
|
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.
|
|
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": "
|
|
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",
|