timsquad 3.5.0 → 3.7.0
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 -107
- package/README.md +100 -104
- package/dist/commands/daemon.d.ts.map +1 -1
- package/dist/commands/daemon.js +48 -2
- package/dist/commands/daemon.js.map +1 -1
- package/dist/commands/init.js +46 -14
- 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/context-writer.d.ts +14 -0
- package/dist/daemon/context-writer.d.ts.map +1 -1
- package/dist/daemon/context-writer.js +29 -0
- package/dist/daemon/context-writer.js.map +1 -1
- package/dist/daemon/event-queue.d.ts +7 -11
- package/dist/daemon/event-queue.d.ts.map +1 -1
- package/dist/daemon/event-queue.js +78 -118
- package/dist/daemon/event-queue.js.map +1 -1
- package/dist/daemon/file-watcher.d.ts +14 -8
- package/dist/daemon/file-watcher.d.ts.map +1 -1
- package/dist/daemon/file-watcher.js +78 -41
- package/dist/daemon/file-watcher.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 +54 -47
- package/dist/daemon/index.js.map +1 -1
- package/dist/index.js +3 -41
- package/dist/index.js.map +1 -1
- package/dist/lib/agent-generator.d.ts.map +1 -1
- package/dist/lib/agent-generator.js +21 -10
- package/dist/lib/agent-generator.js.map +1 -1
- package/dist/lib/compile-rules.d.ts +2 -0
- package/dist/lib/compile-rules.d.ts.map +1 -1
- package/dist/lib/compile-rules.js +39 -4
- package/dist/lib/compile-rules.js.map +1 -1
- package/dist/lib/compiler.d.ts +22 -1
- package/dist/lib/compiler.d.ts.map +1 -1
- package/dist/lib/compiler.js +178 -12
- package/dist/lib/compiler.js.map +1 -1
- package/dist/lib/config.d.ts +3 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +17 -2
- package/dist/lib/config.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 +1 -1
- package/dist/lib/skill-generator.d.ts.map +1 -1
- package/dist/lib/skill-generator.js +19 -44
- package/dist/lib/skill-generator.js.map +1 -1
- package/dist/lib/ssot-map.d.ts +31 -0
- package/dist/lib/ssot-map.d.ts.map +1 -0
- package/dist/lib/ssot-map.js +79 -0
- package/dist/lib/ssot-map.js.map +1 -0
- package/dist/lib/template.d.ts +10 -3
- package/dist/lib/template.d.ts.map +1 -1
- package/dist/lib/template.js +137 -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/lib/workflow-state.d.ts +1 -1
- package/dist/lib/workflow-state.d.ts.map +1 -1
- package/dist/lib/workflow-state.js +1 -1
- package/dist/lib/workflow-state.js.map +1 -1
- package/dist/types/config.d.ts +10 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +46 -41
- 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/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/meta-index.d.ts +8 -0
- package/dist/types/meta-index.d.ts.map +1 -1
- package/dist/types/project.d.ts +6 -1
- 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 +30 -0
- package/dist/types/ssot-map.d.ts.map +1 -0
- package/dist/types/ssot-map.js +6 -0
- package/dist/types/ssot-map.js.map +1 -0
- 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 +45 -0
- 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/_shared/naming-conventions.md +49 -0
- package/templates/base/skills/_template/SKILL.md +31 -17
- 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/tsq-coding/SKILL.md +65 -0
- package/templates/base/skills/tsq-coding/rules/async-patterns.md +81 -0
- package/templates/base/skills/tsq-coding/rules/code-organization.md +80 -0
- package/templates/base/skills/tsq-coding/rules/error-handling.md +76 -0
- package/templates/base/skills/tsq-coding/rules/type-safety.md +85 -0
- package/templates/base/skills/tsq-controller/SKILL.md +81 -0
- package/templates/base/skills/tsq-controller/memory/.gitkeep +0 -0
- package/templates/base/skills/{mobile/dart → tsq-dart}/SKILL.md +5 -3
- package/templates/base/skills/{database → tsq-database}/SKILL.md +13 -27
- package/templates/base/skills/tsq-database/rules/query-optimization.md +32 -0
- package/templates/base/skills/tsq-database/rules/supabase-patterns.md +94 -0
- 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-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/tsq-product-audit/SKILL.md +113 -0
- package/templates/base/skills/tsq-product-audit/checklists/01-security.md +86 -0
- package/templates/base/skills/tsq-product-audit/checklists/02-performance.md +67 -0
- package/templates/base/skills/tsq-product-audit/checklists/03-seo.md +46 -0
- package/templates/base/skills/tsq-product-audit/checklists/04-accessibility.md +66 -0
- package/templates/base/skills/tsq-product-audit/checklists/05-ui-ux.md +50 -0
- package/templates/base/skills/tsq-product-audit/checklists/06-architecture.md +53 -0
- package/templates/base/skills/tsq-product-audit/checklists/07-functional-requirements.md +55 -0
- package/templates/base/skills/tsq-product-audit/rules/audit-protocol.md +136 -0
- package/templates/base/skills/tsq-product-audit/rules/false-positive-guard.md +81 -0
- package/templates/base/skills/tsq-product-audit/rules/scoring-criteria.md +113 -0
- package/templates/base/skills/tsq-product-audit/templates/improvement-plan-template.md +60 -0
- package/templates/base/skills/tsq-product-audit/templates/report-template.md +88 -0
- package/templates/base/skills/tsq-prompt/SKILL.md +86 -0
- package/templates/base/skills/tsq-protocol/SKILL.md +101 -33
- 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/tsq-retro/references/improvement-template.md +26 -0
- package/templates/base/skills/tsq-security/SKILL.md +66 -0
- package/templates/base/skills/tsq-security/rules/auth-patterns.md +62 -0
- package/templates/base/skills/tsq-security/rules/dependency-security.md +69 -0
- package/templates/base/skills/tsq-security/rules/input-validation.md +68 -0
- package/templates/base/skills/tsq-security/rules/secrets-management.md +65 -0
- package/templates/base/skills/tsq-spec/SKILL.md +58 -0
- 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 +12 -3
- package/templates/base/skills/tsq-testing/SKILL.md +69 -0
- package/templates/base/skills/tsq-testing/references/e2e-stability.md +33 -0
- package/templates/base/skills/{typescript → tsq-typescript}/SKILL.md +5 -11
- 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/test-spec.template.md +11 -1
- package/templates/base/timsquad/ssot/ui-ux-spec.template.md +43 -3
- package/templates/base/timsquad/ssot-map.template.yaml +69 -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/api-conventions.md +12 -0
- 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/librarian-constraints.md +11 -0
- 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/test-conventions.md +13 -0
- 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 +110 -0
- package/templates/platforms/claude-code/scripts/check-capability.sh +68 -0
- package/templates/platforms/claude-code/scripts/completion-guard.sh +134 -14
- package/templates/platforms/claude-code/scripts/context-restore.sh +95 -0
- package/templates/platforms/claude-code/scripts/e2e-commit-gate.sh +70 -0
- package/templates/platforms/claude-code/scripts/e2e-marker.sh +51 -0
- package/templates/platforms/claude-code/scripts/phase-guard.sh +3 -6
- package/templates/platforms/claude-code/scripts/pre-compact.sh +70 -0
- 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 +28 -56
- 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/compile.d.ts +0 -3
- package/dist/commands/compile.d.ts.map +0 -1
- package/dist/commands/compile.js +0 -170
- 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 -1436
- 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 -401
- 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 -828
- 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 -3
- package/dist/commands/workflow.d.ts.map +0 -1
- package/dist/commands/workflow.js +0 -607
- package/dist/commands/workflow.js.map +0 -1
- package/templates/base/skills/coding/SKILL.md +0 -47
- package/templates/base/skills/controller/SKILL.md +0 -111
- package/templates/base/skills/prompt-engineering/SKILL.md +0 -103
- package/templates/base/skills/retrospective/SKILL.md +0 -102
- package/templates/base/skills/security/SKILL.md +0 -55
- package/templates/base/skills/testing/SKILL.md +0 -63
- 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-rules.json +0 -85
- package/templates/platforms/claude-code/scripts/skill-suggest.sh +0 -105
- /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/patterns.md +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/{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/{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/{security → tsq-security}/rules/owasp-examples.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/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
package/dist/commands/retro.js
DELETED
|
@@ -1,828 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import fs from 'fs-extra';
|
|
3
|
-
import { execSync, execFileSync } from 'child_process';
|
|
4
|
-
import { colors, printHeader, printError, printSuccess, printKeyValue } from '../utils/colors.js';
|
|
5
|
-
import { findProjectRoot, getProjectInfo } from '../lib/project.js';
|
|
6
|
-
import { exists, writeFile, listFiles } from '../utils/fs.js';
|
|
7
|
-
import { getTimestamp, getDateString } from '../utils/date.js';
|
|
8
|
-
import { promptText, promptConfirm } from '../utils/prompts.js';
|
|
9
|
-
export function registerRetroCommand(program) {
|
|
10
|
-
const retroCmd = program
|
|
11
|
-
.command('retro')
|
|
12
|
-
.description('Retrospective learning system');
|
|
13
|
-
// tsq retro phase <phase-name>
|
|
14
|
-
retroCmd
|
|
15
|
-
.command('phase <phase>')
|
|
16
|
-
.description('Run phase-level retrospective (saves locally)')
|
|
17
|
-
.action(async (phase) => {
|
|
18
|
-
try {
|
|
19
|
-
await runPhaseRetro(phase);
|
|
20
|
-
}
|
|
21
|
-
catch (error) {
|
|
22
|
-
printError(error.message);
|
|
23
|
-
process.exit(1);
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
// tsq retro start
|
|
27
|
-
retroCmd
|
|
28
|
-
.command('start')
|
|
29
|
-
.description('Start a new retrospective cycle')
|
|
30
|
-
.action(async () => {
|
|
31
|
-
try {
|
|
32
|
-
await startRetro();
|
|
33
|
-
}
|
|
34
|
-
catch (error) {
|
|
35
|
-
printError(error.message);
|
|
36
|
-
process.exit(1);
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
// tsq retro collect
|
|
40
|
-
retroCmd
|
|
41
|
-
.command('collect')
|
|
42
|
-
.description('Collect logs and metrics')
|
|
43
|
-
.action(async () => {
|
|
44
|
-
try {
|
|
45
|
-
await collectRetro();
|
|
46
|
-
}
|
|
47
|
-
catch (error) {
|
|
48
|
-
printError(error.message);
|
|
49
|
-
process.exit(1);
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
// tsq retro analyze
|
|
53
|
-
retroCmd
|
|
54
|
-
.command('analyze')
|
|
55
|
-
.description('Analyze patterns (requires Claude)')
|
|
56
|
-
.action(async () => {
|
|
57
|
-
try {
|
|
58
|
-
await analyzeRetro();
|
|
59
|
-
}
|
|
60
|
-
catch (error) {
|
|
61
|
-
printError(error.message);
|
|
62
|
-
process.exit(1);
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
// tsq retro report
|
|
66
|
-
retroCmd
|
|
67
|
-
.command('report')
|
|
68
|
-
.description('Generate aggregated report + create GitHub Issue')
|
|
69
|
-
.option('--local', 'Skip GitHub Issue creation')
|
|
70
|
-
.action(async (options) => {
|
|
71
|
-
try {
|
|
72
|
-
await generateReport(options.local);
|
|
73
|
-
}
|
|
74
|
-
catch (error) {
|
|
75
|
-
printError(error.message);
|
|
76
|
-
process.exit(1);
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
// tsq retro apply
|
|
80
|
-
retroCmd
|
|
81
|
-
.command('apply')
|
|
82
|
-
.description('Apply improvements')
|
|
83
|
-
.action(async () => {
|
|
84
|
-
try {
|
|
85
|
-
await applyImprovements();
|
|
86
|
-
}
|
|
87
|
-
catch (error) {
|
|
88
|
-
printError(error.message);
|
|
89
|
-
process.exit(1);
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
// tsq retro auto
|
|
93
|
-
retroCmd
|
|
94
|
-
.command('auto')
|
|
95
|
-
.description('Run full retrospective cycle automatically (start → collect → report → apply)')
|
|
96
|
-
.option('--local', 'Skip GitHub Issue creation')
|
|
97
|
-
.action(async (options) => {
|
|
98
|
-
try {
|
|
99
|
-
await runAutoRetro(options.local);
|
|
100
|
-
}
|
|
101
|
-
catch (error) {
|
|
102
|
-
printError(error.message);
|
|
103
|
-
process.exit(1);
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
// tsq retro status
|
|
107
|
-
retroCmd
|
|
108
|
-
.command('status')
|
|
109
|
-
.description('Show retrospective status')
|
|
110
|
-
.action(async () => {
|
|
111
|
-
try {
|
|
112
|
-
await showRetroStatus();
|
|
113
|
-
}
|
|
114
|
-
catch (error) {
|
|
115
|
-
printError(error.message);
|
|
116
|
-
process.exit(1);
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
// ============================================================
|
|
121
|
-
// Phase-level retro
|
|
122
|
-
// ============================================================
|
|
123
|
-
const VALID_PHASES = ['planning', 'design', 'implementation', 'review', 'security', 'deployment'];
|
|
124
|
-
async function runPhaseRetro(phase) {
|
|
125
|
-
if (!VALID_PHASES.includes(phase)) {
|
|
126
|
-
throw new Error(`Invalid phase: ${phase}. Valid: ${VALID_PHASES.join(', ')}`);
|
|
127
|
-
}
|
|
128
|
-
const projectRoot = await findProjectRoot();
|
|
129
|
-
if (!projectRoot) {
|
|
130
|
-
throw new Error('Not a TimSquad project');
|
|
131
|
-
}
|
|
132
|
-
const project = await getProjectInfo(projectRoot);
|
|
133
|
-
printHeader(`Phase Retrospective: ${phase}`);
|
|
134
|
-
console.log(colors.dim('KPT 프레임워크로 Phase 회고를 진행합니다.\n'));
|
|
135
|
-
// Interactive KPT input
|
|
136
|
-
console.log(colors.header('Keep (잘 된 것)'));
|
|
137
|
-
const keepInput = await promptText('쉼표로 구분하여 입력:', '');
|
|
138
|
-
const keep = keepInput.split(',').map(s => s.trim()).filter(Boolean);
|
|
139
|
-
console.log('');
|
|
140
|
-
console.log(colors.header('Problem (문제점)'));
|
|
141
|
-
const problemInput = await promptText('쉼표로 구분하여 입력:', '');
|
|
142
|
-
const problem = problemInput.split(',').map(s => s.trim()).filter(Boolean);
|
|
143
|
-
console.log('');
|
|
144
|
-
console.log(colors.header('Try (다음에 시도할 것)'));
|
|
145
|
-
const tryInput = await promptText('쉼표로 구분하여 입력:', '');
|
|
146
|
-
const tryNext = tryInput.split(',').map(s => s.trim()).filter(Boolean);
|
|
147
|
-
// Create phase retro entry
|
|
148
|
-
const entry = {
|
|
149
|
-
id: `PR-${phase}-${Date.now()}`,
|
|
150
|
-
timestamp: getTimestamp(),
|
|
151
|
-
project: project.name,
|
|
152
|
-
phase,
|
|
153
|
-
keep,
|
|
154
|
-
problem,
|
|
155
|
-
try_next: tryNext,
|
|
156
|
-
tags: [phase, project.type],
|
|
157
|
-
};
|
|
158
|
-
// Save locally
|
|
159
|
-
const feedbackDir = path.join(projectRoot, '.timsquad', 'feedback');
|
|
160
|
-
await fs.ensureDir(feedbackDir);
|
|
161
|
-
const fileName = `phase-${phase}-${getDateString()}.json`;
|
|
162
|
-
await fs.writeJson(path.join(feedbackDir, fileName), entry, { spaces: 2 });
|
|
163
|
-
console.log('');
|
|
164
|
-
printSuccess(`Phase 회고 저장: .timsquad/feedback/${fileName}`);
|
|
165
|
-
// Show summary
|
|
166
|
-
console.log('');
|
|
167
|
-
printKeyValue('Keep', keep.length > 0 ? keep.join(', ') : '(없음)');
|
|
168
|
-
printKeyValue('Problem', problem.length > 0 ? problem.join(', ') : '(없음)');
|
|
169
|
-
printKeyValue('Try', tryNext.length > 0 ? tryNext.join(', ') : '(없음)');
|
|
170
|
-
console.log(colors.dim('\n전체 리포트 생성: tsq retro report'));
|
|
171
|
-
}
|
|
172
|
-
// ============================================================
|
|
173
|
-
// Existing retro flow
|
|
174
|
-
// ============================================================
|
|
175
|
-
async function getRetroState(projectRoot) {
|
|
176
|
-
const statePath = path.join(projectRoot, '.timsquad', 'retrospective', 'state.json');
|
|
177
|
-
if (!await exists(statePath)) {
|
|
178
|
-
return { currentCycle: 0, status: 'idle' };
|
|
179
|
-
}
|
|
180
|
-
return fs.readJson(statePath);
|
|
181
|
-
}
|
|
182
|
-
async function saveRetroState(projectRoot, state) {
|
|
183
|
-
const statePath = path.join(projectRoot, '.timsquad', 'retrospective', 'state.json');
|
|
184
|
-
await fs.ensureDir(path.dirname(statePath));
|
|
185
|
-
await fs.writeJson(statePath, state, { spaces: 2 });
|
|
186
|
-
}
|
|
187
|
-
async function startRetro() {
|
|
188
|
-
const projectRoot = await findProjectRoot();
|
|
189
|
-
if (!projectRoot) {
|
|
190
|
-
throw new Error('Not a TimSquad project');
|
|
191
|
-
}
|
|
192
|
-
const state = await getRetroState(projectRoot);
|
|
193
|
-
if (state.status !== 'idle') {
|
|
194
|
-
throw new Error(`Retrospective cycle ${state.currentCycle} is already in progress (${state.status})`);
|
|
195
|
-
}
|
|
196
|
-
state.currentCycle += 1;
|
|
197
|
-
state.status = 'collecting';
|
|
198
|
-
state.startedAt = getTimestamp();
|
|
199
|
-
await saveRetroState(projectRoot, state);
|
|
200
|
-
printSuccess(`Started retrospective cycle ${state.currentCycle}`);
|
|
201
|
-
console.log(colors.dim('\nNext: Run "tsq retro collect" to collect metrics'));
|
|
202
|
-
}
|
|
203
|
-
async function collectRetro() {
|
|
204
|
-
const projectRoot = await findProjectRoot();
|
|
205
|
-
if (!projectRoot) {
|
|
206
|
-
throw new Error('Not a TimSquad project');
|
|
207
|
-
}
|
|
208
|
-
const state = await getRetroState(projectRoot);
|
|
209
|
-
if (state.status !== 'collecting') {
|
|
210
|
-
throw new Error('No active retrospective. Run "tsq retro start" first');
|
|
211
|
-
}
|
|
212
|
-
printHeader(`Collecting Metrics - Cycle ${state.currentCycle}`);
|
|
213
|
-
// Collect log stats
|
|
214
|
-
const logsDir = path.join(projectRoot, '.timsquad', 'logs');
|
|
215
|
-
const logFiles = await listFiles('*.md', logsDir);
|
|
216
|
-
const recentLogs = logFiles.filter(f => !f.startsWith('_'));
|
|
217
|
-
// Count by agent
|
|
218
|
-
const agentStats = {};
|
|
219
|
-
for (const file of recentLogs) {
|
|
220
|
-
const match = file.match(/-([a-z]+)\.md$/);
|
|
221
|
-
if (match) {
|
|
222
|
-
const agent = match[1];
|
|
223
|
-
agentStats[agent] = (agentStats[agent] || 0) + 1;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
// Create metrics JSON
|
|
227
|
-
const metrics = {
|
|
228
|
-
cycle: state.currentCycle,
|
|
229
|
-
collectedAt: getTimestamp(),
|
|
230
|
-
raw_data: {
|
|
231
|
-
log_files: recentLogs.length,
|
|
232
|
-
agents: agentStats,
|
|
233
|
-
},
|
|
234
|
-
summary: {
|
|
235
|
-
total_logs: recentLogs.length,
|
|
236
|
-
agents_active: Object.keys(agentStats).length,
|
|
237
|
-
},
|
|
238
|
-
};
|
|
239
|
-
const metricsPath = path.join(projectRoot, '.timsquad', 'retrospective', 'metrics', `cycle-${state.currentCycle}.json`);
|
|
240
|
-
await fs.ensureDir(path.dirname(metricsPath));
|
|
241
|
-
await fs.writeJson(metricsPath, metrics, { spaces: 2 });
|
|
242
|
-
state.status = 'analyzing';
|
|
243
|
-
state.collectedAt = getTimestamp();
|
|
244
|
-
await saveRetroState(projectRoot, state);
|
|
245
|
-
printSuccess('Metrics collected');
|
|
246
|
-
printKeyValue('Log files', String(recentLogs.length));
|
|
247
|
-
printKeyValue('Active agents', String(Object.keys(agentStats).length));
|
|
248
|
-
console.log(colors.dim('\nNext: Run "tsq retro analyze" to analyze patterns'));
|
|
249
|
-
}
|
|
250
|
-
async function analyzeRetro() {
|
|
251
|
-
const projectRoot = await findProjectRoot();
|
|
252
|
-
if (!projectRoot) {
|
|
253
|
-
throw new Error('Not a TimSquad project');
|
|
254
|
-
}
|
|
255
|
-
const state = await getRetroState(projectRoot);
|
|
256
|
-
if (state.status !== 'analyzing') {
|
|
257
|
-
throw new Error('Collect metrics first. Run "tsq retro collect"');
|
|
258
|
-
}
|
|
259
|
-
printHeader(`Analyzing Patterns - Cycle ${state.currentCycle}`);
|
|
260
|
-
const analysis = await runPatternAnalysis(projectRoot, state.currentCycle);
|
|
261
|
-
// 결과 저장
|
|
262
|
-
const analysisDir = path.join(projectRoot, '.timsquad', 'retrospective', 'analysis');
|
|
263
|
-
await fs.ensureDir(analysisDir);
|
|
264
|
-
await fs.writeJson(path.join(analysisDir, `cycle-${state.currentCycle}-analysis.json`), analysis, { spaces: 2 });
|
|
265
|
-
state.status = 'reporting';
|
|
266
|
-
state.analyzedAt = getTimestamp();
|
|
267
|
-
await saveRetroState(projectRoot, state);
|
|
268
|
-
// 결과 출력
|
|
269
|
-
printSuccess('Pattern analysis completed');
|
|
270
|
-
printKeyValue('Sessions analyzed', String(analysis.sessions));
|
|
271
|
-
printKeyValue('Agents', String(Object.keys(analysis.agents).length));
|
|
272
|
-
printKeyValue('Flags', String(analysis.flags.length));
|
|
273
|
-
if (analysis.flags.length > 0) {
|
|
274
|
-
console.log(colors.warning('\n⚠ Detected issues:'));
|
|
275
|
-
for (const flag of analysis.flags) {
|
|
276
|
-
console.log(colors.dim(` - [${flag.severity}] ${flag.message}`));
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
console.log(colors.dim('\nNext: Run "tsq retro report" to generate report'));
|
|
280
|
-
}
|
|
281
|
-
async function runPatternAnalysis(projectRoot, cycle) {
|
|
282
|
-
const sessionsDir = path.join(projectRoot, '.timsquad', 'logs', 'sessions');
|
|
283
|
-
const agentStats = {};
|
|
284
|
-
const toolStats = {};
|
|
285
|
-
const fileModCounts = {};
|
|
286
|
-
let sessionCount = 0;
|
|
287
|
-
// 세션 로그 파싱
|
|
288
|
-
if (await exists(sessionsDir)) {
|
|
289
|
-
const logFiles = await listFiles('*.jsonl', sessionsDir);
|
|
290
|
-
sessionCount = logFiles.length;
|
|
291
|
-
for (const file of logFiles) {
|
|
292
|
-
try {
|
|
293
|
-
const content = await fs.readFile(path.join(sessionsDir, file), 'utf-8');
|
|
294
|
-
for (const line of content.split('\n')) {
|
|
295
|
-
const trimmed = line.trim();
|
|
296
|
-
if (!trimmed)
|
|
297
|
-
continue;
|
|
298
|
-
try {
|
|
299
|
-
const ev = JSON.parse(trimmed);
|
|
300
|
-
// 에이전트 통계
|
|
301
|
-
if (ev.event === 'SubagentStart' && ev.detail?.subagent_type) {
|
|
302
|
-
const agent = ev.detail.subagent_type;
|
|
303
|
-
if (!agentStats[agent])
|
|
304
|
-
agentStats[agent] = { calls: 0, failures: 0 };
|
|
305
|
-
agentStats[agent].calls++;
|
|
306
|
-
}
|
|
307
|
-
// 도구 통계
|
|
308
|
-
if (ev.event === 'PostToolUse' && ev.tool) {
|
|
309
|
-
if (!toolStats[ev.tool])
|
|
310
|
-
toolStats[ev.tool] = { uses: 0, failures: 0 };
|
|
311
|
-
toolStats[ev.tool].uses++;
|
|
312
|
-
}
|
|
313
|
-
if (ev.event === 'PostToolUseFailure' && ev.tool) {
|
|
314
|
-
if (!toolStats[ev.tool])
|
|
315
|
-
toolStats[ev.tool] = { uses: 0, failures: 0 };
|
|
316
|
-
toolStats[ev.tool].failures++;
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
catch { /* skip malformed line */ }
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
catch { /* skip unreadable file */ }
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
// L1 태스크 로그에서 rework 파일 탐지
|
|
326
|
-
const tasksDir = path.join(projectRoot, '.timsquad', 'logs', 'tasks');
|
|
327
|
-
if (await exists(tasksDir)) {
|
|
328
|
-
const taskFiles = await listFiles('*.json', tasksDir);
|
|
329
|
-
for (const file of taskFiles) {
|
|
330
|
-
try {
|
|
331
|
-
const task = await fs.readJson(path.join(tasksDir, file));
|
|
332
|
-
if (task.mechanical?.files) {
|
|
333
|
-
for (const f of task.mechanical.files) {
|
|
334
|
-
fileModCounts[f.path] = (fileModCounts[f.path] || 0) + 1;
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
// 에이전트 실패 추적 (status !== 'completed')
|
|
338
|
-
if (task.agent && task.status !== 'completed') {
|
|
339
|
-
if (!agentStats[task.agent])
|
|
340
|
-
agentStats[task.agent] = { calls: 0, failures: 0 };
|
|
341
|
-
agentStats[task.agent].failures++;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
catch { /* skip */ }
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
// rework 파일 (3회+ 수정)
|
|
348
|
-
const reworkFiles = Object.entries(fileModCounts)
|
|
349
|
-
.filter(([, count]) => count >= 3)
|
|
350
|
-
.sort(([, a], [, b]) => b - a)
|
|
351
|
-
.map(([filePath, modifications]) => ({ path: filePath, modifications }));
|
|
352
|
-
// 에이전트별 실패율 계산
|
|
353
|
-
const agents = {};
|
|
354
|
-
for (const [name, stat] of Object.entries(agentStats)) {
|
|
355
|
-
const total = stat.calls + stat.failures;
|
|
356
|
-
agents[name] = {
|
|
357
|
-
calls: stat.calls,
|
|
358
|
-
failures: stat.failures,
|
|
359
|
-
failureRate: total > 0 ? Math.round((stat.failures / total) * 100) : 0,
|
|
360
|
-
};
|
|
361
|
-
}
|
|
362
|
-
// 도구별 실패율 계산
|
|
363
|
-
const tools = {};
|
|
364
|
-
for (const [name, stat] of Object.entries(toolStats)) {
|
|
365
|
-
const total = stat.uses + stat.failures;
|
|
366
|
-
tools[name] = {
|
|
367
|
-
uses: stat.uses,
|
|
368
|
-
failures: stat.failures,
|
|
369
|
-
failureRate: total > 0 ? Math.round((stat.failures / total) * 100) : 0,
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
// 이상 탐지 플래그
|
|
373
|
-
const flags = [];
|
|
374
|
-
// 에이전트 실패율 > 20%
|
|
375
|
-
for (const [name, stat] of Object.entries(agents)) {
|
|
376
|
-
if (stat.failureRate > 20 && (stat.calls + stat.failures) >= 3) {
|
|
377
|
-
flags.push({
|
|
378
|
-
severity: 'warn',
|
|
379
|
-
category: 'agent-failure',
|
|
380
|
-
message: `Agent "${name}" failure rate ${stat.failureRate}% (${stat.failures}/${stat.calls + stat.failures})`,
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
// 도구 실패율 > 30% (3회 이상 사용)
|
|
385
|
-
for (const [name, stat] of Object.entries(tools)) {
|
|
386
|
-
if (stat.failureRate > 30 && (stat.uses + stat.failures) >= 3) {
|
|
387
|
-
flags.push({
|
|
388
|
-
severity: 'warn',
|
|
389
|
-
category: 'tool-failure',
|
|
390
|
-
message: `Tool "${name}" failure rate ${stat.failureRate}% (${stat.failures}/${stat.uses + stat.failures})`,
|
|
391
|
-
});
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
// rework 파일
|
|
395
|
-
for (const rw of reworkFiles.slice(0, 5)) {
|
|
396
|
-
flags.push({
|
|
397
|
-
severity: 'info',
|
|
398
|
-
category: 'rework',
|
|
399
|
-
message: `File "${rw.path}" modified ${rw.modifications} times (possible rework)`,
|
|
400
|
-
});
|
|
401
|
-
}
|
|
402
|
-
return {
|
|
403
|
-
cycle,
|
|
404
|
-
analyzedAt: getTimestamp(),
|
|
405
|
-
sessions: sessionCount,
|
|
406
|
-
agents,
|
|
407
|
-
tools,
|
|
408
|
-
reworkFiles,
|
|
409
|
-
flags,
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
// ============================================================
|
|
413
|
-
// Aggregated report + GitHub Issue
|
|
414
|
-
// ============================================================
|
|
415
|
-
async function loadFeedbackEntries(projectRoot) {
|
|
416
|
-
const feedbackDir = path.join(projectRoot, '.timsquad', 'feedback');
|
|
417
|
-
if (!await exists(feedbackDir))
|
|
418
|
-
return [];
|
|
419
|
-
const files = await listFiles('FB-*.json', feedbackDir);
|
|
420
|
-
const entries = [];
|
|
421
|
-
for (const file of files) {
|
|
422
|
-
try {
|
|
423
|
-
const data = await fs.readJson(path.join(feedbackDir, file));
|
|
424
|
-
entries.push(data);
|
|
425
|
-
}
|
|
426
|
-
catch {
|
|
427
|
-
// skip invalid files
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
return entries.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
431
|
-
}
|
|
432
|
-
async function loadPhaseRetros(projectRoot) {
|
|
433
|
-
const feedbackDir = path.join(projectRoot, '.timsquad', 'feedback');
|
|
434
|
-
if (!await exists(feedbackDir))
|
|
435
|
-
return [];
|
|
436
|
-
const files = await listFiles('phase-*.json', feedbackDir);
|
|
437
|
-
const entries = [];
|
|
438
|
-
for (const file of files) {
|
|
439
|
-
try {
|
|
440
|
-
const data = await fs.readJson(path.join(feedbackDir, file));
|
|
441
|
-
entries.push(data);
|
|
442
|
-
}
|
|
443
|
-
catch {
|
|
444
|
-
// skip invalid files
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
return entries.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
448
|
-
}
|
|
449
|
-
function buildAggregatedReport(project, phases, feedbacks) {
|
|
450
|
-
const byLevel = {};
|
|
451
|
-
const byPhase = {};
|
|
452
|
-
const issueCount = {};
|
|
453
|
-
for (const fb of feedbacks) {
|
|
454
|
-
const levelKey = `Level ${fb.level ?? '?'}`;
|
|
455
|
-
byLevel[levelKey] = (byLevel[levelKey] || 0) + 1;
|
|
456
|
-
if (fb.phase) {
|
|
457
|
-
byPhase[fb.phase] = (byPhase[fb.phase] || 0) + 1;
|
|
458
|
-
}
|
|
459
|
-
if (fb.trigger) {
|
|
460
|
-
issueCount[fb.trigger] = (issueCount[fb.trigger] || 0) + 1;
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
for (const pr of phases) {
|
|
464
|
-
byPhase[pr.phase] = (byPhase[pr.phase] || 0) + pr.problem.length;
|
|
465
|
-
for (const p of pr.problem) {
|
|
466
|
-
issueCount[p] = (issueCount[p] || 0) + 1;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
const topIssues = Object.entries(issueCount)
|
|
470
|
-
.sort(([, a], [, b]) => b - a)
|
|
471
|
-
.slice(0, 5)
|
|
472
|
-
.map(([issue]) => issue);
|
|
473
|
-
const timestamps = [
|
|
474
|
-
...phases.map(p => p.timestamp),
|
|
475
|
-
...feedbacks.map(f => f.timestamp),
|
|
476
|
-
].sort();
|
|
477
|
-
return {
|
|
478
|
-
project: project.name,
|
|
479
|
-
generated_at: getTimestamp(),
|
|
480
|
-
period: {
|
|
481
|
-
start: timestamps[0] || getTimestamp(),
|
|
482
|
-
end: timestamps[timestamps.length - 1] || getTimestamp(),
|
|
483
|
-
},
|
|
484
|
-
phases,
|
|
485
|
-
feedbacks,
|
|
486
|
-
summary: {
|
|
487
|
-
total_feedbacks: feedbacks.length,
|
|
488
|
-
by_level: byLevel,
|
|
489
|
-
by_phase: byPhase,
|
|
490
|
-
top_issues: topIssues,
|
|
491
|
-
},
|
|
492
|
-
};
|
|
493
|
-
}
|
|
494
|
-
function renderReportMarkdown(report) {
|
|
495
|
-
const lines = [];
|
|
496
|
-
lines.push(`# [retro] ${report.project} - Retrospective Report`);
|
|
497
|
-
lines.push('');
|
|
498
|
-
lines.push(`> Generated: ${report.generated_at}`);
|
|
499
|
-
lines.push(`> Period: ${report.period.start?.split('T')[0] ?? 'N/A'} ~ ${report.period.end?.split('T')[0] ?? 'N/A'}`);
|
|
500
|
-
lines.push('');
|
|
501
|
-
// Summary
|
|
502
|
-
lines.push('## Summary');
|
|
503
|
-
lines.push('');
|
|
504
|
-
lines.push('| Metric | Value |');
|
|
505
|
-
lines.push('|--------|-------|');
|
|
506
|
-
lines.push(`| Phase retros | ${report.phases.length} |`);
|
|
507
|
-
lines.push(`| Feedbacks | ${report.summary.total_feedbacks} |`);
|
|
508
|
-
for (const [level, count] of Object.entries(report.summary.by_level)) {
|
|
509
|
-
lines.push(`| ${level} | ${count} |`);
|
|
510
|
-
}
|
|
511
|
-
lines.push('');
|
|
512
|
-
// Top issues
|
|
513
|
-
if (report.summary.top_issues.length > 0) {
|
|
514
|
-
lines.push('## Top Issues');
|
|
515
|
-
lines.push('');
|
|
516
|
-
for (const issue of report.summary.top_issues) {
|
|
517
|
-
lines.push(`- ${issue}`);
|
|
518
|
-
}
|
|
519
|
-
lines.push('');
|
|
520
|
-
}
|
|
521
|
-
// Phase retros
|
|
522
|
-
if (report.phases.length > 0) {
|
|
523
|
-
lines.push('## Phase Retrospectives');
|
|
524
|
-
lines.push('');
|
|
525
|
-
for (const phase of report.phases) {
|
|
526
|
-
lines.push(`### ${phase.phase} (${phase.timestamp?.split('T')[0] ?? 'N/A'})`);
|
|
527
|
-
lines.push('');
|
|
528
|
-
if (phase.keep.length > 0) {
|
|
529
|
-
lines.push('**Keep:**');
|
|
530
|
-
for (const k of phase.keep)
|
|
531
|
-
lines.push(`- ${k}`);
|
|
532
|
-
lines.push('');
|
|
533
|
-
}
|
|
534
|
-
if (phase.problem.length > 0) {
|
|
535
|
-
lines.push('**Problem:**');
|
|
536
|
-
for (const p of phase.problem)
|
|
537
|
-
lines.push(`- ${p}`);
|
|
538
|
-
lines.push('');
|
|
539
|
-
}
|
|
540
|
-
if (phase.try_next.length > 0) {
|
|
541
|
-
lines.push('**Try:**');
|
|
542
|
-
for (const t of phase.try_next)
|
|
543
|
-
lines.push(`- ${t}`);
|
|
544
|
-
lines.push('');
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
// Feedbacks
|
|
549
|
-
if (report.feedbacks.length > 0) {
|
|
550
|
-
lines.push('## Feedbacks');
|
|
551
|
-
lines.push('');
|
|
552
|
-
lines.push('| Date | Level | Trigger | Message |');
|
|
553
|
-
lines.push('|------|:-----:|---------|---------|');
|
|
554
|
-
for (const fb of report.feedbacks) {
|
|
555
|
-
const date = fb.timestamp?.split('T')[0] ?? 'N/A';
|
|
556
|
-
lines.push(`| ${date} | ${fb.level ?? '-'} | ${fb.trigger ?? '-'} | ${fb.message.substring(0, 60)} |`);
|
|
557
|
-
}
|
|
558
|
-
lines.push('');
|
|
559
|
-
}
|
|
560
|
-
lines.push('## Improvement Suggestions');
|
|
561
|
-
lines.push('');
|
|
562
|
-
for (const issue of report.summary.top_issues) {
|
|
563
|
-
lines.push(`- [ ] Address: ${issue}`);
|
|
564
|
-
}
|
|
565
|
-
lines.push('');
|
|
566
|
-
lines.push('---');
|
|
567
|
-
lines.push('Generated by TimSquad v2.0.0');
|
|
568
|
-
return lines.join('\n');
|
|
569
|
-
}
|
|
570
|
-
function createGitHubIssue(title, body) {
|
|
571
|
-
try {
|
|
572
|
-
execSync('gh --version', { stdio: 'ignore' });
|
|
573
|
-
// Write body to temp file to avoid shell escaping issues
|
|
574
|
-
const tmpFile = path.join('/tmp', `tsq-retro-${Date.now()}.md`);
|
|
575
|
-
fs.writeFileSync(tmpFile, body, 'utf-8');
|
|
576
|
-
const result = execFileSync('gh', ['issue', 'create', '--repo', 'ericson/timsquad', '--title', title, '--label', 'retro-feedback', '--body-file', tmpFile], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
577
|
-
fs.removeSync(tmpFile);
|
|
578
|
-
return result.trim();
|
|
579
|
-
}
|
|
580
|
-
catch {
|
|
581
|
-
return null;
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
async function generateReport(localOnly) {
|
|
585
|
-
const projectRoot = await findProjectRoot();
|
|
586
|
-
if (!projectRoot) {
|
|
587
|
-
throw new Error('Not a TimSquad project');
|
|
588
|
-
}
|
|
589
|
-
const project = await getProjectInfo(projectRoot);
|
|
590
|
-
printHeader('Generating Aggregated Report');
|
|
591
|
-
// Load all local feedback
|
|
592
|
-
const phases = await loadPhaseRetros(projectRoot);
|
|
593
|
-
const feedbacks = await loadFeedbackEntries(projectRoot);
|
|
594
|
-
if (phases.length === 0 && feedbacks.length === 0) {
|
|
595
|
-
console.log(colors.warning('\n⚠ No feedback data found.'));
|
|
596
|
-
console.log(colors.dim(' Run "tsq retro phase <name>" or "tsq feedback <message>" first.\n'));
|
|
597
|
-
return;
|
|
598
|
-
}
|
|
599
|
-
printKeyValue('Phase retros', String(phases.length));
|
|
600
|
-
printKeyValue('Feedbacks', String(feedbacks.length));
|
|
601
|
-
// Build aggregated report
|
|
602
|
-
const report = buildAggregatedReport(project, phases, feedbacks);
|
|
603
|
-
const markdown = renderReportMarkdown(report);
|
|
604
|
-
// Save report locally
|
|
605
|
-
const state = await getRetroState(projectRoot);
|
|
606
|
-
const cycleNum = state.currentCycle > 0 ? state.currentCycle : 1;
|
|
607
|
-
const reportDir = path.join(projectRoot, '.timsquad', 'retrospective', 'cycles');
|
|
608
|
-
await fs.ensureDir(reportDir);
|
|
609
|
-
const reportPath = path.join(reportDir, `cycle-${cycleNum}.md`);
|
|
610
|
-
await writeFile(reportPath, markdown);
|
|
611
|
-
const jsonPath = path.join(reportDir, `cycle-${cycleNum}.json`);
|
|
612
|
-
await fs.writeJson(jsonPath, report, { spaces: 2 });
|
|
613
|
-
console.log('');
|
|
614
|
-
printSuccess(`Report saved: cycle-${cycleNum}.md`);
|
|
615
|
-
console.log(colors.path(` ${reportPath}`));
|
|
616
|
-
// Create GitHub Issue (unless --local)
|
|
617
|
-
if (!localOnly) {
|
|
618
|
-
console.log('');
|
|
619
|
-
const shouldCreate = await promptConfirm('GitHub Issue를 생성하시겠습니까?', true);
|
|
620
|
-
if (shouldCreate) {
|
|
621
|
-
const issueTitle = `[retro] ${project.name} - Cycle ${cycleNum}`;
|
|
622
|
-
const issueUrl = createGitHubIssue(issueTitle, markdown);
|
|
623
|
-
if (issueUrl) {
|
|
624
|
-
console.log('');
|
|
625
|
-
printSuccess(`GitHub Issue created: ${issueUrl}`);
|
|
626
|
-
}
|
|
627
|
-
else {
|
|
628
|
-
console.log(colors.warning('\n⚠ GitHub Issue 생성 실패 (gh CLI 확인 필요)'));
|
|
629
|
-
console.log(colors.dim(' 수동 생성: gh issue create --repo ericson/timsquad'));
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
// Update retro state
|
|
634
|
-
state.status = 'applying';
|
|
635
|
-
await saveRetroState(projectRoot, state);
|
|
636
|
-
console.log(colors.dim('\nNext: Review report and run "tsq retro apply" to complete'));
|
|
637
|
-
}
|
|
638
|
-
async function applyImprovements() {
|
|
639
|
-
const projectRoot = await findProjectRoot();
|
|
640
|
-
if (!projectRoot) {
|
|
641
|
-
throw new Error('Not a TimSquad project');
|
|
642
|
-
}
|
|
643
|
-
const state = await getRetroState(projectRoot);
|
|
644
|
-
if (state.status !== 'applying') {
|
|
645
|
-
throw new Error('Generate report first. Run "tsq retro report"');
|
|
646
|
-
}
|
|
647
|
-
console.log(colors.warning('\n⚠ Improvement application requires manual review'));
|
|
648
|
-
console.log(colors.dim(' 1. Review the generated report'));
|
|
649
|
-
console.log(colors.dim(' 2. Update templates/prompts as needed'));
|
|
650
|
-
console.log(colors.dim(' 3. This cycle will be marked as complete\n'));
|
|
651
|
-
// Archive processed feedback
|
|
652
|
-
const feedbackDir = path.join(projectRoot, '.timsquad', 'feedback');
|
|
653
|
-
const archiveDir = path.join(feedbackDir, `archive-cycle-${state.currentCycle}`);
|
|
654
|
-
if (await exists(feedbackDir)) {
|
|
655
|
-
const files = await listFiles('*.json', feedbackDir);
|
|
656
|
-
if (files.length > 0) {
|
|
657
|
-
await fs.ensureDir(archiveDir);
|
|
658
|
-
for (const file of files) {
|
|
659
|
-
await fs.move(path.join(feedbackDir, file), path.join(archiveDir, file), { overwrite: true });
|
|
660
|
-
}
|
|
661
|
-
console.log(colors.dim(` Feedback archived: ${archiveDir}`));
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
state.status = 'idle';
|
|
665
|
-
await saveRetroState(projectRoot, state);
|
|
666
|
-
printSuccess(`Retrospective cycle ${state.currentCycle} completed!`);
|
|
667
|
-
}
|
|
668
|
-
// ============================================================
|
|
669
|
-
// Auto retro (전체 사이클 자동 실행)
|
|
670
|
-
// start → collect → (analyze skip) → report → apply
|
|
671
|
-
// ============================================================
|
|
672
|
-
async function runAutoRetro(localOnly) {
|
|
673
|
-
const projectRoot = await findProjectRoot();
|
|
674
|
-
if (!projectRoot) {
|
|
675
|
-
throw new Error('Not a TimSquad project');
|
|
676
|
-
}
|
|
677
|
-
printHeader('Auto Retrospective');
|
|
678
|
-
console.log(colors.dim('전체 회고 사이클을 자동으로 실행합니다.\n'));
|
|
679
|
-
// Step 1: Start
|
|
680
|
-
let state = await getRetroState(projectRoot);
|
|
681
|
-
if (state.status !== 'idle') {
|
|
682
|
-
console.log(colors.warning(`⚠ 기존 cycle ${state.currentCycle} (${state.status}) 가 진행 중입니다.`));
|
|
683
|
-
console.log(colors.dim(' 기존 사이클을 이어서 진행합니다.\n'));
|
|
684
|
-
}
|
|
685
|
-
else {
|
|
686
|
-
state.currentCycle += 1;
|
|
687
|
-
state.status = 'collecting';
|
|
688
|
-
state.startedAt = getTimestamp();
|
|
689
|
-
await saveRetroState(projectRoot, state);
|
|
690
|
-
printSuccess(`[1/4] Cycle ${state.currentCycle} started`);
|
|
691
|
-
}
|
|
692
|
-
// Step 2: Collect
|
|
693
|
-
if (state.status === 'collecting') {
|
|
694
|
-
const logsDir = path.join(projectRoot, '.timsquad', 'logs');
|
|
695
|
-
const logFiles = await listFiles('*.md', logsDir);
|
|
696
|
-
const recentLogs = logFiles.filter(f => !f.startsWith('_'));
|
|
697
|
-
const agentStats = {};
|
|
698
|
-
for (const file of recentLogs) {
|
|
699
|
-
const match = file.match(/-([a-z]+)\.md$/);
|
|
700
|
-
if (match) {
|
|
701
|
-
agentStats[match[1]] = (agentStats[match[1]] || 0) + 1;
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
const metricsPath = path.join(projectRoot, '.timsquad', 'retrospective', 'metrics', `cycle-${state.currentCycle}.json`);
|
|
705
|
-
await fs.ensureDir(path.dirname(metricsPath));
|
|
706
|
-
await fs.writeJson(metricsPath, {
|
|
707
|
-
cycle: state.currentCycle,
|
|
708
|
-
collectedAt: getTimestamp(),
|
|
709
|
-
raw_data: { log_files: recentLogs.length, agents: agentStats },
|
|
710
|
-
summary: { total_logs: recentLogs.length, agents_active: Object.keys(agentStats).length },
|
|
711
|
-
}, { spaces: 2 });
|
|
712
|
-
state.status = 'analyzing';
|
|
713
|
-
state.collectedAt = getTimestamp();
|
|
714
|
-
await saveRetroState(projectRoot, state);
|
|
715
|
-
printSuccess(`[2/4] Metrics collected (${recentLogs.length} logs, ${Object.keys(agentStats).length} agents)`);
|
|
716
|
-
}
|
|
717
|
-
// Step 3: Pattern analysis
|
|
718
|
-
if (state.status === 'analyzing') {
|
|
719
|
-
const analysis = await runPatternAnalysis(projectRoot, state.currentCycle);
|
|
720
|
-
const analysisDir = path.join(projectRoot, '.timsquad', 'retrospective', 'analysis');
|
|
721
|
-
await fs.ensureDir(analysisDir);
|
|
722
|
-
await fs.writeJson(path.join(analysisDir, `cycle-${state.currentCycle}-analysis.json`), analysis, { spaces: 2 });
|
|
723
|
-
state.status = 'reporting';
|
|
724
|
-
state.analyzedAt = getTimestamp();
|
|
725
|
-
await saveRetroState(projectRoot, state);
|
|
726
|
-
printSuccess(`[3/4] Analysis completed (${analysis.flags.length} flags)`);
|
|
727
|
-
}
|
|
728
|
-
// Step 4: Report + Apply
|
|
729
|
-
if (state.status === 'reporting') {
|
|
730
|
-
const project = await getProjectInfo(projectRoot);
|
|
731
|
-
const phases = await loadPhaseRetros(projectRoot);
|
|
732
|
-
const feedbacks = await loadFeedbackEntries(projectRoot);
|
|
733
|
-
if (phases.length === 0 && feedbacks.length === 0) {
|
|
734
|
-
console.log(colors.dim('\n 피드백 데이터 없음 - 빈 리포트 생성을 건너뜁니다.'));
|
|
735
|
-
}
|
|
736
|
-
else {
|
|
737
|
-
const report = buildAggregatedReport(project, phases, feedbacks);
|
|
738
|
-
const markdown = renderReportMarkdown(report);
|
|
739
|
-
const reportDir = path.join(projectRoot, '.timsquad', 'retrospective', 'cycles');
|
|
740
|
-
await fs.ensureDir(reportDir);
|
|
741
|
-
await writeFile(path.join(reportDir, `cycle-${state.currentCycle}.md`), markdown);
|
|
742
|
-
await fs.writeJson(path.join(reportDir, `cycle-${state.currentCycle}.json`), report, { spaces: 2 });
|
|
743
|
-
printKeyValue(' Phase retros', String(phases.length));
|
|
744
|
-
printKeyValue(' Feedbacks', String(feedbacks.length));
|
|
745
|
-
// GitHub Issue (auto mode에서는 --local이 아니면 자동 생성 시도)
|
|
746
|
-
if (!localOnly) {
|
|
747
|
-
const issueTitle = `[retro] ${project.name} - Cycle ${state.currentCycle}`;
|
|
748
|
-
const issueUrl = createGitHubIssue(issueTitle, markdown);
|
|
749
|
-
if (issueUrl) {
|
|
750
|
-
printSuccess(` GitHub Issue: ${issueUrl}`);
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
// Apply: archive feedback
|
|
755
|
-
const feedbackDir = path.join(projectRoot, '.timsquad', 'feedback');
|
|
756
|
-
const archiveDir = path.join(feedbackDir, `archive-cycle-${state.currentCycle}`);
|
|
757
|
-
if (await exists(feedbackDir)) {
|
|
758
|
-
const files = await listFiles('*.json', feedbackDir);
|
|
759
|
-
if (files.length > 0) {
|
|
760
|
-
await fs.ensureDir(archiveDir);
|
|
761
|
-
for (const file of files) {
|
|
762
|
-
await fs.move(path.join(feedbackDir, file), path.join(archiveDir, file), { overwrite: true });
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
state.status = 'idle';
|
|
767
|
-
await saveRetroState(projectRoot, state);
|
|
768
|
-
printSuccess(`[4/4] Cycle ${state.currentCycle} completed`);
|
|
769
|
-
}
|
|
770
|
-
// Bonus: retro → improve 자동 연결
|
|
771
|
-
// GitHub Issue가 생성되었으면 improve fetch+analyze 자동 실행
|
|
772
|
-
try {
|
|
773
|
-
execSync('gh --version', { stdio: 'ignore' });
|
|
774
|
-
console.log(colors.dim('\n Improvement analysis 자동 실행 중...'));
|
|
775
|
-
try {
|
|
776
|
-
execFileSync('npx', ['tsq', 'improve', 'fetch', '--limit', '20'], {
|
|
777
|
-
cwd: projectRoot,
|
|
778
|
-
stdio: 'ignore',
|
|
779
|
-
timeout: 15000,
|
|
780
|
-
});
|
|
781
|
-
execFileSync('npx', ['tsq', 'improve', 'analyze'], {
|
|
782
|
-
cwd: projectRoot,
|
|
783
|
-
stdio: 'ignore',
|
|
784
|
-
timeout: 15000,
|
|
785
|
-
});
|
|
786
|
-
printSuccess(' Improvement analysis completed');
|
|
787
|
-
console.log(colors.dim(' 결과 확인: tsq improve summary'));
|
|
788
|
-
}
|
|
789
|
-
catch {
|
|
790
|
-
console.log(colors.dim(' Improvement analysis skipped (no issues or error)'));
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
catch {
|
|
794
|
-
// gh not available, skip
|
|
795
|
-
}
|
|
796
|
-
console.log(colors.dim('\nTip: tsq retro status 로 결과 확인'));
|
|
797
|
-
}
|
|
798
|
-
async function showRetroStatus() {
|
|
799
|
-
const projectRoot = await findProjectRoot();
|
|
800
|
-
if (!projectRoot) {
|
|
801
|
-
throw new Error('Not a TimSquad project');
|
|
802
|
-
}
|
|
803
|
-
const state = await getRetroState(projectRoot);
|
|
804
|
-
printHeader('Retrospective Status');
|
|
805
|
-
printKeyValue('Current Cycle', String(state.currentCycle));
|
|
806
|
-
printKeyValue('Status', state.status);
|
|
807
|
-
if (state.startedAt) {
|
|
808
|
-
printKeyValue('Started', state.startedAt?.split('T')[0] ?? 'N/A');
|
|
809
|
-
}
|
|
810
|
-
// Count pending feedback
|
|
811
|
-
const feedbackDir = path.join(projectRoot, '.timsquad', 'feedback');
|
|
812
|
-
if (await exists(feedbackDir)) {
|
|
813
|
-
const phaseFiles = await listFiles('phase-*.json', feedbackDir);
|
|
814
|
-
const fbFiles = await listFiles('FB-*.json', feedbackDir);
|
|
815
|
-
console.log('');
|
|
816
|
-
printKeyValue('Pending phase retros', String(phaseFiles.length));
|
|
817
|
-
printKeyValue('Pending feedbacks', String(fbFiles.length));
|
|
818
|
-
}
|
|
819
|
-
// List completed cycles
|
|
820
|
-
const cyclesDir = path.join(projectRoot, '.timsquad', 'retrospective', 'cycles');
|
|
821
|
-
if (await exists(cyclesDir)) {
|
|
822
|
-
const cycles = await listFiles('cycle-*.md', cyclesDir);
|
|
823
|
-
if (cycles.length > 0) {
|
|
824
|
-
console.log(colors.dim(`\nCompleted cycles: ${cycles.length}`));
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
//# sourceMappingURL=retro.js.map
|