@planu/cli 0.88.1 → 0.90.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/activate.d.ts +14 -0
- package/dist/cli/commands/activate.d.ts.map +1 -0
- package/dist/cli/commands/activate.js +174 -0
- package/dist/cli/commands/activate.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +16 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +162 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/install.d.ts +48 -0
- package/dist/cli/commands/install.d.ts.map +1 -0
- package/dist/cli/commands/install.js +348 -0
- package/dist/cli/commands/install.js.map +1 -0
- package/dist/cli/commands/uninstall.d.ts +10 -0
- package/dist/cli/commands/uninstall.d.ts.map +1 -0
- package/dist/cli/commands/uninstall.js +133 -0
- package/dist/cli/commands/uninstall.js.map +1 -0
- package/dist/cli/router.d.ts.map +1 -1
- package/dist/cli/router.js +9 -1
- package/dist/cli/router.js.map +1 -1
- package/dist/config/license-plans.json +5 -2
- package/dist/engine/agent-generator.test.d.ts +2 -0
- package/dist/engine/agent-generator.test.d.ts.map +1 -0
- package/dist/engine/agent-generator.test.js +556 -0
- package/dist/engine/agent-generator.test.js.map +1 -0
- package/dist/engine/analyzer.test.d.ts +2 -0
- package/dist/engine/analyzer.test.d.ts.map +1 -0
- package/dist/engine/analyzer.test.js +1461 -0
- package/dist/engine/analyzer.test.js.map +1 -0
- package/dist/engine/auditor.test.d.ts +2 -0
- package/dist/engine/auditor.test.d.ts.map +1 -0
- package/dist/engine/auditor.test.js +2075 -0
- package/dist/engine/auditor.test.js.map +1 -0
- package/dist/engine/convention-scanner/codebase-scanner.js +2 -2
- package/dist/engine/convention-scanner/codebase-scanner.js.map +1 -1
- package/dist/engine/conventions-cache.d.ts +6 -0
- package/dist/engine/conventions-cache.d.ts.map +1 -0
- package/dist/engine/conventions-cache.js +20 -0
- package/dist/engine/conventions-cache.js.map +1 -0
- package/dist/engine/doc-generator.test.d.ts +2 -0
- package/dist/engine/doc-generator.test.d.ts.map +1 -0
- package/dist/engine/doc-generator.test.js +961 -0
- package/dist/engine/doc-generator.test.js.map +1 -0
- package/dist/engine/estimator.test.d.ts +2 -0
- package/dist/engine/estimator.test.d.ts.map +1 -0
- package/dist/engine/estimator.test.js +334 -0
- package/dist/engine/estimator.test.js.map +1 -0
- package/dist/engine/skill-generator.test.d.ts +2 -0
- package/dist/engine/skill-generator.test.d.ts.map +1 -0
- package/dist/engine/skill-generator.test.js +742 -0
- package/dist/engine/skill-generator.test.js.map +1 -0
- package/dist/engine/spec-migrator/filesystem-import.d.ts +14 -0
- package/dist/engine/spec-migrator/filesystem-import.d.ts.map +1 -0
- package/dist/engine/spec-migrator/filesystem-import.js +96 -0
- package/dist/engine/spec-migrator/filesystem-import.js.map +1 -0
- package/dist/engine/spec-migrator/flatten-specs.d.ts +12 -0
- package/dist/engine/spec-migrator/flatten-specs.d.ts.map +1 -0
- package/dist/engine/spec-migrator/flatten-specs.js +111 -0
- package/dist/engine/spec-migrator/flatten-specs.js.map +1 -0
- package/dist/engine/spec-migrator/folder-operations.d.ts +9 -0
- package/dist/engine/spec-migrator/folder-operations.d.ts.map +1 -0
- package/dist/engine/spec-migrator/folder-operations.js +109 -0
- package/dist/engine/spec-migrator/folder-operations.js.map +1 -0
- package/dist/engine/spec-migrator/frontmatter-parser.d.ts +11 -0
- package/dist/engine/spec-migrator/frontmatter-parser.d.ts.map +1 -0
- package/dist/engine/spec-migrator/frontmatter-parser.js +92 -0
- package/dist/engine/spec-migrator/frontmatter-parser.js.map +1 -0
- package/dist/engine/spec-migrator/index.d.ts +9 -0
- package/dist/engine/spec-migrator/index.d.ts.map +1 -0
- package/dist/engine/spec-migrator/index.js +18 -0
- package/dist/engine/spec-migrator/index.js.map +1 -0
- package/dist/engine/spec-migrator/legacy-migration.d.ts +13 -0
- package/dist/engine/spec-migrator/legacy-migration.d.ts.map +1 -0
- package/dist/engine/spec-migrator/legacy-migration.js +75 -0
- package/dist/engine/spec-migrator/legacy-migration.js.map +1 -0
- package/dist/engine/spec-migrator/migration-validator.d.ts +20 -0
- package/dist/engine/spec-migrator/migration-validator.d.ts.map +1 -0
- package/dist/engine/spec-migrator/migration-validator.js +35 -0
- package/dist/engine/spec-migrator/migration-validator.js.map +1 -0
- package/dist/engine/spec-migrator/path-utils.d.ts +13 -0
- package/dist/engine/spec-migrator/path-utils.d.ts.map +1 -0
- package/dist/engine/spec-migrator/path-utils.js +40 -0
- package/dist/engine/spec-migrator/path-utils.js.map +1 -0
- package/dist/engine/spec-migrator/prefix-migration.d.ts +11 -0
- package/dist/engine/spec-migrator/prefix-migration.d.ts.map +1 -0
- package/dist/engine/spec-migrator/prefix-migration.js +73 -0
- package/dist/engine/spec-migrator/prefix-migration.js.map +1 -0
- package/dist/engine/spec-migrator/reconcile-paths.d.ts +12 -0
- package/dist/engine/spec-migrator/reconcile-paths.d.ts.map +1 -0
- package/dist/engine/spec-migrator/reconcile-paths.js +77 -0
- package/dist/engine/spec-migrator/reconcile-paths.js.map +1 -0
- package/dist/engine/spec-migrator/version-detection.d.ts +5 -0
- package/dist/engine/spec-migrator/version-detection.d.ts.map +1 -0
- package/dist/engine/spec-migrator/version-detection.js +19 -0
- package/dist/engine/spec-migrator/version-detection.js.map +1 -0
- package/dist/engine/spec-migrator.d.ts +1 -58
- package/dist/engine/spec-migrator.d.ts.map +1 -1
- package/dist/engine/spec-migrator.js +2 -658
- package/dist/engine/spec-migrator.js.map +1 -1
- package/dist/engine/spec-summary-html/dashboard-renderer.d.ts +6 -0
- package/dist/engine/spec-summary-html/dashboard-renderer.d.ts.map +1 -0
- package/dist/engine/spec-summary-html/dashboard-renderer.js +333 -0
- package/dist/engine/spec-summary-html/dashboard-renderer.js.map +1 -0
- package/dist/engine/spec-summary-html/hash-utils.d.ts +11 -0
- package/dist/engine/spec-summary-html/hash-utils.d.ts.map +1 -0
- package/dist/engine/spec-summary-html/hash-utils.js +39 -0
- package/dist/engine/spec-summary-html/hash-utils.js.map +1 -0
- package/dist/engine/spec-summary-html/index.d.ts +4 -0
- package/dist/engine/spec-summary-html/index.d.ts.map +1 -0
- package/dist/engine/spec-summary-html/index.js +6 -0
- package/dist/engine/spec-summary-html/index.js.map +1 -0
- package/dist/engine/spec-summary-html/report-renderer.d.ts +9 -0
- package/dist/engine/spec-summary-html/report-renderer.d.ts.map +1 -0
- package/dist/engine/spec-summary-html/report-renderer.js +139 -0
- package/dist/engine/spec-summary-html/report-renderer.js.map +1 -0
- package/dist/engine/spec-summary-html.d.ts +1 -0
- package/dist/engine/spec-summary-html.d.ts.map +1 -1
- package/dist/engine/spec-summary-html.js +19 -473
- package/dist/engine/spec-summary-html.js.map +1 -1
- package/dist/engine/update-notifier.d.ts +8 -0
- package/dist/engine/update-notifier.d.ts.map +1 -0
- package/dist/engine/update-notifier.js +130 -0
- package/dist/engine/update-notifier.js.map +1 -0
- package/dist/engine/validator/dor-dod.d.ts.map +1 -1
- package/dist/engine/validator/dor-dod.js +8 -5
- package/dist/engine/validator/dor-dod.js.map +1 -1
- package/dist/engine/validator.d.ts.map +1 -1
- package/dist/engine/validator.js +4 -3
- package/dist/engine/validator.js.map +1 -1
- package/dist/engine/validator.test.d.ts +2 -0
- package/dist/engine/validator.test.d.ts.map +1 -0
- package/dist/engine/validator.test.js +2371 -0
- package/dist/engine/validator.test.js.map +1 -0
- package/dist/engine/web-fetcher.test.d.ts +2 -0
- package/dist/engine/web-fetcher.test.d.ts.map +1 -0
- package/dist/engine/web-fetcher.test.js +360 -0
- package/dist/engine/web-fetcher.test.js.map +1 -0
- package/dist/i18n/index.test.d.ts +2 -0
- package/dist/i18n/index.test.d.ts.map +1 -0
- package/dist/i18n/index.test.js +375 -0
- package/dist/i18n/index.test.js.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/index.test.d.ts +2 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/index.test.js +124 -0
- package/dist/index.test.js.map +1 -0
- package/dist/resources/patterns.test.d.ts +2 -0
- package/dist/resources/patterns.test.d.ts.map +1 -0
- package/dist/resources/patterns.test.js +142 -0
- package/dist/resources/patterns.test.js.map +1 -0
- package/dist/resources/process.test.d.ts +2 -0
- package/dist/resources/process.test.d.ts.map +1 -0
- package/dist/resources/process.test.js +48 -0
- package/dist/resources/process.test.js.map +1 -0
- package/dist/resources/registry.test.d.ts +2 -0
- package/dist/resources/registry.test.d.ts.map +1 -0
- package/dist/resources/registry.test.js +138 -0
- package/dist/resources/registry.test.js.map +1 -0
- package/dist/resources/specs.test.d.ts +2 -0
- package/dist/resources/specs.test.d.ts.map +1 -0
- package/dist/resources/specs.test.js +130 -0
- package/dist/resources/specs.test.js.map +1 -0
- package/dist/resources/templates.test.d.ts +2 -0
- package/dist/resources/templates.test.d.ts.map +1 -0
- package/dist/resources/templates.test.js +119 -0
- package/dist/resources/templates.test.js.map +1 -0
- package/dist/smoke.test.d.ts +2 -0
- package/dist/smoke.test.d.ts.map +1 -0
- package/dist/smoke.test.js +229 -0
- package/dist/smoke.test.js.map +1 -0
- package/dist/storage/base-store.test.d.ts +2 -0
- package/dist/storage/base-store.test.d.ts.map +1 -0
- package/dist/storage/base-store.test.js +180 -0
- package/dist/storage/base-store.test.js.map +1 -0
- package/dist/storage/global-store.test.d.ts +2 -0
- package/dist/storage/global-store.test.d.ts.map +1 -0
- package/dist/storage/global-store.test.js +327 -0
- package/dist/storage/global-store.test.js.map +1 -0
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +1 -0
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/index.test.d.ts +2 -0
- package/dist/storage/index.test.d.ts.map +1 -0
- package/dist/storage/index.test.js +56 -0
- package/dist/storage/index.test.js.map +1 -0
- package/dist/storage/knowledge-store.test.d.ts +2 -0
- package/dist/storage/knowledge-store.test.d.ts.map +1 -0
- package/dist/storage/knowledge-store.test.js +368 -0
- package/dist/storage/knowledge-store.test.js.map +1 -0
- package/dist/storage/lessons-store.d.ts +10 -0
- package/dist/storage/lessons-store.d.ts.map +1 -0
- package/dist/storage/lessons-store.js +67 -0
- package/dist/storage/lessons-store.js.map +1 -0
- package/dist/storage/metrics-store.test.d.ts +2 -0
- package/dist/storage/metrics-store.test.d.ts.map +1 -0
- package/dist/storage/metrics-store.test.js +212 -0
- package/dist/storage/metrics-store.test.js.map +1 -0
- package/dist/storage/pattern-store.test.d.ts +2 -0
- package/dist/storage/pattern-store.test.d.ts.map +1 -0
- package/dist/storage/pattern-store.test.js +224 -0
- package/dist/storage/pattern-store.test.js.map +1 -0
- package/dist/storage/spec-store.test.d.ts +2 -0
- package/dist/storage/spec-store.test.d.ts.map +1 -0
- package/dist/storage/spec-store.test.js +227 -0
- package/dist/storage/spec-store.test.js.map +1 -0
- package/dist/tools/audit.test.d.ts +2 -0
- package/dist/tools/audit.test.d.ts.map +1 -0
- package/dist/tools/audit.test.js +169 -0
- package/dist/tools/audit.test.js.map +1 -0
- package/dist/tools/challenge-spec.test.d.ts +2 -0
- package/dist/tools/challenge-spec.test.d.ts.map +1 -0
- package/dist/tools/challenge-spec.test.js +782 -0
- package/dist/tools/challenge-spec.test.js.map +1 -0
- package/dist/tools/check-versions.test.d.ts +2 -0
- package/dist/tools/check-versions.test.d.ts.map +1 -0
- package/dist/tools/check-versions.test.js +214 -0
- package/dist/tools/check-versions.test.js.map +1 -0
- package/dist/tools/clarify-requirements.test.d.ts +2 -0
- package/dist/tools/clarify-requirements.test.d.ts.map +1 -0
- package/dist/tools/clarify-requirements.test.js +161 -0
- package/dist/tools/clarify-requirements.test.js.map +1 -0
- package/dist/tools/consult-docs.test.d.ts +2 -0
- package/dist/tools/consult-docs.test.d.ts.map +1 -0
- package/dist/tools/consult-docs.test.js +140 -0
- package/dist/tools/consult-docs.test.js.map +1 -0
- package/dist/tools/create-spec/lessons-injector.d.ts +6 -0
- package/dist/tools/create-spec/lessons-injector.d.ts.map +1 -0
- package/dist/tools/create-spec/lessons-injector.js +53 -0
- package/dist/tools/create-spec/lessons-injector.js.map +1 -0
- package/dist/tools/create-spec.d.ts.map +1 -1
- package/dist/tools/create-spec.js +6 -1
- package/dist/tools/create-spec.js.map +1 -1
- package/dist/tools/create-spec.test.d.ts +2 -0
- package/dist/tools/create-spec.test.d.ts.map +1 -0
- package/dist/tools/create-spec.test.js +233 -0
- package/dist/tools/create-spec.test.js.map +1 -0
- package/dist/tools/define-ui-contract.test.d.ts +2 -0
- package/dist/tools/define-ui-contract.test.d.ts.map +1 -0
- package/dist/tools/define-ui-contract.test.js +479 -0
- package/dist/tools/define-ui-contract.test.js.map +1 -0
- package/dist/tools/design-schema.test.d.ts +2 -0
- package/dist/tools/design-schema.test.d.ts.map +1 -0
- package/dist/tools/design-schema.test.js +301 -0
- package/dist/tools/design-schema.test.js.map +1 -0
- package/dist/tools/detect-agent.test.d.ts +2 -0
- package/dist/tools/detect-agent.test.d.ts.map +1 -0
- package/dist/tools/detect-agent.test.js +133 -0
- package/dist/tools/detect-agent.test.js.map +1 -0
- package/dist/tools/detect-drift.test.d.ts +2 -0
- package/dist/tools/detect-drift.test.d.ts.map +1 -0
- package/dist/tools/detect-drift.test.js +312 -0
- package/dist/tools/detect-drift.test.js.map +1 -0
- package/dist/tools/discover-mcps.test.d.ts +2 -0
- package/dist/tools/discover-mcps.test.d.ts.map +1 -0
- package/dist/tools/discover-mcps.test.js +345 -0
- package/dist/tools/discover-mcps.test.js.map +1 -0
- package/dist/tools/estimate.test.d.ts +2 -0
- package/dist/tools/estimate.test.d.ts.map +1 -0
- package/dist/tools/estimate.test.js +137 -0
- package/dist/tools/estimate.test.js.map +1 -0
- package/dist/tools/generate-adr.test.d.ts +2 -0
- package/dist/tools/generate-adr.test.d.ts.map +1 -0
- package/dist/tools/generate-adr.test.js +206 -0
- package/dist/tools/generate-adr.test.js.map +1 -0
- package/dist/tools/generate-checklist.test.d.ts +2 -0
- package/dist/tools/generate-checklist.test.d.ts.map +1 -0
- package/dist/tools/generate-checklist.test.js +201 -0
- package/dist/tools/generate-checklist.test.js.map +1 -0
- package/dist/tools/generate-docs.test.d.ts +2 -0
- package/dist/tools/generate-docs.test.d.ts.map +1 -0
- package/dist/tools/generate-docs.test.js +183 -0
- package/dist/tools/generate-docs.test.js.map +1 -0
- package/dist/tools/generate-execution-plan.test.d.ts +2 -0
- package/dist/tools/generate-execution-plan.test.d.ts.map +1 -0
- package/dist/tools/generate-execution-plan.test.js +643 -0
- package/dist/tools/generate-execution-plan.test.js.map +1 -0
- package/dist/tools/generate-rules.test.d.ts +2 -0
- package/dist/tools/generate-rules.test.d.ts.map +1 -0
- package/dist/tools/generate-rules.test.js +148 -0
- package/dist/tools/generate-rules.test.js.map +1 -0
- package/dist/tools/generate-skill.test.d.ts +2 -0
- package/dist/tools/generate-skill.test.d.ts.map +1 -0
- package/dist/tools/generate-skill.test.js +138 -0
- package/dist/tools/generate-skill.test.js.map +1 -0
- package/dist/tools/generate-sub-agent.test.d.ts +2 -0
- package/dist/tools/generate-sub-agent.test.d.ts.map +1 -0
- package/dist/tools/generate-sub-agent.test.js +162 -0
- package/dist/tools/generate-sub-agent.test.js.map +1 -0
- package/dist/tools/generate-tests.test.d.ts +2 -0
- package/dist/tools/generate-tests.test.d.ts.map +1 -0
- package/dist/tools/generate-tests.test.js +222 -0
- package/dist/tools/generate-tests.test.js.map +1 -0
- package/dist/tools/init-constitution.test.d.ts +2 -0
- package/dist/tools/init-constitution.test.d.ts.map +1 -0
- package/dist/tools/init-constitution.test.js +398 -0
- package/dist/tools/init-constitution.test.js.map +1 -0
- package/dist/tools/init-project/config-builder.d.ts +12 -0
- package/dist/tools/init-project/config-builder.d.ts.map +1 -0
- package/dist/tools/init-project/config-builder.js +31 -0
- package/dist/tools/init-project/config-builder.js.map +1 -0
- package/dist/tools/init-project/git-setup.d.ts +8 -0
- package/dist/tools/init-project/git-setup.d.ts.map +1 -0
- package/dist/tools/init-project/git-setup.js +70 -0
- package/dist/tools/init-project/git-setup.js.map +1 -0
- package/dist/tools/init-project/handler.d.ts.map +1 -1
- package/dist/tools/init-project/handler.js +27 -364
- package/dist/tools/init-project/handler.js.map +1 -1
- package/dist/tools/init-project/lifecycle-helpers.d.ts +32 -0
- package/dist/tools/init-project/lifecycle-helpers.d.ts.map +1 -0
- package/dist/tools/init-project/lifecycle-helpers.js +153 -0
- package/dist/tools/init-project/lifecycle-helpers.js.map +1 -0
- package/dist/tools/init-project/migration-runner.d.ts +28 -0
- package/dist/tools/init-project/migration-runner.d.ts.map +1 -0
- package/dist/tools/init-project/migration-runner.js +57 -0
- package/dist/tools/init-project/migration-runner.js.map +1 -0
- package/dist/tools/init-project/result-builder.d.ts.map +1 -1
- package/dist/tools/init-project/result-builder.js +1 -0
- package/dist/tools/init-project/result-builder.js.map +1 -1
- package/dist/tools/init-project/rules-writer.d.ts +14 -0
- package/dist/tools/init-project/rules-writer.d.ts.map +1 -0
- package/dist/tools/init-project/rules-writer.js +43 -0
- package/dist/tools/init-project/rules-writer.js.map +1 -0
- package/dist/tools/init-project/scaffold-writer.d.ts +29 -0
- package/dist/tools/init-project/scaffold-writer.d.ts.map +1 -0
- package/dist/tools/init-project/scaffold-writer.js +76 -0
- package/dist/tools/init-project/scaffold-writer.js.map +1 -0
- package/dist/tools/init-project/stack-detector.d.ts +16 -0
- package/dist/tools/init-project/stack-detector.d.ts.map +1 -0
- package/dist/tools/init-project/stack-detector.js +19 -0
- package/dist/tools/init-project/stack-detector.js.map +1 -0
- package/dist/tools/init-project.test.d.ts +2 -0
- package/dist/tools/init-project.test.d.ts.map +1 -0
- package/dist/tools/init-project.test.js +158 -0
- package/dist/tools/init-project.test.js.map +1 -0
- package/dist/tools/integrate-pm.test.d.ts +2 -0
- package/dist/tools/integrate-pm.test.d.ts.map +1 -0
- package/dist/tools/integrate-pm.test.js +558 -0
- package/dist/tools/integrate-pm.test.js.map +1 -0
- package/dist/tools/learn.test.d.ts +2 -0
- package/dist/tools/learn.test.d.ts.map +1 -0
- package/dist/tools/learn.test.js +123 -0
- package/dist/tools/learn.test.js.map +1 -0
- package/dist/tools/lessons-handler.d.ts +6 -0
- package/dist/tools/lessons-handler.d.ts.map +1 -0
- package/dist/tools/lessons-handler.js +64 -0
- package/dist/tools/lessons-handler.js.map +1 -0
- package/dist/tools/list-specs.js +1 -1
- package/dist/tools/list-specs.js.map +1 -1
- package/dist/tools/list-specs.test.d.ts +2 -0
- package/dist/tools/list-specs.test.d.ts.map +1 -0
- package/dist/tools/list-specs.test.js +110 -0
- package/dist/tools/list-specs.test.js.map +1 -0
- package/dist/tools/manage-context.test.d.ts +2 -0
- package/dist/tools/manage-context.test.d.ts.map +1 -0
- package/dist/tools/manage-context.test.js +359 -0
- package/dist/tools/manage-context.test.js.map +1 -0
- package/dist/tools/manage-git.test.d.ts +2 -0
- package/dist/tools/manage-git.test.d.ts.map +1 -0
- package/dist/tools/manage-git.test.js +882 -0
- package/dist/tools/manage-git.test.js.map +1 -0
- package/dist/tools/orchestrate.test.d.ts +2 -0
- package/dist/tools/orchestrate.test.d.ts.map +1 -0
- package/dist/tools/orchestrate.test.js +1117 -0
- package/dist/tools/orchestrate.test.js.map +1 -0
- package/dist/tools/reconcile-spec.test.d.ts +2 -0
- package/dist/tools/reconcile-spec.test.d.ts.map +1 -0
- package/dist/tools/reconcile-spec.test.js +259 -0
- package/dist/tools/reconcile-spec.test.js.map +1 -0
- package/dist/tools/red-team.d.ts +3 -0
- package/dist/tools/red-team.d.ts.map +1 -0
- package/dist/tools/red-team.js +302 -0
- package/dist/tools/red-team.js.map +1 -0
- package/dist/tools/register-lessons-tools.d.ts +3 -0
- package/dist/tools/register-lessons-tools.d.ts.map +1 -0
- package/dist/tools/register-lessons-tools.js +62 -0
- package/dist/tools/register-lessons-tools.js.map +1 -0
- package/dist/tools/register-platform-tools/design-stack-tools.d.ts.map +1 -1
- package/dist/tools/register-platform-tools/design-stack-tools.js +14 -0
- package/dist/tools/register-platform-tools/design-stack-tools.js.map +1 -1
- package/dist/tools/register-platform-tools.test.d.ts +2 -0
- package/dist/tools/register-platform-tools.test.d.ts.map +1 -0
- package/dist/tools/register-platform-tools.test.js +404 -0
- package/dist/tools/register-platform-tools.test.js.map +1 -0
- package/dist/tools/register-spec-tools.test.d.ts +2 -0
- package/dist/tools/register-spec-tools.test.d.ts.map +1 -0
- package/dist/tools/register-spec-tools.test.js +407 -0
- package/dist/tools/register-spec-tools.test.js.map +1 -0
- package/dist/tools/reverse-engineer.test.d.ts +2 -0
- package/dist/tools/reverse-engineer.test.d.ts.map +1 -0
- package/dist/tools/reverse-engineer.test.js +206 -0
- package/dist/tools/reverse-engineer.test.js.map +1 -0
- package/dist/tools/schemas.d.ts +20 -0
- package/dist/tools/schemas.d.ts.map +1 -0
- package/dist/tools/schemas.js +133 -0
- package/dist/tools/schemas.js.map +1 -0
- package/dist/tools/schemas.test.d.ts +2 -0
- package/dist/tools/schemas.test.d.ts.map +1 -0
- package/dist/tools/schemas.test.js +245 -0
- package/dist/tools/schemas.test.js.map +1 -0
- package/dist/tools/set-locale.test.d.ts +2 -0
- package/dist/tools/set-locale.test.d.ts.map +1 -0
- package/dist/tools/set-locale.test.js +74 -0
- package/dist/tools/set-locale.test.js.map +1 -0
- package/dist/tools/suggest-mcps.test.d.ts +2 -0
- package/dist/tools/suggest-mcps.test.d.ts.map +1 -0
- package/dist/tools/suggest-mcps.test.js +198 -0
- package/dist/tools/suggest-mcps.test.js.map +1 -0
- package/dist/tools/suggest-stack.test.d.ts +2 -0
- package/dist/tools/suggest-stack.test.d.ts.map +1 -0
- package/dist/tools/suggest-stack.test.js +181 -0
- package/dist/tools/suggest-stack.test.js.map +1 -0
- package/dist/tools/suggest-tooling.test.d.ts +2 -0
- package/dist/tools/suggest-tooling.test.d.ts.map +1 -0
- package/dist/tools/suggest-tooling.test.js +213 -0
- package/dist/tools/suggest-tooling.test.js.map +1 -0
- package/dist/tools/summarize-spec.test.d.ts +2 -0
- package/dist/tools/summarize-spec.test.d.ts.map +1 -0
- package/dist/tools/summarize-spec.test.js +180 -0
- package/dist/tools/summarize-spec.test.js.map +1 -0
- package/dist/tools/update-status/dod-gates.d.ts +16 -0
- package/dist/tools/update-status/dod-gates.d.ts.map +1 -0
- package/dist/tools/update-status/dod-gates.js +117 -0
- package/dist/tools/update-status/dod-gates.js.map +1 -0
- package/dist/tools/update-status/file-sync.d.ts +6 -0
- package/dist/tools/update-status/file-sync.d.ts.map +1 -0
- package/dist/tools/update-status/file-sync.js +112 -0
- package/dist/tools/update-status/file-sync.js.map +1 -0
- package/dist/tools/update-status/index.d.ts +3 -0
- package/dist/tools/update-status/index.d.ts.map +1 -0
- package/dist/tools/update-status/index.js +181 -0
- package/dist/tools/update-status/index.js.map +1 -0
- package/dist/tools/update-status/response-builder.d.ts +4 -0
- package/dist/tools/update-status/response-builder.d.ts.map +1 -0
- package/dist/tools/update-status/response-builder.js +69 -0
- package/dist/tools/update-status/response-builder.js.map +1 -0
- package/dist/tools/update-status/side-effects.d.ts +15 -0
- package/dist/tools/update-status/side-effects.d.ts.map +1 -0
- package/dist/tools/update-status/side-effects.js +64 -0
- package/dist/tools/update-status/side-effects.js.map +1 -0
- package/dist/tools/update-status/transition-guard.d.ts +20 -0
- package/dist/tools/update-status/transition-guard.d.ts.map +1 -0
- package/dist/tools/update-status/transition-guard.js +75 -0
- package/dist/tools/update-status/transition-guard.js.map +1 -0
- package/dist/tools/update-status.d.ts +1 -2
- package/dist/tools/update-status.d.ts.map +1 -1
- package/dist/tools/update-status.js +2 -461
- package/dist/tools/update-status.js.map +1 -1
- package/dist/tools/update-status.test.d.ts +2 -0
- package/dist/tools/update-status.test.d.ts.map +1 -0
- package/dist/tools/update-status.test.js +142 -0
- package/dist/tools/update-status.test.js.map +1 -0
- package/dist/tools/validate.d.ts.map +1 -1
- package/dist/tools/validate.js +18 -4
- package/dist/tools/validate.js.map +1 -1
- package/dist/tools/validate.test.d.ts +2 -0
- package/dist/tools/validate.test.d.ts.map +1 -0
- package/dist/tools/validate.test.js +137 -0
- package/dist/tools/validate.test.js.map +1 -0
- package/dist/types/analysis.d.ts +2 -1
- package/dist/types/analysis.d.ts.map +1 -1
- package/dist/types/conventions.d.ts +5 -0
- package/dist/types/conventions.d.ts.map +1 -1
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/lessons.d.ts +50 -0
- package/dist/types/lessons.d.ts.map +1 -0
- package/dist/types/lessons.js +3 -0
- package/dist/types/lessons.js.map +1 -0
- package/dist/types/project/planu-config.d.ts +2 -0
- package/dist/types/project/planu-config.d.ts.map +1 -1
- package/dist/types/red-team.d.ts +29 -0
- package/dist/types/red-team.d.ts.map +1 -0
- package/dist/types/red-team.js +3 -0
- package/dist/types/red-team.js.map +1 -0
- package/dist/types/update-notifier.d.ts +5 -0
- package/dist/types/update-notifier.d.ts.map +1 -0
- package/dist/types/update-notifier.js +3 -0
- package/dist/types/update-notifier.js.map +1 -0
- package/package.json +9 -2
- package/src/config/license-plans.json +5 -2
- package/src/i18n/messages/en.json +5 -0
- package/src/i18n/messages/es.json +5 -0
- package/src/i18n/messages/pt.json +5 -0
|
@@ -0,0 +1,882 @@
|
|
|
1
|
+
// Tests for handleManageGit
|
|
2
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
+
vi.mock('../i18n/index.js', () => ({
|
|
4
|
+
t: (key) => key,
|
|
5
|
+
ti: (key, vars) => `${key}:${JSON.stringify(vars)}`,
|
|
6
|
+
}));
|
|
7
|
+
vi.mock('../storage/index.js', () => ({
|
|
8
|
+
specStore: {
|
|
9
|
+
getSpec: vi.fn(),
|
|
10
|
+
updateSpec: vi.fn(),
|
|
11
|
+
listSpecs: vi.fn(),
|
|
12
|
+
},
|
|
13
|
+
knowledgeStore: { getKnowledge: vi.fn() },
|
|
14
|
+
}));
|
|
15
|
+
// Mock promisify to return the fn as-is so we can mock execFile to return promises directly
|
|
16
|
+
vi.mock('node:util', () => ({
|
|
17
|
+
promisify: (fn) => fn,
|
|
18
|
+
}));
|
|
19
|
+
vi.mock('node:child_process', () => ({
|
|
20
|
+
execFile: vi.fn(),
|
|
21
|
+
}));
|
|
22
|
+
vi.mock('node:fs/promises', () => ({
|
|
23
|
+
writeFile: vi.fn(),
|
|
24
|
+
mkdir: vi.fn(),
|
|
25
|
+
}));
|
|
26
|
+
import { handleManageGit } from './manage-git.js';
|
|
27
|
+
import { specStore, knowledgeStore } from '../storage/index.js';
|
|
28
|
+
import { execFile } from 'node:child_process';
|
|
29
|
+
const mockGetKnowledge = vi.mocked(knowledgeStore.getKnowledge);
|
|
30
|
+
const mockGetSpec = vi.mocked(specStore.getSpec);
|
|
31
|
+
const mockUpdateSpec = vi.mocked(specStore.updateSpec);
|
|
32
|
+
const mockListSpecs = vi.mocked(specStore.listSpecs);
|
|
33
|
+
const mockExecFile = execFile;
|
|
34
|
+
const baseKnowledge = {
|
|
35
|
+
language: 'typescript',
|
|
36
|
+
framework: 'express',
|
|
37
|
+
projectPath: '/test/project',
|
|
38
|
+
stack: [],
|
|
39
|
+
conventions: {},
|
|
40
|
+
architecture: { primary: 'monolith', secondary: [], layers: [] },
|
|
41
|
+
database: 'postgresql',
|
|
42
|
+
testCommand: 'npm test',
|
|
43
|
+
buildCommand: 'npm run build',
|
|
44
|
+
linting: { detectedLinters: [{ tool: 'eslint' }] },
|
|
45
|
+
apiContracts: [],
|
|
46
|
+
layers: [],
|
|
47
|
+
};
|
|
48
|
+
const baseSpec = {
|
|
49
|
+
id: 'SPEC-001',
|
|
50
|
+
slug: 'user-auth',
|
|
51
|
+
title: 'User Authentication',
|
|
52
|
+
type: 'feature',
|
|
53
|
+
scope: 'feature',
|
|
54
|
+
target: 'backend',
|
|
55
|
+
tags: ['auth'],
|
|
56
|
+
status: 'approved',
|
|
57
|
+
risk: 'medium',
|
|
58
|
+
difficulty: 3,
|
|
59
|
+
estimation: { devHours: 8, reviewHours: 2, totalCostUsd: 100.00 },
|
|
60
|
+
dependencies: [],
|
|
61
|
+
gitBranch: null,
|
|
62
|
+
impactAnalysis: { requiresMigration: false, requiresFeatureFlag: false },
|
|
63
|
+
};
|
|
64
|
+
function mockGitSuccess(stdout = '') {
|
|
65
|
+
mockExecFile.mockResolvedValue({ stdout, stderr: '' });
|
|
66
|
+
}
|
|
67
|
+
beforeEach(() => {
|
|
68
|
+
vi.clearAllMocks();
|
|
69
|
+
mockGetKnowledge.mockResolvedValue(baseKnowledge);
|
|
70
|
+
mockUpdateSpec.mockResolvedValue(undefined);
|
|
71
|
+
});
|
|
72
|
+
describe('handleManageGit', () => {
|
|
73
|
+
// === create-branch ===
|
|
74
|
+
it('should error when create-branch called without specId', async () => {
|
|
75
|
+
const result = await handleManageGit({
|
|
76
|
+
projectId: 'proj-1',
|
|
77
|
+
action: 'create-branch',
|
|
78
|
+
});
|
|
79
|
+
expect(result.isError).toBe(true);
|
|
80
|
+
expect(result.content[0].text).toContain('specId is required');
|
|
81
|
+
});
|
|
82
|
+
it('should error when spec not found for create-branch', async () => {
|
|
83
|
+
mockGetSpec.mockResolvedValue(null);
|
|
84
|
+
const result = await handleManageGit({
|
|
85
|
+
projectId: 'proj-1',
|
|
86
|
+
action: 'create-branch',
|
|
87
|
+
specId: 'SPEC-999',
|
|
88
|
+
});
|
|
89
|
+
expect(result.isError).toBe(true);
|
|
90
|
+
});
|
|
91
|
+
it('should create a new branch', async () => {
|
|
92
|
+
mockGetSpec.mockResolvedValue(baseSpec);
|
|
93
|
+
// branch --list returns empty (no existing branch), then checkout -b succeeds
|
|
94
|
+
mockExecFile.mockResolvedValue({ stdout: '', stderr: '' });
|
|
95
|
+
const result = await handleManageGit({
|
|
96
|
+
projectId: 'proj-1',
|
|
97
|
+
action: 'create-branch',
|
|
98
|
+
specId: 'SPEC-001',
|
|
99
|
+
});
|
|
100
|
+
expect(result.isError).toBeUndefined();
|
|
101
|
+
const data = JSON.parse(result.content[0].text);
|
|
102
|
+
expect(data.action).toBe('create-branch');
|
|
103
|
+
expect(data.branchName).toContain('feat/');
|
|
104
|
+
expect(data.branchName).toContain('SPEC-001');
|
|
105
|
+
});
|
|
106
|
+
// === generate-pr ===
|
|
107
|
+
it('should error when generate-pr called without specId', async () => {
|
|
108
|
+
const result = await handleManageGit({
|
|
109
|
+
projectId: 'proj-1',
|
|
110
|
+
action: 'generate-pr',
|
|
111
|
+
});
|
|
112
|
+
expect(result.isError).toBe(true);
|
|
113
|
+
});
|
|
114
|
+
it('should generate PR template', async () => {
|
|
115
|
+
mockGetSpec.mockResolvedValue(baseSpec);
|
|
116
|
+
mockExecFile.mockImplementation((_cmd, args) => {
|
|
117
|
+
const argsArr = args;
|
|
118
|
+
if (argsArr.includes('--abbrev-ref')) {
|
|
119
|
+
return Promise.resolve({ stdout: 'feat/SPEC-001-user-auth\n', stderr: '' });
|
|
120
|
+
}
|
|
121
|
+
if (argsArr.includes('--verify')) {
|
|
122
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
123
|
+
}
|
|
124
|
+
if (argsArr.includes('--oneline')) {
|
|
125
|
+
return Promise.resolve({ stdout: 'abc1234 feat: add auth\n', stderr: '' });
|
|
126
|
+
}
|
|
127
|
+
if (argsArr.includes('--name-only')) {
|
|
128
|
+
return Promise.resolve({ stdout: 'src/auth.ts\n', stderr: '' });
|
|
129
|
+
}
|
|
130
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
131
|
+
});
|
|
132
|
+
const result = await handleManageGit({
|
|
133
|
+
projectId: 'proj-1',
|
|
134
|
+
action: 'generate-pr',
|
|
135
|
+
specId: 'SPEC-001',
|
|
136
|
+
});
|
|
137
|
+
expect(result.isError).toBeUndefined();
|
|
138
|
+
const data = JSON.parse(result.content[0].text);
|
|
139
|
+
expect(data.action).toBe('generate-pr');
|
|
140
|
+
expect(data.prTemplate).toBeDefined();
|
|
141
|
+
expect(data.prTemplate.title).toContain('SPEC-001');
|
|
142
|
+
});
|
|
143
|
+
// === link-commits ===
|
|
144
|
+
it('should link commits to specs', async () => {
|
|
145
|
+
mockExecFile.mockImplementation((_cmd, args) => {
|
|
146
|
+
const argsArr = args;
|
|
147
|
+
if (argsArr.includes('--grep=SPEC-')) {
|
|
148
|
+
return Promise.resolve({ stdout: 'abc12345|[SPEC-001] feat: auth|dev|2025-01-01T00:00:00Z\n', stderr: '' });
|
|
149
|
+
}
|
|
150
|
+
if (argsArr.includes('diff-tree')) {
|
|
151
|
+
return Promise.resolve({ stdout: 'src/auth.ts\n', stderr: '' });
|
|
152
|
+
}
|
|
153
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
154
|
+
});
|
|
155
|
+
const result = await handleManageGit({
|
|
156
|
+
projectId: 'proj-1',
|
|
157
|
+
action: 'link-commits',
|
|
158
|
+
});
|
|
159
|
+
const data = JSON.parse(result.content[0].text);
|
|
160
|
+
expect(data.action).toBe('link-commits');
|
|
161
|
+
expect(data.linkedCommits).toBeDefined();
|
|
162
|
+
});
|
|
163
|
+
// === generate-changelog ===
|
|
164
|
+
it('should generate changelog', async () => {
|
|
165
|
+
mockListSpecs.mockResolvedValue([
|
|
166
|
+
{ ...baseSpec, status: 'done' },
|
|
167
|
+
]);
|
|
168
|
+
mockGitSuccess('abc1234 feat: something\n');
|
|
169
|
+
const result = await handleManageGit({
|
|
170
|
+
projectId: 'proj-1',
|
|
171
|
+
action: 'generate-changelog',
|
|
172
|
+
});
|
|
173
|
+
const data = JSON.parse(result.content[0].text);
|
|
174
|
+
expect(data.action).toBe('generate-changelog');
|
|
175
|
+
expect(data.changelog).toBeDefined();
|
|
176
|
+
expect(data.changelog.content).toContain('Changelog');
|
|
177
|
+
});
|
|
178
|
+
// === setup-hooks ===
|
|
179
|
+
it('should setup git hooks', async () => {
|
|
180
|
+
mockExecFile.mockImplementation((_cmd, args) => {
|
|
181
|
+
const argsArr = args;
|
|
182
|
+
if (argsArr.includes('--git-dir')) {
|
|
183
|
+
return Promise.resolve({ stdout: '.git\n', stderr: '' });
|
|
184
|
+
}
|
|
185
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
186
|
+
});
|
|
187
|
+
const result = await handleManageGit({
|
|
188
|
+
projectId: 'proj-1',
|
|
189
|
+
action: 'setup-hooks',
|
|
190
|
+
});
|
|
191
|
+
const data = JSON.parse(result.content[0].text);
|
|
192
|
+
expect(data.action).toBe('setup-hooks');
|
|
193
|
+
expect(data.hooksInstalled).toBeDefined();
|
|
194
|
+
expect(data.hooksInstalled).toContain('pre-commit');
|
|
195
|
+
expect(data.hooksInstalled).toContain('commit-msg');
|
|
196
|
+
expect(data.hooksInstalled).toContain('pre-push');
|
|
197
|
+
});
|
|
198
|
+
// === check-branch ===
|
|
199
|
+
it('should check branch naming', async () => {
|
|
200
|
+
mockGetSpec.mockResolvedValue(baseSpec);
|
|
201
|
+
mockGitSuccess('feat/SPEC-001-user-auth\n');
|
|
202
|
+
const result = await handleManageGit({
|
|
203
|
+
projectId: 'proj-1',
|
|
204
|
+
action: 'check-branch',
|
|
205
|
+
});
|
|
206
|
+
const data = JSON.parse(result.content[0].text);
|
|
207
|
+
expect(data.action).toBe('check-branch');
|
|
208
|
+
expect(data.followsConvention).toBe(true);
|
|
209
|
+
});
|
|
210
|
+
// === status ===
|
|
211
|
+
it('should return git status', async () => {
|
|
212
|
+
mockListSpecs.mockResolvedValue([
|
|
213
|
+
{ ...baseSpec, status: 'implementing' },
|
|
214
|
+
]);
|
|
215
|
+
mockExecFile.mockImplementation((_cmd, args) => {
|
|
216
|
+
const argsArr = args;
|
|
217
|
+
if (argsArr.includes('--abbrev-ref')) {
|
|
218
|
+
return Promise.resolve({ stdout: 'feat/SPEC-001-auth\n', stderr: '' });
|
|
219
|
+
}
|
|
220
|
+
if (argsArr.includes('--porcelain')) {
|
|
221
|
+
return Promise.resolve({ stdout: ' M src/auth.ts\n', stderr: '' });
|
|
222
|
+
}
|
|
223
|
+
if (argsArr.includes('--verify')) {
|
|
224
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
225
|
+
}
|
|
226
|
+
if (argsArr.includes('--count')) {
|
|
227
|
+
return Promise.resolve({ stdout: '3\n', stderr: '' });
|
|
228
|
+
}
|
|
229
|
+
if (argsArr.includes('--format=%s')) {
|
|
230
|
+
return Promise.resolve({ stdout: '[SPEC-001] auth\n', stderr: '' });
|
|
231
|
+
}
|
|
232
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
233
|
+
});
|
|
234
|
+
const result = await handleManageGit({
|
|
235
|
+
projectId: 'proj-1',
|
|
236
|
+
action: 'status',
|
|
237
|
+
});
|
|
238
|
+
const data = JSON.parse(result.content[0].text);
|
|
239
|
+
expect(data.action).toBe('status');
|
|
240
|
+
expect(data.status).toBeDefined();
|
|
241
|
+
});
|
|
242
|
+
// === unknown action ===
|
|
243
|
+
it('should return error for unknown action', async () => {
|
|
244
|
+
const result = await handleManageGit({
|
|
245
|
+
projectId: 'proj-1',
|
|
246
|
+
action: 'unknown',
|
|
247
|
+
});
|
|
248
|
+
expect(result.isError).toBe(true);
|
|
249
|
+
expect(result.content[0].text).toContain('Unknown git action');
|
|
250
|
+
});
|
|
251
|
+
// === general error handling ===
|
|
252
|
+
it('should catch and return errors from git commands', async () => {
|
|
253
|
+
mockGetKnowledge.mockRejectedValue(new Error('Network error'));
|
|
254
|
+
const result = await handleManageGit({
|
|
255
|
+
projectId: 'proj-1',
|
|
256
|
+
action: 'status',
|
|
257
|
+
});
|
|
258
|
+
expect(result.isError).toBe(true);
|
|
259
|
+
expect(result.content[0].text).toContain('Network error');
|
|
260
|
+
});
|
|
261
|
+
// --- Coverage for link-commits with no matching commits (line 374+) ---
|
|
262
|
+
it('should return empty linkedCommits when git log has no output', async () => {
|
|
263
|
+
mockExecFile.mockResolvedValue({ stdout: '', stderr: '' });
|
|
264
|
+
const result = await handleManageGit({
|
|
265
|
+
projectId: 'proj-1',
|
|
266
|
+
action: 'link-commits',
|
|
267
|
+
specId: 'SPEC-001',
|
|
268
|
+
});
|
|
269
|
+
const data = JSON.parse(result.content[0].text);
|
|
270
|
+
expect(data.linkedCommits).toHaveLength(0);
|
|
271
|
+
expect(data.message).toContain('0 commit(s)');
|
|
272
|
+
});
|
|
273
|
+
it('should handle link-commits when git log throws error', async () => {
|
|
274
|
+
// First call is for resolveProjectPath, subsequent calls fail
|
|
275
|
+
let callCount = 0;
|
|
276
|
+
mockExecFile.mockImplementation(() => {
|
|
277
|
+
callCount++;
|
|
278
|
+
if (callCount <= 1) {
|
|
279
|
+
// This is still resolved through resolveProjectPath -> knowledge
|
|
280
|
+
return Promise.reject(new Error('fatal: not a git repository'));
|
|
281
|
+
}
|
|
282
|
+
return Promise.reject(new Error('git error'));
|
|
283
|
+
});
|
|
284
|
+
// But resolveProjectPath uses knowledgeStore, not git. So the first git call is the log.
|
|
285
|
+
// When git log fails, it should catch and return empty commits
|
|
286
|
+
mockExecFile.mockRejectedValue({ stderr: 'fatal: bad revision', message: 'bad revision' });
|
|
287
|
+
const result = await handleManageGit({
|
|
288
|
+
projectId: 'proj-1',
|
|
289
|
+
action: 'link-commits',
|
|
290
|
+
});
|
|
291
|
+
const data = JSON.parse(result.content[0].text);
|
|
292
|
+
expect(data.linkedCommits).toHaveLength(0);
|
|
293
|
+
});
|
|
294
|
+
it('should handle link-commits lines with missing hash/message', async () => {
|
|
295
|
+
mockExecFile.mockImplementation((_cmd, args) => {
|
|
296
|
+
const argsArr = args;
|
|
297
|
+
if (argsArr.includes('--grep=SPEC-')) {
|
|
298
|
+
// One valid line and one with missing fields
|
|
299
|
+
return Promise.resolve({
|
|
300
|
+
stdout: 'abc12345|[SPEC-001] feat: auth|dev|2025-01-01T00:00:00Z\n|||\n',
|
|
301
|
+
stderr: '',
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
if (argsArr.includes('diff-tree')) {
|
|
305
|
+
return Promise.resolve({ stdout: 'src/file.ts\n', stderr: '' });
|
|
306
|
+
}
|
|
307
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
308
|
+
});
|
|
309
|
+
const result = await handleManageGit({
|
|
310
|
+
projectId: 'proj-1',
|
|
311
|
+
action: 'link-commits',
|
|
312
|
+
});
|
|
313
|
+
const data = JSON.parse(result.content[0].text);
|
|
314
|
+
// Only the valid line should be processed, empty hash/message lines skipped
|
|
315
|
+
expect(data.linkedCommits).toHaveLength(1);
|
|
316
|
+
});
|
|
317
|
+
it('should handle diff-tree failure for individual commits', async () => {
|
|
318
|
+
mockExecFile.mockImplementation((_cmd, args) => {
|
|
319
|
+
const argsArr = args;
|
|
320
|
+
if (argsArr.includes('--grep=SPEC-')) {
|
|
321
|
+
return Promise.resolve({
|
|
322
|
+
stdout: 'abc12345|[SPEC-001] feat: auth|dev|2025-01-01T00:00:00Z\n',
|
|
323
|
+
stderr: '',
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
if (argsArr.includes('diff-tree')) {
|
|
327
|
+
return Promise.reject(new Error('diff-tree failed'));
|
|
328
|
+
}
|
|
329
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
330
|
+
});
|
|
331
|
+
const result = await handleManageGit({
|
|
332
|
+
projectId: 'proj-1',
|
|
333
|
+
action: 'link-commits',
|
|
334
|
+
});
|
|
335
|
+
const data = JSON.parse(result.content[0].text);
|
|
336
|
+
expect(data.linkedCommits).toHaveLength(1);
|
|
337
|
+
expect(data.linkedCommits[0].filesChanged).toHaveLength(0);
|
|
338
|
+
});
|
|
339
|
+
// --- Coverage for generate-pr with master fallback (line 679+) ---
|
|
340
|
+
it('should fallback to master branch when main does not exist', async () => {
|
|
341
|
+
mockGetSpec.mockResolvedValue(baseSpec);
|
|
342
|
+
mockExecFile.mockImplementation((_cmd, args) => {
|
|
343
|
+
const argsArr = args;
|
|
344
|
+
if (argsArr.includes('--abbrev-ref')) {
|
|
345
|
+
return Promise.resolve({ stdout: 'feat/SPEC-001-user-auth\n', stderr: '' });
|
|
346
|
+
}
|
|
347
|
+
if (argsArr.includes('--verify') && argsArr.includes('main')) {
|
|
348
|
+
return Promise.reject(new Error('not found'));
|
|
349
|
+
}
|
|
350
|
+
if (argsArr.includes('--verify') && argsArr.includes('master')) {
|
|
351
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
352
|
+
}
|
|
353
|
+
if (argsArr.includes('--oneline')) {
|
|
354
|
+
return Promise.resolve({ stdout: 'abc1234 feat: add auth\n', stderr: '' });
|
|
355
|
+
}
|
|
356
|
+
if (argsArr.includes('--name-only')) {
|
|
357
|
+
return Promise.resolve({ stdout: 'src/auth.ts\n', stderr: '' });
|
|
358
|
+
}
|
|
359
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
360
|
+
});
|
|
361
|
+
const result = await handleManageGit({
|
|
362
|
+
projectId: 'proj-1',
|
|
363
|
+
action: 'generate-pr',
|
|
364
|
+
specId: 'SPEC-001',
|
|
365
|
+
});
|
|
366
|
+
expect(result.isError).toBeUndefined();
|
|
367
|
+
const data = JSON.parse(result.content[0].text);
|
|
368
|
+
expect(data.prTemplate).toBeDefined();
|
|
369
|
+
});
|
|
370
|
+
it('should fallback to HEAD~10 when both main and master do not exist', async () => {
|
|
371
|
+
mockGetSpec.mockResolvedValue(baseSpec);
|
|
372
|
+
mockExecFile.mockImplementation((_cmd, args) => {
|
|
373
|
+
const argsArr = args;
|
|
374
|
+
if (argsArr.includes('--abbrev-ref')) {
|
|
375
|
+
return Promise.resolve({ stdout: 'feat/SPEC-001-user-auth\n', stderr: '' });
|
|
376
|
+
}
|
|
377
|
+
if (argsArr.includes('--verify')) {
|
|
378
|
+
return Promise.reject(new Error('not found'));
|
|
379
|
+
}
|
|
380
|
+
if (argsArr.includes('--oneline')) {
|
|
381
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
382
|
+
}
|
|
383
|
+
if (argsArr.includes('--name-only')) {
|
|
384
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
385
|
+
}
|
|
386
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
387
|
+
});
|
|
388
|
+
const result = await handleManageGit({
|
|
389
|
+
projectId: 'proj-1',
|
|
390
|
+
action: 'generate-pr',
|
|
391
|
+
specId: 'SPEC-001',
|
|
392
|
+
});
|
|
393
|
+
expect(result.isError).toBeUndefined();
|
|
394
|
+
const data = JSON.parse(result.content[0].text);
|
|
395
|
+
expect(data.prTemplate.body).toContain('no changes detected');
|
|
396
|
+
});
|
|
397
|
+
// --- Coverage for generate-pr commit log failure ---
|
|
398
|
+
it('should handle commit log failure in generate-pr', async () => {
|
|
399
|
+
mockGetSpec.mockResolvedValue(baseSpec);
|
|
400
|
+
mockExecFile.mockImplementation((_cmd, args) => {
|
|
401
|
+
const argsArr = args;
|
|
402
|
+
if (argsArr.includes('--abbrev-ref')) {
|
|
403
|
+
return Promise.resolve({ stdout: 'feat/SPEC-001-user-auth\n', stderr: '' });
|
|
404
|
+
}
|
|
405
|
+
if (argsArr.includes('--verify')) {
|
|
406
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
407
|
+
}
|
|
408
|
+
if (argsArr.includes('--oneline')) {
|
|
409
|
+
return Promise.reject(new Error('log failed'));
|
|
410
|
+
}
|
|
411
|
+
if (argsArr.includes('--name-only')) {
|
|
412
|
+
return Promise.reject(new Error('diff failed'));
|
|
413
|
+
}
|
|
414
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
415
|
+
});
|
|
416
|
+
const result = await handleManageGit({
|
|
417
|
+
projectId: 'proj-1',
|
|
418
|
+
action: 'generate-pr',
|
|
419
|
+
specId: 'SPEC-001',
|
|
420
|
+
});
|
|
421
|
+
expect(result.isError).toBeUndefined();
|
|
422
|
+
const data = JSON.parse(result.content[0].text);
|
|
423
|
+
expect(data.prTemplate.body).toContain('unable to determine');
|
|
424
|
+
});
|
|
425
|
+
// --- Coverage for generate-pr with high/critical risk labels ---
|
|
426
|
+
it('should add risk and scope labels for high-risk architectural spec', async () => {
|
|
427
|
+
mockGetSpec.mockResolvedValue({
|
|
428
|
+
...baseSpec,
|
|
429
|
+
risk: 'critical',
|
|
430
|
+
scope: 'architectural',
|
|
431
|
+
impactAnalysis: { requiresMigration: true, requiresFeatureFlag: true },
|
|
432
|
+
});
|
|
433
|
+
mockExecFile.mockImplementation((_cmd, args) => {
|
|
434
|
+
const argsArr = args;
|
|
435
|
+
if (argsArr.includes('--abbrev-ref')) {
|
|
436
|
+
return Promise.resolve({ stdout: 'feat/SPEC-001-auth\n', stderr: '' });
|
|
437
|
+
}
|
|
438
|
+
if (argsArr.includes('--verify')) {
|
|
439
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
440
|
+
}
|
|
441
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
442
|
+
});
|
|
443
|
+
const result = await handleManageGit({
|
|
444
|
+
projectId: 'proj-1',
|
|
445
|
+
action: 'generate-pr',
|
|
446
|
+
specId: 'SPEC-001',
|
|
447
|
+
});
|
|
448
|
+
const data = JSON.parse(result.content[0].text);
|
|
449
|
+
expect(data.prTemplate.labels).toContain('risk:critical');
|
|
450
|
+
expect(data.prTemplate.labels).toContain('scope:architectural');
|
|
451
|
+
expect(data.prTemplate.body).toContain('Migration tested');
|
|
452
|
+
expect(data.prTemplate.body).toContain('Feature flag configured');
|
|
453
|
+
});
|
|
454
|
+
// --- Coverage for status: master fallback (lines 731-735) ---
|
|
455
|
+
it('should fallback to master in status when main is not found', async () => {
|
|
456
|
+
mockListSpecs.mockResolvedValue([]);
|
|
457
|
+
mockExecFile.mockImplementation((_cmd, args) => {
|
|
458
|
+
const argsArr = args;
|
|
459
|
+
if (argsArr.includes('--abbrev-ref')) {
|
|
460
|
+
return Promise.resolve({ stdout: 'feat/SPEC-001-auth\n', stderr: '' });
|
|
461
|
+
}
|
|
462
|
+
if (argsArr.includes('--porcelain')) {
|
|
463
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
464
|
+
}
|
|
465
|
+
if (argsArr.includes('--verify') && argsArr.includes('main')) {
|
|
466
|
+
return Promise.reject(new Error('not found'));
|
|
467
|
+
}
|
|
468
|
+
if (argsArr.includes('--verify') && argsArr.includes('master')) {
|
|
469
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
470
|
+
}
|
|
471
|
+
if (argsArr.includes('--count')) {
|
|
472
|
+
return Promise.resolve({ stdout: '5\n', stderr: '' });
|
|
473
|
+
}
|
|
474
|
+
if (argsArr.includes('--format=%s')) {
|
|
475
|
+
return Promise.resolve({ stdout: '[SPEC-001] feat\n', stderr: '' });
|
|
476
|
+
}
|
|
477
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
478
|
+
});
|
|
479
|
+
const result = await handleManageGit({
|
|
480
|
+
projectId: 'proj-1',
|
|
481
|
+
action: 'status',
|
|
482
|
+
});
|
|
483
|
+
const data = JSON.parse(result.content[0].text);
|
|
484
|
+
expect(data.status.commitsAhead).toBe(5);
|
|
485
|
+
});
|
|
486
|
+
it('should handle status when both main and master do not exist', async () => {
|
|
487
|
+
mockListSpecs.mockResolvedValue([]);
|
|
488
|
+
mockExecFile.mockImplementation((_cmd, args) => {
|
|
489
|
+
const argsArr = args;
|
|
490
|
+
if (argsArr.includes('--abbrev-ref')) {
|
|
491
|
+
return Promise.resolve({ stdout: 'feat/SPEC-001-auth\n', stderr: '' });
|
|
492
|
+
}
|
|
493
|
+
if (argsArr.includes('--porcelain')) {
|
|
494
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
495
|
+
}
|
|
496
|
+
if (argsArr.includes('--verify')) {
|
|
497
|
+
return Promise.reject(new Error('not found'));
|
|
498
|
+
}
|
|
499
|
+
if (argsArr.includes('--format=%s')) {
|
|
500
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
501
|
+
}
|
|
502
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
503
|
+
});
|
|
504
|
+
const result = await handleManageGit({
|
|
505
|
+
projectId: 'proj-1',
|
|
506
|
+
action: 'status',
|
|
507
|
+
});
|
|
508
|
+
const data = JSON.parse(result.content[0].text);
|
|
509
|
+
// baseBranch is '', so commitsAhead remains 0
|
|
510
|
+
expect(data.status.commitsAhead).toBe(0);
|
|
511
|
+
});
|
|
512
|
+
// --- Coverage for create-branch: existing branch checkout ---
|
|
513
|
+
it('should switch to existing branch when it already exists', async () => {
|
|
514
|
+
mockGetSpec.mockResolvedValue(baseSpec);
|
|
515
|
+
mockExecFile.mockImplementation((_cmd, args) => {
|
|
516
|
+
const argsArr = args;
|
|
517
|
+
if (argsArr.includes('--list')) {
|
|
518
|
+
return Promise.resolve({ stdout: ' feat/SPEC-001-user-auth\n', stderr: '' });
|
|
519
|
+
}
|
|
520
|
+
if (argsArr.includes('checkout')) {
|
|
521
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
522
|
+
}
|
|
523
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
524
|
+
});
|
|
525
|
+
const result = await handleManageGit({
|
|
526
|
+
projectId: 'proj-1',
|
|
527
|
+
action: 'create-branch',
|
|
528
|
+
specId: 'SPEC-001',
|
|
529
|
+
});
|
|
530
|
+
const data = JSON.parse(result.content[0].text);
|
|
531
|
+
expect(data.message).toContain('Switched to existing branch');
|
|
532
|
+
});
|
|
533
|
+
// --- Coverage for create-branch: branch list failure ---
|
|
534
|
+
it('should create branch when branch list command fails', async () => {
|
|
535
|
+
mockGetSpec.mockResolvedValue(baseSpec);
|
|
536
|
+
let callIdx = 0;
|
|
537
|
+
mockExecFile.mockImplementation(() => {
|
|
538
|
+
callIdx++;
|
|
539
|
+
if (callIdx === 1) {
|
|
540
|
+
// branch --list fails
|
|
541
|
+
return Promise.reject(new Error('git error'));
|
|
542
|
+
}
|
|
543
|
+
// checkout -b succeeds
|
|
544
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
545
|
+
});
|
|
546
|
+
const result = await handleManageGit({
|
|
547
|
+
projectId: 'proj-1',
|
|
548
|
+
action: 'create-branch',
|
|
549
|
+
specId: 'SPEC-001',
|
|
550
|
+
});
|
|
551
|
+
const data = JSON.parse(result.content[0].text);
|
|
552
|
+
expect(data.action).toBe('create-branch');
|
|
553
|
+
expect(data.branchName).toContain('SPEC-001');
|
|
554
|
+
});
|
|
555
|
+
// --- Coverage for setup-hooks: spec-id commit format ---
|
|
556
|
+
it('should setup hooks with spec-id commit format', async () => {
|
|
557
|
+
mockExecFile.mockImplementation((_cmd, args) => {
|
|
558
|
+
const argsArr = args;
|
|
559
|
+
if (argsArr.includes('--git-dir')) {
|
|
560
|
+
return Promise.resolve({ stdout: '.git\n', stderr: '' });
|
|
561
|
+
}
|
|
562
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
563
|
+
});
|
|
564
|
+
const result = await handleManageGit({
|
|
565
|
+
projectId: 'proj-1',
|
|
566
|
+
action: 'setup-hooks',
|
|
567
|
+
config: {
|
|
568
|
+
commitFormat: 'spec-id',
|
|
569
|
+
requireSpecInCommit: false,
|
|
570
|
+
},
|
|
571
|
+
});
|
|
572
|
+
const data = JSON.parse(result.content[0].text);
|
|
573
|
+
expect(data.hooksInstalled).toContain('commit-msg');
|
|
574
|
+
});
|
|
575
|
+
// --- Coverage for setup-hooks: custom commit format ---
|
|
576
|
+
it('should setup hooks with custom commit format', async () => {
|
|
577
|
+
mockExecFile.mockImplementation((_cmd, args) => {
|
|
578
|
+
const argsArr = args;
|
|
579
|
+
if (argsArr.includes('--git-dir')) {
|
|
580
|
+
return Promise.resolve({ stdout: '.git\n', stderr: '' });
|
|
581
|
+
}
|
|
582
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
583
|
+
});
|
|
584
|
+
const result = await handleManageGit({
|
|
585
|
+
projectId: 'proj-1',
|
|
586
|
+
action: 'setup-hooks',
|
|
587
|
+
config: {
|
|
588
|
+
commitFormat: 'custom',
|
|
589
|
+
},
|
|
590
|
+
});
|
|
591
|
+
const data = JSON.parse(result.content[0].text);
|
|
592
|
+
expect(data.hooksInstalled).toContain('commit-msg');
|
|
593
|
+
});
|
|
594
|
+
// --- Coverage for setup-hooks: git-dir fallback ---
|
|
595
|
+
it('should fallback to .git/hooks when git rev-parse fails', async () => {
|
|
596
|
+
mockExecFile.mockImplementation((_cmd, args) => {
|
|
597
|
+
const argsArr = args;
|
|
598
|
+
if (argsArr.includes('--git-dir')) {
|
|
599
|
+
return Promise.reject(new Error('not a git repo'));
|
|
600
|
+
}
|
|
601
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
602
|
+
});
|
|
603
|
+
const result = await handleManageGit({
|
|
604
|
+
projectId: 'proj-1',
|
|
605
|
+
action: 'setup-hooks',
|
|
606
|
+
});
|
|
607
|
+
const data = JSON.parse(result.content[0].text);
|
|
608
|
+
expect(data.hooksInstalled).toContain('pre-commit');
|
|
609
|
+
});
|
|
610
|
+
// --- Coverage for check-branch: protected branch ---
|
|
611
|
+
it('should detect protected branch issues', async () => {
|
|
612
|
+
mockExecFile.mockResolvedValue({ stdout: 'main\n', stderr: '' });
|
|
613
|
+
const result = await handleManageGit({
|
|
614
|
+
projectId: 'proj-1',
|
|
615
|
+
action: 'check-branch',
|
|
616
|
+
});
|
|
617
|
+
const data = JSON.parse(result.content[0].text);
|
|
618
|
+
expect(data.isProtected).toBe(true);
|
|
619
|
+
expect(data.issues.length).toBeGreaterThan(0);
|
|
620
|
+
expect(data.issues[0]).toContain('protected branch');
|
|
621
|
+
});
|
|
622
|
+
// --- Coverage for check-branch: non-convention branch that is not protected ---
|
|
623
|
+
it('should detect non-convention branch name', async () => {
|
|
624
|
+
mockExecFile.mockResolvedValue({ stdout: 'my-random-branch\n', stderr: '' });
|
|
625
|
+
const result = await handleManageGit({
|
|
626
|
+
projectId: 'proj-1',
|
|
627
|
+
action: 'check-branch',
|
|
628
|
+
});
|
|
629
|
+
const data = JSON.parse(result.content[0].text);
|
|
630
|
+
expect(data.followsConvention).toBe(false);
|
|
631
|
+
expect(data.isProtected).toBe(false);
|
|
632
|
+
expect(data.issues.some((i) => i.includes('naming convention'))).toBe(true);
|
|
633
|
+
});
|
|
634
|
+
// --- Coverage for check-branch: spec referenced in branch but not found ---
|
|
635
|
+
it('should warn when spec referenced in branch name is not found', async () => {
|
|
636
|
+
mockGetSpec.mockResolvedValue(null);
|
|
637
|
+
mockExecFile.mockResolvedValue({ stdout: 'feat/SPEC-999-missing\n', stderr: '' });
|
|
638
|
+
const result = await handleManageGit({
|
|
639
|
+
projectId: 'proj-1',
|
|
640
|
+
action: 'check-branch',
|
|
641
|
+
});
|
|
642
|
+
const data = JSON.parse(result.content[0].text);
|
|
643
|
+
expect(data.linkedSpecId).toBe('SPEC-999');
|
|
644
|
+
expect(data.specExists).toBe(false);
|
|
645
|
+
expect(data.issues.some((i) => i.includes('not found in project'))).toBe(true);
|
|
646
|
+
});
|
|
647
|
+
// --- Coverage for generate-changelog: filtered by specId ---
|
|
648
|
+
it('should generate changelog filtered by specific specId', async () => {
|
|
649
|
+
mockListSpecs.mockResolvedValue([
|
|
650
|
+
{ ...baseSpec, id: 'SPEC-001', status: 'done' },
|
|
651
|
+
{ ...baseSpec, id: 'SPEC-002', status: 'done', title: 'Other Feature' },
|
|
652
|
+
]);
|
|
653
|
+
mockExecFile.mockResolvedValue({ stdout: '[SPEC-001] feat: auth\nabc1234 misc change\n', stderr: '' });
|
|
654
|
+
const result = await handleManageGit({
|
|
655
|
+
projectId: 'proj-1',
|
|
656
|
+
action: 'generate-changelog',
|
|
657
|
+
specId: 'SPEC-001',
|
|
658
|
+
});
|
|
659
|
+
const data = JSON.parse(result.content[0].text);
|
|
660
|
+
expect(data.changelog.content).toContain('SPEC-001');
|
|
661
|
+
});
|
|
662
|
+
// --- Coverage for generate-changelog: git log failure ---
|
|
663
|
+
it('should generate changelog even when git log fails', async () => {
|
|
664
|
+
mockListSpecs.mockResolvedValue([
|
|
665
|
+
{ ...baseSpec, status: 'done' },
|
|
666
|
+
]);
|
|
667
|
+
mockExecFile.mockRejectedValue(new Error('git log failed'));
|
|
668
|
+
const result = await handleManageGit({
|
|
669
|
+
projectId: 'proj-1',
|
|
670
|
+
action: 'generate-changelog',
|
|
671
|
+
});
|
|
672
|
+
const data = JSON.parse(result.content[0].text);
|
|
673
|
+
expect(data.changelog).toBeDefined();
|
|
674
|
+
});
|
|
675
|
+
// --- Coverage for non-Error thrown in catch block ---
|
|
676
|
+
it('should handle non-Error thrown values', async () => {
|
|
677
|
+
mockGetKnowledge.mockRejectedValue('string error');
|
|
678
|
+
const result = await handleManageGit({
|
|
679
|
+
projectId: 'proj-1',
|
|
680
|
+
action: 'status',
|
|
681
|
+
});
|
|
682
|
+
expect(result.isError).toBe(true);
|
|
683
|
+
expect(result.content[0].text).toContain('string error');
|
|
684
|
+
});
|
|
685
|
+
// --- Coverage for generate-pr spec not found ---
|
|
686
|
+
it('should error when spec not found for generate-pr', async () => {
|
|
687
|
+
mockGetSpec.mockResolvedValue(null);
|
|
688
|
+
const result = await handleManageGit({
|
|
689
|
+
projectId: 'proj-1',
|
|
690
|
+
action: 'generate-pr',
|
|
691
|
+
specId: 'SPEC-999',
|
|
692
|
+
});
|
|
693
|
+
expect(result.isError).toBe(true);
|
|
694
|
+
});
|
|
695
|
+
// --- Coverage for project not found ---
|
|
696
|
+
it('should error when project not found', async () => {
|
|
697
|
+
mockGetKnowledge.mockResolvedValue(null);
|
|
698
|
+
const result = await handleManageGit({
|
|
699
|
+
projectId: 'missing',
|
|
700
|
+
action: 'status',
|
|
701
|
+
});
|
|
702
|
+
expect(result.isError).toBe(true);
|
|
703
|
+
expect(result.content[0].text).toContain('Project not found');
|
|
704
|
+
});
|
|
705
|
+
// --- Coverage for status: on same branch as base ---
|
|
706
|
+
it('should not count commits ahead when on base branch', async () => {
|
|
707
|
+
mockListSpecs.mockResolvedValue([]);
|
|
708
|
+
mockExecFile.mockImplementation((_cmd, args) => {
|
|
709
|
+
const argsArr = args;
|
|
710
|
+
if (argsArr.includes('--abbrev-ref')) {
|
|
711
|
+
return Promise.resolve({ stdout: 'main\n', stderr: '' });
|
|
712
|
+
}
|
|
713
|
+
if (argsArr.includes('--porcelain')) {
|
|
714
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
715
|
+
}
|
|
716
|
+
if (argsArr.includes('--verify')) {
|
|
717
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
718
|
+
}
|
|
719
|
+
if (argsArr.includes('--format=%s')) {
|
|
720
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
721
|
+
}
|
|
722
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
723
|
+
});
|
|
724
|
+
const result = await handleManageGit({
|
|
725
|
+
projectId: 'proj-1',
|
|
726
|
+
action: 'status',
|
|
727
|
+
});
|
|
728
|
+
const data = JSON.parse(result.content[0].text);
|
|
729
|
+
expect(data.status.commitsAhead).toBe(0);
|
|
730
|
+
});
|
|
731
|
+
// --- Coverage for status: rev-list count failure ---
|
|
732
|
+
it('should handle rev-list count failure in status', async () => {
|
|
733
|
+
mockListSpecs.mockResolvedValue([]);
|
|
734
|
+
mockExecFile.mockImplementation((_cmd, args) => {
|
|
735
|
+
const argsArr = args;
|
|
736
|
+
if (argsArr.includes('--abbrev-ref')) {
|
|
737
|
+
return Promise.resolve({ stdout: 'feat/SPEC-001\n', stderr: '' });
|
|
738
|
+
}
|
|
739
|
+
if (argsArr.includes('--porcelain')) {
|
|
740
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
741
|
+
}
|
|
742
|
+
if (argsArr.includes('--verify')) {
|
|
743
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
744
|
+
}
|
|
745
|
+
if (argsArr.includes('--count')) {
|
|
746
|
+
return Promise.reject(new Error('rev-list failed'));
|
|
747
|
+
}
|
|
748
|
+
if (argsArr.includes('--format=%s')) {
|
|
749
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
750
|
+
}
|
|
751
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
752
|
+
});
|
|
753
|
+
const result = await handleManageGit({
|
|
754
|
+
projectId: 'proj-1',
|
|
755
|
+
action: 'status',
|
|
756
|
+
});
|
|
757
|
+
const data = JSON.parse(result.content[0].text);
|
|
758
|
+
expect(data.status.commitsAhead).toBe(0);
|
|
759
|
+
});
|
|
760
|
+
// --- Coverage for link-commits: commit without SPEC pattern in message uses specId ---
|
|
761
|
+
it('should use specId as fallback when commit message has no SPEC pattern', async () => {
|
|
762
|
+
mockExecFile.mockImplementation((_cmd, args) => {
|
|
763
|
+
const argsArr = args;
|
|
764
|
+
if (argsArr.includes('--grep=SPEC-001')) {
|
|
765
|
+
return Promise.resolve({
|
|
766
|
+
stdout: 'abc12345|feat: auth without spec ref|dev|2025-01-01T00:00:00Z\n',
|
|
767
|
+
stderr: '',
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
if (argsArr.includes('diff-tree')) {
|
|
771
|
+
return Promise.resolve({ stdout: 'src/auth.ts\n', stderr: '' });
|
|
772
|
+
}
|
|
773
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
774
|
+
});
|
|
775
|
+
const result = await handleManageGit({
|
|
776
|
+
projectId: 'proj-1',
|
|
777
|
+
action: 'link-commits',
|
|
778
|
+
specId: 'SPEC-001',
|
|
779
|
+
});
|
|
780
|
+
const data = JSON.parse(result.content[0].text);
|
|
781
|
+
expect(data.linkedCommits).toHaveLength(1);
|
|
782
|
+
expect(data.linkedCommits[0].specId).toBe('SPEC-001');
|
|
783
|
+
});
|
|
784
|
+
// --- Coverage for create-branch with different spec types ---
|
|
785
|
+
it('should use refactor prefix for refactor spec type', async () => {
|
|
786
|
+
mockGetSpec.mockResolvedValue({
|
|
787
|
+
...baseSpec,
|
|
788
|
+
type: 'refactor',
|
|
789
|
+
});
|
|
790
|
+
mockExecFile.mockResolvedValue({ stdout: '', stderr: '' });
|
|
791
|
+
const result = await handleManageGit({
|
|
792
|
+
projectId: 'proj-1',
|
|
793
|
+
action: 'create-branch',
|
|
794
|
+
specId: 'SPEC-001',
|
|
795
|
+
});
|
|
796
|
+
const data = JSON.parse(result.content[0].text);
|
|
797
|
+
expect(data.branchName).toContain('refactor/');
|
|
798
|
+
});
|
|
799
|
+
it('should use fix prefix for bugfix spec type', async () => {
|
|
800
|
+
mockGetSpec.mockResolvedValue({
|
|
801
|
+
...baseSpec,
|
|
802
|
+
type: 'bugfix',
|
|
803
|
+
});
|
|
804
|
+
mockExecFile.mockResolvedValue({ stdout: '', stderr: '' });
|
|
805
|
+
const result = await handleManageGit({
|
|
806
|
+
projectId: 'proj-1',
|
|
807
|
+
action: 'create-branch',
|
|
808
|
+
specId: 'SPEC-001',
|
|
809
|
+
});
|
|
810
|
+
const data = JSON.parse(result.content[0].text);
|
|
811
|
+
expect(data.branchName).toContain('fix/');
|
|
812
|
+
});
|
|
813
|
+
// --- Coverage for setup-hooks with requireSpecInCommit true (explicit) ---
|
|
814
|
+
it('should setup hooks with requireSpecInCommit enabled and custom protectedBranches', async () => {
|
|
815
|
+
mockExecFile.mockImplementation((_cmd, args) => {
|
|
816
|
+
const argsArr = args;
|
|
817
|
+
if (argsArr.includes('--git-dir')) {
|
|
818
|
+
return Promise.resolve({ stdout: '.git\n', stderr: '' });
|
|
819
|
+
}
|
|
820
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
821
|
+
});
|
|
822
|
+
const result = await handleManageGit({
|
|
823
|
+
projectId: 'proj-1',
|
|
824
|
+
action: 'setup-hooks',
|
|
825
|
+
config: {
|
|
826
|
+
requireSpecInCommit: true,
|
|
827
|
+
protectedBranches: ['main', 'staging'],
|
|
828
|
+
},
|
|
829
|
+
});
|
|
830
|
+
const data = JSON.parse(result.content[0].text);
|
|
831
|
+
expect(data.hooksInstalled).toContain('pre-commit');
|
|
832
|
+
expect(data.hooksInstalled).toContain('commit-msg');
|
|
833
|
+
expect(data.hooksInstalled).toContain('pre-push');
|
|
834
|
+
});
|
|
835
|
+
// --- Coverage for check-branch with custom config ---
|
|
836
|
+
it('should check branch with custom protectedBranches config', async () => {
|
|
837
|
+
mockGetSpec.mockResolvedValue(baseSpec);
|
|
838
|
+
mockExecFile.mockResolvedValue({ stdout: 'staging\n', stderr: '' });
|
|
839
|
+
const result = await handleManageGit({
|
|
840
|
+
projectId: 'proj-1',
|
|
841
|
+
action: 'check-branch',
|
|
842
|
+
config: {
|
|
843
|
+
protectedBranches: ['main', 'staging'],
|
|
844
|
+
},
|
|
845
|
+
});
|
|
846
|
+
const data = JSON.parse(result.content[0].text);
|
|
847
|
+
expect(data.action).toBe('check-branch');
|
|
848
|
+
expect(data.isProtected).toBe(true);
|
|
849
|
+
expect(data.branchName).toBe('staging');
|
|
850
|
+
});
|
|
851
|
+
// --- Coverage for status: rev-list returns non-numeric string (line 742: parseInt || 0) ---
|
|
852
|
+
it('should default commitsAhead to 0 when rev-list returns non-numeric output', async () => {
|
|
853
|
+
mockListSpecs.mockResolvedValue([]);
|
|
854
|
+
mockExecFile.mockImplementation((_cmd, args) => {
|
|
855
|
+
const argsArr = args;
|
|
856
|
+
if (argsArr.includes('--abbrev-ref')) {
|
|
857
|
+
return Promise.resolve({ stdout: 'feat/SPEC-001-auth\n', stderr: '' });
|
|
858
|
+
}
|
|
859
|
+
if (argsArr.includes('--porcelain')) {
|
|
860
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
861
|
+
}
|
|
862
|
+
if (argsArr.includes('--verify')) {
|
|
863
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
864
|
+
}
|
|
865
|
+
if (argsArr.includes('--count')) {
|
|
866
|
+
// Return non-numeric string so parseInt returns NaN, triggering || 0
|
|
867
|
+
return Promise.resolve({ stdout: 'not-a-number\n', stderr: '' });
|
|
868
|
+
}
|
|
869
|
+
if (argsArr.includes('--format=%s')) {
|
|
870
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
871
|
+
}
|
|
872
|
+
return Promise.resolve({ stdout: '', stderr: '' });
|
|
873
|
+
});
|
|
874
|
+
const result = await handleManageGit({
|
|
875
|
+
projectId: 'proj-1',
|
|
876
|
+
action: 'status',
|
|
877
|
+
});
|
|
878
|
+
const data = JSON.parse(result.content[0].text);
|
|
879
|
+
expect(data.status.commitsAhead).toBe(0);
|
|
880
|
+
});
|
|
881
|
+
});
|
|
882
|
+
//# sourceMappingURL=manage-git.test.js.map
|