@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,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Daemon mode: types, config, pure functions, and main loop.
|
|
3
|
+
*
|
|
4
|
+
* The daemon wraps the existing auto mode — each wake cycle calls
|
|
5
|
+
* runAutoMode() with bounded cycles. Between cycles, the session lock
|
|
6
|
+
* is released so manual `promptwheel` runs can proceed.
|
|
7
|
+
*/
|
|
8
|
+
import * as fs from 'node:fs';
|
|
9
|
+
import * as path from 'node:path';
|
|
10
|
+
import { execSync } from 'node:child_process';
|
|
11
|
+
import { getPromptwheelDir } from './solo-config.js';
|
|
12
|
+
import { acquireSessionLock, releaseSessionLock } from './retention.js';
|
|
13
|
+
import { rotateDaemonLog, removeDaemonPid } from './daemon-fork.js';
|
|
14
|
+
import { notifyAll } from './daemon-notifier.js';
|
|
15
|
+
import { runAutoMode } from './solo-auto.js';
|
|
16
|
+
export const DEFAULT_DAEMON_CONFIG = {
|
|
17
|
+
pollIntervalMinutes: 30,
|
|
18
|
+
cyclesPerWake: 3,
|
|
19
|
+
notifications: [],
|
|
20
|
+
maxLogSizeMB: 10,
|
|
21
|
+
};
|
|
22
|
+
const WAKE_METRICS_FILE = 'daemon-wake-metrics.json';
|
|
23
|
+
export function writeDaemonWakeMetrics(repoRoot, metrics) {
|
|
24
|
+
const dir = getPromptwheelDir(repoRoot);
|
|
25
|
+
if (!fs.existsSync(dir))
|
|
26
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
27
|
+
fs.writeFileSync(path.join(dir, WAKE_METRICS_FILE), JSON.stringify(metrics, null, 2));
|
|
28
|
+
}
|
|
29
|
+
export function readDaemonWakeMetrics(repoRoot) {
|
|
30
|
+
const filePath = path.join(getPromptwheelDir(repoRoot), WAKE_METRICS_FILE);
|
|
31
|
+
try {
|
|
32
|
+
if (!fs.existsSync(filePath))
|
|
33
|
+
return null;
|
|
34
|
+
const data = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
35
|
+
// Clean up after reading
|
|
36
|
+
fs.unlinkSync(filePath);
|
|
37
|
+
return data;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// ── Pure functions ───────────────────────────────────────────────────────────
|
|
44
|
+
/**
|
|
45
|
+
* Compute adaptive polling interval based on recent activity.
|
|
46
|
+
*
|
|
47
|
+
* - Work done + new commits → base * 0.5
|
|
48
|
+
* - New commits but no proposals → base * 1.0
|
|
49
|
+
* - No new commits → base * 1.5
|
|
50
|
+
* - Quiet hours with boost → base * 0.25
|
|
51
|
+
* - Floor: 5 minutes. Ceiling: base * 3.
|
|
52
|
+
*/
|
|
53
|
+
export function computeAdaptiveInterval(state, config, context) {
|
|
54
|
+
const baseMs = config.pollIntervalMinutes * 60_000;
|
|
55
|
+
const floorMs = 5 * 60_000;
|
|
56
|
+
const ceilingMs = baseMs * 3;
|
|
57
|
+
let multiplier;
|
|
58
|
+
if (context.isQuietBoost) {
|
|
59
|
+
multiplier = 0.25;
|
|
60
|
+
}
|
|
61
|
+
else if (context.hadWork && context.hadNewCommits) {
|
|
62
|
+
multiplier = 0.5;
|
|
63
|
+
}
|
|
64
|
+
else if (context.hadNewCommits) {
|
|
65
|
+
multiplier = 1.0;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
// No new commits — lengthen based on consecutive idle cycles
|
|
69
|
+
multiplier = Math.min(1.5 + state.consecutiveNoWorkCycles * 0.25, 3.0);
|
|
70
|
+
}
|
|
71
|
+
return Math.max(floorMs, Math.min(Math.round(baseMs * multiplier), ceilingMs));
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Check if current time falls within the quiet hours window.
|
|
75
|
+
* Handles midnight crossing (e.g. start=22:00, end=06:00).
|
|
76
|
+
*/
|
|
77
|
+
export function isInQuietHours(config, now) {
|
|
78
|
+
if (!config.quietHours)
|
|
79
|
+
return false;
|
|
80
|
+
const d = now ?? new Date();
|
|
81
|
+
const currentMinutes = d.getHours() * 60 + d.getMinutes();
|
|
82
|
+
const [startH, startM] = config.quietHours.start.split(':').map(Number);
|
|
83
|
+
const [endH, endM] = config.quietHours.end.split(':').map(Number);
|
|
84
|
+
const startMinutes = startH * 60 + startM;
|
|
85
|
+
const endMinutes = endH * 60 + endM;
|
|
86
|
+
if (startMinutes <= endMinutes) {
|
|
87
|
+
// Same-day window (e.g. 09:00 – 17:00)
|
|
88
|
+
return currentMinutes >= startMinutes && currentMinutes < endMinutes;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
// Midnight-crossing window (e.g. 22:00 – 06:00)
|
|
92
|
+
return currentMinutes >= startMinutes || currentMinutes < endMinutes;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// ── State persistence ────────────────────────────────────────────────────────
|
|
96
|
+
const DAEMON_STATE_FILE = 'daemon-state.json';
|
|
97
|
+
export function readDaemonState(repoRoot) {
|
|
98
|
+
const filePath = path.join(getPromptwheelDir(repoRoot), DAEMON_STATE_FILE);
|
|
99
|
+
try {
|
|
100
|
+
if (!fs.existsSync(filePath))
|
|
101
|
+
return null;
|
|
102
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
export function writeDaemonState(repoRoot, state) {
|
|
109
|
+
const dir = getPromptwheelDir(repoRoot);
|
|
110
|
+
if (!fs.existsSync(dir))
|
|
111
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
112
|
+
fs.writeFileSync(path.join(dir, DAEMON_STATE_FILE), JSON.stringify(state, null, 2));
|
|
113
|
+
}
|
|
114
|
+
export function createInitialDaemonState(config) {
|
|
115
|
+
return {
|
|
116
|
+
startedAt: Date.now(),
|
|
117
|
+
lastWakeAt: 0,
|
|
118
|
+
lastTrigger: 'timer',
|
|
119
|
+
totalWakes: 0,
|
|
120
|
+
totalCyclesCompleted: 0,
|
|
121
|
+
totalTicketsCompleted: 0,
|
|
122
|
+
totalTicketsFailed: 0,
|
|
123
|
+
currentInterval: config.pollIntervalMinutes * 60_000,
|
|
124
|
+
consecutiveNoWorkCycles: 0,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function checkTrigger(repoRoot, state) {
|
|
128
|
+
const now = Date.now();
|
|
129
|
+
const timerExpired = state.lastWakeAt === 0 || (now - state.lastWakeAt) >= state.currentInterval;
|
|
130
|
+
// Check for new commits since last wake
|
|
131
|
+
let hadNewCommits = false;
|
|
132
|
+
if (state.lastWakeAt > 0) {
|
|
133
|
+
try {
|
|
134
|
+
const since = new Date(state.lastWakeAt).toISOString();
|
|
135
|
+
const output = execSync(`git log --since="${since}" --oneline --max-count=1`, {
|
|
136
|
+
cwd: repoRoot,
|
|
137
|
+
encoding: 'utf-8',
|
|
138
|
+
timeout: 10_000,
|
|
139
|
+
}).trim();
|
|
140
|
+
hadNewCommits = output.length > 0;
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// git log failed — proceed with timer only
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
shouldWake: timerExpired || hadNewCommits,
|
|
148
|
+
trigger: hadNewCommits ? 'commits' : 'timer',
|
|
149
|
+
hadNewCommits,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
// ── Sleep helper ─────────────────────────────────────────────────────────────
|
|
153
|
+
function sleep(ms) {
|
|
154
|
+
return new Promise(r => setTimeout(r, ms));
|
|
155
|
+
}
|
|
156
|
+
// ── Main daemon loop ─────────────────────────────────────────────────────────
|
|
157
|
+
function log(msg) {
|
|
158
|
+
console.log(`[${new Date().toISOString()}] ${msg}`);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Main daemon entry point. Runs in the forked child process.
|
|
162
|
+
* Wakes periodically, runs bounded auto sessions, and sleeps.
|
|
163
|
+
*/
|
|
164
|
+
export async function startDaemon(repoRoot, config) {
|
|
165
|
+
const state = readDaemonState(repoRoot) ?? createInitialDaemonState(config);
|
|
166
|
+
state.startedAt = Date.now();
|
|
167
|
+
writeDaemonState(repoRoot, state);
|
|
168
|
+
let shutdown = false;
|
|
169
|
+
process.on('SIGTERM', () => { shutdown = true; });
|
|
170
|
+
process.on('SIGINT', () => { shutdown = true; });
|
|
171
|
+
log(`Daemon started (pid=${process.pid}, interval=${config.pollIntervalMinutes}m)`);
|
|
172
|
+
while (!shutdown) {
|
|
173
|
+
rotateDaemonLog(repoRoot, config.maxLogSizeMB);
|
|
174
|
+
const trigger = checkTrigger(repoRoot, state);
|
|
175
|
+
const quiet = isInQuietHours(config);
|
|
176
|
+
const isBoost = quiet && config.quietHours?.policy === 'boost';
|
|
177
|
+
if (!trigger.shouldWake || (quiet && !isBoost)) {
|
|
178
|
+
// Not time to wake yet, or in quiet hours with pause policy
|
|
179
|
+
await sleep(Math.min(state.currentInterval, 60_000));
|
|
180
|
+
if (shutdown)
|
|
181
|
+
break;
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
// Try to acquire session lock
|
|
185
|
+
const lockResult = acquireSessionLock(repoRoot);
|
|
186
|
+
if (!lockResult.acquired) {
|
|
187
|
+
log('Session lock held by another process, skipping this cycle');
|
|
188
|
+
await sleep(60_000);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
const wakeStart = Date.now();
|
|
192
|
+
state.lastWakeAt = wakeStart;
|
|
193
|
+
state.lastTrigger = trigger.trigger;
|
|
194
|
+
state.totalWakes++;
|
|
195
|
+
let hadWork = false;
|
|
196
|
+
log(`Wake #${state.totalWakes} (trigger=${trigger.trigger})`);
|
|
197
|
+
try {
|
|
198
|
+
const exitCode = await runAutoMode({
|
|
199
|
+
wheel: true,
|
|
200
|
+
cycles: String(config.cyclesPerWake),
|
|
201
|
+
formula: config.formula,
|
|
202
|
+
scope: config.scope,
|
|
203
|
+
daemon: true,
|
|
204
|
+
yes: true,
|
|
205
|
+
tui: false,
|
|
206
|
+
});
|
|
207
|
+
hadWork = exitCode === 0;
|
|
208
|
+
if (hadWork) {
|
|
209
|
+
state.consecutiveNoWorkCycles = 0;
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
state.consecutiveNoWorkCycles++;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
catch (err) {
|
|
216
|
+
log(`Wake cycle error: ${err instanceof Error ? err.message : err}`);
|
|
217
|
+
state.consecutiveNoWorkCycles++;
|
|
218
|
+
}
|
|
219
|
+
// Release session lock so manual runs can proceed
|
|
220
|
+
releaseSessionLock(repoRoot);
|
|
221
|
+
// Read metrics persisted by finalizeSession
|
|
222
|
+
const wakeMetrics = readDaemonWakeMetrics(repoRoot);
|
|
223
|
+
if (wakeMetrics) {
|
|
224
|
+
state.totalCyclesCompleted += wakeMetrics.cyclesCompleted;
|
|
225
|
+
state.totalTicketsCompleted += wakeMetrics.ticketsCompleted;
|
|
226
|
+
state.totalTicketsFailed += wakeMetrics.ticketsFailed;
|
|
227
|
+
}
|
|
228
|
+
// Notifications
|
|
229
|
+
if (config.notifications.length > 0) {
|
|
230
|
+
const summary = {
|
|
231
|
+
repoName: path.basename(repoRoot),
|
|
232
|
+
startTime: wakeStart,
|
|
233
|
+
endTime: Date.now(),
|
|
234
|
+
cyclesCompleted: wakeMetrics?.cyclesCompleted ?? 0,
|
|
235
|
+
ticketsCompleted: wakeMetrics?.ticketsCompleted ?? 0,
|
|
236
|
+
ticketsFailed: wakeMetrics?.ticketsFailed ?? 0,
|
|
237
|
+
prUrls: wakeMetrics?.prUrls ?? [],
|
|
238
|
+
trigger: trigger.trigger,
|
|
239
|
+
reportPath: wakeMetrics?.reportPath,
|
|
240
|
+
};
|
|
241
|
+
await notifyAll(config.notifications, summary, log);
|
|
242
|
+
}
|
|
243
|
+
// Adaptive interval
|
|
244
|
+
state.currentInterval = computeAdaptiveInterval(state, config, {
|
|
245
|
+
hadWork,
|
|
246
|
+
hadNewCommits: trigger.hadNewCommits,
|
|
247
|
+
isQuietBoost: isBoost,
|
|
248
|
+
});
|
|
249
|
+
writeDaemonState(repoRoot, state);
|
|
250
|
+
log(`Next wake in ${Math.round(state.currentInterval / 60_000)}m`);
|
|
251
|
+
// Sleep until next wake
|
|
252
|
+
const sleepTarget = state.currentInterval;
|
|
253
|
+
const sleepChunk = 10_000; // Check shutdown every 10s
|
|
254
|
+
let slept = 0;
|
|
255
|
+
while (!shutdown && slept < sleepTarget) {
|
|
256
|
+
await sleep(Math.min(sleepChunk, sleepTarget - slept));
|
|
257
|
+
slept += sleepChunk;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
// Cleanup
|
|
261
|
+
log('Daemon shutting down');
|
|
262
|
+
removeDaemonPid(repoRoot);
|
|
263
|
+
writeDaemonState(repoRoot, state);
|
|
264
|
+
}
|
|
265
|
+
//# sourceMappingURL=daemon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/lib/daemon.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,SAAS,EAA4B,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAqC7C,MAAM,CAAC,MAAM,qBAAqB,GAAiB;IACjD,mBAAmB,EAAE,EAAE;IACvB,aAAa,EAAE,CAAC;IAChB,aAAa,EAAE,EAAE;IACjB,YAAY,EAAE,EAAE;CACjB,CAAC;AAaF,MAAM,iBAAiB,GAAG,0BAA0B,CAAC;AAErD,MAAM,UAAU,sBAAsB,CAAC,QAAgB,EAAE,OAA0B;IACjF,MAAM,GAAG,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IACxC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,iBAAiB,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AACxF,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,QAAgB;IACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,EAAE,iBAAiB,CAAC,CAAC;IAC3E,IAAI,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;QAC5D,yBAAyB;QACzB,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,gFAAgF;AAEhF;;;;;;;;GAQG;AACH,MAAM,UAAU,uBAAuB,CACrC,KAAkB,EAClB,MAAoB,EACpB,OAA4E;IAE5E,MAAM,MAAM,GAAG,MAAM,CAAC,mBAAmB,GAAG,MAAM,CAAC;IACnD,MAAM,OAAO,GAAG,CAAC,GAAG,MAAM,CAAC;IAC3B,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,CAAC;IAE7B,IAAI,UAAkB,CAAC;IAEvB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;QACzB,UAAU,GAAG,IAAI,CAAC;IACpB,CAAC;SAAM,IAAI,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;QACpD,UAAU,GAAG,GAAG,CAAC;IACnB,CAAC;SAAM,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;QACjC,UAAU,GAAG,GAAG,CAAC;IACnB,CAAC;SAAM,CAAC;QACN,6DAA6D;QAC7D,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,KAAK,CAAC,uBAAuB,GAAG,IAAI,EAAE,GAAG,CAAC,CAAC;IACzE,CAAC;IAED,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;AACjF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,MAAoB,EAAE,GAAU;IAC7D,IAAI,CAAC,MAAM,CAAC,UAAU;QAAE,OAAO,KAAK,CAAC;IAErC,MAAM,CAAC,GAAG,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;IAC5B,MAAM,cAAc,GAAG,CAAC,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,UAAU,EAAE,CAAC;IAE1D,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACxE,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAClE,MAAM,YAAY,GAAG,MAAM,GAAG,EAAE,GAAG,MAAM,CAAC;IAC1C,MAAM,UAAU,GAAG,IAAI,GAAG,EAAE,GAAG,IAAI,CAAC;IAEpC,IAAI,YAAY,IAAI,UAAU,EAAE,CAAC;QAC/B,uCAAuC;QACvC,OAAO,cAAc,IAAI,YAAY,IAAI,cAAc,GAAG,UAAU,CAAC;IACvE,CAAC;SAAM,CAAC;QACN,gDAAgD;QAChD,OAAO,cAAc,IAAI,YAAY,IAAI,cAAc,GAAG,UAAU,CAAC;IACvE,CAAC;AACH,CAAC;AAED,gFAAgF;AAEhF,MAAM,iBAAiB,GAAG,mBAAmB,CAAC;AAE9C,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,EAAE,iBAAiB,CAAC,CAAC;IAC3E,IAAI,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QAC1C,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,QAAgB,EAAE,KAAkB;IACnE,MAAM,GAAG,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IACxC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChE,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,iBAAiB,CAAC,EACjC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAC/B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,MAAoB;IAC3D,OAAO;QACL,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;QACrB,UAAU,EAAE,CAAC;QACb,WAAW,EAAE,OAAO;QACpB,UAAU,EAAE,CAAC;QACb,oBAAoB,EAAE,CAAC;QACvB,qBAAqB,EAAE,CAAC;QACxB,kBAAkB,EAAE,CAAC;QACrB,eAAe,EAAE,MAAM,CAAC,mBAAmB,GAAG,MAAM;QACpD,uBAAuB,EAAE,CAAC;KAC3B,CAAC;AACJ,CAAC;AAUD,SAAS,YAAY,CAAC,QAAgB,EAAE,KAAkB;IACxD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,YAAY,GAAG,KAAK,CAAC,UAAU,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC,eAAe,CAAC;IAEjG,wCAAwC;IACxC,IAAI,aAAa,GAAG,KAAK,CAAC;IAC1B,IAAI,KAAK,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;YACvD,MAAM,MAAM,GAAG,QAAQ,CAAC,oBAAoB,KAAK,2BAA2B,EAAE;gBAC5E,GAAG,EAAE,QAAQ;gBACb,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,MAAM;aAChB,CAAC,CAAC,IAAI,EAAE,CAAC;YACV,aAAa,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,2CAA2C;QAC7C,CAAC;IACH,CAAC;IAED,OAAO;QACL,UAAU,EAAE,YAAY,IAAI,aAAa;QACzC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;QAC5C,aAAa;KACd,CAAC;AACJ,CAAC;AAED,gFAAgF;AAEhF,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,gFAAgF;AAEhF,SAAS,GAAG,CAAC,GAAW;IACtB,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,GAAG,EAAE,CAAC,CAAC;AACtD,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,QAAgB,EAAE,MAAoB;IACtE,MAAM,KAAK,GAAG,eAAe,CAAC,QAAQ,CAAC,IAAI,wBAAwB,CAAC,MAAM,CAAC,CAAC;IAC5E,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,gBAAgB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAElC,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,GAAG,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAClD,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAEjD,GAAG,CAAC,uBAAuB,OAAO,CAAC,GAAG,cAAc,MAAM,CAAC,mBAAmB,IAAI,CAAC,CAAC;IAEpF,OAAO,CAAC,QAAQ,EAAE,CAAC;QACjB,eAAe,CAAC,QAAQ,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;QAE/C,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAC9C,MAAM,KAAK,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;QACrC,MAAM,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC;QAE/D,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAC/C,4DAA4D;YAC5D,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC,CAAC;YACrD,IAAI,QAAQ;gBAAE,MAAM;YACpB,SAAS;QACX,CAAC;QAED,8BAA8B;QAC9B,MAAM,UAAU,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;QAChD,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;YACzB,GAAG,CAAC,2DAA2D,CAAC,CAAC;YACjE,MAAM,KAAK,CAAC,MAAM,CAAC,CAAC;YACpB,SAAS;QACX,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,KAAK,CAAC,UAAU,GAAG,SAAS,CAAC;QAC7B,KAAK,CAAC,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;QACpC,KAAK,CAAC,UAAU,EAAE,CAAC;QACnB,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,GAAG,CAAC,SAAS,KAAK,CAAC,UAAU,aAAa,OAAO,CAAC,OAAO,GAAG,CAAC,CAAC;QAE9D,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC;gBACjC,KAAK,EAAE,IAAI;gBACX,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC;gBACpC,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,MAAM,EAAE,IAAI;gBACZ,GAAG,EAAE,IAAI;gBACT,GAAG,EAAE,KAAK;aACX,CAAC,CAAC;YAEH,OAAO,GAAG,QAAQ,KAAK,CAAC,CAAC;YACzB,IAAI,OAAO,EAAE,CAAC;gBACZ,KAAK,CAAC,uBAAuB,GAAG,CAAC,CAAC;YACpC,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,uBAAuB,EAAE,CAAC;YAClC,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,qBAAqB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;YACrE,KAAK,CAAC,uBAAuB,EAAE,CAAC;QAClC,CAAC;QAED,kDAAkD;QAClD,kBAAkB,CAAC,QAAQ,CAAC,CAAC;QAE7B,4CAA4C;QAC5C,MAAM,WAAW,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAC;QACpD,IAAI,WAAW,EAAE,CAAC;YAChB,KAAK,CAAC,oBAAoB,IAAI,WAAW,CAAC,eAAe,CAAC;YAC1D,KAAK,CAAC,qBAAqB,IAAI,WAAW,CAAC,gBAAgB,CAAC;YAC5D,KAAK,CAAC,kBAAkB,IAAI,WAAW,CAAC,aAAa,CAAC;QACxD,CAAC;QAED,gBAAgB;QAChB,IAAI,MAAM,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpC,MAAM,OAAO,GAAwB;gBACnC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBACjC,SAAS,EAAE,SAAS;gBACpB,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE;gBACnB,eAAe,EAAE,WAAW,EAAE,eAAe,IAAI,CAAC;gBAClD,gBAAgB,EAAE,WAAW,EAAE,gBAAgB,IAAI,CAAC;gBACpD,aAAa,EAAE,WAAW,EAAE,aAAa,IAAI,CAAC;gBAC9C,MAAM,EAAE,WAAW,EAAE,MAAM,IAAI,EAAE;gBACjC,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,UAAU,EAAE,WAAW,EAAE,UAAU;aACpC,CAAC;YACF,MAAM,SAAS,CAAC,MAAM,CAAC,aAAa,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;QACtD,CAAC;QAED,oBAAoB;QACpB,KAAK,CAAC,eAAe,GAAG,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE;YAC7D,OAAO;YACP,aAAa,EAAE,OAAO,CAAC,aAAa;YACpC,YAAY,EAAE,OAAO;SACtB,CAAC,CAAC;QACH,gBAAgB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAElC,GAAG,CAAC,gBAAgB,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,eAAe,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAEnE,wBAAwB;QACxB,MAAM,WAAW,GAAG,KAAK,CAAC,eAAe,CAAC;QAC1C,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,2BAA2B;QACtD,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,OAAO,CAAC,QAAQ,IAAI,KAAK,GAAG,WAAW,EAAE,CAAC;YACxC,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC;YACvD,KAAK,IAAI,UAAU,CAAC;QACtB,CAAC;IACH,CAAC;IAED,UAAU;IACV,GAAG,CAAC,sBAAsB,CAAC,CAAC;IAC5B,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC1B,gBAAgB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-run dedup memory with temporal decay.
|
|
3
|
+
*
|
|
4
|
+
* Persists completed/rejected proposal titles to `.promptwheel/dedup-memory.json`
|
|
5
|
+
* so the scout prompt knows what NOT to propose.
|
|
6
|
+
*
|
|
7
|
+
* Pure algorithms (decay, entry management) live in @promptwheel/core/dedup/shared.
|
|
8
|
+
* This file wraps them with filesystem I/O and adds CLI-specific extensions
|
|
9
|
+
* (failureReason, relatedTitles, metrics).
|
|
10
|
+
*/
|
|
11
|
+
import { type DedupEntry as CoreDedupEntry } from '@promptwheel/core/dedup/shared';
|
|
12
|
+
export interface DedupEntry extends CoreDedupEntry {
|
|
13
|
+
/** Why this entry failed (if not completed) */
|
|
14
|
+
failureReason?: 'qa_failed' | 'scope_violation' | 'spindle_abort' | 'agent_error' | 'no_changes';
|
|
15
|
+
/** Titles of proposals that were in the same batch (dependency tracking) */
|
|
16
|
+
relatedTitles?: string[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Load dedup memory, apply decay, prune dead entries, write back.
|
|
20
|
+
* Call once per session start (mirrors learnings.loadLearnings).
|
|
21
|
+
*/
|
|
22
|
+
export declare function loadDedupMemory(projectRoot: string): DedupEntry[];
|
|
23
|
+
/**
|
|
24
|
+
* Record a title as "seen" — either a new duplicate rejection or a completed ticket.
|
|
25
|
+
* If the title already exists, bumps weight and hit_count (re-confirmation).
|
|
26
|
+
*/
|
|
27
|
+
export declare function recordDedupEntry(projectRoot: string, title: string, completed: boolean, failureReason?: 'qa_failed' | 'scope_violation' | 'spindle_abort' | 'agent_error' | 'no_changes', relatedTitles?: string[]): void;
|
|
28
|
+
/**
|
|
29
|
+
* Batch-record multiple titles at once (avoids repeated file I/O).
|
|
30
|
+
*/
|
|
31
|
+
export declare function recordDedupEntries(projectRoot: string, titles: {
|
|
32
|
+
title: string;
|
|
33
|
+
completed: boolean;
|
|
34
|
+
}[]): void;
|
|
35
|
+
/**
|
|
36
|
+
* Format dedup memory for prompt injection.
|
|
37
|
+
* Highest-weight entries first, respects char budget.
|
|
38
|
+
*/
|
|
39
|
+
export declare function formatDedupForPrompt(entries: DedupEntry[], budget?: number): string;
|
|
40
|
+
/**
|
|
41
|
+
* Find proposals enabled by recently completed work.
|
|
42
|
+
* Returns related titles from completed entries within 48h
|
|
43
|
+
* that are NOT themselves completed.
|
|
44
|
+
*/
|
|
45
|
+
export declare function getEnabledProposals(projectRoot: string): string[];
|
|
46
|
+
//# sourceMappingURL=dedup-memory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dedup-memory.d.ts","sourceRoot":"","sources":["../../src/lib/dedup-memory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,EACL,KAAK,UAAU,IAAI,cAAc,EAGlC,MAAM,gCAAgC,CAAC;AAMxC,MAAM,WAAW,UAAW,SAAQ,cAAc;IAChD,+CAA+C;IAC/C,aAAa,CAAC,EAAE,WAAW,GAAG,iBAAiB,GAAG,eAAe,GAAG,aAAa,GAAG,YAAY,CAAC;IACjG,4EAA4E;IAC5E,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AA8CD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,UAAU,EAAE,CAajE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,OAAO,EAClB,aAAa,CAAC,EAAE,WAAW,GAAG,iBAAiB,GAAG,eAAe,GAAG,aAAa,GAAG,YAAY,EAChG,aAAa,CAAC,EAAE,MAAM,EAAE,GACvB,IAAI,CA8BN;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,EAAE,GAC9C,IAAI,CA0BN;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,MAAM,GAAE,MAAuB,GAAG,MAAM,CAwBnG;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAsBjE"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-run dedup memory with temporal decay.
|
|
3
|
+
*
|
|
4
|
+
* Persists completed/rejected proposal titles to `.promptwheel/dedup-memory.json`
|
|
5
|
+
* so the scout prompt knows what NOT to propose.
|
|
6
|
+
*
|
|
7
|
+
* Pure algorithms (decay, entry management) live in @promptwheel/core/dedup/shared.
|
|
8
|
+
* This file wraps them with filesystem I/O and adds CLI-specific extensions
|
|
9
|
+
* (failureReason, relatedTitles, metrics).
|
|
10
|
+
*/
|
|
11
|
+
import * as fs from 'node:fs';
|
|
12
|
+
import * as path from 'node:path';
|
|
13
|
+
import { metric } from './metrics.js';
|
|
14
|
+
import { applyDecay, DEDUP_DEFAULTS, } from '@promptwheel/core/dedup/shared';
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Constants
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
const DEDUP_FILE = 'dedup-memory.json';
|
|
19
|
+
const MAX_WEIGHT = DEDUP_DEFAULTS.MAX_WEIGHT;
|
|
20
|
+
const BUMP_AMOUNT = DEDUP_DEFAULTS.BUMP_AMOUNT;
|
|
21
|
+
const COMPLETED_WEIGHT = DEDUP_DEFAULTS.COMPLETED_WEIGHT;
|
|
22
|
+
const DEFAULT_WEIGHT = DEDUP_DEFAULTS.DEFAULT_WEIGHT;
|
|
23
|
+
const DEFAULT_BUDGET = DEDUP_DEFAULTS.DEFAULT_BUDGET;
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// File I/O
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
function dedupPath(projectRoot) {
|
|
28
|
+
return path.join(projectRoot, '.promptwheel', DEDUP_FILE);
|
|
29
|
+
}
|
|
30
|
+
function readEntries(projectRoot) {
|
|
31
|
+
const fp = dedupPath(projectRoot);
|
|
32
|
+
if (!fs.existsSync(fp))
|
|
33
|
+
return [];
|
|
34
|
+
try {
|
|
35
|
+
const raw = fs.readFileSync(fp, 'utf8');
|
|
36
|
+
const parsed = JSON.parse(raw);
|
|
37
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function writeEntries(projectRoot, entries) {
|
|
44
|
+
const fp = dedupPath(projectRoot);
|
|
45
|
+
const dir = path.dirname(fp);
|
|
46
|
+
if (!fs.existsSync(dir)) {
|
|
47
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
48
|
+
}
|
|
49
|
+
fs.writeFileSync(fp, JSON.stringify(entries, null, 2) + '\n', 'utf8');
|
|
50
|
+
}
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
// Core API
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
/**
|
|
55
|
+
* Load dedup memory, apply decay, prune dead entries, write back.
|
|
56
|
+
* Call once per session start (mirrors learnings.loadLearnings).
|
|
57
|
+
*/
|
|
58
|
+
export function loadDedupMemory(projectRoot) {
|
|
59
|
+
const entries = readEntries(projectRoot);
|
|
60
|
+
const surviving = applyDecay(entries, DEDUP_DEFAULTS.DECAY_RATE);
|
|
61
|
+
writeEntries(projectRoot, surviving);
|
|
62
|
+
// Instrument: track dedup memory load
|
|
63
|
+
metric('dedup', 'loaded', {
|
|
64
|
+
count: surviving.length,
|
|
65
|
+
decayed: entries.length - surviving.length,
|
|
66
|
+
completed: surviving.filter(e => e.completed).length,
|
|
67
|
+
});
|
|
68
|
+
return surviving;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Record a title as "seen" — either a new duplicate rejection or a completed ticket.
|
|
72
|
+
* If the title already exists, bumps weight and hit_count (re-confirmation).
|
|
73
|
+
*/
|
|
74
|
+
export function recordDedupEntry(projectRoot, title, completed, failureReason, relatedTitles) {
|
|
75
|
+
const entries = readEntries(projectRoot);
|
|
76
|
+
const now = new Date().toISOString();
|
|
77
|
+
const normalized = title.toLowerCase().trim();
|
|
78
|
+
const existing = entries.find(e => e.title.toLowerCase().trim() === normalized);
|
|
79
|
+
if (existing) {
|
|
80
|
+
existing.weight = Math.min(MAX_WEIGHT, existing.weight + BUMP_AMOUNT);
|
|
81
|
+
existing.last_seen_at = now;
|
|
82
|
+
existing.hit_count++;
|
|
83
|
+
if (completed)
|
|
84
|
+
existing.completed = true;
|
|
85
|
+
if (failureReason)
|
|
86
|
+
existing.failureReason = failureReason;
|
|
87
|
+
if (relatedTitles?.length)
|
|
88
|
+
existing.relatedTitles = relatedTitles;
|
|
89
|
+
// Instrument: duplicate detected
|
|
90
|
+
metric('dedup', 'duplicate_found', { hitCount: existing.hit_count, completed });
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
entries.push({
|
|
94
|
+
title,
|
|
95
|
+
weight: completed ? COMPLETED_WEIGHT : DEFAULT_WEIGHT,
|
|
96
|
+
created_at: now,
|
|
97
|
+
last_seen_at: now,
|
|
98
|
+
hit_count: 1,
|
|
99
|
+
completed,
|
|
100
|
+
failureReason,
|
|
101
|
+
relatedTitles,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
writeEntries(projectRoot, entries);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Batch-record multiple titles at once (avoids repeated file I/O).
|
|
108
|
+
*/
|
|
109
|
+
export function recordDedupEntries(projectRoot, titles) {
|
|
110
|
+
if (titles.length === 0)
|
|
111
|
+
return;
|
|
112
|
+
const entries = readEntries(projectRoot);
|
|
113
|
+
const now = new Date().toISOString();
|
|
114
|
+
for (const { title, completed } of titles) {
|
|
115
|
+
const normalized = title.toLowerCase().trim();
|
|
116
|
+
const existing = entries.find(e => e.title.toLowerCase().trim() === normalized);
|
|
117
|
+
if (existing) {
|
|
118
|
+
existing.weight = Math.min(MAX_WEIGHT, existing.weight + BUMP_AMOUNT);
|
|
119
|
+
existing.last_seen_at = now;
|
|
120
|
+
existing.hit_count++;
|
|
121
|
+
if (completed)
|
|
122
|
+
existing.completed = true;
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
entries.push({
|
|
126
|
+
title,
|
|
127
|
+
weight: completed ? COMPLETED_WEIGHT : DEFAULT_WEIGHT,
|
|
128
|
+
created_at: now,
|
|
129
|
+
last_seen_at: now,
|
|
130
|
+
hit_count: 1,
|
|
131
|
+
completed,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
writeEntries(projectRoot, entries);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Format dedup memory for prompt injection.
|
|
139
|
+
* Highest-weight entries first, respects char budget.
|
|
140
|
+
*/
|
|
141
|
+
export function formatDedupForPrompt(entries, budget = DEFAULT_BUDGET) {
|
|
142
|
+
if (entries.length === 0)
|
|
143
|
+
return '';
|
|
144
|
+
const sorted = [...entries].sort((a, b) => b.weight - a.weight);
|
|
145
|
+
const lines = [];
|
|
146
|
+
let charCount = 0;
|
|
147
|
+
const header = '<already-completed>\n## Already Completed — Do NOT Propose These\n\nThe following improvements have already been done or attempted. Do NOT propose similar changes:\n';
|
|
148
|
+
const footer = '\n</already-completed>';
|
|
149
|
+
charCount += header.length + footer.length;
|
|
150
|
+
for (const e of sorted) {
|
|
151
|
+
const status = e.completed ? '✓ done'
|
|
152
|
+
: e.failureReason === 'scope_violation' ? 'scope issue — may work with broader scope'
|
|
153
|
+
: e.failureReason === 'no_changes' ? 'no changes produced'
|
|
154
|
+
: 'attempted — failed';
|
|
155
|
+
const line = `- ${e.title} (${status}, seen ${e.hit_count}x)`;
|
|
156
|
+
if (charCount + line.length + 1 > budget)
|
|
157
|
+
break;
|
|
158
|
+
lines.push(line);
|
|
159
|
+
charCount += line.length + 1;
|
|
160
|
+
}
|
|
161
|
+
if (lines.length === 0)
|
|
162
|
+
return '';
|
|
163
|
+
return header + lines.join('\n') + footer;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Find proposals enabled by recently completed work.
|
|
167
|
+
* Returns related titles from completed entries within 48h
|
|
168
|
+
* that are NOT themselves completed.
|
|
169
|
+
*/
|
|
170
|
+
export function getEnabledProposals(projectRoot) {
|
|
171
|
+
const entries = readEntries(projectRoot);
|
|
172
|
+
const now = Date.now();
|
|
173
|
+
const cutoff = 48 * 60 * 60 * 1000;
|
|
174
|
+
const completedTitles = new Set(entries.filter(e => e.completed).map(e => e.title.toLowerCase().trim()));
|
|
175
|
+
const enabled = [];
|
|
176
|
+
for (const e of entries) {
|
|
177
|
+
if (!e.completed || !e.relatedTitles?.length)
|
|
178
|
+
continue;
|
|
179
|
+
const lastSeen = new Date(e.last_seen_at).getTime();
|
|
180
|
+
if (now - lastSeen > cutoff)
|
|
181
|
+
continue;
|
|
182
|
+
for (const rt of e.relatedTitles) {
|
|
183
|
+
if (!completedTitles.has(rt.toLowerCase().trim())) {
|
|
184
|
+
enabled.push(rt);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return [...new Set(enabled)];
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=dedup-memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dedup-memory.js","sourceRoot":"","sources":["../../src/lib/dedup-memory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deduplication utilities for proposal filtering.
|
|
3
|
+
*
|
|
4
|
+
* Pure algorithms (normalizeTitle, titleSimilarity, isDuplicate) live in
|
|
5
|
+
* @promptwheel/core/dedup/shared. This file re-exports them and adds
|
|
6
|
+
* I/O-dependent helpers (database lookups, git branch listing).
|
|
7
|
+
*/
|
|
8
|
+
import type { DatabaseAdapter } from '@promptwheel/core/db';
|
|
9
|
+
import type { TicketProposal } from '@promptwheel/core/scout';
|
|
10
|
+
export { normalizeTitle, titleSimilarity } from '@promptwheel/core/dedup/shared';
|
|
11
|
+
/**
|
|
12
|
+
* Sleep helper
|
|
13
|
+
*/
|
|
14
|
+
export declare function sleep(ms: number): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Check if a proposal is a duplicate of existing tickets or open PRs
|
|
17
|
+
*/
|
|
18
|
+
export declare function isDuplicateProposal(proposal: {
|
|
19
|
+
title: string;
|
|
20
|
+
files?: string[];
|
|
21
|
+
}, existingTitles: string[], openPrBranches: string[], similarityThreshold?: number): Promise<{
|
|
22
|
+
isDuplicate: boolean;
|
|
23
|
+
reason?: string;
|
|
24
|
+
}>;
|
|
25
|
+
/**
|
|
26
|
+
* Get existing ticket titles and open PR branches for deduplication
|
|
27
|
+
*/
|
|
28
|
+
export declare function getDeduplicationContext(adapter: DatabaseAdapter, projectId: string, repoRoot: string): Promise<{
|
|
29
|
+
existingTitles: string[];
|
|
30
|
+
openPrBranches: string[];
|
|
31
|
+
}>;
|
|
32
|
+
/**
|
|
33
|
+
* Determine adaptive parallel count based on ticket complexity.
|
|
34
|
+
* When --parallel is NOT explicitly set, scale based on proposal mix.
|
|
35
|
+
*/
|
|
36
|
+
export declare function getAdaptiveParallelCount(proposals: TicketProposal[]): number;
|
|
37
|
+
//# sourceMappingURL=dedup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dedup.d.ts","sourceRoot":"","sources":["../../src/lib/dedup.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAI9D,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAGjF;;GAEG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,EAC7C,cAAc,EAAE,MAAM,EAAE,EACxB,cAAc,EAAE,MAAM,EAAE,EACxB,mBAAmB,SAAM,GACxB,OAAO,CAAC;IAAE,WAAW,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAyBpD;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,eAAe,EACxB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;IAAE,cAAc,EAAE,MAAM,EAAE,CAAC;IAAC,cAAc,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAyBjE;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,cAAc,EAAE,GAAG,MAAM,CAO5E"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deduplication utilities for proposal filtering.
|
|
3
|
+
*
|
|
4
|
+
* Pure algorithms (normalizeTitle, titleSimilarity, isDuplicate) live in
|
|
5
|
+
* @promptwheel/core/dedup/shared. This file re-exports them and adds
|
|
6
|
+
* I/O-dependent helpers (database lookups, git branch listing).
|
|
7
|
+
*/
|
|
8
|
+
import { tickets } from '@promptwheel/core/repos';
|
|
9
|
+
import { gitExecFile } from './solo-git.js';
|
|
10
|
+
// Re-export pure algorithms from core
|
|
11
|
+
export { normalizeTitle, titleSimilarity } from '@promptwheel/core/dedup/shared';
|
|
12
|
+
import { normalizeTitle, titleSimilarity } from '@promptwheel/core/dedup/shared';
|
|
13
|
+
/**
|
|
14
|
+
* Sleep helper
|
|
15
|
+
*/
|
|
16
|
+
export function sleep(ms) {
|
|
17
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Check if a proposal is a duplicate of existing tickets or open PRs
|
|
21
|
+
*/
|
|
22
|
+
export async function isDuplicateProposal(proposal, existingTitles, openPrBranches, similarityThreshold = 0.6) {
|
|
23
|
+
const normalizedProposal = normalizeTitle(proposal.title);
|
|
24
|
+
for (const existing of existingTitles) {
|
|
25
|
+
if (normalizeTitle(existing) === normalizedProposal) {
|
|
26
|
+
return { isDuplicate: true, reason: `Exact match: "${existing}"` };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
for (const existing of existingTitles) {
|
|
30
|
+
const sim = titleSimilarity(proposal.title, existing);
|
|
31
|
+
if (sim >= similarityThreshold) {
|
|
32
|
+
return { isDuplicate: true, reason: `Similar (${Math.round(sim * 100)}%): "${existing}"` };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
for (const branch of openPrBranches) {
|
|
36
|
+
// Extract slug from branch name (e.g., 'promptwheel/tkt_abc123/fix-login-bug' -> 'fix login bug')
|
|
37
|
+
const branchTitle = branch.replace(/^promptwheel\/tkt_[a-z0-9]+\//, '').replace(/-/g, ' ');
|
|
38
|
+
if (branchTitle && titleSimilarity(proposal.title, branchTitle) >= similarityThreshold) {
|
|
39
|
+
return { isDuplicate: true, reason: `Open PR branch: ${branch}` };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return { isDuplicate: false };
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Get existing ticket titles and open PR branches for deduplication
|
|
46
|
+
*/
|
|
47
|
+
export async function getDeduplicationContext(adapter, projectId, repoRoot) {
|
|
48
|
+
const allTickets = await tickets.listByProject(adapter, projectId, {
|
|
49
|
+
limit: 200,
|
|
50
|
+
});
|
|
51
|
+
const existingTitles = allTickets
|
|
52
|
+
.filter(t => t.status !== 'ready')
|
|
53
|
+
.map(t => t.title);
|
|
54
|
+
let openPrBranches = [];
|
|
55
|
+
try {
|
|
56
|
+
const stdout = await gitExecFile('git', ['branch', '-r', '--list', 'origin/promptwheel/*'], {
|
|
57
|
+
cwd: repoRoot,
|
|
58
|
+
});
|
|
59
|
+
if (stdout) {
|
|
60
|
+
openPrBranches = stdout
|
|
61
|
+
.split('\n')
|
|
62
|
+
.map(b => b.trim().replace('origin/', ''))
|
|
63
|
+
.filter(Boolean);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Ignore git errors
|
|
68
|
+
}
|
|
69
|
+
return { existingTitles, openPrBranches };
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Determine adaptive parallel count based on ticket complexity.
|
|
73
|
+
* When --parallel is NOT explicitly set, scale based on proposal mix.
|
|
74
|
+
*/
|
|
75
|
+
export function getAdaptiveParallelCount(proposals) {
|
|
76
|
+
const heavy = proposals.filter(p => p.estimated_complexity === 'moderate' || p.estimated_complexity === 'complex').length;
|
|
77
|
+
const light = proposals.filter(p => p.estimated_complexity === 'trivial' || p.estimated_complexity === 'simple').length;
|
|
78
|
+
if (heavy === 0)
|
|
79
|
+
return 5; // all light → go wide
|
|
80
|
+
if (light === 0)
|
|
81
|
+
return 2; // all heavy → conservative
|
|
82
|
+
const ratio = light / (light + heavy);
|
|
83
|
+
return Math.max(2, Math.min(5, Math.round(2 + ratio * 3))); // 2-5
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=dedup.js.map
|