@proletariat/cli 0.3.94 → 0.3.96
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/dist/commands/agent/cleanup.d.ts +3 -4
- package/dist/commands/agent/cleanup.js +5 -4
- package/dist/commands/agent/cleanup.js.map +1 -1
- package/dist/commands/agent/gc.d.ts +3 -4
- package/dist/commands/agent/gc.js +5 -4
- package/dist/commands/agent/gc.js.map +1 -1
- package/dist/commands/agent/index.d.ts +3 -4
- package/dist/commands/agent/index.js +5 -4
- package/dist/commands/agent/index.js.map +1 -1
- package/dist/commands/agent/list.d.ts +3 -4
- package/dist/commands/agent/list.js +5 -4
- package/dist/commands/agent/list.js.map +1 -1
- package/dist/commands/agent/remove.d.ts +3 -4
- package/dist/commands/agent/remove.js +5 -4
- package/dist/commands/agent/remove.js.map +1 -1
- package/dist/commands/agent/staff/index.d.ts +3 -4
- package/dist/commands/agent/staff/index.js +5 -4
- package/dist/commands/agent/staff/index.js.map +1 -1
- package/dist/commands/agent/staff/remove.d.ts +3 -4
- package/dist/commands/agent/staff/remove.js +5 -4
- package/dist/commands/agent/staff/remove.js.map +1 -1
- package/dist/commands/agent/status.d.ts +3 -4
- package/dist/commands/agent/status.js +5 -4
- package/dist/commands/agent/status.js.map +1 -1
- package/dist/commands/agent/visit.d.ts +3 -4
- package/dist/commands/agent/visit.js +5 -4
- package/dist/commands/agent/visit.js.map +1 -1
- package/dist/commands/branch/create.js +1 -12
- package/dist/commands/branch/create.js.map +1 -1
- package/dist/commands/branch/list.d.ts +3 -4
- package/dist/commands/branch/list.js +5 -4
- package/dist/commands/branch/list.js.map +1 -1
- package/dist/commands/branch/validate.d.ts +3 -4
- package/dist/commands/branch/validate.js +6 -5
- package/dist/commands/branch/validate.js.map +1 -1
- package/dist/commands/branch/where.d.ts +3 -4
- package/dist/commands/branch/where.js +5 -4
- package/dist/commands/branch/where.js.map +1 -1
- package/dist/commands/commit.js +1 -1
- package/dist/commands/commit.js.map +1 -1
- package/dist/commands/config/index.js +1 -1
- package/dist/commands/config/index.js.map +1 -1
- package/dist/commands/db/backup.d.ts +16 -0
- package/dist/commands/db/backup.js +125 -0
- package/dist/commands/db/backup.js.map +1 -0
- package/dist/commands/db/repair.js +167 -45
- package/dist/commands/db/repair.js.map +1 -1
- package/dist/commands/execution/config.d.ts +3 -4
- package/dist/commands/execution/config.js +5 -4
- package/dist/commands/execution/config.js.map +1 -1
- package/dist/commands/execution/index.d.ts +3 -4
- package/dist/commands/execution/index.js +5 -4
- package/dist/commands/execution/index.js.map +1 -1
- package/dist/commands/execution/list.d.ts +3 -4
- package/dist/commands/execution/list.js +5 -4
- package/dist/commands/execution/list.js.map +1 -1
- package/dist/commands/execution/logs.d.ts +3 -4
- package/dist/commands/execution/logs.js +5 -4
- package/dist/commands/execution/logs.js.map +1 -1
- package/dist/commands/execution/stop.d.ts +3 -4
- package/dist/commands/execution/stop.js +5 -4
- package/dist/commands/execution/stop.js.map +1 -1
- package/dist/commands/execution/view.d.ts +3 -4
- package/dist/commands/execution/view.js +5 -4
- package/dist/commands/execution/view.js.map +1 -1
- package/dist/commands/gc.d.ts +13 -0
- package/dist/commands/gc.js +178 -0
- package/dist/commands/gc.js.map +1 -0
- package/dist/commands/hook/export.d.ts +3 -4
- package/dist/commands/hook/export.js +5 -4
- package/dist/commands/hook/export.js.map +1 -1
- package/dist/commands/hook/fire.d.ts +3 -4
- package/dist/commands/hook/fire.js +5 -4
- package/dist/commands/hook/fire.js.map +1 -1
- package/dist/commands/hook/list.d.ts +3 -4
- package/dist/commands/hook/list.js +5 -4
- package/dist/commands/hook/list.js.map +1 -1
- package/dist/commands/hook/preset.d.ts +3 -4
- package/dist/commands/hook/preset.js +5 -4
- package/dist/commands/hook/preset.js.map +1 -1
- package/dist/commands/orchestrate/index.d.ts +3 -4
- package/dist/commands/orchestrate/index.js +5 -4
- package/dist/commands/orchestrate/index.js.map +1 -1
- package/dist/commands/orchestrator/index.d.ts +3 -4
- package/dist/commands/orchestrator/index.js +5 -4
- package/dist/commands/orchestrator/index.js.map +1 -1
- package/dist/commands/pr/checks.d.ts +3 -4
- package/dist/commands/pr/checks.js +5 -4
- package/dist/commands/pr/checks.js.map +1 -1
- package/dist/commands/pr/merge.js +1 -1
- package/dist/commands/pr/merge.js.map +1 -1
- package/dist/commands/repo/add.d.ts +3 -4
- package/dist/commands/repo/add.js +5 -4
- package/dist/commands/repo/add.js.map +1 -1
- package/dist/commands/repo/create.d.ts +3 -4
- package/dist/commands/repo/create.js +5 -4
- package/dist/commands/repo/create.js.map +1 -1
- package/dist/commands/repo/fix-remotes.d.ts +3 -4
- package/dist/commands/repo/fix-remotes.js +5 -4
- package/dist/commands/repo/fix-remotes.js.map +1 -1
- package/dist/commands/repo/index.d.ts +3 -4
- package/dist/commands/repo/index.js +5 -4
- package/dist/commands/repo/index.js.map +1 -1
- package/dist/commands/repo/list.d.ts +3 -4
- package/dist/commands/repo/list.js +5 -4
- package/dist/commands/repo/list.js.map +1 -1
- package/dist/commands/session/attach.d.ts +3 -3
- package/dist/commands/session/attach.js +37 -79
- package/dist/commands/session/attach.js.map +1 -1
- package/dist/commands/session/cleanup.d.ts +3 -4
- package/dist/commands/session/cleanup.js +5 -4
- package/dist/commands/session/cleanup.js.map +1 -1
- package/dist/commands/session/create.d.ts +3 -4
- package/dist/commands/session/create.js +5 -4
- package/dist/commands/session/create.js.map +1 -1
- package/dist/commands/session/exec.d.ts +3 -4
- package/dist/commands/session/exec.js +6 -5
- package/dist/commands/session/exec.js.map +1 -1
- package/dist/commands/session/health.js +19 -1
- package/dist/commands/session/health.js.map +1 -1
- package/dist/commands/session/index.d.ts +3 -4
- package/dist/commands/session/index.js +9 -4
- package/dist/commands/session/index.js.map +1 -1
- package/dist/commands/session/inspect.d.ts +3 -4
- package/dist/commands/session/inspect.js +6 -5
- package/dist/commands/session/inspect.js.map +1 -1
- package/dist/commands/session/list.d.ts +4 -4
- package/dist/commands/session/list.js +129 -82
- package/dist/commands/session/list.js.map +1 -1
- package/dist/commands/session/peek.js +1 -1
- package/dist/commands/session/peek.js.map +1 -1
- package/dist/commands/session/poke.js +1 -1
- package/dist/commands/session/poke.js.map +1 -1
- package/dist/commands/session/prune.d.ts +3 -4
- package/dist/commands/session/prune.js +55 -15
- package/dist/commands/session/prune.js.map +1 -1
- package/dist/commands/session/report.d.ts +25 -5
- package/dist/commands/session/report.js +201 -5
- package/dist/commands/session/report.js.map +1 -1
- package/dist/commands/session/restart.d.ts +3 -4
- package/dist/commands/session/restart.js +6 -5
- package/dist/commands/session/restart.js.map +1 -1
- package/dist/commands/session/watch.d.ts +17 -0
- package/dist/commands/session/watch.js +139 -0
- package/dist/commands/session/watch.js.map +1 -0
- package/dist/commands/{action → sync}/index.d.ts +2 -6
- package/dist/commands/sync/index.js +68 -0
- package/dist/commands/sync/index.js.map +1 -0
- package/dist/commands/sync/pause.d.ts +10 -0
- package/dist/commands/sync/pause.js +35 -0
- package/dist/commands/sync/pause.js.map +1 -0
- package/dist/commands/sync/queue.d.ts +10 -0
- package/dist/commands/sync/queue.js +106 -0
- package/dist/commands/sync/queue.js.map +1 -0
- package/dist/commands/sync/resume.d.ts +10 -0
- package/dist/commands/sync/resume.js +34 -0
- package/dist/commands/sync/resume.js.map +1 -0
- package/dist/commands/sync/start.d.ts +11 -0
- package/dist/commands/sync/start.js +73 -0
- package/dist/commands/sync/start.js.map +1 -0
- package/dist/commands/sync/status.d.ts +10 -0
- package/dist/commands/sync/status.js +40 -0
- package/dist/commands/sync/status.js.map +1 -0
- package/dist/commands/sync/stop.d.ts +10 -0
- package/dist/commands/sync/stop.js +39 -0
- package/dist/commands/sync/stop.js.map +1 -0
- package/dist/commands/ticket/create.js +1 -1
- package/dist/commands/ticket/create.js.map +1 -1
- package/dist/commands/ticket/edit.js +1 -1
- package/dist/commands/ticket/edit.js.map +1 -1
- package/dist/commands/ticket/index.d.ts +3 -4
- package/dist/commands/ticket/index.js +5 -4
- package/dist/commands/ticket/index.js.map +1 -1
- package/dist/commands/ticket/list.js +1 -1
- package/dist/commands/ticket/list.js.map +1 -1
- package/dist/commands/ticket/update.js +1 -1
- package/dist/commands/ticket/update.js.map +1 -1
- package/dist/commands/web.d.ts +20 -0
- package/dist/commands/web.js +82 -0
- package/dist/commands/web.js.map +1 -0
- package/dist/commands/work/complete.js +1 -1
- package/dist/commands/work/complete.js.map +1 -1
- package/dist/commands/work/hooks/add.d.ts +3 -4
- package/dist/commands/work/hooks/add.js +5 -4
- package/dist/commands/work/hooks/add.js.map +1 -1
- package/dist/commands/work/hooks/list.d.ts +3 -4
- package/dist/commands/work/hooks/list.js +5 -4
- package/dist/commands/work/hooks/list.js.map +1 -1
- package/dist/commands/work/hooks/toggle.d.ts +3 -4
- package/dist/commands/work/hooks/toggle.js +5 -4
- package/dist/commands/work/hooks/toggle.js.map +1 -1
- package/dist/commands/work/peek.d.ts +3 -4
- package/dist/commands/work/peek.js +5 -4
- package/dist/commands/work/peek.js.map +1 -1
- package/dist/commands/work/propose.d.ts +23 -0
- package/dist/commands/work/propose.js +57 -0
- package/dist/commands/work/propose.js.map +1 -0
- package/dist/commands/work/ready.js +3 -3
- package/dist/commands/work/ready.js.map +1 -1
- package/dist/commands/work/rebase.d.ts +1 -1
- package/dist/commands/work/rebase.js +52 -49
- package/dist/commands/work/rebase.js.map +1 -1
- package/dist/commands/work/resolve.js +1 -1
- package/dist/commands/work/resolve.js.map +1 -1
- package/dist/commands/work/ship.d.ts +6 -0
- package/dist/commands/work/ship.js +218 -49
- package/dist/commands/work/ship.js.map +1 -1
- package/dist/commands/work/start.js +16 -90
- package/dist/commands/work/start.js.map +1 -1
- package/dist/commands/work/stop.d.ts +3 -4
- package/dist/commands/work/stop.js +5 -4
- package/dist/commands/work/stop.js.map +1 -1
- package/dist/hooks/init.js +11 -1
- package/dist/hooks/init.js.map +1 -1
- package/dist/lib/branch/index.d.ts +6 -5
- package/dist/lib/branch/index.js +8 -13
- package/dist/lib/branch/index.js.map +1 -1
- package/dist/lib/dashboard/data.js +1 -1
- package/dist/lib/dashboard/data.js.map +1 -1
- package/dist/lib/dashboard/html.d.ts +2 -1
- package/dist/lib/dashboard/html.js +150 -522
- package/dist/lib/dashboard/html.js.map +1 -1
- package/dist/lib/database/db-safety.d.ts +70 -9
- package/dist/lib/database/db-safety.js +395 -43
- package/dist/lib/database/db-safety.js.map +1 -1
- package/dist/lib/database/index.d.ts +1 -1
- package/dist/lib/database/index.js +1 -1
- package/dist/lib/database/index.js.map +1 -1
- package/dist/lib/database/migrations/0017_drop_agent_work_fk.d.ts +13 -0
- package/dist/lib/database/migrations/0017_drop_agent_work_fk.js +85 -0
- package/dist/lib/database/migrations/0017_drop_agent_work_fk.js.map +1 -0
- package/dist/lib/database/migrations/0018_create_ticket_refs.d.ts +11 -0
- package/dist/lib/database/migrations/0018_create_ticket_refs.js +40 -0
- package/dist/lib/database/migrations/0018_create_ticket_refs.js.map +1 -0
- package/dist/lib/database/migrations/index.js +4 -0
- package/dist/lib/database/migrations/index.js.map +1 -1
- package/dist/lib/database/workspace.js +3 -1
- package/dist/lib/database/workspace.js.map +1 -1
- package/dist/lib/execution/devcontainer.js +1 -1
- package/dist/lib/execution/devcontainer.js.map +1 -1
- package/dist/lib/execution/runners/docker-management.d.ts +9 -0
- package/dist/lib/execution/runners/docker-management.js +14 -3
- package/dist/lib/execution/runners/docker-management.js.map +1 -1
- package/dist/lib/execution/runners/docker.js +8 -0
- package/dist/lib/execution/runners/docker.js.map +1 -1
- package/dist/lib/execution/runners/prompt-builder.js +24 -9
- package/dist/lib/execution/runners/prompt-builder.js.map +1 -1
- package/dist/lib/execution/runners/shared.d.ts +1 -1
- package/dist/lib/execution/runners/shared.js +1 -1
- package/dist/lib/execution/runners/shared.js.map +1 -1
- package/dist/lib/execution/session-utils.d.ts +16 -0
- package/dist/lib/execution/session-utils.js +42 -0
- package/dist/lib/execution/session-utils.js.map +1 -1
- package/dist/lib/execution/spawner.js +4 -5
- package/dist/lib/execution/spawner.js.map +1 -1
- package/dist/lib/execution/storage.d.ts +29 -1
- package/dist/lib/execution/storage.js +79 -1
- package/dist/lib/execution/storage.js.map +1 -1
- package/dist/lib/execution/types.d.ts +17 -6
- package/dist/lib/execution/types.js +10 -7
- package/dist/lib/execution/types.js.map +1 -1
- package/dist/lib/external-issues/utils.d.ts +25 -0
- package/dist/lib/external-issues/utils.js +32 -0
- package/dist/lib/external-issues/utils.js.map +1 -0
- package/dist/lib/gc/index.d.ts +126 -0
- package/dist/lib/gc/index.js +410 -0
- package/dist/lib/gc/index.js.map +1 -0
- package/dist/lib/machine-config.d.ts +8 -0
- package/dist/lib/machine-config.js +37 -0
- package/dist/lib/machine-config.js.map +1 -1
- package/dist/lib/mcp/tools/action.js +1 -1
- package/dist/lib/mcp/tools/action.js.map +1 -1
- package/dist/lib/mcp/tools/ticket.js +1 -1
- package/dist/lib/mcp/tools/ticket.js.map +1 -1
- package/dist/lib/orchestrate/actions.js +30 -0
- package/dist/lib/orchestrate/actions.js.map +1 -1
- package/dist/lib/orchestrate/poller.d.ts +1 -1
- package/dist/lib/orchestrate/poller.js +4 -4
- package/dist/lib/orchestrate/poller.js.map +1 -1
- package/dist/lib/orchestrate/presets.js +2 -1
- package/dist/lib/orchestrate/presets.js.map +1 -1
- package/dist/lib/orchestrate/types.d.ts +1 -1
- package/dist/lib/orchestrate/types.js +1 -0
- package/dist/lib/orchestrate/types.js.map +1 -1
- package/dist/lib/pmo/index.js +1 -1
- package/dist/lib/pmo/index.js.map +1 -1
- package/dist/lib/pmo/markdown.js +1 -1
- package/dist/lib/pmo/markdown.js.map +1 -1
- package/dist/lib/pmo/schema.d.ts +3 -1
- package/dist/lib/pmo/schema.js +26 -2
- package/dist/lib/pmo/schema.js.map +1 -1
- package/dist/lib/pmo/storage/actions.js +1 -1
- package/dist/lib/pmo/storage/actions.js.map +1 -1
- package/dist/lib/pmo/storage/base.js +42 -18
- package/dist/lib/pmo/storage/base.js.map +1 -1
- package/dist/lib/pmo/storage/projects.js +2 -1
- package/dist/lib/pmo/storage/projects.js.map +1 -1
- package/dist/lib/pmo/storage/statuses.js +1 -1
- package/dist/lib/pmo/storage/statuses.js.map +1 -1
- package/dist/lib/pmo/storage/subtasks.js +1 -1
- package/dist/lib/pmo/storage/subtasks.js.map +1 -1
- package/dist/lib/pmo/storage/templates.js +1 -1
- package/dist/lib/pmo/storage/templates.js.map +1 -1
- package/dist/lib/pmo/storage/tickets.js +2 -1
- package/dist/lib/pmo/storage/tickets.js.map +1 -1
- package/dist/lib/pmo/storage/workflow-rules.js +1 -1
- package/dist/lib/pmo/storage/workflow-rules.js.map +1 -1
- package/dist/lib/pmo/utils.d.ts +6 -189
- package/dist/lib/pmo/utils.js +6 -306
- package/dist/lib/pmo/utils.js.map +1 -1
- package/dist/lib/providers/index.d.ts +1 -0
- package/dist/lib/providers/index.js +1 -0
- package/dist/lib/providers/index.js.map +1 -1
- package/dist/lib/providers/resolver.js +16 -0
- package/dist/lib/providers/resolver.js.map +1 -1
- package/dist/lib/providers/trello-provider.d.ts +28 -0
- package/dist/lib/providers/trello-provider.js +381 -0
- package/dist/lib/providers/trello-provider.js.map +1 -0
- package/dist/lib/session/heartbeat.d.ts +45 -0
- package/dist/lib/session/heartbeat.js +150 -0
- package/dist/lib/session/heartbeat.js.map +1 -0
- package/dist/lib/session/index.d.ts +7 -0
- package/dist/lib/session/index.js +8 -0
- package/dist/lib/session/index.js.map +1 -0
- package/dist/lib/session/watcher.d.ts +79 -0
- package/dist/lib/session/watcher.js +142 -0
- package/dist/lib/session/watcher.js.map +1 -0
- package/dist/lib/shipping/auto-merge.d.ts +57 -0
- package/dist/lib/shipping/auto-merge.js +311 -0
- package/dist/lib/shipping/auto-merge.js.map +1 -0
- package/dist/lib/shipping/github.d.ts +68 -0
- package/dist/lib/shipping/github.js +217 -0
- package/dist/lib/shipping/github.js.map +1 -0
- package/dist/lib/shipping/index.d.ts +13 -0
- package/dist/lib/shipping/index.js +11 -0
- package/dist/lib/shipping/index.js.map +1 -0
- package/dist/lib/shipping/rebase.d.ts +35 -0
- package/dist/lib/shipping/rebase.js +107 -0
- package/dist/lib/shipping/rebase.js.map +1 -0
- package/dist/lib/shipping/types.d.ts +181 -0
- package/dist/lib/shipping/types.js +9 -0
- package/dist/lib/shipping/types.js.map +1 -0
- package/dist/lib/sync/daemon-process.d.ts +9 -0
- package/dist/lib/sync/daemon-process.js +184 -0
- package/dist/lib/sync/daemon-process.js.map +1 -0
- package/dist/lib/sync/daemon.d.ts +39 -0
- package/dist/lib/sync/daemon.js +91 -0
- package/dist/lib/sync/daemon.js.map +1 -0
- package/dist/lib/sync/engine.d.ts +38 -0
- package/dist/lib/sync/engine.js +145 -0
- package/dist/lib/sync/engine.js.map +1 -0
- package/dist/lib/sync/merge-queue.d.ts +116 -0
- package/dist/lib/sync/merge-queue.js +321 -0
- package/dist/lib/sync/merge-queue.js.map +1 -0
- package/dist/lib/sync/reconciler.d.ts +44 -0
- package/dist/lib/sync/reconciler.js +88 -0
- package/dist/lib/sync/reconciler.js.map +1 -0
- package/dist/lib/trello/client.d.ts +1 -0
- package/dist/lib/trello/client.js +6 -0
- package/dist/lib/trello/client.js.map +1 -1
- package/dist/lib/utils/text.d.ts +44 -0
- package/dist/lib/utils/text.js +72 -0
- package/dist/lib/utils/text.js.map +1 -0
- package/dist/lib/work-lifecycle/post-execution.js +1 -1
- package/dist/lib/work-lifecycle/post-execution.js.map +1 -1
- package/dist/lib/work-lifecycle/settings.d.ts +138 -0
- package/dist/lib/work-lifecycle/settings.js +213 -0
- package/dist/lib/work-lifecycle/settings.js.map +1 -0
- package/oclif.manifest.json +2549 -2901
- package/package.json +8 -8
- package/dist/commands/action/create.d.ts +0 -26
- package/dist/commands/action/create.js +0 -245
- package/dist/commands/action/create.js.map +0 -1
- package/dist/commands/action/delete.d.ts +0 -18
- package/dist/commands/action/delete.js +0 -78
- package/dist/commands/action/delete.js.map +0 -1
- package/dist/commands/action/index.js +0 -103
- package/dist/commands/action/index.js.map +0 -1
- package/dist/commands/action/list.d.ts +0 -15
- package/dist/commands/action/list.js +0 -90
- package/dist/commands/action/list.js.map +0 -1
- package/dist/commands/action/run.d.ts +0 -20
- package/dist/commands/action/run.js +0 -188
- package/dist/commands/action/run.js.map +0 -1
- package/dist/commands/action/show.d.ts +0 -17
- package/dist/commands/action/show.js +0 -78
- package/dist/commands/action/show.js.map +0 -1
- package/dist/commands/action/update.d.ts +0 -27
- package/dist/commands/action/update.js +0 -288
- package/dist/commands/action/update.js.map +0 -1
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Daemon Process
|
|
3
|
+
*
|
|
4
|
+
* This is the background process spawned by `prlt sync start`.
|
|
5
|
+
* It runs a sync cycle and merge queue cycle on a configurable interval.
|
|
6
|
+
*
|
|
7
|
+
* Args: [hqPath, intervalSeconds]
|
|
8
|
+
*/
|
|
9
|
+
import * as fs from 'node:fs';
|
|
10
|
+
import { openWorkspaceDatabase } from '../database/index.js';
|
|
11
|
+
import { getPMOContext } from '../pmo/pmo-context.js';
|
|
12
|
+
import { runSyncCycle } from './engine.js';
|
|
13
|
+
import { runMergeQueueCycle } from './merge-queue.js';
|
|
14
|
+
import { removeDaemonPid, getDaemonLogPath } from './daemon.js';
|
|
15
|
+
import { GCScheduler, collectGCCandidates, executeGC, } from '../gc/index.js';
|
|
16
|
+
const args = process.argv.slice(2);
|
|
17
|
+
const hqPath = args[0];
|
|
18
|
+
const intervalSeconds = parseInt(args[1] || '60', 10);
|
|
19
|
+
if (!hqPath) {
|
|
20
|
+
console.error('Usage: daemon-process <hqPath> [intervalSeconds]');
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
const logPath = getDaemonLogPath(hqPath);
|
|
24
|
+
function log(msg) {
|
|
25
|
+
const timestamp = new Date().toISOString();
|
|
26
|
+
const line = `[${timestamp}] ${msg}\n`;
|
|
27
|
+
fs.appendFileSync(logPath, line);
|
|
28
|
+
}
|
|
29
|
+
async function getProjectId() {
|
|
30
|
+
const context = await getPMOContext();
|
|
31
|
+
const projects = await context.storage.listProjects();
|
|
32
|
+
await context.storage.close();
|
|
33
|
+
if (projects.length === 0) {
|
|
34
|
+
throw new Error('No projects found');
|
|
35
|
+
}
|
|
36
|
+
return projects[0].id;
|
|
37
|
+
}
|
|
38
|
+
// GC scheduler with 1-hour grace period — persists across daemon cycles
|
|
39
|
+
const gcScheduler = new GCScheduler(60 * 60 * 1000);
|
|
40
|
+
/** Map ticket priority strings to numeric values (lower = higher priority). */
|
|
41
|
+
function priorityToNumber(priority) {
|
|
42
|
+
switch (priority) {
|
|
43
|
+
case 'P0': return 0;
|
|
44
|
+
case 'P1': return 1;
|
|
45
|
+
case 'P2': return 2;
|
|
46
|
+
case 'P3': return 3;
|
|
47
|
+
default: return 3;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async function cycle() {
|
|
51
|
+
let db;
|
|
52
|
+
try {
|
|
53
|
+
db = openWorkspaceDatabase(hqPath);
|
|
54
|
+
const context = await getPMOContext();
|
|
55
|
+
const projectId = await getProjectId();
|
|
56
|
+
const storage = context.storage;
|
|
57
|
+
// Phase 1: Reconciliation — sync ticket state with PR state
|
|
58
|
+
const report = await runSyncCycle(db, storage, projectId, {
|
|
59
|
+
cwd: hqPath,
|
|
60
|
+
log,
|
|
61
|
+
});
|
|
62
|
+
if (report.applied.length > 0) {
|
|
63
|
+
log(`Applied ${report.applied.length} correction(s)`);
|
|
64
|
+
}
|
|
65
|
+
if (report.failed.length > 0) {
|
|
66
|
+
log(`Failed ${report.failed.length} correction(s)`);
|
|
67
|
+
}
|
|
68
|
+
// Phase 2: Merge Queue — sequential rebase-test-merge pipeline
|
|
69
|
+
try {
|
|
70
|
+
// Build priority map from tickets for queue ordering
|
|
71
|
+
const branchPriorities = new Map();
|
|
72
|
+
const startedTickets = await storage.listTickets(projectId, { statusCategory: 'started' });
|
|
73
|
+
for (const ticket of startedTickets) {
|
|
74
|
+
if (ticket.branch) {
|
|
75
|
+
branchPriorities.set(ticket.branch, priorityToNumber(ticket.priority));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const mqResult = runMergeQueueCycle({
|
|
79
|
+
hqPath,
|
|
80
|
+
cwd: hqPath,
|
|
81
|
+
log,
|
|
82
|
+
branchPriorities,
|
|
83
|
+
});
|
|
84
|
+
if (mqResult.action !== 'none' && mqResult.action !== 'paused') {
|
|
85
|
+
log(`Merge queue: ${mqResult.action}${mqResult.prNumber ? ` PR #${mqResult.prNumber}` : ''}${mqResult.reason ? ` — ${mqResult.reason}` : ''}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (mqErr) {
|
|
89
|
+
const mqMsg = mqErr instanceof Error ? mqErr.message : String(mqErr);
|
|
90
|
+
log(`Merge queue error: ${mqMsg}`);
|
|
91
|
+
}
|
|
92
|
+
// Phase 3: Garbage Collection — schedule cleanup for merged PRs, run ready cleanups
|
|
93
|
+
try {
|
|
94
|
+
// Schedule GC for any tickets that just moved to Done (PR merged)
|
|
95
|
+
const mergedActions = report.applied.filter(a => a.type === 'move_to_done');
|
|
96
|
+
if (mergedActions.length > 0) {
|
|
97
|
+
const completedTickets = await storage.listTickets(projectId, { statusCategory: 'completed' });
|
|
98
|
+
for (const action of mergedActions) {
|
|
99
|
+
const ticket = completedTickets.find(t => t.id === action.ticketId);
|
|
100
|
+
if (ticket?.branch) {
|
|
101
|
+
gcScheduler.schedule(ticket.branch);
|
|
102
|
+
log(`GC: Scheduled cleanup for branch ${ticket.branch} (1hr grace period)`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Process any ready cleanups (grace period expired)
|
|
107
|
+
const readyBranches = gcScheduler.getReady();
|
|
108
|
+
if (readyBranches.length > 0) {
|
|
109
|
+
log(`GC: Processing ${readyBranches.length} cleanup(s) past grace period`);
|
|
110
|
+
const candidates = collectGCCandidates({
|
|
111
|
+
hqPath,
|
|
112
|
+
staleDays: 7,
|
|
113
|
+
log,
|
|
114
|
+
});
|
|
115
|
+
// Only clean up the branches that are ready
|
|
116
|
+
const readySet = new Set(readyBranches);
|
|
117
|
+
const readyCandidates = candidates.filter(c => readySet.has(c.branch));
|
|
118
|
+
if (readyCandidates.length > 0) {
|
|
119
|
+
const result = executeGC(readyCandidates, {
|
|
120
|
+
hqPath,
|
|
121
|
+
execute: true,
|
|
122
|
+
log,
|
|
123
|
+
});
|
|
124
|
+
if (result.worktreesRemoved.length > 0) {
|
|
125
|
+
log(`GC: Removed ${result.worktreesRemoved.length} worktree(s), ${result.containersRemoved.length} container(s), ${result.branchesPruned.length} branch(es)`);
|
|
126
|
+
}
|
|
127
|
+
if (result.errors.length > 0) {
|
|
128
|
+
log(`GC: ${result.errors.length} error(s): ${result.errors.join('; ')}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Mark as completed regardless of whether cleanup was needed
|
|
132
|
+
for (const branch of readyBranches) {
|
|
133
|
+
gcScheduler.complete(branch);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
catch (gcErr) {
|
|
138
|
+
const gcMsg = gcErr instanceof Error ? gcErr.message : String(gcErr);
|
|
139
|
+
log(`GC error: ${gcMsg}`);
|
|
140
|
+
}
|
|
141
|
+
await context.storage.close();
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
145
|
+
log(`Error: ${msg}`);
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
if (db) {
|
|
149
|
+
try {
|
|
150
|
+
db.close();
|
|
151
|
+
}
|
|
152
|
+
catch { }
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// Signal handling
|
|
157
|
+
process.on('SIGTERM', () => {
|
|
158
|
+
log('Received SIGTERM — shutting down');
|
|
159
|
+
removeDaemonPid(hqPath);
|
|
160
|
+
process.exit(0);
|
|
161
|
+
});
|
|
162
|
+
process.on('SIGINT', () => {
|
|
163
|
+
log('Received SIGINT — shutting down');
|
|
164
|
+
removeDaemonPid(hqPath);
|
|
165
|
+
process.exit(0);
|
|
166
|
+
});
|
|
167
|
+
// Main loop
|
|
168
|
+
log(`Daemon started (PID ${process.pid}, interval ${intervalSeconds}s)`);
|
|
169
|
+
async function run() {
|
|
170
|
+
// Run initial cycle immediately
|
|
171
|
+
await cycle();
|
|
172
|
+
// Then schedule periodic runs
|
|
173
|
+
setInterval(() => {
|
|
174
|
+
cycle().catch(err => {
|
|
175
|
+
log(`Unhandled error in cycle: ${err}`);
|
|
176
|
+
});
|
|
177
|
+
}, intervalSeconds * 1000);
|
|
178
|
+
}
|
|
179
|
+
run().catch(err => {
|
|
180
|
+
log(`Fatal: ${err}`);
|
|
181
|
+
removeDaemonPid(hqPath);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
});
|
|
184
|
+
//# sourceMappingURL=daemon-process.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon-process.js","sourceRoot":"","sources":["../../../src/lib/sync/daemon-process.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync Daemon
|
|
3
|
+
*
|
|
4
|
+
* Manages a background process that periodically runs the sync engine.
|
|
5
|
+
* Uses a PID file in the HQ directory to track the daemon process.
|
|
6
|
+
*/
|
|
7
|
+
export interface DaemonInfo {
|
|
8
|
+
pid: number;
|
|
9
|
+
startedAt: string;
|
|
10
|
+
intervalSeconds: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Get the path to the daemon PID file.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getDaemonPidPath(hqPath: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Get the path to the daemon log file.
|
|
18
|
+
*/
|
|
19
|
+
export declare function getDaemonLogPath(hqPath: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Read daemon info from PID file.
|
|
22
|
+
*/
|
|
23
|
+
export declare function getDaemonInfo(hqPath: string): DaemonInfo | null;
|
|
24
|
+
/**
|
|
25
|
+
* Write daemon info to PID file.
|
|
26
|
+
*/
|
|
27
|
+
export declare function writeDaemonInfo(hqPath: string, info: DaemonInfo): void;
|
|
28
|
+
/**
|
|
29
|
+
* Remove daemon PID file.
|
|
30
|
+
*/
|
|
31
|
+
export declare function removeDaemonPid(hqPath: string): void;
|
|
32
|
+
/**
|
|
33
|
+
* Check if a daemon process is alive by PID.
|
|
34
|
+
*/
|
|
35
|
+
export declare function isDaemonRunning(hqPath: string): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Stop the daemon process.
|
|
38
|
+
*/
|
|
39
|
+
export declare function stopDaemon(hqPath: string): boolean;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync Daemon
|
|
3
|
+
*
|
|
4
|
+
* Manages a background process that periodically runs the sync engine.
|
|
5
|
+
* Uses a PID file in the HQ directory to track the daemon process.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'node:fs';
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
const DAEMON_PID_FILE = 'sync-daemon.pid';
|
|
10
|
+
const DAEMON_LOG_FILE = 'sync-daemon.log';
|
|
11
|
+
/**
|
|
12
|
+
* Get the path to the daemon PID file.
|
|
13
|
+
*/
|
|
14
|
+
export function getDaemonPidPath(hqPath) {
|
|
15
|
+
return path.join(hqPath, '.proletariat', DAEMON_PID_FILE);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Get the path to the daemon log file.
|
|
19
|
+
*/
|
|
20
|
+
export function getDaemonLogPath(hqPath) {
|
|
21
|
+
return path.join(hqPath, '.proletariat', DAEMON_LOG_FILE);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Read daemon info from PID file.
|
|
25
|
+
*/
|
|
26
|
+
export function getDaemonInfo(hqPath) {
|
|
27
|
+
const pidPath = getDaemonPidPath(hqPath);
|
|
28
|
+
try {
|
|
29
|
+
const content = fs.readFileSync(pidPath, 'utf-8');
|
|
30
|
+
return JSON.parse(content);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Write daemon info to PID file.
|
|
38
|
+
*/
|
|
39
|
+
export function writeDaemonInfo(hqPath, info) {
|
|
40
|
+
const pidPath = getDaemonPidPath(hqPath);
|
|
41
|
+
fs.writeFileSync(pidPath, JSON.stringify(info, null, 2));
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Remove daemon PID file.
|
|
45
|
+
*/
|
|
46
|
+
export function removeDaemonPid(hqPath) {
|
|
47
|
+
const pidPath = getDaemonPidPath(hqPath);
|
|
48
|
+
try {
|
|
49
|
+
fs.unlinkSync(pidPath);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// File may not exist
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Check if a daemon process is alive by PID.
|
|
57
|
+
*/
|
|
58
|
+
export function isDaemonRunning(hqPath) {
|
|
59
|
+
const info = getDaemonInfo(hqPath);
|
|
60
|
+
if (!info)
|
|
61
|
+
return false;
|
|
62
|
+
try {
|
|
63
|
+
// Sending signal 0 checks if process exists without killing it
|
|
64
|
+
process.kill(info.pid, 0);
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// Process doesn't exist — clean up stale PID file
|
|
69
|
+
removeDaemonPid(hqPath);
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Stop the daemon process.
|
|
75
|
+
*/
|
|
76
|
+
export function stopDaemon(hqPath) {
|
|
77
|
+
const info = getDaemonInfo(hqPath);
|
|
78
|
+
if (!info)
|
|
79
|
+
return false;
|
|
80
|
+
try {
|
|
81
|
+
process.kill(info.pid, 'SIGTERM');
|
|
82
|
+
removeDaemonPid(hqPath);
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// Process may already be dead
|
|
87
|
+
removeDaemonPid(hqPath);
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=daemon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../../src/lib/sync/daemon.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAA;AAC7B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AAEjC,MAAM,eAAe,GAAG,iBAAiB,CAAA;AACzC,MAAM,eAAe,GAAG,iBAAiB,CAAA;AAQzC;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,EAAE,eAAe,CAAC,CAAA;AAC3D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,EAAE,eAAe,CAAC,CAAA;AAC3D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,MAAM,OAAO,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAA;IACxC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QACjD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAe,CAAA;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,IAAgB;IAC9D,MAAM,OAAO,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAA;IACxC,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;AAC1D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,MAAc;IAC5C,MAAM,OAAO,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAA;IACxC,IAAI,CAAC;QACH,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAA;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,qBAAqB;IACvB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,MAAc;IAC5C,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,CAAA;IAClC,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAA;IAEvB,IAAI,CAAC;QACH,+DAA+D;QAC/D,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;QACzB,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,MAAM,CAAC;QACP,kDAAkD;QAClD,eAAe,CAAC,MAAM,CAAC,CAAA;QACvB,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,MAAc;IACvC,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,CAAA;IAClC,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAA;IAEvB,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAA;QACjC,eAAe,CAAC,MAAM,CAAC,CAAA;QACvB,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,MAAM,CAAC;QACP,8BAA8B;QAC9B,eAAe,CAAC,MAAM,CAAC,CAAA;QACvB,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync Engine
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates the reconciliation process: gathers ticket state and PR state,
|
|
5
|
+
* runs the reconciler, and applies corrective actions through providers.
|
|
6
|
+
*
|
|
7
|
+
* This is the "run once" engine. The daemon wraps this with a polling loop.
|
|
8
|
+
*/
|
|
9
|
+
import type Database from 'better-sqlite3';
|
|
10
|
+
import type { ProviderStorage } from '../providers/types.js';
|
|
11
|
+
import type { PMOStorage } from '../pmo/types.js';
|
|
12
|
+
import { type ReconcileAction, type ReconcileResult } from './reconciler.js';
|
|
13
|
+
export interface SyncEngineOptions {
|
|
14
|
+
/** Working directory for git/gh operations */
|
|
15
|
+
cwd?: string;
|
|
16
|
+
/** Callback for logging */
|
|
17
|
+
log?: (msg: string) => void;
|
|
18
|
+
/** If true, don't actually move tickets — just report what would happen */
|
|
19
|
+
dryRun?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface SyncReport {
|
|
22
|
+
result: ReconcileResult;
|
|
23
|
+
applied: ReconcileAction[];
|
|
24
|
+
skipped: ReconcileAction[];
|
|
25
|
+
failed: Array<{
|
|
26
|
+
action: ReconcileAction;
|
|
27
|
+
error: string;
|
|
28
|
+
}>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Run a single reconciliation cycle.
|
|
32
|
+
*
|
|
33
|
+
* 1. List all tickets in started/review states
|
|
34
|
+
* 2. For each, check PR status
|
|
35
|
+
* 3. Run reconciler to determine actions
|
|
36
|
+
* 4. Apply actions through providers
|
|
37
|
+
*/
|
|
38
|
+
export declare function runSyncCycle(db: Database.Database, storage: PMOStorage & ProviderStorage, projectId: string, options?: SyncEngineOptions): Promise<SyncReport>;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync Engine
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates the reconciliation process: gathers ticket state and PR state,
|
|
5
|
+
* runs the reconciler, and applies corrective actions through providers.
|
|
6
|
+
*
|
|
7
|
+
* This is the "run once" engine. The daemon wraps this with a polling loop.
|
|
8
|
+
*/
|
|
9
|
+
import { resolveTicketProvider } from '../providers/resolver.js';
|
|
10
|
+
import { ExecutionStorage } from '../execution/storage.js';
|
|
11
|
+
import { getPRForBranch, getPRChecks, listOpenPRs, } from '../pr/index.js';
|
|
12
|
+
import { reconcileTicket, } from './reconciler.js';
|
|
13
|
+
// =============================================================================
|
|
14
|
+
// Engine
|
|
15
|
+
// =============================================================================
|
|
16
|
+
/**
|
|
17
|
+
* Run a single reconciliation cycle.
|
|
18
|
+
*
|
|
19
|
+
* 1. List all tickets in started/review states
|
|
20
|
+
* 2. For each, check PR status
|
|
21
|
+
* 3. Run reconciler to determine actions
|
|
22
|
+
* 4. Apply actions through providers
|
|
23
|
+
*/
|
|
24
|
+
export async function runSyncCycle(db, storage, projectId, options = {}) {
|
|
25
|
+
const { cwd, log, dryRun } = options;
|
|
26
|
+
const execStorage = new ExecutionStorage(db);
|
|
27
|
+
// Gather all tickets in active states (started = In Progress / In Review)
|
|
28
|
+
const startedTickets = await storage.listTickets(projectId, { statusCategory: 'started' });
|
|
29
|
+
// Also check recently completed tickets to catch any that were manually moved
|
|
30
|
+
// but whose PR state doesn't match
|
|
31
|
+
log?.(`Checking ${startedTickets.length} active ticket(s)...`);
|
|
32
|
+
// Pre-fetch open PRs to reduce API calls
|
|
33
|
+
const openPRs = listOpenPRs(cwd);
|
|
34
|
+
const prByBranch = new Map();
|
|
35
|
+
for (const pr of openPRs) {
|
|
36
|
+
prByBranch.set(pr.headBranch, pr);
|
|
37
|
+
}
|
|
38
|
+
const actions = [];
|
|
39
|
+
const errors = [];
|
|
40
|
+
for (const ticket of startedTickets) {
|
|
41
|
+
try {
|
|
42
|
+
const ctx = buildContext(ticket, prByBranch, execStorage, cwd);
|
|
43
|
+
const action = reconcileTicket(ctx);
|
|
44
|
+
if (action) {
|
|
45
|
+
actions.push(action);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
50
|
+
errors.push({ ticketId: ticket.id, error: msg });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const result = {
|
|
54
|
+
actions,
|
|
55
|
+
checked: startedTickets.length,
|
|
56
|
+
errors,
|
|
57
|
+
};
|
|
58
|
+
// Apply actions
|
|
59
|
+
const applied = [];
|
|
60
|
+
const skipped = [];
|
|
61
|
+
const failed = [];
|
|
62
|
+
for (const action of actions) {
|
|
63
|
+
if (dryRun) {
|
|
64
|
+
skipped.push(action);
|
|
65
|
+
log?.(`[dry-run] Would ${formatAction(action)}`);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (action.type === 'flag_stale') {
|
|
69
|
+
// Stale tickets are logged but not moved
|
|
70
|
+
log?.(`Stale: ${action.ticketId} — ${action.reason}`);
|
|
71
|
+
applied.push(action);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
const ticket = await storage.getTicket(action.ticketId);
|
|
76
|
+
if (!ticket) {
|
|
77
|
+
failed.push({ action, error: 'Ticket not found' });
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const provider = resolveTicketProvider(action.ticketId, ticket.projectId ?? projectId, db, storage, ticket.metadata);
|
|
81
|
+
if (action.targetStatus) {
|
|
82
|
+
const moveResult = await provider.moveTicket(action.ticketId, action.targetStatus);
|
|
83
|
+
if (moveResult.success) {
|
|
84
|
+
log?.(`Synced: ${action.ticketId} → ${action.targetStatus} (${action.reason})`);
|
|
85
|
+
applied.push(action);
|
|
86
|
+
// If moving to Done, also mark any running executions as completed
|
|
87
|
+
if (action.type === 'move_to_done') {
|
|
88
|
+
markExecutionsCompleted(execStorage, action.ticketId);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
failed.push({ action, error: moveResult.error ?? 'Move failed' });
|
|
93
|
+
log?.(`Failed: ${action.ticketId} — ${moveResult.error}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
99
|
+
failed.push({ action, error: msg });
|
|
100
|
+
log?.(`Error: ${action.ticketId} — ${msg}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return { result, applied, skipped, failed };
|
|
104
|
+
}
|
|
105
|
+
// =============================================================================
|
|
106
|
+
// Helpers
|
|
107
|
+
// =============================================================================
|
|
108
|
+
function buildContext(ticket, prByBranch, execStorage, cwd) {
|
|
109
|
+
// Look up PR by branch — try ticket.branch first, then external key patterns
|
|
110
|
+
let pr = null;
|
|
111
|
+
let checks = [];
|
|
112
|
+
if (ticket.branch) {
|
|
113
|
+
// First check the pre-fetched open PRs
|
|
114
|
+
pr = prByBranch.get(ticket.branch) ?? null;
|
|
115
|
+
// If not found in open PRs, the PR might be merged/closed — fetch directly
|
|
116
|
+
if (!pr) {
|
|
117
|
+
pr = getPRForBranch(ticket.branch, cwd);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (pr && pr.state === 'OPEN') {
|
|
121
|
+
checks = getPRChecks(pr.number, cwd);
|
|
122
|
+
}
|
|
123
|
+
// Check for active execution
|
|
124
|
+
const hasActiveExecution = !!execStorage.getRunningExecution(ticket.id);
|
|
125
|
+
return { ticket, pr, checks, hasActiveExecution };
|
|
126
|
+
}
|
|
127
|
+
function markExecutionsCompleted(execStorage, ticketId) {
|
|
128
|
+
const running = execStorage.getRunningExecution(ticketId);
|
|
129
|
+
if (running) {
|
|
130
|
+
execStorage.updateStatus(running.id, 'completed');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function formatAction(action) {
|
|
134
|
+
switch (action.type) {
|
|
135
|
+
case 'move_to_done':
|
|
136
|
+
return `move ${action.ticketId} to Done (${action.reason})`;
|
|
137
|
+
case 'move_to_review':
|
|
138
|
+
return `move ${action.ticketId} to Review (${action.reason})`;
|
|
139
|
+
case 'move_to_backlog':
|
|
140
|
+
return `move ${action.ticketId} to Backlog (${action.reason})`;
|
|
141
|
+
case 'flag_stale':
|
|
142
|
+
return `flag ${action.ticketId} as stale (${action.reason})`;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../../src/lib/sync/engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge Queue
|
|
3
|
+
*
|
|
4
|
+
* Sequential rebase-test-merge pipeline for parallel agent PRs.
|
|
5
|
+
*
|
|
6
|
+
* With strict branch protection (PR must be up-to-date with main), parallel
|
|
7
|
+
* agent PRs create a rebase treadmill. The merge queue processes green PRs
|
|
8
|
+
* one at a time: rebase on main → wait for CI → merge if green → eject if red.
|
|
9
|
+
*
|
|
10
|
+
* State machine per cycle:
|
|
11
|
+
* 1. If paused → skip
|
|
12
|
+
* 2. If a PR is currently being processed (rebased, waiting for CI):
|
|
13
|
+
* - CI green → merge, rebase siblings
|
|
14
|
+
* - CI failed → eject (label + comment)
|
|
15
|
+
* - CI pending → wait
|
|
16
|
+
* 3. If no current PR:
|
|
17
|
+
* - Build queue: open, non-draft PRs with green CI, ordered by priority then age
|
|
18
|
+
* - Pick first → rebase → set as current
|
|
19
|
+
* - If rebase fails (conflicts) → eject, try next
|
|
20
|
+
*/
|
|
21
|
+
import type { PRInfo } from '../pr/index.js';
|
|
22
|
+
import { listOpenPRs, getPRChecks, getPRByNumber, mergePR } from '../pr/index.js';
|
|
23
|
+
import { rebaseSiblingPRs } from '../shipping/rebase.js';
|
|
24
|
+
export interface MergeQueueState {
|
|
25
|
+
/** Whether the merge queue is paused */
|
|
26
|
+
paused: boolean;
|
|
27
|
+
/** ISO timestamp when paused */
|
|
28
|
+
pausedAt: string | null;
|
|
29
|
+
/** PR currently being processed (rebased, waiting for CI) */
|
|
30
|
+
currentPR: CurrentPRState | null;
|
|
31
|
+
/** Last PR that was successfully merged */
|
|
32
|
+
lastMergedPR: MergedPRInfo | null;
|
|
33
|
+
/** ISO timestamp of last cycle */
|
|
34
|
+
lastCycleAt: string | null;
|
|
35
|
+
/** Recently ejected PRs (max 10) */
|
|
36
|
+
ejectedPRs: EjectedPRInfo[];
|
|
37
|
+
}
|
|
38
|
+
export interface CurrentPRState {
|
|
39
|
+
prNumber: number;
|
|
40
|
+
headBranch: string;
|
|
41
|
+
baseBranch: string;
|
|
42
|
+
ticketId: string | null;
|
|
43
|
+
priority: number;
|
|
44
|
+
rebasedAt: string;
|
|
45
|
+
}
|
|
46
|
+
export interface MergedPRInfo {
|
|
47
|
+
prNumber: number;
|
|
48
|
+
headBranch: string;
|
|
49
|
+
mergedAt: string;
|
|
50
|
+
}
|
|
51
|
+
export interface EjectedPRInfo {
|
|
52
|
+
prNumber: number;
|
|
53
|
+
headBranch: string;
|
|
54
|
+
reason: string;
|
|
55
|
+
ejectedAt: string;
|
|
56
|
+
}
|
|
57
|
+
export interface MergeQueueEntry {
|
|
58
|
+
pr: PRInfo;
|
|
59
|
+
ticketId: string | null;
|
|
60
|
+
priority: number;
|
|
61
|
+
}
|
|
62
|
+
export interface MergeQueueCycleResult {
|
|
63
|
+
action: 'none' | 'rebase_started' | 'waiting_ci' | 'merged' | 'ejected' | 'paused' | 'error';
|
|
64
|
+
prNumber?: number;
|
|
65
|
+
reason?: string;
|
|
66
|
+
queueSize: number;
|
|
67
|
+
}
|
|
68
|
+
export interface MergeQueueOptions {
|
|
69
|
+
/** HQ path for state file */
|
|
70
|
+
hqPath: string;
|
|
71
|
+
/** Working directory for git/gh operations */
|
|
72
|
+
cwd?: string;
|
|
73
|
+
/** Logging callback */
|
|
74
|
+
log?: (msg: string) => void;
|
|
75
|
+
/** Merge method (default: squash) */
|
|
76
|
+
mergeMethod?: 'merge' | 'squash' | 'rebase';
|
|
77
|
+
/** Delete branch after merging (default: true) */
|
|
78
|
+
deleteBranch?: boolean;
|
|
79
|
+
/** Map of PR head branch to priority (0=P0, 1=P1, 2=P2, 3=P3). Lower = higher priority */
|
|
80
|
+
branchPriorities?: Map<string, number>;
|
|
81
|
+
}
|
|
82
|
+
/** Injectable dependencies for testing */
|
|
83
|
+
export interface MergeQueueDeps {
|
|
84
|
+
listOpenPRs: typeof listOpenPRs;
|
|
85
|
+
getPRChecks: typeof getPRChecks;
|
|
86
|
+
getPRByNumber: typeof getPRByNumber;
|
|
87
|
+
mergePR: typeof mergePR;
|
|
88
|
+
rebaseSiblingPRs: typeof rebaseSiblingPRs;
|
|
89
|
+
updatePRBranch: (prNumber: number, cwd?: string) => import('../shipping/types.js').UpdateBranchResult;
|
|
90
|
+
addLabel: (prNumber: number, label: string, cwd?: string) => boolean;
|
|
91
|
+
addComment: (prNumber: number, body: string, cwd?: string) => boolean;
|
|
92
|
+
}
|
|
93
|
+
export declare function getMergeQueueStatePath(hqPath: string): string;
|
|
94
|
+
export declare function getDefaultState(): MergeQueueState;
|
|
95
|
+
export declare function getMergeQueueState(hqPath: string): MergeQueueState;
|
|
96
|
+
export declare function saveMergeQueueState(hqPath: string, state: MergeQueueState): void;
|
|
97
|
+
export declare function isMergeQueuePaused(hqPath: string): boolean;
|
|
98
|
+
export declare function pauseMergeQueue(hqPath: string): MergeQueueState;
|
|
99
|
+
export declare function resumeMergeQueue(hqPath: string): MergeQueueState;
|
|
100
|
+
/**
|
|
101
|
+
* Build the merge queue: open, non-draft PRs with all CI checks green,
|
|
102
|
+
* ordered by priority (P0 first) then by creation date (oldest first).
|
|
103
|
+
*/
|
|
104
|
+
export declare function buildMergeQueue(options: {
|
|
105
|
+
cwd?: string;
|
|
106
|
+
branchPriorities?: Map<string, number>;
|
|
107
|
+
}, deps?: Pick<MergeQueueDeps, 'listOpenPRs' | 'getPRChecks'>): MergeQueueEntry[];
|
|
108
|
+
/**
|
|
109
|
+
* Run a single merge queue cycle.
|
|
110
|
+
*
|
|
111
|
+
* Called by the daemon on each interval. Handles the state machine:
|
|
112
|
+
* - If paused, returns immediately
|
|
113
|
+
* - If a PR is in progress (rebased, waiting CI), checks status
|
|
114
|
+
* - If no PR in progress, picks next from queue and rebases
|
|
115
|
+
*/
|
|
116
|
+
export declare function runMergeQueueCycle(options: MergeQueueOptions, deps?: MergeQueueDeps): MergeQueueCycleResult;
|