@oevery/rsp 3.1.0-beta.4 → 3.1.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/README.md +45 -346
- package/README.zh-CN.md +46 -335
- package/dist/{chunk-D7XDXBFO.mjs → chunk-3Z4IO2TD.mjs} +1 -1
- package/dist/{chunk-FI3SYHVH.mjs → chunk-MUNLLVVH.mjs} +141 -6
- package/dist/{chunk-FJVH7ZYU.mjs → chunk-WJXKLLIF.mjs} +15 -7
- package/dist/cli.mjs +242 -31
- package/dist/{entry-FVUG6YW2.mjs → entry-IPHJBI53.mjs} +2 -2
- package/dist/{entry-5OPKUZHF.mjs → entry-KAWA2AS4.mjs} +2 -2
- package/package.json +10 -3
- package/rules/rsp-rules.md +4 -2
- package/skills/rsp/SKILL.md +5 -15
- package/skills/rsp/references/groups-dependencies.md +1 -0
- package/skills/rsp/references/managed-routing.md +7 -22
- package/skills/rsp/references/release-operations.md +9 -0
- package/skills/rsp/references/reopen-recovery.md +9 -0
- package/skills/rsp-commit/SKILL.md +5 -1
- package/skills/rsp-implement/SKILL.md +5 -5
- package/skills/rsp-manage/SKILL.md +10 -4
- package/skills/rsp-shape/SKILL.md +2 -0
- package/skills/rsp-shape/references/external-issue-input.md +9 -0
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
resolveLanguagePolicy,
|
|
30
30
|
resolveManagePolicy,
|
|
31
31
|
toErrorMessage
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-WJXKLLIF.mjs";
|
|
33
33
|
|
|
34
34
|
// src/status/inspect.ts
|
|
35
35
|
import { readFile as readFile5, stat } from "fs/promises";
|
|
@@ -666,6 +666,7 @@ function normalizeLogicalPath2(path) {
|
|
|
666
666
|
|
|
667
667
|
// src/core/change-group.ts
|
|
668
668
|
var GROUP_BRIEF_SECTIONS = getCanonicalSectionHeadings(GROUP_BRIEF_DOCUMENT_SCHEMA);
|
|
669
|
+
var GROUP_REOPEN_COMPLETION_PREFIX = "Resolve reopened concern from";
|
|
669
670
|
async function resolveExecutableChange(name, options = {}) {
|
|
670
671
|
const ref = resolveWorkRef(name, { ...options, executable: true });
|
|
671
672
|
if (ref.kind === "group-change")
|
|
@@ -711,6 +712,8 @@ async function inspectChangeGroups(options = {}) {
|
|
|
711
712
|
const groups = [];
|
|
712
713
|
const archivedSlicesByGroup = /* @__PURE__ */ new Map();
|
|
713
714
|
const archivedGroups = /* @__PURE__ */ new Set();
|
|
715
|
+
const archivedGroupReopenEvidence = /* @__PURE__ */ new Map();
|
|
716
|
+
const archivedGroupBriefPaths = /* @__PURE__ */ new Map();
|
|
714
717
|
const openGroupNames = new Set(workTree.briefs.map((brief) => brief.group));
|
|
715
718
|
for (const path of archiveTree.files) {
|
|
716
719
|
const archivePath = normalizeLogicalPath(relative2(ARCHIVES_DIR, path));
|
|
@@ -736,8 +739,16 @@ async function inspectChangeGroups(options = {}) {
|
|
|
736
739
|
archivedSlicesByGroup.set(archiveGroup, groupSlices);
|
|
737
740
|
}
|
|
738
741
|
}
|
|
739
|
-
if (getDocumentTitle(parseRspDocument(content, GROUP_BRIEF_DOCUMENT_SCHEMA), "identity") === archiveGroup)
|
|
742
|
+
if (getDocumentTitle(parseRspDocument(content, GROUP_BRIEF_DOCUMENT_SCHEMA), "identity") === archiveGroup) {
|
|
740
743
|
archivedGroups.add(archiveGroup);
|
|
744
|
+
const evidence = archivedGroupReopenEvidence.get(archiveGroup) ?? /* @__PURE__ */ new Set();
|
|
745
|
+
for (const item of getGroupReopenEvidence(content))
|
|
746
|
+
evidence.add(item.key);
|
|
747
|
+
archivedGroupReopenEvidence.set(archiveGroup, evidence);
|
|
748
|
+
const retainedPaths = archivedGroupBriefPaths.get(archiveGroup) ?? /* @__PURE__ */ new Set();
|
|
749
|
+
retainedPaths.add(`.rsp/archives/${archivePath}`);
|
|
750
|
+
archivedGroupBriefPaths.set(archiveGroup, retainedPaths);
|
|
751
|
+
}
|
|
741
752
|
} catch (error) {
|
|
742
753
|
diagnostics.push({
|
|
743
754
|
severity: "error",
|
|
@@ -764,7 +775,11 @@ async function inspectChangeGroups(options = {}) {
|
|
|
764
775
|
}
|
|
765
776
|
const parsed = parseGroupBrief(brief.group, content);
|
|
766
777
|
diagnostics.push(...parsed.diagnostics.map((diagnostic) => ({ ...diagnostic, path: brief.path })));
|
|
767
|
-
if (archivedGroups.has(brief.group)
|
|
778
|
+
if (archivedGroups.has(brief.group) && !hasExplicitGroupReopenEvidence(
|
|
779
|
+
content,
|
|
780
|
+
archivedGroupReopenEvidence.get(brief.group) ?? /* @__PURE__ */ new Set(),
|
|
781
|
+
archivedGroupBriefPaths.get(brief.group) ?? /* @__PURE__ */ new Set()
|
|
782
|
+
)) {
|
|
768
783
|
diagnostics.push({
|
|
769
784
|
severity: "error",
|
|
770
785
|
code: "group_identity_reopened",
|
|
@@ -855,6 +870,16 @@ async function inspectChangeGroups(options = {}) {
|
|
|
855
870
|
groups.sort((left, right) => left.name.localeCompare(right.name));
|
|
856
871
|
return { groups, diagnostics };
|
|
857
872
|
}
|
|
873
|
+
function getGroupReopenEvidence(content) {
|
|
874
|
+
const completion = getDocumentSectionBody(parseRspDocument(content, GROUP_BRIEF_DOCUMENT_SCHEMA), "completionConditions");
|
|
875
|
+
return completion.split("\n").flatMap((line) => {
|
|
876
|
+
const match = line.trim().match(/^-\s+\[([ /xX-])\]\s+(Resolve reopened concern from `(\.rsp\/archives\/[^`]+)`: \S.*)$/);
|
|
877
|
+
return match ? [{ active: match[1] !== "-", key: match[2], sourcePath: match[3] }] : [];
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
function hasExplicitGroupReopenEvidence(content, retainedEvidence, retainedGroupBriefPaths) {
|
|
881
|
+
return getGroupReopenEvidence(content).some((item) => item.active && retainedGroupBriefPaths.has(item.sourcePath) && !retainedEvidence.has(item.key));
|
|
882
|
+
}
|
|
858
883
|
async function requireDeclaredGroupSlice(ref) {
|
|
859
884
|
const briefPath = join2(dirname(ref.path), GROUP_BRIEF_FILENAME);
|
|
860
885
|
let content;
|
|
@@ -975,6 +1000,69 @@ import { readFile as readFile3 } from "fs/promises";
|
|
|
975
1000
|
import { readFile as readFile2 } from "fs/promises";
|
|
976
1001
|
import { basename, dirname as dirname2, relative as relative3 } from "path";
|
|
977
1002
|
|
|
1003
|
+
// src/core/issue-relationship.ts
|
|
1004
|
+
var ISSUE_RELATIONS = /* @__PURE__ */ new Set(["relates", "closes"]);
|
|
1005
|
+
var ISSUE_FIELDS = /* @__PURE__ */ new Set(["url", "relation"]);
|
|
1006
|
+
var IssueRelationshipError = class extends Error {
|
|
1007
|
+
constructor(code, message) {
|
|
1008
|
+
super(message);
|
|
1009
|
+
this.code = code;
|
|
1010
|
+
this.name = "IssueRelationshipError";
|
|
1011
|
+
}
|
|
1012
|
+
code;
|
|
1013
|
+
};
|
|
1014
|
+
function parseIssueRelationships(frontmatter) {
|
|
1015
|
+
if (!frontmatter || !("issues" in frontmatter))
|
|
1016
|
+
return [];
|
|
1017
|
+
if (!Array.isArray(frontmatter.issues))
|
|
1018
|
+
throw new IssueRelationshipError("invalid_issue_shape", 'frontmatter field "issues" must be a list');
|
|
1019
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1020
|
+
return frontmatter.issues.map((value, index) => {
|
|
1021
|
+
if (!isPlainObject(value))
|
|
1022
|
+
throw new IssueRelationshipError("invalid_issue_shape", `issues[${index}] must be a mapping with url and relation`);
|
|
1023
|
+
const fields = Object.keys(value);
|
|
1024
|
+
const unsupported = fields.filter((field) => !ISSUE_FIELDS.has(field));
|
|
1025
|
+
if (unsupported.length > 0)
|
|
1026
|
+
throw new IssueRelationshipError("unsupported_issue_field", `issues[${index}] contains unsupported field(s): ${unsupported.sort().join(", ")}`);
|
|
1027
|
+
if (fields.length !== 2 || !("url" in value) || !("relation" in value))
|
|
1028
|
+
throw new IssueRelationshipError("invalid_issue_shape", `issues[${index}] must contain exactly url and relation`);
|
|
1029
|
+
if (typeof value.url !== "string")
|
|
1030
|
+
throw new IssueRelationshipError("invalid_issue_url", `issues[${index}].url must be an absolute http or https URL`);
|
|
1031
|
+
if (typeof value.relation !== "string" || !ISSUE_RELATIONS.has(value.relation))
|
|
1032
|
+
throw new IssueRelationshipError("unsupported_issue_relation", `issues[${index}].relation must be relates or closes`);
|
|
1033
|
+
const url = normalizeIssueUrl(value.url, `issues[${index}].url`);
|
|
1034
|
+
if (seen.has(url))
|
|
1035
|
+
throw new IssueRelationshipError("duplicate_issue_url", `issues contains duplicate normalized URL: ${url}`);
|
|
1036
|
+
seen.add(url);
|
|
1037
|
+
return { url, relation: value.relation };
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
function createIssueRelationship(url, relation = "relates") {
|
|
1041
|
+
return parseIssueRelationships({ issues: [{ url, relation }] })[0];
|
|
1042
|
+
}
|
|
1043
|
+
function normalizeIssueUrl(raw, label) {
|
|
1044
|
+
if (raw.trim() !== raw || raw === "")
|
|
1045
|
+
throw new IssueRelationshipError("invalid_issue_url", `${label} must be an absolute http or https URL without surrounding whitespace`);
|
|
1046
|
+
let parsed;
|
|
1047
|
+
try {
|
|
1048
|
+
parsed = new URL(raw);
|
|
1049
|
+
} catch {
|
|
1050
|
+
throw new IssueRelationshipError("invalid_issue_url", `${label} must be an absolute http or https URL`);
|
|
1051
|
+
}
|
|
1052
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:")
|
|
1053
|
+
throw new IssueRelationshipError("invalid_issue_url", `${label} must use http or https`);
|
|
1054
|
+
if (!parsed.hostname)
|
|
1055
|
+
throw new IssueRelationshipError("invalid_issue_url", `${label} must include a host`);
|
|
1056
|
+
if (parsed.username || parsed.password)
|
|
1057
|
+
throw new IssueRelationshipError("unsafe_issue_url", `${label} must not contain credentials`);
|
|
1058
|
+
if (parsed.hash)
|
|
1059
|
+
throw new IssueRelationshipError("unsafe_issue_url", `${label} must not contain a fragment`);
|
|
1060
|
+
return parsed.toString();
|
|
1061
|
+
}
|
|
1062
|
+
function isPlainObject(value) {
|
|
1063
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1064
|
+
}
|
|
1065
|
+
|
|
978
1066
|
// src/history/model.ts
|
|
979
1067
|
var HISTORY_DEFAULT_LIMIT = 20;
|
|
980
1068
|
var HISTORY_MAX_LIMIT = 100;
|
|
@@ -1011,6 +1099,7 @@ async function inspectArchiveHistory(options = {}) {
|
|
|
1011
1099
|
}));
|
|
1012
1100
|
const runtime = [];
|
|
1013
1101
|
const records = [];
|
|
1102
|
+
const groupBriefs = [];
|
|
1014
1103
|
if (!tree.rootExists && tree.diagnostics.length === 0) {
|
|
1015
1104
|
diagnostics.push({
|
|
1016
1105
|
severity: "error",
|
|
@@ -1034,14 +1123,18 @@ async function inspectArchiveHistory(options = {}) {
|
|
|
1034
1123
|
diagnostics.push(parsed.diagnostic);
|
|
1035
1124
|
if (parsed.record)
|
|
1036
1125
|
records.push(parsed.record);
|
|
1126
|
+
if (parsed.groupBrief)
|
|
1127
|
+
groupBriefs.push(parsed.groupBrief);
|
|
1037
1128
|
}
|
|
1038
1129
|
records.sort(compareHistoryRecords);
|
|
1130
|
+
groupBriefs.sort(compareArchivedGroupBriefs);
|
|
1039
1131
|
diagnostics.sort(compareDiagnostics);
|
|
1040
1132
|
const diagnosticTotal = diagnostics.length;
|
|
1041
1133
|
const boundedDiagnostics = diagnostics.slice(0, HISTORY_MAX_DIAGNOSTICS);
|
|
1042
1134
|
return {
|
|
1043
1135
|
rootExists: tree.rootExists,
|
|
1044
1136
|
records,
|
|
1137
|
+
groupBriefs,
|
|
1045
1138
|
diagnostics: boundedDiagnostics,
|
|
1046
1139
|
diagnosticSummary: {
|
|
1047
1140
|
total: diagnosticTotal,
|
|
@@ -1082,7 +1175,15 @@ function parseArchiveEntry(sourcePath, path, archivesDir, content) {
|
|
|
1082
1175
|
if (!archiveGroup || group !== archiveGroup || frontmatter.kind !== "group" || !matchesArchiveStem(archiveStem, "brief")) {
|
|
1083
1176
|
return { diagnostic: errorDiagnostic("archive_identity_mismatch", path, `archived Change Group identity ${group} does not match its archive path`) };
|
|
1084
1177
|
}
|
|
1085
|
-
return {};
|
|
1178
|
+
return { groupBrief: { date, group, path, sourcePath } };
|
|
1179
|
+
}
|
|
1180
|
+
let issues;
|
|
1181
|
+
if ("issues" in frontmatter) {
|
|
1182
|
+
try {
|
|
1183
|
+
issues = parseIssueRelationships(frontmatter);
|
|
1184
|
+
} catch {
|
|
1185
|
+
issues = void 0;
|
|
1186
|
+
}
|
|
1086
1187
|
}
|
|
1087
1188
|
const workRef = changeHeadings[0];
|
|
1088
1189
|
const segments = workRef.split("/");
|
|
@@ -1107,6 +1208,7 @@ function parseArchiveEntry(sourcePath, path, archivesDir, content) {
|
|
|
1107
1208
|
summary: summary.value,
|
|
1108
1209
|
summaryTruncated: summary.truncated,
|
|
1109
1210
|
path,
|
|
1211
|
+
...issues === void 0 ? {} : { issues },
|
|
1110
1212
|
sourcePath,
|
|
1111
1213
|
searchSummary: rawSummary
|
|
1112
1214
|
}
|
|
@@ -1151,6 +1253,9 @@ function escapeRegExp(value) {
|
|
|
1151
1253
|
function compareHistoryRecords(left, right) {
|
|
1152
1254
|
return right.date.localeCompare(left.date) || left.workRef.localeCompare(right.workRef) || left.path.localeCompare(right.path);
|
|
1153
1255
|
}
|
|
1256
|
+
function compareArchivedGroupBriefs(left, right) {
|
|
1257
|
+
return right.date.localeCompare(left.date) || left.group.localeCompare(right.group) || left.path.localeCompare(right.path);
|
|
1258
|
+
}
|
|
1154
1259
|
function compareDiagnostics(left, right) {
|
|
1155
1260
|
return (left.path ?? "").localeCompare(right.path ?? "") || left.code.localeCompare(right.code) || left.message.localeCompare(right.message);
|
|
1156
1261
|
}
|
|
@@ -1205,6 +1310,17 @@ function selectArchiveHistoryRecord(records, selector) {
|
|
|
1205
1310
|
}
|
|
1206
1311
|
return matches[0];
|
|
1207
1312
|
}
|
|
1313
|
+
function selectArchivedGroupBrief(records, selector) {
|
|
1314
|
+
const matches = "path" in selector ? records.filter((record) => record.path === selector.path) : records.filter((record) => record.group === selector.group);
|
|
1315
|
+
const identity = "path" in selector ? selector.path : selector.group;
|
|
1316
|
+
if (matches.length === 0)
|
|
1317
|
+
throw new ArchiveHistoryError("archive_not_found", `archived Change Group not found: ${identity}`);
|
|
1318
|
+
if (matches.length > 1) {
|
|
1319
|
+
const candidates = matches.map((record) => record.path).sort();
|
|
1320
|
+
throw new ArchiveHistoryError("archive_ambiguous", `multiple archives match Change Group ${identity}`, candidates);
|
|
1321
|
+
}
|
|
1322
|
+
return matches[0];
|
|
1323
|
+
}
|
|
1208
1324
|
async function readArchiveHistoryDetail(record) {
|
|
1209
1325
|
let content;
|
|
1210
1326
|
try {
|
|
@@ -1600,6 +1716,7 @@ async function inspectProjectStatus(options = {}) {
|
|
|
1600
1716
|
let done = 0;
|
|
1601
1717
|
let total = 0;
|
|
1602
1718
|
let progressKnown = false;
|
|
1719
|
+
let issues = [];
|
|
1603
1720
|
if (path) {
|
|
1604
1721
|
try {
|
|
1605
1722
|
const content = await readFile5(path, "utf-8");
|
|
@@ -1608,10 +1725,21 @@ async function inspectProjectStatus(options = {}) {
|
|
|
1608
1725
|
try {
|
|
1609
1726
|
const frontmatter = parseFrontmatter(content);
|
|
1610
1727
|
kind = frontmatter?.kind ? String(frontmatter.kind) : "\u2014";
|
|
1728
|
+
issues = parseIssueRelationships(frontmatter);
|
|
1611
1729
|
} catch (error) {
|
|
1612
1730
|
kind = "(invalid)";
|
|
1731
|
+
const issueError = error instanceof IssueRelationshipError;
|
|
1732
|
+
if (issueError) {
|
|
1733
|
+
diagnostics.push({
|
|
1734
|
+
severity: "error",
|
|
1735
|
+
code: error.code,
|
|
1736
|
+
change: name,
|
|
1737
|
+
path,
|
|
1738
|
+
message: error.message
|
|
1739
|
+
});
|
|
1740
|
+
}
|
|
1613
1741
|
runtime.push({
|
|
1614
|
-
code: "frontmatter_parse_failed",
|
|
1742
|
+
code: issueError ? error.code : "frontmatter_parse_failed",
|
|
1615
1743
|
operation: "parseFrontmatter",
|
|
1616
1744
|
path,
|
|
1617
1745
|
message: toErrorMessage(error)
|
|
@@ -1692,7 +1820,8 @@ async function inspectProjectStatus(options = {}) {
|
|
|
1692
1820
|
ageDays,
|
|
1693
1821
|
isFocused,
|
|
1694
1822
|
isBlocked,
|
|
1695
|
-
path: path ? normalizeLogicalPath(path) : null
|
|
1823
|
+
path: path ? normalizeLogicalPath(path) : null,
|
|
1824
|
+
issues
|
|
1696
1825
|
},
|
|
1697
1826
|
progressKnown,
|
|
1698
1827
|
title,
|
|
@@ -1785,16 +1914,22 @@ export {
|
|
|
1785
1914
|
inspectFocusTree,
|
|
1786
1915
|
inspectArchiveTree,
|
|
1787
1916
|
inspectWorkTree,
|
|
1917
|
+
GROUP_REOPEN_COMPLETION_PREFIX,
|
|
1788
1918
|
resolveExecutableChange,
|
|
1789
1919
|
generateGroupBriefContent,
|
|
1790
1920
|
inspectChangeGroups,
|
|
1921
|
+
getGroupReopenEvidence,
|
|
1791
1922
|
hasArchivedGroupBrief,
|
|
1923
|
+
IssueRelationshipError,
|
|
1924
|
+
parseIssueRelationships,
|
|
1925
|
+
createIssueRelationship,
|
|
1792
1926
|
HISTORY_DEFAULT_LIMIT,
|
|
1793
1927
|
HISTORY_MAX_LIMIT,
|
|
1794
1928
|
ArchiveHistoryError,
|
|
1795
1929
|
inspectArchiveHistory,
|
|
1796
1930
|
queryArchiveHistory,
|
|
1797
1931
|
selectArchiveHistoryRecord,
|
|
1932
|
+
selectArchivedGroupBrief,
|
|
1798
1933
|
readArchiveHistoryDetail,
|
|
1799
1934
|
historyInspectionComplete,
|
|
1800
1935
|
inspectChangeDependencies,
|
|
@@ -472,10 +472,11 @@ function normalizeLogicalPath(pathValue) {
|
|
|
472
472
|
function generateChangeContent(name, summary = "", kind, options = {}) {
|
|
473
473
|
const placeholder = "<\u2026>";
|
|
474
474
|
const proposalSummary = summary || placeholder;
|
|
475
|
+
const issueFrontmatter = renderIssueFrontmatter(options.issues ?? []);
|
|
475
476
|
if (name === "project-setup") {
|
|
476
477
|
return `---
|
|
477
478
|
kind: ops
|
|
478
|
-
---
|
|
479
|
+
${issueFrontmatter}---
|
|
479
480
|
|
|
480
481
|
${renderDocumentTitle(CHANGE_DOCUMENT_SCHEMA, "project-setup")}
|
|
481
482
|
|
|
@@ -533,11 +534,11 @@ ${changeSectionHeading("blockers")}
|
|
|
533
534
|
}
|
|
534
535
|
const frontmatterKind = kind ?? "<choose: feature | fix | refactor | docs | ops | research>";
|
|
535
536
|
if (options.lite)
|
|
536
|
-
return generateLiteChangeContent(name, proposalSummary, frontmatterKind);
|
|
537
|
+
return generateLiteChangeContent(name, proposalSummary, frontmatterKind, issueFrontmatter);
|
|
537
538
|
const template = getChangeTemplateByKind(kind);
|
|
538
539
|
return `---
|
|
539
540
|
kind: "${frontmatterKind}"
|
|
540
|
-
---
|
|
541
|
+
${issueFrontmatter}---
|
|
541
542
|
|
|
542
543
|
${renderDocumentTitle(CHANGE_DOCUMENT_SCHEMA, name)}
|
|
543
544
|
|
|
@@ -582,11 +583,11 @@ ${changeSectionHeading("blockers")}
|
|
|
582
583
|
- none
|
|
583
584
|
`;
|
|
584
585
|
}
|
|
585
|
-
function generateLiteChangeContent(name, proposalSummary, frontmatterKind) {
|
|
586
|
+
function generateLiteChangeContent(name, proposalSummary, frontmatterKind, issueFrontmatter) {
|
|
586
587
|
const placeholder = "<\u2026>";
|
|
587
588
|
return `---
|
|
588
589
|
kind: "${frontmatterKind}"
|
|
589
|
-
---
|
|
590
|
+
${issueFrontmatter}---
|
|
590
591
|
|
|
591
592
|
${renderDocumentTitle(CHANGE_DOCUMENT_SCHEMA, name)}
|
|
592
593
|
|
|
@@ -635,6 +636,14 @@ ${changeSectionHeading("blockers")}
|
|
|
635
636
|
- none
|
|
636
637
|
`;
|
|
637
638
|
}
|
|
639
|
+
function renderIssueFrontmatter(issues) {
|
|
640
|
+
if (issues.length === 0)
|
|
641
|
+
return "";
|
|
642
|
+
return `issues:
|
|
643
|
+
${issues.map((issue) => ` - url: ${JSON.stringify(issue.url)}
|
|
644
|
+
relation: ${issue.relation}`).join("\n")}
|
|
645
|
+
`;
|
|
646
|
+
}
|
|
638
647
|
function getChangeTemplateByKind(kind) {
|
|
639
648
|
const placeholder = "<\u2026>";
|
|
640
649
|
const delta = kind === "feature" || kind === "research" || kind === void 0 ? "ADDED" : "MODIFIED";
|
|
@@ -642,8 +651,7 @@ function getChangeTemplateByKind(kind) {
|
|
|
642
651
|
why: placeholder,
|
|
643
652
|
scope: placeholder,
|
|
644
653
|
nonGoals: placeholder,
|
|
645
|
-
specSection:
|
|
646
|
-
### ${delta}
|
|
654
|
+
specSection: `### ${delta}
|
|
647
655
|
- Requirement: ${placeholder}
|
|
648
656
|
- ${placeholder}`,
|
|
649
657
|
acceptanceSection: `#### Scenario: ${placeholder}
|