@golden-frijoles/kit 0.0.0 → 0.2.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 +25 -1
  2. package/bin.mjs +108 -0
  3. package/dist/LICENSE +202 -0
  4. package/dist/NOTICE +7 -0
  5. package/dist/babysit-pr.mjs +213 -0
  6. package/dist/build-order-sync.mjs +141 -0
  7. package/dist/build-order.mjs +178 -0
  8. package/dist/cross-panel.mjs +216 -0
  9. package/dist/cross-panel.prompt.md +115 -0
  10. package/dist/doc-format.enforced.json +4 -0
  11. package/dist/doc-format.mjs +856 -0
  12. package/dist/doc-hygiene.mjs +279 -0
  13. package/dist/lib/cross-agent-cli.mjs +695 -0
  14. package/dist/lib/gh-rest.mjs +229 -0
  15. package/dist/lib/golden-onboarding.mjs +179 -0
  16. package/dist/lib/jev.mjs +334 -0
  17. package/dist/lib/log-branch.mjs +106 -0
  18. package/dist/lib/pmo-benchmarks.mjs +61 -0
  19. package/dist/lib/pmo-delivery.mjs +102 -0
  20. package/dist/lib/pmo-metrics.mjs +200 -0
  21. package/dist/lib/pmo-templates.mjs +132 -0
  22. package/dist/lib/pmo-window-log.mjs +99 -0
  23. package/dist/lib/project-root.mjs +78 -0
  24. package/dist/lib/prose-brief.mjs +191 -0
  25. package/dist/lib/prose-guard.mjs +761 -0
  26. package/dist/lib/prose-writer.mjs +410 -0
  27. package/dist/lib/report-registry.mjs +469 -0
  28. package/dist/lib/reporting-config.mjs +266 -0
  29. package/dist/lib/roadmap-contract.mjs +305 -0
  30. package/dist/lib/roadmap-status-buckets.mjs +20 -0
  31. package/dist/lib/standup-deck.mjs +120 -0
  32. package/dist/lib/telegram-format.mjs +91 -0
  33. package/dist/live-smoke.mjs +303 -0
  34. package/dist/pmo/benchmarks.json +77 -0
  35. package/dist/pmo/templates/metrics-sheet.md +27 -0
  36. package/dist/pmo/templates/monthly-stakeholder-packet.md +53 -0
  37. package/dist/pmo/templates/weekly-story-deck.md +66 -0
  38. package/dist/pmo-report.mjs +338 -0
  39. package/dist/preflight.mjs +444 -0
  40. package/dist/prose/cpo-persona.md +108 -0
  41. package/dist/prose/internal.task.md +48 -0
  42. package/dist/prose/standup.task.md +38 -0
  43. package/dist/prose/weekly.task.md +36 -0
  44. package/dist/prose-draft.mjs +198 -0
  45. package/dist/prose-lessons.md +59 -0
  46. package/dist/roadmap-extract.mjs +410 -0
  47. package/dist/standup/templates/daily-story-deck.md +46 -0
  48. package/dist/standup.mjs +634 -0
  49. package/dist/vercel-prune-previews.mjs +164 -0
  50. package/dist/weekly-recap.mjs +526 -0
  51. package/package.json +33 -1
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env node
2
+ // vercel-prune-previews.mjs — delete stale PREVIEW deployments for a Vercel project.
3
+ //
4
+ // Why: Vercel retains every deployment forever. Deleting a merged git branch does NOT remove its
5
+ // preview deployments — they linger as clutter (a deleted branch can leave a dozen). This prunes
6
+ // them. It NEVER touches production deployments (your rollback history) — only `target !== production`.
7
+ //
8
+ // Usage:
9
+ // node scripts/vercel-prune-previews.mjs --project <name> # DRY-RUN, all previews
10
+ // node scripts/vercel-prune-previews.mjs --project <name> --age 7 # only previews older than 7 days
11
+ // node scripts/vercel-prune-previews.mjs --project <name> --apply # actually delete (dry-run is the default)
12
+ // node scripts/vercel-prune-previews.mjs --project <name> --keep-branch feat/x,feat/y # protect open-PR branches
13
+ //
14
+ // `--project` is REQUIRED and has no default. An earlier version defaulted to the origin project's
15
+ // name, which meant a fresh checkout ran confidently against SOMEONE ELSE'S Vercel project — a
16
+ // wrong-target delete is exactly the failure a default should never be able to cause.
17
+ //
18
+ // ALWAYS pass --keep-branch for any branch that still has an OPEN PR (its preview is the live review
19
+ // target) — or just run after that PR merges. Production deployments are never touched.
20
+ //
21
+ // Token resolution (first that works): VERCEL_API_TOKEN / VERCEL_TOKEN env, else the local Vercel
22
+ // CLI login (auth.json). Team: VERCEL_TEAM_ID env, else auto-detected from the account's first team.
23
+ // Zero npm deps — Node 18+ (global fetch).
24
+
25
+ import { readFileSync, existsSync } from 'node:fs';
26
+ import { join } from 'node:path';
27
+
28
+ const API = 'https://api.vercel.com';
29
+
30
+ function arg(name, def = undefined) {
31
+ const i = process.argv.indexOf(`--${name}`);
32
+ if (i < 0) return def;
33
+ const next = process.argv[i + 1];
34
+ return next && !next.startsWith('--') ? next : true;
35
+ }
36
+ const PROJECT_ARG = arg('project');
37
+ if (!PROJECT_ARG || PROJECT_ARG === true) {
38
+ console.error('vercel-prune-previews: --project <name> is required (no default, by design).');
39
+ console.error(' Pass the Vercel project this repo deploys to. There is deliberately no fallback:');
40
+ console.error(' a defaulted project name means pruning whatever project the default happens to');
41
+ console.error(' point at, which is a destructive no-op at best and the wrong target at worst.');
42
+ process.exit(2);
43
+ }
44
+ const PROJECT = String(PROJECT_ARG);
45
+ const AGE_DAYS = Number(arg('age', 0)); // delete previews strictly older than this many days
46
+ const APPLY = !!arg('apply', false); // dry-run unless --apply
47
+ const KEEP = new Set(
48
+ String(arg('keep-branch', '') || '')
49
+ .split(',')
50
+ .map((s) => s.trim())
51
+ .filter(Boolean)
52
+ );
53
+
54
+ function resolveToken() {
55
+ if (process.env.VERCEL_API_TOKEN) return process.env.VERCEL_API_TOKEN;
56
+ if (process.env.VERCEL_TOKEN) return process.env.VERCEL_TOKEN;
57
+ const candidates = [
58
+ join(process.env.HOME || '', 'Library/Application Support/com.vercel.cli/auth.json'),
59
+ join(
60
+ process.env.XDG_DATA_HOME || join(process.env.HOME || '', '.local/share'),
61
+ 'com.vercel.cli/auth.json'
62
+ ),
63
+ join(process.env.HOME || '', '.vercel/auth.json'),
64
+ ];
65
+ for (const p of candidates) {
66
+ try {
67
+ if (existsSync(p)) {
68
+ const t = JSON.parse(readFileSync(p, 'utf8')).token;
69
+ if (t) return t;
70
+ }
71
+ } catch {}
72
+ }
73
+ console.error('No Vercel token: set VERCEL_API_TOKEN or run `vercel login`.');
74
+ process.exit(1);
75
+ }
76
+ const TOKEN = resolveToken();
77
+ const H = { Authorization: `Bearer ${TOKEN}` };
78
+
79
+ async function api(path) {
80
+ const r = await fetch(`${API}${path}`, { headers: H });
81
+ const j = await r.json();
82
+ if (!r.ok) throw new Error(`${path} → ${r.status} ${JSON.stringify(j).slice(0, 200)}`);
83
+ return j;
84
+ }
85
+
86
+ async function resolveTeamId() {
87
+ if (process.env.VERCEL_TEAM_ID) return process.env.VERCEL_TEAM_ID;
88
+ const j = await api('/v2/teams');
89
+ return j.teams && j.teams[0] && j.teams[0].id ? j.teams[0].id : '';
90
+ }
91
+
92
+ (async () => {
93
+ const teamId = await resolveTeamId();
94
+ const tq = teamId ? `teamId=${teamId}` : '';
95
+ const proj = await api(`/v9/projects/${PROJECT}?${tq}`);
96
+ const projectId = proj.id;
97
+
98
+ // Page all deployments; keep only previews (target !== 'production').
99
+ const now = Date.now();
100
+ let until,
101
+ previews = [],
102
+ total = 0;
103
+ for (let i = 0; i < 100; i++) {
104
+ const j = await api(
105
+ `/v6/deployments?projectId=${projectId}&${tq}&limit=100${until ? `&until=${until}` : ''}`
106
+ );
107
+ if (!j.deployments || !j.deployments.length) break;
108
+ total += j.deployments.length;
109
+ for (const d of j.deployments) {
110
+ if (d.target === 'production') continue; // never touch production
111
+ if (['BUILDING', 'QUEUED', 'INITIALIZING'].includes(d.state)) continue; // skip in-flight
112
+ const branch = (d.meta && (d.meta.githubCommitRef || d.meta.gitBranch)) || '(unknown)';
113
+ if (KEEP.has(branch)) continue; // protect live / open-PR branches
114
+ const ageDays = (now - d.created) / 86400000;
115
+ if (ageDays >= AGE_DAYS) previews.push({ id: d.uid, age: ageDays, branch });
116
+ }
117
+ if (!j.pagination || !j.pagination.next) break;
118
+ until = j.pagination.next;
119
+ }
120
+
121
+ console.log(`Project ${PROJECT} (${projectId}) — scanned ${total} deployments.`);
122
+ console.log(
123
+ `Preview deployments to remove (target!=production${AGE_DAYS ? `, older than ${AGE_DAYS}d` : ''}): ${previews.length}`
124
+ );
125
+ const byBranch = {};
126
+ for (const p of previews) byBranch[p.branch] = (byBranch[p.branch] || 0) + 1;
127
+ Object.entries(byBranch)
128
+ .sort((a, b) => b[1] - a[1])
129
+ .slice(0, 40)
130
+ .forEach(([b, n]) => console.log(` ${String(n).padStart(3)} ${b}`));
131
+
132
+ if (!previews.length) {
133
+ console.log('Nothing to prune.');
134
+ return;
135
+ }
136
+ if (!APPLY) {
137
+ console.log('\nDRY-RUN — re-run with --apply to delete the above.');
138
+ return;
139
+ }
140
+
141
+ console.log(`\nDeleting ${previews.length} preview deployments…`);
142
+ let ok = 0,
143
+ fail = 0,
144
+ idx = 0;
145
+ const worker = async () => {
146
+ while (idx < previews.length) {
147
+ const p = previews[idx++];
148
+ try {
149
+ const r = await fetch(`${API}/v13/deployments/${p.id}?${tq}`, { method: 'DELETE', headers: H });
150
+ if (!r.ok) throw new Error(`${r.status} ${(await r.text()).slice(0, 120)}`);
151
+ ok++;
152
+ } catch (e) {
153
+ fail++;
154
+ console.error(` ! ${p.id} (${p.branch}): ${e.message}`);
155
+ }
156
+ }
157
+ };
158
+ await Promise.all(Array.from({ length: 8 }, worker)); // small concurrency pool
159
+ console.log(`Done — deleted ${ok}, failed ${fail}.`);
160
+ if (fail) process.exit(1);
161
+ })().catch((e) => {
162
+ console.error(e.message || e);
163
+ process.exit(1);
164
+ });