@revotools/cli 0.2.2 → 0.3.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 (3) hide show
  1. package/README.md +41 -5
  2. package/dist/revo +464 -18
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -8,8 +8,13 @@ Revo is a fork of [Mars](https://github.com/dean0x/mars). It keeps everything
8
8
  Mars gives you — tag-based filtering, coordinated branches, zero dependencies —
9
9
  and adds features designed for working with coding agents:
10
10
 
11
+ - **`revo init`** auto-detects existing git repos in the current directory and
12
+ bootstraps a workspace around them, no manual `revo add` required.
13
+ - **`revo detect`** does the same on demand for an already-populated folder
14
+ full of clones.
11
15
  - **`revo context`** scans your repos and writes a root-level `CLAUDE.md` so
12
- Claude Code can understand the whole workspace at a glance.
16
+ Claude Code can understand the whole workspace at a glance — including
17
+ active feature briefs and a built-in revo command reference.
13
18
  - **`revo feature <name>`** creates a coordinated branch and shared context
14
19
  file across matching repos.
15
20
  - **`revo commit`**, **`revo push`**, **`revo pr`** let you commit, push, and
@@ -22,6 +27,8 @@ Pure bash 3.2+, works on macOS out of the box, no runtime dependencies beyond
22
27
 
23
28
  ## Quick Start
24
29
 
30
+ ### From an empty directory
31
+
25
32
  ```bash
26
33
  mkdir my-project && cd my-project
27
34
  revo init
@@ -30,10 +37,23 @@ revo add git@github.com:org/shared-types.git --tags shared
30
37
  revo add git@github.com:org/backend.git --tags backend,api --depends-on shared-types
31
38
  revo add git@github.com:org/frontend.git --tags frontend,web --depends-on backend
32
39
 
33
- revo clone # CLAUDE.md is auto-generated after first clone
40
+ revo clone # CLAUDE.md is regenerated after each clone batch
34
41
  revo context # regenerate it any time repos change
35
42
  ```
36
43
 
44
+ ### From a folder you already populated with clones
45
+
46
+ ```bash
47
+ cd ~/code/my-project # already has frontend/, backend/, shared/ as git repos
48
+ revo init # auto-detects existing repos, links them, writes CLAUDE.md
49
+ # — or —
50
+ revo detect # same idea, runs on its own without prompting
51
+ ```
52
+
53
+ `init` and `detect` both link root-level repos into `repos/` via relative
54
+ symlinks so the rest of revo's data model keeps working without moving any
55
+ files around.
56
+
37
57
  Then point Claude Code at the workspace directory and it will read `CLAUDE.md`
38
58
  on its own.
39
59
 
@@ -61,13 +81,14 @@ revo pr "Clock endpoint for students" ── coordinated PRs via gh
61
81
 
62
82
  ## Commands
63
83
 
64
- ### Workspace (from Mars)
84
+ ### Workspace
65
85
 
66
86
  | Command | Description |
67
87
  |---------|-------------|
68
- | `revo init` | Initialize a new workspace |
88
+ | `revo init` | Initialize a new workspace; auto-detects existing git repos in cwd |
89
+ | `revo detect` | Bootstrap revo around git repos already present in cwd |
69
90
  | `revo add <url> [--tags t1,t2] [--depends-on r1,r2]` | Add a repository to config |
70
- | `revo clone [--tag TAG]` | Clone configured repositories |
91
+ | `revo clone [--tag TAG]` | Clone configured repositories (regenerates `CLAUDE.md` after) |
71
92
  | `revo list [--tag TAG]` | List configured repositories |
72
93
  | `revo status [--tag TAG]` | Show status of all repositories |
73
94
  | `revo sync [--tag TAG] [--rebase]` | Pull latest changes |
@@ -85,6 +106,21 @@ revo pr "Clock endpoint for students" ── coordinated PRs via gh
85
106
  | `revo push [--tag TAG]` | Push branches across repos |
86
107
  | `revo pr <title> [--tag TAG] [--body BODY]` | Create coordinated PRs via `gh` |
87
108
 
109
+ ### What `revo context` detects per repo
110
+
111
+ - **Node.js** — Next.js, Nuxt, Remix, SvelteKit, Astro, Vite, NestJS, Fastify,
112
+ Express, Hono, Angular, React, Vue, Svelte, React Native, Expo
113
+ - **Python** — Django, FastAPI, Flask, Starlette
114
+ - **Go** (module name), **Rust** (Cargo), **Java/Kotlin** (Gradle), **Java**
115
+ (Maven), **Ruby** (Rails, Sinatra), **Swift** (Package.swift)
116
+ - API route directories (`src/routes/`, `src/api/`, `app/api/`, `routes/`,
117
+ `pages/api/`, etc.)
118
+ - Whether the repo ships its own `CLAUDE.md`, `Dockerfile`, or
119
+ `docker-compose.yml`
120
+ - The first non-trivial line of `README.md` as a one-line description
121
+ - Any `.revo/features/*.md` briefs at the workspace root, listed as
122
+ `## Active Features`
123
+
88
124
  ## Tag Filtering
89
125
 
90
126
  Target subsets of repos using `--tag`:
package/dist/revo CHANGED
@@ -8,7 +8,7 @@ set -euo pipefail
8
8
  # Exit cleanly on SIGPIPE (e.g., revo clone | grep, revo status | head)
9
9
  trap 'exit 0' PIPE
10
10
 
11
- REVO_VERSION="0.2.0"
11
+ REVO_VERSION="0.3.0"
12
12
 
13
13
 
14
14
  # === lib/ui.sh ===
@@ -1232,6 +1232,10 @@ _scan_node_framework() {
1232
1232
  printf 'SvelteKit'
1233
1233
  elif _scan_pkg_has_dep "$file" "astro"; then
1234
1234
  printf 'Astro'
1235
+ elif _scan_pkg_has_dep "$file" "expo"; then
1236
+ printf 'Expo (React Native)'
1237
+ elif _scan_pkg_has_dep "$file" "react-native"; then
1238
+ printf 'React Native'
1235
1239
  elif _scan_pkg_has_dep "$file" "vite"; then
1236
1240
  printf 'Vite'
1237
1241
  elif _scan_pkg_has_dep "$file" "nestjs" || _scan_pkg_has_dep "$file" "@nestjs/core"; then
@@ -1242,6 +1246,8 @@ _scan_node_framework() {
1242
1246
  printf 'Express'
1243
1247
  elif _scan_pkg_has_dep "$file" "hono"; then
1244
1248
  printf 'Hono'
1249
+ elif _scan_pkg_has_dep "$file" "@angular/core"; then
1250
+ printf 'Angular'
1245
1251
  elif _scan_pkg_has_dep "$file" "react"; then
1246
1252
  printf 'React'
1247
1253
  elif _scan_pkg_has_dep "$file" "vue"; then
@@ -1251,6 +1257,16 @@ _scan_node_framework() {
1251
1257
  fi
1252
1258
  }
1253
1259
 
1260
+ # Detect framework from a Gemfile
1261
+ _scan_ruby_framework() {
1262
+ local file="$1"
1263
+ if grep -q "rails" "$file" 2>/dev/null; then
1264
+ printf 'Rails'
1265
+ elif grep -q "sinatra" "$file" 2>/dev/null; then
1266
+ printf 'Sinatra'
1267
+ fi
1268
+ }
1269
+
1254
1270
  # Detect framework from Python dependency files
1255
1271
  _scan_python_framework() {
1256
1272
  local repo_dir="$1"
@@ -1387,6 +1403,27 @@ scan_repo() {
1387
1403
  SCAN_NAME=$(grep -m1 -E '^[[:space:]]*name[[:space:]]*=' "$repo_dir/Cargo.toml" 2>/dev/null | sed -E 's/.*=[[:space:]]*"([^"]*)".*/\1/' || true)
1388
1404
  fi
1389
1405
 
1406
+ # Java/Kotlin (Gradle)
1407
+ if [[ -z "$SCAN_LANG" ]] && { [[ -f "$repo_dir/build.gradle" ]] || [[ -f "$repo_dir/build.gradle.kts" ]]; }; then
1408
+ SCAN_LANG="Java/Kotlin (Gradle)"
1409
+ fi
1410
+
1411
+ # Java (Maven)
1412
+ if [[ -z "$SCAN_LANG" ]] && [[ -f "$repo_dir/pom.xml" ]]; then
1413
+ SCAN_LANG="Java (Maven)"
1414
+ fi
1415
+
1416
+ # Ruby
1417
+ if [[ -z "$SCAN_LANG" ]] && [[ -f "$repo_dir/Gemfile" ]]; then
1418
+ SCAN_LANG="Ruby"
1419
+ SCAN_FRAMEWORK=$(_scan_ruby_framework "$repo_dir/Gemfile")
1420
+ fi
1421
+
1422
+ # Swift
1423
+ if [[ -z "$SCAN_LANG" ]] && [[ -f "$repo_dir/Package.swift" ]]; then
1424
+ SCAN_LANG="Swift"
1425
+ fi
1426
+
1390
1427
  # Routes
1391
1428
  SCAN_ROUTES=$(_scan_list_routes "$repo_dir")
1392
1429
 
@@ -1412,12 +1449,181 @@ scan_repo() {
1412
1449
 
1413
1450
  # === lib/commands/init.sh ===
1414
1451
  # Revo CLI - init command
1415
- # Interactive workspace initialization
1452
+ # Initializes a workspace and auto-detects existing git repos in the current
1453
+ # directory (and in repos/), so that running `revo init` in a folder you
1454
+ # already populated with clones bootstraps a usable workspace immediately.
1455
+
1456
+ # Suggest tags for an auto-detected repo based on its package contents.
1457
+ # Always returns the repo name as a tag, plus an optional category tag
1458
+ # (frontend/backend) when one can be inferred. Skips the category when it
1459
+ # would be a duplicate of the name.
1460
+ _init_auto_tags() {
1461
+ local dir="$1"
1462
+ local name="$2"
1463
+ local category=""
1464
+
1465
+ if [[ -f "$dir/package.json" ]]; then
1466
+ if grep -qE '"(next|nuxt|react|vue|svelte|@angular/core|astro|@remix-run/react|@sveltejs/kit|expo|react-native)"' "$dir/package.json" 2>/dev/null; then
1467
+ category="frontend"
1468
+ elif grep -qE '"(express|fastify|hono|@nestjs/core|nestjs|koa)"' "$dir/package.json" 2>/dev/null; then
1469
+ category="backend"
1470
+ fi
1471
+ elif [[ -f "$dir/go.mod" ]] || [[ -f "$dir/Cargo.toml" ]] || [[ -f "$dir/pom.xml" ]] || [[ -f "$dir/build.gradle" ]] || [[ -f "$dir/build.gradle.kts" ]]; then
1472
+ category="backend"
1473
+ elif [[ -f "$dir/pyproject.toml" ]] || [[ -f "$dir/requirements.txt" ]]; then
1474
+ category="backend"
1475
+ fi
1476
+
1477
+ if [[ -n "$category" ]] && [[ "$category" != "$name" ]]; then
1478
+ printf '%s, %s' "$name" "$category"
1479
+ else
1480
+ printf '%s' "$name"
1481
+ fi
1482
+ }
1483
+
1484
+ # Scan for git repos in the current directory and in repos/.
1485
+ # Sets _INIT_FOUND_DIRS to a newline-separated list of repo paths.
1486
+ _INIT_FOUND_DIRS=""
1487
+ _init_scan_existing() {
1488
+ _INIT_FOUND_DIRS=""
1489
+
1490
+ local d
1491
+ for d in */; do
1492
+ d="${d%/}"
1493
+ [[ "$d" == "repos" ]] && continue
1494
+ [[ "$d" == ".revo" ]] && continue
1495
+ if [[ -d "$d/.git" ]]; then
1496
+ if [[ -z "$_INIT_FOUND_DIRS" ]]; then
1497
+ _INIT_FOUND_DIRS="$d"
1498
+ else
1499
+ _INIT_FOUND_DIRS="$_INIT_FOUND_DIRS"$'\n'"$d"
1500
+ fi
1501
+ fi
1502
+ done
1503
+
1504
+ if [[ -d "repos" ]]; then
1505
+ for d in repos/*/; do
1506
+ d="${d%/}"
1507
+ if [[ -d "$d/.git" ]]; then
1508
+ if [[ -z "$_INIT_FOUND_DIRS" ]]; then
1509
+ _INIT_FOUND_DIRS="$d"
1510
+ else
1511
+ _INIT_FOUND_DIRS="$_INIT_FOUND_DIRS"$'\n'"$d"
1512
+ fi
1513
+ fi
1514
+ done
1515
+ fi
1516
+ }
1517
+
1518
+ # Write a Claude-first onboarding CLAUDE.md, or append a revo section if the
1519
+ # user already has their own CLAUDE.md in the workspace root.
1520
+ _init_write_claude_md() {
1521
+ local out="$REVO_WORKSPACE_ROOT/CLAUDE.md"
1522
+
1523
+ if [[ -f "$out" ]] && ! grep -q "managed by revo" "$out" 2>/dev/null; then
1524
+ if grep -q "Workspace Tool: revo" "$out" 2>/dev/null; then
1525
+ return 0
1526
+ fi
1527
+ cat >> "$out" << 'EOF'
1528
+
1529
+ ---
1530
+
1531
+ ## Workspace Tool: revo
1532
+
1533
+ This workspace uses revo to manage multiple repos.
1534
+ Source: https://github.com/jippylong12/revo
1535
+
1536
+ ### Setup commands
1537
+ - `revo add <git-url> --tags <tags> [--depends-on <repo>]` — add a repo to workspace
1538
+ - `revo clone` — clone all configured repos
1539
+ - `revo context` — scan repos and regenerate the workspace context section
1540
+
1541
+ ### Daily commands
1542
+ - `revo status` — branch and dirty state across all repos
1543
+ - `revo sync` — pull latest across all repos
1544
+ - `revo feature <name>` — create feature branch across all repos
1545
+ - `revo commit "msg"` — commit all dirty repos with same message
1546
+ - `revo push` — push all repos
1547
+ - `revo pr "title"` — create coordinated PRs via gh CLI
1548
+ - `revo exec "cmd" --tag <tag>` — run command in filtered repos
1549
+
1550
+ ### Working in this workspace
1551
+ - Repos live in the repos/ subdirectory (or wherever configured in revo.yaml)
1552
+ - Edit files across repos directly
1553
+ - Check .revo/features/ for active feature briefs
1554
+ - Follow dependency order when making cross-repo changes
1555
+ EOF
1556
+ ui_step_done "Appended revo section to existing CLAUDE.md"
1557
+ return 0
1558
+ fi
1559
+
1560
+ cat > "$out" << 'EOF'
1561
+ # Workspace managed by revo
1562
+
1563
+ This is a multi-repo workspace managed by revo.
1564
+ revo is installed and available in the terminal.
1565
+
1566
+ ## Quick reference
1567
+
1568
+ ### If repos are not yet added
1569
+ The user may give you repo URLs or descriptions. Use these commands to set up:
1570
+
1571
+ ```bash
1572
+ revo add <git-url> --tags <tag1,tag2> [--depends-on <repo-name>]
1573
+ revo clone
1574
+ revo context # regenerates this file with full repo details
1575
+ ```
1576
+
1577
+ Example:
1578
+ ```bash
1579
+ revo add git@github.com:org/shared.git --tags shared,types
1580
+ revo add git@github.com:org/backend.git --tags backend,api --depends-on shared
1581
+ revo add git@github.com:org/frontend.git --tags frontend,web --depends-on backend
1582
+ revo clone
1583
+ ```
1584
+
1585
+ ### If repos are already set up
1586
+ Use `revo status` to see all repos, branches, and dirty state.
1587
+
1588
+ ## Available commands
1589
+ - `revo status` — branch and dirty state across all repos
1590
+ - `revo sync` — pull latest across all repos
1591
+ - `revo feature <name>` — create feature branch across all repos
1592
+ - `revo commit "msg"` — commit all dirty repos with same message
1593
+ - `revo push` — push all repos
1594
+ - `revo pr "title"` — create coordinated PRs via gh CLI
1595
+ - `revo exec "cmd" --tag <tag>` — run command in filtered repos
1596
+ - `revo context` — regenerate this file after repos change
1597
+ - `revo add <url> --tags <t> --depends-on <d>` — add a repo
1598
+
1599
+ ### Tag filtering
1600
+ All commands support `--tag <tag>` to target specific repos:
1601
+
1602
+ ```bash
1603
+ revo exec "npm test" --tag frontend
1604
+ revo sync --tag backend
1605
+ revo branch hotfix --tag api
1606
+ ```
1607
+
1608
+ ## Working in this workspace
1609
+ - Repos are in the repos/ subdirectory (or as configured in revo.yaml)
1610
+ - Edit files across repos/ directly — you have full access
1611
+ - When making cross-repo changes, follow the dependency order below
1612
+ - Check .revo/features/ for active feature briefs
1613
+ - Use revo commands instead of manually running git in each repo
1614
+
1615
+ ## Dependency order
1616
+ <!-- revo context will fill this in once repos are cloned -->
1617
+ Run `revo context` after cloning to populate repo details and dependency order.
1618
+ EOF
1619
+
1620
+ ui_step_done "Created CLAUDE.md (Claude reads this automatically)"
1621
+ }
1416
1622
 
1417
1623
  cmd_init() {
1418
1624
  local workspace_name=""
1419
1625
 
1420
- # Check if already initialized
1626
+ # Already initialized? Bail out.
1421
1627
  if [[ -f "revo.yaml" ]] || [[ -f "mars.yaml" ]]; then
1422
1628
  ui_step_error "Workspace already initialized in this directory"
1423
1629
  return 1
@@ -1425,20 +1631,21 @@ cmd_init() {
1425
1631
 
1426
1632
  ui_intro "Revo - Claude-first Multi-Repo Workspace"
1427
1633
 
1428
- # Get workspace name
1634
+ # Default workspace name to current directory basename so init can run
1635
+ # non-interactively. The user can still override by typing a value.
1636
+ local default_name
1637
+ default_name=$(basename "$PWD")
1429
1638
  ui_step "Workspace name?"
1430
1639
  printf '%s ' "$(ui_bar)"
1431
1640
  read -r workspace_name
1432
-
1433
1641
  if [[ -z "$workspace_name" ]]; then
1434
- ui_outro_cancel "Cancelled - workspace name is required"
1435
- return 1
1642
+ workspace_name="$default_name"
1436
1643
  fi
1437
1644
 
1438
1645
  ui_step_done "Workspace:" "$workspace_name"
1439
1646
  ui_bar_line
1440
1647
 
1441
- # Initialize workspace
1648
+ # Initialize the on-disk config.
1442
1649
  if ! config_init "$workspace_name"; then
1443
1650
  ui_step_error "Failed to initialize workspace"
1444
1651
  return 1
@@ -1448,8 +1655,177 @@ cmd_init() {
1448
1655
  ui_step_done "Created .gitignore"
1449
1656
  ui_step_done "Created repos/ directory"
1450
1657
 
1658
+ # Auto-detect existing git repos in the current directory.
1659
+ _init_scan_existing
1660
+ local detected_count=0
1661
+ if [[ -n "$_INIT_FOUND_DIRS" ]]; then
1662
+ local dir name remote tags path
1663
+ while IFS= read -r dir; do
1664
+ [[ -z "$dir" ]] && continue
1665
+ name=$(basename "$dir")
1666
+ remote=$(cd "$dir" && git remote get-url origin 2>/dev/null || true)
1667
+ if [[ -z "$remote" ]]; then
1668
+ ui_step_error "Skipping $name (no git remote)"
1669
+ continue
1670
+ fi
1671
+
1672
+ path="$name"
1673
+
1674
+ # If the repo lives at the workspace root (not under repos/),
1675
+ # link it into repos/ so the rest of revo's data model — which
1676
+ # always resolves $REVO_REPOS_DIR/$path — works without copying
1677
+ # or moving files.
1678
+ if [[ "$dir" != repos/* ]]; then
1679
+ if [[ ! -e "repos/$name" ]]; then
1680
+ ln -s "../$name" "repos/$name"
1681
+ fi
1682
+ fi
1683
+
1684
+ tags=$(_init_auto_tags "$dir" "$name")
1685
+ yaml_add_repo "$remote" "$path" "$tags" ""
1686
+ ui_step_done "Detected:" "$name → $remote"
1687
+ detected_count=$((detected_count + 1))
1688
+ done <<< "$_INIT_FOUND_DIRS"
1689
+
1690
+ if [[ $detected_count -gt 0 ]]; then
1691
+ config_save
1692
+ ui_info "$(ui_dim "Edit revo.yaml to adjust tags or add depends_on relationships")"
1693
+ fi
1694
+ fi
1695
+
1696
+ # Write the Claude-first onboarding CLAUDE.md (or append to user's own).
1697
+ _init_write_claude_md
1698
+
1699
+ # If we detected repos, immediately generate the full workspace context.
1700
+ if [[ $detected_count -gt 0 ]]; then
1701
+ ui_bar_line
1702
+ cmd_context
1703
+ return 0
1704
+ fi
1705
+
1451
1706
  ui_outro "Workspace initialized! Run 'revo add <url>' to add repositories."
1707
+ return 0
1708
+ }
1709
+
1710
+ # === lib/commands/detect.sh ===
1711
+ # Revo CLI - detect command
1712
+ # Bootstraps a workspace around git repos that already exist in the current
1713
+ # directory. Use this when you have a parent folder full of clones and want
1714
+ # revo to wrap them without re-cloning.
1452
1715
 
1716
+ cmd_detect() {
1717
+ while [[ $# -gt 0 ]]; do
1718
+ case "$1" in
1719
+ --help|-h)
1720
+ printf 'Usage: revo detect\n\n'
1721
+ printf 'Auto-detect git repositories in the current directory and\n'
1722
+ printf 'bootstrap a revo workspace around them. Generates revo.yaml\n'
1723
+ printf 'and CLAUDE.md from what it finds.\n'
1724
+ return 0
1725
+ ;;
1726
+ *)
1727
+ ui_step_error "Unknown option: $1"
1728
+ return 1
1729
+ ;;
1730
+ esac
1731
+ done
1732
+
1733
+ if [[ -f "revo.yaml" ]] || [[ -f "mars.yaml" ]]; then
1734
+ ui_step_error "Workspace already initialized — run 'revo context' to regenerate CLAUDE.md"
1735
+ return 1
1736
+ fi
1737
+
1738
+ ui_intro "Revo - Detect Existing Repositories"
1739
+ ui_step "Scanning for git repos..."
1740
+
1741
+ # Initialize the workspace using the cwd basename. This sets
1742
+ # REVO_WORKSPACE_ROOT/REVO_CONFIG_FILE/REVO_REPOS_DIR and writes an empty
1743
+ # revo.yaml + .gitignore.
1744
+ local default_name
1745
+ default_name=$(basename "$PWD")
1746
+ if ! config_init "$default_name"; then
1747
+ ui_step_error "Failed to initialize workspace"
1748
+ return 1
1749
+ fi
1750
+
1751
+ local found=0
1752
+ local d name remote category tags
1753
+
1754
+ for d in */; do
1755
+ d="${d%/}"
1756
+ [[ "$d" == "repos" ]] && continue
1757
+ [[ "$d" == ".revo" ]] && continue
1758
+ [[ -d "$d/.git" ]] || continue
1759
+
1760
+ name="$d"
1761
+ remote=$(cd "$d" && git remote get-url origin 2>/dev/null || echo "local://$d")
1762
+
1763
+ # Auto-categorize from package contents.
1764
+ category=""
1765
+ if [[ -f "$d/package.json" ]]; then
1766
+ if grep -qE '"(next|nuxt|react|vue|svelte|@angular/core|astro|@remix-run/react|@sveltejs/kit|expo|react-native)"' "$d/package.json" 2>/dev/null; then
1767
+ category="frontend"
1768
+ elif grep -qE '"(express|fastify|hono|@nestjs/core|nestjs|koa)"' "$d/package.json" 2>/dev/null; then
1769
+ category="backend"
1770
+ fi
1771
+ elif [[ -f "$d/go.mod" ]] || [[ -f "$d/Cargo.toml" ]] || [[ -f "$d/pom.xml" ]] || [[ -f "$d/build.gradle" ]] || [[ -f "$d/build.gradle.kts" ]]; then
1772
+ category="backend"
1773
+ elif [[ -f "$d/pyproject.toml" ]] || [[ -f "$d/requirements.txt" ]]; then
1774
+ category="backend"
1775
+ fi
1776
+
1777
+ if [[ -n "$category" ]] && [[ "$category" != "$name" ]]; then
1778
+ tags="$name, $category"
1779
+ else
1780
+ tags="$name"
1781
+ fi
1782
+
1783
+ # Link the root-level repo into repos/ so the rest of revo's data
1784
+ # model (which always resolves $REVO_REPOS_DIR/$path) works.
1785
+ if [[ ! -e "repos/$name" ]]; then
1786
+ ln -s "../$name" "repos/$name"
1787
+ fi
1788
+
1789
+ yaml_add_repo "$remote" "$name" "$tags" ""
1790
+ ui_step_done "Found:" "$name ($remote)"
1791
+ found=$((found + 1))
1792
+ done
1793
+
1794
+ if [[ -d "repos" ]]; then
1795
+ for d in repos/*/; do
1796
+ d="${d%/}"
1797
+ [[ -d "$d/.git" ]] || continue
1798
+ name=$(basename "$d")
1799
+ # Skip repos already added via symlink in the loop above.
1800
+ local already=0
1801
+ local i
1802
+ for ((i = 0; i < YAML_REPO_COUNT; i++)); do
1803
+ if [[ "$(yaml_get_path "$i")" == "$name" ]]; then
1804
+ already=1
1805
+ break
1806
+ fi
1807
+ done
1808
+ [[ $already -eq 1 ]] && continue
1809
+ remote=$(cd "$d" && git remote get-url origin 2>/dev/null || echo "local://$d")
1810
+ yaml_add_repo "$remote" "$name" "$name" ""
1811
+ ui_step_done "Found:" "$name ($remote)"
1812
+ found=$((found + 1))
1813
+ done
1814
+ fi
1815
+
1816
+ if [[ $found -eq 0 ]]; then
1817
+ rm -f "$REVO_CONFIG_FILE"
1818
+ ui_step_error "No git repos found in current directory. Use 'revo init' instead."
1819
+ ui_outro_cancel "Nothing to detect"
1820
+ return 1
1821
+ fi
1822
+
1823
+ config_save
1824
+ ui_step_done "Detected $found repository(ies)"
1825
+ ui_info "$(ui_dim "Edit revo.yaml to adjust tags or add depends_on relationships")"
1826
+ ui_bar_line
1827
+
1828
+ cmd_context
1453
1829
  return 0
1454
1830
  }
1455
1831
 
@@ -1548,9 +1924,10 @@ cmd_clone() {
1548
1924
  fi
1549
1925
  done <<< "$repos"
1550
1926
 
1551
- # Auto-generate workspace CLAUDE.md on first successful clone
1552
- if [[ $fail_count -eq 0 ]] && [[ $success_count -gt 0 ]]; then
1553
- context_autogenerate_if_missing
1927
+ # Always regenerate the workspace CLAUDE.md after a clone batch so that
1928
+ # newly cloned repos appear in the context immediately.
1929
+ if [[ $fail_count -eq 0 ]] && { [[ $success_count -gt 0 ]] || [[ $skip_count -gt 0 ]]; }; then
1930
+ context_regenerate_silent
1554
1931
  fi
1555
1932
 
1556
1933
  # Summary
@@ -2489,7 +2866,29 @@ _context_write_file() {
2489
2866
  printf '\n> Warning: a dependency cycle was detected. Listed in best-effort order.\n' >> "$output"
2490
2867
  fi
2491
2868
 
2492
- # Agent instructions
2869
+ # Active features (any .revo/features/*.md files)
2870
+ local features_dir="$REVO_WORKSPACE_ROOT/.revo/features"
2871
+ if [[ -d "$features_dir" ]]; then
2872
+ local has_features=0
2873
+ local f
2874
+ for f in "$features_dir"/*.md; do
2875
+ [[ -f "$f" ]] || continue
2876
+ if [[ $has_features -eq 0 ]]; then
2877
+ {
2878
+ printf '\n'
2879
+ printf '## Active Features\n'
2880
+ printf '\n'
2881
+ } >> "$output"
2882
+ has_features=1
2883
+ fi
2884
+ local fname rel
2885
+ fname=$(basename "$f" .md)
2886
+ rel=".revo/features/$(basename "$f")"
2887
+ printf -- '- **%s** — see [%s](%s)\n' "$fname" "$rel" "$rel" >> "$output"
2888
+ done
2889
+ fi
2890
+
2891
+ # Agent instructions + revo tool docs
2493
2892
  {
2494
2893
  printf '\n'
2495
2894
  printf '## Agent Instructions\n'
@@ -2504,6 +2903,49 @@ _context_write_file() {
2504
2903
  printf '6. Use `revo commit "msg"` to commit across all repos at once\n'
2505
2904
  printf '7. Use `revo feature <name>` to start a coordinated feature workspace\n'
2506
2905
  printf '8. Use `revo pr "title"` to open coordinated pull requests\n'
2906
+ printf '\n'
2907
+ printf '## Workspace Tool: revo\n'
2908
+ printf '\n'
2909
+ printf 'This workspace is managed by revo.\n'
2910
+ printf 'Source: https://github.com/jippylong12/revo\n'
2911
+ printf '\n'
2912
+ printf '### Available commands (run in terminal)\n'
2913
+ printf '\n'
2914
+ printf '**Setup:**\n'
2915
+ printf -- '- `revo add <git-url> --tags <tags> [--depends-on <repo>]` — add a repo\n'
2916
+ printf -- '- `revo clone` — clone all configured repos\n'
2917
+ printf -- '- `revo context` — regenerate this file\n'
2918
+ printf -- '- `revo detect` — bootstrap around existing repos in cwd\n'
2919
+ printf '\n'
2920
+ printf '**Daily workflow:**\n'
2921
+ printf -- '- `revo status` — branch and dirty state across all repos\n'
2922
+ printf -- '- `revo sync` — pull latest across all repos\n'
2923
+ printf -- '- `revo feature <name> [--tag t]` — create feature branch across repos\n'
2924
+ printf -- '- `revo commit "msg" [--tag t]` — commit all dirty repos\n'
2925
+ printf -- '- `revo push [--tag t]` — push all repos\n'
2926
+ printf -- '- `revo pr "title" [--tag t]` — create coordinated PRs via gh CLI\n'
2927
+ printf -- '- `revo exec "cmd" [--tag t]` — run command in filtered repos\n'
2928
+ printf -- '- `revo checkout <branch> [--tag t]` — switch branch across repos\n'
2929
+ printf '\n'
2930
+ printf '### Tag filtering\n'
2931
+ printf '\n'
2932
+ printf 'All commands support `--tag <tag>` to target specific repos:\n'
2933
+ printf '\n'
2934
+ printf '```\n'
2935
+ printf 'revo exec "npm test" --tag frontend\n'
2936
+ printf 'revo sync --tag backend\n'
2937
+ printf 'revo branch hotfix --tag api\n'
2938
+ printf '```\n'
2939
+ printf '\n'
2940
+ printf '### Feature workflow\n'
2941
+ printf '\n'
2942
+ printf '```\n'
2943
+ printf 'revo feature my-feature # branches all repos\n'
2944
+ printf '# edit .revo/features/my-feature.md to describe scope\n'
2945
+ printf '# work across repos\n'
2946
+ printf 'revo commit "feat: my feature" # commits all dirty repos\n'
2947
+ printf 'revo pr "My feature" # coordinated PRs\n'
2948
+ printf '```\n'
2507
2949
  } >> "$output"
2508
2950
  }
2509
2951
 
@@ -2551,15 +2993,15 @@ cmd_context() {
2551
2993
  return 0
2552
2994
  }
2553
2995
 
2554
- # Called automatically from cmd_clone if CLAUDE.md does not yet exist.
2555
- context_autogenerate_if_missing() {
2996
+ # Called automatically from cmd_clone after a successful clone batch.
2997
+ # Always rewrites the workspace CLAUDE.md so newly cloned repos show up.
2998
+ context_regenerate_silent() {
2556
2999
  [[ -z "$REVO_WORKSPACE_ROOT" ]] && return 0
2557
- local output="$REVO_WORKSPACE_ROOT/CLAUDE.md"
2558
- [[ -f "$output" ]] && return 0
2559
3000
  [[ $YAML_REPO_COUNT -eq 0 ]] && return 0
2560
3001
 
3002
+ local output="$REVO_WORKSPACE_ROOT/CLAUDE.md"
2561
3003
  _context_write_file "$output"
2562
- ui_info "$(ui_dim "Auto-generated CLAUDE.md for Claude Code")"
3004
+ ui_info "$(ui_dim "Regenerated CLAUDE.md for Claude Code")"
2563
3005
  return 0
2564
3006
  }
2565
3007
 
@@ -3144,7 +3586,8 @@ Revo - Claude-first multi-repo workspace manager v$REVO_VERSION
3144
3586
  Usage: revo <command> [options]
3145
3587
 
3146
3588
  Workspace commands:
3147
- init Initialize a new workspace
3589
+ init Initialize a new workspace (auto-detects existing repos)
3590
+ detect Bootstrap revo around existing git repos in cwd
3148
3591
  add URL [--tags TAGS] Add a repository to the workspace
3149
3592
  clone [--tag TAG] Clone configured repositories
3150
3593
  list [--tag TAG] List configured repositories
@@ -3207,6 +3650,9 @@ main() {
3207
3650
  init)
3208
3651
  cmd_init "$@"
3209
3652
  ;;
3653
+ detect)
3654
+ cmd_detect "$@"
3655
+ ;;
3210
3656
  clone)
3211
3657
  cmd_clone "$@"
3212
3658
  ;;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@revotools/cli",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "Claude-first multi-repo workspace manager (fork of Mars)",
5
5
  "bin": {
6
6
  "revo": "./dist/revo"