@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,231 @@
|
|
|
1
|
+
// scripts/lib/cmd-state.mjs — tf state subcommand handler
|
|
2
|
+
import { parseArgs } from 'node:util';
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
4
|
+
import { existsSync, mkdirSync } from 'node:fs';
|
|
5
|
+
import { dirname, join } from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { readState, writeState, updateField, rebuildState } from './state-loader.mjs';
|
|
8
|
+
import { computeArtifactsHash, computeContractHash } from './hash.mjs';
|
|
9
|
+
|
|
10
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
|
|
12
|
+
const VALID_STATES = [
|
|
13
|
+
'exploring', 'specifying', 'bridging', 'approved-for-build',
|
|
14
|
+
'executing', 'debugging', 'closing', 'abandoned',
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
const SETTABLE_FIELDS = [
|
|
18
|
+
'workflow', 'test_result', 'batches_completed', 'spec_merged',
|
|
19
|
+
'dp_0_decisions', 'dp_0_confirmed', 'dp_0_timestamp', 'dp_0_result',
|
|
20
|
+
'dp_1_result', 'dp_1_timestamp', 'dp_1_decisions', 'dp_1_confirmed',
|
|
21
|
+
'dp_2_result', 'dp_2_timestamp', 'dp_2_decisions', 'dp_2_confirmed',
|
|
22
|
+
'dp_3_result', 'dp_3_timestamp', 'dp_3_decisions', 'dp_3_confirmed',
|
|
23
|
+
'dp_5_result', 'dp_5_timestamp', 'dp_5_decisions', 'dp_5_confirmed',
|
|
24
|
+
'dp_6_result', 'dp_6_timestamp', 'dp_6_decisions', 'dp_6_confirmed',
|
|
25
|
+
'dp_7_result', 'dp_7_timestamp', 'dp_7_decisions', 'dp_7_confirmed',
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
export async function run(args) {
|
|
29
|
+
const { positionals, values } = parseArgs({
|
|
30
|
+
args,
|
|
31
|
+
options: {
|
|
32
|
+
json: { type: 'boolean', default: false },
|
|
33
|
+
},
|
|
34
|
+
allowPositionals: true,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const sub = positionals[0]; // init | check | transition | get | rebuild | set
|
|
38
|
+
const changeDir = positionals[1];
|
|
39
|
+
const arg = positionals[2]; // <to-state> for transition, <field> for get
|
|
40
|
+
|
|
41
|
+
if (!changeDir) {
|
|
42
|
+
console.error('Usage: tf state <subcommand> <change-dir> [arg]');
|
|
43
|
+
console.error('Subcommands: init, check, transition, get, rebuild, set');
|
|
44
|
+
process.exit(2);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Unknown subcommand: report a usage error (exit 2) BEFORE the BUG-B
|
|
48
|
+
// state-file existence check, so a bad subcommand is not masked by the
|
|
49
|
+
// "No state file" error (which would return exit 1 instead of exit 2).
|
|
50
|
+
const KNOWN_SUBS = ['init', 'check', 'transition', 'get', 'rebuild', 'set'];
|
|
51
|
+
if (!KNOWN_SUBS.includes(sub)) {
|
|
52
|
+
console.error(`Unknown subcommand: ${sub}. Valid: ${KNOWN_SUBS.join(', ')}`);
|
|
53
|
+
process.exit(2);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// BUG-B: every subcommand except `init` requires an existing state file.
|
|
57
|
+
// Without this check, readState silently returns defaults and `set` would
|
|
58
|
+
// create a phantom .team-flow.yaml in the wrong directory (state drift).
|
|
59
|
+
if (sub !== 'init') {
|
|
60
|
+
const STATE_FILE = '.team-flow.yaml';
|
|
61
|
+
if (!existsSync(join(changeDir, STATE_FILE))) {
|
|
62
|
+
console.error(`No state file at ${join(changeDir, STATE_FILE)}. Run 'tf state init <change-dir>' first.`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
switch (sub) {
|
|
68
|
+
case 'init': {
|
|
69
|
+
if (!existsSync(changeDir)) {
|
|
70
|
+
mkdirSync(changeDir, { recursive: true });
|
|
71
|
+
}
|
|
72
|
+
const hash = computeArtifactsHash(changeDir);
|
|
73
|
+
const ch = computeContractHash(changeDir);
|
|
74
|
+
const state = readState(changeDir);
|
|
75
|
+
state.artifacts_hash = hash;
|
|
76
|
+
state.contract_hash = ch;
|
|
77
|
+
state.last_transition = new Date().toISOString();
|
|
78
|
+
writeState(changeDir, state);
|
|
79
|
+
if (values.json) {
|
|
80
|
+
console.log(JSON.stringify({ ok: true, artifacts_hash: hash, contract_hash: ch }));
|
|
81
|
+
} else {
|
|
82
|
+
console.log(`State initialized. artifacts_hash: ${hash}`);
|
|
83
|
+
}
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
case 'check': {
|
|
87
|
+
const state = readState(changeDir);
|
|
88
|
+
const currentHash = computeArtifactsHash(changeDir);
|
|
89
|
+
const consistent = state.artifacts_hash === currentHash;
|
|
90
|
+
if (values.json) {
|
|
91
|
+
console.log(JSON.stringify({
|
|
92
|
+
consistent,
|
|
93
|
+
stored_hash: state.artifacts_hash,
|
|
94
|
+
current_hash: currentHash,
|
|
95
|
+
state: state.state,
|
|
96
|
+
}));
|
|
97
|
+
} else {
|
|
98
|
+
if (consistent) {
|
|
99
|
+
console.log('State consistent with artifacts.');
|
|
100
|
+
} else {
|
|
101
|
+
console.log('State INCONSISTENT — artifacts have changed since last transition.');
|
|
102
|
+
}
|
|
103
|
+
console.log(` State: ${state.state}, stored hash: ${state.artifacts_hash}`);
|
|
104
|
+
console.log(` Current hash: ${currentHash}`);
|
|
105
|
+
}
|
|
106
|
+
process.exit(consistent ? 0 : 1);
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
case 'transition': {
|
|
110
|
+
const toState = arg;
|
|
111
|
+
if (!toState) {
|
|
112
|
+
console.error('Usage: tf state transition <change-dir> <to-state>');
|
|
113
|
+
process.exit(2);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Validate state name
|
|
117
|
+
if (!VALID_STATES.includes(toState)) {
|
|
118
|
+
console.error(`Invalid state: '${toState}'. Must be one of: ${VALID_STATES.join(', ')}`);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const state = readState(changeDir);
|
|
123
|
+
const fromState = state.state;
|
|
124
|
+
|
|
125
|
+
// Run guard before allowing transition (H-2: enforce guard)
|
|
126
|
+
const guardScript = join(__dirname, '..', 'guard', 'guard.mjs');
|
|
127
|
+
const rawWorkflow = state.workflow || 'full';
|
|
128
|
+
// Normalize: guard only accepts full/hotfix/tweak, not "auto"
|
|
129
|
+
const workflow = rawWorkflow === 'auto' ? 'full' : rawWorkflow;
|
|
130
|
+
const guardResult = spawnSync('node', [guardScript, 'check', changeDir, fromState, toState, '--json', '--workflow', workflow], {
|
|
131
|
+
cwd: join(__dirname, '..', '..'),
|
|
132
|
+
timeout: 10_000,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const guardOutput = guardResult.stdout?.toString() ?? '';
|
|
136
|
+
const guardStderr = guardResult.stderr?.toString().trim() ?? '';
|
|
137
|
+
if (guardResult.error) {
|
|
138
|
+
console.error(`Guard check failed for ${fromState} -> ${toState}:`);
|
|
139
|
+
console.error(` [guard-error] ${guardResult.error.message}`);
|
|
140
|
+
if (guardStderr) console.error(` ${guardStderr}`);
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
let parsed;
|
|
145
|
+
try {
|
|
146
|
+
parsed = JSON.parse(guardOutput);
|
|
147
|
+
} catch {
|
|
148
|
+
console.error(`Guard check failed for ${fromState} -> ${toState}:`);
|
|
149
|
+
console.error(' [guard-error] Guard did not return valid JSON.');
|
|
150
|
+
if (guardStderr) console.error(` ${guardStderr}`);
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (guardResult.status !== 0 || parsed.pass !== true) {
|
|
155
|
+
const failures = (parsed.checks || [])
|
|
156
|
+
.filter(c => !c.pass)
|
|
157
|
+
.flatMap(c => (c.failures || []).map(f => `[${c.dimension}] ${f}`));
|
|
158
|
+
console.error(`Guard check failed for ${fromState} -> ${toState}:`);
|
|
159
|
+
for (const f of failures) console.error(` ${f}`);
|
|
160
|
+
if (parsed.error) console.error(` ${parsed.error}`);
|
|
161
|
+
if (failures.length === 0 && !parsed.error) {
|
|
162
|
+
console.error(' [guard-error] Guard failed without a structured failure message.');
|
|
163
|
+
}
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
state.state = toState;
|
|
168
|
+
state.last_transition_from = fromState;
|
|
169
|
+
state.last_transition_to = toState;
|
|
170
|
+
state.last_transition = new Date().toISOString();
|
|
171
|
+
writeState(changeDir, state);
|
|
172
|
+
if (values.json) {
|
|
173
|
+
console.log(JSON.stringify({ ok: true, from: fromState, to: toState }));
|
|
174
|
+
} else {
|
|
175
|
+
console.log(`State transitioned: ${fromState} -> ${toState}`);
|
|
176
|
+
}
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
case 'get': {
|
|
180
|
+
const field = arg;
|
|
181
|
+
if (!field) {
|
|
182
|
+
console.error('Usage: tf state get <change-dir> <field>');
|
|
183
|
+
process.exit(2);
|
|
184
|
+
}
|
|
185
|
+
const state = readState(changeDir);
|
|
186
|
+
if (!Object.prototype.hasOwnProperty.call(state, field) && field in state) {
|
|
187
|
+
console.error(`Field '${field}' is not a valid state field`);
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
const value = state[field];
|
|
191
|
+
if (values.json) {
|
|
192
|
+
console.log(JSON.stringify({ field, value: value ?? null }));
|
|
193
|
+
} else {
|
|
194
|
+
console.log(value ?? 'null');
|
|
195
|
+
}
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
case 'rebuild': {
|
|
199
|
+
const state = rebuildState(changeDir, { computeArtifactsHash, computeContractHash });
|
|
200
|
+
if (values.json) {
|
|
201
|
+
console.log(JSON.stringify({ ok: true, state: state.state }));
|
|
202
|
+
} else {
|
|
203
|
+
console.log(`State rebuilt from artifacts. state: ${state.state}`);
|
|
204
|
+
}
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
case 'set': {
|
|
208
|
+
// tf state set <change-dir> <field> <value>
|
|
209
|
+
const field = arg;
|
|
210
|
+
const value = positionals[3];
|
|
211
|
+
if (!field || value === undefined) {
|
|
212
|
+
console.error('Usage: tf state set <change-dir> <field> <value>');
|
|
213
|
+
process.exit(2);
|
|
214
|
+
}
|
|
215
|
+
if (!SETTABLE_FIELDS.includes(field)) {
|
|
216
|
+
console.error(`⛔ Field '${field}' is not settable (use 'transition' for state, or check SETTABLE_FIELDS)`);
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
219
|
+
updateField(changeDir, field, value);
|
|
220
|
+
if (values.json) {
|
|
221
|
+
console.log(JSON.stringify({ ok: true, field, value }));
|
|
222
|
+
} else {
|
|
223
|
+
console.log(`✅ Set ${field} = ${value}`);
|
|
224
|
+
}
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
default:
|
|
228
|
+
console.error(`Unknown subcommand: ${sub}. Valid: init, check, transition, get, rebuild, set`);
|
|
229
|
+
process.exit(2);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// tf sync <change-dir> — merge delta specs into main specs with conflict detection
|
|
2
|
+
import { readFileSync, readdirSync, writeFileSync, existsSync, statSync, mkdirSync } from 'node:fs';
|
|
3
|
+
import path, { join, basename } from 'node:path';
|
|
4
|
+
import { validateSpecPathLayout } from './spec-paths.mjs';
|
|
5
|
+
|
|
6
|
+
function toPosix(value) {
|
|
7
|
+
return value.replace(/\\/g, '/');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function pathApiFor(...values) {
|
|
11
|
+
return values.some(value => value.includes('\\')) ? path.win32 : path.posix;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function deriveCapabilityDir(changeSpecsDir, specFile) {
|
|
15
|
+
const api = pathApiFor(changeSpecsDir, specFile);
|
|
16
|
+
const relative = toPosix(api.relative(changeSpecsDir, specFile));
|
|
17
|
+
return relative.replace(/\/spec\.md$/, '');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function run(args) {
|
|
21
|
+
if (args.length < 1) {
|
|
22
|
+
console.error('Usage: tf sync <change-dir>');
|
|
23
|
+
process.exit(2);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const changeDir = args[0];
|
|
27
|
+
if (!existsSync(changeDir)) {
|
|
28
|
+
console.error(`Error: "${changeDir}" not found`);
|
|
29
|
+
process.exit(2);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const { Validator } = await import('../../dist/index.js');
|
|
33
|
+
const validator = new Validator();
|
|
34
|
+
|
|
35
|
+
// Collect all unsynced changes for conflict detection
|
|
36
|
+
const changesDir = join(process.cwd(), 'changes');
|
|
37
|
+
const allDeltas = [];
|
|
38
|
+
|
|
39
|
+
if (existsSync(changesDir)) {
|
|
40
|
+
for (const dir of readdirSync(changesDir)) {
|
|
41
|
+
const dirPath = join(changesDir, dir);
|
|
42
|
+
if (!statSync(dirPath).isDirectory()) continue;
|
|
43
|
+
const layout = validateSpecPathLayout(dirPath, { requireSpecs: false });
|
|
44
|
+
if (!layout.pass) {
|
|
45
|
+
for (const failure of layout.failures) console.error(failure);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
if (layout.specFiles.length === 0) continue;
|
|
49
|
+
|
|
50
|
+
for (const specFile of layout.specFiles) {
|
|
51
|
+
const content = readFileSync(specFile, 'utf-8');
|
|
52
|
+
allDeltas.push({ changeName: dir, content });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Check for conflicts
|
|
58
|
+
if (allDeltas.length > 0) {
|
|
59
|
+
const conflictReport = validator.detectSyncConflicts(allDeltas);
|
|
60
|
+
if (conflictReport.hasConflicts) {
|
|
61
|
+
console.log('⚠️ Sync conflicts detected:\n');
|
|
62
|
+
for (const conflict of conflictReport.conflicts) {
|
|
63
|
+
console.log(` Requirement: "${conflict.requirement}"`);
|
|
64
|
+
console.log(` Modified by: ${conflict.changes.join(', ')}\n`);
|
|
65
|
+
}
|
|
66
|
+
console.log('Resolve conflicts before syncing. Consider syncing changes one at a time.');
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Perform sync: copy delta specs to main specs/
|
|
72
|
+
const changeSpecsDir = join(changeDir, 'specs');
|
|
73
|
+
const mainSpecsDir = join(process.cwd(), 'specs');
|
|
74
|
+
const changeName = basename(changeDir);
|
|
75
|
+
const layout = validateSpecPathLayout(changeDir, { requireSpecs: true });
|
|
76
|
+
if (!layout.pass) {
|
|
77
|
+
for (const failure of layout.failures) console.error(failure);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!existsSync(mainSpecsDir)) {
|
|
82
|
+
mkdirSync(mainSpecsDir, { recursive: true });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let synced = 0;
|
|
86
|
+
|
|
87
|
+
for (const specFile of layout.specFiles) {
|
|
88
|
+
const capabilityDir = deriveCapabilityDir(changeSpecsDir, specFile);
|
|
89
|
+
const targetDir = join(mainSpecsDir, capabilityDir);
|
|
90
|
+
|
|
91
|
+
if (!existsSync(targetDir)) {
|
|
92
|
+
mkdirSync(targetDir, { recursive: true });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const content = readFileSync(specFile, 'utf-8');
|
|
96
|
+
writeFileSync(join(targetDir, 'spec.md'), content);
|
|
97
|
+
console.log(` 📋 Synced: specs/${capabilityDir}/spec.md`);
|
|
98
|
+
synced++;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
console.log(`\n✅ Synced ${synced} spec(s) from ${changeName} to specs/`);
|
|
102
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// tf validate <dir> — validate artifacts in a change directory
|
|
2
|
+
import { readFileSync, existsSync, statSync } from 'node:fs';
|
|
3
|
+
import { join, basename } from 'node:path';
|
|
4
|
+
import { loadConfig } from './config-loader.mjs';
|
|
5
|
+
import { validateSpecPathLayout, relativeSpecPath } from './spec-paths.mjs';
|
|
6
|
+
|
|
7
|
+
async function getValidator() {
|
|
8
|
+
const mod = await import('../../dist/index.js');
|
|
9
|
+
return new mod.Validator(false);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function printReport(label, report) {
|
|
13
|
+
console.log(`\n 📋 ${label}`);
|
|
14
|
+
if (report.valid) {
|
|
15
|
+
console.log(` ✅ valid (${report.summary.errors} errors, ${report.summary.warnings} warnings, ${report.summary.info} info)`);
|
|
16
|
+
} else {
|
|
17
|
+
console.log(` ❌ invalid (${report.summary.errors} errors, ${report.summary.warnings} warnings, ${report.summary.info} info)`);
|
|
18
|
+
}
|
|
19
|
+
for (const issue of report.issues) {
|
|
20
|
+
const icon = issue.level === 'ERROR' ? '🔴' : issue.level === 'WARNING' ? '🟡' : '🔵';
|
|
21
|
+
console.log(` ${icon} [${issue.level}] ${issue.path}: ${issue.message}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function run(args) {
|
|
26
|
+
if (args.length < 1) {
|
|
27
|
+
console.error('Usage: tf validate <change-dir>');
|
|
28
|
+
process.exit(2);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const changeDir = args[0];
|
|
32
|
+
if (!existsSync(changeDir) || !statSync(changeDir).isDirectory()) {
|
|
33
|
+
console.error(`Error: "${changeDir}" is not a valid directory`);
|
|
34
|
+
process.exit(2);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const config = loadConfig(process.cwd());
|
|
38
|
+
const changeName = basename(changeDir);
|
|
39
|
+
const validator = await getValidator();
|
|
40
|
+
|
|
41
|
+
console.log(`🔍 Validating: ${changeDir}`);
|
|
42
|
+
console.log(` Change: ${changeName}`);
|
|
43
|
+
|
|
44
|
+
let hasErrors = false;
|
|
45
|
+
|
|
46
|
+
// Validate proposal.md
|
|
47
|
+
const proposalPath = join(changeDir, 'proposal.md');
|
|
48
|
+
if (existsSync(proposalPath)) {
|
|
49
|
+
const content = readFileSync(proposalPath, 'utf-8');
|
|
50
|
+
const report = validator.validateChangeContent(changeName, content);
|
|
51
|
+
printReport('proposal.md', report);
|
|
52
|
+
if (!report.valid) hasErrors = true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Validate specs/*/spec.md
|
|
56
|
+
const specLayout = validateSpecPathLayout(changeDir, { requireSpecs: true });
|
|
57
|
+
for (const failure of specLayout.failures) {
|
|
58
|
+
printReport('specs/', {
|
|
59
|
+
valid: false,
|
|
60
|
+
issues: [{ level: 'ERROR', path: 'specs/', message: failure }],
|
|
61
|
+
summary: { errors: 1, warnings: 0, info: 0 },
|
|
62
|
+
});
|
|
63
|
+
hasErrors = true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for (const specFile of specLayout.specFiles) {
|
|
67
|
+
const content = readFileSync(specFile, 'utf-8');
|
|
68
|
+
const report = validator.validateDeltaSpec(content);
|
|
69
|
+
const rel = relativeSpecPath(changeDir, specFile);
|
|
70
|
+
printReport(rel, report);
|
|
71
|
+
if (!report.valid) hasErrors = true;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Basic structural validation for design.md and tasks.md (shared pattern)
|
|
75
|
+
const STRUCTURAL_CHECKS = [
|
|
76
|
+
{ file: 'design.md', errorMsg: 'design.md is too short (< 50 chars) — provide architecture decisions, trade-offs, and data flow', warningMsg: 'design.md has no section headings — consider adding ## Architecture, ## Data Flow, ## Error Handling' },
|
|
77
|
+
{ file: 'tasks.md', errorMsg: 'tasks.md is too short (< 50 chars) — provide actionable, ordered implementation tasks', warningMsg: 'tasks.md has no section headings — consider adding ## File Structure and ## Tasks' },
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
for (const { file, errorMsg, warningMsg } of STRUCTURAL_CHECKS) {
|
|
81
|
+
const filePath = join(changeDir, file);
|
|
82
|
+
if (!existsSync(filePath)) continue;
|
|
83
|
+
|
|
84
|
+
const content = readFileSync(filePath, 'utf-8').trim();
|
|
85
|
+
const issues = [];
|
|
86
|
+
if (content.length < 50) issues.push({ level: 'ERROR', path: file, message: errorMsg });
|
|
87
|
+
if (!content.includes('##')) issues.push({ level: 'WARNING', path: file, message: warningMsg });
|
|
88
|
+
const report = {
|
|
89
|
+
valid: issues.filter(i => i.level === 'ERROR').length === 0,
|
|
90
|
+
issues,
|
|
91
|
+
summary: { errors: issues.filter(i => i.level === 'ERROR').length, warnings: issues.filter(i => i.level === 'WARNING').length, info: 0 },
|
|
92
|
+
};
|
|
93
|
+
printReport(file, report);
|
|
94
|
+
if (!report.valid) hasErrors = true;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
console.log('');
|
|
98
|
+
if (hasErrors) {
|
|
99
|
+
console.log('❌ Validation failed with errors.');
|
|
100
|
+
process.exit(1);
|
|
101
|
+
} else {
|
|
102
|
+
console.log('✅ All artifacts validated.');
|
|
103
|
+
process.exit(0);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// tf version <semver> — sync version to all manifest and documentation files
|
|
2
|
+
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
|
|
5
|
+
// ── JSON manifests with structured path ──
|
|
6
|
+
const MANIFESTS = [
|
|
7
|
+
{ file: 'package.json', path: ['version'] },
|
|
8
|
+
{ file: 'plugin.json', path: ['version'] },
|
|
9
|
+
{ file: '.claude-plugin/plugin.json', path: ['version'] },
|
|
10
|
+
{ file: '.claude-plugin/marketplace.json', path: ['plugins', '0', 'version'] },
|
|
11
|
+
{ file: '.cursor-plugin/plugin.json', path: ['version'] },
|
|
12
|
+
{ file: '.cursor-plugin/marketplace.json', path: ['metadata', 'version'] },
|
|
13
|
+
{ file: '.codex-plugin/plugin.json', path: ['version'] },
|
|
14
|
+
{ file: 'gemini-extension.json', path: ['version'] },
|
|
15
|
+
{ file: '.github/plugin/marketplace.json', path: ['metadata', 'version'] },
|
|
16
|
+
{ file: '.github/plugin/marketplace.json', path: ['plugins', '0', 'version'] },
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
// ── Text files with regex patterns (first capture group = version to replace) ──
|
|
20
|
+
// Note: CLAUDE.md is intentionally gitignored (project-local AI instructions).
|
|
21
|
+
const TEXT_FILES = [
|
|
22
|
+
{ file: 'README.md', pattern: /(当前版本:`v?)0\.\d+\.\d+(`?)/g, replacement: '$10.%MINOR%.%PATCH%$2' },
|
|
23
|
+
{ file: 'INSTALL.md', pattern: /(当前发布版本:\*\*v)0\.\d+\.\d+(\*\*)/g, replacement: '$10.%MINOR%.%PATCH%$2' },
|
|
24
|
+
{ file: 'docs/README_en.md', pattern: /(Current: `v)0\.\d+\.\d+(`)/g, replacement: '$10.%MINOR%.%PATCH%$2' },
|
|
25
|
+
{ file: 'hooks/session-start', pattern: /(# v)0\.\d+\.\d+(: conditional injection)/g, replacement: '$10.%MINOR%.%PATCH%$2' },
|
|
26
|
+
{ file: 'llms.txt', pattern: /(Current version: v)0\.\d+\.\d+(\.)/g, replacement: '$10.%MINOR%.%PATCH%$2' },
|
|
27
|
+
{ file: '.claude/always/phase-guard.md', pattern: /(# team-flow v)0\.\d+\.\d+( \|)/g, replacement: '$10.%MINOR%.%PATCH%$2' },
|
|
28
|
+
{ file: 'GEMINI.md', pattern: /(# team-flow v)0\.\d+\.\d+( \|)/g, replacement: '$10.%MINOR%.%PATCH%$2' },
|
|
29
|
+
{ file: 'skills/workflow-start/SKILL.md', pattern: /(npx --yes --package @xulthekl\/team-flow@)0\.\d+\.\d+( tf)/g, replacement: '$10.%MINOR%.%PATCH%$2' },
|
|
30
|
+
{ file: 'skills/need-explorer/SKILL.md', pattern: /(npx --yes --package @xulthekl\/team-flow@)0\.\d+\.\d+( tf)/g, replacement: '$10.%MINOR%.%PATCH%$2' },
|
|
31
|
+
{ file: 'skills/spec-writer/SKILL.md', pattern: /(npx --yes --package @xulthekl\/team-flow@)0\.\d+\.\d+( tf)/g, replacement: '$10.%MINOR%.%PATCH%$2' },
|
|
32
|
+
{ file: 'skills/contract-builder/SKILL.md', pattern: /(npx --yes --package @xulthekl\/team-flow@)0\.\d+\.\d+( tf)/g, replacement: '$10.%MINOR%.%PATCH%$2' },
|
|
33
|
+
{ file: 'skills/build-executor/SKILL.md', pattern: /(npx --yes --package @xulthekl\/team-flow@)0\.\d+\.\d+( tf)/g, replacement: '$10.%MINOR%.%PATCH%$2' },
|
|
34
|
+
{ file: 'skills/build-executor/implementer-prompt.md', pattern: /(npx --yes --package @xulthekl\/team-flow@)0\.\d+\.\d+( tf)/g, replacement: '$10.%MINOR%.%PATCH%$2' },
|
|
35
|
+
{ file: 'skills/build-executor/task-reviewer-prompt.md', pattern: /(npx --yes --package @xulthekl\/team-flow@)0\.\d+\.\d+( tf)/g, replacement: '$10.%MINOR%.%PATCH%$2' },
|
|
36
|
+
{ file: 'skills/code-reviewer/SKILL.md', pattern: /(npx --yes --package @xulthekl\/team-flow@)0\.\d+\.\d+( tf)/g, replacement: '$10.%MINOR%.%PATCH%$2' },
|
|
37
|
+
{ file: 'skills/code-reviewer/code-reviewer-prompt.md', pattern: /(npx --yes --package @xulthekl\/team-flow@)0\.\d+\.\d+( tf)/g, replacement: '$10.%MINOR%.%PATCH%$2' },
|
|
38
|
+
{ file: 'skills/bug-investigator/SKILL.md', pattern: /(npx --yes --package @xulthekl\/team-flow@)0\.\d+\.\d+( tf)/g, replacement: '$10.%MINOR%.%PATCH%$2' },
|
|
39
|
+
{ file: 'skills/release-archivist/SKILL.md', pattern: /(npx --yes --package @xulthekl\/team-flow@)0\.\d+\.\d+( tf)/g, replacement: '$10.%MINOR%.%PATCH%$2' },
|
|
40
|
+
{ file: 'skills/spec-merger/SKILL.md', pattern: /(npx --yes --package @xulthekl\/team-flow@)0\.\d+\.\d+( tf)/g, replacement: '$10.%MINOR%.%PATCH%$2' },
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
function getNestedValue(obj, pathParts) {
|
|
44
|
+
let val = obj;
|
|
45
|
+
for (const p of pathParts) {
|
|
46
|
+
if (val === undefined || val === null) return undefined;
|
|
47
|
+
val = val[p];
|
|
48
|
+
}
|
|
49
|
+
return val;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function setNestedValue(obj, pathParts, value) {
|
|
53
|
+
let target = obj;
|
|
54
|
+
for (let i = 0; i < pathParts.length - 1; i++) {
|
|
55
|
+
target = target[pathParts[i]];
|
|
56
|
+
}
|
|
57
|
+
target[pathParts[pathParts.length - 1]] = value;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function run(args) {
|
|
61
|
+
const dryRun = args.includes('--dry-run');
|
|
62
|
+
const semverArgs = args.filter(a => a !== '--dry-run');
|
|
63
|
+
|
|
64
|
+
if (semverArgs.length < 1) {
|
|
65
|
+
console.error('Usage: tf version <semver> [--dry-run]');
|
|
66
|
+
process.exit(2);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const newVersion = semverArgs[0];
|
|
70
|
+
const match = newVersion.match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
71
|
+
if (!match) {
|
|
72
|
+
console.error(`Invalid semver: ${newVersion}`);
|
|
73
|
+
process.exit(2);
|
|
74
|
+
}
|
|
75
|
+
const [, , minor, patch] = match;
|
|
76
|
+
|
|
77
|
+
console.log(`Version sync → ${newVersion}${dryRun ? ' (dry run)' : ''}\n`);
|
|
78
|
+
let changed = 0;
|
|
79
|
+
let unchanged = 0;
|
|
80
|
+
let skipped = 0;
|
|
81
|
+
|
|
82
|
+
// ── Phase 1: JSON manifests ──
|
|
83
|
+
console.log('── JSON manifests ──');
|
|
84
|
+
const seenFiles = new Set();
|
|
85
|
+
for (const manifest of MANIFESTS) {
|
|
86
|
+
const key = `${manifest.file}:${manifest.path.join('.')}`;
|
|
87
|
+
if (seenFiles.has(key)) continue;
|
|
88
|
+
seenFiles.add(key);
|
|
89
|
+
|
|
90
|
+
const filePath = join(process.cwd(), manifest.file);
|
|
91
|
+
if (!existsSync(filePath)) {
|
|
92
|
+
console.log(` ⏭️ ${manifest.file} — not found, skipping`);
|
|
93
|
+
skipped++;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const content = JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
98
|
+
const currentVersion = getNestedValue(content, manifest.path);
|
|
99
|
+
|
|
100
|
+
if (currentVersion === newVersion) {
|
|
101
|
+
console.log(` ✅ ${manifest.file} [${manifest.path.join('.')}]: ${currentVersion} (unchanged)`);
|
|
102
|
+
unchanged++;
|
|
103
|
+
} else {
|
|
104
|
+
console.log(` 📝 ${manifest.file} [${manifest.path.join('.')}]: ${currentVersion || 'N/A'} → ${newVersion}`);
|
|
105
|
+
changed++;
|
|
106
|
+
if (!dryRun) {
|
|
107
|
+
setNestedValue(content, manifest.path, newVersion);
|
|
108
|
+
writeFileSync(filePath, JSON.stringify(content, null, 2) + '\n');
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ── Phase 2: Text files (regex) ──
|
|
114
|
+
console.log('\n── Documentation & scripts ──');
|
|
115
|
+
for (const entry of TEXT_FILES) {
|
|
116
|
+
const filePath = join(process.cwd(), entry.file);
|
|
117
|
+
if (!existsSync(filePath)) {
|
|
118
|
+
console.log(` ⏭️ ${entry.file} — not found, skipping`);
|
|
119
|
+
skipped++;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
124
|
+
const replacement = entry.replacement
|
|
125
|
+
.replace('%MINOR%', minor)
|
|
126
|
+
.replace('%PATCH%', patch);
|
|
127
|
+
|
|
128
|
+
const newContent = content.replace(entry.pattern, replacement);
|
|
129
|
+
if (newContent === content) {
|
|
130
|
+
console.log(` ✅ ${entry.file}: no version pattern matched (may already be correct)`);
|
|
131
|
+
unchanged++;
|
|
132
|
+
} else {
|
|
133
|
+
console.log(` 📝 ${entry.file}: version string updated`);
|
|
134
|
+
changed++;
|
|
135
|
+
if (!dryRun) {
|
|
136
|
+
writeFileSync(filePath, newContent);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
console.log(`\n${'─'.repeat(40)}`);
|
|
142
|
+
console.log(`Changed: ${changed} Unchanged: ${unchanged} Skipped: ${skipped}`);
|
|
143
|
+
if (dryRun) {
|
|
144
|
+
console.log('\nDry run complete. Run without --dry-run to apply changes.');
|
|
145
|
+
} else {
|
|
146
|
+
console.log('\n✅ Version synced across all files. Remember to commit and update CHANGELOG.md.');
|
|
147
|
+
}
|
|
148
|
+
}
|