@reneturcios/greenlight 0.3.11 → 0.5.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.
Files changed (51) hide show
  1. package/README.md +42 -1
  2. package/dist/automate.d.ts +14 -1
  3. package/dist/automate.js +30 -12
  4. package/dist/automate.js.map +1 -1
  5. package/dist/cli.js +194 -7
  6. package/dist/cli.js.map +1 -1
  7. package/dist/digest.d.ts +1 -1
  8. package/dist/digest.js +1 -1
  9. package/dist/digest.js.map +1 -1
  10. package/dist/doctor.d.ts +34 -0
  11. package/dist/doctor.js +158 -0
  12. package/dist/doctor.js.map +1 -0
  13. package/dist/friction.d.ts +7 -0
  14. package/dist/friction.js +36 -0
  15. package/dist/friction.js.map +1 -1
  16. package/dist/github.js +2 -2
  17. package/dist/github.js.map +1 -1
  18. package/dist/guard.d.ts +5 -3
  19. package/dist/guard.js +60 -5
  20. package/dist/guard.js.map +1 -1
  21. package/dist/help.d.ts +25 -0
  22. package/dist/help.js +234 -0
  23. package/dist/help.js.map +1 -0
  24. package/dist/index.js +85 -33
  25. package/dist/index.js.map +1 -1
  26. package/dist/init.d.ts +61 -0
  27. package/dist/init.js +298 -0
  28. package/dist/init.js.map +1 -0
  29. package/dist/protocol-fetch.d.ts +142 -0
  30. package/dist/protocol-fetch.js +350 -0
  31. package/dist/protocol-fetch.js.map +1 -0
  32. package/dist/protocol.d.ts +232 -0
  33. package/dist/protocol.js +484 -0
  34. package/dist/protocol.js.map +1 -0
  35. package/dist/verify-comment.d.ts +75 -0
  36. package/dist/verify-comment.js +149 -0
  37. package/dist/verify-comment.js.map +1 -0
  38. package/dist/verify-parsers.d.ts +25 -0
  39. package/dist/verify-parsers.js +56 -0
  40. package/dist/verify-parsers.js.map +1 -0
  41. package/dist/verify.d.ts +52 -0
  42. package/dist/verify.js +276 -0
  43. package/dist/verify.js.map +1 -0
  44. package/integrations/README.md +4 -1
  45. package/integrations/claude-code/CLAUDE.snippet.md +6 -5
  46. package/integrations/cline/README.md +1 -1
  47. package/integrations/copilot/copilot-instructions.snippet.md +6 -3
  48. package/integrations/cursor/greenlight.mdc +2 -1
  49. package/integrations/windsurf/README.md +1 -1
  50. package/package.json +1 -1
  51. package/server.json +2 -2
