timsquad 3.6.0 → 3.7.1
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/README.ko.md +103 -113
- package/README.md +100 -110
- package/dist/commands/init.js +4 -8
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/{upgrade.d.ts → update.d.ts} +3 -3
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/{upgrade.js → update.js} +12 -17
- package/dist/commands/update.js.map +1 -0
- package/dist/daemon/event-queue.d.ts +3 -11
- package/dist/daemon/event-queue.d.ts.map +1 -1
- package/dist/daemon/event-queue.js +62 -203
- package/dist/daemon/event-queue.js.map +1 -1
- package/dist/daemon/index.d.ts +1 -1
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +15 -14
- package/dist/daemon/index.js.map +1 -1
- package/dist/index.js +3 -43
- package/dist/index.js.map +1 -1
- package/dist/lib/agent-generator.d.ts.map +1 -1
- package/dist/lib/agent-generator.js +10 -10
- package/dist/lib/agent-generator.js.map +1 -1
- package/dist/lib/compile-rules.d.ts.map +1 -1
- package/dist/lib/compile-rules.js +37 -4
- package/dist/lib/compile-rules.js.map +1 -1
- package/dist/lib/compiler.d.ts +1 -0
- package/dist/lib/compiler.d.ts.map +1 -1
- package/dist/lib/compiler.js +67 -11
- package/dist/lib/compiler.js.map +1 -1
- package/dist/{commands/log.d.ts → lib/log-utils.d.ts} +7 -15
- package/dist/lib/log-utils.d.ts.map +1 -0
- package/dist/lib/log-utils.js +347 -0
- package/dist/lib/log-utils.js.map +1 -0
- package/dist/lib/skill-generator.d.ts.map +1 -1
- package/dist/lib/skill-generator.js +4 -44
- package/dist/lib/skill-generator.js.map +1 -1
- package/dist/lib/ssot-map.d.ts.map +1 -1
- package/dist/lib/ssot-map.js +3 -0
- package/dist/lib/ssot-map.js.map +1 -1
- package/dist/lib/template.d.ts +10 -3
- package/dist/lib/template.d.ts.map +1 -1
- package/dist/lib/template.js +142 -22
- package/dist/lib/template.js.map +1 -1
- package/dist/lib/upgrade-backup.js +1 -1
- package/dist/lib/upgrade-backup.js.map +1 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +37 -34
- package/dist/types/config.js.map +1 -1
- package/dist/types/feedback.d.ts +1 -54
- package/dist/types/feedback.d.ts.map +1 -1
- package/dist/types/feedback.js +1 -22
- package/dist/types/feedback.js.map +1 -1
- package/dist/types/project.d.ts +5 -0
- package/dist/types/project.d.ts.map +1 -1
- package/dist/types/project.js +15 -0
- package/dist/types/project.js.map +1 -1
- package/dist/types/ssot-map.d.ts +2 -0
- package/dist/types/ssot-map.d.ts.map +1 -1
- package/package.json +1 -1
- package/templates/base/agents/base/tsq-architect.md +1 -1
- package/templates/base/agents/base/tsq-dba.md +3 -1
- package/templates/base/agents/base/tsq-designer.md +3 -1
- package/templates/base/agents/base/tsq-developer.md +3 -1
- package/templates/base/agents/base/tsq-librarian.md +1 -1
- package/templates/base/agents/base/tsq-qa.md +3 -1
- package/templates/base/agents/base/tsq-security.md +3 -1
- package/templates/base/agents/overlays/platform/claude-code.md +2 -2
- package/templates/base/config.template.yaml +17 -28
- package/templates/base/knowledge/templates/task-result.md +5 -10
- package/templates/base/skills/_template/SKILL.md +1 -3
- package/templates/base/skills/{architecture → tsq-architecture}/SKILL.md +2 -2
- package/templates/base/skills/tsq-audit/SKILL.md +74 -0
- package/templates/base/skills/{methodology/bdd → tsq-bdd}/SKILL.md +14 -9
- package/templates/base/skills/{coding → tsq-coding}/SKILL.md +2 -4
- package/templates/base/skills/tsq-controller/SKILL.md +81 -0
- package/templates/base/skills/{mobile/dart → tsq-dart}/SKILL.md +5 -3
- package/templates/base/skills/{database → tsq-database}/SKILL.md +5 -2
- package/templates/base/skills/{methodology/ddd → tsq-ddd}/SKILL.md +15 -10
- package/templates/base/skills/{methodology/debugging → tsq-debugging}/SKILL.md +2 -2
- package/templates/base/skills/tsq-decompose/SKILL.md +117 -0
- package/templates/base/skills/tsq-delete/SKILL.md +72 -0
- package/templates/base/skills/{mobile/flutter → tsq-flutter}/SKILL.md +6 -3
- package/templates/base/skills/tsq-full/SKILL.md +67 -0
- package/templates/base/skills/tsq-grill/SKILL.md +86 -0
- package/templates/base/skills/{backend/node → tsq-hono}/SKILL.md +6 -4
- package/templates/base/skills/tsq-librarian/SKILL.md +78 -0
- package/templates/base/skills/tsq-log/SKILL.md +30 -0
- package/templates/base/skills/{frontend/nextjs → tsq-nextjs}/SKILL.md +14 -9
- package/templates/base/skills/{planning → tsq-planning}/SKILL.md +2 -2
- package/templates/base/skills/{database/prisma → tsq-prisma}/SKILL.md +15 -9
- package/templates/base/skills/{product-audit → tsq-product-audit}/SKILL.md +2 -4
- package/templates/base/skills/{prompt-engineering → tsq-prompt}/SKILL.md +6 -4
- package/templates/base/skills/tsq-protocol/SKILL.md +85 -33
- package/templates/base/skills/tsq-quick/SKILL.md +58 -0
- package/templates/base/skills/{frontend/react → tsq-react}/SKILL.md +6 -3
- package/templates/base/skills/tsq-retro/SKILL.md +86 -0
- package/templates/base/skills/tsq-retro/references/feedback-guide.md +58 -0
- package/templates/base/skills/tsq-retro/references/improve-protocol.md +87 -0
- package/templates/base/skills/{security → tsq-security}/SKILL.md +2 -4
- package/templates/base/skills/{spec → tsq-spec}/SKILL.md +4 -6
- package/templates/base/skills/{stability-verification → tsq-stability}/SKILL.md +3 -3
- package/templates/base/skills/tsq-start/SKILL.md +90 -0
- package/templates/base/skills/tsq-start/references/onboarding-questions.md +177 -0
- package/templates/base/skills/tsq-status/SKILL.md +32 -0
- package/templates/base/skills/{methodology/tdd → tsq-tdd}/SKILL.md +6 -3
- package/templates/base/skills/{testing → tsq-testing}/SKILL.md +5 -7
- package/templates/base/skills/{typescript → tsq-typescript}/SKILL.md +2 -2
- package/templates/base/skills/{ui-design → tsq-ui}/SKILL.md +2 -2
- package/templates/base/skills/tsq-update/SKILL.md +48 -0
- package/templates/base/timsquad/constraints/competency-framework.xml +2 -2
- package/templates/base/timsquad/constraints/ssot-schema.xml +2 -2
- package/templates/base/timsquad/process/phase-checklist.yaml +1 -1
- package/templates/base/timsquad/process/state-machine.xml +2 -2
- package/templates/base/timsquad/process/validation-rules.xml +8 -8
- package/templates/base/timsquad/process/workflow-base.xml +8 -8
- package/templates/base/timsquad/retrospective/cycle-report.template.md +2 -2
- package/templates/base/timsquad/retrospective/patterns/failure-patterns.md +1 -1
- package/templates/base/timsquad/retrospective/patterns/success-patterns.md +2 -2
- package/templates/base/timsquad/retrospective/retrospective-state.xml +2 -2
- package/templates/base/timsquad/ssot/audit-trail-spec.template.md +155 -0
- package/templates/base/timsquad/ssot/compliance-matrix.template.md +105 -0
- package/templates/base/timsquad/ssot/component-map.template.md +181 -0
- package/templates/base/timsquad/ssot/data-design.template.md +4 -4
- package/templates/base/timsquad/ssot/deployment-spec.template.md +29 -22
- package/templates/base/timsquad/ssot/env-config.template.md +4 -2
- package/templates/base/timsquad/ssot/error-codes.template.md +3 -3
- package/templates/base/timsquad/ssot/functional-spec.template.md +40 -3
- package/templates/base/timsquad/ssot/glossary.template.md +2 -2
- package/templates/base/timsquad/ssot/infra-topology.template.md +191 -0
- package/templates/base/timsquad/ssot/integration-spec.template.md +2 -2
- package/templates/base/timsquad/ssot/monitoring-spec.template.md +185 -0
- package/templates/base/timsquad/ssot/navigation-map.template.md +154 -0
- package/templates/base/timsquad/ssot/performance-budget.template.md +132 -0
- package/templates/base/timsquad/ssot/planning.template.md +3 -3
- package/templates/base/timsquad/ssot/prd/_template.md +73 -0
- package/templates/base/timsquad/ssot/prd.template.md +10 -21
- package/templates/base/timsquad/ssot/requirements.template.md +3 -3
- package/templates/base/timsquad/ssot/sdk-spec.template.md +223 -0
- package/templates/base/timsquad/ssot/service-spec.template.md +3 -3
- package/templates/base/timsquad/ssot/state-machine.template.md +127 -0
- package/templates/base/timsquad/ssot/ui-ux-spec.template.md +43 -3
- package/templates/base/timsquad/ssot-map.template.yaml +28 -0
- package/templates/base/timsquad/state/workspace.xml +11 -11
- package/templates/platforms/claude-code/rules/adr-rules.md +1 -1
- package/templates/platforms/claude-code/rules/build-gate.md +1 -1
- package/templates/platforms/claude-code/rules/completion-verification.md +0 -2
- package/templates/platforms/claude-code/rules/context-monitor.md +1 -1
- package/templates/platforms/claude-code/rules/feedback-routing.md +2 -2
- package/templates/platforms/claude-code/rules/phase-management.md +2 -2
- package/templates/platforms/claude-code/rules/plan-review.md +2 -2
- package/templates/platforms/claude-code/rules/quality-guards.md +0 -2
- package/templates/platforms/claude-code/rules/sequence-management.md +15 -15
- package/templates/platforms/claude-code/rules/session-notes.md +1 -1
- package/templates/platforms/claude-code/rules/workspace-sync.md +1 -1
- package/templates/platforms/claude-code/scripts/build-gate.sh +6 -1
- package/templates/platforms/claude-code/scripts/change-scope-guard.sh +1 -4
- package/templates/platforms/claude-code/scripts/check-capability.sh +68 -0
- package/templates/platforms/claude-code/scripts/completion-guard.sh +62 -4
- package/templates/platforms/claude-code/scripts/context-restore.sh +33 -6
- package/templates/platforms/claude-code/scripts/phase-guard.sh +2 -5
- package/templates/platforms/claude-code/scripts/pre-compact.sh +3 -3
- package/templates/platforms/claude-code/scripts/safe-guard.sh +2 -5
- package/templates/platforms/claude-code/scripts/subagent-start.sh +11 -0
- package/templates/platforms/claude-code/scripts/subagent-stop.sh +11 -0
- package/templates/platforms/claude-code/settings.json +20 -74
- package/templates/project-types/api-backend/config.yaml +9 -5
- package/templates/project-types/api-backend/process/workflow.xml +2 -2
- package/templates/project-types/fintech/config.yaml +13 -19
- package/templates/project-types/fintech/ssot/audit-trail-spec.template.md +207 -0
- package/templates/project-types/fintech/ssot/compliance-matrix.template.md +187 -0
- package/templates/project-types/infra/config.yaml +7 -4
- package/templates/project-types/infra/process/workflow.xml +3 -3
- package/templates/project-types/mobile-app/config.yaml +8 -14
- package/templates/project-types/mobile-app/process/workflow.xml +4 -4
- package/templates/project-types/platform/config.yaml +8 -5
- package/templates/project-types/platform/process/workflow.xml +3 -3
- package/templates/project-types/web-app/config.yaml +9 -15
- package/templates/project-types/web-app/process/workflow.xml +6 -6
- package/templates/project-types/web-service/config.yaml +10 -19
- package/templates/project-types/web-service/process/workflow.xml +6 -6
- package/dist/commands/audit.d.ts +0 -22
- package/dist/commands/audit.d.ts.map +0 -1
- package/dist/commands/audit.js +0 -233
- package/dist/commands/audit.js.map +0 -1
- package/dist/commands/compile.d.ts +0 -3
- package/dist/commands/compile.d.ts.map +0 -1
- package/dist/commands/compile.js +0 -251
- package/dist/commands/compile.js.map +0 -1
- package/dist/commands/feedback.d.ts +0 -12
- package/dist/commands/feedback.d.ts.map +0 -1
- package/dist/commands/feedback.js +0 -382
- package/dist/commands/feedback.js.map +0 -1
- package/dist/commands/full.d.ts +0 -3
- package/dist/commands/full.d.ts.map +0 -1
- package/dist/commands/full.js +0 -88
- package/dist/commands/full.js.map +0 -1
- package/dist/commands/git/commit.d.ts +0 -3
- package/dist/commands/git/commit.d.ts.map +0 -1
- package/dist/commands/git/commit.js +0 -85
- package/dist/commands/git/commit.js.map +0 -1
- package/dist/commands/git/index.d.ts +0 -5
- package/dist/commands/git/index.d.ts.map +0 -1
- package/dist/commands/git/index.js +0 -5
- package/dist/commands/git/index.js.map +0 -1
- package/dist/commands/git/pr.d.ts +0 -3
- package/dist/commands/git/pr.d.ts.map +0 -1
- package/dist/commands/git/pr.js +0 -139
- package/dist/commands/git/pr.js.map +0 -1
- package/dist/commands/git/release.d.ts +0 -3
- package/dist/commands/git/release.d.ts.map +0 -1
- package/dist/commands/git/release.js +0 -153
- package/dist/commands/git/release.js.map +0 -1
- package/dist/commands/git/sync.d.ts +0 -3
- package/dist/commands/git/sync.d.ts.map +0 -1
- package/dist/commands/git/sync.js +0 -132
- package/dist/commands/git/sync.js.map +0 -1
- package/dist/commands/improve.d.ts +0 -3
- package/dist/commands/improve.d.ts.map +0 -1
- package/dist/commands/improve.js +0 -286
- package/dist/commands/improve.js.map +0 -1
- package/dist/commands/knowledge.d.ts +0 -3
- package/dist/commands/knowledge.d.ts.map +0 -1
- package/dist/commands/knowledge.js +0 -316
- package/dist/commands/knowledge.js.map +0 -1
- package/dist/commands/log.d.ts.map +0 -1
- package/dist/commands/log.js +0 -1468
- package/dist/commands/log.js.map +0 -1
- package/dist/commands/meta-index.d.ts +0 -3
- package/dist/commands/meta-index.d.ts.map +0 -1
- package/dist/commands/meta-index.js +0 -431
- package/dist/commands/meta-index.js.map +0 -1
- package/dist/commands/metrics.d.ts +0 -3
- package/dist/commands/metrics.d.ts.map +0 -1
- package/dist/commands/metrics.js +0 -843
- package/dist/commands/metrics.js.map +0 -1
- package/dist/commands/quick.d.ts +0 -3
- package/dist/commands/quick.d.ts.map +0 -1
- package/dist/commands/quick.js +0 -136
- package/dist/commands/quick.js.map +0 -1
- package/dist/commands/retro.d.ts +0 -3
- package/dist/commands/retro.d.ts.map +0 -1
- package/dist/commands/retro.js +0 -885
- package/dist/commands/retro.js.map +0 -1
- package/dist/commands/session.d.ts +0 -3
- package/dist/commands/session.d.ts.map +0 -1
- package/dist/commands/session.js +0 -346
- package/dist/commands/session.js.map +0 -1
- package/dist/commands/skills.d.ts +0 -12
- package/dist/commands/skills.d.ts.map +0 -1
- package/dist/commands/skills.js +0 -228
- package/dist/commands/skills.js.map +0 -1
- package/dist/commands/status.d.ts +0 -3
- package/dist/commands/status.d.ts.map +0 -1
- package/dist/commands/status.js +0 -127
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/upgrade.d.ts.map +0 -1
- package/dist/commands/upgrade.js.map +0 -1
- package/dist/commands/watch.d.ts +0 -3
- package/dist/commands/watch.d.ts.map +0 -1
- package/dist/commands/watch.js +0 -213
- package/dist/commands/watch.js.map +0 -1
- package/dist/commands/workflow.d.ts +0 -5
- package/dist/commands/workflow.d.ts.map +0 -1
- package/dist/commands/workflow.js +0 -781
- package/dist/commands/workflow.js.map +0 -1
- package/templates/base/skills/audit/SKILL.md +0 -66
- package/templates/base/skills/controller/SKILL.md +0 -77
- package/templates/base/skills/controller/delegation/developer.md +0 -25
- package/templates/base/skills/controller/delegation/librarian.md +0 -33
- package/templates/base/skills/controller/delegation/reviewer.md +0 -19
- package/templates/base/skills/controller/triggers/phase-complete.md +0 -25
- package/templates/base/skills/controller/triggers/sequence-complete.md +0 -15
- package/templates/base/skills/controller/triggers/ssot-changed.md +0 -14
- package/templates/base/skills/controller/triggers/task-complete.md +0 -14
- package/templates/base/skills/librarian/SKILL.md +0 -53
- package/templates/base/skills/main-session-constraints/SKILL.md +0 -62
- package/templates/base/skills/retrospective/SKILL.md +0 -77
- package/templates/base/skills/review/SKILL.md +0 -72
- package/templates/base/skills/tsq-cli/SKILL.md +0 -73
- package/templates/base/skills/tsq-cli/references/cli-reference.md +0 -92
- package/templates/base/timsquad/feedback/feedback-router.sh +0 -341
- package/templates/base/timsquad/feedback/routing-rules.yaml +0 -352
- package/templates/platforms/claude-code/CLAUDE.md.template +0 -89
- package/templates/platforms/claude-code/rules/skill-suggest.md +0 -27
- package/templates/platforms/claude-code/scripts/skill-inject.sh +0 -216
- package/templates/platforms/claude-code/scripts/skill-rules.json +0 -95
- package/templates/platforms/claude-code/scripts/skill-suggest.sh +0 -105
- package/templates/platforms/claude-code/scripts/subagent-inject.sh +0 -53
- /package/templates/base/skills/{architecture → tsq-architecture}/references/adr-template.md +0 -0
- /package/templates/base/skills/{architecture → tsq-architecture}/references/api-design.md +0 -0
- /package/templates/base/skills/{methodology/bdd → tsq-bdd}/rules/gherkin-patterns.md +0 -0
- /package/templates/base/skills/{coding → tsq-coding}/rules/async-patterns.md +0 -0
- /package/templates/base/skills/{coding → tsq-coding}/rules/code-organization.md +0 -0
- /package/templates/base/skills/{coding → tsq-coding}/rules/error-handling.md +0 -0
- /package/templates/base/skills/{coding → tsq-coding}/rules/patterns.md +0 -0
- /package/templates/base/skills/{coding → tsq-coding}/rules/type-safety.md +0 -0
- /package/templates/base/skills/{controller → tsq-controller}/memory/.gitkeep +0 -0
- /package/templates/base/skills/{controller → tsq-controller}/references/README.md +0 -0
- /package/templates/base/skills/{controller → tsq-controller}/rules/README.md +0 -0
- /package/templates/base/skills/{mobile/dart → tsq-dart}/rules/async-patterns.md +0 -0
- /package/templates/base/skills/{mobile/dart → tsq-dart}/rules/code-style.md +0 -0
- /package/templates/base/skills/{mobile/dart → tsq-dart}/rules/null-safety.md +0 -0
- /package/templates/base/skills/{mobile/dart → tsq-dart}/rules/type-system.md +0 -0
- /package/templates/base/skills/{database → tsq-database}/rules/query-optimization.md +0 -0
- /package/templates/base/skills/{database → tsq-database}/rules/supabase-patterns.md +0 -0
- /package/templates/base/skills/{methodology/ddd → tsq-ddd}/rules/strategic-patterns.md +0 -0
- /package/templates/base/skills/{methodology/debugging → tsq-debugging}/references/root-cause-tracing.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/ci-cd/SKILL.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/ci-cd/references/ci-cd-pipeline.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/ci-cd/rules/code-signing.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/ci-cd/rules/codemagic-setup.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/ci-cd/rules/fastlane-setup.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/ci-cd/rules/github-actions.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/ci-cd/rules/store-deployment.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/ci-cd/rules/versioning.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/i18n/SKILL.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/i18n/references/i18n-architecture.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/i18n/rules/arb-files.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/i18n/rules/locale-switching.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/i18n/rules/localization-setup.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/i18n/rules/plural-gender.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/i18n/rules/text-direction.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/monitoring/SKILL.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/monitoring/references/monitoring-architecture.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/monitoring/rules/analytics.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/monitoring/rules/crashlytics-setup.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/monitoring/rules/logging.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/monitoring/rules/performance-monitoring.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/monitoring/rules/sentry-integration.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/networking/SKILL.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/networking/references/api-client-architecture.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/networking/rules/caching.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/networking/rules/connectivity.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/networking/rules/dio-setup.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/networking/rules/error-handling.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/networking/rules/interceptors.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/networking/rules/retrofit-patterns.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/push-notifications/SKILL.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/push-notifications/references/notification-architecture.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/push-notifications/references/platform-setup.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/push-notifications/rules/background-processing.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/push-notifications/rules/deep-linking.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/push-notifications/rules/fcm-setup.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/push-notifications/rules/local-notifications.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/push-notifications/rules/notification-handling.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/push-notifications/rules/notification-permissions.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/push-notifications/rules/rich-notifications.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/references/freezed-patterns.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/references/project-structure.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/rules/animations.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/rules/architecture.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/rules/navigation-routing.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/rules/performance.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/rules/platform-adaptive.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/rules/state-management.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/rules/testing.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/rules/widget-conventions.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/security/SKILL.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/security/references/mobile-security-checklist.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/security/rules/api-key-protection.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/security/rules/authentication.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/security/rules/data-protection.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/security/rules/obfuscation.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/security/rules/secure-storage.md +0 -0
- /package/templates/base/skills/{mobile/flutter → tsq-flutter}/security/rules/ssl-pinning.md +0 -0
- /package/templates/base/skills/{backend/node → tsq-hono}/rules/async-patterns.md +0 -0
- /package/templates/base/skills/{backend/node → tsq-hono}/rules/deployment.md +0 -0
- /package/templates/base/skills/{backend/node → tsq-hono}/rules/env-config.md +0 -0
- /package/templates/base/skills/{backend/node → tsq-hono}/rules/error-handling.md +0 -0
- /package/templates/base/skills/{backend/node → tsq-hono}/rules/hono-app-setup.md +0 -0
- /package/templates/base/skills/{backend/node → tsq-hono}/rules/jwt-auth.md +0 -0
- /package/templates/base/skills/{backend/node → tsq-hono}/rules/middleware.md +0 -0
- /package/templates/base/skills/{backend/node → tsq-hono}/rules/testing.md +0 -0
- /package/templates/base/skills/{frontend/nextjs → tsq-nextjs}/rules/app-router.md +0 -0
- /package/templates/base/skills/{planning → tsq-planning}/references/prd-guide.md +0 -0
- /package/templates/base/skills/{planning → tsq-planning}/references/requirements-guide.md +0 -0
- /package/templates/base/skills/{database/prisma → tsq-prisma}/rules/queries.md +0 -0
- /package/templates/base/skills/{database/prisma → tsq-prisma}/rules/schema-design.md +0 -0
- /package/templates/base/skills/{product-audit → tsq-product-audit}/checklists/01-security.md +0 -0
- /package/templates/base/skills/{product-audit → tsq-product-audit}/checklists/02-performance.md +0 -0
- /package/templates/base/skills/{product-audit → tsq-product-audit}/checklists/03-seo.md +0 -0
- /package/templates/base/skills/{product-audit → tsq-product-audit}/checklists/04-accessibility.md +0 -0
- /package/templates/base/skills/{product-audit → tsq-product-audit}/checklists/05-ui-ux.md +0 -0
- /package/templates/base/skills/{product-audit → tsq-product-audit}/checklists/06-architecture.md +0 -0
- /package/templates/base/skills/{product-audit → tsq-product-audit}/checklists/07-functional-requirements.md +0 -0
- /package/templates/base/skills/{product-audit → tsq-product-audit}/rules/audit-protocol.md +0 -0
- /package/templates/base/skills/{product-audit → tsq-product-audit}/rules/false-positive-guard.md +0 -0
- /package/templates/base/skills/{product-audit → tsq-product-audit}/rules/scoring-criteria.md +0 -0
- /package/templates/base/skills/{product-audit → tsq-product-audit}/templates/improvement-plan-template.md +0 -0
- /package/templates/base/skills/{product-audit → tsq-product-audit}/templates/report-template.md +0 -0
- /package/templates/base/skills/{frontend/react → tsq-react}/rules/_sections.md +0 -0
- /package/templates/base/skills/{frontend/react → tsq-react}/rules/anti-patterns.md +0 -0
- /package/templates/base/skills/{frontend/react → tsq-react}/rules/async-api-routes.md +0 -0
- /package/templates/base/skills/{frontend/react → tsq-react}/rules/async-defer-await.md +0 -0
- /package/templates/base/skills/{frontend/react → tsq-react}/rules/async-dependencies.md +0 -0
- /package/templates/base/skills/{frontend/react → tsq-react}/rules/async-parallel.md +0 -0
- /package/templates/base/skills/{frontend/react → tsq-react}/rules/async-suspense-boundaries.md +0 -0
- /package/templates/base/skills/{frontend/react → tsq-react}/rules/bundle-barrel-imports.md +0 -0
- /package/templates/base/skills/{frontend/react → tsq-react}/rules/bundle-defer-third-party.md +0 -0
- /package/templates/base/skills/{frontend/react → tsq-react}/rules/bundle-dynamic-imports.md +0 -0
- /package/templates/base/skills/{frontend/react → tsq-react}/rules/component-conventions.md +0 -0
- /package/templates/base/skills/{frontend/react → tsq-react}/rules/js-combine-iterations.md +0 -0
- /package/templates/base/skills/{frontend/react → tsq-react}/rules/js-early-exit.md +0 -0
- /package/templates/base/skills/{frontend/react → tsq-react}/rules/js-index-maps.md +0 -0
- /package/templates/base/skills/{frontend/react → tsq-react}/rules/js-set-map-lookups.md +0 -0
- /package/templates/base/skills/{frontend/react → tsq-react}/rules/rendering-conditional-render.md +0 -0
- /package/templates/base/skills/{frontend/react → tsq-react}/rules/rendering-content-visibility.md +0 -0
- /package/templates/base/skills/{frontend/react → tsq-react}/rules/rendering-hoist-jsx.md +0 -0
- /package/templates/base/skills/{frontend/react → tsq-react}/rules/rerender-defer-reads.md +0 -0
- /package/templates/base/skills/{frontend/react → tsq-react}/rules/rerender-derived-state.md +0 -0
- /package/templates/base/skills/{frontend/react → tsq-react}/rules/rerender-memo.md +0 -0
- /package/templates/base/skills/{frontend/react → tsq-react}/rules/rerender-transitions.md +0 -0
- /package/templates/base/skills/{frontend/react → tsq-react}/rules/server-after-nonblocking.md +0 -0
- /package/templates/base/skills/{frontend/react → tsq-react}/rules/server-cache-react.md +0 -0
- /package/templates/base/skills/{frontend/react → tsq-react}/rules/server-parallel-fetching.md +0 -0
- /package/templates/base/skills/{frontend/react → tsq-react}/rules/state-location.md +0 -0
- /package/templates/base/skills/{retrospective → tsq-retro}/references/improvement-template.md +0 -0
- /package/templates/base/skills/{security → tsq-security}/rules/auth-patterns.md +0 -0
- /package/templates/base/skills/{security → tsq-security}/rules/dependency-security.md +0 -0
- /package/templates/base/skills/{security → tsq-security}/rules/input-validation.md +0 -0
- /package/templates/base/skills/{security → tsq-security}/rules/owasp-examples.md +0 -0
- /package/templates/base/skills/{security → tsq-security}/rules/secrets-management.md +0 -0
- /package/templates/base/skills/{security → tsq-security}/scripts/check-secrets.sh +0 -0
- /package/templates/base/skills/{stability-verification → tsq-stability}/references/release-checklist.md +0 -0
- /package/templates/base/skills/{stability-verification → tsq-stability}/references/security-fix-patterns.md +0 -0
- /package/templates/base/skills/{stability-verification → tsq-stability}/rules/verification-layers.md +0 -0
- /package/templates/base/skills/{stability-verification → tsq-stability}/rules/verification-workflow.md +0 -0
- /package/templates/base/skills/{stability-verification → tsq-stability}/scripts/verify.sh +0 -0
- /package/templates/base/skills/{methodology/tdd → tsq-tdd}/rules/real-world-example.md +0 -0
- /package/templates/base/skills/{methodology/tdd → tsq-tdd}/rules/techniques.md +0 -0
- /package/templates/base/skills/{testing → tsq-testing}/references/e2e-stability.md +0 -0
- /package/templates/base/skills/{testing → tsq-testing}/references/testing-patterns.md +0 -0
- /package/templates/base/skills/{typescript → tsq-typescript}/rules/type-patterns.md +0 -0
- /package/templates/base/skills/{typescript → tsq-typescript}/rules/utility-types.md +0 -0
|
@@ -1,781 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import fs from 'fs-extra';
|
|
3
|
-
import { colors, printHeader, printError, printSuccess, printKeyValue } from '../utils/colors.js';
|
|
4
|
-
import { findProjectRoot } from '../lib/project.js';
|
|
5
|
-
import { getTimestamp } from '../utils/date.js';
|
|
6
|
-
import { loadWorkflowState, saveWorkflowState, isSequenceComplete, findSequenceForAgent, } from '../lib/workflow-state.js';
|
|
7
|
-
import { getSequencesDir, getPhasesDir, buildSequenceLogData, buildPhaseGateData, loadSequenceTaskLogs, hasSemantic, } from './log.js';
|
|
8
|
-
import { queryDaemon } from '../daemon/meta-cache.js';
|
|
9
|
-
import { writeContext } from '../daemon/context-writer.js';
|
|
10
|
-
import { parseAgentPrerequisites, checkStale, loadManifest } from '../lib/compiler.js';
|
|
11
|
-
const LOG_SCHEMA_VERSION = '1.0.0';
|
|
12
|
-
export function registerWorkflowCommand(program) {
|
|
13
|
-
const wfCmd = program
|
|
14
|
-
.command('workflow')
|
|
15
|
-
.alias('wf')
|
|
16
|
-
.description('Manage automated workflow (sequences, phases, automation toggles)');
|
|
17
|
-
// tsq workflow set-phase <phase-id>
|
|
18
|
-
wfCmd
|
|
19
|
-
.command('set-phase <phase-id>')
|
|
20
|
-
.description('Set the current active phase')
|
|
21
|
-
.option('--force', 'Force phase transition even if gate conditions are not met')
|
|
22
|
-
.action(async (phaseId, options) => {
|
|
23
|
-
try {
|
|
24
|
-
await setPhase(phaseId, options.force);
|
|
25
|
-
}
|
|
26
|
-
catch (error) {
|
|
27
|
-
printError(error.message);
|
|
28
|
-
process.exit(1);
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
// tsq workflow add-sequence <seq-id>
|
|
32
|
-
wfCmd
|
|
33
|
-
.command('add-sequence <seq-id>')
|
|
34
|
-
.description('Register a sequence for workflow tracking')
|
|
35
|
-
.requiredOption('--agents <list>', 'Comma-separated expected agent types (e.g. developer,dba)')
|
|
36
|
-
.option('--phase <id>', 'Phase ID (defaults to current phase)')
|
|
37
|
-
.action(async (seqId, options) => {
|
|
38
|
-
try {
|
|
39
|
-
await addSequence(seqId, options);
|
|
40
|
-
}
|
|
41
|
-
catch (error) {
|
|
42
|
-
printError(error.message);
|
|
43
|
-
process.exit(1);
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
// tsq workflow remove-sequence <seq-id>
|
|
47
|
-
wfCmd
|
|
48
|
-
.command('remove-sequence <seq-id>')
|
|
49
|
-
.description('Remove a sequence from tracking')
|
|
50
|
-
.action(async (seqId) => {
|
|
51
|
-
try {
|
|
52
|
-
await removeSequence(seqId);
|
|
53
|
-
}
|
|
54
|
-
catch (error) {
|
|
55
|
-
printError(error.message);
|
|
56
|
-
process.exit(1);
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
// tsq workflow status
|
|
60
|
-
wfCmd
|
|
61
|
-
.command('status')
|
|
62
|
-
.description('Show workflow state and automation status')
|
|
63
|
-
.action(async () => {
|
|
64
|
-
try {
|
|
65
|
-
await showStatus();
|
|
66
|
-
}
|
|
67
|
-
catch (error) {
|
|
68
|
-
printError(error.message);
|
|
69
|
-
process.exit(1);
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
// tsq workflow config <key> <value>
|
|
73
|
-
wfCmd
|
|
74
|
-
.command('config <key> <value>')
|
|
75
|
-
.description('Toggle automation settings (e.g. sequence_log on/off)')
|
|
76
|
-
.action(async (key, value) => {
|
|
77
|
-
try {
|
|
78
|
-
await setConfig(key, value);
|
|
79
|
-
}
|
|
80
|
-
catch (error) {
|
|
81
|
-
printError(error.message);
|
|
82
|
-
process.exit(1);
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
// tsq task start --agent <agent> --scope <paths>
|
|
86
|
-
wfCmd
|
|
87
|
-
.command('task-start')
|
|
88
|
-
.description('Prepare task context for a sub-agent (creates task-context.json)')
|
|
89
|
-
.requiredOption('--agent <type>', 'Agent type (developer, qa, dba, etc.)')
|
|
90
|
-
.option('--scope <paths>', 'Comma-separated scope paths (e.g. "src/auth,src/models")')
|
|
91
|
-
.action(async (options) => {
|
|
92
|
-
try {
|
|
93
|
-
await taskStart(options);
|
|
94
|
-
}
|
|
95
|
-
catch (error) {
|
|
96
|
-
printError(error.message);
|
|
97
|
-
process.exit(1);
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
// tsq workflow track-task <agent> <task-log-path> (internal — called by hook)
|
|
101
|
-
wfCmd
|
|
102
|
-
.command('track-task <agent> <task-log-path>')
|
|
103
|
-
.description('(internal) Track a completed task in workflow state')
|
|
104
|
-
.action(async (agent, taskLogPath) => {
|
|
105
|
-
try {
|
|
106
|
-
await trackTask(agent, taskLogPath);
|
|
107
|
-
}
|
|
108
|
-
catch {
|
|
109
|
-
// Silent fail for hook usage — don't break the hook chain
|
|
110
|
-
process.exit(0);
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
// tsq workflow check (internal — called by hook on SessionEnd)
|
|
114
|
-
wfCmd
|
|
115
|
-
.command('check')
|
|
116
|
-
.description('(internal) Check workflow state and auto-create L2/L3/gate/retro')
|
|
117
|
-
.action(async () => {
|
|
118
|
-
try {
|
|
119
|
-
await checkAndAutomate();
|
|
120
|
-
}
|
|
121
|
-
catch {
|
|
122
|
-
// Silent fail for hook usage
|
|
123
|
-
process.exit(0);
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
// ============================================================
|
|
128
|
-
// Commands
|
|
129
|
-
// ============================================================
|
|
130
|
-
async function setPhase(phaseId, force) {
|
|
131
|
-
const projectRoot = await findProjectRoot();
|
|
132
|
-
if (!projectRoot)
|
|
133
|
-
throw new Error('Not a TimSquad project');
|
|
134
|
-
const state = await loadWorkflowState(projectRoot);
|
|
135
|
-
// Phase Gate enforcement: check current phase before transitioning
|
|
136
|
-
if (state.current_phase && state.current_phase.id !== phaseId && state.automation.phase_gate) {
|
|
137
|
-
const gateResult = await buildPhaseGateData(projectRoot, state.current_phase.id);
|
|
138
|
-
if (!gateResult.can_transition) {
|
|
139
|
-
if (force) {
|
|
140
|
-
console.log(colors.warning(`\n ⚠ Phase gate BLOCKED for "${state.current_phase.id}" — forced override`));
|
|
141
|
-
for (const condition of gateResult.blocking_conditions) {
|
|
142
|
-
console.log(colors.dim(` - ${condition}`));
|
|
143
|
-
}
|
|
144
|
-
for (const report of gateResult.missing_reports) {
|
|
145
|
-
console.log(colors.dim(` - Missing report: ${report}`));
|
|
146
|
-
}
|
|
147
|
-
console.log('');
|
|
148
|
-
}
|
|
149
|
-
else {
|
|
150
|
-
const lines = [
|
|
151
|
-
`Phase gate BLOCKED for "${state.current_phase.id}". Cannot transition to "${phaseId}".`,
|
|
152
|
-
];
|
|
153
|
-
if (gateResult.blocking_conditions.length > 0) {
|
|
154
|
-
lines.push('Blocking conditions:');
|
|
155
|
-
for (const c of gateResult.blocking_conditions) {
|
|
156
|
-
lines.push(` - ${c}`);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
if (gateResult.missing_reports.length > 0) {
|
|
160
|
-
lines.push('Missing reports:');
|
|
161
|
-
for (const r of gateResult.missing_reports) {
|
|
162
|
-
lines.push(` - ${r}`);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
lines.push('', 'Use --force to override.');
|
|
166
|
-
throw new Error(lines.join('\n'));
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
// Archive current phase if exists
|
|
171
|
-
if (state.current_phase && state.current_phase.id !== phaseId) {
|
|
172
|
-
state.completed_phases.push(state.current_phase.id);
|
|
173
|
-
}
|
|
174
|
-
state.current_phase = {
|
|
175
|
-
id: phaseId,
|
|
176
|
-
started_at: getTimestamp(),
|
|
177
|
-
sequences: [],
|
|
178
|
-
};
|
|
179
|
-
// Atomic sync: update workflow.json and current-phase.json together
|
|
180
|
-
await syncPhaseFiles(projectRoot, state);
|
|
181
|
-
printSuccess(`Phase set: ${phaseId}`);
|
|
182
|
-
printKeyValue('Automation', formatAutomation(state.automation));
|
|
183
|
-
}
|
|
184
|
-
/**
|
|
185
|
-
* Atomically sync workflow.json and current-phase.json
|
|
186
|
-
* Uses write-to-temp + rename pattern to prevent desync on failure
|
|
187
|
-
*/
|
|
188
|
-
async function syncPhaseFiles(projectRoot, state) {
|
|
189
|
-
const stateDir = path.join(projectRoot, '.timsquad', 'state');
|
|
190
|
-
await fs.ensureDir(stateDir);
|
|
191
|
-
const workflowPath = path.join(stateDir, 'workflow.json');
|
|
192
|
-
const phasePath = path.join(stateDir, 'current-phase.json');
|
|
193
|
-
const workflowTmp = workflowPath + '.tmp';
|
|
194
|
-
const phaseTmp = phasePath + '.tmp';
|
|
195
|
-
const phaseData = state.current_phase
|
|
196
|
-
? { current: state.current_phase.id, startedAt: state.current_phase.started_at, progress: 0 }
|
|
197
|
-
: { current: 'planning', startedAt: getTimestamp(), progress: 0 };
|
|
198
|
-
try {
|
|
199
|
-
// 1. Write both temp files
|
|
200
|
-
await fs.writeJson(workflowTmp, state, { spaces: 2 });
|
|
201
|
-
await fs.writeJson(phaseTmp, phaseData, { spaces: 2 });
|
|
202
|
-
// 2. Rename atomically (rename is atomic on POSIX filesystems)
|
|
203
|
-
await fs.rename(workflowTmp, workflowPath);
|
|
204
|
-
await fs.rename(phaseTmp, phasePath);
|
|
205
|
-
}
|
|
206
|
-
catch (err) {
|
|
207
|
-
// Cleanup temp files on failure
|
|
208
|
-
await fs.remove(workflowTmp).catch(() => { });
|
|
209
|
-
await fs.remove(phaseTmp).catch(() => { });
|
|
210
|
-
throw new Error(`Phase sync failed: ${err.message}`);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
async function addSequence(seqId, options) {
|
|
214
|
-
const projectRoot = await findProjectRoot();
|
|
215
|
-
if (!projectRoot)
|
|
216
|
-
throw new Error('Not a TimSquad project');
|
|
217
|
-
const state = await loadWorkflowState(projectRoot);
|
|
218
|
-
const phaseId = options.phase || state.current_phase?.id;
|
|
219
|
-
if (!phaseId) {
|
|
220
|
-
throw new Error('No active phase. Run `tsq workflow set-phase <phase-id>` first.');
|
|
221
|
-
}
|
|
222
|
-
if (state.sequences[seqId]) {
|
|
223
|
-
throw new Error(`Sequence ${seqId} already exists. Remove it first to re-add.`);
|
|
224
|
-
}
|
|
225
|
-
const agents = options.agents.split(',').map(a => a.trim());
|
|
226
|
-
state.sequences[seqId] = {
|
|
227
|
-
status: 'pending',
|
|
228
|
-
phase: phaseId,
|
|
229
|
-
expected_agents: agents,
|
|
230
|
-
completed_tasks: [],
|
|
231
|
-
report_path: null,
|
|
232
|
-
l2_created: false,
|
|
233
|
-
};
|
|
234
|
-
// Add to current phase's sequence list
|
|
235
|
-
if (state.current_phase && !state.current_phase.sequences.includes(seqId)) {
|
|
236
|
-
state.current_phase.sequences.push(seqId);
|
|
237
|
-
}
|
|
238
|
-
await saveWorkflowState(projectRoot, state);
|
|
239
|
-
printSuccess(`Sequence added: ${seqId}`);
|
|
240
|
-
printKeyValue('Phase', phaseId);
|
|
241
|
-
printKeyValue('Expected agents', agents.join(', '));
|
|
242
|
-
}
|
|
243
|
-
async function removeSequence(seqId) {
|
|
244
|
-
const projectRoot = await findProjectRoot();
|
|
245
|
-
if (!projectRoot)
|
|
246
|
-
throw new Error('Not a TimSquad project');
|
|
247
|
-
const state = await loadWorkflowState(projectRoot);
|
|
248
|
-
if (!state.sequences[seqId]) {
|
|
249
|
-
throw new Error(`Sequence ${seqId} not found`);
|
|
250
|
-
}
|
|
251
|
-
delete state.sequences[seqId];
|
|
252
|
-
if (state.current_phase) {
|
|
253
|
-
state.current_phase.sequences = state.current_phase.sequences.filter(s => s !== seqId);
|
|
254
|
-
}
|
|
255
|
-
await saveWorkflowState(projectRoot, state);
|
|
256
|
-
printSuccess(`Sequence removed: ${seqId}`);
|
|
257
|
-
}
|
|
258
|
-
async function showStatus() {
|
|
259
|
-
const projectRoot = await findProjectRoot();
|
|
260
|
-
if (!projectRoot)
|
|
261
|
-
throw new Error('Not a TimSquad project');
|
|
262
|
-
const state = await loadWorkflowState(projectRoot);
|
|
263
|
-
printHeader('Workflow Status');
|
|
264
|
-
// Automation toggles
|
|
265
|
-
console.log(colors.subheader(' Automation'));
|
|
266
|
-
for (const [key, val] of Object.entries(state.automation)) {
|
|
267
|
-
const icon = val ? colors.success('ON') : colors.dim('OFF');
|
|
268
|
-
console.log(` ${key.padEnd(16)} ${icon}`);
|
|
269
|
-
}
|
|
270
|
-
// Current phase
|
|
271
|
-
console.log(colors.subheader('\n Phase'));
|
|
272
|
-
if (state.current_phase) {
|
|
273
|
-
printKeyValue(' Current', state.current_phase.id);
|
|
274
|
-
printKeyValue(' Started', state.current_phase.started_at);
|
|
275
|
-
printKeyValue(' Sequences', state.current_phase.sequences.join(', ') || '(none)');
|
|
276
|
-
}
|
|
277
|
-
else {
|
|
278
|
-
console.log(colors.dim(' No active phase. Run `tsq wf set-phase <id>`'));
|
|
279
|
-
}
|
|
280
|
-
// Sequences
|
|
281
|
-
const seqEntries = Object.entries(state.sequences);
|
|
282
|
-
if (seqEntries.length > 0) {
|
|
283
|
-
console.log(colors.subheader('\n Sequences'));
|
|
284
|
-
for (const [seqId, seq] of seqEntries) {
|
|
285
|
-
const statusIcon = seq.status === 'completed'
|
|
286
|
-
? colors.success('✓')
|
|
287
|
-
: seq.status === 'in_progress'
|
|
288
|
-
? colors.warning('▸')
|
|
289
|
-
: seq.status === 'blocked'
|
|
290
|
-
? colors.error('✗')
|
|
291
|
-
: colors.dim('○');
|
|
292
|
-
const l2Icon = seq.l2_created ? colors.success('[L2]') : colors.dim('[--]');
|
|
293
|
-
const completedAgents = seq.completed_tasks.map(t => t.agent);
|
|
294
|
-
const remaining = seq.expected_agents.filter(a => !completedAgents.includes(a));
|
|
295
|
-
console.log(` ${statusIcon} ${colors.agent(seqId)} ${l2Icon} ${colors.dim(`[${seq.phase}]`)}`);
|
|
296
|
-
console.log(` agents: ${seq.expected_agents.map(a => completedAgents.includes(a) ? colors.success(a) : colors.dim(a)).join(', ')}`);
|
|
297
|
-
if (remaining.length > 0 && seq.status !== 'completed') {
|
|
298
|
-
console.log(` ${colors.dim(`waiting: ${remaining.join(', ')}`)}`);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
// Completed phases
|
|
303
|
-
if (state.completed_phases.length > 0) {
|
|
304
|
-
console.log(colors.subheader('\n Completed Phases'));
|
|
305
|
-
console.log(` ${state.completed_phases.join(', ')}`);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
async function setConfig(key, value) {
|
|
309
|
-
const projectRoot = await findProjectRoot();
|
|
310
|
-
if (!projectRoot)
|
|
311
|
-
throw new Error('Not a TimSquad project');
|
|
312
|
-
const state = await loadWorkflowState(projectRoot);
|
|
313
|
-
const validKeys = Object.keys(state.automation);
|
|
314
|
-
if (!validKeys.includes(key)) {
|
|
315
|
-
throw new Error(`Invalid key: ${key}. Valid: ${validKeys.join(', ')}`);
|
|
316
|
-
}
|
|
317
|
-
const boolVal = value === 'on' || value === 'true' || value === '1';
|
|
318
|
-
state.automation[key] = boolVal;
|
|
319
|
-
await saveWorkflowState(projectRoot, state);
|
|
320
|
-
printSuccess(`${key} = ${boolVal ? 'ON' : 'OFF'}`);
|
|
321
|
-
}
|
|
322
|
-
// ============================================================
|
|
323
|
-
// Internal: Called by hooks
|
|
324
|
-
// ============================================================
|
|
325
|
-
/**
|
|
326
|
-
* Prepare task context for a sub-agent.
|
|
327
|
-
* Queries daemon IPC (or disk fallback) for scope data and writes task-context.json.
|
|
328
|
-
*/
|
|
329
|
-
async function taskStart(options) {
|
|
330
|
-
const projectRoot = await findProjectRoot();
|
|
331
|
-
if (!projectRoot)
|
|
332
|
-
throw new Error('Not a TimSquad project');
|
|
333
|
-
const scopePaths = options.scope?.split(',').map(s => s.trim()) || [];
|
|
334
|
-
if (scopePaths.length === 0) {
|
|
335
|
-
printSuccess(`Task started for ${options.agent} (no scope filter)`);
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
// SCR (Single Change Rule) enforcement: detect compound tasks (11-B)
|
|
339
|
-
const scrResult = analyzeScopeComplexity(scopePaths);
|
|
340
|
-
if (scrResult.compound) {
|
|
341
|
-
console.log(colors.warning(`\n ⚠ SCR Warning: ${scrResult.reason}`));
|
|
342
|
-
if (scrResult.groups.length > 1) {
|
|
343
|
-
console.log(colors.dim(' Suggested splits:'));
|
|
344
|
-
scrResult.groups.forEach((g, i) => {
|
|
345
|
-
console.log(colors.dim(` ${i + 1}. [${g.module}] ${g.paths.join(', ')}`));
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
console.log(colors.dim(' Consider splitting into sequential single-scope tasks.\n'));
|
|
349
|
-
}
|
|
350
|
-
// Prerequisite auto-validation (11-A): check agent prerequisites against compiled specs
|
|
351
|
-
await validateAgentPrerequisites(projectRoot, options.agent);
|
|
352
|
-
// 데몬 IPC로 scope 쿼리
|
|
353
|
-
let scopedData = null;
|
|
354
|
-
try {
|
|
355
|
-
const response = await queryDaemon(projectRoot, 'scope', { paths: scopePaths });
|
|
356
|
-
scopedData = response.context;
|
|
357
|
-
}
|
|
358
|
-
catch {
|
|
359
|
-
// 데몬 미실행 — fallback으로 MetaCache 사용
|
|
360
|
-
const { MetaCache } = await import('../daemon/meta-cache.js');
|
|
361
|
-
const cache = new MetaCache(projectRoot);
|
|
362
|
-
await cache.load();
|
|
363
|
-
scopedData = cache.filterByScope(scopePaths);
|
|
364
|
-
}
|
|
365
|
-
if (scopedData && Object.keys(scopedData.files).length > 0) {
|
|
366
|
-
const ctxPath = await writeContext(projectRoot, options.agent, scopedData);
|
|
367
|
-
printSuccess(`Task context created for ${options.agent}`);
|
|
368
|
-
printKeyValue(' Context', ctxPath);
|
|
369
|
-
printKeyValue(' Files', String(Object.keys(scopedData.files).length));
|
|
370
|
-
}
|
|
371
|
-
else {
|
|
372
|
-
console.log(colors.dim(` No files matched scope: ${scopePaths.join(', ')}`));
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
/**
|
|
376
|
-
* Track a completed task in workflow state.
|
|
377
|
-
* Called by SubagentStop hook: tsq workflow track-task <agent> <task-log-path>
|
|
378
|
-
* Creates L2 sequence log inline when sequence is completed (no daemon dependency).
|
|
379
|
-
*/
|
|
380
|
-
async function trackTask(agent, taskLogPath) {
|
|
381
|
-
const projectRoot = await findProjectRoot();
|
|
382
|
-
if (!projectRoot)
|
|
383
|
-
return;
|
|
384
|
-
const state = await loadWorkflowState(projectRoot);
|
|
385
|
-
// Find matching sequence
|
|
386
|
-
const seqId = findSequenceForAgent(state, agent);
|
|
387
|
-
if (!seqId)
|
|
388
|
-
return; // No match or ambiguous — skip silently
|
|
389
|
-
const seq = state.sequences[seqId];
|
|
390
|
-
seq.completed_tasks.push({
|
|
391
|
-
agent,
|
|
392
|
-
task_log: taskLogPath,
|
|
393
|
-
completed_at: getTimestamp(),
|
|
394
|
-
});
|
|
395
|
-
// Update status
|
|
396
|
-
if (seq.status === 'pending') {
|
|
397
|
-
seq.status = 'in_progress';
|
|
398
|
-
}
|
|
399
|
-
if (isSequenceComplete(seq)) {
|
|
400
|
-
seq.status = 'completed';
|
|
401
|
-
// Inline L2 creation — don't wait for daemon or SessionEnd
|
|
402
|
-
if (state.automation.sequence_log && !seq.l2_created) {
|
|
403
|
-
try {
|
|
404
|
-
const entry = await buildSequenceLogData(projectRoot, seqId, {
|
|
405
|
-
phase: seq.phase,
|
|
406
|
-
report: seq.report_path || '(auto) no report',
|
|
407
|
-
verdict: 'proceed',
|
|
408
|
-
});
|
|
409
|
-
const seqDir = getSequencesDir(projectRoot);
|
|
410
|
-
await fs.ensureDir(seqDir);
|
|
411
|
-
await fs.writeJson(path.join(seqDir, `${seqId}.json`), entry, { spaces: 2 });
|
|
412
|
-
seq.l2_created = true;
|
|
413
|
-
await appendWorkflowLog(projectRoot, `[AUTO] L2 sequence log created: ${seqId}`);
|
|
414
|
-
}
|
|
415
|
-
catch { /* L2 creation failure — checkAndAutomate fallback will retry */ }
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
await saveWorkflowState(projectRoot, state);
|
|
419
|
-
}
|
|
420
|
-
/**
|
|
421
|
-
* Check workflow state and auto-create L2/L3/gate/retro.
|
|
422
|
-
* Called by SessionEnd hook: tsq workflow check
|
|
423
|
-
*/
|
|
424
|
-
async function checkAndAutomate() {
|
|
425
|
-
const projectRoot = await findProjectRoot();
|
|
426
|
-
if (!projectRoot)
|
|
427
|
-
return;
|
|
428
|
-
const state = await loadWorkflowState(projectRoot);
|
|
429
|
-
let stateChanged = false;
|
|
430
|
-
// 1. Auto-create L2 for completed sequences
|
|
431
|
-
if (state.automation.sequence_log) {
|
|
432
|
-
for (const [seqId, seq] of Object.entries(state.sequences)) {
|
|
433
|
-
if (seq.status === 'completed' && !seq.l2_created) {
|
|
434
|
-
try {
|
|
435
|
-
const entry = await buildSequenceLogData(projectRoot, seqId, {
|
|
436
|
-
phase: seq.phase,
|
|
437
|
-
report: seq.report_path || `(auto) no report`,
|
|
438
|
-
verdict: 'proceed',
|
|
439
|
-
});
|
|
440
|
-
const seqDir = getSequencesDir(projectRoot);
|
|
441
|
-
await fs.ensureDir(seqDir);
|
|
442
|
-
await fs.writeJson(path.join(seqDir, `${seqId}.json`), entry, { spaces: 2 });
|
|
443
|
-
seq.l2_created = true;
|
|
444
|
-
stateChanged = true;
|
|
445
|
-
// Log the automation
|
|
446
|
-
await appendWorkflowLog(projectRoot, `[AUTO] L2 sequence log created: ${seqId}`);
|
|
447
|
-
}
|
|
448
|
-
catch { /* skip on error */ }
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
// 2. Check if all sequences in current phase are completed
|
|
453
|
-
if (state.current_phase && (state.automation.phase_log || state.automation.phase_gate)) {
|
|
454
|
-
const phaseSeqs = state.current_phase.sequences;
|
|
455
|
-
const allCompleted = phaseSeqs.length > 0 && phaseSeqs.every(id => {
|
|
456
|
-
const seq = state.sequences[id];
|
|
457
|
-
return seq && seq.status === 'completed' && seq.l2_created;
|
|
458
|
-
});
|
|
459
|
-
if (allCompleted) {
|
|
460
|
-
const phaseId = state.current_phase.id;
|
|
461
|
-
// 2a. Auto phase gate
|
|
462
|
-
if (state.automation.phase_gate) {
|
|
463
|
-
try {
|
|
464
|
-
const gateResult = await buildPhaseGateData(projectRoot, phaseId);
|
|
465
|
-
await appendWorkflowLog(projectRoot, `[AUTO] Phase gate ${phaseId}: ${gateResult.can_transition ? 'PASSED' : 'BLOCKED'}`);
|
|
466
|
-
}
|
|
467
|
-
catch { /* skip */ }
|
|
468
|
-
}
|
|
469
|
-
// 2b. Auto create L3 phase log
|
|
470
|
-
if (state.automation.phase_log) {
|
|
471
|
-
const phaseDir = getPhasesDir(projectRoot);
|
|
472
|
-
const phaseLogPath = path.join(phaseDir, `${phaseId}.json`);
|
|
473
|
-
if (!await fs.pathExists(phaseLogPath)) {
|
|
474
|
-
try {
|
|
475
|
-
const phaseLog = await buildPhaseLogData(projectRoot, phaseId, phaseSeqs);
|
|
476
|
-
await fs.ensureDir(phaseDir);
|
|
477
|
-
await fs.writeJson(phaseLogPath, phaseLog, { spaces: 2 });
|
|
478
|
-
await appendWorkflowLog(projectRoot, `[AUTO] L3 phase log created: ${phaseId}`);
|
|
479
|
-
stateChanged = true;
|
|
480
|
-
}
|
|
481
|
-
catch { /* skip */ }
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
// 2c. Auto retro (basic KPT collection)
|
|
485
|
-
if (state.automation.retro) {
|
|
486
|
-
try {
|
|
487
|
-
await autoRetro(projectRoot, phaseId, phaseSeqs, state);
|
|
488
|
-
await appendWorkflowLog(projectRoot, `[AUTO] Retro generated: ${phaseId}`);
|
|
489
|
-
}
|
|
490
|
-
catch { /* skip */ }
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
// 3. Auto metrics (already handled by auto-metrics.sh, but log)
|
|
495
|
-
if (state.automation.metrics) {
|
|
496
|
-
// auto-metrics.sh handles this — no-op here
|
|
497
|
-
}
|
|
498
|
-
// 4. Pending feedback cleanup
|
|
499
|
-
if (state.automation.feedback) {
|
|
500
|
-
const feedbackDir = path.join(projectRoot, '.timsquad', 'feedback');
|
|
501
|
-
const cleanedPending = [];
|
|
502
|
-
for (const fbId of state.pending_feedback || []) {
|
|
503
|
-
const fbPath = path.join(feedbackDir, `${fbId}.json`);
|
|
504
|
-
if (await fs.pathExists(fbPath)) {
|
|
505
|
-
try {
|
|
506
|
-
const fb = await fs.readJson(fbPath);
|
|
507
|
-
if (fb.status === 'open' || fb.status === 'in_review') {
|
|
508
|
-
cleanedPending.push(fbId);
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
catch { /* skip */ }
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
if (cleanedPending.length !== (state.pending_feedback || []).length) {
|
|
515
|
-
state.pending_feedback = cleanedPending;
|
|
516
|
-
stateChanged = true;
|
|
517
|
-
}
|
|
518
|
-
if (cleanedPending.length > 0) {
|
|
519
|
-
await appendWorkflowLog(projectRoot, `[AUTO] ${cleanedPending.length} pending feedback(s) blocking phase gate`);
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
if (stateChanged) {
|
|
523
|
-
await saveWorkflowState(projectRoot, state);
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
// ============================================================
|
|
527
|
-
// Internal helpers
|
|
528
|
-
// ============================================================
|
|
529
|
-
export async function buildPhaseLogData(projectRoot, phaseId, seqIds) {
|
|
530
|
-
const seqDir = getSequencesDir(projectRoot);
|
|
531
|
-
const now = getTimestamp();
|
|
532
|
-
const seqLogs = [];
|
|
533
|
-
let completed = 0, blocked = 0;
|
|
534
|
-
for (const id of seqIds) {
|
|
535
|
-
const seqPath = path.join(seqDir, `${id}.json`);
|
|
536
|
-
if (await fs.pathExists(seqPath)) {
|
|
537
|
-
const data = await fs.readJson(seqPath);
|
|
538
|
-
seqLogs.push(data);
|
|
539
|
-
if (data.execution.status === 'completed')
|
|
540
|
-
completed++;
|
|
541
|
-
else
|
|
542
|
-
blocked++;
|
|
543
|
-
}
|
|
544
|
-
else {
|
|
545
|
-
blocked++;
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
let totalTasks = 0, totalSuccess = 0, totalRework = 0, totalFiles = 0;
|
|
549
|
-
let totalIssues1 = 0, totalIssues2 = 0, totalIssues3 = 0;
|
|
550
|
-
const durations = [];
|
|
551
|
-
for (const seq of seqLogs) {
|
|
552
|
-
totalTasks += seq.tasks.total;
|
|
553
|
-
totalSuccess += seq.tasks.success;
|
|
554
|
-
totalRework += seq.tasks.rework;
|
|
555
|
-
durations.push(seq.execution.duration_ms);
|
|
556
|
-
for (const axis of Object.values(seq.analysis)) {
|
|
557
|
-
const r = axis;
|
|
558
|
-
for (const issue of r.issues || []) {
|
|
559
|
-
if (issue.level === 1)
|
|
560
|
-
totalIssues1++;
|
|
561
|
-
else if (issue.level === 2)
|
|
562
|
-
totalIssues2++;
|
|
563
|
-
else
|
|
564
|
-
totalIssues3++;
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
for (const seqId of seqIds) {
|
|
569
|
-
const taskLogs = await loadSequenceTaskLogs(projectRoot, seqId);
|
|
570
|
-
for (const { data } of taskLogs) {
|
|
571
|
-
totalFiles += data.mechanical?.files?.length || 0;
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
const timestamps = seqLogs
|
|
575
|
-
.map(s => [new Date(s.execution.started_at).getTime(), new Date(s.execution.completed_at).getTime()])
|
|
576
|
-
.flat()
|
|
577
|
-
.filter(t => t > 0);
|
|
578
|
-
const leadTime = timestamps.length >= 2 ? Math.max(...timestamps) - Math.min(...timestamps) : 0;
|
|
579
|
-
return {
|
|
580
|
-
schema_version: LOG_SCHEMA_VERSION,
|
|
581
|
-
trace: { phase_id: phaseId },
|
|
582
|
-
execution: {
|
|
583
|
-
status: blocked > 0 ? 'aborted' : 'completed',
|
|
584
|
-
started_at: seqLogs[0]?.execution.started_at || now,
|
|
585
|
-
completed_at: now,
|
|
586
|
-
duration_ms: leadTime,
|
|
587
|
-
sessions_count: seqLogs.length,
|
|
588
|
-
},
|
|
589
|
-
sequences: { total: seqIds.length, completed, blocked, ids: seqIds },
|
|
590
|
-
aggregate_metrics: {
|
|
591
|
-
total_tasks: totalTasks,
|
|
592
|
-
task_success_rate: totalTasks > 0 ? totalSuccess / totalTasks : 0,
|
|
593
|
-
task_rework_rate: totalTasks > 0 ? totalRework / totalTasks : 0,
|
|
594
|
-
total_files_changed: totalFiles,
|
|
595
|
-
total_issues: { level_1: totalIssues1, level_2: totalIssues2, level_3: totalIssues3 },
|
|
596
|
-
ssot_conformance_rate: 0,
|
|
597
|
-
mean_sequence_duration_ms: durations.length > 0 ? durations.reduce((a, b) => a + b, 0) / durations.length : 0,
|
|
598
|
-
},
|
|
599
|
-
dora_derived: {
|
|
600
|
-
lead_time_ms: leadTime,
|
|
601
|
-
change_failure_rate: totalTasks > 0 ? (totalTasks - totalSuccess) / totalTasks : 0,
|
|
602
|
-
mean_recovery_time_ms: null,
|
|
603
|
-
},
|
|
604
|
-
planning: {
|
|
605
|
-
original_sequences: seqIds,
|
|
606
|
-
added_sequences: [],
|
|
607
|
-
removed_sequences: [],
|
|
608
|
-
scope_changes: [],
|
|
609
|
-
plan_adherence_rate: 1,
|
|
610
|
-
},
|
|
611
|
-
retrospective: { keep: [], problem: [], try: [] },
|
|
612
|
-
knowledge_extracted: [],
|
|
613
|
-
};
|
|
614
|
-
}
|
|
615
|
-
/**
|
|
616
|
-
* Auto-generate a basic retrospective from task/sequence data
|
|
617
|
-
*/
|
|
618
|
-
async function autoRetro(projectRoot, phaseId, seqIds, state) {
|
|
619
|
-
const retroDir = path.join(projectRoot, '.timsquad', 'retrospective', 'cycles');
|
|
620
|
-
await fs.ensureDir(retroDir);
|
|
621
|
-
const retroPath = path.join(retroDir, `${phaseId}-auto.md`);
|
|
622
|
-
if (await fs.pathExists(retroPath))
|
|
623
|
-
return; // Already exists
|
|
624
|
-
const keep = [];
|
|
625
|
-
const problem = [];
|
|
626
|
-
const tryItems = [];
|
|
627
|
-
// Analyze task logs for patterns
|
|
628
|
-
let totalTasks = 0, tasksWithSemantic = 0, totalRework = 0;
|
|
629
|
-
for (const seqId of seqIds) {
|
|
630
|
-
const taskLogs = await loadSequenceTaskLogs(projectRoot, seqId);
|
|
631
|
-
for (const { data } of taskLogs) {
|
|
632
|
-
totalTasks++;
|
|
633
|
-
if (hasSemantic(data.semantic))
|
|
634
|
-
tasksWithSemantic++;
|
|
635
|
-
if (data.semantic?.issues?.length) {
|
|
636
|
-
for (const issue of data.semantic.issues) {
|
|
637
|
-
if (issue.level >= 2) {
|
|
638
|
-
problem.push(`[${seqId}] ${issue.description}`);
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
const seq = state.sequences[seqId];
|
|
644
|
-
if (seq) {
|
|
645
|
-
const reworkCount = seq.completed_tasks.length - seq.expected_agents.length;
|
|
646
|
-
if (reworkCount > 0)
|
|
647
|
-
totalRework += reworkCount;
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
// Generate observations
|
|
651
|
-
const semanticCoverage = totalTasks > 0 ? Math.round((tasksWithSemantic / totalTasks) * 100) : 0;
|
|
652
|
-
if (semanticCoverage >= 80) {
|
|
653
|
-
keep.push(`Semantic coverage ${semanticCoverage}% — good documentation discipline`);
|
|
654
|
-
}
|
|
655
|
-
else if (semanticCoverage < 50) {
|
|
656
|
-
problem.push(`Semantic coverage ${semanticCoverage}% — agents not consistently enriching logs`);
|
|
657
|
-
tryItems.push('Enforce tsq log enrich in agent protocols');
|
|
658
|
-
}
|
|
659
|
-
if (totalRework === 0) {
|
|
660
|
-
keep.push('No rework detected — first-pass quality was high');
|
|
661
|
-
}
|
|
662
|
-
else {
|
|
663
|
-
problem.push(`${totalRework} rework tasks detected`);
|
|
664
|
-
tryItems.push('Review SSOT alignment before implementation starts');
|
|
665
|
-
}
|
|
666
|
-
if (seqIds.length > 0) {
|
|
667
|
-
keep.push(`${seqIds.length} sequences completed in phase ${phaseId}`);
|
|
668
|
-
}
|
|
669
|
-
// Write retro markdown
|
|
670
|
-
const content = `# Retrospective: ${phaseId} (Auto-generated)
|
|
671
|
-
|
|
672
|
-
> Generated at ${getTimestamp()} by tsq workflow check
|
|
673
|
-
|
|
674
|
-
## Keep
|
|
675
|
-
${keep.map(k => `- ${k}`).join('\n') || '- (none detected)'}
|
|
676
|
-
|
|
677
|
-
## Problem
|
|
678
|
-
${problem.map(p => `- ${p}`).join('\n') || '- (none detected)'}
|
|
679
|
-
|
|
680
|
-
## Try
|
|
681
|
-
${tryItems.map(t => `- ${t}`).join('\n') || '- (no suggestions)'}
|
|
682
|
-
|
|
683
|
-
---
|
|
684
|
-
|
|
685
|
-
## Metrics
|
|
686
|
-
- Total tasks: ${totalTasks}
|
|
687
|
-
- Semantic coverage: ${semanticCoverage}%
|
|
688
|
-
- Rework tasks: ${totalRework}
|
|
689
|
-
- Sequences: ${seqIds.join(', ')}
|
|
690
|
-
`;
|
|
691
|
-
await fs.writeFile(retroPath, content, 'utf-8');
|
|
692
|
-
}
|
|
693
|
-
/**
|
|
694
|
-
* Append to workflow automation log (for debugging/auditing)
|
|
695
|
-
*/
|
|
696
|
-
async function appendWorkflowLog(projectRoot, message) {
|
|
697
|
-
const logDir = path.join(projectRoot, '.timsquad', 'logs');
|
|
698
|
-
const logFile = path.join(logDir, 'workflow-automation.log');
|
|
699
|
-
await fs.ensureDir(logDir);
|
|
700
|
-
const line = `${getTimestamp()} ${message}\n`;
|
|
701
|
-
await fs.appendFile(logFile, line, 'utf-8');
|
|
702
|
-
}
|
|
703
|
-
function analyzeScopeComplexity(scopePaths) {
|
|
704
|
-
// Group by logical module (2nd-level dir or top-level for shallow paths)
|
|
705
|
-
const moduleMap = new Map();
|
|
706
|
-
for (const p of scopePaths) {
|
|
707
|
-
const parts = p.split('/').filter(Boolean);
|
|
708
|
-
// Use first 2 segments as module key (e.g. "src/commands", "src/lib", "tests/unit")
|
|
709
|
-
const moduleKey = parts.length >= 2 ? `${parts[0]}/${parts[1]}` : parts[0] || p;
|
|
710
|
-
if (!moduleMap.has(moduleKey))
|
|
711
|
-
moduleMap.set(moduleKey, []);
|
|
712
|
-
moduleMap.get(moduleKey).push(p);
|
|
713
|
-
}
|
|
714
|
-
const groups = [...moduleMap.entries()].map(([module, paths]) => ({ module, paths }));
|
|
715
|
-
// Exclude test-related modules from independent module count
|
|
716
|
-
const nonTestModules = groups.filter(g => !g.module.startsWith('tests/') && !g.module.startsWith('__tests__/'));
|
|
717
|
-
const topLevelDirs = new Set(scopePaths.map(p => p.split('/')[0]));
|
|
718
|
-
if (nonTestModules.length > 2) {
|
|
719
|
-
return {
|
|
720
|
-
compound: true,
|
|
721
|
-
reason: `Scope spans ${nonTestModules.length} independent modules (${nonTestModules.map(g => g.module).join(', ')})`,
|
|
722
|
-
groups,
|
|
723
|
-
};
|
|
724
|
-
}
|
|
725
|
-
if (topLevelDirs.size > 2) {
|
|
726
|
-
return {
|
|
727
|
-
compound: true,
|
|
728
|
-
reason: `Scope spans ${topLevelDirs.size} top-level directories (${[...topLevelDirs].join(', ')})`,
|
|
729
|
-
groups,
|
|
730
|
-
};
|
|
731
|
-
}
|
|
732
|
-
return { compound: false, reason: '', groups };
|
|
733
|
-
}
|
|
734
|
-
/**
|
|
735
|
-
* Validate agent prerequisites against compiled specs (11-A).
|
|
736
|
-
* Checks: agent file exists, prerequisites resolved, specs not stale.
|
|
737
|
-
*/
|
|
738
|
-
async function validateAgentPrerequisites(projectRoot, agent) {
|
|
739
|
-
const agentPath = path.join(projectRoot, '.claude', 'agents', `${agent}.md`);
|
|
740
|
-
if (!await fs.pathExists(agentPath))
|
|
741
|
-
return; // No agent file — skip validation
|
|
742
|
-
const agentContent = await fs.readFile(agentPath, 'utf-8');
|
|
743
|
-
const prereqs = parseAgentPrerequisites(agentContent);
|
|
744
|
-
if (prereqs.length === 0)
|
|
745
|
-
return; // No prerequisites declared
|
|
746
|
-
const controllerDir = path.join(projectRoot, '.claude', 'skills', 'controller');
|
|
747
|
-
const manifest = await loadManifest(controllerDir);
|
|
748
|
-
if (!manifest) {
|
|
749
|
-
console.log(colors.warning('\n ⚠ Prerequisite Warning: No compile manifest found'));
|
|
750
|
-
console.log(colors.dim(' Run `tsq compile` before delegating tasks.\n'));
|
|
751
|
-
return;
|
|
752
|
-
}
|
|
753
|
-
// Check missing prerequisites
|
|
754
|
-
const compiledSources = new Set(manifest.entries.map(e => e.source.replace('ssot/', '').replace('.md', '')));
|
|
755
|
-
const missing = prereqs.filter(p => !compiledSources.has(p));
|
|
756
|
-
if (missing.length > 0) {
|
|
757
|
-
console.log(colors.warning(`\n ⚠ Missing prerequisites for agent "${agent}":`));
|
|
758
|
-
for (const m of missing) {
|
|
759
|
-
console.log(colors.dim(` - ${m} (not compiled)`));
|
|
760
|
-
}
|
|
761
|
-
console.log(colors.dim(' Run `tsq compile` to resolve.\n'));
|
|
762
|
-
}
|
|
763
|
-
// Check stale specs
|
|
764
|
-
const staleItems = await checkStale(projectRoot, controllerDir);
|
|
765
|
-
const relevantStale = staleItems.filter(s => {
|
|
766
|
-
const source = s.source.replace('ssot/', '').replace('.md', '');
|
|
767
|
-
return prereqs.includes(source) || s.source === '*';
|
|
768
|
-
});
|
|
769
|
-
if (relevantStale.length > 0) {
|
|
770
|
-
console.log(colors.warning(`\n ⚠ Stale specs detected for agent "${agent}":`));
|
|
771
|
-
for (const s of relevantStale) {
|
|
772
|
-
console.log(colors.dim(` - ${s.source}: ${s.reason}`));
|
|
773
|
-
}
|
|
774
|
-
console.log(colors.dim(' Run `tsq compile` to refresh.\n'));
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
function formatAutomation(auto) {
|
|
778
|
-
const enabled = Object.entries(auto).filter(([, v]) => v).map(([k]) => k);
|
|
779
|
-
return enabled.length > 0 ? enabled.join(', ') : 'all OFF';
|
|
780
|
-
}
|
|
781
|
-
//# sourceMappingURL=workflow.js.map
|