@sk8metal/michi-cli 0.0.9 → 0.1.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/CHANGELOG.md +27 -0
- package/README.md +235 -57
- package/dist/scripts/__tests__/create-project.test.js +24 -28
- package/dist/scripts/__tests__/create-project.test.js.map +1 -1
- package/dist/scripts/__tests__/jira-transitions.test.d.ts +5 -0
- package/dist/scripts/__tests__/jira-transitions.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/jira-transitions.test.js +172 -0
- package/dist/scripts/__tests__/jira-transitions.test.js.map +1 -0
- package/dist/scripts/__tests__/multi-project-estimate.test.js +14 -15
- package/dist/scripts/__tests__/multi-project-estimate.test.js.map +1 -1
- package/dist/scripts/__tests__/setup-existing-project.test.js +79 -0
- package/dist/scripts/__tests__/setup-existing-project.test.js.map +1 -1
- package/dist/scripts/__tests__/setup-interactive.test.js +23 -17
- package/dist/scripts/__tests__/setup-interactive.test.js.map +1 -1
- package/dist/scripts/__tests__/spec-impl-workflow.test.d.ts +5 -0
- package/dist/scripts/__tests__/spec-impl-workflow.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/spec-impl-workflow.test.js +321 -0
- package/dist/scripts/__tests__/spec-impl-workflow.test.js.map +1 -0
- package/dist/scripts/__tests__/spec-loader.test.d.ts +5 -0
- package/dist/scripts/__tests__/spec-loader.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/spec-loader.test.js +153 -0
- package/dist/scripts/__tests__/spec-loader.test.js.map +1 -0
- package/dist/scripts/__tests__/validate-phase.test.js +26 -22
- package/dist/scripts/__tests__/validate-phase.test.js.map +1 -1
- package/dist/scripts/config/config-schema.d.ts +17 -0
- package/dist/scripts/config/config-schema.d.ts.map +1 -1
- package/dist/scripts/config/config-schema.js +55 -26
- package/dist/scripts/config/config-schema.js.map +1 -1
- package/dist/scripts/config-interactive.d.ts.map +1 -1
- package/dist/scripts/config-interactive.js +53 -38
- package/dist/scripts/config-interactive.js.map +1 -1
- package/dist/scripts/confluence-sync.d.ts.map +1 -1
- package/dist/scripts/confluence-sync.js +0 -11
- package/dist/scripts/confluence-sync.js.map +1 -1
- package/dist/scripts/constants/__tests__/environments.test.js +39 -5
- package/dist/scripts/constants/__tests__/environments.test.js.map +1 -1
- package/dist/scripts/constants/environments.d.ts +1 -1
- package/dist/scripts/constants/environments.d.ts.map +1 -1
- package/dist/scripts/constants/environments.js +22 -7
- package/dist/scripts/constants/environments.js.map +1 -1
- package/dist/scripts/constants/test-commands.d.ts +36 -0
- package/dist/scripts/constants/test-commands.d.ts.map +1 -0
- package/dist/scripts/constants/test-commands.js +70 -0
- package/dist/scripts/constants/test-commands.js.map +1 -0
- package/dist/scripts/jira-sync.d.ts +89 -3
- package/dist/scripts/jira-sync.d.ts.map +1 -1
- package/dist/scripts/jira-sync.js +366 -96
- package/dist/scripts/jira-sync.js.map +1 -1
- package/dist/scripts/markdown-to-confluence.js +1 -1
- package/dist/scripts/markdown-to-confluence.js.map +1 -1
- package/dist/scripts/phase-runner.d.ts +1 -1
- package/dist/scripts/phase-runner.d.ts.map +1 -1
- package/dist/scripts/phase-runner.js +809 -13
- package/dist/scripts/phase-runner.js.map +1 -1
- package/dist/scripts/pr-automation.d.ts.map +1 -1
- package/dist/scripts/pr-automation.js.map +1 -1
- package/dist/scripts/pre-flight-check.js +1 -1
- package/dist/scripts/pre-flight-check.js.map +1 -1
- package/dist/scripts/setup-existing-project.js +61 -29
- package/dist/scripts/setup-existing-project.js.map +1 -1
- package/dist/scripts/setup-interactive.js +3 -3
- package/dist/scripts/setup-interactive.js.map +1 -1
- package/dist/scripts/spec-impl-workflow.d.ts +94 -0
- package/dist/scripts/spec-impl-workflow.d.ts.map +1 -0
- package/dist/scripts/spec-impl-workflow.js +354 -0
- package/dist/scripts/spec-impl-workflow.js.map +1 -0
- package/dist/scripts/template/__tests__/renderer.test.js.map +1 -1
- package/dist/scripts/test-execution-generator.d.ts +52 -0
- package/dist/scripts/test-execution-generator.d.ts.map +1 -0
- package/dist/scripts/test-execution-generator.js +576 -0
- package/dist/scripts/test-execution-generator.js.map +1 -0
- package/dist/scripts/test-interactive.d.ts +10 -0
- package/dist/scripts/test-interactive.d.ts.map +1 -0
- package/dist/scripts/test-interactive.js +627 -0
- package/dist/scripts/test-interactive.js.map +1 -0
- package/dist/scripts/test-new-features.d.ts +5 -0
- package/dist/scripts/test-new-features.d.ts.map +1 -0
- package/dist/scripts/test-new-features.js +145 -0
- package/dist/scripts/test-new-features.js.map +1 -0
- package/dist/scripts/test-spec-generator.d.ts +29 -0
- package/dist/scripts/test-spec-generator.d.ts.map +1 -0
- package/dist/scripts/test-spec-generator.js +494 -0
- package/dist/scripts/test-spec-generator.js.map +1 -0
- package/dist/scripts/test-workflow-stages.d.ts +6 -0
- package/dist/scripts/test-workflow-stages.d.ts.map +1 -0
- package/dist/scripts/test-workflow-stages.js +43 -0
- package/dist/scripts/test-workflow-stages.js.map +1 -0
- package/dist/scripts/utils/__tests__/aidlc-parser.test.d.ts +5 -0
- package/dist/scripts/utils/__tests__/aidlc-parser.test.d.ts.map +1 -0
- package/dist/scripts/utils/__tests__/aidlc-parser.test.js +315 -0
- package/dist/scripts/utils/__tests__/aidlc-parser.test.js.map +1 -0
- package/dist/scripts/utils/__tests__/business-days.test.d.ts +5 -0
- package/dist/scripts/utils/__tests__/business-days.test.d.ts.map +1 -0
- package/dist/scripts/utils/__tests__/business-days.test.js +171 -0
- package/dist/scripts/utils/__tests__/business-days.test.js.map +1 -0
- package/dist/scripts/utils/__tests__/config-loader.test.js +1 -1
- package/dist/scripts/utils/__tests__/config-loader.test.js.map +1 -1
- package/dist/scripts/utils/__tests__/config-validator.test.js +164 -35
- package/dist/scripts/utils/__tests__/config-validator.test.js.map +1 -1
- package/dist/scripts/utils/__tests__/env-config.test.d.ts +5 -0
- package/dist/scripts/utils/__tests__/env-config.test.d.ts.map +1 -0
- package/dist/scripts/utils/__tests__/env-config.test.js +218 -0
- package/dist/scripts/utils/__tests__/env-config.test.js.map +1 -0
- package/dist/scripts/utils/__tests__/jira-issue-type-fetcher.test.d.ts +5 -0
- package/dist/scripts/utils/__tests__/jira-issue-type-fetcher.test.d.ts.map +1 -0
- package/dist/scripts/utils/__tests__/jira-issue-type-fetcher.test.js +202 -0
- package/dist/scripts/utils/__tests__/jira-issue-type-fetcher.test.js.map +1 -0
- package/dist/scripts/utils/__tests__/tasks-converter.test.d.ts +5 -0
- package/dist/scripts/utils/__tests__/tasks-converter.test.d.ts.map +1 -0
- package/dist/scripts/utils/__tests__/tasks-converter.test.js +500 -0
- package/dist/scripts/utils/__tests__/tasks-converter.test.js.map +1 -0
- package/dist/scripts/utils/__tests__/tasks-format-validator.test.d.ts +5 -0
- package/dist/scripts/utils/__tests__/tasks-format-validator.test.d.ts.map +1 -0
- package/dist/scripts/utils/__tests__/tasks-format-validator.test.js +314 -0
- package/dist/scripts/utils/__tests__/tasks-format-validator.test.js.map +1 -0
- package/dist/scripts/utils/__tests__/test-runner.test.d.ts +5 -0
- package/dist/scripts/utils/__tests__/test-runner.test.d.ts.map +1 -0
- package/dist/scripts/utils/__tests__/test-runner.test.js +64 -0
- package/dist/scripts/utils/__tests__/test-runner.test.js.map +1 -0
- package/dist/scripts/utils/aidlc-parser.d.ts +86 -0
- package/dist/scripts/utils/aidlc-parser.d.ts.map +1 -0
- package/dist/scripts/utils/aidlc-parser.js +208 -0
- package/dist/scripts/utils/aidlc-parser.js.map +1 -0
- package/dist/scripts/utils/business-days.d.ts +52 -0
- package/dist/scripts/utils/business-days.d.ts.map +1 -0
- package/dist/scripts/utils/business-days.js +98 -0
- package/dist/scripts/utils/business-days.js.map +1 -0
- package/dist/scripts/utils/ci-generator.d.ts +14 -0
- package/dist/scripts/utils/ci-generator.d.ts.map +1 -0
- package/dist/scripts/utils/ci-generator.js +61 -0
- package/dist/scripts/utils/ci-generator.js.map +1 -0
- package/dist/scripts/utils/config-loader.js +2 -2
- package/dist/scripts/utils/config-loader.js.map +1 -1
- package/dist/scripts/utils/config-validator.d.ts +7 -1
- package/dist/scripts/utils/config-validator.d.ts.map +1 -1
- package/dist/scripts/utils/config-validator.js +136 -23
- package/dist/scripts/utils/config-validator.js.map +1 -1
- package/dist/scripts/utils/confluence-approval.d.ts +46 -0
- package/dist/scripts/utils/confluence-approval.d.ts.map +1 -0
- package/dist/scripts/utils/confluence-approval.js +118 -0
- package/dist/scripts/utils/confluence-approval.js.map +1 -0
- package/dist/scripts/utils/confluence-hierarchy.d.ts.map +1 -1
- package/dist/scripts/utils/confluence-hierarchy.js +1 -1
- package/dist/scripts/utils/confluence-hierarchy.js.map +1 -1
- package/dist/scripts/utils/docker-generator.d.ts +9 -0
- package/dist/scripts/utils/docker-generator.d.ts.map +1 -0
- package/dist/scripts/utils/docker-generator.js +132 -0
- package/dist/scripts/utils/docker-generator.js.map +1 -0
- package/dist/scripts/utils/docker-requirement-detector.d.ts +15 -0
- package/dist/scripts/utils/docker-requirement-detector.d.ts.map +1 -0
- package/dist/scripts/utils/docker-requirement-detector.js +124 -0
- package/dist/scripts/utils/docker-requirement-detector.js.map +1 -0
- package/dist/scripts/utils/env-config.d.ts +54 -0
- package/dist/scripts/utils/env-config.d.ts.map +1 -0
- package/dist/scripts/utils/env-config.js +414 -0
- package/dist/scripts/utils/env-config.js.map +1 -0
- package/dist/scripts/utils/jira-issue-type-fetcher.d.ts +70 -0
- package/dist/scripts/utils/jira-issue-type-fetcher.d.ts.map +1 -0
- package/dist/scripts/utils/jira-issue-type-fetcher.js +147 -0
- package/dist/scripts/utils/jira-issue-type-fetcher.js.map +1 -0
- package/dist/scripts/utils/language-detector.d.ts +14 -0
- package/dist/scripts/utils/language-detector.d.ts.map +1 -0
- package/dist/scripts/utils/language-detector.js +119 -0
- package/dist/scripts/utils/language-detector.js.map +1 -0
- package/dist/scripts/utils/markdown-parser.d.ts +55 -0
- package/dist/scripts/utils/markdown-parser.d.ts.map +1 -0
- package/dist/scripts/utils/markdown-parser.js +289 -0
- package/dist/scripts/utils/markdown-parser.js.map +1 -0
- package/dist/scripts/utils/project-detector.d.ts +17 -0
- package/dist/scripts/utils/project-detector.d.ts.map +1 -0
- package/dist/scripts/utils/project-detector.js +166 -0
- package/dist/scripts/utils/project-detector.js.map +1 -0
- package/dist/scripts/utils/project-finder.js +2 -2
- package/dist/scripts/utils/project-finder.js.map +1 -1
- package/dist/scripts/utils/release-notes-generator.d.ts +56 -0
- package/dist/scripts/utils/release-notes-generator.d.ts.map +1 -0
- package/dist/scripts/utils/release-notes-generator.js +162 -0
- package/dist/scripts/utils/release-notes-generator.js.map +1 -0
- package/dist/scripts/utils/spec-loader.d.ts +79 -0
- package/dist/scripts/utils/spec-loader.d.ts.map +1 -0
- package/dist/scripts/utils/spec-loader.js +80 -0
- package/dist/scripts/utils/spec-loader.js.map +1 -0
- package/dist/scripts/utils/spec-updater.d.ts +7 -0
- package/dist/scripts/utils/spec-updater.d.ts.map +1 -1
- package/dist/scripts/utils/spec-updater.js.map +1 -1
- package/dist/scripts/utils/tasks-converter.d.ts +57 -0
- package/dist/scripts/utils/tasks-converter.d.ts.map +1 -0
- package/dist/scripts/utils/tasks-converter.js +322 -0
- package/dist/scripts/utils/tasks-converter.js.map +1 -0
- package/dist/scripts/utils/tasks-format-validator.d.ts +36 -0
- package/dist/scripts/utils/tasks-format-validator.d.ts.map +1 -0
- package/dist/scripts/utils/tasks-format-validator.js +158 -0
- package/dist/scripts/utils/tasks-format-validator.js.map +1 -0
- package/dist/scripts/utils/template-applier.d.ts +37 -0
- package/dist/scripts/utils/template-applier.d.ts.map +1 -0
- package/dist/scripts/utils/template-applier.js +129 -0
- package/dist/scripts/utils/template-applier.js.map +1 -0
- package/dist/scripts/utils/test-config-generator.d.ts +12 -0
- package/dist/scripts/utils/test-config-generator.d.ts.map +1 -0
- package/dist/scripts/utils/test-config-generator.js +185 -0
- package/dist/scripts/utils/test-config-generator.js.map +1 -0
- package/dist/scripts/utils/test-runner.d.ts +31 -0
- package/dist/scripts/utils/test-runner.d.ts.map +1 -0
- package/dist/scripts/utils/test-runner.js +103 -0
- package/dist/scripts/utils/test-runner.js.map +1 -0
- package/dist/scripts/validate-phase.d.ts +1 -1
- package/dist/scripts/validate-phase.d.ts.map +1 -1
- package/dist/scripts/validate-phase.js +153 -5
- package/dist/scripts/validate-phase.js.map +1 -1
- package/dist/scripts/workflow-orchestrator.d.ts +8 -0
- package/dist/scripts/workflow-orchestrator.d.ts.map +1 -1
- package/dist/scripts/workflow-orchestrator.js +108 -7
- package/dist/scripts/workflow-orchestrator.js.map +1 -1
- package/dist/src/__tests__/integration/internationalization.test.d.ts +8 -0
- package/dist/src/__tests__/integration/internationalization.test.d.ts.map +1 -0
- package/dist/src/__tests__/integration/internationalization.test.js +333 -0
- package/dist/src/__tests__/integration/internationalization.test.js.map +1 -0
- package/dist/src/__tests__/integration/setup/claude-agent.test.d.ts +1 -1
- package/dist/src/__tests__/integration/setup/claude-agent.test.js +17 -20
- package/dist/src/__tests__/integration/setup/claude-agent.test.js.map +1 -1
- package/dist/src/__tests__/integration/setup/cursor.test.js +23 -19
- package/dist/src/__tests__/integration/setup/cursor.test.js.map +1 -1
- package/dist/src/__tests__/integration/setup/validation.test.js +41 -58
- package/dist/src/__tests__/integration/setup/validation.test.js.map +1 -1
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +208 -18
- package/dist/src/cli.js.map +1 -1
- package/dist/src/commands/setup-existing.d.ts +3 -0
- package/dist/src/commands/setup-existing.d.ts.map +1 -1
- package/dist/src/commands/setup-existing.js +334 -47
- package/dist/src/commands/setup-existing.js.map +1 -1
- package/docs/README.md +3 -1
- package/docs/context.md +59 -0
- package/docs/design-issue-55.md +240 -0
- package/docs/design-issue-56.md +181 -0
- package/docs/michi-development/testing/manual-verification-flow.md +2242 -0
- package/docs/michi-development/testing/pre-publish-checklist.md +560 -0
- package/docs/plan.md +275 -0
- package/docs/user-guide/getting-started/github-token-setup.md +509 -0
- package/docs/{getting-started → user-guide/getting-started}/quick-start.md +16 -0
- package/docs/{getting-started → user-guide/getting-started}/setup.md +28 -1
- package/docs/user-guide/guides/internationalization.md +540 -0
- package/docs/{guides → user-guide/guides}/multi-project.md +1 -1
- package/docs/{guides → user-guide/guides}/phase-automation.md +67 -9
- package/docs/user-guide/guides/workflow.md +582 -0
- package/docs/user-guide/hands-on/README.md +142 -0
- package/docs/user-guide/hands-on/claude-agent-setup.md +455 -0
- package/docs/user-guide/hands-on/claude-setup.md +398 -0
- package/docs/user-guide/hands-on/cursor-setup.md +352 -0
- package/docs/user-guide/hands-on/troubleshooting.md +964 -0
- package/docs/user-guide/hands-on/verification-checklist.md +438 -0
- package/docs/user-guide/hands-on/workflow-walkthrough.md +906 -0
- package/docs/user-guide/reference/config.md +564 -0
- package/docs/{reference → user-guide/reference}/quick-reference.md +53 -40
- package/docs/user-guide/release/ci-setup.md +541 -0
- package/docs/user-guide/release/release-flow.md +476 -0
- package/docs/user-guide/templates/test-specs/README.md +173 -0
- package/docs/user-guide/templates/test-specs/e2e-test-spec-template.md +547 -0
- package/docs/user-guide/templates/test-specs/integration-test-spec-template.md +435 -0
- package/docs/user-guide/templates/test-specs/performance-test-spec-template.md +454 -0
- package/docs/user-guide/templates/test-specs/security-test-spec-template.md +664 -0
- package/docs/user-guide/templates/test-specs/unit-test-spec-template.md +328 -0
- package/docs/{testing → user-guide/testing}/integration-tests.md +24 -9
- package/docs/user-guide/testing/tdd-cycle.md +349 -0
- package/docs/user-guide/testing/test-execution-flow.md +396 -0
- package/docs/user-guide/testing/test-failure-handling.md +521 -0
- package/docs/user-guide/testing/test-planning-flow.md +181 -0
- package/docs/user-guide/testing-strategy.md +185 -0
- package/docs/verification-guide.md +518 -0
- package/package.json +7 -2
- package/scripts/__tests__/create-project.test.ts +67 -49
- package/scripts/__tests__/jira-transitions.test.ts +225 -0
- package/scripts/__tests__/multi-project-estimate.test.ts +36 -30
- package/scripts/__tests__/setup-existing-project.test.ts +98 -1
- package/scripts/__tests__/setup-interactive.test.ts +52 -46
- package/scripts/__tests__/spec-impl-workflow.test.ts +429 -0
- package/scripts/__tests__/spec-loader.test.ts +199 -0
- package/scripts/__tests__/validate-phase.test.ts +78 -54
- package/scripts/config/config-schema.ts +89 -50
- package/scripts/config-interactive.ts +191 -136
- package/scripts/confluence-sync.ts +0 -12
- package/scripts/constants/__tests__/environments.test.ts +42 -6
- package/scripts/constants/environments.ts +33 -13
- package/scripts/constants/test-commands.ts +96 -0
- package/scripts/jira-sync.ts +767 -232
- package/scripts/markdown-to-confluence.ts +1 -1
- package/scripts/phase-runner.ts +1056 -63
- package/scripts/pr-automation.ts +0 -1
- package/scripts/pre-flight-check.ts +1 -1
- package/scripts/pre-publish-check.sh +311 -0
- package/scripts/quick-verify.sh +115 -0
- package/scripts/setup-existing-project.ts +201 -117
- package/scripts/setup-interactive.ts +4 -4
- package/scripts/spec-impl-workflow.ts +505 -0
- package/scripts/template/__tests__/renderer.test.ts +1 -2
- package/scripts/test-execution-generator.ts +695 -0
- package/scripts/test-interactive.ts +779 -0
- package/scripts/test-new-features.ts +168 -0
- package/scripts/test-npm-package.sh +345 -0
- package/scripts/test-spec-generator.ts +574 -0
- package/scripts/test-workflow-stages.ts +53 -0
- package/scripts/utils/__tests__/aidlc-parser.test.ts +349 -0
- package/scripts/utils/__tests__/business-days.test.ts +214 -0
- package/scripts/utils/__tests__/config-loader.test.ts +1 -1
- package/scripts/utils/__tests__/config-validator.test.ts +309 -88
- package/scripts/utils/__tests__/env-config.test.ts +259 -0
- package/scripts/utils/__tests__/jira-issue-type-fetcher.test.ts +272 -0
- package/scripts/utils/__tests__/tasks-converter.test.ts +582 -0
- package/scripts/utils/__tests__/tasks-format-validator.test.ts +338 -0
- package/scripts/utils/__tests__/test-runner.test.ts +77 -0
- package/scripts/utils/aidlc-parser.ts +289 -0
- package/scripts/utils/business-days.ts +115 -0
- package/scripts/utils/ci-generator.ts +84 -0
- package/scripts/utils/config-loader.ts +2 -2
- package/scripts/utils/config-validator.ts +304 -117
- package/scripts/utils/confluence-approval.ts +167 -0
- package/scripts/utils/confluence-hierarchy.ts +2 -4
- package/scripts/utils/docker-generator.ts +151 -0
- package/scripts/utils/docker-requirement-detector.ts +153 -0
- package/scripts/utils/env-config.ts +526 -0
- package/scripts/utils/jira-issue-type-fetcher.ts +199 -0
- package/scripts/utils/language-detector.ts +139 -0
- package/scripts/utils/markdown-parser.ts +376 -0
- package/scripts/utils/project-detector.ts +192 -0
- package/scripts/utils/project-finder.ts +2 -2
- package/scripts/utils/release-notes-generator.ts +210 -0
- package/scripts/utils/spec-loader.ts +125 -0
- package/scripts/utils/spec-updater.ts +8 -1
- package/scripts/utils/tasks-converter.ts +601 -0
- package/scripts/utils/tasks-format-validator.ts +193 -0
- package/scripts/utils/template-applier.ts +202 -0
- package/scripts/utils/test-config-generator.ts +210 -0
- package/scripts/utils/test-runner.ts +133 -0
- package/scripts/validate-phase.ts +186 -9
- package/scripts/workflow-orchestrator.ts +130 -12
- package/templates/ci/github-actions/java.yml +54 -0
- package/templates/ci/github-actions/nodejs.yml +46 -0
- package/templates/ci/github-actions/php.yml +52 -0
- package/templates/ci/screwdriver/java.yaml +17 -0
- package/templates/ci/screwdriver/nodejs.yaml +17 -0
- package/templates/ci/screwdriver/php.yaml +20 -0
- package/templates/claude/commands/kiro/kiro-spec-impl.md +244 -0
- package/templates/claude/commands/kiro/kiro-spec-tasks.md +354 -0
- package/templates/claude-agent/README.md +7 -1
- package/templates/claude-agent/agents/.gitkeep +0 -0
- package/templates/claude-agent/agents/designer.md +79 -0
- package/templates/claude-agent/agents/developer.md +68 -0
- package/templates/claude-agent/agents/manager-agent.md +59 -0
- package/templates/claude-agent/agents/tester.md +101 -0
- package/templates/claude-agent/commands/kiro/.gitkeep +0 -0
- package/templates/claude-agent/commands/kiro/kiro-spec-impl.md +244 -0
- package/templates/claude-agent/commands/kiro/kiro-spec-tasks.md +354 -0
- package/templates/cline/rules/atlassian-integration.md +36 -0
- package/templates/cline/rules/michi-core.md +56 -0
- package/templates/codex/AGENTS.override.md +277 -0
- package/templates/codex/prompts/confluence-sync.md +177 -0
- package/templates/codex/rules/README.md +210 -0
- package/templates/common/.kiro/project.json.template +21 -0
- package/templates/cursor/commands/kiro/kiro-spec-impl.md +244 -0
- package/templates/cursor/commands/kiro/kiro-spec-tasks.md +354 -0
- package/templates/gemini/commands/README.md +41 -0
- package/templates/gemini/rules/GEMINI.md +80 -0
- package/docs/guides/workflow.md +0 -342
- package/docs/reference/config.md +0 -545
- package/scripts/setup-existing.sh +0 -279
- /package/docs/{contributing → michi-development/contributing}/development.md +0 -0
- /package/docs/{contributing → michi-development/contributing}/release.md +0 -0
- /package/docs/{testing-strategy.md → michi-development/testing-strategy.md} +0 -0
- /package/docs/{getting-started → user-guide/getting-started}/new-repository-setup.md +0 -0
- /package/docs/{guides → user-guide/guides}/customization.md +0 -0
- /package/docs/{reference → user-guide/reference}/tasks-template.md +0 -0
|
@@ -23,14 +23,14 @@ import { config } from 'dotenv';
|
|
|
23
23
|
import { loadProjectMeta } from './utils/project-meta.js';
|
|
24
24
|
import { validateFeatureNameOrThrow } from './utils/feature-name-validator.js';
|
|
25
25
|
import { getConfig, getConfigPath } from './utils/config-loader.js';
|
|
26
|
-
import {
|
|
27
|
-
import { updateSpecJsonAfterJiraSync } from './utils/spec-updater.js';
|
|
26
|
+
import { validateForJiraSyncAsync } from './utils/config-validator.js';
|
|
27
|
+
import { updateSpecJsonAfterJiraSync, } from './utils/spec-updater.js';
|
|
28
28
|
config();
|
|
29
29
|
/**
|
|
30
30
|
* リクエスト間のスリープ処理(レートリミット対策)
|
|
31
31
|
*/
|
|
32
32
|
function sleep(ms) {
|
|
33
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
33
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
36
|
* リクエスト間の待機時間(ミリ秒)
|
|
@@ -73,18 +73,18 @@ function extractStoryDetails(tasksContent, storyTitle) {
|
|
|
73
73
|
if (criteriaMatch) {
|
|
74
74
|
details.acceptanceCriteria = criteriaMatch[1]
|
|
75
75
|
.split('\n')
|
|
76
|
-
.filter(line => line.trim().startsWith('- ['))
|
|
77
|
-
.map(line => line.replace(/^- \[.\]\s*/, '').trim())
|
|
78
|
-
.filter(line => line.length > 0);
|
|
76
|
+
.filter((line) => line.trim().startsWith('- ['))
|
|
77
|
+
.map((line) => line.replace(/^- \[.\]\s*/, '').trim())
|
|
78
|
+
.filter((line) => line.length > 0);
|
|
79
79
|
}
|
|
80
80
|
// サブタスク抽出
|
|
81
81
|
const subtasksMatch = storySection.match(/\*\*サブタスク\*\*:\s*\n((?:- \[.\].*\n?)+)/);
|
|
82
82
|
if (subtasksMatch) {
|
|
83
83
|
details.subtasks = subtasksMatch[1]
|
|
84
84
|
.split('\n')
|
|
85
|
-
.filter(line => line.trim().startsWith('- ['))
|
|
86
|
-
.map(line => line.replace(/^- \[.\]\s*/, '').trim())
|
|
87
|
-
.filter(line => line.length > 0);
|
|
85
|
+
.filter((line) => line.trim().startsWith('- ['))
|
|
86
|
+
.map((line) => line.replace(/^- \[.\]\s*/, '').trim())
|
|
87
|
+
.filter((line) => line.length > 0);
|
|
88
88
|
}
|
|
89
89
|
// 依存関係抽出
|
|
90
90
|
const dependenciesMatch = storySection.match(/\*\*依存関係\*\*:\s*(.+)/);
|
|
@@ -102,11 +102,11 @@ function createRichADF(details, phaseLabel, githubUrl) {
|
|
|
102
102
|
content.push({
|
|
103
103
|
type: 'heading',
|
|
104
104
|
attrs: { level: 2 },
|
|
105
|
-
content: [{ type: 'text', text: '説明' }]
|
|
105
|
+
content: [{ type: 'text', text: '説明' }],
|
|
106
106
|
});
|
|
107
107
|
content.push({
|
|
108
108
|
type: 'paragraph',
|
|
109
|
-
content: [{ type: 'text', text: details.description }]
|
|
109
|
+
content: [{ type: 'text', text: details.description }],
|
|
110
110
|
});
|
|
111
111
|
}
|
|
112
112
|
// メタデータセクション
|
|
@@ -123,12 +123,12 @@ function createRichADF(details, phaseLabel, githubUrl) {
|
|
|
123
123
|
content.push({
|
|
124
124
|
type: 'heading',
|
|
125
125
|
attrs: { level: 2 },
|
|
126
|
-
content: [{ type: 'text', text: 'メタデータ' }]
|
|
126
|
+
content: [{ type: 'text', text: 'メタデータ' }],
|
|
127
127
|
});
|
|
128
|
-
metadata.forEach(item => {
|
|
128
|
+
metadata.forEach((item) => {
|
|
129
129
|
content.push({
|
|
130
130
|
type: 'paragraph',
|
|
131
|
-
content: [{ type: 'text', text: item }]
|
|
131
|
+
content: [{ type: 'text', text: item }],
|
|
132
132
|
});
|
|
133
133
|
});
|
|
134
134
|
}
|
|
@@ -137,18 +137,20 @@ function createRichADF(details, phaseLabel, githubUrl) {
|
|
|
137
137
|
content.push({
|
|
138
138
|
type: 'heading',
|
|
139
139
|
attrs: { level: 2 },
|
|
140
|
-
content: [{ type: 'text', text: '完了条件' }]
|
|
140
|
+
content: [{ type: 'text', text: '完了条件' }],
|
|
141
141
|
});
|
|
142
|
-
const listItems = details.acceptanceCriteria.map(criterion => ({
|
|
142
|
+
const listItems = details.acceptanceCriteria.map((criterion) => ({
|
|
143
143
|
type: 'listItem',
|
|
144
|
-
content: [
|
|
144
|
+
content: [
|
|
145
|
+
{
|
|
145
146
|
type: 'paragraph',
|
|
146
|
-
content: [{ type: 'text', text: criterion }]
|
|
147
|
-
}
|
|
147
|
+
content: [{ type: 'text', text: criterion }],
|
|
148
|
+
},
|
|
149
|
+
],
|
|
148
150
|
}));
|
|
149
151
|
content.push({
|
|
150
152
|
type: 'bulletList',
|
|
151
|
-
content: listItems
|
|
153
|
+
content: listItems,
|
|
152
154
|
});
|
|
153
155
|
}
|
|
154
156
|
// サブタスクセクション
|
|
@@ -156,30 +158,32 @@ function createRichADF(details, phaseLabel, githubUrl) {
|
|
|
156
158
|
content.push({
|
|
157
159
|
type: 'heading',
|
|
158
160
|
attrs: { level: 2 },
|
|
159
|
-
content: [{ type: 'text', text: 'サブタスク' }]
|
|
161
|
+
content: [{ type: 'text', text: 'サブタスク' }],
|
|
160
162
|
});
|
|
161
|
-
const listItems = details.subtasks.map(subtask => ({
|
|
163
|
+
const listItems = details.subtasks.map((subtask) => ({
|
|
162
164
|
type: 'listItem',
|
|
163
|
-
content: [
|
|
165
|
+
content: [
|
|
166
|
+
{
|
|
164
167
|
type: 'paragraph',
|
|
165
|
-
content: [{ type: 'text', text: subtask }]
|
|
166
|
-
}
|
|
168
|
+
content: [{ type: 'text', text: subtask }],
|
|
169
|
+
},
|
|
170
|
+
],
|
|
167
171
|
}));
|
|
168
172
|
content.push({
|
|
169
173
|
type: 'bulletList',
|
|
170
|
-
content: listItems
|
|
174
|
+
content: listItems,
|
|
171
175
|
});
|
|
172
176
|
}
|
|
173
177
|
// フッター(Phase、GitHubリンク)
|
|
174
178
|
content.push({
|
|
175
|
-
type: 'rule'
|
|
179
|
+
type: 'rule',
|
|
176
180
|
});
|
|
177
181
|
content.push({
|
|
178
182
|
type: 'paragraph',
|
|
179
183
|
content: [
|
|
180
184
|
{ type: 'text', text: 'Phase: ', marks: [{ type: 'strong' }] },
|
|
181
|
-
{ type: 'text', text: phaseLabel }
|
|
182
|
-
]
|
|
185
|
+
{ type: 'text', text: phaseLabel },
|
|
186
|
+
],
|
|
183
187
|
});
|
|
184
188
|
content.push({
|
|
185
189
|
type: 'paragraph',
|
|
@@ -188,17 +192,19 @@ function createRichADF(details, phaseLabel, githubUrl) {
|
|
|
188
192
|
{
|
|
189
193
|
type: 'text',
|
|
190
194
|
text: githubUrl,
|
|
191
|
-
marks: [
|
|
195
|
+
marks: [
|
|
196
|
+
{
|
|
192
197
|
type: 'link',
|
|
193
|
-
attrs: { href: githubUrl }
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
|
|
198
|
+
attrs: { href: githubUrl },
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
},
|
|
202
|
+
],
|
|
197
203
|
});
|
|
198
204
|
return {
|
|
199
205
|
type: 'doc',
|
|
200
206
|
version: 1,
|
|
201
|
-
content: content
|
|
207
|
+
content: content,
|
|
202
208
|
};
|
|
203
209
|
}
|
|
204
210
|
/**
|
|
@@ -206,19 +212,19 @@ function createRichADF(details, phaseLabel, githubUrl) {
|
|
|
206
212
|
*/
|
|
207
213
|
function textToADF(text) {
|
|
208
214
|
// 改行で分割して段落を作成
|
|
209
|
-
const paragraphs = text.split('\n').filter(line => line.trim().length > 0);
|
|
215
|
+
const paragraphs = text.split('\n').filter((line) => line.trim().length > 0);
|
|
210
216
|
return {
|
|
211
217
|
type: 'doc',
|
|
212
218
|
version: 1,
|
|
213
|
-
content: paragraphs.map(para => ({
|
|
219
|
+
content: paragraphs.map((para) => ({
|
|
214
220
|
type: 'paragraph',
|
|
215
221
|
content: [
|
|
216
222
|
{
|
|
217
223
|
type: 'text',
|
|
218
|
-
text: para.trim()
|
|
219
|
-
}
|
|
220
|
-
]
|
|
221
|
-
}))
|
|
224
|
+
text: para.trim(),
|
|
225
|
+
},
|
|
226
|
+
],
|
|
227
|
+
})),
|
|
222
228
|
};
|
|
223
229
|
}
|
|
224
230
|
function getJIRAConfig() {
|
|
@@ -247,17 +253,43 @@ class JIRAClient {
|
|
|
247
253
|
// レートリミット対策: リクエスト前に待機
|
|
248
254
|
await sleep(this.requestDelay);
|
|
249
255
|
try {
|
|
256
|
+
// JIRA API v3の検索エンドポイントを使用
|
|
257
|
+
// GET /rest/api/3/search でJQL検索を実行(GETメソッドが推奨)
|
|
250
258
|
const response = await axios.get(`${this.baseUrl}/search`, {
|
|
251
|
-
params: {
|
|
259
|
+
params: {
|
|
260
|
+
jql,
|
|
261
|
+
maxResults: 100,
|
|
262
|
+
fields: 'summary,issuetype,status,key',
|
|
263
|
+
},
|
|
252
264
|
headers: {
|
|
253
|
-
|
|
254
|
-
'Content-Type': 'application/json'
|
|
255
|
-
}
|
|
265
|
+
Authorization: `Basic ${this.auth}`,
|
|
266
|
+
'Content-Type': 'application/json',
|
|
267
|
+
},
|
|
256
268
|
});
|
|
257
269
|
return response.data.issues || [];
|
|
258
270
|
}
|
|
259
271
|
catch (error) {
|
|
260
|
-
|
|
272
|
+
// エラーハンドリング改善
|
|
273
|
+
if (axios.isAxiosError(error)) {
|
|
274
|
+
const status = error.response?.status;
|
|
275
|
+
const errorMessages = error.response?.data?.errorMessages || [];
|
|
276
|
+
const message = errorMessages.join(', ') || error.message;
|
|
277
|
+
console.error(`Error searching issues (HTTP ${status}): ${message}`);
|
|
278
|
+
if (status === 410) {
|
|
279
|
+
console.error('💡 Hint: The search API endpoint returned 410 (Gone).');
|
|
280
|
+
console.error(' This may indicate the endpoint has been deprecated or disabled.');
|
|
281
|
+
console.error(' Check JIRA instance configuration or try alternative search methods.');
|
|
282
|
+
}
|
|
283
|
+
else if (status === 401) {
|
|
284
|
+
console.error('💡 Hint: Authentication failed. Check ATLASSIAN_API_TOKEN in .env');
|
|
285
|
+
}
|
|
286
|
+
else if (status === 403) {
|
|
287
|
+
console.error('💡 Hint: Permission denied. Check API token permissions in JIRA.');
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
console.error('Error searching issues:', error instanceof Error ? error.message : error);
|
|
292
|
+
}
|
|
261
293
|
throw error; // エラーを再スローして呼び出し元で処理
|
|
262
294
|
}
|
|
263
295
|
}
|
|
@@ -266,9 +298,9 @@ class JIRAClient {
|
|
|
266
298
|
await sleep(this.requestDelay);
|
|
267
299
|
const response = await axios.post(`${this.baseUrl}/issue`, payload, {
|
|
268
300
|
headers: {
|
|
269
|
-
|
|
270
|
-
'Content-Type': 'application/json'
|
|
271
|
-
}
|
|
301
|
+
Authorization: `Basic ${this.auth}`,
|
|
302
|
+
'Content-Type': 'application/json',
|
|
303
|
+
},
|
|
272
304
|
});
|
|
273
305
|
return response.data;
|
|
274
306
|
}
|
|
@@ -277,28 +309,159 @@ class JIRAClient {
|
|
|
277
309
|
await sleep(this.requestDelay);
|
|
278
310
|
await axios.put(`${this.baseUrl}/issue/${issueKey}`, payload, {
|
|
279
311
|
headers: {
|
|
280
|
-
|
|
281
|
-
'Content-Type': 'application/json'
|
|
282
|
-
}
|
|
312
|
+
Authorization: `Basic ${this.auth}`,
|
|
313
|
+
'Content-Type': 'application/json',
|
|
314
|
+
},
|
|
283
315
|
});
|
|
284
316
|
}
|
|
317
|
+
/**
|
|
318
|
+
* JIRAチケットのステータスを変更(トランジション実行)
|
|
319
|
+
* @param issueKey JIRAチケットキー (例: "PROJ-123")
|
|
320
|
+
* @param transitionName 遷移先ステータス名 (例: "In Progress", "Ready for Review")
|
|
321
|
+
* @throws トランジションが見つからない場合はエラー
|
|
322
|
+
*/
|
|
323
|
+
async transitionIssue(issueKey, transitionName) {
|
|
324
|
+
// レートリミット対策: リクエスト前に待機
|
|
325
|
+
await sleep(this.requestDelay);
|
|
326
|
+
try {
|
|
327
|
+
// 1. 利用可能なトランジションを取得
|
|
328
|
+
const transitionsResponse = await axios.get(`${this.baseUrl}/issue/${issueKey}/transitions`, {
|
|
329
|
+
headers: {
|
|
330
|
+
Authorization: `Basic ${this.auth}`,
|
|
331
|
+
'Content-Type': 'application/json',
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
const transitions = transitionsResponse.data.transitions || [];
|
|
335
|
+
// 2. transitionNameに一致するトランジションIDを特定
|
|
336
|
+
// 名前の完全一致または部分一致で検索
|
|
337
|
+
const transition = transitions.find((t) => t.name.toLowerCase() === transitionName.toLowerCase() ||
|
|
338
|
+
t.name.toLowerCase().includes(transitionName.toLowerCase()));
|
|
339
|
+
if (!transition) {
|
|
340
|
+
const availableTransitions = transitions
|
|
341
|
+
.map((t) => t.name)
|
|
342
|
+
.join(', ');
|
|
343
|
+
throw new Error(`Transition "${transitionName}" not found for issue ${issueKey}. ` +
|
|
344
|
+
`Available transitions: ${availableTransitions || 'none'}`);
|
|
345
|
+
}
|
|
346
|
+
// レートリミット対策: リクエスト前に待機
|
|
347
|
+
await sleep(this.requestDelay);
|
|
348
|
+
// 3. トランジションを実行
|
|
349
|
+
await axios.post(`${this.baseUrl}/issue/${issueKey}/transitions`, {
|
|
350
|
+
transition: { id: transition.id },
|
|
351
|
+
}, {
|
|
352
|
+
headers: {
|
|
353
|
+
Authorization: `Basic ${this.auth}`,
|
|
354
|
+
'Content-Type': 'application/json',
|
|
355
|
+
},
|
|
356
|
+
});
|
|
357
|
+
console.log(`✅ ${issueKey} のステータスを「${transition.name}」に変更しました`);
|
|
358
|
+
}
|
|
359
|
+
catch (error) {
|
|
360
|
+
if (axios.isAxiosError(error)) {
|
|
361
|
+
const status = error.response?.status;
|
|
362
|
+
const errorMessages = error.response?.data?.errorMessages || [];
|
|
363
|
+
const message = errorMessages.join(', ') || error.message;
|
|
364
|
+
console.error(`Error transitioning issue ${issueKey} (HTTP ${status}): ${message}`);
|
|
365
|
+
if (status === 404) {
|
|
366
|
+
console.error(`💡 Hint: Issue ${issueKey} was not found. Check the issue key.`);
|
|
367
|
+
}
|
|
368
|
+
else if (status === 400) {
|
|
369
|
+
console.error('💡 Hint: The transition may not be valid from the current status.');
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
throw error;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* JIRAチケットにコメントを追加
|
|
377
|
+
* @param issueKey JIRAチケットキー
|
|
378
|
+
* @param commentText コメント内容
|
|
379
|
+
*/
|
|
380
|
+
async addComment(issueKey, commentText) {
|
|
381
|
+
// レートリミット対策: リクエスト前に待機
|
|
382
|
+
await sleep(this.requestDelay);
|
|
383
|
+
try {
|
|
384
|
+
// Atlassian Document Format (ADF) でコメントを作成
|
|
385
|
+
const commentBody = {
|
|
386
|
+
type: 'doc',
|
|
387
|
+
version: 1,
|
|
388
|
+
content: [
|
|
389
|
+
{
|
|
390
|
+
type: 'paragraph',
|
|
391
|
+
content: [
|
|
392
|
+
{
|
|
393
|
+
type: 'text',
|
|
394
|
+
text: commentText,
|
|
395
|
+
},
|
|
396
|
+
],
|
|
397
|
+
},
|
|
398
|
+
],
|
|
399
|
+
};
|
|
400
|
+
await axios.post(`${this.baseUrl}/issue/${issueKey}/comment`, {
|
|
401
|
+
body: commentBody,
|
|
402
|
+
}, {
|
|
403
|
+
headers: {
|
|
404
|
+
Authorization: `Basic ${this.auth}`,
|
|
405
|
+
'Content-Type': 'application/json',
|
|
406
|
+
},
|
|
407
|
+
});
|
|
408
|
+
console.log(`✅ ${issueKey} にコメントを追加しました`);
|
|
409
|
+
}
|
|
410
|
+
catch (error) {
|
|
411
|
+
if (axios.isAxiosError(error)) {
|
|
412
|
+
const status = error.response?.status;
|
|
413
|
+
const errorMessages = error.response?.data?.errorMessages || [];
|
|
414
|
+
const message = errorMessages.join(', ') || error.message;
|
|
415
|
+
console.error(`Error adding comment to ${issueKey} (HTTP ${status}): ${message}`);
|
|
416
|
+
if (status === 404) {
|
|
417
|
+
console.error(`💡 Hint: Issue ${issueKey} was not found. Check the issue key.`);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
throw error;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* プロジェクトのIssue Type IDを取得
|
|
425
|
+
* @param projectKey プロジェクトキー
|
|
426
|
+
* @param issueTypeName Issue Type名(例: "Epic", "Story")
|
|
427
|
+
* @returns Issue Type ID
|
|
428
|
+
*/
|
|
429
|
+
async getIssueTypeId(projectKey, issueTypeName) {
|
|
430
|
+
await sleep(this.requestDelay);
|
|
431
|
+
try {
|
|
432
|
+
const response = await axios.get(`${this.baseUrl}/project/${projectKey}`, {
|
|
433
|
+
headers: {
|
|
434
|
+
Authorization: `Basic ${this.auth}`,
|
|
435
|
+
'Content-Type': 'application/json',
|
|
436
|
+
},
|
|
437
|
+
});
|
|
438
|
+
const issueTypes = (response.data.issueTypes || []);
|
|
439
|
+
const issueType = issueTypes.find((it) => it.name.toLowerCase() === issueTypeName.toLowerCase() ||
|
|
440
|
+
it.name === issueTypeName);
|
|
441
|
+
return issueType ? issueType.id : null;
|
|
442
|
+
}
|
|
443
|
+
catch (error) {
|
|
444
|
+
console.error(`Error getting issue type ID for ${issueTypeName}:`, error instanceof Error ? error.message : error);
|
|
445
|
+
return null;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
285
448
|
}
|
|
286
449
|
async function syncTasksToJIRA(featureName) {
|
|
287
450
|
console.log(`Syncing tasks for feature: ${featureName}`);
|
|
288
451
|
// feature名のバリデーション(必須)
|
|
289
452
|
validateFeatureNameOrThrow(featureName);
|
|
290
|
-
//
|
|
291
|
-
const validation =
|
|
453
|
+
// 実行前の必須設定値チェック(非同期版:Issue Type IDの存在チェック付き)
|
|
454
|
+
const validation = await validateForJiraSyncAsync();
|
|
292
455
|
if (validation.info.length > 0) {
|
|
293
|
-
validation.info.forEach(msg => console.log(`ℹ️ ${msg}`));
|
|
456
|
+
validation.info.forEach((msg) => console.log(`ℹ️ ${msg}`));
|
|
294
457
|
}
|
|
295
458
|
if (validation.warnings.length > 0) {
|
|
296
459
|
console.warn('⚠️ Warnings:');
|
|
297
|
-
validation.warnings.forEach(warning => console.warn(` ${warning}`));
|
|
460
|
+
validation.warnings.forEach((warning) => console.warn(` ${warning}`));
|
|
298
461
|
}
|
|
299
462
|
if (validation.errors.length > 0) {
|
|
300
463
|
console.error('❌ Configuration errors:');
|
|
301
|
-
validation.errors.forEach(error => console.error(` ${error}`));
|
|
464
|
+
validation.errors.forEach((error) => console.error(` ${error}`));
|
|
302
465
|
const configPath = getConfigPath();
|
|
303
466
|
console.error(`\n設定ファイル: ${configPath}`);
|
|
304
467
|
throw new Error('JIRA同期に必要な設定値が不足しています。上記のエラーを確認して設定を修正してください。');
|
|
@@ -306,26 +469,35 @@ async function syncTasksToJIRA(featureName) {
|
|
|
306
469
|
console.log(`⏳ Request delay: ${getRequestDelay()}ms (set ATLASSIAN_REQUEST_DELAY to adjust)`);
|
|
307
470
|
// 設定からissue type IDを取得(検索と作成の両方で使用)
|
|
308
471
|
const appConfig = getConfig();
|
|
309
|
-
const
|
|
310
|
-
const
|
|
472
|
+
const projectMeta = loadProjectMeta();
|
|
473
|
+
const config = getJIRAConfig();
|
|
474
|
+
const client = new JIRAClient(config);
|
|
475
|
+
// StoryタイプのIDを動的に取得(日本語JIRAでは "ストーリー" という名前の場合がある)
|
|
476
|
+
let storyIssueTypeId = appConfig.jira?.issueTypes?.story || process.env.JIRA_ISSUE_TYPE_STORY;
|
|
477
|
+
console.log(`📋 Story Issue Type ID from config/env: ${storyIssueTypeId || 'not found'}`);
|
|
311
478
|
if (!storyIssueTypeId) {
|
|
312
|
-
|
|
479
|
+
console.log('🔍 Attempting to find Story issue type dynamically...');
|
|
480
|
+
const foundId = (await client.getIssueTypeId(projectMeta.jiraProjectKey, 'Story')) ||
|
|
481
|
+
(await client.getIssueTypeId(projectMeta.jiraProjectKey, 'ストーリー'));
|
|
482
|
+
storyIssueTypeId = foundId ?? undefined;
|
|
483
|
+
console.log(`📋 Story Issue Type ID from API: ${storyIssueTypeId || 'not found'}`);
|
|
484
|
+
}
|
|
485
|
+
if (!storyIssueTypeId) {
|
|
486
|
+
throw new Error('JIRA Story issue type ID is not configured and could not be found in project. ' +
|
|
313
487
|
'Please set JIRA_ISSUE_TYPE_STORY environment variable or configure it in .michi/config.json. ' +
|
|
314
488
|
'You can find the issue type ID in JIRA UI (Settings > Issues > Issue types) or via REST API: ' +
|
|
315
|
-
'GET https://your-domain.atlassian.net/rest/api/3/
|
|
489
|
+
'GET https://your-domain.atlassian.net/rest/api/3/project/{projectKey}');
|
|
316
490
|
}
|
|
317
|
-
|
|
491
|
+
console.log(`✅ Using Story Issue Type ID: ${storyIssueTypeId}`);
|
|
318
492
|
const tasksPath = resolve(`.kiro/specs/${featureName}/tasks.md`);
|
|
319
493
|
const tasksContent = readFileSync(tasksPath, 'utf-8');
|
|
320
|
-
const config = getJIRAConfig();
|
|
321
|
-
const client = new JIRAClient(config);
|
|
322
494
|
// spec.jsonを読み込んで既存のEpicキーを確認
|
|
323
495
|
const specPath = resolve(`.kiro/specs/${featureName}/spec.json`);
|
|
324
496
|
let spec = {};
|
|
325
497
|
try {
|
|
326
498
|
spec = JSON.parse(readFileSync(specPath, 'utf-8'));
|
|
327
499
|
}
|
|
328
|
-
catch
|
|
500
|
+
catch {
|
|
329
501
|
console.error('spec.json not found or invalid');
|
|
330
502
|
}
|
|
331
503
|
let epic;
|
|
@@ -347,8 +519,10 @@ async function syncTasksToJIRA(featureName) {
|
|
|
347
519
|
}
|
|
348
520
|
catch (error) {
|
|
349
521
|
console.error('❌ Failed to search existing Epics:', error instanceof Error ? error.message : error);
|
|
350
|
-
console.error('⚠️ Cannot verify idempotency - Epic creation
|
|
351
|
-
|
|
522
|
+
console.error('⚠️ Cannot verify idempotency - proceeding with Epic creation');
|
|
523
|
+
console.error(' If Epic already exists, manual cleanup may be required');
|
|
524
|
+
// 検索失敗時はフォールバック: 新規作成を試みる(重複リスクあり)
|
|
525
|
+
existingEpics = [];
|
|
352
526
|
}
|
|
353
527
|
if (existingEpics.length > 0) {
|
|
354
528
|
console.log(`Found existing Epic with similar title: ${existingEpics[0].key}`);
|
|
@@ -356,27 +530,38 @@ async function syncTasksToJIRA(featureName) {
|
|
|
356
530
|
epic = existingEpics[0];
|
|
357
531
|
}
|
|
358
532
|
else {
|
|
533
|
+
// EpicタイプのIDを取得(日本語JIRAでは "エピック" という名前の場合がある)
|
|
534
|
+
const epicTypeId = (await client.getIssueTypeId(projectMeta.jiraProjectKey, 'Epic')) ||
|
|
535
|
+
(await client.getIssueTypeId(projectMeta.jiraProjectKey, 'エピック'));
|
|
536
|
+
if (!epicTypeId) {
|
|
537
|
+
throw new Error('Epic issue type not found in project. ' +
|
|
538
|
+
'Please ensure the project has Epic issue type enabled.');
|
|
539
|
+
}
|
|
359
540
|
const epicDescription = `機能: ${featureName}\nGitHub: ${projectMeta.repository}/tree/main/.kiro/specs/${featureName}`;
|
|
360
541
|
const epicPayload = {
|
|
361
542
|
fields: {
|
|
362
543
|
project: { key: projectMeta.jiraProjectKey },
|
|
363
544
|
summary: epicSummary,
|
|
364
545
|
description: textToADF(epicDescription), // ADF形式に変換
|
|
365
|
-
issuetype: {
|
|
366
|
-
labels: projectMeta.confluenceLabels
|
|
367
|
-
}
|
|
546
|
+
issuetype: { id: epicTypeId }, // IDを使用(nameではなく)
|
|
547
|
+
labels: projectMeta.confluenceLabels,
|
|
548
|
+
},
|
|
368
549
|
};
|
|
369
550
|
epic = await client.createIssue(epicPayload);
|
|
370
551
|
console.log(`✅ Epic created: ${epic.key}`);
|
|
371
552
|
}
|
|
372
553
|
}
|
|
554
|
+
// Epicが確実に設定されていることを確認
|
|
555
|
+
if (!epic) {
|
|
556
|
+
throw new Error('Epic creation or retrieval failed');
|
|
557
|
+
}
|
|
373
558
|
// 既存のStoryを検索(重複防止)
|
|
374
559
|
// ラベルで検索(summary検索では "Story: タイトル" 形式に一致しないため)
|
|
375
560
|
// issuetype検索にはIDを使用(名前は言語依存のため)
|
|
376
|
-
const
|
|
561
|
+
const storyJql = `project = ${projectMeta.jiraProjectKey} AND issuetype = ${storyIssueTypeId} AND labels = "${featureName}"`;
|
|
377
562
|
let existingStories = [];
|
|
378
563
|
try {
|
|
379
|
-
existingStories = await client.searchIssues(
|
|
564
|
+
existingStories = await client.searchIssues(storyJql);
|
|
380
565
|
}
|
|
381
566
|
catch (error) {
|
|
382
567
|
console.error('❌ Failed to search existing Stories:', error instanceof Error ? error.message : error);
|
|
@@ -385,12 +570,17 @@ async function syncTasksToJIRA(featureName) {
|
|
|
385
570
|
// 検索失敗時も処理を継続(既存ストーリーなしとして扱う)
|
|
386
571
|
existingStories = [];
|
|
387
572
|
}
|
|
388
|
-
const existingStorySummaries = new Set(existingStories
|
|
389
|
-
|
|
573
|
+
const existingStorySummaries = new Set(existingStories
|
|
574
|
+
.filter((s) => s?.fields?.summary)
|
|
575
|
+
.map((s) => s.fields.summary));
|
|
576
|
+
const existingStoryKeys = new Set(existingStories
|
|
577
|
+
.filter((s) => s?.key)
|
|
578
|
+
.map((s) => s.key));
|
|
390
579
|
console.log(`Found ${existingStories.length} existing stories for this feature`);
|
|
391
580
|
// フェーズラベル検出用の正規表現
|
|
392
581
|
// Phase X: フェーズ名(ラベル)の形式を検出
|
|
393
|
-
|
|
582
|
+
// Phase番号: 数字(0, 1, 2...)、ドット付き数字(0.1, 0.2...)、英字(A, B)に対応
|
|
583
|
+
const phasePattern = /## Phase [\d.A-Z]+:\s*(.+?)(?:((.+?)))?/;
|
|
394
584
|
// Story作成(フェーズ検出付きパーサー)
|
|
395
585
|
const lines = tasksContent.split('\n');
|
|
396
586
|
let currentPhaseLabel = 'implementation'; // デフォルトは実装フェーズ
|
|
@@ -400,27 +590,98 @@ async function syncTasksToJIRA(featureName) {
|
|
|
400
590
|
// フェーズ検出
|
|
401
591
|
const phaseMatch = line.match(phasePattern);
|
|
402
592
|
if (phaseMatch) {
|
|
403
|
-
const
|
|
404
|
-
//
|
|
405
|
-
|
|
593
|
+
const phaseTitle = phaseMatch[1]; // フェーズタイトル全体
|
|
594
|
+
const phaseName = phaseMatch[2] || phaseTitle; // 括弧内のラベル(例: Requirements)または全体
|
|
595
|
+
// Phase番号を抽出(例: "0.1", "2", "A")
|
|
596
|
+
const phaseNumberMatch = line.match(/## Phase ([\d.A-Z]+):/);
|
|
597
|
+
const phaseNumber = phaseNumberMatch ? phaseNumberMatch[1] : '';
|
|
598
|
+
// フェーズ番号またはフェーズ名からラベルを決定
|
|
599
|
+
// 新ワークフロー構造に対応
|
|
600
|
+
if (phaseNumber === '0.0' ||
|
|
601
|
+
phaseName.includes('初期化') ||
|
|
602
|
+
phaseName.toLowerCase().includes('init')) {
|
|
603
|
+
currentPhaseLabel = 'spec-init';
|
|
604
|
+
}
|
|
605
|
+
else if (phaseNumber === '0.1' ||
|
|
606
|
+
phaseName.includes('要件定義') ||
|
|
607
|
+
phaseName.toLowerCase().includes('requirements')) {
|
|
406
608
|
currentPhaseLabel = 'requirements';
|
|
407
609
|
}
|
|
408
|
-
else if (
|
|
610
|
+
else if (phaseNumber === '0.2' ||
|
|
611
|
+
phaseName.includes('設計') ||
|
|
612
|
+
phaseName.toLowerCase().includes('design')) {
|
|
409
613
|
currentPhaseLabel = 'design';
|
|
410
614
|
}
|
|
411
|
-
else if (
|
|
615
|
+
else if (phaseNumber === '0.3' ||
|
|
616
|
+
phaseName.includes('テストタイプ') ||
|
|
617
|
+
phaseName.toLowerCase().includes('test-type') ||
|
|
618
|
+
phaseName.toLowerCase().includes('test type')) {
|
|
619
|
+
currentPhaseLabel = 'test-type-selection';
|
|
620
|
+
}
|
|
621
|
+
else if (phaseNumber === '0.4' ||
|
|
622
|
+
phaseName.includes('テスト仕様') ||
|
|
623
|
+
phaseName.toLowerCase().includes('test-spec') ||
|
|
624
|
+
phaseName.toLowerCase().includes('test spec')) {
|
|
625
|
+
currentPhaseLabel = 'test-spec';
|
|
626
|
+
}
|
|
627
|
+
else if (phaseNumber === '0.5' ||
|
|
628
|
+
phaseName.includes('タスク分割') ||
|
|
629
|
+
phaseName.toLowerCase().includes('tasks') ||
|
|
630
|
+
phaseName.toLowerCase().includes('task breakdown')) {
|
|
631
|
+
currentPhaseLabel = 'spec-tasks';
|
|
632
|
+
}
|
|
633
|
+
else if (phaseNumber === '0.6' ||
|
|
634
|
+
phaseName.includes('JIRA') ||
|
|
635
|
+
phaseName.toLowerCase().includes('jira')) {
|
|
636
|
+
currentPhaseLabel = 'jira-sync';
|
|
637
|
+
}
|
|
638
|
+
else if (phaseNumber === '1' ||
|
|
639
|
+
phaseName.includes('環境構築') ||
|
|
640
|
+
phaseName.toLowerCase().includes('environment') ||
|
|
641
|
+
phaseName.toLowerCase().includes('setup')) {
|
|
642
|
+
currentPhaseLabel = 'environment-setup';
|
|
643
|
+
}
|
|
644
|
+
else if (phaseNumber === '2' ||
|
|
645
|
+
phaseName.includes('実装') ||
|
|
646
|
+
phaseName.includes('TDD') ||
|
|
647
|
+
phaseName.toLowerCase().includes('implementation')) {
|
|
412
648
|
currentPhaseLabel = 'implementation';
|
|
413
649
|
}
|
|
414
|
-
else if (
|
|
415
|
-
|
|
650
|
+
else if (phaseNumber === 'A' ||
|
|
651
|
+
phaseNumber.toLowerCase() === 'a' ||
|
|
652
|
+
phaseName.includes('PR前') ||
|
|
653
|
+
phaseName.toLowerCase().includes('pr-test') ||
|
|
654
|
+
phaseName.toLowerCase().includes('pr test')) {
|
|
655
|
+
currentPhaseLabel = 'phase-a';
|
|
416
656
|
}
|
|
417
|
-
else if (
|
|
657
|
+
else if (phaseNumber === '3' ||
|
|
658
|
+
phaseName.includes('追加QA') ||
|
|
659
|
+
phaseName.includes('QA') ||
|
|
660
|
+
phaseName.includes('試験') ||
|
|
661
|
+
phaseName.toLowerCase().includes('testing') ||
|
|
662
|
+
phaseName.toLowerCase().includes('additional qa')) {
|
|
663
|
+
currentPhaseLabel = 'additional-qa';
|
|
664
|
+
}
|
|
665
|
+
else if (phaseNumber === 'B' ||
|
|
666
|
+
phaseNumber.toLowerCase() === 'b' ||
|
|
667
|
+
phaseName.includes('リリース準備テスト') ||
|
|
668
|
+
phaseName.toLowerCase().includes('release-test') ||
|
|
669
|
+
phaseName.toLowerCase().includes('release test')) {
|
|
670
|
+
currentPhaseLabel = 'phase-b';
|
|
671
|
+
}
|
|
672
|
+
else if (phaseNumber === '4' ||
|
|
673
|
+
phaseName.includes('リリース準備') ||
|
|
674
|
+
phaseName.toLowerCase().includes('release-prep') ||
|
|
675
|
+
phaseName.toLowerCase().includes('release preparation')) {
|
|
418
676
|
currentPhaseLabel = 'release-prep';
|
|
419
677
|
}
|
|
420
|
-
else if (
|
|
678
|
+
else if (phaseNumber === '5' ||
|
|
679
|
+
(phaseName.includes('リリース') && !phaseName.includes('準備')) ||
|
|
680
|
+
(phaseName.toLowerCase().includes('release') &&
|
|
681
|
+
!phaseName.toLowerCase().includes('prep'))) {
|
|
421
682
|
currentPhaseLabel = 'release';
|
|
422
683
|
}
|
|
423
|
-
console.log(`📌 Phase detected: ${
|
|
684
|
+
console.log(`📌 Phase detected: ${phaseTitle} (number: ${phaseNumber}, label: ${currentPhaseLabel})`);
|
|
424
685
|
continue;
|
|
425
686
|
}
|
|
426
687
|
// Story検出
|
|
@@ -432,9 +693,13 @@ async function syncTasksToJIRA(featureName) {
|
|
|
432
693
|
// 既に同じタイトルのStoryが存在するかチェック
|
|
433
694
|
if (existingStorySummaries.has(storySummary)) {
|
|
434
695
|
console.log(`Skipping Story (already exists): ${storyTitle}`);
|
|
435
|
-
const existing = existingStories.find((s) => s
|
|
696
|
+
const existing = existingStories.find((s) => s?.fields?.summary === storySummary);
|
|
436
697
|
if (existing) {
|
|
437
698
|
createdStories.push(existing.key);
|
|
699
|
+
existingStoryKeys.add(existing.key);
|
|
700
|
+
}
|
|
701
|
+
else {
|
|
702
|
+
console.warn(`⚠️ Warning: Story "${storyTitle}" is in summary set but not found in existingStories array`);
|
|
438
703
|
}
|
|
439
704
|
continue;
|
|
440
705
|
}
|
|
@@ -448,9 +713,9 @@ async function syncTasksToJIRA(featureName) {
|
|
|
448
713
|
const richDescription = createRichADF(storyDetails, currentPhaseLabel, githubUrl);
|
|
449
714
|
// 優先度のマッピング(デフォルト: Medium)
|
|
450
715
|
const priorityMap = {
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
716
|
+
High: 'High',
|
|
717
|
+
Medium: 'Medium',
|
|
718
|
+
Low: 'Low',
|
|
454
719
|
};
|
|
455
720
|
const priority = storyDetails.priority && priorityMap[storyDetails.priority]
|
|
456
721
|
? priorityMap[storyDetails.priority]
|
|
@@ -470,9 +735,13 @@ async function syncTasksToJIRA(featureName) {
|
|
|
470
735
|
summary: storySummary,
|
|
471
736
|
description: richDescription, // リッチなADF形式
|
|
472
737
|
issuetype: { id: storyIssueTypeId },
|
|
473
|
-
labels: [
|
|
474
|
-
|
|
475
|
-
|
|
738
|
+
labels: [
|
|
739
|
+
...projectMeta.confluenceLabels,
|
|
740
|
+
featureName,
|
|
741
|
+
currentPhaseLabel,
|
|
742
|
+
],
|
|
743
|
+
priority: { name: priority },
|
|
744
|
+
},
|
|
476
745
|
};
|
|
477
746
|
// 期限(Due Date)を設定
|
|
478
747
|
if (storyDetails.dueDate) {
|
|
@@ -516,7 +785,8 @@ async function syncTasksToJIRA(featureName) {
|
|
|
516
785
|
if (error.response?.data) {
|
|
517
786
|
console.error(' 📋 JIRA API Error Details:', JSON.stringify(error.response.data, null, 2));
|
|
518
787
|
// Story Pointsフィールドのエラーの場合、警告を表示
|
|
519
|
-
if (error.response.data.errors &&
|
|
788
|
+
if (error.response.data.errors &&
|
|
789
|
+
Object.keys(error.response.data.errors).some((key) => key.includes('customfield'))) {
|
|
520
790
|
console.error(' ⚠️ Story Pointsフィールドの設定に失敗しました。');
|
|
521
791
|
console.error(' 💡 環境変数 JIRA_STORY_POINTS_FIELD を正しいカスタムフィールドIDに設定してください。');
|
|
522
792
|
console.error(' 💡 JIRA管理画面でStory PointsのカスタムフィールドIDを確認してください。');
|
|
@@ -526,8 +796,8 @@ async function syncTasksToJIRA(featureName) {
|
|
|
526
796
|
}
|
|
527
797
|
}
|
|
528
798
|
// 新規作成数と再利用数を正確に計算
|
|
529
|
-
const newStoryCount = createdStories.filter(key => !existingStoryKeys.has(key)).length;
|
|
530
|
-
const reusedStoryCount = createdStories.filter(key => existingStoryKeys.has(key)).length;
|
|
799
|
+
const newStoryCount = createdStories.filter((key) => !existingStoryKeys.has(key)).length;
|
|
800
|
+
const reusedStoryCount = createdStories.filter((key) => existingStoryKeys.has(key)).length;
|
|
531
801
|
console.log('\n✅ JIRA sync completed');
|
|
532
802
|
console.log(` Epic: ${epic.key}`);
|
|
533
803
|
console.log(` Stories: ${createdStories.length} processed (${newStoryCount} new, ${reusedStoryCount} reused)`);
|
|
@@ -538,7 +808,7 @@ async function syncTasksToJIRA(featureName) {
|
|
|
538
808
|
projectKey: projectMeta.jiraProjectKey,
|
|
539
809
|
epicKey: epic.key,
|
|
540
810
|
epicUrl: `${jiraBaseUrl}/browse/${epic.key}`,
|
|
541
|
-
storyKeys: createdStories
|
|
811
|
+
storyKeys: createdStories,
|
|
542
812
|
});
|
|
543
813
|
}
|
|
544
814
|
catch (error) {
|