@clawplays/ospec-cli 0.3.7 → 0.3.8
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/cli.js +1 -1
- package/dist/commands/ArchiveCommand.js +1 -0
- package/dist/commands/NewCommand.js +19 -11
- package/dist/commands/VerifyCommand.js +4 -40
- package/dist/services/ProjectService.d.ts +1 -0
- package/dist/services/ProjectService.js +257 -406
- package/dist/services/QueueService.js +1 -0
- package/dist/services/TemplateGenerator.js +6 -6
- package/dist/services/templates/ExecutionTemplateBuilder.js +52 -28
- package/dist/services/templates/TemplateBuilderBase.d.ts +5 -0
- package/dist/services/templates/TemplateBuilderBase.js +31 -2
- package/dist/services/templates/TemplateInputFactory.js +4 -0
- package/dist/services/templates/templateTypes.d.ts +4 -0
- package/dist/tools/build-index.js +127 -35
- package/package.json +5 -1
|
@@ -4545,6 +4545,7 @@ class ProjectService {
|
|
|
4545
4545
|
|
|
4546
4546
|
|
|
4547
4547
|
|
|
4548
|
+
await this.rebaseMovedChangeMarkdownLinks(resolvedFeaturePath, archivePath);
|
|
4548
4549
|
await this.rebuildIndex(projectRoot);
|
|
4549
4550
|
|
|
4550
4551
|
|
|
@@ -4593,6 +4594,51 @@ class ProjectService {
|
|
|
4593
4594
|
|
|
4594
4595
|
|
|
4595
4596
|
|
|
4597
|
+
async rebaseMovedChangeMarkdownLinks(previousChangePath, nextChangePath) {
|
|
4598
|
+
const previousRoot = path_1.default.resolve(previousChangePath);
|
|
4599
|
+
const nextRoot = path_1.default.resolve(nextChangePath);
|
|
4600
|
+
const markdownFiles = [
|
|
4601
|
+
constants_1.FILE_NAMES.PROPOSAL,
|
|
4602
|
+
constants_1.FILE_NAMES.TASKS,
|
|
4603
|
+
constants_1.FILE_NAMES.VERIFICATION,
|
|
4604
|
+
constants_1.FILE_NAMES.REVIEW,
|
|
4605
|
+
];
|
|
4606
|
+
for (const fileName of markdownFiles) {
|
|
4607
|
+
const nextFilePath = path_1.default.join(nextRoot, fileName);
|
|
4608
|
+
if (!(await this.fileService.exists(nextFilePath))) {
|
|
4609
|
+
continue;
|
|
4610
|
+
}
|
|
4611
|
+
const previousFilePath = path_1.default.join(previousRoot, fileName);
|
|
4612
|
+
const originalContent = await this.fileService.readFile(nextFilePath);
|
|
4613
|
+
const previousDir = path_1.default.dirname(previousFilePath);
|
|
4614
|
+
const nextDir = path_1.default.dirname(nextFilePath);
|
|
4615
|
+
const rewrittenContent = originalContent.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (match, label, rawHref) => {
|
|
4616
|
+
const href = String(rawHref || '').trim();
|
|
4617
|
+
if (!this.isRelativeMarkdownHref(href)) {
|
|
4618
|
+
return match;
|
|
4619
|
+
}
|
|
4620
|
+
const previousTargetPath = path_1.default.resolve(previousDir, href);
|
|
4621
|
+
if (this.isPathWithin(previousTargetPath, previousRoot)) {
|
|
4622
|
+
return match;
|
|
4623
|
+
}
|
|
4624
|
+
const rebasedHref = path_1.default.relative(nextDir, previousTargetPath).replace(/\\/g, '/');
|
|
4625
|
+
return `[${label}](${rebasedHref || '.'})`;
|
|
4626
|
+
});
|
|
4627
|
+
if (rewrittenContent !== originalContent) {
|
|
4628
|
+
await this.fileService.writeFile(nextFilePath, rewrittenContent);
|
|
4629
|
+
}
|
|
4630
|
+
}
|
|
4631
|
+
}
|
|
4632
|
+
isRelativeMarkdownHref(href) {
|
|
4633
|
+
return Boolean(href) &&
|
|
4634
|
+
!href.startsWith('#') &&
|
|
4635
|
+
!href.startsWith('//') &&
|
|
4636
|
+
!/^[a-z][a-z0-9+.-]*:/i.test(href);
|
|
4637
|
+
}
|
|
4638
|
+
isPathWithin(targetPath, parentPath) {
|
|
4639
|
+
const relativePath = path_1.default.relative(parentPath, targetPath);
|
|
4640
|
+
return relativePath === '' || (!relativePath.startsWith('..') && !path_1.default.isAbsolute(relativePath));
|
|
4641
|
+
}
|
|
4596
4642
|
async getFeatureProjectContext(rootDir, affects = []) {
|
|
4597
4643
|
|
|
4598
4644
|
|
|
@@ -5275,7 +5321,8 @@ class ProjectService {
|
|
|
5275
5321
|
}
|
|
5276
5322
|
async syncConfigDocumentLanguage(rootDir, config, documentLanguage) {
|
|
5277
5323
|
const normalized = this.normalizeDocumentLanguage(documentLanguage);
|
|
5278
|
-
|
|
5324
|
+
const configured = this.normalizeDocumentLanguage(config?.documentLanguage);
|
|
5325
|
+
if (!normalized || configured) {
|
|
5279
5326
|
return false;
|
|
5280
5327
|
}
|
|
5281
5328
|
config.documentLanguage = normalized;
|
|
@@ -5284,13 +5331,28 @@ class ProjectService {
|
|
|
5284
5331
|
}
|
|
5285
5332
|
|
|
5286
5333
|
detectDocumentLanguageFromTexts(contents) {
|
|
5287
|
-
|
|
5334
|
+
const detectionCounts = new Map();
|
|
5335
|
+
const firstSeenOrder = new Map();
|
|
5336
|
+
for (const [index, content] of contents.entries()) {
|
|
5288
5337
|
const detected = this.detectDocumentLanguageFromText(content);
|
|
5289
5338
|
if (detected) {
|
|
5290
|
-
|
|
5339
|
+
detectionCounts.set(detected, (detectionCounts.get(detected) || 0) + 1);
|
|
5340
|
+
if (!firstSeenOrder.has(detected)) {
|
|
5341
|
+
firstSeenOrder.set(detected, index);
|
|
5342
|
+
}
|
|
5291
5343
|
}
|
|
5292
5344
|
}
|
|
5293
|
-
|
|
5345
|
+
if (detectionCounts.size === 0) {
|
|
5346
|
+
return undefined;
|
|
5347
|
+
}
|
|
5348
|
+
return Array.from(detectionCounts.entries())
|
|
5349
|
+
.sort((left, right) => {
|
|
5350
|
+
if (right[1] !== left[1]) {
|
|
5351
|
+
return right[1] - left[1];
|
|
5352
|
+
}
|
|
5353
|
+
return (firstSeenOrder.get(left[0]) ?? Number.MAX_SAFE_INTEGER) -
|
|
5354
|
+
(firstSeenOrder.get(right[0]) ?? Number.MAX_SAFE_INTEGER);
|
|
5355
|
+
})[0][0];
|
|
5294
5356
|
}
|
|
5295
5357
|
detectDocumentLanguageFromText(content) {
|
|
5296
5358
|
if (typeof content !== 'string' || content.trim().length === 0) {
|
|
@@ -5299,28 +5361,28 @@ class ProjectService {
|
|
|
5299
5361
|
if (/[\u0600-\u06FF]/.test(content)) {
|
|
5300
5362
|
return 'ar';
|
|
5301
5363
|
}
|
|
5302
|
-
if (
|
|
5303
|
-
return 'ja-JP';
|
|
5304
|
-
}
|
|
5305
|
-
if (this.isLikelyJapaneseKanjiContent(content)) {
|
|
5364
|
+
if (this.hasJapaneseKana(content)) {
|
|
5306
5365
|
return 'ja-JP';
|
|
5307
5366
|
}
|
|
5308
|
-
if (
|
|
5309
|
-
return 'zh-CN';
|
|
5367
|
+
if (this.hasCjkIdeographs(content)) {
|
|
5368
|
+
return this.isLikelyJapaneseKanjiContent(content) ? 'ja-JP' : 'zh-CN';
|
|
5310
5369
|
}
|
|
5311
5370
|
if (/[A-Za-z]/.test(content)) {
|
|
5312
5371
|
return 'en-US';
|
|
5313
5372
|
}
|
|
5314
5373
|
return undefined;
|
|
5315
5374
|
}
|
|
5375
|
+
hasJapaneseKana(content) {
|
|
5376
|
+
return /[\u3040-\u30FF]/.test(content);
|
|
5377
|
+
}
|
|
5378
|
+
hasCjkIdeographs(content) {
|
|
5379
|
+
return /[\u3400-\u9FFF]/.test(content);
|
|
5380
|
+
}
|
|
5316
5381
|
isLikelyJapaneseKanjiContent(content) {
|
|
5317
|
-
if (
|
|
5382
|
+
if (!this.hasCjkIdeographs(content)) {
|
|
5318
5383
|
return false;
|
|
5319
5384
|
}
|
|
5320
|
-
|
|
5321
|
-
return true;
|
|
5322
|
-
}
|
|
5323
|
-
return /(一覧|詳細|設定|権限|検索|構成|変更|確認|対応|連携|承認|申請|手順|履歴|機能|実装|設計|運用|画面|帳票|組織|拠点|区分|種別|完了|開始|終了|表示|取得|追加|削除|更新|登録)/.test(content);
|
|
5385
|
+
return /[\u3005\u3006\u300C-\u300F\u30F5\u30F6]/.test(content);
|
|
5324
5386
|
}
|
|
5325
5387
|
|
|
5326
5388
|
|
|
@@ -11034,197 +11096,103 @@ ${formatSuggestion()}
|
|
|
11034
11096
|
|
|
11035
11097
|
|
|
11036
11098
|
const content = await this.fileService.readFile(filePath);
|
|
11037
|
-
|
|
11038
|
-
|
|
11039
|
-
|
|
11040
|
-
|
|
11041
|
-
|
|
11042
|
-
|
|
11043
|
-
|
|
11044
|
-
|
|
11045
|
-
|
|
11046
|
-
|
|
11047
|
-
|
|
11048
|
-
|
|
11049
|
-
|
|
11050
|
-
|
|
11051
|
-
|
|
11052
|
-
|
|
11053
|
-
|
|
11054
|
-
|
|
11055
|
-
|
|
11056
|
-
|
|
11057
|
-
|
|
11058
|
-
|
|
11059
|
-
|
|
11060
|
-
|
|
11061
|
-
|
|
11062
|
-
|
|
11063
|
-
|
|
11064
|
-
|
|
11065
|
-
|
|
11066
|
-
|
|
11067
|
-
|
|
11068
|
-
const
|
|
11069
|
-
|
|
11070
|
-
|
|
11071
|
-
|
|
11072
|
-
|
|
11073
|
-
|
|
11074
|
-
|
|
11075
|
-
|
|
11099
|
+
const hasFrontmatter = /^---\r?\n[\s\S]*?\r?\n---(?:\r?\n|$)/.test(content);
|
|
11100
|
+
let parsed = null;
|
|
11101
|
+
let parseError = null;
|
|
11102
|
+
if (hasFrontmatter) {
|
|
11103
|
+
try {
|
|
11104
|
+
parsed = (0, gray_matter_1.default)(content);
|
|
11105
|
+
}
|
|
11106
|
+
catch (error) {
|
|
11107
|
+
parseError = error;
|
|
11108
|
+
}
|
|
11109
|
+
}
|
|
11110
|
+
const data = parsed?.data ?? {};
|
|
11111
|
+
const optionalStepsFieldValid = Array.isArray(data.optional_steps);
|
|
11112
|
+
const optionalSteps = optionalStepsFieldValid ? data.optional_steps : [];
|
|
11113
|
+
const createdFieldValid = (typeof data.created === 'string' && data.created.trim().length > 0) ||
|
|
11114
|
+
(data.created instanceof Date && !Number.isNaN(data.created.getTime()));
|
|
11115
|
+
const missingRequiredFields = [];
|
|
11116
|
+
if (typeof data.feature !== 'string' || data.feature.trim().length === 0) {
|
|
11117
|
+
missingRequiredFields.push('feature');
|
|
11118
|
+
}
|
|
11119
|
+
if (!createdFieldValid) {
|
|
11120
|
+
missingRequiredFields.push('created');
|
|
11121
|
+
}
|
|
11122
|
+
if (!optionalStepsFieldValid) {
|
|
11123
|
+
missingRequiredFields.push('optional_steps');
|
|
11124
|
+
}
|
|
11125
|
+
const missing = optionalStepsFieldValid
|
|
11126
|
+
? activatedSteps.filter(step => !optionalSteps.includes(step))
|
|
11127
|
+
: [...activatedSteps];
|
|
11128
|
+
const checklistItems = parsed?.content.match(/^\s*-\s+\[(?: |x|X)\]\s+.+$/gm) ?? [];
|
|
11129
|
+
const uncheckedItems = parsed?.content.match(/^\s*-\s+\[ \]\s+.+$/gm) ?? [];
|
|
11130
|
+
const checklistStructureValid = checklistItems.length > 0;
|
|
11131
|
+
const checklistComplete = hasFrontmatter &&
|
|
11132
|
+
parseError === null &&
|
|
11133
|
+
missingRequiredFields.length === 0 &&
|
|
11134
|
+
checklistStructureValid &&
|
|
11135
|
+
uncheckedItems.length === 0;
|
|
11136
|
+
let frontmatterMessage = `${name} frontmatter parsed successfully`;
|
|
11137
|
+
if (!hasFrontmatter) {
|
|
11138
|
+
frontmatterMessage = `${name} is missing a valid frontmatter block`;
|
|
11139
|
+
}
|
|
11140
|
+
else if (parseError) {
|
|
11141
|
+
frontmatterMessage = `${name} frontmatter cannot be parsed: ${parseError.message}`;
|
|
11142
|
+
}
|
|
11143
|
+
let requiredFieldsMessage = `${name} has all required frontmatter fields`;
|
|
11144
|
+
if (!hasFrontmatter || parseError) {
|
|
11145
|
+
requiredFieldsMessage = `Cannot validate required fields in ${name} because frontmatter is invalid`;
|
|
11146
|
+
}
|
|
11147
|
+
else if (missingRequiredFields.length > 0) {
|
|
11148
|
+
requiredFieldsMessage = `Missing or invalid required fields in ${name}: ${missingRequiredFields.join(', ')}`;
|
|
11149
|
+
}
|
|
11150
|
+
let optionalStepsMessage = `All activated optional steps are present in ${name}`;
|
|
11151
|
+
if (!optionalStepsFieldValid) {
|
|
11152
|
+
optionalStepsMessage = `${name} frontmatter field optional_steps must be an array`;
|
|
11153
|
+
}
|
|
11154
|
+
else if (missing.length > 0) {
|
|
11155
|
+
optionalStepsMessage = `Missing optional steps in ${name}: ${missing.join(', ')}`;
|
|
11156
|
+
}
|
|
11157
|
+
let checklistStatus = 'pass';
|
|
11158
|
+
let checklistMessage = `${name} checklist is complete`;
|
|
11159
|
+
if (!hasFrontmatter || parseError) {
|
|
11160
|
+
checklistStatus = 'fail';
|
|
11161
|
+
checklistMessage = `${name} checklist cannot be validated because frontmatter is invalid`;
|
|
11162
|
+
}
|
|
11163
|
+
else if (!checklistStructureValid) {
|
|
11164
|
+
checklistStatus = 'fail';
|
|
11165
|
+
checklistMessage = `${name} must contain at least one Markdown checklist item`;
|
|
11166
|
+
}
|
|
11167
|
+
else if (uncheckedItems.length > 0) {
|
|
11168
|
+
checklistStatus = 'warn';
|
|
11169
|
+
checklistMessage = `${name} still has unchecked items`;
|
|
11170
|
+
}
|
|
11076
11171
|
return {
|
|
11077
|
-
|
|
11078
|
-
|
|
11079
|
-
|
|
11080
|
-
|
|
11081
|
-
|
|
11082
|
-
|
|
11083
|
-
|
|
11084
11172
|
optionalSteps,
|
|
11085
|
-
|
|
11086
|
-
|
|
11087
|
-
|
|
11088
|
-
|
|
11089
|
-
|
|
11090
|
-
|
|
11091
|
-
|
|
11092
11173
|
checklistComplete,
|
|
11093
|
-
|
|
11094
|
-
|
|
11095
|
-
|
|
11096
|
-
|
|
11097
|
-
|
|
11098
|
-
|
|
11099
|
-
|
|
11100
11174
|
checks: [
|
|
11101
|
-
|
|
11102
|
-
|
|
11103
|
-
|
|
11104
|
-
|
|
11105
|
-
|
|
11106
|
-
|
|
11107
|
-
|
|
11108
11175
|
{
|
|
11109
|
-
|
|
11110
|
-
|
|
11111
|
-
|
|
11112
|
-
|
|
11113
|
-
|
|
11114
|
-
|
|
11115
|
-
|
|
11176
|
+
name: `${name}.frontmatter`,
|
|
11177
|
+
status: hasFrontmatter && parseError === null ? 'pass' : 'fail',
|
|
11178
|
+
message: frontmatterMessage,
|
|
11179
|
+
},
|
|
11180
|
+
{
|
|
11181
|
+
name: `${name}.required_fields`,
|
|
11182
|
+
status: hasFrontmatter && parseError === null && missingRequiredFields.length === 0 ? 'pass' : 'fail',
|
|
11183
|
+
message: requiredFieldsMessage,
|
|
11184
|
+
},
|
|
11185
|
+
{
|
|
11116
11186
|
name: `${name}.optional_steps`,
|
|
11117
|
-
|
|
11118
|
-
|
|
11119
|
-
|
|
11120
|
-
|
|
11121
|
-
|
|
11122
|
-
|
|
11123
|
-
|
|
11124
|
-
status: missing.length === 0 ? 'pass' : 'fail',
|
|
11125
|
-
|
|
11126
|
-
|
|
11127
|
-
|
|
11128
|
-
|
|
11129
|
-
|
|
11130
|
-
|
|
11131
|
-
|
|
11132
|
-
message: missing.length === 0
|
|
11133
|
-
|
|
11134
|
-
|
|
11135
|
-
|
|
11136
|
-
|
|
11137
|
-
|
|
11138
|
-
|
|
11139
|
-
|
|
11140
|
-
? `All activated optional steps are present in ${name}`
|
|
11141
|
-
|
|
11142
|
-
|
|
11143
|
-
|
|
11144
|
-
|
|
11145
|
-
|
|
11146
|
-
|
|
11147
|
-
|
|
11148
|
-
: `Missing optional steps in ${name}: ${missing.join(', ')}`,
|
|
11149
|
-
|
|
11150
|
-
|
|
11151
|
-
|
|
11152
|
-
|
|
11153
|
-
|
|
11154
|
-
|
|
11155
|
-
|
|
11187
|
+
status: optionalStepsFieldValid && missing.length === 0 ? 'pass' : 'fail',
|
|
11188
|
+
message: optionalStepsMessage,
|
|
11156
11189
|
},
|
|
11157
|
-
|
|
11158
|
-
|
|
11159
|
-
|
|
11160
|
-
|
|
11161
|
-
|
|
11162
|
-
|
|
11163
|
-
|
|
11164
11190
|
{
|
|
11165
|
-
|
|
11166
|
-
|
|
11167
|
-
|
|
11168
|
-
|
|
11169
|
-
|
|
11170
|
-
|
|
11171
|
-
|
|
11172
11191
|
name: `${name}.checklist`,
|
|
11173
|
-
|
|
11174
|
-
|
|
11175
|
-
|
|
11176
|
-
|
|
11177
|
-
|
|
11178
|
-
|
|
11179
|
-
|
|
11180
|
-
status: checklistComplete ? 'pass' : 'warn',
|
|
11181
|
-
|
|
11182
|
-
|
|
11183
|
-
|
|
11184
|
-
|
|
11185
|
-
|
|
11186
|
-
|
|
11187
|
-
|
|
11188
|
-
message: checklistComplete
|
|
11189
|
-
|
|
11190
|
-
|
|
11191
|
-
|
|
11192
|
-
|
|
11193
|
-
|
|
11194
|
-
|
|
11195
|
-
|
|
11196
|
-
? `${name} checklist is complete`
|
|
11197
|
-
|
|
11198
|
-
|
|
11199
|
-
|
|
11200
|
-
|
|
11201
|
-
|
|
11202
|
-
|
|
11203
|
-
|
|
11204
|
-
: `${name} still has unchecked items`,
|
|
11205
|
-
|
|
11206
|
-
|
|
11207
|
-
|
|
11208
|
-
|
|
11209
|
-
|
|
11210
|
-
|
|
11211
|
-
|
|
11192
|
+
status: checklistStatus,
|
|
11193
|
+
message: checklistMessage,
|
|
11212
11194
|
},
|
|
11213
|
-
|
|
11214
|
-
|
|
11215
|
-
|
|
11216
|
-
|
|
11217
|
-
|
|
11218
|
-
|
|
11219
|
-
|
|
11220
11195
|
],
|
|
11221
|
-
|
|
11222
|
-
|
|
11223
|
-
|
|
11224
|
-
|
|
11225
|
-
|
|
11226
|
-
|
|
11227
|
-
|
|
11228
11196
|
};
|
|
11229
11197
|
|
|
11230
11198
|
|
|
@@ -11250,229 +11218,112 @@ ${formatSuggestion()}
|
|
|
11250
11218
|
|
|
11251
11219
|
|
|
11252
11220
|
const content = await this.fileService.readFile(filePath);
|
|
11253
|
-
|
|
11254
|
-
|
|
11255
|
-
|
|
11256
|
-
|
|
11257
|
-
|
|
11258
|
-
|
|
11259
|
-
|
|
11260
|
-
|
|
11261
|
-
|
|
11262
|
-
|
|
11263
|
-
|
|
11264
|
-
|
|
11265
|
-
|
|
11266
|
-
|
|
11267
|
-
|
|
11268
|
-
const
|
|
11269
|
-
|
|
11270
|
-
|
|
11271
|
-
|
|
11272
|
-
|
|
11273
|
-
|
|
11274
|
-
|
|
11275
|
-
|
|
11276
|
-
|
|
11277
|
-
|
|
11278
|
-
|
|
11279
|
-
|
|
11280
|
-
|
|
11281
|
-
|
|
11282
|
-
|
|
11283
|
-
|
|
11284
|
-
|
|
11285
|
-
|
|
11286
|
-
|
|
11287
|
-
|
|
11288
|
-
|
|
11289
|
-
|
|
11290
|
-
|
|
11291
|
-
|
|
11292
|
-
|
|
11293
|
-
|
|
11294
|
-
|
|
11295
|
-
|
|
11296
|
-
|
|
11297
|
-
|
|
11298
|
-
|
|
11299
|
-
|
|
11300
|
-
|
|
11301
|
-
|
|
11302
|
-
|
|
11303
|
-
|
|
11304
|
-
|
|
11305
|
-
|
|
11306
|
-
|
|
11307
|
-
|
|
11308
|
-
|
|
11309
|
-
|
|
11310
|
-
|
|
11311
|
-
|
|
11312
|
-
|
|
11313
|
-
|
|
11314
|
-
|
|
11315
|
-
|
|
11221
|
+
const hasFrontmatter = /^---\r?\n[\s\S]*?\r?\n---(?:\r?\n|$)/.test(content);
|
|
11222
|
+
let parsed = null;
|
|
11223
|
+
let parseError = null;
|
|
11224
|
+
if (hasFrontmatter) {
|
|
11225
|
+
try {
|
|
11226
|
+
parsed = (0, gray_matter_1.default)(content);
|
|
11227
|
+
}
|
|
11228
|
+
catch (error) {
|
|
11229
|
+
parseError = error;
|
|
11230
|
+
}
|
|
11231
|
+
}
|
|
11232
|
+
const data = parsed?.data ?? {};
|
|
11233
|
+
const optionalStepsFieldValid = Array.isArray(data.optional_steps);
|
|
11234
|
+
const passedOptionalStepsFieldValid = Array.isArray(data.passed_optional_steps);
|
|
11235
|
+
const optionalSteps = optionalStepsFieldValid ? data.optional_steps : [];
|
|
11236
|
+
const passedOptionalSteps = passedOptionalStepsFieldValid ? data.passed_optional_steps : [];
|
|
11237
|
+
const createdFieldValid = (typeof data.created === 'string' && data.created.trim().length > 0) ||
|
|
11238
|
+
(data.created instanceof Date && !Number.isNaN(data.created.getTime()));
|
|
11239
|
+
const missingRequiredFields = [];
|
|
11240
|
+
if (typeof data.feature !== 'string' || data.feature.trim().length === 0) {
|
|
11241
|
+
missingRequiredFields.push('feature');
|
|
11242
|
+
}
|
|
11243
|
+
if (!createdFieldValid) {
|
|
11244
|
+
missingRequiredFields.push('created');
|
|
11245
|
+
}
|
|
11246
|
+
if (typeof data.status !== 'string' || data.status.trim().length === 0) {
|
|
11247
|
+
missingRequiredFields.push('status');
|
|
11248
|
+
}
|
|
11249
|
+
if (!optionalStepsFieldValid) {
|
|
11250
|
+
missingRequiredFields.push('optional_steps');
|
|
11251
|
+
}
|
|
11252
|
+
if (!passedOptionalStepsFieldValid) {
|
|
11253
|
+
missingRequiredFields.push('passed_optional_steps');
|
|
11254
|
+
}
|
|
11255
|
+
const missing = optionalStepsFieldValid
|
|
11256
|
+
? activatedSteps.filter(step => !optionalSteps.includes(step))
|
|
11257
|
+
: [...activatedSteps];
|
|
11258
|
+
const checklistItems = parsed?.content.match(/^\s*-\s+\[(?: |x|X)\]\s+.+$/gm) ?? [];
|
|
11259
|
+
const uncheckedItems = parsed?.content.match(/^\s*-\s+\[ \]\s+.+$/gm) ?? [];
|
|
11260
|
+
const checklistStructureValid = checklistItems.length > 0;
|
|
11261
|
+
const checklistComplete = hasFrontmatter &&
|
|
11262
|
+
parseError === null &&
|
|
11263
|
+
missingRequiredFields.length === 0 &&
|
|
11264
|
+
checklistStructureValid &&
|
|
11265
|
+
uncheckedItems.length === 0;
|
|
11266
|
+
let frontmatterMessage = 'verification.md frontmatter parsed successfully';
|
|
11267
|
+
if (!hasFrontmatter) {
|
|
11268
|
+
frontmatterMessage = 'verification.md is missing a valid frontmatter block';
|
|
11269
|
+
}
|
|
11270
|
+
else if (parseError) {
|
|
11271
|
+
frontmatterMessage = `verification.md frontmatter cannot be parsed: ${parseError.message}`;
|
|
11272
|
+
}
|
|
11273
|
+
let requiredFieldsMessage = 'verification.md has all required frontmatter fields';
|
|
11274
|
+
if (!hasFrontmatter || parseError) {
|
|
11275
|
+
requiredFieldsMessage = 'Cannot validate required fields in verification.md because frontmatter is invalid';
|
|
11276
|
+
}
|
|
11277
|
+
else if (missingRequiredFields.length > 0) {
|
|
11278
|
+
requiredFieldsMessage = `Missing or invalid required fields in verification.md: ${missingRequiredFields.join(', ')}`;
|
|
11279
|
+
}
|
|
11280
|
+
let optionalStepsMessage = 'All activated optional steps are present in verification.md';
|
|
11281
|
+
if (!optionalStepsFieldValid) {
|
|
11282
|
+
optionalStepsMessage = 'verification.md frontmatter field optional_steps must be an array';
|
|
11283
|
+
}
|
|
11284
|
+
else if (missing.length > 0) {
|
|
11285
|
+
optionalStepsMessage = `Missing optional steps in verification.md: ${missing.join(', ')}`;
|
|
11286
|
+
}
|
|
11287
|
+
let checklistStatus = 'pass';
|
|
11288
|
+
let checklistMessage = 'verification.md checklist is complete';
|
|
11289
|
+
if (!hasFrontmatter || parseError) {
|
|
11290
|
+
checklistStatus = 'fail';
|
|
11291
|
+
checklistMessage = 'verification.md checklist cannot be validated because frontmatter is invalid';
|
|
11292
|
+
}
|
|
11293
|
+
else if (!checklistStructureValid) {
|
|
11294
|
+
checklistStatus = 'fail';
|
|
11295
|
+
checklistMessage = 'verification.md must contain at least one Markdown checklist item';
|
|
11296
|
+
}
|
|
11297
|
+
else if (uncheckedItems.length > 0) {
|
|
11298
|
+
checklistStatus = 'warn';
|
|
11299
|
+
checklistMessage = 'verification.md still has unchecked items';
|
|
11300
|
+
}
|
|
11316
11301
|
return {
|
|
11317
|
-
|
|
11318
|
-
|
|
11319
|
-
|
|
11320
|
-
|
|
11321
|
-
|
|
11322
|
-
|
|
11323
|
-
|
|
11324
11302
|
optionalSteps,
|
|
11325
|
-
|
|
11326
|
-
|
|
11327
|
-
|
|
11328
|
-
|
|
11329
|
-
|
|
11330
|
-
|
|
11331
|
-
|
|
11332
11303
|
passedOptionalSteps,
|
|
11333
|
-
|
|
11334
|
-
|
|
11335
|
-
|
|
11336
|
-
|
|
11337
|
-
|
|
11338
|
-
|
|
11339
|
-
|
|
11340
11304
|
checklistComplete,
|
|
11341
|
-
|
|
11342
|
-
|
|
11343
|
-
|
|
11344
|
-
|
|
11345
|
-
|
|
11346
|
-
|
|
11347
|
-
|
|
11348
11305
|
checks: [
|
|
11349
|
-
|
|
11350
|
-
|
|
11351
|
-
|
|
11352
|
-
|
|
11353
|
-
|
|
11354
|
-
|
|
11355
|
-
|
|
11356
11306
|
{
|
|
11357
|
-
|
|
11358
|
-
|
|
11359
|
-
|
|
11360
|
-
|
|
11361
|
-
|
|
11362
|
-
|
|
11363
|
-
|
|
11307
|
+
name: 'verification.md.frontmatter',
|
|
11308
|
+
status: hasFrontmatter && parseError === null ? 'pass' : 'fail',
|
|
11309
|
+
message: frontmatterMessage,
|
|
11310
|
+
},
|
|
11311
|
+
{
|
|
11312
|
+
name: 'verification.md.required_fields',
|
|
11313
|
+
status: hasFrontmatter && parseError === null && missingRequiredFields.length === 0 ? 'pass' : 'fail',
|
|
11314
|
+
message: requiredFieldsMessage,
|
|
11315
|
+
},
|
|
11316
|
+
{
|
|
11364
11317
|
name: 'verification.md.optional_steps',
|
|
11365
|
-
|
|
11366
|
-
|
|
11367
|
-
|
|
11368
|
-
|
|
11369
|
-
|
|
11370
|
-
|
|
11371
|
-
|
|
11372
|
-
status: missing.length === 0 ? 'pass' : 'fail',
|
|
11373
|
-
|
|
11374
|
-
|
|
11375
|
-
|
|
11376
|
-
|
|
11377
|
-
|
|
11378
|
-
|
|
11379
|
-
|
|
11380
|
-
message: missing.length === 0
|
|
11381
|
-
|
|
11382
|
-
|
|
11383
|
-
|
|
11384
|
-
|
|
11385
|
-
|
|
11386
|
-
|
|
11387
|
-
|
|
11388
|
-
? 'All activated optional steps are present in verification.md'
|
|
11389
|
-
|
|
11390
|
-
|
|
11391
|
-
|
|
11392
|
-
|
|
11393
|
-
|
|
11394
|
-
|
|
11395
|
-
|
|
11396
|
-
: `Missing optional steps in verification.md: ${missing.join(', ')}`,
|
|
11397
|
-
|
|
11398
|
-
|
|
11399
|
-
|
|
11400
|
-
|
|
11401
|
-
|
|
11402
|
-
|
|
11403
|
-
|
|
11318
|
+
status: optionalStepsFieldValid && missing.length === 0 ? 'pass' : 'fail',
|
|
11319
|
+
message: optionalStepsMessage,
|
|
11404
11320
|
},
|
|
11405
|
-
|
|
11406
|
-
|
|
11407
|
-
|
|
11408
|
-
|
|
11409
|
-
|
|
11410
|
-
|
|
11411
|
-
|
|
11412
11321
|
{
|
|
11413
|
-
|
|
11414
|
-
|
|
11415
|
-
|
|
11416
|
-
|
|
11417
|
-
|
|
11418
|
-
|
|
11419
|
-
|
|
11420
11322
|
name: 'verification.md.checklist',
|
|
11421
|
-
|
|
11422
|
-
|
|
11423
|
-
|
|
11424
|
-
|
|
11425
|
-
|
|
11426
|
-
|
|
11427
|
-
|
|
11428
|
-
status: checklistComplete ? 'pass' : 'warn',
|
|
11429
|
-
|
|
11430
|
-
|
|
11431
|
-
|
|
11432
|
-
|
|
11433
|
-
|
|
11434
|
-
|
|
11435
|
-
|
|
11436
|
-
message: checklistComplete
|
|
11437
|
-
|
|
11438
|
-
|
|
11439
|
-
|
|
11440
|
-
|
|
11441
|
-
|
|
11442
|
-
|
|
11443
|
-
|
|
11444
|
-
? 'verification.md checklist is complete'
|
|
11445
|
-
|
|
11446
|
-
|
|
11447
|
-
|
|
11448
|
-
|
|
11449
|
-
|
|
11450
|
-
|
|
11451
|
-
|
|
11452
|
-
: 'verification.md still has unchecked items',
|
|
11453
|
-
|
|
11454
|
-
|
|
11455
|
-
|
|
11456
|
-
|
|
11457
|
-
|
|
11458
|
-
|
|
11459
|
-
|
|
11323
|
+
status: checklistStatus,
|
|
11324
|
+
message: checklistMessage,
|
|
11460
11325
|
},
|
|
11461
|
-
|
|
11462
|
-
|
|
11463
|
-
|
|
11464
|
-
|
|
11465
|
-
|
|
11466
|
-
|
|
11467
|
-
|
|
11468
11326
|
],
|
|
11469
|
-
|
|
11470
|
-
|
|
11471
|
-
|
|
11472
|
-
|
|
11473
|
-
|
|
11474
|
-
|
|
11475
|
-
|
|
11476
11327
|
};
|
|
11477
11328
|
|
|
11478
11329
|
|