@waoooo/claude-skills 1.0.0
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 +48 -0
- package/package.json +28 -0
- package/registry.json +431 -0
- package/skills/acceptance-review/SKILL.md +537 -0
- package/skills/all-plan/SKILL.md +19 -0
- package/skills/all-plan/references/flow.md +750 -0
- package/skills/api-docs-generate/SKILL.md +204 -0
- package/skills/api-docs-generate/assets/templates/api.mdx +140 -0
- package/skills/api-docs-generate/scripts/generate-api-docs.ts +308 -0
- package/skills/ask/SKILL.md +42 -0
- package/skills/autonew/SKILL.md +34 -0
- package/skills/capability-analyze/SKILL.md +300 -0
- package/skills/capability-analyze/scripts/analyze-capabilities.ts +531 -0
- package/skills/capability-docs-generate/SKILL.md +155 -0
- package/skills/capability-docs-generate/assets/templates/capability.mdx +271 -0
- package/skills/capability-docs-generate/scripts/generate-capability-docs.ts +358 -0
- package/skills/capability-tree-query/SKILL.md +112 -0
- package/skills/capability-tree-query/scripts/build-capability-tree.ts +402 -0
- package/skills/changelog-generator/SKILL.md +104 -0
- package/skills/continue/SKILL.md +39 -0
- package/skills/continue/agents/openai.yaml +3 -0
- package/skills/creating-skills/SKILL.md +158 -0
- package/skills/creating-skills/references/official_best_practices.md +128 -0
- package/skills/creating-skills/references/skill_examples.md +199 -0
- package/skills/docx/LICENSE.txt +30 -0
- package/skills/docx/SKILL.md +197 -0
- package/skills/docx/docx-js.md +350 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/skills/docx/ooxml/schemas/mce/mc.xsd +75 -0
- package/skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
- package/skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
- package/skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
- package/skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/skills/docx/ooxml/scripts/pack.py +159 -0
- package/skills/docx/ooxml/scripts/unpack.py +29 -0
- package/skills/docx/ooxml/scripts/validate.py +69 -0
- package/skills/docx/ooxml/scripts/validation/__init__.py +15 -0
- package/skills/docx/ooxml/scripts/validation/base.py +951 -0
- package/skills/docx/ooxml/scripts/validation/docx.py +274 -0
- package/skills/docx/ooxml/scripts/validation/pptx.py +315 -0
- package/skills/docx/ooxml/scripts/validation/redlining.py +279 -0
- package/skills/docx/ooxml.md +610 -0
- package/skills/docx/scripts/__init__.py +1 -0
- package/skills/docx/scripts/document.py +1276 -0
- package/skills/docx/scripts/templates/comments.xml +3 -0
- package/skills/docx/scripts/templates/commentsExtended.xml +3 -0
- package/skills/docx/scripts/templates/commentsExtensible.xml +3 -0
- package/skills/docx/scripts/templates/commentsIds.xml +3 -0
- package/skills/docx/scripts/templates/people.xml +3 -0
- package/skills/docx/scripts/utilities.py +374 -0
- package/skills/git-branch-create/SKILL.md +170 -0
- package/skills/git-branch-merge/SKILL.md +176 -0
- package/skills/git-commit/SKILL.md +56 -0
- package/skills/git-commit/references/commit_examples.md +311 -0
- package/skills/github-actions-trigger/SKILL.md +367 -0
- package/skills/github-issue-create/SKILL.md +294 -0
- package/skills/github-pr-creation/SKILL.md +137 -0
- package/skills/github-pr-creation/references/pr_templates.md +187 -0
- package/skills/github-pr-merge/SKILL.md +112 -0
- package/skills/github-pr-review/SKILL.md +110 -0
- package/skills/github-pr-review/references/severity_guide.md +168 -0
- package/skills/job-create/SKILL.md +294 -0
- package/skills/job-create/scripts/create-job.ts +105 -0
- package/skills/job-workflow/SKILL.md +212 -0
- package/skills/keyword-extract/SKILL.md +229 -0
- package/skills/mcp-builder/LICENSE.txt +202 -0
- package/skills/mcp-builder/SKILL.md +236 -0
- package/skills/mcp-builder/reference/evaluation.md +602 -0
- package/skills/mcp-builder/reference/mcp_best_practices.md +249 -0
- package/skills/mcp-builder/reference/node_mcp_server.md +970 -0
- package/skills/mcp-builder/reference/python_mcp_server.md +719 -0
- package/skills/mcp-builder/scripts/connections.py +151 -0
- package/skills/mcp-builder/scripts/evaluation.py +373 -0
- package/skills/mcp-builder/scripts/example_evaluation.xml +22 -0
- package/skills/mcp-builder/scripts/requirements.txt +2 -0
- package/skills/mdx-to-openspec/SKILL.md +827 -0
- package/skills/mdx-to-openspec/scripts/mdx-to-openspec.ts +320 -0
- package/skills/mounted/SKILL.md +20 -0
- package/skills/multi-model-review/SKILL.md +459 -0
- package/skills/notion-automation/SKILL.md +215 -0
- package/skills/openspec-review/SKILL.md +513 -0
- package/skills/pdf/LICENSE.txt +30 -0
- package/skills/pdf/SKILL.md +294 -0
- package/skills/pdf/forms.md +205 -0
- package/skills/pdf/reference.md +612 -0
- package/skills/pdf/scripts/check_bounding_boxes.py +70 -0
- package/skills/pdf/scripts/check_bounding_boxes_test.py +226 -0
- package/skills/pdf/scripts/check_fillable_fields.py +12 -0
- package/skills/pdf/scripts/convert_pdf_to_images.py +35 -0
- package/skills/pdf/scripts/create_validation_image.py +41 -0
- package/skills/pdf/scripts/extract_form_field_info.py +152 -0
- package/skills/pdf/scripts/fill_fillable_fields.py +114 -0
- package/skills/pdf/scripts/fill_pdf_form_with_annotations.py +108 -0
- package/skills/pend/SKILL.md +33 -0
- package/skills/ping/SKILL.md +39 -0
- package/skills/pptx/LICENSE.txt +30 -0
- package/skills/pptx/SKILL.md +484 -0
- package/skills/pptx/html2pptx.md +625 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/skills/pptx/ooxml/schemas/mce/mc.xsd +75 -0
- package/skills/pptx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
- package/skills/pptx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
- package/skills/pptx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
- package/skills/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/skills/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/skills/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/skills/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/skills/pptx/ooxml/scripts/pack.py +159 -0
- package/skills/pptx/ooxml/scripts/unpack.py +29 -0
- package/skills/pptx/ooxml/scripts/validate.py +69 -0
- package/skills/pptx/ooxml/scripts/validation/__init__.py +15 -0
- package/skills/pptx/ooxml/scripts/validation/base.py +951 -0
- package/skills/pptx/ooxml/scripts/validation/docx.py +274 -0
- package/skills/pptx/ooxml/scripts/validation/pptx.py +315 -0
- package/skills/pptx/ooxml/scripts/validation/redlining.py +279 -0
- package/skills/pptx/ooxml.md +427 -0
- package/skills/pptx/scripts/html2pptx.js +979 -0
- package/skills/pptx/scripts/inventory.py +1020 -0
- package/skills/pptx/scripts/rearrange.py +231 -0
- package/skills/pptx/scripts/replace.py +385 -0
- package/skills/pptx/scripts/thumbnail.py +450 -0
- package/skills/progress-update/SKILL.md +394 -0
- package/skills/progress-update/scripts/update-progress.ts +221 -0
- package/skills/release-automation/SKILL.md +306 -0
- package/skills/requirement-parse/SKILL.md +212 -0
- package/skills/requirement-parse/scripts/infer-builder-config.ts +346 -0
- package/skills/requirement-parse/scripts/merge-requirements.ts +228 -0
- package/skills/requirement-parse/scripts/parse-docs.ts +206 -0
- package/skills/requirement-parse/scripts/parse-openspec.ts +168 -0
- package/skills/roadmap-docs-generate/SKILL.md +483 -0
- package/skills/roadmap-docs-generate/assets/templates/ROADMAP.mdx +75 -0
- package/skills/roadmap-docs-generate/assets/templates/ROADMAP.mdx.template +330 -0
- package/skills/roadmap-docs-generate/assets/templates/TODO.mdx +56 -0
- package/skills/roadmap-docs-generate/assets/templates/TODO.mdx.template +363 -0
- package/skills/roadmap-docs-generate/scripts/json-to-mdx.ts +445 -0
- package/skills/roadmap-docs-generate/scripts/json-to-mdx.ts.backup +411 -0
- package/skills/roadmap-generate/SKILL.md +396 -0
- package/skills/roadmap-generate/scripts/generate-roadmap.ts +496 -0
- package/skills/skill-creator/LICENSE.txt +202 -0
- package/skills/skill-creator/SKILL.md +356 -0
- package/skills/skill-creator/references/output-patterns.md +82 -0
- package/skills/skill-creator/references/workflows.md +28 -0
- package/skills/skill-creator/scripts/init_skill.py +303 -0
- package/skills/skill-creator/scripts/package_skill.py +110 -0
- package/skills/skill-creator/scripts/quick_validate.py +95 -0
- package/skills/spec-generate/SKILL.md +408 -0
- package/skills/spec-generate/scripts/generate-specs.ts +538 -0
- package/skills/spec-generate/scripts/generate-tasks.ts +174 -0
- package/skills/specs-review/SKILL.md +370 -0
- package/skills/task-execute/SKILL.md +399 -0
- package/skills/task-update-status/SKILL.md +349 -0
- package/skills/task-update-status/scripts/update-task-status.ts +192 -0
- package/skills/task-verify/SKILL.md +407 -0
- package/skills/ui-ux-pro-max/SKILL.md +386 -0
- package/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/skills/ui-ux-pro-max/data/styles.csv +68 -0
- package/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/skills/ui-ux-pro-max/scripts/core.py +253 -0
- package/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/skills/ui-ux-pro-max/scripts/search.py +114 -0
- package/skills/vercel-automation/SKILL.md +226 -0
- package/skills/webapp-testing/LICENSE.txt +202 -0
- package/skills/webapp-testing/SKILL.md +96 -0
- package/skills/webapp-testing/examples/console_logging.py +35 -0
- package/skills/webapp-testing/examples/element_discovery.py +40 -0
- package/skills/webapp-testing/examples/static_html_automation.py +33 -0
- package/skills/webapp-testing/scripts/with_server.py +106 -0
- package/skills/worktree-manager/SKILL.md +725 -0
- package/skills/worktree-manager/config.json +15 -0
- package/skills/worktree-manager/scripts/allocate-ports.sh +100 -0
- package/skills/worktree-manager/scripts/cleanup.sh +185 -0
- package/skills/worktree-manager/scripts/launch-agent.sh +155 -0
- package/skills/worktree-manager/scripts/register.sh +125 -0
- package/skills/worktree-manager/scripts/release-ports.sh +48 -0
- package/skills/worktree-manager/scripts/status.sh +169 -0
- package/skills/worktree-manager/scripts/sync.sh +168 -0
- package/skills/worktree-manager/templates/worktree.json +23 -0
- package/skills/xlsx/LICENSE.txt +30 -0
- package/skills/xlsx/SKILL.md +289 -0
- package/skills/xlsx/recalc.py +178 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Infer Builder Configuration from MDX Content
|
|
3
|
+
*
|
|
4
|
+
* This script analyzes MDX content and automatically infers:
|
|
5
|
+
* - Required capabilities (L1/L2/L3)
|
|
6
|
+
* - Layer hints
|
|
7
|
+
*
|
|
8
|
+
* Uses simple keyword matching (keywords from MDX frontmatter vs capability tree keywords)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import fs from 'fs';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import type { UnifiedRequirement } from '../types/unified-requirements';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Load capability definitions from capability tree
|
|
17
|
+
*/
|
|
18
|
+
function loadCapabilityDefinitions(): Array<{
|
|
19
|
+
id: string;
|
|
20
|
+
name: string;
|
|
21
|
+
tags: string[];
|
|
22
|
+
layer: string;
|
|
23
|
+
}> {
|
|
24
|
+
const capabilityTreePath = path.join(process.cwd(), 'exports/capability-tree/capability-tree.json');
|
|
25
|
+
|
|
26
|
+
if (!fs.existsSync(capabilityTreePath)) {
|
|
27
|
+
throw new Error(`
|
|
28
|
+
ā Capability tree not found: ${capabilityTreePath}
|
|
29
|
+
|
|
30
|
+
Please run: pnpm capability:build-tree
|
|
31
|
+
|
|
32
|
+
Or create the capability tree manually.
|
|
33
|
+
`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const treeData = JSON.parse(fs.readFileSync(capabilityTreePath, 'utf-8'));
|
|
38
|
+
const capabilities: Array<{ id: string; name: string; tags: string[]; layer: string }> = [];
|
|
39
|
+
|
|
40
|
+
// Flatten capability tree
|
|
41
|
+
function flattenTree(nodes: any[], layer: string = 'L1') {
|
|
42
|
+
for (const node of nodes) {
|
|
43
|
+
if (node.id && node.name) {
|
|
44
|
+
capabilities.push({
|
|
45
|
+
id: node.id,
|
|
46
|
+
name: node.name,
|
|
47
|
+
tags: node.tags || [node.id, node.name.toLowerCase()],
|
|
48
|
+
layer: node.layer || layer,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
if (node.children && node.children.length > 0) {
|
|
52
|
+
flattenTree(node.children, node.layer || layer);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
flattenTree(treeData.capabilities || treeData);
|
|
58
|
+
|
|
59
|
+
if (capabilities.length === 0) {
|
|
60
|
+
throw new Error('Capability tree is empty');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
console.log(` Loaded ${capabilities.length} capabilities from tree`);
|
|
64
|
+
|
|
65
|
+
return capabilities;
|
|
66
|
+
} catch (error) {
|
|
67
|
+
throw new Error(`Failed to load capability tree: ${error}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Extract keywords from text (simple tokenization)
|
|
73
|
+
*/
|
|
74
|
+
function extractTags(text: string): string[] {
|
|
75
|
+
// Remove special characters and split by whitespace
|
|
76
|
+
const words = text
|
|
77
|
+
.toLowerCase()
|
|
78
|
+
.replace(/[^\w\s-]/g, ' ')
|
|
79
|
+
.split(/\s+/)
|
|
80
|
+
.filter(w => w.length > 2); // Filter out short words
|
|
81
|
+
|
|
82
|
+
// Remove duplicates and return
|
|
83
|
+
return Array.from(new Set(words));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Match keywords with capabilities (simple intersection)
|
|
88
|
+
*/
|
|
89
|
+
function matchCapabilities(
|
|
90
|
+
docKeywords: string[],
|
|
91
|
+
capabilities: Array<{ id: string; name: string; tags: string[]; layer: string }>
|
|
92
|
+
): Array<{ id: string; layer: string; score: number }> {
|
|
93
|
+
const matches: Array<{ id: string; layer: string; score: number }> = [];
|
|
94
|
+
|
|
95
|
+
// Normalize keywords to lowercase
|
|
96
|
+
const normalizedKeywords = docKeywords.map(k => k.toLowerCase());
|
|
97
|
+
|
|
98
|
+
for (const cap of capabilities) {
|
|
99
|
+
// Normalize capability tags to lowercase
|
|
100
|
+
const normalizedTags = cap.tags.map(t => t.toLowerCase());
|
|
101
|
+
|
|
102
|
+
// Calculate intersection (how many keywords match)
|
|
103
|
+
const matchCount = normalizedKeywords.filter(k => normalizedTags.includes(k)).length;
|
|
104
|
+
|
|
105
|
+
if (matchCount > 0) {
|
|
106
|
+
// Score = match count / total tags (normalized)
|
|
107
|
+
const score = matchCount / normalizedTags.length;
|
|
108
|
+
matches.push({
|
|
109
|
+
id: cap.id,
|
|
110
|
+
layer: cap.layer,
|
|
111
|
+
score,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Sort by score (descending)
|
|
117
|
+
return matches.sort((a, b) => b.score - a.score);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Infer required capabilities from requirements
|
|
122
|
+
*/
|
|
123
|
+
export function inferCapabilities(requirements: UnifiedRequirement[], docKeywords?: string[]): {
|
|
124
|
+
L1: string[];
|
|
125
|
+
L2: string[];
|
|
126
|
+
L3: string[];
|
|
127
|
+
} {
|
|
128
|
+
// Load capability definitions
|
|
129
|
+
const capabilityDefs = loadCapabilityDefinitions();
|
|
130
|
+
|
|
131
|
+
// Use provided keywords or extract from requirements
|
|
132
|
+
let keywords: string[];
|
|
133
|
+
if (docKeywords && docKeywords.length > 0) {
|
|
134
|
+
keywords = docKeywords;
|
|
135
|
+
console.log('š Using provided keywords:', keywords.slice(0, 10).join(', '), '...');
|
|
136
|
+
} else {
|
|
137
|
+
// Fallback: extract from requirements
|
|
138
|
+
const allText = requirements
|
|
139
|
+
.map(req => `${req.title} ${req.description} ${req.acceptance?.join(' ')} ${req.tasks?.join(' ')}`)
|
|
140
|
+
.join(' ');
|
|
141
|
+
keywords = extractTags(allText);
|
|
142
|
+
console.log('š Extracted keywords:', keywords.slice(0, 10).join(', '), '...');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Match capabilities
|
|
146
|
+
const matches = matchCapabilities(keywords, capabilityDefs);
|
|
147
|
+
console.log('šÆ Matched capabilities:', matches.slice(0, 5).map(m => `${m.id}(${m.score.toFixed(2)})`).join(', '));
|
|
148
|
+
|
|
149
|
+
// Group by layer
|
|
150
|
+
const capabilities = {
|
|
151
|
+
L1: new Set<string>(),
|
|
152
|
+
L2: new Set<string>(),
|
|
153
|
+
L3: new Set<string>(),
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// Take top matches (score > 0.1)
|
|
157
|
+
matches
|
|
158
|
+
.filter(m => m.score > 0.1)
|
|
159
|
+
.forEach(match => {
|
|
160
|
+
const layer = match.layer as 'L1' | 'L2' | 'L3';
|
|
161
|
+
if (capabilities[layer]) {
|
|
162
|
+
capabilities[layer].add(match.id);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Also check explicit hints from requirements
|
|
167
|
+
requirements.forEach(req => {
|
|
168
|
+
if (req.layerHint && req.capabilityHints) {
|
|
169
|
+
req.capabilityHints.forEach(capId => {
|
|
170
|
+
const layer = req.layerHint as 'L1' | 'L2' | 'L3';
|
|
171
|
+
if (capabilities[layer]) {
|
|
172
|
+
capabilities[layer].add(capId);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
L1: Array.from(capabilities.L1),
|
|
180
|
+
L2: Array.from(capabilities.L2),
|
|
181
|
+
L3: Array.from(capabilities.L3),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Infer project name from requirements
|
|
187
|
+
*/
|
|
188
|
+
function inferProjectName(requirements: UnifiedRequirement[]): string {
|
|
189
|
+
// Try to extract from first requirement's source file
|
|
190
|
+
const firstReq = requirements[0];
|
|
191
|
+
if (firstReq?.sourceFiles?.[0]) {
|
|
192
|
+
const filename = firstReq.sourceFiles[0].split('/').pop()?.replace('.mdx', '');
|
|
193
|
+
if (filename) {
|
|
194
|
+
return filename
|
|
195
|
+
.replace(/-/g, ' ')
|
|
196
|
+
.replace(/\b\w/g, l => l.toUpperCase());
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Fallback: use common words from titles
|
|
201
|
+
const words = requirements
|
|
202
|
+
.map(r => r.title.split(/\s+/))
|
|
203
|
+
.flat()
|
|
204
|
+
.filter(w => w.length > 2);
|
|
205
|
+
|
|
206
|
+
const wordCount = new Map<string, number>();
|
|
207
|
+
words.forEach(w => {
|
|
208
|
+
wordCount.set(w, (wordCount.get(w) || 0) + 1);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
const mostCommon = Array.from(wordCount.entries())
|
|
212
|
+
.sort((a, b) => b[1] - a[1])[0];
|
|
213
|
+
|
|
214
|
+
return mostCommon ? `${mostCommon[0]} System` : 'Untitled Project';
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Generate Builder configuration from requirements
|
|
219
|
+
* Output format: UnifiedRequirements (for merging)
|
|
220
|
+
*/
|
|
221
|
+
export function generateBuilderConfig(
|
|
222
|
+
requirements: UnifiedRequirement[],
|
|
223
|
+
docKeywords?: string[]
|
|
224
|
+
): {
|
|
225
|
+
version: string;
|
|
226
|
+
sources: {
|
|
227
|
+
builder: {
|
|
228
|
+
method: string;
|
|
229
|
+
capabilities: string[];
|
|
230
|
+
extractedAt: string;
|
|
231
|
+
keywords?: string[];
|
|
232
|
+
};
|
|
233
|
+
};
|
|
234
|
+
requirements: UnifiedRequirement[];
|
|
235
|
+
} {
|
|
236
|
+
const capabilities = inferCapabilities(requirements, docKeywords);
|
|
237
|
+
const allCapabilities = [...capabilities.L1, ...capabilities.L2, ...capabilities.L3];
|
|
238
|
+
|
|
239
|
+
// Generate requirements from inferred capabilities
|
|
240
|
+
const builderRequirements: UnifiedRequirement[] = [];
|
|
241
|
+
|
|
242
|
+
// Add L1 capabilities
|
|
243
|
+
capabilities.L1.forEach(capId => {
|
|
244
|
+
builderRequirements.push({
|
|
245
|
+
reqId: `REQ-BUILDER-${capId.toUpperCase()}`,
|
|
246
|
+
title: `éę ${capId} č½å`,
|
|
247
|
+
description: `ä½æēØ ${capId} č½å`,
|
|
248
|
+
source: 'builder',
|
|
249
|
+
type: 'feature',
|
|
250
|
+
status: 'ready',
|
|
251
|
+
layerHint: 'L1',
|
|
252
|
+
capabilityHints: [capId],
|
|
253
|
+
acceptance: [],
|
|
254
|
+
tasks: [],
|
|
255
|
+
blockedBy: [],
|
|
256
|
+
blocks: [],
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// Add L2 capabilities
|
|
261
|
+
capabilities.L2.forEach(capId => {
|
|
262
|
+
builderRequirements.push({
|
|
263
|
+
reqId: `REQ-BUILDER-${capId.toUpperCase()}`,
|
|
264
|
+
title: `éę ${capId} č½å`,
|
|
265
|
+
description: `ä½æēØ ${capId} č½å`,
|
|
266
|
+
source: 'builder',
|
|
267
|
+
type: 'feature',
|
|
268
|
+
status: 'ready',
|
|
269
|
+
layerHint: 'L2',
|
|
270
|
+
capabilityHints: [capId],
|
|
271
|
+
acceptance: [],
|
|
272
|
+
tasks: [],
|
|
273
|
+
blockedBy: [],
|
|
274
|
+
blocks: [],
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// Add L3 capabilities
|
|
279
|
+
capabilities.L3.forEach(capId => {
|
|
280
|
+
builderRequirements.push({
|
|
281
|
+
reqId: `REQ-BUILDER-${capId.toUpperCase()}`,
|
|
282
|
+
title: `éę ${capId} č½å`,
|
|
283
|
+
description: `ä½æēØ ${capId} č½å`,
|
|
284
|
+
source: 'builder',
|
|
285
|
+
type: 'feature',
|
|
286
|
+
status: 'ready',
|
|
287
|
+
layerHint: 'L3',
|
|
288
|
+
capabilityHints: [capId],
|
|
289
|
+
acceptance: [],
|
|
290
|
+
tasks: [],
|
|
291
|
+
blockedBy: [],
|
|
292
|
+
blocks: [],
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
return {
|
|
297
|
+
version: '1.0.0',
|
|
298
|
+
sources: {
|
|
299
|
+
builder: {
|
|
300
|
+
method: 'inferred',
|
|
301
|
+
capabilities: allCapabilities,
|
|
302
|
+
extractedAt: new Date().toISOString(),
|
|
303
|
+
keywords: docKeywords, // Include keywords in output
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
requirements: builderRequirements,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* CLI entry point
|
|
312
|
+
*/
|
|
313
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
314
|
+
const args = process.argv.slice(2);
|
|
315
|
+
const inputFile = args[0] || './exports/requirements.pricing-from-docs.json';
|
|
316
|
+
const outputFile = args[1] || './exports/requirements.pricing-from-builder-inferred.json';
|
|
317
|
+
|
|
318
|
+
console.log('š Inferring Builder configuration from requirements...');
|
|
319
|
+
console.log(' Input:', inputFile);
|
|
320
|
+
|
|
321
|
+
// Load requirements
|
|
322
|
+
const data = JSON.parse(fs.readFileSync(inputFile, 'utf-8'));
|
|
323
|
+
const requirements = data.requirements as UnifiedRequirement[];
|
|
324
|
+
const docKeywords = data.sources?.docs?.keywords as string[] | undefined;
|
|
325
|
+
|
|
326
|
+
if (docKeywords && docKeywords.length > 0) {
|
|
327
|
+
console.log(` Found ${docKeywords.length} keywords from docs`);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Generate config
|
|
331
|
+
const config = generateBuilderConfig(requirements, docKeywords);
|
|
332
|
+
|
|
333
|
+
console.log('\nā
Inferred capabilities:');
|
|
334
|
+
const l1Caps = config.requirements.filter(r => r.layerHint === 'L1').map(r => r.capabilityHints?.[0]).filter(Boolean);
|
|
335
|
+
const l2Caps = config.requirements.filter(r => r.layerHint === 'L2').map(r => r.capabilityHints?.[0]).filter(Boolean);
|
|
336
|
+
const l3Caps = config.requirements.filter(r => r.layerHint === 'L3').map(r => r.capabilityHints?.[0]).filter(Boolean);
|
|
337
|
+
|
|
338
|
+
console.log(' L1:', l1Caps.join(', ') || 'none');
|
|
339
|
+
console.log(' L2:', l2Caps.join(', ') || 'none');
|
|
340
|
+
console.log(' L3:', l3Caps.join(', ') || 'none');
|
|
341
|
+
console.log(` Total: ${config.requirements.length} capability requirements`);
|
|
342
|
+
|
|
343
|
+
// Write output
|
|
344
|
+
fs.writeFileSync(outputFile, JSON.stringify(config, null, 2));
|
|
345
|
+
console.log('\nš Output:', outputFile);
|
|
346
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge Requirements from Multiple Sources
|
|
3
|
+
*
|
|
4
|
+
* This script merges requirements from:
|
|
5
|
+
* - MDX planning documents (narrative, flexible)
|
|
6
|
+
* - Product Builder (structured, technical)
|
|
7
|
+
* - Historical data (preserves status)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import fs from 'fs';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import type { UnifiedRequirement, UnifiedRequirements } from '../types/unified-requirements';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Merge requirements from multiple sources
|
|
16
|
+
*/
|
|
17
|
+
export function mergeRequirements(
|
|
18
|
+
fromDocs?: UnifiedRequirements,
|
|
19
|
+
fromBuilder?: UnifiedRequirements,
|
|
20
|
+
history?: UnifiedRequirements
|
|
21
|
+
): UnifiedRequirements {
|
|
22
|
+
const merged = new Map<string, UnifiedRequirement>();
|
|
23
|
+
|
|
24
|
+
// 1. Load history (preserve status)
|
|
25
|
+
if (history) {
|
|
26
|
+
history.requirements.forEach(req => {
|
|
27
|
+
merged.set(req.reqId, { ...req });
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 2. Merge docs (high priority)
|
|
32
|
+
if (fromDocs) {
|
|
33
|
+
fromDocs.requirements.forEach(req => {
|
|
34
|
+
const existing = merged.get(req.reqId);
|
|
35
|
+
|
|
36
|
+
if (existing) {
|
|
37
|
+
// Preserve completed status
|
|
38
|
+
if (existing.status === 'done') {
|
|
39
|
+
req.status = 'done';
|
|
40
|
+
req.completedAt = existing.completedAt;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Merge source files
|
|
44
|
+
req.sourceFiles = [
|
|
45
|
+
...(existing.sourceFiles || []),
|
|
46
|
+
...(req.sourceFiles || []),
|
|
47
|
+
];
|
|
48
|
+
req.source = 'merged';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
merged.set(req.reqId, req);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// 3. Supplement with builder info (only add missing fields)
|
|
56
|
+
if (fromBuilder) {
|
|
57
|
+
fromBuilder.requirements.forEach(req => {
|
|
58
|
+
const existing = merged.get(req.reqId);
|
|
59
|
+
|
|
60
|
+
if (!existing) {
|
|
61
|
+
// New requirement from builder
|
|
62
|
+
merged.set(req.reqId, req);
|
|
63
|
+
} else {
|
|
64
|
+
// Supplement technical fields
|
|
65
|
+
if (!existing.layerHint) existing.layerHint = req.layerHint;
|
|
66
|
+
if (!existing.capabilityHints) existing.capabilityHints = req.capabilityHints;
|
|
67
|
+
if (!existing.module) existing.module = req.module;
|
|
68
|
+
if (!existing.modules) existing.modules = req.modules;
|
|
69
|
+
existing.source = 'merged';
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 4. Calculate dependencies and execution order
|
|
75
|
+
const requirements = Array.from(merged.values());
|
|
76
|
+
calculateBlockedBy(requirements);
|
|
77
|
+
const executionOrder = calculateExecutionOrder(requirements);
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
version: '1.0.0',
|
|
81
|
+
generatedAt: new Date().toISOString(),
|
|
82
|
+
sources: {
|
|
83
|
+
docs: fromDocs?.sources.docs,
|
|
84
|
+
builder: fromBuilder?.sources.builder,
|
|
85
|
+
},
|
|
86
|
+
requirements,
|
|
87
|
+
executionOrder,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Calculate blockedBy and blocks fields
|
|
93
|
+
*/
|
|
94
|
+
function calculateBlockedBy(requirements: UnifiedRequirement[]): void {
|
|
95
|
+
const reqMap = new Map(requirements.map(r => [r.reqId, r]));
|
|
96
|
+
|
|
97
|
+
// Don't clear existing blockedBy/blocks - preserve them from parse-openspec
|
|
98
|
+
// Only initialize if they don't exist
|
|
99
|
+
requirements.forEach(req => {
|
|
100
|
+
if (!req.blockedBy) req.blockedBy = [];
|
|
101
|
+
if (!req.blocks) req.blocks = [];
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Add additional dependencies from dependsOn field
|
|
105
|
+
requirements.forEach(req => {
|
|
106
|
+
if (req.dependsOn) {
|
|
107
|
+
req.dependsOn.forEach(depId => {
|
|
108
|
+
const dep = reqMap.get(depId);
|
|
109
|
+
if (dep) {
|
|
110
|
+
// This req is blocked by dep
|
|
111
|
+
if (dep.status !== 'done' && !req.blockedBy!.includes(depId)) {
|
|
112
|
+
req.blockedBy!.push(depId);
|
|
113
|
+
}
|
|
114
|
+
// dep blocks this req
|
|
115
|
+
if (!dep.blocks) dep.blocks = [];
|
|
116
|
+
if (!dep.blocks.includes(req.reqId)) {
|
|
117
|
+
dep.blocks.push(req.reqId);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Calculate execution order using topological sort
|
|
127
|
+
*/
|
|
128
|
+
function calculateExecutionOrder(requirements: UnifiedRequirement[]): string[] {
|
|
129
|
+
const graph = new Map<string, string[]>();
|
|
130
|
+
const inDegree = new Map<string, number>();
|
|
131
|
+
|
|
132
|
+
// Build graph
|
|
133
|
+
requirements.forEach(req => {
|
|
134
|
+
graph.set(req.reqId, req.dependsOn || []);
|
|
135
|
+
inDegree.set(req.reqId, 0);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Calculate in-degrees
|
|
139
|
+
requirements.forEach(req => {
|
|
140
|
+
(req.dependsOn || []).forEach(depId => {
|
|
141
|
+
inDegree.set(depId, (inDegree.get(depId) || 0) + 1);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Topological sort
|
|
146
|
+
const queue: string[] = [];
|
|
147
|
+
const sorted: string[] = [];
|
|
148
|
+
|
|
149
|
+
// Start with nodes that have no dependencies
|
|
150
|
+
inDegree.forEach((degree, reqId) => {
|
|
151
|
+
if (degree === 0) {
|
|
152
|
+
queue.push(reqId);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
while (queue.length > 0) {
|
|
157
|
+
const reqId = queue.shift()!;
|
|
158
|
+
sorted.push(reqId);
|
|
159
|
+
|
|
160
|
+
const deps = graph.get(reqId) || [];
|
|
161
|
+
deps.forEach(depId => {
|
|
162
|
+
const newDegree = (inDegree.get(depId) || 0) - 1;
|
|
163
|
+
inDegree.set(depId, newDegree);
|
|
164
|
+
if (newDegree === 0) {
|
|
165
|
+
queue.push(depId);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Check for cycles
|
|
171
|
+
if (sorted.length !== requirements.length) {
|
|
172
|
+
console.warn('Warning: Circular dependencies detected');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return sorted;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* CLI entry point
|
|
180
|
+
*/
|
|
181
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
182
|
+
const args = process.argv.slice(2);
|
|
183
|
+
|
|
184
|
+
if (args.length < 3) {
|
|
185
|
+
console.error('Usage: tsx merge-requirements.ts <docs.json> <form.json> <output.json>');
|
|
186
|
+
console.error('');
|
|
187
|
+
console.error('Example:');
|
|
188
|
+
console.error(' tsx merge-requirements.ts docs.json form.json unified.json');
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const docsFile = args[0];
|
|
193
|
+
const builderFile = args[1];
|
|
194
|
+
const outputFile = args[2];
|
|
195
|
+
|
|
196
|
+
console.log('š Merging requirements...');
|
|
197
|
+
console.log(' Docs:', docsFile);
|
|
198
|
+
console.log(' Builder:', builderFile);
|
|
199
|
+
|
|
200
|
+
// Load inputs
|
|
201
|
+
const fromDocs = fs.existsSync(docsFile)
|
|
202
|
+
? JSON.parse(fs.readFileSync(docsFile, 'utf-8'))
|
|
203
|
+
: undefined;
|
|
204
|
+
|
|
205
|
+
const fromBuilder = fs.existsSync(builderFile)
|
|
206
|
+
? JSON.parse(fs.readFileSync(builderFile, 'utf-8'))
|
|
207
|
+
: undefined;
|
|
208
|
+
|
|
209
|
+
if (!fromDocs && !fromBuilder) {
|
|
210
|
+
console.error('ā Error: At least one input file must exist');
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Merge (no history for now)
|
|
215
|
+
const result = mergeRequirements(fromDocs, fromBuilder, undefined);
|
|
216
|
+
|
|
217
|
+
console.log(`\nā
Merged ${result.requirements.length} requirements`);
|
|
218
|
+
console.log(` Execution order: ${result.executionOrder?.length || 0} items`);
|
|
219
|
+
|
|
220
|
+
// Write output
|
|
221
|
+
const outputDir = path.dirname(outputFile);
|
|
222
|
+
if (!fs.existsSync(outputDir)) {
|
|
223
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
fs.writeFileSync(outputFile, JSON.stringify(result, null, 2));
|
|
227
|
+
console.log(`\nš Output: ${outputFile}`);
|
|
228
|
+
}
|