@open-tomato/rafa 0.36.0 → 0.38.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 +18 -4
- package/dist/cli.js +11916 -8823
- package/dist/{describe-5hqfwbjc.js → describe-pwhh0vws.js} +3 -3
- package/dist/effort/store/index.js +4 -4
- package/dist/{index-qvbeqrx3.js → index-74btmn1e.js} +177 -43
- package/dist/{index-2n2nedrk.js → index-e3vhhfka.js} +9500 -8342
- package/dist/{index-k6sd4ym5.js → index-eyjq63mx.js} +2 -2
- package/dist/{index-fzhd3d4y.js → index-m2ahxjw8.js} +5 -1
- package/dist/{index-ddzzmcaz.js → index-mzcz139z.js} +2333 -650
- package/dist/{index-3mzw3kzh.js → index-sr3kc4mf.js} +1 -1
- package/dist/{index-jfas157k.js → index-w9naz372.js} +10 -1
- package/dist/{index-7bb0fdb7.js → index-zdz0mp63.js} +2 -2
- package/dist/index.js +10 -8
- package/dist/plan/index.js +3 -3
- package/package.json +1 -1
|
@@ -3,9 +3,9 @@ import {
|
|
|
3
3
|
DESCRIBE_BINARY,
|
|
4
4
|
DESCRIBE_SCHEMA_VERSION,
|
|
5
5
|
describeRegistry
|
|
6
|
-
} from "./index-
|
|
7
|
-
import"./index-
|
|
8
|
-
import"./index-
|
|
6
|
+
} from "./index-eyjq63mx.js";
|
|
7
|
+
import"./index-sr3kc4mf.js";
|
|
8
|
+
import"./index-m2ahxjw8.js";
|
|
9
9
|
import"./index-wp8c28fd.js";
|
|
10
10
|
export {
|
|
11
11
|
describeRegistry,
|
|
@@ -14,13 +14,13 @@ import {
|
|
|
14
14
|
openNdjsonStore,
|
|
15
15
|
openSqliteStore,
|
|
16
16
|
selectEffortStore
|
|
17
|
-
} from "../../index-
|
|
18
|
-
import"../../index-
|
|
17
|
+
} from "../../index-74btmn1e.js";
|
|
18
|
+
import"../../index-w9naz372.js";
|
|
19
19
|
import"../../index-d6d0942q.js";
|
|
20
|
-
import"../../index-
|
|
20
|
+
import"../../index-sr3kc4mf.js";
|
|
21
21
|
import {
|
|
22
22
|
STORE_BACKENDS
|
|
23
|
-
} from "../../index-
|
|
23
|
+
} from "../../index-m2ahxjw8.js";
|
|
24
24
|
import"../../index-wp8c28fd.js";
|
|
25
25
|
export {
|
|
26
26
|
selectEffortStore,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
parseConfigText,
|
|
16
16
|
readRafaBlocks,
|
|
17
17
|
resolveConfig
|
|
18
|
-
} from "./index-
|
|
18
|
+
} from "./index-w9naz372.js";
|
|
19
19
|
import {
|
|
20
20
|
bless,
|
|
21
21
|
merge
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
import {
|
|
24
24
|
CommandExit,
|
|
25
25
|
commandSpelling
|
|
26
|
-
} from "./index-
|
|
26
|
+
} from "./index-sr3kc4mf.js";
|
|
27
27
|
import {
|
|
28
28
|
BUSY_TIMEOUT_MAX_MS,
|
|
29
29
|
BUSY_TIMEOUT_MIN_MS,
|
|
@@ -39,7 +39,7 @@ import {
|
|
|
39
39
|
messageOf,
|
|
40
40
|
resolveInstinctScopes,
|
|
41
41
|
tierExists
|
|
42
|
-
} from "./index-
|
|
42
|
+
} from "./index-m2ahxjw8.js";
|
|
43
43
|
import {
|
|
44
44
|
actionHash,
|
|
45
45
|
triggerKey
|
|
@@ -259,7 +259,7 @@ import { Database as Database2 } from "bun:sqlite";
|
|
|
259
259
|
|
|
260
260
|
// package.json
|
|
261
261
|
var name = "@open-tomato/rafa";
|
|
262
|
-
var version = "0.
|
|
262
|
+
var version = "0.38.0";
|
|
263
263
|
|
|
264
264
|
// src/cli/version.ts
|
|
265
265
|
var RAFA_VERSION = version;
|
|
@@ -5120,6 +5120,62 @@ function createClaudePlanner(options) {
|
|
|
5120
5120
|
return Object.freeze({ create });
|
|
5121
5121
|
}
|
|
5122
5122
|
|
|
5123
|
+
// src/adapters/tracker/github-edit.ts
|
|
5124
|
+
var PREFIX2 = "github tracker";
|
|
5125
|
+
var EDITABLE_FIELDS = "title,body,state,labels,author";
|
|
5126
|
+
function viewedEditable(payload, labelNames) {
|
|
5127
|
+
if (!isMapping(payload))
|
|
5128
|
+
return `${describeValue(payload)}, expected a mapping`;
|
|
5129
|
+
const { title, body, state, author } = payload;
|
|
5130
|
+
if (typeof title !== "string")
|
|
5131
|
+
return `title ${describeValue(title)}, expected a string`;
|
|
5132
|
+
if (typeof body !== "string")
|
|
5133
|
+
return `body ${describeValue(body)}, expected a string`;
|
|
5134
|
+
if (state !== "OPEN" && state !== "CLOSED")
|
|
5135
|
+
return `state ${describeValue(state)}, expected "OPEN" or "CLOSED"`;
|
|
5136
|
+
const labels = labelNames(payload["labels"]);
|
|
5137
|
+
if (labels === null)
|
|
5138
|
+
return "labels that are not a list of named labels";
|
|
5139
|
+
const login = isMapping(author) ? author["login"] : undefined;
|
|
5140
|
+
if (typeof login !== "string")
|
|
5141
|
+
return `author ${describeValue(author)}, expected a mapping holding a string login`;
|
|
5142
|
+
return { title, body, open: state === "OPEN", labels, author: login };
|
|
5143
|
+
}
|
|
5144
|
+
function editProblem(change) {
|
|
5145
|
+
if (!isMapping(change))
|
|
5146
|
+
return `the change is ${describeValue(change)}, expected a mapping`;
|
|
5147
|
+
const { body, title } = change;
|
|
5148
|
+
if (body === undefined && title === undefined)
|
|
5149
|
+
return "the change names neither a body nor a title";
|
|
5150
|
+
if (body !== undefined && typeof body !== "string")
|
|
5151
|
+
return `body is ${describeValue(body)}, expected a string`;
|
|
5152
|
+
return title !== undefined && typeof title !== "string" ? `title is ${describeValue(title)}, expected a string` : null;
|
|
5153
|
+
}
|
|
5154
|
+
function optionalFlag(name2, value) {
|
|
5155
|
+
return value === undefined ? [] : [name2, value];
|
|
5156
|
+
}
|
|
5157
|
+
function createGithubEditMembers(readers) {
|
|
5158
|
+
const { run, issueNumberOf, parseJson, labelNames } = readers;
|
|
5159
|
+
const editable = async (ref) => {
|
|
5160
|
+
const number = issueNumberOf(ref);
|
|
5161
|
+
const command = `gh issue view ${number}`;
|
|
5162
|
+
const stdout = await run(["issue", "view", number, ...optionalFlag("--repo", ref.repo), "--json", EDITABLE_FIELDS], command);
|
|
5163
|
+
const viewed = viewedEditable(parseJson(stdout, command), labelNames);
|
|
5164
|
+
if (typeof viewed === "string")
|
|
5165
|
+
throw new Error(`${PREFIX2}: ${command} answered ${viewed}`);
|
|
5166
|
+
return { ref, ...viewed };
|
|
5167
|
+
};
|
|
5168
|
+
const edit = async (ref, change) => {
|
|
5169
|
+
const number = issueNumberOf(ref);
|
|
5170
|
+
const problem = editProblem(change);
|
|
5171
|
+
if (problem !== null)
|
|
5172
|
+
throw new TypeError(`${PREFIX2}: refused an edit of issue #${number}: ${problem}`);
|
|
5173
|
+
const bodyFlag = change.body === undefined ? [] : ["--body-file", "-"];
|
|
5174
|
+
await run(["issue", "edit", number, ...optionalFlag("--repo", ref.repo), ...optionalFlag("--title", change.title), ...bodyFlag], `gh issue edit ${number}`, change.body);
|
|
5175
|
+
};
|
|
5176
|
+
return Object.freeze({ editable, edit });
|
|
5177
|
+
}
|
|
5178
|
+
|
|
5123
5179
|
// src/adapters/tracker/issue-values.ts
|
|
5124
5180
|
var TYPE_MEMBERS = {
|
|
5125
5181
|
code: true,
|
|
@@ -5151,7 +5207,7 @@ var ISSUE_PRIORITIES = Object.freeze(Object.keys(PRIORITY_MEMBERS));
|
|
|
5151
5207
|
var ISSUE_STATES = Object.freeze(Object.keys(STATE_MEMBERS));
|
|
5152
5208
|
|
|
5153
5209
|
// src/adapters/tracker/github.ts
|
|
5154
|
-
var
|
|
5210
|
+
var PREFIX3 = "github tracker";
|
|
5155
5211
|
var GITHUB_LABELS = Object.freeze({
|
|
5156
5212
|
modulePrefix: "module:",
|
|
5157
5213
|
typePrefix: "type:",
|
|
@@ -5181,7 +5237,7 @@ var REST_STATES = {
|
|
|
5181
5237
|
};
|
|
5182
5238
|
function createGhRunner(options) {
|
|
5183
5239
|
const { cwd, command = "gh", env, timeoutMs } = options;
|
|
5184
|
-
return async (args) => {
|
|
5240
|
+
return async (args, stdin) => {
|
|
5185
5241
|
const executable = env === undefined ? command : Bun.which(command, { PATH: env.PATH ?? "", cwd });
|
|
5186
5242
|
if (executable === null) {
|
|
5187
5243
|
return { ok: false, stdout: "", stderr: notOnPath(command, cwd, env?.PATH) };
|
|
@@ -5190,7 +5246,7 @@ function createGhRunner(options) {
|
|
|
5190
5246
|
const proc = Bun.spawn([executable, ...args], {
|
|
5191
5247
|
cwd,
|
|
5192
5248
|
...env === undefined ? {} : { env: { ...env } },
|
|
5193
|
-
stdin: "ignore",
|
|
5249
|
+
stdin: stdin === undefined ? "ignore" : new TextEncoder().encode(stdin),
|
|
5194
5250
|
stdout: "pipe",
|
|
5195
5251
|
stderr: "pipe"
|
|
5196
5252
|
});
|
|
@@ -5245,7 +5301,7 @@ function isRepoName(value) {
|
|
|
5245
5301
|
function isLabelPart(value) {
|
|
5246
5302
|
return typeof value === "string" && !value.includes(",");
|
|
5247
5303
|
}
|
|
5248
|
-
function
|
|
5304
|
+
function optionalFlag2(name2, value) {
|
|
5249
5305
|
return value === undefined ? [] : [name2, value];
|
|
5250
5306
|
}
|
|
5251
5307
|
function detailOf(result) {
|
|
@@ -5288,13 +5344,13 @@ function labelsFor(draft) {
|
|
|
5288
5344
|
}
|
|
5289
5345
|
function issueNumberOfRef(ref) {
|
|
5290
5346
|
if (ref.kind !== "github") {
|
|
5291
|
-
throw new Error(`${
|
|
5347
|
+
throw new Error(`${PREFIX3}: refused a ref of kind ${describeValue(ref.kind)}; this tracker reads github refs only`);
|
|
5292
5348
|
}
|
|
5293
5349
|
if (!isIssueNumber3(ref.externalId)) {
|
|
5294
|
-
throw new Error(`${
|
|
5350
|
+
throw new Error(`${PREFIX3}: externalId ${describeValue(ref.externalId)} is not a GitHub issue number,` + " expected a positive whole number with no leading zero");
|
|
5295
5351
|
}
|
|
5296
5352
|
if (ref.repo !== undefined && !isRepoName(ref.repo)) {
|
|
5297
|
-
throw new Error(`${
|
|
5353
|
+
throw new Error(`${PREFIX3}: repo ${describeValue(ref.repo)} is not a repository, expected owner/name`);
|
|
5298
5354
|
}
|
|
5299
5355
|
return ref.externalId;
|
|
5300
5356
|
}
|
|
@@ -5302,7 +5358,7 @@ function parseJson(stdout, command) {
|
|
|
5302
5358
|
try {
|
|
5303
5359
|
return JSON.parse(stdout);
|
|
5304
5360
|
} catch (error) {
|
|
5305
|
-
throw new Error(`${
|
|
5361
|
+
throw new Error(`${PREFIX3}: ${command} wrote output that is not JSON: ${messageOf(error)}`, {
|
|
5306
5362
|
cause: error
|
|
5307
5363
|
});
|
|
5308
5364
|
}
|
|
@@ -5336,19 +5392,19 @@ function viewedIssue(stdout, number) {
|
|
|
5336
5392
|
const payload = parseJson(stdout, command);
|
|
5337
5393
|
const problem = viewProblem(payload, number);
|
|
5338
5394
|
if (problem !== null)
|
|
5339
|
-
throw new Error(`${
|
|
5395
|
+
throw new Error(`${PREFIX3}: ${command} answered ${problem}`);
|
|
5340
5396
|
const checked = payload;
|
|
5341
5397
|
return { ...checked, labels: labelNames(checked.labels) ?? [] };
|
|
5342
5398
|
}
|
|
5343
5399
|
function listedRows(stdout) {
|
|
5344
5400
|
const payload = parseJson(stdout, LIST_COMMAND);
|
|
5345
5401
|
if (!Array.isArray(payload)) {
|
|
5346
|
-
throw new Error(`${
|
|
5402
|
+
throw new Error(`${PREFIX3}: ${LIST_COMMAND} answered ${describeValue(payload)}, expected a list`);
|
|
5347
5403
|
}
|
|
5348
5404
|
return payload;
|
|
5349
5405
|
}
|
|
5350
5406
|
function rowRefusal(index, problem) {
|
|
5351
|
-
return new Error(`${
|
|
5407
|
+
return new Error(`${PREFIX3}: ${LIST_COMMAND} answered row ${index} ${problem}`);
|
|
5352
5408
|
}
|
|
5353
5409
|
function listedRow(row, index) {
|
|
5354
5410
|
if (!isMapping(row))
|
|
@@ -5421,15 +5477,15 @@ function readBackState(state) {
|
|
|
5421
5477
|
function stateQueryRefusal(state) {
|
|
5422
5478
|
const bucket = REST_STATES[state];
|
|
5423
5479
|
const siblings = ISSUE_STATES.filter((other) => other !== state && REST_STATES[other] === bucket);
|
|
5424
|
-
return `${
|
|
5480
|
+
return `${PREFIX3}: find cannot narrow by state ${describeValue(state)}: GitHub holds an issue open or` + ` closed, and ${state} shares ${bucket} with ${siblings.join(", ")}, so a result would include` + " those too. Search without a state, then read each issue with get.";
|
|
5425
5481
|
}
|
|
5426
5482
|
function createGithubTracker(options) {
|
|
5427
5483
|
const { gh } = options;
|
|
5428
5484
|
let knownLabels = new Set;
|
|
5429
|
-
async function run(args, command) {
|
|
5430
|
-
const result = await gh(args);
|
|
5485
|
+
async function run(args, command, stdin) {
|
|
5486
|
+
const result = await gh(args, stdin);
|
|
5431
5487
|
if (!result.ok)
|
|
5432
|
-
throw new Error(`${
|
|
5488
|
+
throw new Error(`${PREFIX3}: ${command} failed: ${detailOf(result)}`);
|
|
5433
5489
|
return result.stdout;
|
|
5434
5490
|
}
|
|
5435
5491
|
async function ensureLabels(names) {
|
|
@@ -5462,7 +5518,7 @@ function createGithubTracker(options) {
|
|
|
5462
5518
|
create: async (draft) => {
|
|
5463
5519
|
const problem = draftProblem(draft);
|
|
5464
5520
|
if (problem !== null)
|
|
5465
|
-
throw new TypeError(`${
|
|
5521
|
+
throw new TypeError(`${PREFIX3}: refused to file an invalid draft: ${problem}`);
|
|
5466
5522
|
const labels = labelsFor(draft);
|
|
5467
5523
|
await ensureLabels(labels);
|
|
5468
5524
|
const stdout = await run([
|
|
@@ -5478,20 +5534,20 @@ function createGithubTracker(options) {
|
|
|
5478
5534
|
`).at(-1) ?? "";
|
|
5479
5535
|
const number = CREATED_ISSUE_URL.exec(url)?.[1];
|
|
5480
5536
|
if (number === undefined) {
|
|
5481
|
-
throw new Error(`${
|
|
5537
|
+
throw new Error(`${PREFIX3}: gh issue create exited 0 and printed no issue URL, so the issue may exist` + ` unrecorded; it wrote ${describeValue(stdout)}`);
|
|
5482
5538
|
}
|
|
5483
5539
|
return { opt: draft.opt, kind: "github", externalId: number, url, module: draft.module };
|
|
5484
5540
|
},
|
|
5485
5541
|
get: async (ref) => {
|
|
5486
5542
|
const number = issueNumberOfRef(ref);
|
|
5487
|
-
const stdout = await run(["issue", "view", number, ...
|
|
5543
|
+
const stdout = await run(["issue", "view", number, ...optionalFlag2("--repo", ref.repo), "--json", VIEW_FIELDS], `gh issue view ${number}`);
|
|
5488
5544
|
return issueOf(ref, viewedIssue(stdout, number));
|
|
5489
5545
|
},
|
|
5490
5546
|
find: async (query) => {
|
|
5491
5547
|
if (query.state !== undefined)
|
|
5492
5548
|
throw new Error(stateQueryRefusal(query.state));
|
|
5493
5549
|
if (query.module !== undefined && !isLabelPart(query.module)) {
|
|
5494
|
-
throw new TypeError(`${
|
|
5550
|
+
throw new TypeError(`${PREFIX3}: find refused module ${describeValue(query.module)}, expected a string holding no comma`);
|
|
5495
5551
|
}
|
|
5496
5552
|
const moduleLabel = query.module === undefined ? undefined : `${GITHUB_LABELS.modulePrefix}${query.module}`;
|
|
5497
5553
|
const typeLabel = query.type === undefined ? undefined : `${GITHUB_LABELS.typePrefix}${query.type}`;
|
|
@@ -5504,15 +5560,15 @@ function createGithubTracker(options) {
|
|
|
5504
5560
|
LIST_FIELDS,
|
|
5505
5561
|
"--limit",
|
|
5506
5562
|
String(query.limit ?? DEFAULT_FIND_LIMIT),
|
|
5507
|
-
...
|
|
5508
|
-
...
|
|
5509
|
-
...
|
|
5563
|
+
...optionalFlag2("--label", moduleLabel),
|
|
5564
|
+
...optionalFlag2("--label", typeLabel),
|
|
5565
|
+
...optionalFlag2("--search", query.text)
|
|
5510
5566
|
], LIST_COMMAND);
|
|
5511
5567
|
return listedIssues(stdout).map(refOfRow);
|
|
5512
5568
|
},
|
|
5513
5569
|
openIssues: async (type) => {
|
|
5514
5570
|
if (!isOneOf(ISSUE_TYPES, type)) {
|
|
5515
|
-
throw new TypeError(`${
|
|
5571
|
+
throw new TypeError(`${PREFIX3}: openIssues refused type ${describeValue(type)}, expected one of: ${ISSUE_TYPES.join(", ")}`);
|
|
5516
5572
|
}
|
|
5517
5573
|
const stdout = await run([
|
|
5518
5574
|
"issue",
|
|
@@ -5531,21 +5587,21 @@ function createGithubTracker(options) {
|
|
|
5531
5587
|
comment: async (ref, body) => {
|
|
5532
5588
|
const number = issueNumberOfRef(ref);
|
|
5533
5589
|
if (typeof body !== "string") {
|
|
5534
|
-
throw new TypeError(`${
|
|
5590
|
+
throw new TypeError(`${PREFIX3}: refused a comment body ${describeValue(body)}, expected a string`);
|
|
5535
5591
|
}
|
|
5536
|
-
await run(["issue", "comment", number, ...
|
|
5592
|
+
await run(["issue", "comment", number, ...optionalFlag2("--repo", ref.repo), "--body", body], `gh issue comment ${number}`);
|
|
5537
5593
|
},
|
|
5538
5594
|
transition: async (ref, state) => {
|
|
5539
5595
|
const number = issueNumberOfRef(ref);
|
|
5540
5596
|
if (!isOneOf(ISSUE_STATES, state)) {
|
|
5541
|
-
throw new TypeError(`${
|
|
5597
|
+
throw new TypeError(`${PREFIX3}: refused state ${describeValue(state)}, expected one of: ${ISSUE_STATES.join(", ")}`);
|
|
5542
5598
|
}
|
|
5543
5599
|
if (REST_STATES[state] === "closed") {
|
|
5544
5600
|
const path = `repos/${ref.repo ?? "{owner}/{repo}"}/issues/${number}`;
|
|
5545
5601
|
const reason = state === "cancelled" ? "not_planned" : "completed";
|
|
5546
5602
|
await run(["api", path, "-X", "PATCH", "-f", "state=closed", "-f", `state_reason=${reason}`], `gh api PATCH ${path}`);
|
|
5547
5603
|
} else {
|
|
5548
|
-
await run(["issue", "reopen", number, ...
|
|
5604
|
+
await run(["issue", "reopen", number, ...optionalFlag2("--repo", ref.repo)], `gh issue reopen ${number}`);
|
|
5549
5605
|
}
|
|
5550
5606
|
const readBack = readBackState(state);
|
|
5551
5607
|
if (readBack === state)
|
|
@@ -5553,7 +5609,8 @@ function createGithubTracker(options) {
|
|
|
5553
5609
|
return {
|
|
5554
5610
|
warning: `issue #${number} is now ${REST_STATES[state]}, but GitHub Issues without a board` + ` holds no ${state} state, so get reads it back as ${readBack}`
|
|
5555
5611
|
};
|
|
5556
|
-
}
|
|
5612
|
+
},
|
|
5613
|
+
...createGithubEditMembers({ run, issueNumberOf: issueNumberOfRef, parseJson, labelNames })
|
|
5557
5614
|
};
|
|
5558
5615
|
return Object.freeze(tracker);
|
|
5559
5616
|
}
|
|
@@ -5561,7 +5618,7 @@ function createGithubTracker(options) {
|
|
|
5561
5618
|
// src/adapters/tracker/local.ts
|
|
5562
5619
|
import { mkdir as mkdir3, readdir, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
5563
5620
|
import { join as join16 } from "path";
|
|
5564
|
-
var
|
|
5621
|
+
var PREFIX4 = "local tracker";
|
|
5565
5622
|
var FRONTMATTER = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/;
|
|
5566
5623
|
var ISSUE_NUMBER2 = /^[1-9]\d*$/;
|
|
5567
5624
|
var CLOSED_STATES = new Set(["done", "released", "cancelled"]);
|
|
@@ -5659,7 +5716,7 @@ function renderLocalIssue(record) {
|
|
|
5659
5716
|
};
|
|
5660
5717
|
const problem = frontmatterProblem(frontmatter) ?? (typeof draft.body === "string" ? null : `body is ${describeValue(draft.body)}, expected a string`);
|
|
5661
5718
|
if (problem !== null) {
|
|
5662
|
-
throw new TypeError(`${
|
|
5719
|
+
throw new TypeError(`${PREFIX4}: refused to write an invalid issue: ${problem}`);
|
|
5663
5720
|
}
|
|
5664
5721
|
return `---
|
|
5665
5722
|
${Bun.YAML.stringify(frontmatter, null, 2)}
|
|
@@ -5670,18 +5727,18 @@ function parseLocalIssue(contents, sourcePath) {
|
|
|
5670
5727
|
const at = sourcePath === undefined ? "" : ` at ${sourcePath}`;
|
|
5671
5728
|
const match = FRONTMATTER.exec(contents);
|
|
5672
5729
|
if (match === null) {
|
|
5673
|
-
throw new Error(`${
|
|
5730
|
+
throw new Error(`${PREFIX4}: the issue${at} has no YAML frontmatter block`);
|
|
5674
5731
|
}
|
|
5675
5732
|
const [, block = "", body = ""] = match;
|
|
5676
5733
|
let meta;
|
|
5677
5734
|
try {
|
|
5678
5735
|
meta = Bun.YAML.parse(block);
|
|
5679
5736
|
} catch (error) {
|
|
5680
|
-
throw new Error(`${
|
|
5737
|
+
throw new Error(`${PREFIX4}: invalid issue${at}: ${messageOf(error)}`, { cause: error });
|
|
5681
5738
|
}
|
|
5682
5739
|
const problem = frontmatterProblem(meta);
|
|
5683
5740
|
if (problem !== null) {
|
|
5684
|
-
throw new Error(`${
|
|
5741
|
+
throw new Error(`${PREFIX4}: invalid issue${at}: ${problem}`);
|
|
5685
5742
|
}
|
|
5686
5743
|
const fields = meta;
|
|
5687
5744
|
return {
|
|
@@ -5713,7 +5770,7 @@ async function listLocalIssues(issuesDir, warn = warnThroughActiveOutput2, reade
|
|
|
5713
5770
|
try {
|
|
5714
5771
|
contents = await readFile3(path, "utf8");
|
|
5715
5772
|
} catch (error) {
|
|
5716
|
-
throw new Error(`${
|
|
5773
|
+
throw new Error(`${PREFIX4}: could not read ${path}: ${messageOf(error)}`, { cause: error });
|
|
5717
5774
|
}
|
|
5718
5775
|
return { number, path, record: parseLocalIssue(contents, path) };
|
|
5719
5776
|
}));
|
|
@@ -5742,11 +5799,11 @@ function refFor(number, opt) {
|
|
|
5742
5799
|
}
|
|
5743
5800
|
function issueNumberOfRef2(ref) {
|
|
5744
5801
|
if (ref.kind !== "local") {
|
|
5745
|
-
throw new Error(`${
|
|
5802
|
+
throw new Error(`${PREFIX4}: refused a ref of kind ${describeValue(ref.kind)}; this tracker reads local refs only`);
|
|
5746
5803
|
}
|
|
5747
5804
|
const number = typeof ref.externalId === "string" ? parseIssueNumber(ref.externalId) : null;
|
|
5748
5805
|
if (number === null) {
|
|
5749
|
-
throw new Error(`${
|
|
5806
|
+
throw new Error(`${PREFIX4}: externalId ${describeValue(ref.externalId)} is not a local issue number,` + " expected a positive whole number with no leading zero");
|
|
5750
5807
|
}
|
|
5751
5808
|
return number;
|
|
5752
5809
|
}
|
|
@@ -5761,7 +5818,7 @@ function createLocalTracker(options) {
|
|
|
5761
5818
|
try {
|
|
5762
5819
|
contents = await readFile3(path, "utf8");
|
|
5763
5820
|
} catch (cause) {
|
|
5764
|
-
throw new Error(`${
|
|
5821
|
+
throw new Error(`${PREFIX4}: no issue ${number} under ${issuesDir}`, { cause });
|
|
5765
5822
|
}
|
|
5766
5823
|
return { number, record: parseLocalIssue(contents, path) };
|
|
5767
5824
|
}
|
|
@@ -5796,7 +5853,7 @@ function createLocalTracker(options) {
|
|
|
5796
5853
|
throw error;
|
|
5797
5854
|
}
|
|
5798
5855
|
}
|
|
5799
|
-
throw new Error(`${
|
|
5856
|
+
throw new Error(`${PREFIX4}: no issue number is left under ${issuesDir}`);
|
|
5800
5857
|
},
|
|
5801
5858
|
get: async (ref) => {
|
|
5802
5859
|
const { record } = await read(ref);
|
|
@@ -5822,6 +5879,29 @@ function createLocalTracker(options) {
|
|
|
5822
5879
|
const { number, record } = await read(ref);
|
|
5823
5880
|
await rewrite(number, { ...record, state });
|
|
5824
5881
|
return {};
|
|
5882
|
+
},
|
|
5883
|
+
editable: async (ref) => {
|
|
5884
|
+
const { number, record } = await read(ref);
|
|
5885
|
+
return {
|
|
5886
|
+
ref: refFor(number, record.draft.opt),
|
|
5887
|
+
title: record.draft.title,
|
|
5888
|
+
body: record.draft.body,
|
|
5889
|
+
open: !CLOSED_STATES.has(record.state),
|
|
5890
|
+
labels: [],
|
|
5891
|
+
author: ""
|
|
5892
|
+
};
|
|
5893
|
+
},
|
|
5894
|
+
edit: async (ref, change) => {
|
|
5895
|
+
if (change.body === undefined && change.title === undefined) {
|
|
5896
|
+
throw new TypeError(`${PREFIX4}: refused an edit naming neither a body nor a title`);
|
|
5897
|
+
}
|
|
5898
|
+
const { number, record } = await read(ref);
|
|
5899
|
+
const draft = {
|
|
5900
|
+
...record.draft,
|
|
5901
|
+
...change.body === undefined ? {} : { body: change.body },
|
|
5902
|
+
...change.title === undefined ? {} : { title: change.title }
|
|
5903
|
+
};
|
|
5904
|
+
await rewrite(number, { ...record, draft });
|
|
5825
5905
|
}
|
|
5826
5906
|
};
|
|
5827
5907
|
return Object.freeze(tracker);
|
|
@@ -6110,6 +6190,60 @@ function requireTrustedAuthor(item, reading) {
|
|
|
6110
6190
|
return;
|
|
6111
6191
|
throw new CommandExit(TRUST_REFUSAL_EXIT, trustRefusalMessage(item, reading));
|
|
6112
6192
|
}
|
|
6193
|
+
var USER_COMMAND = "gh api user";
|
|
6194
|
+
function noLogin(detail) {
|
|
6195
|
+
return { login: null, detail };
|
|
6196
|
+
}
|
|
6197
|
+
function createGhEditorLogin(options) {
|
|
6198
|
+
const { gh } = options;
|
|
6199
|
+
return async () => {
|
|
6200
|
+
const result = await gh(["api", "user"]);
|
|
6201
|
+
if (!result.ok)
|
|
6202
|
+
return noLogin(detailOf2(result, USER_COMMAND));
|
|
6203
|
+
let payload;
|
|
6204
|
+
try {
|
|
6205
|
+
payload = JSON.parse(result.stdout);
|
|
6206
|
+
} catch (error) {
|
|
6207
|
+
return noLogin(`${USER_COMMAND} wrote output that is not JSON: ${messageOf(error)}`);
|
|
6208
|
+
}
|
|
6209
|
+
if (!isMapping(payload)) {
|
|
6210
|
+
return noLogin(`${USER_COMMAND} answered ${describeValue(payload)}, expected a mapping`);
|
|
6211
|
+
}
|
|
6212
|
+
const login = payload["login"];
|
|
6213
|
+
return typeof login === "string" && isGitHubLogin(login) ? { login, detail: "" } : noLogin(`${USER_COMMAND} answered ${describeValue(login)} as its login, which is not a GitHub login`);
|
|
6214
|
+
};
|
|
6215
|
+
}
|
|
6216
|
+
async function readEditorTrust(trust, editor) {
|
|
6217
|
+
const read = await editor();
|
|
6218
|
+
if (read.login === null) {
|
|
6219
|
+
return { login: null, trusted: false, refusal: "ownership-unknown", reading: null, detail: read.detail };
|
|
6220
|
+
}
|
|
6221
|
+
const reading = await readBoardTrust(trust, read.login);
|
|
6222
|
+
return {
|
|
6223
|
+
login: read.login,
|
|
6224
|
+
trusted: reading.trusted,
|
|
6225
|
+
refusal: editorRefusalOf(reading),
|
|
6226
|
+
reading,
|
|
6227
|
+
detail: ""
|
|
6228
|
+
};
|
|
6229
|
+
}
|
|
6230
|
+
function editorRefusalOf(reading) {
|
|
6231
|
+
if (reading.trusted)
|
|
6232
|
+
return null;
|
|
6233
|
+
return reading.refusal === "lookup-failed" ? "ownership-unknown" : "ownership";
|
|
6234
|
+
}
|
|
6235
|
+
function editorRefusalMessage(item, reading) {
|
|
6236
|
+
const what = `${item.kind} #${String(item.number)}`;
|
|
6237
|
+
if (reading.reading !== null) {
|
|
6238
|
+
const because = trustRefusalClause(item.repo, reading.reading);
|
|
6239
|
+
return `${what} cannot be edited by ${reading.reading.login}, ${because}; a member must make the edit`;
|
|
6240
|
+
}
|
|
6241
|
+
if (reading.trusted) {
|
|
6242
|
+
throw new TypeError("board trust: a trusted editor has no refusal to name");
|
|
6243
|
+
}
|
|
6244
|
+
const detail = reading.detail === "" ? UNREPORTED : reading.detail;
|
|
6245
|
+
return `${what} cannot be edited: the account running the edit could not be read (${detail}); ` + "log gh in with `gh auth login` and run it again";
|
|
6246
|
+
}
|
|
6113
6247
|
// src/effort/sync/wire.ts
|
|
6114
6248
|
import { existsSync as existsSync14, mkdtempSync as mkdtempSync2, rmSync as rmSync8 } from "fs";
|
|
6115
6249
|
import { tmpdir as tmpdir4 } from "os";
|
|
@@ -6363,4 +6497,4 @@ function selectEffortStore(repoRoot, config) {
|
|
|
6363
6497
|
return adapter.create({ repoRoot });
|
|
6364
6498
|
}
|
|
6365
6499
|
|
|
6366
|
-
export { EFFORT_STORE_DIR, effortStoreFileName, EFFORT_DIR_VARIABLE, effortStoreDir, guardTestProcess, EFFORT_KEY_PROJECTIONS, openNdjsonStore, name, version, RAFA_VERSION, versionLine, createGitRunner, gitSaid, NOT_A_REPOSITORY, mainCheckoutOf, SCOPE_DIR, INIT_COMMAND, DISK_FILE_SYSTEM, ScopeError, scopeAt, initHint, selfAndAncestors, resolveScope, POSITION_FILE, positionFilePath, readPositionFile, writePositionFile, positionAt, rehome, hop, goHome, HOP_FILE, hopFilePath, readHopRecord, writeHopRecord, staleAgainst, SessionRecordError, isSessionId, sessionPhase, SessionConflictError, SessionStateError, runsDir, sessionFilePath, errorCode, isPidAlive, readSessions, readSession, beginSession, updateSession, DEVELOPMENT_NEXT_STEP, DEVELOPMENT_MARK, DevelopmentBuildRefusedError, refuseUnownedDevelopmentWrite, LEGACY_GATE_OPEN, LEGACY_GATE_CLOSED, SQLITE_MIGRATIONS, sqliteCatalogue, planSchema, SchemaRefusedError, readStoreSchema, writeNames, ACROSS_STORES_ORDER, STAMPED_COLUMNS, stampedValues, activeStoreSettings, normalizeRemote, gitRemoteUrl, readStoreMeta, SQLITE_STORE_FILE_NAME, sqliteStorePath, LONE_SURROGATE, withSqliteStore, writeSqliteStore, openSqliteStore, EffortCopyRefusal, EffortCopyFailure, copyEffortStore, RebuildRefusal, readConfigFile, loadConfig, plansDirAt, requireProject, resolvePlansDir, resolveProjectConfig, countTasks, formatCounts, checkbox, planFileName, stubOfPlanFile, isFile, issueLine, plural, expectNoArgument, expectOneArgument, expectAtMostOneArgument, readSwitch, fileStamp, keepsIdLine, fix_schema_default, minutesBetween, readCommitLog, UnionSchemaMismatch, migrateStore, MOVE_TO_SQLITE, MergeRefusal, mergeStore, createFileSync, SyncModuleMissing, selectSync, SKILL_FILE, MARKDOWN_EXTENSION, scanLayout, declaredNameIssue, readFrontmatterDocument, readFrontmatter, renderFrontmatter, mergeFrontmatter, writeFrontmatter, ACTION_HEADING, CAUSE_HEADING, parseInstinct, writeInstinct, readScopes, allRecords, findRecords, instinctProject, toHeldRecords, EVENT_PREFIX, padKind, oneLine, sessionSpawnEnv, CLAUDE_BIN, claudeArgs, interruptClaudeSessions, spawnClaude, runClaude, spawnClaudeCaptured, runClaudeCaptured, planFilePath, ClaudePlannerError, ISSUE_TYPES, ISSUE_PRIORITIES, ISSUE_STATES, GITHUB_LABELS, createGhRunner, typeOfLabels, moduleOfLabels, createLocalTracker, PORT_VERSIONS, createAdapterRegistry, CORE_ADAPTER_REGISTRY, TRUSTED_PERMISSIONS, createGhPermissions, readAuthorTrust, ghBoardTrust, readBoardTrust, requireTrustedBoardAuthor, trustRefusalClause, requireTrustedAuthor, WIRE_FORMAT, WIRE_VERSION, WireFormatError, WireExportRefusal, encodeWirePayload, decodeWirePayload, exportWirePayload, materialiseWirePayload, selectEffortStore };
|
|
6500
|
+
export { EFFORT_STORE_DIR, effortStoreFileName, EFFORT_DIR_VARIABLE, effortStoreDir, guardTestProcess, EFFORT_KEY_PROJECTIONS, openNdjsonStore, name, version, RAFA_VERSION, versionLine, createGitRunner, gitSaid, NOT_A_REPOSITORY, mainCheckoutOf, SCOPE_DIR, INIT_COMMAND, DISK_FILE_SYSTEM, ScopeError, scopeAt, initHint, selfAndAncestors, resolveScope, POSITION_FILE, positionFilePath, readPositionFile, writePositionFile, positionAt, rehome, hop, goHome, HOP_FILE, hopFilePath, readHopRecord, writeHopRecord, staleAgainst, SessionRecordError, isSessionId, sessionPhase, SessionConflictError, SessionStateError, runsDir, sessionFilePath, errorCode, isPidAlive, readSessions, readSession, beginSession, updateSession, DEVELOPMENT_NEXT_STEP, DEVELOPMENT_MARK, DevelopmentBuildRefusedError, refuseUnownedDevelopmentWrite, LEGACY_GATE_OPEN, LEGACY_GATE_CLOSED, SQLITE_MIGRATIONS, sqliteCatalogue, planSchema, SchemaRefusedError, readStoreSchema, writeNames, ACROSS_STORES_ORDER, STAMPED_COLUMNS, stampedValues, activeStoreSettings, normalizeRemote, gitRemoteUrl, readStoreMeta, SQLITE_STORE_FILE_NAME, sqliteStorePath, LONE_SURROGATE, withSqliteStore, writeSqliteStore, openSqliteStore, EffortCopyRefusal, EffortCopyFailure, copyEffortStore, RebuildRefusal, readConfigFile, loadConfig, plansDirAt, requireProject, resolvePlansDir, resolveProjectConfig, countTasks, formatCounts, checkbox, planFileName, stubOfPlanFile, isFile, issueLine, plural, expectNoArgument, expectOneArgument, expectAtMostOneArgument, readSwitch, fileStamp, keepsIdLine, fix_schema_default, minutesBetween, readCommitLog, UnionSchemaMismatch, migrateStore, MOVE_TO_SQLITE, MergeRefusal, mergeStore, createFileSync, SyncModuleMissing, selectSync, SKILL_FILE, MARKDOWN_EXTENSION, scanLayout, declaredNameIssue, readFrontmatterDocument, readFrontmatter, renderFrontmatter, mergeFrontmatter, writeFrontmatter, ACTION_HEADING, CAUSE_HEADING, parseInstinct, writeInstinct, readScopes, allRecords, findRecords, instinctProject, toHeldRecords, EVENT_PREFIX, padKind, oneLine, sessionSpawnEnv, CLAUDE_BIN, claudeArgs, interruptClaudeSessions, spawnClaude, runClaude, spawnClaudeCaptured, runClaudeCaptured, planFilePath, ClaudePlannerError, ISSUE_TYPES, ISSUE_PRIORITIES, ISSUE_STATES, GITHUB_LABELS, createGhRunner, typeOfLabels, moduleOfLabels, createLocalTracker, PORT_VERSIONS, createAdapterRegistry, CORE_ADAPTER_REGISTRY, TRUSTED_PERMISSIONS, TRUST_REFUSAL_EXIT, createGhPermissions, readAuthorTrust, ghBoardTrust, readBoardTrust, requireTrustedBoardAuthor, trustRefusalClause, trustRefusalMessage, requireTrustedAuthor, createGhEditorLogin, readEditorTrust, editorRefusalMessage, WIRE_FORMAT, WIRE_VERSION, WireFormatError, WireExportRefusal, encodeWirePayload, decodeWirePayload, exportWirePayload, materialiseWirePayload, selectEffortStore };
|