compounding-loop 0.1.0 → 0.4.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.
- package/README.md +40 -6
- package/dist/cli/checkLanes.js +60 -0
- package/dist/cli/checkLanes.js.map +1 -0
- package/dist/cli/claim.js +84 -0
- package/dist/cli/claim.js.map +1 -0
- package/dist/cli/doctor.js +54 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/index.js +253 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.js +2 -1
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/laneRound.js +96 -0
- package/dist/cli/laneRound.js.map +1 -0
- package/dist/cli/laneStatus.js +54 -0
- package/dist/cli/laneStatus.js.map +1 -0
- package/dist/cli/lanesInit.js +56 -0
- package/dist/cli/lanesInit.js.map +1 -0
- package/dist/cli/lock.js +62 -0
- package/dist/cli/lock.js.map +1 -0
- package/dist/cli/loops.js +30 -3
- package/dist/cli/loops.js.map +1 -1
- package/dist/cli/new.js +5 -5
- package/dist/cli/new.js.map +1 -1
- package/dist/cli/next.js +18 -0
- package/dist/cli/next.js.map +1 -0
- package/dist/cli/pause.js +81 -0
- package/dist/cli/pause.js.map +1 -0
- package/dist/cli/run.js +119 -19
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/runs.js +83 -0
- package/dist/cli/runs.js.map +1 -0
- package/dist/cli/shipCheck.js +59 -0
- package/dist/cli/shipCheck.js.map +1 -0
- package/dist/core/doctor.js +49 -0
- package/dist/core/doctor.js.map +1 -0
- package/dist/core/laneRound.js +63 -0
- package/dist/core/laneRound.js.map +1 -0
- package/dist/core/laneStatus.js +82 -0
- package/dist/core/laneStatus.js.map +1 -0
- package/dist/core/lanes.js +149 -0
- package/dist/core/lanes.js.map +1 -0
- package/dist/core/lanesInit.js +143 -0
- package/dist/core/lanesInit.js.map +1 -0
- package/dist/core/next.js +34 -0
- package/dist/core/next.js.map +1 -0
- package/dist/core/parse/changelog.js +41 -0
- package/dist/core/parse/changelog.js.map +1 -0
- package/dist/core/parse/claim.js +24 -0
- package/dist/core/parse/claim.js.map +1 -0
- package/dist/core/parse/controlRoom.js +38 -0
- package/dist/core/parse/controlRoom.js.map +1 -0
- package/dist/core/parse/index.js +12 -0
- package/dist/core/parse/index.js.map +1 -0
- package/dist/core/parse/markers.js +20 -0
- package/dist/core/parse/markers.js.map +1 -0
- package/dist/core/parse/paused.js +26 -0
- package/dist/core/parse/paused.js.map +1 -0
- package/dist/core/parse/runs.js +132 -0
- package/dist/core/parse/runs.js.map +1 -0
- package/dist/core/parse/task.js +96 -0
- package/dist/core/parse/task.js.map +1 -0
- package/dist/core/repo.js +2 -7
- package/dist/core/repo.js.map +1 -1
- package/dist/core/runPrompt.js +9 -3
- package/dist/core/runPrompt.js.map +1 -1
- package/dist/core/shipCheck.js +45 -0
- package/dist/core/shipCheck.js.map +1 -0
- package/dist/core/table.js +22 -1
- package/dist/core/table.js.map +1 -1
- package/dist/server/checkLabels.js +27 -0
- package/dist/server/checkLabels.js.map +1 -0
- package/dist/server/controls.js +72 -0
- package/dist/server/controls.js.map +1 -0
- package/dist/server/data.js +28 -8
- package/dist/server/data.js.map +1 -1
- package/dist/server/events.js +1 -0
- package/dist/server/events.js.map +1 -1
- package/dist/server/health.js +47 -8
- package/dist/server/health.js.map +1 -1
- package/dist/server/index.js +29 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/laneChecks.js +25 -0
- package/dist/server/laneChecks.js.map +1 -0
- package/dist/server/live/build.js +163 -0
- package/dist/server/live/build.js.map +1 -0
- package/dist/server/live/github.js +54 -0
- package/dist/server/live/github.js.map +1 -0
- package/dist/server/live/index.js +9 -0
- package/dist/server/live/index.js.map +1 -0
- package/dist/server/live/merge.js +16 -0
- package/dist/server/live/merge.js.map +1 -0
- package/dist/server/live/urls.js +59 -0
- package/dist/server/live/urls.js.map +1 -0
- package/dist/server/loops.js +146 -39
- package/dist/server/loops.js.map +1 -1
- package/dist/server/roundState.js +27 -0
- package/dist/server/roundState.js.map +1 -0
- package/dist/server/runs.js +81 -0
- package/dist/server/runs.js.map +1 -0
- package/dist/server/static-export.js +22 -1
- package/dist/server/static-export.js.map +1 -1
- package/dist/server/taskBullets.js +33 -0
- package/dist/server/taskBullets.js.map +1 -0
- package/dist/server/this-repo.json +1574 -0
- package/dist/web/assets/index-BOPytDNU.js +53 -0
- package/dist/web/assets/index-BSHEI7fa.css +2 -0
- package/dist/web/index.html +3 -3
- package/method/COMPOUNDING_LOOP.md +55 -0
- package/method/example/K-0002.md +16 -0
- package/method/example/K-0003.md +16 -0
- package/method/retro.md +7 -0
- package/package.json +6 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/commands/loop-lanes.md +11 -0
- package/plugin/commands/loop-next.md +11 -0
- package/plugin/commands/loop-pause.md +10 -0
- package/runners/github-actions/lanes.yml +156 -0
- package/runners/local/README.md +4 -1
- package/runners/local/compounding-loop.service +20 -0
- package/runners/local/compounding-loop.timer +11 -0
- package/runners/local/launchd.plist +36 -0
- package/runners/routine/README.md +38 -1
- package/runners/routine/control-room-prompt.md +16 -0
- package/runners/routine/lane-prompt.md +12 -0
- package/runners/routine/prompt.md +3 -3
- package/templates/chrome-extension/extension/icons/icon128.png +0 -0
- package/templates/chrome-extension/extension/icons/icon16.png +0 -0
- package/templates/chrome-extension/extension/icons/icon48.png +0 -0
- package/templates/chrome-extension/extension/manifest.json +19 -3
- package/templates/chrome-extension/gitignore +1 -1
- package/templates/lanes/README.md +23 -0
- package/templates/lanes/ci-check-lanes.yml +38 -0
- package/templates/lanes/claude-section.md +20 -0
- package/templates/lanes/control-room.md +3 -0
- package/templates/lanes/outbox.md +5 -0
- package/templates/lanes/task.md +36 -0
- package/templates/static-site/.ai/loop-prompt.md +2 -2
- package/templates/static-site/CLAUDE.md +2 -0
- package/templates/static-site/gitignore +1 -1
- package/dist/web/assets/index-BDdtnG33.js +0 -49
- package/dist/web/assets/index-D3-N8DN4.css +0 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markers.js","sourceRoot":"","sources":["../../../src/core/parse/markers.ts"],"names":[],"mappings":"AAAA,sFAAsF;AAEtF,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAa5C,MAAM,UAAU,gBAAgB,CAAC,KAAwB;IACvD,MAAM,OAAO,GAAgB,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC;IACrH,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC;QAC5C,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;aAC3C,IAAI,IAAI,KAAK,YAAY;YAAE,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;aACtD,IAAI,IAAI,KAAK,YAAY;YAAE,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;aACjD,CAAC;YACJ,MAAM,KAAK,GAAG,4CAA4C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtE,IAAI,KAAK;gBAAE,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAW,CAAC,CAAC;QACvG,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// `.ai/PAUSED`: one line, `who · paused <UTC ISO>[ · reason]`. Its existence stops every lane run. Pure.
|
|
2
|
+
export const PAUSED_FILE = '.ai/PAUSED';
|
|
3
|
+
export function formatPaused(by, at, reason) {
|
|
4
|
+
return `${by} · paused ${at.toISOString()}${reason ? ` · ${reason}` : ''}\n`;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Reads a PAUSED file. The file's existence is what pauses the loop, so a hand-written or malformed file still
|
|
8
|
+
* yields a value (`by` is then its first line).
|
|
9
|
+
*/
|
|
10
|
+
export function parsePaused(text) {
|
|
11
|
+
const line = text.split(/\r?\n/).find((l) => l.trim() !== '')?.trim() ?? '';
|
|
12
|
+
const match = /^(.+?)\s+·\s+paused\s+(\S+)(?:\s+·\s+(.+))?$/.exec(line);
|
|
13
|
+
const at = match ? new Date(match[2]) : undefined;
|
|
14
|
+
if (!match || !at || Number.isNaN(at.getTime()))
|
|
15
|
+
return { by: line || 'unknown' };
|
|
16
|
+
return { by: match[1].trim(), at, ...(match[3] && { reason: match[3].trim() }) };
|
|
17
|
+
}
|
|
18
|
+
/** True when the repo-relative file list contains `.ai/PAUSED`. */
|
|
19
|
+
export function isPaused(files) {
|
|
20
|
+
return files.some((f) => f.replace(/\\/g, '/') === PAUSED_FILE);
|
|
21
|
+
}
|
|
22
|
+
/** One line for `loop status` and the CLI: who paused and when. */
|
|
23
|
+
export function describePaused(paused) {
|
|
24
|
+
return `Paused by ${paused.by}${paused.at ? ` since ${paused.at.toISOString().slice(0, 16).replace('T', ' ')} UTC` : ''}${paused.reason ? ` (${paused.reason})` : ''}`;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=paused.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paused.js","sourceRoot":"","sources":["../../../src/core/parse/paused.ts"],"names":[],"mappings":"AAAA,yGAAyG;AASzG,MAAM,CAAC,MAAM,WAAW,GAAG,YAAY,CAAC;AAExC,MAAM,UAAU,YAAY,CAAC,EAAU,EAAE,EAAQ,EAAE,MAAe;IAChE,OAAO,GAAG,EAAE,aAAa,EAAE,CAAC,WAAW,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC;AAC/E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAC5E,MAAM,KAAK,GAAG,8CAA8C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxE,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5D,IAAI,CAAC,KAAK,IAAI,CAAC,EAAE,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,IAAI,SAAS,EAAE,CAAC;IAClF,OAAO,EAAE,EAAE,EAAG,KAAK,CAAC,CAAC,CAAY,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC;AAC/F,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,QAAQ,CAAC,KAAwB;IAC/C,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,KAAK,WAAW,CAAC,CAAC;AAClE,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,OAAO,aAAa,MAAM,CAAC,EAAE,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AACzK,CAAC"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// The per-lane run log (`.ai/lanes/<lane>/runs.jsonl`) and the scheduled slots a lane's cron promises. Pure.
|
|
2
|
+
export const runLogPath = (lane) => `.ai/lanes/${lane}/runs.jsonl`;
|
|
3
|
+
export function formatRunEntry(entry) {
|
|
4
|
+
return `${JSON.stringify(entry)}\n`;
|
|
5
|
+
}
|
|
6
|
+
const isCount = (v) => typeof v === 'number' && Number.isInteger(v) && v >= 0;
|
|
7
|
+
function toEntry(value) {
|
|
8
|
+
if (!value || typeof value !== 'object')
|
|
9
|
+
return undefined;
|
|
10
|
+
const v = value;
|
|
11
|
+
if (typeof v.at !== 'string' || Number.isNaN(new Date(v.at).getTime()) || typeof v.lane !== 'string' || typeof v.pushed !== 'boolean')
|
|
12
|
+
return undefined;
|
|
13
|
+
const tests = v.tests;
|
|
14
|
+
return {
|
|
15
|
+
at: v.at,
|
|
16
|
+
lane: v.lane,
|
|
17
|
+
...(typeof v.session === 'string' && { session: v.session }),
|
|
18
|
+
units: Array.isArray(v.units) ? v.units.filter((u) => typeof u === 'string') : [],
|
|
19
|
+
...(tests && isCount(tests.passed) && isCount(tests.failed) && { tests: { passed: tests.passed, failed: tests.failed } }),
|
|
20
|
+
pushed: v.pushed,
|
|
21
|
+
...(typeof v.note === 'string' && { note: v.note }),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/** Reads a runs.jsonl; lines that are not valid entries (a half-written line, a merge marker) are skipped. Oldest first. */
|
|
25
|
+
export function parseRunLog(text) {
|
|
26
|
+
const entries = [];
|
|
27
|
+
for (const line of text.split(/\r?\n/)) {
|
|
28
|
+
if (!line.trim())
|
|
29
|
+
continue;
|
|
30
|
+
try {
|
|
31
|
+
const entry = toEntry(JSON.parse(line));
|
|
32
|
+
if (entry)
|
|
33
|
+
entries.push(entry);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// skipped on purpose: see above
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return entries.sort((a, b) => a.at.localeCompare(b.at));
|
|
40
|
+
}
|
|
41
|
+
// ---- cron ----
|
|
42
|
+
const RANGES = [[0, 59], [0, 23], [1, 31], [1, 12], [0, 7]];
|
|
43
|
+
/** Expands one cron field (star, a number, a list, a range, or a step such as star-slash-15) into the matching numbers; undefined when unreadable. */
|
|
44
|
+
function expandField(field, [min, max]) {
|
|
45
|
+
const out = new Set();
|
|
46
|
+
for (const part of field.split(',')) {
|
|
47
|
+
const match = /^(\*|\d+(?:-\d+)?)(?:\/(\d+))?$/.exec(part);
|
|
48
|
+
if (!match)
|
|
49
|
+
return undefined;
|
|
50
|
+
const step = match[2] === undefined ? 1 : Number(match[2]);
|
|
51
|
+
if (step < 1)
|
|
52
|
+
return undefined;
|
|
53
|
+
let [from, to] = [min, max];
|
|
54
|
+
if (match[1] !== '*') {
|
|
55
|
+
const [a, b] = match[1].split('-');
|
|
56
|
+
from = Number(a);
|
|
57
|
+
to = b === undefined ? (match[2] === undefined ? from : max) : Number(b);
|
|
58
|
+
}
|
|
59
|
+
if (from < min || to > max || from > to)
|
|
60
|
+
return undefined;
|
|
61
|
+
for (let n = from; n <= to; n += step)
|
|
62
|
+
out.add(n);
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The UTC minutes in (from, to] at which a 5-field cron expression fires. Empty for an expression this cannot read.
|
|
68
|
+
* Like cron, day-of-month and day-of-week both restrict when both are set (either may match).
|
|
69
|
+
*/
|
|
70
|
+
export function expectedSlots(cron, from, to) {
|
|
71
|
+
const fields = cron.trim().split(/\s+/);
|
|
72
|
+
if (fields.length !== 5)
|
|
73
|
+
return [];
|
|
74
|
+
const sets = fields.map((f, i) => expandField(f, RANGES[i]));
|
|
75
|
+
const [minutes, hours, days, months, weekdays] = sets;
|
|
76
|
+
if (!minutes || !hours || !days || !months || !weekdays)
|
|
77
|
+
return [];
|
|
78
|
+
const dayRestricted = fields[2] !== '*';
|
|
79
|
+
const weekdayRestricted = fields[4] !== '*';
|
|
80
|
+
const slots = [];
|
|
81
|
+
const start = Math.floor(from.getTime() / 60_000) * 60_000 + 60_000;
|
|
82
|
+
for (let t = start; t <= to.getTime() && slots.length < 10_000; t += 60_000) {
|
|
83
|
+
const d = new Date(t);
|
|
84
|
+
if (!minutes.has(d.getUTCMinutes()) || !hours.has(d.getUTCHours()) || !months.has(d.getUTCMonth() + 1))
|
|
85
|
+
continue;
|
|
86
|
+
const dayOk = days.has(d.getUTCDate());
|
|
87
|
+
const weekdayOk = weekdays.has(d.getUTCDay()) || (d.getUTCDay() === 0 && weekdays.has(7));
|
|
88
|
+
if (dayRestricted && weekdayRestricted ? dayOk || weekdayOk : dayOk && weekdayOk)
|
|
89
|
+
slots.push(d);
|
|
90
|
+
}
|
|
91
|
+
return slots;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Pairs each scheduled slot with the run that answered it. A run belongs to the latest slot at or before its end
|
|
95
|
+
* time. A slot with no line but a commit before the next slot still counts as run (the agent forgot the log step)
|
|
96
|
+
* and is reported as `pushed`. Anything else is "no run recorded (usage limit, idle or failed)".
|
|
97
|
+
*/
|
|
98
|
+
export function matchSlots(slots, entries, commitTimes = []) {
|
|
99
|
+
const sorted = [...slots].sort((a, b) => a.getTime() - b.getTime());
|
|
100
|
+
const slotFor = (time) => {
|
|
101
|
+
let found = -1;
|
|
102
|
+
for (let i = 0; i < sorted.length && sorted[i].getTime() <= time; i++)
|
|
103
|
+
found = i;
|
|
104
|
+
return found;
|
|
105
|
+
};
|
|
106
|
+
const bySlot = new Map();
|
|
107
|
+
for (const entry of entries) {
|
|
108
|
+
// A long run can end after the next slot starts: walk back to the latest slot nobody has claimed yet.
|
|
109
|
+
for (let i = slotFor(new Date(entry.at).getTime()); i >= 0; i--) {
|
|
110
|
+
if (!bySlot.has(i)) {
|
|
111
|
+
bySlot.set(i, entry);
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const committed = new Set(commitTimes.map((c) => slotFor(c.getTime())).filter((i) => i >= 0));
|
|
117
|
+
return sorted.map((slot, i) => {
|
|
118
|
+
const entry = bySlot.get(i);
|
|
119
|
+
const iso = slot.toISOString();
|
|
120
|
+
if (entry)
|
|
121
|
+
return { slot: iso, entry, outcome: entry.tests && entry.tests.failed > 0 ? 'tests-failed' : entry.pushed ? 'pushed' : 'nothing-pushed' };
|
|
122
|
+
return { slot: iso, outcome: committed.has(i) ? 'pushed' : 'no-run-recorded' };
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/** How many scheduled runs in a row, newest first, left no trace. The dashboard raises an attention item at 3. */
|
|
126
|
+
export function missedInARow(results) {
|
|
127
|
+
let count = 0;
|
|
128
|
+
for (let i = results.length - 1; i >= 0 && results[i]?.outcome === 'no-run-recorded'; i--)
|
|
129
|
+
count++;
|
|
130
|
+
return count;
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=runs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runs.js","sourceRoot":"","sources":["../../../src/core/parse/runs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// Task-file parsing. Pure (no Node imports): the live dashboard runs it in the browser.
|
|
2
|
+
/** Reads the `Run: N / LIMIT` line of a task.md; undefined when the line is missing or malformed. */
|
|
3
|
+
export function parseRunCounter(taskText) {
|
|
4
|
+
const match = /^Run:\s*(\d+)\s*\/\s*(\d+)\b/m.exec(taskText);
|
|
5
|
+
if (!match)
|
|
6
|
+
return undefined;
|
|
7
|
+
return { run: Number(match[1]), limit: Number(match[2]) };
|
|
8
|
+
}
|
|
9
|
+
/** Body of the `## <heading>` section (up to the next `## `), matched by heading prefix, case-insensitive. Handoffs are appended, so callers wanting the newest ask for `last`. */
|
|
10
|
+
export function sectionOf(text, heading, which = 'first') {
|
|
11
|
+
const lines = text.split(/\r?\n/);
|
|
12
|
+
const want = heading.toLowerCase();
|
|
13
|
+
const isMatch = (l) => /^##\s/.test(l) && l.replace(/^##\s+/, '').trim().toLowerCase().startsWith(want);
|
|
14
|
+
const start = which === 'last' ? lines.map(isMatch).lastIndexOf(true) : lines.findIndex(isMatch);
|
|
15
|
+
if (start < 0)
|
|
16
|
+
return undefined;
|
|
17
|
+
const rest = lines.slice(start + 1);
|
|
18
|
+
const end = rest.findIndex((l) => /^##\s/.test(l));
|
|
19
|
+
return (end < 0 ? rest : rest.slice(0, end)).join('\n');
|
|
20
|
+
}
|
|
21
|
+
export function parseUnits(taskText) {
|
|
22
|
+
const body = sectionOf(taskText, 'Units');
|
|
23
|
+
if (!body)
|
|
24
|
+
return [];
|
|
25
|
+
const units = [];
|
|
26
|
+
for (const line of body.split(/\r?\n/)) {
|
|
27
|
+
const match = /^(\d+)\.\s+(.+?)\s*$/.exec(line);
|
|
28
|
+
if (match)
|
|
29
|
+
units.push({ number: Number(match[1]), text: match[2] });
|
|
30
|
+
}
|
|
31
|
+
return units;
|
|
32
|
+
}
|
|
33
|
+
/** Parses the parts of a lane's task.md that status screens need. Missing parts are simply absent. */
|
|
34
|
+
export function parseTaskFile(taskText) {
|
|
35
|
+
const status = /^Status:[ \t]*(.*)$/m.exec(taskText)?.[1]?.trim();
|
|
36
|
+
const handoff = sectionOf(taskText, 'Handoff', 'last')
|
|
37
|
+
?.split(/\r?\n/)
|
|
38
|
+
.map((l) => l.trim())
|
|
39
|
+
.find((l) => l !== '');
|
|
40
|
+
const parsed = { units: parseUnits(taskText) };
|
|
41
|
+
const run = parseRunCounter(taskText);
|
|
42
|
+
if (run)
|
|
43
|
+
parsed.run = run;
|
|
44
|
+
if (status)
|
|
45
|
+
parsed.status = status;
|
|
46
|
+
if (handoff)
|
|
47
|
+
parsed.handoffFirstLine = handoff;
|
|
48
|
+
return parsed;
|
|
49
|
+
}
|
|
50
|
+
const NUMBER_LIST = String.raw `\d+(?:\s*[-\u2013]\s*\d+)?(?:\s*(?:,|and|&)\s*\d+(?:\s*[-\u2013]\s*\d+)?)*`;
|
|
51
|
+
const DONE_AFTER = /\b(?:shipped|finished|completed|done)\b[:\s]+([^\n]*)/gi;
|
|
52
|
+
const UNITS_IN_LINE = new RegExp(String.raw `\bunits?\s+(${NUMBER_LIST})\b(?!\s+(?:partly|partially|not))`, 'gi');
|
|
53
|
+
const BARE_LIST = new RegExp(String.raw `^\s*(?:with\s+)?(${NUMBER_LIST})\b(?!\s+(?:partly|partially|not))`, 'i');
|
|
54
|
+
const DONE_BEFORE = new RegExp(String.raw `\bunits?\s+(${NUMBER_LIST})\s+(?:are\s+|is\s+|were\s+)?(?:done|shipped|finished|complete[d]?)\b`, 'gi');
|
|
55
|
+
function expandNumbers(list, into) {
|
|
56
|
+
for (const part of list.split(/,|and|&/)) {
|
|
57
|
+
const range = /^\s*(\d+)(?:\s*[-\u2013]\s*(\d+))?\s*$/.exec(part);
|
|
58
|
+
if (!range)
|
|
59
|
+
continue;
|
|
60
|
+
const from = Number(range[1]);
|
|
61
|
+
const to = range[2] === undefined ? from : Number(range[2]);
|
|
62
|
+
for (let n = from; n <= Math.min(to, from + 200); n++)
|
|
63
|
+
into.add(n);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Unit numbers a lane says it finished, read from its `Status:` line and newest Handoff, e.g.
|
|
68
|
+
* "units 1-3 done" or "Run 2 shipped units 1, 2 and 4". "unit 5 partly" does not count.
|
|
69
|
+
*/
|
|
70
|
+
export function doneUnits(taskText) {
|
|
71
|
+
const status = /^Status:[ \t]*(.*)$/m.exec(taskText)?.[1] ?? '';
|
|
72
|
+
const handoff = sectionOf(taskText, 'Handoff', 'last') ?? '';
|
|
73
|
+
// Only the lines before "Next:" describe finished work; what follows plans it.
|
|
74
|
+
const finished = handoff.split(/\r?\n/).filter((l) => !/^\s*next\b/i.test(l)).join('\n');
|
|
75
|
+
const done = new Set();
|
|
76
|
+
for (const text of [status, finished]) {
|
|
77
|
+
for (const m of text.matchAll(DONE_BEFORE))
|
|
78
|
+
expandNumbers(m[1], done);
|
|
79
|
+
for (const m of text.matchAll(DONE_AFTER)) {
|
|
80
|
+
const rest = m[1];
|
|
81
|
+
const named = [...rest.matchAll(UNITS_IN_LINE)];
|
|
82
|
+
for (const u of named)
|
|
83
|
+
expandNumbers(u[1], done);
|
|
84
|
+
const bare = named.length === 0 ? BARE_LIST.exec(rest) : null;
|
|
85
|
+
if (bare)
|
|
86
|
+
expandNumbers(bare[1], done);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return done;
|
|
90
|
+
}
|
|
91
|
+
/** Units not yet reported done, in file order. */
|
|
92
|
+
export function openUnits(taskText) {
|
|
93
|
+
const done = doneUnits(taskText);
|
|
94
|
+
return parseUnits(taskText).filter((u) => !done.has(u.number));
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=task.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task.js","sourceRoot":"","sources":["../../../src/core/parse/task.ts"],"names":[],"mappings":"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"}
|
package/dist/core/repo.js
CHANGED
|
@@ -2,14 +2,9 @@ import { execFile } from 'node:child_process';
|
|
|
2
2
|
import { readdir, readFile, stat } from 'node:fs/promises';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { promisify } from 'node:util';
|
|
5
|
+
import { parseRunCounter } from './parse/task.js';
|
|
5
6
|
const execFileAsync = promisify(execFile);
|
|
6
|
-
|
|
7
|
-
export function parseRunCounter(taskText) {
|
|
8
|
-
const match = /^Run:\s*(\d+)\s*\/\s*(\d+)\b/m.exec(taskText);
|
|
9
|
-
if (!match)
|
|
10
|
-
return undefined;
|
|
11
|
-
return { run: Number(match[1]), limit: Number(match[2]) };
|
|
12
|
-
}
|
|
7
|
+
export { parseRunCounter };
|
|
13
8
|
/** Parses the single UTC timestamp line of `.ai/session.lock`; undefined when it is not a valid date. */
|
|
14
9
|
export function parseLockTime(lockText) {
|
|
15
10
|
const line = lockText.trim().split(/\r?\n/)[0]?.trim();
|
package/dist/core/repo.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repo.js","sourceRoot":"","sources":["../../src/core/repo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"repo.js","sourceRoot":"","sources":["../../src/core/repo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAIlD,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE1C,OAAO,EAAE,eAAe,EAAE,CAAC;AAG3B,yGAAyG;AACzG,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC5C,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;IACvD,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5B,gGAAgG;IAChG,MAAM,OAAO,GAAG,0BAA0B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtD,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;IACnE,OAAO,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;AACrD,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,eAAe,CAAC,SAA4B;IAC1D,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;AACzE,CAAC;AAED,KAAK,UAAU,MAAM,CAAC,IAAY;IAChC,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,IAAY;IACtC,IAAI,CAAC;QACH,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,6FAA6F;AAC7F,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,GAAW;IAChD,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE;YAC3E,GAAG,EAAE,GAAG;YACR,6FAA6F;YAC7F,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,uBAAuB,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE;SAClF,CAAC,CAAC;QACH,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QACnC,OAAO,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,4GAA4G;AAC5G,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAW;IAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACpC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAc,CAAC,CAAC;IACjE,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC;IACtE,MAAM,MAAM,GAAG,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IAC5E,MAAM,YAAY,GAAG,MAAM,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACjD,OAAO;QACL,wEAAwE;QACxE,UAAU,EAAE,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;QACtD,OAAO,EAAE,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAChD,UAAU,EAAE,eAAe,CAAC,OAAO,CAAC;QACpC,GAAG,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,CAAC;QACzB,GAAG,CAAC,YAAY,IAAI,EAAE,YAAY,EAAE,CAAC;QACrC,cAAc,EAAE,EAAE;KACnB,CAAC;AACJ,CAAC;AAED,0DAA0D;AAC1D,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAW;IAC9C,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC;IAClE,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;AAChE,CAAC"}
|
package/dist/core/runPrompt.js
CHANGED
|
@@ -2,9 +2,9 @@ import { fillPlaceholders } from './template.js';
|
|
|
2
2
|
/** Used only when neither the repo nor the package ships a prompt file. A copy of runners/routine/prompt.md; a test keeps them equal. */
|
|
3
3
|
export const DEFAULT_PROMPT = `You are one scheduled run of the build loop for this repository ({{name}}). Nobody is watching; the owner reads the results later. Read CLAUDE.md and follow it exactly, especially "Unattended mode". IDEA.md is the brief. Your task file is \`.ai/task.md\`.
|
|
4
4
|
|
|
5
|
-
0. Run \`date -u\` and note it as this run's start time. Run \`git checkout main && git pull --ff-only\`. Every commit goes to \`main\` and is pushed with \`git push origin main\`. Never create or push a \`claude/\` branch: the next run starts from \`main\` and would never see it. If a push is rejected, run \`git pull --rebase\`, re-run the checks if incoming commits touched code, and push again.
|
|
6
|
-
1. Session lock: if \`.ai/session.lock\` holds a UTC time less than 90 minutes old, another session owns the repo.
|
|
7
|
-
2. If \`DONE.md\` or \`BLOCKED.md\` exists, reply "Nothing to do" and stop.
|
|
5
|
+
0. Run \`date -u\` and note it as this run's start time. Run \`git checkout main && git pull --ff-only\`. If that pull fails because local \`main\` and \`origin/main\` have diverged or look unrelated, the sandbox started from a stale cached clone; origin \`main\` is never rewritten, so do not reset anything: run \`git fetch origin main && git switch -c lane-run FETCH_HEAD\`, work there, and wherever these steps say pull or push use \`git pull --rebase origin main\` and \`git push origin HEAD:main\`. Every commit goes to \`main\` and is pushed with \`git push origin main\`. Never create or push a \`claude/\` branch: the next run starts from \`main\` and would never see it. If a push is rejected, run \`git pull --rebase\`, re-run the checks if incoming commits touched code, and push again.
|
|
6
|
+
1. Session lock: if \`.ai/session.lock\` holds a UTC time less than 90 minutes old, another session owns the repo, unless the lock is your own (the runner's run note says so when the loop runner created it). Otherwise reply "Nothing to do" and stop. If the file is older than that, it is stale: ignore it.
|
|
7
|
+
2. If \`DONE.md\` or \`BLOCKED.md\` exists, reply "Nothing to do" and stop. Pause: if \`.ai/PAUSED\` exists, the owner paused the loop: reply "Paused" and stop.
|
|
8
8
|
3. Run the install command from CLAUDE.md (\`npm install\` unless it says otherwise). If it fails, write \`BLOCKED.md\` with the error and stop.
|
|
9
9
|
4. If \`.ai/task.md\` exists, run the resume protocol (.ai/method.md §9.2) and add 1 to its \`Run: N / 30\` counter. When the counter reaches 30, write \`BLOCKED.md\` saying "run budget reached", push, and stop. If there is no task file, write one from IDEA.md first.
|
|
10
10
|
5. Run the test command from CLAUDE.md (\`npm test\` unless it says otherwise) on the fresh pull. If it fails, fixing it is your first unit ("main stays green"). Otherwise take the next unit from the task file. Do one unit at a time.
|
|
@@ -13,6 +13,12 @@ export const DEFAULT_PROMPT = `You are one scheduled run of the build loop for t
|
|
|
13
13
|
8. Run the handoff (§9.1) in the task file so a fresh run with zero memory can continue at full quality. Commit it, push to \`main\`, and stop. If every done-when in the contract passes and the reviewer finds no blockers, run the retro (§12), write \`DONE.md\` (what was built, test results, anything unsure, the 10 best next improvements ranked by value) and push instead.
|
|
14
14
|
|
|
15
15
|
Never spend money, sign up for anything, create accounts, publish anything, add API keys, secrets, ads, analytics or tracking, copy other projects' content without a compatible licence, force-push, rewrite history, or touch anything outside this repository.`;
|
|
16
|
+
/** The claim rule for a lane run (IDEA.md Round 3): a teammate's unexpired claim means this lane is taken. */
|
|
17
|
+
export function claimRule(lane) {
|
|
18
|
+
return `Claim: if \`.ai/lanes/${lane}/claim\` exists (one line, \`name · expires <UTC ISO>\`), names someone other than you (compare with \`git config user.name\`; an unattended scheduled run has no name, so any unexpired claim stops it), and its expiry is later than \`date -u\`, a teammate is working this lane: reply "Nothing to do" and stop. An expired claim counts as none; leave the file, \`loop claim\` overwrites it.`;
|
|
19
|
+
}
|
|
20
|
+
/** The pause rule (IDEA.md Round 4 B1): `.ai/PAUSED` stops every run like a root DONE.md. */
|
|
21
|
+
export const PAUSED_RULE = 'Pause: if `.ai/PAUSED` exists, the owner paused the loop: reply "Paused" and stop.';
|
|
16
22
|
/** Fills `{{name}}`-style placeholders and normalises the ending. */
|
|
17
23
|
export function buildRunPrompt(promptText, vars) {
|
|
18
24
|
return `${fillPlaceholders(promptText, vars).trim()}\n`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runPrompt.js","sourceRoot":"","sources":["../../src/core/runPrompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjD,yIAAyI;AACzI,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;;;;kQAYoO,CAAC;AAEnQ,qEAAqE;AACrE,MAAM,UAAU,cAAc,CAAC,UAAkB,EAAE,IAAsC;IACvF,OAAO,GAAG,gBAAgB,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC;AAC1D,CAAC;AAQD,qJAAqJ;AACrJ,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,CAAU,CAAC;AAE5G,oGAAoG;AACpG,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,OAA0B;IACxE,OAAO;QACL,IAAI;QACJ,MAAM;QACN,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACpD,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,gCAAgC,CAAC,CAAC,CAAC,CAAC,CAAC,mBAAmB,EAAE,aAAa,EAAE,gBAAgB,EAAE,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;KAClJ,CAAC;AACJ,CAAC"}
|
|
1
|
+
{"version":3,"file":"runPrompt.js","sourceRoot":"","sources":["../../src/core/runPrompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjD,yIAAyI;AACzI,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;;;;kQAYoO,CAAC;AAEnQ,8GAA8G;AAC9G,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,OAAO,yBAAyB,IAAI,qYAAqY,CAAC;AAC5a,CAAC;AAED,6FAA6F;AAC7F,MAAM,CAAC,MAAM,WAAW,GAAG,oFAAoF,CAAC;AAEhH,qEAAqE;AACrE,MAAM,UAAU,cAAc,CAAC,UAAkB,EAAE,IAAsC;IACvF,OAAO,GAAG,gBAAgB,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC;AAC1D,CAAC;AAQD,qJAAqJ;AACrJ,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,CAAU,CAAC;AAE5G,oGAAoG;AACpG,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,OAA0B;IACxE,OAAO;QACL,IAAI;QACJ,MAAM;QACN,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACpD,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,gCAAgC,CAAC,CAAC,CAAC,CAAC,CAAC,mBAAmB,EAAE,aAAa,EAAE,gBAAgB,EAAE,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;KAClJ,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// "Is it ready to ship?" as a checklist (IDEA.md Round 4 B4). Pure and browser-safe: the CLI and the dashboard share it.
|
|
2
|
+
import { listItems } from './brief.js';
|
|
3
|
+
import { parseChangelog, releaseFor } from './parse/changelog.js';
|
|
4
|
+
const FINDINGS_HEADING = /^#{1,6}\s+.*\b(open findings?|minor findings|unsure|not verified|known (?:issues|problems)|still open)\b/i;
|
|
5
|
+
/** Bullets under DONE.md headings that name open or uncertain things. */
|
|
6
|
+
export function openFindings(doneText) {
|
|
7
|
+
const lines = doneText.split(/\r?\n/);
|
|
8
|
+
const found = [];
|
|
9
|
+
for (let i = 0; i < lines.length; i++) {
|
|
10
|
+
if (!FINDINGS_HEADING.test(lines[i]))
|
|
11
|
+
continue;
|
|
12
|
+
const rest = lines.slice(i + 1);
|
|
13
|
+
const end = rest.findIndex((l) => /^#{1,6}\s/.test(l));
|
|
14
|
+
found.push(...listItems((end === -1 ? rest : rest.slice(0, end)).join('\n')));
|
|
15
|
+
}
|
|
16
|
+
return found;
|
|
17
|
+
}
|
|
18
|
+
const NONE = /^(none|nothing|n\/a|no (?:open )?findings?)\b/i;
|
|
19
|
+
export function shipCheck(facts) {
|
|
20
|
+
const findings = facts.doneText === undefined ? [] : openFindings(facts.doneText).filter((f) => !NONE.test(f));
|
|
21
|
+
const blockers = findings.filter((f) => /\bblocker\b/i.test(f) && !/\b(?:no|not an?|non-?|zero|without)\s+(?:open\s+)?blockers?\b/i.test(f));
|
|
22
|
+
const release = facts.changelogText === undefined ? undefined : releaseFor(parseChangelog(facts.changelogText), facts.version);
|
|
23
|
+
const unreleased = facts.changelogText === undefined ? undefined : releaseFor(parseChangelog(facts.changelogText), 'Unreleased');
|
|
24
|
+
const changelogOk = (release !== undefined && release.items.length > 0) || (unreleased !== undefined && unreleased.items.length > 0);
|
|
25
|
+
const tests = facts.tests;
|
|
26
|
+
const items = [
|
|
27
|
+
{ id: 'done', label: 'Round is done', ok: facts.doneText !== undefined, detail: facts.doneText !== undefined ? 'DONE.md is present.' : 'No root DONE.md: the control room writes it when every lane is done.' },
|
|
28
|
+
{
|
|
29
|
+
id: 'findings', label: 'No open blockers', ok: blockers.length === 0,
|
|
30
|
+
detail: blockers.length > 0 ? `${blockers.length} open blocker${blockers.length === 1 ? '' : 's'} in DONE.md.` : findings.length > 0 ? `${findings.length} open finding${findings.length === 1 ? '' : 's'} listed in DONE.md, none a blocker.` : 'DONE.md lists no open findings.',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: 'changelog', label: 'CHANGELOG has this release', ok: changelogOk,
|
|
34
|
+
detail: release && release.items.length > 0 ? `[${facts.version}] has ${release.items.length} entries.` : unreleased && unreleased.items.length > 0 ? `[Unreleased] has ${unreleased.items.length} entries to move under ${facts.version}.` : `No [${facts.version}] section and nothing under [Unreleased].`,
|
|
35
|
+
},
|
|
36
|
+
{ id: 'tag', label: 'Tag is free', ok: !facts.tagExists, detail: facts.tagExists ? `Tag v${facts.version} already exists: bump the version first.` : `Tag v${facts.version} does not exist yet.` },
|
|
37
|
+
{ id: 'clean', label: 'Working tree is clean', ok: facts.treeClean, detail: facts.treeClean ? 'No uncommitted changes.' : 'Uncommitted changes: commit or discard them.' },
|
|
38
|
+
{
|
|
39
|
+
id: 'tests', label: 'Tests pass', ok: tests !== undefined && tests.failed === 0 && tests.passed > 0,
|
|
40
|
+
detail: tests === undefined ? 'No test result yet: lane runs record one with `loop runs log --passed N --failed N`, or `npm test` can write .ai/last-test.json.' : tests.failed > 0 ? `${tests.failed} failing in the latest result${tests.at ? ` (${tests.at})` : ''}.` : tests.passed === 0 ? 'The latest result ran no tests.' : `${tests.passed} passed${tests.at ? ` (${tests.at})` : ''}.`,
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
return { ready: items.every((i) => i.ok), items, openFindings: findings };
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=shipCheck.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shipCheck.js","sourceRoot":"","sources":["../../src/core/shipCheck.ts"],"names":[],"mappings":"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"}
|
package/dist/core/table.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { deriveStatus } from './status.js';
|
|
2
|
-
const ICON = { blocked: '!', failing: 'x', done: '+', building: '~', waiting: '
|
|
2
|
+
const ICON = { blocked: '!', failing: 'x', done: '+', building: '~', waiting: '-' };
|
|
3
3
|
// Needs-you states first: blocked, failing, done, then the quiet ones.
|
|
4
4
|
const ORDER = ['blocked', 'failing', 'done', 'building', 'waiting'];
|
|
5
5
|
/** Plain-text fleet table; status is always a word plus an icon, never colour alone. */
|
|
@@ -21,4 +21,25 @@ export function formatFleet(rows, now = new Date()) {
|
|
|
21
21
|
const summary = needs === 0 ? 'Nothing needs you.' : `${needs} ${needs === 1 ? 'loop needs' : 'loops need'} you.`;
|
|
22
22
|
return [summary, '', render(header), ...lines.map(render)].join('\n');
|
|
23
23
|
}
|
|
24
|
+
const LANE_ICON = { blocked: '!', stalled: 'x', done: '+', building: '~', waiting: '-' };
|
|
25
|
+
/** One row per lane, for `loop status` on a laned loop. */
|
|
26
|
+
export function formatLanes(lanes, now = new Date()) {
|
|
27
|
+
const ago = (iso) => {
|
|
28
|
+
const minutes = Math.max(0, Math.round((now.getTime() - new Date(iso).getTime()) / 60_000));
|
|
29
|
+
return minutes < 60 ? `${minutes}m ago` : minutes < 2880 ? `${Math.round(minutes / 60)}h ago` : `${Math.round(minutes / 1440)}d ago`;
|
|
30
|
+
};
|
|
31
|
+
const anyClaim = lanes.some((l) => l.claim);
|
|
32
|
+
const rows = lanes.map((l) => [
|
|
33
|
+
l.name,
|
|
34
|
+
`${LANE_ICON[l.state]} ${l.state}`,
|
|
35
|
+
l.limit > 0 ? `run ${l.run}/${l.limit}` : 'run -',
|
|
36
|
+
l.lastCommit ? `${ago(l.lastCommit.at)}: ${l.lastCommit.subject.slice(0, 48)}` : 'no lane commits yet',
|
|
37
|
+
l.waitingOn?.length ? `waits on ${l.waitingOn.join(', ')}` : l.unanswered.length ? `${l.unanswered.length} unanswered` : '',
|
|
38
|
+
...(anyClaim ? [l.claim ? `${l.claim.name} until ${l.claim.expiresAt.slice(11, 16)} UTC` : ''] : []),
|
|
39
|
+
]);
|
|
40
|
+
const header = ['LANE', 'STATUS', 'RUN', 'LAST COMMIT', 'MESSAGES', ...(anyClaim ? ['CLAIM'] : [])];
|
|
41
|
+
const widths = header.map((h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? '').length)));
|
|
42
|
+
const render = (cols) => cols.map((c, i) => c.padEnd(widths[i] ?? 0)).join(' ').trimEnd();
|
|
43
|
+
return [render(header), ...rows.map(render)].join('\n');
|
|
44
|
+
}
|
|
24
45
|
//# sourceMappingURL=table.js.map
|
package/dist/core/table.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table.js","sourceRoot":"","sources":["../../src/core/table.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAS3C,MAAM,IAAI,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAW,CAAC;AAC7F,uEAAuE;AACvE,MAAM,KAAK,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,CAAU,CAAC;AAE7E,wFAAwF;AACxF,MAAM,UAAU,WAAW,CAAC,IAAyB,EAAE,GAAG,GAAS,IAAI,IAAI,EAAE;IAC3E,MAAM,KAAK,GAAG,IAAI;SACf,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;SAC7D,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;SACrH,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QACxB,GAAG,CAAC,IAAI;QACR,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,EAAE;QACvC,SAAS,GAAG,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,EAAE;QACnC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,OAAO;QACzD,MAAM,CAAC,MAAM;KACd,CAAC,CAAC;IACL,MAAM,MAAM,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IACzD,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAClG,MAAM,MAAM,GAAG,CAAC,IAAc,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;IACrG,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;IAC7E,MAAM,OAAO,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,OAAO,CAAC;IAClH,OAAO,CAAC,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACxE,CAAC"}
|
|
1
|
+
{"version":3,"file":"table.js","sourceRoot":"","sources":["../../src/core/table.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const LABELS = {
|
|
2
|
+
leak: ['No leaks', 'Leak found'],
|
|
3
|
+
'stale-lock': ['No stale lock', 'Stale lock'],
|
|
4
|
+
'short-runs': ['Runs do real work', 'Runs end in seconds'],
|
|
5
|
+
'failing-tests': ['Tests pass', 'Tests failing'],
|
|
6
|
+
'runner-silent': ['Runner active', 'Runner silent'],
|
|
7
|
+
'lane-stalled': ['Lanes moving', 'Lane stalled'],
|
|
8
|
+
'lane-waiting-on-finished': ['No lane waits on a finished lane', 'Lane waits on a finished lane'],
|
|
9
|
+
};
|
|
10
|
+
/** What a check states as its result, never just its name ("Tests failing", not "OK · Tests failing"). */
|
|
11
|
+
export function checkLabel(kind, ok) {
|
|
12
|
+
return LABELS[kind][ok ? 0 : 1];
|
|
13
|
+
}
|
|
14
|
+
const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
15
|
+
const two = (n) => String(n).padStart(2, '0');
|
|
16
|
+
/** `11 Oct 14:53 UTC` for an ISO time; anything that is not a date is returned as given. */
|
|
17
|
+
export function formatUtc(iso) {
|
|
18
|
+
const d = typeof iso === 'string' ? new Date(iso) : iso;
|
|
19
|
+
if (Number.isNaN(d.getTime()))
|
|
20
|
+
return String(iso);
|
|
21
|
+
return `${d.getUTCDate()} ${MONTHS[d.getUTCMonth()]} ${two(d.getUTCHours())}:${two(d.getUTCMinutes())} UTC`;
|
|
22
|
+
}
|
|
23
|
+
/** Adds the result-stating `label` to a check (with or without its `loopId`); an existing label is kept. */
|
|
24
|
+
export function withLabel(check) {
|
|
25
|
+
return { ...check, label: check.label ?? checkLabel(check.kind, check.ok) };
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=checkLabels.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkLabels.js","sourceRoot":"","sources":["../../src/server/checkLabels.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,GAAuE;IACjF,IAAI,EAAE,CAAC,UAAU,EAAE,YAAY,CAAC;IAChC,YAAY,EAAE,CAAC,eAAe,EAAE,YAAY,CAAC;IAC7C,YAAY,EAAE,CAAC,mBAAmB,EAAE,qBAAqB,CAAC;IAC1D,eAAe,EAAE,CAAC,YAAY,EAAE,eAAe,CAAC;IAChD,eAAe,EAAE,CAAC,eAAe,EAAE,eAAe,CAAC;IACnD,cAAc,EAAE,CAAC,cAAc,EAAE,cAAc,CAAC;IAChD,0BAA0B,EAAE,CAAC,kCAAkC,EAAE,+BAA+B,CAAC;CAClG,CAAC;AAEF,0GAA0G;AAC1G,MAAM,UAAU,UAAU,CAAC,IAAyB,EAAE,EAAW;IAC/D,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,MAAM,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAU,CAAC;AAC7G,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAEtD,4FAA4F;AAC5F,MAAM,UAAU,SAAS,CAAC,GAAkB;IAC1C,MAAM,CAAC,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IACxD,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QAAE,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;IAClD,OAAO,GAAG,CAAC,CAAC,UAAU,EAAE,IAAI,MAAM,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC,MAAM,CAAC;AAC9G,CAAC;AAED,4GAA4G;AAC5G,MAAM,UAAU,SAAS,CAAuE,KAAQ;IACtG,OAAO,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;AAC9E,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Pure (no Node imports): the GitHub pages the static dashboard opens instead of holding a token. Every URL is a
|
|
2
|
+
// function of the repo slug, so a user with write access to the repo is the only one who can finish the action.
|
|
3
|
+
import { formatPaused, PAUSED_FILE } from '../core/parse/index.js';
|
|
4
|
+
import { isRepoSlug } from './live/urls.js';
|
|
5
|
+
export const RELEASE_WORKFLOW = 'release.yml';
|
|
6
|
+
export const NEXT_ROUND_FILE = '.ai/next-round.md';
|
|
7
|
+
const GITHUB = 'https://github.com';
|
|
8
|
+
const API = 'https://api.github.com';
|
|
9
|
+
function checkedSlug(slug) {
|
|
10
|
+
if (!isRepoSlug(slug))
|
|
11
|
+
throw new Error(`not a GitHub repo slug: ${slug}`);
|
|
12
|
+
return slug;
|
|
13
|
+
}
|
|
14
|
+
/** A repo-relative path with no way out of the repo (no `..`, no leading slash, no backslash). */
|
|
15
|
+
function checkedPath(file) {
|
|
16
|
+
if (file === '' || file.startsWith('/') || file.includes('\\') || file.split('/').some((part) => part === '..' || part === ''))
|
|
17
|
+
throw new Error(`not a repo file path: ${file}`);
|
|
18
|
+
return file;
|
|
19
|
+
}
|
|
20
|
+
/** GitHub answers 414 above roughly 8 KB; stay well under it. */
|
|
21
|
+
export const MAX_URL_LENGTH = 7500;
|
|
22
|
+
/** A branch name keeps its slashes (`feature/x`) and encodes every other character. */
|
|
23
|
+
const encodeBranch = (branch) => branch.split('/').map(encodeURIComponent).join('/');
|
|
24
|
+
const oneLine = (text) => text.replace(/\s+/g, ' ').trim();
|
|
25
|
+
const encodePath = (file) => checkedPath(file).split('/').map(encodeURIComponent).join('/');
|
|
26
|
+
/** GitHub's "new file" page with the file name and content filled in. */
|
|
27
|
+
export function newFileUrl(slug, file, content, branch = 'main') {
|
|
28
|
+
const url = `${GITHUB}/${checkedSlug(slug)}/new/${encodeBranch(branch)}?filename=${encodeURIComponent(checkedPath(file))}&value=${encodeURIComponent(content)}`;
|
|
29
|
+
// The caller falls back to "copy the text, then open the blank new-file page".
|
|
30
|
+
if (url.length > MAX_URL_LENGTH)
|
|
31
|
+
throw new RangeError(`content too long for a prefilled GitHub URL (${url.length} > ${MAX_URL_LENGTH})`);
|
|
32
|
+
return url;
|
|
33
|
+
}
|
|
34
|
+
/** GitHub's delete page for a file (the confirmation commit happens on GitHub). */
|
|
35
|
+
export function deleteFileUrl(slug, file, branch = 'main') {
|
|
36
|
+
return `${GITHUB}/${checkedSlug(slug)}/delete/${encodeBranch(branch)}/${encodePath(file)}`;
|
|
37
|
+
}
|
|
38
|
+
/** GitHub's web editor for a file, e.g. BLOCKED.md. */
|
|
39
|
+
export function editFileUrl(slug, file, branch = 'main') {
|
|
40
|
+
return `${GITHUB}/${checkedSlug(slug)}/edit/${encodeBranch(branch)}/${encodePath(file)}`;
|
|
41
|
+
}
|
|
42
|
+
/** Pause: the new-file page for `.ai/PAUSED` with the one-line content core writes (`who · paused <ISO>[ · reason]`). */
|
|
43
|
+
export function pauseUrl(slug, by, now, reason, branch = 'main') {
|
|
44
|
+
return newFileUrl(slug, PAUSED_FILE, formatPaused(oneLine(by) || 'dashboard', now, (reason && oneLine(reason)) || undefined), branch);
|
|
45
|
+
}
|
|
46
|
+
/** Resume: the delete page for `.ai/PAUSED`. */
|
|
47
|
+
export const resumeUrl = (slug, branch = 'main') => deleteFileUrl(slug, PAUSED_FILE, branch);
|
|
48
|
+
/** Start next round: the new-file page for `.ai/next-round.md` holding the brief; the next-round workflow takes over on push. */
|
|
49
|
+
export function nextRoundUrl(slug, brief, branch = 'main') {
|
|
50
|
+
return newFileUrl(slug, NEXT_ROUND_FILE, brief.endsWith('\n') ? brief : `${brief}\n`, branch);
|
|
51
|
+
}
|
|
52
|
+
/** The Actions page where the owner presses "Run workflow" for the release workflow. */
|
|
53
|
+
export const releaseWorkflowUrl = (slug) => `${GITHUB}/${checkedSlug(slug)}/actions/workflows/${RELEASE_WORKFLOW}`;
|
|
54
|
+
/** API URL for the latest run of the release workflow (the dashboard shows its result). */
|
|
55
|
+
export const latestReleaseRunApiUrl = (slug) => `${API}/repos/${checkedSlug(slug)}/actions/workflows/${RELEASE_WORKFLOW}/runs?per_page=1`;
|
|
56
|
+
/** API URL listing the repo's tags (for the ship-check "tag does not exist yet" item). */
|
|
57
|
+
export const tagsApiUrl = (slug) => `${API}/repos/${checkedSlug(slug)}/tags?per_page=100`;
|
|
58
|
+
/** API URL matching one release tag by prefix; unlike `tagsApiUrl` it is not capped by the repo's first 100 tags. Response items carry `ref: "refs/tags/<name>"`. */
|
|
59
|
+
export const tagLookupApiUrl = (slug, version) => `${API}/repos/${checkedSlug(slug)}/git/matching-refs/tags/v${encodeURIComponent(version)}`;
|
|
60
|
+
/** True when `nextRoundUrl` can carry this brief; otherwise the UI offers Copy and the blank new-file page. */
|
|
61
|
+
export function briefFitsUrl(slug, brief) {
|
|
62
|
+
try {
|
|
63
|
+
nextRoundUrl(slug, brief);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
if (error instanceof RangeError)
|
|
68
|
+
return false;
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=controls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controls.js","sourceRoot":"","sources":["../../src/server/controls.ts"],"names":[],"mappings":"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"}
|