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,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* Each Claude Code hook event has one thin dispatcher binary in
|
|
5
|
+
* `apps/cli/bin/hooks/`. Every dispatcher delegates to `runEvent()`
|
|
6
|
+
* here, which:
|
|
7
|
+
*
|
|
8
|
+
* 1. Loads `.agileflow/hook-manifest.yaml`.
|
|
9
|
+
* 2. Filters to hooks for the requested event whose `enabled !== false`.
|
|
10
|
+
* 3. Applies user overrides from `agileflow.config.json.hooks` (per-hook
|
|
11
|
+
* enabled / timeout / skipOnError).
|
|
12
|
+
* 4. Topologically orders the chain via runAfter, detecting cycles.
|
|
13
|
+
* 5. Spawns each hook's `script` as a child process, forwarding the
|
|
14
|
+
* original stdin so Claude Code's payload reaches the hook.
|
|
15
|
+
* 6. Enforces per-hook timeout via AbortController.
|
|
16
|
+
* 7. Logs each step to `.agileflow/logs/hook-execution.jsonl`.
|
|
17
|
+
* 8. Honors `skipOnError`: a non-zero exit becomes a logged WARN that
|
|
18
|
+
* does not interrupt the chain.
|
|
19
|
+
* 9. Exits 0 unless a hook with `skipOnError: false` failed.
|
|
20
|
+
*
|
|
21
|
+
* The actual child-process spawn is in `runHook` — it's exported and
|
|
22
|
+
* injectable so unit tests can substitute a stub without touching the
|
|
23
|
+
* filesystem.
|
|
24
|
+
*/
|
|
25
|
+
const path = require('path');
|
|
26
|
+
const fs = require('fs');
|
|
27
|
+
const { spawn } = require('child_process');
|
|
28
|
+
|
|
29
|
+
const { loadHookManifest } = require('./manifest-loader.js');
|
|
30
|
+
const { orderChain } = require('./chain.js');
|
|
31
|
+
const { appendHookLog } = require('./logger.js');
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @typedef {import('./manifest-loader.js').HookEntry} HookEntry
|
|
35
|
+
*
|
|
36
|
+
* @typedef {Object} RunHookResult
|
|
37
|
+
* @property {number|null} exitCode - null when timed out / signal-killed
|
|
38
|
+
* @property {string} stdout
|
|
39
|
+
* @property {string} stderr
|
|
40
|
+
* @property {number} durationMs
|
|
41
|
+
* @property {boolean} timedOut
|
|
42
|
+
*
|
|
43
|
+
* @typedef {Object} HookOverride
|
|
44
|
+
* @property {boolean} [enabled]
|
|
45
|
+
* @property {number} [timeout]
|
|
46
|
+
* @property {boolean} [skipOnError]
|
|
47
|
+
*
|
|
48
|
+
* @typedef {Object} RunEventOptions
|
|
49
|
+
* @property {string} event - e.g. "SessionStart" or "PreToolUse"
|
|
50
|
+
* @property {string} agileflowDir - absolute path to .agileflow/
|
|
51
|
+
* @property {string} [matcher] - the actual tool name (e.g. "Bash") for
|
|
52
|
+
* tool-related events. Used to filter manifest hooks whose
|
|
53
|
+
* own `matcher` pattern selects this tool.
|
|
54
|
+
* @property {Buffer|string} [stdin] - data forwarded to each hook on stdin
|
|
55
|
+
* @property {Record<string, HookOverride>} [overrides] - from agileflow.config.json.hooks
|
|
56
|
+
* @property {(hook: HookEntry, ctx: { stdin: Buffer|string, agileflowDir: string }) => Promise<RunHookResult>} [runHook]
|
|
57
|
+
* Injected for tests; defaults to `defaultRunHook`.
|
|
58
|
+
*
|
|
59
|
+
* @typedef {Object} ChainOutcome
|
|
60
|
+
* @property {number} exitCode - 0 unless a non-skipOnError hook failed
|
|
61
|
+
* @property {Array<{ hook: HookEntry, status: string, exitCode: number|null, durationMs: number }>} steps
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Default child-process runner for hook scripts.
|
|
66
|
+
*
|
|
67
|
+
* Picks an interpreter based on extension:
|
|
68
|
+
* .js → `node <script>`
|
|
69
|
+
* .sh → `bash <script>`
|
|
70
|
+
* else → execute directly (must have a shebang and be executable)
|
|
71
|
+
*
|
|
72
|
+
* Times out via AbortController after `hook.timeout` ms.
|
|
73
|
+
*
|
|
74
|
+
* @type {(hook: HookEntry, ctx: { stdin: Buffer|string, agileflowDir: string }) => Promise<RunHookResult>}
|
|
75
|
+
*/
|
|
76
|
+
async function defaultRunHook(hook, ctx) {
|
|
77
|
+
const ext = path.extname(hook.script).toLowerCase();
|
|
78
|
+
/** @type {string} */
|
|
79
|
+
let cmd;
|
|
80
|
+
/** @type {string[]} */
|
|
81
|
+
let args;
|
|
82
|
+
if (ext === '.js') {
|
|
83
|
+
cmd = process.execPath;
|
|
84
|
+
args = [hook.script];
|
|
85
|
+
} else if (ext === '.sh') {
|
|
86
|
+
cmd = 'bash';
|
|
87
|
+
args = [hook.script];
|
|
88
|
+
} else {
|
|
89
|
+
cmd = hook.script;
|
|
90
|
+
args = [];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const ac = new AbortController();
|
|
94
|
+
const timer = setTimeout(() => ac.abort(), hook.timeout);
|
|
95
|
+
|
|
96
|
+
const start = Date.now();
|
|
97
|
+
return await new Promise((resolve) => {
|
|
98
|
+
const child = spawn(cmd, args, {
|
|
99
|
+
cwd: path.dirname(ctx.agileflowDir),
|
|
100
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
101
|
+
signal: ac.signal,
|
|
102
|
+
env: { ...process.env, AGILEFLOW_DIR: ctx.agileflowDir },
|
|
103
|
+
});
|
|
104
|
+
/** @type {string[]} */
|
|
105
|
+
const out = [];
|
|
106
|
+
/** @type {string[]} */
|
|
107
|
+
const err = [];
|
|
108
|
+
child.stdout.on('data', (d) => out.push(d.toString('utf8')));
|
|
109
|
+
child.stderr.on('data', (d) => err.push(d.toString('utf8')));
|
|
110
|
+
child.on('error', () => {
|
|
111
|
+
// Spawn-level errors (ENOENT for missing script, abort signal) — we
|
|
112
|
+
// surface as exitCode null + timedOut iff we triggered the abort.
|
|
113
|
+
});
|
|
114
|
+
child.on('close', (code, signal) => {
|
|
115
|
+
clearTimeout(timer);
|
|
116
|
+
resolve({
|
|
117
|
+
exitCode: code,
|
|
118
|
+
stdout: out.join(''),
|
|
119
|
+
stderr: err.join(''),
|
|
120
|
+
durationMs: Date.now() - start,
|
|
121
|
+
timedOut: signal === 'SIGTERM' && ac.signal.aborted,
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
if (ctx.stdin != null) {
|
|
125
|
+
try {
|
|
126
|
+
child.stdin.end(ctx.stdin);
|
|
127
|
+
} catch {
|
|
128
|
+
/* swallow if stdin already closed */
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
131
|
+
child.stdin.end();
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Match a manifest matcher pattern against a tool name (or any actual
|
|
138
|
+
* value). Mirrors Claude Code's matcher semantics:
|
|
139
|
+
*
|
|
140
|
+
* - empty/null/undefined/`'*'` → match all
|
|
141
|
+
* - alphanumeric + `_` + `|` only → exact match against any pipe-
|
|
142
|
+
* separated alternative (`'Bash'` or `'Edit|Write'`)
|
|
143
|
+
* - anything else → JavaScript regex (`'^Notebook'`, `'mcp__memory__.*'`)
|
|
144
|
+
*
|
|
145
|
+
* @param {string|null|undefined} pattern
|
|
146
|
+
* @param {string|null|undefined} actual
|
|
147
|
+
* @returns {boolean}
|
|
148
|
+
*/
|
|
149
|
+
function matcherMatches(pattern, actual) {
|
|
150
|
+
if (!pattern || pattern === '*') return true;
|
|
151
|
+
if (typeof actual !== 'string' || actual.length === 0) return false;
|
|
152
|
+
if (/^[A-Za-z0-9_|]+$/.test(pattern)) {
|
|
153
|
+
return pattern.split('|').includes(actual);
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
return new RegExp(pattern).test(actual);
|
|
157
|
+
} catch {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Apply per-hook overrides from user config to a hook entry. Returns a
|
|
164
|
+
* new normalized HookEntry without mutating the input.
|
|
165
|
+
* @param {HookEntry} hook
|
|
166
|
+
* @param {HookOverride} [override]
|
|
167
|
+
* @returns {HookEntry}
|
|
168
|
+
*/
|
|
169
|
+
function applyOverride(hook, override) {
|
|
170
|
+
if (!override) return hook;
|
|
171
|
+
return {
|
|
172
|
+
...hook,
|
|
173
|
+
enabled: override.enabled ?? hook.enabled,
|
|
174
|
+
timeout: override.timeout ?? hook.timeout,
|
|
175
|
+
skipOnError: override.skipOnError ?? hook.skipOnError,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Resolve the script path against the agileflow-dir parent (the project
|
|
181
|
+
* root). Manifest paths are relative to the project root by convention.
|
|
182
|
+
* @param {HookEntry} hook
|
|
183
|
+
* @param {string} agileflowDir
|
|
184
|
+
* @returns {HookEntry}
|
|
185
|
+
*/
|
|
186
|
+
function resolveScriptPath(hook, agileflowDir) {
|
|
187
|
+
const projectRoot = path.dirname(agileflowDir);
|
|
188
|
+
const absolute = path.isAbsolute(hook.script)
|
|
189
|
+
? hook.script
|
|
190
|
+
: path.join(projectRoot, hook.script);
|
|
191
|
+
return { ...hook, script: absolute };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Run the chain for a given event. Always exits the orchestrator with
|
|
196
|
+
* code 0 unless a non-skipOnError hook fails or the chain itself fails
|
|
197
|
+
* to load (in which case the dispatcher decides whether to fail open).
|
|
198
|
+
*
|
|
199
|
+
* @param {RunEventOptions} options
|
|
200
|
+
* @returns {Promise<ChainOutcome>}
|
|
201
|
+
*/
|
|
202
|
+
async function runEvent(options) {
|
|
203
|
+
const {
|
|
204
|
+
event,
|
|
205
|
+
agileflowDir,
|
|
206
|
+
matcher,
|
|
207
|
+
stdin = '',
|
|
208
|
+
overrides = {},
|
|
209
|
+
runHook = defaultRunHook,
|
|
210
|
+
} = options;
|
|
211
|
+
|
|
212
|
+
const manifestPath = path.join(agileflowDir, 'hook-manifest.yaml');
|
|
213
|
+
const logPath = path.join(agileflowDir, 'logs', 'hook-execution.jsonl');
|
|
214
|
+
|
|
215
|
+
let manifest;
|
|
216
|
+
try {
|
|
217
|
+
manifest = await loadHookManifest(manifestPath);
|
|
218
|
+
} catch (err) {
|
|
219
|
+
// Bad manifest is treated as "no hooks" but logged for visibility.
|
|
220
|
+
await appendHookLog(logPath, {
|
|
221
|
+
timestamp: new Date().toISOString(),
|
|
222
|
+
event,
|
|
223
|
+
hookId: '<orchestrator>',
|
|
224
|
+
status: 'error',
|
|
225
|
+
exitCode: null,
|
|
226
|
+
durationMs: 0,
|
|
227
|
+
reason: `manifest load failed: ${err.message}`,
|
|
228
|
+
}).catch(() => {});
|
|
229
|
+
return { exitCode: 0, steps: [] };
|
|
230
|
+
}
|
|
231
|
+
if (!manifest) return { exitCode: 0, steps: [] };
|
|
232
|
+
|
|
233
|
+
// Filter to this event, then by matcher (a manifest hook with no
|
|
234
|
+
// matcher matches all; a manifest hook with a matcher only matches
|
|
235
|
+
// when the runtime supplied a matcher that satisfies it). Then
|
|
236
|
+
// apply user overrides and the enabled gate.
|
|
237
|
+
const eventHooks = manifest.hooks
|
|
238
|
+
.filter((h) => h.event === event)
|
|
239
|
+
.filter((h) => !h.matcher || matcherMatches(h.matcher, matcher))
|
|
240
|
+
.map((h) => applyOverride(h, overrides[h.id]))
|
|
241
|
+
.filter((h) => h.enabled);
|
|
242
|
+
|
|
243
|
+
if (!eventHooks.length) return { exitCode: 0, steps: [] };
|
|
244
|
+
|
|
245
|
+
// Topo sort within the event (orderChain only sees this event's hooks
|
|
246
|
+
// so cross-event runAfter is rejected as "unknown hook").
|
|
247
|
+
let ordered;
|
|
248
|
+
try {
|
|
249
|
+
ordered = orderChain(eventHooks);
|
|
250
|
+
} catch (err) {
|
|
251
|
+
await appendHookLog(logPath, {
|
|
252
|
+
timestamp: new Date().toISOString(),
|
|
253
|
+
event,
|
|
254
|
+
hookId: '<orchestrator>',
|
|
255
|
+
status: 'error',
|
|
256
|
+
exitCode: null,
|
|
257
|
+
durationMs: 0,
|
|
258
|
+
reason: `chain ordering failed: ${err.message}`,
|
|
259
|
+
}).catch(() => {});
|
|
260
|
+
return { exitCode: 1, steps: [] };
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/** @type {ChainOutcome['steps']} */
|
|
264
|
+
const steps = [];
|
|
265
|
+
let chainExitCode = 0;
|
|
266
|
+
|
|
267
|
+
for (const hook of ordered) {
|
|
268
|
+
const resolved = resolveScriptPath(hook, agileflowDir);
|
|
269
|
+
let result;
|
|
270
|
+
let runError = null;
|
|
271
|
+
try {
|
|
272
|
+
result = await runHook(resolved, { stdin, agileflowDir });
|
|
273
|
+
} catch (err) {
|
|
274
|
+
runError = err;
|
|
275
|
+
result = {
|
|
276
|
+
exitCode: null,
|
|
277
|
+
stdout: '',
|
|
278
|
+
stderr: err && err.message ? err.message : String(err),
|
|
279
|
+
durationMs: 0,
|
|
280
|
+
timedOut: false,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
const ok = result.exitCode === 0 && !runError;
|
|
284
|
+
/** @type {'ok'|'error'|'timeout'|'skipped'} */
|
|
285
|
+
const status = ok ? 'ok' : result.timedOut ? 'timeout' : 'error';
|
|
286
|
+
const skippedByOnError = !ok && hook.skipOnError === true;
|
|
287
|
+
|
|
288
|
+
await appendHookLog(logPath, {
|
|
289
|
+
timestamp: new Date().toISOString(),
|
|
290
|
+
event,
|
|
291
|
+
hookId: hook.id,
|
|
292
|
+
status,
|
|
293
|
+
exitCode: result.exitCode,
|
|
294
|
+
durationMs: result.durationMs,
|
|
295
|
+
stdout: result.stdout,
|
|
296
|
+
stderr: result.stderr,
|
|
297
|
+
skippedByOnError: skippedByOnError || undefined,
|
|
298
|
+
}).catch(() => {});
|
|
299
|
+
|
|
300
|
+
steps.push({
|
|
301
|
+
hook: resolved,
|
|
302
|
+
status,
|
|
303
|
+
exitCode: result.exitCode,
|
|
304
|
+
durationMs: result.durationMs,
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
if (!ok && !hook.skipOnError) {
|
|
308
|
+
chainExitCode = 1;
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return { exitCode: chainExitCode, steps };
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
module.exports = {
|
|
317
|
+
runEvent,
|
|
318
|
+
defaultRunHook,
|
|
319
|
+
applyOverride,
|
|
320
|
+
resolveScriptPath,
|
|
321
|
+
matcherMatches,
|
|
322
|
+
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IDE / CLI capability declarations.
|
|
3
|
+
*
|
|
4
|
+
* Different agentic IDEs and CLIs support different subsets of the
|
|
5
|
+
* AgileFlow surface. Claude Code is the full-feature target — hooks,
|
|
6
|
+
* skills, slash commands, subagents, MCP. Cursor / Windsurf / Codex
|
|
7
|
+
* support only a subset (mostly slash commands and MCP, with no
|
|
8
|
+
* formalized hook system).
|
|
9
|
+
*
|
|
10
|
+
* The installer reads this map to gate plugin content: a plugin's
|
|
11
|
+
* hooks won't be written into a `cursor` install because Cursor has
|
|
12
|
+
* nothing to invoke them; a plugin's skills won't be installed into
|
|
13
|
+
* Windsurf because Windsurf has no skill discovery mechanism. Plugins
|
|
14
|
+
* stay portable — the gate is at install time, not at authoring time.
|
|
15
|
+
*
|
|
16
|
+
* When an IDE adds a feature, bump its capability map here and the
|
|
17
|
+
* existing plugin content starts flowing into it on the next install.
|
|
18
|
+
*
|
|
19
|
+
* Note: the capability values reflect AgileFlow v4's *current*
|
|
20
|
+
* willingness to ship to that IDE. They are conservative for
|
|
21
|
+
* non-Claude-Code targets — it's safer to start with "off" and turn
|
|
22
|
+
* features on as we validate them than to ship features that silently
|
|
23
|
+
* misbehave.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {Object} IdeCapabilities
|
|
28
|
+
* @property {boolean} hooks - SessionStart / PreToolUse / etc. hooks
|
|
29
|
+
* @property {boolean} skills - skill discovery + invocation
|
|
30
|
+
* @property {boolean} commands - slash commands
|
|
31
|
+
* @property {boolean} agents - subagents
|
|
32
|
+
* @property {boolean} mcp - MCP server registration
|
|
33
|
+
* @property {string} settingsFile - path the installer writes IDE config to
|
|
34
|
+
* (relative to project root)
|
|
35
|
+
* @property {string} description - human-readable label for the picker
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/** @type {Record<string, IdeCapabilities>} */
|
|
39
|
+
const IDE_CAPABILITIES = {
|
|
40
|
+
'claude-code': {
|
|
41
|
+
hooks: true,
|
|
42
|
+
skills: true,
|
|
43
|
+
commands: true,
|
|
44
|
+
agents: true,
|
|
45
|
+
mcp: true,
|
|
46
|
+
settingsFile: '.claude/settings.json',
|
|
47
|
+
description: 'Anthropic Claude Code (full feature set)',
|
|
48
|
+
},
|
|
49
|
+
cursor: {
|
|
50
|
+
hooks: false,
|
|
51
|
+
skills: false,
|
|
52
|
+
commands: true,
|
|
53
|
+
agents: false,
|
|
54
|
+
mcp: true,
|
|
55
|
+
settingsFile: '.cursor/settings.json',
|
|
56
|
+
description: 'Cursor IDE (slash commands + MCP only)',
|
|
57
|
+
},
|
|
58
|
+
windsurf: {
|
|
59
|
+
hooks: false,
|
|
60
|
+
skills: false,
|
|
61
|
+
commands: true,
|
|
62
|
+
agents: false,
|
|
63
|
+
mcp: false,
|
|
64
|
+
settingsFile: '.windsurf/settings.json',
|
|
65
|
+
description: 'Windsurf (slash commands only)',
|
|
66
|
+
},
|
|
67
|
+
codex: {
|
|
68
|
+
hooks: false,
|
|
69
|
+
skills: false,
|
|
70
|
+
commands: false,
|
|
71
|
+
agents: false,
|
|
72
|
+
mcp: false,
|
|
73
|
+
settingsFile: '.codex/settings.json',
|
|
74
|
+
description: 'OpenAI Codex CLI (limited integration)',
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const SUPPORTED_IDES = Object.keys(IDE_CAPABILITIES);
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @param {string} ide
|
|
82
|
+
* @returns {IdeCapabilities}
|
|
83
|
+
* @throws {Error} when the ide is not recognized
|
|
84
|
+
*/
|
|
85
|
+
function capabilitiesFor(ide) {
|
|
86
|
+
const caps = IDE_CAPABILITIES[ide];
|
|
87
|
+
if (!caps) {
|
|
88
|
+
throw new Error(
|
|
89
|
+
`Unknown IDE "${ide}". Supported: ${SUPPORTED_IDES.join(', ')}`,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
return caps;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @param {string} ide
|
|
97
|
+
* @param {keyof IdeCapabilities} feature
|
|
98
|
+
* @returns {boolean}
|
|
99
|
+
*/
|
|
100
|
+
function supports(ide, feature) {
|
|
101
|
+
const caps = IDE_CAPABILITIES[ide];
|
|
102
|
+
if (!caps) return false;
|
|
103
|
+
return Boolean(caps[feature]);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
module.exports = {
|
|
107
|
+
IDE_CAPABILITIES,
|
|
108
|
+
SUPPORTED_IDES,
|
|
109
|
+
capabilitiesFor,
|
|
110
|
+
supports,
|
|
111
|
+
};
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code settings.json writer.
|
|
3
|
+
*
|
|
4
|
+
* When the target IDE is `claude-code`, the installer registers our 6
|
|
5
|
+
* hook entry points in `.claude/settings.json` so Claude Code actually
|
|
6
|
+
* invokes them at runtime. We use the unified `npx --no-install
|
|
7
|
+
* agileflow hook <event> [--matcher <m>]` command so the path resolves
|
|
8
|
+
* regardless of how the package is installed.
|
|
9
|
+
*
|
|
10
|
+
* The writer is **non-destructive**: it merges with any existing
|
|
11
|
+
* settings.json. Only AgileFlow-owned hook entries (identified by the
|
|
12
|
+
* `agileflow hook` substring in their command) are replaced; the
|
|
13
|
+
* user's other hook registrations and non-hook fields (permissions,
|
|
14
|
+
* env, etc.) are preserved.
|
|
15
|
+
*
|
|
16
|
+
* On a non-claude-code switch, `removeManagedHooks` strips our entries
|
|
17
|
+
* but leaves the rest of the file alone.
|
|
18
|
+
*/
|
|
19
|
+
const fs = require('fs');
|
|
20
|
+
const path = require('path');
|
|
21
|
+
|
|
22
|
+
const HOOK_COMMAND_MARKER = 'agileflow hook';
|
|
23
|
+
const HOOK_TIMEOUT_SECONDS = 30;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The 6 hook registrations we own. Each is `{ event, matcher?, command }`.
|
|
27
|
+
* The `command` is what gets baked into settings.json.
|
|
28
|
+
*/
|
|
29
|
+
const MANAGED_HOOKS = [
|
|
30
|
+
{ event: 'SessionStart', matcher: null, command: 'npx --no-install agileflow hook SessionStart' },
|
|
31
|
+
{ event: 'PreToolUse', matcher: 'Bash', command: 'npx --no-install agileflow hook PreToolUse --matcher Bash' },
|
|
32
|
+
{ event: 'PreToolUse', matcher: 'Edit', command: 'npx --no-install agileflow hook PreToolUse --matcher Edit' },
|
|
33
|
+
{ event: 'PreToolUse', matcher: 'Write', command: 'npx --no-install agileflow hook PreToolUse --matcher Write' },
|
|
34
|
+
{ event: 'PreCompact', matcher: null, command: 'npx --no-install agileflow hook PreCompact' },
|
|
35
|
+
{ event: 'Stop', matcher: null, command: 'npx --no-install agileflow hook Stop' },
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const MANAGED_EVENTS = new Set(MANAGED_HOOKS.map((h) => h.event));
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Detect whether a settings.json hook entry belongs to AgileFlow.
|
|
42
|
+
* @param {*} entry
|
|
43
|
+
*/
|
|
44
|
+
function isAgileflowEntry(entry) {
|
|
45
|
+
if (!entry || typeof entry !== 'object' || !Array.isArray(entry.hooks)) return false;
|
|
46
|
+
return entry.hooks.some(
|
|
47
|
+
(h) =>
|
|
48
|
+
h &&
|
|
49
|
+
typeof h === 'object' &&
|
|
50
|
+
h.type === 'command' &&
|
|
51
|
+
typeof h.command === 'string' &&
|
|
52
|
+
h.command.includes(HOOK_COMMAND_MARKER),
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Build a fresh entry for one MANAGED_HOOKS row.
|
|
58
|
+
* @param {{ matcher: string|null, command: string }} hook
|
|
59
|
+
*/
|
|
60
|
+
function buildEntry({ matcher, command }) {
|
|
61
|
+
/** @type {{ matcher?: string, hooks: object[] }} */
|
|
62
|
+
const out = {
|
|
63
|
+
hooks: [
|
|
64
|
+
{ type: 'command', command, timeout: HOOK_TIMEOUT_SECONDS },
|
|
65
|
+
],
|
|
66
|
+
};
|
|
67
|
+
if (matcher) out.matcher = matcher;
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Read existing settings.json (or {} if absent / malformed).
|
|
73
|
+
* @param {string} settingsPath
|
|
74
|
+
* @returns {Promise<object>}
|
|
75
|
+
*/
|
|
76
|
+
async function readExisting(settingsPath) {
|
|
77
|
+
try {
|
|
78
|
+
const raw = await fs.promises.readFile(settingsPath, 'utf8');
|
|
79
|
+
const parsed = JSON.parse(raw);
|
|
80
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return {};
|
|
81
|
+
return parsed;
|
|
82
|
+
} catch (err) {
|
|
83
|
+
if (err.code === 'ENOENT') return {};
|
|
84
|
+
if (err instanceof SyntaxError) return {};
|
|
85
|
+
throw err;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Atomically write the merged settings.
|
|
91
|
+
* @param {string} settingsPath
|
|
92
|
+
* @param {object} settings
|
|
93
|
+
*/
|
|
94
|
+
async function writeMerged(settingsPath, settings) {
|
|
95
|
+
await fs.promises.mkdir(path.dirname(settingsPath), { recursive: true });
|
|
96
|
+
const tmp = path.join(
|
|
97
|
+
path.dirname(settingsPath),
|
|
98
|
+
`.${path.basename(settingsPath)}.tmp-${process.pid}-${Math.random().toString(36).slice(2, 10)}`,
|
|
99
|
+
);
|
|
100
|
+
const text = JSON.stringify(settings, null, 2) + '\n';
|
|
101
|
+
try {
|
|
102
|
+
await fs.promises.writeFile(tmp, text, 'utf8');
|
|
103
|
+
await fs.promises.rename(tmp, settingsPath);
|
|
104
|
+
} catch (err) {
|
|
105
|
+
try {
|
|
106
|
+
await fs.promises.unlink(tmp);
|
|
107
|
+
} catch {
|
|
108
|
+
/* swallow */
|
|
109
|
+
}
|
|
110
|
+
throw err;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Compute the merged settings: drop any existing AgileFlow entries from
|
|
116
|
+
* the events we manage, append our fresh ones in the canonical order,
|
|
117
|
+
* leave everything else alone.
|
|
118
|
+
*
|
|
119
|
+
* @param {object} existing
|
|
120
|
+
* @returns {object}
|
|
121
|
+
*/
|
|
122
|
+
function mergeManagedHooks(existing) {
|
|
123
|
+
const next = { ...existing };
|
|
124
|
+
// Reject array-shaped `hooks`: spreading an array into a plain object
|
|
125
|
+
// produces numeric-keyed garbage. Treat it as missing instead.
|
|
126
|
+
const baseHooks =
|
|
127
|
+
existing.hooks &&
|
|
128
|
+
typeof existing.hooks === 'object' &&
|
|
129
|
+
!Array.isArray(existing.hooks)
|
|
130
|
+
? existing.hooks
|
|
131
|
+
: {};
|
|
132
|
+
const hooks = { ...baseHooks };
|
|
133
|
+
|
|
134
|
+
for (const event of MANAGED_EVENTS) {
|
|
135
|
+
const prior = Array.isArray(hooks[event]) ? hooks[event] : [];
|
|
136
|
+
// Strip our previous registrations, keep user's other entries.
|
|
137
|
+
const userEntries = prior.filter((e) => !isAgileflowEntry(e));
|
|
138
|
+
const ours = MANAGED_HOOKS.filter((h) => h.event === event).map(buildEntry);
|
|
139
|
+
hooks[event] = [...ours, ...userEntries];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
next.hooks = hooks;
|
|
143
|
+
return next;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Compute the unmanaged settings: strip AgileFlow entries from every
|
|
148
|
+
* event we manage. Used when switching to a non-claude-code IDE.
|
|
149
|
+
*
|
|
150
|
+
* @param {object} existing
|
|
151
|
+
* @returns {object}
|
|
152
|
+
*/
|
|
153
|
+
function unmanageHooks(existing) {
|
|
154
|
+
const next = { ...existing };
|
|
155
|
+
if (
|
|
156
|
+
!existing.hooks ||
|
|
157
|
+
typeof existing.hooks !== 'object' ||
|
|
158
|
+
Array.isArray(existing.hooks)
|
|
159
|
+
) {
|
|
160
|
+
return next;
|
|
161
|
+
}
|
|
162
|
+
const hooks = { ...existing.hooks };
|
|
163
|
+
for (const event of MANAGED_EVENTS) {
|
|
164
|
+
if (!Array.isArray(hooks[event])) continue;
|
|
165
|
+
const userEntries = hooks[event].filter((e) => !isAgileflowEntry(e));
|
|
166
|
+
if (userEntries.length === 0) {
|
|
167
|
+
delete hooks[event];
|
|
168
|
+
} else {
|
|
169
|
+
hooks[event] = userEntries;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// If hooks ended up with no keys, drop the field entirely.
|
|
173
|
+
if (Object.keys(hooks).length === 0) {
|
|
174
|
+
delete next.hooks;
|
|
175
|
+
} else {
|
|
176
|
+
next.hooks = hooks;
|
|
177
|
+
}
|
|
178
|
+
return next;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Register the 6 AgileFlow hooks in `<projectRoot>/.claude/settings.json`.
|
|
183
|
+
*
|
|
184
|
+
* @param {string} projectRoot
|
|
185
|
+
* @returns {Promise<string>} absolute path of the written settings file
|
|
186
|
+
*/
|
|
187
|
+
async function writeClaudeCodeSettings(projectRoot) {
|
|
188
|
+
const settingsPath = path.join(projectRoot, '.claude', 'settings.json');
|
|
189
|
+
const existing = await readExisting(settingsPath);
|
|
190
|
+
const merged = mergeManagedHooks(existing);
|
|
191
|
+
await writeMerged(settingsPath, merged);
|
|
192
|
+
return settingsPath;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Strip AgileFlow hook registrations from settings.json. If the file
|
|
197
|
+
* ends up empty (no hooks AND no other fields), it's removed; if it
|
|
198
|
+
* still has content, the cleaned version is written back.
|
|
199
|
+
*
|
|
200
|
+
* @param {string} projectRoot
|
|
201
|
+
* @returns {Promise<{ removed: boolean, settingsPath: string|null }>}
|
|
202
|
+
*/
|
|
203
|
+
async function removeClaudeCodeSettings(projectRoot) {
|
|
204
|
+
const settingsPath = path.join(projectRoot, '.claude', 'settings.json');
|
|
205
|
+
// readExisting already converts ENOENT and SyntaxError to {} — any
|
|
206
|
+
// exception here means a real filesystem problem (EACCES / EIO).
|
|
207
|
+
// Do NOT swallow: a silent success would lie about successfully
|
|
208
|
+
// removing entries that are still in an unreadable file.
|
|
209
|
+
const existing = await readExisting(settingsPath);
|
|
210
|
+
const next = unmanageHooks(existing);
|
|
211
|
+
if (Object.keys(next).length === 0) {
|
|
212
|
+
try {
|
|
213
|
+
await fs.promises.unlink(settingsPath);
|
|
214
|
+
return { removed: true, settingsPath };
|
|
215
|
+
} catch (err) {
|
|
216
|
+
if (err.code === 'ENOENT') return { removed: false, settingsPath: null };
|
|
217
|
+
throw err;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
await writeMerged(settingsPath, next);
|
|
221
|
+
return { removed: false, settingsPath };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
module.exports = {
|
|
225
|
+
writeClaudeCodeSettings,
|
|
226
|
+
removeClaudeCodeSettings,
|
|
227
|
+
mergeManagedHooks,
|
|
228
|
+
unmanageHooks,
|
|
229
|
+
isAgileflowEntry,
|
|
230
|
+
MANAGED_HOOKS,
|
|
231
|
+
MANAGED_EVENTS,
|
|
232
|
+
HOOK_COMMAND_MARKER,
|
|
233
|
+
HOOK_TIMEOUT_SECONDS,
|
|
234
|
+
};
|