rl-workspaces 0.0.0-stage → 0.5.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.
Files changed (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +703 -2
  3. package/bin/rl +54 -0
  4. package/config.example.zsh +9 -0
  5. package/dist/access.js +40 -0
  6. package/dist/activity.js +90 -0
  7. package/dist/agent-claude.js +16 -0
  8. package/dist/agent-codex.js +22 -0
  9. package/dist/agent-launch.js +74 -0
  10. package/dist/agent-presets.js +45 -0
  11. package/dist/agent-registry.js +8 -0
  12. package/dist/agent-resume.js +96 -0
  13. package/dist/agent-runner.js +366 -0
  14. package/dist/agent-workflows.js +231 -0
  15. package/dist/agents.js +73 -0
  16. package/dist/automation-config.js +242 -0
  17. package/dist/base.js +66 -0
  18. package/dist/cleanup-path.js +33 -0
  19. package/dist/cli.js +57 -0
  20. package/dist/codex-sessions.js +188 -0
  21. package/dist/command-context.js +67 -0
  22. package/dist/common.js +262 -0
  23. package/dist/delete.js +113 -0
  24. package/dist/display.js +134 -0
  25. package/dist/doctor.js +145 -0
  26. package/dist/git-safety.js +24 -0
  27. package/dist/git-workflow.js +183 -0
  28. package/dist/help.js +726 -0
  29. package/dist/hooks.js +159 -0
  30. package/dist/json.js +34 -0
  31. package/dist/mcp.js +336 -0
  32. package/dist/picker.js +182 -0
  33. package/dist/pr-discovery.js +223 -0
  34. package/dist/pr-update.js +419 -0
  35. package/dist/pr-workflow.js +161 -0
  36. package/dist/repos.js +537 -0
  37. package/dist/review-policy.js +150 -0
  38. package/dist/session-workflows.js +616 -0
  39. package/dist/session.js +12 -0
  40. package/dist/state.js +250 -0
  41. package/dist/storage.js +366 -0
  42. package/dist/store.js +379 -0
  43. package/dist/summary.js +164 -0
  44. package/dist/task-state.js +65 -0
  45. package/dist/task.js +192 -0
  46. package/dist/templates.js +4 -0
  47. package/dist/workflow-config.js +177 -0
  48. package/dist/workflow.js +211 -0
  49. package/lib/rl.zsh +330 -0
  50. package/node_modules/graceful-fs/LICENSE +15 -0
  51. package/node_modules/graceful-fs/README.md +143 -0
  52. package/node_modules/graceful-fs/clone.js +23 -0
  53. package/node_modules/graceful-fs/graceful-fs.js +448 -0
  54. package/node_modules/graceful-fs/legacy-streams.js +118 -0
  55. package/node_modules/graceful-fs/package.json +53 -0
  56. package/node_modules/graceful-fs/polyfills.js +355 -0
  57. package/node_modules/proper-lockfile/LICENSE +21 -0
  58. package/node_modules/proper-lockfile/README.md +183 -0
  59. package/node_modules/proper-lockfile/index.js +40 -0
  60. package/node_modules/proper-lockfile/lib/adapter.js +85 -0
  61. package/node_modules/proper-lockfile/lib/lockfile.js +342 -0
  62. package/node_modules/proper-lockfile/lib/mtime-precision.js +55 -0
  63. package/node_modules/proper-lockfile/package.json +71 -0
  64. package/node_modules/retry/.npmignore +3 -0
  65. package/node_modules/retry/.travis.yml +15 -0
  66. package/node_modules/retry/License +21 -0
  67. package/node_modules/retry/Makefile +18 -0
  68. package/node_modules/retry/README.md +227 -0
  69. package/node_modules/retry/equation.gif +0 -0
  70. package/node_modules/retry/example/dns.js +31 -0
  71. package/node_modules/retry/example/stop.js +40 -0
  72. package/node_modules/retry/index.js +1 -0
  73. package/node_modules/retry/lib/retry.js +100 -0
  74. package/node_modules/retry/lib/retry_operation.js +158 -0
  75. package/node_modules/retry/package.json +32 -0
  76. package/node_modules/retry/test/common.js +10 -0
  77. package/node_modules/retry/test/integration/test-forever.js +24 -0
  78. package/node_modules/retry/test/integration/test-retry-operation.js +258 -0
  79. package/node_modules/retry/test/integration/test-retry-wrap.js +101 -0
  80. package/node_modules/retry/test/integration/test-timeouts.js +69 -0
  81. package/node_modules/signal-exit/LICENSE.txt +16 -0
  82. package/node_modules/signal-exit/README.md +39 -0
  83. package/node_modules/signal-exit/index.js +202 -0
  84. package/node_modules/signal-exit/package.json +38 -0
  85. package/node_modules/signal-exit/signals.js +53 -0
  86. package/package.json +57 -4
  87. package/shell/rl.zsh +11 -0
package/dist/help.js ADDED
@@ -0,0 +1,726 @@
1
+ import path from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ import { stripVTControlCharacters } from "node:util";
4
+ const topics = {
5
+ checkout: {
6
+ group: "Workspaces",
7
+ description: "Switch to a repository",
8
+ usage: "rl checkout [repository]",
9
+ notes: [
10
+ "Choose a registered repository, or omit its name to select from a picker.",
11
+ "Enters its main checkout. Bare rl then selects a workspace in that repository.",
12
+ "Shell integration changes your directory; the standalone executable prints the path.",
13
+ ],
14
+ examples: ["rl checkout", "rl checkout frontend", "rl checkout backend"],
15
+ related: ["repo add", "switch"],
16
+ },
17
+ switch: {
18
+ group: "Workspaces",
19
+ description: "Open or create a workspace",
20
+ usage: "rl switch <name|branch|@|-> [options]",
21
+ options: [
22
+ "-c, --create Create a new branch workspace",
23
+ "-b, --base <ref> Creation base (default: origin/test unless configured)",
24
+ "--no-fetch Use cached refs when creating",
25
+ "--setup Run configured project setup",
26
+ "--agent codex|claude Start an agent after switching",
27
+ "-- <agent args> Forward options to the selected agent",
28
+ ],
29
+ notes: [
30
+ "Use rl checkout [repository] to switch repositories in your shell.",
31
+ "@ opens the main checkout; - returns to the previous workspace.",
32
+ "Shell integration changes your terminal's directory.",
33
+ "Related creation helpers: new reopens existing workspaces; open requires an existing branch.",
34
+ ],
35
+ examples: [
36
+ "rl switch feature/login",
37
+ "rl switch feature/login -c --base origin/test",
38
+ ],
39
+ related: ["new", "open", "path"],
40
+ },
41
+ list: {
42
+ group: "Workspaces",
43
+ description: "List workspaces",
44
+ usage: "rl list [options]",
45
+ options: [
46
+ "--all List workspaces across registered repositories",
47
+ "--json Print structured output",
48
+ "--fetch Refresh remote refs first",
49
+ "--include-deleted Include archived records",
50
+ ],
51
+ notes: [
52
+ "Commit counts are relative to each row's displayed upstream; CHANGES counts files.",
53
+ ],
54
+ },
55
+ status: {
56
+ group: "Workspaces",
57
+ description: "Inspect the selected repository",
58
+ usage: "rl status [--workspace] [--json] [--fetch]",
59
+ options: [
60
+ "--workspace Show the current workspace's Git, PR, sessions and task details",
61
+ "--json Print structured output",
62
+ "--fetch Refresh remote refs first",
63
+ ],
64
+ notes: [
65
+ "Shows repository selection, checkout path, base, workspaces and saved session counts.",
66
+ "--workspace includes Git changes, PR, sessions, task handoff and latest workflow.",
67
+ "Use rl list for workspace overviews; status --list and --all remain supported.",
68
+ ],
69
+ related: ["list", "instances"],
70
+ },
71
+ delete: {
72
+ group: "Workspaces",
73
+ description: "Remove workspaces",
74
+ usage: "rl delete [names...] [options]",
75
+ options: [
76
+ "--dry-run Preview removals",
77
+ "--force Allow dirty or unmerged workspaces",
78
+ "--jobs <n> Parallel removals (1–32; default: 4)",
79
+ ],
80
+ notes: [
81
+ "Without names, opens a multi-select picker.",
82
+ "Saved sessions, notes and task history are retained.",
83
+ ],
84
+ },
85
+ storage: {
86
+ group: "Workspaces",
87
+ description: "Inspect disk usage and clean generated files",
88
+ usage: "rl storage [--json] [--older-than <age>]",
89
+ options: [
90
+ "--json Print structured output",
91
+ "--older-than <age> Age for suggestions (default: 14d)",
92
+ ],
93
+ notes: [
94
+ "Measures only the current adopted workspace. Cleanup uses its configured clean paths.",
95
+ ],
96
+ related: ["setup show"],
97
+ },
98
+ "storage clean": {
99
+ description: "Remove configured generated directories",
100
+ usage: "rl storage clean [--dry-run] [--json]",
101
+ notes: [
102
+ "Only removes configured ignored directories in the current workspace.",
103
+ "Tracked source, credentials, notes and sessions are protected.",
104
+ ],
105
+ examples: ["rl storage clean --dry-run", "rl storage clean"],
106
+ },
107
+ resume: {
108
+ group: "Agents and tasks",
109
+ description: "Resume a saved conversation",
110
+ usage: "rl resume [codex|claude] [--latest|-l|--l]",
111
+ options: [
112
+ "--latest, -l, --l Resume the most recently used matching session directly",
113
+ ],
114
+ notes: [
115
+ "Without a filter, offers both Codex and Claude sessions in this workspace.",
116
+ "One match resumes automatically; multiple matches open a picker.",
117
+ ],
118
+ related: ["agent sessions", "agent save", "continue"],
119
+ },
120
+ agent: {
121
+ group: "Agents and tasks",
122
+ description: "Configure native agent roles and saved sessions",
123
+ usage: "rl agent <command>",
124
+ related: ["resume", "workflow", "policy"],
125
+ },
126
+ "agent start": {
127
+ description: "Start an interactive agent",
128
+ usage: "rl agent start <codex|claude> [--workspace <name>] [-- <agent args>]",
129
+ examples: ["rl agent start codex -- --model <model>"],
130
+ },
131
+ "agent run": {
132
+ description: "Run an agent non-interactively",
133
+ usage: "rl agent run <codex|claude> --prompt <text> [--workspace <name>] [-- <agent args>]",
134
+ notes: [
135
+ "Returns the agent's exit status and preserves its native options.",
136
+ ],
137
+ },
138
+ "agent list": {
139
+ description: "List workflow agent profiles",
140
+ usage: "rl agent list [--json]",
141
+ },
142
+ "agent add": {
143
+ description: "Add a role for your session's native subagents",
144
+ usage: "rl agent add <name> --instructions <file|-> [--description <text>] [--model <name>] [--reasoning <level>]",
145
+ notes: [
146
+ "Alternatively use --file <profile.json|->. Profiles never launch another provider; the current session supplies native subagents.",
147
+ ],
148
+ },
149
+ "agent set": {
150
+ description: "Save an agent profile",
151
+ usage: "rl agent set <name> --file <profile.json|->",
152
+ },
153
+ "agent show": {
154
+ description: "Inspect a workflow agent profile",
155
+ usage: "rl agent show <name> [--json]",
156
+ notes: [
157
+ "Profiles live in .rl/agents.json; they contain instructions and execution settings.",
158
+ ],
159
+ },
160
+ "agent sessions": {
161
+ description: "List saved conversations",
162
+ usage: "rl agent sessions [codex|claude]",
163
+ },
164
+ "agent save": {
165
+ description: "Save the current or an explicit conversation",
166
+ usage: "rl agent save [codex|claude] [UUID] [--json]",
167
+ notes: [
168
+ "Defaults to the current Codex session ID. Claude requires an explicit UUID.",
169
+ ],
170
+ },
171
+ "agent handoff": {
172
+ description: "Export shared task context",
173
+ usage: "rl agent handoff [codex|claude] [--workspace <name>] [--stdout | --output <file>]",
174
+ notes: [
175
+ "Exports notes; agent conversations remain in their original sessions.",
176
+ ],
177
+ },
178
+ "agent mcp": {
179
+ description: "Serve workspace context and notes through MCP",
180
+ usage: "rl agent mcp",
181
+ notes: ["Uses stdio. Start the server in the adopted workspace."],
182
+ },
183
+ workflow: {
184
+ group: "Agents and tasks",
185
+ description: "Define and track session-driven agent workflows",
186
+ usage: "rl workflow <command>",
187
+ notes: [
188
+ "Run controls apply only to this adopted workspace.",
189
+ "Omit a run UUID, or use latest, to select its most recently created run.",
190
+ ],
191
+ related: ["agent", "policy"],
192
+ },
193
+ "workflow init": {
194
+ description: "Create editable profiles, policies and workflows",
195
+ usage: "rl workflow init [--provider inherit|codex|claude] [--json]",
196
+ notes: [
197
+ "Creates .rl/agents.json, .rl/policies.json and .rl/agent-workflows.json.",
198
+ "Existing configuration is never overwritten.",
199
+ "Roles inherit the current session by default. The preset queues a review before finishing changed work.",
200
+ ],
201
+ },
202
+ "workflow list": {
203
+ description: "List configured workflows",
204
+ usage: "rl workflow list [--json]",
205
+ },
206
+ "workflow show": {
207
+ description: "Inspect steps and dependencies",
208
+ usage: "rl workflow show <name> [--json]",
209
+ },
210
+ "workflow run": {
211
+ description: "Prepare a workflow for this session's native agents",
212
+ usage: "rl workflow run [name] [options]",
213
+ options: [
214
+ "--base <ref> Local comparison ref (default: repository setting)",
215
+ "--prompt <text> Feature intent for the reviewers",
216
+ "--dry-run Preview scope and steps without launching agents",
217
+ "--json Print structured results",
218
+ "--agent codex|claude Parent session provider",
219
+ "--session <UUID> Parent conversation (inferred inside Codex)",
220
+ "--request <UUID> Start a queued workflow request instead of choosing a name",
221
+ ],
222
+ notes: [
223
+ "Default workflow: triple-review. RL never launches workflow agent or check processes.",
224
+ "The parent claims ready steps, spawns native subagents and reports evidence. RL applies the policy; provider usage is parent-reported.",
225
+ ],
226
+ examples: ["rl workflow run --base origin/test --dry-run"],
227
+ },
228
+ "workflow add": {
229
+ description: "Add a workflow with dependencies and spawn triggers",
230
+ usage: "rl workflow add <name> --file <workflow.json|->",
231
+ },
232
+ "workflow set": {
233
+ description: "Save a workflow definition and trigger rules",
234
+ usage: "rl workflow set <name> --file <workflow.json|->",
235
+ },
236
+ "workflow context": {
237
+ description: "Discover profiles, policies, workflows and trigger rules",
238
+ usage: "rl workflow context [--json]",
239
+ },
240
+ "workflow pending": {
241
+ description: "Read queued workflows for the parent session",
242
+ usage: "rl workflow pending [--agent codex|claude] [--session <UUID>]",
243
+ },
244
+ "workflow event": {
245
+ description: "Report a milestone and match workflow triggers",
246
+ usage: "rl workflow event --file <event.json|-> [--agent codex|claude] [--session <UUID>]",
247
+ },
248
+ "workflow claim": {
249
+ description: "Claim a ready step for a native subagent",
250
+ usage: "rl workflow claim <run> [--step <name>] [--agent codex|claude] [--session <UUID>]",
251
+ },
252
+ "workflow report": {
253
+ description: "Submit a claimed native agent's evidence or a check result",
254
+ usage: "rl workflow report <run> --file <report.json|-> [--agent codex|claude] [--session <UUID>]",
255
+ },
256
+ "workflow dismiss": {
257
+ description: "Dismiss an unavailable workflow with a reason",
258
+ usage: "rl workflow dismiss --file <dismissal.json|-> [--agent codex|claude] [--session <UUID>]",
259
+ },
260
+ "workflow status": {
261
+ description: "Inspect a saved run",
262
+ usage: "rl workflow status [UUID|latest] [--json]",
263
+ },
264
+ "workflow metrics": {
265
+ description: "Inspect measured duration and provider usage",
266
+ usage: "rl workflow metrics [UUID|latest] [--json]",
267
+ },
268
+ "workflow pause": {
269
+ description: "Pause a workflow run",
270
+ usage: "rl workflow pause [UUID|latest] [--json]",
271
+ notes: [
272
+ "Stops new step claims. The parent session owns and stops native child processes; existing claims may still report while paused.",
273
+ ],
274
+ },
275
+ "workflow cancel": {
276
+ description: "Cancel a workflow run",
277
+ usage: "rl workflow cancel [UUID|latest] [--json]",
278
+ },
279
+ "workflow resume": {
280
+ description: "Continue unfinished workflow stages",
281
+ usage: "rl workflow resume [UUID|latest] [--agent codex|claude] [--session <UUID>] [--json]",
282
+ notes: [
283
+ "Uses the saved configuration. Changed review scope requires a new run.",
284
+ ],
285
+ },
286
+ "workflow score": {
287
+ description: "Rescore saved findings using a policy",
288
+ usage: "rl workflow score [UUID|latest] [--policy <name>] [--json]",
289
+ notes: [
290
+ "Makes no agent calls. Preserves the original configuration and assessment history.",
291
+ ],
292
+ },
293
+ policy: {
294
+ group: "Agents and tasks",
295
+ description: "Inspect review criteria and scoring rules",
296
+ usage: "rl policy <command>",
297
+ notes: [
298
+ "Edit .rl/policies.json to change criteria, weights and readiness gates.",
299
+ ],
300
+ related: ["workflow score"],
301
+ },
302
+ "policy list": {
303
+ description: "List review policies",
304
+ usage: "rl policy list [--json]",
305
+ },
306
+ "policy show": {
307
+ description: "Inspect a review policy",
308
+ usage: "rl policy show <name> [--json]",
309
+ },
310
+ pause: {
311
+ group: "Agents and tasks",
312
+ description: "Save a task handoff",
313
+ usage: "rl pause --file <handoff.json|-> [--workspace <name>] [--json]",
314
+ options: [
315
+ "--summary <text> Task summary",
316
+ "--changes <text> Concrete changes",
317
+ "--validation <text> Checks actually run, or Not run",
318
+ "--next <text> Next steps",
319
+ "--blockers <text> Optional blockers",
320
+ "--agent codex|claude Agent owning the handoff (default: codex)",
321
+ "--session <UUID> Conversation ID; inferred inside Codex",
322
+ ],
323
+ notes: [
324
+ "Alternatively, supply summary, changes, validation and next directly instead of --file.",
325
+ "Saves the task; use workflow pause to pause an executing workflow.",
326
+ ],
327
+ related: ["continue"],
328
+ },
329
+ continue: {
330
+ group: "Agents and tasks",
331
+ description: "Continue a saved task handoff",
332
+ usage: "rl continue [workspace] [--no-agent]",
333
+ notes: [
334
+ "Defaults to the most recently paused task in this repository.",
335
+ "--no-agent opens the workspace without launching the saved conversation.",
336
+ ],
337
+ related: ["resume", "workflow resume"],
338
+ },
339
+ git: {
340
+ group: "Code and reviews",
341
+ description: "Inspect and integrate branch changes",
342
+ usage: "rl git <command>",
343
+ },
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
+ "git merge": {
369
+ description: "Integrate into a checked-out base branch",
370
+ usage: "rl git merge [target] [--squash -m <message>] [--cleanup] [--dry-run]",
371
+ notes: [
372
+ "Requires clean source and target workspaces; defaults to fast-forward only.",
373
+ "Use pr update to merge the latest base into remote PR branches instead.",
374
+ ],
375
+ },
376
+ pr: {
377
+ group: "Code and reviews",
378
+ description: "Open and manage pull requests",
379
+ usage: "rl pr [command]",
380
+ notes: ["With no command, opens this branch's PR in your browser."],
381
+ related: ["workflow"],
382
+ },
383
+ "pr open": {
384
+ description: "Open the current PR in a browser",
385
+ usage: "rl pr [open] [--json]",
386
+ notes: ["Keeps a usable URL in the output if the browser cannot open."],
387
+ },
388
+ "pr show": {
389
+ description: "Read the cached PR summary",
390
+ usage: "rl pr show [--json]",
391
+ notes: ["Works offline; sync refreshes the saved snapshot."],
392
+ },
393
+ "pr sync": {
394
+ description: "Refresh the saved PR snapshot",
395
+ usage: "rl pr sync [number|URL] [--json]",
396
+ },
397
+ "pr checks": {
398
+ description: "Show GitHub check results",
399
+ usage: "rl pr checks [--json]",
400
+ },
401
+ "pr checkout": {
402
+ description: "Create a workspace for a PR",
403
+ usage: "rl pr checkout <number> [--name <branch>]",
404
+ },
405
+ "pr update": {
406
+ description: "Merge the latest base into your RL session PRs",
407
+ usage: "rl pr update [numbers...] [options]",
408
+ options: [
409
+ "--base <branch> PR target branch (default: repository setting)",
410
+ "--dry-run Preview without changing remote branches",
411
+ "--limit <n> Maximum matching PRs to inspect (1–200; default: 100)",
412
+ "--json Print structured results",
413
+ ],
414
+ notes: [
415
+ "Without numbers, opens a multi-select picker of behind PRs.",
416
+ "Only branches belonging to active RL workspaces in this repository are eligible.",
417
+ "PRs stay open; results report updates, conflicts, failures and pending work.",
418
+ ],
419
+ examples: ["rl pr update --base test", "rl pr update 123 456 --dry-run"],
420
+ },
421
+ notes: {
422
+ group: "Project",
423
+ description: "Read and update shared task notes",
424
+ usage: "rl notes <command>",
425
+ },
426
+ "notes show": {
427
+ description: "Read shared context or progress",
428
+ usage: "rl notes show [context|progress]",
429
+ notes: ["Defaults to context."],
430
+ },
431
+ "notes path": {
432
+ description: "Print a shared document's path",
433
+ usage: "rl notes path [context|progress]",
434
+ },
435
+ "notes set": {
436
+ description: "Replace a shared document",
437
+ usage: "rl notes set <context|progress> <file|->",
438
+ },
439
+ "notes append": {
440
+ description: "Append a milestone or domain note",
441
+ usage: "rl notes append <context|progress> <file|->",
442
+ notes: [
443
+ "- reads stdin. Appends are locked to preserve concurrent updates.",
444
+ ],
445
+ },
446
+ "notes summary": {
447
+ description: "Export a workspace summary",
448
+ usage: "rl notes summary [--base <ref>] [--stdout | -o <file>] [--force]",
449
+ notes: ["Defaults to summary.md in this instance's state directory."],
450
+ },
451
+ repo: {
452
+ group: "Project",
453
+ description: "Manage registered repositories",
454
+ usage: "rl repo <command>",
455
+ notes: [
456
+ "Use rl checkout [repository] to switch repositories in your shell.",
457
+ "Use rl --repo <alias> <command> to select a repository explicitly.",
458
+ ],
459
+ },
460
+ "repo add": {
461
+ description: "Register a repository",
462
+ usage: "rl repo add [path] [--alias <name>] [--base <ref>] [--worktree-name <name>]",
463
+ notes: [
464
+ "Defaults to the current directory. Uses the origin repository name, or the main checkout directory name when there is no origin.",
465
+ "Use --alias for a custom name. The existing <alias> <path> form also works.",
466
+ ],
467
+ examples: [
468
+ "rl repo add .",
469
+ "rl repo add . --alias frontend --base origin/test",
470
+ ],
471
+ },
472
+ "repo list": {
473
+ description: "List registered repositories",
474
+ usage: "rl repo list [--json]",
475
+ },
476
+ "repo use": {
477
+ description: "Set the default repository",
478
+ usage: "rl repo use <alias>",
479
+ notes: ["Applies outside registered workspaces."],
480
+ },
481
+ "repo remove": {
482
+ description: "Unregister a repository",
483
+ usage: "rl repo remove <alias>",
484
+ notes: ["Retains its workspaces and saved state."],
485
+ },
486
+ setup: {
487
+ group: "Project",
488
+ description: "Configure and prepare a workspace",
489
+ usage: "rl setup [run] [--workspace <name>] [--dry-run]",
490
+ notes: ["With no subcommand, runs the configured project setup."],
491
+ related: ["workflow init"],
492
+ },
493
+ "setup init": {
494
+ description: "Create project setup configuration",
495
+ usage: "rl setup init",
496
+ notes: [
497
+ "Creates .rl/workflows.json without overwriting existing configuration.",
498
+ ],
499
+ },
500
+ "setup show": {
501
+ description: "Show project setup configuration",
502
+ usage: "rl setup show",
503
+ },
504
+ "setup run": {
505
+ description: "Run configured project setup",
506
+ usage: "rl setup [run] [--workspace <name>] [--dry-run]",
507
+ },
508
+ "setup adopt": {
509
+ description: "Register the current workspace and install hooks",
510
+ usage: "rl setup adopt [--extended-hooks]",
511
+ notes: ["Preserves existing hooks. Review Codex hook trust with /hooks."],
512
+ },
513
+ "setup shell": {
514
+ description: "Print shell integration",
515
+ usage: "rl setup shell <zsh|bash>",
516
+ examples: ['eval "$(rl setup shell zsh)"'],
517
+ },
518
+ doctor: {
519
+ group: "Project",
520
+ description: "Check and repair agent integration",
521
+ usage: "rl doctor [codex|claude] [--repair] [--json]",
522
+ notes: [
523
+ "Reports CLI availability, installed hooks and observed activity.",
524
+ "--repair reinstalls this workspace's RL hooks.",
525
+ ],
526
+ },
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
+ path: {
538
+ description: "Print a workspace path",
539
+ usage: "rl path [name|branch|@]",
540
+ related: ["switch"],
541
+ },
542
+ exec: {
543
+ description: "Run a command in a workspace",
544
+ usage: "rl exec [--workspace <name>] -- <command> [args...]",
545
+ related: ["setup run"],
546
+ },
547
+ instances: {
548
+ description: "Inspect active and archived instance records",
549
+ usage: "rl instances",
550
+ related: ["status", "list"],
551
+ },
552
+ };
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
+ const children = (name) => Object.entries(topics).filter(([key]) => key.startsWith(name + " ") &&
597
+ key.split(" ").length === name.split(" ").length + 1);
598
+ const safe = (value) => stripVTControlCharacters(value).replace(/[\x00-\x1f\x7f-\x9f]/g, " ");
599
+ export function mainHelp() {
600
+ const lines = [
601
+ "rl — workspaces, agents, and reviews",
602
+ "",
603
+ " rl Pick a workspace",
604
+ ];
605
+ for (const group of [
606
+ "Workspaces",
607
+ "Agents and tasks",
608
+ "Code and reviews",
609
+ "Project",
610
+ ]) {
611
+ lines.push("", group);
612
+ for (const [name, topic] of Object.entries(topics))
613
+ if (topic.group === group)
614
+ lines.push(` ${name.padEnd(12)}${topic.description}`);
615
+ }
616
+ return [...lines, "", "Run rl help <command> for usage and options."].join("\n");
617
+ }
618
+ function match(words) {
619
+ let name;
620
+ for (let length = 1; length <= words.length; length++)
621
+ if (Object.hasOwn(topics, words.slice(0, length).join(" ")))
622
+ name = words.slice(0, length).join(" ");
623
+ return name;
624
+ }
625
+ export function topicHelp(words) {
626
+ if (!words.length)
627
+ return mainHelp();
628
+ const normalized = canonical(words), name = match(normalized);
629
+ if (!name ||
630
+ (children(name).length && normalized.length > name.split(" ").length))
631
+ throw new Error(`Unknown help topic: ${safe(words.join(" "))}`);
632
+ const topic = topics[name];
633
+ const lines = [topic.description, "", `Usage: ${topic.usage}`];
634
+ const commands = children(name);
635
+ if (commands.length)
636
+ lines.push("", "Commands:", ...commands.map(([key, item]) => ` ${key.split(" ").at(-1).padEnd(12)}${item.description}`));
637
+ if (topic.options?.length)
638
+ lines.push("", "Options:", ...topic.options.map((option) => " " + option));
639
+ if (topic.notes?.length)
640
+ lines.push("", ...topic.notes);
641
+ if (topic.examples?.length)
642
+ lines.push("", "Examples:", ...topic.examples.map((example) => " " + example));
643
+ if (topic.related?.length)
644
+ lines.push("", `Related: ${topic.related.map((name) => `rl help ${name}`).join(", ")}`);
645
+ return [
646
+ ...lines,
647
+ "",
648
+ commands.length ? `Run rl help ${name} <command> for details.` : "",
649
+ ]
650
+ .filter((line, index, array) => line !== "" || index < array.length - 1)
651
+ .join("\n");
652
+ }
653
+ const CONTEXT = new Set([
654
+ "repo",
655
+ "state-dir",
656
+ "base-dir",
657
+ "default-base",
658
+ "legacy-root",
659
+ "legacy-name",
660
+ "legacy-base",
661
+ "selection",
662
+ "output-path",
663
+ ]);
664
+ export function invocation(module, argv) {
665
+ let index = 0;
666
+ while (argv[index]?.startsWith("--") && CONTEXT.has(argv[index].slice(2)))
667
+ index += 2;
668
+ const rest = argv.slice(index);
669
+ const prefix = {
670
+ delete: ["delete"],
671
+ summary: ["summary"],
672
+ mcp: ["mcp"],
673
+ picker: [],
674
+ };
675
+ const words = Object.hasOwn(prefix, module)
676
+ ? [...prefix[module], ...rest]
677
+ : module === "agent" && rest[0] !== "handoff"
678
+ ? ["agent", ...rest]
679
+ : rest;
680
+ const defaults = {
681
+ state: "status",
682
+ repos: "repo",
683
+ workflow: "switch",
684
+ git: "git",
685
+ agent: "agent",
686
+ automation: "workflow",
687
+ storage: "storage",
688
+ "pr-update": "pr",
689
+ };
690
+ return canonical(words.length ? words : defaults[module] ? [defaults[module]] : []);
691
+ }
692
+ export function usageHint(module, argv, details = false) {
693
+ const words = invocation(module, argv), name = match(words);
694
+ if (!name)
695
+ return "Run rl help for available commands.";
696
+ const topic = topics[name], lines = [`Usage: ${topic.usage}`];
697
+ if (details && topic.options?.length)
698
+ lines.push("", "Options:", ...topic.options.map((option) => " " + option));
699
+ if (details && children(name).length)
700
+ lines.push("", "Commands:", ...children(name).map(([key, child]) => ` ${key.split(" ").at(-1).padEnd(12)}${child.description}`));
701
+ return [...lines, `See rl help ${name}.`].join("\n");
702
+ }
703
+ export function explicitHelp(argv) {
704
+ let words = [...argv];
705
+ if (words[0] === "--repo")
706
+ words = words.slice(2);
707
+ else if (words[0]?.startsWith("--repo="))
708
+ words = words.slice(1);
709
+ if (words[0] === "help")
710
+ words.shift();
711
+ const separator = words.indexOf("--");
712
+ if (separator >= 0)
713
+ words = words.slice(0, separator);
714
+ words = words.filter((word) => word !== "--help" && word !== "-h");
715
+ return topicHelp(words);
716
+ }
717
+ if (process.argv[1] &&
718
+ fileURLToPath(import.meta.url) === path.resolve(process.argv[1])) {
719
+ try {
720
+ console.log(explicitHelp(process.argv.slice(2)));
721
+ }
722
+ catch (error) {
723
+ console.error(`rl: ${safe(error instanceof Error ? error.message : String(error))}\nRun rl help for available commands.`);
724
+ process.exitCode = 1;
725
+ }
726
+ }