ai-project-manage-cli 5.0.14 → 5.0.16
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 +80 -88
- package/package.json +2 -2
- package/template/AGENTS.md +14 -15
- package/template/rules/reply.md +15 -0
- package/template/rules/write_doc.md +30 -0
- package/template/skills/apm-apply-change/SKILL.md +60 -122
- package/template/skills/apm-deploy/SKILL.md +4 -15
- package/template/skills/apm-dev/SKILL.md +26 -10
- package/template/skills/apm-propose/SKILL.md +29 -168
- package/template/skills/apm-propose/design.md +37 -0
- package/template/skills/apm-propose/proposal.md +31 -0
- package/template/skills/apm-propose/specs.md +39 -0
- package/template/skills/apm-propose/tasks.md +16 -0
- package/template/skills/apm-review/SKILL.md +10 -14
- package/template/skills/apm-write-prd/SKILL.md +8 -61
- package/template/skills/apm-write-prd/template.md +55 -0
package/dist/index.js
CHANGED
|
@@ -10,6 +10,12 @@ import { join } from "path";
|
|
|
10
10
|
var APM_CONFIG_DIR = join(homedir(), ".config", "apm");
|
|
11
11
|
var APM_CONFIG_PATH = join(APM_CONFIG_DIR, "config.json");
|
|
12
12
|
var DEFAULT_BASE_URL = "http://127.0.0.1:3000";
|
|
13
|
+
function resolveClientMachineId(cfg) {
|
|
14
|
+
return (cfg.clientMachineId ?? cfg.userId ?? "").trim();
|
|
15
|
+
}
|
|
16
|
+
function resolveAccount(cfg) {
|
|
17
|
+
return (cfg.account ?? cfg.email ?? "").trim();
|
|
18
|
+
}
|
|
13
19
|
async function readApmConfig() {
|
|
14
20
|
try {
|
|
15
21
|
const raw = readFileSync(APM_CONFIG_PATH, "utf8");
|
|
@@ -18,14 +24,17 @@ async function readApmConfig() {
|
|
|
18
24
|
return null;
|
|
19
25
|
}
|
|
20
26
|
const rawCfg = v;
|
|
21
|
-
if (typeof rawCfg.baseUrl !== "string" || typeof rawCfg.
|
|
27
|
+
if (typeof rawCfg.baseUrl !== "string" || typeof rawCfg.token !== "string") {
|
|
22
28
|
return null;
|
|
23
29
|
}
|
|
24
30
|
const cfg = v;
|
|
31
|
+
const clientMachineId = resolveClientMachineId(cfg);
|
|
32
|
+
const account = resolveAccount(cfg);
|
|
25
33
|
return {
|
|
26
34
|
baseUrl: cfg.baseUrl.trim().replace(/\/+$/, ""),
|
|
27
|
-
userId: cfg.userId,
|
|
28
35
|
token: cfg.token,
|
|
36
|
+
...clientMachineId ? { clientMachineId } : {},
|
|
37
|
+
...account ? { account } : {},
|
|
29
38
|
...typeof cfg.email === "string" ? { email: cfg.email } : {}
|
|
30
39
|
};
|
|
31
40
|
} catch {
|
|
@@ -35,8 +44,9 @@ async function readApmConfig() {
|
|
|
35
44
|
function defaultApmConfig() {
|
|
36
45
|
return {
|
|
37
46
|
baseUrl: DEFAULT_BASE_URL,
|
|
38
|
-
|
|
47
|
+
clientMachineId: "",
|
|
39
48
|
token: "",
|
|
49
|
+
account: "",
|
|
40
50
|
email: ""
|
|
41
51
|
};
|
|
42
52
|
}
|
|
@@ -54,11 +64,13 @@ async function ensureApmConfig() {
|
|
|
54
64
|
return defaults;
|
|
55
65
|
}
|
|
56
66
|
async function writeApmConfig(cfg) {
|
|
67
|
+
const clientMachineId = resolveClientMachineId(cfg);
|
|
68
|
+
const account = resolveAccount(cfg);
|
|
57
69
|
const normalized = {
|
|
58
70
|
baseUrl: cfg.baseUrl.trim().replace(/\/+$/, ""),
|
|
59
|
-
userId: cfg.userId,
|
|
60
71
|
token: cfg.token,
|
|
61
|
-
...
|
|
72
|
+
...clientMachineId ? { clientMachineId } : {},
|
|
73
|
+
...account ? { account, email: account } : {}
|
|
62
74
|
};
|
|
63
75
|
mkdirSync(APM_CONFIG_DIR, { recursive: true });
|
|
64
76
|
writeFileSync(APM_CONFIG_PATH, JSON.stringify(normalized, null, 2) + "\n", {
|
|
@@ -96,6 +108,12 @@ function sessionDocsDir(sessionId, apmRoot) {
|
|
|
96
108
|
function sessionRulePath(sessionId, apmRoot) {
|
|
97
109
|
return join2(sessionDir(sessionId, apmRoot), "RULE.md");
|
|
98
110
|
}
|
|
111
|
+
function sessionTaskPath(sessionId, apmRoot) {
|
|
112
|
+
return join2(sessionDir(sessionId, apmRoot), "TASK.md");
|
|
113
|
+
}
|
|
114
|
+
function sessionLogPath(sessionId, apmRoot) {
|
|
115
|
+
return join2(sessionDir(sessionId, apmRoot), "LOG.md");
|
|
116
|
+
}
|
|
99
117
|
function sessionYamlPath(sessionId, apmRoot) {
|
|
100
118
|
return join2(sessionDir(sessionId, apmRoot), "session.yaml");
|
|
101
119
|
}
|
|
@@ -231,7 +249,7 @@ var requestConfig = {
|
|
|
231
249
|
}),
|
|
232
250
|
branchBaseline: defineEndpoint({
|
|
233
251
|
method: "GET",
|
|
234
|
-
path: "/cli/
|
|
252
|
+
path: "/cli/tasks/branch-baseline"
|
|
235
253
|
}),
|
|
236
254
|
listSkills: defineEndpoint({
|
|
237
255
|
method: "GET",
|
|
@@ -256,13 +274,13 @@ async function runLogin(opts) {
|
|
|
256
274
|
const baseUrl = (opts.server?.trim() || process.env.AI_PM_SERVER?.trim() || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
257
275
|
const api = createApmApiClient({
|
|
258
276
|
baseUrl,
|
|
259
|
-
|
|
277
|
+
clientMachineId: "",
|
|
260
278
|
token: ""
|
|
261
279
|
});
|
|
262
280
|
let data;
|
|
263
281
|
try {
|
|
264
282
|
data = await api.auth.login({
|
|
265
|
-
|
|
283
|
+
account: opts.email,
|
|
266
284
|
password: opts.password
|
|
267
285
|
});
|
|
268
286
|
} catch (raw) {
|
|
@@ -277,24 +295,29 @@ async function runLogin(opts) {
|
|
|
277
295
|
console.error("[apm] \u8BF7\u6C42\u5931\u8D25:", raw instanceof Error ? raw.message : raw);
|
|
278
296
|
process.exit(1);
|
|
279
297
|
}
|
|
280
|
-
const
|
|
298
|
+
const clientMachineId = data?.clientMachine?.id;
|
|
281
299
|
const token = data?.token;
|
|
282
|
-
if (!
|
|
300
|
+
if (!clientMachineId || !token) {
|
|
283
301
|
console.error(
|
|
284
|
-
"[apm] \u54CD\u5E94\u7F3A\u5C11
|
|
302
|
+
"[apm] \u54CD\u5E94\u7F3A\u5C11 clientMachine.id / token\uFF08\u8BF7\u786E\u8BA4\u670D\u52A1\u7AEF\u4E3A /api/v1/auth/login\uFF09"
|
|
285
303
|
);
|
|
286
304
|
process.exit(1);
|
|
287
305
|
}
|
|
288
306
|
const cfg = {
|
|
289
307
|
baseUrl,
|
|
290
|
-
|
|
308
|
+
clientMachineId,
|
|
291
309
|
token,
|
|
310
|
+
account: opts.email,
|
|
292
311
|
email: opts.email
|
|
293
312
|
};
|
|
294
313
|
await writeApmConfig(cfg);
|
|
295
314
|
console.error(`[apm] \u5DF2\u4FDD\u5B58\u767B\u5F55\u4FE1\u606F: ${APM_CONFIG_PATH}`);
|
|
296
315
|
console.log(
|
|
297
|
-
JSON.stringify(
|
|
316
|
+
JSON.stringify(
|
|
317
|
+
{ clientMachineId: cfg.clientMachineId, baseUrl: cfg.baseUrl },
|
|
318
|
+
null,
|
|
319
|
+
2
|
|
320
|
+
)
|
|
298
321
|
);
|
|
299
322
|
}
|
|
300
323
|
|
|
@@ -489,7 +512,7 @@ function formatSessionMessagesXml(sessionId, messages) {
|
|
|
489
512
|
lines.push(
|
|
490
513
|
` <message id="${escapeXmlAttr(message.id)}" name="${escapeXmlAttr(
|
|
491
514
|
message.name
|
|
492
|
-
)}"
|
|
515
|
+
)}" expert="${escapeXmlAttr(message.expert)}"${roundAttr}>`,
|
|
493
516
|
` <content>${wrapCdata(message.content)}</content>`,
|
|
494
517
|
" </message>"
|
|
495
518
|
);
|
|
@@ -603,6 +626,12 @@ async function runPull(sessionId, apmRoot) {
|
|
|
603
626
|
detail.description ?? "",
|
|
604
627
|
"utf8"
|
|
605
628
|
);
|
|
629
|
+
writeFileSync4(
|
|
630
|
+
sessionTaskPath(trimmedId, apmRoot),
|
|
631
|
+
detail.task.description ?? "",
|
|
632
|
+
"utf8"
|
|
633
|
+
);
|
|
634
|
+
writeFileSync4(sessionLogPath(trimmedId, apmRoot), detail.log ?? "", "utf8");
|
|
606
635
|
for (const doc of documents) {
|
|
607
636
|
const fileName = documentLocalFileName(doc.name);
|
|
608
637
|
writeFileSync4(join5(docsDir, fileName), doc.content ?? "", "utf8");
|
|
@@ -613,7 +642,7 @@ async function runPull(sessionId, apmRoot) {
|
|
|
613
642
|
description: "./RULE.md",
|
|
614
643
|
members: members.map((m) => ({
|
|
615
644
|
name: m.displayName,
|
|
616
|
-
|
|
645
|
+
expert: m.expert,
|
|
617
646
|
system_persona: m.systemPersona,
|
|
618
647
|
skills: []
|
|
619
648
|
})),
|
|
@@ -734,6 +763,7 @@ import {
|
|
|
734
763
|
import { join as join7 } from "path";
|
|
735
764
|
var AGENTS_TEMPLATE_PATH = join7(CLI_TEMPLATE_DIR, "AGENTS.md");
|
|
736
765
|
var BASE_SKILLS_TEMPLATE_DIR = join7(CLI_TEMPLATE_DIR, "skills");
|
|
766
|
+
var BASE_RULES_TEMPLATE_DIR = join7(CLI_TEMPLATE_DIR, "rules");
|
|
737
767
|
function sanitizeSkillDirName(name) {
|
|
738
768
|
const trimmed = name.trim();
|
|
739
769
|
if (!trimmed) return "skill";
|
|
@@ -752,6 +782,23 @@ function syncAgentsGuide(apmDir) {
|
|
|
752
782
|
copyFileSync(AGENTS_TEMPLATE_PATH, join7(apmDir, "AGENTS.md"));
|
|
753
783
|
return true;
|
|
754
784
|
}
|
|
785
|
+
function listBaseRuleFileNames() {
|
|
786
|
+
if (!existsSync3(BASE_RULES_TEMPLATE_DIR)) return [];
|
|
787
|
+
return readdirSync2(BASE_RULES_TEMPLATE_DIR).filter((name) => {
|
|
788
|
+
const path8 = join7(BASE_RULES_TEMPLATE_DIR, name);
|
|
789
|
+
return statSync2(path8).isFile();
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
function syncBaseRules(rulesDir) {
|
|
793
|
+
mkdirSync3(rulesDir, { recursive: true });
|
|
794
|
+
const names = listBaseRuleFileNames();
|
|
795
|
+
for (const name of names) {
|
|
796
|
+
const src = join7(BASE_RULES_TEMPLATE_DIR, name);
|
|
797
|
+
const dest = join7(rulesDir, name);
|
|
798
|
+
copyFileSync(src, dest);
|
|
799
|
+
}
|
|
800
|
+
return names;
|
|
801
|
+
}
|
|
755
802
|
function syncBaseSkills(skillsDir) {
|
|
756
803
|
mkdirSync3(skillsDir, { recursive: true });
|
|
757
804
|
const names = listBaseSkillDirNames();
|
|
@@ -809,6 +856,11 @@ async function runUpdateSkills() {
|
|
|
809
856
|
if (syncAgentsGuide(apmDir)) {
|
|
810
857
|
console.log("[apm] \u5DF2\u540C\u6B65 APM \u6307\u5357: .apm/AGENTS.md");
|
|
811
858
|
}
|
|
859
|
+
const rulesDir = join8(apmDir, "rules");
|
|
860
|
+
const ruleNames = syncBaseRules(rulesDir);
|
|
861
|
+
for (const name of ruleNames) {
|
|
862
|
+
console.log(`[apm] \u5DF2\u540C\u6B65\u57FA\u7840\u89C4\u5219: rules/${name}`);
|
|
863
|
+
}
|
|
812
864
|
const skillsDir = join8(apmDir, "skills");
|
|
813
865
|
mkdirSync4(skillsDir, { recursive: true });
|
|
814
866
|
const baseNames = syncBaseSkills(skillsDir);
|
|
@@ -831,7 +883,7 @@ async function runUpdateSkills() {
|
|
|
831
883
|
console.log(`[apm] \u5DF2\u79FB\u9664\u5DF2\u4E0B\u7EBF\u7684\u8865\u5145\u6280\u80FD: skills/${name}/`);
|
|
832
884
|
}
|
|
833
885
|
console.log(
|
|
834
|
-
`[apm] \
|
|
886
|
+
`[apm] \u540C\u6B65\u5B8C\u6210\uFF1A${ruleNames.length} \u4E2A\u57FA\u7840\u89C4\u5219\uFF0C${baseNames.length} \u4E2A\u57FA\u7840\u6280\u80FD\uFF0C${written.length} \u4E2A\u8865\u5145\u6280\u80FD`
|
|
835
887
|
);
|
|
836
888
|
}
|
|
837
889
|
|
|
@@ -1102,6 +1154,7 @@ async function handleInboundMessage(cfg, raw) {
|
|
|
1102
1154
|
try {
|
|
1103
1155
|
await runBranch(msg.sessionId, { cwd: msg.workdir });
|
|
1104
1156
|
await runPull(msg.sessionId, workspaceApmDir(msg.workdir));
|
|
1157
|
+
await commitWorkingTreeIfDirty(msg.workdir, "fix: apm pull");
|
|
1105
1158
|
await updateMessageStatus(cfg, messageId, "TYPING");
|
|
1106
1159
|
await runCursorAgent(cfg, {
|
|
1107
1160
|
messageId: msg.messageId,
|
|
@@ -1134,13 +1187,13 @@ async function handleInboundMessage(cfg, raw) {
|
|
|
1134
1187
|
}
|
|
1135
1188
|
}
|
|
1136
1189
|
}
|
|
1137
|
-
function startHeartbeat(ws,
|
|
1190
|
+
function startHeartbeat(ws, clientMachineId) {
|
|
1138
1191
|
const send = () => {
|
|
1139
1192
|
if (ws.readyState === WebSocket.OPEN) {
|
|
1140
1193
|
ws.send(
|
|
1141
1194
|
serializeAgentWsMessage({
|
|
1142
1195
|
type: "heartbeat",
|
|
1143
|
-
userId
|
|
1196
|
+
userId: clientMachineId
|
|
1144
1197
|
})
|
|
1145
1198
|
);
|
|
1146
1199
|
}
|
|
@@ -1154,8 +1207,9 @@ async function runConnect(options) {
|
|
|
1154
1207
|
if (options.server?.trim()) {
|
|
1155
1208
|
cfg.baseUrl = options.server.trim().replace(/\/+$/, "");
|
|
1156
1209
|
}
|
|
1157
|
-
|
|
1158
|
-
|
|
1210
|
+
const clientMachineId = resolveClientMachineId(cfg);
|
|
1211
|
+
if (!clientMachineId) {
|
|
1212
|
+
console.error("[apm] config \u7F3A\u5C11 clientMachineId\uFF0C\u8BF7\u91CD\u65B0 apm login");
|
|
1159
1213
|
process.exit(1);
|
|
1160
1214
|
}
|
|
1161
1215
|
const url = buildAgentWsUrl(cfg.baseUrl, cfg.token);
|
|
@@ -1178,7 +1232,7 @@ async function runConnect(options) {
|
|
|
1178
1232
|
};
|
|
1179
1233
|
ws.on("open", () => {
|
|
1180
1234
|
console.log("[apm] WebSocket \u5DF2\u8FDE\u63A5");
|
|
1181
|
-
stopHeartbeat = startHeartbeat(ws,
|
|
1235
|
+
stopHeartbeat = startHeartbeat(ws, clientMachineId);
|
|
1182
1236
|
});
|
|
1183
1237
|
ws.on("message", (data) => {
|
|
1184
1238
|
const text = Buffer.isBuffer(data) ? data.toString("utf8") : String(data);
|
|
@@ -1862,40 +1916,6 @@ function registerDeployBackendCommands(program) {
|
|
|
1862
1916
|
import { copyFile, readdir as readdir2, stat } from "node:fs/promises";
|
|
1863
1917
|
import path7 from "node:path";
|
|
1864
1918
|
|
|
1865
|
-
// src/commands/deploy/internal/load-apm-dotenv.ts
|
|
1866
|
-
import { existsSync as existsSync10, readFileSync as readFileSync9 } from "node:fs";
|
|
1867
|
-
import { join as join9 } from "node:path";
|
|
1868
|
-
function loadApmDotEnvIfPresent() {
|
|
1869
|
-
const p = join9(workspaceApmDir(), ".env");
|
|
1870
|
-
if (!existsSync10(p)) {
|
|
1871
|
-
return;
|
|
1872
|
-
}
|
|
1873
|
-
let text;
|
|
1874
|
-
try {
|
|
1875
|
-
text = readFileSync9(p, "utf8");
|
|
1876
|
-
} catch {
|
|
1877
|
-
return;
|
|
1878
|
-
}
|
|
1879
|
-
for (const line of text.split("\n")) {
|
|
1880
|
-
const t = line.trim();
|
|
1881
|
-
if (!t || t.startsWith("#")) {
|
|
1882
|
-
continue;
|
|
1883
|
-
}
|
|
1884
|
-
const eq = t.indexOf("=");
|
|
1885
|
-
if (eq <= 0) {
|
|
1886
|
-
continue;
|
|
1887
|
-
}
|
|
1888
|
-
const key = t.slice(0, eq).trim();
|
|
1889
|
-
let val = t.slice(eq + 1).trim();
|
|
1890
|
-
if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
|
|
1891
|
-
val = val.slice(1, -1);
|
|
1892
|
-
}
|
|
1893
|
-
if (process.env[key] === void 0) {
|
|
1894
|
-
process.env[key] = val;
|
|
1895
|
-
}
|
|
1896
|
-
}
|
|
1897
|
-
}
|
|
1898
|
-
|
|
1899
1919
|
// src/commands/deploy/internal/minio.ts
|
|
1900
1920
|
import { statSync as statSync5 } from "node:fs";
|
|
1901
1921
|
import { readdir, readFile } from "node:fs/promises";
|
|
@@ -2059,31 +2079,6 @@ function artifactObjectKey(namePrefix, branchSegment, relativePath) {
|
|
|
2059
2079
|
const rel = sanitizeRelativePath(relativePath);
|
|
2060
2080
|
return `${base}/${branchSegment}/dist/${rel}`;
|
|
2061
2081
|
}
|
|
2062
|
-
function mergeMinioFromEnv(settings) {
|
|
2063
|
-
const ep = process.env.MINIO_ENDPOINT?.trim();
|
|
2064
|
-
const portRaw = process.env.MINIO_PORT?.trim();
|
|
2065
|
-
const sslRaw = process.env.MINIO_USE_SSL?.trim().toLowerCase();
|
|
2066
|
-
const ak = process.env.MINIO_ACCESS_KEY?.trim();
|
|
2067
|
-
const sk = process.env.MINIO_SECRET_KEY?.trim();
|
|
2068
|
-
const bucket = process.env.MINIO_BUCKET?.trim();
|
|
2069
|
-
const port = portRaw ? Number.parseInt(portRaw, 10) : void 0;
|
|
2070
|
-
let useSsl = settings.useSsl;
|
|
2071
|
-
if (sslRaw === "true" || sslRaw === "1") {
|
|
2072
|
-
useSsl = true;
|
|
2073
|
-
}
|
|
2074
|
-
if (sslRaw === "false" || sslRaw === "0") {
|
|
2075
|
-
useSsl = false;
|
|
2076
|
-
}
|
|
2077
|
-
return {
|
|
2078
|
-
...settings,
|
|
2079
|
-
endpoint: ep || settings.endpoint,
|
|
2080
|
-
port: port !== void 0 && Number.isFinite(port) && port > 0 ? port : settings.port,
|
|
2081
|
-
useSsl,
|
|
2082
|
-
accessKey: ak || settings.accessKey,
|
|
2083
|
-
secretKey: sk || settings.secretKey,
|
|
2084
|
-
bucket: bucket || settings.bucket
|
|
2085
|
-
};
|
|
2086
|
-
}
|
|
2087
2082
|
async function ensureArtifactRootIndexHtml(root) {
|
|
2088
2083
|
const indexHtmlPath = path7.join(root, "index.html");
|
|
2089
2084
|
try {
|
|
@@ -2124,7 +2119,7 @@ async function ensureArtifactRootIndexHtml(root) {
|
|
|
2124
2119
|
}
|
|
2125
2120
|
function registerDeployFrontendCommands(program) {
|
|
2126
2121
|
program.command("deploy-frontend").description(
|
|
2127
|
-
"\u9012\u5F52\u4E0A\u4F20\u524D\u7AEF\u4EA7\u7269\u76EE\u5F55\u5230 MinIO\uFF0C\u5E76\u5728\u6210\u529F\u540E\u4E3A\u8BE5\u6876\u8BBE\u7F6E\u533F\u540D\u53EF\u8BFB\u7B56\u7565\
|
|
2122
|
+
"\u9012\u5F52\u4E0A\u4F20\u524D\u7AEF\u4EA7\u7269\u76EE\u5F55\u5230 MinIO\uFF0C\u5E76\u5728\u6210\u529F\u540E\u4E3A\u8BE5\u6876\u8BBE\u7F6E\u533F\u540D\u53EF\u8BFB\u7B56\u7565\uFF08MinIO \u8FDE\u63A5\u4FE1\u606F\u4EC5\u6765\u81EA apm.config.json frontendDeploy\uFF09"
|
|
2128
2123
|
).argument("[name]", "\u73AF\u5883\u540D", "online").option(
|
|
2129
2124
|
"--dir <path>",
|
|
2130
2125
|
"\u4EA7\u7269\u76EE\u5F55\uFF1B\u5355\u4ED3\u9ED8\u8BA4 apps/web/dist\uFF08\u9700\u5148 rush build / vite build\uFF09",
|
|
@@ -2138,12 +2133,9 @@ function registerDeployFrontendCommands(program) {
|
|
|
2138
2133
|
(v) => Number.parseInt(String(v), 10)
|
|
2139
2134
|
).action(
|
|
2140
2135
|
async (name, opts) => {
|
|
2141
|
-
loadApmDotEnvIfPresent();
|
|
2142
2136
|
const cfg = loadApmConfig({ configPath: opts.config });
|
|
2143
2137
|
const namePrefix = resolveArtifactNamePrefix(cfg);
|
|
2144
|
-
const settings =
|
|
2145
|
-
resolveFrontendDeployFromApmConfig(cfg)
|
|
2146
|
-
);
|
|
2138
|
+
const settings = resolveFrontendDeployFromApmConfig(cfg);
|
|
2147
2139
|
const minio = new MinioClient({
|
|
2148
2140
|
endPoint: settings.endpoint,
|
|
2149
2141
|
port: settings.port,
|
|
@@ -2249,16 +2241,16 @@ function buildProgram() {
|
|
|
2249
2241
|
await runUpdate();
|
|
2250
2242
|
});
|
|
2251
2243
|
program.command("update-skills").description(
|
|
2252
|
-
"\u540C\u6B65\u6280\u80FD\
|
|
2244
|
+
"\u540C\u6B65 .apm/ \u4E0B\u7684\u89C4\u5219\u4E0E\u6280\u80FD\uFF1A\u57FA\u7840\u5185\u5BB9\u6765\u81EA CLI \u6A21\u677F\uFF0C\u8865\u5145\u6280\u80FD\u6765\u81EA\u5E73\u53F0"
|
|
2253
2245
|
).action(async () => {
|
|
2254
2246
|
await runUpdateSkills();
|
|
2255
2247
|
});
|
|
2256
2248
|
program.command("pull").description(
|
|
2257
|
-
"\u62C9\u53D6\u6C9F\u901A\u7FA4\u6570\u636E\u5230 .apm/sessions/<sessionId>/\uFF08session.yaml\u3001RULE.md\u3001docs\u3001attachments\uFF09"
|
|
2249
|
+
"\u62C9\u53D6\u6C9F\u901A\u7FA4\u6570\u636E\u5230 .apm/sessions/<sessionId>/\uFF08session.yaml\u3001RULE.md\u3001TASK.md\u3001LOG.md\u3001docs\u3001attachments\uFF09"
|
|
2258
2250
|
).argument("<sessionId>", "\u6C9F\u901A\u7FA4 ID").action(async (sessionId) => {
|
|
2259
2251
|
await runPull(sessionId);
|
|
2260
2252
|
});
|
|
2261
|
-
program.command("sync-document").description("\u5C06\u672C\u5730 Markdown \u8986\u76D6\u5F0F upsert \u5230\u5E73\u53F0\
|
|
2253
|
+
program.command("sync-document").description("\u5C06\u672C\u5730 Markdown \u8986\u76D6\u5F0F upsert \u5230\u5E73\u53F0\u4EFB\u52A1\u6587\u6863").argument("<sessionId>", "\u6C9F\u901A\u7FA4 ID").requiredOption(
|
|
2262
2254
|
"--file <name>",
|
|
2263
2255
|
"\u6587\u6863\u540D\u79F0\uFF08\u5982 PRD \u6216 PRD.md\uFF09\uFF0C\u8BFB\u53D6 .apm/sessions/<sessionId>/docs/ \u4E0B\u5BF9\u5E94\u6587\u4EF6"
|
|
2264
2256
|
).action(async (sessionId, opts) => {
|
package/package.json
CHANGED
package/template/AGENTS.md
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
## APM 指南
|
|
2
2
|
|
|
3
|
-
### 回复消息命令
|
|
4
|
-
|
|
5
|
-
- 命令: `apm append-message --id=<消息ID> --content=<消息内容>`
|
|
6
|
-
- 注意事项:
|
|
7
|
-
- 如果消息内容中包含文档地址,不要把前缀也写出来,直接用文档名称即可,并且文档名称要用倒引号包裹
|
|
8
|
-
- 这个是补充消息内容,只要消息没有结束,都可以用这个命令来继续补充发言。
|
|
9
|
-
|
|
10
3
|
### 创建/更新群文档命令
|
|
11
4
|
|
|
12
5
|
- 命令: `apm sync-document <会话ID> --file <文档名称>`
|
|
@@ -17,13 +10,19 @@
|
|
|
17
10
|
1. 读取 `.apm/sessions/<会话ID>/session.yaml`,必要时读取`messages.xml` 了解当前会话状态与历史消息
|
|
18
11
|
2. 根据你的名字从 session.yaml 中找到你对应的人设(system_persona)
|
|
19
12
|
3. 根据人设完成用户指定的任务
|
|
20
|
-
4.
|
|
21
|
-
5.
|
|
13
|
+
4. 任务有阶段行进展或者任务完成后必须回复消息(具体见规则 `reply.md`)
|
|
14
|
+
5. 写工作日志(具体见规则 `write_doc.md`)
|
|
22
15
|
|
|
23
16
|
## 目录声明
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
17
|
+
**除了技能和规则**,后面的所有文件都在 `.apm/sessions/<会话ID>` 下,
|
|
18
|
+
|
|
19
|
+
+ 技能: `.apm/skills/<技能名称>/SKILL.md`,一些常用的工作方法,根据你当前的角色按需阅读
|
|
20
|
+
+ 规则: `.apm/rules/*`
|
|
21
|
+
- reply.md:当需要回复消息时需要读取这个文档,记住回复规则
|
|
22
|
+
- write_doc.md:当需要写文档时需要读取这个文档,记住写文档的规则
|
|
23
|
+
+ 本轮会话状态: `session.yaml`,从这里可以看到每个成员的信息,找到可以协助你一起解决问题的人,可以结合 `RULE.md`一起看
|
|
24
|
+
+ 群文档: `docs/xxxx.md`,当需要相关上下文可以在这里查找,按需阅读
|
|
25
|
+
+ 协作规则: `RULE.md`,在这里可以看到不同成员的协作规则,从而找其他成员协助你一起解决问题,按需阅读
|
|
26
|
+
+ 团队日志: `LOG.md`,在这里可以看到之前每个成员都做了什么,按需阅读
|
|
27
|
+
+ 历史消息记录: `message.xml`,历史消息列表,数据量很大,按需阅读
|
|
28
|
+
+ 初始目标: `TASK.md`,最初的目标,不一定具体,团队成员会有人负责让这个任务变得具体,仅供参考。如果你是相关的角色,则你需要先读取这个目标,然后规划接下来的动作。
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
## 群内回复规则
|
|
2
|
+
|
|
3
|
+
1. 如果回复的内容包含文档地址,不要把前缀也写出来,直接用文档名称即可。例如: 我已经把待处理的内容更新到了 `PRD.md` 中,请查阅。
|
|
4
|
+
2. 回复的内容并非对你工作的总结,要尽可能简洁,不要长篇大论,如果同步的内容确实较多,你可以先写文档,然后指引用户去阅读你写的文档。
|
|
5
|
+
3. 只要你有任何进展都想都可以调用下面的命令进行回复,你可以一直补充内容。
|
|
6
|
+
|
|
7
|
+
## 执行回复的方法
|
|
8
|
+
|
|
9
|
+
命令: `apm append-message --id=<消息ID> --content=<你要回复的消息内容>`
|
|
10
|
+
|
|
11
|
+
示例: apm append-message --id=xxxxx --content="我已经把待处理的内容更新到了 `PRD.md`,请查阅"
|
|
12
|
+
|
|
13
|
+
## 写文档的方法
|
|
14
|
+
|
|
15
|
+
如果知道写文档的方法,可以阅读 `./write_doc.md`
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
## 写工作日志
|
|
2
|
+
|
|
3
|
+
命名方式: <你的名字>-工作日志.md
|
|
4
|
+
保存位置: .apm/sessions/<会话 ID>/docs/<文件名>
|
|
5
|
+
日志格式:见如下 markdown 中间的内容
|
|
6
|
+
|
|
7
|
+
```markdown
|
|
8
|
+
## <简要总结本轮干了什么>
|
|
9
|
+
|
|
10
|
+
<内容>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## 写其他文件
|
|
14
|
+
|
|
15
|
+
命名方式: <你的名字>-<主题>.md
|
|
16
|
+
保存位置: .apm/sessions/<会话 ID>/docs/<文件名>
|
|
17
|
+
文档内容格式: 根据你的主题来,不限制,禁止记流水账。
|
|
18
|
+
|
|
19
|
+
## 在保存完成之后需要同步到远程
|
|
20
|
+
|
|
21
|
+
同步命令: `apm sync-document <会话 ID> --file=<文档名称>
|
|
22
|
+
|
|
23
|
+
示例: `apm sync-document <会话ID> --file=张三.md`
|
|
24
|
+
|
|
25
|
+
## 注意事项
|
|
26
|
+
|
|
27
|
+
1. 文档内容可以被其他团队成员看到,要有价值,禁止记流水账
|
|
28
|
+
2. 你可以根据你的名字查到自己写的文档,并且禁止更改其他人写的文档
|
|
29
|
+
3. 不要随便创建新文档,可以在已有文档的基础上添加内容
|
|
30
|
+
4. 文档不可删除,不可更新文档名称,在写入时要谨慎
|