agileflow 3.4.2 → 4.0.0-alpha.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/CHANGELOG.md +235 -494
- 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/session-welcome.js +19 -0
- package/content/plugins/core/plugin.yaml +34 -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 +254 -0
- package/src/cli/commands/status.js +47 -0
- package/src/cli/commands/update.js +82 -0
- package/src/cli/index.js +73 -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 +45 -0
- package/src/runtime/config/loader.js +118 -0
- package/src/runtime/config/schema.json +76 -0
- package/src/runtime/config/writer.js +54 -0
- package/src/runtime/hooks/aggregator.js +133 -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 +306 -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/claude-cli-bridge.js +0 -215
- 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/dashboard-automations.js +0 -130
- package/lib/dashboard-git.js +0 -254
- package/lib/dashboard-inbox.js +0 -64
- package/lib/dashboard-protocol.js +0 -605
- package/lib/dashboard-server.js +0 -1296
- package/lib/dashboard-session.js +0 -136
- package/lib/dashboard-status.js +0 -72
- package/lib/dashboard-terminal.js +0 -354
- package/lib/dashboard-websocket.js +0 -88
- 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 -819
- 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-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/dashboard-serve.js +0 -336
- 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 -761
- package/scripts/lib/scale-detector.js +0 -396
- package/scripts/lib/sessionRegistry.js +0 -678
- package/scripts/lib/signal-detectors.js +0 -880
- 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 -611
- 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 -437
- 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 -347
- package/src/core/commands/code/api.md +0 -297
- package/src/core/commands/code/architecture.md +0 -297
- package/src/core/commands/code/completeness.md +0 -503
- package/src/core/commands/code/legal.md +0 -493
- package/src/core/commands/code/logic.md +0 -416
- package/src/core/commands/code/performance.md +0 -490
- package/src/core/commands/code/security.md +0 -493
- package/src/core/commands/code/test.md +0 -489
- 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 -496
- package/src/core/commands/ideate/history.md +0 -403
- package/src/core/commands/ideate/new.md +0 -899
- 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 -428
- 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/serve.md +0 -127
- 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/serve.js +0 -492
- 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,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code skill mirror.
|
|
3
|
+
*
|
|
4
|
+
* Plugin skills are sourced under `apps/cli/content/plugins/<id>/skills/<skill-id>/`
|
|
5
|
+
* and copied into `.agileflow/plugins/<id>/skills/<skill-id>/` by the
|
|
6
|
+
* sync engine. But Claude Code discovers skills from `.claude/skills/<skill-id>/`,
|
|
7
|
+
* so this module mirrors enabled plugin skills to that canonical
|
|
8
|
+
* location and prunes orphaned skills when plugins are disabled.
|
|
9
|
+
*
|
|
10
|
+
* We use copy (not symlink) for portability — Windows symlink behavior
|
|
11
|
+
* is inconsistent and Claude Code itself runs there. The duplication
|
|
12
|
+
* cost is negligible at v4-alpha skill counts.
|
|
13
|
+
*/
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {import('../plugins/registry.js').PluginManifest} PluginManifest
|
|
19
|
+
*
|
|
20
|
+
* @typedef {Object} MirrorResult
|
|
21
|
+
* @property {string[]} mirrored - skill ids written to .claude/skills/
|
|
22
|
+
* @property {string[]} pruned - skill ids removed from .claude/skills/ (orphans)
|
|
23
|
+
* @property {Array<{ skillId: string, error: string }>} skipped - skills whose source was missing
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Recursively copy a directory tree.
|
|
28
|
+
* @param {string} src
|
|
29
|
+
* @param {string} dest
|
|
30
|
+
*/
|
|
31
|
+
async function copyDir(src, dest) {
|
|
32
|
+
// Read the source listing FIRST. If src is missing (ENOENT), the
|
|
33
|
+
// error propagates BEFORE we create dest, so a missing source can't
|
|
34
|
+
// leave behind an empty destination dir for the caller to mistake
|
|
35
|
+
// for a successful copy.
|
|
36
|
+
const entries = await fs.promises.readdir(src, { withFileTypes: true });
|
|
37
|
+
await fs.promises.mkdir(dest, { recursive: true });
|
|
38
|
+
for (const e of entries) {
|
|
39
|
+
const s = path.join(src, e.name);
|
|
40
|
+
const d = path.join(dest, e.name);
|
|
41
|
+
if (e.isDirectory()) {
|
|
42
|
+
await copyDir(s, d);
|
|
43
|
+
} else if (e.isFile()) {
|
|
44
|
+
await fs.promises.copyFile(s, d);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Recursively remove a directory tree (no-op when missing).
|
|
51
|
+
* @param {string} dir
|
|
52
|
+
*/
|
|
53
|
+
async function rmDir(dir) {
|
|
54
|
+
await fs.promises.rm(dir, { recursive: true, force: true });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Collect every skill declared by an enabled plugin manifest. Returns
|
|
59
|
+
* a list of `{ skillId, sourceDir }` where sourceDir is absolute.
|
|
60
|
+
*
|
|
61
|
+
* @param {PluginManifest[]} orderedPlugins
|
|
62
|
+
* @returns {Array<{ skillId: string, sourceDir: string }>}
|
|
63
|
+
*/
|
|
64
|
+
function collectPluginSkills(orderedPlugins) {
|
|
65
|
+
/** @type {Array<{ skillId: string, sourceDir: string }>} */
|
|
66
|
+
const out = [];
|
|
67
|
+
for (const plugin of orderedPlugins) {
|
|
68
|
+
const skills =
|
|
69
|
+
plugin.provides && Array.isArray(plugin.provides.skills)
|
|
70
|
+
? plugin.provides.skills
|
|
71
|
+
: [];
|
|
72
|
+
for (const s of skills) {
|
|
73
|
+
// Reject missing-or-empty id/dir. An empty `dir: ""` would
|
|
74
|
+
// pass `typeof === 'string'` but path.join silently treats it as
|
|
75
|
+
// the plugin root, which is never what the author intended.
|
|
76
|
+
if (
|
|
77
|
+
!s ||
|
|
78
|
+
typeof s.id !== 'string' ||
|
|
79
|
+
!s.id ||
|
|
80
|
+
typeof s.dir !== 'string' ||
|
|
81
|
+
!s.dir
|
|
82
|
+
) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
out.push({
|
|
86
|
+
skillId: s.id,
|
|
87
|
+
sourceDir: path.join(plugin.dir, s.dir),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Mirror enabled plugin skills into `<projectRoot>/.claude/skills/<skill-id>/`
|
|
96
|
+
* and remove any `.claude/skills/<id>/` whose id is not in the current
|
|
97
|
+
* enabled set.
|
|
98
|
+
*
|
|
99
|
+
* Only AgileFlow-tracked skills are pruned: a skill is "tracked" iff
|
|
100
|
+
* its dir contains a `SKILL.md` whose first line is `---` AND its name
|
|
101
|
+
* appears in any plugin's `provides.skills` (i.e., we recognize it).
|
|
102
|
+
* To stay safe, we limit pruning to skills we've seen previously by
|
|
103
|
+
* recording the install set in the file index — but for v4-alpha
|
|
104
|
+
* simplicity, we prune every dir under `.claude/skills/` whose name
|
|
105
|
+
* matches an `agileflow-*` convention OR is in the previously-mirrored
|
|
106
|
+
* set on disk. Unknown user-placed skill dirs without that prefix are
|
|
107
|
+
* left alone.
|
|
108
|
+
*
|
|
109
|
+
* @param {PluginManifest[]} orderedPlugins
|
|
110
|
+
* @param {string} projectRoot
|
|
111
|
+
* @returns {Promise<MirrorResult>}
|
|
112
|
+
*/
|
|
113
|
+
async function mirrorClaudeCodeSkills(orderedPlugins, projectRoot) {
|
|
114
|
+
const claudeSkills = path.join(projectRoot, '.claude', 'skills');
|
|
115
|
+
await fs.promises.mkdir(claudeSkills, { recursive: true });
|
|
116
|
+
|
|
117
|
+
const want = collectPluginSkills(orderedPlugins);
|
|
118
|
+
const wantIds = new Set(want.map((w) => w.skillId));
|
|
119
|
+
|
|
120
|
+
/** @type {string[]} */
|
|
121
|
+
const mirrored = [];
|
|
122
|
+
/** @type {Array<{ skillId: string, error: string }>} */
|
|
123
|
+
const skipped = [];
|
|
124
|
+
for (const { skillId, sourceDir } of want) {
|
|
125
|
+
const dest = path.join(claudeSkills, skillId);
|
|
126
|
+
try {
|
|
127
|
+
// Replace the destination wholesale so removed files in source
|
|
128
|
+
// don't linger in the user's .claude/skills/.
|
|
129
|
+
await rmDir(dest);
|
|
130
|
+
await copyDir(sourceDir, dest);
|
|
131
|
+
mirrored.push(skillId);
|
|
132
|
+
} catch (err) {
|
|
133
|
+
// A missing source dir (ENOENT) shouldn't crash the entire
|
|
134
|
+
// install — log and continue so other skills still mirror. A
|
|
135
|
+
// permission error or unrelated failure still propagates.
|
|
136
|
+
if (err && err.code === 'ENOENT') {
|
|
137
|
+
skipped.push({
|
|
138
|
+
skillId,
|
|
139
|
+
error: `source not found: ${sourceDir}`,
|
|
140
|
+
});
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
throw err;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** @type {string[]} */
|
|
148
|
+
const pruned = [];
|
|
149
|
+
let entries;
|
|
150
|
+
try {
|
|
151
|
+
entries = await fs.promises.readdir(claudeSkills, { withFileTypes: true });
|
|
152
|
+
} catch (err) {
|
|
153
|
+
if (err.code === 'ENOENT') return { mirrored, pruned };
|
|
154
|
+
throw err;
|
|
155
|
+
}
|
|
156
|
+
for (const e of entries) {
|
|
157
|
+
if (!e.isDirectory()) continue;
|
|
158
|
+
if (wantIds.has(e.name)) continue;
|
|
159
|
+
// Only prune skills that LOOK like ours: agileflow-* prefix. This
|
|
160
|
+
// avoids blasting a user-placed third-party skill whose id we
|
|
161
|
+
// don't manage. A dedicated prune-from-index approach can replace
|
|
162
|
+
// this heuristic in a later phase.
|
|
163
|
+
if (!e.name.startsWith('agileflow-')) continue;
|
|
164
|
+
await rmDir(path.join(claudeSkills, e.name));
|
|
165
|
+
pruned.push(e.name);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return { mirrored, pruned, skipped };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Remove all AgileFlow-mirrored skills from `.claude/skills/`. Used
|
|
173
|
+
* when switching to a non-skill IDE.
|
|
174
|
+
*
|
|
175
|
+
* @param {string} projectRoot
|
|
176
|
+
* @returns {Promise<string[]>} ids that were removed
|
|
177
|
+
*/
|
|
178
|
+
async function unmirrorClaudeCodeSkills(projectRoot) {
|
|
179
|
+
const claudeSkills = path.join(projectRoot, '.claude', 'skills');
|
|
180
|
+
/** @type {string[]} */
|
|
181
|
+
const removed = [];
|
|
182
|
+
let entries;
|
|
183
|
+
try {
|
|
184
|
+
entries = await fs.promises.readdir(claudeSkills, { withFileTypes: true });
|
|
185
|
+
} catch (err) {
|
|
186
|
+
if (err.code === 'ENOENT') return removed;
|
|
187
|
+
throw err;
|
|
188
|
+
}
|
|
189
|
+
for (const e of entries) {
|
|
190
|
+
if (!e.isDirectory()) continue;
|
|
191
|
+
if (!e.name.startsWith('agileflow-')) continue;
|
|
192
|
+
await rmDir(path.join(claudeSkills, e.name));
|
|
193
|
+
removed.push(e.name);
|
|
194
|
+
}
|
|
195
|
+
return removed;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
module.exports = {
|
|
199
|
+
mirrorClaudeCodeSkills,
|
|
200
|
+
unmirrorClaudeCodeSkills,
|
|
201
|
+
collectPluginSkills,
|
|
202
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File index read/write for the sync engine.
|
|
3
|
+
*
|
|
4
|
+
* The file index (`_cfg/files.json`) records the SHA256 hash of every
|
|
5
|
+
* file the installer has written into the user's project and whether
|
|
6
|
+
* the user has since diverged from that baseline. The sync engine uses
|
|
7
|
+
* the index to decide whether a destination file is safe to overwrite,
|
|
8
|
+
* should be preserved, or should trigger a stash.
|
|
9
|
+
*
|
|
10
|
+
* Schema (version 1):
|
|
11
|
+
* {
|
|
12
|
+
* schema: 1,
|
|
13
|
+
* generated_at: <ISO string>,
|
|
14
|
+
* version: <CLI version that wrote this index>,
|
|
15
|
+
* files: {
|
|
16
|
+
* "<posix-relative-path>": {
|
|
17
|
+
* sha256: "<hex>",
|
|
18
|
+
* protected: <bool>
|
|
19
|
+
* },
|
|
20
|
+
* ...
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
*/
|
|
24
|
+
const fs = require('fs');
|
|
25
|
+
const path = require('path');
|
|
26
|
+
|
|
27
|
+
const SCHEMA_VERSION = 1;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @typedef {{ sha256: string, protected: boolean }} FileRecord
|
|
31
|
+
* @typedef {{ schema: 1, generated_at: string, version: string, files: Record<string, FileRecord> }} FileIndex
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Build an empty index. Caller fills `files` as sync proceeds.
|
|
36
|
+
* @param {string} cliVersion
|
|
37
|
+
* @returns {FileIndex}
|
|
38
|
+
*/
|
|
39
|
+
function emptyIndex(cliVersion) {
|
|
40
|
+
return {
|
|
41
|
+
schema: SCHEMA_VERSION,
|
|
42
|
+
generated_at: new Date().toISOString(),
|
|
43
|
+
version: cliVersion,
|
|
44
|
+
files: {},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Read an index file. Returns null on missing file, malformed JSON, or
|
|
50
|
+
* schema mismatch — the caller should treat any of those as "first
|
|
51
|
+
* install / no baseline" and start fresh.
|
|
52
|
+
*
|
|
53
|
+
* @param {string} indexPath
|
|
54
|
+
* @returns {Promise<FileIndex|null>}
|
|
55
|
+
*/
|
|
56
|
+
async function readFileIndex(indexPath) {
|
|
57
|
+
let raw;
|
|
58
|
+
try {
|
|
59
|
+
raw = await fs.promises.readFile(indexPath, 'utf8');
|
|
60
|
+
} catch (err) {
|
|
61
|
+
if (err.code === 'ENOENT') return null;
|
|
62
|
+
throw err;
|
|
63
|
+
}
|
|
64
|
+
let parsed;
|
|
65
|
+
try {
|
|
66
|
+
parsed = JSON.parse(raw);
|
|
67
|
+
} catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return null;
|
|
71
|
+
if (parsed.schema !== SCHEMA_VERSION) return null;
|
|
72
|
+
if (!parsed.files || typeof parsed.files !== 'object' || Array.isArray(parsed.files)) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
return /** @type {FileIndex} */ (parsed);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Write the index atomically (temp + rename), matching the writer's
|
|
80
|
+
* pattern for the user config. Callers should pass the final index they
|
|
81
|
+
* produced — nothing about `files` is validated here.
|
|
82
|
+
*
|
|
83
|
+
* @param {string} indexPath
|
|
84
|
+
* @param {FileIndex} index
|
|
85
|
+
* @returns {Promise<void>}
|
|
86
|
+
*/
|
|
87
|
+
async function writeFileIndex(indexPath, index) {
|
|
88
|
+
const dir = path.dirname(indexPath);
|
|
89
|
+
await fs.promises.mkdir(dir, { recursive: true });
|
|
90
|
+
const tmp = path.join(
|
|
91
|
+
dir,
|
|
92
|
+
`${path.basename(indexPath)}.tmp-${process.pid}-${Math.random().toString(36).slice(2, 10)}`,
|
|
93
|
+
);
|
|
94
|
+
const payload = {
|
|
95
|
+
...index,
|
|
96
|
+
generated_at: new Date().toISOString(),
|
|
97
|
+
};
|
|
98
|
+
const content = JSON.stringify(payload, null, 2) + '\n';
|
|
99
|
+
try {
|
|
100
|
+
await fs.promises.writeFile(tmp, content, 'utf8');
|
|
101
|
+
await fs.promises.rename(tmp, indexPath);
|
|
102
|
+
} catch (err) {
|
|
103
|
+
try {
|
|
104
|
+
await fs.promises.unlink(tmp);
|
|
105
|
+
} catch {
|
|
106
|
+
/* swallow */
|
|
107
|
+
}
|
|
108
|
+
throw err;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
module.exports = { SCHEMA_VERSION, emptyIndex, readFileIndex, writeFileIndex };
|
|
@@ -0,0 +1,306 @@
|
|
|
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 {
|
|
32
|
+
validatePluginSet,
|
|
33
|
+
hasErrors,
|
|
34
|
+
} = require('../plugins/validator.js');
|
|
35
|
+
const {
|
|
36
|
+
writeAggregatedManifest,
|
|
37
|
+
removeAggregatedManifest,
|
|
38
|
+
buildHookManifest,
|
|
39
|
+
} = require('../hooks/aggregator.js');
|
|
40
|
+
const { normalizeManifest } = require('../hooks/manifest-loader.js');
|
|
41
|
+
const { capabilitiesFor } = require('../ide/capabilities.js');
|
|
42
|
+
const {
|
|
43
|
+
writeClaudeCodeSettings,
|
|
44
|
+
removeClaudeCodeSettings,
|
|
45
|
+
} = require('../ide/claude-code-settings.js');
|
|
46
|
+
const {
|
|
47
|
+
mirrorClaudeCodeSkills,
|
|
48
|
+
unmirrorClaudeCodeSkills,
|
|
49
|
+
} = require('../ide/claude-code-skills.js');
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @typedef {import('../plugins/registry.js').PluginManifest} PluginManifest
|
|
53
|
+
*
|
|
54
|
+
* @typedef {Object} InstallOptions
|
|
55
|
+
* @property {PluginManifest[]} discovered
|
|
56
|
+
* @property {Iterable<string>} userSelected
|
|
57
|
+
* @property {string} agileflowDir - target install root (typically `<cwd>/.agileflow`)
|
|
58
|
+
* @property {string} cliVersion - written into the file index header
|
|
59
|
+
* @property {string} [ide='claude-code'] - target IDE for capability gating
|
|
60
|
+
* @property {boolean} [force=false] - overwrite user modifications
|
|
61
|
+
*
|
|
62
|
+
* @typedef {Object} InstallResult
|
|
63
|
+
* @property {string[]} ordered - plugin ids in install order
|
|
64
|
+
* @property {string[]} autoEnabled - ids pulled in via depends
|
|
65
|
+
* @property {string[]} removed - ids whose dir was removed (no longer enabled)
|
|
66
|
+
* @property {import('./sync-engine.js').FileOpsCounters} ops
|
|
67
|
+
* @property {string} agileflowDir
|
|
68
|
+
* @property {string} indexPath
|
|
69
|
+
* @property {string} timestamp
|
|
70
|
+
* @property {string|null} hookManifestPath - path of the written hook manifest, or null
|
|
71
|
+
* @property {string|null} settingsPath - path of the written .claude/settings.json, or null
|
|
72
|
+
* @property {string[]} skillsMirrored - skill ids copied into .claude/skills/
|
|
73
|
+
* @property {string[]} skillsPruned - skill ids removed from .claude/skills/
|
|
74
|
+
* @property {Array<{skillId:string, error:string}>} [skillsSkipped] - skills with missing source
|
|
75
|
+
* @property {string} ide - the target IDE for this install
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Build a stable file-system-safe timestamp string for stash bucketing.
|
|
80
|
+
* @returns {string}
|
|
81
|
+
*/
|
|
82
|
+
function makeTimestamp() {
|
|
83
|
+
return new Date().toISOString().replace(/[:.]/g, '-');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** @param {string} p */
|
|
87
|
+
function toPosix(p) {
|
|
88
|
+
return p.split(path.sep).join('/');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Yield absolute paths to every file under `dir`, depth-first.
|
|
93
|
+
* @param {string} dir
|
|
94
|
+
* @returns {AsyncGenerator<string>}
|
|
95
|
+
*/
|
|
96
|
+
async function* walkFiles(dir) {
|
|
97
|
+
const entries = await fs.promises.readdir(dir, { withFileTypes: true });
|
|
98
|
+
for (const e of entries) {
|
|
99
|
+
const full = path.join(dir, e.name);
|
|
100
|
+
if (e.isDirectory()) {
|
|
101
|
+
yield* walkFiles(full);
|
|
102
|
+
} else if (e.isFile()) {
|
|
103
|
+
yield full;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Sync every file in `plugin.dir` into `agileflowDir/plugins/<id>/...`.
|
|
110
|
+
*
|
|
111
|
+
* @param {PluginManifest} plugin
|
|
112
|
+
* @param {string} agileflowDir
|
|
113
|
+
* @param {import('./file-index.js').FileIndex} fileIndex
|
|
114
|
+
* @param {import('./sync-engine.js').FileOpsCounters} ops
|
|
115
|
+
* @param {string} timestamp
|
|
116
|
+
* @param {boolean} force
|
|
117
|
+
*/
|
|
118
|
+
async function installOnePlugin(plugin, agileflowDir, fileIndex, ops, timestamp, force) {
|
|
119
|
+
const cfgDir = path.join(agileflowDir, '_cfg');
|
|
120
|
+
const pluginRoot = path.join(agileflowDir, 'plugins', plugin.id);
|
|
121
|
+
|
|
122
|
+
for await (const sourcePath of walkFiles(plugin.dir)) {
|
|
123
|
+
const relInPlugin = path.relative(plugin.dir, sourcePath);
|
|
124
|
+
const dest = path.join(pluginRoot, relInPlugin);
|
|
125
|
+
const relativePath = toPosix(path.relative(agileflowDir, dest));
|
|
126
|
+
const content = await fs.promises.readFile(sourcePath);
|
|
127
|
+
await syncFile({
|
|
128
|
+
content,
|
|
129
|
+
dest,
|
|
130
|
+
relativePath,
|
|
131
|
+
fileIndex,
|
|
132
|
+
cfgDir,
|
|
133
|
+
timestamp,
|
|
134
|
+
force,
|
|
135
|
+
ops,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Remove plugin directories for plugins that were previously installed
|
|
142
|
+
* (have entries in the file index under `plugins/<id>/...`) but are
|
|
143
|
+
* NOT in the currently enabled set. Their file index entries are also
|
|
144
|
+
* pruned so a future re-enable produces fresh CREATED records.
|
|
145
|
+
*
|
|
146
|
+
* @param {Set<string>} enabledIds
|
|
147
|
+
* @param {Set<string>} knownIds - all discovered ids (so we don't blow
|
|
148
|
+
* away unknown directories the user might have placed manually)
|
|
149
|
+
* @param {string} agileflowDir
|
|
150
|
+
* @param {import('./file-index.js').FileIndex} fileIndex
|
|
151
|
+
* @param {import('./sync-engine.js').FileOpsCounters} ops
|
|
152
|
+
* @returns {Promise<string[]>} the plugin ids that were removed
|
|
153
|
+
*/
|
|
154
|
+
async function removeDisabledPlugins(enabledIds, knownIds, agileflowDir, fileIndex, ops) {
|
|
155
|
+
const pluginsRoot = path.join(agileflowDir, 'plugins');
|
|
156
|
+
/** @type {string[]} */
|
|
157
|
+
const removed = [];
|
|
158
|
+
let entries;
|
|
159
|
+
try {
|
|
160
|
+
entries = await fs.promises.readdir(pluginsRoot, { withFileTypes: true });
|
|
161
|
+
} catch (err) {
|
|
162
|
+
if (err.code === 'ENOENT') return removed;
|
|
163
|
+
throw err;
|
|
164
|
+
}
|
|
165
|
+
for (const e of entries) {
|
|
166
|
+
if (!e.isDirectory()) continue;
|
|
167
|
+
const id = e.name;
|
|
168
|
+
if (enabledIds.has(id)) continue;
|
|
169
|
+
if (!knownIds.has(id)) continue; // leave unknown dirs alone
|
|
170
|
+
const dir = path.join(pluginsRoot, id);
|
|
171
|
+
await fs.promises.rm(dir, { recursive: true, force: true });
|
|
172
|
+
for (const key of Object.keys(fileIndex.files)) {
|
|
173
|
+
if (key.startsWith(`plugins/${id}/`)) {
|
|
174
|
+
delete fileIndex.files[key];
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
removed.push(id);
|
|
178
|
+
ops.removed++;
|
|
179
|
+
}
|
|
180
|
+
return removed;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* @param {InstallOptions} options
|
|
185
|
+
* @returns {Promise<InstallResult>}
|
|
186
|
+
*/
|
|
187
|
+
async function installPlugins(options) {
|
|
188
|
+
const {
|
|
189
|
+
discovered,
|
|
190
|
+
userSelected,
|
|
191
|
+
agileflowDir,
|
|
192
|
+
cliVersion,
|
|
193
|
+
ide = 'claude-code',
|
|
194
|
+
force = false,
|
|
195
|
+
} = options;
|
|
196
|
+
|
|
197
|
+
// 1. Strict-validate. Errors abort; warnings are surfaced elsewhere.
|
|
198
|
+
const issues = validatePluginSet(discovered);
|
|
199
|
+
if (hasErrors(issues)) {
|
|
200
|
+
const errors = issues
|
|
201
|
+
.filter((i) => i.severity === 'error')
|
|
202
|
+
.map((i) => ` ${i.pluginId}: ${i.message}`)
|
|
203
|
+
.join('\n');
|
|
204
|
+
throw new Error(`Plugin validation failed:\n${errors}`);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// 2. Resolve dependency order.
|
|
208
|
+
const { ordered, autoEnabled } = resolvePlugins(discovered, userSelected);
|
|
209
|
+
|
|
210
|
+
// 3. Read or seed the file index.
|
|
211
|
+
const cfgDir = path.join(agileflowDir, '_cfg');
|
|
212
|
+
const indexPath = path.join(cfgDir, 'files.json');
|
|
213
|
+
const fileIndex = (await readFileIndex(indexPath)) || emptyIndex(cliVersion);
|
|
214
|
+
|
|
215
|
+
// 4-6 wrapped in try/finally so the file index is ALWAYS persisted,
|
|
216
|
+
// even if a sync fails mid-loop. Otherwise on-disk files would exist
|
|
217
|
+
// without index entries and the next run would misclassify them as
|
|
218
|
+
// "user-modified" and stash them.
|
|
219
|
+
const ops = emptyCounters();
|
|
220
|
+
const timestamp = makeTimestamp();
|
|
221
|
+
/** @type {string[]} */
|
|
222
|
+
let removed = [];
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
// 4. Sync each plugin in order.
|
|
226
|
+
for (const plugin of ordered) {
|
|
227
|
+
await installOnePlugin(plugin, agileflowDir, fileIndex, ops, timestamp, force);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// 5. Remove disabled plugin directories.
|
|
231
|
+
const enabledIds = new Set(ordered.map((p) => p.id));
|
|
232
|
+
const knownIds = new Set(discovered.map((p) => p.id));
|
|
233
|
+
removed = await removeDisabledPlugins(
|
|
234
|
+
enabledIds,
|
|
235
|
+
knownIds,
|
|
236
|
+
agileflowDir,
|
|
237
|
+
fileIndex,
|
|
238
|
+
ops,
|
|
239
|
+
);
|
|
240
|
+
} finally {
|
|
241
|
+
// 6. Persist the file index. Always.
|
|
242
|
+
await writeFileIndex(indexPath, fileIndex);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// 7. Validate-before-write: build the manifest in memory and run
|
|
246
|
+
// the loader's normalizer against it. Surfacing an invalid plugin
|
|
247
|
+
// contribution NOW prevents step 8 from registering hook
|
|
248
|
+
// dispatchers in settings.json that point at an unparseable
|
|
249
|
+
// manifest.
|
|
250
|
+
const caps = capabilitiesFor(ide);
|
|
251
|
+
let hookManifestPath = null;
|
|
252
|
+
if (caps.hooks) {
|
|
253
|
+
const manifestObj = buildHookManifest(ordered);
|
|
254
|
+
try {
|
|
255
|
+
normalizeManifest(manifestObj);
|
|
256
|
+
} catch (err) {
|
|
257
|
+
throw new Error(`Hook manifest validation failed: ${err.message}`);
|
|
258
|
+
}
|
|
259
|
+
hookManifestPath = await writeAggregatedManifest(ordered, agileflowDir);
|
|
260
|
+
} else {
|
|
261
|
+
await removeAggregatedManifest(agileflowDir);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// 8. Register or unregister our hook dispatchers in
|
|
265
|
+
// `.claude/settings.json`. Only when ide=claude-code.
|
|
266
|
+
const projectRoot = path.dirname(agileflowDir);
|
|
267
|
+
let settingsPath = null;
|
|
268
|
+
if (ide === 'claude-code') {
|
|
269
|
+
settingsPath = await writeClaudeCodeSettings(projectRoot);
|
|
270
|
+
} else {
|
|
271
|
+
await removeClaudeCodeSettings(projectRoot);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// 9. Mirror skills into `.claude/skills/<id>/`. Copy (not symlink)
|
|
275
|
+
// for Windows portability. Missing skill sources are skipped
|
|
276
|
+
// gracefully; non-ENOENT errors propagate.
|
|
277
|
+
let skillsMirrored = [];
|
|
278
|
+
let skillsPruned = [];
|
|
279
|
+
let skillsSkipped = [];
|
|
280
|
+
if (caps.skills) {
|
|
281
|
+
const r = await mirrorClaudeCodeSkills(ordered, projectRoot);
|
|
282
|
+
skillsMirrored = r.mirrored;
|
|
283
|
+
skillsPruned = r.pruned;
|
|
284
|
+
skillsSkipped = r.skipped || [];
|
|
285
|
+
} else {
|
|
286
|
+
skillsPruned = await unmirrorClaudeCodeSkills(projectRoot);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return {
|
|
290
|
+
ordered: ordered.map((p) => p.id),
|
|
291
|
+
autoEnabled,
|
|
292
|
+
removed,
|
|
293
|
+
ops,
|
|
294
|
+
agileflowDir,
|
|
295
|
+
indexPath,
|
|
296
|
+
timestamp,
|
|
297
|
+
hookManifestPath,
|
|
298
|
+
settingsPath,
|
|
299
|
+
skillsMirrored,
|
|
300
|
+
skillsPruned,
|
|
301
|
+
skillsSkipped,
|
|
302
|
+
ide,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
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 };
|