@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,42 @@
|
|
|
1
|
+
// Raw-package smoke: canonical skills must work without host plugin-root variables
|
|
2
|
+
// or a globally installed `tf` binary.
|
|
3
|
+
import { describe, it } from 'node:test';
|
|
4
|
+
import assert from 'node:assert/strict';
|
|
5
|
+
import { mkdtempSync, readFileSync, rmSync } 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
|
+
|
|
12
|
+
function run(command, args, options) {
|
|
13
|
+
const result = spawnSync(command, args, { encoding: 'utf8', ...options });
|
|
14
|
+
assert.equal(result.status, 0, `${command} ${args.join(' ')}\n${result.stderr}`);
|
|
15
|
+
return result.stdout;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe('raw-package runtime smoke', () => {
|
|
19
|
+
it('runs a packed canonical runtime with no plugin-root variables or global tf', () => {
|
|
20
|
+
const pkg = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf8'));
|
|
21
|
+
assert.equal(pkg.scripts['test:raw-mode'], 'node --test tests/lib/raw-mode-smoke.test.mjs');
|
|
22
|
+
|
|
23
|
+
const tempRoot = mkdtempSync(join(tmpdir(), 'tf-raw-mode-'));
|
|
24
|
+
try {
|
|
25
|
+
const packJson = run('npm', ['pack', '--json', '--pack-destination', tempRoot], { cwd: ROOT });
|
|
26
|
+
const [{ filename }] = JSON.parse(packJson);
|
|
27
|
+
const env = { ...process.env };
|
|
28
|
+
delete env.CLAUDE_PLUGIN_ROOT;
|
|
29
|
+
delete env.PLUGIN_ROOT;
|
|
30
|
+
delete env.CODEX_HOME;
|
|
31
|
+
|
|
32
|
+
const output = run('npx', [
|
|
33
|
+
'--yes', '--package', join(tempRoot, filename), 'tf',
|
|
34
|
+
'runtime', 'asset', 'read', 'docs/state-machine.md',
|
|
35
|
+
], { cwd: tempRoot, env });
|
|
36
|
+
|
|
37
|
+
assert.match(output, /State Machine/);
|
|
38
|
+
} finally {
|
|
39
|
+
rmSync(tempRoot, { recursive: true, force: true });
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { describe, it, beforeEach, afterEach } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { tmpdir } from 'node:os';
|
|
6
|
+
import {
|
|
7
|
+
saveCheckpoint, listCheckpoints, createHandoff, listHandoffs, finishHandoff, resolveHandoff,
|
|
8
|
+
} from '../../scripts/lib/sdd-overlay.mjs';
|
|
9
|
+
|
|
10
|
+
let changeDir;
|
|
11
|
+
|
|
12
|
+
const validHandoffInput = {
|
|
13
|
+
type: 'research',
|
|
14
|
+
title: 'Research findings',
|
|
15
|
+
question: 'Which approach should the implementation use?',
|
|
16
|
+
context: 'Compare the available implementation approaches.',
|
|
17
|
+
source: 'Local repository inspection',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
changeDir = mkdtempSync(join(tmpdir(), 'sdd-overlay-'));
|
|
22
|
+
writeFileSync(join(changeDir, 'tasks.md'), '# Tasks\n\n- [ ] 1.1 Original task text\n');
|
|
23
|
+
writeFileSync(join(changeDir, 'proposal.md'), '# Proposal\n\nA valid proposal.\n');
|
|
24
|
+
writeFileSync(join(changeDir, 'design.md'), '# Design\n\nA valid design.\n');
|
|
25
|
+
writeFileSync(join(changeDir, '.team-flow.yaml'), 'state: executing\nworkflow: auto\n');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
rmSync(changeDir, { recursive: true, force: true });
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('checkpoint storage', () => {
|
|
33
|
+
it('persists commit boundaries and recovery fields across list and show reads', () => {
|
|
34
|
+
saveCheckpoint(changeDir, {
|
|
35
|
+
taskId: '1.1',
|
|
36
|
+
next: 'Run the focused test',
|
|
37
|
+
completed: 'Added parser tests',
|
|
38
|
+
evidence: 'tests/lib/sdd-overlay.test.mjs',
|
|
39
|
+
review: 'review.md',
|
|
40
|
+
risk: 'None',
|
|
41
|
+
commitStart: 'aaaaaaa',
|
|
42
|
+
commitEnd: 'bbbbbbb',
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const checkpoint = listCheckpoints(changeDir)[0];
|
|
46
|
+
assert.equal(checkpoint.task_id, '1.1');
|
|
47
|
+
assert.equal(checkpoint.next, 'Run the focused test');
|
|
48
|
+
assert.equal(checkpoint.completed, 'Added parser tests');
|
|
49
|
+
assert.equal(checkpoint.evidence, 'tests/lib/sdd-overlay.test.mjs');
|
|
50
|
+
assert.equal(checkpoint.review, 'review.md');
|
|
51
|
+
assert.equal(checkpoint.risk, 'None');
|
|
52
|
+
assert.equal(checkpoint.commit_start, 'aaaaaaa');
|
|
53
|
+
assert.equal(checkpoint.commit_end, 'bbbbbbb');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('marks a checkpoint stale when its numbered task line changes', () => {
|
|
57
|
+
saveCheckpoint(changeDir, { taskId: '1.1', next: 'Run the focused test' });
|
|
58
|
+
writeFileSync(join(changeDir, 'tasks.md'), '# Tasks\n\n- [ ] 1.1 Changed task text\n');
|
|
59
|
+
|
|
60
|
+
assert.equal(listCheckpoints(changeDir)[0].stale, true);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('marks a checkpoint stale when its task is removed', () => {
|
|
64
|
+
saveCheckpoint(changeDir, { taskId: '1.1', next: 'Run the focused test' });
|
|
65
|
+
writeFileSync(join(changeDir, 'tasks.md'), '# Tasks\n');
|
|
66
|
+
|
|
67
|
+
assert.equal(listCheckpoints(changeDir)[0].stale, true);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('handoff storage', () => {
|
|
72
|
+
it('creates HANDOFF.md and HANDOFF_RESULT.md and validates the latter on finish', () => {
|
|
73
|
+
const handoff = createHandoff(changeDir, validHandoffInput);
|
|
74
|
+
const handoffPath = join(handoff.directory, 'HANDOFF.md');
|
|
75
|
+
const resultPath = join(handoff.directory, 'HANDOFF_RESULT.md');
|
|
76
|
+
|
|
77
|
+
assert.equal(existsSync(handoffPath), true);
|
|
78
|
+
assert.equal(existsSync(resultPath), true);
|
|
79
|
+
writeFileSync(resultPath, validResult());
|
|
80
|
+
assert.equal(finishHandoff(changeDir, handoff.id).status, 'result-ready');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('keeps a handoff active when finish validation fails', () => {
|
|
84
|
+
const handoff = createHandoff(changeDir, validHandoffInput);
|
|
85
|
+
|
|
86
|
+
assert.throws(() => finishHandoff(changeDir, handoff.id), /Conclusion/);
|
|
87
|
+
assert.equal(listHandoffs(changeDir)[0].status, 'active');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('requires explicit drift acknowledgement before resolving', () => {
|
|
91
|
+
const handoff = createReadyHandoff();
|
|
92
|
+
writeFileSync(join(changeDir, 'design.md'), '# Changed design\n');
|
|
93
|
+
|
|
94
|
+
assert.throws(
|
|
95
|
+
() => resolveHandoff(changeDir, handoff.id, 'accept', false),
|
|
96
|
+
/acknowledge-source-drift/,
|
|
97
|
+
);
|
|
98
|
+
assert.equal(resolveHandoff(changeDir, handoff.id, 'accept', true).status, 'resolved');
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
function createReadyHandoff() {
|
|
103
|
+
const handoff = createHandoff(changeDir, validHandoffInput);
|
|
104
|
+
const resultPath = join(changeDir, '.superpowers', 'sdd', 'handoffs', handoff.id, 'HANDOFF_RESULT.md');
|
|
105
|
+
writeFileSync(resultPath, validResult());
|
|
106
|
+
return finishHandoff(changeDir, handoff.id);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function validResult() {
|
|
110
|
+
return [
|
|
111
|
+
'## Conclusion\nThe research is complete.',
|
|
112
|
+
'## Evidence\nRepository evidence was reviewed.',
|
|
113
|
+
'## Produced Artifacts\nThe handoff record was produced.',
|
|
114
|
+
'## Risks\nNo additional risks were found.',
|
|
115
|
+
'## Suggested Changes\nProceed with the selected approach.',
|
|
116
|
+
'',
|
|
117
|
+
].join('\n');
|
|
118
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { describe, it, before, after } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { tmpdir } from 'node:os';
|
|
6
|
+
|
|
7
|
+
let mod;
|
|
8
|
+
let tempDir;
|
|
9
|
+
|
|
10
|
+
describe('spec-paths', () => {
|
|
11
|
+
before(async () => {
|
|
12
|
+
tempDir = mkdtempSync(join(tmpdir(), 'tf-spec-paths-'));
|
|
13
|
+
mod = await import(join(process.cwd(), 'scripts/lib/spec-paths.mjs'));
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
after(() => {
|
|
17
|
+
if (tempDir) rmSync(tempDir, { recursive: true, force: true });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('finds canonical specs in sorted order', () => {
|
|
21
|
+
const dir = mkdtempSync(join(tempDir, 'canonical-'));
|
|
22
|
+
mkdirSync(join(dir, 'specs', 'z-auth'), { recursive: true });
|
|
23
|
+
mkdirSync(join(dir, 'specs', 'a-ui'), { recursive: true });
|
|
24
|
+
writeFileSync(join(dir, 'specs', 'z-auth', 'spec.md'), 'z');
|
|
25
|
+
writeFileSync(join(dir, 'specs', 'a-ui', 'spec.md'), 'a');
|
|
26
|
+
|
|
27
|
+
const files = mod.findCanonicalSpecFiles(dir).map(f => f.replace(dir + '/', ''));
|
|
28
|
+
assert.deepEqual(files, ['specs/a-ui/spec.md', 'specs/z-auth/spec.md']);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('ignores nested spec.md files when finding canonical specs', () => {
|
|
32
|
+
const dir = mkdtempSync(join(tempDir, 'nested-'));
|
|
33
|
+
mkdirSync(join(dir, 'specs', 'ui-theme', 'nested'), { recursive: true });
|
|
34
|
+
writeFileSync(join(dir, 'specs', 'ui-theme', 'nested', 'spec.md'), 'nested');
|
|
35
|
+
|
|
36
|
+
assert.deepEqual(mod.findCanonicalSpecFiles(dir), []);
|
|
37
|
+
|
|
38
|
+
const result = mod.validateSpecPathLayout(dir, { requireSpecs: true });
|
|
39
|
+
assert.equal(result.pass, false);
|
|
40
|
+
assert.deepEqual(result.specFiles, []);
|
|
41
|
+
assert.ok(result.failures.some(f => f.includes('No canonical spec files found')));
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('reports flat capability specs with expected canonical path', () => {
|
|
45
|
+
const dir = mkdtempSync(join(tempDir, 'flat-'));
|
|
46
|
+
mkdirSync(join(dir, 'specs'), { recursive: true });
|
|
47
|
+
writeFileSync(join(dir, 'specs', 'ui-theme.md'), 'flat');
|
|
48
|
+
|
|
49
|
+
const invalid = mod.findInvalidSpecFiles(dir);
|
|
50
|
+
assert.deepEqual(invalid.map(i => i.path), ['specs/ui-theme.md']);
|
|
51
|
+
assert.deepEqual(invalid.map(i => i.expected), ['specs/ui-theme/spec.md']);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('reports root specs/spec.md as invalid', () => {
|
|
55
|
+
const dir = mkdtempSync(join(tempDir, 'root-spec-'));
|
|
56
|
+
mkdirSync(join(dir, 'specs'), { recursive: true });
|
|
57
|
+
writeFileSync(join(dir, 'specs', 'spec.md'), 'root');
|
|
58
|
+
|
|
59
|
+
const invalid = mod.findInvalidSpecFiles(dir);
|
|
60
|
+
assert.deepEqual(invalid, [{ path: 'specs/spec.md', expected: 'specs/<capability>/spec.md' }]);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('does not reject README.md inside specs', () => {
|
|
64
|
+
const dir = mkdtempSync(join(tempDir, 'readme-'));
|
|
65
|
+
mkdirSync(join(dir, 'specs'), { recursive: true });
|
|
66
|
+
writeFileSync(join(dir, 'specs', 'README.md'), 'notes');
|
|
67
|
+
|
|
68
|
+
assert.deepEqual(mod.findInvalidSpecFiles(dir), []);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('fails layout validation when canonical specs are required but missing', () => {
|
|
72
|
+
const dir = mkdtempSync(join(tempDir, 'missing-'));
|
|
73
|
+
mkdirSync(join(dir, 'specs'), { recursive: true });
|
|
74
|
+
|
|
75
|
+
const result = mod.validateSpecPathLayout(dir, { requireSpecs: true });
|
|
76
|
+
assert.equal(result.pass, false);
|
|
77
|
+
assert.deepEqual(result.specFiles, []);
|
|
78
|
+
assert.ok(result.failures.some(f => f.includes('No canonical spec files found')));
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
// tests/lib/state-loader.test.mjs
|
|
2
|
+
// Tests for scripts/lib/state-loader.mjs
|
|
3
|
+
import { describe, it, before, after } from 'node:test';
|
|
4
|
+
import assert from 'node:assert/strict';
|
|
5
|
+
import { mkdtempSync, writeFileSync, readFileSync, rmSync, existsSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { tmpdir } from 'node:os';
|
|
8
|
+
|
|
9
|
+
let tempDir;
|
|
10
|
+
|
|
11
|
+
describe('state-loader: readState()', () => {
|
|
12
|
+
let stateLoader;
|
|
13
|
+
|
|
14
|
+
before(async () => {
|
|
15
|
+
tempDir = mkdtempSync(join(tmpdir(), 'tf-state-test-'));
|
|
16
|
+
const modulePath = join(process.cwd(), 'scripts/lib/state-loader.mjs');
|
|
17
|
+
stateLoader = await import(modulePath);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
after(() => {
|
|
21
|
+
if (tempDir) rmSync(tempDir, { recursive: true, force: true });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('returns defaults when no state file exists', () => {
|
|
25
|
+
const state = stateLoader.readState(tempDir);
|
|
26
|
+
assert.equal(state.state, 'exploring');
|
|
27
|
+
assert.equal(state.workflow, 'auto');
|
|
28
|
+
assert.equal(state.batches_completed, 0);
|
|
29
|
+
assert.equal(state.execution_mode, null);
|
|
30
|
+
assert.equal(state.execution_plan_hash, null);
|
|
31
|
+
assert.equal(state.execution_plan_revision, null);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('infers change_name from directory when no state file', () => {
|
|
35
|
+
const changeDir = join(tempDir, 'my-feature');
|
|
36
|
+
const state = stateLoader.readState(changeDir);
|
|
37
|
+
assert.equal(state.change_name, 'my-feature');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('parses existing state file correctly', () => {
|
|
41
|
+
const stateYaml = [
|
|
42
|
+
'state: executing',
|
|
43
|
+
'workflow: full',
|
|
44
|
+
'artifacts_hash: sha256:abc123',
|
|
45
|
+
'batches_completed: 3',
|
|
46
|
+
'test_result: pass',
|
|
47
|
+
'change_name: export-csv',
|
|
48
|
+
'dp_0_confirmed: true',
|
|
49
|
+
'dp_1_result: confirmed: add csv export',
|
|
50
|
+
].join('\n');
|
|
51
|
+
|
|
52
|
+
writeFileSync(join(tempDir, '.team-flow.yaml'), stateYaml);
|
|
53
|
+
|
|
54
|
+
const state = stateLoader.readState(tempDir);
|
|
55
|
+
|
|
56
|
+
assert.equal(state.state, 'executing');
|
|
57
|
+
assert.equal(state.workflow, 'full');
|
|
58
|
+
assert.equal(state.artifacts_hash, 'sha256:abc123');
|
|
59
|
+
assert.equal(state.batches_completed, 3);
|
|
60
|
+
assert.equal(state.test_result, 'pass');
|
|
61
|
+
assert.equal(state.change_name, 'export-csv');
|
|
62
|
+
assert.equal(state.dp_0_confirmed, 'true');
|
|
63
|
+
assert.equal(state.dp_1_result, 'confirmed: add csv export');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('handles null values in YAML', () => {
|
|
67
|
+
const stateYaml = [
|
|
68
|
+
'state: exploring',
|
|
69
|
+
'artifacts_hash: null',
|
|
70
|
+
'contract_hash: null',
|
|
71
|
+
'dp_0_timestamp: null',
|
|
72
|
+
].join('\n');
|
|
73
|
+
|
|
74
|
+
writeFileSync(join(tempDir, '.team-flow.yaml'), stateYaml);
|
|
75
|
+
|
|
76
|
+
const state = stateLoader.readState(tempDir);
|
|
77
|
+
|
|
78
|
+
assert.equal(state.artifacts_hash, null);
|
|
79
|
+
assert.equal(state.contract_hash, null);
|
|
80
|
+
assert.equal(state.dp_0_timestamp, null);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('ignores comments and empty lines in YAML', () => {
|
|
84
|
+
const stateYaml = [
|
|
85
|
+
'# This is a comment',
|
|
86
|
+
'',
|
|
87
|
+
'state: specifying',
|
|
88
|
+
' ',
|
|
89
|
+
'# Another comment',
|
|
90
|
+
'workflow: tweak',
|
|
91
|
+
].join('\n');
|
|
92
|
+
|
|
93
|
+
writeFileSync(join(tempDir, '.team-flow.yaml'), stateYaml);
|
|
94
|
+
|
|
95
|
+
const state = stateLoader.readState(tempDir);
|
|
96
|
+
|
|
97
|
+
assert.equal(state.state, 'specifying');
|
|
98
|
+
assert.equal(state.workflow, 'tweak');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('merges partial state file with defaults', () => {
|
|
102
|
+
const stateYaml = 'state: approved-for-build\nworkflow: hotfix';
|
|
103
|
+
|
|
104
|
+
writeFileSync(join(tempDir, '.team-flow.yaml'), stateYaml);
|
|
105
|
+
|
|
106
|
+
const state = stateLoader.readState(tempDir);
|
|
107
|
+
assert.equal(state.state, 'approved-for-build');
|
|
108
|
+
assert.equal(state.workflow, 'hotfix');
|
|
109
|
+
// Unspecified fields should use defaults
|
|
110
|
+
assert.equal(state.batches_completed, 0);
|
|
111
|
+
assert.equal(state.test_result, null);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe('state-loader: writeState()', () => {
|
|
116
|
+
let stateLoader;
|
|
117
|
+
|
|
118
|
+
before(async () => {
|
|
119
|
+
tempDir = mkdtempSync(join(tmpdir(), 'tf-state-write-'));
|
|
120
|
+
const modulePath = join(process.cwd(), 'scripts/lib/state-loader.mjs');
|
|
121
|
+
stateLoader = await import(modulePath);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
after(() => {
|
|
125
|
+
if (tempDir) rmSync(tempDir, { recursive: true, force: true });
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('writes state file with correct structure', () => {
|
|
129
|
+
const state = {
|
|
130
|
+
state: 'executing',
|
|
131
|
+
workflow: 'full',
|
|
132
|
+
artifacts_hash: 'sha256:def456',
|
|
133
|
+
contract_hash: null,
|
|
134
|
+
batches_completed: 2,
|
|
135
|
+
test_result: null,
|
|
136
|
+
change_name: 'test-change',
|
|
137
|
+
dp_0_confirmed: 'true',
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
stateLoader.writeState(tempDir, state);
|
|
141
|
+
|
|
142
|
+
const filePath = join(tempDir, '.team-flow.yaml');
|
|
143
|
+
assert.ok(existsSync(filePath));
|
|
144
|
+
|
|
145
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
146
|
+
assert.ok(content.includes('state: executing'));
|
|
147
|
+
assert.ok(content.includes('workflow: full'));
|
|
148
|
+
assert.ok(content.includes('artifacts_hash: sha256:def456'));
|
|
149
|
+
assert.ok(content.includes('contract_hash: null'));
|
|
150
|
+
assert.ok(content.includes('batches_completed: 2'));
|
|
151
|
+
assert.ok(content.includes('execution_plan_hash: null'));
|
|
152
|
+
assert.ok(content.includes('execution_plan_revision: null'));
|
|
153
|
+
assert.ok(content.includes('change_name: test-change'));
|
|
154
|
+
assert.ok(content.includes('dp_0_confirmed: true'));
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('writes all DP fields (0-7)', () => {
|
|
158
|
+
const state = {
|
|
159
|
+
state: 'closing',
|
|
160
|
+
dp_0_decisions: 'scope: csv export only, tech: nestjs',
|
|
161
|
+
dp_0_result: 'confirmed: scope ok',
|
|
162
|
+
dp_0_confirmed: 'true',
|
|
163
|
+
dp_0_timestamp: '2026-07-01T08:00:00Z',
|
|
164
|
+
dp_1_result: 'confirmed: csv export',
|
|
165
|
+
dp_2_result: 'approved: artifacts ok',
|
|
166
|
+
dp_3_result: 'contract signed',
|
|
167
|
+
dp_4_result: 'execution started',
|
|
168
|
+
dp_5_result: null,
|
|
169
|
+
dp_6_result: null,
|
|
170
|
+
dp_7_result: null,
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
stateLoader.writeState(tempDir, state);
|
|
174
|
+
|
|
175
|
+
const content = readFileSync(join(tempDir, '.team-flow.yaml'), 'utf-8');
|
|
176
|
+
assert.ok(content.includes('dp_0_decisions: scope: csv export only, tech: nestjs'));
|
|
177
|
+
assert.ok(content.includes('dp_0_result: confirmed: scope ok'));
|
|
178
|
+
assert.ok(content.includes('dp_0_confirmed: true'));
|
|
179
|
+
assert.ok(content.includes('dp_1_result: confirmed: csv export'));
|
|
180
|
+
assert.ok(content.includes('dp_5_result: null'));
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('round-trips dp_0_result when updated through state loader', () => {
|
|
184
|
+
stateLoader.writeState(tempDir, {
|
|
185
|
+
state: 'exploring',
|
|
186
|
+
dp_0_decisions: 'scope confirmed',
|
|
187
|
+
dp_0_confirmed: 'true',
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
stateLoader.updateField(tempDir, 'dp_0_result', 'confirmed: manual');
|
|
191
|
+
|
|
192
|
+
const content = readFileSync(join(tempDir, '.team-flow.yaml'), 'utf-8');
|
|
193
|
+
const state = stateLoader.readState(tempDir);
|
|
194
|
+
|
|
195
|
+
assert.ok(content.includes('dp_0_result: confirmed: manual'));
|
|
196
|
+
assert.equal(state.dp_0_result, 'confirmed: manual');
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('round-trips state through write then read', () => {
|
|
200
|
+
const original = {
|
|
201
|
+
state: 'bridging',
|
|
202
|
+
workflow: 'hotfix',
|
|
203
|
+
artifacts_hash: 'sha256:xyz789',
|
|
204
|
+
batches_completed: 0,
|
|
205
|
+
dp_1_result: 'confirmed: refactor auth',
|
|
206
|
+
dp_1_timestamp: '2026-07-01T10:00:00Z',
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
stateLoader.writeState(tempDir, original);
|
|
210
|
+
const read = stateLoader.readState(tempDir);
|
|
211
|
+
|
|
212
|
+
assert.equal(read.state, original.state);
|
|
213
|
+
assert.equal(read.workflow, original.workflow);
|
|
214
|
+
assert.equal(read.artifacts_hash, original.artifacts_hash);
|
|
215
|
+
assert.equal(read.batches_completed, original.batches_completed);
|
|
216
|
+
assert.equal(read.dp_1_result, original.dp_1_result);
|
|
217
|
+
assert.equal(read.dp_1_timestamp, original.dp_1_timestamp);
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
describe('state-loader: updateField()', () => {
|
|
222
|
+
let stateLoader;
|
|
223
|
+
|
|
224
|
+
before(async () => {
|
|
225
|
+
tempDir = mkdtempSync(join(tmpdir(), 'tf-state-update-'));
|
|
226
|
+
const modulePath = join(process.cwd(), 'scripts/lib/state-loader.mjs');
|
|
227
|
+
stateLoader = await import(modulePath);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
after(() => {
|
|
231
|
+
if (tempDir) rmSync(tempDir, { recursive: true, force: true });
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('updates a single field without affecting others', () => {
|
|
235
|
+
// Initialize with some state
|
|
236
|
+
stateLoader.writeState(tempDir, {
|
|
237
|
+
state: 'exploring',
|
|
238
|
+
workflow: 'full',
|
|
239
|
+
batches_completed: 0,
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
stateLoader.updateField(tempDir, 'state', 'specifying');
|
|
243
|
+
stateLoader.updateField(tempDir, 'batches_completed', 3);
|
|
244
|
+
|
|
245
|
+
const state = stateLoader.readState(tempDir);
|
|
246
|
+
assert.equal(state.state, 'specifying');
|
|
247
|
+
assert.equal(state.batches_completed, 3);
|
|
248
|
+
// workflow should still be 'full'
|
|
249
|
+
assert.equal(state.workflow, 'full');
|
|
250
|
+
});
|
|
251
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// tests/lib/token-baseline.test.mjs
|
|
2
|
+
// Tests for scripts/token-baseline.mjs — measurement and comparison
|
|
3
|
+
|
|
4
|
+
import { describe, it } from 'node:test';
|
|
5
|
+
import assert from 'node:assert/strict';
|
|
6
|
+
import { execSync } from 'node:child_process';
|
|
7
|
+
import { writeFileSync, unlinkSync, readFileSync } from 'node:fs';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
import { tmpdir } from 'node:os';
|
|
10
|
+
|
|
11
|
+
const BASELINE_PATH = join(process.cwd(), 'scripts/token-baseline.mjs');
|
|
12
|
+
const TMP = tmpdir();
|
|
13
|
+
|
|
14
|
+
describe('token-baseline: measurement', () => {
|
|
15
|
+
it('outputs valid JSON with components and totals', () => {
|
|
16
|
+
const out = execSync(`node "${BASELINE_PATH}" --files hooks/session-start`, { encoding: 'utf-8' });
|
|
17
|
+
const data = JSON.parse(out);
|
|
18
|
+
assert.ok(Array.isArray(data.components), 'components should be an array');
|
|
19
|
+
assert.ok(data.components.length > 0, 'should have at least one component');
|
|
20
|
+
const c = data.components[0];
|
|
21
|
+
assert.ok(typeof c.lines === 'number' && c.lines > 0, 'lines should be positive');
|
|
22
|
+
assert.ok(typeof c.chars === 'number' && c.chars > 0, 'chars should be positive');
|
|
23
|
+
assert.ok(typeof c.estimatedTokens === 'number' && c.estimatedTokens > 0, 'estimatedTokens should be positive');
|
|
24
|
+
assert.ok(typeof data.totals === 'object', 'totals should be an object');
|
|
25
|
+
assert.ok(data.totals.estimatedTokens > 0, 'total estimatedTokens should be positive');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('--files filter limits output', () => {
|
|
29
|
+
const out = execSync(`node "${BASELINE_PATH}" --files hooks/session-start`, { encoding: 'utf-8' });
|
|
30
|
+
const data = JSON.parse(out);
|
|
31
|
+
assert.equal(data.components.length, 1, 'should only have one component');
|
|
32
|
+
assert.ok(data.components[0].path.includes('hooks/session-start'), 'should be the filtered file');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('--output writes to file', () => {
|
|
36
|
+
const outPath = join(TMP, 'token-baseline-test.json');
|
|
37
|
+
execSync(`node "${BASELINE_PATH}" --files hooks/session-start --output "${outPath}"`, { encoding: 'utf-8' });
|
|
38
|
+
const data = JSON.parse(readFileSync(outPath, 'utf-8'));
|
|
39
|
+
assert.ok(data.totals.estimatedTokens > 0, 'file should contain valid data');
|
|
40
|
+
unlinkSync(outPath);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('token-baseline: comparison', () => {
|
|
45
|
+
it('--compare produces change deltas', () => {
|
|
46
|
+
const baselinePath = join(TMP, 'baseline-cmp-test.json');
|
|
47
|
+
// Create a baseline with slightly different values
|
|
48
|
+
const baseline = {
|
|
49
|
+
timestamp: '2026-01-01T00:00:00Z',
|
|
50
|
+
components: [
|
|
51
|
+
{ path: 'hooks/session-start', label: 'hooks/session-start', lines: 30, chars: 2000, estimatedTokens: 500 },
|
|
52
|
+
],
|
|
53
|
+
totals: { lines: 30, chars: 2000, estimatedTokens: 500 },
|
|
54
|
+
};
|
|
55
|
+
writeFileSync(baselinePath, JSON.stringify(baseline));
|
|
56
|
+
|
|
57
|
+
const out = execSync(`node "${BASELINE_PATH}" --files hooks/session-start --compare "${baselinePath}"`, { encoding: 'utf-8' });
|
|
58
|
+
const data = JSON.parse(out);
|
|
59
|
+
assert.ok(Array.isArray(data.changes), 'should have changes array');
|
|
60
|
+
assert.ok(data.changes.length > 0, 'should have at least one change');
|
|
61
|
+
assert.ok(typeof data.summary.totalDelta === 'number', 'should have totalDelta');
|
|
62
|
+
assert.ok(typeof data.summary.totalPct === 'string', 'should have totalPct');
|
|
63
|
+
|
|
64
|
+
unlinkSync(baselinePath);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('missing file in baseline reports error', () => {
|
|
68
|
+
const out = execSync(`node "${BASELINE_PATH}" --files hooks/session-start --compare /nonexistent/baseline.json 2>&1 || true`, { encoding: 'utf-8' });
|
|
69
|
+
// Should fail gracefully — error message from file not found
|
|
70
|
+
assert.ok(out.length > 0, 'should produce output even on error');
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// tests/lib/token-rules.test.mjs
|
|
2
|
+
// Tests for scripts/lint/rules/token-rules.mjs — lint rule functions
|
|
3
|
+
|
|
4
|
+
import { describe, it } from 'node:test';
|
|
5
|
+
import assert from 'node:assert/strict';
|
|
6
|
+
import { checkMaxLines, checkMaxChars, checkMaxEmphasisMarkers, checkMaxCodeBlockLength } from '../../scripts/lint/rules/token-rules.mjs';
|
|
7
|
+
|
|
8
|
+
const CTX = { skillDirs: ['test-skill'], skillsDir: '/fake/skills' };
|
|
9
|
+
|
|
10
|
+
describe('token-rules: checkMaxLines', () => {
|
|
11
|
+
it('passes when under limit', async () => {
|
|
12
|
+
const issues = await checkMaxLines('test', 'line1\nline2\nline3', CTX, 10);
|
|
13
|
+
assert.equal(issues.length, 0);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('fails when over limit', async () => {
|
|
17
|
+
const content = Array(251).fill('line').join('\n');
|
|
18
|
+
const issues = await checkMaxLines('test', content, CTX, 250);
|
|
19
|
+
assert.equal(issues.length, 1);
|
|
20
|
+
assert.equal(issues[0].severity, 'error');
|
|
21
|
+
assert.ok(issues[0].message.includes('251'));
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('token-rules: checkMaxChars', () => {
|
|
26
|
+
it('passes when under limit', async () => {
|
|
27
|
+
const issues = await checkMaxChars('test', 'short', CTX, 100);
|
|
28
|
+
assert.equal(issues.length, 0);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('warns when over limit', async () => {
|
|
32
|
+
const content = 'x'.repeat(10001);
|
|
33
|
+
const issues = await checkMaxChars('test', content, CTX, 10000);
|
|
34
|
+
assert.equal(issues.length, 1);
|
|
35
|
+
assert.equal(issues[0].severity, 'warning');
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('token-rules: checkMaxEmphasisMarkers', () => {
|
|
40
|
+
it('passes for content without banned markers', async () => {
|
|
41
|
+
const issues = await checkMaxEmphasisMarkers('test', 'normal text with **some** emphasis', CTX, 30);
|
|
42
|
+
assert.equal(issues.length, 0);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('detects EXTREMELY_IMPORTANT', async () => {
|
|
46
|
+
const issues = await checkMaxEmphasisMarkers('test', 'This is EXTREMELY_IMPORTANT content', CTX, 30);
|
|
47
|
+
const eiIssue = issues.find(i => i.message.includes('EXTREMELY_IMPORTANT'));
|
|
48
|
+
assert.ok(eiIssue, 'should flag EXTREMELY_IMPORTANT');
|
|
49
|
+
assert.equal(eiIssue.severity, 'error');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('detects CRITICAL', async () => {
|
|
53
|
+
const issues = await checkMaxEmphasisMarkers('test', 'CRITICAL: do not skip', CTX, 30);
|
|
54
|
+
const crIssue = issues.find(i => i.message.includes('CRITICAL'));
|
|
55
|
+
assert.ok(crIssue, 'should flag CRITICAL');
|
|
56
|
+
assert.equal(crIssue.severity, 'error');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('warns on too many IMPORTANT occurrences', async () => {
|
|
60
|
+
const content = 'IMPORTANT\n'.repeat(5) + 'text';
|
|
61
|
+
const issues = await checkMaxEmphasisMarkers('test', content, CTX, 30);
|
|
62
|
+
const impIssue = issues.find(i => i.message.includes('IMPORTANT'));
|
|
63
|
+
assert.ok(impIssue, 'should flag excessive IMPORTANT');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('warns on too many emphasis markers', async () => {
|
|
67
|
+
const content = '**a** **b** '.repeat(31);
|
|
68
|
+
const issues = await checkMaxEmphasisMarkers('test', content, CTX, 30);
|
|
69
|
+
const empIssue = issues.find(i => i.message.includes('emphasis markers'));
|
|
70
|
+
assert.ok(empIssue, 'should flag excessive emphasis');
|
|
71
|
+
assert.equal(empIssue.severity, 'warning');
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('token-rules: checkMaxCodeBlockLength', () => {
|
|
76
|
+
it('passes for short code blocks', async () => {
|
|
77
|
+
const issues = await checkMaxCodeBlockLength('test', '```\nline1\nline2\n```', CTX, 5);
|
|
78
|
+
assert.equal(issues.length, 0);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('warns for long code blocks', async () => {
|
|
82
|
+
const lines = Array(17).fill('code').join('\n');
|
|
83
|
+
const content = '```\n' + lines + '\n```';
|
|
84
|
+
const issues = await checkMaxCodeBlockLength('test', content, CTX, 15);
|
|
85
|
+
assert.equal(issues.length, 1);
|
|
86
|
+
assert.equal(issues[0].severity, 'warning');
|
|
87
|
+
assert.ok(issues[0].message.includes('19'), `expected message to include '19', got: ${issues[0].message}`);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('handles content with no code blocks', async () => {
|
|
91
|
+
const issues = await checkMaxCodeBlockLength('test', 'just text, no code', CTX, 15);
|
|
92
|
+
assert.equal(issues.length, 0);
|
|
93
|
+
});
|
|
94
|
+
});
|