@clawplays/ospec-cli 0.3.9 → 0.3.10
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.
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
2
|
|
|
3
|
-
if [
|
|
4
|
-
|
|
3
|
+
if [ -f ".ospec/tools/build-index-auto.cjs" ]; then
|
|
4
|
+
OSPEC_BUILD_INDEX_SCRIPT=".ospec/tools/build-index-auto.cjs"
|
|
5
|
+
elif [ -f "build-index-auto.cjs" ]; then
|
|
6
|
+
OSPEC_BUILD_INDEX_SCRIPT="build-index-auto.cjs"
|
|
7
|
+
elif [ -f "build-index-auto.js" ]; then
|
|
8
|
+
OSPEC_BUILD_INDEX_SCRIPT="build-index-auto.js"
|
|
9
|
+
else
|
|
10
|
+
echo "[ospec] .ospec/tools/build-index-auto.cjs not found, skip hook check"
|
|
5
11
|
exit 0
|
|
6
12
|
fi
|
|
7
13
|
|
|
8
|
-
node
|
|
14
|
+
node "$OSPEC_BUILD_INDEX_SCRIPT" hook-check post-merge
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
2
|
|
|
3
|
-
if [
|
|
4
|
-
|
|
3
|
+
if [ -f ".ospec/tools/build-index-auto.cjs" ]; then
|
|
4
|
+
OSPEC_BUILD_INDEX_SCRIPT=".ospec/tools/build-index-auto.cjs"
|
|
5
|
+
elif [ -f "build-index-auto.cjs" ]; then
|
|
6
|
+
OSPEC_BUILD_INDEX_SCRIPT="build-index-auto.cjs"
|
|
7
|
+
elif [ -f "build-index-auto.js" ]; then
|
|
8
|
+
OSPEC_BUILD_INDEX_SCRIPT="build-index-auto.js"
|
|
9
|
+
else
|
|
10
|
+
echo "[ospec] .ospec/tools/build-index-auto.cjs not found, skip hook check"
|
|
5
11
|
exit 0
|
|
6
12
|
fi
|
|
7
13
|
|
|
8
|
-
node
|
|
14
|
+
node "$OSPEC_BUILD_INDEX_SCRIPT" hook-check pre-commit
|
package/dist/cli.js
CHANGED
|
@@ -224,7 +224,7 @@ const services_1 = require("./services");
|
|
|
224
224
|
|
|
225
225
|
|
|
226
226
|
|
|
227
|
-
const CLI_VERSION = '0.3.
|
|
227
|
+
const CLI_VERSION = '0.3.10';
|
|
228
228
|
|
|
229
229
|
function showInitUsage() {
|
|
230
230
|
console.log('Usage: ospec init [root-dir] [--summary "..."] [--tech-stack node,react] [--architecture "..."] [--document-language en-US|zh-CN|ja-JP|ar]');
|
|
@@ -4,7 +4,6 @@ const fs = require('fs');
|
|
|
4
4
|
const fsp = require('fs/promises');
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const { spawnSync } = require('child_process');
|
|
7
|
-
const matter = require('gray-matter');
|
|
8
7
|
|
|
9
8
|
const SKIP_DIRS = new Set(['node_modules', 'dist', '.git', 'changes', 'for-ai']);
|
|
10
9
|
const INDEX_FILE = 'SKILL.index.json';
|
|
@@ -469,7 +468,7 @@ function analyzeWorkflowChecklistDocument(content, options) {
|
|
|
469
468
|
|
|
470
469
|
if (hasFrontmatter) {
|
|
471
470
|
try {
|
|
472
|
-
parsed =
|
|
471
|
+
parsed = parseFrontmatter(content, { strict: true });
|
|
473
472
|
} catch (error) {
|
|
474
473
|
parseError = error;
|
|
475
474
|
}
|
|
@@ -484,8 +483,8 @@ function analyzeWorkflowChecklistDocument(content, options) {
|
|
|
484
483
|
const missingActivatedSteps = optionalStepsFieldValid
|
|
485
484
|
? options.activatedSteps.filter(step => !optionalSteps.includes(step))
|
|
486
485
|
: [...options.activatedSteps];
|
|
487
|
-
const checklistItems = parsed?.
|
|
488
|
-
const uncheckedItems = parsed?.
|
|
486
|
+
const checklistItems = parsed?.body.match(/^\s*-\s+\[(?: |x|X)\]\s+.+$/gm) ?? [];
|
|
487
|
+
const uncheckedItems = parsed?.body.match(/^\s*-\s+\[ \]\s+.+$/gm) ?? [];
|
|
489
488
|
const checklistStructureValid = checklistItems.length > 0;
|
|
490
489
|
|
|
491
490
|
let frontmatterMessage = `${options.name} frontmatter parsed successfully`;
|
|
@@ -553,40 +552,61 @@ function normalizeLineEndings(content) {
|
|
|
553
552
|
return String(content || '').replace(/\r\n?/g, '\n');
|
|
554
553
|
}
|
|
555
554
|
|
|
556
|
-
function parseFrontmatter(content) {
|
|
557
|
-
const
|
|
555
|
+
function parseFrontmatter(content, options = {}) {
|
|
556
|
+
const normalizedContent = normalizeLineEndings(content);
|
|
557
|
+
const match = normalizedContent.match(/^---\n([\s\S]*?)\n---(?:\n|$)/);
|
|
558
558
|
if (!match) {
|
|
559
|
-
return { data: {}, body:
|
|
559
|
+
return { data: {}, body: normalizedContent };
|
|
560
560
|
}
|
|
561
561
|
|
|
562
562
|
const data = {};
|
|
563
|
-
const lines = match[1].split(
|
|
563
|
+
const lines = match[1].split('\n');
|
|
564
564
|
let currentKey = null;
|
|
565
565
|
|
|
566
|
-
for (
|
|
566
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
567
|
+
const line = lines[index];
|
|
568
|
+
const lineNumber = index + 1;
|
|
569
|
+
const trimmed = line.trim();
|
|
570
|
+
|
|
571
|
+
if (trimmed === '' || trimmed.startsWith('#')) {
|
|
572
|
+
continue;
|
|
573
|
+
}
|
|
574
|
+
|
|
567
575
|
if (/^\s*-\s+/.test(line) && currentKey) {
|
|
568
576
|
if (!Array.isArray(data[currentKey])) {
|
|
569
577
|
data[currentKey] = [];
|
|
570
578
|
}
|
|
571
|
-
data[currentKey].push(
|
|
579
|
+
data[currentKey].push(
|
|
580
|
+
parseValue(line.replace(/^\s*-\s+/, '').trim(), options, {
|
|
581
|
+
key: currentKey,
|
|
582
|
+
lineNumber,
|
|
583
|
+
})
|
|
584
|
+
);
|
|
572
585
|
continue;
|
|
573
586
|
}
|
|
574
587
|
|
|
588
|
+
if (/^\s*-\s+/.test(line) && options.strict) {
|
|
589
|
+
throw createFrontmatterParseError('Unexpected list item outside an array field', lineNumber);
|
|
590
|
+
}
|
|
591
|
+
|
|
575
592
|
const keyMatch = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
|
|
576
593
|
if (!keyMatch) {
|
|
594
|
+
if (options.strict) {
|
|
595
|
+
throw createFrontmatterParseError(`Invalid frontmatter line: ${trimmed}`, lineNumber);
|
|
596
|
+
}
|
|
577
597
|
currentKey = null;
|
|
578
598
|
continue;
|
|
579
599
|
}
|
|
580
600
|
|
|
581
601
|
const key = keyMatch[1];
|
|
582
602
|
const rawValue = keyMatch[2].trim();
|
|
583
|
-
data[key] = parseValue(rawValue);
|
|
603
|
+
data[key] = parseValue(rawValue, options, { key, lineNumber });
|
|
584
604
|
currentKey = Array.isArray(data[key]) && rawValue === '' ? key : null;
|
|
585
605
|
}
|
|
586
606
|
|
|
587
607
|
return {
|
|
588
608
|
data,
|
|
589
|
-
body:
|
|
609
|
+
body: normalizedContent.slice(match[0].length),
|
|
590
610
|
};
|
|
591
611
|
}
|
|
592
612
|
|
|
@@ -609,7 +629,7 @@ function isValidFrontmatterField(value, type) {
|
|
|
609
629
|
return false;
|
|
610
630
|
}
|
|
611
631
|
|
|
612
|
-
function parseValue(rawValue) {
|
|
632
|
+
function parseValue(rawValue, options = {}, context = {}) {
|
|
613
633
|
if (rawValue === '') {
|
|
614
634
|
return [];
|
|
615
635
|
}
|
|
@@ -622,25 +642,102 @@ function parseValue(rawValue) {
|
|
|
622
642
|
if (rawValue === 'false') {
|
|
623
643
|
return false;
|
|
624
644
|
}
|
|
645
|
+
if (options.strict) {
|
|
646
|
+
validateFrontmatterValue(rawValue, context);
|
|
647
|
+
}
|
|
625
648
|
if (/^\[(.*)\]$/.test(rawValue)) {
|
|
626
649
|
const inner = rawValue.slice(1, -1).trim();
|
|
627
650
|
if (!inner) {
|
|
628
651
|
return [];
|
|
629
652
|
}
|
|
630
653
|
|
|
631
|
-
return inner
|
|
632
|
-
.split(',')
|
|
633
|
-
.map(item => stripQuotes(item.trim()))
|
|
634
|
-
.filter(Boolean);
|
|
654
|
+
return splitInlineArray(inner, options, context);
|
|
635
655
|
}
|
|
636
656
|
|
|
637
657
|
return stripQuotes(rawValue);
|
|
638
658
|
}
|
|
639
659
|
|
|
660
|
+
function validateFrontmatterValue(rawValue, context) {
|
|
661
|
+
const startsArray = rawValue.startsWith('[');
|
|
662
|
+
const endsArray = rawValue.endsWith(']');
|
|
663
|
+
if (startsArray !== endsArray) {
|
|
664
|
+
throw createFrontmatterParseError(
|
|
665
|
+
`Unterminated inline array for ${context.key || 'field'}`,
|
|
666
|
+
context.lineNumber
|
|
667
|
+
);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
if (!rawValue) {
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
const quote = rawValue[0];
|
|
675
|
+
if ((quote === '"' || quote === "'") && rawValue[rawValue.length - 1] !== quote) {
|
|
676
|
+
throw createFrontmatterParseError(
|
|
677
|
+
`Unterminated quoted string for ${context.key || 'field'}`,
|
|
678
|
+
context.lineNumber
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
function splitInlineArray(inner, options = {}, context = {}) {
|
|
684
|
+
const values = [];
|
|
685
|
+
let current = '';
|
|
686
|
+
let activeQuote = null;
|
|
687
|
+
|
|
688
|
+
for (let index = 0; index < inner.length; index += 1) {
|
|
689
|
+
const char = inner[index];
|
|
690
|
+
if (activeQuote) {
|
|
691
|
+
current += char;
|
|
692
|
+
if (char === activeQuote && inner[index - 1] !== '\\') {
|
|
693
|
+
activeQuote = null;
|
|
694
|
+
}
|
|
695
|
+
continue;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
if (char === '"' || char === "'") {
|
|
699
|
+
activeQuote = char;
|
|
700
|
+
current += char;
|
|
701
|
+
continue;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
if (char === ',') {
|
|
705
|
+
const parsed = parseValue(current.trim(), {}, context);
|
|
706
|
+
if (parsed !== '') {
|
|
707
|
+
values.push(parsed);
|
|
708
|
+
}
|
|
709
|
+
current = '';
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
current += char;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
if (activeQuote && options.strict) {
|
|
717
|
+
throw createFrontmatterParseError(
|
|
718
|
+
`Unterminated quoted string in inline array for ${context.key || 'field'}`,
|
|
719
|
+
context.lineNumber
|
|
720
|
+
);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
const parsed = parseValue(current.trim(), {}, context);
|
|
724
|
+
if (parsed !== '') {
|
|
725
|
+
values.push(parsed);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
return values.filter(value => value !== '');
|
|
729
|
+
}
|
|
730
|
+
|
|
640
731
|
function stripQuotes(value) {
|
|
641
732
|
return value.replace(/^['"]|['"]$/g, '');
|
|
642
733
|
}
|
|
643
734
|
|
|
735
|
+
function createFrontmatterParseError(message, lineNumber) {
|
|
736
|
+
const error = new Error(lineNumber ? `line ${lineNumber}: ${message}` : message);
|
|
737
|
+
error.name = 'FrontmatterParseError';
|
|
738
|
+
return error;
|
|
739
|
+
}
|
|
740
|
+
|
|
644
741
|
function extractSections(content) {
|
|
645
742
|
const sections = {};
|
|
646
743
|
const matches = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clawplays/ospec-cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.10",
|
|
4
4
|
"description": "Official OSpec CLI package for spec-driven development (SDD) and document-driven development in AI coding agent and CLI workflows.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|