@open-tomato/rafa 0.10.0 → 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/README.md +66 -30
- package/dist/cli.js +20364 -16545
- package/dist/effort/store/index.js +1 -1
- package/dist/{index-srdtw7w1.js → index-4g6vyznd.js} +5 -4
- package/dist/index.js +903 -155
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -50,7 +50,7 @@ import {
|
|
|
50
50
|
sqliteStorePath,
|
|
51
51
|
withSqliteStore,
|
|
52
52
|
writeSqliteStore
|
|
53
|
-
} from "./index-
|
|
53
|
+
} from "./index-4g6vyznd.js";
|
|
54
54
|
import {
|
|
55
55
|
BUDGET_FLAG,
|
|
56
56
|
CLAUDE_SETTING_SOURCES,
|
|
@@ -337,6 +337,13 @@ var PROMPT_SHAPES = [
|
|
|
337
337
|
prefix: "The pull request for branch ",
|
|
338
338
|
firstLineInfix: " is not mergeable: ",
|
|
339
339
|
source: "src/start/pr-lifecycle.ts"
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
kind: "search",
|
|
343
|
+
label: "search",
|
|
344
|
+
prefix: "# Inventory search instructions",
|
|
345
|
+
firstLineInfix: null,
|
|
346
|
+
source: "src/inventory/search/prompt.ts"
|
|
340
347
|
}
|
|
341
348
|
];
|
|
342
349
|
var ENQUEUE_SCAN_WINDOW = 10;
|
|
@@ -1895,7 +1902,7 @@ async function report(args, repoRoot) {
|
|
|
1895
1902
|
}
|
|
1896
1903
|
}
|
|
1897
1904
|
// package.json
|
|
1898
|
-
var version = "0.
|
|
1905
|
+
var version = "0.12.0";
|
|
1899
1906
|
|
|
1900
1907
|
// src/modules/manifest.ts
|
|
1901
1908
|
var MANIFEST_VERSION = 1;
|
|
@@ -2185,9 +2192,11 @@ function requireProject(context, command) {
|
|
|
2185
2192
|
return context.project;
|
|
2186
2193
|
}
|
|
2187
2194
|
function resolvePlansDir(project, command, warn) {
|
|
2195
|
+
return plansDirAt(project.root, resolveProjectConfig(project, command, warn).planDir);
|
|
2196
|
+
}
|
|
2197
|
+
function resolveProjectConfig(project, command, warn) {
|
|
2188
2198
|
try {
|
|
2189
|
-
|
|
2190
|
-
return plansDirAt(project.root, config.planDir);
|
|
2199
|
+
return loadConfig({ root: project.root, home: project.home }, {}, warn).config;
|
|
2191
2200
|
} catch (error) {
|
|
2192
2201
|
if (!(error instanceof ConfigError))
|
|
2193
2202
|
throw error;
|
|
@@ -2376,8 +2385,7 @@ function definitionName(text) {
|
|
|
2376
2385
|
const name = frontmatter["name"];
|
|
2377
2386
|
return typeof name === "string" && name.length > 0 ? name : null;
|
|
2378
2387
|
}
|
|
2379
|
-
function
|
|
2380
|
-
const dir = join3(root, AGENT_DEFINITION_DIR);
|
|
2388
|
+
function readAgentDirectory(dir) {
|
|
2381
2389
|
const found = [];
|
|
2382
2390
|
for (const file of markdownFiles(dir)) {
|
|
2383
2391
|
const path = join3(dir, file);
|
|
@@ -2391,6 +2399,9 @@ function readAgentDefinitions(root) {
|
|
|
2391
2399
|
}
|
|
2392
2400
|
return found.sort((a, b) => a.name.localeCompare(b.name));
|
|
2393
2401
|
}
|
|
2402
|
+
function readAgentDefinitions(root) {
|
|
2403
|
+
return readAgentDirectory(join3(root, AGENT_DEFINITION_DIR));
|
|
2404
|
+
}
|
|
2394
2405
|
function firstByName(files) {
|
|
2395
2406
|
const byName = new Map;
|
|
2396
2407
|
for (const file of files) {
|
|
@@ -4724,112 +4735,8 @@ async function resolvePlanSpec(options) {
|
|
|
4724
4735
|
});
|
|
4725
4736
|
}
|
|
4726
4737
|
|
|
4727
|
-
// src/
|
|
4738
|
+
// src/cli/prompt/confirm.ts
|
|
4728
4739
|
import { createInterface as createInterface2 } from "readline";
|
|
4729
|
-
|
|
4730
|
-
// src/project/roots.ts
|
|
4731
|
-
import { accessSync, constants, readFileSync as readFileSync8, statSync as statSync7 } from "fs";
|
|
4732
|
-
|
|
4733
|
-
// src/project/scope.ts
|
|
4734
|
-
import { existsSync as existsSync9, realpathSync as realpathSync2 } from "fs";
|
|
4735
|
-
import { dirname as dirname3, isAbsolute as isAbsolute2, join as join6 } from "path";
|
|
4736
|
-
var SCOPE_DIR = dirname3(CONFIG_FILE);
|
|
4737
|
-
var INIT_COMMAND = "rafa init";
|
|
4738
|
-
var DISK_FILE_SYSTEM = Object.freeze({
|
|
4739
|
-
exists: (path3) => existsSync9(path3),
|
|
4740
|
-
realpath: (path3) => realpathSync2(path3)
|
|
4741
|
-
});
|
|
4742
|
-
|
|
4743
|
-
class ScopeError extends Error {
|
|
4744
|
-
constructor(message, options) {
|
|
4745
|
-
super(`rafa scope: ${message}`, options);
|
|
4746
|
-
this.name = "ScopeError";
|
|
4747
|
-
}
|
|
4748
|
-
}
|
|
4749
|
-
function scopeAt(base) {
|
|
4750
|
-
return { dir: join6(base, SCOPE_DIR), configFile: configFilePath(base) };
|
|
4751
|
-
}
|
|
4752
|
-
function initHint(start, passedUserConfig = null) {
|
|
4753
|
-
const passed2 = passedUserConfig === null ? [] : [`${passedUserConfig} is the user scope's config and marks no project`];
|
|
4754
|
-
return [
|
|
4755
|
-
`not inside a rafa project: no ${CONFIG_FILE} in ${start} or any directory above it`,
|
|
4756
|
-
...passed2,
|
|
4757
|
-
`run \`${INIT_COMMAND}\` to set one up`
|
|
4758
|
-
].join(`
|
|
4759
|
-
`);
|
|
4760
|
-
}
|
|
4761
|
-
function requireAbsolute(label, path3) {
|
|
4762
|
-
if (!isAbsolute2(path3)) {
|
|
4763
|
-
throw new ScopeError(`${label} is ${describeValue(path3)}, expected an absolute path`);
|
|
4764
|
-
}
|
|
4765
|
-
}
|
|
4766
|
-
function realStartOf(start, fs2) {
|
|
4767
|
-
try {
|
|
4768
|
-
return fs2.realpath(start);
|
|
4769
|
-
} catch (error) {
|
|
4770
|
-
const message = `start directory ${start} does not resolve (${messageOf(error)})`;
|
|
4771
|
-
throw new ScopeError(message, { cause: error });
|
|
4772
|
-
}
|
|
4773
|
-
}
|
|
4774
|
-
function realHomeOf(home, fs2) {
|
|
4775
|
-
try {
|
|
4776
|
-
return fs2.realpath(home);
|
|
4777
|
-
} catch {
|
|
4778
|
-
return null;
|
|
4779
|
-
}
|
|
4780
|
-
}
|
|
4781
|
-
function selfAndAncestors(path3) {
|
|
4782
|
-
const parent = dirname3(path3);
|
|
4783
|
-
return parent === path3 ? [path3] : [path3, ...selfAndAncestors(parent)];
|
|
4784
|
-
}
|
|
4785
|
-
function walkUp(realStart, realHome, fs2) {
|
|
4786
|
-
let passedUserConfig = false;
|
|
4787
|
-
for (const dir of selfAndAncestors(realStart)) {
|
|
4788
|
-
if (!fs2.exists(configFilePath(dir)))
|
|
4789
|
-
continue;
|
|
4790
|
-
if (dir !== realHome)
|
|
4791
|
-
return { root: dir, passedUserConfig };
|
|
4792
|
-
passedUserConfig = true;
|
|
4793
|
-
}
|
|
4794
|
-
return { root: null, passedUserConfig };
|
|
4795
|
-
}
|
|
4796
|
-
function resolveScope(start, seams) {
|
|
4797
|
-
const { home, fs: fs2 = DISK_FILE_SYSTEM } = seams;
|
|
4798
|
-
requireAbsolute("start directory", start);
|
|
4799
|
-
requireAbsolute("home directory", home);
|
|
4800
|
-
const user = scopeAt(home);
|
|
4801
|
-
const walk = walkUp(realStartOf(start, fs2), realHomeOf(home, fs2), fs2);
|
|
4802
|
-
if (walk.root === null) {
|
|
4803
|
-
const passed2 = walk.passedUserConfig ? user.configFile : null;
|
|
4804
|
-
return { found: false, start, home, user, hint: initHint(start, passed2) };
|
|
4805
|
-
}
|
|
4806
|
-
return { found: true, root: walk.root, home, project: scopeAt(walk.root), user };
|
|
4807
|
-
}
|
|
4808
|
-
|
|
4809
|
-
// src/project/roots.ts
|
|
4810
|
-
var REFUSED_ROOTS = Object.freeze([
|
|
4811
|
-
{ path: "/", why: "is the filesystem root" },
|
|
4812
|
-
{ path: "/var", why: "holds system state" },
|
|
4813
|
-
{ path: "/etc", why: "holds system configuration" },
|
|
4814
|
-
{ path: "/usr", why: "holds system programs" },
|
|
4815
|
-
{ path: "/home", why: "holds the home directories of every user" },
|
|
4816
|
-
{ path: "/Users", why: "holds the home directories of every user" }
|
|
4817
|
-
]);
|
|
4818
|
-
var HOME_WHY = `is the home directory, whose ${SCOPE_DIR}/ is the user scope`;
|
|
4819
|
-
var MONOREPO_MARKER_FILES = Object.freeze([
|
|
4820
|
-
"turbo.json",
|
|
4821
|
-
"pnpm-workspace.yaml",
|
|
4822
|
-
"nx.json",
|
|
4823
|
-
"lerna.json"
|
|
4824
|
-
]);
|
|
4825
|
-
var DISK_ROOTS_FILE_SYSTEM = Object.freeze({
|
|
4826
|
-
...DISK_FILE_SYSTEM,
|
|
4827
|
-
readFile: (path3) => readFileSync8(path3, "utf8"),
|
|
4828
|
-
isDirectory: (path3) => statSync7(path3).isDirectory(),
|
|
4829
|
-
assertWritable: (path3) => accessSync(path3, constants.W_OK)
|
|
4830
|
-
});
|
|
4831
|
-
|
|
4832
|
-
// src/project/root-choice.ts
|
|
4833
4740
|
function createLinePrompter(input, output) {
|
|
4834
4741
|
const buffered = [];
|
|
4835
4742
|
const waiting = [];
|
|
@@ -5637,12 +5544,90 @@ import {
|
|
|
5637
5544
|
linkSync as linkSync2,
|
|
5638
5545
|
mkdirSync as mkdirSync4,
|
|
5639
5546
|
readdirSync as readdirSync4,
|
|
5640
|
-
readFileSync as
|
|
5547
|
+
readFileSync as readFileSync8,
|
|
5641
5548
|
renameSync as renameSync2,
|
|
5642
5549
|
rmSync,
|
|
5643
5550
|
writeFileSync as writeFileSync3
|
|
5644
5551
|
} from "fs";
|
|
5645
5552
|
import { basename as basename4, dirname as dirname4, join as join7 } from "path";
|
|
5553
|
+
|
|
5554
|
+
// src/project/scope.ts
|
|
5555
|
+
import { existsSync as existsSync9, realpathSync as realpathSync2 } from "fs";
|
|
5556
|
+
import { dirname as dirname3, isAbsolute as isAbsolute2, join as join6 } from "path";
|
|
5557
|
+
var SCOPE_DIR = dirname3(CONFIG_FILE);
|
|
5558
|
+
var INIT_COMMAND = "rafa init";
|
|
5559
|
+
var DISK_FILE_SYSTEM = Object.freeze({
|
|
5560
|
+
exists: (path3) => existsSync9(path3),
|
|
5561
|
+
realpath: (path3) => realpathSync2(path3)
|
|
5562
|
+
});
|
|
5563
|
+
|
|
5564
|
+
class ScopeError extends Error {
|
|
5565
|
+
constructor(message, options) {
|
|
5566
|
+
super(`rafa scope: ${message}`, options);
|
|
5567
|
+
this.name = "ScopeError";
|
|
5568
|
+
}
|
|
5569
|
+
}
|
|
5570
|
+
function scopeAt(base) {
|
|
5571
|
+
return { dir: join6(base, SCOPE_DIR), configFile: configFilePath(base) };
|
|
5572
|
+
}
|
|
5573
|
+
function initHint(start, passedUserConfig = null) {
|
|
5574
|
+
const passed2 = passedUserConfig === null ? [] : [`${passedUserConfig} is the user scope's config and marks no project`];
|
|
5575
|
+
return [
|
|
5576
|
+
`not inside a rafa project: no ${CONFIG_FILE} in ${start} or any directory above it`,
|
|
5577
|
+
...passed2,
|
|
5578
|
+
`run \`${INIT_COMMAND}\` to set one up`
|
|
5579
|
+
].join(`
|
|
5580
|
+
`);
|
|
5581
|
+
}
|
|
5582
|
+
function requireAbsolute(label, path3) {
|
|
5583
|
+
if (!isAbsolute2(path3)) {
|
|
5584
|
+
throw new ScopeError(`${label} is ${describeValue(path3)}, expected an absolute path`);
|
|
5585
|
+
}
|
|
5586
|
+
}
|
|
5587
|
+
function realStartOf(start, fs2) {
|
|
5588
|
+
try {
|
|
5589
|
+
return fs2.realpath(start);
|
|
5590
|
+
} catch (error) {
|
|
5591
|
+
const message = `start directory ${start} does not resolve (${messageOf(error)})`;
|
|
5592
|
+
throw new ScopeError(message, { cause: error });
|
|
5593
|
+
}
|
|
5594
|
+
}
|
|
5595
|
+
function realHomeOf(home, fs2) {
|
|
5596
|
+
try {
|
|
5597
|
+
return fs2.realpath(home);
|
|
5598
|
+
} catch {
|
|
5599
|
+
return null;
|
|
5600
|
+
}
|
|
5601
|
+
}
|
|
5602
|
+
function selfAndAncestors(path3) {
|
|
5603
|
+
const parent = dirname3(path3);
|
|
5604
|
+
return parent === path3 ? [path3] : [path3, ...selfAndAncestors(parent)];
|
|
5605
|
+
}
|
|
5606
|
+
function walkUp(realStart, realHome, fs2) {
|
|
5607
|
+
let passedUserConfig = false;
|
|
5608
|
+
for (const dir of selfAndAncestors(realStart)) {
|
|
5609
|
+
if (!fs2.exists(configFilePath(dir)))
|
|
5610
|
+
continue;
|
|
5611
|
+
if (dir !== realHome)
|
|
5612
|
+
return { root: dir, passedUserConfig };
|
|
5613
|
+
passedUserConfig = true;
|
|
5614
|
+
}
|
|
5615
|
+
return { root: null, passedUserConfig };
|
|
5616
|
+
}
|
|
5617
|
+
function resolveScope(start, seams) {
|
|
5618
|
+
const { home, fs: fs2 = DISK_FILE_SYSTEM } = seams;
|
|
5619
|
+
requireAbsolute("start directory", start);
|
|
5620
|
+
requireAbsolute("home directory", home);
|
|
5621
|
+
const user = scopeAt(home);
|
|
5622
|
+
const walk = walkUp(realStartOf(start, fs2), realHomeOf(home, fs2), fs2);
|
|
5623
|
+
if (walk.root === null) {
|
|
5624
|
+
const passed2 = walk.passedUserConfig ? user.configFile : null;
|
|
5625
|
+
return { found: false, start, home, user, hint: initHint(start, passed2) };
|
|
5626
|
+
}
|
|
5627
|
+
return { found: true, root: walk.root, home, project: scopeAt(walk.root), user };
|
|
5628
|
+
}
|
|
5629
|
+
|
|
5630
|
+
// src/loop/sessions.ts
|
|
5646
5631
|
var SESSION_STATES = Object.freeze(["running", "paused", "stopped", "done"]);
|
|
5647
5632
|
var RUNS_DIR = "runs";
|
|
5648
5633
|
var RECORD_EXTENSION = ".json";
|
|
@@ -5790,7 +5775,7 @@ function readState2(record, isAlive = isPidAlive) {
|
|
|
5790
5775
|
function readRecordFile(file) {
|
|
5791
5776
|
let text;
|
|
5792
5777
|
try {
|
|
5793
|
-
text =
|
|
5778
|
+
text = readFileSync8(file, "utf8");
|
|
5794
5779
|
} catch (error) {
|
|
5795
5780
|
throw new SessionRecordError(file, `cannot be read: ${messageOf(error)}`);
|
|
5796
5781
|
}
|
|
@@ -6003,15 +5988,15 @@ function openNextSources(context, seams = DEFAULT_NEXT_SOURCE_SEAMS) {
|
|
|
6003
5988
|
}
|
|
6004
5989
|
|
|
6005
5990
|
// src/commands/plan/list.ts
|
|
6006
|
-
import { existsSync as existsSync10, readdirSync as readdirSync5, readFileSync as
|
|
5991
|
+
import { existsSync as existsSync10, readdirSync as readdirSync5, readFileSync as readFileSync9 } from "fs";
|
|
6007
5992
|
import { join as join9 } from "path";
|
|
6008
5993
|
var USAGE2 = "rafa plan list";
|
|
6009
5994
|
function listing(dir, stub) {
|
|
6010
5995
|
const plan = join9(dir, planFileName(stub, false));
|
|
6011
5996
|
const trackerPath = join9(dir, planFileName(stub, true));
|
|
6012
5997
|
const tracker = isFile(trackerPath) ? trackerPath : null;
|
|
6013
|
-
const planModel = parsePlan(
|
|
6014
|
-
const counted2 = tracker === null ? planModel : parsePlan(
|
|
5998
|
+
const planModel = parsePlan(readFileSync9(plan, "utf8"));
|
|
5999
|
+
const counted2 = tracker === null ? planModel : parsePlan(readFileSync9(tracker, "utf8"));
|
|
6015
6000
|
return { stub, plan, tracker, tasks: countTasks(counted2.tasks), issues: planModel.issues.length };
|
|
6016
6001
|
}
|
|
6017
6002
|
function listPlans(plans) {
|
|
@@ -7027,7 +7012,7 @@ import { homedir as homedir3 } from "os";
|
|
|
7027
7012
|
var RAFA_VERSION = version;
|
|
7028
7013
|
|
|
7029
7014
|
// src/notices/notices.ts
|
|
7030
|
-
import { mkdirSync as mkdirSync5, readFileSync as
|
|
7015
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync4 } from "fs";
|
|
7031
7016
|
import { dirname as dirname5, join as join10 } from "path";
|
|
7032
7017
|
var NOTICE_IDS = Object.freeze(["alpha", "danger"]);
|
|
7033
7018
|
var FEEDBACK_URL = "https://github.com/open-tomato/rafa/issues/new/choose";
|
|
@@ -7041,7 +7026,7 @@ function isNoticeId(value) {
|
|
|
7041
7026
|
function readDismissed(home) {
|
|
7042
7027
|
let raw;
|
|
7043
7028
|
try {
|
|
7044
|
-
raw =
|
|
7029
|
+
raw = readFileSync10(noticesPath(home), "utf8");
|
|
7045
7030
|
} catch {
|
|
7046
7031
|
return [];
|
|
7047
7032
|
}
|
|
@@ -8258,16 +8243,16 @@ function writeProgress(repoRoot, planStub2) {
|
|
|
8258
8243
|
|
|
8259
8244
|
// src/start/preflight.ts
|
|
8260
8245
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
8261
|
-
import { existsSync as existsSync14, readFileSync as
|
|
8246
|
+
import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
|
|
8262
8247
|
import { homedir as homedir5 } from "os";
|
|
8263
8248
|
import { basename as basename5 } from "path";
|
|
8264
8249
|
|
|
8265
8250
|
// src/preflight/first-dispatch.ts
|
|
8266
|
-
import { readFileSync as
|
|
8251
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
8267
8252
|
var TICKED_TASK = /^- \[[xX]\] (.+)/;
|
|
8268
8253
|
function readIfReadable(path5) {
|
|
8269
8254
|
try {
|
|
8270
|
-
return
|
|
8255
|
+
return readFileSync11(path5, "utf8");
|
|
8271
8256
|
} catch {
|
|
8272
8257
|
return null;
|
|
8273
8258
|
}
|
|
@@ -8304,7 +8289,7 @@ function agentSourcePath(planPath) {
|
|
|
8304
8289
|
}
|
|
8305
8290
|
function readIfReadable2(path5) {
|
|
8306
8291
|
try {
|
|
8307
|
-
return
|
|
8292
|
+
return readFileSync12(path5, "utf8");
|
|
8308
8293
|
} catch {
|
|
8309
8294
|
return null;
|
|
8310
8295
|
}
|
|
@@ -8798,7 +8783,7 @@ async function handleRedChecks(io, branch, prNumber, rows) {
|
|
|
8798
8783
|
}
|
|
8799
8784
|
|
|
8800
8785
|
// src/release/prepare.ts
|
|
8801
|
-
import { readFileSync as
|
|
8786
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "fs";
|
|
8802
8787
|
|
|
8803
8788
|
// src/release/changelog.ts
|
|
8804
8789
|
var RELEASE_HEADING_FIELDS = ["version", "date", "title"];
|
|
@@ -8983,11 +8968,11 @@ function changelogDate(now) {
|
|
|
8983
8968
|
}
|
|
8984
8969
|
|
|
8985
8970
|
// src/release/enabled.ts
|
|
8986
|
-
import { statSync as
|
|
8971
|
+
import { statSync as statSync7 } from "fs";
|
|
8987
8972
|
import { resolve as resolve9 } from "path";
|
|
8988
8973
|
function isRegularFile(full) {
|
|
8989
8974
|
try {
|
|
8990
|
-
return
|
|
8975
|
+
return statSync7(full).isFile();
|
|
8991
8976
|
} catch {
|
|
8992
8977
|
return false;
|
|
8993
8978
|
}
|
|
@@ -9030,7 +9015,7 @@ function resolveReleaseLevel(declared, notes) {
|
|
|
9030
9015
|
}
|
|
9031
9016
|
|
|
9032
9017
|
// src/release/version.ts
|
|
9033
|
-
import { readFileSync as
|
|
9018
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync6 } from "fs";
|
|
9034
9019
|
var RELEASE_REMOTE = "origin";
|
|
9035
9020
|
var RELEASE_BASE_BRANCH = "main";
|
|
9036
9021
|
var VERSION_SHAPE = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z.-]+))?(?:\+([0-9A-Za-z.-]+))?$/;
|
|
@@ -9122,7 +9107,7 @@ function writeManifestVersion(path5, next) {
|
|
|
9122
9107
|
const unwritten = { path: path5, written: false, previous: null, version: next };
|
|
9123
9108
|
let text;
|
|
9124
9109
|
try {
|
|
9125
|
-
text =
|
|
9110
|
+
text = readFileSync13(path5, "utf8");
|
|
9126
9111
|
} catch (error) {
|
|
9127
9112
|
return { ...unwritten, problem: `${path5} could not be read: ${messageOf(error)}` };
|
|
9128
9113
|
}
|
|
@@ -9225,7 +9210,7 @@ function planVersion(input, versionFile, level) {
|
|
|
9225
9210
|
}
|
|
9226
9211
|
function textOf(path5) {
|
|
9227
9212
|
try {
|
|
9228
|
-
return
|
|
9213
|
+
return readFileSync14(path5, "utf8");
|
|
9229
9214
|
} catch {
|
|
9230
9215
|
return null;
|
|
9231
9216
|
}
|
|
@@ -9338,7 +9323,7 @@ function changelogFailure(failed3, versionEdit, restored) {
|
|
|
9338
9323
|
}
|
|
9339
9324
|
|
|
9340
9325
|
// src/release/verify.ts
|
|
9341
|
-
import { readFileSync as
|
|
9326
|
+
import { readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "fs";
|
|
9342
9327
|
function changelogInsertionSpan(before, after) {
|
|
9343
9328
|
const beforeLines = before.split(`
|
|
9344
9329
|
`);
|
|
@@ -9355,7 +9340,7 @@ function changelogInsertionSpan(before, after) {
|
|
|
9355
9340
|
}
|
|
9356
9341
|
function textOf2(path5) {
|
|
9357
9342
|
try {
|
|
9358
|
-
return
|
|
9343
|
+
return readFileSync15(path5, "utf8");
|
|
9359
9344
|
} catch {
|
|
9360
9345
|
return null;
|
|
9361
9346
|
}
|
|
@@ -9689,12 +9674,768 @@ async function finishRelease(input, seams = {}) {
|
|
|
9689
9674
|
return { outcome: "released", version: version2, subject, sha, sentence: null, body: null };
|
|
9690
9675
|
}
|
|
9691
9676
|
|
|
9677
|
+
// src/start/risk-total.ts
|
|
9678
|
+
import { readFileSync as readFileSync17 } from "fs";
|
|
9679
|
+
|
|
9680
|
+
// src/plan/risk.ts
|
|
9681
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
9682
|
+
import { isAbsolute as isAbsolute3, join as join13, relative as relative2 } from "path";
|
|
9683
|
+
|
|
9684
|
+
// src/schema/tiers.ts
|
|
9685
|
+
import { existsSync as existsSync16, realpathSync as realpathSync3 } from "fs";
|
|
9686
|
+
import { dirname as dirname6, join as join12 } from "path";
|
|
9687
|
+
var SKILL_TIERS = ["project", "rafa", "user"];
|
|
9688
|
+
var CLAUDE_SKILLS_PATH = join12(".claude", "skills");
|
|
9689
|
+
var RAFA_INSTINCTS_PATH = join12(".rafa", "instincts");
|
|
9690
|
+
var BUNDLED_SKILLS_DIR = "skills";
|
|
9691
|
+
function realEntry(entry) {
|
|
9692
|
+
try {
|
|
9693
|
+
return realpathSync3(entry);
|
|
9694
|
+
} catch {
|
|
9695
|
+
return entry;
|
|
9696
|
+
}
|
|
9697
|
+
}
|
|
9698
|
+
function bundledSkillsDirectory(entry) {
|
|
9699
|
+
return join12(dirname6(realEntry(entry ?? Bun.main)), BUNDLED_SKILLS_DIR);
|
|
9700
|
+
}
|
|
9701
|
+
function skillTierDirectory(tier, seams) {
|
|
9702
|
+
if (tier === "user")
|
|
9703
|
+
return join12(seams.home, CLAUDE_SKILLS_PATH);
|
|
9704
|
+
if (tier === "rafa")
|
|
9705
|
+
return bundledSkillsDirectory(seams.entry);
|
|
9706
|
+
return seams.projectRoot === null ? null : join12(seams.projectRoot, CLAUDE_SKILLS_PATH);
|
|
9707
|
+
}
|
|
9708
|
+
function resolveSkillTiers(seams) {
|
|
9709
|
+
const located = [];
|
|
9710
|
+
for (const tier of SKILL_TIERS) {
|
|
9711
|
+
const dir = skillTierDirectory(tier, seams);
|
|
9712
|
+
if (dir !== null)
|
|
9713
|
+
located.push({ tier, dir });
|
|
9714
|
+
}
|
|
9715
|
+
return located;
|
|
9716
|
+
}
|
|
9717
|
+
|
|
9718
|
+
// src/plan/risk/accounts.ts
|
|
9719
|
+
var PUSH_REMOTE = "origin";
|
|
9720
|
+
var GITHUB_TRACKER = "github";
|
|
9721
|
+
var URL_USER_INFO = /^([A-Za-z][A-Za-z0-9+.-]*:\/\/)[^@/]+@/;
|
|
9722
|
+
function redactRemote(url) {
|
|
9723
|
+
return url.trim().replace(URL_USER_INFO, "$1***@");
|
|
9724
|
+
}
|
|
9725
|
+
function trackerChain(settings) {
|
|
9726
|
+
return [...new Set([settings.trackerDefault, ...settings.trackerFallback])];
|
|
9727
|
+
}
|
|
9728
|
+
async function readAccounts(settings, seams) {
|
|
9729
|
+
const pushUrl = remoteUrl(seams.git, ["remote", "get-url", "--push", PUSH_REMOTE]);
|
|
9730
|
+
const fetchUrl = remoteUrl(seams.git, ["remote", "get-url", PUSH_REMOTE]);
|
|
9731
|
+
const chain = trackerChain(settings);
|
|
9732
|
+
const reading2 = resolvePrProvider({
|
|
9733
|
+
configured: settings.prProvider,
|
|
9734
|
+
dir: "",
|
|
9735
|
+
readRemote: () => fetchUrl
|
|
9736
|
+
});
|
|
9737
|
+
const needsOwnRepo = chain.includes(GITHUB_TRACKER) || reading2.provider === "gh";
|
|
9738
|
+
const ownRepo = needsOwnRepo ? await viewRepo(seams.gh, []) : null;
|
|
9739
|
+
return [
|
|
9740
|
+
finding("push-remote", pushRemoteText(pushUrl)),
|
|
9741
|
+
finding("visibility", await visibilityText(pushUrl, seams.gh)),
|
|
9742
|
+
finding("issue-tracker", trackerText(chain, ownRepo)),
|
|
9743
|
+
finding("pr-provider", providerText(reading2.provider, reading2.source, ownRepo))
|
|
9744
|
+
];
|
|
9745
|
+
}
|
|
9746
|
+
function finding(subject, text) {
|
|
9747
|
+
return { level: "note", kind: "account", subject, text };
|
|
9748
|
+
}
|
|
9749
|
+
function remoteUrl(git, args) {
|
|
9750
|
+
const result = git(args);
|
|
9751
|
+
const url = result.stdout.trim();
|
|
9752
|
+
return result.ok && url !== "" ? url : null;
|
|
9753
|
+
}
|
|
9754
|
+
function pushRemoteText(pushUrl) {
|
|
9755
|
+
return pushUrl === null ? `push remote: no ${PUSH_REMOTE} remote, so the loop's push would fail` : `push remote: ${PUSH_REMOTE} \u2192 ${redactRemote(pushUrl)}`;
|
|
9756
|
+
}
|
|
9757
|
+
async function visibilityText(pushUrl, gh) {
|
|
9758
|
+
if (pushUrl === null) {
|
|
9759
|
+
return "visibility: not read, there is no push remote";
|
|
9760
|
+
}
|
|
9761
|
+
if (!isGitHubRemote(pushUrl)) {
|
|
9762
|
+
const host = remoteHost(pushUrl) ?? "a local path";
|
|
9763
|
+
return `visibility: not read, ${host} is not a GitHub host`;
|
|
9764
|
+
}
|
|
9765
|
+
const slug = repoSlug(pushUrl);
|
|
9766
|
+
const view = await viewRepo(gh, [slug]);
|
|
9767
|
+
if (!view.ok) {
|
|
9768
|
+
return `visibility: not read, ${view.reason}`;
|
|
9769
|
+
}
|
|
9770
|
+
const name = view.name ?? slug;
|
|
9771
|
+
return view.visibility === null ? `visibility: not read, gh named no visibility for ${name}` : `visibility: ${name} is ${view.visibility}`;
|
|
9772
|
+
}
|
|
9773
|
+
function repoSlug(url) {
|
|
9774
|
+
return normalizeRemote(url).replace(/^([^/]+):\d+\//, "$1/").replace(/^ssh\.github\.com\//, "github.com/");
|
|
9775
|
+
}
|
|
9776
|
+
function trackerText(chain, ownRepo) {
|
|
9777
|
+
const [first, ...rest] = chain;
|
|
9778
|
+
const fallback = rest.length === 0 ? "" : `, falling back to ${rest.join(", then ")}`;
|
|
9779
|
+
const where = chain.includes(GITHUB_TRACKER) && ownRepo !== null ? `; github files on ${repoPhrase(ownRepo)}` : "";
|
|
9780
|
+
return `issue tracker: ${first ?? "none configured"}${fallback}${where}`;
|
|
9781
|
+
}
|
|
9782
|
+
function providerText(provider, source, ownRepo) {
|
|
9783
|
+
const from = source === "config" ? "set in the config" : `inferred from ${PUSH_REMOTE}`;
|
|
9784
|
+
const effect = provider === "gh" && ownRepo !== null ? `, opening pull requests on ${repoPhrase(ownRepo)}` : "";
|
|
9785
|
+
const none = provider === "none" ? ", so the loop pushes and opens no pull request" : "";
|
|
9786
|
+
return `pull request provider: ${provider} (${from})${effect}${none}`;
|
|
9787
|
+
}
|
|
9788
|
+
function repoPhrase(view) {
|
|
9789
|
+
if (!view.ok) {
|
|
9790
|
+
return `the repository gh resolves, which it could not: ${view.reason}`;
|
|
9791
|
+
}
|
|
9792
|
+
return view.name ?? "the repository gh resolves, which it did not name";
|
|
9793
|
+
}
|
|
9794
|
+
async function viewRepo(gh, target) {
|
|
9795
|
+
const result = await gh(["repo", "view", ...target, "--json", "nameWithOwner,visibility"]);
|
|
9796
|
+
if (!result.ok) {
|
|
9797
|
+
return { ok: false, reason: `gh repo view: ${firstLine(result.stderr) ?? "gh exited non-zero"}` };
|
|
9798
|
+
}
|
|
9799
|
+
const parsed = parseJsonObject(result.stdout);
|
|
9800
|
+
if (parsed === null) {
|
|
9801
|
+
return { ok: false, reason: "gh repo view: answered no JSON object" };
|
|
9802
|
+
}
|
|
9803
|
+
return {
|
|
9804
|
+
ok: true,
|
|
9805
|
+
name: stringField(parsed, "nameWithOwner"),
|
|
9806
|
+
visibility: stringField(parsed, "visibility")?.toLowerCase() ?? null
|
|
9807
|
+
};
|
|
9808
|
+
}
|
|
9809
|
+
function parseJsonObject(text) {
|
|
9810
|
+
try {
|
|
9811
|
+
const value = JSON.parse(text);
|
|
9812
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
9813
|
+
} catch {
|
|
9814
|
+
return null;
|
|
9815
|
+
}
|
|
9816
|
+
}
|
|
9817
|
+
function stringField(object, key) {
|
|
9818
|
+
const value = object[key];
|
|
9819
|
+
return typeof value === "string" && value !== "" ? value : null;
|
|
9820
|
+
}
|
|
9821
|
+
function firstLine(text) {
|
|
9822
|
+
return text.split(`
|
|
9823
|
+
`).map((line) => line.trim()).find((line) => line !== "") ?? null;
|
|
9824
|
+
}
|
|
9825
|
+
|
|
9826
|
+
// src/plan/risk/commands.ts
|
|
9827
|
+
import { posix } from "path";
|
|
9828
|
+
|
|
9829
|
+
// src/check/shell-lines.ts
|
|
9830
|
+
var SHELL_FENCE_LANGUAGES = ["bash", "sh", "shell", "zsh", "console"];
|
|
9831
|
+
var FOREIGN_KEYWORDS = [
|
|
9832
|
+
"async",
|
|
9833
|
+
"await",
|
|
9834
|
+
"class",
|
|
9835
|
+
"const",
|
|
9836
|
+
"def",
|
|
9837
|
+
"enum",
|
|
9838
|
+
"fn",
|
|
9839
|
+
"from",
|
|
9840
|
+
"func",
|
|
9841
|
+
"impl",
|
|
9842
|
+
"import",
|
|
9843
|
+
"interface",
|
|
9844
|
+
"new",
|
|
9845
|
+
"package",
|
|
9846
|
+
"private",
|
|
9847
|
+
"protected",
|
|
9848
|
+
"public",
|
|
9849
|
+
"static",
|
|
9850
|
+
"struct",
|
|
9851
|
+
"throw",
|
|
9852
|
+
"val",
|
|
9853
|
+
"var"
|
|
9854
|
+
];
|
|
9855
|
+
var HEREDOC = /<<-?\s*['"]?([A-Za-z_][A-Za-z0-9_]*)['"]?/;
|
|
9856
|
+
var CONTINUES = /\\$/;
|
|
9857
|
+
var SPACED_ASSIGNMENT = /^\s*[A-Za-z_$][A-Za-z0-9_$.]*\s+=(?!=)\s+\S/;
|
|
9858
|
+
var FOREIGN_COMMENT = /^\s*(?:\/\/|\/\*|\*\/|\*\s)/;
|
|
9859
|
+
function isShellFence(info) {
|
|
9860
|
+
return SHELL_FENCE_LANGUAGES.includes(info);
|
|
9861
|
+
}
|
|
9862
|
+
function commandOf(line, info) {
|
|
9863
|
+
const text = line.trim();
|
|
9864
|
+
if (text === "" || text.startsWith("#"))
|
|
9865
|
+
return null;
|
|
9866
|
+
if (text.startsWith("$ ") || text === "$")
|
|
9867
|
+
return text.slice(1).trim() || null;
|
|
9868
|
+
if (info === "console")
|
|
9869
|
+
return null;
|
|
9870
|
+
if (text.startsWith("> "))
|
|
9871
|
+
return null;
|
|
9872
|
+
return text;
|
|
9873
|
+
}
|
|
9874
|
+
function heredocTerminator(command) {
|
|
9875
|
+
return HEREDOC.exec(command)?.[1] ?? null;
|
|
9876
|
+
}
|
|
9877
|
+
function continuesLine(command) {
|
|
9878
|
+
return CONTINUES.test(command);
|
|
9879
|
+
}
|
|
9880
|
+
function isForeignCodeLine(line) {
|
|
9881
|
+
const text = line.trim();
|
|
9882
|
+
if (text === "")
|
|
9883
|
+
return false;
|
|
9884
|
+
if (FOREIGN_COMMENT.test(text))
|
|
9885
|
+
return true;
|
|
9886
|
+
if (SPACED_ASSIGNMENT.test(text))
|
|
9887
|
+
return true;
|
|
9888
|
+
return FOREIGN_KEYWORDS.includes(text.split(/\s+/)[0] ?? "");
|
|
9889
|
+
}
|
|
9890
|
+
|
|
9891
|
+
// src/plan/risk/patterns.ts
|
|
9892
|
+
var COMMAND_WRAPPERS = [
|
|
9893
|
+
"command",
|
|
9894
|
+
"do",
|
|
9895
|
+
"else",
|
|
9896
|
+
"env",
|
|
9897
|
+
"exec",
|
|
9898
|
+
"nohup",
|
|
9899
|
+
"sudo",
|
|
9900
|
+
"then",
|
|
9901
|
+
"time",
|
|
9902
|
+
"xargs"
|
|
9903
|
+
];
|
|
9904
|
+
var OPENER = String.raw`(?:^|[;&|({\x60]|\s-exec(?:dir)?\s)`;
|
|
9905
|
+
var ASSIGNMENTS = String.raw`(?:[A-Za-z_]\w*=\S*\s+)*`;
|
|
9906
|
+
var WRAPPERS = String.raw`(?:(?:${COMMAND_WRAPPERS.join("|")})(?:\s+-\S+)*\s+${ASSIGNMENTS})*`;
|
|
9907
|
+
var AT_COMMAND = String.raw`${OPENER}\s*${ASSIGNMENTS}${WRAPPERS}`;
|
|
9908
|
+
var REST = "[^;&|]*";
|
|
9909
|
+
var GIT_OPTIONS = String.raw`(?:\s+(?:-[Cc]\s+\S+|--?[\w-]+(?:=\S+)?))*`;
|
|
9910
|
+
function hasFlag(short, long) {
|
|
9911
|
+
return String.raw`(?=(?:${REST}\s)?(?:-[A-Za-z]*${short}[A-Za-z]*|--${long})(?=\s|$))`;
|
|
9912
|
+
}
|
|
9913
|
+
function atCommand(name, body) {
|
|
9914
|
+
return { name, expression: new RegExp(`${AT_COMMAND}${body}`) };
|
|
9915
|
+
}
|
|
9916
|
+
var DESTRUCTIVE_PATTERNS = [
|
|
9917
|
+
atCommand("rm -rf", String.raw`rm\s+${hasFlag("[rR]", "recursive")}${hasFlag("f", "force")}`),
|
|
9918
|
+
atCommand("git push --force", String.raw`git${GIT_OPTIONS}\s+push\b(?=${REST}\s(?:--force|-[A-Za-z]*f[A-Za-z]*)(?=\s|$))`),
|
|
9919
|
+
atCommand("git reset --hard", String.raw`git${GIT_OPTIONS}\s+reset\b(?=${REST}\s--hard(?=\s|$))`),
|
|
9920
|
+
atCommand("git clean -f", String.raw`git${GIT_OPTIONS}\s+clean\s+${hasFlag("f", "force")}`),
|
|
9921
|
+
atCommand("sudo", String.raw`sudo(?=\s|$)`),
|
|
9922
|
+
atCommand("brew install", String.raw`brew(?:\s+-\S+)*\s+install\b`),
|
|
9923
|
+
atCommand("npm i -g", String.raw`npm\s+(?:i|install|add)(?=\s)(?=${REST}\s(?:-g|--global)(?=\s|$))`),
|
|
9924
|
+
atCommand("pip install", String.raw`(?:python3?\s+-m\s+)?pip3?\s+install\b`),
|
|
9925
|
+
atCommand("npm publish", String.raw`npm\s+publish\b(?!${REST}--dry-run)`),
|
|
9926
|
+
atCommand("curl \u2026 | sh", String.raw`curl\b[^;&|]*\|(?!\|)\s*${WRAPPERS}(?:ba|z|da)?sh(?=\s|$)`),
|
|
9927
|
+
atCommand("chmod -R", String.raw`chmod\s+${hasFlag("R", "recursive")}`),
|
|
9928
|
+
{ name: "DROP TABLE", expression: /\bDROP\s+TABLE\b/i },
|
|
9929
|
+
atCommand("docker system prune", String.raw`docker\s+system\s+prune\b`)
|
|
9930
|
+
];
|
|
9931
|
+
function destructiveMatches(command) {
|
|
9932
|
+
return DESTRUCTIVE_PATTERNS.filter((pattern) => pattern.expression.test(command));
|
|
9933
|
+
}
|
|
9934
|
+
|
|
9935
|
+
// src/plan/risk/commands.ts
|
|
9936
|
+
var DEVICE_FILES = [
|
|
9937
|
+
"/dev/null",
|
|
9938
|
+
"/dev/stdin",
|
|
9939
|
+
"/dev/stdout",
|
|
9940
|
+
"/dev/stderr",
|
|
9941
|
+
"/dev/tty"
|
|
9942
|
+
];
|
|
9943
|
+
var WRITES_EVERY_OPERAND = ["mv", "rm", "tee"];
|
|
9944
|
+
var COMMAND_PREFIXES = [...COMMAND_WRAPPERS, "!", "{"];
|
|
9945
|
+
var FENCE_OPEN = /^ {0,3}(`{3,}|~{3,})[ \t]*(\S*)/;
|
|
9946
|
+
var FENCE_CLOSE = /^ {0,3}(`{3,}|~{3,})[ \t]*$/;
|
|
9947
|
+
var CODE_SPAN2 = /`([^`\n]+)`/g;
|
|
9948
|
+
var REDIRECT = /^(?:&>>|&>|>>|>\||>&|<<<|<<-|<<|<&|<>|>|<)/;
|
|
9949
|
+
var OUTPUT_REDIRECTS = /^(?:\d*(?:>>|>\||>&|<>|>)|&>>|&>)$/;
|
|
9950
|
+
var HEREDOC_REDIRECTS = /^\d*<<[-<]?$/;
|
|
9951
|
+
var SEPARATORS2 = ";&|()`";
|
|
9952
|
+
var ASSIGNMENT = /^[A-Za-z_]\w*=/;
|
|
9953
|
+
var SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*:\/\//;
|
|
9954
|
+
function openFence(open) {
|
|
9955
|
+
const run = open[1] ?? "";
|
|
9956
|
+
return {
|
|
9957
|
+
info: (open[2] ?? "").split(/[\s,{]/)[0]?.toLowerCase() ?? "",
|
|
9958
|
+
delimiter: run[0] ?? "`",
|
|
9959
|
+
length: run.length,
|
|
9960
|
+
terminator: null,
|
|
9961
|
+
pending: null,
|
|
9962
|
+
foreign: false,
|
|
9963
|
+
commands: []
|
|
9964
|
+
};
|
|
9965
|
+
}
|
|
9966
|
+
function closesFence(line, fence) {
|
|
9967
|
+
const run = FENCE_CLOSE.exec(line)?.[1] ?? "";
|
|
9968
|
+
return run !== "" && run[0] === fence.delimiter && run.length >= fence.length;
|
|
9969
|
+
}
|
|
9970
|
+
function withoutBackslash(piece) {
|
|
9971
|
+
return piece.replace(/\\$/, "").trim();
|
|
9972
|
+
}
|
|
9973
|
+
function flush(fence) {
|
|
9974
|
+
if (fence.pending !== null)
|
|
9975
|
+
fence.commands.push({ text: fence.pending.text, line: fence.pending.line });
|
|
9976
|
+
fence.pending = null;
|
|
9977
|
+
}
|
|
9978
|
+
function continuationOf(line, info) {
|
|
9979
|
+
const text = line.trim();
|
|
9980
|
+
return info === "console" && text.startsWith("> ") ? text.slice(2) : text;
|
|
9981
|
+
}
|
|
9982
|
+
function readFenceLine(fence, line, number) {
|
|
9983
|
+
const pending = fence.pending;
|
|
9984
|
+
if (pending === null && fence.terminator !== null) {
|
|
9985
|
+
if (line.trim() === fence.terminator)
|
|
9986
|
+
fence.terminator = null;
|
|
9987
|
+
return;
|
|
9988
|
+
}
|
|
9989
|
+
const piece = pending === null ? commandOf(line, fence.info) : continuationOf(line, fence.info);
|
|
9990
|
+
if (piece === null || piece === "")
|
|
9991
|
+
return;
|
|
9992
|
+
if (isForeignCodeLine(piece)) {
|
|
9993
|
+
fence.foreign = true;
|
|
9994
|
+
return;
|
|
9995
|
+
}
|
|
9996
|
+
fence.terminator = heredocTerminator(piece) ?? fence.terminator;
|
|
9997
|
+
if (pending === null)
|
|
9998
|
+
fence.pending = { text: withoutBackslash(piece), line: number };
|
|
9999
|
+
else
|
|
10000
|
+
pending.text = `${pending.text} ${withoutBackslash(piece)}`;
|
|
10001
|
+
if (!continuesLine(piece))
|
|
10002
|
+
flush(fence);
|
|
10003
|
+
}
|
|
10004
|
+
function commandsOf(fence) {
|
|
10005
|
+
flush(fence);
|
|
10006
|
+
return fence.foreign ? [] : fence.commands;
|
|
10007
|
+
}
|
|
10008
|
+
function shellCommandLines(body) {
|
|
10009
|
+
const found = [];
|
|
10010
|
+
let fence = null;
|
|
10011
|
+
for (const [index, line] of body.split(`
|
|
10012
|
+
`).entries()) {
|
|
10013
|
+
if (fence === null) {
|
|
10014
|
+
const open = FENCE_OPEN.exec(line);
|
|
10015
|
+
if (open !== null)
|
|
10016
|
+
fence = openFence(open);
|
|
10017
|
+
continue;
|
|
10018
|
+
}
|
|
10019
|
+
if (closesFence(line, fence)) {
|
|
10020
|
+
found.push(...commandsOf(fence));
|
|
10021
|
+
fence = null;
|
|
10022
|
+
continue;
|
|
10023
|
+
}
|
|
10024
|
+
if (isShellFence(fence.info) && !fence.foreign)
|
|
10025
|
+
readFenceLine(fence, line, index + 1);
|
|
10026
|
+
}
|
|
10027
|
+
if (fence !== null)
|
|
10028
|
+
found.push(...commandsOf(fence));
|
|
10029
|
+
return found;
|
|
10030
|
+
}
|
|
10031
|
+
function taskLineCommands(taskLine, line) {
|
|
10032
|
+
const found = [];
|
|
10033
|
+
for (const match of taskLine.matchAll(CODE_SPAN2)) {
|
|
10034
|
+
const command = commandOf(match[1] ?? "", "sh");
|
|
10035
|
+
if (command !== null && !isForeignCodeLine(command))
|
|
10036
|
+
found.push({ text: command, line });
|
|
10037
|
+
}
|
|
10038
|
+
return found;
|
|
10039
|
+
}
|
|
10040
|
+
function quotedEnd(command, start) {
|
|
10041
|
+
const quote = command[start];
|
|
10042
|
+
let index = start + 1;
|
|
10043
|
+
while (index < command.length && command[index] !== quote) {
|
|
10044
|
+
index += quote === '"' && command[index] === "\\" ? 2 : 1;
|
|
10045
|
+
}
|
|
10046
|
+
return Math.min(index + 1, command.length);
|
|
10047
|
+
}
|
|
10048
|
+
function unquote(run) {
|
|
10049
|
+
const closed = run.length > 1 && run.endsWith(run[0] ?? "");
|
|
10050
|
+
const inner = run.slice(1, closed ? -1 : undefined);
|
|
10051
|
+
return run.startsWith('"') ? inner.replace(/\\(["\\$`])/g, "$1") : inner;
|
|
10052
|
+
}
|
|
10053
|
+
function opensRedirect(rest) {
|
|
10054
|
+
return rest.startsWith(">") || rest.startsWith("<") || rest.startsWith("&>");
|
|
10055
|
+
}
|
|
10056
|
+
function shellTokens(command) {
|
|
10057
|
+
const tokens = [];
|
|
10058
|
+
let word = "";
|
|
10059
|
+
let hasWord = false;
|
|
10060
|
+
const endWord = () => {
|
|
10061
|
+
if (hasWord)
|
|
10062
|
+
tokens.push({ kind: "word", text: word });
|
|
10063
|
+
word = "";
|
|
10064
|
+
hasWord = false;
|
|
10065
|
+
};
|
|
10066
|
+
let index = 0;
|
|
10067
|
+
while (index < command.length) {
|
|
10068
|
+
const char = command[index] ?? "";
|
|
10069
|
+
const rest = command.slice(index);
|
|
10070
|
+
if (opensRedirect(rest)) {
|
|
10071
|
+
const fd = /^\d+$/.test(word) ? word : "";
|
|
10072
|
+
if (fd === "")
|
|
10073
|
+
endWord();
|
|
10074
|
+
word = "";
|
|
10075
|
+
hasWord = false;
|
|
10076
|
+
const operator = REDIRECT.exec(rest)?.[0] ?? char;
|
|
10077
|
+
tokens.push({ kind: "redirect", text: `${fd}${operator}` });
|
|
10078
|
+
index += operator.length;
|
|
10079
|
+
} else if (char === "'" || char === '"') {
|
|
10080
|
+
const end = quotedEnd(command, index);
|
|
10081
|
+
word += unquote(command.slice(index, end));
|
|
10082
|
+
hasWord = true;
|
|
10083
|
+
index = end;
|
|
10084
|
+
} else if (char === "\\") {
|
|
10085
|
+
word += command[index + 1] ?? "";
|
|
10086
|
+
hasWord = true;
|
|
10087
|
+
index += 2;
|
|
10088
|
+
} else if (/\s/.test(char)) {
|
|
10089
|
+
endWord();
|
|
10090
|
+
index += 1;
|
|
10091
|
+
} else if (SEPARATORS2.includes(char)) {
|
|
10092
|
+
endWord();
|
|
10093
|
+
const operator = /^(?:&&|\|\||[;&|()`])/.exec(rest)?.[0] ?? char;
|
|
10094
|
+
tokens.push({ kind: "separator", text: operator });
|
|
10095
|
+
index += operator.length;
|
|
10096
|
+
} else {
|
|
10097
|
+
word += char;
|
|
10098
|
+
hasWord = true;
|
|
10099
|
+
index += 1;
|
|
10100
|
+
}
|
|
10101
|
+
}
|
|
10102
|
+
endWord();
|
|
10103
|
+
return tokens;
|
|
10104
|
+
}
|
|
10105
|
+
function segmentsOf(tokens) {
|
|
10106
|
+
const segments = [[]];
|
|
10107
|
+
for (const token of tokens) {
|
|
10108
|
+
if (token.kind === "separator")
|
|
10109
|
+
segments.push([]);
|
|
10110
|
+
else
|
|
10111
|
+
segments.at(-1)?.push(token);
|
|
10112
|
+
}
|
|
10113
|
+
return segments.filter((segment) => segment.length > 0);
|
|
10114
|
+
}
|
|
10115
|
+
function wordsOf2(segment) {
|
|
10116
|
+
const argv = [];
|
|
10117
|
+
const redirected = [];
|
|
10118
|
+
let operator = null;
|
|
10119
|
+
for (const token of segment) {
|
|
10120
|
+
if (token.kind === "redirect") {
|
|
10121
|
+
operator = token.text;
|
|
10122
|
+
} else if (operator === null) {
|
|
10123
|
+
argv.push(token.text);
|
|
10124
|
+
} else {
|
|
10125
|
+
if (!HEREDOC_REDIRECTS.test(operator)) {
|
|
10126
|
+
redirected.push({ word: token.text, writes: OUTPUT_REDIRECTS.test(operator) });
|
|
10127
|
+
}
|
|
10128
|
+
operator = null;
|
|
10129
|
+
}
|
|
10130
|
+
}
|
|
10131
|
+
return { argv, redirected };
|
|
10132
|
+
}
|
|
10133
|
+
function commandIndex(argv) {
|
|
10134
|
+
let index = 0;
|
|
10135
|
+
let afterWrapper = false;
|
|
10136
|
+
while (index < argv.length) {
|
|
10137
|
+
const word = argv[index] ?? "";
|
|
10138
|
+
const isWrapperFlag = afterWrapper && word.startsWith("-");
|
|
10139
|
+
if (!ASSIGNMENT.test(word) && !COMMAND_PREFIXES.includes(word) && !isWrapperFlag)
|
|
10140
|
+
return index;
|
|
10141
|
+
afterWrapper = COMMAND_PREFIXES.includes(word) || isWrapperFlag;
|
|
10142
|
+
index += 1;
|
|
10143
|
+
}
|
|
10144
|
+
return index;
|
|
10145
|
+
}
|
|
10146
|
+
function operandsOf(args) {
|
|
10147
|
+
const end = args.indexOf("--");
|
|
10148
|
+
const flagged = end === -1 ? args : args.slice(0, end);
|
|
10149
|
+
const after = end === -1 ? [] : args.slice(end + 1);
|
|
10150
|
+
return [...flagged.filter((word) => !word.startsWith("-")), ...after];
|
|
10151
|
+
}
|
|
10152
|
+
function copyTargets(args) {
|
|
10153
|
+
const flag = args.indexOf("-t");
|
|
10154
|
+
if (flag !== -1 && args[flag + 1] !== undefined)
|
|
10155
|
+
return [args[flag + 1] ?? ""];
|
|
10156
|
+
const long = args.find((word) => word.startsWith("--target-directory="));
|
|
10157
|
+
if (long !== undefined)
|
|
10158
|
+
return [long.slice("--target-directory=".length)];
|
|
10159
|
+
return operandsOf(args).slice(-1);
|
|
10160
|
+
}
|
|
10161
|
+
function writtenWords(argv) {
|
|
10162
|
+
const at = commandIndex(argv);
|
|
10163
|
+
const name = posix.basename(argv[at] ?? "");
|
|
10164
|
+
const args = argv.slice(at + 1);
|
|
10165
|
+
if (WRITES_EVERY_OPERAND.includes(name))
|
|
10166
|
+
return new Set(operandsOf(args));
|
|
10167
|
+
if (name === "cp")
|
|
10168
|
+
return new Set(copyTargets(args));
|
|
10169
|
+
return new Set;
|
|
10170
|
+
}
|
|
10171
|
+
function pathCandidate(word) {
|
|
10172
|
+
if (word.startsWith("/") || word.startsWith("~"))
|
|
10173
|
+
return word;
|
|
10174
|
+
const equals = word.indexOf("=");
|
|
10175
|
+
return equals === -1 ? word : word.slice(equals + 1);
|
|
10176
|
+
}
|
|
10177
|
+
function isWithin(root, target) {
|
|
10178
|
+
const step = posix.relative(posix.resolve(root), posix.resolve(target));
|
|
10179
|
+
return !step.startsWith("..") && !posix.isAbsolute(step);
|
|
10180
|
+
}
|
|
10181
|
+
function isOutsidePath(path5, seams) {
|
|
10182
|
+
if (SCHEME.test(path5) || path5.startsWith("//") || DEVICE_FILES.includes(path5))
|
|
10183
|
+
return false;
|
|
10184
|
+
if (path5 === "~" || path5.startsWith("~/"))
|
|
10185
|
+
return !isWithin(seams.repoRoot, posix.join(seams.home, path5.slice(1)));
|
|
10186
|
+
if (path5.startsWith("~"))
|
|
10187
|
+
return true;
|
|
10188
|
+
return path5.startsWith("/") && !isWithin(seams.repoRoot, path5);
|
|
10189
|
+
}
|
|
10190
|
+
function outsidePaths(command, seams) {
|
|
10191
|
+
const found = new Map;
|
|
10192
|
+
const note = (word, writes) => {
|
|
10193
|
+
const path5 = pathCandidate(word);
|
|
10194
|
+
if (isOutsidePath(path5, seams))
|
|
10195
|
+
found.set(path5, (found.get(path5) ?? false) || writes);
|
|
10196
|
+
};
|
|
10197
|
+
for (const segment of segmentsOf(shellTokens(command))) {
|
|
10198
|
+
const { argv, redirected } = wordsOf2(segment);
|
|
10199
|
+
const written = writtenWords(argv);
|
|
10200
|
+
for (const word of argv)
|
|
10201
|
+
note(word, written.has(word) || written.has(pathCandidate(word)));
|
|
10202
|
+
for (const target of redirected)
|
|
10203
|
+
note(target.word, target.writes);
|
|
10204
|
+
}
|
|
10205
|
+
return Array.from(found, ([path5, writes]) => ({ path: path5, writes }));
|
|
10206
|
+
}
|
|
10207
|
+
function findingsOf(command, seams) {
|
|
10208
|
+
const destructive = destructiveMatches(command.text).map((pattern) => ({
|
|
10209
|
+
level: "high",
|
|
10210
|
+
kind: "destructive",
|
|
10211
|
+
text: `${pattern.name} \u2014 ${command.text}`,
|
|
10212
|
+
subject: pattern.name,
|
|
10213
|
+
command: command.text,
|
|
10214
|
+
line: command.line
|
|
10215
|
+
}));
|
|
10216
|
+
const paths = outsidePaths(command.text, seams).map((path5) => ({
|
|
10217
|
+
level: path5.writes ? "high" : "note",
|
|
10218
|
+
kind: "outside-path",
|
|
10219
|
+
text: `${path5.path} \u2014 ${path5.writes ? "written by" : "named in"} ${command.text}`,
|
|
10220
|
+
subject: path5.path,
|
|
10221
|
+
command: command.text,
|
|
10222
|
+
line: command.line
|
|
10223
|
+
}));
|
|
10224
|
+
return [...destructive, ...paths];
|
|
10225
|
+
}
|
|
10226
|
+
function readCommands2(commands, seams) {
|
|
10227
|
+
return commands.flatMap((command) => findingsOf(command, seams));
|
|
10228
|
+
}
|
|
10229
|
+
function readBodyCommands(body, seams) {
|
|
10230
|
+
return readCommands2(shellCommandLines(body), seams);
|
|
10231
|
+
}
|
|
10232
|
+
function readTaskLineCommands(taskLine, line, seams) {
|
|
10233
|
+
return readCommands2(taskLineCommands(taskLine, line), seams);
|
|
10234
|
+
}
|
|
10235
|
+
|
|
10236
|
+
// src/plan/risk/secrets.ts
|
|
10237
|
+
var SECRET_NAME_SHAPES = [
|
|
10238
|
+
{ name: "*_TOKEN", expression: /_TOKEN$/ },
|
|
10239
|
+
{ name: "*_KEY", expression: /_KEY$/ },
|
|
10240
|
+
{ name: "*_SECRET", expression: /_SECRET$/ },
|
|
10241
|
+
{ name: "*PASSWORD*", expression: /PASSWORD/ },
|
|
10242
|
+
{ name: "AWS_*", expression: /^AWS_/ }
|
|
10243
|
+
];
|
|
10244
|
+
function secretShapesOf(name) {
|
|
10245
|
+
return SECRET_NAME_SHAPES.filter((shape) => shape.expression.test(name));
|
|
10246
|
+
}
|
|
10247
|
+
function scanSecretNames(environment) {
|
|
10248
|
+
return [...Object.keys(environment)].sort().flatMap((name) => {
|
|
10249
|
+
const shapes = secretShapesOf(name);
|
|
10250
|
+
if (shapes.length === 0) {
|
|
10251
|
+
return [];
|
|
10252
|
+
}
|
|
10253
|
+
const matched = shapes.map((shape) => shape.name).join(", ");
|
|
10254
|
+
return [{ level: "note", kind: "secret", text: `${name} \u2014 matches ${matched}`, subject: name }];
|
|
10255
|
+
});
|
|
10256
|
+
}
|
|
10257
|
+
|
|
10258
|
+
// src/plan/risk.ts
|
|
10259
|
+
var RISK_KINDS = ["tools", "destructive", "outside-path", "account", "secret"];
|
|
10260
|
+
var EVERY_TOOL = "every tool";
|
|
10261
|
+
var SHELL_TOOL = "Bash";
|
|
10262
|
+
function agentTools(frontmatter) {
|
|
10263
|
+
const value = frontmatter["tools"];
|
|
10264
|
+
let names = [];
|
|
10265
|
+
if (typeof value === "string")
|
|
10266
|
+
names = value.split(",");
|
|
10267
|
+
else if (Array.isArray(value))
|
|
10268
|
+
names = value.filter((entry) => typeof entry === "string");
|
|
10269
|
+
const tools = [...new Set(names.map((name) => name.trim()).filter((name) => name !== ""))];
|
|
10270
|
+
return tools.length === 0 ? null : tools;
|
|
10271
|
+
}
|
|
10272
|
+
function holdsShell(tools) {
|
|
10273
|
+
return tools.some((tool) => tool === SHELL_TOOL || tool.startsWith(`${SHELL_TOOL}(`));
|
|
10274
|
+
}
|
|
10275
|
+
function frontmatterString(frontmatter, key) {
|
|
10276
|
+
const value = frontmatter[key];
|
|
10277
|
+
return typeof value === "string" && value.trim() !== "" ? value.trim() : null;
|
|
10278
|
+
}
|
|
10279
|
+
function taskAccess(declaration, seams) {
|
|
10280
|
+
const budget = declaration?.budget ?? null;
|
|
10281
|
+
const agent = declaration?.agent ?? null;
|
|
10282
|
+
if (agent === null) {
|
|
10283
|
+
const tools2 = declaration?.tools ?? null;
|
|
10284
|
+
return {
|
|
10285
|
+
tools: tools2,
|
|
10286
|
+
source: tools2 === null ? "no agent= and no tools=" : "tools=",
|
|
10287
|
+
model: declaration?.model ?? null,
|
|
10288
|
+
budget
|
|
10289
|
+
};
|
|
10290
|
+
}
|
|
10291
|
+
const definition = findAgentDefinition(agent, { repoRoot: seams.repoRoot, home: seams.home }, seams.settingSources);
|
|
10292
|
+
if (definition === null) {
|
|
10293
|
+
return { tools: null, source: `agent=${agent} resolves to no definition`, model: null, budget };
|
|
10294
|
+
}
|
|
10295
|
+
const tools = agentTools(definition.frontmatter);
|
|
10296
|
+
return {
|
|
10297
|
+
tools,
|
|
10298
|
+
source: tools === null ? `agent=${agent} has no tools: line` : `agent=${agent}`,
|
|
10299
|
+
model: frontmatterString(definition.frontmatter, "model"),
|
|
10300
|
+
budget
|
|
10301
|
+
};
|
|
10302
|
+
}
|
|
10303
|
+
function toolsFinding(task, plan2, seams) {
|
|
10304
|
+
const access = taskAccess(task.declaration, seams);
|
|
10305
|
+
const model = `model ${access.model ?? "the session default"}`;
|
|
10306
|
+
const budget = access.budget === null ? "no budget=" : `budget $${String(access.budget)}`;
|
|
10307
|
+
const where = { task: task.text, file: plan2, line: task.lineNum + 1 };
|
|
10308
|
+
if (access.tools === null) {
|
|
10309
|
+
return { level: "high", kind: "tools", text: `${EVERY_TOOL} \u2014 ${access.source}; ${model}; ${budget}`, ...where };
|
|
10310
|
+
}
|
|
10311
|
+
if (holdsShell(access.tools) && access.budget === null) {
|
|
10312
|
+
const listed = access.tools.join(", ");
|
|
10313
|
+
return { level: "note", kind: "tools", text: `${SHELL_TOOL} with no budget= \u2014 ${access.source}: ${listed}; ${model}`, ...where };
|
|
10314
|
+
}
|
|
10315
|
+
return null;
|
|
10316
|
+
}
|
|
10317
|
+
function displayPath(path5, seams) {
|
|
10318
|
+
const absolute = isAbsolute3(path5) ? path5 : join13(seams.repoRoot, path5);
|
|
10319
|
+
const within = (root) => {
|
|
10320
|
+
const step = relative2(root, absolute);
|
|
10321
|
+
return step === "" || step.startsWith("..") || isAbsolute3(step) ? null : step;
|
|
10322
|
+
};
|
|
10323
|
+
const inRepo = within(seams.repoRoot);
|
|
10324
|
+
if (inRepo !== null)
|
|
10325
|
+
return inRepo;
|
|
10326
|
+
const inHome = within(seams.home);
|
|
10327
|
+
return inHome === null ? absolute : `~/${inHome}`;
|
|
10328
|
+
}
|
|
10329
|
+
function located(finding2, file, task) {
|
|
10330
|
+
const base = { level: finding2.level, kind: finding2.kind, text: finding2.text };
|
|
10331
|
+
return task === undefined ? { ...base, file, line: finding2.line } : { ...base, task, file, line: finding2.line };
|
|
10332
|
+
}
|
|
10333
|
+
function readText4(path5) {
|
|
10334
|
+
try {
|
|
10335
|
+
return readFileSync16(path5, "utf8");
|
|
10336
|
+
} catch {
|
|
10337
|
+
return null;
|
|
10338
|
+
}
|
|
10339
|
+
}
|
|
10340
|
+
function findSkillFile(name, seams) {
|
|
10341
|
+
const tiers = resolveSkillTiers({ home: seams.home, projectRoot: seams.repoRoot, entry: seams.entry });
|
|
10342
|
+
for (const { dir } of tiers) {
|
|
10343
|
+
const path5 = join13(dir, name, "SKILL.md");
|
|
10344
|
+
if (readText4(path5) !== null)
|
|
10345
|
+
return path5;
|
|
10346
|
+
}
|
|
10347
|
+
return null;
|
|
10348
|
+
}
|
|
10349
|
+
function taskFiles(declaration, seams) {
|
|
10350
|
+
if (declaration === null)
|
|
10351
|
+
return [];
|
|
10352
|
+
const agent = declaration.agent === null ? null : findAgentDefinition(declaration.agent, { repoRoot: seams.repoRoot, home: seams.home }, seams.settingSources);
|
|
10353
|
+
const skills = (declaration.skills ?? []).map((name) => findSkillFile(name, seams));
|
|
10354
|
+
return [agent?.path ?? null, ...skills].filter((path5) => path5 !== null);
|
|
10355
|
+
}
|
|
10356
|
+
function commandFindings(plan2, display, tasks, seams) {
|
|
10357
|
+
const paths = { repoRoot: seams.repoRoot, home: seams.home };
|
|
10358
|
+
const found = readBodyCommands(plan2.text, paths).map((finding2) => located(finding2, display));
|
|
10359
|
+
const read = new Set;
|
|
10360
|
+
const lines = plan2.text.split(`
|
|
10361
|
+
`);
|
|
10362
|
+
for (const task of tasks) {
|
|
10363
|
+
const spans = readTaskLineCommands(lines[task.lineNum] ?? "", task.lineNum + 1, paths);
|
|
10364
|
+
found.push(...spans.map((finding2) => located(finding2, display, task.text)));
|
|
10365
|
+
for (const file of taskFiles(task.declaration, seams)) {
|
|
10366
|
+
if (read.has(file))
|
|
10367
|
+
continue;
|
|
10368
|
+
read.add(file);
|
|
10369
|
+
const shown = displayPath(file, seams);
|
|
10370
|
+
found.push(...readBodyCommands(readText4(file) ?? "", paths).map((finding2) => located(finding2, shown)));
|
|
10371
|
+
}
|
|
10372
|
+
}
|
|
10373
|
+
return found;
|
|
10374
|
+
}
|
|
10375
|
+
function openTasks(tasks, hidden) {
|
|
10376
|
+
return [...tasks, ...hidden].filter((task) => task.status !== "done").sort((a, b) => a.lineNum - b.lineNum);
|
|
10377
|
+
}
|
|
10378
|
+
function riskTotal(findings) {
|
|
10379
|
+
return {
|
|
10380
|
+
high: findings.filter((finding2) => finding2.level === "high").length,
|
|
10381
|
+
note: findings.filter((finding2) => finding2.level === "note").length
|
|
10382
|
+
};
|
|
10383
|
+
}
|
|
10384
|
+
async function assessPlanRisk(plan2, seams) {
|
|
10385
|
+
const model = parsePlan(plan2.text);
|
|
10386
|
+
const display = displayPath(plan2.path, seams);
|
|
10387
|
+
const tasks = openTasks(model.tasks, model.hiddenTasks);
|
|
10388
|
+
const tools = tasks.flatMap((task) => toolsFinding(task, display, seams) ?? []);
|
|
10389
|
+
const commands = commandFindings(plan2, display, tasks, seams);
|
|
10390
|
+
const accounts = (await readAccounts(seams.accounts, seams.runners)).map(({ level, kind, text }) => ({ level, kind, text }));
|
|
10391
|
+
const secrets = scanSecretNames(seams.environment).map(({ level, kind, text }) => ({ level, kind, text }));
|
|
10392
|
+
const findings = [...tools, ...commands, ...accounts, ...secrets];
|
|
10393
|
+
return { plan: display, total: riskTotal(findings), findings };
|
|
10394
|
+
}
|
|
10395
|
+
function counted2(count, word) {
|
|
10396
|
+
return count === 1 ? `${String(count)} ${word}` : `${String(count)} ${word}s`;
|
|
10397
|
+
}
|
|
10398
|
+
function riskTotalLine(report2) {
|
|
10399
|
+
return `\uD83D\uDEE1 Risk: ${String(report2.total.high)} high, ${counted2(report2.total.note, "note")} \u2014 rafa plan risk ${report2.plan}`;
|
|
10400
|
+
}
|
|
10401
|
+
var KIND_WIDTH = Math.max(...RISK_KINDS.map((kind) => kind.length));
|
|
10402
|
+
|
|
10403
|
+
// src/start/risk-total.ts
|
|
10404
|
+
function spawningRunners(root) {
|
|
10405
|
+
return { git: createGitRunner(root), gh: createGhRunner({ cwd: root }) };
|
|
10406
|
+
}
|
|
10407
|
+
async function readRiskTotalLine(input, seams = {}) {
|
|
10408
|
+
const makeRunners = seams.runners ?? spawningRunners;
|
|
10409
|
+
const report2 = await assessPlanRisk({ path: input.planPath, text: readFileSync17(input.planPath, "utf8") }, {
|
|
10410
|
+
repoRoot: input.repoRoot,
|
|
10411
|
+
home: input.home,
|
|
10412
|
+
settingSources: input.config.settingSources,
|
|
10413
|
+
environment: input.environment,
|
|
10414
|
+
accounts: {
|
|
10415
|
+
prProvider: input.config.prProvider,
|
|
10416
|
+
trackerDefault: input.config.trackerDefault,
|
|
10417
|
+
trackerFallback: input.config.trackerFallback
|
|
10418
|
+
},
|
|
10419
|
+
runners: makeRunners(input.repoRoot)
|
|
10420
|
+
});
|
|
10421
|
+
return riskTotalLine(report2);
|
|
10422
|
+
}
|
|
10423
|
+
async function announceRiskTotal(input, seams = {}) {
|
|
10424
|
+
const output = seams.output ?? activeOutput();
|
|
10425
|
+
try {
|
|
10426
|
+
output.info(await readRiskTotalLine(input, seams));
|
|
10427
|
+
} catch (error) {
|
|
10428
|
+
const said = error instanceof Error ? error.message : String(error);
|
|
10429
|
+
output.warn(`\u26A0\uFE0F Risk: could not read ${input.planPath} \u2014 ${said}`);
|
|
10430
|
+
}
|
|
10431
|
+
}
|
|
10432
|
+
|
|
9692
10433
|
// src/start/session.ts
|
|
9693
10434
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
9694
|
-
import { relative as
|
|
10435
|
+
import { relative as relative3 } from "path";
|
|
9695
10436
|
var NOTHING_DISPATCHED = " Nothing was checked and nothing was dispatched.";
|
|
9696
10437
|
function recordLabel(repoRoot, sessionId) {
|
|
9697
|
-
return
|
|
10438
|
+
return relative3(repoRoot, sessionFilePath(repoRoot, sessionId));
|
|
9698
10439
|
}
|
|
9699
10440
|
function conflictLine(repoRoot, conflict) {
|
|
9700
10441
|
const { record } = conflict;
|
|
@@ -9724,7 +10465,7 @@ function openRefusal(repoRoot, draft, error) {
|
|
|
9724
10465
|
if (!(error instanceof SessionRecordError) && !isSystemError)
|
|
9725
10466
|
return null;
|
|
9726
10467
|
return [
|
|
9727
|
-
`\u274C Refusing to start: the session records under ${
|
|
10468
|
+
`\u274C Refusing to start: the session records under ${relative3(repoRoot, runsDir(repoRoot))}/ cannot be read or written.`,
|
|
9728
10469
|
` ${messageOf(error)}`,
|
|
9729
10470
|
NOTHING_DISPATCHED
|
|
9730
10471
|
].join(`
|
|
@@ -9760,7 +10501,7 @@ function openRunSession(options) {
|
|
|
9760
10501
|
const draft = {
|
|
9761
10502
|
sessionId: (seams.newSessionId ?? randomUUID8)(),
|
|
9762
10503
|
planStub: options.planStub,
|
|
9763
|
-
plan:
|
|
10504
|
+
plan: relative3(repoRoot, options.planPath),
|
|
9764
10505
|
branch: options.branch,
|
|
9765
10506
|
pid: seams.pid ?? process.pid,
|
|
9766
10507
|
startedAt: (seams.now ?? (() => new Date))().toISOString()
|
|
@@ -9837,11 +10578,11 @@ function announcePlanIssues(planContent) {
|
|
|
9837
10578
|
}
|
|
9838
10579
|
|
|
9839
10580
|
// src/start/runtime.ts
|
|
9840
|
-
import { existsSync as
|
|
10581
|
+
import { existsSync as existsSync17, readdirSync as readdirSync6, realpathSync as realpathSync4, statSync as statSync8 } from "fs";
|
|
9841
10582
|
import { homedir as homedir6 } from "os";
|
|
9842
|
-
import { isAbsolute as
|
|
10583
|
+
import { isAbsolute as isAbsolute4, join as join14, relative as relative4, resolve as resolve10, sep } from "path";
|
|
9843
10584
|
var RUNTIME_FLAG = "--runtime";
|
|
9844
|
-
var RUNTIME_SUBDIR =
|
|
10585
|
+
var RUNTIME_SUBDIR = join14(".rafa", "runtime");
|
|
9845
10586
|
var RUNTIME_ENTRY = "cli.js";
|
|
9846
10587
|
var SOURCE_DIR = "src";
|
|
9847
10588
|
var RUNTIME_COMMAND = ["start"];
|
|
@@ -9858,10 +10599,10 @@ function resolveRuntime(value, places) {
|
|
|
9858
10599
|
throw refusal3([`\u274C Refusing to start: \`--runtime\` names no runtime. Spell it ${SPELLING}.`]);
|
|
9859
10600
|
}
|
|
9860
10601
|
const isVersion = VERSION_SHAPE2.test(value);
|
|
9861
|
-
const named = isVersion ?
|
|
10602
|
+
const named = isVersion ? join14(places.home, RUNTIME_SUBDIR, value, RUNTIME_ENTRY) : resolve10(places.cwd, value);
|
|
9862
10603
|
refuseInsideSource(value, named, places);
|
|
9863
10604
|
const entry = entryOf(value, named, isVersion, places.home);
|
|
9864
|
-
const real =
|
|
10605
|
+
const real = realpathSync4(entry);
|
|
9865
10606
|
refuseInsideSource(value, real, places);
|
|
9866
10607
|
return real;
|
|
9867
10608
|
}
|
|
@@ -9869,7 +10610,7 @@ function entryOf(value, named, isVersion, home) {
|
|
|
9869
10610
|
if (isVersion) {
|
|
9870
10611
|
if (isFile2(named))
|
|
9871
10612
|
return named;
|
|
9872
|
-
const root =
|
|
10613
|
+
const root = join14(home, RUNTIME_SUBDIR);
|
|
9873
10614
|
const installed = installedVersions(root);
|
|
9874
10615
|
throw refusal3([
|
|
9875
10616
|
`\u274C Refusing to start: \`--runtime=${value}\` names no installed runtime: ${named} is no file.`,
|
|
@@ -9878,7 +10619,7 @@ function entryOf(value, named, isVersion, home) {
|
|
|
9878
10619
|
}
|
|
9879
10620
|
if (isFile2(named))
|
|
9880
10621
|
return named;
|
|
9881
|
-
const inside =
|
|
10622
|
+
const inside = join14(named, RUNTIME_ENTRY);
|
|
9882
10623
|
if (isDirectory(named) && isFile2(inside))
|
|
9883
10624
|
return inside;
|
|
9884
10625
|
throw refusal3([
|
|
@@ -9896,31 +10637,31 @@ function refuseInsideSource(value, path5, places) {
|
|
|
9896
10637
|
]);
|
|
9897
10638
|
}
|
|
9898
10639
|
function sourceDirs({ cwd, root }) {
|
|
9899
|
-
const dirs = [
|
|
9900
|
-
const real = dirs.filter((dir) =>
|
|
10640
|
+
const dirs = [join14(cwd, SOURCE_DIR), join14(root, SOURCE_DIR)];
|
|
10641
|
+
const real = dirs.filter((dir) => existsSync17(dir)).map((dir) => realpathSync4(dir));
|
|
9901
10642
|
return [...new Set([...dirs, ...real])];
|
|
9902
10643
|
}
|
|
9903
10644
|
function isInside(path5, dir) {
|
|
9904
|
-
const rel =
|
|
9905
|
-
return rel === "" || !
|
|
10645
|
+
const rel = relative4(dir, path5);
|
|
10646
|
+
return rel === "" || !isAbsolute4(rel) && rel !== ".." && !rel.startsWith(`..${sep}`);
|
|
9906
10647
|
}
|
|
9907
10648
|
function installedVersions(root) {
|
|
9908
10649
|
try {
|
|
9909
|
-
return readdirSync6(root).filter((name) => isFile2(
|
|
10650
|
+
return readdirSync6(root).filter((name) => isFile2(join14(root, name, RUNTIME_ENTRY))).sort((a, b) => a.localeCompare(b));
|
|
9910
10651
|
} catch {
|
|
9911
10652
|
return [];
|
|
9912
10653
|
}
|
|
9913
10654
|
}
|
|
9914
10655
|
function isFile2(path5) {
|
|
9915
10656
|
try {
|
|
9916
|
-
return
|
|
10657
|
+
return statSync8(path5).isFile();
|
|
9917
10658
|
} catch {
|
|
9918
10659
|
return false;
|
|
9919
10660
|
}
|
|
9920
10661
|
}
|
|
9921
10662
|
function isDirectory(path5) {
|
|
9922
10663
|
try {
|
|
9923
|
-
return
|
|
10664
|
+
return statSync8(path5).isDirectory();
|
|
9924
10665
|
} catch {
|
|
9925
10666
|
return false;
|
|
9926
10667
|
}
|
|
@@ -9949,7 +10690,7 @@ async function runFromSelectedRuntime(seams) {
|
|
|
9949
10690
|
}
|
|
9950
10691
|
function realOrResolved(path5) {
|
|
9951
10692
|
try {
|
|
9952
|
-
return
|
|
10693
|
+
return realpathSync4(path5);
|
|
9953
10694
|
} catch {
|
|
9954
10695
|
return resolve10(path5);
|
|
9955
10696
|
}
|
|
@@ -10037,11 +10778,11 @@ function eventOf(line) {
|
|
|
10037
10778
|
}
|
|
10038
10779
|
|
|
10039
10780
|
// src/triage/triage.ts
|
|
10040
|
-
import { basename as basename6, join as
|
|
10781
|
+
import { basename as basename6, join as join15 } from "path";
|
|
10041
10782
|
|
|
10042
10783
|
// src/effort/store/tracker-refs.ts
|
|
10043
10784
|
import { randomUUID as randomUUID9 } from "crypto";
|
|
10044
|
-
import { existsSync as
|
|
10785
|
+
import { existsSync as existsSync18 } from "fs";
|
|
10045
10786
|
var SELECT_HELD = `
|
|
10046
10787
|
SELECT seq, tracker_ref FROM findings
|
|
10047
10788
|
WHERE session_id = ? AND artifact = ?
|
|
@@ -10164,7 +10905,7 @@ function writeTrackerRef(repoRoot, write, seams = {}) {
|
|
|
10164
10905
|
wanted.json,
|
|
10165
10906
|
collectedAt
|
|
10166
10907
|
];
|
|
10167
|
-
const [action, stored] = withSqliteStore(path5, !
|
|
10908
|
+
const [action, stored] = withSqliteStore(path5, !existsSync18(path5), (db) => {
|
|
10168
10909
|
const place = db.transaction(() => placeRef(db, path5, write, wanted, insertValues));
|
|
10169
10910
|
return place.immediate();
|
|
10170
10911
|
});
|
|
@@ -10173,14 +10914,14 @@ function writeTrackerRef(repoRoot, write, seams = {}) {
|
|
|
10173
10914
|
function readTrackerRef(repoRoot, artifact) {
|
|
10174
10915
|
checkArtifact("read", artifact);
|
|
10175
10916
|
const path5 = sqliteStorePath(repoRoot);
|
|
10176
|
-
if (!
|
|
10917
|
+
if (!existsSync18(path5))
|
|
10177
10918
|
return null;
|
|
10178
10919
|
const oldest = withSqliteStore(path5, false, (db) => db.query(SELECT_OLDEST).get(artifact));
|
|
10179
10920
|
return oldest === null ? null : heldRefOf(path5, oldest.seq, oldest.tracker_ref).ref;
|
|
10180
10921
|
}
|
|
10181
10922
|
|
|
10182
10923
|
// src/triage/triage.ts
|
|
10183
|
-
var PRIVATE_TRIAGE_DIR =
|
|
10924
|
+
var PRIVATE_TRIAGE_DIR = join15(".rafa", "triage", "private");
|
|
10184
10925
|
var SECRET_ENV_NAMES = ["GITHUB_TOKEN", "ANTHROPIC_API_KEY", "LINEAR_API_KEY"];
|
|
10185
10926
|
var TRIAGE_MODULE = "unassigned";
|
|
10186
10927
|
var BLOCKER_SEPARATOR = "; ";
|
|
@@ -10191,7 +10932,7 @@ var MIN_FENCE_LENGTH = 3;
|
|
|
10191
10932
|
var ISSUE_OPENING = "An out-of-scope bug a rafa task session reported. The loop filed it and" + " dispatches no task for it: a plan that wants it fixed declares a task.";
|
|
10192
10933
|
var COMMENT_OPENING = "Reported again by a rafa task session.";
|
|
10193
10934
|
function privateTriageDir(repoRoot) {
|
|
10194
|
-
return
|
|
10935
|
+
return join15(repoRoot, PRIVATE_TRIAGE_DIR);
|
|
10195
10936
|
}
|
|
10196
10937
|
function createPrivateTriageTracker(repoRoot, options = {}) {
|
|
10197
10938
|
return createLocalTracker({
|
|
@@ -10768,6 +11509,13 @@ async function start(args, repoRoot) {
|
|
|
10768
11509
|
args
|
|
10769
11510
|
});
|
|
10770
11511
|
guardRunBranch(planStub2, branch, args);
|
|
11512
|
+
await announceRiskTotal({
|
|
11513
|
+
repoRoot,
|
|
11514
|
+
home: homedir7(),
|
|
11515
|
+
planPath,
|
|
11516
|
+
config: runConfig.config,
|
|
11517
|
+
environment: process.env
|
|
11518
|
+
});
|
|
10771
11519
|
await requireNoticesAnswered();
|
|
10772
11520
|
setActivePlanStub(planStub2);
|
|
10773
11521
|
const session = openRunSession({ repoRoot, planPath, planStub: planStub2, branch });
|