@cregis-dev/cckit 0.4.0 → 0.4.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/LICENSE +21 -21
- package/README.md +76 -84
- package/bin/cckit.js +3 -3
- package/package.json +7 -5
- package/registry.json +234 -234
- package/src/adapters/trae-adapter.js +90 -90
- package/src/cli.js +2 -2
- package/src/commands/init.js +337 -337
- package/src/commands/status.js +62 -62
- package/src/commands/sync.js +325 -325
- package/src/commands/update.js +430 -430
- package/src/core/config.js +82 -82
- package/src/core/differ.js +57 -57
- package/src/core/installer.js +97 -97
- package/src/core/plugin-installer.js +278 -278
- package/src/core/registry.js +75 -75
- package/src/core/templatize.js +42 -42
- package/src/core/upstream.js +357 -357
- package/src/utils/fs.js +55 -55
- package/src/utils/logger.js +16 -16
- package/templates/bmad/_config/agent-manifest.csv +12 -12
- package/templates/bmad/_config/agents/bmm-analyst.customize.yaml +41 -41
- package/templates/bmad/_config/agents/bmm-architect.customize.yaml +41 -41
- package/templates/bmad/_config/agents/bmm-dev.customize.yaml +41 -41
- package/templates/bmad/_config/agents/bmm-pm.customize.yaml +41 -41
- package/templates/bmad/_config/agents/bmm-qa.customize.yaml +41 -41
- package/templates/bmad/_config/agents/bmm-quick-flow-solo-dev.customize.yaml +41 -41
- package/templates/bmad/_config/agents/bmm-sm.customize.yaml +41 -41
- package/templates/bmad/_config/agents/bmm-tech-writer.customize.yaml +41 -41
- package/templates/bmad/_config/agents/bmm-ux-designer.customize.yaml +41 -41
- package/templates/bmad/_config/agents/core-bmad-master.customize.yaml +41 -41
- package/templates/bmad/_config/agents/tea-tea.customize.yaml +41 -41
- package/templates/bmad/_config/bmad-help.csv +48 -48
- package/templates/bmad/_config/files-manifest.csv +437 -437
- package/templates/bmad/_config/ides/claude-code.yaml +5 -5
- package/templates/bmad/_config/ides/opencode.yaml +5 -5
- package/templates/bmad/_config/ides/trae.yaml +5 -5
- package/templates/bmad/_config/task-manifest.csv +8 -8
- package/templates/bmad/_config/tool-manifest.csv +1 -1
- package/templates/bmad/_config/workflow-manifest.csv +35 -35
- package/templates/bmad/bmm/agents/analyst.md +78 -78
- package/templates/bmad/bmm/agents/architect.md +58 -58
- package/templates/bmad/bmm/agents/dev.md +69 -69
- package/templates/bmad/bmm/agents/pm.md +72 -72
- package/templates/bmad/bmm/agents/qa.md +92 -92
- package/templates/bmad/bmm/agents/quick-flow-solo-dev.md +69 -69
- package/templates/bmad/bmm/agents/sm.md +70 -70
- package/templates/bmad/bmm/agents/tech-writer/tech-writer.md +70 -70
- package/templates/bmad/bmm/agents/ux-designer.md +57 -57
- package/templates/bmad/bmm/config.yaml +15 -15
- package/templates/bmad/bmm/data/project-context-template.md +26 -26
- package/templates/bmad/bmm/module-help.csv +31 -31
- package/templates/bmad/bmm/teams/default-party.csv +20 -20
- package/templates/bmad/bmm/teams/team-fullstack.yaml +12 -12
- package/templates/bmad/bmm/workflows/1-analysis/create-product-brief/product-brief.template.md +10 -10
- package/templates/bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-01-init.md +177 -177
- package/templates/bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-01b-continue.md +161 -161
- package/templates/bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-02-vision.md +199 -199
- package/templates/bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-03-users.md +202 -202
- package/templates/bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-04-metrics.md +205 -205
- package/templates/bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-05-scope.md +219 -219
- package/templates/bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-06-complete.md +162 -162
- package/templates/bmad/bmm/workflows/1-analysis/create-product-brief/workflow.md +57 -57
- package/templates/bmad/bmm/workflows/1-analysis/research/domain-steps/step-01-init.md +137 -137
- package/templates/bmad/bmm/workflows/1-analysis/research/domain-steps/step-02-domain-analysis.md +229 -229
- package/templates/bmad/bmm/workflows/1-analysis/research/domain-steps/step-03-competitive-landscape.md +238 -238
- package/templates/bmad/bmm/workflows/1-analysis/research/domain-steps/step-04-regulatory-focus.md +206 -206
- package/templates/bmad/bmm/workflows/1-analysis/research/domain-steps/step-05-technical-trends.md +234 -234
- package/templates/bmad/bmm/workflows/1-analysis/research/domain-steps/step-06-research-synthesis.md +444 -444
- package/templates/bmad/bmm/workflows/1-analysis/research/market-steps/step-01-init.md +182 -182
- package/templates/bmad/bmm/workflows/1-analysis/research/market-steps/step-02-customer-behavior.md +237 -237
- package/templates/bmad/bmm/workflows/1-analysis/research/market-steps/step-03-customer-pain-points.md +249 -249
- package/templates/bmad/bmm/workflows/1-analysis/research/market-steps/step-04-customer-decisions.md +259 -259
- package/templates/bmad/bmm/workflows/1-analysis/research/market-steps/step-05-competitive-analysis.md +177 -177
- package/templates/bmad/bmm/workflows/1-analysis/research/market-steps/step-06-research-completion.md +476 -476
- package/templates/bmad/bmm/workflows/1-analysis/research/research.template.md +29 -29
- package/templates/bmad/bmm/workflows/1-analysis/research/technical-steps/step-01-init.md +137 -137
- package/templates/bmad/bmm/workflows/1-analysis/research/technical-steps/step-02-technical-overview.md +239 -239
- package/templates/bmad/bmm/workflows/1-analysis/research/technical-steps/step-03-integration-patterns.md +248 -248
- package/templates/bmad/bmm/workflows/1-analysis/research/technical-steps/step-04-architectural-patterns.md +202 -202
- package/templates/bmad/bmm/workflows/1-analysis/research/technical-steps/step-05-implementation-research.md +233 -233
- package/templates/bmad/bmm/workflows/1-analysis/research/technical-steps/step-06-research-synthesis.md +487 -487
- package/templates/bmad/bmm/workflows/1-analysis/research/workflow-domain-research.md +54 -54
- package/templates/bmad/bmm/workflows/1-analysis/research/workflow-market-research.md +54 -54
- package/templates/bmad/bmm/workflows/1-analysis/research/workflow-technical-research.md +54 -54
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/data/domain-complexity.csv +14 -14
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/data/prd-purpose.md +197 -197
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/data/project-types.csv +10 -10
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-01-init.md +191 -191
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-01b-continue.md +152 -152
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02-discovery.md +224 -224
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02b-vision.md +154 -154
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02c-executive-summary.md +170 -170
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-03-success.md +226 -226
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-04-journeys.md +213 -213
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-05-domain.md +207 -207
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-06-innovation.md +226 -226
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-07-project-type.md +237 -237
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-08-scoping.md +228 -228
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-09-functional.md +231 -231
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-10-nonfunctional.md +242 -242
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-11-polish.md +217 -217
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-12-complete.md +124 -124
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-01-discovery.md +247 -247
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-01b-legacy-conversion.md +208 -208
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-02-review.md +249 -249
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-03-edit.md +253 -253
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-04-complete.md +168 -168
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-01-discovery.md +226 -226
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-02-format-detection.md +191 -191
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-02b-parity-check.md +209 -209
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-03-density-validation.md +174 -174
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-04-brief-coverage-validation.md +214 -214
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-05-measurability-validation.md +228 -228
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-06-traceability-validation.md +217 -217
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-07-implementation-leakage-validation.md +205 -205
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-08-domain-compliance-validation.md +243 -243
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-09-project-type-validation.md +263 -263
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-10-smart-validation.md +209 -209
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-11-holistic-quality-validation.md +264 -264
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-12-completeness-validation.md +242 -242
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-13-report-complete.md +231 -231
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/templates/prd-template.md +10 -10
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/workflow-create-prd.md +63 -63
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/workflow-edit-prd.md +65 -65
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-prd/workflow-validate-prd.md +63 -63
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-01-init.md +135 -135
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-01b-continue.md +127 -127
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-02-discovery.md +190 -190
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-03-core-experience.md +216 -216
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-04-emotional-response.md +219 -219
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-05-inspiration.md +234 -234
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-06-design-system.md +252 -252
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-07-defining-experience.md +254 -254
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-08-visual-foundation.md +224 -224
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-09-design-directions.md +224 -224
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-10-user-journeys.md +241 -241
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-11-component-strategy.md +248 -248
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-12-ux-patterns.md +237 -237
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-13-responsive-accessibility.md +264 -264
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-14-complete.md +171 -171
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-ux-design/ux-design-template.md +13 -13
- package/templates/bmad/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md +42 -42
- package/templates/bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-01-document-discovery.md +184 -184
- package/templates/bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-02-prd-analysis.md +172 -172
- package/templates/bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-03-epic-coverage-validation.md +173 -173
- package/templates/bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-04-ux-alignment.md +133 -133
- package/templates/bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-05-epic-quality-review.md +245 -245
- package/templates/bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-06-final-assessment.md +129 -129
- package/templates/bmad/bmm/workflows/3-solutioning/check-implementation-readiness/templates/readiness-report-template.md +4 -4
- package/templates/bmad/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md +54 -54
- package/templates/bmad/bmm/workflows/3-solutioning/create-architecture/architecture-decision-template.md +12 -12
- package/templates/bmad/bmm/workflows/3-solutioning/create-architecture/data/domain-complexity.csv +12 -12
- package/templates/bmad/bmm/workflows/3-solutioning/create-architecture/data/project-types.csv +6 -6
- package/templates/bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-01-init.md +153 -153
- package/templates/bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-01b-continue.md +173 -173
- package/templates/bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-02-context.md +224 -224
- package/templates/bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-03-starter.md +329 -329
- package/templates/bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-04-decisions.md +318 -318
- package/templates/bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-05-patterns.md +359 -359
- package/templates/bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-06-structure.md +379 -379
- package/templates/bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-07-validation.md +359 -359
- package/templates/bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-08-complete.md +76 -76
- package/templates/bmad/bmm/workflows/3-solutioning/create-architecture/workflow.md +49 -49
- package/templates/bmad/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-01-validate-prerequisites.md +259 -259
- package/templates/bmad/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-02-design-epics.md +233 -233
- package/templates/bmad/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-03-create-stories.md +272 -272
- package/templates/bmad/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-04-final-validation.md +149 -149
- package/templates/bmad/bmm/workflows/3-solutioning/create-epics-and-stories/templates/epics-template.md +57 -57
- package/templates/bmad/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md +58 -58
- package/templates/bmad/bmm/workflows/4-implementation/code-review/checklist.md +23 -23
- package/templates/bmad/bmm/workflows/4-implementation/code-review/instructions.xml +226 -226
- package/templates/bmad/bmm/workflows/4-implementation/code-review/workflow.yaml +43 -43
- package/templates/bmad/bmm/workflows/4-implementation/correct-course/checklist.md +288 -288
- package/templates/bmad/bmm/workflows/4-implementation/correct-course/instructions.md +207 -207
- package/templates/bmad/bmm/workflows/4-implementation/correct-course/workflow.yaml +53 -53
- package/templates/bmad/bmm/workflows/4-implementation/create-story/checklist.md +358 -358
- package/templates/bmad/bmm/workflows/4-implementation/create-story/instructions.xml +346 -346
- package/templates/bmad/bmm/workflows/4-implementation/create-story/template.md +49 -49
- package/templates/bmad/bmm/workflows/4-implementation/create-story/workflow.yaml +52 -52
- package/templates/bmad/bmm/workflows/4-implementation/dev-story/checklist.md +80 -80
- package/templates/bmad/bmm/workflows/4-implementation/dev-story/instructions.xml +410 -410
- package/templates/bmad/bmm/workflows/4-implementation/dev-story/workflow.yaml +20 -20
- package/templates/bmad/bmm/workflows/4-implementation/retrospective/instructions.md +1444 -1444
- package/templates/bmad/bmm/workflows/4-implementation/retrospective/workflow.yaml +52 -52
- package/templates/bmad/bmm/workflows/4-implementation/sprint-planning/checklist.md +33 -33
- package/templates/bmad/bmm/workflows/4-implementation/sprint-planning/instructions.md +226 -226
- package/templates/bmad/bmm/workflows/4-implementation/sprint-planning/sprint-status-template.yaml +55 -55
- package/templates/bmad/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +47 -47
- package/templates/bmad/bmm/workflows/4-implementation/sprint-status/instructions.md +230 -230
- package/templates/bmad/bmm/workflows/4-implementation/sprint-status/workflow.yaml +25 -25
- package/templates/bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-01-mode-detection.md +174 -174
- package/templates/bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-02-context-gathering.md +118 -118
- package/templates/bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-03-execute.md +111 -111
- package/templates/bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-04-self-check.md +111 -111
- package/templates/bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-05-adversarial-review.md +104 -104
- package/templates/bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-06-resolve-findings.md +146 -146
- package/templates/bmad/bmm/workflows/bmad-quick-flow/quick-dev/workflow.md +50 -50
- package/templates/bmad/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-01-understand.md +189 -189
- package/templates/bmad/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-02-investigate.md +143 -143
- package/templates/bmad/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-03-generate.md +126 -126
- package/templates/bmad/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-04-review.md +200 -200
- package/templates/bmad/bmm/workflows/bmad-quick-flow/quick-spec/tech-spec-template.md +74 -74
- package/templates/bmad/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md +79 -79
- package/templates/bmad/bmm/workflows/document-project/checklist.md +245 -245
- package/templates/bmad/bmm/workflows/document-project/documentation-requirements.csv +12 -12
- package/templates/bmad/bmm/workflows/document-project/instructions.md +130 -130
- package/templates/bmad/bmm/workflows/document-project/templates/deep-dive-template.md +345 -345
- package/templates/bmad/bmm/workflows/document-project/templates/index-template.md +169 -169
- package/templates/bmad/bmm/workflows/document-project/templates/project-overview-template.md +103 -103
- package/templates/bmad/bmm/workflows/document-project/templates/project-scan-report-schema.json +160 -160
- package/templates/bmad/bmm/workflows/document-project/templates/source-tree-template.md +135 -135
- package/templates/bmad/bmm/workflows/document-project/workflow.yaml +22 -22
- package/templates/bmad/bmm/workflows/document-project/workflows/deep-dive-instructions.md +298 -298
- package/templates/bmad/bmm/workflows/document-project/workflows/deep-dive.yaml +31 -31
- package/templates/bmad/bmm/workflows/document-project/workflows/full-scan-instructions.md +1106 -1106
- package/templates/bmad/bmm/workflows/document-project/workflows/full-scan.yaml +31 -31
- package/templates/bmad/bmm/workflows/generate-project-context/project-context-template.md +21 -21
- package/templates/bmad/bmm/workflows/generate-project-context/steps/step-01-discover.md +184 -184
- package/templates/bmad/bmm/workflows/generate-project-context/steps/step-02-generate.md +318 -318
- package/templates/bmad/bmm/workflows/generate-project-context/steps/step-03-complete.md +278 -278
- package/templates/bmad/bmm/workflows/generate-project-context/workflow.md +49 -49
- package/templates/bmad/bmm/workflows/qa/automate/checklist.md +33 -33
- package/templates/bmad/bmm/workflows/qa/automate/instructions.md +110 -110
- package/templates/bmad/bmm/workflows/qa/automate/workflow.yaml +44 -44
- package/templates/bmad/bmm/workflows/qa-generate-e2e-tests/checklist.md +33 -33
- package/templates/bmad/bmm/workflows/qa-generate-e2e-tests/instructions.md +110 -110
- package/templates/bmad/bmm/workflows/qa-generate-e2e-tests/workflow.yaml +42 -42
- package/templates/bmad/commands/bmad-agent-bmad-master.md +15 -15
- package/templates/bmad/commands/bmad-agent-bmm-analyst.md +15 -15
- package/templates/bmad/commands/bmad-agent-bmm-architect.md +15 -15
- package/templates/bmad/commands/bmad-agent-bmm-dev.md +15 -15
- package/templates/bmad/commands/bmad-agent-bmm-pm.md +15 -15
- package/templates/bmad/commands/bmad-agent-bmm-qa.md +15 -15
- package/templates/bmad/commands/bmad-agent-bmm-quick-flow-solo-dev.md +15 -15
- package/templates/bmad/commands/bmad-agent-bmm-sm.md +15 -15
- package/templates/bmad/commands/bmad-agent-bmm-tech-writer.md +15 -15
- package/templates/bmad/commands/bmad-agent-bmm-ux-designer.md +15 -15
- package/templates/bmad/commands/bmad-agent-tea-tea.md +15 -15
- package/templates/bmad/commands/bmad-bmm-check-implementation-readiness.md +6 -6
- package/templates/bmad/commands/bmad-bmm-code-review.md +14 -14
- package/templates/bmad/commands/bmad-bmm-correct-course.md +14 -14
- package/templates/bmad/commands/bmad-bmm-create-architecture.md +6 -6
- package/templates/bmad/commands/bmad-bmm-create-epics-and-stories.md +6 -6
- package/templates/bmad/commands/bmad-bmm-create-prd.md +6 -6
- package/templates/bmad/commands/bmad-bmm-create-product-brief.md +6 -6
- package/templates/bmad/commands/bmad-bmm-create-story.md +14 -14
- package/templates/bmad/commands/bmad-bmm-create-ux-design.md +6 -6
- package/templates/bmad/commands/bmad-bmm-dev-story.md +14 -14
- package/templates/bmad/commands/bmad-bmm-document-project.md +14 -14
- package/templates/bmad/commands/bmad-bmm-domain-research.md +6 -6
- package/templates/bmad/commands/bmad-bmm-edit-prd.md +6 -6
- package/templates/bmad/commands/bmad-bmm-generate-project-context.md +6 -6
- package/templates/bmad/commands/bmad-bmm-market-research.md +6 -6
- package/templates/bmad/commands/bmad-bmm-qa-automate.md +15 -15
- package/templates/bmad/commands/bmad-bmm-qa-generate-e2e-tests.md +14 -14
- package/templates/bmad/commands/bmad-bmm-quick-dev.md +6 -6
- package/templates/bmad/commands/bmad-bmm-quick-spec.md +6 -6
- package/templates/bmad/commands/bmad-bmm-retrospective.md +14 -14
- package/templates/bmad/commands/bmad-bmm-sprint-planning.md +14 -14
- package/templates/bmad/commands/bmad-bmm-sprint-status.md +14 -14
- package/templates/bmad/commands/bmad-bmm-technical-research.md +6 -6
- package/templates/bmad/commands/bmad-bmm-validate-prd.md +6 -6
- package/templates/bmad/commands/bmad-brainstorming.md +6 -6
- package/templates/bmad/commands/bmad-editorial-review-prose.md +10 -10
- package/templates/bmad/commands/bmad-editorial-review-structure.md +10 -10
- package/templates/bmad/commands/bmad-help.md +10 -10
- package/templates/bmad/commands/bmad-index-docs.md +10 -10
- package/templates/bmad/commands/bmad-party-mode.md +6 -6
- package/templates/bmad/commands/bmad-review-adversarial-general.md +10 -10
- package/templates/bmad/commands/bmad-review-edge-case-hunter.md +10 -10
- package/templates/bmad/commands/bmad-shard-doc.md +10 -10
- package/templates/bmad/commands/bmad-tea-teach-me-testing.md +6 -6
- package/templates/bmad/commands/bmad-tea-testarch-atdd.md +14 -14
- package/templates/bmad/commands/bmad-tea-testarch-automate.md +14 -14
- package/templates/bmad/commands/bmad-tea-testarch-ci.md +14 -14
- package/templates/bmad/commands/bmad-tea-testarch-framework.md +14 -14
- package/templates/bmad/commands/bmad-tea-testarch-nfr.md +14 -14
- package/templates/bmad/commands/bmad-tea-testarch-test-design.md +14 -14
- package/templates/bmad/commands/bmad-tea-testarch-test-review.md +14 -14
- package/templates/bmad/commands/bmad-tea-testarch-trace.md +14 -14
- package/templates/bmad/core/agents/bmad-master.md +56 -56
- package/templates/bmad/core/config.yaml +8 -8
- package/templates/bmad/core/module-help.csv +10 -10
- package/templates/bmad/core/tasks/editorial-review-prose.xml +101 -101
- package/templates/bmad/core/tasks/editorial-review-structure.xml +207 -207
- package/templates/bmad/core/tasks/help.md +86 -86
- package/templates/bmad/core/tasks/index-docs.xml +64 -64
- package/templates/bmad/core/tasks/review-adversarial-general.xml +48 -48
- package/templates/bmad/core/tasks/review-edge-case-hunter.xml +63 -63
- package/templates/bmad/core/tasks/shard-doc.xml +107 -107
- package/templates/bmad/core/tasks/workflow.xml +234 -234
- package/templates/bmad/core/workflows/advanced-elicitation/methods.csv +51 -51
- package/templates/bmad/core/workflows/advanced-elicitation/workflow.xml +117 -117
- package/templates/bmad/core/workflows/brainstorming/brain-methods.csv +61 -61
- package/templates/bmad/core/workflows/brainstorming/steps/step-01-session-setup.md +210 -210
- package/templates/bmad/core/workflows/brainstorming/steps/step-01b-continue.md +122 -122
- package/templates/bmad/core/workflows/brainstorming/steps/step-02a-user-selected.md +225 -225
- package/templates/bmad/core/workflows/brainstorming/steps/step-02b-ai-recommended.md +237 -237
- package/templates/bmad/core/workflows/brainstorming/steps/step-02c-random-selection.md +209 -209
- package/templates/bmad/core/workflows/brainstorming/steps/step-02d-progressive-flow.md +264 -264
- package/templates/bmad/core/workflows/brainstorming/steps/step-03-technique-execution.md +399 -399
- package/templates/bmad/core/workflows/brainstorming/steps/step-04-idea-organization.md +303 -303
- package/templates/bmad/core/workflows/brainstorming/template.md +15 -15
- package/templates/bmad/core/workflows/brainstorming/workflow.md +60 -60
- package/templates/bmad/core/workflows/party-mode/steps/step-01-agent-loading.md +138 -138
- package/templates/bmad/core/workflows/party-mode/steps/step-02-discussion-orchestration.md +187 -187
- package/templates/bmad/core/workflows/party-mode/steps/step-03-graceful-exit.md +168 -168
- package/templates/bmad/core/workflows/party-mode/workflow.md +194 -194
- package/templates/bmad/tea/agents/tea.md +71 -71
- package/templates/bmad/tea/config.yaml +24 -24
- package/templates/mcp/claude-code/.mcp.json +35 -35
- package/templates/mcp/trae/mcp.json +35 -35
- package/templates/trae-bmad/rules/bmad-agent-bmad-master.md +15 -15
- package/templates/trae-bmad/rules/bmad-agent-bmm-analyst.md +15 -15
- package/templates/trae-bmad/rules/bmad-agent-bmm-architect.md +15 -15
- package/templates/trae-bmad/rules/bmad-agent-bmm-dev.md +15 -15
- package/templates/trae-bmad/rules/bmad-agent-bmm-pm.md +15 -15
- package/templates/trae-bmad/rules/bmad-agent-bmm-qa.md +15 -15
- package/templates/trae-bmad/rules/bmad-agent-bmm-quick-flow-solo-dev.md +15 -15
- package/templates/trae-bmad/rules/bmad-agent-bmm-sm.md +15 -15
- package/templates/trae-bmad/rules/bmad-agent-bmm-tech-writer.md +15 -15
- package/templates/trae-bmad/rules/bmad-agent-bmm-ux-designer.md +15 -15
- package/templates/trae-bmad/rules/bmad-agent-tea-tea.md +15 -15
- package/templates/trae-bmad/rules/bmad-bmm-check-implementation-readiness.md +6 -6
- package/templates/trae-bmad/rules/bmad-bmm-code-review.md +14 -14
- package/templates/trae-bmad/rules/bmad-bmm-correct-course.md +14 -14
- package/templates/trae-bmad/rules/bmad-bmm-create-architecture.md +6 -6
- package/templates/trae-bmad/rules/bmad-bmm-create-epics-and-stories.md +6 -6
- package/templates/trae-bmad/rules/bmad-bmm-create-prd.md +6 -6
- package/templates/trae-bmad/rules/bmad-bmm-create-product-brief.md +6 -6
- package/templates/trae-bmad/rules/bmad-bmm-create-story.md +14 -14
- package/templates/trae-bmad/rules/bmad-bmm-create-ux-design.md +6 -6
- package/templates/trae-bmad/rules/bmad-bmm-dev-story.md +14 -14
- package/templates/trae-bmad/rules/bmad-bmm-document-project.md +14 -14
- package/templates/trae-bmad/rules/bmad-bmm-domain-research.md +6 -6
- package/templates/trae-bmad/rules/bmad-bmm-edit-prd.md +6 -6
- package/templates/trae-bmad/rules/bmad-bmm-generate-project-context.md +6 -6
- package/templates/trae-bmad/rules/bmad-bmm-market-research.md +6 -6
- package/templates/trae-bmad/rules/bmad-bmm-qa-automate.md +15 -15
- package/templates/trae-bmad/rules/bmad-bmm-qa-generate-e2e-tests.md +14 -14
- package/templates/trae-bmad/rules/bmad-bmm-quick-dev.md +6 -6
- package/templates/trae-bmad/rules/bmad-bmm-quick-spec.md +6 -6
- package/templates/trae-bmad/rules/bmad-bmm-retrospective.md +14 -14
- package/templates/trae-bmad/rules/bmad-bmm-sprint-planning.md +14 -14
- package/templates/trae-bmad/rules/bmad-bmm-sprint-status.md +14 -14
- package/templates/trae-bmad/rules/bmad-bmm-technical-research.md +6 -6
- package/templates/trae-bmad/rules/bmad-bmm-validate-prd.md +6 -6
- package/templates/trae-bmad/rules/bmad-brainstorming.md +6 -6
- package/templates/trae-bmad/rules/bmad-editorial-review-prose.md +10 -10
- package/templates/trae-bmad/rules/bmad-editorial-review-structure.md +10 -10
- package/templates/trae-bmad/rules/bmad-help.md +10 -10
- package/templates/trae-bmad/rules/bmad-index-docs.md +10 -10
- package/templates/trae-bmad/rules/bmad-party-mode.md +6 -6
- package/templates/trae-bmad/rules/bmad-review-adversarial-general.md +10 -10
- package/templates/trae-bmad/rules/bmad-review-edge-case-hunter.md +10 -10
- package/templates/trae-bmad/rules/bmad-shard-doc.md +10 -10
- package/templates/trae-bmad/rules/bmad-tea-teach-me-testing.md +6 -6
- package/templates/trae-bmad/rules/bmad-tea-testarch-atdd.md +14 -14
- package/templates/trae-bmad/rules/bmad-tea-testarch-automate.md +14 -14
- package/templates/trae-bmad/rules/bmad-tea-testarch-ci.md +14 -14
- package/templates/trae-bmad/rules/bmad-tea-testarch-framework.md +14 -14
- package/templates/trae-bmad/rules/bmad-tea-testarch-nfr.md +14 -14
- package/templates/trae-bmad/rules/bmad-tea-testarch-test-design.md +14 -14
- package/templates/trae-bmad/rules/bmad-tea-testarch-test-review.md +14 -14
- package/templates/trae-bmad/rules/bmad-tea-testarch-trace.md +14 -14
package/src/commands/update.js
CHANGED
|
@@ -1,430 +1,430 @@
|
|
|
1
|
-
import path from 'node:path'
|
|
2
|
-
import { fileURLToPath } from 'node:url'
|
|
3
|
-
import fse from 'fs-extra'
|
|
4
|
-
import YAML from 'yaml'
|
|
5
|
-
import { createLogger } from '../utils/logger.js'
|
|
6
|
-
import { computeFileHash, interpolate, listFiles, TEXT_EXTENSIONS } from '../utils/fs.js'
|
|
7
|
-
import { loadRegistry, resolveModules, getDefaultModules } from '../core/registry.js'
|
|
8
|
-
import { ADAPTERS } from '../adapters/trae-adapter.js'
|
|
9
|
-
|
|
10
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
11
|
-
const CCKIT_ROOT = path.resolve(__dirname, '../..')
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Extract the fileRegistry from a manifest, handling both formats:
|
|
15
|
-
* - cckit.fileRegistry (produced by init command)
|
|
16
|
-
* - top-level fileRegistry (legacy/manual)
|
|
17
|
-
*
|
|
18
|
-
* @param {object} manifest - Parsed manifest object
|
|
19
|
-
* @returns {object} File registry map { relativePath: hash }
|
|
20
|
-
*/
|
|
21
|
-
function extractFileRegistry(manifest) {
|
|
22
|
-
if (manifest.cckit && manifest.cckit.fileRegistry) {
|
|
23
|
-
return manifest.cckit.fileRegistry
|
|
24
|
-
}
|
|
25
|
-
if (manifest.fileRegistry) {
|
|
26
|
-
return manifest.fileRegistry
|
|
27
|
-
}
|
|
28
|
-
return {}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Build a sourceMap from registry modules and config.
|
|
33
|
-
*
|
|
34
|
-
* Maps each target directory to its source template info:
|
|
35
|
-
* { templateDir, adapter, prefix, moduleId }
|
|
36
|
-
*
|
|
37
|
-
* @param {object} registry - Loaded registry
|
|
38
|
-
* @param {object} config - Config from manifest (ides, languages, etc.)
|
|
39
|
-
* @returns {object} sourceMap keyed by targetDir
|
|
40
|
-
*/
|
|
41
|
-
function buildSourceMap(registry, config) {
|
|
42
|
-
const selectedIds = getDefaultModules(registry)
|
|
43
|
-
const resolved = resolveModules(registry, selectedIds)
|
|
44
|
-
const sourceMap = {}
|
|
45
|
-
const ides = config.ides || ['claude-code']
|
|
46
|
-
|
|
47
|
-
for (const mod of resolved) {
|
|
48
|
-
if (mod.targets) {
|
|
49
|
-
for (const ide of ides) {
|
|
50
|
-
const target = mod.targets[ide]
|
|
51
|
-
if (!target) continue
|
|
52
|
-
|
|
53
|
-
const templateDir = path.resolve(
|
|
54
|
-
CCKIT_ROOT,
|
|
55
|
-
target.templateDir || mod.templateDir
|
|
56
|
-
)
|
|
57
|
-
sourceMap[target.targetDir] = {
|
|
58
|
-
templateDir,
|
|
59
|
-
adapter: target.adapter ? ADAPTERS[target.adapter] : undefined,
|
|
60
|
-
prefix: target.prefix || undefined,
|
|
61
|
-
moduleId: mod.id
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
} else if (mod.ideTarget) {
|
|
65
|
-
if (ides.includes(mod.ideTarget)) {
|
|
66
|
-
sourceMap[mod.targetDir] = {
|
|
67
|
-
templateDir: path.resolve(CCKIT_ROOT, mod.templateDir),
|
|
68
|
-
adapter: undefined,
|
|
69
|
-
prefix: undefined,
|
|
70
|
-
moduleId: mod.id
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
} else {
|
|
74
|
-
sourceMap[mod.targetDir] = {
|
|
75
|
-
templateDir: path.resolve(CCKIT_ROOT, mod.templateDir),
|
|
76
|
-
adapter: undefined,
|
|
77
|
-
prefix: undefined,
|
|
78
|
-
moduleId: mod.id
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return sourceMap
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Find the source template file for an installed file.
|
|
88
|
-
*
|
|
89
|
-
* @param {string} relPath - Relative path of the installed file (e.g. '.claude/rules/common/coding-style.md')
|
|
90
|
-
* @param {object} sourceMap - Map from targetDir to source info
|
|
91
|
-
* @returns {object|null} { srcAbsPath, adapter } or null if not found
|
|
92
|
-
*/
|
|
93
|
-
function findSourceForFile(relPath, sourceMap) {
|
|
94
|
-
// Normalize to forward slashes for matching
|
|
95
|
-
const normalizedRel = relPath.replace(/\\/g, '/')
|
|
96
|
-
|
|
97
|
-
// Collect all matching entries, pick the one with the longest prefix (most specific)
|
|
98
|
-
let bestMatch = null
|
|
99
|
-
let bestPrefixLen = -1
|
|
100
|
-
|
|
101
|
-
for (const [targetDir, source] of Object.entries(sourceMap)) {
|
|
102
|
-
const normalizedTarget = targetDir.replace(/\\/g, '/')
|
|
103
|
-
// Check if file is under this target directory
|
|
104
|
-
// Handle both "dir/file" and "dir" == "." (root-level targets)
|
|
105
|
-
const prefix = normalizedTarget === '.'
|
|
106
|
-
? ''
|
|
107
|
-
: normalizedTarget + '/'
|
|
108
|
-
|
|
109
|
-
if (prefix === '' ? true : normalizedRel.startsWith(prefix)) {
|
|
110
|
-
if (prefix.length > bestPrefixLen) {
|
|
111
|
-
const remainder = prefix === ''
|
|
112
|
-
? normalizedRel
|
|
113
|
-
: normalizedRel.slice(prefix.length)
|
|
114
|
-
|
|
115
|
-
// If a prefix was applied to the filename during install, strip it to find source
|
|
116
|
-
let srcRelPath = remainder
|
|
117
|
-
if (source.prefix) {
|
|
118
|
-
const fileName = path.basename(remainder)
|
|
119
|
-
if (fileName.startsWith(source.prefix)) {
|
|
120
|
-
const originalName = fileName.slice(source.prefix.length)
|
|
121
|
-
const dir = path.dirname(remainder)
|
|
122
|
-
srcRelPath = dir === '.' ? originalName : path.join(dir, originalName)
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const srcAbsPath = path.join(source.templateDir, srcRelPath)
|
|
127
|
-
bestPrefixLen = prefix.length
|
|
128
|
-
bestMatch = {
|
|
129
|
-
srcAbsPath,
|
|
130
|
-
adapter: source.adapter
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
return bestMatch
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Reinstall a single file from its template source.
|
|
140
|
-
*
|
|
141
|
-
* @param {string} targetDir - Project root directory
|
|
142
|
-
* @param {string} relPath - Relative path of the file
|
|
143
|
-
* @param {object} source - { srcAbsPath, adapter }
|
|
144
|
-
* @param {object} variables - Template interpolation variables
|
|
145
|
-
* @returns {Promise<boolean>} true if successfully reinstalled
|
|
146
|
-
*/
|
|
147
|
-
async function reinstallFile(targetDir, relPath, source, variables) {
|
|
148
|
-
const destPath = path.join(targetDir, relPath)
|
|
149
|
-
const ext = path.extname(relPath).toLowerCase()
|
|
150
|
-
|
|
151
|
-
if (!await fse.pathExists(source.srcAbsPath)) {
|
|
152
|
-
return false
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
await fse.ensureDir(path.dirname(destPath))
|
|
156
|
-
|
|
157
|
-
if (TEXT_EXTENSIONS.has(ext)) {
|
|
158
|
-
let content = await fse.readFile(source.srcAbsPath, 'utf8')
|
|
159
|
-
content = interpolate(content, variables)
|
|
160
|
-
if (source.adapter) {
|
|
161
|
-
content = source.adapter(content, path.basename(relPath))
|
|
162
|
-
}
|
|
163
|
-
await fse.writeFile(destPath, content, 'utf8')
|
|
164
|
-
} else {
|
|
165
|
-
await fse.copy(source.srcAbsPath, destPath)
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
return true
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Discover new files from templates that are not yet tracked in fileRegistry.
|
|
173
|
-
* This handles newly added modules/skills in cckit updates.
|
|
174
|
-
*
|
|
175
|
-
* @param {object} sourceMap - Map from targetDir to source info
|
|
176
|
-
* @param {object} fileRegistry - Current file registry from manifest
|
|
177
|
-
* @returns {Promise<Array<{ relPath: string, source: { srcAbsPath: string, adapter?: Function } }>>}
|
|
178
|
-
*/
|
|
179
|
-
async function discoverNewFiles(sourceMap, fileRegistry) {
|
|
180
|
-
const newFiles = []
|
|
181
|
-
|
|
182
|
-
for (const [targetDir, source] of Object.entries(sourceMap)) {
|
|
183
|
-
if (!await fse.pathExists(source.templateDir)) continue
|
|
184
|
-
|
|
185
|
-
const templateFiles = await listFiles(source.templateDir)
|
|
186
|
-
|
|
187
|
-
for (const relPath of templateFiles) {
|
|
188
|
-
const fileName = path.basename(relPath)
|
|
189
|
-
const dir = path.dirname(relPath)
|
|
190
|
-
|
|
191
|
-
// Apply prefix if configured (e.g. ecc-agent- prefix)
|
|
192
|
-
const installedName = source.prefix
|
|
193
|
-
? `${source.prefix}${fileName}`
|
|
194
|
-
: fileName
|
|
195
|
-
|
|
196
|
-
// Build the installed relative path
|
|
197
|
-
const installedRelPath = targetDir === '.'
|
|
198
|
-
? (dir === '.' ? installedName : `${dir}/${installedName}`)
|
|
199
|
-
: (dir === '.' ? `${targetDir}/${installedName}` : `${targetDir}/${dir}/${installedName}`)
|
|
200
|
-
|
|
201
|
-
const normalizedPath = installedRelPath.replace(/\\/g, '/')
|
|
202
|
-
|
|
203
|
-
if (!(normalizedPath in fileRegistry)) {
|
|
204
|
-
newFiles.push({
|
|
205
|
-
relPath: normalizedPath,
|
|
206
|
-
source: {
|
|
207
|
-
srcAbsPath: path.join(source.templateDir, relPath),
|
|
208
|
-
adapter: source.adapter,
|
|
209
|
-
},
|
|
210
|
-
})
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return newFiles
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Run the update command.
|
|
220
|
-
*
|
|
221
|
-
* Reads the installed manifest, compares current file hashes against
|
|
222
|
-
* the recorded fileRegistry, and reports which files are unchanged,
|
|
223
|
-
* modified by the user, or missing.
|
|
224
|
-
*
|
|
225
|
-
* When --apply is set, reinstalls files from cckit templates.
|
|
226
|
-
*
|
|
227
|
-
* @param {object} opts - Command options
|
|
228
|
-
* @param {string} [opts.dir] - Target directory (default: cwd)
|
|
229
|
-
* @param {boolean} [opts.dryRun] - Preview changes without applying
|
|
230
|
-
* @param {boolean} [opts.apply] - Reinstall files from templates
|
|
231
|
-
* @param {boolean} [opts.force] - Force overwrite modified files without backup
|
|
232
|
-
* @param {boolean} [opts.debug] - Enable debug output
|
|
233
|
-
* @returns {Promise<object>} Result with { checked, modified, unchanged, missing, applied? }
|
|
234
|
-
* @throws {Error} If cckit is not installed
|
|
235
|
-
*/
|
|
236
|
-
export async function runUpdate(opts = {}) {
|
|
237
|
-
const logger = createLogger({ debug: opts.debug })
|
|
238
|
-
const targetDir = path.resolve(opts.dir || process.cwd())
|
|
239
|
-
const manifestPath = path.join(targetDir, '_bmad', '_config', 'manifest.yaml')
|
|
240
|
-
|
|
241
|
-
if (!await fse.pathExists(manifestPath)) {
|
|
242
|
-
throw new Error('cckit is not installed. Run `cckit init` first.')
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
const manifestContent = await fse.readFile(manifestPath, 'utf8')
|
|
246
|
-
const manifest = YAML.parse(manifestContent)
|
|
247
|
-
|
|
248
|
-
logger.banner('cckit update')
|
|
249
|
-
|
|
250
|
-
const fileRegistry = extractFileRegistry(manifest)
|
|
251
|
-
const modified = []
|
|
252
|
-
const unchanged = []
|
|
253
|
-
const missing = []
|
|
254
|
-
|
|
255
|
-
for (const [relPath, expectedHash] of Object.entries(fileRegistry)) {
|
|
256
|
-
const fullPath = path.join(targetDir, relPath)
|
|
257
|
-
|
|
258
|
-
if (!await fse.pathExists(fullPath)) {
|
|
259
|
-
missing.push(relPath)
|
|
260
|
-
continue
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
const currentHash = await computeFileHash(fullPath)
|
|
264
|
-
if (currentHash !== expectedHash) {
|
|
265
|
-
modified.push(relPath)
|
|
266
|
-
} else {
|
|
267
|
-
unchanged.push(relPath)
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// Report results
|
|
272
|
-
const totalFiles = Object.keys(fileRegistry).length
|
|
273
|
-
if (totalFiles > 0) {
|
|
274
|
-
logger.info(` Checked ${totalFiles} registered file(s)`)
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
if (unchanged.length > 0) {
|
|
278
|
-
logger.success(`${unchanged.length} file(s) unchanged`)
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
if (modified.length > 0) {
|
|
282
|
-
logger.warn(`${modified.length} file(s) modified by user:`)
|
|
283
|
-
for (const f of modified) {
|
|
284
|
-
logger.info(` ${f}`)
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
if (missing.length > 0) {
|
|
289
|
-
logger.warn(`${missing.length} file(s) missing:`)
|
|
290
|
-
for (const f of missing) {
|
|
291
|
-
logger.info(` ${f}`)
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
if (totalFiles === 0) {
|
|
296
|
-
logger.info(' No files registered in manifest.')
|
|
297
|
-
} else if (modified.length === 0 && missing.length === 0) {
|
|
298
|
-
logger.success('All files match. Nothing to update.')
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
if (opts.dryRun) {
|
|
302
|
-
logger.info('\n (dry run - no changes made)')
|
|
303
|
-
return { checked: true, modified, unchanged, missing }
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// Apply mode: reinstall files from templates
|
|
307
|
-
if (opts.apply) {
|
|
308
|
-
const applied = { updated: 0, backedUp: 0, restored: 0, added: 0, failed: 0, orphaned: 0 }
|
|
309
|
-
|
|
310
|
-
let registry
|
|
311
|
-
let sourceMap
|
|
312
|
-
let variables
|
|
313
|
-
|
|
314
|
-
try {
|
|
315
|
-
registry = await loadRegistry()
|
|
316
|
-
const config = manifest.cckit && manifest.cckit.config
|
|
317
|
-
? manifest.cckit.config
|
|
318
|
-
: {}
|
|
319
|
-
|
|
320
|
-
sourceMap = buildSourceMap(registry, config)
|
|
321
|
-
variables = {
|
|
322
|
-
user_name: config.user_name || '',
|
|
323
|
-
communication_language: config.communication_language || '',
|
|
324
|
-
document_output_language: config.document_output_language || '',
|
|
325
|
-
languages: Array.isArray(config.languages)
|
|
326
|
-
? config.languages.join(', ')
|
|
327
|
-
: (config.languages || ''),
|
|
328
|
-
'project-root': '.'
|
|
329
|
-
}
|
|
330
|
-
} catch (err) {
|
|
331
|
-
logger.debug(`Failed to load registry for apply: ${err.message}`)
|
|
332
|
-
// Return result with empty applied if registry cannot be loaded
|
|
333
|
-
if (manifest.cckit) {
|
|
334
|
-
manifest.cckit.lastUpdated = new Date().toISOString()
|
|
335
|
-
}
|
|
336
|
-
await fse.writeFile(manifestPath, YAML.stringify(manifest), 'utf8')
|
|
337
|
-
logger.success('Update complete.')
|
|
338
|
-
return { checked: true, modified, unchanged, missing, applied }
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// Process all files: unchanged + modified + missing
|
|
342
|
-
const allFiles = [...unchanged, ...modified, ...missing]
|
|
343
|
-
|
|
344
|
-
for (const relPath of allFiles) {
|
|
345
|
-
const source = findSourceForFile(relPath, sourceMap)
|
|
346
|
-
if (!source || !await fse.pathExists(source.srcAbsPath)) {
|
|
347
|
-
logger.debug(`No source template found for: ${relPath} (orphaned)`)
|
|
348
|
-
applied.orphaned += 1
|
|
349
|
-
// Remove orphaned file from registry so it's no longer tracked
|
|
350
|
-
delete fileRegistry[relPath]
|
|
351
|
-
continue
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
const fullPath = path.join(targetDir, relPath)
|
|
355
|
-
const isModified = modified.includes(relPath)
|
|
356
|
-
|
|
357
|
-
// Backup modified files unless --force
|
|
358
|
-
if (isModified && !opts.force) {
|
|
359
|
-
const bakPath = `${fullPath}.bak.${Date.now()}`
|
|
360
|
-
try {
|
|
361
|
-
await fse.copy(fullPath, bakPath)
|
|
362
|
-
applied.backedUp += 1
|
|
363
|
-
logger.debug(`Backed up: ${relPath} -> ${path.basename(bakPath)}`)
|
|
364
|
-
} catch (err) {
|
|
365
|
-
logger.debug(`Failed to backup ${relPath}: ${err.message}`)
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
// Reinstall file
|
|
370
|
-
const success = await reinstallFile(targetDir, relPath, source, variables)
|
|
371
|
-
if (success) {
|
|
372
|
-
applied.updated += 1
|
|
373
|
-
// Recompute hash for updated file
|
|
374
|
-
const newHash = await computeFileHash(fullPath)
|
|
375
|
-
fileRegistry[relPath] = newHash
|
|
376
|
-
|
|
377
|
-
if (missing.includes(relPath)) {
|
|
378
|
-
applied.restored += 1
|
|
379
|
-
}
|
|
380
|
-
} else {
|
|
381
|
-
applied.failed += 1
|
|
382
|
-
logger.debug(`Source not found for: ${relPath}`)
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
// Discover and install new files from templates not yet in fileRegistry
|
|
387
|
-
const newFiles = await discoverNewFiles(sourceMap, fileRegistry)
|
|
388
|
-
if (newFiles.length > 0) {
|
|
389
|
-
logger.info(` Discovered ${newFiles.length} new file(s) from updated templates`)
|
|
390
|
-
for (const { relPath, source } of newFiles) {
|
|
391
|
-
const success = await reinstallFile(targetDir, relPath, source, variables)
|
|
392
|
-
if (success) {
|
|
393
|
-
applied.added += 1
|
|
394
|
-
const fullPath = path.join(targetDir, relPath)
|
|
395
|
-
const newHash = await computeFileHash(fullPath)
|
|
396
|
-
fileRegistry[relPath] = newHash
|
|
397
|
-
} else {
|
|
398
|
-
applied.failed += 1
|
|
399
|
-
logger.debug(`Failed to install new file: ${relPath}`)
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// Update manifest with new fileRegistry
|
|
405
|
-
if (manifest.cckit) {
|
|
406
|
-
manifest.cckit.fileRegistry = fileRegistry
|
|
407
|
-
manifest.cckit.lastUpdated = new Date().toISOString()
|
|
408
|
-
}
|
|
409
|
-
await fse.writeFile(manifestPath, YAML.stringify(manifest), 'utf8')
|
|
410
|
-
|
|
411
|
-
logger.newline()
|
|
412
|
-
if (applied.orphaned > 0) {
|
|
413
|
-
logger.warn(`${applied.orphaned} file(s) orphaned (module removed, no longer tracked)`)
|
|
414
|
-
}
|
|
415
|
-
logger.success(
|
|
416
|
-
`Applied: ${applied.updated} updated, ${applied.added} added, ${applied.restored} restored, ${applied.backedUp} backed up, ${applied.failed} failed`
|
|
417
|
-
)
|
|
418
|
-
|
|
419
|
-
return { checked: true, modified, unchanged, missing, applied }
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// Non-dry-run, non-apply: update the lastUpdated timestamp only
|
|
423
|
-
if (manifest.cckit) {
|
|
424
|
-
manifest.cckit.lastUpdated = new Date().toISOString()
|
|
425
|
-
}
|
|
426
|
-
await fse.writeFile(manifestPath, YAML.stringify(manifest), 'utf8')
|
|
427
|
-
|
|
428
|
-
logger.success('Update complete.')
|
|
429
|
-
return { checked: true, modified, unchanged, missing }
|
|
430
|
-
}
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import fse from 'fs-extra'
|
|
4
|
+
import YAML from 'yaml'
|
|
5
|
+
import { createLogger } from '../utils/logger.js'
|
|
6
|
+
import { computeFileHash, interpolate, listFiles, TEXT_EXTENSIONS } from '../utils/fs.js'
|
|
7
|
+
import { loadRegistry, resolveModules, getDefaultModules } from '../core/registry.js'
|
|
8
|
+
import { ADAPTERS } from '../adapters/trae-adapter.js'
|
|
9
|
+
|
|
10
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
11
|
+
const CCKIT_ROOT = path.resolve(__dirname, '../..')
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Extract the fileRegistry from a manifest, handling both formats:
|
|
15
|
+
* - cckit.fileRegistry (produced by init command)
|
|
16
|
+
* - top-level fileRegistry (legacy/manual)
|
|
17
|
+
*
|
|
18
|
+
* @param {object} manifest - Parsed manifest object
|
|
19
|
+
* @returns {object} File registry map { relativePath: hash }
|
|
20
|
+
*/
|
|
21
|
+
function extractFileRegistry(manifest) {
|
|
22
|
+
if (manifest.cckit && manifest.cckit.fileRegistry) {
|
|
23
|
+
return manifest.cckit.fileRegistry
|
|
24
|
+
}
|
|
25
|
+
if (manifest.fileRegistry) {
|
|
26
|
+
return manifest.fileRegistry
|
|
27
|
+
}
|
|
28
|
+
return {}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Build a sourceMap from registry modules and config.
|
|
33
|
+
*
|
|
34
|
+
* Maps each target directory to its source template info:
|
|
35
|
+
* { templateDir, adapter, prefix, moduleId }
|
|
36
|
+
*
|
|
37
|
+
* @param {object} registry - Loaded registry
|
|
38
|
+
* @param {object} config - Config from manifest (ides, languages, etc.)
|
|
39
|
+
* @returns {object} sourceMap keyed by targetDir
|
|
40
|
+
*/
|
|
41
|
+
function buildSourceMap(registry, config) {
|
|
42
|
+
const selectedIds = getDefaultModules(registry)
|
|
43
|
+
const resolved = resolveModules(registry, selectedIds)
|
|
44
|
+
const sourceMap = {}
|
|
45
|
+
const ides = config.ides || ['claude-code']
|
|
46
|
+
|
|
47
|
+
for (const mod of resolved) {
|
|
48
|
+
if (mod.targets) {
|
|
49
|
+
for (const ide of ides) {
|
|
50
|
+
const target = mod.targets[ide]
|
|
51
|
+
if (!target) continue
|
|
52
|
+
|
|
53
|
+
const templateDir = path.resolve(
|
|
54
|
+
CCKIT_ROOT,
|
|
55
|
+
target.templateDir || mod.templateDir
|
|
56
|
+
)
|
|
57
|
+
sourceMap[target.targetDir] = {
|
|
58
|
+
templateDir,
|
|
59
|
+
adapter: target.adapter ? ADAPTERS[target.adapter] : undefined,
|
|
60
|
+
prefix: target.prefix || undefined,
|
|
61
|
+
moduleId: mod.id
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
} else if (mod.ideTarget) {
|
|
65
|
+
if (ides.includes(mod.ideTarget)) {
|
|
66
|
+
sourceMap[mod.targetDir] = {
|
|
67
|
+
templateDir: path.resolve(CCKIT_ROOT, mod.templateDir),
|
|
68
|
+
adapter: undefined,
|
|
69
|
+
prefix: undefined,
|
|
70
|
+
moduleId: mod.id
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
sourceMap[mod.targetDir] = {
|
|
75
|
+
templateDir: path.resolve(CCKIT_ROOT, mod.templateDir),
|
|
76
|
+
adapter: undefined,
|
|
77
|
+
prefix: undefined,
|
|
78
|
+
moduleId: mod.id
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return sourceMap
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Find the source template file for an installed file.
|
|
88
|
+
*
|
|
89
|
+
* @param {string} relPath - Relative path of the installed file (e.g. '.claude/rules/common/coding-style.md')
|
|
90
|
+
* @param {object} sourceMap - Map from targetDir to source info
|
|
91
|
+
* @returns {object|null} { srcAbsPath, adapter } or null if not found
|
|
92
|
+
*/
|
|
93
|
+
function findSourceForFile(relPath, sourceMap) {
|
|
94
|
+
// Normalize to forward slashes for matching
|
|
95
|
+
const normalizedRel = relPath.replace(/\\/g, '/')
|
|
96
|
+
|
|
97
|
+
// Collect all matching entries, pick the one with the longest prefix (most specific)
|
|
98
|
+
let bestMatch = null
|
|
99
|
+
let bestPrefixLen = -1
|
|
100
|
+
|
|
101
|
+
for (const [targetDir, source] of Object.entries(sourceMap)) {
|
|
102
|
+
const normalizedTarget = targetDir.replace(/\\/g, '/')
|
|
103
|
+
// Check if file is under this target directory
|
|
104
|
+
// Handle both "dir/file" and "dir" == "." (root-level targets)
|
|
105
|
+
const prefix = normalizedTarget === '.'
|
|
106
|
+
? ''
|
|
107
|
+
: normalizedTarget + '/'
|
|
108
|
+
|
|
109
|
+
if (prefix === '' ? true : normalizedRel.startsWith(prefix)) {
|
|
110
|
+
if (prefix.length > bestPrefixLen) {
|
|
111
|
+
const remainder = prefix === ''
|
|
112
|
+
? normalizedRel
|
|
113
|
+
: normalizedRel.slice(prefix.length)
|
|
114
|
+
|
|
115
|
+
// If a prefix was applied to the filename during install, strip it to find source
|
|
116
|
+
let srcRelPath = remainder
|
|
117
|
+
if (source.prefix) {
|
|
118
|
+
const fileName = path.basename(remainder)
|
|
119
|
+
if (fileName.startsWith(source.prefix)) {
|
|
120
|
+
const originalName = fileName.slice(source.prefix.length)
|
|
121
|
+
const dir = path.dirname(remainder)
|
|
122
|
+
srcRelPath = dir === '.' ? originalName : path.join(dir, originalName)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const srcAbsPath = path.join(source.templateDir, srcRelPath)
|
|
127
|
+
bestPrefixLen = prefix.length
|
|
128
|
+
bestMatch = {
|
|
129
|
+
srcAbsPath,
|
|
130
|
+
adapter: source.adapter
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return bestMatch
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Reinstall a single file from its template source.
|
|
140
|
+
*
|
|
141
|
+
* @param {string} targetDir - Project root directory
|
|
142
|
+
* @param {string} relPath - Relative path of the file
|
|
143
|
+
* @param {object} source - { srcAbsPath, adapter }
|
|
144
|
+
* @param {object} variables - Template interpolation variables
|
|
145
|
+
* @returns {Promise<boolean>} true if successfully reinstalled
|
|
146
|
+
*/
|
|
147
|
+
async function reinstallFile(targetDir, relPath, source, variables) {
|
|
148
|
+
const destPath = path.join(targetDir, relPath)
|
|
149
|
+
const ext = path.extname(relPath).toLowerCase()
|
|
150
|
+
|
|
151
|
+
if (!await fse.pathExists(source.srcAbsPath)) {
|
|
152
|
+
return false
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
await fse.ensureDir(path.dirname(destPath))
|
|
156
|
+
|
|
157
|
+
if (TEXT_EXTENSIONS.has(ext)) {
|
|
158
|
+
let content = await fse.readFile(source.srcAbsPath, 'utf8')
|
|
159
|
+
content = interpolate(content, variables)
|
|
160
|
+
if (source.adapter) {
|
|
161
|
+
content = source.adapter(content, path.basename(relPath))
|
|
162
|
+
}
|
|
163
|
+
await fse.writeFile(destPath, content, 'utf8')
|
|
164
|
+
} else {
|
|
165
|
+
await fse.copy(source.srcAbsPath, destPath)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return true
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Discover new files from templates that are not yet tracked in fileRegistry.
|
|
173
|
+
* This handles newly added modules/skills in cckit updates.
|
|
174
|
+
*
|
|
175
|
+
* @param {object} sourceMap - Map from targetDir to source info
|
|
176
|
+
* @param {object} fileRegistry - Current file registry from manifest
|
|
177
|
+
* @returns {Promise<Array<{ relPath: string, source: { srcAbsPath: string, adapter?: Function } }>>}
|
|
178
|
+
*/
|
|
179
|
+
async function discoverNewFiles(sourceMap, fileRegistry) {
|
|
180
|
+
const newFiles = []
|
|
181
|
+
|
|
182
|
+
for (const [targetDir, source] of Object.entries(sourceMap)) {
|
|
183
|
+
if (!await fse.pathExists(source.templateDir)) continue
|
|
184
|
+
|
|
185
|
+
const templateFiles = await listFiles(source.templateDir)
|
|
186
|
+
|
|
187
|
+
for (const relPath of templateFiles) {
|
|
188
|
+
const fileName = path.basename(relPath)
|
|
189
|
+
const dir = path.dirname(relPath)
|
|
190
|
+
|
|
191
|
+
// Apply prefix if configured (e.g. ecc-agent- prefix)
|
|
192
|
+
const installedName = source.prefix
|
|
193
|
+
? `${source.prefix}${fileName}`
|
|
194
|
+
: fileName
|
|
195
|
+
|
|
196
|
+
// Build the installed relative path
|
|
197
|
+
const installedRelPath = targetDir === '.'
|
|
198
|
+
? (dir === '.' ? installedName : `${dir}/${installedName}`)
|
|
199
|
+
: (dir === '.' ? `${targetDir}/${installedName}` : `${targetDir}/${dir}/${installedName}`)
|
|
200
|
+
|
|
201
|
+
const normalizedPath = installedRelPath.replace(/\\/g, '/')
|
|
202
|
+
|
|
203
|
+
if (!(normalizedPath in fileRegistry)) {
|
|
204
|
+
newFiles.push({
|
|
205
|
+
relPath: normalizedPath,
|
|
206
|
+
source: {
|
|
207
|
+
srcAbsPath: path.join(source.templateDir, relPath),
|
|
208
|
+
adapter: source.adapter,
|
|
209
|
+
},
|
|
210
|
+
})
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return newFiles
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Run the update command.
|
|
220
|
+
*
|
|
221
|
+
* Reads the installed manifest, compares current file hashes against
|
|
222
|
+
* the recorded fileRegistry, and reports which files are unchanged,
|
|
223
|
+
* modified by the user, or missing.
|
|
224
|
+
*
|
|
225
|
+
* When --apply is set, reinstalls files from cckit templates.
|
|
226
|
+
*
|
|
227
|
+
* @param {object} opts - Command options
|
|
228
|
+
* @param {string} [opts.dir] - Target directory (default: cwd)
|
|
229
|
+
* @param {boolean} [opts.dryRun] - Preview changes without applying
|
|
230
|
+
* @param {boolean} [opts.apply] - Reinstall files from templates
|
|
231
|
+
* @param {boolean} [opts.force] - Force overwrite modified files without backup
|
|
232
|
+
* @param {boolean} [opts.debug] - Enable debug output
|
|
233
|
+
* @returns {Promise<object>} Result with { checked, modified, unchanged, missing, applied? }
|
|
234
|
+
* @throws {Error} If cckit is not installed
|
|
235
|
+
*/
|
|
236
|
+
export async function runUpdate(opts = {}) {
|
|
237
|
+
const logger = createLogger({ debug: opts.debug })
|
|
238
|
+
const targetDir = path.resolve(opts.dir || process.cwd())
|
|
239
|
+
const manifestPath = path.join(targetDir, '_bmad', '_config', 'manifest.yaml')
|
|
240
|
+
|
|
241
|
+
if (!await fse.pathExists(manifestPath)) {
|
|
242
|
+
throw new Error('cckit is not installed. Run `cckit init` first.')
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const manifestContent = await fse.readFile(manifestPath, 'utf8')
|
|
246
|
+
const manifest = YAML.parse(manifestContent)
|
|
247
|
+
|
|
248
|
+
logger.banner('cckit update')
|
|
249
|
+
|
|
250
|
+
const fileRegistry = extractFileRegistry(manifest)
|
|
251
|
+
const modified = []
|
|
252
|
+
const unchanged = []
|
|
253
|
+
const missing = []
|
|
254
|
+
|
|
255
|
+
for (const [relPath, expectedHash] of Object.entries(fileRegistry)) {
|
|
256
|
+
const fullPath = path.join(targetDir, relPath)
|
|
257
|
+
|
|
258
|
+
if (!await fse.pathExists(fullPath)) {
|
|
259
|
+
missing.push(relPath)
|
|
260
|
+
continue
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const currentHash = await computeFileHash(fullPath)
|
|
264
|
+
if (currentHash !== expectedHash) {
|
|
265
|
+
modified.push(relPath)
|
|
266
|
+
} else {
|
|
267
|
+
unchanged.push(relPath)
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Report results
|
|
272
|
+
const totalFiles = Object.keys(fileRegistry).length
|
|
273
|
+
if (totalFiles > 0) {
|
|
274
|
+
logger.info(` Checked ${totalFiles} registered file(s)`)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (unchanged.length > 0) {
|
|
278
|
+
logger.success(`${unchanged.length} file(s) unchanged`)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (modified.length > 0) {
|
|
282
|
+
logger.warn(`${modified.length} file(s) modified by user:`)
|
|
283
|
+
for (const f of modified) {
|
|
284
|
+
logger.info(` ${f}`)
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (missing.length > 0) {
|
|
289
|
+
logger.warn(`${missing.length} file(s) missing:`)
|
|
290
|
+
for (const f of missing) {
|
|
291
|
+
logger.info(` ${f}`)
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (totalFiles === 0) {
|
|
296
|
+
logger.info(' No files registered in manifest.')
|
|
297
|
+
} else if (modified.length === 0 && missing.length === 0) {
|
|
298
|
+
logger.success('All files match. Nothing to update.')
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (opts.dryRun) {
|
|
302
|
+
logger.info('\n (dry run - no changes made)')
|
|
303
|
+
return { checked: true, modified, unchanged, missing }
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Apply mode: reinstall files from templates
|
|
307
|
+
if (opts.apply) {
|
|
308
|
+
const applied = { updated: 0, backedUp: 0, restored: 0, added: 0, failed: 0, orphaned: 0 }
|
|
309
|
+
|
|
310
|
+
let registry
|
|
311
|
+
let sourceMap
|
|
312
|
+
let variables
|
|
313
|
+
|
|
314
|
+
try {
|
|
315
|
+
registry = await loadRegistry()
|
|
316
|
+
const config = manifest.cckit && manifest.cckit.config
|
|
317
|
+
? manifest.cckit.config
|
|
318
|
+
: {}
|
|
319
|
+
|
|
320
|
+
sourceMap = buildSourceMap(registry, config)
|
|
321
|
+
variables = {
|
|
322
|
+
user_name: config.user_name || '',
|
|
323
|
+
communication_language: config.communication_language || '',
|
|
324
|
+
document_output_language: config.document_output_language || '',
|
|
325
|
+
languages: Array.isArray(config.languages)
|
|
326
|
+
? config.languages.join(', ')
|
|
327
|
+
: (config.languages || ''),
|
|
328
|
+
'project-root': '.'
|
|
329
|
+
}
|
|
330
|
+
} catch (err) {
|
|
331
|
+
logger.debug(`Failed to load registry for apply: ${err.message}`)
|
|
332
|
+
// Return result with empty applied if registry cannot be loaded
|
|
333
|
+
if (manifest.cckit) {
|
|
334
|
+
manifest.cckit.lastUpdated = new Date().toISOString()
|
|
335
|
+
}
|
|
336
|
+
await fse.writeFile(manifestPath, YAML.stringify(manifest), 'utf8')
|
|
337
|
+
logger.success('Update complete.')
|
|
338
|
+
return { checked: true, modified, unchanged, missing, applied }
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Process all files: unchanged + modified + missing
|
|
342
|
+
const allFiles = [...unchanged, ...modified, ...missing]
|
|
343
|
+
|
|
344
|
+
for (const relPath of allFiles) {
|
|
345
|
+
const source = findSourceForFile(relPath, sourceMap)
|
|
346
|
+
if (!source || !await fse.pathExists(source.srcAbsPath)) {
|
|
347
|
+
logger.debug(`No source template found for: ${relPath} (orphaned)`)
|
|
348
|
+
applied.orphaned += 1
|
|
349
|
+
// Remove orphaned file from registry so it's no longer tracked
|
|
350
|
+
delete fileRegistry[relPath]
|
|
351
|
+
continue
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const fullPath = path.join(targetDir, relPath)
|
|
355
|
+
const isModified = modified.includes(relPath)
|
|
356
|
+
|
|
357
|
+
// Backup modified files unless --force
|
|
358
|
+
if (isModified && !opts.force) {
|
|
359
|
+
const bakPath = `${fullPath}.bak.${Date.now()}`
|
|
360
|
+
try {
|
|
361
|
+
await fse.copy(fullPath, bakPath)
|
|
362
|
+
applied.backedUp += 1
|
|
363
|
+
logger.debug(`Backed up: ${relPath} -> ${path.basename(bakPath)}`)
|
|
364
|
+
} catch (err) {
|
|
365
|
+
logger.debug(`Failed to backup ${relPath}: ${err.message}`)
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Reinstall file
|
|
370
|
+
const success = await reinstallFile(targetDir, relPath, source, variables)
|
|
371
|
+
if (success) {
|
|
372
|
+
applied.updated += 1
|
|
373
|
+
// Recompute hash for updated file
|
|
374
|
+
const newHash = await computeFileHash(fullPath)
|
|
375
|
+
fileRegistry[relPath] = newHash
|
|
376
|
+
|
|
377
|
+
if (missing.includes(relPath)) {
|
|
378
|
+
applied.restored += 1
|
|
379
|
+
}
|
|
380
|
+
} else {
|
|
381
|
+
applied.failed += 1
|
|
382
|
+
logger.debug(`Source not found for: ${relPath}`)
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Discover and install new files from templates not yet in fileRegistry
|
|
387
|
+
const newFiles = await discoverNewFiles(sourceMap, fileRegistry)
|
|
388
|
+
if (newFiles.length > 0) {
|
|
389
|
+
logger.info(` Discovered ${newFiles.length} new file(s) from updated templates`)
|
|
390
|
+
for (const { relPath, source } of newFiles) {
|
|
391
|
+
const success = await reinstallFile(targetDir, relPath, source, variables)
|
|
392
|
+
if (success) {
|
|
393
|
+
applied.added += 1
|
|
394
|
+
const fullPath = path.join(targetDir, relPath)
|
|
395
|
+
const newHash = await computeFileHash(fullPath)
|
|
396
|
+
fileRegistry[relPath] = newHash
|
|
397
|
+
} else {
|
|
398
|
+
applied.failed += 1
|
|
399
|
+
logger.debug(`Failed to install new file: ${relPath}`)
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Update manifest with new fileRegistry
|
|
405
|
+
if (manifest.cckit) {
|
|
406
|
+
manifest.cckit.fileRegistry = fileRegistry
|
|
407
|
+
manifest.cckit.lastUpdated = new Date().toISOString()
|
|
408
|
+
}
|
|
409
|
+
await fse.writeFile(manifestPath, YAML.stringify(manifest), 'utf8')
|
|
410
|
+
|
|
411
|
+
logger.newline()
|
|
412
|
+
if (applied.orphaned > 0) {
|
|
413
|
+
logger.warn(`${applied.orphaned} file(s) orphaned (module removed, no longer tracked)`)
|
|
414
|
+
}
|
|
415
|
+
logger.success(
|
|
416
|
+
`Applied: ${applied.updated} updated, ${applied.added} added, ${applied.restored} restored, ${applied.backedUp} backed up, ${applied.failed} failed`
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
return { checked: true, modified, unchanged, missing, applied }
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Non-dry-run, non-apply: update the lastUpdated timestamp only
|
|
423
|
+
if (manifest.cckit) {
|
|
424
|
+
manifest.cckit.lastUpdated = new Date().toISOString()
|
|
425
|
+
}
|
|
426
|
+
await fse.writeFile(manifestPath, YAML.stringify(manifest), 'utf8')
|
|
427
|
+
|
|
428
|
+
logger.success('Update complete.')
|
|
429
|
+
return { checked: true, modified, unchanged, missing }
|
|
430
|
+
}
|