@proletariat/cli 0.3.110 → 0.3.112

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.
Files changed (86) hide show
  1. package/dist/commands/gateway/connect.d.ts +33 -0
  2. package/dist/commands/gateway/connect.js +130 -0
  3. package/dist/commands/gateway/connect.js.map +1 -0
  4. package/dist/commands/gateway/disconnect.d.ts +21 -0
  5. package/dist/commands/gateway/disconnect.js +69 -0
  6. package/dist/commands/gateway/disconnect.js.map +1 -0
  7. package/dist/commands/gateway/start.d.ts +23 -0
  8. package/dist/commands/gateway/start.js +133 -0
  9. package/dist/commands/gateway/start.js.map +1 -0
  10. package/dist/commands/gateway/status.d.ts +16 -0
  11. package/dist/commands/gateway/status.js +76 -0
  12. package/dist/commands/gateway/status.js.map +1 -0
  13. package/dist/commands/gateway/test.d.ts +22 -0
  14. package/dist/commands/gateway/test.js +83 -0
  15. package/dist/commands/gateway/test.js.map +1 -0
  16. package/dist/commands/orchestrator/attach.d.ts +2 -0
  17. package/dist/commands/orchestrator/attach.js +80 -118
  18. package/dist/commands/orchestrator/attach.js.map +1 -1
  19. package/dist/commands/orchestrator/start.js +21 -0
  20. package/dist/commands/orchestrator/start.js.map +1 -1
  21. package/dist/commands/orchestrator/status.d.ts +3 -0
  22. package/dist/commands/orchestrator/status.js +104 -130
  23. package/dist/commands/orchestrator/status.js.map +1 -1
  24. package/dist/commands/orchestrator/stop.d.ts +2 -0
  25. package/dist/commands/orchestrator/stop.js +105 -107
  26. package/dist/commands/orchestrator/stop.js.map +1 -1
  27. package/dist/commands/reconcile.d.ts +29 -0
  28. package/dist/commands/reconcile.js +140 -0
  29. package/dist/commands/reconcile.js.map +1 -0
  30. package/dist/commands/session/attach.d.ts +2 -6
  31. package/dist/commands/session/attach.js +68 -97
  32. package/dist/commands/session/attach.js.map +1 -1
  33. package/dist/commands/session/list.d.ts +4 -1
  34. package/dist/commands/session/list.js +160 -326
  35. package/dist/commands/session/list.js.map +1 -1
  36. package/dist/commands/work/ship.js +131 -61
  37. package/dist/commands/work/ship.js.map +1 -1
  38. package/dist/commands/work/start.js +104 -49
  39. package/dist/commands/work/start.js.map +1 -1
  40. package/dist/lib/execution/session-utils.d.ts +4 -1
  41. package/dist/lib/execution/session-utils.js +3 -0
  42. package/dist/lib/execution/session-utils.js.map +1 -1
  43. package/dist/lib/gateway/channel-factory.d.ts +13 -0
  44. package/dist/lib/gateway/channel-factory.js +37 -0
  45. package/dist/lib/gateway/channel-factory.js.map +1 -0
  46. package/dist/lib/gateway/channels/telegram.d.ts +115 -0
  47. package/dist/lib/gateway/channels/telegram.js +215 -0
  48. package/dist/lib/gateway/channels/telegram.js.map +1 -0
  49. package/dist/lib/gateway/router.d.ts +84 -0
  50. package/dist/lib/gateway/router.js +140 -0
  51. package/dist/lib/gateway/router.js.map +1 -0
  52. package/dist/lib/gateway/session-poker.d.ts +35 -0
  53. package/dist/lib/gateway/session-poker.js +85 -0
  54. package/dist/lib/gateway/session-poker.js.map +1 -0
  55. package/dist/lib/gateway/types.d.ts +124 -0
  56. package/dist/lib/gateway/types.js +17 -0
  57. package/dist/lib/gateway/types.js.map +1 -0
  58. package/dist/lib/machine-db-mirror.d.ts +64 -0
  59. package/dist/lib/machine-db-mirror.js +82 -0
  60. package/dist/lib/machine-db-mirror.js.map +1 -0
  61. package/dist/lib/machine-db.d.ts +98 -0
  62. package/dist/lib/machine-db.js +152 -0
  63. package/dist/lib/machine-db.js.map +1 -1
  64. package/dist/lib/orchestrate/prompt-chain.d.ts +19 -4
  65. package/dist/lib/orchestrate/prompt-chain.js +19 -4
  66. package/dist/lib/orchestrate/prompt-chain.js.map +1 -1
  67. package/dist/lib/pr/index.d.ts +34 -2
  68. package/dist/lib/pr/index.js +95 -4
  69. package/dist/lib/pr/index.js.map +1 -1
  70. package/dist/lib/reconcile/core.d.ts +62 -0
  71. package/dist/lib/reconcile/core.js +137 -0
  72. package/dist/lib/reconcile/core.js.map +1 -0
  73. package/dist/lib/reconcile/index.d.ts +54 -0
  74. package/dist/lib/reconcile/index.js +377 -0
  75. package/dist/lib/reconcile/index.js.map +1 -0
  76. package/dist/lib/reconcile/types.d.ts +133 -0
  77. package/dist/lib/reconcile/types.js +16 -0
  78. package/dist/lib/reconcile/types.js.map +1 -0
  79. package/dist/lib/session/renderer.d.ts +121 -0
  80. package/dist/lib/session/renderer.js +547 -0
  81. package/dist/lib/session/renderer.js.map +1 -0
  82. package/dist/lib/update-check.d.ts +64 -7
  83. package/dist/lib/update-check.js +164 -20
  84. package/dist/lib/update-check.js.map +1 -1
  85. package/oclif.manifest.json +1203 -750
  86. package/package.json +1 -1
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Tier 2 State Reconciler — Pure core (PRLT-1280)
3
+ *
4
+ * This module holds the deterministic, provider-agnostic, side-effect-free
5
+ * logic of the reconciler. It answers a single question:
6
+ *
7
+ * Given a ticket and the live state of its linked PR(s), what target
8
+ * column should the ticket be in, and is that different from its
9
+ * current column?
10
+ *
11
+ * Splitting this out from the IO-heavy runner keeps the rules trivially
12
+ * unit-testable without spinning up a database or the gh CLI.
13
+ */
14
+ /**
15
+ * Derive the expected state for a ticket given its linked PRs.
16
+ *
17
+ * Rules (mirrors the Tier 2 spec in PRLT-1280):
18
+ * 1. Any linked PR is MERGED → ticket belongs in Done
19
+ * 2. Ticket is currently in "review" category AND every open PR has been
20
+ * CLOSED without merging → move back to In Progress so humans can
21
+ * resume work. (Rule 4 in the existing sync reconciler uses Backlog,
22
+ * but PRLT-1280 explicitly asks for "back to In Progress or a review
23
+ * queue" — In Progress matches the active work better than Backlog.)
24
+ * 3. Otherwise: no expected transition.
25
+ *
26
+ * `null` means "leave the ticket alone". The reconciler MUST treat `null`
27
+ * as the idempotent path — that's how "second run is a no-op" works.
28
+ */
29
+ export function deriveExpectedState(ticket, linkedPRs, config) {
30
+ const category = ticket.statusCategory;
31
+ const doneStatus = config?.done ?? 'Done';
32
+ const inProgressStatus = config?.in_progress ?? 'In Progress';
33
+ // Terminal states are load-bearing: we never pull a ticket back out of
34
+ // Done or Canceled. A human may have marked it closed for a reason.
35
+ if (category === 'completed' || category === 'canceled') {
36
+ return null;
37
+ }
38
+ // Rule 1: any merged PR wins. Even if another linked PR is still open,
39
+ // a merged PR means the ticket's work shipped.
40
+ const merged = linkedPRs.find(lp => lp.pr.state === 'MERGED');
41
+ if (merged) {
42
+ // Idempotency: if the ticket is already sitting in the done column
43
+ // name we resolved, there is nothing to do.
44
+ if (statusMatches(ticket.statusName, doneStatus)) {
45
+ return null;
46
+ }
47
+ return {
48
+ targetState: doneStatus,
49
+ reason: `GitHub PR #${merged.pr.number} is MERGED (merged upstream), ` +
50
+ `expected ${doneStatus}, was ${ticket.statusName ?? category ?? 'unknown'}`,
51
+ driver: merged,
52
+ };
53
+ }
54
+ // Rule 2: ticket is in review-land, but every linked PR is closed without
55
+ // merging. Pull it back to In Progress so it gets picked up again.
56
+ const hasLinkedPRs = linkedPRs.length > 0;
57
+ const allClosedNoMerge = hasLinkedPRs && linkedPRs.every(lp => lp.pr.state === 'CLOSED');
58
+ const inReview = category === 'started' && isReviewStatus(ticket.statusName, config);
59
+ if (allClosedNoMerge && inReview) {
60
+ if (statusMatches(ticket.statusName, inProgressStatus)) {
61
+ return null;
62
+ }
63
+ const closed = linkedPRs[0];
64
+ return {
65
+ targetState: inProgressStatus,
66
+ reason: `GitHub PR #${closed.pr.number} is CLOSED without merging, ` +
67
+ `expected ${inProgressStatus}, was ${ticket.statusName ?? category ?? 'unknown'}`,
68
+ driver: closed,
69
+ };
70
+ }
71
+ return null;
72
+ }
73
+ /**
74
+ * Build a {@link ReconcileTransition} record from a derived expected-state
75
+ * decision. Factored out so the runner and the tests both produce the
76
+ * same transition shape.
77
+ */
78
+ export function buildTransition(params) {
79
+ const { ticket, provider, targetState, reason, driver, now } = params;
80
+ return {
81
+ ticketId: ticket.id,
82
+ ticketTitle: ticket.title ?? '',
83
+ projectId: ticket.projectId ?? '',
84
+ provider,
85
+ fromState: ticket.statusName ?? null,
86
+ toState: targetState,
87
+ prNumber: driver?.pr.number,
88
+ prState: driver?.pr.state,
89
+ reason,
90
+ decidedAt: (now ?? new Date()).toISOString(),
91
+ };
92
+ }
93
+ /**
94
+ * Format a transition for the structured log line required by the AC.
95
+ * The format is stable so scripts and tests can grep on it.
96
+ *
97
+ * Example:
98
+ * [reconcile] TKT-142: In Review → Done | PR #321 MERGED @ 2026-04-08T00:00:00Z | reason: ...
99
+ */
100
+ export function formatTransitionLogLine(t) {
101
+ const prPart = t.prNumber !== undefined
102
+ ? `PR #${t.prNumber} ${t.prState ?? ''}`.trim() + ` @ ${t.decidedAt}`
103
+ : `no PR @ ${t.decidedAt}`;
104
+ const from = t.fromState ?? 'unknown';
105
+ return `[reconcile] ${t.ticketId}: ${from} → ${t.toState} | ${prPart} | reason: ${t.reason}`;
106
+ }
107
+ // -----------------------------------------------------------------------------
108
+ // helpers
109
+ // -----------------------------------------------------------------------------
110
+ function statusMatches(current, target) {
111
+ if (!current)
112
+ return false;
113
+ return current.toLowerCase() === target.toLowerCase();
114
+ }
115
+ /**
116
+ * Match the existing sync reconciler's notion of "review status" so the
117
+ * Tier 2 reconciler treats the same columns as review that Tier 1 does.
118
+ * When a WorkflowConfig is provided, matches exactly against the
119
+ * configured review column; otherwise falls back to substring matching.
120
+ */
121
+ function isReviewStatus(statusName, config) {
122
+ const name = (statusName ?? '').toLowerCase();
123
+ if (!name)
124
+ return false;
125
+ if (config?.review) {
126
+ return name === config.review.toLowerCase();
127
+ }
128
+ return name.includes('review');
129
+ }
130
+ /**
131
+ * Re-export for callers that want a direct PRInfo tuple without the
132
+ * LinkedPR wrapper.
133
+ */
134
+ export function toLinkedPR(pr, source) {
135
+ return { pr, source };
136
+ }
137
+ //# sourceMappingURL=core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.js","sourceRoot":"","sources":["../../../src/lib/reconcile/core.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Tier 2 State Reconciler — Runner (PRLT-1280)
3
+ *
4
+ * This module owns the IO: it talks to the database, the GitHub CLI, and
5
+ * the provider adapter layer. The pure rule logic lives in ./core.ts so
6
+ * it can be unit-tested without these dependencies.
7
+ *
8
+ * Scope (narrow — the ticket is very explicit about what this is NOT):
9
+ * - Single idempotent reconciliation function on a schedule
10
+ * - Provider-agnostic — the reconciler does not branch on provider type
11
+ * - Fires normal state-transition code path so hooks & cleanup fire too
12
+ *
13
+ * Everything outside that — webhook listeners, LLM escalation, human
14
+ * inbox, supervision tree — is explicitly out of scope per PRLT-1280.
15
+ */
16
+ import type Database from 'better-sqlite3';
17
+ import type { PMOStorage } from '../pmo/types.js';
18
+ import type { ProviderStorage } from '../providers/types.js';
19
+ import type { PRLookupFn, ReconcileOptions, ReconcileReport } from './types.js';
20
+ export type { LinkedPR, LinkedPRSource, PRLookupFn, PRLookupRef, ReconcileOptions, ReconcileReport, ReconcileTicket, ReconcileTransition, } from './types.js';
21
+ export { deriveExpectedState, buildTransition, formatTransitionLogLine } from './core.js';
22
+ /**
23
+ * Default live PR lookup. Always goes to the remote — the Tier 2
24
+ * reconciler must not depend on any local PR cache.
25
+ */
26
+ export declare const defaultPRLookup: PRLookupFn;
27
+ /**
28
+ * Run one reconciliation cycle.
29
+ *
30
+ * Strictly idempotent: if a ticket is already in its expected state the
31
+ * runner computes no transition and touches nothing. Running twice
32
+ * back-to-back produces no redundant side effects.
33
+ */
34
+ export declare function runReconcile(db: Database.Database, storage: PMOStorage & ProviderStorage, options?: ReconcileOptions): Promise<ReconcileReport>;
35
+ /**
36
+ * Parse a PR number out of a github.com URL.
37
+ * Accepts both `/pull/123` and `/pull/123/...` shapes.
38
+ */
39
+ declare function parsePRNumberFromUrl(url: string): number | null;
40
+ /**
41
+ * Run the reconciler on a timer until the caller-supplied `shouldStop`
42
+ * callback returns true.
43
+ *
44
+ * Each iteration runs one full {@link runReconcile} cycle and then sleeps
45
+ * for `intervalMs` before the next one. Errors inside a cycle are caught
46
+ * and surfaced via `log` — a single bad cycle never kills the watcher.
47
+ */
48
+ export declare function watchReconcile(db: Database.Database, storage: PMOStorage & ProviderStorage, options?: ReconcileOptions & {
49
+ intervalMs?: number;
50
+ shouldStop?: () => boolean;
51
+ onCycle?: (report: ReconcileReport) => void;
52
+ sleep?: (ms: number) => Promise<void>;
53
+ }): Promise<void>;
54
+ export { parsePRNumberFromUrl as _parsePRNumberFromUrl };
@@ -0,0 +1,377 @@
1
+ /**
2
+ * Tier 2 State Reconciler — Runner (PRLT-1280)
3
+ *
4
+ * This module owns the IO: it talks to the database, the GitHub CLI, and
5
+ * the provider adapter layer. The pure rule logic lives in ./core.ts so
6
+ * it can be unit-tested without these dependencies.
7
+ *
8
+ * Scope (narrow — the ticket is very explicit about what this is NOT):
9
+ * - Single idempotent reconciliation function on a schedule
10
+ * - Provider-agnostic — the reconciler does not branch on provider type
11
+ * - Fires normal state-transition code path so hooks & cleanup fire too
12
+ *
13
+ * Everything outside that — webhook listeners, LLM escalation, human
14
+ * inbox, supervision tree — is explicitly out of scope per PRLT-1280.
15
+ */
16
+ import { PMO_TABLES } from '../pmo/schema.js';
17
+ import { resolveTicketProvider } from '../providers/resolver.js';
18
+ import { getPRForBranch, getPRByNumber } from '../pr/index.js';
19
+ import { getWorkflowConfig } from '../work-lifecycle/settings.js';
20
+ import { buildTransition, deriveExpectedState, formatTransitionLogLine } from './core.js';
21
+ export { deriveExpectedState, buildTransition, formatTransitionLogLine } from './core.js';
22
+ /**
23
+ * Non-terminal state categories that the reconciler scans.
24
+ *
25
+ * The ticket spec says:
26
+ * "Query all tickets in non-terminal states across all connected
27
+ * providers (Linear, PMO, Notion, Asana, Jira, Shortcut)"
28
+ *
29
+ * Terminal states (`completed`, `canceled`) are deliberately excluded —
30
+ * the reconciler never resurrects a closed ticket.
31
+ */
32
+ const NON_TERMINAL_CATEGORIES = [
33
+ 'triage',
34
+ 'backlog',
35
+ 'unstarted',
36
+ 'started',
37
+ ];
38
+ /**
39
+ * Default live PR lookup. Always goes to the remote — the Tier 2
40
+ * reconciler must not depend on any local PR cache.
41
+ */
42
+ export const defaultPRLookup = (ref, cwd) => {
43
+ switch (ref.kind) {
44
+ case 'branch':
45
+ return getPRForBranch(ref.branch, cwd);
46
+ case 'number':
47
+ return getPRByNumber(ref.number, cwd);
48
+ case 'url': {
49
+ // `gh pr view <url>` works with either a number or URL; reuse it.
50
+ const num = parsePRNumberFromUrl(ref.url);
51
+ if (num !== null)
52
+ return getPRByNumber(num, cwd);
53
+ return null;
54
+ }
55
+ }
56
+ };
57
+ /**
58
+ * Run one reconciliation cycle.
59
+ *
60
+ * Strictly idempotent: if a ticket is already in its expected state the
61
+ * runner computes no transition and touches nothing. Running twice
62
+ * back-to-back produces no redundant side effects.
63
+ */
64
+ export async function runReconcile(db, storage, options = {}) {
65
+ const startedAt = new Date();
66
+ const { dryRun = false, cwd, log, projectId } = options;
67
+ const prLookup = options.prLookup ?? defaultPRLookup;
68
+ // Workflow config is used to resolve "Done" / "In Progress" / "Review"
69
+ // column names for the ticket's board. Fall back gracefully if the
70
+ // settings table is missing (older databases, unit tests).
71
+ let workflowConfig;
72
+ try {
73
+ workflowConfig = getWorkflowConfig(db);
74
+ }
75
+ catch {
76
+ workflowConfig = undefined;
77
+ }
78
+ // Gather every non-terminal ticket in scope. Either one project if
79
+ // -P was passed, or every project in the workspace.
80
+ const projectIds = projectId ? [projectId] : await listAllProjectIds(storage);
81
+ const tickets = [];
82
+ for (const pid of projectIds) {
83
+ for (const category of NON_TERMINAL_CATEGORIES) {
84
+ try {
85
+ // eslint-disable-next-line no-await-in-loop -- sequential is fine; small N
86
+ const batch = await storage.listTickets(pid, { statusCategory: category });
87
+ tickets.push(...batch);
88
+ }
89
+ catch {
90
+ // Tolerate transient read errors on a single category; keep going.
91
+ }
92
+ }
93
+ }
94
+ log?.(`Scanning ${tickets.length} non-terminal ticket(s) across ${projectIds.length} project(s)...`);
95
+ const transitions = [];
96
+ const errors = [];
97
+ for (const ticket of tickets) {
98
+ try {
99
+ const linkedPRs = gatherLinkedPRs(db, ticket, prLookup, cwd);
100
+ if (linkedPRs.length === 0) {
101
+ continue;
102
+ }
103
+ // Resolve the provider for this ticket so we can log which backend
104
+ // owns it. The actual move happens through the same resolver later
105
+ // in applyTransition() — this is just for the transition record.
106
+ const providerName = providerNameFor(db, storage, ticket);
107
+ const decision = deriveExpectedState(toReconcileTicket(ticket), linkedPRs, workflowConfig);
108
+ if (!decision)
109
+ continue;
110
+ transitions.push(buildTransition({
111
+ ticket: toReconcileTicket(ticket),
112
+ provider: providerName,
113
+ targetState: decision.targetState,
114
+ reason: decision.reason,
115
+ driver: decision.driver,
116
+ }));
117
+ }
118
+ catch (err) {
119
+ errors.push({ ticketId: ticket.id, error: err instanceof Error ? err.message : String(err) });
120
+ }
121
+ }
122
+ // Apply (or dry-run) every transition. Failures on one ticket do not
123
+ // stop the others — the reconciler is a best-effort safety net.
124
+ const applied = [];
125
+ const skipped = [];
126
+ const failed = [];
127
+ for (const transition of transitions) {
128
+ log?.(formatTransitionLogLine(transition));
129
+ if (dryRun) {
130
+ skipped.push(transition);
131
+ continue;
132
+ }
133
+ try {
134
+ // eslint-disable-next-line no-await-in-loop
135
+ await applyTransition(db, storage, transition);
136
+ applied.push(transition);
137
+ // Dual-identity tickets: if the primary provider is NOT local PMO,
138
+ // the local PMO board may still hold a stale mirror row. Nudge it
139
+ // to the same column so the local kanban view does not drift.
140
+ if (transition.provider !== 'pmo') {
141
+ // eslint-disable-next-line no-await-in-loop
142
+ await reconcilePmoMirror(db, storage, transition, log);
143
+ }
144
+ }
145
+ catch (err) {
146
+ failed.push({
147
+ transition,
148
+ error: err instanceof Error ? err.message : String(err),
149
+ });
150
+ }
151
+ }
152
+ const finishedAt = new Date();
153
+ return {
154
+ checked: tickets.length,
155
+ applied,
156
+ skipped,
157
+ failed,
158
+ errors,
159
+ startedAt: startedAt.toISOString(),
160
+ finishedAt: finishedAt.toISOString(),
161
+ };
162
+ }
163
+ // -----------------------------------------------------------------------------
164
+ // PR lookup
165
+ // -----------------------------------------------------------------------------
166
+ /**
167
+ * Gather every PR linked to a ticket by:
168
+ * 1. the ticket's branch (if set), and
169
+ * 2. PR URLs stored in `pmo_external_execution_prs` under any of the
170
+ * ticket's external-source identities.
171
+ *
172
+ * Every lookup goes through the live PR lookup function — no cache.
173
+ */
174
+ function gatherLinkedPRs(db, ticket, prLookup, cwd) {
175
+ const results = [];
176
+ const seen = new Set(); // de-dupe by PR number
177
+ const push = (pr, source) => {
178
+ if (!pr)
179
+ return;
180
+ if (seen.has(pr.number))
181
+ return;
182
+ seen.add(pr.number);
183
+ results.push({ pr, source });
184
+ };
185
+ // 1. Ticket branch
186
+ if (ticket.branch) {
187
+ push(prLookup({ kind: 'branch', branch: ticket.branch }, cwd), 'ticket_branch');
188
+ }
189
+ // 2. PR URLs stored in the external execution map for this ticket.
190
+ // A ticket can carry multiple external identities (metadata.external_id,
191
+ // external_key) and can have been linked to multiple PRs over time.
192
+ const prUrls = listLinkedPrUrls(db, ticket);
193
+ for (const url of prUrls) {
194
+ push(prLookup({ kind: 'url', url }, cwd), 'external_map');
195
+ }
196
+ return results;
197
+ }
198
+ /**
199
+ * Read every PR URL stored for this ticket in `pmo_external_execution_prs`.
200
+ * Keyed by (provider, external_id) — the reconciler doesn't care which
201
+ * provider it came from, so we union across all of them.
202
+ */
203
+ function listLinkedPrUrls(db, ticket) {
204
+ const urls = new Set();
205
+ const md = ticket.metadata ?? {};
206
+ // Collect every identifier that might key the external mapping table.
207
+ const externalIds = new Set();
208
+ if (md.external_id)
209
+ externalIds.add(md.external_id);
210
+ if (md.external_key)
211
+ externalIds.add(md.external_key);
212
+ // A PMO-native ticket's id can also appear in the map under provider='pmo'.
213
+ externalIds.add(ticket.id);
214
+ if (externalIds.size === 0)
215
+ return [];
216
+ let stmt;
217
+ try {
218
+ stmt = db.prepare(`
219
+ SELECT pr_url FROM ${PMO_TABLES.external_execution_prs}
220
+ WHERE external_id = ?
221
+ `);
222
+ }
223
+ catch {
224
+ // Table may not exist yet (fresh DB or older schema).
225
+ return [];
226
+ }
227
+ for (const extId of externalIds) {
228
+ try {
229
+ const rows = stmt.all(extId);
230
+ for (const row of rows)
231
+ urls.add(row.pr_url);
232
+ }
233
+ catch {
234
+ // Non-fatal: skip this id.
235
+ }
236
+ }
237
+ return [...urls];
238
+ }
239
+ /**
240
+ * Parse a PR number out of a github.com URL.
241
+ * Accepts both `/pull/123` and `/pull/123/...` shapes.
242
+ */
243
+ function parsePRNumberFromUrl(url) {
244
+ const m = url.match(/\/pull\/(\d+)(?:\b|\/)/);
245
+ if (!m)
246
+ return null;
247
+ const n = Number.parseInt(m[1], 10);
248
+ return Number.isFinite(n) ? n : null;
249
+ }
250
+ // -----------------------------------------------------------------------------
251
+ // Apply (goes through normal state-transition code path)
252
+ // -----------------------------------------------------------------------------
253
+ /**
254
+ * Apply a transition through the same provider adapter layer that
255
+ * `prlt ticket move` uses. This is load-bearing: it ensures hooks,
256
+ * provider sync, and worktree cleanup all fire.
257
+ */
258
+ async function applyTransition(db, storage, transition) {
259
+ const ticket = await storage.getTicket(transition.ticketId);
260
+ if (!ticket) {
261
+ throw new Error(`Ticket ${transition.ticketId} vanished between scan and apply`);
262
+ }
263
+ const provider = resolveTicketProvider(ticket.id, ticket.projectId ?? transition.projectId, db, storage, ticket.metadata);
264
+ const result = await provider.moveTicket(ticket.id, transition.toState);
265
+ if (!result.success) {
266
+ throw new Error(result.error ?? 'moveTicket failed without an error message');
267
+ }
268
+ }
269
+ /**
270
+ * For tickets whose source of truth is an external provider (Linear,
271
+ * Jira, etc.), the local PMO board can still hold a mirror row. After
272
+ * successfully moving the external ticket, reconcile the local PMO side
273
+ * too so `prlt board` does not drift.
274
+ *
275
+ * Best effort — failures here never block the primary transition.
276
+ */
277
+ async function reconcilePmoMirror(db, storage, transition, log) {
278
+ try {
279
+ const mirror = await storage.getTicket(transition.ticketId);
280
+ if (!mirror)
281
+ return;
282
+ // Only move if the mirror truly holds a different status — that way
283
+ // we stay idempotent and do not fire redundant hooks.
284
+ if (mirror.statusName && mirror.statusName.toLowerCase() === transition.toState.toLowerCase()) {
285
+ return;
286
+ }
287
+ // Use the pmo provider directly (bypass resolver so we definitely
288
+ // hit the local mirror, not bounce back to Linear).
289
+ const pmoProvider = resolveTicketProvider(mirror.id, mirror.projectId ?? transition.projectId, db, storage,
290
+ // Strip external_source so the resolver picks PMO.
291
+ null);
292
+ if (pmoProvider.name !== 'pmo')
293
+ return;
294
+ const result = await pmoProvider.moveTicket(mirror.id, transition.toState);
295
+ if (!result.success && log) {
296
+ log(`[reconcile] pmo mirror update for ${mirror.id} failed: ${result.error}`);
297
+ }
298
+ }
299
+ catch (err) {
300
+ log?.(`[reconcile] pmo mirror update for ${transition.ticketId} errored: ` +
301
+ (err instanceof Error ? err.message : String(err)));
302
+ }
303
+ }
304
+ // -----------------------------------------------------------------------------
305
+ // Project / provider resolution helpers
306
+ // -----------------------------------------------------------------------------
307
+ async function listAllProjectIds(storage) {
308
+ try {
309
+ const projects = await storage.listProjects();
310
+ return projects.map(p => p.id);
311
+ }
312
+ catch {
313
+ return [];
314
+ }
315
+ }
316
+ /**
317
+ * Resolve the provider name for a ticket without triggering any state
318
+ * resolution or event emission. Used purely so the transition record
319
+ * can log which backend owns the ticket.
320
+ */
321
+ function providerNameFor(db, storage, ticket) {
322
+ try {
323
+ const provider = resolveTicketProvider(ticket.id, ticket.projectId ?? '', db, storage, ticket.metadata);
324
+ return provider.name;
325
+ }
326
+ catch {
327
+ return 'pmo';
328
+ }
329
+ }
330
+ function toReconcileTicket(ticket) {
331
+ return {
332
+ id: ticket.id,
333
+ title: ticket.title,
334
+ projectId: ticket.projectId,
335
+ statusName: ticket.statusName,
336
+ statusCategory: ticket.statusCategory,
337
+ branch: ticket.branch,
338
+ metadata: ticket.metadata,
339
+ };
340
+ }
341
+ // -----------------------------------------------------------------------------
342
+ // Watch loop
343
+ // -----------------------------------------------------------------------------
344
+ /**
345
+ * Run the reconciler on a timer until the caller-supplied `shouldStop`
346
+ * callback returns true.
347
+ *
348
+ * Each iteration runs one full {@link runReconcile} cycle and then sleeps
349
+ * for `intervalMs` before the next one. Errors inside a cycle are caught
350
+ * and surfaced via `log` — a single bad cycle never kills the watcher.
351
+ */
352
+ export async function watchReconcile(db, storage, options = {}) {
353
+ const { intervalMs = 5 * 60 * 1000, shouldStop = () => false, onCycle, sleep = ms => new Promise(resolve => setTimeout(resolve, ms)), log, } = options;
354
+ // Cycle counter for tests that want to bound the loop.
355
+ let cycle = 0;
356
+ while (!shouldStop()) {
357
+ try {
358
+ // eslint-disable-next-line no-await-in-loop
359
+ const report = await runReconcile(db, storage, options);
360
+ cycle += 1;
361
+ log?.(`[reconcile] cycle ${cycle} complete — checked=${report.checked} ` +
362
+ `applied=${report.applied.length} skipped=${report.skipped.length} ` +
363
+ `failed=${report.failed.length}`);
364
+ onCycle?.(report);
365
+ }
366
+ catch (err) {
367
+ log?.(`[reconcile] cycle failed: ${err instanceof Error ? err.message : String(err)}`);
368
+ }
369
+ if (shouldStop())
370
+ return;
371
+ // eslint-disable-next-line no-await-in-loop
372
+ await sleep(intervalMs);
373
+ }
374
+ }
375
+ // Exported for tests that want to call the PR URL parser directly.
376
+ export { parsePRNumberFromUrl as _parsePRNumberFromUrl };
377
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/reconcile/index.ts"],"names":[],"mappings":"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"}