bmad-module-skill-forge 2.2.0 → 3.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/.claude-plugin/marketplace.json +1 -1
- package/README.md +49 -49
- package/docs/404.md +10 -8
- package/docs/_data/pinned.yaml +22 -13
- package/docs/_internal/STABILITY.md +58 -35
- package/docs/agents.md +39 -53
- package/docs/architecture.md +65 -51
- package/docs/bmad-synergy.md +53 -51
- package/docs/campaign.md +127 -75
- package/docs/concepts.md +34 -28
- package/docs/examples.md +70 -68
- package/docs/forge-auto.md +34 -31
- package/docs/getting-started.md +118 -57
- package/docs/how-it-works.md +25 -21
- package/docs/index.md +15 -15
- package/docs/skill-model.md +70 -59
- package/docs/troubleshooting.md +135 -18
- package/docs/verifying-a-skill.md +83 -57
- package/docs/why-skf.md +24 -22
- package/docs/workflows.md +274 -110
- package/package.json +18 -10
- package/src/forger/preferences.yaml +8 -1
- package/src/knowledge/agentskills-spec.md +2 -0
- package/src/knowledge/ccc-bridge.md +40 -24
- package/src/knowledge/confidence-tiers.md +9 -5
- package/src/knowledge/overview.md +3 -3
- package/src/knowledge/progressive-capability.md +8 -8
- package/src/knowledge/provenance-tracking.md +9 -5
- package/src/knowledge/qmd-registry.md +2 -2
- package/src/knowledge/skf-knowledge-index.csv +2 -2
- package/src/knowledge/skill-lifecycle.md +18 -11
- package/src/knowledge/split-body-strategy.md +0 -3
- package/src/knowledge/tool-resolution.md +4 -2
- package/src/knowledge/version-paths.md +101 -15
- package/src/module-help.csv +3 -3
- package/src/module.yaml +10 -0
- package/src/shared/data/ast-grep-recipes.yaml +608 -0
- package/src/shared/data/ide-context-files.json +30 -0
- package/src/shared/health-check.md +32 -12
- package/src/shared/references/description-guard-protocol.md +12 -9
- package/src/shared/references/feasibility-report-schema.md +19 -13
- package/src/shared/references/headless-gate-convention.md +55 -3
- package/src/shared/references/output-contract-schema.md +19 -8
- package/src/shared/references/pipeline-contracts.md +94 -29
- package/src/shared/references/tessl-review.md +77 -0
- package/src/shared/scripts/schemas/skf-analyze-result-envelope.v1.json +194 -0
- package/src/shared/scripts/schemas/skf-audit-result-envelope.v1.json +192 -0
- package/src/shared/scripts/schemas/skf-brief-result-envelope.v1.json +32 -4
- package/src/shared/scripts/schemas/skf-campaign-result-envelope.v1.json +174 -0
- package/src/shared/scripts/schemas/skf-create-skill-result-envelope.v1.json +284 -0
- package/src/shared/scripts/schemas/skf-drop-skill-result-envelope.v1.json +164 -0
- package/src/shared/scripts/schemas/skf-export-result-envelope.v1.json +160 -0
- package/src/shared/scripts/schemas/skf-quick-skill-result-envelope.v1.json +261 -0
- package/src/shared/scripts/schemas/skf-refine-architecture-result-envelope.v1.json +192 -0
- package/src/shared/scripts/schemas/skf-rename-skill-result-envelope.v1.json +179 -0
- package/src/shared/scripts/schemas/skf-setup-result-envelope.v1.json +27 -11
- package/src/shared/scripts/schemas/skf-stack-result-envelope.v1.json +176 -0
- package/src/shared/scripts/schemas/skf-test-result-envelope.v1.json +173 -0
- package/src/shared/scripts/schemas/skf-update-result-envelope.v1.json +19 -5
- package/src/shared/scripts/schemas/skf-verify-stack-result-envelope.v1.json +180 -0
- package/src/shared/scripts/schemas/skill-brief.v1.json +34 -0
- package/src/shared/scripts/schemas/workspace-detection.v1.json +1 -1
- package/src/shared/scripts/skf-atomic-write.py +30 -14
- package/src/shared/scripts/skf-build-change-manifest.py +2203 -24
- package/src/shared/scripts/skf-ccc-git-hygiene.py +532 -0
- package/src/shared/scripts/skf-check-unit-records.py +184 -0
- package/src/shared/scripts/skf-check-workspace-drift.py +538 -9
- package/src/shared/scripts/skf-classify-changed-files.py +694 -0
- package/src/shared/scripts/skf-comention-pairs.py +1333 -123
- package/src/shared/scripts/skf-compare-file-hashes.py +1 -1
- package/src/shared/scripts/skf-count-imports.py +1867 -0
- package/src/shared/scripts/skf-count-tokens.py +1 -1
- package/src/shared/scripts/skf-derive-assembly-shape.py +3 -2
- package/src/shared/scripts/skf-description-guard.py +99 -11
- package/src/shared/scripts/skf-detect-docs.py +694 -2
- package/src/shared/scripts/skf-detect-language.py +486 -107
- package/src/shared/scripts/skf-detect-registry.py +887 -0
- package/src/shared/scripts/skf-detect-scripts-assets.py +1 -1
- package/src/shared/scripts/skf-detect-tools.py +209 -27
- package/src/shared/scripts/skf-detect-workspaces.py +506 -30
- package/src/shared/scripts/skf-disqualify-candidates.py +821 -182
- package/src/shared/scripts/skf-emit-brief-result-envelope.py +69 -191
- package/src/shared/scripts/skf-emit-result-envelope.py +1604 -106
- package/src/shared/scripts/skf-enumerate-stack-skills.py +986 -117
- package/src/shared/scripts/skf-extract-public-api.py +4084 -85
- package/src/shared/scripts/skf-extraction-inventory.py +746 -0
- package/src/shared/scripts/skf-extraction-snapshot.py +539 -0
- package/src/shared/scripts/skf-fetch-temporal.py +674 -0
- package/src/shared/scripts/skf-find-cycles.py +97 -29
- package/src/shared/scripts/skf-find-test-report.py +476 -0
- package/src/shared/scripts/skf-forge-tier-rw.py +444 -203
- package/src/shared/scripts/skf-github-fetch.py +365 -0
- package/src/shared/scripts/skf-github-probe.py +661 -0
- package/src/shared/scripts/skf-hash-content.py +166 -12
- package/src/shared/scripts/skf-language-corpora.py +1 -1
- package/src/shared/scripts/skf-load-provenance.py +159 -27
- package/src/shared/scripts/skf-manifest-ops.py +1 -1
- package/src/shared/scripts/skf-merge-ccc-exclusions.py +2307 -223
- package/src/shared/scripts/skf-merge-doc-urls.py +1 -1
- package/src/shared/scripts/skf-names-present.py +221 -0
- package/src/shared/scripts/skf-pair-intersect.py +119 -28
- package/src/shared/scripts/skf-parse-gaps.py +1384 -0
- package/src/shared/scripts/skf-preapply.py +1 -1
- package/src/shared/scripts/skf-preflight.py +161 -27
- package/src/shared/scripts/skf-promote-staged.py +430 -0
- package/src/shared/scripts/skf-provenance-gap-dispatch.py +39 -7
- package/src/shared/scripts/skf-qmd-classify-collections.py +229 -45
- package/src/shared/scripts/skf-quick-batch.py +774 -0
- package/src/shared/scripts/skf-rebuild-managed-sections.py +972 -88
- package/src/shared/scripts/skf-recommend-scope-type.py +323 -156
- package/src/shared/scripts/skf-render-metadata-stats.py +156 -12
- package/src/shared/scripts/skf-render-quick-metadata.py +132 -28
- package/src/shared/scripts/skf-render-stack-metadata.py +976 -0
- package/src/shared/scripts/skf-resolve-authoritative-files.py +128 -13
- package/src/shared/scripts/skf-resolve-package.py +523 -106
- package/src/shared/scripts/skf-rewrite-skill-name.py +433 -47
- package/src/shared/scripts/skf-run-lock.py +577 -0
- package/src/shared/scripts/skf-scan-manifests.py +998 -57
- package/src/shared/scripts/skf-scan-skill-md-structure.py +1341 -37
- package/src/shared/scripts/skf-severity-classify.py +712 -108
- package/src/shared/scripts/skf-shape-detect.py +298 -204
- package/src/shared/scripts/skf-shard-body.py +9 -8
- package/src/shared/scripts/skf-skill-inventory.py +2412 -89
- package/src/shared/scripts/skf-skills-module.py +472 -0
- package/src/shared/scripts/skf-source-tree.py +1796 -0
- package/src/shared/scripts/skf-structural-diff.py +670 -119
- package/src/shared/scripts/skf-tessl-review.py +706 -0
- package/src/shared/scripts/skf-update-active-symlink.py +5 -5
- package/src/shared/scripts/skf-update-run-state.py +624 -0
- package/src/shared/scripts/skf-validate-brief-inputs.py +92 -29
- package/src/shared/scripts/skf-validate-brief-schema.py +4 -1
- package/src/shared/scripts/skf-validate-feasibility-report.py +577 -66
- package/src/shared/scripts/skf-validate-frontmatter.py +103 -26
- package/src/shared/scripts/skf-validate-output.py +712 -64
- package/src/shared/scripts/skf-validate-pins.py +1 -1
- package/src/shared/scripts/skf-verify-no-trace.py +116 -10
- package/src/shared/scripts/skf-verify-provenance-completeness.py +2919 -54
- package/src/shared/scripts/skf-write-skill-brief.py +367 -19
- package/src/shared/tool-requirements.yaml +201 -0
- package/src/skf-analyze-source/SKILL.md +41 -36
- package/src/skf-analyze-source/assets/skill-brief-schema.md +7 -8
- package/src/skf-analyze-source/customize.toml +36 -24
- package/src/skf-analyze-source/references/auto-docs-only.md +58 -20
- package/src/skf-analyze-source/references/continue.md +2 -13
- package/src/skf-analyze-source/references/discover-additional-source.md +147 -0
- package/src/skf-analyze-source/references/generate-briefs.md +121 -55
- package/src/skf-analyze-source/references/headless-contract.md +42 -15
- package/src/skf-analyze-source/references/identify-units.md +96 -110
- package/src/skf-analyze-source/references/init.md +73 -71
- package/src/skf-analyze-source/references/map-and-detect.md +123 -105
- package/src/skf-analyze-source/references/map-unit-exports.md +68 -0
- package/src/skf-analyze-source/references/recommend.md +51 -70
- package/src/skf-analyze-source/references/scan-project.md +60 -84
- package/src/skf-analyze-source/references/scan-root.md +39 -0
- package/src/skf-analyze-source/references/step-auto-scope-coexistence.md +47 -0
- package/src/skf-analyze-source/references/step-auto-scope-corpora.md +53 -0
- package/src/skf-analyze-source/references/step-auto-scope-split.md +91 -0
- package/src/skf-analyze-source/references/step-auto-scope.md +208 -293
- package/src/skf-analyze-source/references/step-shape-detect.md +14 -12
- package/src/skf-analyze-source/references/unit-detection-heuristics.md +37 -42
- package/src/skf-audit-skill/SKILL.md +43 -51
- package/src/skf-audit-skill/assets/drift-report-template.md +27 -3
- package/src/skf-audit-skill/customize.toml +41 -20
- package/src/skf-audit-skill/references/constituent-freshness.md +91 -0
- package/src/skf-audit-skill/references/doc-drift.md +141 -0
- package/src/skf-audit-skill/references/headless-contract.md +29 -0
- package/src/skf-audit-skill/references/init.md +148 -210
- package/src/skf-audit-skill/references/re-index.md +96 -108
- package/src/skf-audit-skill/references/report.md +96 -33
- package/src/skf-audit-skill/references/semantic-diff.md +27 -18
- package/src/skf-audit-skill/references/severity-classify.md +79 -65
- package/src/skf-audit-skill/references/severity-rules.md +15 -6
- package/src/skf-audit-skill/references/structural-diff.md +75 -90
- package/src/skf-audit-skill/references/upstream-checkout.md +77 -0
- package/src/skf-audit-skill/scripts/render-drift-tables.py +685 -0
- package/src/skf-brief-skill/SKILL.md +36 -25
- package/src/skf-brief-skill/assets/description-voice-examples.md +1 -1
- package/src/skf-brief-skill/assets/scope-templates.md +17 -24
- package/src/skf-brief-skill/assets/skill-brief-schema.md +29 -15
- package/src/skf-brief-skill/customize.toml +48 -20
- package/src/skf-brief-skill/references/analyze-target.md +142 -125
- package/src/skf-brief-skill/references/confirm-brief.md +10 -8
- package/src/skf-brief-skill/references/draft-checkpoint.md +36 -17
- package/src/skf-brief-skill/references/gather-intent-ratify.md +68 -0
- package/src/skf-brief-skill/references/gather-intent.md +87 -182
- package/src/skf-brief-skill/references/headless-args.md +84 -16
- package/src/skf-brief-skill/references/health-check.md +1 -1
- package/src/skf-brief-skill/references/invocation-contract.md +18 -13
- package/src/skf-brief-skill/references/portfolio-similarity-check.md +1 -1
- package/src/skf-brief-skill/references/qmd-collection-registration.md +7 -10
- package/src/skf-brief-skill/references/scope-definition.md +123 -47
- package/src/skf-brief-skill/references/step-auto-brief.md +24 -80
- package/src/skf-brief-skill/references/step-auto-validate.md +20 -121
- package/src/skf-brief-skill/references/version-resolution.md +3 -2
- package/src/skf-brief-skill/references/write-brief.md +66 -57
- package/src/skf-campaign/SKILL.md +31 -34
- package/src/skf-campaign/assets/campaign-state-schema.json +25 -4
- package/src/skf-campaign/customize.toml +47 -16
- package/src/skf-campaign/references/campaign-contracts.md +39 -29
- package/src/skf-campaign/references/campaign-directive-spec.md +4 -2
- package/src/skf-campaign/references/health-check.md +7 -16
- package/src/skf-campaign/references/step-01-setup.md +46 -84
- package/src/skf-campaign/references/step-02-strategy.md +31 -30
- package/src/skf-campaign/references/step-03-pins.md +26 -18
- package/src/skf-campaign/references/step-04-provenance.md +24 -23
- package/src/skf-campaign/references/step-05-skill-loop.md +67 -48
- package/src/skf-campaign/references/step-06-batch.md +41 -34
- package/src/skf-campaign/references/step-07-capstone.md +40 -25
- package/src/skf-campaign/references/step-08-verify.md +28 -29
- package/src/skf-campaign/references/step-09-refine.md +29 -30
- package/src/skf-campaign/references/step-10-export.md +48 -36
- package/src/skf-campaign/references/step-11-maintenance.md +31 -34
- package/src/skf-campaign/references/step-resume.md +37 -68
- package/src/skf-campaign/scripts/campaign-deps.py +57 -18
- package/src/skf-campaign/scripts/campaign-parse-manifest.py +361 -54
- package/src/skf-campaign/scripts/campaign-provenance.py +96 -53
- package/src/skf-campaign/scripts/campaign-quality-gate.py +592 -0
- package/src/skf-campaign/scripts/campaign-render-batch.py +257 -49
- package/src/skf-campaign/scripts/campaign-render-kickoff.py +343 -50
- package/src/skf-campaign/scripts/campaign-report.py +204 -37
- package/src/skf-campaign/scripts/campaign-state.py +1128 -0
- package/src/skf-campaign/scripts/campaign-status.py +10 -74
- package/src/skf-campaign/scripts/campaign-validate-pins.py +30 -3
- package/src/skf-campaign/scripts/campaign-validate-state.py +34 -4
- package/src/skf-campaign/templates/campaign-brief-template.yaml +4 -5
- package/src/skf-campaign/templates/campaign-report-template.md +5 -1
- package/src/skf-campaign/templates/kickoff-template.md +0 -14
- package/src/skf-create-skill/SKILL.md +27 -20
- package/src/skf-create-skill/assets/compile-assembly-rules.md +6 -5
- package/src/skf-create-skill/assets/skill-sections.md +15 -5
- package/src/skf-create-skill/customize.toml +20 -8
- package/src/skf-create-skill/references/authoritative-files-protocol.md +41 -42
- package/src/skf-create-skill/references/batch-mode.md +81 -0
- package/src/skf-create-skill/references/ccc-index-check.md +27 -0
- package/src/skf-create-skill/references/compile.md +107 -29
- package/src/skf-create-skill/references/component-extraction.md +113 -91
- package/src/skf-create-skill/references/enrich.md +41 -23
- package/src/skf-create-skill/references/entry-points-by-hand.md +20 -0
- package/src/skf-create-skill/references/extract.md +200 -161
- package/src/skf-create-skill/references/extraction-patterns-by-hand.md +141 -0
- package/src/skf-create-skill/references/extraction-patterns-tracing.md +2 -2
- package/src/skf-create-skill/references/extraction-patterns.md +584 -196
- package/src/skf-create-skill/references/generate-artifacts.md +145 -93
- package/src/skf-create-skill/references/health-check.md +1 -1
- package/src/skf-create-skill/references/load-brief.md +53 -36
- package/src/skf-create-skill/references/relabel-rule.md +12 -0
- package/src/skf-create-skill/references/report.md +28 -59
- package/src/skf-create-skill/references/source-resolution-protocols.md +49 -155
- package/src/skf-create-skill/references/step-auto-shard.md +20 -93
- package/src/skf-create-skill/references/step-doc-rot.md +44 -60
- package/src/skf-create-skill/references/step-doc-sources.md +28 -19
- package/src/skf-create-skill/references/sub/ccc-discover.md +18 -7
- package/src/skf-create-skill/references/sub/fetch-docs.md +106 -59
- package/src/skf-create-skill/references/sub/fetch-temporal.md +53 -166
- package/src/skf-create-skill/references/tier-degradation-rules.md +18 -9
- package/src/skf-create-skill/references/validate.md +173 -99
- package/src/skf-create-skill/scripts/scan-doc-rot.py +232 -99
- package/src/skf-create-stack-skill/SKILL.md +29 -56
- package/src/skf-create-stack-skill/assets/metadata-contract.md +51 -0
- package/src/skf-create-stack-skill/assets/provenance-map-schema.md +15 -15
- package/src/skf-create-stack-skill/assets/stack-skill-template.md +23 -57
- package/src/skf-create-stack-skill/customize.toml +23 -12
- package/src/skf-create-stack-skill/references/compile-stack.md +57 -66
- package/src/skf-create-stack-skill/references/compose-mode-rules.md +24 -44
- package/src/skf-create-stack-skill/references/detect-integrations.md +92 -95
- package/src/skf-create-stack-skill/references/detect-manifests.md +46 -71
- package/src/skf-create-stack-skill/references/generate-output.md +138 -128
- package/src/skf-create-stack-skill/references/init.md +90 -32
- package/src/skf-create-stack-skill/references/integration-patterns.md +1 -17
- package/src/skf-create-stack-skill/references/invocation-contract.md +34 -0
- package/src/skf-create-stack-skill/references/manifest-patterns.md +32 -20
- package/src/skf-create-stack-skill/references/parallel-extract.md +112 -38
- package/src/skf-create-stack-skill/references/rank-and-confirm.md +57 -75
- package/src/skf-create-stack-skill/references/report.md +39 -33
- package/src/skf-create-stack-skill/references/validate.md +20 -39
- package/src/skf-drop-skill/SKILL.md +28 -51
- package/src/skf-drop-skill/customize.toml +44 -30
- package/src/skf-drop-skill/references/execute.md +79 -146
- package/src/skf-drop-skill/references/invocation-contract.md +64 -0
- package/src/skf-drop-skill/references/report.md +46 -30
- package/src/skf-drop-skill/references/select.md +118 -124
- package/src/skf-drop-skill/scripts/dir-sizes.py +54 -26
- package/src/skf-drop-skill/scripts/drop-roster.py +322 -0
- package/src/skf-export-skill/SKILL.md +40 -62
- package/src/skf-export-skill/assets/managed-section-format.md +14 -50
- package/src/skf-export-skill/assets/snippet-format.md +3 -1
- package/src/skf-export-skill/customize.toml +42 -13
- package/src/skf-export-skill/references/generate-snippet.md +54 -46
- package/src/skf-export-skill/references/health-check.md +2 -0
- package/src/skf-export-skill/references/invocation-contract.md +15 -0
- package/src/skf-export-skill/references/load-skill.md +88 -62
- package/src/skf-export-skill/references/manifest-rebuild.md +9 -10
- package/src/skf-export-skill/references/multi-skill-mode.md +4 -4
- package/src/skf-export-skill/references/orphan-context-detection.md +13 -5
- package/src/skf-export-skill/references/orphan-row-detection.md +31 -22
- package/src/skf-export-skill/references/package.md +15 -73
- package/src/skf-export-skill/references/preflight-snippet-root-probe.md +51 -70
- package/src/skf-export-skill/references/result-envelope.md +31 -16
- package/src/skf-export-skill/references/summary.md +41 -17
- package/src/skf-export-skill/references/token-report.md +4 -12
- package/src/skf-export-skill/references/update-context.md +118 -237
- package/src/skf-forger/SKILL.md +61 -37
- package/src/skf-forger/bmad-skill-manifest.yaml +4 -1
- package/src/skf-forger/customize.toml +43 -0
- package/src/skf-forger/references/pipeline-mode.md +73 -18
- package/src/skf-forger/scripts/forge-status.py +299 -0
- package/src/skf-forger/scripts/parse-pipeline.py +322 -54
- package/src/skf-forger/scripts/pipeline-gate.py +428 -0
- package/src/skf-forger/scripts/pipeline-journal.py +1052 -0
- package/src/skf-quick-skill/SKILL.md +54 -53
- package/src/skf-quick-skill/assets/skill-template.md +4 -2
- package/src/skf-quick-skill/customize.toml +51 -20
- package/src/skf-quick-skill/references/batch-mode.md +74 -21
- package/src/skf-quick-skill/references/compile.md +30 -31
- package/src/skf-quick-skill/references/ecosystem-check.md +7 -57
- package/src/skf-quick-skill/references/finalize.md +69 -18
- package/src/skf-quick-skill/references/halt-contract.md +41 -29
- package/src/skf-quick-skill/references/health-check.md +5 -3
- package/src/skf-quick-skill/references/quick-extract.md +114 -45
- package/src/skf-quick-skill/references/registry-resolution.md +9 -64
- package/src/skf-quick-skill/references/resolve-target.md +116 -66
- package/src/skf-quick-skill/references/write-and-validate.md +79 -40
- package/src/skf-refine-architecture/SKILL.md +43 -53
- package/src/skf-refine-architecture/customize.toml +49 -20
- package/src/skf-refine-architecture/references/compile.md +97 -54
- package/src/skf-refine-architecture/references/exit-codes.md +30 -0
- package/src/skf-refine-architecture/references/finding-storage.md +21 -0
- package/src/skf-refine-architecture/references/gap-analysis.md +101 -38
- package/src/skf-refine-architecture/references/improvements.md +14 -20
- package/src/skf-refine-architecture/references/init.md +98 -57
- package/src/skf-refine-architecture/references/issue-detection.md +32 -39
- package/src/skf-refine-architecture/references/refinement-rules.md +37 -48
- package/src/skf-refine-architecture/references/report.md +65 -54
- package/src/skf-refine-architecture/scripts/skf-check-preservation.py +1619 -0
- package/src/skf-rename-skill/SKILL.md +19 -54
- package/src/skf-rename-skill/customize.toml +32 -26
- package/src/skf-rename-skill/references/execute.md +230 -184
- package/src/skf-rename-skill/references/exit-codes.md +7 -7
- package/src/skf-rename-skill/references/health-check.md +1 -7
- package/src/skf-rename-skill/references/invocation-contract.md +25 -0
- package/src/skf-rename-skill/references/report.md +63 -52
- package/src/skf-rename-skill/references/select.md +197 -115
- package/src/skf-rename-skill/scripts/skf-validate-rename-name.py +161 -21
- package/src/skf-setup/SKILL.md +26 -26
- package/src/skf-setup/customize.toml +14 -10
- package/src/skf-setup/references/auto-index.md +50 -63
- package/src/skf-setup/references/ccc-index.md +29 -90
- package/src/skf-setup/references/detect-and-tier.md +58 -67
- package/src/skf-setup/references/health-check.md +2 -0
- package/src/skf-setup/references/invocation-contract.md +14 -0
- package/src/skf-setup/references/report.md +43 -166
- package/src/skf-setup/references/tier-rules.md +5 -5
- package/src/skf-setup/references/write-config.md +34 -57
- package/src/skf-test-skill/SKILL.md +17 -57
- package/src/skf-test-skill/assets/output-section-formats.md +33 -34
- package/src/skf-test-skill/customize.toml +27 -13
- package/src/skf-test-skill/references/coherence-check.md +134 -62
- package/src/skf-test-skill/references/coverage-check-tiers.md +35 -0
- package/src/skf-test-skill/references/coverage-check.md +217 -254
- package/src/skf-test-skill/references/detect-mode.md +5 -5
- package/src/skf-test-skill/references/external-validators.md +94 -82
- package/src/skf-test-skill/references/health-check.md +6 -7
- package/src/skf-test-skill/references/init.md +163 -88
- package/src/skf-test-skill/references/invocation-contract.md +66 -0
- package/src/skf-test-skill/references/migration-section-rules.md +5 -2
- package/src/skf-test-skill/references/report.md +162 -142
- package/src/skf-test-skill/references/score.md +80 -107
- package/src/skf-test-skill/references/scoring-rules.md +41 -155
- package/src/skf-test-skill/references/source-access-protocol.md +19 -92
- package/src/skf-test-skill/references/step-hard-gate.md +51 -33
- package/src/skf-test-skill/scripts/aggregate-coherence.py +218 -30
- package/src/skf-test-skill/scripts/build-result-context.py +287 -0
- package/src/skf-test-skill/scripts/check-metadata-coherence.py +37 -4
- package/src/skf-test-skill/scripts/combine-external-scores.py +23 -6
- package/src/skf-test-skill/scripts/compute-score.py +223 -45
- package/src/skf-test-skill/scripts/gap-ledger.py +1381 -0
- package/src/skf-test-skill/scripts/hard-gate.py +203 -0
- package/src/skf-test-skill/scripts/load-coverage-inputs.py +760 -0
- package/src/skf-test-skill/scripts/locate-export-segments.py +835 -0
- package/src/skf-test-skill/scripts/reconcile-coverage.py +273 -79
- package/src/skf-test-skill/scripts/score-signatures.py +459 -0
- package/src/skf-test-skill/scripts/stage-helper-payload.py +148 -0
- package/src/skf-test-skill/scripts/validate-inventory.py +311 -16
- package/src/skf-test-skill/scripts/verify-declared-numerator.py +127 -52
- package/src/skf-test-skill/templates/test-report-template.md +21 -15
- package/src/skf-update-skill/SKILL.md +38 -25
- package/src/skf-update-skill/customize.toml +27 -12
- package/src/skf-update-skill/references/detect-changes.md +233 -222
- package/src/skf-update-skill/references/gap-driven.md +336 -0
- package/src/skf-update-skill/references/health-check.md +31 -5
- package/src/skf-update-skill/references/init.md +250 -84
- package/src/skf-update-skill/references/invocation-contract.md +12 -0
- package/src/skf-update-skill/references/manual-section-rules.md +6 -23
- package/src/skf-update-skill/references/merge.md +180 -47
- package/src/skf-update-skill/references/re-extract.md +53 -262
- package/src/skf-update-skill/references/report.md +100 -35
- package/src/skf-update-skill/references/write.md +331 -149
- package/src/skf-update-skill/scripts/skf-new-file-diff.py +29 -7
- package/src/skf-verify-stack/SKILL.md +23 -42
- package/src/skf-verify-stack/assets/feasibility-report-template.md +11 -5
- package/src/skf-verify-stack/customize.toml +55 -22
- package/src/skf-verify-stack/references/coverage-patterns.md +17 -8
- package/src/skf-verify-stack/references/coverage.md +67 -64
- package/src/skf-verify-stack/references/exit-codes.md +21 -8
- package/src/skf-verify-stack/references/init.md +107 -62
- package/src/skf-verify-stack/references/integration-verification-rules.md +12 -13
- package/src/skf-verify-stack/references/integrations.md +106 -65
- package/src/skf-verify-stack/references/report.md +59 -42
- package/src/skf-verify-stack/references/requirements.md +32 -9
- package/src/skf-verify-stack/references/synthesize.md +53 -39
- package/src/skf-verify-stack/scripts/skf-coverage-tally.py +398 -52
- package/src/skf-verify-stack/scripts/skf-previous-report.py +330 -0
- package/src/skf-verify-stack/scripts/skf-report-delta.py +453 -49
- package/src/skf-verify-stack/scripts/skf-verdict-rollup.py +240 -27
- package/tools/cli/commands/install.js +11 -4
- package/tools/cli/commands/status.js +15 -3
- package/tools/cli/commands/update.js +11 -4
- package/tools/cli/lib/tool-check.js +320 -0
- package/tools/cli/lib/version-check.js +22 -9
- package/tools/cli/skf-cli.js +4 -1
- package/.gitattributes +0 -16
- package/.nvmrc +0 -1
- package/docs/_internal/RELEASING.md +0 -679
- package/release-audits/v1.0.0-launch-audit.md +0 -728
- package/src/skf-audit-skill/references/step-doc-drift.md +0 -177
- package/src/skf-brief-skill/references/headless-source-authority-detection.md +0 -26
- package/src/skf-campaign/manifest.yaml +0 -15
- package/src/skf-create-skill/assets/tessl-dismissal-rules.md +0 -106
- package/src/skf-create-skill/references/ecosystem-check.md +0 -74
- package/src/skf-drop-skill/references/headless-contract.md +0 -18
- package/src/skf-update-skill/references/merge-conflict-rules.md +0 -15
- package/src/skf-update-skill/references/remote-source-resolution.md +0 -98
- package/src/skf-update-skill/references/validate.md +0 -124
- package/tools/validate-docs-drift.js +0 -233
- package/tools/validate-docs-links.js +0 -665
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
---
|
|
2
2
|
returnToStep: 'extract.md'
|
|
3
3
|
extractionPatternsData: 'references/extraction-patterns.md'
|
|
4
|
+
# If neither path exists, Phase 1 goes on without demo exclusion or
|
|
5
|
+
# registry detection.
|
|
6
|
+
detectRegistryProbeOrder:
|
|
7
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-detect-registry.py'
|
|
8
|
+
- '{project-root}/src/shared/scripts/skf-detect-registry.py'
|
|
9
|
+
# If neither path exists, Phase 4 runs the AST Extraction Protocol instead.
|
|
10
|
+
extractPublicApiProbeOrder:
|
|
11
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-extract-public-api.py'
|
|
12
|
+
- '{project-root}/src/shared/scripts/skf-extract-public-api.py'
|
|
13
|
+
# If neither path exists, Phase 2b saves no answer and the next run asks
|
|
14
|
+
# again.
|
|
15
|
+
writeSkillBriefProbeOrder:
|
|
16
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-write-skill-brief.py'
|
|
17
|
+
- '{project-root}/src/shared/scripts/skf-write-skill-brief.py'
|
|
4
18
|
---
|
|
5
19
|
|
|
6
20
|
<!-- Config: communicate in {communication_language}. -->
|
|
@@ -14,105 +28,119 @@ When `scope.type: "component-library"`, perform specialized extraction that trea
|
|
|
14
28
|
## Rules
|
|
15
29
|
|
|
16
30
|
- Focus only on extracting component catalog, props interfaces, and shared types
|
|
17
|
-
- Do not compile SKILL.md content (Step 05)
|
|
31
|
+
- Do not compile SKILL.md content (Step 05). Write only the scratch files under `{component_scan}`, Phase 4's runner JSON at `{extraction_json}` and, in Phase 2b, the brief
|
|
18
32
|
- Every extracted item must have a provenance citation: `[AST:{file}:L{line}]` or `[SRC:{file}:L{line}]`
|
|
19
33
|
|
|
20
34
|
## MANDATORY SEQUENCE
|
|
21
35
|
|
|
22
|
-
**Prerequisite
|
|
36
|
+
**Prerequisite: §2a already ran.** Step-03 executes `§2a Discovered Authoritative Files Protocol` before delegating to this file. Any promoted authoritative files (`llms.txt`, `AGENTS.md`, etc.) are tracked in the `promoted_docs[]` context list and will be written to `file_entries[]` with `file_type: "doc"` by step 5 §6. Phase 1 lists them with the brief's other files (a promotion adds each to `scope.include`), and no Phase 4 recipe reads a doc, so this file needs no special handling for them. See step 3 §2a for the full flow.
|
|
23
37
|
|
|
24
38
|
### Phase 1: Demo/Example Exclusion
|
|
25
39
|
|
|
26
40
|
Before extraction, identify and exclude demo/example files to avoid inflating export counts.
|
|
27
41
|
|
|
28
|
-
|
|
42
|
+
Bind `{brief_file}` ← the brief file step 1 loaded: `{brief_path}`, `{forge_data_folder}/{skill-name}/skill-brief.yaml` for a skill name, or the current brief of a `--batch` run. Make a scratch folder for this step's files, from `{project-root}`, and bind `{component_scan}` ← the path it prints:
|
|
29
43
|
|
|
30
|
-
|
|
44
|
+
```bash
|
|
45
|
+
mkdir -p "{project-root}/_bmad-output/.skf-run" && mktemp -d "{project-root}/_bmad-output/.skf-run/skf-create-skill-3d-XXXXXXXX"
|
|
46
|
+
```
|
|
31
47
|
|
|
32
|
-
|
|
48
|
+
Resolve `{detectRegistryHelper}` from `{detectRegistryProbeOrder}`. It lists the files of the brief's scope under `{source_root}`, the tree extraction reads, and counts the demo files among them. From `{project-root}`, run it with one `--pattern` for each glob of the brief's `scope.demo_patterns` when it sets them:
|
|
33
49
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
50
|
+
```bash
|
|
51
|
+
uv run {detectRegistryHelper} demo --source-root "{source_root}" --brief "{brief_file}" [--pattern "{glob}"] --files-to "{component_scan}/files.txt" --kept-to "{component_scan}/kept.txt"
|
|
52
|
+
```
|
|
37
53
|
|
|
38
|
-
|
|
54
|
+
With no local tree (`{source_root}` is not a folder: a Quick-tier remote source, or one step 3 §2b could not read into a tree), pipe it the repository's file list, `gh api "repos/{owner}/{repo}/git/trees/{source_ref}?recursive=1" --jq '.tree[] | select(.type == "blob") | .path'`, with `--files-from -` in place of `--source-root`.
|
|
39
55
|
|
|
40
|
-
|
|
41
|
-
{list detected patterns with counts}
|
|
56
|
+
It writes the brief's files to `files.txt` and those no demo pattern matches to `kept.txt`, and prints `patterns[]` (each glob, its `files` count and a `sample`), `excluded`, `directories` and `also_matched[]`: `uv run {detectRegistryHelper} --help` describes them.
|
|
42
57
|
|
|
43
|
-
|
|
58
|
+
**If `scope.demo_patterns` is specified in the brief:** exclude what those patterns match, with no prompt. When `also_matched[]` is not empty, log: "Files the default demo patterns match are left in, since the brief's `scope.demo_patterns` names none of them: {each `also_matched[]` glob, its file count and its sample}. Add a glob to the brief's `scope.demo_patterns` to leave them out."
|
|
44
59
|
|
|
45
|
-
**
|
|
60
|
+
**Otherwise, when `excluded` is not 0, ask for confirmation**, since some `examples/` directories hold API-level code (`{N}` is `excluded`, `{M}` is `directories`):
|
|
46
61
|
|
|
47
|
-
|
|
62
|
+
"**Auto-detected {N} demo/example files** in {M} directories matching these patterns:
|
|
63
|
+
{for each patterns[] entry: its glob, its file count and its sample files}
|
|
48
64
|
|
|
49
|
-
|
|
65
|
+
Confirm exclusion? [Y/n] Or adjust patterns:"
|
|
50
66
|
|
|
51
|
-
|
|
67
|
+
**GATE [default: Y]**: if `{headless_mode}` is true, auto-confirm the auto-detected exclusion patterns, log "headless: auto-confirm demo/example exclusion ({N} files, {M} directories)", and record it per the Workflow Rules: stage `{"step": "component-extraction", "gate": "demo-exclusion", "decision": "Y", "value": "{N} files / {M} dirs", "rationale": "headless mode: auto-detected demo patterns accepted", "timestamp": "{ISO}"}` as `{run_dir}/decision.json` and run `uv run {emitEnvelopeHelper} record --workflow skf-create-skill --run-dir "{run_dir}" --decision < "{run_dir}/decision.json"` (step 5 §7 renders it in the evidence report's `## Auto-Decisions` table). Then proceed without waiting.
|
|
52
68
|
|
|
53
|
-
|
|
69
|
+
Wait for user response (interactive only). When the user adjusts the patterns, run the command again with one `--pattern` for each pattern given and use that result. On `n`, exclude nothing. Record `demo_files_excluded: {count}` in context (0 on `n`), and `demo_answer` ← the `patterns[]` globs the user confirmed or gave (none on `n`, in headless mode, or when the brief set them).
|
|
54
70
|
|
|
55
|
-
|
|
71
|
+
Bind `{scan_list}`, the filtered file list for the rest of this step: `{component_scan}/kept.txt` when files were excluded, else `{component_scan}/files.txt`.
|
|
56
72
|
|
|
57
|
-
|
|
73
|
+
**If the command exits non-zero, or no `{detectRegistryHelper}` candidate resolves:** warn "Demo exclusion and registry detection are skipped: {its stderr, or 'skf-detect-registry.py is missing, so re-install SKF'}.", exclude nothing and bind `{scan_list}` to null. Phase 2 then takes a brief's `scope.registry_path` as the registry unscored, or goes on with no registry.
|
|
58
74
|
|
|
59
|
-
|
|
75
|
+
### Phase 2: Registry Detection
|
|
76
|
+
|
|
77
|
+
The component registry is the primary API surface for component libraries. Score it with the helper, from `{project-root}`:
|
|
60
78
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
- Files named `components.ts`, `components.tsx` in `registry/`, `catalog/`, or `components/` directories
|
|
64
|
-
- Files named `index.ts`, `index.tsx` in `registry/` or `catalog/` directories
|
|
79
|
+
- **If the brief sets `scope.registry_path`:** `uv run {detectRegistryHelper} registry --source-root "{source_root}" --path "{scope.registry_path}"`. Its `selected` file is the registry whatever its score: continue to Phase 3 with no prompt. When `selected` is null (`candidates[0].error` says why), warn "The brief's scope.registry_path `{scope.registry_path}` could not be read ({error}): detecting the registry instead.", set `registry_path_unreadable` ← true, and run the next command.
|
|
80
|
+
- **Otherwise:** `uv run {detectRegistryHelper} registry --files-from "{scan_list}" --source-root "{source_root}"`.
|
|
65
81
|
|
|
66
|
-
|
|
67
|
-
- Arrays of objects with fields like `id`, `name`, `component`, `category`
|
|
68
|
-
- Type annotations containing `Component[]` or similar array types
|
|
69
|
-
- 10+ entries to qualify as a registry (reject small arrays)
|
|
82
|
+
With no local tree, first fetch the files the command reads into `{component_scan}/registry/`, each at its own relative path (`gh api "repos/{owner}/{repo}/contents/{path}?ref={source_ref}" -H "Accept: application/vnd.github.raw"`): the brief's `scope.registry_path` or a path the user gives below, else the paths `uv run {detectRegistryHelper} registry --files-from "{scan_list}" --candidates-only` prints, one per line. Then pass `--source-root "{component_scan}/registry"`.
|
|
70
83
|
|
|
71
|
-
|
|
72
|
-
- +3 points: Contains `id` field per entry
|
|
73
|
-
- +2 points: Contains `name` or `component` field per entry
|
|
74
|
-
- +2 points: Contains `category` or `tags` field per entry
|
|
75
|
-
- +1 point: Located in a `registry/` or `catalog/` directory
|
|
76
|
-
- +1 point: Has 20+ entries
|
|
77
|
-
- Minimum threshold: 5 points to qualify
|
|
84
|
+
It prints `selected` (the best qualifying candidate, or null), `headless_accept` and `candidates[]`, each with its `path`, `score` out of 9, `entry_count` and `sample` (its first entries): `uv run {detectRegistryHelper} --help` gives the candidate rules and the score rubric. When it exits non-zero, go on as when no registry was found.
|
|
78
85
|
|
|
79
|
-
|
|
86
|
+
**If `selected` is not null, ask for confirmation:**
|
|
80
87
|
|
|
81
88
|
"**Registry candidate detected** at `{path}` (confidence: {score}/9, {count} entries).
|
|
82
89
|
|
|
83
90
|
Sample entries:
|
|
84
|
-
{show
|
|
91
|
+
{show the selected candidate's sample entries with id, name, category}
|
|
92
|
+
{if another candidate qualifies: 'Other candidates: {path} ({score}/9, {count} entries)'}
|
|
85
93
|
|
|
86
94
|
Is this the component registry? [Y/n] Or provide the correct path:"
|
|
87
95
|
|
|
88
|
-
**GATE [default: Y]
|
|
96
|
+
**GATE [default: Y]**: if `{headless_mode}` is true and `headless_accept` is true: auto-confirm the `selected` candidate, log "headless: auto-confirm registry candidate `{path}` (score {score}/9)", and record it per the Workflow Rules: stage `{"step": "component-extraction", "gate": "registry-confirm", "decision": "Y", "value": "{path} score={score}/9", "rationale": "headless mode: high-confidence registry candidate auto-accepted", "timestamp": "{ISO}"}` as `{run_dir}/decision.json` and run `uv run {emitEnvelopeHelper} record --workflow skf-create-skill --run-dir "{run_dir}" --decision < "{run_dir}/decision.json"`. If `headless_accept` is false in headless mode, auto-reject the candidate (treat as "no registry found"), log "headless: auto-reject registry candidate `{path}` (score {score}/9), below the auto-accept threshold", record the decision the same way (`"decision": "reject-low-score"`), and fall through to the "no registry found" branch below.
|
|
89
97
|
|
|
90
|
-
Wait for user response (interactive only).
|
|
98
|
+
Wait for user response (interactive only). On `Y`, the registry is `selected`: set `registry_answer` ← its path, with `registry_evidence` ← "score {score}/9, {count} entries". When the user gives a path instead, run the `--path` command above with it: when its `selected` is null, show its `error` and ask again; otherwise use that file, with `registry_answer` ← the path and `registry_evidence` ← "given by the user, {count} entries". On `n` with no path, no registry was found.
|
|
91
99
|
|
|
92
|
-
**If no registry found and no `registry_path
|
|
100
|
+
**If no registry was found and the brief holds no usable `scope.registry_path`:**
|
|
93
101
|
|
|
94
102
|
"**No component registry detected.** Component-library extraction works best with a registry file. Options:
|
|
95
103
|
- **[P]** Provide the registry file path
|
|
96
104
|
- **[S]** Skip registry — proceed with standard props-first extraction only"
|
|
97
105
|
|
|
98
|
-
**GATE [default: S]
|
|
106
|
+
**GATE [default: S]**: if `{headless_mode}` is true: auto-select [S] Skip (props-first extraction without registry), log "headless: no registry detected, auto-skip to props-first extraction (no usable path in brief.scope.registry_path)", and record it per the Workflow Rules: stage `{"step": "component-extraction", "gate": "provide-or-skip-registry", "decision": "S", "rationale": "headless mode: no human to provide registry path", "timestamp": "{ISO}"}` as `{run_dir}/decision.json` and run `uv run {emitEnvelopeHelper} record --workflow skf-create-skill --run-dir "{run_dir}" --decision < "{run_dir}/decision.json"`. The default is `[S]` rather than `[P]` because providing a path requires user input that headless cannot supply; skipping degrades gracefully to a smaller but valid extraction.
|
|
107
|
+
|
|
108
|
+
Wait for user response (interactive only). On [P], run the `--path` command above with the path given: when its `selected` is null, show its `error` and ask again; otherwise use that file, with `registry_answer` ← the path and `registry_evidence` ← "given by the user, {count} entries".
|
|
109
|
+
|
|
110
|
+
### Phase 2b: Record the Answers in the Brief
|
|
111
|
+
|
|
112
|
+
Write back only what a user answered at a gate above, never a headless auto-decision, so that a second run of the same brief asks neither question: `demo_answer`, which the brief never set, and `registry_answer`, which fills an unset `scope.registry_path` or replaces one Phase 2 could not read (`registry_path_unreadable`). **Skip this phase when neither `demo_answer` nor `registry_answer` is set.**
|
|
113
|
+
|
|
114
|
+
Resolve `{writeSkillBriefHelper}` from `{writeSkillBriefProbeOrder}` and, from `{project-root}`, pipe it each answer that is set (leave out the key of one that is not), with its amendment entries: one per glob of `demo_answer` (`action: "demo-excluded"`, `evidence`: "{files} files" from the glob's `patterns[]` entry) and one for `registry_answer` (`action: "registry-confirmed"`, `evidence`: `registry_evidence`). Every entry takes `category: "demo-and-registry"`, today's ISO `date`, `workflow: "skf-create-skill"` and a `reason`: the user's own, else "user confirmed the demo exclusion at create-skill step 3d", "user adjusted the demo patterns at create-skill step 3d", "user confirmed the detected registry at create-skill step 3d", "user gave the registry path at create-skill step 3d" or, when `registry_path_unreadable` is true, "user replaced the unreadable registry path at create-skill step 3d":
|
|
99
115
|
|
|
100
|
-
|
|
116
|
+
```bash
|
|
117
|
+
uv run {writeSkillBriefHelper} amend --target "{brief_file}" <<'SKF_BRIEF_ANSWERS'
|
|
118
|
+
{"demo_patterns": ["{glob}", ...], "registry_path": "{registry_answer}", "amendments": [{"path": "{glob or registry file}", "action": "{action}", "category": "demo-and-registry", "reason": "{reason}", "evidence": "{evidence}", "date": "{today ISO}", "workflow": "skf-create-skill"}, ...]}
|
|
119
|
+
SKF_BRIEF_ANSWERS
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
It reads the brief again (step 3 §2a may have amended it since step 1 loaded it), sets the fields, appends the entries, keeps the previous brief as `{brief_file}.bak` and writes the new one atomically. On exit 0, display: "**Recorded in the brief:** {`scope.demo_patterns` and/or `scope.registry_path`}, with a `demo-and-registry` amendment for each." When it exits non-zero, or no candidate resolves, warn "The step 3d answers were not saved to the brief ({its stderr}): the next run asks again." and continue.
|
|
101
123
|
|
|
102
124
|
### Phase 3: Parse Registry
|
|
103
125
|
|
|
104
|
-
If a registry was confirmed,
|
|
126
|
+
If a registry was confirmed, bind `{registry_file}` ← its path (the brief's `scope.registry_path`, `selected` or the path the user gave) and list its entries, from `{project-root}`, with the `--source-root` Phase 2 used:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
uv run {detectRegistryHelper} registry --source-root "{source_root}" --path "{registry_file}" --entries
|
|
130
|
+
```
|
|
105
131
|
|
|
106
|
-
|
|
132
|
+
Map each of its `entries[]` (one per object literal of the registry's array, each with its `line`, its `keys` and the `id`, `name`, `component`, `category`, `tags` and `description` it has) to one `component_catalog[]` entry:
|
|
107
133
|
|
|
108
|
-
- `id
|
|
109
|
-
- `name
|
|
110
|
-
- `description`
|
|
111
|
-
- `category`
|
|
112
|
-
- `available_in[]
|
|
113
|
-
- `code_paths[]
|
|
114
|
-
- `tags[]`
|
|
115
|
-
- Provenance citation: `[SRC:{registry_file}:L{line}]`
|
|
134
|
+
- `id`: the entry's `id` (used for the CLI `add` command)
|
|
135
|
+
- `name`: display name (PascalCase), from `name` or `component`
|
|
136
|
+
- `description`: `description`, or empty
|
|
137
|
+
- `category`: `category`, or from the directory structure
|
|
138
|
+
- `available_in[]`: which design system variants include this component
|
|
139
|
+
- `code_paths[]`: source file path for each variant (from the entry's other `keys`, read at its `line` in the registry file, or from the directory structure)
|
|
140
|
+
- `tags[]`: `tags`, or empty
|
|
141
|
+
- Provenance citation: `[SRC:{registry_file}:L{line}]`, with the entry's `line`
|
|
142
|
+
|
|
143
|
+
When the command exits non-zero, or no candidate resolves, read the entries from the registry file by eye instead.
|
|
116
144
|
|
|
117
145
|
Display: "**Parsed component catalog: {N} components across {M} categories.**"
|
|
118
146
|
|
|
@@ -122,47 +150,35 @@ Display: "**Parsed component catalog: {N} components across {M} categories.**"
|
|
|
122
150
|
|
|
123
151
|
Extract props interfaces as the primary API contracts, then link to components.
|
|
124
152
|
|
|
125
|
-
|
|
153
|
+
At Quick tier no recipe runs: read the shapes the steps below name from source, as T1-low. At Forge, Forge+ and Deep tiers, run the component-library recipes over `{scan_list}` in one call. Resolve `{extractPublicApiHelper}` from `{extractPublicApiProbeOrder}`; it writes `{extraction_json}`, which step 3 §2b bound beside the staging folder: step 3 §5's `init` seeds the inventory from it, so it sits outside the scratch folder Phase 6 removes. From `{project-root}`, create its folder (the runner creates none), remove the JSON an earlier run left there and run the runner with `{tier}` the forge tier (when `{scan_list}` is null, pass `--brief "{brief_file}"` in place of `--files-from "{scan_list}"`: the runner then lists the brief's files itself):
|
|
126
154
|
|
|
127
|
-
|
|
155
|
+
```bash
|
|
156
|
+
mkdir -p "{project-root}/_bmad-output/.skf-stage"
|
|
157
|
+
rm -f "{extraction_json}"
|
|
158
|
+
uv run {extractPublicApiHelper} --mode full --source-root "{source_root}" --files-from "{scan_list}" --recipe-set component-library --scope-type component-library --tier {tier} -o "{extraction_json}"
|
|
159
|
+
```
|
|
128
160
|
|
|
129
|
-
|
|
161
|
+
Act on the JSON's `status`, never on the exit code. **On `ok` or `incomplete`**, each of the `exports[]` in `{extraction_json}` is T1: it carries its `citation` (`[AST:{file}:L{line}]`), `ast_recipe`, `ast_node_type`, `export_type`, `confidence` and `extraction_method`, and the steps below sort the exports by `ast_recipe`. Read by eye, as T1-low, each file its `file_issues` lists as `syntax-errors` or `not-utf8`, where a recipe can miss an export. A `no-recipes` issue is a file no recipe reads, such as a stylesheet: there is nothing to extract from it. A `missing` or `unreadable` issue is a listed file the runner could not read: record it for the evidence report as not extracted. Step 3 §5's `init` records the runner's warnings, and Gate 2 shows them: the head-cap warning when `truncated` is true (a recipe, which `recipes[]` marks, found more matches than the head cap keeps), and each `errors[]` item of an `incomplete` run (an ast-grep run failed or timed out, so its files may be unread).
|
|
130
162
|
|
|
131
|
-
|
|
132
|
-
# React/TypeScript props interfaces
|
|
133
|
-
id: react-props-interfaces
|
|
134
|
-
language: typescript
|
|
135
|
-
rule:
|
|
136
|
-
pattern: 'export interface $NAME { $$$ }'
|
|
137
|
-
constraints:
|
|
138
|
-
NAME:
|
|
139
|
-
regex: '.*Props$'
|
|
140
|
-
```
|
|
163
|
+
**On `no-ast-grep`, or with no JSON at `{extraction_json}`** (an input error, or no candidate resolves): load `{extractionPatternsData}`, which holds every recipe the steps below name and the forms each does not cover (Known Limitation #11), and run each of them as its AST Extraction Protocol says: its **When the Runner Cannot Run** loads the recipes' notes, the languages each runs in among them.
|
|
141
164
|
|
|
142
|
-
|
|
143
|
-
|
|
165
|
+
**Step 1 (extract Props interfaces):**
|
|
166
|
+
|
|
167
|
+
The Props contracts are the exports `react-props-interfaces` finds (`typescript` for `.ts` files, `tsx` for `.tsx` files) and, in a Vue component library, those `vue-define-props` finds in the `.vue` files (`vue-define-props-tsx` for a `<script setup lang="tsx">` block): each such match's `$NAME` is the props type of the component its `.vue` file defines. Without the runner, the Vue recipes run through the CLI with the scratch `sgconfig.yml` the Vue note in `extraction-patterns-by-hand.md` describes.
|
|
168
|
+
|
|
169
|
+
For each Props contract found (a `*Props` interface, or a Vue props type):
|
|
170
|
+
- Extract all fields with types, optionality, and default values (for a Vue props type, from the interface or type literal it names)
|
|
144
171
|
- Extract JSDoc descriptions per field (if present)
|
|
145
172
|
- Record: interface name, fields[], source file, line number
|
|
146
173
|
- Provenance: `[AST:{file}:L{line}]` or `[SRC:{file}:L{line}]`
|
|
147
174
|
|
|
148
|
-
**Step 2
|
|
149
|
-
|
|
150
|
-
```yaml
|
|
151
|
-
# React component exports (PascalCase)
|
|
152
|
-
id: react-component-exports
|
|
153
|
-
language: tsx # Use 'tsx' for .tsx files, 'typescript' for .ts files
|
|
154
|
-
rule:
|
|
155
|
-
pattern: 'export function $NAME($$$PARAMS)'
|
|
156
|
-
constraints:
|
|
157
|
-
NAME:
|
|
158
|
-
regex: '^[A-Z]'
|
|
159
|
-
```
|
|
175
|
+
**Step 2 (extract component exports):**
|
|
160
176
|
|
|
161
|
-
|
|
177
|
+
The component exports are those `react-component-functions` (its `javascript` form for `.js` and `.jsx` files), `react-component-arrow-functions` and `react-wrapped-components` (for `memo`, `forwardRef` and `lazy` components) find, and the items of a local list such as the `export { Button, buttonVariants }` that ends a shadcn/ui component file, which `js-local-exports` finds. Sort a local list's items by what they name: the runner records an item's `export_type` as the kind of the declaration it names (`interface`, `type` or `enum` for a type). A type-only item (one in an `export type { ... }` list, or a `type X` specifier) is a type: one whose name ends in `Props` is a Props contract for Steps 1 and 3 (read its fields from the interface or type the list names), and any other is a shared type for Step 4. A PascalCase value item is a component, and any other value item (such as `buttonVariants`) is a shared export: record it with the shared types of Step 4.
|
|
162
178
|
|
|
163
179
|
For each component export: record name, source file, line number. Do not document the function signature in detail (it's always `(props: XProps) => JSX.Element`).
|
|
164
180
|
|
|
165
|
-
**Step 3
|
|
181
|
+
**Step 3 (link Props to components):**
|
|
166
182
|
|
|
167
183
|
Use a 3-level fallback chain:
|
|
168
184
|
|
|
@@ -170,14 +186,11 @@ Use a 3-level fallback chain:
|
|
|
170
186
|
2. **File co-location (fallback):** If naming doesn't match, check if a Props interface and a component are defined in the same file
|
|
171
187
|
3. **Generic parameter (deep fallback):** Look for `ComponentProps<typeof Foo>` or similar generic patterns that reference the component
|
|
172
188
|
|
|
173
|
-
For each linked pair, record the association. For unlinked Props interfaces, include them as standalone type exports.
|
|
189
|
+
For each linked pair, record the association. For unlinked Props interfaces, include them as standalone type exports. Include an unlinked component export with a note that no Props interface was found (signature-only, T1-low confidence for API contract).
|
|
174
190
|
|
|
175
|
-
**Step 4
|
|
191
|
+
**Step 4 (extract shared types):**
|
|
176
192
|
|
|
177
|
-
|
|
178
|
-
- `export type $NAME = $VALUE`
|
|
179
|
-
- `export enum $NAME { $$$ }`
|
|
180
|
-
- `export interface $NAME { $$$ }` (excluding `*Props` already captured)
|
|
193
|
+
The shared types are the exports `ts-exported-types` finds: the exported interfaces, type aliases and enums, generic, `extends` and `export declare` forms included, less the `*Props` interfaces Step 1 took. It returns top-level exports only: a member of an `export namespace` block is not a module export.
|
|
181
194
|
|
|
182
195
|
### Phase 5: Variant Consolidation
|
|
183
196
|
|
|
@@ -204,17 +217,20 @@ Display: "**Variant consolidation: {unique} unique components across {variant_co
|
|
|
204
217
|
|
|
205
218
|
### Phase 6: Build Component Extraction Results
|
|
206
219
|
|
|
207
|
-
|
|
220
|
+
Step 3 §5 takes the runner's exports from `{extraction_json}` as it wrote them. Prepare what it sends on top: an entry below for each export read by eye (Quick tier, a `file_issues` file, or the runner's fallback), each Props interface's fields as its `params`, and `component_catalog[]`; keep the aggregate counts below in context for step 5's `metadata.json` stats:
|
|
208
221
|
|
|
209
222
|
**Per-export entry (for Props interfaces — primary API):**
|
|
210
223
|
|
|
211
224
|
- Interface name (e.g., `NativeLiquidButtonProps`)
|
|
212
225
|
- Full interface with all fields and types
|
|
213
|
-
-
|
|
226
|
+
- `params`: each field as `{name, type, default, optional, description}`, sent with `patch`
|
|
214
227
|
- Linked component name (e.g., `NativeLiquidButton`)
|
|
215
228
|
- Source file and line number
|
|
216
229
|
- Provenance citation
|
|
217
|
-
- Confidence tier
|
|
230
|
+
- Confidence tier: T1 when an ast-grep rule matched the interface, T1-low when it was read by eye
|
|
231
|
+
- `extraction_method`: `ast-grep` or `source-read`, the tool that produced the entry
|
|
232
|
+
- `ast_node_type`: the `kind` the matching recipe declares (`export_statement` for `react-props-interfaces`: the recipe matches the whole export, not the `interface_declaration` inside it), or `null` when read by eye
|
|
233
|
+
- `ast_recipe`: the `id` of the recipe that matched it, or `null` when read by eye
|
|
218
234
|
|
|
219
235
|
**Per-export entry (for component functions):**
|
|
220
236
|
|
|
@@ -222,7 +238,7 @@ Compile all extracted data into the format expected by step 3 section 5:
|
|
|
222
238
|
- Linked Props interface (if found)
|
|
223
239
|
- Source file and line number
|
|
224
240
|
- Provenance citation
|
|
225
|
-
- Confidence tier
|
|
241
|
+
- Confidence tier, `extraction_method`, `ast_node_type` and `ast_recipe`, labeled by the tool that produced the entry as for Props interfaces above (a component `js-local-exports` found records `export_specifier`, the kind that recipe declares)
|
|
226
242
|
|
|
227
243
|
**Per-export entry (for shared types):**
|
|
228
244
|
|
|
@@ -239,6 +255,12 @@ Compile all extracted data into the format expected by step 3 section 5:
|
|
|
239
255
|
|
|
240
256
|
**Store `component_catalog[]` in context** — this is consumed by step 5 for the Component Catalog section.
|
|
241
257
|
|
|
258
|
+
Remove this step's scratch folder, from `{project-root}` (the `case` guard deletes nothing unless `{component_scan}` is the folder Phase 1 made):
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
case "{component_scan}" in "{project-root}/_bmad-output/.skf-run/skf-create-skill-3d-"*) rm -rf "{component_scan}" ;; esac
|
|
262
|
+
```
|
|
263
|
+
|
|
242
264
|
Display: "**Component extraction complete.** Returning to main extraction flow."
|
|
243
265
|
|
|
244
266
|
## RETURN PROTOCOL
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
nextStepFile: 'compile.md'
|
|
3
|
+
forgeTierConfig: '{sidecar_path}/forge-tier.yaml'
|
|
4
|
+
# If neither path exists, §2 finds no enrichment collection and the step is
|
|
5
|
+
# skipped.
|
|
6
|
+
forgeTierRwProbeOrder:
|
|
7
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-forge-tier-rw.py'
|
|
8
|
+
- '{project-root}/src/shared/scripts/skf-forge-tier-rw.py'
|
|
9
|
+
# §4 appends the T2 annotations to the extraction inventory through it.
|
|
10
|
+
extractionInventoryProbeOrder:
|
|
11
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-extraction-inventory.py'
|
|
12
|
+
- '{project-root}/src/shared/scripts/skf-extraction-inventory.py'
|
|
3
13
|
---
|
|
4
14
|
|
|
5
15
|
<!-- Config: communicate in {communication_language}. -->
|
|
@@ -31,44 +41,47 @@ Continue to step 2.
|
|
|
31
41
|
|
|
32
42
|
### 2. Collection Inventory Pre-check (Deep Tier Only)
|
|
33
43
|
|
|
34
|
-
Before searching,
|
|
44
|
+
Before searching, find this skill's enrichment collections. Resolve `{forgeTierRwHelper}` from `{forgeTierRwProbeOrder}` and read the registry as JSON, from `{project-root}`:
|
|
35
45
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
46
|
+
```bash
|
|
47
|
+
uv run {forgeTierRwHelper} read --target "{forgeTierConfig}"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
1. Bind `{enrichment_collections}` ← the `name` of each entry of its `data.qmd_collections` whose `skill_name` is `{skill-name}` and whose `type` is `"temporal"` (issues, PRs and changelogs, from step 3b) or `"docs"` (fetched external documentation, from step 3c): at most `{skill-name}-temporal` and `{skill-name}-docs`. Another skill's collections never count, and neither do `"extraction"` or `"brief"` collections. The list is empty when `exists` is false, the command fails or no candidate resolves.
|
|
51
|
+
2. **If `{enrichment_collections}` is empty:** report this and auto-proceed. Display:
|
|
39
52
|
|
|
40
53
|
"**Enrichment: no enrichment collections available.**
|
|
41
|
-
|
|
54
|
+
No temporal context (issues, PRs, changelogs) or docs collection of this skill is indexed in QMD. {IF extraction_mode is 'docs-only' AND T3 items exist in extraction_inventory: 'T3 documentation content is available in the extraction inventory: enrichment through QMD is skipped, and the T3 content will be compiled.'} {ELSE: 'Enrichment skipped (expected for first-run skill creation). Enrichment becomes available when temporal or docs context is indexed into QMD collections.'}
|
|
42
55
|
|
|
43
56
|
Proceeding to compilation..."
|
|
44
57
|
|
|
45
58
|
Then immediately load, read entire file, then execute `{nextStepFile}`.
|
|
46
59
|
|
|
47
|
-
|
|
60
|
+
3. **Otherwise** (a temporal collection, a docs collection, or both): continue to step 3.
|
|
48
61
|
|
|
49
62
|
### 3. QMD Enrichment Searches (Deep Tier Only)
|
|
50
63
|
|
|
51
|
-
For each major exported function
|
|
64
|
+
For each major exported function (the **top-level public API surface**, typically the 10-20 functions that will appear in the context-snippet, not all extracted exports), search `{enrichment_collections}` for context. Every query below passes them as its collections, so no other skill's issues or docs can annotate this one:
|
|
52
65
|
|
|
53
66
|
**Search query construction:**
|
|
54
67
|
|
|
55
|
-
For each function, derive the **module context** from
|
|
68
|
+
Read the functions and their source files from `{extraction_inventory}`, the extraction inventory step 3 §5 wrote. For each function, derive the **module context** from its source file path (e.g., `src/graph/neo4j/index.ts` → module context `graph neo4j`). This context improves search relevance by scoping results to the function's subsystem without adding extra queries.
|
|
56
69
|
|
|
57
70
|
**Primary searches (BM25 — always runs, no GPU/VRAM dependency):**
|
|
58
71
|
|
|
59
|
-
1. **Issues/PRs:** `qmd_bridge.query(searches=[{type:'lex', query:'{module_context} {function_name}', intent:'issues-prs'}])
|
|
60
|
-
2. **Changelog entries:** `qmd_bridge.query(searches=[{type:'lex', query:'{function_name} changelog', intent:'changelog'}])
|
|
61
|
-
3. **Migration notes:** `qmd_bridge.query(searches=[{type:'lex', query:'{function_name} migration deprecated breaking', intent:'migration'}])
|
|
72
|
+
1. **Issues/PRs:** `qmd_bridge.query(searches=[{type:'lex', query:'{module_context} {function_name}', intent:'issues-prs'}], collections={enrichment_collections})`: related discussions, scoped by module context
|
|
73
|
+
2. **Changelog entries:** `qmd_bridge.query(searches=[{type:'lex', query:'{function_name} changelog', intent:'changelog'}], collections={enrichment_collections})`: version history and breaking changes (kept generic, since changelogs rarely use module paths)
|
|
74
|
+
3. **Migration notes:** `qmd_bridge.query(searches=[{type:'lex', query:'{function_name} migration deprecated breaking', intent:'migration'}], collections={enrichment_collections})`: deprecation or migration context, by exact keyword matching
|
|
62
75
|
|
|
63
76
|
**Supplemental search (best-effort — requires GPU/VRAM, may fail):**
|
|
64
77
|
|
|
65
|
-
4. **Semantic migration context:** `qmd_bridge.query(searches=[{type:'vec', query:'{module_context} {function_name} migration deprecated', intent:'semantic-migration'}])
|
|
78
|
+
4. **Semantic migration context:** `qmd_bridge.query(searches=[{type:'vec', query:'{module_context} {function_name} migration deprecated', intent:'semantic-migration'}], collections={enrichment_collections})`: adds semantic matches (synonyms, paraphrases) that BM25 keyword search may miss. Merge results with search #3, deduplicating by document ID. If the `vec`-type search fails (VRAM, GPU driver, model loading), discard silently: the BM25 (`lex`) results from search #3 provide baseline coverage.
|
|
66
79
|
|
|
67
|
-
**Tool resolution for qmd_bridge:** QMD MCP exposes a single `query` tool that accepts a `searches[]` array. Each
|
|
80
|
+
**Tool resolution for qmd_bridge:** QMD MCP exposes a single `query` tool that accepts a `searches[]` array and a `collections` list. Each search has a `type` field (`'lex'` for BM25 keyword search, `'vec'` for semantic vector search, `'hyde'` for hypothetical-document retrieval) plus `query` and `intent` fields. Claude Code: `mcp__plugin_qmd-plugin_qmd__query`. Cursor: qmd MCP server. CLI: `qmd search "{query}"` (BM25) or `qmd vsearch "{query}"` (semantic), with one `-c {collection}` for each collection of `{enrichment_collections}`. See `knowledge/tool-resolution.md`.
|
|
68
81
|
|
|
69
82
|
**Tool probe (graceful degradation):** If any tool-not-found error surfaces while invoking `query` (e.g., legacy `vector_search` still expected by an older client, or a bridge returns "tool not registered"), treat it as a non-fatal failure for that function's enrichment and continue with the remaining functions. Do not retry against the stale `vector_search` tool name — that name was removed from the QMD MCP server. Record the degradation in context for the evidence report.
|
|
70
83
|
|
|
71
|
-
**For each QMD result
|
|
84
|
+
**For each QMD result from a collection of `{enrichment_collections}`** (drop a result from any other collection):
|
|
72
85
|
|
|
73
86
|
- Create a T2 annotation: `[QMD:{collection}:{doc}]`
|
|
74
87
|
- Classify temporal relevance:
|
|
@@ -83,19 +96,24 @@ For each function, derive the **module context** from the extraction inventory's
|
|
|
83
96
|
|
|
84
97
|
### 4. Annotate Extraction Inventory
|
|
85
98
|
|
|
86
|
-
Add enrichment annotations to
|
|
99
|
+
Add the enrichment annotations to `{extraction_inventory}` without modifying its extraction data, so compile reads them from disk. One annotation per finding, for each function that has one:
|
|
87
100
|
|
|
88
|
-
**Per-function enrichment (if found):**
|
|
89
101
|
- Related issues/PRs with summary
|
|
90
102
|
- Changelog history (version changes, breaking changes)
|
|
91
103
|
- Migration/deprecation context
|
|
92
104
|
- T2 provenance citation for each annotation
|
|
93
105
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
106
|
+
Resolve `{extractionInventoryHelper}` ← first existing path in `{extractionInventoryProbeOrder}` and, from `{project-root}`, append them all in one call:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
uv run {extractionInventoryHelper} add --inventory "{extraction_inventory}" --field t2_annotations <<'SKF_T2'
|
|
110
|
+
[{"export_name": "...", "kind": "issue|pr|changelog|migration", "temporal": "T2-past|T2-future", "summary": "...", "citation": "[QMD:{collection}:{doc}]"}]
|
|
111
|
+
SKF_T2
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
When it exits non-zero, fix the JSON and run it once more: an annotation the inventory already holds is not added twice. When it fails again, or no path resolves, warn "T2 annotations were not saved to the extraction inventory: {its message}" and continue without them: enrichment never halts the workflow.
|
|
115
|
+
|
|
116
|
+
**Enrichment summary counts** come from the call's output, never counted by hand: `counts.functions_enriched` (the functions with at least one annotation), `counts.t2_annotations`, `counts.t2_past` and `counts.t2_future` (the annotations by their `temporal`). When the call failed, each is 0.
|
|
99
117
|
|
|
100
118
|
### 5. Report Enrichment (Deep Tier Only)
|
|
101
119
|
|
|
@@ -103,8 +121,8 @@ Display brief enrichment summary:
|
|
|
103
121
|
|
|
104
122
|
"**Enrichment complete.**
|
|
105
123
|
|
|
106
|
-
**Functions enriched:** {
|
|
107
|
-
**T2 annotations:** {
|
|
124
|
+
**Functions enriched:** {counts.functions_enriched} of {the functions §3 searched}
|
|
125
|
+
**T2 annotations:** {counts.t2_annotations} ({counts.t2_past} historical, {counts.t2_future} forward-looking)
|
|
108
126
|
|
|
109
127
|
Proceeding to compilation..."
|
|
110
128
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<!-- Config: communicate in {communication_language}. -->
|
|
2
|
+
|
|
3
|
+
# Entry Points by Hand
|
|
4
|
+
|
|
5
|
+
Step 3 §4b loads this file only when the recipe runner did not diff the entry points: at Quick tier, for extraction by source reading, for a brief whose language no recipe reads, and in the AST Extraction Protocol's fallback. Read the entry points yourself and compare them with the extracted exports, then count `exports_public_api` (the entry points' public names) and `exports_internal` (every other non-underscore export) for step 5 §4:
|
|
6
|
+
|
|
7
|
+
- **Python:** Read `{source_root}/__init__.py`: extract imports to build the public export list. Compare against AST results:
|
|
8
|
+
- In AST but not entry point → mark as internal (exclude from `metadata.json` exports)
|
|
9
|
+
- In entry point but not AST → flag as extraction gap (trace via re-export protocol)
|
|
10
|
+
- **TypeScript/JS:** Read `index.ts`/`index.js`: same comparison logic.
|
|
11
|
+
- **Rust:** Read `lib.rs`: extract `pub use` items. Same logic. **Go:** Scan for exported (capitalized) identifiers.
|
|
12
|
+
|
|
13
|
+
**Multi-entry packages (`exports` map / declaration-file entry points).** A single per-language entry-point read misses public surface that a package ships through its `package.json` `exports` map, especially committed `.d.ts` / `.d.mts` declaration files that resolve **outside** the conventional source dir (e.g. a monorepo package whose `./macro` subpath maps to `macro/index.d.mts`, listed in `files[]` but not under `src/`). When the in-scope package declares an `exports` map:
|
|
14
|
+
|
|
15
|
+
- Resolve each `exports` subpath to its target file and treat that file, and any committed `.d.ts` / `.d.mts` declaration it resolves to, as an authoritative public entry point, reading it the same way as the primary barrel above even when it lives outside `src/`.
|
|
16
|
+
- If a resolved `exports` subpath target falls **outside** the brief's `scope.include` globs, surface a note: `"warn: public entry point {path} (exports subpath '{subpath}') resolves outside scope.include: widen scope.include before extraction, or this surface stays undocumented while exports_public_api still counts it, so public_api_coverage drops (only effective_denominator, for the curated-subset shapes, leaves it out)."` Widening `scope.include` here keeps the documented surface aligned with the `effective_denominator` that compile.md §4 derives from those same globs, without mid-run scope surgery.
|
|
17
|
+
|
|
18
|
+
Use the entry point as the authoritative source for `metadata.json`'s `exports[]` array.
|
|
19
|
+
|
|
20
|
+
**If entry point is missing or unreadable:** Skip validation with a warning.
|