lee-spec-kit 0.6.40 → 0.6.41
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 +140 -29
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5687,13 +5687,21 @@ function hasStructuredReviewSummary(value) {
|
|
|
5687
5687
|
if (!value) return false;
|
|
5688
5688
|
const trimmed = value.trim();
|
|
5689
5689
|
if (!trimmed) return false;
|
|
5690
|
-
|
|
5690
|
+
const match = trimmed.match(/^(?:summary|요약)\s*[::]\s*(.+)$/i);
|
|
5691
|
+
if (!match) return false;
|
|
5692
|
+
const payload = (match[1] || "").trim();
|
|
5693
|
+
if (!payload) return false;
|
|
5694
|
+
return !isReviewDraftPlaceholder(payload) && !isPlaceholderReviewEvidence(payload);
|
|
5691
5695
|
}
|
|
5692
5696
|
function hasStructuredReviewDecision(value) {
|
|
5693
5697
|
if (!value) return false;
|
|
5694
5698
|
const trimmed = value.trim();
|
|
5695
5699
|
if (!trimmed) return false;
|
|
5696
|
-
|
|
5700
|
+
const match = trimmed.match(/^(?:decision|결정)\s*[::]\s*(.+)$/i);
|
|
5701
|
+
if (!match) return false;
|
|
5702
|
+
const payload = (match[1] || "").trim();
|
|
5703
|
+
if (!payload) return false;
|
|
5704
|
+
return !isReviewDraftPlaceholder(payload) && !isPlaceholderReviewEvidence(payload);
|
|
5697
5705
|
}
|
|
5698
5706
|
function parsePrePrDecisionOutcome(value) {
|
|
5699
5707
|
if (!value) return void 0;
|
|
@@ -13436,7 +13444,9 @@ function insertFieldInGithubIssueSection(content, key, value) {
|
|
|
13436
13444
|
}
|
|
13437
13445
|
function insertFieldInMetadataSection(content, key, value) {
|
|
13438
13446
|
const lines = content.split("\n");
|
|
13439
|
-
const headingIndex = lines.findIndex(
|
|
13447
|
+
const headingIndex = lines.findIndex(
|
|
13448
|
+
(line) => /^\s*##\s+(?:Metadata|메타데이터)\s*$/.test(line)
|
|
13449
|
+
);
|
|
13440
13450
|
if (headingIndex < 0) return { content, changed: false };
|
|
13441
13451
|
let end = lines.length;
|
|
13442
13452
|
for (let i = headingIndex + 1; i < lines.length; i++) {
|
|
@@ -15199,14 +15209,6 @@ function asNonEmptyString(value, fallback) {
|
|
|
15199
15209
|
const trimmed = value.trim();
|
|
15200
15210
|
return trimmed || fallback;
|
|
15201
15211
|
}
|
|
15202
|
-
function asRequiredNonEmptyString(value, field) {
|
|
15203
|
-
const normalized = asNonEmptyString(value, "");
|
|
15204
|
-
if (normalized) return normalized;
|
|
15205
|
-
throw createCliError(
|
|
15206
|
-
"VALIDATION_FAILED",
|
|
15207
|
-
`Evidence JSON ${field} is required.`
|
|
15208
|
-
);
|
|
15209
|
-
}
|
|
15210
15212
|
function asRequiredBoolean(value, field) {
|
|
15211
15213
|
if (typeof value === "boolean") return value;
|
|
15212
15214
|
throw createCliError(
|
|
@@ -15223,9 +15225,67 @@ function asRequiredNonNegativeInteger(value, field) {
|
|
|
15223
15225
|
`Evidence JSON ${field} must be a non-negative integer.`
|
|
15224
15226
|
);
|
|
15225
15227
|
}
|
|
15228
|
+
function asRequiredTextLike(value, field) {
|
|
15229
|
+
if (typeof value === "string") {
|
|
15230
|
+
const trimmed = value.trim();
|
|
15231
|
+
if (trimmed) return trimmed;
|
|
15232
|
+
}
|
|
15233
|
+
if (typeof value === "number" && Number.isFinite(value) && !Number.isNaN(value)) {
|
|
15234
|
+
return String(value);
|
|
15235
|
+
}
|
|
15236
|
+
throw createCliError(
|
|
15237
|
+
"VALIDATION_FAILED",
|
|
15238
|
+
`Evidence JSON ${field} is required.`
|
|
15239
|
+
);
|
|
15240
|
+
}
|
|
15241
|
+
function isPlaceholderReviewEvidence2(value) {
|
|
15242
|
+
return /^(?:-|#)?\s*(?:tbd|todo|n\/a|na|none|pending|미정|없음|-)\s*$/i.test(
|
|
15243
|
+
value.trim()
|
|
15244
|
+
);
|
|
15245
|
+
}
|
|
15246
|
+
function isReviewDraftPlaceholder2(value) {
|
|
15247
|
+
return /^(?:-|#)?\s*(?:tbd|todo|pending|fill(?:\s+in)?|template|example|미정|작성|기입|n\/a|na)\b/i.test(
|
|
15248
|
+
value.trim()
|
|
15249
|
+
);
|
|
15250
|
+
}
|
|
15251
|
+
function isExplicitNoResidualRiskEntry2(value) {
|
|
15252
|
+
const trimmed = value.trim().toLowerCase();
|
|
15253
|
+
return trimmed === "none" || trimmed === "no residual risk" || trimmed === "no residual risks" || trimmed === "no residual risks found" || trimmed === "no residual risks found in reviewed scope" || trimmed === "\uC794\uC5EC \uB9AC\uC2A4\uD06C \uC5C6\uC74C" || trimmed === "\uC794\uC5EC \uC704\uD5D8 \uC5C6\uC74C";
|
|
15254
|
+
}
|
|
15255
|
+
function asRequiredReviewField(value, field, options) {
|
|
15256
|
+
const normalized = asRequiredTextLike(value, field);
|
|
15257
|
+
const allowExplicitNone = !!options?.allowExplicitNone;
|
|
15258
|
+
if (isReviewDraftPlaceholder2(normalized)) {
|
|
15259
|
+
throw createCliError(
|
|
15260
|
+
"VALIDATION_FAILED",
|
|
15261
|
+
`Evidence JSON ${field} contains draft placeholder text.`
|
|
15262
|
+
);
|
|
15263
|
+
}
|
|
15264
|
+
if (isPlaceholderReviewEvidence2(normalized) && !(allowExplicitNone && normalized.trim().toLowerCase() === "none")) {
|
|
15265
|
+
throw createCliError(
|
|
15266
|
+
"VALIDATION_FAILED",
|
|
15267
|
+
`Evidence JSON ${field} contains placeholder evidence text.`
|
|
15268
|
+
);
|
|
15269
|
+
}
|
|
15270
|
+
return normalized;
|
|
15271
|
+
}
|
|
15226
15272
|
function normalizeCommandsExecuted(value) {
|
|
15227
|
-
if (
|
|
15228
|
-
|
|
15273
|
+
if (value == null) return [];
|
|
15274
|
+
if (!Array.isArray(value)) {
|
|
15275
|
+
throw createCliError(
|
|
15276
|
+
"VALIDATION_FAILED",
|
|
15277
|
+
'Evidence JSON "commandsExecuted" must be an array when provided.'
|
|
15278
|
+
);
|
|
15279
|
+
}
|
|
15280
|
+
return value.map((entry, index) => {
|
|
15281
|
+
if (typeof entry !== "string" || !entry.trim()) {
|
|
15282
|
+
throw createCliError(
|
|
15283
|
+
"VALIDATION_FAILED",
|
|
15284
|
+
`Evidence JSON commandsExecuted[${index}] must be a non-empty string.`
|
|
15285
|
+
);
|
|
15286
|
+
}
|
|
15287
|
+
return entry.trim();
|
|
15288
|
+
});
|
|
15229
15289
|
}
|
|
15230
15290
|
function normalizeGitPath3(value) {
|
|
15231
15291
|
return value.trim().replace(/\\/g, "/").replace(/^\.\/+/, "").replace(/\/+$/, "");
|
|
@@ -15243,6 +15303,16 @@ function uniquePaths(values) {
|
|
|
15243
15303
|
}
|
|
15244
15304
|
return out;
|
|
15245
15305
|
}
|
|
15306
|
+
function normalizeFileLine(value, field) {
|
|
15307
|
+
const normalized = asRequiredTextLike(value, field);
|
|
15308
|
+
if (!/^\d+(?:[-:]\d+)?$/.test(normalized)) {
|
|
15309
|
+
throw createCliError(
|
|
15310
|
+
"VALIDATION_FAILED",
|
|
15311
|
+
`Evidence JSON ${field} must start with a numeric line reference (for example "88" or "88-96").`
|
|
15312
|
+
);
|
|
15313
|
+
}
|
|
15314
|
+
return normalized;
|
|
15315
|
+
}
|
|
15246
15316
|
function normalizeEvidenceFiles(value) {
|
|
15247
15317
|
if (!Array.isArray(value)) {
|
|
15248
15318
|
throw createCliError(
|
|
@@ -15265,22 +15335,61 @@ function normalizeEvidenceFiles(value) {
|
|
|
15265
15335
|
`Evidence JSON files[${index}].path is required.`
|
|
15266
15336
|
);
|
|
15267
15337
|
}
|
|
15268
|
-
const review = file.review
|
|
15338
|
+
const review = file.review && typeof file.review === "object" ? file.review : file;
|
|
15269
15339
|
return {
|
|
15270
15340
|
path: filePath,
|
|
15271
15341
|
review: {
|
|
15272
|
-
risk:
|
|
15273
|
-
security:
|
|
15274
|
-
|
|
15275
|
-
|
|
15342
|
+
risk: asRequiredTextLike(review.risk, `"files[${index}].risk"`),
|
|
15343
|
+
security: asRequiredTextLike(
|
|
15344
|
+
review.security,
|
|
15345
|
+
`"files[${index}].security"`
|
|
15346
|
+
),
|
|
15347
|
+
perf: asRequiredTextLike(
|
|
15348
|
+
review.perf ?? review.performance,
|
|
15349
|
+
`"files[${index}].perf"`
|
|
15350
|
+
),
|
|
15351
|
+
maintainability: asRequiredTextLike(
|
|
15276
15352
|
review.maintainability,
|
|
15277
|
-
"
|
|
15353
|
+
`"files[${index}].maintainability"`
|
|
15278
15354
|
),
|
|
15279
|
-
fileLine:
|
|
15355
|
+
fileLine: normalizeFileLine(
|
|
15356
|
+
review.fileLine,
|
|
15357
|
+
`"files[${index}].fileLine"`
|
|
15358
|
+
)
|
|
15280
15359
|
}
|
|
15281
15360
|
};
|
|
15282
15361
|
});
|
|
15283
15362
|
}
|
|
15363
|
+
function normalizeResidualRisks(value) {
|
|
15364
|
+
if (typeof value === "string" && value.trim()) {
|
|
15365
|
+
const normalized = asRequiredReviewField(
|
|
15366
|
+
value,
|
|
15367
|
+
'"residualRisks"',
|
|
15368
|
+
{ allowExplicitNone: true }
|
|
15369
|
+
);
|
|
15370
|
+
if (!isExplicitNoResidualRiskEntry2(normalized) && isPlaceholderReviewEvidence2(normalized)) {
|
|
15371
|
+
throw createCliError(
|
|
15372
|
+
"VALIDATION_FAILED",
|
|
15373
|
+
'Evidence JSON "residualRisks" contains placeholder evidence text.'
|
|
15374
|
+
);
|
|
15375
|
+
}
|
|
15376
|
+
return [normalized];
|
|
15377
|
+
}
|
|
15378
|
+
if (Array.isArray(value)) {
|
|
15379
|
+
const entries = value.map(
|
|
15380
|
+
(entry, index) => asRequiredReviewField(entry, `"residualRisks[${index}]"`, {
|
|
15381
|
+
allowExplicitNone: true
|
|
15382
|
+
})
|
|
15383
|
+
).filter(
|
|
15384
|
+
(entry) => isExplicitNoResidualRiskEntry2(entry) || !isReviewDraftPlaceholder2(entry) && !isPlaceholderReviewEvidence2(entry)
|
|
15385
|
+
);
|
|
15386
|
+
if (entries.length > 0) return entries;
|
|
15387
|
+
}
|
|
15388
|
+
throw createCliError(
|
|
15389
|
+
"VALIDATION_FAILED",
|
|
15390
|
+
'Evidence JSON "residualRisks" must be a non-empty string or string array.'
|
|
15391
|
+
);
|
|
15392
|
+
}
|
|
15284
15393
|
var PrePrReviewValidator = class {
|
|
15285
15394
|
constructor(ctx) {
|
|
15286
15395
|
this.ctx = ctx;
|
|
@@ -15307,18 +15416,19 @@ var PrePrReviewValidator = class {
|
|
|
15307
15416
|
);
|
|
15308
15417
|
}
|
|
15309
15418
|
const normalizedEvidence = {
|
|
15310
|
-
summary:
|
|
15311
|
-
featureIntentSummary:
|
|
15419
|
+
summary: asRequiredReviewField(evidence.summary, '"summary"'),
|
|
15420
|
+
featureIntentSummary: asRequiredReviewField(
|
|
15312
15421
|
evidence.featureIntentSummary,
|
|
15313
15422
|
'"featureIntentSummary"'
|
|
15314
15423
|
),
|
|
15315
|
-
implementationFit:
|
|
15424
|
+
implementationFit: asRequiredReviewField(
|
|
15316
15425
|
evidence.implementationFit,
|
|
15317
15426
|
'"implementationFit"'
|
|
15318
15427
|
),
|
|
15319
|
-
missingCases:
|
|
15428
|
+
missingCases: asRequiredReviewField(
|
|
15320
15429
|
evidence.missingCases,
|
|
15321
|
-
'"missingCases"'
|
|
15430
|
+
'"missingCases"',
|
|
15431
|
+
{ allowExplicitNone: true }
|
|
15322
15432
|
),
|
|
15323
15433
|
specAlignmentChecked: asRequiredBoolean(
|
|
15324
15434
|
evidence.specAlignmentChecked,
|
|
@@ -15333,7 +15443,7 @@ var PrePrReviewValidator = class {
|
|
|
15333
15443
|
'"blockingFindings"'
|
|
15334
15444
|
),
|
|
15335
15445
|
files: normalizeEvidenceFiles(evidence.files),
|
|
15336
|
-
residualRisks:
|
|
15446
|
+
residualRisks: normalizeResidualRisks(evidence.residualRisks),
|
|
15337
15447
|
commandsExecuted: normalizeCommandsExecuted(evidence.commandsExecuted)
|
|
15338
15448
|
};
|
|
15339
15449
|
if (normalizedEvidence.blockingFindings > normalizedEvidence.findingCount) {
|
|
@@ -15515,7 +15625,7 @@ var DEFAULT_EVIDENCE_FOR_ANY_MODE = {
|
|
|
15515
15625
|
findingCount: 0,
|
|
15516
15626
|
blockingFindings: 0,
|
|
15517
15627
|
files: [],
|
|
15518
|
-
residualRisks: "
|
|
15628
|
+
residualRisks: ["none"],
|
|
15519
15629
|
commandsExecuted: []
|
|
15520
15630
|
};
|
|
15521
15631
|
function escapeRegExp4(value) {
|
|
@@ -15602,7 +15712,7 @@ ${normalizedCommands.map((c) => ` - \`${c}\``).join("\n")}
|
|
|
15602
15712
|
|
|
15603
15713
|
` : "";
|
|
15604
15714
|
let filesSection = "";
|
|
15605
|
-
if (input.evidence.files.length === 0) {
|
|
15715
|
+
if (input.evidence.findingCount === 0 || input.evidence.files.length === 0) {
|
|
15606
15716
|
filesSection = " - 0 findings";
|
|
15607
15717
|
} else {
|
|
15608
15718
|
filesSection = input.evidence.files.map((f) => {
|
|
@@ -15613,6 +15723,7 @@ ${normalizedCommands.map((c) => ` - \`${c}\``).join("\n")}
|
|
|
15613
15723
|
- Maintainability: ${f.review.maintainability}`;
|
|
15614
15724
|
}).join("\n");
|
|
15615
15725
|
}
|
|
15726
|
+
const residualRisksSection = input.evidence.residualRisks.length > 0 ? input.evidence.residualRisks.map((entry) => ` - ${entry}`).join("\n") : " - none";
|
|
15616
15727
|
const mainScopeFiles = input.scope.mainChangedFiles.length > 0 ? input.scope.mainChangedFiles.map((entry) => ` - ${entry}`).join("\n") : " - (none)";
|
|
15617
15728
|
const worktreeScopeFiles = input.scope.worktreeChangedFiles.length > 0 ? input.scope.worktreeChangedFiles.map((entry) => ` - ${entry}`).join("\n") : " - (none)";
|
|
15618
15729
|
return `## Pre-PR Review Log (${input.date})
|
|
@@ -15631,7 +15742,7 @@ ${normalizedCommands.map((c) => ` - \`${c}\``).join("\n")}
|
|
|
15631
15742
|
${commandsRun}
|
|
15632
15743
|
|
|
15633
15744
|
- **Residual Risks**:
|
|
15634
|
-
|
|
15745
|
+
${residualRisksSection}
|
|
15635
15746
|
|
|
15636
15747
|
- **Review Scope**:
|
|
15637
15748
|
- **Main Base Ref**: ${input.scope.baseRef}
|