ai-project-manage-cli 7.0.1 → 7.0.2
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 +132 -23
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1449,6 +1449,86 @@ import {
|
|
|
1449
1449
|
} from "@cursor/sdk";
|
|
1450
1450
|
import { setMaxListeners as setMaxListeners2 } from "node:events";
|
|
1451
1451
|
|
|
1452
|
+
// src/commands/connect/plan-document.ts
|
|
1453
|
+
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync7 } from "node:fs";
|
|
1454
|
+
import { join as join11 } from "node:path";
|
|
1455
|
+
function normalizePlanContent(plan) {
|
|
1456
|
+
return plan.replace(/\\n/g, "\n").trim();
|
|
1457
|
+
}
|
|
1458
|
+
function extractPlanFromArgs(args) {
|
|
1459
|
+
if (args == null) {
|
|
1460
|
+
return void 0;
|
|
1461
|
+
}
|
|
1462
|
+
let parsed = args;
|
|
1463
|
+
if (typeof args === "string") {
|
|
1464
|
+
const trimmed = args.trim();
|
|
1465
|
+
if (!trimmed) {
|
|
1466
|
+
return void 0;
|
|
1467
|
+
}
|
|
1468
|
+
try {
|
|
1469
|
+
parsed = JSON.parse(trimmed);
|
|
1470
|
+
} catch {
|
|
1471
|
+
return normalizePlanContent(trimmed);
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
if (typeof parsed === "object" && parsed !== null && "plan" in parsed) {
|
|
1475
|
+
const plan = parsed.plan;
|
|
1476
|
+
if (typeof plan === "string" && plan.trim()) {
|
|
1477
|
+
return normalizePlanContent(plan);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
return void 0;
|
|
1481
|
+
}
|
|
1482
|
+
function memberPlanFileName(displayName) {
|
|
1483
|
+
const sanitized = displayName.trim().replace(/[/\\:*?"<>|]/g, "-").replace(/\s+/g, " ").trim() || "member";
|
|
1484
|
+
return `${sanitized}-PLAN.md`;
|
|
1485
|
+
}
|
|
1486
|
+
function collectCreatePlanContent(events) {
|
|
1487
|
+
for (let i = events.length - 1; i >= 0; i--) {
|
|
1488
|
+
const event = events[i];
|
|
1489
|
+
if (event.type !== "tool_call") continue;
|
|
1490
|
+
if (event.name !== "createPlan") continue;
|
|
1491
|
+
if (event.status !== "completed") continue;
|
|
1492
|
+
const content = extractPlanFromArgs(event.args);
|
|
1493
|
+
if (content) {
|
|
1494
|
+
return content;
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
return void 0;
|
|
1498
|
+
}
|
|
1499
|
+
function extractCreatePlanFromSessionEvents(events) {
|
|
1500
|
+
for (let i = events.length - 1; i >= 0; i--) {
|
|
1501
|
+
const event = events[i];
|
|
1502
|
+
if (event.type !== "tool_call") continue;
|
|
1503
|
+
if (event.name !== "createPlan") continue;
|
|
1504
|
+
if (event.status !== "completed") continue;
|
|
1505
|
+
const content = extractPlanFromArgs(event.args);
|
|
1506
|
+
if (content) {
|
|
1507
|
+
return content;
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
return void 0;
|
|
1511
|
+
}
|
|
1512
|
+
function saveMemberPlanDocument(input) {
|
|
1513
|
+
const trimmedContent = input.content.trim();
|
|
1514
|
+
if (!trimmedContent) {
|
|
1515
|
+
throw new Error("\u8BA1\u5212\u5185\u5BB9\u4E3A\u7A7A\uFF0C\u65E0\u6CD5\u4FDD\u5B58\u6587\u6863");
|
|
1516
|
+
}
|
|
1517
|
+
const fileName = memberPlanFileName(input.memberDisplayName);
|
|
1518
|
+
const docsDir = taskDocsDir(
|
|
1519
|
+
input.taskId,
|
|
1520
|
+
workspaceApmDir(input.workdir),
|
|
1521
|
+
input.workdir
|
|
1522
|
+
);
|
|
1523
|
+
mkdirSync5(toFsPath(docsDir), { recursive: true });
|
|
1524
|
+
const filePath = join11(docsDir, fileName);
|
|
1525
|
+
const normalized = trimmedContent.endsWith("\n") ? trimmedContent : `${trimmedContent}
|
|
1526
|
+
`;
|
|
1527
|
+
writeFileSync7(toFsPath(filePath), normalized, "utf8");
|
|
1528
|
+
console.log(`[apm] \u5DF2\u4FDD\u5B58\u8BA1\u5212\u6587\u6863: ${fileName}`);
|
|
1529
|
+
return fileName;
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1452
1532
|
// src/event-session.ts
|
|
1453
1533
|
var EventSession = class {
|
|
1454
1534
|
events = [];
|
|
@@ -1573,6 +1653,9 @@ var EventSession = class {
|
|
|
1573
1653
|
getAssistantText() {
|
|
1574
1654
|
return this.events.filter((e) => e.type === "assistant").map((e) => String(e.content ?? "")).join("\n").trim();
|
|
1575
1655
|
}
|
|
1656
|
+
getCreatePlanContent() {
|
|
1657
|
+
return extractCreatePlanFromSessionEvents(this.events);
|
|
1658
|
+
}
|
|
1576
1659
|
};
|
|
1577
1660
|
|
|
1578
1661
|
// src/commands/connect/abort-signal-debug.ts
|
|
@@ -1599,7 +1682,7 @@ function logAbortSignalStats(signal, label) {
|
|
|
1599
1682
|
}
|
|
1600
1683
|
|
|
1601
1684
|
// src/commands/connect/agent-task-registry.ts
|
|
1602
|
-
import { existsSync as existsSync7, mkdirSync as
|
|
1685
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync8 } from "node:fs";
|
|
1603
1686
|
import { dirname as dirname4, resolve as resolve3 } from "node:path";
|
|
1604
1687
|
function registryPath(workdir, taskId) {
|
|
1605
1688
|
return resolve3(workdir, ".apm", "tasks", taskId, "cursor-agents.json");
|
|
@@ -1626,8 +1709,8 @@ function readRegistry(path10) {
|
|
|
1626
1709
|
return {};
|
|
1627
1710
|
}
|
|
1628
1711
|
function writeRegistry(path10, registry) {
|
|
1629
|
-
|
|
1630
|
-
|
|
1712
|
+
mkdirSync6(dirname4(path10), { recursive: true });
|
|
1713
|
+
writeFileSync8(path10, `${JSON.stringify(registry, null, 2)}
|
|
1631
1714
|
`, "utf8");
|
|
1632
1715
|
}
|
|
1633
1716
|
function loadTaskAgentId(workdir, taskId, user) {
|
|
@@ -1766,7 +1849,10 @@ async function obtainAgent(ctx) {
|
|
|
1766
1849
|
}
|
|
1767
1850
|
}
|
|
1768
1851
|
}
|
|
1769
|
-
const agent = await Agent.create(
|
|
1852
|
+
const agent = await Agent.create({
|
|
1853
|
+
...agentOptions,
|
|
1854
|
+
...ctx.mode ? { mode: ctx.mode } : {}
|
|
1855
|
+
});
|
|
1770
1856
|
if (ctx.user) {
|
|
1771
1857
|
saveTaskAgentId(ctx.workdir, ctx.taskId, ctx.user, agent.agentId);
|
|
1772
1858
|
}
|
|
@@ -1788,8 +1874,9 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
1788
1874
|
|
|
1789
1875
|
---
|
|
1790
1876
|
\u8BF7\u5B8C\u6210\u4E0A\u8FF0\u4EFB\u52A1\u3002\u6267\u884C\u8FC7\u7A0B\u4E2D\u8BF7\u7528 append_mail_reply \u589E\u91CF\u66F4\u65B0\u7ED9\u53D1\u4FE1\u4EBA\u7684\u56DE\u590D\uFF08\u53EF\u5148\u7B80\u77ED\u786E\u8BA4\uFF0C\u6709\u8FDB\u5C55\u7EE7\u7EED\u8FFD\u52A0\uFF09\uFF1BAgent \u6B63\u5E38\u7ED3\u675F\u540E\u4F1A\u81EA\u52A8\u63D0\u4EA4\u5B8C\u6574\u56DE\u4FE1\u3002\u5982\u9700\u7F16\u5199\u6587\u6863\uFF0C\u4FDD\u5B58\u5230 \`.apm/tasks/${ctx.taskId}/docs/\` \u76EE\u5F55\u3002`;
|
|
1877
|
+
const mode = ctx.mode ?? "agent";
|
|
1791
1878
|
console.log(
|
|
1792
|
-
`[apm] Cursor Agent \u5F00\u59CB mailId=${ctx.mailId} taskId=${ctx.taskId} cwd=${workdir}`
|
|
1879
|
+
`[apm] Cursor Agent \u5F00\u59CB mailId=${ctx.mailId} taskId=${ctx.taskId} mode=${mode} cwd=${workdir}`
|
|
1793
1880
|
);
|
|
1794
1881
|
const { agent, resumed } = await obtainAgent({
|
|
1795
1882
|
apiKey,
|
|
@@ -1797,6 +1884,7 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
1797
1884
|
workdir,
|
|
1798
1885
|
taskId: ctx.taskId,
|
|
1799
1886
|
user: ctx.user,
|
|
1887
|
+
mode,
|
|
1800
1888
|
customTools
|
|
1801
1889
|
});
|
|
1802
1890
|
const eventSession = new EventSession(prompt);
|
|
@@ -1822,8 +1910,13 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
1822
1910
|
signal?.addEventListener("abort", abortRun, { once: true });
|
|
1823
1911
|
const streamEvents = [];
|
|
1824
1912
|
try {
|
|
1913
|
+
const forceSend = options?.forceSend ?? resumed;
|
|
1825
1914
|
const run = await agent.send(prompt, {
|
|
1826
|
-
|
|
1915
|
+
mode,
|
|
1916
|
+
local: {
|
|
1917
|
+
customTools,
|
|
1918
|
+
...forceSend ? { force: true } : {}
|
|
1919
|
+
}
|
|
1827
1920
|
});
|
|
1828
1921
|
activeRun = run;
|
|
1829
1922
|
console.log(`[apm] Cursor run id=${run.id} agentId=${agent.agentId}`);
|
|
@@ -1839,6 +1932,7 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
1839
1932
|
await syncRemoteLog.flush(eventSession);
|
|
1840
1933
|
const result = await run.wait();
|
|
1841
1934
|
const assistantText = eventSession.getAssistantText() || collectAssistantText(streamEvents);
|
|
1935
|
+
const createPlanContent = eventSession.getCreatePlanContent() || collectCreatePlanContent(streamEvents);
|
|
1842
1936
|
if (result.status === "error") {
|
|
1843
1937
|
if (resumed) {
|
|
1844
1938
|
clearTaskAgentId(workdir, ctx.taskId, ctx.user);
|
|
@@ -1855,7 +1949,8 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
1855
1949
|
runId: result.id,
|
|
1856
1950
|
agentId: agent.agentId,
|
|
1857
1951
|
status: result.status,
|
|
1858
|
-
assistantText: assistantText || result.result || ""
|
|
1952
|
+
assistantText: assistantText || result.result || "",
|
|
1953
|
+
...createPlanContent ? { createPlanContent } : {}
|
|
1859
1954
|
};
|
|
1860
1955
|
} catch (err) {
|
|
1861
1956
|
if (err instanceof CursorAgentError) {
|
|
@@ -1947,13 +2042,13 @@ function createMailReplyDraft() {
|
|
|
1947
2042
|
}
|
|
1948
2043
|
|
|
1949
2044
|
// src/commands/connect/task-pull.ts
|
|
1950
|
-
import { writeFileSync as
|
|
1951
|
-
import { join as
|
|
2045
|
+
import { writeFileSync as writeFileSync10 } from "fs";
|
|
2046
|
+
import { join as join13 } from "path";
|
|
1952
2047
|
import { stringify as yamlStringify } from "yaml";
|
|
1953
2048
|
|
|
1954
2049
|
// src/rules-sync.ts
|
|
1955
|
-
import { basename as basename2, extname, join as
|
|
1956
|
-
import { existsSync as existsSync8, readFileSync as readFileSync9, rmSync as rmSync3, writeFileSync as
|
|
2050
|
+
import { basename as basename2, extname, join as join12 } from "path";
|
|
2051
|
+
import { existsSync as existsSync8, readFileSync as readFileSync9, rmSync as rmSync3, writeFileSync as writeFileSync9 } from "fs";
|
|
1957
2052
|
var MANIFEST_FILE2 = ".rules-sync-manifest.json";
|
|
1958
2053
|
function ruleLocalFileName(ruleName) {
|
|
1959
2054
|
const trimmed = ruleName.trim();
|
|
@@ -1963,7 +2058,7 @@ function ruleLocalFileName(ruleName) {
|
|
|
1963
2058
|
return `${sanitized}.md`;
|
|
1964
2059
|
}
|
|
1965
2060
|
function loadManifest(rulesDir) {
|
|
1966
|
-
const path10 =
|
|
2061
|
+
const path10 = join12(rulesDir, MANIFEST_FILE2);
|
|
1967
2062
|
if (!existsSync8(toFsPath(path10))) {
|
|
1968
2063
|
return { version: 1, rules: {} };
|
|
1969
2064
|
}
|
|
@@ -1979,8 +2074,8 @@ function loadManifest(rulesDir) {
|
|
|
1979
2074
|
return { version: 1, rules: {} };
|
|
1980
2075
|
}
|
|
1981
2076
|
function saveManifest(rulesDir, manifest) {
|
|
1982
|
-
|
|
1983
|
-
toFsPath(
|
|
2077
|
+
writeFileSync9(
|
|
2078
|
+
toFsPath(join12(rulesDir, MANIFEST_FILE2)),
|
|
1984
2079
|
`${JSON.stringify(manifest, null, 2)}
|
|
1985
2080
|
`,
|
|
1986
2081
|
"utf8"
|
|
@@ -2001,7 +2096,7 @@ async function syncPlatformRules(cfg, workdirPath, apmRoot) {
|
|
|
2001
2096
|
const api = createApmApiClient(cfg);
|
|
2002
2097
|
const baseline = await api.cli.workspaceBaseline({ workdirPath });
|
|
2003
2098
|
const repositoryId = baseline.repositoryId;
|
|
2004
|
-
const rulesDir =
|
|
2099
|
+
const rulesDir = join12(apmRoot ?? workspaceApmDir(workdirPath), "rules");
|
|
2005
2100
|
await ensureDirExists(rulesDir);
|
|
2006
2101
|
if (!repositoryId) {
|
|
2007
2102
|
console.log(
|
|
@@ -2018,7 +2113,7 @@ async function syncPlatformRules(cfg, workdirPath, apmRoot) {
|
|
|
2018
2113
|
for (const rule of list) {
|
|
2019
2114
|
remoteIds.add(rule.id);
|
|
2020
2115
|
const fileName = ruleLocalFileName(rule.name);
|
|
2021
|
-
const dest =
|
|
2116
|
+
const dest = join12(rulesDir, fileName);
|
|
2022
2117
|
const entry = manifest.rules[rule.id];
|
|
2023
2118
|
const updatedAt = rule.updatedAt ?? "";
|
|
2024
2119
|
if (isRuleUpToDate(entry, rule, dest)) {
|
|
@@ -2027,7 +2122,7 @@ async function syncPlatformRules(cfg, workdirPath, apmRoot) {
|
|
|
2027
2122
|
console.log(`[apm] \u89C4\u5219\u65E0\u53D8\u5316\uFF0C\u5DF2\u8DF3\u8FC7: rules/${fileName}`);
|
|
2028
2123
|
continue;
|
|
2029
2124
|
}
|
|
2030
|
-
|
|
2125
|
+
writeFileSync9(toFsPath(dest), rule.content ?? "", "utf8");
|
|
2031
2126
|
nextManifest.rules[rule.id] = { fileName, updatedAt };
|
|
2032
2127
|
written.push(fileName);
|
|
2033
2128
|
console.log(`[apm] \u5DF2\u540C\u6B65\u5E73\u53F0\u89C4\u5219: rules/${fileName}`);
|
|
@@ -2036,7 +2131,7 @@ async function syncPlatformRules(cfg, workdirPath, apmRoot) {
|
|
|
2036
2131
|
for (const [ruleId, entry] of Object.entries(manifest.rules)) {
|
|
2037
2132
|
if (remoteIds.has(ruleId)) continue;
|
|
2038
2133
|
if (isBaseRuleFileName(entry.fileName)) continue;
|
|
2039
|
-
const dest =
|
|
2134
|
+
const dest = join12(rulesDir, entry.fileName);
|
|
2040
2135
|
if (existsSync8(toFsPath(dest))) {
|
|
2041
2136
|
rmSync3(toFsPath(dest), { force: true });
|
|
2042
2137
|
}
|
|
@@ -2055,15 +2150,15 @@ async function runTaskPull(cfg, detail) {
|
|
|
2055
2150
|
const dir = taskDir(taskId, apmRoot, workdir);
|
|
2056
2151
|
const docsDir = taskDocsDir(taskId, apmRoot, workdir);
|
|
2057
2152
|
await ensureDirExists(docsDir);
|
|
2058
|
-
|
|
2059
|
-
|
|
2153
|
+
writeFileSync10(taskRulePath(taskId, apmRoot, workdir), "", "utf8");
|
|
2154
|
+
writeFileSync10(
|
|
2060
2155
|
taskTaskPath(taskId, apmRoot, workdir),
|
|
2061
2156
|
detail.task.description ?? "",
|
|
2062
2157
|
"utf8"
|
|
2063
2158
|
);
|
|
2064
2159
|
for (const doc of detail.documents) {
|
|
2065
2160
|
const fileName = documentLocalFileName(doc.name);
|
|
2066
|
-
|
|
2161
|
+
writeFileSync10(join13(docsDir, fileName), doc.content ?? "", "utf8");
|
|
2067
2162
|
}
|
|
2068
2163
|
const members = detail.studio?.members ?? [];
|
|
2069
2164
|
const taskYaml = yamlStringify(
|
|
@@ -2081,7 +2176,7 @@ async function runTaskPull(cfg, detail) {
|
|
|
2081
2176
|
},
|
|
2082
2177
|
{ lineWidth: 0 }
|
|
2083
2178
|
);
|
|
2084
|
-
|
|
2179
|
+
writeFileSync10(
|
|
2085
2180
|
taskYamlPath(taskId, apmRoot, workdir),
|
|
2086
2181
|
taskYaml.endsWith("\n") ? taskYaml : `${taskYaml}
|
|
2087
2182
|
`,
|
|
@@ -2095,6 +2190,9 @@ async function runTaskPull(cfg, detail) {
|
|
|
2095
2190
|
}
|
|
2096
2191
|
|
|
2097
2192
|
// src/commands/connect/mail-processor.ts
|
|
2193
|
+
function resolveCursorAgentMode(phase) {
|
|
2194
|
+
return phase === "PLANNING" ? "plan" : "agent";
|
|
2195
|
+
}
|
|
2098
2196
|
async function processMail(mail, options) {
|
|
2099
2197
|
const api = createApmApiClient(options.cfg);
|
|
2100
2198
|
console.log(`[apm] \u5F00\u59CB\u5904\u7406\u4FE1\u4EF6 id=${mail.id}`);
|
|
@@ -2151,6 +2249,7 @@ async function processMail(mail, options) {
|
|
|
2151
2249
|
hasReplyContent
|
|
2152
2250
|
} = createMailReplyDraft();
|
|
2153
2251
|
try {
|
|
2252
|
+
const cursorMode = resolveCursorAgentMode(detail.studio?.phase);
|
|
2154
2253
|
const result = await runCursorAgent(
|
|
2155
2254
|
options.cfg,
|
|
2156
2255
|
{
|
|
@@ -2160,7 +2259,8 @@ async function processMail(mail, options) {
|
|
|
2160
2259
|
model: detail.recipient.model?.trim() || "default",
|
|
2161
2260
|
apiKey: cursorApiKey,
|
|
2162
2261
|
workdir: detail.workdir,
|
|
2163
|
-
user: detail.recipient.displayName
|
|
2262
|
+
user: detail.recipient.displayName,
|
|
2263
|
+
mode: cursorMode
|
|
2164
2264
|
},
|
|
2165
2265
|
{
|
|
2166
2266
|
signal: options.signal,
|
|
@@ -2171,6 +2271,14 @@ async function processMail(mail, options) {
|
|
|
2171
2271
|
if (!replyContent) {
|
|
2172
2272
|
throw new Error("Agent \u672A\u4EA7\u51FA\u53EF\u63D0\u4EA4\u7684\u56DE\u4FE1\u5185\u5BB9");
|
|
2173
2273
|
}
|
|
2274
|
+
if (cursorMode === "plan" && result.createPlanContent) {
|
|
2275
|
+
saveMemberPlanDocument({
|
|
2276
|
+
workdir: detail.workdir,
|
|
2277
|
+
taskId: detail.taskId,
|
|
2278
|
+
memberDisplayName: detail.recipient.displayName,
|
|
2279
|
+
content: result.createPlanContent
|
|
2280
|
+
});
|
|
2281
|
+
}
|
|
2174
2282
|
await api.cli.completeMailboxMessage({
|
|
2175
2283
|
id: detail.id,
|
|
2176
2284
|
status: "SUCCEEDED",
|
|
@@ -2197,6 +2305,7 @@ async function processMail(mail, options) {
|
|
|
2197
2305
|
completeErr instanceof Error ? completeErr.message : completeErr
|
|
2198
2306
|
);
|
|
2199
2307
|
}
|
|
2308
|
+
markMailReplied(detail.id);
|
|
2200
2309
|
}
|
|
2201
2310
|
}
|
|
2202
2311
|
function createMailProcessor(options) {
|