ai-project-manage-cli 6.0.42 → 6.0.44-alpha.1
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
|
@@ -127,6 +127,13 @@ function resolveWorkdirPath(cwd = process.cwd()) {
|
|
|
127
127
|
return normalizeWorkdirPath(absolute);
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
|
+
function requireRemoteWorkdir(workdir) {
|
|
131
|
+
const trimmed = typeof workdir === "string" ? workdir.trim() : "";
|
|
132
|
+
if (!trimmed) {
|
|
133
|
+
throw new Error("[apm] \u8FDC\u7A0B\u6D88\u606F\u7F3A\u5C11\u5DE5\u4F5C\u76EE\u5F55 workdir");
|
|
134
|
+
}
|
|
135
|
+
return resolveWorkdirPath(trimmed);
|
|
136
|
+
}
|
|
130
137
|
|
|
131
138
|
// src/command-utils.ts
|
|
132
139
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -611,7 +618,7 @@ async function runBranch(sessionId, options = {}) {
|
|
|
611
618
|
|
|
612
619
|
// src/commands/pull.ts
|
|
613
620
|
import { writeFileSync as writeFileSync6 } from "fs";
|
|
614
|
-
import {
|
|
621
|
+
import { join as join7 } from "path";
|
|
615
622
|
import { stringify as yamlStringify } from "yaml";
|
|
616
623
|
|
|
617
624
|
// src/session-messages-xml.ts
|
|
@@ -908,13 +915,7 @@ async function syncPlatformRules(cfg, sessionId, workdirPath, apmRoot) {
|
|
|
908
915
|
}
|
|
909
916
|
|
|
910
917
|
// src/commands/pull.ts
|
|
911
|
-
function
|
|
912
|
-
if (apmRoot) {
|
|
913
|
-
return resolveWorkdirPath(dirname2(apmRoot));
|
|
914
|
-
}
|
|
915
|
-
return resolveWorkdirPath();
|
|
916
|
-
}
|
|
917
|
-
async function runPull(sessionId, apmRoot) {
|
|
918
|
+
async function runPull(sessionId, remoteWorkdir) {
|
|
918
919
|
const trimmedId = sessionId.trim();
|
|
919
920
|
if (!trimmedId) {
|
|
920
921
|
console.error("[apm] sessionId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
@@ -922,6 +923,8 @@ async function runPull(sessionId, apmRoot) {
|
|
|
922
923
|
}
|
|
923
924
|
const cfg = await ensureLoggedConfig();
|
|
924
925
|
const api = createApmApiClient(cfg);
|
|
926
|
+
const workdir = remoteWorkdir === void 0 ? resolveWorkdirPath() : requireRemoteWorkdir(remoteWorkdir);
|
|
927
|
+
const apmRoot = workspaceApmDir(workdir);
|
|
925
928
|
const [detail, members, documents, attachments, messages] = await Promise.all(
|
|
926
929
|
[
|
|
927
930
|
api.cli.sessionDetail({ sessionId: trimmedId }),
|
|
@@ -976,8 +979,7 @@ async function runPull(sessionId, apmRoot) {
|
|
|
976
979
|
"utf8"
|
|
977
980
|
);
|
|
978
981
|
await syncSessionAttachments(cfg, trimmedId, attachments, apmRoot);
|
|
979
|
-
|
|
980
|
-
await syncPlatformRules(cfg, trimmedId, workdirPath, apmRoot);
|
|
982
|
+
await syncPlatformRules(cfg, trimmedId, workdir, apmRoot);
|
|
981
983
|
console.log(`[apm] \u5DF2\u540C\u6B65\u4F1A\u8BDD\u5DE5\u4F5C\u533A: ${dir}`);
|
|
982
984
|
return dir;
|
|
983
985
|
}
|
|
@@ -987,12 +989,12 @@ import { spawnSync } from "child_process";
|
|
|
987
989
|
|
|
988
990
|
// src/version.ts
|
|
989
991
|
import { readFileSync as readFileSync5 } from "fs";
|
|
990
|
-
import { dirname as
|
|
992
|
+
import { dirname as dirname2, join as join8 } from "path";
|
|
991
993
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
992
994
|
var CLI_PACKAGE_NAME = "ai-project-manage-cli";
|
|
993
995
|
function readCliVersion() {
|
|
994
996
|
try {
|
|
995
|
-
const dir =
|
|
997
|
+
const dir = dirname2(fileURLToPath2(import.meta.url));
|
|
996
998
|
const pkgPath = join8(dir, "..", "package.json");
|
|
997
999
|
const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
|
|
998
1000
|
return pkg.version ?? "0.0.0";
|
|
@@ -1430,7 +1432,6 @@ import {
|
|
|
1430
1432
|
CursorAgentError
|
|
1431
1433
|
} from "@cursor/sdk";
|
|
1432
1434
|
import { setMaxListeners as setMaxListeners2 } from "node:events";
|
|
1433
|
-
import { resolve as resolve4 } from "path";
|
|
1434
1435
|
|
|
1435
1436
|
// src/session-utils.ts
|
|
1436
1437
|
var EventSession = class {
|
|
@@ -1592,7 +1593,7 @@ ${JSON.stringify(event, null, 2)}
|
|
|
1592
1593
|
|
|
1593
1594
|
// src/commands/connect/agent-session-registry.ts
|
|
1594
1595
|
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync7 } from "node:fs";
|
|
1595
|
-
import { dirname as
|
|
1596
|
+
import { dirname as dirname3, resolve as resolve3 } from "node:path";
|
|
1596
1597
|
function registryPath(workdir, sessionId) {
|
|
1597
1598
|
return resolve3(workdir, ".apm", "sessions", sessionId, "cursor-agents.json");
|
|
1598
1599
|
}
|
|
@@ -1618,7 +1619,7 @@ function readRegistry(path10) {
|
|
|
1618
1619
|
return {};
|
|
1619
1620
|
}
|
|
1620
1621
|
function writeRegistry(path10, registry) {
|
|
1621
|
-
mkdirSync5(
|
|
1622
|
+
mkdirSync5(dirname3(path10), { recursive: true });
|
|
1622
1623
|
writeFileSync7(path10, `${JSON.stringify(registry, null, 2)}
|
|
1623
1624
|
`, "utf8");
|
|
1624
1625
|
}
|
|
@@ -1755,6 +1756,16 @@ function createAppendMessageCustomTools(cfg, messageId) {
|
|
|
1755
1756
|
};
|
|
1756
1757
|
}
|
|
1757
1758
|
|
|
1759
|
+
// src/commands/connect/playwright-mcp.ts
|
|
1760
|
+
function createPlaywrightMcpServers() {
|
|
1761
|
+
return {
|
|
1762
|
+
playwright: {
|
|
1763
|
+
command: "npx",
|
|
1764
|
+
args: ["@playwright/mcp@latest", "--browser", "chrome", "--vision"]
|
|
1765
|
+
}
|
|
1766
|
+
};
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1758
1769
|
// src/commands/connect/cursor-agent.ts
|
|
1759
1770
|
setMaxListeners2(50);
|
|
1760
1771
|
installAbortSignalDebug();
|
|
@@ -1782,7 +1793,8 @@ async function obtainAgent(ctx) {
|
|
|
1782
1793
|
model: { id: ctx.model || "default" },
|
|
1783
1794
|
local: {
|
|
1784
1795
|
cwd: ctx.cwd
|
|
1785
|
-
}
|
|
1796
|
+
},
|
|
1797
|
+
mcpServers: createPlaywrightMcpServers()
|
|
1786
1798
|
};
|
|
1787
1799
|
const savedAgentId = ctx.user ? loadSessionAgentId(ctx.workdir, ctx.sessionId, ctx.user) : void 0;
|
|
1788
1800
|
if (savedAgentId) {
|
|
@@ -1816,16 +1828,16 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
1816
1828
|
if (!apiKey) {
|
|
1817
1829
|
throw new Error("\u7F3A\u5C11 apiKey\uFF0C\u65E0\u6CD5\u8C03\u7528 Cursor SDK");
|
|
1818
1830
|
}
|
|
1819
|
-
const
|
|
1831
|
+
const workdir = resolveWorkdirPath(ctx.workdir);
|
|
1820
1832
|
const prompt = ctx.prompt;
|
|
1821
1833
|
console.log(
|
|
1822
|
-
`[apm] Cursor Agent \u5F00\u59CB messageId=${ctx.messageId} sessionId=${ctx.sessionId} cwd=${
|
|
1834
|
+
`[apm] Cursor Agent \u5F00\u59CB messageId=${ctx.messageId} sessionId=${ctx.sessionId} cwd=${workdir}`
|
|
1823
1835
|
);
|
|
1824
1836
|
const { agent, resumed } = await obtainAgent({
|
|
1825
1837
|
apiKey,
|
|
1826
1838
|
model: ctx.model,
|
|
1827
|
-
cwd,
|
|
1828
|
-
workdir
|
|
1839
|
+
cwd: workdir,
|
|
1840
|
+
workdir,
|
|
1829
1841
|
sessionId: ctx.sessionId,
|
|
1830
1842
|
user: ctx.user
|
|
1831
1843
|
});
|
|
@@ -1850,6 +1862,7 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
1850
1862
|
logAbortSignalStats(signal, "runCursorAgent:after-addListener");
|
|
1851
1863
|
try {
|
|
1852
1864
|
const run = await agent.send(prompt, {
|
|
1865
|
+
mcpServers: createPlaywrightMcpServers(),
|
|
1853
1866
|
local: {
|
|
1854
1867
|
customTools: createAppendMessageCustomTools(cfg, ctx.messageId)
|
|
1855
1868
|
}
|
|
@@ -1884,7 +1897,7 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
1884
1897
|
});
|
|
1885
1898
|
console.error(`[apm] ${failureMessage}`);
|
|
1886
1899
|
if (resumed) {
|
|
1887
|
-
clearSessionAgentId(
|
|
1900
|
+
clearSessionAgentId(workdir, ctx.sessionId, ctx.user);
|
|
1888
1901
|
}
|
|
1889
1902
|
throw new Error(failureMessage);
|
|
1890
1903
|
}
|
|
@@ -1895,7 +1908,7 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
1895
1908
|
} catch (err) {
|
|
1896
1909
|
if (err instanceof CursorAgentError) {
|
|
1897
1910
|
if (resumed) {
|
|
1898
|
-
clearSessionAgentId(
|
|
1911
|
+
clearSessionAgentId(workdir, ctx.sessionId, ctx.user);
|
|
1899
1912
|
}
|
|
1900
1913
|
throw new Error(
|
|
1901
1914
|
`Cursor \u542F\u52A8\u5931\u8D25: ${err.message}${err.isRetryable ? "\uFF08\u53EF\u91CD\u8BD5\uFF09" : ""}`
|
|
@@ -1912,23 +1925,27 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
1912
1925
|
|
|
1913
1926
|
// src/commands/connect/pre-step-cache.ts
|
|
1914
1927
|
var PULL_TTL_MS = 3e4;
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1928
|
+
function sessionWorkdirKey(sessionId, workdir) {
|
|
1929
|
+
return `${sessionId}\0${workdir}`;
|
|
1930
|
+
}
|
|
1931
|
+
var lastBranchKey = null;
|
|
1932
|
+
var lastPullAtByKey = /* @__PURE__ */ new Map();
|
|
1933
|
+
function shouldRunBranch(sessionId, workdir) {
|
|
1934
|
+
return lastBranchKey !== sessionWorkdirKey(sessionId, workdir);
|
|
1919
1935
|
}
|
|
1920
|
-
function markBranchDone(sessionId) {
|
|
1921
|
-
|
|
1936
|
+
function markBranchDone(sessionId, workdir) {
|
|
1937
|
+
lastBranchKey = sessionWorkdirKey(sessionId, workdir);
|
|
1922
1938
|
}
|
|
1923
|
-
function shouldRunPull(sessionId) {
|
|
1924
|
-
const
|
|
1939
|
+
function shouldRunPull(sessionId, workdir) {
|
|
1940
|
+
const key = sessionWorkdirKey(sessionId, workdir);
|
|
1941
|
+
const last = lastPullAtByKey.get(key);
|
|
1925
1942
|
if (last == null) {
|
|
1926
1943
|
return true;
|
|
1927
1944
|
}
|
|
1928
1945
|
return Date.now() - last >= PULL_TTL_MS;
|
|
1929
1946
|
}
|
|
1930
|
-
function markPullDone(sessionId) {
|
|
1931
|
-
|
|
1947
|
+
function markPullDone(sessionId, workdir) {
|
|
1948
|
+
lastPullAtByKey.set(sessionWorkdirKey(sessionId, workdir), Date.now());
|
|
1932
1949
|
}
|
|
1933
1950
|
|
|
1934
1951
|
// src/commands/connect/run-slot-pool.ts
|
|
@@ -1941,10 +1958,10 @@ function createRunSlotPool(maxConcurrent = DEFAULT_MAX_CONCURRENT) {
|
|
|
1941
1958
|
active += 1;
|
|
1942
1959
|
return Promise.resolve();
|
|
1943
1960
|
}
|
|
1944
|
-
return new Promise((
|
|
1961
|
+
return new Promise((resolve5) => {
|
|
1945
1962
|
waiters.push(() => {
|
|
1946
1963
|
active += 1;
|
|
1947
|
-
|
|
1964
|
+
resolve5();
|
|
1948
1965
|
});
|
|
1949
1966
|
});
|
|
1950
1967
|
};
|
|
@@ -1978,6 +1995,8 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
|
|
|
1978
1995
|
if (isUserCancelled(ctx)) return;
|
|
1979
1996
|
if (signal.aborted) return;
|
|
1980
1997
|
const messageId = msg.messageId;
|
|
1998
|
+
const workdir = requireRemoteWorkdir(msg.workdir);
|
|
1999
|
+
const apmRoot = workspaceApmDir(workdir);
|
|
1981
2000
|
const runStep = async (step, fn) => {
|
|
1982
2001
|
const startedAt = Date.now();
|
|
1983
2002
|
try {
|
|
@@ -1995,24 +2014,18 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
|
|
|
1995
2014
|
"status-typing",
|
|
1996
2015
|
() => updateMessageStatus(cfg, messageId, "TYPING")
|
|
1997
2016
|
);
|
|
1998
|
-
if (shouldRunBranch(msg.sessionId)) {
|
|
2017
|
+
if (shouldRunBranch(msg.sessionId, workdir)) {
|
|
1999
2018
|
if (signal.aborted) return;
|
|
2000
|
-
await runStep(
|
|
2001
|
-
|
|
2002
|
-
() => runBranch(msg.sessionId, { cwd: msg.workdir })
|
|
2003
|
-
);
|
|
2004
|
-
markBranchDone(msg.sessionId);
|
|
2019
|
+
await runStep("branch", () => runBranch(msg.sessionId, { cwd: workdir }));
|
|
2020
|
+
markBranchDone(msg.sessionId, workdir);
|
|
2005
2021
|
} else {
|
|
2006
2022
|
console.log(`[apm] step=branch skipped sessionId=${msg.sessionId}`);
|
|
2007
2023
|
}
|
|
2008
2024
|
let pullRan = false;
|
|
2009
|
-
if (shouldRunPull(msg.sessionId)) {
|
|
2025
|
+
if (shouldRunPull(msg.sessionId, workdir)) {
|
|
2010
2026
|
if (signal.aborted) return;
|
|
2011
|
-
await runStep(
|
|
2012
|
-
|
|
2013
|
-
() => runPull(msg.sessionId, workspaceApmDir(msg.workdir))
|
|
2014
|
-
);
|
|
2015
|
-
markPullDone(msg.sessionId);
|
|
2027
|
+
await runStep("pull", () => runPull(msg.sessionId, workdir));
|
|
2028
|
+
markPullDone(msg.sessionId, workdir);
|
|
2016
2029
|
pullRan = true;
|
|
2017
2030
|
} else {
|
|
2018
2031
|
console.log(`[apm] step=pull skipped sessionId=${msg.sessionId}`);
|
|
@@ -2021,7 +2034,7 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
|
|
|
2021
2034
|
if (signal.aborted) return;
|
|
2022
2035
|
await runStep(
|
|
2023
2036
|
"commit-pull",
|
|
2024
|
-
() => commitWorkingTreeIfDirty(
|
|
2037
|
+
() => commitWorkingTreeIfDirty(workdir, "fix: apm pull")
|
|
2025
2038
|
);
|
|
2026
2039
|
} else {
|
|
2027
2040
|
console.log(`[apm] step=commit-pull skipped sessionId=${msg.sessionId}`);
|
|
@@ -2037,7 +2050,7 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
|
|
|
2037
2050
|
prompt: msg.content,
|
|
2038
2051
|
model: msg.model,
|
|
2039
2052
|
apiKey: msg.apiKey,
|
|
2040
|
-
workdir
|
|
2053
|
+
workdir,
|
|
2041
2054
|
user: msg.user
|
|
2042
2055
|
},
|
|
2043
2056
|
{ signal }
|
|
@@ -2045,11 +2058,11 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
|
|
|
2045
2058
|
);
|
|
2046
2059
|
await runStep(
|
|
2047
2060
|
"sync-documents",
|
|
2048
|
-
() => syncSessionDocuments(cfg, msg.sessionId,
|
|
2061
|
+
() => syncSessionDocuments(cfg, msg.sessionId, apmRoot)
|
|
2049
2062
|
);
|
|
2050
2063
|
await runStep(
|
|
2051
2064
|
"commit-files",
|
|
2052
|
-
() => commitWorkingTreeIfDirty(
|
|
2065
|
+
() => commitWorkingTreeIfDirty(workdir, "chore(apm): commit working tree")
|
|
2053
2066
|
);
|
|
2054
2067
|
await runStep(
|
|
2055
2068
|
"status-success",
|
|
@@ -2109,7 +2122,7 @@ async function runConnect(options) {
|
|
|
2109
2122
|
}
|
|
2110
2123
|
const url = buildAgentWsUrl(cfg.baseUrl, resolveApiKey(cfg));
|
|
2111
2124
|
console.log(`[apm] \u8FDE\u63A5 ${cfg.baseUrl} \u2026`);
|
|
2112
|
-
await new Promise((
|
|
2125
|
+
await new Promise((resolve5, reject) => {
|
|
2113
2126
|
const ws = new WebSocket(url);
|
|
2114
2127
|
let stopHeartbeat;
|
|
2115
2128
|
let shuttingDown = false;
|
|
@@ -2138,7 +2151,7 @@ async function runConnect(options) {
|
|
|
2138
2151
|
]);
|
|
2139
2152
|
} catch {
|
|
2140
2153
|
}
|
|
2141
|
-
|
|
2154
|
+
resolve5();
|
|
2142
2155
|
process.exit(code);
|
|
2143
2156
|
};
|
|
2144
2157
|
ws.on("open", () => {
|
|
@@ -2226,11 +2239,11 @@ import path5 from "node:path";
|
|
|
2226
2239
|
|
|
2227
2240
|
// src/commands/deploy/internal/apm-config.ts
|
|
2228
2241
|
import { existsSync as existsSync9, readFileSync as readFileSync8 } from "node:fs";
|
|
2229
|
-
import { resolve as
|
|
2242
|
+
import { resolve as resolve4 } from "node:path";
|
|
2230
2243
|
function loadApmConfig(options) {
|
|
2231
|
-
const p =
|
|
2244
|
+
const p = resolve4(
|
|
2232
2245
|
process.cwd(),
|
|
2233
|
-
options?.configPath ??
|
|
2246
|
+
options?.configPath ?? resolve4(workspaceApmDir(), "apm.config.json")
|
|
2234
2247
|
);
|
|
2235
2248
|
if (!existsSync9(p)) {
|
|
2236
2249
|
console.error(`\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6\uFF1A${p}`);
|
|
@@ -2497,17 +2510,17 @@ var DockerodeClient = class {
|
|
|
2497
2510
|
await this.client.getImage(image).remove({ force: true });
|
|
2498
2511
|
}
|
|
2499
2512
|
async pullImage(image, auth) {
|
|
2500
|
-
const stream = await new Promise((
|
|
2513
|
+
const stream = await new Promise((resolve5, reject) => {
|
|
2501
2514
|
const pullOptions = auth ? { authconfig: auth } : void 0;
|
|
2502
2515
|
this.client.pull(image, pullOptions, (err, output) => {
|
|
2503
2516
|
if (err || !output) {
|
|
2504
2517
|
reject(err ?? new Error("docker pull \u8FD4\u56DE\u7A7A\u8F93\u51FA"));
|
|
2505
2518
|
return;
|
|
2506
2519
|
}
|
|
2507
|
-
|
|
2520
|
+
resolve5(output);
|
|
2508
2521
|
});
|
|
2509
2522
|
});
|
|
2510
|
-
await new Promise((
|
|
2523
|
+
await new Promise((resolve5, reject) => {
|
|
2511
2524
|
this.client.modem.followProgress(
|
|
2512
2525
|
stream,
|
|
2513
2526
|
(err) => {
|
|
@@ -2515,7 +2528,7 @@ var DockerodeClient = class {
|
|
|
2515
2528
|
reject(err);
|
|
2516
2529
|
return;
|
|
2517
2530
|
}
|
|
2518
|
-
|
|
2531
|
+
resolve5();
|
|
2519
2532
|
},
|
|
2520
2533
|
() => void 0
|
|
2521
2534
|
);
|
|
@@ -3006,14 +3019,14 @@ var MinioClient = class {
|
|
|
3006
3019
|
async deleteObjectsByPrefix(bucket, prefix) {
|
|
3007
3020
|
const objectsStream = this.inner.listObjectsV2(bucket, prefix, true);
|
|
3008
3021
|
const keys = [];
|
|
3009
|
-
await new Promise((
|
|
3022
|
+
await new Promise((resolve5, reject) => {
|
|
3010
3023
|
objectsStream.on("data", (obj) => {
|
|
3011
3024
|
if (obj.name) {
|
|
3012
3025
|
keys.push(obj.name);
|
|
3013
3026
|
}
|
|
3014
3027
|
});
|
|
3015
3028
|
objectsStream.on("error", reject);
|
|
3016
|
-
objectsStream.on("end",
|
|
3029
|
+
objectsStream.on("end", resolve5);
|
|
3017
3030
|
});
|
|
3018
3031
|
const chunkSize = 500;
|
|
3019
3032
|
for (let i = 0; i < keys.length; i += chunkSize) {
|
|
@@ -3251,7 +3264,7 @@ async function ensureRemoteDir(sftp, dir) {
|
|
|
3251
3264
|
}
|
|
3252
3265
|
}
|
|
3253
3266
|
function execCommand(client, command) {
|
|
3254
|
-
return new Promise((
|
|
3267
|
+
return new Promise((resolve5, reject) => {
|
|
3255
3268
|
client.exec(command, (err, stream) => {
|
|
3256
3269
|
if (err) return reject(err);
|
|
3257
3270
|
let stdout = "";
|
|
@@ -3261,7 +3274,7 @@ function execCommand(client, command) {
|
|
|
3261
3274
|
reject(new Error(`\u8FDC\u7A0B\u547D\u4EE4\u5931\u8D25 (${code}): ${stderr || stdout}`));
|
|
3262
3275
|
return;
|
|
3263
3276
|
}
|
|
3264
|
-
|
|
3277
|
+
resolve5(stdout);
|
|
3265
3278
|
}).on("data", (data) => {
|
|
3266
3279
|
stdout += data.toString();
|
|
3267
3280
|
});
|
package/package.json
CHANGED
|
@@ -6,7 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
1. 用 **Read** 工具阅读本端计划:前端读 `.apm/sessions/<会话ID>/docs/FRONTEND-PLAN.md`,后端读 `docs/BACKEND-PLAN.md`;计划不存在则退出流程并回复说明(兼容旧流程:若存在 `PRD.md` + `FRONTEND.md` / `BACKEND.md` + `API.md`,按旧文档执行)。
|
|
8
8
|
2. 前端涉及接口对接时,以后端计划中的「API 契约」章节为准,**不等后端部署完成**;契约没写清的字段 `@后端` 确认,禁止自行猜测。
|
|
9
|
-
3.
|
|
9
|
+
3. **假设门禁(开发前必须检查)**:查看计划「依据与假设」章节——
|
|
10
|
+
- 「假设」仍有未确认项:**Read** `.apm/sessions/<会话ID>/messages.xml`,查找项目经理是否已回复确认;
|
|
11
|
+
- 项目经理已回复:先按 `.apm/skills/apm-write-plan/SKILL.md` 步骤 5 把确认结果**回填进计划文档并同步**(确认的假设移入「依据」,否定的修订实现步骤与白名单),然后再开发;
|
|
12
|
+
- 项目经理未回复:`@项目经理` 列出待确认假设,**停止本次开发**。
|
|
13
|
+
严禁带着未回填的澄清直接开发——项目经理在聊天里给过的口径若没落进计划,开发与 diff 评审都不会认。
|
|
10
14
|
|
|
11
15
|
### 步骤 2: 明确开发模式
|
|
12
16
|
|
|
@@ -44,6 +44,20 @@
|
|
|
44
44
|
- **无假设**:回复计划已就绪,可进入测试要点编写。
|
|
45
45
|
- 前端计划依赖的接口后端契约还没出:在计划「期望接口」小节写出前端期望的接口形态,回复时 `@后端` 对齐,不要空等。
|
|
46
46
|
|
|
47
|
+
### 步骤 5:假设回填(项目经理确认后必须执行)
|
|
48
|
+
|
|
49
|
+
当你被安排「根据项目经理的确认更新计划」,或发现项目经理已回复假设确认时:
|
|
50
|
+
|
|
51
|
+
1. **Read** `.apm/sessions/<会话ID>/messages.xml`,找到项目经理针对假设的最新回复,**逐条对照你列出的假设**,不要只看 speakContext 的转述。
|
|
52
|
+
2. 更新计划文档:
|
|
53
|
+
- 被确认的假设 → **移入「依据」表格**,来源写「项目经理确认(第 N 轮)」;
|
|
54
|
+
- 被否定或修正的假设 → 按项目经理给出的口径**修订「实现步骤」与「改动文件白名单」**;
|
|
55
|
+
- 项目经理没有回应的假设 → 保留在「假设」中,再次 `@项目经理` 追问。
|
|
56
|
+
3. 重新执行 `apm sync-document` 同步计划。
|
|
57
|
+
4. 回复消息:逐条说明每个假设的处理结果(确认采纳 / 按口径修订了什么),全部解决则声明「假设已清零,可进入测试要点编写」。
|
|
58
|
+
|
|
59
|
+
**禁止**跳过回填直接开发:澄清结论必须落进计划文档,后续开发与 diff 评审都只认计划文档,不认聊天记录。
|
|
60
|
+
|
|
47
61
|
---
|
|
48
62
|
|
|
49
63
|
## 写作要求
|