agileflow 3.4.3 → 4.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +238 -473
- package/README.md +22 -114
- package/bin/agileflow.js +15 -0
- package/bin/hooks/pre-bash.js +35 -0
- package/bin/hooks/pre-compact.js +34 -0
- package/bin/hooks/pre-edit.js +32 -0
- package/bin/hooks/pre-write.js +32 -0
- package/bin/hooks/session-start.js +42 -0
- package/bin/hooks/stop.js +34 -0
- package/content/plugins/ads/plugin.yaml +14 -0
- package/content/plugins/audit/plugin.yaml +14 -0
- package/content/plugins/core/hooks/babysit-mentor-injector.js +55 -0
- package/content/plugins/core/hooks/context-loader.js +169 -0
- package/content/plugins/core/hooks/damage-control-bash.js +78 -0
- package/content/plugins/core/hooks/damage-control-edit.js +76 -0
- package/content/plugins/core/hooks/damage-control-patterns.yaml +100 -0
- package/content/plugins/core/hooks/damage-control-write.js +72 -0
- package/content/plugins/core/hooks/pre-compact-state.js +90 -0
- package/content/plugins/core/hooks/session-welcome.js +19 -0
- package/content/plugins/core/plugin.yaml +82 -0
- package/content/plugins/core/skills/agileflow-adr/SKILL.md +179 -0
- package/content/plugins/core/skills/agileflow-babysit-mentor/SKILL.md +144 -0
- package/content/plugins/core/skills/agileflow-epic-planner/SKILL.md +179 -0
- package/content/plugins/core/skills/agileflow-status-updater/SKILL.md +132 -0
- package/content/plugins/core/skills/agileflow-story-writer/SKILL.md +200 -0
- package/content/plugins/council/plugin.yaml +14 -0
- package/content/plugins/seo/plugin.yaml +14 -0
- package/package.json +29 -49
- package/src/cli/commands/doctor.js +159 -0
- package/src/cli/commands/hook.js +80 -0
- package/src/cli/commands/setup.js +292 -0
- package/src/cli/commands/status.js +47 -0
- package/src/cli/commands/update.js +83 -0
- package/src/cli/index.js +73 -0
- package/src/cli/wizard/behaviors-picker.js +108 -0
- package/src/cli/wizard/ide-picker.js +57 -0
- package/src/cli/wizard/personalization.js +64 -0
- package/src/cli/wizard/plugin-picker.js +106 -0
- package/src/lib/hash.js +41 -0
- package/src/runtime/config/defaults.js +61 -0
- package/src/runtime/config/loader.js +117 -0
- package/src/runtime/config/schema.json +99 -0
- package/src/runtime/config/writer.js +55 -0
- package/src/runtime/hooks/aggregator.js +157 -0
- package/src/runtime/hooks/chain.js +93 -0
- package/src/runtime/hooks/logger.js +68 -0
- package/src/runtime/hooks/manifest-loader.js +228 -0
- package/src/runtime/hooks/orchestrator.js +322 -0
- package/src/runtime/ide/capabilities.js +111 -0
- package/src/runtime/ide/claude-code-settings.js +234 -0
- package/src/runtime/ide/claude-code-skills.js +202 -0
- package/src/runtime/installer/file-index.js +112 -0
- package/src/runtime/installer/install.js +329 -0
- package/src/runtime/installer/stash.js +61 -0
- package/src/runtime/installer/sync-engine.js +205 -0
- package/src/runtime/plugins/registry.js +132 -0
- package/src/runtime/plugins/resolver.js +138 -0
- package/src/runtime/plugins/validator.js +196 -0
- package/src/runtime/skills/validator.js +335 -0
- package/lib/README.md +0 -178
- package/lib/api-routes.js +0 -625
- package/lib/api-server.js +0 -278
- package/lib/cache-provider.js +0 -155
- package/lib/codebase-indexer.js +0 -819
- package/lib/colors.generated.js +0 -117
- package/lib/colors.js +0 -341
- package/lib/consent.js +0 -232
- package/lib/content-sanitizer.js +0 -464
- package/lib/correlation.js +0 -277
- package/lib/drivers/claude-driver.ts +0 -312
- package/lib/drivers/codex-driver.ts +0 -464
- package/lib/drivers/driver-manager.ts +0 -159
- package/lib/drivers/gemini-driver.ts +0 -498
- package/lib/drivers/index.ts +0 -17
- package/lib/error-codes.js +0 -590
- package/lib/errors.js +0 -670
- package/lib/feature-flags.js +0 -171
- package/lib/feedback.js +0 -595
- package/lib/file-cache.js +0 -541
- package/lib/flag-detection.js +0 -344
- package/lib/format-error.js +0 -156
- package/lib/gate-runner.js +0 -282
- package/lib/generator-factory.js +0 -333
- package/lib/git-operations.js +0 -266
- package/lib/lazy-require.js +0 -59
- package/lib/lock-file.js +0 -144
- package/lib/logger.js +0 -106
- package/lib/merge-operations.js +0 -1006
- package/lib/path-resolver.js +0 -544
- package/lib/path-utils.js +0 -49
- package/lib/paths.js +0 -291
- package/lib/placeholder-registry.js +0 -822
- package/lib/process-executor.js +0 -214
- package/lib/progress.js +0 -334
- package/lib/protocol/driver.ts +0 -354
- package/lib/protocol/index.ts +0 -12
- package/lib/protocol/ir.ts +0 -271
- package/lib/registry-cache.js +0 -80
- package/lib/registry-di.js +0 -358
- package/lib/result-schema.js +0 -363
- package/lib/result.js +0 -210
- package/lib/session-display.js +0 -331
- package/lib/session-operations.js +0 -611
- package/lib/session-registry.js +0 -484
- package/lib/session-state-machine.js +0 -465
- package/lib/session-switching.js +0 -191
- package/lib/skill-loader.js +0 -213
- package/lib/smart-json-file.js +0 -682
- package/lib/state-machine.js +0 -286
- package/lib/table-formatter.js +0 -519
- package/lib/template-loader.js +0 -143
- package/lib/transient-status.js +0 -374
- package/lib/ui-manager.js +0 -612
- package/lib/validate-args.js +0 -213
- package/lib/validate-commands.js +0 -308
- package/lib/validate-names.js +0 -143
- package/lib/validate-paths.js +0 -434
- package/lib/validate.js +0 -134
- package/lib/worktree-operations.js +0 -201
- package/lib/yaml-utils.js +0 -164
- package/scripts/README.md +0 -267
- package/scripts/af +0 -34
- package/scripts/agent-loop.js +0 -879
- package/scripts/agileflow-configure.js +0 -368
- package/scripts/agileflow-statusline.sh +0 -857
- package/scripts/agileflow-welcome.js +0 -2246
- package/scripts/api-server-runner.js +0 -177
- package/scripts/archive-completed-stories.sh +0 -308
- package/scripts/auto-self-improve.js +0 -326
- package/scripts/automation-run-due.js +0 -128
- package/scripts/babysit-clear-restore.js +0 -154
- package/scripts/babysit-context-restore.js +0 -89
- package/scripts/backfill-ideation-status.js +0 -128
- package/scripts/batch-pmap-loop.js +0 -551
- package/scripts/check-sessions.js +0 -116
- package/scripts/check-update.js +0 -282
- package/scripts/ci-summary.js +0 -294
- package/scripts/claude-smart.sh +0 -85
- package/scripts/claude-tmux.sh +0 -737
- package/scripts/claude-watchdog.sh +0 -225
- package/scripts/clear-active-command.js +0 -48
- package/scripts/compress-status.sh +0 -116
- package/scripts/context-loader.js +0 -310
- package/scripts/damage-control/bash-tool-damage-control.js +0 -22
- package/scripts/damage-control/edit-tool-damage-control.js +0 -19
- package/scripts/damage-control/patterns.yaml +0 -227
- package/scripts/damage-control/write-tool-damage-control.js +0 -19
- package/scripts/damage-control-bash.js +0 -51
- package/scripts/damage-control-edit.js +0 -48
- package/scripts/damage-control-multi-agent.js +0 -231
- package/scripts/damage-control-write.js +0 -48
- package/scripts/dependency-check.js +0 -311
- package/scripts/document-repl.js +0 -793
- package/scripts/expertise-metrics.sh +0 -264
- package/scripts/generate-all.sh +0 -77
- package/scripts/generate-colors.js +0 -314
- package/scripts/generators/agent-registry.js +0 -183
- package/scripts/generators/command-registry.js +0 -166
- package/scripts/generators/index.js +0 -85
- package/scripts/generators/inject-babysit.js +0 -191
- package/scripts/generators/inject-help.js +0 -125
- package/scripts/generators/inject-readme.js +0 -166
- package/scripts/generators/skill-registry.js +0 -188
- package/scripts/get-env.js +0 -225
- package/scripts/init.sh +0 -76
- package/scripts/lib/README-portable-tasks.md +0 -424
- package/scripts/lib/ac-test-matcher.js +0 -452
- package/scripts/lib/audit-cleanup.js +0 -250
- package/scripts/lib/audit-registry.js +0 -340
- package/scripts/lib/automation-registry.js +0 -544
- package/scripts/lib/automation-runner.js +0 -476
- package/scripts/lib/browser-qa-evidence.js +0 -409
- package/scripts/lib/browser-qa-status.js +0 -192
- package/scripts/lib/bus-utils.js +0 -473
- package/scripts/lib/colors.generated.sh +0 -82
- package/scripts/lib/colors.sh +0 -46
- package/scripts/lib/command-prereqs.js +0 -280
- package/scripts/lib/concurrency-limiter.js +0 -511
- package/scripts/lib/configure-detect.js +0 -596
- package/scripts/lib/configure-features.js +0 -1927
- package/scripts/lib/configure-repair.js +0 -327
- package/scripts/lib/configure-utils.js +0 -114
- package/scripts/lib/context-formatter.js +0 -1158
- package/scripts/lib/context-loader.js +0 -840
- package/scripts/lib/counter.js +0 -103
- package/scripts/lib/damage-control-utils.js +0 -619
- package/scripts/lib/feature-catalog.js +0 -332
- package/scripts/lib/file-lock.js +0 -392
- package/scripts/lib/file-tracking.js +0 -735
- package/scripts/lib/frontmatter-parser.js +0 -133
- package/scripts/lib/gate-enforcer.js +0 -295
- package/scripts/lib/hook-metrics.js +0 -324
- package/scripts/lib/ideation-index.js +0 -1205
- package/scripts/lib/json-utils.sh +0 -162
- package/scripts/lib/lifecycle-detector.js +0 -125
- package/scripts/lib/model-profiles.js +0 -118
- package/scripts/lib/portable-tasks-cli.js +0 -274
- package/scripts/lib/portable-tasks.js +0 -479
- package/scripts/lib/process-cleanup.js +0 -527
- package/scripts/lib/quality-gates.js +0 -788
- package/scripts/lib/scale-detector.js +0 -396
- package/scripts/lib/sessionRegistry.js +0 -678
- package/scripts/lib/signal-detectors.js +0 -867
- package/scripts/lib/skill-catalog.js +0 -557
- package/scripts/lib/skill-recommender.js +0 -311
- package/scripts/lib/state-migrator.js +0 -353
- package/scripts/lib/status-task-bridge.js +0 -522
- package/scripts/lib/status-writer.js +0 -255
- package/scripts/lib/story-claiming.js +0 -704
- package/scripts/lib/story-state-machine.js +0 -437
- package/scripts/lib/sync-ideation-status.js +0 -291
- package/scripts/lib/task-registry-cache.js +0 -490
- package/scripts/lib/task-registry.js +0 -1191
- package/scripts/lib/task-sync.js +0 -230
- package/scripts/lib/tdd-phase-manager.js +0 -455
- package/scripts/lib/team-events.js +0 -510
- package/scripts/lib/tmux-audit-monitor.js +0 -612
- package/scripts/lib/tmux-group-colors.js +0 -113
- package/scripts/lib/tool-registry.yaml +0 -241
- package/scripts/lib/tool-shed.js +0 -441
- package/scripts/lib/validation-registry.js +0 -177
- package/scripts/messaging-bridge.js +0 -561
- package/scripts/migrate-ideation-index.js +0 -553
- package/scripts/native-team-observer.js +0 -219
- package/scripts/obtain-context.js +0 -272
- package/scripts/pre-push-check.sh +0 -46
- package/scripts/precompact-context.sh +0 -306
- package/scripts/query-codebase.js +0 -543
- package/scripts/ralph-loop.js +0 -1278
- package/scripts/resume-session.sh +0 -121
- package/scripts/screenshot-verifier.js +0 -215
- package/scripts/session-boundary.js +0 -138
- package/scripts/session-coordinator.sh +0 -232
- package/scripts/session-manager.js +0 -546
- package/scripts/smart-detect.js +0 -449
- package/scripts/spawn-audit-sessions.js +0 -877
- package/scripts/spawn-parallel.js +0 -751
- package/scripts/strip-ai-attribution.js +0 -63
- package/scripts/task-completed-gate.js +0 -237
- package/scripts/team-manager.js +0 -596
- package/scripts/team-status-display.js +0 -200
- package/scripts/teammate-idle-gate.js +0 -237
- package/scripts/test-session-boundary.js +0 -80
- package/scripts/tmux-close-windows.sh +0 -180
- package/scripts/tmux-restore-window.sh +0 -67
- package/scripts/tmux-save-closed-window.sh +0 -35
- package/scripts/tui/App.js +0 -151
- package/scripts/tui/Dashboard.js +0 -277
- package/scripts/tui/blessed/data/watcher.js +0 -180
- package/scripts/tui/blessed/index.js +0 -244
- package/scripts/tui/blessed/panels/output.js +0 -101
- package/scripts/tui/blessed/panels/sessions.js +0 -150
- package/scripts/tui/blessed/panels/trace.js +0 -97
- package/scripts/tui/blessed/ui/help.js +0 -77
- package/scripts/tui/blessed/ui/screen.js +0 -52
- package/scripts/tui/blessed/ui/statusbar.js +0 -47
- package/scripts/tui/blessed/ui/tabbar.js +0 -99
- package/scripts/tui/index.js +0 -70
- package/scripts/tui/lib/crashRecovery.js +0 -304
- package/scripts/tui/lib/eventStream.js +0 -309
- package/scripts/tui/lib/keyboard.js +0 -261
- package/scripts/tui/lib/loopControl.js +0 -371
- package/scripts/tui/panels/OutputPanel.js +0 -240
- package/scripts/tui/panels/SessionPanel.js +0 -170
- package/scripts/tui/panels/TracePanel.js +0 -298
- package/scripts/tui/simple-tui.js +0 -510
- package/scripts/validate-expertise.sh +0 -263
- package/scripts/validate-tokens.sh +0 -73
- package/scripts/validators/README.md +0 -143
- package/scripts/validators/component-validator.js +0 -239
- package/scripts/validators/json-schema-validator.js +0 -186
- package/scripts/validators/markdown-validator.js +0 -152
- package/scripts/validators/migration-validator.js +0 -129
- package/scripts/validators/security-validator.js +0 -380
- package/scripts/validators/story-format-validator.js +0 -197
- package/scripts/validators/test-result-validator.js +0 -114
- package/scripts/validators/workflow-validator.js +0 -247
- package/scripts/welcome-deferred.js +0 -437
- package/scripts/worktree-create.sh +0 -111
- package/src/core/agents/a11y-analyzer-aria.md +0 -155
- package/src/core/agents/a11y-analyzer-forms.md +0 -162
- package/src/core/agents/a11y-analyzer-keyboard.md +0 -175
- package/src/core/agents/a11y-analyzer-semantic.md +0 -153
- package/src/core/agents/a11y-analyzer-visual.md +0 -158
- package/src/core/agents/a11y-consensus.md +0 -248
- package/src/core/agents/accessibility.md +0 -515
- package/src/core/agents/adr-writer.md +0 -463
- package/src/core/agents/ads-audit-budget.md +0 -181
- package/src/core/agents/ads-audit-compliance.md +0 -169
- package/src/core/agents/ads-audit-creative.md +0 -164
- package/src/core/agents/ads-audit-google.md +0 -226
- package/src/core/agents/ads-audit-meta.md +0 -183
- package/src/core/agents/ads-audit-tracking.md +0 -197
- package/src/core/agents/ads-consensus.md +0 -396
- package/src/core/agents/ads-generate.md +0 -145
- package/src/core/agents/ads-performance-tracker.md +0 -197
- package/src/core/agents/analytics.md +0 -617
- package/src/core/agents/api-quality-analyzer-conventions.md +0 -148
- package/src/core/agents/api-quality-analyzer-docs.md +0 -176
- package/src/core/agents/api-quality-analyzer-errors.md +0 -183
- package/src/core/agents/api-quality-analyzer-pagination.md +0 -171
- package/src/core/agents/api-quality-analyzer-versioning.md +0 -143
- package/src/core/agents/api-quality-consensus.md +0 -214
- package/src/core/agents/api-validator.md +0 -183
- package/src/core/agents/api.md +0 -665
- package/src/core/agents/arch-analyzer-circular.md +0 -148
- package/src/core/agents/arch-analyzer-complexity.md +0 -171
- package/src/core/agents/arch-analyzer-coupling.md +0 -146
- package/src/core/agents/arch-analyzer-layering.md +0 -151
- package/src/core/agents/arch-analyzer-patterns.md +0 -162
- package/src/core/agents/arch-consensus.md +0 -227
- package/src/core/agents/brainstorm-analyzer-features.md +0 -169
- package/src/core/agents/brainstorm-analyzer-growth.md +0 -161
- package/src/core/agents/brainstorm-analyzer-integration.md +0 -172
- package/src/core/agents/brainstorm-analyzer-market.md +0 -147
- package/src/core/agents/brainstorm-analyzer-ux.md +0 -167
- package/src/core/agents/brainstorm-consensus.md +0 -237
- package/src/core/agents/browser-qa.md +0 -328
- package/src/core/agents/ci.md +0 -511
- package/src/core/agents/code-reviewer.md +0 -288
- package/src/core/agents/codebase-query.md +0 -266
- package/src/core/agents/completeness-analyzer-api.md +0 -190
- package/src/core/agents/completeness-analyzer-conditional.md +0 -201
- package/src/core/agents/completeness-analyzer-handlers.md +0 -159
- package/src/core/agents/completeness-analyzer-imports.md +0 -159
- package/src/core/agents/completeness-analyzer-routes.md +0 -182
- package/src/core/agents/completeness-analyzer-state.md +0 -188
- package/src/core/agents/completeness-analyzer-stubs.md +0 -198
- package/src/core/agents/completeness-consensus.md +0 -286
- package/src/core/agents/compliance.md +0 -509
- package/src/core/agents/council-advocate.md +0 -206
- package/src/core/agents/council-analyst.md +0 -252
- package/src/core/agents/council-optimist.md +0 -170
- package/src/core/agents/database.md +0 -601
- package/src/core/agents/datamigration.md +0 -699
- package/src/core/agents/design.md +0 -525
- package/src/core/agents/devops.md +0 -720
- package/src/core/agents/documentation.md +0 -504
- package/src/core/agents/epic-planner.md +0 -480
- package/src/core/agents/error-analyzer.md +0 -201
- package/src/core/agents/integrations.md +0 -603
- package/src/core/agents/legal-analyzer-a11y.md +0 -110
- package/src/core/agents/legal-analyzer-ai.md +0 -117
- package/src/core/agents/legal-analyzer-consumer.md +0 -108
- package/src/core/agents/legal-analyzer-content.md +0 -113
- package/src/core/agents/legal-analyzer-international.md +0 -115
- package/src/core/agents/legal-analyzer-licensing.md +0 -115
- package/src/core/agents/legal-analyzer-privacy.md +0 -108
- package/src/core/agents/legal-analyzer-security.md +0 -112
- package/src/core/agents/legal-analyzer-terms.md +0 -111
- package/src/core/agents/legal-consensus.md +0 -242
- package/src/core/agents/logic-analyzer-edge.md +0 -170
- package/src/core/agents/logic-analyzer-flow.md +0 -253
- package/src/core/agents/logic-analyzer-invariant.md +0 -206
- package/src/core/agents/logic-analyzer-race.md +0 -266
- package/src/core/agents/logic-analyzer-type.md +0 -217
- package/src/core/agents/logic-consensus.md +0 -253
- package/src/core/agents/mentor.md +0 -654
- package/src/core/agents/mobile.md +0 -501
- package/src/core/agents/monitoring.md +0 -537
- package/src/core/agents/multi-expert.md +0 -311
- package/src/core/agents/orchestrator.md +0 -749
- package/src/core/agents/perf-analyzer-assets.md +0 -174
- package/src/core/agents/perf-analyzer-bundle.md +0 -165
- package/src/core/agents/perf-analyzer-caching.md +0 -160
- package/src/core/agents/perf-analyzer-compute.md +0 -165
- package/src/core/agents/perf-analyzer-memory.md +0 -182
- package/src/core/agents/perf-analyzer-network.md +0 -157
- package/src/core/agents/perf-analyzer-queries.md +0 -155
- package/src/core/agents/perf-analyzer-rendering.md +0 -156
- package/src/core/agents/perf-consensus.md +0 -280
- package/src/core/agents/performance.md +0 -492
- package/src/core/agents/product.md +0 -535
- package/src/core/agents/qa.md +0 -765
- package/src/core/agents/readme-updater.md +0 -579
- package/src/core/agents/refactor.md +0 -558
- package/src/core/agents/research.md +0 -453
- package/src/core/agents/rlm-subcore.md +0 -207
- package/src/core/agents/schema-validator.md +0 -454
- package/src/core/agents/security-analyzer-api.md +0 -199
- package/src/core/agents/security-analyzer-auth.md +0 -160
- package/src/core/agents/security-analyzer-authz.md +0 -168
- package/src/core/agents/security-analyzer-deps.md +0 -147
- package/src/core/agents/security-analyzer-infra.md +0 -176
- package/src/core/agents/security-analyzer-injection.md +0 -148
- package/src/core/agents/security-analyzer-input.md +0 -191
- package/src/core/agents/security-analyzer-secrets.md +0 -175
- package/src/core/agents/security-consensus.md +0 -276
- package/src/core/agents/security.md +0 -486
- package/src/core/agents/seo-analyzer-content.md +0 -167
- package/src/core/agents/seo-analyzer-images.md +0 -187
- package/src/core/agents/seo-analyzer-performance.md +0 -206
- package/src/core/agents/seo-analyzer-schema.md +0 -176
- package/src/core/agents/seo-analyzer-sitemap.md +0 -172
- package/src/core/agents/seo-analyzer-technical.md +0 -144
- package/src/core/agents/seo-consensus.md +0 -289
- package/src/core/agents/team-coordinator.md +0 -333
- package/src/core/agents/team-lead.md +0 -171
- package/src/core/agents/test-analyzer-assertions.md +0 -181
- package/src/core/agents/test-analyzer-coverage.md +0 -183
- package/src/core/agents/test-analyzer-fragility.md +0 -185
- package/src/core/agents/test-analyzer-integration.md +0 -155
- package/src/core/agents/test-analyzer-maintenance.md +0 -173
- package/src/core/agents/test-analyzer-mocking.md +0 -178
- package/src/core/agents/test-analyzer-patterns.md +0 -189
- package/src/core/agents/test-analyzer-structure.md +0 -177
- package/src/core/agents/test-consensus.md +0 -294
- package/src/core/agents/testing.md +0 -527
- package/src/core/agents/ui-validator.md +0 -331
- package/src/core/agents/ui.md +0 -1227
- package/src/core/commands/adr/list.md +0 -191
- package/src/core/commands/adr/update.md +0 -258
- package/src/core/commands/adr/view.md +0 -274
- package/src/core/commands/adr.md +0 -394
- package/src/core/commands/ads/audit.md +0 -453
- package/src/core/commands/ads/budget.md +0 -97
- package/src/core/commands/ads/competitor.md +0 -112
- package/src/core/commands/ads/creative.md +0 -85
- package/src/core/commands/ads/generate.md +0 -238
- package/src/core/commands/ads/google.md +0 -112
- package/src/core/commands/ads/health.md +0 -327
- package/src/core/commands/ads/landing.md +0 -119
- package/src/core/commands/ads/linkedin.md +0 -112
- package/src/core/commands/ads/meta.md +0 -91
- package/src/core/commands/ads/microsoft.md +0 -115
- package/src/core/commands/ads/plan.md +0 -321
- package/src/core/commands/ads/test-plan.md +0 -317
- package/src/core/commands/ads/tiktok.md +0 -129
- package/src/core/commands/ads/track.md +0 -288
- package/src/core/commands/ads/youtube.md +0 -124
- package/src/core/commands/ads.md +0 -140
- package/src/core/commands/agent.md +0 -256
- package/src/core/commands/api.md +0 -267
- package/src/core/commands/assign.md +0 -369
- package/src/core/commands/audit.md +0 -531
- package/src/core/commands/auto.md +0 -556
- package/src/core/commands/automate.md +0 -415
- package/src/core/commands/babysit.md +0 -643
- package/src/core/commands/baseline.md +0 -743
- package/src/core/commands/batch.md +0 -551
- package/src/core/commands/blockers.md +0 -602
- package/src/core/commands/board.md +0 -509
- package/src/core/commands/browser-qa.md +0 -240
- package/src/core/commands/changelog.md +0 -582
- package/src/core/commands/choose.md +0 -430
- package/src/core/commands/ci.md +0 -330
- package/src/core/commands/code/accessibility.md +0 -363
- package/src/core/commands/code/api.md +0 -313
- package/src/core/commands/code/architecture.md +0 -313
- package/src/core/commands/code/completeness.md +0 -519
- package/src/core/commands/code/legal.md +0 -509
- package/src/core/commands/code/logic.md +0 -432
- package/src/core/commands/code/performance.md +0 -506
- package/src/core/commands/code/security.md +0 -509
- package/src/core/commands/code/test.md +0 -505
- package/src/core/commands/compress.md +0 -408
- package/src/core/commands/configure.md +0 -1159
- package/src/core/commands/context/export.md +0 -296
- package/src/core/commands/context/full.md +0 -353
- package/src/core/commands/context/note.md +0 -380
- package/src/core/commands/council.md +0 -592
- package/src/core/commands/debt.md +0 -491
- package/src/core/commands/deploy.md +0 -864
- package/src/core/commands/deps.md +0 -728
- package/src/core/commands/diagnose.md +0 -404
- package/src/core/commands/docs.md +0 -469
- package/src/core/commands/epic/edit.md +0 -213
- package/src/core/commands/epic/list.md +0 -190
- package/src/core/commands/epic/view.md +0 -267
- package/src/core/commands/epic.md +0 -477
- package/src/core/commands/export.md +0 -238
- package/src/core/commands/feedback.md +0 -603
- package/src/core/commands/handoff.md +0 -386
- package/src/core/commands/help.md +0 -194
- package/src/core/commands/ideate/brief.md +0 -363
- package/src/core/commands/ideate/discover.md +0 -399
- package/src/core/commands/ideate/features.md +0 -497
- package/src/core/commands/ideate/history.md +0 -403
- package/src/core/commands/ideate/new.md +0 -900
- package/src/core/commands/impact.md +0 -407
- package/src/core/commands/install.md +0 -529
- package/src/core/commands/learn/explain.md +0 -118
- package/src/core/commands/learn/glossary.md +0 -135
- package/src/core/commands/learn/patterns.md +0 -138
- package/src/core/commands/learn/tour.md +0 -126
- package/src/core/commands/maintain.md +0 -558
- package/src/core/commands/metrics.md +0 -844
- package/src/core/commands/migrate/codemods.md +0 -151
- package/src/core/commands/migrate/plan.md +0 -131
- package/src/core/commands/migrate/scan.md +0 -114
- package/src/core/commands/migrate/validate.md +0 -119
- package/src/core/commands/multi-expert.md +0 -447
- package/src/core/commands/packages.md +0 -535
- package/src/core/commands/pr.md +0 -337
- package/src/core/commands/readme-sync.md +0 -329
- package/src/core/commands/research/analyze.md +0 -798
- package/src/core/commands/research/ask.md +0 -864
- package/src/core/commands/research/import.md +0 -1025
- package/src/core/commands/research/list.md +0 -273
- package/src/core/commands/research/synthesize.md +0 -928
- package/src/core/commands/research/view.md +0 -323
- package/src/core/commands/retro.md +0 -795
- package/src/core/commands/review.md +0 -694
- package/src/core/commands/rlm.md +0 -446
- package/src/core/commands/roadmap/analyze.md +0 -400
- package/src/core/commands/rpi.md +0 -633
- package/src/core/commands/seo/audit.md +0 -444
- package/src/core/commands/seo/competitor.md +0 -174
- package/src/core/commands/seo/content.md +0 -107
- package/src/core/commands/seo/geo.md +0 -229
- package/src/core/commands/seo/hreflang.md +0 -140
- package/src/core/commands/seo/images.md +0 -96
- package/src/core/commands/seo/page.md +0 -198
- package/src/core/commands/seo/plan.md +0 -163
- package/src/core/commands/seo/programmatic.md +0 -131
- package/src/core/commands/seo/references/cwv-thresholds.md +0 -64
- package/src/core/commands/seo/references/eeat-framework.md +0 -110
- package/src/core/commands/seo/references/quality-gates.md +0 -91
- package/src/core/commands/seo/references/schema-types.md +0 -102
- package/src/core/commands/seo/schema.md +0 -183
- package/src/core/commands/seo/sitemap.md +0 -97
- package/src/core/commands/seo/technical.md +0 -100
- package/src/core/commands/seo.md +0 -107
- package/src/core/commands/session/cleanup.md +0 -452
- package/src/core/commands/session/end.md +0 -865
- package/src/core/commands/session/history.md +0 -293
- package/src/core/commands/session/init.md +0 -210
- package/src/core/commands/session/new.md +0 -827
- package/src/core/commands/session/resume.md +0 -291
- package/src/core/commands/session/spawn.md +0 -205
- package/src/core/commands/session/status.md +0 -274
- package/src/core/commands/skill/list.md +0 -139
- package/src/core/commands/skill/recommend.md +0 -216
- package/src/core/commands/sprint.md +0 -714
- package/src/core/commands/status/undo.md +0 -191
- package/src/core/commands/status.md +0 -423
- package/src/core/commands/story/edit.md +0 -204
- package/src/core/commands/story/list.md +0 -199
- package/src/core/commands/story/view.md +0 -312
- package/src/core/commands/story-validate.md +0 -491
- package/src/core/commands/story.md +0 -465
- package/src/core/commands/tdd-next.md +0 -238
- package/src/core/commands/tdd.md +0 -211
- package/src/core/commands/team/guide.md +0 -688
- package/src/core/commands/team/list.md +0 -59
- package/src/core/commands/team/start.md +0 -130
- package/src/core/commands/team/status.md +0 -66
- package/src/core/commands/team/stop.md +0 -78
- package/src/core/commands/template.md +0 -644
- package/src/core/commands/tests.md +0 -731
- package/src/core/commands/update.md +0 -591
- package/src/core/commands/validate-expertise.md +0 -305
- package/src/core/commands/velocity.md +0 -630
- package/src/core/commands/verify.md +0 -534
- package/src/core/commands/whats-new.md +0 -201
- package/src/core/commands/workflow.md +0 -449
- package/src/core/council/sessions/.gitkeep +0 -0
- package/src/core/council/shared_reasoning.template.md +0 -106
- package/src/core/experts/README.md +0 -236
- package/src/core/experts/_core-expertise.yaml +0 -105
- package/src/core/experts/accessibility/expertise.yaml +0 -115
- package/src/core/experts/accessibility/question.md +0 -41
- package/src/core/experts/accessibility/self-improve.md +0 -45
- package/src/core/experts/accessibility/workflow.md +0 -59
- package/src/core/experts/adr-writer/expertise.yaml +0 -138
- package/src/core/experts/adr-writer/question.md +0 -56
- package/src/core/experts/adr-writer/self-improve.md +0 -106
- package/src/core/experts/adr-writer/workflow.md +0 -184
- package/src/core/experts/analytics/expertise.yaml +0 -119
- package/src/core/experts/analytics/question.md +0 -74
- package/src/core/experts/analytics/self-improve.md +0 -163
- package/src/core/experts/analytics/workflow.md +0 -272
- package/src/core/experts/api/expertise.yaml +0 -124
- package/src/core/experts/api/question.md +0 -74
- package/src/core/experts/api/self-improve.md +0 -122
- package/src/core/experts/api/workflow.md +0 -248
- package/src/core/experts/ci/expertise.yaml +0 -106
- package/src/core/experts/ci/question.md +0 -69
- package/src/core/experts/ci/self-improve.md +0 -100
- package/src/core/experts/ci/workflow.md +0 -145
- package/src/core/experts/codebase-query/expertise.yaml +0 -121
- package/src/core/experts/codebase-query/question.md +0 -73
- package/src/core/experts/codebase-query/self-improve.md +0 -105
- package/src/core/experts/compliance/expertise.yaml +0 -101
- package/src/core/experts/compliance/question.md +0 -56
- package/src/core/experts/compliance/self-improve.md +0 -106
- package/src/core/experts/compliance/workflow.md +0 -184
- package/src/core/experts/database/expertise.yaml +0 -109
- package/src/core/experts/database/question.md +0 -74
- package/src/core/experts/database/self-improve.md +0 -121
- package/src/core/experts/database/workflow.md +0 -234
- package/src/core/experts/datamigration/expertise.yaml +0 -141
- package/src/core/experts/datamigration/question.md +0 -56
- package/src/core/experts/datamigration/self-improve.md +0 -106
- package/src/core/experts/datamigration/workflow.md +0 -184
- package/src/core/experts/design/expertise.yaml +0 -116
- package/src/core/experts/design/question.md +0 -56
- package/src/core/experts/design/self-improve.md +0 -106
- package/src/core/experts/design/workflow.md +0 -184
- package/src/core/experts/devops/expertise.yaml +0 -116
- package/src/core/experts/devops/question.md +0 -68
- package/src/core/experts/devops/self-improve.md +0 -102
- package/src/core/experts/devops/workflow.md +0 -142
- package/src/core/experts/documentation/expertise.yaml +0 -126
- package/src/core/experts/documentation/question.md +0 -41
- package/src/core/experts/documentation/self-improve.md +0 -45
- package/src/core/experts/documentation/workflow.md +0 -55
- package/src/core/experts/epic-planner/expertise.yaml +0 -144
- package/src/core/experts/epic-planner/question.md +0 -56
- package/src/core/experts/epic-planner/self-improve.md +0 -106
- package/src/core/experts/epic-planner/workflow.md +0 -184
- package/src/core/experts/integrations/expertise.yaml +0 -113
- package/src/core/experts/integrations/question.md +0 -74
- package/src/core/experts/integrations/self-improve.md +0 -151
- package/src/core/experts/integrations/workflow.md +0 -246
- package/src/core/experts/mentor/expertise.yaml +0 -125
- package/src/core/experts/mentor/question.md +0 -56
- package/src/core/experts/mentor/self-improve.md +0 -106
- package/src/core/experts/mentor/workflow.md +0 -184
- package/src/core/experts/mobile/expertise.yaml +0 -136
- package/src/core/experts/mobile/question.md +0 -72
- package/src/core/experts/mobile/self-improve.md +0 -140
- package/src/core/experts/mobile/workflow.md +0 -240
- package/src/core/experts/monitoring/expertise.yaml +0 -132
- package/src/core/experts/monitoring/question.md +0 -76
- package/src/core/experts/monitoring/self-improve.md +0 -150
- package/src/core/experts/monitoring/workflow.md +0 -264
- package/src/core/experts/performance/expertise.yaml +0 -68
- package/src/core/experts/performance/question.md +0 -41
- package/src/core/experts/performance/self-improve.md +0 -45
- package/src/core/experts/performance/workflow.md +0 -61
- package/src/core/experts/product/expertise.yaml +0 -143
- package/src/core/experts/product/question.md +0 -56
- package/src/core/experts/product/self-improve.md +0 -106
- package/src/core/experts/product/workflow.md +0 -184
- package/src/core/experts/qa/expertise.yaml +0 -110
- package/src/core/experts/qa/question.md +0 -56
- package/src/core/experts/qa/self-improve.md +0 -106
- package/src/core/experts/qa/workflow.md +0 -184
- package/src/core/experts/readme-updater/expertise.yaml +0 -141
- package/src/core/experts/readme-updater/question.md +0 -56
- package/src/core/experts/readme-updater/self-improve.md +0 -106
- package/src/core/experts/readme-updater/workflow.md +0 -184
- package/src/core/experts/refactor/expertise.yaml +0 -135
- package/src/core/experts/refactor/question.md +0 -41
- package/src/core/experts/refactor/self-improve.md +0 -45
- package/src/core/experts/refactor/workflow.md +0 -57
- package/src/core/experts/research/expertise.yaml +0 -143
- package/src/core/experts/research/question.md +0 -56
- package/src/core/experts/research/self-improve.md +0 -106
- package/src/core/experts/research/workflow.md +0 -184
- package/src/core/experts/security/expertise.yaml +0 -117
- package/src/core/experts/security/question.md +0 -77
- package/src/core/experts/security/self-improve.md +0 -102
- package/src/core/experts/security/workflow.md +0 -152
- package/src/core/experts/templates/expertise-template.yaml +0 -67
- package/src/core/experts/templates/question-template.md +0 -56
- package/src/core/experts/templates/self-improve-template.md +0 -106
- package/src/core/experts/templates/workflow-template.md +0 -184
- package/src/core/experts/testing/expertise.yaml +0 -112
- package/src/core/experts/testing/question.md +0 -68
- package/src/core/experts/testing/self-improve.md +0 -102
- package/src/core/experts/testing/workflow.md +0 -143
- package/src/core/experts/ui/expertise.yaml +0 -133
- package/src/core/experts/ui/question.md +0 -74
- package/src/core/experts/ui/self-improve.md +0 -122
- package/src/core/experts/ui/workflow.md +0 -262
- package/src/core/knowledge/ads/ad-audit-checklist-scoring.md +0 -424
- package/src/core/knowledge/ads/ad-optimization-logic.md +0 -590
- package/src/core/knowledge/ads/ad-technical-specifications.md +0 -385
- package/src/core/knowledge/ads/definitive-advertising-reference-2026.md +0 -506
- package/src/core/knowledge/ads/paid-advertising-research-2026.md +0 -445
- package/src/core/profiles/COMPARISON.md +0 -170
- package/src/core/profiles/README.md +0 -178
- package/src/core/profiles/claude-code.yaml +0 -111
- package/src/core/profiles/codex.yaml +0 -103
- package/src/core/profiles/cursor.yaml +0 -134
- package/src/core/profiles/examples.js +0 -250
- package/src/core/profiles/loader.js +0 -235
- package/src/core/profiles/windsurf.yaml +0 -159
- package/src/core/skills/_learnings/README.md +0 -91
- package/src/core/skills/_learnings/_template.yaml +0 -106
- package/src/core/skills/_learnings/code-review.yaml +0 -118
- package/src/core/skills/_learnings/commit.yaml +0 -69
- package/src/core/skills/_learnings/story-writer.yaml +0 -71
- package/src/core/teams/backend.json +0 -41
- package/src/core/teams/builder-validator.json +0 -51
- package/src/core/teams/code-review.json +0 -41
- package/src/core/teams/frontend.json +0 -41
- package/src/core/teams/fullstack.json +0 -41
- package/src/core/teams/logic-audit.json +0 -53
- package/src/core/teams/perf-audit.json +0 -71
- package/src/core/teams/qa.json +0 -41
- package/src/core/teams/security-audit.json +0 -71
- package/src/core/teams/solo.json +0 -35
- package/src/core/teams/test-audit.json +0 -71
- package/src/core/templates/CONTEXT.md.example +0 -49
- package/src/core/templates/README-template.md +0 -16
- package/src/core/templates/adr-template.md +0 -28
- package/src/core/templates/agent-coordination-pattern.md +0 -38
- package/src/core/templates/agent-profile-template.md +0 -51
- package/src/core/templates/agileflow-metadata.json +0 -150
- package/src/core/templates/browser-qa-spec.yaml +0 -94
- package/src/core/templates/ci-workflow.yml +0 -74
- package/src/core/templates/claude-settings.advanced.example.json +0 -75
- package/src/core/templates/claude-settings.example.json +0 -26
- package/src/core/templates/command-documentation.md +0 -187
- package/src/core/templates/command-prerequisites.yaml +0 -169
- package/src/core/templates/comms-note-template.md +0 -24
- package/src/core/templates/damage-control-patterns.yaml +0 -243
- package/src/core/templates/environment.json +0 -18
- package/src/core/templates/epic-template.md +0 -27
- package/src/core/templates/plan-template.md +0 -125
- package/src/core/templates/preserve-rules-common.md +0 -107
- package/src/core/templates/preserve-rules.json +0 -42
- package/src/core/templates/proactive-action-spec.md +0 -29
- package/src/core/templates/product-brief.md +0 -136
- package/src/core/templates/quality-gate-priorities.md +0 -34
- package/src/core/templates/research-template.md +0 -44
- package/src/core/templates/session-harness-protocol.md +0 -128
- package/src/core/templates/session-state.json +0 -56
- package/src/core/templates/story-lifecycle.md +0 -213
- package/src/core/templates/story-template.md +0 -92
- package/src/core/templates/tdd-test-template.js +0 -241
- package/src/core/templates/worktrees-guide.md +0 -231
- package/tools/agileflow-npx.js +0 -52
- package/tools/cli/agileflow-cli.js +0 -72
- package/tools/cli/commands/config.js +0 -285
- package/tools/cli/commands/doctor.js +0 -496
- package/tools/cli/commands/list.js +0 -385
- package/tools/cli/commands/session.js +0 -1176
- package/tools/cli/commands/setup.js +0 -255
- package/tools/cli/commands/status.js +0 -101
- package/tools/cli/commands/tui.js +0 -56
- package/tools/cli/commands/uninstall.js +0 -155
- package/tools/cli/commands/update.js +0 -299
- package/tools/cli/installers/core/installer.js +0 -892
- package/tools/cli/installers/ide/_base-ide.js +0 -518
- package/tools/cli/installers/ide/_interface.js +0 -238
- package/tools/cli/installers/ide/claude-code.js +0 -432
- package/tools/cli/installers/ide/codex.js +0 -426
- package/tools/cli/installers/ide/cursor.js +0 -217
- package/tools/cli/installers/ide/manager.js +0 -222
- package/tools/cli/installers/ide/windsurf.js +0 -282
- package/tools/cli/lib/command-context.js +0 -382
- package/tools/cli/lib/config-manager.js +0 -446
- package/tools/cli/lib/content-injector.js +0 -969
- package/tools/cli/lib/content-transformer.js +0 -496
- package/tools/cli/lib/docs-setup.js +0 -464
- package/tools/cli/lib/error-handler.js +0 -165
- package/tools/cli/lib/ide-error-factory.js +0 -421
- package/tools/cli/lib/ide-errors.js +0 -367
- package/tools/cli/lib/ide-generator.js +0 -357
- package/tools/cli/lib/ide-health-monitor.js +0 -364
- package/tools/cli/lib/ide-registry.js +0 -297
- package/tools/cli/lib/npm-utils.js +0 -103
- package/tools/cli/lib/self-update.js +0 -148
- package/tools/cli/lib/ui.js +0 -211
- package/tools/cli/lib/utils.js +0 -87
- package/tools/cli/lib/validation-middleware.js +0 -491
- package/tools/cli/lib/version-checker.js +0 -95
- package/tools/postinstall.js +0 -190
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `installPlugins` — end-to-end orchestrator that wires the plugin
|
|
3
|
+
* registry, validator, resolver, and sync engine into a single call.
|
|
4
|
+
*
|
|
5
|
+
* Flow:
|
|
6
|
+
* 1. Strict-validate every discovered plugin (errors → throw with
|
|
7
|
+
* a flat report; warnings ignored here, surfaced by `doctor`).
|
|
8
|
+
* 2. Resolve transitive dependencies + topological order from the
|
|
9
|
+
* user's selection (resolver throws on cycles or missing deps).
|
|
10
|
+
* 3. Read the existing `_cfg/files.json` (or build a fresh index).
|
|
11
|
+
* 4. For each resolved plugin, walk its source directory and
|
|
12
|
+
* `syncFile` every file into `<agileflowDir>/plugins/<id>/...`.
|
|
13
|
+
* 5. Remove directories of previously-installed plugins that are no
|
|
14
|
+
* longer enabled. Their entries are also pruned from the file
|
|
15
|
+
* index.
|
|
16
|
+
* 6. Write the file index atomically.
|
|
17
|
+
*
|
|
18
|
+
* Side effects only on the destination project. The bundled `content/`
|
|
19
|
+
* source tree is read-only.
|
|
20
|
+
*/
|
|
21
|
+
const fs = require("fs");
|
|
22
|
+
const path = require("path");
|
|
23
|
+
|
|
24
|
+
const { syncFile, emptyCounters } = require("./sync-engine.js");
|
|
25
|
+
const {
|
|
26
|
+
emptyIndex,
|
|
27
|
+
readFileIndex,
|
|
28
|
+
writeFileIndex,
|
|
29
|
+
} = require("./file-index.js");
|
|
30
|
+
const { resolvePlugins } = require("../plugins/resolver.js");
|
|
31
|
+
const { validatePluginSet, hasErrors } = require("../plugins/validator.js");
|
|
32
|
+
const {
|
|
33
|
+
writeAggregatedManifest,
|
|
34
|
+
removeAggregatedManifest,
|
|
35
|
+
buildHookManifest,
|
|
36
|
+
} = require("../hooks/aggregator.js");
|
|
37
|
+
const { normalizeManifest } = require("../hooks/manifest-loader.js");
|
|
38
|
+
const { capabilitiesFor } = require("../ide/capabilities.js");
|
|
39
|
+
const {
|
|
40
|
+
writeClaudeCodeSettings,
|
|
41
|
+
removeClaudeCodeSettings,
|
|
42
|
+
} = require("../ide/claude-code-settings.js");
|
|
43
|
+
const {
|
|
44
|
+
mirrorClaudeCodeSkills,
|
|
45
|
+
unmirrorClaudeCodeSkills,
|
|
46
|
+
} = require("../ide/claude-code-skills.js");
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @typedef {import('../plugins/registry.js').PluginManifest} PluginManifest
|
|
50
|
+
*
|
|
51
|
+
* @typedef {Object} InstallOptions
|
|
52
|
+
* @property {PluginManifest[]} discovered
|
|
53
|
+
* @property {Iterable<string>} userSelected
|
|
54
|
+
* @property {string} agileflowDir - target install root (typically `<cwd>/.agileflow`)
|
|
55
|
+
* @property {string} cliVersion - written into the file index header
|
|
56
|
+
* @property {string} [ide='claude-code'] - target IDE for capability gating
|
|
57
|
+
* @property {Record<string, boolean>} [behaviors] - behavior preset toggles
|
|
58
|
+
* @property {boolean} [force=false] - overwrite user modifications
|
|
59
|
+
*
|
|
60
|
+
* @typedef {Object} InstallResult
|
|
61
|
+
* @property {string[]} ordered - plugin ids in install order
|
|
62
|
+
* @property {string[]} autoEnabled - ids pulled in via depends
|
|
63
|
+
* @property {string[]} removed - ids whose dir was removed (no longer enabled)
|
|
64
|
+
* @property {import('./sync-engine.js').FileOpsCounters} ops
|
|
65
|
+
* @property {string} agileflowDir
|
|
66
|
+
* @property {string} indexPath
|
|
67
|
+
* @property {string} timestamp
|
|
68
|
+
* @property {string|null} hookManifestPath - path of the written hook manifest, or null
|
|
69
|
+
* @property {string|null} settingsPath - path of the written .claude/settings.json, or null
|
|
70
|
+
* @property {string[]} skillsMirrored - skill ids copied into .claude/skills/
|
|
71
|
+
* @property {string[]} skillsPruned - skill ids removed from .claude/skills/
|
|
72
|
+
* @property {Array<{skillId:string, error:string}>} [skillsSkipped] - skills with missing source
|
|
73
|
+
* @property {string} ide - the target IDE for this install
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Build a stable file-system-safe timestamp string for stash bucketing.
|
|
78
|
+
* @returns {string}
|
|
79
|
+
*/
|
|
80
|
+
function makeTimestamp() {
|
|
81
|
+
return new Date().toISOString().replace(/[:.]/g, "-");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** @param {string} p */
|
|
85
|
+
function toPosix(p) {
|
|
86
|
+
return p.split(path.sep).join("/");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Yield absolute paths to every file under `dir`, depth-first.
|
|
91
|
+
* @param {string} dir
|
|
92
|
+
* @returns {AsyncGenerator<string>}
|
|
93
|
+
*/
|
|
94
|
+
async function* walkFiles(dir) {
|
|
95
|
+
const entries = await fs.promises.readdir(dir, { withFileTypes: true });
|
|
96
|
+
for (const e of entries) {
|
|
97
|
+
const full = path.join(dir, e.name);
|
|
98
|
+
if (e.isDirectory()) {
|
|
99
|
+
yield* walkFiles(full);
|
|
100
|
+
} else if (e.isFile()) {
|
|
101
|
+
yield full;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Sync every file in `plugin.dir` into `agileflowDir/plugins/<id>/...`.
|
|
108
|
+
*
|
|
109
|
+
* @param {PluginManifest} plugin
|
|
110
|
+
* @param {string} agileflowDir
|
|
111
|
+
* @param {import('./file-index.js').FileIndex} fileIndex
|
|
112
|
+
* @param {import('./sync-engine.js').FileOpsCounters} ops
|
|
113
|
+
* @param {string} timestamp
|
|
114
|
+
* @param {boolean} force
|
|
115
|
+
*/
|
|
116
|
+
async function installOnePlugin(
|
|
117
|
+
plugin,
|
|
118
|
+
agileflowDir,
|
|
119
|
+
fileIndex,
|
|
120
|
+
ops,
|
|
121
|
+
timestamp,
|
|
122
|
+
force,
|
|
123
|
+
) {
|
|
124
|
+
const cfgDir = path.join(agileflowDir, "_cfg");
|
|
125
|
+
const pluginRoot = path.join(agileflowDir, "plugins", plugin.id);
|
|
126
|
+
|
|
127
|
+
for await (const sourcePath of walkFiles(plugin.dir)) {
|
|
128
|
+
const relInPlugin = path.relative(plugin.dir, sourcePath);
|
|
129
|
+
const dest = path.join(pluginRoot, relInPlugin);
|
|
130
|
+
const relativePath = toPosix(path.relative(agileflowDir, dest));
|
|
131
|
+
const content = await fs.promises.readFile(sourcePath);
|
|
132
|
+
await syncFile({
|
|
133
|
+
content,
|
|
134
|
+
dest,
|
|
135
|
+
relativePath,
|
|
136
|
+
fileIndex,
|
|
137
|
+
cfgDir,
|
|
138
|
+
timestamp,
|
|
139
|
+
force,
|
|
140
|
+
ops,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Remove plugin directories for plugins that were previously installed
|
|
147
|
+
* (have entries in the file index under `plugins/<id>/...`) but are
|
|
148
|
+
* NOT in the currently enabled set. Their file index entries are also
|
|
149
|
+
* pruned so a future re-enable produces fresh CREATED records.
|
|
150
|
+
*
|
|
151
|
+
* @param {Set<string>} enabledIds
|
|
152
|
+
* @param {Set<string>} knownIds - all discovered ids (so we don't blow
|
|
153
|
+
* away unknown directories the user might have placed manually)
|
|
154
|
+
* @param {string} agileflowDir
|
|
155
|
+
* @param {import('./file-index.js').FileIndex} fileIndex
|
|
156
|
+
* @param {import('./sync-engine.js').FileOpsCounters} ops
|
|
157
|
+
* @returns {Promise<string[]>} the plugin ids that were removed
|
|
158
|
+
*/
|
|
159
|
+
async function removeDisabledPlugins(
|
|
160
|
+
enabledIds,
|
|
161
|
+
knownIds,
|
|
162
|
+
agileflowDir,
|
|
163
|
+
fileIndex,
|
|
164
|
+
ops,
|
|
165
|
+
) {
|
|
166
|
+
const pluginsRoot = path.join(agileflowDir, "plugins");
|
|
167
|
+
/** @type {string[]} */
|
|
168
|
+
const removed = [];
|
|
169
|
+
let entries;
|
|
170
|
+
try {
|
|
171
|
+
entries = await fs.promises.readdir(pluginsRoot, { withFileTypes: true });
|
|
172
|
+
} catch (err) {
|
|
173
|
+
if (err.code === "ENOENT") return removed;
|
|
174
|
+
throw err;
|
|
175
|
+
}
|
|
176
|
+
for (const e of entries) {
|
|
177
|
+
if (!e.isDirectory()) continue;
|
|
178
|
+
const id = e.name;
|
|
179
|
+
if (enabledIds.has(id)) continue;
|
|
180
|
+
if (!knownIds.has(id)) continue; // leave unknown dirs alone
|
|
181
|
+
const dir = path.join(pluginsRoot, id);
|
|
182
|
+
await fs.promises.rm(dir, { recursive: true, force: true });
|
|
183
|
+
for (const key of Object.keys(fileIndex.files)) {
|
|
184
|
+
if (key.startsWith(`plugins/${id}/`)) {
|
|
185
|
+
delete fileIndex.files[key];
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
removed.push(id);
|
|
189
|
+
ops.removed++;
|
|
190
|
+
}
|
|
191
|
+
return removed;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* @param {InstallOptions} options
|
|
196
|
+
* @returns {Promise<InstallResult>}
|
|
197
|
+
*/
|
|
198
|
+
async function installPlugins(options) {
|
|
199
|
+
const {
|
|
200
|
+
discovered,
|
|
201
|
+
userSelected,
|
|
202
|
+
agileflowDir,
|
|
203
|
+
cliVersion,
|
|
204
|
+
ide = "claude-code",
|
|
205
|
+
behaviors,
|
|
206
|
+
force = false,
|
|
207
|
+
} = options;
|
|
208
|
+
|
|
209
|
+
// 1. Strict-validate. Errors abort; warnings are surfaced elsewhere.
|
|
210
|
+
const issues = validatePluginSet(discovered);
|
|
211
|
+
if (hasErrors(issues)) {
|
|
212
|
+
const errors = issues
|
|
213
|
+
.filter((i) => i.severity === "error")
|
|
214
|
+
.map((i) => ` ${i.pluginId}: ${i.message}`)
|
|
215
|
+
.join("\n");
|
|
216
|
+
throw new Error(`Plugin validation failed:\n${errors}`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// 2. Resolve dependency order.
|
|
220
|
+
const { ordered, autoEnabled } = resolvePlugins(discovered, userSelected);
|
|
221
|
+
|
|
222
|
+
// 3. Read or seed the file index.
|
|
223
|
+
const cfgDir = path.join(agileflowDir, "_cfg");
|
|
224
|
+
const indexPath = path.join(cfgDir, "files.json");
|
|
225
|
+
const fileIndex = (await readFileIndex(indexPath)) || emptyIndex(cliVersion);
|
|
226
|
+
|
|
227
|
+
// 4-6 wrapped in try/finally so the file index is ALWAYS persisted,
|
|
228
|
+
// even if a sync fails mid-loop. Otherwise on-disk files would exist
|
|
229
|
+
// without index entries and the next run would misclassify them as
|
|
230
|
+
// "user-modified" and stash them.
|
|
231
|
+
const ops = emptyCounters();
|
|
232
|
+
const timestamp = makeTimestamp();
|
|
233
|
+
/** @type {string[]} */
|
|
234
|
+
let removed = [];
|
|
235
|
+
|
|
236
|
+
try {
|
|
237
|
+
// 4. Sync each plugin in order.
|
|
238
|
+
for (const plugin of ordered) {
|
|
239
|
+
await installOnePlugin(
|
|
240
|
+
plugin,
|
|
241
|
+
agileflowDir,
|
|
242
|
+
fileIndex,
|
|
243
|
+
ops,
|
|
244
|
+
timestamp,
|
|
245
|
+
force,
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// 5. Remove disabled plugin directories.
|
|
250
|
+
const enabledIds = new Set(ordered.map((p) => p.id));
|
|
251
|
+
const knownIds = new Set(discovered.map((p) => p.id));
|
|
252
|
+
removed = await removeDisabledPlugins(
|
|
253
|
+
enabledIds,
|
|
254
|
+
knownIds,
|
|
255
|
+
agileflowDir,
|
|
256
|
+
fileIndex,
|
|
257
|
+
ops,
|
|
258
|
+
);
|
|
259
|
+
} finally {
|
|
260
|
+
// 6. Persist the file index. Always.
|
|
261
|
+
await writeFileIndex(indexPath, fileIndex);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// 7. Validate-before-write: build the manifest in memory and run
|
|
265
|
+
// the loader's normalizer against it. Surfacing an invalid plugin
|
|
266
|
+
// contribution NOW prevents step 8 from registering hook
|
|
267
|
+
// dispatchers in settings.json that point at an unparseable
|
|
268
|
+
// manifest.
|
|
269
|
+
const caps = capabilitiesFor(ide);
|
|
270
|
+
let hookManifestPath = null;
|
|
271
|
+
if (caps.hooks) {
|
|
272
|
+
const manifestObj = buildHookManifest(ordered, behaviors);
|
|
273
|
+
try {
|
|
274
|
+
normalizeManifest(manifestObj);
|
|
275
|
+
} catch (err) {
|
|
276
|
+
throw new Error(`Hook manifest validation failed: ${err.message}`);
|
|
277
|
+
}
|
|
278
|
+
hookManifestPath = await writeAggregatedManifest(
|
|
279
|
+
ordered,
|
|
280
|
+
agileflowDir,
|
|
281
|
+
behaviors,
|
|
282
|
+
);
|
|
283
|
+
} else {
|
|
284
|
+
await removeAggregatedManifest(agileflowDir);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// 8. Register or unregister our hook dispatchers in
|
|
288
|
+
// `.claude/settings.json`. Only when ide=claude-code.
|
|
289
|
+
const projectRoot = path.dirname(agileflowDir);
|
|
290
|
+
let settingsPath = null;
|
|
291
|
+
if (ide === "claude-code") {
|
|
292
|
+
settingsPath = await writeClaudeCodeSettings(projectRoot);
|
|
293
|
+
} else {
|
|
294
|
+
await removeClaudeCodeSettings(projectRoot);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// 9. Mirror skills into `.claude/skills/<id>/`. Copy (not symlink)
|
|
298
|
+
// for Windows portability. Missing skill sources are skipped
|
|
299
|
+
// gracefully; non-ENOENT errors propagate.
|
|
300
|
+
let skillsMirrored = [];
|
|
301
|
+
let skillsPruned = [];
|
|
302
|
+
let skillsSkipped = [];
|
|
303
|
+
if (caps.skills) {
|
|
304
|
+
const r = await mirrorClaudeCodeSkills(ordered, projectRoot);
|
|
305
|
+
skillsMirrored = r.mirrored;
|
|
306
|
+
skillsPruned = r.pruned;
|
|
307
|
+
skillsSkipped = r.skipped || [];
|
|
308
|
+
} else {
|
|
309
|
+
skillsPruned = await unmirrorClaudeCodeSkills(projectRoot);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return {
|
|
313
|
+
ordered: ordered.map((p) => p.id),
|
|
314
|
+
autoEnabled,
|
|
315
|
+
removed,
|
|
316
|
+
ops,
|
|
317
|
+
agileflowDir,
|
|
318
|
+
indexPath,
|
|
319
|
+
timestamp,
|
|
320
|
+
hookManifestPath,
|
|
321
|
+
settingsPath,
|
|
322
|
+
skillsMirrored,
|
|
323
|
+
skillsPruned,
|
|
324
|
+
skillsSkipped,
|
|
325
|
+
ide,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
module.exports = { installPlugins };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stash writer for the sync engine.
|
|
3
|
+
*
|
|
4
|
+
* When the sync engine encounters a user-modified destination file whose
|
|
5
|
+
* content diverges from the incoming upstream version, it preserves the
|
|
6
|
+
* user's file in place and writes the new version to a stash directory
|
|
7
|
+
* at `<cfgDir>/updates/<timestamp>/<relativePath>` so the user can
|
|
8
|
+
* manually diff and merge.
|
|
9
|
+
*
|
|
10
|
+
* Path-traversal-safe: a malicious or buggy `relativePath` containing
|
|
11
|
+
* `..` segments cannot escape the updates directory. The resolved
|
|
12
|
+
* destination is checked against the resolved updates root and rejected
|
|
13
|
+
* if it points outside.
|
|
14
|
+
*/
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {Object} args
|
|
20
|
+
* @param {string} args.cfgDir - absolute path to .agileflow/_cfg/
|
|
21
|
+
* @param {string} args.timestamp - stable stamp for this sync run
|
|
22
|
+
* @param {string} args.relativePath - posix-style path (no leading slash, no `..`)
|
|
23
|
+
* @param {Buffer|string} args.content - bytes to stash (as would have been written)
|
|
24
|
+
* @returns {Promise<string>} absolute path the stash file was written to
|
|
25
|
+
*/
|
|
26
|
+
async function writeStash({ cfgDir, timestamp, relativePath, content }) {
|
|
27
|
+
// Reject absolute paths up front. `path.join` would otherwise treat
|
|
28
|
+
// `/etc/passwd` as a relative segment, silently producing
|
|
29
|
+
// `<updatesRoot>/etc/passwd` which is technically inside the root
|
|
30
|
+
// but not what the caller intended.
|
|
31
|
+
if (path.isAbsolute(relativePath)) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
`writeStash: relativePath "${relativePath}" escapes the updates directory (must be a relative path)`,
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const updatesRoot = path.resolve(path.join(cfgDir, 'updates', timestamp));
|
|
38
|
+
const stashPath = path.resolve(path.join(updatesRoot, relativePath));
|
|
39
|
+
|
|
40
|
+
// Path-traversal guard: stashPath must be at or under updatesRoot.
|
|
41
|
+
// We check both equality (a relativePath that resolves to the dir
|
|
42
|
+
// itself) and the path.sep prefix (any descendant).
|
|
43
|
+
if (
|
|
44
|
+
stashPath !== updatesRoot &&
|
|
45
|
+
!stashPath.startsWith(updatesRoot + path.sep)
|
|
46
|
+
) {
|
|
47
|
+
throw new Error(
|
|
48
|
+
`writeStash: relativePath "${relativePath}" escapes the updates directory`,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
await fs.promises.mkdir(path.dirname(stashPath), { recursive: true });
|
|
53
|
+
if (typeof content === 'string') {
|
|
54
|
+
await fs.promises.writeFile(stashPath, content, 'utf8');
|
|
55
|
+
} else {
|
|
56
|
+
await fs.promises.writeFile(stashPath, content);
|
|
57
|
+
}
|
|
58
|
+
return stashPath;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports = { writeStash };
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync engine — SHA256-based conflict-aware file installer.
|
|
3
|
+
*
|
|
4
|
+
* Ported from v3 `packages/cli/tools/cli/installers/core/installer.js`
|
|
5
|
+
* lines 349-455 (`copyFileWithPolicy`). Same decision tree, rewritten in
|
|
6
|
+
* plain JS + JSDoc and split into testable pieces.
|
|
7
|
+
*
|
|
8
|
+
* The engine handles one file at a time and returns the action taken.
|
|
9
|
+
* Callers loop over plugin content and aggregate results into
|
|
10
|
+
* `FileOpsCounters`.
|
|
11
|
+
*
|
|
12
|
+
* Decision tree (same as v3):
|
|
13
|
+
*
|
|
14
|
+
* destExists?
|
|
15
|
+
* no → write; record fresh hash; CREATED
|
|
16
|
+
* yes → force?
|
|
17
|
+
* yes → overwrite; record fresh hash; UPDATED
|
|
18
|
+
* no → compute currentHash of dest
|
|
19
|
+
* protected flag in index?
|
|
20
|
+
* yes → currentHash == newHash?
|
|
21
|
+
* yes → clear protected; UNCHANGED (auto-converged)
|
|
22
|
+
* no → stash new; keep user file; PRESERVED
|
|
23
|
+
* no → index.sha256 == currentHash? (file untouched since last install)
|
|
24
|
+
* yes → currentHash != newHash? write new; record hash; UPDATED
|
|
25
|
+
* else: nothing to do; UNCHANGED
|
|
26
|
+
* no → (unknown baseline / locally modified)
|
|
27
|
+
* currentHash == newHash?
|
|
28
|
+
* yes → adopt new hash; UNCHANGED
|
|
29
|
+
* no → stash new; keep user file; PRESERVED
|
|
30
|
+
*
|
|
31
|
+
* The `fileIndex.files[relativePath]` entry is always updated so that
|
|
32
|
+
* the next sync run has an accurate baseline.
|
|
33
|
+
*/
|
|
34
|
+
const fs = require('fs');
|
|
35
|
+
const path = require('path');
|
|
36
|
+
|
|
37
|
+
const { sha256Hex, sha256File } = require('../../lib/hash.js');
|
|
38
|
+
const { writeStash } = require('./stash.js');
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @typedef {'created' | 'updated' | 'preserved' | 'unchanged'} SyncAction
|
|
42
|
+
*
|
|
43
|
+
* @typedef {Object} FileOpsCounters
|
|
44
|
+
* @property {number} created
|
|
45
|
+
* @property {number} updated
|
|
46
|
+
* @property {number} preserved
|
|
47
|
+
* @property {number} unchanged
|
|
48
|
+
* @property {number} stashed
|
|
49
|
+
* @property {number} removed
|
|
50
|
+
* @property {string|null} updatesPath
|
|
51
|
+
*
|
|
52
|
+
* @typedef {Object} SyncOptions
|
|
53
|
+
* @property {Buffer|string} content - bytes to write at `dest`
|
|
54
|
+
* @property {string} dest - absolute destination path
|
|
55
|
+
* @property {string} relativePath - posix-style path used as the fileIndex key
|
|
56
|
+
* @property {import('./file-index.js').FileIndex} fileIndex - mutated with new hashes
|
|
57
|
+
* @property {string} cfgDir - absolute path to .agileflow/_cfg/
|
|
58
|
+
* @property {string} timestamp - ISO stamp used as the stash bucket
|
|
59
|
+
* @property {boolean} [force=false] - if true, overwrite user modifications
|
|
60
|
+
* @property {FileOpsCounters} [ops] - optional counters; if omitted, caller loses aggregate info
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @returns {FileOpsCounters}
|
|
65
|
+
*/
|
|
66
|
+
function emptyCounters() {
|
|
67
|
+
return {
|
|
68
|
+
created: 0,
|
|
69
|
+
updated: 0,
|
|
70
|
+
preserved: 0,
|
|
71
|
+
unchanged: 0,
|
|
72
|
+
stashed: 0,
|
|
73
|
+
removed: 0,
|
|
74
|
+
updatesPath: null,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Write `content` to `dest` atomically: write a temp file in the same
|
|
80
|
+
* directory, then `rename` it into place. Crashes mid-`writeFile`
|
|
81
|
+
* cannot leave a truncated `dest` — readers always see either the old
|
|
82
|
+
* content or the new content, never a half-write.
|
|
83
|
+
*
|
|
84
|
+
* Temp filename uses pid + random suffix so two concurrent calls in
|
|
85
|
+
* the same process (rare but possible in tests / cluster setups) don't
|
|
86
|
+
* collide on each other's temp file.
|
|
87
|
+
*
|
|
88
|
+
* @param {Buffer|string} content
|
|
89
|
+
* @param {string} dest
|
|
90
|
+
*/
|
|
91
|
+
async function writeContent(content, dest) {
|
|
92
|
+
await fs.promises.mkdir(path.dirname(dest), { recursive: true });
|
|
93
|
+
const tmp = `${dest}.tmp-${process.pid}-${Math.random().toString(36).slice(2, 10)}`;
|
|
94
|
+
try {
|
|
95
|
+
if (typeof content === 'string') {
|
|
96
|
+
await fs.promises.writeFile(tmp, content, 'utf8');
|
|
97
|
+
} else {
|
|
98
|
+
await fs.promises.writeFile(tmp, content);
|
|
99
|
+
}
|
|
100
|
+
await fs.promises.rename(tmp, dest);
|
|
101
|
+
} catch (err) {
|
|
102
|
+
try {
|
|
103
|
+
await fs.promises.unlink(tmp);
|
|
104
|
+
} catch {
|
|
105
|
+
/* swallow */
|
|
106
|
+
}
|
|
107
|
+
throw err;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Sync a single file. Returns the action taken and mutates `fileIndex`
|
|
113
|
+
* plus `ops` (if provided).
|
|
114
|
+
*
|
|
115
|
+
* @param {SyncOptions} options
|
|
116
|
+
* @returns {Promise<SyncAction>}
|
|
117
|
+
*/
|
|
118
|
+
async function syncFile(options) {
|
|
119
|
+
const {
|
|
120
|
+
content,
|
|
121
|
+
dest,
|
|
122
|
+
relativePath,
|
|
123
|
+
fileIndex,
|
|
124
|
+
cfgDir,
|
|
125
|
+
timestamp,
|
|
126
|
+
force = false,
|
|
127
|
+
ops,
|
|
128
|
+
} = options;
|
|
129
|
+
|
|
130
|
+
const newHash = sha256Hex(content);
|
|
131
|
+
const existing = fileIndex.files[relativePath];
|
|
132
|
+
const existingRecord =
|
|
133
|
+
existing && typeof existing === 'object' && typeof existing.sha256 === 'string'
|
|
134
|
+
? existing
|
|
135
|
+
: null;
|
|
136
|
+
|
|
137
|
+
const currentHash = await sha256File(dest);
|
|
138
|
+
const destExists = currentHash !== null;
|
|
139
|
+
|
|
140
|
+
if (!destExists) {
|
|
141
|
+
await writeContent(content, dest);
|
|
142
|
+
fileIndex.files[relativePath] = { sha256: newHash, protected: false };
|
|
143
|
+
if (ops) ops.created++;
|
|
144
|
+
return 'created';
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (force) {
|
|
148
|
+
await writeContent(content, dest);
|
|
149
|
+
fileIndex.files[relativePath] = { sha256: newHash, protected: false };
|
|
150
|
+
if (ops) ops.updated++;
|
|
151
|
+
return 'updated';
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Respect a previously-protected file unless it has since converged
|
|
155
|
+
// with upstream (user's edits happen to match the new content — rare
|
|
156
|
+
// but worth auto-clearing the protected flag so future updates flow).
|
|
157
|
+
if (existingRecord && existingRecord.protected) {
|
|
158
|
+
if (currentHash === newHash) {
|
|
159
|
+
fileIndex.files[relativePath] = { sha256: newHash, protected: false };
|
|
160
|
+
if (ops) ops.unchanged++;
|
|
161
|
+
return 'unchanged';
|
|
162
|
+
}
|
|
163
|
+
const stashPath = await writeStash({ cfgDir, timestamp, relativePath, content });
|
|
164
|
+
fileIndex.files[relativePath] = { sha256: currentHash, protected: true };
|
|
165
|
+
if (ops) {
|
|
166
|
+
ops.preserved++;
|
|
167
|
+
ops.stashed++;
|
|
168
|
+
ops.updatesPath = path.dirname(stashPath);
|
|
169
|
+
}
|
|
170
|
+
return 'preserved';
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Baseline path: user hasn't touched this file since the last install.
|
|
174
|
+
if (existingRecord && existingRecord.sha256 === currentHash) {
|
|
175
|
+
if (currentHash === newHash) {
|
|
176
|
+
// Nothing to do; record stays current.
|
|
177
|
+
fileIndex.files[relativePath] = { sha256: newHash, protected: false };
|
|
178
|
+
if (ops) ops.unchanged++;
|
|
179
|
+
return 'unchanged';
|
|
180
|
+
}
|
|
181
|
+
await writeContent(content, dest);
|
|
182
|
+
fileIndex.files[relativePath] = { sha256: newHash, protected: false };
|
|
183
|
+
if (ops) ops.updated++;
|
|
184
|
+
return 'updated';
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// No baseline OR user-modified file. If the current content happens to
|
|
188
|
+
// match upstream, adopt the hash and move on; otherwise preserve+stash.
|
|
189
|
+
if (currentHash === newHash) {
|
|
190
|
+
fileIndex.files[relativePath] = { sha256: newHash, protected: false };
|
|
191
|
+
if (ops) ops.unchanged++;
|
|
192
|
+
return 'unchanged';
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const stashPath = await writeStash({ cfgDir, timestamp, relativePath, content });
|
|
196
|
+
fileIndex.files[relativePath] = { sha256: currentHash, protected: true };
|
|
197
|
+
if (ops) {
|
|
198
|
+
ops.preserved++;
|
|
199
|
+
ops.stashed++;
|
|
200
|
+
ops.updatesPath = path.dirname(stashPath);
|
|
201
|
+
}
|
|
202
|
+
return 'preserved';
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
module.exports = { syncFile, emptyCounters };
|