@reneturcios/greenlight 0.8.2 → 0.9.1
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 +49 -3
- package/dist/automate.js +13 -0
- package/dist/automate.js.map +1 -1
- package/dist/babysit.d.ts +73 -1
- package/dist/babysit.js +261 -71
- package/dist/babysit.js.map +1 -1
- package/dist/claim-health.d.ts +44 -0
- package/dist/claim-health.js +88 -0
- package/dist/claim-health.js.map +1 -0
- package/dist/claim-import.d.ts +59 -0
- package/dist/claim-import.js +164 -0
- package/dist/claim-import.js.map +1 -0
- package/dist/claims-registry.d.ts +35 -0
- package/dist/claims-registry.js +71 -0
- package/dist/claims-registry.js.map +1 -0
- package/dist/cli.js +187 -14
- package/dist/cli.js.map +1 -1
- package/dist/digest.d.ts +32 -1
- package/dist/digest.js +46 -4
- package/dist/digest.js.map +1 -1
- package/dist/doctor.js +14 -6
- package/dist/doctor.js.map +1 -1
- package/dist/gates.d.ts +1 -1
- package/dist/gates.js +39 -2
- package/dist/gates.js.map +1 -1
- package/dist/github.d.ts +4 -0
- package/dist/github.js +11 -0
- package/dist/github.js.map +1 -1
- package/dist/guard.d.ts +13 -1
- package/dist/guard.js +15 -1
- package/dist/guard.js.map +1 -1
- package/dist/help.js +51 -7
- package/dist/help.js.map +1 -1
- package/dist/index.js +39 -1
- package/dist/index.js.map +1 -1
- package/dist/init.js +1 -1
- package/dist/proxy.d.ts +50 -0
- package/dist/proxy.js +111 -0
- package/dist/proxy.js.map +1 -0
- package/dist/scout.d.ts +114 -0
- package/dist/scout.js +266 -0
- package/dist/scout.js.map +1 -0
- package/dist/triage.d.ts +23 -0
- package/dist/triage.js +83 -0
- package/dist/triage.js.map +1 -0
- package/dist/types.d.ts +17 -1
- package/dist/verdict.d.ts +15 -6
- package/dist/verdict.js +14 -0
- package/dist/verdict.js.map +1 -1
- package/integrations/README.md +3 -1
- package/integrations/claude-code/CLAUDE.snippet.md +3 -2
- package/integrations/cline/README.md +1 -1
- package/integrations/windsurf/README.md +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/** "No activity" window for the unlinked-stale bucket. */
|
|
2
|
+
export const UNLINKED_STALE_HOURS = 72;
|
|
3
|
+
/** Lease-remaining threshold for the expiring bucket. */
|
|
4
|
+
export const EXPIRING_HOURS = 48;
|
|
5
|
+
function hoursBetween(a, b) {
|
|
6
|
+
const t = new Date(a).getTime();
|
|
7
|
+
if (Number.isNaN(t))
|
|
8
|
+
return null;
|
|
9
|
+
return (b.getTime() - t) / 3600000;
|
|
10
|
+
}
|
|
11
|
+
export function bucketClaim(input) {
|
|
12
|
+
const { claim, status } = input;
|
|
13
|
+
const now = input.now ?? new Date();
|
|
14
|
+
const tag = `${claim.owner}/${claim.repo}#${claim.number}`;
|
|
15
|
+
const claimState = status.claim;
|
|
16
|
+
// Linked: an open PR against the issue means the work is with the
|
|
17
|
+
// maintainer. Prefer the operator's own PR; a known PR URL from the
|
|
18
|
+
// registry counts too.
|
|
19
|
+
const openPrs = status.competing_prs.filter((p) => p.state === "open");
|
|
20
|
+
const mine = claim.claimant
|
|
21
|
+
? openPrs.find((p) => p.author.toLowerCase() === claim.claimant.toLowerCase())
|
|
22
|
+
: undefined;
|
|
23
|
+
const anyOpen = mine ?? openPrs.find((p) => p.url === claim.prUrl) ?? openPrs[0];
|
|
24
|
+
if (claimState?.state === "linked" && openPrs.length > 0) {
|
|
25
|
+
return {
|
|
26
|
+
bucket: "linked",
|
|
27
|
+
detail: `${tag}: PR open (${anyOpen ? `#${anyOpen.number} by @${anyOpen.author}` : "claim linked"}) — work is with the maintainer`,
|
|
28
|
+
prUrl: anyOpen?.url ?? claim.prUrl,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
// Queue-lost: the lease died, or the issue's active claim belongs to
|
|
32
|
+
// someone else (superseded), or someone ahead completed.
|
|
33
|
+
const someoneElseClaiming = claimState !== null && claim.claimant && claimState.claimant.toLowerCase() !== claim.claimant.toLowerCase();
|
|
34
|
+
if (claimState?.state === "expired") {
|
|
35
|
+
return { bucket: "queue-lost", detail: `${tag}: claim lease expired ${claimState.expires_at.slice(0, 10)} — re-claim or move on` };
|
|
36
|
+
}
|
|
37
|
+
if (someoneElseClaiming) {
|
|
38
|
+
return {
|
|
39
|
+
bucket: "queue-lost",
|
|
40
|
+
detail: `${tag}: active protocol claim belongs to @${claimState.claimant}, not you — your claim was superseded`,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const completedAhead = status.competing_prs.some((p) => p.state === "merged" && p.author !== claim.claimant);
|
|
44
|
+
if (completedAhead) {
|
|
45
|
+
return { bucket: "queue-lost", detail: `${tag}: someone else's PR already merged — the work is done without you` };
|
|
46
|
+
}
|
|
47
|
+
// Expiring: the lease has under 48h left (or the ETA is blown).
|
|
48
|
+
const remaining = claimState ? (new Date(claimState.expires_at).getTime() - now.getTime()) / 3600000 : null;
|
|
49
|
+
if (remaining !== null && remaining < EXPIRING_HOURS) {
|
|
50
|
+
return {
|
|
51
|
+
bucket: "expiring",
|
|
52
|
+
detail: `${tag}: lease ${remaining <= 0 ? "expired" : `expires in ${Math.round(remaining)}h`} (${claimState.expires_at.slice(0, 10)}) — renew the claim or land the PR`,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const ageHours = hoursBetween(claim.postedAt, now);
|
|
56
|
+
if (ageHours !== null && claim.etaDays !== undefined && ageHours > claim.etaDays * 24) {
|
|
57
|
+
return {
|
|
58
|
+
bucket: "expiring",
|
|
59
|
+
detail: `${tag}: ETA blown by ${Math.round(ageHours - claim.etaDays * 24)}h (promised ${claim.etaDays}d) — update the issue or land the PR`,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
// Unlinked-stale: 72h of claim with no PR ever linked.
|
|
63
|
+
if (claimState?.state === "active" && ageHours !== null && ageHours > UNLINKED_STALE_HOURS) {
|
|
64
|
+
return {
|
|
65
|
+
bucket: "unlinked-stale",
|
|
66
|
+
detail: `${tag}: claimed ${Math.round(ageHours / 24)}d ago with no PR linked — the claim is talk, not work`,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
// Heartbeat-due: lease past half with no activity on the issue since
|
|
70
|
+
// the claim was posted.
|
|
71
|
+
const leaseTotal = claimState ? hoursBetween(claimState.claimed_at, new Date(claimState.expires_at)) : null;
|
|
72
|
+
const leaseElapsed = claimState ? hoursBetween(claimState.claimed_at, now) : null;
|
|
73
|
+
const pastHalf = leaseTotal !== null && leaseTotal > 0 && leaseElapsed !== null && leaseElapsed > leaseTotal / 2;
|
|
74
|
+
const updatedSince = input.issueUpdatedAt ? hoursBetween(input.issueUpdatedAt, now) : null;
|
|
75
|
+
const noActivity = (input.issueUpdatedAt === null && updatedSince === null) ||
|
|
76
|
+
(updatedSince !== null && ageHours !== null && updatedSince >= ageHours);
|
|
77
|
+
if (pastHalf && noActivity) {
|
|
78
|
+
return {
|
|
79
|
+
bucket: "heartbeat-due",
|
|
80
|
+
detail: `${tag}: lease past half (${Math.round(leaseElapsed / 24)}d of ${Math.round(leaseTotal / 24)}d) with no issue activity since the claim — post a progress note`,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
bucket: "healthy-active",
|
|
85
|
+
detail: `${tag}: claim ${claimState ? `state ${claimState.state}, ` : ""}lease ${remaining !== null ? `${Math.round(remaining)}h left` : "unknown"} — on pace`,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=claim-health.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claim-health.js","sourceRoot":"","sources":["../src/claim-health.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `greenlight claim --import`: backfill the my-claims registry for claims
|
|
3
|
+
* that already exist. v0.9.0's registry auto-registers only newly posted
|
|
4
|
+
* claims, so anyone upgrading arrives with an empty registry while the
|
|
5
|
+
* public Claim Ledger already knows about their claims (field case: Rene
|
|
6
|
+
* held 11 live claims and the babysitter said "no claims registry yet").
|
|
7
|
+
*
|
|
8
|
+
* Two sources:
|
|
9
|
+
* --ledger-user <github-username> fetch the public Claim Ledger's
|
|
10
|
+
* static API and import every active or linked claim by that user.
|
|
11
|
+
* --urls <issue-url,...> import a manual list of issue URLs.
|
|
12
|
+
*
|
|
13
|
+
* Registration reuses registerClaim, so re-imports refresh rather than
|
|
14
|
+
* duplicate. States completed/released/expired/superseded are skipped:
|
|
15
|
+
* the babysitter would retire them on sight anyway.
|
|
16
|
+
*/
|
|
17
|
+
import { type MyClaim } from "./claims-registry.js";
|
|
18
|
+
/** The public Claim Ledger's static API base (no auth). */
|
|
19
|
+
export declare const LEDGER_BASE_URL = "https://cloudcorprecords.github.io/claim-ledger";
|
|
20
|
+
/** Claim states worth watching: active leases and linked claims. Retired states are skipped. */
|
|
21
|
+
export declare const IMPORTABLE_CLAIM_STATES: Set<string>;
|
|
22
|
+
export interface LedgerIndexRepo {
|
|
23
|
+
owner: string;
|
|
24
|
+
repo: string;
|
|
25
|
+
}
|
|
26
|
+
export interface LedgerClaimEntry {
|
|
27
|
+
issue: number;
|
|
28
|
+
issue_url: string;
|
|
29
|
+
claimant: string;
|
|
30
|
+
state: string;
|
|
31
|
+
queue: unknown[];
|
|
32
|
+
}
|
|
33
|
+
export interface ImportDeps {
|
|
34
|
+
/** JSON GET, injectable for tests; the default hits the Ledger pages site. */
|
|
35
|
+
fetchJson?: (url: string) => Promise<unknown>;
|
|
36
|
+
}
|
|
37
|
+
export declare function fetchLedgerRepos(deps?: ImportDeps): Promise<LedgerIndexRepo[]>;
|
|
38
|
+
export declare function fetchLedgerClaims(owner: string, repo: string, deps?: ImportDeps): Promise<LedgerClaimEntry[]>;
|
|
39
|
+
export interface ImportResult {
|
|
40
|
+
/** Claims newly added to the registry. */
|
|
41
|
+
added: number;
|
|
42
|
+
/** Claims skipped: already registered, wrong claimant, or a retired state. */
|
|
43
|
+
skipped: number;
|
|
44
|
+
/** Repos scanned on the Ledger. */
|
|
45
|
+
reposScanned: number;
|
|
46
|
+
/** The registered entries, in registration order. */
|
|
47
|
+
claims: MyClaim[];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Import every active or linked Ledger claim held by `username` into the
|
|
51
|
+
* registry. Pure of side effects except the registry file write.
|
|
52
|
+
*/
|
|
53
|
+
export declare function importLedgerClaims(registryPath: string, username: string, deps?: ImportDeps): Promise<ImportResult>;
|
|
54
|
+
/**
|
|
55
|
+
* Import a manual list of issue URLs (backfill for claims posted before
|
|
56
|
+
* the registry existed, or on repos the Ledger does not index). Never
|
|
57
|
+
* touches the network: pure registration with dedupe.
|
|
58
|
+
*/
|
|
59
|
+
export declare function importUrlList(registryPath: string, urls: string[]): ImportResult;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `greenlight claim --import`: backfill the my-claims registry for claims
|
|
3
|
+
* that already exist. v0.9.0's registry auto-registers only newly posted
|
|
4
|
+
* claims, so anyone upgrading arrives with an empty registry while the
|
|
5
|
+
* public Claim Ledger already knows about their claims (field case: Rene
|
|
6
|
+
* held 11 live claims and the babysitter said "no claims registry yet").
|
|
7
|
+
*
|
|
8
|
+
* Two sources:
|
|
9
|
+
* --ledger-user <github-username> fetch the public Claim Ledger's
|
|
10
|
+
* static API and import every active or linked claim by that user.
|
|
11
|
+
* --urls <issue-url,...> import a manual list of issue URLs.
|
|
12
|
+
*
|
|
13
|
+
* Registration reuses registerClaim, so re-imports refresh rather than
|
|
14
|
+
* duplicate. States completed/released/expired/superseded are skipped:
|
|
15
|
+
* the babysitter would retire them on sight anyway.
|
|
16
|
+
*/
|
|
17
|
+
import { registerClaim, loadRegistry } from "./claims-registry.js";
|
|
18
|
+
import { parseIssueUrl } from "./github.js";
|
|
19
|
+
/** The public Claim Ledger's static API base (no auth). */
|
|
20
|
+
export const LEDGER_BASE_URL = "https://cloudcorprecords.github.io/claim-ledger";
|
|
21
|
+
/** Claim states worth watching: active leases and linked claims. Retired states are skipped. */
|
|
22
|
+
export const IMPORTABLE_CLAIM_STATES = new Set(["active", "linked"]);
|
|
23
|
+
async function defaultFetchJson(url) {
|
|
24
|
+
const res = await fetch(url, {
|
|
25
|
+
headers: { Accept: "application/json", "User-Agent": "greenlight-cli" },
|
|
26
|
+
signal: AbortSignal.timeout(30000),
|
|
27
|
+
});
|
|
28
|
+
if (!res.ok)
|
|
29
|
+
throw new Error(`Ledger request failed: ${url} -> HTTP ${res.status}`);
|
|
30
|
+
return res.json();
|
|
31
|
+
}
|
|
32
|
+
export async function fetchLedgerRepos(deps = {}) {
|
|
33
|
+
const fetchJson = deps.fetchJson ?? defaultFetchJson;
|
|
34
|
+
const index = (await fetchJson(`${LEDGER_BASE_URL}/v1/index.json`));
|
|
35
|
+
const repos = [];
|
|
36
|
+
for (const r of index.repos ?? []) {
|
|
37
|
+
if (typeof r.owner === "string" && typeof r.repo === "string") {
|
|
38
|
+
repos.push({ owner: r.owner, repo: r.repo });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return repos;
|
|
42
|
+
}
|
|
43
|
+
export async function fetchLedgerClaims(owner, repo, deps = {}) {
|
|
44
|
+
const fetchJson = deps.fetchJson ?? defaultFetchJson;
|
|
45
|
+
const body = (await fetchJson(`${LEDGER_BASE_URL}/v1/repos/${owner}/${repo}/claims.json`));
|
|
46
|
+
const out = [];
|
|
47
|
+
for (const c of body.active_claims ?? []) {
|
|
48
|
+
if (typeof c.issue !== "number" || typeof c.issue_url !== "string" || typeof c.claimant !== "string" || typeof c.state !== "string") {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
out.push({
|
|
52
|
+
issue: c.issue,
|
|
53
|
+
issue_url: c.issue_url,
|
|
54
|
+
claimant: c.claimant,
|
|
55
|
+
state: c.state,
|
|
56
|
+
queue: Array.isArray(c.queue) ? c.queue : [],
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Import every active or linked Ledger claim held by `username` into the
|
|
63
|
+
* registry. Pure of side effects except the registry file write.
|
|
64
|
+
*/
|
|
65
|
+
export async function importLedgerClaims(registryPath, username, deps = {}) {
|
|
66
|
+
const user = username.toLowerCase();
|
|
67
|
+
const repos = await fetchLedgerRepos(deps);
|
|
68
|
+
const added = [];
|
|
69
|
+
let skipped = 0;
|
|
70
|
+
for (const { owner, repo } of repos) {
|
|
71
|
+
let entries;
|
|
72
|
+
try {
|
|
73
|
+
entries = await fetchLedgerClaims(owner, repo, deps);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// One repo's file failing must not abort the whole import.
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
for (const e of entries) {
|
|
80
|
+
if (e.claimant.toLowerCase() !== user) {
|
|
81
|
+
skipped++;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (!IMPORTABLE_CLAIM_STATES.has(e.state)) {
|
|
85
|
+
skipped++;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
let issueOwner = owner;
|
|
89
|
+
let issueRepo = repo;
|
|
90
|
+
let issueNumber = e.issue;
|
|
91
|
+
let issueUrl = e.issue_url;
|
|
92
|
+
try {
|
|
93
|
+
const parsed = parseIssueUrl(e.issue_url);
|
|
94
|
+
issueOwner = parsed.owner;
|
|
95
|
+
issueRepo = parsed.repo;
|
|
96
|
+
issueNumber = parsed.number;
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// The Ledger's own issue_url is canonical enough; keep the row's.
|
|
100
|
+
}
|
|
101
|
+
const key = issueUrl.toLowerCase();
|
|
102
|
+
const already = loadRegistry(registryPath).claims.some((c) => c.issueUrl.toLowerCase() === key);
|
|
103
|
+
if (already) {
|
|
104
|
+
skipped++;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const entry = {
|
|
108
|
+
issueUrl,
|
|
109
|
+
owner: issueOwner,
|
|
110
|
+
repo: issueRepo,
|
|
111
|
+
number: issueNumber,
|
|
112
|
+
postedAt: new Date().toISOString(),
|
|
113
|
+
claimant: e.claimant,
|
|
114
|
+
};
|
|
115
|
+
registerClaim(registryPath, entry);
|
|
116
|
+
added.push(entry);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return { added: added.length, skipped, reposScanned: repos.length, claims: added };
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Import a manual list of issue URLs (backfill for claims posted before
|
|
123
|
+
* the registry existed, or on repos the Ledger does not index). Never
|
|
124
|
+
* touches the network: pure registration with dedupe.
|
|
125
|
+
*/
|
|
126
|
+
export function importUrlList(registryPath, urls) {
|
|
127
|
+
const added = [];
|
|
128
|
+
let skipped = 0;
|
|
129
|
+
for (const raw of urls) {
|
|
130
|
+
const url = raw.trim();
|
|
131
|
+
if (!url) {
|
|
132
|
+
skipped++;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
let owner;
|
|
136
|
+
let repo;
|
|
137
|
+
let number;
|
|
138
|
+
try {
|
|
139
|
+
({ owner, repo, number } = parseIssueUrl(url));
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
skipped++;
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
const canonical = `https://github.com/${owner}/${repo}/issues/${number}`;
|
|
146
|
+
const key = canonical.toLowerCase();
|
|
147
|
+
const already = loadRegistry(registryPath).claims.some((c) => c.issueUrl.toLowerCase() === key);
|
|
148
|
+
if (already) {
|
|
149
|
+
skipped++;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const entry = {
|
|
153
|
+
issueUrl: canonical,
|
|
154
|
+
owner,
|
|
155
|
+
repo,
|
|
156
|
+
number,
|
|
157
|
+
postedAt: new Date().toISOString(),
|
|
158
|
+
};
|
|
159
|
+
registerClaim(registryPath, entry);
|
|
160
|
+
added.push(entry);
|
|
161
|
+
}
|
|
162
|
+
return { added: added.length, skipped, reposScanned: 0, claims: added };
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=claim-import.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claim-import.js","sourceRoot":"","sources":["../src/claim-import.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** One claim the operator posted or registered. */
|
|
2
|
+
export interface MyClaim {
|
|
3
|
+
/** Canonical issue URL, e.g. https://github.com/o/r/issues/65. */
|
|
4
|
+
issueUrl: string;
|
|
5
|
+
owner: string;
|
|
6
|
+
repo: string;
|
|
7
|
+
number: number;
|
|
8
|
+
/** ISO timestamp the claim comment was posted (or the claim registered). */
|
|
9
|
+
postedAt: string;
|
|
10
|
+
/** Days the operator promised to deliver, from --eta-days. */
|
|
11
|
+
etaDays?: number;
|
|
12
|
+
/** GitHub user who posted the claim comment; null when unknown. */
|
|
13
|
+
claimant?: string | null;
|
|
14
|
+
/** URL of the claim comment posted by the claim command. */
|
|
15
|
+
commentUrl?: string;
|
|
16
|
+
/** Known PR URL, set when the claim links to one. */
|
|
17
|
+
prUrl?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ClaimsRegistryFile {
|
|
20
|
+
version: 1;
|
|
21
|
+
claims: MyClaim[];
|
|
22
|
+
}
|
|
23
|
+
/** Override path for tests and scratch registries. */
|
|
24
|
+
export declare function defaultRegistryPath(): string;
|
|
25
|
+
export declare function loadRegistry(path: string): ClaimsRegistryFile;
|
|
26
|
+
export declare function saveRegistry(path: string, registry: ClaimsRegistryFile): void;
|
|
27
|
+
/**
|
|
28
|
+
* Add a claim to the registry (deduped by issue URL; a re-register of
|
|
29
|
+
* the same issue refreshes the entry). Returns the new registry.
|
|
30
|
+
*/
|
|
31
|
+
export declare function registerClaim(path: string, entry: MyClaim): ClaimsRegistryFile;
|
|
32
|
+
/** Remove a claim by issue URL; returns true when one was removed. */
|
|
33
|
+
export declare function removeClaim(path: string, issueUrl: string): boolean;
|
|
34
|
+
/** True when the file exists at all (used for "no registry yet" messaging). */
|
|
35
|
+
export declare function registryExists(path: string): boolean;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* My-claims registry (Greenlight v0.9.0): the campaign's memory of which
|
|
3
|
+
* issues the operator personally claimed. Written by `greenlight claim`
|
|
4
|
+
* when a claim comment actually posts (and by `greenlight claim
|
|
5
|
+
* --register <issue-url>` for claims posted any other way), read by the
|
|
6
|
+
* babysitter's claims section.
|
|
7
|
+
*
|
|
8
|
+
* The registry lives at ~/.greenlight/my-claims.json (override with
|
|
9
|
+
* GREENLIGHT_CLAIMS_FILE) and is a plain JSON array: easy to read,
|
|
10
|
+
* hand-edit, and carry between machines. A claim leaves the registry
|
|
11
|
+
* when it resolves (merged PR, closed issue, released claim) or when
|
|
12
|
+
* the operator removes it.
|
|
13
|
+
*/
|
|
14
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
15
|
+
import { homedir } from "node:os";
|
|
16
|
+
import { dirname, join } from "node:path";
|
|
17
|
+
/** Override path for tests and scratch registries. */
|
|
18
|
+
export function defaultRegistryPath() {
|
|
19
|
+
return process.env.GREENLIGHT_CLAIMS_FILE ?? join(homedir(), ".greenlight", "my-claims.json");
|
|
20
|
+
}
|
|
21
|
+
export function loadRegistry(path) {
|
|
22
|
+
try {
|
|
23
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
24
|
+
if (parsed && typeof parsed === "object" && Array.isArray(parsed.claims)) {
|
|
25
|
+
return { version: 1, claims: parsed.claims };
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// Missing or broken registry: start empty rather than crash the
|
|
30
|
+
// babysitter.
|
|
31
|
+
}
|
|
32
|
+
return { version: 1, claims: [] };
|
|
33
|
+
}
|
|
34
|
+
export function saveRegistry(path, registry) {
|
|
35
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
36
|
+
writeFileSync(path, JSON.stringify(registry, null, 2) + "\n", "utf8");
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Add a claim to the registry (deduped by issue URL; a re-register of
|
|
40
|
+
* the same issue refreshes the entry). Returns the new registry.
|
|
41
|
+
*/
|
|
42
|
+
export function registerClaim(path, entry) {
|
|
43
|
+
const registry = loadRegistry(path);
|
|
44
|
+
const key = entry.issueUrl.toLowerCase();
|
|
45
|
+
const idx = registry.claims.findIndex((c) => c.issueUrl.toLowerCase() === key);
|
|
46
|
+
if (idx >= 0) {
|
|
47
|
+
registry.claims[idx] = { ...registry.claims[idx], ...entry };
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
registry.claims.push(entry);
|
|
51
|
+
}
|
|
52
|
+
saveRegistry(path, registry);
|
|
53
|
+
return registry;
|
|
54
|
+
}
|
|
55
|
+
/** Remove a claim by issue URL; returns true when one was removed. */
|
|
56
|
+
export function removeClaim(path, issueUrl) {
|
|
57
|
+
const registry = loadRegistry(path);
|
|
58
|
+
const before = registry.claims.length;
|
|
59
|
+
const key = issueUrl.toLowerCase();
|
|
60
|
+
registry.claims = registry.claims.filter((c) => c.issueUrl.toLowerCase() !== key);
|
|
61
|
+
if (registry.claims.length !== before) {
|
|
62
|
+
saveRegistry(path, registry);
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
/** True when the file exists at all (used for "no registry yet" messaging). */
|
|
68
|
+
export function registryExists(path) {
|
|
69
|
+
return existsSync(path);
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=claims-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claims-registry.js","sourceRoot":"","sources":["../src/claims-registry.ts"],"names":[],"mappings":"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"}
|