@damian87/omp 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/.github/skills/schedule/SKILL.md +71 -0
- package/.github/skills/team/SKILL.md +82 -57
- package/.github/skills/team/scripts/team-launch.sh +140 -42
- package/README.md +21 -2
- package/catalog/skills-general.json +2 -2
- package/dist/src/cli.d.ts +1 -7
- package/dist/src/cli.js +128 -1
- package/dist/src/cli.js.map +1 -1
- package/dist/src/commands/registry.d.ts +3 -0
- package/dist/src/commands/registry.js +11 -0
- package/dist/src/commands/registry.js.map +1 -0
- package/dist/src/commands/suggest.d.ts +19 -0
- package/dist/src/commands/suggest.js +158 -0
- package/dist/src/commands/suggest.js.map +1 -0
- package/dist/src/commands/types.d.ts +16 -0
- package/dist/src/commands/types.js +2 -0
- package/dist/src/commands/types.js.map +1 -0
- package/dist/src/instructions-memory.js +8 -15
- package/dist/src/instructions-memory.js.map +1 -1
- package/dist/src/schedule/commands.d.ts +34 -0
- package/dist/src/schedule/commands.js +130 -0
- package/dist/src/schedule/commands.js.map +1 -0
- package/dist/src/schedule/installer.d.ts +20 -0
- package/dist/src/schedule/installer.js +76 -0
- package/dist/src/schedule/installer.js.map +1 -0
- package/dist/src/schedule/installers/crontab.d.ts +17 -0
- package/dist/src/schedule/installers/crontab.js +112 -0
- package/dist/src/schedule/installers/crontab.js.map +1 -0
- package/dist/src/schedule/installers/launchd.d.ts +22 -0
- package/dist/src/schedule/installers/launchd.js +125 -0
- package/dist/src/schedule/installers/launchd.js.map +1 -0
- package/dist/src/schedule/installers/systemd.d.ts +15 -0
- package/dist/src/schedule/installers/systemd.js +136 -0
- package/dist/src/schedule/installers/systemd.js.map +1 -0
- package/dist/src/schedule/job-store.d.ts +21 -0
- package/dist/src/schedule/job-store.js +102 -0
- package/dist/src/schedule/job-store.js.map +1 -0
- package/dist/src/schedule/lock.d.ts +9 -0
- package/dist/src/schedule/lock.js +83 -0
- package/dist/src/schedule/lock.js.map +1 -0
- package/dist/src/schedule/paths.d.ts +15 -0
- package/dist/src/schedule/paths.js +36 -0
- package/dist/src/schedule/paths.js.map +1 -0
- package/dist/src/schedule/runner.d.ts +8 -0
- package/dist/src/schedule/runner.js +151 -0
- package/dist/src/schedule/runner.js.map +1 -0
- package/dist/src/schedule/types.d.ts +60 -0
- package/dist/src/schedule/types.js +5 -0
- package/dist/src/schedule/types.js.map +1 -0
- package/dist/src/team/index.d.ts +1 -0
- package/dist/src/team/index.js +1 -0
- package/dist/src/team/index.js.map +1 -1
- package/dist/src/team/pane-monitor.d.ts +39 -0
- package/dist/src/team/pane-monitor.js +128 -0
- package/dist/src/team/pane-monitor.js.map +1 -0
- package/dist/src/team/runtime.js +12 -1
- package/dist/src/team/runtime.js.map +1 -1
- package/dist/src/team/tmux.d.ts +12 -0
- package/dist/src/team/tmux.js +38 -0
- package/dist/src/team/tmux.js.map +1 -1
- package/docs/research/2026-06-01-schedule-cron-feature.md +346 -0
- package/package.json +2 -1
- package/scripts/lib/schedule-results.mjs +88 -0
- package/scripts/session-start.mjs +8 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: schedule
|
|
3
|
+
description: Register a durable local cron job that re-runs a prompt on a schedule (e.g. "check the PR every 15 min"). Use with /schedule when you want a fresh agent session to fire unattended on an interval and survive reboot.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Schedule
|
|
7
|
+
|
|
8
|
+
Use `/schedule` to register, inspect, or remove **durable local cron jobs**. Each
|
|
9
|
+
job spawns a fresh non-interactive agent session at its interval, survives
|
|
10
|
+
machine restart (via the OS scheduler — launchd / systemd / crontab), and writes
|
|
11
|
+
results back to the project so the next session sees them.
|
|
12
|
+
|
|
13
|
+
This is the in-session front-end to the `omp schedule` CLI. You (the agent) run
|
|
14
|
+
the `omp schedule …` commands on the user's behalf.
|
|
15
|
+
|
|
16
|
+
## When to use
|
|
17
|
+
|
|
18
|
+
- The user wants something to run on a repeat: "check the PR every 15 minutes",
|
|
19
|
+
"run the tests nightly", "poll the deployment hourly".
|
|
20
|
+
- The work should keep happening after the terminal closes and across reboots.
|
|
21
|
+
|
|
22
|
+
## Do not use when
|
|
23
|
+
|
|
24
|
+
- The user wants a one-shot task now — just do it.
|
|
25
|
+
- The user wants to keep iterating within this session — use `/ralph` or `/loop`.
|
|
26
|
+
|
|
27
|
+
## Steps
|
|
28
|
+
|
|
29
|
+
1. **Clarify** the prompt to run and the cadence (turn natural language into a
|
|
30
|
+
5-field cron expression, local time). E.g. "every 15 min" → `*/15 * * * *`;
|
|
31
|
+
"weekdays at 9am" → `0 9 * * 1-5`.
|
|
32
|
+
2. **Decide tool access.** By default jobs run WITHOUT `--allow-all-tools`, so the
|
|
33
|
+
unattended agent is limited to read-only/allowlisted tools. If the job must
|
|
34
|
+
act (edit files, run commands, push), add `--allow-all-tools` and confirm with
|
|
35
|
+
the user first — it runs unattended with full access.
|
|
36
|
+
3. **Register** the job:
|
|
37
|
+
```bash
|
|
38
|
+
omp schedule add --id <id> --cron "<expr>" --prompt "<text>" \
|
|
39
|
+
[--allow-all-tools] [--cwd <dir>] [--model <m>] [--timeout <ms>] \
|
|
40
|
+
[--max-runs <n>] [--ttl-hours <h>] --json
|
|
41
|
+
```
|
|
42
|
+
Jobs auto-expire after 72h by default (`--ttl-hours`) — set a longer TTL or a
|
|
43
|
+
`--max-runs` cap as needed. Use `--dry-run` to preview the OS entry first.
|
|
44
|
+
4. **Confirm** by listing: `omp schedule list --json`.
|
|
45
|
+
5. **Trigger now** to test it once: `omp schedule run-now --id <id>`.
|
|
46
|
+
6. **Inspect** results: `omp schedule status --id <id> --json` (recent results
|
|
47
|
+
are also surfaced automatically at the start of future sessions).
|
|
48
|
+
7. **Remove** when done: `omp schedule remove --id <id>` (fully uninstalls the OS
|
|
49
|
+
entry; do NOT delete `.omp/state/schedule/` by hand).
|
|
50
|
+
|
|
51
|
+
## Safety
|
|
52
|
+
|
|
53
|
+
- Scheduled runs are unattended. Keep prompts specific and bounded.
|
|
54
|
+
- `--allow-all-tools` grants full tool access with no human in the loop — opt in
|
|
55
|
+
deliberately, scope the `--cwd`, and prefer a `--max-runs`/`--ttl-hours` cap.
|
|
56
|
+
- Overlapping runs are prevented automatically (one run per job at a time), and
|
|
57
|
+
every run is killed at its `--timeout` (default 5 min).
|
|
58
|
+
|
|
59
|
+
## Examples
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Babysit a PR every 15 minutes, read-only, for 24 hours
|
|
63
|
+
omp schedule add --id pr-watch --cron "*/15 * * * *" \
|
|
64
|
+
--prompt "Check open PRs on this repo and summarize any new review comments." \
|
|
65
|
+
--ttl-hours 24
|
|
66
|
+
|
|
67
|
+
# Nightly test run that may fix things (full access), capped at 7 runs
|
|
68
|
+
omp schedule add --id nightly-tests --cron "0 2 * * *" \
|
|
69
|
+
--prompt "Run the test suite; if anything fails, open an issue with the log." \
|
|
70
|
+
--allow-all-tools --max-runs 7
|
|
71
|
+
```
|
|
@@ -1,102 +1,127 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: team
|
|
3
|
-
description: Split an approved plan into parallel tmux panes
|
|
4
|
-
argument-hint: "<number of
|
|
3
|
+
description: Split an approved plan into parallel tmux panes in the current window so the user can watch agents work. Prefer this visual flow by default; use `omp team` only when the user explicitly wants background execution or runtime messaging/status APIs.
|
|
4
|
+
argument-hint: "<number of workers> <task description>"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Team — tmux-based parallel agent execution
|
|
8
8
|
|
|
9
|
-
`/team`
|
|
9
|
+
`/team` launches independent Copilot CLI agents in parallel tmux panes.
|
|
10
|
+
|
|
11
|
+
**Default behavior:** use the **split-window** flow so the user sees agents working in the current tmux window.
|
|
12
|
+
|
|
13
|
+
Use **runtime mode** (`omp team`) only when the user explicitly asks for background execution, detached monitoring, or runtime APIs like status, nudging, or worker messaging.
|
|
14
|
+
|
|
15
|
+
Two modes available:
|
|
16
|
+
|
|
17
|
+
| Mode | Command | Panes visible in | Best for |
|
|
18
|
+
|------|---------|-------------------|----------|
|
|
19
|
+
| **Split** | `team-launch.sh` | Current window | **Default**. Visual demo, watching agents work |
|
|
20
|
+
| **Runtime** | `omp team N:copilot "task"` | Separate tmux session | Explicit background jobs, task tracking, nudging, messaging |
|
|
10
21
|
|
|
11
22
|
## When to use
|
|
12
23
|
|
|
13
24
|
- Work has **independent lanes** (no shared files, no ordering constraints)
|
|
14
|
-
- You want
|
|
25
|
+
- You want parallel execution in split terminals
|
|
15
26
|
|
|
16
|
-
##
|
|
27
|
+
## Default mode — Split window (`team-launch.sh`)
|
|
17
28
|
|
|
18
|
-
|
|
29
|
+
Use this unless the user asks for background execution.
|
|
19
30
|
|
|
20
|
-
###
|
|
31
|
+
### When to choose it
|
|
21
32
|
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
- `prompt`: complete task prompt — must be self-contained with all context the agent needs (files to change, what to do, commit message)
|
|
33
|
+
- The user wants to **see** the agents working
|
|
34
|
+
- You want panes in the **current tmux window**
|
|
35
|
+
- You are demoing or smoke-testing the skill
|
|
26
36
|
|
|
27
|
-
|
|
37
|
+
### Step 1 — Write lanes JSON
|
|
28
38
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
Write a temporary lanes file at `/tmp/team-lanes-<timestamp>.json`:
|
|
39
|
+
Write a temporary file at `/tmp/team-lanes-<timestamp>.json`:
|
|
32
40
|
|
|
33
41
|
```json
|
|
34
42
|
[
|
|
35
|
-
{
|
|
36
|
-
|
|
37
|
-
"name": "Short descriptive name",
|
|
38
|
-
"prompt": "Complete self-contained task prompt for the agent..."
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
"id": "lane-b",
|
|
42
|
-
"name": "Another lane name",
|
|
43
|
-
"prompt": "Another complete task prompt..."
|
|
44
|
-
}
|
|
43
|
+
{ "id": "lane-a", "name": "Short name", "prompt": "Complete self-contained task prompt..." },
|
|
44
|
+
{ "id": "lane-b", "name": "Another lane", "prompt": "Another task prompt..." }
|
|
45
45
|
]
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
### Step
|
|
49
|
-
|
|
50
|
-
Run the launch script, passing the session name and lanes file path:
|
|
48
|
+
### Step 2 — Launch
|
|
51
49
|
|
|
50
|
+
```bash
|
|
51
|
+
bash ~/.copilot/installed-plugins/oh-my-copilot/oh-my-copilot/.github/skills/team/scripts/team-launch.sh \
|
|
52
|
+
--session "team-<name>" --lanes <lanes-file>
|
|
52
53
|
```
|
|
53
|
-
|
|
54
|
+
|
|
55
|
+
The script:
|
|
56
|
+
1. Splits the **current window** into panes
|
|
57
|
+
2. Launches `omp --madmax` in each
|
|
58
|
+
3. Auto-accepts folder trust prompts
|
|
59
|
+
4. Waits for readiness, sends prompts
|
|
60
|
+
5. Monitors completion and prints a results summary
|
|
61
|
+
|
|
62
|
+
### Step 3 — Report
|
|
63
|
+
|
|
64
|
+
The script blocks and prints all results. Relay the output to the user.
|
|
65
|
+
|
|
66
|
+
## Optional mode — Runtime (`omp team`)
|
|
67
|
+
|
|
68
|
+
Choose this only when the user explicitly wants the team to run in the background or needs runtime features.
|
|
69
|
+
|
|
70
|
+
### When to choose it
|
|
71
|
+
|
|
72
|
+
- The user asked for a **background** team
|
|
73
|
+
- You need `omp team status`, shutdown, or runtime task APIs
|
|
74
|
+
- You do **not** need the panes in the current tmux window
|
|
75
|
+
|
|
76
|
+
### Launch
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
omp team <N>:copilot "<task description>"
|
|
54
80
|
```
|
|
55
81
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
-
|
|
60
|
-
|
|
82
|
+
The runtime automatically:
|
|
83
|
+
1. Creates a tmux session with split panes
|
|
84
|
+
2. Launches `copilot --allow-all-tools` in each pane
|
|
85
|
+
3. Auto-accepts folder trust prompts
|
|
86
|
+
4. Waits for readiness, then sends the task prompt
|
|
87
|
+
5. Tracks task state, heartbeats, and supports idle-nudging
|
|
61
88
|
|
|
62
|
-
###
|
|
89
|
+
### Monitor and cleanup
|
|
63
90
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
91
|
+
```bash
|
|
92
|
+
omp team status <team-name> # check progress
|
|
93
|
+
tmux attach -t omp-team-<name> # watch panes live
|
|
94
|
+
omp team shutdown <team-name> # kill when done
|
|
95
|
+
```
|
|
69
96
|
|
|
70
97
|
## Prerequisites
|
|
71
98
|
|
|
72
|
-
- `tmux` installed and running
|
|
73
|
-
- `omp`
|
|
74
|
-
-
|
|
75
|
-
- `jq` for JSON parsing
|
|
99
|
+
- `tmux` installed and session running
|
|
100
|
+
- `omp` on PATH
|
|
101
|
+
- `jq` for JSON parsing (split mode only)
|
|
76
102
|
|
|
77
|
-
##
|
|
103
|
+
## Task / prompt guidelines
|
|
78
104
|
|
|
79
|
-
Each
|
|
105
|
+
Each task must be **self-contained**. The agent has no context from this session. Include:
|
|
80
106
|
- Exact files or directories to work in
|
|
81
|
-
- What to do (fix, upgrade,
|
|
82
|
-
- How to verify (run tests,
|
|
107
|
+
- What to do (fix, upgrade, etc.)
|
|
108
|
+
- How to verify (run tests, etc.)
|
|
83
109
|
- Commit message to use
|
|
84
110
|
|
|
85
|
-
### Good
|
|
111
|
+
### Good example
|
|
86
112
|
|
|
87
|
-
>
|
|
113
|
+
> In src/auth/login.ts, replace bcrypt with argon2. Update the import, change the verify call, run `npm test -- --grep auth`. Commit: "refactor: switch to argon2".
|
|
88
114
|
|
|
89
|
-
### Bad
|
|
115
|
+
### Bad example
|
|
90
116
|
|
|
91
|
-
> Fix the auth module. (Too vague
|
|
117
|
+
> Fix the auth module. (Too vague)
|
|
92
118
|
|
|
93
119
|
## Composition
|
|
94
120
|
|
|
95
|
-
Use `/ralplan` before `/team` to produce the plan
|
|
121
|
+
Use `/ralplan` before `/team` to produce the plan. Use `/verify` after completion.
|
|
96
122
|
|
|
97
123
|
## Limitations
|
|
98
124
|
|
|
99
|
-
- Each pane is an independent
|
|
100
|
-
-
|
|
101
|
-
-
|
|
102
|
-
- Best for independent, non-conflicting work streams
|
|
125
|
+
- Each pane is an independent session — no shared state
|
|
126
|
+
- Workers can message each other via `omp team api send-message` (runtime mode only)
|
|
127
|
+
- If tasks depend on each other, use `/ralph` instead
|
|
@@ -7,9 +7,11 @@ set -euo pipefail
|
|
|
7
7
|
# Usage:
|
|
8
8
|
# team-launch.sh --session <name> --lanes <lanes.json>
|
|
9
9
|
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# for
|
|
10
|
+
# Flow:
|
|
11
|
+
# 1. Split panes and launch agent CLI in each
|
|
12
|
+
# 2. Wait for each agent to be ready (auto-accept folder trust prompts)
|
|
13
|
+
# 3. Send lane prompts via send-keys
|
|
14
|
+
# 4. Monitor until all agents finish, then print summary
|
|
13
15
|
|
|
14
16
|
SESSION=""
|
|
15
17
|
LANES_FILE=""
|
|
@@ -33,13 +35,11 @@ if [[ ! -f "$LANES_FILE" ]]; then
|
|
|
33
35
|
fi
|
|
34
36
|
|
|
35
37
|
if ! command -v tmux &>/dev/null; then
|
|
36
|
-
echo "tmux not found" >&2
|
|
37
|
-
exit 1
|
|
38
|
+
echo "tmux not found" >&2; exit 1
|
|
38
39
|
fi
|
|
39
40
|
|
|
40
41
|
if [[ -z "${TMUX:-}" ]]; then
|
|
41
|
-
echo "Not inside a tmux session. Run this from within tmux." >&2
|
|
42
|
-
exit 1
|
|
42
|
+
echo "Not inside a tmux session. Run this from within tmux." >&2; exit 1
|
|
43
43
|
fi
|
|
44
44
|
|
|
45
45
|
if command -v omp &>/dev/null; then
|
|
@@ -47,31 +47,60 @@ if command -v omp &>/dev/null; then
|
|
|
47
47
|
elif command -v copilot &>/dev/null; then
|
|
48
48
|
AGENT_CMD="copilot"
|
|
49
49
|
else
|
|
50
|
-
echo "Neither omp nor copilot CLI found" >&2
|
|
51
|
-
exit 1
|
|
50
|
+
echo "Neither omp nor copilot CLI found" >&2; exit 1
|
|
52
51
|
fi
|
|
53
52
|
|
|
54
53
|
LANE_COUNT=$(jq length "$LANES_FILE")
|
|
55
54
|
if [[ "$LANE_COUNT" -lt 1 ]]; then
|
|
56
|
-
echo "No lanes defined in $LANES_FILE" >&2
|
|
57
|
-
exit 1
|
|
55
|
+
echo "No lanes defined in $LANES_FILE" >&2; exit 1
|
|
58
56
|
fi
|
|
59
57
|
|
|
60
58
|
CWD=$(pwd)
|
|
61
|
-
|
|
59
|
+
POLL="${TEAM_POLL_INTERVAL:-2}"
|
|
60
|
+
MAX_READY="${TEAM_MAX_READY_WAIT:-60}"
|
|
61
|
+
MAX_DONE="${TEAM_MAX_COMPLETION_WAIT:-300}"
|
|
62
|
+
|
|
63
|
+
# ── helpers ──────────────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
# Capture visible pane content (last N lines)
|
|
66
|
+
pane_text() { tmux capture-pane -t "$1" -p -S "-${2:-20}" 2>/dev/null || true; }
|
|
67
|
+
|
|
68
|
+
# Wait until the agent CLI is fully ready ('/ commands' status bar visible).
|
|
69
|
+
# Auto-accepts the folder-trust dialog if it appears.
|
|
70
|
+
wait_for_ready() {
|
|
71
|
+
local pane="$1" elapsed=0 accepted=0
|
|
72
|
+
while (( elapsed < MAX_READY )); do
|
|
73
|
+
local txt
|
|
74
|
+
txt=$(pane_text "$pane" 25)
|
|
75
|
+
|
|
76
|
+
# Ready: the '/ commands' status bar means the CLI input prompt is active
|
|
77
|
+
if echo "$txt" | grep -q '/ commands'; then
|
|
78
|
+
return 0
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
# Auto-accept folder trust dialog
|
|
82
|
+
if (( accepted == 0 )) && echo "$txt" | grep -q 'Do you trust'; then
|
|
83
|
+
tmux send-keys -t "$pane" C-m
|
|
84
|
+
accepted=1
|
|
85
|
+
echo " ↳ Auto-accepted folder trust for $pane"
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
sleep "$POLL"
|
|
89
|
+
elapsed=$((elapsed + POLL))
|
|
90
|
+
done
|
|
91
|
+
return 1
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# ── step 1: create panes ─────────────────────────────────────────────
|
|
62
95
|
|
|
63
96
|
echo "🚀 Splitting current window into $LANE_COUNT panes ($SESSION)"
|
|
64
97
|
echo ""
|
|
65
98
|
|
|
66
|
-
# Collect pane IDs as we create them
|
|
67
99
|
PANE_IDS=()
|
|
68
|
-
|
|
69
100
|
for i in $(seq 0 $((LANE_COUNT - 1))); do
|
|
70
101
|
LANE_NAME=$(jq -r ".[$i].name" "$LANES_FILE")
|
|
71
|
-
LANE_PROMPT=$(jq -r ".[$i].prompt" "$LANES_FILE")
|
|
72
102
|
LANE_ID=$(jq -r ".[$i].id" "$LANES_FILE")
|
|
73
103
|
|
|
74
|
-
# Split: alternate horizontal/vertical for a grid
|
|
75
104
|
if (( i == 0 )); then
|
|
76
105
|
PANE_ID=$(tmux split-window -h -c "$CWD" -P -F '#{pane_id}')
|
|
77
106
|
elif (( i % 2 == 1 )); then
|
|
@@ -79,54 +108,123 @@ for i in $(seq 0 $((LANE_COUNT - 1))); do
|
|
|
79
108
|
else
|
|
80
109
|
PANE_ID=$(tmux split-window -v -t "${PANE_IDS[$((i-2))]}" -c "$CWD" -P -F '#{pane_id}')
|
|
81
110
|
fi
|
|
82
|
-
|
|
83
111
|
PANE_IDS+=("$PANE_ID")
|
|
84
112
|
|
|
85
|
-
# Set pane title
|
|
86
113
|
tmux select-pane -t "$PANE_ID" -T "$LANE_ID: $LANE_NAME"
|
|
114
|
+
tmux send-keys -t "$PANE_ID" "$AGENT_CMD" C-m
|
|
87
115
|
|
|
88
|
-
|
|
89
|
-
tmux send-keys -t "$PANE_ID" "echo '═══ $LANE_NAME ═══' && $AGENT_CMD" C-m
|
|
90
|
-
|
|
91
|
-
echo " ✅ Pane $PANE_ID → $LANE_NAME (launching agent...)"
|
|
116
|
+
echo " ✅ Pane $PANE_ID → $LANE_NAME"
|
|
92
117
|
done
|
|
93
118
|
|
|
94
|
-
# Rebalance layout
|
|
95
119
|
tmux select-layout tiled
|
|
96
120
|
|
|
97
|
-
#
|
|
121
|
+
# ── step 2: wait for readiness ───────────────────────────────────────
|
|
122
|
+
|
|
98
123
|
echo ""
|
|
99
|
-
echo "⏳ Waiting
|
|
100
|
-
sleep "$WAIT_SECS"
|
|
124
|
+
echo "⏳ Waiting for agents to initialise (up to ${MAX_READY}s)..."
|
|
101
125
|
|
|
102
|
-
|
|
126
|
+
for i in $(seq 0 $((LANE_COUNT - 1))); do
|
|
127
|
+
PANE_ID="${PANE_IDS[$i]}"
|
|
128
|
+
LANE_NAME=$(jq -r ".[$i].name" "$LANES_FILE")
|
|
129
|
+
if wait_for_ready "$PANE_ID"; then
|
|
130
|
+
echo " ✅ $PANE_ID ($LANE_NAME) ready"
|
|
131
|
+
else
|
|
132
|
+
echo " ⚠️ $PANE_ID ($LANE_NAME) not ready after ${MAX_READY}s — sending anyway"
|
|
133
|
+
fi
|
|
134
|
+
done
|
|
135
|
+
|
|
136
|
+
# ── step 3: send prompts (literal text + Enter separately) ───────────
|
|
137
|
+
|
|
138
|
+
echo ""
|
|
139
|
+
echo "📨 Sending prompts..."
|
|
103
140
|
for i in $(seq 0 $((LANE_COUNT - 1))); do
|
|
104
141
|
LANE_PROMPT=$(jq -r ".[$i].prompt" "$LANES_FILE")
|
|
105
142
|
LANE_NAME=$(jq -r ".[$i].name" "$LANES_FILE")
|
|
106
143
|
PANE_ID="${PANE_IDS[$i]}"
|
|
107
144
|
|
|
108
|
-
#
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
# Send via tmux send-keys -l (literal) then Enter
|
|
113
|
-
tmux send-keys -t "$PANE_ID" -l "$(cat "$PROMPT_FILE")"
|
|
145
|
+
# -l = literal (no key interpretation), then C-m = Enter as a separate call
|
|
146
|
+
tmux send-keys -t "$PANE_ID" -l "$LANE_PROMPT"
|
|
147
|
+
sleep 0.3
|
|
114
148
|
tmux send-keys -t "$PANE_ID" C-m
|
|
115
149
|
|
|
116
|
-
echo " 📨 Sent
|
|
150
|
+
echo " 📨 Sent to $PANE_ID ($LANE_NAME)"
|
|
117
151
|
done
|
|
118
152
|
|
|
119
|
-
# Switch focus back to the original (leader) pane
|
|
120
153
|
tmux select-pane -t '{left}'
|
|
121
154
|
|
|
155
|
+
# ── step 4: monitor completion ───────────────────────────────────────
|
|
156
|
+
|
|
122
157
|
echo ""
|
|
123
|
-
echo "
|
|
158
|
+
echo "⏳ Monitoring agents for completion (up to ${MAX_DONE}s)..."
|
|
159
|
+
|
|
160
|
+
# Brief pause so agents start processing before we poll
|
|
161
|
+
sleep 5
|
|
162
|
+
|
|
163
|
+
# State per lane: 0=waiting-for-busy, 1=busy, 2=done
|
|
164
|
+
LANE_STATE=()
|
|
165
|
+
for i in $(seq 0 $((LANE_COUNT - 1))); do LANE_STATE[$i]=0; done
|
|
166
|
+
|
|
167
|
+
COMPLETED=0
|
|
168
|
+
ELAPSED=0
|
|
169
|
+
while (( COMPLETED < LANE_COUNT && ELAPSED < MAX_DONE )); do
|
|
170
|
+
sleep "$POLL"
|
|
171
|
+
ELAPSED=$((ELAPSED + POLL))
|
|
172
|
+
|
|
173
|
+
for i in $(seq 0 $((LANE_COUNT - 1))); do
|
|
174
|
+
[[ "${LANE_STATE[$i]}" == "2" ]] && continue
|
|
175
|
+
|
|
176
|
+
PANE_ID="${PANE_IDS[$i]}"
|
|
177
|
+
LANE_NAME=$(jq -r ".[$i].name" "$LANES_FILE")
|
|
178
|
+
|
|
179
|
+
# Pane died?
|
|
180
|
+
if ! tmux list-panes -a -F '#{pane_id}' 2>/dev/null | grep -q "^${PANE_ID}$"; then
|
|
181
|
+
echo " ❌ $PANE_ID ($LANE_NAME) — pane died"
|
|
182
|
+
LANE_STATE[$i]=2; COMPLETED=$((COMPLETED + 1)); continue
|
|
183
|
+
fi
|
|
184
|
+
|
|
185
|
+
local_capture=$(pane_text "$PANE_ID" 15)
|
|
186
|
+
|
|
187
|
+
# State 0→1: agent started working (response marker ● appears)
|
|
188
|
+
if [[ "${LANE_STATE[$i]}" == "0" ]]; then
|
|
189
|
+
if echo "$local_capture" | grep -q '●'; then
|
|
190
|
+
LANE_STATE[$i]=1
|
|
191
|
+
fi
|
|
192
|
+
continue
|
|
193
|
+
fi
|
|
194
|
+
|
|
195
|
+
# State 1→2: agent finished (back to idle — '/ commands' in last 5 lines)
|
|
196
|
+
if echo "$local_capture" | tail -5 | grep -q '/ commands'; then
|
|
197
|
+
echo " ✅ $PANE_ID ($LANE_NAME) — agent finished"
|
|
198
|
+
LANE_STATE[$i]=2; COMPLETED=$((COMPLETED + 1))
|
|
199
|
+
fi
|
|
200
|
+
done
|
|
201
|
+
done
|
|
202
|
+
|
|
124
203
|
echo ""
|
|
125
|
-
|
|
204
|
+
if (( COMPLETED == LANE_COUNT )); then
|
|
205
|
+
echo "🎉 All $LANE_COUNT agents completed!"
|
|
206
|
+
else
|
|
207
|
+
echo "⏰ Timeout — $((LANE_COUNT - COMPLETED)) agent(s) still running"
|
|
208
|
+
fi
|
|
209
|
+
|
|
210
|
+
# ── summary ──────────────────────────────────────────────────────────
|
|
211
|
+
|
|
126
212
|
echo ""
|
|
127
|
-
echo "
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
213
|
+
echo "═══ Results Summary ═══"
|
|
214
|
+
for i in $(seq 0 $((LANE_COUNT - 1))); do
|
|
215
|
+
PANE_ID="${PANE_IDS[$i]}"
|
|
216
|
+
LANE_NAME=$(jq -r ".[$i].name" "$LANES_FILE")
|
|
217
|
+
LANE_ID=$(jq -r ".[$i].id" "$LANES_FILE")
|
|
218
|
+
echo ""
|
|
219
|
+
echo "── $LANE_ID: $LANE_NAME ($PANE_ID) ──"
|
|
220
|
+
if tmux list-panes -a -F '#{pane_id}' 2>/dev/null | grep -q "^${PANE_ID}$"; then
|
|
221
|
+
pane_text "$PANE_ID" 40 | grep -E '●|✅|❌|⚠|error|Error|FAIL|PASS|done|Done' | tail -10 || echo " (no notable output captured)"
|
|
222
|
+
else
|
|
223
|
+
echo " (pane no longer exists)"
|
|
224
|
+
fi
|
|
225
|
+
done
|
|
226
|
+
|
|
131
227
|
echo ""
|
|
132
|
-
echo "
|
|
228
|
+
echo "Pane IDs: ${PANE_IDS[*]}"
|
|
229
|
+
echo "Navigate: Ctrl-b + arrow keys"
|
|
230
|
+
echo "💡 Agents are interactive — send follow-up prompts to any pane"
|
package/README.md
CHANGED
|
@@ -82,6 +82,7 @@ That's it.
|
|
|
82
82
|
### Developer Experience
|
|
83
83
|
|
|
84
84
|
- **MCP server** ships with `notepad`, `project-memory`, `shared-memory`, `state`, and `trace` tools out of the box
|
|
85
|
+
- **Lightweight Copilot context** — managed instructions keep only the repo goal plus on-demand memory commands; set `OMP_DISABLE_INSTRUCTIONS_MEMORY=1` to skip writing the managed block entirely
|
|
85
86
|
- **File-state coordination** — outbox JSONL + byte cursor, atomic `O_EXCL` task locks, optimistic CAS on claim
|
|
86
87
|
- **Idle nudge** — content-based pane idle detection that pokes stuck workers
|
|
87
88
|
- **Mode-state loops** — single source of truth per loop (Ralph/Ultrawork/UltraQA state files)
|
|
@@ -113,6 +114,7 @@ These run **inside a Copilot CLI session** after the plugin is installed.
|
|
|
113
114
|
| `/grill-me` | Stress-test a plan with Socratic questions | `/grill-me` |
|
|
114
115
|
| `/caveman` | Ultra-compressed communication mode | `/caveman` |
|
|
115
116
|
| `/worktree` | Git worktree-based parallel branch work | `/worktree` |
|
|
117
|
+
| `/schedule` | Durable local cron job — re-runs a prompt on a schedule, survives reboot | `/schedule "check the PR every 15 min"` |
|
|
116
118
|
|
|
117
119
|
---
|
|
118
120
|
|
|
@@ -182,6 +184,7 @@ omp list # show discovered skills and agents
|
|
|
182
184
|
omp setup [--dry-run] [--scope project|user]
|
|
183
185
|
omp launch -- [copilot flags…] # forward arbitrary args to copilot
|
|
184
186
|
omp --madmax -p "edit src/foo.ts" # bare-flag, maps to copilot --yolo
|
|
187
|
+
omp suggest "fix flaky tests" # recommend a slash-skill workflow
|
|
185
188
|
omp team 3:executor "fix all type errors" # spawn tmux workers
|
|
186
189
|
omp team status <name>
|
|
187
190
|
omp team shutdown <name>
|
|
@@ -189,6 +192,11 @@ omp ralph start "<task>" [--max-iterations N]
|
|
|
189
192
|
omp ultrawork start "<objective>" [--task-count N]
|
|
190
193
|
omp ultraqa start "<goal>" [--max-cycles N]
|
|
191
194
|
omp council "<question>" [--models a,b,c] [--context @file] [--json] # multi-model council
|
|
195
|
+
omp schedule add --id <id> --cron "*/15 * * * *" --prompt "<text>" [--allow-all-tools] [--cwd <dir>] [--model <m>] [--timeout <ms>] [--max-runs N] [--ttl-hours H] [--dry-run]
|
|
196
|
+
omp schedule list # registered jobs + OS-install status
|
|
197
|
+
omp schedule status <id> # last run + result summary
|
|
198
|
+
omp schedule run-now <id> # trigger one run immediately
|
|
199
|
+
omp schedule remove <id> # uninstall the OS entry + delete the job
|
|
192
200
|
omp mcp # MCP server over stdio
|
|
193
201
|
omp catalog list | validate | capability <id>
|
|
194
202
|
omp jira render <plan-file>
|
|
@@ -199,6 +207,17 @@ Environment overrides:
|
|
|
199
207
|
|
|
200
208
|
- `OMP_PLUGIN_ROOT` — path to the plugin checkout (with `OMC_PLUGIN_ROOT` accepted for back-compat)
|
|
201
209
|
- `OMP_COPILOT_BIN` — alternate `copilot` binary
|
|
210
|
+
- `OMP_BIN` — absolute path to the `omp` wrapper written into OS-scheduler entries (overrides `which omp`)
|
|
211
|
+
|
|
212
|
+
**Scheduled jobs** register a durable per-job entry with the OS scheduler (macOS launchd,
|
|
213
|
+
Linux systemd-user timers, or a managed `crontab` block as a cross-platform fallback) that
|
|
214
|
+
invokes `omp schedule run --id <id>` on the cron schedule. Each tick spawns a fresh agent
|
|
215
|
+
session; overlapping runs are locked out and every run is killed at its `--timeout`
|
|
216
|
+
(default 5 min). Jobs default to **read-only** (`--allow-all-tools` is opt-in and prints a
|
|
217
|
+
warning) and auto-expire after 72h unless `--ttl-hours`/`--max-runs` say otherwise. Recent
|
|
218
|
+
run results are surfaced automatically at the start of new Copilot sessions. Always use
|
|
219
|
+
`omp schedule remove`, never delete `.omp/state/schedule/` by hand, so the OS entry is
|
|
220
|
+
uninstalled cleanly.
|
|
202
221
|
|
|
203
222
|
---
|
|
204
223
|
|
|
@@ -218,9 +237,9 @@ Auto-snapshot the working tree before any tool-driven file edit. `omp rollback [
|
|
|
218
237
|
|
|
219
238
|
One command to consult an alternate provider CLI (`claude`, `codex`, `gemini`) and save the response as a markdown artifact under `.omp/artifacts/ask/`. Same surface in-session via `/ask`.
|
|
220
239
|
|
|
221
|
-
### v0.5 — Scheduled tasks
|
|
240
|
+
### v0.5 — Scheduled tasks ✅ (shipped)
|
|
222
241
|
|
|
223
|
-
|
|
242
|
+
Durable local cron: `omp schedule add --id pr-watch --cron "*/15 * * * *" --prompt "…"` plus `/schedule` in-session. Each job registers an OS-scheduler entry (launchd / systemd-user / crontab fallback) that fires a fresh agent session, survives reboot, locks out overlap, and surfaces results at the next session start. Follow-ups: natural-language cron parsing, notification-gateway delivery, pause/resume/edit, and an orphan-sweep (`omp schedule gc`).
|
|
224
243
|
|
|
225
244
|
### v0.6 — Browser tool (MCP)
|
|
226
245
|
|
|
@@ -91,8 +91,8 @@
|
|
|
91
91
|
"source": ".github/skills/team/SKILL.md",
|
|
92
92
|
"sourcePath": ".github/skills/team/SKILL.md",
|
|
93
93
|
"canonicalPath": ".github/skills/team/SKILL.md",
|
|
94
|
-
"description": "Split approved plans into
|
|
95
|
-
"summary": "Split approved plans into
|
|
94
|
+
"description": "Split approved plans into visible tmux lanes in the current window by default.",
|
|
95
|
+
"summary": "Split approved plans into visible tmux lanes by default.",
|
|
96
96
|
"support": "project-skill",
|
|
97
97
|
"aliases": [],
|
|
98
98
|
"slashCommands": [
|
package/dist/src/cli.d.ts
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
ok: boolean;
|
|
4
|
-
exitCode?: number;
|
|
5
|
-
output?: unknown;
|
|
6
|
-
message?: string;
|
|
7
|
-
}
|
|
2
|
+
import type { CliResult } from "./commands/types.js";
|
|
8
3
|
export declare function runCli(argv?: string[]): Promise<CliResult>;
|
|
9
4
|
/** Parse a --models value: comma-separated `model` or `model:role:weight` tokens. */
|
|
10
5
|
export declare function parseModelsFlag(value: string): {
|
|
@@ -14,4 +9,3 @@ export declare function parseModelsFlag(value: string): {
|
|
|
14
9
|
}[];
|
|
15
10
|
/** Parse a numeric flag as a finite positive integer; throw on malformed input. */
|
|
16
11
|
export declare function parsePositiveIntFlag(value: string | undefined, flag: string): number | undefined;
|
|
17
|
-
export {};
|