issue-flow 0.11.1 → 0.12.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/dist/{analyze-XV3K25K3.js → analyze-OEET7KGE.js} +15 -10
- package/dist/{analyze-XV3K25K3.js.map → analyze-OEET7KGE.js.map} +1 -1
- package/dist/{apply-WUJHDD7A.js → apply-ZIA3C52S.js} +6 -4
- package/dist/{apply-WUJHDD7A.js.map → apply-ZIA3C52S.js.map} +1 -1
- package/dist/{chunk-6N7LZZ5U.js → chunk-3QHK4YQA.js} +204 -107
- package/dist/chunk-3QHK4YQA.js.map +1 -0
- package/dist/chunk-42PJFMAR.js +605 -0
- package/dist/chunk-42PJFMAR.js.map +1 -0
- package/dist/{chunk-EWDJENFW.js → chunk-4JSOYE4G.js} +77 -12
- package/dist/chunk-4JSOYE4G.js.map +1 -0
- package/dist/chunk-56KS3TFQ.js +703 -0
- package/dist/chunk-56KS3TFQ.js.map +1 -0
- package/dist/{chunk-AAY6KM7N.js → chunk-73ATPIYX.js} +160 -154
- package/dist/chunk-73ATPIYX.js.map +1 -0
- package/dist/{chunk-KQ4JQN7T.js → chunk-A4SLHOW7.js} +68 -12
- package/dist/chunk-A4SLHOW7.js.map +1 -0
- package/dist/chunk-BAUPQXPE.js +173 -0
- package/dist/chunk-BAUPQXPE.js.map +1 -0
- package/dist/{chunk-HC2MT7BX.js → chunk-BYZDPR7N.js} +14 -581
- package/dist/chunk-BYZDPR7N.js.map +1 -0
- package/dist/chunk-CD5ULRVC.js +615 -0
- package/dist/chunk-CD5ULRVC.js.map +1 -0
- package/dist/chunk-DIDUVL6O.js +1365 -0
- package/dist/chunk-DIDUVL6O.js.map +1 -0
- package/dist/{chunk-47VIN33V.js → chunk-EZATO3DW.js} +7 -5
- package/dist/{chunk-47VIN33V.js.map → chunk-EZATO3DW.js.map} +1 -1
- package/dist/{chunk-BXCPXRBR.js → chunk-FADTCQGM.js} +46 -11
- package/dist/chunk-FADTCQGM.js.map +1 -0
- package/dist/{chunk-GANSDJV7.js → chunk-FJP3UQGD.js} +15 -13
- package/dist/chunk-FJP3UQGD.js.map +1 -0
- package/dist/chunk-KMMASOA6.js +134 -0
- package/dist/chunk-KMMASOA6.js.map +1 -0
- package/dist/{chunk-HQYXVIH6.js → chunk-LAFNLLQP.js} +3 -3
- package/dist/chunk-LWRSMTTZ.js +18 -0
- package/dist/chunk-LWRSMTTZ.js.map +1 -0
- package/dist/{chunk-IVHZEYAX.js → chunk-M5UCXTIT.js} +13 -11
- package/dist/{chunk-IVHZEYAX.js.map → chunk-M5UCXTIT.js.map} +1 -1
- package/dist/chunk-M66SJHQQ.js +206 -0
- package/dist/chunk-M66SJHQQ.js.map +1 -0
- package/dist/{chunk-OBLVLIUS.js → chunk-MJULIKQU.js} +42 -6
- package/dist/chunk-MJULIKQU.js.map +1 -0
- package/dist/{chunk-RKTEV6EG.js → chunk-NS6R37II.js} +4 -4
- package/dist/{run-DBF2VWUN.js → chunk-NTMMF5UD.js} +515 -544
- package/dist/chunk-NTMMF5UD.js.map +1 -0
- package/dist/{chunk-EY6WMHV2.js → chunk-QCPGHBSZ.js} +14 -12
- package/dist/{chunk-EY6WMHV2.js.map → chunk-QCPGHBSZ.js.map} +1 -1
- package/dist/{chunk-LVKQ2AKT.js → chunk-TSOLWK7Y.js} +8 -6
- package/dist/chunk-TSOLWK7Y.js.map +1 -0
- package/dist/{chunk-34WNTK3K.js → chunk-XQUSIEKA.js} +11 -9
- package/dist/{chunk-34WNTK3K.js.map → chunk-XQUSIEKA.js.map} +1 -1
- package/dist/chunk-Y5ZDJ4PJ.js +76 -0
- package/dist/chunk-Y5ZDJ4PJ.js.map +1 -0
- package/dist/cli.js +156 -26
- package/dist/cli.js.map +1 -1
- package/dist/{config-4THHOXNN.js → config-PRJGWW7J.js} +15 -3
- package/dist/execute-XBNVL753.js +18 -0
- package/dist/{generate-TBVTLMOP.js → generate-EGB4KV2B.js} +16 -10
- package/dist/{generate-TBVTLMOP.js.map → generate-EGB4KV2B.js.map} +1 -1
- package/dist/init-AJHCXJBP.js +10 -0
- package/dist/operations-BYT626KQ.js +365 -0
- package/dist/operations-BYT626KQ.js.map +1 -0
- package/dist/plan-QOZ3RIO6.js +21 -0
- package/dist/{policy-XQW5NUIE.js → policy-6S4GFC76.js} +6 -4
- package/dist/{policy-XQW5NUIE.js.map → policy-6S4GFC76.js.map} +1 -1
- package/dist/pr-EBBHD2ZX.js +26 -0
- package/dist/pr-review-VVMW5PO4.js +20 -0
- package/dist/prd-JQSZF7PR.js +21 -0
- package/dist/resume-PPCNQVVV.js +221 -0
- package/dist/resume-PPCNQVVV.js.map +1 -0
- package/dist/review-3F6MARWH.js +19 -0
- package/dist/run-RXE5GDQO.js +36 -0
- package/dist/run-RXE5GDQO.js.map +1 -0
- package/dist/state-manager-2EDVDBAR.js +34 -0
- package/dist/state-manager-2EDVDBAR.js.map +1 -0
- package/dist/{web-ANQQTTRD.js → web-EF5WIIGG.js} +13 -8
- package/dist/{web-ANQQTTRD.js.map → web-EF5WIIGG.js.map} +1 -1
- package/package.json +1 -1
- package/prompts/pr.md +11 -4
- package/web/public/app.js +12 -0
- package/dist/chunk-3BOHTH4V.js +0 -58
- package/dist/chunk-3BOHTH4V.js.map +0 -1
- package/dist/chunk-6N7LZZ5U.js.map +0 -1
- package/dist/chunk-AAY6KM7N.js.map +0 -1
- package/dist/chunk-BXCPXRBR.js.map +0 -1
- package/dist/chunk-EWDJENFW.js.map +0 -1
- package/dist/chunk-GANSDJV7.js.map +0 -1
- package/dist/chunk-HC2MT7BX.js.map +0 -1
- package/dist/chunk-HDV3YDTG.js +0 -52
- package/dist/chunk-HDV3YDTG.js.map +0 -1
- package/dist/chunk-KQ4JQN7T.js.map +0 -1
- package/dist/chunk-LVKQ2AKT.js.map +0 -1
- package/dist/chunk-OBLVLIUS.js.map +0 -1
- package/dist/chunk-VQ5VN7DF.js +0 -1018
- package/dist/chunk-VQ5VN7DF.js.map +0 -1
- package/dist/execute-5OEKLM5X.js +0 -15
- package/dist/init-PRMOTQ7N.js +0 -9
- package/dist/plan-Y2BYZD24.js +0 -17
- package/dist/pr-H6GLOSXF.js +0 -21
- package/dist/pr-review-CK5FR6WX.js +0 -17
- package/dist/prd-4PWLCGH5.js +0 -17
- package/dist/review-QGVAHEZF.js +0 -15
- package/dist/run-DBF2VWUN.js.map +0 -1
- /package/dist/{chunk-HQYXVIH6.js.map → chunk-LAFNLLQP.js.map} +0 -0
- /package/dist/{chunk-RKTEV6EG.js.map → chunk-NS6R37II.js.map} +0 -0
- /package/dist/{config-4THHOXNN.js.map → config-PRJGWW7J.js.map} +0 -0
- /package/dist/{execute-5OEKLM5X.js.map → execute-XBNVL753.js.map} +0 -0
- /package/dist/{init-PRMOTQ7N.js.map → init-AJHCXJBP.js.map} +0 -0
- /package/dist/{plan-Y2BYZD24.js.map → plan-QOZ3RIO6.js.map} +0 -0
- /package/dist/{pr-H6GLOSXF.js.map → pr-EBBHD2ZX.js.map} +0 -0
- /package/dist/{pr-review-CK5FR6WX.js.map → pr-review-VVMW5PO4.js.map} +0 -0
- /package/dist/{prd-4PWLCGH5.js.map → prd-JQSZF7PR.js.map} +0 -0
- /package/dist/{review-QGVAHEZF.js.map → review-3F6MARWH.js.map} +0 -0
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
PIPELINE_PHASES,
|
|
4
|
+
PipelineManager,
|
|
5
|
+
loadExecutionPlan,
|
|
6
|
+
parseJournal
|
|
7
|
+
} from "./chunk-56KS3TFQ.js";
|
|
8
|
+
import {
|
|
9
|
+
describeRunLockOwner,
|
|
10
|
+
isRunLockStale,
|
|
11
|
+
readRunLock
|
|
12
|
+
} from "./chunk-BAUPQXPE.js";
|
|
13
|
+
import "./chunk-KMMASOA6.js";
|
|
14
|
+
import {
|
|
15
|
+
QUEUES_DIR_NAME,
|
|
16
|
+
getIssuePaths,
|
|
17
|
+
resolveProjectPaths
|
|
18
|
+
} from "./chunk-DIDUVL6O.js";
|
|
19
|
+
import {
|
|
20
|
+
printError,
|
|
21
|
+
printInfo,
|
|
22
|
+
printWarning
|
|
23
|
+
} from "./chunk-BYZDPR7N.js";
|
|
24
|
+
import {
|
|
25
|
+
loadTaskPlan
|
|
26
|
+
} from "./chunk-CD5ULRVC.js";
|
|
27
|
+
|
|
28
|
+
// src/commands/operations.ts
|
|
29
|
+
import { readdir, readFile } from "fs/promises";
|
|
30
|
+
import { join } from "path";
|
|
31
|
+
async function project() {
|
|
32
|
+
try {
|
|
33
|
+
return await resolveProjectPaths();
|
|
34
|
+
} catch (err) {
|
|
35
|
+
printError(`Not inside a usable project: ${err instanceof Error ? err.message : String(err)}`);
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function readJson(path) {
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(await readFile(path, "utf-8"));
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async function loadIssueState(projectId, id) {
|
|
47
|
+
let paths;
|
|
48
|
+
try {
|
|
49
|
+
paths = getIssuePaths(projectId, id);
|
|
50
|
+
} catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
let plan = null;
|
|
54
|
+
try {
|
|
55
|
+
plan = await loadTaskPlan(paths.tasksFile);
|
|
56
|
+
} catch {
|
|
57
|
+
plan = null;
|
|
58
|
+
}
|
|
59
|
+
if (plan === null) return null;
|
|
60
|
+
const snapshot = await readJson(paths.sessionFile);
|
|
61
|
+
const phases = plan.noBranch === true ? PIPELINE_PHASES.filter((phase) => phase !== "pr") : [...PIPELINE_PHASES];
|
|
62
|
+
if (plan.prReview?.enabled === true) phases.push("pr-review");
|
|
63
|
+
return {
|
|
64
|
+
id,
|
|
65
|
+
plan,
|
|
66
|
+
snapshot,
|
|
67
|
+
nextPhase: new PipelineManager(plan, paths.tasksFile, phases).getNextPhase(),
|
|
68
|
+
paths
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
async function allIssues(paths) {
|
|
72
|
+
let ids;
|
|
73
|
+
try {
|
|
74
|
+
ids = await readdir(paths.issuesDir);
|
|
75
|
+
} catch {
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
const states = [];
|
|
79
|
+
for (const id of ids) {
|
|
80
|
+
const state = await loadIssueState(paths.projectId, id);
|
|
81
|
+
if (state !== null) states.push(state);
|
|
82
|
+
}
|
|
83
|
+
return states.sort((a, b) => attemptedAt(b.plan) - attemptedAt(a.plan));
|
|
84
|
+
}
|
|
85
|
+
function attemptedAt(plan) {
|
|
86
|
+
const at = Date.parse(plan?.lastAttemptAt ?? "");
|
|
87
|
+
return Number.isNaN(at) ? 0 : at;
|
|
88
|
+
}
|
|
89
|
+
async function allQueues(paths) {
|
|
90
|
+
const dir = join(paths.projectDir, QUEUES_DIR_NAME);
|
|
91
|
+
let entries;
|
|
92
|
+
try {
|
|
93
|
+
entries = await readdir(dir);
|
|
94
|
+
} catch {
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
const plans = [];
|
|
98
|
+
for (const entry of entries) {
|
|
99
|
+
const plan = await loadExecutionPlan(join(dir, entry, "execution-plan.json"));
|
|
100
|
+
if (plan !== null) plans.push(plan);
|
|
101
|
+
}
|
|
102
|
+
return plans;
|
|
103
|
+
}
|
|
104
|
+
function since(at, now = Date.now()) {
|
|
105
|
+
if (at === null || at === void 0 || at === "") return "never";
|
|
106
|
+
const parsed = Date.parse(at);
|
|
107
|
+
if (Number.isNaN(parsed)) return "unknown";
|
|
108
|
+
return `${formatSeconds(Math.max(0, Math.round((now - parsed) / 1e3)))} ago`;
|
|
109
|
+
}
|
|
110
|
+
function formatSeconds(total) {
|
|
111
|
+
if (total < 60) return `${total}s`;
|
|
112
|
+
const minutes = Math.floor(total / 60);
|
|
113
|
+
if (minutes < 60) return `${minutes}m ${total % 60}s`;
|
|
114
|
+
const hours = Math.floor(minutes / 60);
|
|
115
|
+
return `${hours}h ${minutes % 60}m`;
|
|
116
|
+
}
|
|
117
|
+
async function runStatus(issue, options = {}) {
|
|
118
|
+
const paths = await project();
|
|
119
|
+
if (paths === null) return 1;
|
|
120
|
+
const lock = await readRunLock(paths.runLockFile);
|
|
121
|
+
const issues = issue === void 0 ? await allIssues(paths) : [await loadIssueState(paths.projectId, issue)].filter(
|
|
122
|
+
(state) => state !== null
|
|
123
|
+
);
|
|
124
|
+
const queues = await allQueues(paths);
|
|
125
|
+
if (options.json === true) {
|
|
126
|
+
printInfo(
|
|
127
|
+
JSON.stringify(
|
|
128
|
+
{
|
|
129
|
+
owner: lock,
|
|
130
|
+
ownerStale: lock === null ? null : isRunLockStale(lock),
|
|
131
|
+
issues: issues.map((state) => ({
|
|
132
|
+
id: state.id,
|
|
133
|
+
issueStatus: state.plan?.issueStatus ?? null,
|
|
134
|
+
runState: state.plan?.runState ?? null,
|
|
135
|
+
nextPhase: state.nextPhase,
|
|
136
|
+
branch: state.plan?.branchName ?? null,
|
|
137
|
+
lastActivityAt: state.snapshot?.updatedAt ?? state.plan?.lastAttemptAt ?? null,
|
|
138
|
+
retries: state.snapshot?.execution.retries ?? null
|
|
139
|
+
})),
|
|
140
|
+
queues: queues.map((plan) => ({
|
|
141
|
+
id: plan.id,
|
|
142
|
+
status: plan.status,
|
|
143
|
+
issues: plan.issues.map((entry) => ({
|
|
144
|
+
id: entry.id,
|
|
145
|
+
status: entry.status,
|
|
146
|
+
attempts: entry.attempts,
|
|
147
|
+
blockedReason: entry.blockedReason
|
|
148
|
+
}))
|
|
149
|
+
}))
|
|
150
|
+
},
|
|
151
|
+
null,
|
|
152
|
+
2
|
|
153
|
+
)
|
|
154
|
+
);
|
|
155
|
+
return 0;
|
|
156
|
+
}
|
|
157
|
+
printOwner(lock);
|
|
158
|
+
if (issues.length === 0) {
|
|
159
|
+
printInfo("No issue in this project has any state yet.");
|
|
160
|
+
return 0;
|
|
161
|
+
}
|
|
162
|
+
for (const state of issues.slice(0, issue === void 0 ? 5 : 1)) {
|
|
163
|
+
printIssueLine(state);
|
|
164
|
+
}
|
|
165
|
+
for (const plan of queues) {
|
|
166
|
+
if (plan.status === "completed") continue;
|
|
167
|
+
printInfo(`Queue ${plan.id} \u2014 ${plan.status}`);
|
|
168
|
+
for (const entry of plan.issues) {
|
|
169
|
+
const attempts = entry.attempts > 0 ? `, ${entry.attempts} attempt(s)` : "";
|
|
170
|
+
const blocked = entry.blockedReason === null ? "" : ` \u2014 ${entry.blockedReason}`;
|
|
171
|
+
printInfo(` #${entry.id} ${entry.status}${attempts}${blocked}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return 0;
|
|
175
|
+
}
|
|
176
|
+
function printOwner(lock) {
|
|
177
|
+
if (lock === null) {
|
|
178
|
+
printInfo("Nothing is running in this project (no run.lock).");
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (isRunLockStale(lock)) {
|
|
182
|
+
printWarning(`A stale lock is left behind: ${describeRunLockOwner(lock)}.`);
|
|
183
|
+
printInfo("It will be taken over by the next run or resume.");
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
printInfo(`Running: ${describeRunLockOwner(lock)}`);
|
|
187
|
+
printInfo(`Last heartbeat ${since(lock.lastHeartbeatAt)}.`);
|
|
188
|
+
}
|
|
189
|
+
function printIssueLine(state) {
|
|
190
|
+
const plan = state.plan;
|
|
191
|
+
const run = plan?.runState;
|
|
192
|
+
const phase = run?.currentPhase ?? state.nextPhase ?? "\u2014";
|
|
193
|
+
const attempt = run !== void 0 && run.attempt > 0 ? `, attempt ${run.attempt}` : "";
|
|
194
|
+
const retries = state.snapshot?.execution.retries;
|
|
195
|
+
const retryLine = retries === void 0 || retries === 0 ? "" : `, ${retries} retry(ies)`;
|
|
196
|
+
const activity = since(state.snapshot?.updatedAt ?? plan?.lastAttemptAt);
|
|
197
|
+
printInfo(
|
|
198
|
+
`#${state.id} ${plan?.issueStatus ?? "unknown"}${run === void 0 ? "" : ` / ${run.status}`} \u2014 phase ${phase}${attempt}${retryLine}; last activity ${activity}`
|
|
199
|
+
);
|
|
200
|
+
if (run?.blockedReason !== null && run?.blockedReason !== void 0) {
|
|
201
|
+
printWarning(` Blocked: ${run.blockedReason}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
async function runRuns() {
|
|
205
|
+
const paths = await project();
|
|
206
|
+
if (paths === null) return 1;
|
|
207
|
+
const issues = await allIssues(paths);
|
|
208
|
+
if (issues.length === 0) {
|
|
209
|
+
printInfo("No runs recorded for this project yet.");
|
|
210
|
+
return 0;
|
|
211
|
+
}
|
|
212
|
+
for (const state of issues) {
|
|
213
|
+
const plan = state.plan;
|
|
214
|
+
if (plan === null) continue;
|
|
215
|
+
const duration = state.snapshot?.elapsedSeconds === null || state.snapshot?.elapsedSeconds === void 0 ? "\u2014" : formatSeconds(state.snapshot.elapsedSeconds);
|
|
216
|
+
const cause = plan.lastError === null ? "" : ` \u2014 ${plan.lastError.message.split("\n")[0] ?? ""}`;
|
|
217
|
+
printInfo(
|
|
218
|
+
`#${state.id.padEnd(6)} ${plan.issueStatus.padEnd(12)} ${duration.padEnd(10)} last attempt ${since(plan.lastAttemptAt)}${cause}`
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
return 0;
|
|
222
|
+
}
|
|
223
|
+
var DEFAULT_TAIL = 50;
|
|
224
|
+
var FOLLOW_POLL_MS = 1e3;
|
|
225
|
+
async function runLogs(issue, options = {}) {
|
|
226
|
+
const paths = await project();
|
|
227
|
+
if (paths === null) return 1;
|
|
228
|
+
const target = issue ?? (await allIssues(paths))[0]?.id;
|
|
229
|
+
if (target === void 0) {
|
|
230
|
+
printInfo("No issue in this project has a journal yet.");
|
|
231
|
+
return 0;
|
|
232
|
+
}
|
|
233
|
+
const issuePaths = getIssuePaths(paths.projectId, target);
|
|
234
|
+
const kinds = new Set(options.kind ?? []);
|
|
235
|
+
const tail = options.tail ?? DEFAULT_TAIL;
|
|
236
|
+
const shown = /* @__PURE__ */ new Set();
|
|
237
|
+
const render = (content, limit) => {
|
|
238
|
+
const entries = parseJournal(content).filter(
|
|
239
|
+
(entry) => kinds.size === 0 || kinds.has(entry.event.type)
|
|
240
|
+
);
|
|
241
|
+
const slice = limit === null ? entries : entries.slice(-limit);
|
|
242
|
+
for (const entry of slice) {
|
|
243
|
+
if (shown.has(entry.seq)) continue;
|
|
244
|
+
shown.add(entry.seq);
|
|
245
|
+
printInfo(formatEvent(entry.seq, entry.event));
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
const readAll = async () => `${await readIfPresent(issuePaths.rotatedEventsFile)}${await readIfPresent(issuePaths.eventsFile)}`;
|
|
249
|
+
const initial = await readAll();
|
|
250
|
+
if (initial === "") {
|
|
251
|
+
printInfo(
|
|
252
|
+
`Issue #${target} has no journal. Enable it with resilience.journal.enabled or --continuous.`
|
|
253
|
+
);
|
|
254
|
+
return 0;
|
|
255
|
+
}
|
|
256
|
+
render(initial, tail);
|
|
257
|
+
if (options.follow !== true) return 0;
|
|
258
|
+
const poll = options.pollMs ?? FOLLOW_POLL_MS;
|
|
259
|
+
const signal = options.signal;
|
|
260
|
+
while (signal === void 0 || !signal.aborted) {
|
|
261
|
+
await sleep(poll, signal);
|
|
262
|
+
if (signal?.aborted === true) break;
|
|
263
|
+
render(await readAll(), null);
|
|
264
|
+
}
|
|
265
|
+
return 0;
|
|
266
|
+
}
|
|
267
|
+
function formatEvent(seq, event) {
|
|
268
|
+
const at = "at" in event ? event.at : "";
|
|
269
|
+
const detail = describeEvent(event);
|
|
270
|
+
return `${String(seq).padStart(5)} ${at} ${event.type}${detail === "" ? "" : ` \u2014 ${detail}`}`;
|
|
271
|
+
}
|
|
272
|
+
function describeEvent(event) {
|
|
273
|
+
switch (event.type) {
|
|
274
|
+
case "phase:start":
|
|
275
|
+
return event.phase;
|
|
276
|
+
case "phase:end":
|
|
277
|
+
return `${event.phase} ${event.success ? "ok" : `failed: ${event.error ?? ""}`}`;
|
|
278
|
+
case "retry":
|
|
279
|
+
return `attempt ${event.attempt}, ${event.delaySeconds}s \u2014 ${event.reason}`;
|
|
280
|
+
case "log":
|
|
281
|
+
return `${event.level}: ${event.message}`;
|
|
282
|
+
case "session:end":
|
|
283
|
+
return event.status;
|
|
284
|
+
default:
|
|
285
|
+
return "";
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
async function readIfPresent(path) {
|
|
289
|
+
try {
|
|
290
|
+
return await readFile(path, "utf-8");
|
|
291
|
+
} catch {
|
|
292
|
+
return "";
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function sleep(ms, signal) {
|
|
296
|
+
return new Promise((resolve) => {
|
|
297
|
+
const timer = setTimeout(resolve, ms);
|
|
298
|
+
timer.unref?.();
|
|
299
|
+
signal?.addEventListener("abort", () => {
|
|
300
|
+
clearTimeout(timer);
|
|
301
|
+
resolve();
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
async function runPause(options = {}) {
|
|
306
|
+
return stopOwner("paused", options);
|
|
307
|
+
}
|
|
308
|
+
async function runCancel(issue, options = {}) {
|
|
309
|
+
const code = await stopOwner("cancelled", options);
|
|
310
|
+
if (code !== 0) return code;
|
|
311
|
+
const paths = await project();
|
|
312
|
+
if (paths === null) return 1;
|
|
313
|
+
const target = issue ?? (await allIssues(paths))[0]?.id;
|
|
314
|
+
if (target === void 0) return 0;
|
|
315
|
+
const state = await loadIssueState(paths.projectId, target);
|
|
316
|
+
if (state === null || state.plan === null) return 0;
|
|
317
|
+
const { saveTaskPlan, isoNow } = await import("./state-manager-2EDVDBAR.js");
|
|
318
|
+
await saveTaskPlan(state.paths.tasksFile, {
|
|
319
|
+
...state.plan,
|
|
320
|
+
runState: {
|
|
321
|
+
currentPhase: state.plan.runState?.currentPhase ?? null,
|
|
322
|
+
attempt: state.plan.runState?.attempt ?? 0,
|
|
323
|
+
owner: null,
|
|
324
|
+
status: "blocked",
|
|
325
|
+
blockedReason: "Cancelled by the user",
|
|
326
|
+
lastHeartbeatAt: isoNow()
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
printInfo(`Issue #${target} is marked cancelled. A resume will report it instead of running it.`);
|
|
330
|
+
return 0;
|
|
331
|
+
}
|
|
332
|
+
async function stopOwner(intent, options) {
|
|
333
|
+
const paths = await project();
|
|
334
|
+
if (paths === null) return 1;
|
|
335
|
+
const lock = await readRunLock(paths.runLockFile);
|
|
336
|
+
if (lock === null) {
|
|
337
|
+
printInfo("Nothing is running in this project.");
|
|
338
|
+
return 0;
|
|
339
|
+
}
|
|
340
|
+
if (isRunLockStale(lock)) {
|
|
341
|
+
printWarning(`The lock is stale (${describeRunLockOwner(lock)}); nothing to stop.`);
|
|
342
|
+
return 0;
|
|
343
|
+
}
|
|
344
|
+
const send = options.signalProcess ?? ((pid, signal) => process.kill(pid, signal));
|
|
345
|
+
try {
|
|
346
|
+
send(lock.pid, "SIGTERM");
|
|
347
|
+
} catch (err) {
|
|
348
|
+
printError(
|
|
349
|
+
`Could not signal pid ${lock.pid}: ${err instanceof Error ? err.message : String(err)}`
|
|
350
|
+
);
|
|
351
|
+
return 1;
|
|
352
|
+
}
|
|
353
|
+
printInfo(
|
|
354
|
+
`Asked pid ${lock.pid} to stop (${intent}). It writes a checkpoint, stops the agent and closes its journal before exiting.`
|
|
355
|
+
);
|
|
356
|
+
return 0;
|
|
357
|
+
}
|
|
358
|
+
export {
|
|
359
|
+
runCancel,
|
|
360
|
+
runLogs,
|
|
361
|
+
runPause,
|
|
362
|
+
runRuns,
|
|
363
|
+
runStatus
|
|
364
|
+
};
|
|
365
|
+
//# sourceMappingURL=operations-BYT626KQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/operations.ts"],"sourcesContent":["import { readdir, readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { parseJournal } from '../core/journal.js';\nimport { PIPELINE_PHASES, PipelineManager, type PipelinePhase } from '../core/pipeline.js';\nimport type { SessionEvent, SessionSnapshot } from '../core/session-state.js';\nimport { loadTaskPlan } from '../core/state-manager.js';\nimport { loadExecutionPlan } from '../execution/plan.js';\nimport type { ExecutionPlan } from '../execution/types.js';\nimport { describeRunLockOwner, isRunLockStale, readRunLock } from '../storage/lock.js';\nimport { getIssuePaths, QUEUES_DIR_NAME } from '../storage/paths.js';\nimport { resolveProjectPaths } from '../storage/resolve.js';\nimport type { RunLock } from '../storage/schemas.js';\nimport type { TaskPlan } from '../types.js';\nimport { printError, printInfo, printWarning } from '../ui/logger.js';\n\n/**\n * The operation surface of a long run: `status`, `runs`, `logs`, `pause`,\n * `cancel`.\n *\n * The CLI declared thirteen commands and not one of them answered \"what is\n * running\", \"what failed\", or \"stop what you are doing\". During a six-hour\n * unattended execution the only way to find out was to read JSON by hand, and\n * the only way to stop it was to find the pid.\n *\n * Everything here **reads state that already exists** — `run.lock`,\n * `tasks.json`, `session.json`, `execution-plan.json`, `events.jsonl` — and\n * writes nothing except the two control commands, which do the one thing a\n * person cannot do from outside: ask the owner to stop, gracefully.\n */\n\ninterface ProjectPaths {\n projectId: string;\n projectDir: string;\n issuesDir: string;\n runLockFile: string;\n}\n\n/** One issue's state, assembled from every artifact that knows part of it. */\ninterface IssueState {\n id: string;\n plan: TaskPlan | null;\n snapshot: SessionSnapshot | null;\n nextPhase: PipelinePhase | null;\n paths: ReturnType<typeof getIssuePaths>;\n}\n\nasync function project(): Promise<ProjectPaths | null> {\n try {\n return await resolveProjectPaths();\n } catch (err) {\n printError(`Not inside a usable project: ${err instanceof Error ? err.message : String(err)}`);\n return null;\n }\n}\n\nasync function readJson<T>(path: string): Promise<T | null> {\n try {\n return JSON.parse(await readFile(path, 'utf-8')) as T;\n } catch {\n return null;\n }\n}\n\nasync function loadIssueState(projectId: string, id: string): Promise<IssueState | null> {\n let paths: ReturnType<typeof getIssuePaths>;\n try {\n paths = getIssuePaths(projectId, id);\n } catch {\n return null;\n }\n\n let plan: TaskPlan | null = null;\n try {\n plan = await loadTaskPlan(paths.tasksFile);\n } catch {\n plan = null;\n }\n if (plan === null) return null;\n\n const snapshot = await readJson<SessionSnapshot>(paths.sessionFile);\n const phases: PipelinePhase[] =\n plan.noBranch === true\n ? PIPELINE_PHASES.filter((phase) => phase !== 'pr')\n : [...PIPELINE_PHASES];\n if (plan.prReview?.enabled === true) phases.push('pr-review');\n\n return {\n id,\n plan,\n snapshot,\n nextPhase: new PipelineManager(plan, paths.tasksFile, phases).getNextPhase(),\n paths,\n };\n}\n\n/** Every issue this project has state for, newest attempt first. */\nasync function allIssues(paths: ProjectPaths): Promise<IssueState[]> {\n let ids: string[];\n try {\n ids = await readdir(paths.issuesDir);\n } catch {\n return [];\n }\n\n const states: IssueState[] = [];\n for (const id of ids) {\n const state = await loadIssueState(paths.projectId, id);\n if (state !== null) states.push(state);\n }\n return states.sort((a, b) => attemptedAt(b.plan) - attemptedAt(a.plan));\n}\n\nfunction attemptedAt(plan: TaskPlan | null): number {\n const at = Date.parse(plan?.lastAttemptAt ?? '');\n return Number.isNaN(at) ? 0 : at;\n}\n\n/** Every queue plan of the project. */\nasync function allQueues(paths: ProjectPaths): Promise<ExecutionPlan[]> {\n const dir = join(paths.projectDir, QUEUES_DIR_NAME);\n let entries: string[];\n try {\n entries = await readdir(dir);\n } catch {\n return [];\n }\n\n const plans: ExecutionPlan[] = [];\n for (const entry of entries) {\n const plan = await loadExecutionPlan(join(dir, entry, 'execution-plan.json'));\n if (plan !== null) plans.push(plan);\n }\n return plans;\n}\n\n/** `2m 13s ago`, or `never`. */\nfunction since(at: string | null | undefined, now = Date.now()): string {\n if (at === null || at === undefined || at === '') return 'never';\n const parsed = Date.parse(at);\n if (Number.isNaN(parsed)) return 'unknown';\n return `${formatSeconds(Math.max(0, Math.round((now - parsed) / 1000)))} ago`;\n}\n\nfunction formatSeconds(total: number): string {\n if (total < 60) return `${total}s`;\n const minutes = Math.floor(total / 60);\n if (minutes < 60) return `${minutes}m ${total % 60}s`;\n const hours = Math.floor(minutes / 60);\n return `${hours}h ${minutes % 60}m`;\n}\n\n/* ── status ─────────────────────────────────────────────────────────────── */\n\nexport interface StatusOptions {\n /** Emit the assembled state as JSON instead of a screen. */\n json?: boolean;\n}\n\n/**\n * One screen answering \"what is happening right now\".\n *\n * The three artifacts each know part of it and none knows all of it: the lock\n * knows *who* and *how long since it last said anything*, the task plan knows\n * *which phase and which attempt*, and the queue plan knows *where in the\n * queue*. Reading them together is the whole command.\n */\nexport async function runStatus(issue?: string, options: StatusOptions = {}): Promise<number> {\n const paths = await project();\n if (paths === null) return 1;\n\n const lock = await readRunLock(paths.runLockFile);\n const issues =\n issue === undefined\n ? await allIssues(paths)\n : [await loadIssueState(paths.projectId, issue)].filter(\n (state): state is IssueState => state !== null,\n );\n const queues = await allQueues(paths);\n\n if (options.json === true) {\n printInfo(\n JSON.stringify(\n {\n owner: lock,\n ownerStale: lock === null ? null : isRunLockStale(lock),\n issues: issues.map((state) => ({\n id: state.id,\n issueStatus: state.plan?.issueStatus ?? null,\n runState: state.plan?.runState ?? null,\n nextPhase: state.nextPhase,\n branch: state.plan?.branchName ?? null,\n lastActivityAt: state.snapshot?.updatedAt ?? state.plan?.lastAttemptAt ?? null,\n retries: state.snapshot?.execution.retries ?? null,\n })),\n queues: queues.map((plan) => ({\n id: plan.id,\n status: plan.status,\n issues: plan.issues.map((entry) => ({\n id: entry.id,\n status: entry.status,\n attempts: entry.attempts,\n blockedReason: entry.blockedReason,\n })),\n })),\n },\n null,\n 2,\n ),\n );\n return 0;\n }\n\n printOwner(lock);\n\n if (issues.length === 0) {\n printInfo('No issue in this project has any state yet.');\n return 0;\n }\n\n for (const state of issues.slice(0, issue === undefined ? 5 : 1)) {\n printIssueLine(state);\n }\n\n for (const plan of queues) {\n if (plan.status === 'completed') continue;\n printInfo(`Queue ${plan.id} — ${plan.status}`);\n for (const entry of plan.issues) {\n const attempts = entry.attempts > 0 ? `, ${entry.attempts} attempt(s)` : '';\n const blocked = entry.blockedReason === null ? '' : ` — ${entry.blockedReason}`;\n printInfo(` #${entry.id} ${entry.status}${attempts}${blocked}`);\n }\n }\n\n return 0;\n}\n\nfunction printOwner(lock: RunLock | null): void {\n if (lock === null) {\n printInfo('Nothing is running in this project (no run.lock).');\n return;\n }\n if (isRunLockStale(lock)) {\n printWarning(`A stale lock is left behind: ${describeRunLockOwner(lock)}.`);\n printInfo('It will be taken over by the next run or resume.');\n return;\n }\n printInfo(`Running: ${describeRunLockOwner(lock)}`);\n printInfo(`Last heartbeat ${since(lock.lastHeartbeatAt)}.`);\n}\n\nfunction printIssueLine(state: IssueState): void {\n const plan = state.plan;\n const run = plan?.runState;\n const phase = run?.currentPhase ?? state.nextPhase ?? '—';\n const attempt = run !== undefined && run.attempt > 0 ? `, attempt ${run.attempt}` : '';\n const retries = state.snapshot?.execution.retries;\n const retryLine = retries === undefined || retries === 0 ? '' : `, ${retries} retry(ies)`;\n const activity = since(state.snapshot?.updatedAt ?? plan?.lastAttemptAt);\n\n printInfo(\n `#${state.id} ${plan?.issueStatus ?? 'unknown'}${run === undefined ? '' : ` / ${run.status}`}` +\n ` — phase ${phase}${attempt}${retryLine}; last activity ${activity}`,\n );\n if (run?.blockedReason !== null && run?.blockedReason !== undefined) {\n printWarning(` Blocked: ${run.blockedReason}`);\n }\n}\n\n/* ── runs ───────────────────────────────────────────────────────────────── */\n\n/**\n * The history: what has been run in this project, how it ended, and why.\n *\n * Deliberately built from the same artifacts `status` reads rather than from a\n * separate history file — a second record of the truth is a second thing that\n * can be wrong.\n */\nexport async function runRuns(): Promise<number> {\n const paths = await project();\n if (paths === null) return 1;\n\n const issues = await allIssues(paths);\n if (issues.length === 0) {\n printInfo('No runs recorded for this project yet.');\n return 0;\n }\n\n for (const state of issues) {\n const plan = state.plan;\n if (plan === null) continue;\n const duration =\n state.snapshot?.elapsedSeconds === null || state.snapshot?.elapsedSeconds === undefined\n ? '—'\n : formatSeconds(state.snapshot.elapsedSeconds);\n const cause =\n plan.lastError === null ? '' : ` — ${plan.lastError.message.split('\\n')[0] ?? ''}`;\n\n printInfo(\n `#${state.id.padEnd(6)} ${plan.issueStatus.padEnd(12)} ${duration.padEnd(10)} ` +\n `last attempt ${since(plan.lastAttemptAt)}${cause}`,\n );\n }\n return 0;\n}\n\n/* ── logs ───────────────────────────────────────────────────────────────── */\n\nexport interface LogsOptions {\n /** Only these event types (`retry`, `phase:end`, …). Empty means all. */\n kind?: string[];\n /** Keep reading as the journal grows. */\n follow?: boolean;\n /** How many entries to show before following. */\n tail?: number;\n /** Polling interval of `--follow`, for tests. */\n pollMs?: number;\n /** Stops `--follow`. */\n signal?: AbortSignal;\n}\n\nconst DEFAULT_TAIL = 50;\nconst FOLLOW_POLL_MS = 1000;\n\n/**\n * Read the journal, filtered and readable.\n *\n * The journal is the only record that keeps *what happened in which order* —\n * the snapshot folded it away — so this is the command that answers \"what was\n * it doing at 3am\". `--kind retry,failover` is the filter that matters most:\n * on a six-hour run those are a handful of lines among thousands.\n */\nexport async function runLogs(issue?: string, options: LogsOptions = {}): Promise<number> {\n const paths = await project();\n if (paths === null) return 1;\n\n const target = issue ?? (await allIssues(paths))[0]?.id;\n if (target === undefined) {\n printInfo('No issue in this project has a journal yet.');\n return 0;\n }\n\n const issuePaths = getIssuePaths(paths.projectId, target);\n const kinds = new Set(options.kind ?? []);\n const tail = options.tail ?? DEFAULT_TAIL;\n\n const shown = new Set<number>();\n const render = (content: string, limit: number | null): void => {\n const entries = parseJournal(content).filter(\n (entry) => kinds.size === 0 || kinds.has(entry.event.type),\n );\n const slice = limit === null ? entries : entries.slice(-limit);\n for (const entry of slice) {\n if (shown.has(entry.seq)) continue;\n shown.add(entry.seq);\n printInfo(formatEvent(entry.seq, entry.event));\n }\n };\n\n const readAll = async (): Promise<string> =>\n `${await readIfPresent(issuePaths.rotatedEventsFile)}${await readIfPresent(issuePaths.eventsFile)}`;\n\n const initial = await readAll();\n if (initial === '') {\n printInfo(\n `Issue #${target} has no journal. Enable it with resilience.journal.enabled or --continuous.`,\n );\n return 0;\n }\n render(initial, tail);\n\n if (options.follow !== true) return 0;\n\n const poll = options.pollMs ?? FOLLOW_POLL_MS;\n const signal = options.signal;\n while (signal === undefined || !signal.aborted) {\n await sleep(poll, signal);\n if (signal?.aborted === true) break;\n render(await readAll(), null);\n }\n return 0;\n}\n\nfunction formatEvent(seq: number, event: SessionEvent): string {\n const at = 'at' in event ? event.at : '';\n const detail = describeEvent(event);\n return `${String(seq).padStart(5)} ${at} ${event.type}${detail === '' ? '' : ` — ${detail}`}`;\n}\n\nfunction describeEvent(event: SessionEvent): string {\n switch (event.type) {\n case 'phase:start':\n return event.phase;\n case 'phase:end':\n return `${event.phase} ${event.success ? 'ok' : `failed: ${event.error ?? ''}`}`;\n case 'retry':\n return `attempt ${event.attempt}, ${event.delaySeconds}s — ${event.reason}`;\n case 'log':\n return `${event.level}: ${event.message}`;\n case 'session:end':\n return event.status;\n default:\n return '';\n }\n}\n\nasync function readIfPresent(path: string): Promise<string> {\n try {\n return await readFile(path, 'utf-8');\n } catch {\n return '';\n }\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve) => {\n const timer = setTimeout(resolve, ms);\n timer.unref?.();\n signal?.addEventListener('abort', () => {\n clearTimeout(timer);\n resolve();\n });\n });\n}\n\n/* ── pause and cancel ───────────────────────────────────────────────────── */\n\nexport interface ControlOptions {\n /** Injectable signaller, so a test never signals a real process. */\n signalProcess?: (pid: number, signal: NodeJS.Signals) => void;\n}\n\n/**\n * Ask the running owner to stop, gracefully.\n *\n * `SIGTERM` is the whole implementation, and deliberately so: the owner already\n * knows how to stop well (`core/shutdown.ts` writes the checkpoint, stops the\n * agent with a grace period and closes the journal). Reimplementing any of that\n * from outside the process would be a second, worse version of it.\n */\nexport async function runPause(options: ControlOptions = {}): Promise<number> {\n return stopOwner('paused', options);\n}\n\n/**\n * Stop the run and mark it so a later `resume` does not pick it up silently.\n *\n * The difference from `pause` is not in how the process ends — it ends the same\n * way — but in what the state says afterwards: a paused run is meant to be\n * resumed, a cancelled one is not.\n */\nexport async function runCancel(issue?: string, options: ControlOptions = {}): Promise<number> {\n const code = await stopOwner('cancelled', options);\n if (code !== 0) return code;\n\n const paths = await project();\n if (paths === null) return 1;\n\n const target = issue ?? (await allIssues(paths))[0]?.id;\n if (target === undefined) return 0;\n\n const state = await loadIssueState(paths.projectId, target);\n if (state === null || state.plan === null) return 0;\n\n const { saveTaskPlan, isoNow } = await import('../core/state-manager.js');\n await saveTaskPlan(state.paths.tasksFile, {\n ...state.plan,\n runState: {\n currentPhase: state.plan.runState?.currentPhase ?? null,\n attempt: state.plan.runState?.attempt ?? 0,\n owner: null,\n status: 'blocked',\n blockedReason: 'Cancelled by the user',\n lastHeartbeatAt: isoNow(),\n },\n });\n printInfo(`Issue #${target} is marked cancelled. A resume will report it instead of running it.`);\n return 0;\n}\n\nasync function stopOwner(intent: string, options: ControlOptions): Promise<number> {\n const paths = await project();\n if (paths === null) return 1;\n\n const lock = await readRunLock(paths.runLockFile);\n if (lock === null) {\n printInfo('Nothing is running in this project.');\n return 0;\n }\n if (isRunLockStale(lock)) {\n printWarning(`The lock is stale (${describeRunLockOwner(lock)}); nothing to stop.`);\n return 0;\n }\n\n const send =\n options.signalProcess ?? ((pid: number, signal: NodeJS.Signals) => process.kill(pid, signal));\n try {\n send(lock.pid, 'SIGTERM');\n } catch (err) {\n printError(\n `Could not signal pid ${lock.pid}: ${err instanceof Error ? err.message : String(err)}`,\n );\n return 1;\n }\n\n printInfo(\n `Asked pid ${lock.pid} to stop (${intent}). It writes a checkpoint, stops the agent and closes its journal before exiting.`,\n );\n return 0;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
runPlan
|
|
4
|
+
} from "./chunk-QCPGHBSZ.js";
|
|
5
|
+
import "./chunk-Y5ZDJ4PJ.js";
|
|
6
|
+
import "./chunk-73ATPIYX.js";
|
|
7
|
+
import "./chunk-4JSOYE4G.js";
|
|
8
|
+
import "./chunk-KMMASOA6.js";
|
|
9
|
+
import "./chunk-LWRSMTTZ.js";
|
|
10
|
+
import "./chunk-MJULIKQU.js";
|
|
11
|
+
import "./chunk-LAFNLLQP.js";
|
|
12
|
+
import "./chunk-M66SJHQQ.js";
|
|
13
|
+
import "./chunk-EZATO3DW.js";
|
|
14
|
+
import "./chunk-42PJFMAR.js";
|
|
15
|
+
import "./chunk-DIDUVL6O.js";
|
|
16
|
+
import "./chunk-BYZDPR7N.js";
|
|
17
|
+
import "./chunk-CD5ULRVC.js";
|
|
18
|
+
export {
|
|
19
|
+
runPlan
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=plan-QOZ3RIO6.js.map
|
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
import "./chunk-3D4A4B46.js";
|
|
3
3
|
import {
|
|
4
4
|
loadRepositoryPolicy
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-EZATO3DW.js";
|
|
6
|
+
import "./chunk-42PJFMAR.js";
|
|
7
|
+
import "./chunk-DIDUVL6O.js";
|
|
7
8
|
import {
|
|
8
9
|
printError,
|
|
9
10
|
printInfo,
|
|
10
11
|
printWarning
|
|
11
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-BYZDPR7N.js";
|
|
13
|
+
import "./chunk-CD5ULRVC.js";
|
|
12
14
|
|
|
13
15
|
// src/commands/policy.ts
|
|
14
16
|
function groupSources(sources) {
|
|
@@ -114,4 +116,4 @@ async function runPolicy(options = {}) {
|
|
|
114
116
|
export {
|
|
115
117
|
runPolicy
|
|
116
118
|
};
|
|
117
|
-
//# sourceMappingURL=policy-
|
|
119
|
+
//# sourceMappingURL=policy-6S4GFC76.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/policy.ts"],"sourcesContent":["import type { PolicySource, RepositoryPolicy } from '../policy/index.js';\nimport { loadRepositoryPolicy } from '../policy/index.js';\nimport { printError, printInfo, printWarning } from '../ui/logger.js';\n\n/**\n * `issue-flow policy [--scope <dir>] [--json]`.\n *\n * The inspection surface of the policy layer, and the only one for now: this\n * issue delivers the foundation, so no prompt and no skill consumes\n * `loadRepositoryPolicy()` yet.\n *\n * It exists for two audiences. A human debugging a wrong discovery reads the\n * default output, where every section names the files it came from. The Agent\n * Skills read `--json`: they are markdown and cannot import TypeScript, so a\n * versioned JSON document on stdout is the only bridge available to them —\n * which is why `schemaVersion` is part of the payload rather than of the CLI.\n *\n * That makes `--json` a **published contract**, not a debugging convenience.\n * `skills/_shared/repository-policy.md` documents the payload, and\n * `policy-parity.test.ts` pins the fields the skills read: renaming or dropping\n * one silently makes every skill fall back to its defaults, and nothing would\n * fail — the two paths would simply start deciding differently. Adding a field\n * is safe (readers ignore what they do not know); removing or renaming one\n * bumps `schemaVersion`.\n */\n\nexport interface PolicyCommandOptions {\n scope?: string;\n json?: boolean;\n}\n\n/** Group the provenance entries by kind, preserving discovery order. */\nfunction groupSources(sources: PolicySource[]): Map<PolicySource['kind'], PolicySource[]> {\n const grouped = new Map<PolicySource['kind'], PolicySource[]>();\n for (const source of sources) {\n const bucket = grouped.get(source.kind);\n if (bucket === undefined) {\n grouped.set(source.kind, [source]);\n } else {\n bucket.push(source);\n }\n }\n return grouped;\n}\n\nfunction describeSource(source: PolicySource): string {\n const where = source.path ?? source.origin;\n const detail = source.detail === null ? '' : ` — ${source.detail}`;\n return `${source.status === 'found' ? '' : `[${source.status}] `}${where}${detail}`;\n}\n\n/**\n * Human-readable rendering.\n *\n * Content is deliberately never printed: a resolved policy carries whole\n * `AGENTS.md` and template bodies, and dumping them would bury the one thing\n * this view is for — which file each value came from. `--json` is where the\n * content lives.\n */\nfunction render(policy: RepositoryPolicy): void {\n const lines: string[] = [];\n const push = (line = ''): void => {\n lines.push(line);\n };\n\n push(`Repository: ${policy.root}`);\n push(`Scope: ${policy.scope ?? '(root)'}`);\n if (!policy.enabled) {\n push();\n push('Policy discovery is disabled (policy.enabled = false).');\n console.log(lines.join('\\n'));\n return;\n }\n\n push();\n push('Issues');\n push(` templates: ${policy.issues.templates.length}`);\n for (const template of policy.issues.templates) {\n const labels = template.labels.length > 0 ? ` labels=[${template.labels.join(', ')}]` : '';\n const type = template.type === null ? '' : ` type=${template.type}`;\n push(` - ${template.path} (${template.format}, ${template.origin})${type}${labels}`);\n }\n push(` types: ${policy.issues.types.length > 0 ? policy.issues.types.join(', ') : '(none)'}`);\n push(` labels: ${policy.issues.labels.length}`);\n for (const label of policy.issues.labels) {\n push(` - ${label.name}${label.description === null ? '' : ` — ${label.description}`}`);\n }\n push(` titleConvention: ${policy.issues.titleConvention ?? '(none)'}`);\n\n push();\n push('Pull Requests');\n push(` template: ${policy.pullRequests.templates[0]?.path ?? '(none)'}`);\n for (const template of policy.pullRequests.templates.slice(1)) {\n push(` - ${template.path}`);\n }\n push(` baseBranch: ${policy.pullRequests.baseBranch ?? '(none)'}`);\n push(` titleConvention: ${policy.pullRequests.titleConvention ?? '(none)'}`);\n\n push();\n push('Git');\n push(` branchConvention: ${policy.git.branchConvention ?? '(none)'}`);\n push(` commitConvention: ${policy.git.commitConvention ?? '(none)'}`);\n\n push();\n push(`Documents: ${policy.docs.length}`);\n for (const doc of policy.docs) {\n const scope = doc.scope === '' ? 'root' : doc.scope;\n const from = doc.referencedFrom === null ? '' : ` ← ${doc.referencedFrom}`;\n push(` - ${doc.path} (${doc.kind}, scope=${scope})${from}`);\n }\n\n push();\n push(`CODEOWNERS: ${policy.codeowners === null ? '(none)' : 'found'}`);\n\n push();\n push('Sources');\n for (const [kind, entries] of groupSources(policy.sources)) {\n push(` ${kind}`);\n for (const entry of entries) {\n push(` - ${describeSource(entry)}`);\n }\n }\n\n console.log(lines.join('\\n'));\n}\n\n/**\n * Resolve and print the repository policy. Returns the process exit code.\n *\n * Only a repository that cannot be located at all is a failure: an empty policy\n * is a legitimate — and common — answer, printed like any other.\n */\nexport async function runPolicy(options: PolicyCommandOptions = {}): Promise<number> {\n let policy: RepositoryPolicy;\n try {\n policy = await loadRepositoryPolicy({ scope: options.scope ?? null });\n } catch (err) {\n printError(err instanceof Error ? err.message : String(err));\n return 1;\n }\n\n if (options.json === true) {\n console.log(JSON.stringify(policy, null, 2));\n return 0;\n }\n\n render(policy);\n\n const unavailable = policy.sources.filter((source) => source.status === 'unavailable');\n if (unavailable.length > 0) {\n console.log('');\n printWarning(\n `${unavailable.length} source(s) could not be consulted; the policy above is incomplete.`,\n );\n } else if (policy.sources.length === 0) {\n console.log('');\n printInfo('This repository declares no policy sources; Issue Flow defaults apply.');\n }\n\n return 0;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/commands/policy.ts"],"sourcesContent":["import type { PolicySource, RepositoryPolicy } from '../policy/index.js';\nimport { loadRepositoryPolicy } from '../policy/index.js';\nimport { printError, printInfo, printWarning } from '../ui/logger.js';\n\n/**\n * `issue-flow policy [--scope <dir>] [--json]`.\n *\n * The inspection surface of the policy layer, and the only one for now: this\n * issue delivers the foundation, so no prompt and no skill consumes\n * `loadRepositoryPolicy()` yet.\n *\n * It exists for two audiences. A human debugging a wrong discovery reads the\n * default output, where every section names the files it came from. The Agent\n * Skills read `--json`: they are markdown and cannot import TypeScript, so a\n * versioned JSON document on stdout is the only bridge available to them —\n * which is why `schemaVersion` is part of the payload rather than of the CLI.\n *\n * That makes `--json` a **published contract**, not a debugging convenience.\n * `skills/_shared/repository-policy.md` documents the payload, and\n * `policy-parity.test.ts` pins the fields the skills read: renaming or dropping\n * one silently makes every skill fall back to its defaults, and nothing would\n * fail — the two paths would simply start deciding differently. Adding a field\n * is safe (readers ignore what they do not know); removing or renaming one\n * bumps `schemaVersion`.\n */\n\nexport interface PolicyCommandOptions {\n scope?: string;\n json?: boolean;\n}\n\n/** Group the provenance entries by kind, preserving discovery order. */\nfunction groupSources(sources: PolicySource[]): Map<PolicySource['kind'], PolicySource[]> {\n const grouped = new Map<PolicySource['kind'], PolicySource[]>();\n for (const source of sources) {\n const bucket = grouped.get(source.kind);\n if (bucket === undefined) {\n grouped.set(source.kind, [source]);\n } else {\n bucket.push(source);\n }\n }\n return grouped;\n}\n\nfunction describeSource(source: PolicySource): string {\n const where = source.path ?? source.origin;\n const detail = source.detail === null ? '' : ` — ${source.detail}`;\n return `${source.status === 'found' ? '' : `[${source.status}] `}${where}${detail}`;\n}\n\n/**\n * Human-readable rendering.\n *\n * Content is deliberately never printed: a resolved policy carries whole\n * `AGENTS.md` and template bodies, and dumping them would bury the one thing\n * this view is for — which file each value came from. `--json` is where the\n * content lives.\n */\nfunction render(policy: RepositoryPolicy): void {\n const lines: string[] = [];\n const push = (line = ''): void => {\n lines.push(line);\n };\n\n push(`Repository: ${policy.root}`);\n push(`Scope: ${policy.scope ?? '(root)'}`);\n if (!policy.enabled) {\n push();\n push('Policy discovery is disabled (policy.enabled = false).');\n console.log(lines.join('\\n'));\n return;\n }\n\n push();\n push('Issues');\n push(` templates: ${policy.issues.templates.length}`);\n for (const template of policy.issues.templates) {\n const labels = template.labels.length > 0 ? ` labels=[${template.labels.join(', ')}]` : '';\n const type = template.type === null ? '' : ` type=${template.type}`;\n push(` - ${template.path} (${template.format}, ${template.origin})${type}${labels}`);\n }\n push(` types: ${policy.issues.types.length > 0 ? policy.issues.types.join(', ') : '(none)'}`);\n push(` labels: ${policy.issues.labels.length}`);\n for (const label of policy.issues.labels) {\n push(` - ${label.name}${label.description === null ? '' : ` — ${label.description}`}`);\n }\n push(` titleConvention: ${policy.issues.titleConvention ?? '(none)'}`);\n\n push();\n push('Pull Requests');\n push(` template: ${policy.pullRequests.templates[0]?.path ?? '(none)'}`);\n for (const template of policy.pullRequests.templates.slice(1)) {\n push(` - ${template.path}`);\n }\n push(` baseBranch: ${policy.pullRequests.baseBranch ?? '(none)'}`);\n push(` titleConvention: ${policy.pullRequests.titleConvention ?? '(none)'}`);\n\n push();\n push('Git');\n push(` branchConvention: ${policy.git.branchConvention ?? '(none)'}`);\n push(` commitConvention: ${policy.git.commitConvention ?? '(none)'}`);\n\n push();\n push(`Documents: ${policy.docs.length}`);\n for (const doc of policy.docs) {\n const scope = doc.scope === '' ? 'root' : doc.scope;\n const from = doc.referencedFrom === null ? '' : ` ← ${doc.referencedFrom}`;\n push(` - ${doc.path} (${doc.kind}, scope=${scope})${from}`);\n }\n\n push();\n push(`CODEOWNERS: ${policy.codeowners === null ? '(none)' : 'found'}`);\n\n push();\n push('Sources');\n for (const [kind, entries] of groupSources(policy.sources)) {\n push(` ${kind}`);\n for (const entry of entries) {\n push(` - ${describeSource(entry)}`);\n }\n }\n\n console.log(lines.join('\\n'));\n}\n\n/**\n * Resolve and print the repository policy. Returns the process exit code.\n *\n * Only a repository that cannot be located at all is a failure: an empty policy\n * is a legitimate — and common — answer, printed like any other.\n */\nexport async function runPolicy(options: PolicyCommandOptions = {}): Promise<number> {\n let policy: RepositoryPolicy;\n try {\n policy = await loadRepositoryPolicy({ scope: options.scope ?? null });\n } catch (err) {\n printError(err instanceof Error ? err.message : String(err));\n return 1;\n }\n\n if (options.json === true) {\n console.log(JSON.stringify(policy, null, 2));\n return 0;\n }\n\n render(policy);\n\n const unavailable = policy.sources.filter((source) => source.status === 'unavailable');\n if (unavailable.length > 0) {\n console.log('');\n printWarning(\n `${unavailable.length} source(s) could not be consulted; the policy above is incomplete.`,\n );\n } else if (policy.sources.length === 0) {\n console.log('');\n printInfo('This repository declares no policy sources; Issue Flow defaults apply.');\n }\n\n return 0;\n}\n"],"mappings":";;;;;;;;;;;;;;;AAgCA,SAAS,aAAa,SAAoE;AACxF,QAAM,UAAU,oBAAI,IAA0C;AAC9D,aAAW,UAAU,SAAS;AAC5B,UAAM,SAAS,QAAQ,IAAI,OAAO,IAAI;AACtC,QAAI,WAAW,QAAW;AACxB,cAAQ,IAAI,OAAO,MAAM,CAAC,MAAM,CAAC;AAAA,IACnC,OAAO;AACL,aAAO,KAAK,MAAM;AAAA,IACpB;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,eAAe,QAA8B;AACpD,QAAM,QAAQ,OAAO,QAAQ,OAAO;AACpC,QAAM,SAAS,OAAO,WAAW,OAAO,KAAK,WAAM,OAAO,MAAM;AAChE,SAAO,GAAG,OAAO,WAAW,UAAU,KAAK,IAAI,OAAO,MAAM,IAAI,GAAG,KAAK,GAAG,MAAM;AACnF;AAUA,SAAS,OAAO,QAAgC;AAC9C,QAAM,QAAkB,CAAC;AACzB,QAAM,OAAO,CAAC,OAAO,OAAa;AAChC,UAAM,KAAK,IAAI;AAAA,EACjB;AAEA,OAAK,eAAe,OAAO,IAAI,EAAE;AACjC,OAAK,eAAe,OAAO,SAAS,QAAQ,EAAE;AAC9C,MAAI,CAAC,OAAO,SAAS;AACnB,SAAK;AACL,SAAK,wDAAwD;AAC7D,YAAQ,IAAI,MAAM,KAAK,IAAI,CAAC;AAC5B;AAAA,EACF;AAEA,OAAK;AACL,OAAK,QAAQ;AACb,OAAK,gBAAgB,OAAO,OAAO,UAAU,MAAM,EAAE;AACrD,aAAW,YAAY,OAAO,OAAO,WAAW;AAC9C,UAAM,SAAS,SAAS,OAAO,SAAS,IAAI,YAAY,SAAS,OAAO,KAAK,IAAI,CAAC,MAAM;AACxF,UAAM,OAAO,SAAS,SAAS,OAAO,KAAK,SAAS,SAAS,IAAI;AACjE,SAAK,SAAS,SAAS,IAAI,KAAK,SAAS,MAAM,KAAK,SAAS,MAAM,IAAI,IAAI,GAAG,MAAM,EAAE;AAAA,EACxF;AACA,OAAK,aAAa,OAAO,OAAO,MAAM,SAAS,IAAI,OAAO,OAAO,MAAM,KAAK,IAAI,IAAI,QAAQ,EAAE;AAC9F,OAAK,aAAa,OAAO,OAAO,OAAO,MAAM,EAAE;AAC/C,aAAW,SAAS,OAAO,OAAO,QAAQ;AACxC,SAAK,SAAS,MAAM,IAAI,GAAG,MAAM,gBAAgB,OAAO,KAAK,WAAM,MAAM,WAAW,EAAE,EAAE;AAAA,EAC1F;AACA,OAAK,sBAAsB,OAAO,OAAO,mBAAmB,QAAQ,EAAE;AAEtE,OAAK;AACL,OAAK,eAAe;AACpB,OAAK,gBAAgB,OAAO,aAAa,UAAU,CAAC,GAAG,QAAQ,QAAQ,EAAE;AACzE,aAAW,YAAY,OAAO,aAAa,UAAU,MAAM,CAAC,GAAG;AAC7D,SAAK,SAAS,SAAS,IAAI,EAAE;AAAA,EAC/B;AACA,OAAK,sBAAsB,OAAO,aAAa,cAAc,QAAQ,EAAE;AACvE,OAAK,sBAAsB,OAAO,aAAa,mBAAmB,QAAQ,EAAE;AAE5E,OAAK;AACL,OAAK,KAAK;AACV,OAAK,uBAAuB,OAAO,IAAI,oBAAoB,QAAQ,EAAE;AACrE,OAAK,uBAAuB,OAAO,IAAI,oBAAoB,QAAQ,EAAE;AAErE,OAAK;AACL,OAAK,cAAc,OAAO,KAAK,MAAM,EAAE;AACvC,aAAW,OAAO,OAAO,MAAM;AAC7B,UAAM,QAAQ,IAAI,UAAU,KAAK,SAAS,IAAI;AAC9C,UAAM,OAAO,IAAI,mBAAmB,OAAO,KAAK,WAAM,IAAI,cAAc;AACxE,SAAK,OAAO,IAAI,IAAI,KAAK,IAAI,IAAI,WAAW,KAAK,IAAI,IAAI,EAAE;AAAA,EAC7D;AAEA,OAAK;AACL,OAAK,eAAe,OAAO,eAAe,OAAO,WAAW,OAAO,EAAE;AAErE,OAAK;AACL,OAAK,SAAS;AACd,aAAW,CAAC,MAAM,OAAO,KAAK,aAAa,OAAO,OAAO,GAAG;AAC1D,SAAK,KAAK,IAAI,EAAE;AAChB,eAAW,SAAS,SAAS;AAC3B,WAAK,SAAS,eAAe,KAAK,CAAC,EAAE;AAAA,IACvC;AAAA,EACF;AAEA,UAAQ,IAAI,MAAM,KAAK,IAAI,CAAC;AAC9B;AAQA,eAAsB,UAAU,UAAgC,CAAC,GAAoB;AACnF,MAAI;AACJ,MAAI;AACF,aAAS,MAAM,qBAAqB,EAAE,OAAO,QAAQ,SAAS,KAAK,CAAC;AAAA,EACtE,SAAS,KAAK;AACZ,eAAW,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC3D,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ,SAAS,MAAM;AACzB,YAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAC3C,WAAO;AAAA,EACT;AAEA,SAAO,MAAM;AAEb,QAAM,cAAc,OAAO,QAAQ,OAAO,CAAC,WAAW,OAAO,WAAW,aAAa;AACrF,MAAI,YAAY,SAAS,GAAG;AAC1B,YAAQ,IAAI,EAAE;AACd;AAAA,MACE,GAAG,YAAY,MAAM;AAAA,IACvB;AAAA,EACF,WAAW,OAAO,QAAQ,WAAW,GAAG;AACtC,YAAQ,IAAI,EAAE;AACd,cAAU,wEAAwE;AAAA,EACpF;AAEA,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
issueClosesLines,
|
|
4
|
+
multiIssueContext,
|
|
5
|
+
runPr
|
|
6
|
+
} from "./chunk-FADTCQGM.js";
|
|
7
|
+
import "./chunk-TSOLWK7Y.js";
|
|
8
|
+
import "./chunk-Y5ZDJ4PJ.js";
|
|
9
|
+
import "./chunk-73ATPIYX.js";
|
|
10
|
+
import "./chunk-4JSOYE4G.js";
|
|
11
|
+
import "./chunk-KMMASOA6.js";
|
|
12
|
+
import "./chunk-LWRSMTTZ.js";
|
|
13
|
+
import "./chunk-MJULIKQU.js";
|
|
14
|
+
import "./chunk-LAFNLLQP.js";
|
|
15
|
+
import "./chunk-M66SJHQQ.js";
|
|
16
|
+
import "./chunk-EZATO3DW.js";
|
|
17
|
+
import "./chunk-42PJFMAR.js";
|
|
18
|
+
import "./chunk-DIDUVL6O.js";
|
|
19
|
+
import "./chunk-BYZDPR7N.js";
|
|
20
|
+
import "./chunk-CD5ULRVC.js";
|
|
21
|
+
export {
|
|
22
|
+
issueClosesLines,
|
|
23
|
+
multiIssueContext,
|
|
24
|
+
runPr
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=pr-EBBHD2ZX.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
exitCodeFor,
|
|
4
|
+
runPrReview
|
|
5
|
+
} from "./chunk-A4SLHOW7.js";
|
|
6
|
+
import "./chunk-TSOLWK7Y.js";
|
|
7
|
+
import "./chunk-4JSOYE4G.js";
|
|
8
|
+
import "./chunk-MJULIKQU.js";
|
|
9
|
+
import "./chunk-LAFNLLQP.js";
|
|
10
|
+
import "./chunk-M66SJHQQ.js";
|
|
11
|
+
import "./chunk-EZATO3DW.js";
|
|
12
|
+
import "./chunk-42PJFMAR.js";
|
|
13
|
+
import "./chunk-DIDUVL6O.js";
|
|
14
|
+
import "./chunk-BYZDPR7N.js";
|
|
15
|
+
import "./chunk-CD5ULRVC.js";
|
|
16
|
+
export {
|
|
17
|
+
exitCodeFor,
|
|
18
|
+
runPrReview
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=pr-review-VVMW5PO4.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
runPrd
|
|
4
|
+
} from "./chunk-M5UCXTIT.js";
|
|
5
|
+
import "./chunk-Y5ZDJ4PJ.js";
|
|
6
|
+
import "./chunk-73ATPIYX.js";
|
|
7
|
+
import "./chunk-4JSOYE4G.js";
|
|
8
|
+
import "./chunk-KMMASOA6.js";
|
|
9
|
+
import "./chunk-LWRSMTTZ.js";
|
|
10
|
+
import "./chunk-MJULIKQU.js";
|
|
11
|
+
import "./chunk-LAFNLLQP.js";
|
|
12
|
+
import "./chunk-M66SJHQQ.js";
|
|
13
|
+
import "./chunk-EZATO3DW.js";
|
|
14
|
+
import "./chunk-42PJFMAR.js";
|
|
15
|
+
import "./chunk-DIDUVL6O.js";
|
|
16
|
+
import "./chunk-BYZDPR7N.js";
|
|
17
|
+
import "./chunk-CD5ULRVC.js";
|
|
18
|
+
export {
|
|
19
|
+
runPrd
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=prd-JQSZF7PR.js.map
|