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
|
@@ -1,892 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AgileFlow CLI - Core Installer
|
|
3
|
-
*
|
|
4
|
-
* Handles the main installation logic for AgileFlow.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const path = require('node:path');
|
|
8
|
-
const fs = require('fs-extra');
|
|
9
|
-
const chalk = require('chalk');
|
|
10
|
-
const ora = require('ora');
|
|
11
|
-
const { safeLoad, safeDump } = require('../../../../lib/yaml-utils');
|
|
12
|
-
const { injectContent } = require('../../lib/content-injector');
|
|
13
|
-
const { sha256Hex, toPosixPath, safeTimestampForPath } = require('../../lib/utils');
|
|
14
|
-
const { validatePath, PathValidationError } = require('../../../../lib/validate');
|
|
15
|
-
const {
|
|
16
|
-
createTypedError,
|
|
17
|
-
getErrorCodeFromError,
|
|
18
|
-
attachErrorCode,
|
|
19
|
-
} = require('../../../../lib/error-codes');
|
|
20
|
-
const { setSecurePermissions, SECURE_FILE_MODE } = require('../../../../lib/smart-json-file');
|
|
21
|
-
|
|
22
|
-
const TEXT_EXTENSIONS = new Set(['.md', '.yaml', '.yml', '.txt', '.json']);
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Get the source path for AgileFlow content
|
|
26
|
-
* @returns {string} Path to src directory
|
|
27
|
-
*/
|
|
28
|
-
function getSourcePath() {
|
|
29
|
-
return path.join(__dirname, '..', '..', '..', '..', 'src');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Get the package root path
|
|
34
|
-
* @returns {string} Path to package root
|
|
35
|
-
*/
|
|
36
|
-
function getPackageRoot() {
|
|
37
|
-
return path.join(__dirname, '..', '..', '..', '..');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Core Installer class
|
|
42
|
-
*/
|
|
43
|
-
class Installer {
|
|
44
|
-
constructor() {
|
|
45
|
-
this.sourcePath = getSourcePath();
|
|
46
|
-
this.packageRoot = getPackageRoot();
|
|
47
|
-
this.coreDir = path.join(this.sourcePath, 'core');
|
|
48
|
-
|
|
49
|
-
// Load version from package.json
|
|
50
|
-
const packageJson = require(path.join(this.packageRoot, 'package.json'));
|
|
51
|
-
this.version = packageJson.version;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Clean up existing content directories before installing.
|
|
56
|
-
* Removes agents/, commands/, skills/, scripts/, templates/ but preserves _cfg/ and config.yaml.
|
|
57
|
-
* @param {string} agileflowDir - AgileFlow installation directory
|
|
58
|
-
*/
|
|
59
|
-
async cleanup(agileflowDir) {
|
|
60
|
-
const dirsToRemove = ['agents', 'commands', 'skills', 'scripts', 'templates', 'teams'];
|
|
61
|
-
|
|
62
|
-
for (const dir of dirsToRemove) {
|
|
63
|
-
const dirPath = path.join(agileflowDir, dir);
|
|
64
|
-
if (await fs.pathExists(dirPath)) {
|
|
65
|
-
await fs.remove(dirPath);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Install AgileFlow to a project
|
|
72
|
-
* @param {Object} config - Installation configuration
|
|
73
|
-
* @param {Object} options - Installation options
|
|
74
|
-
* @param {boolean} options.force - Overwrite local changes
|
|
75
|
-
* @returns {Promise<Object>} Installation result
|
|
76
|
-
*/
|
|
77
|
-
async install(config, options = {}) {
|
|
78
|
-
const { directory, ides, userName, agileflowFolder, docsFolder } = config;
|
|
79
|
-
const requestedForce = Boolean(options.force);
|
|
80
|
-
|
|
81
|
-
const agileflowDir = path.join(directory, agileflowFolder);
|
|
82
|
-
const spinner = ora('Installing AgileFlow...').start();
|
|
83
|
-
|
|
84
|
-
try {
|
|
85
|
-
// Create AgileFlow directory
|
|
86
|
-
await fs.ensureDir(agileflowDir);
|
|
87
|
-
spinner.text = 'Creating directory structure...';
|
|
88
|
-
|
|
89
|
-
// Create _cfg directory for manifest
|
|
90
|
-
const cfgDir = path.join(agileflowDir, '_cfg');
|
|
91
|
-
await fs.ensureDir(cfgDir);
|
|
92
|
-
|
|
93
|
-
const manifestPath = path.join(cfgDir, 'manifest.yaml');
|
|
94
|
-
const fileIndexPath = path.join(cfgDir, 'files.json');
|
|
95
|
-
|
|
96
|
-
const hasManifest = await fs.pathExists(manifestPath);
|
|
97
|
-
const existingFileIndex = await this.readFileIndex(fileIndexPath);
|
|
98
|
-
const hasValidFileIndex = Boolean(existingFileIndex);
|
|
99
|
-
|
|
100
|
-
// Migration: installation predates file indexing
|
|
101
|
-
const isMigration = hasManifest && !hasValidFileIndex;
|
|
102
|
-
const effectiveForce = requestedForce || isMigration;
|
|
103
|
-
|
|
104
|
-
const timestamp = safeTimestampForPath();
|
|
105
|
-
let backupPath = null;
|
|
106
|
-
if (isMigration) {
|
|
107
|
-
spinner.text = 'Backing up existing installation...';
|
|
108
|
-
backupPath = await this.createBackup(agileflowDir, cfgDir, timestamp);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Clean up existing content directories to remove stale files
|
|
112
|
-
// This happens AFTER backup so we can restore if needed
|
|
113
|
-
spinner.text = 'Cleaning up old content...';
|
|
114
|
-
await this.cleanup(agileflowDir);
|
|
115
|
-
|
|
116
|
-
// Reset file index since we removed all content - start fresh
|
|
117
|
-
const fileIndex = { schema: 1, files: {} };
|
|
118
|
-
|
|
119
|
-
const fileOps = {
|
|
120
|
-
created: 0,
|
|
121
|
-
updated: 0,
|
|
122
|
-
preserved: 0,
|
|
123
|
-
stashed: 0,
|
|
124
|
-
backupPath,
|
|
125
|
-
updatesPath: null,
|
|
126
|
-
isMigration,
|
|
127
|
-
force: effectiveForce,
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
// Copy core content
|
|
131
|
-
spinner.text = 'Installing core content...';
|
|
132
|
-
const coreSourcePath = path.join(this.sourcePath, 'core');
|
|
133
|
-
|
|
134
|
-
if (await fs.pathExists(coreSourcePath)) {
|
|
135
|
-
await this.copyContent(coreSourcePath, agileflowDir, agileflowFolder, {
|
|
136
|
-
agileflowDir,
|
|
137
|
-
cfgDir,
|
|
138
|
-
fileIndex,
|
|
139
|
-
fileOps,
|
|
140
|
-
force: effectiveForce,
|
|
141
|
-
timestamp,
|
|
142
|
-
docsFolder: docsFolder || 'docs',
|
|
143
|
-
});
|
|
144
|
-
} else {
|
|
145
|
-
// Fallback: copy from old structure (commands, agents, skills at root)
|
|
146
|
-
await this.copyLegacyContent(directory, agileflowDir, agileflowFolder, {
|
|
147
|
-
agileflowDir,
|
|
148
|
-
cfgDir,
|
|
149
|
-
fileIndex,
|
|
150
|
-
fileOps,
|
|
151
|
-
force: effectiveForce,
|
|
152
|
-
timestamp,
|
|
153
|
-
docsFolder: docsFolder || 'docs',
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Copy essential scripts to user's scripts/ directory
|
|
158
|
-
// Always force-overwrite scripts - they are internal AgileFlow code,
|
|
159
|
-
// never user-edited, and must stay in sync with the installed version.
|
|
160
|
-
spinner.text = 'Installing scripts...';
|
|
161
|
-
await this.installScripts(directory, { force: true });
|
|
162
|
-
|
|
163
|
-
// Copy lib/ directory (shared utilities used by scripts)
|
|
164
|
-
// Always force-overwrite lib files - they are internal AgileFlow code,
|
|
165
|
-
// never user-edited, and must stay in sync with the installed version.
|
|
166
|
-
spinner.text = 'Installing shared libraries...';
|
|
167
|
-
await this.installLib(directory, { force: true });
|
|
168
|
-
|
|
169
|
-
// Copy CHANGELOG.md for /agileflow:whats-new command
|
|
170
|
-
spinner.text = 'Installing changelog...';
|
|
171
|
-
await this.installChangelog(agileflowDir, { force: effectiveForce });
|
|
172
|
-
|
|
173
|
-
// Create config.yaml
|
|
174
|
-
spinner.text = 'Creating configuration...';
|
|
175
|
-
await this.createConfig(agileflowDir, userName, agileflowFolder, { force: effectiveForce });
|
|
176
|
-
|
|
177
|
-
// Create manifest
|
|
178
|
-
spinner.text = 'Creating manifest...';
|
|
179
|
-
await this.createManifest(
|
|
180
|
-
cfgDir,
|
|
181
|
-
{ ides, userName, agileflowFolder, docsFolder },
|
|
182
|
-
{ force: effectiveForce }
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
// Persist file index (used for safe future updates)
|
|
186
|
-
spinner.text = 'Writing file index...';
|
|
187
|
-
await this.writeFileIndex(fileIndexPath, fileIndex);
|
|
188
|
-
|
|
189
|
-
// Count installed items
|
|
190
|
-
const counts = await this.countInstalledItems(agileflowDir);
|
|
191
|
-
|
|
192
|
-
spinner.succeed('Core installation complete');
|
|
193
|
-
|
|
194
|
-
return {
|
|
195
|
-
success: true,
|
|
196
|
-
path: agileflowDir,
|
|
197
|
-
projectDir: directory,
|
|
198
|
-
counts,
|
|
199
|
-
fileOps,
|
|
200
|
-
};
|
|
201
|
-
} catch (error) {
|
|
202
|
-
spinner.fail('Installation failed');
|
|
203
|
-
|
|
204
|
-
// Convert to typed error if not already
|
|
205
|
-
if (!error.errorCode) {
|
|
206
|
-
const errorCode = getErrorCodeFromError(error);
|
|
207
|
-
attachErrorCode(error, errorCode.code);
|
|
208
|
-
error.context = { directory, agileflowFolder };
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
throw error;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Validate that a path is within the allowed installation directory.
|
|
217
|
-
* Prevents path traversal attacks when writing files.
|
|
218
|
-
*
|
|
219
|
-
* @param {string} filePath - Path to validate
|
|
220
|
-
* @param {string} baseDir - Allowed base directory
|
|
221
|
-
* @throws {PathValidationError} If path escapes base directory
|
|
222
|
-
*/
|
|
223
|
-
validateInstallPath(filePath, baseDir) {
|
|
224
|
-
const result = validatePath(filePath, baseDir, { allowSymlinks: false });
|
|
225
|
-
if (!result.ok) {
|
|
226
|
-
throw result.error;
|
|
227
|
-
}
|
|
228
|
-
return result.resolvedPath;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Copy content from source to destination with placeholder replacement
|
|
233
|
-
* @param {string} source - Source directory
|
|
234
|
-
* @param {string} dest - Destination directory
|
|
235
|
-
* @param {string} agileflowFolder - AgileFlow folder name
|
|
236
|
-
* @param {Object|null} policy - Copy policy (safe update behavior)
|
|
237
|
-
*/
|
|
238
|
-
async copyContent(source, dest, agileflowFolder, policy = null) {
|
|
239
|
-
const entries = await fs.readdir(source, { withFileTypes: true });
|
|
240
|
-
|
|
241
|
-
// Get base directory for validation (agileflowDir from policy or dest itself)
|
|
242
|
-
const baseDir = policy?.agileflowDir || dest;
|
|
243
|
-
|
|
244
|
-
for (const entry of entries) {
|
|
245
|
-
const srcPath = path.join(source, entry.name);
|
|
246
|
-
const destPath = path.join(dest, entry.name);
|
|
247
|
-
|
|
248
|
-
// Validate destination path to prevent traversal attacks via malicious filenames
|
|
249
|
-
this.validateInstallPath(destPath, baseDir);
|
|
250
|
-
|
|
251
|
-
if (entry.isDirectory()) {
|
|
252
|
-
await fs.ensureDir(destPath);
|
|
253
|
-
await this.copyContent(srcPath, destPath, agileflowFolder, policy);
|
|
254
|
-
} else {
|
|
255
|
-
if (policy) {
|
|
256
|
-
await this.copyFileWithPolicy(srcPath, destPath, agileflowFolder, policy);
|
|
257
|
-
} else {
|
|
258
|
-
await this.copyFileWithReplacements(srcPath, destPath, agileflowFolder);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Copy legacy content structure (from root commands/agents/skills)
|
|
266
|
-
* @param {string} projectDir - Project directory
|
|
267
|
-
* @param {string} agileflowDir - AgileFlow installation directory
|
|
268
|
-
* @param {string} agileflowFolder - AgileFlow folder name
|
|
269
|
-
* @param {Object|null} policy - Copy policy (safe update behavior)
|
|
270
|
-
*/
|
|
271
|
-
async copyLegacyContent(projectDir, agileflowDir, agileflowFolder, policy = null) {
|
|
272
|
-
const packageRoot = this.packageRoot;
|
|
273
|
-
|
|
274
|
-
// Copy commands
|
|
275
|
-
const commandsSource = path.join(packageRoot, 'commands');
|
|
276
|
-
const commandsDest = path.join(agileflowDir, 'commands');
|
|
277
|
-
if (await fs.pathExists(commandsSource)) {
|
|
278
|
-
await fs.ensureDir(commandsDest);
|
|
279
|
-
await this.copyContent(commandsSource, commandsDest, agileflowFolder, policy);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// Copy agents
|
|
283
|
-
const agentsSource = path.join(packageRoot, 'agents');
|
|
284
|
-
const agentsDest = path.join(agileflowDir, 'agents');
|
|
285
|
-
if (await fs.pathExists(agentsSource)) {
|
|
286
|
-
await fs.ensureDir(agentsDest);
|
|
287
|
-
await this.copyContent(agentsSource, agentsDest, agileflowFolder, policy);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Copy skills
|
|
291
|
-
const skillsSource = path.join(packageRoot, 'skills');
|
|
292
|
-
const skillsDest = path.join(agileflowDir, 'skills');
|
|
293
|
-
if (await fs.pathExists(skillsSource)) {
|
|
294
|
-
await fs.ensureDir(skillsDest);
|
|
295
|
-
await this.copyContent(skillsSource, skillsDest, agileflowFolder, policy);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* Copy a file with placeholder replacements using content injector
|
|
301
|
-
* @param {string} source - Source file path
|
|
302
|
-
* @param {string} dest - Destination file path
|
|
303
|
-
* @param {string} agileflowFolder - AgileFlow folder name
|
|
304
|
-
* @param {string} docsFolder - Docs folder name (default: 'docs')
|
|
305
|
-
*/
|
|
306
|
-
async copyFileWithReplacements(source, dest, agileflowFolder, docsFolder = 'docs') {
|
|
307
|
-
const ext = path.extname(source).toLowerCase();
|
|
308
|
-
|
|
309
|
-
if (TEXT_EXTENSIONS.has(ext)) {
|
|
310
|
-
let content = await fs.readFile(source, 'utf8');
|
|
311
|
-
|
|
312
|
-
// Use content injector for all placeholder replacements
|
|
313
|
-
content = injectContent(content, {
|
|
314
|
-
coreDir: this.coreDir,
|
|
315
|
-
agileflowFolder,
|
|
316
|
-
docsFolder,
|
|
317
|
-
version: this.version,
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
await fs.writeFile(dest, content, 'utf8');
|
|
321
|
-
} else {
|
|
322
|
-
await fs.copy(source, dest);
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* Copy a file with safe update behavior.
|
|
328
|
-
* - If the destination file is unchanged since the last install, overwrite.
|
|
329
|
-
* - If it has local changes, preserve and write the new version to _cfg/updates/<timestamp>/...
|
|
330
|
-
* - If --force is set, overwrite all.
|
|
331
|
-
* @param {string} source - Source file path
|
|
332
|
-
* @param {string} dest - Destination file path
|
|
333
|
-
* @param {string} agileflowFolder - AgileFlow folder name
|
|
334
|
-
* @param {Object} policy - Copy policy
|
|
335
|
-
*/
|
|
336
|
-
async copyFileWithPolicy(source, dest, agileflowFolder, policy) {
|
|
337
|
-
const {
|
|
338
|
-
agileflowDir,
|
|
339
|
-
cfgDir,
|
|
340
|
-
fileIndex,
|
|
341
|
-
fileOps,
|
|
342
|
-
force,
|
|
343
|
-
timestamp,
|
|
344
|
-
docsFolder = 'docs',
|
|
345
|
-
} = policy;
|
|
346
|
-
|
|
347
|
-
const relativePath = toPosixPath(path.relative(agileflowDir, dest));
|
|
348
|
-
const maybeRecord = fileIndex.files[relativePath];
|
|
349
|
-
const existingRecord = maybeRecord && typeof maybeRecord === 'object' ? maybeRecord : null;
|
|
350
|
-
|
|
351
|
-
const ext = path.extname(source).toLowerCase();
|
|
352
|
-
const isText = TEXT_EXTENSIONS.has(ext);
|
|
353
|
-
|
|
354
|
-
let newContent;
|
|
355
|
-
if (isText) {
|
|
356
|
-
let content = await fs.readFile(source, 'utf8');
|
|
357
|
-
// Use content injector for all placeholder replacements
|
|
358
|
-
content = injectContent(content, {
|
|
359
|
-
coreDir: this.coreDir,
|
|
360
|
-
agileflowFolder,
|
|
361
|
-
docsFolder,
|
|
362
|
-
version: this.version,
|
|
363
|
-
});
|
|
364
|
-
newContent = content;
|
|
365
|
-
} else {
|
|
366
|
-
newContent = await fs.readFile(source);
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
const newHash = sha256Hex(newContent);
|
|
370
|
-
const destExists = await fs.pathExists(dest);
|
|
371
|
-
|
|
372
|
-
if (!destExists) {
|
|
373
|
-
await fs.ensureDir(path.dirname(dest));
|
|
374
|
-
if (isText) {
|
|
375
|
-
await fs.writeFile(dest, newContent, 'utf8');
|
|
376
|
-
} else {
|
|
377
|
-
await fs.copy(source, dest);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
fileIndex.files[relativePath] = { sha256: newHash, protected: false };
|
|
381
|
-
fileOps.created++;
|
|
382
|
-
return;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
if (force) {
|
|
386
|
-
if (isText) {
|
|
387
|
-
await fs.writeFile(dest, newContent, 'utf8');
|
|
388
|
-
} else {
|
|
389
|
-
await fs.copy(source, dest);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
fileIndex.files[relativePath] = { sha256: newHash, protected: false };
|
|
393
|
-
fileOps.updated++;
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
const currentHash = await this.hashFile(dest);
|
|
398
|
-
|
|
399
|
-
// Respect previously protected files unless they now match upstream.
|
|
400
|
-
if (existingRecord && existingRecord.protected) {
|
|
401
|
-
if (currentHash === newHash) {
|
|
402
|
-
fileIndex.files[relativePath] = { sha256: newHash, protected: false };
|
|
403
|
-
return;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
await this.writeStash(cfgDir, timestamp, relativePath, isText, newContent, source, fileOps);
|
|
407
|
-
fileIndex.files[relativePath] = { sha256: currentHash, protected: true };
|
|
408
|
-
fileOps.preserved++;
|
|
409
|
-
return;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// If we have a baseline hash and the file is unchanged since last install, we can update in place.
|
|
413
|
-
if (existingRecord && existingRecord.sha256 === currentHash) {
|
|
414
|
-
if (currentHash !== newHash) {
|
|
415
|
-
if (isText) {
|
|
416
|
-
await fs.writeFile(dest, newContent, 'utf8');
|
|
417
|
-
} else {
|
|
418
|
-
await fs.copy(source, dest);
|
|
419
|
-
}
|
|
420
|
-
fileOps.updated++;
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
fileIndex.files[relativePath] = { sha256: newHash, protected: false };
|
|
424
|
-
return;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
// Unknown or locally modified file: preserve and stash the new version for manual merge.
|
|
428
|
-
if (currentHash === newHash) {
|
|
429
|
-
fileIndex.files[relativePath] = { sha256: newHash, protected: false };
|
|
430
|
-
return;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
await this.writeStash(cfgDir, timestamp, relativePath, isText, newContent, source, fileOps);
|
|
434
|
-
fileIndex.files[relativePath] = { sha256: currentHash, protected: true };
|
|
435
|
-
fileOps.preserved++;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
async writeStash(cfgDir, timestamp, relativePath, isText, newContent, source, fileOps) {
|
|
439
|
-
const updatesRoot = path.join(cfgDir, 'updates', timestamp);
|
|
440
|
-
const stashPath = path.join(updatesRoot, relativePath);
|
|
441
|
-
|
|
442
|
-
await fs.ensureDir(path.dirname(stashPath));
|
|
443
|
-
if (isText) {
|
|
444
|
-
await fs.writeFile(stashPath, newContent, 'utf8');
|
|
445
|
-
} else {
|
|
446
|
-
await fs.copy(source, stashPath);
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
fileOps.stashed++;
|
|
450
|
-
fileOps.updatesPath = updatesRoot;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
async hashFile(filePath) {
|
|
454
|
-
const data = await fs.readFile(filePath);
|
|
455
|
-
return sha256Hex(data);
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
async readFileIndex(fileIndexPath) {
|
|
459
|
-
if (!(await fs.pathExists(fileIndexPath))) return null;
|
|
460
|
-
try {
|
|
461
|
-
const index = await fs.readJson(fileIndexPath);
|
|
462
|
-
if (!index || typeof index !== 'object') return null;
|
|
463
|
-
if (index.schema !== 1) return null;
|
|
464
|
-
if (!index.files || typeof index.files !== 'object') return null;
|
|
465
|
-
return index;
|
|
466
|
-
} catch {
|
|
467
|
-
return null;
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
async writeFileIndex(fileIndexPath, fileIndex) {
|
|
472
|
-
const packageJson = require(path.join(this.packageRoot, 'package.json'));
|
|
473
|
-
const nextIndex = {
|
|
474
|
-
schema: 1,
|
|
475
|
-
generated_at: new Date().toISOString(),
|
|
476
|
-
version: packageJson.version,
|
|
477
|
-
files: fileIndex.files || {},
|
|
478
|
-
};
|
|
479
|
-
|
|
480
|
-
await fs.writeJson(fileIndexPath, nextIndex, { spaces: 2 });
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
async createBackup(agileflowDir, cfgDir, timestamp) {
|
|
484
|
-
const backupRoot = path.join(cfgDir, 'backups', timestamp);
|
|
485
|
-
await fs.ensureDir(backupRoot);
|
|
486
|
-
|
|
487
|
-
const candidates = ['agents', 'commands', 'skills', 'scripts', 'templates', 'config.yaml'];
|
|
488
|
-
for (const name of candidates) {
|
|
489
|
-
const srcPath = path.join(agileflowDir, name);
|
|
490
|
-
if (await fs.pathExists(srcPath)) {
|
|
491
|
-
await fs.copy(srcPath, path.join(backupRoot, name));
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
const manifestPath = path.join(cfgDir, 'manifest.yaml');
|
|
496
|
-
if (await fs.pathExists(manifestPath)) {
|
|
497
|
-
await fs.copy(manifestPath, path.join(backupRoot, 'manifest.yaml'));
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
return backupRoot;
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
/**
|
|
504
|
-
* Create configuration file
|
|
505
|
-
* @param {string} agileflowDir - AgileFlow directory
|
|
506
|
-
* @param {string} userName - User name
|
|
507
|
-
* @param {string} agileflowFolder - AgileFlow folder name
|
|
508
|
-
* @param {Object} options - Options
|
|
509
|
-
* @param {boolean} options.force - Overwrite existing file
|
|
510
|
-
*/
|
|
511
|
-
async createConfig(agileflowDir, userName, agileflowFolder, options = {}) {
|
|
512
|
-
const configPath = path.join(agileflowDir, 'config.yaml');
|
|
513
|
-
const packageJson = require(path.join(this.packageRoot, 'package.json'));
|
|
514
|
-
|
|
515
|
-
if (!(await fs.pathExists(configPath))) {
|
|
516
|
-
const config = {
|
|
517
|
-
version: packageJson.version,
|
|
518
|
-
user_name: userName,
|
|
519
|
-
agileflow_folder: agileflowFolder,
|
|
520
|
-
communication_language: 'English',
|
|
521
|
-
created_at: new Date().toISOString(),
|
|
522
|
-
};
|
|
523
|
-
|
|
524
|
-
await fs.writeFile(configPath, safeDump(config), 'utf8');
|
|
525
|
-
// Security: Set secure permissions (0o600) on config file
|
|
526
|
-
setSecurePermissions(configPath);
|
|
527
|
-
return;
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
try {
|
|
531
|
-
const existingContent = await fs.readFile(configPath, 'utf8');
|
|
532
|
-
let loaded;
|
|
533
|
-
try {
|
|
534
|
-
loaded = safeLoad(existingContent);
|
|
535
|
-
} catch (parseErr) {
|
|
536
|
-
// Attach error code for YAML parse errors
|
|
537
|
-
throw createTypedError(`Failed to parse config.yaml: ${parseErr.message}`, 'EPARSE', {
|
|
538
|
-
cause: parseErr,
|
|
539
|
-
context: { configPath },
|
|
540
|
-
});
|
|
541
|
-
}
|
|
542
|
-
const existing = loaded && typeof loaded === 'object' && !Array.isArray(loaded) ? loaded : {};
|
|
543
|
-
|
|
544
|
-
const next = {
|
|
545
|
-
...existing,
|
|
546
|
-
version: packageJson.version,
|
|
547
|
-
user_name: userName,
|
|
548
|
-
agileflow_folder: agileflowFolder,
|
|
549
|
-
created_at: existing.created_at || new Date().toISOString(),
|
|
550
|
-
updated_at: new Date().toISOString(),
|
|
551
|
-
};
|
|
552
|
-
|
|
553
|
-
await fs.writeFile(configPath, safeDump(next), 'utf8');
|
|
554
|
-
// Security: Set secure permissions (0o600) on config file
|
|
555
|
-
setSecurePermissions(configPath);
|
|
556
|
-
} catch (err) {
|
|
557
|
-
// If it's a typed parse error and not forcing, re-throw
|
|
558
|
-
if (err.errorCode === 'EPARSE' && !options.force) {
|
|
559
|
-
throw err;
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
if (options.force) {
|
|
563
|
-
const config = {
|
|
564
|
-
version: packageJson.version,
|
|
565
|
-
user_name: userName,
|
|
566
|
-
agileflow_folder: agileflowFolder,
|
|
567
|
-
communication_language: 'English',
|
|
568
|
-
created_at: new Date().toISOString(),
|
|
569
|
-
};
|
|
570
|
-
|
|
571
|
-
await fs.writeFile(configPath, safeDump(config), 'utf8');
|
|
572
|
-
// Security: Set secure permissions (0o600) on config file
|
|
573
|
-
setSecurePermissions(configPath);
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
/**
|
|
579
|
-
* Create manifest file
|
|
580
|
-
* @param {string} cfgDir - Config directory
|
|
581
|
-
* @param {Object} config - Manifest configuration
|
|
582
|
-
* @param {Object} options - Options
|
|
583
|
-
* @param {boolean} options.force - Overwrite existing file
|
|
584
|
-
*/
|
|
585
|
-
async createManifest(cfgDir, config, options = {}) {
|
|
586
|
-
const { ides, userName, agileflowFolder, docsFolder } = config;
|
|
587
|
-
const packageJson = require(path.join(this.packageRoot, 'package.json'));
|
|
588
|
-
|
|
589
|
-
const manifestPath = path.join(cfgDir, 'manifest.yaml');
|
|
590
|
-
const now = new Date().toISOString();
|
|
591
|
-
|
|
592
|
-
if (!(await fs.pathExists(manifestPath))) {
|
|
593
|
-
const manifest = {
|
|
594
|
-
version: packageJson.version,
|
|
595
|
-
installed_at: now,
|
|
596
|
-
updated_at: now,
|
|
597
|
-
ides: ides,
|
|
598
|
-
modules: ['core'],
|
|
599
|
-
user_name: userName,
|
|
600
|
-
agileflow_folder: agileflowFolder || '.agileflow',
|
|
601
|
-
docs_folder: docsFolder || 'docs',
|
|
602
|
-
};
|
|
603
|
-
|
|
604
|
-
await fs.writeFile(manifestPath, safeDump(manifest), 'utf8');
|
|
605
|
-
// Security: Set secure permissions (0o600) on manifest file
|
|
606
|
-
setSecurePermissions(manifestPath);
|
|
607
|
-
return;
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
try {
|
|
611
|
-
const existingContent = await fs.readFile(manifestPath, 'utf8');
|
|
612
|
-
let loaded;
|
|
613
|
-
try {
|
|
614
|
-
loaded = safeLoad(existingContent);
|
|
615
|
-
} catch (parseErr) {
|
|
616
|
-
// Attach error code for YAML parse errors
|
|
617
|
-
throw createTypedError(`Failed to parse manifest.yaml: ${parseErr.message}`, 'EPARSE', {
|
|
618
|
-
cause: parseErr,
|
|
619
|
-
context: { manifestPath },
|
|
620
|
-
});
|
|
621
|
-
}
|
|
622
|
-
const existing = loaded && typeof loaded === 'object' && !Array.isArray(loaded) ? loaded : {};
|
|
623
|
-
|
|
624
|
-
const manifest = {
|
|
625
|
-
...existing,
|
|
626
|
-
version: packageJson.version,
|
|
627
|
-
installed_at: existing.installed_at || now,
|
|
628
|
-
updated_at: now,
|
|
629
|
-
ides: ides,
|
|
630
|
-
modules: existing.modules || ['core'],
|
|
631
|
-
user_name: userName,
|
|
632
|
-
agileflow_folder: agileflowFolder || existing.agileflow_folder || '.agileflow',
|
|
633
|
-
docs_folder: docsFolder || existing.docs_folder || 'docs',
|
|
634
|
-
};
|
|
635
|
-
|
|
636
|
-
await fs.writeFile(manifestPath, safeDump(manifest), 'utf8');
|
|
637
|
-
// Security: Set secure permissions (0o600) on manifest file
|
|
638
|
-
setSecurePermissions(manifestPath);
|
|
639
|
-
} catch (err) {
|
|
640
|
-
// If it's a typed parse error and not forcing, re-throw
|
|
641
|
-
if (err.errorCode === 'EPARSE' && !options.force) {
|
|
642
|
-
throw err;
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
if (options.force) {
|
|
646
|
-
const manifest = {
|
|
647
|
-
version: packageJson.version,
|
|
648
|
-
installed_at: now,
|
|
649
|
-
updated_at: now,
|
|
650
|
-
ides: ides,
|
|
651
|
-
modules: ['core'],
|
|
652
|
-
user_name: userName,
|
|
653
|
-
agileflow_folder: agileflowFolder || '.agileflow',
|
|
654
|
-
docs_folder: docsFolder || 'docs',
|
|
655
|
-
};
|
|
656
|
-
|
|
657
|
-
await fs.writeFile(manifestPath, safeDump(manifest), 'utf8');
|
|
658
|
-
// Security: Set secure permissions (0o600) on manifest file
|
|
659
|
-
setSecurePermissions(manifestPath);
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
/**
|
|
665
|
-
* Count installed items
|
|
666
|
-
* @param {string} agileflowDir - AgileFlow directory
|
|
667
|
-
* @returns {Promise<Object>} Counts
|
|
668
|
-
*/
|
|
669
|
-
async countInstalledItems(agileflowDir) {
|
|
670
|
-
const counts = {
|
|
671
|
-
agents: 0,
|
|
672
|
-
commands: 0,
|
|
673
|
-
skills: 0,
|
|
674
|
-
};
|
|
675
|
-
|
|
676
|
-
// Count agents
|
|
677
|
-
const agentsDir = path.join(agileflowDir, 'agents');
|
|
678
|
-
if (await fs.pathExists(agentsDir)) {
|
|
679
|
-
const files = await fs.readdir(agentsDir);
|
|
680
|
-
counts.agents = files.filter(f => f.endsWith('.md')).length;
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
// Count commands
|
|
684
|
-
const commandsDir = path.join(agileflowDir, 'commands');
|
|
685
|
-
if (await fs.pathExists(commandsDir)) {
|
|
686
|
-
const files = await fs.readdir(commandsDir);
|
|
687
|
-
counts.commands = files.filter(f => f.endsWith('.md')).length;
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
// Count skills
|
|
691
|
-
const skillsDir = path.join(agileflowDir, 'skills');
|
|
692
|
-
if (await fs.pathExists(skillsDir)) {
|
|
693
|
-
const entries = await fs.readdir(skillsDir, { withFileTypes: true });
|
|
694
|
-
counts.skills = entries.filter(e => e.isDirectory()).length;
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
return counts;
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
/**
|
|
701
|
-
* Copy CHANGELOG.md to .agileflow/ for /agileflow:whats-new command
|
|
702
|
-
* @param {string} agileflowDir - AgileFlow installation directory
|
|
703
|
-
* @param {Object} options - Installation options
|
|
704
|
-
* @param {boolean} options.force - Overwrite existing file
|
|
705
|
-
*/
|
|
706
|
-
async installChangelog(agileflowDir, options = {}) {
|
|
707
|
-
const { force = false } = options;
|
|
708
|
-
const changelogSource = path.join(this.packageRoot, 'CHANGELOG.md');
|
|
709
|
-
const changelogDest = path.join(agileflowDir, 'CHANGELOG.md');
|
|
710
|
-
|
|
711
|
-
// Skip if source changelog doesn't exist
|
|
712
|
-
if (!(await fs.pathExists(changelogSource))) {
|
|
713
|
-
return;
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
const destExists = await fs.pathExists(changelogDest);
|
|
717
|
-
|
|
718
|
-
// If destination exists and not forcing, check if identical
|
|
719
|
-
if (destExists && !force) {
|
|
720
|
-
const srcContent = await fs.readFile(changelogSource, 'utf8');
|
|
721
|
-
const destContent = await fs.readFile(changelogDest, 'utf8');
|
|
722
|
-
if (srcContent === destContent) {
|
|
723
|
-
return; // Identical, skip
|
|
724
|
-
}
|
|
725
|
-
// Different content - always update changelog since it's managed by package
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
// Copy the changelog
|
|
729
|
-
await fs.copy(changelogSource, changelogDest);
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
/**
|
|
733
|
-
* Install all scripts from packages/cli/scripts/ to .agileflow/scripts/
|
|
734
|
-
* Copies everything automatically - no manual list to maintain
|
|
735
|
-
* @param {string} directory - Project directory
|
|
736
|
-
* @param {Object} options - Installation options
|
|
737
|
-
* @param {boolean} options.force - Overwrite existing scripts
|
|
738
|
-
*/
|
|
739
|
-
async installScripts(directory, options = {}) {
|
|
740
|
-
const { force = false } = options;
|
|
741
|
-
const scriptsSourceDir = path.join(this.packageRoot, 'scripts');
|
|
742
|
-
const scriptsDestDir = path.join(directory, '.agileflow', 'scripts');
|
|
743
|
-
|
|
744
|
-
// Skip if source scripts directory doesn't exist
|
|
745
|
-
if (!(await fs.pathExists(scriptsSourceDir))) {
|
|
746
|
-
return;
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
// Ensure destination scripts directory exists
|
|
750
|
-
await fs.ensureDir(scriptsDestDir);
|
|
751
|
-
|
|
752
|
-
// Copy all scripts recursively
|
|
753
|
-
await this.copyScriptsRecursive(scriptsSourceDir, scriptsDestDir, force);
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
/**
|
|
757
|
-
* Copy lib/ directory to user's project
|
|
758
|
-
* Scripts in .agileflow/scripts/ require ../lib/ for shared utilities
|
|
759
|
-
* @param {string} directory - Project directory
|
|
760
|
-
* @param {Object} options - Installation options
|
|
761
|
-
* @param {boolean} options.force - Overwrite existing files
|
|
762
|
-
*/
|
|
763
|
-
async installLib(directory, options = {}) {
|
|
764
|
-
const { force = false } = options;
|
|
765
|
-
const libSourceDir = path.join(this.packageRoot, 'lib');
|
|
766
|
-
const libDestDir = path.join(directory, '.agileflow', 'lib');
|
|
767
|
-
|
|
768
|
-
// Skip if source lib directory doesn't exist
|
|
769
|
-
if (!(await fs.pathExists(libSourceDir))) {
|
|
770
|
-
return;
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
// Ensure destination lib directory exists
|
|
774
|
-
await fs.ensureDir(libDestDir);
|
|
775
|
-
|
|
776
|
-
// Copy all lib files recursively (reuse scripts copy logic)
|
|
777
|
-
await this.copyScriptsRecursive(libSourceDir, libDestDir, force);
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
/**
|
|
781
|
-
* Recursively copy scripts from source to destination
|
|
782
|
-
* @param {string} srcDir - Source directory
|
|
783
|
-
* @param {string} destDir - Destination directory
|
|
784
|
-
* @param {boolean} force - Overwrite existing files
|
|
785
|
-
* @param {string} [baseDir] - Base directory for path validation (defaults to destDir on first call)
|
|
786
|
-
*/
|
|
787
|
-
async copyScriptsRecursive(srcDir, destDir, force, baseDir = null) {
|
|
788
|
-
const entries = await fs.readdir(srcDir, { withFileTypes: true });
|
|
789
|
-
|
|
790
|
-
// Use destDir as base for validation on first call
|
|
791
|
-
const validationBase = baseDir || destDir;
|
|
792
|
-
|
|
793
|
-
for (const entry of entries) {
|
|
794
|
-
const srcPath = path.join(srcDir, entry.name);
|
|
795
|
-
const destPath = path.join(destDir, entry.name);
|
|
796
|
-
|
|
797
|
-
// Validate destination path to prevent traversal via malicious filenames
|
|
798
|
-
this.validateInstallPath(destPath, validationBase);
|
|
799
|
-
|
|
800
|
-
if (entry.isDirectory()) {
|
|
801
|
-
// Recursively copy subdirectories
|
|
802
|
-
await fs.ensureDir(destPath);
|
|
803
|
-
await this.copyScriptsRecursive(srcPath, destPath, force, validationBase);
|
|
804
|
-
} else {
|
|
805
|
-
// Copy file
|
|
806
|
-
const destExists = await fs.pathExists(destPath);
|
|
807
|
-
|
|
808
|
-
// If destination exists and not forcing, check if identical
|
|
809
|
-
if (destExists && !force) {
|
|
810
|
-
const srcContent = await fs.readFile(srcPath);
|
|
811
|
-
const destContent = await fs.readFile(destPath);
|
|
812
|
-
if (srcContent.equals(destContent)) {
|
|
813
|
-
continue; // Identical, skip
|
|
814
|
-
}
|
|
815
|
-
// Different content but not forcing, skip to preserve user changes
|
|
816
|
-
continue;
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
// Copy the file
|
|
820
|
-
await fs.copy(srcPath, destPath);
|
|
821
|
-
|
|
822
|
-
// Make executable on Unix for shell scripts and JS files
|
|
823
|
-
if (process.platform !== 'win32') {
|
|
824
|
-
const ext = path.extname(entry.name).toLowerCase();
|
|
825
|
-
if (['.sh', '.js'].includes(ext)) {
|
|
826
|
-
await fs.chmod(destPath, 0o755);
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
/**
|
|
834
|
-
* Get installation status
|
|
835
|
-
* @param {string} directory - Project directory
|
|
836
|
-
* @returns {Promise<Object>} Installation status
|
|
837
|
-
*/
|
|
838
|
-
async getStatus(directory) {
|
|
839
|
-
const status = {
|
|
840
|
-
installed: false,
|
|
841
|
-
path: null,
|
|
842
|
-
version: null,
|
|
843
|
-
ides: [],
|
|
844
|
-
modules: [],
|
|
845
|
-
userName: null,
|
|
846
|
-
agileflowFolder: null,
|
|
847
|
-
docsFolder: null,
|
|
848
|
-
installedAt: null,
|
|
849
|
-
updatedAt: null,
|
|
850
|
-
};
|
|
851
|
-
|
|
852
|
-
// Look for AgileFlow installation
|
|
853
|
-
const possibleFolders = ['.agileflow', 'agileflow', '.aflow'];
|
|
854
|
-
|
|
855
|
-
for (const folder of possibleFolders) {
|
|
856
|
-
const agileflowDir = path.join(directory, folder);
|
|
857
|
-
const manifestPath = path.join(agileflowDir, '_cfg', 'manifest.yaml');
|
|
858
|
-
|
|
859
|
-
if (await fs.pathExists(manifestPath)) {
|
|
860
|
-
status.installed = true;
|
|
861
|
-
status.path = agileflowDir;
|
|
862
|
-
|
|
863
|
-
const manifestContent = await fs.readFile(manifestPath, 'utf8');
|
|
864
|
-
let manifest;
|
|
865
|
-
try {
|
|
866
|
-
manifest = safeLoad(manifestContent);
|
|
867
|
-
} catch (parseErr) {
|
|
868
|
-
// Attach error code for YAML parse errors
|
|
869
|
-
throw createTypedError(`Failed to parse manifest.yaml: ${parseErr.message}`, 'EPARSE', {
|
|
870
|
-
cause: parseErr,
|
|
871
|
-
context: { manifestPath },
|
|
872
|
-
});
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
status.version = manifest.version;
|
|
876
|
-
status.ides = manifest.ides || [];
|
|
877
|
-
status.modules = manifest.modules || [];
|
|
878
|
-
status.userName = manifest.user_name || 'Developer';
|
|
879
|
-
status.agileflowFolder = manifest.agileflow_folder || folder;
|
|
880
|
-
status.docsFolder = manifest.docs_folder || 'docs';
|
|
881
|
-
status.installedAt = manifest.installed_at;
|
|
882
|
-
status.updatedAt = manifest.updated_at;
|
|
883
|
-
|
|
884
|
-
break;
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
return status;
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
module.exports = { Installer, getSourcePath, getPackageRoot };
|