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
package/scripts/ralph-loop.js
DELETED
|
@@ -1,1278 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* ralph-loop.js - Autonomous Story Processing Loop
|
|
5
|
-
*
|
|
6
|
-
* This script is the brain of AgileFlow's autonomous work mode.
|
|
7
|
-
* It runs as a Stop hook and handles:
|
|
8
|
-
* 1. Checking if loop mode is enabled
|
|
9
|
-
* 2. Running test validation
|
|
10
|
-
* 3. Running screenshot verification (Visual Mode)
|
|
11
|
-
* 4. Updating story status on success
|
|
12
|
-
* 5. Feeding context back for next iteration
|
|
13
|
-
* 6. Tracking iterations and enforcing limits
|
|
14
|
-
*
|
|
15
|
-
* Named after the "Ralph Wiggum" pattern from Anthropic.
|
|
16
|
-
*
|
|
17
|
-
* Visual Mode:
|
|
18
|
-
* When visual_mode is enabled, the loop also verifies that all
|
|
19
|
-
* screenshots have been reviewed (verified- prefix). This ensures
|
|
20
|
-
* Claude actually looks at UI screenshots before declaring completion.
|
|
21
|
-
*
|
|
22
|
-
* Usage (as Stop hook):
|
|
23
|
-
* node scripts/ralph-loop.js
|
|
24
|
-
*
|
|
25
|
-
* Manual control:
|
|
26
|
-
* node scripts/ralph-loop.js --status # Check loop status
|
|
27
|
-
* node scripts/ralph-loop.js --stop # Stop the loop
|
|
28
|
-
* node scripts/ralph-loop.js --reset # Reset loop state
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
|
-
const fs = require('fs');
|
|
32
|
-
const path = require('path');
|
|
33
|
-
const { execFileSync, spawnSync } = require('child_process');
|
|
34
|
-
|
|
35
|
-
// Shared utilities
|
|
36
|
-
const { c } = require('../lib/colors');
|
|
37
|
-
const { getProjectRoot, getStatusPath, getSessionStatePath } = require('../lib/paths');
|
|
38
|
-
const { safeReadJSON, safeWriteJSON, tryOptional } = require('../lib/errors');
|
|
39
|
-
const { isValidEpicId, parseIntBounded } = require('../lib/validate');
|
|
40
|
-
|
|
41
|
-
// Agent Teams integration (lazy-loaded)
|
|
42
|
-
let _featureFlags, _taskSync, _messagingBridge;
|
|
43
|
-
function getFeatureFlags() {
|
|
44
|
-
if (!_featureFlags) {
|
|
45
|
-
try {
|
|
46
|
-
_featureFlags = require('../lib/feature-flags');
|
|
47
|
-
} catch (e) {
|
|
48
|
-
_featureFlags = null;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return _featureFlags;
|
|
52
|
-
}
|
|
53
|
-
function getTaskSync() {
|
|
54
|
-
if (!_taskSync) {
|
|
55
|
-
try {
|
|
56
|
-
_taskSync = require('./lib/task-sync');
|
|
57
|
-
} catch (e) {
|
|
58
|
-
_taskSync = null;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return _taskSync;
|
|
62
|
-
}
|
|
63
|
-
function getMessagingBridge() {
|
|
64
|
-
if (!_messagingBridge) {
|
|
65
|
-
try {
|
|
66
|
-
_messagingBridge = require('./messaging-bridge');
|
|
67
|
-
} catch (e) {
|
|
68
|
-
_messagingBridge = null;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return _messagingBridge;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Read session state
|
|
75
|
-
function getSessionState(rootDir) {
|
|
76
|
-
const statePath = getSessionStatePath(rootDir);
|
|
77
|
-
const result = safeReadJSON(statePath, { defaultValue: {} });
|
|
78
|
-
return result.ok ? result.data : {};
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Write session state
|
|
82
|
-
function saveSessionState(rootDir, state) {
|
|
83
|
-
const statePath = getSessionStatePath(rootDir);
|
|
84
|
-
safeWriteJSON(statePath, state, { createDir: true });
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Read status.json for stories
|
|
88
|
-
function getStatus(rootDir) {
|
|
89
|
-
const statusPath = getStatusPath(rootDir);
|
|
90
|
-
const result = safeReadJSON(statusPath, { defaultValue: { stories: {}, epics: {} } });
|
|
91
|
-
return result.ok ? result.data : { stories: {}, epics: {} };
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Save status.json
|
|
95
|
-
function saveStatus(rootDir, status) {
|
|
96
|
-
const statusPath = getStatusPath(rootDir);
|
|
97
|
-
safeWriteJSON(statusPath, status);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Get test command from package.json or metadata
|
|
101
|
-
function getTestCommand(rootDir) {
|
|
102
|
-
// Check agileflow metadata first
|
|
103
|
-
const metadataPath = path.join(rootDir, 'docs/00-meta/agileflow-metadata.json');
|
|
104
|
-
const result = safeReadJSON(metadataPath, { defaultValue: {} });
|
|
105
|
-
|
|
106
|
-
if (result.ok && result.data?.ralph_loop?.test_command) {
|
|
107
|
-
return result.data.ralph_loop.test_command;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Default to npm test
|
|
111
|
-
return 'npm test';
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Run tests and return result
|
|
115
|
-
function runTests(rootDir, testCommand) {
|
|
116
|
-
const result = { passed: false, output: '', duration: 0 };
|
|
117
|
-
const startTime = Date.now();
|
|
118
|
-
|
|
119
|
-
try {
|
|
120
|
-
// Parse test command into executable and args
|
|
121
|
-
const parts = testCommand.split(/\s+/);
|
|
122
|
-
const cmd = parts[0];
|
|
123
|
-
const args = parts.slice(1);
|
|
124
|
-
|
|
125
|
-
const output = execFileSync(cmd, args, {
|
|
126
|
-
cwd: rootDir,
|
|
127
|
-
encoding: 'utf8',
|
|
128
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
129
|
-
timeout: 300000, // 5 minute timeout
|
|
130
|
-
});
|
|
131
|
-
result.passed = true;
|
|
132
|
-
result.output = output;
|
|
133
|
-
} catch (e) {
|
|
134
|
-
result.passed = false;
|
|
135
|
-
result.output = (e.stdout || '') + '\n' + (e.stderr || '');
|
|
136
|
-
if (e.message) {
|
|
137
|
-
result.output += '\n' + e.message;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
result.duration = Date.now() - startTime;
|
|
142
|
-
return result;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Get coverage command from metadata or default
|
|
146
|
-
function getCoverageCommand(rootDir) {
|
|
147
|
-
const metadataPath = path.join(rootDir, 'docs/00-meta/agileflow-metadata.json');
|
|
148
|
-
const result = safeReadJSON(metadataPath, { defaultValue: {} });
|
|
149
|
-
|
|
150
|
-
if (result.ok && result.data?.ralph_loop?.coverage_command) {
|
|
151
|
-
return result.data.ralph_loop.coverage_command;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Default: try common coverage commands
|
|
155
|
-
return 'npm run test:coverage || npm test -- --coverage';
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Get coverage report path from metadata or default
|
|
159
|
-
function getCoverageReportPath(rootDir) {
|
|
160
|
-
const metadataPath = path.join(rootDir, 'docs/00-meta/agileflow-metadata.json');
|
|
161
|
-
const result = safeReadJSON(metadataPath, { defaultValue: {} });
|
|
162
|
-
|
|
163
|
-
if (result.ok && result.data?.ralph_loop?.coverage_report_path) {
|
|
164
|
-
return result.data.ralph_loop.coverage_report_path;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return 'coverage/coverage-summary.json';
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// ===== DISCRETION MARKERS =====
|
|
171
|
-
// Semantic conditions wrapped in **...**
|
|
172
|
-
// These are evaluated by the loop to determine completion
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Built-in discretion conditions that can be evaluated programmatically
|
|
176
|
-
* Format: condition key -> evaluation function
|
|
177
|
-
*/
|
|
178
|
-
const DISCRETION_CONDITIONS = {
|
|
179
|
-
// Test-related conditions
|
|
180
|
-
'all tests passing': (rootDir, _ctx) => {
|
|
181
|
-
const testCommand = getTestCommand(rootDir);
|
|
182
|
-
const result = runTests(rootDir, testCommand);
|
|
183
|
-
return {
|
|
184
|
-
passed: result.passed,
|
|
185
|
-
message: result.passed
|
|
186
|
-
? 'All tests passing'
|
|
187
|
-
: `Tests failing: ${result.output.split('\n').slice(-3).join(' ').substring(0, 100)}`,
|
|
188
|
-
};
|
|
189
|
-
},
|
|
190
|
-
|
|
191
|
-
'tests pass': (rootDir, _ctx) => {
|
|
192
|
-
const testCommand = getTestCommand(rootDir);
|
|
193
|
-
const result = runTests(rootDir, testCommand);
|
|
194
|
-
return {
|
|
195
|
-
passed: result.passed,
|
|
196
|
-
message: result.passed ? 'Tests pass' : 'Tests failing',
|
|
197
|
-
};
|
|
198
|
-
},
|
|
199
|
-
|
|
200
|
-
// Coverage conditions (requires threshold in context)
|
|
201
|
-
'coverage above threshold': (rootDir, ctx) => {
|
|
202
|
-
const threshold = ctx.coverageThreshold || 80;
|
|
203
|
-
const result = verifyCoverage(rootDir, threshold);
|
|
204
|
-
return {
|
|
205
|
-
passed: result.passed,
|
|
206
|
-
message: `Coverage: ${result.coverage?.toFixed(1) || 0}% (threshold: ${threshold}%)`,
|
|
207
|
-
};
|
|
208
|
-
},
|
|
209
|
-
|
|
210
|
-
// Lint conditions
|
|
211
|
-
'no linting errors': (rootDir, _ctx) => {
|
|
212
|
-
try {
|
|
213
|
-
execFileSync('npm', ['run', 'lint'], {
|
|
214
|
-
cwd: rootDir,
|
|
215
|
-
encoding: 'utf8',
|
|
216
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
217
|
-
timeout: 120000,
|
|
218
|
-
});
|
|
219
|
-
return { passed: true, message: 'No linting errors' };
|
|
220
|
-
} catch (e) {
|
|
221
|
-
return { passed: false, message: 'Linting errors found' };
|
|
222
|
-
}
|
|
223
|
-
},
|
|
224
|
-
|
|
225
|
-
// Type checking conditions
|
|
226
|
-
'no type errors': (rootDir, _ctx) => {
|
|
227
|
-
try {
|
|
228
|
-
execFileSync('npx', ['tsc', '--noEmit'], {
|
|
229
|
-
cwd: rootDir,
|
|
230
|
-
encoding: 'utf8',
|
|
231
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
232
|
-
timeout: 120000,
|
|
233
|
-
});
|
|
234
|
-
return { passed: true, message: 'No type errors' };
|
|
235
|
-
} catch (e) {
|
|
236
|
-
return { passed: false, message: 'Type errors found' };
|
|
237
|
-
}
|
|
238
|
-
},
|
|
239
|
-
|
|
240
|
-
// Build conditions
|
|
241
|
-
'build succeeds': (rootDir, _ctx) => {
|
|
242
|
-
try {
|
|
243
|
-
execFileSync('npm', ['run', 'build'], {
|
|
244
|
-
cwd: rootDir,
|
|
245
|
-
encoding: 'utf8',
|
|
246
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
247
|
-
timeout: 300000,
|
|
248
|
-
});
|
|
249
|
-
return { passed: true, message: 'Build succeeds' };
|
|
250
|
-
} catch (e) {
|
|
251
|
-
return { passed: false, message: 'Build failed' };
|
|
252
|
-
}
|
|
253
|
-
},
|
|
254
|
-
|
|
255
|
-
// Screenshot/visual conditions
|
|
256
|
-
'all screenshots verified': (rootDir, _ctx) => {
|
|
257
|
-
const result = verifyScreenshots(rootDir);
|
|
258
|
-
return {
|
|
259
|
-
passed: result.passed,
|
|
260
|
-
message: result.passed
|
|
261
|
-
? 'All screenshots verified'
|
|
262
|
-
: `${result.unverified?.length || 0} unverified screenshots`,
|
|
263
|
-
};
|
|
264
|
-
},
|
|
265
|
-
|
|
266
|
-
// AC conditions (checks story acceptance criteria in status.json)
|
|
267
|
-
'all acceptance criteria verified': (rootDir, ctx) => {
|
|
268
|
-
const storyId = ctx.currentStoryId;
|
|
269
|
-
if (!storyId) {
|
|
270
|
-
return { passed: false, message: 'No story ID in context' };
|
|
271
|
-
}
|
|
272
|
-
const status = getStatus(rootDir);
|
|
273
|
-
const story = status.stories?.[storyId];
|
|
274
|
-
if (!story) {
|
|
275
|
-
return { passed: false, message: `Story ${storyId} not found` };
|
|
276
|
-
}
|
|
277
|
-
// Check if story has AC and if they're marked complete
|
|
278
|
-
const ac = story.acceptance_criteria || story.ac || [];
|
|
279
|
-
if (!Array.isArray(ac) || ac.length === 0) {
|
|
280
|
-
return { passed: true, message: 'No AC defined (assuming complete)' };
|
|
281
|
-
}
|
|
282
|
-
// Check for ac_status field (supports auto-verified and likely-covered from ac-test-matcher)
|
|
283
|
-
const acStatus = story.ac_status || {};
|
|
284
|
-
const verifiedStatuses = ['verified', 'auto-verified', 'likely-covered'];
|
|
285
|
-
const verifiedCount = ac.filter(
|
|
286
|
-
(_, i) => verifiedStatuses.includes(acStatus[i]) || acStatus[i] === true
|
|
287
|
-
).length;
|
|
288
|
-
const allVerified = verifiedCount === ac.length;
|
|
289
|
-
return {
|
|
290
|
-
passed: allVerified,
|
|
291
|
-
message: allVerified ? 'All AC verified' : `${verifiedCount}/${ac.length} AC verified`,
|
|
292
|
-
};
|
|
293
|
-
},
|
|
294
|
-
|
|
295
|
-
// AC test coverage condition (uses ac-test-matcher for automated checks)
|
|
296
|
-
'ac test coverage sufficient': (rootDir, ctx) => {
|
|
297
|
-
const storyId = ctx.currentStoryId;
|
|
298
|
-
if (!storyId) {
|
|
299
|
-
return { passed: false, message: 'No story ID in context' };
|
|
300
|
-
}
|
|
301
|
-
try {
|
|
302
|
-
const { matchACToTests } = require(path.join(__dirname, 'lib', 'ac-test-matcher'));
|
|
303
|
-
const result = matchACToTests(storyId, rootDir);
|
|
304
|
-
if (result.error) {
|
|
305
|
-
return { passed: false, message: result.error };
|
|
306
|
-
}
|
|
307
|
-
const threshold = ctx.coverageThreshold || 0.5;
|
|
308
|
-
const passed = result.coverage >= threshold;
|
|
309
|
-
return {
|
|
310
|
-
passed,
|
|
311
|
-
message: `AC test coverage: ${Math.round(result.coverage * 100)}% (${result.matched.length}/${result.total} matched, threshold: ${Math.round(threshold * 100)}%)`,
|
|
312
|
-
};
|
|
313
|
-
} catch (e) {
|
|
314
|
-
return { passed: false, message: `AC matcher error: ${e.message}` };
|
|
315
|
-
}
|
|
316
|
-
},
|
|
317
|
-
};
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* Parse discretion condition from string
|
|
321
|
-
* @param {string} condition - e.g., "**all tests passing**" or "**coverage above 80%**"
|
|
322
|
-
* @returns {object} { key, threshold? }
|
|
323
|
-
*/
|
|
324
|
-
function parseDiscretionCondition(condition) {
|
|
325
|
-
// Remove ** markers
|
|
326
|
-
const cleaned = condition.replace(/\*\*/g, '').trim().toLowerCase();
|
|
327
|
-
|
|
328
|
-
// Check for threshold patterns like "coverage above 80%"
|
|
329
|
-
const coverageMatch = cleaned.match(/coverage (?:above|>=?) (\d+)%?/);
|
|
330
|
-
if (coverageMatch) {
|
|
331
|
-
return { key: 'coverage above threshold', threshold: parseInt(coverageMatch[1]) };
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
return { key: cleaned };
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Evaluate a discretion condition
|
|
339
|
-
* @param {string} condition - The condition string (with or without ** markers)
|
|
340
|
-
* @param {string} rootDir - Project root
|
|
341
|
-
* @param {object} ctx - Context (currentStoryId, coverageThreshold, etc.)
|
|
342
|
-
* @returns {object} { passed: boolean, message: string }
|
|
343
|
-
*/
|
|
344
|
-
function evaluateDiscretionCondition(condition, rootDir, ctx = {}) {
|
|
345
|
-
const parsed = parseDiscretionCondition(condition);
|
|
346
|
-
|
|
347
|
-
// Set threshold in context if parsed from condition
|
|
348
|
-
if (parsed.threshold) {
|
|
349
|
-
ctx.coverageThreshold = parsed.threshold;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
const evaluator = DISCRETION_CONDITIONS[parsed.key];
|
|
353
|
-
if (!evaluator) {
|
|
354
|
-
return {
|
|
355
|
-
passed: false,
|
|
356
|
-
message: `Unknown condition: "${parsed.key}". Available: ${Object.keys(DISCRETION_CONDITIONS).join(', ')}`,
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
return evaluator(rootDir, ctx);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* Get discretion conditions from metadata
|
|
365
|
-
* @param {string} rootDir
|
|
366
|
-
* @returns {string[]} Array of condition strings
|
|
367
|
-
*/
|
|
368
|
-
function getDiscretionConditions(rootDir) {
|
|
369
|
-
const metadataPath = path.join(rootDir, 'docs/00-meta/agileflow-metadata.json');
|
|
370
|
-
const result = safeReadJSON(metadataPath, { defaultValue: {} });
|
|
371
|
-
|
|
372
|
-
if (result.ok && result.data?.ralph_loop?.conditions) {
|
|
373
|
-
return result.data.ralph_loop.conditions;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
return [];
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// Parse coverage report (Jest/NYC format)
|
|
380
|
-
function parseCoverageReport(rootDir) {
|
|
381
|
-
const reportPath = getCoverageReportPath(rootDir);
|
|
382
|
-
const fullPath = path.join(rootDir, reportPath);
|
|
383
|
-
const report = safeReadJSON(fullPath, { defaultValue: null });
|
|
384
|
-
|
|
385
|
-
if (!report.ok || !report.data) {
|
|
386
|
-
return { passed: false, coverage: 0, error: 'Coverage report not found at ' + reportPath };
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
// Jest/NYC format: { total: { lines: { pct: 80 }, statements: { pct: 80 } } }
|
|
390
|
-
const total = report.data.total;
|
|
391
|
-
if (!total) {
|
|
392
|
-
return { passed: false, coverage: 0, error: 'Invalid coverage report format' };
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
const coverage = total.lines?.pct || total.statements?.pct || 0;
|
|
396
|
-
|
|
397
|
-
return { passed: true, coverage, raw: report.data };
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// Verify coverage meets threshold
|
|
401
|
-
function verifyCoverage(rootDir, threshold) {
|
|
402
|
-
const result = parseCoverageReport(rootDir);
|
|
403
|
-
|
|
404
|
-
if (!result.passed) {
|
|
405
|
-
return {
|
|
406
|
-
passed: false,
|
|
407
|
-
coverage: 0,
|
|
408
|
-
message: `${c.red}✗ ${result.error}${c.reset}`,
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
const met = result.coverage >= threshold;
|
|
413
|
-
|
|
414
|
-
return {
|
|
415
|
-
passed: met,
|
|
416
|
-
coverage: result.coverage,
|
|
417
|
-
threshold: threshold,
|
|
418
|
-
message: met
|
|
419
|
-
? `${c.green}✓ Coverage ${result.coverage.toFixed(1)}% ≥ ${threshold}%${c.reset}`
|
|
420
|
-
: `${c.yellow}⏳ Coverage ${result.coverage.toFixed(1)}% < ${threshold}% (need ${(threshold - result.coverage).toFixed(1)}% more)${c.reset}`,
|
|
421
|
-
};
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
// Run coverage command
|
|
425
|
-
function runCoverage(rootDir) {
|
|
426
|
-
const coverageCmd = getCoverageCommand(rootDir);
|
|
427
|
-
const result = { passed: false, output: '', duration: 0 };
|
|
428
|
-
const startTime = Date.now();
|
|
429
|
-
|
|
430
|
-
try {
|
|
431
|
-
// Parse coverage command into executable and args
|
|
432
|
-
const parts = coverageCmd.split(/\s+/);
|
|
433
|
-
const cmd = parts[0];
|
|
434
|
-
const args = parts.slice(1);
|
|
435
|
-
|
|
436
|
-
const output = execFileSync(cmd, args, {
|
|
437
|
-
cwd: rootDir,
|
|
438
|
-
encoding: 'utf8',
|
|
439
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
440
|
-
timeout: 300000, // 5 minute timeout
|
|
441
|
-
});
|
|
442
|
-
result.passed = true;
|
|
443
|
-
result.output = output;
|
|
444
|
-
} catch (e) {
|
|
445
|
-
// Coverage command might fail but still generate report
|
|
446
|
-
result.passed = true; // We'll check the report
|
|
447
|
-
result.output = (e.stdout || '') + '\n' + (e.stderr || '');
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
result.duration = Date.now() - startTime;
|
|
451
|
-
return result;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
// Get screenshots directory from metadata or default
|
|
455
|
-
function getScreenshotsDir(rootDir) {
|
|
456
|
-
return (
|
|
457
|
-
tryOptional(() => {
|
|
458
|
-
const metadataPath = path.join(rootDir, 'docs/00-meta/agileflow-metadata.json');
|
|
459
|
-
if (fs.existsSync(metadataPath)) {
|
|
460
|
-
const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8'));
|
|
461
|
-
if (metadata.ralph_loop?.screenshots_dir) {
|
|
462
|
-
return metadata.ralph_loop.screenshots_dir;
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
}, 'metadata read') || './screenshots'
|
|
466
|
-
);
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
// Run screenshot verification (Visual Mode)
|
|
470
|
-
function verifyScreenshots(rootDir) {
|
|
471
|
-
const result = { passed: false, output: '', total: 0, verified: 0, unverified: [] };
|
|
472
|
-
const screenshotsDir = getScreenshotsDir(rootDir);
|
|
473
|
-
const fullPath = path.resolve(rootDir, screenshotsDir);
|
|
474
|
-
|
|
475
|
-
// Check if directory exists
|
|
476
|
-
if (!fs.existsSync(fullPath)) {
|
|
477
|
-
result.passed = true; // No screenshots = nothing to verify
|
|
478
|
-
result.output = 'No screenshots directory found';
|
|
479
|
-
return result;
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
// Get all image files
|
|
483
|
-
const imageExtensions = ['.png', '.jpg', '.jpeg', '.webp', '.gif'];
|
|
484
|
-
let files;
|
|
485
|
-
try {
|
|
486
|
-
files = fs.readdirSync(fullPath).filter(file => {
|
|
487
|
-
const ext = path.extname(file).toLowerCase();
|
|
488
|
-
return imageExtensions.includes(ext);
|
|
489
|
-
});
|
|
490
|
-
} catch (e) {
|
|
491
|
-
result.output = `Error reading screenshots directory: ${e.message}`;
|
|
492
|
-
return result;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
if (files.length === 0) {
|
|
496
|
-
result.passed = true;
|
|
497
|
-
result.output = 'No screenshots found in directory';
|
|
498
|
-
return result;
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
// Check each file for verified- prefix
|
|
502
|
-
for (const file of files) {
|
|
503
|
-
if (file.startsWith('verified-')) {
|
|
504
|
-
result.verified++;
|
|
505
|
-
} else {
|
|
506
|
-
result.unverified.push(file);
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
result.total = files.length;
|
|
511
|
-
result.passed = result.unverified.length === 0;
|
|
512
|
-
|
|
513
|
-
if (result.passed) {
|
|
514
|
-
result.output = `All ${result.total} screenshots verified`;
|
|
515
|
-
} else {
|
|
516
|
-
result.output = `${result.unverified.length} screenshots missing 'verified-' prefix`;
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
return result;
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
// Get next ready story in epic
|
|
523
|
-
function getNextStory(status, epicId, currentStoryId) {
|
|
524
|
-
const stories = status.stories || {};
|
|
525
|
-
|
|
526
|
-
// Get all stories in this epic that are ready
|
|
527
|
-
const readyStories = Object.entries(stories)
|
|
528
|
-
.filter(([id, story]) => {
|
|
529
|
-
return story.epic === epicId && story.status === 'ready' && id !== currentStoryId;
|
|
530
|
-
})
|
|
531
|
-
.sort((a, b) => {
|
|
532
|
-
// Sort by story number if possible
|
|
533
|
-
const numA = parseInt(a[0].replace(/\D/g, '')) || 0;
|
|
534
|
-
const numB = parseInt(b[0].replace(/\D/g, '')) || 0;
|
|
535
|
-
return numA - numB;
|
|
536
|
-
});
|
|
537
|
-
|
|
538
|
-
if (readyStories.length > 0) {
|
|
539
|
-
return { id: readyStories[0][0], ...readyStories[0][1] };
|
|
540
|
-
}
|
|
541
|
-
return null;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
// Check if a native team session is active
|
|
545
|
-
function isTeamSessionActive(rootDir) {
|
|
546
|
-
const ff = getFeatureFlags();
|
|
547
|
-
if (!ff || !ff.isAgentTeamsEnabled({ rootDir })) return false;
|
|
548
|
-
|
|
549
|
-
const state = getSessionState(rootDir);
|
|
550
|
-
return !!(state.active_team && state.active_team.status === 'running');
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
// Mark story as completed
|
|
554
|
-
function markStoryComplete(rootDir, storyId) {
|
|
555
|
-
const status = getStatus(rootDir);
|
|
556
|
-
if (status.stories && status.stories[storyId]) {
|
|
557
|
-
status.stories[storyId].status = 'completed';
|
|
558
|
-
status.stories[storyId].completed_at = new Date().toISOString();
|
|
559
|
-
saveStatus(rootDir, status);
|
|
560
|
-
|
|
561
|
-
// Sync to native task list when team session is active
|
|
562
|
-
if (isTeamSessionActive(rootDir)) {
|
|
563
|
-
const taskSync = getTaskSync();
|
|
564
|
-
if (taskSync) {
|
|
565
|
-
taskSync.syncToStatus(rootDir, storyId, {
|
|
566
|
-
status: 'completed',
|
|
567
|
-
completed_at: status.stories[storyId].completed_at,
|
|
568
|
-
});
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
return true;
|
|
573
|
-
}
|
|
574
|
-
return false;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
// Mark story as in_progress
|
|
578
|
-
function markStoryInProgress(rootDir, storyId) {
|
|
579
|
-
const status = getStatus(rootDir);
|
|
580
|
-
if (status.stories && status.stories[storyId]) {
|
|
581
|
-
status.stories[storyId].status = 'in_progress';
|
|
582
|
-
status.stories[storyId].started_at = new Date().toISOString();
|
|
583
|
-
saveStatus(rootDir, status);
|
|
584
|
-
|
|
585
|
-
// Sync to native task list when team session is active
|
|
586
|
-
if (isTeamSessionActive(rootDir)) {
|
|
587
|
-
const taskSync = getTaskSync();
|
|
588
|
-
if (taskSync) {
|
|
589
|
-
taskSync.syncToStatus(rootDir, storyId, {
|
|
590
|
-
status: 'in_progress',
|
|
591
|
-
started_at: status.stories[storyId].started_at,
|
|
592
|
-
});
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
return true;
|
|
597
|
-
}
|
|
598
|
-
return false;
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
// Get story details
|
|
602
|
-
function getStoryDetails(rootDir, storyId) {
|
|
603
|
-
const status = getStatus(rootDir);
|
|
604
|
-
if (status.stories && status.stories[storyId]) {
|
|
605
|
-
return { id: storyId, ...status.stories[storyId] };
|
|
606
|
-
}
|
|
607
|
-
return null;
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
// Count stories in epic by status
|
|
611
|
-
function getEpicProgress(status, epicId) {
|
|
612
|
-
const stories = status.stories || {};
|
|
613
|
-
const epicStories = Object.entries(stories).filter(([_, s]) => s.epic === epicId);
|
|
614
|
-
|
|
615
|
-
return {
|
|
616
|
-
total: epicStories.length,
|
|
617
|
-
completed: epicStories.filter(([_, s]) => s.status === 'completed').length,
|
|
618
|
-
in_progress: epicStories.filter(([_, s]) => s.status === 'in_progress').length,
|
|
619
|
-
ready: epicStories.filter(([_, s]) => s.status === 'ready').length,
|
|
620
|
-
blocked: epicStories.filter(([_, s]) => s.status === 'blocked').length,
|
|
621
|
-
};
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
// Main loop logic
|
|
625
|
-
function handleLoop(rootDir) {
|
|
626
|
-
const state = getSessionState(rootDir);
|
|
627
|
-
const loop = state.ralph_loop;
|
|
628
|
-
|
|
629
|
-
// Check if loop mode is enabled
|
|
630
|
-
if (!loop || !loop.enabled) {
|
|
631
|
-
return; // Silent exit - not in loop mode
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
const status = getStatus(rootDir);
|
|
635
|
-
const iteration = (loop.iteration || 0) + 1;
|
|
636
|
-
const maxIterations = loop.max_iterations || 20;
|
|
637
|
-
const visualMode = loop.visual_mode || false;
|
|
638
|
-
const coverageMode = loop.coverage_mode || false;
|
|
639
|
-
const coverageThreshold = loop.coverage_threshold || 80;
|
|
640
|
-
const discretionConditions = loop.conditions || getDiscretionConditions(rootDir);
|
|
641
|
-
// Visual, Coverage, and Discretion modes require at least 2 iterations for confirmation
|
|
642
|
-
const hasDiscretionConditions = discretionConditions.length > 0;
|
|
643
|
-
const minIterations = visualMode || coverageMode || hasDiscretionConditions ? 2 : 1;
|
|
644
|
-
|
|
645
|
-
console.log('');
|
|
646
|
-
console.log(
|
|
647
|
-
`${c.brand}${c.bold}══════════════════════════════════════════════════════════${c.reset}`
|
|
648
|
-
);
|
|
649
|
-
let modeLabel = '';
|
|
650
|
-
if (visualMode) modeLabel += ' [VISUAL]';
|
|
651
|
-
if (coverageMode) modeLabel += ` [COVERAGE ≥${coverageThreshold}%]`;
|
|
652
|
-
if (hasDiscretionConditions) modeLabel += ` [${discretionConditions.length} CONDITIONS]`;
|
|
653
|
-
console.log(
|
|
654
|
-
`${c.brand}${c.bold} RALPH LOOP - Iteration ${iteration}/${maxIterations}${modeLabel}${c.reset}`
|
|
655
|
-
);
|
|
656
|
-
console.log(
|
|
657
|
-
`${c.brand}${c.bold}══════════════════════════════════════════════════════════${c.reset}`
|
|
658
|
-
);
|
|
659
|
-
console.log('');
|
|
660
|
-
// State Narration: Loop iteration marker
|
|
661
|
-
console.log(`🔄 Iteration ${iteration}/${maxIterations}`);
|
|
662
|
-
|
|
663
|
-
// Check iteration limit
|
|
664
|
-
if (iteration > maxIterations) {
|
|
665
|
-
console.log(`${c.yellow}⚠ Max iterations (${maxIterations}) reached. Stopping loop.${c.reset}`);
|
|
666
|
-
console.log(`${c.dim}Run /agileflow:babysit MODE=loop to restart${c.reset}`);
|
|
667
|
-
state.ralph_loop.enabled = false;
|
|
668
|
-
state.ralph_loop.stopped_reason = 'max_iterations';
|
|
669
|
-
saveSessionState(rootDir, state);
|
|
670
|
-
return;
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
// Get current story
|
|
674
|
-
const currentStoryId = loop.current_story;
|
|
675
|
-
const currentStory = getStoryDetails(rootDir, currentStoryId);
|
|
676
|
-
const epicId = loop.epic;
|
|
677
|
-
|
|
678
|
-
if (!currentStory) {
|
|
679
|
-
console.log(`${c.red}✗ Current story ${currentStoryId} not found${c.reset}`);
|
|
680
|
-
state.ralph_loop.enabled = false;
|
|
681
|
-
state.ralph_loop.stopped_reason = 'story_not_found';
|
|
682
|
-
saveSessionState(rootDir, state);
|
|
683
|
-
return;
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
// State Narration: Current position marker
|
|
687
|
-
console.log(`📍 Working on: ${currentStoryId} - ${currentStory.title || 'Untitled'}`);
|
|
688
|
-
console.log('');
|
|
689
|
-
|
|
690
|
-
// Run tests
|
|
691
|
-
const testCommand = getTestCommand(rootDir);
|
|
692
|
-
console.log(`${c.blue}Running:${c.reset} ${testCommand}`);
|
|
693
|
-
console.log(`${c.dim}${'─'.repeat(58)}${c.reset}`);
|
|
694
|
-
|
|
695
|
-
const testResult = runTests(rootDir, testCommand);
|
|
696
|
-
|
|
697
|
-
if (testResult.passed) {
|
|
698
|
-
console.log(`${c.green}✓ Tests passed${c.reset} (${(testResult.duration / 1000).toFixed(1)}s)`);
|
|
699
|
-
|
|
700
|
-
// Visual Mode: Also verify screenshots
|
|
701
|
-
let screenshotResult = { passed: true };
|
|
702
|
-
if (visualMode) {
|
|
703
|
-
console.log('');
|
|
704
|
-
console.log(`${c.blue}Verifying screenshots...${c.reset}`);
|
|
705
|
-
screenshotResult = verifyScreenshots(rootDir);
|
|
706
|
-
|
|
707
|
-
if (screenshotResult.passed) {
|
|
708
|
-
console.log(`${c.green}✓ ${screenshotResult.output}${c.reset}`);
|
|
709
|
-
state.ralph_loop.screenshots_verified = true;
|
|
710
|
-
} else {
|
|
711
|
-
console.log(`${c.yellow}⚠ ${screenshotResult.output}${c.reset}`);
|
|
712
|
-
if (screenshotResult.unverified.length > 0) {
|
|
713
|
-
console.log(`${c.dim}Unverified screenshots:${c.reset}`);
|
|
714
|
-
screenshotResult.unverified.slice(0, 5).forEach(file => {
|
|
715
|
-
console.log(` ${c.yellow}- ${file}${c.reset}`);
|
|
716
|
-
});
|
|
717
|
-
if (screenshotResult.unverified.length > 5) {
|
|
718
|
-
console.log(
|
|
719
|
-
` ${c.dim}... and ${screenshotResult.unverified.length - 5} more${c.reset}`
|
|
720
|
-
);
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
state.ralph_loop.screenshots_verified = false;
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
// Coverage Mode: Run coverage and verify threshold
|
|
728
|
-
let coverageResult = { passed: true };
|
|
729
|
-
if (coverageMode) {
|
|
730
|
-
console.log('');
|
|
731
|
-
console.log(`${c.blue}Running coverage check...${c.reset}`);
|
|
732
|
-
runCoverage(rootDir);
|
|
733
|
-
coverageResult = verifyCoverage(rootDir, coverageThreshold);
|
|
734
|
-
console.log(coverageResult.message);
|
|
735
|
-
|
|
736
|
-
// Update state with current coverage
|
|
737
|
-
state.ralph_loop.coverage_current = coverageResult.coverage;
|
|
738
|
-
|
|
739
|
-
if (coverageResult.passed) {
|
|
740
|
-
state.ralph_loop.coverage_verified = true;
|
|
741
|
-
} else {
|
|
742
|
-
state.ralph_loop.coverage_verified = false;
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
// Enforce minimum iterations for Visual and Coverage modes
|
|
747
|
-
if ((visualMode || coverageMode) && iteration < minIterations) {
|
|
748
|
-
const modeNames = [];
|
|
749
|
-
if (visualMode) modeNames.push('Visual');
|
|
750
|
-
if (coverageMode) modeNames.push('Coverage');
|
|
751
|
-
|
|
752
|
-
console.log('');
|
|
753
|
-
console.log(
|
|
754
|
-
`${c.yellow}⚠ ${modeNames.join(' + ')} Mode requires ${minIterations}+ iterations for confirmation${c.reset}`
|
|
755
|
-
);
|
|
756
|
-
console.log(
|
|
757
|
-
`${c.dim}Current: iteration ${iteration}. Let loop run once more to confirm.${c.reset}`
|
|
758
|
-
);
|
|
759
|
-
|
|
760
|
-
state.ralph_loop.iteration = iteration;
|
|
761
|
-
saveSessionState(rootDir, state);
|
|
762
|
-
|
|
763
|
-
console.log('');
|
|
764
|
-
console.log(`${c.brand}▶ Continue working. Loop will verify again.${c.reset}`);
|
|
765
|
-
return;
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
// Evaluate discretion conditions
|
|
769
|
-
const discretionResults = [];
|
|
770
|
-
if (hasDiscretionConditions) {
|
|
771
|
-
console.log('');
|
|
772
|
-
console.log(`${c.blue}Evaluating discretion conditions...${c.reset}`);
|
|
773
|
-
const ctx = { currentStoryId, coverageThreshold };
|
|
774
|
-
|
|
775
|
-
for (const condition of discretionConditions) {
|
|
776
|
-
const result = evaluateDiscretionCondition(condition, rootDir, ctx);
|
|
777
|
-
discretionResults.push({ condition, ...result });
|
|
778
|
-
const marker = result.passed ? `${c.green}✓` : `${c.yellow}⏳`;
|
|
779
|
-
console.log(
|
|
780
|
-
` ${marker} **${condition.replace(/\*\*/g, '')}**: ${result.message}${c.reset}`
|
|
781
|
-
);
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
// Track which conditions have been verified
|
|
785
|
-
const allConditionsPassed = discretionResults.every(r => r.passed);
|
|
786
|
-
state.ralph_loop.conditions_verified = allConditionsPassed;
|
|
787
|
-
state.ralph_loop.condition_results = discretionResults.map(r => ({
|
|
788
|
-
condition: r.condition,
|
|
789
|
-
passed: r.passed,
|
|
790
|
-
message: r.message,
|
|
791
|
-
}));
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
// Check if all verification modes passed
|
|
795
|
-
const allDiscretionPassed = !hasDiscretionConditions || discretionResults.every(r => r.passed);
|
|
796
|
-
const canComplete =
|
|
797
|
-
testResult.passed &&
|
|
798
|
-
(!visualMode || screenshotResult.passed) &&
|
|
799
|
-
(!coverageMode || coverageResult.passed) &&
|
|
800
|
-
allDiscretionPassed;
|
|
801
|
-
|
|
802
|
-
if (!canComplete) {
|
|
803
|
-
// Something not verified yet
|
|
804
|
-
state.ralph_loop.iteration = iteration;
|
|
805
|
-
saveSessionState(rootDir, state);
|
|
806
|
-
|
|
807
|
-
console.log('');
|
|
808
|
-
if (visualMode && !screenshotResult.passed) {
|
|
809
|
-
console.log(`${c.cyan}▶ Review unverified screenshots:${c.reset}`);
|
|
810
|
-
console.log(`${c.dim} 1. View each screenshot in screenshots/ directory${c.reset}`);
|
|
811
|
-
console.log(`${c.dim} 2. Rename verified files with 'verified-' prefix${c.reset}`);
|
|
812
|
-
console.log(`${c.dim} 3. Loop will re-verify when you stop${c.reset}`);
|
|
813
|
-
}
|
|
814
|
-
if (coverageMode && !coverageResult.passed) {
|
|
815
|
-
console.log(`${c.cyan}▶ Increase test coverage:${c.reset}`);
|
|
816
|
-
console.log(`${c.dim} Current: ${coverageResult.coverage?.toFixed(1) || 0}%${c.reset}`);
|
|
817
|
-
console.log(`${c.dim} Target: ${coverageThreshold}%${c.reset}`);
|
|
818
|
-
console.log(`${c.dim} Write more tests to cover uncovered code paths.${c.reset}`);
|
|
819
|
-
}
|
|
820
|
-
if (hasDiscretionConditions && !allDiscretionPassed) {
|
|
821
|
-
const failedConditions = discretionResults.filter(r => !r.passed);
|
|
822
|
-
console.log(`${c.cyan}▶ Fix failing conditions:${c.reset}`);
|
|
823
|
-
for (const fc of failedConditions) {
|
|
824
|
-
console.log(`${c.dim} - ${fc.condition.replace(/\*\*/g, '')}: ${fc.message}${c.reset}`);
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
return;
|
|
828
|
-
}
|
|
829
|
-
console.log('');
|
|
830
|
-
|
|
831
|
-
// Mark story complete
|
|
832
|
-
markStoryComplete(rootDir, currentStoryId);
|
|
833
|
-
// State Narration: Completion marker
|
|
834
|
-
console.log(`✅ Story complete: ${currentStoryId}`);
|
|
835
|
-
console.log(`${c.green}✓ Marked ${currentStoryId} as completed${c.reset}`);
|
|
836
|
-
|
|
837
|
-
// Notify team via messaging bridge if team session is active
|
|
838
|
-
if (isTeamSessionActive(rootDir)) {
|
|
839
|
-
const bridge = getMessagingBridge();
|
|
840
|
-
if (bridge) {
|
|
841
|
-
bridge.sendMessage(rootDir, {
|
|
842
|
-
from: 'ralph-loop',
|
|
843
|
-
to: 'team-lead',
|
|
844
|
-
type: 'story_completed',
|
|
845
|
-
story_id: currentStoryId,
|
|
846
|
-
iteration,
|
|
847
|
-
});
|
|
848
|
-
console.log(`${c.dim} Notified team of completion${c.reset}`);
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
// Get next story
|
|
853
|
-
const nextStory = getNextStory(status, epicId, currentStoryId);
|
|
854
|
-
|
|
855
|
-
if (nextStory) {
|
|
856
|
-
// Move to next story
|
|
857
|
-
markStoryInProgress(rootDir, nextStory.id);
|
|
858
|
-
state.ralph_loop.current_story = nextStory.id;
|
|
859
|
-
state.ralph_loop.iteration = iteration;
|
|
860
|
-
saveSessionState(rootDir, state);
|
|
861
|
-
|
|
862
|
-
const progress = getEpicProgress(getStatus(rootDir), epicId);
|
|
863
|
-
console.log('');
|
|
864
|
-
console.log(`${c.cyan}━━━ Next Story ━━━${c.reset}`);
|
|
865
|
-
console.log(`${c.bold}${nextStory.id}:${c.reset} ${nextStory.title || 'Untitled'}`);
|
|
866
|
-
if (nextStory.acceptance_criteria) {
|
|
867
|
-
console.log(`${c.dim}Acceptance Criteria:${c.reset}`);
|
|
868
|
-
const criteria = Array.isArray(nextStory.acceptance_criteria)
|
|
869
|
-
? nextStory.acceptance_criteria
|
|
870
|
-
: [nextStory.acceptance_criteria];
|
|
871
|
-
criteria.slice(0, 3).forEach(ac => console.log(` • ${ac}`));
|
|
872
|
-
}
|
|
873
|
-
console.log('');
|
|
874
|
-
console.log(
|
|
875
|
-
`${c.dim}Epic Progress: ${progress.completed}/${progress.total} stories complete${c.reset}`
|
|
876
|
-
);
|
|
877
|
-
|
|
878
|
-
// Send next-story details to idle teammates via messaging bridge
|
|
879
|
-
if (isTeamSessionActive(rootDir)) {
|
|
880
|
-
const bridge = getMessagingBridge();
|
|
881
|
-
if (bridge) {
|
|
882
|
-
bridge.sendTaskAssignment(
|
|
883
|
-
rootDir,
|
|
884
|
-
'ralph-loop',
|
|
885
|
-
'team-lead',
|
|
886
|
-
nextStory.id,
|
|
887
|
-
`${nextStory.id}: ${nextStory.title || 'Untitled'}`
|
|
888
|
-
);
|
|
889
|
-
console.log(`${c.dim} Sent next-story assignment to team${c.reset}`);
|
|
890
|
-
}
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
console.log('');
|
|
894
|
-
console.log(`${c.brand}▶ Continue implementing ${nextStory.id}${c.reset}`);
|
|
895
|
-
console.log(`${c.dim} Run tests when ready. Loop will validate and continue.${c.reset}`);
|
|
896
|
-
} else {
|
|
897
|
-
// No more stories - epic complete!
|
|
898
|
-
const progress = getEpicProgress(getStatus(rootDir), epicId);
|
|
899
|
-
state.ralph_loop.enabled = false;
|
|
900
|
-
state.ralph_loop.stopped_reason = 'epic_complete';
|
|
901
|
-
state.ralph_loop.completed_at = new Date().toISOString();
|
|
902
|
-
saveSessionState(rootDir, state);
|
|
903
|
-
|
|
904
|
-
// Reconcile native task states back to status.json on epic completion
|
|
905
|
-
if (isTeamSessionActive(rootDir)) {
|
|
906
|
-
const taskSync = getTaskSync();
|
|
907
|
-
const bridge = getMessagingBridge();
|
|
908
|
-
if (taskSync) {
|
|
909
|
-
const syncResult = taskSync.syncFromStatus(rootDir, { epic: epicId });
|
|
910
|
-
if (syncResult.ok && syncResult.tasks.length > 0) {
|
|
911
|
-
taskSync.reconcile(rootDir, syncResult.tasks);
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
if (bridge) {
|
|
915
|
-
bridge.sendMessage(rootDir, {
|
|
916
|
-
from: 'ralph-loop',
|
|
917
|
-
to: 'team-lead',
|
|
918
|
-
type: 'coordination',
|
|
919
|
-
message: `Epic ${epicId} complete. All stories finished in ${iteration} iterations.`,
|
|
920
|
-
});
|
|
921
|
-
}
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
console.log('');
|
|
925
|
-
console.log(
|
|
926
|
-
`${c.green}${c.bold}════════════════════════════════════════════════════════${c.reset}`
|
|
927
|
-
);
|
|
928
|
-
console.log(`${c.green}${c.bold} 🎉 EPIC COMPLETE!${c.reset}`);
|
|
929
|
-
console.log(
|
|
930
|
-
`${c.green}${c.bold}════════════════════════════════════════════════════════${c.reset}`
|
|
931
|
-
);
|
|
932
|
-
console.log('');
|
|
933
|
-
console.log(`${c.green}Epic ${epicId} finished in ${iteration} iterations${c.reset}`);
|
|
934
|
-
console.log(`${c.dim}${progress.completed} stories completed${c.reset}`);
|
|
935
|
-
console.log('');
|
|
936
|
-
}
|
|
937
|
-
} else {
|
|
938
|
-
// Tests failed - feed back to Claude
|
|
939
|
-
// State Narration: Error marker
|
|
940
|
-
console.log(`⚠️ Error: Test failure - ${(testResult.duration / 1000).toFixed(1)}s`);
|
|
941
|
-
console.log(`${c.red}✗ Tests failed${c.reset} (${(testResult.duration / 1000).toFixed(1)}s)`);
|
|
942
|
-
console.log('');
|
|
943
|
-
|
|
944
|
-
state.ralph_loop.iteration = iteration;
|
|
945
|
-
state.ralph_loop.last_failure = new Date().toISOString();
|
|
946
|
-
saveSessionState(rootDir, state);
|
|
947
|
-
|
|
948
|
-
console.log(`${c.yellow}━━━ Test Failures ━━━${c.reset}`);
|
|
949
|
-
|
|
950
|
-
// Show truncated output (last 50 lines most relevant)
|
|
951
|
-
const outputLines = testResult.output.split('\n');
|
|
952
|
-
const relevantLines = outputLines.slice(-50);
|
|
953
|
-
console.log(relevantLines.join('\n'));
|
|
954
|
-
|
|
955
|
-
console.log('');
|
|
956
|
-
console.log(`${c.brand}▶ Fix the failing tests and continue${c.reset}`);
|
|
957
|
-
console.log(`${c.dim} Loop will re-run tests when you stop.${c.reset}`);
|
|
958
|
-
console.log(`${c.dim} Iteration ${iteration}/${maxIterations}${c.reset}`);
|
|
959
|
-
}
|
|
960
|
-
|
|
961
|
-
console.log('');
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
// Handle CLI arguments
|
|
965
|
-
function handleCLI() {
|
|
966
|
-
const args = process.argv.slice(2);
|
|
967
|
-
const rootDir = getProjectRoot();
|
|
968
|
-
|
|
969
|
-
if (args.includes('--status')) {
|
|
970
|
-
const state = getSessionState(rootDir);
|
|
971
|
-
const loop = state.ralph_loop;
|
|
972
|
-
|
|
973
|
-
if (!loop || !loop.enabled) {
|
|
974
|
-
console.log(`${c.dim}Ralph Loop: not active${c.reset}`);
|
|
975
|
-
} else {
|
|
976
|
-
let modeLabel = '';
|
|
977
|
-
if (loop.visual_mode) modeLabel += ` ${c.cyan}[VISUAL]${c.reset}`;
|
|
978
|
-
if (loop.coverage_mode)
|
|
979
|
-
modeLabel += ` ${c.magenta}[COVERAGE ≥${loop.coverage_threshold}%]${c.reset}`;
|
|
980
|
-
if (loop.conditions?.length > 0)
|
|
981
|
-
modeLabel += ` ${c.blue}[${loop.conditions.length} CONDITIONS]${c.reset}`;
|
|
982
|
-
console.log(`${c.green}Ralph Loop: active${c.reset}${modeLabel}`);
|
|
983
|
-
console.log(` Epic: ${loop.epic}`);
|
|
984
|
-
console.log(` Current Story: ${loop.current_story}`);
|
|
985
|
-
console.log(` Iteration: ${loop.iteration || 0}/${loop.max_iterations || 20}`);
|
|
986
|
-
if (loop.visual_mode) {
|
|
987
|
-
const verified = loop.screenshots_verified
|
|
988
|
-
? `${c.green}yes${c.reset}`
|
|
989
|
-
: `${c.yellow}no${c.reset}`;
|
|
990
|
-
console.log(` Screenshots Verified: ${verified}`);
|
|
991
|
-
}
|
|
992
|
-
if (loop.coverage_mode) {
|
|
993
|
-
const verified = loop.coverage_verified
|
|
994
|
-
? `${c.green}yes${c.reset}`
|
|
995
|
-
: `${c.yellow}no${c.reset}`;
|
|
996
|
-
console.log(
|
|
997
|
-
` Coverage: ${(loop.coverage_current || 0).toFixed(1)}% / ${loop.coverage_threshold}% (Verified: ${verified})`
|
|
998
|
-
);
|
|
999
|
-
console.log(` Baseline: ${(loop.coverage_baseline || 0).toFixed(1)}%`);
|
|
1000
|
-
}
|
|
1001
|
-
if (loop.conditions?.length > 0) {
|
|
1002
|
-
const verified = loop.conditions_verified
|
|
1003
|
-
? `${c.green}yes${c.reset}`
|
|
1004
|
-
: `${c.yellow}no${c.reset}`;
|
|
1005
|
-
console.log(
|
|
1006
|
-
` Discretion Conditions: ${loop.conditions.length} (All Verified: ${verified})`
|
|
1007
|
-
);
|
|
1008
|
-
for (const result of loop.condition_results || []) {
|
|
1009
|
-
const mark = result.passed ? `${c.green}✓${c.reset}` : `${c.yellow}⏳${c.reset}`;
|
|
1010
|
-
console.log(` ${mark} ${result.condition.replace(/\*\*/g, '')}`);
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
return true;
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
|
-
if (args.includes('--stop')) {
|
|
1018
|
-
const state = getSessionState(rootDir);
|
|
1019
|
-
if (state.ralph_loop) {
|
|
1020
|
-
state.ralph_loop.enabled = false;
|
|
1021
|
-
state.ralph_loop.stopped_reason = 'manual';
|
|
1022
|
-
saveSessionState(rootDir, state);
|
|
1023
|
-
console.log(`${c.yellow}Ralph Loop stopped${c.reset}`);
|
|
1024
|
-
} else {
|
|
1025
|
-
console.log(`${c.dim}Ralph Loop was not active${c.reset}`);
|
|
1026
|
-
}
|
|
1027
|
-
return true;
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
if (args.includes('--reset')) {
|
|
1031
|
-
const state = getSessionState(rootDir);
|
|
1032
|
-
delete state.ralph_loop;
|
|
1033
|
-
saveSessionState(rootDir, state);
|
|
1034
|
-
console.log(`${c.green}Ralph Loop state reset${c.reset}`);
|
|
1035
|
-
return true;
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
// Handle --init
|
|
1039
|
-
if (args.some(a => a.startsWith('--init'))) {
|
|
1040
|
-
const epicArg = args.find(a => a.startsWith('--epic='));
|
|
1041
|
-
const maxArg = args.find(a => a.startsWith('--max='));
|
|
1042
|
-
const visualArg = args.includes('--visual') || args.includes('-v');
|
|
1043
|
-
const coverageArg = args.find(a => a.startsWith('--coverage='));
|
|
1044
|
-
// Parse conditions (--condition="**all tests passing**" or -c "...")
|
|
1045
|
-
const conditionArgs = args.filter(a => a.startsWith('--condition=') || a.startsWith('-c='));
|
|
1046
|
-
const conditions = conditionArgs.map(a => a.split('=').slice(1).join('=').replace(/"/g, ''));
|
|
1047
|
-
|
|
1048
|
-
if (!epicArg) {
|
|
1049
|
-
console.log(`${c.red}Error: --epic=EP-XXXX is required${c.reset}`);
|
|
1050
|
-
return true;
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
const epicId = epicArg.split('=')[1];
|
|
1054
|
-
|
|
1055
|
-
// Validate epic ID format
|
|
1056
|
-
if (!isValidEpicId(epicId)) {
|
|
1057
|
-
console.log(`${c.red}Error: Invalid epic ID "${epicId}". Expected format: EP-XXXX${c.reset}`);
|
|
1058
|
-
return true;
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
// Validate and bound max iterations (1-100)
|
|
1062
|
-
const maxIterations = parseIntBounded(maxArg ? maxArg.split('=')[1] : null, 20, 1, 100);
|
|
1063
|
-
const visualMode = visualArg;
|
|
1064
|
-
|
|
1065
|
-
// Parse coverage threshold (0-100)
|
|
1066
|
-
let coverageMode = false;
|
|
1067
|
-
let coverageThreshold = 80;
|
|
1068
|
-
if (coverageArg) {
|
|
1069
|
-
coverageMode = true;
|
|
1070
|
-
const threshold = parseFloat(coverageArg.split('=')[1]);
|
|
1071
|
-
if (!isNaN(threshold)) {
|
|
1072
|
-
coverageThreshold = Math.max(0, Math.min(100, threshold));
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
|
|
1076
|
-
// Find first ready story in epic
|
|
1077
|
-
const status = getStatus(rootDir);
|
|
1078
|
-
const stories = status.stories || {};
|
|
1079
|
-
const readyStories = Object.entries(stories)
|
|
1080
|
-
.filter(([_, s]) => s.epic === epicId && s.status === 'ready')
|
|
1081
|
-
.sort((a, b) => {
|
|
1082
|
-
const numA = parseInt(a[0].replace(/\D/g, '')) || 0;
|
|
1083
|
-
const numB = parseInt(b[0].replace(/\D/g, '')) || 0;
|
|
1084
|
-
return numA - numB;
|
|
1085
|
-
});
|
|
1086
|
-
|
|
1087
|
-
if (readyStories.length === 0) {
|
|
1088
|
-
console.log(`${c.yellow}No ready stories found in ${epicId}${c.reset}`);
|
|
1089
|
-
console.log(`${c.dim}Create stories with status "ready" first${c.reset}`);
|
|
1090
|
-
return true;
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
const firstStory = readyStories[0];
|
|
1094
|
-
const storyId = firstStory[0];
|
|
1095
|
-
|
|
1096
|
-
// Mark first story as in_progress
|
|
1097
|
-
markStoryInProgress(rootDir, storyId);
|
|
1098
|
-
|
|
1099
|
-
// Get baseline coverage if coverage mode is enabled
|
|
1100
|
-
let coverageBaseline = 0;
|
|
1101
|
-
if (coverageMode) {
|
|
1102
|
-
console.log(`${c.dim}Running baseline coverage check...${c.reset}`);
|
|
1103
|
-
runCoverage(rootDir);
|
|
1104
|
-
const baseline = parseCoverageReport(rootDir);
|
|
1105
|
-
if (baseline.passed) {
|
|
1106
|
-
coverageBaseline = baseline.coverage;
|
|
1107
|
-
console.log(`${c.dim}Baseline coverage: ${coverageBaseline.toFixed(1)}%${c.reset}`);
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
|
-
|
|
1111
|
-
// Get conditions from metadata if not provided via CLI
|
|
1112
|
-
const allConditions = conditions.length > 0 ? conditions : getDiscretionConditions(rootDir);
|
|
1113
|
-
|
|
1114
|
-
// Initialize loop state
|
|
1115
|
-
const state = getSessionState(rootDir);
|
|
1116
|
-
state.ralph_loop = {
|
|
1117
|
-
enabled: true,
|
|
1118
|
-
epic: epicId,
|
|
1119
|
-
current_story: storyId,
|
|
1120
|
-
iteration: 0,
|
|
1121
|
-
max_iterations: maxIterations,
|
|
1122
|
-
visual_mode: visualMode,
|
|
1123
|
-
screenshots_verified: false,
|
|
1124
|
-
coverage_mode: coverageMode,
|
|
1125
|
-
coverage_threshold: coverageThreshold,
|
|
1126
|
-
coverage_baseline: coverageBaseline,
|
|
1127
|
-
coverage_current: coverageBaseline,
|
|
1128
|
-
coverage_verified: false,
|
|
1129
|
-
conditions: allConditions,
|
|
1130
|
-
conditions_verified: false,
|
|
1131
|
-
condition_results: [],
|
|
1132
|
-
started_at: new Date().toISOString(),
|
|
1133
|
-
};
|
|
1134
|
-
saveSessionState(rootDir, state);
|
|
1135
|
-
|
|
1136
|
-
const progress = getEpicProgress(status, epicId);
|
|
1137
|
-
|
|
1138
|
-
console.log('');
|
|
1139
|
-
let modeLabel = '';
|
|
1140
|
-
if (visualMode) modeLabel += ` ${c.cyan}[VISUAL]${c.reset}`;
|
|
1141
|
-
if (coverageMode) modeLabel += ` ${c.magenta}[COVERAGE ≥${coverageThreshold}%]${c.reset}`;
|
|
1142
|
-
console.log(`${c.green}${c.bold}Ralph Loop Initialized${c.reset}${modeLabel}`);
|
|
1143
|
-
console.log(`${c.dim}${'─'.repeat(40)}${c.reset}`);
|
|
1144
|
-
console.log(` Epic: ${c.cyan}${epicId}${c.reset}`);
|
|
1145
|
-
console.log(` Stories: ${progress.ready} ready, ${progress.total} total`);
|
|
1146
|
-
console.log(` Max Iterations: ${maxIterations}`);
|
|
1147
|
-
if (visualMode) {
|
|
1148
|
-
console.log(` Visual Mode: ${c.cyan}enabled${c.reset} (screenshot verification)`);
|
|
1149
|
-
}
|
|
1150
|
-
if (coverageMode) {
|
|
1151
|
-
console.log(
|
|
1152
|
-
` Coverage Mode: ${c.magenta}enabled${c.reset} (threshold: ${coverageThreshold}%)`
|
|
1153
|
-
);
|
|
1154
|
-
console.log(` Baseline: ${coverageBaseline.toFixed(1)}%`);
|
|
1155
|
-
}
|
|
1156
|
-
if (allConditions.length > 0) {
|
|
1157
|
-
console.log(` Conditions: ${c.blue}${allConditions.length} discretion conditions${c.reset}`);
|
|
1158
|
-
for (const cond of allConditions) {
|
|
1159
|
-
console.log(` - **${cond.replace(/\*\*/g, '')}**`);
|
|
1160
|
-
}
|
|
1161
|
-
}
|
|
1162
|
-
if (visualMode || coverageMode || allConditions.length > 0) {
|
|
1163
|
-
console.log(` Min Iterations: 2 (for confirmation)`);
|
|
1164
|
-
}
|
|
1165
|
-
console.log(`${c.dim}${'─'.repeat(40)}${c.reset}`);
|
|
1166
|
-
console.log('');
|
|
1167
|
-
console.log(`${c.brand}▶ Starting Story:${c.reset} ${storyId}`);
|
|
1168
|
-
console.log(` ${firstStory[1].title || 'Untitled'}`);
|
|
1169
|
-
if (firstStory[1].acceptance_criteria) {
|
|
1170
|
-
const criteria = Array.isArray(firstStory[1].acceptance_criteria)
|
|
1171
|
-
? firstStory[1].acceptance_criteria
|
|
1172
|
-
: [firstStory[1].acceptance_criteria];
|
|
1173
|
-
console.log(`${c.dim} Acceptance Criteria:${c.reset}`);
|
|
1174
|
-
criteria.slice(0, 3).forEach(ac => console.log(` • ${ac}`));
|
|
1175
|
-
}
|
|
1176
|
-
console.log('');
|
|
1177
|
-
console.log(
|
|
1178
|
-
`${c.dim}Work on this story. When you stop, tests will run automatically.${c.reset}`
|
|
1179
|
-
);
|
|
1180
|
-
console.log(`${c.dim}If tests pass, the next story will be loaded.${c.reset}`);
|
|
1181
|
-
console.log('');
|
|
1182
|
-
|
|
1183
|
-
return true;
|
|
1184
|
-
}
|
|
1185
|
-
|
|
1186
|
-
if (args.includes('--help')) {
|
|
1187
|
-
console.log(`
|
|
1188
|
-
${c.brand}${c.bold}ralph-loop.js${c.reset} - Autonomous Story Processing
|
|
1189
|
-
|
|
1190
|
-
${c.bold}Usage:${c.reset}
|
|
1191
|
-
node scripts/ralph-loop.js Run loop check (Stop hook)
|
|
1192
|
-
node scripts/ralph-loop.js --init --epic=EP-XXX Initialize loop for epic
|
|
1193
|
-
node scripts/ralph-loop.js --init --epic=EP-XXX --visual Initialize with Visual Mode
|
|
1194
|
-
node scripts/ralph-loop.js --init --epic=EP-XXX --coverage=80 Initialize with Coverage Mode
|
|
1195
|
-
node scripts/ralph-loop.js --init --epic=EP-XXX --condition="**all tests passing**"
|
|
1196
|
-
node scripts/ralph-loop.js --status Check loop status
|
|
1197
|
-
node scripts/ralph-loop.js --stop Stop the loop
|
|
1198
|
-
node scripts/ralph-loop.js --reset Reset loop state
|
|
1199
|
-
|
|
1200
|
-
${c.bold}Options:${c.reset}
|
|
1201
|
-
--epic=EP-XXXX Epic ID to process (required for --init)
|
|
1202
|
-
--max=N Max iterations (default: 20)
|
|
1203
|
-
--visual, -v Enable Visual Mode (screenshot verification)
|
|
1204
|
-
--coverage=N Enable Coverage Mode (iterate until N% coverage)
|
|
1205
|
-
--condition="..." Add discretion condition (can use multiple times)
|
|
1206
|
-
|
|
1207
|
-
${c.bold}Visual Mode:${c.reset}
|
|
1208
|
-
When --visual is enabled, the loop also verifies that all screenshots
|
|
1209
|
-
in the screenshots/ directory have been reviewed (verified- prefix).
|
|
1210
|
-
|
|
1211
|
-
This ensures Claude actually looks at UI screenshots before declaring
|
|
1212
|
-
completion. Requires minimum 2 iterations for confirmation.
|
|
1213
|
-
|
|
1214
|
-
${c.bold}Coverage Mode:${c.reset}
|
|
1215
|
-
When --coverage=N is enabled, the loop verifies test coverage meets
|
|
1216
|
-
the threshold N% before completing stories.
|
|
1217
|
-
|
|
1218
|
-
Coverage is read from coverage/coverage-summary.json (Jest/NYC format).
|
|
1219
|
-
Configure in docs/00-meta/agileflow-metadata.json:
|
|
1220
|
-
{ "ralph_loop": { "coverage_command": "npm run test:coverage" } }
|
|
1221
|
-
|
|
1222
|
-
Workflow:
|
|
1223
|
-
1. Tests run → must pass
|
|
1224
|
-
2. Coverage checked → must meet threshold
|
|
1225
|
-
3. Minimum 2 iterations → confirms coverage is stable
|
|
1226
|
-
4. Only then → story marked complete
|
|
1227
|
-
|
|
1228
|
-
${c.bold}Discretion Conditions:${c.reset}
|
|
1229
|
-
Semantic conditions that must pass before story completion.
|
|
1230
|
-
Use --condition multiple times for multiple conditions.
|
|
1231
|
-
|
|
1232
|
-
Built-in conditions:
|
|
1233
|
-
**all tests passing** Tests must pass
|
|
1234
|
-
**tests pass** Tests must pass (alias)
|
|
1235
|
-
**coverage above 80%** Coverage must meet threshold
|
|
1236
|
-
**no linting errors** npm run lint must pass
|
|
1237
|
-
**no type errors** npx tsc --noEmit must pass
|
|
1238
|
-
**build succeeds** npm run build must pass
|
|
1239
|
-
**all screenshots verified** Screenshots need verified- prefix
|
|
1240
|
-
**all acceptance criteria verified** AC marked complete in status.json
|
|
1241
|
-
|
|
1242
|
-
Configure in docs/00-meta/agileflow-metadata.json:
|
|
1243
|
-
{
|
|
1244
|
-
"ralph_loop": {
|
|
1245
|
-
"conditions": [
|
|
1246
|
-
"**all tests passing**",
|
|
1247
|
-
"**no linting errors**"
|
|
1248
|
-
]
|
|
1249
|
-
}
|
|
1250
|
-
}
|
|
1251
|
-
|
|
1252
|
-
${c.bold}How it works:${c.reset}
|
|
1253
|
-
1. Start loop with /agileflow:babysit EPIC=EP-XXX MODE=loop COVERAGE=80
|
|
1254
|
-
2. Work on the current story
|
|
1255
|
-
3. When you stop, this hook runs tests and verifications
|
|
1256
|
-
4. If all pass → story marked complete, next story loaded
|
|
1257
|
-
5. If any fail → failures shown, you continue fixing
|
|
1258
|
-
6. Loop repeats until epic done or max iterations
|
|
1259
|
-
`);
|
|
1260
|
-
return true;
|
|
1261
|
-
}
|
|
1262
|
-
|
|
1263
|
-
return false;
|
|
1264
|
-
}
|
|
1265
|
-
|
|
1266
|
-
// Main
|
|
1267
|
-
function main() {
|
|
1268
|
-
// Handle CLI commands first
|
|
1269
|
-
if (handleCLI()) {
|
|
1270
|
-
return;
|
|
1271
|
-
}
|
|
1272
|
-
|
|
1273
|
-
// Otherwise run the loop handler
|
|
1274
|
-
const rootDir = getProjectRoot();
|
|
1275
|
-
handleLoop(rootDir);
|
|
1276
|
-
}
|
|
1277
|
-
|
|
1278
|
-
main();
|