@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,138 @@
|
|
|
1
|
+
// tests/lib/hash.test.mjs
|
|
2
|
+
// Tests for scripts/lib/hash.mjs
|
|
3
|
+
import { describe, it, before, after } from 'node:test';
|
|
4
|
+
import assert from 'node:assert/strict';
|
|
5
|
+
import { mkdtempSync, writeFileSync, mkdirSync, rmSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { tmpdir } from 'node:os';
|
|
8
|
+
import crypto from 'node:crypto';
|
|
9
|
+
|
|
10
|
+
let tempDir;
|
|
11
|
+
|
|
12
|
+
describe('hash: computeArtifactsHash()', () => {
|
|
13
|
+
let hashMod;
|
|
14
|
+
|
|
15
|
+
before(async () => {
|
|
16
|
+
tempDir = mkdtempSync(join(tmpdir(), 'tf-hash-test-'));
|
|
17
|
+
const modulePath = join(process.cwd(), 'scripts/lib/hash.mjs');
|
|
18
|
+
hashMod = await import(modulePath);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
after(() => {
|
|
22
|
+
if (tempDir) rmSync(tempDir, { recursive: true, force: true });
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('returns null when no artifacts exist', () => {
|
|
26
|
+
const hash = hashMod.computeArtifactsHash(tempDir);
|
|
27
|
+
assert.equal(hash, null);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('computes hash from proposal.md alone', () => {
|
|
31
|
+
writeFileSync(join(tempDir, 'proposal.md'), '## Why\nTest proposal content');
|
|
32
|
+
|
|
33
|
+
const hash = hashMod.computeArtifactsHash(tempDir);
|
|
34
|
+
assert.ok(hash.startsWith('sha256:'));
|
|
35
|
+
assert.equal(hash.length, 64 + 7); // 'sha256:' + 64 hex chars
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('is deterministic — same content produces same hash', () => {
|
|
39
|
+
const content = '## Why\nTest proposal for hash determinism';
|
|
40
|
+
writeFileSync(join(tempDir, 'proposal.md'), content);
|
|
41
|
+
|
|
42
|
+
const h1 = hashMod.computeArtifactsHash(tempDir);
|
|
43
|
+
const h2 = hashMod.computeArtifactsHash(tempDir);
|
|
44
|
+
assert.equal(h1, h2);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('different content produces different hash', () => {
|
|
48
|
+
writeFileSync(join(tempDir, 'proposal.md'), '## Why\nContent version A');
|
|
49
|
+
const h1 = hashMod.computeArtifactsHash(tempDir);
|
|
50
|
+
|
|
51
|
+
writeFileSync(join(tempDir, 'proposal.md'), '## Why\nContent version B');
|
|
52
|
+
const h2 = hashMod.computeArtifactsHash(tempDir);
|
|
53
|
+
|
|
54
|
+
assert.notEqual(h1, h2);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('includes all four artifacts in hash', () => {
|
|
58
|
+
writeFileSync(join(tempDir, 'proposal.md'), '## Why\nproposal content');
|
|
59
|
+
mkdirSync(join(tempDir, 'specs', 'ui-theme'), { recursive: true });
|
|
60
|
+
writeFileSync(join(tempDir, 'specs', 'ui-theme', 'spec.md'), '# Spec\nRequirement content');
|
|
61
|
+
writeFileSync(join(tempDir, 'design.md'), '# Design\nDesign content');
|
|
62
|
+
writeFileSync(join(tempDir, 'tasks.md'), '# Tasks\nTasks content');
|
|
63
|
+
|
|
64
|
+
const h1 = hashMod.computeArtifactsHash(tempDir);
|
|
65
|
+
|
|
66
|
+
// Change one artifact → hash should change
|
|
67
|
+
writeFileSync(join(tempDir, 'design.md'), '# Design\nModified design content');
|
|
68
|
+
const h2 = hashMod.computeArtifactsHash(tempDir);
|
|
69
|
+
|
|
70
|
+
assert.notEqual(h1, h2);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('reads canonical specs in sorted order for determinism', () => {
|
|
74
|
+
mkdirSync(join(tempDir, 'specs', 'z-auth'), { recursive: true });
|
|
75
|
+
mkdirSync(join(tempDir, 'specs', 'a-ui'), { recursive: true });
|
|
76
|
+
writeFileSync(join(tempDir, 'proposal.md'), 'proposal');
|
|
77
|
+
writeFileSync(join(tempDir, 'specs', 'z-auth', 'spec.md'), 'auth spec');
|
|
78
|
+
writeFileSync(join(tempDir, 'specs', 'a-ui', 'spec.md'), 'ui spec');
|
|
79
|
+
|
|
80
|
+
const h1 = hashMod.computeArtifactsHash(tempDir);
|
|
81
|
+
|
|
82
|
+
// Same files, same content → same hash
|
|
83
|
+
const h2 = hashMod.computeArtifactsHash(tempDir);
|
|
84
|
+
assert.equal(h1, h2);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('ignores non-.md files in specs directory', () => {
|
|
88
|
+
mkdirSync(join(tempDir, 'specs', 'ui-theme'), { recursive: true });
|
|
89
|
+
writeFileSync(join(tempDir, 'proposal.md'), 'proposal');
|
|
90
|
+
writeFileSync(join(tempDir, 'specs', 'ui-theme', 'spec.md'), 'spec content');
|
|
91
|
+
writeFileSync(join(tempDir, 'specs', 'notes.txt'), 'some notes'); // non-.md
|
|
92
|
+
|
|
93
|
+
const h1 = hashMod.computeArtifactsHash(tempDir);
|
|
94
|
+
|
|
95
|
+
// Change non-.md file → hash should NOT change
|
|
96
|
+
writeFileSync(join(tempDir, 'specs', 'notes.txt'), 'modified notes');
|
|
97
|
+
const h2 = hashMod.computeArtifactsHash(tempDir);
|
|
98
|
+
|
|
99
|
+
assert.equal(h1, h2);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('hash: computeContractHash()', () => {
|
|
104
|
+
let hashMod;
|
|
105
|
+
|
|
106
|
+
before(async () => {
|
|
107
|
+
tempDir = mkdtempSync(join(tmpdir(), 'tf-hash-contract-'));
|
|
108
|
+
const modulePath = join(process.cwd(), 'scripts/lib/hash.mjs');
|
|
109
|
+
hashMod = await import(modulePath);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
after(() => {
|
|
113
|
+
if (tempDir) rmSync(tempDir, { recursive: true, force: true });
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('returns null when execution-contract.md does not exist', () => {
|
|
117
|
+
const hash = hashMod.computeContractHash(tempDir);
|
|
118
|
+
assert.equal(hash, null);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('computes hash of execution-contract.md', () => {
|
|
122
|
+
writeFileSync(join(tempDir, 'execution-contract.md'), '## Intent Lock\nContract content');
|
|
123
|
+
|
|
124
|
+
const hash = hashMod.computeContractHash(tempDir);
|
|
125
|
+
assert.ok(hash.startsWith('sha256:'));
|
|
126
|
+
assert.equal(hash.length, 64 + 7);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('is independent of artifacts hash', () => {
|
|
130
|
+
writeFileSync(join(tempDir, 'execution-contract.md'), 'contract v1');
|
|
131
|
+
writeFileSync(join(tempDir, 'proposal.md'), 'proposal v1');
|
|
132
|
+
|
|
133
|
+
const contractHash = hashMod.computeContractHash(tempDir);
|
|
134
|
+
const artifactsHash = hashMod.computeArtifactsHash(tempDir);
|
|
135
|
+
|
|
136
|
+
assert.notEqual(contractHash, artifactsHash);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// tests/lib/infer-workflow.test.mjs
|
|
2
|
+
// Tests for scripts/infer-workflow.mjs — mode inference logic
|
|
3
|
+
import { describe, it, before, after } from 'node:test';
|
|
4
|
+
import assert from 'node:assert/strict';
|
|
5
|
+
import { mkdtempSync, writeFileSync, rmSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { tmpdir } from 'node:os';
|
|
8
|
+
|
|
9
|
+
let tempDir;
|
|
10
|
+
|
|
11
|
+
describe('infer-workflow: inferMode()', () => {
|
|
12
|
+
let inferMode;
|
|
13
|
+
|
|
14
|
+
before(async () => {
|
|
15
|
+
tempDir = mkdtempSync(join(tmpdir(), 'tf-infer-'));
|
|
16
|
+
const modulePath = join(process.cwd(), 'scripts/infer-workflow.mjs');
|
|
17
|
+
const mod = await import(modulePath);
|
|
18
|
+
inferMode = mod.inferMode;
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
after(() => {
|
|
22
|
+
if (tempDir) rmSync(tempDir, { recursive: true, force: true });
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('returns full mode for empty directory (no artifacts)', () => {
|
|
26
|
+
const result = inferMode(tempDir);
|
|
27
|
+
assert.equal(result.mode, 'full');
|
|
28
|
+
assert.equal(result.explicit, false);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('preserves explicit hotfix override', () => {
|
|
32
|
+
writeFileSync(join(tempDir, '.team-flow.yaml'), 'state: executing\nworkflow: hotfix');
|
|
33
|
+
const result = inferMode(tempDir);
|
|
34
|
+
assert.equal(result.mode, 'hotfix');
|
|
35
|
+
assert.equal(result.explicit, true);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('preserves explicit tweak override', () => {
|
|
39
|
+
writeFileSync(join(tempDir, '.team-flow.yaml'), 'state: executing\nworkflow: tweak');
|
|
40
|
+
const result = inferMode(tempDir);
|
|
41
|
+
assert.equal(result.mode, 'tweak');
|
|
42
|
+
assert.equal(result.explicit, true);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('infers hotfix for small change (≤2 tasks, ≤2 files, no code files in tasks)', () => {
|
|
46
|
+
// Use consistent paths — same file names in proposal AND tasks to avoid unique-count inflation
|
|
47
|
+
writeFileSync(join(tempDir, '.team-flow.yaml'), 'state: exploring\nworkflow: auto');
|
|
48
|
+
writeFileSync(join(tempDir, 'proposal.md'), '# Proposal\nFix typo in README.md');
|
|
49
|
+
writeFileSync(join(tempDir, 'tasks.md'), '- [ ] Fix typo in README.md\n- [ ] Verify fix');
|
|
50
|
+
|
|
51
|
+
const result = inferMode(tempDir);
|
|
52
|
+
// Hotfix check runs before tweak; 2 tasks, 1 file, no keywords → hotfix wins
|
|
53
|
+
assert.equal(result.mode, 'hotfix', `Expected hotfix but got ${result.mode}: ${result.reason}`);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('infers hotfix for small code change (≤2 tasks, ≤2 files, no schema)', () => {
|
|
57
|
+
writeFileSync(join(tempDir, '.team-flow.yaml'), 'state: exploring\nworkflow: auto');
|
|
58
|
+
writeFileSync(join(tempDir, 'proposal.md'), '# Proposal\nFix null check in util.ts');
|
|
59
|
+
writeFileSync(join(tempDir, 'tasks.md'), '- [ ] Add null check in util.ts\n- [ ] Add test for null case');
|
|
60
|
+
|
|
61
|
+
const result = inferMode(tempDir);
|
|
62
|
+
// 2 tasks, 1 file (util.ts), no schema keyword, code file → hotfix
|
|
63
|
+
assert.equal(result.mode, 'hotfix', `Expected hotfix but got ${result.mode}: ${result.reason}`);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('infers hotfix for small Java code changes', () => {
|
|
67
|
+
const changeDir = mkdtempSync(join(tempDir, 'java-hotfix-'));
|
|
68
|
+
writeFileSync(join(changeDir, '.team-flow.yaml'), 'state: exploring\nworkflow: auto');
|
|
69
|
+
writeFileSync(join(changeDir, 'proposal.md'), '# Proposal\nFix null check in src/Main.java');
|
|
70
|
+
writeFileSync(join(changeDir, 'tasks.md'), '- [ ] Fix null check in src/Main.java\n- [ ] Add regression test');
|
|
71
|
+
|
|
72
|
+
const result = inferMode(changeDir);
|
|
73
|
+
|
|
74
|
+
assert.equal(result.mode, 'hotfix', `Expected hotfix but got ${result.mode}: ${result.reason}`);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('does not infer tweak for multi-task Java and Go code changes', () => {
|
|
78
|
+
const javaDir = mkdtempSync(join(tempDir, 'java-code-'));
|
|
79
|
+
writeFileSync(join(javaDir, '.team-flow.yaml'), 'state: exploring\nworkflow: auto');
|
|
80
|
+
writeFileSync(join(javaDir, 'proposal.md'), '# Proposal\nRefactor service in src/Main.java');
|
|
81
|
+
writeFileSync(join(javaDir, 'tasks.md'), '- [ ] Update service in src/Main.java\n- [ ] Add unit test\n- [ ] Update integration test');
|
|
82
|
+
|
|
83
|
+
const goDir = mkdtempSync(join(tempDir, 'go-code-'));
|
|
84
|
+
writeFileSync(join(goDir, '.team-flow.yaml'), 'state: exploring\nworkflow: auto');
|
|
85
|
+
writeFileSync(join(goDir, 'proposal.md'), '# Proposal\nRefactor handler in cmd/server/main.go');
|
|
86
|
+
writeFileSync(join(goDir, 'tasks.md'), '- [ ] Update handler in cmd/server/main.go\n- [ ] Add unit test\n- [ ] Update wiring');
|
|
87
|
+
|
|
88
|
+
assert.equal(inferMode(javaDir).mode, 'full');
|
|
89
|
+
assert.equal(inferMode(goDir).mode, 'full');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('does not infer tweak for multi-task Python and Rust code changes', () => {
|
|
93
|
+
const pythonDir = mkdtempSync(join(tempDir, 'python-code-'));
|
|
94
|
+
writeFileSync(join(pythonDir, '.team-flow.yaml'), 'state: exploring\nworkflow: auto');
|
|
95
|
+
writeFileSync(join(pythonDir, 'proposal.md'), '# Proposal\nRefactor worker in app/worker.py');
|
|
96
|
+
writeFileSync(join(pythonDir, 'tasks.md'), '- [ ] Update worker in app/worker.py\n- [ ] Add unit test\n- [ ] Update scheduler');
|
|
97
|
+
|
|
98
|
+
const rustDir = mkdtempSync(join(tempDir, 'rust-code-'));
|
|
99
|
+
writeFileSync(join(rustDir, '.team-flow.yaml'), 'state: exploring\nworkflow: auto');
|
|
100
|
+
writeFileSync(join(rustDir, 'proposal.md'), '# Proposal\nRefactor parser in src/parser.rs');
|
|
101
|
+
writeFileSync(join(rustDir, 'tasks.md'), '- [ ] Update parser in src/parser.rs\n- [ ] Add unit test\n- [ ] Update caller');
|
|
102
|
+
|
|
103
|
+
assert.equal(inferMode(pythonDir).mode, 'full');
|
|
104
|
+
assert.equal(inferMode(rustDir).mode, 'full');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('infers tweak for config/doc-only change (≤4 tasks)', () => {
|
|
108
|
+
writeFileSync(join(tempDir, '.team-flow.yaml'), 'state: exploring\nworkflow: auto');
|
|
109
|
+
writeFileSync(join(tempDir, 'proposal.md'), '# Proposal\nUpdate README.md');
|
|
110
|
+
writeFileSync(join(tempDir, 'tasks.md'), '- [ ] Update README.md\n- [ ] Update CHANGELOG.md\n- [ ] Update version in package.json');
|
|
111
|
+
|
|
112
|
+
const result = inferMode(tempDir);
|
|
113
|
+
// 3 tasks, only doc/config files → tweak
|
|
114
|
+
assert.equal(result.mode, 'tweak', `Expected tweak but got ${result.mode}: ${result.reason}`);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('infers full when schema keyword detected', () => {
|
|
118
|
+
writeFileSync(join(tempDir, '.team-flow.yaml'), 'state: exploring\nworkflow: auto');
|
|
119
|
+
writeFileSync(join(tempDir, 'proposal.md'), '# Proposal\nChange the API interface for src/auth.ts');
|
|
120
|
+
writeFileSync(join(tempDir, 'tasks.md'), '- [ ] Update API\n- [ ] Update tests');
|
|
121
|
+
|
|
122
|
+
const result = inferMode(tempDir);
|
|
123
|
+
assert.equal(result.mode, 'full');
|
|
124
|
+
assert.ok(result.reason.includes('schema/API'));
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('infers full when new module keyword detected', () => {
|
|
128
|
+
writeFileSync(join(tempDir, '.team-flow.yaml'), 'state: exploring\nworkflow: auto');
|
|
129
|
+
writeFileSync(join(tempDir, 'proposal.md'), '# Proposal\nAdd 新增模块 for payment processing');
|
|
130
|
+
writeFileSync(join(tempDir, 'tasks.md'), '- [ ] Create new module');
|
|
131
|
+
|
|
132
|
+
const result = inferMode(tempDir);
|
|
133
|
+
assert.equal(result.mode, 'full');
|
|
134
|
+
assert.ok(result.reason.includes('new module'));
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('infers full when too many files (> 2) for hotfix', () => {
|
|
138
|
+
writeFileSync(join(tempDir, '.team-flow.yaml'), 'state: exploring\nworkflow: auto');
|
|
139
|
+
writeFileSync(join(tempDir, 'proposal.md'), '# Big change\nModify src/a.ts src/b.ts src/c.ts');
|
|
140
|
+
writeFileSync(join(tempDir, 'tasks.md'), '- [ ] Task 1\n- [ ] Task 2\n- [ ] Task 3');
|
|
141
|
+
|
|
142
|
+
const result = inferMode(tempDir);
|
|
143
|
+
// 3 files > 2 → not hotfix; 3 tasks ≤ 4 but files contain code → not tweak → full
|
|
144
|
+
assert.equal(result.mode, 'full');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('infers full when too many tasks (> 2) for hotfix (but not tweak due to code files)', () => {
|
|
148
|
+
writeFileSync(join(tempDir, '.team-flow.yaml'), 'state: exploring\nworkflow: auto');
|
|
149
|
+
writeFileSync(join(tempDir, 'proposal.md'), '# Change\nModify src/a.ts');
|
|
150
|
+
writeFileSync(join(tempDir, 'tasks.md'), '- [ ] Task 1\n- [ ] Task 2\n- [ ] Task 3\n- [ ] Task 4\n- [ ] Task 5');
|
|
151
|
+
|
|
152
|
+
const result = inferMode(tempDir);
|
|
153
|
+
assert.equal(result.mode, 'full');
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('returns reason string for all modes', () => {
|
|
157
|
+
const result = inferMode(tempDir);
|
|
158
|
+
assert.ok(typeof result.reason === 'string');
|
|
159
|
+
assert.ok(result.reason.length > 0);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
|
|
6
|
+
const source = readFileSync(join(process.cwd(), 'scripts', 'install-git-hooks.mjs'), 'utf8');
|
|
7
|
+
|
|
8
|
+
describe('install-git-hooks', () => {
|
|
9
|
+
it('resolves the hook directory and checked repository from Git at runtime', () => {
|
|
10
|
+
assert.match(source, /git rev-parse --git-path hooks/);
|
|
11
|
+
assert.match(source, /git rev-parse --show-toplevel/);
|
|
12
|
+
assert.doesNotMatch(source, /REPO_ROOT="\$\(cd "\$\(dirname "\$0"\)\/\.\.\/\.\." && pwd\)"/);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('upgrades an existing team-flow hook when its generated content is stale', () => {
|
|
16
|
+
assert.match(source, /existing === HOOK_SCRIPT/);
|
|
17
|
+
assert.match(source, /Updating existing pre-commit hook/);
|
|
18
|
+
assert.doesNotMatch(source, /if \(existing\.includes\(MARKER\)\) \{\s*console\.log\([^\n]+\);\s*process\.exit\(0\);/s);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
|
|
6
|
+
const read = file => readFileSync(join(process.cwd(), file), 'utf8');
|
|
7
|
+
|
|
8
|
+
describe('marketplace release documentation', () => {
|
|
9
|
+
it('uses the real Codex selectors and upgrade flow', () => {
|
|
10
|
+
for (const text of [read('README.md'), read('INSTALL.md')]) {
|
|
11
|
+
assert.match(text, /team-flow@awesome-codex-plugins/);
|
|
12
|
+
assert.match(text, /team-flow@team-flow/);
|
|
13
|
+
assert.match(text, /codex plugin marketplace upgrade awesome-codex-plugins/);
|
|
14
|
+
assert.doesNotMatch(text, /codex plugin update/);
|
|
15
|
+
assert.match(text, /codex plugin list/);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('records v0.9.0 highlights and the external delivery gate', () => {
|
|
20
|
+
const readme = read('README.md');
|
|
21
|
+
const checklist = read('docs/release-checklist.md');
|
|
22
|
+
assert.match(readme, /v0\.9\.0/);
|
|
23
|
+
assert.match(readme, /Node 20/);
|
|
24
|
+
assert.match(readme, /model profiles/);
|
|
25
|
+
assert.match(readme, /最小性/);
|
|
26
|
+
assert.match(checklist, /AI Agent Marketplace Delivery/);
|
|
27
|
+
assert.match(checklist, /verify-marketplace-release/);
|
|
28
|
+
assert.match(checklist, /同步 PR/);
|
|
29
|
+
assert.match(checklist, /干净 Codex/);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
|
|
6
|
+
const ROOT = process.cwd();
|
|
7
|
+
const read = file => readFileSync(join(ROOT, file), 'utf8');
|
|
8
|
+
|
|
9
|
+
describe('minimality discipline: broad reviewer', () => {
|
|
10
|
+
it('traces unrequested complexity to the missing requirement and diff line', () => {
|
|
11
|
+
const skill = read('skills/code-reviewer/SKILL.md');
|
|
12
|
+
const prompt = read('skills/code-reviewer/code-reviewer-prompt.md');
|
|
13
|
+
assert.match(skill, /## Minimality And Scope/);
|
|
14
|
+
assert.match(skill, /missing task requirement and diff line/);
|
|
15
|
+
assert.match(skill, /Important for merge-blocking complexity/);
|
|
16
|
+
assert.match(skill, /Minor for safe/);
|
|
17
|
+
assert.match(skill, /never score by line count/);
|
|
18
|
+
assert.match(prompt, /## Minimality And Scope/);
|
|
19
|
+
assert.match(prompt, /unrequested dependency, configuration surface, abstraction, or unrelated refactor/);
|
|
20
|
+
assert.match(prompt, /missing task requirement and diff line/);
|
|
21
|
+
assert.match(prompt, /merge-blocking complexity as Important/);
|
|
22
|
+
assert.match(prompt, /behavior-neutral redundancy as Minor/);
|
|
23
|
+
assert.match(prompt, /Do not use line count as evidence/);
|
|
24
|
+
assert.match(prompt, /do not recommend removing\s+required tests, validation, security, or error handling/);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('records #38 without adding a Ponytail dependency', () => {
|
|
28
|
+
const changelog = read('CHANGELOG.md');
|
|
29
|
+
const pkg = JSON.parse(read('package.json'));
|
|
30
|
+
const lock = JSON.parse(read('package-lock.json'));
|
|
31
|
+
assert.match(changelog, /#38/);
|
|
32
|
+
assert.match(changelog, /不引入 Ponytail 或任何 runtime dependency/);
|
|
33
|
+
assert.equal(pkg.dependencies?.ponytail, undefined);
|
|
34
|
+
assert.equal(pkg.devDependencies?.ponytail, undefined);
|
|
35
|
+
assert.equal(lock.packages['node_modules/ponytail'], undefined);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
|
|
6
|
+
const ROOT = process.cwd();
|
|
7
|
+
const PROFILES = ['mechanical', 'standard', 'strong', 'review'];
|
|
8
|
+
const FILES = [
|
|
9
|
+
'skills/build-executor/SKILL.md',
|
|
10
|
+
'README.md',
|
|
11
|
+
'docs/README_en.md',
|
|
12
|
+
'CHANGELOG.md',
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
describe('model profile documentation', () => {
|
|
16
|
+
it('names every supported profile in all affected documentation', () => {
|
|
17
|
+
for (const file of FILES) {
|
|
18
|
+
const content = readFileSync(join(ROOT, file), 'utf8');
|
|
19
|
+
for (const profile of PROFILES) {
|
|
20
|
+
assert.match(content, new RegExp(`\\b${profile}\\b`), `${file} must name ${profile}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('documents read-only resolution without automatic switching', () => {
|
|
26
|
+
const zh = readFileSync(join(ROOT, 'README.md'), 'utf8');
|
|
27
|
+
const en = readFileSync(join(ROOT, 'docs/README_en.md'), 'utf8');
|
|
28
|
+
assert.match(zh, /--resolve-model/);
|
|
29
|
+
assert.match(zh, /不切换当前会话模型/);
|
|
30
|
+
assert.match(en, /--resolve-model/);
|
|
31
|
+
assert.match(en, /does not switch models/);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
|
|
6
|
+
const ROOT = process.cwd();
|
|
7
|
+
const read = file => readFileSync(join(ROOT, file), 'utf8');
|
|
8
|
+
const ciJob = (ci, name, nextName) => {
|
|
9
|
+
const start = ci.indexOf(` ${name}:`);
|
|
10
|
+
const end = nextName ? ci.indexOf(` ${nextName}:`, start) : ci.length;
|
|
11
|
+
return ci.slice(start, end);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
describe('Node 20 compatibility contract', () => {
|
|
15
|
+
it('declares Node 20 in package metadata and lockfile', () => {
|
|
16
|
+
const pkg = JSON.parse(read('package.json'));
|
|
17
|
+
const lock = JSON.parse(read('package-lock.json'));
|
|
18
|
+
assert.equal(pkg.engines.node, '>=20');
|
|
19
|
+
assert.equal(lock.packages[''].engines.node, '>=20');
|
|
20
|
+
assert.equal(lock.packages['node_modules/typescript'].engines.node, '>=14.17');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('isolates CI jobs so adjacent runtime settings cannot satisfy a job contract', () => {
|
|
24
|
+
const fixture = ` build-and-test:
|
|
25
|
+
strategy:
|
|
26
|
+
matrix:
|
|
27
|
+
node-version: [20, 22]
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/setup-node
|
|
30
|
+
with:
|
|
31
|
+
node-version: 22
|
|
32
|
+
platform-install-smoke:
|
|
33
|
+
strategy:
|
|
34
|
+
matrix:
|
|
35
|
+
node-version: [20, 22]
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/setup-node
|
|
38
|
+
with:
|
|
39
|
+
node-version: \${{ matrix.node-version }}`;
|
|
40
|
+
|
|
41
|
+
assert.doesNotMatch(
|
|
42
|
+
ciJob(fixture, 'build-and-test', 'platform-install-smoke'),
|
|
43
|
+
/node-version: \$\{\{ matrix\.node-version \}\}/,
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('runs build, test, doctor, CLI smoke, and installers on Node 20 and 22', () => {
|
|
48
|
+
const ci = read('.github/workflows/ci.yml');
|
|
49
|
+
const build = ciJob(ci, 'build-and-test', 'platform-install-smoke');
|
|
50
|
+
const platform = ciJob(ci, 'platform-install-smoke', 'release');
|
|
51
|
+
const release = ciJob(ci, 'release');
|
|
52
|
+
|
|
53
|
+
for (const job of [build, platform]) {
|
|
54
|
+
assert.match(job, /node-version: \[20, 22\]/);
|
|
55
|
+
assert.match(job, /node-version: \$\{\{ matrix\.node-version \}\}/);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
assert.match(build, /- run: npm run build/);
|
|
59
|
+
assert.match(build, /- run: npm test/);
|
|
60
|
+
assert.match(build, /run: node scripts\/check-version-consistency\.mjs/);
|
|
61
|
+
assert.match(build, /node scripts\/team-flow\.mjs doctor/);
|
|
62
|
+
assert.match(build, /node scripts\/team-flow\.mjs --version/);
|
|
63
|
+
assert.match(build, /config --get execution\.inlineThreshold/);
|
|
64
|
+
assert.match(release, /node-version: 22\n registry-url:/);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('keeps project guidance aligned with the Node 20 test command', () => {
|
|
68
|
+
const guide = read('AGENTS.md');
|
|
69
|
+
assert.match(guide, /Node 20\+ native test runner/);
|
|
70
|
+
assert.match(guide, /tests\/e2e\.test\.mjs/);
|
|
71
|
+
assert.doesNotMatch(guide, /experimental-strip-types/);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
|
|
6
|
+
const ROOT = process.cwd();
|
|
7
|
+
const pkg = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf8'));
|
|
8
|
+
|
|
9
|
+
describe('Node 20 test entry', () => {
|
|
10
|
+
it('runs only ESM JavaScript test files without strip-types', () => {
|
|
11
|
+
assert.match(pkg.scripts.test, /tests\/e2e\.test\.mjs/);
|
|
12
|
+
assert.match(pkg.scripts.test, /tests\/lib\/\*\.test\.mjs/);
|
|
13
|
+
assert.doesNotMatch(pkg.scripts.test, /experimental-strip-types/);
|
|
14
|
+
assert.equal(existsSync(join(ROOT, 'tests', 'e2e.test.mjs')), true);
|
|
15
|
+
assert.equal(existsSync(join(ROOT, 'tests', 'e2e.test.ts')), false);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// Canonical skill protocol checks. Later distribution waves extend this file
|
|
2
|
+
// with installer and platform inventory fixtures.
|
|
3
|
+
import { describe, it } from 'node:test';
|
|
4
|
+
import assert from 'node:assert/strict';
|
|
5
|
+
import { readFileSync, realpathSync } from 'node:fs';
|
|
6
|
+
import { execFileSync } from 'node:child_process';
|
|
7
|
+
import { mkdtempSync, rmSync } from 'node:fs';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
import { tmpdir } from 'node:os';
|
|
10
|
+
import { PLATFORM_RUNTIME_INVENTORY, ZCODE_COMPATIBILITY_PATH } from '../../scripts/lib/platform-runtime-inventory.mjs';
|
|
11
|
+
|
|
12
|
+
const ROOT = process.cwd();
|
|
13
|
+
const VERSION = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf8')).version;
|
|
14
|
+
const PREFIX = `npx --yes --package @xulthekl/team-flow@${VERSION} tf`;
|
|
15
|
+
const CLI = join(ROOT, 'scripts', 'team-flow.mjs');
|
|
16
|
+
const RUNTIME_SKILLS = [
|
|
17
|
+
'workflow-start',
|
|
18
|
+
'need-explorer',
|
|
19
|
+
'spec-writer',
|
|
20
|
+
'contract-builder',
|
|
21
|
+
'build-executor',
|
|
22
|
+
'code-reviewer',
|
|
23
|
+
'bug-investigator',
|
|
24
|
+
'release-archivist',
|
|
25
|
+
'spec-merger',
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
function skill(name) {
|
|
29
|
+
return readFileSync(join(ROOT, 'skills', name, 'SKILL.md'), 'utf8');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe('canonical skill runtime protocol', () => {
|
|
33
|
+
it('uses the exact package-version prefix for every runtime-dependent skill', () => {
|
|
34
|
+
for (const name of RUNTIME_SKILLS) {
|
|
35
|
+
const content = skill(name);
|
|
36
|
+
assert.match(content, new RegExp(PREFIX.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')),
|
|
37
|
+
`${name} should use the canonical portable prefix`);
|
|
38
|
+
assert.doesNotMatch(content, /\$\{CLAUDE_PLUGIN_ROOT\}|\$\{PLUGIN_ROOT\}/,
|
|
39
|
+
`${name} should not require a host plugin-root variable`);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('uses allowlisted runtime assets for build-executor prompts', () => {
|
|
44
|
+
const content = skill('build-executor');
|
|
45
|
+
|
|
46
|
+
assert.match(content, /runtime asset read skills\/build-executor\/implementer-prompt\.md/);
|
|
47
|
+
assert.match(content, /runtime asset read skills\/build-executor\/task-reviewer-prompt\.md/);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('does not leave bare tf commands in runtime instructions or reviewer templates', () => {
|
|
51
|
+
const files = [
|
|
52
|
+
...RUNTIME_SKILLS.map(name => join(ROOT, 'skills', name, 'SKILL.md')),
|
|
53
|
+
join(ROOT, 'skills', 'build-executor', 'implementer-prompt.md'),
|
|
54
|
+
join(ROOT, 'skills', 'build-executor', 'task-reviewer-prompt.md'),
|
|
55
|
+
join(ROOT, 'skills', 'code-reviewer', 'code-reviewer-prompt.md'),
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
for (const file of files) {
|
|
59
|
+
const content = readFileSync(file, 'utf8');
|
|
60
|
+
assert.doesNotMatch(content, /`tf\s+(?:handoff|checkpoint|execution|state|runtime|validate|doctor|config|version)\b/,
|
|
61
|
+
`${file} contains a bare tf command`);
|
|
62
|
+
assert.doesNotMatch(content, /^\s*tf\s+(?:handoff|checkpoint|execution|state|runtime|validate|doctor|config|version)\b/m,
|
|
63
|
+
`${file} contains an unprefixed tf command line`);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('local runtime deployment', () => {
|
|
69
|
+
it('rewrites the canonical prefix to ZCODE\'s installed runtime tree', () => {
|
|
70
|
+
const target = mkdtempSync(join(tmpdir(), 'tf-zcode-runtime-'));
|
|
71
|
+
try {
|
|
72
|
+
execFileSync(process.execPath, [CLI, 'install-zcode', '--local', ROOT], {
|
|
73
|
+
cwd: target,
|
|
74
|
+
stdio: 'pipe',
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const pluginRoot = join(target, '.zcode', 'team-flow');
|
|
78
|
+
const content = readFileSync(join(target, '.zcode', 'skills', 'workflow-start', 'SKILL.md'), 'utf8');
|
|
79
|
+
const localPrefix = `node '${join(realpathSync(pluginRoot), 'scripts', 'team-flow.mjs')}'`;
|
|
80
|
+
|
|
81
|
+
assert.match(content, new RegExp(localPrefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
|
|
82
|
+
assert.doesNotMatch(content, new RegExp(PREFIX.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
|
|
83
|
+
} finally {
|
|
84
|
+
rmSync(target, { recursive: true, force: true });
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('emits a shell-literal local runtime command when the target path contains a dollar sign', () => {
|
|
89
|
+
const target = mkdtempSync(join(tmpdir(), 'tf-$runtime-'));
|
|
90
|
+
try {
|
|
91
|
+
execFileSync(process.execPath, [CLI, 'install-zcode', '--local', ROOT], {
|
|
92
|
+
cwd: target,
|
|
93
|
+
stdio: 'pipe',
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const content = readFileSync(join(target, '.zcode', 'skills', 'workflow-start', 'SKILL.md'), 'utf8');
|
|
97
|
+
const command = content.match(/node '[^']+' runtime asset read docs\/state-machine\.md/);
|
|
98
|
+
assert.ok(command, 'workflow-start should contain a shell-literal local runtime command');
|
|
99
|
+
|
|
100
|
+
const output = execFileSync('sh', ['-c', command[0]], { cwd: target, encoding: 'utf8' });
|
|
101
|
+
assert.match(output, /State Machine/);
|
|
102
|
+
} finally {
|
|
103
|
+
rmSync(target, { recursive: true, force: true });
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe('platform runtime inventory', () => {
|
|
109
|
+
it('lists every documented platform and the ZCODE compatibility path', () => {
|
|
110
|
+
const ids = new Set(PLATFORM_RUNTIME_INVENTORY.map(platform => platform.id));
|
|
111
|
+
|
|
112
|
+
assert.deepEqual(ids, new Set([
|
|
113
|
+
'claude-code', 'cursor', 'codex-cli', 'codex-app', 'copilot-cli', 'gemini-cli',
|
|
114
|
+
'opencode', 'workbuddy', 'trae', 'cline', 'kiro', 'windsurf', 'qwen',
|
|
115
|
+
'amazon-q', 'roocode', 'continue', 'pi',
|
|
116
|
+
]));
|
|
117
|
+
assert.equal(ZCODE_COMPATIBILITY_PATH.id, 'zcode');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('gives each documented distribution an explicit, testable runtime mode', () => {
|
|
121
|
+
const validModes = new Set(['native-root', 'installer-rewrite', 'canonical-fallback']);
|
|
122
|
+
|
|
123
|
+
for (const platform of PLATFORM_RUNTIME_INVENTORY) {
|
|
124
|
+
assert.ok(platform.modes.length > 0, `${platform.id} needs at least one distribution mode`);
|
|
125
|
+
for (const mode of platform.modes) assert.ok(validModes.has(mode), `${platform.id}: ${mode}`);
|
|
126
|
+
}
|
|
127
|
+
assert.deepEqual(ZCODE_COMPATIBILITY_PATH.modes, ['installer-rewrite']);
|
|
128
|
+
assert.ok(idsWithMode('codex-cli', 'canonical-fallback'));
|
|
129
|
+
assert.ok(idsWithMode('codex-app', 'canonical-fallback'));
|
|
130
|
+
assert.ok(idsWithMode('opencode', 'canonical-fallback'));
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe('runtime version synchronization', () => {
|
|
135
|
+
it('includes code-reviewer when a release version is dry-run', () => {
|
|
136
|
+
const output = execFileSync(process.execPath, [CLI, 'version', '0.9.2', '--dry-run'], {
|
|
137
|
+
cwd: ROOT,
|
|
138
|
+
encoding: 'utf8',
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
assert.match(output, /skills\/code-reviewer\/SKILL\.md: version string updated/);
|
|
142
|
+
assert.match(output, /skills\/build-executor\/implementer-prompt\.md: version string updated/);
|
|
143
|
+
assert.match(output, /skills\/build-executor\/task-reviewer-prompt\.md: version string updated/);
|
|
144
|
+
assert.match(output, /skills\/code-reviewer\/code-reviewer-prompt\.md: version string updated/);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
function idsWithMode(id, mode) {
|
|
149
|
+
return PLATFORM_RUNTIME_INVENTORY.find(platform => platform.id === id)?.modes.includes(mode);
|
|
150
|
+
}
|