@ngxtm/devkit 2.1.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -197
- package/cli/detect.js +292 -0
- package/cli/index.js +204 -92
- package/cli/init.js +245 -0
- package/cli/update.js +243 -0
- package/cli/utils.js +195 -0
- package/hooks/lib/__tests__/ck-config-utils.test.cjs +10 -0
- package/hooks/lib/__tests__/statusline-integration.test.cjs +46 -75
- package/hooks/scout-block/tests/test-monorepo-scenarios.cjs +2 -2
- package/hooks/tests/test-ckignore.cjs +7 -2
- package/package.json +16 -20
- package/rules-index.json +76 -0
- package/scripts/generate-index.js +223 -0
- package/scripts/merge-commands.js +290 -0
- package/scripts/organize-rules.js +226 -0
- package/templates/base/hooks/lib/ck-config-utils.cjs +769 -0
- package/templates/base/hooks/lib/ck-paths.cjs +110 -0
- package/templates/base/hooks/lib/colors.cjs +95 -0
- package/templates/base/hooks/lib/config-counter.cjs +103 -0
- package/templates/base/hooks/lib/context-builder.cjs +600 -0
- package/templates/base/hooks/lib/context-tracker.cjs +335 -0
- package/templates/base/hooks/lib/privacy-checker.cjs +297 -0
- package/templates/base/hooks/lib/project-detector.cjs +430 -0
- package/templates/base/hooks/lib/scout-checker.cjs +172 -0
- package/templates/base/hooks/lib/transcript-parser.cjs +164 -0
- package/templates/base/hooks/privacy-block.cjs +145 -0
- package/agents/backend-engineer.md +0 -154
- package/agents/brainstormer.md +0 -169
- package/agents/business-analyst.md +0 -166
- package/agents/database-architect.md +0 -159
- package/agents/debugger.md +0 -155
- package/agents/designer.md +0 -150
- package/agents/devops-engineer.md +0 -155
- package/agents/docs-manager.md +0 -171
- package/agents/frontend-engineer.md +0 -159
- package/agents/game-engineer.md +0 -148
- package/agents/mobile-engineer.md +0 -149
- package/agents/performance-engineer.md +0 -152
- package/agents/planner.md +0 -161
- package/agents/project-manager.md +0 -160
- package/agents/researcher.md +0 -146
- package/agents/reviewer.md +0 -155
- package/agents/scouter.md +0 -157
- package/agents/security-engineer.md +0 -154
- package/agents/tech-lead.md +0 -159
- package/agents/tester.md +0 -157
- package/agents-claudekit/brainstormer.md +0 -113
- package/agents-claudekit/code-reviewer.md +0 -157
- package/agents-claudekit/code-simplifier.md +0 -42
- package/agents-claudekit/copywriter.md +0 -110
- package/agents-claudekit/database-admin.md +0 -92
- package/agents-claudekit/debugger.md +0 -137
- package/agents-claudekit/docs-manager.md +0 -208
- package/agents-claudekit/fullstack-developer.md +0 -95
- package/agents-claudekit/git-manager.md +0 -394
- package/agents-claudekit/journal-writer.md +0 -113
- package/agents-claudekit/mcp-manager.md +0 -93
- package/agents-claudekit/planner.md +0 -108
- package/agents-claudekit/project-manager.md +0 -125
- package/agents-claudekit/researcher.md +0 -38
- package/agents-claudekit/scout-external.md +0 -141
- package/agents-claudekit/scout.md +0 -107
- package/agents-claudekit/tester.md +0 -105
- package/agents-claudekit/ui-ux-designer.md +0 -236
- package/commands/ask.md +0 -64
- package/commands/brainstorm.md +0 -64
- package/commands/code.md +0 -64
- package/commands/cook.md +0 -64
- package/commands/debug.md +0 -64
- package/commands/design/fast.md +0 -134
- package/commands/fix/fast.md +0 -84
- package/commands/fix/hard.md +0 -116
- package/commands/fix.md +0 -64
- package/commands/plan/fast.md +0 -78
- package/commands/plan/hard.md +0 -131
- package/commands/plan.md +0 -64
- package/commands/test.md +0 -64
- package/hooks/tests/test-modularization-hook.cjs +0 -126
- package/matrix-skills/_index.yaml +0 -275
- package/matrix-skills/ai-ml.yaml +0 -353
- package/matrix-skills/architecture.yaml +0 -93
- package/matrix-skills/backend.yaml +0 -280
- package/matrix-skills/cloud.yaml +0 -112
- package/matrix-skills/data.yaml +0 -74
- package/matrix-skills/design.yaml +0 -98
- package/matrix-skills/devops.yaml +0 -200
- package/matrix-skills/frontend.yaml +0 -200
- package/matrix-skills/gaming.yaml +0 -39
- package/matrix-skills/languages.yaml +0 -160
- package/matrix-skills/management.yaml +0 -50
- package/matrix-skills/mcp.yaml +0 -82
- package/matrix-skills/mobile.yaml +0 -85
- package/matrix-skills/performance.yaml +0 -23
- package/matrix-skills/planning.yaml +0 -117
- package/matrix-skills/quality.yaml +0 -195
- package/matrix-skills/research.yaml +0 -106
- package/matrix-skills/security.yaml +0 -293
- package/matrix-skills/tools.yaml +0 -352
- package/output-styles/coding-level-0-eli5.md +0 -103
- package/output-styles/coding-level-1-junior.md +0 -124
- package/output-styles/coding-level-2-mid.md +0 -146
- package/output-styles/coding-level-3-senior.md +0 -148
- package/output-styles/coding-level-4-lead.md +0 -159
- package/output-styles/coding-level-5-god.md +0 -91
- package/rules/README.md +0 -141
- package/rules/metadata.json +0 -54
- package/settings.json +0 -3
- package/statusline.cjs +0 -500
- package/statusline.ps1 +0 -307
- package/statusline.sh +0 -237
- package/workflows/development-rules.md +0 -42
- package/workflows/documentation-management.md +0 -121
- package/workflows/orchestration-protocol.md +0 -16
- package/workflows/primary-workflow.md +0 -45
- /package/{commands → merged-commands}/ask/fast.md +0 -0
- /package/{commands → merged-commands}/ask/hard.md +0 -0
- /package/{commands-claudekit → merged-commands}/ask.md +0 -0
- /package/{commands → merged-commands}/auto.md +0 -0
- /package/{commands-claudekit → merged-commands}/bootstrap/auto/fast.md +0 -0
- /package/{commands-claudekit → merged-commands}/bootstrap/auto/parallel.md +0 -0
- /package/{commands-claudekit → merged-commands}/bootstrap/auto.md +0 -0
- /package/{commands-claudekit → merged-commands}/bootstrap.md +0 -0
- /package/{commands → merged-commands}/brainstorm/fast.md +0 -0
- /package/{commands → merged-commands}/brainstorm/hard.md +0 -0
- /package/{commands-claudekit → merged-commands}/brainstorm.md +0 -0
- /package/{commands-claudekit → merged-commands}/ck-help.md +0 -0
- /package/{commands-claudekit → merged-commands}/code/auto.md +0 -0
- /package/{commands → merged-commands}/code/fast.md +0 -0
- /package/{commands → merged-commands}/code/hard.md +0 -0
- /package/{commands-claudekit → merged-commands}/code/no-test.md +0 -0
- /package/{commands-claudekit → merged-commands}/code/parallel.md +0 -0
- /package/{commands-claudekit → merged-commands}/code.md +0 -0
- /package/{commands-claudekit → merged-commands}/coding-level.md +0 -0
- /package/{commands-claudekit → merged-commands}/content/cro.md +0 -0
- /package/{commands-claudekit → merged-commands}/content/enhance.md +0 -0
- /package/{commands-claudekit → merged-commands}/content/fast.md +0 -0
- /package/{commands-claudekit → merged-commands}/content/good.md +0 -0
- /package/{commands-claudekit → merged-commands}/cook/auto/fast.md +0 -0
- /package/{commands-claudekit → merged-commands}/cook/auto/parallel.md +0 -0
- /package/{commands-claudekit → merged-commands}/cook/auto.md +0 -0
- /package/{commands → merged-commands}/cook/fast.md +0 -0
- /package/{commands → merged-commands}/cook/hard.md +0 -0
- /package/{commands-claudekit → merged-commands}/cook.md +0 -0
- /package/{commands → merged-commands}/debug/fast.md +0 -0
- /package/{commands → merged-commands}/debug/hard.md +0 -0
- /package/{commands-claudekit → merged-commands}/debug.md +0 -0
- /package/{commands → merged-commands}/deploy/check.md +0 -0
- /package/{commands → merged-commands}/deploy/preview.md +0 -0
- /package/{commands → merged-commands}/deploy/production.md +0 -0
- /package/{commands → merged-commands}/deploy/rollback.md +0 -0
- /package/{commands → merged-commands}/deploy.md +0 -0
- /package/{commands-claudekit → merged-commands}/design/3d.md +0 -0
- /package/{commands-claudekit → merged-commands}/design/describe.md +0 -0
- /package/{commands-claudekit → merged-commands}/design/fast.md +0 -0
- /package/{commands-claudekit → merged-commands}/design/good.md +0 -0
- /package/{commands → merged-commands}/design/hard.md +0 -0
- /package/{commands-claudekit → merged-commands}/design/screenshot.md +0 -0
- /package/{commands-claudekit → merged-commands}/design/video.md +0 -0
- /package/{commands → merged-commands}/design.md +0 -0
- /package/{commands → merged-commands}/docs/audit.md +0 -0
- /package/{commands → merged-commands}/docs/business.md +0 -0
- /package/{commands → merged-commands}/docs/core.md +0 -0
- /package/{commands-claudekit → merged-commands}/docs/init.md +0 -0
- /package/{commands-claudekit → merged-commands}/docs/summarize.md +0 -0
- /package/{commands-claudekit → merged-commands}/docs/update.md +0 -0
- /package/{commands → merged-commands}/docs.md +0 -0
- /package/{commands-claudekit → merged-commands}/fix/ci.md +0 -0
- /package/{commands-claudekit → merged-commands}/fix/fast.md +0 -0
- /package/{commands-claudekit → merged-commands}/fix/hard.md +0 -0
- /package/{commands-claudekit → merged-commands}/fix/logs.md +0 -0
- /package/{commands-claudekit → merged-commands}/fix/parallel.md +0 -0
- /package/{commands-claudekit → merged-commands}/fix/test.md +0 -0
- /package/{commands-claudekit → merged-commands}/fix/types.md +0 -0
- /package/{commands-claudekit → merged-commands}/fix/ui.md +0 -0
- /package/{commands-claudekit → merged-commands}/fix.md +0 -0
- /package/{commands-claudekit → merged-commands}/git/cm.md +0 -0
- /package/{commands-claudekit → merged-commands}/git/cp.md +0 -0
- /package/{commands-claudekit → merged-commands}/git/merge.md +0 -0
- /package/{commands-claudekit → merged-commands}/git/pr.md +0 -0
- /package/{commands-claudekit → merged-commands}/integrate/polar.md +0 -0
- /package/{commands-claudekit → merged-commands}/integrate/sepay.md +0 -0
- /package/{commands-claudekit → merged-commands}/journal.md +0 -0
- /package/{commands-claudekit → merged-commands}/kanban.md +0 -0
- /package/{commands-claudekit → merged-commands}/plan/archive.md +0 -0
- /package/{commands-claudekit → merged-commands}/plan/ci.md +0 -0
- /package/{commands-claudekit → merged-commands}/plan/cro.md +0 -0
- /package/{commands-claudekit → merged-commands}/plan/fast.md +0 -0
- /package/{commands-claudekit → merged-commands}/plan/hard.md +0 -0
- /package/{commands-claudekit → merged-commands}/plan/parallel.md +0 -0
- /package/{commands-claudekit → merged-commands}/plan/two.md +0 -0
- /package/{commands-claudekit → merged-commands}/plan/validate.md +0 -0
- /package/{commands-claudekit → merged-commands}/plan.md +0 -0
- /package/{commands-claudekit → merged-commands}/preview.md +0 -0
- /package/{commands-claudekit → merged-commands}/review/codebase/parallel.md +0 -0
- /package/{commands-claudekit → merged-commands}/review/codebase.md +0 -0
- /package/{commands → merged-commands}/review/fast.md +0 -0
- /package/{commands → merged-commands}/review/hard.md +0 -0
- /package/{commands → merged-commands}/review.md +0 -0
- /package/{commands-claudekit → merged-commands}/scout/ext.md +0 -0
- /package/{commands-claudekit → merged-commands}/scout.md +0 -0
- /package/{commands-claudekit → merged-commands}/skill/add.md +0 -0
- /package/{commands-claudekit → merged-commands}/skill/create.md +0 -0
- /package/{commands-claudekit → merged-commands}/skill/fix-logs.md +0 -0
- /package/{commands-claudekit → merged-commands}/skill/optimize/auto.md +0 -0
- /package/{commands-claudekit → merged-commands}/skill/optimize.md +0 -0
- /package/{commands-claudekit → merged-commands}/skill/plan.md +0 -0
- /package/{commands-claudekit → merged-commands}/skill/update.md +0 -0
- /package/{commands → merged-commands}/test/fast.md +0 -0
- /package/{commands → merged-commands}/test/hard.md +0 -0
- /package/{commands-claudekit → merged-commands}/test/ui.md +0 -0
- /package/{commands-claudekit → merged-commands}/test.md +0 -0
- /package/{commands-claudekit → merged-commands}/use-mcp.md +0 -0
- /package/{commands-claudekit → merged-commands}/watzup.md +0 -0
- /package/{commands-claudekit → merged-commands}/worktree.md +0 -0
- /package/{rules → templates/dart/rules}/dart/best-practices/SKILL.md +0 -0
- /package/{rules → templates/dart/rules}/dart/language/SKILL.md +0 -0
- /package/{rules → templates/dart/rules}/dart/tooling/SKILL.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/auto-route-navigation/SKILL.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/auto-route-navigation/references/REFERENCE.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/auto-route-navigation/references/router-config.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/bloc-state-management/SKILL.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/bloc-state-management/references/REFERENCE.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/bloc-state-management/references/auth-bloc-example.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/bloc-state-management/references/equatable-usage.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/bloc-state-management/references/property-based-state.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/bloc.rule.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/cicd/SKILL.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/cicd/references/advanced-workflow.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/cicd/references/fastlane.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/cicd/references/github-actions.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/dependency-injection/SKILL.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/dependency-injection/references/REFERENCE.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/dependency-injection/references/modules.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/error-handling/SKILL.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/error-handling/references/REFERENCE.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/error-handling/references/error-mapping.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/feature-based-clean-architecture/SKILL.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/feature-based-clean-architecture/references/REFERENCE.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/feature-based-clean-architecture/references/folder-structure.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/getx-navigation/SKILL.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/getx-navigation/references/app-pages.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/getx-navigation/references/middleware-example.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/getx-state-management/SKILL.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/getx-state-management/references/binding-example.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/getx-state-management/references/reactive-vs-simple.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/go-router-navigation/SKILL.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/idiomatic-flutter/SKILL.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/layer-based-clean-architecture/SKILL.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/layer-based-clean-architecture/references/REFERENCE.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/layer-based-clean-architecture/references/repository-mapping.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/localization/SKILL.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/localization/references/REFERENCE.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/localization/references/sheet-loader.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/navigator-v1-navigation/SKILL.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/navigator-v1-navigation/references/on-generate-route.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/performance/SKILL.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/retrofit-networking/SKILL.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/retrofit-networking/references/REFERENCE.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/retrofit-networking/references/token-refresh.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/riverpod-state-management/SKILL.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/riverpod-state-management/references/architecture.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/riverpod-state-management/references/best-practices.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/riverpod-state-management/references/testing.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/riverpod.rule.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/security/SKILL.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/security/references/REFERENCE.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/security/references/network-security.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/testing/SKILL.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/testing/references/REFERENCE.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/testing/references/bloc-testing.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/testing/references/integration-testing.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/testing/references/robot-pattern.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/testing/references/unit-testing.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/testing/references/widget-testing.md +0 -0
- /package/{rules → templates/flutter/rules}/flutter/widgets/SKILL.md +0 -0
- /package/{rules → templates/golang/rules}/golang/chi-router/SKILL.md +0 -0
- /package/{rules → templates/golang/rules}/golang/chi-router/references/REFERENCE.md +0 -0
- /package/{rules → templates/golang/rules}/golang/chi-router/references/routing-patterns.md +0 -0
- /package/{rules → templates/golang/rules}/golang/cobra-cli/SKILL.md +0 -0
- /package/{rules → templates/golang/rules}/golang/cobra-cli/references/REFERENCE.md +0 -0
- /package/{rules → templates/golang/rules}/golang/cobra-cli/references/command-patterns.md +0 -0
- /package/{rules → templates/golang/rules}/golang/core/SKILL.md +0 -0
- /package/{rules → templates/golang/rules}/golang/core/references/REFERENCE.md +0 -0
- /package/{rules → templates/golang/rules}/golang/core/references/concurrency-patterns.md +0 -0
- /package/{rules → templates/golang/rules}/golang/core/references/error-handling.md +0 -0
- /package/{rules → templates/golang/rules}/golang/echo-framework/SKILL.md +0 -0
- /package/{rules → templates/golang/rules}/golang/echo-framework/references/REFERENCE.md +0 -0
- /package/{rules → templates/golang/rules}/golang/echo-framework/references/middleware-patterns.md +0 -0
- /package/{rules → templates/golang/rules}/golang/echo-framework/references/routing-patterns.md +0 -0
- /package/{rules → templates/golang/rules}/golang/ent-orm/SKILL.md +0 -0
- /package/{rules → templates/golang/rules}/golang/ent-orm/references/REFERENCE.md +0 -0
- /package/{rules → templates/golang/rules}/golang/ent-orm/references/schema-patterns.md +0 -0
- /package/{rules → templates/golang/rules}/golang/fiber-framework/SKILL.md +0 -0
- /package/{rules → templates/golang/rules}/golang/fiber-framework/references/REFERENCE.md +0 -0
- /package/{rules → templates/golang/rules}/golang/fiber-framework/references/routing-patterns.md +0 -0
- /package/{rules → templates/golang/rules}/golang/gin-framework/SKILL.md +0 -0
- /package/{rules → templates/golang/rules}/golang/gin-framework/references/REFERENCE.md +0 -0
- /package/{rules → templates/golang/rules}/golang/gin-framework/references/middleware-patterns.md +0 -0
- /package/{rules → templates/golang/rules}/golang/gorm-orm/SKILL.md +0 -0
- /package/{rules → templates/golang/rules}/golang/gorm-orm/references/REFERENCE.md +0 -0
- /package/{rules → templates/golang/rules}/golang/gorm-orm/references/model-definitions.md +0 -0
- /package/{rules → templates/golang/rules}/golang/gorm-orm/references/query-patterns.md +0 -0
- /package/{rules → templates/golang/rules}/golang/grpc/SKILL.md +0 -0
- /package/{rules → templates/golang/rules}/golang/grpc/references/REFERENCE.md +0 -0
- /package/{rules → templates/golang/rules}/golang/grpc/references/service-patterns.md +0 -0
- /package/{rules → templates/golang/rules}/golang/testify/SKILL.md +0 -0
- /package/{rules → templates/golang/rules}/golang/testify/references/REFERENCE.md +0 -0
- /package/{rules → templates/golang/rules}/golang/testify/references/assert-patterns.md +0 -0
- /package/{rules → templates/golang/rules}/golang/validator/SKILL.md +0 -0
- /package/{rules → templates/golang/rules}/golang/validator/references/REFERENCE.md +0 -0
- /package/{rules → templates/golang/rules}/golang/validator/references/validation-tags.md +0 -0
- /package/{rules → templates/golang/rules}/golang/viper-config/SKILL.md +0 -0
- /package/{rules → templates/golang/rules}/golang/viper-config/references/REFERENCE.md +0 -0
- /package/{rules → templates/golang/rules}/golang/viper-config/references/config-loading.md +0 -0
- /package/{rules → templates/golang/rules}/golang/wire-di/SKILL.md +0 -0
- /package/{rules → templates/golang/rules}/golang/wire-di/references/REFERENCE.md +0 -0
- /package/{rules → templates/golang/rules}/golang/wire-di/references/provider-patterns.md +0 -0
- /package/{rules → templates/golang/rules}/golang/zap-logging/SKILL.md +0 -0
- /package/{rules → templates/golang/rules}/golang/zap-logging/references/REFERENCE.md +0 -0
- /package/{rules → templates/golang/rules}/golang/zap-logging/references/logger-config.md +0 -0
- /package/{rules → templates/java/rules}/java/build-gradle/SKILL.md +0 -0
- /package/{rules → templates/java/rules}/java/build-gradle/references/REFERENCE.md +0 -0
- /package/{rules → templates/java/rules}/java/build-gradle/references/kotlin-dsl.md +0 -0
- /package/{rules → templates/java/rules}/java/build-gradle/references/task-configuration.md +0 -0
- /package/{rules → templates/java/rules}/java/build-maven/SKILL.md +0 -0
- /package/{rules → templates/java/rules}/java/build-maven/references/REFERENCE.md +0 -0
- /package/{rules → templates/java/rules}/java/build-maven/references/dependency-management.md +0 -0
- /package/{rules → templates/java/rules}/java/build-maven/references/lifecycle-phases.md +0 -0
- /package/{rules → templates/java/rules}/java/graalvm-native/SKILL.md +0 -0
- /package/{rules → templates/java/rules}/java/graalvm-native/references/REFERENCE.md +0 -0
- /package/{rules → templates/java/rules}/java/java-collections-streams/SKILL.md +0 -0
- /package/{rules → templates/java/rules}/java/java-collections-streams/references/REFERENCE.md +0 -0
- /package/{rules → templates/java/rules}/java/java-collections-streams/references/collectors-patterns.md +0 -0
- /package/{rules → templates/java/rules}/java/java-collections-streams/references/stream-pipelines.md +0 -0
- /package/{rules → templates/java/rules}/java/java-concurrency/SKILL.md +0 -0
- /package/{rules → templates/java/rules}/java/java-concurrency/references/REFERENCE.md +0 -0
- /package/{rules → templates/java/rules}/java/java-concurrency/references/completable-future.md +0 -0
- /package/{rules → templates/java/rules}/java/java-concurrency/references/executor-patterns.md +0 -0
- /package/{rules → templates/java/rules}/java/java-concurrency/references/virtual-threads.md +0 -0
- /package/{rules → templates/java/rules}/java/java-core-language/SKILL.md +0 -0
- /package/{rules → templates/java/rules}/java/java-core-language/references/REFERENCE.md +0 -0
- /package/{rules → templates/java/rules}/java/java-core-language/references/jvm-memory-model.md +0 -0
- /package/{rules → templates/java/rules}/java/java-core-language/references/modern-java-features.md +0 -0
- /package/{rules → templates/java/rules}/java/java-project-structure/SKILL.md +0 -0
- /package/{rules → templates/java/rules}/java/java-project-structure/references/REFERENCE.md +0 -0
- /package/{rules → templates/java/rules}/java/java-project-structure/references/maven-project-layout.md +0 -0
- /package/{rules → templates/java/rules}/java/java-project-structure/references/module-system.md +0 -0
- /package/{rules → templates/java/rules}/java/micronaut-core/SKILL.md +0 -0
- /package/{rules → templates/java/rules}/java/micronaut-core/references/REFERENCE.md +0 -0
- /package/{rules → templates/java/rules}/java/micronaut-reactive/SKILL.md +0 -0
- /package/{rules → templates/java/rules}/java/micronaut-reactive/references/REFERENCE.md +0 -0
- /package/{rules → templates/java/rules}/java/quarkus-core/SKILL.md +0 -0
- /package/{rules → templates/java/rules}/java/quarkus-core/references/REFERENCE.md +0 -0
- /package/{rules → templates/java/rules}/java/quarkus-reactive/SKILL.md +0 -0
- /package/{rules → templates/java/rules}/java/quarkus-reactive/references/REFERENCE.md +0 -0
- /package/{rules → templates/java/rules}/java/spring-batch/SKILL.md +0 -0
- /package/{rules → templates/java/rules}/java/spring-batch/references/REFERENCE.md +0 -0
- /package/{rules → templates/java/rules}/java/spring-boot-architecture/SKILL.md +0 -0
- /package/{rules → templates/java/rules}/java/spring-boot-architecture/references/REFERENCE.md +0 -0
- /package/{rules → templates/java/rules}/java/spring-boot-architecture/references/auto-configuration.md +0 -0
- /package/{rules → templates/java/rules}/java/spring-boot-architecture/references/configuration-properties.md +0 -0
- /package/{rules → templates/java/rules}/java/spring-boot-web/SKILL.md +0 -0
- /package/{rules → templates/java/rules}/java/spring-boot-web/references/REFERENCE.md +0 -0
- /package/{rules → templates/java/rules}/java/spring-cloud/SKILL.md +0 -0
- /package/{rules → templates/java/rules}/java/spring-cloud/references/REFERENCE.md +0 -0
- /package/{rules → templates/java/rules}/java/spring-data-jpa/SKILL.md +0 -0
- /package/{rules → templates/java/rules}/java/spring-data-jpa/references/REFERENCE.md +0 -0
- /package/{rules → templates/java/rules}/java/spring-security/SKILL.md +0 -0
- /package/{rules → templates/java/rules}/java/spring-security/references/REFERENCE.md +0 -0
- /package/{rules → templates/java/rules}/java/spring-security/references/jwt-auth-flow.md +0 -0
- /package/{rules → templates/java/rules}/java/testing-junit-mockito/SKILL.md +0 -0
- /package/{rules → templates/java/rules}/java/testing-junit-mockito/references/REFERENCE.md +0 -0
- /package/{rules → templates/java/rules}/java/testing-junit-mockito/references/junit5-patterns.md +0 -0
- /package/{rules → templates/java/rules}/java/testing-junit-mockito/references/mockito-patterns.md +0 -0
- /package/{rules → templates/java/rules}/java/testing-junit-mockito/references/spring-boot-testing.md +0 -0
- /package/{rules → templates/javascript/rules}/javascript/best-practices/SKILL.md +0 -0
- /package/{rules → templates/javascript/rules}/javascript/best-practices/references/REFERENCE.md +0 -0
- /package/{rules → templates/javascript/rules}/javascript/language/SKILL.md +0 -0
- /package/{rules → templates/javascript/rules}/javascript/language/references/REFERENCE.md +0 -0
- /package/{rules → templates/javascript/rules}/javascript/tooling/SKILL.md +0 -0
- /package/{rules → templates/javascript/rules}/javascript/tooling/references/REFERENCE.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/api-standards/SKILL.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/api-standards/references/pagination-wrapper.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/architecture/SKILL.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/architecture/references/dynamic-module.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/caching/SKILL.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/caching/references/REFERENCE.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/caching/references/cache-patterns.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/configuration/SKILL.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/configuration/references/REFERENCE.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/configuration/references/config-patterns.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/controllers-services/SKILL.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/controllers-services/references/REFERENCE.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/controllers-services/references/controller-patterns.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/controllers-services/references/service-patterns.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/database/SKILL.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/database/references/REFERENCE.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/database/references/typeorm-patterns.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/deployment/SKILL.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/deployment/references/REFERENCE.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/deployment/references/deployment-patterns.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/documentation/SKILL.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/documentation/references/REFERENCE.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/documentation/references/swagger-patterns.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/error-handling/SKILL.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/error-handling/references/REFERENCE.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/error-handling/references/exception-filters.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/file-uploads/SKILL.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/file-uploads/references/REFERENCE.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/file-uploads/references/upload-patterns.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/observability/SKILL.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/observability/references/REFERENCE.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/observability/references/logging-metrics.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/performance/SKILL.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/performance/references/REFERENCE.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/performance/references/performance-patterns.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/real-time/SKILL.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/real-time/references/REFERENCE.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/real-time/references/websocket-patterns.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/scheduling/SKILL.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/scheduling/references/REFERENCE.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/scheduling/references/scheduling-patterns.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/search/SKILL.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/search/references/REFERENCE.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/search/references/search-patterns.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/security/SKILL.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/security/references/REFERENCE.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/security/references/authentication.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/testing/SKILL.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/testing/references/REFERENCE.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/testing/references/unit-testing.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/transport/SKILL.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/transport/references/REFERENCE.md +0 -0
- /package/{rules → templates/nestjs/rules}/nestjs/transport/references/microservices-patterns.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/app-router/SKILL.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/app-router/references/REFERENCE.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/app-router/references/routing-patterns.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/architecture/SKILL.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/architecture/references/fsd-structure.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/authentication/SKILL.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/authentication/references/auth-implementation.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/caching/SKILL.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/caching/references/REFERENCE.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/caching/references/cache-strategies.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/data-access-layer/SKILL.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/data-access-layer/references/patterns.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/data-fetching/SKILL.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/data-fetching/references/REFERENCE.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/data-fetching/references/fetch-patterns.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/internationalization/SKILL.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/internationalization/references/REFERENCE.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/internationalization/references/i18n-patterns.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/optimization/SKILL.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/optimization/references/REFERENCE.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/optimization/references/optimization-patterns.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/rendering/SKILL.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/rendering/references/REFERENCE.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/rendering/references/rendering-modes.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/server-actions/SKILL.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/server-actions/references/REFERENCE.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/server-actions/references/action-patterns.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/server-components/SKILL.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/server-components/references/REFERENCE.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/server-components/references/component-patterns.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/state-management/SKILL.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/state-management/references/REFERENCE.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/state-management/references/state-patterns.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/styling/SKILL.md +0 -0
- /package/{rules → templates/nextjs/rules}/nextjs/styling/references/implementation.md +0 -0
- /package/{rules → templates/react/rules}/react/component-patterns/SKILL.md +0 -0
- /package/{rules → templates/react/rules}/react/component-patterns/references/REFERENCE.md +0 -0
- /package/{rules → templates/react/rules}/react/hooks/SKILL.md +0 -0
- /package/{rules → templates/react/rules}/react/hooks/references/REFERENCE.md +0 -0
- /package/{rules → templates/react/rules}/react/hooks.rule.md +0 -0
- /package/{rules → templates/react/rules}/react/performance/SKILL.md +0 -0
- /package/{rules → templates/react/rules}/react/performance/references/REFERENCE.md +0 -0
- /package/{rules → templates/react/rules}/react/security/SKILL.md +0 -0
- /package/{rules → templates/react/rules}/react/security/references/REFERENCE.md +0 -0
- /package/{rules → templates/react/rules}/react/state-management/SKILL.md +0 -0
- /package/{rules → templates/react/rules}/react/state-management/references/REFERENCE.md +0 -0
- /package/{rules → templates/react/rules}/react/testing/SKILL.md +0 -0
- /package/{rules → templates/react/rules}/react/testing/references/REFERENCE.md +0 -0
- /package/{rules → templates/react/rules}/react/tooling/SKILL.md +0 -0
- /package/{rules → templates/react/rules}/react/typescript/SKILL.md +0 -0
- /package/{rules → templates/rust/rules}/rust/actix-web/SKILL.md +0 -0
- /package/{rules → templates/rust/rules}/rust/actix-web/references/REFERENCE.md +0 -0
- /package/{rules → templates/rust/rules}/rust/actix-web/references/handler-patterns.md +0 -0
- /package/{rules → templates/rust/rules}/rust/async-graphql/SKILL.md +0 -0
- /package/{rules → templates/rust/rules}/rust/async-graphql/references/REFERENCE.md +0 -0
- /package/{rules → templates/rust/rules}/rust/async-graphql/references/schema-patterns.md +0 -0
- /package/{rules → templates/rust/rules}/rust/axum/SKILL.md +0 -0
- /package/{rules → templates/rust/rules}/rust/axum/references/REFERENCE.md +0 -0
- /package/{rules → templates/rust/rules}/rust/axum/references/handler-patterns.md +0 -0
- /package/{rules → templates/rust/rules}/rust/bevy/SKILL.md +0 -0
- /package/{rules → templates/rust/rules}/rust/bevy/references/REFERENCE.md +0 -0
- /package/{rules → templates/rust/rules}/rust/bevy/references/ecs-patterns.md +0 -0
- /package/{rules → templates/rust/rules}/rust/clap/SKILL.md +0 -0
- /package/{rules → templates/rust/rules}/rust/clap/references/REFERENCE.md +0 -0
- /package/{rules → templates/rust/rules}/rust/clap/references/derive-patterns.md +0 -0
- /package/{rules → templates/rust/rules}/rust/core/SKILL.md +0 -0
- /package/{rules → templates/rust/rules}/rust/core/references/REFERENCE.md +0 -0
- /package/{rules → templates/rust/rules}/rust/core/references/error-handling.md +0 -0
- /package/{rules → templates/rust/rules}/rust/diesel-orm/SKILL.md +0 -0
- /package/{rules → templates/rust/rules}/rust/diesel-orm/references/REFERENCE.md +0 -0
- /package/{rules → templates/rust/rules}/rust/diesel-orm/references/schema-patterns.md +0 -0
- /package/{rules → templates/rust/rules}/rust/rocket/SKILL.md +0 -0
- /package/{rules → templates/rust/rules}/rust/rocket/references/REFERENCE.md +0 -0
- /package/{rules → templates/rust/rules}/rust/rocket/references/handler-patterns.md +0 -0
- /package/{rules → templates/rust/rules}/rust/sea-orm/SKILL.md +0 -0
- /package/{rules → templates/rust/rules}/rust/sea-orm/references/REFERENCE.md +0 -0
- /package/{rules → templates/rust/rules}/rust/sea-orm/references/entity-patterns.md +0 -0
- /package/{rules → templates/rust/rules}/rust/serde-serialization/SKILL.md +0 -0
- /package/{rules → templates/rust/rules}/rust/serde-serialization/references/REFERENCE.md +0 -0
- /package/{rules → templates/rust/rules}/rust/serde-serialization/references/serialization-patterns.md +0 -0
- /package/{rules → templates/rust/rules}/rust/sqlx-database/SKILL.md +0 -0
- /package/{rules → templates/rust/rules}/rust/sqlx-database/references/REFERENCE.md +0 -0
- /package/{rules → templates/rust/rules}/rust/sqlx-database/references/query-patterns.md +0 -0
- /package/{rules → templates/rust/rules}/rust/tauri/SKILL.md +0 -0
- /package/{rules → templates/rust/rules}/rust/tauri/references/REFERENCE.md +0 -0
- /package/{rules → templates/rust/rules}/rust/tauri/references/command-patterns.md +0 -0
- /package/{rules → templates/rust/rules}/rust/tokio-runtime/SKILL.md +0 -0
- /package/{rules → templates/rust/rules}/rust/tokio-runtime/references/REFERENCE.md +0 -0
- /package/{rules → templates/rust/rules}/rust/tokio-runtime/references/async-patterns.md +0 -0
- /package/{rules → templates/rust/rules}/rust/tokio-runtime/references/synchronization.md +0 -0
- /package/{rules → templates/rust/rules}/rust/tonic/SKILL.md +0 -0
- /package/{rules → templates/rust/rules}/rust/tonic/references/REFERENCE.md +0 -0
- /package/{rules → templates/rust/rules}/rust/tonic/references/service-patterns.md +0 -0
- /package/{rules → templates/rust/rules}/rust/tracing/SKILL.md +0 -0
- /package/{rules → templates/rust/rules}/rust/tracing/references/REFERENCE.md +0 -0
- /package/{rules → templates/rust/rules}/rust/tracing/references/instrumentation.md +0 -0
- /package/{rules → templates/typescript/rules}/typescript/best-practices/SKILL.md +0 -0
- /package/{rules → templates/typescript/rules}/typescript/best-practices/references/REFERENCE.md +0 -0
- /package/{rules → templates/typescript/rules}/typescript/language/SKILL.md +0 -0
- /package/{rules → templates/typescript/rules}/typescript/language/references/REFERENCE.md +0 -0
- /package/{rules → templates/typescript/rules}/typescript/patterns.rule.md +0 -0
- /package/{rules → templates/typescript/rules}/typescript/security/SKILL.md +0 -0
- /package/{rules → templates/typescript/rules}/typescript/security/references/REFERENCE.md +0 -0
- /package/{rules → templates/typescript/rules}/typescript/tooling/SKILL.md +0 -0
- /package/{rules → templates/typescript/rules}/typescript/tooling/references/REFERENCE.md +0 -0
|
@@ -0,0 +1,769 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utilities for ClaudeKit hooks
|
|
3
|
+
*
|
|
4
|
+
* Contains config loading, path sanitization, and common constants
|
|
5
|
+
* used by session-init.cjs and dev-rules-reminder.cjs
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const os = require('os');
|
|
11
|
+
|
|
12
|
+
const LOCAL_CONFIG_PATH = '.claude/.ck.json';
|
|
13
|
+
const GLOBAL_CONFIG_PATH = path.join(os.homedir(), '.claude', '.ck.json');
|
|
14
|
+
|
|
15
|
+
// Legacy export for backward compatibility
|
|
16
|
+
const CONFIG_PATH = LOCAL_CONFIG_PATH;
|
|
17
|
+
|
|
18
|
+
const DEFAULT_CONFIG = {
|
|
19
|
+
plan: {
|
|
20
|
+
namingFormat: '{date}-{issue}-{slug}',
|
|
21
|
+
dateFormat: 'YYMMDD-HHmm',
|
|
22
|
+
issuePrefix: null,
|
|
23
|
+
reportsDir: 'reports',
|
|
24
|
+
resolution: {
|
|
25
|
+
// CHANGED: Removed 'mostRecent' - only explicit session state activates plans
|
|
26
|
+
// Branch matching now returns 'suggested' not 'active'
|
|
27
|
+
order: ['session', 'branch'],
|
|
28
|
+
branchPattern: '(?:feat|fix|chore|refactor|docs)/(?:[^/]+/)?(.+)'
|
|
29
|
+
},
|
|
30
|
+
validation: {
|
|
31
|
+
mode: 'prompt', // 'auto' | 'prompt' | 'off'
|
|
32
|
+
minQuestions: 3,
|
|
33
|
+
maxQuestions: 8,
|
|
34
|
+
focusAreas: ['assumptions', 'risks', 'tradeoffs', 'architecture']
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
paths: {
|
|
38
|
+
docs: 'docs',
|
|
39
|
+
plans: 'plans'
|
|
40
|
+
},
|
|
41
|
+
docs: {
|
|
42
|
+
maxLoc: 800 // Maximum lines of code per doc file before warning
|
|
43
|
+
},
|
|
44
|
+
locale: {
|
|
45
|
+
thinkingLanguage: null, // Language for reasoning (e.g., "en" for precision)
|
|
46
|
+
responseLanguage: null // Language for user-facing output (e.g., "vi")
|
|
47
|
+
},
|
|
48
|
+
trust: {
|
|
49
|
+
passphrase: null,
|
|
50
|
+
enabled: false
|
|
51
|
+
},
|
|
52
|
+
project: {
|
|
53
|
+
type: 'auto',
|
|
54
|
+
packageManager: 'auto',
|
|
55
|
+
framework: 'auto'
|
|
56
|
+
},
|
|
57
|
+
skills: {
|
|
58
|
+
research: {
|
|
59
|
+
useGemini: true // Toggle Gemini CLI usage in research skill
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
assertions: []
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Deep merge objects (source values override target, nested objects merged recursively)
|
|
67
|
+
* Arrays are replaced entirely (not concatenated) to avoid duplicate entries
|
|
68
|
+
* @param {Object} target - Base object
|
|
69
|
+
* @param {Object} source - Object to merge (takes precedence)
|
|
70
|
+
* @returns {Object} Merged object
|
|
71
|
+
*/
|
|
72
|
+
function deepMerge(target, source) {
|
|
73
|
+
if (!source || typeof source !== 'object') return target;
|
|
74
|
+
if (!target || typeof target !== 'object') return source;
|
|
75
|
+
|
|
76
|
+
const result = { ...target };
|
|
77
|
+
for (const key of Object.keys(source)) {
|
|
78
|
+
const sourceVal = source[key];
|
|
79
|
+
const targetVal = target[key];
|
|
80
|
+
|
|
81
|
+
// Arrays: replace entirely (don't concatenate)
|
|
82
|
+
if (Array.isArray(sourceVal)) {
|
|
83
|
+
result[key] = [...sourceVal];
|
|
84
|
+
}
|
|
85
|
+
// Objects: recurse (but not null)
|
|
86
|
+
else if (sourceVal !== null && typeof sourceVal === 'object' && !Array.isArray(sourceVal)) {
|
|
87
|
+
result[key] = deepMerge(targetVal || {}, sourceVal);
|
|
88
|
+
}
|
|
89
|
+
// Primitives: source wins
|
|
90
|
+
else {
|
|
91
|
+
result[key] = sourceVal;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Load config from a specific file path
|
|
99
|
+
* @param {string} configPath - Path to config file
|
|
100
|
+
* @returns {Object|null} Parsed config or null if not found/invalid
|
|
101
|
+
*/
|
|
102
|
+
function loadConfigFromPath(configPath) {
|
|
103
|
+
try {
|
|
104
|
+
if (!fs.existsSync(configPath)) return null;
|
|
105
|
+
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
106
|
+
} catch (e) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Get session temp file path
|
|
113
|
+
* @param {string} sessionId - Session identifier
|
|
114
|
+
* @returns {string} Path to session temp file
|
|
115
|
+
*/
|
|
116
|
+
function getSessionTempPath(sessionId) {
|
|
117
|
+
return path.join(os.tmpdir(), `ck-session-${sessionId}.json`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Read session state from temp file
|
|
122
|
+
* @param {string} sessionId - Session identifier
|
|
123
|
+
* @returns {Object|null} Session state or null
|
|
124
|
+
*/
|
|
125
|
+
function readSessionState(sessionId) {
|
|
126
|
+
if (!sessionId) return null;
|
|
127
|
+
const tempPath = getSessionTempPath(sessionId);
|
|
128
|
+
try {
|
|
129
|
+
if (!fs.existsSync(tempPath)) return null;
|
|
130
|
+
return JSON.parse(fs.readFileSync(tempPath, 'utf8'));
|
|
131
|
+
} catch (e) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Write session state atomically to temp file
|
|
138
|
+
* @param {string} sessionId - Session identifier
|
|
139
|
+
* @param {Object} state - State object to persist
|
|
140
|
+
* @returns {boolean} Success status
|
|
141
|
+
*/
|
|
142
|
+
function writeSessionState(sessionId, state) {
|
|
143
|
+
if (!sessionId) return false;
|
|
144
|
+
const tempPath = getSessionTempPath(sessionId);
|
|
145
|
+
const tmpFile = tempPath + '.' + Math.random().toString(36).slice(2);
|
|
146
|
+
try {
|
|
147
|
+
fs.writeFileSync(tmpFile, JSON.stringify(state, null, 2));
|
|
148
|
+
fs.renameSync(tmpFile, tempPath);
|
|
149
|
+
return true;
|
|
150
|
+
} catch (e) {
|
|
151
|
+
try { fs.unlinkSync(tmpFile); } catch (_) { /* ignore */ }
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Characters invalid in filenames across Windows, macOS, Linux
|
|
158
|
+
* Windows: < > : " / \ | ? *
|
|
159
|
+
* macOS/Linux: / and null byte
|
|
160
|
+
* Also includes control characters and other problematic chars
|
|
161
|
+
*/
|
|
162
|
+
const INVALID_FILENAME_CHARS = /[<>:"/\\|?*\x00-\x1f\x7f]/g;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Sanitize slug for safe filesystem usage
|
|
166
|
+
* - Removes invalid filename characters
|
|
167
|
+
* - Replaces non-alphanumeric (except hyphen) with hyphen
|
|
168
|
+
* - Collapses multiple hyphens
|
|
169
|
+
* - Removes leading/trailing hyphens
|
|
170
|
+
* - Limits length to prevent filesystem issues
|
|
171
|
+
*
|
|
172
|
+
* @param {string} slug - Slug to sanitize
|
|
173
|
+
* @returns {string} Sanitized slug (empty string if nothing valid remains)
|
|
174
|
+
*/
|
|
175
|
+
function sanitizeSlug(slug) {
|
|
176
|
+
if (!slug || typeof slug !== 'string') return '';
|
|
177
|
+
|
|
178
|
+
let sanitized = slug
|
|
179
|
+
// Remove invalid filename chars first
|
|
180
|
+
.replace(INVALID_FILENAME_CHARS, '')
|
|
181
|
+
// Replace any non-alphanumeric (except hyphen) with hyphen
|
|
182
|
+
.replace(/[^a-z0-9-]/gi, '-')
|
|
183
|
+
// Collapse multiple consecutive hyphens
|
|
184
|
+
.replace(/-+/g, '-')
|
|
185
|
+
// Remove leading/trailing hyphens
|
|
186
|
+
.replace(/^-+|-+$/g, '')
|
|
187
|
+
// Limit length (most filesystems support 255, but keep reasonable)
|
|
188
|
+
.slice(0, 100);
|
|
189
|
+
|
|
190
|
+
return sanitized;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Extract feature slug from git branch name
|
|
195
|
+
* Pattern: (?:feat|fix|chore|refactor|docs)/(?:[^/]+/)?(.+)
|
|
196
|
+
* @param {string} branch - Git branch name
|
|
197
|
+
* @param {string} pattern - Regex pattern (optional)
|
|
198
|
+
* @returns {string|null} Extracted slug or null
|
|
199
|
+
*/
|
|
200
|
+
function extractSlugFromBranch(branch, pattern) {
|
|
201
|
+
if (!branch) return null;
|
|
202
|
+
const defaultPattern = /(?:feat|fix|chore|refactor|docs)\/(?:[^\/]+\/)?(.+)/;
|
|
203
|
+
const regex = pattern ? new RegExp(pattern) : defaultPattern;
|
|
204
|
+
const match = branch.match(regex);
|
|
205
|
+
return match ? sanitizeSlug(match[1]) : null;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Find most recent plan folder by timestamp prefix
|
|
210
|
+
* @param {string} plansDir - Plans directory path
|
|
211
|
+
* @returns {string|null} Most recent plan path or null
|
|
212
|
+
*/
|
|
213
|
+
function findMostRecentPlan(plansDir) {
|
|
214
|
+
try {
|
|
215
|
+
if (!fs.existsSync(plansDir)) return null;
|
|
216
|
+
const entries = fs.readdirSync(plansDir, { withFileTypes: true });
|
|
217
|
+
const planDirs = entries
|
|
218
|
+
.filter(e => e.isDirectory() && /^\d{6}/.test(e.name))
|
|
219
|
+
.map(e => e.name)
|
|
220
|
+
.sort()
|
|
221
|
+
.reverse();
|
|
222
|
+
return planDirs.length > 0 ? path.join(plansDir, planDirs[0]) : null;
|
|
223
|
+
} catch (e) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Default timeout for git commands (5 seconds)
|
|
230
|
+
* Prevents indefinite hangs on network mounts or corrupted repos
|
|
231
|
+
*/
|
|
232
|
+
const DEFAULT_EXEC_TIMEOUT_MS = 5000;
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Safely execute shell command (internal helper)
|
|
236
|
+
* SECURITY: Only accepts whitelisted git read commands
|
|
237
|
+
* @param {string} cmd - Command to execute
|
|
238
|
+
* @param {Object} options - Execution options
|
|
239
|
+
* @param {string} options.cwd - Working directory (optional)
|
|
240
|
+
* @param {number} options.timeout - Timeout in ms (default: 5000)
|
|
241
|
+
* @returns {string|null} Command output or null
|
|
242
|
+
*/
|
|
243
|
+
function execSafe(cmd, options = {}) {
|
|
244
|
+
// Whitelist of safe read-only commands
|
|
245
|
+
const allowedCommands = [
|
|
246
|
+
'git branch --show-current',
|
|
247
|
+
'git rev-parse --abbrev-ref HEAD',
|
|
248
|
+
'git rev-parse --show-toplevel'
|
|
249
|
+
];
|
|
250
|
+
if (!allowedCommands.includes(cmd)) {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const { cwd = undefined, timeout = DEFAULT_EXEC_TIMEOUT_MS } = options;
|
|
255
|
+
|
|
256
|
+
try {
|
|
257
|
+
return require('child_process')
|
|
258
|
+
.execSync(cmd, {
|
|
259
|
+
encoding: 'utf8',
|
|
260
|
+
timeout,
|
|
261
|
+
cwd,
|
|
262
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
263
|
+
})
|
|
264
|
+
.trim();
|
|
265
|
+
} catch (e) {
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Resolve active plan path using cascading resolution with tracking
|
|
272
|
+
*
|
|
273
|
+
* Resolution semantics:
|
|
274
|
+
* - 'session': Explicitly set via set-active-plan.cjs → ACTIVE (directive)
|
|
275
|
+
* - 'branch': Matched from git branch name → SUGGESTED (hint only)
|
|
276
|
+
* - 'mostRecent': REMOVED - was causing stale plan pollution
|
|
277
|
+
*
|
|
278
|
+
* @param {string} sessionId - Session identifier (optional)
|
|
279
|
+
* @param {Object} config - ClaudeKit config
|
|
280
|
+
* @returns {{ path: string|null, resolvedBy: 'session'|'branch'|null }} Resolution result with tracking
|
|
281
|
+
*/
|
|
282
|
+
function resolvePlanPath(sessionId, config) {
|
|
283
|
+
const plansDir = config?.paths?.plans || 'plans';
|
|
284
|
+
const resolution = config?.plan?.resolution || {};
|
|
285
|
+
const order = resolution.order || ['session', 'branch'];
|
|
286
|
+
const branchPattern = resolution.branchPattern;
|
|
287
|
+
|
|
288
|
+
for (const method of order) {
|
|
289
|
+
switch (method) {
|
|
290
|
+
case 'session': {
|
|
291
|
+
const state = readSessionState(sessionId);
|
|
292
|
+
if (state?.activePlan) {
|
|
293
|
+
// Issue #335: Handle both absolute and relative paths
|
|
294
|
+
// - Absolute paths (from updated set-active-plan.cjs): use as-is
|
|
295
|
+
// - Relative paths (legacy): resolve using sessionOrigin if available
|
|
296
|
+
let resolvedPath = state.activePlan;
|
|
297
|
+
if (!path.isAbsolute(resolvedPath) && state.sessionOrigin) {
|
|
298
|
+
// Resolve relative path using session origin directory
|
|
299
|
+
resolvedPath = path.join(state.sessionOrigin, resolvedPath);
|
|
300
|
+
}
|
|
301
|
+
return { path: resolvedPath, resolvedBy: 'session' };
|
|
302
|
+
}
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
case 'branch': {
|
|
306
|
+
try {
|
|
307
|
+
const branch = execSafe('git branch --show-current');
|
|
308
|
+
const slug = extractSlugFromBranch(branch, branchPattern);
|
|
309
|
+
if (slug && fs.existsSync(plansDir)) {
|
|
310
|
+
const entries = fs.readdirSync(plansDir, { withFileTypes: true })
|
|
311
|
+
.filter(e => e.isDirectory() && e.name.includes(slug));
|
|
312
|
+
if (entries.length > 0) {
|
|
313
|
+
return {
|
|
314
|
+
path: path.join(plansDir, entries[entries.length - 1].name),
|
|
315
|
+
resolvedBy: 'branch'
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
} catch (e) {
|
|
320
|
+
// Ignore errors reading plans dir
|
|
321
|
+
}
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
// NOTE: 'mostRecent' case intentionally removed - was causing stale plan pollution
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return { path: null, resolvedBy: null };
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Normalize path value (trim, remove trailing slashes, handle empty)
|
|
332
|
+
* @param {string} pathValue - Path to normalize
|
|
333
|
+
* @returns {string|null} Normalized path or null if invalid
|
|
334
|
+
*/
|
|
335
|
+
function normalizePath(pathValue) {
|
|
336
|
+
if (!pathValue || typeof pathValue !== 'string') return null;
|
|
337
|
+
|
|
338
|
+
// Trim whitespace
|
|
339
|
+
let normalized = pathValue.trim();
|
|
340
|
+
|
|
341
|
+
// Empty after trim = invalid
|
|
342
|
+
if (!normalized) return null;
|
|
343
|
+
|
|
344
|
+
// Remove trailing slashes (but keep root "/" or "C:\")
|
|
345
|
+
normalized = normalized.replace(/[/\\]+$/, '');
|
|
346
|
+
|
|
347
|
+
// If it became empty (was just slashes), return null
|
|
348
|
+
if (!normalized) return null;
|
|
349
|
+
|
|
350
|
+
return normalized;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Check if path is absolute
|
|
355
|
+
* @param {string} pathValue - Path to check
|
|
356
|
+
* @returns {boolean} True if absolute path
|
|
357
|
+
*/
|
|
358
|
+
function isAbsolutePath(pathValue) {
|
|
359
|
+
if (!pathValue) return false;
|
|
360
|
+
// Unix absolute: starts with /
|
|
361
|
+
// Windows absolute: starts with drive letter (C:\) or UNC (\\)
|
|
362
|
+
return path.isAbsolute(pathValue);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Sanitize path values
|
|
367
|
+
* - Normalizes path (trim, remove trailing slashes)
|
|
368
|
+
* - Allows absolute paths (for consolidated plans use case)
|
|
369
|
+
* - Prevents obvious security issues (null bytes, etc.)
|
|
370
|
+
*
|
|
371
|
+
* @param {string} pathValue - Path to sanitize
|
|
372
|
+
* @param {string} projectRoot - Project root for relative path resolution
|
|
373
|
+
* @returns {string|null} Sanitized path or null if invalid
|
|
374
|
+
*/
|
|
375
|
+
function sanitizePath(pathValue, projectRoot) {
|
|
376
|
+
// Normalize first
|
|
377
|
+
const normalized = normalizePath(pathValue);
|
|
378
|
+
if (!normalized) return null;
|
|
379
|
+
|
|
380
|
+
// Block null bytes and other dangerous chars
|
|
381
|
+
if (/[\x00]/.test(normalized)) return null;
|
|
382
|
+
|
|
383
|
+
// Allow absolute paths (user explicitly wants consolidated plans elsewhere)
|
|
384
|
+
if (isAbsolutePath(normalized)) {
|
|
385
|
+
return normalized;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// For relative paths, resolve and validate
|
|
389
|
+
const resolved = path.resolve(projectRoot, normalized);
|
|
390
|
+
|
|
391
|
+
// Prevent path traversal outside project (../ attacks)
|
|
392
|
+
// But allow if user explicitly set absolute path
|
|
393
|
+
if (!resolved.startsWith(projectRoot + path.sep) && resolved !== projectRoot) {
|
|
394
|
+
// This is a relative path trying to escape - block it
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
return normalized;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Validate and sanitize config paths
|
|
403
|
+
*/
|
|
404
|
+
function sanitizeConfig(config, projectRoot) {
|
|
405
|
+
const result = { ...config };
|
|
406
|
+
|
|
407
|
+
if (result.plan) {
|
|
408
|
+
result.plan = { ...result.plan };
|
|
409
|
+
if (!sanitizePath(result.plan.reportsDir, projectRoot)) {
|
|
410
|
+
result.plan.reportsDir = DEFAULT_CONFIG.plan.reportsDir;
|
|
411
|
+
}
|
|
412
|
+
// Merge resolution defaults
|
|
413
|
+
result.plan.resolution = {
|
|
414
|
+
...DEFAULT_CONFIG.plan.resolution,
|
|
415
|
+
...result.plan.resolution
|
|
416
|
+
};
|
|
417
|
+
// Merge validation defaults
|
|
418
|
+
result.plan.validation = {
|
|
419
|
+
...DEFAULT_CONFIG.plan.validation,
|
|
420
|
+
...result.plan.validation
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (result.paths) {
|
|
425
|
+
result.paths = { ...result.paths };
|
|
426
|
+
if (!sanitizePath(result.paths.docs, projectRoot)) {
|
|
427
|
+
result.paths.docs = DEFAULT_CONFIG.paths.docs;
|
|
428
|
+
}
|
|
429
|
+
if (!sanitizePath(result.paths.plans, projectRoot)) {
|
|
430
|
+
result.paths.plans = DEFAULT_CONFIG.paths.plans;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (result.locale) {
|
|
435
|
+
result.locale = { ...result.locale };
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
return result;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Load config with cascading resolution: DEFAULT → global → local
|
|
443
|
+
*
|
|
444
|
+
* Resolution order (each layer overrides the previous):
|
|
445
|
+
* 1. DEFAULT_CONFIG (hardcoded defaults)
|
|
446
|
+
* 2. Global config (~/.claude/.ck.json) - user preferences
|
|
447
|
+
* 3. Local config (./.claude/.ck.json) - project-specific overrides
|
|
448
|
+
*
|
|
449
|
+
* @param {Object} options - Options for config loading
|
|
450
|
+
* @param {boolean} options.includeProject - Include project section (default: true)
|
|
451
|
+
* @param {boolean} options.includeAssertions - Include assertions (default: true)
|
|
452
|
+
* @param {boolean} options.includeLocale - Include locale section (default: true)
|
|
453
|
+
*/
|
|
454
|
+
function loadConfig(options = {}) {
|
|
455
|
+
const { includeProject = true, includeAssertions = true, includeLocale = true } = options;
|
|
456
|
+
const projectRoot = process.cwd();
|
|
457
|
+
|
|
458
|
+
// Load configs from both locations
|
|
459
|
+
const globalConfig = loadConfigFromPath(GLOBAL_CONFIG_PATH);
|
|
460
|
+
const localConfig = loadConfigFromPath(LOCAL_CONFIG_PATH);
|
|
461
|
+
|
|
462
|
+
// No config files found - use defaults
|
|
463
|
+
if (!globalConfig && !localConfig) {
|
|
464
|
+
return getDefaultConfig(includeProject, includeAssertions, includeLocale);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
try {
|
|
468
|
+
// Deep merge: DEFAULT → global → local (local wins)
|
|
469
|
+
let merged = deepMerge({}, DEFAULT_CONFIG);
|
|
470
|
+
if (globalConfig) merged = deepMerge(merged, globalConfig);
|
|
471
|
+
if (localConfig) merged = deepMerge(merged, localConfig);
|
|
472
|
+
|
|
473
|
+
// Build result with optional sections
|
|
474
|
+
const result = {
|
|
475
|
+
plan: merged.plan || DEFAULT_CONFIG.plan,
|
|
476
|
+
paths: merged.paths || DEFAULT_CONFIG.paths,
|
|
477
|
+
docs: merged.docs || DEFAULT_CONFIG.docs
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
if (includeLocale) {
|
|
481
|
+
result.locale = merged.locale || DEFAULT_CONFIG.locale;
|
|
482
|
+
}
|
|
483
|
+
// Always include trust config for verification
|
|
484
|
+
result.trust = merged.trust || DEFAULT_CONFIG.trust;
|
|
485
|
+
if (includeProject) {
|
|
486
|
+
result.project = merged.project || DEFAULT_CONFIG.project;
|
|
487
|
+
}
|
|
488
|
+
if (includeAssertions) {
|
|
489
|
+
result.assertions = merged.assertions || [];
|
|
490
|
+
}
|
|
491
|
+
// Coding level for output style selection (-1 to 5, default: -1 = disabled)
|
|
492
|
+
// -1 = disabled (no injection, saves tokens)
|
|
493
|
+
// 0-5 = inject corresponding level guidelines
|
|
494
|
+
result.codingLevel = merged.codingLevel ?? -1;
|
|
495
|
+
// Skills configuration
|
|
496
|
+
result.skills = merged.skills || DEFAULT_CONFIG.skills;
|
|
497
|
+
|
|
498
|
+
return sanitizeConfig(result, projectRoot);
|
|
499
|
+
} catch (e) {
|
|
500
|
+
return getDefaultConfig(includeProject, includeAssertions, includeLocale);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Get default config with optional sections
|
|
506
|
+
*/
|
|
507
|
+
function getDefaultConfig(includeProject = true, includeAssertions = true, includeLocale = true) {
|
|
508
|
+
const result = {
|
|
509
|
+
plan: { ...DEFAULT_CONFIG.plan },
|
|
510
|
+
paths: { ...DEFAULT_CONFIG.paths },
|
|
511
|
+
docs: { ...DEFAULT_CONFIG.docs },
|
|
512
|
+
codingLevel: -1, // Default: disabled (no injection, saves tokens)
|
|
513
|
+
skills: { ...DEFAULT_CONFIG.skills }
|
|
514
|
+
};
|
|
515
|
+
if (includeLocale) {
|
|
516
|
+
result.locale = { ...DEFAULT_CONFIG.locale };
|
|
517
|
+
}
|
|
518
|
+
if (includeProject) {
|
|
519
|
+
result.project = { ...DEFAULT_CONFIG.project };
|
|
520
|
+
}
|
|
521
|
+
if (includeAssertions) {
|
|
522
|
+
result.assertions = [];
|
|
523
|
+
}
|
|
524
|
+
return result;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Escape shell special characters for env file values
|
|
529
|
+
* Handles: backslash, double quote, dollar sign, backtick
|
|
530
|
+
*/
|
|
531
|
+
function escapeShellValue(str) {
|
|
532
|
+
if (typeof str !== 'string') return str;
|
|
533
|
+
return str
|
|
534
|
+
.replace(/\\/g, '\\\\') // Backslash first
|
|
535
|
+
.replace(/"/g, '\\"') // Double quotes
|
|
536
|
+
.replace(/\$/g, '\\$') // Dollar sign
|
|
537
|
+
.replace(/`/g, '\\`'); // Backticks (command substitution)
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Write environment variable to CLAUDE_ENV_FILE (with escaping)
|
|
542
|
+
*/
|
|
543
|
+
function writeEnv(envFile, key, value) {
|
|
544
|
+
if (envFile && value !== null && value !== undefined) {
|
|
545
|
+
const escaped = escapeShellValue(String(value));
|
|
546
|
+
fs.appendFileSync(envFile, `export ${key}="${escaped}"\n`);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Get reports path based on plan resolution
|
|
552
|
+
* Only uses plan-specific path for 'session' resolved plans (explicitly active)
|
|
553
|
+
* Branch-matched (suggested) plans use default path to avoid pollution
|
|
554
|
+
*
|
|
555
|
+
* @param {string|null} planPath - The plan path
|
|
556
|
+
* @param {string|null} resolvedBy - How plan was resolved ('session'|'branch'|null)
|
|
557
|
+
* @param {Object} planConfig - Plan configuration
|
|
558
|
+
* @param {Object} pathsConfig - Paths configuration
|
|
559
|
+
* @param {string|null} baseDir - Optional base directory for absolute path resolution
|
|
560
|
+
* @returns {string} Reports path (absolute if baseDir provided, relative otherwise)
|
|
561
|
+
*/
|
|
562
|
+
function getReportsPath(planPath, resolvedBy, planConfig, pathsConfig, baseDir = null) {
|
|
563
|
+
const reportsDir = normalizePath(planConfig?.reportsDir) || 'reports';
|
|
564
|
+
const plansDir = normalizePath(pathsConfig?.plans) || 'plans';
|
|
565
|
+
|
|
566
|
+
let reportPath;
|
|
567
|
+
// Only use plan-specific reports path if explicitly active (session state)
|
|
568
|
+
// Issue #327: Validate normalized path to prevent whitespace-only paths creating invalid directories
|
|
569
|
+
const normalizedPlanPath = planPath && resolvedBy === 'session' ? normalizePath(planPath) : null;
|
|
570
|
+
if (normalizedPlanPath) {
|
|
571
|
+
reportPath = `${normalizedPlanPath}/${reportsDir}`;
|
|
572
|
+
} else {
|
|
573
|
+
// Default path for no plan or suggested (branch-matched) plans
|
|
574
|
+
reportPath = `${plansDir}/${reportsDir}`;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Return absolute path if baseDir provided
|
|
578
|
+
if (baseDir) {
|
|
579
|
+
return path.join(baseDir, reportPath);
|
|
580
|
+
}
|
|
581
|
+
return reportPath + '/';
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Format issue ID with prefix
|
|
586
|
+
*/
|
|
587
|
+
function formatIssueId(issueId, planConfig) {
|
|
588
|
+
if (!issueId) return null;
|
|
589
|
+
return planConfig.issuePrefix ? `${planConfig.issuePrefix}${issueId}` : `#${issueId}`;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Extract issue ID from branch name
|
|
594
|
+
*/
|
|
595
|
+
function extractIssueFromBranch(branch) {
|
|
596
|
+
if (!branch) return null;
|
|
597
|
+
const patterns = [
|
|
598
|
+
/(?:issue|gh|fix|feat|bug)[/-]?(\d+)/i,
|
|
599
|
+
/[/-](\d+)[/-]/,
|
|
600
|
+
/#(\d+)/
|
|
601
|
+
];
|
|
602
|
+
for (const pattern of patterns) {
|
|
603
|
+
const match = branch.match(pattern);
|
|
604
|
+
if (match) return match[1];
|
|
605
|
+
}
|
|
606
|
+
return null;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Format date according to dateFormat config
|
|
611
|
+
* Supports: YYMMDD, YYMMDD-HHmm, YYYYMMDD, etc.
|
|
612
|
+
* @param {string} format - Date format string
|
|
613
|
+
* @returns {string} Formatted date
|
|
614
|
+
*/
|
|
615
|
+
function formatDate(format) {
|
|
616
|
+
const now = new Date();
|
|
617
|
+
const pad = (n, len = 2) => String(n).padStart(len, '0');
|
|
618
|
+
|
|
619
|
+
const tokens = {
|
|
620
|
+
'YYYY': now.getFullYear(),
|
|
621
|
+
'YY': String(now.getFullYear()).slice(-2),
|
|
622
|
+
'MM': pad(now.getMonth() + 1),
|
|
623
|
+
'DD': pad(now.getDate()),
|
|
624
|
+
'HH': pad(now.getHours()),
|
|
625
|
+
'mm': pad(now.getMinutes()),
|
|
626
|
+
'ss': pad(now.getSeconds())
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
let result = format;
|
|
630
|
+
for (const [token, value] of Object.entries(tokens)) {
|
|
631
|
+
result = result.replace(token, value);
|
|
632
|
+
}
|
|
633
|
+
return result;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* Validate naming pattern result
|
|
638
|
+
* Ensures pattern resolves to a usable directory name
|
|
639
|
+
*
|
|
640
|
+
* @param {string} pattern - Resolved naming pattern
|
|
641
|
+
* @returns {{ valid: boolean, error?: string }} Validation result
|
|
642
|
+
*/
|
|
643
|
+
function validateNamingPattern(pattern) {
|
|
644
|
+
if (!pattern || typeof pattern !== 'string') {
|
|
645
|
+
return { valid: false, error: 'Pattern is empty or not a string' };
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// After removing {slug} placeholder, should still have content
|
|
649
|
+
const withoutSlug = pattern.replace(/\{slug\}/g, '').replace(/-+/g, '-').replace(/^-|-$/g, '');
|
|
650
|
+
if (!withoutSlug) {
|
|
651
|
+
return { valid: false, error: 'Pattern resolves to empty after removing {slug}' };
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// Check for remaining unresolved placeholders (besides {slug})
|
|
655
|
+
const unresolvedMatch = withoutSlug.match(/\{[^}]+\}/);
|
|
656
|
+
if (unresolvedMatch) {
|
|
657
|
+
return { valid: false, error: `Unresolved placeholder: ${unresolvedMatch[0]}` };
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// Pattern must contain {slug} for agents to substitute
|
|
661
|
+
if (!pattern.includes('{slug}')) {
|
|
662
|
+
return { valid: false, error: 'Pattern must contain {slug} placeholder' };
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
return { valid: true };
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* Resolve naming pattern with date and optional issue prefix
|
|
670
|
+
* Keeps {slug} as placeholder for agents to substitute
|
|
671
|
+
*
|
|
672
|
+
* Example: namingFormat="{date}-{issue}-{slug}", dateFormat="YYMMDD-HHmm", issue="GH-88"
|
|
673
|
+
* Returns: "251212-1830-GH-88-{slug}" (if issue exists)
|
|
674
|
+
* Returns: "251212-1830-{slug}" (if no issue)
|
|
675
|
+
*
|
|
676
|
+
* @param {Object} planConfig - Plan configuration
|
|
677
|
+
* @param {string|null} gitBranch - Current git branch (for issue extraction)
|
|
678
|
+
* @returns {string} Resolved naming pattern with {slug} placeholder
|
|
679
|
+
*/
|
|
680
|
+
function resolveNamingPattern(planConfig, gitBranch) {
|
|
681
|
+
const { namingFormat, dateFormat, issuePrefix } = planConfig;
|
|
682
|
+
const formattedDate = formatDate(dateFormat);
|
|
683
|
+
|
|
684
|
+
// Try to extract issue ID from branch name
|
|
685
|
+
const issueId = extractIssueFromBranch(gitBranch);
|
|
686
|
+
const fullIssue = issueId && issuePrefix ? `${issuePrefix}${issueId}` : null;
|
|
687
|
+
|
|
688
|
+
// Build pattern by substituting {date} and {issue}, keep {slug}
|
|
689
|
+
let pattern = namingFormat;
|
|
690
|
+
pattern = pattern.replace('{date}', formattedDate);
|
|
691
|
+
|
|
692
|
+
if (fullIssue) {
|
|
693
|
+
pattern = pattern.replace('{issue}', fullIssue);
|
|
694
|
+
} else {
|
|
695
|
+
// Remove {issue} and any trailing/leading dash
|
|
696
|
+
pattern = pattern.replace(/-?\{issue\}-?/, '-').replace(/--+/g, '-');
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// Clean up the result:
|
|
700
|
+
// - Remove leading/trailing hyphens
|
|
701
|
+
// - Collapse multiple hyphens (except around {slug})
|
|
702
|
+
pattern = pattern
|
|
703
|
+
.replace(/^-+/, '') // Remove leading hyphens
|
|
704
|
+
.replace(/-+$/, '') // Remove trailing hyphens
|
|
705
|
+
.replace(/-+(\{slug\})/g, '-$1') // Single hyphen before {slug}
|
|
706
|
+
.replace(/(\{slug\})-+/g, '$1-') // Single hyphen after {slug}
|
|
707
|
+
.replace(/--+/g, '-'); // Collapse other multiple hyphens
|
|
708
|
+
|
|
709
|
+
// Validate the resulting pattern
|
|
710
|
+
const validation = validateNamingPattern(pattern);
|
|
711
|
+
if (!validation.valid) {
|
|
712
|
+
// Log warning but return pattern anyway (fail-safe)
|
|
713
|
+
if (process.env.CK_DEBUG) {
|
|
714
|
+
console.error(`[ck-config] Warning: ${validation.error}`);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
return pattern;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* Get current git branch (safe execution)
|
|
723
|
+
* @param {string|null} cwd - Working directory to run git command from (optional)
|
|
724
|
+
* @returns {string|null} Current branch name or null
|
|
725
|
+
*/
|
|
726
|
+
function getGitBranch(cwd = null) {
|
|
727
|
+
return execSafe('git branch --show-current', { cwd: cwd || undefined });
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* Get git repository root directory
|
|
732
|
+
* @param {string|null} cwd - Working directory to run git command from (optional)
|
|
733
|
+
* @returns {string|null} Git root absolute path or null if not in git repo
|
|
734
|
+
*/
|
|
735
|
+
function getGitRoot(cwd = null) {
|
|
736
|
+
return execSafe('git rev-parse --show-toplevel', { cwd: cwd || undefined });
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
module.exports = {
|
|
740
|
+
CONFIG_PATH,
|
|
741
|
+
LOCAL_CONFIG_PATH,
|
|
742
|
+
GLOBAL_CONFIG_PATH,
|
|
743
|
+
DEFAULT_CONFIG,
|
|
744
|
+
INVALID_FILENAME_CHARS,
|
|
745
|
+
deepMerge,
|
|
746
|
+
loadConfigFromPath,
|
|
747
|
+
loadConfig,
|
|
748
|
+
normalizePath,
|
|
749
|
+
isAbsolutePath,
|
|
750
|
+
sanitizePath,
|
|
751
|
+
sanitizeSlug,
|
|
752
|
+
sanitizeConfig,
|
|
753
|
+
escapeShellValue,
|
|
754
|
+
writeEnv,
|
|
755
|
+
getSessionTempPath,
|
|
756
|
+
readSessionState,
|
|
757
|
+
writeSessionState,
|
|
758
|
+
resolvePlanPath,
|
|
759
|
+
extractSlugFromBranch,
|
|
760
|
+
findMostRecentPlan,
|
|
761
|
+
getReportsPath,
|
|
762
|
+
formatIssueId,
|
|
763
|
+
extractIssueFromBranch,
|
|
764
|
+
formatDate,
|
|
765
|
+
validateNamingPattern,
|
|
766
|
+
resolveNamingPattern,
|
|
767
|
+
getGitBranch,
|
|
768
|
+
getGitRoot
|
|
769
|
+
};
|