lee-spec-kit 0.6.30 → 0.6.31
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
|
@@ -514,8 +514,8 @@ var ko = {
|
|
|
514
514
|
reviewFixCommitIssueGuidance: "PR \uB9AC\uBDF0 \uC218\uC815 \uCEE4\uBC0B\uC744 \uC9C4\uD589\uD558\uC138\uC694. \uB9AC\uBDF0 \uBC18\uC601 \uD30C\uC77C\uB9CC \uC2A4\uD14C\uC774\uC9D5\uD55C \uB4A4 `fix(#{issueNumber}): <review-fix-summary>` \uD615\uC2DD\uC73C\uB85C \uCEE4\uBC0B\uD558\uC138\uC694. `<review-fix-summary>`\uC5D0\uB294 \uC774\uBC88 \uCEE4\uBC0B\uC5D0\uC11C \uC2E4\uC81C\uB85C \uD574\uACB0\uD55C \uB9AC\uBDF0 \uD56D\uBAA9 \uC694\uC57D\uC744 \uC791\uC131\uD558\uC138\uC694. (\uD0DC\uC2A4\uD06C \uC81C\uBAA9 \uC7AC\uC0AC\uC6A9 \uAE08\uC9C0)",
|
|
515
515
|
reviewFixCommitGuidance: "PR \uB9AC\uBDF0 \uC218\uC815 \uCEE4\uBC0B\uC744 \uC9C4\uD589\uD558\uC138\uC694. \uB9AC\uBDF0 \uBC18\uC601 \uD30C\uC77C\uB9CC \uC2A4\uD14C\uC774\uC9D5\uD55C \uB4A4 `fix(review): <review-fix-summary>` \uD615\uC2DD\uC73C\uB85C \uCEE4\uBC0B\uD558\uC138\uC694. `<review-fix-summary>`\uC5D0\uB294 \uC774\uBC88 \uCEE4\uBC0B\uC5D0\uC11C \uC2E4\uC81C\uB85C \uD574\uACB0\uD55C \uB9AC\uBDF0 \uD56D\uBAA9 \uC694\uC57D\uC744 \uC791\uC131\uD558\uC138\uC694. (\uD0DC\uC2A4\uD06C \uC81C\uBAA9 \uC7AC\uC0AC\uC6A9 \uAE08\uC9C0)",
|
|
516
516
|
standaloneNeedsProjectRoot: "standalone \uBAA8\uB4DC\uC5D0\uC11C\uB294 projectRoot \uC124\uC815\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. (npx lee-spec-kit config --project-root ...)",
|
|
517
|
-
createBranch: 'cd "{projectGitCwd}" && mkdir -p .worktrees && (git worktree add ".worktrees/feat-{issueNumber}-{slug}" "feat/{issueNumber}-{slug}" || git worktree add -b "feat/{issueNumber}-{slug}" ".worktrees/feat-{issueNumber}-{slug}") && echo "worktree: {projectGitCwd}/.worktrees/feat-{issueNumber}-{slug}"',
|
|
518
|
-
worktreeCleanupCommand: 'cd "{projectGitCwd}" && git worktree remove "
|
|
517
|
+
createBranch: 'cd "{projectGitCwd}" && mkdir -p .worktrees && (git worktree add ".worktrees/feat-{issueNumber}-{slug}" "feat/{issueNumber}-{slug}" || git worktree add -b "feat/{issueNumber}-{slug}" ".worktrees/feat-{issueNumber}-{slug}") && WT="{projectGitCwd}/.worktrees/feat-{issueNumber}-{slug}" && for f in .env .env.local .env.development .env.development.local .env.test .env.test.local .env.production .env.production.local; do [ -f "{projectGitCwd}/$f" ] && [ ! -e "$WT/$f" ] && cp "{projectGitCwd}/$f" "$WT/$f" || true; done && echo "worktree: {projectGitCwd}/.worktrees/feat-{issueNumber}-{slug}"',
|
|
518
|
+
worktreeCleanupCommand: 'cd "{projectGitCwd}" && WT="{worktreePath}" && ROOT="$(pwd)" && case "$WT" in "$ROOT"/.worktrees/*) if git worktree list --porcelain | grep -Fxq "worktree $WT"; then git worktree remove --force "$WT" || true; fi; [ -d "$WT" ] && rm -rf "$WT" || true ;; *) echo "skip unsafe worktree path: $WT" ;; esac && git worktree prune && CURRENT_BRANCH=$(git branch --show-current) && DEFAULT_BRANCH=$(git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null | cut -d/ -f2-) && TARGET_BRANCH="${DEFAULT_BRANCH:-$CURRENT_BRANCH}" && if [ -n "$TARGET_BRANCH" ]; then git checkout "$TARGET_BRANCH" >/dev/null 2>&1 || true; fi && if git rev-parse --abbrev-ref --symbolic-full-name "@{u}" >/dev/null 2>&1 && [ -z "$(git status --porcelain)" ]; then git pull --ff-only || true; fi',
|
|
519
519
|
tasksAllDoneButNoChecklist: '\uC644\uB8CC \uC870\uAC74 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8\uB97C \uC791\uC131\uD558\uC138\uC694. tasks.md\uC758 "\uC644\uB8CC \uC870\uAC74" \uC139\uC158\uC5D0 \uAC80\uC99D \uD56D\uBAA9\uC744 \uCD94\uAC00\uD558\uACE0, \uC0AC\uC6A9\uC790\uC640 \uD655\uC778 \uD6C4 \uCDA9\uC871 \uD56D\uBAA9\uC744 [x]\uB85C \uCCB4\uD06C\uD558\uC138\uC694. \uCD5C\uC885 \uC2B9\uC778(OK)\uB3C4 \uBC18\uC601\uD558\uC138\uC694.',
|
|
520
520
|
tasksAllDoneButChecklist: "\uC644\uB8CC \uC870\uAC74 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8\uC758 \uB0A8\uC740 \uD56D\uBAA9\uC744 \uC9C4\uD589\uD558\uC138\uC694. \uD604\uC7AC \uC9C4\uD589: ({checked}/{total}) \uC0AC\uC6A9\uC790\uC640 \uD655\uC778 \uD6C4 \uCDA9\uC871 \uD56D\uBAA9\uC744 [x]\uB85C \uCCB4\uD06C\uD558\uACE0 \uCD5C\uC885 \uC2B9\uC778(OK)\uC744 \uBC18\uC601\uD558\uC138\uC694.",
|
|
521
521
|
finishDoingTask: '\uD604\uC7AC DOING/REVIEW \uD0DC\uC2A4\uD06C\uB97C \uC218\uD589\uD558\uC138\uC694: "{title}" ({done}/{total}) \uC644\uB8CC \uC2DC \uACB0\uACFC/\uAC80\uC99D\uC744 \uACF5\uC720\uD558\uACE0 DONE \uCC98\uB9AC',
|
|
@@ -1025,8 +1025,8 @@ var en = {
|
|
|
1025
1025
|
reviewFixCommitIssueGuidance: "Commit PR review fixes. Stage only review-fix files, then commit with `fix(#{issueNumber}): <review-fix-summary>`. `<review-fix-summary>` must describe review comments resolved in this commit (do not reuse task titles).",
|
|
1026
1026
|
reviewFixCommitGuidance: "Commit PR review fixes. Stage only review-fix files, then commit with `fix(review): <review-fix-summary>`. `<review-fix-summary>` must describe review comments resolved in this commit (do not reuse task titles).",
|
|
1027
1027
|
standaloneNeedsProjectRoot: "Standalone mode requires projectRoot. (npx lee-spec-kit config --project-root ...)",
|
|
1028
|
-
createBranch: 'cd "{projectGitCwd}" && mkdir -p .worktrees && (git worktree add ".worktrees/feat-{issueNumber}-{slug}" "feat/{issueNumber}-{slug}" || git worktree add -b "feat/{issueNumber}-{slug}" ".worktrees/feat-{issueNumber}-{slug}") && echo "worktree: {projectGitCwd}/.worktrees/feat-{issueNumber}-{slug}"',
|
|
1029
|
-
worktreeCleanupCommand: 'cd "{projectGitCwd}" && git worktree remove "
|
|
1028
|
+
createBranch: 'cd "{projectGitCwd}" && mkdir -p .worktrees && (git worktree add ".worktrees/feat-{issueNumber}-{slug}" "feat/{issueNumber}-{slug}" || git worktree add -b "feat/{issueNumber}-{slug}" ".worktrees/feat-{issueNumber}-{slug}") && WT="{projectGitCwd}/.worktrees/feat-{issueNumber}-{slug}" && for f in .env .env.local .env.development .env.development.local .env.test .env.test.local .env.production .env.production.local; do [ -f "{projectGitCwd}/$f" ] && [ ! -e "$WT/$f" ] && cp "{projectGitCwd}/$f" "$WT/$f" || true; done && echo "worktree: {projectGitCwd}/.worktrees/feat-{issueNumber}-{slug}"',
|
|
1029
|
+
worktreeCleanupCommand: 'cd "{projectGitCwd}" && WT="{worktreePath}" && ROOT="$(pwd)" && case "$WT" in "$ROOT"/.worktrees/*) if git worktree list --porcelain | grep -Fxq "worktree $WT"; then git worktree remove --force "$WT" || true; fi; [ -d "$WT" ] && rm -rf "$WT" || true ;; *) echo "skip unsafe worktree path: $WT" ;; esac && git worktree prune && CURRENT_BRANCH=$(git branch --show-current) && DEFAULT_BRANCH=$(git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null | cut -d/ -f2-) && TARGET_BRANCH="${DEFAULT_BRANCH:-$CURRENT_BRANCH}" && if [ -n "$TARGET_BRANCH" ]; then git checkout "$TARGET_BRANCH" >/dev/null 2>&1 || true; fi && if git rev-parse --abbrev-ref --symbolic-full-name "@{u}" >/dev/null 2>&1 && [ -z "$(git status --porcelain)" ]; then git pull --ff-only || true; fi',
|
|
1030
1030
|
tasksAllDoneButNoChecklist: 'Create the completion checklist. Add verification items to the tasks.md "Completion Criteria" section, then mark satisfied items as [x] after user confirmation. Record final approval (OK) as well.',
|
|
1031
1031
|
tasksAllDoneButChecklist: "Proceed with remaining completion checklist items. Current progress: ({checked}/{total}) Mark items as [x] only after user confirmation and real verification. Record final approval (OK) as well.",
|
|
1032
1032
|
finishDoingTask: 'Continue working on the current DOING/REVIEW task: "{title}" ({done}/{total}) After it is complete, share outcome/verification and mark it DONE',
|
|
@@ -2951,13 +2951,15 @@ function resolveWorkflowPolicy(workflow) {
|
|
|
2951
2951
|
requireIssue: false,
|
|
2952
2952
|
requireBranch: false,
|
|
2953
2953
|
requirePr: false,
|
|
2954
|
-
requireReview: false
|
|
2954
|
+
requireReview: false,
|
|
2955
|
+
requireMerge: false
|
|
2955
2956
|
} : {
|
|
2956
2957
|
mode,
|
|
2957
2958
|
requireIssue: true,
|
|
2958
2959
|
requireBranch: true,
|
|
2959
2960
|
requirePr: true,
|
|
2960
|
-
requireReview: true
|
|
2961
|
+
requireReview: true,
|
|
2962
|
+
requireMerge: true
|
|
2961
2963
|
};
|
|
2962
2964
|
if (typeof workflow?.requireIssue === "boolean") {
|
|
2963
2965
|
policy.requireIssue = workflow.requireIssue;
|
|
@@ -2971,13 +2973,20 @@ function resolveWorkflowPolicy(workflow) {
|
|
|
2971
2973
|
if (typeof workflow?.requireReview === "boolean") {
|
|
2972
2974
|
policy.requireReview = workflow.requireReview;
|
|
2973
2975
|
}
|
|
2976
|
+
if (typeof workflow?.requireMerge === "boolean") {
|
|
2977
|
+
policy.requireMerge = workflow.requireMerge;
|
|
2978
|
+
}
|
|
2974
2979
|
if (!policy.requireIssue) {
|
|
2975
2980
|
policy.requireBranch = false;
|
|
2976
2981
|
}
|
|
2977
2982
|
if (!policy.requirePr) {
|
|
2978
2983
|
policy.requireReview = false;
|
|
2984
|
+
policy.requireMerge = false;
|
|
2979
2985
|
} else if (policy.requireReview) {
|
|
2980
2986
|
policy.requirePr = true;
|
|
2987
|
+
policy.requireMerge = true;
|
|
2988
|
+
} else if (policy.requireMerge) {
|
|
2989
|
+
policy.requirePr = true;
|
|
2981
2990
|
}
|
|
2982
2991
|
return policy;
|
|
2983
2992
|
}
|
|
@@ -3076,7 +3085,7 @@ function isPrePrReviewSatisfied(feature, prePrReviewPolicy) {
|
|
|
3076
3085
|
return true;
|
|
3077
3086
|
}
|
|
3078
3087
|
function isFeatureDone(feature, workflowPolicy, prePrReviewPolicy) {
|
|
3079
|
-
return feature.specStatus === "Approved" && feature.planStatus === "Approved" && !feature.git.docsHasUncommittedChanges && !feature.git.projectHasUncommittedChanges && feature.docs.tasksExists && feature.tasks.total > 0 && feature.tasks.total === feature.tasks.done && isCompletionChecklistDone(feature) && isTasksDocApproved(feature) && (!workflowPolicy.requireIssue || !!feature.issueNumber) && (!workflowPolicy.requirePr || isPrMetadataConfigured(feature) && !!feature.pr.link) && (!workflowPolicy.
|
|
3088
|
+
return feature.specStatus === "Approved" && feature.planStatus === "Approved" && !feature.git.docsHasUncommittedChanges && !feature.git.projectHasUncommittedChanges && feature.docs.tasksExists && feature.tasks.total > 0 && feature.tasks.total === feature.tasks.done && isCompletionChecklistDone(feature) && isTasksDocApproved(feature) && (!workflowPolicy.requireIssue || !!feature.issueNumber) && (!workflowPolicy.requirePr || isPrMetadataConfigured(feature) && !!feature.pr.link) && (!workflowPolicy.requireMerge || feature.pr.status === "Approved") && isPrePrReviewSatisfied(feature, prePrReviewPolicy);
|
|
3080
3089
|
}
|
|
3081
3090
|
function getPrReviewRemoteBlockReasons(feature, lang) {
|
|
3082
3091
|
const remote = feature.pr.remote;
|
|
@@ -3981,10 +3990,10 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
|
|
|
3981
3990
|
step: 14,
|
|
3982
3991
|
name: tr(lang, "steps", "codeReview"),
|
|
3983
3992
|
checklist: {
|
|
3984
|
-
done: (f) => !workflowPolicy.
|
|
3993
|
+
done: (f) => !workflowPolicy.requireMerge || isPrMetadataConfigured(f) && f.pr.status === "Approved"
|
|
3985
3994
|
},
|
|
3986
3995
|
current: {
|
|
3987
|
-
when: (f) => workflowPolicy.
|
|
3996
|
+
when: (f) => workflowPolicy.requireMerge && isPrMetadataConfigured(f) && !!f.pr.link && f.pr.status !== "Approved",
|
|
3988
3997
|
actions: (f) => {
|
|
3989
3998
|
if (!f.pr.status) {
|
|
3990
3999
|
return [
|
|
@@ -3998,7 +4007,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
|
|
|
3998
4007
|
];
|
|
3999
4008
|
}
|
|
4000
4009
|
if (f.pr.status === "Review") {
|
|
4001
|
-
if (f.pr.remote?.available && f.pr.remote.isMerged) {
|
|
4010
|
+
if (workflowPolicy.requireReview && f.pr.remote?.available && f.pr.remote.isMerged) {
|
|
4002
4011
|
return [
|
|
4003
4012
|
{
|
|
4004
4013
|
type: "instruction",
|
|
@@ -4009,7 +4018,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
|
|
|
4009
4018
|
}
|
|
4010
4019
|
];
|
|
4011
4020
|
}
|
|
4012
|
-
if (!f.docs.prReviewEvidenceFieldExists) {
|
|
4021
|
+
if (workflowPolicy.requireReview && !f.docs.prReviewEvidenceFieldExists) {
|
|
4013
4022
|
return [
|
|
4014
4023
|
{
|
|
4015
4024
|
type: "instruction",
|
|
@@ -4020,7 +4029,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
|
|
|
4020
4029
|
}
|
|
4021
4030
|
];
|
|
4022
4031
|
}
|
|
4023
|
-
if (!f.prReview.evidenceProvided) {
|
|
4032
|
+
if (workflowPolicy.requireReview && !f.prReview.evidenceProvided) {
|
|
4024
4033
|
return [
|
|
4025
4034
|
{
|
|
4026
4035
|
type: "instruction",
|
|
@@ -4031,7 +4040,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
|
|
|
4031
4040
|
}
|
|
4032
4041
|
];
|
|
4033
4042
|
}
|
|
4034
|
-
if (!f.docs.prReviewDecisionFieldExists) {
|
|
4043
|
+
if (workflowPolicy.requireReview && !f.docs.prReviewDecisionFieldExists) {
|
|
4035
4044
|
return [
|
|
4036
4045
|
{
|
|
4037
4046
|
type: "instruction",
|
|
@@ -4042,7 +4051,7 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
|
|
|
4042
4051
|
}
|
|
4043
4052
|
];
|
|
4044
4053
|
}
|
|
4045
|
-
if (!f.prReview.decisionProvided) {
|
|
4054
|
+
if (workflowPolicy.requireReview && !f.prReview.decisionProvided) {
|
|
4046
4055
|
return [
|
|
4047
4056
|
{
|
|
4048
4057
|
type: "instruction",
|
|
@@ -4055,15 +4064,16 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
|
|
|
4055
4064
|
}
|
|
4056
4065
|
const remoteBlockReasons = getPrReviewRemoteBlockReasons(f, lang);
|
|
4057
4066
|
const remoteUnavailable = workflowPolicy.mode === "github" && !!f.pr.link && (!f.pr.remote || !f.pr.remote.available);
|
|
4058
|
-
const actions = [
|
|
4059
|
-
|
|
4067
|
+
const actions = [];
|
|
4068
|
+
if (workflowPolicy.requireReview) {
|
|
4069
|
+
actions.push({
|
|
4060
4070
|
type: "instruction",
|
|
4061
4071
|
category: "code_review",
|
|
4062
4072
|
requiresUserCheck: true,
|
|
4063
4073
|
uiDetailKey: "context.actionDetail.codeReviewResolve",
|
|
4064
4074
|
message: tr(lang, "messages", "prReviewResolve")
|
|
4065
|
-
}
|
|
4066
|
-
|
|
4075
|
+
});
|
|
4076
|
+
}
|
|
4067
4077
|
if (!f.git.projectGitCwd) {
|
|
4068
4078
|
actions.push({
|
|
4069
4079
|
type: "instruction",
|
|
@@ -4121,7 +4131,18 @@ ${tr(lang, "messages", "taskCommitGateWarnProceed", {
|
|
|
4121
4131
|
})
|
|
4122
4132
|
});
|
|
4123
4133
|
}
|
|
4124
|
-
return actions;
|
|
4134
|
+
if (actions.length > 0) return actions;
|
|
4135
|
+
return [
|
|
4136
|
+
{
|
|
4137
|
+
type: "instruction",
|
|
4138
|
+
category: "code_review",
|
|
4139
|
+
requiresUserCheck: true,
|
|
4140
|
+
uiDetailKey: "context.actionDetail.codeReviewMergeAfterOk",
|
|
4141
|
+
message: tr(lang, "messages", "prReviewMerge", {
|
|
4142
|
+
featureRef: f.id || f.folderName
|
|
4143
|
+
})
|
|
4144
|
+
}
|
|
4145
|
+
];
|
|
4125
4146
|
}
|
|
4126
4147
|
return [
|
|
4127
4148
|
{
|
|
@@ -5331,7 +5352,7 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
5331
5352
|
prDocPrFieldExists = hasAnySpecKey(content, ["PR", "Pull Request"]);
|
|
5332
5353
|
prDocReviewStatusFieldExists = hasAnySpecKey(content, ["PR \uC0C1\uD0DC", "PR Status"]);
|
|
5333
5354
|
}
|
|
5334
|
-
if (workflowPolicy.
|
|
5355
|
+
if (workflowPolicy.requireMerge && prStatus === "Review" && prLink && effectiveProjectGitCwd) {
|
|
5335
5356
|
prRemote = resolvePrRemoteStatus(prLink, effectiveProjectGitCwd) || void 0;
|
|
5336
5357
|
}
|
|
5337
5358
|
const warnings = [];
|
|
@@ -5473,7 +5494,7 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
5473
5494
|
}
|
|
5474
5495
|
const tasksDocApproved = !tasksDocStatusFieldExists || tasksDocStatus === "Approved";
|
|
5475
5496
|
const implementationDone = tasksExists && tasksSummary.total > 0 && tasksSummary.total === tasksSummary.done && isCompletionChecklistDone2({ completionChecklist }) && tasksDocApproved;
|
|
5476
|
-
const workflowDone = implementationDone && !docsHasUncommittedChanges && !projectHasUncommittedChanges && specStatus === "Approved" && planStatus === "Approved" && (!workflowPolicy.requireIssue || !!issueNumber) && (!workflowPolicy.requirePr || isPrMetadataConfigured2({ docs: { prFieldExists, prStatusFieldExists } }) && !!prLink) && (!workflowPolicy.
|
|
5497
|
+
const workflowDone = implementationDone && !docsHasUncommittedChanges && !projectHasUncommittedChanges && specStatus === "Approved" && planStatus === "Approved" && (!workflowPolicy.requireIssue || !!issueNumber) && (!workflowPolicy.requirePr || isPrMetadataConfigured2({ docs: { prFieldExists, prStatusFieldExists } }) && !!prLink) && (!workflowPolicy.requireMerge || prStatus === "Approved") && isPrePrReviewSatisfied2(
|
|
5477
5498
|
{
|
|
5478
5499
|
docs: {
|
|
5479
5500
|
prePrReviewFieldExists,
|
|
@@ -5504,11 +5525,11 @@ async function parseFeature(featurePath, type, context, options) {
|
|
|
5504
5525
|
}
|
|
5505
5526
|
if (workflowPolicy.requirePr && prFieldExists && prStatusFieldExists) {
|
|
5506
5527
|
if (!prLink) warnings.push(tr(lang, "warnings", "workflowPrLinkMissing"));
|
|
5507
|
-
if (workflowPolicy.
|
|
5528
|
+
if (workflowPolicy.requireMerge) {
|
|
5508
5529
|
if (!prStatus) warnings.push(tr(lang, "warnings", "workflowPrStatusMissing"));
|
|
5509
5530
|
if (prStatus && prStatus !== "Approved") {
|
|
5510
5531
|
warnings.push(tr(lang, "warnings", "workflowPrStatusNotApproved"));
|
|
5511
|
-
if (prStatus === "Review") {
|
|
5532
|
+
if (workflowPolicy.requireReview && prStatus === "Review") {
|
|
5512
5533
|
if (!prReviewEvidenceFieldExists || !prReviewEvidenceProvided) {
|
|
5513
5534
|
warnings.push(tr(lang, "warnings", "workflowPrReviewEvidenceMissing"));
|
|
5514
5535
|
}
|
|
@@ -10808,8 +10829,14 @@ function runGhJson2(args, cwd, lang) {
|
|
|
10808
10829
|
}
|
|
10809
10830
|
function ensureSections(body, sections, kind, lang) {
|
|
10810
10831
|
const hasHeading = (sectionHeading) => {
|
|
10811
|
-
const
|
|
10812
|
-
|
|
10832
|
+
const target = normalizeHeading(sectionHeading);
|
|
10833
|
+
const lines = body.split("\n");
|
|
10834
|
+
for (const line of lines) {
|
|
10835
|
+
const match = line.match(/^\s*##\s+(.+?)\s*$/);
|
|
10836
|
+
if (!match) continue;
|
|
10837
|
+
if (normalizeHeading(match[1]) === target) return true;
|
|
10838
|
+
}
|
|
10839
|
+
return false;
|
|
10813
10840
|
};
|
|
10814
10841
|
const hasMetadataField = (field) => {
|
|
10815
10842
|
const re = new RegExp(`^\\s*-\\s*\\*\\*${escapeRegExp3(field)}\\*\\*\\s*:`, "m");
|
|
@@ -10970,7 +10997,13 @@ function getFeatureDocPaths(feature) {
|
|
|
10970
10997
|
};
|
|
10971
10998
|
}
|
|
10972
10999
|
function normalizeHeading(value) {
|
|
10973
|
-
|
|
11000
|
+
let normalized = value.trim();
|
|
11001
|
+
for (; ; ) {
|
|
11002
|
+
const next = normalized.replace(/\s*\([^)]*\)\s*$/, "").replace(/\s*([^)]*)\s*$/, "").trim();
|
|
11003
|
+
if (next === normalized) break;
|
|
11004
|
+
normalized = next;
|
|
11005
|
+
}
|
|
11006
|
+
return normalized.replace(/\s+/g, " ").toLowerCase();
|
|
10974
11007
|
}
|
|
10975
11008
|
function extractMarkdownByHeadings(content, headings, levels) {
|
|
10976
11009
|
const targets = new Set(headings.map((heading) => normalizeHeading(heading)));
|