ai-project-manage-cli 3.0.17 → 3.0.18
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 +238 -239
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -158,8 +158,8 @@ import { fileURLToPath } from "url";
|
|
|
158
158
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
159
159
|
var CLI_TEMPLATE_DIR = resolve(__dirname, "../template");
|
|
160
160
|
var WORKSPACE_APM_DIR = resolve(process.cwd(), ".apm");
|
|
161
|
-
function requirementWorkitemsDir(requirementId) {
|
|
162
|
-
return join2(
|
|
161
|
+
function requirementWorkitemsDir(requirementId, workspaceDir = WORKSPACE_APM_DIR) {
|
|
162
|
+
return join2(workspaceDir, "workitems", requirementId);
|
|
163
163
|
}
|
|
164
164
|
async function ensureLoggedConfig() {
|
|
165
165
|
const cfg = await ensureApmConfig();
|
|
@@ -227,7 +227,6 @@ async function runComment(requirementId, file, model) {
|
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
// src/commands/connect.ts
|
|
230
|
-
import { execSync } from "child_process";
|
|
231
230
|
import { randomUUID } from "crypto";
|
|
232
231
|
import WebSocket from "ws";
|
|
233
232
|
import { Agent } from "@cursor/sdk";
|
|
@@ -329,6 +328,228 @@ var EventSession = class {
|
|
|
329
328
|
}
|
|
330
329
|
};
|
|
331
330
|
|
|
331
|
+
// src/commands/upload-artifact.ts
|
|
332
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
333
|
+
import { join as join3, relative, sep } from "path";
|
|
334
|
+
var EXCLUDED_RELATIVE_PATHS = /* @__PURE__ */ new Set([
|
|
335
|
+
"defect.xml",
|
|
336
|
+
"prd.md",
|
|
337
|
+
"requirement-status.yaml",
|
|
338
|
+
"reviews.xml",
|
|
339
|
+
"testcase.xml"
|
|
340
|
+
]);
|
|
341
|
+
function toPosixRelative(root, absoluteFile) {
|
|
342
|
+
return relative(root, absoluteFile).split(sep).join("/");
|
|
343
|
+
}
|
|
344
|
+
function artifactTagFromRelPath(relPosix) {
|
|
345
|
+
const parts = relPosix.split("/");
|
|
346
|
+
const fileName = parts[parts.length - 1] ?? relPosix;
|
|
347
|
+
if (parts.length > 1) {
|
|
348
|
+
return parts[0] ?? fileName;
|
|
349
|
+
}
|
|
350
|
+
const dot = fileName.lastIndexOf(".");
|
|
351
|
+
return dot > 0 ? fileName.slice(0, dot) : fileName;
|
|
352
|
+
}
|
|
353
|
+
function* walkMarkdownFiles(dir) {
|
|
354
|
+
const names = readdirSync2(dir);
|
|
355
|
+
for (const name of names) {
|
|
356
|
+
if (name.startsWith(".")) continue;
|
|
357
|
+
const full = join3(dir, name);
|
|
358
|
+
const st = statSync2(full);
|
|
359
|
+
if (st.isDirectory()) {
|
|
360
|
+
yield* walkMarkdownFiles(full);
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
if (!st.isFile()) continue;
|
|
364
|
+
if (!name.toLowerCase().endsWith(".md")) continue;
|
|
365
|
+
yield full;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
async function deleteAllArtifactsForRequirement(api, requirementId) {
|
|
369
|
+
const pageSize = 100;
|
|
370
|
+
let page = 1;
|
|
371
|
+
const rows = [];
|
|
372
|
+
while (true) {
|
|
373
|
+
const batch = await api.requirementArtifact.list({
|
|
374
|
+
requirementId,
|
|
375
|
+
page,
|
|
376
|
+
pageSize
|
|
377
|
+
});
|
|
378
|
+
rows.push(...batch.items);
|
|
379
|
+
if (batch.total === 0 || rows.length >= batch.total) break;
|
|
380
|
+
page += 1;
|
|
381
|
+
}
|
|
382
|
+
for (const row of rows) {
|
|
383
|
+
await api.requirementArtifact.delete({ artifactId: row.id });
|
|
384
|
+
}
|
|
385
|
+
return rows.length;
|
|
386
|
+
}
|
|
387
|
+
async function runUploadArtifact(requirementId, workspaceDir) {
|
|
388
|
+
const cfg = await ensureLoggedConfig();
|
|
389
|
+
const api = createApmApiClient(cfg);
|
|
390
|
+
const root = requirementWorkitemsDir(requirementId, workspaceDir);
|
|
391
|
+
if (!existsSync2(root)) {
|
|
392
|
+
console.error(
|
|
393
|
+
`[apm] \u76EE\u5F55\u4E0D\u5B58\u5728: ${root}
|
|
394
|
+
\u8BF7\u5148\u6267\u884C: apm pull ${requirementId}`
|
|
395
|
+
);
|
|
396
|
+
process.exit(1);
|
|
397
|
+
}
|
|
398
|
+
const deleted = await deleteAllArtifactsForRequirement(api, requirementId);
|
|
399
|
+
console.log(`[apm] \u5DF2\u6E05\u7A7A\u9700\u6C42\u4EA7\u7269\u6587\u6863 ${deleted} \u6761`);
|
|
400
|
+
const paths = [...walkMarkdownFiles(root)];
|
|
401
|
+
let created = 0;
|
|
402
|
+
let skipped = 0;
|
|
403
|
+
for (const abs of paths) {
|
|
404
|
+
const relPosix = toPosixRelative(root, abs);
|
|
405
|
+
if (EXCLUDED_RELATIVE_PATHS.has(relPosix)) {
|
|
406
|
+
skipped += 1;
|
|
407
|
+
console.log(`[apm] \u8DF3\u8FC7\uFF08\u6392\u9664\u5217\u8868\uFF09: ${relPosix}`);
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
const content = readFileSync3(abs, "utf8");
|
|
411
|
+
const tag = artifactTagFromRelPath(relPosix);
|
|
412
|
+
await api.requirementArtifact.create({
|
|
413
|
+
requirementId,
|
|
414
|
+
tag,
|
|
415
|
+
fileName: relPosix,
|
|
416
|
+
content
|
|
417
|
+
});
|
|
418
|
+
created += 1;
|
|
419
|
+
console.log(`[apm] \u5DF2\u4E0A\u4F20\u4EA7\u7269: ${relPosix} (tag=${tag})`);
|
|
420
|
+
}
|
|
421
|
+
console.log(
|
|
422
|
+
`[apm] \u5B8C\u6210\uFF1A\u5220\u9664 ${deleted}\uFF0C\u65B0\u5EFA ${created}\uFF0C\u8DF3\u8FC7\uFF08\u6392\u9664\uFF09 ${skipped}\uFF0C\u5171\u626B\u63CF ${paths.length} \u4E2A Markdown \u6587\u4EF6`
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// src/commands/pull.ts
|
|
427
|
+
import { writeFileSync as writeFileSync2 } from "fs";
|
|
428
|
+
import { join as join4 } from "path";
|
|
429
|
+
import { stringify as yamlStringify } from "yaml";
|
|
430
|
+
function valueToXmlContent(value) {
|
|
431
|
+
if (value === null || value === void 0) return "";
|
|
432
|
+
if (typeof value === "string") return xmlEscape(value);
|
|
433
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
434
|
+
return xmlEscape(String(value));
|
|
435
|
+
}
|
|
436
|
+
return `<![CDATA[${JSON.stringify(value)}]]>`;
|
|
437
|
+
}
|
|
438
|
+
function recordToXmlLines(record, indent) {
|
|
439
|
+
const lines = [];
|
|
440
|
+
for (const [rawKey, val] of Object.entries(record)) {
|
|
441
|
+
const key = /^[a-zA-Z_][\w.-]*$/.test(rawKey) ? rawKey : "field";
|
|
442
|
+
lines.push(`${indent}<${key}>${valueToXmlContent(val)}</${key}>`);
|
|
443
|
+
}
|
|
444
|
+
return lines;
|
|
445
|
+
}
|
|
446
|
+
function unknownArrayToXml(rootName, itemName, items) {
|
|
447
|
+
const lines = [`<${rootName}>`];
|
|
448
|
+
items.forEach((item, index) => {
|
|
449
|
+
if (item !== null && typeof item === "object" && !Array.isArray(item)) {
|
|
450
|
+
lines.push(` <${itemName} index="${index}">`);
|
|
451
|
+
lines.push(...recordToXmlLines(item, " "));
|
|
452
|
+
lines.push(` </${itemName}>`);
|
|
453
|
+
} else {
|
|
454
|
+
lines.push(
|
|
455
|
+
` <${itemName} index="${index}">${valueToXmlContent(
|
|
456
|
+
item
|
|
457
|
+
)}</${itemName}>`
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
lines.push(`</${rootName}>`, "");
|
|
462
|
+
return lines.join("\n");
|
|
463
|
+
}
|
|
464
|
+
function tasksForStatusYaml(tasks) {
|
|
465
|
+
return tasks.map((t) => ({
|
|
466
|
+
id: t.id,
|
|
467
|
+
title: t.title,
|
|
468
|
+
status: t.status,
|
|
469
|
+
executor: t.executorType
|
|
470
|
+
}));
|
|
471
|
+
}
|
|
472
|
+
function escapeForCdata(text) {
|
|
473
|
+
return text.replace(/\]\]>/g, "]]]]><![CDATA[>");
|
|
474
|
+
}
|
|
475
|
+
function defectsToXml(defects) {
|
|
476
|
+
const sorted = [...defects].sort(
|
|
477
|
+
(a, b) => new Date(a.createdAt ?? 0).getTime() - new Date(b.createdAt ?? 0).getTime()
|
|
478
|
+
);
|
|
479
|
+
const lines = ["<defects>"];
|
|
480
|
+
for (const d of sorted) {
|
|
481
|
+
lines.push(` <defect id="${xmlEscape(d.id)}">`);
|
|
482
|
+
lines.push(` <status>${xmlEscape(d.status)}</status>`);
|
|
483
|
+
lines.push(
|
|
484
|
+
` <current><![CDATA[${escapeForCdata(
|
|
485
|
+
d.currentState ?? ""
|
|
486
|
+
)}]]></current>`
|
|
487
|
+
);
|
|
488
|
+
lines.push(
|
|
489
|
+
` <expected><![CDATA[${escapeForCdata(
|
|
490
|
+
d.expectedEffect ?? ""
|
|
491
|
+
)}]]></expected>`
|
|
492
|
+
);
|
|
493
|
+
lines.push(` </defect>`);
|
|
494
|
+
}
|
|
495
|
+
lines.push("</defects>", "");
|
|
496
|
+
return lines.join("\n");
|
|
497
|
+
}
|
|
498
|
+
async function runPull(requirementId, workspaceDir) {
|
|
499
|
+
const cfg = await ensureLoggedConfig();
|
|
500
|
+
const api = createApmApiClient(cfg);
|
|
501
|
+
const data = await api.cliRequirements.pull({ requirementId });
|
|
502
|
+
const WORKITEMS_DIR = requirementWorkitemsDir(requirementId, workspaceDir);
|
|
503
|
+
await ensureDirExists(WORKITEMS_DIR);
|
|
504
|
+
const req2 = data.requirement;
|
|
505
|
+
const statusYaml = yamlStringify(
|
|
506
|
+
{
|
|
507
|
+
id: req2.id,
|
|
508
|
+
status: req2.status,
|
|
509
|
+
title: req2.title,
|
|
510
|
+
env: req2.envName || "",
|
|
511
|
+
tasks: tasksForStatusYaml(data.tasks ?? [])
|
|
512
|
+
},
|
|
513
|
+
{ lineWidth: 0 }
|
|
514
|
+
);
|
|
515
|
+
writeFileSync2(
|
|
516
|
+
join4(WORKITEMS_DIR, "requirement-status.yaml"),
|
|
517
|
+
statusYaml.endsWith("\n") ? statusYaml : `${statusYaml}
|
|
518
|
+
`,
|
|
519
|
+
"utf8"
|
|
520
|
+
);
|
|
521
|
+
writeFileSync2(join4(WORKITEMS_DIR, "prd.md"), req2.content || "", "utf8");
|
|
522
|
+
const reviews = data.reviews ?? [];
|
|
523
|
+
const reviewsXml = [
|
|
524
|
+
"<reviews>",
|
|
525
|
+
...reviews.map((r) => {
|
|
526
|
+
return [
|
|
527
|
+
` <review id="${xmlEscape(r.id)}">`,
|
|
528
|
+
` <model>${xmlEscape(r.model ?? "")}</model>`,
|
|
529
|
+
` <content>`,
|
|
530
|
+
`${xmlEscape(r.content ?? "")}`,
|
|
531
|
+
` </content>`,
|
|
532
|
+
` <reply>`,
|
|
533
|
+
`${xmlEscape(r.reply ?? "")}`,
|
|
534
|
+
` </reply>`,
|
|
535
|
+
" </review>"
|
|
536
|
+
].join("\n");
|
|
537
|
+
}),
|
|
538
|
+
"</reviews>",
|
|
539
|
+
""
|
|
540
|
+
].join("\n");
|
|
541
|
+
writeFileSync2(join4(WORKITEMS_DIR, "reviews.xml"), reviewsXml, "utf8");
|
|
542
|
+
const defectsXml = defectsToXml(data.defects ?? []);
|
|
543
|
+
writeFileSync2(join4(WORKITEMS_DIR, "defect.xml"), defectsXml, "utf8");
|
|
544
|
+
const testCasesXml = unknownArrayToXml(
|
|
545
|
+
"testcases",
|
|
546
|
+
"case",
|
|
547
|
+
data.testCases ?? []
|
|
548
|
+
);
|
|
549
|
+
writeFileSync2(join4(WORKITEMS_DIR, "testcase.xml"), testCasesXml, "utf8");
|
|
550
|
+
return WORKITEMS_DIR;
|
|
551
|
+
}
|
|
552
|
+
|
|
332
553
|
// src/commands/connect.ts
|
|
333
554
|
function runConnect(opts) {
|
|
334
555
|
void (async () => {
|
|
@@ -373,10 +594,7 @@ function runConnect(opts) {
|
|
|
373
594
|
}
|
|
374
595
|
const payload = msg.payload;
|
|
375
596
|
try {
|
|
376
|
-
|
|
377
|
-
cwd: payload.cwd,
|
|
378
|
-
encoding: "utf8"
|
|
379
|
-
});
|
|
597
|
+
await runPull(payload.requirementId, payload.cwd);
|
|
380
598
|
} catch (pullErr) {
|
|
381
599
|
console.error("[apm] apm pull \u5931\u8D25:", pullErr);
|
|
382
600
|
throw pullErr;
|
|
@@ -455,10 +673,7 @@ function runConnect(opts) {
|
|
|
455
673
|
console.log("[Done]");
|
|
456
674
|
session.writeToFile(payload.cwd, payload.requirementId, run.agentId);
|
|
457
675
|
try {
|
|
458
|
-
|
|
459
|
-
cwd: payload.cwd,
|
|
460
|
-
encoding: "utf8"
|
|
461
|
-
});
|
|
676
|
+
await runUploadArtifact(payload.requirementId, payload.cwd);
|
|
462
677
|
} catch (pullErr) {
|
|
463
678
|
console.error("[apm] apm upload-artifact \u5931\u8D25:", pullErr);
|
|
464
679
|
throw pullErr;
|
|
@@ -478,17 +693,17 @@ function runConnect(opts) {
|
|
|
478
693
|
}
|
|
479
694
|
|
|
480
695
|
// src/commands/init.ts
|
|
481
|
-
import { join as
|
|
482
|
-
import { readFileSync as
|
|
696
|
+
import { join as join5 } from "path";
|
|
697
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
483
698
|
async function runInit(name) {
|
|
484
699
|
await ensureWorkspaceApmDirForInit();
|
|
485
700
|
await copyTemplateFiles(WORKSPACE_APM_DIR);
|
|
486
701
|
if (name) {
|
|
487
|
-
const apmConfigPath =
|
|
488
|
-
const config =
|
|
702
|
+
const apmConfigPath = join5(WORKSPACE_APM_DIR, "apm.config.json");
|
|
703
|
+
const config = readFileSync4(apmConfigPath, "utf8");
|
|
489
704
|
const configJson = JSON.parse(config);
|
|
490
705
|
configJson.name = name;
|
|
491
|
-
|
|
706
|
+
writeFileSync3(apmConfigPath, JSON.stringify(configJson, null, 2), "utf8");
|
|
492
707
|
}
|
|
493
708
|
}
|
|
494
709
|
|
|
@@ -675,134 +890,13 @@ async function runBranch(requirementId, options = {}) {
|
|
|
675
890
|
return branch;
|
|
676
891
|
}
|
|
677
892
|
|
|
678
|
-
// src/commands/pull.ts
|
|
679
|
-
import { writeFileSync as writeFileSync3 } from "fs";
|
|
680
|
-
import { join as join4 } from "path";
|
|
681
|
-
import { stringify as yamlStringify } from "yaml";
|
|
682
|
-
function valueToXmlContent(value) {
|
|
683
|
-
if (value === null || value === void 0) return "";
|
|
684
|
-
if (typeof value === "string") return xmlEscape(value);
|
|
685
|
-
if (typeof value === "number" || typeof value === "boolean") {
|
|
686
|
-
return xmlEscape(String(value));
|
|
687
|
-
}
|
|
688
|
-
return `<![CDATA[${JSON.stringify(value)}]]>`;
|
|
689
|
-
}
|
|
690
|
-
function recordToXmlLines(record, indent) {
|
|
691
|
-
const lines = [];
|
|
692
|
-
for (const [rawKey, val] of Object.entries(record)) {
|
|
693
|
-
const key = /^[a-zA-Z_][\w.-]*$/.test(rawKey) ? rawKey : "field";
|
|
694
|
-
lines.push(`${indent}<${key}>${valueToXmlContent(val)}</${key}>`);
|
|
695
|
-
}
|
|
696
|
-
return lines;
|
|
697
|
-
}
|
|
698
|
-
function unknownArrayToXml(rootName, itemName, items) {
|
|
699
|
-
const lines = [`<${rootName}>`];
|
|
700
|
-
items.forEach((item, index) => {
|
|
701
|
-
if (item !== null && typeof item === "object" && !Array.isArray(item)) {
|
|
702
|
-
lines.push(` <${itemName} index="${index}">`);
|
|
703
|
-
lines.push(...recordToXmlLines(item, " "));
|
|
704
|
-
lines.push(` </${itemName}>`);
|
|
705
|
-
} else {
|
|
706
|
-
lines.push(
|
|
707
|
-
` <${itemName} index="${index}">${valueToXmlContent(
|
|
708
|
-
item
|
|
709
|
-
)}</${itemName}>`
|
|
710
|
-
);
|
|
711
|
-
}
|
|
712
|
-
});
|
|
713
|
-
lines.push(`</${rootName}>`, "");
|
|
714
|
-
return lines.join("\n");
|
|
715
|
-
}
|
|
716
|
-
function tasksForStatusYaml(tasks) {
|
|
717
|
-
return tasks.map((t) => ({
|
|
718
|
-
id: t.id,
|
|
719
|
-
title: t.title,
|
|
720
|
-
status: t.status,
|
|
721
|
-
executor: t.executorType
|
|
722
|
-
}));
|
|
723
|
-
}
|
|
724
|
-
function escapeForCdata(text) {
|
|
725
|
-
return text.replace(/\]\]>/g, "]]]]><![CDATA[>");
|
|
726
|
-
}
|
|
727
|
-
function defectsToXml(defects) {
|
|
728
|
-
const sorted = [...defects].sort(
|
|
729
|
-
(a, b) => new Date(a.createdAt ?? 0).getTime() - new Date(b.createdAt ?? 0).getTime()
|
|
730
|
-
);
|
|
731
|
-
const lines = ["<defects>"];
|
|
732
|
-
for (const d of sorted) {
|
|
733
|
-
lines.push(` <defect id="${xmlEscape(d.id)}">`);
|
|
734
|
-
lines.push(` <status>${xmlEscape(d.status)}</status>`);
|
|
735
|
-
lines.push(` <current><![CDATA[${escapeForCdata(d.currentState ?? "")}]]></current>`);
|
|
736
|
-
lines.push(
|
|
737
|
-
` <expected><![CDATA[${escapeForCdata(d.expectedEffect ?? "")}]]></expected>`
|
|
738
|
-
);
|
|
739
|
-
lines.push(` </defect>`);
|
|
740
|
-
}
|
|
741
|
-
lines.push("</defects>", "");
|
|
742
|
-
return lines.join("\n");
|
|
743
|
-
}
|
|
744
|
-
async function runPull(requirementId) {
|
|
745
|
-
const cfg = await ensureLoggedConfig();
|
|
746
|
-
const api = createApmApiClient(cfg);
|
|
747
|
-
const data = await api.cliRequirements.pull({ requirementId });
|
|
748
|
-
const WORKITEMS_DIR = requirementWorkitemsDir(requirementId);
|
|
749
|
-
await ensureDirExists(WORKITEMS_DIR);
|
|
750
|
-
const req2 = data.requirement;
|
|
751
|
-
const statusYaml = yamlStringify(
|
|
752
|
-
{
|
|
753
|
-
id: req2.id,
|
|
754
|
-
status: req2.status,
|
|
755
|
-
title: req2.title,
|
|
756
|
-
env: req2.envName || "",
|
|
757
|
-
tasks: tasksForStatusYaml(data.tasks ?? [])
|
|
758
|
-
},
|
|
759
|
-
{ lineWidth: 0 }
|
|
760
|
-
);
|
|
761
|
-
writeFileSync3(
|
|
762
|
-
join4(WORKITEMS_DIR, "requirement-status.yaml"),
|
|
763
|
-
statusYaml.endsWith("\n") ? statusYaml : `${statusYaml}
|
|
764
|
-
`,
|
|
765
|
-
"utf8"
|
|
766
|
-
);
|
|
767
|
-
writeFileSync3(join4(WORKITEMS_DIR, "prd.md"), req2.content || "", "utf8");
|
|
768
|
-
const reviews = data.reviews ?? [];
|
|
769
|
-
const reviewsXml = [
|
|
770
|
-
"<reviews>",
|
|
771
|
-
...reviews.map((r) => {
|
|
772
|
-
return [
|
|
773
|
-
` <review id="${xmlEscape(r.id)}">`,
|
|
774
|
-
` <model>${xmlEscape(r.model ?? "")}</model>`,
|
|
775
|
-
` <content>`,
|
|
776
|
-
`${xmlEscape(r.content ?? "")}`,
|
|
777
|
-
` </content>`,
|
|
778
|
-
` <reply>`,
|
|
779
|
-
`${xmlEscape(r.reply ?? "")}`,
|
|
780
|
-
` </reply>`,
|
|
781
|
-
" </review>"
|
|
782
|
-
].join("\n");
|
|
783
|
-
}),
|
|
784
|
-
"</reviews>",
|
|
785
|
-
""
|
|
786
|
-
].join("\n");
|
|
787
|
-
writeFileSync3(join4(WORKITEMS_DIR, "reviews.xml"), reviewsXml, "utf8");
|
|
788
|
-
const defectsXml = defectsToXml(data.defects ?? []);
|
|
789
|
-
writeFileSync3(join4(WORKITEMS_DIR, "defect.xml"), defectsXml, "utf8");
|
|
790
|
-
const testCasesXml = unknownArrayToXml(
|
|
791
|
-
"testcases",
|
|
792
|
-
"case",
|
|
793
|
-
data.testCases ?? []
|
|
794
|
-
);
|
|
795
|
-
writeFileSync3(join4(WORKITEMS_DIR, "testcase.xml"), testCasesXml, "utf8");
|
|
796
|
-
return WORKITEMS_DIR;
|
|
797
|
-
}
|
|
798
|
-
|
|
799
893
|
// src/commands/refine.ts
|
|
800
|
-
import { readFileSync as
|
|
801
|
-
import { join as
|
|
894
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
895
|
+
import { join as join6 } from "path";
|
|
802
896
|
async function runRefine(requirementId) {
|
|
803
897
|
const cfg = await ensureLoggedConfig();
|
|
804
|
-
const filePath =
|
|
805
|
-
const content =
|
|
898
|
+
const filePath = join6(WORKSPACE_APM_DIR, "workitems", requirementId, "prd.md");
|
|
899
|
+
const content = readFileSync5(filePath, "utf8");
|
|
806
900
|
const api = createApmApiClient(cfg);
|
|
807
901
|
const data = await api.cliRequirements.refine({ requirementId, content });
|
|
808
902
|
console.log(JSON.stringify(data, null, 2));
|
|
@@ -830,101 +924,6 @@ async function runUpdateStatus(requirementId, status) {
|
|
|
830
924
|
console.log(JSON.stringify(data, null, 2));
|
|
831
925
|
}
|
|
832
926
|
|
|
833
|
-
// src/commands/upload-artifact.ts
|
|
834
|
-
import { existsSync as existsSync2, readFileSync as readFileSync5, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
835
|
-
import { join as join6, relative, sep } from "path";
|
|
836
|
-
var EXCLUDED_RELATIVE_PATHS = /* @__PURE__ */ new Set([
|
|
837
|
-
"defect.xml",
|
|
838
|
-
"prd.md",
|
|
839
|
-
"requirement-status.yaml",
|
|
840
|
-
"reviews.xml",
|
|
841
|
-
"testcase.xml"
|
|
842
|
-
]);
|
|
843
|
-
function toPosixRelative(root, absoluteFile) {
|
|
844
|
-
return relative(root, absoluteFile).split(sep).join("/");
|
|
845
|
-
}
|
|
846
|
-
function artifactTagFromRelPath(relPosix) {
|
|
847
|
-
const parts = relPosix.split("/");
|
|
848
|
-
const fileName = parts[parts.length - 1] ?? relPosix;
|
|
849
|
-
if (parts.length > 1) {
|
|
850
|
-
return parts[0] ?? fileName;
|
|
851
|
-
}
|
|
852
|
-
const dot = fileName.lastIndexOf(".");
|
|
853
|
-
return dot > 0 ? fileName.slice(0, dot) : fileName;
|
|
854
|
-
}
|
|
855
|
-
function* walkMarkdownFiles(dir) {
|
|
856
|
-
const names = readdirSync2(dir);
|
|
857
|
-
for (const name of names) {
|
|
858
|
-
if (name.startsWith(".")) continue;
|
|
859
|
-
const full = join6(dir, name);
|
|
860
|
-
const st = statSync2(full);
|
|
861
|
-
if (st.isDirectory()) {
|
|
862
|
-
yield* walkMarkdownFiles(full);
|
|
863
|
-
continue;
|
|
864
|
-
}
|
|
865
|
-
if (!st.isFile()) continue;
|
|
866
|
-
if (!name.toLowerCase().endsWith(".md")) continue;
|
|
867
|
-
yield full;
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
async function deleteAllArtifactsForRequirement(api, requirementId) {
|
|
871
|
-
const pageSize = 100;
|
|
872
|
-
let page = 1;
|
|
873
|
-
const rows = [];
|
|
874
|
-
while (true) {
|
|
875
|
-
const batch = await api.requirementArtifact.list({
|
|
876
|
-
requirementId,
|
|
877
|
-
page,
|
|
878
|
-
pageSize
|
|
879
|
-
});
|
|
880
|
-
rows.push(...batch.items);
|
|
881
|
-
if (batch.total === 0 || rows.length >= batch.total) break;
|
|
882
|
-
page += 1;
|
|
883
|
-
}
|
|
884
|
-
for (const row of rows) {
|
|
885
|
-
await api.requirementArtifact.delete({ artifactId: row.id });
|
|
886
|
-
}
|
|
887
|
-
return rows.length;
|
|
888
|
-
}
|
|
889
|
-
async function runUploadArtifact(requirementId) {
|
|
890
|
-
const cfg = await ensureLoggedConfig();
|
|
891
|
-
const api = createApmApiClient(cfg);
|
|
892
|
-
const root = requirementWorkitemsDir(requirementId);
|
|
893
|
-
if (!existsSync2(root)) {
|
|
894
|
-
console.error(
|
|
895
|
-
`[apm] \u76EE\u5F55\u4E0D\u5B58\u5728: ${root}
|
|
896
|
-
\u8BF7\u5148\u6267\u884C: apm pull ${requirementId}`
|
|
897
|
-
);
|
|
898
|
-
process.exit(1);
|
|
899
|
-
}
|
|
900
|
-
const deleted = await deleteAllArtifactsForRequirement(api, requirementId);
|
|
901
|
-
console.log(`[apm] \u5DF2\u6E05\u7A7A\u9700\u6C42\u4EA7\u7269\u6587\u6863 ${deleted} \u6761`);
|
|
902
|
-
const paths = [...walkMarkdownFiles(root)];
|
|
903
|
-
let created = 0;
|
|
904
|
-
let skipped = 0;
|
|
905
|
-
for (const abs of paths) {
|
|
906
|
-
const relPosix = toPosixRelative(root, abs);
|
|
907
|
-
if (EXCLUDED_RELATIVE_PATHS.has(relPosix)) {
|
|
908
|
-
skipped += 1;
|
|
909
|
-
console.log(`[apm] \u8DF3\u8FC7\uFF08\u6392\u9664\u5217\u8868\uFF09: ${relPosix}`);
|
|
910
|
-
continue;
|
|
911
|
-
}
|
|
912
|
-
const content = readFileSync5(abs, "utf8");
|
|
913
|
-
const tag = artifactTagFromRelPath(relPosix);
|
|
914
|
-
await api.requirementArtifact.create({
|
|
915
|
-
requirementId,
|
|
916
|
-
tag,
|
|
917
|
-
fileName: relPosix,
|
|
918
|
-
content
|
|
919
|
-
});
|
|
920
|
-
created += 1;
|
|
921
|
-
console.log(`[apm] \u5DF2\u4E0A\u4F20\u4EA7\u7269: ${relPosix} (tag=${tag})`);
|
|
922
|
-
}
|
|
923
|
-
console.log(
|
|
924
|
-
`[apm] \u5B8C\u6210\uFF1A\u5220\u9664 ${deleted}\uFF0C\u65B0\u5EFA ${created}\uFF0C\u8DF3\u8FC7\uFF08\u6392\u9664\uFF09 ${skipped}\uFF0C\u5171\u626B\u63CF ${paths.length} \u4E2A Markdown \u6587\u4EF6`
|
|
925
|
-
);
|
|
926
|
-
}
|
|
927
|
-
|
|
928
927
|
// src/commands/deploy/backend.ts
|
|
929
928
|
import path5 from "node:path";
|
|
930
929
|
|
|
@@ -1341,7 +1340,7 @@ function assertDeployImageTag(tag) {
|
|
|
1341
1340
|
import { platform } from "node:os";
|
|
1342
1341
|
|
|
1343
1342
|
// src/commands/deploy/lib/backend-deploy/command-runner.ts
|
|
1344
|
-
import { execSync
|
|
1343
|
+
import { execSync } from "child_process";
|
|
1345
1344
|
|
|
1346
1345
|
// src/commands/deploy/lib/backend-deploy/logger.ts
|
|
1347
1346
|
var Logger = class {
|
|
@@ -1367,7 +1366,7 @@ var CommandRunner = class {
|
|
|
1367
1366
|
static exec(command, cwd) {
|
|
1368
1367
|
try {
|
|
1369
1368
|
Logger.info(`\u6267\u884C\u547D\u4EE4: ${command}`);
|
|
1370
|
-
const result =
|
|
1369
|
+
const result = execSync(command, {
|
|
1371
1370
|
cwd,
|
|
1372
1371
|
encoding: "utf8",
|
|
1373
1372
|
stdio: "pipe"
|
|
@@ -1385,7 +1384,7 @@ var CommandRunner = class {
|
|
|
1385
1384
|
static execWithOutput(command, cwd) {
|
|
1386
1385
|
try {
|
|
1387
1386
|
Logger.info(`\u6267\u884C\u547D\u4EE4: ${command}`);
|
|
1388
|
-
|
|
1387
|
+
execSync(command, {
|
|
1389
1388
|
cwd,
|
|
1390
1389
|
stdio: "inherit"
|
|
1391
1390
|
});
|