aiwf 0.3.23 → 0.4.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/.claude-plugin/marketplace.json +146 -0
- package/CHANGELOG.md +49 -1
- package/README.ko.md +118 -309
- package/README.md +68 -254
- package/docs/modernization/BROWNFIELD-GREENFIELD.ko.md +105 -0
- package/docs/modernization/CLAUDE-PLAN-REVIEW-2026-10-03.ko.md +77 -0
- package/docs/modernization/CLI-PRODUCTIVITY-REVIEWED-2026-10-03.ko.md +177 -0
- package/docs/modernization/CLI-PRODUCTIVITY.ko.md +193 -0
- package/docs/modernization/CORE-PACKAGE-PLAN.md +7 -0
- package/docs/modernization/DEEP-REVERSE-ENGINEERING.ko.md +88 -0
- package/docs/modernization/DELEGATION-OPTIONAL.ko.md +57 -0
- package/docs/modernization/DIRECTION.ko.md +56 -0
- package/docs/modernization/FULL-TEST-2026-10-03.md +49 -0
- package/docs/modernization/LEGACY-REMOVAL-PLAN.md +9 -0
- package/docs/modernization/PILOT-RESULT-2026-10-03.ko.md +85 -0
- package/docs/modernization/PILOT-UC-001.ko.md +79 -0
- package/docs/modernization/PLAN.md +28 -0
- package/docs/modernization/SKILLS.ko.md +105 -0
- package/docs/modernization/SPRINTABLE.ko.md +47 -0
- package/docs/modernization/SYNC-DOCS-VALIDATION-2026-10-04.ko.md +38 -0
- package/docs/modernization/VALIDATION.md +79 -0
- package/docs/modernization/evidence/example-review-packet.json +115 -0
- package/docs/modernization/evidence/example-spec-pin.json +48 -0
- package/docs/modernization/evidence/full-test-20261003/claude-lint.md +22 -0
- package/docs/modernization/evidence/full-test-20261003/claude-review-retry.md +56 -0
- package/docs/modernization/evidence/full-test-20261003/codex-review-packet.json +159 -0
- package/docs/modernization/evidence/full-test-20261003/src/expense.mjs +40 -0
- package/docs/modernization/evidence/full-test-20261003/summary.json +106 -0
- package/docs/modernization/evidence/full-test-20261003/tests/expense.test.mjs +61 -0
- package/docs/modernization/evidence/local-pilot-20261003/README.ko.md +40 -0
- package/docs/modernization/evidence/local-pilot-20261003/execution.json +1055 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/dependencies.json +24 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/dependencies.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/dependencies.stdout.txt +5 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/docs.json +24 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/docs.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/docs.stdout.txt +6 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/example.json +24 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/example.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/example.stdout.txt +5 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/local-docs.json +24 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/local-docs.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/local-docs.stdout.txt +6 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/node.json +23 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/node.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/node.stdout.txt +535 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/provenance.json +24 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/provenance.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/provenance.stdout.txt +5 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/readme-replay.json +24 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/readme-replay.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/readme-replay.stdout.txt +273 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/upstream.json +24 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/upstream.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/maintenance/upstream.stdout.txt +7 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/archive-service.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/archive-service.stdout.txt +148 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/archive-structure.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/archive-structure.stdout.txt +9 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-check.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-check.stdout.txt +67 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-evidence.json +34 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-packet.json +128 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-packet.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-packet.stdout.txt +128 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-pin.json +54 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-pin.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-pin.stdout.txt +60 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-readback.json +15 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-service.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-service.stdout.txt +82 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-structure.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/baseline-structure.stdout.txt +9 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/changed-check.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/changed-check.stdout.txt +79 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/changed-packet-refused.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/changed-packet-refused.stdout.txt +7 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/changed-pin-refresh.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/changed-pin-refresh.stdout.txt +66 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/changed-pin.json +60 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/changed-structure.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/changed-structure.stdout.txt +9 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/final-check.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/final-check.stdout.txt +73 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/final-evidence.json +34 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/final-packet.json +134 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/final-packet.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/final-packet.stdout.txt +134 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/final-readback.json +15 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/final-service.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/final-service.stdout.txt +148 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/final-structure.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/final-structure.stdout.txt +9 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/node-version.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/node-version.stdout.txt +1 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/python-version.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/python-version.stdout.txt +1 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/red-evidence.json +34 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/red-packet.json +134 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/red-packet.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/red-packet.stdout.txt +134 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/red-readback.json +15 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/red-service.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/red-service.stdout.txt +266 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/red-structure.stderr.txt +0 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/artifacts/red-structure.stdout.txt +9 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/docs/entity_model.md +33 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/docs/glossary.md +9 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/docs/plans/UC-001.md +20 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/docs/requirements.md +10 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/docs/test_cases/TC-001-submit-expense.md +37 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/docs/test_cases/TC-002-description-limit.md +42 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/docs/use_cases/UC-001-submit-expense.md +76 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/docs/use_cases.puml +12 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/docs/vision.md +21 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/src/expense.mjs +48 -0
- package/docs/modernization/evidence/local-pilot-20261003/project/tests/expense.test.mjs +116 -0
- package/docs/modernization/evidence/local-pilot-20261003/scope.json +31 -0
- package/docs/modernization/reviews/2026-10-03-claude-cli/CONTRACTS.ko.md +122 -0
- package/docs/modernization/reviews/2026-10-03-claude-cli/DIRECTION.ko.md +84 -0
- package/examples/spec-workflow/README.md +54 -0
- package/examples/spec-workflow/docs/entity_model.md +33 -0
- package/examples/spec-workflow/docs/glossary.md +9 -0
- package/examples/spec-workflow/docs/requirements.md +10 -0
- package/examples/spec-workflow/docs/test_cases/TC-001-submit-expense.md +37 -0
- package/examples/spec-workflow/docs/use_cases/UC-001-submit-expense.md +63 -0
- package/examples/spec-workflow/docs/use_cases.puml +12 -0
- package/examples/spec-workflow/docs/vision.md +21 -0
- package/package.json +37 -105
- package/plugins/aiwf-angular-jpa/.claude-plugin/plugin.json +11 -0
- package/plugins/aiwf-angular-jpa/LICENSE +201 -0
- package/plugins/aiwf-angular-jpa/NOTICE +16 -0
- package/plugins/aiwf-angular-jpa/README.md +54 -0
- package/plugins/aiwf-angular-jpa/UPSTREAM.json +35 -0
- package/plugins/aiwf-angular-jpa/agents/uc-coverage.md +263 -0
- package/plugins/aiwf-angular-jpa/rules/mcp-servers.md +64 -0
- package/plugins/aiwf-angular-jpa/skills/coverage-check/SKILL.md +190 -0
- package/plugins/aiwf-angular-jpa/skills/flyway-migration/SKILL.md +119 -0
- package/plugins/aiwf-angular-jpa/skills/implement/SKILL.md +528 -0
- package/plugins/aiwf-angular-jpa/skills/implement/references/module-layout.md +95 -0
- package/plugins/aiwf-angular-jpa/skills/playwright-test/SKILL.md +360 -0
- package/plugins/aiwf-angular-jpa/skills/spring-boot-test/SKILL.md +504 -0
- package/plugins/aiwf-angular-jpa/skills/vitest-test/SKILL.md +309 -0
- package/plugins/aiwf-blazor-dotnet/.claude-plugin/plugin.json +11 -0
- package/plugins/aiwf-blazor-dotnet/LICENSE +201 -0
- package/plugins/aiwf-blazor-dotnet/NOTICE +16 -0
- package/plugins/aiwf-blazor-dotnet/README.md +53 -0
- package/plugins/aiwf-blazor-dotnet/UPSTREAM.json +31 -0
- package/plugins/aiwf-blazor-dotnet/rules/mcp-servers.md +25 -0
- package/plugins/aiwf-blazor-dotnet/skills/bunit-test/SKILL.md +110 -0
- package/plugins/aiwf-blazor-dotnet/skills/dotnet-test/SKILL.md +108 -0
- package/plugins/aiwf-blazor-dotnet/skills/ef-migration/SKILL.md +43 -0
- package/plugins/aiwf-blazor-dotnet/skills/implement/SKILL.md +149 -0
- package/plugins/aiwf-blazor-dotnet/skills/playwright-test/SKILL.md +130 -0
- package/plugins/aiwf-core/.claude-plugin/plugin.json +11 -0
- package/plugins/aiwf-core/LICENSE +201 -0
- package/plugins/aiwf-core/NOTICE +20 -0
- package/plugins/aiwf-core/README.md +38 -0
- package/plugins/aiwf-core/UPSTREAM.json +50 -0
- package/plugins/aiwf-core/skills/entity-model/SKILL.md +160 -0
- package/plugins/aiwf-core/skills/entity-model/references/REFERENCE.md +36 -0
- package/plugins/aiwf-core/skills/requirements/SKILL.md +157 -0
- package/plugins/aiwf-core/skills/requirements/references/REFERENCE.md +70 -0
- package/plugins/aiwf-core/skills/requirements/references/glossary.md +7 -0
- package/plugins/aiwf-core/skills/reverse-engineer/SKILL.md +509 -0
- package/plugins/aiwf-core/skills/reverse-engineer/references/stack-signals.md +210 -0
- package/plugins/aiwf-core/skills/spec-review/SKILL.md +197 -0
- package/plugins/aiwf-core/skills/spec-review/references/lint-codes.md +63 -0
- package/plugins/aiwf-core/skills/spec-review/references/review-checklist.md +189 -0
- package/plugins/aiwf-core/skills/spec-review/scripts/spec_lint.py +1216 -0
- package/plugins/aiwf-core/skills/test-case/SKILL.md +138 -0
- package/plugins/aiwf-core/skills/test-case/references/example-process.bpmn +118 -0
- package/plugins/aiwf-core/skills/test-case/references/example.md +38 -0
- package/plugins/aiwf-core/skills/test-case/references/test-case.md +35 -0
- package/plugins/aiwf-core/skills/test-case/scripts/bpmn_paths.py +542 -0
- package/plugins/aiwf-core/skills/use-case-diagram/SKILL.md +99 -0
- package/plugins/aiwf-core/skills/use-case-spec/SKILL.md +257 -0
- package/plugins/aiwf-core/skills/use-case-spec/references/clarify-checklist.md +70 -0
- package/plugins/aiwf-core/skills/use-case-spec/references/example.md +88 -0
- package/plugins/aiwf-core/skills/use-case-spec/references/format-spec.md +246 -0
- package/plugins/aiwf-core/skills/use-case-spec/references/use-case.md +50 -0
- package/plugins/aiwf-core/skills/use-case-spec/scripts/validate_use_case.py +941 -0
- package/plugins/aiwf-delegate-claude/.claude-plugin/plugin.json +9 -0
- package/plugins/aiwf-delegate-claude/LICENSE +21 -0
- package/plugins/aiwf-delegate-claude/NOTICE +4 -0
- package/plugins/aiwf-delegate-claude/README.md +10 -0
- package/plugins/aiwf-delegate-claude/plugin.json +13 -0
- package/plugins/aiwf-delegate-claude/skills/delegate-claude/LICENSE +21 -0
- package/plugins/aiwf-delegate-claude/skills/delegate-claude/NOTICE +4 -0
- package/plugins/aiwf-delegate-claude/skills/delegate-claude/SKILL.md +49 -0
- package/plugins/aiwf-delegate-claude/skills/delegate-claude/agents/openai.yaml +2 -0
- package/plugins/aiwf-delegate-codex/.claude-plugin/plugin.json +9 -0
- package/plugins/aiwf-delegate-codex/LICENSE +21 -0
- package/plugins/aiwf-delegate-codex/NOTICE +4 -0
- package/plugins/aiwf-delegate-codex/README.md +10 -0
- package/plugins/aiwf-delegate-codex/plugin.json +13 -0
- package/plugins/aiwf-delegate-codex/skills/delegate-codex/LICENSE +21 -0
- package/plugins/aiwf-delegate-codex/skills/delegate-codex/NOTICE +4 -0
- package/plugins/aiwf-delegate-codex/skills/delegate-codex/SKILL.md +47 -0
- package/plugins/aiwf-delegate-codex/skills/delegate-codex/agents/openai.yaml +2 -0
- package/plugins/aiwf-nestjs-nextjs/.claude-plugin/plugin.json +11 -0
- package/plugins/aiwf-nestjs-nextjs/LICENSE +201 -0
- package/plugins/aiwf-nestjs-nextjs/NOTICE +16 -0
- package/plugins/aiwf-nestjs-nextjs/README.md +53 -0
- package/plugins/aiwf-nestjs-nextjs/UPSTREAM.json +32 -0
- package/plugins/aiwf-nestjs-nextjs/rules/mcp-servers.md +59 -0
- package/plugins/aiwf-nestjs-nextjs/skills/drizzle-migration/SKILL.md +222 -0
- package/plugins/aiwf-nestjs-nextjs/skills/implement/SKILL.md +393 -0
- package/plugins/aiwf-nestjs-nextjs/skills/implement/references/project-layout.md +158 -0
- package/plugins/aiwf-nestjs-nextjs/skills/nest-test/SKILL.md +300 -0
- package/plugins/aiwf-nestjs-nextjs/skills/playwright-test/SKILL.md +245 -0
- package/plugins/aiwf-nestjs-nextjs/skills/react-test/SKILL.md +217 -0
- package/plugins/aiwf-spec/.claude-plugin/plugin.json +9 -0
- package/plugins/aiwf-spec/LICENSE +201 -0
- package/plugins/aiwf-spec/NOTICE +20 -0
- package/plugins/aiwf-spec/README.md +36 -0
- package/plugins/aiwf-spec/skills/sync-docs/SKILL.md +47 -0
- package/plugins/aiwf-spec/skills/workflow/SKILL.md +42 -0
- package/plugins/aiwf-vaadin-jooq/.claude-plugin/plugin.json +11 -0
- package/plugins/aiwf-vaadin-jooq/LICENSE +201 -0
- package/plugins/aiwf-vaadin-jooq/NOTICE +16 -0
- package/plugins/aiwf-vaadin-jooq/README.md +56 -0
- package/plugins/aiwf-vaadin-jooq/UPSTREAM.json +43 -0
- package/plugins/aiwf-vaadin-jooq/agents/uc-coverage.md +260 -0
- package/plugins/aiwf-vaadin-jooq/rules/mcp-servers.md +44 -0
- package/plugins/aiwf-vaadin-jooq/skills/browserless-test/SKILL.md +407 -0
- package/plugins/aiwf-vaadin-jooq/skills/browserless-test/references/UC001ManagePersonsTest.java +111 -0
- package/plugins/aiwf-vaadin-jooq/skills/coverage-check/SKILL.md +190 -0
- package/plugins/aiwf-vaadin-jooq/skills/flyway-migration/SKILL.md +70 -0
- package/plugins/aiwf-vaadin-jooq/skills/hilla-test/SKILL.md +350 -0
- package/plugins/aiwf-vaadin-jooq/skills/hilla-test/references/UC001ManagePersonsServiceTest.java +81 -0
- package/plugins/aiwf-vaadin-jooq/skills/hilla-test/references/UC001ManagePersonsViewTest.tsx +87 -0
- package/plugins/aiwf-vaadin-jooq/skills/implement/SKILL.md +196 -0
- package/plugins/aiwf-vaadin-jooq/skills/implement-hilla/SKILL.md +216 -0
- package/plugins/aiwf-vaadin-jooq/skills/karibu-test/SKILL.md +298 -0
- package/plugins/aiwf-vaadin-jooq/skills/karibu-test/references/UC001ManagePersonsTest.java +93 -0
- package/plugins/aiwf-vaadin-jooq/skills/playwright-test/SKILL.md +237 -0
- package/plugins/aiwf-vaadin-jooq/skills/playwright-test/references/ExampleViewIT.java +143 -0
- package/plugins/aiwf-vaadin-jooq/skills/playwright-test/references/TC001CustomerOnboardingIT.java +144 -0
- package/plugins/aiwf-vaadin-jooq/skills/playwright-test/references/dramafinder-api.md +190 -0
- package/scripts/check-dependencies.js +26 -96
- package/scripts/install-spec-skills.mjs +126 -0
- package/scripts/validate-spec-plugin.mjs +145 -0
- package/src/cli/spec-cli.js +292 -0
- package/src/lib/spec-workflow.js +982 -0
- package/docs/ADR_MANAGEMENT_GUIDE.ko.md +0 -602
- package/docs/ADR_MANAGEMENT_GUIDE.md +0 -602
- package/docs/AI-WORKFLOW.ko.md +0 -299
- package/docs/AI-WORKFLOW.md +0 -401
- package/docs/API_REFERENCE_FULL.ko.md +0 -1135
- package/docs/API_REFERENCE_FULL.md +0 -1135
- package/docs/ARCHITECTURE.ko.md +0 -314
- package/docs/ARCHITECTURE.md +0 -314
- package/docs/CLI_USAGE_GUIDE.ko.md +0 -634
- package/docs/CLI_USAGE_GUIDE.md +0 -640
- package/docs/CODE_CLEANUP_GUIDE.ko.md +0 -415
- package/docs/CODE_CLEANUP_GUIDE.md +0 -415
- package/docs/COMMANDS_GUIDE.ko.md +0 -1037
- package/docs/COMMANDS_GUIDE.md +0 -1037
- package/docs/CONTRIBUTING.ko.md +0 -408
- package/docs/CONTRIBUTING.md +0 -408
- package/docs/DEVELOPMENT_GUIDE.ko.md +0 -440
- package/docs/DEVELOPMENT_GUIDE.md +0 -727
- package/docs/EXAMPLES.ko.md +0 -695
- package/docs/EXAMPLES.md +0 -693
- package/docs/GETTING_STARTED.ko.md +0 -219
- package/docs/GETTING_STARTED.md +0 -476
- package/docs/MODULE_MANAGEMENT_GUIDE.ko.md +0 -289
- package/docs/MODULE_MANAGEMENT_GUIDE.md +0 -289
- package/docs/PERFORMANCE_ARCHITECTURE.md +0 -494
- package/docs/PERFORMANCE_GUIDELINES.ko.md +0 -388
- package/docs/PERFORMANCE_GUIDELINES.md +0 -553
- package/docs/PRD.ko.md +0 -148
- package/docs/PRD.md +0 -150
- package/docs/ROADMAP_v0.4.0.md +0 -286
- package/docs/STATE_MANAGEMENT_GUIDE.ko.md +0 -278
- package/docs/STATE_MANAGEMENT_GUIDE.md +0 -278
- package/docs/TROUBLESHOOTING.ko.md +0 -366
- package/docs/TROUBLESHOOTING.md +0 -722
- package/docs/VALIDATOR_API.ko.md +0 -324
- package/docs/VALIDATOR_API.md +0 -324
- package/docs/YOLO_SYSTEM_GUIDE.ko.md +0 -542
- package/docs/YOLO_SYSTEM_GUIDE.md +0 -542
- package/docs/designs/AI_PERSONA_SYSTEM_DESIGN.md +0 -516
- package/docs/designs/API_DOCUMENTATION.md +0 -932
- package/docs/designs/API_REFERENCE.md +0 -979
- package/docs/designs/Enhanced_Installation_Flow_Design.md +0 -498
- package/docs/designs/aiwf-metadata-system-prd.md +0 -127
- package/docs/designs/offline-template-cache.md +0 -323
- package/docs/designs/persona-aware-compression.md +0 -168
- package/docs/guides/ai-personas-guide-ko.md +0 -239
- package/docs/guides/ai-personas-guide.md +0 -239
- package/docs/guides/checkpoint-system-guide-ko.md +0 -356
- package/docs/guides/checkpoint-system-guide.md +0 -356
- package/docs/guides/context-compression-guide-ko.md +0 -313
- package/docs/guides/context-compression-guide.md +0 -313
- package/docs/guides/independent-sprint-guide-ko.md +0 -321
- package/docs/guides/independent-sprint-guide.md +0 -321
- package/rules/global/aiwf-code-style-guide.md +0 -30
- package/rules/global/aiwf-coding-principles.md +0 -33
- package/rules/global/aiwf-development-process.md +0 -41
- package/rules/global/aiwf-global-rules.md +0 -84
- package/rules/manual/aiwf-generate-plan-docs.md +0 -280
- package/scripts/run-integration-tests.js +0 -317
- package/scripts/update-file-lists.js +0 -267
- package/scripts/validate-commands.js +0 -254
- package/src/cli/index.js +0 -184
- package/src/commands/sprint-independent.js +0 -393
- package/src/commands/state.js +0 -1164
- package/src/commands/yolo-config.js +0 -502
- package/src/config/file-lists.js +0 -147
- package/src/config/yolo-config-template.yaml +0 -168
- package/src/lib/backup-manager.js +0 -271
- package/src/lib/file-downloader.js +0 -304
- package/src/lib/installer.js +0 -1270
- package/src/lib/rollback-manager.js +0 -418
- package/src/lib/validator.js +0 -376
- package/src/utils/checkpoint-manager.js +0 -435
- package/src/utils/language-utils.js +0 -331
- package/src/utils/messages.js +0 -190
- package/src/utils/paths.js +0 -112
package/src/lib/installer.js
DELETED
|
@@ -1,1270 +0,0 @@
|
|
|
1
|
-
import fs from 'fs/promises';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import os from 'os';
|
|
4
|
-
import ora from 'ora';
|
|
5
|
-
import chalk from 'chalk';
|
|
6
|
-
import prompts from 'prompts';
|
|
7
|
-
import {
|
|
8
|
-
fetchContent,
|
|
9
|
-
downloadFile,
|
|
10
|
-
downloadDirectory,
|
|
11
|
-
GITHUB_RAW_URL,
|
|
12
|
-
GITHUB_CONTENT_PREFIX
|
|
13
|
-
} from './file-downloader.js';
|
|
14
|
-
import {
|
|
15
|
-
backupCommandsAndDocs,
|
|
16
|
-
restoreFromBackup,
|
|
17
|
-
getCurrentBackupDir,
|
|
18
|
-
setBackupDir
|
|
19
|
-
} from './backup-manager.js';
|
|
20
|
-
import { getMessages } from '../utils/messages.js';
|
|
21
|
-
import {
|
|
22
|
-
validateInstallation,
|
|
23
|
-
displaySpecCompliantValidationResults
|
|
24
|
-
} from './validator.js';
|
|
25
|
-
import { rollbackTool } from './rollback-manager.js';
|
|
26
|
-
import {
|
|
27
|
-
detectLanguage,
|
|
28
|
-
loadUserLanguageConfig,
|
|
29
|
-
saveUserLanguageConfig,
|
|
30
|
-
getInstallationLanguagePath,
|
|
31
|
-
SUPPORTED_LANGUAGES,
|
|
32
|
-
DEFAULT_LANGUAGE
|
|
33
|
-
} from '../utils/language-utils.js';
|
|
34
|
-
import {
|
|
35
|
-
FILES,
|
|
36
|
-
TOOL_DIRS,
|
|
37
|
-
AIWF_DIRS,
|
|
38
|
-
BASE_DIRS,
|
|
39
|
-
getAllAiwfDirectories,
|
|
40
|
-
getAllClaudeMdFiles
|
|
41
|
-
} from '../utils/paths.js';
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Check if AIWF is already installed
|
|
45
|
-
* @returns {Promise<boolean>} Installation status
|
|
46
|
-
*/
|
|
47
|
-
async function checkExistingInstallation() {
|
|
48
|
-
const flagExists = await fs
|
|
49
|
-
.access(FILES.INSTALLED_FLAG)
|
|
50
|
-
.then(() => true)
|
|
51
|
-
.catch(() => false);
|
|
52
|
-
return flagExists;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Check if existing project files exist
|
|
57
|
-
* @returns {Promise<{hasProject: boolean, projectFiles: string[]}>} Project status
|
|
58
|
-
*/
|
|
59
|
-
async function checkExistingProject() {
|
|
60
|
-
const projectFiles = [];
|
|
61
|
-
let hasProject = false;
|
|
62
|
-
|
|
63
|
-
// Check for key project files
|
|
64
|
-
const keyFiles = [
|
|
65
|
-
FILES.PROJECT_MANIFEST,
|
|
66
|
-
path.join(AIWF_DIRS.PROJECT_DOCS, 'README.md'),
|
|
67
|
-
path.join(AIWF_DIRS.REQUIREMENTS, 'requirements.md'),
|
|
68
|
-
path.join(AIWF_DIRS.SPRINTS, 'sprint-01.md'),
|
|
69
|
-
path.join(AIWF_DIRS.GENERAL_TASKS, 'tasks.md'),
|
|
70
|
-
path.join(AIWF_DIRS.ARCHITECTURAL_DECISIONS, 'decisions.md'),
|
|
71
|
-
path.join(AIWF_DIRS.STATE_OF_PROJECT, 'current-state.md')
|
|
72
|
-
];
|
|
73
|
-
|
|
74
|
-
for (const file of keyFiles) {
|
|
75
|
-
try {
|
|
76
|
-
await fs.access(file);
|
|
77
|
-
projectFiles.push(file);
|
|
78
|
-
hasProject = true;
|
|
79
|
-
} catch (error) {
|
|
80
|
-
// File doesn't exist, continue
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Check for any .md files in project directories
|
|
85
|
-
const projectDirs = [
|
|
86
|
-
AIWF_DIRS.PROJECT_DOCS,
|
|
87
|
-
AIWF_DIRS.REQUIREMENTS,
|
|
88
|
-
AIWF_DIRS.SPRINTS,
|
|
89
|
-
AIWF_DIRS.GENERAL_TASKS,
|
|
90
|
-
AIWF_DIRS.ARCHITECTURAL_DECISIONS,
|
|
91
|
-
AIWF_DIRS.STATE_OF_PROJECT
|
|
92
|
-
];
|
|
93
|
-
|
|
94
|
-
for (const dir of projectDirs) {
|
|
95
|
-
try {
|
|
96
|
-
const files = await fs.readdir(dir);
|
|
97
|
-
const mdFiles = files.filter(file => file.endsWith('.md'));
|
|
98
|
-
if (mdFiles.length > 0) {
|
|
99
|
-
hasProject = true;
|
|
100
|
-
projectFiles.push(...mdFiles.map(file => path.join(dir, file)));
|
|
101
|
-
}
|
|
102
|
-
} catch (error) {
|
|
103
|
-
// Directory doesn't exist, continue
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
return { hasProject, projectFiles };
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Log with spinner for debug mode
|
|
112
|
-
* @param {Object} spinner - Ora spinner instance
|
|
113
|
-
* @param {string} message - Message to log
|
|
114
|
-
* @param {boolean} debugLog - Debug mode flag
|
|
115
|
-
*/
|
|
116
|
-
function logWithSpinner(spinner, message, debugLog) {
|
|
117
|
-
if (debugLog) console.log(chalk.gray(message));
|
|
118
|
-
if (spinner) spinner.text = message;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Select language for installation
|
|
123
|
-
* @param {Object} options - Installation options
|
|
124
|
-
* @returns {Promise<string>} Selected language
|
|
125
|
-
*/
|
|
126
|
-
async function selectLanguage(options) {
|
|
127
|
-
let selectedLanguage = DEFAULT_LANGUAGE;
|
|
128
|
-
|
|
129
|
-
if (!options.force) {
|
|
130
|
-
// 1. Check existing configuration
|
|
131
|
-
const existingConfig = await loadUserLanguageConfig();
|
|
132
|
-
|
|
133
|
-
if (existingConfig && existingConfig.language) {
|
|
134
|
-
selectedLanguage = existingConfig.language;
|
|
135
|
-
console.log(
|
|
136
|
-
chalk.blue(
|
|
137
|
-
`🌐 기존 언어 설정을 사용합니다 / Using existing language setting: ${selectedLanguage}`
|
|
138
|
-
)
|
|
139
|
-
);
|
|
140
|
-
} else {
|
|
141
|
-
// 2. Try auto-detection
|
|
142
|
-
const detectedLang = await detectLanguage();
|
|
143
|
-
|
|
144
|
-
console.log(
|
|
145
|
-
chalk.gray(`🔍 시스템 언어 감지 / System language detected: ${detectedLang}`)
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
// 3. Prompt user for language selection
|
|
149
|
-
const languageResponse = await prompts({
|
|
150
|
-
type: 'select',
|
|
151
|
-
name: 'language',
|
|
152
|
-
message: 'Please select language / 언어를 선택해주세요:',
|
|
153
|
-
choices: [
|
|
154
|
-
{ title: 'English', value: 'en' },
|
|
155
|
-
{ title: '한국어 (Korean)', value: 'ko' }
|
|
156
|
-
],
|
|
157
|
-
initial:
|
|
158
|
-
SUPPORTED_LANGUAGES.indexOf(detectedLang) !== -1
|
|
159
|
-
? SUPPORTED_LANGUAGES.indexOf(detectedLang)
|
|
160
|
-
: 0
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
if (languageResponse.language) {
|
|
164
|
-
selectedLanguage = languageResponse.language;
|
|
165
|
-
} else {
|
|
166
|
-
selectedLanguage = detectedLang;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// 4. Save selected language
|
|
170
|
-
try {
|
|
171
|
-
await saveUserLanguageConfig(selectedLanguage, {
|
|
172
|
-
autoDetect: true,
|
|
173
|
-
fallback: DEFAULT_LANGUAGE
|
|
174
|
-
});
|
|
175
|
-
console.log(
|
|
176
|
-
chalk.green(
|
|
177
|
-
`✅ 언어 설정이 저장되었습니다 / Language preference saved: ${selectedLanguage}`
|
|
178
|
-
)
|
|
179
|
-
);
|
|
180
|
-
} catch (error) {
|
|
181
|
-
console.warn(
|
|
182
|
-
chalk.yellow(
|
|
183
|
-
`⚠️ 언어 설정 저장 실패 / Failed to save language preference: ${error.message}`
|
|
184
|
-
)
|
|
185
|
-
);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
} else {
|
|
189
|
-
// Force mode: try auto-detection
|
|
190
|
-
try {
|
|
191
|
-
selectedLanguage = await detectLanguage();
|
|
192
|
-
} catch (error) {
|
|
193
|
-
selectedLanguage = DEFAULT_LANGUAGE;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return selectedLanguage;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Handle existing installation
|
|
202
|
-
* @param {Object} msg - Localized messages
|
|
203
|
-
* @param {Object} options - Installation options
|
|
204
|
-
* @returns {Promise<{continue: boolean, installType: string, preserveProject: boolean}>} Installation decision
|
|
205
|
-
*/
|
|
206
|
-
async function handleExistingInstallation(msg, options) {
|
|
207
|
-
|
|
208
|
-
if (options.force) {
|
|
209
|
-
return { continue: true, installType: 'reinstall', preserveProject: false };
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// First, ask about installation type
|
|
213
|
-
const installResponse = await prompts({
|
|
214
|
-
type: 'select',
|
|
215
|
-
name: 'action',
|
|
216
|
-
message: msg.existingDetected,
|
|
217
|
-
choices: [
|
|
218
|
-
{ title: msg.updateOption, value: 'update' },
|
|
219
|
-
{ title: msg.reinstallOption, value: 'reinstall' },
|
|
220
|
-
{ title: msg.skipOption, value: 'skip' },
|
|
221
|
-
{ title: msg.cancelOption, value: 'cancel' }
|
|
222
|
-
]
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
if (installResponse.action === 'skip' || installResponse.action === 'cancel') {
|
|
228
|
-
console.log(chalk.yellow(msg.installCancelled));
|
|
229
|
-
process.exit(0);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
let preserveProject = true; // Default to preserve
|
|
233
|
-
|
|
234
|
-
// If reinstalling, don't preserve project (complete fresh install)
|
|
235
|
-
if (installResponse.action === 'reinstall') {
|
|
236
|
-
preserveProject = false;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Backup if updating
|
|
240
|
-
if (installResponse.action === 'update') {
|
|
241
|
-
await backupCommandsAndDocs(msg);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
return {
|
|
245
|
-
continue: true,
|
|
246
|
-
installType: installResponse.action,
|
|
247
|
-
preserveProject
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* Backup project files
|
|
253
|
-
* @param {string[]} projectFiles - List of project files to backup
|
|
254
|
-
* @param {Object} msg - Localized messages
|
|
255
|
-
* @returns {Promise<string>} Backup directory path
|
|
256
|
-
*/
|
|
257
|
-
async function backupProjectFiles(projectFiles, msg) {
|
|
258
|
-
if (projectFiles.length === 0) {
|
|
259
|
-
return null;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
const backupDir = path.join(os.tmpdir(), `aiwf-project-backup-${Date.now()}`);
|
|
263
|
-
await fs.mkdir(backupDir, { recursive: true });
|
|
264
|
-
|
|
265
|
-
console.log(chalk.cyan('💾 Backing up project files...'));
|
|
266
|
-
|
|
267
|
-
for (const file of projectFiles) {
|
|
268
|
-
try {
|
|
269
|
-
const relativePath = path.relative(process.cwd(), file);
|
|
270
|
-
const backupPath = path.join(backupDir, relativePath);
|
|
271
|
-
const backupDirPath = path.dirname(backupPath);
|
|
272
|
-
|
|
273
|
-
await fs.mkdir(backupDirPath, { recursive: true });
|
|
274
|
-
await fs.copyFile(file, backupPath);
|
|
275
|
-
} catch (error) {
|
|
276
|
-
console.log(chalk.yellow(`Warning: Could not backup ${file}: ${error.message}`));
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
console.log(chalk.green(`✅ Project files backed up to: ${backupDir}`));
|
|
281
|
-
return backupDir;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* Restore project files from backup
|
|
286
|
-
* @param {string} backupDir - Backup directory path
|
|
287
|
-
* @param {Object} msg - Localized messages
|
|
288
|
-
*/
|
|
289
|
-
async function restoreProjectFiles(backupDir, msg) {
|
|
290
|
-
if (!backupDir) {
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
console.log(chalk.cyan('🔄 Restoring project files...'));
|
|
295
|
-
|
|
296
|
-
try {
|
|
297
|
-
const backupFiles = await fs.readdir(backupDir, { recursive: true });
|
|
298
|
-
|
|
299
|
-
for (const file of backupFiles) {
|
|
300
|
-
const backupFilePath = path.join(backupDir, file);
|
|
301
|
-
const stat = await fs.stat(backupFilePath);
|
|
302
|
-
|
|
303
|
-
if (stat.isFile()) {
|
|
304
|
-
const targetPath = path.join(process.cwd(), file);
|
|
305
|
-
const targetDir = path.dirname(targetPath);
|
|
306
|
-
|
|
307
|
-
await fs.mkdir(targetDir, { recursive: true });
|
|
308
|
-
await fs.copyFile(backupFilePath, targetPath);
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
console.log(chalk.green(msg.projectPreserved));
|
|
313
|
-
|
|
314
|
-
// Clean up backup directory
|
|
315
|
-
await fs.rm(backupDir, { recursive: true, force: true });
|
|
316
|
-
} catch (error) {
|
|
317
|
-
console.log(chalk.yellow(`Warning: Could not restore some project files: ${error.message}`));
|
|
318
|
-
console.log(chalk.yellow(`Backup preserved at: ${backupDir}`));
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
/**
|
|
323
|
-
* Remove project files
|
|
324
|
-
* @param {string[]} projectFiles - List of project files to remove
|
|
325
|
-
* @param {Object} msg - Localized messages
|
|
326
|
-
*/
|
|
327
|
-
async function removeProjectFiles(projectFiles, msg) {
|
|
328
|
-
if (projectFiles.length === 0) {
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
console.log(chalk.cyan('🗑️ Removing existing project files...'));
|
|
333
|
-
|
|
334
|
-
for (const file of projectFiles) {
|
|
335
|
-
try {
|
|
336
|
-
await fs.unlink(file);
|
|
337
|
-
} catch (error) {
|
|
338
|
-
// File might already be deleted, continue
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
console.log(chalk.green(msg.projectOverwritten));
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* Create AIWF directory structure
|
|
347
|
-
*/
|
|
348
|
-
async function createDirectoryStructure() {
|
|
349
|
-
const aiwfDirs = getAllAiwfDirectories();
|
|
350
|
-
|
|
351
|
-
// Create AIWF directories
|
|
352
|
-
for (const dir of aiwfDirs) {
|
|
353
|
-
await fs.mkdir(dir, { recursive: true });
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// Create tool-specific directories
|
|
357
|
-
await fs.mkdir(TOOL_DIRS.CURSOR_RULES, { recursive: true });
|
|
358
|
-
await fs.mkdir(TOOL_DIRS.WINDSURF_RULES, { recursive: true });
|
|
359
|
-
await fs.mkdir(TOOL_DIRS.CLAUDE_AGENTS, { recursive: true });
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
/**
|
|
363
|
-
* Download manifest file
|
|
364
|
-
* @param {string} languagePath - Language path
|
|
365
|
-
* @param {Object} spinner - Ora spinner instance
|
|
366
|
-
* @param {Object} msg - Localized messages
|
|
367
|
-
* @param {boolean} debugLog - Debug mode flag
|
|
368
|
-
*/
|
|
369
|
-
async function downloadManifest(languagePath, spinner, msg, debugLog) {
|
|
370
|
-
logWithSpinner(spinner, msg.downloading, debugLog);
|
|
371
|
-
try {
|
|
372
|
-
const manifestUrl = `${GITHUB_RAW_URL}/${GITHUB_CONTENT_PREFIX}/${languagePath}/.aiwf/00_PROJECT_MANIFEST.md`;
|
|
373
|
-
await downloadFile(manifestUrl, FILES.PROJECT_MANIFEST);
|
|
374
|
-
} catch (error) {
|
|
375
|
-
// If manifest doesn't exist, that's okay
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
/**
|
|
380
|
-
* Download templates
|
|
381
|
-
* @param {string} languagePath - Language path
|
|
382
|
-
* @param {Object} spinner - Ora spinner instance
|
|
383
|
-
* @param {Object} msg - Localized messages
|
|
384
|
-
* @param {boolean} debugLog - Debug mode flag
|
|
385
|
-
*/
|
|
386
|
-
async function downloadTemplates(languagePath, spinner, msg, debugLog) {
|
|
387
|
-
try {
|
|
388
|
-
logWithSpinner(spinner, msg.downloadingTemplates, debugLog);
|
|
389
|
-
|
|
390
|
-
// .aiwf 전체 디렉토리를 동적으로 다운로드
|
|
391
|
-
logWithSpinner(spinner, `Downloading .aiwf directory structure...`, debugLog);
|
|
392
|
-
await downloadDirectory(
|
|
393
|
-
`${GITHUB_CONTENT_PREFIX}/${languagePath}/.aiwf`,
|
|
394
|
-
AIWF_DIRS.ROOT,
|
|
395
|
-
spinner,
|
|
396
|
-
msg,
|
|
397
|
-
languagePath,
|
|
398
|
-
true // useDynamicFetch = true
|
|
399
|
-
);
|
|
400
|
-
|
|
401
|
-
logWithSpinner(spinner, `Successfully downloaded .aiwf directory`, debugLog);
|
|
402
|
-
} catch (error) {
|
|
403
|
-
logWithSpinner(spinner, `Error downloading .aiwf: ${error.message}`, debugLog);
|
|
404
|
-
|
|
405
|
-
// Fallback: 개별 디렉토리 다운로드 시도
|
|
406
|
-
try {
|
|
407
|
-
logWithSpinner(spinner, `Trying individual directory downloads...`, debugLog);
|
|
408
|
-
|
|
409
|
-
await downloadDirectory(
|
|
410
|
-
`${GITHUB_CONTENT_PREFIX}/${languagePath}/.aiwf/98_PROMPTS`,
|
|
411
|
-
AIWF_DIRS.PROMPTS,
|
|
412
|
-
spinner,
|
|
413
|
-
msg,
|
|
414
|
-
languagePath,
|
|
415
|
-
true
|
|
416
|
-
);
|
|
417
|
-
|
|
418
|
-
await downloadDirectory(
|
|
419
|
-
`${GITHUB_CONTENT_PREFIX}/${languagePath}/.aiwf/99_TEMPLATES`,
|
|
420
|
-
AIWF_DIRS.TEMPLATES,
|
|
421
|
-
spinner,
|
|
422
|
-
msg,
|
|
423
|
-
languagePath,
|
|
424
|
-
true
|
|
425
|
-
);
|
|
426
|
-
} catch (fallbackError) {
|
|
427
|
-
logWithSpinner(spinner, msg.templatesNotFound, debugLog);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
/**
|
|
433
|
-
* Update CLAUDE.md documentation files
|
|
434
|
-
* @param {string} languagePath - Language path
|
|
435
|
-
* @param {Object} spinner - Ora spinner instance
|
|
436
|
-
* @param {Object} msg - Localized messages
|
|
437
|
-
* @param {boolean} debugLog - Debug mode flag
|
|
438
|
-
*/
|
|
439
|
-
async function updateDocumentation(languagePath, spinner, msg, debugLog) {
|
|
440
|
-
logWithSpinner(spinner, msg.updatingDocs, debugLog);
|
|
441
|
-
const claudeFiles = getAllClaudeMdFiles();
|
|
442
|
-
|
|
443
|
-
for (const claudeFile of claudeFiles) {
|
|
444
|
-
try {
|
|
445
|
-
const relativePath = claudeFile.replace(process.cwd() + '/', '');
|
|
446
|
-
const claudeUrl = `${GITHUB_RAW_URL}/${GITHUB_CONTENT_PREFIX}/${languagePath}/${relativePath}`;
|
|
447
|
-
await downloadFile(claudeUrl, claudeFile);
|
|
448
|
-
} catch (error) {
|
|
449
|
-
// If CLAUDE.md doesn't exist, that's okay
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
/**
|
|
455
|
-
* Update Claude commands and agents
|
|
456
|
-
* @param {string} languagePath - Language path
|
|
457
|
-
* @param {Object} spinner - Ora spinner instance
|
|
458
|
-
* @param {Object} msg - Localized messages
|
|
459
|
-
* @param {boolean} debugLog - Debug mode flag
|
|
460
|
-
*/
|
|
461
|
-
async function updateCommands(languagePath, spinner, msg, debugLog) {
|
|
462
|
-
// Delete existing commands folder if it exists
|
|
463
|
-
if (await fs.access(TOOL_DIRS.CLAUDE_COMMANDS).then(() => true).catch(() => false)) {
|
|
464
|
-
logWithSpinner(spinner, msg.deletingOldCommands, debugLog);
|
|
465
|
-
await fs.rm(TOOL_DIRS.CLAUDE_COMMANDS, { recursive: true, force: true });
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
// Delete existing agents folder if it exists
|
|
469
|
-
if (await fs.access(TOOL_DIRS.CLAUDE_AGENTS).then(() => true).catch(() => false)) {
|
|
470
|
-
logWithSpinner(spinner, 'Deleting old agents...', debugLog);
|
|
471
|
-
await fs.rm(TOOL_DIRS.CLAUDE_AGENTS, { recursive: true, force: true });
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
await fs.mkdir(TOOL_DIRS.CLAUDE_COMMANDS, { recursive: true });
|
|
475
|
-
await fs.mkdir(TOOL_DIRS.CLAUDE_AGENTS, { recursive: true });
|
|
476
|
-
|
|
477
|
-
// Download commands
|
|
478
|
-
logWithSpinner(spinner, msg.downloadingCommands, debugLog);
|
|
479
|
-
try {
|
|
480
|
-
await downloadDirectory(
|
|
481
|
-
`${GITHUB_CONTENT_PREFIX}/${languagePath}/${TOOL_DIRS.CLAUDE_COMMANDS}`,
|
|
482
|
-
TOOL_DIRS.CLAUDE_COMMANDS,
|
|
483
|
-
spinner,
|
|
484
|
-
msg,
|
|
485
|
-
languagePath,
|
|
486
|
-
true // commands도 동적 디렉터리 탐색 사용 (파일 리스트 의존 제거)
|
|
487
|
-
);
|
|
488
|
-
} catch (error) {
|
|
489
|
-
logWithSpinner(spinner, msg.commandsNotFound, debugLog);
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
// Download agents
|
|
493
|
-
logWithSpinner(spinner, 'Downloading agents...', debugLog);
|
|
494
|
-
try {
|
|
495
|
-
await downloadDirectory(
|
|
496
|
-
`${GITHUB_CONTENT_PREFIX}/${languagePath}/${TOOL_DIRS.CLAUDE_AGENTS}`,
|
|
497
|
-
TOOL_DIRS.CLAUDE_AGENTS,
|
|
498
|
-
spinner,
|
|
499
|
-
msg,
|
|
500
|
-
languagePath,
|
|
501
|
-
true // agents도 동적 디렉터리 탐색 사용 (파일 리스트 의존 제거)
|
|
502
|
-
);
|
|
503
|
-
logWithSpinner(spinner, 'Successfully downloaded agents', debugLog);
|
|
504
|
-
} catch (error) {
|
|
505
|
-
logWithSpinner(spinner, 'Agents not found or failed to download', debugLog);
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
// Download full .claude directory to include tools, workflow, and any other subfolders
|
|
509
|
-
logWithSpinner(spinner, 'Downloading full .claude directory (tools, workflow, etc.)...', debugLog);
|
|
510
|
-
try {
|
|
511
|
-
await downloadDirectory(
|
|
512
|
-
`${GITHUB_CONTENT_PREFIX}/${languagePath}/.claude`,
|
|
513
|
-
'.claude',
|
|
514
|
-
spinner,
|
|
515
|
-
msg,
|
|
516
|
-
languagePath,
|
|
517
|
-
true // 전체 .claude 폴더 동적 다운로드
|
|
518
|
-
);
|
|
519
|
-
logWithSpinner(spinner, 'Successfully downloaded .claude directory', debugLog);
|
|
520
|
-
} catch (error) {
|
|
521
|
-
logWithSpinner(spinner, `Failed to download full .claude directory: ${error.message}`, debugLog);
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
/**
|
|
526
|
-
* Check if tool directories exist and handle them
|
|
527
|
-
* @param {Object} spinner - Ora spinner instance
|
|
528
|
-
* @param {Object} msg - Localized messages
|
|
529
|
-
* @param {boolean} debugLog - Debug mode flag
|
|
530
|
-
* @returns {Promise<Object>} Directory status info
|
|
531
|
-
*/
|
|
532
|
-
async function checkAndHandleExistingToolDirs(spinner, msg, debugLog) {
|
|
533
|
-
const toolDirStatus = {
|
|
534
|
-
cursor: {
|
|
535
|
-
exists: false,
|
|
536
|
-
hadFiles: false,
|
|
537
|
-
backedUp: false
|
|
538
|
-
},
|
|
539
|
-
windsurf: {
|
|
540
|
-
exists: false,
|
|
541
|
-
hadFiles: false,
|
|
542
|
-
backedUp: false
|
|
543
|
-
}
|
|
544
|
-
};
|
|
545
|
-
|
|
546
|
-
// Check Cursor directory
|
|
547
|
-
try {
|
|
548
|
-
await fs.access(TOOL_DIRS.CURSOR_RULES);
|
|
549
|
-
toolDirStatus.cursor.exists = true;
|
|
550
|
-
|
|
551
|
-
const cursorFiles = await fs.readdir(TOOL_DIRS.CURSOR_RULES);
|
|
552
|
-
const mdcFiles = cursorFiles.filter(file => file.endsWith('.mdc'));
|
|
553
|
-
|
|
554
|
-
if (mdcFiles.length > 0) {
|
|
555
|
-
toolDirStatus.cursor.hadFiles = true;
|
|
556
|
-
logWithSpinner(spinner, `${msg.foundExistingCursor} (${mdcFiles.length} files)`, debugLog);
|
|
557
|
-
|
|
558
|
-
// Backup existing files
|
|
559
|
-
logWithSpinner(spinner, msg.backingUpToolFiles, debugLog);
|
|
560
|
-
const backupDir = path.join(TOOL_DIRS.CURSOR_RULES, `backup_${Date.now()}`);
|
|
561
|
-
await fs.mkdir(backupDir, { recursive: true });
|
|
562
|
-
|
|
563
|
-
for (const file of mdcFiles) {
|
|
564
|
-
const srcPath = path.join(TOOL_DIRS.CURSOR_RULES, file);
|
|
565
|
-
const backupPath = path.join(backupDir, file);
|
|
566
|
-
await fs.copyFile(srcPath, backupPath);
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
toolDirStatus.cursor.backedUp = true;
|
|
570
|
-
logWithSpinner(spinner, `${msg.toolBackupCreated}: ${backupDir}`, debugLog);
|
|
571
|
-
|
|
572
|
-
// Remove existing files
|
|
573
|
-
for (const file of mdcFiles) {
|
|
574
|
-
await fs.unlink(path.join(TOOL_DIRS.CURSOR_RULES, file));
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
} catch (error) {
|
|
578
|
-
// Directory doesn't exist, that's fine
|
|
579
|
-
logWithSpinner(spinner, `Cursor rules directory not found, will create new`, debugLog);
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
// Check Windsurf directory
|
|
583
|
-
try {
|
|
584
|
-
await fs.access(TOOL_DIRS.WINDSURF_RULES);
|
|
585
|
-
toolDirStatus.windsurf.exists = true;
|
|
586
|
-
|
|
587
|
-
const windsurfFiles = await fs.readdir(TOOL_DIRS.WINDSURF_RULES);
|
|
588
|
-
const mdFiles = windsurfFiles.filter(file => file.endsWith('.md'));
|
|
589
|
-
|
|
590
|
-
if (mdFiles.length > 0) {
|
|
591
|
-
toolDirStatus.windsurf.hadFiles = true;
|
|
592
|
-
logWithSpinner(spinner, `${msg.foundExistingWindsurf} (${mdFiles.length} files)`, debugLog);
|
|
593
|
-
|
|
594
|
-
// Backup existing files
|
|
595
|
-
logWithSpinner(spinner, msg.backingUpToolFiles, debugLog);
|
|
596
|
-
const backupDir = path.join(TOOL_DIRS.WINDSURF_RULES, `backup_${Date.now()}`);
|
|
597
|
-
await fs.mkdir(backupDir, { recursive: true });
|
|
598
|
-
|
|
599
|
-
for (const file of mdFiles) {
|
|
600
|
-
const srcPath = path.join(TOOL_DIRS.WINDSURF_RULES, file);
|
|
601
|
-
const backupPath = path.join(backupDir, file);
|
|
602
|
-
await fs.copyFile(srcPath, backupPath);
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
toolDirStatus.windsurf.backedUp = true;
|
|
606
|
-
logWithSpinner(spinner, `${msg.toolBackupCreated}: ${backupDir}`, debugLog);
|
|
607
|
-
|
|
608
|
-
// Remove existing files
|
|
609
|
-
for (const file of mdFiles) {
|
|
610
|
-
await fs.unlink(path.join(TOOL_DIRS.WINDSURF_RULES, file));
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
} catch (error) {
|
|
614
|
-
// Directory doesn't exist, that's fine
|
|
615
|
-
logWithSpinner(spinner, `Windsurf rules directory not found, will create new`, debugLog);
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
return toolDirStatus;
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
/**
|
|
622
|
-
* Download and process rules
|
|
623
|
-
* @param {Object} spinner - Ora spinner instance
|
|
624
|
-
* @param {Object} msg - Localized messages
|
|
625
|
-
* @param {boolean} debugLog - Debug mode flag
|
|
626
|
-
*/
|
|
627
|
-
async function downloadAndProcessRules(spinner, msg, debugLog) {
|
|
628
|
-
// Check and handle existing tool directories first
|
|
629
|
-
logWithSpinner(spinner, msg.checkingExistingTools, debugLog);
|
|
630
|
-
const toolDirStatus = await checkAndHandleExistingToolDirs(spinner, msg, debugLog);
|
|
631
|
-
// Try to download from GitHub first, fallback to local files
|
|
632
|
-
const tmpRulesGlobal = path.join(os.tmpdir(), 'aiwf-rules-global');
|
|
633
|
-
const tmpRulesManual = path.join(os.tmpdir(), 'aiwf-rules-manual');
|
|
634
|
-
|
|
635
|
-
let useLocalGlobal = false;
|
|
636
|
-
let useLocalManual = false;
|
|
637
|
-
|
|
638
|
-
// Try to download rules/global from GitHub
|
|
639
|
-
try {
|
|
640
|
-
await downloadDirectory(
|
|
641
|
-
`rules/global`,
|
|
642
|
-
tmpRulesGlobal,
|
|
643
|
-
spinner,
|
|
644
|
-
debugLog
|
|
645
|
-
);
|
|
646
|
-
logWithSpinner(spinner, `Downloaded rules/global from GitHub`, debugLog);
|
|
647
|
-
} catch (error) {
|
|
648
|
-
logWithSpinner(spinner, `Failed to download rules/global from GitHub, trying local files`, debugLog);
|
|
649
|
-
// Fallback to local files
|
|
650
|
-
const localRulesGlobal = path.join(process.cwd(), 'rules/global');
|
|
651
|
-
try {
|
|
652
|
-
logWithSpinner(spinner, `Trying to access: ${localRulesGlobal}`, debugLog);
|
|
653
|
-
await fs.access(localRulesGlobal);
|
|
654
|
-
logWithSpinner(spinner, `Successfully accessed local rules/global`, debugLog);
|
|
655
|
-
// Copy local files to temp directory
|
|
656
|
-
await fs.mkdir(tmpRulesGlobal, { recursive: true });
|
|
657
|
-
const files = await fs.readdir(localRulesGlobal);
|
|
658
|
-
logWithSpinner(spinner, `Found ${files.length} files in rules/global: ${files.join(', ')}`, debugLog);
|
|
659
|
-
for (const file of files) {
|
|
660
|
-
const srcPath = path.join(localRulesGlobal, file);
|
|
661
|
-
const destPath = path.join(tmpRulesGlobal, file);
|
|
662
|
-
await fs.copyFile(srcPath, destPath);
|
|
663
|
-
logWithSpinner(spinner, `Copied: ${file}`, debugLog);
|
|
664
|
-
}
|
|
665
|
-
useLocalGlobal = true;
|
|
666
|
-
logWithSpinner(spinner, `Using local rules/global directory`, debugLog);
|
|
667
|
-
} catch (localError) {
|
|
668
|
-
logWithSpinner(spinner, `Local rules/global error: ${localError.message}`, debugLog);
|
|
669
|
-
logWithSpinner(spinner, msg.rulesGlobalNotFound, debugLog);
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
// Try to download rules/manual from GitHub
|
|
674
|
-
try {
|
|
675
|
-
await downloadDirectory(
|
|
676
|
-
`rules/manual`,
|
|
677
|
-
tmpRulesManual,
|
|
678
|
-
spinner,
|
|
679
|
-
debugLog
|
|
680
|
-
);
|
|
681
|
-
logWithSpinner(spinner, `Downloaded rules/manual from GitHub`, debugLog);
|
|
682
|
-
} catch (error) {
|
|
683
|
-
logWithSpinner(spinner, `Failed to download rules/manual from GitHub, trying local files`, debugLog);
|
|
684
|
-
// Fallback to local files
|
|
685
|
-
const localRulesManual = path.join(process.cwd(), 'rules/manual');
|
|
686
|
-
try {
|
|
687
|
-
await fs.access(localRulesManual);
|
|
688
|
-
// Copy local files to temp directory
|
|
689
|
-
await fs.mkdir(tmpRulesManual, { recursive: true });
|
|
690
|
-
const files = await fs.readdir(localRulesManual);
|
|
691
|
-
for (const file of files) {
|
|
692
|
-
const srcPath = path.join(localRulesManual, file);
|
|
693
|
-
const destPath = path.join(tmpRulesManual, file);
|
|
694
|
-
await fs.copyFile(srcPath, destPath);
|
|
695
|
-
}
|
|
696
|
-
useLocalManual = true;
|
|
697
|
-
logWithSpinner(spinner, `Using local rules/manual directory`, debugLog);
|
|
698
|
-
} catch (localError) {
|
|
699
|
-
logWithSpinner(spinner, msg.rulesManualNotFound, debugLog);
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
// Process rules/global
|
|
704
|
-
try {
|
|
705
|
-
await fs.access(tmpRulesGlobal);
|
|
706
|
-
await fs.mkdir(TOOL_DIRS.CURSOR_RULES, { recursive: true });
|
|
707
|
-
await fs.mkdir(TOOL_DIRS.WINDSURF_RULES, { recursive: true });
|
|
708
|
-
const files = await fs.readdir(tmpRulesGlobal);
|
|
709
|
-
for (const file of files) {
|
|
710
|
-
const srcPath = path.join(tmpRulesGlobal, file);
|
|
711
|
-
const stat = await fs.stat(srcPath);
|
|
712
|
-
if (!stat.isFile()) continue;
|
|
713
|
-
|
|
714
|
-
// .cursor/rules: add .mdc extension and header
|
|
715
|
-
const base = path.parse(file).name;
|
|
716
|
-
const cursorTarget = path.join(TOOL_DIRS.CURSOR_RULES, base + '.mdc');
|
|
717
|
-
const content = await fs.readFile(srcPath, 'utf8');
|
|
718
|
-
const header = '---\ndescription: \nglobs: \nalwaysApply: true\n---\n';
|
|
719
|
-
await fs.writeFile(cursorTarget, header + content, 'utf8');
|
|
720
|
-
logWithSpinner(
|
|
721
|
-
spinner,
|
|
722
|
-
`[rules/global] ${file} -> ${TOOL_DIRS.CURSOR_RULES}/${base}.mdc`,
|
|
723
|
-
debugLog
|
|
724
|
-
);
|
|
725
|
-
|
|
726
|
-
// .windsurf/rules: keep extension, no header
|
|
727
|
-
const windsurfTarget = path.join(TOOL_DIRS.WINDSURF_RULES, file);
|
|
728
|
-
const windsurfContent = await fs.readFile(srcPath, 'utf8');
|
|
729
|
-
const windsurfHeader = '---\ntrigger: always_on\n---\n';
|
|
730
|
-
await fs.writeFile(windsurfTarget, windsurfHeader + windsurfContent, 'utf8');
|
|
731
|
-
logWithSpinner(
|
|
732
|
-
spinner,
|
|
733
|
-
`[rules/global] ${file} -> ${TOOL_DIRS.WINDSURF_RULES}/${file}`,
|
|
734
|
-
debugLog
|
|
735
|
-
);
|
|
736
|
-
}
|
|
737
|
-
// Clean up temp folder (only if not using local files)
|
|
738
|
-
if (!useLocalGlobal) {
|
|
739
|
-
await fs.rm(tmpRulesGlobal, { recursive: true, force: true });
|
|
740
|
-
}
|
|
741
|
-
} catch (error) {
|
|
742
|
-
logWithSpinner(spinner, `No rules/global directory found`, debugLog);
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
// Process rules/manual
|
|
746
|
-
try {
|
|
747
|
-
await fs.access(tmpRulesManual);
|
|
748
|
-
await fs.mkdir(TOOL_DIRS.CURSOR_RULES, { recursive: true });
|
|
749
|
-
await fs.mkdir(TOOL_DIRS.WINDSURF_RULES, { recursive: true });
|
|
750
|
-
const files = await fs.readdir(tmpRulesManual);
|
|
751
|
-
for (const file of files) {
|
|
752
|
-
const srcPath = path.join(tmpRulesManual, file);
|
|
753
|
-
const stat = await fs.stat(srcPath);
|
|
754
|
-
if (!stat.isFile()) continue;
|
|
755
|
-
|
|
756
|
-
// .cursor/rules: add .mdc extension and header (alwaysApply: false)
|
|
757
|
-
const base = path.parse(file).name;
|
|
758
|
-
const cursorTarget = path.join(TOOL_DIRS.CURSOR_RULES, base + '.mdc');
|
|
759
|
-
const content = await fs.readFile(srcPath, 'utf8');
|
|
760
|
-
const header = '---\ndescription: \nglobs: \nalwaysApply: false\n---\n';
|
|
761
|
-
await fs.writeFile(cursorTarget, header + content, 'utf8');
|
|
762
|
-
logWithSpinner(
|
|
763
|
-
spinner,
|
|
764
|
-
`[rules/manual] ${file} -> ${TOOL_DIRS.CURSOR_RULES}/${base}.mdc`,
|
|
765
|
-
debugLog
|
|
766
|
-
);
|
|
767
|
-
|
|
768
|
-
// .windsurf/rules: keep extension, no header
|
|
769
|
-
const windsurfTarget = path.join(TOOL_DIRS.WINDSURF_RULES, file);
|
|
770
|
-
const windsurfContent = await fs.readFile(srcPath, 'utf8');
|
|
771
|
-
const windsurfHeader = '---\ntrigger: manual\n---\n';
|
|
772
|
-
await fs.writeFile(windsurfTarget, windsurfHeader + windsurfContent, 'utf8');
|
|
773
|
-
logWithSpinner(
|
|
774
|
-
spinner,
|
|
775
|
-
`[rules/manual] ${file} -> ${TOOL_DIRS.WINDSURF_RULES}/${file}`,
|
|
776
|
-
debugLog
|
|
777
|
-
);
|
|
778
|
-
}
|
|
779
|
-
// Clean up temp folder (only if not using local files)
|
|
780
|
-
if (!useLocalManual) {
|
|
781
|
-
await fs.rm(tmpRulesManual, { recursive: true, force: true });
|
|
782
|
-
}
|
|
783
|
-
} catch (error) {
|
|
784
|
-
logWithSpinner(spinner, `No rules/manual directory found`, debugLog);
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
// Success message for tool rules installation
|
|
788
|
-
logWithSpinner(spinner, msg.toolRulesInstalled, debugLog);
|
|
789
|
-
|
|
790
|
-
// Display installation results for tool directories
|
|
791
|
-
if (toolDirStatus.cursor.backedUp || toolDirStatus.windsurf.backedUp) {
|
|
792
|
-
console.log(chalk.cyan(`\n📂 Tool Directory Installation Results:`));
|
|
793
|
-
|
|
794
|
-
if (toolDirStatus.cursor.backedUp) {
|
|
795
|
-
console.log(chalk.yellow(` • Cursor: Existing rules backed up and replaced`));
|
|
796
|
-
} else if (toolDirStatus.cursor.exists) {
|
|
797
|
-
console.log(chalk.green(` • Cursor: Rules installed (directory was empty)`));
|
|
798
|
-
} else {
|
|
799
|
-
console.log(chalk.green(` • Cursor: Rules installed (new directory)`));
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
if (toolDirStatus.windsurf.backedUp) {
|
|
803
|
-
console.log(chalk.yellow(` • Windsurf: Existing rules backed up and replaced`));
|
|
804
|
-
} else if (toolDirStatus.windsurf.exists) {
|
|
805
|
-
console.log(chalk.green(` • Windsurf: Rules installed (directory was empty)`));
|
|
806
|
-
} else {
|
|
807
|
-
console.log(chalk.green(` • Windsurf: Rules installed (new directory)`));
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
return toolDirStatus;
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
/**
|
|
815
|
-
* Display installation summary
|
|
816
|
-
* @param {boolean} hasExisting - Has existing installation
|
|
817
|
-
* @param {Object} msg - Localized messages
|
|
818
|
-
*/
|
|
819
|
-
function displaySummary(hasExisting, msg) {
|
|
820
|
-
if (hasExisting) {
|
|
821
|
-
console.log(chalk.blue(`\n${msg.updateHistory}`));
|
|
822
|
-
console.log(chalk.gray(` • ${TOOL_DIRS.CLAUDE_COMMANDS}${msg.updatedCommands}`));
|
|
823
|
-
console.log(chalk.gray(` • ${msg.updatedDocs}`));
|
|
824
|
-
console.log(chalk.green(`\n${msg.workPreserved}`));
|
|
825
|
-
console.log(chalk.gray(` • ${msg.allFilesPreserved}`));
|
|
826
|
-
console.log(chalk.gray(` • ${msg.backupCreated}`));
|
|
827
|
-
} else {
|
|
828
|
-
console.log(chalk.blue(`\n${msg.structureCreated}`));
|
|
829
|
-
console.log(chalk.gray(` ${AIWF_DIRS.ROOT}/ - ${msg.aiwfRoot}`));
|
|
830
|
-
console.log(chalk.gray(` ${TOOL_DIRS.CLAUDE_COMMANDS}/ - ${msg.claudeCommands}`));
|
|
831
|
-
|
|
832
|
-
console.log(chalk.green(`\n${msg.nextSteps}`));
|
|
833
|
-
console.log(chalk.white(` 1. ${msg.nextStep1}`));
|
|
834
|
-
console.log(chalk.white(` 2. ${msg.nextStep2}`));
|
|
835
|
-
console.log(chalk.white(` 3. ${msg.nextStep3}\n`));
|
|
836
|
-
|
|
837
|
-
console.log(chalk.blue(`\n${msg.gettingStarted}`));
|
|
838
|
-
console.log(chalk.gray(` 1. ${msg.startStep1}`));
|
|
839
|
-
console.log(chalk.gray(` 2. ${msg.startStep2}`));
|
|
840
|
-
console.log(chalk.gray(`\n${msg.checkDocs}`));
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
/**
|
|
845
|
-
* Offer reinstallation options
|
|
846
|
-
* @param {Object} validationResults - Validation results
|
|
847
|
-
* @param {string} language - Language code
|
|
848
|
-
* @returns {Promise<string>} Selected action
|
|
849
|
-
*/
|
|
850
|
-
async function offerReinstallationOptions(validationResults, language) {
|
|
851
|
-
console.log(
|
|
852
|
-
chalk.yellow(
|
|
853
|
-
language === 'ko'
|
|
854
|
-
? '\n⚠️ 일부 설치가 실패했습니다. 무엇을 하시겠습니까?'
|
|
855
|
-
: '\n⚠️ Some installations failed. What would you like to do?'
|
|
856
|
-
)
|
|
857
|
-
);
|
|
858
|
-
|
|
859
|
-
// List failed tools
|
|
860
|
-
const failedTools = validationResults.failed.map(({ tool }) => tool);
|
|
861
|
-
|
|
862
|
-
if (failedTools.length > 0) {
|
|
863
|
-
console.log(
|
|
864
|
-
chalk.red(
|
|
865
|
-
language === 'ko'
|
|
866
|
-
? `실패한 도구: ${failedTools.join(', ')}`
|
|
867
|
-
: `Failed tools: ${failedTools.join(', ')}`
|
|
868
|
-
)
|
|
869
|
-
);
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
const response = await prompts({
|
|
873
|
-
type: 'select',
|
|
874
|
-
name: 'action',
|
|
875
|
-
message:
|
|
876
|
-
language === 'ko' ? '어떤 작업을 수행하시겠습니까?' : 'What would you like to do?',
|
|
877
|
-
choices: [
|
|
878
|
-
{
|
|
879
|
-
title: language === 'ko' ? '전체 재설치' : 'Full reinstallation',
|
|
880
|
-
value: 'reinstall'
|
|
881
|
-
},
|
|
882
|
-
{
|
|
883
|
-
title: language === 'ko' ? '실패한 도구만 롤백' : 'Rollback failed tools only',
|
|
884
|
-
value: 'rollback'
|
|
885
|
-
},
|
|
886
|
-
{
|
|
887
|
-
title: language === 'ko' ? '현재 상태 유지' : 'Keep current state',
|
|
888
|
-
value: 'keep'
|
|
889
|
-
}
|
|
890
|
-
]
|
|
891
|
-
});
|
|
892
|
-
|
|
893
|
-
return response.action;
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
/**
|
|
897
|
-
* 사용자 설치 옵션 선택
|
|
898
|
-
* @param {Object} msg - 지역화된 메시지
|
|
899
|
-
* @returns {Promise<Array>} 선택된 설치 옵션 목록
|
|
900
|
-
*/
|
|
901
|
-
async function selectInstallationOptions(msg) {
|
|
902
|
-
console.log(chalk.cyan(`\n${msg.installationOptions}`));
|
|
903
|
-
|
|
904
|
-
const installOptions = [
|
|
905
|
-
{
|
|
906
|
-
title: msg.aiwfDocs,
|
|
907
|
-
value: 'aiwf-docs',
|
|
908
|
-
description: 'AIWF 문서 폴더'
|
|
909
|
-
},
|
|
910
|
-
{
|
|
911
|
-
title: msg.claudeCodeCommands,
|
|
912
|
-
value: 'claude-code-commands',
|
|
913
|
-
description: 'Claude Code 명령어 및 에이전트'
|
|
914
|
-
},
|
|
915
|
-
{
|
|
916
|
-
title: msg.windsurfRules,
|
|
917
|
-
value: 'windsurf-rules',
|
|
918
|
-
description: 'Windsurf 규칙 파일'
|
|
919
|
-
},
|
|
920
|
-
{
|
|
921
|
-
title: msg.cursorRules,
|
|
922
|
-
value: 'cursor-rules',
|
|
923
|
-
description: 'Cursor 규칙 파일'
|
|
924
|
-
}
|
|
925
|
-
];
|
|
926
|
-
|
|
927
|
-
// 1단계: 전체 설치 vs 개별 선택
|
|
928
|
-
const installTypeResponse = await prompts({
|
|
929
|
-
type: 'select',
|
|
930
|
-
name: 'installType',
|
|
931
|
-
message: msg.installationOptions || '설치 옵션을 선택하세요:',
|
|
932
|
-
choices: [
|
|
933
|
-
{
|
|
934
|
-
title: msg.selectAll || '✅ 모든 항목 설치 (권장)',
|
|
935
|
-
value: 'all',
|
|
936
|
-
description: '모든 AIWF 컴포넌트를 설치합니다'
|
|
937
|
-
},
|
|
938
|
-
{
|
|
939
|
-
title: '🎯 개별 항목 선택',
|
|
940
|
-
value: 'custom',
|
|
941
|
-
description: '원하는 항목만 선택하여 설치합니다'
|
|
942
|
-
}
|
|
943
|
-
]
|
|
944
|
-
});
|
|
945
|
-
|
|
946
|
-
// 취소된 경우
|
|
947
|
-
if (!installTypeResponse.installType) {
|
|
948
|
-
console.log(chalk.yellow(msg.noOptionsSelected || '설치가 취소되었습니다.'));
|
|
949
|
-
process.exit(0);
|
|
950
|
-
}
|
|
951
|
-
|
|
952
|
-
// 전체 설치 선택
|
|
953
|
-
if (installTypeResponse.installType === 'all') {
|
|
954
|
-
return installOptions.map(opt => opt.value);
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
// 2단계: 개별 항목 선택
|
|
958
|
-
const customResponse = await prompts({
|
|
959
|
-
type: 'multiselect',
|
|
960
|
-
name: 'selectedOptions',
|
|
961
|
-
message: '설치할 항목을 선택하세요 (스페이스로 선택/해제, 엔터로 확인):',
|
|
962
|
-
choices: installOptions,
|
|
963
|
-
hint: '↑↓ 이동, 스페이스 선택/해제, 엔터 확인',
|
|
964
|
-
instructions: false
|
|
965
|
-
});
|
|
966
|
-
|
|
967
|
-
// 취소되거나 아무것도 선택하지 않은 경우
|
|
968
|
-
if (!customResponse.selectedOptions || customResponse.selectedOptions.length === 0) {
|
|
969
|
-
console.log(chalk.yellow(msg.noOptionsSelected || '선택된 항목이 없습니다.'));
|
|
970
|
-
process.exit(0);
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
return customResponse.selectedOptions;
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
/**
|
|
977
|
-
* 선택된 옵션별 설치 실행
|
|
978
|
-
* @param {Array} selectedOptions - 선택된 설치 옵션들
|
|
979
|
-
* @param {string} languagePath - 언어 경로
|
|
980
|
-
* @param {Object} msg - 지역화된 메시지
|
|
981
|
-
* @param {boolean} debugLog - 디버그 로그 여부
|
|
982
|
-
*/
|
|
983
|
-
async function installSelectedOptions(selectedOptions, languagePath, msg, debugLog) {
|
|
984
|
-
for (const option of selectedOptions) {
|
|
985
|
-
const spinner = ora(`${msg.installingOption}${option}...`).start();
|
|
986
|
-
|
|
987
|
-
try {
|
|
988
|
-
switch (option) {
|
|
989
|
-
case 'aiwf-docs':
|
|
990
|
-
await installAIWFDocs(languagePath, debugLog);
|
|
991
|
-
break;
|
|
992
|
-
case 'claude-code-commands':
|
|
993
|
-
await installClaudeCodeCommands(languagePath, debugLog);
|
|
994
|
-
break;
|
|
995
|
-
case 'windsurf-rules':
|
|
996
|
-
await installWindsurfRules(debugLog);
|
|
997
|
-
break;
|
|
998
|
-
case 'cursor-rules':
|
|
999
|
-
await installCursorRules(debugLog);
|
|
1000
|
-
break;
|
|
1001
|
-
default:
|
|
1002
|
-
spinner.warn(`Unknown option: ${option}`);
|
|
1003
|
-
continue;
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
spinner.succeed(`${msg.optionInstalled}${option}`);
|
|
1007
|
-
} catch (error) {
|
|
1008
|
-
spinner.fail(`Failed to install ${option}: ${error.message}`);
|
|
1009
|
-
if (debugLog) {
|
|
1010
|
-
console.error(error);
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
/**
|
|
1017
|
-
* AIWF 문서 설치
|
|
1018
|
-
*/
|
|
1019
|
-
async function installAIWFDocs(languagePath, debugLog) {
|
|
1020
|
-
await createDirectoryStructure();
|
|
1021
|
-
const spinner = ora('Downloading AIWF files...').start();
|
|
1022
|
-
const msg = {
|
|
1023
|
-
downloading: 'Downloading AIWF documentation...',
|
|
1024
|
-
downloadingTemplates: 'Downloading templates...',
|
|
1025
|
-
templatesNotFound: 'Templates not found'
|
|
1026
|
-
};
|
|
1027
|
-
|
|
1028
|
-
try {
|
|
1029
|
-
// 매니페스트 다운로드
|
|
1030
|
-
await downloadManifest(languagePath, spinner, msg, debugLog);
|
|
1031
|
-
|
|
1032
|
-
// 전체 .aiwf 디렉토리 다운로드
|
|
1033
|
-
await downloadTemplates(languagePath, spinner, msg, debugLog);
|
|
1034
|
-
|
|
1035
|
-
spinner.succeed('AIWF documentation installed successfully');
|
|
1036
|
-
} catch (error) {
|
|
1037
|
-
spinner.fail('Failed to install AIWF documentation');
|
|
1038
|
-
if (debugLog) {
|
|
1039
|
-
console.error(error);
|
|
1040
|
-
}
|
|
1041
|
-
throw error;
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
/**
|
|
1046
|
-
* Claude Code 명령어 설치
|
|
1047
|
-
*/
|
|
1048
|
-
async function installClaudeCodeCommands(languagePath, debugLog) {
|
|
1049
|
-
const GITHUB_CONTENT_LANGUAGE_PREFIX = `${GITHUB_CONTENT_PREFIX}/${languagePath}`;
|
|
1050
|
-
const dummySpinner = null;
|
|
1051
|
-
const dummyMsg = { downloadingCommands: 'Downloading Claude Code commands...' };
|
|
1052
|
-
await updateCommands(languagePath, dummySpinner, dummyMsg, debugLog);
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
|
-
/**
|
|
1056
|
-
* Windsurf 규칙 설치
|
|
1057
|
-
*/
|
|
1058
|
-
async function installWindsurfRules(debugLog) {
|
|
1059
|
-
const spinner = ora('Installing Windsurf rules...').start();
|
|
1060
|
-
const msg = {
|
|
1061
|
-
checkingExistingTools: 'Checking existing tools...',
|
|
1062
|
-
foundExistingCursor: 'Found existing Cursor rules',
|
|
1063
|
-
foundExistingWindsurf: 'Found existing Windsurf rules',
|
|
1064
|
-
backingUpToolFiles: 'Backing up tool files...',
|
|
1065
|
-
toolBackupCreated: 'Tool backup created',
|
|
1066
|
-
rulesGlobalNotFound: 'Rules global not found',
|
|
1067
|
-
rulesManualNotFound: 'Rules manual not found',
|
|
1068
|
-
toolRulesInstalled: 'Tool rules installed successfully'
|
|
1069
|
-
};
|
|
1070
|
-
|
|
1071
|
-
try {
|
|
1072
|
-
await downloadAndProcessRules(spinner, msg, debugLog);
|
|
1073
|
-
spinner.succeed('Windsurf rules installed successfully');
|
|
1074
|
-
} catch (error) {
|
|
1075
|
-
spinner.fail('Failed to install Windsurf rules');
|
|
1076
|
-
if (debugLog) {
|
|
1077
|
-
console.error(error);
|
|
1078
|
-
}
|
|
1079
|
-
throw error;
|
|
1080
|
-
}
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
/**
|
|
1084
|
-
* Cursor 규칙 설치
|
|
1085
|
-
*/
|
|
1086
|
-
async function installCursorRules(debugLog) {
|
|
1087
|
-
const spinner = ora('Installing Cursor rules...').start();
|
|
1088
|
-
const msg = {
|
|
1089
|
-
checkingExistingTools: 'Checking existing tools...',
|
|
1090
|
-
foundExistingCursor: 'Found existing Cursor rules',
|
|
1091
|
-
foundExistingWindsurf: 'Found existing Windsurf rules',
|
|
1092
|
-
backingUpToolFiles: 'Backing up tool files...',
|
|
1093
|
-
toolBackupCreated: 'Tool backup created',
|
|
1094
|
-
rulesGlobalNotFound: 'Rules global not found',
|
|
1095
|
-
rulesManualNotFound: 'Rules manual not found',
|
|
1096
|
-
toolRulesInstalled: 'Tool rules installed successfully'
|
|
1097
|
-
};
|
|
1098
|
-
|
|
1099
|
-
try {
|
|
1100
|
-
await downloadAndProcessRules(spinner, msg, debugLog);
|
|
1101
|
-
spinner.succeed('Cursor rules installed successfully');
|
|
1102
|
-
} catch (error) {
|
|
1103
|
-
spinner.fail('Failed to install Cursor rules');
|
|
1104
|
-
if (debugLog) {
|
|
1105
|
-
console.error(error);
|
|
1106
|
-
}
|
|
1107
|
-
throw error;
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
|
-
|
|
1111
|
-
/**
|
|
1112
|
-
* Main installation function
|
|
1113
|
-
* @param {Object} options - Installation options
|
|
1114
|
-
*/
|
|
1115
|
-
export async function installAIWF(options = {}) {
|
|
1116
|
-
const debugLog = options.debugLog || false;
|
|
1117
|
-
|
|
1118
|
-
// Select language
|
|
1119
|
-
const selectedLanguage = await selectLanguage(options);
|
|
1120
|
-
const msg = getMessages(selectedLanguage);
|
|
1121
|
-
|
|
1122
|
-
console.log(chalk.blue.bold(msg.welcome));
|
|
1123
|
-
console.log(chalk.gray(msg.description));
|
|
1124
|
-
console.log(chalk.gray(msg.optimized));
|
|
1125
|
-
|
|
1126
|
-
// Set language path
|
|
1127
|
-
const languagePath = getInstallationLanguagePath(selectedLanguage);
|
|
1128
|
-
|
|
1129
|
-
// Check existing installation
|
|
1130
|
-
const hasExisting = await checkExistingInstallation();
|
|
1131
|
-
|
|
1132
|
-
let installDecision = { continue: true, installType: 'fresh', preserveProject: false };
|
|
1133
|
-
let projectBackupDir = null;
|
|
1134
|
-
|
|
1135
|
-
if (hasExisting) {
|
|
1136
|
-
installDecision = await handleExistingInstallation(msg, options);
|
|
1137
|
-
if (!installDecision.continue) return;
|
|
1138
|
-
|
|
1139
|
-
// If reinstalling and preserving project, backup project files
|
|
1140
|
-
if (installDecision.installType === 'reinstall' && installDecision.preserveProject) {
|
|
1141
|
-
const { hasProject, projectFiles } = await checkExistingProject();
|
|
1142
|
-
if (hasProject) {
|
|
1143
|
-
projectBackupDir = await backupProjectFiles(projectFiles, msg);
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
|
-
// 설치 옵션 선택
|
|
1149
|
-
let selectedOptions = ['aiwf-docs', 'claude-code-commands', 'windsurf-rules', 'cursor-rules'];
|
|
1150
|
-
|
|
1151
|
-
if (options.preselectedOptions) {
|
|
1152
|
-
// CLI에서 미리 선택된 옵션들을 사용
|
|
1153
|
-
selectedOptions = options.preselectedOptions;
|
|
1154
|
-
} else if (!options.force && !options.interactive) {
|
|
1155
|
-
// 기본적으로는 대화형 선택을 사용
|
|
1156
|
-
selectedOptions = await selectInstallationOptions(msg);
|
|
1157
|
-
} else if (options.interactive) {
|
|
1158
|
-
// --interactive 플래그가 명시적으로 설정된 경우
|
|
1159
|
-
selectedOptions = await selectInstallationOptions(msg);
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
console.log(chalk.blue(`\n설치할 옵션: ${selectedOptions.join(', ')}`));
|
|
1163
|
-
|
|
1164
|
-
try {
|
|
1165
|
-
// 선택된 옵션들을 설치
|
|
1166
|
-
await installSelectedOptions(selectedOptions, languagePath, msg, debugLog);
|
|
1167
|
-
|
|
1168
|
-
// Restore project files if needed
|
|
1169
|
-
if (projectBackupDir) {
|
|
1170
|
-
await restoreProjectFiles(projectBackupDir, msg);
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
|
-
// Success message
|
|
1174
|
-
if (hasExisting && installDecision.installType === 'update') {
|
|
1175
|
-
console.log(chalk.green(`\n✅ ${msg.updateSuccess}`));
|
|
1176
|
-
} else {
|
|
1177
|
-
console.log(chalk.green(`\n✅ ${msg.installSuccess}`));
|
|
1178
|
-
}
|
|
1179
|
-
|
|
1180
|
-
// Display summary
|
|
1181
|
-
displaySummary(hasExisting, msg);
|
|
1182
|
-
|
|
1183
|
-
// Run validation based on selected options
|
|
1184
|
-
console.log(
|
|
1185
|
-
chalk.blue('\n🔍 Installation Validation / 설치 검증을 시작합니다...')
|
|
1186
|
-
);
|
|
1187
|
-
|
|
1188
|
-
// Map selected options to validation tools
|
|
1189
|
-
const validationTools = [];
|
|
1190
|
-
if (selectedOptions.includes('claude-code-commands')) {
|
|
1191
|
-
validationTools.push('claude-code');
|
|
1192
|
-
}
|
|
1193
|
-
if (selectedOptions.includes('cursor-rules')) {
|
|
1194
|
-
validationTools.push('cursor');
|
|
1195
|
-
}
|
|
1196
|
-
if (selectedOptions.includes('windsurf-rules')) {
|
|
1197
|
-
validationTools.push('windsurf');
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
|
-
if (validationTools.length > 0) {
|
|
1201
|
-
const validationResults = await validateInstallation(
|
|
1202
|
-
validationTools,
|
|
1203
|
-
selectedLanguage
|
|
1204
|
-
);
|
|
1205
|
-
displaySpecCompliantValidationResults(validationResults, selectedLanguage);
|
|
1206
|
-
|
|
1207
|
-
// Handle partial failures
|
|
1208
|
-
if (validationResults.failed.length > 0) {
|
|
1209
|
-
const action = await offerReinstallationOptions(
|
|
1210
|
-
validationResults,
|
|
1211
|
-
selectedLanguage
|
|
1212
|
-
);
|
|
1213
|
-
|
|
1214
|
-
if (action === 'reinstall') {
|
|
1215
|
-
console.log(
|
|
1216
|
-
chalk.blue('\n🔄 재설치를 시작합니다... / Starting reinstallation...')
|
|
1217
|
-
);
|
|
1218
|
-
// Retry installation once
|
|
1219
|
-
if (!options.isRetry) {
|
|
1220
|
-
return await installAIWF({ ...options, isRetry: true });
|
|
1221
|
-
}
|
|
1222
|
-
} else if (action === 'rollback') {
|
|
1223
|
-
console.log(
|
|
1224
|
-
chalk.blue('\n↩️ 실패한 도구를 롤백합니다... / Rolling back failed tools...')
|
|
1225
|
-
);
|
|
1226
|
-
|
|
1227
|
-
// Rollback failed tools
|
|
1228
|
-
const failedTools = validationResults.failed.map(({ tool }) => tool);
|
|
1229
|
-
const backupDir = getCurrentBackupDir();
|
|
1230
|
-
|
|
1231
|
-
for (const tool of failedTools) {
|
|
1232
|
-
if (backupDir) {
|
|
1233
|
-
await rollbackTool(
|
|
1234
|
-
tool,
|
|
1235
|
-
backupDir,
|
|
1236
|
-
selectedLanguage
|
|
1237
|
-
);
|
|
1238
|
-
}
|
|
1239
|
-
}
|
|
1240
|
-
|
|
1241
|
-
// Re-validate after rollback
|
|
1242
|
-
console.log(
|
|
1243
|
-
chalk.blue('\n🔍 롤백 후 재검증... / Re-validating after rollback...')
|
|
1244
|
-
);
|
|
1245
|
-
const postRollbackValidation = await validateInstallation(
|
|
1246
|
-
validationTools,
|
|
1247
|
-
selectedLanguage
|
|
1248
|
-
);
|
|
1249
|
-
displaySpecCompliantValidationResults(postRollbackValidation, selectedLanguage);
|
|
1250
|
-
}
|
|
1251
|
-
}
|
|
1252
|
-
} else {
|
|
1253
|
-
console.log(chalk.yellow('\n⚠️ 검증할 도구가 선택되지 않았습니다.'));
|
|
1254
|
-
}
|
|
1255
|
-
|
|
1256
|
-
console.log(chalk.green(msg.enjoy));
|
|
1257
|
-
} catch (error) {
|
|
1258
|
-
if (hasExisting) {
|
|
1259
|
-
spinner.fail(chalk.red(msg.updateFailed));
|
|
1260
|
-
await restoreFromBackup(spinner, msg);
|
|
1261
|
-
} else {
|
|
1262
|
-
spinner.fail(chalk.red(msg.installFailed));
|
|
1263
|
-
}
|
|
1264
|
-
console.error(chalk.red(error.message));
|
|
1265
|
-
process.exit(1);
|
|
1266
|
-
}
|
|
1267
|
-
|
|
1268
|
-
// Create installation flag file
|
|
1269
|
-
await fs.writeFile(FILES.INSTALLED_FLAG, 'installed', 'utf8');
|
|
1270
|
-
}
|