@xulthekl/team-flow 0.22.4
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/.agents/plugins/marketplace.json +20 -0
- package/.agents/skills +1 -0
- package/.claude/always/phase-guard.md +3 -0
- package/.claude-plugin/marketplace.json +20 -0
- package/.claude-plugin/plugin.json +31 -0
- package/.codex-plugin/plugin.json +45 -0
- package/.codexignore +6 -0
- package/.cursor-plugin/marketplace.json +17 -0
- package/.cursor-plugin/plugin.json +31 -0
- package/.github/copilot-instructions.md +15 -0
- package/.github/dependabot.yml +10 -0
- package/.github/plugin/marketplace.json +42 -0
- package/.github/workflows/ci.yml +98 -0
- package/.github/workflows/hol-plugin-scanner.yml +17 -0
- package/.opencode/INSTALL.md +61 -0
- package/.opencode/plugins/team-flow.js +70 -0
- package/AGENTS.md +280 -0
- package/CHANGELOG.md +635 -0
- package/CONTRIBUTING.md +115 -0
- package/GEMINI.md +14 -0
- package/HANDOFF.md +219 -0
- package/INSTALL.md +810 -0
- package/LICENSE +21 -0
- package/README.md +185 -0
- package/SECURITY.md +37 -0
- package/agents/bug-investigator.md +180 -0
- package/agents/change-split-auditor.md +257 -0
- package/agents/code-reviewer.md +171 -0
- package/agents/cross-change-consistency-checker.md +215 -0
- package/agents/prd-completeness-reviewer.md +197 -0
- package/agents/prototype-builder.md +230 -0
- package/agents/prototype-env-scout.md +156 -0
- package/agents/prototype-reviewer.md +226 -0
- package/assets/icon.svg +6 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +5 -0
- package/dist/parsing/change-parser.d.ts +12 -0
- package/dist/parsing/change-parser.js +54 -0
- package/dist/parsing/requirement-blocks.d.ts +31 -0
- package/dist/parsing/requirement-blocks.js +192 -0
- package/dist/schema/base.d.ts +7 -0
- package/dist/schema/base.js +1 -0
- package/dist/schema/change.d.ts +25 -0
- package/dist/schema/change.js +1 -0
- package/dist/schema/index.d.ts +3 -0
- package/dist/schema/index.js +1 -0
- package/dist/schema/spec.d.ts +11 -0
- package/dist/schema/spec.js +1 -0
- package/dist/validation/constants.d.ts +42 -0
- package/dist/validation/constants.js +42 -0
- package/dist/validation/tokenizer.d.ts +17 -0
- package/dist/validation/tokenizer.js +173 -0
- package/dist/validation/types.d.ts +40 -0
- package/dist/validation/types.js +1 -0
- package/dist/validation/validator.d.ts +21 -0
- package/dist/validation/validator.js +489 -0
- package/docs/README_en.md +358 -0
- package/docs/artifact-contract.md +96 -0
- package/docs/decision-points.md +88 -0
- package/docs/e2e-integration-design.md +207 -0
- package/docs/examples/README.md +25 -0
- package/docs/examples/add-dark-mode/README.md +13 -0
- package/docs/examples/add-dark-mode/design.md +49 -0
- package/docs/examples/add-dark-mode/execution-contract.md +56 -0
- package/docs/examples/add-dark-mode/proposal.md +46 -0
- package/docs/examples/add-dark-mode/specs/ui-theme/spec.md +35 -0
- package/docs/examples/add-dark-mode/tasks.md +26 -0
- package/docs/examples/refactor-auth-boundary/README.md +15 -0
- package/docs/examples/refactor-auth-boundary/design.md +41 -0
- package/docs/examples/refactor-auth-boundary/execution-contract.md +61 -0
- package/docs/examples/refactor-auth-boundary/proposal.md +47 -0
- package/docs/examples/refactor-auth-boundary/specs/auth-boundary/spec.md +45 -0
- package/docs/examples/refactor-auth-boundary/tasks.md +25 -0
- package/docs/plans/2026-07-07-fix-batch-design.md +68 -0
- package/docs/plans/2026-07-20-001-refactor-skill-collaboration-protocol-plan.md +128 -0
- package/docs/platform-matrix.md +45 -0
- package/docs/prototype-design-research.md +206 -0
- package/docs/release-checklist.md +96 -0
- package/docs/showcase.html +699 -0
- package/docs/skill-rename-v0.8.0.md +49 -0
- package/docs/state-machine.md +162 -0
- package/gemini-extension.json +6 -0
- package/hooks/hooks-cursor.json +8 -0
- package/hooks/hooks.json +26 -0
- package/hooks/pre-tool-use-guard +116 -0
- package/hooks/session-start +14 -0
- package/llms.txt +51 -0
- package/package.json +48 -0
- package/plugin.json +27 -0
- package/prd/v1/plan.md +443 -0
- package/prd/v1/prd.md +580 -0
- package/scripts/check-update.mjs +91 -0
- package/scripts/check-version-consistency.mjs +345 -0
- package/scripts/ensure-branch.mjs +71 -0
- package/scripts/get-config +31 -0
- package/scripts/guard/checks/artifacts-exist.mjs +36 -0
- package/scripts/guard/checks/contract-current.mjs +33 -0
- package/scripts/guard/checks/contract-fresh.mjs +17 -0
- package/scripts/guard/checks/dp-gate-passed.mjs +35 -0
- package/scripts/guard/checks/dp3-approved.mjs +22 -0
- package/scripts/guard/checks/execution-plan-ready.mjs +40 -0
- package/scripts/guard/checks/execution-reviews-passed.mjs +36 -0
- package/scripts/guard/checks/schema-valid.mjs +63 -0
- package/scripts/guard/checks/specs-merged.mjs +51 -0
- package/scripts/guard/checks/tasks-complete.mjs +31 -0
- package/scripts/guard/checks/tests-passing.mjs +31 -0
- package/scripts/guard/design-token-guard.mjs +237 -0
- package/scripts/guard/guard.mjs +205 -0
- package/scripts/infer-workflow.mjs +136 -0
- package/scripts/install-amazon-q.mjs +9 -0
- package/scripts/install-cline.mjs +9 -0
- package/scripts/install-continue.mjs +9 -0
- package/scripts/install-cursor.mjs +265 -0
- package/scripts/install-git-hooks.mjs +82 -0
- package/scripts/install-kiro.mjs +9 -0
- package/scripts/install-pi.mjs +9 -0
- package/scripts/install-qoder.mjs +9 -0
- package/scripts/install-qwen.mjs +9 -0
- package/scripts/install-roocode.mjs +9 -0
- package/scripts/install-windsurf.mjs +9 -0
- package/scripts/install-zcode.mjs +265 -0
- package/scripts/lib/cmd-audit.mjs +127 -0
- package/scripts/lib/cmd-checkpoint.mjs +118 -0
- package/scripts/lib/cmd-config.mjs +88 -0
- package/scripts/lib/cmd-doctor.mjs +267 -0
- package/scripts/lib/cmd-execution.mjs +206 -0
- package/scripts/lib/cmd-handoff.mjs +94 -0
- package/scripts/lib/cmd-inject.mjs +310 -0
- package/scripts/lib/cmd-install-amazon-q.mjs +11 -0
- package/scripts/lib/cmd-install-cline.mjs +11 -0
- package/scripts/lib/cmd-install-continue.mjs +11 -0
- package/scripts/lib/cmd-install-cursor.mjs +14 -0
- package/scripts/lib/cmd-install-kiro.mjs +11 -0
- package/scripts/lib/cmd-install-pi.mjs +11 -0
- package/scripts/lib/cmd-install-qoder.mjs +11 -0
- package/scripts/lib/cmd-install-qwen.mjs +11 -0
- package/scripts/lib/cmd-install-roocode.mjs +11 -0
- package/scripts/lib/cmd-install-windsurf.mjs +11 -0
- package/scripts/lib/cmd-install-workbuddy.mjs +359 -0
- package/scripts/lib/cmd-install-zcode.mjs +14 -0
- package/scripts/lib/cmd-isolate.mjs +37 -0
- package/scripts/lib/cmd-list.mjs +71 -0
- package/scripts/lib/cmd-runtime.mjs +85 -0
- package/scripts/lib/cmd-solutions.mjs +71 -0
- package/scripts/lib/cmd-state.mjs +231 -0
- package/scripts/lib/cmd-sync.mjs +102 -0
- package/scripts/lib/cmd-validate.mjs +105 -0
- package/scripts/lib/cmd-version.mjs +148 -0
- package/scripts/lib/config-loader.mjs +107 -0
- package/scripts/lib/execution-plan.mjs +492 -0
- package/scripts/lib/execution-recommendation.mjs +221 -0
- package/scripts/lib/hash.mjs +74 -0
- package/scripts/lib/install.mjs +267 -0
- package/scripts/lib/platform-runtime-inventory.mjs +27 -0
- package/scripts/lib/platforms.mjs +165 -0
- package/scripts/lib/prototype-sync.mjs +192 -0
- package/scripts/lib/sdd-overlay.mjs +262 -0
- package/scripts/lib/shell-quote.mjs +4 -0
- package/scripts/lib/solutions-capture.mjs +102 -0
- package/scripts/lib/solutions-index-gen.mjs +112 -0
- package/scripts/lib/solutions-inject.mjs +77 -0
- package/scripts/lib/solutions-promote.mjs +145 -0
- package/scripts/lib/spec-paths.mjs +85 -0
- package/scripts/lib/state-loader.mjs +154 -0
- package/scripts/lint/lint-skills.mjs +149 -0
- package/scripts/lint/rules/behavior-consistency.mjs +56 -0
- package/scripts/lint/rules/dp-trigger-points.mjs +60 -0
- package/scripts/lint/rules/exception-handling.mjs +59 -0
- package/scripts/lint/rules/no-contradictory-instructions.mjs +67 -0
- package/scripts/lint/rules/no-redundant-checks.mjs +50 -0
- package/scripts/lint/rules/token-rules.mjs +122 -0
- package/scripts/review-package +44 -0
- package/scripts/task-brief +40 -0
- package/scripts/team-flow.mjs +164 -0
- package/scripts/token-baseline.mjs +158 -0
- package/scripts/validate-artifacts +96 -0
- package/scripts/verify-marketplace-release.mjs +59 -0
- package/skills/architecture-design/SKILL.md +140 -0
- package/skills/architecture-design/chapters/ch01-4a-domains.md +27 -0
- package/skills/architecture-design/chapters/ch02-change-cascade.md +27 -0
- package/skills/architecture-design/chapters/ch03-architecture-outputs.md +25 -0
- package/skills/architecture-design/chapters/ch04-entity-to-aggregate.md +27 -0
- package/skills/architecture-design/chapters/ch05-cqrs.md +32 -0
- package/skills/architecture-design/chapters/ch06-integration.md +54 -0
- package/skills/architecture-design/cheatsheet.md +51 -0
- package/skills/architecture-design/glossary.md +23 -0
- package/skills/architecture-design/patterns.md +32 -0
- package/skills/bug-investigator/SKILL.md +77 -0
- package/skills/build-executor/SKILL.md +163 -0
- package/skills/build-executor/implementer-prompt.md +162 -0
- package/skills/build-executor/references/execution-modes.md +86 -0
- package/skills/build-executor/task-reviewer-prompt.md +195 -0
- package/skills/ce-brainstorm/SKILL.md +415 -0
- package/skills/ce-brainstorm/references/agents/slack-researcher.md +127 -0
- package/skills/ce-brainstorm/references/blindspot-pass.md +70 -0
- package/skills/ce-brainstorm/references/brainstorm-sections.md +285 -0
- package/skills/ce-brainstorm/references/handoff.md +168 -0
- package/skills/ce-brainstorm/references/html-rendering.md +634 -0
- package/skills/ce-brainstorm/references/markdown-rendering.md +241 -0
- package/skills/ce-brainstorm/references/model-tiers.md +9 -0
- package/skills/ce-brainstorm/references/prd-mapping.md +71 -0
- package/skills/ce-brainstorm/references/product-pressure-test.md +42 -0
- package/skills/ce-brainstorm/references/reasoning-elevation.md +63 -0
- package/skills/ce-brainstorm/references/settled-decisions.md +45 -0
- package/skills/ce-brainstorm/references/synthesis-summary.md +282 -0
- package/skills/ce-brainstorm/references/universal-brainstorming.md +73 -0
- package/skills/ce-brainstorm/references/verdict-routing.md +26 -0
- package/skills/ce-brainstorm/references/visual-probes.md +158 -0
- package/skills/ce-brainstorm/scripts/visual-probe-server.js +418 -0
- package/skills/ce-compound/SKILL.md +114 -0
- package/skills/ce-compound/assets/resolution-template.md +94 -0
- package/skills/ce-compound/references/agents/best-practices-researcher.md +115 -0
- package/skills/ce-compound/references/agents/data-integrity-guardian.md +68 -0
- package/skills/ce-compound/references/agents/framework-docs-researcher.md +93 -0
- package/skills/ce-compound/references/agents/pattern-recognition-specialist.md +55 -0
- package/skills/ce-compound/references/agents/performance-oracle.md +108 -0
- package/skills/ce-compound/references/agents/security-sentinel.md +91 -0
- package/skills/ce-compound/references/agents/session-historian.md +83 -0
- package/skills/ce-compound/references/concepts-vocabulary.md +78 -0
- package/skills/ce-compound/references/full-mode-workflow.md +413 -0
- package/skills/ce-compound/references/grounding-validation.md +84 -0
- package/skills/ce-compound/references/lightweight-mode.md +53 -0
- package/skills/ce-compound/references/output-standards.md +182 -0
- package/skills/ce-compound/references/promotion-rules.md +38 -0
- package/skills/ce-compound/references/schema.yaml +231 -0
- package/skills/ce-compound/references/three-tier-index.md +55 -0
- package/skills/ce-compound/references/v0.5-upgrade.md +32 -0
- package/skills/ce-compound/references/write-flow.md +36 -0
- package/skills/ce-compound/references/yaml-schema.md +118 -0
- package/skills/ce-compound/scripts/session-history/discover-sessions.sh +130 -0
- package/skills/ce-compound/scripts/session-history/extract-errors.py +254 -0
- package/skills/ce-compound/scripts/session-history/extract-metadata.py +456 -0
- package/skills/ce-compound/scripts/session-history/extract-skeleton.py +575 -0
- package/skills/ce-compound/scripts/validate-doc-claims.py +346 -0
- package/skills/ce-compound/scripts/validate-frontmatter.py +137 -0
- package/skills/ce-ideate/SKILL.md +402 -0
- package/skills/ce-ideate/references/agents/issue-intelligence-analyst.md +200 -0
- package/skills/ce-ideate/references/agents/learnings-researcher.md +247 -0
- package/skills/ce-ideate/references/agents/slack-researcher.md +127 -0
- package/skills/ce-ideate/references/agents/web-researcher.md +121 -0
- package/skills/ce-ideate/references/divergent-ideation.md +89 -0
- package/skills/ce-ideate/references/html-rendering.md +634 -0
- package/skills/ce-ideate/references/ideation-sections.md +191 -0
- package/skills/ce-ideate/references/markdown-rendering.md +238 -0
- package/skills/ce-ideate/references/post-ideation-workflow.md +167 -0
- package/skills/ce-ideate/references/universal-ideation.md +107 -0
- package/skills/ce-ideate/references/web-research-cache.md +56 -0
- package/skills/ce-plan/SKILL.md +137 -0
- package/skills/ce-plan/references/agents/agent-native-planning-strategist.md +62 -0
- package/skills/ce-plan/references/agents/architecture-strategist.md +46 -0
- package/skills/ce-plan/references/agents/best-practices-researcher.md +114 -0
- package/skills/ce-plan/references/agents/data-integrity-guardian.md +68 -0
- package/skills/ce-plan/references/agents/data-migration-reviewer.md +103 -0
- package/skills/ce-plan/references/agents/deployment-verification-agent.md +157 -0
- package/skills/ce-plan/references/agents/framework-docs-researcher.md +93 -0
- package/skills/ce-plan/references/agents/git-history-analyzer.md +40 -0
- package/skills/ce-plan/references/agents/learnings-researcher.md +247 -0
- package/skills/ce-plan/references/agents/pattern-recognition-specialist.md +55 -0
- package/skills/ce-plan/references/agents/performance-oracle.md +108 -0
- package/skills/ce-plan/references/agents/repo-research-analyst.md +258 -0
- package/skills/ce-plan/references/agents/security-sentinel.md +91 -0
- package/skills/ce-plan/references/agents/slack-researcher.md +127 -0
- package/skills/ce-plan/references/agents/spec-flow-analyzer.md +80 -0
- package/skills/ce-plan/references/agents/web-researcher.md +121 -0
- package/skills/ce-plan/references/approach-altitude.md +55 -0
- package/skills/ce-plan/references/change-splitting.md +65 -0
- package/skills/ce-plan/references/completion-contract.md +33 -0
- package/skills/ce-plan/references/core-principles.md +40 -0
- package/skills/ce-plan/references/deepening-workflow.md +263 -0
- package/skills/ce-plan/references/dependency-graph.md +36 -0
- package/skills/ce-plan/references/html-rendering.md +634 -0
- package/skills/ce-plan/references/intake-routing.md +189 -0
- package/skills/ce-plan/references/markdown-rendering.md +238 -0
- package/skills/ce-plan/references/plan-handoff.md +150 -0
- package/skills/ce-plan/references/plan-sections.md +427 -0
- package/skills/ce-plan/references/plan-structure.md +120 -0
- package/skills/ce-plan/references/planning-modes.md +69 -0
- package/skills/ce-plan/references/reasoning-elevation.md +63 -0
- package/skills/ce-plan/references/research-workflow.md +165 -0
- package/skills/ce-plan/references/review-and-write.md +127 -0
- package/skills/ce-plan/references/settled-decisions.md +45 -0
- package/skills/ce-plan/references/synthesis-summary.md +418 -0
- package/skills/ce-plan/references/universal-planning.md +168 -0
- package/skills/ce-proof/SKILL.md +346 -0
- package/skills/ce-strategy/SKILL.md +97 -0
- package/skills/ce-strategy/references/interview.md +143 -0
- package/skills/ce-strategy/references/strategy-template.md +89 -0
- package/skills/code-reviewer/SKILL.md +84 -0
- package/skills/code-reviewer/code-reviewer-prompt.md +195 -0
- package/skills/contract-builder/SKILL.md +72 -0
- package/skills/design-system/SKILL.md +75 -0
- package/skills/design-system/references/agents/design-system-architect.md +138 -0
- package/skills/design-system/references/creation-flow.md +170 -0
- package/skills/design-system/references/preview-template.html +319 -0
- package/skills/design-system/references/token-derivation.md +115 -0
- package/skills/design-system/references/variant-schema.md +60 -0
- package/skills/e2e/SKILL.md +56 -0
- package/skills/e2e/references/test-verifier.md +40 -0
- package/skills/need-explorer/SKILL.md +79 -0
- package/skills/prototype/SKILL.md +98 -0
- package/skills/prototype/references/agents/design-system-architect.md +122 -0
- package/skills/prototype/references/checklist.md +82 -0
- package/skills/prototype/references/craft/accessibility-baseline.md +71 -0
- package/skills/prototype/references/craft/anti-ai-slop.md +87 -0
- package/skills/prototype/references/craft/laws-of-ux.md +60 -0
- package/skills/prototype/references/craft/state-coverage.md +81 -0
- package/skills/prototype/references/craft/typography-hierarchy.md +50 -0
- package/skills/prototype/references/layouts.md +223 -0
- package/skills/prototype/references/orchestration-flow.md +101 -0
- package/skills/prototype/references/prototype-scaffold/assets/design-tokens.css +19 -0
- package/skills/prototype/references/prototype-scaffold/components/button.html +3 -0
- package/skills/prototype/references/prototype-scaffold/design-system.md +7 -0
- package/skills/prototype/references/prototype-scaffold/flow.md +11 -0
- package/skills/prototype/references/prototype-scaffold/index.html +20 -0
- package/skills/prototype/references/prototype-scaffold/pages/index.html +11 -0
- package/skills/prototype/references/template.html +396 -0
- package/skills/release-archivist/SKILL.md +166 -0
- package/skills/release-archivist/references/closing-procedures.md +81 -0
- package/skills/session-handoff/SKILL.md +110 -0
- package/skills/session-handoff/references/context-extraction.md +70 -0
- package/skills/session-handoff/references/handoff-template.md +90 -0
- package/skills/session-handoff/references/skill-recommendation.md +43 -0
- package/skills/spec-merger/SKILL.md +66 -0
- package/skills/spec-writer/SKILL.md +149 -0
- package/skills/workflow-bootstrap/SKILL.md +148 -0
- package/skills/workflow-bootstrap/references/agents/codebase-recon-analyst.md +121 -0
- package/skills/workflow-bootstrap/references/b1-reconnaissance.md +80 -0
- package/skills/workflow-bootstrap/scripts/recon-probe.sh +328 -0
- package/skills/workflow-feedback/SKILL.md +120 -0
- package/skills/workflow-feedback/references/category-guide.md +121 -0
- package/skills/workflow-feedback/references/feedback-template.md +73 -0
- package/skills/workflow-feedback/references/review-mode.md +92 -0
- package/skills/workflow-orchestrator/SKILL.md +94 -0
- package/skills/workflow-orchestrator/references/feedback-loops.md +56 -0
- package/skills/workflow-orchestrator/references/s1-path-router.md +81 -0
- package/skills/workflow-orchestrator/references/s2-prd-prototype-loop.md +96 -0
- package/skills/workflow-orchestrator/references/s3-plan-pipeline.md +50 -0
- package/skills/workflow-orchestrator/references/s4-split-validate.md +108 -0
- package/skills/workflow-orchestrator/references/s5-monitoring.md +61 -0
- package/skills/workflow-orchestrator/references/state-model.md +185 -0
- package/skills/workflow-start/SKILL.md +147 -0
- package/skills/workflow-start/references/routing-rules.md +155 -0
- package/src/index.ts +16 -0
- package/src/parsing/change-parser.ts +79 -0
- package/src/parsing/requirement-blocks.ts +244 -0
- package/src/schema/base.ts +8 -0
- package/src/schema/change.ts +29 -0
- package/src/schema/index.ts +3 -0
- package/src/schema/spec.ts +12 -0
- package/src/validation/constants.ts +53 -0
- package/src/validation/tokenizer.ts +180 -0
- package/src/validation/types.ts +47 -0
- package/src/validation/validator.ts +552 -0
- package/templates/abandonment-summary.md +29 -0
- package/templates/design.md +37 -0
- package/templates/execution-contract.md +99 -0
- package/templates/plan.md +86 -0
- package/templates/prd.md +468 -0
- package/templates/proposal.md +37 -0
- package/templates/spec.md +31 -0
- package/templates/tasks.md +47 -0
- package/tests/e2e.test.mjs +266 -0
- package/tests/lib/cmd-audit.test.mjs +202 -0
- package/tests/lib/cmd-checkpoint.test.mjs +98 -0
- package/tests/lib/cmd-config.test.mjs +101 -0
- package/tests/lib/cmd-doctor.test.mjs +450 -0
- package/tests/lib/cmd-execution.test.mjs +647 -0
- package/tests/lib/cmd-handoff.test.mjs +137 -0
- package/tests/lib/cmd-inject.test.mjs +218 -0
- package/tests/lib/cmd-install-workbuddy.test.mjs +113 -0
- package/tests/lib/cmd-install-zcode.test.mjs +42 -0
- package/tests/lib/cmd-isolate.test.mjs +68 -0
- package/tests/lib/cmd-list.test.mjs +127 -0
- package/tests/lib/cmd-runtime.test.mjs +83 -0
- package/tests/lib/cmd-state-missing.test.mjs +65 -0
- package/tests/lib/cmd-state.test.mjs +365 -0
- package/tests/lib/cmd-sync-paths.test.mjs +84 -0
- package/tests/lib/cmd-validate-paths.test.mjs +90 -0
- package/tests/lib/config-loader.test.mjs +175 -0
- package/tests/lib/ensure-branch.test.mjs +59 -0
- package/tests/lib/execution-control-plane.test.mjs +168 -0
- package/tests/lib/execution-plan.test.mjs +474 -0
- package/tests/lib/guard-specs-merged.test.mjs +108 -0
- package/tests/lib/guard-tests-passing.test.mjs +112 -0
- package/tests/lib/guard-transitions.test.mjs +262 -0
- package/tests/lib/guard.test.mjs +611 -0
- package/tests/lib/hash.test.mjs +138 -0
- package/tests/lib/infer-workflow.test.mjs +161 -0
- package/tests/lib/install-git-hooks.test.mjs +20 -0
- package/tests/lib/marketplace-release-docs.test.mjs +31 -0
- package/tests/lib/minimality-discipline.test.mjs +37 -0
- package/tests/lib/model-profiles-docs.test.mjs +33 -0
- package/tests/lib/node20-compatibility.test.mjs +73 -0
- package/tests/lib/node20-test-entry.test.mjs +17 -0
- package/tests/lib/platform-runtime-distribution.test.mjs +150 -0
- package/tests/lib/raw-mode-smoke.test.mjs +42 -0
- package/tests/lib/sdd-overlay.test.mjs +118 -0
- package/tests/lib/spec-paths.test.mjs +80 -0
- package/tests/lib/state-loader.test.mjs +251 -0
- package/tests/lib/token-baseline.test.mjs +72 -0
- package/tests/lib/token-rules.test.mjs +94 -0
- package/tests/lib/verify-marketplace-release.test.mjs +128 -0
- package/tests/tsconfig.json +12 -0
- package/token-baseline.json +95 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
export interface RequirementBlock {
|
|
2
|
+
headerLine: string;
|
|
3
|
+
name: string;
|
|
4
|
+
raw: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface RequirementsSectionParts {
|
|
8
|
+
before: string;
|
|
9
|
+
headerLine: string;
|
|
10
|
+
preamble: string;
|
|
11
|
+
bodyBlocks: RequirementBlock[];
|
|
12
|
+
after: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function normalizeRequirementName(name: string): string {
|
|
16
|
+
return name.trim();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const REQUIREMENT_HEADER_REGEX = /^###\s*Requirement:\s*(.+)\s*$/i;
|
|
20
|
+
|
|
21
|
+
function normalizeLineEndings(content: string): string {
|
|
22
|
+
return content.replace(/\r\n?/g, '\n');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function extractRequirementsSection(content: string): RequirementsSectionParts {
|
|
26
|
+
const normalized = normalizeLineEndings(content);
|
|
27
|
+
const lines = normalized.split('\n');
|
|
28
|
+
const reqHeaderIndex = lines.findIndex((l) => /^##\s+Requirements\s*$/i.test(l));
|
|
29
|
+
|
|
30
|
+
if (reqHeaderIndex === -1) {
|
|
31
|
+
const before = content.trimEnd();
|
|
32
|
+
const headerLine = '## Requirements';
|
|
33
|
+
return {
|
|
34
|
+
before: before ? before + '\n\n' : '',
|
|
35
|
+
headerLine,
|
|
36
|
+
preamble: '',
|
|
37
|
+
bodyBlocks: [],
|
|
38
|
+
after: '\n',
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let endIndex = lines.length;
|
|
43
|
+
for (let i = reqHeaderIndex + 1; i < lines.length; i++) {
|
|
44
|
+
if (/^##\s+/.test(lines[i])) {
|
|
45
|
+
endIndex = i;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const before = lines.slice(0, reqHeaderIndex).join('\n');
|
|
51
|
+
const headerLine = lines[reqHeaderIndex];
|
|
52
|
+
const sectionBodyLines = lines.slice(reqHeaderIndex + 1, endIndex);
|
|
53
|
+
|
|
54
|
+
const blocks: RequirementBlock[] = [];
|
|
55
|
+
let cursor = 0;
|
|
56
|
+
let preambleLines: string[] = [];
|
|
57
|
+
|
|
58
|
+
while (
|
|
59
|
+
cursor < sectionBodyLines.length &&
|
|
60
|
+
!REQUIREMENT_HEADER_REGEX.test(sectionBodyLines[cursor])
|
|
61
|
+
) {
|
|
62
|
+
preambleLines.push(sectionBodyLines[cursor]);
|
|
63
|
+
cursor++;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
while (cursor < sectionBodyLines.length) {
|
|
67
|
+
const headerLineCandidate = sectionBodyLines[cursor];
|
|
68
|
+
const headerMatch = headerLineCandidate.match(REQUIREMENT_HEADER_REGEX);
|
|
69
|
+
if (!headerMatch) {
|
|
70
|
+
cursor++;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const name = normalizeRequirementName(headerMatch[1]);
|
|
74
|
+
cursor++;
|
|
75
|
+
const bodyLines: string[] = [headerLineCandidate];
|
|
76
|
+
while (
|
|
77
|
+
cursor < sectionBodyLines.length &&
|
|
78
|
+
!REQUIREMENT_HEADER_REGEX.test(sectionBodyLines[cursor]) &&
|
|
79
|
+
!/^##\s+/.test(sectionBodyLines[cursor])
|
|
80
|
+
) {
|
|
81
|
+
bodyLines.push(sectionBodyLines[cursor]);
|
|
82
|
+
cursor++;
|
|
83
|
+
}
|
|
84
|
+
const raw = bodyLines.join('\n').trimEnd();
|
|
85
|
+
blocks.push({ headerLine: headerLineCandidate, name, raw });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const after = lines.slice(endIndex).join('\n');
|
|
89
|
+
const preamble = preambleLines.join('\n').trimEnd();
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
before: before.trimEnd() ? before + '\n' : before,
|
|
93
|
+
headerLine,
|
|
94
|
+
preamble,
|
|
95
|
+
bodyBlocks: blocks,
|
|
96
|
+
after: after.startsWith('\n') ? after : '\n' + after,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface DeltaPlan {
|
|
101
|
+
added: RequirementBlock[];
|
|
102
|
+
modified: RequirementBlock[];
|
|
103
|
+
removed: string[];
|
|
104
|
+
renamed: Array<{ from: string; to: string }>;
|
|
105
|
+
sectionPresence: {
|
|
106
|
+
added: boolean;
|
|
107
|
+
modified: boolean;
|
|
108
|
+
removed: boolean;
|
|
109
|
+
renamed: boolean;
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function splitTopLevelSections(content: string): Record<string, string> {
|
|
114
|
+
const lines = content.split('\n');
|
|
115
|
+
const result: Record<string, string> = {};
|
|
116
|
+
const indices: Array<{ title: string; index: number; level: number }> = [];
|
|
117
|
+
for (let i = 0; i < lines.length; i++) {
|
|
118
|
+
const m = lines[i].match(/^(##)\s+(.+)$/);
|
|
119
|
+
if (m) {
|
|
120
|
+
const level = m[1].length;
|
|
121
|
+
indices.push({ title: m[2].trim(), index: i, level });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
for (let i = 0; i < indices.length; i++) {
|
|
125
|
+
const current = indices[i];
|
|
126
|
+
const next = indices[i + 1];
|
|
127
|
+
const body = lines
|
|
128
|
+
.slice(current.index + 1, next ? next.index : lines.length)
|
|
129
|
+
.join('\n');
|
|
130
|
+
result[current.title] = body;
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function getSectionCaseInsensitive(
|
|
136
|
+
sections: Record<string, string>,
|
|
137
|
+
desired: string
|
|
138
|
+
): { body: string; found: boolean } {
|
|
139
|
+
const target = desired.toLowerCase();
|
|
140
|
+
for (const [title, body] of Object.entries(sections)) {
|
|
141
|
+
if (title.toLowerCase() === target) return { body, found: true };
|
|
142
|
+
}
|
|
143
|
+
return { body: '', found: false };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function parseRequirementBlocksFromSection(sectionBody: string): RequirementBlock[] {
|
|
147
|
+
if (!sectionBody) return [];
|
|
148
|
+
const lines = normalizeLineEndings(sectionBody).split('\n');
|
|
149
|
+
const blocks: RequirementBlock[] = [];
|
|
150
|
+
let i = 0;
|
|
151
|
+
while (i < lines.length) {
|
|
152
|
+
while (i < lines.length && !REQUIREMENT_HEADER_REGEX.test(lines[i])) i++;
|
|
153
|
+
if (i >= lines.length) break;
|
|
154
|
+
const headerLine = lines[i];
|
|
155
|
+
const m = headerLine.match(REQUIREMENT_HEADER_REGEX);
|
|
156
|
+
if (!m) {
|
|
157
|
+
i++;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const name = normalizeRequirementName(m[1]);
|
|
161
|
+
const buf: string[] = [headerLine];
|
|
162
|
+
i++;
|
|
163
|
+
while (
|
|
164
|
+
i < lines.length &&
|
|
165
|
+
!REQUIREMENT_HEADER_REGEX.test(lines[i]) &&
|
|
166
|
+
!/^##\s+/.test(lines[i])
|
|
167
|
+
) {
|
|
168
|
+
buf.push(lines[i]);
|
|
169
|
+
i++;
|
|
170
|
+
}
|
|
171
|
+
blocks.push({ headerLine, name, raw: buf.join('\n').trimEnd() });
|
|
172
|
+
}
|
|
173
|
+
return blocks;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function parseRemovedNames(sectionBody: string): string[] {
|
|
177
|
+
if (!sectionBody) return [];
|
|
178
|
+
const names: string[] = [];
|
|
179
|
+
const lines = normalizeLineEndings(sectionBody).split('\n');
|
|
180
|
+
for (const line of lines) {
|
|
181
|
+
const m = line.match(REQUIREMENT_HEADER_REGEX);
|
|
182
|
+
if (m) {
|
|
183
|
+
names.push(normalizeRequirementName(m[1]));
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
const bullet = line.match(/^\s*-\s*`?###\s*Requirement:\s*(.+?)`?\s*$/);
|
|
187
|
+
if (bullet) {
|
|
188
|
+
names.push(normalizeRequirementName(bullet[1]));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return names;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function parseRenamedPairs(
|
|
195
|
+
sectionBody: string
|
|
196
|
+
): Array<{ from: string; to: string }> {
|
|
197
|
+
if (!sectionBody) return [];
|
|
198
|
+
const pairs: Array<{ from: string; to: string }> = [];
|
|
199
|
+
const lines = normalizeLineEndings(sectionBody).split('\n');
|
|
200
|
+
let current: { from?: string; to?: string } = {};
|
|
201
|
+
for (const line of lines) {
|
|
202
|
+
const fromMatch = line.match(
|
|
203
|
+
/^\s*-?\s*FROM:\s*`?###\s*Requirement:\s*(.+?)`?\s*$/
|
|
204
|
+
);
|
|
205
|
+
const toMatch = line.match(
|
|
206
|
+
/^\s*-?\s*TO:\s*`?###\s*Requirement:\s*(.+?)`?\s*$/
|
|
207
|
+
);
|
|
208
|
+
if (fromMatch) {
|
|
209
|
+
current.from = normalizeRequirementName(fromMatch[1]);
|
|
210
|
+
} else if (toMatch) {
|
|
211
|
+
current.to = normalizeRequirementName(toMatch[1]);
|
|
212
|
+
if (current.from && current.to) {
|
|
213
|
+
pairs.push({ from: current.from, to: current.to });
|
|
214
|
+
current = {};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return pairs;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export function parseDeltaSpec(content: string): DeltaPlan {
|
|
222
|
+
const normalized = normalizeLineEndings(content);
|
|
223
|
+
const sections = splitTopLevelSections(normalized);
|
|
224
|
+
const addedLookup = getSectionCaseInsensitive(sections, 'ADDED Requirements');
|
|
225
|
+
const modifiedLookup = getSectionCaseInsensitive(sections, 'MODIFIED Requirements');
|
|
226
|
+
const removedLookup = getSectionCaseInsensitive(sections, 'REMOVED Requirements');
|
|
227
|
+
const renamedLookup = getSectionCaseInsensitive(sections, 'RENAMED Requirements');
|
|
228
|
+
const added = parseRequirementBlocksFromSection(addedLookup.body);
|
|
229
|
+
const modified = parseRequirementBlocksFromSection(modifiedLookup.body);
|
|
230
|
+
const removedNames = parseRemovedNames(removedLookup.body);
|
|
231
|
+
const renamedPairs = parseRenamedPairs(renamedLookup.body);
|
|
232
|
+
return {
|
|
233
|
+
added,
|
|
234
|
+
modified,
|
|
235
|
+
removed: removedNames,
|
|
236
|
+
renamed: renamedPairs,
|
|
237
|
+
sectionPresence: {
|
|
238
|
+
added: addedLookup.found,
|
|
239
|
+
modified: modifiedLookup.found,
|
|
240
|
+
removed: removedLookup.found,
|
|
241
|
+
renamed: renamedLookup.found,
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Requirement } from './base.js';
|
|
2
|
+
|
|
3
|
+
export type DeltaOperationType = 'ADDED' | 'MODIFIED' | 'REMOVED' | 'RENAMED';
|
|
4
|
+
|
|
5
|
+
export interface Rename {
|
|
6
|
+
from: string;
|
|
7
|
+
to: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface Delta {
|
|
11
|
+
spec: string;
|
|
12
|
+
operation: DeltaOperationType;
|
|
13
|
+
description: string;
|
|
14
|
+
requirement?: Requirement;
|
|
15
|
+
requirements?: Requirement[];
|
|
16
|
+
rename?: Rename;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface Change {
|
|
20
|
+
name: string;
|
|
21
|
+
why: string;
|
|
22
|
+
whatChanges: string;
|
|
23
|
+
deltas: Delta[];
|
|
24
|
+
metadata?: {
|
|
25
|
+
version?: string;
|
|
26
|
+
format?: string;
|
|
27
|
+
sourcePath?: string;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export const MIN_WHY_SECTION_LENGTH = 50;
|
|
2
|
+
export const MIN_PURPOSE_LENGTH = 50;
|
|
3
|
+
|
|
4
|
+
export const MAX_WHY_SECTION_LENGTH = 1000;
|
|
5
|
+
export const MAX_REQUIREMENT_TEXT_LENGTH = 500;
|
|
6
|
+
export const MAX_DELTAS_PER_CHANGE = 10;
|
|
7
|
+
|
|
8
|
+
export const VALIDATION_MESSAGES = {
|
|
9
|
+
SCENARIO_EMPTY: 'Scenario text cannot be empty',
|
|
10
|
+
REQUIREMENT_EMPTY: 'Requirement text cannot be empty',
|
|
11
|
+
REQUIREMENT_NO_SHALL: 'Requirement must contain SHALL or MUST keyword',
|
|
12
|
+
REQUIREMENT_NO_SCENARIOS: 'Requirement must have at least one scenario',
|
|
13
|
+
SPEC_NAME_EMPTY: 'Spec name cannot be empty',
|
|
14
|
+
SPEC_PURPOSE_EMPTY: 'Purpose section cannot be empty',
|
|
15
|
+
SPEC_NO_REQUIREMENTS: 'Spec must have at least one requirement',
|
|
16
|
+
CHANGE_NAME_EMPTY: 'Change name cannot be empty',
|
|
17
|
+
CHANGE_WHY_TOO_SHORT: `Why section must be at least ${MIN_WHY_SECTION_LENGTH} characters`,
|
|
18
|
+
CHANGE_WHY_TOO_LONG: `Why section should not exceed ${MAX_WHY_SECTION_LENGTH} characters`,
|
|
19
|
+
CHANGE_WHAT_EMPTY: 'What Changes section cannot be empty',
|
|
20
|
+
CHANGE_NO_DELTAS: 'Change must have at least one delta',
|
|
21
|
+
CHANGE_TOO_MANY_DELTAS: `Consider splitting changes with more than ${MAX_DELTAS_PER_CHANGE} deltas`,
|
|
22
|
+
DELTA_SPEC_EMPTY: 'Spec name cannot be empty',
|
|
23
|
+
DELTA_DESCRIPTION_EMPTY: 'Delta description cannot be empty',
|
|
24
|
+
|
|
25
|
+
PURPOSE_TOO_BRIEF: `Purpose section is too brief (less than ${MIN_PURPOSE_LENGTH} characters)`,
|
|
26
|
+
REQUIREMENT_TOO_LONG: `Requirement text is very long (>${MAX_REQUIREMENT_TEXT_LENGTH} characters). Consider breaking it down.`,
|
|
27
|
+
DELTA_DESCRIPTION_TOO_BRIEF: 'Delta description is too brief',
|
|
28
|
+
DELTA_MISSING_REQUIREMENTS: 'Delta should include requirements',
|
|
29
|
+
|
|
30
|
+
GUIDE_NO_DELTAS:
|
|
31
|
+
'No deltas found. Ensure your change has a specs/ directory with capability folders (e.g. specs/http-server/spec.md) containing .md files that use delta headers (## ADDED/MODIFIED/REMOVED/RENAMED Requirements) and that each requirement includes at least one "#### Scenario:" block. Tip: run "openspec change show <change-id> --json --deltas-only" to inspect parsed deltas.',
|
|
32
|
+
GUIDE_MISSING_SPEC_SECTIONS:
|
|
33
|
+
'Missing required sections. Expected headers: "## Purpose" and "## Requirements". Example:\n## Purpose\n[brief purpose]\n\n## Requirements\n### Requirement: Clear requirement statement\nUsers SHALL ...\n\n#### Scenario: Descriptive name\n- **WHEN** ...\n- **THEN** ...',
|
|
34
|
+
GUIDE_MISSING_CHANGE_SECTIONS:
|
|
35
|
+
'Missing required sections. Expected headers: "## Why" and "## What Changes". Ensure deltas are documented in specs/ using delta headers.',
|
|
36
|
+
GUIDE_SCENARIO_FORMAT:
|
|
37
|
+
'Scenarios must use level-4 headers. Convert bullet lists into:\n#### Scenario: Short name\n- **WHEN** ...\n- **THEN** ...\n- **AND** ...',
|
|
38
|
+
} as const;
|
|
39
|
+
|
|
40
|
+
export const VERIFICATION_DIMENSIONS = ['Completeness', 'Correctness', 'Coherence'] as const;
|
|
41
|
+
|
|
42
|
+
export const VERIFICATION_MESSAGES = {
|
|
43
|
+
COMPLETENESS_MISSING_TASK: 'Task in tasks.md has no corresponding code change in diff summary',
|
|
44
|
+
COMPLETENESS_MISSING_REQUIREMENT: 'SHALL/MUST requirement in spec has no matching implementation in diff summary: {requirement}',
|
|
45
|
+
CORRECTNESS_TEST_FAILURE: 'Test suite has failures',
|
|
46
|
+
CORRECTNESS_MISSING_SCENARIO: 'Spec scenario has no corresponding test assertion',
|
|
47
|
+
COHERENCE_NAMING_MISMATCH: 'Design decision naming does not match implementation naming',
|
|
48
|
+
COHERENCE_PATTERN_MISSING: 'Architecture pattern from design.md not found in implementation: {pattern}',
|
|
49
|
+
VERIFICATION_PLACEHOLDER_DETECTED: 'Diff summary contains placeholder markers (TODO, FIXME, HACK)',
|
|
50
|
+
CONFLICT_DETECTED: 'Requirement "{requirement}" is modified by multiple changes: {changes}',
|
|
51
|
+
} as const;
|
|
52
|
+
|
|
53
|
+
export const MIN_ABANDONMENT_REASON_LENGTH = 50;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-language tokenizer for team-flow keyword extraction.
|
|
3
|
+
* Supports English (stemming + stop words) and Chinese (CJK sliding window).
|
|
4
|
+
* Zero external dependencies.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// --- English stop words ---
|
|
8
|
+
const ENGLISH_STOP_WORDS = new Set([
|
|
9
|
+
'the', 'be', 'to', 'of', 'and', 'a', 'in', 'that', 'have', 'i',
|
|
10
|
+
'it', 'for', 'not', 'on', 'with', 'he', 'as', 'you', 'do', 'at',
|
|
11
|
+
'this', 'but', 'his', 'by', 'from', 'they', 'we', 'say', 'her', 'she',
|
|
12
|
+
'or', 'an', 'will', 'my', 'one', 'all', 'would', 'there', 'their',
|
|
13
|
+
'what', 'so', 'up', 'out', 'if', 'about', 'who', 'get', 'which', 'go',
|
|
14
|
+
'me', 'when', 'make', 'can', 'like', 'time', 'no', 'just', 'him',
|
|
15
|
+
'know', 'take', 'people', 'into', 'year', 'your', 'good', 'some',
|
|
16
|
+
'could', 'them', 'see', 'other', 'than', 'then', 'now', 'look',
|
|
17
|
+
'only', 'come', 'its', 'over', 'think', 'also', 'back', 'after',
|
|
18
|
+
'use', 'two', 'how', 'our', 'work', 'first', 'well', 'way', 'even',
|
|
19
|
+
'new', 'want', 'because', 'any', 'these', 'give', 'day', 'most', 'us',
|
|
20
|
+
'is', 'am', 'are', 'was', 'were', 'been', 'has', 'had', 'did', 'does',
|
|
21
|
+
'based', 'using', 'used',
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
// --- Chinese stop words ---
|
|
25
|
+
const CHINESE_STOP_WORDS = new Set([
|
|
26
|
+
'的', '了', '在', '是', '我', '有', '和', '就', '不', '人',
|
|
27
|
+
'都', '一', '一个', '上', '也', '很', '到', '说', '要', '去',
|
|
28
|
+
'你', '会', '着', '没有', '看', '好', '自己', '这',
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
// --- Chinese punctuation ---
|
|
32
|
+
const CHINESE_PUNCTUATION = /[,。!?;:、""''()【】《》]/;
|
|
33
|
+
|
|
34
|
+
// --- CJK Unicode ranges ---
|
|
35
|
+
const CJK_REGEX = /[一-鿿㐀-䶿]+/g;
|
|
36
|
+
const CJK_CHAR_REGEX = /[一-鿿㐀-䶿]/g;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Lightweight English stemmer — copy of the one in validator.ts.
|
|
40
|
+
* Strips only the most common suffixes (-ing, -er, -ed, -s, -tion)
|
|
41
|
+
* so that natural variations like "limiting" / "limiter" / "limit"
|
|
42
|
+
* collapse to the same stem.
|
|
43
|
+
*
|
|
44
|
+
* Intentionally conservative — false-negative matching is preferred
|
|
45
|
+
* over false-positive.
|
|
46
|
+
*/
|
|
47
|
+
function stem(word: string): string {
|
|
48
|
+
const w = word.toLowerCase();
|
|
49
|
+
if (w.length <= 3) return w;
|
|
50
|
+
// Longer suffixes first so "ting" strips before "ing", "tion" before "ion", etc.
|
|
51
|
+
const suffixes: Array<[string, number]> = [
|
|
52
|
+
['ation', 3], ['tion', 3], ['ness', 3], ['ment', 3],
|
|
53
|
+
['ings', 3], ['ally', 3],
|
|
54
|
+
['ing', 3], ['ier', 3], ['ied', 3], ['ies', 3],
|
|
55
|
+
['ted', 3], ['ned', 3], ['red', 3], ['sed', 3], ['led', 3],
|
|
56
|
+
['ped', 3], ['ded', 3], ['ved', 3], ['wed', 3], ['xed', 3],
|
|
57
|
+
['zed', 3], ['ced', 3], ['ged', 3], ['ked', 3],
|
|
58
|
+
['ers', 3], ['ors', 3],
|
|
59
|
+
['ary', 3], ['ory', 3], ['ity', 3], ['ism', 3], ['ist', 3],
|
|
60
|
+
['ent', 3], ['ant', 3], ['ous', 3], ['ive', 3], ['ful', 3],
|
|
61
|
+
['ly', 3], ['ed', 3], ['er', 3], ['es', 3],
|
|
62
|
+
['al', 3], ['en', 3], ['ty', 3], ['or', 3], ['ar', 3],
|
|
63
|
+
['ry', 3], ['ic', 3], ['id', 3],
|
|
64
|
+
];
|
|
65
|
+
for (const [suffix, minRoot] of suffixes) {
|
|
66
|
+
if (w.endsWith(suffix) && w.length - suffix.length >= minRoot) {
|
|
67
|
+
return w.slice(0, -suffix.length);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (w.endsWith('s') && w.length > 4) return w.slice(0, -1);
|
|
71
|
+
return w;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Tokenize English text: lowercase, split on non-alphanumeric,
|
|
76
|
+
* remove stop words, apply stemming, filter short tokens.
|
|
77
|
+
*/
|
|
78
|
+
function tokenizeEnglish(text: string): Set<string> {
|
|
79
|
+
const tokens = new Set<string>();
|
|
80
|
+
const words = text.toLowerCase().split(/[^a-z0-9]+/).filter(w => w.length > 0);
|
|
81
|
+
for (const word of words) {
|
|
82
|
+
if (ENGLISH_STOP_WORDS.has(word)) continue;
|
|
83
|
+
const stemmed = stem(word);
|
|
84
|
+
if (stemmed.length < 3) continue;
|
|
85
|
+
tokens.add(stemmed);
|
|
86
|
+
}
|
|
87
|
+
return tokens;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Tokenize Chinese text: extract CJK sequences, produce sliding-window
|
|
92
|
+
* tokens (2-char through 5-char), also extract ASCII words with English stemming.
|
|
93
|
+
* Remove Chinese stop words.
|
|
94
|
+
*/
|
|
95
|
+
function tokenizeChinese(text: string): Set<string> {
|
|
96
|
+
const tokens = new Set<string>();
|
|
97
|
+
|
|
98
|
+
// Split on Chinese punctuation first
|
|
99
|
+
const segments = text.split(CHINESE_PUNCTUATION);
|
|
100
|
+
|
|
101
|
+
for (const segment of segments) {
|
|
102
|
+
// Extract CJK character sequences
|
|
103
|
+
const cjkRuns = segment.match(CJK_REGEX);
|
|
104
|
+
if (cjkRuns) {
|
|
105
|
+
for (const run of cjkRuns) {
|
|
106
|
+
if (run.length < 2) continue;
|
|
107
|
+
|
|
108
|
+
// Add the full run as a token (if not a stop word)
|
|
109
|
+
if (!CHINESE_STOP_WORDS.has(run)) {
|
|
110
|
+
tokens.add(run);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Sliding windows from length 2 up to min(run.length, 5)
|
|
114
|
+
const maxWindow = Math.min(run.length, 5);
|
|
115
|
+
for (let windowSize = 2; windowSize <= maxWindow; windowSize++) {
|
|
116
|
+
for (let i = 0; i <= run.length - windowSize; i++) {
|
|
117
|
+
const ngram = run.slice(i, i + windowSize);
|
|
118
|
+
if (!CHINESE_STOP_WORDS.has(ngram)) {
|
|
119
|
+
tokens.add(ngram);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Extract ASCII words from mixed content
|
|
127
|
+
const asciiWords = segment.match(/[a-zA-Z0-9]+/g);
|
|
128
|
+
if (asciiWords) {
|
|
129
|
+
for (const word of asciiWords) {
|
|
130
|
+
const lower = word.toLowerCase();
|
|
131
|
+
if (ENGLISH_STOP_WORDS.has(lower)) continue;
|
|
132
|
+
const stemmed = stem(lower);
|
|
133
|
+
if (stemmed.length < 3) continue;
|
|
134
|
+
tokens.add(stemmed);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return tokens;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Auto-detect the language of the given text.
|
|
144
|
+
* Returns 'en', 'zh', or 'mixed'.
|
|
145
|
+
*/
|
|
146
|
+
export function detectLanguage(text: string): 'en' | 'zh' | 'mixed' {
|
|
147
|
+
const cjkMatches = text.match(CJK_CHAR_REGEX);
|
|
148
|
+
const cjkCount = cjkMatches ? cjkMatches.length : 0;
|
|
149
|
+
const totalChars = text.replace(/\s/g, '').length;
|
|
150
|
+
const cjkRatio = totalChars > 0 ? cjkCount / totalChars : 0;
|
|
151
|
+
|
|
152
|
+
const hasAsciiWords = /[a-zA-Z]{2,}/.test(text);
|
|
153
|
+
|
|
154
|
+
if (cjkRatio > 0.3 && hasAsciiWords) return 'mixed';
|
|
155
|
+
if (cjkRatio > 0.3) return 'zh';
|
|
156
|
+
return 'en';
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Main tokenizer entry point.
|
|
161
|
+
* @param text - The text to tokenize.
|
|
162
|
+
* @param language - 'auto' | 'en' | 'zh'. Defaults to 'auto'.
|
|
163
|
+
* @returns Set of token strings.
|
|
164
|
+
*/
|
|
165
|
+
export function tokenize(text: string, language?: 'auto' | 'en' | 'zh'): Set<string> {
|
|
166
|
+
const lang = language ?? 'auto';
|
|
167
|
+
|
|
168
|
+
if (lang === 'en') return tokenizeEnglish(text);
|
|
169
|
+
if (lang === 'zh') return tokenizeChinese(text);
|
|
170
|
+
|
|
171
|
+
// auto-detect
|
|
172
|
+
const detected = detectLanguage(text);
|
|
173
|
+
if (detected === 'en') return tokenizeEnglish(text);
|
|
174
|
+
if (detected === 'zh') return tokenizeChinese(text);
|
|
175
|
+
|
|
176
|
+
// mixed: union of both
|
|
177
|
+
const enTokens = tokenizeEnglish(text);
|
|
178
|
+
const zhTokens = tokenizeChinese(text);
|
|
179
|
+
return new Set([...enTokens, ...zhTokens]);
|
|
180
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export type ValidationLevel = 'ERROR' | 'WARNING' | 'INFO';
|
|
2
|
+
|
|
3
|
+
export interface ValidationIssue {
|
|
4
|
+
level: ValidationLevel;
|
|
5
|
+
path: string;
|
|
6
|
+
message: string;
|
|
7
|
+
line?: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ValidationReport {
|
|
11
|
+
valid: boolean;
|
|
12
|
+
issues: ValidationIssue[];
|
|
13
|
+
summary: {
|
|
14
|
+
errors: number;
|
|
15
|
+
warnings: number;
|
|
16
|
+
info: number;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type VerificationDimension = 'Completeness' | 'Correctness' | 'Coherence';
|
|
21
|
+
export type VerificationStatus = 'PASS' | 'FAIL' | 'WARN';
|
|
22
|
+
|
|
23
|
+
export interface VerificationFinding {
|
|
24
|
+
level: 'CRITICAL' | 'IMPORTANT' | 'INFO';
|
|
25
|
+
dimension: VerificationDimension;
|
|
26
|
+
message: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface VerificationReport {
|
|
30
|
+
dimensions: {
|
|
31
|
+
name: VerificationDimension;
|
|
32
|
+
status: VerificationStatus;
|
|
33
|
+
findings: VerificationFinding[];
|
|
34
|
+
}[];
|
|
35
|
+
verdict: 'PASS' | 'CONDITIONAL' | 'FAIL';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface SyncConflict {
|
|
39
|
+
requirement: string;
|
|
40
|
+
spec: string;
|
|
41
|
+
changes: string[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface ConflictReport {
|
|
45
|
+
hasConflicts: boolean;
|
|
46
|
+
conflicts: SyncConflict[];
|
|
47
|
+
}
|