@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,221 @@
|
|
|
1
|
+
// Evidence-based execution-mode recommendation for DP-4.
|
|
2
|
+
|
|
3
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { loadConfig } from './config-loader.mjs';
|
|
7
|
+
import { computeArtifactsHash, computeContractHash } from './hash.mjs';
|
|
8
|
+
import { getOverlayPaths } from './sdd-overlay.mjs';
|
|
9
|
+
import { readState } from './state-loader.mjs';
|
|
10
|
+
|
|
11
|
+
export function recommendExecutionModes({ workflow, taskCount, inlineThreshold, waves = [] }) {
|
|
12
|
+
const normalizedWorkflow = typeof workflow === 'string' ? workflow : null;
|
|
13
|
+
const normalizedTaskCount = Number.isInteger(taskCount) && taskCount >= 0 ? taskCount : null;
|
|
14
|
+
const threshold = Number.isInteger(inlineThreshold) && inlineThreshold > 0 ? inlineThreshold : 3;
|
|
15
|
+
const normalizedWaves = Array.isArray(waves) ? waves : [];
|
|
16
|
+
const plannedTaskCount = normalizedWaves.reduce((count, wave) => (
|
|
17
|
+
count + (Array.isArray(wave?.tasks) ? wave.tasks.length : 0)
|
|
18
|
+
), 0);
|
|
19
|
+
const hasParallelWave = normalizedWaves.some(wave => wave?.strategy === 'parallel');
|
|
20
|
+
const hasMultipleWaves = normalizedWaves.length > 1;
|
|
21
|
+
const facts = {
|
|
22
|
+
workflow: normalizedWorkflow,
|
|
23
|
+
documented_task_count: normalizedTaskCount,
|
|
24
|
+
planned_task_count: plannedTaskCount,
|
|
25
|
+
planned_wave_count: normalizedWaves.length,
|
|
26
|
+
has_parallel_wave: hasParallelWave,
|
|
27
|
+
inline_threshold: threshold,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
if (normalizedWorkflow === 'tweak') {
|
|
31
|
+
return result(['inline'], 'inline', [
|
|
32
|
+
'Tweak uses direct inline execution and is exempt from execution-plan and review-receipt gates.',
|
|
33
|
+
], facts);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (hasParallelWave) {
|
|
37
|
+
return result(['inline', 'batch-inline', 'sdd'], 'sdd', [
|
|
38
|
+
'The declared execution waves include parallel work, which SDD can dispatch and review independently.',
|
|
39
|
+
], facts);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (hasMultipleWaves) {
|
|
43
|
+
return result(['inline', 'batch-inline', 'sdd'], 'sdd', [
|
|
44
|
+
'The declared work spans multiple waves, so SDD keeps dependencies and review receipts explicit.',
|
|
45
|
+
], facts);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const effectiveTaskCount = plannedTaskCount || normalizedTaskCount;
|
|
49
|
+
if (effectiveTaskCount === 1) {
|
|
50
|
+
return result(['inline', 'batch-inline', 'sdd'], 'inline', [
|
|
51
|
+
'The change has a single sequential task, so inline execution keeps the work focused without dispatch overhead.',
|
|
52
|
+
], facts);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (effectiveTaskCount !== null && effectiveTaskCount > 1 && effectiveTaskCount <= threshold) {
|
|
56
|
+
return result(['inline', 'batch-inline', 'sdd'], 'batch-inline', [
|
|
57
|
+
`The ${effectiveTaskCount} sequential tasks are within the configured inline threshold of ${threshold}.`,
|
|
58
|
+
], facts);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return result(['inline', 'batch-inline', 'sdd'], 'sdd', [
|
|
62
|
+
effectiveTaskCount === null
|
|
63
|
+
? 'The task count cannot be determined, so SDD is recommended until the execution shape is made explicit.'
|
|
64
|
+
: `The ${effectiveTaskCount} tasks exceed the configured inline threshold of ${threshold}.`,
|
|
65
|
+
], facts);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function recommendExecutionModesForChange(changeDir, waves = []) {
|
|
69
|
+
const state = readState(changeDir);
|
|
70
|
+
const config = loadConfig(changeDir);
|
|
71
|
+
return recommendExecutionModes({
|
|
72
|
+
workflow: state.workflow,
|
|
73
|
+
taskCount: countDocumentedTasks(changeDir),
|
|
74
|
+
inlineThreshold: config.execution.inlineThreshold,
|
|
75
|
+
waves,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function createRecommendationReceipt(changeDir, waves = []) {
|
|
80
|
+
const state = readState(changeDir);
|
|
81
|
+
const receipt = {
|
|
82
|
+
recommendation: recommendExecutionModesForChange(changeDir, waves),
|
|
83
|
+
waves: normalizeWaves(waves),
|
|
84
|
+
artifacts_hash: computeArtifactsHash(changeDir),
|
|
85
|
+
contract_hash: computeContractHash(changeDir),
|
|
86
|
+
workflow: state.workflow,
|
|
87
|
+
execution_plan_revision_at_recommendation: state.execution_plan_revision ?? null,
|
|
88
|
+
created_at: new Date().toISOString(),
|
|
89
|
+
};
|
|
90
|
+
receipt.hash = hashReceipt(receipt);
|
|
91
|
+
return receipt;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function writeRecommendationReceipt(changeDir, receipt) {
|
|
95
|
+
const failures = validateRecommendationReceiptStructure(receipt);
|
|
96
|
+
if (failures.length > 0) throw new Error(`Invalid execution recommendation: ${failures.join('; ')}`);
|
|
97
|
+
const paths = getOverlayPaths(changeDir);
|
|
98
|
+
mkdirSync(paths.root, { recursive: true });
|
|
99
|
+
atomicWrite(paths.executionRecommendation, `${JSON.stringify(receipt, null, 2)}\n`);
|
|
100
|
+
return readRecommendationReceipt(changeDir);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function readRecommendationReceipt(changeDir) {
|
|
104
|
+
const filePath = getOverlayPaths(changeDir).executionRecommendation;
|
|
105
|
+
if (!existsSync(filePath)) return null;
|
|
106
|
+
try {
|
|
107
|
+
return JSON.parse(readFileSync(filePath, 'utf8'));
|
|
108
|
+
} catch (error) {
|
|
109
|
+
throw new Error(`Unable to read execution recommendation: ${error.message}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function readCurrentRecommendationReceipt(changeDir, waves = [], expectedPlanRevision = null) {
|
|
114
|
+
const receipt = readRecommendationReceipt(changeDir);
|
|
115
|
+
if (!receipt) {
|
|
116
|
+
throw new Error('Run "tf execution recommend" for these waves before confirming an execution plan');
|
|
117
|
+
}
|
|
118
|
+
const failures = validateRecommendationReceipt(changeDir, receipt, waves, expectedPlanRevision);
|
|
119
|
+
if (failures.length > 0) {
|
|
120
|
+
throw new Error(`Run "tf execution recommend" again before confirming an execution plan: ${failures.join('; ')}`);
|
|
121
|
+
}
|
|
122
|
+
return receipt;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function validateRecommendationReceipt(changeDir, receipt, waves = [], expectedPlanRevision = null) {
|
|
126
|
+
const failures = validateRecommendationReceiptStructure(receipt);
|
|
127
|
+
if (receipt?.artifacts_hash !== computeArtifactsHash(changeDir)) failures.push('recommendation artifacts hash is stale');
|
|
128
|
+
if (receipt?.contract_hash !== computeContractHash(changeDir)) failures.push('recommendation contract hash is stale');
|
|
129
|
+
if (receipt?.workflow !== readState(changeDir).workflow) failures.push('recommendation workflow does not match state');
|
|
130
|
+
if (stableJson(receipt?.waves) !== stableJson(normalizeWaves(waves))) failures.push('recommendation waves do not match the proposed execution plan');
|
|
131
|
+
if (receipt?.execution_plan_revision_at_recommendation !== expectedPlanRevision) {
|
|
132
|
+
failures.push('recommendation was not generated after the current execution plan revision');
|
|
133
|
+
}
|
|
134
|
+
return failures;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function countDocumentedTasks(changeDir) {
|
|
138
|
+
const tasksPath = join(changeDir, 'tasks.md');
|
|
139
|
+
if (!existsSync(tasksPath)) return null;
|
|
140
|
+
return (readFileSync(tasksPath, 'utf8').match(/^- \[[ xX]\] /gm) || []).length;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function result(availableModes, mode, reasons, facts) {
|
|
144
|
+
return {
|
|
145
|
+
available_modes: availableModes,
|
|
146
|
+
recommendation: { mode, reasons },
|
|
147
|
+
facts,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function validateRecommendationReceiptStructure(receipt) {
|
|
152
|
+
const failures = [];
|
|
153
|
+
if (!isObject(receipt)) return ['execution recommendation must be an object'];
|
|
154
|
+
if (!isObject(receipt.recommendation)) failures.push('execution recommendation payload is required');
|
|
155
|
+
else {
|
|
156
|
+
const recommendation = receipt.recommendation;
|
|
157
|
+
if (!Array.isArray(recommendation.available_modes) || recommendation.available_modes.some(mode => !['inline', 'batch-inline', 'sdd'].includes(mode))) {
|
|
158
|
+
failures.push('execution recommendation available modes are invalid');
|
|
159
|
+
}
|
|
160
|
+
if (!isObject(recommendation.recommendation) || !['inline', 'batch-inline', 'sdd'].includes(recommendation.recommendation.mode)) {
|
|
161
|
+
failures.push('execution recommendation mode is invalid');
|
|
162
|
+
}
|
|
163
|
+
if (!Array.isArray(recommendation.recommendation?.reasons) || recommendation.recommendation.reasons.some(reason => typeof reason !== 'string' || !reason.trim())) {
|
|
164
|
+
failures.push('execution recommendation reasons are invalid');
|
|
165
|
+
}
|
|
166
|
+
if (!isObject(recommendation.facts)) failures.push('execution recommendation facts are invalid');
|
|
167
|
+
}
|
|
168
|
+
if (!Array.isArray(receipt.waves)) failures.push('execution recommendation waves are invalid');
|
|
169
|
+
if (!isNullableHash(receipt.artifacts_hash)) failures.push('execution recommendation artifacts hash is invalid');
|
|
170
|
+
if (!isNullableHash(receipt.contract_hash)) failures.push('execution recommendation contract hash is invalid');
|
|
171
|
+
if (typeof receipt.workflow !== 'string' || !receipt.workflow.trim()) failures.push('execution recommendation workflow is invalid');
|
|
172
|
+
if (receipt.execution_plan_revision_at_recommendation !== null
|
|
173
|
+
&& (!Number.isInteger(receipt.execution_plan_revision_at_recommendation) || receipt.execution_plan_revision_at_recommendation < 1)) {
|
|
174
|
+
failures.push('execution recommendation plan revision is invalid');
|
|
175
|
+
}
|
|
176
|
+
if (typeof receipt.created_at !== 'string' || !receipt.created_at.trim()) failures.push('execution recommendation timestamp is invalid');
|
|
177
|
+
if (receipt.hash !== hashReceipt(receipt)) failures.push('execution recommendation hash mismatch');
|
|
178
|
+
return failures;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function normalizeWaves(waves) {
|
|
182
|
+
return Array.isArray(waves) ? waves.map(wave => ({
|
|
183
|
+
id: wave?.id,
|
|
184
|
+
strategy: wave?.strategy,
|
|
185
|
+
tasks: Array.isArray(wave?.tasks) ? [...wave.tasks] : wave?.tasks,
|
|
186
|
+
depends_on: Array.isArray(wave?.depends_on) ? [...wave.depends_on] : wave?.depends_on,
|
|
187
|
+
})) : [];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function hashReceipt(receipt) {
|
|
191
|
+
const { hash, ...content } = receipt || {};
|
|
192
|
+
return `sha256:${createHash('sha256').update(stableJson(content)).digest('hex')}`;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function stableJson(value, seen = new WeakSet()) {
|
|
196
|
+
if (value === null || typeof value !== 'object') return JSON.stringify(value);
|
|
197
|
+
if (seen.has(value)) throw new Error('circular recommendation data');
|
|
198
|
+
seen.add(value);
|
|
199
|
+
if (Array.isArray(value)) {
|
|
200
|
+
const result = `[${value.map(item => stableJson(item, seen)).join(',')}]`;
|
|
201
|
+
seen.delete(value);
|
|
202
|
+
return result;
|
|
203
|
+
}
|
|
204
|
+
const result = `{${Object.keys(value).sort().map(key => `${JSON.stringify(key)}:${stableJson(value[key], seen)}`).join(',')}}`;
|
|
205
|
+
seen.delete(value);
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function atomicWrite(targetPath, content) {
|
|
210
|
+
const tempPath = `${targetPath}.tmp-${process.pid}-${randomUUID()}`;
|
|
211
|
+
writeFileSync(tempPath, content, 'utf8');
|
|
212
|
+
renameSync(tempPath, targetPath);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function isObject(value) {
|
|
216
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function isNullableHash(value) {
|
|
220
|
+
return value === null || (typeof value === 'string' && value.startsWith('sha256:'));
|
|
221
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// scripts/lib/hash.mjs — SHA256 artifact hashing for fast staleness detection
|
|
2
|
+
import crypto from 'node:crypto';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { findCanonicalSpecFiles } from './spec-paths.mjs';
|
|
6
|
+
|
|
7
|
+
// Compute a joint SHA256 hash of all 4 planning artifacts.
|
|
8
|
+
// Input: proposal.md + specs/*/spec.md (sorted) + design.md + tasks.md
|
|
9
|
+
export function computeArtifactsHash(changeDir) {
|
|
10
|
+
const hash = crypto.createHash('sha256');
|
|
11
|
+
let hasContent = false;
|
|
12
|
+
|
|
13
|
+
// proposal.md
|
|
14
|
+
const proposal = path.join(changeDir, 'proposal.md');
|
|
15
|
+
if (fs.existsSync(proposal)) {
|
|
16
|
+
hash.update(fs.readFileSync(proposal, 'utf-8'));
|
|
17
|
+
hasContent = true;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// specs/*/spec.md (sorted for deterministic output)
|
|
21
|
+
for (const f of findCanonicalSpecFiles(changeDir)) {
|
|
22
|
+
hash.update(fs.readFileSync(f, 'utf-8'));
|
|
23
|
+
hasContent = true;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// design.md
|
|
27
|
+
const design = path.join(changeDir, 'design.md');
|
|
28
|
+
if (fs.existsSync(design)) {
|
|
29
|
+
hash.update(fs.readFileSync(design, 'utf-8'));
|
|
30
|
+
hasContent = true;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// tasks.md
|
|
34
|
+
const tasks = path.join(changeDir, 'tasks.md');
|
|
35
|
+
if (fs.existsSync(tasks)) {
|
|
36
|
+
hash.update(fs.readFileSync(tasks, 'utf-8'));
|
|
37
|
+
hasContent = true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return hasContent ? `sha256:${hash.digest('hex')}` : null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Compute SHA256 hash of execution-contract.md alone.
|
|
44
|
+
export function computeContractHash(changeDir) {
|
|
45
|
+
const contract = path.join(changeDir, 'execution-contract.md');
|
|
46
|
+
if (!fs.existsSync(contract)) return null;
|
|
47
|
+
const hash = crypto.createHash('sha256');
|
|
48
|
+
hash.update(fs.readFileSync(contract, 'utf-8'));
|
|
49
|
+
return `sha256:${hash.digest('hex')}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Fast staleness check: compare stored artifacts_hash against current.
|
|
53
|
+
export function isContractFresh(changeDir, stateLoader) {
|
|
54
|
+
const stateFile = path.join(changeDir, '.team-flow.yaml');
|
|
55
|
+
if (!fs.existsSync(stateFile)) return false;
|
|
56
|
+
|
|
57
|
+
const raw = fs.readFileSync(stateFile, 'utf-8');
|
|
58
|
+
const storedHash = extractYamlField(raw, 'artifacts_hash');
|
|
59
|
+
if (!storedHash || storedHash === 'null') return false;
|
|
60
|
+
|
|
61
|
+
const currentHash = computeArtifactsHash(changeDir);
|
|
62
|
+
return storedHash === currentHash;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function extractYamlField(content, field) {
|
|
66
|
+
for (const line of content.split('\n')) {
|
|
67
|
+
const match = line.match(new RegExp(`^${field}:\\s*(.*)`));
|
|
68
|
+
if (match) {
|
|
69
|
+
const val = match[1].trim();
|
|
70
|
+
return val === 'null' || val === '' ? null : val;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
// scripts/lib/install.mjs
|
|
2
|
+
// Shared installer for team-flow cross-platform deployment.
|
|
3
|
+
//
|
|
4
|
+
// Mirrors the proven logic of scripts/install-cursor.mjs, parameterized by a
|
|
5
|
+
// PlatformConfig from ./platforms.mjs. For each platform it:
|
|
6
|
+
// 1. Resolves the plugin source (latest GitHub release or --local <path>).
|
|
7
|
+
// 2. Copies RUNTIME_DIRS (scripts/docs/templates/dist/hooks) to
|
|
8
|
+
// <skillsDir>/team-flow/ so local runtime commands resolve.
|
|
9
|
+
// 3. Copies skills/ to <skillsDir>/skills/ with portable package commands
|
|
10
|
+
// rewritten to the bundled runtime.
|
|
11
|
+
// 4. Writes a phase-guard rule file to the platform's rules directory
|
|
12
|
+
// (when the platform has one) so the workflow entry rule is auto-included.
|
|
13
|
+
//
|
|
14
|
+
// team-flow's guard is the phase-guard RULE (auto-included by the
|
|
15
|
+
// platform), not a PreToolUse hook; no hook configs are written here.
|
|
16
|
+
|
|
17
|
+
import { existsSync, mkdirSync, readdirSync, statSync, rmSync, readFileSync, writeFileSync } from 'node:fs';
|
|
18
|
+
import { cp, writeFile, mkdtemp } from 'node:fs/promises';
|
|
19
|
+
import { dirname, join, resolve } from 'node:path';
|
|
20
|
+
import { fileURLToPath } from 'node:url';
|
|
21
|
+
import { execFileSync } from 'node:child_process';
|
|
22
|
+
import { parseArgs } from 'node:util';
|
|
23
|
+
import { getPlatform, rulesTargetDir, phaseGuardFileName } from './platforms.mjs';
|
|
24
|
+
import { shellQuote } from './shell-quote.mjs';
|
|
25
|
+
|
|
26
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
27
|
+
const defaultPluginRoot = resolve(__dirname, '..', '..'); // repo root when run from clone
|
|
28
|
+
|
|
29
|
+
const GITHUB_REPO = 'MageByte-Zero/team-flow';
|
|
30
|
+
const GITHUB_API_URL = `https://api.github.com/repos/${GITHUB_REPO}/releases/latest`;
|
|
31
|
+
|
|
32
|
+
// Directories needed by local installer-rewritten runtime commands.
|
|
33
|
+
const RUNTIME_DIRS = ['scripts', 'docs', 'templates', 'dist', 'hooks'];
|
|
34
|
+
|
|
35
|
+
function ensureDir(dir) {
|
|
36
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function fetchLatestTag() {
|
|
40
|
+
const res = await fetch(GITHUB_API_URL);
|
|
41
|
+
if (!res.ok) {
|
|
42
|
+
throw new Error(`GitHub API failed: ${res.status} ${res.statusText}`);
|
|
43
|
+
}
|
|
44
|
+
const data = await res.json();
|
|
45
|
+
if (!data.tag_name) {
|
|
46
|
+
throw new Error('GitHub API response missing tag_name');
|
|
47
|
+
}
|
|
48
|
+
return data.tag_name;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function cloneRelease(tag) {
|
|
52
|
+
const tmpDir = await mkdtemp(join('/tmp', 'team-flow-'));
|
|
53
|
+
const url = `https://github.com/${GITHUB_REPO}.git`;
|
|
54
|
+
console.log(`📥 Cloning ${tag} into ${tmpDir} ...`);
|
|
55
|
+
execFileSync('git', ['clone', '--depth', '1', '--branch', tag, url, tmpDir], {
|
|
56
|
+
stdio: 'inherit',
|
|
57
|
+
});
|
|
58
|
+
return tmpDir;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Recursively copy a directory; returns the number of top-level entries. */
|
|
62
|
+
async function copyDir(src, dst) {
|
|
63
|
+
if (!existsSync(src)) return 0;
|
|
64
|
+
ensureDir(dst);
|
|
65
|
+
const entries = readdirSync(src);
|
|
66
|
+
for (const name of entries) {
|
|
67
|
+
const srcPath = join(src, name);
|
|
68
|
+
const dstPath = join(dst, name);
|
|
69
|
+
const st = statSync(srcPath);
|
|
70
|
+
if (st.isDirectory()) {
|
|
71
|
+
await copyDir(srcPath, dstPath);
|
|
72
|
+
} else {
|
|
73
|
+
await cp(srcPath, dstPath, { force: true });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return entries.length;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Copy skills to target, retaining legacy root replacement and rewriting the
|
|
81
|
+
* portable package command to the target's bundled runtime.
|
|
82
|
+
*/
|
|
83
|
+
async function copySkillsWithRoot(sourceSkills, targetSkills, pluginRootAbs) {
|
|
84
|
+
// Clean old skill directories before copying.
|
|
85
|
+
if (existsSync(targetSkills)) {
|
|
86
|
+
const oldEntries = readdirSync(targetSkills).filter(name => {
|
|
87
|
+
const full = join(targetSkills, name);
|
|
88
|
+
try { return statSync(full).isDirectory(); } catch { return false; }
|
|
89
|
+
});
|
|
90
|
+
for (const name of oldEntries) {
|
|
91
|
+
rmSync(join(targetSkills, name), { recursive: true, force: true });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
ensureDir(targetSkills);
|
|
95
|
+
const entries = readdirSync(sourceSkills).filter(name => {
|
|
96
|
+
const full = join(sourceSkills, name);
|
|
97
|
+
try { return statSync(full).isDirectory(); } catch { return false; }
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
function rewriteRoot(filePath) {
|
|
101
|
+
if (!existsSync(filePath)) return;
|
|
102
|
+
let content = readFileSync(filePath, 'utf-8');
|
|
103
|
+
if (content.includes('${CLAUDE_PLUGIN_ROOT}')) {
|
|
104
|
+
content = content.replace(/\$\{CLAUDE_PLUGIN_ROOT\}/g, pluginRootAbs);
|
|
105
|
+
}
|
|
106
|
+
content = content.replace(
|
|
107
|
+
/npx --yes --package @xulthekl\/team-flow@\d+\.\d+\.\d+ tf/g,
|
|
108
|
+
`node ${shellQuote(join(pluginRootAbs, 'scripts', 'team-flow.mjs'))}`,
|
|
109
|
+
);
|
|
110
|
+
writeFileSync(filePath, content, 'utf-8');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
for (const name of entries) {
|
|
114
|
+
const src = join(sourceSkills, name);
|
|
115
|
+
const dst = join(targetSkills, name);
|
|
116
|
+
await cp(src, dst, { recursive: true, force: true });
|
|
117
|
+
rewriteRoot(join(dst, 'SKILL.md'));
|
|
118
|
+
// Also fix sub-prompt / reference markdown files in the skill dir.
|
|
119
|
+
for (const sub of readdirSync(dst).filter(f => f.endsWith('.md') && f !== 'SKILL.md')) {
|
|
120
|
+
rewriteRoot(join(dst, sub));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return entries.length;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Phase-guard rule content. `mdc` gets Cursor frontmatter; `md`/`copilot` stay plain. */
|
|
127
|
+
function phaseGuardContent(rulesFormat, platformId) {
|
|
128
|
+
const body = `# Phase Guard — team-flow (${platformId})
|
|
129
|
+
|
|
130
|
+
## 入口规则
|
|
131
|
+
|
|
132
|
+
- 所有工作必须从 "/workflow-start" 入口开始。
|
|
133
|
+
- 在 .team-flow.yaml 中确认当前 state 和 workflow 模式之前,不要开始写代码。
|
|
134
|
+
|
|
135
|
+
## 全局禁止
|
|
136
|
+
|
|
137
|
+
- 没有 execution-contract.md 或未经用户明确批准,不得进入实现。
|
|
138
|
+
- full/hotfix 必须先运行 tf execution plan <change-dir> ...;没有 current execution plan 不得开始实现。
|
|
139
|
+
- 只有 all pass review receipts 后才可 closing;不得把未审查的 wave 当作完成。
|
|
140
|
+
- 上述 plan/receipt gates 仅适用于 full/hotfix;tweak 免除这些 gates (tweak exempt)。
|
|
141
|
+
- 执行过程中如果发现需求/范围变化,必须回退到 specifying 或 bridging,而不是直接改代码。
|
|
142
|
+
- 不要直接调用执行类 skill(如 "/build-executor"),必须通过入口路由。
|
|
143
|
+
|
|
144
|
+
## 决策点协议
|
|
145
|
+
|
|
146
|
+
- DP-0:设计前确认
|
|
147
|
+
- DP-1:需求确认
|
|
148
|
+
- DP-2:工件审查
|
|
149
|
+
- DP-3:是否批准 execution contract?
|
|
150
|
+
- DP-4:先运行 tf execution recommend,展示可用模式与推荐;用户用 --confirm 确认,非推荐选择额外使用 --acknowledge-recommendation
|
|
151
|
+
- DP-5:调试升级
|
|
152
|
+
- DP-6:验证失败
|
|
153
|
+
- DP-7:是否收口归档?
|
|
154
|
+
|
|
155
|
+
> 本文件由 team-flow 安装脚本生成(platform: ${platformId});
|
|
156
|
+
> 对具体变更的 guard 内容请运行 \`tf inject <change-dir>\` 更新。
|
|
157
|
+
`;
|
|
158
|
+
if (rulesFormat === 'mdc') {
|
|
159
|
+
return `---
|
|
160
|
+
description: team-flow phase guard — 防止阶段漂移和未授权实现
|
|
161
|
+
alwaysApply: true
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
${body}`;
|
|
165
|
+
}
|
|
166
|
+
return body;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Install team-flow for a platform.
|
|
171
|
+
*
|
|
172
|
+
* @param {string} platformId - one of NEW_PLATFORM_IDS
|
|
173
|
+
* @param {Object} [opts]
|
|
174
|
+
* @param {string} [opts.local] - deploy from a local repo path instead of release
|
|
175
|
+
* @param {string} [opts.tag] - specific release tag to install
|
|
176
|
+
* @param {string} [opts.cwd] - target project root (defaults to process.cwd())
|
|
177
|
+
*/
|
|
178
|
+
export async function installPlatform(platformId, opts = {}) {
|
|
179
|
+
const platform = getPlatform(platformId);
|
|
180
|
+
const { values } = parseArgs({
|
|
181
|
+
options: {
|
|
182
|
+
local: { type: 'string' },
|
|
183
|
+
tag: { type: 'string' },
|
|
184
|
+
},
|
|
185
|
+
// No `args` => parseArgs reads process.argv.slice(2), so `--local`/`--tag`
|
|
186
|
+
// passed on the CLI (or forwarded by cmd-install-<id>.mjs) are honored.
|
|
187
|
+
});
|
|
188
|
+
const local = opts.local ?? values.local;
|
|
189
|
+
const tag = opts.tag ?? values.tag;
|
|
190
|
+
|
|
191
|
+
const targetRoot = opts.cwd || process.cwd();
|
|
192
|
+
|
|
193
|
+
let pluginRoot = defaultPluginRoot;
|
|
194
|
+
let isTemp = false;
|
|
195
|
+
let installedTag = null;
|
|
196
|
+
|
|
197
|
+
if (local) {
|
|
198
|
+
pluginRoot = resolve(local);
|
|
199
|
+
console.log(`📁 Using local repo: ${pluginRoot}`);
|
|
200
|
+
} else {
|
|
201
|
+
installedTag = tag || await fetchLatestTag();
|
|
202
|
+
console.log(`⬆️ Installing team-flow ${installedTag} for ${platform.name} ...`);
|
|
203
|
+
pluginRoot = await cloneRelease(installedTag);
|
|
204
|
+
isTemp = true;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const sourceSkills = join(pluginRoot, 'skills');
|
|
208
|
+
if (!existsSync(sourceSkills)) {
|
|
209
|
+
console.error(`❌ skills/ directory not found at ${pluginRoot}`);
|
|
210
|
+
if (isTemp) rmSync(pluginRoot, { recursive: true, force: true });
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Target paths (project-local scope).
|
|
215
|
+
const targetPluginDir = join(targetRoot, platform.skillsDir, 'team-flow');
|
|
216
|
+
const targetSkills = join(targetRoot, platform.skillsDir, 'skills');
|
|
217
|
+
const pluginRootAbs = resolve(targetPluginDir);
|
|
218
|
+
|
|
219
|
+
try {
|
|
220
|
+
// 1. Copy runtime dependencies to <skillsDir>/team-flow/
|
|
221
|
+
console.log('📋 Copying runtime dependencies...');
|
|
222
|
+
for (const dir of RUNTIME_DIRS) {
|
|
223
|
+
const src = join(pluginRoot, dir);
|
|
224
|
+
const dst = join(targetPluginDir, dir);
|
|
225
|
+
if (existsSync(src)) {
|
|
226
|
+
const count = await copyDir(src, dst);
|
|
227
|
+
console.log(` ${dir}/ → ${dst} (${count} entries)`);
|
|
228
|
+
} else {
|
|
229
|
+
console.log(` ${dir}/ — skipped (not found)`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// 2. Copy skills to <skillsDir>/skills/ with portable commands rewritten
|
|
234
|
+
const count = await copySkillsWithRoot(sourceSkills, targetSkills, pluginRootAbs);
|
|
235
|
+
console.log(` skills/ → ${targetSkills} (${count} skills, paths rewritten)`);
|
|
236
|
+
|
|
237
|
+
// 3. Write phase-guard rule (when the platform has a rules directory)
|
|
238
|
+
const rulesDir = rulesTargetDir(platform, targetRoot);
|
|
239
|
+
if (rulesDir) {
|
|
240
|
+
ensureDir(rulesDir);
|
|
241
|
+
const fileName = phaseGuardFileName(platform.rulesFormat);
|
|
242
|
+
const ruleFile = join(rulesDir, fileName);
|
|
243
|
+
await writeFile(ruleFile, phaseGuardContent(platform.rulesFormat, platformId), 'utf-8');
|
|
244
|
+
console.log(` phase guard → ${ruleFile}`);
|
|
245
|
+
} else {
|
|
246
|
+
console.log(` phase guard — skipped (${platform.name} has no rules directory)`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
console.log(`\n✅ ${platform.name} install complete:`);
|
|
250
|
+
console.log(` Plugin root: ${pluginRootAbs}`);
|
|
251
|
+
if (installedTag) {
|
|
252
|
+
console.log(` Version: ${installedTag}`);
|
|
253
|
+
}
|
|
254
|
+
if (rulesDir) {
|
|
255
|
+
console.log(`\nNext: the phase-guard rule is auto-included. Try "/workflow-start".`);
|
|
256
|
+
} else {
|
|
257
|
+
console.log(`\nNext: ${platform.name} has no rules dir — invoke "/workflow-start" manually to start.`);
|
|
258
|
+
}
|
|
259
|
+
if (platform.notes) {
|
|
260
|
+
console.log(` Note: ${platform.notes}`);
|
|
261
|
+
}
|
|
262
|
+
} finally {
|
|
263
|
+
if (isTemp) {
|
|
264
|
+
rmSync(pluginRoot, { recursive: true, force: true });
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Runtime distribution contract for every platform claimed in the public docs.
|
|
2
|
+
// Modes are intentionally host-agnostic: tests validate the final artifact,
|
|
3
|
+
// rather than assuming that every loader injects a plugin-root environment.
|
|
4
|
+
export const PLATFORM_RUNTIME_INVENTORY = [
|
|
5
|
+
{ id: 'claude-code', modes: ['native-root', 'canonical-fallback'] },
|
|
6
|
+
{ id: 'cursor', modes: ['native-root', 'installer-rewrite'] },
|
|
7
|
+
{ id: 'codex-cli', modes: ['native-root', 'canonical-fallback'] },
|
|
8
|
+
{ id: 'codex-app', modes: ['native-root', 'canonical-fallback'] },
|
|
9
|
+
{ id: 'copilot-cli', modes: ['native-root', 'canonical-fallback'] },
|
|
10
|
+
{ id: 'gemini-cli', modes: ['native-root', 'canonical-fallback'] },
|
|
11
|
+
{ id: 'opencode', modes: ['canonical-fallback'] },
|
|
12
|
+
{ id: 'workbuddy', modes: ['native-root', 'installer-rewrite'] },
|
|
13
|
+
{ id: 'trae', modes: ['native-root', 'canonical-fallback'] },
|
|
14
|
+
{ id: 'cline', modes: ['installer-rewrite'] },
|
|
15
|
+
{ id: 'kiro', modes: ['installer-rewrite'] },
|
|
16
|
+
{ id: 'windsurf', modes: ['installer-rewrite'] },
|
|
17
|
+
{ id: 'qwen', modes: ['installer-rewrite'] },
|
|
18
|
+
{ id: 'amazon-q', modes: ['installer-rewrite'] },
|
|
19
|
+
{ id: 'roocode', modes: ['installer-rewrite'] },
|
|
20
|
+
{ id: 'continue', modes: ['installer-rewrite'] },
|
|
21
|
+
{ id: 'pi', modes: ['installer-rewrite'] },
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
export const ZCODE_COMPATIBILITY_PATH = {
|
|
25
|
+
id: 'zcode',
|
|
26
|
+
modes: ['installer-rewrite'],
|
|
27
|
+
};
|