rl-workspaces 0.5.0 → 0.7.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 +234 -193
- package/bin/rl +2 -54
- package/dist/agent-launch.js +1 -1
- package/dist/agent-presets.js +42 -17
- package/dist/agent-resume.js +2 -2
- package/dist/agent-runner.js +20 -0
- package/dist/agent-workflows.js +60 -16
- package/dist/automation-config.js +144 -5
- package/dist/bootstrap.js +74 -0
- package/dist/cli.js +2 -0
- package/dist/command-context.js +1 -1
- package/dist/common.js +3 -2
- package/dist/dependencies.js +197 -0
- package/dist/doctor.js +1 -1
- package/dist/git-workflow.js +8 -102
- package/dist/help.js +113 -116
- package/dist/hooks.js +1 -1
- package/dist/install.js +143 -0
- package/dist/mcp-setup.js +171 -0
- package/dist/mcp.js +13 -16
- package/dist/picker.js +24 -7
- package/dist/pr-workflow.js +7 -28
- package/dist/repos.js +5 -1
- package/dist/session-workflows.js +122 -18
- package/dist/session.js +1 -1
- package/dist/state.js +4 -14
- package/dist/storage.js +2 -2
- package/dist/store.js +2 -2
- package/dist/summary.js +1 -1
- package/dist/workflow-collaboration.js +531 -0
- package/dist/workflow.js +24 -38
- package/lib/entry.zsh +54 -0
- package/lib/rl.zsh +37 -9
- package/node_modules/proper-lockfile/CHANGELOG.md +108 -0
- package/node_modules/smol-toml/LICENSE +24 -0
- package/node_modules/smol-toml/README.md +605 -0
- package/node_modules/smol-toml/dist/date.d.ts +41 -0
- package/node_modules/smol-toml/dist/date.js +156 -0
- package/node_modules/smol-toml/dist/error.d.ts +37 -0
- package/node_modules/smol-toml/dist/error.js +67 -0
- package/node_modules/smol-toml/dist/extract.js +293 -0
- package/node_modules/smol-toml/dist/index.cjs +771 -0
- package/node_modules/smol-toml/dist/index.d.ts +44 -0
- package/node_modules/smol-toml/dist/index.js +34 -0
- package/node_modules/smol-toml/dist/parse.d.ts +73 -0
- package/node_modules/smol-toml/dist/parse.js +164 -0
- package/node_modules/smol-toml/dist/primitive.js +151 -0
- package/node_modules/smol-toml/dist/stringify.d.ts +32 -0
- package/node_modules/smol-toml/dist/stringify.js +219 -0
- package/node_modules/smol-toml/dist/struct.js +156 -0
- package/node_modules/smol-toml/dist/util.d.ts +43 -0
- package/node_modules/smol-toml/dist/util.js +57 -0
- package/node_modules/smol-toml/package.json +68 -0
- package/package.json +6 -3
- package/shell/rl.zsh +1 -1
package/dist/help.js
CHANGED
|
@@ -21,7 +21,7 @@ const topics = {
|
|
|
21
21
|
options: [
|
|
22
22
|
"-c, --create Create a new branch workspace",
|
|
23
23
|
"-b, --base <ref> Creation base (default: origin/test unless configured)",
|
|
24
|
-
"--no-fetch Use cached refs when creating",
|
|
24
|
+
"--no-fetch Use cached refs when opening or creating",
|
|
25
25
|
"--setup Run configured project setup",
|
|
26
26
|
"--agent codex|claude Start an agent after switching",
|
|
27
27
|
"-- <agent args> Forward options to the selected agent",
|
|
@@ -30,13 +30,13 @@ const topics = {
|
|
|
30
30
|
"Use rl checkout [repository] to switch repositories in your shell.",
|
|
31
31
|
"@ opens the main checkout; - returns to the previous workspace.",
|
|
32
32
|
"Shell integration changes your terminal's directory.",
|
|
33
|
-
"
|
|
33
|
+
"An existing branch without a checkout gets a managed workspace; origin branches are tracked.",
|
|
34
34
|
],
|
|
35
35
|
examples: [
|
|
36
36
|
"rl switch feature/login",
|
|
37
37
|
"rl switch feature/login -c --base origin/test",
|
|
38
38
|
],
|
|
39
|
-
related: ["
|
|
39
|
+
related: ["path"],
|
|
40
40
|
},
|
|
41
41
|
list: {
|
|
42
42
|
group: "Workspaces",
|
|
@@ -64,9 +64,9 @@ const topics = {
|
|
|
64
64
|
notes: [
|
|
65
65
|
"Shows repository selection, checkout path, base, workspaces and saved session counts.",
|
|
66
66
|
"--workspace includes Git changes, PR, sessions, task handoff and latest workflow.",
|
|
67
|
-
"Use rl list for workspace overviews
|
|
67
|
+
"Use rl list for workspace overviews and --include-deleted for archived records.",
|
|
68
68
|
],
|
|
69
|
-
related: ["list"
|
|
69
|
+
related: ["list"],
|
|
70
70
|
},
|
|
71
71
|
delete: {
|
|
72
72
|
group: "Workspaces",
|
|
@@ -122,6 +122,9 @@ const topics = {
|
|
|
122
122
|
description: "Configure native agent roles and saved sessions",
|
|
123
123
|
usage: "rl agent <command>",
|
|
124
124
|
related: ["resume", "workflow", "policy"],
|
|
125
|
+
notes: [
|
|
126
|
+
"Existing agent sessions discover their workflow with rl workflow context --json. A special launcher or MCP connection is not required.",
|
|
127
|
+
],
|
|
125
128
|
},
|
|
126
129
|
"agent start": {
|
|
127
130
|
description: "Start an interactive agent",
|
|
@@ -149,6 +152,9 @@ const topics = {
|
|
|
149
152
|
"agent set": {
|
|
150
153
|
description: "Save an agent profile",
|
|
151
154
|
usage: "rl agent set <name> --file <profile.json|->",
|
|
155
|
+
notes: [
|
|
156
|
+
"Use --instructions <file|-> to change instructions while preserving the profile's access, model and other settings.",
|
|
157
|
+
],
|
|
152
158
|
},
|
|
153
159
|
"agent show": {
|
|
154
160
|
description: "Inspect a workflow agent profile",
|
|
@@ -177,8 +183,11 @@ const topics = {
|
|
|
177
183
|
},
|
|
178
184
|
"agent mcp": {
|
|
179
185
|
description: "Serve workspace context and notes through MCP",
|
|
180
|
-
usage: "rl agent mcp",
|
|
181
|
-
notes: [
|
|
186
|
+
usage: "rl agent mcp [--workspace <absolute-path>]",
|
|
187
|
+
notes: [
|
|
188
|
+
"Uses stdio. Start the server in the adopted workspace, or bind one explicitly with --workspace.",
|
|
189
|
+
],
|
|
190
|
+
related: ["setup mcp"],
|
|
182
191
|
},
|
|
183
192
|
workflow: {
|
|
184
193
|
group: "Agents and tasks",
|
|
@@ -196,7 +205,7 @@ const topics = {
|
|
|
196
205
|
notes: [
|
|
197
206
|
"Creates .rl/agents.json, .rl/policies.json and .rl/agent-workflows.json.",
|
|
198
207
|
"Existing configuration is never overwritten.",
|
|
199
|
-
"
|
|
208
|
+
"Adopted workspaces use built-in writer/reviewer/DRY roles immediately. Init writes editable copies of their profiles, policy and workflow.",
|
|
200
209
|
],
|
|
201
210
|
},
|
|
202
211
|
"workflow list": {
|
|
@@ -220,22 +229,25 @@ const topics = {
|
|
|
220
229
|
"--request <UUID> Start a queued workflow request instead of choosing a name",
|
|
221
230
|
],
|
|
222
231
|
notes: [
|
|
223
|
-
"Default workflow:
|
|
232
|
+
"Default workflow: writer-reviewer-dry. The parent spawns all three native peers in parallel and binds their handles; RL never launches agent processes.",
|
|
224
233
|
"The parent claims ready steps, spawns native subagents and reports evidence. RL applies the policy; provider usage is parent-reported.",
|
|
225
234
|
],
|
|
226
235
|
examples: ["rl workflow run --base origin/test --dry-run"],
|
|
227
236
|
},
|
|
228
237
|
"workflow add": {
|
|
229
238
|
description: "Add a workflow with dependencies and spawn triggers",
|
|
230
|
-
usage: "rl workflow add <name> --file <workflow.json|->",
|
|
239
|
+
usage: "rl workflow add <name> --file <workflow.json|-> [--default]",
|
|
231
240
|
},
|
|
232
241
|
"workflow set": {
|
|
233
242
|
description: "Save a workflow definition and trigger rules",
|
|
234
|
-
usage: "rl workflow set <name> --file <workflow.json|->",
|
|
243
|
+
usage: "rl workflow set <name> --file <workflow.json|-> [--default]",
|
|
235
244
|
},
|
|
236
245
|
"workflow context": {
|
|
237
246
|
description: "Discover profiles, policies, workflows and trigger rules",
|
|
238
247
|
usage: "rl workflow context [--json]",
|
|
248
|
+
notes: [
|
|
249
|
+
"For an existing agent session: --json includes profiles, policies, workflows, and protocol.tools with every CLI command and input schema. Use your normal shell tool; MCP and rl agent start are optional.",
|
|
250
|
+
],
|
|
239
251
|
},
|
|
240
252
|
"workflow pending": {
|
|
241
253
|
description: "Read queued workflows for the parent session",
|
|
@@ -257,6 +269,30 @@ const topics = {
|
|
|
257
269
|
description: "Dismiss an unavailable workflow with a reason",
|
|
258
270
|
usage: "rl workflow dismiss --file <dismissal.json|-> [--agent codex|claude] [--session <UUID>]",
|
|
259
271
|
},
|
|
272
|
+
"workflow bind": {
|
|
273
|
+
description: "Bind a role to the native peer spawned by this session",
|
|
274
|
+
usage: "rl workflow bind <run> --file <binding.json|-> [--agent codex|claude] [--session <UUID>]",
|
|
275
|
+
},
|
|
276
|
+
"workflow inbox": {
|
|
277
|
+
description: "Read a peer's requests, review checkpoints and ratings",
|
|
278
|
+
usage: "rl workflow inbox <run> --step <step> --claim <token> [--agent codex|claude] [--session <UUID>]",
|
|
279
|
+
},
|
|
280
|
+
"workflow consult": {
|
|
281
|
+
description: "Ask an existing peer through a configured workflow route",
|
|
282
|
+
usage: "rl workflow consult <run> --file <consultation.json|-> [--agent codex|claude] [--session <UUID>]",
|
|
283
|
+
},
|
|
284
|
+
"workflow answer": {
|
|
285
|
+
description: "Return reuse or duplication evidence to a requesting peer",
|
|
286
|
+
usage: "rl workflow answer <run> --file <answer.json|-> [--agent codex|claude] [--session <UUID>]",
|
|
287
|
+
},
|
|
288
|
+
"workflow submit": {
|
|
289
|
+
description: "Submit writer work to the existing reviewer",
|
|
290
|
+
usage: "rl workflow submit <run> --file <submission.json|-> [--agent codex|claude] [--session <UUID>]",
|
|
291
|
+
},
|
|
292
|
+
"workflow review": {
|
|
293
|
+
description: "Return a rating and evidence for a stable writer checkpoint",
|
|
294
|
+
usage: "rl workflow review <run> --file <review.json|-> [--agent codex|claude] [--session <UUID>]",
|
|
295
|
+
},
|
|
260
296
|
"workflow status": {
|
|
261
297
|
description: "Inspect a saved run",
|
|
262
298
|
usage: "rl workflow status [UUID|latest] [--json]",
|
|
@@ -338,33 +374,9 @@ const topics = {
|
|
|
338
374
|
},
|
|
339
375
|
git: {
|
|
340
376
|
group: "Code and reviews",
|
|
341
|
-
description: "
|
|
377
|
+
description: "Integrate changes across workspaces",
|
|
342
378
|
usage: "rl git <command>",
|
|
343
379
|
},
|
|
344
|
-
"git diff": {
|
|
345
|
-
description: "Show workspace changes against the base",
|
|
346
|
-
usage: "rl git diff [--base <ref>] [-- <display options> [-- paths...]]",
|
|
347
|
-
examples: ["rl git diff -- --stat"],
|
|
348
|
-
},
|
|
349
|
-
"git log": {
|
|
350
|
-
description: "Show feature commits",
|
|
351
|
-
usage: "rl git log [--base <ref>] [-- <display options>]",
|
|
352
|
-
examples: ["rl git log -- --graph"],
|
|
353
|
-
},
|
|
354
|
-
"git commit": {
|
|
355
|
-
description: "Commit staged changes",
|
|
356
|
-
usage: "rl git commit -m <message> [--all] [--dry-run]",
|
|
357
|
-
notes: ["--all includes modified tracked files."],
|
|
358
|
-
},
|
|
359
|
-
"git rebase": {
|
|
360
|
-
description: "Rebase onto the base branch",
|
|
361
|
-
usage: "rl git rebase [--base <ref>] [--dry-run]",
|
|
362
|
-
},
|
|
363
|
-
"git squash": {
|
|
364
|
-
description: "Squash feature commits",
|
|
365
|
-
usage: "rl git squash -m <message> [--base <ref>] [--dry-run]",
|
|
366
|
-
notes: ["The original HEAD is retained under a printed backup ref."],
|
|
367
|
-
},
|
|
368
380
|
"git merge": {
|
|
369
381
|
description: "Integrate into a checked-out base branch",
|
|
370
382
|
usage: "rl git merge [target] [--squash -m <message>] [--cleanup] [--dry-run]",
|
|
@@ -394,10 +406,6 @@ const topics = {
|
|
|
394
406
|
description: "Refresh the saved PR snapshot",
|
|
395
407
|
usage: "rl pr sync [number|URL] [--json]",
|
|
396
408
|
},
|
|
397
|
-
"pr checks": {
|
|
398
|
-
description: "Show GitHub check results",
|
|
399
|
-
usage: "rl pr checks [--json]",
|
|
400
|
-
},
|
|
401
409
|
"pr checkout": {
|
|
402
410
|
description: "Create a workspace for a PR",
|
|
403
411
|
usage: "rl pr checkout <number> [--name <branch>]",
|
|
@@ -510,6 +518,34 @@ const topics = {
|
|
|
510
518
|
usage: "rl setup adopt [--extended-hooks]",
|
|
511
519
|
notes: ["Preserves existing hooks. Review Codex hook trust with /hooks."],
|
|
512
520
|
},
|
|
521
|
+
"setup mcp": {
|
|
522
|
+
description: "Connect this workspace's RL MCP server to your agent",
|
|
523
|
+
usage: "rl setup mcp [codex|claude] [--dry-run] [--json]",
|
|
524
|
+
notes: [
|
|
525
|
+
"Configures both clients by default: .codex/config.toml and .mcp.json in this adopted workspace.",
|
|
526
|
+
"Preserves other settings and servers. Existing unmanaged entries named rl require manual resolution.",
|
|
527
|
+
"Launches the installed Node/RL directly, with this workspace and its RL state bound explicitly. Rerun after moving the installation.",
|
|
528
|
+
"Reload your client and review its normal project trust/MCP approval. Your existing session creates native subagents.",
|
|
529
|
+
],
|
|
530
|
+
},
|
|
531
|
+
"setup dependencies": {
|
|
532
|
+
description: "Install or check Git, zsh, fzf and GitHub CLI",
|
|
533
|
+
usage: "rl setup dependencies [--check | --dry-run] [--json]",
|
|
534
|
+
notes: [
|
|
535
|
+
"npm installation installs missing tools through an existing package manager. This command repairs skipped or failed dependency setup.",
|
|
536
|
+
"Uses Homebrew on macOS, and Homebrew, APT, DNF, Yum, Pacman or APK on Linux. Administrator access may be required on Linux.",
|
|
537
|
+
"Runs directly with Node, so it also works before zsh or a repository is available.",
|
|
538
|
+
],
|
|
539
|
+
},
|
|
540
|
+
"setup install": {
|
|
541
|
+
description: "Complete or repair RL installation and shell integration",
|
|
542
|
+
usage: "rl setup install [--shell zsh|bash]",
|
|
543
|
+
notes: [
|
|
544
|
+
"Global npm installation runs this automatically: missing Git, zsh, fzf and gh are installed, then your shell startup file is updated.",
|
|
545
|
+
"Preserves your settings and updates one RL-owned block. New terminals load RL automatically; reload the installing terminal once.",
|
|
546
|
+
"Use this command after an install with lifecycle scripts disabled, or to repair setup.",
|
|
547
|
+
],
|
|
548
|
+
},
|
|
513
549
|
"setup shell": {
|
|
514
550
|
description: "Print shell integration",
|
|
515
551
|
usage: "rl setup shell <zsh|bash>",
|
|
@@ -524,16 +560,6 @@ const topics = {
|
|
|
524
560
|
"--repair reinstalls this workspace's RL hooks.",
|
|
525
561
|
],
|
|
526
562
|
},
|
|
527
|
-
new: {
|
|
528
|
-
description: "Create or reopen a branch workspace",
|
|
529
|
-
usage: "rl new <branch> [--base <ref>] [--no-fetch] [--setup]",
|
|
530
|
-
related: ["switch"],
|
|
531
|
-
},
|
|
532
|
-
open: {
|
|
533
|
-
description: "Open an existing local or origin branch",
|
|
534
|
-
usage: "rl open --branch <branch> [--setup] [--no-fetch]",
|
|
535
|
-
related: ["switch"],
|
|
536
|
-
},
|
|
537
563
|
path: {
|
|
538
564
|
description: "Print a workspace path",
|
|
539
565
|
usage: "rl path [name|branch|@]",
|
|
@@ -544,55 +570,7 @@ const topics = {
|
|
|
544
570
|
usage: "rl exec [--workspace <name>] -- <command> [args...]",
|
|
545
571
|
related: ["setup run"],
|
|
546
572
|
},
|
|
547
|
-
instances: {
|
|
548
|
-
description: "Inspect active and archived instance records",
|
|
549
|
-
usage: "rl instances",
|
|
550
|
-
related: ["status", "list"],
|
|
551
|
-
},
|
|
552
573
|
};
|
|
553
|
-
const aliases = [
|
|
554
|
-
[["step"], ["git"]],
|
|
555
|
-
[["diff"], ["git", "diff"]],
|
|
556
|
-
[["log"], ["git", "log"]],
|
|
557
|
-
[["merge"], ["git", "merge"]],
|
|
558
|
-
[["clean"], ["storage", "clean"]],
|
|
559
|
-
[["summary"], ["notes", "summary"]],
|
|
560
|
-
[["handoff"], ["agent", "handoff"]],
|
|
561
|
-
[["mcp"], ["agent", "mcp"]],
|
|
562
|
-
[
|
|
563
|
-
["session", "list"],
|
|
564
|
-
["agent", "sessions"],
|
|
565
|
-
],
|
|
566
|
-
[
|
|
567
|
-
["session", "save"],
|
|
568
|
-
["agent", "save"],
|
|
569
|
-
],
|
|
570
|
-
[
|
|
571
|
-
["session", "add"],
|
|
572
|
-
["agent", "save"],
|
|
573
|
-
],
|
|
574
|
-
[["session"], ["agent"]],
|
|
575
|
-
[
|
|
576
|
-
["config", "init"],
|
|
577
|
-
["setup", "init"],
|
|
578
|
-
],
|
|
579
|
-
[
|
|
580
|
-
["config", "show"],
|
|
581
|
-
["setup", "show"],
|
|
582
|
-
],
|
|
583
|
-
[["config"], ["setup"]],
|
|
584
|
-
[["adopt"], ["setup", "adopt"]],
|
|
585
|
-
[["init"], ["setup", "shell"]],
|
|
586
|
-
];
|
|
587
|
-
function canonical(words) {
|
|
588
|
-
if (words[0] === "status" &&
|
|
589
|
-
words.some((word) => word === "--all" || word === "--list"))
|
|
590
|
-
return ["list", ...words.slice(1)];
|
|
591
|
-
if (["context", "progress"].includes(words[0]))
|
|
592
|
-
return ["notes", words[1] ?? "show", words[0], ...words.slice(2)];
|
|
593
|
-
const alias = aliases.find(([from]) => from.every((word, i) => words[i] === word));
|
|
594
|
-
return alias ? [...alias[1], ...words.slice(alias[0].length)] : words;
|
|
595
|
-
}
|
|
596
574
|
const children = (name) => Object.entries(topics).filter(([key]) => key.startsWith(name + " ") &&
|
|
597
575
|
key.split(" ").length === name.split(" ").length + 1);
|
|
598
576
|
const safe = (value) => stripVTControlCharacters(value).replace(/[\x00-\x1f\x7f-\x9f]/g, " ");
|
|
@@ -625,9 +603,8 @@ function match(words) {
|
|
|
625
603
|
export function topicHelp(words) {
|
|
626
604
|
if (!words.length)
|
|
627
605
|
return mainHelp();
|
|
628
|
-
const
|
|
629
|
-
if (!name ||
|
|
630
|
-
(children(name).length && normalized.length > name.split(" ").length))
|
|
606
|
+
const name = match(words);
|
|
607
|
+
if (!name || (children(name).length && words.length > name.split(" ").length))
|
|
631
608
|
throw new Error(`Unknown help topic: ${safe(words.join(" "))}`);
|
|
632
609
|
const topic = topics[name];
|
|
633
610
|
const lines = [topic.description, "", `Usage: ${topic.usage}`];
|
|
@@ -666,28 +643,48 @@ export function invocation(module, argv) {
|
|
|
666
643
|
while (argv[index]?.startsWith("--") && CONTEXT.has(argv[index].slice(2)))
|
|
667
644
|
index += 2;
|
|
668
645
|
const rest = argv.slice(index);
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
646
|
+
if (module === "summary")
|
|
647
|
+
return ["notes", "summary", ...rest];
|
|
648
|
+
if (module === "mcp")
|
|
649
|
+
return ["agent", "mcp", ...rest];
|
|
650
|
+
if (module === "mcp-setup")
|
|
651
|
+
return ["setup", "mcp", ...rest];
|
|
652
|
+
if (module === "delete")
|
|
653
|
+
return ["delete", ...rest];
|
|
654
|
+
if (module === "picker")
|
|
655
|
+
return [];
|
|
656
|
+
if (module === "agent")
|
|
657
|
+
return ["agent", ...rest];
|
|
658
|
+
if (module === "git")
|
|
659
|
+
return ["git", ...rest];
|
|
660
|
+
if (module === "storage")
|
|
661
|
+
return rest[0] === "clean" ? ["storage", ...rest] : rest;
|
|
662
|
+
if (module === "workflow" && rest[0] === "config")
|
|
663
|
+
return ["setup", ...rest.slice(1)];
|
|
664
|
+
if (module === "state") {
|
|
665
|
+
if (rest[0] === "session")
|
|
666
|
+
return [
|
|
667
|
+
"agent",
|
|
668
|
+
rest[1] === "list" ? "sessions" : "save",
|
|
669
|
+
...rest.slice(2),
|
|
670
|
+
];
|
|
671
|
+
if (rest[0] === "adopt")
|
|
672
|
+
return ["setup", ...rest];
|
|
673
|
+
if (["context", "progress"].includes(rest[0]))
|
|
674
|
+
return ["notes", rest[1] ?? "show", rest[0], ...rest.slice(2)];
|
|
675
|
+
}
|
|
676
|
+
if (module === "repos" &&
|
|
677
|
+
rest[0] === "status" &&
|
|
678
|
+
rest.some((word) => word === "--all" || word === "--list"))
|
|
679
|
+
return ["list", ...rest.slice(1)];
|
|
680
680
|
const defaults = {
|
|
681
681
|
state: "status",
|
|
682
682
|
repos: "repo",
|
|
683
683
|
workflow: "switch",
|
|
684
|
-
git: "git",
|
|
685
|
-
agent: "agent",
|
|
686
684
|
automation: "workflow",
|
|
687
|
-
storage: "storage",
|
|
688
685
|
"pr-update": "pr",
|
|
689
686
|
};
|
|
690
|
-
return
|
|
687
|
+
return rest.length ? rest : defaults[module] ? [defaults[module]] : [];
|
|
691
688
|
}
|
|
692
689
|
export function usageHint(module, argv, details = false) {
|
|
693
690
|
const words = invocation(module, argv), name = match(words);
|
package/dist/hooks.js
CHANGED
|
@@ -21,7 +21,7 @@ export async function handleHook(store, key, name) {
|
|
|
21
21
|
// cannot change the original instance or the destination instance.
|
|
22
22
|
const [file, data] = store.instanceForWorktree(root, git(root, "branch", "--show-current"));
|
|
23
23
|
if (data.key !== key)
|
|
24
|
-
throw new Error("Hook belongs to a different RL instance; run 'rl adopt' to repair it");
|
|
24
|
+
throw new Error("Hook belongs to a different RL instance; run 'rl setup adopt' to repair it");
|
|
25
25
|
try {
|
|
26
26
|
const event = adapter.parse(payload);
|
|
27
27
|
// Workflow workers are ephemeral. Supply task context without turning a
|
package/dist/install.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { parseArgs } from "node:util";
|
|
6
|
+
import { atomicWrite, canonical, inside, shellQuote } from "./common.js";
|
|
7
|
+
import { inspectDependencies, setupDependencies } from "./dependencies.js";
|
|
8
|
+
const BEGIN = "# >>> rl shell integration >>>";
|
|
9
|
+
const END = "# <<< rl shell integration <<<";
|
|
10
|
+
const SHIM = "#!/bin/sh\n# Managed by RL installation\n";
|
|
11
|
+
function profileChange(home, requested, after) {
|
|
12
|
+
const file = canonical(requested);
|
|
13
|
+
if (!inside(file, home))
|
|
14
|
+
throw new Error(`Shell setup path must stay in your home directory: ${requested}`);
|
|
15
|
+
const stat = fs.statSync(file, { throwIfNoEntry: false });
|
|
16
|
+
if (stat && (!stat.isFile() || stat.size > 1024 * 1024))
|
|
17
|
+
throw new Error(`Expected a shell setup file up to 1 MiB: ${requested}`);
|
|
18
|
+
const before = stat ? fs.readFileSync(file, "utf8") : undefined;
|
|
19
|
+
return {
|
|
20
|
+
file,
|
|
21
|
+
before,
|
|
22
|
+
after: after(before ?? ""),
|
|
23
|
+
...(stat ? { mode: stat.mode & 0o777 } : {}),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function integration(text, block, shell) {
|
|
27
|
+
const lines = text.split(/\r?\n/);
|
|
28
|
+
const starts = lines.flatMap((line, index) => line === BEGIN ? [index] : []);
|
|
29
|
+
const ends = lines.flatMap((line, index) => (line === END ? [index] : []));
|
|
30
|
+
if (starts.length || ends.length) {
|
|
31
|
+
if (starts.length !== 1 || ends.length !== 1 || starts[0] >= ends[0])
|
|
32
|
+
throw new Error("Malformed RL shell integration block; preserve your settings and repair its markers before reinstalling");
|
|
33
|
+
const prefix = lines.slice(0, starts[0]).join("\n");
|
|
34
|
+
const suffix = lines.slice(ends[0] + 1).join("\n");
|
|
35
|
+
return (prefix ? prefix + "\n" : "") + block + "\n" + suffix;
|
|
36
|
+
}
|
|
37
|
+
const existing = lines.findIndex((line) => [
|
|
38
|
+
`eval "$(rl setup shell ${shell})"`,
|
|
39
|
+
`eval "$(command rl setup shell ${shell})"`,
|
|
40
|
+
`eval "$(rl init ${shell})"`,
|
|
41
|
+
`eval "$(command rl init ${shell})"`,
|
|
42
|
+
].includes(line.trim()));
|
|
43
|
+
if (existing >= 0) {
|
|
44
|
+
lines[existing] = block;
|
|
45
|
+
return lines.join("\n");
|
|
46
|
+
}
|
|
47
|
+
return (text + (text ? (text.endsWith("\n") ? "\n" : "\n\n") : "") + block + "\n");
|
|
48
|
+
}
|
|
49
|
+
export function configureShell(host, requestedShell) {
|
|
50
|
+
const env = host.env ?? process.env;
|
|
51
|
+
const home = canonical(host.home ?? os.homedir());
|
|
52
|
+
const shell = requestedShell ??
|
|
53
|
+
path.basename(env.SHELL ??
|
|
54
|
+
((host.platform ?? process.platform) === "darwin" ? "zsh" : "bash"));
|
|
55
|
+
if (shell !== "zsh" && shell !== "bash")
|
|
56
|
+
throw new Error(`Automatic RL shell setup supports zsh and bash. Run 'rl setup install --shell zsh' or '--shell bash' for the shell you use.`);
|
|
57
|
+
const root = host.packageRoot ?? fileURLToPath(new URL("..", import.meta.url));
|
|
58
|
+
const node = host.node ?? process.execPath;
|
|
59
|
+
const executable = path.join(root, "bin/rl");
|
|
60
|
+
const shimDirectory = path.join(home, ".local/share/rl/bin");
|
|
61
|
+
const shim = profileChange(home, path.join(shimDirectory, "rl"), (before) => {
|
|
62
|
+
if (before && !before.startsWith(SHIM))
|
|
63
|
+
throw new Error("The RL installation shim already contains an unmanaged command; it was not replaced");
|
|
64
|
+
return `${SHIM}exec ${shellQuote(node)} ${shellQuote(executable)} "$@"\n`;
|
|
65
|
+
});
|
|
66
|
+
shim.mode = 0o755;
|
|
67
|
+
const toolDirectories = [
|
|
68
|
+
...new Set([
|
|
69
|
+
path.dirname(node),
|
|
70
|
+
...inspectDependencies(env).flatMap((tool) => tool.path ? [path.dirname(tool.path)] : []),
|
|
71
|
+
]),
|
|
72
|
+
];
|
|
73
|
+
const block = [
|
|
74
|
+
BEGIN,
|
|
75
|
+
`export PATH=${shellQuote(shimDirectory)}:"$PATH":${toolDirectories.map(shellQuote).join(":")}`,
|
|
76
|
+
`eval "$(${shellQuote(node)} ${shellQuote(executable)} setup shell ${shell})"`,
|
|
77
|
+
END,
|
|
78
|
+
].join("\n");
|
|
79
|
+
const targets = [
|
|
80
|
+
path.join(shell === "zsh" && env.ZDOTDIR ? path.resolve(home, env.ZDOTDIR) : home, shell === "zsh" ? ".zshrc" : ".bashrc"),
|
|
81
|
+
];
|
|
82
|
+
if (shell === "bash") {
|
|
83
|
+
const login = [".bash_profile", ".bash_login", ".profile"]
|
|
84
|
+
.map((name) => path.join(home, name))
|
|
85
|
+
.find((file) => fs.existsSync(file));
|
|
86
|
+
targets.push(login ?? path.join(home, ".bash_profile"));
|
|
87
|
+
}
|
|
88
|
+
const profiles = targets.map((file) => profileChange(home, file, (text) => integration(text, block, shell)));
|
|
89
|
+
const changes = [
|
|
90
|
+
...new Map([shim, ...profiles].map((change) => [change.file, change])).values(),
|
|
91
|
+
];
|
|
92
|
+
for (const change of changes) {
|
|
93
|
+
const current = fs.existsSync(change.file)
|
|
94
|
+
? fs.readFileSync(change.file, "utf8")
|
|
95
|
+
: undefined;
|
|
96
|
+
if (current !== change.before)
|
|
97
|
+
throw new Error("Shell configuration changed during installation; retry");
|
|
98
|
+
}
|
|
99
|
+
for (const change of changes) {
|
|
100
|
+
if (change.before !== change.after)
|
|
101
|
+
atomicWrite(change.file, change.after);
|
|
102
|
+
if (change.mode !== undefined)
|
|
103
|
+
fs.chmodSync(change.file, change.mode);
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
shell,
|
|
107
|
+
profiles: profiles.map((change) => change.file),
|
|
108
|
+
shim: shim.file,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
export async function installRl(argv, host = {}) {
|
|
112
|
+
const { values, positionals } = parseArgs({
|
|
113
|
+
args: argv,
|
|
114
|
+
options: { shell: { type: "string" } },
|
|
115
|
+
allowPositionals: true,
|
|
116
|
+
});
|
|
117
|
+
if (positionals.length)
|
|
118
|
+
throw new Error("Usage: rl setup install [--shell zsh|bash]");
|
|
119
|
+
const env = host.env ?? process.env;
|
|
120
|
+
if (env.npm_lifecycle_event === "postinstall" &&
|
|
121
|
+
env.npm_config_global !== "true") {
|
|
122
|
+
console.log("RL checkout installed. Run 'rl setup install' to install system tools and register shell integration.");
|
|
123
|
+
return 0;
|
|
124
|
+
}
|
|
125
|
+
const code = await setupDependencies([], host);
|
|
126
|
+
if (code)
|
|
127
|
+
return code;
|
|
128
|
+
const configured = configureShell(host, values.shell);
|
|
129
|
+
console.log(`RL shell integration installed in ${configured.profiles.join(", ")}.`);
|
|
130
|
+
console.log("New terminals can use rl directly. To activate directory switching in this terminal now, run:");
|
|
131
|
+
console.log(` eval "$(${shellQuote(configured.shim)} setup shell ${configured.shell})"`);
|
|
132
|
+
return 0;
|
|
133
|
+
}
|
|
134
|
+
if (process.argv[1] &&
|
|
135
|
+
path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
136
|
+
try {
|
|
137
|
+
process.exitCode = await installRl(process.argv.slice(2));
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
console.error(`RL installation: ${error instanceof Error ? error.message : String(error)}`);
|
|
141
|
+
process.exitCode = 1;
|
|
142
|
+
}
|
|
143
|
+
}
|