@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,149 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// lint-skills.mjs — Skill content static analyzer
|
|
3
|
+
// Scans skills/*/SKILL.md against registered rules, outputs structured report.
|
|
4
|
+
|
|
5
|
+
import { readdirSync, readFileSync, existsSync } from 'node:fs';
|
|
6
|
+
import { join, dirname } from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
|
|
9
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const SKILLS_DIR = join(__dirname, '..', '..', 'skills');
|
|
11
|
+
const RULES_DIR = join(__dirname, 'rules');
|
|
12
|
+
|
|
13
|
+
async function loadRules() {
|
|
14
|
+
if (!existsSync(RULES_DIR)) return [];
|
|
15
|
+
const ruleFiles = readdirSync(RULES_DIR).filter(f => f.endsWith('.mjs'));
|
|
16
|
+
const rules = [];
|
|
17
|
+
for (const file of ruleFiles.sort()) {
|
|
18
|
+
try {
|
|
19
|
+
const mod = await import(join(RULES_DIR, file));
|
|
20
|
+
if (mod.default && typeof mod.default.check === 'function') {
|
|
21
|
+
rules.push(mod.default);
|
|
22
|
+
}
|
|
23
|
+
} catch (e) {
|
|
24
|
+
console.error(`Warning: failed to load rule ${file}: ${e.message}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return rules;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param {Object} options
|
|
32
|
+
* @param {string[]} [options.skills] — specific skill names to lint (default: all)
|
|
33
|
+
* @returns {Promise<{valid: boolean, results: Array, summary: string}>}
|
|
34
|
+
*/
|
|
35
|
+
export async function lintSkills(options = {}) {
|
|
36
|
+
const rules = await loadRules();
|
|
37
|
+
const allDirs = readdirSync(SKILLS_DIR, { withFileTypes: true })
|
|
38
|
+
.filter(d => d.isDirectory())
|
|
39
|
+
.map(d => d.name);
|
|
40
|
+
|
|
41
|
+
const skillDirs = options.skills
|
|
42
|
+
? allDirs.filter(s => options.skills.includes(s))
|
|
43
|
+
: allDirs;
|
|
44
|
+
|
|
45
|
+
const results = [];
|
|
46
|
+
for (const skillName of skillDirs) {
|
|
47
|
+
const skillMd = join(SKILLS_DIR, skillName, 'SKILL.md');
|
|
48
|
+
if (!existsSync(skillMd)) {
|
|
49
|
+
results.push({ skill: skillName, issues: [{ severity: 'error', rule: 'file-existence', message: 'SKILL.md not found' }] });
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const content = readFileSync(skillMd, 'utf-8');
|
|
54
|
+
const ctx = { skillDirs: allDirs, skillsDir: SKILLS_DIR };
|
|
55
|
+
const issues = [];
|
|
56
|
+
|
|
57
|
+
for (const rule of rules) {
|
|
58
|
+
try {
|
|
59
|
+
const ruleIssues = await rule.check(skillName, content, ctx);
|
|
60
|
+
if (Array.isArray(ruleIssues)) {
|
|
61
|
+
issues.push(...ruleIssues.map(i => ({ ...i, rule: rule.name || 'unknown' })));
|
|
62
|
+
}
|
|
63
|
+
} catch (e) {
|
|
64
|
+
issues.push({ severity: 'error', rule: rule.name || 'unknown', message: `Rule threw: ${e.message}` });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
results.push({ skill: skillName, issues });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const totalIssues = results.reduce((sum, r) => sum + r.issues.length, 0);
|
|
72
|
+
const errorCount = results.reduce((sum, r) => sum + r.issues.filter(i => i.severity === 'error').length, 0);
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
valid: errorCount === 0,
|
|
76
|
+
results,
|
|
77
|
+
summary: `${totalIssues} issue(s) (${errorCount} errors) across ${results.length} skills`,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Load only token-specific rules (--include token).
|
|
83
|
+
*/
|
|
84
|
+
async function loadTokenRules() {
|
|
85
|
+
const tokenRulesPath = join(RULES_DIR, 'token-rules.mjs');
|
|
86
|
+
if (!existsSync(tokenRulesPath)) return [];
|
|
87
|
+
try {
|
|
88
|
+
const mod = await import(tokenRulesPath);
|
|
89
|
+
return mod.default && typeof mod.default.check === 'function' ? [mod.default] : [];
|
|
90
|
+
} catch (e) {
|
|
91
|
+
console.error(`Warning: failed to load token-rules: ${e.message}`);
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// CLI entry
|
|
97
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
98
|
+
const includeToken = process.argv.includes('--include') && process.argv[process.argv.indexOf('--include') + 1] === 'token';
|
|
99
|
+
const verbose = process.argv.includes('--verbose');
|
|
100
|
+
const fileArgIdx = process.argv.indexOf('--file');
|
|
101
|
+
const fileArg = fileArgIdx !== -1 ? process.argv[fileArgIdx + 1] : null;
|
|
102
|
+
|
|
103
|
+
let report;
|
|
104
|
+
if (includeToken) {
|
|
105
|
+
// Token mode: only run token rules
|
|
106
|
+
const rules = await loadTokenRules();
|
|
107
|
+
const skillName = fileArg ? fileArg.replace(/.*skills\/([^/]+)\/.*/, '$1') : 'all';
|
|
108
|
+
const results = [];
|
|
109
|
+
const dirs = fileArg
|
|
110
|
+
? [skillName]
|
|
111
|
+
: readdirSync(SKILLS_DIR, { withFileTypes: true }).filter(d => d.isDirectory()).map(d => d.name);
|
|
112
|
+
|
|
113
|
+
for (const dir of dirs) {
|
|
114
|
+
const skillMd = fileArg || join(SKILLS_DIR, dir, 'SKILL.md');
|
|
115
|
+
const mdPath = fileArg || skillMd;
|
|
116
|
+
if (!existsSync(mdPath)) {
|
|
117
|
+
results.push({ skill: dir, issues: [{ severity: 'error', rule: 'file-existence', message: 'SKILL.md not found' }] });
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const content = readFileSync(mdPath, 'utf-8');
|
|
121
|
+
const ctx = { skillDirs: dirs, skillsDir: SKILLS_DIR };
|
|
122
|
+
const issues = [];
|
|
123
|
+
for (const rule of rules) {
|
|
124
|
+
try {
|
|
125
|
+
const ruleIssues = await rule.check(dir, content, ctx);
|
|
126
|
+
if (Array.isArray(ruleIssues)) {
|
|
127
|
+
issues.push(...ruleIssues.map(i => ({ ...i, rule: rule.name || 'unknown' })));
|
|
128
|
+
}
|
|
129
|
+
} catch (e) {
|
|
130
|
+
issues.push({ severity: 'error', rule: rule.name || 'unknown', message: `Rule threw: ${e.message}` });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
results.push({ skill: dir, issues });
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const totalIssues = results.reduce((sum, r) => sum + r.issues.length, 0);
|
|
137
|
+
const errorCount = results.reduce((sum, r) => sum + r.issues.filter(i => i.severity === 'error').length, 0);
|
|
138
|
+
report = { valid: errorCount === 0, results, summary: `${totalIssues} issue(s) (${errorCount} errors) across ${results.length} skills` };
|
|
139
|
+
} else {
|
|
140
|
+
report = await lintSkills();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (!report.valid || verbose) {
|
|
144
|
+
console.log(JSON.stringify(report, null, 2));
|
|
145
|
+
} else {
|
|
146
|
+
console.log(`✅ ${report.summary}`);
|
|
147
|
+
}
|
|
148
|
+
process.exit(report.valid ? 0 : 1);
|
|
149
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Rule: behavior-consistency
|
|
2
|
+
// Detects: Skill description that claims behavior not enforced in guard/phase-guard
|
|
3
|
+
// Detects: "⛔ 禁止操作" without corresponding guard enforcement
|
|
4
|
+
|
|
5
|
+
const FORBIDDEN_PATTERNS = [
|
|
6
|
+
{ pattern: /⛔\s*禁止操作|Forbidden|Prohibited/i, label: 'forbidden-ops' },
|
|
7
|
+
{ pattern: /cannot be skipped|硬门禁|hard gate|不可跳过/i, label: 'hard-gate' },
|
|
8
|
+
{ pattern: /before.*test|without.*failing test|TDD Iron Law/i, label: 'tdd-requirement' },
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
export default {
|
|
12
|
+
name: 'behavior-consistency',
|
|
13
|
+
|
|
14
|
+
async check(skillName, content, ctx) {
|
|
15
|
+
const issues = [];
|
|
16
|
+
|
|
17
|
+
// Check 1: "hard gate" claims — should be in contract-builder or build-executor (DP-3, DP-4)
|
|
18
|
+
const hardGateMatch = content.match(/(hard gate|硬门禁|不可跳过|cannot be skipped)/i);
|
|
19
|
+
if (hardGateMatch && !['contract-builder', 'build-executor', 'workflow-start', 'release-archivist'].includes(skillName)) {
|
|
20
|
+
issues.push({
|
|
21
|
+
severity: 'warning',
|
|
22
|
+
message: `Claims "hard gate" but ${skillName} is not a gate-owning skill (expected: contract-builder, build-executor, workflow-start, release-archivist)`,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Check 2: "route to" consistency with workflow-start
|
|
27
|
+
if (skillName !== 'workflow-start') {
|
|
28
|
+
const routeRefs = [...content.matchAll(/[Rr]oute to [`""]?([a-z-]+)[`""]?/g)];
|
|
29
|
+
for (const [, target] of routeRefs) {
|
|
30
|
+
// This skill says "route to X" — workflow-start should have a corresponding rule
|
|
31
|
+
// We flag this for manual verification (can't easily cross-check without loading workflow-start)
|
|
32
|
+
// Instead, check that the routed-to skill exists
|
|
33
|
+
if (!ctx.skillDirs.includes(target)) {
|
|
34
|
+
issues.push({
|
|
35
|
+
severity: 'error',
|
|
36
|
+
message: `Routes to "${target}" which does not exist in skills/`,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Check 3: Decision point references should align with docs/decision-points.md
|
|
43
|
+
const dpRefs = [...content.matchAll(/DP-(\d)/g)].map(m => parseInt(m[1]));
|
|
44
|
+
const validDPs = [0, 1, 2, 3, 4, 5, 6, 7];
|
|
45
|
+
for (const dp of dpRefs) {
|
|
46
|
+
if (!validDPs.includes(dp)) {
|
|
47
|
+
issues.push({
|
|
48
|
+
severity: 'error',
|
|
49
|
+
message: `References DP-${dp} which is not a valid decision point (valid: DP-0 through DP-7)`,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return issues;
|
|
55
|
+
},
|
|
56
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Rule: dp-trigger-points
|
|
2
|
+
// Detects: Missing DP trigger descriptions and record commands in associated skills
|
|
3
|
+
|
|
4
|
+
const DP_SKILL_MAP = {
|
|
5
|
+
'DP-0': { skills: ['workflow-start'], name: '设计前确认' },
|
|
6
|
+
'DP-1': { skills: ['need-explorer'], name: '需求确认' },
|
|
7
|
+
'DP-2': { skills: ['spec-writer'], name: '工件审查' },
|
|
8
|
+
'DP-3': { skills: ['contract-builder'], name: '契约批准' },
|
|
9
|
+
'DP-4': { skills: ['build-executor'], name: '执行模式选择' },
|
|
10
|
+
'DP-5': { skills: ['bug-investigator'], name: '调试升级' },
|
|
11
|
+
'DP-6': { skills: ['release-archivist'], name: '验证失败' },
|
|
12
|
+
'DP-7': { skills: ['release-archivist'], name: '归档确认' },
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export default {
|
|
16
|
+
name: 'dp-trigger-points',
|
|
17
|
+
|
|
18
|
+
async check(skillName, content, ctx) {
|
|
19
|
+
const issues = [];
|
|
20
|
+
|
|
21
|
+
for (const [dp, { skills, name }] of Object.entries(DP_SKILL_MAP)) {
|
|
22
|
+
if (!skills.includes(skillName)) continue;
|
|
23
|
+
|
|
24
|
+
// Check 1: Is DP referenced by name/number?
|
|
25
|
+
const dpReferenced = content.includes(dp);
|
|
26
|
+
|
|
27
|
+
// Check 2: Is there a record command (tf state set ... dp_N_*)?
|
|
28
|
+
const dpNum = dp.slice(-1);
|
|
29
|
+
const hasRecordCommand = new RegExp(`dp_${dpNum}_(result|timestamp|confirmed)`).test(content);
|
|
30
|
+
|
|
31
|
+
// Check 3: Is the trigger condition described?
|
|
32
|
+
const hasTriggerDesc = new RegExp(
|
|
33
|
+
`触发条件|When (to|should)|Trigger|Use (this|the).*${dp}`,
|
|
34
|
+
'i'
|
|
35
|
+
).test(content) || content.includes(`DP-${dpNum}`);
|
|
36
|
+
|
|
37
|
+
if (!dpReferenced) {
|
|
38
|
+
issues.push({
|
|
39
|
+
severity: 'error',
|
|
40
|
+
message: `${dp} (${name}) is not referenced in ${skillName} — trigger point missing`,
|
|
41
|
+
});
|
|
42
|
+
} else {
|
|
43
|
+
if (!hasTriggerDesc) {
|
|
44
|
+
issues.push({
|
|
45
|
+
severity: 'warning',
|
|
46
|
+
message: `${dp} is mentioned but has no trigger condition description`,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
if (!hasRecordCommand) {
|
|
50
|
+
issues.push({
|
|
51
|
+
severity: 'warning',
|
|
52
|
+
message: `${dp} has no tf state set dp_${dpNum}_* record command`,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return issues;
|
|
59
|
+
},
|
|
60
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Rule: exception-handling
|
|
2
|
+
// Detects: Missing error handling for common failure scenarios
|
|
3
|
+
// Each skill should cover: parse failures, missing files, user interruption
|
|
4
|
+
|
|
5
|
+
const REQUIRED_EXCEPTION_PATTERNS = [
|
|
6
|
+
{
|
|
7
|
+
id: 'parse-failure',
|
|
8
|
+
patterns: [/解析失败|parse (error|fail)|malformed|格式不正确/i],
|
|
9
|
+
message: 'No guidance for parse/malformed-input failures',
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
id: 'missing-files',
|
|
13
|
+
patterns: [/文件缺失|file (not found|missing)|missing (file|artifact)/i],
|
|
14
|
+
message: 'No guidance for missing file/artifact scenarios',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: 'user-interruption',
|
|
18
|
+
patterns: [/用户中断|user (interrupt|cancel|stop)|恢复|resume|restart/i],
|
|
19
|
+
message: 'No guidance for user interruption and session recovery',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: 'validation-failure',
|
|
23
|
+
patterns: [/验证失败|validation (fail|error)|check fail/i],
|
|
24
|
+
message: 'No guidance for validation failure scenarios',
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
export default {
|
|
29
|
+
name: 'exception-handling',
|
|
30
|
+
|
|
31
|
+
async check(skillName, content, ctx) {
|
|
32
|
+
const issues = [];
|
|
33
|
+
|
|
34
|
+
// Every skill should have at least basic error guidance
|
|
35
|
+
for (const { id, patterns, message } of REQUIRED_EXCEPTION_PATTERNS) {
|
|
36
|
+
const hasCoverage = patterns.some(p => p.test(content));
|
|
37
|
+
|
|
38
|
+
// parse-failure is required for all skills
|
|
39
|
+
// missing-files is required for skills that read artifacts
|
|
40
|
+
// user-interruption is required for all skills
|
|
41
|
+
// validation-failure is required for spec-writer and contract-builder
|
|
42
|
+
|
|
43
|
+
const isRequired =
|
|
44
|
+
id === 'parse-failure' ||
|
|
45
|
+
id === 'user-interruption' ||
|
|
46
|
+
(id === 'missing-files' && /read|inspect|check|parse/i.test(content)) ||
|
|
47
|
+
(id === 'validation-failure' && ['spec-writer', 'contract-builder', 'release-archivist'].includes(skillName));
|
|
48
|
+
|
|
49
|
+
if (isRequired && !hasCoverage) {
|
|
50
|
+
issues.push({
|
|
51
|
+
severity: 'warning',
|
|
52
|
+
message,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return issues;
|
|
58
|
+
},
|
|
59
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Rule: no-contradictory-instructions
|
|
2
|
+
// Detects: DP trigger conditions that differ between skills
|
|
3
|
+
// Detects: State transition rules that differ between workflow-start and target skills
|
|
4
|
+
|
|
5
|
+
const DP_NAMES = {
|
|
6
|
+
'DP-0': '设计前确认', 'DP-1': '需求确认', 'DP-2': '工件审查', 'DP-3': '契约批准',
|
|
7
|
+
'DP-4': '执行模式选择', 'DP-5': '调试升级', 'DP-6': '验证失败', 'DP-7': '归档确认',
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const DP_SKILL_MAP = {
|
|
11
|
+
'DP-0': ['workflow-start'],
|
|
12
|
+
'DP-1': ['need-explorer'],
|
|
13
|
+
'DP-2': ['spec-writer'],
|
|
14
|
+
'DP-3': ['contract-builder'],
|
|
15
|
+
'DP-4': ['build-executor'],
|
|
16
|
+
'DP-5': ['bug-investigator'],
|
|
17
|
+
'DP-6': ['release-archivist'],
|
|
18
|
+
'DP-7': ['release-archivist'],
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default {
|
|
22
|
+
name: 'no-contradictory-instructions',
|
|
23
|
+
|
|
24
|
+
async check(skillName, content, ctx) {
|
|
25
|
+
const issues = [];
|
|
26
|
+
|
|
27
|
+
// Check 1: DP references — is the skill referencing DPs it shouldn't, or missing DPs it should?
|
|
28
|
+
const referencedDPs = new Set();
|
|
29
|
+
for (const match of content.matchAll(/DP-(\d)/g)) {
|
|
30
|
+
referencedDPs.add(`DP-${match[1]}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
for (const [dp, skills] of Object.entries(DP_SKILL_MAP)) {
|
|
34
|
+
if (skills.includes(skillName) && !referencedDPs.has(dp)) {
|
|
35
|
+
issues.push({
|
|
36
|
+
severity: 'error',
|
|
37
|
+
message: `${dp} (${DP_NAMES[dp]}) should be referenced in ${skillName} but was not found`,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Check 2: "hard gate" / "硬门禁" language — must match decision-points.md
|
|
43
|
+
if (content.includes('hard gate') || content.includes('硬门禁')) {
|
|
44
|
+
const dp3Refs = [...content.matchAll(/DP-3/g)];
|
|
45
|
+
if (dp3Refs.length === 0 && skillName === 'contract-builder') {
|
|
46
|
+
issues.push({
|
|
47
|
+
severity: 'warning',
|
|
48
|
+
message: `${skillName} mentions "hard gate" but does not reference DP-3`,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Check 3: If skill says "route to `X`", verify X is a real skill
|
|
54
|
+
// Only match backtick-quoted routes (explicit skill references), not plain text
|
|
55
|
+
const routeRefs = [...content.matchAll(/[Rr]oute to `([a-z-]+)`/g)];
|
|
56
|
+
for (const [, target] of routeRefs) {
|
|
57
|
+
if (!ctx.skillDirs.includes(target)) {
|
|
58
|
+
issues.push({
|
|
59
|
+
severity: 'warning',
|
|
60
|
+
message: `Routes to \`${target}\` but no such skill exists in skills/`,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return issues;
|
|
66
|
+
},
|
|
67
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Rule: no-redundant-checks
|
|
2
|
+
// Detects: Skill-internal validation that duplicates guard checks
|
|
3
|
+
// Detects: Same checklist items repeated across multiple skills
|
|
4
|
+
|
|
5
|
+
const GUARD_CHECK_DIMENSIONS = [
|
|
6
|
+
'artifacts-exist', 'schema-valid', 'contract-fresh', 'tasks-complete', 'tests-passing',
|
|
7
|
+
];
|
|
8
|
+
|
|
9
|
+
const REDUNDANT_PATTERNS = [
|
|
10
|
+
{ pattern: /proposal\.md.*has.*## Why/i, dimension: 'schema-valid' },
|
|
11
|
+
{ pattern: /specs?\/.*valid/i, dimension: 'schema-valid' },
|
|
12
|
+
{ pattern: /contract.*fresh|stale.*contract|contract.*stale/i, dimension: 'contract-fresh' },
|
|
13
|
+
{ pattern: /tasks?.*complete|all.*checked/i, dimension: 'tasks-complete' },
|
|
14
|
+
{ pattern: /tests?.*pass/i, dimension: 'tests-passing' },
|
|
15
|
+
{ pattern: /artifacts?.*exist|file.*present/i, dimension: 'artifacts-exist' },
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
export default {
|
|
19
|
+
name: 'no-redundant-checks',
|
|
20
|
+
|
|
21
|
+
async check(skillName, content, ctx) {
|
|
22
|
+
const issues = [];
|
|
23
|
+
|
|
24
|
+
// Skills that are expected to do validation (NOT redundant):
|
|
25
|
+
// - spec-writer: validates artifacts it just created (primary validation)
|
|
26
|
+
// - guard: this IS the guard system
|
|
27
|
+
// Redundancy only applies when a skill re-checks what guard already checks
|
|
28
|
+
const SKILLS_THAT_OWN_VALIDATION = ['spec-writer', 'workflow-start'];
|
|
29
|
+
|
|
30
|
+
if (SKILLS_THAT_OWN_VALIDATION.includes(skillName)) {
|
|
31
|
+
return issues; // These skills legitimately validate
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// For other skills, flag explicit re-checks of guard dimensions
|
|
35
|
+
for (const { pattern, dimension } of REDUNDANT_PATTERNS) {
|
|
36
|
+
if (pattern.test(content)) {
|
|
37
|
+
// Check if this is the skill's own checklist (allowed) or a duplicated guard check
|
|
38
|
+
const checklistSection = content.match(/##\s*(?:Self-Review|Quality Gate|Validation|Checklist|检查)/i);
|
|
39
|
+
if (checklistSection) {
|
|
40
|
+
issues.push({
|
|
41
|
+
severity: 'warning',
|
|
42
|
+
message: `Skill includes "${dimension}" check — guard.mjs already performs this check on transition. Consider referencing guard instead of re-checking.`,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return issues;
|
|
49
|
+
},
|
|
50
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// Bundle: token-rules
|
|
2
|
+
// Collection of token efficiency lint rules for skill files.
|
|
3
|
+
// Each rule is exported individually; also exported as a default bundle.
|
|
4
|
+
|
|
5
|
+
const MAX_LINES_PER_SKILL = 250;
|
|
6
|
+
const MAX_CHARS_PER_SKILL = 10000;
|
|
7
|
+
const MAX_EMPHASIS_MARKERS = 30;
|
|
8
|
+
const MAX_IMPORTANT_OCCURRENCES = 3;
|
|
9
|
+
const MAX_CODE_BLOCK_LINES = 15;
|
|
10
|
+
|
|
11
|
+
function countEmphasisMarkers(content) {
|
|
12
|
+
return (content.match(/\*\*[^*]+\*\*/g) || []).length;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function countImportant(content) {
|
|
16
|
+
return (content.match(/\bIMPORTANT\b/g) || []).length;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function countExtremelyImportant(content) {
|
|
20
|
+
return (content.match(/EXTREMELY_IMPORTANT/g) || []).length;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function countCritical(content) {
|
|
24
|
+
return (content.match(/\bCRITICAL\b/g) || []).length;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function getCodeBlocks(content) {
|
|
28
|
+
const blocks = [];
|
|
29
|
+
const regex = /```[\s\S]*?```/g;
|
|
30
|
+
let match;
|
|
31
|
+
while ((match = regex.exec(content)) !== null) {
|
|
32
|
+
const lines = match[0].split('\n').length;
|
|
33
|
+
blocks.push({ content: match[0], lines });
|
|
34
|
+
}
|
|
35
|
+
return blocks;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Check file line count does not exceed max.
|
|
40
|
+
* @param {string} _skillName
|
|
41
|
+
* @param {string} content
|
|
42
|
+
* @param {object} _ctx
|
|
43
|
+
* @param {number} [max=250]
|
|
44
|
+
*/
|
|
45
|
+
export async function checkMaxLines(_skillName, content, _ctx, max = MAX_LINES_PER_SKILL) {
|
|
46
|
+
const lineCount = content.split('\n').length;
|
|
47
|
+
if (lineCount > max) {
|
|
48
|
+
return [{ severity: 'error', message: `File has ${lineCount} lines (max: ${max})` }];
|
|
49
|
+
}
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Check file character count does not exceed max.
|
|
55
|
+
*/
|
|
56
|
+
export async function checkMaxChars(_skillName, content, _ctx, max = MAX_CHARS_PER_SKILL) {
|
|
57
|
+
const charCount = content.length;
|
|
58
|
+
if (charCount > max) {
|
|
59
|
+
return [{ severity: 'warning', message: `File has ${charCount} chars (max: ${max})` }];
|
|
60
|
+
}
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Check emphasis markers (**bold**) count does not exceed max.
|
|
66
|
+
* Also checks for banned markers (EXTREMELY_IMPORTANT, CRITICAL).
|
|
67
|
+
*/
|
|
68
|
+
export async function checkMaxEmphasisMarkers(_skillName, content, _ctx, max = MAX_EMPHASIS_MARKERS) {
|
|
69
|
+
const issues = [];
|
|
70
|
+
const emphasis = countEmphasisMarkers(content);
|
|
71
|
+
if (emphasis > max) {
|
|
72
|
+
issues.push({ severity: 'warning', message: `File has ${emphasis} emphasis markers (max: ${max})` });
|
|
73
|
+
}
|
|
74
|
+
const ei = countExtremelyImportant(content);
|
|
75
|
+
if (ei > 0) {
|
|
76
|
+
issues.push({ severity: 'error', message: `File contains EXTREMELY_IMPORTANT (${ei}x) — banned marker` });
|
|
77
|
+
}
|
|
78
|
+
const cr = countCritical(content);
|
|
79
|
+
if (cr > 0) {
|
|
80
|
+
issues.push({ severity: 'error', message: `File contains CRITICAL (${cr}x) — banned marker` });
|
|
81
|
+
}
|
|
82
|
+
const imp = countImportant(content);
|
|
83
|
+
if (imp > MAX_IMPORTANT_OCCURRENCES) {
|
|
84
|
+
issues.push({ severity: 'warning', message: `File contains IMPORTANT ${imp}x (max: ${MAX_IMPORTANT_OCCURRENCES})` });
|
|
85
|
+
}
|
|
86
|
+
return issues;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Check code blocks do not exceed max lines.
|
|
91
|
+
*/
|
|
92
|
+
export async function checkMaxCodeBlockLength(_skillName, content, _ctx, max = MAX_CODE_BLOCK_LINES) {
|
|
93
|
+
const issues = [];
|
|
94
|
+
const blocks = getCodeBlocks(content);
|
|
95
|
+
blocks.forEach((block, i) => {
|
|
96
|
+
if (block.lines > max) {
|
|
97
|
+
issues.push({
|
|
98
|
+
severity: 'warning',
|
|
99
|
+
message: `Code block #${i + 1} has ${block.lines} lines (max: ${max})`,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
return issues;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Bundle: run all token rules and return combined issues.
|
|
108
|
+
*/
|
|
109
|
+
async function checkAll(skillName, content, ctx) {
|
|
110
|
+
const results = await Promise.all([
|
|
111
|
+
checkMaxLines(skillName, content, ctx),
|
|
112
|
+
checkMaxChars(skillName, content, ctx),
|
|
113
|
+
checkMaxEmphasisMarkers(skillName, content, ctx),
|
|
114
|
+
checkMaxCodeBlockLength(skillName, content, ctx),
|
|
115
|
+
]);
|
|
116
|
+
return results.flat();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export default {
|
|
120
|
+
name: 'token-rules',
|
|
121
|
+
check: checkAll,
|
|
122
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Generate a review package: commit list, stat summary, and the net
|
|
3
|
+
# diff with extended context, written to a file the reviewer reads in one
|
|
4
|
+
# call. Using the recorded per-task BASE (not HEAD~1) keeps multi-commit
|
|
5
|
+
# tasks intact.
|
|
6
|
+
#
|
|
7
|
+
# Usage: review-package BASE HEAD [OUTFILE]
|
|
8
|
+
# Default OUTFILE: <repo-root>/.superpowers/sdd/review-<base7>..<head7>.diff
|
|
9
|
+
# (named per range, so a re-review after fixes gets a distinct fresh file).
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
if [ $# -lt 2 ] || [ $# -gt 3 ]; then
|
|
13
|
+
echo "usage: review-package BASE HEAD [OUTFILE]" >&2
|
|
14
|
+
exit 2
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
base=$1
|
|
18
|
+
head=$2
|
|
19
|
+
|
|
20
|
+
git rev-parse --verify --quiet "$base" >/dev/null || { echo "bad BASE: $base" >&2; exit 2; }
|
|
21
|
+
git rev-parse --verify --quiet "$head" >/dev/null || { echo "bad HEAD: $head" >&2; exit 2; }
|
|
22
|
+
|
|
23
|
+
if [ $# -eq 3 ]; then
|
|
24
|
+
out=$3
|
|
25
|
+
else
|
|
26
|
+
dir="$(cd "$(dirname "$0")" && pwd)/sdd-workspace"
|
|
27
|
+
out="$dir/review-$(git rev-parse --short "$base")..$(git rev-parse --short "$head").diff"
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
{
|
|
31
|
+
echo "# Review package: ${base}..${head}"
|
|
32
|
+
echo
|
|
33
|
+
echo "## Commits"
|
|
34
|
+
git log --oneline "${base}..${head}"
|
|
35
|
+
echo
|
|
36
|
+
echo "## Files changed"
|
|
37
|
+
git diff --stat "${base}..${head}"
|
|
38
|
+
echo
|
|
39
|
+
echo "## Diff"
|
|
40
|
+
git diff -U10 "${base}..${head}"
|
|
41
|
+
} > "$out"
|
|
42
|
+
|
|
43
|
+
commits=$(git rev-list --count "${base}..${head}")
|
|
44
|
+
echo "wrote ${out}: ${commits} commit(s), $(wc -c < "$out" | tr -d ' ') bytes"
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Extract one task's full text from an implementation plan into a file the
|
|
3
|
+
# implementer reads in one call, so the task text never has to be pasted
|
|
4
|
+
# through the controller's context.
|
|
5
|
+
#
|
|
6
|
+
# Usage: task-brief PLAN_FILE TASK_NUMBER [OUTFILE]
|
|
7
|
+
# Default OUTFILE: <repo-root>/.superpowers/sdd/task-<N>-brief.md
|
|
8
|
+
# (per worktree; concurrent runs in the same working tree share it).
|
|
9
|
+
set -euo pipefail
|
|
10
|
+
|
|
11
|
+
if [ $# -lt 2 ] || [ $# -gt 3 ]; then
|
|
12
|
+
echo "usage: task-brief PLAN_FILE TASK_NUMBER [OUTFILE]" >&2
|
|
13
|
+
exit 2
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
plan=$1
|
|
17
|
+
n=$2
|
|
18
|
+
[ -f "$plan" ] || { echo "no such plan file: $plan" >&2; exit 2; }
|
|
19
|
+
|
|
20
|
+
if [ $# -eq 3 ]; then
|
|
21
|
+
out=$3
|
|
22
|
+
else
|
|
23
|
+
dir="$(cd "$(dirname "$0")" && pwd)/sdd-workspace"
|
|
24
|
+
out="$dir/task-${n}-brief.md"
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
awk -v n="$n" '
|
|
28
|
+
/^```/ { infence = !infence }
|
|
29
|
+
!infence && /^#+[ \t]+Task[ \t]+[0-9]+/ {
|
|
30
|
+
intask = ($0 ~ ("^#+[ \t]+Task[ \t]+" n "([^0-9]|$)"))
|
|
31
|
+
}
|
|
32
|
+
intask { print }
|
|
33
|
+
' "$plan" > "$out"
|
|
34
|
+
|
|
35
|
+
if [ ! -s "$out" ]; then
|
|
36
|
+
echo "task ${n} not found in ${plan} (no heading matching 'Task ${n}')" >&2
|
|
37
|
+
exit 3
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
echo "wrote ${out}: $(wc -l < "$out" | tr -d ' ') lines"
|