@promptwheel/cli 0.6.0
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/README.md +215 -0
- package/dist/bin/promptwheel.d.ts +17 -0
- package/dist/bin/promptwheel.d.ts.map +1 -0
- package/dist/bin/promptwheel.js +90 -0
- package/dist/bin/promptwheel.js.map +1 -0
- package/dist/commands/auto-auth.d.ts +26 -0
- package/dist/commands/auto-auth.d.ts.map +1 -0
- package/dist/commands/auto-auth.js +216 -0
- package/dist/commands/auto-auth.js.map +1 -0
- package/dist/commands/auto-ci-mode.d.ts +9 -0
- package/dist/commands/auto-ci-mode.d.ts.map +1 -0
- package/dist/commands/auto-ci-mode.js +176 -0
- package/dist/commands/auto-ci-mode.js.map +1 -0
- package/dist/commands/solo-analytics.d.ts +6 -0
- package/dist/commands/solo-analytics.d.ts.map +1 -0
- package/dist/commands/solo-analytics.js +267 -0
- package/dist/commands/solo-analytics.js.map +1 -0
- package/dist/commands/solo-auto.d.ts +6 -0
- package/dist/commands/solo-auto.d.ts.map +1 -0
- package/dist/commands/solo-auto.js +135 -0
- package/dist/commands/solo-auto.js.map +1 -0
- package/dist/commands/solo-daemon.d.ts +12 -0
- package/dist/commands/solo-daemon.d.ts.map +1 -0
- package/dist/commands/solo-daemon.js +177 -0
- package/dist/commands/solo-daemon.js.map +1 -0
- package/dist/commands/solo-exec.d.ts +6 -0
- package/dist/commands/solo-exec.d.ts.map +1 -0
- package/dist/commands/solo-exec.js +656 -0
- package/dist/commands/solo-exec.js.map +1 -0
- package/dist/commands/solo-inspect.d.ts +6 -0
- package/dist/commands/solo-inspect.d.ts.map +1 -0
- package/dist/commands/solo-inspect.js +765 -0
- package/dist/commands/solo-inspect.js.map +1 -0
- package/dist/commands/solo-lifecycle.d.ts +6 -0
- package/dist/commands/solo-lifecycle.d.ts.map +1 -0
- package/dist/commands/solo-lifecycle.js +226 -0
- package/dist/commands/solo-lifecycle.js.map +1 -0
- package/dist/commands/solo-nudge.d.ts +6 -0
- package/dist/commands/solo-nudge.d.ts.map +1 -0
- package/dist/commands/solo-nudge.js +49 -0
- package/dist/commands/solo-nudge.js.map +1 -0
- package/dist/commands/solo-qa.d.ts +6 -0
- package/dist/commands/solo-qa.d.ts.map +1 -0
- package/dist/commands/solo-qa.js +253 -0
- package/dist/commands/solo-qa.js.map +1 -0
- package/dist/commands/solo-trajectory.d.ts +6 -0
- package/dist/commands/solo-trajectory.d.ts.map +1 -0
- package/dist/commands/solo-trajectory.js +268 -0
- package/dist/commands/solo-trajectory.js.map +1 -0
- package/dist/commands/solo.d.ts +11 -0
- package/dist/commands/solo.d.ts.map +1 -0
- package/dist/commands/solo.js +52 -0
- package/dist/commands/solo.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/artifacts.d.ts +136 -0
- package/dist/lib/artifacts.d.ts.map +1 -0
- package/dist/lib/artifacts.js +147 -0
- package/dist/lib/artifacts.js.map +1 -0
- package/dist/lib/codebase-index.d.ts +5 -0
- package/dist/lib/codebase-index.d.ts.map +1 -0
- package/dist/lib/codebase-index.js +5 -0
- package/dist/lib/codebase-index.js.map +1 -0
- package/dist/lib/cycle-context.d.ts +62 -0
- package/dist/lib/cycle-context.d.ts.map +1 -0
- package/dist/lib/cycle-context.js +141 -0
- package/dist/lib/cycle-context.js.map +1 -0
- package/dist/lib/daemon-fork.d.ts +39 -0
- package/dist/lib/daemon-fork.d.ts.map +1 -0
- package/dist/lib/daemon-fork.js +164 -0
- package/dist/lib/daemon-fork.js.map +1 -0
- package/dist/lib/daemon-notifier.d.ts +29 -0
- package/dist/lib/daemon-notifier.d.ts.map +1 -0
- package/dist/lib/daemon-notifier.js +147 -0
- package/dist/lib/daemon-notifier.js.map +1 -0
- package/dist/lib/daemon.d.ts +75 -0
- package/dist/lib/daemon.d.ts.map +1 -0
- package/dist/lib/daemon.js +265 -0
- package/dist/lib/daemon.js.map +1 -0
- package/dist/lib/dedup-memory.d.ts +46 -0
- package/dist/lib/dedup-memory.d.ts.map +1 -0
- package/dist/lib/dedup-memory.js +190 -0
- package/dist/lib/dedup-memory.js.map +1 -0
- package/dist/lib/dedup.d.ts +37 -0
- package/dist/lib/dedup.d.ts.map +1 -0
- package/dist/lib/dedup.js +85 -0
- package/dist/lib/dedup.js.map +1 -0
- package/dist/lib/display-adapter-log.d.ts +26 -0
- package/dist/lib/display-adapter-log.d.ts.map +1 -0
- package/dist/lib/display-adapter-log.js +63 -0
- package/dist/lib/display-adapter-log.js.map +1 -0
- package/dist/lib/display-adapter-spinner.d.ts +28 -0
- package/dist/lib/display-adapter-spinner.d.ts.map +1 -0
- package/dist/lib/display-adapter-spinner.js +93 -0
- package/dist/lib/display-adapter-spinner.js.map +1 -0
- package/dist/lib/display-adapter-tui.d.ts +32 -0
- package/dist/lib/display-adapter-tui.d.ts.map +1 -0
- package/dist/lib/display-adapter-tui.js +77 -0
- package/dist/lib/display-adapter-tui.js.map +1 -0
- package/dist/lib/display-adapter.d.ts +40 -0
- package/dist/lib/display-adapter.d.ts.map +1 -0
- package/dist/lib/display-adapter.js +9 -0
- package/dist/lib/display-adapter.js.map +1 -0
- package/dist/lib/doctor.d.ts +45 -0
- package/dist/lib/doctor.d.ts.map +1 -0
- package/dist/lib/doctor.js +382 -0
- package/dist/lib/doctor.js.map +1 -0
- package/dist/lib/exclusion-index.d.ts +50 -0
- package/dist/lib/exclusion-index.d.ts.map +1 -0
- package/dist/lib/exclusion-index.js +249 -0
- package/dist/lib/exclusion-index.js.map +1 -0
- package/dist/lib/exec.d.ts +24 -0
- package/dist/lib/exec.d.ts.map +1 -0
- package/dist/lib/exec.js +295 -0
- package/dist/lib/exec.js.map +1 -0
- package/dist/lib/execution-backends/claude.d.ts +27 -0
- package/dist/lib/execution-backends/claude.d.ts.map +1 -0
- package/dist/lib/execution-backends/claude.js +161 -0
- package/dist/lib/execution-backends/claude.js.map +1 -0
- package/dist/lib/execution-backends/codex.d.ts +28 -0
- package/dist/lib/execution-backends/codex.d.ts.map +1 -0
- package/dist/lib/execution-backends/codex.js +216 -0
- package/dist/lib/execution-backends/codex.js.map +1 -0
- package/dist/lib/execution-backends/index.d.ts +10 -0
- package/dist/lib/execution-backends/index.d.ts.map +1 -0
- package/dist/lib/execution-backends/index.js +9 -0
- package/dist/lib/execution-backends/index.js.map +1 -0
- package/dist/lib/execution-backends/kimi.d.ts +25 -0
- package/dist/lib/execution-backends/kimi.d.ts.map +1 -0
- package/dist/lib/execution-backends/kimi.js +76 -0
- package/dist/lib/execution-backends/kimi.js.map +1 -0
- package/dist/lib/execution-backends/types.d.ts +38 -0
- package/dist/lib/execution-backends/types.d.ts.map +1 -0
- package/dist/lib/execution-backends/types.js +5 -0
- package/dist/lib/execution-backends/types.js.map +1 -0
- package/dist/lib/failure-classifier.d.ts +11 -0
- package/dist/lib/failure-classifier.d.ts.map +1 -0
- package/dist/lib/failure-classifier.js +24 -0
- package/dist/lib/failure-classifier.js.map +1 -0
- package/dist/lib/file-cooldown.d.ts +9 -0
- package/dist/lib/file-cooldown.d.ts.map +1 -0
- package/dist/lib/file-cooldown.js +75 -0
- package/dist/lib/file-cooldown.js.map +1 -0
- package/dist/lib/formulas.d.ts +52 -0
- package/dist/lib/formulas.d.ts.map +1 -0
- package/dist/lib/formulas.js +123 -0
- package/dist/lib/formulas.js.map +1 -0
- package/dist/lib/git.d.ts +9 -0
- package/dist/lib/git.d.ts.map +1 -0
- package/dist/lib/git.js +60 -0
- package/dist/lib/git.js.map +1 -0
- package/dist/lib/goals.d.ts +59 -0
- package/dist/lib/goals.d.ts.map +1 -0
- package/dist/lib/goals.js +216 -0
- package/dist/lib/goals.js.map +1 -0
- package/dist/lib/guidelines.d.ts +32 -0
- package/dist/lib/guidelines.d.ts.map +1 -0
- package/dist/lib/guidelines.js +102 -0
- package/dist/lib/guidelines.js.map +1 -0
- package/dist/lib/learnings.d.ts +65 -0
- package/dist/lib/learnings.d.ts.map +1 -0
- package/dist/lib/learnings.js +193 -0
- package/dist/lib/learnings.js.map +1 -0
- package/dist/lib/logger.d.ts +17 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +42 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/meta-learnings.d.ts +27 -0
- package/dist/lib/meta-learnings.d.ts.map +1 -0
- package/dist/lib/meta-learnings.js +270 -0
- package/dist/lib/meta-learnings.js.map +1 -0
- package/dist/lib/metrics.d.ts +48 -0
- package/dist/lib/metrics.d.ts.map +1 -0
- package/dist/lib/metrics.js +107 -0
- package/dist/lib/metrics.js.map +1 -0
- package/dist/lib/openai-local-execution.d.ts +29 -0
- package/dist/lib/openai-local-execution.d.ts.map +1 -0
- package/dist/lib/openai-local-execution.js +282 -0
- package/dist/lib/openai-local-execution.js.map +1 -0
- package/dist/lib/project-metadata/csharp.d.ts +6 -0
- package/dist/lib/project-metadata/csharp.d.ts.map +1 -0
- package/dist/lib/project-metadata/csharp.js +19 -0
- package/dist/lib/project-metadata/csharp.js.map +1 -0
- package/dist/lib/project-metadata/elixir.d.ts +6 -0
- package/dist/lib/project-metadata/elixir.d.ts.map +1 -0
- package/dist/lib/project-metadata/elixir.js +23 -0
- package/dist/lib/project-metadata/elixir.js.map +1 -0
- package/dist/lib/project-metadata/go.d.ts +6 -0
- package/dist/lib/project-metadata/go.d.ts.map +1 -0
- package/dist/lib/project-metadata/go.js +30 -0
- package/dist/lib/project-metadata/go.js.map +1 -0
- package/dist/lib/project-metadata/index.d.ts +13 -0
- package/dist/lib/project-metadata/index.d.ts.map +1 -0
- package/dist/lib/project-metadata/index.js +111 -0
- package/dist/lib/project-metadata/index.js.map +1 -0
- package/dist/lib/project-metadata/java.d.ts +6 -0
- package/dist/lib/project-metadata/java.d.ts.map +1 -0
- package/dist/lib/project-metadata/java.js +37 -0
- package/dist/lib/project-metadata/java.js.map +1 -0
- package/dist/lib/project-metadata/node.d.ts +6 -0
- package/dist/lib/project-metadata/node.d.ts.map +1 -0
- package/dist/lib/project-metadata/node.js +135 -0
- package/dist/lib/project-metadata/node.js.map +1 -0
- package/dist/lib/project-metadata/php.d.ts +6 -0
- package/dist/lib/project-metadata/php.d.ts.map +1 -0
- package/dist/lib/project-metadata/php.js +29 -0
- package/dist/lib/project-metadata/php.js.map +1 -0
- package/dist/lib/project-metadata/python.d.ts +6 -0
- package/dist/lib/project-metadata/python.d.ts.map +1 -0
- package/dist/lib/project-metadata/python.js +79 -0
- package/dist/lib/project-metadata/python.js.map +1 -0
- package/dist/lib/project-metadata/ruby.d.ts +6 -0
- package/dist/lib/project-metadata/ruby.d.ts.map +1 -0
- package/dist/lib/project-metadata/ruby.js +38 -0
- package/dist/lib/project-metadata/ruby.js.map +1 -0
- package/dist/lib/project-metadata/rust.d.ts +6 -0
- package/dist/lib/project-metadata/rust.d.ts.map +1 -0
- package/dist/lib/project-metadata/rust.js +33 -0
- package/dist/lib/project-metadata/rust.js.map +1 -0
- package/dist/lib/project-metadata/swift.d.ts +6 -0
- package/dist/lib/project-metadata/swift.d.ts.map +1 -0
- package/dist/lib/project-metadata/swift.js +19 -0
- package/dist/lib/project-metadata/swift.js.map +1 -0
- package/dist/lib/project-metadata/types.d.ts +37 -0
- package/dist/lib/project-metadata/types.d.ts.map +1 -0
- package/dist/lib/project-metadata/types.js +5 -0
- package/dist/lib/project-metadata/types.js.map +1 -0
- package/dist/lib/proposal-review.d.ts +9 -0
- package/dist/lib/proposal-review.d.ts.map +1 -0
- package/dist/lib/proposal-review.js +9 -0
- package/dist/lib/proposal-review.js.map +1 -0
- package/dist/lib/providers/claude.d.ts +6 -0
- package/dist/lib/providers/claude.d.ts.map +1 -0
- package/dist/lib/providers/claude.js +21 -0
- package/dist/lib/providers/claude.js.map +1 -0
- package/dist/lib/providers/codex.d.ts +6 -0
- package/dist/lib/providers/codex.d.ts.map +1 -0
- package/dist/lib/providers/codex.js +26 -0
- package/dist/lib/providers/codex.js.map +1 -0
- package/dist/lib/providers/index.d.ts +22 -0
- package/dist/lib/providers/index.d.ts.map +1 -0
- package/dist/lib/providers/index.js +39 -0
- package/dist/lib/providers/index.js.map +1 -0
- package/dist/lib/providers/kimi.d.ts +6 -0
- package/dist/lib/providers/kimi.d.ts.map +1 -0
- package/dist/lib/providers/kimi.js +22 -0
- package/dist/lib/providers/kimi.js.map +1 -0
- package/dist/lib/providers/openai-local.d.ts +8 -0
- package/dist/lib/providers/openai-local.d.ts.map +1 -0
- package/dist/lib/providers/openai-local.js +30 -0
- package/dist/lib/providers/openai-local.js.map +1 -0
- package/dist/lib/providers/types.d.ts +37 -0
- package/dist/lib/providers/types.d.ts.map +1 -0
- package/dist/lib/providers/types.js +5 -0
- package/dist/lib/providers/types.js.map +1 -0
- package/dist/lib/qa-stats.d.ts +113 -0
- package/dist/lib/qa-stats.d.ts.map +1 -0
- package/dist/lib/qa-stats.js +311 -0
- package/dist/lib/qa-stats.js.map +1 -0
- package/dist/lib/retention.d.ts +125 -0
- package/dist/lib/retention.d.ts.map +1 -0
- package/dist/lib/retention.js +767 -0
- package/dist/lib/retention.js.map +1 -0
- package/dist/lib/run-history.d.ts +52 -0
- package/dist/lib/run-history.d.ts.map +1 -0
- package/dist/lib/run-history.js +116 -0
- package/dist/lib/run-history.js.map +1 -0
- package/dist/lib/run-state.d.ts +129 -0
- package/dist/lib/run-state.d.ts.map +1 -0
- package/dist/lib/run-state.js +312 -0
- package/dist/lib/run-state.js.map +1 -0
- package/dist/lib/scope.d.ts +8 -0
- package/dist/lib/scope.d.ts.map +1 -0
- package/dist/lib/scope.js +8 -0
- package/dist/lib/scope.js.map +1 -0
- package/dist/lib/scout-prompt-builder.d.ts +22 -0
- package/dist/lib/scout-prompt-builder.d.ts.map +1 -0
- package/dist/lib/scout-prompt-builder.js +97 -0
- package/dist/lib/scout-prompt-builder.js.map +1 -0
- package/dist/lib/sectors.d.ts +21 -0
- package/dist/lib/sectors.d.ts.map +1 -0
- package/dist/lib/sectors.js +96 -0
- package/dist/lib/sectors.js.map +1 -0
- package/dist/lib/selection.d.ts +35 -0
- package/dist/lib/selection.d.ts.map +1 -0
- package/dist/lib/selection.js +110 -0
- package/dist/lib/selection.js.map +1 -0
- package/dist/lib/session-report.d.ts +26 -0
- package/dist/lib/session-report.d.ts.map +1 -0
- package/dist/lib/session-report.js +143 -0
- package/dist/lib/session-report.js.map +1 -0
- package/dist/lib/solo-auto-between-cycles.d.ts +10 -0
- package/dist/lib/solo-auto-between-cycles.d.ts.map +1 -0
- package/dist/lib/solo-auto-between-cycles.js +577 -0
- package/dist/lib/solo-auto-between-cycles.js.map +1 -0
- package/dist/lib/solo-auto-execute.d.ts +22 -0
- package/dist/lib/solo-auto-execute.d.ts.map +1 -0
- package/dist/lib/solo-auto-execute.js +530 -0
- package/dist/lib/solo-auto-execute.js.map +1 -0
- package/dist/lib/solo-auto-filter.d.ts +14 -0
- package/dist/lib/solo-auto-filter.d.ts.map +1 -0
- package/dist/lib/solo-auto-filter.js +311 -0
- package/dist/lib/solo-auto-filter.js.map +1 -0
- package/dist/lib/solo-auto-finalize.d.ts +6 -0
- package/dist/lib/solo-auto-finalize.d.ts.map +1 -0
- package/dist/lib/solo-auto-finalize.js +242 -0
- package/dist/lib/solo-auto-finalize.js.map +1 -0
- package/dist/lib/solo-auto-init-qa.d.ts +23 -0
- package/dist/lib/solo-auto-init-qa.d.ts.map +1 -0
- package/dist/lib/solo-auto-init-qa.js +183 -0
- package/dist/lib/solo-auto-init-qa.js.map +1 -0
- package/dist/lib/solo-auto-planning.d.ts +40 -0
- package/dist/lib/solo-auto-planning.d.ts.map +1 -0
- package/dist/lib/solo-auto-planning.js +171 -0
- package/dist/lib/solo-auto-planning.js.map +1 -0
- package/dist/lib/solo-auto-scout.d.ts +18 -0
- package/dist/lib/solo-auto-scout.d.ts.map +1 -0
- package/dist/lib/solo-auto-scout.js +269 -0
- package/dist/lib/solo-auto-scout.js.map +1 -0
- package/dist/lib/solo-auto-state.d.ts +20 -0
- package/dist/lib/solo-auto-state.d.ts.map +1 -0
- package/dist/lib/solo-auto-state.js +859 -0
- package/dist/lib/solo-auto-state.js.map +1 -0
- package/dist/lib/solo-auto-types.d.ts +197 -0
- package/dist/lib/solo-auto-types.d.ts.map +1 -0
- package/dist/lib/solo-auto-types.js +11 -0
- package/dist/lib/solo-auto-types.js.map +1 -0
- package/dist/lib/solo-auto-utils.d.ts +23 -0
- package/dist/lib/solo-auto-utils.d.ts.map +1 -0
- package/dist/lib/solo-auto-utils.js +53 -0
- package/dist/lib/solo-auto-utils.js.map +1 -0
- package/dist/lib/solo-auto.d.ts +25 -0
- package/dist/lib/solo-auto.d.ts.map +1 -0
- package/dist/lib/solo-auto.js +345 -0
- package/dist/lib/solo-auto.js.map +1 -0
- package/dist/lib/solo-ci.d.ts +84 -0
- package/dist/lib/solo-ci.d.ts.map +1 -0
- package/dist/lib/solo-ci.js +306 -0
- package/dist/lib/solo-ci.js.map +1 -0
- package/dist/lib/solo-config.d.ts +242 -0
- package/dist/lib/solo-config.d.ts.map +1 -0
- package/dist/lib/solo-config.js +354 -0
- package/dist/lib/solo-config.js.map +1 -0
- package/dist/lib/solo-cycle-formula.d.ts +36 -0
- package/dist/lib/solo-cycle-formula.d.ts.map +1 -0
- package/dist/lib/solo-cycle-formula.js +90 -0
- package/dist/lib/solo-cycle-formula.js.map +1 -0
- package/dist/lib/solo-git.d.ts +87 -0
- package/dist/lib/solo-git.d.ts.map +1 -0
- package/dist/lib/solo-git.js +365 -0
- package/dist/lib/solo-git.js.map +1 -0
- package/dist/lib/solo-hints.d.ts +32 -0
- package/dist/lib/solo-hints.d.ts.map +1 -0
- package/dist/lib/solo-hints.js +98 -0
- package/dist/lib/solo-hints.js.map +1 -0
- package/dist/lib/solo-prompt-builder.d.ts +12 -0
- package/dist/lib/solo-prompt-builder.d.ts.map +1 -0
- package/dist/lib/solo-prompt-builder.js +53 -0
- package/dist/lib/solo-prompt-builder.js.map +1 -0
- package/dist/lib/solo-qa-retry.d.ts +43 -0
- package/dist/lib/solo-qa-retry.d.ts.map +1 -0
- package/dist/lib/solo-qa-retry.js +125 -0
- package/dist/lib/solo-qa-retry.js.map +1 -0
- package/dist/lib/solo-remote.d.ts +14 -0
- package/dist/lib/solo-remote.d.ts.map +1 -0
- package/dist/lib/solo-remote.js +48 -0
- package/dist/lib/solo-remote.js.map +1 -0
- package/dist/lib/solo-session-summary.d.ts +52 -0
- package/dist/lib/solo-session-summary.d.ts.map +1 -0
- package/dist/lib/solo-session-summary.js +188 -0
- package/dist/lib/solo-session-summary.js.map +1 -0
- package/dist/lib/solo-stdin.d.ts +33 -0
- package/dist/lib/solo-stdin.d.ts.map +1 -0
- package/dist/lib/solo-stdin.js +299 -0
- package/dist/lib/solo-stdin.js.map +1 -0
- package/dist/lib/solo-ticket-qa.d.ts +19 -0
- package/dist/lib/solo-ticket-qa.d.ts.map +1 -0
- package/dist/lib/solo-ticket-qa.js +68 -0
- package/dist/lib/solo-ticket-qa.js.map +1 -0
- package/dist/lib/solo-ticket-types.d.ts +146 -0
- package/dist/lib/solo-ticket-types.d.ts.map +1 -0
- package/dist/lib/solo-ticket-types.js +26 -0
- package/dist/lib/solo-ticket-types.js.map +1 -0
- package/dist/lib/solo-ticket.d.ts +12 -0
- package/dist/lib/solo-ticket.d.ts.map +1 -0
- package/dist/lib/solo-ticket.js +192 -0
- package/dist/lib/solo-ticket.js.map +1 -0
- package/dist/lib/solo-utils.d.ts +130 -0
- package/dist/lib/solo-utils.d.ts.map +1 -0
- package/dist/lib/solo-utils.js +277 -0
- package/dist/lib/solo-utils.js.map +1 -0
- package/dist/lib/spindle/failure-patterns.d.ts +15 -0
- package/dist/lib/spindle/failure-patterns.d.ts.map +1 -0
- package/dist/lib/spindle/failure-patterns.js +22 -0
- package/dist/lib/spindle/failure-patterns.js.map +1 -0
- package/dist/lib/spindle/format.d.ts +9 -0
- package/dist/lib/spindle/format.d.ts.map +1 -0
- package/dist/lib/spindle/format.js +37 -0
- package/dist/lib/spindle/format.js.map +1 -0
- package/dist/lib/spindle/index.d.ts +33 -0
- package/dist/lib/spindle/index.d.ts.map +1 -0
- package/dist/lib/spindle/index.js +231 -0
- package/dist/lib/spindle/index.js.map +1 -0
- package/dist/lib/spindle/oscillation.d.ts +17 -0
- package/dist/lib/spindle/oscillation.d.ts.map +1 -0
- package/dist/lib/spindle/oscillation.js +96 -0
- package/dist/lib/spindle/oscillation.js.map +1 -0
- package/dist/lib/spindle/repetition.d.ts +16 -0
- package/dist/lib/spindle/repetition.d.ts.map +1 -0
- package/dist/lib/spindle/repetition.js +52 -0
- package/dist/lib/spindle/repetition.js.map +1 -0
- package/dist/lib/spindle/similarity.d.ts +14 -0
- package/dist/lib/spindle/similarity.d.ts.map +1 -0
- package/dist/lib/spindle/similarity.js +58 -0
- package/dist/lib/spindle/similarity.js.map +1 -0
- package/dist/lib/spindle/types.d.ts +97 -0
- package/dist/lib/spindle/types.d.ts.map +1 -0
- package/dist/lib/spindle/types.js +46 -0
- package/dist/lib/spindle/types.js.map +1 -0
- package/dist/lib/spinner.d.ts +41 -0
- package/dist/lib/spinner.d.ts.map +1 -0
- package/dist/lib/spinner.js +186 -0
- package/dist/lib/spinner.js.map +1 -0
- package/dist/lib/taste-profile.d.ts +26 -0
- package/dist/lib/taste-profile.d.ts.map +1 -0
- package/dist/lib/taste-profile.js +121 -0
- package/dist/lib/taste-profile.js.map +1 -0
- package/dist/lib/ticket-steps/index.d.ts +14 -0
- package/dist/lib/ticket-steps/index.d.ts.map +1 -0
- package/dist/lib/ticket-steps/index.js +13 -0
- package/dist/lib/ticket-steps/index.js.map +1 -0
- package/dist/lib/ticket-steps/step-agent.d.ts +6 -0
- package/dist/lib/ticket-steps/step-agent.d.ts.map +1 -0
- package/dist/lib/ticket-steps/step-agent.js +122 -0
- package/dist/lib/ticket-steps/step-agent.js.map +1 -0
- package/dist/lib/ticket-steps/step-cleanup.d.ts +6 -0
- package/dist/lib/ticket-steps/step-cleanup.d.ts.map +1 -0
- package/dist/lib/ticket-steps/step-cleanup.js +11 -0
- package/dist/lib/ticket-steps/step-cleanup.js.map +1 -0
- package/dist/lib/ticket-steps/step-commit.d.ts +6 -0
- package/dist/lib/ticket-steps/step-commit.d.ts.map +1 -0
- package/dist/lib/ticket-steps/step-commit.js +31 -0
- package/dist/lib/ticket-steps/step-commit.js.map +1 -0
- package/dist/lib/ticket-steps/step-pr.d.ts +6 -0
- package/dist/lib/ticket-steps/step-pr.d.ts.map +1 -0
- package/dist/lib/ticket-steps/step-pr.js +38 -0
- package/dist/lib/ticket-steps/step-pr.js.map +1 -0
- package/dist/lib/ticket-steps/step-push.d.ts +6 -0
- package/dist/lib/ticket-steps/step-push.d.ts.map +1 -0
- package/dist/lib/ticket-steps/step-push.js +35 -0
- package/dist/lib/ticket-steps/step-push.js.map +1 -0
- package/dist/lib/ticket-steps/step-qa.d.ts +6 -0
- package/dist/lib/ticket-steps/step-qa.d.ts.map +1 -0
- package/dist/lib/ticket-steps/step-qa.js +300 -0
- package/dist/lib/ticket-steps/step-qa.js.map +1 -0
- package/dist/lib/ticket-steps/step-scope.d.ts +6 -0
- package/dist/lib/ticket-steps/step-scope.d.ts.map +1 -0
- package/dist/lib/ticket-steps/step-scope.js +121 -0
- package/dist/lib/ticket-steps/step-scope.js.map +1 -0
- package/dist/lib/ticket-steps/step-spindle.d.ts +9 -0
- package/dist/lib/ticket-steps/step-spindle.d.ts.map +1 -0
- package/dist/lib/ticket-steps/step-spindle.js +160 -0
- package/dist/lib/ticket-steps/step-spindle.js.map +1 -0
- package/dist/lib/ticket-steps/step-worktree.d.ts +6 -0
- package/dist/lib/ticket-steps/step-worktree.d.ts.map +1 -0
- package/dist/lib/ticket-steps/step-worktree.js +157 -0
- package/dist/lib/ticket-steps/step-worktree.js.map +1 -0
- package/dist/lib/ticket-steps/types.d.ts +70 -0
- package/dist/lib/ticket-steps/types.d.ts.map +1 -0
- package/dist/lib/ticket-steps/types.js +8 -0
- package/dist/lib/ticket-steps/types.js.map +1 -0
- package/dist/lib/tool-command-map.d.ts +11 -0
- package/dist/lib/tool-command-map.d.ts.map +1 -0
- package/dist/lib/tool-command-map.js +24 -0
- package/dist/lib/tool-command-map.js.map +1 -0
- package/dist/lib/trajectory-generate.d.ts +24 -0
- package/dist/lib/trajectory-generate.d.ts.map +1 -0
- package/dist/lib/trajectory-generate.js +163 -0
- package/dist/lib/trajectory-generate.js.map +1 -0
- package/dist/lib/trajectory.d.ts +20 -0
- package/dist/lib/trajectory.d.ts.map +1 -0
- package/dist/lib/trajectory.js +104 -0
- package/dist/lib/trajectory.js.map +1 -0
- package/dist/lib/trigger-config.d.ts +12 -0
- package/dist/lib/trigger-config.d.ts.map +1 -0
- package/dist/lib/trigger-config.js +37 -0
- package/dist/lib/trigger-config.js.map +1 -0
- package/dist/lib/update-check.d.ts +28 -0
- package/dist/lib/update-check.d.ts.map +1 -0
- package/dist/lib/update-check.js +178 -0
- package/dist/lib/update-check.js.map +1 -0
- package/dist/lib/wave-scheduling.d.ts +18 -0
- package/dist/lib/wave-scheduling.d.ts.map +1 -0
- package/dist/lib/wave-scheduling.js +29 -0
- package/dist/lib/wave-scheduling.js.map +1 -0
- package/dist/tui/app.d.ts +17 -0
- package/dist/tui/app.d.ts.map +1 -0
- package/dist/tui/app.js +139 -0
- package/dist/tui/app.js.map +1 -0
- package/dist/tui/index.d.ts +10 -0
- package/dist/tui/index.d.ts.map +1 -0
- package/dist/tui/index.js +9 -0
- package/dist/tui/index.js.map +1 -0
- package/dist/tui/poller.d.ts +42 -0
- package/dist/tui/poller.d.ts.map +1 -0
- package/dist/tui/poller.js +62 -0
- package/dist/tui/poller.js.map +1 -0
- package/dist/tui/screens/auto.d.ts +85 -0
- package/dist/tui/screens/auto.d.ts.map +1 -0
- package/dist/tui/screens/auto.js +440 -0
- package/dist/tui/screens/auto.js.map +1 -0
- package/dist/tui/screens/overview.d.ts +9 -0
- package/dist/tui/screens/overview.d.ts.map +1 -0
- package/dist/tui/screens/overview.js +189 -0
- package/dist/tui/screens/overview.js.map +1 -0
- package/dist/tui/state.d.ts +93 -0
- package/dist/tui/state.d.ts.map +1 -0
- package/dist/tui/state.js +169 -0
- package/dist/tui/state.js.map +1 -0
- package/dist/tui/ticket-output-buffer.d.ts +23 -0
- package/dist/tui/ticket-output-buffer.d.ts.map +1 -0
- package/dist/tui/ticket-output-buffer.js +60 -0
- package/dist/tui/ticket-output-buffer.js.map +1 -0
- package/dist/tui/types.d.ts +18 -0
- package/dist/tui/types.d.ts.map +1 -0
- package/dist/tui/types.js +5 -0
- package/dist/tui/types.js.map +1 -0
- package/package.json +70 -0
|
@@ -0,0 +1,767 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retention & Cleanup
|
|
3
|
+
*
|
|
4
|
+
* Prune logic for all unbounded state accumulation points.
|
|
5
|
+
* Called automatically on session start and via `promptwheel prune`.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'node:fs';
|
|
8
|
+
import * as os from 'node:os';
|
|
9
|
+
import * as path from 'node:path';
|
|
10
|
+
import { spawnSync } from 'node:child_process';
|
|
11
|
+
import { DEFAULT_RETENTION_CONFIG, getPromptwheelDir, } from './solo-config.js';
|
|
12
|
+
import { readRunState, writeRunState } from './run-state.js';
|
|
13
|
+
function emptyReport() {
|
|
14
|
+
return {
|
|
15
|
+
runFoldersRemoved: 0,
|
|
16
|
+
historyLinesRemoved: 0,
|
|
17
|
+
artifactsRemoved: 0,
|
|
18
|
+
spoolArchivesRemoved: 0,
|
|
19
|
+
deferredProposalsRemoved: 0,
|
|
20
|
+
completedTicketsRemoved: 0,
|
|
21
|
+
mergedBranchesRemoved: 0,
|
|
22
|
+
staleBranchesRemoved: 0,
|
|
23
|
+
staleWorktreesRemoved: 0,
|
|
24
|
+
logsRotated: 0,
|
|
25
|
+
metricsLinesRemoved: 0,
|
|
26
|
+
artifactsByAgeRemoved: 0,
|
|
27
|
+
totalPruned: 0,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
// =============================================================================
|
|
31
|
+
// Config helper
|
|
32
|
+
// =============================================================================
|
|
33
|
+
export function getRetentionConfig(config) {
|
|
34
|
+
return {
|
|
35
|
+
...DEFAULT_RETENTION_CONFIG,
|
|
36
|
+
...(config?.retention ?? {}),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
// =============================================================================
|
|
40
|
+
// Individual prune functions
|
|
41
|
+
// =============================================================================
|
|
42
|
+
/**
|
|
43
|
+
* Sort .promptwheel/runs/ by mtime, delete oldest beyond cap.
|
|
44
|
+
*/
|
|
45
|
+
export function pruneRunFolders(repoRoot, maxRuns, dryRun = false) {
|
|
46
|
+
const runsDir = path.join(getPromptwheelDir(repoRoot), 'runs');
|
|
47
|
+
if (!fs.existsSync(runsDir))
|
|
48
|
+
return 0;
|
|
49
|
+
const entries = fs.readdirSync(runsDir, { withFileTypes: true })
|
|
50
|
+
.filter(e => e.isDirectory())
|
|
51
|
+
.map(e => ({
|
|
52
|
+
name: e.name,
|
|
53
|
+
path: path.join(runsDir, e.name),
|
|
54
|
+
mtime: fs.statSync(path.join(runsDir, e.name)).mtimeMs,
|
|
55
|
+
}))
|
|
56
|
+
.sort((a, b) => b.mtime - a.mtime); // newest first
|
|
57
|
+
if (entries.length <= maxRuns)
|
|
58
|
+
return 0;
|
|
59
|
+
const toRemove = entries.slice(maxRuns);
|
|
60
|
+
if (!dryRun) {
|
|
61
|
+
for (const entry of toRemove) {
|
|
62
|
+
fs.rmSync(entry.path, { recursive: true, force: true });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return toRemove.length;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Keep last N lines in history.ndjson, rewrite file.
|
|
69
|
+
*/
|
|
70
|
+
export function pruneHistory(repoRoot, maxEntries, dryRun = false) {
|
|
71
|
+
const historyPath = path.join(getPromptwheelDir(repoRoot), 'history.ndjson');
|
|
72
|
+
if (!fs.existsSync(historyPath))
|
|
73
|
+
return 0;
|
|
74
|
+
const content = fs.readFileSync(historyPath, 'utf-8');
|
|
75
|
+
const lines = content.split('\n').filter(l => l.trim().length > 0);
|
|
76
|
+
if (lines.length <= maxEntries)
|
|
77
|
+
return 0;
|
|
78
|
+
const removed = lines.length - maxEntries;
|
|
79
|
+
if (!dryRun) {
|
|
80
|
+
const kept = lines.slice(-maxEntries);
|
|
81
|
+
fs.writeFileSync(historyPath, kept.join('\n') + '\n');
|
|
82
|
+
}
|
|
83
|
+
return removed;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Within each run folder, keep newest N artifact files.
|
|
87
|
+
*/
|
|
88
|
+
export function pruneArtifacts(repoRoot, maxPerRun, dryRun = false) {
|
|
89
|
+
const runsDir = path.join(getPromptwheelDir(repoRoot), 'runs');
|
|
90
|
+
if (!fs.existsSync(runsDir))
|
|
91
|
+
return 0;
|
|
92
|
+
let totalRemoved = 0;
|
|
93
|
+
const runDirs = fs.readdirSync(runsDir, { withFileTypes: true })
|
|
94
|
+
.filter(e => e.isDirectory());
|
|
95
|
+
for (const runDir of runDirs) {
|
|
96
|
+
const runPath = path.join(runsDir, runDir.name);
|
|
97
|
+
const files = fs.readdirSync(runPath, { withFileTypes: true })
|
|
98
|
+
.filter(e => e.isFile())
|
|
99
|
+
.map(e => ({
|
|
100
|
+
name: e.name,
|
|
101
|
+
path: path.join(runPath, e.name),
|
|
102
|
+
mtime: fs.statSync(path.join(runPath, e.name)).mtimeMs,
|
|
103
|
+
}))
|
|
104
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
105
|
+
if (files.length <= maxPerRun)
|
|
106
|
+
continue;
|
|
107
|
+
const toRemove = files.slice(maxPerRun);
|
|
108
|
+
if (!dryRun) {
|
|
109
|
+
for (const file of toRemove) {
|
|
110
|
+
fs.unlinkSync(file.path);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
totalRemoved += toRemove.length;
|
|
114
|
+
}
|
|
115
|
+
return totalRemoved;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Delete oldest *.archived.ndjson in spool/ beyond cap.
|
|
119
|
+
*/
|
|
120
|
+
export function pruneSpoolArchives(repoRoot, maxArchives, dryRun = false) {
|
|
121
|
+
const spoolDir = path.join(getPromptwheelDir(repoRoot), 'spool');
|
|
122
|
+
if (!fs.existsSync(spoolDir))
|
|
123
|
+
return 0;
|
|
124
|
+
const archives = fs.readdirSync(spoolDir)
|
|
125
|
+
.filter(f => f.endsWith('.archived.ndjson'))
|
|
126
|
+
.map(f => ({
|
|
127
|
+
name: f,
|
|
128
|
+
path: path.join(spoolDir, f),
|
|
129
|
+
mtime: fs.statSync(path.join(spoolDir, f)).mtimeMs,
|
|
130
|
+
}))
|
|
131
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
132
|
+
if (archives.length <= maxArchives)
|
|
133
|
+
return 0;
|
|
134
|
+
const toRemove = archives.slice(maxArchives);
|
|
135
|
+
if (!dryRun) {
|
|
136
|
+
for (const archive of toRemove) {
|
|
137
|
+
fs.unlinkSync(archive.path);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return toRemove.length;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Trim oldest deferred proposals from run-state.json beyond cap.
|
|
144
|
+
*/
|
|
145
|
+
export function pruneDeferredProposals(repoRoot, maxDeferred, dryRun = false) {
|
|
146
|
+
const state = readRunState(repoRoot);
|
|
147
|
+
if (state.deferredProposals.length <= maxDeferred)
|
|
148
|
+
return 0;
|
|
149
|
+
const removed = state.deferredProposals.length - maxDeferred;
|
|
150
|
+
if (!dryRun) {
|
|
151
|
+
// Keep the newest (last added)
|
|
152
|
+
state.deferredProposals = state.deferredProposals.slice(-maxDeferred);
|
|
153
|
+
writeRunState(repoRoot, state);
|
|
154
|
+
}
|
|
155
|
+
return removed;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Hard-delete oldest completed tickets beyond cap.
|
|
159
|
+
* Uses the DatabaseAdapter interface (Postgres $1 placeholders).
|
|
160
|
+
*/
|
|
161
|
+
export async function pruneCompletedTickets(adapter, maxCompleted, dryRun = false) {
|
|
162
|
+
if (!adapter)
|
|
163
|
+
return 0;
|
|
164
|
+
try {
|
|
165
|
+
const countResult = await adapter.query(`SELECT COUNT(*) as cnt FROM tickets WHERE status = 'done'`);
|
|
166
|
+
const count = countResult.rows[0]?.cnt ?? 0;
|
|
167
|
+
if (count <= maxCompleted)
|
|
168
|
+
return 0;
|
|
169
|
+
const toRemove = count - maxCompleted;
|
|
170
|
+
if (!dryRun) {
|
|
171
|
+
// Subquery to find oldest N completed tickets, then delete them.
|
|
172
|
+
// Works on both SQLite and Postgres.
|
|
173
|
+
await adapter.query(`DELETE FROM tickets WHERE id IN (
|
|
174
|
+
SELECT id FROM tickets WHERE status = 'done'
|
|
175
|
+
ORDER BY updated_at ASC LIMIT $1
|
|
176
|
+
)`, [toRemove]);
|
|
177
|
+
}
|
|
178
|
+
return toRemove;
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
// Table might not exist or schema differs — non-fatal
|
|
182
|
+
return 0;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Delete local promptwheel/* branches that are fully merged, keeping the
|
|
187
|
+
* newest N merged branches. Only touches local branches — never deletes
|
|
188
|
+
* remote branches, and never touches unmerged branches.
|
|
189
|
+
*
|
|
190
|
+
* NOT called during auto-prune-on-start — only via `promptwheel prune`.
|
|
191
|
+
*/
|
|
192
|
+
export function pruneMergedBranches(repoRoot, maxMergedBranches, dryRun = false) {
|
|
193
|
+
try {
|
|
194
|
+
// Get the list of promptwheel/* branches that are fully merged into HEAD
|
|
195
|
+
const mergedResult = spawnSync('git', ['branch', '--merged', 'HEAD', '--list', 'promptwheel/*', '--sort=-committerdate', '--format=%(refname:short)'], { cwd: repoRoot, encoding: 'utf-8' });
|
|
196
|
+
if (mergedResult.status !== 0 || !mergedResult.stdout)
|
|
197
|
+
return 0;
|
|
198
|
+
// Check current branch so we never delete it
|
|
199
|
+
const headResult = spawnSync('git', ['branch', '--show-current'], {
|
|
200
|
+
cwd: repoRoot,
|
|
201
|
+
encoding: 'utf-8',
|
|
202
|
+
});
|
|
203
|
+
const currentBranch = headResult.stdout?.trim() ?? '';
|
|
204
|
+
const mergedBranches = mergedResult.stdout.trim().split('\n')
|
|
205
|
+
.filter(b => b && b !== currentBranch);
|
|
206
|
+
if (mergedBranches.length <= maxMergedBranches)
|
|
207
|
+
return 0;
|
|
208
|
+
// Keep the newest N, delete the rest
|
|
209
|
+
const toDelete = mergedBranches.slice(maxMergedBranches);
|
|
210
|
+
let removed = 0;
|
|
211
|
+
if (!dryRun) {
|
|
212
|
+
for (const branch of toDelete) {
|
|
213
|
+
const delResult = spawnSync('git', ['branch', '-d', branch], {
|
|
214
|
+
cwd: repoRoot,
|
|
215
|
+
encoding: 'utf-8',
|
|
216
|
+
});
|
|
217
|
+
if (delResult.status === 0)
|
|
218
|
+
removed++;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
removed = toDelete.length;
|
|
223
|
+
}
|
|
224
|
+
return removed;
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
return 0;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// =============================================================================
|
|
231
|
+
// Stale unmerged branch cleanup
|
|
232
|
+
// =============================================================================
|
|
233
|
+
/**
|
|
234
|
+
* Delete local promptwheel/tkt_* and promptwheel/milestone-* branches that are
|
|
235
|
+
* NOT merged and whose last commit is older than `maxDays` days. Never touches
|
|
236
|
+
* promptwheel-direct or user branches. Never deletes the current branch.
|
|
237
|
+
*/
|
|
238
|
+
export function pruneStaleBranches(repoRoot, maxDays, dryRun = false) {
|
|
239
|
+
if (maxDays <= 0)
|
|
240
|
+
return 0;
|
|
241
|
+
try {
|
|
242
|
+
// Current branch — never delete
|
|
243
|
+
const headResult = spawnSync('git', ['branch', '--show-current'], {
|
|
244
|
+
cwd: repoRoot,
|
|
245
|
+
encoding: 'utf-8',
|
|
246
|
+
});
|
|
247
|
+
const currentBranch = headResult.stdout?.trim() ?? '';
|
|
248
|
+
const cutoff = Date.now() - (maxDays * 24 * 60 * 60 * 1000);
|
|
249
|
+
let removed = 0;
|
|
250
|
+
// Prune both tkt_* and milestone-* branches
|
|
251
|
+
const refPatterns = ['refs/heads/promptwheel/tkt_*', 'refs/heads/promptwheel/milestone-*'];
|
|
252
|
+
for (const pattern of refPatterns) {
|
|
253
|
+
const listResult = spawnSync('git', ['for-each-ref', '--format=%(refname:short) %(committerdate:unix)', pattern], { cwd: repoRoot, encoding: 'utf-8' });
|
|
254
|
+
if (listResult.status !== 0 || !listResult.stdout?.trim())
|
|
255
|
+
continue;
|
|
256
|
+
for (const line of listResult.stdout.trim().split('\n')) {
|
|
257
|
+
const parts = line.trim().split(' ');
|
|
258
|
+
if (parts.length < 2)
|
|
259
|
+
continue;
|
|
260
|
+
const branch = parts[0];
|
|
261
|
+
const epochSec = parseInt(parts[1], 10);
|
|
262
|
+
if (!branch || isNaN(epochSec))
|
|
263
|
+
continue;
|
|
264
|
+
if (branch === currentBranch)
|
|
265
|
+
continue;
|
|
266
|
+
const commitMs = epochSec * 1000;
|
|
267
|
+
if (commitMs >= cutoff)
|
|
268
|
+
continue; // too recent
|
|
269
|
+
if (!dryRun) {
|
|
270
|
+
const delResult = spawnSync('git', ['branch', '-D', branch], {
|
|
271
|
+
cwd: repoRoot,
|
|
272
|
+
encoding: 'utf-8',
|
|
273
|
+
});
|
|
274
|
+
if (delResult.status === 0)
|
|
275
|
+
removed++;
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
removed++;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return removed;
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
return 0;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// =============================================================================
|
|
289
|
+
// Stale worktree cleanup
|
|
290
|
+
// =============================================================================
|
|
291
|
+
/**
|
|
292
|
+
* Remove orphaned worktrees that have no associated running process.
|
|
293
|
+
* Uses `git worktree list` to find stale entries and `git worktree remove --force`.
|
|
294
|
+
*/
|
|
295
|
+
export function pruneStaleWorktrees(repoRoot, dryRun = false) {
|
|
296
|
+
try {
|
|
297
|
+
const worktreesDir = path.join(repoRoot, '.promptwheel', 'worktrees');
|
|
298
|
+
if (!fs.existsSync(worktreesDir))
|
|
299
|
+
return 0;
|
|
300
|
+
const entries = fs.readdirSync(worktreesDir).filter(e => {
|
|
301
|
+
const fullPath = path.join(worktreesDir, e);
|
|
302
|
+
return fs.statSync(fullPath).isDirectory();
|
|
303
|
+
});
|
|
304
|
+
if (entries.length === 0)
|
|
305
|
+
return 0;
|
|
306
|
+
// Get list of worktrees git knows about
|
|
307
|
+
const listResult = spawnSync('git', ['worktree', 'list', '--porcelain'], {
|
|
308
|
+
cwd: repoRoot,
|
|
309
|
+
encoding: 'utf-8',
|
|
310
|
+
});
|
|
311
|
+
const gitWorktrees = new Set((listResult.stdout ?? '').split('\n')
|
|
312
|
+
.filter(l => l.startsWith('worktree '))
|
|
313
|
+
.map(l => l.replace('worktree ', '').trim()));
|
|
314
|
+
let removed = 0;
|
|
315
|
+
for (const entry of entries) {
|
|
316
|
+
// Skip _milestone — that's managed by solo-git
|
|
317
|
+
if (entry === '_milestone')
|
|
318
|
+
continue;
|
|
319
|
+
const worktreePath = path.join(worktreesDir, entry);
|
|
320
|
+
// Check if git still tracks this worktree with a lock file
|
|
321
|
+
// If git doesn't know about it, it's a leftover directory
|
|
322
|
+
const isTracked = gitWorktrees.has(path.resolve(worktreePath));
|
|
323
|
+
if (!dryRun) {
|
|
324
|
+
try {
|
|
325
|
+
if (isTracked) {
|
|
326
|
+
spawnSync('git', ['worktree', 'remove', '--force', worktreePath], {
|
|
327
|
+
cwd: repoRoot,
|
|
328
|
+
encoding: 'utf-8',
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
// Not a git worktree anymore — just remove the directory
|
|
333
|
+
fs.rmSync(worktreePath, { recursive: true, force: true });
|
|
334
|
+
}
|
|
335
|
+
// Also delete the associated branch
|
|
336
|
+
if (entry.startsWith('tkt_')) {
|
|
337
|
+
spawnSync('git', ['branch', '-D', `promptwheel/${entry}`], {
|
|
338
|
+
cwd: repoRoot,
|
|
339
|
+
encoding: 'utf-8',
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
removed++;
|
|
343
|
+
}
|
|
344
|
+
catch {
|
|
345
|
+
// Individual removal failure is non-fatal
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
removed++;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return removed;
|
|
353
|
+
}
|
|
354
|
+
catch {
|
|
355
|
+
return 0;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
// =============================================================================
|
|
359
|
+
// Stale codex session cleanup
|
|
360
|
+
// =============================================================================
|
|
361
|
+
/**
|
|
362
|
+
* Remove codex session rollout files from incompatible versions and old sessions.
|
|
363
|
+
*
|
|
364
|
+
* Codex stores per-thread rollout files in ~/.codex/sessions/YYYY/MM/DD/.
|
|
365
|
+
* After a codex upgrade, old-version rollout files cause "state db missing
|
|
366
|
+
* rollout path" errors on every invocation because the new version can't
|
|
367
|
+
* index the old format. This prunes:
|
|
368
|
+
* 1. Rollout files from a different major.minor codex version
|
|
369
|
+
* 2. Rollout files older than `maxDays` days
|
|
370
|
+
* 3. Abandoned rollout files — same version, recent, but the owning
|
|
371
|
+
* process stopped writing (mtime > 5 min stale). These are left
|
|
372
|
+
* behind when a codex process is killed mid-execution.
|
|
373
|
+
*/
|
|
374
|
+
export function pruneStaleCodexSessions(maxDays, dryRun = false) {
|
|
375
|
+
if (maxDays <= 0)
|
|
376
|
+
return 0;
|
|
377
|
+
try {
|
|
378
|
+
const codexDir = path.join(os.homedir(), '.codex', 'sessions');
|
|
379
|
+
if (!fs.existsSync(codexDir))
|
|
380
|
+
return 0;
|
|
381
|
+
// Detect current codex version
|
|
382
|
+
const verResult = spawnSync('codex', ['--version'], { encoding: 'utf-8', timeout: 5000 });
|
|
383
|
+
const verMatch = verResult.stdout?.match(/(\d+\.\d+)/);
|
|
384
|
+
const currentMajorMinor = verMatch?.[1] ?? null;
|
|
385
|
+
// Only prune abandoned threads if no codex process is running
|
|
386
|
+
const codexRunning = isCodexRunning();
|
|
387
|
+
const cutoff = Date.now() - (maxDays * 24 * 60 * 60 * 1000);
|
|
388
|
+
const staleCutoff = Date.now() - (5 * 60 * 1000); // 5 minutes
|
|
389
|
+
let removed = 0;
|
|
390
|
+
// Walk YYYY/MM/DD directory structure
|
|
391
|
+
for (const year of safeReaddir(codexDir)) {
|
|
392
|
+
const yearPath = path.join(codexDir, year);
|
|
393
|
+
if (!safeStat(yearPath)?.isDirectory())
|
|
394
|
+
continue;
|
|
395
|
+
for (const month of safeReaddir(yearPath)) {
|
|
396
|
+
const monthPath = path.join(yearPath, month);
|
|
397
|
+
if (!safeStat(monthPath)?.isDirectory())
|
|
398
|
+
continue;
|
|
399
|
+
for (const day of safeReaddir(monthPath)) {
|
|
400
|
+
const dayPath = path.join(monthPath, day);
|
|
401
|
+
if (!safeStat(dayPath)?.isDirectory())
|
|
402
|
+
continue;
|
|
403
|
+
for (const file of safeReaddir(dayPath)) {
|
|
404
|
+
const filePath = path.join(dayPath, file);
|
|
405
|
+
try {
|
|
406
|
+
const stat = fs.statSync(filePath);
|
|
407
|
+
let shouldRemove = stat.mtimeMs < cutoff;
|
|
408
|
+
// Check version incompatibility for rollout files
|
|
409
|
+
if (!shouldRemove && currentMajorMinor && file.endsWith('.jsonl')) {
|
|
410
|
+
const firstLine = readFirstLine(filePath);
|
|
411
|
+
if (firstLine) {
|
|
412
|
+
const verInFile = firstLine.match(/"cli_version":"(\d+\.\d+)/);
|
|
413
|
+
if (verInFile && verInFile[1] !== currentMajorMinor) {
|
|
414
|
+
shouldRemove = true;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
// Check for abandoned rollout files — stale mtime, no codex running.
|
|
419
|
+
// These are left behind when a codex process is killed mid-execution.
|
|
420
|
+
// Only prune if no codex process is currently running (safe window).
|
|
421
|
+
if (!shouldRemove && !codexRunning && file.endsWith('.jsonl') && stat.mtimeMs < staleCutoff) {
|
|
422
|
+
shouldRemove = true;
|
|
423
|
+
}
|
|
424
|
+
if (shouldRemove) {
|
|
425
|
+
if (!dryRun)
|
|
426
|
+
fs.unlinkSync(filePath);
|
|
427
|
+
removed++;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
catch { /* skip */ }
|
|
431
|
+
}
|
|
432
|
+
// Remove empty day directories
|
|
433
|
+
try {
|
|
434
|
+
if (!dryRun && safeReaddir(dayPath).length === 0)
|
|
435
|
+
fs.rmdirSync(dayPath);
|
|
436
|
+
}
|
|
437
|
+
catch { /* skip */ }
|
|
438
|
+
}
|
|
439
|
+
try {
|
|
440
|
+
if (!dryRun && safeReaddir(monthPath).length === 0)
|
|
441
|
+
fs.rmdirSync(monthPath);
|
|
442
|
+
}
|
|
443
|
+
catch { /* skip */ }
|
|
444
|
+
}
|
|
445
|
+
try {
|
|
446
|
+
if (!dryRun && safeReaddir(yearPath).length === 0)
|
|
447
|
+
fs.rmdirSync(yearPath);
|
|
448
|
+
}
|
|
449
|
+
catch { /* skip */ }
|
|
450
|
+
}
|
|
451
|
+
return removed;
|
|
452
|
+
}
|
|
453
|
+
catch {
|
|
454
|
+
return 0;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Check if any codex process is currently running.
|
|
459
|
+
* Used to gate abandoned-thread pruning — if codex is running,
|
|
460
|
+
* skip aggressive pruning to avoid deleting active rollout files.
|
|
461
|
+
*/
|
|
462
|
+
function isCodexRunning() {
|
|
463
|
+
try {
|
|
464
|
+
const result = spawnSync('pgrep', ['-f', 'codex'], { encoding: 'utf-8', timeout: 5000 });
|
|
465
|
+
return result.status === 0 && !!result.stdout?.trim();
|
|
466
|
+
}
|
|
467
|
+
catch {
|
|
468
|
+
return true; // assume running if we can't check — safer to skip pruning
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
function safeReaddir(dir) {
|
|
472
|
+
try {
|
|
473
|
+
return fs.readdirSync(dir);
|
|
474
|
+
}
|
|
475
|
+
catch {
|
|
476
|
+
return [];
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
function safeStat(p) {
|
|
480
|
+
try {
|
|
481
|
+
return fs.statSync(p);
|
|
482
|
+
}
|
|
483
|
+
catch {
|
|
484
|
+
return null;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
function readFirstLine(filePath) {
|
|
488
|
+
try {
|
|
489
|
+
const fd = fs.openSync(filePath, 'r');
|
|
490
|
+
const buf = Buffer.alloc(512);
|
|
491
|
+
const bytesRead = fs.readSync(fd, buf, 0, 512, 0);
|
|
492
|
+
fs.closeSync(fd);
|
|
493
|
+
const text = buf.toString('utf-8', 0, bytesRead);
|
|
494
|
+
const nl = text.indexOf('\n');
|
|
495
|
+
return nl >= 0 ? text.slice(0, nl) : text;
|
|
496
|
+
}
|
|
497
|
+
catch {
|
|
498
|
+
return null;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
// =============================================================================
|
|
502
|
+
// Log rotation
|
|
503
|
+
// =============================================================================
|
|
504
|
+
/**
|
|
505
|
+
* Rotate tui.log when it exceeds maxBytes.
|
|
506
|
+
* Renames current to tui.log.1, starts fresh.
|
|
507
|
+
* Only keeps 1 backup — previous .1 is overwritten.
|
|
508
|
+
*/
|
|
509
|
+
export function rotateLogs(repoRoot, maxBytes, dryRun = false) {
|
|
510
|
+
if (maxBytes <= 0)
|
|
511
|
+
return 0;
|
|
512
|
+
const bsDir = getPromptwheelDir(repoRoot);
|
|
513
|
+
const logPath = path.join(bsDir, 'tui.log');
|
|
514
|
+
if (!fs.existsSync(logPath))
|
|
515
|
+
return 0;
|
|
516
|
+
try {
|
|
517
|
+
const stat = fs.statSync(logPath);
|
|
518
|
+
if (stat.size <= maxBytes)
|
|
519
|
+
return 0;
|
|
520
|
+
if (!dryRun) {
|
|
521
|
+
const backupPath = path.join(bsDir, 'tui.log.1');
|
|
522
|
+
// Overwrite previous backup
|
|
523
|
+
try {
|
|
524
|
+
fs.unlinkSync(backupPath);
|
|
525
|
+
}
|
|
526
|
+
catch { /* may not exist */ }
|
|
527
|
+
fs.renameSync(logPath, backupPath);
|
|
528
|
+
}
|
|
529
|
+
return 1;
|
|
530
|
+
}
|
|
531
|
+
catch {
|
|
532
|
+
return 0;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Keep last N lines in metrics.ndjson, rewrite file.
|
|
537
|
+
*/
|
|
538
|
+
export function pruneMetrics(repoRoot, maxEntries, dryRun = false) {
|
|
539
|
+
if (maxEntries <= 0)
|
|
540
|
+
return 0;
|
|
541
|
+
const metricsPath = path.join(getPromptwheelDir(repoRoot), 'metrics.ndjson');
|
|
542
|
+
if (!fs.existsSync(metricsPath))
|
|
543
|
+
return 0;
|
|
544
|
+
const content = fs.readFileSync(metricsPath, 'utf-8');
|
|
545
|
+
const lines = content.split('\n').filter(l => l.trim().length > 0);
|
|
546
|
+
if (lines.length <= maxEntries)
|
|
547
|
+
return 0;
|
|
548
|
+
const removed = lines.length - maxEntries;
|
|
549
|
+
if (!dryRun) {
|
|
550
|
+
const kept = lines.slice(-maxEntries);
|
|
551
|
+
fs.writeFileSync(metricsPath, kept.join('\n') + '\n');
|
|
552
|
+
}
|
|
553
|
+
return removed;
|
|
554
|
+
}
|
|
555
|
+
// =============================================================================
|
|
556
|
+
// Time-based artifact expiry
|
|
557
|
+
// =============================================================================
|
|
558
|
+
/**
|
|
559
|
+
* Delete artifact files older than maxDays from .promptwheel/artifacts/.
|
|
560
|
+
* Walks all subdirectories (executions/, diffs/, runs/, violations/).
|
|
561
|
+
* Removes empty subdirectories afterward.
|
|
562
|
+
*/
|
|
563
|
+
export function pruneArtifactsByAge(repoRoot, maxDays, dryRun = false) {
|
|
564
|
+
if (maxDays <= 0)
|
|
565
|
+
return 0;
|
|
566
|
+
const artifactsDir = path.join(getPromptwheelDir(repoRoot), 'artifacts');
|
|
567
|
+
if (!fs.existsSync(artifactsDir))
|
|
568
|
+
return 0;
|
|
569
|
+
const cutoff = Date.now() - (maxDays * 24 * 60 * 60 * 1000);
|
|
570
|
+
let removed = 0;
|
|
571
|
+
for (const subdir of safeReaddir(artifactsDir)) {
|
|
572
|
+
const subdirPath = path.join(artifactsDir, subdir);
|
|
573
|
+
if (!safeStat(subdirPath)?.isDirectory())
|
|
574
|
+
continue;
|
|
575
|
+
for (const file of safeReaddir(subdirPath)) {
|
|
576
|
+
const filePath = path.join(subdirPath, file);
|
|
577
|
+
try {
|
|
578
|
+
const stat = fs.statSync(filePath);
|
|
579
|
+
if (!stat.isFile())
|
|
580
|
+
continue;
|
|
581
|
+
if (stat.mtimeMs < cutoff) {
|
|
582
|
+
if (!dryRun)
|
|
583
|
+
fs.unlinkSync(filePath);
|
|
584
|
+
removed++;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
catch { /* skip */ }
|
|
588
|
+
}
|
|
589
|
+
// Remove empty subdirectory
|
|
590
|
+
try {
|
|
591
|
+
if (!dryRun && safeReaddir(subdirPath).length === 0) {
|
|
592
|
+
fs.rmdirSync(subdirPath);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
catch { /* skip */ }
|
|
596
|
+
}
|
|
597
|
+
return removed;
|
|
598
|
+
}
|
|
599
|
+
// =============================================================================
|
|
600
|
+
// Git worktree prune
|
|
601
|
+
// =============================================================================
|
|
602
|
+
/**
|
|
603
|
+
* Run `git worktree prune` to clean dangling worktree refs that manual
|
|
604
|
+
* cleanup misses (e.g. worktree directories deleted without `git worktree remove`).
|
|
605
|
+
*/
|
|
606
|
+
export function gitWorktreePrune(repoRoot) {
|
|
607
|
+
try {
|
|
608
|
+
spawnSync('git', ['worktree', 'prune'], {
|
|
609
|
+
cwd: repoRoot,
|
|
610
|
+
encoding: 'utf-8',
|
|
611
|
+
timeout: 10_000,
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
catch { /* non-fatal */ }
|
|
615
|
+
}
|
|
616
|
+
// =============================================================================
|
|
617
|
+
// Session lock file
|
|
618
|
+
// =============================================================================
|
|
619
|
+
/**
|
|
620
|
+
* Write a session lock file (.promptwheel/session.pid) with the current PID.
|
|
621
|
+
* Returns true if lock acquired, false if another live session holds it.
|
|
622
|
+
*/
|
|
623
|
+
export function acquireSessionLock(repoRoot) {
|
|
624
|
+
const lockPath = path.join(getPromptwheelDir(repoRoot), 'session.pid');
|
|
625
|
+
// Check existing lock
|
|
626
|
+
if (fs.existsSync(lockPath)) {
|
|
627
|
+
try {
|
|
628
|
+
const content = fs.readFileSync(lockPath, 'utf-8').trim();
|
|
629
|
+
const pid = parseInt(content, 10);
|
|
630
|
+
if (!isNaN(pid) && pid > 0) {
|
|
631
|
+
// Check if PID is still alive
|
|
632
|
+
try {
|
|
633
|
+
process.kill(pid, 0); // signal 0 = existence check only
|
|
634
|
+
// Process is alive — another session is running
|
|
635
|
+
return { acquired: false };
|
|
636
|
+
}
|
|
637
|
+
catch {
|
|
638
|
+
// Process is dead — stale lock, clean it up
|
|
639
|
+
fs.unlinkSync(lockPath);
|
|
640
|
+
// Fall through to acquire
|
|
641
|
+
return acquireLock(lockPath, pid);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
catch {
|
|
646
|
+
// Corrupt lock file — remove and proceed
|
|
647
|
+
try {
|
|
648
|
+
fs.unlinkSync(lockPath);
|
|
649
|
+
}
|
|
650
|
+
catch { /* skip */ }
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
return acquireLock(lockPath);
|
|
654
|
+
}
|
|
655
|
+
function acquireLock(lockPath, stalePid) {
|
|
656
|
+
try {
|
|
657
|
+
fs.writeFileSync(lockPath, String(process.pid));
|
|
658
|
+
return { acquired: true, stalePid };
|
|
659
|
+
}
|
|
660
|
+
catch {
|
|
661
|
+
return { acquired: false };
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Release the session lock file on clean shutdown.
|
|
666
|
+
*/
|
|
667
|
+
export function releaseSessionLock(repoRoot) {
|
|
668
|
+
const lockPath = path.join(getPromptwheelDir(repoRoot), 'session.pid');
|
|
669
|
+
try {
|
|
670
|
+
// Only remove if we own it
|
|
671
|
+
const content = fs.readFileSync(lockPath, 'utf-8').trim();
|
|
672
|
+
if (parseInt(content, 10) === process.pid) {
|
|
673
|
+
fs.unlinkSync(lockPath);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
catch { /* non-fatal */ }
|
|
677
|
+
}
|
|
678
|
+
// =============================================================================
|
|
679
|
+
// Main prune (sync — file-system only)
|
|
680
|
+
// =============================================================================
|
|
681
|
+
export function pruneAll(repoRoot, config, dryRun = false) {
|
|
682
|
+
const report = emptyReport();
|
|
683
|
+
report.runFoldersRemoved = pruneRunFolders(repoRoot, config.maxRuns, dryRun);
|
|
684
|
+
report.historyLinesRemoved = pruneHistory(repoRoot, config.maxHistoryEntries, dryRun);
|
|
685
|
+
report.artifactsRemoved = pruneArtifacts(repoRoot, config.maxArtifactsPerRun, dryRun);
|
|
686
|
+
report.spoolArchivesRemoved = pruneSpoolArchives(repoRoot, config.maxSpoolArchives, dryRun);
|
|
687
|
+
report.deferredProposalsRemoved = pruneDeferredProposals(repoRoot, config.maxDeferredProposals, dryRun);
|
|
688
|
+
report.staleWorktreesRemoved = pruneStaleWorktrees(repoRoot, dryRun);
|
|
689
|
+
// Branch pruning is NOT run here — only via explicit `promptwheel prune`
|
|
690
|
+
// Log rotation & metrics pruning
|
|
691
|
+
report.logsRotated = rotateLogs(repoRoot, config.maxLogSizeBytes, dryRun);
|
|
692
|
+
report.metricsLinesRemoved = pruneMetrics(repoRoot, config.maxMetricsEntries, dryRun);
|
|
693
|
+
report.artifactsByAgeRemoved = pruneArtifactsByAge(repoRoot, config.maxArtifactAgeDays, dryRun);
|
|
694
|
+
// Git worktree prune (dangling refs)
|
|
695
|
+
if (!dryRun)
|
|
696
|
+
gitWorktreePrune(repoRoot);
|
|
697
|
+
report.totalPruned =
|
|
698
|
+
report.runFoldersRemoved +
|
|
699
|
+
report.historyLinesRemoved +
|
|
700
|
+
report.artifactsRemoved +
|
|
701
|
+
report.spoolArchivesRemoved +
|
|
702
|
+
report.deferredProposalsRemoved +
|
|
703
|
+
report.staleWorktreesRemoved +
|
|
704
|
+
report.logsRotated +
|
|
705
|
+
report.metricsLinesRemoved +
|
|
706
|
+
report.artifactsByAgeRemoved;
|
|
707
|
+
return report;
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Full prune including async DB operations.
|
|
711
|
+
*/
|
|
712
|
+
export async function pruneAllAsync(repoRoot, config, adapter = null, dryRun = false) {
|
|
713
|
+
const report = pruneAll(repoRoot, config, dryRun);
|
|
714
|
+
report.completedTicketsRemoved = await pruneCompletedTickets(adapter, config.maxCompletedTickets, dryRun);
|
|
715
|
+
report.mergedBranchesRemoved = pruneMergedBranches(repoRoot, config.maxMergedBranches, dryRun);
|
|
716
|
+
report.staleBranchesRemoved = pruneStaleBranches(repoRoot, config.maxStaleBranchDays, dryRun);
|
|
717
|
+
report.totalPruned += report.completedTicketsRemoved + report.mergedBranchesRemoved + report.staleBranchesRemoved;
|
|
718
|
+
return report;
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* Format a prune report for display.
|
|
722
|
+
*/
|
|
723
|
+
export function formatPruneReport(report, dryRun = false) {
|
|
724
|
+
const prefix = dryRun ? 'Would remove' : 'Removed';
|
|
725
|
+
const lines = [];
|
|
726
|
+
if (report.runFoldersRemoved > 0) {
|
|
727
|
+
lines.push(` ${prefix} ${report.runFoldersRemoved} run folder(s)`);
|
|
728
|
+
}
|
|
729
|
+
if (report.historyLinesRemoved > 0) {
|
|
730
|
+
lines.push(` ${prefix} ${report.historyLinesRemoved} history line(s)`);
|
|
731
|
+
}
|
|
732
|
+
if (report.artifactsRemoved > 0) {
|
|
733
|
+
lines.push(` ${prefix} ${report.artifactsRemoved} artifact file(s)`);
|
|
734
|
+
}
|
|
735
|
+
if (report.spoolArchivesRemoved > 0) {
|
|
736
|
+
lines.push(` ${prefix} ${report.spoolArchivesRemoved} spool archive(s)`);
|
|
737
|
+
}
|
|
738
|
+
if (report.deferredProposalsRemoved > 0) {
|
|
739
|
+
lines.push(` ${prefix} ${report.deferredProposalsRemoved} deferred proposal(s)`);
|
|
740
|
+
}
|
|
741
|
+
if (report.completedTicketsRemoved > 0) {
|
|
742
|
+
lines.push(` ${prefix} ${report.completedTicketsRemoved} completed ticket(s)`);
|
|
743
|
+
}
|
|
744
|
+
if (report.mergedBranchesRemoved > 0) {
|
|
745
|
+
lines.push(` ${prefix} ${report.mergedBranchesRemoved} merged branch(es)`);
|
|
746
|
+
}
|
|
747
|
+
if (report.staleBranchesRemoved > 0) {
|
|
748
|
+
lines.push(` ${prefix} ${report.staleBranchesRemoved} stale ticket branch(es)`);
|
|
749
|
+
}
|
|
750
|
+
if (report.staleWorktreesRemoved > 0) {
|
|
751
|
+
lines.push(` ${prefix} ${report.staleWorktreesRemoved} stale worktree(s)`);
|
|
752
|
+
}
|
|
753
|
+
if (report.logsRotated > 0) {
|
|
754
|
+
lines.push(` Rotated tui.log`);
|
|
755
|
+
}
|
|
756
|
+
if (report.metricsLinesRemoved > 0) {
|
|
757
|
+
lines.push(` ${prefix} ${report.metricsLinesRemoved} metrics line(s)`);
|
|
758
|
+
}
|
|
759
|
+
if (report.artifactsByAgeRemoved > 0) {
|
|
760
|
+
lines.push(` ${prefix} ${report.artifactsByAgeRemoved} expired artifact(s)`);
|
|
761
|
+
}
|
|
762
|
+
if (lines.length === 0) {
|
|
763
|
+
return ' Nothing to prune.';
|
|
764
|
+
}
|
|
765
|
+
return lines.join('\n');
|
|
766
|
+
}
|
|
767
|
+
//# sourceMappingURL=retention.js.map
|