@@ -0,0 +1,232 @@
1
+ /**
2
+ * The Greenlight Claim Protocol core engine (Phase 1, Greenlight v0.4.0).
3
+ *
4
+ * Implements SPEC.md sections 3 (the claim), 4 (policy file) and 5.1 (status
5
+ * payload) as pure functions over injected data: no network, no dates other
6
+ * than the caller's `now`, no exceptions on bad input.
7
+ *
8
+ * Design commitments carried over from the spec:
9
+ * - GitHub is the source of truth: a claim is a comment carrying the marker
10
+ * `<!-- greenlight-claim v1 -->`, ordered by the comment's own created_at
11
+ * (ties broken by comment id). The engine never invents timestamps.
12
+ * - Claims are leases, not property: expiry is computed lazily by any reader.
13
+ * - The state machine replays events chronologically (claims, heartbeats,
14
+ * releases, PR links), so a later build phase (the Ledger indexer) and the
15
+ * client-side CLI share one truth.
16
+ *
17
+ * Spec ambiguities resolved here (each is flagged DECISION in a comment):
18
+ * 1. A second protocol claim by the same author while their earlier claim is
19
+ * still alive is treated as a heartbeat, not a new claim (anti-squatting).
20
+ * 2. A promoted claim restarts its lease at promotion time: its expiry is
21
+ * promotion + lease (capped by promotion + tenure cap). Without this, long
22
+ * queues would be born dead.
23
+ * 3. A rocket reaction carries no timestamp in our input shape, so it counts
24
+ * as a heartbeat "as of now" (extends expiry to now + lease).
25
+ * 4. A linked PR in state "closed" (not merged) keeps the claim "linked"
26
+ * (lease frozen); the claim does not quietly resume.
27
+ * 5. A mergedPrNumber that is not among linkedPrs is synthesized as a linked
28
+ * merged PR with createdAt = now. It freezes the current holder; it does
29
+ * not resurrect a claim that already expired before the merge was known
30
+ * (expiry is lazy but materialized, not speculative).
31
+ * 6. Heartbeats only count after a claim's activation; a queued claimant's
32
+ * earlier comments do not pre-extend the lease.
33
+ * 7. Comments with unparseable createdAt are skipped entirely (they cannot be
34
+ * ordered, and ordering is the whole game).
35
+ */
36
+ import type { Verdict } from "./types.js";
37
+ export declare const PROTOCOL_VERSION = 1;
38
+ export declare const CLAIM_MARKER = "<!-- greenlight-claim v1 -->";
39
+ export declare const RELEASE_MARKER = "<!-- greenlight-claim release -->";
40
+ /** Spec 3.4: squatting budget, indexed active claims per account. */
41
+ export declare const MAX_ACTIVE_CLAIMS_PER_ACCOUNT = 5;
42
+ /** Repo claim policy, from `.greenlight/claims.json`. */
43
+ export interface ClaimsPolicy {
44
+ protocol: 1;
45
+ lease_days: number;
46
+ tenure_cap_days: number;
47
+ queue: boolean;
48
+ who_can_claim: string;
49
+ maintainers: string[];
50
+ /** True when the policy text could not be parsed or named another protocol
51
+ * version; the rest of the fields are the safe defaults. */
52
+ invalid?: boolean;
53
+ }
54
+ export declare function defaultClaimsPolicy(): ClaimsPolicy;
55
+ /**
56
+ * Parse a `.greenlight/claims.json` body. Never throws: bad JSON (or a
57
+ * protocol version this engine cannot read) degrades to the defaults with
58
+ * `invalid: true`.
59
+ */
60
+ export declare function parseClaimsPolicy(jsonText: string): ClaimsPolicy;
61
+ export interface ParsedClaimComment {
62
+ isClaim: boolean;
63
+ isRelease: boolean;
64
+ plan: string | null;
65
+ etaDays: number | null;
66
+ queueOk: boolean | null;
67
+ }
68
+ /**
69
+ * Detect a protocol claim (or release) comment and parse the optional
70
+ * structured lines after the marker. Unknown lines are ignored; the marker
71
+ * alone is a valid claim.
72
+ */
73
+ export declare function parseClaimComment(body: string): ParsedClaimComment;
74
+ /** One issue comment, as seen by the protocol engine. */
75
+ export interface ProtocolCommentInput {
76
+ author: string;
77
+ body: string;
78
+ createdAt: string;
79
+ id: number | string;
80
+ url?: string;
81
+ }
82
+ /** A reaction on a comment. No timestamp in this shape (see DECISION 3). */
83
+ export interface ProtocolReactionInput {
84
+ commentId: number | string;
85
+ user: string;
86
+ content: string;
87
+ }
88
+ /**
89
+ * A PR with closing intent toward the issue. Callers pre-classify link
90
+ * strength (reuse guard's extractors); this engine only needs the timing.
91
+ */
92
+ export interface ProtocolLinkedPrInput {
93
+ number: number;
94
+ state: "open" | "closed" | "merged";
95
+ createdAt: string;
96
+ }
97
+ export interface ClaimStateInput {
98
+ comments: ProtocolCommentInput[];
99
+ reactions?: ProtocolReactionInput[];
100
+ linkedPrs?: ProtocolLinkedPrInput[];
101
+ /** A PR known to have merged fixing this issue (used when linkedPrs lacks it). */
102
+ mergedPrNumber?: number | null;
103
+ policy?: ClaimsPolicy;
104
+ /** ISO "now". Defaults to the real current time; pass explicitly in tests. */
105
+ now?: string;
106
+ repo?: string;
107
+ issue?: number;
108
+ }
109
+ export type ClaimStateName = "active" | "linked" | "completed" | "released" | "expired" | "queued";
110
+ export interface ClaimHistoryEvent {
111
+ event: string;
112
+ at: string;
113
+ by: string;
114
+ }
115
+ /** One protocol claim in the chain, fully resolved. */
116
+ export interface ResolvedClaim {
117
+ /** 1-based position in the chronological chain (1 = head). */
118
+ position: number;
119
+ claimant: string;
120
+ claimedAt: string;
121
+ commentId: number | string;
122
+ commentUrl: string | null;
123
+ plan: string | null;
124
+ etaDays: number | null;
125
+ queueOk: boolean | null;
126
+ state: ClaimStateName;
127
+ /** When this claim's lease started (claimedAt, or promotion time). */
128
+ activatedAt: string;
129
+ expiresAt: string;
130
+ lastHeartbeatAt: string;
131
+ linkedPr: number | null;
132
+ history: ClaimHistoryEvent[];
133
+ }
134
+ /** The SPEC 3.2 record shape, as stored in the Ledger and returned by the API. */
135
+ export interface ProtocolClaimRecord {
136
+ v: 1;
137
+ repo: string | null;
138
+ issue: number | null;
139
+ claimant: string;
140
+ claimed_at: string;
141
+ comment_url: string | null;
142
+ state: ClaimStateName;
143
+ lease_days: number;
144
+ expires_at: string;
145
+ last_heartbeat_at: string;
146
+ linked_pr: number | null;
147
+ queue_position: number;
148
+ history: ClaimHistoryEvent[];
149
+ over_cap?: boolean;
150
+ }
151
+ export interface ProtocolComputation {
152
+ /** Every protocol claim ever posted, in claimed_at order (ties by id). */
153
+ claims: ResolvedClaim[];
154
+ /** The claim holding the issue at `now`, or null when the issue is free. */
155
+ active: ResolvedClaim | null;
156
+ /** Claims waiting behind the holder (positions 2+). */
157
+ queue: ResolvedClaim[];
158
+ /** The SPEC 3.2 record for the active claim, or null. */
159
+ activeRecord: ProtocolClaimRecord | null;
160
+ policy: ClaimsPolicy;
161
+ }
162
+ /**
163
+ * Replay the claim chain for one issue and resolve every claim's state at
164
+ * `now`. Pure: no network, no exceptions on messy input.
165
+ */
166
+ export declare function computeClaimState(input: ClaimStateInput): ProtocolComputation;
167
+ /**
168
+ * Spec 3.4: mark claims whose claimant holds more than the squatting budget
169
+ * of active claims. `activeCounts` maps login -> active claim count (the
170
+ * caller supplies it from the Ledger or another computation; nothing is
171
+ * fetched here). Returns new records; the input is untouched.
172
+ */
173
+ export declare function applyOverCap(records: ProtocolClaimRecord[], activeCounts: Record<string, number>): ProtocolClaimRecord[];
174
+ export interface StatusCompetingPr {
175
+ number: number;
176
+ author: string;
177
+ state: "open" | "closed" | "merged";
178
+ url?: string;
179
+ }
180
+ export interface StatusGate {
181
+ kind: string;
182
+ detected: boolean;
183
+ evidence?: string;
184
+ }
185
+ export interface IssueStatusArgs {
186
+ issueUrl: string;
187
+ computation: ProtocolComputation;
188
+ competingPrs?: StatusCompetingPr[];
189
+ gates?: StatusGate[];
190
+ /** Null when the repo has no policy file (protocol works as a signal only). */
191
+ repoProtocol: {
192
+ honored: boolean;
193
+ lease_days: number;
194
+ } | null;
195
+ checkedAt?: string;
196
+ staleAfterSeconds?: number;
197
+ }
198
+ export interface IssueStatusPayload {
199
+ issue: string;
200
+ /** Null when there are no protocol claims: the verdict is the caller's. */
201
+ verdict: Verdict | null;
202
+ reason: string | null;
203
+ claim: {
204
+ state: ClaimStateName;
205
+ claimant: string;
206
+ claimed_at: string;
207
+ expires_at: string;
208
+ queue: Array<{
209
+ claimant: string;
210
+ position: number;
211
+ claimed_at: string;
212
+ }>;
213
+ over_cap?: boolean;
214
+ } | null;
215
+ protocol_note: string | null;
216
+ competing_prs: StatusCompetingPr[];
217
+ gates: StatusGate[];
218
+ repo_protocol: {
219
+ honored: boolean;
220
+ lease_days: number;
221
+ } | null;
222
+ checked_at: string;
223
+ stale_after_seconds: number;
224
+ }
225
+ /**
226
+ * Build the SPEC 5.1 issue-status payload from a protocol computation plus
227
+ * caller-supplied context (competing PRs, gates). Verdict mapping:
228
+ * active protocol claim -> TAKEN (expiry in the reason); a completed claim
229
+ * -> TAKEN naming the merged PR; expired-only history -> GO with a note;
230
+ * no protocol claims at all -> verdict null, claim null.
231
+ */
232
+ export declare function buildIssueStatus(args: IssueStatusArgs): IssueStatusPayload;