@damian87/omp 0.4.1 → 0.6.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/agents/researcher.md +7 -6
- package/.github/copilot-instructions.md +23 -0
- package/.github/skills/daily-log/SKILL.md +64 -0
- package/.github/skills/goal/SKILL.md +33 -0
- package/.github/skills/schedule/SKILL.md +71 -0
- package/README.md +19 -2
- package/dist/src/cli.js +272 -9
- package/dist/src/cli.js.map +1 -1
- package/dist/src/comms/index.d.ts +116 -0
- package/dist/src/comms/index.js +258 -0
- package/dist/src/comms/index.js.map +1 -0
- package/dist/src/comms/resolve-session.d.ts +35 -0
- package/dist/src/comms/resolve-session.js +53 -0
- package/dist/src/comms/resolve-session.js.map +1 -0
- package/dist/src/daily-log.d.ts +18 -0
- package/dist/src/daily-log.js +138 -0
- package/dist/src/daily-log.js.map +1 -0
- package/dist/src/goal.d.ts +4 -0
- package/dist/src/goal.js +44 -0
- package/dist/src/goal.js.map +1 -0
- package/dist/src/instructions-memory.d.ts +9 -0
- package/dist/src/instructions-memory.js +72 -0
- package/dist/src/instructions-memory.js.map +1 -0
- package/dist/src/mcp/tools/daily-log.d.ts +2 -0
- package/dist/src/mcp/tools/daily-log.js +148 -0
- package/dist/src/mcp/tools/daily-log.js.map +1 -0
- package/dist/src/omp-root.d.ts +1 -0
- package/dist/src/omp-root.js +19 -0
- package/dist/src/omp-root.js.map +1 -0
- package/dist/src/project-memory.d.ts +13 -0
- package/dist/src/project-memory.js +105 -0
- package/dist/src/project-memory.js.map +1 -0
- 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/state.d.ts +17 -0
- package/dist/src/state.js +101 -0
- package/dist/src/state.js.map +1 -0
- package/dist/src/trace.d.ts +19 -0
- package/dist/src/trace.js +74 -0
- package/dist/src/trace.js.map +1 -0
- package/dist/test/catalog.test.d.ts +1 -0
- package/dist/test/catalog.test.js +21 -0
- package/dist/test/catalog.test.js.map +1 -0
- package/dist/test/jira.test.d.ts +1 -0
- package/dist/test/jira.test.js +26 -0
- package/dist/test/jira.test.js.map +1 -0
- package/dist/test/lint.test.d.ts +1 -0
- package/dist/test/lint.test.js +9 -0
- package/dist/test/lint.test.js.map +1 -0
- package/dist/test/sync.test.d.ts +1 -0
- package/dist/test/sync.test.js +15 -0
- package/dist/test/sync.test.js.map +1 -0
- package/docs/research/2026-06-01-schedule-cron-feature.md +346 -0
- package/package.json +1 -1
- package/scripts/lib/daily-log.mjs +155 -0
- package/scripts/lib/hook-output.mjs +2 -1
- package/scripts/lib/omp-root.mjs +15 -0
- package/scripts/lib/project-memory.mjs +21 -0
- package/scripts/lib/schedule-results.mjs +88 -0
- package/scripts/prompt-submit.mjs +14 -2
- package/scripts/session-end.mjs +6 -1
- package/scripts/session-start.mjs +60 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: researcher
|
|
3
|
-
description: Map the codebase or external docs to answer a specific question. Returns evidence, not opinions. Backed by the oh-my-copilot
|
|
3
|
+
description: Map the codebase or external docs to answer a specific question. Returns evidence, not opinions. Backed by the oh-my-copilot CLI (omp daily-log / project-memory / state) for note-taking across turns.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# researcher
|
|
@@ -17,10 +17,11 @@ Find evidence and document it. Do not propose changes.
|
|
|
17
17
|
- Open questions that need more digging (if any).
|
|
18
18
|
|
|
19
19
|
## Guidance
|
|
20
|
-
- Use the
|
|
21
|
-
- `
|
|
22
|
-
- `
|
|
23
|
-
- `
|
|
20
|
+
- Use the `omp` CLI for note-taking (run as shell commands):
|
|
21
|
+
- `omp daily-log add "<text>"` to record interim findings as you go.
|
|
22
|
+
- `omp project-memory add-note "<text>"` for facts worth keeping for the whole project.
|
|
23
|
+
- `omp state write <key> <json>` / `omp state read <key>` for transient structured scratch between turns.
|
|
24
|
+
- `omp trace timeline` if the question involves event history.
|
|
24
25
|
- Cite, don't paraphrase. Quote the line you found.
|
|
25
26
|
- If you can't find an answer, say so plainly — don't invent one.
|
|
26
|
-
- Do not modify code or files unrelated to research artifacts (
|
|
27
|
+
- Do not modify code or files unrelated to research artifacts (daily log, project memory).
|
|
@@ -33,3 +33,26 @@ Skip when the session was fewer than 3 turns or contained no user corrections.
|
|
|
33
33
|
Invoke `/self-evolve` **at most once per session.** After it reports back, do not invoke it again — even if the user replies "thanks" or "done" to the report itself. Just acknowledge and end the session.
|
|
34
34
|
|
|
35
35
|
The skill lives at `.github/skills/self-evolve/SKILL.md`. It is self-contained: it tells you which signals count as corrections, where to log them, and when a recurring pattern should become a draft skill.
|
|
36
|
+
|
|
37
|
+
## Daily log & repo goal
|
|
38
|
+
|
|
39
|
+
Keep lightweight memory in `.omp/` so the next session has context. **Be sparse** — these are
|
|
40
|
+
memory aids, not a transcript. Float kills their value.
|
|
41
|
+
|
|
42
|
+
- **Repo goal** (`.omp/goal.md`, via `/goal`): set/update the one-line objective when it is
|
|
43
|
+
established or genuinely shifts. Not every session.
|
|
44
|
+
- **Daily log** (`.omp/memory/daily/<date>.md`, via `/daily-log`):
|
|
45
|
+
- When a heavy mode (`/ralph`, `/ralplan`, `/omp-autopilot`, `/team`, `/ultrawork`,
|
|
46
|
+
`/ultraqa`) starts, set today's goal from its objective: `omp daily-log set-goal "<text>"`.
|
|
47
|
+
- When such a mode finishes, add **one** concise summary entry — `omp daily-log add "<text>"`:
|
|
48
|
+
what changed, key decisions, next step.
|
|
49
|
+
- Otherwise add an entry only at a genuine milestone you judge worth remembering.
|
|
50
|
+
- **Project memory**: `omp project-memory add-directive "<rule>"` for a must-follow rule —
|
|
51
|
+
directives are **injected at every session start** (never on-demand), so add only real,
|
|
52
|
+
lasting rules and keep them few. `omp project-memory add-note "<title>" [--body "<text>"]`
|
|
53
|
+
for durable facts; list them with `omp project-memory index` and load one with
|
|
54
|
+
`omp project-memory read <id>` — bodies stay on disk until asked, so notes never bloat context.
|
|
55
|
+
- **Caps & retention:** keep directives to a handful (only the first ~12 are injected at start);
|
|
56
|
+
a few daily entries per day — decisions and outcomes, not narration; skip trivial sessions;
|
|
57
|
+
never log per skill invocation. Trim old day-files with `omp daily-log prune [--keep-days <n>]`
|
|
58
|
+
(default 30).
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: daily-log
|
|
3
|
+
description: Keep a simple per-project daily memory log under the project's daily-memory directory. Review it at session start when relevant, set today's goal, append milestones, and summarize before wrapping up. Use when the user says /daily-log, asks to record/recall what was done, or at end of session.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Daily Log
|
|
7
|
+
|
|
8
|
+
A lightweight, human-readable journal stored **per project** at `.omp/memory/daily/<YYYY-MM-DD>.md`.
|
|
9
|
+
Each day has a `## Goal` and a timestamped `## Log`. It is written and read through the `omp`
|
|
10
|
+
CLI (run these as shell commands):
|
|
11
|
+
|
|
12
|
+
- `omp daily-log set-goal "<text>"` — set/replace today's goal (the day's intent).
|
|
13
|
+
- `omp daily-log add "<text>"` — append a timestamped bullet to today's log.
|
|
14
|
+
- `omp daily-log read [--days N]` — read today + the previous N days (default 1).
|
|
15
|
+
|
|
16
|
+
The command writes under the current project's `.omp/`, so every project keeps its own log
|
|
17
|
+
automatically.
|
|
18
|
+
|
|
19
|
+
## When to use each
|
|
20
|
+
|
|
21
|
+
### At session start (review)
|
|
22
|
+
The SessionStart hook injects a one-line breadcrumb, e.g.:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
[DAILY LOG] Goal: <today's goal>
|
|
26
|
+
N entries logged in the last 7 days — run `omp daily-log read` to load if relevant.
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
If the breadcrumb looks relevant to what the user is asking for — same feature, an open
|
|
30
|
+
goal, or continuation of yesterday's work — run `omp daily-log read` to pull the full recent
|
|
31
|
+
log. If the new request is clearly unrelated, **skip it**; loading is optional.
|
|
32
|
+
|
|
33
|
+
### When intent is established
|
|
34
|
+
Once you and the user agree on what today is about, run `omp daily-log set-goal "<text>"` with a
|
|
35
|
+
short one-line goal (e.g. "Ship the daily-log feature").
|
|
36
|
+
|
|
37
|
+
### At milestones
|
|
38
|
+
After a meaningful step — a decision made, a feature landed, a blocker hit — run
|
|
39
|
+
`omp daily-log add "<text>"` with a concise note. Record *what changed and why*, not narration.
|
|
40
|
+
Good: "Chose breadcrumb+lazy-load over always-inject to keep start message small."
|
|
41
|
+
Avoid: "Ran the build."
|
|
42
|
+
|
|
43
|
+
### Resume nudge (start of the next session)
|
|
44
|
+
There is no per-turn nudge. Instead, if a session did real work but wrote nothing,
|
|
45
|
+
the **next** SessionStart injects a one-line reminder:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
[DAILY LOG] Your last session made progress but recorded nothing ...
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Treat it as a prompt to capture what changed before moving on — or, better, summarize
|
|
52
|
+
into the log *before ending* the session (below), so the nudge never needs to fire.
|
|
53
|
+
|
|
54
|
+
### Before wrapping up (the real end-of-session write)
|
|
55
|
+
When the user signals they're done, **summarize the session into the log** with one or two
|
|
56
|
+
`omp daily-log add "<text>"` commands: what was accomplished, key decisions, and the next step
|
|
57
|
+
for tomorrow. The SessionEnd hook cannot reach the model, so this end-of-session summary happens here.
|
|
58
|
+
|
|
59
|
+
## Notes
|
|
60
|
+
- Keep entries short; the log is a memory aid, not a transcript.
|
|
61
|
+
- The file is safe to hand-edit: the CLI preserves your lines in the Goal and Log
|
|
62
|
+
sections verbatim (only blank spacer lines between entries are dropped on rewrite).
|
|
63
|
+
- Do not put secrets in the log — it is a plain file committed nowhere by this skill but
|
|
64
|
+
readable by anyone with repo access.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: goal
|
|
3
|
+
description: Set or show the repo's durable objective — what we want to achieve in this repo — stored per project at .omp/goal.md. Use when the user says /goal, states the repo's north-star, or asks "what are we trying to achieve here".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Goal
|
|
7
|
+
|
|
8
|
+
The repo's **durable objective** — the north-star for this project — stored per project at
|
|
9
|
+
`.omp/goal.md`. It is distinct from a daily log's per-day goal: this is the long-lived "what
|
|
10
|
+
we want to achieve in this repo", set rarely and updated only when the objective changes.
|
|
11
|
+
|
|
12
|
+
It is read and written through the `omp` CLI (run these as shell commands):
|
|
13
|
+
|
|
14
|
+
- `omp goal set "<objective>"` — set/replace the repo objective (one concise line).
|
|
15
|
+
- `omp goal read` — print the current repo objective.
|
|
16
|
+
|
|
17
|
+
The command writes under the current project's `.omp/`, so every repo keeps its own goal
|
|
18
|
+
automatically.
|
|
19
|
+
|
|
20
|
+
## When to use
|
|
21
|
+
|
|
22
|
+
- **`/goal <text>`** — the user states the objective; run `omp goal set "<one concise line>"`.
|
|
23
|
+
- **`/goal`** (no text) — run `omp goal read` and show the current objective. If none is set,
|
|
24
|
+
offer to set one.
|
|
25
|
+
- The SessionStart hook surfaces the goal as a `[REPO GOAL] …` line, so it is already in
|
|
26
|
+
context at the start of each session — only run `omp goal read` when the user asks explicitly.
|
|
27
|
+
|
|
28
|
+
## Notes
|
|
29
|
+
|
|
30
|
+
- Keep it to a single north-star sentence; it is not a task list. Use the daily log
|
|
31
|
+
(`/daily-log`) for day-to-day progress toward this goal.
|
|
32
|
+
- Update it when the objective genuinely shifts — not every session.
|
|
33
|
+
- Do not put secrets in it; `.omp/goal.md` is a plain file readable by anyone with repo access.
|
|
@@ -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
|
+
```
|
package/README.md
CHANGED
|
@@ -113,6 +113,7 @@ These run **inside a Copilot CLI session** after the plugin is installed.
|
|
|
113
113
|
| `/grill-me` | Stress-test a plan with Socratic questions | `/grill-me` |
|
|
114
114
|
| `/caveman` | Ultra-compressed communication mode | `/caveman` |
|
|
115
115
|
| `/worktree` | Git worktree-based parallel branch work | `/worktree` |
|
|
116
|
+
| `/schedule` | Durable local cron job — re-runs a prompt on a schedule, survives reboot | `/schedule "check the PR every 15 min"` |
|
|
116
117
|
|
|
117
118
|
---
|
|
118
119
|
|
|
@@ -189,6 +190,11 @@ omp ralph start "<task>" [--max-iterations N]
|
|
|
189
190
|
omp ultrawork start "<objective>" [--task-count N]
|
|
190
191
|
omp ultraqa start "<goal>" [--max-cycles N]
|
|
191
192
|
omp council "<question>" [--models a,b,c] [--context @file] [--json] # multi-model council
|
|
193
|
+
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]
|
|
194
|
+
omp schedule list # registered jobs + OS-install status
|
|
195
|
+
omp schedule status <id> # last run + result summary
|
|
196
|
+
omp schedule run-now <id> # trigger one run immediately
|
|
197
|
+
omp schedule remove <id> # uninstall the OS entry + delete the job
|
|
192
198
|
omp mcp # MCP server over stdio
|
|
193
199
|
omp catalog list | validate | capability <id>
|
|
194
200
|
omp jira render <plan-file>
|
|
@@ -199,6 +205,17 @@ Environment overrides:
|
|
|
199
205
|
|
|
200
206
|
- `OMP_PLUGIN_ROOT` — path to the plugin checkout (with `OMC_PLUGIN_ROOT` accepted for back-compat)
|
|
201
207
|
- `OMP_COPILOT_BIN` — alternate `copilot` binary
|
|
208
|
+
- `OMP_BIN` — absolute path to the `omp` wrapper written into OS-scheduler entries (overrides `which omp`)
|
|
209
|
+
|
|
210
|
+
**Scheduled jobs** register a durable per-job entry with the OS scheduler (macOS launchd,
|
|
211
|
+
Linux systemd-user timers, or a managed `crontab` block as a cross-platform fallback) that
|
|
212
|
+
invokes `omp schedule run --id <id>` on the cron schedule. Each tick spawns a fresh agent
|
|
213
|
+
session; overlapping runs are locked out and every run is killed at its `--timeout`
|
|
214
|
+
(default 5 min). Jobs default to **read-only** (`--allow-all-tools` is opt-in and prints a
|
|
215
|
+
warning) and auto-expire after 72h unless `--ttl-hours`/`--max-runs` say otherwise. Recent
|
|
216
|
+
run results are surfaced automatically at the start of new Copilot sessions. Always use
|
|
217
|
+
`omp schedule remove`, never delete `.omp/state/schedule/` by hand, so the OS entry is
|
|
218
|
+
uninstalled cleanly.
|
|
202
219
|
|
|
203
220
|
---
|
|
204
221
|
|
|
@@ -218,9 +235,9 @@ Auto-snapshot the working tree before any tool-driven file edit. `omp rollback [
|
|
|
218
235
|
|
|
219
236
|
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
237
|
|
|
221
|
-
### v0.5 — Scheduled tasks
|
|
238
|
+
### v0.5 — Scheduled tasks ✅ (shipped)
|
|
222
239
|
|
|
223
|
-
|
|
240
|
+
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
241
|
|
|
225
242
|
### v0.6 — Browser tool (MCP)
|
|
226
243
|
|
package/dist/src/cli.js
CHANGED
|
@@ -22,7 +22,7 @@ function printResult(result, json) {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
function help() {
|
|
25
|
-
return `oh-my-copilot\n\nRun \`omp\` with no arguments to launch copilot (permissions bypass OFF).\nUse \`omp help\` to show this list.\n\nCommands:\n (no args) launch copilot (bypass OFF by default)\n version [--json]\n list [--json]\n setup [--dry-run] [--scope project|user] [--plugin-root <dir>] [--json]\n doctor [--json] [--copilot-bin <path>] [--skip-copilot]\n launch -- <args...>\n --madmax [args...] (bare-flag launch with permissions bypass; alias of --yolo)\n team <N:role> "<task>" [--name <name>] [--json]\n team status <name> [--json]\n team shutdown <name> [--json]\n team api claim-task --input '<json>' [--json]\n team api transition-task-status --input '<json>' [--json]\n team api send-message --input '<json>' [--json]\n team api broadcast --input '<json>' [--json]\n team api mailbox-list --input '<json>' [--json]\n team api mailbox-mark-delivered --input '<json>' [--json]\n council "<question>" [--models a,b,c|m:role:weight] [--context <text|@file>] [--rubric <text|@file>] [--synth <model>] [--probe] [--timeout <ms>] [--synth-timeout <ms>] [--min-survivors <n>] [--max-concurrency <n>] [--tmp-dir <dir>] [--json]\n
|
|
25
|
+
return `oh-my-copilot\n\nRun \`omp\` with no arguments to launch copilot (permissions bypass OFF).\nUse \`omp help\` to show this list.\n\nCommands:\n (no args) launch copilot (bypass OFF by default)\n version [--json]\n list [--json]\n setup [--dry-run] [--scope project|user] [--plugin-root <dir>] [--json]\n doctor [--json] [--copilot-bin <path>] [--skip-copilot]\n launch -- <args...>\n --madmax [args...] (bare-flag launch with permissions bypass; alias of --yolo)\n team <N:role> "<task>" [--name <name>] [--json]\n team status <name> [--json]\n team shutdown <name> [--json]\n team api claim-task --input '<json>' [--json]\n team api transition-task-status --input '<json>' [--json]\n team api send-message --input '<json>' [--json]\n team api broadcast --input '<json>' [--json]\n team api mailbox-list --input '<json>' [--json]\n team api mailbox-mark-delivered --input '<json>' [--json]\n council "<question>" [--models a,b,c|m:role:weight] [--context <text|@file>] [--rubric <text|@file>] [--synth <model>] [--probe] [--timeout <ms>] [--synth-timeout <ms>] [--min-survivors <n>] [--max-concurrency <n>] [--tmp-dir <dir>] [--json]\n ralph start "<task>" [--max-iterations <n>] [--session-id <id>] [--json]\n ralph status [--json]\n ralph tick [--json]\n ralph cancel [--json]\n ultrawork start "<objective>" [--task-count <n>] [--summary <s>] [--json]\n ultrawork status [--json]\n ultrawork cancel [--json]\n ultraqa start "<goal>" [--max-cycles <n>] [--json]\n ultraqa cycle pass|fail|pending [--json]\n ultraqa status [--json]\n ultraqa cancel [--json]\n schedule add --id <id> --cron \"<expr>\" --prompt \"<text>\" [--bin copilot] [--model <m>] [--cwd <dir>] [--timeout <ms>] [--max-runs <n>] [--ttl-hours <h>] [--allow-all-tools] [--dry-run] [--json]\n schedule list [--json]\n schedule status <id> [--json]\n schedule run-now <id> [--json]\n schedule remove <id> [--json]\n goal set "<objective>" [--json]\n goal read [--json]\n memory sync [--json] (render goal+directives into copilot-instructions.md)\n daily-log set-goal "<text>" [--json]\n daily-log add "<text>" [--json]\n daily-log read [--days <n>] [--json]\n daily-log prune [--keep-days <n>] [--json]\n state write <key> <val> [--ttl <s>] | read|delete|status <key> | list | cleanup [--json]\n project-memory read [<id>] | index | add-note "<title>" [--body "<text>"] | add-directive "<rule>" [--json]\n trace timeline [<sessionId>] [--limit <n>] | summary [<sessionId>] | add <sessionId> <event> [<json>] [--json]\n catalog list [--json]\n catalog validate [--json]\n catalog capability <id> [--json]\n project inspect [--json]\n skill install <skill-dir> [--root <repo>] [--scope project|user] [--dry-run] [--json]\n lint:skills [--root <repo>]\n sync:dry-run [--root <repo>]\n jira:dry-run [--root <repo>]\n jira render <plan-file> [--root <repo>] [--json]\n jira apply <ticket-key-or-plan-file> --comment|--update|--transition|--link [--dry-run] [--json]\n`;
|
|
26
26
|
}
|
|
27
27
|
async function resolveExistingInputPath(value) {
|
|
28
28
|
const { existsSync } = await import("node:fs");
|
|
@@ -117,14 +117,6 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
117
117
|
if (group === "council") {
|
|
118
118
|
return await handleCouncilCommand(argv, json);
|
|
119
119
|
}
|
|
120
|
-
if (group === "mcp") {
|
|
121
|
-
const { runMcpServer } = await import("./mcp/server.js");
|
|
122
|
-
const { allTools } = await import("./mcp/tools/index.js");
|
|
123
|
-
const { filterToolsByEnv } = await import("./mcp/server.js");
|
|
124
|
-
const tools = filterToolsByEnv(allTools);
|
|
125
|
-
await runMcpServer({ name: "oh-my-copilot", version: "0.1.0", tools });
|
|
126
|
-
return { ok: true, message: "mcp server exited" };
|
|
127
|
-
}
|
|
128
120
|
if (group === "ralph") {
|
|
129
121
|
return await handleModeCommand("ralph", argv, json);
|
|
130
122
|
}
|
|
@@ -134,6 +126,198 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
134
126
|
if (group === "ultraqa") {
|
|
135
127
|
return await handleModeCommand("ultraqa", argv, json);
|
|
136
128
|
}
|
|
129
|
+
if (group === "schedule") {
|
|
130
|
+
return await handleScheduleCommand(argv, json);
|
|
131
|
+
}
|
|
132
|
+
if (group === "goal") {
|
|
133
|
+
const { readRepoGoal, writeRepoGoal } = await import("./goal.js");
|
|
134
|
+
const { syncInstructionsMemory } = await import("./instructions-memory.js");
|
|
135
|
+
const cwd = flagValue(argv, "--root") ?? process.cwd();
|
|
136
|
+
if (command === "set") {
|
|
137
|
+
if (!value || !value.trim() || value.startsWith("-")) {
|
|
138
|
+
return { ok: false, exitCode: 1, message: 'usage: omp goal set "<objective>"' };
|
|
139
|
+
}
|
|
140
|
+
const goal = writeRepoGoal(cwd, value);
|
|
141
|
+
syncInstructionsMemory(cwd); // refresh the managed block Copilot reads
|
|
142
|
+
return json ? { ok: true, output: { ok: true, goal } } : { ok: true, message: `repo goal set: ${goal}` };
|
|
143
|
+
}
|
|
144
|
+
if (command === "read" || command === undefined) {
|
|
145
|
+
const goal = readRepoGoal(cwd);
|
|
146
|
+
return json ? { ok: true, output: { goal } } : { ok: true, message: goal || "(no repo goal set)" };
|
|
147
|
+
}
|
|
148
|
+
return { ok: false, exitCode: 1, message: 'Unknown goal subcommand. Try: goal set "<text>" | goal read' };
|
|
149
|
+
}
|
|
150
|
+
if (group === "memory") {
|
|
151
|
+
const { syncInstructionsMemory } = await import("./instructions-memory.js");
|
|
152
|
+
const cwd = flagValue(argv, "--root") ?? process.cwd();
|
|
153
|
+
if (command === "sync" || command === undefined) {
|
|
154
|
+
const r = syncInstructionsMemory(cwd);
|
|
155
|
+
return json
|
|
156
|
+
? { ok: r.wrote, output: r }
|
|
157
|
+
: { ok: r.wrote, message: r.wrote ? `memory synced to ${r.path}` : `could not write ${r.path}` };
|
|
158
|
+
}
|
|
159
|
+
return { ok: false, exitCode: 1, message: "Unknown memory subcommand. Try: memory sync" };
|
|
160
|
+
}
|
|
161
|
+
if (group === "daily-log") {
|
|
162
|
+
const { setDailyGoal, addLogEntry, readDailyLog, pruneDailyLog } = await import("./daily-log.js");
|
|
163
|
+
const cwd = flagValue(argv, "--root") ?? process.cwd();
|
|
164
|
+
if (command === "set-goal") {
|
|
165
|
+
if (!value || !value.trim() || value.startsWith("-")) {
|
|
166
|
+
return { ok: false, exitCode: 1, message: 'usage: omp daily-log set-goal "<text>"' };
|
|
167
|
+
}
|
|
168
|
+
const res = setDailyGoal(cwd, value);
|
|
169
|
+
return json ? { ok: true, output: { ok: true, ...res } } : { ok: true, message: `daily goal set (${res.date}): ${res.goal}` };
|
|
170
|
+
}
|
|
171
|
+
if (command === "add") {
|
|
172
|
+
if (!value || !value.trim() || value.startsWith("-")) {
|
|
173
|
+
return { ok: false, exitCode: 1, message: 'usage: omp daily-log add "<text>"' };
|
|
174
|
+
}
|
|
175
|
+
const res = addLogEntry(cwd, value);
|
|
176
|
+
return json ? { ok: true, output: { ok: true, ...res } } : { ok: true, message: `logged (${res.date}); ${res.count} entr${res.count === 1 ? "y" : "ies"} today` };
|
|
177
|
+
}
|
|
178
|
+
if (command === "read" || command === undefined) {
|
|
179
|
+
const daysRaw = flagValue(argv, "--days");
|
|
180
|
+
const days = daysRaw !== undefined && Number.isFinite(Number(daysRaw)) ? Number(daysRaw) : 1;
|
|
181
|
+
const text = readDailyLog(cwd, days);
|
|
182
|
+
return json ? { ok: true, output: { log: text } } : { ok: true, message: text || "(no daily log entries)" };
|
|
183
|
+
}
|
|
184
|
+
if (command === "prune") {
|
|
185
|
+
const keepRaw = flagValue(argv, "--keep-days");
|
|
186
|
+
const keepDays = keepRaw !== undefined && Number.isFinite(Number(keepRaw)) ? Number(keepRaw) : 30;
|
|
187
|
+
const removed = pruneDailyLog(cwd, keepDays);
|
|
188
|
+
return json
|
|
189
|
+
? { ok: true, output: { removed } }
|
|
190
|
+
: { ok: true, message: `pruned ${removed.length} day-file(s) older than ${keepDays}d` };
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
ok: false,
|
|
194
|
+
exitCode: 1,
|
|
195
|
+
message: 'Unknown daily-log subcommand. Try: daily-log set-goal "<text>" | add "<text>" | read [--days N] | prune [--keep-days N]',
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
if (group === "state") {
|
|
199
|
+
const s = await import("./state.js");
|
|
200
|
+
const cwd = flagValue(argv, "--root") ?? process.cwd();
|
|
201
|
+
if (command === "read" && value) {
|
|
202
|
+
return { ok: true, output: s.stateRead(cwd, value) };
|
|
203
|
+
}
|
|
204
|
+
if (command === "write" && value) {
|
|
205
|
+
const raw = argv[3];
|
|
206
|
+
if (raw === undefined)
|
|
207
|
+
return { ok: false, exitCode: 1, message: "usage: omp state write <key> <json-or-string> [--ttl <sec>]" };
|
|
208
|
+
let parsed;
|
|
209
|
+
try {
|
|
210
|
+
parsed = JSON.parse(raw);
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
parsed = raw;
|
|
214
|
+
}
|
|
215
|
+
const ttlRaw = flagValue(argv, "--ttl");
|
|
216
|
+
const ttl = ttlRaw !== undefined && Number.isFinite(Number(ttlRaw)) ? Number(ttlRaw) : undefined;
|
|
217
|
+
const expiresAt = s.stateWrite(cwd, value, parsed, ttl);
|
|
218
|
+
return json ? { ok: true, output: { ok: true, expiresAt } } : { ok: true, message: `wrote ${value}${expiresAt ? ` (expires ${expiresAt})` : ""}` };
|
|
219
|
+
}
|
|
220
|
+
if (command === "delete" && value) {
|
|
221
|
+
s.stateDelete(cwd, value);
|
|
222
|
+
return { ok: true, message: `deleted ${value}` };
|
|
223
|
+
}
|
|
224
|
+
if (command === "list") {
|
|
225
|
+
return { ok: true, output: { keys: s.stateList(cwd) } };
|
|
226
|
+
}
|
|
227
|
+
if (command === "cleanup") {
|
|
228
|
+
const deleted = s.stateCleanup(cwd);
|
|
229
|
+
return json ? { ok: true, output: { deleted } } : { ok: true, message: `cleaned ${deleted} expired` };
|
|
230
|
+
}
|
|
231
|
+
if (command === "status" && value) {
|
|
232
|
+
return { ok: true, output: s.stateStatus(cwd, value) };
|
|
233
|
+
}
|
|
234
|
+
return { ok: false, exitCode: 1, message: "Unknown state subcommand. Try: state read <key> | write <key> <val> [--ttl s] | delete <key> | list | cleanup | status <key>" };
|
|
235
|
+
}
|
|
236
|
+
if (group === "project-memory") {
|
|
237
|
+
const pm = await import("./project-memory.js");
|
|
238
|
+
const { syncInstructionsMemory } = await import("./instructions-memory.js");
|
|
239
|
+
const cwd = flagValue(argv, "--root") ?? process.cwd();
|
|
240
|
+
if (command === "add-note") {
|
|
241
|
+
// Accept a --title flag or a positional title; reject a flag-like value in
|
|
242
|
+
// either slot, so `add-note --title --body x` doesn't store "--body".
|
|
243
|
+
const titleFlag = flagValue(argv, "--title");
|
|
244
|
+
const title = titleFlag && !titleFlag.startsWith("-")
|
|
245
|
+
? titleFlag
|
|
246
|
+
: value && !value.startsWith("-")
|
|
247
|
+
? value
|
|
248
|
+
: undefined;
|
|
249
|
+
if (!title || !title.trim()) {
|
|
250
|
+
return { ok: false, exitCode: 1, message: 'usage: omp project-memory add-note "<title>" [--body "<text>"]' };
|
|
251
|
+
}
|
|
252
|
+
const id = pm.addNote(cwd, title, flagValue(argv, "--body"));
|
|
253
|
+
syncInstructionsMemory(cwd); // refresh the managed block Copilot reads
|
|
254
|
+
return json ? { ok: true, output: { ok: true, id } } : { ok: true, message: `note added: ${id}` };
|
|
255
|
+
}
|
|
256
|
+
if (command === "add-directive") {
|
|
257
|
+
const directiveFlag = flagValue(argv, "--directive");
|
|
258
|
+
const directive = directiveFlag && !directiveFlag.startsWith("-")
|
|
259
|
+
? directiveFlag
|
|
260
|
+
: value && !value.startsWith("-")
|
|
261
|
+
? value
|
|
262
|
+
: undefined;
|
|
263
|
+
if (!directive || !directive.trim()) {
|
|
264
|
+
return { ok: false, exitCode: 1, message: 'usage: omp project-memory add-directive "<rule>"' };
|
|
265
|
+
}
|
|
266
|
+
const count = pm.addDirective(cwd, directive);
|
|
267
|
+
syncInstructionsMemory(cwd); // refresh the managed block Copilot reads
|
|
268
|
+
return json ? { ok: true, output: { ok: true, count } } : { ok: true, message: `directive added (${count} total)` };
|
|
269
|
+
}
|
|
270
|
+
if (command === "index") {
|
|
271
|
+
return { ok: true, output: { notes: pm.noteIndex(cwd) } };
|
|
272
|
+
}
|
|
273
|
+
if (command === "read" || command === undefined) {
|
|
274
|
+
// `read <id>` loads one note's body on demand; bare `read` returns the
|
|
275
|
+
// bounded summary (directives + note index — never note bodies).
|
|
276
|
+
if (value && !value.startsWith("-")) {
|
|
277
|
+
const note = pm.readNote(cwd, value);
|
|
278
|
+
if (note === null)
|
|
279
|
+
return { ok: false, exitCode: 1, message: `no note with id: ${value}` };
|
|
280
|
+
return { ok: true, message: note };
|
|
281
|
+
}
|
|
282
|
+
return { ok: true, output: { directives: pm.readDirectives(cwd), notes: pm.noteIndex(cwd) } };
|
|
283
|
+
}
|
|
284
|
+
return {
|
|
285
|
+
ok: false,
|
|
286
|
+
exitCode: 1,
|
|
287
|
+
message: 'Unknown project-memory subcommand. Try: project-memory read [<id>] | index | add-note "<title>" [--body "<text>"] | add-directive "<rule>"',
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
if (group === "trace") {
|
|
291
|
+
const tr = await import("./trace.js");
|
|
292
|
+
const cwd = flagValue(argv, "--root") ?? process.cwd();
|
|
293
|
+
const sid = value && !value.startsWith("-") ? value : undefined;
|
|
294
|
+
if (command === "timeline" || command === undefined) {
|
|
295
|
+
const limitRaw = flagValue(argv, "--limit");
|
|
296
|
+
const limit = limitRaw !== undefined && Number.isFinite(Number(limitRaw)) ? Number(limitRaw) : 50;
|
|
297
|
+
return { ok: true, output: tr.traceTimeline(cwd, sid, limit) };
|
|
298
|
+
}
|
|
299
|
+
if (command === "summary") {
|
|
300
|
+
return { ok: true, output: tr.traceSummary(cwd, sid) };
|
|
301
|
+
}
|
|
302
|
+
if (command === "add" && sid) {
|
|
303
|
+
const event = argv[3];
|
|
304
|
+
if (!event)
|
|
305
|
+
return { ok: false, exitCode: 1, message: "usage: omp trace add <sessionId> <event> [json-payload]" };
|
|
306
|
+
const payloadRaw = argv[4];
|
|
307
|
+
let payload;
|
|
308
|
+
if (payloadRaw !== undefined) {
|
|
309
|
+
try {
|
|
310
|
+
payload = JSON.parse(payloadRaw);
|
|
311
|
+
}
|
|
312
|
+
catch {
|
|
313
|
+
payload = payloadRaw;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
tr.appendTraceEntry(cwd, sid, { event, payload });
|
|
317
|
+
return { ok: true, message: `trace appended to ${sid}` };
|
|
318
|
+
}
|
|
319
|
+
return { ok: false, exitCode: 1, message: "Unknown trace subcommand. Try: trace timeline [sessionId] [--limit N] | summary [sessionId] | add <sessionId> <event> [json]" };
|
|
320
|
+
}
|
|
137
321
|
if (group === "catalog") {
|
|
138
322
|
if (command === "list") {
|
|
139
323
|
const bundle = loadCatalogBundle();
|
|
@@ -605,6 +789,85 @@ async function handleModeCommand(mode, argv, json) {
|
|
|
605
789
|
message: `Unknown ${mode} subcommand. Try: ${mode} start "<task>" | status | cancel${mode === "ralph" ? " | tick" : ""}${mode === "ultraqa" ? " | cycle pass|fail|pending" : ""}`,
|
|
606
790
|
};
|
|
607
791
|
}
|
|
792
|
+
async function handleScheduleCommand(argv, json) {
|
|
793
|
+
const [, command, value] = argv;
|
|
794
|
+
const cwd = flagValue(argv, "--root") ?? process.cwd();
|
|
795
|
+
// The OS scheduler invokes `omp schedule run --id <id> --root <dir>`, so prefer
|
|
796
|
+
// the --id flag; fall back to the positional form for human-typed commands.
|
|
797
|
+
const targetId = flagValue(argv, "--id") ?? (value && !value.startsWith("--") ? value : undefined);
|
|
798
|
+
const mod = await import("./schedule/commands.js");
|
|
799
|
+
if (command === "add") {
|
|
800
|
+
const id = flagValue(argv, "--id");
|
|
801
|
+
const cron = flagValue(argv, "--cron");
|
|
802
|
+
const prompt = flagValue(argv, "--prompt");
|
|
803
|
+
if (!id || !cron || !prompt) {
|
|
804
|
+
return { ok: false, exitCode: 1, message: 'schedule add requires --id, --cron, and --prompt' };
|
|
805
|
+
}
|
|
806
|
+
let timeoutMs;
|
|
807
|
+
let maxRuns;
|
|
808
|
+
let ttlHours;
|
|
809
|
+
try {
|
|
810
|
+
timeoutMs = parsePositiveIntFlag(flagValue(argv, "--timeout"), "--timeout");
|
|
811
|
+
maxRuns = parsePositiveIntFlag(flagValue(argv, "--max-runs"), "--max-runs");
|
|
812
|
+
ttlHours = parsePositiveIntFlag(flagValue(argv, "--ttl-hours"), "--ttl-hours");
|
|
813
|
+
}
|
|
814
|
+
catch (err) {
|
|
815
|
+
return { ok: false, exitCode: 1, message: String(err instanceof Error ? err.message : err) };
|
|
816
|
+
}
|
|
817
|
+
const result = mod.addScheduleJob(cwd, {
|
|
818
|
+
id,
|
|
819
|
+
cron,
|
|
820
|
+
prompt,
|
|
821
|
+
bin: flagValue(argv, "--bin"),
|
|
822
|
+
model: flagValue(argv, "--model"),
|
|
823
|
+
cwd: flagValue(argv, "--cwd"),
|
|
824
|
+
timeoutMs,
|
|
825
|
+
maxRuns,
|
|
826
|
+
ttlHours,
|
|
827
|
+
allowAllTools: hasFlag(argv, "--allow-all-tools"),
|
|
828
|
+
dryRun: hasFlag(argv, "--dry-run"),
|
|
829
|
+
});
|
|
830
|
+
return json
|
|
831
|
+
? { ok: result.ok, exitCode: result.ok ? 0 : 1, output: result }
|
|
832
|
+
: { ok: result.ok, exitCode: result.ok ? 0 : 1, message: result.ok ? result.messages.join("\n") : (result.error ?? "schedule add failed") };
|
|
833
|
+
}
|
|
834
|
+
if (command === "list") {
|
|
835
|
+
const jobs = mod.listScheduleJobs(cwd);
|
|
836
|
+
return json
|
|
837
|
+
? { ok: true, output: jobs }
|
|
838
|
+
: {
|
|
839
|
+
ok: true,
|
|
840
|
+
message: jobs.length
|
|
841
|
+
? jobs.map((j) => `${j.id}\t${j.cron}\t${j.backend}\tinstalled=${j.osInstalled}\tlast=${j.lastStatus ?? "-"}`).join("\n")
|
|
842
|
+
: "(no scheduled jobs)",
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
if (command === "status" && targetId) {
|
|
846
|
+
const st = mod.getScheduleStatus(cwd, targetId);
|
|
847
|
+
if (!st.job)
|
|
848
|
+
return { ok: false, exitCode: 1, output: json ? st : undefined, message: `no schedule job "${targetId}"` };
|
|
849
|
+
return json
|
|
850
|
+
? { ok: true, output: st }
|
|
851
|
+
: { ok: true, message: `${st.job.id} cron=${st.job.cron} backend=${st.job.backend} installed=${st.osInstalled} runs=${st.job.runCount} last=${st.job.lastStatus ?? "-"}` };
|
|
852
|
+
}
|
|
853
|
+
if (command === "remove" && targetId) {
|
|
854
|
+
const result = mod.removeScheduleJob(cwd, targetId);
|
|
855
|
+
return json
|
|
856
|
+
? { ok: result.removed, exitCode: result.removed ? 0 : 1, output: result }
|
|
857
|
+
: { ok: result.removed, exitCode: result.removed ? 0 : 1, message: result.removed ? `removed "${targetId}"` : `no schedule job "${targetId}"` };
|
|
858
|
+
}
|
|
859
|
+
if ((command === "run" || command === "run-now") && targetId) {
|
|
860
|
+
const result = await mod.runScheduleById(cwd, targetId);
|
|
861
|
+
return json
|
|
862
|
+
? { ok: result.ok, exitCode: result.ok ? 0 : 1, output: result }
|
|
863
|
+
: { ok: result.ok, exitCode: result.ok ? 0 : 1, message: result.message };
|
|
864
|
+
}
|
|
865
|
+
return {
|
|
866
|
+
ok: false,
|
|
867
|
+
exitCode: 1,
|
|
868
|
+
message: 'Unknown schedule subcommand. Try: schedule add --id <id> --cron "<expr>" --prompt "<text>" | list | status <id> | remove <id> | run-now <id>',
|
|
869
|
+
};
|
|
870
|
+
}
|
|
608
871
|
function isEntrypoint() {
|
|
609
872
|
const argv1 = process.argv[1];
|
|
610
873
|
if (!argv1)
|