@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,137 @@
|
|
|
1
|
+
// Dispatcher-level tests for tf handoff.
|
|
2
|
+
import { afterEach, beforeEach, describe, it } from 'node:test';
|
|
3
|
+
import assert from 'node:assert/strict';
|
|
4
|
+
import { execFileSync } from 'node:child_process';
|
|
5
|
+
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
6
|
+
import { tmpdir } from 'node:os';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
|
|
9
|
+
const CLI = join(process.cwd(), 'scripts/team-flow.mjs');
|
|
10
|
+
let changeDir;
|
|
11
|
+
|
|
12
|
+
function runSsf(args) {
|
|
13
|
+
try {
|
|
14
|
+
const stdout = execFileSync(process.execPath, [CLI, ...args], {
|
|
15
|
+
encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
|
|
16
|
+
});
|
|
17
|
+
return { exitCode: 0, stdout, stderr: '' };
|
|
18
|
+
} catch (error) {
|
|
19
|
+
return {
|
|
20
|
+
exitCode: error.status || 1,
|
|
21
|
+
stdout: error.stdout?.toString() || '',
|
|
22
|
+
stderr: error.stderr?.toString() || error.message,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
changeDir = mkdtempSync(join(tmpdir(), 'tf-handoff-'));
|
|
29
|
+
writeFileSync(join(changeDir, 'proposal.md'), '# Proposal\n\nA valid proposal.\n');
|
|
30
|
+
writeFileSync(join(changeDir, 'design.md'), '# Design\n\nA valid design.\n');
|
|
31
|
+
writeFileSync(join(changeDir, 'tasks.md'), '# Tasks\n\n- [ ] 1.1 Original task text\n');
|
|
32
|
+
writeFileSync(join(changeDir, '.team-flow.yaml'), 'state: executing\nworkflow: auto\n');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
rmSync(changeDir, { recursive: true, force: true });
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('tf handoff', () => {
|
|
40
|
+
it('creates, finishes, and resolves a prototype contract without mutating planning files', () => {
|
|
41
|
+
const designBefore = readFileSync(join(changeDir, 'design.md'));
|
|
42
|
+
const tasksBefore = readFileSync(join(changeDir, 'tasks.md'));
|
|
43
|
+
const create = runSsf([
|
|
44
|
+
'handoff', 'create', changeDir, '--type', 'prototype',
|
|
45
|
+
'--objective', 'Compare two filter interactions',
|
|
46
|
+
'--expected-output', 'A short recommendation with evidence',
|
|
47
|
+
'--acceptance', 'Document one recommended interaction',
|
|
48
|
+
'--boundary', 'Do not alter planning artifacts', '--boundary', 'Use only fixture data',
|
|
49
|
+
]);
|
|
50
|
+
assert.equal(create.exitCode, 0, create.stderr);
|
|
51
|
+
|
|
52
|
+
const listed = runSsf(['handoff', 'list', changeDir, '--json']);
|
|
53
|
+
assert.equal(listed.exitCode, 0, listed.stderr);
|
|
54
|
+
const handoff = JSON.parse(listed.stdout).handoffs[0];
|
|
55
|
+
assert.equal(handoff.type, 'prototype');
|
|
56
|
+
assert.match(handoff.source, /- Do not alter planning artifacts/);
|
|
57
|
+
assert.match(handoff.source, /- Use only fixture data/);
|
|
58
|
+
|
|
59
|
+
writeFileSync(join(handoff.directory, 'HANDOFF_RESULT.md'), validResult());
|
|
60
|
+
assert.equal(runSsf(['handoff', 'finish', changeDir, handoff.id]).exitCode, 0);
|
|
61
|
+
assert.equal(runSsf(['handoff', 'resolve', changeDir, handoff.id, '--decision', 'accept']).exitCode, 0);
|
|
62
|
+
assert.deepEqual(readFileSync(join(changeDir, 'design.md')), designBefore);
|
|
63
|
+
assert.deepEqual(readFileSync(join(changeDir, 'tasks.md')), tasksBefore);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('rejects an unknown handoff type', () => {
|
|
67
|
+
const result = runSsf([
|
|
68
|
+
'handoff', 'create', changeDir, '--type', 'unknown', '--objective', 'Objective',
|
|
69
|
+
'--expected-output', 'Expected output', '--acceptance', 'Acceptance',
|
|
70
|
+
]);
|
|
71
|
+
assert.equal(result.exitCode, 1);
|
|
72
|
+
assert.match(result.stderr, /Unsupported handoff type 'unknown'/);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('rejects a result without evidence and leaves the handoff active', () => {
|
|
76
|
+
const handoff = createHandoff();
|
|
77
|
+
writeFileSync(join(handoff.directory, 'HANDOFF_RESULT.md'), validResult().replace('Prototype screenshots and task notes.', ''));
|
|
78
|
+
|
|
79
|
+
const result = runSsf(['handoff', 'finish', changeDir, handoff.id]);
|
|
80
|
+
assert.equal(result.exitCode, 1);
|
|
81
|
+
assert.match(result.stderr, /Evidence must contain non-empty content/);
|
|
82
|
+
assert.equal(getHandoff(handoff.id).status, 'active');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('rejects an active handoff resolution', () => {
|
|
86
|
+
const handoff = createHandoff();
|
|
87
|
+
const result = runSsf(['handoff', 'resolve', changeDir, handoff.id, '--decision', 'accept']);
|
|
88
|
+
assert.equal(result.exitCode, 1);
|
|
89
|
+
assert.match(result.stderr, /is not result-ready/);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('rejects an unknown resolution decision', () => {
|
|
93
|
+
const handoff = createHandoff();
|
|
94
|
+
const result = runSsf(['handoff', 'resolve', changeDir, handoff.id, '--decision', 'ignore']);
|
|
95
|
+
assert.equal(result.exitCode, 1);
|
|
96
|
+
assert.match(result.stderr, /Unsupported handoff decision 'ignore'/);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('requires acknowledgement when source artifacts drift', () => {
|
|
100
|
+
const handoff = createHandoff();
|
|
101
|
+
writeFileSync(join(handoff.directory, 'HANDOFF_RESULT.md'), validResult());
|
|
102
|
+
assert.equal(runSsf(['handoff', 'finish', changeDir, handoff.id]).exitCode, 0);
|
|
103
|
+
writeFileSync(join(changeDir, 'proposal.md'), '# Proposal\n\nChanged after handoff creation.\n');
|
|
104
|
+
|
|
105
|
+
const unresolved = runSsf(['handoff', 'resolve', changeDir, handoff.id, '--decision', 'accept']);
|
|
106
|
+
assert.equal(unresolved.exitCode, 1);
|
|
107
|
+
assert.match(unresolved.stderr, /acknowledge-source-drift/);
|
|
108
|
+
assert.equal(runSsf([
|
|
109
|
+
'handoff', 'resolve', changeDir, handoff.id, '--decision', 'accept', '--acknowledge-source-drift',
|
|
110
|
+
]).exitCode, 0);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
function createHandoff() {
|
|
115
|
+
const result = runSsf([
|
|
116
|
+
'handoff', 'create', changeDir, '--type', 'research', '--objective', 'Inspect behavior',
|
|
117
|
+
'--expected-output', 'Document findings', '--acceptance', 'List evidence',
|
|
118
|
+
]);
|
|
119
|
+
assert.equal(result.exitCode, 0, result.stderr);
|
|
120
|
+
return JSON.parse(runSsf(['handoff', 'list', changeDir, '--json']).stdout).handoffs[0];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function getHandoff(id) {
|
|
124
|
+
return JSON.parse(runSsf(['handoff', 'list', changeDir, '--json']).stdout)
|
|
125
|
+
.handoffs.find(handoff => handoff.id === id);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function validResult() {
|
|
129
|
+
return [
|
|
130
|
+
'## Conclusion\n\nKeep the compact filter interaction.',
|
|
131
|
+
'## Evidence\n\nPrototype screenshots and task notes.',
|
|
132
|
+
'## Produced Artifacts\n\nprototype/filter.md',
|
|
133
|
+
'## Risks\n\nKeyboard flow needs a follow-up.',
|
|
134
|
+
'## Suggested Changes\n\n- Add keyboard behavior to tasks.md.',
|
|
135
|
+
'',
|
|
136
|
+
].join('\n');
|
|
137
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
// tests/lib/cmd-inject.test.mjs
|
|
2
|
+
// Tests for scripts/lib/cmd-inject.mjs
|
|
3
|
+
import { describe, it, before } from 'node:test';
|
|
4
|
+
import assert from 'node:assert/strict';
|
|
5
|
+
import { execFileSync } from 'node:child_process';
|
|
6
|
+
import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
7
|
+
import { tmpdir } from 'node:os';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
|
|
10
|
+
let generatePhaseGuard, toCursorMdc, toCopilotInstructions;
|
|
11
|
+
|
|
12
|
+
describe('cmd-inject: generatePhaseGuard()', () => {
|
|
13
|
+
before(async () => {
|
|
14
|
+
const modulePath = join(process.cwd(), 'scripts/lib/cmd-inject.mjs');
|
|
15
|
+
const mod = await import(modulePath);
|
|
16
|
+
generatePhaseGuard = mod.generatePhaseGuard;
|
|
17
|
+
toCursorMdc = mod.toCursorMdc;
|
|
18
|
+
toCopilotInstructions = mod.toCopilotInstructions;
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('replaces {{change_name}} placeholder', () => {
|
|
22
|
+
const result = generatePhaseGuard({ state: 'exploring', change_name: 'add-csv-export' });
|
|
23
|
+
assert.ok(result.includes('add-csv-export'), `Expected "add-csv-export" in output, got: ${result.substring(0, 100)}`);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('replaces {{state}} placeholder', () => {
|
|
27
|
+
const result = generatePhaseGuard({ state: 'executing', change_name: 'test' });
|
|
28
|
+
assert.ok(result.includes('executing'));
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('replaces {{workflow}} placeholder', () => {
|
|
32
|
+
const result = generatePhaseGuard({ state: 'exploring', workflow: 'hotfix', change_name: 'test' });
|
|
33
|
+
assert.ok(result.includes('hotfix'));
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('generates exploring phase with allowed operations', () => {
|
|
37
|
+
const result = generatePhaseGuard({ state: 'exploring', change_name: 'test' });
|
|
38
|
+
assert.ok(result.includes('澄清需求'));
|
|
39
|
+
assert.ok(result.includes('禁止操作'));
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('generates specifying phase with allowed operations', () => {
|
|
43
|
+
const result = generatePhaseGuard({ state: 'specifying', change_name: 'test' });
|
|
44
|
+
assert.ok(result.includes('specs/'));
|
|
45
|
+
assert.ok(result.includes('design.md'));
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('generates bridging phase with contract operations', () => {
|
|
49
|
+
const result = generatePhaseGuard({ state: 'bridging', change_name: 'test' });
|
|
50
|
+
assert.ok(result.includes('execution-contract.md'));
|
|
51
|
+
assert.ok(result.includes('tf validate'));
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('generates approved-for-build phase', () => {
|
|
55
|
+
const result = generatePhaseGuard({ state: 'approved-for-build', change_name: 'test' });
|
|
56
|
+
assert.ok(result.includes('执行模式'));
|
|
57
|
+
assert.ok(result.includes('DP-4'));
|
|
58
|
+
assert.match(result, /execution plan/);
|
|
59
|
+
assert.match(result, /不得开始实现/);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('generates executing phase with test prohibition', () => {
|
|
63
|
+
const result = generatePhaseGuard({ state: 'executing', change_name: 'test' });
|
|
64
|
+
assert.ok(result.includes('跳过测试'));
|
|
65
|
+
assert.match(result, /wave review/);
|
|
66
|
+
assert.match(result, /review receipt/);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('generates debugging phase with root cause analysis', () => {
|
|
70
|
+
const result = generatePhaseGuard({ state: 'debugging', change_name: 'test' });
|
|
71
|
+
assert.ok(result.includes('根因分析'));
|
|
72
|
+
assert.ok(result.includes('TDD 修复循环'));
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('generates closing phase with verification', () => {
|
|
76
|
+
const result = generatePhaseGuard({ state: 'closing', change_name: 'test' });
|
|
77
|
+
assert.ok(result.includes('三维验证'));
|
|
78
|
+
assert.ok(result.includes('DP-7'));
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('generates abandoned terminal state', () => {
|
|
82
|
+
const result = generatePhaseGuard({ state: 'abandoned', change_name: 'test' });
|
|
83
|
+
assert.ok(result.includes('终止状态'));
|
|
84
|
+
assert.ok(result.includes('不得合并'));
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('falls back to exploring for unknown state', () => {
|
|
88
|
+
const result = generatePhaseGuard({ state: 'unknown-state', change_name: 'test' });
|
|
89
|
+
assert.ok(result.includes('澄清需求'), `Expected exploring fallback, got: ${result.substring(0, 200)}`);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('uses defaults when optional fields missing', () => {
|
|
93
|
+
const result = generatePhaseGuard({ state: 'exploring' });
|
|
94
|
+
// change_name defaults to 'unknown'
|
|
95
|
+
assert.ok(result.includes('unknown'));
|
|
96
|
+
// workflow defaults to 'full'
|
|
97
|
+
assert.ok(result.includes('full'));
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe('cmd-inject: toCursorMdc()', () => {
|
|
102
|
+
it('wraps base content with Cursor MDC frontmatter', () => {
|
|
103
|
+
const base = '# Phase Guard: test-change\n\n## Allowed\n- Do stuff';
|
|
104
|
+
const result = toCursorMdc(base);
|
|
105
|
+
|
|
106
|
+
assert.ok(result.includes('---'), 'Should have frontmatter delimiter');
|
|
107
|
+
assert.ok(result.includes('description: team-flow phase guard'));
|
|
108
|
+
assert.ok(result.includes('alwaysApply: true'));
|
|
109
|
+
assert.ok(result.includes('test-change'));
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe('cmd-inject: toCopilotInstructions()', () => {
|
|
114
|
+
it('simplifies heading for Copilot format', () => {
|
|
115
|
+
const base = '# Phase Guard: test-change\n\n## Allowed\n- Do stuff';
|
|
116
|
+
const result = toCopilotInstructions(base);
|
|
117
|
+
|
|
118
|
+
assert.ok(result.includes('# Phase Guard'));
|
|
119
|
+
assert.ok(result.includes('## Allowed'));
|
|
120
|
+
// Should NOT contain the change name in heading
|
|
121
|
+
assert.ok(!result.includes('# Phase Guard: test-change'));
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe('cmd-inject: platform resolution', () => {
|
|
126
|
+
let resolvePlatforms, detectProjectPlatforms;
|
|
127
|
+
|
|
128
|
+
before(async () => {
|
|
129
|
+
const modulePath = join(process.cwd(), 'scripts/lib/cmd-inject.mjs');
|
|
130
|
+
const mod = await import(modulePath);
|
|
131
|
+
resolvePlatforms = mod.resolvePlatforms;
|
|
132
|
+
detectProjectPlatforms = mod.detectProjectPlatforms;
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('resolves explicit all to all supported platforms', () => {
|
|
136
|
+
const result = resolvePlatforms('all', process.cwd());
|
|
137
|
+
assert.deepEqual(result, { ok: true, platforms: ['claude', 'cursor', 'copilot', 'gemini'] });
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('detects Cursor from .cursor marker', () => {
|
|
141
|
+
const dir = mkdtempSync(join(tmpdir(), 'tf-inject-cursor-'));
|
|
142
|
+
try {
|
|
143
|
+
mkdirSync(join(dir, '.cursor'), { recursive: true });
|
|
144
|
+
assert.deepEqual(detectProjectPlatforms(dir), ['cursor']);
|
|
145
|
+
assert.deepEqual(resolvePlatforms(undefined, dir), { ok: true, platforms: ['cursor'] });
|
|
146
|
+
} finally {
|
|
147
|
+
rmSync(dir, { recursive: true, force: true });
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('rejects omitted platforms when no marker exists', () => {
|
|
152
|
+
const dir = mkdtempSync(join(tmpdir(), 'tf-inject-none-'));
|
|
153
|
+
try {
|
|
154
|
+
const result = resolvePlatforms(undefined, dir);
|
|
155
|
+
assert.equal(result.ok, false);
|
|
156
|
+
assert.match(result.message, /--platforms/);
|
|
157
|
+
} finally {
|
|
158
|
+
rmSync(dir, { recursive: true, force: true });
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('rejects omitted platforms when multiple markers exist', () => {
|
|
163
|
+
const dir = mkdtempSync(join(tmpdir(), 'tf-inject-multi-'));
|
|
164
|
+
try {
|
|
165
|
+
mkdirSync(join(dir, '.cursor'), { recursive: true });
|
|
166
|
+
mkdirSync(join(dir, '.claude'), { recursive: true });
|
|
167
|
+
const result = resolvePlatforms(undefined, dir);
|
|
168
|
+
assert.equal(result.ok, false);
|
|
169
|
+
assert.match(result.message, /multiple/i);
|
|
170
|
+
} finally {
|
|
171
|
+
rmSync(dir, { recursive: true, force: true });
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
describe('cmd-inject: CLI writes', () => {
|
|
177
|
+
const CLI = join(process.cwd(), 'scripts/team-flow.mjs');
|
|
178
|
+
|
|
179
|
+
function runInject(cwd, changeDir, extraArgs = []) {
|
|
180
|
+
try {
|
|
181
|
+
const stdout = execFileSync(process.execPath, [CLI, 'inject', changeDir, ...extraArgs], { cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
182
|
+
return { exitCode: 0, stdout, stderr: '' };
|
|
183
|
+
} catch (err) {
|
|
184
|
+
return { exitCode: err.status || 1, stdout: err.stdout?.toString() || '', stderr: err.stderr?.toString() || err.message };
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
it('cursor-only injection does not write GEMINI.md', () => {
|
|
189
|
+
const root = mkdtempSync(join(tmpdir(), 'tf-inject-cli-cursor-'));
|
|
190
|
+
try {
|
|
191
|
+
const change = join(root, 'change');
|
|
192
|
+
mkdirSync(change, { recursive: true });
|
|
193
|
+
writeFileSync(join(change, '.team-flow.yaml'), 'state: exploring\nworkflow: full\nchange_name: inject-test\n');
|
|
194
|
+
const result = runInject(root, change, ['--platforms', 'cursor']);
|
|
195
|
+
assert.equal(result.exitCode, 0, result.stdout + result.stderr);
|
|
196
|
+
assert.equal(existsSync(join(root, '.cursor', 'rules', 'phase-guard.mdc')), true);
|
|
197
|
+
assert.equal(existsSync(join(root, 'GEMINI.md')), false);
|
|
198
|
+
} finally {
|
|
199
|
+
rmSync(root, { recursive: true, force: true });
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('omitted ambiguous platform exits before writing files', () => {
|
|
204
|
+
const root = mkdtempSync(join(tmpdir(), 'tf-inject-cli-none-'));
|
|
205
|
+
try {
|
|
206
|
+
const change = join(root, 'change');
|
|
207
|
+
mkdirSync(change, { recursive: true });
|
|
208
|
+
writeFileSync(join(change, '.team-flow.yaml'), 'state: exploring\nworkflow: full\nchange_name: inject-test\n');
|
|
209
|
+
const result = runInject(root, change);
|
|
210
|
+
assert.equal(result.exitCode, 2);
|
|
211
|
+
assert.match(result.stderr || result.stdout, /--platforms/);
|
|
212
|
+
assert.equal(existsSync(join(root, '.cursor')), false);
|
|
213
|
+
assert.equal(existsSync(join(root, 'GEMINI.md')), false);
|
|
214
|
+
} finally {
|
|
215
|
+
rmSync(root, { recursive: true, force: true });
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
});
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// tests/lib/cmd-install-workbuddy.test.mjs
|
|
2
|
+
// Tests for scripts/lib/cmd-install-workbuddy.mjs
|
|
3
|
+
import { describe, it, beforeEach, afterEach } from 'node:test';
|
|
4
|
+
import assert from 'node:assert/strict';
|
|
5
|
+
import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync, existsSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { tmpdir } from 'node:os';
|
|
8
|
+
|
|
9
|
+
let tempDir;
|
|
10
|
+
let planInstall, installWorkBuddy;
|
|
11
|
+
|
|
12
|
+
describe('cmd-install-workbuddy', () => {
|
|
13
|
+
beforeEach(async () => {
|
|
14
|
+
tempDir = mkdtempSync(join(tmpdir(), 'tf-workbuddy-'));
|
|
15
|
+
const mod = await import(join(process.cwd(), 'scripts/lib/cmd-install-workbuddy.mjs'));
|
|
16
|
+
planInstall = mod.planInstall;
|
|
17
|
+
installWorkBuddy = mod.installWorkBuddy;
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
if (tempDir) rmSync(tempDir, { recursive: true, force: true });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
function makePluginRoot() {
|
|
25
|
+
const pluginRoot = join(tempDir, 'team-flow');
|
|
26
|
+
const skillsDir = join(pluginRoot, 'skills');
|
|
27
|
+
mkdirSync(join(skillsDir, 'workflow-start'), { recursive: true });
|
|
28
|
+
mkdirSync(join(skillsDir, 'need-explorer'), { recursive: true });
|
|
29
|
+
writeFileSync(join(skillsDir, 'workflow-start', 'SKILL.md'), '---\nname: workflow-start\n---\n');
|
|
30
|
+
writeFileSync(join(skillsDir, 'need-explorer', 'SKILL.md'), '---\nname: need-explorer\n---\n');
|
|
31
|
+
writeFileSync(join(pluginRoot, 'package.json'), JSON.stringify({ version: '0.8.14' }));
|
|
32
|
+
return pluginRoot;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
it('plans WorkBuddy marketplace paths and single enabled plugin key', () => {
|
|
36
|
+
const pluginRoot = makePluginRoot();
|
|
37
|
+
const plan = planInstall({
|
|
38
|
+
pluginRoot,
|
|
39
|
+
homeDir: join(tempDir, 'home'),
|
|
40
|
+
marketplaceName: 'cb_teams_marketplace',
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
assert.deepEqual(plan.skillNames, ['need-explorer', 'workflow-start']);
|
|
44
|
+
assert.equal(
|
|
45
|
+
plan.pluginsDir,
|
|
46
|
+
join(tempDir, 'home', '.workbuddy', 'plugins', 'marketplaces', 'cb_teams_marketplace', 'plugins'),
|
|
47
|
+
);
|
|
48
|
+
// Single plugin key, not per-skill.
|
|
49
|
+
assert.equal(plan.enabledPluginKey, 'team-flow@cb_teams_marketplace');
|
|
50
|
+
// Plugin dir targets team-flow, not per-skill.
|
|
51
|
+
assert.equal(plan.targetPluginDir, join(plan.pluginsDir, 'team-flow'));
|
|
52
|
+
assert.equal(plan.targetSkills, join(plan.targetPluginDir, 'skills'));
|
|
53
|
+
assert.equal(plan.targetRules, join(plan.targetPluginDir, 'rules'));
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('deploys skills, runtime dirs, rules, manifest, and preserves existing settings', async () => {
|
|
57
|
+
const pluginRoot = makePluginRoot();
|
|
58
|
+
const homeDir = join(tempDir, 'home');
|
|
59
|
+
const settingsDir = join(homeDir, '.workbuddy');
|
|
60
|
+
mkdirSync(settingsDir, { recursive: true });
|
|
61
|
+
writeFileSync(
|
|
62
|
+
join(settingsDir, 'settings.json'),
|
|
63
|
+
JSON.stringify({ enabledPlugins: { 'existing@codebuddy-plugins-official': true } }, null, 2),
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// Create a scripts dir so RUNTIME_DIRS copy has something to copy.
|
|
67
|
+
mkdirSync(join(pluginRoot, 'scripts'), { recursive: true });
|
|
68
|
+
writeFileSync(join(pluginRoot, 'scripts', 'check-update.mjs'), '// test');
|
|
69
|
+
|
|
70
|
+
const result = await installWorkBuddy({ pluginRoot, homeDir, marketplaceName: 'cb_teams_marketplace' });
|
|
71
|
+
assert.equal(result.skillNames.length, 2);
|
|
72
|
+
|
|
73
|
+
// Settings: existing key preserved, single team-flow key added.
|
|
74
|
+
const settings = JSON.parse(readFileSync(join(settingsDir, 'settings.json'), 'utf-8'));
|
|
75
|
+
assert.equal(settings.enabledPlugins['existing@codebuddy-plugins-official'], true);
|
|
76
|
+
assert.equal(settings.enabledPlugins['team-flow@cb_teams_marketplace'], true);
|
|
77
|
+
// Old per-skill keys should NOT exist.
|
|
78
|
+
assert.equal(settings.enabledPlugins['workflow-start@cb_teams_marketplace'], undefined);
|
|
79
|
+
assert.equal(settings.enabledPlugins['need-explorer@cb_teams_marketplace'], undefined);
|
|
80
|
+
|
|
81
|
+
// Skills deployed under <plugin>/skills/<name>/SKILL.md
|
|
82
|
+
const pluginDir = join(homeDir, '.workbuddy', 'plugins', 'marketplaces', 'cb_teams_marketplace', 'plugins', 'team-flow');
|
|
83
|
+
assert.match(
|
|
84
|
+
readFileSync(join(pluginDir, 'skills', 'workflow-start', 'SKILL.md'), 'utf-8'),
|
|
85
|
+
/workflow-start/,
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
// Runtime dirs copied.
|
|
89
|
+
assert.ok(existsSync(join(pluginDir, 'scripts', 'check-update.mjs')));
|
|
90
|
+
|
|
91
|
+
// Phase-guard rule deployed.
|
|
92
|
+
assert.ok(existsSync(join(pluginDir, 'rules', 'phase-guard.md')));
|
|
93
|
+
|
|
94
|
+
// Plugin manifest deployed.
|
|
95
|
+
const manifest = JSON.parse(readFileSync(join(pluginDir, '.codebuddy-plugin', 'plugin.json'), 'utf-8'));
|
|
96
|
+
assert.equal(manifest.name, 'team-flow');
|
|
97
|
+
assert.equal(manifest.skills.length, 2);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('uses the package root by default instead of the caller cwd', () => {
|
|
101
|
+
const previousCwd = process.cwd();
|
|
102
|
+
process.chdir(tempDir);
|
|
103
|
+
try {
|
|
104
|
+
const plan = planInstall({ homeDir: join(tempDir, 'home') });
|
|
105
|
+
assert.equal(plan.skillNames.length, 23);
|
|
106
|
+
assert.equal(plan.skillsDir, join(plan.pluginRoot, 'skills'));
|
|
107
|
+
assert.ok(existsSync(plan.skillsDir));
|
|
108
|
+
assert.notEqual(plan.skillsDir, join(tempDir, 'skills'));
|
|
109
|
+
} finally {
|
|
110
|
+
process.chdir(previousCwd);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// tests/lib/cmd-install-zcode.test.mjs
|
|
2
|
+
// Regression for #29: `tf install-zcode` deploys skills to .zcode/ with the
|
|
3
|
+
// CLAUDE_PLUGIN_ROOT placeholder rewritten to an absolute path.
|
|
4
|
+
import { describe, it, before, after } from 'node:test';
|
|
5
|
+
import assert from 'node:assert/strict';
|
|
6
|
+
import { execSync } from 'node:child_process';
|
|
7
|
+
import { mkdtempSync, rmSync, existsSync, readFileSync } from 'node:fs';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
import { tmpdir } from 'node:os';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
import { dirname } from 'node:path';
|
|
12
|
+
|
|
13
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const ROOT = join(__dirname, '..', '..');
|
|
15
|
+
const CLI = join(ROOT, 'scripts', 'team-flow.mjs');
|
|
16
|
+
|
|
17
|
+
describe('BUG/#29: install-zcode deploys skills', () => {
|
|
18
|
+
let cwd;
|
|
19
|
+
before(() => { cwd = mkdtempSync(join(tmpdir(), 'tf-zcode-install-')); });
|
|
20
|
+
after(() => { if (existsSync(cwd)) rmSync(cwd, { recursive: true, force: true }); });
|
|
21
|
+
|
|
22
|
+
it('SHALL deploy skills to .zcode/skills/ with CLAUDE_PLUGIN_ROOT rewritten', () => {
|
|
23
|
+
execSync(`node ${CLI} install-zcode --local "${ROOT}"`, { cwd, stdio: 'pipe', timeout: 60000 });
|
|
24
|
+
const skillsDir = join(cwd, '.zcode', 'skills');
|
|
25
|
+
assert.equal(existsSync(skillsDir), true, '.zcode/skills should exist');
|
|
26
|
+
const wfStart = join(skillsDir, 'workflow-start', 'SKILL.md');
|
|
27
|
+
assert.equal(existsSync(wfStart), true, 'workflow-start skill should be deployed');
|
|
28
|
+
const content = readFileSync(wfStart, 'utf-8');
|
|
29
|
+
assert.equal(content.includes('${CLAUDE_PLUGIN_ROOT}'), false, 'CLAUDE_PLUGIN_ROOT should be rewritten to an absolute path');
|
|
30
|
+
assert.equal(existsSync(join(cwd, '.zcode', 'rules', 'phase-guard.mdc')), true, 'phase guard should be written');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('SHALL give contract-builder the portable execution-contract asset command', () => {
|
|
34
|
+
execSync(`node ${CLI} install-zcode --local "${ROOT}"`, { cwd, stdio: 'pipe', timeout: 60000 });
|
|
35
|
+
|
|
36
|
+
const templatePath = join(cwd, '.zcode', 'team-flow', 'templates', 'execution-contract.md');
|
|
37
|
+
const contractBuilder = readFileSync(join(cwd, '.zcode', 'skills', 'contract-builder', 'SKILL.md'), 'utf-8');
|
|
38
|
+
|
|
39
|
+
assert.equal(existsSync(templatePath), true, 'deployed template should exist');
|
|
40
|
+
assert.match(contractBuilder, /runtime asset read templates\/execution-contract\.md/);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Regression tests for #52: optional isolate names must not become the
|
|
2
|
+
// literal string "undefined" when cmd-isolate invokes ensure-branch.
|
|
3
|
+
import { afterEach, describe, it } from 'node:test';
|
|
4
|
+
import assert from 'node:assert/strict';
|
|
5
|
+
import { chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
6
|
+
import { spawnSync } from 'node:child_process';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { tmpdir } from 'node:os';
|
|
9
|
+
|
|
10
|
+
const ROOT = process.cwd();
|
|
11
|
+
const CLI = join(ROOT, 'scripts', 'team-flow.mjs');
|
|
12
|
+
const ENSURE = join(ROOT, 'scripts', 'ensure-branch.mjs');
|
|
13
|
+
const tempDirs = [];
|
|
14
|
+
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
while (tempDirs.length > 0) {
|
|
17
|
+
rmSync(tempDirs.pop(), { recursive: true, force: true });
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
function runIsolate(args) {
|
|
22
|
+
const tempDir = mkdtempSync(join(tmpdir(), 'tf-cmd-isolate-'));
|
|
23
|
+
tempDirs.push(tempDir);
|
|
24
|
+
const binDir = join(tempDir, 'bin');
|
|
25
|
+
const capture = join(tempDir, 'argv.txt');
|
|
26
|
+
const fakeNode = join(binDir, 'node');
|
|
27
|
+
|
|
28
|
+
mkdirSync(binDir);
|
|
29
|
+
writeFileSync(fakeNode, '#!/bin/sh\nprintf "%s\\n" "$@" > "$SSF_CAPTURE"\n', 'utf8');
|
|
30
|
+
chmodSync(fakeNode, 0o755);
|
|
31
|
+
|
|
32
|
+
const result = spawnSync(process.execPath, [CLI, 'isolate', ...args], {
|
|
33
|
+
encoding: 'utf8',
|
|
34
|
+
env: {
|
|
35
|
+
...process.env,
|
|
36
|
+
PATH: `${binDir}:${process.env.PATH}`,
|
|
37
|
+
SSF_CAPTURE: capture,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
result,
|
|
43
|
+
argv: readFileSync(capture, 'utf8').trimEnd().split('\n'),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
describe('cmd-isolate optional change name (#52)', () => {
|
|
48
|
+
it('omits the optional name from ensure-branch argv when the caller omits it', () => {
|
|
49
|
+
const { result, argv } = runIsolate(['/tmp/changes/demo']);
|
|
50
|
+
|
|
51
|
+
assert.equal(result.status, 0, result.stderr);
|
|
52
|
+
assert.deepEqual(argv, [ENSURE, '/tmp/changes/demo']);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('forwards an explicitly supplied name unchanged', () => {
|
|
56
|
+
const { result, argv } = runIsolate(['/tmp/changes/demo', 'runtime-fix']);
|
|
57
|
+
|
|
58
|
+
assert.equal(result.status, 0, result.stderr);
|
|
59
|
+
assert.deepEqual(argv, [ENSURE, '/tmp/changes/demo', 'runtime-fix']);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('forwards force without manufacturing an optional name', () => {
|
|
63
|
+
const { result, argv } = runIsolate(['/tmp/changes/demo', '--force']);
|
|
64
|
+
|
|
65
|
+
assert.equal(result.status, 0, result.stderr);
|
|
66
|
+
assert.deepEqual(argv, [ENSURE, '/tmp/changes/demo', '--force']);
|
|
67
|
+
});
|
|
68
|
+
});
|